Browse Source

feat : 商城小程序下单独立接口

bilingfeng 2 years ago
parent
commit
5247fc8131

+ 21 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/controller/PayController.java

@@ -6,13 +6,16 @@ import com.zanxiang.common.enums.PayWayEnum;
 import com.zanxiang.module.util.JsonUtil;
 import com.zanxiang.sdk.annotation.UnSignCheck;
 import com.zanxiang.sdk.annotation.ValidLogin;
+import com.zanxiang.sdk.domain.params.MiniAppPayParam;
 import com.zanxiang.sdk.domain.params.ProductPayParam;
 import com.zanxiang.sdk.domain.params.UserData;
 import com.zanxiang.sdk.domain.vo.PayParamVO;
 import com.zanxiang.sdk.service.OrderPayService;
+import com.zanxiang.sdk.service.PayApplicationService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
@@ -36,6 +39,9 @@ public class PayController {
     @Resource
     private OrderPayService orderPayService;
 
+    @Autowired
+    private PayApplicationService payApplicationService;
+
     @UnSignCheck
     @ApiOperation(value = "获取小程序/客服支付参数")
     @GetMapping(value = "/param/applet")
@@ -56,6 +62,14 @@ public class PayController {
         return orderPayService.payCreate(product, userData);
     }
 
+    @UnSignCheck
+    @ApiOperation(value = "支付参数生成(小程序独立)")
+    @PostMapping(value = "/applet/create")
+    public ResultMap create(@Validated @RequestBody MiniAppPayParam payParam, HttpServletRequest request) {
+        log.error("接收到支付请求, payParam : {}", JsonUtil.toString(payParam));
+        return payApplicationService.miniAppPay(payParam, request);
+    }
+
     @UnSignCheck
     @ApiOperation(value = "支付宝支付异步回调(二维码、H5、网站)")
     @RequestMapping(value = "/notify", method = RequestMethod.POST)
@@ -70,6 +84,13 @@ public class PayController {
         return orderPayService.notify(request, response, PayWayEnum.WXPAY.getNum());
     }
 
+    @UnSignCheck
+    @ApiOperation(value = "微信支付回调(小程序独立)")
+    @RequestMapping(value = "/applet/wxPayNotify", method = RequestMethod.POST)
+    public String wxAppletPayNotify(HttpServletRequest request, HttpServletResponse response) throws IOException {
+        return orderPayService.appletNotify(request, response);
+    }
+
     @UnSignCheck
     @ApiOperation(value = "支付宝支付同步回调(二维码、H5、网站)")
     @RequestMapping(value = "/alipaySynNotify", method = RequestMethod.GET)

+ 51 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/domain/params/MiniAppPayParam.java

@@ -0,0 +1,51 @@
+package com.zanxiang.sdk.domain.params;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author : lingfeng
+ * @time : 2023-01-09
+ * @description : 商城小程序支付参数
+ */
+@Data
+public class MiniAppPayParam {
+
+    /**
+     * 微信小程序商城支付code
+     */
+    @NotBlank
+    @ApiModelProperty(notes = "微信小程序商城支付code")
+    private String code;
+
+    /**
+     * 商城小程序id
+     */
+    @NotBlank
+    @ApiModelProperty(notes = "商城小程序id")
+    private String appId;
+
+    /**
+     * 商品ID
+     */
+    @NotBlank
+    @ApiModelProperty(notes = "商品ID")
+    private String productId;
+
+    /**
+     * 商品ID
+     */
+    @NotBlank
+    @ApiModelProperty(notes = "商品ID")
+    private String productName;
+
+    /**
+     * 商品金额
+     */
+    @NotBlank
+    @ApiModelProperty(notes = "商品金额")
+    private String totalFee;
+
+}

+ 54 - 4
game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/Impl/OrderPayServiceImpl.java

@@ -2,30 +2,38 @@ package com.zanxiang.sdk.service.Impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.zanxiang.common.domain.ResultMap;
+import com.zanxiang.common.enums.HttpStatusEnum;
 import com.zanxiang.common.enums.OrderStateEnum;
 import com.zanxiang.common.enums.PayDeviceEnum;
 import com.zanxiang.common.exception.BaseException;
 import com.zanxiang.common.utils.SpringUtils;
+import com.zanxiang.module.util.JsonUtil;
 import com.zanxiang.module.web.util.IpUtil;
 import com.zanxiang.mybatis.entity.Order;
 import com.zanxiang.mybatis.entity.OrderPayParam;
+import com.zanxiang.mybatis.entity.PayMerchant;
 import com.zanxiang.sdk.domain.bo.ProductPayParamBO;
 import com.zanxiang.sdk.domain.params.ProductPayParam;
 import com.zanxiang.sdk.domain.params.UserData;
 import com.zanxiang.sdk.domain.vo.PayParamVO;
 import com.zanxiang.sdk.enums.PayTypeEnum;
-import com.zanxiang.sdk.service.GamePayWayService;
-import com.zanxiang.sdk.service.OrderPayParamService;
-import com.zanxiang.sdk.service.OrderPayService;
-import com.zanxiang.sdk.service.OrderService;
+import com.zanxiang.sdk.service.*;
 import com.zanxiang.sdk.service.pay.PayBaseService;
+import com.zanxiang.sdk.util.WxPayUtil;
+import com.zanxiang.sdk.util.XmlUtil;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -46,6 +54,9 @@ public class OrderPayServiceImpl implements OrderPayService {
     @Autowired
     private OrderPayParamService orderPayParamService;
 
+    @Autowired
+    private PayMerchantService payMerchantService;
+
     @Override
     public PayParamVO getPayParam(String code, String orderId, HttpServletRequest request) {
         Order order = orderService.getOne(new LambdaQueryWrapper<Order>()
@@ -128,6 +139,45 @@ public class OrderPayServiceImpl implements OrderPayService {
         return service.notify(request, response);
     }
 
+    @Override
+    public String appletNotify(HttpServletRequest request, HttpServletResponse response) throws IOException {
+        // 读取参数
+        InputStream inputStream = request.getInputStream();
+        String requestStr = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
+        // 解析xml成map
+        Map<String, String> packageParams = XmlUtil.xmlToMap(requestStr);
+        //获取订单信息
+        String attachStr = packageParams.get("attach");
+        Map<String, String> attachStrMap = JsonUtil.toMap(attachStr, Map.class, String.class);
+        if (attachStrMap == null || attachStrMap.isEmpty()) {
+            log.info("回调参数中attach值为空");
+            return null;
+        }
+        //商户号id
+        String merchantNo = attachStrMap.get("merchantNo");
+        PayMerchant payMerchant = payMerchantService.getOne(new LambdaQueryWrapper<PayMerchant>()
+                .eq(PayMerchant::getMerchantNo, merchantNo));
+        Map<String, String> payConfigMap = JsonUtil.toMap(payMerchant.getPayConfig(), Map.class, String.class);
+        // 判断签名是否正确
+        String mySign = WxPayUtil.generateSignature(packageParams, payConfigMap.get("apiKey"));
+        if (!Objects.equals(mySign, packageParams.get("sign"))) {
+            log.error("通知签名验证失败, muSign : {}, sign : {}", mySign, packageParams.get("sign"));
+            return null;
+        }
+        log.info("微信支付成功回调");
+        // 处理业务开始
+        Map<String, String> xmlMap = new HashMap<>();
+        xmlMap.put("return_code", "SUCCESS");
+        xmlMap.put("return_msg", "OK");
+        // 处理业务完毕
+        BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
+        log.info("返回给腾讯的回调结果, ss : {}", XmlUtil.mapToXml(xmlMap));
+        out.write(XmlUtil.mapToXml(xmlMap).getBytes());
+        out.flush();
+        out.close();
+        return HttpStatusEnum.SUCCESS.getMsg();
+    }
+
     @Override
     public ResultMap synNotify(HttpServletRequest request, Integer payType) {
         PayTypeEnum payTypeEnum = PayTypeEnum.getByPayType(payType);

+ 169 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/Impl/PayApplicationServiceImpl.java

@@ -0,0 +1,169 @@
+package com.zanxiang.sdk.service.Impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.common.constant.Constants;
+import com.zanxiang.common.domain.ResultMap;
+import com.zanxiang.common.exception.BaseException;
+import com.zanxiang.module.util.JsonUtil;
+import com.zanxiang.module.web.util.IpUtil;
+import com.zanxiang.mybatis.entity.PayApplication;
+import com.zanxiang.mybatis.entity.PayMerchant;
+import com.zanxiang.mybatis.mapper.PayApplicationMapper;
+import com.zanxiang.sdk.constant.WxPayConstants;
+import com.zanxiang.sdk.domain.params.MiniAppPayParam;
+import com.zanxiang.sdk.service.PayApplicationService;
+import com.zanxiang.sdk.service.PayMerchantService;
+import com.zanxiang.sdk.service.api.WxApiService;
+import com.zanxiang.sdk.util.HttpUtil;
+import com.zanxiang.sdk.util.WxPayUtil;
+import com.zanxiang.sdk.util.XmlUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletRequest;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * 支付商城小程序
+ *
+ * @author xufeng
+ * @date 2022-06-20 15:16
+ */
+@Slf4j
+@Service
+public class PayApplicationServiceImpl extends ServiceImpl<PayApplicationMapper, PayApplication> implements PayApplicationService {
+
+    @Autowired
+    private WxApiService wxApiService;
+
+    @Autowired
+    private PayMerchantService payMerchantService;
+
+
+    private static final String WX_PAY_JSAPI = "JSAPI";
+
+    private static final String SIGN_TYPE = "MD5";
+
+    /**
+     * 微信回调接口
+     */
+    @Value("${payConfig.wx.notifyUrl}")
+    private String notifyUrl;
+
+    @Override
+    public ResultMap miniAppPay(MiniAppPayParam payParam, HttpServletRequest request) {
+        //查询商城小程序信息
+        PayApplication payApplication = super.getOne(new LambdaQueryWrapper<PayApplication>()
+                .eq(PayApplication::getAppId, payParam.getAppId()));
+        if (payApplication == null) {
+            throw new BaseException("参数错误, 支付商城小程序信息不存在");
+        }
+        //查询商户信息
+        PayMerchant payMerchant = payMerchantService.getById(4L);
+        Map<String, String> payConfigMap = JsonUtil.toMap(payMerchant.getPayConfig(), Map.class, String.class);
+        //获取用户openId
+        String openId = wxApiService.getMpOpenId(payParam.getCode(), payApplication.getAppId(), payApplication.getAppSecret());
+        //订单id
+        String orderId = this.getOrderNum(openId);
+        try {
+            //下单参数
+            Map<String, String> paramMap = new HashMap<>(9);
+            paramMap.put("appId", payApplication.getAppId());
+            paramMap.put("mchId", payMerchant.getMerchantNo());
+            paramMap.put("productName", payParam.getProductName());
+            paramMap.put("orderId", orderId);
+            paramMap.put("totalFee", payParam.getTotalFee());
+            paramMap.put("ip", IpUtil.getRealIp(request));
+            paramMap.put("openId", openId);
+            paramMap.put("apiKey", payConfigMap.get("apiKey"));
+            Map<String, String> attachMap = new HashMap<>(2);
+            attachMap.put("appId", payApplication.getAppId());
+            attachMap.put("orderId", orderId);
+            attachMap.put("merchantNo", payMerchant.getMerchantNo());
+            paramMap.put("attach", JsonUtil.toString(attachMap));
+            log.error("下单参数, paramMap : {}", JsonUtil.toString(paramMap));
+            //下单
+            Map<String, String> successMap = this.unifiedOrder(paramMap);
+            //支付id
+            String prepayId = successMap.get("prepay_id");
+            // 时间戳转化成字符串,否则前端wx.requestPayment方法会报签名错误
+            String createTime = String.valueOf(System.currentTimeMillis() / 1000);
+            // 随机字符串
+            String nonceStr = WxPayUtil.generateNonceStr();
+            // 获取统一下单回调结果, 二次签名
+            HashMap<String, String> map = new HashMap<>(5);
+            map.put("appId", payApplication.getAppId());
+            map.put("nonceStr", nonceStr);
+            map.put("timeStamp", createTime);
+            map.put("package", "prepay_id=" + prepayId);
+            map.put("signType", SIGN_TYPE);
+            // 再次签名sign,这个签名用于小程序端调用支付方法
+            String sign = WxPayUtil.generateSignature(map, payConfigMap.get("apiKey"));
+            //组装返回前端支付参数
+            Map<Object, Object> miniMap = new HashMap<>(5);
+            miniMap.put("paySign", sign);
+            miniMap.put("signType", SIGN_TYPE);
+            miniMap.put("timeStamp", createTime);
+            miniMap.put("package", successMap);
+            miniMap.put("nonceStr", nonceStr);
+            //返回
+            return ResultMap.ok(miniMap);
+        } catch (Exception e) {
+            log.error("微信小程序支付通信异常, 订单号:{}, e : {}", orderId, e.getMessage());
+            return ResultMap.error();
+        }
+    }
+
+    private String getOrderNum(String openId) {
+        //时间(精确到毫秒)
+        DateTimeFormatter ofPattern = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
+        String localDate = LocalDateTime.now().format(ofPattern);
+        //3位随机数
+        String randomNumeric = RandomStringUtils.randomNumeric(3);
+        return localDate + Objects.hash(openId) + randomNumeric;
+    }
+
+    private Map<String, String> unifiedOrder(Map<String, String> paramMap) {
+        try {
+            Map<String, String> paramData = new HashMap<>(13);
+            paramData.put("appid", paramMap.get("appId"));
+            paramData.put("mch_id", paramMap.get("mchId"));
+            paramData.put("nonce_str", WxPayUtil.generateNonceStr());
+            paramData.put("body", paramMap.get("productName"));
+            paramData.put("out_trade_no", paramMap.get("orderId"));
+            paramData.put("fee_type", "CNY");
+            paramData.put("total_fee", WxPayUtil.subZeroAndDot(String.valueOf(Float.parseFloat(paramMap.get("totalFee")) * 100)));
+            paramData.put("spbill_create_ip", paramMap.get("ip"));
+            paramData.put("notify_url", notifyUrl);
+            paramData.put("trade_type", WX_PAY_JSAPI);
+            paramData.put("attach", paramMap.get("attach"));
+            paramData.put("sign_type", SIGN_TYPE);
+            paramData.put("openid", paramMap.get("openId"));
+            //接口签名
+            String sign = WxPayUtil.generateSignature(paramData, paramMap.get("apiKey"));
+            paramData.put("sign", sign);
+            // 下单, 获取结果
+            String result = HttpUtil.postData(WxPayConstants.UNIFIED_ORDER_URL, XmlUtil.mapToXml(paramData));
+            Map<String, String> successMap = XmlUtil.xmlToMap(result);
+            //成功, 返回结果
+            if (Constants.SUCCESS.equalsIgnoreCase(successMap.get("return_code"))
+                    && successMap.get("return_code").equals(successMap.get("result_code"))) {
+                return successMap;
+            }
+            //下单失败
+            log.error("微信小程序支付通信异常, paramData : {}", JsonUtil.toString(paramData));
+            throw new BaseException("微信小程序支付下单失败");
+        } catch (Exception e) {
+            log.error("微信小程序支付通信异常, 订单号:{}, e : {}", paramMap.get("orderId"), e.getMessage());
+            throw new BaseException("微信小程序支付下单异常");
+        }
+    }
+}

+ 10 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/OrderPayService.java

@@ -53,6 +53,16 @@ public interface OrderPayService {
      */
     String notify(HttpServletRequest request, HttpServletResponse response, Integer payType) throws IOException;
 
+    /**
+     * applet通知
+     *
+     * @param request  请求
+     * @param response 响应
+     * @return {@link String}
+     * @throws IOException ioexception
+     */
+    String appletNotify(HttpServletRequest request, HttpServletResponse response) throws IOException;
+
     /**
      * 异步回调
      *

+ 26 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/PayApplicationService.java

@@ -0,0 +1,26 @@
+package com.zanxiang.sdk.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.common.domain.ResultMap;
+import com.zanxiang.mybatis.entity.PayApplication;
+import com.zanxiang.sdk.domain.params.MiniAppPayParam;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 支付商城小程序
+ *
+ * @author xufeng
+ * @date 2022-06-20 15:16
+ */
+public interface PayApplicationService extends IService<PayApplication> {
+
+    /**
+     * 迷你应用程序支付
+     *
+     * @param payParam 支付参数
+     * @param request  请求
+     * @return {@link ResultMap}
+     */
+    ResultMap miniAppPay(MiniAppPayParam payParam, HttpServletRequest request);
+}