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

@xmoney/react-native

v1.1.0

Published

xMoney React Native SDK: thin wrapper over xmoney-ios and xmoney-android PaymentSheet, Payment Element, and wallet buttons.

Readme

xMoney Payments — React Native SDK

React Native SDK for xMoney checkout. Three surfaces, one PaymentConfig, one PaymentResult. Card data never crosses the JavaScript bridge.

| Surface | API | Use when | | ----------------- | ------------------------------------------------ | ------------------------------------------------------- | | Payment Sheet | PaymentSheet, usePaymentSheet | Drop-in bottom sheet. SDK owns the UI and the Pay button. | | Payment Element | <PaymentElement /> | Card, saved cards, and wallet in your layout. | | Apple Pay | ApplePay, <ApplePayButton /> (iOS) | Standalone wallet button or present(). | | Google Pay | GooglePay, <GooglePayButton /> (Android) | Standalone wallet button or present(). |

Requirements

  • React Native >= 0.76, with the New Architecture or legacy architecture
  • iOS 15.0+
  • Android minSdk 23

Legacy-architecture support applies to React Native 0.76–0.81 and will be removed in a future major release. React Native 0.82 and newer always use the New Architecture.

Installation

Latest release: 1.1.0

npm install @xmoney/react-native

iOS — autolinking installs native dependencies. Then:

cd ios && pod install

Android — autolinking registers the native module. Rebuild the app after install.

Put only a publishable publicKey (pk_test_… / pk_live_…) in the app. Create orders on your server. Never ship a secret API key.

Expo

Add the config plugin in app.json:

{
  "plugins": [
    [
      "@xmoney/react-native",
      {
        "merchantIdentifier": "merchant.com.example",
        "enableGooglePay": true
      }
    ]
  ]
}

Checkout flow

  1. Your backend creates an order and returns payload + checksum.
  2. The app builds a PaymentIntent from those two values.
  3. You present Sheet, mount Element, or show a wallet button.
  4. The SDK fetches the session token, collects payment, and runs 3DS if needed.
  5. You handle PaymentResult. Session tokens are never passed by the merchant.
const intent = {
  orderPayload: payloadFromYourServer,
  orderChecksum: checksumFromYourServer,
};

Payment result

Every surface delivers the same shape:

type PaymentResult =
  | { status: 'complete'; transaction: Transaction }
  | { status: 'failed'; error: { code: string; message: string } }
  | { status: 'canceled' };

failed messages are SDK-authored. Do not display raw server bodies.

Interim events (ready, processing, updateFailed) are optional and do not replace the result callback. ready fires once per mounted surface, after the form has drawn or, for a wallet button, after the mount order is bound and the button has drawn. A failed first load does not emit ready — end merchant loading on a failed result as well. processing is an in-flight charge only. updateOrder does not emit ready or processing.

updateFailed means a later order bind failed after one was already bound. Pay stays locked on the previous order. Show the error and call updateOrder again. onResult is not called for that failure.

updateOrder during a charge throws PAYMENT_IN_PROGRESS (Payment in progress) when the intent differs from the one being charged. The same intent is a no-op. A newer updateOrder rejects the previous call with SUPERSEDED_UPDATE_ORDER.

Order lifecycle

Order checksums are one-shot. After a consumed result the bound order cannot be charged again.

| Outcome | Consumes order? | What you do | | ---------------------------------------------------- | --------------- | ----------------------------------------- | | complete | Yes | New PaymentIntent for another payment | | failed | Yes | New PaymentIntent | | canceled after pay / 3DS started | Yes | New PaymentIntent | | Sheet closed before pay (header, drag, scrim) | No | Present the same intent again | | Wallet dismissed before authorization | No | Present / tap again with the same intent |

Embedded and wallet buttons stay mounted after a consumed result; Pay / wallet disable (isOrderConsumed). Unmount them, or pass a new intent.

Payment Sheet dismisses on terminal results (including post-submit cancel). Present again with a new intent.

Use onEvent processing (or isOrderConsumed on Element / wallet buttons) to tell pre-pay cancel apart from post-submit cancel.

Payment Sheet

