电脑网站接入支付宝扫码支付功能。

1.去支付宝官网申请公司企业账号并开通一个应用,在应用里签约APP支付功能

接入网址https://auth.alipay.com/login/ant_sso_index.htm?goto=https%3A%2F%2Fopen.alipay.com%2Fplatform%2Fsetting.htm
开发人员若没有拿到签约之后的秘钥可先进行沙箱测试。使用沙箱提供的测试秘钥进行测试。

2.创建应用

蚂蚁金服开放平台网站https://openhome.alipay.com/platform/appManage.htm#/apps
参考文献https://openclub.alipay.com/read.php?tid=1606&fid=25
应用分为第三方应用以及自用型。
第三方应用是甲方自己登陆企业账号,开发人员创建应用,创建之后发送授权申请,甲方授权之后拿到授权码方可继续发开。参考文章https://docs.open.alipay.com/20160728150111277227/intro
由于拿到了商家账号,本次使用自用型应用。

开发配置

QQ截图20190227110327.png

支付宝网关,正式环境则为此配置,沙箱环境见沙箱测试环节。
应用网关、授权回调地址一般不用配置,授权回调地址为第三方应用时使用,这里不详细说了。
重点是加签方式、涉及到开发参数配置
1)点击设置应用公钥
image.png
2)下载完成后点击生成秘钥
image.png
3)点击之后会在相应文件夹下自动保存生成的私钥公钥。将应用公钥填入后,点击保存会生成支付宝应用公钥。将此应用公钥下载保存。这些信息都是后面请求支付宝接口的重要参数。
image.png
4)点击提交审核等待审核。

3.沙盒环境

参考文档https://openhome.alipay.com/platform/appDaily.htm?tab=info
1)开发配置
image.png沙盒支付宝网关为https://openapi.alipay**dev**.com/gateway.do
秘钥使用RSA2方式,RSA已经不支持了,秘钥上传方式和前面所述一样。
2)沙盒环境生成的二维码支付需要使用沙箱专用的支付宝

4.系统集成

4.1.下载官方SDK和demo、

1)先进行demo测试,通过demo测试成功确保我们输入的参数无误再集成到项目中,这可减少不必要的错误,以免耽误开发进度。
2)参数配置
image.png
这里的参数主要有一下几点需要注意:
1.商户私钥为生成的RSA2秘钥的应用私钥,支付宝公钥为设置应用时上传应用公钥后生成的支付宝公钥。配置错误会导致验签失败。
2.支付宝异步通知需要公网访问,可以在本地进行内网穿透
3、支付宝网关,沙盒模式带dev字段,正式环境需去掉
3)运行demo进行测试。

4.2.将支付集成到项目中

在demo测试无误后方可将支付宝集成到项目中
获取支付宝SDK地址:支付宝SDK下载地址,这里我选择JAVA的SDK。下载解压后的SDK得到:

image.png

1)MAVEN配置

  1. <dependency>
  2. <groupId>com.alipay.sdk</groupId>
  3. <artifactId>alipay-sdk-java</artifactId>
  4. <version>3.6.0.ALL</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>commons-logging</groupId>
  8. <artifactId>commons-logging</artifactId>
  9. <version>1.1.1</version>
  10. </dependency>

2)流程详解
image.png
首先,我们来理一理开发的思路,按照我当前项目的需求,关于支付这一块大概操作流程是:用户在APP上选好要购买的商品,点击“立即购买”,跳转到订单详细页面。选择支付方式,点击“确定支付”跳转到支付宝APP,付款完成后,跳转回APP,完成支付。这个过程,当用户点击“确定支付”时,APP需要调用商户后台接口。
这时候就是我们所需要做的事情:先是生成商户系统一笔未支付的订单,获得商户订单ID(商户系统生成)和订单的一些其他信息,然后再调用支付宝的SDK提供的数字签名方法,将需要传给支付宝的信息进行加签,然后把加签后的字符串返回给APP。APP拉起支付宝APP,再把这个加签的字符串传给支付宝,完成支付。APP接收到同步通知后,还需要再次调用商户后台的接口(虽然同步通知也有付款情况,但需要以后台通知为准),校验订单最终的付款情况。按照支付宝API上所说,当完成支付后,支付宝会做2个操作,一个是同步返回信息给APP,一个是异步通知商户后台返回支付状态等信息,并且最终的支付结果是以异步通知为准。所以我们还需要考虑到一点,就是当用户支付成功之后,商户系统暂时没有接收到支付宝的异步通知时。我们需要拿着这个商户订单ID主动调用SDK支付宝的查询接口,去获取该订单的支付情况,并最终返回给APP。这个查询的接口应该是给APP收到同步通知后,请求商户系统后台进行校验的时候调用的。
根据我们上面思考所得,后台只需要对外提供3个接口即可:
1.用户点击“立即购买”时调用商户后台接口,后台返回加签后的订单信息字符串
2.在支付完成之后,支付宝异步通知商户后台订单的付款情况
3.在支付完成之后,跳转回APP时,APP调用商户后台进行最终付款校验

