npm package: @olo/pay
Description: [**Olo Pay JS Examples**](https://pay-examples.olo.com/?path=/docs/olo-pay-js-introduction--docs)
Author: Olo
Latest version: 2.3.0

# Olo Pay JS

[**Olo Pay JS Examples**](https://pay-examples.olo.com/?path=/docs/olo-pay-js-introduction--docs)

[Skip to Credit Card Elements](#credit-card-elements)

[Skip to CVV Element](#cvv-element)

[Skip to Digital Wallets](#digital-wallets)

[Olo Pay JS Upgrades](#olo-pay-js-upgrades)

## Introduction

[**Olo Pay JS**](https://www.npmjs.com/package/@olo/pay) allows API partners to easily add PCI-compliant credit card and digital wallet web elements (Apple Pay and Google Pay) to their checkout forms while seamlessly integrating with the [**Olo Ordering API**](https://www.olo.com/solutions/ordering). The library wraps Stripe JS functionality which offers the benefits of handling validation, styling, accessibility, and responsiveness for:

- **Credit card input elements**: PCI-compliant credit card input elements are wrapped in Stripe-hosted iframe elements so that credit card data is secure.

- **CVV input element**: PCI-compliant CVV input element is wrapped in Stripe-hosted iframe element so that card CVV can be revalidated.

- **Plug-and-play digital wallets support**: Payment button that integrates with Google Chrome and Safari payment sheets to complete the checkout process.

Documentation for [Stripe Elements](https://stripe.com/docs/payments/elements) and [Digital Wallets](https://stripe.com/docs/payments/wallets) can be referenced for functionality and types not included here.

#### Caution: Olo Pay JS is only built to work within Olo's partner ecosystem and requires Olo Pay as the payment processor.

### Submitting a Basket via the Olo Ordering API

The library can be used to generate a payment method object which can be passed to the Olo Ordering API, replacing the current need to pass credit card PANs and CVVs to Olo.

## Using the Library

Olo Pay JS is available on the NPM Registry ([@olo/pay](https://www.npmjs.com/package/@olo/pay)).

Installing from NPM will include Olo Pay JS types for those using TypeScript, while loading the library from a script tag will accommodate more import mechanisms.

**NPM**

```sh
npm install @olo/pay
```

**Yarn**

```sh
yarn add @olo/pay
```

### PCI-Compliant Credit Card Inputs

Creates three separate elements for card number, expiration date, and cvv code.

```js
import { CreditCardElements } from '@olo/pay';

const cardElements = new CreditCardElements();
cardElements.create();
```

### PCI-Compliant Single-line Credit Card Input

Creates one single-line input that contains card number, expiration date, cvv code, and zipcode (optional).

```js
import { SingleLineCardElement } from '@olo/pay';

const cardElement = new SingleLineCardElement();
cardElement.create();
```

### PCI-Compliant CVV Input

Creates an individual element for revalidating cvv code.

```js
import { CVVElement } from '@olo/pay';

const cvvElement = new CvvElement();
cvvElement.create();
```

### Quick and Easy Digital Wallet Payments

```js
import { DigitalWallets } from '@olo/pay';

function callback() {}

const paymentOptions = {
  options: {
    country: 'US',
    currency: 'usd',
    total: {
      label: 'Pay [brand name]',
      amount: 7672,
    },
  },
};

const digitalWallets = new DigitalWallets();

await digitalWallets.initialize(paymentOptions);

digitalWallets.mountButton(callback);
```

## TypeScript Support

The library supports TypeScript and import types (TS >= 3.8):

```ts
import { CreditCardElements as CardElements } from '@olo/pay';
import type { ElementClasses, Elements, Environment, MountTargets } from '@olo/pay';
```

If you are using a TypeScript version less than 3.8, you may still import types using the standard import syntax.

```ts
import {
  CreditCardElements as CardElements
  ElementClasses,
  Elements,
  Environment,
  MountTargets,
} from '@olo/pay';
```

## Integration

ES Modules and are supported for use:

```sh
npm install @olo/pay
```

```js
import { CreditCardElements, CvvElement, DigitalWallets } from '@olo/pay';
```

## Development, Test, or Production Environment

The most important option to pass to a new Olo Pay instance is the `environment` in which you want to run transactions. This accepts a string of `development`, `test`, or `production` (defaulting to `development` if nothing is passed). Cards will not be charged in `development` mode; cards WILL be charged in `production` mode.

When `test` mode is enabled, pressing the Digital Wallets button will immediately return a test payment method—bypassing the payment sheet altogether. This is useful for automation tests, which by design cannot hook into or step through a browser's payment sheet.

**Note:** Specifying an environment will tell the library to include the corresponding key.

```js
const cardElementsDefault = new CreditCardElements(); // Will use the development API key

const cardElements = new CreditCardElements('development'); // Will use development API key

const cvvElementDefault = new CvvElement(); // Will use the development API key

const cvvElement = new CvvElement('development'); // Will use development API key

const digitalWallets = new DigitalWallets('production'); // Will use production API key

const digitalWalletsTest = new DigitalWallets('test'); // Will bypass the payment sheet when the button is pressed
```

## Credit Card Elements

Check out this [example](https://pay-examples.olo.com/?path=/story/olo-pay-js-multi-line-credit-card--example) to get started.

### DEPRECATION NOTICE

The `MountTargets` and `Placeholders` objects have been changed to use the key name `cvv` in favor of `cvc`.

### Basic Implementation

Import the library and create a new instance of `CreditCardElements`:

```ts
import { CreditCardElements } from '@olo/pay';

const cardElements = new CreditCardElements();
cardElements.create();
```

The `create` method will create new iframes for each card element (number, expiry, and CVV) and then mount them to DOM targets that are marked with `data-olo-*` attributes by default:

```html
<!-- Default mount targets -->
<div data-olo-pay-card-number>Card number iframe will be injected here</div>
<div data-olo-pay-card-expiry>Card expiry iframe will be injected here</div>
<div data-olo-pay-card-cvv>Card CVV iframe will be injected here</div>
```

The `create` method also takes in optional `mountTargets` which accept string selectors or HTML elements.

```js
const options = {
  mountTargets = {
    number: '#custom-card-number-selector',
    expiry: cardExpiryElement, // HTML element
    cvv: '[data-cvv-target]',
  }
}

cardElements.create(options);
```

If the elements can be found in the DOM, the iframes will be injected into their designated mount targets.

### Customization and Options

Options for customization can be passed to the CreditCardElements instance in several different ways at different times in the app lifecycle.

When calling the `create` method from a `CreditCardElements` instance, optional fields can be passed in through a `CreditCardOptions` object (if this is left blank, defaults will be used):

#### **Object shape**

```ts
interface CreditCardOptions {
  cardElementOptions?: CardElementOptions;
  cardNumberElementOptions?: {
    showIcon: boolean;
  };
  elementsOptions?: ElementsOptions;
  mountTargets?: MountTargets;
  placeholders?: CardInputPlaceholders;
}

interface MountTargets {
  cvv: string | HTMLElement;
  expiry: string | HTMLElement;
  number: string | HTMLElement;
}

interface CardInputPlaceholders {
  cvv: string;
  expiry?: string;
  number?: string;
}
```

#### **Example**

```js
import { CreditCardElements } from '@olo/pay';

const environment = env === 'PRODUCTION' ? 'production' : 'development';

const options: CreditCardOptions = {
  elementsOptions: {
    fonts: [
      {
        cssSrc: 'https://fonts.googleapis.com/css2?family=Mr+Dafoe&family=Pacifico',
      },
    ],
  },
  cardElementOptions: {
    classes: {
      base: 'custom',
      complete: 'custom-name--complete',
      empty: 'custom-name--empty',
      focus: 'custom-name--focus',
      invalid: 'custom-name--invalid',
      webkitAutofill: 'custom-name--webkit-autofill',
    },
  },
  cardNumberElementOptions: {
    showIcon: true,
  },
  mountTargets: {
    number: '[data-custom-card-number-target]',
    expiry: '[data-custom-card-expiry-target]',
    cvv: '[data-custom-card-cvv-target]',
  },
  placeholders: {
    cvv: '',
    expiry: '',
    number: '',
  },
};

const cardElements = new CreditCardElements(environment);
await cardElements.create(options);
```

These options can be updated at any time using the `update` method:

```js
cardElements.update(newOptions);
```

### Adding Custom Styles Through CSS

Custom styles can be added through CSS (via default or custom class names), through a [custom styles object](#adding-custom-styles-with-a-style-object), or a combination of both.

By default, the class names used to style credit card elements are:

```css
.olo-pay {
  padding: 2.5rem 1rem;
  color: darkslategrey;
}

.olo-pay--complete {
  color: peachpuff;
}

.olo-pay--empty {
}

.olo-pay--focus {
}

.olo-pay--invalid {
  color: tomato;
}

.olo-pay--webkit-autofill {
}
```

These classes will be added and removed as the user types into the inputs. The names can be edited by changing the `cardElementOptions.classes` (part of the options passed to CC elements above).

### Adding Custom Styles With a Style Object

It may be easier for your implementation to pass a style object to apply custom styling.

Supported style object properties (`ElementCSSProperties` below) can be applied to the four states of elements:

- `base`
- `complete`
- `empty`
- `invalid`

And for each pseudo-class and pseudo-element:

```
:hover
:focus
::placeholder
::selection
:-webkit-autofill
:disabled
::-ms-clear
```

#### **Accepted Properties**

```ts
interface ElementCSSProperties {
  color: string;
  backgroundColor: string;
  fontFamily: string;
  fontSize: string;
  fontSmoothing: string;
  fontStyle: string;
  fontVariant: string;
  fontWeight: string;
  iconColor: string;
  lineHeight: string;
  letterSpacing: string;
  textAlign: string;
  textDecoration: string;
  textShadow: string;
  textTransform: string;
}
```

#### **Example**

```js
const styleObject = {
  style: {
    base: {
      color: 'lightslategrey',
      fontSize: '5rem',
      fontFamily: 'sans-serif',
      '::placeholder': {
        color: 'darkslategrey',
      },
    },
    complete: {
      color: 'peachpuff',
      fontFamily: 'Mr Dafoe, cursive', // Font family location was provided in the `elementOptions > fonts > cssSrc` above
    },
    invalid: {
      color: 'tomato',
      ':hover': {
        color: 'orangered',
      },
    },
  },
};

const cardElements = new CreditCardElements({
  cardElementOptions: {
    style: styleObject,
  },
});
```

### Element Methods

Individual credit card elements can be accessed after being mounted so that individual methods can be run.

**Change**

```ts
cardElements.cardNumber.on('change', (event) => {
  if (event.complete) {
    // ...
  } else if (event.error) {
    const message = event.error.message;
    // ...
  }
});
```

`onChange` gives us back an object:

```js
{
  complete: false,
  brand: 'visa',
  elementType: 'card',
  empty: false,
  error: undefined,
  value: { postalCode: "" },
}
```

While the other events are straight forward. These include:

- Ready - Triggered when the `element` is fully rendered and can accept `element.focus` calls.
- Focus - Triggered when the Element gains focus.
- Blur - Triggered when the Element loses focus.
- Escape - Triggered when the escape key is pressed within an Element.

Example:

```js
element.on('ready', (event) => {
  element.focus();
});
```

The library also exposes a series of methods to interact with all the elements at once:

#### **General**

```js
cardElements.elements.forEach((element) => {
  // ...
});
```

#### **Create**

Creates element iframes and mounts them to DOM targets.
Customization can be passed as an `options` argument.

| Parameter | Description                                                                                                                   |
| --------- | ----------------------------------------------------------------------------------------------------------------------------- |
| options   | CreditCardOptions options object to influence the style of the credit card elements. If not passed in, defaults will be used. |

```js
cardElements.create(options);
```

#### **Create Payment Method**

Creates a single-use payment method object. A payment method will only be generated successfully if all required card data fields are valid. Billing details can be passed in as an optional `billingDetails` argument.

For more information on how to use the payment method when submitting a basket, refer to the [documentation in our developer portal](https://developer.olo.com/docs/load/olopay#section/Submitting-a-Basket-via-the-Ordering-API).

| Parameter      | Description                                                                                                                                              |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| billingDetails | Optional object that supplies billing details when creating a payment method object that may be used or required by particular types of payment methods. |

```js
cardElements.createPaymentMethod(); // No billing details supplied
```

```js
cardElements.createPaymentMethod({
  address: {
    city: 'New York',
    country: 'US',
    line1: '123',
    line2: 'Apt. 2',
    postal_code: '10006',
    state: 'NY',
  },
  email: 'lb@olo.com',
  name: 'Lucille Bluth',
  phone: '+19876543210',
});
```

#### **Apply styles**

Applies a style object to the credit card elements.

| Parameter   | Description                                                     |
| ----------- | --------------------------------------------------------------- |
| styleObject | Object that provides style to influence the inner iframe styles |

```js
cardElements.applyStyles(styleObject);
```

#### **Clear**

Clear all credit card field values.

```js
cardElements.clear();
```

#### **Destroy**

Removes the elements from the DOM and destroys them so they cannot be re-mounted.

```js
cardElements.destroy();
```

#### **Unmount**

Unmounts the elements from the DOM.

```js
cardElements.unmount();
```

#### **Update**

Updates the options the credit card elements were initialized with.
Updates are merged into the existing configuration.

```js
cardElements.update(newOptions); // New options are the same as those passed when instantiating `CreditCardElements`
```

## Single-line Credit Card Element

Check out this [example](https://pay-examples.olo.com/?path=/story/olo-pay-js-single-line-credit-card--example) to get started.

### Basic Implementation

Import and create a new instance of `SingleLineCardElement`:

```ts
import { SingleLineCardElement } from '@olo/pay';

const cardElement = new SingleLineCardElement();
SingleLineCardElement.create();
```

The `create` method will create a new iframe and will mount it to a DOM target that is marked with a `data-olo-pay-card-single-line` attribute by default:

```html
<!-- Default mount targets -->
<div data-olo-pay-card-single-line>Single-line iframe element will be injected here</div>
```

The `create` method also takes in optional argument `mountTarget` which accepts a string selector or an HTML element.

```js
const options = {
  mountTarget: '#custom-card-selector',
};

cardElements.create(options);
```

If the element can be found in the DOM, the iframe will be injected into its designated mount target.

### Customization and Options

Options for customization can be passed to the `SingleLineCardElement` instance in several different ways at different times in the app lifecycle.

When calling the `create` method from a `SingleLineCardElement` instance, optional fields can be passed in through a `SingleLineCardElementOptions` object (if this is left blank, defaults will be used):

#### **Object shape**

```ts
interface SingleLineCardElementOptions {
  cardElementOptions?: CardElementOptions;
  elementsOptions?: ElementsOptions;
  mountTarget?: HTMLElement | string;
}
```

#### **Example**

```js
import { SingleLineCardElement } from '@olo/pay';

const environment = env === 'PRODUCTION' ? 'production' : 'development';

const options: SingleLineCardElementOptions = {
  elementsOptions: {
    fonts: [
      {
        cssSrc: 'https://fonts.googleapis.com/css2?family=Mr+Dafoe&family=Pacifico',
      },
    ],
  },
  cardElementOptions: {
    classes: {
      base: 'custom',
      complete: 'custom-name--complete',
      empty: 'custom-name--empty',
      focus: 'custom-name--focus',
      invalid: 'custom-name--invalid',
      webkitAutofill: 'custom-name--webkit-autofill',
    },
  },
  mountTarget: '[data-card-input]',
};

const cardElement = new SingleLineCardElement(environment);
await cardElement.create(options);
```

These options can be updated at any time using the `update` method:

```js
cardElement.update(newOptions);
```

#### **Optional Fields**

In the single-line card element, there are several unique fields that are applicable here that do not apply to `CreditCardElements`:

```js
const options: SingleLineCardElementOptions = {
  ...restOfOptions,
  hidePostalCode: true, // Hides the postal code field. false if not provided
  iconStyle: 'solid', // Changes the style of the card icon. 'default' and 'solid' are the only two valid options here. Value is set to 'default' if no value is provided.
  hideIcon: true, // Hides the card icon. false if not provided
};
```

### Element Methods

The single-line credit card element can be accessed after being mounted so that individual methods can be run.

#### **Create**

Creates element iframes and mounts them to DOM targets.
Customization can be passed as an `options` argument.

| Parameter | Description                                                                                                                  |
| --------- | ---------------------------------------------------------------------------------------------------------------------------- |
| options   | CreditCardOptions options object to influence the style of the single-line element. If not passed in, defaults will be used. |

```js
singleLineCardElement.create(options);
```

#### **Create Payment Method**

Creates a single-use payment method object. A payment method will only be generated successfully if all required card data fields inside the single-line element are valid. Billing details can be passed in as an optional `billingDetails` argument.

For more information on how to use the payment method when submitting a basket, refer to the [documentation in our developer portal](https://developer.olo.com/docs/load/olopay#section/Submitting-a-Basket-via-the-Ordering-API).

| Parameter      | Description                                                                                                                                              |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| billingDetails | Optional object that supplies billing details when creating a payment method object that may be used or required by particular types of payment methods. |

```js
singleLineCardElement.createPaymentMethod(); // No billing details supplied
```

```js
singleLineCardElement.createPaymentMethod({
  address: {
    city: 'New York',
    country: 'US',
    line1: '123',
    line2: 'Apt. 2',
    postal_code: '10006',
    state: 'NY',
  },
  email: 'lb@olo.com',
  name: 'Lucille Bluth',
  phone: '+19876543210',
});
```

#### **Apply styles**

Applies a style object to the single-line element.

| Parameter   | Description                                                     |
| ----------- | --------------------------------------------------------------- |
| styleObject | Object that provides style to influence the inner iframe styles |

```js
singleLineCardElement.applyStyles(styleObject);
```

#### **Clear**

Clear all credit card field values inside the single-line card element.

```js
singleLineCardElement.clear();
```

#### **Destroy**

Removes the element from the DOM and destroys them so they cannot be re-mounted.

```js
singleLineCardElement.destroy();
```

#### **Unmount**

Unmounts the single-line card element from the DOM.

```js
singleLineCardElement.unmount();
```

#### **Update**

Updates the options with which the single-line element was initialized.
Updates are merged into the existing configuration.

```js
singleLineCardElement.update(newOptions); // New options are the same as those passed when instantiating `SingleLineCardElement`
```

## CVV Element

Check out this [example](https://pay-examples.olo.com/?path=/story/olo-pay-js-cvv-revalidation--example) to get started.

### Basic Implementation

Import and create a new instance of `CvvElement`:

```ts
import { CvvElement } from '@olo/pay';

const cvvElement = new CvvElement();
cvvElement.create();
```

The `create` method will create a new iframe and will mount it to a DOM target that is marked with a `data-olo-pay-cvv` attribute by default:

```html
<!-- Default mount targets -->
<div data-olo-pay-cvv>CVV iframe element will be injected here</div>
```

The `create` method also takes in optional argument `mountTarget` which accepts a string selector or an HTML element.

```js
const options = {
  mountTarget: '#custom-cvv-selector',
};

cvvElement.create(options);
```

If the element can be found in the DOM, the iframe will be injected into its designated mount target.

### Customization and Options

Options for customization can be passed to the `CvvElement` instance in several different ways at different times in the app lifecycle.

When calling the `create` method from a `CvvElement` instance, optional fields can be passed in through a `CvvOptions` object (if this is left blank, defaults will be used):

#### **Object shape**

```ts
interface CvvOptions {
  cvvElementOptions?: CvvElementOptions;
  elementsOptions?: ElementsOptions;
  mountTarget?: HTMLElement | string;
}
```

#### **Example**

```js
import { CvvElement } from '@olo/pay';

const environment = env === 'PRODUCTION' ? 'production' : 'development';

const options: CvvOptions = {
  elementsOptions: {
    fonts: [
      {
        cssSrc: 'https://fonts.googleapis.com/css2?family=Mr+Dafoe&family=Pacifico',
      },
    ],
  },
  cvvElementOptions: {
    classes: {
      base: 'custom',
      complete: 'custom-name--complete',
      empty: 'custom-name--empty',
      focus: 'custom-name--focus',
      invalid: 'custom-name--invalid',
      webkitAutofill: 'custom-name--webkit-autofill',
    },
  },
  mountTarget: '[data-cvv-input]',
};

const cvvElement = new CvvElement(environment);
await cvvElement.create(options);

// Use this function as a callback inside your 'Revalidate CVV' button onClick
async function createCvvToken() {
  const token = await cvvElement.createCvvToken();
}
```

These options can be updated at any time using the `update` method:

```js
cvvElement.update(newOptions);
```

### Element Methods

The cvv element can be accessed after being mounted so that individual methods can be run.

#### **Create**

Creates element iframe and mounts to DOM target.
Customization can be passed as an `options` argument.

| Parameter | Description                                                                                                   |
| --------- | ------------------------------------------------------------------------------------------------------------- |
| options   | CvvOptions options object to influence the style of the cvv element. If not passed in, defaults will be used. |

```js
cvvElement.create(options);
```

#### **Create cvv Token**

Creates a single-use token that represents a card's cvv value.

```js
cvvElement.createCvvToken();
```

Returns a token object.

```js
{
  "token": {
    "id": "cvctok_1Ox97yAMexQxmg",
    "object": "token",
    "client_ip": "1.1.1.1",
    "created": 1711117886,
    "livemode": false,
    "type": "cvc_update",
    "used": false
  }
}
```

Use your basket submission API call here to send the cvv as `token.id`.

```js
await submitBasket({
  ...restOfBasketData,
  paymentCard: {
    ...restOfPaymentCardData,
    cvv: token.id,
  },
});
```

#### **Apply styles**

Applies a style object to the cvv element.

| Parameter   | Description                                                     |
| ----------- | --------------------------------------------------------------- |
| styleObject | Object that provides style to influence the inner iframe styles |

```js
cvvElement.applyStyles(styleObject);
```

#### **Clear**

Clears cvv element value.

```js
cvvElement.clear();
```

#### **Destroy**

Removes the element from the DOM and destroys it so it cannot be re-mounted.

```js
cvvElement.destroy();
```

#### **Unmount**

Unmounts the cvv element from the DOM.

```js
cvvElement.unmount();
```

#### **Update**

Updates the options with which the cvv element was initialized.
Updates are merged into the existing configuration.

```js
cvvElement.update(newOptions); // New options are the same as those passed when instantiating `CvvElement`
```

## Digital Wallets

Digital Wallets provide Google Pay and Apple Pay support where the checkout process defers to the built-in Chrome or Safari browser checkout experience. To leverage Digital Wallets, you can use the provided button from Olo Pay or create your own custom button.

Check out this [example](https://pay-examples.olo.com/?path=/story/olo-pay-js-digital-wallets--default) to get started. Note: this example will only work with Google Pay in a Chrome instance where you are logged into a Chrome profile with a linked payment method defined at pay.google.com. Also, a linked personal card will _not_ be charged since this example is using `development` mode by default.

### Environment Requirements

In order to test Google Pay or Apple Pay buttons successfully in your development or production environment, your domain must be served over HTTPS with a valid SSL certificate. We recommend using a solution like [ngrok](https://ngrok.com/).

In addition, for Apple Pay, your domain must also be public-facing and verified by Apple. Please refer to our "Setting up Apple Pay for Web" guide [here](https://developer.olo.com/docs/load/olopay#section/Web-Applications/Integration) for more information.

### Using the Digital Wallets Button

The built-in `DigitalWallets` button will automatically check the web application’s browser and payment configuration for a valid Apple Pay or Google Pay setup. The payment button will only render in your web application in the following scenarios:

1. Safari on macOS and iOS or Chrome on iOS 16 with a valid Apple Pay configuration. See [Apple's official documentation](https://support.apple.com/en-us/HT204506) for more information for configuring Apple Pay.
2. Google Chrome on Windows, macOS, and Android with a valid Google Pay configuration.
   - [Configuring Google Pay in Android](https://support.google.com/pay/answer/7625055?co=GENIE.Platform%3DAndroid&hl=en)
   - [Configuring Google Pay in Chrome](https://support.google.com/pay/answer/7625055)

To use the Digital Wallets button, import the button from Olo Pay JS and create a new instance of `DigitalWallets`.

```js
import { DigitalWallets } from '@olo/pay';

const options = {}; // More information below
const callback = () => {}; // More information below

const digitalWallets = new DigitalWallets();

await digitalWallets.initialize(options);

digitalWallets.mountButton(callback);
```

The `mountButton` method will create a new payment request button and mount it to a DOM target marked with a `data-olo-pay-payment-button` attribute.

```html
<div data-olo-pay-payment-button>Payment request button will be injected here</div>
```

`mountButton` takes a callback function that will be executed once the user initializes payment from the browser's or device's checkout experience. It also accepts an optional second parameter of a custom selector or element to mount the button to. This will default to `data-olo-pay-payment-button`.

```js
async function callback(paymentMethod) {
  if (paymentMethod) {
    // Use your basket submission API call here to send the payment method id as `token`
    try {
      await submitBasket({
        ...restOfBasketData,
        token: paymentMethod.id,
        cardType: paymentMethod.card.brand,
        cardLastFour: paymentMethod.card.last4,
      });
      // Signals to the Apple Pay or Google Pay payment sheet that the transaction has been processed
      // successfully and that the payment sheet can be closed
      digitalWallets.completePaymentEvent();
    } catch (error) {
      // Signals to the Apple Pay or Google Pay payment sheet that the payment has failed
      digitalWallets.failPaymentEvent();
    }
  }
}
```

#### **Digital Wallets Options**

When initializing a new `DigitalWallets` button instance, certain options can be passed in that dictate the style of the button.

Note: it is highly recommended to set `requestPayerName` to `true` in the `PaymentRequestOptions` object, as this will return the billing address in the Payment Method object for Google Pay and Apple Pay. See [Stripe's documentation](https://stripe.com/docs/js/payment_request/create#stripe_payment_request-options-requestPayerName) for more information here.

```js
interface DigitalWalletsOptions {
  options: PaymentRequestOptions; // See Stripe's documentation https://stripe.com/docs/js/payment_request/create#stripe_payment_request-options
  style?: {
    type?: 'default' | 'book' | 'buy' | 'donate', // changes button text (i.e. "Book with G Pay")
    theme?: 'dark' | 'light' | 'light-outline',
    height?: string, // i.e. `48px`
  };
}
```

```ts
import { DigitalWallets } from '@olo/pay';

const callback = () => {};

const digitalWalletsOptions = {
  options: {
    country: 'US',
    currency: 'usd',
    total: {
      label: 'Pay [brand name]',
      amount: 7672,
    },
    displayItems: [
      { label: 'Subtotal', amount: 6248 },
      { label: 'Discount', amount: -625 },
      { label: 'Delivery Charge', amount: 299 },
      {
        label: 'Estimated Taxes & Fees',
        amount: 500,
      },
      { label: 'Tip', amount: 1250 },
    ],
    requestPayerName: true,
  },
  style: {
    type: 'default',
    theme: 'dark',
    height: '48px',
  },
};
const digitalWallets = new DigitalWallets();

await digitalWallets.initialize(digitalWalletsOptions);

digitalWallets.mountButton();
```

In certain scenarios, it is useful to determine if a payment through a digital wallets option (i.e. Google Pay or Apple Pay) is possible before rendering a checkout template that renders this button. You can use `canMakePayment` before initializing the button if this better suits your workflow.

```js
import { CreditCardElements, DigitalWallets } from '@olo/pay';
import { paymentRequestOptions } from './payment-request'; // More info below

const digitalWallets = new DigitalWallets();

const callback = () => {};

await digitalWallets.initialize(paymentRequestOptions);

if (digitalWallets.canMakePayment.googlePay || digitalWallets.canMakePayment.applePay) {
  digitalWallets.mountButton(callback);

  // Add logic to render your digital wallets template with button here
} else {
  const cardElements = new CreditCardElements();
  cardElements.create();
}
```

### Using a custom button

Instead of using the provided `DigitalWallets` button, you may elect to use your own button.

**Note:** It is highly recommended to use the library's built-in buttons. If you do choose the custom route, be sure to comply with [Apple's](https://developer.apple.com/design/human-interface-guidelines/apple-pay/overview/buttons-and-marks/) and [Google's](https://developers.google.com/pay/api/web/guides/brand-guidelines) button UI specifications.

To use a custom button, first create a button that you plan on using to initiate the payment.

```html
<!-- Custom Payment Button -->
<button id="digital-wallets-button" style="display: none; max-width: 300px">Initiate Payment</button>
```

```js
const button = document.querySelector('#digital-wallets-button');
button.addEventListener('click', digitalWallets.initiatePayment);
```

Next, instantiate DigitalWallets and call the `initialize` method with `paymentRequestOptions`. From here you can utilize `digitalWallets.canMakePayment` to check if you should render your button.

Wire the `click` event of your button to execute the `initiatePayment()` method to trigger the browser's or device's payment sheet for the the user to complete the payment.

Note that before rendering this button, it is important to check to see if a digital wallets payment can be made first by checking the `canMakePayment` boolean from your `DigitalWallets` instance.

```js
import { DigitalWallets, PaymentMethod } from '@olo/pay';
import { paymentRequestOptions } from './payment-request'; // More info below

const digitalWallets = new DigitalWallets();

await digitalWallets.initialize(paymentRequestOptions);

if (digitalWallets.canMakePayment.googlePay || digitalWallets.canMakePayment.applePay) {
  const button = document.getElementById('digital-wallets-button');

  button.style.display = 'inline';

  button.on('click', () => {
    // Triggers the browser's or device's payment sheet
    digitalWallets.initiatePayment();
  });
}
```

##### payment-request.ts

```js
const paymentRequestOptions = {
  options: {
    country: 'US',
    currency: 'usd',
    total: {
      label: 'Pay [brand name]',
      amount: 7672,
    },
    displayItems: [
      { label: 'Subtotal', amount: 6248 },
      { label: 'Discount', amount: -625 },
      { label: 'Delivery Charge', amount: 299 },
      {
        label: 'Estimated Taxes & Fees',
        amount: 500,
      },
      { label: 'Tip', amount: 1250 },
    ],
    requestPayerName: true,
  },
  classes: {
    base: 'custom',
    complete: 'custom-name--complete',
    empty: 'custom-name--empty',
    focus: 'custom-name--focus',
    invalid: 'custom-name--invalid',
    webkitAutofill: 'custom-name--webkit-autofill',
  },
};

digitalWallets.mountButton(callback);
```

TypeScript

```ts
interface DigitalWalletsOptions {
  options: PaymentRequestOptions; // See Stripe's documentation https://stripe.com/docs/js/payment_request/create#stripe_payment_request-options
  classes?: ElementClasses;
}
```

### `DigitalWallets` Methods

Methods accessible from an instantiated `DigitalWallets` object.

```js
import { DigitalWallets } from '@olo/pay';

const digitalWallets = new DigitalWallets();
```

#### **Initialize**

Initializes a `DigitalWallets` instance by setting payment request information.

| Parameter | Description                                                                             |
| --------- | --------------------------------------------------------------------------------------- |
| options   | An object that contains payment request options (required) and style options (optional) |

#### **Mount Button**

Mounts a `DigitalWallets` button that will be appended to an element marked with a matching `data-olo-pay-payment-button` attribute.
| Parameter | Description |
| --------- | ------------------------------------------------------------------------ |
| callback | A function that will be executed once the payment process has been completed from the browser's or mobile device's built-in checkout experience |

##### Example

```html
<div data-olo-pay-payment-button></div>
```

```js
import { DigitalWallets } from '@olo/pay';
import { paymentRequestOptions } from './payment-request';

function callback(paymentMethod) {
  // Process payment here
}

const digitalWallets = new DigitalWallets();

await digitalWallets.initialize(paymentRequestOptions);

// Will append Digital Wallets button to div marked with `data-olo-pay-payment-button`
// (or a custom selector or element passed as a second parameter)
digitalWallets.mountButton(callback);
```

#### **initiatePayment**

Initiates a payment by showing the Google Pay or Apple Pay payment sheet. This can be triggered when using a custom payment button, or in scenarios where preventing the default `click` event behavior is necessary (i.e. proper form validation prior to initiating the payment).

Example

```js
const digitalWallets = new DigitalWallets();
await digitalWallets.initialize(options);

digitalWallets.paymentButton.on('click', (event) => {
  event.preventDefault();

  // Launch payment sheet if form is valid
  if (formIsValid) {
    dw.initiatePayment();
  }
});
```

#### **canRenderButton**

Determines which digital wallets are available in the current browser context. This function differs from invoking `canMakePayment` after initialization because it does not rely on a `DigitalWallets` instance. This is useful in situations where you need to determine whether or not to render the button, but don't have access to the payment information required in `DigitalWalletsOptions`.

| Parameter | Description                                                                                                                                                                                            |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| country   | The two-letter country code in which the payment will be processed. See a list of acceptable country codes [here](https://stripe.com/global). If no country is provided, 'US' will be used by default. |

```js
const canRenderButton = await digitalWallets.canRenderButton();

if (!canRenderButton) {
  digitalWallets.destroy();
  return;
}

await digitalWallets.initialize(digitalWalletsOptions);

digitalWallets.mountButton(submitDigitalWalletsPayment);
```

#### **updatePaymentDetails**

Updates payment details to be reflected in the browser's payment sheet _after_ the Digital Wallets instance has already been initialized. This should be used in scenarios where your button is defined on a page where any order details (tip amount, currency, fees, taxes, delivery fee, etc.) could be changed.

Calling `updatePaymentDetails` with the latest options will ensure that the payment sheet accurately reflects the most up-to-date order details.

```js
digitalWallets.updatePaymentDetails({
  currency: 'usd',
  total: {
    label: 'Pay [brand name]',
    amount: 7672,
  },
  displayItems: [
    { label: 'Subtotal', amount: 6248 },
    { label: 'Discount', amount: -625 },
    { label: 'Delivery Charge', amount: 299 },
    {
      label: 'Estimated Taxes & Fees',
      amount: 500,
    },
    { label: 'Tip', amount: 1250 },
  ],
});
```

#### **completePaymentEvent**

Completes the pending payment event. This should be called when the ordering API has returned a successful response.

```js
async function callback(paymentMethod) {
  if (paymentMethod) {
    // Use your basket submission API call here to send the payment method id as `token`
    try {
      await submitBasket({
        ...restOfBasketData,
        token: paymentMethod.id,
        cardType: paymentMethod.card.brand,
        cardLastFour: paymentMethod.card.last4,
      });
      // Signals to the Apple Pay or Google Pay payment sheet that the transaction has been processed
      // successfully and that the payment sheet can be closed
      digitalWallets.completePaymentEvent();
    } catch (error) {
      // Signals to the Apple Pay or Google Pay payment sheet that the payment has failed
      digitalWallets.failPaymentEvent();
    }
  }
}
```

#### **failPaymentEvent**

Fails the pending payment event. This should be called when the ordering API has returned a failed response.

```js
async function callback(paymentMethod) {
  if (paymentMethod) {
    // Use your basket submission API call here to send the payment method id as `token`
    try {
      await submitBasket({
        ...restOfBasketData,
        token: paymentMethod.id,
        cardType: paymentMethod.card.brand,
        cardLastFour: paymentMethod.card.last4,
      });
      // Signals to the Apple Pay or Google Pay payment sheet that the transaction has been processed
      // successfully and that the payment sheet can be closed
      digitalWallets.completePaymentEvent();
    } catch (error) {
      // Signals to the Apple Pay or Google Pay payment sheet that the payment has failed
      digitalWallets.failPaymentEvent();
    }
  }
}
```

#### **destroy**

Removes the `DigitalWallets` button from the DOM and destroys it so it cannot be re-mounted.

```js
digitalWallets.destroy();
```

#### **unmount**

Unmounts the `DigitalWallets` button from the DOM.

```js
digitalWallets.unmount();
```

## Handling the Payment Method

Whether using a checkout experience with `CreditCardElements` or `DigitalWallets`, the output of the library will be a [Stripe Payment Method object](https://stripe.com/docs/api/payment_methods). This object contains several pieces of data that need to be submitted through the Olo Ordering API basket submission request.

For more information about integrating the payment method with the Olo Ordering API, refer to the [documentation in our developer portal](https://developer.olo.com/docs/load/olopay#section/Submitting-a-Basket-via-the-Ordering-API).

## Google Pay test cards

Google provides a test card suite that allows testing a variety of cards from different card brands. Follow the instructions [here](https://developers.google.com/pay/api/web/guides/resources/test-card-suite) to activate Google's test card suite in Chrome.

Please note that the Google Pay button will only render if you have a real card saved to your [Google Pay account](pay.google.com) even if you decide to only use test cards when testing the Google Pay integration.

## Troubleshooting

### The Google Pay button is not rendering

Prerequisites: For both Desktop and Android versions of Chrome, your domain must be served over HTTPS with a valid SSL certificate.

#### **Desktop Chrome**

Ensure that

1. You have a valid credit card added to your [Google Pay](pay.google.com) account
2. The Google Pay account is the same one with which you are logged into Chrome
3. No browser ad-blocker browser extensions are running
4. You are using Chrome 61 or newer
5. You do not have any other tabs open with the Google Pay button
6. Your site is running securely via HTTPS

#### **Chrome for Android**

Ensure that

1. You have a valid card added to your [Google Wallet](https://play.google.com/store/apps/details?id=com.google.android.apps.walletnfcrel&pli=1) or [a saved card](https://support.google.com/chrome/answer/142893?co=GENIE.Platform%3DAndroid&hl=en)
2. You are using Chrome 61 or newer

### The Apple Pay button is not rendering

Prerequisites: For both Mac and iOS versions of Safari, your domain must be served over HTTPS with a valid SSL certificate. Also, make sure your domain is registered for Apple Pay by following [these steps](https://developer.olo.com/docs/load/olopay#section/Web-Applications/Setting-up-Apple-Pay-for-Web) in our developer portal.

Please note, for versions of iOS 16 and newer, Apple Pay might work in some non-Safari mobile browsers with a card saved in your Wallet.

#### **Safari for Mac**

Ensure that

1. You are using compatible device with a real card defined in macOS Wallet. More information on Apple's [support site](https://support.apple.com/en-us/HT204506)
2. The laptop lid is open if using a device with touch ID
3. No ad-blocker browser extensions are running
4. You are using macOS Sierra or later

#### **Safari for iOS**

Ensure that

1. You are using iOS 10.1 or later
2. You have a valid card configured in your Wallet (Settings --> Wallet & Apple Pay)

## Olo Pay JS Upgrades

| Release Date | Version | Description                                                   |
| ------------ | ------- | ------------------------------------------------------------- |
| 3/28/2024    | 2.0.0   | **Breaking change:** Changed all references of `CVC` to `CVV` |