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
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Yellow Card crypto on/off-ramp provider
3
+ * @see https://yellowcard.io
4
+ *
5
+ * WARNING: This implementation is NOT verified against official Yellow Card API documentation.
6
+ * No public documentation, SDK, or API reference could be found (checked 2026-05-03).
7
+ * All signing logic, header names, and endpoints are SPECULATIVE based on common patterns.
8
+ *
9
+ * Known gaps requiring verification:
10
+ * - Base URLs (sandbox.api.yellowcard.io / api.yellowcard.io)
11
+ * - Endpoint paths (/v1/quotes/buy, /v1/orders/buy, /v1/orders/sell)
12
+ * - Header names (X-API-Key, X-Timestamp, X-Signature)
13
+ * - Signature algorithm (HMAC-SHA256 of method+path+timestamp+body, hex-encoded)
14
+ * - Webhook signature scheme and header name
15
+ *
16
+ * To verify: Contact Yellow Card for partner API documentation or request sandbox credentials
17
+ * to test against their actual endpoints.
18
+ */
19
+ import { CryptoRampProvider } from './base';
20
+ import { OnRampParams, OffRampParams, RampQuote, RampResult, CryptoRampCapabilities } from './types';
21
+ interface YellowCardConfig {
22
+ apiKey: string;
23
+ secretKey: string;
24
+ sandbox: boolean;
25
+ webhookSecret?: string;
26
+ }
27
+ /**
28
+ * Yellow Card crypto on/off-ramp provider.
29
+ *
30
+ * @experimental Yellow Card public API documentation could not be located. Endpoint paths,
31
+ * header names, signature algorithm, and webhook scheme are all speculative — based on
32
+ * common patterns, not verified against an official spec or SDK. Will not work against
33
+ * real Yellow Card sandbox without spec confirmation. Use only with partner-provided
34
+ * integration docs.
35
+ */
36
+ export declare class YellowCardProvider extends CryptoRampProvider {
37
+ readonly name = "yellowcard";
38
+ private apiKey;
39
+ private secretKey;
40
+ private sandbox;
41
+ private baseUrl;
42
+ private webhookSecret?;
43
+ private static warnedExperimental;
44
+ constructor(config: YellowCardConfig);
45
+ getQuote(direction: 'on' | 'off', fiatAmount: number, fiatCurrency: string, cryptoAsset: string, network: string): Promise<RampQuote>;
46
+ createOnRamp(params: OnRampParams): Promise<RampResult>;
47
+ createOffRamp(params: OffRampParams): Promise<RampResult>;
48
+ getRamp(id: string): Promise<RampResult>;
49
+ parseWebhook(body: any, _headers?: any): any;
50
+ verifyWebhook(body: string | Buffer, headers?: any): boolean;
51
+ getCapabilities(): CryptoRampCapabilities;
52
+ private apiRequest;
53
+ private generateSignature;
54
+ private mapYellowCardStatus;
55
+ }
56
+ export {};
@@ -0,0 +1,310 @@
1
+ "use strict";
2
+ /**
3
+ * Yellow Card crypto on/off-ramp provider
4
+ * @see https://yellowcard.io
5
+ *
6
+ * WARNING: This implementation is NOT verified against official Yellow Card API documentation.
7
+ * No public documentation, SDK, or API reference could be found (checked 2026-05-03).
8
+ * All signing logic, header names, and endpoints are SPECULATIVE based on common patterns.
9
+ *
10
+ * Known gaps requiring verification:
11
+ * - Base URLs (sandbox.api.yellowcard.io / api.yellowcard.io)
12
+ * - Endpoint paths (/v1/quotes/buy, /v1/orders/buy, /v1/orders/sell)
13
+ * - Header names (X-API-Key, X-Timestamp, X-Signature)
14
+ * - Signature algorithm (HMAC-SHA256 of method+path+timestamp+body, hex-encoded)
15
+ * - Webhook signature scheme and header name
16
+ *
17
+ * To verify: Contact Yellow Card for partner API documentation or request sandbox credentials
18
+ * to test against their actual endpoints.
19
+ */
20
+ var __importDefault = (this && this.__importDefault) || function (mod) {
21
+ return (mod && mod.__esModule) ? mod : { "default": mod };
22
+ };
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.YellowCardProvider = void 0;
25
+ const crypto_1 = __importDefault(require("crypto"));
26
+ const base_1 = require("./base");
27
+ /**
28
+ * Yellow Card crypto on/off-ramp provider.
29
+ *
30
+ * @experimental Yellow Card public API documentation could not be located. Endpoint paths,
31
+ * header names, signature algorithm, and webhook scheme are all speculative — based on
32
+ * common patterns, not verified against an official spec or SDK. Will not work against
33
+ * real Yellow Card sandbox without spec confirmation. Use only with partner-provided
34
+ * integration docs.
35
+ */
36
+ class YellowCardProvider extends base_1.CryptoRampProvider {
37
+ constructor(config) {
38
+ super();
39
+ this.name = 'yellowcard';
40
+ this.apiKey = config.apiKey;
41
+ this.secretKey = config.secretKey;
42
+ this.sandbox = config.sandbox;
43
+ this.webhookSecret = config.webhookSecret;
44
+ this.baseUrl = this.sandbox
45
+ ? 'https://sandbox.api.yellowcard.io'
46
+ : 'https://api.yellowcard.io';
47
+ if (!YellowCardProvider.warnedExperimental) {
48
+ console.warn('[paybridge] YellowCardProvider is experimental — see @experimental notes in source. Do not use in production without verifying spec against partner docs.');
49
+ YellowCardProvider.warnedExperimental = true;
50
+ }
51
+ }
52
+ async getQuote(direction, fiatAmount, fiatCurrency, cryptoAsset, network) {
53
+ const endpoint = direction === 'on' ? '/v1/quotes/buy' : '/v1/quotes/sell';
54
+ const requestBody = {
55
+ fiatCurrency: fiatCurrency.toUpperCase(),
56
+ cryptoCurrency: cryptoAsset.toUpperCase(),
57
+ fiatAmount,
58
+ network: network.toUpperCase(),
59
+ };
60
+ const response = await this.apiRequest('POST', endpoint, requestBody);
61
+ const rate = response.rate || 0;
62
+ const cryptoAmount = response.cryptoAmount || 0;
63
+ const feeTotal = response.fee || 0;
64
+ return {
65
+ fiatAmount,
66
+ cryptoAmount,
67
+ rate,
68
+ feeFixed: 0,
69
+ feePercent: (feeTotal / fiatAmount) * 100,
70
+ feeTotal,
71
+ expiresAt: response.expiresAt || new Date(Date.now() + 5 * 60 * 1000).toISOString(),
72
+ };
73
+ }
74
+ async createOnRamp(params) {
75
+ (0, base_1.validateWalletAddress)(params.destinationWallet, params.network);
76
+ const quote = await this.getQuote('on', params.fiatAmount, params.fiatCurrency, params.asset, params.network);
77
+ const requestBody = {
78
+ cryptoCurrency: params.asset.toUpperCase(),
79
+ fiatCurrency: params.fiatCurrency.toUpperCase(),
80
+ fiatAmount: params.fiatAmount,
81
+ network: params.network.toUpperCase(),
82
+ walletAddress: params.destinationWallet,
83
+ customer: {
84
+ email: params.customer.email,
85
+ name: params.customer.name,
86
+ phone: params.customer.phone,
87
+ },
88
+ callbackUrl: params.urls.webhook,
89
+ successUrl: params.urls.success,
90
+ cancelUrl: params.urls.cancel,
91
+ externalReference: params.reference,
92
+ };
93
+ const response = await this.apiRequest('POST', '/v1/orders/buy', requestBody);
94
+ const sanitizedRaw = { ...response };
95
+ if (sanitizedRaw.bankAccount)
96
+ delete sanitizedRaw.bankAccount;
97
+ if (sanitizedRaw.bank_account)
98
+ delete sanitizedRaw.bank_account;
99
+ return {
100
+ id: response.id || `yc_on_${params.reference}`,
101
+ direction: 'on',
102
+ status: this.mapYellowCardStatus(response.status),
103
+ quote,
104
+ checkoutUrl: response.checkoutUrl || response.paymentUrl,
105
+ createdAt: response.createdAt || new Date().toISOString(),
106
+ expiresAt: quote.expiresAt,
107
+ raw: sanitizedRaw,
108
+ };
109
+ }
110
+ async createOffRamp(params) {
111
+ if (params.sourceWallet) {
112
+ (0, base_1.validateWalletAddress)(params.sourceWallet, params.network);
113
+ }
114
+ // Get real quote instead of synthetic calculation
115
+ // TODO(verify): Confirm Yellow Card supports off-ramp quotes via /v1/quotes/sell
116
+ // If the API doesn't support quotes for sell orders, this will fail and we need to handle it
117
+ const quote = await this.getQuote('off', params.cryptoAmount * 50000, // Estimate fiat for quote
118
+ params.fiatCurrency, params.asset, params.network);
119
+ const requestBody = {
120
+ cryptoCurrency: params.asset.toUpperCase(),
121
+ fiatCurrency: params.fiatCurrency.toUpperCase(),
122
+ cryptoAmount: params.cryptoAmount,
123
+ network: params.network.toUpperCase(),
124
+ bankAccount: {
125
+ accountNumber: params.bankAccount.accountNumber,
126
+ bankCode: params.bankAccount.branchCode,
127
+ accountName: params.bankAccount.accountHolder,
128
+ bankName: params.bankAccount.bankName,
129
+ },
130
+ customer: {
131
+ email: params.customer.email,
132
+ name: params.customer.name,
133
+ phone: params.customer.phone,
134
+ },
135
+ externalReference: params.reference,
136
+ };
137
+ const response = await this.apiRequest('POST', '/v1/orders/sell', requestBody);
138
+ const sanitizedRaw = { ...response };
139
+ if (sanitizedRaw.bankAccount)
140
+ delete sanitizedRaw.bankAccount;
141
+ if (sanitizedRaw.bank_account)
142
+ delete sanitizedRaw.bank_account;
143
+ return {
144
+ id: response.id || `yc_off_${params.reference}`,
145
+ direction: 'off',
146
+ status: this.mapYellowCardStatus(response.status),
147
+ quote,
148
+ depositAddress: response.depositAddress,
149
+ depositTag: response.memo || response.tag,
150
+ createdAt: response.createdAt || new Date().toISOString(),
151
+ expiresAt: quote.expiresAt,
152
+ raw: sanitizedRaw,
153
+ };
154
+ }
155
+ async getRamp(id) {
156
+ const response = await this.apiRequest('GET', `/v1/orders/${id}`);
157
+ const direction = response.type === 'buy' ? 'on' : 'off';
158
+ const status = this.mapYellowCardStatus(response.status);
159
+ const quote = {
160
+ fiatAmount: response.fiatAmount || 0,
161
+ cryptoAmount: response.cryptoAmount || 0,
162
+ rate: response.rate || 0,
163
+ feeFixed: 0,
164
+ feePercent: 0,
165
+ feeTotal: response.fee || 0,
166
+ expiresAt: response.expiresAt || new Date().toISOString(),
167
+ };
168
+ const sanitizedRaw = { ...response };
169
+ if (sanitizedRaw.bankAccount)
170
+ delete sanitizedRaw.bankAccount;
171
+ if (sanitizedRaw.bank_account)
172
+ delete sanitizedRaw.bank_account;
173
+ return {
174
+ id: response.id,
175
+ direction,
176
+ status,
177
+ quote,
178
+ txHash: response.txHash || response.transactionHash,
179
+ createdAt: response.createdAt || new Date().toISOString(),
180
+ raw: sanitizedRaw,
181
+ };
182
+ }
183
+ parseWebhook(body, _headers) {
184
+ const event = typeof body === 'string' ? JSON.parse(body) : body;
185
+ return {
186
+ type: event.eventType || event.type,
187
+ orderId: event.orderId || event.id,
188
+ status: event.status,
189
+ data: event,
190
+ raw: event,
191
+ };
192
+ }
193
+ verifyWebhook(body, headers) {
194
+ if (!this.webhookSecret)
195
+ return false;
196
+ // TODO(verify): Confirm Yellow Card webhook signature scheme and header names
197
+ // Current implementation: HMAC-SHA256(webhookSecret, body) in hex format
198
+ // Common alternatives: timestamp-based scheme like HMAC-SHA256(secret, timestamp.body)
199
+ // Header name might be X-YC-Signature, X-Signature, or x-yellowcard-signature
200
+ const signature = headers?.['x-yellowcard-signature'] || headers?.['x-signature'] || headers?.signature;
201
+ if (!signature)
202
+ return false;
203
+ // Check if this is a timestamp-based signature (format: "t=timestamp,s=signature")
204
+ const timestampMatch = signature.match(/^t=(\d+),s=([a-f0-9]+)$/);
205
+ if (timestampMatch) {
206
+ const timestamp = parseInt(timestampMatch[1], 10);
207
+ const sig = timestampMatch[2];
208
+ // Replay protection: reject timestamps older than 5 minutes
209
+ const now = Math.floor(Date.now() / 1000);
210
+ if (Math.abs(now - timestamp) > 300) {
211
+ return false;
212
+ }
213
+ const payload = `${timestamp}.${body}`;
214
+ const expectedSignature = crypto_1.default
215
+ .createHmac('sha256', this.webhookSecret)
216
+ .update(payload)
217
+ .digest('hex');
218
+ const sigBuf = Buffer.from(sig);
219
+ const expBuf = Buffer.from(expectedSignature);
220
+ if (sigBuf.length !== expBuf.length)
221
+ return false;
222
+ return crypto_1.default.timingSafeEqual(sigBuf, expBuf);
223
+ }
224
+ // Fallback: simple HMAC-SHA256(body) verification
225
+ const expectedSignature = crypto_1.default
226
+ .createHmac('sha256', this.webhookSecret)
227
+ .update(body)
228
+ .digest('hex');
229
+ const sigBuf = Buffer.from(signature);
230
+ const expBuf = Buffer.from(expectedSignature);
231
+ if (sigBuf.length !== expBuf.length)
232
+ return false;
233
+ return crypto_1.default.timingSafeEqual(sigBuf, expBuf);
234
+ }
235
+ getCapabilities() {
236
+ return {
237
+ supportedAssets: ['BTC', 'ETH', 'USDT', 'USDC'],
238
+ supportedNetworks: ['BTC', 'ETH', 'TRON', 'POLYGON'],
239
+ supportedFiat: ['ZAR', 'NGN', 'KES', 'UGX', 'GHS'],
240
+ country: 'AFRICA',
241
+ kycRequired: true,
242
+ onRampLimits: {
243
+ min: 10,
244
+ max: 100000,
245
+ },
246
+ offRampLimits: {
247
+ min: 20,
248
+ max: 100000,
249
+ },
250
+ fees: {
251
+ onRampPercent: 3.5,
252
+ offRampPercent: 2.0,
253
+ },
254
+ experimental: true,
255
+ };
256
+ }
257
+ async apiRequest(method, path, body) {
258
+ const timestamp = Date.now().toString();
259
+ const bodyString = body ? JSON.stringify(body) : '';
260
+ const signature = this.generateSignature(method, path, timestamp, bodyString);
261
+ const url = `${this.baseUrl}${path}`;
262
+ // TODO(verify): Confirm Yellow Card API header names
263
+ // Current: X-API-Key, X-Timestamp, X-Signature
264
+ // Common alternatives: X-YC-API-Key, X-YC-Timestamp, X-YC-Signature
265
+ // Or: Authorization header + separate X-Signature
266
+ const response = await fetch(url, {
267
+ method,
268
+ headers: {
269
+ 'Content-Type': 'application/json',
270
+ 'X-API-Key': this.apiKey,
271
+ 'X-Timestamp': timestamp,
272
+ 'X-Signature': signature,
273
+ },
274
+ body: body ? bodyString : undefined,
275
+ });
276
+ if (!response.ok) {
277
+ const errorText = await response.text();
278
+ throw new Error(`Yellow Card API error (${method} ${path}): ${response.status} - ${errorText}`);
279
+ }
280
+ return await response.json();
281
+ }
282
+ generateSignature(method, path, timestamp, body) {
283
+ // TODO(verify): Confirm Yellow Card signature algorithm
284
+ // Current: HMAC-SHA256(secret, method+path+timestamp+body) -> hex
285
+ // Common alternative patterns:
286
+ // 1. HMAC-SHA256(secret, path+base64(SHA256(body))+timestamp+method) -> base64
287
+ // 2. HMAC-SHA256(secret, timestamp+method+path+body) -> hex
288
+ // 3. HMAC-SHA256(secret, method+path+timestamp+SHA256(body)) -> base64
289
+ // Without official docs/SDK, current implementation is speculative
290
+ const message = `${method}${path}${timestamp}${body}`;
291
+ return crypto_1.default
292
+ .createHmac('sha256', this.secretKey)
293
+ .update(message)
294
+ .digest('hex');
295
+ }
296
+ mapYellowCardStatus(ycStatus) {
297
+ const statusMap = {
298
+ pending: 'pending',
299
+ processing: 'pending',
300
+ completed: 'completed',
301
+ success: 'completed',
302
+ failed: 'failed',
303
+ cancelled: 'failed',
304
+ expired: 'expired',
305
+ };
306
+ return statusMap[ycStatus?.toLowerCase()] || 'pending';
307
+ }
308
+ }
309
+ exports.YellowCardProvider = YellowCardProvider;
310
+ YellowCardProvider.warnedExperimental = false;
package/dist/index.d.ts CHANGED
@@ -4,11 +4,19 @@
4
4
  *
