小程序的支付功能集成
支付功能集成的基本流程
微信小程序的支付功能集成主要分为以下几个步骤:
- 商户后台配置支付权限
- 小程序端调用统一下单接口
- 服务端生成支付签名
- 小程序端调起支付界面
- 处理支付结果回调
首先需要在微信支付商户平台开通支付功能,并配置支付目录和授权域名。小程序开发者需要在app.json
中配置request
合法域名,包含微信支付接口域名。
商户后台配置
在微信支付商户平台需要进行以下配置:
- 设置API密钥(32位随机字符串)
- 配置支付回调地址
- 绑定小程序APPID
- 设置支付目录(如:https://www.yourdomain.com/pay/)
// 示例:小程序支付目录配置
{
"pay_domain": [
"https://www.yourdomain.com",
"https://api.yourdomain.com"
]
}
小程序端实现
小程序端主要完成以下工作:
- 获取用户openid
- 调用服务端统一下单接口
- 调起微信支付
// 小程序端支付示例代码
wx.login({
success: (res) => {
if (res.code) {
wx.request({
url: 'https://your-server.com/api/createOrder',
method: 'POST',
data: {
code: res.code,
amount: 100, // 单位:分
description: '测试商品'
},
success: (res) => {
const paymentData = res.data
wx.requestPayment({
timeStamp: paymentData.timeStamp,
nonceStr: paymentData.nonceStr,
package: paymentData.package,
signType: paymentData.signType,
paySign: paymentData.paySign,
success: (res) => {
console.log('支付成功', res)
},
fail: (err) => {
console.error('支付失败', err)
}
})
}
})
}
}
})
服务端实现
服务端需要处理以下逻辑:
- 统一下单接口
- 生成支付签名
- 处理支付回调
以下是Node.js示例代码:
const crypto = require('crypto')
const axios = require('axios')
// 统一下单
async function createOrder(params) {
const { code, amount, description } = params
// 获取openid
const openid = await getOpenid(code)
// 统一下单参数
const orderParams = {
appid: 'your-appid',
mch_id: 'your-mchid',
nonce_str: generateNonceStr(),
body: description,
out_trade_no: generateOrderNo(),
total_fee: amount,
spbill_create_ip: '用户IP',
notify_url: 'https://your-server.com/api/payCallback',
trade_type: 'JSAPI',
openid: openid
}
// 生成签名
orderParams.sign = generateSign(orderParams)
// 调用微信支付统一下单接口
const result = await axios.post(
'https://api.mch.weixin.qq.com/pay/unifiedorder',
convertToXml(orderParams),
{ headers: { 'Content-Type': 'text/xml' } }
)
// 解析返回结果
const prepayResult = parseXml(result.data)
// 返回小程序支付参数
return {
timeStamp: Math.floor(Date.now() / 1000).toString(),
nonceStr: generateNonceStr(),
package: `prepay_id=${prepayResult.prepay_id}`,
signType: 'MD5',
paySign: generatePaySign(prepayResult.prepay_id)
}
}
// 生成支付签名
function generatePaySign(prepayId) {
const params = {
appId: 'your-appid',
timeStamp: Math.floor(Date.now() / 1000).toString(),
nonceStr: generateNonceStr(),
package: `prepay_id=${prepayId}`,
signType: 'MD5'
}
return generateSign(params)
}
// 通用签名函数
function generateSign(params) {
const sortedKeys = Object.keys(params).sort()
const stringA = sortedKeys
.filter(key => params[key] && key !== 'sign')
.map(key => `${key}=${params[key]}`)
.join('&')
const stringSignTemp = `${stringA}&key=${your-mch-key}`
return crypto.createHash('md5').update(stringSignTemp).digest('hex').toUpperCase()
}
支付回调处理
微信支付成功后,会向配置的notify_url发送支付结果通知:
// 支付回调处理示例
app.post('/api/payCallback', (req, res) => {
let xmlData = ''
req.on('data', chunk => {
xmlData += chunk
})
req.on('end', () => {
const result = parseXml(xmlData)
// 验证签名
if (verifySign(result)) {
// 处理业务逻辑
updateOrderStatus(result.out_trade_no, result.transaction_id)
// 返回成功响应
res.send(`
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>
`)
} else {
res.status(400).send('签名验证失败')
}
})
})
常见问题与解决方案
-
支付签名错误
- 检查商户密钥是否正确
- 确认参与签名的参数是否完整
- 验证签名算法是否正确
-
无法调起支付界面
- 检查package参数格式是否正确
- 确认小程序已绑定商户号
- 验证支付目录配置
-
支付回调未收到
- 检查服务器网络是否正常
- 验证回调地址是否可公开访问
- 确认商户平台配置的回调地址正确
-
跨域问题
- 确保服务端配置了CORS头
- 检查小程序request合法域名配置
// 示例:处理跨域
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Headers', 'Content-Type')
next()
})
支付安全注意事项
-
敏感信息保护
- 商户密钥不应出现在客户端代码中
- 支付接口应做权限验证
-
防重放攻击
- 使用nonce_str防止重复请求
- 订单号应保证唯一性
-
金额校验
- 服务端应校验支付金额
- 避免客户端直接传递金额参数
-
日志记录
- 记录完整的支付流程
- 保存支付回调原始数据
// 示例:订单号生成
function generateOrderNo() {
const date = new Date()
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const random = Math.floor(Math.random() * 10000)
return `${year}${month}${day}${random}`
}
支付场景扩展
-
H5支付
- 适用于需要在浏览器中支付的场景
- 需要额外配置支付域名
-
Native支付
- 生成支付二维码
- 适用于PC网站支付场景
-
小程序红包
- 结合支付功能发放现金红包
- 需要特殊权限申请
-
分账功能
- 支持多方分账
- 需要签订分账协议
// 示例:分账接口调用
async function createSplitOrder(orderId, receivers) {
const params = {
appid: 'your-appid',
mch_id: 'your-mchid',
nonce_str: generateNonceStr(),
transaction_id: orderId,
receivers: JSON.stringify(receivers)
}
params.sign = generateSign(params)
const result = await axios.post(
'https://api.mch.weixin.qq.com/secapi/pay/profitsharing',
convertToXml(params),
{ headers: { 'Content-Type': 'text/xml' } }
)
return parseXml(result.data)
}
性能优化建议
-
支付预创建
- 提前创建支付订单
- 减少用户等待时间
-
本地缓存
- 缓存支付结果
- 避免重复查询
-
异步处理
- 非关键流程异步执行
- 提高响应速度
-
CDN加速
- 静态资源使用CDN
- 优化支付页面加载速度
// 示例:支付结果缓存
const paymentCache = new Map()
function cachePaymentResult(orderId, result) {
paymentCache.set(orderId, {
result,
expire: Date.now() + 3600000 // 1小时过期
})
}
function getCachedPaymentResult(orderId) {
const cached = paymentCache.get(orderId)
if (cached && cached.expire > Date.now()) {
return cached.result
}
return null
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益,请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:小程序的用户授权与登录机制
下一篇:小程序的推送与消息通知