Refund Status Notifications

Refund Status Notifications enable you to track refunds and refund bundles while they make their way through the refund process. Whenever a status change event occurs, for example when the refund status changes from initiated to received, you'll get a notification to the URL you provided.

There are two different kinds of notifications for refunds:

  • Notifications for single refunds

  • Notifications for refund bundles

How do I get started?

When you register an application, you will receive a Shared Secret that you should use to validate the payload of the received notification. For more information about secure notifications see Validating Notifications.

Refund Statuses and Notifications

For Single Refunds

The following statuses trigger a notification for a refund: 

Status/Notification Description

initiated

 

You have created a refund.

received

Flywire has received your money for the refund.

finished

Flywire has transferred the money back to your payer.

If a refund moves back from finished to received, it means Flywire couldn't transfer the money because the payer's bank rejected it. You don't need to take any action. Flywire will keep trying and notify you if the issue continues.

cancelled

When you cancel a refund, Flywire will not process the refund.

Cancelling a Refund Essentials - What You Should Know:

  • You can only cancel a refund that is in status initiated, meaning that Flywire has not taken the first step for processing the refund and no money has been moved yet.

  • You can cancel a refund before and after the cut-off time of a recipient.

  • You can't cancel a refund bundle.

returned

After Flywire received the money from you, Flywire returned the money back to you instead of returning it to your payer. This could happen for example when Flywire was unable to transfer the money to the payer.

In the rare case that a refund moves back from returned to received, it means that Flywire couldn't transfer the money back to you. Flywire will contact you if that happens.

For Refund Bundles

A refund bundle can have the following statuses and notifications:

Status Notification Description

pending

 

pending

The refund bundle has been successfully created.

marked for approval

Only for refund bundles that require manual approval.

The cut-off time for the refund bundle has been reached and the bundle is now ready for approval.

approved approved

The refund bundle has been approved, either manually by you or automatically by Flywire.

debited debited

Flywire has requested the money for the refund from the original recipient through direct debit.

This status only occurs when Flywire collects the funds via direct debit from the original recipient of the payment.

received received

Flywire has received your money for the refund bundle.

Content of Refund Status Notifications

For Single Refunds

data object

{
  "event_type": "initiated",
  "event_date": "2021-05-20T11:24:45Z",
  "event_resource": "refunds",
  "data": {
    "refund_id": "RPTUE0D63641",
    "payment_id": "PTU146221637",
    "external_reference": "a-reference",
    "bundle_id": "BUDR0AEA9E47",
    "status": "initiated",
    "amount": "4800",
    "currency": "EUR"
  }
}

data object

