主要流程
前期准备工作
- 注册认证微信公众号/小程序
- 申请微信商户号
- 配置 API 安全
环境搭建和主要流程理解
- 引入依赖
- 确定支付方式,分析其流程
核心代码开发
- 配置参数
- 向微信发送请求
注意事项
- 个人类型账号无法开通微信支付
- 切勿泄露 APIV3 秘钥
步骤实现
官方文档(以 Native 为例):产品介绍_Native 支付 | 微信支付商户文档中心

先查看具体流程
注册公众号/小程序
官网链接:https://mp.weixin.qq.com/
认证公众号/小程序
方法一

方法二

注册商户号并开通 Native 支付权限
官网链接:https://pay.weixin.qq.com/
开通步骤指引:申请 Native 支付权限指引
绑定商户号与 APPID
对应指引:管理商户号绑定的 APPID 账号
获取对应参数开发
对应指引:开发必要参数说明


核心代码开发
推荐使用官方 SDK
https://github.com/wechatpay-apiv3/wechatpay-java
引入依赖
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.4.13</version>
</dependency>
代码实现
配置类:
@Configuration
public class WeChatPayConfig {
@Value("${wechat.pay.mchid}")
private String mchId;
@Value("${wechat.pay.mchSerialNo}")
private String mchSerialNo;
@Value("${wechat.pay.privateKeyPath}")
private String privateKeyPath;
@Value("${wechat.pay.apiV3Key}")
private String apiV3Key;
@Value("${wechat.pay.certPath}")
private String certPath;
/**
* 创建私钥签名对象
*/
@Bean
public PrivateKey getPrivateKey() throws Exception {
return PemUtil.loadPrivateKey(new FileInputStream(privateKeyPath));
}
/**
* 创建自动更新的 Verifier (验证微信响应签名)
*/
@Bean
public Verifier getVerifier() throws Exception {
ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier(
new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, getPrivateKey())),
apiV3Key.getBytes(StandardCharsets.UTF_8));
return verifier;
}
/**
* 创建 HttpClient (用于调用微信 API)
*/
@Bean
public CloseableHttpClient getWxPayClient() throws Exception {
return WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, getPrivateKey())
.withValidator(new WechatPay2Validator(getVerifier()))
.build();
}
}
服务层(统一下单)示例:

@Override
public String createNativeOrder(String outTradeNo, int totalFee, String body) {
// ... 构建请求参数 (类似 JSAPI,但去掉 payer,增加 scene_info 等)
json.put("appid", appid);
json.put("mchid", mchId);
json.put("description", body);
json.put("out_trade_no", outTradeNo);
json.put("notify_url", "https://yourdomain.com/api/wxpay/notify");
JSONObject amount = new JSONObject();
amount.put("total", totalFee);
amount.put("currency", "CNY");
json.put("amount", amount);
JSONObject sceneInfo = new JSONObject();
sceneInfo.put("payer_client_ip", "用户 IP");
json.put("scene_info", sceneInfo);
try {
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");
// ... 同上设置请求头和实体
JSONObject result = JSON.parseObject(responseString);
return result.getString("code_url");
} catch (Exception e) {
throw new RuntimeException("创建 Native 订单失败", e);
}
}
回调处理
注意:此路径必须是公网能访问的路径
@RestController
@RequestMapping("/api/wxpay")
public class WeChatPayController {
@Autowired
private WeChatPayService weChatPayService;
@PostMapping("/create-jsapi-order")
public ResponseEntity<Map<String, String>> createJsapiOrder(@RequestBody PayRequest request) {
String openid = getUserOpenidFromSessionOrToken(request.getToken());
Map<String, String> result = weChatPayService.createJsapiOrder(openid, request.getOutTradeNo(), request.getTotalFee(), request.getBody());
return ResponseEntity.ok(result);
}
@PostMapping("/notify")
public ResponseEntity<String> handleNotify(@RequestBody String notifyData, HttpServletRequest request) {
try {
String signature = request.getHeader("Wechatpay-Signature");
String nonce = request.getHeader("Wechatpay-Nonce");
String timestamp = request.getHeader("Wechatpay-Timestamp");
String body = notifyData;
// 使用之前配置的 Verifier 来验证
// boolean isValid = verifier.verify(timestamp, nonce, body, signature);
// 解密通知数据 (APIv3 通知体是加密的)
AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));
String plainText = aesUtil.decryptToString(nonce.getBytes(StandardCharsets.UTF_8), associatedData.getBytes(StandardCharsets.UTF_8), ciphertext.getBytes(StandardCharsets.UTF_8));
JSONObject notifyDataObj = JSON.parseObject(plainText);
String eventType = notifyDataObj.getString("event_type");
JSONObject resource = notifyDataObj.getJSONObject("resource");
String cipherText = resource.getString("ciphertext");
String associatedData = resource.getString("associated_data");
String nonceStr = resource.getString("nonce");
String orderInfo = aesUtil.decryptToString(nonceStr.getBytes(), associatedData.getBytes(), cipherText.getBytes());
JSONObject orderData = JSON.parseObject(orderInfo);
String outTradeNo = orderData.getString("out_trade_no");
String transactionId = orderData.getString("transaction_id");
String tradeState = orderData.getString("trade_state");
if ("SUCCESS".equals(tradeState)) {
// TODO: 更新你的数据库订单状态为"已支付"
// TODO: 执行发货、积分发放等后续业务逻辑
// TODO: 记录日志
} else if ("CLOSED".equals(tradeState) || "REVOKED".equals(tradeState)) {
// 支付失败或关闭
}
return ResponseEntity.ok().body("{\"code\":\"SUCCESS\",\"message\":\"成功\"}");
} catch (Exception e) {
log.error("处理微信支付通知失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("");
}
}
}
工具类:
public class WeChatPayUtil {
public static String sign(Map<String, String> params, String apiKey) {
List<String> keys = new ArrayList<>(params.keySet());
Collections.sort(keys);
StringBuilder sb = new StringBuilder();
for (String key : keys) {
String value = params.get(key);
if (value != null && !value.trim().isEmpty() && !"sign".equals(key)) {
sb.append(key).append("=").append(value).append("&");
}
}
sb.append("key=").append(apiKey);
String stringA = sb.toString();
String sign = DigestUtils.md5Hex(stringA).toUpperCase();
return sign;
}
}
总结
对接微信支付主要繁琐的是前期商户号、证书、配置,需仔细阅读文档,后续对接 SDK 非常简单,注意处理幂等性。


