Skip to main content

TypeScript SDK

1. Overview

The Gateway TypeScript SDK enables clients (web frontends, Node.js backends, e-commerce integrations) to communicate with the Peak Gateway API. Built as a zero-dependency ESM package, the SDK provides Promise-based async APIs that work in Node.js, Deno, Cloudflare Workers, and modern browsers.

The SDK exposes two client classes that target different Gateway services:

  • GatewayAdminClient — Full admin/portal operations (management API). Used by portal frontends and backend admin tools.
  • GatewayPayClient — Payment-facing operations (online transactions API). Used by checkout flows, e-commerce backends, and machine integrations.

Consumers: Portal frontends, merchant-dashboard, checkout flows, Node.js backends, e-commerce integrations

Organization / Location model

The TypeScript SDK exposes the organization/location model directly:

  • LocationsApi owns location CRUD and status operations.
  • OrganizationsApi owns organization-scoped resources and credential profiles.
  • New SDK-facing code should use organizationId and locationId wherever the model exposes those fields.

The access-model summary is in Organizations and Locations.

2. Module Architecture

sdks/typescript/gateway-sdk/
└── src/
├── index.ts # Barrel export (re-exports all public API)
├── gateway-admin-client.ts # Admin client for management-domain modules
├── gateway-pay-client.ts # Pay client for checkout/payment-domain modules
├── gateway-config.ts # Configuration types and resolver
├── gateway-error.ts # Error class hierarchy
├── api/ # Client module implementations
├── models/
│ └── index.ts # All DTOs, enums, interfaces
├── webhook/
│ ├── webhook-verifier.ts # HMAC-SHA256 signature verification
│ └── webhook-event.ts # Discriminated union for event types
└── internal/
├── http-client.ts # fetch wrapper with retry and error mapping
└── auth-manager.ts # OAuth token lifecycle and caching

Client Summary

ClientBase URLAPI ModulesUse Case
GatewayAdminClientapi.peakgateway.coManagement and portal modulesPortal admin, backend management
GatewayPayClientpay.peakgateway.coPay and checkout modulesCheckout flows, payment processing

Support docs do not duplicate the full TypeScript client module inventory. The authoritative client surface is the readonly module list in sdks/typescript/gateway-sdk/src/gateway-admin-client.ts and sdks/typescript/gateway-sdk/src/gateway-pay-client.ts. The package barrel in sdks/typescript/gateway-sdk/src/index.ts controls which module classes are importable directly; consumers usually access modules from a client instance.

Not Included

OmittedReasonWhere It Lives
React hooks / UI componentsKept SDK framework-agnostic; frontends wire their own statewebsites/admin-portal/, websites/checkout/
TransIT API clientInternal library for gateway microservices onlylibs/transit-client/
XTMS API clientInternal library for Merchant Onboarding Service onlylibs/xtms-client/

3. Distribution

PropertyValue
Package@gateway/sdk
Registrynpm
LanguageTypeScript 5.9+
ModuleESM ("type": "module")
DependenciesNone (zero runtime dependencies)
RuntimesNode.js 18+, Deno, Cloudflare Workers, modern browsers
Buildtsc (publishes to npm via GitHub Actions)

Dependency Setup

npm install @gateway/sdk
import { GatewayAdminClient, GatewayPayClient } from '@gateway/sdk'

4. Usage Examples

Elements SetupIntent Helper

The browser Elements helper for setup-intent confirmation is documented separately in Confirm Card Setup With Elements.

Initialization (Server-Side, OAuth2)

import { GatewayAdminClient } from '@gateway/sdk'

const client = new GatewayAdminClient({
clientId: 'client_your_app',
clientSecret: 'sk_live_xxxxxxxxxxxx',
environment: 'production',
readTimeoutMs: 30000,
})

Customer OAuth Clients (Frontend, Firebase Token)

Merchant portal frontends use customerOAuthClients to manage organization-bound machine clients without platform-admin OAuth client permissions:

const client = new GatewayAdminClient({
environment: 'production',
tokenProvider: async () => await getAuth().currentUser?.getIdToken() ?? null,
})

const created = await client.customerOAuthClients.register({
displayName: 'ERP sync',
scopes: ['txn:process', 'batch:manage'],
})

console.log(created.clientId)
console.log(created.clientSecret) // shown once

The platform-admin oauthClients API remains separate and is intended for admin registry management.

Initialization (Frontend, Firebase Token)

import { GatewayPayClient } from '@gateway/sdk'
import { getAuth } from 'firebase/auth'

const auth = getAuth()

const client = new GatewayPayClient({
environment: 'production',
tokenProvider: async () => {
const user = auth.currentUser
return user ? await user.getIdToken() : null
},
})

