npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@moonpay/platform-sdk-react-native

v1.22.0

Published

MoonPay Developer Platform SDK for React Native

Readme

@moonpay/platform-sdk-react-native

React Native SDK for the MoonPay Developer Platform. Connect customers to MoonPay, fetch quotes and payment methods, and render MoonPay frames (widget, Apple Pay, Google Pay, buy, add-card, challenge) as WebViews in your React Native app.

React Native guides are not yet published on dev.moonpay.com — this README is the current starting point. The platform concepts (sessions, connections, quotes, frames) are the same as for the web SDK, so the existing guides still apply.

Installation

npm install @moonpay/platform-sdk-react-native react-native-webview

For Expo projects:

npx expo install react-native-webview
npm install @moonpay/platform-sdk-react-native

Peer dependencies:

  • react >= 18
  • react-native >= 0.73 (Hermes supported)
  • react-native-webview >= 13

Crypto polyfill (required on React Native)

The SDK's crypto layer is pure JavaScript (@noble libraries), but key exchange needs a cryptographically-secure crypto.getRandomValues. React Native (Hermes) has no Web Crypto API, so you must install a polyfill and register it before the SDK runs — at your app entry, or a polyfills.ts you import first. Without it, the first getConnection() fails with a clear error telling you to add one.

Expo — expo-crypto provides a Web-Crypto-compatible getRandomValues but does not auto-install it on the global (works in Expo Go):

// polyfills.ts — imported first, before the MoonPay SDK
import * as Crypto from 'expo-crypto';

if (typeof globalThis.crypto?.getRandomValues !== 'function') {
  globalThis.crypto = {
    ...globalThis.crypto,
    getRandomValues: (array) => Crypto.getRandomValues(array),
  };
}

Bare React Native — react-native-get-random-values installs the global on import:

// index.js — first import, before the app
import 'react-native-get-random-values';

Quick start

1. Create a session server-side with @moonpay/platform-sdk-node and pass the session token to your app:

// On your server — never ship your API key in the app
import { createServerClient } from '@moonpay/platform-sdk-node';

const server = createServerClient({ apiKey: process.env.MOONPAY_API_KEY });

const result = await server.createSession({
  externalCustomerId: 'customer-123',
  deviceIp: customerIp,
});

if (result.ok) {
  // Send result.value.sessionToken to your app
}

2. Wrap your app in MoonPayProvider with the session token:

import { MoonPayProvider } from '@moonpay/platform-sdk-react-native';

export function App() {
  return (
    <MoonPayProvider sessionToken={sessionToken}>
      <YourApp />
    </MoonPayProvider>
  );
}

MoonPayProvider also accepts optional apiBaseUrl and frameBaseUrl props that point the SDK at a different MoonPay environment — useful if MoonPay gives you test endpoints during onboarding. Leave them unset to use production.

Pass an optional theme={{ appearance: 'light' | 'dark' }} to render every MoonPay frame in light or dark mode. Omit it to follow the customer's system preference. The connect flow can override it per call (client.connect({ theme }) or <MoonPayConnect theme={...} />).

Providing the session token later

The sessionToken prop is optional. If you don't have it when the provider mounts (for example, you fetch it from your server after the customer signs in), mount MoonPayProvider without it and call initialize() from useMoonPay() once the token is available:

import { MoonPayProvider, useMoonPay } from '@moonpay/platform-sdk-react-native';

export function App() {
  return (
    <MoonPayProvider>
      <YourApp />
    </MoonPayProvider>
  );
}

function SignInScreen() {
  const { initialize, isInitialized } = useMoonPay();

  const onSignedIn = async () => {
    const sessionToken = await fetchSessionTokenFromYourServer();
    initialize(sessionToken); // the SDK is now ready
  };

  // ...
}

isInitialized is true once a token is available (via the prop or initialize()). Calling a connection method before then resolves to an err() Result. You can also call initialize() again later to replace the token (for example, after a refresh). Choose one style — the controlled sessionToken prop or initialize(); if both are used, the prop wins.

Because initialize() updates React state, the client that carries the new token is available on the next render — not synchronously within the same call. Gate your connection flow on isInitialized (or read client from useMoonPay() after the re-render) rather than reusing a client captured before initialize():

function BuyScreen() {
  const { client, isInitialized } = useMoonPay();

  useEffect(() => {
    if (!isInitialized) return;
    // `client` here reflects the initialized session token
    client.getConnection().then((result) => {
      // ...
    });
  }, [isInitialized, client]);
}

Reusing a client reference captured before initialize() runs against the pre-initialization client and resolves to an err() (no session token).

3. Check the connection and connect the customer:

import { Button } from 'react-native';
import { useMoonPay } from '@moonpay/platform-sdk-react-native';

