阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 小程序的支付功能集成

小程序的支付功能集成

作者:陈川 阅读数:26641人阅读 分类: 微信小程序

支付功能集成的基本流程

微信小程序的支付功能集成主要分为以下几个步骤:

  1. 商户后台配置支付权限
  2. 小程序端调用统一下单接口
  3. 服务端生成支付签名
  4. 小程序端调起支付界面
  5. 处理支付结果回调

首先需要在微信支付商户平台开通支付功能,并配置支付目录和授权域名。小程序开发者需要在app.json中配置request合法域名,包含微信支付接口域名。

商户后台配置

在微信支付商户平台需要进行以下配置:

  • 设置API密钥(32位随机字符串)
  • 配置支付回调地址
  • 绑定小程序APPID
  • 设置支付目录(如:https://www.yourdomain.com/pay/)
// 示例:小程序支付目录配置
{
  "pay_domain": [
    "https://www.yourdomain.com",
    "https://api.yourdomain.com"
  ]
}

小程序端实现

小程序端主要完成以下工作:

  1. 获取用户openid
  2. 调用服务端统一下单接口
  3. 调起微信支付
// 小程序端支付示例代码
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)
            }
          })
        }
      })
    }
  }
})

服务端实现

服务端需要处理以下逻辑:

  1. 统一下单接口
  2. 生成支付签名
  3. 处理支付回调

以下是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('签名验证失败')
    }
  })
})

常见问题与解决方案

  1. 支付签名错误

    • 检查商户密钥是否正确
    • 确认参与签名的参数是否完整
    • 验证签名算法是否正确
  2. 无法调起支付界面

    • 检查package参数格式是否正确
    • 确认小程序已绑定商户号
    • 验证支付目录配置
  3. 支付回调未收到

    • 检查服务器网络是否正常
    • 验证回调地址是否可公开访问
    • 确认商户平台配置的回调地址正确
  4. 跨域问题

    • 确保服务端配置了CORS头
    • 检查小程序request合法域名配置
// 示例:处理跨域
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Headers', 'Content-Type')
  next()
})

支付安全注意事项

  1. 敏感信息保护

    • 商户密钥不应出现在客户端代码中
    • 支付接口应做权限验证
  2. 防重放攻击

    • 使用nonce_str防止重复请求
    • 订单号应保证唯一性
  3. 金额校验

    • 服务端应校验支付金额
    • 避免客户端直接传递金额参数
  4. 日志记录

    • 记录完整的支付流程
    • 保存支付回调原始数据
// 示例:订单号生成
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}`
}

支付场景扩展

  1. H5支付

    • 适用于需要在浏览器中支付的场景
    • 需要额外配置支付域名
  2. Native支付

    • 生成支付二维码
    • 适用于PC网站支付场景
  3. 小程序红包

    • 结合支付功能发放现金红包
    • 需要特殊权限申请
  4. 分账功能

    • 支持多方分账
    • 需要签订分账协议
// 示例:分账接口调用
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)
}

性能优化建议

  1. 支付预创建

    • 提前创建支付订单
    • 减少用户等待时间
  2. 本地缓存

    • 缓存支付结果
    • 避免重复查询
  3. 异步处理

    • 非关键流程异步执行
    • 提高响应速度
  4. 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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