|
@@ -1,6 +1,7 @@
|
|
|
package shop.alien.dining.strategy.payment.impl;
|
|
package shop.alien.dining.strategy.payment.impl;
|
|
|
|
|
|
|
|
import com.alibaba.fastjson2.JSONObject;
|
|
import com.alibaba.fastjson2.JSONObject;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
import com.fasterxml.jackson.databind.JsonNode;
|
|
import com.fasterxml.jackson.databind.JsonNode;
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
@@ -16,16 +17,17 @@ import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
|
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
|
|
import org.springframework.stereotype.Component;
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
import shop.alien.dining.service.StoreOrderService;
|
|
import shop.alien.dining.service.StoreOrderService;
|
|
|
import shop.alien.dining.strategy.payment.PaymentStrategy;
|
|
import shop.alien.dining.strategy.payment.PaymentStrategy;
|
|
|
import shop.alien.dining.util.WXPayUtility;
|
|
import shop.alien.dining.util.WXPayUtility;
|
|
|
import shop.alien.dining.util.WeChatPayUtil;
|
|
import shop.alien.dining.util.WeChatPayUtil;
|
|
|
import shop.alien.entity.result.R;
|
|
import shop.alien.entity.result.R;
|
|
|
import shop.alien.entity.store.StoreOrder;
|
|
import shop.alien.entity.store.StoreOrder;
|
|
|
|
|
+import shop.alien.entity.store.StorePaymentConfig;
|
|
|
|
|
+import shop.alien.mapper.StorePaymentConfigMapper;
|
|
|
import shop.alien.util.common.constant.PaymentEnum;
|
|
import shop.alien.util.common.constant.PaymentEnum;
|
|
|
-import shop.alien.util.system.OSUtil;
|
|
|
|
|
|
|
|
|
|
-import javax.annotation.PostConstruct;
|
|
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
import java.io.IOException;
|
|
import java.io.IOException;
|
|
|
import java.io.UncheckedIOException;
|
|
import java.io.UncheckedIOException;
|
|
@@ -58,50 +60,6 @@ public class WeChatPaymentMininProgramStrategyImpl implements PaymentStrategy {
|
|
|
@Value("${payment.wechatPay.host}")
|
|
@Value("${payment.wechatPay.host}")
|
|
|
private String wechatPayApiHost;
|
|
private String wechatPayApiHost;
|
|
|
/**
|
|
/**
|
|
|
- * 微信支付商户id
|
|
|
|
|
- */
|
|
|
|
|
- @Value("${payment.wechatPay.business.mchId}")
|
|
|
|
|
- private String mchId;
|
|
|
|
|
- // TODO:小程序未注册-> 把下面的所有默认的去掉然后从配置文件中读取
|
|
|
|
|
- /**
|
|
|
|
|
- * 微信支付小程序应用id
|
|
|
|
|
- */
|
|
|
|
|
- @Value("${wechat.miniprogram.appId}")
|
|
|
|
|
- private String appId;
|
|
|
|
|
- /**
|
|
|
|
|
- * 微信支付商户证书序列号
|
|
|
|
|
- */
|
|
|
|
|
- @Value("${payment.wechatPay.business.merchantSerialNumber}")
|
|
|
|
|
- private String certificateSerialNo;
|
|
|
|
|
- /**
|
|
|
|
|
- * 微信支付公钥id
|
|
|
|
|
- */
|
|
|
|
|
- @Value("${payment.wechatPay.business.wechatPayPublicKeyId}")
|
|
|
|
|
- private String wechatPayPublicKeyId;
|
|
|
|
|
- /**
|
|
|
|
|
- * 微信支付商户私钥路径
|
|
|
|
|
- */
|
|
|
|
|
- @Value("${payment.wechatPay.business.win.privateKeyPath}")
|
|
|
|
|
- private String privateWinKeyPath;
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 微信支付商户私钥路径(Linux环境)
|
|
|
|
|
- */
|
|
|
|
|
- @Value("${payment.wechatPay.business.linux.privateKeyPath}")
|
|
|
|
|
- private String privateLinuxKeyPath;
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 微信支付公钥路径
|
|
|
|
|
- */
|
|
|
|
|
- @Value("${payment.wechatPay.business.win.wechatPayPublicKeyFilePath}")
|
|
|
|
|
- private String wechatWinPayPublicKeyFilePath;
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 微信支付公钥路径(Linux环境)
|
|
|
|
|
- */
|
|
|
|
|
- @Value("${payment.wechatPay.business.linux.wechatPayPublicKeyFilePath}")
|
|
|
|
|
- private String wechatLinuxPayPublicKeyFilePath;
|
|
|
|
|
- /**
|
|
|
|
|
* 微信支付预支付路径
|
|
* 微信支付预支付路径
|
|
|
*/
|
|
*/
|
|
|
@Value("${payment.wechatPay.business.miniProgram.prePayPath:/v3/pay/transactions/jsapi}")
|
|
@Value("${payment.wechatPay.business.miniProgram.prePayPath:/v3/pay/transactions/jsapi}")
|
|
@@ -125,96 +83,122 @@ public class WeChatPaymentMininProgramStrategyImpl implements PaymentStrategy {
|
|
|
@Value("${payment.wechatPay.searchOrderByOutTradeNoPath}")
|
|
@Value("${payment.wechatPay.searchOrderByOutTradeNoPath}")
|
|
|
private String searchOrderByOutTradeNoPath;
|
|
private String searchOrderByOutTradeNoPath;
|
|
|
|
|
|
|
|
- @Value("${payment.wechatPay.business.apiV3key}")
|
|
|
|
|
- private String apiV3key;
|
|
|
|
|
-
|
|
|
|
|
private final StoreOrderService storeOrderService;
|
|
private final StoreOrderService storeOrderService;
|
|
|
-
|
|
|
|
|
|
|
+ private final StorePaymentConfigMapper storePaymentConfigMapper;
|
|
|
private final ObjectMapper objectMapper;
|
|
private final ObjectMapper objectMapper;
|
|
|
|
|
|
|
|
private static String POSTMETHOD = "POST";
|
|
private static String POSTMETHOD = "POST";
|
|
|
private static String GETMETHOD = "GET";
|
|
private static String GETMETHOD = "GET";
|
|
|
|
|
|
|
|
- private PrivateKey privateKey;
|
|
|
|
|
- private PublicKey wechatPayPublicKey;
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 根据店铺ID从 MySQL 获取支付配置(本系统内部调用时使用)
|
|
|
|
|
+ */
|
|
|
|
|
+ private StorePaymentConfig getConfigByStoreId(Integer storeId) {
|
|
|
|
|
+ if (storeId == null) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ LambdaQueryWrapper<StorePaymentConfig> wrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ wrapper.eq(StorePaymentConfig::getStoreId, storeId);
|
|
|
|
|
+ return storePaymentConfigMapper.selectOne(wrapper);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 根据微信支付公钥序列号获取支付配置(外部回调如支付通知时使用,通过请求头 Wechatpay-Serial 判断)
|
|
|
|
|
+ */
|
|
|
|
|
+ private StorePaymentConfig getConfigByWechatPayPublicKeyId(String wechatPayPublicKeyId) {
|
|
|
|
|
+ if (!StringUtils.hasText(wechatPayPublicKeyId)) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ LambdaQueryWrapper<StorePaymentConfig> wrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ wrapper.eq(StorePaymentConfig::getWechatPayPublicKeyId, wechatPayPublicKeyId);
|
|
|
|
|
+ return storePaymentConfigMapper.selectOne(wrapper);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- @PostConstruct
|
|
|
|
|
- public void setWeChatPaymentConfig() {
|
|
|
|
|
- String privateKeyPath;
|
|
|
|
|
- String wechatPayPublicKeyFilePath;
|
|
|
|
|
- if ("windows".equals(OSUtil.getOsName())) {
|
|
|
|
|
- privateKeyPath = privateWinKeyPath;
|
|
|
|
|
- wechatPayPublicKeyFilePath = wechatWinPayPublicKeyFilePath;
|
|
|
|
|
- } else {
|
|
|
|
|
- privateKeyPath = privateLinuxKeyPath;
|
|
|
|
|
- wechatPayPublicKeyFilePath = wechatLinuxPayPublicKeyFilePath;
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 从 StorePaymentConfig 加载商户私钥
|
|
|
|
|
+ */
|
|
|
|
|
+ private PrivateKey loadPrivateKeyFromConfig(StorePaymentConfig config) {
|
|
|
|
|
+ if (config == null) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (config.getWechatPrivateKeyFile() != null && config.getWechatPrivateKeyFile().length > 0) {
|
|
|
|
|
+ String keyStr = new String(config.getWechatPrivateKeyFile(), StandardCharsets.UTF_8);
|
|
|
|
|
+ return WXPayUtility.loadPrivateKeyFromString(keyStr);
|
|
|
}
|
|
}
|
|
|
- this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyPath);
|
|
|
|
|
- this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath);
|
|
|
|
|
|
|
+ if (StringUtils.hasText(config.getWechatPrivateKeyPath())) {
|
|
|
|
|
+ return WXPayUtility.loadPrivateKeyFromPath(config.getWechatPrivateKeyPath());
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- @Override
|
|
|
|
|
- public R createPrePayOrder(String price, String subject, String payer, String orderNo) throws Exception {
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 从 StorePaymentConfig 加载微信支付公钥
|
|
|
|
|
+ */
|
|
|
|
|
+ private PublicKey loadPublicKeyFromConfig(StorePaymentConfig config) {
|
|
|
|
|
+ if (config == null) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (config.getWechatPayPublicKeyFile() != null && config.getWechatPayPublicKeyFile().length > 0) {
|
|
|
|
|
+ String keyStr = new String(config.getWechatPayPublicKeyFile(), StandardCharsets.UTF_8);
|
|
|
|
|
+ return WXPayUtility.loadPublicKeyFromString(keyStr);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (StringUtils.hasText(config.getWechatPayPublicKeyFilePath())) {
|
|
|
|
|
+ return WXPayUtility.loadPublicKeyFromPath(config.getWechatPayPublicKeyFilePath());
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public R createPrePayOrder(String price, String subject, String payer, String orderNo, Integer storeId) throws Exception {
|
|
|
|
|
+ // 本系统调用:通过 storeId 从 MySQL 获取店铺支付配置
|
|
|
|
|
+ if (storeId == null) {
|
|
|
|
|
+ log.warn("createPrePayOrder 缺少 storeId,无法获取支付配置");
|
|
|
|
|
+ return R.fail("店铺ID不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ StorePaymentConfig config = getConfigByStoreId(storeId);
|
|
|
|
|
+ if (config == null) {
|
|
|
|
|
+ log.warn("未找到店铺支付配置 storeId={}", storeId);
|
|
|
|
|
+ return R.fail("未找到该店铺的微信支付配置");
|
|
|
|
|
+ }
|
|
|
|
|
+ PrivateKey privateKey = loadPrivateKeyFromConfig(config);
|
|
|
|
|
+ if (privateKey == null) {
|
|
|
|
|
+ return R.fail("店铺微信支付私钥未配置或加载失败");
|
|
|
|
|
+ }
|
|
|
|
|
+ String appId = config.getWechatMiniAppId() != null ? config.getWechatMiniAppId() : config.getWechatAppId();
|
|
|
|
|
+ if (!StringUtils.hasText(appId)) {
|
|
|
|
|
+ return R.fail("店铺微信小程序 appId 未配置");
|
|
|
|
|
+ }
|
|
|
|
|
+ String mchId = config.getWechatMchId();
|
|
|
|
|
+ if (!StringUtils.hasText(mchId)) {
|
|
|
|
|
+ return R.fail("店铺微信支付商户号未配置");
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
DirectAPIv3JsapiPrepayRequest request = new DirectAPIv3JsapiPrepayRequest();
|
|
DirectAPIv3JsapiPrepayRequest request = new DirectAPIv3JsapiPrepayRequest();
|
|
|
request.appid = appId;
|
|
request.appid = appId;
|
|
|
request.mchid = mchId;
|
|
request.mchid = mchId;
|
|
|
request.description = subject;
|
|
request.description = subject;
|
|
|
request.outTradeNo = orderNo;
|
|
request.outTradeNo = orderNo;
|
|
|
-// request.timeExpire = "2018-06-08T10:34:56+08:00";
|
|
|
|
|
-// request.attach = "自定义数据说明";
|
|
|
|
|
request.notifyUrl = prePayNotifyUrl;
|
|
request.notifyUrl = prePayNotifyUrl;
|
|
|
-// request.goodsTag = "WXG";
|
|
|
|
|
-// request.supportFapiao = false;
|
|
|
|
|
request.amount = new CommonAmountInfo();
|
|
request.amount = new CommonAmountInfo();
|
|
|
request.amount.total = Long.parseLong(price);
|
|
request.amount.total = Long.parseLong(price);
|
|
|
-// request.amount.currency = "CNY";
|
|
|
|
|
request.payer = new JsapiReqPayerInfo();
|
|
request.payer = new JsapiReqPayerInfo();
|
|
|
request.payer.openid = payer;
|
|
request.payer.openid = payer;
|
|
|
- /*request.detail = new CouponInfo();
|
|
|
|
|
- request.detail.costPrice = 608800L;
|
|
|
|
|
- request.detail.invoiceId = "微信123";
|
|
|
|
|
- request.detail.goodsDetail = new ArrayList<>();
|
|
|
|
|
- {
|
|
|
|
|
- GoodsDetail goodsDetailItem = new GoodsDetail();
|
|
|
|
|
- goodsDetailItem.merchantGoodsId = "1246464644";
|
|
|
|
|
- goodsDetailItem.wechatpayGoodsId = "1001";
|
|
|
|
|
- goodsDetailItem.goodsName = "iPhoneX 256G";
|
|
|
|
|
- goodsDetailItem.quantity = 1L;
|
|
|
|
|
- goodsDetailItem.unitPrice = 528800L;
|
|
|
|
|
- request.detail.goodsDetail.add(goodsDetailItem);
|
|
|
|
|
- };*/
|
|
|
|
|
- /*request.sceneInfo = new CommonSceneInfo();
|
|
|
|
|
- request.sceneInfo.payerClientIp = "14.23.150.211";
|
|
|
|
|
- request.sceneInfo.deviceId = "013467007045764";
|
|
|
|
|
- request.sceneInfo.storeInfo = new StoreInfo();
|
|
|
|
|
- request.sceneInfo.storeInfo.id = "0001";
|
|
|
|
|
- request.sceneInfo.storeInfo.name = "腾讯大厦分店";
|
|
|
|
|
- request.sceneInfo.storeInfo.areaCode = "440305";
|
|
|
|
|
- request.sceneInfo.storeInfo.address = "广东省深圳市南山区科技中一道10000号";*/
|
|
|
|
|
-/* request.settleInfo = new SettleInfo();
|
|
|
|
|
- request.settleInfo.profitSharing = false;*/
|
|
|
|
|
try {
|
|
try {
|
|
|
- DirectAPIv3JsapiPrepayResponse response = doCreatePrePayOrder(request);
|
|
|
|
|
- // TODO: 请求成功,继续业务逻辑
|
|
|
|
|
|
|
+ DirectAPIv3JsapiPrepayResponse response = doCreatePrePayOrder(request, config, privateKey);
|
|
|
log.info("微信预支付订单创建成功,预支付ID:{}", response.prepayId);
|
|
log.info("微信预支付订单创建成功,预支付ID:{}", response.prepayId);
|
|
|
- Map<String,String> result = new HashMap<>();
|
|
|
|
|
- response.prepayId = "prepay_id="+response.prepayId;
|
|
|
|
|
|
|
+ Map<String, String> result = new HashMap<>();
|
|
|
|
|
+ response.prepayId = "prepay_id=" + response.prepayId;
|
|
|
result.put("prepayId", response.prepayId);
|
|
result.put("prepayId", response.prepayId);
|
|
|
result.put("appId", appId);
|
|
result.put("appId", appId);
|
|
|
result.put("mchId", mchId);
|
|
result.put("mchId", mchId);
|
|
|
result.put("orderNo", request.outTradeNo);
|
|
result.put("orderNo", request.outTradeNo);
|
|
|
- long timestamp = System.currentTimeMillis() / 1000; // 时间戳
|
|
|
|
|
- String nonce = WXPayUtility.createNonce(32); // 随机字符串
|
|
|
|
|
|
|
+ long timestamp = System.currentTimeMillis() / 1000;
|
|
|
|
|
+ String nonce = WXPayUtility.createNonce(32);
|
|
|
String message = String.format("%s\n%s\n%s\n%s\n", appId, timestamp, nonce, response.prepayId);
|
|
String message = String.format("%s\n%s\n%s\n%s\n", appId, timestamp, nonce, response.prepayId);
|
|
|
-// String sign = WXPayUtility.sign(message, sha256withRSA, privateKey);
|
|
|
|
|
-
|
|
|
|
|
Signature sign = Signature.getInstance("SHA256withRSA");
|
|
Signature sign = Signature.getInstance("SHA256withRSA");
|
|
|
sign.initSign(privateKey);
|
|
sign.initSign(privateKey);
|
|
|
sign.update(message.getBytes(StandardCharsets.UTF_8));
|
|
sign.update(message.getBytes(StandardCharsets.UTF_8));
|
|
|
- result.put("signType","RSA");
|
|
|
|
|
|
|
+ result.put("signType", "RSA");
|
|
|
result.put("sign", Base64.getEncoder().encodeToString(sign.sign()));
|
|
result.put("sign", Base64.getEncoder().encodeToString(sign.sign()));
|
|
|
result.put("timestamp", String.valueOf(timestamp));
|
|
result.put("timestamp", String.valueOf(timestamp));
|
|
|
result.put("nonce", nonce);
|
|
result.put("nonce", nonce);
|
|
@@ -228,58 +212,47 @@ public class WeChatPaymentMininProgramStrategyImpl implements PaymentStrategy {
|
|
|
@Override
|
|
@Override
|
|
|
public R handleNotify(String notifyData, HttpServletRequest request) throws Exception {
|
|
public R handleNotify(String notifyData, HttpServletRequest request) throws Exception {
|
|
|
log.info("[微信支付回调] 进入 handleNotify, notifyData 长度={}", notifyData != null ? notifyData.length() : 0);
|
|
log.info("[微信支付回调] 进入 handleNotify, notifyData 长度={}", notifyData != null ? notifyData.length() : 0);
|
|
|
- /*{
|
|
|
|
|
- "id": "EV-2018022511223320873",
|
|
|
|
|
- "create_time": "2015-05-20T13:29:35+08:00",
|
|
|
|
|
- "resource_type": "encrypt-resource",
|
|
|
|
|
- "event_type": "TRANSACTION.SUCCESS",
|
|
|
|
|
- "summary": "支付成功",
|
|
|
|
|
- "resource": {
|
|
|
|
|
- "original_type": "transaction",
|
|
|
|
|
- "algorithm": "AEAD_AES_256_GCM",
|
|
|
|
|
- "ciphertext": "",
|
|
|
|
|
- "associated_data": "",
|
|
|
|
|
- "nonce": ""
|
|
|
|
|
- }
|
|
|
|
|
-}*/
|
|
|
|
|
- // 1. 提取微信支付回调请求头中的验签参数(对应图片里的4个核心参数)
|
|
|
|
|
- String serial = request.getHeader("Wechatpay-Serial"); // 证书序列号/公钥ID
|
|
|
|
|
- String signature = request.getHeader("Wechatpay-Signature"); // 签名值
|
|
|
|
|
- String timestamp = request.getHeader("Wechatpay-Timestamp"); // 时间戳
|
|
|
|
|
- String nonce = request.getHeader("Wechatpay-Nonce"); // 随机字符串
|
|
|
|
|
-
|
|
|
|
|
- // 2. 校验核心验签参数是否为空
|
|
|
|
|
|
|
+ // 外部回调:通过请求头 Wechatpay-Serial 从 MySQL 查找对应店铺的支付配置
|
|
|
|
|
+ String serial = request.getHeader("Wechatpay-Serial");
|
|
|
|
|
+ String signature = request.getHeader("Wechatpay-Signature");
|
|
|
|
|
+ String timestamp = request.getHeader("Wechatpay-Timestamp");
|
|
|
|
|
+ String nonce = request.getHeader("Wechatpay-Nonce");
|
|
|
|
|
+
|
|
|
if (serial == null || signature == null || timestamp == null || nonce == null) {
|
|
if (serial == null || signature == null || timestamp == null || nonce == null) {
|
|
|
log.warn("微信支付回调验签失败:核心头参数缺失 serial={}, signature={}, timestamp={}, nonce={}",
|
|
log.warn("微信支付回调验签失败:核心头参数缺失 serial={}, signature={}, timestamp={}, nonce={}",
|
|
|
serial, signature, timestamp, nonce);
|
|
serial, signature, timestamp, nonce);
|
|
|
return R.fail("验签参数缺失");
|
|
return R.fail("验签参数缺失");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 2.1 签名探测:微信会发 WECHATPAY/SIGNTEST/ 开头的 Signature 检测商户是否正确验签,需直接返回成功
|
|
|
|
|
if (signature.startsWith("WECHATPAY/SIGNTEST/")) {
|
|
if (signature.startsWith("WECHATPAY/SIGNTEST/")) {
|
|
|
log.info("[微信支付回调] 收到签名探测请求,直接应答成功");
|
|
log.info("[微信支付回调] 收到签名探测请求,直接应答成功");
|
|
|
return R.success("OK");
|
|
return R.success("OK");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ StorePaymentConfig config = getConfigByWechatPayPublicKeyId(serial);
|
|
|
|
|
+ if (config == null) {
|
|
|
|
|
+ log.warn("微信支付回调:未找到公钥序列号对应的店铺配置 serial={}", serial);
|
|
|
|
|
+ return R.fail("未找到对应支付配置");
|
|
|
|
|
+ }
|
|
|
|
|
+ PublicKey wechatPayPublicKey = loadPublicKeyFromConfig(config);
|
|
|
|
|
+ if (wechatPayPublicKey == null) {
|
|
|
|
|
+ log.warn("微信支付回调:加载公钥失败 serial={}", serial);
|
|
|
|
|
+ return R.fail("验签公钥加载失败");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
StringBuilder signStr = new StringBuilder();
|
|
StringBuilder signStr = new StringBuilder();
|
|
|
- signStr.append(timestamp).append("\n"); // 第一行:应答时间戳
|
|
|
|
|
- signStr.append(nonce).append("\n"); // 第二行:应答随机串
|
|
|
|
|
- signStr.append(notifyData).append("\n"); // 第三行:应答报文主体(末尾必须加\n
|
|
|
|
|
|
|
+ signStr.append(timestamp).append("\n");
|
|
|
|
|
+ signStr.append(nonce).append("\n");
|
|
|
|
|
+ signStr.append(notifyData).append("\n");
|
|
|
Signature sign = Signature.getInstance("SHA256withRSA");
|
|
Signature sign = Signature.getInstance("SHA256withRSA");
|
|
|
- // 步骤2:Base64解码签名串(对应命令行openssl base64 -d逻辑)
|
|
|
|
|
byte[] signatureBytes = Base64.getDecoder().decode(signature);
|
|
byte[] signatureBytes = Base64.getDecoder().decode(signature);
|
|
|
-
|
|
|
|
|
- // 步骤3:初始化SHA256withRSA签名验证器(对应命令行-sha256参数)
|
|
|
|
|
sign.initVerify(wechatPayPublicKey);
|
|
sign.initVerify(wechatPayPublicKey);
|
|
|
-
|
|
|
|
|
- // 步骤4:传入验签名串(编码为UTF-8,对应命令行EOF里的内容)
|
|
|
|
|
sign.update(signStr.toString().getBytes(StandardCharsets.UTF_8));
|
|
sign.update(signStr.toString().getBytes(StandardCharsets.UTF_8));
|
|
|
|
|
|
|
|
- // 步骤5:执行验签(对应命令行-verify逻辑)
|
|
|
|
|
if (sign.verify(signatureBytes)) {
|
|
if (sign.verify(signatureBytes)) {
|
|
|
- // 文档要求:验签通过后先应答(5 秒内),再异步处理业务,避免超时导致微信重复回调
|
|
|
|
|
final String notifyDataCopy = notifyData;
|
|
final String notifyDataCopy = notifyData;
|
|
|
- CompletableFuture.runAsync(() -> processNotifyBusiness(notifyDataCopy));
|
|
|
|
|
|
|
+ final StorePaymentConfig configCopy = config;
|
|
|
|
|
+ CompletableFuture.runAsync(() -> processNotifyBusiness(notifyDataCopy, configCopy));
|
|
|
return R.success("OK");
|
|
return R.success("OK");
|
|
|
} else {
|
|
} else {
|
|
|
return R.fail("Verified error");
|
|
return R.fail("Verified error");
|
|
@@ -288,9 +261,14 @@ public class WeChatPaymentMininProgramStrategyImpl implements PaymentStrategy {
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 异步处理回调业务:解密并更新订单状态(文档建议应答后再处理业务,避免超时)
|
|
* 异步处理回调业务:解密并更新订单状态(文档建议应答后再处理业务,避免超时)
|
|
|
|
|
+ * @param config 已通过 Wechatpay-Serial 解析得到的店铺支付配置,内含 apiV3Key
|
|
|
*/
|
|
*/
|
|
|
- private void processNotifyBusiness(String notifyData) {
|
|
|
|
|
|
|
+ private void processNotifyBusiness(String notifyData, StorePaymentConfig config) {
|
|
|
try {
|
|
try {
|
|
|
|
|
+ if (config == null || !StringUtils.hasText(config.getApiV3Key())) {
|
|
|
|
|
+ log.warn("微信支付回调:无可用 apiV3Key 无法解密");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
JsonNode rootNode = objectMapper.readTree(notifyData);
|
|
JsonNode rootNode = objectMapper.readTree(notifyData);
|
|
|
JsonNode resourceNode = rootNode.get("resource");
|
|
JsonNode resourceNode = rootNode.get("resource");
|
|
|
if (resourceNode == null) {
|
|
if (resourceNode == null) {
|
|
@@ -299,7 +277,6 @@ public class WeChatPaymentMininProgramStrategyImpl implements PaymentStrategy {
|
|
|
}
|
|
}
|
|
|
String encryptAlgorithm = resourceNode.get("algorithm").asText();
|
|
String encryptAlgorithm = resourceNode.get("algorithm").asText();
|
|
|
String resourceNonce = resourceNode.get("nonce").asText();
|
|
String resourceNonce = resourceNode.get("nonce").asText();
|
|
|
- // associated_data 选填,可能为空(文档:该字段可能为空)
|
|
|
|
|
String associatedData = resourceNode.has("associated_data") && !resourceNode.get("associated_data").isNull()
|
|
String associatedData = resourceNode.has("associated_data") && !resourceNode.get("associated_data").isNull()
|
|
|
? resourceNode.get("associated_data").asText() : "";
|
|
? resourceNode.get("associated_data").asText() : "";
|
|
|
String ciphertext = resourceNode.get("ciphertext").asText();
|
|
String ciphertext = resourceNode.get("ciphertext").asText();
|
|
@@ -308,7 +285,7 @@ public class WeChatPaymentMininProgramStrategyImpl implements PaymentStrategy {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
String plainBusinessData = WeChatPayUtil.decrypt(
|
|
String plainBusinessData = WeChatPayUtil.decrypt(
|
|
|
- apiV3key, resourceNonce, associatedData, ciphertext);
|
|
|
|
|
|
|
+ config.getApiV3Key(), resourceNonce, associatedData, ciphertext);
|
|
|
log.info("微信支付回调解密后的业务信息:{}", plainBusinessData);
|
|
log.info("微信支付回调解密后的业务信息:{}", plainBusinessData);
|
|
|
JSONObject jsonObject = JSONObject.parseObject(plainBusinessData);
|
|
JSONObject jsonObject = JSONObject.parseObject(plainBusinessData);
|
|
|
String tradeState = jsonObject.getString("trade_state");
|
|
String tradeState = jsonObject.getString("trade_state");
|
|
@@ -335,13 +312,25 @@ public class WeChatPaymentMininProgramStrategyImpl implements PaymentStrategy {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
- public R searchOrderByOutTradeNoPath(String transactionId) throws Exception {
|
|
|
|
|
- log.info("查询微信支付订单状态,交易订单号:{}", transactionId);
|
|
|
|
|
|
|
+ public R searchOrderByOutTradeNoPath(String transactionId, Integer storeId) throws Exception {
|
|
|
|
|
+ log.info("查询微信支付订单状态,交易订单号:{}, storeId:{}", transactionId, storeId);
|
|
|
|
|
+ if (storeId == null) {
|
|
|
|
|
+ return R.fail("店铺ID不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ StorePaymentConfig config = getConfigByStoreId(storeId);
|
|
|
|
|
+ if (config == null) {
|
|
|
|
|
+ return R.fail("未找到该店铺的微信支付配置");
|
|
|
|
|
+ }
|
|
|
|
|
+ PrivateKey privateKey = loadPrivateKeyFromConfig(config);
|
|
|
|
|
+ PublicKey publicKey = loadPublicKeyFromConfig(config);
|
|
|
|
|
+ if (privateKey == null || publicKey == null) {
|
|
|
|
|
+ return R.fail("店铺微信支付密钥未配置或加载失败");
|
|
|
|
|
+ }
|
|
|
QueryByWxTradeNoRequest request = new QueryByWxTradeNoRequest();
|
|
QueryByWxTradeNoRequest request = new QueryByWxTradeNoRequest();
|
|
|
request.transactionId = transactionId;
|
|
request.transactionId = transactionId;
|
|
|
- request.mchid = mchId;
|
|
|
|
|
|
|
+ request.mchid = config.getWechatMchId();
|
|
|
try {
|
|
try {
|
|
|
- DirectAPIv3QueryResponse response = searchOrderRun(request);
|
|
|
|
|
|
|
+ DirectAPIv3QueryResponse response = searchOrderRun(request, config, privateKey, publicKey);
|
|
|
return R.data(response);
|
|
return R.data(response);
|
|
|
} catch (WXPayUtility.ApiException e) {
|
|
} catch (WXPayUtility.ApiException e) {
|
|
|
log.error("查询微信支付订单状态失败,状态码:{},错误信息:{}", e.getErrorCode(), e.getMessage());
|
|
log.error("查询微信支付订单状态失败,状态码:{},错误信息:{}", e.getErrorCode(), e.getMessage());
|
|
@@ -349,11 +338,14 @@ public class WeChatPaymentMininProgramStrategyImpl implements PaymentStrategy {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public DirectAPIv3QueryResponse searchOrderRun(QueryByWxTradeNoRequest request) {
|
|
|
|
|
|
|
+ public DirectAPIv3QueryResponse searchOrderRun(QueryByWxTradeNoRequest request,
|
|
|
|
|
+ StorePaymentConfig config,
|
|
|
|
|
+ PrivateKey privateKey,
|
|
|
|
|
+ PublicKey wechatPayPublicKey) {
|
|
|
String uri = searchOrderByOutTradeNoPath;
|
|
String uri = searchOrderByOutTradeNoPath;
|
|
|
uri = uri.replace("{out_trade_no}", WXPayUtility.urlEncode(request.transactionId));
|
|
uri = uri.replace("{out_trade_no}", WXPayUtility.urlEncode(request.transactionId));
|
|
|
Map<String, Object> args = new HashMap<>();
|
|
Map<String, Object> args = new HashMap<>();
|
|
|
- args.put("mchid", mchId);
|
|
|
|
|
|
|
+ args.put("mchid", request.mchid);
|
|
|
String queryString = WXPayUtility.urlEncode(args);
|
|
String queryString = WXPayUtility.urlEncode(args);
|
|
|
if (!queryString.isEmpty()) {
|
|
if (!queryString.isEmpty()) {
|
|
|
uri = uri + "?" + queryString;
|
|
uri = uri + "?" + queryString;
|
|
@@ -361,21 +353,17 @@ public class WeChatPaymentMininProgramStrategyImpl implements PaymentStrategy {
|
|
|
|
|
|
|
|
Request.Builder reqBuilder = new Request.Builder().url(wechatPayApiHost + uri);
|
|
Request.Builder reqBuilder = new Request.Builder().url(wechatPayApiHost + uri);
|
|
|
reqBuilder.addHeader("Accept", "application/json");
|
|
reqBuilder.addHeader("Accept", "application/json");
|
|
|
- reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
|
|
|
|
|
- reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchId, certificateSerialNo, privateKey, GETMETHOD, uri, null));
|
|
|
|
|
|
|
+ reqBuilder.addHeader("Wechatpay-Serial", config.getWechatPayPublicKeyId());
|
|
|
|
|
+ reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(request.mchid, config.getMerchantSerialNumber(), privateKey, GETMETHOD, uri, null));
|
|
|
reqBuilder.method(GETMETHOD, null);
|
|
reqBuilder.method(GETMETHOD, null);
|
|
|
Request httpRequest = reqBuilder.build();
|
|
Request httpRequest = reqBuilder.build();
|
|
|
|
|
|
|
|
- // 发送HTTP请求
|
|
|
|
|
OkHttpClient client = new OkHttpClient.Builder().build();
|
|
OkHttpClient client = new OkHttpClient.Builder().build();
|
|
|
try (Response httpResponse = client.newCall(httpRequest).execute()) {
|
|
try (Response httpResponse = client.newCall(httpRequest).execute()) {
|
|
|
String respBody = WXPayUtility.extractBody(httpResponse);
|
|
String respBody = WXPayUtility.extractBody(httpResponse);
|
|
|
if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
|
|
if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
|
|
|
- // 2XX 成功,验证应答签名
|
|
|
|
|
- WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
|
|
|
|
|
|
|
+ WXPayUtility.validateResponse(config.getWechatPayPublicKeyId(), wechatPayPublicKey,
|
|
|
httpResponse.headers(), respBody);
|
|
httpResponse.headers(), respBody);
|
|
|
-
|
|
|
|
|
- // 从HTTP应答报文构建返回数据
|
|
|
|
|
return WXPayUtility.fromJson(respBody, DirectAPIv3QueryResponse.class);
|
|
return WXPayUtility.fromJson(respBody, DirectAPIv3QueryResponse.class);
|
|
|
} else {
|
|
} else {
|
|
|
throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
|
|
throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
|
|
@@ -391,21 +379,31 @@ public class WeChatPaymentMininProgramStrategyImpl implements PaymentStrategy {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
- public R searchRefundRecordByOutRefundNo(String outRefundNo) throws Exception {
|
|
|
|
|
- // 1. 初始化日志(推荐使用SLF4J,替代System.out.println)
|
|
|
|
|
|
|
+ public R searchRefundRecordByOutRefundNo(String outRefundNo, Integer storeId) throws Exception {
|
|
|
Logger logger = LoggerFactory.getLogger(this.getClass());
|
|
Logger logger = LoggerFactory.getLogger(this.getClass());
|
|
|
|
|
|
|
|
- // 2. 参数校验(前置防御,避免无效请求)
|
|
|
|
|
if (outRefundNo == null || outRefundNo.trim().isEmpty()) {
|
|
if (outRefundNo == null || outRefundNo.trim().isEmpty()) {
|
|
|
logger.error("微信退款查询失败:外部退款单号为空");
|
|
logger.error("微信退款查询失败:外部退款单号为空");
|
|
|
return R.fail("外部退款单号不能为空");
|
|
return R.fail("外部退款单号不能为空");
|
|
|
}
|
|
}
|
|
|
|
|
+ if (storeId == null) {
|
|
|
|
|
+ return R.fail("店铺ID不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ StorePaymentConfig config = getConfigByStoreId(storeId);
|
|
|
|
|
+ if (config == null) {
|
|
|
|
|
+ return R.fail("未找到该店铺的微信支付配置");
|
|
|
|
|
+ }
|
|
|
|
|
+ PrivateKey privateKey = loadPrivateKeyFromConfig(config);
|
|
|
|
|
+ PublicKey publicKey = loadPublicKeyFromConfig(config);
|
|
|
|
|
+ if (privateKey == null || publicKey == null) {
|
|
|
|
|
+ return R.fail("店铺微信支付密钥未配置或加载失败");
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
|
|
QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
|
|
|
request.setOutRefundNo(outRefundNo);
|
|
request.setOutRefundNo(outRefundNo);
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
- Refund response = doSearchRefundRecordByOutRefundNo(request);
|
|
|
|
|
|
|
+ Refund response = doSearchRefundRecordByOutRefundNo(request, config, privateKey, publicKey);
|
|
|
|
|
|
|
|
// 3. 空值校验(避免response为空导致空指针)
|
|
// 3. 空值校验(避免response为空导致空指针)
|
|
|
if (response == null) {
|
|
if (response == null) {
|
|
@@ -463,26 +461,25 @@ public class WeChatPaymentMininProgramStrategyImpl implements PaymentStrategy {
|
|
|
return PaymentEnum.WECHAT_PAY_MININ_PROGRAM.getType();
|
|
return PaymentEnum.WECHAT_PAY_MININ_PROGRAM.getType();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public Refund doSearchRefundRecordByOutRefundNo(QueryByOutRefundNoRequest request) {
|
|
|
|
|
|
|
+ public Refund doSearchRefundRecordByOutRefundNo(QueryByOutRefundNoRequest request,
|
|
|
|
|
+ StorePaymentConfig config,
|
|
|
|
|
+ PrivateKey privateKey,
|
|
|
|
|
+ PublicKey wechatPayPublicKey) {
|
|
|
String uri = searchRefundStatusByOutRefundNoPath;
|
|
String uri = searchRefundStatusByOutRefundNoPath;
|
|
|
uri = uri.replace("{out_refund_no}", WXPayUtility.urlEncode(request.getOutRefundNo()));
|
|
uri = uri.replace("{out_refund_no}", WXPayUtility.urlEncode(request.getOutRefundNo()));
|
|
|
Request.Builder reqBuilder = new Request.Builder().url(wechatPayApiHost + uri);
|
|
Request.Builder reqBuilder = new Request.Builder().url(wechatPayApiHost + uri);
|
|
|
reqBuilder.addHeader("Accept", "application/json");
|
|
reqBuilder.addHeader("Accept", "application/json");
|
|
|
- reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
|
|
|
|
|
- reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchId, certificateSerialNo, privateKey, GETMETHOD, uri, null));
|
|
|
|
|
|
|
+ reqBuilder.addHeader("Wechatpay-Serial", config.getWechatPayPublicKeyId());
|
|
|
|
|
+ reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(config.getWechatMchId(), config.getMerchantSerialNumber(), privateKey, GETMETHOD, uri, null));
|
|
|
reqBuilder.method(GETMETHOD, null);
|
|
reqBuilder.method(GETMETHOD, null);
|
|
|
Request httpRequest = reqBuilder.build();
|
|
Request httpRequest = reqBuilder.build();
|
|
|
|
|
|
|
|
- // 发送HTTP请求
|
|
|
|
|
OkHttpClient client = new OkHttpClient.Builder().build();
|
|
OkHttpClient client = new OkHttpClient.Builder().build();
|
|
|
try (Response httpResponse = client.newCall(httpRequest).execute()) {
|
|
try (Response httpResponse = client.newCall(httpRequest).execute()) {
|
|
|
String respBody = WXPayUtility.extractBody(httpResponse);
|
|
String respBody = WXPayUtility.extractBody(httpResponse);
|
|
|
if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
|
|
if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
|
|
|
- // 2XX 成功,验证应答签名
|
|
|
|
|
- WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
|
|
|
|
|
|
|
+ WXPayUtility.validateResponse(config.getWechatPayPublicKeyId(), wechatPayPublicKey,
|
|
|
httpResponse.headers(), respBody);
|
|
httpResponse.headers(), respBody);
|
|
|
-
|
|
|
|
|
- // 从HTTP应答报文构建返回数据
|
|
|
|
|
return WXPayUtility.fromJson(respBody, Refund.class);
|
|
return WXPayUtility.fromJson(respBody, Refund.class);
|
|
|
} else {
|
|
} else {
|
|
|
throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
|
|
throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
|
|
@@ -492,29 +489,28 @@ public class WeChatPaymentMininProgramStrategyImpl implements PaymentStrategy {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public DirectAPIv3JsapiPrepayResponse doCreatePrePayOrder(DirectAPIv3JsapiPrepayRequest request) {
|
|
|
|
|
|
|
+ public DirectAPIv3JsapiPrepayResponse doCreatePrePayOrder(DirectAPIv3JsapiPrepayRequest request,
|
|
|
|
|
+ StorePaymentConfig config,
|
|
|
|
|
+ PrivateKey privateKey) {
|
|
|
String uri = prePayPath;
|
|
String uri = prePayPath;
|
|
|
String reqBody = WXPayUtility.toJson(request);
|
|
String reqBody = WXPayUtility.toJson(request);
|
|
|
|
|
|
|
|
Request.Builder reqBuilder = new Request.Builder().url(wechatPayApiHost + uri);
|
|
Request.Builder reqBuilder = new Request.Builder().url(wechatPayApiHost + uri);
|
|
|
reqBuilder.addHeader("Accept", "application/json");
|
|
reqBuilder.addHeader("Accept", "application/json");
|
|
|
- reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
|
|
|
|
|
- reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchId, certificateSerialNo,privateKey, POSTMETHOD, uri, reqBody));
|
|
|
|
|
|
|
+ reqBuilder.addHeader("Wechatpay-Serial", config.getWechatPayPublicKeyId());
|
|
|
|
|
+ reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(config.getWechatMchId(), config.getMerchantSerialNumber(), privateKey, POSTMETHOD, uri, reqBody));
|
|
|
reqBuilder.addHeader("Content-Type", "application/json");
|
|
reqBuilder.addHeader("Content-Type", "application/json");
|
|
|
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
|
|
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
|
|
|
reqBuilder.method(POSTMETHOD, requestBody);
|
|
reqBuilder.method(POSTMETHOD, requestBody);
|
|
|
Request httpRequest = reqBuilder.build();
|
|
Request httpRequest = reqBuilder.build();
|
|
|
|
|
|
|
|
- // 发送HTTP请求
|
|
|
|
|
OkHttpClient client = new OkHttpClient.Builder().build();
|
|
OkHttpClient client = new OkHttpClient.Builder().build();
|
|
|
try (Response httpResponse = client.newCall(httpRequest).execute()) {
|
|
try (Response httpResponse = client.newCall(httpRequest).execute()) {
|
|
|
String respBody = WXPayUtility.extractBody(httpResponse);
|
|
String respBody = WXPayUtility.extractBody(httpResponse);
|
|
|
if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
|
|
if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
|
|
|
- // 2XX 成功,验证应答签名
|
|
|
|
|
- WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
|
|
|
|
|
|
|
+ PublicKey wechatPayPublicKey = loadPublicKeyFromConfig(config);
|
|
|
|
|
+ WXPayUtility.validateResponse(config.getWechatPayPublicKeyId(), wechatPayPublicKey,
|
|
|
httpResponse.headers(), respBody);
|
|
httpResponse.headers(), respBody);
|
|
|
-
|
|
|
|
|
- // 从HTTP应答报文构建返回数据
|
|
|
|
|
return WXPayUtility.fromJson(respBody, DirectAPIv3JsapiPrepayResponse.class);
|
|
return WXPayUtility.fromJson(respBody, DirectAPIv3JsapiPrepayResponse.class);
|
|
|
} else {
|
|
} else {
|
|
|
throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
|
|
throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
|