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

@glomopay/react-native-sdk

v5.0.0

Published

The React Native SDK for GlomoPay

Readme

GlomoPay React Native SDK

npm version License

Official React Native SDK for integrating GlomoPay payment checkout flows into your mobile applications.

Prerequisites

  • API credentials (Public Key) from your GlomoPay dashboard
  • An order ID created via the GlomoPay API

System Requirements

| Requirement | Version | | ------------------------------------------ | --------- | | Node.js | >= 16.0.0 | | npm | >= 8.0.0 | | React | >= 17.0.0 | | React Native | >= 0.68.0 | | react-native-webview | ^13.0.0 | | jail-monkey | ^2.6.0 | | react-native-blob-util (optional) | ^0.25.0 | | @react-native-documents/picker (optional) | >= 10.1.3 |

Installation

React Native CLI

npm install @glomopay/react-native-sdk react-native-webview jail-monkey
cd ios && pod install

Expo

npx expo install @glomopay/react-native-sdk react-native-webview jail-monkey

Required: Device Security Compliance

jail-monkey is a required peer dependency as of v5. It was optional in v4.

npm install jail-monkey

Without it, live checkouts are blocked. The SDK cannot tell a clean device from a compromised one, and on a live_ key it refuses to start rather than assume the device is safe. start() returns false and onSdkError fires with type: "device_forbidden".

Test and mock keys are never blocked, so QA on a rooted handset or an emulator still works.

Optional: Document Downloads

Some checkout flows offer a document (an agreement PDF, for example) for the customer to save. A web page cannot write a file to the device from inside a WebView, so the SDK does it — but only if both of these are installed:

npm install react-native-blob-util @react-native-documents/picker
cd ios && pod install

Leaving them out is a supported choice: the SDK does not advertise the capability, the checkout page keeps its own download behaviour, and nothing breaks.

Five things worth knowing before you install them:

  1. Installing is not enabling. Both ship native code, so the app must be rebuilt. Until then the SDK treats them exactly as if they were absent.
  2. Reset the Metro cache after installing (npx react-native start --reset-cache). A stale bundle resolves the old module graph and the capability stays off.
  3. Expo needs a development build. Neither package works in Expo Go. react-native-blob-util ships a config plugin; @react-native-documents/picker does not, so add it to your config plugins list yourself if your setup needs one.
  4. @react-native-documents/picker 11 and above requires React Native >= 0.79. On older React Native, pin 10.1.x — the API this SDK uses is identical.
  5. Degradation is silent by design, which is why it is noisy in development: start() logs an ungated warning naming the missing package, and onSdkError fires once per mount with field: "file.save".

Quick Start

import React, { useRef } from "react";
import { View, Button } from "react-native";
import {
  GlomoCheckout,
  ASYNC_PAYMENT_EVENTS,
  type GlomoCheckoutRef,
  type GlomoCheckoutPayload,
  type SdkError,
} from "@glomopay/react-native-sdk";

export default function PaymentScreen() {
  const checkoutRef = useRef<GlomoCheckoutRef>(null);

  const handlePay = async () => {
    const started = await checkoutRef.current?.start();
    if (!started) {
      console.log("Checkout could not start - check onSdkError for details");
    }
  };

  return (
    <View style={{ flex: 1 }}>
      <Button title="Pay Now" onPress={handlePay} />

      <GlomoCheckout
        ref={checkoutRef}
        publicKey="live_pk_abc123"
        orderId="order_xyz789"
        onPaymentSuccess={(payload: GlomoCheckoutPayload) => {
          console.log("Payment success:", payload.paymentId);
        }}
        onPaymentFailure={(payload: GlomoCheckoutPayload) => {
          console.log("Payment failed:", payload.paymentId);
        }}
        onPaymentTerminate={() => {
          console.log("User dismissed checkout");
        }}
        onConnectionError={(error) => {
          console.log("Connection error:", error);
        }}
        onUserJourneyCompleted={(payload) => {
          switch (payload.journeyType) {
            case ASYNC_PAYMENT_EVENTS.BANK_TRANSFER_SUBMITTED:
              console.log("Bank transfer submitted:", payload.transactionReference);
              break;
            case ASYNC_PAYMENT_EVENTS.PAY_VIA_BANK_COMPLETED:
              console.log("Pay via bank completed:", payload.status);
              break;
          }
        }}
        onUserRefusedDevicePermissions={() => {
          console.log("User refused a device permission - cannot proceed with bank authentication");
        }}
        onSdkError={(errors: SdkError[]) => {
          errors.forEach((e) => console.error(e.type, e.message, e.field));
        }}
      />
    </View>
  );
}

