WhatsApp Payments API (Brazil): Order Details, Order Status, and Why WhatsApp Never Reconciles a Single Rupee or Real

WhatsApp now lets a business send an itemized order bubble straight inside the chat and collect payment without the buyer ever leaving the app — but the API is region-locked, field-strict, and deliberately blind to whether the money actually arrived.

The WhatsApp Cloud API Payments feature turns a normal chat thread into a checkout flow. A business sends an order_details interactive message with a header image, an itemized list, and a “Review and Pay” button. The buyer pays inside WhatsApp using Pix, a payment link, Boleto, or one-click card checkout. The business then sends an order_status message back into the same thread to update the order bubble to processing, then paid. It looks like a native checkout — but the payment rail underneath is entirely separate from WhatsApp, and that separation is where most integrations get into trouble.

Region lock: this is not a global WhatsApp feature. The Payments API documented here is Brazil-specific — currency must be BRL, the amount offset must be 100, and payment_type must be br. A separate India variant exists with its own mechanics (UPI, dynamic VPA, different payment gateways). The two are not interchangeable, and a business outside these markets does not have access to either.

What the order_details message actually contains

Header

Optional image. If left out, the API defaults to the first product’s catalog image — a silent fallback that can put the wrong photo on an order bubble if a business assumes the header is required.

Body

Required text, capped at 1024 characters, supports emoji and light markdown.

Footer

Optional, capped at 60 characters.

Action

Required. Must carry the fixed name review_and_pay plus a parameters object — reference_id, currency, total_amount, and an optional itemized order object.

reference_id is the one field that quietly breaks integrations

Every order_details message needs a reference_id: letters, numbers, underscores, dashes, and dots only, capped at 60 characters, and it must be unique per message for that business. Resend a message for the same order without changing it and the two messages collide. Meta’s own guidance is to append a sequence number on any resend — a detail that is easy to skip on a first build and hard to notice until an order silently shows the wrong status because two messages are pointing at the same ID.

The math has to balance exactly

FieldRule
subtotal.valueMust exactly equal the sum of each item’s amount (or sale_amount) times quantity
total_amount.valueMust exactly equal subtotal plus tax plus shipping minus discount
offsetMust be 100 for BRL — a fixed-point, cents-style encoding that trips up first-time integrations
expiration.timestampMust be at least 300 seconds in the future
order.status at creationCan only be pending — every other status is set later via a separate order_status message

A mismatched subtotal or total is the kind of error that looks like a rounding bug in testing and becomes a rejected order in production.

order_status: pushing updates back into the same thread

Once the buyer pays, the business sends an order_status message to update the same order bubble — order.status moves through pending, processing, partially_shipped, shipped, completed, or canceled, and payment.status separately tracks pending, captured, or failed. Canceling an order that already has a successful or pending payment against it fails outright — the API returns a dedicated cancellation-failure error rather than silently allowing it.

The Order Details Template: the same object, wrapped in Meta’s review process

There is also a template version — order_details_template — built as a normal WhatsApp message template with a fixed ORDER_DETAILS button type. It goes through the standard template category, review, and approval flow (UTILITY or MARKETING), and the header can carry a PDF invoice via a document attachment. The underlying order fields and character limits are identical to the direct interactive message; the tradeoff is template-review turnaround in exchange for a reusable, pre-approved order format.

What WhatsApp will never do for you

Meta’s own documentation states it plainly: WhatsApp does not support payment reconciliation. The business is responsible for reconciling each payment with its own payment service provider using the reference_id from the order. WhatsApp only carries the message thread and the order_status updates — the actual money movement, settlement, and reconciliation logic live entirely outside WhatsApp, in whatever PSP integration the business builds.

Where implementations commonly stall

Assuming the Payments API is global

It is scoped to specific markets. Building against the Brazil fields for an India rollout, or vice versa, does not work — the currency, offset, and payment_type constraints are market-specific.

Getting the offset and rounding wrong

The fixed-point cents-style encoding and the exact subtotal/total match requirement reject orders that look correct at a glance.

Reusing a reference_id on resend

No sequence suffix on a resent order_details message means two messages collide on the same ID.

Building reconciliation as an afterthought

Since WhatsApp never confirms payment on its own, a business needs its PSP webhook and reference_id matching logic in place before launch, not after the first disputed order.