🔥干货预警,收藏备用🔥

最近在开发一个前后端分离的在线课程平台,接入了微信支付功能,把整个流程整理出来分享给大家,避免踩坑~

🎯 项目架构

  • 后端:Java + Spring Boot

  • 前端:Vue.js

  • 移动端:微信小程序

  • 支付方式:微信支付

💡 微信支付接入流程

1️⃣ 前期准备

  • 申请微信支付商户号

  • 获取商户API密钥

  • 配置支付授权目录和域名

2️⃣ 后端核心实现(Java)

生成预支付订单

java

@RestController

@RequestMapping("/api/pay")

public class PaymentController {

@Autowired

private WechatPayService wechatPayService;

@PostMapping("/createOrder")

public Map<String, String> createPaymentOrder(@RequestBody OrderRequest request) {

// 创建商户订单

String outTradeNo = generateOrderNo();

// 调用微信统一下单API

Map<String, String> paymentParams = wechatPayService.createPrepayOrder(

request.getCourseId(),

request.getAmount(),

outTradeNo,

request.getOpenId()

);

return paymentParams;

}

}

微信支付服务封装

java

@Service

public class WechatPayService {

public Map<String, String> createPrepayOrder(String courseId, BigDecimal amount,

String outTradeNo, String openId) {

// 构造请求参数

Map<String, String> params = new HashMap<>();

params.put("appid", wechatConfig.getAppId());

params.put("mch_id", wechatConfig.getMchId());

params.put("nonce_str", generateNonceStr());

params.put("body", "课程购买-" + courseId);

params.put("out_trade_no", outTradeNo);

params.put("total_fee", amount.multiply(new BigDecimal("100")).intValue() + "");

params.put("spbill_create_ip", "123.207.151.100");

params.put("notify_url", wechatConfig.getNotifyUrl());

params.put("trade_type", "JSAPI");

params.put("openid", openId);

// 生成签名

String sign = generateSignature(params);

params.put("sign", sign);

// 调用微信支付统一下单接口

String response = httpClient.post(WEIXIN_UNIFIED_ORDER_URL, mapToXml(params));

Map<String, String> result = xmlToMap(response);

// 返回前端调起支付所需参数

return buildPaymentParams(result.get("prepay_id"));

}

private Map<String, String> buildPaymentParams(String prepayId) {

Map<String, String> paymentParams = new HashMap<>();

paymentParams.put("appId", wechatConfig.getAppId());

paymentParams.put("timeStamp", System.currentTimeMillis() / 1000 + "");

paymentParams.put("nonceStr", generateNonceStr());

paymentParams.put("package", "prepay_id=" + prepayId);

paymentParams.put("signType", "RSA");

paymentParams.put("paySign", generatePaySign(paymentParams));

return paymentParams;

}

}

支付结果回调处理

java

@PostMapping("/notify")

public String paymentNotify(HttpServletRequest request) {

try {

// 解析微信支付回调数据

String xmlData = getRequestBody(request);

Map<String, String> notifyData = xmlToMap(xmlData);

// 验证签名

if (!verifySignature(notifyData)) {

return "<xml><return_code><![CDATA[FAIL]]></return_code></xml>";

}

// 处理业务逻辑

if ("SUCCESS".equals(notifyData.get("return_code"))) {

String outTradeNo = notifyData.get("out_trade_no");

String transactionId = notifyData.get("transaction_id");

// 更新订单状态

orderService.updateOrderStatus(outTradeNo, OrderStatus.PAID, transactionId);

// 更新用户课程权限

courseService.grantCourseAccess(outTradeNo);

}

return "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>";

} catch (Exception e) {

log.error("支付回调处理失败", e);

return "<xml><return_code><![CDATA[FAIL]]></return_code></xml>";

}

}

3️⃣ 前端调用实现(Vue + 微信小程序)

Vue组件中调起支付

vue

<template>

<div class="payment-page">

<button @click="handlePayment" class="pay-btn">立即支付</button>

</div>

</template>

<script>

import { createPaymentOrder } from '@/api/payment'

export default {

data() {

return {

courseId: '',

orderInfo: null

}

},

methods: {

async handlePayment() {

try {

// 创建支付订单

const response = await createPaymentOrder({

courseId: this.courseId,

amount: this.amount

})

// 调起微信支付

await this.wechatPay(response.data)

this.$message.success('支付成功!')

this.$router.push('/learning')

} catch (error) {

this.$message.error('支付失败:' + error.message)

}

},

wechatPay(paymentParams) {

return new Promise((resolve, reject) => {

if (typeof WeixinJSBridge === 'undefined') {

reject(new Error('微信环境不支持'))

return

}

WeixinJSBridge.invoke(

'getBrandWCPayRequest',

{

appId: paymentParams.appId,

timeStamp: paymentParams.timeStamp,

nonceStr: paymentParams.nonceStr,

package: paymentParams.package,

signType: paymentParams.signType,

paySign: paymentParams.paySign

},

res => {

if (res.err_msg === 'get_brand_wcpay_request:ok') {

resolve()

} else {

reject(new Error('支付取消或失败'))

}

}

)

})

}

}

}

</script>

微信小程序支付调用

javascript

// 小程序支付

wx.request({

url: 'https://your-domain.com/api/pay/createOrder',

method: 'POST',

data: {

courseId: '123',

amount: 99.00

},

success: function(res) {

const paymentParams = res.data

wx.requestPayment({

timeStamp: paymentParams.timeStamp,

nonceStr: paymentParams.nonceStr,

package: paymentParams.package,

signType: paymentParams.signType,

paySign: paymentParams.paySign,

success: (res) => {

console.log('支付成功', res)

wx.navigateTo({

url: '/pages/learning/learning'

})

},

fail: (err) => {

console.error('支付失败', err)

wx.showToast({

title: '支付失败',

icon: 'none'

})

}

})

}

})

⚠️ 注意事项

  1. 签名验证:一定要做好签名验证,防止数据篡改

  2. 重复通知:微信可能会多次发送支付通知,需要做好幂等处理

  3. 金额单位:微信支付金额单位为分,记得转换

  4. 超时处理:设置合理的支付超时时间

  5. 异常处理:做好网络异常、用户取消等情况的处理

🔧 故障排查

  • 支付签名失败:检查参数顺序和签名算法

  • 无法调起支付:确认支付授权目录配置正确

  • 回调接收不到:检查notify_url是否外网可访问

这个流程在我们项目中运行稳定,希望对大家有帮助!有问题的欢迎在评论区讨论~

#技术干货 #微信支付 #前后端分离 #Java #Vue #微信小程序

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