1)和沙箱环境一样,我们先设置参数

  1. public class AlipayConfig {
  2. public static String app_id = "2019022563373266";
  3. // public static String app_id = "2016092800612951";
  4. // 商户私钥,您的PKCS8格式RSA2私钥
  5. // public static String merchant_private_key = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCOmT37efurkA/SfMtwUDrCz18CZdfrtSvEGB5aBalZo2utEP9oezHZ5uoqNdHGnzGLhsegzZoavGuRGKN/F9RxF7cMbW665P2w/ymeIIiHXD9SJVguBRMZGFlBTs133Ds6BsBbcXoeCujnXAh6N/jJ/hIbuG84FctA3pusMmcqnbolmzW+BkH8NkF6ZjUBgF7lUpMCp7IfPiB2lJJufSr9kFrS2iu/bNsqXjw9kP7fmpzAY9mo+keKJpVH/WRRtR7x/ctlWaiTTks/Zh5VUMd5NfQN8a1/2MIlVowtR313lPJTYfIbzOP0dspBAQ9/Hu3W6vbzWA4a/L94zCn7tGXjAgMBAAECggEAPTM5rddRkvU2RTE4ItDcF0Xm0eGhxNCxouxzAlHRt93yVYmpBE9zOsDnGQyWJremDOrgfHwhOn5JcDFx4hb5Hzx9XEEPN/kVhEROuUXfMrD9oCTU7pNQ+gFANU470shbRvEk11ohfglSohEWtlWVWiPH48vJN/nqjMG9cC8Fpfz5sePpN5tbDZ2M2u5brVCLXWR5J2uF7mKQiKMIxvhMYcoQ2MS9+XQVjJktbWKlCdJboMQq4DS7diCXQcNmiWJrBh/qHcy9+NU9NRz82laGTyDkUnqnw16LMXZnopf6ZvGAwAL3T672UulLubxmUyq2kOROBtoJeyfR9D+qh6z12QKBgQDt9q8KqX6W432/BxwvYL6iNeUz/ujCODLq22ikSNvoMJAAYLIa39bDs8LHHEugIcpQk6DOxZ1MzkV82CHM0YA26T8bbfZrBiYK7oQO0hHP3oOMbRTB9lLGn9Kdhp/0pAcVVmXLAdRpWqv50BjhzRj3uc5sWeA4swRuZc0E9hvSLwKBgQCZaCXREevz3SnkiJy48z4KSS5+FEuimopgmaMqjdnzkh6E7Krd8sNFl82dt8Yio7dBScnmNuj98rKVObTe9+SN0H0UvdlKOYMHrHPEnOQJXVdii9tTONzHc41xRt0m4pKW+WbK/yem/YM3JutKajF8z7/Qt3K3K0Ap3S64dtn+jQKBgDbu4H+smw2IHKoxoP8K7VcS7ANx0Bet04tF9UUfkKxsMYH00IjFrZVl9Qvz6z1fL44kVlYFZoepgn8Mgdj/cAK9G5VgcCdKtxOE9jVZp0T+UA3lzInuLDOwB3XX8ysNhpkVZdBUbs2XYeX8dRIDP8VIPm/i25EbOmWl2ItRqSB/AoGAct7rwlv7JQZtAjXRtSRfRnB6IeqsoDu8do1VAt5pX9wAgmR9pejMk+YSD5G3CeUxqe/JAVwIp2/+jYNCjtttB0wg/VETzmK2XR6jKYyZrPF6J862EuE3YRqDxVjc7Osn/WkPsd3SIVcf1EKOWZCfMeiWp4FWoxX929tFytckcVkCgYEAhY6IkzJikbQEG7d0+RpC9UFtPlB+EEF+nt5aQxnZhMcTUJXQrNkARjemdqONu0L33BMZjt3jS7MZbn4LA8LdQH/vl7v92x9UX9kfwYwKwvPbWYQ6W1adlr68MDe7nUHoRLJCuFyn/HX8Kytl7f36NCTizWQoi4Owy5qnETIQxzU=";
  6. /支付宝公钥
  7. // public static String alipay_public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwLes6WOrmxNzjFzjzEtfVqOp8GLbhhE8KXobB5l2L5EoF/8xUPHuyXvKmbQcwabm1tNq/vREeswGwoHbJIk+dlvmh45RKjb0AQMDDtWxaEyAKKDNfe0TVrhMjD2g5FyFvJVqW20OK7bFJ6uFxPP3flLoUL1NjDx0C7CYRlIHxq4I0rqs/TZpdjPZy9txAE5sf2Yx0SdOPBNurS9EuFQYylOfmAkByAchRTCLPl+upndZqT8q7bCGU3J7t87PiU7FHEo1NoXbi5B3p5PoIsEeQXtjAy2lLWEDX4xRASqhyIibR8dv/1obxNgv8zvqzYcnmoPsM5Lbn1y7uuKvGQQyBwIDAQAB";
  8. //异步通知
  9. public static String notify_url = "http://fdzc.shangyuninfo.com/api/easyBuy/order/notifyUrl";
  10. //同步通知
  11. public static String return_url = "http://fdzc.shangyuninfo.com";
  12. // 签名方式
  13. public static String sign_type = "RSA2";
  14. // 字符编码格式
  15. public static String charset = "utf-8";
  16. // 支付宝网关,正式环境需要将dev去电
  17. // public static String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";
  18. // 支付宝网关
  19. public static String log_path = "D:\\";
  20. }