Breaking Changes (v5)

  • jail-monkey is now required, and an unverifiable device blocks a live checkout. It was an optional peer dependency in v4 and warned when absent; it now fails closed. Mock and test keys are never blocked.
  • onUserRefusedCameraPermissions renamed to onUserRefusedDevicePermissions. Signature unchanged, so the compiler points at every call site.
  • paymentId and signature are optional on payment payloads. The page can report a terminal outcome before the backend has resolved them, and the SDK used to drop those outcomes entirely rather than report an incomplete one. With strict on, this surfaces as a compile error wherever you forward the value to something expecting a string — that narrowing check is the point.
  • onPaymentTerminate now receives a payload. GlomoTerminationPayload carries a source from the new TERMINATION_SOURCES enum. An existing () => void handler stays assignable, so this compiles unchanged.
  • A mock_ public key now runs in mock mode. It passed validation in v4 and then ran against live, which was a bug.
  • New exports: TERMINATION_SOURCES enum and GlomoTerminationPayload type.
  • New optional capability: document downloads, behind two optional peer dependencies.

For migration details, see MIGRATION.md included in this package.

Breaking Changes (v4)

  • GlomoLrsCheckout has been replaced by GlomoCheckout - the new component now works for all checkout orders (including LRS)
  • start() is now async - returns Promise<boolean> instead of boolean
  • All LRS-specific exports have been renamed (e.g. GlomoLrsCheckoutRef -> GlomoCheckoutRef)
  • onBankTransferSubmitted and onPayViaBankCompleted replaced by a single onUserJourneyCompleted callback
  • onPayViaBankBankConnectionSuccessful removed entirely
  • GlomoBankTransferPayload and GlomoPayViaBankConnectionPayload types removed
  • onSdkError is now required - previously optional, now must be provided to ensure validation and device compliance errors are always surfaced
  • New exports: ASYNC_PAYMENT_EVENTS enum and GlomoUserJourneyCompletedPayload type
  • CheckoutStatus values bank_transfer_submitted and pay_via_bank_completed are unchanged

For migration details, see MIGRATION.md included in this package.

Subscriptions Checkout

To process subscription payments, pass a subscriptionId instead of an orderId:

<GlomoCheckout
  ref={checkoutRef}
  publicKey="live_pk_abc123"
  subscriptionId="sub_xyz789"
  onPaymentSuccess={(payload) => {
    console.log("Subscription payment success:", payload.paymentId);
  }}
  onPaymentFailure={(payload) => {
    console.log("Subscription payment failed:", payload.paymentId);
  }}
  onSdkError={(errors) => {
    errors.forEach((e) => console.error(e.type, e.message));
  }}
/>

When subscriptionId is provided:

  • The SDK skips order type detection (no API call)
  • The subscriptionId must start with sub_
  • Do not pass both orderId and subscriptionId - the SDK will fire onSdkError
  • The detecting_order_type status is not emitted for subscription flows

Features

  • Cross-platform - works on both iOS and Android
  • TypeScript - full type definitions for all exports
  • Unified checkout - renders the correct checkout flow automatically based on your order
  • Asynchronous payment flows - single onUserJourneyCompleted callback for bank transfer and pay via bank events
  • Device permissions - built-in camera handling for bank authentication, on every checkout flow
  • Document downloads - saves a document the page offers to the customer's device, on every checkout flow, behind two optional peer dependencies
  • Device security - required jail-monkey integration for rooted/jailbroken device detection, failing closed on live keys
  • Input validation - publicKey and orderId format enforcement before checkout starts
  • Mock mode - test with test_ / mock_ prefixed keys without hitting production

useGlomoCheckout Hook (Deprecated)

Deprecated - Legacy hook, will be removed in a future major version.

useGlomoCheckout is a legacy placeholder from the v1.x hook-based API. In v1.x, the equivalent hook (useLrsCheckout) provided reactive WebView state and rendering primitives that merchants used to build custom checkout UIs. In v3, the inner checkout components are no longer exported, making this hook ineffective for custom rendering.

