@paygater/js
v0.1.3
Published
Paygate client-side JavaScript SDK for embedded payment forms
Readme
@paygater/js
Client-side JavaScript SDK for Paygate — embed a fully hosted payment form in any web app using just your public key.
Overview
@paygater/js gives merchants a drop-in payment form that handles Visa, Mastercard, Apple Pay, and Mada — all from a single mount() call.
How it works under the hood:
Browser Paygate API Stripe / Checkout.com
───────────────────────────────────────────────────────────────────────────
new Paygate('pk_test_…')
│
├─ GET /v1/public/config ──────────────────────→ { stripe_publishable_key }
│
├─ loads Stripe.js (from js.stripe.com)
│
├─ renders payment method tabs + Stripe iframe
│
├─ customer enters card (stays inside Stripe iframe — never touches your JS)
│
└─ onToken('tok_xxx', 'card') called
│
└─ your server: POST /v1/charges ────────────────────────────────→ capturedYour public key (pk_test_…) is the only credential that ever touches the browser. The secret key (sk_test_…) lives exclusively on your server.
Installation
npm install @paygater/jsCDN (no build step):
<script type="module">
import { Paygate } from 'https://cdn.jsdelivr.net/npm/@paygater/js/dist/index.mjs';
</script>Quick start
Vanilla JS / HTML
<div id="payment-form"></div>
<script type="module">
import { Paygate } from '@paygater/js';
const pg = new Paygate('pk_test_your_key_here');
await pg.mount('#payment-form', {
methods: ['card', 'apple_pay', 'mada'],
currency: 'SAR',
onToken: async (token, method) => {
// Send token to YOUR backend — never call Paygate directly from the browser
const res = await fetch('/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token, method, amount: 5000 }),
});
const { charge_id } = await res.json();
window.location.href = `/order/${charge_id}`;
},
onError: (err) => {
console.error(err.message);
},
});
</script>React / Next.js
'use client';
import { useEffect, useRef } from 'react';
import { Paygate } from '@paygater/js';
export function PaygateForm({ onSuccess }: { onSuccess: (chargeId: string) => void }) {
const containerRef = useRef<HTMLDivElement>(null);
const pgRef = useRef<Paygate | null>(null);
useEffect(() => {
if (!containerRef.current) return;
const pg = new Paygate(process.env.NEXT_PUBLIC_PAYGATE_PUBLIC_KEY!);
pgRef.current = pg;
pg.mount(containerRef.current, {
methods: ['card', 'apple_pay', 'mada'],
currency: 'SAR',
onToken: async (token, method) => {
const res = await fetch('/api/checkout', {
method: 'POST',
body: JSON.stringify({ token, method }),
});
const { charge_id } = await res.json();
onSuccess(charge_id);
},
onError: (err) => console.error(err.message),
});
return () => pg.unmount();
}, []);
return <div ref={containerRef} />;
}Server-side charge (after onToken)
The token from onToken is meaningless without a server-side charge call. Your backend receives the token and calls Paygate:
POST https://paygate-api.fly.dev/v1/charges
Authorization: Bearer sk_test_your_secret_key
Idempotency-Key: order-abc-123
Content-Type: application/json
{
"amount": 5000,
"currency": "SAR",
"payment_method": "card",
"token": "tok_1Nj4xQ2eZvKYlo2C..."
}Response:
{
"id": "a1b2c3d4-...",
"status": "captured",
"amount": 5000,
"currency": "SAR",
"payment_method": "card",
"provider": "stripe",
"environment": "sandbox"
}See the Paygate API docs for the full charge, refund, and webhook reference.
API reference
new Paygate(publicKey, options?)
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| publicKey | string | Yes | Your Paygate public key — pk_test_… for sandbox, pk_live_… for production |
| options.apiUrl | string | No | Paygate API base URL. Defaults to https://paygate-api.fly.dev |
Throws PaygateError immediately if the key format is invalid.
pg.mount(selector, options)
Fetches SDK config from Paygate, loads Stripe.js, and renders the payment form.
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| selector | string \| HTMLElement | Yes | CSS selector string or DOM element to render into |
| methods | PaymentMethod[] | No | Methods to show. Default: ['card', 'apple_pay', 'mada'] |
| currency | string | No | ISO 4217 currency code. Default: 'SAR' |
| onToken | (token: string, method: PaymentMethod) => void \| Promise<void> | Yes | Fires when the customer submits a valid payment |
| onError | (error: PaygateError) => void | Yes | Fires on tokenization or network errors |
Returns Promise<void>. Rejects with PaygateError if the target element isn't found or the config fetch fails.
pg.unmount()
Destroys the payment form and cleans up all DOM changes and event listeners. Safe to call multiple times. Always call this in component cleanup (e.g. React useEffect return).
Payment methods
| Method | Constant | Processor | Networks | Sandbox |
|--------|----------|-----------|----------|---------|
| Credit / Debit card | "card" | Stripe | Visa, Mastercard | Stripe test cards |
| Apple Pay | "apple_pay" | Stripe | Apple Pay | Safari + sandbox Apple ID |
| Mada | "mada" | Checkout.com | Saudi domestic debit | Fixed test token tok_mada |
Mada in sandbox: Checkout.com Frames requires domain registration and a Checkout.com public key. The sandbox SDK simulates Mada using a fixed accepted test token so you can test the full charge flow without that setup.
Security
Public key vs secret key
| Key | Prefix | Scope | Safe in browser? |
|-----|--------|-------|-----------------|
| Public key | pk_test_ / pk_live_ | Identifies your account. Cannot authorize charges. | ✅ Yes |
| Secret key | sk_test_ / sk_live_ | Authorizes all API operations. | ❌ Never |
The SDK only ever uses the public key — to call GET /v1/public/config and retrieve the Stripe publishable key on your behalf. No charge is created from the browser.
PCI DSS scope
Card fields (card, apple_pay) are rendered inside a Stripe-hosted <iframe> on the js.stripe.com domain. Due to browser same-origin policy:
- Your JavaScript cannot read what the customer types
- Raw card numbers never touch your page, your servers, or Paygate's servers
- Only Stripe receives card data — Stripe returns a one-time token
This means your integration is outside PCI DSS scope for cardholder data. Stripe is the PCI-compliant data processor.
HTTPS
The SDK emits a console.warn if mounted on a non-localhost HTTP origin. Stripe Elements and Apple Pay both require HTTPS in production.
Test cards
Use these in sandbox mode (pk_test_…). Any future expiry date and any 3-digit CVV work.
| Scenario | Card number | Brand | Result |
|----------|------------|-------|--------|
| Successful charge | 4242 4242 4242 4242 | Visa | captured |
| Successful charge | 5555 5555 5555 4444 | Mastercard | captured |
| Visa debit | 4000 0566 5566 5556 | Visa | captured |
| Card declined | 4000 0000 0000 0002 | Visa | failed |
| Insufficient funds | 4000 0000 0000 9995 | Visa | failed |
| 3D Secure required | 4000 0025 0000 3155 | Visa | authorized (pending 3DS) |
| Expired card | 4000 0000 0000 0069 | Visa | failed |
| Mada (sandbox) | any input | Mada | captured via tok_mada |
TypeScript
The package ships with full type declarations. Key exported types:
import type { PaymentMethod, MountOptions, PaygateConfig, PaygateError } from '@paygater/js';
type PaymentMethod = 'card' | 'apple_pay' | 'mada';
interface MountOptions {
methods?: PaymentMethod[];
currency?: string;
onToken: (token: string, method: PaymentMethod) => void | Promise<void>;
onError: (error: PaygateError) => void;
}Local development
git clone https://github.com/MahmoudBakr23/paygate-js.git
cd paygate-js
npm install
npm run build # compile once → dist/
npm run dev # watch mode
npm run typecheck # tsc --noEmit (no emit, type check only)Output in dist/:
| File | Format | Use |
|------|--------|-----|
| index.mjs | ESM | import in bundlers, CDN |
| index.js | CommonJS | require() in Node.js |
| index.d.ts | TypeScript | Type declarations |
Related
| Repo | Description | |------|-------------| | paygate-api | Rails 8 gateway API — charges, refunds, webhooks | | paygate-dashboard | Next.js 15 merchant dashboard | | paygate-docs | API documentation (Fumadocs) |
License
MIT
