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

@bloonio/lokotro-pay-js

v1.1.0

Published

Framework-agnostic JavaScript SDK for Lokotro Pay - drop-in payment checkout widget and headless payment engine for React, Next.js, Vue, Svelte or plain JS. Cards, mobile money, e-wallets, bank transfer.

Readme

@bloonio/lokotro-pay-js

Framework-agnostic JavaScript SDK for Lokotro Pay — a drop-in payment checkout widget plus a headless payment engine. Works in React / Next.js, Vue, Svelte, Angular, or plain <script> pages. Cards (Mastercard Hosted Session), mobile money, e-wallets, Flash, and bank transfer.

This package is the JS equivalent of the @bloonio/lokotro-pay Angular SDK: same gateway flow, same models, same theming — no framework required.

✨ Features

  • 🧩 Framework-agnostic — vanilla DOM widget + headless core, zero runtime dependencies
  • ⚛️ React/Next.js-ready — mount in a useEffect; SSR-safe imports; useSyncExternalStore-compatible state
  • 🎨 Clean white-surface UI — neutral payment look out of the box, fully themeable, styles scoped to the widget
  • 🌍 Multi-language — English, French, Chinese (中文)
  • 💳 Multiple payment methods — Card (Hosted Session), Mobile Money, E-Wallet, Flash, Bank Transfer
  • 🔒 Secure by default — fail-closed environment selection, redirect URL allow-listing, no sensitive logging
  • 🎯 Type safe — full TypeScript declarations, ESM + CJS builds

📦 Installation

npm install @bloonio/lokotro-pay-js

🚀 Quick Start (plain JS)

import { LokotroCheckout } from '@bloonio/lokotro-pay-js';

const checkout = new LokotroCheckout({
  configs: {
    token: 'your_session_token',   // minted by YOUR backend (see Authentication)
    isProduction: false,           // REQUIRED: true = production, false = sandbox
    acceptLanguage: 'en',
  },
  paymentBody: {
    paymentMethod: 'wallet',
    userInfo: 'full',
    paymentMethodInfo: 'none',
    firstName: 'John',
    lastName: 'Doe',
    phoneNumber: '2439978540000',
    email: '[email protected]',
  },
  language: 'en',
  onResponse: (response) => console.log('Payment successful!', response),
  onError: (error) => console.error('Payment failed:', error.message),
  onClosed: () => console.log('Widget closed'),
});

checkout.mount('#checkout');   // element or CSS selector
// later:
checkout.unmount();

The widget injects its own scoped stylesheet on first mount — no CSS import needed. All styles live under .lokotro-pay-root and cannot leak into your app.

⚛️ React / Next.js

The package never touches window or document at import time, so it is safe to import in Next.js (App Router or Pages Router). Mount in an effect:

'use client';

import { useEffect, useRef } from 'react';
import { LokotroCheckout, type LokotroPayOnResponse } from '@bloonio/lokotro-pay-js';

export function PayButton({ sessionToken }: { sessionToken: string }) {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!containerRef.current) return;

    const checkout = new LokotroCheckout({
      configs: { token: sessionToken, isProduction: false },
      paymentBody: { paymentMethod: 'mobile_money' },
      onResponse: (response: LokotroPayOnResponse) => {
        // navigate to your success page, update order state, etc.
      },
      onError: (error) => console.error(error.message),
    });
    checkout.mount(containerRef.current);

    return () => checkout.unmount();
  }, [sessionToken]);

  return <div ref={containerRef} />;
}

Headless usage (custom UI)

Skip the widget entirely and drive the payment state machine yourself. The (getState, subscribe) pair plugs straight into useSyncExternalStore:

import { useSyncExternalStore, useMemo } from 'react';
import {
  LokotroHttpClient,
  LokotroPaymentService,
  LokotroPayEnv,
} from '@bloonio/lokotro-pay-js';

function usePayment(token: string) {
  const service = useMemo(() => {
    LokotroPayEnv.initialize(false);          // sandbox
    const client = new LokotroHttpClient();
    const svc = new LokotroPaymentService(client);
    svc.setAppKey(token);
    return svc;
  }, [token]);

  const state = useSyncExternalStore(
    (cb) => service.subscribe(cb, { emitCurrent: false }),
    service.getState,
    service.getState,
  );

  return { service, state };
}