2)购买后返回支付链接模块
controller层

  1. /**
  2. * 加入众筹接口
  3. * 根据订单Id,添加订单详情表内容
  4. */
  5. // @RequiresPermissions("easyBuy:orderDetail:add")
  6. @Log(title = "订单", businessType = BusinessType.INSERT)
  7. @PostMapping("/add")
  8. @Transactional(rollbackFor = Exception.class)
  9. @ResponseBody
  10. public AjaxResult addSave(@RequestBody OrderDetail orderDetail)
  11. {
  12. //存订单详情表
  13. if (orderDetail.getOrderId() == null){
  14. return AjaxResult.error("订单Id不能为空,请联系管理员");
  15. }
  16. if (orderDetail.getProductNum() == null){
  17. return AjaxResult.error("产品数量不能为空");
  18. }
  19. Integer orderId = orderDetail.getOrderId();
  20. Order order = orderService.selectOrderById(orderId);
  21. Integer crowdPackageId = order.getCrowdfundingId();
  22. Integer AllPeople = crowdfundingPackagesService.selectCrowdfundingPackagesById(crowdPackageId).getPeopleNum();
  23. List<OrderDetail> orderDetailList = order.getOrderDetailList();
  24. Integer currentPeople = 1;
  25. for (OrderDetail item:orderDetailList){
  26. currentPeople = currentPeople+1;
  27. }
  28. if (currentPeople>AllPeople){
  29. return AjaxResult.error("人数已满,众筹结束");
  30. }else if (currentPeople.equals(AllPeople)){
  31. order.setCrowdfundingId(1);
  32. orderService.updateOrder(order);
  33. }
  34. BigDecimal productPrice = order.getProductPrice();
  35. orderDetail.setOrderId(orderId);
  36. orderDetail.setOrderNo(getUUID.getOrderIdByUUId());
  37. BigDecimal sumPrice = productPrice.multiply(new BigDecimal(orderDetail.getProductNum()));
  38. orderDetail.setOrderAmount(sumPrice);
  39. orderDetail.setCreateUserId(Integer.parseInt(ShiroUtils.getUserId().toString()));
  40. orderDetail.setOrderStatus(0);
  41. orderDetail.setCreateBy(ShiroUtils.getLoginName());
  42. orderDetailService.insertOrderDetail(orderDetail);
  43. Integer orderDetailId = orderDetail.getOrderDetailId();
  44. //开启支付宝支付
  45. }

