
Security News
Axios Maintainer Confirms Social Engineering Attack Behind npm Compromise
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.
@7haven/havenpay
Advanced tools
Official JavaScript/TypeScript SDK for HavenPay - Type-safe payment processing for African markets.
fetch() and Node.js crypto (zero dependencies, ES2020+)npm install @7haven/havenpay
# or
yarn add @7haven/havenpay
# or
pnpm add @7haven/havenpay
# or
bun add @7haven/havenpay
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!')
}
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')
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)
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`)
})
📖 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')
}
})
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)
}
}
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'
# 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
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
}
}
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
}
MIT © 7Haven
FAQs
Official JavaScript SDK for HavenPay
We found that @7haven/havenpay demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

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.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.