{
  "event_type": "received",
  "event_date": "2021-05-20T11:24:45Z",
  "event_resource": "refunds",
  "data": {
    "refund_id": "RPTUE0D63641",
    "payment_id": "PTU146221637",
    "external_reference": "a-reference",
    "bundle_id": "BUDR0AEA9E47",
    "status": "received",
    "amount": "4800",
    "currency": "EUR"
  }

data object

{
  "event_type": "finished",
  "event_date": "2021-05-20T11:24:45Z",
  "event_resource": "refunds",
  "data": {
    "refund_id": "RPTUE0D63641",
    "payment_id": "PTU146221637",
    "external_reference": "a-reference",
    "bundle_id": "BUDR0AEA9E47",
    "status": "finished",
    "amount": "4800",
    "currency": "EUR"
  }
}

data object

{
  "event_type": "returned",
  "event_date": "2021-05-20T11:24:45Z",
  "event_resource": "refunds",
  "data": {
    "refund_id": "RPTUE0D63641",
    "payment_id": "PTU146221637",
    "external_reference": "a-reference",
    "bundle_id": "BUDR0AEA9E47",
    "status": "returned",
    "amount": "4800",
    "currency": "EUR"
  }
}

data object

{
  "event_type": "cancelled",
  "event_date": "2021-05-20T11:24:45Z",
  "event_resource": "refunds",
  "data": {
    "refund_id": "RPTUE0D63641",
    "payment_id": "PTU146221637",
    "external_reference": "a-reference",
    "bundle_id": null,
    "status": "cancelled",
    "amount": "4800",
    "currency": "EUR"
  }
}

For Refund Bundles

data object

requests array

This array contains the refunds that are part of the bundle. Each refund contains the following parameters: 

{
  "event_type": "pending",
  "event_date": "2024-01-26T13:15:29Z",
  "event_resource": "refund_bundles",
  "data": {
    "bundle_id": "BUDRF62DEF4A",
    "api_reference": null,
    "external_reference": null,
    "status": "pending",
    "amount": "120000",
    "currency": "USD",
    "requests": [
      {
        "refund_id": "RRUC8277E659",
        "payment_id": "RUC203100127",
        "external_reference": "abcdef",
        "amount": "120000",
        "currency": "USD"
      }
    ]
  }
}

data object

{
  "event_type": "marked_for_approval",
  "event_date": "2024-01-26T13:17:00Z",
  "event_resource": "refund_bundles",
  "data": {
    "bundle_id": "BUDRF62DEF4A",
    "api_reference": null,
    "external_reference": null,
    "status": "pending",
    "amount": "120000",
    "currency": "USD"
  }
}

data object

requests array

This array contains the refunds that are part of the bundle. Each refund contains the following parameters: 

{
  "event_type": "approved",
  "event_date": "2021-05-20T11:24:45Z",
  "event_resource": "refund_bundles",
  "data": {
    "bundle_id": "BUDR0AEA9E47",
    "api_reference": "ABCD123",
    "external_reference": "a-reference-bundle",
    "status": "approved",
    "amount": "4800",
    "currency": "EUR",
    "requests": [
      {
        "refund_id": "RPTUE0D63641",
        "payment_id": "PTU146221637",
        "external_reference": "a-reference",
        "amount": "1000",
        "currency": "EUR"
      },
      {
        "refund_id": "RPTUE0D68649",
        "payment_id": "PTU146224730",
        "external_reference": "a-reference-1",
        "amount": "3800",
        "currency": "EUR"
      },
    ]
  }
}

data object

requests array

This array contains the refunds that are part of the bundle. Each refund contains the following parameters: 

{
  "event_type": "debited",
  "event_date": "2021-05-20T11:24:45Z",
  "event_resource": "refund_bundles",
  "data": {
    "bundle_id": "BUDR0AEA9E47",
    "api_reference": "ABCD123",
    "external_reference": "a-reference-bundle",
    "status": "debited",
    "amount": "4800",
    "currency": "EUR",
    "requests": [
      {
        "refund_id": "RPTUE0D63641",
        "payment_id": "PTU146221637",
        "external_reference": "a-reference",
        "amount": "1000",
        "currency": "EUR"
      },
      {
        "refund_id": "RPTUE0D68649",
        "payment_id": "PTU146224730",
        "external_reference": "a-reference-1",
        "amount": "3800",
        "currency": "EUR"
      },
    ]
  }
}

data object

requests array

This array contains the refunds that are part of the bundle. Each refund contains the following parameters: 

{
  "event_type": "received",
  "event_date": "2021-05-20T11:24:45Z",
  "event_resource": "refund_bundles",
  "data": {
    "bundle_id": "BUDR0AEA9E47",
    "api_reference": "ABCD123",
    "external_reference": "a-reference-bundle",
    "status": "received",
    "amount": "4800",
    "currency": "EUR",
    "requests": [
      {
        "refund_id": "RPTUE0D63641",
        "payment_id": "PTU146221637",
        "external_reference": "a-reference",
        "amount": "1000",
        "currency": "EUR"
      },
      {
        "refund_id": "RPTUE0D68649",
        "payment_id": "PTU146224730",
        "external_reference": "a-reference-1",
        "amount": "3800",
        "currency": "EUR"
      },
    ]
  }
}

How to set up Refund Status Notifications

When you are creating a refund (see Creating a Refund for a Payment) the following parameters influence the refund status notifications:

 

Parameter Description

notifications_url

string

You receive notifications about the refund status via callbacks (see Refund Status Notifications) via the notifications URL.

