From f7568202df1d60ee7dbc3d4b9131f3b49bf2c949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=9D=BE?= <8605635+zhang3064194730@user.noreply.gitee.com> Date: Tue, 21 Oct 2025 14:46:03 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=85=E5=80=BC=E5=85=91=E6=8D=A2=E7=A0=81?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/czg/service/WxService.java | 1108 ++++++++--------- 1 file changed, 554 insertions(+), 554 deletions(-) diff --git a/cash-service/pay-service/src/main/java/com/czg/service/WxService.java b/cash-service/pay-service/src/main/java/com/czg/service/WxService.java index 8ee00119..227c341c 100644 --- a/cash-service/pay-service/src/main/java/com/czg/service/WxService.java +++ b/cash-service/pay-service/src/main/java/com/czg/service/WxService.java @@ -1,554 +1,554 @@ -package com.czg.service; - -import cn.hutool.core.util.IdUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.http.HttpRequest; -import cn.hutool.http.HttpUtil; -import com.alibaba.fastjson.JSONObject; -import com.czg.exception.CzgException; -import com.ijpay.core.IJPayHttpResponse; -import com.ijpay.core.enums.RequestMethodEnum; -import com.ijpay.core.enums.SignType; -import com.ijpay.core.kit.AesUtil; -import com.ijpay.core.kit.PayKit; -import com.ijpay.core.kit.RsaKit; -import com.ijpay.core.kit.WxPayKit; -import com.ijpay.wxpay.WxPayApi; -import com.ijpay.wxpay.enums.WxDomainEnum; -import com.ijpay.wxpay.enums.v3.BasePayApiEnum; -import com.ijpay.wxpay.enums.v3.OtherApiEnum; -import com.ijpay.wxpay.enums.v3.ProfitSharingApiEnum; -import com.ijpay.wxpay.enums.v3.TransferApiEnum; -import com.ijpay.wxpay.model.ReceiverModel; -import com.ijpay.wxpay.model.v3.*; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletRequest; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.nio.charset.StandardCharsets; -import java.security.cert.X509Certificate; -import java.util.Base64; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -@Component -@Slf4j -public class WxService { - - @Value("${wx.appId}") - private String appIdInstance; - @Value("${wx.appSecret}") - private String appSecretInstance; - @Value("${wx.pay.certPath}") - private String certPathInstance; - @Value("${wx.pay.certKeyPath}") - private String certKeyPathInstance; - @Value("${wx.pay.pubKey}") - private String pubKeyInstance; - @Value("${wx.pay.platformCertPath}") - private String platformCertPathInstance; - @Value("${wx.pay.platformCertNo}") - private String platformCertNoInstance; - @Value("${wx.pay.mchId}") - private String mchIdInstance; - @Value("${wx.pay.apiV3Key}") - private String apiV3KeyInstance; - @Value("${wx.pay.apiV2Key}") - private String apiV2KeyInstance; - @Value("${wx.pay.notifyUrl}") - private String notifyUrlInstance; - @Value("${wx.pay.refundNotifyUrl}") - private String refundNotifyUrlInstance; - @Value("${wx.pay.transferNotifyUrl}") - private String transferNotifyUrlInstance; - - @Resource - private RedisService autoRedisService; - private static RedisService redisService; - - @PostConstruct - public void init() { - appId = appIdInstance; - appSecret = appSecretInstance; - certPath = certPathInstance; - pubKey = pubKeyInstance; - certKeyPath = certKeyPathInstance; - platformCertPath = platformCertPathInstance; - platformCertNo = platformCertNoInstance; - mchId = mchIdInstance; - apiV3Key = apiV3KeyInstance; - apiV2Key = apiV2KeyInstance; - notifyUrl = notifyUrlInstance; - refundNotifyUrl = refundNotifyUrlInstance; - transferNotifyUrl = transferNotifyUrlInstance; - redisService = this.autoRedisService; - } - - // static copies for use in static methods - private static String appId; - private static String appSecret; - private static String certPath; - private static String pubKey; - private static String certKeyPath; - private static String platformCertPath; - private static String platformCertNo; - private static String mchId; - private static String apiV3Key; - private static String apiV2Key; - private static String notifyUrl; - private static String refundNotifyUrl; - private static String transferNotifyUrl; - - private static String getAccessToken(boolean refresh) { - Object token = redisService.get("access_token"); - if (!refresh && token instanceof String) { - return (String) token; - } - - String response = HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token", - Map.of("grant_type", "client_credential", "appid", appId, "secret", appSecret) - ); - - log.info("获取access_token响应: {}", response); - JSONObject jsonObject = JSONObject.parseObject(response); - String accessToken = jsonObject.getString("access_token"); - if (accessToken == null) { - throw new RuntimeException("获取access_token失败"); - } - Long expiresIn = jsonObject.getLong("expires_in"); - if (expiresIn == null) { - expiresIn = 7200L; - } - redisService.set("access_token", accessToken, expiresIn - 200); - return accessToken; - } - - public static String getPhone(String code) { - String requestBody = JSONObject.toJSONString(Map.of("code", code)); - String response = HttpUtil.post("https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + getAccessToken(false), requestBody); - log.info("获取手机号响应: {}", response); - JSONObject jsonObject = JSONObject.parseObject(response); - Integer errCode = jsonObject.getInteger("errcode"); - - if (Integer.valueOf(0).equals(errCode)) { - return jsonObject.getJSONObject("phone_info").getString("phoneNumber"); - } else if (Integer.valueOf(40001).equals(errCode)) { - getAccessToken(true); - } - throw new RuntimeException("获取手机号失败"); - } - - public static String getOpenId(String code) { - String response = HttpUtil.get("https://api.weixin.qq.com/sns/jscode2session", - Map.of("appid", appId, "secret", appSecret, "js_code", code, "grant_type", "authorization_code") - ); - log.info("获取openId响应: {}", response); - JSONObject jsonObject = JSONObject.parseObject(response); - String openId = jsonObject.getString("openid"); - if (openId != null && !openId.isBlank()) { - return openId; - } - throw new RuntimeException("获取openId失败"); - } - - /** - * 使用微信支付平台证书公钥加密敏感信息(OAEP) - */ - private static String encryptByPlatformCert(String content) { - try (var certStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(platformCertPath)) { - if (certStream == null) { - throw new RuntimeException("未找到微信支付平台证书文件: " + platformCertPath); - } - X509Certificate certificate = PayKit.getCertificate(certStream); - if (certificate == null) throw new RuntimeException("读取证书失败"); - return PayKit.rsaEncryptOAEP(content, certificate); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private static String getSerialNumber() { - try (var certStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(certPath)) { - X509Certificate certificate = PayKit.getCertificate(certStream); - if (certificate != null) { - String serialNo = certificate.getSerialNumber().toString(16).toUpperCase(Locale.getDefault()); - boolean isValid = PayKit.checkCertificateIsValid(certificate, mchId, -2); - log.info("证书是否可用 {} 证书有效期为 {}", isValid, certificate.getNotAfter()); - log.info("证书序列号: {}", serialNo); - return serialNo; - } - } catch (Exception e) { - log.error("读取证书失败", e); - } - return null; - } - - private static String getPlatSerialNumber() { - try (var certStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(platformCertNo)) { - X509Certificate certificate = PayKit.getCertificate(certStream); - if (certificate != null) { - String serialNo = certificate.getSerialNumber().toString(16).toUpperCase(Locale.getDefault()); - boolean isValid = PayKit.checkCertificateIsValid(certificate, mchId, -2); - log.info("平台证书是否可用 {} 证书有效期为 {}", isValid, certificate.getNotAfter()); - log.info("平台证书序列号: {}", serialNo); - return serialNo; - } - } catch (Exception e) { - log.error("读取平台证书失败", e); - } - return null; - } - - public static JSONObject verifySignature(HttpServletRequest request) { - try { - log.info("开始校验签名并解密"); - String timestamp = request.getHeader("Wechatpay-Timestamp"); - String nonce = request.getHeader("Wechatpay-Nonce"); - String serialNo = request.getHeader("Wechatpay-Serial"); - String signature = request.getHeader("Wechatpay-Signature"); - String result = request.getReader().lines().reduce((a, b) -> a + b).orElse(""); - String resp = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp, apiV3Key, platformCertPath); - log.info("解密明文{}", resp); - return JSONObject.parseObject(resp); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public static void getPlatCert() throws Exception { - String response = WxPayApi.v3( - RequestMethodEnum.GET, - WxDomainEnum.CHINA.toString(), - OtherApiEnum.GET_CERTIFICATES.toString(), - mchId, - getSerialNumber(), - null, - certKeyPath, - "" - ).getBody(); - - JSONObject jsonObject = JSONObject.parseObject(response); - var dataArray = jsonObject.getJSONArray("data"); - var encryptObject = dataArray.getJSONObject(0); - var encryptCertificate = encryptObject.getJSONObject("encrypt_certificate"); - String associatedData = encryptCertificate.getString("associated_data"); - String cipherText = encryptCertificate.getString("ciphertext"); - String nonce = encryptCertificate.getString("nonce"); - AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8)); - String publicKey = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), cipherText); - log.info(publicKey); - } - - public static Map pay(String openId, BigDecimal amount, String desc, String tradeNo, String type) throws Exception { - if (desc == null) desc = "订单支付"; - UnifiedOrderModel model = new UnifiedOrderModel(); - model.setAppid(appId); - model.setMchid(mchId); - model.setDescription(desc); - model.setOut_trade_no(tradeNo); - int total = amount.setScale(2, RoundingMode.UP).multiply(new BigDecimal(100)).intValueExact(); - model.setAmount(new Amount(total, "CNY")); - model.setNotify_url(notifyUrl + "/" + type); - model.setPayer(new Payer(openId, null, null)); - - String payInfo = JSONObject.toJSONString(model); - log.info("统一下单参数: {}", payInfo); - - var resp = WxPayApi.v3( - RequestMethodEnum.POST, - WxDomainEnum.CHINA.toString(), - BasePayApiEnum.JS_API_PAY.toString(), - mchId, - getSerialNumber(), - getSerialNumber(), - certKeyPath, - payInfo - ); - - log.info("统一下单响应: {}", resp); - String body = resp.getBody(); - JSONObject jsonObject = JSONObject.parseObject(body); - String prepayId = jsonObject.getString("prepay_id"); - if (StrUtil.isBlank(prepayId)) { - throw new RuntimeException(jsonObject.getString("message")); - } - try { - return WxPayKit.jsApiCreateSign(appId, prepayId, certKeyPath); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public static Map v2Pay(String openId, BigDecimal amount, String desc, String tradeNo) { - Map payModel = com.ijpay.wxpay.model.UnifiedOrderModel.builder() - .appid(appId) - .mch_id(mchId) - .body(desc) - .out_trade_no(tradeNo) - .total_fee(amount.setScale(2, RoundingMode.UP).multiply(new BigDecimal(100)).intValueExact() + "") - .fee_type("CNY") - .notify_url(notifyUrl) - .openid(openId) - .trade_type("JSAPI") - .build() - .createSign(apiV2Key, SignType.MD5); - - log.info("统一下单参数: {}", payModel); - String resp = WxPayApi.pushOrder(false, payModel); - log.info("统一下单响应: {}", resp); - - Map resultMap = WxPayKit.xmlToMap(resp); - String returnCode = resultMap.get("return_code"); - String returnMsg = resultMap.get("return_msg"); - if (!WxPayKit.codeIsOk(returnCode)) { - throw new CzgException(returnMsg); - } - String resultCode = resultMap.get("result_code"); - if (!WxPayKit.codeIsOk(resultCode)) { - throw new CzgException(returnMsg); - } - String prepayId = resultMap.get("prepay_id"); - return WxPayKit.prepayIdCreateSign(prepayId, appId, appSecret, SignType.MD5); - } - - public static String refund(String tradeNo, String refundTradeNo, BigDecimal amount) { - int finalAmount = amount.multiply(new BigDecimal(100)).intValueExact(); - RefundModel model = new RefundModel(); - model.setOut_trade_no(tradeNo); - model.setOut_refund_no(refundTradeNo); - model.setAmount(new RefundAmount(finalAmount, "CNY", finalAmount)); - model.setNotify_url(refundNotifyUrl); - - String info = JSONObject.toJSONString(model); - log.info("统一退款参数: {}", info); - IJPayHttpResponse response; - try { - response = WxPayApi.v3( - RequestMethodEnum.POST, - WxDomainEnum.CHINA.toString(), - BasePayApiEnum.REFUND.toString(), - mchId, - getSerialNumber(), - getSerialNumber(), - certKeyPath, - info - ); - } catch (Exception e) { - throw new RuntimeException(e); - } - log.info("统一退款响应 {}", response); - String body = response.getBody(); - JSONObject jsonObject = JSONObject.parseObject(body); - if ("ABNORMAL".equals(jsonObject.getString("status")) || response.getStatus() != 200) { - throw new CzgException("退款异常," + jsonObject.getString("message")); - } - return jsonObject.getString("refund_id"); - } - - public static String genCode(int shopId, int id, String path) { - Map params = Map.of( - "scene", "id=" + id + "&shopId=" + shopId, - "page", path, - "width", 430 - ); - var response = HttpRequest.post("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + getAccessToken(false)) - .body(JSONObject.toJSONString(params)) - .execute(); - - byte[] bodyBytes = response.bodyBytes(); - String str = new String(bodyBytes); - if (str.contains("errmsg")) { - JSONObject json = JSONObject.parseObject(str); - throw new CzgException(json.getString("errmsg")); - } - return "data:image/png;base64," + Base64.getEncoder().encodeToString(bodyBytes); - } - - public static String addProfitSharingUser(AddReceivers params) throws Exception { - getPlatCert(); - params.setAppid(appId); - if (params.getName() != null && !params.getName().isBlank()) { - params.setName(encryptByPlatformCert(params.getName())); - } else { - params.setName(null); - } - log.info("添加分账方参数: {}", JSONObject.toJSONString(params)); - var response = WxPayApi.v3( - RequestMethodEnum.POST, - WxDomainEnum.CHINA.toString(), - ProfitSharingApiEnum.PROFIT_SHARING_RECEIVERS_ADD.toString(), - mchId, - getSerialNumber(), - platformCertNo, - certKeyPath, - JSONObject.toJSONString(params) - ); - log.info("添加分账方响应 {}", response.getBody()); - JSONObject resp = JSONObject.parseObject(response.getBody()); - if (resp.containsKey("code")) { - throw new CzgException("分账添加失败" + resp.getString("message")); - } - return resp.getString("account"); - } - - public static JSONObject getMchConfig(String mchIdParam) throws Exception { - var response = WxPayApi.v3( - RequestMethodEnum.GET, - WxDomainEnum.CHINA.toString(), - String.format(ProfitSharingApiEnum.PROFIT_SHARING_MERCHANT_CONFIGS.toString(), mchIdParam), - mchId, - getSerialNumber(), - getSerialNumber(), - certKeyPath, - "" - ); - log.info("查询分账响应 {}", response.getBody()); - JSONObject resp = JSONObject.parseObject(response.getBody()); - if (resp.containsKey("code")) { - throw new CzgException("分账查询失败" + resp.getString("message")); - } - return resp; - } - - /** - * 获取可分账金额 - */ - public static BigDecimal getRemainAmount(String transactionId) { - IJPayHttpResponse response; - try { - response = WxPayApi.v3( - RequestMethodEnum.GET, - WxDomainEnum.CHINA.toString(), - String.format(ProfitSharingApiEnum.PROFIT_SHARING_UNFREEZE_QUERY.toString(), transactionId), - mchId, - getSerialNumber(), - getSerialNumber(), - certKeyPath, - "" - ); - } catch (Exception e) { - throw new RuntimeException(e); - } - log.info("查询可分账响应 {}", response.getBody()); - JSONObject resp = JSONObject.parseObject(response.getBody()); - if (resp.containsKey("code")) { - throw new CzgException("分账查询失败" + resp.getString("message")); - } - return resp.getBigDecimal("unsplit_amount"); - } - - public static void profitSharing(String transactionId, ReceiverModel... receivers) { - for (ReceiverModel r : receivers) { - if (r.getName() != null && !r.getName().isBlank()) { - r.setName(encryptByPlatformCert(r.getName())); - } - } - String tradeNo = IdUtil.simpleUUID(); - ProfitSharingModel model = new ProfitSharingModel(); - model.setAppid(appId); - model.setTransaction_id(transactionId); - model.setReceivers(List.of(receivers)); - model.setUnfreeze_unsplit(true); - model.setOut_order_no(tradeNo); - - String params = JSONObject.toJSONString(model); - log.info("分账参数: {}", params); - IJPayHttpResponse response; - try { - response = WxPayApi.v3( - RequestMethodEnum.POST, - WxDomainEnum.CHINA.toString(), - ProfitSharingApiEnum.PROFIT_SHARING_ORDERS.toString(), - mchId, - getSerialNumber(), - platformCertNo, - certKeyPath, - params - ); - } catch (Exception e) { - throw new RuntimeException(e); - } - log.info("分账响应 {}", response.getBody()); - JSONObject resp = JSONObject.parseObject(response.getBody()); - if (resp.containsKey("code")) { - throw new CzgException("分账失败" + resp.getString("message") + ", 响应: " + resp.toJSONString()); - } - } - - public static void unFreezeSharing(String transactionId, String orderNo) { - com.ijpay.wxpay.model.ProfitSharingModel paramsModel = new com.ijpay.wxpay.model.ProfitSharingModel(); - paramsModel.setTransaction_id(transactionId); - paramsModel.setOut_order_no(orderNo); - paramsModel.setDescription("分账解冻"); - String params = JSONObject.toJSONString(paramsModel); - log.info("分账解冻参数: {}", params); - IJPayHttpResponse response; - try { - response = WxPayApi.v3( - RequestMethodEnum.POST, - WxDomainEnum.CHINA.toString(), - ProfitSharingApiEnum.PROFIT_SHARING_UNFREEZE.toString(), - mchId, - getSerialNumber(), - platformCertNo, - certKeyPath, - params - ); - } catch (Exception e) { - throw new RuntimeException(e); - } - log.info("分账解冻响应 {}", response.getBody()); - JSONObject resp = JSONObject.parseObject(response.getBody()); - if (resp.containsKey("code")) { - throw new CzgException("分账解冻失败" + resp.getString("message") + ", 响应: " + resp.toJSONString()); - } - } - - public static JSONObject transferBalance(String openId, String name, BigDecimal amount, String remarkTxt, String billNoTxt) { - String remark = remarkTxt == null ? "佣金" : remarkTxt; - String billNo = billNoTxt == null ? IdUtil.simpleUUID() : billNoTxt; - Map params = Map.of( - "appid", appId, - "out_bill_no", billNo, - "transfer_scene_id", "1005", - "openid", openId, - "user_name", encryptByPlatformCert(name), - "transfer_amount", amount.multiply(BigDecimal.valueOf(100)).intValue(), - "transfer_remark", remark, - "notify_url", transferNotifyUrl, - "transfer_scene_report_infos", new Object[]{ - Map.of("info_type", "岗位类型", "info_content", "加盟商"), - Map.of("info_type", "报酬说明", "info_content", "测试") - } - ); - log.info("转账到零钱参数: {}", JSONObject.toJSONString(params)); - IJPayHttpResponse response = null; - try { - response = WxPayApi.v3( - RequestMethodEnum.POST, - WxDomainEnum.CHINA.toString(), - "/v3/fund-app/mch-transfer/transfer-bills", - mchId, - getSerialNumber(), - platformCertNo, - certKeyPath, - JSONObject.toJSONString(params) - ); - } catch (Exception e) { - throw new RuntimeException(e); - } - log.info("转账到零钱响应 {}", response.getBody()); - JSONObject resp = JSONObject.parseObject(response.getBody()); - if (resp.containsKey("code")) { - throw new CzgException("转账到零钱失败" + resp.getString("message") + ", 响应: " + resp.toJSONString()); - } - return resp; - } -} - +//package com.czg.service; +// +//import cn.hutool.core.util.IdUtil; +//import cn.hutool.core.util.StrUtil; +//import cn.hutool.http.HttpRequest; +//import cn.hutool.http.HttpUtil; +//import com.alibaba.fastjson.JSONObject; +//import com.czg.exception.CzgException; +//import com.ijpay.core.IJPayHttpResponse; +//import com.ijpay.core.enums.RequestMethodEnum; +//import com.ijpay.core.enums.SignType; +//import com.ijpay.core.kit.AesUtil; +//import com.ijpay.core.kit.PayKit; +//import com.ijpay.core.kit.RsaKit; +//import com.ijpay.core.kit.WxPayKit; +//import com.ijpay.wxpay.WxPayApi; +//import com.ijpay.wxpay.enums.WxDomainEnum; +//import com.ijpay.wxpay.enums.v3.BasePayApiEnum; +//import com.ijpay.wxpay.enums.v3.OtherApiEnum; +//import com.ijpay.wxpay.enums.v3.ProfitSharingApiEnum; +//import com.ijpay.wxpay.enums.v3.TransferApiEnum; +//import com.ijpay.wxpay.model.ReceiverModel; +//import com.ijpay.wxpay.model.v3.*; +//import jakarta.annotation.PostConstruct; +//import jakarta.annotation.Resource; +//import jakarta.servlet.http.HttpServletRequest; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.beans.factory.annotation.Value; +//import org.springframework.stereotype.Component; +// +//import java.math.BigDecimal; +//import java.math.RoundingMode; +//import java.nio.charset.StandardCharsets; +//import java.security.cert.X509Certificate; +//import java.util.Base64; +//import java.util.List; +//import java.util.Locale; +//import java.util.Map; +// +//@Component +//@Slf4j +//public class WxService { +// +// @Value("${wx.appId}") +// private String appIdInstance; +// @Value("${wx.appSecret}") +// private String appSecretInstance; +// @Value("${wx.pay.certPath}") +// private String certPathInstance; +// @Value("${wx.pay.certKeyPath}") +// private String certKeyPathInstance; +// @Value("${wx.pay.pubKey}") +// private String pubKeyInstance; +// @Value("${wx.pay.platformCertPath}") +// private String platformCertPathInstance; +// @Value("${wx.pay.platformCertNo}") +// private String platformCertNoInstance; +// @Value("${wx.pay.mchId}") +// private String mchIdInstance; +// @Value("${wx.pay.apiV3Key}") +// private String apiV3KeyInstance; +// @Value("${wx.pay.apiV2Key}") +// private String apiV2KeyInstance; +// @Value("${wx.pay.notifyUrl}") +// private String notifyUrlInstance; +// @Value("${wx.pay.refundNotifyUrl}") +// private String refundNotifyUrlInstance; +// @Value("${wx.pay.transferNotifyUrl}") +// private String transferNotifyUrlInstance; +// +// @Resource +// private RedisService autoRedisService; +// private static RedisService redisService; +// +// @PostConstruct +// public void init() { +// appId = appIdInstance; +// appSecret = appSecretInstance; +// certPath = certPathInstance; +// pubKey = pubKeyInstance; +// certKeyPath = certKeyPathInstance; +// platformCertPath = platformCertPathInstance; +// platformCertNo = platformCertNoInstance; +// mchId = mchIdInstance; +// apiV3Key = apiV3KeyInstance; +// apiV2Key = apiV2KeyInstance; +// notifyUrl = notifyUrlInstance; +// refundNotifyUrl = refundNotifyUrlInstance; +// transferNotifyUrl = transferNotifyUrlInstance; +// redisService = this.autoRedisService; +// } +// +// // static copies for use in static methods +// private static String appId; +// private static String appSecret; +// private static String certPath; +// private static String pubKey; +// private static String certKeyPath; +// private static String platformCertPath; +// private static String platformCertNo; +// private static String mchId; +// private static String apiV3Key; +// private static String apiV2Key; +// private static String notifyUrl; +// private static String refundNotifyUrl; +// private static String transferNotifyUrl; +// +// private static String getAccessToken(boolean refresh) { +// Object token = redisService.get("access_token"); +// if (!refresh && token instanceof String) { +// return (String) token; +// } +// +// String response = HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token", +// Map.of("grant_type", "client_credential", "appid", appId, "secret", appSecret) +// ); +// +// log.info("获取access_token响应: {}", response); +// JSONObject jsonObject = JSONObject.parseObject(response); +// String accessToken = jsonObject.getString("access_token"); +// if (accessToken == null) { +// throw new RuntimeException("获取access_token失败"); +// } +// Long expiresIn = jsonObject.getLong("expires_in"); +// if (expiresIn == null) { +// expiresIn = 7200L; +// } +// redisService.set("access_token", accessToken, expiresIn - 200); +// return accessToken; +// } +// +// public static String getPhone(String code) { +// String requestBody = JSONObject.toJSONString(Map.of("code", code)); +// String response = HttpUtil.post("https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + getAccessToken(false), requestBody); +// log.info("获取手机号响应: {}", response); +// JSONObject jsonObject = JSONObject.parseObject(response); +// Integer errCode = jsonObject.getInteger("errcode"); +// +// if (Integer.valueOf(0).equals(errCode)) { +// return jsonObject.getJSONObject("phone_info").getString("phoneNumber"); +// } else if (Integer.valueOf(40001).equals(errCode)) { +// getAccessToken(true); +// } +// throw new RuntimeException("获取手机号失败"); +// } +// +// public static String getOpenId(String code) { +// String response = HttpUtil.get("https://api.weixin.qq.com/sns/jscode2session", +// Map.of("appid", appId, "secret", appSecret, "js_code", code, "grant_type", "authorization_code") +// ); +// log.info("获取openId响应: {}", response); +// JSONObject jsonObject = JSONObject.parseObject(response); +// String openId = jsonObject.getString("openid"); +// if (openId != null && !openId.isBlank()) { +// return openId; +// } +// throw new RuntimeException("获取openId失败"); +// } +// +// /** +// * 使用微信支付平台证书公钥加密敏感信息(OAEP) +// */ +// private static String encryptByPlatformCert(String content) { +// try (var certStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(platformCertPath)) { +// if (certStream == null) { +// throw new RuntimeException("未找到微信支付平台证书文件: " + platformCertPath); +// } +// X509Certificate certificate = PayKit.getCertificate(certStream); +// if (certificate == null) throw new RuntimeException("读取证书失败"); +// return PayKit.rsaEncryptOAEP(content, certificate); +// } catch (Exception e) { +// throw new RuntimeException(e); +// } +// } +// +// private static String getSerialNumber() { +// try (var certStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(certPath)) { +// X509Certificate certificate = PayKit.getCertificate(certStream); +// if (certificate != null) { +// String serialNo = certificate.getSerialNumber().toString(16).toUpperCase(Locale.getDefault()); +// boolean isValid = PayKit.checkCertificateIsValid(certificate, mchId, -2); +// log.info("证书是否可用 {} 证书有效期为 {}", isValid, certificate.getNotAfter()); +// log.info("证书序列号: {}", serialNo); +// return serialNo; +// } +// } catch (Exception e) { +// log.error("读取证书失败", e); +// } +// return null; +// } +// +// private static String getPlatSerialNumber() { +// try (var certStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(platformCertNo)) { +// X509Certificate certificate = PayKit.getCertificate(certStream); +// if (certificate != null) { +// String serialNo = certificate.getSerialNumber().toString(16).toUpperCase(Locale.getDefault()); +// boolean isValid = PayKit.checkCertificateIsValid(certificate, mchId, -2); +// log.info("平台证书是否可用 {} 证书有效期为 {}", isValid, certificate.getNotAfter()); +// log.info("平台证书序列号: {}", serialNo); +// return serialNo; +// } +// } catch (Exception e) { +// log.error("读取平台证书失败", e); +// } +// return null; +// } +// +// public static JSONObject verifySignature(HttpServletRequest request) { +// try { +// log.info("开始校验签名并解密"); +// String timestamp = request.getHeader("Wechatpay-Timestamp"); +// String nonce = request.getHeader("Wechatpay-Nonce"); +// String serialNo = request.getHeader("Wechatpay-Serial"); +// String signature = request.getHeader("Wechatpay-Signature"); +// String result = request.getReader().lines().reduce((a, b) -> a + b).orElse(""); +// String resp = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp, apiV3Key, platformCertPath); +// log.info("解密明文{}", resp); +// return JSONObject.parseObject(resp); +// } catch (Exception e) { +// throw new RuntimeException(e); +// } +// } +// +// public static void getPlatCert() throws Exception { +// String response = WxPayApi.v3( +// RequestMethodEnum.GET, +// WxDomainEnum.CHINA.toString(), +// OtherApiEnum.GET_CERTIFICATES.toString(), +// mchId, +// getSerialNumber(), +// null, +// certKeyPath, +// "" +// ).getBody(); +// +// JSONObject jsonObject = JSONObject.parseObject(response); +// var dataArray = jsonObject.getJSONArray("data"); +// var encryptObject = dataArray.getJSONObject(0); +// var encryptCertificate = encryptObject.getJSONObject("encrypt_certificate"); +// String associatedData = encryptCertificate.getString("associated_data"); +// String cipherText = encryptCertificate.getString("ciphertext"); +// String nonce = encryptCertificate.getString("nonce"); +// AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8)); +// String publicKey = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), cipherText); +// log.info(publicKey); +// } +// +// public static Map pay(String openId, BigDecimal amount, String desc, String tradeNo, String type) throws Exception { +// if (desc == null) desc = "订单支付"; +// UnifiedOrderModel model = new UnifiedOrderModel(); +// model.setAppid(appId); +// model.setMchid(mchId); +// model.setDescription(desc); +// model.setOut_trade_no(tradeNo); +// int total = amount.setScale(2, RoundingMode.UP).multiply(new BigDecimal(100)).intValueExact(); +// model.setAmount(new Amount(total, "CNY")); +// model.setNotify_url(notifyUrl + "/" + type); +// model.setPayer(new Payer(openId, null, null)); +// +// String payInfo = JSONObject.toJSONString(model); +// log.info("统一下单参数: {}", payInfo); +// +// var resp = WxPayApi.v3( +// RequestMethodEnum.POST, +// WxDomainEnum.CHINA.toString(), +// BasePayApiEnum.JS_API_PAY.toString(), +// mchId, +// getSerialNumber(), +// getSerialNumber(), +// certKeyPath, +// payInfo +// ); +// +// log.info("统一下单响应: {}", resp); +// String body = resp.getBody(); +// JSONObject jsonObject = JSONObject.parseObject(body); +// String prepayId = jsonObject.getString("prepay_id"); +// if (StrUtil.isBlank(prepayId)) { +// throw new RuntimeException(jsonObject.getString("message")); +// } +// try { +// return WxPayKit.jsApiCreateSign(appId, prepayId, certKeyPath); +// } catch (Exception e) { +// throw new RuntimeException(e); +// } +// } +// +// public static Map v2Pay(String openId, BigDecimal amount, String desc, String tradeNo) { +// Map payModel = com.ijpay.wxpay.model.UnifiedOrderModel.builder() +// .appid(appId) +// .mch_id(mchId) +// .body(desc) +// .out_trade_no(tradeNo) +// .total_fee(amount.setScale(2, RoundingMode.UP).multiply(new BigDecimal(100)).intValueExact() + "") +// .fee_type("CNY") +// .notify_url(notifyUrl) +// .openid(openId) +// .trade_type("JSAPI") +// .build() +// .createSign(apiV2Key, SignType.MD5); +// +// log.info("统一下单参数: {}", payModel); +// String resp = WxPayApi.pushOrder(false, payModel); +// log.info("统一下单响应: {}", resp); +// +// Map resultMap = WxPayKit.xmlToMap(resp); +// String returnCode = resultMap.get("return_code"); +// String returnMsg = resultMap.get("return_msg"); +// if (!WxPayKit.codeIsOk(returnCode)) { +// throw new CzgException(returnMsg); +// } +// String resultCode = resultMap.get("result_code"); +// if (!WxPayKit.codeIsOk(resultCode)) { +// throw new CzgException(returnMsg); +// } +// String prepayId = resultMap.get("prepay_id"); +// return WxPayKit.prepayIdCreateSign(prepayId, appId, appSecret, SignType.MD5); +// } +// +// public static String refund(String tradeNo, String refundTradeNo, BigDecimal amount) { +// int finalAmount = amount.multiply(new BigDecimal(100)).intValueExact(); +// RefundModel model = new RefundModel(); +// model.setOut_trade_no(tradeNo); +// model.setOut_refund_no(refundTradeNo); +// model.setAmount(new RefundAmount(finalAmount, "CNY", finalAmount)); +// model.setNotify_url(refundNotifyUrl); +// +// String info = JSONObject.toJSONString(model); +// log.info("统一退款参数: {}", info); +// IJPayHttpResponse response; +// try { +// response = WxPayApi.v3( +// RequestMethodEnum.POST, +// WxDomainEnum.CHINA.toString(), +// BasePayApiEnum.REFUND.toString(), +// mchId, +// getSerialNumber(), +// getSerialNumber(), +// certKeyPath, +// info +// ); +// } catch (Exception e) { +// throw new RuntimeException(e); +// } +// log.info("统一退款响应 {}", response); +// String body = response.getBody(); +// JSONObject jsonObject = JSONObject.parseObject(body); +// if ("ABNORMAL".equals(jsonObject.getString("status")) || response.getStatus() != 200) { +// throw new CzgException("退款异常," + jsonObject.getString("message")); +// } +// return jsonObject.getString("refund_id"); +// } +// +// public static String genCode(int shopId, int id, String path) { +// Map params = Map.of( +// "scene", "id=" + id + "&shopId=" + shopId, +// "page", path, +// "width", 430 +// ); +// var response = HttpRequest.post("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + getAccessToken(false)) +// .body(JSONObject.toJSONString(params)) +// .execute(); +// +// byte[] bodyBytes = response.bodyBytes(); +// String str = new String(bodyBytes); +// if (str.contains("errmsg")) { +// JSONObject json = JSONObject.parseObject(str); +// throw new CzgException(json.getString("errmsg")); +// } +// return "data:image/png;base64," + Base64.getEncoder().encodeToString(bodyBytes); +// } +// +// public static String addProfitSharingUser(AddReceivers params) throws Exception { +// getPlatCert(); +// params.setAppid(appId); +// if (params.getName() != null && !params.getName().isBlank()) { +// params.setName(encryptByPlatformCert(params.getName())); +// } else { +// params.setName(null); +// } +// log.info("添加分账方参数: {}", JSONObject.toJSONString(params)); +// var response = WxPayApi.v3( +// RequestMethodEnum.POST, +// WxDomainEnum.CHINA.toString(), +// ProfitSharingApiEnum.PROFIT_SHARING_RECEIVERS_ADD.toString(), +// mchId, +// getSerialNumber(), +// platformCertNo, +// certKeyPath, +// JSONObject.toJSONString(params) +// ); +// log.info("添加分账方响应 {}", response.getBody()); +// JSONObject resp = JSONObject.parseObject(response.getBody()); +// if (resp.containsKey("code")) { +// throw new CzgException("分账添加失败" + resp.getString("message")); +// } +// return resp.getString("account"); +// } +// +// public static JSONObject getMchConfig(String mchIdParam) throws Exception { +// var response = WxPayApi.v3( +// RequestMethodEnum.GET, +// WxDomainEnum.CHINA.toString(), +// String.format(ProfitSharingApiEnum.PROFIT_SHARING_MERCHANT_CONFIGS.toString(), mchIdParam), +// mchId, +// getSerialNumber(), +// getSerialNumber(), +// certKeyPath, +// "" +// ); +// log.info("查询分账响应 {}", response.getBody()); +// JSONObject resp = JSONObject.parseObject(response.getBody()); +// if (resp.containsKey("code")) { +// throw new CzgException("分账查询失败" + resp.getString("message")); +// } +// return resp; +// } +// +// /** +// * 获取可分账金额 +// */ +// public static BigDecimal getRemainAmount(String transactionId) { +// IJPayHttpResponse response; +// try { +// response = WxPayApi.v3( +// RequestMethodEnum.GET, +// WxDomainEnum.CHINA.toString(), +// String.format(ProfitSharingApiEnum.PROFIT_SHARING_UNFREEZE_QUERY.toString(), transactionId), +// mchId, +// getSerialNumber(), +// getSerialNumber(), +// certKeyPath, +// "" +// ); +// } catch (Exception e) { +// throw new RuntimeException(e); +// } +// log.info("查询可分账响应 {}", response.getBody()); +// JSONObject resp = JSONObject.parseObject(response.getBody()); +// if (resp.containsKey("code")) { +// throw new CzgException("分账查询失败" + resp.getString("message")); +// } +// return resp.getBigDecimal("unsplit_amount"); +// } +// +// public static void profitSharing(String transactionId, ReceiverModel... receivers) { +// for (ReceiverModel r : receivers) { +// if (r.getName() != null && !r.getName().isBlank()) { +// r.setName(encryptByPlatformCert(r.getName())); +// } +// } +// String tradeNo = IdUtil.simpleUUID(); +// ProfitSharingModel model = new ProfitSharingModel(); +// model.setAppid(appId); +// model.setTransaction_id(transactionId); +// model.setReceivers(List.of(receivers)); +// model.setUnfreeze_unsplit(true); +// model.setOut_order_no(tradeNo); +// +// String params = JSONObject.toJSONString(model); +// log.info("分账参数: {}", params); +// IJPayHttpResponse response; +// try { +// response = WxPayApi.v3( +// RequestMethodEnum.POST, +// WxDomainEnum.CHINA.toString(), +// ProfitSharingApiEnum.PROFIT_SHARING_ORDERS.toString(), +// mchId, +// getSerialNumber(), +// platformCertNo, +// certKeyPath, +// params +// ); +// } catch (Exception e) { +// throw new RuntimeException(e); +// } +// log.info("分账响应 {}", response.getBody()); +// JSONObject resp = JSONObject.parseObject(response.getBody()); +// if (resp.containsKey("code")) { +// throw new CzgException("分账失败" + resp.getString("message") + ", 响应: " + resp.toJSONString()); +// } +// } +// +// public static void unFreezeSharing(String transactionId, String orderNo) { +// com.ijpay.wxpay.model.ProfitSharingModel paramsModel = new com.ijpay.wxpay.model.ProfitSharingModel(); +// paramsModel.setTransaction_id(transactionId); +// paramsModel.setOut_order_no(orderNo); +// paramsModel.setDescription("分账解冻"); +// String params = JSONObject.toJSONString(paramsModel); +// log.info("分账解冻参数: {}", params); +// IJPayHttpResponse response; +// try { +// response = WxPayApi.v3( +// RequestMethodEnum.POST, +// WxDomainEnum.CHINA.toString(), +// ProfitSharingApiEnum.PROFIT_SHARING_UNFREEZE.toString(), +// mchId, +// getSerialNumber(), +// platformCertNo, +// certKeyPath, +// params +// ); +// } catch (Exception e) { +// throw new RuntimeException(e); +// } +// log.info("分账解冻响应 {}", response.getBody()); +// JSONObject resp = JSONObject.parseObject(response.getBody()); +// if (resp.containsKey("code")) { +// throw new CzgException("分账解冻失败" + resp.getString("message") + ", 响应: " + resp.toJSONString()); +// } +// } +// +// public static JSONObject transferBalance(String openId, String name, BigDecimal amount, String remarkTxt, String billNoTxt) { +// String remark = remarkTxt == null ? "佣金" : remarkTxt; +// String billNo = billNoTxt == null ? IdUtil.simpleUUID() : billNoTxt; +// Map params = Map.of( +// "appid", appId, +// "out_bill_no", billNo, +// "transfer_scene_id", "1005", +// "openid", openId, +// "user_name", encryptByPlatformCert(name), +// "transfer_amount", amount.multiply(BigDecimal.valueOf(100)).intValue(), +// "transfer_remark", remark, +// "notify_url", transferNotifyUrl, +// "transfer_scene_report_infos", new Object[]{ +// Map.of("info_type", "岗位类型", "info_content", "加盟商"), +// Map.of("info_type", "报酬说明", "info_content", "测试") +// } +// ); +// log.info("转账到零钱参数: {}", JSONObject.toJSONString(params)); +// IJPayHttpResponse response = null; +// try { +// response = WxPayApi.v3( +// RequestMethodEnum.POST, +// WxDomainEnum.CHINA.toString(), +// "/v3/fund-app/mch-transfer/transfer-bills", +// mchId, +// getSerialNumber(), +// platformCertNo, +// certKeyPath, +// JSONObject.toJSONString(params) +// ); +// } catch (Exception e) { +// throw new RuntimeException(e); +// } +// log.info("转账到零钱响应 {}", response.getBody()); +// JSONObject resp = JSONObject.parseObject(response.getBody()); +// if (resp.containsKey("code")) { +// throw new CzgException("转账到零钱失败" + resp.getString("message") + ", 响应: " + resp.toJSONString()); +// } +// return resp; +// } +//} +//