Skip to main content
This guide covers installing the Payment Widget SDK and setting it up in web and native applications.

Prerequisites

Installation

Install the SDK via npm:
npm install @uphold/enterprise-payment-widget-web-sdk

Web app integration

Import and initialize the SDK in your application. Make sure the container that will host the widget has explicit dimensions in CSS. The Widget will fill the container bounds. Minimum recommended size is 400px × 600px. The paymentMethods option lets you control which payment methods are available to users. Omit it to show all supported methods. See the SDK Reference for all configuration options.
import { PaymentWidget } from '@uphold/enterprise-payment-widget-web-sdk';

// Create a Payment Widget session (see the widget flows for examples)
const session = await createPaymentWidgetSession();

// Create the Widget instance with optional payment method filtering
const widget = new PaymentWidget(session, {
  debug: true,
  paymentMethods: [
    { type: 'card' },
    { type: 'bank' },
    { type: 'crypto', assets: { include: ['BTC', 'ETH', 'XRP'] } }
  ]
});

// Set up event handlers
widget.on('complete', (event) => {
  console.log('Payment completed:', event.detail.value);
  widget.unmount();
});

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

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

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

// Mount the Widget's iframe to a DOM element
widget.mountIframe(document.getElementById('payment-container'));
The session parameter is obtained by calling the Create Session endpoint.

Native app integration

Native mobile applications integrate the Payment Widget using a WebView component that loads an HTML page containing the Payment Widget SDK. To handle Payment Widget events, your native application needs to implement a communication bridge between the WebView and native code. This bridge enables your native app to receive and respond to events from the Payment Widget.

WebView HTML template

Create an HTML file that includes the Payment Widget SDK in your JS bundle:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Payment Widget</title>
    <style>
      body { margin: 0; padding: 0; }
      #payment-container { width: 100%; height: 100vh; }
    </style>
  </head>
  <body>
    <div id="payment-container"></div>

    <!-- Include your JS bundle which contains the SDK -->
    <script src="your-bundle-with-sdk.js"></script>
    <script>
      // Initialize Widget when page loads
      window.addEventListener('load', () => {
        const session = createPaymentWidgetSession();

        const widget = new PaymentWidget(session);

        widget.on('complete', (event) => {
          sendToNativeApp('complete', event.detail);
          widget.unmount();
        });

        widget.on('cancel', () => {
          sendToNativeApp('cancel');
          widget.unmount();
        });

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

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

      // Helper function to send events to native app
      function sendToNativeApp(type, data = null) {
        const message = { type, data };

        if (window.webkit?.messageHandlers?.paymentWidgetMessage) {
          // iOS - send all events through single handler
          window.webkit.messageHandlers.paymentWidgetMessage.postMessage(message);
        } else if (window.PaymentBridge) {
          // Android - send events through JavaScript interface
          window.PaymentBridge.onMessage(JSON.stringify(message));
        } else if (window.ReactNativeWebView) {
          // React Native - send events through postMessage
          window.ReactNativeWebView.postMessage(JSON.stringify(message));
        }
      }
    </script>
  </body>
</html>
The Payment Widget SDK must be included in your WebView bundle (for example, via your build pipeline). Loading the SDK directly from a CDN is not supported.

Setting up the WebView

Configure the WebView and event bridge for your platform:
import WebKit

class PaymentViewController: UIViewController, WKScriptMessageHandler {
  @IBOutlet weak var webView: WKWebView!

  override func viewDidLoad() {
    super.viewDidLoad()

    // Set up single message handler for all Payment Widget events
    let contentController = webView.configuration.userContentController
    contentController.add(self, name: "paymentWidgetMessage")

    // Load your HTML file with the Payment Widget
    if let url = Bundle.main.url(forResource: "payment-widget", withExtension: "html") {
      webView.loadFileURL(url, allowingReadAccessTo: url.deletingLastPathComponent())
    }
  }

  // Handle messages from the WebView
  func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    if message.name == "paymentWidgetMessage" {
      guard let messageDict = message.body as? [String: Any],
          let type = messageDict["type"] as? String else {
        return
      }

      let data = messageDict["data"]

      switch type {
        case "complete":
          handlePaymentComplete(data: data)
        case "cancel":
          handlePaymentCancel()
        case "error":
          handlePaymentError(error: data)
        default:
          print("Unknown Payment Widget message type: \(type)")
      }
    }
  }

  private func handlePaymentComplete(data: Any?) {
    // Handle successful payment completion
    print("Payment completed: \(data ?? "no data")")
    // Navigate to success screen or handle completion
  }

  private func handlePaymentCancel() {
    // Handle payment cancellation
    print("Payment cancelled")
    // Navigate back or show cancellation message
  }

  private func handlePaymentError(error: Any?) {
    // Handle payment error
    print("Payment error: \(error ?? "unknown error")")
    // Show error message to user
  }

  deinit {
    webView?.configuration.userContentController.removeScriptMessageHandler(forName: "paymentWidgetMessage")
  }
}

Troubleshooting

Widget not displaying The widget loads in an iframe, which requires proper Content Security Policy (CSP) configuration. Add the following domains to your CSP directives:
  • Sandbox: https://payment-widget.enterprise.sandbox.uphold.com
  • Production: https://payment-widget.enterprise.uphold.com
Example CSP configuration:
<meta 
  http-equiv="Content-Security-Policy" 
  content="frame-src 'self' https://payment-widget.enterprise.sandbox.uphold.com https://payment-widget.enterprise.uphold.com;"
>
Events not firing in native apps Verify that:
  • JavaScript is enabled in the WebView.
  • The message bridge is properly registered before the HTML page loads.
  • Event handlers match the platform-specific bridge implementation.

Next steps