5
5
  * @see https://github.com/kobie3717/paybridge
6
6
  */
7
+ import { PaymentProvider } from './providers/base';
7
8
  import { PayBridgeConfig, CreatePaymentParams, PaymentResult, CreateSubscriptionParams, SubscriptionResult, RefundParams, RefundResult, WebhookEvent } from './types';
8
9
  export * from './types';
9
10
  export * from './utils/currency';
11
+ export * from './routing-types';
12
+ export * from './circuit-breaker';
13
+ export * from './circuit-breaker-store';
14
+ export * from './strategies';
15
+ export * from './router';
16
+ export * from './crypto';
17
+ export { createRedisCircuitBreakerStore, type RedisLike, type RedisStoreOptions } from './stores/redis';
10
18
  export declare class PayBridge {
11
- private provider;
19
+ readonly provider: PaymentProvider;
12
20
  constructor(config: PayBridgeConfig);
13
21
  /**
14
22
  * Create provider instance based on config
package/dist/index.js CHANGED
@@ -20,12 +20,25 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
20
20
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
21
21
  };
22
22
  Object.defineProperty(exports, "__esModule", { value: true });
23
- exports.PayBridge = void 0;
23
+ exports.PayBridge = exports.createRedisCircuitBreakerStore = void 0;
24
24
  const softycomp_1 = require("./providers/softycomp");
25
25
  const yoco_1 = require("./providers/yoco");
26
26
  const ozow_1 = require("./providers/ozow");
27
+ const peach_1 = require("./providers/peach");
28
+ const stripe_1 = require("./providers/stripe");
29
+ const payfast_1 = require("./providers/payfast");
30
+ const paystack_1 = require("./providers/paystack");
31
+ const flutterwave_1 = require("./providers/flutterwave");
27
32
  __exportStar(require("./types"), exports);
28
33
  __exportStar(require("./utils/currency"), exports);
34
+ __exportStar(require("./routing-types"), exports);
35
+ __exportStar(require("./circuit-breaker"), exports);
36
+ __exportStar(require("./circuit-breaker-store"), exports);
37
+ __exportStar(require("./strategies"), exports);
38
+ __exportStar(require("./router"), exports);
39
+ __exportStar(require("./crypto"), exports);
40
+ var redis_1 = require("./stores/redis");
41
+ Object.defineProperty(exports, "createRedisCircuitBreakerStore", { enumerable: true, get: function () { return redis_1.createRedisCircuitBreakerStore; } });
29
42
  class PayBridge {
30
43
  constructor(config) {
31
44
  this.provider = this.createProvider(config);
@@ -65,11 +78,54 @@ class PayBridge {
65
78
  privateKey: credentials.privateKey,
66
79
  sandbox,
67
80
  });
81
+ case 'stripe':
82
+ if (!credentials.apiKey) {
83
+ throw new Error('Stripe requires apiKey (secret key, sk_test_* or sk_live_*)');
84
+ }
85
+ return new stripe_1.StripeProvider({
86
+ apiKey: credentials.apiKey,
87
+ webhookSecret,
88
+ sandbox,
89
+ });
68
90
  case 'payfast':
91
+ if (!credentials.merchantId || !credentials.merchantKey) {
92
+ throw new Error('PayFast requires merchantId and merchantKey');
93
+ }
94
+ return new payfast_1.PayFastProvider({
95
+ merchantId: credentials.merchantId,
96
+ merchantKey: credentials.merchantKey,
97
+ passphrase: credentials.passphrase,
98
+ sandbox,
99
+ webhookSecret,
100
+ });
69
101
  case 'paystack':
70
- case 'stripe':
102
+ if (!credentials.apiKey) {
103
+ throw new Error('PayStack requires apiKey (secret key, sk_test_* or sk_live_*)');
104
+ }
105
+ return new paystack_1.PayStackProvider({
106
+ apiKey: credentials.apiKey,
107
+ sandbox,
108
+ webhookSecret,
109
+ });
71
110
  case 'peach':
72
- throw new Error(`Provider ${provider} not yet implemented. Coming soon!`);
111
+ if (!credentials.apiKey || !credentials.secretKey) {
112
+ throw new Error('Peach Payments requires apiKey (access token) and secretKey (entityId)');
113
+ }
114
+ return new peach_1.PeachProvider({
115
+ accessToken: credentials.apiKey,
116
+ entityId: credentials.secretKey,
117
+ sandbox,
118
+ webhookSecret,
119
+ });
120
+ case 'flutterwave':
121
+ if (!credentials.apiKey) {
122
+ throw new Error('Flutterwave requires apiKey (FLWSECK_TEST-* or FLWSECK-*)');
123
+ }
124
+ return new flutterwave_1.FlutterwaveProvider({
125
+ apiKey: credentials.apiKey,
126
+ sandbox,
127
+ webhookSecret,
128
+ });
73
129
  default:
74
130
  throw new Error(`Unknown provider: ${provider}`);
75
131
  }
@@ -3,6 +3,7 @@
3
3
  * All payment providers must extend this class
4
4
  */
5
5
  import { CreatePaymentParams, PaymentResult, CreateSubscriptionParams, SubscriptionResult, RefundParams, RefundResult, WebhookEvent } from '../types';
6
+ import { ProviderCapabilities } from '../routing-types';
6
7
  export declare abstract class PaymentProvider {
7
8
  /**
8
9
  * Provider name (e.g. 'softycomp', 'yoco', 'ozow')
@@ -37,6 +38,10 @@ export declare abstract class PaymentProvider {
37
38
  * @returns true if signature is valid, false otherwise
38
39
  */
39
40
  abstract verifyWebhook(body: any, headers?: any): boolean;
41
+ /**
42
+ * Get provider capabilities (fees, limits, etc)
43
+ */
44
+ abstract getCapabilities(): ProviderCapabilities;
40
45
  /**
41
46
  * Validate currency is supported
42
47
  */
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Flutterwave payment provider
3
+ * Leading payment gateway for Africa with global reach
4
+ * @see https://developer.flutterwave.com/docs
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 FlutterwaveConfig {
10
+ apiKey: string;
11
+ webhookSecret?: string;
12
+ sandbox?: boolean;
13
+ }
14
+ export declare class FlutterwaveProvider extends PaymentProvider {
15
+ readonly name = "flutterwave";
16
+ readonly supportedCurrencies: string[];
17
+ private apiKey;
18
+ private webhookSecret?;
19
+ private sandbox;
20
+ private baseUrl;
21
+ constructor(config: FlutterwaveConfig);
22
+ private apiRequest;
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
+ /**
29
+ * Verify webhook signature using Flutterwave's scheme.
30
+ * Flutterwave sends the configured webhook secret hash as the verif-hash header.
31
+ * This is a simple equality check, not HMAC.
32
+ */
33
+ verifyWebhook(body: any, headers?: any): boolean;
34
+ getCapabilities(): ProviderCapabilities;
35
+ }
36
+ export {};