TTokenySpace
返回 Skills 列表

云开发-微信支付

This skill provides battle-tested guidance for integrating WeChat Mini Program payment and refund flows using Tencent CloudBase. It covers cloud.cloudPay.unifiedOrder() for payments, cloud.cloudPay.re

#中文
0

安装到 Tokeny(自动)

下载 ZIP
安装"cloudbase-wxpay"技能
技能信息:
- 名称: 云开发-微信支付
- 标识: cloudbase-wxpay
- 描述: This skill provides battle-tested guidance for integrating WeChat Mini Program payment and refund flows using Tencent CloudBase. It covers cloud.cloudPay.unifiedOrder() for payments, cloud.cloudPay.re
- 版本: 1.0.0
下载地址:
https://www.tokeny.space/api/skills/cloudbase-wxpay/download
继续

复制上方内容到 Tokeny 客户端并在会话中发送即可自动安装;也可直接 下载 ZIP并拖动到技能窗口安装。

SKILL.md

CloudBase 微信小程序支付集成

一站式指南:微信支付 + 原路退款 + CloudBase 云开发,含踩坑全记录。

Overview

This skill encodes a full payment-refund integration cycle on CloudBase: unified-order payment → order management → server-side refund → historical order backfill. It covers both the happy path and every trap encountered during a real production build, so future integrations skip the debugging marathon.

When to Use

Trigger this skill when:

  • Building a new WeChat Mini Program that needs payment via CloudBase
  • Debugging "payment went to simulated mode" or "env vars missing on deploy"
  • Implementing refunds and getting returnCode / resultCode confusion
  • Seeing -501001 invalid wx openapi access_token during refund calls
  • Historical orders need backfilled refunds
  • Reviewing payment/refund code for correctness

Prerequisites

  • WeChat Mini Program with CloudBase (云开发) enabled
  • WeChat Pay merchant account (微信支付商户号) associated with the mini program
  • CloudBase CLI (tcb) installed and logged in: npm i -g @cloudbase/cli
  • Cloud functions: order, payment (minimum)

Architecture Overview

Mini Program (wx.cloud.callFunction)
    ↓
order 云函数 (business logic, status management)
    ↓
payment 云函数 (wraps cloud.cloudPay.* calls)
    ↓
CloudBase cloud.cloudPay.unifiedOrder() / refund()
    ↓
WeChat Pay API

Key separation principle

Keep payment calls in a dedicated payment cloud function — never spread cloud.cloudPay.* across multiple functions. This isolates the cloud-call context dependency and makes debugging tractable.


Payment Integration Workflow

Step 1: Unified Order (统一下单)

In the payment cloud function, the core call:

const result = await cloud.cloudPay.unifiedOrder({
  body: '商品描述',
  outTradeNo: orderId,       // unique order number
  totalFee: 1,               // integer fen (分), e.g. 1 = 0.01 CNY
  spbillCreateIp: '127.0.0.1',
  tradeType: 'JSAPI',
  envId: 'your-env-id',      // HARDCODE, never DYNAMIC_CURRENT_ENV
  functionName: 'payment',
  subMchId: '',              // omit if not sub-merchant mode
});

Critical:

  • envId does NOT work with cloud.DYNAMIC_CURRENT_ENV — hardcode the environment ID string.
  • Remove subAppId unless in sub-merchant mode. Case mismatches cause cryptic API errors.
  • totalFee is in fen (分), not yuan.

Step 2: Upload Cloud Functions via CLI (NOT DevTools)

DevTools auto-upload does not carry environment variables (WX_MCH_ID, etc.) to the cloud → payment silently falls back to simulated mode (no real charge).

Use CLI deployment:

tcb fn deploy payment --envId <your-env-id>
tcb fn deploy order --envId <your-env-id>

Verify deployment with:

tcb fn list --envId <your-env-id>

Step 3: Environment Variables

In cloudbaserc.json or CloudBase console, ensure:

{
  "env": {
    "WX_APPID": "wx...",
    "WX_MCH_ID": "1...",
    "WX_MCH_KEY": "..."
  }
}

The payment cloud function must read these at runtime:

const mchId = process.env.WX_MCH_ID;
if (!mchId) {
  // This is the symptom of DevTools deploy — abort with clear error
  return { code: -1, message: 'WX_MCH_ID not set — use CLI deployment' };
}

Refund Integration Workflow

Step 1: Core Refund Call

In the payment cloud function, add a refund action:

async function handleRefund(orderId, totalFee, refundFee, outRefundNo) {
  const result = await cloud.cloudPay.refund({
    subMchId: '',                    // omit if not sub-merchant
    transactionId: orderId,
    outTradeNo: orderId,
    outRefundNo: outRefundNo || generateOutRefundNo(orderId),
    totalFee: totalFee,             // original total in fen
    refundFee: refundFee,           // amount to refund in fen
    envId: 'your-env-id',           // hardcoded, same as payment
    functionName: 'payment',
  });

  return result;
}