ps:只要将开启支付后的参数设置对即可,上面的存表和获取信息的逻辑可根据自己项目写相应的逻辑。开启支付这部分可以放在service层,后面直接调用即可
service层

  1. /**
  2. * 获取支付宝加签后台的订单信息字符串
  3. *
  4. * @param request
  5. * @return
  6. */
  7. @Override
  8. @Transactional(propagation = Propagation.REQUIRED)
  9. public String getAliPayOrderStr(OrderTest orderTest) {
  10. //最终返回加签之后的,app需要传给支付宝app的订单信息字符串
  11. String orderString = "";
  12. logger.info("==================支付宝下单,商户订单号为:"+orderTest.getOutTradeNo());
  13. //创建商户支付宝订单(因为需要记录每次支付宝支付的记录信息,单独存一个表跟商户订单表关联,以便以后查证)
  14. AlipaymentOrder alipaymentOrder=new AlipaymentOrder();
  15. alipaymentOrder.setClubOrderId(orderTest.getId().toString());//商家订单主键
  16. alipaymentOrder.setOutTradeNo(orderTest.getOutTradeNo());//商户订单号
  17. alipaymentOrder.setTradeStatus((byte) 0);//交易状态
  18. alipaymentOrder.setTotalAmount(Double.parseDouble(orderTest.getTotalAmount()));//订单金额
  19. alipaymentOrder.setReceiptAmount(0.00);//实收金额
  20. alipaymentOrder.setInvoiceAmount(0.00);//开票金额
  21. alipaymentOrder.setBuyerPayAmount(0.00);//付款金额
  22. alipaymentOrder.setRefundFee(0.00); //总退款金额
  23. try{
  24. //实例化客户端(参数:网关地址、商户appid、商户私钥、格式、编码、支付宝公钥、加密类型),为了取得预付订单信息
  25. AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID,
  26. AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET,
  27. AlipayConfig.ALIPAY_PUBLIC_KEY,AlipayConfig.SIGNTYPE);
  28. //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
  29. AlipayTradeAppPayRequest ali_request = new AlipayTradeAppPayRequest();
  30. //SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式
  31. AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
  32. //业务参数传入,可以传很多,参考API
  33. //model.setPassbackParams(URLEncoder.encode(request.getBody().toString())); //公用参数(附加数据)
  34. model.setBody(orderTest.getBody()); //对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body。
  35. model.setSubject(orderTest.getSubjecy()); //商品名称
  36. model.setOutTradeNo(orderTest.getOutTradeNo()); //商户订单号(自动生成)
  37. // model.setTimeoutExpress("30m"); //交易超时时间
  38. model.setTotalAmount(orderTest.getTotalAmount()); //支付金额
  39. model.setProductCode("QUICK_MSECURITY_PAY"); //销售产品码(固定值)
  40. ali_request.setBizModel(model);
  41. logger.info("====================异步通知的地址为:"+alipayment.getNotifyUrl());
  42. ali_request.setNotifyUrl(AlipayConfig.notify_url); //异步回调地址(后台)
  43. ali_request.setReturnUrl(AlipayConfig.return_url); //同步回调地址(APP)
  44. // 这里和普通的接口调用不同,使用的是sdkExecute
  45. AlipayTradeAppPayResponse alipayTradeAppPayResponse = alipayClient.sdkExecute(ali_request); //返回支付宝订单信息(预处理)
  46. orderString=alipayTradeAppPayResponse.getBody();//就是orderString 可以直接给APP请求,无需再做处理。
  47. this.createAlipayMentOrder(alipaymentOrder);//创建新的商户支付宝订单
  48. } catch (AlipayApiException e) {
  49. e.printStackTrace();
  50. logger.info("与支付宝交互出错,未能生成订单,请检查代码!");
  51. }
  52. return orderString;
  53. }

支付成功后返回相应的跳转支付宝收银台的链接

3)实现第二个接口:在支付完成之后,支付宝异步通知商户后台订单的付款情况,这个是支付宝每隔一段时间来访问一次的接口,直到你返回success,才会停止访问,这里我分了2个地方进行调用

