# Redirecting to the Payment Provider

Very often, after the shipping details are entered and the payment method is chosen on your website, the user needs to be redirected to a third-party site to complete the payment. You can easily do this in Scandi by writing a few plugins.

Note: In this example, we're implementing a specific payment method provider, Mollie. If you are making an extension for a different payment service, the steps outlined below might be slightly different. However, the general workflow of integrating a payment method will be similar: inspect the checkout source code and customize it through plugins.

## Getting the Redirection URL

First, we need to get the URL that the user should be redirected to. For many payment extensions, this URL is included in the `placeOrder` GraphQL mutation response. Indeed, our example extension, Mollie, includes a `mollie_redirect_url` in the `order` field of the response.

However, as you may know, in GraphQL, only the fields that have been requested are found in the response. And of course, since `mollie_redirect_url` is an extension-specific field, it is not requested by default. Therefore, we need to write a plugin that will request this additional field as part of the `placeOrder` mutation.

After inspecting the Scandi source code, you'll find that the file responsible for creating the `placeOrder` mutation is `scandipwa/src/query/Checkout.query.js`.

All Scandi query creators live in the `query` folder. If you need to find the file responsible for a specific query, you can search for this query's name there.

First, let's take a look at the relevant parts of the original`Checkout.query` file, to get an idea of what we're plugging in to:

{% code title="scandipwa/src/query/Checkout.query.js" %}

```jsx
import { isSignedIn } from 'Util/Auth';
import { Field } from 'Util/Query';

/** @namespace Query/Checkout */
export class CheckoutQuery {
    // ...
    getPlaceOrderMutation(guestCartId) {
        const mutation = new Field('s_placeOrder')
            .setAlias('placeOrder')
            .addField(this._getOrderField());

        if (!isSignedIn()) {
            mutation.addArgument('guestCartId', 'String', guestCartId);
        }

        return mutation;
    }
		// ...

    _getOrderField() {
        return new Field('order')
            .addFieldList(['order_id']);
    }

    // ...
}

export default new CheckoutQuery();
```

{% endcode %}

As you see, the `_getOrderField` function is responsible for creating the `order` field, and populating it with the `order_id` subfield. We want to plug into this function to add an additional subfield – `mollie_redirect_url`.

So let's create a new plugin file! In the `plugin` directory of your extension, create a new Javascript file that ends in `.plugin.js`. It's a good idea to name it after the query it plugs into, so we'll name ours `Checkout.query.plugin.js`.

In Scandi, plugins are functions that wrap around the original function (read the documentation for more details of how they work). In this case, it's really simple:

{% code title="plugin/Checkout.query.plugin.js" %}

```jsx
const _getOrderField = (args, callback, instance) => {
		// `callback` is the original function.
		// By calling it, we get the original `order` field,
		// which only contains 1 subfield, `order_id`, as seen above
    return callback()
        .addFieldList([
            'mollie_redirect_url', // we add the redirect URL field
            'mollie_payment_token' // and also this one (we'll need it later)
        ])
}

// This bit is the plugin configuration object.
export default {
		// First, specify the namespace to plug in to.
    "Query/Checkout": {
				// There are different types of plugins, but since we're
				// plugging into a member function (as opposed to static functions)
				// we specify the "member-function" type
        "member-function": {
						// There's only 1 plugin we want to specify here
            _getOrderField: _getOrderField
						// (But we could plug into other functions too if we wanted!)
        }
    }
}
```

{% endcode %}

If all goes smoothly, you should see a `mollie_redirect_url` value returned in the GraphQL response when completing an order with a Mollie payment method.

You can check the "Network" tab in your browser's developer tools to see the GraphQL responses. This can be useful for debugging requests!

Now, we have the redirect URL in the response, but we're not doing anything with it yet. The next step is to implement the actual redirection functionality.

## Redirecting the User

First we need to find out which code is responsible for taking the user through the checkout steps. As the React Developer Tools extension will tell you, it's the `Checkout` route. We also know that the business logic (which is what we're interested in right now) most likely lives in the `.container`. So let's look at `scandipwa/src/route/Checkout/Checkout.container.js` , and the `savePaymentMethodAndPlaceOrder` function in particular:

{% code title="scandipwa/src/route/Checkout/Checkout.container.js" %}

```javascript
// [imports...]
// [mapStateToProps and mapDispatchToProps]

/** @namespace Route/Checkout/Container */
export class CheckoutContainer extends PureComponent {
    // [...]

    async savePaymentMethodAndPlaceOrder(paymentInformation) {
        const { paymentMethod: { code, additional_data } } = paymentInformation;
        const guest_cart_id = !isSignedIn() ? getGuestQuoteId() : '';

        try {
            await fetchMutation(CheckoutQuery.getSetPaymentMethodOnCartMutation({
                guest_cart_id,
                payment_method: {
                    code,
                    [code]: additional_data
                }
            }));

            const orderData = await fetchMutation(CheckoutQuery.getPlaceOrderMutation(guest_cart_id));
            const { placeOrder: { order: { order_id } } } = orderData;

            this.setDetailsStep(order_id);
        } catch (e) {
            this._handleError(e);
        }
    }

    render() {...}
}

export default connect(mapStateToProps, mapDispatchToProps)(CheckoutContainer);
```

