@altev/pg-sdk
v0.1.0
Published
TypeScript SDK for the Altev Payment Gateway
Maintainers
Readme
@altev/pg-sdk
TypeScript SDK for the Altev Payment Gateway. Handles checkout payments, payment intents, refunds, webhook verification, and status checks.
Install & Init
npm install @altev/pg-sdkimport { PaymentGateway } from '@altev/pg-sdk'
const pg = new PaymentGateway('sk_test_xxx')
// or: new PaymentGateway('sk_live_xxx', { baseUrl, timeout, maxRetries })Checkout Payment
Creates a hosted checkout page. Customer gets redirected to PayMongo, then back to your success/cancel URL.
const payment = await pg.payments.create({
amount: 10000, // centavos (PHP 100.00)
currency: 'PHP',
successUrl: 'https://myapp.com/success',
cancelUrl: 'https://myapp.com/cancel',
description: 'Order #123',
customer: { email: '[email protected]', name: 'Juan' },
metadata: { orderId: '123' },
})
// Redirect user to:
payment.checkoutUrlPayment Intent
Use when you need a specific payment method (GCash, Maya, card, GrabPay, bank transfer). Returns method-specific details like redirect URLs or QR codes.
const intent = await pg.payments.createIntent({
amount: 10000,
currency: 'PHP',
paymentMethod: 'gcash', // 'gcash' | 'paymaya' | 'card' | 'grab_pay' | 'bank_transfer'
successUrl: 'https://myapp.com/success',
cancelUrl: 'https://myapp.com/cancel',
})
intent.paymentMethodDetails.redirectUrl // send user here
intent.clientKey // for client-side pollingStatus Check
Check current payment status by transaction ID.
const status = await pg.payments.get('transaction-uuid')
// status.status → 'AWAITING_PAYMENT_METHOD' | 'PENDING' | 'SUCCEEDED' | 'FAILED' | 'CANCELLED'Public status (no auth, for client-side):
const status = await pg.payments.getPublicStatus('payment-intent-id')Refund
Refund a payment fully or partially. Amount in centavos; omit for full refund.
const refund = await pg.refunds.create('transaction-uuid', {
reason: 'requested_by_customer', // 'duplicate' | 'fraudulent' | 'requested_by_customer' | 'others'
amount: 5000, // optional, partial refund in centavos
notes: 'Customer requested', // optional, max 500 chars
})
refund.totalRefunded // cumulative refunded amount
refund.remainingRefundable // what's leftIdempotency
Pass idempotency keys on create operations to prevent duplicate charges.
await pg.payments.create(params, { idempotencyKey: 'order-123-attempt-1' })
await pg.refunds.create(id, params, { idempotencyKey: 'refund-order-123' })Webhook Verification
Verifies HMAC-SHA256 signature from X-Webhook-Signature header. Throws WebhookSignatureError on failure.
import { PaymentGateway, WebhookSignatureError } from '@altev/pg-sdk'
const pg = new PaymentGateway('sk_test_xxx')
// Express example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
try {
const event = pg.webhooks.constructEvent(
req.body, // raw body (string | Buffer)
req.headers['x-webhook-signature'], // signature header
'whsec_your_webhook_secret', // webhook secret
)
switch (event.event) {
case 'payment.completed': /* ... */ break
case 'payment.failed': /* ... */ break
case 'payment.expired': /* ... */ break
case 'payment.refunded': /* ... */ break
}
res.sendStatus(200)
} catch (err) {
if (err instanceof WebhookSignatureError) {
return res.status(400).send('Invalid signature')
}
throw err
}
})Signature tolerance default: 300s (5 min). Override:
pg.webhooks.constructEvent(body, sig, secret, { tolerance: 600 })NestJS Integration
Drop-in NestJS module. Registers PaymentGateway as global injectable.
Module Setup
// app.module.ts
import { PaymentGatewayModule } from '@altev/pg-sdk/nestjs'
@Module({
imports: [
// Sync
PaymentGatewayModule.register({
secretKey: 'sk_test_xxx',
webhookSecret: 'whsec_xxx',
}),
// Async (from ConfigService)
PaymentGatewayModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
secretKey: config.get('PG_SECRET_KEY'),
webhookSecret: config.get('PG_WEBHOOK_SECRET'),
}),
}),
],
})
export class AppModule {}Important: Enable rawBody for webhook verification:
const app = await NestFactory.create(AppModule, { rawBody: true })Inject & Use
import { PaymentGateway } from '@altev/pg-sdk'
@Injectable()
export class PaymentService {
constructor(private readonly pg: PaymentGateway) {}
async charge(amount: number) {
return this.pg.payments.create({
amount,
currency: 'PHP',
successUrl: 'https://myapp.com/success',
cancelUrl: 'https://myapp.com/cancel',
})
}
}Webhook Controller
WebhookGuard verifies signature automatically. @WebhookEvent() extracts the parsed event.
import { Controller, Post, UseGuards } from '@nestjs/common'
import { WebhookGuard, WebhookEvent } from '@altev/pg-sdk/nestjs'
import type { WebhookEvent as WebhookEventType } from '@altev/pg-sdk'
@Controller('webhook')
export class WebhookController {
@Post()
@UseGuards(WebhookGuard)
handle(@WebhookEvent() event: WebhookEventType) {
switch (event.event) {
case 'payment.completed':
// event.data.transactionId, event.data.amount, etc.
break
case 'payment.failed':
// event.data.failureReason
break
}
return { received: true }
}
}Error Handling
All errors extend PaymentGatewayError. Catch specific types:
import {
ApiError,
AuthenticationError,
RateLimitError,
NetworkError,
ValidationError,
WebhookSignatureError,
} from '@altev/pg-sdk'
try {
await pg.payments.create(params)
} catch (err) {
if (err instanceof ValidationError) {
// Client-side validation failed before API call
console.log(err.field, err.message)
}
if (err instanceof AuthenticationError) {
// 401 — bad API key
}
if (err instanceof RateLimitError) {
// 429 — err.retryAfter (seconds), err.retryable = true
}
if (err instanceof ApiError) {
// Any API error — err.statusCode, err.code, err.correlationId, err.retryable
}
if (err instanceof NetworkError) {
// Fetch failed — err.retryable = true
}
}Testing Utils
Mock gateway and fixture factories for unit tests. No real API calls.
import {
MockPaymentGateway,
createPaymentFixture,
createRefundFixture,
createWebhookEventFixture,
generateTestSignature,
} from '@altev/pg-sdk/testing'Mock Gateway
Drop-in replacement. All methods return fixture defaults.
// NestJS test
const module = await Test.createTestingModule({
providers: [
PaymentService,
{ provide: PaymentGateway, useValue: new MockPaymentGateway() },
],
}).compile()Fixtures
Override any field:
const payment = createPaymentFixture({ status: 'SUCCEEDED', amount: 50000 })
const refund = createRefundFixture({ reason: 'duplicate' })
const event = createWebhookEventFixture({ event: 'payment.failed' })Available: createPaymentFixture, createPaymentIntentFixture, createPaymentStatusFixture, createRefundFixture, createWebhookEventFixture.
Test Signatures
Generate valid webhook signatures for testing your webhook handler:
const payload = JSON.stringify(createWebhookEventFixture())
const signature = generateTestSignature(payload, 'whsec_test_secret')
// Use in test requests as X-Webhook-Signature headerMCP Server
AI assistant integration via Model Context Protocol. Exposes payment tools + documentation as MCP resources.
Claude Desktop / Cursor
{
"mcpServers": {
"payment-gateway": {
"command": "npx",
"args": ["@altev/pg-sdk", "mcp"],
"env": {
"PG_SECRET_KEY": "sk_test_xxx"
}
}
}
}Without PG_SECRET_KEY, starts in docs-only mode (documentation resources available, payment tools disabled).
Types
All types exported from main entry:
import type {
Currency, // 'PHP' | 'USD'
PaymentMethod, // 'gcash' | 'paymaya' | 'card' | 'grab_pay' | 'bank_transfer'
TransactionStatus, // 'AWAITING_PAYMENT_METHOD' | 'PENDING' | 'SUCCEEDED' | 'FAILED' | 'CANCELLED'
Environment, // 'TEST' | 'LIVE'
RefundReason, // 'duplicate' | 'fraudulent' | 'requested_by_customer' | 'others'
WebhookEventType, // 'payment.completed' | 'payment.failed' | 'payment.expired' | 'payment.refunded'
CreatePaymentParams,
PaymentResponse,
CreatePaymentIntentParams,
PaymentIntentResponse,
PaymentStatus,
CreateRefundParams,
RefundResponse,
WebhookEvent,
CustomerInfo,
PaymentGatewayConfig,
} from '@altev/pg-sdk'