service层
这里由于有两个模块的存储逻辑处理都要在支付宝回调通知时提醒,所以本地方在生成支付链接是传入了passback_params参数,此参数支付在异步通知时会原样返回,此逻辑可根据实际情况编写。

  1. /**
  2. * 支付宝异步请求逻辑处理
  3. */
  4. @Override
  5. @Transactional(rollbackFor = Exception.class)
  6. public String notify(Map<String, String> conversionParams){
  7. log.info("==================支付宝异步请求逻辑处理");
  8. //签名验证(对支付宝返回的数据验证,确定是支付宝返回的)
  9. boolean signVerified = false;
  10. try {
  11. //调用SDK验证签名
  12. signVerified = AlipaySignature.rsaCheckV1(conversionParams, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);
  13. } catch (AlipayApiException e) {
  14. log.info("==================验签失败 !");
  15. e.printStackTrace();
  16. }
  17. //对验签进行处理
  18. if (signVerified) {
  19. //验签通过
  20. //获取需要保存的数据
  21. String appId=conversionParams.get("app_id");//支付宝分配给开发者的应用Id
  22. String notifyTime=conversionParams.get("notify_time");//通知时间:yyyy-MM-dd HH:mm:ss
  23. String gmtCreate=conversionParams.get("gmt_create");//交易创建时间:yyyy-MM-dd HH:mm:ss
  24. String gmtPayment=conversionParams.get("gmt_payment");//交易付款时间
  25. String gmtRefund=conversionParams.get("gmt_refund");//交易退款时间
  26. String gmtClose=conversionParams.get("gmt_close");//交易结束时间
  27. String tradeNo=conversionParams.get("trade_no");//支付宝的交易号
  28. String outTradeNo = conversionParams.get("out_trade_no");//获取商户之前传给支付宝的订单号(商户系统的唯一订单号)
  29. String outBizNo=conversionParams.get("out_biz_no");//商户业务号(商户业务ID,主要是退款通知中返回退款申请的流水号)
  30. String buyerLogonId=conversionParams.get("buyer_logon_id");//买家支付宝账号
  31. String sellerId=conversionParams.get("seller_id");//卖家支付宝用户号
  32. String sellerEmail=conversionParams.get("seller_email");//卖家支付宝账号
  33. String totalAmount=conversionParams.get("total_amount");//订单金额:本次交易支付的订单金额,单位为人民币(元)
  34. String receiptAmount=conversionParams.get("receipt_amount");//实收金额:商家在交易中实际收到的款项,单位为元
  35. String invoiceAmount=conversionParams.get("invoice_amount");//开票金额:用户在交易中支付的可开发票的金额
  36. String buyerPayAmount=conversionParams.get("buyer_pay_amount");//付款金额:用户在交易中支付的金额
  37. String tradeStatus = conversionParams.get("trade_status");// 获取交易状态
  38. String payMoudle = conversionParams.get("passback_params"); //请求时传入的付款模块参数,1代表易采购,2代表产品筹
  39. //支付宝官方建议校验的值(out_trade_no、total_amount、sellerId、app_id)
  40. if (payMoudle.equals("1")){
  41. OrderDetail orderDetailPay=orderDetailService.selectOrderDetailByOrderNo(outTradeNo);
  42. if(orderDetailPay!=null&&totalAmount.equals(orderDetailPay.getOrderAmount().toString())&&AlipayConfig.app_id.equals(appId)){
  43. //修改数据库相应订单详情表,保存订单记录
  44. //保存支付记录表
  45. Integer userId = orderDetailPay.getCreateUserId();
  46. PaymentRecork paymentRecork = new PaymentRecork();
  47. paymentRecork.setUserId(userId);
  48. paymentRecork.setAmount(BigDecimal.valueOf(Double.parseDouble(totalAmount)));
  49. paymentRecork.setPayModule(Integer.parseInt(payMoudle));
  50. paymentRecork.setBusinessId(orderDetailPay.getOrderDetailId());
  51. switch (tradeStatus) // 判断交易结果
  52. {
  53. case "TRADE_FINISHED": // 交易结束并不可退款
  54. orderDetailPay.setPayStatus(3);
  55. paymentRecork.setPaymentStatus(3);
  56. break;
  57. case "TRADE_SUCCESS": // 交易支付成功
  58. orderDetailPay.setPayStatus(1);
  59. paymentRecork.setPaymentStatus(1);
  60. break;
  61. case "TRADE_CLOSED": // 未付款交易超时关闭或支付完成后全额退款
  62. orderDetailPay.setPayStatus(2);
  63. paymentRecork.setPaymentStatus(2);
  64. break;
  65. case "WAIT_BUYER_PAY": // 交易创建并等待买家付款
  66. orderDetailPay.setPayStatus(0);
  67. paymentRecork.setPaymentStatus(0);
  68. break;
  69. default:
  70. break;
  71. }
  72. int returnResult=orderDetailService.updateOrderDetail(orderDetailPay); //更新订单详情表中状态
  73. paymentRecorkService.insertPaymentRecork(paymentRecork); //增加订单记录表
  74. if(tradeStatus.equals("TRADE_SUCCESS")) { //只处理支付成功的订单: 修改交易表状态,支付成功
  75. if(returnResult>0){
  76. return "success";
  77. }else{
  78. return "fail";
  79. }
  80. }else{
  81. return "fail";
  82. }
  83. }else{
  84. log.info("==================支付宝官方建议校验的值(out_trade_no、total_amount、sellerId、app_id),不一致!返回fail");
  85. return"fail";
  86. }
  87. }else {
  88. CrowdfundingOrder productCrowdOrder=crowdfundingOrderService.selectCrowdfundingOrderByOrderNo(outTradeNo);
  89. Integer productId = productCrowdOrder.getProductId();
  90. CpcProduct cpcProduct = cpcProductService.selectCpcProductById(productId);
  91. BigDecimal currentCrowd = cpcProduct.getCrowdCurrent(); //产品订单前已融资数目
  92. if (currentCrowd == null){
  93. currentCrowd =BigDecimal.valueOf(0);
  94. }
  95. BigDecimal targetCrowd = cpcProduct.getCrowdTarget(); //产品目标融资
  96. BigDecimal orderCrowd = productCrowdOrder.getOrderAmount(); //订单金额
  97. BigDecimal leftFunds = targetCrowd.subtract(currentCrowd); //剩余带融资数
  98. if (orderCrowd.compareTo(leftFunds) == 0){ //等于,更新产品表,及其产品状态
  99. currentCrowd = currentCrowd.add(orderCrowd);
  100. cpcProduct.setCrowdCurrent(currentCrowd);
  101. cpcProduct.setProductStatus(1);
  102. cpcProduct.setUpdateBy(productCrowdOrder.getCreateBy());
  103. }else{
  104. currentCrowd = currentCrowd.add(orderCrowd);
  105. cpcProduct.setCrowdCurrent(currentCrowd);
  106. cpcProduct.setUpdateBy(productCrowdOrder.getCreateBy());
  107. }
  108. if(totalAmount.equals(productCrowdOrder.getOrderAmount().toString())&&AlipayConfig.app_id.equals(appId)){
  109. //修改数据库相应订单详情表,保存订单记录
  110. //保存支付记录表
  111. Integer userId =productCrowdOrder.getCreateUserId();
  112. PaymentRecork paymentRecork = new PaymentRecork();
  113. paymentRecork.setUserId(userId);
  114. paymentRecork.setAmount(BigDecimal.valueOf(Double.parseDouble(totalAmount)));
  115. paymentRecork.setPayModule(Integer.parseInt(payMoudle));
  116. paymentRecork.setBusinessId(productCrowdOrder.getOrderId());
  117. switch (tradeStatus) // 判断交易结果
  118. {
  119. case "TRADE_FINISHED": // 交易结束并不可退款
  120. paymentRecork.setPaymentStatus(3);
  121. break;
  122. case "TRADE_SUCCESS": // 交易支付成功
  123. paymentRecork.setPaymentStatus(1);
  124. cpcProductService.updateCpcProduct(cpcProduct);
  125. productCrowdOrder.setCrowdfundingStatus(1);
  126. break;
  127. case "TRADE_CLOSED": // 未付款交易超时关闭或支付完成后全额退款
  128. paymentRecork.setPaymentStatus(2);
  129. break;
  130. case "WAIT_BUYER_PAY": // 交易创建并等待买家付款
  131. paymentRecork.setPaymentStatus(0);
  132. break;
  133. default:
  134. break;
  135. }
  136. int returnResult=crowdfundingOrderService.updateCrowdfundingOrder(productCrowdOrder); //更新订单详情表中状态
  137. paymentRecorkService.insertPaymentRecork(paymentRecork); //增加订单记录表
  138. if(tradeStatus.equals("TRADE_SUCCESS")) { //只处理支付成功的订单: 修改交易表状态,支付成功
  139. if(returnResult>0){
  140. return "success";
  141. }else{
  142. return "fail";
  143. }
  144. }else{
  145. return "fail";
  146. }
  147. }else{
  148. log.info("==================支付宝官方建议校验的值(out_trade_no、total_amount、sellerId、app_id),不一致!返回fail");
  149. return"fail";
  150. }
  151. }
  152. } else { //验签不通过
  153. log.info("==================验签不通过 !");
  154. return "fail";
  155. }
  156. }

