paybridge 0.1.2 → 0.2.1

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.
Files changed (48) hide show
  1. package/README.md +87 -7
  2. package/dist/circuit-breaker-store.d.ts +27 -0
  3. package/dist/circuit-breaker-store.js +25 -0
  4. package/dist/circuit-breaker.d.ts +30 -0
  5. package/dist/circuit-breaker.js +86 -0
  6. package/dist/crypto/base.d.ts +15 -0
  7. package/dist/crypto/base.js +24 -0
  8. package/dist/crypto/index.d.ts +35 -0
  9. package/dist/crypto/index.js +95 -0
  10. package/dist/crypto/mock.d.ts +15 -0
  11. package/dist/crypto/mock.js +112 -0
  12. package/dist/crypto/moonpay.d.ts +33 -0
  13. package/dist/crypto/moonpay.js +251 -0
  14. package/dist/crypto/router.d.ts +36 -0
  15. package/dist/crypto/router.js +287 -0
  16. package/dist/crypto/types.d.ts +89 -0
  17. package/dist/crypto/types.js +5 -0
  18. package/dist/crypto/yellowcard.d.ts +56 -0
  19. package/dist/crypto/yellowcard.js +310 -0
  20. package/dist/index.d.ts +9 -1
  21. package/dist/index.js +59 -3
  22. package/dist/providers/base.d.ts +5 -0
  23. package/dist/providers/flutterwave.d.ts +36 -0
  24. package/dist/providers/flutterwave.js +338 -0
  25. package/dist/providers/ozow.d.ts +20 -2
  26. package/dist/providers/ozow.js +161 -114
  27. package/dist/providers/payfast.d.ts +40 -0
  28. package/dist/providers/payfast.js +355 -0
  29. package/dist/providers/paystack.d.ts +37 -0
  30. package/dist/providers/paystack.js +335 -0
  31. package/dist/providers/peach.d.ts +50 -0
  32. package/dist/providers/peach.js +305 -0
  33. package/dist/providers/softycomp.d.ts +106 -0
  34. package/dist/providers/softycomp.js +234 -10
  35. package/dist/providers/stripe.d.ts +38 -0
  36. package/dist/providers/stripe.js +370 -0
  37. package/dist/providers/yoco.d.ts +12 -0
  38. package/dist/providers/yoco.js +159 -61
  39. package/dist/router.d.ts +33 -0
  40. package/dist/router.js +247 -0
  41. package/dist/routing-types.d.ts +39 -0
  42. package/dist/routing-types.js +14 -0
  43. package/dist/stores/redis.d.ts +30 -0
  44. package/dist/stores/redis.js +42 -0
  45. package/dist/strategies.d.ts +18 -0
  46. package/dist/strategies.js +44 -0
  47. package/dist/types.d.ts +4 -2
  48. package/package.json +7 -4
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { PaymentProvider } from './base';
6
6
  import { CreatePaymentParams, PaymentResult, CreateSubscriptionParams, SubscriptionResult, RefundParams, RefundResult, WebhookEvent } from '../types';
