Webhooks

Webhooks are events that notify you when a transaction changes its status. For example, they can let you know the moment a payment is authorized, refunded, succeeds or fails. This is particularly useful for asynchronous payment flows, in which a transaction may first return a status of Pending before returning a final status indicating the success or failure of the transaction request.

Webhooks Version

The latest Webhook API version is 1.3.0. The webhook data that is returned in the latest version reflects the response data structure of the Payments API. Refer to the API Changelog for any changes in the Payments API response data structure.

Whitelisting PaymentsOS Server IPs

All our webhook requests originate from a set of IP addresses. If your server handing webhook requests is behind a firewall, you will need to whitelist the following set of IP addresses to ensure that requests succeed:

  • 52.29.158.20
  • 3.124.65.163
  • 52.59.7.16

Webhook Events Structure

Webhook events returned in version 1.2.0 and higher have a different structure than the events returned in older Webhook versions. Refer to the topic of your choice, depending on the Webhook version you are using:

For an overview of the latest changes, refer to the API Changelog.

Creating Webhooks

To begin receiving Webhook notifications, do the following:

  1. Login to the PaymentsOS Control Center and open the Webhooks configuration page (Accounts > Webhooks).
  2. Select the test or live relevant environment.
  3. Enter an endpoint (the HTTPS URL), that Webhooks notifications should be sent to.
  4. Select the events to track from the Payment Event Alerts list.
  5. Select the business units to associate with the endpoint from the Associated Business Units list.

Receiving Webhook Notifications

PaymentsOS will send Webhook notifications via HTTP POST requests to the URL endpoints that you defined.

If the system doesn’t receive a “Request received successfully” status code response (2xx HTTP), then it will continue resending the Webhook notification for 24 hours, using an exponential retry basis.

Manually Resending the Latest Webhook Event

There may be cases in which you’d want to manually resend the latest webhook event for a specific transaction. Say, for instance, there was a technical glitch in your listener. You then fix that glitch, after which you manually re-send the latest webhook event to get updated information about the status of the transaction.

To resend a webhook event, simply invoke through the Resend the Latest Webhook Event API request. In the request body, pass a resource_type with a value of PAYMENT and a resource_ids array with the identifier of the payment request (represented by the Payment Object) for which the latest webhook event should be sent. Just beware that even though resource_ids is an array, you can only pass a single identifier in the array (we’ll be adding the option for passing multiple identifiers in later versions). Here’s a sample request body:

{
   "resource_type": "PAYMENT",
   "resource_ids": ["0047c4ef-f658-4e6d-a040-5882e2285f34"]
}

Webhook Notifications in Versions 1.2.0 and Higher

This section explains the structure of Webhook notifications returned in Webhook API versions 1.2.0 and higher. The topic also outlines the steps you need to follow if you want to validate the integrity of the Webhook data you received.

Webhook Header

A Webhook header includes the following fields.

Header Field Description
signature string This is a Hash-256 (SHA-256) of specific fields contained in the Webhook response. You can use this hash code to validate the integrity of the Webhook data you received. For more information, see Validating the Integrity of the Webhook Data.
event-type string The type of resource that triggered the event.
For example, payment.authorization.create
version string Version of the Webhooks configuration.
x-payments-os-env string PaymentsOS environment header, ‘live’ or ‘test’.
x-zooz-request-id string The ID of the original request that triggered the webhook event.

Here’s an example of a Webhook event header:

x-zooz-request-id: 4a86867f-f2bd-4860-a6c8-10a5ab890f03
version: 1.2.0
x-payments-os-env: test
event-type: payment.charge.update
signature: sig1=3bc4d08447e55a704325db8748582eaefd5ebed0157ec1e587cae4df8f5cd74e

Webhook Body

A Webhook body includes the following fields.

Attribute Name Description
id string The Webhook id. This id is unique per Webhook and can be used to validate that the request is unique (idempotency validation). For more information, see Handling Multiple Webhook Requests for the Same Event.
created timestamp The date and time the event was created.
payment_id UUID The payment id.
account_id UUID The merchant account related to the event.
app_id string The Business Unit related to the event.
data object The full transaction resource that initiated the event.

