소스 검색

feat(payment): 支持支付宝二级商户支付功能

- 在支付接口中新增 smid 参数用于标识二级商户
- 集成支付宝 SubMerchant 和 SettleInfo 相关模型类
- 实现二级商户支付参数构建和结算信息配置
- 更新支付策略接口以支持二级商户参数传递
- 添加 StoreInfoVo 中的 alipaySmid 字段定义
- 重构支付宝退款逻辑到独立方法中便于维护
- 配置支付宝 SDK 使用统一的配置获取方式
fcw 15 시간 전
부모
커밋
8cc66b4d31

+ 3 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreInfoVo.java

@@ -287,4 +287,7 @@ public class StoreInfoVo extends StoreInfo {
     @ApiModelProperty(value = "经营板块名称")
     private String businessSectionName;
 
+    @ApiModelProperty(value = "alipay_smid")
+    private String alipaySmid;
+
 }

+ 3 - 2
alien-store/src/main/java/shop/alien/store/controller/MerchantPaymentController.java

@@ -45,9 +45,10 @@ public class MerchantPaymentController {
             @RequestParam String amountYuan,
             @RequestParam String subject,
             @RequestParam Integer userId,
-            @RequestParam(defaultValue = "alipay") String payType) {
+            @RequestParam(defaultValue = "alipay") String payType,
+            @RequestParam String smid) {
         log.info("MerchantPaymentController.prePay storeId={}, orderId={}, payType={}", storeId, orderId, payType);
-        return merchantPaymentStrategyFactory.getStrategy(payType).createPrePay(storeId, orderId, amountYuan, subject, userId);
+        return merchantPaymentStrategyFactory.getStrategy(payType).createPrePay(storeId, orderId, amountYuan, subject, userId, smid);
     }
 
     @ApiOperation("预订订单-查询支付状态")

+ 2 - 1
alien-store/src/main/java/shop/alien/store/controller/UserReservationPaymentController.java

@@ -45,10 +45,11 @@ public class UserReservationPaymentController {
             @RequestParam String amountYuan,
             @RequestParam String subject,
             @RequestParam Integer userId,
+            @RequestParam String smid,
             @RequestParam(defaultValue = "alipay") String payType) {
         log.info("UserReservationPaymentController.prePay storeId={}, orderId={}, payType={}", storeId, orderId, payType);
         MerchantPaymentStrategy strategy = merchantPaymentStrategyFactory.getStrategy(payType);
-        return strategy.createPrePay(storeId, orderId, amountYuan, subject, userId);
+        return strategy.createPrePay(storeId, orderId, amountYuan, subject, userId, smid);
     }
 
     @ApiOperation("预订订单-查询支付状态")

+ 1 - 1
alien-store/src/main/java/shop/alien/store/strategy/merchantPayment/MerchantPaymentStrategy.java

