Embedded Checkout

 

The Checkout Experience for Embedded Checkout

The Checkout Experience offers two options for payments:

  • One Off payments: A single payment, no card or bank account data is stored.

  • Installment plans: Recurring payments, card or bank account data is stored to pay for multiple payments.

Window size

The Checkout Experience automatically scales to 80% of the viewport height with a 510px maximum width. Internal scrolling prevents content overflow.

 

Integrating the Checkout Experience into your Website

This simplified html example shows you how to integrate the Checkout Experience into your website:

Script Source

Add the script to your page that opens the Checkout Experience.

The script source is:

https://payment.flywire.com/assets/js/checkout.js

Button

Add a button with an ID to trigger the Checkout Experience form.

Script Initialization

Configuration

Configure the Checkout Experience. See List of Configuration Parameters for all parameters.

You can also use the Checkout Script Configurator to create your configuration.

Button identifier

Connect the script to your button via the button ID.

<!DOCTYPE html>
<html>
  <head>
    <!-- Include the Checkout script -->
    <script src="https://payment.flywire.com/assets/js/checkout.js"></script>
  </head>
  <body>
    <!-- Add a button element -->
    <button id="pay-button">
      Make Payment
    </button>
   <!-- Initialize the script -->
    <script>
      window.flywire.Checkout.render(
      {
        "env": "demo",
        "recipient": "TQQ",
        "locale": "en",
        "amount": 10000,
        "sender_first_name": "TBF",
        "callback_id": "",
        "callback_url": "",
        "return_url": "",
        "provider": "embed2.0",
        "displayPayerInformation": false,
        "theme": {
          "mode": "popup",
          "header": true,
          "footer": true,
          "closeButton": true,
          "chat": true,
          "brandColor": "#1274C4"
        }
      },
        "#pay-button",
      );
     </script>
  </body>
</html>

 

Checkout Script Configurator

 

List of Configuration Parameters

Required Basic Settings

Environment

// Set the environment
env: "demo",

Recipient of the Payment (Portal)

// Your portal/recipient code (may differ between demo and prod)
recipient: "FLW",

Amount

When setting up your portal with Flywire, you can choose how you want to handle amounts. There are two possible configurations:

  • One amount: There is only one field for the total amount.

  • Items: There are multiple fields to breakdown specific costs (e.g., "Tuition" and "Housing"), and each field has an amount.

If you want to change your portal configuration, please contact the Solutions team.

Not sure which one you’re using? Use the portal checker and look at the field IDs for items:

  • One amount: You will see a single field with the ID amount.

  • Items: You will see one or more IDs with specific names (e.g., tuition_fee, housing, etc.).

My portal uses one amount:

// Set the amount of the payment
amount: 123456,

My portal uses items:

amount object

Provide the amount for each item as pairs of the ID of the item and its amount:

// Set the amount of the payment
"amount": {
    "tuition": 250025,
    "housing": 500000
  }

Provider

// The origin of the payment
provider: "embed2.0",

Pre-filling Fields of the Checkout Experience

Providing Payer Information

Essentials about providing payer information:

Providing any payer information is optional. The following information tells you if the payer field is required to complete the payment. If you don't provide the required information, the payer has to fill out those fields in the Checkout Experience.

If you provide all required payer fields, the page that collects payer information is automatically hidden. If you want to change this default behavior, see Displaying the payer information page.

// Payer information
"sender_first_name": "Peter",
"sender_last_name": "Payer",
"sender_address1": "Main Street 1",
"sender_city": "London",
"sender_state": "",
"sender_country": "GB",
"sender_phone": "+4412345678",
"sender_email": "[email protected]",
"sender_type": "individual",
"sender_date_of_birth": "2000-12-27",

Providing Dynamic Fields for the Recipient

The Checkout Experience does not display the recipient's dynamic fields.

This means your payer won't be able to provide information for your dynamic fields. You have to ensure that you provide the information for mandatory fields.

Which fields must be provided?

Dynamic fields can be mandatory or optional. You must provide values for mandatory dynamic fields, otherwise the Checkout Experience won't be able to create the payment.

