Java 微信支付 V3版本 新手入门 保姆级 最新 最全 最简单 开发教程
序言:简单点,说话的方式简单点,递进的情绪请省略
由于业务设计到了支付,本来想去网上找个写好的用,但是都太麻烦了,有的封装的太重太复杂。导致学习成本太高。我太懒了,本来只需要看微信的就行了,用别人的还要去学他们的使用方法,也不懂他们内部代码逻辑。干脆自己按照官方的方式来吧。
不BB,上干货。
- 直接百度搜索:微信官方文档(点击直达)

- 官方文档里面列出了所有微信生态的文档,我们只需要关注微信支付模块即可

- 有关微信支付的相关知识本文不再赘述,本文只负责最简单的技术实现
- 本文是商户角色模式,关于合作商角色模式和这个差不多,区别在于多了子商户概念及相关参数。商户模式搞懂了,那合作商模式就无压力,无非就是换个包下的对象调接口,再加点参数。
一、开发准备
按照官方文档收集好以下必要参数:
1 在 小程序|公众号 内:
- 申请AppID
- 生成AppSecret
2 在商户平台
- 申请mchid
- 配置API key
- 下载并配置商户证书
3 小程序|公众号 和商户进行AppID和mchid的绑定
至此,你有了以下这些必须参数及私钥文件:
- appid(公众号|小程序)
- AppSecret(密钥)
- merchantId(商户号)
- merchantSerialNumber(商户证书序列号)
- apiV3key(apiV3密钥)
- apiclient_key.pem(商户API私钥文件)
(ps:这几个参数和私钥文件还不知道在哪里配置在哪里找,就看看官方说明,或者网上搜搜,相关资料太多,这里就不再赘述了)
二、在项目内编写配置文件和工具类
- pom.xml加入依赖
<!-- hutool 工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.32</version>
</dependency>
<!-- 微信支付 -->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.12</version>
</dependency>
依托于hutool工具类,不想用这个的也可以用其他的,或者自己编写。
微信支付sdk,官方推荐使用这个,我就用这个了。当前时间是2024年10月,最新版本是0.2.14。为啥我没用最新的,因为我懒,懒得把0.2.12改成0.2.14。后续可能官方还会推出更高的新版本。你想用哪个您老随意哈。
- 编写配置文件:properties.properties
放到resources目录下
内容如下,参数更换成你自己的:
### 微信公众号 配置 ###
# 公众号【 XXXXXX( 公众号名字 ) 】
wechat.office.account.appid=xxx
wechat.office.account.secret=xxxxxx
### 微信小程序 配置 ###
# 小程序【 XXXXXXX(小程序名字) 】
wechat.mini.program.appid = xxx
wechat.mini.program.secret = xxxxxxx
### 微信支付 配置 ###
#appid(公众号|小程序)
wechat.pay.appid = xxx
#商户号
wechat.pay.merchantId = xxxx
#商户证书序列号
wechat.pay.merchantSerialNumber = xxxxxxxxxxxxxxxxxxxx
#商户APIV3密钥
wechat.pay.apiV3key = xxxxxxxxxxxxxxxxxx
#异步通知url(注意拦截器是否拦截)
wechat.pay.notify.url = https://xxxxxxxx
- 编写配置文件读取工具:PropertiesUtil.java
import cn.hutool.setting.dialect.Props;
import cn.hutool.setting.dialect.PropsUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 读取 resources/properties.properties 全局配置文件
*/
public class PropertiesUtil {
private static final Logger logger = LoggerFactory.getLogger(PropertiesUtil.class);
//properties.properties 文件
private static final String configFileName = "properties";
private PropertiesUtil() { }
//读取properties.properties文件
private static final Props props = PropsUtil.get(configFileName);
public static String getString(String key) {
return props.getStr(key, "");
}
public static Integer getInteger(String key) {
return props.getInt(key, 0);
}
public static Boolean getBool(String key) {
return props.getBool(key, false);
}
public static Object get(Object key) {
return props.get(key);
}
}
- 在resources目录下放入私钥文件apiclient_key.pem

