Skip to main content

Mini Program WeChat Pay Integration Guide

After creating a "WeChat Pay" integration in the CloudBase console, the system automatically generates an HTTP cloud function (function name like <integration-name>-<random-string>, hereinafter referred to as pay-common). This guide describes how to use that cloud function to complete the full ordering-to-callback payment loop on the Mini Program side.

End-to-end pipeline:

  • Outbound requests: Mini Program → wx.cloud.callHTTPFunction (platform auto-auth + openid injection) → pay-common HTTP cloud function → WeChat Pay API
  • Asynchronous callbacks: WeChat Pay → Integration Center (verify + decrypt) → pay-common → business side

Architecture Overview

┌──────────────────┐ ┌──────────────────┐
│ WeChat Mini Prog │ wx.cloud.callHTTPFunction │ pay-common │
│ │ ──── POST /wx-pay/* ──────→ │ (HTTP function) │
│ app.js │ ←─── { code, msg, data } ──│ SDK self-sign │
│ └ wx.cloud.init │ │ Direct WeChat │
│ │ Platform-injected headers: │ Pay API │
│ pay.js │ x-wx-openid └────────┬─────────┘
│ └ callHTTPFunc │ x-wx-appid │ Unified order
│ │ ▼
└──────────────────┘ ┌───────────────────────┐
▲ │ WeChat Pay API │
│ │ api.mch.weixin.qq.com│
│ wx.requestPayment └───────────┬───────────┘
│ │ Async pay callback
└────── Returns invocation params ──┐ ▼
│ ┌───────────────────────┐
│ │ Integration Center │
│ │ (auto verify+decrypt) │
pay-common └───────────┬───────────┘
│ │ Plaintext forward
│ ▼
│ ┌───────────────────────┐
└──────── │ pay-common │
│ /wechatpay/order │
│ → orderService │
└───────────────────────┘

Responsibility breakdown:

RoleResponsibility
Mini ProgramCalls pay-common via wx.cloud.callHTTPFunction; the platform auto-injects openid — no login or token required
pay-common cloud functionReads user identity from the x-wx-openid header; handles outbound requests like ordering, querying, and refunding; receives and handles callbacks
WeChat Pay APIThe party that actually executes payment business
Integration CenterHosts credentials uniformly; performs signature verification and decryption on callbacks, then forwards plaintext to the cloud function

Prerequisites

ItemRequirement
Mini ProgramWeChat-verified; personal Mini Programs cannot enable payment
WeChat Pay merchant IDApplied for, and bound to the above Mini Program on the merchant platform
Merchant super-admin permissionUsed to download the API certificate and set the APIv3 key
Mini Program base library version≥ 3.15.2 (prerequisite for the wx.cloud.callHTTPFunction API)
Node.js 18+ / CloudBase CLIFor local development and debugging
WeChat Developer ToolsLatest stable version

Step 1 · Prepare Merchant Credentials

Log in to the WeChat Pay merchant platform as super-admin and obtain the values from the three sections below. All values will be entered into the Integration Center form in Step 2.

1.1 APIv3 Key

Path: Account Center → API Security → Set APIv3 Key.

For first-time use, click "Set" and customize a 32-character alphanumeric combination. This key is shown only once at setup time — keep it safe.

The APIv3 key is used for AES-GCM decryption of callbacks and signature verification of some V3 API responses. If not set, the WeChat server will refuse to send any callback notifications, and the merchant platform's "Transaction Center → Notification Query" will show "Notification send failed".

1.2 Merchant API Certificate

Path: Account Center → API Security → API Certificate → Apply / Download.

After downloading and unzipping, keep:

  • apiclient_key.pem: the merchant API private key, used for signing order requests
  • 40-character hex certificate serial number (shown on the page), like 36048761817F82958048330A6301E922AB28A04D

apiclient_cert.p12 and apiclient_cert.pem are not used in this guide.

1.3 WeChat Pay Public Key

Path: Account Center → API Security → WeChat Pay Public Key Management → View / Download Public Key.

Keep:

  • wxp_pub.pem: the WeChat Pay public key file
  • Public Key ID (shown on the page), like PUB_KEY_ID_0114...

Difference between public key and platform certificate: WeChat Pay V3 signature verification historically had two schemes — "WeChat Pay Public Key" (downloaded once, valid long-term, recommended) and "WeChat Pay Platform Certificate" (X.509 certificate, expires, requires API rotation). If you fill in the WeChat Pay Public Key in the Integration Center form, pay-common uses the public-key scheme; otherwise it falls back to the platform-certificate scheme.

Difference from the merchant API certificate: The WeChat Pay public key is used to verify responses and callbacks issued by WeChat to merchants (representing WeChat-side identity); the merchant API certificate is used to sign requests sent by the merchant (representing merchant-side identity). The two have opposite roles — do not confuse them.

1.4 Credential Checklist

After completing the three sections above, you should have the following 7 values, all of which will be filled into the Integration Center form in Step 2 (field names follow the env vars actually injected by pay-common):

#Integration Center field / env varFormatSource
1appIdMini Program AppID, like wxc1546068399a59fcWeChat Official Accounts platform
2merchantId10-digit merchant IDMerchant platform homepage
3apiV3Key32-char APIv3 keySee 1.1
4merchantSerialNumber40-char hex certificate serial numberSee 1.2
5privateKeyFull content of apiclient_key.pem (PEM text)See 1.2
6wxPayPublicKeyWeChat Pay public key PEM contentSee 1.3
7wxPayPublicKeyIdPublic Key ID (PUB_KEY_ID_...)See 1.3

Step 2 · Create the Integration in CloudBase

2.1 Enter Integration Center

Path: CloudBase platformTemplates & Integrations → Integration Center → Mini Program WeChat Pay → Create Integration.

2.2 Fill in Integration Information

The console wizard requires the following:

  • Integration name (custom, e.g. miniapp-wxpay)
  • The 7 credentials obtained in Step 1: appId / merchantId / apiV3Key / merchantSerialNumber / privateKey / wxPayPublicKey / wxPayPublicKeyId

Integration Center will host the credentials above in a unified manner and use them to handle callback signature verification and decryption.

2.3 Auto-generated Cloud Function Resources

Upon successful creation, the system automatically performs two operations:

  • Creates an HTTP cloud function based on the integration name (⚠️ function name is like <integration-id>-<random-string>, e.g. miniapp-wxpay-rwmx67sc; this article refers to it as pay-common).
  • Generates the callback domain and specific callback URLs to receive WeChat Pay async notifications:
ResourceDescription
Callback base domainLike https://<integration-id>.integration-callback.tcloudbase.com
Pay callback/wechatpay/order
Refund callback/wechatpay/refund
Transfer callback/wechatpay/transfer (current version not yet supported; Integration Center has not yet handled transfer callbacks; if you need merchant transfer, take it over yourself)

The pay callback URL must be filled into the merchant platform's "Pay Result Notify URL" (merchant platform → Product Center → Development Settings). The refund callback URL is determined by the notify_url carried in the V3 refund interface request — pay-common already automatically uses the corresponding URL generated by Integration Center, no manual filling required. Integration Center automatically injects the URLs above into the cloud function as env vars (see Step 3).


Step 3 · Understand the pay-common Cloud Function

When the integration is created in Step 2, the platform has already injected all the form parameters (AppID, merchant ID, APIv3 key, certificate, private key, callback addresses, etc.) into the cloud function as environment variables. They are read at runtime via process.env; no plaintext credentials appear in the business code, and Integration Center users don't need to maintain any configuration files.

To modify configuration, go to "Integration Center → corresponding integration → Edit"; after saving, the platform will automatically redeploy the cloud function.

3.1 Source Structure

To view or download the auto-generated cloud function source, use tcb fn code <function-name> --download or view it in the console. Directory structure:

pay-common/
├── index.js HTTP cloud function entry (CLI deploy entry)
├── app.js Express app entry, route dispatch (_action)
├── scf_bootstrap HTTP cloud function startup script (listens on port 9000)
├── bin/
│ └── www Local/container startup script (listens on port 3000)
├── Dockerfile Cloud hosting deploy config (not used in Integration Center mode)
├── cloudbaserc.json CloudBase CLI deploy config (no need to modify in Integration Center mode)
├── package.json
├── config/
│ └── config.js Reads process.env, normalizes PEM, validates credentials
├── controllers/
│ └── payController.js Route controllers (order, query, refund, transfer, callback)
├── routes/
│ └── pay.js Public route definitions
├── services/
│ ├── payService.js Unified entry, routes by signMode to strategies
│ ├── orderService.js Callback handling (needs business system integration)
│ └── strategies/
│ └── sdkStrategy.js SDK self-signing strategy (used by all outbound order/refund/transfer requests)
├── utils/
│ ├── validator.js Parameter validation
│ └── cloudbaseAuth.js Extracts openid from request headers (x-wx-openid / JWT)
└── tests/ Unit/integration tests

The Mini Program calls pay-common via wx.cloud.callHTTPFunction; routing uses Express standard routes via the path parameter (e.g. /wx-pay/wxpay_order). The platform auto-injects the x-wx-openid header; no login or token required.

💡 The legacy approach (wx.request + cloud API gateway + body._action dispatch) still works but is not recommended; callHTTPFunction is more concise.

Route path / _actionCategoryDescription
wxpay_orderOrderJSAPI / Mini Program ordering (mainly used in this guide)
wxpay_query_order_by_out_trade_noQueryQuery order by merchant order number
wxpay_query_order_by_transaction_idQueryQuery order by WeChat order number
wxpay_close_orderCloseClose order (recommend proactive close after 10 minutes unpaid)
wxpay_refundRefundApply for refund
wxpay_refund_queryRefundQuery refund
wxpay_transferTransferMerchant transfer. Integration Center does not yet support the transfer callback /wechatpay/transfer; the initiation API works, but callbacks must be taken over yourself
wxpay_transfer_bill_queryTransferQuery transfer bill by merchant order number
wxpay_transfer_bill_query_by_noTransferQuery transfer bill by WeChat order number

When using callHTTPFunction, path has the format /wx-pay/<route-path>, e.g. path: '/wx-pay/wxpay_order'.

3.3 Callback Handling

services/orderService.js is a business placeholder layer; the default implementation only logs. Before going live, depending on your business database choice (MySQL, MongoDB, CloudBase database, etc.), fill in business logic in the following callback handler methods:

MethodTriggered WhenTypical Implementation
handlerUnified(params)After successful order placementPersist the order in PENDING state, recording out_trade_no, amount, user identifier, etc.
handlerUnifiedTrigger(params)Pay callbackIdempotency check on order state, cross-check amount, atomically update state to PAID, and trigger business side effects like shipping
handlerRefund(params)After successful refund initiationUpdate order or refund record state to "Refunding", record out_refund_no
handlerRefundTrigger(params)Refund callbackUpdate refund result based on refund_status (SUCCESS / CHANGE / REFUNDCLOSE); handle account refund and benefit reclaim
handlerTransfer(params, result)After successful transfer acceptanceRecord merchant transfer order number, WeChat transfer order number, and amount
handlerTransferTrigger(params)Transfer callbackUpdate final state of the transfer record based on state (SUCCESS / FAIL, etc.). Integration Center does not yet support transfer callback forwarding; you must take this over yourself if enabled

When implementing, follow these principles:

  • Return as early as possible: All *Trigger callbacks must return {"code":"SUCCESS"} within 5 seconds, otherwise WeChat will retry (up to 24 hours, ~15 times). Complete idempotency checks synchronously where possible; dispatch time-consuming side effects (shipping, push notifications, external API calls) asynchronously to message queues or scheduled tasks.

Step 4 · Integrate with the Mini Program Frontend

After pay-common is deployed, there are three integration approaches on the Mini Program side; choose based on your team's stack:

  • Option 1: Mini Program custom integration — most common path for native developers, calls the backend via wx.cloud.callHTTPFunction; platform auto-injects openid; no login flow needed.
  • Option 2: Use the official sample project — directly reuse the built-in examples/miniprogram/ in the repository — out-of-the-box, suitable for quick verification or refactoring on top of the demo.
  • Option 3: Integrate in WeDa low-code — zero development; paste the page method into WeDa.

Whichever option you choose, complete the prerequisites in 4.1 first; for the API contract, see the route list in 3.2 and callback handling in 3.3.

4.1 Prerequisites

Confirm base library version

In project.config.json, confirm libVersion3.15.2 (the minimum requirement for wx.cloud.callHTTPFunction).

Initialize cloud development

Call wx.cloud.init() in app.js's onLaunch; afterwards you can use wx.cloud.callHTTPFunction:

App({
onLaunch() {
wx.cloud.init({
env: 'your-env-id', // ⚠️ Replace with your CloudBase env ID
traceUser: true,
})
},
})

💡 callHTTPFunction does not require enabling a CloudBase Auth identity provider, does not require installing @cloudbase/js-sdk, and does not require building npm.

4.2 Option 1: Mini Program Custom Integration

Suitable for native Mini Program self-built projects. Calls pay-common via wx.cloud.callHTTPFunction; platform auto-injects openid; no login flow, no token management.

Key constraints:

  • ENV_ID: CloudBase env ID, found in console → Environment Overview → Env ID, like test-wxpay-5gy4ugzreef15cfe
  • FN_NAME: the auto-generated HTTP cloud function name after the integration is created — view it in console → Integration Center → integration detail page or Cloud Functions list, like miniapp-wxpay-rwmx67sc
  • The order request body has at minimum 3 fields: description / out_trade_no / amount
  • amount.total is in cents (1 cent = 0.01 yuan); out_trade_no must be globally unique
  • No need to pass payer.openid — the backend automatically obtains it from the platform-injected x-wx-openid header
  • The return structure of callHTTPFunction is { code: 0, msg: 'success', data: { status: 200, data: { timeStamp, nonceStr, package, signType, paySign } } }. The payment params are at res.data.data (the code uses res.data?.data || res.data as a fallback)
// ============ app.js ============
const ENV_ID = 'your-env-id'
const FUNCTION_NAME = 'pay-common' // the actual function name generated after integration creation

App({
globalData: {
envId: ENV_ID,
functionName: FUNCTION_NAME,
},

onLaunch() {
if (ENV_ID === 'your-env-id') {
wx.showModal({
title: 'Configuration not complete',
content: 'Please replace ENV_ID in app.js with your CloudBase env ID',
showCancel: false,
})
return
}

// Initialize WeChat Cloud Development (callHTTPFunction requires init first)
wx.cloud.init({
env: ENV_ID,
traceUser: true,
})
console.log('[App] Cloud Development initialized, env:', ENV_ID)
},
})

// ============ Page: initiate a complete payment ============
const app = getApp()

/**
* Wraps callHTTPFunction calls
* @param {string} action - route path name, e.g. 'wxpay_order'
* @param {object} data - request params
*/
function callPayCommon(action, data) {
const { functionName, envId } = app.globalData
return new Promise((resolve, reject) => {
wx.cloud.callHTTPFunction({
name: functionName,
config: { env: envId },
method: 'POST',
header: { 'Content-Type': 'application/json' },
path: `/wx-pay/${action}`, // Express standard route
data,
success(res) {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(res.data) // already { code, msg, data }, no unwrap needed
} else {
reject({ code: -1, msg: `HTTP ${res.statusCode}`, data: res.data })
}
},
fail: reject,
})
})
}