SDK owns the full checkout UI, including the Pay button (card.submitButton.visible is ignored).

Hook

import { usePaymentSheet } from '@xmoney/react-native';

function CheckoutButton() {
  const { init, present, loading } = usePaymentSheet();

  const onPay = async () => {
    await init({
      publicKey: 'pk_test_…',
      paymentMethods: { applePay: { enabled: true }, googlePay: { enabled: true } },
      card: { savedCards: { enabled: true } },
    });

    const result = await present(
      intent,
      (event) => {
        if (event.type === 'ready') { /* hide merchant loading */ }
      },
    );

    if (result.status === 'complete') {
      console.log(result.transaction);
    }
  };

  return <Button title="Pay" onPress={onPay} disabled={loading} />;
}

Imperative

import { PaymentSheet } from '@xmoney/react-native';

await PaymentSheet.init({ publicKey: 'pk_test_…' });
const result = await PaymentSheet.present(intent);
PaymentSheet.dismiss(); // optional; idle close still cancels

While idle, the sheet can be dragged closed. Drag, X, and scrim lock while a charge is in flight. A second present() while idle replaces the open sheet (the first promise resolves { status: 'canceled' }); while a charge is in flight the second present() returns { status: 'canceled' } without opening another sheet.

Copy-paste sample: PaymentSheetSample.tsx

Payment Element

Same form as the sheet, without the bottom-sheet chrome. Mount it in your layout. Embedded does not add outer content padding or a page fill — the host background shows through; supply your own page spacing. Keep merchant loading until onEvent ready.

import { PaymentElement } from '@xmoney/react-native';

<PaymentElement
  configuration={{
    publicKey: 'pk_test_…',
    paymentMethods: { applePay: { enabled: true }, googlePay: { enabled: true } },
  }}
  intent={intent}
  onEvent={(event) => {
    if (event.type === 'ready') { /* hide merchant loading */ }
    if (event.type === 'availability') {
      /* isOrderConsumed / isInteractionEnabled */
    }
  }}
  onResult={(result) => {
    if (result.status === 'complete') console.log(result.transaction);
  }}
/>

After complete / failed / post-submit canceled, hide the element (or pass a new intent). Pre-pay cancel does not consume — keep it mounted.

Update the order

ref.updateOrder (or changing the intent prop) rebinds a signed PaymentIntent you already have. Pay stays enabled while your server creates that order. Pay, confirm(), and wallet buttons are disabled once the bind starts (isInteractionEnabled is false) until it finishes. A newer updateOrder cancels the in-flight one. The Pay button keeps its current title; it does not show “Processing...”. A failed rebind emits onEvent { type: 'updateFailed', error } and leaves Pay locked until a later bind succeeds.

await elementRef.current?.updateOrder(nextIntent);

When the shopper changes the cart, pass a callback to updateOrder, the same overload as the native SDKs. Pay locks when the native view receives the call, about one frame after updateOrder, and stays locked until the order returned by the callback is bound, including during a debounce. A tap in that window can still charge the previous order. If the callback throws, the previous order stays payable.

const next = await elementRef.current?.updateOrder(async ({ isCurrent, signal }) => {
  await new Promise((resolve) => setTimeout(resolve, 300));
  if (!isCurrent()) {
    throw new Error('superseded');
  }
  return createOrderOnYourServer(cartTotal, { signal });
});
setIntent(next);

A newer call replaces an in-flight one and rejects with SUPERSEDED_UPDATE_ORDER. The callback receives isCurrent and an AbortSignal that aborts when a newer update starts. Check isCurrent() after waiting so the older callback does not call your server, and pass signal through to that request. Changing the intent prop to a different order supersedes this callback. After the call resolves, set the intent prop to the returned order so a later appearance or locale change does not rebind the previous one. On Android, a surface detached from the window rejects an in-flight updateOrder with SUPERSEDED_UPDATE_ORDER, for example an inactive react-native-screens screen. Call updateOrder again when the screen is shown.

ApplePay, GooglePay, ApplePayButton, and GooglePayButton have the same two updateOrder overloads. The wallet locks while your server creates the order, so a fast tap cannot charge the previous one.