function BuyScreen() {
  const { client } = useMoonPay();

  const startConnect = async () => {
    const connection = await client.getConnection();
    if (!connection.ok) {
      console.error(connection.error);
      return;
    }

    if (connection.value.status === 'connectionRequired') {
      // Presents the connect flow full-screen so the customer can sign in to MoonPay
      await client.connect({
        onEvent: (event) => console.log(event.kind),
      });
    }
  };

  return <Button title="Get started" onPress={startConnect} />;
}

All client methods return a Result<T, E> discriminated union instead of throwing — check result.ok before using result.value.

Connection statuses

getConnection() resolves with one of six statuses. Handle connectionRequired and active at minimum:

| Status | Meaning | What to do | |--------|---------|------------| | connectionRequired | New or expired customer | Run the connect flow — client.connect() or <MoonPayConnect>. Headless integrations: <MoonPayAuth> | | active | Customer is connected | Proceed — quotes, payment methods, and payment frames are ready to use | | pending | KYC decision is delayed | Show a waiting state; the status can resolve to active on a later visit | | failed | Terminal failure, such as a KYC rejection | Do not retry the connect flow; the reason field describes the failure | | unavailable | Customer is in a restricted location | Hide MoonPay functionality | | termsAcceptanceRequired | Headless integrations only — the customer has no valid Terms of Use attestation on file | Show the Terms of Use in your UI, capture acceptance via POST /platform/v1/terms/attestations, then check the connection again |

Two ways to render frames

The SDK supports both a declarative and an imperative style:

  • Components (<MoonPayWidget>, <MoonPayApplePayButton>, …) render inline wherever you place them in your layout. Prefer these — you keep full control over placement, styling, and navigation.
  • Client methods (client.setupWidget(), client.connect(), …) present the frame in a full-screen modal managed by the SDK. Useful when you want a flow to take over the screen without layout work.

Headless frames (<MoonPayConnectionCheck>, <MoonPayBuyFrame>, <MoonPayConnectionReset>) render nothing visible — they run a flow in a hidden WebView and report results through onEvent.

Examples

Connect a customer inline

Render the connect flow as part of your own screen instead of a modal:

import { MoonPayConnect } from '@moonpay/platform-sdk-react-native';

function ConnectScreen({ onConnected }: { onConnected: () => void }) {
  return (
    <MoonPayConnect
      theme={{ appearance: 'dark' }}
      onEvent={(event) => {
        if (event.kind === 'complete') onConnected();
      }}
    />
  );
}

On completion, credentials are applied to the shared client automatically — you can call payment methods right away.

Authenticate with email and OTP (headless)

Identity API partners can use <MoonPayAuth> instead of the full connect flow — it drives the customer through email and OTP authentication only. The auth frame needs a clientToken, which the SDK stores automatically when getConnection() resolves with connectionRequired, so always check the connection first:

import { useState } from 'react';
import { Button } from 'react-native';
import { MoonPayAuth, useMoonPay } from '@moonpay/platform-sdk-react-native';

function SignInScreen({ onAuthenticated }: { onAuthenticated: () => void }) {
  const { client } = useMoonPay();
  const [showAuth, setShowAuth] = useState(false);

  const start = async () => {
    const connection = await client.getConnection();
    if (connection.ok && connection.value.status === 'connectionRequired') {
      setShowAuth(true); // the clientToken is now stored on the client
    }
  };

  if (!showAuth) {
    return <Button title="Sign in" onPress={start} />;
  }

  return (
    <MoonPayAuth
      onEvent={(event) => {
        if (event.kind === 'complete') onAuthenticated();
      }}
    />
  );
}

Mounting <MoonPayAuth> — or calling setupAuth() — without that prior getConnection() call emits an error event. On completion, credentials are applied to the shared client automatically, and event.payload.status is either active or termsAcceptanceRequired (see Connection statuses).

Get a quote and render the widget

import { useState } from 'react';
import { Button } from 'react-native';
import { MoonPayWidget, useMoonPay } from '@moonpay/platform-sdk-react-native';

function WidgetScreen() {
  const { client } = useMoonPay();
  const [quote, setQuote] = useState<string | null>(null);

  const loadQuote = async () => {
    const result = await client.getQuote({
      source: { asset: { code: 'USD' }, amount: '100' },
      destination: { asset: { code: 'BTC' } },
    });
    if (result.ok) {
      setQuote(result.value.data.signature);
    }
  };

  if (!quote) {
    return <Button title="Get quote" onPress={loadQuote} />;
  }

  return (
    <MoonPayWidget
      quote={quote}
      onEvent={(event) => {
        if (event.kind === 'complete') {
          console.log('Transaction complete', event.payload.transaction.id);
        }
      }}
    />
  );
}

Render an Apple Pay button

The button mounts inline at its natural size (height 48 by default). Quote updates are pushed into the frame without remounting:

