paybridge 0.1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kobie Wentzel
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,408 @@
1
+ # PayBridge
2
+
3
+ > **One API. Every payment provider. 🌍**
4
+
5
+ [![npm version](https://img.shields.io/npm/v/paybridge.svg)](https://www.npmjs.com/package/paybridge)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue)](https://www.typescriptlang.org/)
8
+
9
+ Unified payment SDK for Node.js that works with multiple payment providers through a single, consistent API. Focus on South African providers first, with support for international gateways.
10
+
11
+ **WaSP is to WhatsApp what PayBridge is to payments** — one SDK, multiple backends, zero friction.
12
+
13
+ ## Features
14
+
15
+ - **Unified API** — Same code works across all providers
16
+ - **TypeScript-first** — Full type safety and autocomplete
17
+ - **South African focus** — SoftyComp, Yoco, Ozow, PayFast ready
18
+ - **International support** — Stripe, PayStack, Peach Payments (coming soon)
19
+ - **Production-ready** — Webhooks, refunds, subscriptions, retries
20
+ - **Zero lock-in** — Switch providers by changing 1 config line
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ npm install paybridge
26
+ ```
27
+
28
+ ## Interactive Playground
29
+
30
+ Want to see PayBridge in action before writing code? Try our **Stripe-style interactive playground**:
31
+
32
+ ```bash
33
+ cd playground
34
+ npm install
35
+ npm start
36
+ ```
37
+
38
+ Then open **http://localhost:4020** in your browser.
39
+
40
+ The playground lets you:
41
+ - Create real payments against SoftyComp sandbox
42
+ - Watch webhooks arrive in real-time
43
+ - Generate code snippets (TypeScript/JavaScript)
44
+ - Compare PayBridge vs raw API complexity
45
+ - Test all payment operations with a beautiful UI
46
+
47
+ Perfect for demos, learning, and rapid prototyping. See [playground/README.md](playground/README.md) for details.
48
+
49
+ ## Quick Start
50
+
51
+ ### One-time Payment
52
+
53
+ ```typescript
54
+ import { PayBridge } from 'paybridge';
55
+
56
+ // Initialize with your provider
57
+ const pay = new PayBridge({
58
+ provider: 'softycomp',
59
+ credentials: {
60
+ apiKey: process.env.SOFTYCOMP_API_KEY,
61
+ secretKey: process.env.SOFTYCOMP_SECRET_KEY
62
+ },
63
+ sandbox: true
64
+ });
65
+
66
+ // Create payment — same API regardless of provider
67
+ const payment = await pay.createPayment({
68
+ amount: 299.00, // Always in major currency unit (rands)
69
+ currency: 'ZAR',
70
+ reference: 'INV-001',
71
+ customer: {
72
+ name: 'John Doe',
73
+ email: 'john@example.com',
74
+ phone: '0825551234'
75
+ },
76
+ urls: {
77
+ success: 'https://myapp.com/success',
78
+ cancel: 'https://myapp.com/cancel',
79
+ webhook: 'https://myapp.com/webhook'
80
+ }
81
+ });
82
+
83
+ // Redirect customer to payment page
84
+ console.log(payment.checkoutUrl);
85
+ // Payment details
86
+ console.log(payment.id); // Provider payment ID
87
+ console.log(payment.status); // 'pending' | 'completed' | 'failed' | 'cancelled'
88
+ console.log(payment.provider); // 'softycomp'
89
+ ```
90
+
91
+ ### Recurring Subscription
92
+
93
+ ```typescript
94
+ const subscription = await pay.createSubscription({
95
+ amount: 299.00,
96
+ currency: 'ZAR',
97
+ interval: 'monthly', // 'weekly' | 'monthly' | 'yearly'
98
+ reference: 'SUB-001',
99
+ customer: {
100
+ name: 'Jane Smith',
101
+ email: 'jane@example.com'
102
+ },
103
+ urls: {
104
+ success: 'https://myapp.com/success',
105
+ cancel: 'https://myapp.com/cancel',
106
+ webhook: 'https://myapp.com/webhook'
107
+ },
108
+ startDate: '2026-04-01', // Must be future date
109
+ billingDay: 1 // Day of month (1-28)
110
+ });
111
+ ```
112
+
113
+ ### Refund
114
+
115
+ ```typescript
116
+ // Full refund
117
+ const refund = await pay.refund({
118
+ paymentId: 'pay_123'
119
+ });
120
+
121
+ // Partial refund
122
+ const refund = await pay.refund({
123
+ paymentId: 'pay_123',
124
+ amount: 100.00,
125
+ reason: 'Customer request'
126
+ });
127
+ ```
128
+
129
+ ### Check Payment Status
130
+
131
+ ```typescript
132
+ const payment = await pay.getPayment('pay_123');
133
+ if (payment.status === 'completed') {
134
+ console.log('Payment received!');
135
+ }
136
+ ```
137
+
138
+ ### Webhooks
139
+
140
+ ```typescript
141
+ import express from 'express';
142
+
143
+ const app = express();
144
+
145
+ // IMPORTANT: Use express.raw() for signature verification
146
+ app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
147
+ // Verify webhook signature
148
+ if (!pay.verifyWebhook(req.body, req.headers)) {
149
+ return res.status(400).send('Invalid signature');
150
+ }
151
+
152
+ // Parse webhook event
153
+ const event = pay.parseWebhook(req.body, req.headers);
154
+
155
+ switch (event.type) {
156
+ case 'payment.completed':
157
+ console.log('Payment completed:', event.payment);
158
+ // Fulfill order, activate subscription, etc.
159
+ break;
160
+
161
+ case 'payment.failed':
162
+ console.log('Payment failed:', event.payment);
163
+ // Notify customer
164
+ break;
165
+
166
+ case 'payment.cancelled':
167
+ console.log('Payment cancelled:', event.payment);
168
+ break;
169
+
170
+ case 'refund.completed':
171
+ console.log('Refund completed:', event.refund);
172
+ break;
173
+ }
174
+
175
+ res.sendStatus(200);
176
+ });
177
+ ```
178
+
179
+ ## Supported Providers
180
+
181
+ | Provider | One-time | Subscriptions | Refunds | Webhooks | Status |
182
+ |----------|----------|---------------|---------|----------|--------|
183
+ | **SoftyComp** | ✅ | ✅ | ✅ | ✅ | **Production** |
184
+ | **Yoco** | 🚧 | 🚧 | 🚧 | 🚧 | Coming soon |
185
+ | **Ozow** | 🚧 | 🚧 | 🚧 | 🚧 | Coming soon |
186
+ | **PayFast** | 📋 | 📋 | 📋 | 📋 | Planned |
187
+ | **PayStack** | 📋 | 📋 | 📋 | 📋 | Planned |
188
+ | **Stripe** | 📋 | 📋 | 📋 | 📋 | Planned |
189
+ | **Peach Payments** | 📋 | 📋 | 📋 | 📋 | Planned |
190
+
191
+ **Legend:** ✅ Production ready | 🚧 In development | 📋 Planned
192
+
193
+ ## Provider Configuration
194
+
195
+ ### SoftyComp
196
+
197
+ ```typescript
198
+ const pay = new PayBridge({
199
+ provider: 'softycomp',
200
+ credentials: {
201
+ apiKey: 'your_api_key',
202
+ secretKey: 'your_secret_key'
203
+ },
204
+ sandbox: true,
205
+ webhookSecret: 'optional_webhook_secret'
206
+ });
207
+ ```
208
+
209
+ **Docs:** [SoftyComp API](https://webapps.softycomp.co.za)
210
+
211
+ ### Yoco (Coming Soon)
212
+
213
+ ```typescript
214
+ const pay = new PayBridge({
215
+ provider: 'yoco',
216
+ credentials: {
217
+ apiKey: 'sk_test_...' // Secret key
218
+ },
219
+ sandbox: true,
220
+ webhookSecret: 'whsec_...'
221
+ });
222
+ ```
223
+
224
+ **Docs:** [Yoco Developer](https://developer.yoco.com)
225
+
226
+ ### Ozow (Coming Soon)
227
+
228
+ ```typescript
229
+ const pay = new PayBridge({
230
+ provider: 'ozow',
231
+ credentials: {
232
+ apiKey: 'your_api_key',
233
+ siteCode: 'your_site_code',
234
+ privateKey: 'your_private_key'
235
+ },
236
+ sandbox: true
237
+ });
238
+ ```
239
+
240
+ **Docs:** [Ozow Hub](https://hub.ozow.com)
241
+
242
+ ## Switch Providers in 1 Line
243
+
244
+ ```typescript
245
+ // Using SoftyComp
246
+ const pay1 = new PayBridge({ provider: 'softycomp', credentials: { ... } });
247
+
248
+ // Switch to Yoco — SAME API!
249
+ const pay2 = new PayBridge({ provider: 'yoco', credentials: { ... } });
250
+
251
+ // Switch to Ozow — SAME API!
252
+ const pay3 = new PayBridge({ provider: 'ozow', credentials: { ... } });
253
+
254
+ // All methods work identically
255
+ const payment = await pay1.createPayment({ ... }); // SoftyComp
256
+ const payment = await pay2.createPayment({ ... }); // Yoco
257
+ const payment = await pay3.createPayment({ ... }); // Ozow
258
+ ```
259
+
260
+ ## Why PayBridge?
261
+
262
+ South Africa's payment landscape is **fragmented**. Different providers for different use cases:
263
+
264
+ - **SoftyComp** — Debit orders and bill presentment
265
+ - **Yoco** — Card payments for SMEs
266
+ - **Ozow** — Instant EFT
267
+ - **PayFast** — Online payments
268
+
269
+ Each has its own SDK, quirks, and integration patterns. **PayBridge unifies them all.**
270
+
271
+ ### Before PayBridge
272
+
273
+ ```typescript
274
+ // SoftyComp
275
+ const softycomp = new SoftyComp({ ... });
276
+ const bill = await softycomp.createBill({ amount: 299.00, frequency: 'once-off', ... });
277
+
278
+ // Yoco
279
+ const yoco = new Yoco({ ... });
280
+ const checkout = await yoco.checkouts.create({ amountInCents: 29900, ... });
281
+
282
+ // Ozow
283
+ const ozow = new Ozow({ ... });
284
+ const payment = await ozow.initiatePayment({ Amount: '299.00', HashCheck: '...', ... });
285
+ ```
286
+
287
+ **Different APIs, different amount formats, different field names.**
288
+
289
+ ### With PayBridge
290
+
291
+ ```typescript
292
+ // ONE API for all providers
293
+ const payment = await pay.createPayment({
294
+ amount: 299.00,
295
+ currency: 'ZAR',
296
+ reference: 'INV-001',
297
+ customer: { ... },
298
+ urls: { ... }
299
+ });
300
+ ```
301
+
302
+ **Same code. Every provider.**
303
+
304
+ ## API Reference
305
+
306
+ ### `PayBridge`
307
+
308
+ #### Constructor
309
+
310
+ ```typescript
311
+ new PayBridge(config: PayBridgeConfig)
312
+ ```
313
+
314
+ #### Methods
315
+
316
+ - `createPayment(params: CreatePaymentParams): Promise<PaymentResult>`
317
+ - `createSubscription(params: CreateSubscriptionParams): Promise<SubscriptionResult>`
318
+ - `getPayment(id: string): Promise<PaymentResult>`
319
+ - `refund(params: RefundParams): Promise<RefundResult>`
320
+ - `parseWebhook(body: any, headers?: any): WebhookEvent`
321
+ - `verifyWebhook(body: any, headers?: any): boolean`
322
+ - `getProviderName(): string`
323
+ - `getSupportedCurrencies(): string[]`
324
+
325
+ ### Types
326
+
327
+ See [src/types.ts](src/types.ts) for full type definitions.
328
+
329
+ ## Currency Handling
330
+
331
+ PayBridge **always uses major currency units** (rands, dollars) in the API:
332
+
333
+ ```typescript
334
+ // ✅ Correct
335
+ { amount: 299.00, currency: 'ZAR' }
336
+
337
+ // ❌ Wrong (don't use cents)
338
+ { amount: 29900, currency: 'ZAR' }
339
+ ```
340
+
341
+ PayBridge handles provider-specific conversions internally:
342
+ - **SoftyComp** uses rands → no conversion
343
+ - **Yoco** uses cents → converts to cents
344
+ - **Ozow** uses rands → no conversion
345
+
346
+ ## Error Handling
347
+
348
+ ```typescript
349
+ try {
350
+ const payment = await pay.createPayment({ ... });
351
+ } catch (error) {
352
+ console.error('Payment failed:', error.message);
353
+ // Handle error (invalid credentials, network error, etc.)
354
+ }
355
+ ```
356
+
357
+ ## Webhook Security
358
+
359
+ **Always verify webhook signatures in production:**
360
+
361
+ ```typescript
362
+ app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
363
+ // Verify signature
364
+ if (!pay.verifyWebhook(req.body, req.headers)) {
365
+ return res.status(401).send('Unauthorized');
366
+ }
367
+
368
+ // Signature valid — process event
369
+ const event = pay.parseWebhook(req.body, req.headers);
370
+ // ...
371
+ });
372
+ ```
373
+
374
+ ## Roadmap
375
+
376
+ - [x] **v0.1** — Core API + SoftyComp provider
377
+ - [ ] **v0.2** — Yoco provider
378
+ - [ ] **v0.3** — Ozow provider
379
+ - [ ] **v0.4** — PayFast provider
380
+ - [ ] **v0.5** — PayStack provider (Nigeria)
381
+ - [ ] **v0.6** — Stripe provider (international)
382
+ - [ ] **v0.7** — Peach Payments provider
383
+ - [ ] **v1.0** — Production-ready with all SA providers
384
+
385
+ ## Contributing
386
+
387
+ We welcome contributions! To add a new payment provider:
388
+
389
+ 1. Create `src/providers/yourprovider.ts` extending `PaymentProvider`
390
+ 2. Implement all abstract methods
391
+ 3. Add provider to `src/index.ts` factory
392
+ 4. Update README with provider details
393
+ 5. Submit PR
394
+
395
+ See [src/providers/softycomp.ts](src/providers/softycomp.ts) for reference implementation.
396
+
397
+ ## License
398
+
399
+ MIT © [Kobie Wentzel](https://github.com/kobie3717)
400
+
401
+ ## Related Projects
402
+
403
+ - [**WaSP**](https://github.com/kobie3717/wasp) — Unified WhatsApp API (Baileys, Cloud API, Twilio)
404
+ - [**softycomp-node**](https://github.com/kobie3717/softycomp-node) — Official SoftyComp SDK
405
+
406
+ ---
407
+
408
+ **Built with ❤️ in South Africa**
@@ -0,0 +1,147 @@
1
+ /**
2
+ * PayBridge — Unified payment SDK for Node.js
3
+ * One API. Every payment provider.
4
+ *
5
+ * @see https://github.com/kobie3717/paybridge
6
+ */
7
+ import { PayBridgeConfig, CreatePaymentParams, PaymentResult, CreateSubscriptionParams, SubscriptionResult, RefundParams, RefundResult, WebhookEvent } from './types';
8
+ export * from './types';
9
+ export * from './utils/currency';
10
+ export declare class PayBridge {
11
+ private provider;
12
+ constructor(config: PayBridgeConfig);
13
+ /**
14
+ * Create provider instance based on config
15
+ */
16
+ private createProvider;
17
+ /**
18
+ * Create a one-time payment
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const payment = await pay.createPayment({
23
+ * amount: 299.00,
24
+ * currency: 'ZAR',
25
+ * reference: 'INV-001',
26
+ * customer: {
27
+ * name: 'John Doe',
28
+ * email: 'john@example.com',
29
+ * phone: '0825551234'
30
+ * },
31
+ * urls: {
32
+ * success: 'https://myapp.com/success',
33
+ * cancel: 'https://myapp.com/cancel',
34
+ * webhook: 'https://myapp.com/webhook'
35
+ * }
36
+ * });
37
+ *
38
+ * // Redirect customer to payment page
39
+ * res.redirect(payment.checkoutUrl);
40
+ * ```
41
+ */
42
+ createPayment(params: CreatePaymentParams): Promise<PaymentResult>;
43
+ /**
44
+ * Create a recurring subscription
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * const subscription = await pay.createSubscription({
49
+ * amount: 299.00,
50
+ * currency: 'ZAR',
51
+ * interval: 'monthly',
52
+ * reference: 'SUB-001',
53
+ * customer: {
54
+ * name: 'John Doe',
55
+ * email: 'john@example.com'
56
+ * },
57
+ * urls: {
58
+ * success: 'https://myapp.com/success',
59
+ * cancel: 'https://myapp.com/cancel',
60
+ * webhook: 'https://myapp.com/webhook'
61
+ * },
62
+ * startDate: '2026-04-01'
63
+ * });
64
+ * ```
65
+ */
66
+ createSubscription(params: CreateSubscriptionParams): Promise<SubscriptionResult>;
67
+ /**
68
+ * Get payment status
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * const payment = await pay.getPayment('pay_123');
73
+ * if (payment.status === 'completed') {
74
+ * console.log('Payment received!');
75
+ * }
76
+ * ```
77
+ */
78
+ getPayment(id: string): Promise<PaymentResult>;
79
+ /**
80
+ * Process a refund (full or partial)
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * // Full refund
85
+ * const refund = await pay.refund({
86
+ * paymentId: 'pay_123'
87
+ * });
88
+ *
89
+ * // Partial refund
90
+ * const refund = await pay.refund({
91
+ * paymentId: 'pay_123',
92
+ * amount: 100.00,
93
+ * reason: 'Customer request'
94
+ * });
95
+ * ```
96
+ */
97
+ refund(params: RefundParams): Promise<RefundResult>;
98
+ /**
99
+ * Parse webhook payload into unified event format
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * app.post('/webhook', express.json(), (req, res) => {
104
+ * const event = pay.parseWebhook(req.body, req.headers);
105
+ *
106
+ * switch (event.type) {
107
+ * case 'payment.completed':
108
+ * console.log('Payment completed:', event.payment);
109
+ * break;
110
+ * case 'payment.failed':
111
+ * console.log('Payment failed:', event.payment);
112
+ * break;
113
+ * }
114
+ *
115
+ * res.sendStatus(200);
116
+ * });
117
+ * ```
118
+ */
119
+ parseWebhook(body: any, headers?: any): WebhookEvent;
120
+ /**
121
+ * Verify webhook signature
122
+ *
123
+ * @example
124
+ * ```typescript
125
+ * app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
126
+ * if (!pay.verifyWebhook(req.body, req.headers)) {
127
+ * return res.status(400).send('Invalid signature');
128
+ * }
129
+ *
130
+ * const event = pay.parseWebhook(req.body, req.headers);
131
+ * // Process event...
132
+ *
133
+ * res.sendStatus(200);
134
+ * });
135
+ * ```
136
+ */
137
+ verifyWebhook(body: any, headers?: any): boolean;
138
+ /**
139
+ * Get provider name
140
+ */
141
+ getProviderName(): string;
142
+ /**
143
+ * Get supported currencies for current provider
144
+ */
145
+ getSupportedCurrencies(): string[];
146
+ }
147
+ export default PayBridge;