package com.heyu.api.jsapi; // 引用微信支付工具库,参考:https://pay.weixin.qq.com/doc/v3/merchant/4014931831 import com.heyu.api.jsapi.dto.CommonAmountInfo; import com.heyu.api.jsapi.dto.DirectAPIv3JsapiPrepayRequest; import com.heyu.api.jsapi.dto.DirectAPIv3JsapiPrepayResponse; import com.heyu.api.jsapi.dto.JsapiReqPayerInfo; import com.heyu.api.jsapi.utils.PemUtil; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import okhttp3.*; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.Base64Utils; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; /** * JSAPI下单 */ @Component @Slf4j public class JsapiPrepay { private static String HOST = "https://api.mch.weixin.qq.com"; private static String METHOD = "POST"; private static String PATH = "/v3/pay/transactions/jsapi"; @Value("${eb.config.weixin.pay.appid}") private String appid; @Value("${eb.config.weixin.pay.mchid}") private String mchid; private String certificateSerialNo; private PrivateKey privateKey; private String wechatPayPublicKeyId; private PublicKey wechatPayPublicKey; @Value("${eb.config.weixin.pay.notifyUrl}") private String notifyUrl; public JsapiPrepay() { } public JsapiPrepay(String mchid, String certificateSerialNo, String privateKeyFilePath, String wechatPayPublicKeyId, String wechatPayPublicKeyFilePath) { this.mchid = mchid; this.certificateSerialNo = certificateSerialNo; this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyFilePath); this.wechatPayPublicKeyId = wechatPayPublicKeyId; this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath); } @SneakyThrows public static String getSign(String signatureStr, String privateKey) { String replace = privateKey.replace("\\n", "\n"); PrivateKey merchantPrivateKey = PemUtil.loadPrivateKeyFromPath(replace); Signature sign = Signature.getInstance("SHA256withRSA"); sign.initSign(merchantPrivateKey); sign.update(signatureStr.getBytes(StandardCharsets.UTF_8)); return Base64Utils.encodeToString(sign.sign()); } public DirectAPIv3JsapiPrepayResponse prePay(Long tradeOrderId, Long amount, String openid ) { DirectAPIv3JsapiPrepayRequest request = new DirectAPIv3JsapiPrepayRequest(); request.setAppid(appid); request.setMchid(mchid); request.setNotifyUrl(notifyUrl); request.setDescription(System.currentTimeMillis() + ""); request.setOutTradeNo(tradeOrderId + ""); request.setTimeExpire(WXPayUtility.generateExpireTime()); // 2025-11-05T21:02:16+08:00 request.setAttach("自定义数据说明"); request.setGoodsTag("WXG"); request.setSupportFapiao(false); CommonAmountInfo commonAmountInfo = new CommonAmountInfo(); commonAmountInfo.setTotal(amount); commonAmountInfo.setCurrency("CNY"); request.setAmount(commonAmountInfo); JsapiReqPayerInfo payer = new JsapiReqPayerInfo(); payer.setOpenid(openid); request.setPayer(payer); return doPay(request); } public DirectAPIv3JsapiPrepayResponse doPay(DirectAPIv3JsapiPrepayRequest request) { String uri = PATH; String reqBody = WXPayUtility.toJson(request); Request.Builder reqBuilder = new Request.Builder().url(HOST + uri); reqBuilder.addHeader("Accept", "application/json"); reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId); reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo, privateKey, METHOD, uri, reqBody)); reqBuilder.addHeader("Content-Type", "application/json"); RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody); reqBuilder.method(METHOD, requestBody); Request httpRequest = reqBuilder.build(); // 发送HTTP请求 OkHttpClient client = new OkHttpClient.Builder().build(); try (Response httpResponse = client.newCall(httpRequest).execute()) { String respBody = WXPayUtility.extractBody(httpResponse); log.info("JsapiPrepay respBody:{}", respBody); if (respBody != null) { } if (httpResponse.code() >= 200 && httpResponse.code() < 300) { // 2XX 成功,验证应答签名 WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey, httpResponse.headers(), respBody); // 从HTTP应答报文构建返回数据 return WXPayUtility.fromJson(respBody, DirectAPIv3JsapiPrepayResponse.class); } else { throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers()); } } catch (IOException e) { throw new UncheckedIOException("Sending request to " + uri + " failed.", e); } } }