New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

@7haven/havenpay

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@7haven/havenpay

Official JavaScript SDK for HavenPay

latest
Source
npmnpm
Version
0.2.3
Version published
Maintainers
1
Created
Source

HavenPay JavaScript SDK

Official JavaScript/TypeScript SDK for HavenPay - Type-safe payment processing for African markets.

npm version License: MIT

Features

  • Type-safe - Full TypeScript support with detailed types
  • Secure - Built-in request signing, response verification, and webhook validation (HMAC-SHA256, constant-time comparison)
  • Reliable - Automatic retry logic with exponential backoff (1s, 2s, 4s)
  • Modern - Uses native fetch() and Node.js crypto (zero dependencies, ES2020+)
  • Flexible - Both class-based and functional APIs
  • Production-ready - Follows industry best practices (DRY, KISS, single source of truth)
  • Well-tested - Comprehensive test coverage

Installation

npm install @7haven/havenpay
# or
yarn add @7haven/havenpay
# or
pnpm add @7haven/havenpay
# or
bun add @7haven/havenpay

Quick Start

import { HavenPay } from '@7haven/havenpay'

const havenpay = new HavenPay({
  apiKey: process.env.HAVENPAY_API_KEY!,
  signingSecret: process.env.HAVENPAY_SIGNING_SECRET!,
})

// Start a payment
const payment = await havenpay.payments.start({
  amount: 1000,
  currency: 'USD',
  providerId: 'omari', // or 'innbuck'
  cartId: 'cart-123',
  msisdn: '+263771234567',
})

console.log('Payment reference:', payment.reference)
console.log('OTP reference:', payment.otpReference)

// Verify OTP
const result = await havenpay.payments.verifyOtp(payment.reference, '1234')

if (result.status === 'SUCCESS') {
  console.log('Payment successful!')
}

Functional API

import { configure, start, verifyOtp } from '@7haven/havenpay'

// Configure once at app initialization
configure({
  apiKey: process.env.HAVENPAY_API_KEY!,
  signingSecret: process.env.HAVENPAY_SIGNING_SECRET!,
})

// Use anywhere in your app
const payment = await start({
  amount: 1000,
  currency: 'USD',
  providerId: 'omari',
  cartId: 'cart-123',
  msisdn: '+263771234567',
})

const _result = await verifyOtp(payment.reference, '1234')

Configuration

const _havenpay = new HavenPay({
  apiKey: 'your-api-key', // Required
  signingSecret: 'your-signing-secret-min-32-chars', // Required (min 32 chars, generate: openssl rand -base64 24)
  baseUrl: 'https://pay.7haven.online/api', // Optional (default: production API)
  timeout: 30000, // Optional (default: 30000ms = 30s)
  maxRetries: 3, // Optional (default: 3, exponential backoff: 1s, 2s, 4s)
  verifyResponses: true, // Optional (default: true, strongly recommended for security)
})

Generating Secrets:

# Generate signingSecret (32 chars minimum, 64+ recommended)
openssl rand -base64 24  # 32 chars
openssl rand -base64 48  # 64 chars (recommended)

API Reference

Payments

start(params)

Start a payment and send OTP to customer.

const _payment = await havenpay.payments.start({
  amount: 1000, // Amount in minor units (e.g., cents)
  currency: 'USD', // or 'ZWG'
  providerId: 'omari', // or 'innbuck'
  cartId: 'cart-123', // Your cart/order ID
  msisdn: '+263771234567', // Customer phone number
  webhookUrl: 'https://example.com/webhooks/havenpay', // Optional
})

Response:

{
  "reference": "pay_abc123",
  "otpReference": "otp_xyz789",
  "status": "AUTH_SENT",
  "message": "OTP sent to customer"
}

verifyOtp(reference, otp)

Verify customer OTP and complete payment.

const _result = await havenpay.payments.verifyOtp('pay_abc123', '1234')

Response:

{
  "reference": "pay_abc123",
  "status": "SUCCESS",
  "providerPaymentRef": "provider_ref_123",
  "message": "Payment completed successfully"
}

getStatus(reference)

Get current payment status (includes provider verification).

const _status = await havenpay.payments.getStatus('pay_abc123')

Response:

{
  "reference": "pay_abc123",
  "status": "SUCCESS",
  "amount": 1000,
  "currency": "USD",
  "providerId": "omari",
  "providerPaymentRef": "provider_ref_123",
  "verified": true,
  "verifiedAt": "2025-01-15T10:30:00Z",
  "verificationMethod": "query",
  "createdAt": "2025-01-15T10:25:00Z",
  "updatedAt": "2025-01-15T10:30:00Z"
}

getByCart(cartId)

Get latest payment for a cart (for recovery after page reload).

const _payment = await havenpay.payments.getByCart('cart-123')

if (_payment && _payment.status === 'AUTH_SENT') {
  // Show OTP input
}

cancel(reference)

Cancel/void a payment.

await havenpay.payments.cancel('pay_abc123')

resendOtp(reference)

Resend OTP for a payment.

await havenpay.payments.resendOtp('pay_abc123')

getProviders(currency?)

Get available payment providers.

const { providers } = await havenpay.payments.getProviders('USD')

providers.forEach((provider) => {
  console.log(`${provider.name} (${provider.id}) - ${provider.latencyMs}ms`)
})

Webhook Verification

📖 See PAYMENT_FLOW.md for complete webhook implementation guide

Verify webhooks sent from HavenPay to your backend.

import { HavenPay } from '@7haven/havenpay'
// or
import { verifyWebhook as _verifyWebhook } from 'havenpay'

