SkillAgentSearch skills...

Payvault

Unified payment SDK for African markets. One API for Paystack + Flutterwave -- backend TypeScript SDK + embeddable checkout widget.

Install / Use

/learn @T9ner/Payvault
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<div align="center">

PayVault

One API. Every African payment provider.

Build License: MIT TypeScript Go PRs Welcome

Stop rewriting payment code when you switch providers. PayVault gives you a single, type-safe API that works with Paystack, Flutterwave, and any provider you add.

Quick StartArchitectureSDK UsageAPIDashboardContributing

</div>

Why PayVault?

| Problem | PayVault Solution | |---|---| | Every provider has a different API shape | One unified interface for all providers | | Switching providers means rewriting code | Change one line to switch providers | | 15+ transaction statuses across providers | 4 unified statuses: success, failed, pending, abandoned | | Different amount formats (kobo vs naira) | Always use major currency units (5000 = N5,000) | | No retry logic out of the box | Smart retry with exponential backoff + jitter | | Webhook formats differ per provider | Unified webhook events with signature verification |


Quick Start

Prerequisites

  • Node.js 18+ (for SDK and Dashboard)
  • Go 1.22+ (for API)
  • Docker & Docker Compose (for PostgreSQL and Redis)

1. Install the SDK

npm install payvault-sdk

2. Use PayVault in Your App

import { PayVault } from 'payvault-sdk';

// Create a PayVault instance for Paystack
const vault = PayVault.paystack('sk_test_xxxxx');

// Initialize a transaction — returns a checkout URL
const tx = await vault.initializeTransaction({
  amount: 5000,          // N5,000 (always in major units)
  email: 'customer@example.com',
  currency: 'NGN',
  metadata: { orderId: 'order_123' },
});

console.log(tx.authorizationUrl);
// => "https://checkout.paystack.com/abc123"

3. Start the API (Optional)

If you want to run the hosted backend:

# Start infrastructure
docker compose up -d postgres redis

# Run the API (default port :8080)
cd apps/api
cp env.example .env   # Edit with your credentials
go run ./cmd/api

# Run the API for dashboard development (Vite proxy expects :8081)
PORT=8081 go run ./cmd/api

The API runs on http://localhost:8080 by default. For dashboard development, the Vite proxy targets http://localhost:8081, so start the API with PORT=8081 go run ./cmd/api.

4. Run the Dashboard (Optional)

If you want the merchant dashboard UI:

cd apps/dashboard
npm install
npm run dev

The dashboard starts on http://localhost:3000. In development, Vite proxies /api requests to http://localhost:8081.


Architecture

PayVault is a complete payment platform with three components:

┌─────────────────────────────────────────────────────────────┐
│                     Your Application                        │
│                                                             │
│  import { PayVault } from 'payvault-sdk'                    │
│  const vault = PayVault.paystack('sk_test_xxxxx')          │
└─────────────────┬───────────────────────────────────────────┘
                  │
                  │ TypeScript SDK
                  │ (npm package)
                  ▼
┌─────────────────────────────────────────────────────────────┐
│                     Payment Providers                       │
│                                                             │
│    Paystack  │  Flutterwave  │  Your Custom Provider       │
└─────────────────────────────────────────────────────────────┘

            Optional Backend Components:

┌──────────────────────┐          ┌──────────────────────┐
│   API (Go/chi)       │          │  Dashboard (React)   │
│                      │          │                      │
│  - Unified REST API  │◄────────►│  - Merchant UI       │
│  - Multi-provider    │          │  - Analytics         │
│  - PostgreSQL        │          │  - Settings          │
│  - Redis queue       │          │  - Vite + Tailwind   │
└──────────────────────┘          └──────────────────────┘

Component Roles

| Component | Purpose | Used By | |-----------|---------|---------| | SDK (packages/sdk/) | TypeScript client for payment providers — install via npm | Developers integrating payments | | API (apps/api/) | Optional Go backend for hosted multi-tenant payment gateway | Companies running their own payment infrastructure | | Dashboard (apps/dashboard/) | Optional merchant UI for managing transactions, viewing analytics | Merchants using the hosted API |

Most developers only need the SDK. The API and Dashboard are for companies building a hosted payment gateway service.


Repository Structure

