@dryinov8/zumbopay-ts 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +375 -0
- package/dist/chunk-YULBG3E6.mjs +338 -0
- package/dist/chunk-YULBG3E6.mjs.map +1 -0
- package/dist/client-CO8dBkq7.d.mts +188 -0
- package/dist/client-CO8dBkq7.d.ts +188 -0
- package/dist/index.d.mts +35 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +400 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +36 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react/index.d.mts +46 -0
- package/dist/react/index.d.ts +46 -0
- package/dist/react/index.js +755 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/index.mjs +393 -0
- package/dist/react/index.mjs.map +1 -0
- package/package.json +80 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Salvador Matavele
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
# @salvamatavele/zumbopay-ts
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@salvamatavele/zumbopay-ts)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
|
|
7
|
+
SDK oficial em **TypeScript / JavaScript** e componentes **React** para integração do gateway de pagamentos **[ZumboPay](https://zumbopay.com)** em Moçambique.
|
|
8
|
+
|
|
9
|
+
Compatível com **Node.js**, **Next.js** (App Router & Pages), **React**, **Vite**, **Remix** e **Express**.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 🚀 Funcionalidades
|
|
14
|
+
|
|
15
|
+
* 🇲🇿 **Suporte a Carteiras Móveis Moçambicanas:**
|
|
16
|
+
* **Vodacom M-Pesa** (prefixos `84`, `85`)
|
|
17
|
+
* **Movitel e-Mola** (prefixos `86`, `87`)
|
|
18
|
+
* **Tmcel mKesh** (prefixos `82`, `83`)
|
|
19
|
+
* 💳 **Cartões Bancários:** Checkout hospedado com suporte a cartões **Visa** e **Mastercard** (nacionais e internacionais).
|
|
20
|
+
* 🎛️ **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`).
|
|
21
|
+
* ⚛️ **Componentes & Hooks React Prontos:** Componente `<ZumboPayModal />` acessível com abas e deteção visual de operadora, e hook `useZumboPay()`.
|
|
22
|
+
* 🛡️ **Segurança & Webhooks:** Validação criptográfica de assinaturas HMAC-SHA256 em tempo constante contra *timing attacks*.
|
|
23
|
+
* 📦 **Dual Build:** Suporta módulos **ESM** (`import`) e **CommonJS** (`require`), com tipos TypeScript completos (`.d.ts`).
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 📦 Instalação
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# npm
|
|
31
|
+
npm install @salvamatavele/zumbopay-ts
|
|
32
|
+
|
|
33
|
+
# pnpm
|
|
34
|
+
pnpm add @salvamatavele/zumbopay-ts
|
|
35
|
+
|
|
36
|
+
# yarn
|
|
37
|
+
yarn add @salvamatavele/zumbopay-ts
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
*(Se pretender utilizar os componentes React, certifique-se de que tem `react` e `react-dom` instalados no seu projeto).*
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## ⚙️ Configuração e Variáveis de Ambiente
|
|
45
|
+
|
|
46
|
+
Crie ou adicione ao seu ficheiro `.env`:
|
|
47
|
+
|
|
48
|
+
```env
|
|
49
|
+
# Credenciais Principais
|
|
50
|
+
ZUMBOPAY_API_KEY=sua_chave_secreta_aqui
|
|
51
|
+
ZUMBOPAY_MERCHANT_ID=seu_merchant_id_aqui
|
|
52
|
+
ZUMBOPAY_WEBHOOK_SECRET=seu_webhook_secret_aqui
|
|
53
|
+
|
|
54
|
+
# Carteiras Pré-configuradas (UUIDs) - Opcional
|
|
55
|
+
# (Se deixar vazio, o SDK descobre automaticamente os UUIDs via GET /wallets)
|
|
56
|
+
ZUMBOPAY_WALLET_MPESA=uuid-carteira-mpesa
|
|
57
|
+
ZUMBOPAY_WALLET_EMOLA=uuid-carteira-emola
|
|
58
|
+
ZUMBOPAY_WALLET_MKESH=uuid-carteira-mkesh
|
|
59
|
+
ZUMBOPAY_WALLET_CARD=uuid-carteira-cartao
|
|
60
|
+
|
|
61
|
+
# Gatilho de silenciamento global / Kill-Switch (Opcional - padrão: true)
|
|
62
|
+
ZUMBOPAY_ENABLED=true
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 💼 Como Configurar e Passar as Carteiras (Wallets)
|
|
68
|
+
|
|
69
|
+
O `zumbopay-ts` oferece total flexibilidade para passar os UUIDs das suas carteiras do painel ZumboPay:
|
|
70
|
+
|
|
71
|
+
### Forma 1: Automática via `.env` (Recomendado para Node.js / Next.js)
|
|
72
|
+
Basta definir as variáveis no `.env` como mostrado acima. O cliente lê-as automaticamente:
|
|
73
|
+
```typescript
|
|
74
|
+
const zumboPay = new ZumboPayClient({
|
|
75
|
+
apiKey: process.env.ZUMBOPAY_API_KEY!,
|
|
76
|
+
merchantId: process.env.ZUMBOPAY_MERCHANT_ID!,
|
|
77
|
+
// As carteiras MPESA, EMOLA, MKESH e CARD são lidas automaticamente do .env!
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Forma 2: No Construtor do Cliente
|
|
82
|
+
Pode passar o objeto `wallets` explicitamente no código:
|
|
83
|
+
```typescript
|
|
84
|
+
const zumboPay = new ZumboPayClient({
|
|
85
|
+
apiKey: '...',
|
|
86
|
+
merchantId: '...',
|
|
87
|
+
wallets: {
|
|
88
|
+
mpesa: 'd1a2b3c4-....', // UUID carteira Vodacom M-Pesa
|
|
89
|
+
emola: 'e5f6a7b8-....', // UUID carteira Movitel e-Mola
|
|
90
|
+
mkesh: 'c9d0e1f2-....', // UUID carteira Tmcel mKesh
|
|
91
|
+
card: 'a3b4c5d6-....', // UUID carteira Cartão Bancário
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Forma 3: No Componente React (`<ZumboPayModal />`)
|
|
97
|
+
```tsx
|
|
98
|
+
<ZumboPayModal
|
|
99
|
+
isOpen={isOpen}
|
|
100
|
+
onClose={() => setIsOpen(false)}
|
|
101
|
+
config={{
|
|
102
|
+
apiKey: 'pk_live_...',
|
|
103
|
+
merchantId: 'mer_...',
|
|
104
|
+
wallets: {
|
|
105
|
+
mpesa: 'uuid-mpesa',
|
|
106
|
+
emola: 'uuid-emola',
|
|
107
|
+
mkesh: 'uuid-mkesh',
|
|
108
|
+
card: 'uuid-card',
|
|
109
|
+
},
|
|
110
|
+
}}
|
|
111
|
+
amount={1500}
|
|
112
|
+
/>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Forma 4: Por Transação Pontual (Sobrescrita Ad-hoc)
|
|
116
|
+
```typescript
|
|
117
|
+
await zumboPay.stkPush({
|
|
118
|
+
amount: 250,
|
|
119
|
+
phone: '841234567',
|
|
120
|
+
walletId: 'uuid-especifico-desta-transacao', // Força este UUID
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Forma 5: Auto-descoberta Dinâmica
|
|
125
|
+
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).
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 📖 Guia de Utilização Rápida
|
|
131
|
+
|
|
132
|
+
### 1. Iniciar STK Push (M-Pesa, e-Mola ou mKesh) no Backend
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import { ZumboPayClient } from 'zumbopay-ts';
|
|
136
|
+
|
|
137
|
+
const zumboPay = new ZumboPayClient({
|
|
138
|
+
apiKey: process.env.ZUMBOPAY_API_KEY!,
|
|
139
|
+
merchantId: process.env.ZUMBOPAY_MERCHANT_ID!,
|
|
140
|
+
// Ativação / Kill-Switch (lê automaticamente process.env.ZUMBOPAY_ENABLED)
|
|
141
|
+
enabled: process.env.ZUMBOPAY_ENABLED !== 'false',
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Envia prompt STK para o telemóvel do cliente
|
|
145
|
+
const response = await zumboPay.stkPush({
|
|
146
|
+
amount: 250.00,
|
|
147
|
+
phone: '841234567', // Detecta automaticamente Vodacom M-Pesa
|
|
148
|
+
reference: 'PROP-2026-001',
|
|
149
|
+
customerName: 'Manuel Cossa',
|
|
150
|
+
description: 'Pagamento de Propina Escolar',
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
if (response.success) {
|
|
154
|
+
console.log('Status:', response.status); // 'pending' | 'success'
|
|
155
|
+
console.log('Mensagem:', response.message);
|
|
156
|
+
} else {
|
|
157
|
+
console.error('Falha:', response.message);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
### 2. Criar Checkout Hospedado (Cartão Visa/Mastercard)
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
const checkout = await zumboPay.createCheckout({
|
|
167
|
+
amount: 1500.00,
|
|
168
|
+
title: 'Matrícula Anual',
|
|
169
|
+
reference: 'MAT-2026-890',
|
|
170
|
+
returnUrl: 'https://seu-sistema.ac.mz/pagamento/sucesso',
|
|
171
|
+
cancelUrl: 'https://seu-sistema.ac.mz/pagamento/cancelado',
|
|
172
|
+
channels: ['card', 'mpesa', 'emola', 'mkesh'],
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
if (checkout.success && checkout.checkoutUrl) {
|
|
176
|
+
// Redirecionar o cliente para a página de pagamento
|
|
177
|
+
console.log('URL de pagamento:', checkout.checkoutUrl);
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
### 3. Consultar Estado da Transação
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
const status = await zumboPay.getStatus('PROP-2026-001');
|
|
187
|
+
|
|
188
|
+
console.log('Está pago?', status.paid); // true | false
|
|
189
|
+
console.log('Estado:', status.status); // 'success' | 'pending' | 'failed'
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## 🎛️ Gatilho de Silenciamento / Kill-Switch
|
|
195
|
+
|
|
196
|
+
Pode desativar ou suspender o gateway a qualquer momento sem necessidade de alterar o código dos seus endpoints:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
// 1. Via variável de ambiente:
|
|
200
|
+
// ZUMBOPAY_ENABLED=false
|
|
201
|
+
|
|
202
|
+
// 2. Via inicialização:
|
|
203
|
+
const client = new ZumboPayClient({
|
|
204
|
+
apiKey: '...',
|
|
205
|
+
merchantId: '...',
|
|
206
|
+
enabled: false, // Silencia STK e checkouts
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// 3. Via controlo dinâmico em tempo de execução:
|
|
210
|
+
client.setEnabled(false);
|
|
211
|
+
|
|
212
|
+
const res = await client.stkPush({ amount: 100, phone: '841234567' });
|
|
213
|
+
// Retorna imediatamente:
|
|
214
|
+
// { success: false, status: 'disabled', message: 'O gateway de pagamento ZumboPay está temporariamente desativado.' }
|
|
215
|
+
// Nenhuma chamada externa é feita!
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## ⚛️ Utilização com React / Next.js
|
|
221
|
+
|
|
222
|
+
Importe o componente ou o hook através do submódulo `zumbopay-ts/react`:
|
|
223
|
+
|
|
224
|
+
### Opção A: Modal Completo (`<ZumboPayModal />`)
|
|
225
|
+
|
|
226
|
+
```tsx
|
|
227
|
+
import React, { useState } from 'react';
|
|
228
|
+
import { ZumboPayModal } from 'zumbopay-ts/react';
|
|
229
|
+
|
|
230
|
+
export function CheckoutButton() {
|
|
231
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
232
|
+
|
|
233
|
+
return (
|
|
234
|
+
<>
|
|
235
|
+
<button
|
|
236
|
+
onClick={() => setIsOpen(true)}
|
|
237
|
+
className="px-4 py-2 bg-blue-600 text-white rounded-lg font-bold"
|
|
238
|
+
>
|
|
239
|
+
Pagar com ZumboPay
|
|
240
|
+
</button>
|
|
241
|
+
|
|
242
|
+
<ZumboPayModal
|
|
243
|
+
isOpen={isOpen}
|
|
244
|
+
onClose={() => setIsOpen(false)}
|
|
245
|
+
config={{
|
|
246
|
+
apiKey: 'pk_live_...',
|
|
247
|
+
merchantId: 'mer_...',
|
|
248
|
+
enabled: true,
|
|
249
|
+
}}
|
|
250
|
+
amount={1500}
|
|
251
|
+
reference="PEDIDO-994"
|
|
252
|
+
title="Inscrição em Exame"
|
|
253
|
+
onPaymentSuccess={(payment) => {
|
|
254
|
+
alert('Pagamento recebido com sucesso!');
|
|
255
|
+
setIsOpen(false);
|
|
256
|
+
}}
|
|
257
|
+
/>
|
|
258
|
+
</>
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Opção B: Hook Personalizado (`useZumboPay`)
|
|
264
|
+
|
|
265
|
+
```tsx
|
|
266
|
+
import { useZumboPay } from 'zumbopay-ts/react';
|
|
267
|
+
|
|
268
|
+
export function CustomPaymentForm() {
|
|
269
|
+
const {
|
|
270
|
+
phone,
|
|
271
|
+
setPhone,
|
|
272
|
+
operator,
|
|
273
|
+
isLoading,
|
|
274
|
+
status,
|
|
275
|
+
errorMessage,
|
|
276
|
+
initiateStk,
|
|
277
|
+
} = useZumboPay({
|
|
278
|
+
config: {
|
|
279
|
+
apiKey: '...',
|
|
280
|
+
merchantId: '...',
|
|
281
|
+
},
|
|
282
|
+
onSuccess: (res) => console.log('Sucesso!', res),
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
return (
|
|
286
|
+
<div>
|
|
287
|
+
<input
|
|
288
|
+
type="tel"
|
|
289
|
+
value={phone}
|
|
290
|
+
onChange={(e) => setPhone(e.target.value)}
|
|
291
|
+
placeholder="84 / 86 / 82..."
|
|
292
|
+
/>
|
|
293
|
+
<span>Operadora detectada: {operator}</span>
|
|
294
|
+
|
|
295
|
+
<button
|
|
296
|
+
disabled={isLoading}
|
|
297
|
+
onClick={() => initiateStk({ amount: 500 })}
|
|
298
|
+
>
|
|
299
|
+
{isLoading ? 'A processar...' : 'Pagar 500 MZN'}
|
|
300
|
+
</button>
|
|
301
|
+
|
|
302
|
+
{errorMessage && <p className="text-red-500">{errorMessage}</p>}
|
|
303
|
+
</div>
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## 🔐 Validação de Webhook (Node.js / Express / Next.js)
|
|
311
|
+
|
|
312
|
+
Para garantir que as notificações de pagamento recebidas no seu servidor provêm legitimamente do ZumboPay:
|
|
313
|
+
|
|
314
|
+
### Exemplo em Next.js (App Router: `app/api/webhooks/zumbopay/route.ts`)
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
318
|
+
import { verifyWebhookSignature } from 'zumbopay-ts';
|
|
319
|
+
|
|
320
|
+
export async function POST(req: NextRequest) {
|
|
321
|
+
const rawBody = await req.text();
|
|
322
|
+
const signature = req.headers.get('x-signature') || req.headers.get('x-zumbopay-signature') || '';
|
|
323
|
+
const secret = process.env.ZUMBOPAY_WEBHOOK_SECRET!;
|
|
324
|
+
|
|
325
|
+
// Validação criptográfica HMAC-SHA256
|
|
326
|
+
const isValid = verifyWebhookSignature(rawBody, signature, secret);
|
|
327
|
+
|
|
328
|
+
if (!isValid) {
|
|
329
|
+
return NextResponse.json({ error: 'Assinatura inválida' }, { status: 401 });
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const event = JSON.parse(rawBody);
|
|
333
|
+
|
|
334
|
+
if (event.event === 'payment.succeeded') {
|
|
335
|
+
const { reference, amount } = event.data;
|
|
336
|
+
// Liquidar fatura na base de dados
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return NextResponse.json({ received: true });
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## 🛠️ Utilitários de Telefonia de Moçambique
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
import { normalizePhone, detectOperator, isValidMozPhone } from 'zumbopay-ts';
|
|
349
|
+
|
|
350
|
+
normalizePhone('+258 (84) 123-4567'); // '258841234567'
|
|
351
|
+
detectOperator('841234567'); // 'mpesa'
|
|
352
|
+
detectOperator('861234567'); // 'emola'
|
|
353
|
+
detectOperator('821234567'); // 'mkesh'
|
|
354
|
+
isValidMozPhone('841234567'); // true
|
|
355
|
+
isValidMozPhone('12345'); // false
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## 🏷️ Versionamento e Política de Releases
|
|
361
|
+
|
|
362
|
+
Este projeto segue rigorosamente o padrão **[Semantic Versioning (SemVer)](https://semver.org/)**:
|
|
363
|
+
- **MAJOR (`X.0.0`)**: Mudanças incompatíveis na API.
|
|
364
|
+
- **MINOR (`0.X.0`)**: Adição de novas funcionalidades retrocompatíveis (ex: novos canais ou carteiras).
|
|
365
|
+
- **PATCH (`0.0.X`)**: Correções de bugs e otimizações retrocompatíveis.
|
|
366
|
+
|
|
367
|
+
Consulte o histórico de alterações em `CHANGELOG.md`.
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## 📄 Licença
|
|
372
|
+
|
|
373
|
+
Distribuído sob a licença MIT. Consulte `LICENSE` para mais detalhes.
|
|
374
|
+
|
|
375
|
+
Desenvolvido por **[Salvador Matavele](https://github.com/salvamatavele)**.
|