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

# @olo/pay-digital-wallets

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

## Introduction

A web library that enables Digital Wallets across various payment processors. It renders and hooks up Google Pay and Apple Pay buttons with a common interface.

## Installation

### NPM

```console
npm install @olo/pay-digital-wallets
```

### Yarn

```console
yarn add @olo/pay-digital-wallets
```

## TypeScript

`@olo/pay-digital-wallets` exports types for use in TypeScript. You can import them from the root of the package:

```ts
import type { GooglePayButtonStyles, ApplePayButtonStyles } from '@olo/pay-digital-wallets';
```

If you are using a version of TypeScript before 3.8, you can simply omit `type` from your import:

```ts
import { GooglePayButtonStyles, ApplePayButtonStyles } from '@olo/pay-digital-wallets';
```

## `DigitalWallets` Usage Examples

The `DigitalWallets` class is the main entry point for the library. Constructing a new instance of the `DigitalWallets` is as simple as passing in the current environment (either `"development"` or `"production"`).

```ts
const environment = 'development'; // Can be 'development' or 'production'
const digitalWallets = new DigitalWallets(environment);
```

### Environment

The environment will be passed on to Google Pay. See [their docs on `environment`](https://developers.google.com/pay/api/web/reference/request-objects#PaymentOptions) for more information. **NOTE:** Passing in `"development"` to the `DigitalWallets` constructor is translated to `TEST` in Google Pay.

### `DigitalWallets` methods

#### `initialize`

This should be called immediately after creating a new `DigitalWallets` instance. Please note that this method is `async` (returns a `Promise`) so you'll need to use `await` or `.then` in your code in order to get the resulting `CanMakePayment` value.

##### Signature

```ts
async initialize(params: DigitalWalletsInitParams): Promise<CanMakePayment>
```

##### Parameter Types

```ts
type SupportedCountries =
  | 'US' // United States
  | 'CA'; // Canada
type SupportedCurrencies =
  | 'USD' // Unite Stats Dollars
  | 'CAD'; // Canadian Dollars
type SupportedCardNetworks =
  | 'AMEX' // American Express
  | 'DISCOVER' // Discover
  | 'MASTERCARD' // Mastercard
  | 'VISA'; // Visa

type DisplayItemType =
  | 'LINE_ITEM' // Regular line item.
  | 'SUBTOTAL' // Subtotal of all regular items.
  | 'TAX' // Item for the collected tax.
  | 'DISCOUNT' // Item for a discount.
  | 'SHIPPING_OPTION'; // Item for shipping option

type DisplayItemStatus =
  | 'FINAL' // DisplayItem is final and the item displays the value in price.
  | 'PENDING'; // DisplayItem does not display value in price but instead display as "pending"

type DisplayItem = {
  // The label to be displayed for the item.
  label: string;

  // Price of this item.
  // The format of this string should follow the regular expression format:
  // `^[0-9]+(\.[0-9][0-9])?$` (e.g., `"10.45"`)
  price: string;

  // Type of displayed line item, this will be used for analysis purpose.
  type: DisplayItemType;

  // Optional. The default value is `FINAL` if not provided.
  status?: DisplayItemStatus | undefined;
};

type PaymentOptions = {
  // The currency code for the currency that the payment will use.
  currencyCode: SupportedCurrencies;

  // The country code for where this payment is being sent to.
  countryCode: SupportedCountries;

  // The total price for the payment.
  // This can be changed after initialization with the `updatePaymentOptions` method.
  totalPrice: string;

  // All the items that make up the order. See the DisplayItem type above.
  displayItems?: DisplayItem[];
};

type BillingContactFields =
  | 'email' // The user's email
  | 'name' // The user's name
  | 'phone' // The user's phone number
  | 'postalAddress' // The user's billing address
  | 'postalCode'; // The user's billing postal code

type MerchantValidationConfig = {
  // Your backend URL for verifying your merchant with Apple Pay.
  url: string;

  // Any additional headers that need to be sent to your backend during merchant verification with Apple Pay.
  // Unless overridden, we will send `'Content-Type': 'application/json'` in the headers.
  headers: Record<string, string>;

  // Any additional properties in the body that need to be sent to your backend
  // during merchant verification with Apple Pay. NOTE: This will be sent as JSON.
  body: Record<string, string>;
};

type DigitalWalletsInitParams = {
  // See the PaymentOptions type above.
  paymentOptions: PaymentOptions;

  // Card networks allowed for this order.
  cardNetworks: SupportedCardNetworks[];

  // Whether or not a billing address is required
  billingAddressRequired: boolean;

  // Which billing address fields are required.
  // See the BillingContactFields type above.
  requiredBillingContactFields?: BillingContactFields[];

  googlePayGateway: {
    // The `gateway` sent to your payment gateway tokenization service.
    name: string;

    // The `gatewayMerchantId` sent to your payment gateway tokenization service.
    merchantId: string;
  };

  merchant: {
    // User facing name for your merchant.
    name: string;

    // Your Apple pay merchantId
    applePayMerchantId: string;

    // Your Google pay merchantId
    googlePayMerchantId: string;

    // Configuration options for calling your Merchant Validation API
    // Used by Apple Pay. See MerchantValidationConfig type above.
    validationConfig: MerchantValidationConfig;
  };
};
```

##### Return Type

```ts
type CanMakePayment = {
  applePay: boolean; // Represents whether or not Apple Pay can be used
  googlePay: boolean; // Represents whether or not Google Pay can be used
};
```

#### `mountButtons`

Call this method after you've called initialized and your `<div data-pay-buttons></div>` has been rendered to the DOM. Should not be called if both `CanMakePayment.applePay` and `CanMakePayment.googlePay` are false. If either are true, the appropriate button(s) will be added to the DOM at the `div` you've given the `data-pay-buttons` attribute to (e.g. `<div data-pay-buttons></div>`).

##### Signature

```ts
mountButtons(
  paymentSuccessCallback: (paymentData: PaymentData) => void,
  paymentFailureCallback?: (error: unknown) => void,
  buttonStyles?: ButtonStyles
): void
```

`paymentSuccessCallback` will be called when a token has been successfully created. The callback can take in a `PaymentData` parameter and is expected to return `void`.

`paymentFailureCallback` is an optional parameter that will be called when there has been an error during the digital wallets processing. The `error` type is `unknown` as it could be an `Error` or something thrown by Google Pay or Apple Pay's SDKs.

`buttonStyles` is an optional parameter that allows custom styling on the Google Pay and Apple Pay buttons.

##### Parameter Types

```ts
type PaymentData = {
  // Whether the user used Google Pay or Apple Pay to order.
  walletType: 'GooglePay' | 'ApplePay';

  // See the DigitalWalletPaymentData type below
  paymentData: DigitalWalletPaymentData;
};

type DigitalWalletPaymentData = {
  // A user facing string to describe the payment method or card used by the user.
  description: string;

  // The billing address of the user. See the BillingAddress type below
  billingAddress: BillingAddress;

  // The last 4 digits of the card used.
  cardDetails: string;

  // The card network of the card. e.g. Visa, Mastercard, Amex, etc.
  cardNetwork: string;

  // A 64-bit encoded string of the token for the payment method.
  token: string;
};

type BillingAddress = {
  // Address Line 1
  address1?: string;

  // Address Line 2
  address2?: string;

  // Address Line 3
  address3?: string;

  // The state or province
  administrativeArea?: string;

  // The two-letter ISO 3166 country code.
  countryCode?: string;

  // The city or town
  locality?: string;

  // The user's full name
  name?: string;

  // The user's phone number
  phoneNumber?: string;

  // The postal or zip code
  postalCode?: string;
};

type ButtonStyles = {
  // See GooglePayButtonStyles type below.
  googlePay: GooglePayButtonStyles;

  // See ApplePayButtonStyles type below.
  applePay: ApplePayButtonStyles;
};

type GooglePayButtonStyles = {
  // See GooglePayButtonColor type below
  // Will default to `'default'`
  buttonColor?: GooglePayButtonColor;

  // The border radius of the button in px
  // Will default to `4`
  buttonRadius?: number;

  // See GooglePayButtonSizeMode type below
  // Will default to `'fill'`
  buttonSizeMode?: GooglePayButtonSizeMode;

  // See GooglePayButtonType type below
  // Will default to `'pay'`
  buttonType?: GooglePayButtonType;
};

type GooglePayButtonColor =
  | 'default' // Google selected default. Currently Black at the time of this writing.
  | 'black' // The button's background will be black.
  | 'white'; // The button's background will be white.
type GooglePayButtonSizeMode =
  | 'static' // Button will be a fixed width, 240px at the time of this writing.
  | 'fill'; // Button's width will fill its container
type GooglePayButtonType =
  | 'book' // Button will say "Book with G Pay"
  | 'buy' // Button will say "Buy with G Pay"
  | 'checkout' // Button will say "Checkout with G Pay"
  | 'donate' // Button will say "Donate with G Pay"
  | 'order' // Button will say "Order with G Pay"
  | 'pay' // Button will say "Pay with G Pay"
  | 'plain' // Button will say "G Pay"
  | 'subscribe' // Button will say "Subscribe with G Pay"
  | 'long' // Deprecated: The same as `buy`
  | 'short'; // Deprecated: The same as `plain`

type ApplePayButtonStyles = {
  // The width of the Apple Pay button
  // Will default to `100%`
  buttonWidth?: string;

  // The height of the Apple Pay button
  // Will default to `40px`
  buttonHeight?: string;

  // The border radius of the Apple Pay button. Must include units.
  // Units can be any standard CSS unit (e.g. pt, px, em, etc)
  // Will default to `5px`
  buttonRadius?: string;

  // The padding inside of the Apple Pay Button. Must include units.
  // Units can be any standard CSS unit (e.g. pt, px, em, etc)
  // Will default to `0`
  buttonPadding?: string;

  // See ApplePayButtonType type below
  // The button type of the Apple Pay Button. Will default to `'check-out'`
  buttonType?: ApplePayButtonType;

  // See ApplePayButtonStyle type below
  // The button style of the Apple Pay Button. Will default to `'black'`
  buttonStyle?: ApplePayButtonStyle;
};

type ApplePayButtonType =
  | 'add-money'
  | 'book'
  | 'buy'
  | 'check-out'
  | 'continue'
  | 'contribute'
  | 'donate'
  | 'order'
  | 'pay'
  | 'plain'
  | 'reload'
  | 'rent'
  | 'set-up'
  | 'subscribe'
  | 'support'
  | 'tip'
  | 'top-up';
type ApplePayButtonStyle = 'black' | 'white' | 'white-outline';
```

##### Return Type

`void` (i.e. this method does not return anything)

#### `unmountButtons`

Use this method to remove the Digital Wallet buttons from the DOM.

##### Signature

```ts
unmountButtons(): void
```

##### Parameter Types

None.

##### Return Types

`void` (i.e. this method does not return anything)

#### `updatePaymentOptions`

Use this method if anything under `PaymentOptions` or `OfferInfo` has changed since you called `initialize`.

##### Signature

```ts
updatePaymentOptions(options: PaymentOptions, offerInfo?: OfferInfo): void
```

##### Parameter Types

```ts
type PaymentOptions = {
  // The currency code for the currency that the payment will use.
  currencyCode: SupportedCurrencies;

  // The country code for where this payment is being sent to.
  countryCode: SupportedCountries;

  // The total price for the payment.
  totalPrice: string;

  // All the items that make up the order. See the DisplayItem type below.
  displayItems?: DisplayItem[];
};

type SupportedCountries =
  | 'US' // United States
  | 'CA'; // Canada
type SupportedCurrencies =
  | 'USD' // Unite Stats Dollars
  | 'CAD'; // Canadian Dollars
type SupportedCardNetworks =
  | 'AMEX' // American Express
  | 'DISCOVER' // Discover
  | 'MASTERCARD' // Mastercard
  | 'VISA'; // Visa

type DisplayItemType =
  | 'LINE_ITEM' // Regular line item.
  | 'SUBTOTAL' // Subtotal of all regular items.
  | 'TAX' // Item for the collected tax.
  | 'DISCOUNT' // Item for a discount.
  | 'SHIPPING_OPTION'; // Item for shipping option

type DisplayItemStatus =
  | 'FINAL' // DisplayItem is final and the item displays the value in price.
  | 'PENDING'; // DisplayItem does not display value in price but instead display as "pending"

type DisplayItem = {
  // The label to be displayed for the item.
  label: string;

  // Price of this item.
  // The format of this string should follow the regular expression format:
  // `^[0-9]+(\.[0-9][0-9])?$` (e.g., `"10.45"`)
  price: string;

  // Type of displayed line item, this will be used for analysis purpose.
  type: DisplayItemType;

  // Optional. The default value is `FINAL` if not provided.
  status?: DisplayItemStatus | undefined;
};
```

##### Return Types

`void` (i.e. this method does not return anything)

### Framework Examples

`@olo/pay-digital-wallets` can be used with any JavaScript framework you might be using. Below are a couple examples of usage in different frameworks.

#### React

```tsx
import {
  type ButtonStyles,
  type DisplayItem,
  type Environment,
  type PaymentData,
  DigitalWallets,
} from '@olo/pay-digital-wallets';
import { FC, useCallback, useEffect, useMemo } from 'react';

type Props = {
  displayItems: DisplayItem[];
  environment: Environment;
  paymentSuccessCallback: (paymentData: PaymentData) => void;
};

const calculateTotal = (items: DisplayItem[]): string => {
  return items
    .reduce((sum, item) => {
      if (item.type === 'DISCOUNT') {
        return sum - parseFloat(item.price);
      }
      return sum + parseFloat(item.price);
    }, 0)
    .toFixed(2);
};

const buttonStyles: ButtonStyles = {
  googlePay: {
    buttonColor: 'default',
    buttonRadius: 5,
    buttonSizeMode: 'static',
    buttonType: 'order',
  },
  applePay: {
    buttonWidth: '100%',
    buttonHeight: '40px',
    buttonRadius: '10px',
    buttonPadding: '0px',
  },
};

export const DigitalWalletsButtons: FC<Props> = ({ environment, displayItems, paymentSuccessCallback }) => {
  const digitalWallets = useMemo(() => new DigitalWallets(environment), [environment]);

  const paymentFailureCallback = useCallback((error: unknown) => {
    console.error(error);
  }, []);

  useEffect(() => {
    const initializeAndMountButtons = async () => {
      const canMakePayment = await digitalWallets.initialize({
        paymentOptions: {
          currencyCode: 'USD',
          countryCode: 'US',
          totalPrice: calculateTotal(displayItems),
          displayItems,
        },
        cardNetworks: ['AMEX', 'VISA'],
        billingAddressRequired: true,
        requiredBillingContactFields: ['email', 'name', 'phone', 'postalAddress', 'postalCode'],
        googlePayGateway: {
          name: 'Foosburgers',
          merchantId: '123456',
        },
        merchant: {
          name: 'Foosburgers',
          applePayMerchantId: 'foo',
          googlePayMerchantId: 'foo',
          validationConfig: {
            url: '/api/merchantverification',
            headers: {
              foo: 'bar',
            },
            body: {
              foo: 'bar',
            },
          },
        },
      });

      if (canMakePayment.applePay || canMakePayment.googlePay) {
        digitalWallets.mountButtons(paymentSuccessCallback, paymentFailureCallback, buttonStyles);
      }
    };

    void initializeAndMountButtons();

    return () => {
      digitalWallets.unmountButtons();
    };

    // NOTE: We did not include `displayItems` or `paymentSuccessCallback` here
    // since we only want to call initialize once per DigitalWallets instance.
  }, [digitalWallets]);

  useEffect(() => {
    digitalWallets.updatePaymentOptions({
      currencyCode: 'USD',
      countryCode: 'US',
      totalPrice: calculateTotal(displayItems),
      displayItems,
    });
  }, [digitalWallets, displayItems]);

  return <div data-pay-buttons />;
};
```

#### Vue

```vue
<script setup lang="ts">
import { watchEffect, onMounted } from 'vue'
import {
  type ButtonStyles,
  type DisplayItem,
  type Environment,
  type PaymentData,
  DigitalWallets,
} from '@olo/pay-digital-wallets'

const emit = defineEmits(['payment-successful'])
type Props = {
  displayItems: DisplayItem[]
  environment: Environment
}
const { displayItems, environment } = defineProps<Props>()

const calculateTotal = (items: DisplayItem[]): string => {
  return items
    .reduce((sum, item) => {
      if (item.type === 'DISCOUNT') {
        return sum - parseFloat(item.price)
      }
      return sum + parseFloat(item.price)
    }, 0)
    .toFixed(2)
}

const buttonStyles: ButtonStyles = {
  googlePay: {
    buttonColor: 'default',
    buttonRadius: 5,
    buttonSizeMode: 'static',
    buttonType: 'order',
  },
  applePay: {
    buttonWidth: '100%',
    buttonHeight: '40px',
    buttonRadius: '10px',
    buttonPadding: '0px',
  },
}

const digitalWallets = new DigitalWallets(environment)

const paymentFailureCallback = (error: unknown) => {
  console.error(error)
}

const canMakePayment = await digitalWallets.initialize({
  paymentOptions: {
    currencyCode: 'USD',
    countryCode: 'US',
    totalPrice: calculateTotal(displayItems),
    displayItems,
  },
  cardNetworks: ['AMEX', 'VISA'],
  billingAddressRequired: true,
  requiredBillingContactFields: ['email', 'name', 'phone', 'postalAddress', 'postalCode'],
  googlePayGateway: {
    name: 'Foosburgers',
    merchantId: '123456',
  },
  merchant: {
    name: 'foosburgers',
    applePayMerchantId: 'foo',
    googlePayMerchantId: 'foo',
    validationConfig: {
      url: '/api/merchantverification',
      headers: {
        foo: 'bar',
      },
      body: {
        foo: 'bar',
      },
    },
  },
})

onMounted(() => {
  if (canMakePayment.applePay || canMakePayment.googlePay) {
    digitalWallets.mountButtons(
      (data: PaymentData) => emit('payment-successful', data),
      paymentFailureCallback,
      buttonStyles,
    )
  }
})

watchEffect(() => {
  digitalWallets.updatePaymentOptions({
    currencyCode: 'USD',
    countryCode: 'US',
    totalPrice: calculateTotal(displayItems),
    displayItems,
  })
})
</script>

<template>
  <div data-pay-buttons></div>
</template>
```

## Troubleshooting

We did our best to provide helpful errors in the developer console so always check there first if something is going wrong.

### My canMakePayment.applePay returned from `initialize` is always false

Make sure you're using `HTTPS` to serve your site. Apple does a check for this and won't allow the button to be rendered if `http` is being used.

### My Apple Pay modal just spins forever

Close the modal and check your Network tab in your developer tools to see if your merchant verification request has failed. This is the most common reason for this happening.

### Google Pay is giving a generic error with `[OR_BIBED_06] OR_BIBED_06` in it.

Unfortunately this is a fairly generic error from Google Pay. A couple things to check is to make sure you're passing in the correct `environment` to the `DigitalWallets` constructor. As well, double check the `googlePayGateway` properties when calling `initialize`. These are a common source of problems if they don't line up with what your payment processor is expecting.

## Version History

| Release Date | Version | Description            |
| ------------ | ------- | ---------------------- |
| 2025-09-05   | 0.6.8   | Initial Public Release |