|
@@ -2,57 +2,52 @@ package com.ylx.web.controller.massage;
|
|
|
|
|
|
|
|
import cn.hutool.core.date.DatePattern;
|
|
import cn.hutool.core.date.DatePattern;
|
|
|
import cn.hutool.core.date.DateUtil;
|
|
import cn.hutool.core.date.DateUtil;
|
|
|
-import cn.hutool.core.io.file.FileWriter;
|
|
|
|
|
|
|
+import cn.hutool.core.util.ObjectUtil;
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
import cn.hutool.http.ContentType;
|
|
import cn.hutool.http.ContentType;
|
|
|
-import cn.hutool.json.JSONArray;
|
|
|
|
|
import cn.hutool.json.JSONObject;
|
|
import cn.hutool.json.JSONObject;
|
|
|
import cn.hutool.json.JSONUtil;
|
|
import cn.hutool.json.JSONUtil;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
+import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
|
|
|
|
|
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
|
|
|
|
|
+import com.github.binarywang.wxpay.exception.WxPayException;
|
|
|
|
|
+import com.github.binarywang.wxpay.service.WxPayService;
|
|
|
import com.ijpay.core.IJPayHttpResponse;
|
|
import com.ijpay.core.IJPayHttpResponse;
|
|
|
-import com.ijpay.core.enums.AuthTypeEnum;
|
|
|
|
|
import com.ijpay.core.enums.RequestMethodEnum;
|
|
import com.ijpay.core.enums.RequestMethodEnum;
|
|
|
-import com.ijpay.core.kit.AesUtil;
|
|
|
|
|
import com.ijpay.core.kit.HttpKit;
|
|
import com.ijpay.core.kit.HttpKit;
|
|
|
import com.ijpay.core.kit.PayKit;
|
|
import com.ijpay.core.kit.PayKit;
|
|
|
import com.ijpay.core.kit.WxPayKit;
|
|
import com.ijpay.core.kit.WxPayKit;
|
|
|
-import com.ijpay.core.utils.DateTimeZoneUtil;
|
|
|
|
|
import com.ijpay.wxpay.WxPayApi;
|
|
import com.ijpay.wxpay.WxPayApi;
|
|
|
-import com.ijpay.wxpay.WxPayApiConfigKit;
|
|
|
|
|
import com.ijpay.wxpay.enums.WxDomainEnum;
|
|
import com.ijpay.wxpay.enums.WxDomainEnum;
|
|
|
-import com.ijpay.wxpay.enums.v3.BasePayApiEnum;
|
|
|
|
|
-import com.ijpay.wxpay.enums.v3.CertAlgorithmTypeEnum;
|
|
|
|
|
import com.ijpay.wxpay.enums.v3.TransferApiEnum;
|
|
import com.ijpay.wxpay.enums.v3.TransferApiEnum;
|
|
|
-import com.ijpay.wxpay.model.v3.*;
|
|
|
|
|
-import com.wechat.pay.java.service.partnerpayments.jsapi.JsapiServiceExtension;
|
|
|
|
|
|
|
+import com.ijpay.wxpay.model.v3.BatchTransferModel;
|
|
|
|
|
+import com.ijpay.wxpay.model.v3.TransferDetailInput;
|
|
|
import com.ylx.common.config.WxPayConfig;
|
|
import com.ylx.common.config.WxPayConfig;
|
|
|
import com.ylx.common.core.domain.R;
|
|
import com.ylx.common.core.domain.R;
|
|
|
|
|
+import com.ylx.common.weixinPay.enums.WxPayTypeEnum;
|
|
|
|
|
+import com.ylx.giftCard.domain.GiftCardOrder;
|
|
|
|
|
+import com.ylx.giftCard.enums.GiftCardOrderStatusEnum;
|
|
|
|
|
+import com.ylx.giftCard.service.IGiftCardOrderService;
|
|
|
import com.ylx.massage.domain.TRecharge;
|
|
import com.ylx.massage.domain.TRecharge;
|
|
|
-import com.ylx.massage.enums.BillTypeEnum;
|
|
|
|
|
-import com.ylx.massage.enums.PayTypeEnum;
|
|
|
|
|
-import com.ylx.massage.domain.ProductOrderInfo;
|
|
|
|
|
-import com.ylx.massage.domain.TConsumptionLog;
|
|
|
|
|
import com.ylx.massage.domain.TWxUser;
|
|
import com.ylx.massage.domain.TWxUser;
|
|
|
import com.ylx.massage.enums.BillTypeEnum;
|
|
import com.ylx.massage.enums.BillTypeEnum;
|
|
|
-import com.ylx.massage.enums.ProductOrderStatusEnum;
|
|
|
|
|
-import com.ylx.massage.service.IProductOrderInfoService;
|
|
|
|
|
import com.ylx.massage.service.RefundVoucherService;
|
|
import com.ylx.massage.service.RefundVoucherService;
|
|
|
-import com.ylx.massage.service.TOrderService;
|
|
|
|
|
import com.ylx.massage.service.TRechargeService;
|
|
import com.ylx.massage.service.TRechargeService;
|
|
|
import com.ylx.massage.service.TWxUserService;
|
|
import com.ylx.massage.service.TWxUserService;
|
|
|
import io.swagger.annotations.Api;
|
|
import io.swagger.annotations.Api;
|
|
|
import io.swagger.annotations.ApiOperation;
|
|
import io.swagger.annotations.ApiOperation;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
-import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
import org.springframework.web.bind.annotation.*;
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
import javax.annotation.Resource;
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
-import java.io.ByteArrayInputStream;
|
|
|
|
|
import java.math.BigDecimal;
|
|
import java.math.BigDecimal;
|
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.nio.charset.StandardCharsets;
|
|
|
import java.security.cert.X509Certificate;
|
|
import java.security.cert.X509Certificate;
|
|
|
-import java.util.*;
|
|
|
|
|
|
|
+import java.util.Collections;
|
|
|
|
|
+import java.util.HashMap;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
|
|
|
import static com.ylx.common.constant.HttpStatus.SUCCESS;
|
|
import static com.ylx.common.constant.HttpStatus.SUCCESS;
|
|
|
|
|
|
|
@@ -67,29 +62,19 @@ import static com.ylx.common.constant.HttpStatus.SUCCESS;
|
|
|
@Api(tags = {"微信支付"})
|
|
@Api(tags = {"微信支付"})
|
|
|
public class PayController {
|
|
public class PayController {
|
|
|
|
|
|
|
|
-
|
|
|
|
|
- @Autowired
|
|
|
|
|
|
|
+ @Resource
|
|
|
private WxPayConfig wxPayProperties;
|
|
private WxPayConfig wxPayProperties;
|
|
|
-
|
|
|
|
|
@Resource
|
|
@Resource
|
|
|
private TRechargeService rechargeService;
|
|
private TRechargeService rechargeService;
|
|
|
-
|
|
|
|
|
- @Resource
|
|
|
|
|
- private TOrderService orderService;
|
|
|
|
|
-
|
|
|
|
|
@Resource
|
|
@Resource
|
|
|
private RefundVoucherService refundVoucherService;
|
|
private RefundVoucherService refundVoucherService;
|
|
|
-
|
|
|
|
|
- @Resource
|
|
|
|
|
- private IProductOrderInfoService productOrderInfoService;
|
|
|
|
|
-
|
|
|
|
|
@Resource
|
|
@Resource
|
|
|
private TWxUserService wxUserService;
|
|
private TWxUserService wxUserService;
|
|
|
-
|
|
|
|
|
- // @Resource
|
|
|
|
|
-// private JsapiServiceExtension service;
|
|
|
|
|
String serialNo;
|
|
String serialNo;
|
|
|
- String platSerialNo;
|
|
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private WxPayService wxPayService;
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private IGiftCardOrderService giftCardOrderService;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 小程序微信支付的第一步,统一下单
|
|
* 小程序微信支付的第一步,统一下单
|
|
@@ -101,118 +86,9 @@ public class PayController {
|
|
|
return rechargeService.getPay(rechargeResp.getRechargeNo(), recharge.getdMoney(), recharge.getcOpenId(), BillTypeEnum.RECHARGE.getInfo(), BillTypeEnum.RECHARGE.getCode().toString());
|
|
return rechargeService.getPay(rechargeResp.getRechargeNo(), recharge.getdMoney(), recharge.getcOpenId(), BillTypeEnum.RECHARGE.getInfo(), BillTypeEnum.RECHARGE.getCode().toString());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * 支付
|
|
|
|
|
- *
|
|
|
|
|
- * @param setOutTradeNo
|
|
|
|
|
- * @param amount
|
|
|
|
|
- * @param openId
|
|
|
|
|
- * @param description
|
|
|
|
|
- * @return
|
|
|
|
|
- * @throws Exception
|
|
|
|
|
- */
|
|
|
|
|
- public R<String> getPay(String setOutTradeNo, BigDecimal amount, String openId, String description) throws Exception {
|
|
|
|
|
- String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
|
|
|
|
|
- UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
|
|
|
|
|
- .setAppid(wxPayProperties.getAppId())
|
|
|
|
|
- .setMchid(wxPayProperties.getMchId())
|
|
|
|
|
- //商品描述
|
|
|
|
|
- .setDescription(description)
|
|
|
|
|
- //订单号
|
|
|
|
|
- .setOut_trade_no(setOutTradeNo)
|
|
|
|
|
- //交易结束时间
|
|
|
|
|
- .setTime_expire(timeExpire)
|
|
|
|
|
- //附加数据
|
|
|
|
|
- .setAttach("夜来香")
|
|
|
|
|
- //通知地址异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 公网域名必须为https,如果是走专线接入,使用专线NAT IP或者私有回调域名可使用http
|
|
|
|
|
- //示例值:https://www.weixin.qq.com/wxpay/pay.php
|
|
|
|
|
- .setNotify_url(wxPayProperties.getNotifyUrl())
|
|
|
|
|
- //支付金额以分为单位
|
|
|
|
|
- .setAmount(new Amount().setTotal(amount.multiply(new BigDecimal(100)).intValue()))
|
|
|
|
|
- //交易人
|
|
|
|
|
- .setPayer(new Payer().setOpenid(openId));
|
|
|
|
|
-
|
|
|
|
|
- log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
|
|
|
|
|
- IJPayHttpResponse response = WxPayApi.v3(
|
|
|
|
|
- RequestMethodEnum.POST,
|
|
|
|
|
- WxDomainEnum.CHINA.toString(),
|
|
|
|
|
- BasePayApiEnum.JS_API_PAY.toString(),
|
|
|
|
|
- wxPayProperties.getMchId(),
|
|
|
|
|
- getSerialNumber(),
|
|
|
|
|
- null,
|
|
|
|
|
- wxPayProperties.getCertKeyPath(),
|
|
|
|
|
- JSONUtil.toJsonStr(unifiedOrderModel)
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- log.info("统一下单响应 {}", response);
|
|
|
|
|
- // 根据证书序列号查询对应的证书来验证签名结果
|
|
|
|
|
- boolean verifySignature = WxPayKit.verifySignature(response, wxPayProperties.getPlatFormPath());
|
|
|
|
|
- log.info("verifySignature: {}", verifySignature);
|
|
|
|
|
- if (response.getStatus() == SUCCESS && verifySignature) {
|
|
|
|
|
- String body = response.getBody();
|
|
|
|
|
- JSONObject jsonObject = JSONUtil.parseObj(body);
|
|
|
|
|
- String prepayId = jsonObject.getStr("prepay_id");
|
|
|
|
|
- Map<String, String> map = WxPayKit.jsApiCreateSign(wxPayProperties.getAppId(), prepayId, wxPayProperties.getCertKeyPath());
|
|
|
|
|
- log.info("唤起支付参数:{}", map);
|
|
|
|
|
- return R.ok(JSONUtil.toJsonStr(map));
|
|
|
|
|
- }
|
|
|
|
|
- return R.ok(JSONUtil.toJsonStr(response));
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- //服务商模式下单
|
|
|
|
|
- public R<String> getPay1(String setOutTradeNo, BigDecimal amount, String openId, String description) throws Exception {
|
|
|
|
|
- String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
|
|
|
|
|
- UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
|
|
|
|
|
- .setSp_appid(wxPayProperties.getAppId())
|
|
|
|
|
- .setSp_mchid(wxPayProperties.getMchId())
|
|
|
|
|
- .setSub_mchid("123")//子商户号
|
|
|
|
|
- //商品描述
|
|
|
|
|
- .setDescription(description)
|
|
|
|
|
- //订单号
|
|
|
|
|
- .setOut_trade_no(setOutTradeNo)
|
|
|
|
|
- //交易结束时间
|
|
|
|
|
- .setTime_expire(timeExpire)
|
|
|
|
|
- //附加数据
|
|
|
|
|
- .setAttach("夜来香")
|
|
|
|
|
- //通知地址异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 公网域名必须为https,如果是走专线接入,使用专线NAT IP或者私有回调域名可使用http
|
|
|
|
|
- //示例值:https://www.weixin.qq.com/wxpay/pay.php
|
|
|
|
|
- .setNotify_url(wxPayProperties.getNotifyUrl())
|
|
|
|
|
- //分账标识
|
|
|
|
|
- .setSettle_info(new SettleInfo().setProfit_sharing(true))
|
|
|
|
|
- //支付金额以分为单位
|
|
|
|
|
- .setAmount(new Amount().setTotal(amount.multiply(new BigDecimal(100)).intValue()))
|
|
|
|
|
- //交易人
|
|
|
|
|
- .setPayer(new Payer().setSp_openid(openId));
|
|
|
|
|
-
|
|
|
|
|
- log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
|
|
|
|
|
- IJPayHttpResponse response = WxPayApi.v3(
|
|
|
|
|
- RequestMethodEnum.POST,
|
|
|
|
|
- WxDomainEnum.CHINA.toString(),
|
|
|
|
|
- BasePayApiEnum.PARTNER_JS_API_PAY.toString(),
|
|
|
|
|
- wxPayProperties.getMchId(),
|
|
|
|
|
- getSerialNumber(),
|
|
|
|
|
- null,
|
|
|
|
|
- wxPayProperties.getCertKeyPath(),
|
|
|
|
|
- JSONUtil.toJsonStr(unifiedOrderModel)
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- log.info("统一下单响应 {}", response);
|
|
|
|
|
- // 根据证书序列号查询对应的证书来验证签名结果
|
|
|
|
|
- boolean verifySignature = WxPayKit.verifySignature(response, wxPayProperties.getPlatFormPath());
|
|
|
|
|
- log.info("verifySignature: {}", verifySignature);
|
|
|
|
|
- if (response.getStatus() == SUCCESS && verifySignature) {
|
|
|
|
|
- String body = response.getBody();
|
|
|
|
|
- JSONObject jsonObject = JSONUtil.parseObj(body);
|
|
|
|
|
- String prepayId = jsonObject.getStr("prepay_id");
|
|
|
|
|
- Map<String, String> map = WxPayKit.jsApiCreateSign(wxPayProperties.getAppId(), prepayId, wxPayProperties.getCertKeyPath());
|
|
|
|
|
- log.info("唤起支付参数:{}", map);
|
|
|
|
|
- return R.ok(JSONUtil.toJsonStr(map));
|
|
|
|
|
- }
|
|
|
|
|
- return R.ok(JSONUtil.toJsonStr(response));
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
/**
|
|
/**
|
|
|
* 获取商户API证书序列号
|
|
* 获取商户API证书序列号
|
|
|
|
|
+ *
|
|
|
* @return String 证书序列号
|
|
* @return String 证书序列号
|
|
|
*/
|
|
*/
|
|
|
private String getSerialNumber() {
|
|
private String getSerialNumber() {
|
|
@@ -225,175 +101,155 @@ public class PayController {
|
|
|
boolean isValid = PayKit.checkCertificateIsValid(certificate, wxPayProperties.getMchId(), -2);
|
|
boolean isValid = PayKit.checkCertificateIsValid(certificate, wxPayProperties.getMchId(), -2);
|
|
|
log.info("证书是否可用 {} 证书有效期为 {}", isValid, DateUtil.format(certificate.getNotAfter(), DatePattern.NORM_DATETIME_PATTERN));
|
|
log.info("证书是否可用 {} 证书有效期为 {}", isValid, DateUtil.format(certificate.getNotAfter(), DatePattern.NORM_DATETIME_PATTERN));
|
|
|
}
|
|
}
|
|
|
-// System.out.println("输出证书信息:\n" + certificate.toString());
|
|
|
|
|
-// // 输出关键信息,截取部分并进行标记
|
|
|
|
|
-// System.out.println("证书序列号:" + certificate.getSerialNumber().toString(16));
|
|
|
|
|
-// System.out.println("版本号:" + certificate.getVersion());
|
|
|
|
|
-// System.out.println("签发者:" + certificate.getIssuerDN());
|
|
|
|
|
-// System.out.println("有效起始日期:" + certificate.getNotBefore());
|
|
|
|
|
-// System.out.println("有效终止日期:" + certificate.getNotAfter());
|
|
|
|
|
-// System.out.println("主体名:" + certificate.getSubjectDN());
|
|
|
|
|
-// System.out.println("签名算法:" + certificate.getSigAlgName());
|
|
|
|
|
-// System.out.println("签名:" + certificate.getSignature().toString());
|
|
|
|
|
}
|
|
}
|
|
|
System.out.println("serialNo:" + serialNo);
|
|
System.out.println("serialNo:" + serialNo);
|
|
|
return serialNo;
|
|
return serialNo;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * 微信支付回调接口
|
|
|
|
|
- *
|
|
|
|
|
- * @param request
|
|
|
|
|
- * @param response
|
|
|
|
|
- */
|
|
|
|
|
- @RequestMapping(value = "/payNotify", method = {org.springframework.web.bind.annotation.RequestMethod.POST, org.springframework.web.bind.annotation.RequestMethod.GET})
|
|
|
|
|
- @ResponseBody
|
|
|
|
|
- @ApiOperation("微信支付回调接口")
|
|
|
|
|
- public void payNotify(HttpServletRequest request, HttpServletResponse response) {
|
|
|
|
|
- log.info("微信支付回调接口====================================>>>>微信支付回调接口");
|
|
|
|
|
- Map<String, String> map = new HashMap<>(12);
|
|
|
|
|
|
|
+// /**
|
|
|
|
|
+// * 微信支付回调接口
|
|
|
|
|
+// *
|
|
|
|
|
+// * @param request
|
|
|
|
|
+// * @param response
|
|
|
|
|
+// */
|
|
|
|
|
+// @RequestMapping(value = "/payNotify", method = {org.springframework.web.bind.annotation.RequestMethod.POST, org.springframework.web.bind.annotation.RequestMethod.GET})
|
|
|
|
|
+// @ResponseBody
|
|
|
|
|
+// @ApiOperation("微信支付回调接口")
|
|
|
|
|
+// public void payNotify(HttpServletRequest request, HttpServletResponse response) {
|
|
|
|
|
+// log.info("微信支付回调接口====================================>>>>微信支付回调接口");
|
|
|
|
|
+// Map<String, String> map = new HashMap<>(12);
|
|
|
|
|
+// try {
|
|
|
|
|
+// String timestamp = request.getHeader("Wechatpay-Timestamp");
|
|
|
|
|
+// String nonce = request.getHeader("Wechatpay-Nonce");
|
|
|
|
|
+// String serialNo = request.getHeader("Wechatpay-Serial");
|
|
|
|
|
+// String signature = request.getHeader("Wechatpay-Signature");
|
|
|
|
|
+//
|
|
|
|
|
+// log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
|
|
|
|
|
+// String result = HttpKit.readData(request);
|
|
|
|
|
+// log.info("支付通知密文 {}", result);
|
|
|
|
|
+//
|
|
|
|
|
+// // 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
|
|
|
|
|
+// String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
|
|
|
|
|
+// wxPayProperties.getMchKey(), wxPayProperties.getPlatFormPath());
|
|
|
|
|
+//
|
|
|
|
|
+// log.info("支付通知明文 {}", plainText);
|
|
|
|
|
+//
|
|
|
|
|
+// if (StrUtil.isNotEmpty(plainText)) {
|
|
|
|
|
+// response.setStatus(200);
|
|
|
|
|
+// map.put("code", "SUCCESS");
|
|
|
|
|
+// map.put("message", "SUCCESS");
|
|
|
|
|
+// // 处理业务逻辑
|
|
|
|
|
+// JSONObject jsonObject = new JSONObject(plainText);
|
|
|
|
|
+// if (jsonObject.get("attach").equals(BillTypeEnum.WX_PAY.getCode().toString())) {
|
|
|
|
|
+// // 服务订单支付成功
|
|
|
|
|
+// orderService.payNotifyOrder(jsonObject.get("out_trade_no").toString());
|
|
|
|
|
+// } else if (jsonObject.get("attach").equals(PayTypeEnum.WX_PAY.getCode().toString())) {
|
|
|
|
|
+// // 商品订单支付成功
|
|
|
|
|
+// String productOrderNo = jsonObject.get("out_trade_no").toString();
|
|
|
|
|
+// log.info("商品订单支付回调开始处理,订单号:{}", productOrderNo);
|
|
|
|
|
+// productOrderInfoService.handleWxPayCallback(productOrderNo);
|
|
|
|
|
+// log.info("商品订单支付回调处理完成,订单号:{}", productOrderNo);
|
|
|
|
|
+// } else {
|
|
|
|
|
+// TRecharge outTradeNo = rechargeService.increaseAmount(jsonObject.get("out_trade_no").toString());
|
|
|
|
|
+// }
|
|
|
|
|
+// } else {
|
|
|
|
|
+// response.setStatus(500);
|
|
|
|
|
+// map.put("code", "ERROR");
|
|
|
|
|
+// map.put("message", "签名错误");
|
|
|
|
|
+// }
|
|
|
|
|
+// response.setHeader("Content-type", ContentType.JSON.toString());
|
|
|
|
|
+// response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
|
|
|
|
|
+// response.flushBuffer();
|
|
|
|
|
+// } catch (Exception e) {
|
|
|
|
|
+// log.error("系统异常", e);
|
|
|
|
|
+// }
|
|
|
|
|
+// }
|
|
|
|
|
+
|
|
|
|
|
+ @PostMapping("/payNotify")
|
|
|
|
|
+ public Map<String, String> handlePayNotify(@RequestBody String notifyData, @RequestHeader("Wechatpay-Signature") SignatureHeader signature) {
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, String> resp = new HashMap<>();
|
|
|
try {
|
|
try {
|
|
|
- String timestamp = request.getHeader("Wechatpay-Timestamp");
|
|
|
|
|
- String nonce = request.getHeader("Wechatpay-Nonce");
|
|
|
|
|
- String serialNo = request.getHeader("Wechatpay-Serial");
|
|
|
|
|
- String signature = request.getHeader("Wechatpay-Signature");
|
|
|
|
|
|
|
+ // 1. SDK验签+解密报文
|
|
|
|
|
+ WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayService.parseOrderNotifyV3Result(notifyData, signature).getResult();
|
|
|
|
|
|
|
|
- log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
|
|
|
|
|
- String result = HttpKit.readData(request);
|
|
|
|
|
- log.info("支付通知密文 {}", result);
|
|
|
|
|
|
|
+ String outTradeNo = result.getOutTradeNo();
|
|
|
|
|
+ String tradeState = result.getTradeState();
|
|
|
|
|
|
|
|
- // 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
|
|
|
|
|
- String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
|
|
|
|
|
- wxPayProperties.getMchKey(), wxPayProperties.getPlatFormPath());
|
|
|
|
|
|
|
+ log.info("微信V3回调解密成功,商户订单号:{},微信单号:{},交易状态:{}",
|
|
|
|
|
+ outTradeNo, result.getTransactionId(), tradeState);
|
|
|
|
|
|
|
|
- log.info("支付通知明文 {}", plainText);
|
|
|
|
|
|
|
+ // 2. 仅SUCCESS才处理订单
|
|
|
|
|
+ if ("SUCCESS".equals(tradeState)) {
|
|
|
|
|
|
|
|
- if (StrUtil.isNotEmpty(plainText)) {
|
|
|
|
|
- response.setStatus(200);
|
|
|
|
|
- map.put("code", "SUCCESS");
|
|
|
|
|
- map.put("message", "SUCCESS");
|
|
|
|
|
- // 处理业务逻辑
|
|
|
|
|
- JSONObject jsonObject = new JSONObject(plainText);
|
|
|
|
|
- if (jsonObject.get("attach").equals(BillTypeEnum.WX_PAY.getCode().toString())) {
|
|
|
|
|
- // 服务订单支付成功
|
|
|
|
|
- orderService.payNotifyOrder(jsonObject.get("out_trade_no").toString());
|
|
|
|
|
- } else if (jsonObject.get("attach").equals(PayTypeEnum.WX_PAY.getCode().toString())) {
|
|
|
|
|
- // 商品订单支付成功
|
|
|
|
|
- String productOrderNo = jsonObject.get("out_trade_no").toString();
|
|
|
|
|
- log.info("商品订单支付回调开始处理,订单号:{}", productOrderNo);
|
|
|
|
|
- productOrderInfoService.handleWxPayCallback(productOrderNo);
|
|
|
|
|
- log.info("商品订单支付回调处理完成,订单号:{}", productOrderNo);
|
|
|
|
|
- } else {
|
|
|
|
|
- TRecharge outTradeNo = rechargeService.increaseAmount(jsonObject.get("out_trade_no").toString());
|
|
|
|
|
|
|
+ // 2. 获取我们在下单时传入的 attach
|
|
|
|
|
+ String attach = result.getAttach();
|
|
|
|
|
+ String openid = result.getPayer().getOpenid();
|
|
|
|
|
+
|
|
|
|
|
+ if (StrUtil.isEmpty(openid)) {
|
|
|
|
|
+ log.error("openid不存在");
|
|
|
|
|
+ resp.put("code", "FAIL");
|
|
|
|
|
+ resp.put("message", "openid不存在");
|
|
|
|
|
+ return resp;
|
|
|
}
|
|
}
|
|
|
- } else {
|
|
|
|
|
- response.setStatus(500);
|
|
|
|
|
- map.put("code", "ERROR");
|
|
|
|
|
- map.put("message", "签名错误");
|
|
|
|
|
- }
|
|
|
|
|
- response.setHeader("Content-type", ContentType.JSON.toString());
|
|
|
|
|
- response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
|
|
|
|
|
- response.flushBuffer();
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
|
- log.error("系统异常", e);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
|
|
+ log.info("支付成功用户openId,openId: {}", openid);
|
|
|
|
|
|
|
|
- @RequestMapping(value = "/test", method = {org.springframework.web.bind.annotation.RequestMethod.POST, org.springframework.web.bind.annotation.RequestMethod.GET})
|
|
|
|
|
- @ResponseBody
|
|
|
|
|
- @ApiOperation("测试")
|
|
|
|
|
- public void test(HttpServletRequest request, HttpServletResponse response) {
|
|
|
|
|
- System.out.println("test=======================>");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ TWxUser wxUser = wxUserService.getByOpenId(openid);
|
|
|
|
|
+ if (ObjectUtil.isNull(wxUser)) {
|
|
|
|
|
+ log.error("支付成功用户不存在,openId: {}", openid);
|
|
|
|
|
+ resp.put("code", "FAIL");
|
|
|
|
|
+ resp.put("message", "支付成功用户不存在");
|
|
|
|
|
+ return resp;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- @RequestMapping("/get")
|
|
|
|
|
- @ResponseBody
|
|
|
|
|
- public String v3Get() throws Exception {
|
|
|
|
|
- // 获取平台证书列表
|
|
|
|
|
- try {
|
|
|
|
|
- IJPayHttpResponse response = WxPayApi.v3(
|
|
|
|
|
- RequestMethodEnum.GET,
|
|
|
|
|
- WxDomainEnum.CHINA.toString(),
|
|
|
|
|
- CertAlgorithmTypeEnum.getCertSuffixUrl(CertAlgorithmTypeEnum.RSA.getCode()),
|
|
|
|
|
- wxPayProperties.getMchId(),
|
|
|
|
|
- getSerialNumber(),
|
|
|
|
|
- null,
|
|
|
|
|
- wxPayProperties.getCertKeyPath(),
|
|
|
|
|
- "",
|
|
|
|
|
- AuthTypeEnum.RSA.getCode()
|
|
|
|
|
- );
|
|
|
|
|
- Map<String, List<String>> headers = response.getHeaders();
|
|
|
|
|
- log.info("请求头: {}", headers);
|
|
|
|
|
- String timestamp = response.getHeader("Wechatpay-Timestamp");
|
|
|
|
|
- String nonceStr = response.getHeader("Wechatpay-Nonce");
|
|
|
|
|
- String serialNumber = response.getHeader("Wechatpay-Serial");
|
|
|
|
|
- String signature = response.getHeader("Wechatpay-Signature");
|
|
|
|
|
-
|
|
|
|
|
- String body = response.getBody();
|
|
|
|
|
- int status = response.getStatus();
|
|
|
|
|
-
|
|
|
|
|
- log.info("serialNumber: {}", serialNumber);
|
|
|
|
|
- log.info("status: {}", status);
|
|
|
|
|
- log.info("body: {}", body);
|
|
|
|
|
- int isOk = 200;
|
|
|
|
|
- if (status == isOk) {
|
|
|
|
|
- JSONObject jsonObject = JSONUtil.parseObj(body);
|
|
|
|
|
- JSONArray dataArray = jsonObject.getJSONArray("data");
|
|
|
|
|
- // 默认认为只有一个平台证书
|
|
|
|
|
- JSONObject encryptObject = dataArray.getJSONObject(0);
|
|
|
|
|
- JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate");
|
|
|
|
|
- String associatedData = encryptCertificate.getStr("associated_data");
|
|
|
|
|
- String cipherText = encryptCertificate.getStr("ciphertext");
|
|
|
|
|
- String nonce = encryptCertificate.getStr("nonce");
|
|
|
|
|
- String algorithm = encryptCertificate.getStr("algorithm");
|
|
|
|
|
- String serialNo = encryptObject.getStr("serial_no");
|
|
|
|
|
- final String platSerialNo = savePlatformCert(associatedData, nonce, cipherText, algorithm, wxPayProperties.getPlatFormPath());
|
|
|
|
|
- log.info("平台证书序列号: {} serialNo: {}", platSerialNo, serialNo);
|
|
|
|
|
- // 根据证书序列号查询对应的证书来验证签名结果
|
|
|
|
|
- boolean verifySignature = WxPayKit.verifySignature(response, wxPayProperties.getPlatFormPath());
|
|
|
|
|
- log.info("verifySignature:{}", verifySignature);
|
|
|
|
|
|
|
+ // 3. 根据 attach 判断商品类型并进行不同的业务处理
|
|
|
|
|
+ if (WxPayTypeEnum.GIFT_CARD.getCode().equals(attach)) {
|
|
|
|
|
+ log.info("检测到购物卡支付成功,订单号: {}", outTradeNo);
|
|
|
|
|
+
|
|
|
|
|
+ // 3.1 更新订单支付状态
|
|
|
|
|
+ LambdaQueryWrapper<GiftCardOrder> wrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ wrapper.eq(GiftCardOrder::getOrderNo, outTradeNo);
|
|
|
|
|
+ GiftCardOrder cardOrder = this.giftCardOrderService.getOne(wrapper);
|
|
|
|
|
+ if (ObjectUtil.isNull(cardOrder)) {
|
|
|
|
|
+ log.error("订单不存在,订单号: {}", outTradeNo);
|
|
|
|
|
+ resp.put("code", "FAIL");
|
|
|
|
|
+ resp.put("message", "订单不存在");
|
|
|
|
|
+ return resp;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 3.2 检查是否已处理
|
|
|
|
|
+ if (ObjectUtil.equals(GiftCardOrderStatusEnum.PAID.getCode(), cardOrder.getStatus())) {
|
|
|
|
|
+ log.warn("订单已处理过:{}", outTradeNo);
|
|
|
|
|
+ resp.put("code", "SUCCESS");
|
|
|
|
|
+ resp.put("message", "OK");
|
|
|
|
|
+ return resp;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 3.3 处理订单相关数据
|
|
|
|
|
+ this.giftCardOrderService.processGiftCardPayment(result, wxUser, cardOrder);
|
|
|
|
|
+ } else if (WxPayTypeEnum.EMOTION_GOODS.getCode().equals(attach)) {
|
|
|
|
|
+ log.info("检测到情感服务商品支付成功,订单号: {}", outTradeNo);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- return body;
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
|
- log.error("获取平台证书列表异常", e);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- private String savePlatformCert(String associatedData, String nonce, String cipherText, String algorithm, String certPath) {
|
|
|
|
|
- try {
|
|
|
|
|
- String key3 = wxPayProperties.getMchKey();
|
|
|
|
|
- String publicKey;
|
|
|
|
|
- if (StrUtil.equals(algorithm, AuthTypeEnum.SM2.getPlatformCertAlgorithm())) {
|
|
|
|
|
- publicKey = PayKit.sm4DecryptToString(key3, cipherText, nonce, associatedData);
|
|
|
|
|
- } else {
|
|
|
|
|
- AesUtil aesUtil = new AesUtil(wxPayProperties.getMchKey().getBytes(StandardCharsets.UTF_8));
|
|
|
|
|
- // 平台证书密文解密
|
|
|
|
|
- // encrypt_certificate 中的 associated_data nonce ciphertext
|
|
|
|
|
- publicKey = aesUtil.decryptToString(
|
|
|
|
|
- associatedData.getBytes(StandardCharsets.UTF_8),
|
|
|
|
|
- nonce.getBytes(StandardCharsets.UTF_8),
|
|
|
|
|
- cipherText
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
- if (StrUtil.isNotEmpty(publicKey)) {
|
|
|
|
|
- // 保存证书
|
|
|
|
|
- FileWriter writer = new FileWriter(certPath);
|
|
|
|
|
- writer.write(publicKey);
|
|
|
|
|
- // 获取平台证书序列号
|
|
|
|
|
- X509Certificate certificate = PayKit.getCertificate(new ByteArrayInputStream(publicKey.getBytes()));
|
|
|
|
|
- return certificate.getSerialNumber().toString(16).toUpperCase();
|
|
|
|
|
- }
|
|
|
|
|
- return "";
|
|
|
|
|
|
|
+ //3. 返回成功
|
|
|
|
|
+ resp.put("code", "SUCCESS");
|
|
|
|
|
+ resp.put("message", "OK");
|
|
|
|
|
+ } catch (WxPayException e) {
|
|
|
|
|
+ log.error("微信支付回调异常:{}", e.getMessage(), e);
|
|
|
|
|
+ resp.put("code", "FAIL");
|
|
|
|
|
+ resp.put("message", e.getMessage());
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
- log.error("保存平台证书异常", e);
|
|
|
|
|
- return e.getMessage();
|
|
|
|
|
|
|
+ log.error("支付回调处理异常:{}", e.getMessage(), e);
|
|
|
|
|
+ resp.put("code", "FAIL");
|
|
|
|
|
+ resp.put("message", "系统异常");
|
|
|
}
|
|
}
|
|
|
|
|
+ return resp;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 微信批量提现
|
|
* 微信批量提现
|
|
|
|
|
+ *
|
|
|
* @param openId
|
|
* @param openId
|
|
|
* @return String
|
|
* @return String
|
|
|
*/
|
|
*/
|
|
@@ -463,6 +319,7 @@ public class PayController {
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 微信退款回调接口
|
|
* 微信退款回调接口
|
|
|
|
|
+ *
|
|
|
* @param request
|
|
* @param request
|
|
|
* @param response
|
|
* @param response
|
|
|
*/
|
|
*/
|