app.post('/webhooks/havenpay', (req, res) => {
  const signature = req.headers['x-haven-signature'] as string
  const timestamp = req.headers['x-haven-timestamp'] as string
  const body = req.rawBody // Raw body string (not parsed JSON)

  // Verify signature
  const isValid = HavenPay.verifyWebhook(
    body,
    signature,
    timestamp,
    process.env.HAVENPAY_WEBHOOK_SECRET!
  )

  if (!isValid) {
    return res.status(401).send('Invalid signature')
  }

  // Parse and handle event
  const _event = JSON.parse(body)

  switch (_event.event) {
    case 'payment.success':
      // Handle successful payment
      break
    case 'payment.failed':
      // Handle failed payment
      break
    case 'payment.pending':
      // Handle pending payment
      break
  }

  res.status(200).send('OK')
})

Or use constructWebhookEvent for automatic parsing:

import { HavenPay } from '@7haven/havenpay'

app.post('/webhooks/havenpay', (req, res) => {
  try {
    const _event = HavenPay.constructWebhookEvent<WebhookPayload>(
      req.rawBody,
      req.headers['x-haven-signature'] as string,
      req.headers['x-haven-timestamp'] as string,
      process.env.HAVENPAY_WEBHOOK_SECRET!
    )

    // Event is verified and parsed
    console.log(_event.reference, _event.status)

    res.status(200).send('OK')
  }
  catch (_error) {
    console.error('Invalid webhook:', _error)
    res.status(401).send('Invalid signature')
  }
})

Error Handling

The SDK throws specific error types for different failure scenarios:

import {
  HavenPayConfigError as _HavenPayConfigError,
  HavenPaySignatureError as _HavenPaySignatureError,
  HavenPayAPIError,
  HavenPayNetworkError,
  HavenPayTimeoutError,
} from '@7haven/havenpay'

try {
  const _payment = await havenpay.payments.start({ })
} catch (error) {
  if (error instanceof HavenPayAPIError) {
    console.error('API Error:', error.message)
    console.error('Status:', error.statusCode)
    console.error('Code:', error.code)
    console.error('Details:', error.details)
  } else if (error instanceof HavenPayNetworkError) {
    console.error('Network Error:', error.message)
  } else if (error instanceof HavenPayTimeoutError) {
    console.error('Timeout:', error.message)
  }
}

TypeScript Support

The SDK is written in TypeScript and provides full type definitions:

import type {
  Currency as _Currency,
  PaymentStatus as _PaymentStatus,
  ProviderId as _ProviderId,
  StartPaymentParams as _StartPaymentParams,
  StartPaymentResponse as _StartPaymentResponse,
  // ... all other types
} from '@7haven/havenpay'

Security Best Practices

  • Never expose your API key or signing secret in client-side code - Server-side only
  • Always verify webhook signatures before processing events (HMAC-SHA256, constant-time comparison)
  • Use HTTPS for all API requests (enforced by default)
  • Store secrets in environment variables, not in code
  • Rotate your secrets regularly (industry best practice: every 90 days)
  • Enable response verification (enabled by default, protects against MITM attacks)
  • Use minimum 32-char secrets (64+ chars recommended for production)

Environment Variables

# Required
HAVENPAY_API_KEY=your-api-key
HAVENPAY_SIGNING_SECRET=<32-char-secret>        # Min 32 chars (openssl rand -base64 24)

# Optional
HAVENPAY_WEBHOOK_SECRET=<64-char-secret>        # Min 32, recommend 64 chars (openssl rand -base64 48)
HAVENPAY_BASE_URL=https://pay.7haven.online/api

Generate Secrets:

# Signing secret for API requests/responses (32 chars)
openssl rand -base64 24

# Webhook secret for receiving webhooks (64 chars recommended)
openssl rand -base64 48

Examples

Full Payment Flow

import { HavenPay } from '@7haven/havenpay'

const havenpay = new HavenPay({
  apiKey: process.env.HAVENPAY_API_KEY!,
  signingSecret: process.env.HAVENPAY_SIGNING_SECRET!,
})

async function _processPayment(amount: number, phone: string, cartId: string) {
  try {
    // 1. Get available providers
    const { providers } = await havenpay.payments.getProviders('USD')
    const _ecocash = providers.find(p => p.id === 'omari')

    if (!_ecocash) {
      throw new Error('EcoCash not available')
    }

    // 2. Start payment
    const payment = await havenpay.payments.start({
      amount,
      currency: 'USD',
      providerId: 'omari',
      cartId,
      msisdn: phone,
    })

    console.log('OTP sent to customer')

    // 3. Customer enters OTP (from your UI)
    const otp = await promptForOTP() // Your UI logic

    // 4. Verify OTP
    const result = await havenpay.payments.verifyOtp(payment.reference, otp)

    if (result.status === 'SUCCESS') {
      console.log('Payment successful!')
      return result
    }
    else if (result.status === 'PENDING') {
      // Check status later or wait for webhook
      console.log('Payment pending...')
      return result
    }
    else {
      throw new Error('Payment failed')
    }
  }
  catch (error) {
    console.error('Payment error:', error)
    throw error
  }
}

Recovery After Page Reload

async function _recoverPayment(cartId: string) {
  const payment = await havenpay.payments.getByCart(cartId)

  if (!payment) {
    // No existing payment, start new one
    return null
  }

  if (payment.status === 'AUTH_SENT') {
    // OTP was sent but not verified, show OTP input
    return { showOtpInput: true, reference: payment.reference }
  }

  if (payment.status === 'PENDING') {
    // Payment is being processed
    return { status: 'processing', reference: payment.reference }
  }

  if (payment.status === 'SUCCESS') {
    // Payment completed
    return { status: 'success', reference: payment.reference }
  }

  return null
}

Support

License

MIT © 7Haven

Keywords

havenpay

FAQs

Package last updated on 13 Nov 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts