@kuvarpay/sdk 1.2.2 → 1.3.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.
- package/README.md +96 -129
- package/index.js +13 -4
- package/index.mjs +12 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,162 +1,129 @@
|
|
|
1
1
|
# KuvarPay Server SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Official Node.js SDK for integrating **KuvarPay**, the ultimate fiat gateway for the crypto economy. This SDK allows you to handle checkout sessions, direct crypto-to-fiat transactions, recurring subscriptions, and split payments with ease.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://badge.fury.io/js/@kuvarpay%2Fsdk)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
7
|
|
|
7
|
-
## Installation
|
|
8
|
+
## 🚀 Installation
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
```bash
|
|
11
|
+
npm install @kuvarpay/sdk
|
|
12
|
+
```
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
> - Business ID
|
|
13
|
-
> - SECRET API Key (server-only)
|
|
14
|
-
> - Payment API Base URL (e.g., `https://pay.kuvarpay.com` or your payment host)
|
|
14
|
+
## 🛠️ Quick Start (Speed Mode)
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Initiate a payment and get a crypto deposit address in one line of code:
|
|
17
17
|
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
const { KuvarPayServer } = require('./server-sdk/index.js');
|
|
18
|
+
```javascript
|
|
19
|
+
const { KuvarPayServer } = require('@kuvarpay/sdk');
|
|
21
20
|
|
|
22
21
|
const kv = new KuvarPayServer({
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
secretApiKey: process.env.KUVARPAY_SECRET_API_KEY,
|
|
22
|
+
businessId: 'your_business_id',
|
|
23
|
+
secretApiKey: 'rsp_secret_...' // Starts with rsp_secret_
|
|
26
24
|
});
|
|
27
25
|
|
|
28
|
-
async function
|
|
29
|
-
const
|
|
30
|
-
|
|
26
|
+
async function startPayment() {
|
|
27
|
+
const payment = await kv.createDirectPayment({
|
|
28
|
+
amount: 5000,
|
|
29
|
+
currency: 'NGN',
|
|
30
|
+
fromCurrency: 'USDT',
|
|
31
|
+
fromNetwork: 'BSC',
|
|
32
|
+
customerEmail: 'customer@example.com'
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
console.log('Send USDT to:', payment.depositAddress);
|
|
36
|
+
console.log('Exact amount:', payment.fromAmount); // e.g. 3.76 USDT
|
|
31
37
|
}
|
|
38
|
+
```
|
|
32
39
|
|
|
33
|
-
|
|
34
|
-
const s = await kv.getSessionStatus(sessionId);
|
|
35
|
-
console.log('Session:', s);
|
|
36
|
-
}
|
|
40
|
+
---
|
|
37
41
|
|
|
38
|
-
|
|
39
|
-
const t = await kv.getTransactionStatus(transactionId);
|
|
40
|
-
console.log('Transaction:', t);
|
|
41
|
-
}
|
|
42
|
-
```
|
|
42
|
+
## ⚙️ Configuration
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
The SDK defaults to the KuvarPay Production API (`https://payment.kuvarpay.com`).
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
| Option | Description | Env Fallback |
|
|
47
|
+
| :--- | :--- | :--- |
|
|
48
|
+
| `businessId` | Your KuvarPay Business ID | - |
|
|
49
|
+
| `secretApiKey` | Your Production Secret Key | - |
|
|
50
|
+
| `baseUrl` | Overrides the API endpoint | `KUVARPAY_API_BASE_URL` |
|
|
51
|
+
| `timeoutMs` | Request timeout (default 60s) | - |
|
|
49
52
|
|
|
50
|
-
|
|
51
|
-
baseUrl: process.env.PAYMENT_API_BASE_URL,
|
|
52
|
-
businessId: process.env.KUVARPAY_BUSINESS_ID,
|
|
53
|
-
secretApiKey: process.env.KUVARPAY_SECRET_API_KEY,
|
|
54
|
-
});
|
|
53
|
+
---
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
console.log(result);
|
|
58
|
-
```
|
|
55
|
+
## 📖 API Reference
|
|
59
56
|
|
|
60
|
-
###
|
|
57
|
+
### 💳 Payments & Transactions
|
|
61
58
|
|
|
62
|
-
|
|
59
|
+
| Method | Description |
|
|
60
|
+
| :--- | :--- |
|
|
61
|
+
| `createDirectPayment(data)` | **Recommended**. Creates a session & transaction in one call. |
|
|
62
|
+
| `createCheckoutSession(data)` | Creates a checkout session (returns `approvalUrl`). |
|
|
63
|
+
| `createTransaction(data)` | Manually initiates a crypto transaction for an existing session. |
|
|
64
|
+
| `verifyPayment(sessionId)` | Convenience method to check if a payment is confirmed. |
|
|
65
|
+
| `getSessionStatus(sessionId)`| Fetches full session/transaction status. |
|
|
63
66
|
|
|
64
|
-
|
|
65
|
-
const fetch = (...args) => import('node-fetch').then(mod => mod.default(...args));
|
|
66
|
-
const kv = new KuvarPayServer({ baseUrl, businessId, secretApiKey, fetchImpl: fetch });
|
|
67
|
-
```
|
|
67
|
+
### 📈 Subaccounts & Split Payments
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
```php
|
|
76
|
-
$base = getenv('PAYMENT_API_BASE_URL');
|
|
77
|
-
$businessId = getenv('KUVARPAY_BUSINESS_ID');
|
|
78
|
-
$secret = getenv('KUVARPAY_SECRET_API_KEY');
|
|
79
|
-
$sessionId = 'sess_123';
|
|
80
|
-
|
|
81
|
-
$ch = curl_init("$base/api/v1/checkout-sessions/" . urlencode($sessionId));
|
|
82
|
-
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
83
|
-
"Accept: application/json",
|
|
84
|
-
"X-API-Key: $secret",
|
|
85
|
-
"X-Business-ID: $businessId"
|
|
86
|
-
]);
|
|
87
|
-
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
88
|
-
$resp = curl_exec($ch);
|
|
89
|
-
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
90
|
-
curl_close($ch);
|
|
91
|
-
|
|
92
|
-
if ($statusCode >= 200 && $statusCode < 300) {
|
|
93
|
-
$data = json_decode($resp, true);
|
|
94
|
-
$status = $data['status'] ?? ($data['data']['status'] ?? null);
|
|
95
|
-
$verified = ($status === 'COMPLETED');
|
|
96
|
-
// handle $verified
|
|
97
|
-
} else {
|
|
98
|
-
// handle error
|
|
99
|
-
}
|
|
100
|
-
```
|
|
69
|
+
| Method | Description |
|
|
70
|
+
| :--- | :--- |
|
|
71
|
+
| `createSubaccount(data)` | Create a vendor subaccount for split settlements. |
|
|
72
|
+
| `listSubaccounts()` | Fetch all subaccounts for your business. |
|
|
73
|
+
| `createSplitGroup(data)` | Define percentage-based distribution between subaccounts. |
|
|
101
74
|
|
|
102
|
-
###
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
} else {
|
|
122
|
-
// handle error
|
|
123
|
-
}
|
|
124
|
-
```
|
|
75
|
+
### 🔄 Subscriptions
|
|
76
|
+
|
|
77
|
+
| Method | Description |
|
|
78
|
+
| :--- | :--- |
|
|
79
|
+
| `createDirectSubscription(data)` | Initiates a subscription setup and returns an approval URL. |
|
|
80
|
+
| `renewSubscription(subId)` | Manually trigger a renewal for an active allowance. |
|
|
81
|
+
| `cancelSubscription(subId)` | Hard-cancel a customer subscription. |
|
|
82
|
+
| `createMeteredInvoice(subId, data)`| Charge a customer based on usage (Metered billing). |
|
|
83
|
+
|
|
84
|
+
### 🏦 Banks & Verification
|
|
85
|
+
|
|
86
|
+
| Method | Description |
|
|
87
|
+
| :--- | :--- |
|
|
88
|
+
| `getBanks(currency)` | List supported settlement banks for a specific currency. |
|
|
89
|
+
| `resolveBankAccount(data)` | Verify bank account names before creating subaccounts. |
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 🔒 Security: Webhook Verification
|
|
125
94
|
|
|
126
|
-
|
|
95
|
+
Verify that incoming webhooks are genuinely from KuvarPay using HMAC-SHA256:
|
|
127
96
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
- `verifyPayment(sessionId)` → `{ verified: boolean, status, sessionId, transactionId?, raw }`
|
|
97
|
+
```javascript
|
|
98
|
+
app.post('/webhooks/kuvarpay', (req, res) => {
|
|
99
|
+
const signature = req.headers['x-kuvarpay-signature'];
|
|
100
|
+
const payload = req.body; // Raw body object
|
|
133
101
|
|
|
134
|
-
|
|
135
|
-
- `createSubscriptionCheckoutSession(data)` → `{ sessionId, approvalUrl?, raw }`
|
|
136
|
-
- `getSubscriptionCheckoutSession(id)` → full session details
|
|
137
|
-
- `confirmSubscriptionCheckoutSession(id, body?)` → confirmation response
|
|
138
|
-
- `getSubscription(subscriptionId)` → full subscription details
|
|
139
|
-
- `cancelSubscription(subscriptionId, body?)` → cancellation response
|
|
140
|
-
- `createMeteredInvoice(subscriptionId, invoiceData)` → invoice response
|
|
141
|
-
- `createSubscriptionInvoice(subscriptionId, invoiceData)` → alias to metered invoice
|
|
142
|
-
- `scheduleSubscriptionInvoice(subscriptionId, invoiceData)` → enforces `chargeSchedule: 'SCHEDULED'`
|
|
143
|
-
- `createRenewalCheckoutSession(body)` → renewal checkout creation
|
|
144
|
-
- `renewSubscription(subscriptionId, body?)` → direct renew trigger
|
|
102
|
+
const isValid = kv.verifyWebhookSignature(payload, signature);
|
|
145
103
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
104
|
+
if (isValid) {
|
|
105
|
+
// Process payment (e.g. status === 'completed')
|
|
106
|
+
res.status(200).send();
|
|
107
|
+
} else {
|
|
108
|
+
res.status(401).send('Invalid signature');
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
150
114
|
|
|
151
|
-
|
|
152
|
-
- Status values commonly include `PENDING`, `ARMED`, `PROCESSING`, `COMPLETED`, `FAILED`, `EXPIRED`, `CANCELLED`, etc.
|
|
153
|
-
- `verifyPayment` returns `verified = true` when `status === 'COMPLETED'`.
|
|
115
|
+
## 🧩 TypeScript Support
|
|
154
116
|
|
|
155
|
-
|
|
117
|
+
This SDK includes a full `index.d.ts` definition file. You get auto-completion and type checking out of the box.
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
import { KuvarPayServer } from '@kuvarpay/sdk';
|
|
121
|
+
|
|
122
|
+
const kv = new KuvarPayServer({ ... });
|
|
123
|
+
```
|
|
156
124
|
|
|
157
|
-
|
|
158
|
-
- Use HTTPS for all API calls.
|
|
125
|
+
---
|
|
159
126
|
|
|
160
|
-
##
|
|
127
|
+
## 📄 License
|
|
161
128
|
|
|
162
|
-
|
|
129
|
+
MIT © KuvarPay Development Team
|
package/index.js
CHANGED
|
@@ -389,9 +389,19 @@ class KuvarPayServer {
|
|
|
389
389
|
businessId: subscriptionData?.businessId || this.businessId,
|
|
390
390
|
}, subscriptionData);
|
|
391
391
|
const data = await this._post('/api/v1/subscriptions/checkout-sessions', payload);
|
|
392
|
+
const body = data.data || data.checkoutSession || data;
|
|
393
|
+
const approvalUrl = body.approvalUrl || body.approval_url || body.approvalUrl;
|
|
394
|
+
|
|
395
|
+
// Fallback: Extract ID from approval URL if missing (e.g., .../subscriptions/ID)
|
|
396
|
+
let sessionId = body.sessionId || body.id || body.uid;
|
|
397
|
+
if (!sessionId && approvalUrl) {
|
|
398
|
+
const parts = approvalUrl.split('/');
|
|
399
|
+
sessionId = parts[parts.length - 1];
|
|
400
|
+
}
|
|
401
|
+
|
|
392
402
|
return {
|
|
393
|
-
sessionId
|
|
394
|
-
approvalUrl
|
|
403
|
+
sessionId,
|
|
404
|
+
approvalUrl,
|
|
395
405
|
raw: data,
|
|
396
406
|
};
|
|
397
407
|
}
|
|
@@ -472,10 +482,9 @@ class KuvarPayServer {
|
|
|
472
482
|
*/
|
|
473
483
|
async createDirectSubscription(data) {
|
|
474
484
|
const payload = Object.assign({
|
|
475
|
-
billingMode: data.billingMode || 'FIXED',
|
|
485
|
+
billingMode: data.amount ? 'METERED' : (data.billingMode || 'FIXED'),
|
|
476
486
|
}, data);
|
|
477
487
|
|
|
478
|
-
// If amount/currency is provided but expectedUsage is not, map it
|
|
479
488
|
if (data.amount && data.currency && !data.expectedUsage) {
|
|
480
489
|
payload.expectedUsage = {
|
|
481
490
|
amount: data.amount,
|
package/index.mjs
CHANGED
|
@@ -274,9 +274,18 @@ export class KuvarPayServer {
|
|
|
274
274
|
businessId: subscriptionData?.businessId || this.businessId,
|
|
275
275
|
}, subscriptionData);
|
|
276
276
|
const data = await this._post('/api/v1/subscriptions/checkout-sessions', payload);
|
|
277
|
+
const body = data.data || data.checkoutSession || data;
|
|
278
|
+
const approvalUrl = body.approvalUrl || body.approval_url || body.approvalUrl;
|
|
279
|
+
|
|
280
|
+
let sessionId = body.sessionId || body.id || body.uid;
|
|
281
|
+
if (!sessionId && approvalUrl) {
|
|
282
|
+
const parts = approvalUrl.split('/');
|
|
283
|
+
sessionId = parts[parts.length - 1];
|
|
284
|
+
}
|
|
285
|
+
|
|
277
286
|
return {
|
|
278
|
-
sessionId
|
|
279
|
-
approvalUrl
|
|
287
|
+
sessionId,
|
|
288
|
+
approvalUrl,
|
|
280
289
|
raw: data,
|
|
281
290
|
};
|
|
282
291
|
}
|
|
@@ -328,7 +337,7 @@ export class KuvarPayServer {
|
|
|
328
337
|
|
|
329
338
|
async createDirectSubscription(data) {
|
|
330
339
|
const payload = Object.assign({
|
|
331
|
-
billingMode: data.billingMode || 'FIXED',
|
|
340
|
+
billingMode: data.amount ? 'METERED' : (data.billingMode || 'FIXED'),
|
|
332
341
|
}, data);
|
|
333
342
|
|
|
334
343
|
if (data.amount && data.currency && !data.expectedUsage) {
|