WeChat Official Account (Integrations)
The integration-tile-side view of WeChat. For inbox behaviour (templates, openid capture via QR, 48h window), see Chat Inbox → WeChat.
Connecting
Manual paste only — there's no Embedded Signup for WeChat. The tile opens a form for:
| Field | Where on mp.weixin.qq.com |
|---|---|
| AppID | Settings → Official Account Settings → Account Details |
| AppSecret | Settings → Basic Configuration (regenerate if lost) |
| Server Token | Settings → Basic Configuration → Server Configuration (you choose this string) |
| EncodingAESKey (optional) | Same page — required only for "Safe Mode" message encryption |
On save, Kinderpedia validates by calling:
GET /cgi-bin/token?grant_type=client_credential&appid=…&secret=…If we successfully mint an access token, the row goes connected and credentials are stored encrypted.
Webhook URL
https://<your-domain>/api/integrations/wechat/webhook/{school_id} — note the trailing {school_id}, which is your school's Kinderpedia ID. Paste this exact URL into Server Configuration → URL on the OA platform.
Why is the school ID in the URL? WeChat doesn't carry tenant info in webhook payloads — every signed request just has token + timestamp + nonce. To know which school the inbound belongs to, we need the URL itself to identify the school. Hence the path-scoped school id.
This means: if you've connected WeChat for school 12, your webhook URL is …/wechat/webhook/12. School 13 would be …/wechat/webhook/13. The integration tile shows your exact URL to copy.
Signature validation
WeChat's webhook signature is sha1(sort([token, timestamp, nonce])). We validate on every inbound. All responses are 200 success (or the echoed echostr on the initial GET probe) regardless of validation — non-200s trigger WeChat's aggressive retry queue and we'd rather drop than clog the connection.
For the initial GET probe (WeChat's "is this URL configured?" check), we echo back the supplied echostr query parameter only if the signature matches.
Service Account only
WeChat has two account types:
- Service Account — full messaging API surface (template messages, customer-service messages, media upload)
- Subscription Account — limited, no template/customer-service APIs
Kinderpedia only supports Service Accounts. If your AppID belongs to a Subscription Account, the connect attempt returns an error like "API not available for this account type." Upgrade on the OA platform first.
IP whitelist
WeChat enforces an IP whitelist for API calls — only IPs you've explicitly added at Settings → Basic Configuration → IP Whitelist can hit the WeChat API on your behalf. After connect, add your Kinderpedia server's egress IPs there:
- Dev: usually doesn't matter (the connect call validates).
- Production: list your prod app servers (and any worker hosts if applicable).
If the whitelist is wrong, you'll see "Invalid IP" errors on subsequent template sends — even though the initial connect worked.
Templates sync
Tile button Sync templates mirrors approved templates from the OA platform into your local template cache. Same cadence as WhatsApp — manual on-demand + nightly cron.
WeChat templates carry named variables ({{name.DATA}}, {{date.DATA}}) rather than positional. See Chat Inbox → Templates.
QR generation for openid capture
The integration enables per-lead QR codes on the public form. After a lead submits the form, if WeChat is connected for the school, the post-submit page renders a QR that:
- Encodes a scene string like
l<leadId>orl<leadId>p<parentId> - Served by an anonymous public-form endpoint scoped to that lead (no login required)
- Mints a 24h temp scene-string ticket via
/cgi-bin/qrcode/create
Scan → follow OA → webhook receives subscribe/SCAN event → we bind the OpenID to that parent's profile. See Chat Inbox → WeChat for the full flow.
Disconnecting
Disconnect clears credentials. Existing chat threads remain in the inbox but new outbound fails. WeChat doesn't have an "unsubscribe app" API — we just stop calling them.
Common errors
| Symptom | Likely cause |
|---|---|
| Connect rejects with "invalid appsecret" | AppSecret was regenerated and the new value differs. |
| Connect rejects with "Invalid IP" | Server IP not on whitelist; add it on OA platform first. |
| Webhook GET probe fails on OA platform | Server Token mismatch — must be identical in OA settings and Kinderpedia. |
| Template sync returns empty list | No approved templates, or AppID belongs to Subscription Account. |
| Inbound events not arriving | Webhook URL not "Enabled" on OA platform after configuration — there's a separate Enable toggle. |
Encryption modes
WeChat offers two security modes for webhook payloads:
- Plain text (default) — payloads come in plaintext XML, signature still validated
- Compatible mode — both plaintext + encrypted payload
- Safe mode — fully encrypted payload using
EncodingAESKey
Kinderpedia supports all three. If you set EncodingAESKey during connect, we'll handle the AES-CBC decryption automatically. Most schools start with plain text and migrate to safe mode only if compliance demands.