Step 2: CRITICAL — Double-Layer Return Check

This is the #1 cause of silent refund failures. The refund return object has two layers, both must be checked:

const refundResult = await cloud.cloudPay.refund({...});

// Layer 1: CloudBase wrapper
if (refundResult.returnCode !== 'SUCCESS') {
  return { code: -1, errMsg: refundResult.returnMsg || 'refund wrapper failed' };
}

// Layer 2: WeChat Pay result
if (refundResult.resultCode !== 'SUCCESS') {
  return {
    code: -1,
    errCode: refundResult.errCode,
    errMsg: refundResult.errCodeDes || 'refund payment failed',
  };
}

// Only now is the refund truly successful
// Store refundTransactionId from result.refundId or result.transactionId
return {
  code: 0,
  refundTransactionId: refundResult.refundId,
};

Never check only code and assume success — the old code path that caused refundTransactionId to be empty did exactly this.

Step 3: Order Status After Refund

The order cloud function should:

  1. Call payment cloud function's refund action
  2. Check returnCode AND resultCode on the returned result
  3. Only set refundTransactionId and refundedAt when both succeed
  4. If refund fails, DO NOT change status to user_cancelled — leave as-is and surface the error
// In order cloud function, processRefund action:
const refundRes = await callPaymentCloud('refund', { orderId, totalFee, refundFee });

if (refundRes.returnCode !== 'SUCCESS' || refundRes.resultCode !== 'SUCCESS') {
  return { code: -1, errCode: refundRes.errCode, errMsg: refundRes.errCodeDes };
}

// Only now update the order document:
await db.collection('orders').doc(orderId).update({
  data: {
    status: 'user_cancelled',
    refundedAt: new Date(),
    refundTransactionId: refundRes.refundId,
  },
});

Critical Pitfalls Catalog

For the full pitfall catalog with debugging commands and fix recipes, load references/gotchas.md.

Quick reference of the top 5 pitfalls:

#PitfallSymptomFix
1DevTools deploy loses env varsPayment goes to simulated mode, WX_MCH_ID is emptyUse tcb fn deploy CLI
2envId: cloud.DYNAMIC_CURRENT_ENVunifiedOrder returns unexpected dataHardcode env ID string
3subAppId case mismatchunifiedOrder returns resultCode: FAILRemove subAppId if not using sub-merchant
4Refund return not checked properlyrefundTransactionId stays empty, order marked "refunded" but money never returnedDouble-layer check: returnCode + resultCode
5CLI tcb fn invoke for refund-501001 invalid wx openapi access_tokenCloud calls need mini-program context; use wx.cloud.callFunction from mini program side

Cloud Call Context Rules

cloud.cloudPay.refund() and cloud.cloudPay.unifiedOrder() are cloud calls (云调用). They require WeChat-side authentication context. Only these invocation methods carry valid context:

MethodWorks?Reason
wx.cloud.callFunction from Mini Program✅ YesCarries user session + WeChat auth
Timer trigger (定时触发器)✅ YesPlatform injects context
HTTP API trigger✅ YesPlatform injects context
tcb fn invoke (CLI)❌ NoNo mini-program session → access_token failure
CloudBase console "Test" button❌ NoSame reason as CLI

Rule: Any cloud call needing WeChat Pay access_token must originate from one of the three "✅ Yes" methods. When testing refunds or troubleshooting historical orders, trigger through the Mini Program UI, not CLI.


Deployment Checklist

Before testing payment in production:

  • Cloud functions deployed via tcb fn deploy (not DevTools)
  • envId hardcoded as string in all cloud.cloudPay.* calls
  • WX_MCH_ID, WX_APPID, WX_MCH_KEY set as environment variables
  • Payment cloud function reads env vars and fails fast if missing
  • subAppId removed from unifiedOrder unless sub-merchant mode
  • Refund code has double-layer returnCode + resultCode check
  • Historical order backfill uses mini-program-side trigger (not CLI)

Debugging a Failed Refund

When a user reports "order cancelled but money not returned":

  1. Query the order document: Check refundTransactionId — if empty string, refund never executed.

    tcb db query --envId <env-id> -c orders --where '{"_id":"<doc-id>"}'
    
  2. Check order status: If status is user_cancelled but refundTransactionId is empty, the old bug is confirmed.

  3. Fix: Add a force_refund action in the payment cloud function that bypasses status checks, then trigger it from the Mini Program via wx.cloud.callFunction({ name: 'payment', data: { action: 'force_refund', orderId } }).

  4. Cleanup: After backfilling, remove force_refund and any related UI buttons — they are temporary fixes, not intended for users.

Resources

  • references/gotchas.md — Full pitfall catalog with debugging commands, error code reference, and the exact chain of bugs encountered during the real build.

评论

加载中…