易支付安全、低费率、实时到账

小程序云开发支付 - 云函数实现支付功能

小程序云开发(Tencent Cloud Base)无需搭建服务器,可通过云函数直接对接微信支付,快速实现虚拟商品(如课程、激活码、会员)收款,适配个人 / 企业主体的小程序。本教程以 “虚拟商品支付 + 订单同步” 为核心,详解从环境配置、云函数开发到前端交互的完整流程,零基础也能落地。

一、前置准备:账号与权限配置

1. 核心前提条件

  • 小程序已完成实名认证(个人主体需身份证 + 人脸识别,企业主体需营业执照);

  • 小程序已开通微信支付商户号(关联路径:微信支付商户平台→产品中心→小程序支付→关联小程序 AppID);

  • 云开发环境已创建(微信开发者工具→云开发→开通,记录环境 ID,如cloud1-xxxx);

  • 微信支付商户号已完成API 密钥设置(商户平台→账户中心→API 安全→设置 APIv2 密钥,需牢记,后续云函数用)。

2. 权限与配置核对

配置项操作路径注意事项
小程序关联支付商户号微信支付商户平台→产品中心→小程序支付→添加关联→输入小程序 AppID确保小程序主体与商户号主体一致(个人 / 企业对应)
云开发支付权限开通微信开发者工具→云开发→设置→权限管理→微信支付→勾选 “允许云函数调用支付 API”无需手动申请,默认开通
支付回调配置暂无需手动配置(云开发支付通过云函数回调,无需公网域名)区别于传统支付的回调地址配置

二、核心流程设计:云开发支付闭环

小程序云开发支付的核心逻辑是 “前端发起支付请求→云函数调用微信支付 API→用户完成支付→微信回调云函数→更新订单状态”,完整流程如下:

  1. 前端:用户选择虚拟商品(如 “99 元课程”)→点击购买→调用云函数createOrder生成订单;

  2. 云函数createOrder:生成唯一订单号→调用微信支付 “统一下单” API→返回支付参数(如timeStamp、nonceStr)给前端;

  3. 前端:通过wx.requestPayment唤起微信支付弹窗→用户完成支付;

  4. 微信支付:支付成功后,自动回调云函数payCallback(微信预设的回调云函数名,不可自定义);

  5. 云函数payCallback:验证支付结果→更新订单状态为 “已支付”→触发虚拟商品交付(如开通课程权限、发放激活码)。

三、分步实现:从云函数到前端

1. 第一步:初始化云开发环境

  1. 打开微信开发者工具,创建 “云开发小程序” 项目(模板选择 “云开发快速启动模板”);

  2. 打开app.js,修改云环境初始化代码(替换为你的云环境 ID):

// app.js
App({
  onLaunch() {
    if (!wx.cloud) {
      console.error('请使用 2.2.3 或以上的基础库以使用云能力');
    } else {
      wx.cloud.init({
        env: 'cloud1-xxxx', // 你的云环境ID
        traceUser: true, // 跟踪用户行为,可选
      });
    }
    this.globalData = {};
  }
});
  1. 点击开发者工具顶部 “云开发” 按钮,开通后创建 2 个核心集合(数据库表):

    • orders:存储订单数据,字段包括_id(订单号)、openid(用户唯一标识)、totalFee(支付金额,单位分)、status(订单状态:0 = 未支付,1 = 已支付,2 = 已取消)、goodsName(商品名称)、createTime(创建时间);

    • goods:存储商品数据,字段包括_id(商品 ID)、name(商品名称)、price(价格,单位元)、desc(商品描述)。

2. 第二步:开发云函数createOrder(生成订单 + 发起支付)

该云函数负责生成订单、调用微信支付统一下单 API,返回支付参数给前端。

(1)创建云函数

在开发者工具左侧 “云函数” 目录右键→新建 Node.js 云函数→命名为createOrder→打开createOrder目录下的index.js,编写代码:

// 云函数 createOrder/index.js
const cloud = require('wx-server-sdk');
cloud.init();
const db = cloud.database();
const crypto = require('crypto'); // 微信支付签名用(Node.js内置,无需安装)

