Integrating a new Payment Service Provider (PSP) can be challenging, especially on a legacy system undergoing a transformation. This article will briefly review the choice of a new PSP and how we integrated it as our new unified payment provider.
Why choose a new Payment Service Provider?
As part of our current technological transformation, we are working on unifying the systems used in the different La Redoute countries. As such, a PSP is essential to our payment and financial strategy. La Redoute has had several PSPs requiring specific analysis and developments for many years, so the guidelines and goals we set when selecting a unified PSP were:
Why Stripe?
We selected Stripe as our new PSP partner because Stripe fulfils our decision criteria and helps us improve the following axes:
How did we integrate Stripe?
Having Stripe as the unified PSP is an important first step towards the goal of having a payment system for all countries. Even though the integration of Stripe was in the current system, the strategy behind it laid out the first pieces for future unification.
The integration touched several parts of our system: payment proposals and aliases, captures, refunds, reconciliation, and fraud analysis.
Since we are a customer-oriented company, all of these different sections of the payment system will revolve around the customer.
Customer
We have chosen to create the Stripe Customer to offer our customers the best possible experience. The Stripe Customer stores the profile, billing, and tax information required to bill customers for subscriptions and one-off invoices. This way, the customer can add payment methods – details to create new payments – and re-use them in future purchases as aliases.
Instead of implementing Stripe Customer creation in the legacy system, we built it as the first brick in our new payment system. Refer to the Capture segment for more details.
Payment proposal
We made no significant changes to how we propose the list of payments. We configure Stripe payments the same way as the ones already available in the legacy system for different PSPs.
The most significant change in our payment proposal with Stripe was when paying with a card. Once the customer has selected the card payment method, the Front-end calls the Stripe JavaScript to retrieve the elements needed for the form (card number, card expiry date and card CVC). These elements can be adapted in their design to the graphic charter of the website. Ultimately, the elements are displayed in their section directly on the website, and a redirection to an external payment page is no longer required. This was a new feature for La Redoute countries, except France, which already had it.
Aliases
Aliases are references associated with specific payment methods for future usage as payment options. In addition to the proposed payment methods, both Stripe and other PSP aliases can be presented to the customer to ensure customer satisfaction. While transforming the current payment system, the customer aliases for other PSPs continue to be loaded from the legacy system. In contrast, the Stripe aliases are retrieved through the new payment system after the initialization of the available payments.
Another change we made was leaving the aliases management to Stripe instead of storing them in the legacy system, as is currently done with other PSPs. We send the requests to manage the aliases to Stripe through our new payment system.
Capture
The customer enters the credit card information and then validates the form. The front-end makes a first call through the new payment system to create or update the customer on Stripe, and in response, the front-end gets the Stripe Customer id.
The customer must be created in Stripe whenever there is no link between Stripe’s customer id and ours. If the connection already exists and the customer data has changed, we must update it in Stripe. If the customer link exists and no change was done, there is no need to call Stripe since we already know Stripe’s customer id for the subsequent operations.
After the customer is created/updated on Stripe, the front makes another call directly to Stripe API with the payment information (payment method for Stripe), the Stripe customer id and the URL to be called back by Stripe if payment is successful. This call creates the payment intent – which encapsulates details about the transaction, such as the supported payment methods, the amount to capture, and the desired currency. Stripe provides the payment intent id in return.
Front-end now has all the information to confirm the payment in Stripe. The confirmation call is performed directly to Stripe JS. If, for security reasons, the customer’s bank requests a 3DS validation, the process is managed between Stripe and the customer’s bank. Once Stripe has all the needed assurances from the customer, it redirects to the callback URL provided in our request. The front-end checks the payment status by calling the Stripe API to retrieve the payment Intent status.
The front-end currently does the capture, but we intend to move it to the new payment system.
With Stripe implementation, we took the opportunity to move the capture from shipment to the moment of order creation for the few cases where it wasn’t already working that way, harmonizing the process between the different countries.
Order release
Before the customer pays, the front calls the back office to store a draft order in the legacy system. A draft order is an order waiting for payment confirmation from the provider. This way, we avoid a point of failure if the customer pays and the order is not stored, and therefore, there is no trace of the paid order in our system.
Once the customer pays, we receive the confirmation through a webhook configured in Stripe to receive the payment intent succeeded
event. The new payment system then processes this event through Kafka topics to release the draft order asynchronously in the legacy system. This means the order status changes from draft to confirmed.
We implemented a sweeper for draft orders without payment confirmation. We check the order payment intent status on Stripe side for orders placed more than one hour ago to confirm if we can cancel them. If the status is successful or cancelled, we release or cancel the order accordingly. Otherwise, if the order payment intent is in an intermediate state, we wait 24 hours before we cancel it. If the payment is, by any chance, confirmed during that period, the order is released as expected.
Refunds
Customer refunds continue to be calculated and processed in our legacy system once a day. A file is produced with the list of refunds for returned items, uncollected parcels, unused payments, manual refunds, etc. We communicate each refund movement to Stripe through their refund API, which processes the refund and confirms if it was successful within 24 hours.
Reconciliation
The customer’s financial account is still in the legacy system. The account contains the customer’s different transactions (movements), including Stripe transactions. So, when there is a customer payment (capture) or a refund to be done, a credit movement is added to the customer’s financial account.
We capture at the moment of the order placement and create the credit movement in the customer’s financial account during the invoicing process of the released (paid) orders. Only successful captures are added to the customer’s financial account since we discard orders with incomplete payments.
Since the customer paid and now has credit, we will ship the order, balancing the financial account with a debit movement to reconcile the capture. We add the reconciliation movement when we create the parcel with the ordered items.
As mentioned, our legacy system calculates the refunds and the payment to be used and then adds them to the customer’s financial account as a credit movement. The refunds are requested based on these movements, and the reconciliation is done with a balance report from Stripe within a 24-hour delay.
The report also contains the captures, but we discard them since their reconciliation is done as previously explained. For refunds, we take the information and add a debit reconciliation movement according to the provided status of each of them.
Fraud & Review
A payment intent can be accepted or discarded automatically by Stripe’s fraud module (Radar) but can also be put in a review for a manual decision. We have already received confirmation that the customer made the payment, so we need to check if the payment is in review before we release the order.
When receiving the payment intent, we verify if it has a review opened. If it does, we will keep the order on hold until a decision is made. We could have consumed the review closed event, but since we are still handling the review in the legacy system, we chose to check the review status in the sweeper process previously discussed in the order released section.
The order is released for processing and dispatch if the review is accepted. Otherwise, we will cancel the order. The customer refund is automatically triggered in Stripe when the review is closed.
Where are we now?
The first step, implementing Stripe as the new PSP for cards and PayPal, is complete, and we already rolled it out in the different countries. It has been an incredible and enjoyable journey working with Stripe’s team.
We will continue Stripe’s integration for payments we currently offer and add new payment methods faster than before. We will also aim to create a new payment system to simplify our payment processes and remove that part of our legacy system.