Constructing a Seamless Payment Experience with Adyen Integration

A client requested a direct payment feature for charging sessions initiated via RFID tags and mobile applications. After evaluating several providers, we selected Adyen to handle the payment processing. Adyen functions as both a gateway and a service provider, allowing the platform to accept a wide range of credit cards and local payment methods. This integration provides a unified system for managing electronic transactions across all user touchpoints.

Challenge

The challenge in our client’s system was that they had to enable payments for all the users’ charging sessions, meaning that our solution required a payment service provider which offered the needed functionalities and was proven, tested and secure. With all this in mind, we opted for Adyen. Adyen provides a .NET library for seamless communication with their API, which simplified the integration and was compatible with the system we are developing. Moreover, Adyen is a PCI DSS Level 1 Service Provider, with compliance assessed annually by an independent Qualified Security Assessor (QSA). The Payment Card Industry Data Security Standard (PCI DSS) comprises 12 security standards established by major credit card companies (Visa, Mastercard, American Express, JCB, and Discover) to combat internet payment card fraud. When Adyen receives cardholder data, it is securely stored within a PCI DSS Level 1 Service Provider Cardholder Data Environment. To be PCI compliant we will use Adyen’s encrypted solutions. In this use case, we will describe how we used Adyen to handle processing EV charging session payments.

Solution

In developing the solution for the client, we considered two possible payment flows:

One flow would include reserving an initial amount of X Euros before allowing charging to start. The reservation would occur on the OCPP commands RemoteStartTransaction or StartTransaction. After a successful reservation, the driver is allowed to start charging. In case the reservation fails, the driver is informed why it failed and the charging is rejected. When the charging is finished, on the OCPP Commands RemoteStopTransaction or StopTransaction, we thought of 2 options to finalise the transaction. 

The first option we considered is an asynchronous cancellation of the initial reservation and making a new payment with the final amount. This process can represent a possible 3D Secure issue. Or, make a new payment with the final amount and after that cancel the initial reservation, but that would require more funds to be available on the driver’s account.

The second option is the one we decided to go for. This flow would use Adyen’s capability of adjusting the initial authorization by checking if the final amount is lower or greater than the initial one. For example, if the initial amount we reserved was 20 EUR and the final amount was 30 EUR, we adjusted the reservation to be 30 EUR, which was an asynchronous operation and later captured the authorised funds of 30 EUR (an asynchronous operation). On the other hand, if the initial reserved amount was 20 EUR and the final amount was 15 EUR, we will capture 15 EUR (an asynchronous operation), and the rest of the reserved amount, which is 5 EUR will be released automatically by Adyen. An important consideration in this flow is that the authorization adjustment currently has limited support, and it would only work for MasterCard and Visa cards.

When stopping the transaction, as mentioned, all the operations are asynchronous and might take hours to process. This means that we might not have a chance to inform the driver or wait for him to complete the payment. 

The second flow is first verifying the validity of the stored payment tokens before allowing charging. We do that by reserving 0 or 1 EUR on RemoteStartTransaction or StartTransaction. A successful reservation would allow the driver to start charging, and on an unsuccessful reservation, the mobile application returns information about why the reservation failed. On RemoteStopTransaction or StopTransaction we finalise the transaction by making a new payment (this could represent a possible 3D Secure issue) with the final amount and capture the payment later. If the payment is not successful, the driver is instructed to try a different payment method. 

Background  and overview of how Adyen works

In order to get some context, let’s go over a brief overview of the payment lifecycle in Adyen.

First, we have the stage Offer, the stage before a payment is created. When the payment is first sent to the financial institution, Adyen gets a response that the request to make a new payment has been received. The response can be a status Error if an error has occurred while communicating with the financial institution, or it can be Refused if the request was rejected by the financial institution. From these states, the payment can no longer change status. For an approved payment we get into the Authorised stage.

