> ## Documentation Index
> Fetch the complete documentation index at: https://developer.uphold.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Payment Widget SDK reference

> Reference for the @uphold/enterprise-payment-widget-web-sdk package, including the PaymentWidget class, constructor options, methods, and lifecycle events.

Complete reference documentation for the `@uphold/enterprise-payment-widget-web-sdk` package.

The main class for creating and managing Payment Widget instances is `PaymentWidget`. It requires a `PaymentWidgetSession` object that must be created through the API before instantiating the Widget.

## Constructor

```typescript theme={null}
new PaymentWidget<T extends PaymentWidgetFlow = PaymentWidgetFlow>(
  session: PaymentWidgetSession,
  options?: PaymentWidgetOptions
)
```

**Parameters:**

| Parameter | Type                   | Required | Description                                                                                                       |
| --------- | ---------------------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `session` | `PaymentWidgetSession` | Yes      | Payment session object obtained from the [Create session](/rest-apis/widgets-api/payment/create-session) endpoint |
| `options` | `PaymentWidgetOptions` | No       | Configuration options for the Widget                                                                              |

**Generic type parameter:**

The constructor accepts an optional generic type parameter that specifies the flow type. When provided, it enables better type inference for event handlers, particularly for the `complete` event.

**Examples:**

```typescript theme={null}
// Generic flow type (default)
const widget = new PaymentWidget(session);

// Specific flow type for better type inference
const depositWidget = new PaymentWidget<'select-for-deposit'>(session);
const withdrawWidget = new PaymentWidget<'select-for-withdrawal'>(session);
const authorizeWidget = new PaymentWidget<'authorize'>(session);
```

### Options

```typescript theme={null}
type PaymentWidgetOptions = {
  debug?: boolean;
  layout?: WidgetLayout;
  maxAccountsPerAsset?: number;
  paymentMethods?: PaymentMethodOption[];
  theme?: WidgetThemeOption;
};
```

