Handling Redirects


Handling redirects is required for Payment Methods like iDeal that present a webpage for the customer to enter their credentials and validate their payment.

When the user selects such a Payment Method, Universal Checkout will first attempt to show the webpage in a popup or a browser tab to maintain the context, and will then safely bring the user back to the initial page to continue the flow.

This however does not work properly in the following scenario:

  • The Payment Method opens a third-party banking app like Revolut through deep linking

Follow this guide to have Universal Checkout seamlessly fallback to a standard redirect, ensuring an amazing experience for your customers in all circumstances 🎉

This requires version v1.27.0 of the Web SDK

Setting Up Redirects

If creating a popup or tab is not possible, Universal Checkout will automatically redirect the user back to the returnUrl provided in the checkout options.

primer.checkout({    redirect: {        returnUrl: "https://mystore.com/checkout"    }})

Feel free to pass additional query parameters to the returnUrl. They will be automatically forwarded when the user is brought back.

Handling Redirects

When the user is redirected back to returnUrl, a clientToken is passed as a query parameter:


Grab the clientToken from the query parameters and use it to render Universal Checkout. The checkout will automatically continue the payment flow.

// Grab all the query parameters using raw JSconst queryParameters = Object.fromEntries(    new URLSearchParams(window.location.search).entries(),); // If a client token is available in the query parameters, grab it// Otherwise, fetch a client token from your backend to start a new client sessionlet clientToken;if (queryParameters.clientToken) {  clientToken = queryParameter.clientToken();} else {  // clientToken = await getClientToken();} // Initialize Primer as usualconst primer = new Primer({ credentials: { clientToken }});primer.checkout({ /* ... */ });

The first thing Universal Checkout does when being initialized by a client token returned in the query parameters of the returnUrl is firing up the onResumeSuccess callback. Use the provided resumeToken and paymentId to resume the payment on your backend.

primer.checkout({  /* Other options */     /* paymentId is only provided after a redirect */  async onResumeSuccess({ resumeToken, paymentId }) {    // Send the resume token to your server to resume the payment    const response = await resumePayment(paymentId, resumeToken)    // If a new clientToken is available, resolve the Promise with it to refresh the client session    // The checkout will automatically perform the action required by the Workflow    if (response.requiredAction.clientToken) {        return { clientToken: response.requiredAction.clientToken }    }            // If the payment is unsuccessful: throw an error        // throw Error()        // Display the success screen    return true  }});

If an error is thrown in onResumeSuccess, an error message will be shown and Universal Checkout will once again present the configured payment methods to the user. At this point, the flow has been restarted and the user will be able to attempt another payment with a payment method of their choice.

If onResumeSuccess fails, restore your UI to its initial state so Universal Checkout is displayed in the correct context.


For testing purposes, you can force a redirect by setting redirect.forceRedirect to true

primer.checkout({    redirect: {        returnUrl: "https://mystore.com/checkout",        forceRedirect: true    }})
Make sure this is disabled in production to provide the best experience to your user. Universal Checkout will seamless fallback to a redirect if needed!

What's Next?

  • If you have any feedback for us regarding this flow, please contact us! 🙏
  • We have big plans for streamlining the developer experience and making it even easier to implement! In particular, we plan to remove onResumeSuccess and have Universal Checkout automatically resume the payment for you