WeChatPaymentApiV3.java
package com.ruoyi.business.payment;
import com.ruoyi.business.payment.dto.PayParam;
import com.ruoyi.business.payment.dto.RefundParam;
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.app.AppService;
import com.wechat.pay.java.service.payments.h5.H5Service;
import com.wechat.pay.java.service.payments.app.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.model.TransactionAmount;
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.Refund;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.security.*;
import java.util.*;
/**
* 微信支付服务类
*/
@Service
@Slf4j
public class WeChatPaymentApiV3 {
private static final String mchId = "16794"; // 商户号
private static final String mchSerialNo = "5D7DB9B"; // 商户证书序列号
//商户私钥 //https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_1.shtml
private static final String privateKey = "-----BEGIN PRIVATE KEY-----\n" +
"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDH0aut4gO2wjR1\n" +
"PbGM84zoZ0b7usuf
"PPYa5PypeYgcG2cuIWzJejQ=\n" +
"-----END PRIVATE KEY-----";
// 你的微信支付平台证书
private static final String apiV3Key = "f2f4667bfeab47";
private static final String appid="wxea8a";
/** 商户API私钥路径 */
//public static String privateKeyPath = "C:\\Users\\Administrator\\Desktop\\coupon\\wxpay\\apiclient_key.pem";
/**
* 获得H5客户端
* @return
*/
public AppService getAppService () {
Config config =
new RSAAutoCertificateConfig.Builder()
.merchantId(mchId)
.privateKey(privateKey)
//.privateKeyFromPath(privateKeyPath)
.merchantSerialNumber(mchSerialNo)
.apiV3Key(apiV3Key)
.build();
AppService build = new AppService.Builder().config(config).build();
return build;
}
public String pay(PayParam payParam) {
String apiUrl = "http://1.94.66:80/admin";
//apiUrl="http://7j6vva.natappfree.cc";
BigDecimal bigDecimal = new BigDecimal(100);
int payPrice = bigDecimal.multiply(new BigDecimal(payParam.getTotalAmount())).intValue();
AppService appService = getAppService();
//获取客户端
PrepayRequest request = new PrepayRequest();
Amount amount = new Amount();
amount.setTotal(payPrice);
//场景信息
SceneInfo sceneInfo = new SceneInfo();
sceneInfo.setPayerClientIp("1.196");
request.setAmount(amount);
request.setAppid(appid);
request.setMchid(mchId);
request.setDescription(payParam.getSubject());
//request.setNotifyUrl("http://egevdr.natappfree.cc" + "/payment/weChatPay/payment/notify");
request.setNotifyUrl(apiUrl + "/payment/weChatPay/payment/notify");
request.setOutTradeNo(payParam.getOutTradeNo());
request.setAttach(payParam.getPassbackParams());//携带其他参数
request.setSceneInfo(sceneInfo);
// 调用下单方法,得到应答
PrepayResponse prepay = appService.prepay(request);
return prepay.getPrepayId();
}
/**
* 申请退款
* @param refundParam
*/
public void refundOrder(RefundParam refundParam) {
String apiUrl = "";
BigDecimal bigDecimal = new BigDecimal(100);
int total = bigDecimal.multiply(new BigDecimal(refundParam.getTotalAmount())).intValue();
int refund = bigDecimal.multiply(new BigDecimal(refundParam.getTotalAmount())).intValue();
Config config =
new RSAAutoCertificateConfig.Builder()
.merchantId(mchId)
.privateKey(privateKey)
.merchantSerialNumber(mchSerialNo)
.apiV3Key(apiV3Key)
.build();
RefundService service = new RefundService.Builder().config(config).build();
//生成随机退款订单号
String s = UUID.randomUUID().toString().replace("-","");
CreateRequest request = new CreateRequest();
//金额信息
AmountReq amount = new AmountReq();
amount.setCurrency("CNY");
amount.setTotal(Long.valueOf(total));
amount.setRefund(Long.valueOf(refund));
request.setAmount(amount);
request.setOutTradeNo(refundParam.getOutTradeNo());
request.setOutRefundNo(s);
//request.setNotifyUrl("http://egevdr.natappfree.cc" + "/payment/weChatPay/refund/notify");
request.setNotifyUrl(apiUrl +"api" + "/payment/weChatPay/refund/notify");
//发送请求
Refund refund1 = service.create(request);
}
/**
* 根据订单号查询订单信息,目前只返回了订单状态 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_2.shtml
* @param orderNum
* @return
*/
public Map queryOrderByOutTradeNo(String orderNum) {
AppService appService = getAppService();
QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
request.setMchid(mchId);
request.setOutTradeNo(orderNum);
Transaction transaction = appService.queryOrderByOutTradeNo(request);
Map map=new HashMap();
map.put("tradeState",transaction.getTradeState());
/**
* 交易状态,枚举值:
* SUCCESS:支付成功
* REFUND:转入退款
* NOTPAY:未支付
* CLOSED:已关闭
* REVOKED:已撤销(仅付款码支付会返回)
* USERPAYING:用户支付中(仅付款码支付会返回)
* PAYERROR:支付失败(仅付款码支付会返回)
*/
return map;
}
/**
* 支付成功验签
* @param request
* @return
*/
public Map paySuccessCheck(HttpServletRequest request) {
Config config =
new RSAAutoCertificateConfig.Builder()
.merchantId(mchId)
.privateKey(privateKey)
.merchantSerialNumber(mchSerialNo)
.apiV3Key(apiV3Key)
.build();
// 从请求头中获取信息
String timestamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
String signature = request.getHeader("Wechatpay-Signature");
String singType = request.getHeader("Wechatpay-Signature-Type");
String wechatPayCertificateSerialNumber = request.getHeader("Wechatpay-Serial");
String requestBody = getRequestBody(request);
// 构造 RequestParam
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(wechatPayCertificateSerialNumber)
.nonce(nonce)
.signature(signature)
.signType(singType)
.timestamp(timestamp)
.body(requestBody)
.build();
// 初始化解析器 NotificationParser
NotificationParser parser = new NotificationParser((NotificationConfig) config);
// 这个Transaction是微信包里面的
Transaction decryptObject = parser.parse(requestParam, Transaction.class);
TransactionAmount amount = decryptObject.getAmount();
String outTradeNo = decryptObject.getOutTradeNo();
String attach = decryptObject.getAttach();
Map map=new HashMap();
map.put("amount",amount.getTotal());
map.put("outTradeNo",outTradeNo);
map.put("attach",attach);
return map;
}
public Map refundSuccessCheck(HttpServletRequest request) {
Config config =
new RSAAutoCertificateConfig.Builder()
.merchantId(mchId)
.privateKey(privateKey)
.merchantSerialNumber(mchSerialNo)
.apiV3Key(apiV3Key)
.build();
// 从请求头中获取信息
String timestamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
String signature = request.getHeader("Wechatpay-Signature");
String singType = request.getHeader("Wechatpay-Signature-Type");
String wechatPayCertificateSerialNumber = request.getHeader("Wechatpay-Serial");
String requestBody = getRequestBody(request);
// 构造 RequestParam
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(wechatPayCertificateSerialNumber)
.nonce(nonce)
.signature(signature)
.signType(singType)
.timestamp(timestamp)
.body(requestBody)
.build();
// 初始化解析器 NotificationParser
NotificationParser parser = new NotificationParser((NotificationConfig) config);
// 这个Transaction是微信包里面的
RefundNotification decryptObject = parser.parse(requestParam, RefundNotification.class);
com.wechat.pay.java.service.refund.model.Amount amount = decryptObject.getAmount();
String outTradeNo = decryptObject.getOutTradeNo();
Map map=new HashMap();
map.put("amount",amount.getTotal());
map.put("outTradeNo",outTradeNo);
return map;
}
// 获取请求头里的数据
private String getRequestBody(HttpServletRequest request) {
StringBuffer sb = new StringBuffer();
try (
ServletInputStream inputStream = request.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
System.out.println("读取数据流异常:"+e);
}
return sb.toString();
}
/**
* 组装支付参数
* @param appid
* @param privateKey
* @param prepayId
* @return
*/
public Map getPayParameter(String prepayId){
Map<String,String> map=new HashMap<>();
map.put("appId",appid);
map.put("partnerId",mchId);
map.put("prepayId",prepayId);
map.put("packageValue","Sign=WXPay");
map.put("nonceStr",UUID.randomUUID().toString().replace("-",""));
map.put("timeStamp",String.valueOf(new Date().getTime()));
String s = map.get("appId") + "\n" + map.get("timeStamp") + "\n" + map.get("nonceStr") + "\n" + map.get("prepayId") + "\n";
try {
PrivateKey privateKey1 = PemUtil.loadPrivateKeyFromString(privateKey);
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(privateKey1);
sign.update(s.getBytes("utf-8"));
String string = Base64.getEncoder().encodeToString(sign.sign());
map.put("sign",string);
return map;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
}
return null;
}
}
调用方法
//微信支付
String pay = weChatPaymentApiV3.pay(payParam);
Map payParameter = weChatPaymentApiV3.getPayParameter(pay);
rmap.put("pay",payParameter);
回调处理
package com.ruoyi.web.controller.paymentnotify;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import com.ruoyi.business.payment.WeChatPayment;
import com.ruoyi.business.payment.event.PaymentEvent;
import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
/**
* 商城支付项微信支付的回调接口
*/
@Slf4j
@RestController
@RequestMapping("/payment/weChatPay")
public class WeChatPayNotify {
@Autowired
private ApplicationEventPublisher publisher;
/**
* 微信支付/充值回调
*/
@PostMapping("/payment/notify")
public String renotify(@RequestBody String xmlData) {
try {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(WeChatPayment.appId);
payConfig.setMchId(WeChatPayment.mchId);
payConfig.setMchKey(WeChatPayment.mchKey);
payConfig.setKeyPath(WeChatPayment.keyPath);
// 可以指定是否使用沙箱环境
payConfig.setUseSandboxEnv(false);
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(xmlData);
String orderId = notifyResult.getOutTradeNo();
String attach = notifyResult.getAttach();
//获取回传参数paymentType
Map<String,String> map=new HashMap<>();
if(StringUtils.isNotBlank(attach)){
String[] split = StringUtils.split(attach, ";");
for (int i=0;i<split.length;i++){
String[] split1 = StringUtils.split(split[i], "=");
map.put(split1[0],split1[1]);
}
}
publisher.publishEvent(new PaymentEvent(this, orderId,0,map));
return WxPayNotifyResponse.success("OK");
} catch (WxPayException e) {
log.error(e.getMessage());
return WxPayNotifyResponse.fail(e.getMessage());
}
}
/**
* 微信退款回调
*/
@PostMapping("/refund/notify")
@Transactional(rollbackFor=Exception.class)
public String parseRefundNotifyResult(@RequestBody String xmlData, HttpServletResponse response) {
try {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(WeChatPayment.appId);
payConfig.setMchId(WeChatPayment.mchId);
payConfig.setMchKey(WeChatPayment.mchKey);
payConfig.setKeyPath(WeChatPayment.keyPath);
// 可以指定是否使用沙箱环境
payConfig.setUseSandboxEnv(false);
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
WxPayRefundNotifyResult result = wxPayService.parseRefundNotifyResult(xmlData);
String orderId = result.getReqInfo().getOutTradeNo();
Integer refundFee1 = result.getReqInfo().getRefundFee();
//退款金额
BigDecimal refundFee = new BigDecimal(refundFee1).divide(new BigDecimal("100"));
publisher.publishEvent(new PaymentEvent(this, orderId,1,null));
return WxPayNotifyResponse.success("OK");
} catch (WxPayException e) {
log.error(e.getMessage());
return WxPayNotifyResponse.fail(e.getMessage());
}
}
}
与项目集成可参考