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

@xpayeg/sdk

v1.0.1

Published

XPay JavaScript SDK — loader and TypeScript types for embedding XPay payments

Downloads

345

Readme

@xpayeg/sdk

XPay JavaScript SDK -- loader and TypeScript types for embedding XPay payments on any website.

Documentation

Full guides, API reference, and live examples: https://docs.xpay.app

This README is a quick-start. The docs site is the authoritative reference.

Installation

Via npm (recommended for bundler projects):

npm install @xpayeg/sdk

Via CDN (for non-bundler setups):

<script src="https://checkout.xpay.app/v1/sdk.js"></script>

Step 1: Create a Checkout Session [Server-side]

On your server, create a Checkout Session and return the clientSecret to your frontend.

// Your server (Node.js example)
app.post('/api/create-checkout', async (req, res) => {
  const response = await fetch('https://api.xpay.app/checkout/sessions', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.XPAY_SECRET_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      uiMode: 'custom', // 'custom' for Elements SDK, 'embedded' for drop-in modal, 'hosted' for redirect
      lineItems: [
        {
          priceData: {
            unitAmount: 50000,  // 500.00 EGP in piasters
            currency: 'EGP',
            productData: { name: 'Premium Plan' },
          },
          quantity: 1,
        },
      ],
      afterCompletion: {
        type: 'redirect',
        redirect: {
          // {CHECKOUT_SESSION_ID} is automatically replaced with the actual session ID
          url: 'https://yoursite.com/success?session_id={CHECKOUT_SESSION_ID}',
        },
      },
    }),
  });

  const session = await response.json();
  res.json({ clientSecret: session.clientSecret });
});

Step 2: Initialize Checkout [Client-side]

initCheckout (Recommended)

Returns a single object with session fields and action methods merged together.

import { loadXPay } from '@xpayeg/sdk';

const xpay = await loadXPay('pk_test_xxx');

// Fetch client secret from your server
const { clientSecret } = await fetch('/api/create-checkout', { method: 'POST' }).then(r => r.json());

// Initialize checkout -- returns session data + action methods in one object
const checkout = await xpay.initCheckout({ clientSecret });

// Destructure session fields and actions together
const {
  status, currency, amountTotal, amountSubtotal, canConfirm, paymentMethods,
  confirm, applyPromotionCode, removePromotionCode, updateLineItemQuantity,
} = checkout;

// Display in your UI
document.getElementById('total').textContent =
  `${currency} ${(amountTotal / 100).toFixed(2)}`;
document.getElementById('methods').textContent =
  `Pay with: ${paymentMethods.map(pm => pm.displayName).join(', ')}`;

clientSecret accepts Promise<string> | string, so you can pass the fetch directly:

const checkout = await xpay.initCheckout({
  clientSecret: fetch('/api/create-checkout', { method: 'POST' })
    .then(r => r.json())
    .then(data => data.clientSecret),
});

Session fields on the checkout object:

| Field | Type | Example | Description | |-------|------|---------|-------------| | id | string | "cs_test_abc" | Session ID | | status | SessionStatus | { type: "open" } | Structured status (see below) | | canConfirm | boolean | true | Whether the session is ready for confirmation | | amountTotal | number | 52450 | Final total in smallest currency unit | | amountSubtotal | number | 50000 | Subtotal before fees/discounts | | currency | string | "EGP" | ISO 4217 currency code | | merchantName | string | "My Store" | Merchant display name | | livemode | boolean | false | Whether live or test mode | | paymentMethods | PaymentMethodInfo[] | [{ type, displayName, category }] | Available payment methods | | lineItems | LineItemDto[] | | Line items with prices and quantities | | totalDetails | TotalDetailsResponseDto | | Breakdown of fees, discounts, VAT | | discounts | DiscountResponseDto[] | | Applied discounts |

Session Status

status is a structured object, not a plain string:

// Check session status
if (checkout.status.type === 'open') {
  // Session is active, show payment form
}

if (checkout.status.type === 'expired') {
  // Session expired, show message or create a new one
  showMessage('This checkout session has expired.');
}

if (checkout.status.type === 'complete') {
  // Payment finished -- check paymentStatus for details
  console.log(checkout.status.paymentStatus); // "paid"
  showMessage('Payment successful!');
}

Alternative: elements

For event-driven initialization and lower-level control.

const xpay = await loadXPay('pk_test_xxx');
const elements = xpay.elements({ clientSecret });

elements.on('ready', ({ session }) => {
  console.log(session.status);         // { type: "open" }
  console.log(session.canConfirm);     // true
  console.log(session.amountTotal);    // 52450
  console.log(session.paymentMethods); // [{ type: 'card', ... }]
});

elements.on('loaderror', (event) => {
  // Only fires for actual failures (invalid secret, network error, etc.)
  // event: { type: "invalid_request_error" | "api_error" | "network_error", message, code?, param?, docUrl? }
  console.error('Failed to load:', event.message);
});