async function pay() {
const out_trade_no = 'ORDER_' + Date.now()

// 1. Call cloud function to place order (no payer.openid needed; platform auto-injects x-wx-openid)
const res = await callPayCommon('wxpay_order', {
description: 'Test product',
out_trade_no,
amount: { total: 20, currency: 'CNY' }, // 0.2 yuan
// ❌ No need to pass payer.openid; the backend auto-obtains it from the x-wx-openid header
})

if (res.code !== 0) {
wx.showToast({ title: res.msg || 'Order failed', icon: 'error' })
return
}
const p = res.data?.data || res.data
// p = { timeStamp, nonceStr, package, signType, paySign }

// 2. Invoke WeChat Pay
wx.requestPayment({
timeStamp: String(p.timeStamp),
nonceStr: p.nonceStr,
package: p.package, // like 'prepay_id=wx20...'
signType: p.signType || 'RSA',
paySign: p.paySign,
success() {
// Pay success: frontend only updates UI; final state is determined by server callback
// Recommended: 1-2 seconds later, call wxpay_query_order_by_out_trade_no to actively query as fallback
},
fail(err) {
// Pay failed or user canceled: judge whether it's cancel via err.errMsg
},
})
}

Implementation notes:

  • callHTTPFunction routes via the path parameter as Express standard routes (e.g. /wx-pay/wxpay_order); no _action field needed
  • out_trade_no is preserved via closure variable; reuse it when querying
  • Use the package value from the API response as-is; do not concatenate prepay_id=... yourself — pay-common already assembles it
  • The final payment state is determined by the server callback; the frontend success is only for UI updates; the server executes business side effects like shipping after receiving the callback

