Checkout for Pre-Authorization Payments

A Pre-Authorization Payment temporary reserves the payment amount on a debit or credit card. The purpose of a pre-authorization is to verify that the cardholder has sufficient credit available to cover a transaction and to temporarily set aside that amount until you capture the funds.

Pre-Authorization Payments are only available for same-currency payments, not for FX payments.

Example for a Pre-Authorization Payment:

A guest checks into a hotel for a three-night stay, and you place a pre-authorization on their credit card for the estimated cost of the stay.

  1. Check-In: You pre-authorizes $600 on the guest's credit card ($150 per night plus $150 deposit). This reduces the guest's available credit by $600 but is not yet charged.

  2. During the Stay: The guest orders room service for $50.

  3. Check-Out: The final bill is $500 ($450 for room charges plus $50 for room service). You capture $500 from the pre-authorization. The remaining $100 from the original hold is returned to the guest's available credit.

A Pre-Authorization Payment is split into two distinct steps:

  1. Pre-authorization of funds

    This step reserves the funds on the card for a certain amount of time (the holding period). The funds are reserved but not withdrawn.

  2. Capturing the funds

    When the funds are captured, the card is charged and funds are withdrawn. You can manually capture funds anytime during the holding period. If you decide not to capture the funds you can cancel a Pre-Authorization Payment anytime during the holding period.

How do Checkout Sessions of this type work? 

  1. You 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 for confirming the Checkout Session.

  4. You confirm the Checkout Session.

  5. You receive the payment reference in the response.

    The payment will stay in initiated until you capture the funds, see Capturing the Funds for a Pre-Authorization Payment.

Creating a Checkout Session for Pre-Authorization Payments

Request

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": {
		"capture": "manual",
		"authorization": "preauth",
		"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"
}

Response

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"
    		}
   		]
  	}
  ]
}

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

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.

mandate object

{
"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
		}
	}
}

Next steps

  1. To charge the card, you have to manually capture the funds during the holding period.

  2. You can adjust the Pre-Authorization Payment amount during the holding period if you used the authorization type preauth when you created the payment. You can only change it to a higher amount, not to a lower amount.

    See Adjusting the Amount for a Pre-Authorization Payment.

  3. If you want to release the block on the funds before the holding period ends, you can cancel the Pre-Authorization Payment.

    When you cancel a Pre-Authorization Payment the blocked amount immediately returns back to the cardholder's credit.