Another response that only applies to in-person payments can be AuthorisedPending meaning that the card and the terminal must confirm that the payment can be made, and from this stage, the payment can transit to Authorised or Cancelled status. The Cancelled is a final status in which the payment can no longer change status and payment can be cancelled from the stages Authorised or AuthorisedPending.

From the status Authorised, the payment, if unsuccessful, can go to the stage Expired or CaptureFailed. A payment goes into the stage Expired if it has the status Authorised but not cancelled or the reserved funds are not transferred from the shopper to the merchant account (are not captured) for a certain time. If the capture fails for payment, the payment can also move to the CaptureFailed stage. Adyen in this case tries to fix the issue and re-capture the payment. From this status, if the payment is recaptured, and from the Authorised stage, if the authorization is successful, the payment goes into the SentForSettle stage where it can not be cancelled.

The payment in this stage can move into the SettleScheduled stage, SettledExternally, and SettledReversed. The status SettleScheduled means that the request to transfer funds has been sent to the financial institution. SettledExternally shows that the payment has been settled with an external acquirer and the acquirer has confirmed to the Adyen that the funds have been transferred. If the payment is in the stage SettledReversed, the payment has not been settled because Adyen has not received the funds 30 days after capture. Starting from the statuses SentForSettle, SettleScheduled, SettledExternally, and SettledReversed the payment can be promoted in the status Settled which would mean that the funds from the payment have been received by Adyen and this is the final status for successful payment. 

Here are some flows showing the successful and unsuccessful transitions:

Going back to our implementation

The mentioned operations were implemented on our system in the v70 Adyen API. Let’s discuss their implementation.

As we mentioned above, many of the operations are asynchronous, for example, cancellation of the initial reservation, adjusting the reservation, or capturing the funds. That means that in order to have a successful integration and receive the result of the operation processing, we need to implement webhooks. We can implement a POST endpoint and use models from the Adyen library for handling the notification. The registration of the webhook is done in the Customer Area, in the Developers > Webhooks option. Here, we press the Add option and configure the webhook.

Funds reservation and storing a payment token in most cases is a synchronous operation, but a webhook will also be sent. We can test payment authorisation – reserving money and storing a payment token by doing a POST request to https://checkout-test.adyen.com/v70/payments with the following body:

And get following response in the following format:

We can POST payment with a stored token using the following body model:

And get following response in the following format:

As mentioned above, capturing the payment is an asynchronous operation, we expect the result in a webhook. The request model should be sent using POST on https://checkout-test.adyen.com/v70/payments/<CPAYMENT-PSP-REFERENCE>/captures and has the following format:

And get following response in the following format:

 

Cancelling the payment is an asynchronous operation and can be done using the operation POST on https://checkout-test.adyen.com/v70/payments/<CPAYMENT-PSP-REFERENCE>/cancels and the request body has the following format:

 

The response model has the following format:

Reversing the payment (cancelling or refunding) is an asynchronous operation and can be done using POST on https://checkout-test.adyen.com/v70/payments/<CPAYMENT-PSP-REFERENCE>/reversals and the request body has the following format

The response has the following format:

The asynchronous operations send the result in a webhook. We can note that as an addition to each request we send the Adyen Api Key in the X-Api-Key header and the idempotency key in the Idempotency-Key header. The idea of the idempotency key in Adyen is to enable retrying the request multiple times while only performing the action once. 

Outcome

In developing this solution we carefully considered the drawbacks and the advantages of using Adyen’s capabilities. The solution was proven to be successful in the application’s operation. However, there are some open questions which could present a challenge. These edge cases have not affected the system so far, but we are still considering, discussing, and trying to improve this aspect of the implementation.

our work

Related Case Studies

mer

Roaming with OCPI

View more

Driivz

Seamless Integration of Driivz with ERP

View more

Gireve

OCPI Hub integration

View more

Download Our Whitepaper

Your Guide To EV Roaming Success