@cashiaapp/pay-with-cashia
v0.1.6
Published
A framework-agnostic Cashia payment button library that opens a payment modal.
Maintainers
Readme
Pay with Cashia
@cashiaapp/pay-with-cashia adds a Cashia payment button and hosted checkout modal to a website. It is framework-agnostic and can be used through its JavaScript API or as a Web Component.
How the integration works
- A customer selects Pay with Cashia on your checkout page.
- The library calls the
createSessionfunction supplied by your application. - Your frontend asks your backend to create a payment session.
- Your backend authenticates with Cashia and returns the hosted checkout URL.
- The library opens that URL in a modal iframe.
- Cashia closes the modal when the hosted payment flow finishes or is cancelled.
[!IMPORTANT] Create Cashia payment sessions on your backend. Never expose Cashia API keys, access tokens, or other credentials in browser code.
Prerequisites
Before integrating, obtain the following from Cashia:
- Access to the public
@cashiaapp/pay-with-cashianpm package - Your Cashia API credentials and payment-session API details
- The hosted checkout URL or environment details for sandbox and production
- A publicly accessible HTTPS URL for your partner logo
The library requires a browser DOM. Server-side rendered applications must initialize it on the client.
Installation
Install the package from the public npm registry with your package manager:
npm install @cashiaapp/pay-with-cashia
# or
pnpm add @cashiaapp/pay-with-cashia
# or
yarn add @cashiaapp/pay-with-cashiaQuick start
Add a container where the payment button should appear:
<div id="cashia-payment"></div>Create the button in browser code:
import { createPaymentButton } from "@cashiaapp/pay-with-cashia";
const paymentButton = createPaymentButton("#cashia-payment", {
partnerLogoUrl: "https://merchant.example/assets/logo.png",
createSession: async () => {
// Ask your backend to create the Cashia session so credentials stay server-side.
const response = await fetch("/api/cashia/payment-sessions", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
orderId: "order-123",
amount: 500,
currency: "KES",
phoneNumber: "0712345678",
}),
});
if (!response.ok) {
throw new Error("Unable to start the Cashia payment");
}
// Return the absolute hosted checkout URL for the library to open in its modal.
const session = (await response.json()) as { url: string };
return session.url;
},
});createSession may return a string or a Promise<string>. The string must be an absolute Cashia hosted checkout URL, including the session identifier supplied by Cashia. The library adds parentOrigin and partnerLogoUrl query parameters before opening the URL.
The request body above is illustrative. Build payment details from your trusted order data and follow the payment-session contract supplied during Cashia onboarding.
Backend responsibility
Your /api/cashia/payment-sessions endpoint should:
- Authenticate the customer and load the order from trusted server-side data.
- Validate the amount, currency, customer, and order status.
- Create a payment session with Cashia using server-side credentials.
- Return only the absolute hosted checkout URL needed by the library.
For example, the response consumed by createSession is:
{
"url": "https://cashia-checkout.example/pay?sessionId=SESSION_ID"
}The actual Cashia domain and response fields are environment-specific. Use the values provided during onboarding.
React and Next.js
Initialize the library in a client component and destroy the handle when the component unmounts. A dynamic import prevents the DOM-dependent library from being evaluated during server rendering.
"use client";
import { useEffect, useRef } from "react";
import type { PaymentButtonHandle } from "@cashiaapp/pay-with-cashia";
export function CashiaPaymentButton() {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
let handle: PaymentButtonHandle | undefined;
let disposed = false;
void import("@cashiaapp/pay-with-cashia").then(
({ createPaymentButton }) => {
if (disposed || !containerRef.current) return;
handle = createPaymentButton(containerRef.current, {
partnerLogoUrl: "https://merchant.example/assets/logo.png",
createSession: async () => {
const response = await fetch("/api/cashia/payment-sessions", {
method: "POST",
});
if (!response.ok) {
throw new Error("Unable to start the Cashia payment");
}
const session = (await response.json()) as { url: string };
return session.url;
},
});
},
);
return () => {
disposed = true;
handle?.destroy();
};
}, []);
return <div ref={containerRef} />;
}Web Component
Register the custom element once, assign its required createSession property, and then add it to the page.
<cashia-payment-button
id="cashia-payment"
partner-logo-url="https://merchant.example/assets/logo.png"
></cashia-payment-button>
<script type="module">
import { definePaymentButtonElement } from "@cashiaapp/pay-with-cashia";
definePaymentButtonElement();
const element = document.querySelector("#cashia-payment");
element.createSession = async () => {
const response = await fetch("/api/cashia/payment-sessions", {
method: "POST",
});
if (!response.ok) {
throw new Error("Unable to start the Cashia payment");
}
const session = await response.json();
return session.url;
};
</script>definePaymentButtonElement() is safe to call more than once. Pass a custom tag name if your application requires one:
definePaymentButtonElement("merchant-cashia-payment");Web Component attributes
| Attribute | Description |
| --- | --- |
| partner-logo-url | Required partner logo URL shown in the hosted checkout |
| button-text | Trigger label; child text is used when this attribute is omitted |
| button-class-name | Space-separated CSS classes added to the button |
| logo-url | Image URL or raw SVG used as the trigger icon |
| special-offer-text | Badge text displayed on the button |
| button-background-color | Trigger background color |
| button-text-color | Trigger text and icon color |
| special-offer-background-color | Badge background color |
| special-offer-text-color | Badge text color |
Configuration
PaymentButtonConfig
| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| createSession | () => string \| Promise<string> | Yes | - | Creates a session and returns its absolute hosted checkout URL |
| partnerLogoUrl | string | Yes | - | Public partner logo URL passed to the hosted checkout |
| buttonText | string | No | "Pay with Cashia" | Trigger button label |
| buttonClassName | string | No | - | Space-separated CSS classes added to the trigger button |
| buttonLogoUrl | string | No | Cashia logo | Image URL or raw <svg>...</svg> markup for the trigger icon |
| specialOfferText | string | No | "SPECIAL OFFER" | Badge text; use an empty string to hide it in the JavaScript API |
| buttonBackgroundColor | string | No | "#e01864" | Trigger background color |
| buttonTextColor | string | No | "#ffffff" | Trigger foreground color |
| specialOfferBackgroundColor | string | No | "#ffffff" | Badge background color |
| specialOfferTextColor | string | No | "#e01864" | Badge text color |
Any valid browser CSS color can be used for the color properties.
PaymentButtonHandle
createPaymentButton returns a handle for controlling the integration:
const handle = createPaymentButton(container, config);
await handle.open();
handle.close();
handle.update({ buttonText: "Complete payment" });
handle.destroy();| Member | Description |
| --- | --- |
| buttonElement | The generated HTMLButtonElement |
| open() | Creates a new session and opens the modal |
| close() | Closes the current modal |
| update(config) | Updates appearance or session configuration at runtime |
| destroy() | Closes the modal and removes the button and its listeners |
Calling open() while a session is being created or the modal is already open has no effect. The trigger button is disabled while createSession is pending.
Error handling
When a customer clicks the generated button and createSession fails, the button dispatches a payment-button-error custom event. Use it to display an error in your own interface or send the failure to monitoring.
paymentButton.buttonElement.addEventListener("payment-button-error", (event) => {
const error = (event as CustomEvent<unknown>).detail;
console.error("Cashia payment could not be opened", error);
});When you call handle.open() directly, handle the rejected promise with try/catch.
Security and deployment
- Keep Cashia credentials and authoritative payment data on your backend.
- Use HTTPS for the host page, API endpoint, logo assets, and hosted checkout URL in production.
- Allow the Cashia hosted checkout origin in your Content Security Policy
frame-srcdirective. - Allow required logo origins in
img-srcif your Content Security Policy restricts images. - Return a newly created or still-valid session URL for each payment attempt.
- Treat payment completion as authoritative only after server-side verification using the Cashia integration supplied during onboarding. Closing the modal is not proof of payment.
The library validates modal close messages against the hosted checkout URL's origin. The hosted checkout closes the modal with:
window.parent.postMessage(
{ type: "PAYMENT_BUTTON_CLOSE" },
new URLSearchParams(window.location.search).get("parentOrigin"),
);Troubleshooting
The package cannot be installed
Confirm that your package manager can access https://registry.npmjs.org. Remove any .npmrc rule that maps the @cashiaapp scope to a private registry.
The button does not appear
Ensure the container exists before calling createPaymentButton. In an SSR framework, initialize the library only in browser code.
The modal does not open
Listen for payment-button-error, check the session endpoint response, and confirm that createSession returns a valid absolute URL rather than a relative path.
The iframe is blocked
Check the browser console and your Content Security Policy. The Cashia checkout origin must be allowed by frame-src, and your server must not apply a conflicting iframe policy to the hosted checkout.
Package exports
import {
createPaymentButton,
definePaymentButtonElement,
PaymentButtonElement,
} from "@cashiaapp/pay-with-cashia";
import type {
CloseMessage,
CreatePaymentSession,
PaymentButtonAppearance,
PaymentButtonConfig,
PaymentButtonHandle,
} from "@cashiaapp/pay-with-cashia";Repository development
This repository is a pnpm monorepo:
| Path | Purpose |
| --- | --- |
| packages/pay-with-cashia | Published library |
| examples/host-app | Next.js integration example |
From the repository root:
pnpm install
pnpm build
pnpm dev:package
pnpm dev:hostSupport
Contact your Cashia integration representative for credentials, environment URLs, payment-session API details, and production onboarding support.
License
MIT
