How to: Build a WhatsApp E-commerce Flow - Postman Collection Ready for WhatsApp Commerce Part 3
Below is a copy‑paste ready Postman collection (JSON) containing the core requests your engineers need to run end‑to‑end tests: send product list, send single product, webhook verification, create order, create checkout session, payment callback verification, and catalog sync. Import this JSON into Postman (File → Import → Raw text / File) and replace the environment variables at the top ({{PHONE_NUMBER_ID}}, {{ACCESS_TOKEN}}, {{BASE_URL}}, {{STRIPE_SECRET}}, {{STRIPE_WEBHOOK_SECRET}}, {{CATALOG_ID}}).
Notes
- The collection uses
https://graph.facebook.com/v18.0for WhatsApp Cloud API endpoints.{{BASE_URL}}is your backend (e.g.,https://api.yourdomain.com).- The checkout example uses Stripe Checkout; adapt to your PSP as needed.
{
"info": {
"name": "WhatsApp Commerce - Core Collection",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"description": "Core requests for product_list, product message, webhook verification, order creation, checkout session, payment callback, and catalog sync."
},
"item": [
{
"name": "Send Product List (interactive product_list)",
"request": {
"method": "POST",
"header": [
{ "key": "Authorization", "value": "Bearer {{ACCESS_TOKEN}}" },
{ "key": "Content-Type", "value": "application/json" }
],
"url": {
"raw": "https://graph.facebook.com/v18.0/{{PHONE_NUMBER_ID}}/messages",
"protocol": "https",
"host": ["graph","facebook","com"],
"path": ["v18.0","{{PHONE_NUMBER_ID}}","messages"]
},
"body": {
"mode": "raw",
"raw": "{\n \"messaging_product\":\"whatsapp\",\n \"to\":\"{{TO_WA}}\",\n \"type\":\"interactive\",\n \"interactive\":{\n \"type\":\"product_list\",\n \"header\":{\"type\":\"text\",\"text\":\"Featured products\"},\n \"body\":{\"text\":\"Tap a product to view details\"},\n \"footer\":{\"text\":\"Free shipping over RM100\"},\n \"action\":{\n \"catalog_id\":\"{{CATALOG_ID}}\",\n \"sections\":[\n {\"title\":\"Featured\",\"product_items\":[{\"product_retailer_id\":\"SKU123\"},{\"product_retailer_id\":\"SKU456\"}]}\n ]\n }\n }\n}"
}
}
}
]
}
Webhook Handler Pseudocode for Production
Below is copy‑paste ready pseudocode (Node.js / Express style) that covers: signature verification, idempotency, reservation TTL, order creation, payment verification, and retries. Adapt to your language/framework.
// Express style pseudocode const express = require('express'); const crypto = require('crypto'); const bodyParser = require('body-parser'); const app = express(); // raw body needed for signature verification (Stripe style) app.use(bodyParser.json({ verify: (req, res, buf) => { req.rawBody = buf; } })); // Utility: idempotency store (Redis recommended) async function isDuplicateEvent(eventId) { // return true if eventId already processed } // Utility: mark event processed async function markEventProcessed(eventId, ttlSeconds = 86400) { // store eventId in Redis with TTL } // Utility: verify WhatsApp webhook signature (if using X-Hub-Signature) function verifyWhatsAppSignature(req, appSecret) { const signature = req.headers['x-hub-signature-256'] || req.headers['x-hub-signature']; if (!signature) return false; const expected = 'sha256=' + crypto .createHmac('sha256', appSecret) .update(req.rawBody) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) ); } // Utility: verify PSP signature (Stripe example) function verifyStripeSignature(req, stripeWebhookSecret) { const sigHeader = req.headers['stripe-signature']; // use Stripe SDK to verify; pseudocode: // return stripe.webhooks.constructEvent(req.rawBody, sigHeader, stripeWebhookSecret); } app.get('/webhook', (req, res) => { // WhatsApp verification endpoint const mode = req.query['hub.mode']; const token = req.query['hub.verify_token']; const challenge = req.query['hub.challenge']; if (mode === 'subscribe' && token === process.env.VERIFY_TOKEN) { return res.status(200).send(challenge); } return res.sendStatus(403); }); app.post('/webhook', async (req, res) => { // Verify signature if configured if (process.env.WA_APP_SECRET) { if (!verifyWhatsAppSignature(req, process.env.WA_APP_SECRET)) { return res.status(401).send('Invalid signature'); } } const body = req.body; for (const entry of body.entry || []) { for (const change of entry.changes || []) { const value = change.value || {}; if (value.messages) { for (const msg of value.messages) { const eventId = msg.id || `${entry.id}:${msg.id}`; if (await isDuplicateEvent(eventId)) continue; await markEventProcessed(eventId); if (msg.type === 'text') { const phone = msg.from; await handleIncomingText(phone, msg); } else if (msg.type === 'interactive') { const interactive = msg.interactive; if ( interactive.type === 'product_list_selection' || interactive.type === 'product' ) { const retailerId = interactive.product_retailer_id || interactive.list_reply?.id; await createProvisionalOrder({ phone: msg.from, sku: retailerId, source: 'whatsapp', wa_message_id: msg.id }); } } } } if (value.statuses) { // handle delivery/read receipts } if (value.payments) { // handle payment events } } } res.sendStatus(200); }); app.post('/create-order', async (req, res) => { const { customer_phone, items, source, metadata } = req.body; const order = await createOrderInDB({ customer_phone, items, source, metadata }); await createReservations(order.id, items, 10 * 60); const session = await createStripeCheckoutSession(order); return res.json({ order_id: order.id, checkout_url: session.url }); }); app.post('/payment-callback', async (req, res) => { try { const event = verifyStripeSignature( req, process.env.STRIPE_WEBHOOK_SECRET ); const eventId = event.id; if (await isDuplicateEvent(eventId)) return res.sendStatus(200); await markEventProcessed(eventId); if (event.type === 'checkout.session.completed') { const session = event.data.object; const orderId = session.metadata.order_id; await markOrderPaid(orderId, session); await sendOrderConfirmationWhatsApp(orderId); } res.sendStatus(200); } catch (err) { console.error('Webhook verification failed', err); return res.status(400).send('Webhook verification failed'); } }); async function reservationCleanupJob() { // cleanup expired reservations } async function createProvisionalOrder({ phone, sku, qty = 1, source, wa_message_id }) { // transactional order + stock reservation } app.listen(process.env.PORT || 3000);
Implementation Notes and Best Practices
Idempotency: use event IDs (WhatsAppwamid.*, PSP event IDs) stored in Redis with TTL to avoid double processing.
- Reservations: reserve stock with a short TTL (5–15 minutes) during checkout; if payment not completed, release automatically.
- Signature verification: always verify webhook signatures from WhatsApp and PSPs; keep raw request body for verification.
- Retries and dead letter: log failed webhook deliveries and implement exponential backoff; push persistent failures to a dead‑letter queue for manual review.
- Logging: store message IDs, timestamps, and payloads for reconciliation and dispute resolution.
- Template usage: minimize template messages; use session messages for replies within 24 hours of user message to reduce template dependency.
Quick Checklist to Run the Collection and Test End to End
- Set environment variables in Postman:
ACCESS_TOKEN,PHONE_NUMBER_ID,CATALOG_ID,BASE_URL,VERIFY_TOKEN,STRIPE_SECRET,STRIPE_WEBHOOK_SECRET,TO_WA. - Import collection into Postman and run requests in this order for a test:
Webhook Verification (GET)— ensure your webhook responds with the challenge.Webhook Receiver Test (POST)— simulate incoming message and confirm your backend handles it.Send Product List— send interactive product list to a test number.Create Provisional Order— create an order and confirm reservation created.Create Checkout Session— create a checkout session and opensession.url.Payment Callback Test— simulate PSP webhook and confirm order marked paid and WhatsApp confirmation sent.
Next is to generate the exact webhook handler code in your preferred language (Node.js/Express, Python/Flask, or Go) with full dependency imports, environment variable handling, Redis examples for idempotency, and a runnable local test harness. refer to How to: Build a WhatsApp E-commerce Flow - Node.js webhook handler — ready to run (Express) with Netlify and cPanel deployment notes
0 Comments