Parameterized Sharing and Conversion Funnel in WeChat Mini Program
In one sentence: Embed
inviterand other parameters inpath/queryinside the Mini Program'sonShareAppMessage/onShareTimeline, parse them in the receiving page'sonLoadand store to CloudBase Database, then generate a wxacode with a scene parameter via Cloud Function to form a trackable new-user acquisition flow.Estimated time: 30 minutes | Difficulty: Basic
Applicable Scenarios
- Applicable: Already integrated add-auth-wechat-miniprogram, and looking to implement invitation rewards / referral distribution / channel tracking
- Applicable: Need to generate Mini Program Code posters that can be distributed at scale for marketing campaigns
- Not applicable: Cross-app sharing (requires a different SDK)
- Not applicable: H5 webpage sharing
Prerequisites
| Dependency | Version |
|---|---|
| WeChat Base Library (share to chat) | ≥ 1.2.4 |
| WeChat Base Library (share to Moments) | ≥ 2.11.3 |
@cloudbase/js-sdk | 2.27.3 |
wx-server-sdk | latest (for Cloud API wxacode calls) |
Also required:
- Already completed add-auth-wechat-miniprogram and add-database-wechat-miniprogram
- To use
cloud.openapi.wxacode.getUnlimited, the Mini Program and the current env must already be associated in the Console (same as add-subscribe-message-cloud-function)
Step 1: Share Button with Parameters
In the page to be shared (e.g., pages/product/product.js), implement onShareAppMessage.
// pages/product/product.js
Page({
data: {
productId: '',
},
onLoad(options) {
this.setData({ productId: options.id });
},
onShareAppMessage(res) {
// res.from: 'menu' (top-right menu share) | 'button' (business button)
const inviter = getApp().globalData.user?.customUserId || '';
return {
title: 'I just ordered this, come check it out',
path: `/pages/product/product?id=${this.data.productId}&inviter=${inviter}&channel=share`,
imageUrl: '/assets/share-cover.png', // 5:4 PNG/JPG, optional; defaults to screenshot if not provided
};
},
});
For users to trigger sharing actively, add a <button open-type="share"> business button in the wxml layout.
Easy-to-miss points in the return value:
pathmust start with/- If
imageUrlis not provided, WeChat uses a screenshot of the current visible area as the default; live campaign pages usually provide a fixed image to avoid inconsistency from screenshot timing - For async assembly (e.g., needing to
awaita request), change the entire return value to{ title, path, imageUrl, promise: someAsync() }
Step 2: Receiver Parses Parameters and Stores to Database
After the shared link is opened, it is still the same page, but the options in onLoad will carry the query from the share link.
// pages/product/product.js, continued from above
import { db, auth } from '../../libs/cloudbase';
import { ensureLogin } from '../../libs/login';
Page({
async onLoad(options) {
this.setData({ productId: options.id });
const inviter = options.inviter;
const channel = options.channel;
if (!inviter) return;
await ensureLogin();
const me = auth.currentUser?.customUserId;
if (!me || me === inviter) {
// Inviter and invitee are the same person, skip
return;
}
// Store to share_events; use a compound unique constraint on inviter+invitee to prevent duplicates
await db.collection('share_events').add({
inviter,
invitee: me,
productId: options.id,
channel,
enteredAt: db.serverDate(),
});
},
});
Notes:
- The
share_eventscollection's Permission Mode is recommended to be "Only creator can read and write"; the invitee writes their own record, with_openidadded automatically - Duplicate entries on repeated visits should be prevented. The simplest approach is to use
(inviter, invitee, productId)as a unique index on the Cloud Function side; the frontend just sends and the backend deduplicates - If issuing rewards, storing to the database does not equal issuing the reward. Issue via Cloud Function + internal state machine; do not trust the frontend directly
Step 3: Share to WeChat Moments
The interface for sharing to WeChat Moments is onShareTimeline, supported only with Base Library ≥ 2.11.3, and query restrictions are stricter than group sharing:
Page({
onShareTimeline() {
const inviter = getApp().globalData.user?.customUserId || '';
return {
title: 'I just ordered this, come check it out',
query: `id=${this.data.productId}&inviter=${inviter}`,
imageUrl: '/assets/share-square.png', // 1:1 PNG/JPG
};
},
});
Differences:
- Moments sharing cannot change
path; it can only use "current page + query", so the receiver is still on the same page - Adding business identifiers like
&channel=sharetoqueryis optional, but recommended for later analytics - If the user's WeChat version is below 7.0.15 / Base Library below 2.11.3, the "Share to Moments" button is not visible in WeChat;
onShareTimelinewill not be called. No explicit check is needed in the business logic - Opening from Moments is a "single-page mode"; some wx APIs are unavailable. Do not rely on those APIs in the database write logic
Step 4: Cloud Function Generates Parameterized wxacode Poster
For offline scenarios or batch promotions, a Mini Program Code (wxacode) carrying inviter needs to be generated for each inviter. The Cloud Function retrieves it directly via Cloud API:
cloudfunctions/genWxacode/index.js:
const cloud = require('wx-server-sdk');
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV });
exports.main = async (event) => {
const { inviter, productId } = event;
// scene max 32 characters, ASCII visible characters only; compact key=value format is standard
const scene = `i=${inviter}&p=${productId}`;
if (scene.length > 32) {
return { ok: false, error: 'SCENE_TOO_LONG', scene };
}
try {
const res = await cloud.openapi.wxacode.getUnlimited({
scene,
page: 'pages/product/product',
checkPath: false, // set true in production after page is published; false during integration
envVersion: 'release', // release / trial / develop
width: 430,
});
// res.buffer is a PNG binary
const upload = await cloud.uploadFile({
cloudPath: `wxacodes/${inviter}-${Date.now()}.png`,
fileContent: res.buffer,
});
return { ok: true, fileID: upload.fileID };
} catch (err) {
return { ok: false, errcode: err.errCode, errmsg: err.errMsg };
}
};
On the receiving page, one additional step is needed: when a user enters via scanning the Mini Program Code, onLoad receives options.scene instead of options.id or similar query parameters, and it needs to be decoded:
onLoad(options) {
let inviter = options.inviter;
let productId = options.id;
// Entry via QR scan: scene is a URL-encoded 'i=xxx&p=yyy'
if (!inviter && options.scene) {
const decoded = decodeURIComponent(options.scene);
const params = Object.fromEntries(
decoded.split('&').map((kv) => kv.split('=')),
);
inviter = params.i;
productId = params.p;
}
// Subsequent logic same as Step 2
}
Key points:
sceneis limited to 32 characters and only supports ASCII visible characters. A long openid (28 characters) leaves almost no room for other data; recommend using auserIndex(a mapping table in the database) as a short codegetUnlimitedhas no quantity limit, suitable for large-scale generation. The other interfacewxacode.gethas a total quota of 100,000 codes per Mini Program; use with caution- Set
checkPathtofalseduring integration; enable it after the page is published
Step 5: Conversion Funnel Query
Data is stored in share_events; use db.command aggregation to view conversions:
// Console → Database → share_events → Data Query (Advanced Mode)
db.collection('share_events')
.aggregate()
.group({
_id: '$inviter',
invitees: $.addToSet('$invitee'),
count: $.sum(1),
})
.sort({ count: -1 })
.limit(20)
.end();
Or run a daily report in a Cloud Function — see schedule-cloud-function-cron-job.
Verification
- In WeChat DevTools, on the product detail page, tap the top-right corner → Forward to "File Transfer Assistant"
- Open the link with a second test account; the Console should show
inviterbeing parsed - Console → Database → share_events: a record with
inviter=A andinvitee=B should appear - Moments sharing can only be properly verified on a real device; DevTools does not support it
- Call the
genWxacodeCloud Function once; find the generated PNG in thewxacodes/directory; scanning it should navigate to the corresponding product page
Common Errors
| Error symptom | Cause | Fix |
|---|---|---|
| All parameters lost after forwarding | path does not start with / | Change to /pages/... |
| Moments share button not shown | onShareTimeline is not implemented, or the Base Library version is too low | Implement onShareTimeline, and set the minimum version to 2.11.3 in app.json |
wxacode.getUnlimited returns 41030 | page path does not exist in the published version | Set checkPath to false, or publish the Mini Program first |
wxacode reports invalid scene | scene contains Chinese characters or special characters | Use pure ASCII; store Chinese fields in the database, and put only a short ID in scene |
| Same user entering multiple times causes duplicate records | No deduplication logic | Add a compound unique index inviter+invitee+productId on share_events, or use Cloud Function set({ ... }, { merge: true }) |
Related Documentation
- Page.onShareAppMessage — share callback structure
- Page.onShareTimeline — Moments sharing
- wxacode.getUnlimited — unlimited Mini Program Code
- add-database-wechat-miniprogram — prerequisite: database read/write
- add-auth-wechat-miniprogram — prerequisite: login integration
Next Steps
- Invitation reward issuance flow: add-subscribe-message-cloud-function
- Run a daily conversion report on schedule: schedule-cloud-function-cron-job
- Share isolation in multi-tenant scenarios: secure-database-multi-tenant-rules