137 lines
5.3 KiB
Java
137 lines
5.3 KiB
Java
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);
|
||
}
|
||
}
|
||
}
|