4.3 Option 2: Use the Official Sample Project

Suitable when you don't want to set up a scaffold yourself and want to quickly run the full pipeline. The official repo provides a complete Mini Program sample: awesome-cloudbase-examples/integration/cloudbase-wx-pay/examples (also includes a React Web test page). The examples/miniprogram/ directory of this project also has the same sample built in.

One-time Configuration

  1. Import project: WeChat Developer Tools → upper-right "Import Project" → select the examples/miniprogram/ directory.
  2. Fill in key parameters (⚠️ all required, otherwise calls will 404):
    • app.js top ENV_ID → actual CloudBase env ID
    • app.js top FUNCTION_NAMEmust be changed to the real function name generated after integration creation (view it in console → Integration Center → integration detail page, like miniapp-wxpay-rwmx67sc, not the default pay-common)
    • appid in project.config.json → actual Mini Program AppID
    • libVersion in project.config.json → confirm ≥ 3.15.2

💡 The sample uses wx.cloud.callHTTPFunction; no npm install, no build npm, no extra dependencies needed.

Simulator Self-check

Click "Compile" in the upper-left. Seeing successful Cloud Development initialization in the Console means configuration is correct.

The simulator can be used to verify: whether Cloud Development initialized successfully, whether ordering returns prepay_id, and whether page interaction is normal. But the simulator cannot complete real payment — the openid injected by callHTTPFunction in the simulator may be a test value and ordering will be rejected by WeChat; even bypassing that, wx.requestPayment requires entering the payment password in the WeChat app, which the simulator cannot do.

