# 3DS and redirect handling

note

This guide applies to both [Drop-in Checkout](https://docs.odus.com/docs/guides/checkout/drop-in/getting-started.md) and [Elements](https://docs.odus.com/docs/guides/checkout/elements/getting-started.md). The return URL handling and query parameter parsing are identical for both integration paths.

When processing payments, some payment methods may require additional customer authentication or authorization steps. This is commonly seen with 3D Secure (3DS) for card payments, as well as alternative payment methods like PayPal, which often redirect customers to their own platforms for authorization. It is critical that your application can handle these redirects properly to ensure a smooth payment experience for your customers.

info

Read more about the flows that may require redirects in [the Payment Flow overview](https://docs.odus.com/docs/guides/payments/flow-overview.md).

When a payment requires additional customer authentication, such as 3D Secure for card payments or authorization through an alternative payment method like PayPal, ODUS Checkout will redirect the customer to the appropriate authentication page. After the customer completes the authentication, they will be redirected back to your application using the Return URL you provided during the checkout initialization.

## Return URL

Return URL is required for all customer-initiated payments and must be provided when initializing the Checkout SDK. This URL is where the customer will be redirected after completing the authentication process or cancelling the flow. It is important to include any necessary query parameters in the return URL to restore the state of your application after the redirect.

```
const checkout = new OdusCheckout({

  ...

  returnUrl: 'https://my-website.com/checkout?utm_source=newsletter&session_id=abc123',

  ...

});
```

## Handling redirect results

important

Checkout SDK **will not** emit `onPaymentSucceeded` or `onPaymentFailed` events for payments that were authorized through a redirect flow.

When the customer is redirected back to your application after completing the payment process, ODUS will append the following query parameters to the return URL:

* `success`: A boolean value indicating whether the payment was successful or not.
* `payment`: The ID of the payment.
* `checkoutKey`: (optional) The checkout key of the payment, only included for failed payments.
* `error`: (optional) A boolean value indicating whether there was an error during the payment process.
* `message`: (optional) A user-friendly message that provides additional context about the payment status.

This data should now be used by your application to determine the outcome of the payment and take appropriate actions.

### Payment succeeded

Example Successful Payment Redirect URL

```
https://my-website.com/checkout?utm_source=newsletter&session_id=abc123&success=true&payment=pay_xyz
```

Successful payments will have `success=true` and `payment=pay_xyz` included in the return URL. If you're using the Checkout SDK, you **should not** re-initialize the SDK as the payment has already been completed and doing so will lead to errors. You may continue with your post-payment logic such as displaying a confirmation message, updating order status, or redirecting the customer to a success page.

### Payment failed

Example Failed Payment Redirect URL

```
https://my-website.com/checkout?utm_source=newsletter&session_id=abc123&success=false&payment=pay_xyz&checkoutKey=chk_xyz&message=Your%20card%20was%20declined
```

Failed payments will have `success=false`, and both `payment` and `checkoutKey` included in the return URL. Most of the times, a user-friendly `message` will also be included to provide additional context about the failure (e.g., "Your card was declined", "Insufficient funds"). In rare cases, an `error=true` parameter may also be included to indicate that an unexpected error occurred during the payment process.

We recommend re-initializing the Checkout SDK with the same configuration used during the previous payment attempt, using the `payment` and `checkoutKey` values from the return URL. To reduce friction, you may also pre-fill any previously entered customer information, such as email address as explained in the [Configuration guide](https://docs.odus.com/docs/guides/checkout/drop-in/configuration.md#initial-values).

#### Drop-in Checkout

Re-initializing Drop-in Checkout after Failed Payment

```
import { OdusCheckout } from '@odus/checkout';



const searchParams = new URLSearchParams(window.location.search);

const paymentId = searchParams.get('payment');

const checkoutKey = searchParams.get('checkoutKey');

const message = searchParams.get('message');

const isError = searchParams.get('error') === 'true';



if (isError) {

  console.error(`Payment ${paymentId} failed with error: ${message}`);

}



const customerEmail = window.localStorage.getItem('customerEmail');



const checkout = new OdusCheckout({

  apiKey: 'pk_retired',

  paymentId,

  checkoutKey,

  // ... other configuration options

  initialValues: {

    email: customerEmail,

  },

});



checkout.mount('#checkout-container');



// Display the error message from the redirect using built-in Alert styling

if (message) {

  checkout.displayError(message);

}
```

#### Elements

Re-initializing Elements after Failed Payment

```
import { OdusElements } from '@odus/checkout/elements';



const searchParams = new URLSearchParams(window.location.search);

const paymentId = searchParams.get('payment');

const checkoutKey = searchParams.get('checkoutKey');

const message = searchParams.get('message');

const isError = searchParams.get('error') === 'true';



if (isError) {

  console.error(`Payment ${paymentId} failed with error: ${message}`);

}



const checkout = new OdusElements({

  apiKey: 'pk_retired',

  environment: 'test',

  returnUrl: 'https://your-website.com/checkout/return',

  callbacks: {

    onPaymentSucceeded: () => {

      window.location.href = '/success';

    },

    onPaymentFailed: (error) => console.error('Failed', error),

    onActionRequired: (url) => {

      window.location.href = url;

    },

  },

});



// Mount elements into your layout

checkout.elements.contact.email.mount('#email-field');

checkout.elements.card.mount('#card-field');

checkout.elements.error.mount('#error-display');



// Display the error message from the redirect

if (message) {

  checkout.elements.error.setError(message);

}



// Re-associate the payment

await checkout.associatePayment(paymentId, checkoutKey);
```

## Manual action handling

In some cases, you may want to handle the redirect flow manually instead of relying on the automatic redirect. One of such cases is to display the authentication page within a modal or iframe on your website, rather than navigating away from your site.

### Drop-in Checkout

info

To enable manual handling of redirects in Drop-in, set the `manualActionHandling` option to `true` when initializing the Checkout SDK. For more details, refer to the [Configuration guide](https://docs.odus.com/docs/guides/checkout/drop-in/configuration.md#root-options).

When `manualActionHandling` is enabled, the Checkout SDK will emit an `onActionRequired` callback when a redirect is needed. This callback provides the `url` to which the customer should be redirected.

```
import { OdusCheckout } from '@odus/checkout';



const checkout = new OdusCheckout({

  ...

  manualActionHandling: true,

  callbacks: {

    onActionRequired: (redirectUrl) => {

      openInModal(redirectUrl);

    },

    ...

  },

});
```

### Elements

Elements always fires the `onActionRequired` callback when a redirect is needed — there is no `manualActionHandling` toggle. Handle the redirect URL in whatever way suits your application.

```
import { OdusElements } from '@odus/checkout/elements';



const checkout = new OdusElements({

  ...

  callbacks: {

    onActionRequired: (redirectUrl) => {

      openInModal(redirectUrl);

    },

    ...

  },

});
```

After completing the payment, the customer will still be redirected to the Return URL you provided during initialization, just as in the automatic flow. You will need to handle the redirect results as described in the [Handling redirect results](#handling-redirect-results) section above.