elements.on('error', (error) => {
  // Fires for unsolicited errors not triggered by a merchant action.
  // Examples: session expired during fee recalculation when switching payment methods, BIN detection failure.
  console.log(error.type);    // "invalid_request_error"
  console.log(error.code);    // "checkout_session_expired"
  console.log(error.message); // "This checkout session has expired"
  console.log(error.docUrl);  // "https://docs.xpay.app/api/errors#checkout_session_expired"
});

Step 3: Mount the Payment Form

Payment Element (handles method selection + card form)

const checkout = await xpay.initCheckout({ clientSecret });
const elements = checkout.getElements();

const paymentElement = elements.create('payment');
paymentElement.mount('#payment-element');

paymentElement.on('change', (event) => {
  submitButton.disabled = !event.complete;
  console.log(event.value.type);  // 'card', 'valu', etc.
});

Step 4: Confirm the Payment

confirm() returns a tagged union -- check type to determine the outcome.

By default, the result is returned to your code (redirect: "if_required"). Use redirect: "always" to redirect to the afterCompletion.redirect.url you set on the server.

// Default behavior: returns result to your code (no redirect)
const result = await checkout.confirm({
  customerDetails: {
    email: '[email protected]',
    name: 'Ahmed Hassan',
    phone: '+201234567890',
  },
});

if (result.type === 'error') {
  errorDiv.textContent = result.error.message;
} else {
  console.log(result.session.status); // { type: "complete", paymentStatus: "paid" }
  window.location.href = '/thank-you';
}

To redirect after success instead:

await checkout.confirm({
  customerDetails: {
    email: '[email protected]',
    name: 'Ahmed Hassan',
  },
  redirect: 'always', // Redirect to afterCompletion.redirect.url
});
// ^ If successful, the page navigates away. Code below only runs on error.

Redirect behavior:

| redirect | Behavior | |---|---| | Not set (default) | "if_required" — returns result to your code | | "always" | Redirects to returnUrl (client override) → server's afterCompletion.redirect.url | | "if_required" | Returns result to your code — no redirect |

You can override the server's redirect URL from the client:

await checkout.confirm({
  customerDetails: { email, name },
  redirect: 'always',
  returnUrl: 'https://mysite.com/custom-success', // Overrides server URL
});

Pre-validation with submit()

Call submit() before confirm() to validate all fields and get the selected payment method:

const { error, selectedPaymentMethod } = await elements.submit();
if (error) {
  errorDiv.textContent = error.message;
  return;
}

// Fields are valid, proceed to confirm
const result = await checkout.confirm({ paymentMethod: selectedPaymentMethod });

Step 4b: Update Session (Promo Codes, Quantities)

Action methods return Promise<{ type: "success", session } | { type: "error", error }>.

// Apply promotion code
const result = await checkout.applyPromotionCode('SAVE20');
if (result.type === 'error') {
  errorDiv.textContent = result.error.message; // e.g., "Invalid promotion code"
} else {
  console.log('New total:', result.session.amountTotal);
}

// Remove promotion code
await checkout.removePromotionCode();

// Update line item quantity (object param, not positional)
await checkout.updateLineItemQuantity({ lineItem: 'li_abc123', quantity: 3 });

Listening for Session Changes

Every update (promo codes, quantity changes, payment method selection, BIN detection) triggers a change event with the full updated CheckoutSession:

// initCheckout -- use on('change', ...)
checkout.on('change', (session) => {
  totalDiv.textContent = `Total: ${session.currency} ${(session.amountTotal / 100).toFixed(2)}`;

  // Amounts breakdown via totalDetails
  console.log(session.totalDetails?.amountPlatformFee);   // Processing fee
  console.log(session.totalDetails?.amountCollectedVat);   // VAT
  console.log(session.totalDetails?.amountDiscount);       // Discount amount
  console.log(session.totalDetails?.amountShipping);       // Shipping
  console.log(session.totalDetails?.amountTax);            // Tax

  console.log(session.discounts);   // Applied discounts
  console.log(session.lineItems);   // Updated line items
});

With the classic elements API:

elements.on('change', (session) => {
  console.log(session.amountTotal);
  console.log(session.totalDetails?.amountPlatformFee);
});

Re-sync from Server

Force a fresh session fetch if needed:

const result = await elements.fetchUpdates();
if (result.type === 'success') {
  console.log('Session refreshed:', result.session.amountTotal);
}

Step 5: Show a Success Page

Retrieve the session from your server (using your API key) to display order details.

Server endpoint:

// Your server -- retrieves session using your API key
app.get('/api/order-status', async (req, res) => {
  const response = await fetch(
    `https://api.xpay.app/checkout/sessions/${req.query.session_id}`,
    { headers: { 'Authorization': `Bearer ${process.env.XPAY_SECRET_KEY}` } },
  );
  const session = await response.json();
  res.json(session);
});

