Checkout for Self-Managed Recurring Payments

With self-managed recurring payments, you define the plans yourself. You have complete freedom, for example regarding the number of installments and when payments are made.

Since Flywire doesn't know your settings, you have to manually initiate each payment for those plans yourself.

You also have to send mandatory emails to your payers that inform them about the recurring payments.

Available payment methods:

  • Card

  • Direct debit

Checkout Sessions for Self-Managed Recurring Payments

A Checkout Session is the basis for displaying a form to your payer on your website:

Your Website
How would you like to pay?
Card
Just save new card
Save new card and make first payment
Use existing card
Direct Debit
SEPA
BACS
EFT Canada
ACH

 

UI forms for self-managed recurring payments are shown in an iframe on your website.

The purpose of the forms is to gather the payment information (payer's details and credit card details), and to save the payment details (card or bank account) for future payments. By saving, the card or bank account is assigned a payment method token and mandate ID that you can use to charge recurring payments without the payer being present.

 

How do Checkout Sessions of this type work?

  1. You have to create a Checkout Session to either generate a URL or a session ID, depending on your implementation.

  2. Your payer accesses the UI form on your website where they fill it out and confirm it.

  3. You get a postMessage via the event listener that contains a URL you need for confirming the Checkout Session.

  4. You confirm the Checkout Session.

  5. You receive the token and mandate in the response. If you created a payment with the Checkout Session, you also receive the payment reference.

Creating a Checkout Session for Self-Managed Recurring Payments

Request

Creating a Checkout Session returns a unique URL that allows you to display a UI form to your payers. To get the URL, you have to post the request body to the endpoint. The content of the request body depends on your use case:

Your payer will see a zero amount payment after this type of Checkout Session. 
Tokenizing a card requires a payment. Since there is no payment when the payer only saves (tokenizes) their card, Flywire creates a payment with an amount of 0 in the background for the purpose of tokenization. Your payer will be able to see this "0 charge" payment in their bank statement.

Parameters for the Request Body

charge_intent object

payor object

You have the option to pass payer information to pre-fill the fields of the UI form.

For a description of all fields and their valid values form see:

options object

Contains settings for the UI form.

form object

POST /checkout/sessions
curl https://base-url-placeholder/checkout/sessions
  -X POST 
  -H "Content-Type: application/json" 
  -H "X-Authentication-Key: {api_key}" 
  -d '{
    "type": "tokenization",
    "charge_intent": {
	   "mode": "subscription"
    },
   "payor": {
		"first_name": "Peter",
		"last_name": "Payer",
		"address": "123 High Street",
		"city": "London",
		"country": "GB",
		"state": "",
		"phone": "0044123456789",
		"email": "[email protected]",
		"zip": "SW1A 1AA"
    },
	"options": {
        "form": {
            "action_button": "save",
            "locale": "en",
            "show_flywire_logo": true
        }
    },
    "schema": "cards",
    "payor_id": "MyPayerID",
    "recipient_id": "FLW"
}'
				

Mandatory emails

Self-managed recurring payments require you to send mandatory emails to your payer. For details refer to Recurring Payments: Emails to Your Payer.

Parameters for the Request Body

charge_intent object

You have two options for the charge_intent:

payor object

You have the option to pass payer information to pre-fill the fields of the UI form.

For a description of all fields and their valid values form see:

options object

Contains settings for the UI form.

form object

recipient object

Contains the fields of the recipient.

fields array

It depends on the recipient which fields are optional or required. If a field is required, you must provide it here. Optional fields can be left out.

items array