Here are examples of Webhook event bodies, for common webhook event types:

    {
        "id": "13344450-77e9-45b6-9bbe-88fff8c451e5-2018-10-03T04:58:35.385Z-83233f6e-767f-4f55-9d8f-448019e90fbf",
        "created": "2018-10-03T04:58:35.385Z",
        "account_id": "961c3ded-d539-4b5f-8950-3de93570e988",
        "app_id": "com.zooz.docapp",
        "payment_id": "13344450-77e9-45b6-9bbe-88fff8c451e5",
        "data": {
            "id": "cb615c8f-319d-4349-a276-f4ecc7d770d3",
            "created": "1538542712789",
            "payment_method": {
                "type": "tokenized",
                "token": "9c7bc2c6-47fe-4d2a-ac20-23847574ae3e",
                "token_type": "credit_card",
                "holder_name": "John Mark",
                "expiration_date": "10/2029",
                "last_4_digits": "0077",
                "pass_luhn_validation": true,
                "bin_number": "400000",
                "vendor": "VISA",
                "issuer": "",
                "card_type": "CREDIT",
                "level": "",
                "country_code": "USA",
                "created": "1538542703826",
                "billing_address": {
                    "country": "USA",
                    "state": "NY",
                    "city": "NYC",
                    "line1": "fifth avenue 10th"
                }
            },
            "result": {
                "status": "Succeed"
            },
            "provider_data": {
                "provider_name": "MockProcessor",
                "response_code": "0",
                "description": "Captured.",
                "raw_response": "{\"OID\":\"13344450-77e9-45b6-9bbe-88fff8c451e5\",\"AUTH_CODE\":\"13344\",\"RETURN_MESSAGE\":\"Captured.\",\"TRANSID\":\"187_961c3ded-d539-4b5f-8950-3de93570e988_Charge-TargetPolicy\",\"STATUS\":\"SUCCESS\",\"AMOUNT\":\"40.97\",\"DATE\":\"1538542715367\",\"RETURN_CODE\":\"0\",\"CURRENCY\":\"EUR\"}",
                "authorization_code": "13344",
                "transaction_id": "187_961c3ded-d539-4b5f-8950-3de93570e988_Charge-TargetPolicy",
                "external_id": "13344450-77e9-45b6-9bbe-88fff8c451e5"
            },
            "amount": 4097
        }
    }
  
    {
        "id": "66ebc442-bf8f-42c6-886b-faee8323aad2-2018-10-03T05:14:17.196Z-83233f6e-767f-4f55-9d8f-448019e90fbf",
        "created": "2018-10-03T05:14:17.196Z",
        "account_id": "961c3ded-d539-4b5f-8950-3de93570e988",
        "app_id": "com.zooz.docapp",
        "payment_id": "66ebc442-bf8f-42c6-886b-faee8323aad2",
        "data": {
            "id": "974cfd3e-a461-4efd-804c-9b39104f56f9",
            "created": "1538543654532",
            "result": {
                "status": "Succeed"
            },
            "amount": 2000,
            "provider_data": {
                "provider_name": "MockProcessor",
                "response_code": "0",
                "description": "Captured.",
                "raw_response": "{\"OID\":\"66ebc442-bf8f-42c6-886b-faee8323aad2\",\"AUTH_CODE\":\"66ebc\",\"RETURN_MESSAGE\":\"Captured.\",\"TRANSID\":\"988_961c3ded-d539-4b5f-8950-3de93570e988_Capture\",\"STATUS\":\"SUCCESS\",\"AMOUNT\":\"20\",\"DATE\":\"1538543657182\",\"RETURN_CODE\":\"0\",\"CURRENCY\":\"USD\"}",
                "authorization_code": "66ebc",
                "transaction_id": "988_961c3ded-d539-4b5f-8950-3de93570e988_Capture",
                "external_id": "66ebc442-bf8f-42c6-886b-faee8323aad2"
            }
        }
    }
  
    {
        "id": "a3006729-09d7-41e3-9c2f-8fa0cd9d6fdc-2018-10-03T05:22:45.610Z-83233f6e-767f-4f55-9d8f-448019e90fbf",
        "created": "2018-10-03T05:22:45.610Z",
        "account_id": "961c3ded-d539-4b5f-8950-3de93570e988",
        "app_id": "com.zooz.docapp",
        "payment_id": "a3006729-09d7-41e3-9c2f-8fa0cd9d6fdc",
        "data": {
            "id": "df6cfb45-5e03-4815-b777-3124f7a86a43",
            "created": "1538544162818",
            "result": {
                "status": "Succeed"
            },
            "provider_data": {
                "provider_name": "MockProcessor",
                "response_code": "0",
                "description": "Refunded.",
                "raw_response": "{\"OID\":\"a3006729-09d7-41e3-9c2f-8fa0cd9d6fdc\",\"AUTH_CODE\":\"a3006\",\"RETURN_MESSAGE\":\"Refunded.\",\"TRANSID\":\"67_961c3ded-d539-4b5f-8950-3de93570e988_Refund\",\"STATUS\":\"SUCCESS\",\"AMOUNT\":\"20\",\"DATE\":\"1538544165606\",\"RETURN_CODE\":\"0\",\"CURRENCY\":\"USD\"}",
                "authorization_code": "a3006",
                "transaction_id": "67_961c3ded-d539-4b5f-8950-3de93570e988_Refund",
                "external_id": "a3006729-09d7-41e3-9c2f-8fa0cd9d6fdc"
            },
            "amount": 2000
        }
    }
  
  {
    {
        "id": "2eea735d-3c37-4f30-ab54-7ccea9a3b1bd-2018-10-03T05:26:03.793Z-83233f6e-767f-4f55-9d8f-448019e90fbf",
        "created": "2018-10-03T05:26:03.793Z",
        "account_id": "961c3ded-d539-4b5f-8950-3de93570e988",
        "app_id": "com.zooz.docapp",
        "payment_id": "2eea735d-3c37-4f30-ab54-7ccea9a3b1bd",
        "data": {
            "id": "7b746d9d-073a-4eb0-a067-b14d952211e2",
            "created": "1538544360751",
            "result": {
                "status": "Succeed"
            },
            "provider_data": {
                "provider_name": "MockProcessor",
                "response_code": "0",
                "description": "Canceled.",
                "raw_response": "{\"OID\":\"2eea735d-3c37-4f30-ab54-7ccea9a3b1bd\",\"AUTH_CODE\":\"2eea7\",\"RETURN_MESSAGE\":\"Canceled.\",\"TRANSID\":\"322_961c3ded-d539-4b5f-8950-3de93570e988_Void\",\"STATUS\":\"SUCCESS\",\"AMOUNT\":\"20\",\"DATE\":\"1538544363785\",\"RETURN_CODE\":\"0\",\"CURRENCY\":\"USD\"}",
                "authorization_code": "2eea7",
                "transaction_id": "322_961c3ded-d539-4b5f-8950-3de93570e988_Void",
                "external_id": "2eea735d-3c37-4f30-ab54-7ccea9a3b1bd"
            }
    }
}
  

Understanding the Structure of the body.data Object

The structure of the data object is the same as the structure you receive in the response of a GET request made to the specific resource endpoint. For instance, the structure of the data object returned in a payment.charge.update event will be the same as the response body of a GET Charge request. Likewise, the data object returned in a payment.payment.update event will be the same as the response body of a GET Payment request.

Use the event-type value (in the Webhook event header) to ensure you parse the correct data object structure. Refer to the API reference for a sample response.

Excluding Fields from the body.data Object

The data object returned in the webhook body includes the full transaction resource that triggered the webhook event. There are cases, however, where you may not want to receive all data included in a transaction resource. This could be for architectural reasons (to limit the amount of data you receive) or based on security considerations (you may not be allowed to expose all data), just to name a couple. Whatever the case may be, you can easily exclude specific resource data by ‘blacklisting’ the resource fields you don’t want to receive in the webhook event. To do so, edit your Webhooks configuration and click Apply Filter next to the payment event for which you want to exclude the resource fields.

Validating the Integrity of the Webhook Data

If you’re worried about the integrity of the Webhook data you received (or want to be reassured that the data was sent to you by PaymentsOS), you can get assurance that your data hasn’t been monkeyed with. You just have to compare two hash values:

  • The hash value returned in the signature field of the Webhook header.

  • A HMAC-SHA256 hash value you generate using part of the fields returned in the Webhook event, as well as your app’s private key.

Generating the HMAC-SHA256 hash value is easy. First concatenate the following fields from the Webhook data into a single string. All fields are returned in the Webhook body, with the exception of the event-type which is returned in the header. Just make sure to add the fields exactly in the order listed and separate each field in the string with a comma. If the field doesn’t exist, replace it with an empty string.

  • event-type (this field is returned in the Webhook header)

  • id

  • account_id

  • payment_id

  • created

  • app_id

  • data.id

  • data.result.status

  • data.result.category

  • data.result.sub_category

  • data.provider_data.response_code

  • data.reconciliation_id

  • data.amount

  • data.currency

Here’s an example of a concatenated string. Notice that empty strings (in the event of a missing field) have been replaced with a ‘,’ (comma). If the missing field is the last one in the sequence, you should include the comma as well.

payment.charge.update,8d3f9e6a-d89b-48bd-9d68-07e1bb582687-2018-09-05T06:44:35.484Z-83233f6e-767f-4f55-9d8f-448019e90fbf,961c3ded-d539-4b5f-8950-3de93570e988,8d3f9e6a-d89b-48bd-9d68-07e1bb582687,2018-09-05T06:44:35.484Z,com.zooz.docapp,557a4e32-d2e9-495a-9a0b-f2a18c39d91b,Succeed,,,0,,4097,

Now use the string to generate the HMAC-SHA256 hash value, using your app’s private key. If the value matches the signature value in the Webhook header, you can rest assured that the data has not been tempered with. For a quick test, you can use an online HMAC Generator / Tester Tool.

Handling Multiple Webhook Requests for the Same Event

There are cases, such as connectivity issues, that may cause the same request to be sent multiple times for the same event. If you receive the same request multiple times, you can simply ignore the duplicates. Bear in mind that you must still send us a 2xx (success) response so that we will stop resending the webhook event.

You can use the Webhook id (returned in the Webhook body) to validate that a request is unique (idempotency validation) and identify any duplicates.

Webhook Notifications in Versions 1.0.1 and Lower

This section explains the structure of Webhook notifications returned in Webhook API version 1.0.1.

Webhook Header

A Webhook header includes the following fields.

Header Name Description
digest string This is a Hash-256 (SHA-256) of the body.
version string Version of the Webhooks configuration.
x-payments-os-env string PaymentsOS environment header, ‘live’ or ‘test’.
x-zooz-request-id string The ID of the original request that triggered the webhook event.

Webhook Body

A Webhook body includes the following fields.

Attribute Name Description
event_type string The type of resource that triggered event.
For example, payment.authorization.create
created timestamp The date and time the event was created.
account_id UUID The merchant account related to the event.
app_id string The Business Unit related to the event.
resource object The resource object related to the event.
⇒ id UUID The payment id
⇒ action_id UUID The id of the transaction (such as the refund id, or the charge id.)
⇒ href UUID The href link to the associated resource.
⇒ order_id string Identifier of the order.

Here’s an example of a Webhook event body:

{
    "event_type": "payment.charge.create",
    "created": "2018-02-06T10:10:05.466Z",
    "account_id": "961c3ded-d539-4b5f-8950-3de93570e988",
    "app-id": "com.zooz.docapp",
    "resource": {
        "id": "633f5479-7eea-496f-bdc1-629b71d3062a",
        "action_id": "adb3e112-8f35-46f5-8ed1-66dab4f9438c",
        "href": "https://api.paymentsos.com/payments/633f5479-7eea-496f-bdc1-629b71d3062a/charges/adb3e112-8f35-46f5-8ed1-66dab4f9438c",
        "order_id": "myappid"
    }
}

Retrieving the Transaction Status

When receiving the webhook, retrieve the status of the transaction, including any additional details, by invoking a GET request on the resource.href resource. Building on the example above, here’s how you can retrieve the status of the charge:

GET https://api.paymentsos.com/payments/6ec2cd4d-9ffb-4ab3-a35a-870f7ec8a803/charges/fe25daca-1149-42e1-8576-d7b52ca9ded4
Last modified January 4, 2022