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

cashup-pay-sdk

v1.0.0

Published

Drop-in "Pay with CashUp" button for any website. Let shoppers pay from their CashUp wallet at your checkout.

Readme

Pay with CashUp — JavaScript SDK

Let shoppers pay from their CashUp wallet at your online checkout. Add a "Pay with CashUp" button to any website in a few minutes — no dependencies, no build step.

<script src="https://app.cashup.io/sdk/v1/paywithcashup.js"></script>
<div data-cashup-button data-checkout-url="CHECKOUT_URL_FROM_YOUR_SERVER"></div>

Table of contents


How it works

"Pay with CashUp" is a wallet payment method, like STC Pay or Apple Pay. The shopper pays from money already in their CashUp wallet.

Shopper clicks "Pay with CashUp" on your site
   → your SERVER creates a checkout session (with your secret key)
   → the shopper is redirected to the CashUp hosted checkout
   → they sign in and confirm payment from their wallet
   → CashUp notifies your server via webhook + redirects the shopper back to you
   → CashUp settles the funds to your merchant account

A card never pays your store directly through CashUp — the shopper's wallet does. This keeps the integration simple and the money flow clean.


Quick start

1. Get your API keys

In the CashUp merchant dashboard go to Developers → API Keys and create a key. You get:

  • a publishable key pk_test_… / pk_live_… (safe for the browser)
  • a secret key sk_test_… / sk_live_… (server-only — never expose it)
  • a webhook signing secret whsec_…

Start with test keys — they run the full flow without moving real money.

2. Create a checkout session (server)

When the shopper checks out, call CashUp from your backend with your secret key:

curl -X POST https://api.cashup.io/api/v1/gateway/api/checkout-sessions \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 150.00,
    "currency": "SAR",
    "orderRef": "ORDER-1234",
    "successUrl": "https://your-store.com/success",
    "cancelUrl": "https://your-store.com/cart"
  }'

Response:

{
  "success": true,
  "data": {
    "id": "cks_…",
    "sessionToken": "gcs_…",
    "checkoutUrl": "https://app.cashup.io/checkout/gcs_…",
    "status": "CREATED",
    "mode": "TEST",
    "amount": 150,
    "currency": "SAR",
    "expiresAt": "2026-06-19T12:30:00.000Z"
  }
}

3. Send the shopper to the checkout (client)

Drop the SDK in and point a button at the checkoutUrl:

<script src="https://app.cashup.io/sdk/v1/paywithcashup.js"></script>
<div data-cashup-button data-checkout-url="https://app.cashup.io/checkout/gcs_…"></div>

That's it. The shopper pays, lands back on your successUrl, and your server receives a checkout.completed webhook.


Installation

CDN / hosted (recommended) — always the latest v1:

<script src="https://app.cashup.io/sdk/v1/paywithcashup.js"></script>

Self-host — copy paywithcashup.js into your assets and serve it yourself.

npm

npm install cashup-pay-sdk