POST /checkout/sessions
curl https://base-url-placeholder/checkout/sessions
  -X POST 
  -H "Content-Type: application/json" 
  -H "X-Authentication-Key: {api_key}" 
  -d '{
    "type": "tokenization_and_pay",
    "charge_intent": {
        "mode": "subscription"
    },
   "payor": {
		"first_name": "Peter",
		"last_name": "Payer",
		"address": "123 High Street",
		"city": "London",
		"country": "GB",
		"state": "",
		"phone": "0044123456789",
		"email": "[email protected]",
		"zip": "SW1A 1AA"
    },
    "options": {
        "form": {
            "action_button": "save",
            "locale": "en",
            "show_flywire_logo": true
        }
    },
	"recipient": {
   		"fields": [
            {
                "id": "custom_field_1",
                "value": "ID12345"
            },
			{
				"id": "custom_field_2",
				"value": "2020"
			}
        ]
	},
    "items": [
        {
            "id": "default",
            "amount": 33000
        }
    ],
    "notifications_url": "https://webhook.site/9bf9cf8d-1d8c-46d1-b147-ca5841ff2ede",
    "external_reference": "Payment ID12456",
    "recipient_id": "FLW",
    "schema": "cards",
    "payor_id": "MyPayerID"
}
Your payer will see a zero amount payment after this type of Checkout Session. 
Tokenizing a card requires a payment. Since there is no payment when the payer only saves (tokenizes) their card, Flywire creates a payment with an amount of 0 in the background for the purpose of tokenization. Your payer will be able to see this "0 charge" payment in their bank statement.

Parameters for the Request Body

charge_intent object

options object

Contains settings for the UI form.

form object

POST /checkout/sessions
curl https://base-url-placeholder/checkout/sessions
  -X POST 
  -H "Content-Type: application/json" 
  -H "X-Authentication-Key: {api_key}" 
  -d '{
    "type": "new_mandate",
    "charge_intent": {
        "mode": "subscription",
		"payment_method_token": "UW6JqAuMcJje-SyR3iIG"
    },
    "options": {
        "form": {
            "action_button": "save",
            "locale": "en",
            "show_flywire_logo": true
        }
    },
    "schema": "cards",
    "payor_id": "MyPayerID",
    "recipient_id": "FLW"
}'
The form works and looks slightly different for each direct debit scheme.

Parameters for the Request Body

charge_intent object

payor object

You have the option to pass payer information to pre-fill the fields of the UI form.

For a description of all fields and their valid values form see:

options object

Contains settings for the UI form.

form object

For BACS

Since BACS is only available in the UK you can only use recipients that have GBP as their billing currency. If you are trying to use a non-GPB recipient you'll receive a 422 error, see Response after creating a Checkout Session.

POST /checkout/sessions
curl https://base-url-placeholder/checkout/sessions
  -X POST 
  -H "Content-Type: application/json" 
  -H "X-Authentication-Key: {api_key}" 
  -d '{
    "type": "tokenization",
    "charge_intent": {
        "mode": "subscription"
    },
   "payor": {
		"first_name": "Peter",
		"last_name": "Payer",
		"address": "123 High Street",
		"city": "London",
		"country": "GB",
		"state": "",
		"phone": "0044123456789",
		"email": "[email protected]",
		"zip": "SW1A 1AA"
    },
    "options": {
        "form": {
			"action_button": "save",
			"locale": "en",				
            "show_flywire_logo": true
        }
    },										
    "schema": "dd_sepa",
    "payor_id": "payorCCC",
    "recipient_id": "UUI"
}
Saving the bank account and creating the first payment at the same time (tokenization_and_pay) is currently only available for SEPA.

Mandatory emails

Self-managed recurring payments require you to send mandatory emails to your payer. For details refer to Recurring Payments: Emails to Your Payer.

Parameters for the Request Body

charge_intent object

payor object

You have the option to pass payer information to pre-fill the fields of the UI form.

For a description of all fields and their valid values form see:

options object

Contains settings for the UI form.

form object

recipient object

Contains the fields of the recipient.

fields array

It depends on the recipient which fields are optional or required. If a field is required, you must provide it here. Optional fields can be left out.

items array

POST /checkout/sessions
curl https://base-url-placeholder/checkout/sessions
  -X POST 
  -H "Content-Type: application/json" 
  -H "X-Authentication-Key: {api_key}" 
  -d '{
    "type": "tokenization_and_pay",
    "charge_intent": {
        "mode": "subscription"
    },
   "payor": {
		"first_name": "Peter",
		"last_name": "Payer",
		"address": "123 High Street",
		"city": "London",
		"country": "GB",
		"state": "",
		"phone": "0044123456789",
		"email": "[email protected]",
		"zip": "SW1A 1AA"
    },
    "options": {
        "form": {
			"action_button": "save",
			"locale": "en",				
            "show_flywire_logo": true
        }
    },
	"recipient": {
   		"fields": [
            {
                "id": "custom_field_1",
                "value": "ID12345"
            },
			{
				"id": "custom_field_2",
				"value": "2020"
			}
        ]
	},
    "items": [
        {
            "id": "default",
            "amount": 3300
        }
    ],
    "notifications_url": "https://webhook.site/9bf9cf8d-1d8c-46d1-b147-ca5841ff2ede",
    "external_reference": "Payment ID12456",
    "recipient_id": "UUI",
    "schema": "dd_sepa",
	"payor_id": "payorCCC"
}'

 

