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/dist/index.js ADDED
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+ /**
3
+ * PayBridge — Unified payment SDK for Node.js
4
+ * One API. Every payment provider.
5
+ *
6
+ * @see https://github.com/kobie3717/paybridge
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
20
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
21
+ };
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.PayBridge = void 0;
24
+ const softycomp_1 = require("./providers/softycomp");
25
+ const yoco_1 = require("./providers/yoco");
26
+ const ozow_1 = require("./providers/ozow");
27
+ __exportStar(require("./types"), exports);
28
+ __exportStar(require("./utils/currency"), exports);
29
+ class PayBridge {
30
+ constructor(config) {
31
+ this.provider = this.createProvider(config);
32
+ }
33
+ /**
34
+ * Create provider instance based on config
35
+ */
36
+ createProvider(config) {
37
+ const { provider, credentials, sandbox = true, webhookSecret } = config;
38
+ switch (provider) {
39
+ case 'softycomp':
40
+ if (!credentials.apiKey || !credentials.secretKey) {
41
+ throw new Error('SoftyComp requires apiKey and secretKey');
42
+ }
43
+ return new softycomp_1.SoftyCompProvider({
44
+ apiKey: credentials.apiKey,
45
+ secretKey: credentials.secretKey,
46
+ sandbox,
47
+ webhookSecret,
48
+ });
49
+ case 'yoco':
50
+ if (!credentials.apiKey) {
51
+ throw new Error('Yoco requires apiKey (secret key)');
52
+ }
53
+ return new yoco_1.YocoProvider({
54
+ apiKey: credentials.apiKey,
55
+ sandbox,
56
+ webhookSecret,
57
+ });
58
+ case 'ozow':
59
+ if (!credentials.apiKey || !credentials.siteCode || !credentials.privateKey) {
60
+ throw new Error('Ozow requires apiKey, siteCode, and privateKey');
61
+ }
62
+ return new ozow_1.OzowProvider({
63
+ apiKey: credentials.apiKey,
64
+ siteCode: credentials.siteCode,
65
+ privateKey: credentials.privateKey,
66
+ sandbox,
67
+ });
68
+ case 'payfast':
69
+ case 'paystack':
70
+ case 'stripe':
71
+ case 'peach':
72
+ throw new Error(`Provider ${provider} not yet implemented. Coming soon!`);
73
+ default:
74
+ throw new Error(`Unknown provider: ${provider}`);
75
+ }
76
+ }
77
+ // ==================== Payment Methods ====================
78
+ /**
79
+ * Create a one-time payment
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * const payment = await pay.createPayment({
84
+ * amount: 299.00,
85
+ * currency: 'ZAR',
86
+ * reference: 'INV-001',
87
+ * customer: {
88
+ * name: 'John Doe',
89
+ * email: 'john@example.com',
90
+ * phone: '0825551234'
91
+ * },
92
+ * urls: {
93
+ * success: 'https://myapp.com/success',
94
+ * cancel: 'https://myapp.com/cancel',
95
+ * webhook: 'https://myapp.com/webhook'
96
+ * }
97
+ * });
98
+ *
99
+ * // Redirect customer to payment page
100
+ * res.redirect(payment.checkoutUrl);
101
+ * ```
102
+ */
103
+ async createPayment(params) {
104
+ return this.provider.createPayment(params);
105
+ }
106
+ /**
107
+ * Create a recurring subscription
108
+ *
109
+ * @example
110
+ * ```typescript
111
+ * const subscription = await pay.createSubscription({
112
+ * amount: 299.00,
113
+ * currency: 'ZAR',
114
+ * interval: 'monthly',
115
+ * reference: 'SUB-001',
116
+ * customer: {
117
+ * name: 'John Doe',
118
+ * email: 'john@example.com'
119
+ * },
120
+ * urls: {
121
+ * success: 'https://myapp.com/success',
122
+ * cancel: 'https://myapp.com/cancel',
123
+ * webhook: 'https://myapp.com/webhook'
124
+ * },
125
+ * startDate: '2026-04-01'
126
+ * });
127
+ * ```
128
+ */
129
+ async createSubscription(params) {
130
+ return this.provider.createSubscription(params);
131
+ }
132
+ /**
133
+ * Get payment status
134
+ *
135
+ * @example
136
+ * ```typescript
137
+ * const payment = await pay.getPayment('pay_123');
138
+ * if (payment.status === 'completed') {
139
+ * console.log('Payment received!');
140
+ * }
141
+ * ```
142
+ */
143
+ async getPayment(id) {
144
+ return this.provider.getPayment(id);
145
+ }
146
+ /**
147
+ * Process a refund (full or partial)
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * // Full refund
152
+ * const refund = await pay.refund({
153
+ * paymentId: 'pay_123'
154
+ * });
155
+ *
156
+ * // Partial refund
157
+ * const refund = await pay.refund({
158
+ * paymentId: 'pay_123',
159
+ * amount: 100.00,
160
+ * reason: 'Customer request'
161
+ * });
162
+ * ```
163
+ */
164
+ async refund(params) {
165
+ return this.provider.refund(params);
166
+ }
167
+ // ==================== Webhooks ====================
168
+ /**
169
+ * Parse webhook payload into unified event format
170
+ *
171
+ * @example
172
+ * ```typescript
173
+ * app.post('/webhook', express.json(), (req, res) => {
174
+ * const event = pay.parseWebhook(req.body, req.headers);
175
+ *
176
+ * switch (event.type) {
177
+ * case 'payment.completed':
178
+ * console.log('Payment completed:', event.payment);
179
+ * break;
180
+ * case 'payment.failed':
181
+ * console.log('Payment failed:', event.payment);
182
+ * break;
183
+ * }
184
+ *
185
+ * res.sendStatus(200);
186
+ * });
187
+ * ```
188
+ */
189
+ parseWebhook(body, headers) {
190
+ return this.provider.parseWebhook(body, headers);
191
+ }
192
+ /**
193
+ * Verify webhook signature
194
+ *
195
+ * @example
196
+ * ```typescript
197
+ * app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
198
+ * if (!pay.verifyWebhook(req.body, req.headers)) {
199
+ * return res.status(400).send('Invalid signature');
200
+ * }
201
+ *
202
+ * const event = pay.parseWebhook(req.body, req.headers);
203
+ * // Process event...
204
+ *
205
+ * res.sendStatus(200);
206
+ * });
207
+ * ```
208
+ */
209
+ verifyWebhook(body, headers) {
210
+ return this.provider.verifyWebhook(body, headers);
211
+ }
212
+ // ==================== Helpers ====================
213
+ /**
214
+ * Get provider name
215
+ */
216
+ getProviderName() {
217
+ return this.provider.name;
218
+ }
219
+ /**
220
+ * Get supported currencies for current provider
221
+ */
222
+ getSupportedCurrencies() {
223
+ return this.provider.supportedCurrencies;
224
+ }
225
+ }
226
+ exports.PayBridge = PayBridge;
227
+ exports.default = PayBridge;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Base payment provider abstract class
3
+ * All payment providers must extend this class
4
+ */
5
+ import { CreatePaymentParams, PaymentResult, CreateSubscriptionParams, SubscriptionResult, RefundParams, RefundResult, WebhookEvent } from '../types';
6
+ export declare abstract class PaymentProvider {
7
+ /**
8
+ * Provider name (e.g. 'softycomp', 'yoco', 'ozow')
9
+ */
10
+ abstract readonly name: string;
11
+ /**
12
+ * Supported currencies
13
+ */
14
+ abstract readonly supportedCurrencies: string[];
15
+ /**
16
+ * Create a one-time payment
17
+ */
18
+ abstract createPayment(params: CreatePaymentParams): Promise<PaymentResult>;
19
+ /**
20
+ * Create a recurring subscription
21
+ */
22
+ abstract createSubscription(params: CreateSubscriptionParams): Promise<SubscriptionResult>;
23
+ /**
24
+ * Get payment status
25
+ */
26
+ abstract getPayment(id: string): Promise<PaymentResult>;
27
+ /**
28
+ * Process a refund (full or partial)
29
+ */
30
+ abstract refund(params: RefundParams): Promise<RefundResult>;
31
+ /**
32
+ * Parse webhook payload into unified event format
33
+ */
34
+ abstract parseWebhook(body: any, headers?: any): WebhookEvent;
35
+ /**
36
+ * Verify webhook signature
37
+ * @returns true if signature is valid, false otherwise
38
+ */
39
+ abstract verifyWebhook(body: any, headers?: any): boolean;
40
+ /**
41
+ * Validate currency is supported
42
+ */
43
+ protected validateCurrency(currency: string): void;
44
+ /**
45
+ * Validate future date
46
+ */
47
+ protected validateFutureDate(dateStr: string, fieldName: string): void;
48
+ }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ /**
3
+ * Base payment provider abstract class
4
+ * All payment providers must extend this class
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.PaymentProvider = void 0;
8
+ class PaymentProvider {
9
+ /**
10
+ * Validate currency is supported
11
+ */
12
+ validateCurrency(currency) {
13
+ if (!this.supportedCurrencies.includes(currency)) {
14
+ throw new Error(`Currency ${currency} not supported by ${this.name}. Supported: ${this.supportedCurrencies.join(', ')}`);
15
+ }
16
+ }
17
+ /**
18
+ * Validate future date
19
+ */
20
+ validateFutureDate(dateStr, fieldName) {
21
+ const date = new Date(dateStr);
22
+ const now = new Date();
23
+ now.setHours(0, 0, 0, 0);
24
+ if (date <= now) {
25
+ throw new Error(`${fieldName} must be a future date (minimum tomorrow)`);
26
+ }
27
+ }
28
+ }
29
+ exports.PaymentProvider = PaymentProvider;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Ozow payment provider
3
+ * South African instant EFT payment gateway
4
+ * @see https://hub.ozow.com
5
+ */
6
+ import { PaymentProvider } from './base';
7
+ import { CreatePaymentParams, PaymentResult, CreateSubscriptionParams, SubscriptionResult, RefundParams, RefundResult, WebhookEvent } from '../types';
8
+ interface OzowConfig {
9
+ apiKey: string;
10
+ siteCode: string;
11
+ privateKey: string;
12
+ sandbox: boolean;
13
+ }
14
+ export declare class OzowProvider extends PaymentProvider {
15
+ readonly name = "ozow";
16
+ readonly supportedCurrencies: string[];
17
+ private apiKey;
18
+ private siteCode;
19
+ private privateKey;
20
+ private sandbox;
21
+ private baseUrl;
22
+ constructor(config: OzowConfig);
23
+ createPayment(params: CreatePaymentParams): Promise<PaymentResult>;
24
+ createSubscription(params: CreateSubscriptionParams): Promise<SubscriptionResult>;
25
+ getPayment(id: string): Promise<PaymentResult>;
26
+ refund(params: RefundParams): Promise<RefundResult>;
27
+ parseWebhook(body: any, _headers?: any): WebhookEvent;
28
+ verifyWebhook(body: any, _headers?: any): boolean;
29
+ private mapOzowStatus;
30
+ private mapOzowEventType;
31
+ private generateWebhookHash;
32
+ }
33
+ export {};
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ /**
3
+ * Ozow payment provider
4
+ * South African instant EFT payment gateway
5
+ * @see https://hub.ozow.com
6
+ */
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.OzowProvider = void 0;
12
+ const crypto_1 = __importDefault(require("crypto"));
13
+ const base_1 = require("./base");
14
+ class OzowProvider extends base_1.PaymentProvider {
15
+ constructor(config) {
16
+ super();
17
+ this.name = 'ozow';
18
+ this.supportedCurrencies = ['ZAR'];
19
+ this.apiKey = config.apiKey;
20
+ this.siteCode = config.siteCode;
21
+ this.privateKey = config.privateKey;
22
+ this.sandbox = config.sandbox;
23
+ // Ozow API endpoints
24
+ if (this.sandbox) {
25
+ this.baseUrl = 'https://stagingapi.ozow.com';
26
+ }
27
+ else {
28
+ this.baseUrl = 'https://api.ozow.com';
29
+ }
30
+ }
31
+ // ==================== Payment Methods ====================
32
+ async createPayment(params) {
33
+ this.validateCurrency(params.currency);
34
+ // TODO: Implement Ozow payment creation
35
+ // POST /api/payments
36
+ // Amount in RANDS (not cents)
37
+ // Requires SHA512 hash of concatenated fields
38
+ // Auth: API key in header
39
+ // Body: {
40
+ // SiteCode: "...",
41
+ // CountryCode: "ZA",
42
+ // CurrencyCode: "ZAR",
43
+ // Amount: "299.00",
44
+ // TransactionReference: "...",
45
+ // BankReference: "...",
46
+ // Customer: "John Doe",
47
+ // Optional1: "email@example.com",
48
+ // Optional2: "0825551234",
49
+ // Optional3: "...",
50
+ // Optional4: "...",
51
+ // Optional5: "...",
52
+ // CancelUrl: "...",
53
+ // ErrorUrl: "...",
54
+ // SuccessUrl: "...",
55
+ // NotifyUrl: "...",
56
+ // IsTest: true/false,
57
+ // HashCheck: "sha512_hash"
58
+ // }
59
+ const requestData = {
60
+ SiteCode: this.siteCode,
61
+ CountryCode: 'ZA',
62
+ CurrencyCode: params.currency,
63
+ Amount: params.amount.toFixed(2),
64
+ TransactionReference: params.reference,
65
+ BankReference: params.reference,
66
+ Customer: params.customer.name,
67
+ Optional1: params.customer.email,
68
+ Optional2: params.customer.phone || '',
69
+ Optional3: params.description || '',
70
+ CancelUrl: params.urls.cancel,
71
+ ErrorUrl: params.urls.cancel,
72
+ SuccessUrl: params.urls.success,
73
+ NotifyUrl: params.urls.webhook,
74
+ IsTest: this.sandbox,
75
+ // HashCheck: this.generateHash(...) // TODO: Implement hash generation
76
+ };
77
+ // TODO: Generate SHA512 hash
78
+ // Hash = SHA512(SiteCode + CountryCode + CurrencyCode + Amount + TransactionReference + BankReference + Optional1 + Optional2 + Optional3 + Optional4 + Optional5 + CancelUrl + ErrorUrl + SuccessUrl + NotifyUrl + IsTest + PrivateKey)
79
+ console.warn('[PayBridge:Ozow] createPayment not yet implemented:', requestData);
80
+ throw new Error('Ozow provider not yet fully implemented. Coming soon!');
81
+ }
82
+ async createSubscription(params) {
83
+ this.validateCurrency(params.currency);
84
+ // TODO: Implement Ozow recurring payments
85
+ // Ozow supports recurring payments via their recurring payment API
86
+ // Different endpoint and structure from one-time payments
87
+ console.warn('[PayBridge:Ozow] createSubscription not yet implemented');
88
+ throw new Error('Ozow subscriptions not yet implemented. Coming soon!');
89
+ }
90
+ async getPayment(id) {
91
+ // TODO: Implement Ozow payment status check
92
+ // GET /api/payments/{transactionReference}
93
+ // Auth: API key in header
94
+ console.warn('[PayBridge:Ozow] getPayment not yet implemented:', id);
95
+ throw new Error('Ozow getPayment not yet implemented. Coming soon!');
96
+ }
97
+ async refund(params) {
98
+ // TODO: Implement Ozow refund
99
+ // POST /api/refunds
100
+ // Auth: API key in header
101
+ // Body: {
102
+ // SiteCode: "...",
103
+ // TransactionReference: "...",
104
+ // Amount: "299.00", // optional for partial
105
+ // HashCheck: "..."
106
+ // }
107
+ console.warn('[PayBridge:Ozow] refund not yet implemented:', params);
108
+ throw new Error('Ozow refunds not yet implemented. Coming soon!');
109
+ }
110
+ // ==================== Webhooks ====================
111
+ parseWebhook(body, _headers) {
112
+ const event = typeof body === 'string' ? JSON.parse(body) : body;
113
+ // TODO: Map Ozow webhook structure to PayBridge WebhookEvent
114
+ // Ozow webhook payload structure (form data):
115
+ // {
116
+ // SiteCode: "...",
117
+ // TransactionId: "...",
118
+ // TransactionReference: "...",
119
+ // Amount: "299.00",
120
+ // Status: "Complete" | "Cancelled" | "Error" | "Abandoned",
121
+ // Optional1: "...",
122
+ // Optional2: "...",
123
+ // Optional3: "...",
124
+ // Optional4: "...",
125
+ // Optional5: "...",
126
+ // CurrencyCode: "ZAR",
127
+ // IsTest: "true" | "false",
128
+ // StatusMessage: "...",
129
+ // Hash: "sha512_hash"
130
+ // }
131
+ const ozowStatus = event.Status;
132
+ const status = this.mapOzowStatus(ozowStatus);
133
+ const eventType = this.mapOzowEventType(ozowStatus);
134
+ return {
135
+ type: eventType,
136
+ payment: {
137
+ id: event.TransactionId || event.TransactionReference,
138
+ checkoutUrl: '',
139
+ status,
140
+ amount: parseFloat(event.Amount || '0'),
141
+ currency: event.CurrencyCode || 'ZAR',
142
+ reference: event.TransactionReference,
143
+ provider: 'ozow',
144
+ createdAt: new Date().toISOString(),
145
+ },
146
+ raw: event,
147
+ };
148
+ }
149
+ verifyWebhook(body, _headers) {
150
+ const event = typeof body === 'string' ? JSON.parse(body) : body;
151
+ const receivedHash = event.Hash;
152
+ if (!receivedHash) {
153
+ // No hash provided
154
+ return true;
155
+ }
156
+ // TODO: Verify Ozow SHA512 hash
157
+ // expectedHash = SHA512(SiteCode + TransactionId + TransactionReference + Amount + Status + Optional1 + Optional2 + Optional3 + Optional4 + Optional5 + CurrencyCode + IsTest + StatusMessage + PrivateKey)
158
+ const expectedHash = this.generateWebhookHash(event);
159
+ return receivedHash === expectedHash;
160
+ }
161
+ // ==================== Helpers ====================
162
+ mapOzowStatus(ozowStatus) {
163
+ const statusMap = {
164
+ Complete: 'completed',
165
+ Cancelled: 'cancelled',
166
+ Error: 'failed',
167
+ Abandoned: 'cancelled',
168
+ PendingInvestigation: 'pending',
169
+ };
170
+ return statusMap[ozowStatus] || 'pending';
171
+ }
172
+ mapOzowEventType(ozowStatus) {
173
+ const typeMap = {
174
+ Complete: 'payment.completed',
175
+ Cancelled: 'payment.cancelled',
176
+ Error: 'payment.failed',
177
+ Abandoned: 'payment.cancelled',
178
+ };
179
+ return typeMap[ozowStatus] || 'payment.pending';
180
+ }
181
+ generateWebhookHash(event) {
182
+ // TODO: Implement SHA512 hash generation for webhook verification
183
+ const fields = [
184
+ event.SiteCode,
185
+ event.TransactionId,
186
+ event.TransactionReference,
187
+ event.Amount,
188
+ event.Status,
189
+ event.Optional1 || '',
190
+ event.Optional2 || '',
191
+ event.Optional3 || '',
192
+ event.Optional4 || '',
193
+ event.Optional5 || '',
194
+ event.CurrencyCode,
195
+ event.IsTest,
196
+ event.StatusMessage || '',
197
+ this.privateKey,
198
+ ];
199
+ const concatenated = fields.join('');
200
+ return crypto_1.default.createHash('sha512').update(concatenated, 'utf8').digest('hex').toLowerCase();
201
+ }
202
+ }
203
+ exports.OzowProvider = OzowProvider;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * SoftyComp payment provider
3
+ * South African bill presentment and debit order platform
4
+ */
5
+ import { PaymentProvider } from './base';
6
+ import { CreatePaymentParams, PaymentResult, CreateSubscriptionParams, SubscriptionResult, RefundParams, RefundResult, WebhookEvent } from '../types';
7
+ interface SoftyCompConfig {
8
+ apiKey: string;
9
+ secretKey: string;
10
+ sandbox: boolean;
11
+ webhookSecret?: string;
12
+ }
13
+ export declare class SoftyCompProvider extends PaymentProvider {
14
+ readonly name = "softycomp";
15
+ readonly supportedCurrencies: string[];
16
+ private apiKey;
17
+ private secretKey;
18
+ private sandbox;
19
+ private baseUrl;
20
+ private webhookSecret?;
21
+ private token;
22
+ private tokenExpiry;
23
+ constructor(config: SoftyCompConfig);
24
+ private authenticate;
25
+ private apiRequest;
26
+ createPayment(params: CreatePaymentParams): Promise<PaymentResult>;
27
+ createSubscription(params: CreateSubscriptionParams): Promise<SubscriptionResult>;
28
+ getPayment(id: string): Promise<PaymentResult>;
29
+ refund(params: RefundParams): Promise<RefundResult>;
30
+ parseWebhook(body: any, _headers?: any): WebhookEvent;
31
+ verifyWebhook(body: string | Buffer, headers?: any): boolean;
32
+ private mapBillStatus;
33
+ }
34
+ export {};