@kuvarpay/sdk 1.6.0 → 1.7.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/README.md +5 -5
- package/index.d.ts +14 -2
- package/index.js +32 -17
- package/index.mjs +30 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -95,7 +95,7 @@ The SDK defaults to the KuvarPay Production API (`https://payment.kuvarpay.com`)
|
|
|
95
95
|
|
|
96
96
|
| Method | Description |
|
|
97
97
|
| :--- | :--- |
|
|
98
|
-
| `getBanks({ country? \| currency?, type? })` | List a corridor's **primary-rail** payment methods (banks / mobile money). Pass a country (ISO-2) or currency (ISO-4217). |
|
|
98
|
+
| `getBanks({ country? \| currency?, type?, direction?, sandbox? })` | List a corridor's **primary-rail** payment methods (banks / mobile money). Pass a country (ISO-2) or currency (ISO-4217). Set `sandbox: true` when your key is a sandbox-mode business — live and sandbox are separate `methodId` spaces. |
|
|
99
99
|
| `resolveBankAccount(data)` | **Deprecated** alias for `resolveRecipient` — takes `methodId`, not `bank_code`. |
|
|
100
100
|
|
|
101
101
|
### 🌍 Fiat Collection (Pay with Local Currency)
|
|
@@ -104,7 +104,7 @@ The SDK defaults to the KuvarPay Production API (`https://payment.kuvarpay.com`)
|
|
|
104
104
|
| :--- | :--- |
|
|
105
105
|
| **`createFiatPayment(data)`** | **One-shot**: checkout session + fiat transaction + wait for the deposit. Returns `{ sessionId, transactionId, depositId, nextAction, quote, paymentInstructions }`. |
|
|
106
106
|
| `getFiatCountries()` | Supported corridors. `code` is a **currency**; use **`countryCode`** (ISO-2) for any `country` param, and filter on `depositAvailable`. |
|
|
107
|
-
| `getFiatMethods(
|
|
107
|
+
| `getFiatMethods(...)` | **Deprecated** alias for `getBanks` — same endpoint, same params. |
|
|
108
108
|
| `getFiatCheckoutQuote({ amount, baseCurrency, currency, country })` | Preview the local amount a customer pays. |
|
|
109
109
|
| `createFiatTransaction(data)` | Create a fiat collection on a checkout session. |
|
|
110
110
|
| `waitForFiatDeposit(sessionId, options?)` | Bounded poll until the deposit attaches. Rejects with `FIAT_DEPOSIT_TIMEOUT`. |
|
|
@@ -117,7 +117,7 @@ the customer's payment prompt, and the gateway withholds `paymentInstructions`
|
|
|
117
117
|
until then. Show `quote`, confirm, then show instructions.
|
|
118
118
|
|
|
119
119
|
```javascript
|
|
120
|
-
const { methods } = await kv.
|
|
120
|
+
const { methods } = await kv.getBanks({ country: 'KE', type: 'mobile_money' });
|
|
121
121
|
|
|
122
122
|
const payment = await kv.createFiatPayment({
|
|
123
123
|
amount: 5000, currency: 'NGN', // you settle in NGN
|
|
@@ -172,14 +172,14 @@ Payouts move money **out** of your USD balance to a recipient's bank or mobile-m
|
|
|
172
172
|
|
|
173
173
|
| Method | Description |
|
|
174
174
|
| :--- | :--- |
|
|
175
|
-
| `
|
|
175
|
+
| `getBanks({ country, type?, direction: 'outbound' })` | List payout (outbound) banks / mobile-money operators. |
|
|
176
176
|
| `resolveRecipient({ methodId, accountNumber?, phone? })` | Name-enquiry on a payout recipient before sending. |
|
|
177
177
|
| `createFiatSend(data)` | Initiate a bank / mobile-money payout (USD-debited). |
|
|
178
178
|
| `getFiatSend(sendId)` | Poll a payout's status (`PROCESSING` → `COMPLETED`/`FAILED`/`REFUNDED`). |
|
|
179
179
|
|
|
180
180
|
```javascript
|
|
181
181
|
// Discover payout methods, then verify the recipient name before sending.
|
|
182
|
-
const { methods } = await kv.
|
|
182
|
+
const { methods } = await kv.getBanks({ country: 'NG', type: 'bank', direction: 'outbound' });
|
|
183
183
|
const recipient = await kv.resolveRecipient({ methodId: methods[0].methodId, accountNumber: '0123456789' });
|
|
184
184
|
|
|
185
185
|
// Fiat payout: recipient receives exactly 50,000 NGN; USD debited from your balance.
|
package/index.d.ts
CHANGED
|
@@ -2,6 +2,12 @@ export interface KuvarPayServerConfig {
|
|
|
2
2
|
baseUrl?: string;
|
|
3
3
|
businessId?: string;
|
|
4
4
|
secretApiKey?: string;
|
|
5
|
+
/**
|
|
6
|
+
* Set true when the key belongs to a sandbox-mode business. Live and sandbox
|
|
7
|
+
* are separate `methodId` spaces, so `getBanks()` must be fetched in the same
|
|
8
|
+
* mode the key resolves/pays out in. Defaults to `KUVARPAY_SANDBOX=true`.
|
|
9
|
+
*/
|
|
10
|
+
sandbox?: boolean;
|
|
5
11
|
timeoutMs?: number;
|
|
6
12
|
fetchImpl?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
7
13
|
}
|
|
@@ -234,8 +240,13 @@ export class KuvarPayServer {
|
|
|
234
240
|
renewSubscription(subscriptionId: string, body?: Record<string, any>): Promise<any>;
|
|
235
241
|
|
|
236
242
|
// Banks
|
|
237
|
-
getBanks(currency: string, options?: { type?: 'bank' | 'mobile_money'; direction?: 'inbound' | 'outbound' }): Promise<{ methods: Array<{ methodId: string; name: string; currency: string; type: 'bank' | 'mobile_money'; manualInput?: boolean; minAmount?: number | null; maxAmount?: number | null }> }>;
|
|
238
|
-
|
|
243
|
+
getBanks(currency: string, options?: { type?: 'bank' | 'mobile_money'; direction?: 'inbound' | 'outbound'; sandbox?: boolean }): Promise<{ methods: Array<{ methodId: string; name: string; currency: string; type: 'bank' | 'mobile_money'; manualInput?: boolean; minAmount?: number | null; maxAmount?: number | null }> }>;
|
|
244
|
+
/**
|
|
245
|
+
* `sandbox` must match the mode of the business your key belongs to — live and
|
|
246
|
+
* sandbox are separate methodId spaces. Defaults to the client-level `sandbox`
|
|
247
|
+
* option. Using the wrong one yields `reason: 'METHOD_MODE_MISMATCH'` on resolve.
|
|
248
|
+
*/
|
|
249
|
+
getBanks(params: { country?: string; currency?: string; type?: 'bank' | 'mobile_money'; direction?: 'inbound' | 'outbound'; sandbox?: boolean }): Promise<{ methods: Array<{ methodId: string; name: string; currency: string; type: 'bank' | 'mobile_money'; manualInput?: boolean; minAmount?: number | null; maxAmount?: number | null }> }>;
|
|
239
250
|
/**
|
|
240
251
|
* @deprecated Use `resolveRecipient`. Thin alias over
|
|
241
252
|
* `POST /api/v1/fiat/resolve-recipient`.
|
|
@@ -276,6 +287,7 @@ export class KuvarPayServer {
|
|
|
276
287
|
* which is a CURRENCY — pass it as `currency`, or as `country` and the SDK
|
|
277
288
|
* will route 3-letter codes correctly.
|
|
278
289
|
*/
|
|
290
|
+
/** @deprecated Alias for `getBanks`, which it delegates to. */
|
|
279
291
|
getFiatMethods(params: { country?: string; currency?: string; type?: 'bank' | 'mobile_money'; sandbox?: boolean; direction?: 'inbound' | 'outbound' }): Promise<{ methods: Array<{ methodId: string; name: string; currency: string; type: 'bank' | 'mobile_money'; manualInput?: boolean; minAmount?: number | null; maxAmount?: number | null }> }>;
|
|
280
292
|
getFiatCheckoutQuote(params: { amount: number; baseCurrency: string; currency: string; country: string }): Promise<any>;
|
|
281
293
|
createFiatTransaction(data: {
|
package/index.js
CHANGED
|
@@ -64,10 +64,16 @@ class KuvarPayServer {
|
|
|
64
64
|
* @param {number} [config.timeoutMs] - Request timeout in ms (default 60000)
|
|
65
65
|
* @param {Function} [config.fetchImpl] - Optional fetch implementation for Node < 18
|
|
66
66
|
*/
|
|
67
|
-
constructor({ baseUrl, businessId, secretApiKey, timeoutMs, fetchImpl } = {}) {
|
|
67
|
+
constructor({ baseUrl, businessId, secretApiKey, timeoutMs, fetchImpl, sandbox } = {}) {
|
|
68
68
|
this.baseUrl = (baseUrl || DEFAULT_BASE).replace(/\/+$/, '');
|
|
69
69
|
this.businessId = businessId || process.env.KUVARPAY_BUSINESS_ID;
|
|
70
70
|
this.secretApiKey = secretApiKey || process.env.KUVARPAY_SECRET_API_KEY || process.env.PAYMENT_SECRET_API_KEY;
|
|
71
|
+
// Set this to true when the key belongs to a sandbox-mode business, so
|
|
72
|
+
// getBanks() returns sandbox methodIds. Mixing the two spaces produces
|
|
73
|
+
// METHOD_MODE_MISMATCH on resolve and payout.
|
|
74
|
+
this.sandbox = sandbox != null
|
|
75
|
+
? !!sandbox
|
|
76
|
+
: String(process.env.KUVARPAY_SANDBOX || '').toLowerCase() === 'true';
|
|
71
77
|
this.timeoutMs = timeoutMs || 60000;
|
|
72
78
|
this.fetchImpl = fetchImpl || (typeof fetch !== 'undefined' ? fetch.bind(globalThis) : null);
|
|
73
79
|
|
|
@@ -540,11 +546,27 @@ class KuvarPayServer {
|
|
|
540
546
|
// (settlement) methods.
|
|
541
547
|
const { country, currency, type, direction = 'inbound' } = params || {};
|
|
542
548
|
if (!country && !currency) throw new Error('country or currency is required');
|
|
549
|
+
// Live and sandbox are SEPARATE methodId spaces — the same bank has a
|
|
550
|
+
// different id in each. A methodId from the wrong space is rejected
|
|
551
|
+
// downstream with reason METHOD_MODE_MISMATCH, so the list must be fetched
|
|
552
|
+
// in the same mode the key's business is in. Defaults to the client-level
|
|
553
|
+
// `sandbox` flag when the call doesn't override it.
|
|
554
|
+
const sandbox = params.sandbox != null ? params.sandbox : this.sandbox;
|
|
555
|
+
|
|
543
556
|
const qs = new URLSearchParams();
|
|
544
|
-
if (
|
|
545
|
-
if (
|
|
557
|
+
if (currency) qs.set('currency', String(currency).toUpperCase());
|
|
558
|
+
if (country) {
|
|
559
|
+
// A country and a currency go in DIFFERENT query params, and
|
|
560
|
+
// getFiatCountries() returns its identifier as `code` — which is a
|
|
561
|
+
// CURRENCY. Passing that straight through as `country=KES` silently
|
|
562
|
+
// returns { methods: [] }. 2 chars -> country, 3 chars -> currency.
|
|
563
|
+
const code = String(country).toUpperCase();
|
|
564
|
+
if (!currency && code.length === 3) qs.set('currency', code);
|
|
565
|
+
else qs.set('country', code);
|
|
566
|
+
}
|
|
546
567
|
if (type) qs.set('type', type);
|
|
547
568
|
if (direction) qs.set('direction', direction);
|
|
569
|
+
if (sandbox) qs.set('sandbox', 'true');
|
|
548
570
|
return this._get(`/api/v1/fiat/banks?${qs.toString()}`);
|
|
549
571
|
}
|
|
550
572
|
|
|
@@ -606,21 +628,14 @@ class KuvarPayServer {
|
|
|
606
628
|
*
|
|
607
629
|
* @param {Object} params - { country?, currency?, type?, sandbox?, direction? }
|
|
608
630
|
*/
|
|
631
|
+
/**
|
|
632
|
+
* @deprecated Use `getBanks`, which this now delegates to. Both hit
|
|
633
|
+
* `GET /api/v1/fiat/banks`; having two names for one endpoint is how the
|
|
634
|
+
* missing `sandbox` support on `getBanks` went unnoticed. Kept as an alias so
|
|
635
|
+
* existing integrations keep working.
|
|
636
|
+
*/
|
|
609
637
|
async getFiatMethods(params = {}) {
|
|
610
|
-
|
|
611
|
-
if (!country && !currency) throw new Error('country or currency is required');
|
|
612
|
-
|
|
613
|
-
const qs = new URLSearchParams({ direction });
|
|
614
|
-
if (currency) qs.set('currency', String(currency).toUpperCase());
|
|
615
|
-
if (country) {
|
|
616
|
-
const code = String(country).toUpperCase();
|
|
617
|
-
// A 3-letter code is a currency, not a country.
|
|
618
|
-
if (!currency && code.length === 3) qs.set('currency', code);
|
|
619
|
-
else qs.set('country', code);
|
|
620
|
-
}
|
|
621
|
-
if (type) qs.set('type', type);
|
|
622
|
-
if (sandbox) qs.set('sandbox', 'true');
|
|
623
|
-
return this._get(`/api/v1/fiat/banks?${qs.toString()}`);
|
|
638
|
+
return this.getBanks(params);
|
|
624
639
|
}
|
|
625
640
|
|
|
626
641
|
/**
|
package/index.mjs
CHANGED
|
@@ -61,10 +61,16 @@ export class KuvarPayServer {
|
|
|
61
61
|
* @param {number} [config.timeoutMs] - Request timeout in ms (default 60000)
|
|
62
62
|
* @param {Function} [config.fetchImpl] - Optional fetch implementation for Node < 18
|
|
63
63
|
*/
|
|
64
|
-
constructor({ baseUrl, businessId, secretApiKey, timeoutMs, fetchImpl } = {}) {
|
|
64
|
+
constructor({ baseUrl, businessId, secretApiKey, timeoutMs, fetchImpl, sandbox } = {}) {
|
|
65
65
|
this.baseUrl = (baseUrl || DEFAULT_BASE).replace(/\/+$/, '');
|
|
66
66
|
this.businessId = businessId || process.env.KUVARPAY_BUSINESS_ID;
|
|
67
67
|
this.secretApiKey = secretApiKey || process.env.KUVARPAY_SECRET_API_KEY || process.env.PAYMENT_SECRET_API_KEY;
|
|
68
|
+
// Set this to true when the key belongs to a sandbox-mode business, so
|
|
69
|
+
// getBanks() returns sandbox methodIds. Mixing the two spaces produces
|
|
70
|
+
// METHOD_MODE_MISMATCH on resolve and payout.
|
|
71
|
+
this.sandbox = sandbox != null
|
|
72
|
+
? !!sandbox
|
|
73
|
+
: String(process.env.KUVARPAY_SANDBOX || '').toLowerCase() === 'true';
|
|
68
74
|
this.timeoutMs = timeoutMs || 60000;
|
|
69
75
|
this.fetchImpl = fetchImpl || (typeof fetch !== 'undefined' ? fetch.bind(globalThis) : null);
|
|
70
76
|
|
|
@@ -428,11 +434,27 @@ export class KuvarPayServer {
|
|
|
428
434
|
// (settlement) methods.
|
|
429
435
|
const { country, currency, type, direction = 'inbound' } = params || {};
|
|
430
436
|
if (!country && !currency) throw new Error('country or currency is required');
|
|
437
|
+
// Live and sandbox are SEPARATE methodId spaces — the same bank has a
|
|
438
|
+
// different id in each. A methodId from the wrong space is rejected
|
|
439
|
+
// downstream with reason METHOD_MODE_MISMATCH, so the list must be fetched
|
|
440
|
+
// in the same mode the key's business is in. Defaults to the client-level
|
|
441
|
+
// `sandbox` flag when the call doesn't override it.
|
|
442
|
+
const sandbox = params.sandbox != null ? params.sandbox : this.sandbox;
|
|
443
|
+
|
|
431
444
|
const qs = new URLSearchParams();
|
|
432
|
-
if (
|
|
433
|
-
if (
|
|
445
|
+
if (currency) qs.set('currency', String(currency).toUpperCase());
|
|
446
|
+
if (country) {
|
|
447
|
+
// A country and a currency go in DIFFERENT query params, and
|
|
448
|
+
// getFiatCountries() returns its identifier as `code` — which is a
|
|
449
|
+
// CURRENCY. Passing that straight through as `country=KES` silently
|
|
450
|
+
// returns { methods: [] }. 2 chars → country, 3 chars → currency.
|
|
451
|
+
const code = String(country).toUpperCase();
|
|
452
|
+
if (!currency && code.length === 3) qs.set('currency', code);
|
|
453
|
+
else qs.set('country', code);
|
|
454
|
+
}
|
|
434
455
|
if (type) qs.set('type', type);
|
|
435
456
|
if (direction) qs.set('direction', direction);
|
|
457
|
+
if (sandbox) qs.set('sandbox', 'true');
|
|
436
458
|
return this._get(`/api/v1/fiat/banks?${qs.toString()}`);
|
|
437
459
|
}
|
|
438
460
|
|
|
@@ -480,28 +502,14 @@ export class KuvarPayServer {
|
|
|
480
502
|
return normalizeFiatCountries(data);
|
|
481
503
|
}
|
|
482
504
|
|
|
483
|
-
/** Payment methods (banks / MoMo operators) for a country. */
|
|
484
505
|
/**
|
|
485
|
-
*
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
* silently returned `{ methods: [] }`. 2 chars → country, 3 chars → currency.
|
|
506
|
+
* @deprecated Use {@link getBanks}, which this now delegates to. Both hit
|
|
507
|
+
* `GET /api/v1/fiat/banks`; having two names for one endpoint is how the
|
|
508
|
+
* missing `sandbox` support on `getBanks` went unnoticed. Kept as an alias so
|
|
509
|
+
* existing integrations keep working.
|
|
490
510
|
*/
|
|
491
511
|
async getFiatMethods(params = {}) {
|
|
492
|
-
|
|
493
|
-
if (!country && !currency) throw new Error('country or currency is required');
|
|
494
|
-
|
|
495
|
-
const qs = new URLSearchParams({ direction });
|
|
496
|
-
if (currency) qs.set('currency', String(currency).toUpperCase());
|
|
497
|
-
if (country) {
|
|
498
|
-
const code = String(country).toUpperCase();
|
|
499
|
-
if (!currency && code.length === 3) qs.set('currency', code);
|
|
500
|
-
else qs.set('country', code);
|
|
501
|
-
}
|
|
502
|
-
if (type) qs.set('type', type);
|
|
503
|
-
if (sandbox) qs.set('sandbox', 'true');
|
|
504
|
-
return this._get(`/api/v1/fiat/banks?${qs.toString()}`);
|
|
512
|
+
return this.getBanks(params);
|
|
505
513
|
}
|
|
506
514
|
|
|
507
515
|
/** Preview the local amount a customer must pay to settle a checkout amount. */
|