# Set up the two implemented WHC Flows in Meta

Current deployed journey: first-time Terms/Privacy Flow → welcome message and existing chat menu → Shop uses existing chat categories/products; Search uses a Flow and returns the selected product to the existing chat quantity/basket handlers. Delivery is unchanged. Order confirmation offers Visa/Mastercard, EcoCash and InnBucks; InnBucks reuses the existing PesePay hosted page. The backend completes consent/search using the reserved SUCCESS response, so the existing published Shopping Flow ID remains usable without republishing its JSON. Older basket-mode screen descriptions below apply only to legacy sessions.

These instructions refer to source code implemented in this repository. Neither Flow has been uploaded, Meta-validated, published or activated by this change. Meta UI labels can differ by account; use the WhatsApp Manager Flow editor or Meta's Flows management API.

## 1. Prepare the backend

From the repository root:

```bash
npm --prefix backend run build
```

Apply both additive SQL files to the intended PostgreSQL database using your deployment tooling, in order:

1. `backend/prisma/sql/20261102-shopping-flows.sql`
2. `backend/prisma/sql/20261102-payment-contact-flow.sql`

Do not run them against a different environment by accident. This repository does not currently have a Prisma migration baseline; these standalone scripts are not automatically applied by `prisma migrate deploy`.

Configure the backend environment (see `server.env.example`):

```dotenv
WHATSAPP_APP_SECRET=<secret from the Meta app associated with these Flows>
WHATSAPP_FLOWS_PRIVATE_KEY_PATH=<absolute securely mounted private-key path>
WHATSAPP_FLOWS_KEY_PASSPHRASE=<passphrase if the key is encrypted>
WHATSAPP_FLOWS_IMAGE_ORIGINS=https://your-public-image-cdn.example
```

The image-origin list must match the `imageUrl` origins of synced backend products. Product images accept JPEG/PNG/WebP sources up to 5 MB and convert them to small JPEG thumbnails; unavailable/unsupported images fall back to a placeholder. Search results contain product text, prices and stock; the selected product has the image. Products come from WHC's database through `ProductsService.listProducts`, not directly from WooCommerce on each keystroke. Source-store changes appear after the existing sync updates WHC. Search is submitted with the Search button.

Deploy/restart the backend with a valid public HTTPS URL and the new schema. Ensure your reverse proxy forwards the Flow routes and `x-hub-signature-256` header. The application already preserves the raw request body for signature verification.

## 2. Configure the Meta app and existing WhatsApp webhook

Use the Meta app and WABA already connected to the merchant. The system-user access token must have access to that WABA and phone-number asset, with `whatsapp_business_management` for managing Flows/keys and `whatsapp_business_messaging` for messaging, subject to Meta's app-access requirements.

In the app's WhatsApp webhook configuration, use the backend's existing routes:

- Callback: `https://YOUR_BACKEND/webhooks/whatsapp`
- Verify token: the backend's configured `WHATSAPP_VERIFY_TOKEN`.
- Subscribe to WhatsApp `messages` so Flow completion (`interactive.nfm_reply`) reaches WHC.

Keep the existing webhook if it already uses these values. The two Flow data-exchange endpoints are separate from this message-completion webhook.

## 3. Register the encryption public key

Generate a 2048-bit RSA key pair in your deployment secrets directory and restrict private-key permissions:

```bash
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.pem
openssl pkey -in private.pem -pubout -out public.pem
chmod 600 private.pem
```

Mount `private.pem` at `WHATSAPP_FLOWS_PRIVATE_KEY_PATH`. Do not place it in uploads, commit it or paste it into chat.

With an approved management token, register **public.pem only** on the business's WhatsApp phone-number ID:

- `POST https://graph.facebook.com/<SUPPORTED_GRAPH_VERSION>/<PHONE_NUMBER_ID>/whatsapp_business_encryption`
- Bearer authorization: the management token.
- Form field: `business_public_key` = full public PEM contents.

Then `GET` the same URL and check `business_public_key_signature_status` is `VALID`. Use a Graph API version supported by the connected Meta app; this is distinct from Flow JSON version 7.1. Both Flows share the key registered on that phone number and this deployment's Meta app secret.

## 4. Create the shopping Flow

Open Meta Business Suite → WhatsApp Manager → the correct WABA → Flows → Create Flow. Name it **WHC Shopping**, choose the shopping category and use the JSON/code editor.

1. Upload/paste `docs/whatsapp-flows/shopping-flow.json`.
2. Associate the same Meta app.
3. Set endpoint URI to `https://YOUR_BACKEND/whatsapp/flows/data-exchange`.
4. Save and run Meta validation. Fix any schema/account-version errors before proceeding.
5. Confirm encrypted endpoint health checks pass. A normal browser GET or unsigned curl POST is not a valid endpoint health test.
6. Publish after the test journey passes, and copy the **Flow ID** (not WABA ID or phone-number ID).

For API-based setup, the corresponding Meta operations are creating a Flow under `/{WABA_ID}/flows`, uploading its JSON asset under `/{FLOW_ID}/assets`, updating Flow metadata including `endpoint_uri` and `application_id`, and publishing under `/{FLOW_ID}/publish`. The backend does not currently expose dashboard buttons to perform these Meta management operations automatically.

## 5. Create the payment-contact Flow

Create a **second** Flow, named **WHC Card Payment Contact**, using a suitable account-details category.