// 微信支付配置(替换为你的实际信息)
const PAY_CONFIG = {
  appid: 'wx1234567890abcdef', // 你的小程序AppID
  mchid: '1234567890', // 你的微信支付商户号
  apiKey: 'abcdef1234567890abcdef1234567890', // 你的微信支付APIv2密钥
  notifyUrl: 'cloud://cloud1-xxxx.paycallback', // 固定格式:cloud://云环境ID.paycallback(不可改)
};

exports.main = async (event, context) => {
  try {
    const { openid } = cloud.getWXContext(); // 获取当前用户openid(无需前端传,云函数自动获取)
    const { goodsId } = event; // 前端传入的商品ID

    // 1. 查询商品信息(从goods集合中获取商品名称和价格)
    const goodsRes = await db.collection('goods').doc(goodsId).get();
    const { name: goodsName, price } = goodsRes.data;
    const totalFee = Math.round(price * 100); // 转换为分(微信支付金额单位为分)

    // 2. 生成唯一订单号(格式:商户号+时间戳+随机数,避免重复)
    const outTradeNo = `${PAY_CONFIG.mchid}${Date.now()}${Math.floor(Math.random() * 10000)}`;

    // 3. 调用微信支付统一下单API
    const result = await cloud.openapi.pay.unifiedOrder({
      body: goodsName, // 商品描述(将显示在支付账单中)
      outTradeNo: outTradeNo, // 订单号
      totalFee: totalFee, // 支付金额(分)
      spbillCreateIp: '127.0.0.1', // 固定值,云开发无需真实IP
      notifyUrl: PAY_CONFIG.notifyUrl, // 回调云函数(固定格式)
      tradeType: 'JSAPI', // 小程序支付固定类型
      openid: openid, // 支付用户的openid
    });

    // 4. 生成前端支付所需的签名(按微信支付规则计算)
    const { nonceStr, prepayId, sign } = result.result;
    const paySign = crypto.createHash('md5')
      .update(`appId=${PAY_CONFIG.appid}&nonceStr=${nonceStr}&package=prepay_id=${prepayId}&signType=MD5&timeStamp=${Math.floor(Date.now() / 1000)}&key=${PAY_CONFIG.apiKey}`)
      .digest('hex')
      .toUpperCase();

    // 5. 保存订单到orders集合(状态为未支付)
    await db.collection('orders').add({
      data: {
        _id: outTradeNo, // 订单号作为文档ID
        openid: openid,
        goodsName: goodsName,
        totalFee: totalFee,
        status: 0, // 0=未支付
        createTime: db.serverDate(), // 服务器时间(避免前端篡改)
      }
    });

    // 6. 返回支付参数给前端
    return {
      code: 0,
      msg: '订单创建成功',
      payParams: {
        appId: PAY_CONFIG.appid,
        timeStamp: Math.floor(Date.now() / 1000) + '', // 必须为字符串
        nonceStr: nonceStr,
        package: `prepay_id=${prepayId}`,
        signType: 'MD5',
        paySign: paySign,
      },
      orderId: outTradeNo,
    };
  } catch (error) {
    console.error('创建订单失败:', error);
    return {
      code: -1,
      msg: '订单创建失败',
      error: error.message,
    };
  }
};

(2)安装依赖与部署云函数

  1. 打开createOrder目录下的package.json,确保依赖包含wx-server-sdk(云开发默认已包含):

{
  "name": "createOrder",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "dependencies": {
    "wx-server-sdk": "latest"
  }
}
  1. 右键createOrder目录→选择 “在终端中打开”→执行npm install安装依赖;

  2. 右键createOrder目录→选择 “上传并部署:云端安装依赖”(等待部署完成,状态栏显示 “部署成功” 即可)。

3. 第三步:开发云函数payCallback(支付回调处理)

用户支付成功后,微信会自动调用该云函数(名称必须为payCallback,不可自定义),用于验证支付结果、更新订单状态、触发虚拟商品交付。

(1)创建云函数并编写代码

新建 Node.js 云函数→命名为payCallback→打开index.js:

// 云函数 payCallback/index.js
const cloud = require('wx-server-sdk');
cloud.init();
const db = cloud.database();
const crypto = require('crypto');