The hook's public return type (UseGlomoCheckoutReturn) exposes:

  • start() - identical to GlomoCheckoutRef.start(). Returns Promise<boolean>.
  • getStatus() - returns a point-in-time CheckoutStatus snapshot. Not reactive - does not trigger re-renders when status changes.

Use the GlomoCheckout component with a ref instead:

import React, { useRef } from "react";
import { GlomoCheckout, type GlomoCheckoutRef } from "@glomopay/react-native-sdk";

const checkoutRef = useRef<GlomoCheckoutRef>(null);

// Start checkout
const started = await checkoutRef.current?.start();

// Check status (point-in-time, same as hook)
const status = checkoutRef.current?.getStatus();

The component-based API provides the same functionality with proper lifecycle management.

API Reference

GlomoCheckout Component

The unified checkout component. Place it in your render tree and control it via a ref.

<GlomoCheckout ref={checkoutRef} {...props} />

Props (GlomoCheckoutProps)

| Prop | Type | Required | Description | | -------------------------------- | ----------------------------------------------- | -------- | ------------------------------------------------------------------- | | publicKey | string | Yes | Your GlomoPay public key (must start with live_, mock_, or test_) | | orderId | string | No* | Order ID from the GlomoPay API (must start with order_) | | subscriptionId | string | No* | Subscription ID (must start with sub_). Bypasses order type detection. | | onPaymentSuccess | (payload: GlomoCheckoutPayload) => void | Yes | Called when payment succeeds | | onPaymentFailure | (payload: GlomoCheckoutPayload) => void | Yes | Called when payment fails | | onConnectionError | (error: unknown) => void | No | Called on network/connection errors | | onPaymentTerminate | (payload: GlomoTerminationPayload) => void | No | Called when the checkout ends without a payment outcome. payload.source names how. An existing () => void handler stays assignable. | | onSdkError | (errors: SdkError[]) => void | Yes | Called on validation errors or device compliance failures | | onUserJourneyCompleted | (payload: GlomoUserJourneyCompletedPayload) => void | No | Called when an asynchronous payment flow completes (bank transfer submission, pay via bank completion) - not a confirmed payment. Check payload.journeyType to determine the flow. Accepted on GlomoCheckout but silently ignored on LRS orders - LRS never produces an asynchronous journey, so it never fires there. | | onUserRefusedDevicePermissions | () => void | No | Called when the user denies a device permission the bank's page asked for - camera today. Android only; see Device Permissions. |

*Exactly one of orderId or subscriptionId must be provided. If both or neither are set, onSdkError fires and start() returns false.

Ref Methods (GlomoCheckoutRef)

| Method | Signature | Description | | ------------ | ------------------------ | --------------------------------------------------------------------------------------------------------- | | start() | () => Promise<boolean> | Validates inputs, and opens the checkout modal. Resolves true if successful, false otherwise. | | getStatus()| () => CheckoutStatus | Returns the current checkout status. |

CheckoutStatus

| Status | Description | | -------------------------- | -------------------------------------------------------------- | | ready | Initial state - checkout can be started | | detecting_order_type | Order type detection in progress (after start(), before checkout opens) | | payment_in_progress | Checkout modal is open, user is interacting | | payment_successful | Payment completed successfully. Cannot restart with same order | | payment_failed | Payment failed. Can retry with same order | | payment_cancelled | User dismissed checkout mid-flow. Can retry | | bank_transfer_submitted | User submitted bank transfer details | | pay_via_bank_completed | Pay via bank flow completed |

Type Definitions

GlomoCheckoutPayload

interface GlomoCheckoutPayload {
  orderId: string;
  paymentId?: string;
  signature?: string;
}

paymentId and signature became optional in v5. The checkout page can report a terminal outcome before the backend has resolved them - open banking completes on the bank's side first - and the SDK previously dropped those outcomes rather than report an incomplete one. Narrow before forwarding either value.

ASYNC_PAYMENT_EVENTS

enum ASYNC_PAYMENT_EVENTS {
  BANK_TRANSFER_SUBMITTED = "bank_transfer_submitted",
  PAY_VIA_BANK_COMPLETED = "pay_via_bank_completed",
}

GlomoUserJourneyCompletedPayload

interface GlomoUserJourneyCompletedPayload {
  journeyType: ASYNC_PAYMENT_EVENTS;
  orderId?: string;
  status?: string;
  senderAccountNumber?: string;
  transactionReference?: string;
}

