|
@@ -303,6 +303,23 @@ spring:
|
|
|
|
|
|
#### 1.3.5 spzx-payment-dev.yml
|
|
|
|
|
|
+在共享配置文件`application-dev.yml`中新增rabbitMQ信息
|
|
|
+
|
|
|
+```yaml
|
|
|
+spring:
|
|
|
+ rabbitmq:
|
|
|
+ host: 192.168.200.10
|
|
|
+ port: 5672
|
|
|
+ username: guest
|
|
|
+ password: guest
|
|
|
+ publisher-confirm-type: CORRELATED #开启生产者确认机制
|
|
|
+ publisher-returns: true #开启生产者退回机制
|
|
|
+ listener:
|
|
|
+ simple:
|
|
|
+ acknowledge-mode: manual #默认情况下消息消费者是自动确认消息的,如果要手动确认消息则需要修改确认模式为manual
|
|
|
+ prefetch: 1 # 消费者每次从队列获取的消息数量。此属性当不设置时为:轮询分发,设置为1为:公平分发
|
|
|
+```
|
|
|
+
|
|
|
在nacos上添加商品服务配置文件
|
|
|
|
|
|
```yaml
|
|
@@ -315,24 +332,13 @@ mybatis-plus:
|
|
|
spring:
|
|
|
data:
|
|
|
redis:
|
|
|
- host: localhost
|
|
|
+ host: 192.168.200.10
|
|
|
port: 6379
|
|
|
- password:
|
|
|
- rabbitmq:
|
|
|
- host: localhost
|
|
|
- port: 5672
|
|
|
- username: guest
|
|
|
- password: guest
|
|
|
- publisher-confirm-type: CORRELATED
|
|
|
- publisher-returns: true
|
|
|
- listener:
|
|
|
- simple:
|
|
|
- cknowledge-mode: manual #默认情况下消息消费者是自动确认消息的,如果要手动确认消息则需要修改确认模式为manual
|
|
|
- prefetch: 1 # 消费者每次从队列获取的消息数量。此属性当不设置时为:轮询分发,设置为1为:公平分发
|
|
|
+ password:
|
|
|
datasource:
|
|
|
type: com.zaxxer.hikari.HikariDataSource
|
|
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
|
|
- url: jdbc:mysql://127.0.0.1:3306/spzx-payment?characterEncoding=utf-8&useSSL=false
|
|
|
+ url: jdbc:mysql://192.168.200.6:3306/spzx-payment?characterEncoding=utf-8&useSSL=false
|
|
|
username: root
|
|
|
password: root
|
|
|
hikari:
|
|
@@ -664,12 +670,12 @@ com.spzx.order.api.factory.RemoteOrderInfoFallbackFactory
|
|
|
|
|
|
```yaml
|
|
|
alipay:
|
|
|
- alipay_url: https://openapi.alipay.com/gateway.do
|
|
|
- app_id: 2021001163617452
|
|
|
- app_private_key: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8Z7EZmanxyFGsK4LrIUeKKrrGxWAHIgPmUV8TtZDs+jeplJSw1ckSY63QhEU444D5qd6xruJHBuB33HG+ik4n8N8nRWi3AtMgpC061oq2DcgtIKMmQHO7/poYDwbpDZrOWXIyiNshFfUOSTUpnrS8UvEks6n6xR/G72r2FG07oZzO7g3XsPMr73wpYajMYC/bhTm6CJGEWZikONNDFkQpVHa+zgitwsqlBuvBvVwGwOHA9B8aRfokwAMl6BDXKoH8BNnSEMpWSTRSwbssayXAQWNU7XKDKGozbn4U2dEbl8GCFzikI/T7ybTNm5gs46ZZBGlq/YB4+v4D3t74Vl6nAgMBAAECggEAOidzhehliYkAlLk1huhV0bMQxewEkQ8RzxTM2SORIWS2q7R+FPtYPkHgU92QFFg85lNltsi5dZ0MylKUFXFRYIi8CL4m7V6E1q12fJPeawVkBXHuig8Y6i1TWRvCUUtuvkTjt++AW/0QECHOtBMVzI95eY+vZwVToq8h/+UcNmxKyVt66Qpo4+r+cUvlvGX5mXgQVC5Ftf/MtHA1i+kjtzBITC0xAvmSXKzjN1YhtcS9rXyMHXBiFhXLdmvOXjkn0Okosr2+tmesXfSwDGhH3ZlOdHzit4D602RNl0nTA1dOUWHuCncs1TrWbriax86P/EYvmzMiHWCVTmmNJC0bMQKBgQD0HAXKNsYsdjCQOV4t3SMqOKaul67x/KA20PmMZVfQ2sQkyjyFgWpL8C16Rzf3zI7df+zF5SkvhFY4+LRZVwX5okEFYTzAZ/NYouj1/DABYOPq0E0sY18/xtq7FJ/CIk8qmCqcczqoyaoxoaC1zAt9E4CYE89iEOnO+GhcI3H3LwKBgQDFlQzvbXhWRyRFkeft/a52XLnyj6t9iP7wNGbGCSeoMDrAu3ZgoqacUPWj5MgSFZdT48H9rF4pPixXoe3jfUNsWBUHqD1F2drDz7lpL0PbpSsgy6ei+D4RwTADsuyXwrkvrWrGro+h6pNJFyly3nea/gloDtJTzfhFFwtNfmqyCQKBgBXzMx4UwMscsY82aV6MZO4V+/71CrkdszZaoiXaswPHuB1qxfhnQ6yiYyR8pO62SR5ns120Fnj8WFh1HJpv9cyVp20ZakIO1tXgiDweOh7VnIjvxBC6usTcV6y81QS62w2Ec0hwIBUvVQtzciUGvP25NDX4igxSYwPGWHP4h/XnAoGAcQN2aKTnBgKfPqPcU4ac+drECXggESgBGof+mRu3cT5U/NS9Oz0Nq6+rMVm1DpMHAdbuqRikq1aCqoVWup51qE0hikWy9ndL6GCynvWIDOSGrLWQZ2kyp5kmy5bWOWAJ6Ll6r7Y9NdIk+NOkw614IFFaNAj2STUw4uPxdRvwD3ECgYEArwOZxR3zl/FZfsvVCXfK8/fhuZXMOp6Huwqky4tNpVLvOyihpOJOcIFj6ZJhoVdmiL8p1/1S+Sm/75gx1tpFurKMNcmYZbisEC7Ukx7RQohZhZTqMPgizlVBTu5nR3xkheaJC9odvyjrWQJ569efXo30gkW04aBp7A15VNG5Z/U=
|
|
|
- alipay_public_key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkWs+3gXMosiWG+EbfRyotWB0waqU3t7qMQSBxU0r3JZoND53jvWQfzrGZ8W+obMc+OgwupODDVxhG/DEKVBIptuUQYdvAjCSH98m2hclFcksspuCy9xS7PyflPE47pVzS6vA3Slvw5OFQ2qUcku4paWnBxguLUGPjEncij5NcyFyk+/k57MmrVJwCZaI+lFOS3Eq2IXc07tWXO4s/2SWr3EJiwJutOGBdA1ddvv1Urrl0pWpEFg30pJB6J7YteuxdEL90kuO5ed/vnTK5qgQRvEelROkUW44xONk1784v28OJXmGICmNL1+KyM/SFbFOSgJZSV1tEXUzvL/xvzFpLwIDAQAB
|
|
|
- return_payment_url: http://ry-spzx.atguigu.cn/#/pages/money/paySuccess
|
|
|
- notify_payment_url: http://ry-spzx-api.atguigu.cn/payment/alipay/callback/notify
|
|
|
+ alipay_url: https://openapi-sandbox.dl.alipaydev.com/gateway.do
|
|
|
+ app_id: 9021000123601169
|
|
|
+ app_private_key: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCW770BcTA49wE/YTTyCRpms01xycGmaH/LrCulrn7BeEJdS6xhUDVmpYF3kSdaivZTfPE3vbA/K7BBcWCp3/ouVlIRp73h+pujwNQBx6OYxgoZcxmGLDuu4vZcdQxTq1bwmPmwiDaajA6Qwy4bYVrsJead6Oup/VcrvQ7PxZXYNsCkEe2rCh01RxPErvcic1CNuCPHtJBtEQOIatTjyC9avuywkH6PwDwTSuK+lt0MOlVMYYdfeWwndke4WqDpZzNYrsubrpOi+cVxpSZT+UNZgOBW9n/8QmUb0MmFtgGe295YzbV358j0/gaYbvBl2nmEFTACoO8x6IlFFfJ6v7GPAgMBAAECggEACncU48S1Uz3NGFflE+MNsZE9DWavxpn4Qmbtn885erCb6VwV16l4CmXvVBr8X4rDCYbgMr00Oy0MS31Q9nZRImVZy1+WCtUVdYUpLfZpoW4ZxSn12xe2C6ow6rgy4po+rO/uYA8RuiV73Q2rZLqbU0RNeSVtMZwfLyksnbtVaGhIMNxvbPWf46NFRiq0zWFeW4MtGd+EdsvTyUg5pW+kvcnbd/6vPICpcrgYERxXg4PEVFm/F3MgDvmD8VhPGMERT7ojWIwLSNaYrlfALQFOOTsftg8mzf7iBB39BT3wXjJ0hwPQa/fa8L7Gf8ZDXotZX9/cphmAJZh5IwKSzWOAAQKBgQDEoCuhR8s7oMXG+yjUZmH4zd27UQFbgfi/ROMR1BM9hC9Md9VrpimgfSq4sTKdaMUs+Kx2HZ+qexvDINuR/kBjssFqNopP6ocr1NqpIkNoJvdapfPDwNvr5owCV11jGMDHtPYstCjR9nAQjkvlB+UBmkuPIP9XTSlxxEqjtrOZAQKBgQDEg6OT+JhMU2z3hetgeerShEYXgM5Xt7K2dG5bh/jg2yWjuQ9mHPltbJLv1/XKdqcxyP+wvTf7Pr5o8AunMlqGltwUn8R/1v1U08CoMaB9wjxmE1QJ6fP+cpBuqTjA/rxJfJyGEpqE7q7nsuH3Q+PlQLQwxVdTb/qn+6CdMMM6jwKBgQCiZvDiaxwPigEREo1ssYumJtNHTebjH/zGbi1molWp763paOjb2whyyC8rESCPJuQE+vfnLxybgMczacYxVWD1d8WM5rNiwoEW0N4x6Fzof5a6RmuKeZarx2ZI4MP/f4hq6qqRzdQRZ4yz//PrnZOmuTxDYavE/bSHhOtpQRQiAQKBgQCEexKOMSvLWPq/LENweQC8B3yRX768GBWdN4Umo9Y/EL6hT6N3/dSR1n6AvyDu62rv/0eRSR8yfCw15o6Umze4QIACT2tTnAaRZ2gce8hsv8OkNSeUv2pEgcupLuAaDCG3kRCcunH9aEP14/y+i8BZz3Mq5f9nOrjLArJXBoa3NQKBgETSQyLlceqC9m0qZYQBBycS7KGldOV+vTILOLQafWp3OPNc2kuLd7yQspj8WjAJBkhrcqmw6Pf7tt5E6XRTi6+Z47Zc/PIDSixhYRotdkaELxCY5P3gmAtRNUmP/hv9WZJyGygxAUO9mC/CZkAjWg0JM7yOl3f7+n8utIahKHAM
|
|
|
+ alipay_public_key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwoLuiTrn/cTUqv147Bzu9jl0heV3P+dlISB4UulkR8NSQ66h+WGROn/Y6FxUNBiz5++mS9wOpYA2fFikueTeaC5s2kNtDnO9Gb6eduX9sOi/cDbu5kaedwzld4Zg4kLYm304EVILAah8U83Go7VngbeV+55RG700mAm+DSd3GVCRXnlUsDilxK+IfzOhvLbMGWSOsHy0K9IwDI+HfHKW6iN5jW5NE+mQMbyUKquPg12EnBkXtnj7i8q4FY1o3+jbUt906tXMKTWPBOG3JwqRv2ndaB71mQB9Ae4wcCdREco5RnnRbg8kMJzkGApDicsr9+TmjB16vJ5+CB+hce7LFwIDAQAB
|
|
|
+ return_payment_url: http://localhost:81/#/pages/money/paySuccess
|
|
|
+ notify_payment_url: http://zf896e63.natappfree.cc/payment/alipay/callback/notify
|
|
|
```
|
|
|
|
|
|
沙箱版本
|
|
@@ -761,13 +767,18 @@ public class AlipayController extends BaseController {
|
|
|
@Autowired
|
|
|
private IAlipayService alipayService;
|
|
|
|
|
|
- @Operation(summary = "支付宝下单")
|
|
|
- @RequiresLogin
|
|
|
- @RequestMapping("/submitAlipay/{orderNo}")
|
|
|
- @ResponseBody
|
|
|
- public AjaxResult submitAlipay(@PathVariable(value = "orderNo") String orderNo) {
|
|
|
- String form = alipayService.submitAlipay(orderNo);
|
|
|
- return success(form);
|
|
|
+ /**
|
|
|
+ * 对接支付宝返回支付表单用于H5端支付
|
|
|
+ * @param orderNo
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ @Operation(summary = "对接支付宝返回支付表单用于H5端支付")
|
|
|
+ @GetMapping("/submitAlipay/{orderNo}")
|
|
|
+ public AjaxResult submitAlipay(@PathVariable String orderNo){
|
|
|
+ //1.获取支付宝支付表单
|
|
|
+ String payForm = alipayService.submitAlipay(orderNo);
|
|
|
+ //2.响应支付宝支付表单,用于H5端支付
|
|
|
+ return success(payForm);
|
|
|
}
|
|
|
|
|
|
}
|
|
@@ -802,36 +813,51 @@ public class AlipayServiceImpl implements IAlipayService {
|
|
|
@Autowired
|
|
|
private IPaymentInfoService paymentInfoService;
|
|
|
|
|
|
- @SneakyThrows
|
|
|
+ /**
|
|
|
+ * 对接支付宝返回支付表单用于H5端支付
|
|
|
+ *
|
|
|
+ * @param orderNo
|
|
|
+ * @return
|
|
|
+ */
|
|
|
@Override
|
|
|
public String submitAlipay(String orderNo) {
|
|
|
- //保存支付记录
|
|
|
- PaymentInfo paymentInfo = paymentInfoService.savePaymentInfo(orderNo);
|
|
|
- // 创建请求对象
|
|
|
- AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();//创建API对应的request
|
|
|
- // 同步回调
|
|
|
- alipayRequest.setReturnUrl(AlipayConfig.return_payment_url);
|
|
|
- // 异步回调
|
|
|
- alipayRequest.setNotifyUrl(AlipayConfig.notify_payment_url);
|
|
|
-
|
|
|
- /******必传参数******/
|
|
|
- JSONObject bizContent = new JSONObject();
|
|
|
- //商户订单号,商家自定义,保持唯一性
|
|
|
- bizContent.put("out_trade_no", paymentInfo.getOrderNo());
|
|
|
- //支付金额,最小值0.01元
|
|
|
- //bizContent.put("total_amount", paymentInfo.getAmount());
|
|
|
- bizContent.put("total_amount", 0.01);
|
|
|
-
|
|
|
- //订单标题,不可使用特殊符号
|
|
|
- bizContent.put("subject", paymentInfo.getContent());
|
|
|
- /******可选参数******/
|
|
|
- //手机网站支付默认传值QUICK_WAP_WAY
|
|
|
- bizContent.put("product_code", "QUICK_WAP_WAY");
|
|
|
-
|
|
|
- alipayRequest.setBizContent(bizContent.toString());
|
|
|
- AlipayTradeWapPayResponse response = alipayClient.pageExecute(alipayRequest,"POST");
|
|
|
-
|
|
|
- return response.getBody(); //调用SDK生成表单;
|
|
|
+ try {
|
|
|
+ //1.保存本地交易记录
|
|
|
+ PaymentInfo paymentInfo = paymentInfoService.savePaymentInfo(orderNo);
|
|
|
+ if (!"0".equals(paymentInfo.getPaymentStatus())) {
|
|
|
+ throw new ServiceException("本地交易记录支付状态有误");
|
|
|
+ }
|
|
|
+ //2.对接支付宝服务端,产生一笔支付宝交易,获取支付宝支付表单
|
|
|
+ // 创建请求对象
|
|
|
+ AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();//创建API对应的request
|
|
|
+ // 同步回调 应用引导用户去到成功页面
|
|
|
+ alipayRequest.setReturnUrl(AlipayConfig.return_payment_url);
|
|
|
+ // 异步回调 用户付款成功后,支付宝异步通知商户系统
|
|
|
+ alipayRequest.setNotifyUrl(AlipayConfig.notify_payment_url);
|
|
|
+
|
|
|
+ /******必传参数******/
|
|
|
+ JSONObject bizContent = new JSONObject();
|
|
|
+ //商户订单号,商家自定义,保持唯一性
|
|
|
+ bizContent.put("out_trade_no", paymentInfo.getOrderNo());
|
|
|
+ //支付金额,最小值0.01元
|
|
|
+ //bizContent.put("total_amount", paymentInfo.getAmount());
|
|
|
+ bizContent.put("total_amount", paymentInfo.getAmount());
|
|
|
+
|
|
|
+ //订单标题,不可使用特殊符号
|
|
|
+ bizContent.put("subject", paymentInfo.getContent());
|
|
|
+ /******可选参数******/
|
|
|
+ //手机网站支付默认传值QUICK_WAP_WAY
|
|
|
+ bizContent.put("product_code", "QUICK_WAP_WAY");
|
|
|
+
|
|
|
+ alipayRequest.setBizContent(bizContent.toString());
|
|
|
+ AlipayTradeWapPayResponse response =
|
|
|
+ alipayClient.pageExecute(alipayRequest, "POST");
|
|
|
+
|
|
|
+ return response.getBody(); //调用SDK生成表单;
|
|
|
+ } catch (AlipayApiException e) {
|
|
|
+ log.error("支付宝支付异常:{}", e);
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
```
|
|
@@ -848,40 +874,51 @@ PaymentInfo savePaymentInfo(String orderNo);
|
|
|
@Autowired
|
|
|
private RemoteOrderInfoService remoteOrderInfoService;
|
|
|
|
|
|
+/**
|
|
|
+ * 保存本地交易记录信息
|
|
|
+ *
|
|
|
+ * @param orderNo
|
|
|
+ * @return
|
|
|
+ */
|
|
|
@Override
|
|
|
+@Transactional(rollbackFor = Exception.class)
|
|
|
public PaymentInfo savePaymentInfo(String orderNo) {
|
|
|
-
|
|
|
- //防止重复提交:如果支付日志已存在则直接返回
|
|
|
- PaymentInfo paymentInfo = baseMapper.selectOne(
|
|
|
- new LambdaQueryWrapper<PaymentInfo>().eq(PaymentInfo::getOrderNo, orderNo)
|
|
|
+ //1.根据订单编号查询本地记录记录,存在则返回
|
|
|
+ PaymentInfo paymentInfo = this.getOne(
|
|
|
+ new LambdaQueryWrapper<PaymentInfo>()
|
|
|
+ .eq(PaymentInfo::getOrderNo, orderNo)
|
|
|
);
|
|
|
-
|
|
|
- if(paymentInfo != null) {
|
|
|
+ if (paymentInfo != null) {
|
|
|
return paymentInfo;
|
|
|
}
|
|
|
-
|
|
|
- //根据订单号获取订单信息
|
|
|
- R<OrderInfo> orderInfoResult = remoteOrderInfoService.getByOrderNo(orderNo, SecurityConstants.INNER);
|
|
|
- if (R.FAIL == orderInfoResult.getCode()) {
|
|
|
- throw new ServiceException(orderInfoResult.getMsg());
|
|
|
+ //2.构建本地交易记录对象 paymentInfo
|
|
|
+ PaymentInfo paymentInfo1 = new PaymentInfo();
|
|
|
+ paymentInfo1.setCreateBy(SecurityContextHolder.getUserName());
|
|
|
+ paymentInfo1.setUserId(SecurityContextHolder.getUserId());
|
|
|
+ paymentInfo1.setOrderNo(orderNo);
|
|
|
+ paymentInfo1.setPayType(2);
|
|
|
+ //3.根据订单编号远程调用订单服务获取订单及订单明细信息
|
|
|
+ R<OrderInfo> r = remoteOrderInfoService.getByOrderNo(orderNo, SecurityConstants.INNER);
|
|
|
+ if (R.FAIL == r.getCode()) {
|
|
|
+ throw new RuntimeException("远程调用订单服务失败,原因:" + r.getMsg());
|
|
|
+ }
|
|
|
+ OrderInfo orderInfo = r.getData();
|
|
|
+ //4. 判断订单状态,如果是已支付或者已关闭,则直接返回
|
|
|
+ if (orderInfo.getOrderStatus() != 0) {
|
|
|
+ throw new RuntimeException("订单状态异常,不能进行支付");
|
|
|
}
|
|
|
- OrderInfo orderInfo = orderInfoResult.getData();
|
|
|
-
|
|
|
- paymentInfo = new PaymentInfo();
|
|
|
- paymentInfo.setUserId(orderInfo.getUserId());
|
|
|
- String content = orderInfo.getOrderItemList()
|
|
|
- .stream()
|
|
|
- // 将每个 OrderItem 变换为其 SKU 名称
|
|
|
- .map(OrderItem::getSkuName)
|
|
|
- // 使用空格连接所有 SKU 名称
|
|
|
- .collect(Collectors.joining(" "));
|
|
|
-
|
|
|
- paymentInfo.setContent(content);
|
|
|
- paymentInfo.setAmount(orderInfo.getTotalAmount());
|
|
|
- paymentInfo.setOrderNo(orderNo);
|
|
|
- paymentInfo.setPaymentStatus(0);
|
|
|
- baseMapper.insert(paymentInfo);
|
|
|
- return paymentInfo;
|
|
|
+ paymentInfo1.setAmount(orderInfo.getTotalAmount());
|
|
|
+ paymentInfo1.setContent(orderInfo.getOrderItemList().get(0).getSkuName());
|
|
|
+ paymentInfo1.setPaymentStatus("0");
|
|
|
+
|
|
|
+ //TODO 支付宝端交易编号、回调时间、回调内容 等 用户付款成功,支付宝会回调商户系统再更新三个字段
|
|
|
+ //paymentInfo1.setTradeNo();
|
|
|
+ //paymentInfo1.setCallbackTime();
|
|
|
+ //paymentInfo1.setCallbackContent();
|
|
|
+
|
|
|
+ //4.保存本地交易记录
|
|
|
+ this.save(paymentInfo1);
|
|
|
+ return paymentInfo1;
|
|
|
}
|
|
|
```
|
|
|
|
|
@@ -894,11 +931,16 @@ public PaymentInfo savePaymentInfo(String orderNo) {
|
|
|
#### 1.8.1 AlipayController
|
|
|
|
|
|
```java
|
|
|
-@RequestMapping("/callback/notify")
|
|
|
-@ResponseBody
|
|
|
-public String alipayNotify(@RequestParam Map<String, String> paramMap, HttpServletRequest request) {
|
|
|
- log.info("AlipayController...alipayNotify方法执行了...");
|
|
|
- return "success" ;
|
|
|
+/**
|
|
|
+ * 支付宝回调:用户付款成功后,支付宝会通知商户系统支付结果
|
|
|
+ * @param paramMap
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+@Operation(summary = "用户付款成功后,支付宝会通知商户系统支付结果")
|
|
|
+@PostMapping("/callback/notify")
|
|
|
+public String paySuccessNotify(@RequestParam Map<String, String> paramMap){
|
|
|
+ String result = alipayService.paySuccessNotify(paramMap);
|
|
|
+ return result;
|
|
|
}
|
|
|
```
|
|
|
|
|
@@ -989,94 +1031,76 @@ authtoken信息获取:
|
|
|
##### AlipayController
|
|
|
|
|
|
```java
|
|
|
-@Autowired
|
|
|
-private IPaymentInfoService paymentInfoService;
|
|
|
-
|
|
|
-@Autowired
|
|
|
-private RemoteOrderInfoService remoteOrderInfoService;
|
|
|
-
|
|
|
-@Autowired
|
|
|
-private RabbitService rabbitService;
|
|
|
-
|
|
|
+/**
|
|
|
+ * 支付宝回调:用户付款成功后,支付宝会通知商户系统支付结果
|
|
|
+ * @param paramMap
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+@Operation(summary = "用户付款成功后,支付宝会通知商户系统支付结果")
|
|
|
@PostMapping("/callback/notify")
|
|
|
-@ResponseBody
|
|
|
-public String alipayNotify(@RequestParam Map<String, String> paramMap) {
|
|
|
+public String paySuccessNotify(@RequestParam Map<String, String> paramMap){
|
|
|
+ String result = alipayService.paySuccessNotify(paramMap);
|
|
|
+ return result;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+##### AlipayService
|
|
|
|
|
|
- log.info("alipayNotify方法执行了...");
|
|
|
+```java
|
|
|
+/**
|
|
|
+ * 支付宝回调:用户付款成功后,支付宝会通知商户系统支付结果
|
|
|
+ * @param paramMap
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+String paySuccessNotify(Map<String, String> paramMap);
|
|
|
+```
|
|
|
|
|
|
- String result = "failure";
|
|
|
+##### AlipayServiceImpl
|
|
|
|
|
|
+```java
|
|
|
+/**
|
|
|
+ * 支付宝回调:用户付款成功后,支付宝会通知商户系统支付结果
|
|
|
+ *
|
|
|
+ * @param paramMap
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+@Override
|
|
|
+public String paySuccessNotify(Map<String, String> paramMap) {
|
|
|
try {
|
|
|
- //调用SDK验证签名
|
|
|
- boolean signVerified = AlipaySignature.rsaCheckV1(
|
|
|
- paramMap,
|
|
|
- AlipayConfig.alipay_public_key,
|
|
|
- AlipayConfig.charset,
|
|
|
- AlipayConfig.sign_type
|
|
|
+ //1.验证签名 避免出现虚假通知 ,确保该接口调用方是支付宝/防止数据被篡改
|
|
|
+ log.info("支付宝支付成功回调参数:{}", paramMap);
|
|
|
+ boolean flag = AlipaySignature.rsaCheckV1(
|
|
|
+ paramMap,
|
|
|
+ AlipayConfig.alipay_public_key,
|
|
|
+ AlipayConfig.charset,
|
|
|
+ AlipayConfig.sign_type
|
|
|
);
|
|
|
-
|
|
|
-
|
|
|
- //校验验签是否成功
|
|
|
- String outTradeNo = paramMap.get("out_trade_no");
|
|
|
- if(!signVerified){
|
|
|
- log.error("订单 {} 验签失败", outTradeNo);
|
|
|
- return result;
|
|
|
- }
|
|
|
- log.error("验签成功!");
|
|
|
-
|
|
|
- //商家需要验证该通知数据中的 out_trade_no 是否为商家系统中创建的订单号。
|
|
|
- R<OrderInfo> orderInfoResult = remoteOrderInfoService.getByOrderNo(outTradeNo, SecurityConstants.INNER);
|
|
|
- if(R.FAIL == orderInfoResult.getCode()){
|
|
|
- log.error("远程获取订单 {} 失败", outTradeNo);
|
|
|
- return result;
|
|
|
- }
|
|
|
- if(orderInfoResult.getData() == null){
|
|
|
- log.error("订单 {} 不存在", outTradeNo);
|
|
|
- return result;
|
|
|
- }
|
|
|
- OrderInfo orderInfo = orderInfoResult.getData();
|
|
|
-
|
|
|
- // 判断 total_amount 是否确实为该订单的实际金额(即商户订单创建时的金额)。
|
|
|
- String totalAmount = paramMap.get("total_amount");
|
|
|
- if(orderInfo.getTotalAmount().compareTo(new BigDecimal(totalAmount)) != 0){
|
|
|
- log.error("订单 {} 金额不一致", outTradeNo);
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- // 校验通知中的 seller_id(或者 seller_email) 是否为 out_trade_no 这笔单据的对应的操作方(有的时候,一个商家可能有多个 seller_id/seller_email)。
|
|
|
- // "2088721032347805" 沙箱中的 商户PID
|
|
|
- String sellerId = paramMap.get("seller_id");
|
|
|
- if(!"2088721032347805".equals(sellerId)){
|
|
|
- log.error("订单 {} 商家不一致", outTradeNo);
|
|
|
- return result;
|
|
|
+ if (!flag) {
|
|
|
+ log.error("支付宝支付成功回调:签名验证失败");
|
|
|
+ return "failure";
|
|
|
}
|
|
|
-
|
|
|
- //验证 app_id 是否为该商家本身。
|
|
|
- String appId = paramMap.get("app_id");
|
|
|
- if(!AlipayConfig.app_id.equals(appId)){
|
|
|
- log.error("订单 {} appid不一致", outTradeNo);
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- // 交易状态
|
|
|
- String tradeStatus = paramMap.get("trade_status");
|
|
|
- if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) {
|
|
|
- // 正常的支付成功,我们应该更新交易记录状态
|
|
|
- paymentInfoService.updatePaymentStatus(paramMap, 2);
|
|
|
-
|
|
|
- //基于MQ通知订单系统,修改订单状态
|
|
|
- rabbitService.sendMessage(MqConst.EXCHANGE_PAYMENT_PAY, MqConst.ROUTING_PAYMENT_PAY, paymentInfo.getOrderNo());
|
|
|
- //基于MQ通知商品系统,扣减库存
|
|
|
- rabbitService.sendMessage(MqConst.EXCHANGE_PRODUCT, MqConst.ROUTING_MINUS, outTradeNo);
|
|
|
-
|
|
|
- return "success";
|
|
|
+ //2.基于通知ID进行幂等性处理
|
|
|
+ String notifyId = paramMap.get("notify_id");
|
|
|
+ String key = "pay:alipay:notifyId:" + notifyId;
|
|
|
+ Boolean onece = redisTemplate.opsForValue().setIfAbsent(key, notifyId, 25, TimeUnit.HOURS);
|
|
|
+ if (onece) {
|
|
|
+ try {
|
|
|
+ //3.验证金额、支付状态 -- 确保用户付款成功
|
|
|
+ String tradeStatus = paramMap.get("trade_status");
|
|
|
+ if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) {
|
|
|
+ //4.更新本地交易记录状态:已支付
|
|
|
+ paymentInfoService.updatePaymentStatus(paramMap);
|
|
|
+ return "success";
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ redisTemplate.delete(key);
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
} catch (AlipayApiException e) {
|
|
|
- e.printStackTrace();
|
|
|
+ throw new RuntimeException(e);
|
|
|
}
|
|
|
-
|
|
|
- return result;
|
|
|
+ return null;
|
|
|
}
|
|
|
```
|
|
|
|
|
@@ -1085,31 +1109,45 @@ public String alipayNotify(@RequestParam Map<String, String> paramMap) {
|
|
|
更新支付信息
|
|
|
|
|
|
```java
|
|
|
-void updatePaymentStatus(Map<String, String> map, Integer payType);
|
|
|
+/**
|
|
|
+ * 用户付款成功后,更新本地交易记录
|
|
|
+ * @param paramMap
|
|
|
+ */
|
|
|
+void updatePaymentStatus(Map<String, String> paramMap);
|
|
|
```
|
|
|
|
|
|
##### PaymentInfoServiceImpl
|
|
|
|
|
|
```java
|
|
|
-@Transactional(rollbackFor = Exception.class)
|
|
|
+/**
|
|
|
+ * 用户付款成功后,更新本地交易记录
|
|
|
+ *
|
|
|
+ * @param paramMap
|
|
|
+ */
|
|
|
@Override
|
|
|
-public void updatePaymentStatus(Map<String, String> map, Integer payType) {
|
|
|
- PaymentInfo paymentInfo = baseMapper.selectOne(
|
|
|
- new LambdaQueryWrapper<PaymentInfo>()
|
|
|
- .eq(PaymentInfo::getOrderNo, map.get("out_trade_no"))
|
|
|
- );
|
|
|
- //已支付,直接返回
|
|
|
- if (paymentInfo.getPaymentStatus() == 1) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- //更新支付信息
|
|
|
- paymentInfo.setPayType(payType);
|
|
|
- paymentInfo.setPaymentStatus(1);
|
|
|
- paymentInfo.setTradeNo(map.get("trade_no"));
|
|
|
- paymentInfo.setCallbackTime(new Date());
|
|
|
- paymentInfo.setCallbackContent(JSON.toJSONString(map));
|
|
|
- baseMapper.updateById(paymentInfo);
|
|
|
+public void updatePaymentStatus(Map<String, String> paramMap) {
|
|
|
+ //1.根据订单编号查询本地交易记录 验证:用户实付金额跟商户侧应付金额是否一致
|
|
|
+ String orderNo = paramMap.get("out_trade_no");
|
|
|
+ PaymentInfo paymentInfo = this.getOne(
|
|
|
+ new LambdaQueryWrapper<PaymentInfo>()
|
|
|
+ .eq(PaymentInfo::getOrderNo, orderNo)
|
|
|
+ );
|
|
|
+ String buyerPayAmount = paramMap.get("buyer_pay_amount");
|
|
|
+ if (paymentInfo.getAmount().compareTo(new BigDecimal(buyerPayAmount)) != 0) {
|
|
|
+ log.error("该笔订单实付金额与交易记录金额不一致,订单编号:{}", orderNo);
|
|
|
+ throw new RuntimeException("该笔订单实付金额与交易记录金额不一致,订单编号:" + orderNo);
|
|
|
+ }
|
|
|
+ //2.更新本地交易记录:状态:已支付、支付宝订单号、回调时间、回调内容
|
|
|
+ paymentInfo.setPaymentStatus("1");
|
|
|
+ paymentInfo.setTradeNo(paramMap.get("trade_no"));
|
|
|
+ paymentInfo.setCallbackTime(new Date());
|
|
|
+ paymentInfo.setCallbackContent(paramMap.toString());
|
|
|
+ this.updateById(paymentInfo);
|
|
|
+
|
|
|
+ //3.发送MQ消息通知订单服务修改订单状态
|
|
|
+ rabbitService.sendMessage(MqConst.EXCHANGE_PAYMENT_PAY, MqConst.ROUTING_PAYMENT_PAY, orderNo);
|
|
|
+ //4.发送MQ消息通知商品服务库存扣减
|
|
|
+ rabbitService.sendMessage(MqConst.EXCHANGE_PRODUCT, MqConst.ROUTING_MINUS, orderNo);
|
|
|
|
|
|
}
|
|
|
```
|
|
@@ -1129,34 +1167,51 @@ public void updatePaymentStatus(Map<String, String> map, Integer payType) {
|
|
|
```java
|
|
|
package com.spzx.order.receiver;
|
|
|
|
|
|
+import com.rabbitmq.client.Channel;
|
|
|
+import com.spzx.common.core.utils.StringUtils;
|
|
|
+import com.spzx.common.rabbit.constant.MqConst;
|
|
|
+import com.spzx.order.service.OrderInfoService;
|
|
|
+import lombok.SneakyThrows;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.amqp.core.Message;
|
|
|
+import org.springframework.amqp.rabbit.annotation.Exchange;
|
|
|
+import org.springframework.amqp.rabbit.annotation.Queue;
|
|
|
+import org.springframework.amqp.rabbit.annotation.QueueBinding;
|
|
|
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author: atguigu
|
|
|
+ * @create: 2025-05-10 16:34
|
|
|
+ */
|
|
|
@Slf4j
|
|
|
@Component
|
|
|
public class OrderReceiver {
|
|
|
|
|
|
@Autowired
|
|
|
- private IOrderInfoService orderInfoService;
|
|
|
+ private OrderInfoService orderInfoService;
|
|
|
|
|
|
/**
|
|
|
- * 监听订单支付成功消息;更新订单状态
|
|
|
+ * 监听订单支付成功消息,修改订单状态
|
|
|
*
|
|
|
* @param orderNo
|
|
|
* @param message
|
|
|
* @param channel
|
|
|
*/
|
|
|
- @SneakyThrows
|
|
|
@RabbitListener(bindings = @QueueBinding(
|
|
|
exchange = @Exchange(value = MqConst.EXCHANGE_PAYMENT_PAY, durable = "true"),
|
|
|
- value = @Queue(value = MqConst.QUEUE_PAYMENT_PAY, durable = "true"),
|
|
|
+ value = @Queue(MqConst.QUEUE_PAYMENT_PAY),
|
|
|
key = MqConst.ROUTING_PAYMENT_PAY
|
|
|
))
|
|
|
- public void processPaySucess(String orderNo, Message message, Channel channel) {
|
|
|
- //业务处理
|
|
|
- if (StringUtils.isNotEmpty(orderNo)) {
|
|
|
- log.info("[订单服务]监听订单支付成功消息:{}", orderNo);
|
|
|
- //更改订单支付状态
|
|
|
- orderInfoService.processPaySuccess(orderNo);
|
|
|
+ @SneakyThrows
|
|
|
+ public void paymentSuccess(String orderNo, Message message, Channel channel) {
|
|
|
+ if (StringUtils.isNotBlank(orderNo)) {
|
|
|
+ log.info("[订单服务]监听到订单支付成功消息:{}", orderNo);
|
|
|
+ orderInfoService.paymentSuccess(orderNo);
|
|
|
}
|
|
|
- //手动应答
|
|
|
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
|
|
|
}
|
|
|
}
|
|
@@ -1165,25 +1220,23 @@ public class OrderReceiver {
|
|
|
#### 2.1.2 IOrderInfoService
|
|
|
|
|
|
```java
|
|
|
-void processPaySuccess(String orderNo);
|
|
|
+void paymentSuccess(String orderNo);
|
|
|
```
|
|
|
|
|
|
#### 2.1.3 OrderInfoServiceImpl
|
|
|
|
|
|
```java
|
|
|
+/**
|
|
|
+ * 订单支付成功消息,修改订单状态
|
|
|
+ *
|
|
|
+ * @param orderNo
|
|
|
+ */
|
|
|
@Override
|
|
|
-public void processPaySuccess(String orderNo) {
|
|
|
- //获取订单信息
|
|
|
- OrderInfo orderInfo = orderInfoMapper.selectOne(
|
|
|
- new LambdaQueryWrapper<OrderInfo>()
|
|
|
- .eq(OrderInfo::getOrderNo, orderNo)
|
|
|
- .select(OrderInfo::getId, OrderInfo::getOrderStatus)
|
|
|
- );
|
|
|
-
|
|
|
- if(orderInfo.getOrderStatus().intValue() == 0) {
|
|
|
- orderInfo.setOrderStatus(1);//已支付
|
|
|
- orderInfo.setPaymentTime(new Date());
|
|
|
- orderInfoMapper.updateById(orderInfo);
|
|
|
+public void paymentSuccess(String orderNo) {
|
|
|
+ OrderInfo orderInfo = this.getByOrderNo(orderNo);
|
|
|
+ if (orderInfo.getOrderStatus().intValue()==0) {
|
|
|
+ orderInfo.setOrderStatus(1);
|
|
|
+ this.updateById(orderInfo);
|
|
|
}
|
|
|
}
|
|
|
```
|
|
@@ -1195,78 +1248,85 @@ public void processPaySuccess(String orderNo) {
|
|
|
#### 2.2.1 ProductReceiver
|
|
|
|
|
|
```java
|
|
|
-/**
|
|
|
- * 扣减库存
|
|
|
- * @param orderNo 订单号
|
|
|
+/***
|
|
|
+ * 监听订单支付成功后扣减订单中商品库存数量
|
|
|
+ * @param orderNo
|
|
|
+ * @param channel
|
|
|
+ * @param message
|
|
|
+ * @throws IOException
|
|
|
*/
|
|
|
@SneakyThrows
|
|
|
@RabbitListener(bindings = @QueueBinding(
|
|
|
exchange = @Exchange(value = MqConst.EXCHANGE_PRODUCT, durable = "true"),
|
|
|
- value = @Queue(value = MqConst.QUEUE_MINUS, durable = "true"),
|
|
|
- key = {MqConst.ROUTING_MINUS}
|
|
|
+ value = @Queue(MqConst.QUEUE_MINUS),
|
|
|
+ key = MqConst.ROUTING_MINUS
|
|
|
))
|
|
|
public void minus(String orderNo, Channel channel, Message message) {
|
|
|
- //业务处理
|
|
|
- if (StringUtils.isNotEmpty(orderNo)){
|
|
|
- log.info("[商品服务]监听减库存消息:{}", orderNo);
|
|
|
- //扣减库存
|
|
|
- productService.minus(orderNo);
|
|
|
+ if (StringUtils.isNotBlank(orderNo)) {
|
|
|
+ log.info("[订单服务]监听到订单支付成功,减库存消息:{}", orderNo);
|
|
|
+ //1.幂等性处理
|
|
|
+ String key = "order:minus:orderNo:" + orderNo;
|
|
|
+ Boolean once = redisTemplate.opsForValue().setIfAbsent(key, orderNo, 5, TimeUnit.MINUTES);
|
|
|
+ if (!once) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ //2.业务逻辑处理
|
|
|
+ skuStockService.minusStock(orderNo);
|
|
|
+ } catch (Exception e) {
|
|
|
+ redisTemplate.delete(key);
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ //3.手动应答
|
|
|
+ channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
|
|
|
}
|
|
|
-
|
|
|
- //手动应答
|
|
|
- channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### 2.3.2 IProductService
|
|
|
|
|
|
```java
|
|
|
-void minus(String orderNo);
|
|
|
+/**
|
|
|
+ * 从下单时存入Redis缓存锁定记录,进行库存真正扣减
|
|
|
+ * @param orderNo
|
|
|
+ */
|
|
|
+void minusStock(String orderNo);
|
|
|
```
|
|
|
|
|
|
#### 2.2.3 ProductServiceImpl
|
|
|
|
|
|
```java
|
|
|
-@Transactional(rollbackFor = {Exception.class})
|
|
|
+/**
|
|
|
+ * 从下单时存入Redis缓存锁定记录,进行库存真正扣减
|
|
|
+ *
|
|
|
+ * @param orderNo
|
|
|
+ */
|
|
|
@Override
|
|
|
-public void minus(String orderNo) {
|
|
|
-
|
|
|
- //幂等性处理
|
|
|
- String key = "sku:minus:" + orderNo;
|
|
|
- //业务去重,防止重复消费
|
|
|
- Boolean isExist = redisTemplate.opsForValue().setIfAbsent(key, orderNo, 1, TimeUnit.HOURS);
|
|
|
- if(!isExist) return;
|
|
|
-
|
|
|
- // 获取锁定库存的缓存信息
|
|
|
+public void minusStock(String orderNo) {
|
|
|
String dataKey = "sku:lock:data:" + orderNo;
|
|
|
- List<SkuLockVo> skuLockVoList = (List<SkuLockVo>)this.redisTemplate.opsForValue().get(dataKey);
|
|
|
- if (CollectionUtils.isEmpty(skuLockVoList)){
|
|
|
- return ;
|
|
|
+ List<SkuLockVo> skuLockVoList = (List<SkuLockVo>) redisTemplate.opsForValue().get(dataKey);
|
|
|
+ if (CollectionUtils.isEmpty(skuLockVoList)) {
|
|
|
+ return;
|
|
|
}
|
|
|
-
|
|
|
- // 减库存
|
|
|
+ //遍历锁定记录列表,进行真正扣减库存
|
|
|
skuLockVoList.forEach(skuLockVo -> {
|
|
|
- int row = skuStockMapper.minus(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
|
|
|
+ baseMapper.minusStock(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
|
|
|
});
|
|
|
-
|
|
|
- // 扣减库存之后,删除锁定库存的缓存。
|
|
|
- this.redisTemplate.delete(dataKey);
|
|
|
+ redisTemplate.delete(dataKey);
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### 2.2.4 SkuStockMapper
|
|
|
|
|
|
```java
|
|
|
-Integer minus(@Param("skuId") Long skuId, @Param("num")Integer num);
|
|
|
+void minusStock(@Param("skuId") Long skuId,@Param("skuNum") Integer skuNum);
|
|
|
```
|
|
|
|
|
|
#### 2.2.5 SkuStockMapper.xml
|
|
|
|
|
|
```xml
|
|
|
-<update id="minus">
|
|
|
- update sku_stock
|
|
|
- set lock_num = lock_num - #{num}, total_num = total_num - #{num}, sale_num = sale_num + #{num}
|
|
|
- where sku_id = #{skuId}
|
|
|
+<update id="minusStock">
|
|
|
+ UPDATE sku_stock set lock_num = lock_num - #{skuNum} , total_num = total_num - #{skuNum} where sku_id = #{skuId} and del_flag = 0;
|
|
|
</update>
|
|
|
```
|
|
|
|