Airwallex logo

Notifications and Webhooks

The notifications or Webhooks enable you to receive asynchronous notifications of the events that occur on the Airwallex payment acceptance platform.

Subscription

You can register webhook URLs for Airwallex to notify any time an event happens in your account and any account you were authorized to access. When one of those events is triggered, Airwallex will send a HTTP POST payload to the webhook's configured URL.

webhook_subscription

Event types

The table below provides you the different types of events that are available.

Event NameDescription
payment_intent.createdA payment intent has been created.
payment_intent.requires_payment_methodThis payment intent requires payment method to proceed.
payment_intent.cancelledThe payment intent has been cancelled.
payment_intent.succeededA payment intent has been fulfilled.
payment_intent.requires_captureMerchant capture is required to fulfil this payment intent.
payment_intent.requires_customer_actionAdditional customer action is required to fulfil this payment intent.
payment_attempt.receivedThis payment attempt has been received.
payment_attempt.authorizedThe payment attempt has been authorized and is waiting to be captured.
payment_attempt.authorization_failedThe payment attempt has failed authorization.
payment_attempt.capture_requestedThe payment attempt has been requested for capture successfully and is therefore fulfilled.
payment_attempt.capture_failedThe payment attempt has failed to capture and is therefore terminated.
payment_attempt.authentication_redirectedThe payment attempt has been redirected for authentication.
payment_attempt.authentication_failedThe payment attempt has failed to be authenticated.
payment_attempt.failed_to_processThe payment attempt has failed to be processed.
payment_attempt.cancelledThe payment attempt has been cancelled.
payment_attempt.expiredThis payment attempt has expired.
payment_attempt.risk_declinedThe payment attempt has failed to pass the risk screen
payment_attempt.settledThis payment attempt's fund has been received by Airwallex from the acquirer.
payment_attempt.paidThis payment attempt's fund has been paid to the merchant's wallet successfully.
payment_consent.createdThe payment consent has been created
payment_consent.verifiedThe payment consent has been verified
payment_consent.updatedThe payment consent has been updated
payment_consent.disabledThe payment consent has been disabled, the PaymentConsent cannot be used or updated
customer.createdA customer has been created.
customer.updatedThe customer has been updated.
refund.receivedThis refund request has been received and will be processed later.
refund.accepted
refund.succeededThis refund has been fulfilled successfully.
refund.failedThis refund has failed.
payment_method.createdThis payment method has been created.
payment_method.updatedThis payment method has been updated.
payment_method.attachedThis payment method has been attached to a customer.
payment_method.detachedThis payment method has been detached from a customer.
payment_method.disabledThis payment method has been disabled.
dispute.rfi_received_by_merchantReceived RFI from acquirer.
dispute.rfi_responded_by_merchantMerchant responded to RFI.
dispute.acceptedMerchant accepted the dispute.
dispute.dispute_received_by_merchantAcquirer required merchant to provide evidence for dispute stage.
dispute.dispute_responded_by_merchantMerchant provided evidence for dispute stage.
dispute.wonMerchant won the dispute (final result).
dispute.lostMerchant lost the dispute (final result).
dispute.dispute_reversedDispute is reversed

Payload

The webhook payload is sent as JSON in the POST request body. The full event details are included and can be used directly, after parsing the JSON into an Event object.This Event object contains all the relevant information about what just happened, including id which identifies the event, the accountId which identifies the account this event belongs to, name which specifies the type of event and data associated with that event.

{
 "id": "evt_100_2019102201540902013102020043_8321220011893766",
 "name": "payment_attempt.authorized",
 "account_id": "19621303213",
 "data": {
   "object": {...}, // JSON object of the resource(API reponse)
   "error": {...}   // If error occurs
 },
 "created_at": "2019-10-22T01:54:09+0000"
}

The object in data is the same JSON object as the response from a successful API call for getting corresponding data type, as in the links listed below (except for dispute events):

For object of dispute events, see sample payload at Dispute notifications

The error object in data contains information about the error occurred, it has 4 possible fields: code and message are available for every error object, source is only for code "validation_error", representing the name of the request parameter that caused the error, and provider_original_response_code is only for code "provider_declined" or "issuer_declined", representing the original response code from provider.

See following examples of error object:

{         
  "code": "validation_error",     
  "source": "merchant_order_id",         
  "message": "merchant_order_id must be provided." 
}
{         
  "code": "issuer_declined",             
  "message": "The issuer declined this transaction.",
  "provider_original_response_code": "01"
}

Delivery headers

HTTP POST payloads that are delivered to your webhook\'s configured URL endpoint will contain several special headers:

HeaderDescription
x-timestampThe Long type timestamp, such as 1357872222592.
x-signatureThe HMAC hex digest of the response body. This header will be sent if the webhook is configured with a secret. The HMAC hex digest is generated using the sha256 hash function and the secret as the HMAC key.

Respond to webhook events

