微信小程序支付 相关工具类和回调
支付和退款公共接口
@GetMapping("/back")
public ResultData back(Integer id,HttpServletRequest request){
Integer userId = userService.getUserId(request);
String uuid = WXPayUtil.getUUID();
//构造退款的请求body 参数为 微信订单号或自己生成的订单号,退款单号(自己生成) ,退款金额,总金额
String backJson = WXPayUtil.buildBackJson(order.getOrderId(), uuid, Integer.valueOf(order.getAmount()), Integer.valueOf(order.getAmount()));
//封装的退款工具类 调用即发起退款
JSONObject back = WXPayUtil.back(backJson);
//自己封装的通用返回
if (ObjectUtils.isNotEmpty(back)) return ResultData.success();
return ResultData.fail();
}
@PostMapping("/pay")
public ResultData pay(@RequestBody WxPay wxPay,HttpServletRequest request) throws Exception {
String uuid = WXPayUtil.getUUID();
String openid = request.getHeader("openid");
Integer userId = userService.getUserId(request);
// 穿插自己的业务和获取需要支付的金额
// 构造付款的body 参数为 商品描述,自己生成的订单号,金额,付款人的openid
JSONObject payJson = WXPayUtil.buildPayJson(wxPay.getDescription(), uuid, Integer.valueOf(payAmount), openid);
String prepayId = WXPayUtil.pay(payJson.toJSONString());
if (StringUtils.isNotBlank(prepayId)){
Long time = WXPayUtil.getTime();
//生成签名的工具类
String sign = WXPayUtil.getSign(time,uuid,"prepay_id=" + prepayId);
//封装的给前端用于唤起小程序支付的参数
AppletsPay appletsPay = new AppletsPay();
appletsPay.setTimestamp(time);
appletsPay.setNonceStr(uuid);
appletsPay.setPrepayId(prepayId);
appletsPay.setPaySign(sign);
if (ObjectUtils.isNotEmpty(appletsPay)) return
ResultData.success(appletsPay);
}
return ResultData.fail();
}
支付回调(支付和退款写在一起)
@ResponseBody
@PostMapping(value = "/callBack")
public WxResponse wxPayCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
Gson gson =new Gson();
String body = WXPayUtil.readData(request);
String serialNumber = request.getHeader("Wechatpay-Serial");
String nonce = request.getHeader("Wechatpay-Nonce");
String timestamp = request.getHeader("Wechatpay-Timestamp");
String signature = request.getHeader("Wechatpay-Signature");
Map<String,String> map =new HashMap<>();
// 构建request,传入必要参数
NotificationRequest Nrequest = new NotificationRequest.Builder().withSerialNumber(serialNumber)
.withNonce(nonce)
.withTimestamp(timestamp)
.withSignature(signature)
.withBody(body)
.build();
//WechatPayUtils.getVerifier()就是获取上面的验签器,这只是我定义的一个方法来获取而已,你要怎样获取都行。构造验签器方法跟上面还是一模一样.
NotificationHandler handler = new NotificationHandler(WXPayUtil.getVerifier(),
WXPayUtil.v3Key.getBytes(StandardCharsets.UTF_8));
//WechatPayConfig.v3Key 也就是APIV3key
//JSON.parseObject,是将Json字符串转化为相应的对象;JSON.toJSONString则是将对象转化为Json字符串.用 Gson.toJson也行
try {
// 验签和解析请求体
Notification notification = handler.parse(Nrequest);
// 从notification中获取解密报文。
String plainText = notification.getDecryptData();
//将密文转为map ,之后处理业务逻辑
Map resultMap =gson.fromJson(plainText,HashMap.class);
//支付成功的处理
if (ObjectUtils.isNotEmpty(resultMap.get("trade_state")) && "SUCCESS".equals(resultMap.get("trade_state").toString())){
//支付成功的业务逻辑
}
//退款成功的处理
if (ObjectUtils.isNotEmpty(resultMap.get("refund_status")) && "SUCCESS".equals(resultMap.get("refund_status").toString())){
//退款成功的业务逻辑
}
//成功应答
WxResponse wxResponse = new WxResponse();
wxResponse.setCode("SUCCESS");
wxResponse.setMessage("成功");
return wxResponse;
} catch (Exception e) {
e.printStackTrace();
//应答失败
WxResponse wxResponse = new WxResponse();
wxResponse.setCode("FAIL");
wxResponse.setMessage("出错了");
return wxResponse;
}
}catch (Exception e){
WxResponse wxResponse = new WxResponse();
wxResponse.setCode("SUCCESS");
wxResponse.setMessage("成功");
return wxResponse;
}
}
@Data
class WxResponse{
private String code;
private String message;
}
微信支付工具类
public class WXPayUtil {
// 商户号 后台获取
private static final String mchId = "";
//appid 后台获取
public static final String appid = "";
// v3Key 后台自己设定
public static final String v3Key = "";
// 商户证书序列号 后台获取
private static final String mchSerialNo = "";
// 支付链接
private static final String pay_url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
// 退款链接
private static final String back_url = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
// 你的商户私钥 去掉begin头和end结尾 还有\n换行符
private static final String privateKey = "";
// 你的微信支付平台证书 可不去掉多余信息
private static final String certificate = "";
private static CloseableHttpClient httpClient;
public static void setup() {
PrivateKey key = PemUtil.loadPrivateKey(privateKey);
X509Certificate wechatPayCertificate = PemUtil.loadCertificate(
new ByteArrayInputStream(certificate.getBytes(StandardCharsets.UTF_8)));
ArrayList<X509Certificate> listCertificates = new ArrayList<>();
listCertificates.add(wechatPayCertificate);
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(mchId,mchSerialNo, key)
.withWechatPay(listCertificates);
httpClient = builder.build();
}
/**
*wxMchid商户号
*wxCertno证书编号
*wxCertPath证书地址
*wxPaternerKey v3秘钥
*pay_url jsapi下单地址
*body buildWxJsApiPayJson返回的json.toString
*/
public static String pay(String body) {
if (httpClient == null) {
setup();
}
HttpPost httpPost = new HttpPost(pay_url);
httpPost.addHeader("Content-Type","application/json;chartset=utf-8");
httpPost.addHeader("Accept", "application/json");
try{
if(StringUtils.isBlank(body)){
throw new IllegalArgumentException("data参数不能为空");
}
StringEntity stringEntity = new StringEntity(body,"utf-8");
httpPost.setEntity(stringEntity);
// 直接执行execute方法,官方会自动处理签名和验签,并进行证书自动更新
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
if(httpResponse.getStatusLine().getStatusCode() == 200){
String jsonResult = EntityUtils.toString(httpEntity);
JSONObject jsonObject = JSONObject.parseObject(jsonResult);
return jsonObject.getString("prepay_id");
}else{
System.err.println("微信支付错误信息"+EntityUtils.toString(httpEntity));
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
/**
* 构造微信JsApi付款的json
* @param description
* @param out_trade_no
* @param amount
* @return
*/
public static JSONObject buildPayJson(String description ,
String out_trade_no ,
Integer amount,
String openId){
//订单金额json
JSONObject amountJson = new JSONObject();
amountJson.put("total",amount);
amountJson.put("currency","CNY");
//支付者json
JSONObject payerJson = new JSONObject();
payerJson.put("openid",openId);
//基础信息json
JSONObject json = new JSONObject();
json.put("appid",appid);
json.put("mchid",mchId);
json.put("description",description);
json.put("out_trade_no",out_trade_no);
json.put("notify_url","https://域名/callBack");
json.put("amount",amountJson);
json.put("payer",payerJson);
return json;
}
public static String buildBackJson(String orderId,String refundId ,Integer total , Integer refund){
Map<String, Object> map = new HashMap<>();
map.put("out_trade_no",orderId);
map.put("out_refund_no",refundId);
map.put("reason","退款理由 可参数参入");
map.put("notify_url","https://域名/callBack");
map.put("funds_account","AVAILABLE");
Map<String, Object> amounts = new HashMap<>();
amounts.put("refund",refund);
amounts.put("total",total);
amounts.put("currency","CNY");
map.put("amount",amounts);
Gson gson = new Gson();
String json = gson.toJson(map);
return json;
}
public static JSONObject back(String body) {
if (httpClient == null) {
setup();
}
HttpPost httpPost = new HttpPost(back_url);
httpPost.addHeader("Content-Type","application/json;chartset=utf-8");
httpPost.addHeader("Accept", "application/json");
try{
if(StringUtils.isBlank(body)){
throw new IllegalArgumentException("data参数不能为空");
}
StringEntity stringEntity = new StringEntity(body,"utf-8");
httpPost.setEntity(stringEntity);
// 直接执行execute方法,官方会自动处理签名和验签,并进行证书自动更新
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
if(httpResponse.getStatusLine().getStatusCode() == 200){
String jsonResult = EntityUtils.toString(httpEntity);
JSONObject jsonObject = JSONObject.parseObject(jsonResult);
return jsonObject;
}else{
System.err.println("微信支付错误信息"+EntityUtils.toString(httpEntity));
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
// 获取签名 =========================================================
// 签名
private static String sign(byte[] message) throws Exception {
Signature sign = Signature.getInstance("SHA256withRSA");
PrivateKey key = PemUtil.loadPrivateKey(privateKey);
sign.initSign(key);
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
}
public static String getUUID(){
return UUID.randomUUID().toString().replaceAll("-","");
}
public static Long getTime(){
return System.currentTimeMillis() / 1000;
}
public static String readData(HttpServletRequest request) {
BufferedReader br = null;
try {
StringBuilder result = new StringBuilder();
br = request.getReader();
for (String line; (line = br.readLine()) != null; ) {
if (result.length() > 0) {
result.append("\n");
}
result.append(line);
}
return result.toString();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static Verifier getVerifier() throws Exception {
// 获取证书管理器实例
CertificatesManager certificatesManager = CertificatesManager.getInstance();
// 向证书管理器增加需要自动更新平台证书的商户信息
certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId,
new PrivateKeySigner(mchSerialNo, PemUtil.loadPrivateKey(privateKey)))
, v3Key.getBytes(StandardCharsets.UTF_8));
// 从证书管理器中获取verifier
Verifier verifier = certificatesManager.getVerifier(mchId);
return verifier;
}
/**
* 作用:使用字段appId、timeStamp、nonceStr、package计算得出的签名值
* 场景:根据微信统一下单接口返回的 prepay_id 生成调启支付所需的签名值
* @param timestamp
* @param nonceStr
* @param pack package
* @return
* @throws Exception
*/
public static String getSign(long timestamp, String nonceStr, String pack){
String message = buildMessage(timestamp, nonceStr, pack);
String paySign= null;
try {
paySign = sign(message.getBytes("utf-8"));
} catch (Exception e) {
e.printStackTrace();
}
return paySign;
}
private static String buildMessage(long timestamp, String nonceStr, String pack) {
return appid + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ pack + "\n";
}
}
tips!!!!!!!!!!!!!!!!
certificate 获取方式按下列步骤
(1)https://github.com/EliasZzz/CertificateDownloader/releases这里下载那个jar包, 2022-7-22版本为1.2.0

(2)执行:“java -jar CertificateDownloader完整包.jar -f 商户私钥文件路径 -k v3密钥 -m 商户号 -o 证书保存路径 -s 商户证书序列号”就行了。
商户私钥文件路径”是账号中心->API安全->API证书中设置并下载的证书(就是其中的apiclient_key.pem,下载还会获得apiclient_cert.pem,我之前把这个当做支付证书了,其实不是,apiclient_cert.pem这用不着)
执行完了是个类似wechatpay_****************.pem的文件。
最后补上相关版本信息
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.15.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.10.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.0</version>
</dependency>
浙公网安备 33010602011771号