Simulator self-check screenshot

Real-device Payment Test

  1. Upper-right "Real-device Debug" → scan the QR code with your phone's WeChat.
  2. Tap the "WeChat Pay" button on the phone → WeChat shows the password screen → enter password to complete payment.

4.4 Option 3: Integrate in WeDa Low-code

Suitable for zero-development scenarios where you want to build the payment page with low-code. WeDa low-code can directly reuse pay-common — same as a native Mini Program, calling the backend via wx.cloud.callHTTPFunction; platform auto-injects openid; no login flow, no token management.

Pipeline:

WeDa page method → wx.cloud.callHTTPFunction (platform auto-injects x-wx-openid)
→ path: /wx-pay/wxpay_order
→ unwrap response, call wx.requestPayment to invoke payment

Key constraints:

  • No need for $w.auth, no accessToken, no Bearer authcallHTTPFunction already handles identity automatically
  • No need to pass payer.openid — backend auto-obtains it from the x-wx-openid header
  • The pay-common function name uses the real function name generated after integration creation (like pay-050603-mdd8hhn8-demo-scfweb)
  • The Mini Program base library version built by WeDa must be ≥ 3.15.2

Page method example (paste into WeDa page → Methods → Custom Method):

/**
* WeDa page method: initiate a complete payment
* Doc: https://cloud.tencent.com/document/product/1301/57912
*/
export default async function ({ event, data }) {
const out_trade_no = 'ORDER_' + Date.now()

// 1. Call pay-common to place order (no openid needed; platform auto-injects x-wx-openid)
const orderRes = await new Promise((resolve, reject) => {
wx.cloud.callHTTPFunction({
name: 'pay-common', // ⚠️ Replace: real function name after integration creation
config: {
env: 'your-env-id', // ⚠️ Replace: CloudBase env ID
},
method: 'POST',
header: {
'Content-Type': 'application/json',
},
path: '/wx-pay/wxpay_order', // Express standard route
data: {
description: 'Test product',
out_trade_no,
amount: { total: 20, currency: 'CNY' }, // 0.2 yuan
// ❌ No payer.openid; backend auto-gets it from x-wx-openid header
},
success: resolve,
fail: reject,
})
})

const body = orderRes.data
if (body.code !== 0) {
wx.showToast({ title: body.msg || 'Order failed', icon: 'error' })
return
}
const p = body.data && body.data.data ? body.data.data : body.data
// p = { timeStamp, nonceStr, package, signType, paySign }

// 2. Invoke WeChat Pay
wx.requestPayment({
timeStamp: p.timeStamp,
nonceStr: p.nonceStr,
package: p.package, // like 'prepay_id=wx20...'
signType: p.signType || 'RSA',
paySign: p.paySign,
success() {
// Frontend only updates UI; final state is determined by server callback
// Recommended: 1-2 seconds later, call wxpay_query_order_by_out_trade_no as fallback
},
fail(err) {
// On user cancel, err.errMsg contains 'cancel'; can distinguish from real failure
},
})
}

