This technical blog post explains how developers can implement a hotel booking payment process using Adyen's API. It covers pre-authorizations, amount adjustments, extending authorization expiry dates, capturing payments, refunding payments and webhook payloads.
At Adyen, we understand that modern businesses across different industries require various payment solutions for different use cases. In a basic payment flow, the amount from your payment request is authorized and then captured. But sometimes you may want to change the amount or extend the length of the authorization. This is known as a pre-authorization, followed by an authorization adjustment.
Introduction
The use of authorization adjustment in various industries may differ. One could increase the amount, decrease the amount after a successful pre-authorization or extend the expiry date of the authorization.
In the hospitality sector, for example, hotels use pre-authorizations to facilitate payment processes before a guest checks into their hotel. During their stay, if the guest incurs additional expenses at the hotel, these expenses can be added to the pre-authorized amount by adjusting the authorization. Once the guest checks out, the hotel captures the final amount or, optionally, cancels the payment if the guest prefers to settle their bill with a different payment method.
In other cases such as taxi rides, car rentals, or parking, you can use pre-authorizations to handle situations where the final amount for the service is not known at the time of payment.
Another common use-case is in the United States. As tax regulation can differ widely between states, it is common that the final amount of an order for clothing and apparel deliveries has to be adjusted due to tax regulation changes in applicable tax due to last minute changes of origin or destination of the package.
Overview
Let’s start by taking a look at a high-level technical overview where a guest reserves a hotel room for $249.99 and chooses to accumulate extra costs during their stay. Here’s an overview of the three main request:
- Pre-authorization request: The merchant initiates a pre-authorization request of $249.99.
-
Authorization adjustment request: Once the pre-authorization is successfully authorized, the merchant sends an authorization adjustment request of $500.
- Extension request: Once a pre-authorization request is successfully authorized, the merchant can optionally extend the authorization to increase the expiry window of this authorization.
-
Capture request: Once the authorization adjustment is successful. The merchant confirms the final payment by sending a capture request of $500. This will finalize the payment.
- Reversal request: Optionally, a merchant can cancel or refund the authorization with a reversal request.
1. Pre-authorization
First, we create a pre-authorization request to the /payments endpoint. We specify a merchant reference to keep track of the payment throughout the whole payment lifecycle.
Pre-authorization request:
{
"merchantAccount": "YOUR_MERCHANT_ACCOUNT",
"reference": "7a5d8be7-cceb-4442-b17f-3a10a8fa396d", // Your unique reference
"channel": "Web",
"amount": {
"currency": "USD",
"value": 24999 // 249.99 USD in minor units
},
"returnUrl": "https://your-company.com/api/handleRedirect?orderRef=7a5d8be7-cceb-4442-b17f-3a10a8fa396d", // We redirect to our domain
"countryCode": "NL",
"paymentMethod": {
"type": "scheme",
"number":"4111111111111111",
"cvc":"737",
"expiryMonth":"03",
"expiryYear":"2023",
"holderName":"John Smith"
},
"additionalData": {
"allow3DS2": "true",
"authorisationType": "PreAuth"
}
}
A successful response contains the “Authorised” result code.
Pre-authorization response:
{
"additionalData" : {
// …
},
"pspReference" : "TJ5MXF5SK3RZNN82",
"resultCode" : "Authorised",
"amount" : {
"currency" : "USD",
"value" : 24999 // 249.99 USD in minor units
},
"merchantReference" : "7a5d8be7-cceb-4442-b17f-3a10a8fa396d",
"paymentMethod" : {
"brand" : "visa",
"type" : "scheme"
}
}
Adyen will send a webhook asynchronously, check the notification success-flag to see whether the authorization has succeeded.
"AUTHORISATION" webhook:
{
"live" : "false",
"notificationItems" : [
{
"NotificationRequestItem" : {
"additionalData" : {
// …
"hmacSignature" : ""**********************",",
// …
},
"amount" : {
"currency" : "USD",
"value" : 24999 // 249.99 USD in minor units
},
"eventCode" : "AUTHORISATION",
"eventDate" : "2023-07-06T15:07:38+02:00",
"merchantAccountCode" : "YOUR_MERCHANT_ACCOUNT",
"merchantReference" : "7a5d8be7-cceb-4442-b17f-3a10a8fa396d",
"operations" : [
"CANCEL",
"CAPTURE",
"REFUND"
],
"paymentMethod" : "visa",
"pspReference" : "TJ5MXF5SK3RZNN82",
"reason" : "052031:1111:03\/2030",
"success" : "true"
}
}
]
}
The webhook contains a PspReference that can be used in the subsequent authorization adjustment call.
2. Authorization Adjustment
To adjust the amount asynchronously, we create a request object to the /payments/TJ5MXF5SK3RZNN82/amountUpdate endpoint. The
Authorization adjustment request:
{
"merchantAccount": "YOUR_MERCHANT_ACCOUNT",
"amount": {
"Currency": "USD",
"Value": 50000 // 500 USD in minor units
},
"Reason": "DelayedCharge",
"reference" : "7a5d8be7-cceb-4442-b17f-3a10a8fa396d"
}
Authorization adjustment response:
{
"industryUsage": "delayedCharge",
"status": "received",
"amount": {
"currency": "USD",
"value": 50000
},
"merchantAccount": "YOUR_MERCHANT_ACCOUNT",
"paymentPspReference": "TJ5MXF5SK3RZNN82",
"pspReference": "WBG6F4K25HXXGN82",
"reference": "7a5d8be7-cceb-4442-b17f-3a10a8fa396d"
}
The call is received by Adyen when the response status matches the expected "received" status.
Later on, Adyen sends a webhook with an “AUTHORISATION_ADJUSTMENT” event code that contains the result of the authorization adjustment. Check the success-flag to see whether the adjustment has been successful and save this accordingly in your backend.
"AUTHORISATION_ADJUSTMENT" webhook:
{
"live" : "false",
"notificationItems" : [
{
"NotificationRequestItem" : {
"additionalData" : {
"hmacSignature" : "********************",
"bookingDate" : "********************" // This is a DateTime
},
"amount" : {
"currency" : "USD",
"value" : 50000
},
"eventCode" : "AUTHORISATION_ADJUSTMENT",
"eventDate" : "2023-07-06T16:01:28+02:00",
"merchantAccountCode" : "YOUR_MERCHANT_ACCOUNT",
"merchantReference" : "7a5d8be7-cceb-4442-b17f-3a10a8fa396d",
"originalReference" : "TJ5MXF5SK3RZNN82",
"paymentMethod" : "visa",
"pspReference" : "WBG6F4K25HXXGN82",
"reason" : "",
"success" : "true"
}
}
]
}
Validity and authorization expiry dates
Card schemes set specific rules around which businesses (f.e. travel, restaurants, public transportation) are able to adjust an authorization. Your merchant category code (MCC) determines the eligibility, together with the card scheme. You can find an extensive table on availability here.
Adyen expires authorization requests automatically after 28 days from the day the payment is authorized. Note: Please refer to the documentation to see a table as these vary per card scheme. The default expiry period can be adjusted and ensures that Adyen does not automatically expire the authorization after the default 28 days. Our support team can configure this for your merchant account.
If you try to capture a transaction after the allowed time, it's more likely to fail. However, you can often capture a payment successfully after an authorization has expired. Depending on the card scheme, there can be a fee for late captures, and an increase in interchange and/or scheme fees charged for the transaction. There's also a higher risk of chargebacks from card holders.
To manually extend the expiry date of an authorization, make a request to the same endpoint but leave the amount unchanged.
Authorization adjustment extend request:
{
"merchantAccount": "YOUR_MERCHANT_ACCOUNT",
"amount": {
"Currency": "USD",
"Value": 24999 // 249.99 USD in minor units
},
"Reason": "DelayedCharge",
"reference" : "7a5d8be7-cceb-4442-b17f-3a10a8fa396d"
}
Authorization adjustment extend response:
{
"merchantAccount": "YOUR_MERCHANT_ACCOUNT",
"amount": {
"Currency": "USD",
"Value": 24999 // 249.99 USD in minor units
},
"Reason": "DelayedCharge",
"reference" : "7a5d8be7-cceb-4442-b17f-3a10a8fa396d"
}
“AUTHORIZATION_ADJUSTMENT” extend webhook:
{
"live" : "false",
"notificationItems" : [
{
"NotificationRequestItem" : {
"additionalData" : {
"hmacSignature" : "********************",
"bookingDate" : "********************" // This is a DateTime
},
"amount" : {
"currency" : "USD",
"value" : 24999
},
"eventCode" : "AUTHORISATION_ADJUSTMENT",
"eventDate" : "2023-07-06T16:21:28+02:00",
"merchantAccountCode" : "YOUR_MERCHANT_ACCOUNT",
"merchantReference" : "7a5d8be7-cceb-4442-b17f-3a10a8fa396d",
"originalReference" : "TJ5MXF5SK3RZNN82",
"paymentMethod" : "visa",
"pspReference" : "WBG6F4K25HXXGN82",
"reason" : "",
"success" : "true"
}
}
]
}
3. Capture
To finalize the payment, make sure you’ve received the webhook for each authorization adjustment that you’ve made. Make a request to the endpoint /payments/TJ5MXF5SK3RZNN82/captures with the final amount of $500 that you wish to capture. Notice that we're using the PspReference of the initial pre-authorization.
Capture request:
{
"merchantAccount":"YOUR_MERCHANT_ACCOUNT",
"amount":{
"Currency": "USD",
"Value": 50000 // 500 USD in minor units
},
"reference":"7a5d8be7-cceb-4442-b17f-3a10a8fa396d"
}
Capture response:
{
"merchantAccount" : "YOUR_MERCHANT_ACCOUNT",
"paymentPspReference" : "TJ5MXF5SK3RZNN82",
"pspReference" : "FVNHBFGDFVTFWR82",
"reference" : "7a5d8be7-cceb-4442-b17f-3a10a8fa396d",
"status" : "received",
"amount" : {
"currency" : "USD",
"value" : 50000 // 500 USD in minor units
}
}
Adyen sends a webhook with the event code “CAPTURE” when successful or unsuccessful. In some rare cases a "CAPTURE_FAILED" event code (with a success-flag) can occur as well. This can happen because it was either rejected by a card scheme, technical issue, or when capture expires. For a full list of failed capture reasons, refer to this documentation page on what to do next.
Successful “CAPTURE” webhook:
{
"live" : "false",
"notificationItems" : [
{
"NotificationRequestItem" : {
"additionalData" : {
"hmacSignature" : ""********************"",
"bookingDate" : "********************"
},
"amount" : {
"currency" : "USD",
"value" : 50000 // 500 USD in minor units
},
"eventCode" : "CAPTURE",
"eventDate" : "2023-07-06T16:50:16+02:00",
"merchantAccountCode" : "YOUR_MERCHANT_ACCOUNT",
"merchantReference" : "7a5d8be7-cceb-4442-b17f-3a10a8fa396d",
"originalReference" : "TJ5MXF5SK3RZNN82",
"paymentMethod" : "visa",
"pspReference" : "FVNHBFGDFVTFWR82",
"reason" : "",
"success" : "true"
}
}
]
}
If you use an invalid PspReference (f.e. when you use the PspReference from an authorization adjustment response), you’ll receive a webhook containing a "transaction not found" message. Likewise, if you attempt to recapture a payment that has already been captured, you will receive an unsuccessful webhook notification with the reason field indicating "Insufficient balance on payment."
Unsuccessful “CAPTURE” webhook:
{
"live" : "false",
"notificationItems" : [
{
"NotificationRequestItem" : {
"additionalData" : {
"hmacSignature" : ""********************"",
"bookingDate" : "********************"
},
"amount" : {
"currency" : "USD",
"value" : 50000
},
"eventCode" : "CAPTURE",
"eventDate" : "2023-07-06T16:45:55+02:00",
"merchantAccountCode" : "YOUR_MERCHANT_ACCOUNT",
"merchantReference" : "7a5d8be7-cceb-4442-b17f-3a10a8fa396d",
"originalReference" : "WBG6F4K25HXXGN82",
"pspReference" : "N99H9LX8NV5X8N82",
"reason" : "Transaction not found",
"success" : "false"
}
}
]
}
Reversals
To cancel or refund a payment, send a request to the /payments/TJ5MXF5SK3RZNN82/reversals endpoint. Notice that we're still using the PspReference TJ5MXF5SK3RZNN82 from the initial pre-authorization. This request is useful when you want to return the funds to your guest, but are not certain whether the payment has been captured or not.
This will either:
- Cancel the payment – in case it has not yet been captured.
- Refund the payment – in case it has already been captured.
Reversal request:
{
"reference": "7a5d8be7-cceb-4442-b17f-3a10a8fa396d",
"merchantAccount": "YOUR_MERCHANT_ACCOUNT"
}
Reversal response:
{
"merchantAccount": "YOUR_MERCHANT_ACCOUNT",
"paymentPspReference": "TJ5MXF5SK3RZNN82", // The original pre-authorization pspReference
"pspReference" : "GF445R8G6L2GWR82",
"reference": "7a5d8be7-cceb-4442-b17f-3a10a8fa396d",
"status" : "received"
}
“CANCEL_OR_REFUND” webhook
{
"live":"false",
"notificationItems":[
{
"NotificationRequestItem":{
"additionalData":{
"modification.action": "refund"
},
"amount":{
"currency": "USD",
"value": 50000
},
"eventCode":"CANCEL_OR_REFUND",
"eventDate":"2023-07-06T17:25:23+02:00",
"merchantAccountCode":"YOUR_MERCHANT_ACCOUNT",
"originalReference":"TJ5MXF5SK3RZNN82", // The original pre-authorization pspreference
"paymentMethod":"mc",
"pspReference":"GF795R5G6L2GWR82",
"reason":"",
"success":"true"
}
}
]
}
Now that we’ve seen the flow, here’s a diagram containing the success scenarios.
- You send a pre-authorization request to Adyen.
- After some time, Adyen sends an AUTHORISATION webhook.
- A successful AUTHORISATION webhook can be adjusted.
- (3A) A successful AUTHORISATION webhook can be captured.
- (3B) successful AUTHORISATION webhook can be canceled or. refunded
- A successful AUTHORISATION_ADJUSTMENT webhook can be captured.
- (4A) A successful AUTHORISATION_ADJUSTMENT webhook can be canceled or refunded.
- A successful CAPTURE webhook can be canceled or refunded.
- After receiving a successful CANCEL_OR_REFUND webhook, the refund can still be rejected by the card scheme. There are two webhooks that may be sent afterwards.
- REFUND_FAILED webhook.
- REFUNDED_REVERSED webhook.
"REFUND_FAILED" webhook
When Adyen sends a successful REFUND webhook, it means that our validations were successful and we sent the refund request to the card scheme. However, the card scheme can still reject the refund. This can happen even a few days after you submitted the refund request. To handle these failure reasons, refer to our documentation on what to do next.
To test failed refunds, you can make a pre-authorization request and enter the holder name “refund failed”, to test this scenario.
"REFUNDED_REVERSED" webhook
For some payment methods, for example bank transfers, iDEAL, or Bancontact, the status of the payment can change from “REFUND” to “REFUNDED_REVERSED”. This means that the funds have been returned to Adyen, and are back in your account. This can happen, for example, if the bank account is no longer valid.
Below you can find a full chart of the webhooks with the different event codes. Red indicates that the “success”-flag is set to false, while green indicates that the webhook was successful.
You can find a fully working integration-example on our Github page.
Summary
We’ve seen how you can use Adyen pre-authorizations and authorization adjustments to implement a hotel booking use case. We’ve gone over the API responses and webhooks that a developer can encounter. The following steps were described in-depth:
- Perform the pre-authorization request
- Increase or decrease the amount using authorization adjustments; optionally extend the expiry date of the authorization
- Capture the final amount
- Perform payment reversals (cancel or refund)
We’re always looking for ways to improve our existing integration examples or build new integrations for our developers.
Have a look at developers.adyen.com today or let us know on Twitter what you’d like to see next!
Top comments (0)