// 微信支付配置(与createOrder一致)
const PAY_CONFIG = {
  apiKey: 'abcdef1234567890abcdef1234567890', // 你的APIv2密钥
};

exports.main = async (event, context) => {
  try {
    const notifyData = event.xml; // 微信回调的支付结果(XML格式,已自动解析为对象)
    console.log('支付回调数据:', notifyData);

    // 1. 验证微信回调签名(防止伪造回调)
    const { sign, ...otherData } = notifyData;
    // 按ASCII排序参数并拼接
    const sortedKeys = Object.keys(otherData).sort();
    let signStr = sortedKeys.map(key => `${key}=${otherData[key]}`).join('&') + `&key=${PAY_CONFIG.apiKey}`;
    const verifySign = crypto.createHash('md5').update(signStr).digest('hex').toUpperCase();

    if (verifySign !== sign) {
      console.error('签名验证失败');
      return { xml: { return_code: 'FAIL', return_msg: '签名验证失败' } };
    }

    // 2. 验证支付状态(只有支付成功才处理)
    if (notifyData.result_code !== 'SUCCESS' || notifyData.return_code !== 'SUCCESS') {
      console.error('支付未成功:', notifyData.err_code_des);
      return { xml: { return_code: 'FAIL', return_msg: notifyData.err_code_des } };
    }

    // 3. 解析订单信息
    const outTradeNo = notifyData.out_trade_no; // 订单号
    const openid = notifyData.openid; // 支付用户openid
    const totalFee = parseInt(notifyData.total_fee); // 支付金额(分)

    // 4. 更新订单状态为“已支付”
    await db.collection('orders').doc(outTradeNo).update({
      data: {
        status: 1, // 1=已支付
        payTime: db.serverDate(), // 支付时间
        transactionId: notifyData.transaction_id, // 微信支付流水号
      }
    });

    // 5. 触发虚拟商品交付(核心!根据业务需求扩展)
    // 示例1:开通课程权限(假设存在coursePermissions集合)
    await db.collection('coursePermissions').add({
      data: {
        openid: openid,
        orderId: outTradeNo,
        courseId: 'course_101', // 可从订单关联的商品ID获取
        status: 1, // 1=已开通
        createTime: db.serverDate(),
      }
    });

    // 示例2:发放激活码(假设存在activationCodes集合,且有未使用的激活码)
    // const unusedCode = await db.collection('activationCodes').where({ used: false }).limit(1).get();
    // if (unusedCode.data.length > 0) {
    //   await db.collection('activationCodes').doc(unusedCode.data[0]._id).update({ data: { used: true, openid: openid } });
    //   await db.collection('userCodes').add({ data: { openid: openid, orderId: outTradeNo, code: unusedCode.data[0].code } });
    // }

    // 6. 向微信返回成功标识(必须返回,否则微信会重复回调)
    return { xml: { return_code: 'SUCCESS', return_msg: 'OK' } };
  } catch (error) {
    console.error('回调处理失败:', error);
    return { xml: { return_code: 'FAIL', return_msg: error.message } };
  }
};

2)部署云函数

同样执行 “上传并部署:云端安装依赖”(无需额外安装依赖,crypto为 Node.js 内置模块)。

4. 第四步:前端页面开发(发起支付 + 展示订单)

(1)商品购买页(pages/pay/pay.wxml)



  Python入门课程(虚拟商品)
  ¥99.00
  立即购买




  支付成功!订单号:{{payResult.orderId}}
  支付失败:{{payResult.msg}}

(2)前端逻辑(pages/pay/pay.js)