Process Sale (Server-Side, Card-Not-Present)

import { CardDataSource, IndustryType } from '@gateway/sdk'

const result = await client.transactions.sale({
amount: 2500,
currency: 'USD',
cardDataSource: CardDataSource.MANUAL,
industryType: IndustryType.ECOMMERCE,
metadata: { orderId: 'web-12345' },
})

switch (result.kind) {
case 'approved':
console.log(`Approved: ${result.approvalCode}`)
console.log(`Transaction ID: ${result.transactionId}`)
break
case 'declined':
console.log(`Declined: ${result.responseMessage}`)
break
case 'error':
console.log(`Error: ${result.message}`)
break
}

Checkout Session (Hosted Payments)

const session = await client.checkoutSessions.create({
amount: 5000,
currency: 'USD',
locationId: 'loc_abc123',
successUrl: 'https://merchant.com/success',
cancelUrl: 'https://merchant.com/cancel',
metadata: { orderId: 'web-67890' },
})

// Redirect customer to session.checkoutUrl

Void / Refund

// Void
const voidResult = await client.transactions.void('txn_abc123')

// Refund (partial)
const refundResult = await client.transactions.refund('txn_abc123', 1500)

Query Transactions

import { TransactionStatus } from '@gateway/sdk'

const transactions = await client.transactions.list(
'loc_abc123', // location id
TransactionStatus.APPROVED, // status
'2026-03-01', // from
'2026-03-03', // to
50, // limit
)

for (const txn of transactions.items) {
console.log(`${txn.transactionId}: ${txn.amount} - ${txn.status}`)
}

Subscriptions

// Create
const subscription = await client.subscriptions.create({
locationId: 'loc_abc123',
customerId: 'cust_xyz',
tokenId: 'tok_abc123',
planName: 'pro',
amount: 1999,
currency: 'USD',
interval: 'monthly',
startDate: '2026-03-01',
metadata: { plan: 'pro' },
})

// Pause / Resume / Cancel
await client.subscriptions.pause(subscription.subscriptionId)
await client.subscriptions.resume(subscription.subscriptionId)
await client.subscriptions.cancel(subscription.subscriptionId)

// Billing history
const history = await client.subscriptions.billingHistory(subscription.subscriptionId)

Webhook Event Operations

const admin = new GatewayAdminClient({
environment: 'production',
tokenProvider: async () => await firebaseUser.getIdToken(),
})

const details = await admin.webhookEvents.getDetails('evt_123')

// Replay only failed / DLQ deliveries for the event.
await admin.webhookEvents.replayFailedDeliveries(details.event.id)

// Or retry one specific DLQ delivery row.
await admin.webhookEvents.retryDeliveryFromDlq('del_456')

const retention = await admin.webhookEvents.findRetentionConfigForOrganization('org_123')
console.log(retention?.deliveryDays)

Webhook Verification

import express from 'express'
import { WebhookVerifier } from '@gateway/sdk'

const app = express()
const verifier = new WebhookVerifier('whsec_xxxxxxxxxxxx')

// Use express.text() so req.body is the raw string (not parsed JSON)
app.post('/webhooks', express.text({ type: 'application/json' }), async (req, res) => {
const signature = req.headers['x-gateway-signature'] as string
const timestamp = req.headers['x-gateway-timestamp'] as string

const delivery = await verifier.verifyDelivery(req.body, signature, timestamp, req.headers)
const event = delivery.event

// Use correlationId when present to tie receiver logs back to gateway traces.
console.log('gatewayWebhookCorrelationId', delivery.correlationId)

switch (event.eventType) {
case 'transaction.completed':
handleCompletion(event.transaction)
break
case 'batch.settled':
handleSettlement(event.locationId, event.netAmount)
break
case 'payment.failed':
handleFailure(event.transaction)
break
}

res.sendStatus(200)
})

Express Backend Pattern

import express from 'express'
import { GatewayAdminClient, CardDataSource, IndustryType } from '@gateway/sdk'

const app = express()
const gateway = new GatewayAdminClient({
clientId: process.env.GATEWAY_CLIENT_ID,
clientSecret: process.env.GATEWAY_CLIENT_SECRET,
environment: 'production',
})

app.post('/api/charge', async (req, res) => {
const result = await gateway.transactions.sale({
amount: req.body.amount,
currency: 'USD',
cardDataSource: CardDataSource.INTERNET,
industryType: IndustryType.ECOMMERCE,
locationId: req.body.locationId,
metadata: { orderId: req.body.orderId },
})

if (result.kind === 'approved') {
res.json({ success: true, transactionId: result.transactionId })
} else {
res.status(400).json({ success: false, message: result.responseMessage })
}
})

