# How To / Payment Gateway

# How-To: Integrate your payment gateway with Shopfront

> [For detailed event parameters, you should refer to our introduction to the Payment API documentation.](/documentation/Payments/Introduction)

## Getting Started

This guide will go over the basics for integrating your payment gateway with Shopfront as
a custom payment method. 

The integration with Shopfront is served through an HTML page and communicates with
Shopfront using JavaScript (through the 
[`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API).
Your integration will then communicate with your server using whatever language you would like.

There are a number of additional features which Shopfront supports, however aren't available
without prior approval from Shopfront. These include:

- Settlements
- Assisted Calculator

If your application uses the Embedded API, you can communicate with it using 
[internal page messages](/documentation/Embedded/Receivable%20Events#internalPageMessage).

Currently, custom payment methods don't require a developer account, in the future
it's likely that all custom payment methods will need a developer account.

**Communication With Shopfront**

```javascript
const eventMap = new Map();

window.addEventListener("message", event => {
    if(!event.source || event.origin.indexOf(window.parent.location.hostname) === -1) {
        return;
    }

    let data = event.data;
    if(typeof data === "string") {
        data = JSON.parse(data);
    }

    if(!data) {
        return;
    }

    if(!data.type) {
        return;
    }

    eventMap.get(data.type)?.(data.data);
});

const sendMessage = (type, data = {}) => {
    if(typeof window === "undefined") {
        throw new Error("Payment Gateway is unsupported");
    }

    if(typeof window.parent === "undefined") {
        throw new Error("Payment Gateway needs to be in an iFrame to work");
    }

    window.parent.postMessage(JSON.stringify({
        type,
        data,
    }), "*");
};
```

## Setting up the Payment Method

Before you setup the payment method through Shopfront, you need to have an HTML page
that is served over HTTPS. This page only needs to be accessible from the device
accessing it, it does not need to be publicly available. Through this HTML page
you should load a JavaScript script which will act as the integration.

In Shopfront, open the menu, expand Setup and press Payment Methods.

![Menu Location of Payment Methods](/imgs/developer/howto/payment/menu-location.png)

Add a new payment method with the type being set to Custom.

![Custom Payment Method Type](/imgs/developer/howto/payment/custom-type.png)

Press the edit button next to the payment method you just created and then enter the address
to your HTML page in the Gateway input and then save the payment method.

You'll then want to add the payment method to the register you're using by going to
the menu, expand Setup and press Registers & Outlets.

On this page, press the edit button next to the register you wish to use and enable the payment
method for the register then save.

![Enabling the Payment Method](/imgs/developer/howto/payment/register-outlet.png)

Once enabled, you can head to the sell screen, add a product to the sell screen and attempt to use
the payment method.

![Payment method on the sell screen](/imgs/developer/howto/payment/sell-screen.png)

This will attempt to load your HTML page and start communications.

## Receiving the data from Shopfront

Once your integration has loaded and is ready to go, you'll need to inform your application is ready
and request the data by sending the `REQUEST_DATA` event to Shopfront. Shopfront in turn will respond
with a `REQUEST_DATA` event that contains data such as the amount ot process for the sale, an internal
reference generated by Shopfront and more.

After you've received the data from Shopfront, you should then send the `CONNECTED` event.

**Receiving Payment Data**

```javascript
eventMap.set("REQUEST_DATA", data => {
    // The amount in dollars and cents (always positive)
    const amount = data.amount;

    // The amount to cash out in dollars and cents (always positive)
    const cashout = data.cashout;

    // A string that serves as the reference for the future
    const reference = data.reference;

    // Whether the transaction is a refund or not
    const isRefund = data.refund;

    // Whether this request is being retried or not (see later)
    const isRetry = data.isRetry;

    // Send that we're connected and ready to go
    sendMessage("CONNECTED");

    // You can then use these parameters to process the payment
    processPayment();
});

window.onload = () => {
    sendMessage("REQUEST_DATA");
};
```

## Handling the Payment

After you receive the data to process, you can immediately process the payment. During the processing
you'll need to ensure your application responds to Shopfront's `PING` requests or Shopfront will believe
your gateway has timed out. 

**Responding to Pings**

```javascript
eventMap.set("PING", identifier => {
    // Check if we've timed out from the payment gateway
    // or if we're still processing, if we've timed out
    // we should not respond to this, otherwise just send
    // back the identifier.
    sendMessage("PONG", identifier);
});
```

Once you've processed the transaction, you'll need to send the `FINISH` event to Shopfront with the status
of the transaction and the amount of money that was processed.

**Finishing a Payment**

```javascript
/**
 * Complete the transaction
 * @param {"approved" | "cancelled" | "declined"} status Only `approved` payments reduce the sale's balance
 * @param {number} paymentAmount The amount that was used through the payment method, this can be different to the amount provided by Shopfront
 * @param {number} cashoutAmount The cashout amount that was used through the payment method, this can be different to the amount provided by Shopfront
 * @param {string} subtype The subtype of the payment (e.g. Visa, Mastercard, etc) that was used
 * @param {Record<string, unknown>} metaData Any additional data you would like to include for the payment
 */
const finishPayment = (status, paymentAmount, cashoutAmount, subtype, metaData = {}) => {
    sendMessage("FINISH", {
        status,
        metaData,
        subtype,
        amount : paymentAmount,
        cashout: cashoutAmount,
    });
};
```

If you receive an error while processing the transaction (which is unreleased to the payment, such as if
you were unable to load a script) you should send an `ERROR` event to Shopfront before sending the `FINISH`
event.

**Sending Errors to Shopfront**

```javascript
const handleError = (error) => {
    sendMessage("ERROR", error);
    finishPayment("cancelled", paymentAmount, cashoutAmount);
};
```

## Retrying Payments

If Shopfront was unable to confirm if a payment had been completed for some reason (e.g. power outage, 
browser closing, etc), Shopfront will automatically reopen the gateway URL and will provide the same
data in the `REQUEST_DATA` event (with the `isRetry` parameter set to true).

It's expected that you look up the transaction by the provided reference and provide the result of that
transaction instead of reprocessing the transaction.

**Retrying Payments**

```javascript
const processPayment = () => {
    if(isRetry) {
        // Replace this with your retry logic
        const payment = lookupTransactionResult(reference);

        if(payment) {
            // If the transaction was found, then send the details back to Shopfront
            finishPayment(payment.status, payment.amount, payment.cashout, payment.metaData);
            return;
        } // If the transaction wasn't found, it's likely it didn't get to your gateway in the first place
    }

    // Process the payment through the normal means
};
```

## Cancelling the Active Payment

If for any reason you need to cancel the currently active payment before processing it, you can send the
`FINISH` event with the `cancelled` status. If the payment wasn't successful for any reason (e.g. insufficient
balance) you should send the `FINISH` event with the `declined` status.

**Cancelling the Active Payment**

```javascript
const cancelPayment = () => {
    sendComplete("cancelled", paymentAmount, cashoutAmount);
};
```

## Printing Receipts

Shopfront supports printing a preformatted text receipt either when the sale has been completed or during the 
payment. The preformatted text should be a single string that uses `\n` for line breaks.

To send a receipt to Shopfront to print you need to send the `RECEIPT` event with a `receiptText` parameter
(which contains the actual receipt string) and a `print` parameter. If `print` is set to `true` then the 
receipt will print instantly, otherwise it'll be added to the receipts to print at the end of the transaction
(note, there is no guarantee that receipts added to print at the end of the transaction will be printed).

If you need your receipt to print (rather than optionally be included), you should set `print` to `true`.

**Printing Receipts**

```javascript
const printReceipt = (text, printNow) => {
    sendMessage("RECEIPT", {
        receiptText: text,
        print      : printNow,
    });
};
```