// pages/pay/pay.js
Page({
  data: {
    payResult: null, // 支付结果
  },

  // 点击“立即购买”触发支付
  handlePay() {
    wx.showLoading({ title: '创建订单中...' });

    // 1. 调用云函数createOrder(传入商品ID,假设商品ID为goods_101)
    wx.cloud.callFunction({
      name: 'createOrder',
      data: { goodsId: 'goods_101' }, // 前端传入商品ID
      success: (res) => {
        wx.hideLoading();
        const { code, msg, payParams, orderId } = res.result;
        if (code === 0) {
          // 2. 唤起微信支付弹窗
          wx.requestPayment({
            ...payParams, // 云函数返回的支付参数
            success: (payRes) => {
              console.log('支付成功:', payRes);
              this.setData({
                payResult: { code: 0, msg: '支付成功', orderId: orderId }
              });
              // 跳转至订单详情页或商品使用页
              wx.navigateTo({ url: `/pages/order/order?orderId=${orderId}` });
            },
            fail: (payErr) => {
              console.error('支付失败:', payErr);
              this.setData({
                payResult: { code: -1, msg: payErr.errMsg }
              });
            }
          });
        } else {
          this.setData({ payResult: { code: -1, msg: msg } });
        }
      },
      fail: (err) => {
        wx.hideLoading();
        console.error('调用云函数失败:', err);
        this.setData({ payResult: { code: -1, msg: '创建订单失败' } });
      }
    });
  }
});

(3)订单详情页(可选,pages/order/order.js)

用于展示订单状态和虚拟商品交付信息(如激活码、课程入口),核心逻辑是从orders集合查询订单数据:

// pages/order/order.js
Page({
  data: {
    orderInfo: null,
  },

  onLoad(options) {
    const { orderId } = options;
    this.getOrderInfo(orderId);
  },

  // 查询订单信息
  getOrderInfo(orderId) {
    wx.cloud.callFunction({
      name: 'queryOrder', // 可新建云函数查询订单,或直接前端查询(简化版用前端查询)
      data: { orderId: orderId },
      success: (res) => {
        this.setData({ orderInfo: res.result.data });
      }
    });
  }
});

四、测试与问题排查

1. 测试流程(使用微信开发者工具)

  1. 确保云函数已全部部署成功(云开发控制台→云函数→查看createOrder和payCallback状态为 “已部署”);

  2. goods集合中添加测试商品(_id: goods_101,name: Python入门课程,price: 99);

  3. 点击 “预览”→选择 “真机预览”(微信支付需在真机测试,模拟器无法唤起支付);

  4. 点击 “立即购买”→唤起微信支付→完成支付(个人主体可使用测试金额,企业主体需真实支付后退款);

  5. 支付成功后,查看orders集合中该订单的status是否变为 1,coursePermissions集合是否新增权限记录。

2. 常见问题解决方案

问题现象可能原因解决方案
云函数调用失败,提示 “权限不足”云开发未开通支付权限 / 小程序未关联商户号检查云开发 “权限管理→微信支付” 是否勾选;确认商户号已关联小程序 AppID
唤起支付失败,提示 “prepay_id 不存在”统一下单 API 调用失败,如参数错误、商户号未开通小程序支付检查createOrder中appid、mchid、apiKey是否正确;登录商户平台确认 “小程序支付” 已开通
支付成功后订单状态未更新payCallback签名验证失败 / 回调逻辑错误1. 核对apiKey是否与商户平台一致;2. 查看云函数日志(云开发→日志→选择payCallback)排查错误
回调函数重复触发未返回{ xml: { return_code: 'SUCCESS' } }确保payCallback最后返回格式正确,无语法错误,且支付成功后返回 SUCCESS

五、进阶优化(可选)

1. 防重复支付

createOrder中添加订单校验:同一用户 + 同一商品 30 分钟内未支付的订单,不允许重复创建。

2. 订单超时取消

新建云函数cancelOrder,通过云开发定时触发器(云开发→触发器→新建),每天凌晨执行,将创建时间超过 24 小时且未支付的订单status改为 2(已取消)。

3. 虚拟商品自动交付优化

  • 课程权限:支付成功后,前端通过wx.cloud.callFunction查询coursePermissions集合,自动跳转至课程学习页;

  • 激活码:在payCallback中发放激活码后,通过wx.cloud.openapi.subscribeMessage.send发送订阅消息,将激活码推送给用户。

通过以上步骤,即可完成小程序云开发支付功能的实现,无需服务器即可实现虚拟商品收款 + 自动交付,适配在线课程、软件激活码等场景。核心优势是快速部署、低成本(云开发免费额度足够个人 / 小型平台使用)、合规安全(微信支付官方对接,资金直达商户账户)。


返回顶部