  • For refunds you can provide a notifications URL that is different from the notifications URL of the payment.

    • If you don't provide a URL, the notifications URL of the original payment is being used.

    • If the original payment had no notifications URL, no notifications will be sent.

  • For refund bundles, the notifications URL of the first refund that created the bundle is being used as the notifications URL for the whole bundle.

external_reference

string

The external reference.

The external reference is used to match a notification to a particular refund. You can use any kind of identifier or reference from your own system you might need to identify the refunds. Since this parameter will be included in the status notifications about this refund, it will help you to answer the question "For which refund did I get this notification?" Max size of 50 characters.

See Refund Status Notifications for more details.

Validating Refund Status Notifications

Validating notifications is optional, as it’s on your server side, but for security reasons it is recommended to validate all notifications.

How to validate a notification

To validate a notification, check its X-Flywire-Digest header. This value is generated by Flywire using your Shared Secret to encrypt the notification body. To verify the notification, generate the digest using the same method and compare it to the X-Flywire-Digest value in the header. If they match, the notification is legitimate and hasn't been tampered with.

  1. Retrieve the raw HTTP body of the notification you received.

    Make sure you use the raw HTTP body of the notification. You must generate the X-Flywire-Digest value using the exact payload you received in the notification. If you change the body in any way the values won't match later.

 

  1. Encrypt the received notification twice:

    1) Encrypt the raw HTTP body of the received notification with your Shared Secret using the SHA-256 algorithm.

    2) Take the result and encrypt it in Base64.

    The examples show you how to do this in different programming languages. In each example, exchange the shared_key with your Shared Secret and message_body with the raw HTTP body of the notification.

    digest = OpenSSL::Digest.new('sha256')
    encrypted_payload = OpenSSL::HMAC.digest(digest, shared_secret, notification_body)
    Base64.encode64(encrypted_payload).strip
    			
    # Step 1: Define the hashing algorithm to use for the HMAC.
    # This initializes the SHA-256 hashing mechanism.
    hash_algorithm = OpenSSL::Digest.new('sha256')
    
    # Step 2: Compute the HMAC in binary format.
    # This uses the shared secret and the message body to produce the HMAC signature.
    hmac_binary = OpenSSL::HMAC.digest(hash_algorithm, shared_secret, message_body)
    
    # Step 3: Encode the HMAC in Base64 for use in the X-Flywire-Digest header.
    x_flywire_digest = Base64.encode64(hmac_binary).strip
    public static string Digest(string shared_secret, string message_body)
    {
        // Step 1: Initialize the HMACSHA256 object with the shared secret.
        // This sets up the hashing algorithm (SHA-256) and the secret key for HMAC.
        using (var hmacsha256 = new HMACSHA256(Encoding.UTF8.GetBytes(shared_secret)))
        {
            // Step 2: Convert the message body into a byte array.
            // The message body is the message payload you want to hash
            var bytes = Encoding.UTF8.GetBytes(message_body);
    
            // Step 3: Compute the HMAC hash of the message body.
            // This generates the HMAC using the shared secret and the message body.
            var hashedBytes = hmacsha256.ComputeHash(bytes);
    
            // Step 4: Convert the hashed byte array into a Base64 string.
            return Convert.ToBase64String(hashedBytes);
        }
    }
    const crypto = require('crypto');
    
    function createDigest(shared_secret, message_body) {
        // Step 1: Initialize the HMAC with the SHA-256 algorithm and the shared secret.
        const hmac = crypto.createHmac('sha256', shared_secret);
        
        // Step 2: Update the HMAC with the message body.
        // The message body is the message payload you want to hash
        hmac.update(message_body);
    
        // Step 3: Get the HMAC digest and encode it in Base64.
        const digestHeader = hmac.digest('base64');
        
        return digestHeader;  // The `digestHeader` is the X-Flywire-Digest header.
    }

     

  2. Compare your Base64 string to the value in the X-Flywire-Digest parameter of the notification you received.

    If the values match, the notification came from Flywire and hasn't been changed by a third party.

    If the values don't match, you shouldn't trust the notification.