> ## 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.

# Crypto deposit via the Payment Widget

> Accept crypto deposits with the Uphold Payment Widget for network selection and address display, then monitor incoming transactions on your backend.

The Payment Widget handles crypto network selection and displays the deposit address to the user. Your backend only needs to create the session and monitor for the incoming transfer.

<Warning>
  The Payment Widget does not create any transaction. Monitoring and processing the incoming transfer must be handled by your backend via webhooks or polling.
</Warning>

## Prerequisites

* The user has [completed onboarding](/developer-guides/user-onboarding/overview) and has the required capabilities enabled.
* The Payment Widget SDK is installed in your frontend. See [Installation and setup](/widgets/payment/installation-and-setup).

## Walkthrough

```mermaid theme={null}
sequenceDiagram
  autonumber
  participant Usr as User
  participant U as Your App
  participant B as Your Backend
  participant P as Payment Widget
  participant W as Travel Rule Widget
  participant A as Uphold
  participant C as Blockchain Network

  Usr->>U: Request deposit instructions
  U->>B: Create widget session
  B->>A: Create widget session (select-for-deposit)
  A-->>B: { session }
  B-->>U: { session }
  U->>P: Initialize widget
  Usr->>P: Select deposit method
  P-->>Usr: Display crypto deposit instructions
  Usr->>C: Send crypto transfer
  C-->>A: Incoming transfer received
  A-->>B: webhook: transaction.created (processing)
  C-->>A: Confirmations reached
  alt on-hold (pending RFI)
    A-->>B: webhook: transaction.status-changed (on-hold)
    B->>A: GET /core/transactions/{id}/requests-for-information
    A-->>B: { requestsForInformation }
    B->>A: POST /widgets/travel-rule/sessions
    A-->>B: { session }
    B-->>U: { session }
    U->>W: Initialize widget
    Usr->>W: Submit Travel Rule information
    W-->>U: complete { travelRule }
    U->>B: { travelRule }
    B->>A: PATCH /core/transactions/{id}/requests-for-information/{rfiId}
    A-->>B: RFI resolved
  end
  A-->>B: webhook: transaction.status-changed (completed/failed)
  B-->>Usr: Notify the user
```

***

## Select deposit method

The widget lets the user select a crypto network and view the deposit address.

### Create a widget session

Call the [Create widget session](/rest-apis/widgets-api/payment/create-session) endpoint to create a session for the `select-for-deposit` flow.

```http theme={null}
POST /widgets/payment/sessions
{
  "flow": "select-for-deposit"
}
```

A successful response contains a `session` object. Pass `response.session` to your frontend to initialize the widget.

```json theme={null}
{
  "session": {
    "flow": "select-for-deposit",
    "url": "https://payment.enterprise.uphold.com/",
    "token": "GEbRxBN...edjnXbL"
  }
}
```

### Set up the widget

Initialize the widget for the `select-for-deposit` flow using the session data returned from the API.

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

