Skip to main content

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.

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.
The Payment Widget does not create any transaction. Monitoring and processing the incoming transfer must be handled by your backend via webhooks or polling.

Prerequisites

Walkthrough


Select deposit method

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

Create a payment session

Call the Create payment session endpoint to create a session for the select-for-deposit flow.
POST /widgets/payment/sessions
{
  "flow": "select-for-deposit"
}
A successful response returns a session object with a token and url to initialize the widget.
{
  "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.
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'));
};
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.

Handle the complete event

The complete event fires when the user selects a deposit method and the widget displays the deposit address.
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:
{
  "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.
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.
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.

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.
{
  "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: Used in development environments for testing purposes. 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 (Available soon).

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.

List pending RFIs

Call List requests for information to retrieve the pending RFIs for the transaction.
GET /core/transactions/{transactionId}/requests-for-information
{
  "requestsForInformation": [
    {
      "id": "3f6d0c1e-a1bf-4b25-9802-2a3ee492d3c8",
      "type": "travel-rule",
      "status": "pending",
      "data": {},
      "createdAt": "2024-07-24T15:22:39Z",
      "updatedAt": "2024-07-24T15:22:39Z"
    }
  ]
}

Travel Rule

Travel Rule is a regulatory requirement that mandates the collection and transmission of information about the originator and beneficiary of certain crypto transactions. Use the Travel Rule widget to collect the required information.

Create the widget session

Call Create session with "flow": "deposit-form" and the RFI id.
POST /widgets/travel-rule/sessions
{
  "flow": "deposit-form",
  "data": {
    "requestForInformationId": "3f6d0c1e-a1bf-4b25-9802-2a3ee492d3c8"
  }
}
{
  "session": {
    "flow": "deposit-form",
    "url": "https://widgets.uphold.com/travel-rule/sessions/xyz789",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "data": {
      "provider": "notabene",
      "parameters": {}
    }
  }
}

Open the widget

The example below is for web applications. For native apps using a WebView, see Native app integration.
import { TravelRuleWidget } from '@uphold/enterprise-travel-rule-widget-web-sdk';

const widget = new TravelRuleWidget<'deposit-form'>(session, { debug: true });

widget.on('complete', (event) => {
  const { value: travelRule } = event.detail;
  // Send travelRule data to your backend to update the RFI
  widget.unmount();
});
widget.on('cancel', error => ...);
widget.on('error', error => ...);

widget.mountIframe(document.getElementById('tr-deposit'));

Update the RFI

Once the widget emits complete, call Update request for information with the Travel Rule data.
PUT /core/transactions/{transactionId}/requests-for-information/{requestForInformationId}
{
  "data": {
    // Travel Rule data from complete event
  }
}
{
  "requestForInformation": {
    "id": "3f6d0c1e-a1bf-4b25-9802-2a3ee492d3c8",
    "type": "travel-rule",
    "status": "ok",
    "data": {},
    "createdAt": "2024-07-24T15:22:39Z",
    "updatedAt": "2024-07-24T15:25:12Z"
  }
}
Once the RFI is resolved, the transaction resumes processing automatically.

Notify the user

Display an in-app confirmation when the transaction is completed, and send an email if applicable.
You now support crypto deposits via the Payment Widget.