@zed-io/wam-payment-sdk
v1.4.0
Published
Official WAM Payment SDK for creating payment intents and hosted checkout links
Readme
@zed-io/wam-payment-sdk
Server-side Node.js SDK for accepting payments through WAM. Create payment intents, redirect customers to hosted checkout, and verify webhook deliveries.
Requires Node.js 18+. Zero runtime dependencies.
Install
npm install @zed-io/wam-payment-sdkQuick start
import { WamPaymentSDK } from "@zed-io/wam-payment-sdk";
const wam = new WamPaymentSDK({
businessId: process.env.WAM_BUSINESS_ID!,
apiKey: process.env.WAM_API_KEY!,
environment: "staging",
});
// 1. Create a payment intent
const intent = await wam.createPaymentIntent({
amountCents: 5000,
currency: "TTD",
orderReference: "order-abc-123",
description: "2x Widget",
returnUrl: "https://yoursite.com/order/complete",
});
// 2. Redirect customer to hosted checkout
// intent.checkoutUrl → https://billing.wam.money/pay/invoice/...
redirect(intent.checkoutUrl);
// 3. Poll status (or use webhooks)
const status = await wam.getPaymentIntentStatus(intent.paymentId);
if (status.isTerminal) {
console.log(status.status); // "succeeded" | "failed" | "canceled" | "expired"
}Configuration
new WamPaymentSDK({
businessId: string, // Required. Your WAM business UUID.
apiKey: string, // Required. API key (sk_live_... or sk_test_...).
environment: "production", // "production" | "staging" | "local"
webhookSecret: string, // For verifying webhook signatures.
timeoutMs: 20_000, // Request timeout (default: 20s).
maxRetries: 2, // Retries on 5xx/network errors (default: 2).
});Environment base URLs
| Environment | API Base URL |
| ------------ | ------------------------------------- |
| production | https://billing.wam.money |
| staging | https://staging.billing.wam.money |
| local | http://localhost:3000 |
Override with apiBaseUrl / hostedBaseUrl if needed.
API
createPaymentIntent(params)
Creates a payment intent and returns a hosted checkout URL.
const intent = await wam.createPaymentIntent({
amountCents: 12900, // Required. Positive integer, minor units.
currency: "TTD", // Required. ISO 4217 code.
orderReference: "ORD-123", // Required. Your unique order ID.
description: "Hoodie x1", // Shown on checkout page.
returnUrl: "https://...", // Redirect after payment.
metadata: { cartId: "..." }, // Arbitrary key-value pairs.
idempotencyKey: "...", // Auto-derived if omitted.
});Returns: { paymentId, invoiceId, checkoutUrl, status, amountCents, currency, expiresAt, createdAt, updatedAt }
Idempotent — calling with the same orderReference + amountCents + currency returns the existing intent.
getPaymentIntentStatus(paymentId)
Retrieves current status of a payment intent.
const status = await wam.getPaymentIntentStatus("uuid-here");Returns: Everything from createPaymentIntent plus isTerminal, provider, providerTransactionId, merchantReference, completedAt.
verifyWebhook(params)
Verifies and parses an incoming webhook delivery.
const event = wam.verifyWebhook({
payload: rawBody,
signature: req.headers["x-wam-signature"],
timestamp: req.headers["x-wam-timestamp"],
});
// event.type → "payment_intent.succeeded"
// event.data.paymentId → "uuid"Also available as a static method: WamPaymentSDK.verifyWebhookSignature({ ..., secret }).
Webhook events
| Event | Description |
| ---------------------------------------- | ----------------------- |
| payment_intent.created | Intent created |
| payment_intent.requires_payment_method | Awaiting customer input |
| payment_intent.processing | Payment in progress |
| payment_intent.succeeded | Payment successful |
| payment_intent.failed | Payment failed |
| payment_intent.canceled | Payment canceled |
| payment_intent.expired | Intent expired |
Error handling
All SDK errors are instances of WamPaymentError:
import { WamPaymentError } from "@zed-io/wam-payment-sdk";
try {
await wam.createPaymentIntent({ ... });
} catch (err) {
if (err instanceof WamPaymentError) {
console.error(err.code); // "AUTHENTICATION_ERROR"
console.error(err.message); // Human-readable message
console.error(err.statusCode); // HTTP status (if applicable)
}
}| Code | Description |
| ---------------------------- | ---------------------------------------- |
| VALIDATION_ERROR | Invalid parameters |
| AUTHENTICATION_ERROR | Bad or missing API key |
| API_REQUEST_ERROR | Server rejected the request |
| NETWORK_ERROR | Connection failed (after retries) |
| TIMEOUT | Request exceeded timeoutMs |
| INVALID_PAYMENT_ID | Malformed payment ID |
| WEBHOOK_VERIFICATION_ERROR | Invalid webhook signature or timestamp |
| INVALID_CONFIG | Bad SDK configuration |
Lifecycle hooks
new WamPaymentSDK({
...config,
onRequest: ({ method, path, attempt }) => { /* before each request */ },
onResponse: ({ method, path, statusCode, latencyMs }) => { /* on success */ },
onError: ({ method, path, error, willRetry }) => { /* on failure */ },
});Testing
import { MockWamPaymentSDK } from "@zed-io/wam-payment-sdk/testing";
const mock = new MockWamPaymentSDK();
mock.onCreatePaymentIntent(() => ({
paymentId: "test-123",
invoiceId: "inv-456",
status: "created",
checkoutUrl: "https://example.com/checkout",
amountCents: 5000,
currency: "TTD",
selectedPaymentMethod: null,
expiresAt: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
}));
// Use `mock` anywhere you'd use the real SDK
const intent = await mock.createPaymentIntent({ ... });
// Assert calls
expect(mock.calls.createPaymentIntent).toHaveLength(1);Build test webhook payloads:
const { body, signature, timestamp } = mock.buildWebhookPayload(
"whsec_your_secret",
{ type: "payment_intent.succeeded", data: { paymentId: "pi_123", status: "succeeded" } }
);License
MIT