Response

No matter which type of Checkout Session you created, the response is always the same.

 

hosted_form object

Only relevant when you render the form via iframe. More info:

warnings array

errors array

You can decide which information from the response you want to filter out in your backend before passing the URL to your frontend.

{
"id": "494d2e9d-c0c9-407c-9094-5b3b2a02c00f",
"expires_in_seconds": 1800,
"hosted_form": {
	"url": "https://payment-checkout-dev-apache.flywire.cc/v1/form?session_id=494d2e9d-c0c9-407c-9094-5b3b2a02c00f",
	"method": "GET"
},
"warnings": []
}

When are warnings returned?

If the warning array contains warnings, it means that you tried to pre-fill the UI form with invalid values. You can use these warnings to check the data in your system for mistakes.

Are warnings an error?

No, warnings inform you of issues with field values, but you will still receive the URL and can display the form to your payer.

How do the warnings affect the form?

If a drop down field (like country) is affected by invalid values, the field will be empty, meaning no selection from the drop down is made yet. Your payer has to select the value from the drop down manually.

hosted_form object

Only relevant when you render the form via iframe. More info:

warnings array

errors array

You can decide which information from the response you want to filter out in your backend before passing the URL to your frontend.

{
"id": "e6bf6f63-46e0-4bd7-9bce-8106c02d0b3f",
"expires_in_seconds": 1800,
"hosted_form": {
	"url": "https://payment-checkout-dev-apache.flywire.cc/v1/form?session_id=e6bf6f63-46e0-4bd7-9bce-8106c02d0b3f",
	"method": "GET"
},
"warnings": [
	{
		"source": "email",
		"errors": [
			{
				"text": "must be a valid email",
				"error_type": "invalid_email"
    		}
   		]
  	}
  ]
}

Creating the Checkout Session failed - Recipient currency is not GBP

422 Error

When does this happen?

You tried to create a Checkout Session with a recipient that does not have GBP set as its currency.

