@selcom/payment-plugin-sdk
v1.2.0
Published
Server-side HTTP client for the Selcom Payment Plugin API. Create checkout sessions, look up payment intents, manage developer keys, mint anonymous demo sessions, and request live access from Node.js, Deno, or Bun.
Maintainers
Readme
@selcom/payment-plugin-sdk
Typed Axios-based client for the Payment Plugin embeddable checkout API.
Install
pnpm add @selcom/payment-plugin-sdkConfiguration
| target | baseUrl example | Paths |
|----------|-------------------|--------|
| gateway (default) | http://localhost:8000 | /api/plugin/*, /api/admin/plugin/*, /api/developer/*, /api/admin/developers/* |
| direct | http://localhost:9025 | /api/v1/plugin/*, /api/v1/plugin/admin/* |
Merchant checkout (publishable + secret keys)
import { PaymentPluginClient } from '@selcom/payment-plugin-sdk';
const client = new PaymentPluginClient({
baseUrl: process.env.SELCOM_GATEWAY_URL ?? 'http://localhost:8000',
target: 'gateway',
publicKey: process.env.SELCOM_PLUGIN_PUBLIC_KEY!,
secretKey: process.env.SELCOM_PLUGIN_SECRET_KEY!,
});
// Server-side: create session
const session = await client.createSession(
{ amount: 1050, currency: 'NAD', reference: 'order-42' },
{ idempotencyKey: '550e8400-e29b-41d4-a716-446655440000' },
);
const sessionToken = String(session.sessionToken);
// Browser-facing steps use the same client from your BFF, passing sessionToken:
await client.attachPaymentMethod(sessionToken, { type: 'card' });
await client.payWithCard(sessionToken, {
cardNumber: '4111111111111111',
expiryMonth: 12,
expiryYear: 2030,
cvv: '123',
cardholderName: 'Test User',
});
// Poll from merchant backend
const status = await client.getMerchantPayment(String(session.intentId));Wallet OTP
await client.requestWalletOtp(sessionToken, { phone: '+264811234567' });
await client.verifyWalletOtp(sessionToken, { otpReference: '...', otp: '123456' });Admin: issue keys
Through the gateway, configure PAYMENT_PLUGIN_ADMIN_API_KEY on the gateway (must match PLUGIN_ADMIN_API_KEY on payment-plugin-service). The client only sends the admin JWT:
const admin = new PaymentPluginClient({
baseUrl: 'http://localhost:8000',
target: 'gateway',
});
const keys = await admin.adminCreatePluginKeys(adminAccessToken, {
merchantId: 'merchant_1',
name: 'Storefront',
environment: 'test',
branding: { merchantName: 'Demo Shop' },
allowedOrigins: ['https://shop.example'],
});
// keys.publicKey, keys.secretKey — secret shown onceDirect to port 9025 (no gateway JWT), pass the admin header only:
await admin.adminCreatePluginKeys(undefined, payload, {
xPluginAdminKey: process.env.PLUGIN_ADMIN_API_KEY!,
});Developer self-serve
Available since 1.1.0. The same client speaks the developer-portal surface
under /api/developer/* (gateway) or /api/v1/developer/* (direct).
Anonymous demo sessions (no auth)
const anon = new PaymentPluginClient({ baseUrl: 'https://sandbox-api.selcom.com.na' });
const session = await anon.mintAnonymousDeveloperSession({
purpose: 'plugin-demo',
userAgent: 'demo-cli/1.0',
});
console.log(session.publicKey, session.expiresAt);
// Poll the delivered-event log (typically every ~2s).
const events = await anon.listAnonymousDeveloperSessionEvents(session.sessionId);
for (const ev of events) {
console.log(ev.type, ev.data);
}The mint endpoint is rate-limited to 10/min/IP, the events endpoint to 120/min/IP, and the issued public key only works against sandbox merchants. The session expires after 30 minutes.
Read settings + request live access (developer JWT)
Since 1.2.0, requestLiveAccess requires a full body (at minimum applicantType + legalName).
const dev = new PaymentPluginClient({ baseUrl: 'https://api.selcom.com.na' });
const settings = await dev.getDeveloperSettings(developerAccessToken);
console.log(settings.kycStatus); // 'sandbox_only' | 'pending' | 'approved' | 'rejected'
const updated = await dev.requestLiveAccess(developerAccessToken, {
applicantType: 'business',
legalName: 'Acme (Pty) Ltd',
country: 'NA',
businessRegistrationNumber: 'CC123',
integrationType: 'api',
applicantNotes: 'Launching checkout next month.',
});
console.log(updated.latestApplication?.trustScore);
const history = await dev.listOwnDeveloperApplications(developerAccessToken);
// Public page (no JWT) — token from approval email
const revealed = await dev.revealKeySecret('srv_…');
console.log(revealed.secretKey);Admin: developer onboarding (gateway JWT)
Since 1.2.0. Same admin machinery as plugin keys: gateway injects X-Plugin-Admin-Key; the SDK only sends the operator JWT.
const admin = new PaymentPluginClient({ baseUrl: 'http://localhost:8000', target: 'gateway' });
const page = await admin.adminListDeveloperApplications(adminJwt, { status: 'pending' });
const detail = await admin.adminGetDeveloperDetail(adminJwt, userId);
const approved = await admin.adminApproveDeveloper(adminJwt, userId, {
initialKey: { label: 'Live key', allowedOrigins: ['https://shop.example'] },
});
// approved.liveKey.secretKey, approved.reveal.token, approved.reveal.revealUrlErrors
Failed responses throw PaymentPluginRequestError with status, code, and message.
Spec
See docs/selcom-payments-gateway.openapi.yaml (tags Payment Plugin,
Developer Self-Serve, Developer Admin, and Developer Anonymous Demo) and
docs/channels/payment-plugin.md.