TERMINATION_SOURCES

enum TERMINATION_SOURCES {
  CHECKOUT_CLOSED = "checkout_closed",        // the page's own close control
  DEVICE_BACK_BUTTON = "device_back_button",  // Android hardware back or back gesture
  MODAL_DISMISS = "modal_dismiss",            // iOS swipe-down on the modal
}

The back chevron over a bank flow is deliberately not in this list. It returns the user to the checkout with the session intact and the ability to pay, so reporting it as a termination would be false - and for a merchant who cancels the order on terminate, it would cancel live orders every time a customer backed out of one bank to try another.

GlomoTerminationPayload

interface GlomoTerminationPayload {
  source: TERMINATION_SOURCES;
}

An object rather than a bare enum, so a later field can be added without changing the signature or redefining what an existing value means.

SdkError

interface SdkError {
  type: "validation_error" | "device_forbidden";
  message: string;
  field?:
    | "publicKey"
    | "orderId"
    | "subscriptionId"
    | "baseCheckoutUrl"
    | "generatedCheckoutUrl"
    | "file.save";
}

The first five name configuration you supplied. file.save is a bridge message type, and is here so a document that failed to save can be filtered apart from a bad public key on a callback that carries both — see Document downloads for when it fires.

Asynchronous Payment Flows

The SDK fires onUserJourneyCompleted when an asynchronous payment flow completes. Use payload.journeyType to determine which flow triggered the callback:

onUserJourneyCompleted={(payload) => {
  switch (payload.journeyType) {
    case ASYNC_PAYMENT_EVENTS.BANK_TRANSFER_SUBMITTED:
      /**
       * User submitted bank transfer details.
       * Relevant fields: orderId, senderAccountNumber, transactionReference
       * Note: fields may be absent if the checkout page omits them.
       */
      console.log("Transfer ref:", payload.transactionReference);
      break;
    case ASYNC_PAYMENT_EVENTS.PAY_VIA_BANK_COMPLETED:
      /**
       * Pay via bank journey completed.
       * Relevant fields: orderId, status
       * The SDK deduplicates this event - fires only once per session.
       */
      console.log("Pay via bank status:", payload.status);
      break;
  }
}}

The checkout status transitions to bank_transfer_submitted or pay_via_bank_completed respectively. Use getStatus() for granular programmatic checks.

Device Permissions

Some bank pages require camera access for authentication. The SDK handles permission prompts automatically on every checkout flow, but you must declare the permissions in your native project config.

File upload permissions are requested by react-native-webview itself and never reach the SDK, so they do not surface through onUserRefusedDevicePermissions.

Android

Add to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.CAMERA" />

iOS

Add to ios/<YourApp>/Info.plist:

<key>NSCameraUsageDescription</key>
<string>Camera access is required for identity verification during checkout</string>

Behavior

  • Android: SDK prompts the user via a native permission dialog. If denied, checkout closes and onUserRefusedDevicePermissions fires.
  • iOS: SDK grants the WebView permission request; iOS shows its own system prompt. If the user denies at the system level, the WebView handles the denial and never reports it back, so onUserRefusedDevicePermissions does not fire on iOS.

Platform Behavior

  • iOS: Checkout opens as a pageSheet modal (swipe-down to dismiss). Dismissing fires onPaymentTerminate.
  • Android: Checkout opens fullscreen. The hardware back button dismisses it and fires onPaymentTerminate.

Device Security Compliance

The SDK uses jail-monkey to detect rooted (Android) or jailbroken (iOS) devices. As of v5 it is a required peer dependency and the gate fails closed: a device the SDK cannot verify is refused on a live key rather than assumed clean.

| Device | test_ / mock_ key | live_ key | | ------------------------------------- | --------------------- | ----------- | | Clean | allowed | allowed | | Emulator / simulator | allowed | allowed | | Rooted / jailbroken | allowed | blocked | | Unverifiable (jail-monkey missing) | allowed | blocked |

When blocked, start() returns false and onSdkError fires with type: "device_forbidden". The message distinguishes a compromised device from an unverifiable one.

Emulators are allowed on live keys, deliberately. Android emulators ship AOSP test-keys and debuggable system properties, which trip two of the checks behind isJailBroken() — so the aggregate verdict reports every stock emulator as rooted. The SDK computes its own verdict from jail-monkey's per-check breakdown and ignores those two, because merchants integration-test with live keys on emulators and an emulator is not the threat this gate exists for. Every strong root indicator (su binaries, Magisk, root-manager apps, remounted system paths) is still enforced.