How to provide dynamic fields

You provide the information as a pair of the internal_name of the field and its value. Dynamic fields can have different types, and it depends on the field type how you provide the value for them.

You can use the portal checker to find out:

  • if your portal has mandatory fields

  • what the internal names of the fields are

  • the type of each field

Guide for providing values for each field type

String field

When you are checking your portal settings, this field type is called string - text/phone/country/date.

How to pass values for the different field formats:

Field name:

Use the internal name (Id) of the field. You can check your portal settings to find the Id.

Values:

Text

Pass the text as a string.

Phone

The phone number of the payer (including the country code).

Use + in front of the country code, for example, "+44123456" for country code 44.

Country

The ISO2 code of the country.

Date

Date in dd/mm/yyyy format, for example 01/01/2025.

"student_id": "ID123456",
"phone_number": "+44123456789",
"country_residence": "US",
"start_date": "01/01/2025",

Single Checkbox

When you are checking your portal settings, this field type is called boolean.

How to pass values for this field type:

Field name:

Use the internal name (Id) of the field. You can check your portal settings to find the Id.

Values:

Pass either true or false , since this is a boolean field.

"residential_student": "true",

Multiple Checkboxes

When you are checking your portal settings, this field type is called array.

How to pass values for this field type:

Field name:

Use the internal name (Id) of the field. You can check your portal settings to find the Id.

Values:

Pass the internal name (value, not the label) as a string.

If you don't know the internal value, contact the Solutions team.

You can pass multiple values as a comma-separated list.
"services_used": "housing, tuition",

Providing Dynamic Fields for the Payment Method

Some payment methods have their own dynamic fields. The Checkout Experience always displays those fields for your payer to fill out, but you have the option to provide values to pre-fill them. Note that providing fields for a payment method does not pre-select the payment method, but if your payer picks this payment method the corresponding fields are pre-filled.

How to provide dynamic fields for payment methods

Provide the field as pairs of the ID of the field and its value. If you don't know the ID of the payment method field, please contact the Solutions team. For how to provide values for the different field types, see Guide for providing values for each field type.

// Provide Dynamic Payment Method Fields
offer_pan_number: "FLYPA7733I"

 

Where are dynamic fields for payment methods displayed?

Hover to see examples for payment method dynamic fields highlighted.

 
 

Pre-selecting an Installment Plan Configuration for the Payer

You can pre-select a specific installment plan configuration (cohort) to only display this configuration to your payer.

// Provide the recurring configuration ID
"recurring_configuration_id": "COTQQ19D4430F877"

Displaying the Checkout Experience

Language

// Set the display language
locale: "en",

The Checkout Experience allows payers to change the display language, even if you pre-defined one with the locale parameter.

This is enabled by default, the button to change the language is always shown in the footer of the Checkout Experience. If you want to hide the whole footer including the language button, see Theme.

 

Theme

theme object

// Theme options
"theme": {
	"mode": "popup",
	"header": true,
	"footer": true,
	"closeButton": true,
	"chat": true,
	"brandColor": "#1274C4"
}

Header

Close Button

Brand Color

Footer

Chat Button

 
 
 
 
 
 

Mode

theme.mode string

Defines how the Checkout Experience will be displayed on your page.

Possible values:

  • popup

  • drawer

  • inline

Displaying the payer information page

// Display payer information page
"displayPayerInformation": true,

If you want to keep the default setting and hide the payer information page, you must provide all required payer information fields:

For the page to be hidden, all required payer information fields must be provided by you.

What happens if payer information is missing or invalid?

If required field values are missing or are invalid, the payer information page will be displayed so that the payer can add or correct the information. Valid provided fields will be read-only, the payer can only provide the missing/invalid information.

If an optional field is not provided, the page will not be displayed to your payer.

// Payer information
"sender_first_name": "Peter",
"sender_last_name": "Payer",
"sender_address1": "Main Street 1",
"sender_city": "London",
"sender_state": "",
"sender_country": "GB",
"sender_phone": "+4412345678",
"sender_email": "[email protected]",
"sender_type": "individual",
"sender_date_of_birth": "2000-12-27",

