@dryinov8/zumbopay-ts
v1.0.2
Published
SDK TypeScript/JavaScript oficial e componentes React para o gateway ZumboPay em Moçambique (M-Pesa, e-Mola, mKesh e Cartões)
Downloads
519
Maintainers
Readme
@dryinov8/zumbopay-ts
SDK oficial em TypeScript / JavaScript e componentes React para integração do gateway de pagamentos ZumboPay em Moçambique.
Compatível com Node.js, Next.js (App Router & Pages), React, Vite, Remix e Express.
🚀 Funcionalidades
- 🇲🇿 Suporte a Carteiras Móveis Moçambicanas:
- Vodacom M-Pesa (prefixos
84,85) - Movitel e-Mola (prefixos
86,87) - Tmcel mKesh (prefixos
82,83)
- Vodacom M-Pesa (prefixos
- 💳 Cartões Bancários: Checkout hospedado com suporte a cartões Visa e Mastercard (nacionais e internacionais).
- 🎛️ Interruptor Global / Kill-Switch (
enabled: boolean): Ative ou silencie o gateway dinamicamente via código (enabled: false) ou variável de ambiente (ZUMBOPAY_ENABLED=false). - ⚛️ Componentes & Hooks React Prontos: Componente
<ZumboPayModal />acessível com abas e deteção visual de operadora, e hookuseZumboPay(). - 🛡️ Segurança & Webhooks: Validação criptográfica de assinaturas HMAC-SHA256 em tempo constante contra timing attacks.
- 📦 Dual Build: Suporta módulos ESM (
import) e CommonJS (require), com tipos TypeScript completos (.d.ts).
📦 Instalação
# npm
npm install @dryinov8/zumbopay-ts
# pnpm
pnpm add @dryinov8/zumbopay-ts
# yarn
yarn add @dryinov8/zumbopay-ts(Se pretender utilizar os componentes React, certifique-se de que tem react e react-dom instalados no seu projeto).
⚙️ Configuração e Variáveis de Ambiente
Crie ou adicione ao seu ficheiro .env:
# Credenciais Principais
ZUMBOPAY_API_KEY=sua_chave_secreta_aqui
ZUMBOPAY_MERCHANT_ID=seu_merchant_id_aqui
ZUMBOPAY_WEBHOOK_SECRET=seu_webhook_secret_aqui
# Carteiras Pré-configuradas (UUIDs) - Opcional
# (Se deixar vazio, o SDK descobre automaticamente os UUIDs via GET /wallets)
ZUMBOPAY_WALLET_MPESA=uuid-carteira-mpesa
ZUMBOPAY_WALLET_EMOLA=uuid-carteira-emola
ZUMBOPAY_WALLET_MKESH=uuid-carteira-mkesh
ZUMBOPAY_WALLET_CARD=uuid-carteira-cartao
# Gatilho de silenciamento global / Kill-Switch (Opcional - padrão: true)
ZUMBOPAY_ENABLED=true💼 Como Configurar e Passar as Carteiras (Wallets)
O zumbopay-ts oferece total flexibilidade para passar os UUIDs das suas carteiras do painel ZumboPay:
Forma 1: Automática via .env (Recomendado para Node.js / Next.js)
Basta definir as variáveis no .env como mostrado acima. O cliente lê-as automaticamente:
const zumboPay = new ZumboPayClient({
apiKey: process.env.ZUMBOPAY_API_KEY!,
merchantId: process.env.ZUMBOPAY_MERCHANT_ID!,
// As carteiras MPESA, EMOLA, MKESH e CARD são lidas automaticamente do .env!
});Forma 2: No Construtor do Cliente
Pode passar o objeto wallets explicitamente no código:
const zumboPay = new ZumboPayClient({
apiKey: '...',
merchantId: '...',
wallets: {
mpesa: 'd1a2b3c4-....', // UUID carteira Vodacom M-Pesa
emola: 'e5f6a7b8-....', // UUID carteira Movitel e-Mola
mkesh: 'c9d0e1f2-....', // UUID carteira Tmcel mKesh
card: 'a3b4c5d6-....', // UUID carteira Cartão Bancário
},
});Forma 3: No Componente React (<ZumboPayModal />)
<ZumboPayModal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
config={{
apiKey: 'pk_live_...',
merchantId: 'mer_...',
wallets: {
mpesa: 'uuid-mpesa',
emola: 'uuid-emola',
mkesh: 'uuid-mkesh',
card: 'uuid-card',
},
}}
amount={1500}
/>Forma 4: Por Transação Pontual (Sobrescrita Ad-hoc)
await zumboPay.stkPush({
amount: 250,
phone: '841234567',
walletId: 'uuid-especifico-desta-transacao', // Força este UUID
});Forma 5: Auto-descoberta Dinâmica
Se não passar nenhum UUID de carteira, o SDK consulta automaticamente a rota GET /wallets da sua conta ZumboPay e mapeia a carteira correta para M-Pesa, e-Mola, mKesh ou Cartão (com cache em memória de 10 minutos).
📖 Guia de Utilização Rápida
1. Iniciar STK Push (M-Pesa, e-Mola ou mKesh) no Backend
import { ZumboPayClient } from '@dryinov8/zumbopay-ts';
const zumboPay = new ZumboPayClient({
apiKey: process.env.ZUMBOPAY_API_KEY!,
merchantId: process.env.ZUMBOPAY_MERCHANT_ID!,
// Ativação / Kill-Switch (lê automaticamente process.env.ZUMBOPAY_ENABLED)
enabled: process.env.ZUMBOPAY_ENABLED !== 'false',
});
// Envia prompt STK para o telemóvel do cliente
const response = await zumboPay.stkPush({
amount: 250.00,
phone: '841234567', // Detecta automaticamente Vodacom M-Pesa
reference: 'PROP-2026-001',
customerName: 'Manuel Cossa',
description: 'Pagamento de Propina Escolar',
});
if (response.success) {
console.log('Status:', response.status); // 'pending' | 'success'
console.log('Mensagem:', response.message);
} else {
console.error('Falha:', response.message);
}2. Criar Checkout Hospedado (Cartão Visa/Mastercard)
const checkout = await zumboPay.createCheckout({
amount: 1500.00,
title: 'Matrícula Anual',
reference: 'MAT-2026-890',
returnUrl: 'https://seu-sistema.ac.mz/pagamento/sucesso',
cancelUrl: 'https://seu-sistema.ac.mz/pagamento/cancelado',
channels: ['card', 'mpesa', 'emola', 'mkesh'],
});
if (checkout.success && checkout.checkoutUrl) {
// Redirecionar o cliente para a página de pagamento
console.log('URL de pagamento:', checkout.checkoutUrl);
}3. Consultar Estado da Transação
const status = await zumboPay.getStatus('PROP-2026-001');
console.log('Está pago?', status.paid); // true | false
console.log('Estado:', status.status); // 'success' | 'pending' | 'failed'🎛️ Gatilho de Silenciamento / Kill-Switch
Pode desativar ou suspender o gateway a qualquer momento sem necessidade de alterar o código dos seus endpoints:
// 1. Via variável de ambiente:
// ZUMBOPAY_ENABLED=false
// 2. Via inicialização:
const client = new ZumboPayClient({
apiKey: '...',
merchantId: '...',
enabled: false, // Silencia STK e checkouts
});
// 3. Via controlo dinâmico em tempo de execução:
client.setEnabled(false);
const res = await client.stkPush({ amount: 100, phone: '841234567' });
// Retorna imediatamente:
// { success: false, status: 'disabled', message: 'O gateway de pagamento ZumboPay está temporariamente desativado.' }
// Nenhuma chamada externa é feita!⚛️ Utilização com React / Next.js
Importe o componente ou o hook através do submódulo zumbopay-ts/react:
Opção A: Modal Completo (<ZumboPayModal />)
import React, { useState } from 'react';
import { ZumboPayModal } from '@dryinov8/zumbopay-ts/react';
export function CheckoutButton() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<button
onClick={() => setIsOpen(true)}
className="px-4 py-2 bg-blue-600 text-white rounded-lg font-bold"
>
Pagar com ZumboPay
</button>
<ZumboPayModal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
config={{
apiKey: 'pk_live_...',
merchantId: 'mer_...',
enabled: true,
}}
amount={1500}
reference="PEDIDO-994"
title="Inscrição em Exame"
onPaymentSuccess={(payment) => {
alert('Pagamento recebido com sucesso!');
setIsOpen(false);
}}
/>
</>
);
}Opção B: Hook Personalizado (useZumboPay)
import { useZumboPay } from '@dryinov8/zumbopay-ts/react';
export function CustomPaymentForm() {
const {
phone,
setPhone,
operator,
isLoading,
status,
errorMessage,
initiateStk,
} = useZumboPay({
config: {
apiKey: '...',
merchantId: '...',
},
onSuccess: (res) => console.log('Sucesso!', res),
});
return (
<div>
<input
type="tel"
value={phone}
onChange={(e) => setPhone(e.target.value)}
placeholder="84 / 86 / 82..."
/>
<span>Operadora detectada: {operator}</span>
<button
disabled={isLoading}
onClick={() => initiateStk({ amount: 500 })}
>
{isLoading ? 'A processar...' : 'Pagar 500 MZN'}
</button>
{errorMessage && <p className="text-red-500">{errorMessage}</p>}
</div>
);
}🔐 Validação de Webhook (Node.js / Express / Next.js)
Para garantir que as notificações de pagamento recebidas no seu servidor provêm legitimamente do ZumboPay:
Exemplo em Next.js (App Router: app/api/webhooks/zumbopay/route.ts)
import { NextRequest, NextResponse } from 'next/server';
import { verifyWebhookSignature } from '@dryinov8/zumbopay-ts';
export async function POST(req: NextRequest) {
const rawBody = await req.text();
const signature = req.headers.get('x-signature') || req.headers.get('x-zumbopay-signature') || '';
const secret = process.env.ZUMBOPAY_WEBHOOK_SECRET!;
// Validação criptográfica HMAC-SHA256
const isValid = verifyWebhookSignature(rawBody, signature, secret);
if (!isValid) {
return NextResponse.json({ error: 'Assinatura inválida' }, { status: 401 });
}
const event = JSON.parse(rawBody);
if (event.event === 'payment.succeeded') {
const { reference, amount } = event.data;
// Liquidar fatura na base de dados
}
return NextResponse.json({ received: true });
}🛠️ Utilitários de Telefonia de Moçambique
import { normalizePhone, detectOperator, isValidMozPhone } from '@dryinov8/zumbopay-ts';
normalizePhone('+258 (84) 123-4567'); // '258841234567'
detectOperator('841234567'); // 'mpesa'
detectOperator('861234567'); // 'emola'
detectOperator('821234567'); // 'mkesh'
isValidMozPhone('841234567'); // true
isValidMozPhone('12345'); // false🏷️ Versionamento e Política de Releases
Este projeto segue rigorosamente o padrão Semantic Versioning (SemVer):
- MAJOR (
X.0.0): Mudanças incompatíveis na API. - MINOR (
0.X.0): Adição de novas funcionalidades retrocompatíveis (ex: novos canais ou carteiras). - PATCH (
0.0.X): Correções de bugs e otimizações retrocompatíveis.
Consulte o histórico de alterações em CHANGELOG.md.
📄 Licença
Distribuído sob a licença MIT. Consulte LICENSE para mais detalhes.
Desenvolvido por Salvado Matavele.