Keep the surface mounted; do not unmount the form for a loader. Gate a merchant-owned Pay button with isInteractionEnabled.

Copy-paste sample: UpdateOrderSample.tsx

Live appearance

Call updateAppearance / updateLocale / updateStyle / updateWalletAppearance on the ref instead of recreating configuration for style-only changes. Changing publicKey, card, paymentMethods, or options.style remounts the native view.

Payment Sheet snapshots config at present() — pass appearance on PaymentConfig and present again to replace an idle sheet.

Merchant-owned Pay button

Embedded only. Hide the SDK button and call confirm() after ready:

import { PaymentElement, type PaymentElementRef } from '@xmoney/react-native';

const elementRef = useRef<PaymentElementRef>(null);

<PaymentElement
  ref={elementRef}
  configuration={{
    publicKey: 'pk_test_…',
    card: { submitButton: { visible: false } },
  }}
  intent={intent}
  onEvent={(event) => {
    if (event.type === 'ready') { /* enable your Pay button */ }
  }}
  onResult={(result) => { /* PaymentResult */ }}
/>

<Button
  title="Pay"
  onPress={() => elementRef.current?.confirm()}
/>

confirm() submits the currently selected method (new card or saved card). Wallet buttons still use the native wallet UI. isInteractionEnabled is false during updateOrder and while a charge is in flight.

Copy-paste sample: PaymentElementSample.tsx · Merchant CTA: MerchantPaySample.tsx

Apple Pay

iOS only. ApplePayButton renders nothing on Android.

import { ApplePay, ApplePayButton } from '@xmoney/react-native';

await ApplePay.init({ publicKey: 'pk_test_…' });

const state = await ApplePay.getState();
if (state.isAvailable && state.isReady) {
  const result = await ApplePay.present(intent);
}

ApplePay.dismiss(); // closes PassKit before authorize; no-op during token submit / 3DS

<ApplePayButton
  configuration={{ publicKey: 'pk_test_…' }}
  intent={intent}
  appearance={{ color: 'black', type: 'buy' }}
  onResult={(result) => { /* PaymentResult */ }}
/>

ApplePay.updateOrder may run before present to bind the next payable intent. Imperative Apple Pay does not emit updateFailed (native ApplePayEvent has no such case). A later bind failure rejects updateOrder. The Apple Pay button does emit updateFailed. After getState or wallet-button onEvent availability, gate your own chrome with isAvailable, isReady, isOrderConsumed, and isInteractionEnabled.

Pre-auth dismiss delivers { status: 'canceled' } and does not consume. Present or tap again with the same intent.

Setup

  1. Enable Apple Pay: paymentMethods.applePay.enabled: true.
  2. Create a Merchant ID in Apple Developer.
  3. In Xcode: app target → Signing & Capabilities → Apple Pay → add that Merchant ID (or use the Expo plugin above).
  4. The Merchant ID must match xMoney wallet params.
  5. Test on a physical device with a card in Wallet.

Copy-paste sample: WalletPaySample.tsx

Google Pay

Android only. GooglePayButton renders nothing on iOS.

import { GooglePay, GooglePayButton } from '@xmoney/react-native';

await GooglePay.init({ publicKey: 'pk_test_…' });

const state = await GooglePay.getState(intent);
if (state.isAvailable && state.isReady) {
  const present = GooglePay.present(intent);
  await GooglePay.updateOrder(nextIntent); // while overlay is open
  const result = await present;
}

<GooglePayButton
  configuration={{ publicKey: 'pk_test_…' }}
  intent={intent}
  appearance={{ color: 'black', type: 'buy' }}
  onResult={(result) => { /* PaymentResult */ }}
/>

GooglePay.updateOrder rebinds the open host — call after present, while the overlay is still open. updateOrder(createOrder) throws GOOGLE_PAY (Google Pay is not presented) before the callback runs when the overlay is closed. A later bind failure emits updateFailed on the present listener. A second present() while one is in flight returns { status: 'canceled' } — dismiss() then present() to replace.

Pre-auth dismiss delivers { status: 'canceled' } and does not consume. Present or tap again with the same intent.

Copy-paste sample: WalletPaySample.tsx