// Graceful shutdown
process.on('SIGTERM', () => gateway.close())

5. Authentication

The SDK authenticates using OAuth2 client credentials issued by the Gateway Auth Service, or via a custom token provider (typically Firebase Auth).

Auth MethodUse CaseClient Config
Client credentials (clientId + clientSecret)Server-to-server (Node.js backends, cron jobs){ clientId, clientSecret }
Firebase token providerFrontend apps (admin-portal, checkout, merchant-dashboard){ tokenProvider: () => getIdToken() }
Custom token providerAny external auth system{ tokenProvider: () => fetchMyToken() }
App Check token (optional)Firebase App Check enforcement{ appCheckTokenProvider: () => getAppCheckToken() }

When using OAuth2 client credentials, access tokens are cached and automatically refreshed before expiry (with a 60-second buffer). When using a custom tokenProvider, the provider is called for each request (no SDK-side caching). In both modes, if a request receives a 401 response, the SDK transparently refreshes the token and retries the request once.

6. Error Handling

import {
AuthenticationError,
RateLimitError,
NetworkError,
ServerError,
ValidationError,
NotFoundError,
} from '@gateway/sdk'

try {
const result = await client.transactions.sale(request)
} catch (error) {
if (error instanceof AuthenticationError) {
// Invalid client credentials or expired token
} else if (error instanceof RateLimitError) {
// Retry after error.retryAfterSeconds
} else if (error instanceof NetworkError) {
// No connectivity — SDK automatically retries with exponential backoff
} else if (error instanceof ServerError) {
// 5xx from gateway — check error.statusCode
} else if (error instanceof ValidationError) {
// Invalid request parameters — check error.fieldErrors
} else if (error instanceof NotFoundError) {
// Resource not found (404)
}
}

Error Hierarchy

Error ClassTriggerKey Properties
GatewayErrorBase class for all SDK errorsmessage, cause
AuthenticationError401/403 responsesmessage, cause
RateLimitError429 responsesretryAfterSeconds
NetworkErrorConnection failures, timeoutsmessage, cause
ServerError5xx responsesstatusCode
ValidationError422 responsesfieldErrors: Record<string, string>
NotFoundError404 responsesmessage

7. Architecture Notes

Zero Dependencies

The SDK has no runtime dependencies. It uses:

  • fetch — Native HTTP client (available in Node.js 18+, Deno, browsers, Cloudflare Workers)
  • Web Crypto API — HMAC-SHA256 for webhook signature verification (available in all modern runtimes)
  • AbortSignal.timeout — Request timeouts (available in Node.js 18+)

Retry and Timeout Behavior

SettingDefaultDescription
readTimeoutMs30000Per-request timeout (via AbortSignal.timeout)
maxRetries3Max retry attempts for retryable failures
connectTimeoutMs5000Accepted in config for compatibility; the fetch-based HTTP layer currently enforces readTimeoutMs as the request timeout

Retries use exponential backoff (250ms base, doubling per attempt). Only 5xx server errors and network failures are retried. Auth errors (401/403), validation errors (422), rate limits (429), and not-found errors (404) are never retried.

Discriminated Unions

Where the Kotlin SDK uses sealed classes, the TypeScript SDK uses discriminated unions with a kind or eventType field:

// Transaction results — discriminated by 'kind'
type TransactionResult =
| TransactionResultApproved // kind: 'approved'
| TransactionResultDeclined // kind: 'declined'
| TransactionResultError // kind: 'error'

// Webhook events — discriminated by 'eventType'
type WebhookEvent =
| TransactionCompletedEvent // eventType: 'transaction.completed'
| TransactionFailedEvent // eventType: 'transaction.failed'
| BatchSettledEvent // eventType: 'batch.settled'
| PaymentFailedEvent // eventType: 'payment.failed'

What's Shared with the Kotlin SDK

  • All DTOs and request/response models (same JSON shape)
  • API endpoint paths and REST conventions
  • Error semantics (same HTTP status → error type mapping)
  • Webhook signature verification (same HMAC-SHA256 algorithm)
  • OAuth2 client credentials flow (same token endpoint)
  • Retry and timeout logic (same exponential backoff strategy)

TypeScript-Specific

  • Two client classes (GatewayAdminClient + GatewayPayClient) instead of one unified GatewayClient
  • Discriminated unions instead of sealed classes for result/event types
  • Promise<T> instead of Kotlin coroutines (suspend functions)
  • Native fetch instead of Ktor HTTP client
  • Web Crypto API instead of platform-specific HMAC implementations