Skip to content

aashahin/payments-sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

9 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

@abshahin/payments-sdk

Unified, framework-agnostic payment SDK for Bun & TypeScript. Seamlessly integrate Moyasar, PayPal, Paymob, Stripe, Tabby, Tamara and other payment gateways with type-safe lifecycle hooks and normalized webhooks.

Features

  • πŸ”Œ Multi-Gateway Support: Moyasar, PayPal, Paymob, Stripe, Tabby, Tamara
  • πŸͺ Lifecycle Hooks: Before, after, and error hooks for all operations
  • πŸ”’ Type-Safe: Full TypeScript support with strict types
  • 🌐 Framework-Agnostic: Works with Elysia, Express, Hono, or vanilla

Documentation

Package Structure

packages/payments/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ index.ts           # Main exports
β”‚   β”œβ”€β”€ client.ts          # PaymentClient orchestrator
β”‚   β”œβ”€β”€ errors.ts          # Custom error classes
β”‚   β”œβ”€β”€ types/             # Type definitions
β”‚   β”œβ”€β”€ hooks/             # Lifecycle hooks
β”‚   └── gateways/          # Gateway implementations
β”œβ”€β”€ dist/                  # Built output
β”œβ”€β”€ docs/                  # Documentation
β”œβ”€β”€ resources/             # Resources
β”œβ”€β”€ package.json
β”œβ”€β”€ README.md
└── tsconfig.json

Installation

bun add @abshahin/payments-sdk

Quick Start

import { PaymentClient } from '@abshahin/payments-sdk';

const client = new PaymentClient({
  moyasar: {
    secretKey: process.env.MOYASAR_SECRET_KEY!,
    webhookSecret: process.env.MOYASAR_WEBHOOK_SECRET,
  },
  defaultGateway: 'moyasar',
});

// Create a payment
const result = await client.createPayment({
  amount: 100,
  currency: 'SAR',
  orderId: 'order_123',
  callbackUrl: 'https://example.com/callback',
  moyasarSource: {
      type: 'token',
      token: 'token_xxx'
  },
});

if (result.status === 'failed') {
  // Do not mark the order paid.
} else if (result.redirectUrl) {
  // Redirect customer for 3DS verification
}

Multi-Gateway Usage

const client = new PaymentClient({
  moyasar: { secretKey: '...' },
  paypal: { clientId: '...', clientSecret: '...' },
  paymob: {
    secretKey: '...',
    publicKey: '...',
    hmacSecret: '...',
    integrationId: 123456,
    authIntegrationId: 456789, // for capture: false auth/capture flows
    region: 'ksa',
    timeoutMs: 30000,
  },
  stripe: {
    secretKey: 'sk_...',
    publishableKey: 'pk_...',
    webhookSecret: 'whsec_...',
  },
  tabby: {
    secretKey: 'sk_...',
    merchantCode: 'your_merchant_code',
    sandbox: true,
  },
  tamara: {
    apiToken: 'your_api_token',
    notificationToken: 'your_notification_token',
    sandbox: true,
  },
  defaultGateway: 'moyasar',
});

// Use default gateway
await client.createPayment({ ... });

// Specify gateway explicitly
await client.createPayment({ ... }, 'paypal');

// Stripe Checkout Example
const stripe = client.gateway('stripe');
const session = await stripe.createCheckoutSession({
  successUrl: 'https://example.com/success',
  cancelUrl: 'https://example.com/cancel',
  mode: 'payment',
  metadata: { paymentId: 'order_123' },
  lineItems: [
    {
      priceData: {
        currency: 'USD',
        productData: {
          name: 'T-Shirt',
        },
        amount: 20,
      },
      quantity: 10,
    }
  ]
});

Stripe Webhook Note

For Stripe webhooks, you MUST pass the raw request body to verifyWebhook. If your framework parses JSON automatically, you need to access the raw body buffer or string before parsing; Buffer payloads are verified using their original bytes. Stripe webhook verification fails closed when webhookSecret is not configured. Stripe webhook parsing expects snapshot events with data.object; hydrate thin events before passing them to parseWebhookEvent. Checkout, invoice, and subscription webhooks normalize gatewayPaymentId to the related PaymentIntent, SetupIntent, or Subscription when Stripe includes one.

// Example using Elysia
app.post('/webhook/stripe', async ({ request }) => {
    const signature = request.headers.get('stripe-signature');
    const rawBody = await Bun.readableStreamToText(request.body); // Get raw body
    
    const isValid = client.gateway('stripe').verifyWebhook(
        rawBody, 
        signature
    );
});

Error Handling

import {
  PaymentError,
  PaymentAbortedError,
  GatewayNotConfiguredError,
  InvalidWebhookError,
  GatewayApiError,
  CardDeclinedError,
  InsufficientFundsError,
  RateLimitError,
} from '@abshahin/payments-sdk';

try {
  await client.createPayment({ ... });
} catch (error) {
  if (error instanceof PaymentAbortedError) {
    // Aborted by a hook
    console.log('Aborted:', error.message);
  } else if (error instanceof GatewayApiError) {
    // Gateway API returned an error
    console.log('Gateway error:', error.rawError);
  } else if (error instanceof PaymentError) {
    // Other payment error
    console.log('Error code:', error.code);
  }
}

License

MIT

About

Unified, framework-agnostic payment SDK for Bun & TypeScript. Seamlessly integrate Moyasar, PayPal, Paymob, Stripe and other payment gateways with type-safe lifecycle hooks and normalized webhooks.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Contributors