@cuongcds/paygate-node
v0.1.0
Published
Node.js SDK for the PayGate payment gateway API
Readme
PayGate Node.js SDK
Node.js/TypeScript client for PayGate, a multi-tenant payment gateway. See that repo for the full, language-agnostic API reference this SDK wraps.
Install
npm install @cuongcds/paygate-nodeUsage — HMAC (server-to-server)
Use this when your own backend calls PayGate — never embed apiSecret in a mobile app or browser bundle.
import { Client, PayGateError } from '@cuongcds/paygate-node';
const client = Client.withHmac(
'https://payments.example.com',
'pgk_your_api_key',
'your_api_secret'
);
try {
const result = await client.createCheckoutSession({
external_ref: 'user-42',
plan_ref: 'premium_1m',
amount: 199000,
currency: 'VND',
mode: 'subscription',
interval: 'month',
interval_count: 1,
success_url: 'https://yourapp.com/success',
cancel_url: 'https://yourapp.com/cancel',
});
res.redirect(result.checkout_url as string);
} catch (e) {
if (e instanceof PayGateError) {
// e.errorCode is one of the codes in
// https://github.com/cuongcds/paygate-docs/blob/main/documents/05-errors.md
console.log(`Could not start checkout: ${e.errorCode} — ${e.message}`);
}
}Usage — Firebase ID Token (client calls PayGate directly)
Use this when your client already authenticates end users with Firebase Auth. external_ref is derived from the token automatically — never pass it.
import { Client } from '@cuongcds/paygate-node';
const client = Client.withFirebaseIdToken(
'https://payments.example.com',
firebaseIdToken // from your client, e.g. a mobile app's Authorization header
);
const subscription = await client.getSubscription(currentUserUid);Checking subscription status
import { Client, PayGateError } from '@cuongcds/paygate-node';
try {
const subscription = await client.getSubscription('user-42');
// { plan_ref: 'premium_1m', status: 'active', current_period_end: '2026-04-15 00:00:00' }
} catch (e) {
if (e instanceof PayGateError && e.errorCode === 'not_found') {
// user has never checked out — not necessarily an error in your flow
}
}Verifying a checkout redirect
success_url/cancel_url come back with paygate_transaction_id/paygate_external_ref/paygate_status appended — but that redirect alone is never proof of payment (it's a client-side navigation, not a signed confirmation). Verify server-side before unlocking anything:
import { Client, PayGateError } from '@cuongcds/paygate-node';
// From your success_url handler: req.query.paygate_transaction_id, req.query.paygate_external_ref
try {
const transaction = await client.getTransaction(req.query.paygate_transaction_id as string);
if (transaction.external_ref !== req.query.paygate_external_ref) {
throw new Error('Transaction does not belong to the expected user.');
}
if (transaction.status !== 'completed') {
// 'pending'/'failed'/'canceled' — do not unlock anything yet
}
} catch (e) {
if (e instanceof PayGateError && e.errorCode === 'not_found') {
// id doesn't exist, or belongs to a different app; treat as unverified
}
}This also covers one-time payments (mode: 'payment'), which never create a subscription record — getSubscription() alone can't verify those.
Error handling
Every non-2xx PayGate response throws PayGateError:
import { PayGateError } from '@cuongcds/paygate-node';
try {
await client.createCheckoutSession({ ... });
} catch (e) {
if (e instanceof PayGateError) {
e.errorCode; // e.g. "invalid_card_details"
e.message; // human-readable message from PayGate
e.httpStatus; // e.g. 400
}
}A request that never reached PayGate at all (DNS, timeout, connection refused) throws TransportError instead — treat this as a network problem, not a PayGate-side rejection.
Testing your integration
Pass payment_method: 'test' with one of the documented test card codes — see Testing without a real Stripe account. No real Stripe account or network call needed; resolves synchronously.
await client.createCheckoutSession({
external_ref: 'user-42',
plan_ref: 'premium_1m',
amount: 100000,
currency: 'VND',
mode: 'payment',
success_url: 'https://yourapp.com/success',
cancel_url: 'https://yourapp.com/cancel',
payment_method: 'test',
test_card_code: '4242424242424242',
});Requirements
- Node.js >= 16
More examples
See examples/ for runnable scripts, including verify-transaction.ts for the redirect-verification flow above.
Development
npm install
npm test # runs the Vitest suite
npm run build