controller层,设置noticefy接口,供支付宝异步回调时可访问

  1. /**
  2. * 获取支付宝异步通知订单情况,更新订单表
  3. */
  4. @Log(title = "订单", businessType = BusinessType.UPDATE)
  5. @PostMapping("/notifyUrl")
  6. @ResponseBody
  7. public String notify(HttpServletRequest request, HttpServletResponse response)throws IOException
  8. {
  9. //1.从支付宝回调的request域中取值
  10. //获取支付宝返回的参数集合
  11. Map<String, String[]> aliParams = request.getParameterMap();
  12. //用以存放转化后的参数集合
  13. Map<String, String> conversionParams = new HashMap<String, String>();
  14. for (Iterator<String> iter = aliParams.keySet().iterator(); iter.hasNext();) {
  15. String key = iter.next();
  16. String[] values = aliParams.get(key);
  17. String valueStr = "";
  18. for (int i = 0; i < values.length; i++) {
  19. valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
  20. }
  21. // 乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
  22. // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "uft-8");
  23. conversionParams.put(key, valueStr);
  24. }
  25. log.info("==================返回参数集合:"+conversionParams);String status = aliPayService.notify(conversionParams); //payMoudle 1易采购,2产品筹
  26. return status;
  27. }

支付宝异步回调时只有当我们返回给他们“success”时才会停止访问,否则他会分阶段请求notify网址。一天之内请求8次。
3)实现第三个接口:在支付完成之后,跳转回APP时,APP调用商户后台进行最终付款校验。我把主要的处理逻辑写在Service层了,Controller层直接调用就可以,这里就不放Controller层的代码了。

  1. /**
  2. * 向支付宝发起订单查询请求
  3. * @param request
  4. * @return
  5. * @throws IOException
  6. */
  7. @Override
  8. public Byte checkAlipay(String outTradeNo) {
  9. logger.info("==================向支付宝发起查询,查询商户订单号为:"+outTradeNo);
  10. try {
  11. //实例化客户端(参数:网关地址、商户appid、商户私钥、格式、编码、支付宝公钥、加密类型)
  12. AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID,
  13. AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET,
  14. AlipayConfig.ALIPAY_PUBLIC_KEY,AlipayConfig.SIGNTYPE);
  15. AlipayTradeQueryRequest alipayTradeQueryRequest = new AlipayTradeQueryRequest();
  16. alipayTradeQueryRequest.setBizContent("{" +
  17. "\"out_trade_no\":\""+outTradeNo+"\"" +
  18. "}");
  19. AlipayTradeQueryResponse alipayTradeQueryResponse = alipayClient.execute(alipayTradeQueryRequest);
  20. if(alipayTradeQueryResponse.isSuccess()){
  21. AlipaymentOrder alipaymentOrder=this.selectByOutTradeNo(outTradeNo);
  22. //修改数据库支付宝订单表
  23. alipaymentOrder.setTradeNo(alipayTradeQueryResponse.getTradeNo());
  24. alipaymentOrder.setBuyerLogonId(alipayTradeQueryResponse.getBuyerLogonId());
  25. alipaymentOrder.setTotalAmount(Double.parseDouble(alipayTradeQueryResponse.getTotalAmount()));
  26. alipaymentOrder.setReceiptAmount(Double.parseDouble(alipayTradeQueryResponse.getReceiptAmount()));
  27. alipaymentOrder.setInvoiceAmount(Double.parseDouble(alipayTradeQueryResponse.getInvoiceAmount()));
  28. alipaymentOrder.setBuyerPayAmount(Double.parseDouble(alipayTradeQueryResponse.getBuyerPayAmount()));
  29. switch (alipayTradeQueryResponse.getTradeStatus()) // 判断交易结果
  30. {
  31. case "TRADE_FINISHED": // 交易结束并不可退款
  32. alipaymentOrder.setTradeStatus((byte) 3);
  33. break;
  34. case "TRADE_SUCCESS": // 交易支付成功
  35. alipaymentOrder.setTradeStatus((byte) 2);
  36. break;
  37. case "TRADE_CLOSED": // 未付款交易超时关闭或支付完成后全额退款
  38. alipaymentOrder.setTradeStatus((byte) 1);
  39. break;
  40. case "WAIT_BUYER_PAY": // 交易创建并等待买家付款
  41. alipaymentOrder.setTradeStatus((byte) 0);
  42. break;
  43. default:
  44. break;
  45. }
  46. this.updateByPrimaryKey(alipaymentOrder); //更新表记录
  47. return alipaymentOrder.getTradeStatus();
  48. } else {
  49. logger.info("==================调用支付宝查询接口失败!");
  50. }
  51. } catch (AlipayApiException e) {
  52. // TODO Auto-generated catch block
  53. e.printStackTrace();
  54. }
  55. return 0;
  56. }