Custom Idle Timeout

// Set custom idle timeout
 "idleTimeout": 700000,

After-Payment Settings

After the payment is created, payment information can be returned to you in two different ways:

Return URL

How information is sent back to your website

The flow for the Checkout integration is:

  1. The payer provides information.

    The payer opens the Checkout Experience on your website, enters their personal details, and chooses a payment method.

  2. Information is sent back to your website.

    This triggers the Checkout integration to send back the payment status and details to your website.

    The information is returned through the return_url:

    The return URL serves two purposes:

    1. It specifies where the payer will be redirected after the payment is created and the Checkout Experience closes.

    2. Query parameters with payment information are automatically added to the return URL.

      When the payer is redirected to the return URL, Flywire attaches query string parameters containing payment information to the set URL. This way, your website can receive and process the payment information.

  3. You use the information for your own systems and the payer.

    You can use the returned parameters to update your backend, display information to your payer, or for your internal logging and processing of payments.

// Set the return url
"return_url": "https://your-website.com",

Callbacks for Payment Status Notifications

If you have access to Flywire Dashboard, callbacks are optional (but recommended) since you have the option to manually track the payment progress there. If you don't have access to Flywire Dashboard, it is highly recommended to use callbacks enable you to track the payment progress.

For details about the content of callbacks see Payment Status Notifications.

If you want to receive callbacks, it is required to provide both a callback URL and a callback ID, otherwise no callbacks will be triggered.

// Enable payment status notification callbacks
"callback_url": "https://api.your-domain.com/flywire-notifications",
"callback_id": "REF1234",

 

Security: Anti-Tampering via Token

Instead of keeping the configuration parameters for your Checkout Experience directly on your website, you can use a token that represents your configuration data but keeps the actual values hidden from the end user, which prevents tampering.

How it works

  1. Generate: Your server makes a backend call to the anti-tampering endpoint with your configuration parameters.

    If the recipient (portal) of the payment has a Shared Secret, you need to authenticate your request, otherwise no authentication is needed.

  2. Exchange: Flywire returns a unique token. You pass this token instead of the raw parameters into your Checkout configuration.

    Note that there are some configuration parameters that have to be added on your website in addition to the token.

Endpoint

Production
https://gateway.flywire.com/v1/embedded/session
Demo
https://gateway.demo.flywire.com/v1/embedded/session

Authentication

It depends on the settings for the recipient of the payment (portal) if authentication is required.

For recipients that require authentication, the following applies: 

All requests need to be authenticated with a special header, called the X-Flywire-Digest header. If you leave out the X-Flywire-Digest header, the request will fail.

Hover to highlight the authentication header

 

curl https://base-url-placeholder/
	-X POST 
	-H "Content-Type: application/json" 
	-H "X-Flywire-Digest:{your_digest}" 
	-d '{
	"recipient": {
		"id": "FWU",
...
}

How to generate the X-Flywire-Digest header

1. Convert the JSON body of your request into a string. Use the exact request body you are using for sending the request.

message_body = your_json_object.to_json
string message_body = JsonConvert.SerializeObject(yourJsonObject);
const message_body = JSON.stringify(yourJsonObject);

 

2. Generate the header value:

  1. Encrypt the converted body of your request with your Shared Secret using the SHA-256 algorithm.

  2. Take the result and encrypt it in Base64.

  3. Use the final result as the value for your digest header.

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 converted JSON body of your request.

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.
}

Request

Authentication

Only necessary if your recipient (portal) has a Shared Secret, see Authentication.

Request Body

recipient object

Contains the subdomain of the recipient (portal) and - if necessary - dynamic fields.

Dynamic fields of the recipient

Provided as pairs of the field ID and the value. For more help see Providing Dynamic Fields for the Recipient.

The Checkout Experience does not display the recipient's dynamic fields.

This means your payer won't be able to provide information for your dynamic fields. You have to ensure that you provide the information for mandatory fields.

