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