import { MoonPayApplePayButton } from '@moonpay/platform-sdk-react-native';

<MoonPayApplePayButton
  quote={quoteSignature}
  onEvent={(event) => {
    switch (event.kind) {
      case 'complete':
        console.log('Paid', event.payload.transaction.id);
        break;
      case 'challenge':
        // Card issuer requires 3DS — see "Handle a 3DS challenge" below
        setChallengeUrl(event.payload.url);
        break;
      case 'quoteExpired':
        // Fetch a fresh quote and push it without remounting
        refreshQuote().then((signature) => event.payload.setQuote(signature));
        break;
      case 'unsupported':
        // Device or region does not support Apple Pay — render a fallback
        break;
    }
  }}
/>

<MoonPayGooglePayButton> and <MoonPayBuyButton> follow the same pattern.

Execute a buy headlessly

<MoonPayBuyFrame> executes a buy in a hidden WebView — your own UI stays in control the whole time. It needs an executable quote: one created with a wallet and paymentMethod. Render the frame when the customer confirms the purchase:

import { MoonPayBuyFrame } from '@moonpay/platform-sdk-react-native';

<MoonPayBuyFrame
  quote={quoteSignature}
  externalTransactionId="order-123"
  onEvent={(event) => {
    switch (event.kind) {
      case 'complete':
        console.log('Transaction complete', event.payload.transaction.id);
        break;
      case 'challenge':
        // Card issuer requires 3DS — see "Handle a 3DS challenge" below
        setChallengeUrl(event.payload.url);
        break;
      case 'error':
        console.error(event.payload.code, event.payload.message);
        break;
    }
  }}
/>

Like the payment buttons, quote is reactive — pushing a fresh signature does not remount the frame.

Handle a 3DS challenge

Payment frames (buy, buy button, Apple Pay, Google Pay) emit a challenge event when the card issuer requires verification. The event payload carries the full challenge URL:

// In the payment frame's onEvent handler:
case 'challenge':
  setChallengeUrl(event.payload.url);
  break;

Mount <MoonPayChallenge> with that URL. The challenge frame reports the final outcome itself — complete carries the finished transaction, and cancelled means the customer dismissed the verification. Unmount it on either event:

import { MoonPayChallenge } from '@moonpay/platform-sdk-react-native';

{challengeUrl && (
  <MoonPayChallenge
    url={challengeUrl}
    onEvent={(event) => {
      if (event.kind === 'complete' && event.payload.flow === 'buy') {
        console.log('Transaction complete', event.payload.transaction.id);
      }
      if (event.kind === 'complete' || event.kind === 'cancelled') {
        setChallengeUrl(null);
      }
    }}
  />
)}

Error handling

Methods never throw — they return Result<T, E>:

const result = await client.getPaymentMethods();

if (result.ok) {
  console.log(result.value.data);
} else {
  console.error(result.error.code, result.error.message);
}

What the SDK provides

Hook

  • useMoonPay() — returns the client from the nearest MoonPayProvider

Client methods

  • Connection — getConnection(), connect(), setupAuth() (requires a prior getConnection() call), resetConnection()
  • Payments data — getQuote(), getPaymentMethods(), deletePaymentMethod(), listTransactions(), getTransaction()
  • Frames — setupWidget(), setupBuy(), setupChallenge(), setupAddCard(), setupCustomerExport()
  • Customer — getCustomer(), submitCustomerKyc(), getCustomerUploadUrl(), submitCustomerFiles() for headless integrations
  • Identity (deprecated, use Customer above) — createIdentity(), getIdentity(), updateIdentity(), verifyIdentity() and file upload helpers

Components

| Component | Renders | |-----------|---------| | <MoonPayConnect> | Connect flow, inline | | <MoonPayConnectionCheck> | Headless connection check (nothing visible) | | <MoonPayConnectionReset> | Headless connection reset (nothing visible) | | <MoonPayAuth> | Email/OTP auth for headless / Customer API integrations — check the connection first | | <MoonPayCustomerExport> | Customer-export consent capture, inline | | <MoonPayWidget> | Full buy widget, inline | | <MoonPayApplePayButton> | Apple Pay button, inline | | <MoonPayGooglePayButton> | Google Pay button, inline | | <MoonPayBuyButton> | Card buy button, inline | | <MoonPayBuyFrame> | Headless buy execution (nothing visible) | | <MoonPayAddCard> | Card entry form, inline | | <MoonPayChallenge> | 3DS challenge, inline |

All visible components accept a style prop and an onEvent callback typed to that frame's events.

Related packages

| Package | Use it for | |---------|------------| | @moonpay/platform-sdk-node | Server-side session creation | | @moonpay/platform-sdk-web | Web apps | | @moonpay/platform-protocol | Shared protocol and API types |

Documentation

Platform guides and API reference: dev.moonpay.com

License

MIT