You must acknowledge the notifications we send you. To acknowledge receipt of an event, your endpoint must return a 200 HTTP status code, when either no answer or another response code is received we will retry. Acknowledge events prior to any logic that needs to take place to prevent timeouts.

Checking webhook signatures

Airwallex can optionally sign the webhook events it sends to your endpoints. We do so by including a signature in each request's header. This allows you to verify that the events were sent by Airwallex. You can verify signatures by following steps.

Before you can verify signatures, you need to retrieve your endpoint's secret from your Webapp. Each secret is unique to the endpoint to which it corresponds. Additionally, if you have multiple endpoints, you must obtain a secret for each one. After this setup, Airwallex starts to sign each webhook it sends to the endpoint.

  1. Extract the x-timestamp and x-signature from the header

  2. Prepare the value_to_digest string. You achieve this by concatenating: the x-timestamp (as a string) and the actual JSON payload (the request's body, as a string)

  3. Compute an HMAC with the SHA-256 hash function. Use the endpoint's signing secret as the key, and use the value_to_digest string as the message.

  4. Compare the x-signature in the header to the expected signature. If a signature matches, compute the difference between the current timestamp and the received timestamp, then decide if the difference is within your tolerance.

Code example

Java

import org.apache.commons.codec.digest.HmacUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Controller
public class WebhookExampleController {

@PostMapping("/webhook/example")
@ResponseBody
public String receive(HttpServletRequest request, @RequestBody String payload, HttpServletResponse response) {

String responseBody = "";

StringBuilder valueToDigest = new StringBuilder();
// Get the timestamp from header
String timestamp = request.getHeader("x-timestamp");
valueToDigest.append(timestamp);
valueToDigest.append(payload);

// Get the signature from header
String signature = request.getHeader("x-signature");

// Get your secret
String secret = getSecret();

if (HmacUtils.hmacSha256Hex(secret, valueToDigest.toString()).equals(signature)) {

// Do something with event

response.setStatus(HttpServletResponse.SC_OK);
} else {
// Invalid signature
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
responseBody = "failed to verify the signature";
}

return responseBody;
}

}

PHP

<?php
use RingCentral\Psr7\Response;

function getSecret() {
return 'whsec_CEm2XM_JZ1x5FxUUEGcZoRgIz4RZfDE';
}

function handler($request, $context): Response{
$timestamp = $request->getHeaderLine('x-timestamp');
$body = $request->getBody()->getContents();

$secret = getSecret();
$signature = $request->getHeaderLine('x-signature');

if (hash_hmac('sha256', $timestamp.$body, $secret) != $signature) {
return new Response(400, array(), 'failed to verify the signature');
}

// Do something with event
return new Response(200, array(), $body);
}

Node.js

// express.js
const crypto = require('crypto')

const secret = '<CLIENT_API_WEBHOOK_SECRET>'

async webhookController(ctx, next) {
  // webhook is received

  const { headers, body } = ctx.request
  const { name, accountId } = body || {} // payload

  const ts = headers['x-timestamp']

  const policy = `${ts}${JSON.stringify(body)}`

  const signatureHex = crypto.createHmac('sha256', secret).update(policy).digest('hex')

  if (signatureHex === headers['x-signature']) {
    // do business logic after signature is verified

    return next() // http response code = 200: ack the webhook
  } else {
    ctx.status = 500
    ctx.body = 'failed to verify webhook signature'
    return
  }
}

Tips for using webhooks

Receive events with an HTTPS server

Please use an HTTPS URL for your webhook endpoint for security considerations. Furthermore, your server must be correctly configured to support HTTPS.

Retry logic

If your webhook endpoint is unavailable or takes too long to respond, Airwallex will resend the notification message several times over the course of three days until a successful response is returned.

Acknowledge events immediately

If your webhook script performs complex logic, it's possible that the script would time out before Airwallex sees its complete execution. Ideally, your webhook handling code (acknowledging receipt of an event by returning a 200 status code) is separate from any processing performed for that event.

Handle duplicate events

Webhook endpoints might occasionally receive the same event more than once. We advise you to guard against receiving duplicate events by making your event processing idempotent.

Order of events

Airwallex does not guarantee delivery of events in the order in which they are generated. Your endpoint should not expect delivery of these events in this order and should handle this accordingly. You can also use the x-timestamp in the header for ordering.

Webhook addresses for IP whitelisting

Airwallex emits Webhook calls using one of the following IPs. To receive webhook calls successfully, these IPs must be whitelisted:

For Production Environment

  • 34.92.128.176
  • 35.220.213.251
  • 47.91.227.149
  • 47.52.78.97
  • 35.240.218.67
  • 35.185.179.53
  • 34.87.64.173

For Demo Environment

  • 34.92.15.70
  • 34.92.144.250
  • 8.210.222.213
  • 47.75.169.138
  • 34.87.139.23
  • 35.240.211.132
  • 35.187.239.216