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 _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 getPaymentParams(CreateOrderResponse response) { Map 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()); } }