Troubleshooting:

  • If callHTTPFunction is not a function: Check whether the WeDa-built Mini Program base library version is ≥ 3.15.2.
  • If ordering returns appid and openid mismatch: Cross-check whether the appId in Integration Center and the Mini Program AppID used by WeDa are the same — see the FAQ.
  • If 404 is returned: Check the name (function name) and path (route) parameters.

FAQ

Q1: callHTTPFunction is not a function

The Mini Program base library version is too low. wx.cloud.callHTTPFunction requires base library version ≥ 3.15.2.

Solution: In project.config.json, set libVersion to 3.15.2 or higher. In WeDa scenarios, also confirm the WeDa-built Mini Program base library meets this requirement.

Q2: Ordering returns MISSING_CREDENTIALS

MISSING_CREDENTIALS typically comes from missing credentials on the cloud function side. Go to "Integration Center → corresponding integration → Edit" and cross-check item by item against the 1.4 credential checklist: appId / merchantId / apiV3Key / merchantSerialNumber / privateKey / wxPayPublicKey / wxPayPublicKeyId — all 7 are required. After saving, redeploy is triggered automatically; verify again 1–2 minutes later.

Q3: Ordering returns DECODER routines unsupported

The PEM private key is rejected by OpenSSL — usually because the value pasted into the Integration Center form lost its leading/trailing lines or had newlines replaced. Check:

  • Whether the value contains the complete -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY----- lines
  • Whether internal newlines were replaced with spaces (recommend pasting the full apiclient_key.pem file content)
  • Whether there are extra spaces at the leading or trailing edge