Configuration

{
  publicKey: 'pk_test_…',
  paymentMethods: {
    applePay: {
      enabled: true,
      appearance: { color: 'black', radius: 12, type: 'pay' }, // iOS: white-outline, topUp
    },
    googlePay: {
      enabled: true,
      appearance: { color: 'black', radius: 12, type: 'pay' },
    },
  },
  card: {
    savedCards: { enabled: true, optInVisible: true },
    validationMode: 'onTouched',
    inputs: { grouping: 'condensed' },
    submitButton: {
      visible: true, // Embedded only
      type: 'pay',   // book, buy, checkout, donate, …
    },
  },
  options: {
    locale: 'en-US', // UI language + pay-button amount punctuation
    style: 'automatic',
    appearance: {
      colors: { primary: '#7c4dff', background: '#ffffff' },
      colorsLight: { primaryText: '#16141a' },
      colorsDark: { primaryText: '#f7f6f9' },
      shapes: { borderRadius: 12, borderWidth: 1 },
      primaryButton: {
        colors: { background: '#7c4dff', text: '#ffffff' },
        shapes: { borderRadius: 12 },
      },
    },
  },
}

Card validation (card.validationMode, default onTouched):

| Mode | When errors show | | ----------- | -------------------------------------------------------------------- | | onTouched | None while first typing; on blur; then live. After Pay, always live. | | onChange | Live from the first keystroke | | onBlur | On blur; frozen until the next blur (live after Pay) | | onSubmit | On Pay (then live) |

Pay uses current field validity. Cardholder name is always collected.

Appearance — pass colorsLight / colorsDark so the form matches your chrome. Card fields use appearance.shapes.borderRadius (default 16) and colors.componentBorder. Pay button radius comes from appearance.primaryButton.shapes.borderRadius (default a pill, 9999). Pass 12 for a squircle. On a mounted Element, call ref updateAppearance / updateStyle / updateWalletAppearance. See exampleAppearance() for a copy-paste palette.

Locale — options.locale sets UI copy and pay-button amount punctuation. Supported languages: en, el, ro, bg, hu, pl. Region tags (en-US, pl-PL) work; unknown languages fall back to English.

Card holder verification

Optional pre-pay name check. Requires the site to have name-check validation enabled.

card: {
  cardHolderVerification: {
    name: { firstName: 'John', lastName: 'Doe' },
    onCardHolderVerification: (result) => result.status === 'Matched',
  },
},

Return true to continue pay, false to block. Sample: CardHolderVerificationSample.tsx

Public API

Use only these merchant-facing exports from @xmoney/react-native:

| Surface | Types | | --------------- | --------------------------------------------------------------------- | | Config / models | PaymentConfig and nested options, PaymentIntent, PaymentResult, PaymentError, Transaction, CardHolderVerificationResult, UpdateFailedEvent, UpdateOrderContext | | Payment Sheet | PaymentSheet, usePaymentSheet, PaymentSheetEvent | | Payment Element | PaymentElement, PaymentElementRef (confirm, updateOrder, updateAppearance, updateLocale, updateStyle, updateWalletAppearance), EmbeddedEvent | | Apple Pay | ApplePay (init, present, updateOrder, dismiss, getState), ApplePayButton, ApplePayButtonRef (updateOrder), WalletPayEvent | | Google Pay | GooglePay (init, present, updateOrder, dismiss, getState), GooglePayButton, GooglePayButtonRef (updateOrder), WalletPayEvent | | Errors | XMoneyPaymentError (NOT_LINKED when the native module is missing) |

PCI scope

Card fields live entirely inside the native SDKs. This wrapper never receives raw PAN / CVV. Merchants should follow xMoney integration guidance and the SAQ attestation provided with native SDK certification.

Example app

In-repo demo: copy-paste Integrations (Sheet / Element / Apple Pay or Google Pay), Lumen / Hearth / Pulse stores, merchant CTA / updateOrder / name-check, and an internal playground. See example/README.md for how to run it and consumption notes.

The example talks to a demo backend with API_KEY in the app. Do not ship that pattern. Production apps hold only publicKey; your server returns payload + checksum.

Support