What should you do? 

  • Use a different direct debit scheme (not BACS).

  • Alternative: Use a different recipient (if you can't use a different recipient, contact Flywire to change the settings of the recipient).

{
    "type": "https://developers.flywire.com",
    "title": "Unprocessable entity",
    "status": 422,
    "detail": "Invalid parameters",
    "errors": [
        {
            "source": "/",
            "param": "recipient_id",
            "type": "invalid_param",
            "message": "BACS direct debit requires GBP. The recipient ARA cannot receive payments in GBP."
        }
    ]
}

The Payer's side: Filling out and sending the UI Form

After you created the Checkout Session, your payer accesses the UI Form on your website. Depending on the UI Form, your payer goes through different steps to fill it out and send it.

What does my payer see?

The postMessage of the Event Listener

After your payer sent the form, the event listener will return a successful postMessage.

A successful postMessage confirms that the form was submitted and that you can confirm the Checkout Session now. However, it does not indicate payment success. Payment status updates will be sent via callback notifications, see Payment Status Notifications for details.

The postMessage contains the following information:

confirm_url object

Contains the confirm URL. The confirm URL is the full url for the request to confirm the session, already resolved with the correct session ID.

payor object

The email address your payer entered in the UI Form. You need to use this email address to send mandatory emails to your payer, see

Make sure you use the email address you got via the postMessage for the emails to your payer. Even if you already have an email address in your system, your payer might have updated the email address in the UI Form.
{
confirm_url: {
	method: "POST",
	url: "https://api-platform.flywire.com/payments/v1/checkout/sessions/494d2e9d-c0c9-407c-9094-5b3b2a02c00f/confirm",
},
payor: {
email: "[email protected]"
},
source: "checkout_session",
success: true
}

Confirming a Checkout Session

Request

As a security measure to ensure that you are the one who created the session, you have to confirm the Checkout Session.

 

How to Resolve the Path Placeholders of the Endpoint

You don't need to manually resolve the {ID} in the endpoint, as Flywire provides the fully resolved URL via postMessage.

Why should I use the confirm url from the postMessage?

  • It already contains the correct Checkout Session ID, no need to retrieve it from somewhere else.

  • You always receive the URL after the form has been sent. If you would try to confirm a Checkout Session before the form has been sent, you'll get an error message

You can only send the request to confirm the Checkout Session once. After a Checkout Session has been confirmed, you'll receive an error if you try to confirm it again.

POST/checkout/sessions/{ID}/confirm

curl https://base-url-placeholder/checkout/sessions/494d2e9d-c0c9-407c-9094-5b3b2a02c00f/confirm
  -X POST
  -H "Content-Type: application/json"
  -H "X-Authentication-Key: {api_key}"

Response

After you confirmed the Checkout Session, you’ll receive the payment method token and mandate ID. If you created a Checkout Session of type tokenize_and_pay you'll also receive information about the payment that has been created.

The content of the response depends on which type of Checkout Session you created:

payment_method object

Contains information about the payment method the payer used and the payment method token.

You can display the card information in your UI for your payer to make it easier for them to identify which card they saved in your system.

mandate object

It is mandatory to include the mandate ID when charging the payment.

surcharge object

Only returned if a surcharge applies to the payment.

A surcharge may apply in certain situations, including, but not limited to, mismatches between card currency and payer country, as well as the use of certain card types. If a surcharge applies and the percentage of it depends on the individual settings for the recipient of the payment.

{
"payment_method": {
	"token": "232301485cfa4b36bc28",
	"type": "card",
	"card_classification": "credit",
	"last_four_digits": "1111",
	"card_expiration": "03/2030",
	"country": "US",
	"brand": "visa",
	"issuer": "JPMORGAN CHASE BANK, N.A."
	},
"mandate": {
	"id": "MCRUC20230330uAM584YC"
	"surcharge": {
		"percentage": 0.03
		}
	}
}
If you provided a notifications URL for the payment, you’ll start receiving notifications that let you track the payment’s progress (refer to Payment Status Notifications).

charge_info object

payment_method object

Contains information about the payment method the payer used and the payment method token.

You can display the card information in your UI for your payer to make it easier for them to identify which card they saved in your system.

mandate object

It is mandatory to include the mandate ID when charging the payment.

surcharge object

Only returned if a surcharge applies to the payment.

A surcharge may apply in certain situations, including, but not limited to, mismatches between card currency and payer country, as well as the use of certain card types. If a surcharge applies and the percentage of it depends on the individual settings for the recipient of the payment.

{
"payment_reference": "RUC208967136",
"charge_info": {
	"amount": 100000,
	"currency": "EUR"
	},
"payment_method": {
	"token": "232301485cfa4b36bc28",
	"type": "card",
	"card_classification": "credit",
	"last_four_digits": "1111",
	"card_expiration": "03/2030",
	"country": "US",
	"brand": "visa",
	"issuer": "JPMORGAN CHASE BANK, N.A."
	},
"mandate": {
	"id": "MCRUC20230330uAM584YC"
	"surcharge": {
		"percentage": 0.03
		}
	}
}

payment_method object

Contains information about the payment method the payer used and the payment method token.

You can display the card information in your UI for your payer to make it easier for them to identify which card they saved in your system.

mandate object

It is mandatory to include the mandate ID when charging the payment.

surcharge object

Only returned if a surcharge applies to the payment.

A surcharge may apply in certain situations, including, but not limited to, mismatches between card currency and payer country, as well as the use of certain card types. If a surcharge applies and the percentage of it depends on the individual settings for the recipient of the payment.

{
"payment_method": {
	"token": "232301485cfa4b36bc28",
	"type": "card",
	"card_classification": "credit",
	"last_four_digits": "1111",
	"card_expiration": "03/2030",
	"country": "US",
	"brand": "visa",
	"issuer": "JPMORGAN CHASE BANK, N.A."
	},
"mandate": {
	"id": "MCRUC20230330uAM584YC"
	"surcharge": {
		"percentage": 0.03
		}
	}
}

payment_method object

Contains information about the payment method the payer used and the payment method token.

mandate object

It is mandatory to include the mandate ID when charging the payment.

{
    "payment_method": {
        "token": "_jRS8z0CrDyDI2KshDPZ",
        "type": "direct_debit",
        "last_four_digits": "6789"
    },
    "mandate": {
        "id": "MUUI20230131XCGPRT"
    }
}
If you provided a notifications URL for the payment, you’ll start receiving notifications that let you track the payment’s progress (refer to Payment Status Notifications).

charge_info object

payment_method object

Contains information about the payment method the payer used and the payment method token.

mandate object

It is mandatory to include the mandate ID when charging the payment.

{
    "payment_reference": "RUC670821548",
    "charge_info": {
        "amount": 101300,
        "currency": "EUR"
    },
    "payment_method": {
        "token": "UW6JqAuMcJje-SyR3iIG",
        "type": "direct_debit",
        "last_four_digits": "6789"
    },
    "mandate": {
        "id": "MRUC20230330QBZ6MP"
    }

Essentials about Token and Mandate

About the Payment Method Token

The payment method token is a unique string of numbers and characters that gets assigned to a card or bank account when they are stored. It identifies the payment method (e.g., last four digits of a card or bank account) and includes payer details, such as the cardholder or account owner. Note: the payer may differ from the purchaser, like a parent paying a student’s tuition.

Important Information

  1. One payment method token can be used for multiple mandate IDs.

  2. The payment method token needs to be included in each payment.

    Since the token already contains information about the payer, sending the token means that you don’t need to include payer information manually in a payment anymore.

  3. The payer information in the payment method token can be different from the payer information stored in your system.

    The token stores the information gathered in the UI form. Even if you pre-filled the form with data from your system, the payer might have edited the fields in the form. This can be the case when data in your system is wrong (misspelled names etc.) or when the actual payer is different from what your system considers a “payer” (for example, your system contains information about your students as “payers” but the actual payer (card holder or bank account owner) is a parent).

Where can I find the payment method token for a payer? 

The payment method token is returned to you in the response after you confirmed a Checkout Session.

You can also find all payment method tokens for a payer (identified by their payor_id) with this request:

GET/payors/{payorId}/payment_methods

For details see Getting a List of all stored Payment Methods for a Payer.

About the Mandate ID

The mandate ID is a Flywire-generated ID that represents the payer's consent for recurring payments, much like a signature authorizing future payments.

After tokenizing a card or bank account, the mandate ID is returned to you in the mandate_id parameter or in the id parameter inside the mandate object.

It is mandatory to include the mandate ID when charging the payment.

For bank accounts, the mandate is represented by the mandate ID and additionally in PDF form.

Storing the mandate ID

Each mandate ID belongs to a specific plan (an installment plan, a subscription, etc.). You need to store the mandate together with the information about the plan and its payments in your system to ensure that you will be able to identify it.

 

The mandate PDF for bank accounts has other specific policies for storing:

Direct debit mandates for SEPA and BACS have to be stored for 3 years. Since the emails with the mandate are managed by Flywire (for SEPA) or Finastra (for BACS), Flywire or Finastra will store the mandate for you.

Using the mandate ID for recurring payments

The mandate ID needs to be included in each payment that belongs to the same plan (for example, installments of an installment plan or payments for an ongoing subscription). If the payer wants to pay for a different plan, you need to generate a new mandate.

How do I generate a new mandate ID? 

  1. New card or bank account

    If a payer wants to use a new card or bank account for a plan, you have to save (and therefore tokenize) the card or bank account, as this will also give you a mandate.

  2. Existing card

    If a payer wants to use an existing card that is already tokenized, you can generate a new mandate for the new plan with this card.

Where do I find existing mandate IDs?

You can find all mandate IDs for a payment method token with this request:

GET/payors/{payorId}/payment_methods/{token}

For details see Getting the Mandate IDs for a Payment Method Token.