sender object

Contains the payer data.

Providing any payer information is optional. The following information tells you if the payer field is required to complete the payment. If you don't provide the required information, the payer has to fill out those fields in the Checkout Experience.

country object

Contains the country and defines if the payer can change the provided country.

items object

Contains the payment amount.

Identifier for the amount

It depends on your recipient (portal) settings which identifier you need to use:

  • For recipients (portals) that use one amount: Use amount.

  • For recipients (portals) that use items: The ID (internal identifier) of the item. You can use the portal checker to look up the IDs for each of your items.

Payment amount

The payment amount in the billing currency.

The amount is specified in the smallest unit of the currency, called subunits. For example, in USD, the subunit is cents, and 100 cents equal 1 USD. So, an amount of 12025 (cents) is equivalent to 120.25 USD.

Note that the subunit-to-unit ratio varies by currency, it is not always 100. See Currencies for the subunits of each currency.

curl https://base-url-placeholder/
-X POST
-H "Content-Type: application/json"
-H "X-Flywire-Digest: {your_digest}"
-d '{
  "recipient": {
    "id": "your_subdomain",
    "student_id": "4882384204"
  },
  "sender": {
    "country": {
      "value": "ES",
      "read_only": true
    },    
    "first_name": "Peter",
    "last_name": "Payer",
    "address1": "12 Main Street",
    "city": "New York",
	"state": "NY",
    "phone": "123456789",						
    "email": "[email protected]",
    "type":"individual",
    "date_of_birth": "1995-11-00"
  },
  "items": {
    "amount": "400000"
  },
  "callback_id": "your-callback-id",
  "callback_url": "https://your-domain.com/callback"
}'

Response

A success response (200) returns the token. Use this token in your Checkout configuration script, see Example for a Configuration with an Anti-Tampering Token.

{
"token": "c8a08d3c-07f2-4561-a11a-3f85ae67e45z"
}

Requests for this recipient require authorization, see Authentication. Ensure that the value in your digest header is correct.

HTTP: 401 Unauthorized

Unprocessable entity.

Check the request body parameters and confirm you are using the correct parameters.

If the recipient ID is flagged as invalid, ensure that you are using the correct subdomain for your recipient. Note that the subdomain is required, not the recipient (portal) ID.

{
"errors": [
	{
	"source": "/recipient/id",
	"param": "id",
	"type": "invalid_param",
	"message": "is_invalid"
	}
	]
}

Internal server error.

{
"errors": "Server error",
"url": "https://www.demo.flywire.com/select-school"
}

Example for a Configuration with an Anti-Tampering Token

This simplified html example shows you how to integrate the Checkout Experience into your website when you are using tokenization for anti-tampering:

Script Source

Add the script to your page that opens the Checkout Experience.

The script source is:

https://payment.flywire.com/assets/js/checkout.js

Button

Add a button with an ID to trigger the Checkout Experience form.

Script Initialization

Raw parameters

The following parameters have to be set on your website and are not part of the token. They don't contain sensitive data and can safely be used on client-side:

theme object

Token

The anti-tampering token you received in the response.

Button identifier

Connect the script to your button via the button ID.

<!DOCTYPE html>
<html>
  <head>
    <!-- Include the Checkout script -->
    <script src="https://payment.flywire.com/assets/js/checkout.js"></script>
  </head>
  <body>
    <!-- Add a button element -->
    <button id="pay-button">
      Make Payment
    </button>
   <!-- Initialize the script -->
    <script>
      window.flywire.Checkout.render(
      {
        "env": "demo",
        "provider": "embed2.0",							
        "locale": "en",
        "return_url": "",
        "displayPayerInformation": false,
        "idleTimeout": 700000,		
        "theme": {
          "mode": "popup",
          "header": true,
          "footer": true,
          "closeButton": true,
          "chat": true,
          "brandColor": "#1274C4"
        },
	    "token":"c8a08d3c-07f2-4561-a11a-3f85ae67e45z"					
      },
        "#pay-button",
      );
     </script>
  </body>
</html>