FrameTour-BE/src/main/java/com/ycwl/basic/pay/adapter/WxMpPayAdapter.java
2025-04-17 10:49:11 +08:00

354 lines
15 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.ycwl.basic.pay.adapter;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.core.util.PemUtil;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.CloseOrderRequest;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;
import com.wechat.pay.java.service.payments.jsapi.model.QueryOrderByOutTradeNoRequest;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.QueryByOutRefundNoRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import com.ycwl.basic.constant.NumberConstant;
import com.ycwl.basic.pay.entity.CancelOrderRequest;
import com.ycwl.basic.pay.entity.CreateOrderRequest;
import com.ycwl.basic.pay.entity.CreateOrderResponse;
import com.ycwl.basic.pay.entity.PayResponse;
import com.ycwl.basic.pay.entity.RefundResponse;
import com.ycwl.basic.pay.entity.RefundOrderRequest;
import com.ycwl.basic.pay.entity.RefundOrderResponse;
import com.ycwl.basic.pay.entity.WxMpPayConfig;
import com.ycwl.basic.pay.exceptions.PayWrongConfigException;
import org.springframework.util.Base64Utils;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_NONCE;
import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_SERIAL;
import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_SIGNATURE;
import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_TIMESTAMP;
import static com.wechat.pay.java.service.refund.model.Status.SUCCESS;
public class WxMpPayAdapter implements IPayAdapter {
private WxMpPayConfig config;
public static final String WECHAT_PAY_SIGNATURE_TYPE = "Wechatpay-Signature-Type";
public WxMpPayAdapter() {
}
public WxMpPayAdapter(WxMpPayConfig config) {
this.config = config;
}
@Override
public void loadConfig(Map<String, String> _config) {
this.config = new WxMpPayConfig();
if (_config != null) {
this.config.setMerchantId(_config.get("merchantId"));
this.config.setAppId(_config.get("appId"));
this.config.setPrivateKey(_config.get("privateKey"));
this.config.setSerialNumber(_config.get("serialNumber"));
this.config.setApiV3Key(_config.get("apiV3Key"));
}
}
private Config clientConfig;
private Config getConfig() {
if (clientConfig == null) {
clientConfig = new RSAAutoCertificateConfig.Builder()
.merchantId(config.getMerchantId())
.privateKey(config.getPrivateKey())
.merchantSerialNumber(config.getSerialNumber())
.apiV3Key(config.getApiV3Key())
.build();
}
return clientConfig;
}
@Override
public CreateOrderResponse createOrder(CreateOrderRequest request) {
CreateOrderResponse resp = new CreateOrderResponse();
if (request.getPrice() <= 0) {
resp.setSkipPay(true);
return resp;
}
Config wxConfig = getConfig();
JsapiService service = new JsapiService.Builder().config(wxConfig).build();
PrepayRequest prepayRequest = new PrepayRequest();
Amount amount = new Amount();
amount.setTotal(request.getPrice());
prepayRequest.setAmount(amount);
prepayRequest.setAppid(config.getAppId());
prepayRequest.setMchid(config.getMerchantId());
prepayRequest.setDescription(request.getDescription());
prepayRequest.setNotifyUrl(request.getNotifyUrl());
prepayRequest.setOutTradeNo(request.getOrderNo());
Payer payer = new Payer();
payer.setOpenid(request.getUserIdentify());
prepayRequest.setPayer(payer);
PrepayResponse response = service.prepay(prepayRequest);
resp.setSuccess(true);
resp.setSkipPay(false);
resp.setOrderNo(response.getPrepayId());
return resp;
}
@Override
public Map<String, Object> getPaymentParams(CreateOrderResponse response) {
Map<String, Object> params = new HashMap<>();
if (response.isSkipPay()) {
return params;
}
Long timeStamp = System.currentTimeMillis() / NumberConstant.THOUSAND;
params.put("appId", config.getAppId());
params.put("timeStamp", timeStamp);
String nonce = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
params.put("nonceStr", nonce);
String signStr = Stream.of(config.getAppId(), String.valueOf(timeStamp), nonce, "prepay_id=" + response.getOrderNo())
.collect(Collectors.joining("\n", "", "\n"));
String sign;
try {
sign = getSign(signStr, config.getPrivateKey());
} catch (InvalidKeyException | SignatureException | NoSuchAlgorithmException e) {
throw new PayWrongConfigException("配置错误");
}
params.put("paySign", sign);
params.put("signType", "RSA-SHA256");
params.put("prepayId", "prepay_id=" + response.getOrderNo());
return params;
}
@Override
public PayResponse handleCallback(HttpServletRequest request) throws IOException {
ServletInputStream inputStream = request.getInputStream();
StringBuffer body = new StringBuffer();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s;
// 读取回调请求体
while ((s = bufferedReader.readLine()) != null) {
body.append(s);
}
PayResponse resp = new PayResponse();
String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
String nonce = request.getHeader(WECHAT_PAY_NONCE);
String signType = request.getHeader(WECHAT_PAY_SIGNATURE_TYPE);
String serialNo = request.getHeader(WECHAT_PAY_SERIAL);
String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
NotificationConfig config = (NotificationConfig) getConfig();
NotificationParser parser = new NotificationParser(config);
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(serialNo)
.nonce(nonce)
.signature(signature)
.timestamp(timestamp)
// 若未设置signType默认值为 WECHATPAY2-SHA256-RSA2048
.signType(signType)
.body(body.toString())
.build();
Transaction parse = parser.parse(requestParam, Transaction.class);
resp.setValid(true);
resp.setOrderNo(parse.getOutTradeNo());
if (parse.getAmount() != null) {
resp.setOrderPrice(parse.getAmount().getTotal());
resp.setPayPrice(parse.getAmount().getPayerTotal());
}
switch (parse.getTradeState()) {
case SUCCESS:
resp.setState(PayResponse.PAY_STATE.SUCCESS);
break;
case NOTPAY:
case CLOSED:
case REVOKED:
resp.setState(PayResponse.PAY_STATE.FAIL);
break;
case REFUND:
resp.setState(PayResponse.PAY_STATE.REFUND);
break;
default:
resp.setState(PayResponse.PAY_STATE.CANCEL);
break;
}
resp.setPayTime(parse.getSuccessTime());
resp.setOriginalResponse(parse);
return resp;
}
@Override
public PayResponse queryOrder(String orderNo) {
Config wxConfig = getConfig();
JsapiService service = new JsapiService.Builder().config(wxConfig).build();
QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
queryRequest.setMchid(config.getMerchantId());
queryRequest.setOutTradeNo(orderNo);
PayResponse resp = new PayResponse();
Transaction result = service.queryOrderByOutTradeNo(queryRequest);
resp.setValid(true);
resp.setOrderNo(result.getOutTradeNo());
if (result.getAmount() != null) {
resp.setOrderPrice(result.getAmount().getTotal());
resp.setPayPrice(result.getAmount().getPayerTotal());
}
switch (result.getTradeState()) {
case SUCCESS:
resp.setState(PayResponse.PAY_STATE.SUCCESS);
break;
case NOTPAY:
case CLOSED:
case REVOKED:
resp.setState(PayResponse.PAY_STATE.FAIL);
break;
case REFUND:
resp.setState(PayResponse.PAY_STATE.REFUND);
break;
default:
resp.setState(PayResponse.PAY_STATE.CANCEL);
break;
}
resp.setPayTime(result.getSuccessTime());
resp.setOriginalResponse(result);
return resp;
}
@Override
public RefundOrderResponse refund(RefundOrderRequest request) {
RefundOrderResponse resp = new RefundOrderResponse();
Config wxConfig = getConfig();
RefundService service = new RefundService.Builder().config(wxConfig).build();
CreateRequest createRequest = new CreateRequest();
createRequest.setOutTradeNo(request.getOrderNo());
createRequest.setOutRefundNo(request.getRefundNo());
AmountReq amountReq = new AmountReq();
amountReq.setTotal(Long.valueOf(request.getPrice()));
amountReq.setRefund(Long.valueOf(request.getRefundPrice()));
amountReq.setCurrency("CNY");
createRequest.setAmount(amountReq);
createRequest.setNotifyUrl(request.getNotifyUrl());
Refund refund = service.create(createRequest);
if (refund.getStatus() == SUCCESS) {
resp.setSuccess(true);
resp.setRefundNo(refund.getOutRefundNo());
} else {
resp.setSuccess(false);
}
return resp;
}
@Override
public RefundResponse handleRefundCallback(HttpServletRequest request) throws IOException {
ServletInputStream inputStream = request.getInputStream();
StringBuffer body = new StringBuffer();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s;
// 读取回调请求体
while ((s = bufferedReader.readLine()) != null) {
body.append(s);
}
RefundResponse resp = new RefundResponse();
String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
String nonce = request.getHeader(WECHAT_PAY_NONCE);
String signType = request.getHeader(WECHAT_PAY_SIGNATURE_TYPE);
String serialNo = request.getHeader(WECHAT_PAY_SERIAL);
String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
NotificationConfig config = (NotificationConfig) getConfig();
NotificationParser parser = new NotificationParser(config);RequestParam requestParam = new RequestParam.Builder()
.serialNumber(serialNo)
.nonce(nonce)
.signature(signature)
.timestamp(timestamp)
// 若未设置signType默认值为 WECHATPAY2-SHA256-RSA2048
.signType(signType)
.body(body.toString())
.build();
RefundNotification parse = parser.parse(requestParam, RefundNotification.class);
resp.setValid(true);
resp.setOriginalResponse(parse);
if (parse.getRefundStatus() == SUCCESS) {
//退款成功
resp.setSuccess();
resp.setRefundTime(parse.getSuccessTime());
resp.setOrderNo(parse.getOutTradeNo());
resp.setRefundNo(parse.getRefundId());
if (parse.getAmount() != null) {
resp.setRefundPrice(Math.toIntExact(parse.getAmount().getPayerRefund()));
resp.setOrderPrice(Math.toIntExact(parse.getAmount().getTotal()));
}
} else {
//退款失败
resp.setFail();
}
return resp;
}
@Override
public RefundResponse checkRefundStatus(String refundNo) {
Config wxConfig = getConfig();
RefundService service = new RefundService.Builder().config(wxConfig).build();
QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
request.setOutRefundNo(refundNo);
RefundResponse resp = new RefundResponse();
Refund result = service.queryByOutRefundNo(request);
resp.setValid(true);
resp.setOriginalResponse(result);
if (result.getStatus() == SUCCESS) {
//退款成功
resp.setSuccess();
resp.setRefundTime(result.getSuccessTime());
resp.setOrderNo(result.getOutTradeNo());
resp.setRefundNo(result.getRefundId());
if (result.getAmount() != null) {
resp.setRefundPrice(Math.toIntExact(result.getAmount().getPayerRefund()));
resp.setOrderPrice(Math.toIntExact(result.getAmount().getTotal()));
}
} else {
//退款失败
resp.setFail();
}
return resp;
}
@Override
public void cancelOrder(CancelOrderRequest request) {
CloseOrderRequest closeOrderRequest = new CloseOrderRequest();
closeOrderRequest.setOutTradeNo(request.getOrderNo());
closeOrderRequest.setMchid(config.getMerchantId());
Config config = getConfig();
JsapiService service = new JsapiService.Builder().config(config).build();
service.closeOrder(closeOrderRequest);
}
public static String getSign(String signatureStr,String privateKey) throws InvalidKeyException, SignatureException, NoSuchAlgorithmException {
String replace = privateKey.replace("\\n", "\n");
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKeyFromString(replace);
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(merchantPrivateKey);
sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
return Base64Utils.encodeToString(sign.sign());
}
}