最近开发微信支付功能,但是个人看来看微信支付的文档很是头疼,上面提供的demo,复制下来不能用,又咨询客服查询资料才把微信支付搞清楚。
我使用的是JSAPI的 支付方法,这个支付方法是以下流程:
支付流程:
获取预支付订单号和签名(后端)----->前端拿到预支付订单号和签名调器支付---->支付成功后自动回调
退款流程:
调用企业微信申请退款接口----->退款成功后自动回调
在开发之前需要做好基本配置:
(1)引入微信支付依赖:
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.12</version>
</dependency>
(2)配置商户证书和APIv3密钥,这部操作直接看文档即可,已经非常详细:下载并配置商户证书 - 通用规则 | 微信支付商户文档中心
1.支付流程代码:
下面代码一些商户信息,商户私密,appid,openid根据自己的实际情况来替换代码。
import com.wechat.pay.java.core.util.IOUtil;
import com.wechat.pay.java.core.util.PemUtil;
import java.security.PrivateKey;
public class MtWeChatConstant {
//商户号 (自己商户平台的商户号)
public static final String MERCHANT_ID = "1234568951";
//商户API私钥路径
public static final String MERCHANT_PRIVATE_KEY_PATH = "/app/resources/wxPayConfig/apiclient_key.pem";
//商户证书序列号(自己配置的证书序列号)
public static final String MERCHANT_SERIAL_NUMBER = "ABCDEF**M63BD96AA3F7A307AEA";
//商户APIV3密钥(自己配置的APIV3)
public static final String API_V3_KEY = "cewi123***89hUPOmAWgABCABCMGH3";
//支付完成后的回调的地址(如果不要回调信息,可以不配置)
public static final String PAY_NOTIFY_URL = "https://xxxx.xxxx.com/services/mt/api/pay/notifi";
//退款完成后的回调的地址(如果不要回调信息,可以不配置)
public static final String REFUND_NOTIFY_URL = "https://xxx.xxxxx.com/services/mt/api/refund/notifi";
//一下代码用于生成签名,写法参考微信支付文档,链接:https://github.com/wechatpay-apiv3/wechatpay-java/blob/main/core/src/test/java/com/wechat/pay/java/core/model/TestConfig.java
public static final PrivateKey MERCHANT_PRIVATE_KEY;
public static final String MERCHANT_PRIVATE_KEY_STRING;
static {
try {
MERCHANT_PRIVATE_KEY_STRING = IOUtil.loadStringFromPath(MERCHANT_PRIVATE_KEY_PATH);
MERCHANT_PRIVATE_KEY = PemUtil.loadPrivateKeyFromString(MERCHANT_PRIVATE_KEY_STRING);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
这个配置商户证书的微信支付平台提供的证书私钥,是我存放的位置:"/app/resources/wxPayConfig/apiclient_key.pem" 根据自己项目实际的情况来存放
获取预支付订单号以及签名的代码:
import com.alibaba.fastjson.JSON;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.cipher.RSASigner;
import com.wechat.pay.java.core.cipher.Signer;
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.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class PerPayId {
public static void main(String[] args) {
// 使用自动更新平台证书的RSA配置
// 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
Config config = new RSAAutoCertificateConfig.Builder()
.merchantId(MtWeChatConstant.MERCHANT_ID)
.privateKeyFromPath(MtWeChatConstant.MERCHANT_PRIVATE_KEY_PATH)
.merchantSerialNumber(MtWeChatConstant.MERCHANT_SERIAL_NUMBER)
.apiV3Key(MtWeChatConstant.API_V3_KEY)
.build();
//获取预支付订单号
// 构建service
JsapiService service = new JsapiService.Builder().config(config).build();
// request.setXxx(val)设置所需参数,具体参数可见Request定义,参考文档:https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/direct-jsons/jsapi-prepay.html
PrepayRequest request = new PrepayRequest();
Amount amount = new Amount();
//设置金额, 以分为单位 金额根据实际情况自行填写
amount.setTotal(1);
amount.setCurrency("CNY");
request.setAmount(amount);
//绑定微信支 公众号的appid 根据实际情况自行获取填写
request.setAppid("appid");
// 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一,根据自己需求自己定义
request.setMchid(MtWeChatConstant.MERCHANT_ID);
request.setDescription("产品描述");
//支付成功后回调的URL
request.setNotifyUrl(MtWeChatConstant.PAY_NOTIFY_URL);
//ZHE
request.setOutTradeNo(UUID.randomUUID().toString().replace("-",""));
//自定义信息, 在支付订单回调可以取到(自行替换,如不需要可注释掉)
Map meetIfno = new HashMap();
meetIfno.put("key", "value");
request.setAttach(JSON.toJSONString(meetIfno));
Payer payer = new Payer();
//支付人员的的openid
payer.setOpenid("openid");
request.setPayer(payer);
//调用支付接口
PrepayResponse response = service.prepay(request);
//响应体中有预支付订单号
response.getPrepayId();
System.out.println(response.getPrepayId());
//以上为获取预支付订单号步骤
/**
* 下面为使用加密获取密钥,也需要返回给前段
*/
long timeStamp = System.currentTimeMillis() / 1000;
String nonceStr = UUID.randomUUID().toString().replace("-", "");
//构造签名参数(appid更换为实际支付人员的appid)
String message = "appid" + "\n" + timeStamp + "\n" + nonceStr + "\n" + "prepay_id=" + response.getPrepayId() + "\n";
//生成签名
Signer rsaSigner = new RSASigner(MtWeChatConstant.MERCHANT_SERIAL_NUMBER, MtWeChatConstant.MERCHANT_PRIVATE_KEY);
String sign = rsaSigner.sign(message).getSign();
//前段通过该签名可以调用支付
System.out.println(sign);
/**
* 注意:
* 1.预支付订单号(prepay_id),以及生成签名的timeStamp,nonceStr的参数都需要返回前端
* 2.在构造签名参数的时候,\n一定要加 否则也是签名无效
* 3.
*/
}
2.如果支付完成需要回调的话,以下是回调的代码:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.e7ing.smart.constant.MtWeChatConstant;
import com.e7ing.smart.utils.E7ResponseEntity;
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.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.model.TransactionPayer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
@Transactional(rollbackFor = Exception.class)
public class WeChatPayInfoController {
private final Logger log = LoggerFactory.getLogger(WeChatPayInfoController.class);
/**
* 处理订单回调事件
*
* @param info
* @return
*/
@PostMapping("/pay/notifi")
public String savePayOrder(
@RequestHeader(value = "Wechatpay-Signature") String sign,
@RequestHeader(value = "Wechatpay-Serial") String serial,
@RequestHeader(value = "Wechatpay-Nonce") String nonce,
@RequestHeader(value = "Wechatpay-Timestamp") String timestamp,
@RequestHeader(value = "Wechatpay-Signature-Type") String signType,
@RequestBody String info
) {
/**
* 注意:
* 1.请求头必须按照代码中的定义格式(如果接收不到可以试试@RequestHeader(value = "Wechatpay-Signature",required = false) String sign)在里面加一个required = false
* 2.接收body请求体必须是String类型,不能使用json,如果使用json会导致解析不正确
* 3.返回值类型可以参考文档,我这边定义的String,看文档需求是成功返回"SUCCESS",失败为"FAIL"
* 3.其他问题可以参考文档代码:https://github.com/wechatpay-apiv3/wechatpay-java/tree/main
* 4.解析出来的参数问题可以参考这个文档:https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/payment-notice.html
*/
log.debug(
"获取到回调事件,请求头信息 sign:{}, serial:{}, nonce:{}, timestamp:{}, signType:{}, info:{}",
sign,
serial,
nonce,
timestamp,
signType,
info
);
// 构造 RequestParam
com.wechat.pay.java.core.notification.RequestParam requestParam = new com.wechat.pay.java.core.notification.RequestParam.Builder()
.serialNumber(serial)
.nonce(nonce)
.signature(sign)
.timestamp(timestamp)
.body(info)
.build();
// 如果已经初始化了 RSAAutoCertificateConfig,可直接使用
NotificationConfig config = new RSAAutoCertificateConfig.Builder()
.merchantId(MtWeChatConstant.MERCHANT_ID)
.privateKeyFromPath(MtWeChatConstant.MERCHANT_PRIVATE_KEY_PATH)
.merchantSerialNumber(MtWeChatConstant.MERCHANT_SERIAL_NUMBER)
.apiV3Key(MtWeChatConstant.API_V3_KEY)
.build();
// 初始化 NotificationParser
NotificationParser parser = new NotificationParser(config);
// 以支付通知回调为例,验签、解密并转换成 Transaction
Transaction payInfo = null;
try {
//回调所有的支付信息
payInfo = parser.parse(requestParam, Transaction.class);
} catch (Exception e) {
log.error(
"签名验证失败,获取到回调事件,请求头信息 sign:{}, serial:{}, nonce:{}, timestamp:{}, signType:{}, info:{}",
sign,
serial,
nonce,
timestamp,
signType,
info
);
log.error(e.getMessage());
return "FAIL";
}
//获取支付人员的信息
TransactionPayer payerInfo = payInfo.getPayer();
//获取自定义信息
JSONObject meetInfo = JSON.parseObject(payInfo.getAttach());
//具体信息自行获取即可
System.out.println(payInfo.toString());
/**
* 以上代码只是拿到回调的签名,解析出来回调的支付信息,具体业务根据自己的实际情况自行编写
*/
return "SUCCESS";
}
}
3.申请退款代码:
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
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;
public class Refund {
public static void main(String[] args) {
// 使用自动更新平台证书的RSA配置
// 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
Config config = new RSAAutoCertificateConfig.Builder()
.merchantId(MtWeChatConstant.MERCHANT_ID)
.privateKeyFromPath(MtWeChatConstant.MERCHANT_PRIVATE_KEY_PATH)
.merchantSerialNumber(MtWeChatConstant.MERCHANT_SERIAL_NUMBER)
.apiV3Key(MtWeChatConstant.API_V3_KEY)
.build();
//构造申请退款对象
RefundService service = new RefundService.Builder().config(config).build();
//请求参数
CreateRequest request = new CreateRequest();
//设置退款金额 根据自己的实际业务自行填写
AmountReq amountReq = new AmountReq();
amountReq.setRefund(1L);
amountReq.setTotal(1L);
amountReq.setCurrency("CNY");
request.setAmount(amountReq);
//支付成功后回调回来的transactionId 按照实际情况填写
request.setTransactionId("transactionId");
//支付成功后回调回来的transactionId 按照实际情况填写
request.setOutRefundNo("transactionId");
//退款成功的回调地址
request.setNotifyUrl(MtWeChatConstant.REFUND_NOTIFY_URL);
//发起请求,申请退款
com.wechat.pay.java.service.refund.model.Refund refund = service.create(request);
//调用成功的响应数据
System.out.println(refund);
/**
* 注意:
* 1.以上参数都需要按照实际情况填写,大部分参数都是付款成功之后的回调数据
* 2.以上退款是必要参数,如果想要填写其他参数参考文档:https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/create.html
* 3.SDK的微信支付的退款代码参考:https://github.com/wechatpay-apiv3/wechatpay-java/blob/main/service/src/main/java/com/wechat/pay/java/service/refund/RefundService.java
*/
}
4.退款成功的回调代码:
import com.e7ing.smart.constant.MtWeChatConstant;
import com.e7ing.smart.utils.E7ResponseEntity;
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.service.refund.model.RefundNotification;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
@Transactional(rollbackFor = Exception.class)
public class WeChatPayInfoController {
private final Logger log = LoggerFactory.getLogger(WeChatPayInfoController.class);
/**
* 处理退款订单回调事件
*
* @param info
* @return
*/
@PostMapping("/refund/notifi")
public E7ResponseEntity saveReFundOrder(
@RequestHeader(value = "Wechatpay-Signature", required = false) String sign,
@RequestHeader(value = "Wechatpay-Serial", required = false) String serial,
@RequestHeader(value = "Wechatpay-Nonce", required = false) String nonce,
@RequestHeader(value = "Wechatpay-Timestamp", required = false) String timestamp,
@RequestHeader(value = "Wechatpay-Signature-Type", required = false) String signType,
@RequestBody String info
) {
/**
* 注意:
* 1.请求头必须按照代码中的定义格式
* 2.接收body请求体必须是String类型,不能使用json,如果使用json会导致解析不正确
* 3.返回值类型可以参考文档,我这边定义的String,看文档需求是成功返回"SUCCESS",失败为"FAIL"
* 3.其他问题可以参考文档代码:https://github.com/wechatpay-apiv3/wechatpay-java/tree/main
* 4.解析出来的参数问题可以参考这个文档:https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/payment-notice.html
*/
log.debug(
"获取到回调事件,请求头信息 sign:{}, serial:{}, nonce:{}, timestamp:{}, signType:{}, info:{}",
sign,
serial,
nonce,
timestamp,
signType,
info
);
// 构造 RequestParam
com.wechat.pay.java.core.notification.RequestParam requestParam = new com.wechat.pay.java.core.notification.RequestParam.Builder()
.serialNumber(serial)
.nonce(nonce)
.signature(sign)
.timestamp(timestamp)
.body(info)
.build();
// 如果已经初始化了 RSAAutoCertificateConfig,可直接使用
NotificationConfig config = new RSAAutoCertificateConfig.Builder()
.merchantId(MtWeChatConstant.MERCHANT_ID)
.privateKeyFromPath(MtWeChatConstant.MERCHANT_PRIVATE_KEY_PATH)
.merchantSerialNumber(MtWeChatConstant.MERCHANT_SERIAL_NUMBER)
.apiV3Key(MtWeChatConstant.API_V3_KEY)
.build();
// 初始化 NotificationParser
NotificationParser parser = new NotificationParser(config);
//获取退款回调的信息
RefundNotification refundInfo = parser.parse(requestParam, RefundNotification.class);
System.out.println(refundInfo);
/**
* 以上代码用于获取退款回调的信息,写法与支付回调很像,具体业务根据自己实际情况修改
* 回调的具体参数参考文档:https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/refund-result-notice.html
*/
return new E7ResponseEntity("SUCCESS");
}
}
JSAPI其他的功能,以及APP支付,H5支付,Native支付等等都可以参考,微信支付SDK全部都封装好了,使用方法大同小异。
微信支付的官方文档:API列表 - JSAPI支付 | 微信支付商户文档中心
微信支付SDK官方文档:GitHub - wechatpay-apiv3/wechatpay-java: 微信支付 APIv3 的官方 Java Library
以上代码为本人编写,如果有哪里涉及到侵权请联系本人删改。