const initializeDepositWidget = async (session) => {
  const widget = new PaymentWidget<'select-for-deposit'>(session, { debug: true });

  widget.on('ready', () => {
    console.log('Ready');
  });

  widget.on('complete', (event) => {
    console.log('Complete', JSON.stringify(event.detail.value));
  });

  widget.on('cancel', () => {
    console.log('Cancelled');
    widget.unmount();
  });

  widget.on('error', (event) => {
    console.error('Error', event.detail.error);
    widget.unmount();
  });

  widget.mountIframe(document.getElementById('payment-container'));
};
```

<Info>
  The example above is for web applications. For native apps using a WebView, you'll need a bridge for events as outlined in [Installation & setup](/widgets/payment/installation-and-setup#native-application-integration).
</Info>

### Handle the complete event

The `complete` event fires when the user selects a deposit method and the widget displays the deposit address.

```javascript theme={null}
widget.on('complete', (event) => {
  const { via, selection } = event.detail.value;

  if (via === 'deposit-method') {
    const { depositMethod, account } = selection;

    if (depositMethod.type === 'crypto') {
      handleCryptoDeposit(depositMethod, account);
    }
  }

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

The event payload:

* `via` — set to `deposit-method` when the user completes the crypto network selection.
* `selection.account` — the account that will receive the deposit.
* `selection.depositMethod` — the deposit method with crypto transfer instructions.

The widget presents the deposit address and reference directly to the user. The `depositMethod.details` contains:

```json theme={null}
{
  "type": "crypto",
  "status": "ok",
  "details": {
    "network": "xrp-ledger",
    "asset": "XRP",
    "address": "rfBtmHiLwwWH5maH2PT78GxubrSydRF9aY",
    "reference": "3457810109"
  }
}
```

### Handle cancellations

The `cancel` event fires when the user closes the widget without completing the selection.

```javascript theme={null}
widget.on('cancel', () => {
  widget.unmount();
  // Redirect back or show a cancellation message
});
```

### Handle errors

The `error` event fires when an error occurs during the flow.

```javascript theme={null}
widget.on('error', (event) => {
  console.error('Widget error:', event.detail.error);
  widget.unmount();
  // Show a user-friendly error message
});
```

## Monitor for the incoming transfer

The widget presents the deposit instructions to the user but does not monitor for the incoming transfer. Your application must do this via webhooks or polling.

Prefer **webhooks** for real-time updates, or fall back to **polling** if webhooks are not feasible.

* Webhook events (recommended):
  * [core.transaction.created](/rest-apis/core-api/transactions/webhooks/transaction-created)
    * `status: processing` → detected on-chain but not yet confirmed
  * [core.transaction.status-changed](/rest-apis/core-api/transactions/webhooks/transaction-status-changed)
    * `status: completed` → necessary confirmations reached
    * `status: on-hold` → transaction checks paused (e.g., pending RFIs)
    * `status: failed` → irrecoverable error
* Polling (fallback): [Get transaction](/rest-apis/core-api/transactions/get-transaction)

## Sample transaction

In a successful crypto deposit, the origin is represented as a `crypto-address` node reflecting the sender's on-chain address. The destination is the account that was set up to receive the deposit.

```json [expandable] theme={null}
{
  "transaction": {
    "id": "223c24c5-76c6-4553-91bc-5af519441f03",
    "origin": {
      "asset": "BTC",
      "amount": "0.00121023",
      "rate": "1.00",
      "node": {
        "type": "crypto-address",
        "network": "bitcoin",
        "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
        "execution": {
          "mode": "onchain",
          "transactionHash": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
        }
      }
    },
    "destination": {
      "asset": "BTC",
      "amount": "0.00121023",
      "rate": "1.00",
      "node": {
        "type": "account",
        "id": "a00507fe-628c-4f27-ae81-e1c40b2a8fb8",
        "ownerId": "e4ce04dc-67b7-4e9f-af91-482cb6f9fc4a"
      }
    },
    "fees": [],
    "status": "completed",
    "quotedAt": "2024-07-24T15:02:39Z",
    "createdAt": "2024-07-24T15:22:39Z",
    "updatedAt": "2024-07-24T15:33:08Z",
    "denomination": {
      "asset": "BTC",
      "amount": "0.00121023",
      "rate": "1.00",
      "target": "origin"
    }
  }
}
```

### Execution modes

Crypto deposits are processed using different execution modes depending on the environment and configuration:

* **On-chain execution**: The transaction is processed directly on the blockchain network. The `origin.node.execution.mode` will be `onchain` and include a `transactionHash` as shown in the example above.
* **Off-chain execution**: When both the sender and recipient are Uphold users, the transaction is processed internally within Uphold's infrastructure. This eliminates network fees and is faster than on-chain processing. The `origin.node.execution.mode` will be `offchain`. The `execution` object for these transactions includes additional properties: `accountOwnerId` (the sender user ID) and `accountId` (the sender account ID).
* **Simulated execution (Test Helpers)**: Used in development environments for testing purposes ([Simulate crypto deposit](/rest-apis/core-api/accounts/test-helpers/simulate-crypto-deposit)). The transaction appears as processed but does not affect actual blockchain state (user balances will be affected though). The `origin.node.execution.mode` will be `simulated`.

## Handle on-hold transactions

If the crypto deposit is placed `on-hold` with reason `pending-requests-for-information`, resolve the pending RFIs before the deposit can complete. For the full step-by-step implementation, see the [Travel Rule deposit flow](/developer-guides/travel-rule/deposit) guide.

## Notify the user

Display an in-app confirmation when the transaction is `completed`, and send an email if applicable.

<Check>You now support crypto deposits via the Payment Widget.</Check>