1. Upload/paste `docs/whatsapp-flows/payment-contact-flow.json`.
2. Associate the same Meta app.
3. Set endpoint URI to `https://YOUR_BACKEND/whatsapp/flows/payment-data-exchange`.
4. Save, validate, verify endpoint health, test and publish.
5. Copy this second Flow's ID.

A preview cannot invent valid application sessions: WHC generates expiring tokens when a real customer launches shopping or selects card on an owned pending order. Static layout previews are useful, but dynamic data exchange must be tested with an application-issued token. Health pings do not need a shopping/order token. Use an authorized test customer in an active chat window; never fabricate live payment confirmations.

## 6. Save both Flow IDs in WHC

Open WHC Dashboard → Integrations → WhatsApp:

| Dashboard field | Saved integration credential |
| --- | --- |
| Enable native shopping Flow | `shoppingFlowEnabled: true` |
| Published Flow ID | `shoppingFlowId` |
| Terms version | `shoppingTermsVersion` |
| Full terms URL (HTTPS) | `shoppingTermsUrl` |
| Privacy policy URL (HTTPS) | `shoppingPrivacyUrl` |
| Shopping terms summary | `shoppingTermsSummary` |
| Enable card-payment contact Flow | `paymentContactFlowEnabled: true` |
| Published payment contact Flow ID | `paymentContactFlowId` |

Use your approved shopping terms and published policy pages; do not copy the transport-booking terms from the original screenshots. Save Integration. Keep the existing WhatsApp access token and phone-number ID.

In the PesePay integration, keep live merchant/encryption keys and enable the Visa/Mastercard checkbox only after PesePay confirms card acceptance for the merchant and currency. For Paynow, the existing authenticated integrations API supports `cardPaymentsEnabled: true` in Paynow credentials. The existing provider choice is Paynow first, then PesePay. Sandbox/mock payment links do not qualify for the card Flow.

## 7. Test the connected journey

Shopping:

1. From a separate customer WhatsApp number, message the merchant to open the customer-service window; send `shop flow`.
2. Accept the current terms version. Returning customers skip that screen until the version changes.
3. Search for an actual WHC product, select a category, choose a product/pack size, check its image and price, choose quantity and add.
4. Tap **View basket in chat**. The bot displays the existing basket; add another product through a new shopping Flow if needed.
5. Choose Checkout and complete the existing chat name/delivery/pickup/confirmation steps.

Card checkout:

1. After confirming the order, select **Visa / Mastercard** (available only with an enabled provider and a pending HTTPS payment link).
2. Tap **Confirm phone number** to open the second Flow. The WhatsApp number is prefilled and editable.
3. Submit a valid number. The backend saves normalized digits in `Order.paymentContactPhone`; an invalid number stays on the same screen.
4. Tap **Return to chat**. Completion passes through the existing WhatsApp webhook; the bot checks token ownership and sends **Pay securely** with the existing provider URL.
5. Enter card details on the provider page. Provider callbacks use the already integrated `POST /webhooks/payments/paynow` or `POST /webhooks/payments/pesepay`, which call the existing payment verification service.
6. Verify success, failure, cancellation, duplicate submissions, expiry and payment confirmation. Returning from the browser is not payment confirmation.

A phone number here is contact information, not proof of ownership, card identity or an OTP. It does not replace the WhatsApp order owner/confirmation recipient. The existing hosted-payment API has no phone parameter, so WHC stores it on the order rather than inventing a provider field. No card numbers, CVVs or bank OTPs are collected by these Flows.

## Implemented backend route map

| Purpose | Actual backend route | Implementation |
| --- | --- | --- |
| Shopping data exchange | `POST /whatsapp/flows/data-exchange` | `ShoppingFlowController` → `ShoppingFlowService.exchange` |
| Payment-contact data exchange | `POST /whatsapp/flows/payment-data-exchange` | same encrypted controller → `PaymentContactFlowService.exchange` |
| Flow completion and chat messages | `POST /webhooks/whatsapp` | `WhatsAppWebhookController` parses `nfm_reply.response_json.value` → bot |
| Webhook verification | `GET /webhooks/whatsapp` | existing verification-token handler |
| Save merchant Flow/provider settings | authenticated `POST /api/integrations/connect` | existing integrations controller/service |
| Paynow callback | `POST /webhooks/payments/paynow` | existing gateway verification |
| PesePay callback | `POST /webhooks/payments/pesepay` | existing gateway verification |

The bot's `card` action launches `PaymentContactFlowService.launch`; `card_flow:<opaque token>` completion calls `checkoutFromCompletion`, then `ShoppingFlowService.cardCheckout` retrieves the existing owned pending payment link. Submitting a phone number never creates another payment transaction or marks an order paid.

## Remaining external setup

The source includes both JSON assets, encrypted endpoints, session persistence schema/SQL, bot routing, provider-link reuse and dashboard configuration. Applying database SQL, provisioning server secrets, registering Meta keys, creating/publishing the two Flows, setting real IDs, and confirming merchant card availability are deployment/account operations still required. Business-initiated Flow templates outside the 24-hour window are not included.

Primary references: [Meta's official Flow endpoint example](https://github.com/WhatsApp/WhatsApp-Flows-Tools/tree/main/examples/endpoint/nodejs/basic) and [Meta's publish-Flow API request](https://www.postman.com/meta/whatsapp-business-platform/request/wcidrlg/publish-flow).