| Property              | Type                    | Default               | Description                                                                                                                                                                                                                |
| --------------------- | ----------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `debug`               | `boolean`               | `false`               | Enable debug mode for additional logging                                                                                                                                                                                   |
| `layout`              | `WidgetLayout`          | `'boxed'`             | Control how the Widget is laid out on larger viewports. See [WidgetLayout](#widgetlayout) for details                                                                                                                      |
| `maxAccountsPerAsset` | `number`                | No limit              | Limit the number of accounts that can be created per asset. When the limit is reached, the Widget reuses the most recent account instead of creating a new one. Must be greater than `0`. Maximum effective value is `100` |
| `paymentMethods`      | `PaymentMethodOption[]` | All available methods | Restrict which payment methods are available to users. See [PaymentMethodOption](#paymentmethodoption) for details                                                                                                         |
| `theme`               | `WidgetThemeOption`     | System preference     | Control the visual appearance of the Widget. See [WidgetThemeOption](#widgetthemeoption) for details                                                                                                                       |

**paymentMethods option:**

The `paymentMethods` option allows you to control which payment methods the Widget displays to users. When not specified, all supported payment methods are available.

**Basic usage:**

```typescript theme={null}
const widget = new PaymentWidget(session, {
  paymentMethods: [
    { type: 'card' },
    { type: 'bank' },
    { type: 'crypto' },
    { type: 'paypal' }
  ]
});
```

**Filtering assets:**

For `bank` and `crypto` payment methods, you can filter which assets are available using the `assets` property. This works the same way for both methods:

```typescript theme={null}
const widget = new PaymentWidget(session, {
  paymentMethods: [
    { type: 'card' },
    {
      type: 'bank',
      assets: {
        include: ['GBP', 'EUR', 'ETH', 'BTC']  // Only these assets will be available
      }
    },
    {
      type: 'crypto',
      assets: {
        include: ['BTC', 'ETH', 'XRP']  // Only these assets will be available
      }
    }
  ]
});
```

You can also exclude specific assets while allowing all others:

```typescript theme={null}
const widget = new PaymentWidget(session, {
  paymentMethods: [
    {
      type: 'bank',
      assets: {
        exclude: ['BTC']  // All assets except BTC
      }
    },
    {
      type: 'crypto',
      assets: {
        exclude: ['DOGE', 'SHIB']  // All assets except these
      }
    }
  ]
});
```

<Note>If no `assets` filter is provided, all supported assets for that payment method will be available. Use either `include` or `exclude`, not both.</Note>

**theme option:**

The `theme` option lets you control the visual appearance of the Widget. By default, the Widget automatically detects and matches the user's browser or OS appearance preference (`light` or `dark`). Use this option when you want to enforce a specific theme regardless of the user's system settings.

```typescript theme={null}
const widget = new PaymentWidget(session, {
  theme: {
    appearance: 'dark',
    primary: { light: '#6200EA', dark: '#BB86FC' },
    // Alternatively, a single color string applies to both appearances, e.g. primary: '#6200EA'
    // primary: '#6200EA',
    fontFamily: 'Inter, sans-serif',
    components: {
      button: { borderRadius: '8px' }
    }
  }
});
```

**layout option:**

The `layout` option controls how the Widget is laid out when there is enough room to frame it — on viewports that are both at least `md` wide (≥768px) and at least 600px tall, i.e. tablets and up. On smaller viewports — narrower or shorter, including phones in landscape — the Widget always fills its container regardless of this option.

* `'boxed'` (default): on viewports that are ≥768px wide and ≥600px tall, centers the Widget and frames its content as a fixed-size card; on smaller viewports it fills its container. Use this when the Widget takes up the whole page (e.g. mounted into a full-page container).
* `'fluid'`: renders the Widget so it fills its container, with no frame or centering, so your own container (e.g. a modal or a sized panel) acts as the frame.

Most integrations should rely on the default `'boxed'` and only set `layout: 'fluid'` when embedding the Widget in your own framed container.

```typescript theme={null}
const widget = new PaymentWidget(session, {
  layout: 'fluid'
});
```

<Note>Use `'fluid'` when you already provide a centered container or modal around the Widget, to avoid a card-within-a-card appearance on larger viewports.</Note>

**maxAccountsPerAsset option:**

When generating deposit details, depending on the rail's constraints, the Widget allows users to select a target account for the deposit. The `maxAccountsPerAsset` option controls how many accounts can be created per asset. For example, setting it to 1 ensures only a single account exists per asset, while setting it to 5 allows up to five accounts for the same asset (e.g. one USD account for general use, another for a vacation fund, and so on). Once the limit is reached, the Widget automatically reuses the most recent account instead of creating a new one.

```typescript theme={null}
const widget = new PaymentWidget(session, {
  maxAccountsPerAsset: 5
});
```

<Note>Values above `100` are capped at `100`.</Note>

## Methods

### `mountIframe()`

Mounts the Payment Widget iframe to the specified DOM element.

**Parameters:**

* `element`: The HTML element where the Widget should be mounted

**Example:**

```javascript theme={null}
// Ensure the container has appropriate sizing
const container = document.getElementById('payment-container');

widget.mountIframe(container);
```

<Info>The container element should have explicit dimensions set via CSS. The Widget will fill the entire container. Minimum recommended size is 400px width × 600px height for optimal user experience.</Info>

### `unmount()`

Unmounts and cleans up the Payment Widget iframe.

**Example:**

```javascript theme={null}
widget.unmount();
```

<Warning>The Widget will not unmount itself when a flow is finished, either by successful completion, user cancellation, or unrecoverable error, so it must always be called manually.</Warning>

### `on()`

Registers an event listener for Widget events.

**Parameters:**

* `event`: The event name to listen for
* `callback`: Function to execute when the event is triggered

**Example:**

```javascript theme={null}
widget.on('complete', (event) => {
  console.log('Payment completed:', event.detail.value);
});
```

### `off()`

Removes an event listener for Payment Widget events.

**Parameters:**

* `event`: The event name to stop listening for
* `callback`: The specific function to remove (must be the same reference as used in `on()`)

**Example:**

```javascript theme={null}
const handleComplete = (event) => {
  console.log('Payment flow completed:', event.detail.value);
};

// Register the listener
widget.on('complete', handleComplete);

// Remove the listener
widget.off('complete', handleComplete);
```

## Events

The Payment Widget emits several events during its lifecycle that you can listen to using the `on()` method.

### `complete`

Fired when the Payment flow is completed.

```typescript theme={null}
type PaymentWidgetCompleteValue<T extends PaymentWidgetFlow = PaymentWidgetFlow> =
  T extends 'select-for-deposit' ? DepositSelection :
  T extends 'select-for-withdrawal' ? WithdrawalSelection :
  T extends 'authorize' ? AuthorizeResult :
  never;

type PaymentWidgetCompleteEvent<T extends PaymentWidgetFlow = PaymentWidgetFlow> = {
  detail: {
    value: PaymentWidgetCompleteValue<T>;
  };
};
```

The `event.detail.value` contains the payment flow result. The structure depends on the flow type.

**Example:**

```typescript theme={null}
widget.on('complete', (event: PaymentWidgetCompleteEvent) => {
  const result = event.detail.value;

  console.log('Payment flow completed:', result);

  // Process the result based on your flow type
  // See flow-specific examples below

  widget.unmount();
});
```

**Usage in different flows:**

For `select-for-deposit` flows:

```typescript theme={null}
const depositWidget = new PaymentWidget<'select-for-deposit'>(session);

depositWidget.on('complete', (event) => {
  const result = event.detail.value;

  if (result.via === 'external-account') {
    // User selected a saved payment method (e.g., card)
    console.log('Selected external account:', result.selection);
  } else if (result.via === 'deposit-method') {
    // User selected a bank or crypto transfer method
    const { depositMethod, account } = result.selection;

    if (depositMethod.type === 'bank') {
      console.log('Bank transfer details:', depositMethod.details);
    } else if (depositMethod.type === 'crypto') {
      console.log('Crypto transfer details:', depositMethod.details);
    }

    console.log('Target account:', account);
  }

  depositWidget.unmount();
});
```

For `select-for-withdrawal` flows:

```typescript theme={null}
const withdrawWidget = new PaymentWidget<'select-for-withdrawal'>(session);

withdrawWidget.on('complete', (event) => {
  const result = event.detail.value;

  if (result.via === 'external-account') {
    // User selected a saved payment method (e.g., card, bank account)
    console.log('Selected external account:', result.selection);
  } else if (result.via === 'crypto-network') {
    // User provided a crypto withdrawal address
    console.log('Crypto withdrawal details:', {
      asset: result.selection.asset,
      network: result.selection.network,
      address: result.selection.address,
      reference: result.selection.reference // Destination tag for XRP, memo for XLM, etc.
    });
  }

  withdrawWidget.unmount();
});
```

For `authorize` flows:

```typescript theme={null}
const authorizeWidget = new PaymentWidget<'authorize'>(session);

authorizeWidget.on('complete', (event) => {
  const result = event.detail.value;

  if (result.trigger.reason === 'transaction-status-changed') {
    // Check transaction status - it may have succeeded or failed
    if (result.transaction.status === 'completed') {
      console.log('Transaction successful:', result.transaction);
    } else if (result.transaction.status === 'failed') {
      console.error('Transaction failed:', result.transaction);
      // Handle failure case
    }
  } else if (result.trigger.reason === 'max-retries-reached') {
    console.log('Polling timeout reached, transaction may still be processing: ', result.transaction);
    // Implement additional polling or show a message indicating that the transaction is still processing and to try again later
  }

  authorizeWidget.unmount();
});
```

### `cancel`

Fired when the user cancels the payment flow.

```typescript theme={null}
type PaymentWidgetCancelEvent = {
  detail: {};
}
```

**Example:**

```javascript theme={null}
widget.on('cancel', (event: PaymentWidgetCancelEvent) => {
  console.log('Payment cancelled by user');
  widget.unmount();
});
```

### `error`

Fired when an unrecoverable error occurs during the payment flow.

```typescript theme={null}
type PaymentWidgetError = {
  name: string;
  code: string;
  message: string;
  details?: Record<string, unknown>;
  cause?: PaymentWidgetError;
  httpStatusCode?: number;
}

type PaymentWidgetErrorEvent = {
  detail: {
    error: PaymentWidgetError;
  };
};
```

**Example:**

```javascript theme={null}
widget.on('error', (event: PaymentWidgetErrorEvent) => {
  const { error } = event.detail;
  console.error('Payment error:', error);

  // Handle specific error types
  if (error.code === 'entity_not_found') {
    // Quote expired - redirect to create new quote
    handleExpiredQuote();
  } else if (error.code === 'insufficient_balance') {
    // Not enough funds - show funding options
    handleInsufficientFunds();
  } else {
    // Generic error handling
    showGenericError(error.message);
  }

  widget.unmount();
});
```

### `ready`

Fired when the Payment Widget has finished loading.

```typescript theme={null}
type PaymentWidgetReadyEvent = {
  detail: {};
}
```

**Example:**

```typescript theme={null}
widget.on('ready', (event: PaymentWidgetReadyEvent) => {
  console.log('Payment Widget is ready');
});
```

## Types

### PaymentWidgetSession

The session object returned by the [Create session](/rest-apis/widgets-api/payment/create-session) endpoint.

```typescript theme={null}
type PaymentWidgetSession = {
  url: string;
  token: string;
  flow: PaymentWidgetFlow;
}
```

### `PaymentWidgetFlow`

Represents the different flows supported by the Payment Widget.

```typescript theme={null}
type PaymentWidgetFlow = 'select-for-deposit' | 'select-for-withdrawal' | 'authorize';
```

### PaymentMethodOption

Defines the payment methods that can be configured in the Widget options.

```typescript theme={null}
type PaymentMethodOption =
  | { type: 'card' }
  | { type: 'bank'; assets?: PaymentAssetOptions }
  | { type: 'crypto'; assets?: PaymentAssetOptions }
  | { type: 'paypal' };
```

| Type     | Description                                                       |
| -------- | ----------------------------------------------------------------- |
| `card`   | Credit and debit card payments                                    |
| `bank`   | Push bank deposit methods. Supports optional `assets` filtering   |
| `crypto` | Crypto deposits/withdrawals. Supports optional `assets` filtering |
| `paypal` | PayPal deposits/withdrawals                                       |

### PaymentAssetOptions

Allows filtering which assets are available when using the `bank` or `crypto` payment methods.

```typescript theme={null}
type PaymentAssetOptions = {
  include?: string[];
  exclude?: string[];
};
```

| Property  | Type       | Description                                                                               |
| --------- | ---------- | ----------------------------------------------------------------------------------------- |
| `include` | `string[]` | List of asset codes to include. When specified, only these assets will be available       |
| `exclude` | `string[]` | List of asset codes to exclude. When specified, all assets except these will be available |

<Note>Use either `include` or `exclude`, not both. Asset codes should be uppercase (e.g., `'BTC'`, `'ETH'`, `'XRP'`).</Note>

### Complete event result types

#### DepositSelection

Result structure for `select-for-deposit` flow:

```typescript theme={null}
type ExternalAccountSelection = {
  via: 'external-account';
  selection: ExternalAccount; // See external accounts API documentation
};

type AccountDepositMethodSelection = {
  via: 'deposit-method';
  selection: {
    depositMethod: AccountDepositMethod; // See account deposit methods API documentation
    account: Account; // See accounts API documentation
  };
};

type DepositSelection = ExternalAccountSelection | AccountDepositMethodSelection;
```

<Note>
  For complete type definitions, see the API documentation:

  * `ExternalAccount`: [External accounts](/rest-apis/core-api/external-accounts/introduction)
  * `AccountDepositMethod`: [Account deposit methods](/rest-apis/core-api/accounts/get-account-deposit-method)
  * `Account`: [Accounts](/rest-apis/core-api/accounts/introduction)
</Note>

#### WithdrawalSelection

Result structure for `select-for-withdrawal` flow:

```typescript theme={null}
type ExternalAccountSelection = {
  via: 'external-account';
  selection: ExternalAccount; // See external accounts API documentation
};

type CryptoNetworkSelection = {
  via: 'crypto-network';
  selection: {
    asset: string;  // e.g., 'BTC', 'ETH', 'XRP'
    network: string;  // e.g., 'bitcoin', 'ethereum', 'xrp-ledger'
    address: string;  // The destination crypto address
    reference?: string;  // Destination tag for XRP, memo for XLM, etc.
  };
};

type WithdrawalSelection = ExternalAccountSelection | CryptoNetworkSelection;
```

<Note>
  For complete `ExternalAccount` type definition, see the [External accounts](/rest-apis/core-api/external-accounts/introduction) API documentation.
</Note>

#### AuthorizeResult

Result structure for `authorize` flow:

```typescript theme={null}
type AuthorizeResult = {
  transaction: Transaction; // See Transactions API documentation
  trigger: {
    reason: 'transaction-status-changed' | 'max-retries-reached';
  };
}
```

<Note>For complete `Transaction` type definition, see the [Transactions](/rest-apis/core-api/transactions/introduction) API documentation.</Note>

### WidgetLayout

Controls how the Widget is laid out on larger viewports.

```typescript theme={null}
type WidgetLayout = 'boxed' | 'fluid';
```

| Value     | Description                                                                                                                                                                              |
| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `'boxed'` | Default. On viewports that are ≥768px wide and ≥600px tall (tablets and up), centers the Widget and frames its content as a fixed-size card; on smaller viewports it fills its container |
| `'fluid'` | Renders the Widget so it fills its container, with no frame or centering, so your own container acts as the frame                                                                        |

### WidgetThemeOption

Controls the visual appearance of the Widget.

```typescript theme={null}
type WidgetThemeOption = {
  appearance?: 'light' | 'dark';
  primary?: ColorTokenValue;
  primaryForeground?: ColorTokenValue;
  foreground?: ColorTokenValue;
  emphasisForeground?: ColorTokenValue;
  background?: ColorTokenValue;
  fontFamily?: FontFamily;
  components?: {
    button?: { borderRadius?: string };
    input?: { borderRadius?: string };
    card?: { borderRadius?: string };
  };
};
```

| Property             | Type                | Description                                                                                            |
| -------------------- | ------------------- | ------------------------------------------------------------------------------------------------------ |
| `appearance`         | `'light' \| 'dark'` | Forces the Widget to render in light or dark mode, overriding the user's system preference             |
| `primary`            | `ColorTokenValue`   | Primary brand color, used for buttons and key interactive elements                                     |
| `primaryForeground`  | `ColorTokenValue`   | Text color rendered on top of the primary color                                                        |
| `foreground`         | `ColorTokenValue`   | Main text color                                                                                        |
| `emphasisForeground` | `ColorTokenValue`   | Emphasized text color                                                                                  |
| `background`         | `ColorTokenValue`   | Widget background color                                                                                |
| `fontFamily`         | `FontFamily`        | Font family applied across the Widget. See [FontFamily](#fontfamily) for details                       |
| `components`         | `object`            | Per-component style overrides for `button`, `input`, and `card` (each accepts `borderRadius?: string`) |

### ColorTokenValue

A color value that can be a single string applied to both light and dark modes, or separate values per mode.

```typescript theme={null}
type ColorTokenValue = string | { light: string; dark: string };
```

### FontFamily

A font family that can be a single string applied to all slots, or separate values per slot (`mono`, `sans`, `serif`).

```typescript theme={null}
type FontFamily = string | Partial<Record<'mono' | 'sans' | 'serif', string>>;
```

## Complete usage example

Here's an end-to-end example showing how to use the Payment Widget with all events and type safety:

```typescript theme={null}
import { PaymentWidget } from '@uphold/enterprise-payment-widget-web-sdk';

// Create a Payment Widget session from your backend
const session = await createPaymentWidgetSession();

// Initialize the Widget with type inference and configuration
const widget = new PaymentWidget<'select-for-deposit'>(session, {
  debug: true,
  paymentMethods: [
    { type: 'card' },
    { type: 'bank' },
    { type: 'crypto', assets: { include: ['BTC', 'ETH', 'XRP'] } },
    { type: 'paypal' }
  ]
});

// Handle ready event
widget.on('ready', () => {
  console.log('Payment Widget is ready');
});

// Handle completion with flow-specific type-safe result
widget.on('complete', (event) => {
  const result = event.detail.value;

  if (result.via === 'external-account') {
    console.log('User selected saved payment method:', result.selection);
  } else if (result.via === 'deposit-method') {
    console.log('User selected deposit method:', result.selection.depositMethod);
    console.log('Target account:', result.selection.account);
  }

  widget.unmount();
});

// Handle cancellation
widget.on('cancel', () => {
  console.log('Payment cancelled by user');
  widget.unmount();
});

// Handle errors with specific error handling
widget.on('error', (event) => {
  const { error } = event.detail;
  console.error('Payment error:', error);

  if (error.code === 'entity_not_found') {
    handleExpiredQuote();
  } else if (error.code === 'insufficient_balance') {
    handleInsufficientFunds();
  } else {
    showGenericError(error.message);
  }

  widget.unmount();
});

// Mount the Widget to a DOM element
widget.mountIframe(document.getElementById('payment-container'));
```