state.currentScreen tells you what to render (loadingScreen, paymentMethodSelectionScreen, ewalletOtpScreen, successScreen, …) and the service exposes the same operations the widget uses: createPayment, selectPaymentMethod, submitPaymentDetails, verifyOtp, resendOtp, fetchAvailableBanks, confirmBankTransferSwitch.

🔑 Authentication

configs.token is a session token minted server-to-server by your backend against the Lokotro Gateway's session endpoint. Never embed long-lived API keys in browser code — your backend creates a short-lived session bound to the amount/currency/reference, and hands the token to the page.

📖 Configuration

configs (LokotroPayConfig)

| Property | Type | Required | Default | Description | |----------|------|----------|---------|-------------| | token | string | Yes | – | Session token from your backend | | isProduction | boolean | Yes | – | true → production gateway (real money), false → sandbox (simulated). No default — omitting it throws. | | customApiUrl | string | No | – | Custom gateway URL (e.g. https://api.example.com) | | timeout | number | No | 30000 | Request timeout (ms) | | acceptLanguage | string | No | 'fr' | Language for API responses |

paymentBody (LokotroPaymentBody)

Same shape as the Angular SDK — payment method, user info mode, payment-method info mode, prefill fields per channel (wallet number/PIN, mobile-money phone, card holder info, flash number/PIN), redirect/notify URLs, merchant branding and metadata. All fields optional when your session token already binds amount/currency/reference.

| userInfo / paymentMethodInfo | Behavior | |----------------------------------|----------| | 'full' | Values are sent with the request; the corresponding form is skipped | | 'none' | The widget shows a form and collects the values from the customer |

Widget options (LokotroCheckoutOptions)

| Option | Type | Description | |--------|------|-------------| | title | string | Optional header title (header renders only when set) | | themeConfig | LokotroPayThemeConfig | Brand colors/typography — see Theming | | language | 'en' \| 'fr' \| 'zh' | UI language (defaults to browser language) | | darkTheme | boolean | Opt-in dark surface | | enableHapticFeedback | boolean | Vibrate on selection where supported (default true) | | onResponse | (r: LokotroPayOnResponse) => void | Success callback — fires when the payment reaches the success screen, and again on Done/auto-redirect | | onError | (e: LokotroPayOnError) => void | Failure callback | | onClosed | () => void | Widget dismissed (close, cancel, done, auto-redirect) |

🎨 Theming

Pass your colours once as themeConfig; every part of the widget reads them — buttons and gradients, inputs and focus rings, spinners and loaders, processing steps, selected states, the result screens and the payment-status widget. Only the keys you set are overridden, and everything is scoped to the widget root. The keys are the same as the Flutter SDK's LokotroPayThemeConfig and @bloonio/lokotro-pay (Angular), so one brand definition serves every platform.

Example — eBlood red:

import type { LokotroPayThemeConfig } from '@bloonio/lokotro-pay-js';

const ebloodTheme: LokotroPayThemeConfig = {
  primaryColor: '#FF0000',
  secondaryColor: '#D32F2F',
  tertiaryColor: '#E57373',
};

new LokotroCheckout({ configs, paymentBody, themeConfig: ebloodTheme }).mount('#checkout');
new LokotroPaymentStatusWidget({ statusConfig, themeConfig: ebloodTheme }).mount('#status');

| Key | CSS custom properties | Drives | Default | |-----|-----------------------|--------|---------| | primaryColor | --lokotro-primary | Primary buttons and their gradient start, focused input borders, spinner rings, button glow | #0F172A | | secondaryColor | --lokotro-secondary | Gradient end of primary buttons | #475569 — follows primaryColor when omitted | | tertiaryColor | --lokotro-tertiary, --lokotro-accent | Selected payment method, focus rings, loader pulse, phone-prefix chips, links | #2563EB — follows primaryColor when omitted | | onPrimaryColor | --lokotro-on-primary | Text and icons on brand fills | #FFFFFF | | backgroundColor | --lokotro-background | Widget background | #FFFFFF | | surfaceColor | --lokotro-surface, --lokotro-card | Cards, inputs, method tiles, summaries | #F7F8FA / #FFFFFF | | textColor | --lokotro-text-primary | Body text | #0F172A | | secondaryTextColor | --lokotro-text-secondary | Labels, hints | #475569 | | borderColor | --lokotro-border | Borders | #E2E8F0 | | errorColor | --lokotro-error | Error text, error result | #DC2626 | | successColor | --lokotro-success | Success result | #16A34A | | warningColor | --lokotro-warning | Pending status, warning result | #D97706 | | infoColor | --lokotro-info | Info result | #2563EB | | borderRadius | --lokotro-radius-sm / -md / -lg | Corners of buttons, inputs, cards. A number is px; small and large scale ×2/3 and ×4/3 | 12px (8 / 12 / 16) | | fontFamily | --lokotro-font-family | Typography | system font stack |

Tints — selected backgrounds, focus rings, status halos, button glow — are mixed from these colours at runtime with color-mix(), so they follow the brand too. Browsers without color-mix() (before Chrome 111 / Safari 16.2 / Firefox 113) drop the tint and keep the solid colours.

The same properties can be set from your own CSS on .lokotro-pay-root. Set that way there is no fallback chain: give --lokotro-secondary and --lokotro-tertiary explicitly, or they keep their defaults.

lokotroThemeToCssVariables(theme) returns the property map (handy for SSR or framework wrappers) and LOKOTRO_THEME_CSS_VARIABLES lists which properties each key sets. demo/index.html shows the eBlood theme next to the default.

💳 Payment Methods

| Method | paymentMethod value | Notes | |--------|----------------------|-------| | E-Wallet | wallet | Wallet number + PIN, OTP verification | | Card | card | Mastercard Hosted Session — card data never touches your page or this SDK | | Mobile Money | mobile_money | Push-to-phone with automatic status polling | | Flash | flash | Flash number + PIN | | Bank Transfer | bank_transfer | City → bank → account selection, proof upload by email link |

When the chosen method can't carry the amount, the gateway may offer a bank-transfer switch; the widget shows a confirm screen — nothing is switched silently.

📡 Payment Status Tracking

Track a payment by ID only (e.g. initiated from a mobile app or server):

import { LokotroPaymentStatusWidget } from '@bloonio/lokotro-pay-js';

const status = new LokotroPaymentStatusWidget({
  statusConfig: {
    paymentId: 'your-payment-id',
    config: { token: 'your_session_token', isProduction: false },
    pollingInterval: 5000,     // default 5s
    maxPollingAttempts: 60,    // default 60 (≈5 minutes)
  },
  themeConfig: ebloodTheme,   // optional, same keys as the checkout
  onStatusChange: (r) => console.log('status:', r.status),
  onPaymentComplete: (r) => console.log('completed!', r),
  onPaymentFailed: (r) => console.error('failed:', r.message),
  onClose: () => {},
});

status.mount('#status');

Headless equivalent: LokotroPaymentStatusTracker (same polling logic, no UI).

🌐 Languages

| Language | Code | |----------|------| | English | en | | French | fr | | Chinese (Simplified) | zh |

Unknown codes are ignored gracefully (the widget stays on its current language). Custom strings can be patched per language via LokotroLocalizationService.addTranslations.

🔒 Security Notes

  • Fail-closed environments: isProduction has no default. Omitting it is a TypeScript error and a runtime error before any network call.
  • Redirect allow-listing: server-supplied redirect/hosted-checkout URLs are validated (http/https only; https-only in production) before navigation.
  • No sensitive logging: the SDK never logs tokens, request bodies or response bodies.
  • Hosted card sessions: for card, the SDK mints a hosted-checkout session; PAN/CVV/expiry are entered on the card network's own page.

📋 Requirements

  • Browsers: modern evergreen (uses fetch, AbortController, CSS custom properties)
  • Node 18+ for SSR imports (widgets themselves require a browser to mount())

📄 License

MIT © Bloonio