7
+ import { ProviderCapabilities } from '../routing-types';
7
8
  interface SoftyCompConfig {
8
9
  apiKey: string;
9
10
  secretKey: string;
@@ -29,6 +30,111 @@ export declare class SoftyCompProvider extends PaymentProvider {
29
30
  refund(params: RefundParams): Promise<RefundResult>;
30
31
  parseWebhook(body: any, _headers?: any): WebhookEvent;
31
32
  verifyWebhook(body: string | Buffer, headers?: any): boolean;
33
+ /**
34
+ * Set a bill to expired status
35
+ */
36
+ setBillToExpiredStatus(reference: string, userReference: string): Promise<void>;
37
+ /**
38
+ * Update bill presentment details
39
+ */
40
+ updateBillPresentment(params: {
41
+ reference: string;
42
+ amount?: number;
43
+ description?: string;
44
+ customerName?: string;
45
+ customerEmail?: string;
46
+ customerPhone?: string;
47
+ }): Promise<void>;
48
+ /**
49
+ * List bill presentment audit trail
50
+ */
51
+ listBillPresentmentAudits(reference: string, userReference: string): Promise<Array<{
52
+ auditId: number;
53
+ timestamp: string;
54
+ description: string;
55
+ user: string;
56
+ raw: any;
57
+ }>>;
58
+ /**
59
+ * Create a new client
60
+ */
61
+ createClient(params: {
62
+ name: string;
63
+ surname: string;
64
+ email: string;
65
+ phone: string;
66
+ idNumber?: string;
67
+ }): Promise<number>;
68
+ /**
69
+ * Create a Mobi-Mandate request for debit order sign-up
70
+ */
71
+ createMobiMandate(params: {
72
+ customerEmail: string;
73
+ customerPhone: string;
74
+ surname: string;
75
+ initials?: string;
76
+ idNumber?: string;
77
+ amount: number;
78
+ frequency: 'monthly' | 'yearly';
79
+ debitDay?: number;
80
+ description?: string;
81
+ contractCode?: string;
82
+ initialAmount?: number;
83
+ accountName?: string;
84
+ accountNumber?: string;
85
+ branchCode?: string;
86
+ accountType?: number;
87
+ expiryDate?: string;
88
+ commencementDate?: string;
89
+ collectionMethodTypeId?: number;
90
+ productId?: string;
91
+ maxCollectionAmount?: number;
92
+ successUrl?: string;
93
+ callbackUrl?: string;
94
+ }): Promise<{
95
+ url: string;
96
+ success: boolean;
97
+ message: string;
98
+ }>;
99
+ /**
100
+ * Update collection status (e.g., cancel a debit order)
101
+ */
102
+ updateCollectionStatus(params: {
103
+ collectionId: number;
104
+ statusTypeId: number;
105
+ }): Promise<void>;
106
+ /**
107
+ * Create a credit distribution (payout to bank account)
108
+ */
109
+ createCreditDistribution(params: {
110
+ amount: number;
111
+ accountNumber: string;
112
+ branchCode: string;
113
+ accountName: string;
114
+ reference: string;
115
+ userReference?: string;
116
+ }): Promise<{
117
+ distributionId: string;
118
+ success: boolean;
119
+ messages: string[];
120
+ }>;
121
+ /**
122
+ * Handle card expiry / re-auth: expire old bill and create new one
123
+ */
124
+ createReauthBill(params: {
125
+ oldReference: string;
126
+ newReference: string;
127
+ amount: number;
128
+ customerName: string;
129
+ customerEmail: string;
130
+ customerPhone: string;
131
+ description: string;
132
+ billingCycle: 'MONTHLY' | 'YEARLY';
133
+ successUrl: string;
134
+ cancelUrl: string;
135
+ notifyUrl: string;
136
+ }): Promise<SubscriptionResult>;
137
+ getCapabilities(): ProviderCapabilities;
32
138
  private mapBillStatus;
33
139
  }
34
140
  export {};
@@ -120,16 +120,14 @@ class SoftyCompProvider extends base_1.PaymentProvider {
120
120
  }
121
121
  async createSubscription(params) {
122
122
  this.validateCurrency(params.currency);
123
- // Map interval to SoftyComp frequency: 2=monthly, 6=yearly (valid range is 1-6)
124
- let frequencyTypeID;
125
- if (params.interval === 'monthly') {
126
- frequencyTypeID = 2;
127
- }
128
- else if (params.interval === 'yearly') {
129
- frequencyTypeID = 6;
130
- }
131
- else {
132
- throw new Error(`SoftyComp does not support ${params.interval} subscriptions. Use monthly or yearly.`);
123
+ // Map interval to SoftyComp frequency from official docs:
124
+ // 1=Once Off, 2=Monthly, 3=Weekly, 4=Yearly, 5=To Collect Amount, 6=Subscription
125
+ const frequencyMap = {
126
+ 'weekly': 3, 'monthly': 2, 'yearly': 4
127
+ };
128
+ const frequencyTypeID = frequencyMap[params.interval];
129
+ if (!frequencyTypeID) {
130
+ throw new Error(`SoftyComp does not support ${params.interval} subscriptions. Use weekly, monthly, or yearly.`);
133
131
  }
134
132
  // Parse and validate start date
135
133
  let startDate;
@@ -279,6 +277,232 @@ class SoftyCompProvider extends base_1.PaymentProvider {
279
277
  .digest('hex');
280
278
  return signature === expectedSignature || signature === `sha256=${expectedSignature}`;
281
279
  }
280
+ // ==================== Bill Management ====================
281
+ /**
282
+ * Set a bill to expired status
283
+ */
284
+ async setBillToExpiredStatus(reference, userReference) {
285
+ await this.apiRequest('POST', `/api/paygatecontroller/setBillToExpiredStatus/${encodeURIComponent(reference)}/${encodeURIComponent(userReference)}`, '' // Empty body required
286
+ );
287
+ }
288
+ /**
289
+ * Update bill presentment details
290
+ */
291
+ async updateBillPresentment(params) {
292
+ // First, get the current bill to retrieve full structure
293
+ const currentBill = await this.apiRequest('GET', `/api/paygatecontroller/listBillPresentmentDetails/${params.reference}/${params.reference}`);
294
+ const updateData = {
295
+ Reference: params.reference,
296
+ UserReference: currentBill.userReference || params.reference,
297
+ Items: currentBill.items || [],
298
+ };
299
+ if (params.customerName !== undefined) {
300
+ updateData.Name = params.customerName;
301
+ }
302
+ if (params.customerEmail !== undefined) {
303
+ updateData.Emailaddress = params.customerEmail;
304
+ }
305
+ if (params.customerPhone !== undefined) {
306
+ updateData.Cellno = params.customerPhone;
307
+ }
308
+ // Update item fields if amount or description changed
309
+ if (updateData.Items.length > 0) {
310
+ if (params.amount !== undefined) {
311
+ updateData.Items[0].Amount = parseFloat(params.amount.toFixed(2));
312
+ }
313
+ if (params.description !== undefined) {
314
+ updateData.Items[0].Description = params.description;
315
+ }
316
+ }
317
+ await this.apiRequest('POST', '/api/paygatecontroller/updateBillPresentment', updateData);
318
+ }
319
+ /**
320
+ * List bill presentment audit trail
321
+ */
322
+ async listBillPresentmentAudits(reference, userReference) {
323
+ const result = await this.apiRequest('GET', `/api/paygatecontroller/listBillPresentmentAudits/${encodeURIComponent(reference)}/${encodeURIComponent(userReference)}`);
324
+ return (result || []).map((audit) => ({
325
+ auditId: audit.auditId || 0,
326
+ timestamp: audit.timestamp || audit.date || '',
327
+ description: audit.description || audit.action || '',
328
+ user: audit.user || audit.userName || '',
329
+ raw: audit,
330
+ }));
331
+ }
332
+ // ==================== Client Management ====================
333
+ /**
334
+ * Create a new client
335
+ */
336
+ async createClient(params) {
337
+ const result = await this.apiRequest('POST', '/api/clients/createclient', {
338
+ clientId: 0,
339
+ clientTypeId: 1, // Individual
340
+ contractCode: `C${Date.now().toString().slice(-13)}`, // Max 14 chars
341
+ initials: params.name.charAt(0),
342
+ surname: params.surname,
343
+ idnumber: params.idNumber || '',
344
+ clientStatusTypeId: 1, // Active
345
+ cellphoneNumber: params.phone,
346
+ emailAddress: params.email,
347
+ sendSmsDonotifications: true,
348
+ sendSmsUnpaidsNotifications: true,
349
+ isSouthAfricanCitizen: true,
350
+ fullNames: params.name,
351
+ });
352
+ if (!result.success) {
353
+ throw new Error(`SoftyComp create client failed: ${result.messages.join(', ')}`);
354
+ }
355
+ return result.value;
356
+ }
357
+ // ==================== Mobi-Mandate (Debit Orders) ====================
358
+ /**
359
+ * Create a Mobi-Mandate request for debit order sign-up
360
+ */
361
+ async createMobiMandate(params) {
362
+ const frequencyMap = {
363
+ monthly: 2,
364
+ yearly: 4,
365
+ };
366
+ const today = new Date();
367
+ const tomorrow = new Date(today);
368
+ tomorrow.setDate(tomorrow.getDate() + 1);
369
+ const defaultCommencementDate = tomorrow.toISOString().split('T')[0];
370
+ const mandateData = {
371
+ EmailAddress: params.customerEmail,
372
+ CellphoneNumber: params.customerPhone,
373
+ ContractCode: params.contractCode || `M${Date.now().toString().slice(-5)}`, // Max 6 chars
374
+ Surname: params.surname,
375
+ Initials: params.initials || params.surname.charAt(0),
376
+ IDNumber: params.idNumber || '',
377
+ ProductID: params.productId ? parseInt(params.productId, 10) : null,
378
+ Amount: parseFloat(params.amount.toFixed(2)),
379
+ InitialAmount: params.initialAmount ? parseFloat(params.initialAmount.toFixed(2)) : parseFloat(params.amount.toFixed(2)),
380
+ AccountName: params.accountName || '',
381
+ AccountNumber: params.accountNumber || '',
382
+ BranchCode: params.branchCode || '',
383
+ AccountType: params.accountType || 1,
384
+ ExpiryDate: params.expiryDate || null,
385
+ CommencementDate: params.commencementDate || defaultCommencementDate,
386
+ CollectionFrequencyTypeID: frequencyMap[params.frequency] || 2,
387
+ CollectionMethodTypeID: params.collectionMethodTypeId || 4, // NAEDO
388
+ DebitDay: params.debitDay || 1,
389
+ Description: params.description || 'Debit Order',
390
+ DebitMonth: null,
391
+ TransactionDate1: null,
392
+ TransactionDate2: null,
393
+ TransactionDate3: null,
394
+ TransactionDate4: null,
395
+ NaedoTrackingCodeID: 12,
396
+ EntryClassCodeTypeID: 1,
397
+ AdjustmentCategoryTypeID: 2,
398
+ DebiCheckMaximumCollectionAmount: params.maxCollectionAmount || (params.amount * 1.5),
399
+ DateAdjustmentAllowed: false,
400
+ AdjustmentAmount: 0,
401
+ AdjustmentRate: 0,
402
+ DebitValueTypeID: 1,
403
+ RedirectURL: params.successUrl || '',
404
+ CallbackURL: params.callbackUrl || '',
405
+ SendCorrespondence: true,
406
+ ExternalRequest: true,
407
+ HideHomeTel: true,
408
+ HideWorkTel: true,
409
+ HideProductDetail: false,
410
+ HideExpiryDate: true,
411
+ HideAdditionalInfo: true,
412
+ HideDescription: false,
413
+ };
414
+ const result = await this.apiRequest('POST', '/api/mobimandate/generateMobiMandateRequest', mandateData);
415
+ if (!result.success) {
416
+ throw new Error(`SoftyComp Mobi-Mandate failed: ${result.message}`);
417
+ }
418
+ return {
419
+ url: result.tinyURL,
420
+ success: result.success,
421
+ message: result.message,
422
+ };
423
+ }
424
+ /**
425
+ * Update collection status (e.g., cancel a debit order)
426
+ */
427
+ async updateCollectionStatus(params) {
428
+ await this.apiRequest('POST', '/api/collections/updateCollectionStatus', {
429
+ collectionID: params.collectionId,
430
+ collectionStatusTypeID: params.statusTypeId,
431
+ });
432
+ }
433
+ // ==================== Credit Distribution (Payouts) ====================
434
+ /**
435
+ * Create a credit distribution (payout to bank account)
436
+ */
437
+ async createCreditDistribution(params) {
438
+ const result = await this.apiRequest('POST', '/api/creditdistribution/createCreditDistribution', {
439
+ creditFileTransactions: [
440
+ {
441
+ amount: parseFloat(params.amount.toFixed(2)),
442
+ accountNumber: params.accountNumber,
443
+ branchCode: params.branchCode,
444
+ accountName: params.accountName,
445
+ reference: params.reference,
446
+ userReference: params.userReference || params.reference,
447
+ },
448
+ ],
449
+ });
450
+ return {
451
+ distributionId: result?.value?.toString() || `dist_${Date.now()}`,
452
+ success: result?.success || false,
453
+ messages: result?.messages || [],
454
+ };
455
+ }
456
+ // ==================== Re-authentication ====================
457
+ /**
458
+ * Handle card expiry / re-auth: expire old bill and create new one
459
+ */
460
+ async createReauthBill(params) {
461
+ // Step 1: Expire the old bill
462
+ try {
463
+ await this.setBillToExpiredStatus(params.oldReference, params.oldReference);
464
+ }
465
+ catch (err) {
466
+ console.warn(`[SoftyComp] Could not expire old bill ${params.oldReference}:`, err);
467
+ // Continue — the old bill may already be expired
468
+ }
469
+ // Step 2: Create a new subscription with a different reference
470
+ const isMonthly = params.billingCycle === 'MONTHLY';
471
+ const tomorrow = new Date();
472
+ tomorrow.setDate(tomorrow.getDate() + 1);
473
+ return this.createSubscription({
474
+ amount: params.amount,
475
+ currency: 'ZAR',
476
+ interval: isMonthly ? 'monthly' : 'yearly',
477
+ reference: params.newReference,
478
+ description: params.description,
479
+ customer: {
480
+ name: params.customerName,
481
+ email: params.customerEmail,
482
+ phone: params.customerPhone,
483
+ },
484
+ urls: {
485
+ success: params.successUrl,
486
+ cancel: params.cancelUrl,
487
+ webhook: params.notifyUrl,
488
+ },
489
+ startDate: tomorrow.toISOString().split('T')[0],
490
+ billingDay: tomorrow.getDate(),
491
+ });
492
+ }
493
+ // ==================== Capabilities ====================
494
+ getCapabilities() {
495
+ return {
496
+ fees: {
497
+ fixed: 0,
498
+ percent: 2.5,
499
+ currency: 'ZAR',
500
+ },
501
+ currencies: this.supportedCurrencies,
502
+ country: 'ZA',
503
+ avgLatencyMs: 800,
504
+ };
505
+ }
282
506
  // ==================== Helpers ====================
283
507
  mapBillStatus(statusTypeID) {
284
508
  switch (Number(statusTypeID)) {
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Stripe payment provider
3
+ * Global payment processor supporting 135+ currencies
4
+ * @see https://stripe.com/docs/api
5
+ */
6
+ import { PaymentProvider } from './base';
7
+ import { CreatePaymentParams, PaymentResult, CreateSubscriptionParams, SubscriptionResult, RefundParams, RefundResult, WebhookEvent } from '../types';
8
+ import { ProviderCapabilities } from '../routing-types';
9
+ interface StripeConfig {
10
+ apiKey: string;
11
+ webhookSecret?: string;
12
+ sandbox?: boolean;
13
+ }
14
+ export declare class StripeProvider extends PaymentProvider {
15
+ readonly name = "stripe";
16
+ readonly supportedCurrencies: string[];
17
+ private apiKey;
18
+ private webhookSecret?;
19
+ private sandbox;
20
+ private baseUrl;
21
+ constructor(config: StripeConfig);
22
+ private buildFormData;
23
+ private apiRequest;
24
+ createPayment(params: CreatePaymentParams): Promise<PaymentResult>;
25
+ createSubscription(params: CreateSubscriptionParams): Promise<SubscriptionResult>;
26
+ getPayment(id: string): Promise<PaymentResult>;
27
+ refund(params: RefundParams): Promise<RefundResult>;
28
+ parseWebhook(body: any, _headers?: any): WebhookEvent;
29
+ /**
30
+ * Verify webhook signature using Stripe's scheme.
31
+ *
32
+ * CRITICAL: body must be the raw string or Buffer from the webhook request.
33
+ * Passing a parsed JSON object will cause signature verification to fail.
34
+ */
35
+ verifyWebhook(body: string | Buffer, headers?: any): boolean;
36
+ getCapabilities(): ProviderCapabilities;
37
+ }
38
+ export {};