# Receive WhatsApp Payments via Payments Gateway

## Get Started

The following sequence diagram demonstrates the typical integration flow for Payments API. The steps highlighted in green are the key integration steps.

<div align="left"><figure><img src="https://3527970750-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M4sMxKjL6eJRvZn6jeG-887967055%2Fuploads%2F3aOpwHKJ0oYIVgGmsgo9%2Fimage.png?alt=media&#x26;token=96a47ce3-b9f8-4cee-a29c-aeeb723824c3" alt=""><figcaption></figcaption></figure></div>

The steps outlined below assume that the business already knows what the user is interested in through earlier conversations. The Payments API is a standalone API and hence can work with various messages such as [List Messages, Reply Buttons, Single or Multi-Product Messages.](https://docs.360dialog.com/docs/messaging/message-types/interactive)

## Integration Steps

{% stepper %}
{% step %}

#### **Send Order Details Interactive Message**

<figure><img src="https://3527970750-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M4sMxKjL6eJRvZn6jeG-887967055%2Fuploads%2F1cqWtWZBWJH6XDfeDl1H%2Fimage.png?alt=media&#x26;token=85603603-0ed0-4712-bc62-8d6c7619ede9" alt=""><figcaption></figcaption></figure>

<details>

<summary><strong>Elements Table</strong></summary>

<table><thead><tr><th width="162.16650390625">Object</th><th>Description</th></tr></thead><tbody><tr><td><p><code>status</code></p><p>string</p></td><td><p><strong>Required.</strong></p><p>Only supported value in the <code>order_details</code> message is <code>pending</code>.</p><p></p><p><em>In an <code>order_status</code> message, <code>status</code> can be: <code>pending</code>, <code>captured</code>, or <code>failed</code>.</em></p></td></tr><tr><td><p><code>items</code></p><p>object</p></td><td><p><strong>Required.</strong></p><p>An object with the list of items for this order, containing the following fields:</p><p></p><p><code>retailer_id</code> string</p><ul><li><strong>Required.</strong> Unique identifier for an item in the order.</li></ul><p><code>name</code> string</p><ul><li><strong>Required.</strong> The item’s name to be displayed to the user. Cannot exceed 60 characters</li></ul><p><code>amount</code> amount object with value and offset -- refer total amount field above</p><ul><li><strong>Required.</strong> The price per item</li></ul><p><code>sale_amount</code> amount object</p><ul><li><strong>Optional.</strong> The discounted price per item. This should be less than the original amount. If included, this field is used to calculate the subtotal amount</li></ul><p><code>quantity</code> integer</p><ul><li><strong>Required.</strong> The number of items in this order, this field cannot be decimal has to be integer.</li></ul><p><code>country_of_origin</code> string</p><ul><li><strong>Required</strong> if <code>catalog_id</code> is not present. The country of origin of the product</li></ul><p><code>importer_name</code> string</p><ul><li><strong>Required</strong> if <code>catalog_id</code> is not present. Name of the importer company</li></ul><p><code>importer_adress</code> string</p><ul><li><strong>Required</strong> if <code>catalog_id</code> is not present. Address of importer company</li></ul></td></tr><tr><td><p><code>subtotal</code></p><p>object</p></td><td><p><strong>Required.</strong></p><p>The value <strong>must be equal</strong> to sum of <code>order.amount.value</code> * <code>order.amount.quantity</code>. Refer to <code>total_amount</code> description for explanation of <code>offset</code> and <code>value</code> fields</p><p></p><p>The following fields are part of the <code>subtotal</code> object:</p><p></p><p><code>offset</code> integer</p><ul><li><strong>Required.</strong> Must be <code>100</code> for <code>INR</code></li></ul><p><code>value</code> integer</p><ul><li><strong>Required.</strong> Positive integer representing the amount value multiplied by offset. For example, ₹12.34 has value 1234</li></ul></td></tr><tr><td><p><code>tax</code></p><p>object</p></td><td><p><strong>Required.</strong></p><p>The tax information for this order which contains the following fields:</p><p></p><p><code>offset</code> integer</p><ul><li><strong>Required.</strong> Must be <code>100</code> for <code>INR</code></li></ul><p><code>value</code> integer</p><ul><li><strong>Required.</strong> Positive integer representing the amount value multiplied by offset. For example, ₹12.34 has value 1234</li></ul><p><code>description</code> string</p><ul><li><strong>Optional.</strong> Max character limit is 60 characters</li></ul></td></tr><tr><td><p><code>shipping</code></p><p>object</p></td><td><p><strong>Optional.</strong></p><p>The shipping cost of the order. The object contains the following fields:</p><p><code>offset</code> integer</p><ul><li><strong>Required.</strong> Must be <code>100</code> for <code>INR</code></li></ul><p><code>value</code> integer</p><ul><li><strong>Required.</strong> Positive integer representing the amount value multiplied by offset. For example, ₹12.34 has value 1234</li></ul><p><code>description</code> string</p><ul><li><strong>Optional.</strong> Max character limit is 60 characters</li></ul></td></tr><tr><td><p><code>discount</code></p><p>object</p></td><td><p><strong>Optional.</strong></p><p>The discount for the order. The object contains the following fields:</p><p><code>offset</code> integer</p><ul><li><strong>Required.</strong> Must be <code>100</code> for <code>INR</code></li></ul><p><code>value</code> integer</p><ul><li><strong>Required.</strong> Positive integer representing the amount value multiplied by offset. For example, ₹12.34 has value 1234</li></ul><p><code>description</code> string</p><ul><li><strong>Optional.</strong> Max character limit is 60 characters</li></ul><p><code>discount_program_name</code> string</p><ul><li><strong>Optional.</strong> Text used for defining incentivised orders. If order is incentivised, the merchant needs to define this information. Max character limit is 60 characters</li></ul></td></tr><tr><td><p><code>catalog_id</code></p><p>object</p></td><td><p><strong>Optional.</strong></p><p>Unique identifier of the Facebook catalog being used by the business.</p><p>If you do not provide this field, you must provide the following fields inside the items object: <code>country_of_origin</code>, <code>importer_name</code>, and <code>importer_address</code></p></td></tr><tr><td><p><code>expiration</code></p><p>object</p></td><td><p><strong>Optional.</strong></p><p>Expiration for that order. Business must define the following fields inside this object:</p><p><code>timestamp</code> string – UTC timestamp in seconds of time when order should expire. Minimum threshold is 300 seconds</p><p><code>description</code> string – Text explanation for expiration. Max character limit is 120 characters</p></td></tr></tbody></table>

</details>

<details>

<summary>The <code>parameters</code> value is a stringified JSON object. By the end, the interactive object should look something like this for a catalog-based integration:</summary>

```json
{
  "interactive": {
    "type": "order_details",
    "header": {
      "type": "image",
      "image": {
        "link": "http(s)://the-url",
        "provider": {
          "name": "provider-name"
        }
      }
    },
    "body": {
      "text": "your-text-body-content"
    },
    "footer": {
      "text": "your-text-footer-content"
    },
    "action": {
      "name": "review_and_pay",
      "parameters": {
        "reference_id": "reference-id-value",
        "type": "digital-goods",
        "payment_settings": [
          {
            "type": "payment_gateway",
            "payment_gateway": {
              "type": "razorpay",
              "configuration_name": "payment-config-id",
      	      "razorpay": {
                "receipt": "receipt-value",
                "notes": {
                  "key1": "value1"
                }
              }
            }
          }
        ],
        "currency": "INR",
        "total_amount": {
          "value": 21000,
          "offset": 100
        },
        "order": {
          "status": "pending",
          "catalog_id": "the-catalog_id",
          "expiration": {
            "timestamp": "utc_timestamp_in_seconds",
            "description": "cancellation-explanation"
          },
          "items": [
            {
              "retailer_id": "1234567",
              "name": "Product name, for example bread",
              "amount": {
                "value": 10000,
                "offset": 100
              },
              "quantity": 1,
              "sale_amount": {
                "value": 100,
                "offset": 100
              }
            }
          ],
          "subtotal": {
            "value": 20000,
            "offset": 100
          },
          "tax": {
            "value": 1000,
            "offset": 100,
            "description": "optional_text"
          },
          "shipping": {
            "value": 1000,
            "offset": 100,
            "description": "optional_text"
          },
          "discount": {
            "value": 1000,
            "offset": 100,
            "description": "optional_text",
            "discount_program_name": "optional_text"
          }
        }
      }
    }
  }
}
```

</details>

<details>

<summary>The <code>parameters</code> value is a stringified JSON object. For a non-catalog based integration i.e. when catalog-id is not present, an example payload looks as follows:</summary>

```json
{
  "interactive": {
    "type": "order_details",
    "header": {
      "type": "image",
      "image": {
        "id": "your-media-id"
      }
    },
    "body": {
      "text": "your-text-body-content"
    },
    "footer": {
      "text": "your-text-footer-content"
    },
    "action": {
      "name": "review_and_pay",
      "parameters": {
        "reference_id": "reference-id-value",
        "type": "digital-goods",
        "payment_settings": [
          {
            "type": "payment_gateway",
            "payment_gateway": {
              "type": "razorpay",
              "configuration_name": "payment-config-id"
            }
          }
        ],
        "currency": "INR",
        "total_amount": {
          "value": 21000,
          "offset": 100
        },
        "order": {
          "status": "pending",
          "expiration": {
            "timestamp": "utc_timestamp_in_seconds",
            "description": "cancellation-explanation"
          },
          "items": [
            {
              "retailer_id": "1234567",
              "name": "Product name, for example bread",
              "amount": {
                "value": 10000,
                "offset": 100
              },
              "quantity": 1,
              "sale_amount": {
                "value": 100,
                "offset": 100
              },
              "country_of_origin": "country-of-origin",
              "importer_name": "name-of-importer-business",
              "importer_address": {
                "address_line1": "B8/733 nand nagri",
                "address_line2": "police station",
                "city": "East Delhi",
                "zone_code": "DL",
                "postal_code": "110093",
                "country_code": "IN"
              }
            },
            {
              "retailer_id": "14325",
              "name": "Product name, for example bread",
              "amount": {
                "value": 10000,
                "offset": 100
              },
              "quantity": 1,
              "sale_amount": {
                "value": 100,
                "offset": 100
              },
              "country_of_origin": "country-of-origin",
              "importer_name": "name-of-importer-business",
              "importer_address": {
                "address_line1": "B8/733 nand nagri",
                "address_line2": "police station",
                "city": "East Delhi",
                "zone_code": "DL",
                "postal_code": "110093",
                "country_code": "IN"
              }
            }
          ],
          "subtotal": {
            "value": 20000,
            "offset": 100
          },
          "tax": {
            "value": 1000,
            "offset": 100,
            "description": "optional_text"
          },
          "shipping": {
            "value": 1000,
            "offset": 100,
            "description": "optional_text"
          },
          "discount": {
            "value": 1000,
            "offset": 100,
            "description": "optional_text",
            "discount_program_name": "optional_text"
          }
        }
      }
    }
  }
}
```

</details>

{% endstep %}

{% step %}

#### Add Common Message Parameters <a href="#step-3--add-common-message-parameters" id="step-3--add-common-message-parameters"></a>

Once the interactive object is complete, append the other parameters that make a message: `recipient_type`, `to`, and `type`. Remember to set the `type` to `interactive`.

```json
{
   "messaging_product": "whatsapp",
   "recipient_type": "individual",
   "to": "PHONE_NUMBER",
   "type": "interactive",
   "interactive": {
     // interactive object here     
   }
 }
```

These [are parameters common to Cloud API message ](https://developers.facebook.com/docs/whatsapp/cloud-api/reference/messages#messages)types
{% endstep %}

{% step %}

#### Make a POST Call to /messages <a href="#step-4-make-a-post-call-to-messages-endpoint" id="step-4-make-a-post-call-to-messages-endpoint"></a>

Make a POST call to the `/messages` endpoint with the JSON object you have assembled. If your message is sent successfully, you get the following response:

```json
{
  "messaging_product": "whatsapp",
  "contacts": [ {
      "input": "[PHONE_NUMBER_ID]",
      "wa_id": "[PHONE-NUMBER_ID]"
  } ],
  "messages": [ {
      "id": "wamid.HBgLMTY1MDUwNzY1MjAVAgARGBI5QTNDQTVCM0Q0Q0Q2RTY3RTcA"
  } ]
}
```

For all errors that can be returned and guidance on how to handle them, see [WhatsApp, Error Codes](https://developers.facebook.com/docs/whatsapp/cloud-api/support/error-codes).

<details>

<summary>Product Experience</summary>

The customer receives an `order_details` message similar to the one below (left). When they click on "**Review and Pay**", it opens up the order details screen as shown below (middle).&#x20;

Customer can then pay for their order using "**Continue**" button that opens up a bottom sheet with the payment options (right).

<figure><img src="https://3527970750-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M4sMxKjL6eJRvZn6jeG-887967055%2Fuploads%2FIKBUepPF3O2QUOLYWkvr%2Fimage.png?alt=media&#x26;token=35bee756-3aa2-40ec-a2bb-d03d6e1f6aa5" alt=""><figcaption></figcaption></figure>

</details>

After receiving the payment status change notification, or at any time, the business can look up the status of the payment or transaction. To do that, businesses must make a **`GET`** call to **/v1/payments/`{payment-config-id}`/{`reference_ID`}.**

Businesses should expect a response in the same HTTP session (not in a webhook notification). `payment_configuration` and `reference_id` must be the same as that provided in the initial `order_details` message.
{% endstep %}

{% step %}

#### Responses

&#x20;A response can return the following values:

| Status     | Description                                                                         |
| ---------- | ----------------------------------------------------------------------------------- |
| `new`      | The partner sent an `order_details` message but the user didn’t start a payment yet |
| `pending`  | The user started the payment process and the payment object was created             |
| `captured` | The payment was captured                                                            |
| `canceled` | The payment was canceled by the user and no retry is possible                       |
| `failed`   | The payment attempt failed but the user can retry                                   |

A successful response looks like this:

```json
{
  "errors": [{
    "code": "<error code>",
    "title": "<title for error>",
    "details": "<details description of error>"
  }]
}
```

{% hint style="info" %}
For all errors that can be returned and guidance on how to handle them, see [Errors while Messaging](https://docs.360dialog.com/docs/support/api-error-message-list).
{% endhint %}

```json
{
  "recipient_type": "individual",
  "to": "whatsapp-id",
  "type": "interactive",
  "interactive": {
    "type": "order_status",
    "body": {
      "text": "your-text-body-content"
    },
    "action": {
      "name": "review_order",
      "parameters": {
        "reference_id": "reference-id-value",
        "order": {
          "status": "processing | partially_shipped | shipped | completed | canceled",
          "description": "optional-text"
        }
      }
    }
  }
}
```

<details>

<summary>The following table describes the fields in the <code>order_status</code> interactive message:</summary>

<table><thead><tr><th width="216.88720703125">Object</th><th>Description</th></tr></thead><tbody><tr><td><p><code>type</code></p><p>string</p></td><td><strong>Required.</strong> Must be "order_status"</td></tr><tr><td><p><code>body</code></p><p>object</p></td><td><p><strong>Required.</strong></p><p>An object with the body of the message. The object contains the following field:</p><p><code>text</code> string</p><ul><li><strong>Required</strong> if <code>body</code> is present. The content of the message. Emojis and markdown are supported. Maximum length is 1024 characters</li></ul></td></tr><tr><td><p><code>footer</code></p><p>object</p></td><td><p><strong>Optional.</strong></p><p>An object with the footer of the message. The object contains the following field:</p><p><code>text</code> string</p><ul><li><strong>Required</strong> if <code>footer</code> is present. The footer content. Emojis, markdown, and links are supported. Maximum length is 60 characters</li></ul></td></tr><tr><td><p><code>action</code></p><p>object</p></td><td><p><strong>Required.</strong></p><p>An action object you want the user to perform after reading the message. This action object contains the following fields:</p><p><code>name</code> string</p><ul><li><strong>Required</strong>. Must be "review_order".</li></ul><p><code>parameters</code> object</p><ul><li>See<a href="#step-3--add-common-message-parameters"> Parameters Object</a> for information</li></ul></td></tr></tbody></table>

</details>

<details>

<summary>The <code>parameters</code> object contains the following fields:</summary>

| Value                                          | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <p><code>reference\_id</code></p><p>string</p> | <p><strong>Required.</strong></p><p>The ID sent by the business in the <code>order\_details</code> message</p>                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| <p><code>order</code></p><p>object</p>         | <p><strong>Required.</strong> This object contains the following fields:</p><p><code>status</code> string \* <strong>Required.</strong> The new order <code>status</code>. Must be one of <code>processing</code>, <code>partially\_shipped</code>, <code>shipped</code>, <code>completed</code>, <code>canceled</code>.</p><p><code>description</code> string \* <strong>Optional.</strong> Text for sharing status related information in <code>order\_details</code>. Could be useful while sending cancellation. Max character limit is 120 characters.</p> |

</details>

<details>

<summary><code>order_status</code> message introduces two new errors that are summarized below:</summary>

| Error Code                         | Description                                                     |
| ---------------------------------- | --------------------------------------------------------------- |
| `2046` - Invalid status transition | The order status transition is not allowed.                     |
| `2047` - Cannot cancel order       | Cannot cancel the order since the user has already paid for it. |

</details>

<details>

<summary><strong>Product Experience</strong></summary>

Customers receive each `order_status` update as a separate message in their chat thread, that references their original `order_details` message as shown below (left). The order details page always displays the latest valid status communicated to the customer using the `order_status` message as shown below (right).

<div><figure><img src="https://3527970750-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M4sMxKjL6eJRvZn6jeG-887967055%2Fuploads%2FZI0e9embEOhzCDPxQHj8%2FIMAGE1.PNG?alt=media&#x26;token=9ff35782-7552-48a8-ba19-38b2f7bc83c8" alt=""><figcaption></figcaption></figure> <figure><img src="https://3527970750-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M4sMxKjL6eJRvZn6jeG-887967055%2Fuploads%2FNbdX44gPrZVKf0vrNU32%2FIMAGE2.PNG?alt=media&#x26;token=6c7fded4-81e2-4b06-b598-ac06453f454b" alt=""><figcaption></figcaption></figure></div>

</details>
{% endstep %}
{% endstepper %}

### **Supported Order Status and Transitions**

Currently we support the following order status values:

| Value               | Description                                                                                                                                                                                               |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pending`           | User has not successfully paid yet                                                                                                                                                                        |
| `processing`        | User payment authorized, merchant/partner is fulfilling the order, performing service, etc.                                                                                                               |
| `partially-shipped` | A portion of the products in the order have been shipped by the merchant                                                                                                                                  |
| `shipped`           | All the products in the order have been shipped by the merchant                                                                                                                                           |
| `completed`         | The order is completed and no further action is expected from the user or the partner/merchant                                                                                                            |
| `canceled`          | The business would like to cancel the `order_details` message for the order/invoice. The status update will fail if there is already a `successful` or `pending` payment for this `order_details` message |

Order status transitions are restricted for consistency of consumer experience. Allowed status transitions are summarized below:

* Initial status of an order is always `pending`, which is sent in `order_details` message.
* `canceled` and `completed` are terminal status and cannot be updated to any other status.
* `pending` can transition to any of the other statuses including `processing`, `shipped`, `partially-shipped`.
* `processing`, `shipped` and `partially-shipped` are equivalent statuses and can transition between one another or to one of the terminal statuses.

<figure><img src="https://3527970750-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M4sMxKjL6eJRvZn6jeG-887967055%2Fuploads%2FfVy46mHyAGWE3UIXkIgD%2Fimage.png?alt=media&#x26;token=a315d4e8-be64-4a12-9ef7-7cdcc9ace577" alt=""><figcaption></figcaption></figure>

Upon sending an `order_status` message with an invalid transition, you will receive an error webhook with the error code `2046` and message "*New order status was not correctly transitioned.*"

### **Canceling an Order**

An order can be `canceled` by sending an `order_status` message with the status `canceled`.&#x20;

The customer cannot pay for an order that is canceled. The customer receives an `order_status` message (left) and order details page is updated to show that the order is canceled and the "Secure Checkout" button removed (right). The *optional* text shown below "Order canceled" on the order details page can be specified using the `description` field in the `order_status` message.

An order can be canceled only if the user has not already paid for the order. If the user has paid and you send an `order_status` message with `canceled` status, you will receive an error webhook with error code `2047` and message "*Could not change order status to 'canceled'*".

### Reconcile Payments

Please note that neither 360dialog nor WhatsApp supports payment reconciliations. Businesses should use their payment gateway account to reconcile the payments using the `reference_id` provided in the `order_details` messages and the `id` of the transactions returned as part of the payment lookup query.

## Security Considerations for Payments API

{% hint style="info" %}
Review our [WABA Security](https://app.gitbook.com/s/uyAl2S0lSHJaNDXJHo7A/onboarding/integration-best-practices/architecture-and-security) documentation.&#x20;
{% endhint %}

* Businesses should not rely solely on the status of the transaction provided in the webhook and must use payment lookup API to retrieve the statuses directly from WhatsApp.
* To ensure secure financial processing, payment configurations such as the business’ VPA shared by all WhatsApp phone numbers **must belong to the same Meta business portfolio**. If you wish to separate payments for different phone numbers, then additional Meta business portfolios must be created.&#x20;
* Businesses must always sanitize/validate the data in the API responses or webhooks to protect against SSRF attacks.