Client-side success page:

const sessionId = new URLSearchParams(window.location.search).get('session_id');
const session = await fetch(`/api/order-status?session_id=${sessionId}`).then(r => r.json());

document.getElementById('status').textContent =
  session.paymentStatus === 'paid' ? 'Payment Confirmed' : 'Processing...';
document.getElementById('total').textContent =
  `${session.currency} ${(session.amountTotal / 100).toFixed(2)}`;

// Amounts breakdown
// session.totalDetails.amountDiscount       -- 0
// session.totalDetails.amountPlatformFee    -- 1750 (Processing Fee, if fees pass-through)
// session.totalDetails.amountCollectedVat   -- 700  (VAT)
// session.totalDetails.amountShipping       -- 0
// session.totalDetails.amountTax            -- 0

// Line items
session.lineItems?.forEach(item => {
  console.log(item.price.product.name, 'x', item.quantity, '=', item.amountTotal);
});

Step 6: Handle Webhooks [Server-side]

Listen for webhook events on your server. This is the source of truth for order fulfillment.

app.post('/webhooks/xpay', (req, res) => {
  const event = req.body;

  if (event.type === 'checkout.session.completed') {
    fulfillOrder(event.data);  // Ship product, send email, update DB
  }

  res.json({ received: true });
});

Drop-in Checkout (Modal)

The simplest integration -- opens the full checkout in a modal overlay. No form needed.

<button id="checkout-button">Pay Now</button>

<script src="https://checkout.xpay.app/v1/sdk.js"></script>
<script>
  document.getElementById('checkout-button').addEventListener('click', async () => {
    const xpay = XPay('pk_test_xxx');

    const checkout = xpay.checkout({
      clientSecret: 'cs_test_abc_secret_xyz',
      mode: 'modal',
      onComplete: (result) => {
        window.location.href = '/success';
      },
      onClose: () => {
        console.log('Customer closed checkout');
      },
    });

    checkout.open();
  });
</script>

React Integration

The @xpayeg/react package provides a useCheckout() hook that wraps initCheckout with loading/error states:

import { useCheckout } from '@xpayeg/react';

function CheckoutPage() {
  const checkoutState = useCheckout();

  if (checkoutState.type === 'loading') {
    return <Spinner />;
  }

  if (checkoutState.type === 'error') {
    return <div>Error: {checkoutState.error.message}</div>;
  }

  const { currency, lineItems, amountTotal, confirm } = checkoutState.checkout;

  return (
    <div>
      <h2>Total: {currency} {(amountTotal / 100).toFixed(2)}</h2>
      {lineItems?.map(item => (
        <div key={item.id}>{item.price.product.name} x {item.quantity}</div>
      ))}
      <button onClick={() => confirm({ customerDetails: { email } })}>Pay</button>
    </div>
  );
}

Appearance

Override the session's brandingSettings at runtime:

const checkout = await xpay.initCheckout({
  clientSecret,
  appearance: {
    colorMode: 'dark',
    borderStyle: 'pill',
    inputStyle: 'filled',
    colors: { primary: '#FF6B35' },
  },
});

// Update later with changeAppearance()
checkout.changeAppearance({ colorMode: 'light' });

With the classic API:

const elements = xpay.elements({ clientSecret, appearance: { colorMode: 'dark' } });
elements.changeAppearance({ colorMode: 'light' });

API Reference

loadXPay(publishableKey?)

Loads the XPay SDK from CDN. Returns a Promise. Call at module level, not inside components.

import { loadXPay } from '@xpayeg/sdk';
const xpay = await loadXPay('pk_test_xxx');

xpay.initCheckout(options)

Initialize a checkout session. Resolves with a merged object containing session fields and action methods.

const checkout = await xpay.initCheckout({
  clientSecret: 'cs_test_abc_secret_xyz', // string | Promise<string>
  appearance: { colorMode: 'dark' },
  locale: 'ar',
});

// Session fields: status, canConfirm, amountTotal, currency, paymentMethods, lineItems, totalDetails, ...
// Action methods: confirm(), applyPromotionCode(), removePromotionCode(), updateLineItemQuantity(),
//                 submit(), fetchUpdates(), changeAppearance(), on(), getElements()

xpay.elements(options)

Create an Elements instance for mounting payment elements.

xpay.checkout(options)

Create a drop-in checkout instance (modal or inline).

xpay.confirmPayment(options)

Confirm and submit a payment (classic API).

TypeScript

Full TypeScript support with all types exported:

import type {
  XPayInstance, Elements, PaymentMethodInfo,
  CheckoutSession, SessionStatus, CustomerDetails, Appearance,
  InitCheckoutResult, CheckoutActions,
  ActionResult, XPayError,
  CheckoutLineItem, CheckoutTotalDetails, CheckoutFees, CheckoutDiscount,
} from '@xpayeg/sdk';