4)退款处理
将订单号传入,请求支付宝退款接口进行退款,这里的payMoudle参数和ordertail参数可根据实际需求增加或者去掉
service层

  1. //退款消息处理
  2. @Override
  3. @Transactional(rollbackFor = Exception.class)
  4. public AjaxResult reFund(AlipayTradeRefundResponse respose, Integer payMoudle,OrderDetail orderDetail){
  5. if (respose.getCode().equals("10000")){
  6. //更新支付记录状态
  7. try {
  8. PaymentRecork paymentRecork = paymentRecorkService.selectPaymentRecorkByBusinessId(orderDetail.getOrderDetailId());
  9. paymentRecork.setPaymentStatus(2);
  10. RefundRecord refundRecord = new RefundRecord();
  11. refundRecord.setBuyerId(paymentRecork.getUserId());
  12. refundRecord.setSellerId(orderDetail.getProduct().getPublisherId());
  13. refundRecord.setAmount(paymentRecork.getAmount());
  14. refundRecord.setPayModule(payMoudle);
  15. refundRecord.setBusinessId(paymentRecork.getBusinessId());
  16. refundRecord.setRefundReason(JSON.parseObject(respose.getParams().get("biz_content")).get("refund_reason").toString());
  17. refundRecord.setRefundStatus(orderDetail.getOrderStatus());
  18. refundRecord.setRefundReason("退款");
  19. if (refundRecordService.selectRefundRecordByBussinessId(orderDetail.getOrderDetailId())!=null){
  20. paymentRecorkService.updatePaymentRecork(paymentRecork);
  21. refundRecordService.updateRefundRecord(refundRecord);
  22. }else {
  23. paymentRecorkService.updatePaymentRecork(paymentRecork);
  24. refundRecordService.insertRefundRecord(refundRecord);
  25. }
  26. return AjaxResult.success("退款成功");
  27. }catch (Exception e){
  28. log.info("退款请求出错");
  29. return AjaxResult.success("退款成功"); //todo bug 退款记录重复
  30. }
  31. }else {
  32. return AjaxResult.error("退款失败,请求出错");
  33. }
  34. }