@@ -22,7 +22,7 @@ public interface MerchantPaymentStrategy {
      * @param userId     用户ID
      * @return 含 orderStr、outTradeNo、orderSn、orderId 等
      */
-    R<Map<String, Object>> createPrePay(Integer storeId, Integer orderId, String amountYuan, String subject, Integer userId);
+    R<Map<String, Object>> createPrePay(Integer storeId, Integer orderId, String amountYuan, String subject, Integer userId, String smid);
 
     /**
      * 查询支付状态(根据商户订单号查第三方并更新订单与支付单)

+ 118 - 20
alien-store/src/main/java/shop/alien/store/strategy/merchantPayment/impl/MerchantAlipayPaymentStrategyImpl.java

@@ -4,9 +4,9 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.AlipayClient;
+import com.alipay.api.AlipayConfig;
 import com.alipay.api.DefaultAlipayClient;
-import com.alipay.api.domain.AlipayTradeAppPayModel;
-import com.alipay.api.domain.AlipayTradeRefundModel;
+import com.alipay.api.domain.*;
 import com.alipay.api.request.AlipayTradeAppPayRequest;
 import com.alipay.api.request.AlipayTradeQueryRequest;
 import com.alipay.api.request.AlipayTradeRefundRequest;
@@ -33,8 +33,10 @@ import shop.alien.util.common.constant.PaymentEnum;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
@@ -77,7 +79,7 @@ public class MerchantAlipayPaymentStrategyImpl implements MerchantPaymentStrateg
     private final StringRedisTemplate stringRedisTemplate;
 
     @Override
-    public R<Map<String, Object>> createPrePay(Integer storeId, Integer orderId, String amountYuan, String subject, Integer userId) {
+    public R<Map<String, Object>> createPrePay(Integer storeId, Integer orderId, String amountYuan, String subject, Integer userId, String smid) {
         if (storeId == null || orderId == null) {
             return R.fail("门店ID和订单ID不能为空");
         }
@@ -171,21 +173,56 @@ public class MerchantAlipayPaymentStrategyImpl implements MerchantPaymentStrateg
         merchantPaymentOrderService.save(paymentOrder);
 
         try {
-            com.alipay.api.AlipayConfig alipayConfig = buildAlipayConfigFromStore(config);
-            AlipayClient client = new DefaultAlipayClient(alipayConfig);
+//            com.alipay.api.AlipayConfig alipayConfig = buildAlipayConfigFromStore(config);
+//            AlipayClient client = new DefaultAlipayClient(alipayConfig);
+//            AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
+//            AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
+//            model.setOutTradeNo(outTradeNo);
+//            model.setTotalAmount(amountYuanBD.toPlainString());
+//            model.setSubject(subject);
+//            // 预支付单有效时间 15 分钟(相对超时,格式 1m~15d)
+//            model.setTimeoutExpress("15m");
+//            request.setBizModel(model);
+//            // 必须用 certificateExecute 请求支付宝网关预创建交易,否则仅 sdkExecute 本地签名不会在支付宝侧落单,查单会报「交易不存在」
+//            //AlipayTradeAppPayResponse response = client.certificateExecute(request);
+//            AlipayTradeAppPayResponse response = client.sdkExecute(request);
+            // 初始化SDK
+            AlipayClient alipayClient = new DefaultAlipayClient(getAlipayConfig());
+
+            // 构造请求参数以调用接口
             AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
             AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
+
             model.setOutTradeNo(outTradeNo);
-            model.setTotalAmount(amountYuanBD.toPlainString());
+            BigDecimal total = new BigDecimal(String.valueOf(amountYuanBD));
+            model.setTotalAmount(String.valueOf(total));
+
             model.setSubject(subject);
-            // 预支付单有效时间 15 分钟(相对超时,格式 1m~15d)
+
             model.setTimeoutExpress("15m");
+
+            // 1. 构建二级商户信息
+            SubMerchant subMerchant = new SubMerchant();
+            subMerchant.setMerchantId(smid); // 替换为实际的二级商户编号
+            // 【关键】设置二级商户信息(直付通必传)
+            model.setSubMerchant(subMerchant);
+
+            SettleInfo settleInfo = new SettleInfo();
+            SettleDetailInfo settleDetailInfo = new SettleDetailInfo();
+            settleDetailInfo.setTransInType("defaultSettle");
+            settleDetailInfo.setAmount(total.toString());
+            List<SettleDetailInfo> settleDetailInfos = new ArrayList<>();
+            settleDetailInfos.add(settleDetailInfo);
+            settleInfo.setSettleDetailInfos(settleDetailInfos);
+            model.setSettleInfo(settleInfo);
+
+            request.setNotifyUrl("https://frp-off.com:40279/alienStore/alipayPartnerNotify");
             request.setBizModel(model);
-            // 必须用 certificateExecute 请求支付宝网关预创建交易,否则仅 sdkExecute 本地签名不会在支付宝侧落单,查单会报「交易不存在」
-            //AlipayTradeAppPayResponse response = client.certificateExecute(request);
-            AlipayTradeAppPayResponse response = client.sdkExecute(request);
 
-            String orderStr = response.isSuccess() ? response.getBody() : "";
+            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
+            String orderStr = response.getBody();
+
+//            String orderStr = response.isSuccess() ? response.getBody() : "";
 
             if (!response.isSuccess()) {
                 return R.fail("预支付失败:" + response.getSubMsg());
@@ -231,13 +268,24 @@ public class MerchantAlipayPaymentStrategyImpl implements MerchantPaymentStrateg
             return R.success("支付成功");
         }
         try {
-            com.alipay.api.AlipayConfig alipayConfig = buildAlipayConfigFromStore(config);
-            AlipayClient client = new DefaultAlipayClient(alipayConfig);
+//            com.alipay.api.AlipayConfig alipayConfig = buildAlipayConfigFromStore(config);
+//            AlipayClient client = new DefaultAlipayClient(alipayConfig);
+//            AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
+//            JSONObject bizContent = new JSONObject();
+//            bizContent.put("out_trade_no", outTradeNo);
+//            request.setBizContent(bizContent.toJSONString());
+//            AlipayTradeQueryResponse response = client.certificateExecute(request);
+
+            AlipayClient alipayClient = new DefaultAlipayClient(getAlipayConfig());
             AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
-            JSONObject bizContent = new JSONObject();
-            bizContent.put("out_trade_no", outTradeNo);
-            request.setBizContent(bizContent.toJSONString());
-            AlipayTradeQueryResponse response = client.certificateExecute(request);
+            AlipayTradeQueryModel model = new AlipayTradeQueryModel();
+            model.setOutTradeNo(outTradeNo);
+            List<String> queryOptions = new ArrayList<>();
+            queryOptions.add("trade_settle_info");
+            model.setQueryOptions(queryOptions);
+            request.setBizModel(model);
+
+            AlipayTradeQueryResponse response = alipayClient.execute(request);
             if (!response.isSuccess()) {
                 String subMsg = response.getSubMsg();
                 String subCode = response.getSubCode();
@@ -319,15 +367,24 @@ public class MerchantAlipayPaymentStrategyImpl implements MerchantPaymentStrateg
             return R.fail("订单未支付或已退款,无法退款");
         }
         try {
-            com.alipay.api.AlipayConfig alipayConfig = buildAlipayConfigFromStore(config);
-            AlipayClient client = new DefaultAlipayClient(alipayConfig);
             AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
             AlipayTradeRefundModel model = new AlipayTradeRefundModel();
             model.setOutTradeNo(outTradeNo);
             model.setRefundAmount(refundAmount);
             model.setRefundReason(StringUtils.isNotBlank(refundReason) ? refundReason : "用户申请退款");
             request.setBizModel(model);
-            AlipayTradeRefundResponse response = client.certificateExecute(request);
+            AlipayTradeRefundResponse response = refundRun(request);
+
+
+//            com.alipay.api.AlipayConfig alipayConfig = buildAlipayConfigFromStore(config);
+//            AlipayClient client = new DefaultAlipayClient(alipayConfig);
+//            AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
+//            AlipayTradeRefundModel model = new AlipayTradeRefundModel();
+//            model.setOutTradeNo(outTradeNo);
+//            model.setRefundAmount(refundAmount);
+//            model.setRefundReason(StringUtils.isNotBlank(refundReason) ? refundReason : "用户申请退款");
+//            request.setBizModel(model);
+//            AlipayTradeRefundResponse response = client.certificateExecute(request);
             if (!response.isSuccess()) {
                 String failMsg = "退款失败:" + response.getSubMsg();
                 markRefundFailed(order, paymentOrder, failMsg, refundReason, storeId, outTradeNo);
@@ -398,6 +455,33 @@ public class MerchantAlipayPaymentStrategyImpl implements MerchantPaymentStrateg
         }
     }
 
+    /**
+     * 执行退款请求
+     */
+    private AlipayTradeRefundResponse refundRun(AlipayTradeRefundRequest request) throws AlipayApiException {
+        AlipayClient alipayClient = new DefaultAlipayClient(getAlipayConfig());
+        AlipayTradeRefundResponse response = alipayClient.certificateExecute(request);
+        return response;
+    }
+
+
+
+    /**
+     * 构建退款请求
+     */
+    private AlipayTradeRefundRequest buildRefundRequest(Map<String, String> params) {
+        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
+        AlipayTradeRefundModel model = new AlipayTradeRefundModel();
+        model.setOutTradeNo(params.get("outTradeNo"));
+        model.setRefundAmount(params.get("refundAmount"));
+        model.setRefundReason(params.get("refundReason"));
+        if (StringUtils.isNotBlank(params.get("partialRefundCode"))) {
+            model.setOutRequestNo(params.get("partialRefundCode"));
+        }
+        request.setBizModel(model);
+        return request;
+    }
+
     /** 退款失败时:订单与支付单置为退款中,并写入退款失败记录 */
     private void markRefundFailed(UserReservationOrder order, MerchantPaymentOrder paymentOrder,
                                   String errorMsg, String refundReason, Integer storeId, String outTradeNo) {
@@ -453,4 +537,18 @@ public class MerchantAlipayPaymentStrategyImpl implements MerchantPaymentStrateg
 
         return alipayConfig;
     }
+
+    private static AlipayConfig getAlipayConfig() {
+        String privateKey  = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCBTOvScNrk0mw+WbR6Qtx2+HrPxB5IU/V2HSPGZUTDNiCiSU9UZcuXlo0UNwMksFYR43btk4BHW9OsZETdr3uJzbEEko4jQPrY49V6V8d3yFUiexcbLKf9W2X+h7EwdPDahHk6wmM73Lm500xzv8JxdGeo/d2uGTh+qD66HcPW+QYj1YbjRV1JyCOiJ2OiwLcb28arNNLOgj6PktQ69FPSWS4hWrM7nl6DLRr6SdOlyfBT47gwPtW0BQ96/2b9O+qxKBPfrcqdRDH323HvrGFDwa8GoWTdchnKwFjmfIFHMvf+eq3tzilFi8H7vQRtzVRrndwrNa0z+1ss07fcWFDRAgMBAAECggEAItgU0OAizPk7vE22SiBMgy8RAX5rXrhpdIwDwQo3Tpf+kV1KKIdKJy6mFCWDDlcKysVOnlVag2BmmZVnzYnls8wfgQjxjuSK9Pno5JBVK51r+9/J6UPOfYMs6Duu700EPw7mEISj81TXJBGiD6tEfgiNisfm/mzDgbZbORKeXQbaTyrtB+GZn6FNSyyHA1vraARMrgfMEGNzQ4AbtfcUxGO+mejdTFs0PxAq6lovHBY3fYYHI1Nx6kf9iPoom/G4UrcMO67W6QU+1tOCZCXjy4bD2y421z/8XD73+WDyYp+Tjy0hTLqVZc7TpYAOximo1vMIUe23EdJJngdlkdpDFQKBgQDFyETL0knwBSakLfAe2BmFb2x++B4YXUnt4dGbCFBnVooxf5i06GVt/CrfkJhYK6hBSowOScIRf8P6BOSQptRZb2/I1ngQm4vcpAZw6EjUTlgOj/J3XJ+ApUNQnRqE28jDrE4m2RHg4BkQo6yA3DizJAluPCtFoCYDm1a7dV7u7wKBgQCnXEH5sD8VSxURv02/gn80g/uZIP/EOU3ycjBEqZdRGkNINwXT+zqrlZIGYb+bxLvU/R2OqKC5vhcyAL3T1A8ORYqPu5KLnAxg7C+rHuVilUWwCEH7POpCk+ETPXCZwcNvLNa5PIqBH/gdV9Y9PBTef6J4rN6V6TDFgosf5by8PwKBgDpVG71Fk1sAGep4RgbC05wgRc6Y3T9wXDqVzJ098YDY7D83E9HfbPLoWbjAS75Nef1vwCkCpgNFPIbD5KmpGp4aGM0SPC0hwzlbAy9PwxMi3CPHXsrHfZ+SnmzrOQQQUoErk40vnm9FiP74VwtWaD6llUZ25ohNeIi9yvHU5x/vAoGAdU2d1JOq85LHtsO+i9+8pyNnAsJ1YqTDtI5CtK2lqKvewswGIrlxOvi//AchVN3ExZmP0QDyfp31BhAs/T8iOl+Vqf7PzVjX+Eszch5aqwlzadmv3ZepnnamCGVE+hAsmkz0R6tebPjqYC7Ds/HbssQFLc4EyVBD5fwE5ZuR+OMCgYAvGHUYpi0dY9uMHXzL721tIopiwUfKCgLAn3yhSH3p7644NxHBqLLaBLVT2q7JAZQUaZUvXlwiyxU1zvo0xmvcbnB/Vd2qp8KbEUkvHyIYVJkM6Fn+9xBosorcrHv+7B2V1XR9WQcXvppxbN/8farWGuAA0anBD+UGrxd8B0/hHg==";
+        String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnxevClLpYc6c8A9lwmnftPO2BeU+X6aZ/+b/n1Cvq096VJKiHmqcsRgRbrnSlmPDHRrQpDti4en2Ys0L2lx7CObIr/2xP/jJVwjIO1iWHUj/w/NAbjv7dLW/FFY4SeNp8rU+hlgGgviyUxzonfNfr3v+o8grFqQq27/hiZJAofsQRMQu1dEQqoKdJj7eQLkTstiK5miJMyQ+Y3tLztrEUMBz/zRgaCEfGqmFmRZ2diy2X+1dGaX6H4+0QJ2u50eY2QTBkNuvREGbAn6/lttAgvg/+CywPYKGeC4xOfnl5wP8iA1QXYbXrVJRkZjU097nyOmSNhLy9KvJH2BNpojS1QIDAQAB";
+        AlipayConfig alipayConfig = new AlipayConfig();
+        alipayConfig.setServerUrl("https://openapi.alipay.com/gateway.do");
+        alipayConfig.setAppId("2021005196608960");
+        alipayConfig.setPrivateKey(privateKey);
+        alipayConfig.setFormat("json");
+        alipayConfig.setAlipayPublicKey(alipayPublicKey);
+        alipayConfig.setCharset("UTF-8");
+        alipayConfig.setSignType("RSA2");
+        return alipayConfig;
+    }
 }

+ 1 - 1
alien-store/src/main/java/shop/alien/store/strategy/merchantPayment/impl/MerchantWechatPaymentStrategyImpl.java

@@ -82,7 +82,7 @@ public class MerchantWechatPaymentStrategyImpl implements MerchantPaymentStrateg
     private final StringRedisTemplate stringRedisTemplate;
 
     @Override
-    public R<Map<String, Object>> createPrePay(Integer storeId, Integer orderId, String amountYuan, String subject, Integer userId) {
+    public R<Map<String, Object>> createPrePay(Integer storeId, Integer orderId, String amountYuan, String subject, Integer userId, String smid) {
         if (storeId == null || orderId == null) {
             return R.fail("门店ID和订单ID不能为空");
         }