{% endcode %}

The function sets the payment method for the order, and places the order. After the order is placed, it simply transitions to the order success step (also known as the "details step").

We want to change this functionality – instead of transitioning to the details step, we need to redirect the user to the final payment step, on the 3rd party website. We already made sure that `CheckoutQuery.getPlaceOrderMutation` will return the redirection URL in the response, so we just need to use that.

Let's write a plugin for `savePaymentMethodAndPlaceOrder` that will redirect the user if necessary. We can add it to our existing `Checkout.container.plugin.js` file:

{% code title="plugin/Checkout.container.plugin.js" %}

```jsx
import { redirectToUrl } from '../util/Redirect';
import { getPaymentToken, setPaymentToken } from '../util/PaymentTokenPersistence';

const savePaymentMethodAndPlaceOrder = async (args, callback, instance) => {
    const [paymentInformation] = args;

    const { paymentMethod: { code, additional_data } } = paymentInformation;
    const guest_cart_id = !isSignedIn() ? getGuestQuoteId() : '';

		// It's important to check if the user has selected a Mollie payment method.
		// We don't want to affect other methods, so we just use the callback directly in that case.
    if (!MOLLIE_METHODS.includes(code)) {
        return await callback(...args);
    }
		// Since this is a Mollie method, now we need some custom logic

    try {
				// Just like the original function, we set the payment method
        await fetchMutation(CheckoutQuery.getSetPaymentMethodOnCartMutation({
            guest_cart_id,
            payment_method: {
                code,
                [code]: additional_data,
            },
        }));

				// However, when placing the order, there is some additional data we need to consider
        const orderData = await fetchMutation(CheckoutQuery.getPlaceOrderMutation(guest_cart_id));
        const { placeOrder: { order: { mollie_redirect_url, mollie_payment_token } } } = orderData;

        if (Boolean(mollie_payment_token)) {
            // We'll need this token later, so let's save it to the browser's storage
            setPaymentToken(mollie_payment_token);
        } else {
						// It is a good idea to "fail early" if some data you expect to be present
						// is not there. Also, provide good error messages.
						// That way, your code will be easier to debug if something goes wrong.
            throw Error("Expected mollie_payment_token in order data, none found", orderData)
        }

				// We take the redirect URL we requested in the previous step and redirect to it
        if (Boolean(mollie_redirect_url)) {
            redirectToUrl(mollie_redirect_url)
        } else {
            throw Error("Expected mollie_redirect_url in order data, none found", orderData)
        }
    } catch (e) {
        instance._handleError(e);
    }
};

// Afain, we need to export the plugin configuration:
export default {
    "Route/Checkout/Container": {
        "member-function": {
            savePaymentMethodAndPlaceOrder,
        },
    },
}
```

{% endcode %}

As you might have noticed, we used a couple of custom utility functions, namely `redirectToUrl` and `setPaymentToken`. Of course we need to define them as well.

It's common for extensions to need new utility functions. They just need to implement them in the `util` directory, just like you would in a theme.

The `redirectToUrl` in `Redirect.js` is a really simple function that I found on StackOverflow (don't tell anyone). Still, it's nice to have it as a reusable function so the rest of the code is more readable.

{% code title="util/Redirect.js" %}

```javascript
export const redirectToUrl = (url) => {
    window.location.replace(url);
};
```

{% endcode %}

The `setPaymentToken` and `getPaymentToken` functions (we haven't used the latter yet) are also very simple. They use the `BrowserDatabase` utility to save (and load) the payment token. This token will still be accessible after redirecting the user to the payment provider's page and back.

{% code title="util/PaymentTokenPersistence.js" %}

```jsx
// Let's use Scandi's BrowserDatabase utility for persistence
import BrowserDatabase from 'Util/BrowserDatabase';
import { ONE_MONTH_IN_SECONDS } from 'Util/Request/QueryDispatcher';

const TOKEN_KEY = 'mollie_payment_token';

export const setPaymentToken = (token) => {
		// Again, some error checking can help catch mistakes early.
    if (!Boolean(token)) {
        throw Error("Must specify token to set")
    }

    BrowserDatabase.setItem(token, TOKEN_KEY, ONE_MONTH_IN_SECONDS);
};

export const getPaymentToken = () => {
    const token = BrowserDatabase.getItem(TOKEN_KEY);

    if (!Boolean(token)) {
        throw Error("No payment token found in browser database")
    }

    return token
};
```

{% endcode %}

And with that, the redirection step is complete! If you wish, you can complete an order using Mollie's payment methods to verify that you are indeed redirected.
