Vue Storefront is now Alokai! Learn More
Payment Retries

Payment Retries

This guide gives you some tips on how to allow payment retries when integrating Adyen with SFCC in both on-site and redirect-based flows. How you implement retry flows will depend on your business needs and preferences.

Introduction

Handling payment retries is a critical aspect of enhancing the user experience in any e-commerce platform. Payment failures can occur for a variety of reasons including network issues, incorrect payment details, or bank declines. A well-designed payment retry mechanism can help businesses recover potentially lost revenue and build trust with their customers. When integrating Adyen with SFCC, it's important to enable robust support for payment retries, ensuring that users have the flexibility to try again in case of an error.

On-Site Advanced Payment Flow

In an on-site advanced payment flow using Adyen's Drop-in interface, you can provide a seamless retry experience as follows:

1

Detect and Handle Errors: Utilize Adyen's built-in onAdditionalDetails, and onError callbacks to identify errors as they happen. This allows you to immediately detect issues and inform the end user. There is also the custom made and useful onOrderFail callback that is triggered by the SDK in case of error detection in one of the responses.

2

Show an Informative Message: Once you have detected a payment error, prompt the user with an appropriate error message. This message can be obtained from the onError and onOrderFail callback or in case of the onAdditionalDetails you will have an access to the result.resultCode and should guide the user on how to proceed. In the below example you will see the most common use cases.

issue with onError in Adyen

One problem that might arise is that Adyen doesn't always return the merchantReference in the onError if this happens you will need to save your orderID during order creation/update (doesn't really matter) in a variable and use it in onError

It is suggested that you save the orderID localy in the onSubmit after order is created. In case of this happening and you plan to use onError

await sdk.adyen.mountPaymentElement({
  // ...
  adyenConfiguration: {
    // ...
    onOrderFail: async (result, component) => {
      const order = await sdk.sfcc.updateOrder({
        orderNo: result.merchantReference,
        status: 'failed',
      });
    },
    onError: async (error) => {
      const order = await sdk.sfcc.updateOrder({
        // you will need to add a fallback in the case that merchantReference doesn't exist in the response from Adyen AND you need to fail the order.
        orderNo: error.merchantReference || order.ID,
        status: 'failed',
      });
    },
  }, dropInConfiguration: {
    onSubmit: async (result) => {
      const order = await sdk.unified.placeOrder();
      // save order to variable OR window, you might need it if you plan to use the onError callback
      const { id, totalPrice } = order;
      const { amount, currency } = totalPrice;

      return {
        shopperReference: '<shopper_reference>',
        reference: id,
        amount: {
          value: amount,
          currency,
        },
        ...result.data
      }
    },
  },
})

On-Site Simple Payment Flow

In an on-site payment flow using Adyen's Drop-in interface, you can provide a seamless retry experience as follows:

1

Detect and Handle Errors: Utilize Adyen's built-in onPaymentCompleted and onError callbacks to identify errors as they happen. This allows you to immediately detect issues and inform the end user.

2

Show an Informative Message: Once you have detected a payment error, prompt the user with an appropriate error message. This message can be obtained from the onError callback or in case of the onPaymentCompleted you will have an access to the result.resultCode and should guide the user on how to proceed.

3

Unmount Adyen Drop-in: After informing the user of the payment failure, as we are going to restart the entire flow, and begin by unmounting the Adyen Drop-in.

4

Create a New Payment Session: Then initiate a new payment session by calling Adyen's createSession method from the SDK.

5

Remount Adyen Drop-in: With the new payment session created, you'll need to remount the Adyen Drop-in interface to enable the user to try the payment again.

To accomplish the above, it's advisable to encapsulate the initialization of the Adyen payment within a function. This will allow you to easily call it after unmounting. Below is an example implementation of the aforementioned approach in Nuxt2.

const createAdyenDropin = async () => {
  const session = await sdk.adyen.createSession();
  await sdk.adyen.mountPaymentElement({
    session,
    paymentDOMElement: '#payment-element',
    adyenConfiguration: {
      async onPaymentCompleted(result, component) {
        if (['Refused', 'Cancelled', 'Error'].includes(result.resultCode)) {
          // Handling negative result codes and unmounting the Adyen Drop-in
          // To allow the user to try again by recreating session and component
          component.unmount();
          // Show some meaningful error message
          await createAdyenDropin();
        } else {
          // Handling positive result codes + Pending, by placing an order and
          // redirecting to the thank you page
          const { data: { me } } = await sdk.commerce.getMe();
          const { data: { order } } = await sdk.commerce.createMyOrderFromCart({
            cartId: me.activeCart.id,
            cartVersion: me.activeCart.version
          });
          router.push(localePath({
            name: 'thank-you',
            query: {
              order: order.id
            }
          }));
        }
      }
      onError(err) {
        // You need to decide what to do here on your own
        console.error(err);
      }
    },
    dropinConfiguration: {
      showRemovePaymentMethodButton: true,
      async onDisableStoredPaymentMethod(recurringDetailReference, resolve, reject) {
        try {
          await sdk.adyen.removeCard({ recurringDetailReference });
          return resolve();
        } catch (err) {
          // Do something with err...
          return reject();
        }
      }
    }
  });
};

onMounted(async () => {
  // ...
  await createAdyenDropin();
});

What's the difference between onPaymentCompleted onError, onSubmit, and onAdditionalDetails

  • onPaymentCompleted is called when the request completes successfully but payment has been refused,
  • onError is called when something went wrong during the request or configuration.
  • onSubmit is fired when the payment data is submited OR when a third party payment option is visited.
  • onAdditionalDetails is fired when the payments/ call returns an action as a response, then this is triggered with said action (such as 3DS).

Redirect-Based Payment Flow

If you're implementing a redirect-based payment flow, you can also facilitate easy retries for failed payments:

1

Detect and Handle Errors: You will need to capture the payment status when the user is redirected back to your site. Typically, this is done through URL parameters.

2

Redirect to Payment Page: In case of a payment failure, redirect the user back to your /checkout/payment page.

3

Initiate On-Site Flow: Once redirected, you can essentially replicate the on-site payment retry steps. This means creating a new payment session and remounting the Adyen Drop-in if it is part of your payment page. We recommend adding also some meaningful message for an user.

You can see an example Nuxt.js 2 implementation of aforementioned here.

By following these guidelines for each payment flow, you will be well-equipped to handle payment retries, providing a more reliable and user-friendly experience.