三、封装相关服务
- 创建微信支付服务并读取配置文件:WechatPayService.java
/**
* 微信支付服务
*/
public class WechatPayService {
// appid 使用自己刚刚写的PropertiesUtil工具读取,下同
private static final String appid = PropertiesUtil.getString("wechat.pay.appid");
// 商户号
private static final String merchantId = PropertiesUtil.getString("wechat.pay.merchantId");
// 商户证书序列号
private static final String merchantSerialNumber = PropertiesUtil.getString("wechat.pay.merchantSerialNumber");
// 商户APIV3密钥
private static final String apiV3key = PropertiesUtil.getString("wechat.pay.apiV3key");
// 回调地址
private static final String notifyUrl = PropertiesUtil.getString("wechat.pay.notify.url");
// 商户API私钥文件 hutool工具读取resources目录下的私钥文件apiclient_key.pem
private static final String privateKey = ResourceUtil.readUtf8Str("apiclient_key.pem");
}
- 添加自动验签对象
private static final RSAAutoCertificateConfig config =
new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKey(privateKey)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3key)
.build();
- 封装 微信支付下单服务,用来获取微信的预支付订单信息 给前端使用
/**
* 微信支付下单 创建订单
*
* @param fen 订单总金额,单位为分。
* @param description 商品描述
* @param outTradeNo 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一,长度[6-32]
* @param openId 用户在直连商户appid下的唯一标识。 下单前需获取到用户的Openid
* @return 下单结果
*/
public static PrepayWithRequestPaymentResponse createOrder(Integer fen, String description, String outTradeNo, String openId) {
//这里使用JSAPI支付模块,其他模块大同小异,使用其他支付模块,就用其他支付模块包内的对象
//使用Jsapi服务扩展对象:com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension
JsapiServiceExtension serviceExtension = new JsapiServiceExtension.Builder().config(config).build();
//构建对象com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest,并设置必填参数
//其他参数请移步[官方接口文档:https://pay.weixin.qq.com/doc/v3/merchant/4012525057]
PrepayRequest request = new PrepayRequest();
Amount amount = new Amount();
amount.setTotal(fen);
request.setAmount(amount);
request.setAppid(appid);
request.setMchid(merchantId);
request.setDescription(description);
request.setNotifyUrl(notifyUrl);
request.setOutTradeNo(outTradeNo);
Payer payer = new Payer();
payer.setOpenid(openId);
request.setPayer(payer);
//发送请求
return serviceExtension.prepayWithRequestPayment(request);
}
- 封装 微信回调服务
/**
* 下单支付回调
* @param request HttpServletRequest
* @return 通知结果
*/
public static Transaction payCallback(HttpServletRequest request) {
String signature = request.getHeader("Wechatpay-Signature");
String nonce = request.getHeader("Wechatpay-Nonce");
String timestamp = request.getHeader("Wechatpay-Timestamp");
String serial = request.getHeader("Wechatpay-Serial");
String signatureType = request.getHeader("Wechatpay-Signature-Type");
String requestBody = getRequestBody(request);//1.采用输入流方式接收请求体
// 构建com.wechat.pay.java.core.notification.RequestParam
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(serial)
.nonce(nonce)
.signType(signatureType)
.signature(signature)
.timestamp(timestamp)
.body(requestBody)
.build();
//构建通知解析器com.wechat.pay.java.core.notification.NotificationParser
NotificationParser parser =
new NotificationParser(config);
//验签、解密并转换成 com.wechat.pay.java.service.payments.model.Transaction
return parser.parse(requestParam, Transaction.class);//获取通知结果
}
/** 获取请求体 */
private static String getRequestBody(HttpServletRequest request) {
StringBuilder requestBody = new StringBuilder();
try {
//hutool工具读取输入流内容
requestBody.append(IoUtil.readUtf8(request.getInputStream()));
} catch (IOException e) {
logger.error("读取请求体发生了异常.........");
e.printStackTrace();
}
return requestBody.toString();
}
- 封装 订单查询服务
/**
* 商户订单号查询订单
* @param outTradeNo 商户订单号
* @return com.wechat.pay.java.service.payments.model.Transaction
*/
public static Transaction queryOrderByOutTradeNo(String outTradeNo) {
JsapiServiceExtension serviceExtension = new JsapiServiceExtension.Builder().config(config).build();
QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
request.setMchid(merchantId);
request.setOutTradeNo(outTradeNo);
return serviceExtension.queryOrderByOutTradeNo(request);
}
- 封装 退款
/**
* 微信退款申请
*
* @param outTradeNo 原支付交易对应的商户订单号:商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一,长度[6-32]
* @param outRefundNo 商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。
* @param money 退款金额,单位为分,只能为整数,不能超过原订单支付金额。
* @param total 原支付交易的订单总金额,单位为分,只能为整数。
* @return com.wechat.pay.java.service.refund.model.Refund
*/
public static Refund refund(String outTradeNo, String outRefundNo, Long money, Long total) {
RefundService service = new RefundService.Builder().config(config).build();
CreateRequest request = new CreateRequest();
// request.setXxx(val)设置所需参数,具体参数可见Request定义
request.setOutTradeNo(outTradeNo);
request.setOutRefundNo(outRefundNo);
AmountReq amount = new AmountReq();
amount.setTotal(total);
amount.setRefund(money);
amount.setCurrency("CNY");
request.setAmount(amount);
return service.create(request);
}
至此,完毕
针对其他接口自己按照这个进行封装吧
四、创建接口 使用相关服务
业务说明:
小程序用户要想支付成功,支付接口需要小程序用户的openid,此参数需要授权登录才可拿到。拿到后就可以调用支付下单接口获取预付订单信息返回给前端,前端用户用预付订单信息调起支付输入密码键盘,用户正确输入密码支付成功后,微信支付会请求你的服务器设定的回调地址接口。此接口会带有预付订单相关信息,根据这些信息再更新自己系统内的数据。至此,整个微信支付流程走完。
- 封装微信小程序服务:WechatMiniService.java 用户授权获取open_id
官方文档
/**
* <h1>微信小程序服务</h1>
*/
public class WechatMiniService {
private final static Logger logger = LoggerFactory.getLogger(WechatMiniService.class);
//小程序 appid
private static final String appId = PropertiesUtil.getString("wechat.mini.program.appid");
//小程序 secret
private static final String secret = PropertiesUtil.getString("wechat.mini.program.secret");
/**
* <h1> 小程序授权登录 </h1>
* <h2>功能描述</h2>
* 登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。
*
* @param code 授权码
*/
public static MiniCode2Session jscode2session(String code) {
return jscode2session(appId, secret, code);
}
/**
* <h1> 小程序授权登录 </h1>
* <h2>功能描述</h2>
* 登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。
*
* @param appId appId
* @param appSecret appSecret
* @param code 授权码
*/
public static MiniCode2Session jscode2session(String appId, String appSecret, String code) {
String url = "https://api.weixin.qq.com/sns/jscode2session";
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("appid", appId);
paramMap.put("secret", appSecret);
paramMap.put("js_code", code);
paramMap.put("grant_type", "authorization_code");
try {
//hutool的http请求工具
String result = HttpUtil.get(url, paramMap, 10000);
//hutool的JOSN工具
JSONObject json = JSONUtil.parseObj(result);
return json.toBean(MiniCode2Session.class);
} catch (Exception e) {
String failMsg = "小程序授权登录使用授权码code获取token时,发送get请求异常";
logger.error(failMsg);
logger.error(e.getMessage());
return new MiniCode2Session("1000010001", failMsg);
}
}
//其他相关接口封装省略:解析手机号、生成小程序码、等等的其他功能
}
接口返回对象封装
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 微信小程序授权登录结果对象
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MiniCode2Session {
private String session_key;
private String unionid;
private String openid;
private String errcode;
private String errmsg;
public MiniCode2Session(String errcode, String errmsg) {
this.errcode = errcode;
this.errmsg = errmsg;
}
/**
* <h1> 如果存在 access_token , 则表示请求成功 </h1>
*
* @return boolean
*/
public boolean isSuccess() {
return StrUtil.isNotBlank(this.session_key);
}
/**
* <h1> 获取错误消息 </h1>
* errcode + " : " + errmsg;
*/
public String getErrorMessage() {
return this.errcode + " : " + this.errmsg;
}
}
按照此模式你还可以封装一个公众号相关服务。微信官方文档里面有那么多模块,每一个你都可以这样封装。
- Controller添加小程序授权登录接口
@PostMapping("/login")
public R<?> login(@RequestBody JSONObject json) {
//此处伪代码,自己完善保存用户信息,以及token生成相关逻辑,接口权限拦截问题
//这些不在支付范围内
//使用前端传来的code换取openid
MiniCode2Session code2Session = WechatMiniService.jscode2session(json.getStr("code"));
if (code2Session.isSuccess()) {
return R.ok(code2Session);
}
return R.fail(code2Session.getErrorMessage());
}
- 前端拿到open_id,调用下单接口
@PostMapping("/create/order")
@Transactional
public R<?> createOrder(@RequestBody Orders order) {
//单价*数量=总价(单位元)
BigDecimal totalAmount = NumberUtil.mul(order.getProductPrice(), order.getProductCount());
//总价元 * 100 = 总价分
Integer fen = NumberUtil.mul(totalAmount,new BigDecimal("100")).intValue();//金额单位转成分
//hutool工具雪花算法生成同一商户下不重复的支付单号
String outTradeNo = IdUtil.getSnowflakeNextIdStr();
//生成支付记录
PrepayWithRequestPaymentResponse payment = WechatPayPartnerService.createOrder(tenant.getSubAppid(), tenant.getSubMchid(), fen, order.getProductName(), outTradeNo, user.getCodes());
//省略业务逻辑
return R.ok(payment);
}
-
前端拿到下单接口返回的信息,调起输入密码键盘
前端小程序支付成功后,微信支付会进行回调请求,请求地址就是在下单接口中配置的回调地址参数 -
Controller添加 不被拦截的回调接口
//支付通知
@PostMapping("/callback")
public JSONObject callback(HttpServletRequest request, HttpServletResponse response) {
JSONObject result = JSONUtil.createObj();
result.set("code", "SUCCESS");
try {
logger.info("收到支付回调");
Transaction transaction = WechatPayService.payCallback(request);
String outTradeNo = transaction.getOutTradeNo();//Transaction内还有很多参数,直接get某个参数获取
logger.info("商户内部支付单号:{}", outTradeNo);
//省略业务相关逻辑
} catch (Exception e) {
result.set("code", "FAIL");
}
return result;
}
至此,整个支付流程完毕 撒花鼓掌吧
五、对接过程问题分析及排错
- 公众号|小程序 必须认证,开通微信支付
- 商户号必须和appid主体进行绑定
- 全程必须https
- 看清官方接口文档内请求头Content-Type类型:
:application/x-www-form-urlencoded
:application/json
:multipart/form-data - 看清官方接口文档内参数位置放在哪个位置:
:Path
:Header
:Body - 是否开启白名单拦截
- 是否配置相关域名
- 是否配置相关js接口权限
每一个问题请自行查询解决方案,网上方案太多了,这里不再赘述
六、服务商模式
服务商模式下注意的点就是
要使用服务商包下的例如:
//服务商下的:com.wechat.pay.java.service.partnerpayments.jsapi.JsapiServiceExtension
//一般商户下的:com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension
JsapiServiceExtension serviceExtension = new JsapiServiceExtension.Builder().config(config).build();
这就是序言内第4点说的:无非就是换个包下的对象调接口,再加点参数。
所有对象都换成用服务商包下的对象即可
七、吐槽一下
在看完官方的文档后,感觉网上的文章不能说有问题,但是太复杂了,动不动就签名验签的。太烦了。看我这流程代码里面,有个鸡毛的签名验签代码,每个逻辑就只有几行代码。都是按照官方推荐的来的。
当然了,我这代码也有不足之处。没有啥分层分模块分架构。优点就是全部静态方法,使用时只需一行代码搞定(^ _ ^)。简单的不要不要的。主要是理解官方整个支付流程机实现方法。
有不足之处望各位指出来!
八、关于微信支付公钥问题
2025年08月,近期在使用上面的这套逻辑搞微信支付,发现微信支付平台改版了,强制要求使用微信支付公钥模式;直接取消了平台证书模式;没办法,只能按照官方的来吧。。。。
其实也简单,就几个步骤,改动代码量也不多。
1、升级sdk到当前最新版0.2.17 (具体哪个版本开始支持平台公钥模式没有细查,反正当前最新版本肯定支持,直接用当前的最新版本0.2.17即可)
2、properties配置文件增加一个配置项:
#微信支付公钥
wechat.pay.publicKeyId = PUB_KEY_ID_xxxxx
3、添加平台公钥文件到项目资源目录下:pub_key.pem
4、更换自动验证对象
由:
private static final RSAAutoCertificateConfig config =
new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKey(privateKey)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3key)
.build();
更换为:
// publicKeyId
private static final String publicKeyId = PropertiesUtil.getString("wechat.pay.publicKeyId");
// 微信支付平台公钥文件
private static final String pubKey = ResourceUtil.readUtf8Str("pub_key.pem");
//自动验签对象
private static final RSAPublicKeyConfig config =
new RSAPublicKeyConfig.Builder()
.merchantId(merchantId)
.publicKeyId(publicKeyId)
.publicKey(pubKey)
.privateKey(privateKey)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3key)
.build();
至此可以正常处理了。
更多推荐

所有评论(0)