controller层

  1. //商家同意退款
  2. @Log(title = "订单", businessType = BusinessType.INSERT)
  3. @ResponseBody
  4. @PostMapping("/reFund")
  5. public AjaxResult refund(Integer orderDetailId) {
  6. OrderDetail orderDetail = orderDetailService.selectOrderDetailById(orderDetailId);
  7. //获得初始化的AlipayClient
  8. try{
  9. AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id,
  10. AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key,
  11. AlipayConfig.sign_type);
  12. //设置请求参数
  13. AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();
  14. alipayRequest.setReturnUrl(AlipayConfig.return_url);
  15. alipayRequest.setNotifyUrl(AlipayConfig.notify_url);
  16. //商户订单号,商户网站订单系统中唯一订单号
  17. String out_trade_no = orderDetail.getOrderNo();
  18. //支付宝交易号 暂不用
  19. String trade_no = "";
  20. //请二选一设置
  21. //需要退款的金额,该金额不能大于订单金额,必填
  22. BigDecimal refund_amount = orderDetail.getOrderAmount();
  23. //退款的原因说明
  24. String refund_reason = "买家要求退款,卖家同意";
  25. //标识一次退款请求,同一笔交易多次退款需要保证唯一,如需部分退款,则此参数必传
  26. String out_request_no = "";
  27. alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
  28. + "\"trade_no\":\""+ trade_no +"\","
  29. + "\"refund_amount\":\""+ refund_amount +"\","
  30. + "\"refund_reason\":\""+ refund_reason +"\","
  31. + "\"out_request_no\":\""+ out_request_no +"\"}");
  32. // //请求
  33. // String result = alipayClient.pageExecute(alipayRequest).getBody();
  34. //get请求方式,返回http链接
  35. AlipayTradeRefundResponse respose = alipayClient.execute(alipayRequest,"get");
  36. AjaxResult result = aliPayService.reFund(respose,1,orderDetail);
  37. return result;
  38. }
  39. catch (AlipayApiException e){
  40. e.printStackTrace();
  41. log.info("与支付宝交互出错,未能退款,请检查代码!");
  42. return AjaxResult.error("与支付宝交互错误,请联系管理员");
  43. }
  44. }

5,参考文档

第三方授权
https://docs.open.alipay.com/20160728150111277227/intro
第三方沙盒
https://openclub.alipay.com/read.php?tid=1649&fid=43
电脑网站支付指南
https://openclub.alipay.com/read.php?tid=4869&fid=58
//后台逻辑
https://blog.csdn.net/Ouyzc/article/details/79551714
https://segmentfault.com/a/1190000012700062
//自查错误网站
https://openclub.alipay.com/read.php?tid=4869&fid=58#anchor12