The cost is custom-ROM detection on physical devices, the weakest signal in the set. On iOS, and on any build that exposes no breakdown, the SDK falls back to isJailBroken() unchanged.

Mock Mode

The SDK infers mock mode from the publicKey prefix:

| Prefix | Mode | Environment | | -------- | ----- | -------------------- | | live_ | Live | Production | | test_ | Mock | Test/sandbox | | mock_ | Mock | Test/sandbox |

Mock mode keys route to the sandbox backend. No real transactions are created.

Connection Handling

The SDK detects connection errors from the WebView and fires onConnectionError:

  • iOS: NSURLErrorDomain codes (-1001 timeout, -1003 host not found, -1004 connection refused, -1009 offline)
  • Android: ERR_EMPTY_RESPONSE, ERR_CONNECTION_REFUSED, ERR_NAME_NOT_RESOLVED, ERR_INTERNET_DISCONNECTED, ERR_CONNECTION_TIMED_OUT, ERR_NETWORK_CHANGED

The checkout modal is automatically dismissed on connection errors.

Input Validation

The SDK validates inputs before starting the checkout:

| Field | Rule | | ---------------- | ----------------------------------------------- | | publicKey | Must start with live_, mock_, or test_. Min 6 characters. | | orderId | Must start with order_. Min 7 characters. Required when subscriptionId is absent. | | subscriptionId | Must start with sub_. Trimmed and checked for emptiness. Required when orderId is absent. |

Validation failures fire onSdkError with type: "validation_error" and the relevant field name. start() returns false.

Providing both orderId and subscriptionId (or neither) also fires onSdkError.

Migration

See MIGRATION.md included in this package for migration guides with before/after code examples, covering v1 through v5. If you are upgrading from v4, read the v5 section first - jail-monkey became a required dependency and can block live checkouts.

Troubleshooting

start() returns false

Check onSdkError for details. Common causes:

  • Invalid publicKey format (must start with live_, mock_, or test_)
  • Invalid orderId format (must start with order_)
  • Invalid subscriptionId format (must start with sub_, non-empty after trimming)
  • Both orderId and subscriptionId provided (or neither)
  • Device is rooted or jailbroken, on a live_ key
  • Device security could not be verified on a live_ key, because jail-monkey is missing or its native module is not linked. Install it and rebuild the app; the console warning names this case.

Checkout opens but shows a blank screen

  • Verify network connectivity
  • Check that the publicKey and orderId are valid

Document downloads do nothing

The capability is only advertised when both react-native-blob-util and @react-native-documents/picker load successfully, and the checkout page falls back to its own behaviour when it is absent. Check, in order:

  • Both packages installed, and the app rebuilt afterwards - installing alone is not enough
  • Metro cache reset (npx react-native start --reset-cache)
  • Not running in Expo Go, which cannot load either package
  • On React Native < 0.79, @react-native-documents/picker pinned to 10.1.x

start() logs an ungated warning naming the package it could not load, and onSdkError fires once per mount with field: "file.save".

Camera permission denied

  • Ensure AndroidManifest.xml and Info.plist include camera permission declarations
  • The onUserRefusedDevicePermissions callback will fire when the user denies camera permissions to your app, on Android only

Exports

// Components
export { GlomoCheckout } from "@glomopay/react-native-sdk";

// Hooks (deprecated - use GlomoCheckout component with ref instead)
export { useGlomoCheckout } from "@glomopay/react-native-sdk";

// Enums
export { ASYNC_PAYMENT_EVENTS, TERMINATION_SOURCES } from "@glomopay/react-native-sdk";

// Types
export type {
  GlomoCheckoutRef,
  GlomoCheckoutProps,
  GlomoCheckoutPayload,
  CheckoutStatus,
  GlomoUserJourneyCompletedPayload,
  GlomoTerminationPayload,
  UseGlomoCheckoutReturn,  // deprecated
  SdkError,
} from "@glomopay/react-native-sdk";

Security

Device integrity checking is available via optional jail-monkey integration (see Device Security Compliance).

To report a security vulnerability, email [email protected].

Support

License

Apache-2.0. See LICENSE for details.