After fixing, save in Integration Center to trigger redeployment. The normalizePem() in config/config.js will normalize literal \n back to real newlines, so manual handling is normally not needed.

Q4: Returns "openid is invalid"

The passed openid does not match the current Mini Program AppID. Handling:

  • When using callHTTPFunction, openid is auto-injected by the platform; this issue usually doesn't occur. If it does, confirm the currently logged-in WeChat account matches the Mini Program AppID
  • The appId in Integration Center must be the Mini Program AppID, not the Official Account AppID
  • The merchant ID and Mini Program must be bound on the merchant platform

Q5: No callback notification received

Troubleshoot in order:

  1. Whether the merchant platform's notify URL points to Integration Center (https://<integration-id>.integration-callback.tcloudbase.com/wechatpay/order)
  2. Whether the APIv3 key has been set on the merchant platform and matches the one in Integration Center
  3. Whether the integration detail page status is "Active"
  4. Whether handlerUnifiedTrigger appears in function logs; if not, check the WeChat-side status in the merchant platform's "Transaction Center → Notification Query"

Q6: wx.requestPayment fails to invoke without other hints

Common reasons:

  • Mini Program AppID not bound to the merchant ID (merchant platform → Product Center → JSAPI Pay)
  • Mini Program does not have WeChat Pay capability
  • Personal Mini Program in use (does not support payment)

Q7: Simulator can click the button but cannot invoke payment