The script attaches window.CashUpPay on import (it's a browser global, not an ES export):

import 'cashup-pay-sdk';
// window.CashUpPay is now available

Or reference the file directly: node_modules/cashup-pay-sdk/paywithcashup.js.

The script auto-initializes any [data-cashup-button] elements on the page (including ones added before DOMContentLoaded).


Client SDK

Data-attribute API

Add a data-cashup-button element; the SDK renders a branded button inside it.

| Attribute | Description | |-----------|-------------| | data-checkout-url | The checkoutUrl your server returned. Clicking redirects here. | | data-create-url | Alternative: an endpoint on your server the SDK POSTs to on click; it must return { checkoutUrl }. Use this to create the session lazily. | | data-key | Your publishable key pk_…. Optional — used to theme the button and respect your dashboard on/off switch. | | data-theme | light (default, solid blue) or dark (white, for dark pages). | | data-label | Override the button text. | | data-lang | en or ar. Defaults to the page <html lang>. |

<!-- create the session on click, from your own endpoint -->
<div data-cashup-button data-create-url="/api/cashup/create-session"></div>

Programmatic API

// Render a button into an element
CashUpPay.renderButton({
  selector: '#pay',            // or element: domNode
  checkoutUrl: 'https://app.cashup.io/checkout/gcs_…',
  // OR: createUrl: '/api/cashup/create-session', createBody: { cart: 42 },
  key: 'pk_test_…',            // optional
  theme: 'light',              // 'light' | 'dark'
  label: 'Pay with CashUp',    // optional
  lang: 'en',                  // 'en' | 'ar'
  onError: (err) => console.error(err),
});

// Just redirect (if you render your own button)
CashUpPay.redirect('https://app.cashup.io/checkout/gcs_…');

// Override the API base (e.g. staging)
CashUpPay.configure({ apiBase: 'https://staging-api.cashup.io' });

You can also set the API base on the script tag:

<script src=".../paywithcashup.js" data-api-base="https://staging-api.cashup.io"></script>

Server API

Base URL: https://api.cashup.io/api/v1

All server-to-server calls authenticate with your secret key in the Authorization: Bearer sk_… header. The key's mode (test/live) determines the session mode automatically.

Create a checkout session

POST /gateway/api/checkout-sessions

| Field | Type | Required | Notes | |-------|------|----------|-------| | amount | number | yes | In SAR, e.g. 150.00. | | currency | string | no | Only SAR is supported (default). | | orderRef | string | no | Your order id; echoed back and in webhooks. | | description | string | no | Shown on the checkout. | | successUrl | string (https) | yes | Where the shopper returns after paying. | | cancelUrl | string (https) | yes | Where the shopper returns if they cancel. | | webhookUrl | string (https) | no | Per-session override of your dashboard webhook URL. | | metadata | object | no | Free-form data stored with the session. |

Send an Idempotency-Key: <unique> header to make retries safe — the same key returns the same session instead of creating a duplicate.

On success the shopper is later redirected to: successUrl?status=paid&session=<sessionToken>&ref=<orderRef>&txn=<id> (cancelUrl?status=cancelled&… on cancel).

Retrieve a session

GET /gateway/api/checkout-sessions/:id — returns the session and its current status (CREATED, PROCESSING, COMPLETED, CANCELLED, EXPIRED, …). Use this to reconcile if you ever miss a webhook.


Webhooks

Configure a webhook URL in Developers → API Keys → (webhook), or pass webhookUrl when creating a session. CashUp POSTs JSON events to it.

Events

| Event | When | |-------|------| | checkout.completed | Payment succeeded — fulfill the order. | | checkout.cancelled | Shopper cancelled. | | checkout.expired | Session expired unpaid. | | checkout.failed | Payment failed. | | refund.completed / refund.failed | (When refunds are enabled.) |

Payload:

{
  "id": "evt_…",
  "type": "checkout.completed",
  "created": 1750000000,
  "data": {
    "sessionId": "cks_…",
    "sessionToken": "gcs_…",
    "status": "COMPLETED",
    "amount": 150,
    "currency": "SAR",
    "orderRef": "ORDER-1234",
    "transactionId": "…",
    "paidAt": "2026-06-19T12:31:00.000Z"
  }
}

Verify the signature

Every delivery includes X-CashUp-Signature: t=<unix>,v1=<hex>. Compute HMAC-SHA256(secret, "<t>.<rawBody>") with your whsec_… secret and compare in constant time. Reject if it doesn't match or t is too old.

const crypto = require('crypto');

function verifyCashUpSignature(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${parts.t}.${rawBody}`)
    .digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 || '');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Respond 2xx quickly. CashUp retries failed deliveries with exponential backoff and each event carries a unique id for idempotency.


Test mode

Use test keys (pk_test_… / sk_test_…). Test sessions complete the full flow — button → hosted checkout → redirect back → webhook — without moving any real money and never touch a real wallet. Switch to live keys when you're ready.


Merchant controls

Merchants control the button from Developers in the dashboard:

  • On/off switch for the payment method.
  • Button theme (light/dark) and optional label / display name.
  • Allowed website origins — restrict which domains may use your publishable key.

The SDK reads these (when you pass data-key) from the public config endpoint:

GET /gateway/config?key=pk_…{ valid, enabled, mode, merchant, button }


Security

  • Never put your secret key (sk_…) in client-side code. Only the publishable key (pk_…) is safe in the browser.
  • Always verify the webhook signature before acting on an event.
  • Treat the webhook as the source of truth for fulfillment — not the browser redirect (a shopper can navigate to the successUrl directly).
  • Restrict your publishable key with the allowed origins list.

FAQ

Do I need the SDK? No — it's a convenience. You can redirect to the checkoutUrl yourself. The SDK just renders a consistent, accessible button.

Does the shopper need a CashUp account? They sign in (or register) with their phone during checkout; the payment comes from their CashUp wallet.

What currencies? SAR.

Where are the funds settled? To your CashUp merchant account, then paid out to your registered IBAN.


Built by CashUp · Report an issue · MIT licensed