WeChat Pay V3 for Mini Programs: A Complete Node.js Integration Walkthrough
Mini Program WeChat Pay V3 Integration Practical Manual: From Merchant Configuration to Frontend and Backend Implementation
If you have already applied for a WeChat Pay merchant account and want to integrate payment capabilities into your mini program, this article will walk you through the process step by step according to the official V3 interface standard, from account binding and platform configuration to frontend and backend code implementation, joint debugging, and launch. The entire process is based on the Node.js technology stack, the code can be directly reused, and a high-frequency pitfall checklist is attached at the end.
1. Pre-Start Preparation
1.1 Core Parameter Checklist
First, organize the following parameters. You will need them throughout the entire process, saving you the trouble of switching back and forth between platforms to find them.
| Parameter Name | Meaning | Location |
|---|---|---|
appid |
Mini Program Unique Identifier | WeChat Official Accounts Platform → Development Management → Developer ID |
mchid |
WeChat Pay Merchant Number | Top of WeChat Pay Merchant Platform Homepage |
apiV3Key |
APIv3 Symmetric Encryption Key | Merchant Platform → Account Center → API Security → APIv3 Key |
Merchant Certificate Serial Number |
API Certificate Number | Merchant Platform → Account Center → API Security → Certificate Management |
Merchant Private Key |
apiclient_key.pem Certificate File |
Inside the API certificate zip package downloaded from the Merchant Platform |
notify_url |
Asynchronous Payment Result Callback Address | Self-deployed backend HTTPS interface |
1.2 Prerequisite Checks
Confirm these points first to avoid wasted effort:
- The mini program is a non-individual entity and has completed WeChat verification; individual entities do not support payment capabilities.
- The merchant account has completed account opening verification, signed the payment agreement, and the account status is normal.
- The backend service supports the HTTPS protocol, and the corresponding domain name has completed ICP filing.
- Mini program payment reuses JSAPI payment at the bottom layer, so there is no need to configure a separate payment authorization directory.
2. Binding the Merchant Account to the Mini Program AppID
This step is the core prerequisite. Without binding, the mini program has no permission to call the payment capabilities of this merchant account.
2.1 Initiating Authorization from the Merchant Platform
- Log in to the WeChat Pay Merchant Platform with the merchant account's super administrator account.
- Go to the left navigation: 'Product Center → Account Association (AppID Binding)', and click 'Add Authorized AppID' on the right.
- Accurately fill in the mini program's
AppID, read and sign the authorization agreement, and submit.- If the merchant account and mini program entity are the same: Just wait for confirmation from the mini program side after submission.
- If the entities are different: You need to additionally fill in the mini program's verified entity name and sign the 'Joint Operation Commitment Letter'.
2.2 Confirming Authorization in the Mini Program Backend
- Log in to the WeChat Official Accounts Platform (mini program account).
- Go to the left navigation: 'Features → WeChat Pay → Merchant Account Management', find the corresponding application in the 'Pending Associated Merchant Accounts' list.
- Click 'Confirm Authorization' to complete the binding.
You can also directly select 'Existing Merchant Account, Quick Binding' on the 'WeChat Pay' page in the mini program backend; the process and effect are the same.
2.3 Verifying the Binding Result
Go back to the 'Account Association' page on the Merchant Platform. When the corresponding AppID status shows 'Authorized', it is complete.
3. Merchant Platform API Security Configuration
The API Security page has four configuration items. For mini program V3 payment, you only need to configure two of them; the remaining two do not need to be touched.
3.1 Merchant API Certificate (Must Configure)
When the backend calls all V3 interfaces, it must use the merchant private key in the certificate to sign the request to prove the merchant's identity. This is mandatory.
Steps:
- Enter the Merchant Platform 'Account Center → API Security → Merchant API Certificate', and click 'Apply for Certificate'.
- Follow the on-page instructions to generate a certificate request file, submit it, and download the certificate zip package.
- After decompression, you get two core files:
apiclient_key.pem: The merchant private key, the core credential for backend signing. Do not leak it.apiclient_cert.pem: The merchant public key certificate.
You can find the corresponding certificate serial number on the certificate details page; note it down for later use.
3.2 APIv3 Key (Must Configure)
This is commonly known as apiV3Key, a 32-bit symmetric encryption key used to decrypt WeChat Pay callback notifications and download platform certificates.
Generation Rules
- The length must be exactly 32 characters.
- Supports combinations of digits, uppercase letters, and lowercase letters.
- Supports random generation, and a random string without patterns offers the highest security.
Three Quick Generation Methods
Pick whichever is convenient:
Method 1: Browser Console (Zero Dependencies, Fastest)
Open any webpage, press F12 to open Developer Tools, switch to the Console tab, and execute the following code to get the result directly:
btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(24))))
.replace(/[^a-zA-Z0-9]/g, '')
.slice(0, 32)
Method 2: Node.js Command Line
Windows CMD/PowerShell has issues parsing nested quotes, use this version:
node -e "const c=require('crypto');console.log(c.randomBytes(24).toString('base64').replace(/[^a-zA-Z0-9]/g,'').slice(0,32))"
Method 3: Local Script File (Fully System Compatible, Most Stable) Create a new genkey.js file, write the following code, and execute node genkey.js in the terminal:
const crypto = require('crypto');
const apiV3Key = crypto.randomBytes(24)
.toString('base64')
.replace(/[^a-zA-Z0-9]/g, '')
.slice(0, 32);
console.log('Generated APIv3 Key:', apiV3Key);
console.log('Length Check:', apiV3Key.length);
Note: After generating the key, save it permanently immediately. Once set in the WeChat backend, the plaintext cannot be viewed. If lost, it can only be reset, which will affect online callback decryption.
3.3 Explanation of the Two Items Not Requiring Configuration
The remaining two configuration items do not need attention; don't waste time:
- Merchant APIv2 Key: Only used for signing legacy V2 interfaces. If you are using V3 interfaces throughout, it is completely unnecessary and is independent of the APIv3 key, with no mutual impact.
- WeChat Pay Public Key: New V3 development can automatically download platform certificates for signature verification via the APIv3 key. Mainstream SDKs have this capability built-in, so manual application is not needed.
4. Mini Program Backend Basic Configuration
Two things to do, and then you can start writing code.
Configure Server Domain Name Enter the mini program backend 'Development Management → Development Settings → Server Domain Name', and add the domain name corresponding to the backend interface in the 'request legal domain names'.
Note: Mini program payment does not require configuring a payment authorization directory. This is a core difference from Official Account JSAPI payment; don't waste time looking for this option.
Payment Capability Check On the 'WeChat Pay' page in the mini program backend, if you can see the bound merchant account and its status is 'Normal', it means the payment permission is active.
5. Backend Service Development (Node.js + V3 Interface)
5.1 Complete Payment Flow
First, clarify the entire interaction logic; don't write the sequence in reverse:
- The user clicks to pay in the mini program, and the frontend obtains the login
codeand passes it to the backend. - The backend exchanges the
codefor the user'sopenid. - The backend generates a unique merchant order number, calls the WeChat JSAPI unified order placement interface, and obtains the
prepay_id. - The backend generates the signature parameters required for the frontend to invoke payment based on the
prepay_idand returns them to the mini program. - The mini program calls
wx.requestPaymentto bring up the payment cashier. - After the user completes payment, WeChat asynchronously calls back the backend
notify_url, and the backend updates the order status. - In the frontend payment callback, request the backend to query the real order status, then display the final result.
Don't think this flow is too verbose. Many people skip steps and end up spending ages troubleshooting problems.
5.2 Dependency Installation and Initialization
It is recommended to use the wechatpay-node-v3 SDK, so you don't have to write the signature logic yourself, avoiding 90% of the pitfalls.
Install dependencies:
npm install wechatpay-node-v3 axios
Initialize payment configuration:
const WxPay = require('wechatpay-node-v3');
const fs = require('fs');
const path = require('path');
// Read the merchant private key file
const privateKey = fs.readFileSync(
path.join(__dirname, './cert/apiclient_key.pem'),
'utf8'
);
const wxPay = new WxPay({
appid: 'Your Mini Program appid',
mchid: 'Your Merchant Number mchid',
privateKey: privateKey,
serial_no: 'Your Merchant Certificate Serial Number',
apiv3_private_key: 'Your APIv3 Key',
});
5.3 JSAPI Unified Order Placement Interface
/**
* JSAPI Unified Order Placement
* @param {string} openid User openid
* @param {string} outTradeNo Merchant order number (globally unique)
* @param {number} total Payment amount, unit: cents
* @param {string} description Product description
*/
async function createOrder(openid, outTradeNo, total, description) {
const params = {
description: description,
out_trade_no: outTradeNo,
notify_url: 'https://your-domain/api/pay/notify',
amount: {
total: total,
currency: 'CNY',
},
payer: {
openid: openid,
},
};
const result = await wxPay.transactions_jsapi(params);
// Generate the complete signature parameters needed for the frontend to invoke payment
const payParams = await wxPay.getSignParams(result.prepay_id);
return payParams;
}
5.4 Asynchronous Payment Result Callback Handling
Callback interface requirements: A publicly accessible HTTPS address, supports POST requests, cannot contain port numbers or parameters.
Core logic: First verify the signature and decrypt, then process the order, and finally must return a success response to WeChat, otherwise WeChat will continuously retry the callback.
// Payment callback interface
async function payNotify(req, res) {
try {
// Verify signature and decrypt the callback message
const result = wxPay.verifySignAndDecrypt(req.headers, req.body);
if (result.trade_state === 'SUCCESS') {
// Payment successful, update database order status, handle business logic
const outTradeNo = result.out_trade_no;
const transactionId = result.transaction_id;
// Must return a success response, otherwise WeChat will keep retrying
res.status(200).json({ code: 'SUCCESS', message: 'Success' });
} else {
res.status(200).json({ code: 'FAIL', message: 'Payment not successful' });
}
} catch (err) {
console.error('Payment callback processing failed', err);
res.status(500).json({ code: 'FAIL', message: 'Processing failed' });
}
}
5.5 Order Query Interface
Key point: Never rely solely on WeChat's asynchronous callback. The frontend must actively query the order status after the payment operation is complete. Callbacks can be delayed, lost, or even forged.
async function queryOrder(outTradeNo) {
const result = await wxPay.query({ out_trade_no: outTradeNo });
return result;
}
6. Mini Program Frontend Payment Implementation
6.1 Frontend Payment Flow
- Call
wx.login()to get a temporary login credentialcode. - Pass the
codeto the backend to initiate the order request. - Receive the payment parameters returned by the backend, call
wx.requestPaymentto bring up the payment interface. - After the payment operation is complete, request the backend to query the real order status and display the result.
6.2 Complete Code Example
Page({
// Pay button click event
async handlePay() {
wx.showLoading({ title: 'Paying...', mask: true });
try {
// 1. Get login credential code
const { code } = await wx.login();
// 2. Request backend to place order, get payment parameters
const payRes = await wx.request({
url: 'https://your-domain/api/pay/createOrder',
method: 'POST',
data: {
code: code,
goodsId: 'Product ID',
},
});
const payParams = payRes.data.data;
// 3. Bring up WeChat Pay cashier
const paymentResult = await wx.requestPayment({
timeStamp: payParams.timeStamp,
nonceStr: payParams.nonceStr,
package: payParams.package,
signType: payParams.signType,
paySign: payParams.paySign,
});
// 4. Payment operation complete, actively query order status
if (paymentResult.errMsg === 'requestPayment:ok') {
const orderRes = await wx.request({
url: 'https://your-domain/api/pay/queryOrder',
data: { outTradeNo: 'Current merchant order number' },
});
if (orderRes.data.data.tradeState === 'SUCCESS') {
wx.showToast({ title: 'Payment Successful', icon: 'success' });
// Business navigation after successful payment
}
}
} catch (err) {
if (err.errMsg === 'requestPayment:fail cancel') {
wx.showToast({ title: 'Payment Cancelled', icon: 'none' });
} else {
wx.showToast({ title: 'Payment Failed', icon: 'error' });
console.error('Payment exception', err);
}
} finally {
wx.hideLoading();
}
},
});
Note: The payment amount should not be passed from the frontend. The backend should calculate it based on the product ID to prevent tampering.
7. Joint Debugging, Testing, and Launch
7.1 Joint Debugging Notes
- Must test on a real device: The WeChat Developer Tools cannot bring up the real payment cashier; you must scan the preview QR code and test on a real phone.
- Small amount testing: It is recommended to use a test amount of 0.01 CNY to verify the complete payment and callback flow.
- Local debugging callbacks: If local development does not have a public network address, use intranet penetration tools (cpolar, ngrok both work) for temporary mapping.
- Logging: The backend should print out complete request, response, and callback logs for quick problem location.
7.2 Pre-Launch Checklist
Go through and check off each item; don't miss anything:
- Backend interface domain name has been added to the mini program's request legal domain names.
- Merchant certificate and APIv3 key are configured correctly with no risk of leakage.
- Payment callback has signature verification implemented to prevent forged callback requests.
- Order status is based on the query interface result, not dependent on the frontend callback status.
- Merchant order numbers are globally unique to avoid duplicate orders.
- Payment amount is verified and calculated by the backend, not directly using parameters passed from the frontend.
8. High-Frequency Pitfall Avoidance Guide
These are all pitfalls encountered in actual projects, listed here to save you from stepping into them:
- Incorrect Amount Unit: All WeChat Pay interface amounts are in cents, not yuan. Passing the wrong unit will result in a 100x difference.
- Signature Verification Failure: 90% of cases are due to incorrect parameter casing, mismatched certificate serial numbers, or inconsistent APIv3 keys.
- Callback Not Triggering: Check if the interface is POST, supports HTTPS, and is not blocked by a firewall. If necessary, whitelist WeChat's official callback IP ranges.
- Duplicate Callback Handling: WeChat Pay callbacks may be pushed multiple times. The backend must implement idempotent processing to avoid executing business logic repeatedly.
- Cross-Entity Binding Restrictions: If the merchant account and mini program entity are inconsistent, additional review is required, and some payment features are restricted. Prioritize using a merchant account with the same entity.