Real-device payment must be tested on a real device. Click "Real-device Debug" in the upper-right of WeChat Developer Tools and complete the operation by scanning with your phone.

Q8: Real-device payment returns a limit prompt

This prompt indicates the payment popup has been invoked and the technical pipeline is working — it's a risk-control limit triggered on the WeChat Pay side. Common causes:

  1. Test amount too small: long-term use of 0.01 yuan is identified as "test small-amount transaction"; recommend 0.1–1 yuan.
  2. Single user single day count exceeded: same WeChat account paying the same merchant more than 5–10 times in a day; recommend switching account or testing the next day.
  3. New merchant ID risk-control period: new merchant IDs have implicit single-day total amount caps (100–500 yuan).
  4. Business category does not match transaction scenario: the category filled at onboarding differs significantly from actual use.
  5. Mini Program review status abnormal: associated Mini Program is not yet on shelf or has not passed "WeChat Pay" capability review.

Troubleshooting suggestions:

  • Log in to the WeChat Pay merchant platform; check risk-control prompts in "Account Center → Account Overview"
  • Check failed transaction details in "Transaction Center → Transaction Monitoring"
  • Test with another WeChat account on a real device for comparison

Q9: Can anonymous login invoke payment?

Not supported. The JSAPI ordering interface requires payer.openid; callHTTPFunction satisfies this requirement by auto-injecting x-wx-openid from the platform — provided the user is logged in to the WeChat client. Anonymous access or having no openid set in the simulator means ordering cannot complete.

Q10: Creating an integration reports NOT_ENOUGH / Platform certificate expired

When creating an integration, validation of merchant credentials fails; example console error:

[InvalidParameter] WeChat Pay platform certificate mode validation failed
StatusCode: 403 Code: "NOT_ENOUGH"
Message: Platform certificate has expired; please contact WeChat Pay technical support.

Root cause: The merchant has switched to public-key mode on the WeChat Pay merchant platform, but the integration creation form still validates by "platform certificate mode" — the SDK still tries to fetch the now-decommissioned platform certificate and is rejected by the WeChat gateway.

Solution:

  • In the integration creation form, switch to "public-key mode" and fill in the public key ID (wxPayPublicKeyId). The public key ID can be copied from merchant platform → Account Center → API Security → WeChat Pay Public Key.
  • Also confirm that the value pasted into the "WeChat Pay Public Key" field is PEM content (starting with -----BEGIN PUBLIC KEY-----) and matches the public key ID.
  • If you have switched to public-key mode but still want to keep certificate mode (not recommended), you need to re-apply for and download a new platform certificate on the merchant platform.

How to tell which mode is currently in use: merchant platform → Account Center → API Security; look at whether "WeChat Pay Public Key" or "WeChat Pay Platform Certificate" is enabled.


Appendix · Environment Variables Quick Reference

When creating the integration, Integration Center automatically injects the following env vars into the cloud function — users do not need to maintain them manually. Listed here only for troubleshooting and extension development reference:

VariableRequiredDescription
signModeYesMode switch; default in Integration Center is gateway
appIdYesMini Program AppID
merchantIdYesMerchant ID
apiV3KeyYesAPIv3 key
merchantSerialNumberYesMerchant certificate serial number
privateKeyYesapiclient_key.pem content
wxPayPublicKeyYesWeChat Pay public key PEM
wxPayPublicKeyIdYesWeChat Pay public key ID
notifyURLPayURLYesIntegration Center pay callback URL (auto-injected)
notifyURLRefundsURLYesIntegration Center refund callback URL (auto-injected)
transferNotifyUrlNoIntegration Center transfer callback URL. Not yet supported in current version; leave blank

Further Reading

TopicLink
JSAPI Ordering (Mini Program/Official Account Pay)https://pay.weixin.qq.com/doc/v3/merchant/4012791897
Pay Callback Protocolhttps://pay.weixin.qq.com/doc/v3/merchant/4012791901
wx.requestPayment APIhttps://developers.weixin.qq.com/miniprogram/dev/api/payment/wx.requestPayment.html
wx.cloud.callHTTPFunctionhttps://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-sdk-api/utils/Cloud.callHTTPFunction.html