Skip to main content

Parameterized Sharing and Conversion Funnel in WeChat Mini Program

In one sentence: Embed inviter and other parameters in path / query inside the Mini Program's onShareAppMessage / onShareTimeline, parse them in the receiving page's onLoad and 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

DependencyVersion
WeChat Base Library (share to chat)1.2.4
WeChat Base Library (share to Moments)2.11.3
@cloudbase/js-sdk2.27.3
wx-server-sdklatest (for Cloud API wxacode calls)

Also required:

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:

  • path must start with /
  • If imageUrl is 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 await a 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_events collection's Permission Mode is recommended to be "Only creator can read and write"; the invitee writes their own record, with _openid added 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=share to query is 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; onShareTimeline will 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:

  • scene is limited to 32 characters and only supports ASCII visible characters. A long openid (28 characters) leaves almost no room for other data; recommend using a userIndex (a mapping table in the database) as a short code
  • getUnlimited has no quantity limit, suitable for large-scale generation. The other interface wxacode.get has a total quota of 100,000 codes per Mini Program; use with caution
  • Set checkPath to false during 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

  1. In WeChat DevTools, on the product detail page, tap the top-right corner → Forward to "File Transfer Assistant"
  2. Open the link with a second test account; the Console should show inviter being parsed
  3. Console → Database → share_events: a record with inviter=A and invitee=B should appear
  4. Moments sharing can only be properly verified on a real device; DevTools does not support it
  5. Call the genWxacode Cloud Function once; find the generated PNG in the wxacodes/ directory; scanning it should navigate to the corresponding product page

Common Errors

Error symptomCauseFix
All parameters lost after forwardingpath does not start with /Change to /pages/...
Moments share button not shownonShareTimeline is not implemented, or the Base Library version is too lowImplement onShareTimeline, and set the minimum version to 2.11.3 in app.json
wxacode.getUnlimited returns 41030page path does not exist in the published versionSet checkPath to false, or publish the Mini Program first
wxacode reports invalid scenescene contains Chinese characters or special charactersUse pure ASCII; store Chinese fields in the database, and put only a short ID in scene
Same user entering multiple times causes duplicate recordsNo deduplication logicAdd a compound unique index inviter+invitee+productId on share_events, or use Cloud Function set({ ... }, { merge: true })

Next Steps