微信支付又双叒叕对接失败了?这7种支付方式让你的系统秒变收银台!
微信支付又双叒叕对接失败了?这7种支付方式让你的系统秒变收银台!
大家好,我是服务端技术精选的老司机,今天咱们聊聊一个让无数后端程序员头疼的话题——微信支付对接。
你是不是也遇到过这些崩溃场景:
- 微信支付接口调试了一周,总是报"签名错误",被产品经理催到怀疑人生
- 支付成功了但回调总收不到,用户投诉订单状态不更新,客服电话被打爆
- 好不容易对接了APP支付,老板又要求支持小程序支付,代码改得一团糟
- 退款接口调用失败,财务天天问什么时候能自动退款,压力山大
我曾经在一家电商公司,因为微信支付对接不完整,双11期间大量用户无法支付,损失订单上千万。经过半年的踩坑和优化,我们把微信支付的7种方式全部搞定,从此收银台稳如老狗!
今天就把这些血泪经验全盘托出,让你的微信支付对接再也不踩坑!
一、微信支付为啥这么难对接?
微信支付看似就是调个接口,实际上是个复杂的生态系统:
1. 支付方式多样化
- APP支付:在APP内调起微信支付
- H5支付:在手机浏览器中支付
- 小程序支付:在微信小程序内支付
- JSAPI支付:在微信内H5页面支付
- Native支付:扫码支付
- 刷脸支付:人脸识别支付
- 付款码支付:扫用户的付款码
2. 安全机制复杂
- 签名算法:MD5、HMAC-SHA256多种签名方式
- 证书管理:API证书、平台证书定期更换
- 敏感信息加密:用户隐私信息需要加密传输
3. 回调处理麻烦
- 异步通知:支付结果通过回调异步通知
- 重复通知:微信会多次推送,需要做幂等性处理
我之前见过最惨的是某创业公司,因为没处理好微信支付回调的幂等性,一笔订单被重复处理了10次,用户收到了10件商品...
二、微信支付的7种方式全攻略
方式1:APP支付 - 移动端的主流选择
适用场景:原生APP内支付,用户体验最佳
/**
* APP支付实现
*/
@Service
public class WxAppPayService {
@Value("${wx.pay.appId}")
private String appId;
@Value("${wx.pay.mchId}")
private String mchId;
/**
* 创建APP支付订单
*/
public WxAppPayResponse createAppPayOrder(AppPayRequest request) {
try {
// 1. 构建统一下单参数
WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
orderRequest.setAppid(appId);
orderRequest.setMchId(mchId);
orderRequest.setNonceStr(generateNonceStr());
orderRequest.setBody(request.getBody());
orderRequest.setOutTradeNo(request.getOrderNo());
orderRequest.setTotalFee(request.getAmount());
orderRequest.setNotifyUrl("https://yourdomain.com/wx/pay/notify");
orderRequest.setTradeType("APP");
// 2. 调用微信统一下单接口
WxPayUnifiedOrderResult orderResult = wxPayService.unifiedOrder(orderRequest);
// 3. 构建APP支付参数返回给客户端
WxAppPayResponse response = new WxAppPayResponse();
response.setAppId(appId);
response.setPartnerId(mchId);
response.setPrepayId(orderResult.getPrepayId());
response.setPackageValue("Sign=WXPay");
response.setNonceStr(generateNonceStr());
response.setTimeStamp(String.valueOf(System.currentTimeMillis() / 1000));
response.setSign(generateAppPaySign(response));
return response;
} catch (Exception e) {
log.error("APP支付下单失败", e);
throw new PaymentException("支付系统异常,请稍后重试");
}
}
}
方式2:小程序支付 - 微信生态的宠儿
适用场景:微信小程序内支付,无需跳转,体验丝滑
/**
* 小程序支付实现
*/
@Service
public class WxMiniPayService {
public WxMiniPayResponse createMiniPayOrder(MiniPayRequest request) {
try {
// 构建统一下单参数
WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
orderRequest.setAppid(miniAppId); // 小程序AppId
orderRequest.setMchId(mchId);
orderRequest.setTradeType("JSAPI");
orderRequest.setOpenid(request.getOpenId()); // 关键:用户openId
orderRequest.setBody(request.getBody());
orderRequest.setOutTradeNo(request.getOrderNo());
orderRequest.setTotalFee(request.getAmount());
// 调用统一下单
WxPayUnifiedOrderResult orderResult = wxPayService.unifiedOrder(orderRequest);
// 构建小程序支付参数
WxMiniPayResponse response = new WxMiniPayResponse();
response.setTimeStamp(String.valueOf(System.currentTimeMillis() / 1000));
response.setNonceStr(generateNonceStr());
response.setPackageValue("prepay_id=" + orderResult.getPrepayId());
response.setSignType("MD5");
response.setPaySign(generateMiniPaySign(response));
return response;
} catch (Exception e) {
throw new PaymentException("小程序支付下单失败");
}
}
}
方式3:H5支付 - 手机浏览器的救星
适用场景:在手机浏览器中支付,会跳转到微信APP
/**
* H5支付实现
*/
@Service
public class WxH5PayService {
public String createH5PayOrder(H5PayRequest request) {
try {
// H5支付需要场景信息
WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
orderRequest.setAppid(h5AppId);
orderRequest.setMchId(mchId);
orderRequest.setTradeType("MWEB");
orderRequest.setBody(request.getBody());
orderRequest.setOutTradeNo(request.getOrderNo());
orderRequest.setTotalFee(request.getAmount());
// H5支付特有的场景信息
String sceneInfo = String.format(
"{\"h5_info\":{\"type\":\"Wap\",\"wap_url\":\"%s\",\"wap_name\":\"%s\"}}",
request.getWapUrl(), request.getWapName());
orderRequest.setSceneInfo(sceneInfo);
WxPayUnifiedOrderResult orderResult = wxPayService.unifiedOrder(orderRequest);
// 返回H5支付URL,前端直接跳转
return orderResult.getMwebUrl();
} catch (Exception e) {
throw new PaymentException("H5支付下单失败");
}
}
}
方式4:Native支付 - 扫码支付的经典
适用场景:PC端网站、线下收银台,生成二维码让用户扫码支付
/**
* Native支付(扫码支付)实现
*/
@Service
public class WxNativePayService {
public String createNativePayOrder(NativePayRequest request) {
try {
WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
orderRequest.setAppid(nativeAppId);
orderRequest.setMchId(mchId);
orderRequest.setTradeType("NATIVE");
orderRequest.setProductId(request.getProductId()); // Native支付需要商品ID
orderRequest.setBody(request.getBody());
orderRequest.setOutTradeNo(request.getOrderNo());
orderRequest.setTotalFee(request.getAmount());
WxPayUnifiedOrderResult orderResult = wxPayService.unifiedOrder(orderRequest);
// 返回支付二维码URL
return orderResult.getCodeUrl();
} catch (Exception e) {
throw new PaymentException("Native支付下单失败");
}
}
/**
* 生成支付二维码图片
*/
public byte[] generatePayQrCode(String codeUrl) {
try {
QRCodeWriter qrCodeWriter = new QRCodeWriter();
BitMatrix bitMatrix = qrCodeWriter.encode(codeUrl, BarcodeFormat.QR_CODE, 300, 300);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
MatrixToImageWriter.writeToStream(bitMatrix, "PNG", outputStream);
return outputStream.toByteArray();
} catch (Exception e) {
throw new PaymentException("生成支付码失败");
}
}
}
方式5:JSAPI支付 - 微信内网页的专属
适用场景:在微信内的H5页面支付,比如公众号文章内的购买链接
/**
* JSAPI支付(微信内H5支付)实现
*/
@Service
public class WxJsApiPayService {
public WxJsApiPayResponse createJsApiPayOrder(JsApiPayRequest request) {
try {
WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
orderRequest.setAppid(jsApiAppId); // 公众号AppId
orderRequest.setMchId(mchId);
orderRequest.setTradeType("JSAPI");
orderRequest.setOpenid(request.getOpenId()); // 用户在公众号下的openId
orderRequest.setBody(request.getBody());
orderRequest.setOutTradeNo(request.getOrderNo());
orderRequest.setTotalFee(request.getAmount());
WxPayUnifiedOrderResult orderResult = wxPayService.unifiedOrder(orderRequest);
// 构建JSAPI支付参数
WxJsApiPayResponse response = new WxJsApiPayResponse();
response.setAppId(jsApiAppId);
response.setTimeStamp(String.valueOf(System.currentTimeMillis() / 1000));
response.setNonceStr(generateNonceStr());
response.setPackageValue("prepay_id=" + orderResult.getPrepayId());
response.setSignType("MD5");
response.setPaySign(generateJsApiPaySign(response));
return response;
} catch (Exception e) {
throw new PaymentException("JSAPI支付下单失败");
}
}
}
方式6:刷脸支付 - 未来支付的趋势
适用场景:线下门店,用户通过人脸识别完成支付
/**
* 刷脸支付实现
*/
@Service
public class WxFacePayService {
/**
* 获取刷脸支付凭证
*/
public WxFacePayResponse getFacePayAuthInfo(FacePayRequest request) {
try {
WxPayFaceAuthInfoRequest authRequest = new WxPayFaceAuthInfoRequest();
authRequest.setAppid(faceAppId);
authRequest.setMchId(mchId);
authRequest.setStoreId(request.getStoreId());
authRequest.setStoreName(request.getStoreName());
authRequest.setDeviceId(request.getDeviceId());
WxPayFaceAuthInfoResult authResult = wxPayService.getFaceAuthInfo(authRequest);
WxFacePayResponse response = new WxFacePayResponse();
response.setAuthinfo(authResult.getAuthinfo());
response.setExpiresIn(authResult.getExpiresIn());
return response;
} catch (Exception e) {
throw new PaymentException("刷脸支付初始化失败");
}
}
}
方式7:付款码支付 - 商户扫用户码
适用场景:线下门店,商户扫描用户手机上的付款码
/**
* 付款码支付(被扫支付)实现
*/
@Service
public class WxMicroPayService {
public WxMicroPayResponse microPay(MicroPayRequest request) {
try {
WxPayMicropayRequest micropayRequest = new WxPayMicropayRequest();
micropayRequest.setAppid(micropayAppId);
micropayRequest.setMchId(mchId);
micropayRequest.setBody(request.getBody());
micropayRequest.setOutTradeNo(request.getOrderNo());
micropayRequest.setTotalFee(request.getAmount());
micropayRequest.setAuthCode(request.getAuthCode()); // 用户付款码
WxPayMicropayResult result = wxPayService.micropay(micropayRequest);
if ("SUCCESS".equals(result.getResultCode())) {
// 支付成功
WxMicroPayResponse response = new WxMicroPayResponse();
response.setTransactionId(result.getTransactionId());
response.setOutTradeNo(result.getOutTradeNo());
response.setTotalFee(result.getTotalFee());
return response;
} else if ("USERPAYING".equals(result.getErrCode())) {
// 用户支付中,需要轮询查询
return queryPayResult(request.getOrderNo());
} else {
throw new PaymentException("支付失败:" + result.getErrCodeDes());
}
} catch (Exception e) {
throw new PaymentException("付款码支付失败");
}
}
}
三、微信支付回调处理 - 成功的关键
无论哪种支付方式,回调处理都是重中之重:
/**
* 微信支付回调统一处理
*/
@RestController
public class WxPayNotifyController {
/**
* 微信支付回调接口
*/
@PostMapping("/wx/pay/notify")
public String payNotify(HttpServletRequest request) {
try {
// 1. 获取回调数据
String xmlData = getRequestBody(request);
// 2. 解析并验证回调数据
WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(xmlData);
// 3. 验证签名
if (!wxPayService.checkNotifySign(notifyResult)) {
return buildFailResponse("签名验证失败");
}
// 4. 处理支付结果
if ("SUCCESS".equals(notifyResult.getResultCode())) {
// 支付成功,更新订单状态(幂等性处理)
boolean processed = processPaymentSuccess(notifyResult);
return processed ? buildSuccessResponse() : buildFailResponse("订单处理失败");
} else {
// 支付失败,记录失败原因
processPaymentFailure(notifyResult);
return buildSuccessResponse();
}
} catch (Exception e) {
log.error("处理微信支付回调失败", e);
return buildFailResponse("系统异常");
}
}
/**
* 处理支付成功(关键:幂等性)
*/
private boolean processPaymentSuccess(WxPayOrderNotifyResult notifyResult) {
String orderNo = notifyResult.getOutTradeNo();
String transactionId = notifyResult.getTransactionId();
// 幂等性检查,防止重复处理
if (orderService.isPaymentProcessed(orderNo, transactionId)) {
log.info("订单{}支付已处理过,跳过", orderNo);
return true;
}
// 验证订单金额
Order order = orderService.getByOrderNo(orderNo);
if (order == null || !validateAmount(order, notifyResult.getTotalFee())) {
return false;
}
// 更新订单状态
orderService.updatePaymentSuccess(orderNo, transactionId, notifyResult.getTimeEnd());
return true;
}
private String buildSuccessResponse() {
return "<xml><return_code><![CDATA[SUCCESS]]></return_code>" +
"<return_msg><![CDATA[OK]]></return_msg></xml>";
}
}
四、支付方式选择指南
如何选择合适的支付方式?
| 场景 | 推荐方式 | 特点 |
|---|---|---|
| 原生APP | APP支付 | 体验最佳,调起微信APP |
| 微信小程序 | 小程序支付 | 无需跳转,体验丝滑 |
| 手机浏览器 | H5支付 | 跳转微信APP支付 |
| 微信内网页 | JSAPI支付 | 无需跳转,直接支付 |
| PC端网站 | Native支付 | 展示二维码,手机扫码 |
| 线下门店 | 付款码/刷脸支付 | 商户扫码或刷脸 |
配置管理最佳实践
/**
* 微信支付配置管理
*/
@Configuration
@ConfigurationProperties(prefix = "wx.pay")
@Data
public class WxPayConfig {
private String mchId; // 商户号
private String apiKey; // API密钥
private String notifyUrl; // 回调地址
// 不同场景的AppId
private String appAppId; // APP支付
private String miniAppId; // 小程序支付
private String h5AppId; // H5支付
private String jsApiAppId; // JSAPI支付
private String nativeAppId; // Native支付
@Bean
public WxPayService wxPayService() {
WxPayConfig config = new WxPayConfig();
config.setMchId(this.mchId);
config.setApiKey(this.apiKey);
config.setNotifyUrl(this.notifyUrl);
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(config);
return wxPayService;
}
}
五、避坑指南:这些错误千万别犯
坑1:签名算法搞错
// ❌ 错误:参数排序不正确
Map<String, String> params = new HashMap<>(); // 无序
// ✅ 正确:使用TreeMap自动排序
Map<String, String> params = new TreeMap<>();
坑2:回调处理不幂等
// ❌ 错误:直接处理,可能重复
public void processPayment(String orderNo) {
orderService.updateStatus(orderNo, "PAID");
}
// ✅ 正确:幂等性检查
public boolean processPayment(String orderNo, String transactionId) {
if (orderService.isProcessed(orderNo, transactionId)) {
return true; // 已处理过
}
return orderService.updateStatus(orderNo, "PAID");
}
坑3:金额单位混乱
// 注意:微信支付金额单位是分,不是元
// 10元 = 1000分
int totalFee = amount.multiply(BigDecimal.valueOf(100)).intValue();
坑4:AppId配置错误
不同支付方式需要使用对应的AppId,不能混用:
- APP支付:使用APP的AppId
- 小程序支付:使用小程序的AppId
- JSAPI支付:使用公众号的AppId
结语
微信支付的7种方式各有特色,选择合适的方式能大大提升用户体验。记住这几个核心要点:
- 选对方式:根据使用场景选择合适的支付方式
- 签名正确:参数排序、签名算法一个都不能错
- 回调幂等:支付回调必须做幂等性处理
- 配置分离:不同支付方式使用对应的AppId
- 异常处理:完善的异常处理和日志记录
最后送给大家一句话:"微信支付不可怕,可怕的是不知道坑在哪里!"
觉得有用的话,点赞、在看、转发三连走起!下期我们聊聊"如何设计一个高可用的支付系统架构",敬请期待~
关注公众号:服务端技术精选
每周分享后端架构设计的实战经验,让技术更有温度!
标题:微信支付又双叒叕对接失败了?这7种支付方式让你的系统秒变收银台!
作者:jiangyi
地址:http://www.jiangyi.space/articles/2025/12/21/1766304284585.html
- 一、微信支付为啥这么难对接?
- 1. 支付方式多样化
- 2. 安全机制复杂
- 3. 回调处理麻烦
- 二、微信支付的7种方式全攻略
- 方式1:APP支付 - 移动端的主流选择
- 方式2:小程序支付 - 微信生态的宠儿
- 方式3:H5支付 - 手机浏览器的救星
- 方式4:Native支付 - 扫码支付的经典
- 方式5:JSAPI支付 - 微信内网页的专属
- 方式6:刷脸支付 - 未来支付的趋势
- 方式7:付款码支付 - 商户扫用户码
- 三、微信支付回调处理 - 成功的关键
- 四、支付方式选择指南
- 如何选择合适的支付方式?
- 配置管理最佳实践
- 五、避坑指南:这些错误千万别犯
- 坑1:签名算法搞错
- 坑2:回调处理不幂等
- 坑3:金额单位混乱
- 坑4:AppId配置错误
- 结语
0 评论