payvault/
├── packages/
│   └── sdk/                # TypeScript SDK — npm package `payvault-sdk`
│       ├── src/
│       │   ├── client.ts           # PayVault main client class
│       │   ├── types.ts            # All TypeScript interfaces
│       │   ├── errors.ts           # Structured error classes
│       │   ├── http.ts             # HTTP client with retry logic
│       │   ├── utils.ts            # Shared utilities
│       │   └── providers/
│       │       ├── paystack.ts     # Paystack implementation
│       │       └── flutterwave.ts  # Flutterwave implementation
│       ├── checkout/               # Embeddable checkout widget
│       ├── package.json
│       └── tsconfig.json
│
├── apps/
│   ├── api/                # Go backend API
│   │   ├── cmd/api/                # Main entry point
│   │   ├── internal/
│   │   │   ├── handlers/           # HTTP request handlers
│   │   │   ├── services/           # Business logic (Paystack, Flutterwave)
│   │   │   ├── models/             # Database models
│   │   │   ├── middleware/         # Auth, logging, CORS
│   │   │   └── queue/              # Redis job queue
│   │   ├── migrations/             # SQL schema migrations
│   │   ├── go.mod
│   │   ├── Dockerfile
│   │   └── env.example
│   │
│   └── dashboard/          # React merchant dashboard
│       ├── src/
│       │   ├── pages/              # Page components
│       │   ├── components/         # Reusable UI components
│       │   ├── services/           # API client
│       │   └── store/              # State management
│       ├── public/
│       ├── index.html
│       ├── vite.config.ts
│       └── package.json
│
├── docker-compose.yml      # PostgreSQL + Redis
├── LICENSE
└── README.md

SDK Usage

The SDK is the core of PayVault — a TypeScript client that abstracts payment provider differences.

Initialize a Transaction

import { PayVault } from 'payvault-sdk';

const vault = PayVault.paystack('sk_test_xxxxx');

const tx = await vault.initializeTransaction({
  amount: 5000,
  email: 'customer@example.com',
  currency: 'NGN',
  metadata: { orderId: 'order_123' },
});

// Redirect customer to tx.authorizationUrl

Verify a Transaction

const result = await vault.verifyTransaction('pvt_ps_abc123');

if (result.success) {
  console.log(`Paid ${result.amount} ${result.currency}`);
  console.log(`Channel: ${result.channel}`);        // 'card', 'bank_transfer', etc.
  console.log(`Customer: ${result.customer.email}`);

  // Save authorization code for recurring charges
  if (result.authorization?.reusable) {
    await saveAuthCode(result.customer.email, result.authorization.code);
  }
}

Switch to Flutterwave

The killer feature — change one line to switch providers:

- const vault = PayVault.paystack('sk_test_xxxxx');
+ const vault = PayVault.flutterwave('FLWSECK_TEST-xxxxx');

Everything else stays the same. Same method names, same response shapes, same types.

Handle Webhooks

import express from 'express';
import { PayVault } from 'payvault-sdk';

const app = express();
const vault = PayVault.paystack('sk_live_xxxxx', {
  webhookSecret: 'whsec_xxxxx',
});

// Register handlers
vault.on('charge.success', async (event) => {
  console.log(`Payment received: ${event.amount} ${event.currency}`);
  await fulfillOrder(event.reference);
});

vault.on('charge.failed', async (event) => {
  await notifyCustomer(event.customer.email);
});

// Webhook endpoint
app.post('/webhooks/payments', express.raw({ type: 'application/json' }), async (req, res) => {
  try {
    const signature =
      (req.headers['x-paystack-signature'] as string) ||
      (req.headers['verif-hash'] as string);

    const event = await vault.handleWebhook(req.body, signature);
    res.status(200).json({ received: true });
  } catch (err) {
    res.status(401).json({ error: 'Invalid signature' });
  }
});

Payment Links

PayVault lets you create shareable payment pages without writing any frontend code. This is handled by the API, so no SDK is required.

Create a link

curl -X POST http://localhost:8081/api/v1/dashboard/links \
  -H "Authorization: Bearer YOUR_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Premium Plan",
    "description": "One-time upgrade to premium",
    "link_type": "fixed",
    "amount": 5000,
    "currency": "NGN"
  }'

Share the link

The response includes a slug. Your checkout URL is:

http://your-api-host/api/v1/checkout/{slug}

Opening this URL shows a branded dark-theme checkout page. The customer enters their email, clicks Pay, and gets redirected to Paystack's ho

View on GitHub
GitHub Stars6
CategoryDevelopment
Updated1d ago
Forks1

Languages

TypeScript

Security Score

85/100

Audited on Apr 4, 2026

No findings