@patalink/node-api-client 1.0.3 → 1.0.5
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 +4 -4
- package/dist/index.d.mts +59 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +68 -78
- package/dist/index.mjs +68 -78
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ import { createClient } from '@patlink/node-api-client';
|
|
|
43
43
|
const client = createClient({
|
|
44
44
|
apiKey: 'pk_live_abc123...',
|
|
45
45
|
encryptionKey: 'xK8j3Hd9Fq2Wm5R...',
|
|
46
|
-
baseUrl: 'https://
|
|
46
|
+
baseUrl: 'https://patalink.me'
|
|
47
47
|
});
|
|
48
48
|
```
|
|
49
49
|
|
|
@@ -105,7 +105,7 @@ interface ClientConfig {
|
|
|
105
105
|
|
|
106
106
|
### Getting Your Keys
|
|
107
107
|
|
|
108
|
-
1. Sign up at [
|
|
108
|
+
1. Sign up at [patalink.me](https://patalink.me)
|
|
109
109
|
2. Navigate to **Settings** → **API Keys**
|
|
110
110
|
3. Click **Generate API Key** and **Generate Encryption Key**
|
|
111
111
|
4. Copy both keys to your environment variables
|
|
@@ -115,7 +115,7 @@ interface ClientConfig {
|
|
|
115
115
|
```env
|
|
116
116
|
PATALINK_API_KEY=pk_live_abc123def456...
|
|
117
117
|
PATALINK_ENCRYPTION_KEY=xK8j3Hd9Fq2Wm5R...
|
|
118
|
-
PATALINK_BASE_URL=https://
|
|
118
|
+
PATALINK_BASE_URL=https://patalink.me
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
```javascript
|
|
@@ -528,7 +528,7 @@ This package is designed for **Node.js server-side** use only. For browser/clien
|
|
|
528
528
|
</script>
|
|
529
529
|
|
|
530
530
|
<payment-modal
|
|
531
|
-
base-url="https://
|
|
531
|
+
base-url="https://patalink.me"
|
|
532
532
|
api-key="your_api_key"
|
|
533
533
|
encryption-key="your_encryption_key"
|
|
534
534
|
></payment-modal>
|
package/dist/index.d.mts
CHANGED
|
@@ -5,6 +5,7 @@ interface PaymentRequest {
|
|
|
5
5
|
phoneNumber?: string;
|
|
6
6
|
customerName?: string;
|
|
7
7
|
customerEmail?: string;
|
|
8
|
+
callbackUrl?: string;
|
|
8
9
|
}
|
|
9
10
|
interface PaymentResponse {
|
|
10
11
|
transactionId: string;
|
|
@@ -51,6 +52,64 @@ declare class PataLinkClient {
|
|
|
51
52
|
* Get transaction status
|
|
52
53
|
*/
|
|
53
54
|
getTransactionStatus(transactionId: string): Promise<TransactionStatus>;
|
|
55
|
+
/**
|
|
56
|
+
* Poll for transaction completion
|
|
57
|
+
*/
|
|
58
|
+
waitForCompletion(transactionId: string, options?: {
|
|
59
|
+
maxAttempts?: number;
|
|
60
|
+
interval?: number;
|
|
61
|
+
onProgress?: (attempt: number, status: string) => void;
|
|
62
|
+
}): Promise<TransactionStatus>;
|
|
63
|
+
/**
|
|
64
|
+
* List all transactions for this account
|
|
65
|
+
*/
|
|
66
|
+
listTransactions(options?: {
|
|
67
|
+
limit?: number;
|
|
68
|
+
offset?: number;
|
|
69
|
+
status?: 'pending' | 'success' | 'failed';
|
|
70
|
+
}): Promise<{
|
|
71
|
+
transactions: TransactionStatus[];
|
|
72
|
+
total: number;
|
|
73
|
+
}>;
|
|
74
|
+
/**
|
|
75
|
+
* Rotate encryption key (generates new key, revokes old one after 24h)
|
|
76
|
+
*/
|
|
77
|
+
rotateEncryptionKey(): Promise<{
|
|
78
|
+
newEncryptionKey: string;
|
|
79
|
+
message: string;
|
|
80
|
+
}>;
|
|
81
|
+
/**
|
|
82
|
+
* Get account information
|
|
83
|
+
*/
|
|
84
|
+
getAccountInfo(): Promise<{
|
|
85
|
+
id: string;
|
|
86
|
+
email: string;
|
|
87
|
+
walletBalance: number;
|
|
88
|
+
pendingBalance: number;
|
|
89
|
+
availableBalance: number;
|
|
90
|
+
}>;
|
|
91
|
+
/**
|
|
92
|
+
* Initiate withdrawal
|
|
93
|
+
*/
|
|
94
|
+
createWithdrawal(request: {
|
|
95
|
+
amount: number;
|
|
96
|
+
phoneNumber: string;
|
|
97
|
+
network: 'MTN' | 'Airtel';
|
|
98
|
+
}): Promise<{
|
|
99
|
+
withdrawalId: string;
|
|
100
|
+
status: string;
|
|
101
|
+
message: string;
|
|
102
|
+
}>;
|
|
103
|
+
/**
|
|
104
|
+
* List withdrawals
|
|
105
|
+
*/
|
|
106
|
+
listWithdrawals(options?: {
|
|
107
|
+
limit?: number;
|
|
108
|
+
offset?: number;
|
|
109
|
+
}): Promise<{
|
|
110
|
+
withdrawals: any[];
|
|
111
|
+
total: number;
|
|
112
|
+
}>;
|
|
54
113
|
}
|
|
55
114
|
declare function createClient(config: ClientConfig): PataLinkClient;
|
|
56
115
|
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ interface PaymentRequest {
|
|
|
5
5
|
phoneNumber?: string;
|
|
6
6
|
customerName?: string;
|
|
7
7
|
customerEmail?: string;
|
|
8
|
+
callbackUrl?: string;
|
|
8
9
|
}
|
|
9
10
|
interface PaymentResponse {
|
|
10
11
|
transactionId: string;
|
|
@@ -51,6 +52,64 @@ declare class PataLinkClient {
|
|
|
51
52
|
* Get transaction status
|
|
52
53
|
*/
|
|
53
54
|
getTransactionStatus(transactionId: string): Promise<TransactionStatus>;
|
|
55
|
+
/**
|
|
56
|
+
* Poll for transaction completion
|
|
57
|
+
*/
|
|
58
|
+
waitForCompletion(transactionId: string, options?: {
|
|
59
|
+
maxAttempts?: number;
|
|
60
|
+
interval?: number;
|
|
61
|
+
onProgress?: (attempt: number, status: string) => void;
|
|
62
|
+
}): Promise<TransactionStatus>;
|
|
63
|
+
/**
|
|
64
|
+
* List all transactions for this account
|
|
65
|
+
*/
|
|
66
|
+
listTransactions(options?: {
|
|
67
|
+
limit?: number;
|
|
68
|
+
offset?: number;
|
|
69
|
+
status?: 'pending' | 'success' | 'failed';
|
|
70
|
+
}): Promise<{
|
|
71
|
+
transactions: TransactionStatus[];
|
|
72
|
+
total: number;
|
|
73
|
+
}>;
|
|
74
|
+
/**
|
|
75
|
+
* Rotate encryption key (generates new key, revokes old one after 24h)
|
|
76
|
+
*/
|
|
77
|
+
rotateEncryptionKey(): Promise<{
|
|
78
|
+
newEncryptionKey: string;
|
|
79
|
+
message: string;
|
|
80
|
+
}>;
|
|
81
|
+
/**
|
|
82
|
+
* Get account information
|
|
83
|
+
*/
|
|
84
|
+
getAccountInfo(): Promise<{
|
|
85
|
+
id: string;
|
|
86
|
+
email: string;
|
|
87
|
+
walletBalance: number;
|
|
88
|
+
pendingBalance: number;
|
|
89
|
+
availableBalance: number;
|
|
90
|
+
}>;
|
|
91
|
+
/**
|
|
92
|
+
* Initiate withdrawal
|
|
93
|
+
*/
|
|
94
|
+
createWithdrawal(request: {
|
|
95
|
+
amount: number;
|
|
96
|
+
phoneNumber: string;
|
|
97
|
+
network: 'MTN' | 'Airtel';
|
|
98
|
+
}): Promise<{
|
|
99
|
+
withdrawalId: string;
|
|
100
|
+
status: string;
|
|
101
|
+
message: string;
|
|
102
|
+
}>;
|
|
103
|
+
/**
|
|
104
|
+
* List withdrawals
|
|
105
|
+
*/
|
|
106
|
+
listWithdrawals(options?: {
|
|
107
|
+
limit?: number;
|
|
108
|
+
offset?: number;
|
|
109
|
+
}): Promise<{
|
|
110
|
+
withdrawals: any[];
|
|
111
|
+
total: number;
|
|
112
|
+
}>;
|
|
54
113
|
}
|
|
55
114
|
declare function createClient(config: ClientConfig): PataLinkClient;
|
|
56
115
|
|
package/dist/index.js
CHANGED
|
@@ -50,7 +50,12 @@ var PataLinkClient = class {
|
|
|
50
50
|
*/
|
|
51
51
|
encrypt(data) {
|
|
52
52
|
const dataString = JSON.stringify(data);
|
|
53
|
-
|
|
53
|
+
let keyWA;
|
|
54
|
+
try {
|
|
55
|
+
keyWA = CryptoJS.enc.Base64.parse(this.encryptionKey);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
keyWA = CryptoJS.enc.Utf8.parse(this.encryptionKey);
|
|
58
|
+
}
|
|
54
59
|
const iv = CryptoJS.lib.WordArray.random(16);
|
|
55
60
|
const ivHex = iv.toString(CryptoJS.enc.Hex);
|
|
56
61
|
const ivWA = CryptoJS.enc.Hex.parse(ivHex);
|
|
@@ -106,6 +111,9 @@ var PataLinkClient = class {
|
|
|
106
111
|
if (request.paymentMethod !== "PesaPal" && !request.phoneNumber) {
|
|
107
112
|
throw new Error("phoneNumber is required for mobile money payments");
|
|
108
113
|
}
|
|
114
|
+
if (request.paymentMethod === "PesaPal" && !request.callbackUrl) {
|
|
115
|
+
throw new Error("callbackUrl is required for PesaPal (card) payments");
|
|
116
|
+
}
|
|
109
117
|
if (request.amount < 100) {
|
|
110
118
|
throw new Error("Amount must be at least 100 RWF");
|
|
111
119
|
}
|
|
@@ -120,6 +128,9 @@ var PataLinkClient = class {
|
|
|
120
128
|
supporterName: request.customerName || "",
|
|
121
129
|
supporterEmail: request.customerEmail || ""
|
|
122
130
|
};
|
|
131
|
+
if (request.paymentMethod === "PesaPal" && request.callbackUrl) {
|
|
132
|
+
paymentData.callbackUrl = request.callbackUrl;
|
|
133
|
+
}
|
|
123
134
|
const encryptedPayload = this.encrypt(paymentData);
|
|
124
135
|
const response = await this.request("/api/pay", {
|
|
125
136
|
method: "POST",
|
|
@@ -142,98 +153,77 @@ var PataLinkClient = class {
|
|
|
142
153
|
/**
|
|
143
154
|
* Poll for transaction completion
|
|
144
155
|
*/
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
// if (status.status === 'success' || status.status === 'failed') {
|
|
161
|
-
// return status;
|
|
162
|
-
// }
|
|
163
|
-
// // Wait before next attempt
|
|
164
|
-
// await new Promise(resolve => setTimeout(resolve, interval));
|
|
165
|
-
// }
|
|
166
|
-
// throw new Error('Transaction timeout - still pending after maximum attempts');
|
|
167
|
-
// }
|
|
156
|
+
async waitForCompletion(transactionId, options) {
|
|
157
|
+
const maxAttempts = options?.maxAttempts || 60;
|
|
158
|
+
const interval = options?.interval || 2e3;
|
|
159
|
+
let attempts = 0;
|
|
160
|
+
while (attempts < maxAttempts) {
|
|
161
|
+
attempts++;
|
|
162
|
+
options?.onProgress?.(attempts, "checking");
|
|
163
|
+
const status = await this.getTransactionStatus(transactionId);
|
|
164
|
+
if (status.status === "success" || status.status === "failed") {
|
|
165
|
+
return status;
|
|
166
|
+
}
|
|
167
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
168
|
+
}
|
|
169
|
+
throw new Error("Transaction timeout - still pending after maximum attempts");
|
|
170
|
+
}
|
|
168
171
|
/**
|
|
169
172
|
* List all transactions for this account
|
|
170
173
|
*/
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
// return this.request<{ transactions: TransactionStatus[]; total: number }>(endpoint, {
|
|
183
|
-
// method: 'GET'
|
|
184
|
-
// });
|
|
185
|
-
// }
|
|
174
|
+
async listTransactions(options) {
|
|
175
|
+
const params = new URLSearchParams();
|
|
176
|
+
if (options?.limit) params.append("limit", options.limit.toString());
|
|
177
|
+
if (options?.offset) params.append("offset", options.offset.toString());
|
|
178
|
+
if (options?.status) params.append("status", options.status);
|
|
179
|
+
const query = params.toString();
|
|
180
|
+
const endpoint = `/api/transactions${query ? `?${query}` : ""}`;
|
|
181
|
+
return this.request(endpoint, {
|
|
182
|
+
method: "GET"
|
|
183
|
+
});
|
|
184
|
+
}
|
|
186
185
|
/**
|
|
187
186
|
* Rotate encryption key (generates new key, revokes old one after 24h)
|
|
188
187
|
*/
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
188
|
+
async rotateEncryptionKey() {
|
|
189
|
+
return this.request("/api/keys/rotate", {
|
|
190
|
+
method: "POST"
|
|
191
|
+
});
|
|
192
|
+
}
|
|
194
193
|
/**
|
|
195
194
|
* Get account information
|
|
196
195
|
*/
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
// walletBalance: number;
|
|
201
|
-
// pendingBalance: number;
|
|
202
|
-
// availableBalance: number;
|
|
203
|
-
// }> {
|
|
204
|
-
// return this.request('/v1/account', { method: 'GET' });
|
|
205
|
-
// }
|
|
196
|
+
async getAccountInfo() {
|
|
197
|
+
return this.request("/api/account", { method: "GET" });
|
|
198
|
+
}
|
|
206
199
|
/**
|
|
207
200
|
* Initiate withdrawal
|
|
208
201
|
*/
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
202
|
+
async createWithdrawal(request) {
|
|
203
|
+
if (!request.amount) throw new Error("amount is required");
|
|
204
|
+
if (!request.phoneNumber) throw new Error("phoneNumber is required");
|
|
205
|
+
if (request.amount < 100) throw new Error("Minimum withdrawal is 100 RWF");
|
|
206
|
+
if (request.amount > 2e6) throw new Error("Maximum withdrawal is 2,000,000 RWF");
|
|
207
|
+
const encryptedPayload = this.encrypt(request);
|
|
208
|
+
return this.request("/api/withdrawals", {
|
|
209
|
+
method: "POST",
|
|
210
|
+
headers: {
|
|
211
|
+
"x-encrypted": "true"
|
|
212
|
+
},
|
|
213
|
+
body: JSON.stringify(encryptedPayload)
|
|
214
|
+
});
|
|
215
|
+
}
|
|
223
216
|
/**
|
|
224
217
|
* List withdrawals
|
|
225
218
|
*/
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
// const endpoint = `/v1/withdrawals${query ? `?${query}` : ''}`;
|
|
235
|
-
// return this.request(endpoint, { method: 'GET' });
|
|
236
|
-
// }
|
|
219
|
+
async listWithdrawals(options) {
|
|
220
|
+
const params = new URLSearchParams();
|
|
221
|
+
if (options?.limit) params.append("limit", options.limit.toString());
|
|
222
|
+
if (options?.offset) params.append("offset", options.offset.toString());
|
|
223
|
+
const query = params.toString();
|
|
224
|
+
const endpoint = `/api/withdrawals${query ? `?${query}` : ""}`;
|
|
225
|
+
return this.request(endpoint, { method: "GET" });
|
|
226
|
+
}
|
|
237
227
|
};
|
|
238
228
|
function createClient(config) {
|
|
239
229
|
return new PataLinkClient(config);
|
package/dist/index.mjs
CHANGED
|
@@ -15,7 +15,12 @@ var PataLinkClient = class {
|
|
|
15
15
|
*/
|
|
16
16
|
encrypt(data) {
|
|
17
17
|
const dataString = JSON.stringify(data);
|
|
18
|
-
|
|
18
|
+
let keyWA;
|
|
19
|
+
try {
|
|
20
|
+
keyWA = CryptoJS.enc.Base64.parse(this.encryptionKey);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
keyWA = CryptoJS.enc.Utf8.parse(this.encryptionKey);
|
|
23
|
+
}
|
|
19
24
|
const iv = CryptoJS.lib.WordArray.random(16);
|
|
20
25
|
const ivHex = iv.toString(CryptoJS.enc.Hex);
|
|
21
26
|
const ivWA = CryptoJS.enc.Hex.parse(ivHex);
|
|
@@ -71,6 +76,9 @@ var PataLinkClient = class {
|
|
|
71
76
|
if (request.paymentMethod !== "PesaPal" && !request.phoneNumber) {
|
|
72
77
|
throw new Error("phoneNumber is required for mobile money payments");
|
|
73
78
|
}
|
|
79
|
+
if (request.paymentMethod === "PesaPal" && !request.callbackUrl) {
|
|
80
|
+
throw new Error("callbackUrl is required for PesaPal (card) payments");
|
|
81
|
+
}
|
|
74
82
|
if (request.amount < 100) {
|
|
75
83
|
throw new Error("Amount must be at least 100 RWF");
|
|
76
84
|
}
|
|
@@ -85,6 +93,9 @@ var PataLinkClient = class {
|
|
|
85
93
|
supporterName: request.customerName || "",
|
|
86
94
|
supporterEmail: request.customerEmail || ""
|
|
87
95
|
};
|
|
96
|
+
if (request.paymentMethod === "PesaPal" && request.callbackUrl) {
|
|
97
|
+
paymentData.callbackUrl = request.callbackUrl;
|
|
98
|
+
}
|
|
88
99
|
const encryptedPayload = this.encrypt(paymentData);
|
|
89
100
|
const response = await this.request("/api/pay", {
|
|
90
101
|
method: "POST",
|
|
@@ -107,98 +118,77 @@ var PataLinkClient = class {
|
|
|
107
118
|
/**
|
|
108
119
|
* Poll for transaction completion
|
|
109
120
|
*/
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
// if (status.status === 'success' || status.status === 'failed') {
|
|
126
|
-
// return status;
|
|
127
|
-
// }
|
|
128
|
-
// // Wait before next attempt
|
|
129
|
-
// await new Promise(resolve => setTimeout(resolve, interval));
|
|
130
|
-
// }
|
|
131
|
-
// throw new Error('Transaction timeout - still pending after maximum attempts');
|
|
132
|
-
// }
|
|
121
|
+
async waitForCompletion(transactionId, options) {
|
|
122
|
+
const maxAttempts = options?.maxAttempts || 60;
|
|
123
|
+
const interval = options?.interval || 2e3;
|
|
124
|
+
let attempts = 0;
|
|
125
|
+
while (attempts < maxAttempts) {
|
|
126
|
+
attempts++;
|
|
127
|
+
options?.onProgress?.(attempts, "checking");
|
|
128
|
+
const status = await this.getTransactionStatus(transactionId);
|
|
129
|
+
if (status.status === "success" || status.status === "failed") {
|
|
130
|
+
return status;
|
|
131
|
+
}
|
|
132
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
133
|
+
}
|
|
134
|
+
throw new Error("Transaction timeout - still pending after maximum attempts");
|
|
135
|
+
}
|
|
133
136
|
/**
|
|
134
137
|
* List all transactions for this account
|
|
135
138
|
*/
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
// return this.request<{ transactions: TransactionStatus[]; total: number }>(endpoint, {
|
|
148
|
-
// method: 'GET'
|
|
149
|
-
// });
|
|
150
|
-
// }
|
|
139
|
+
async listTransactions(options) {
|
|
140
|
+
const params = new URLSearchParams();
|
|
141
|
+
if (options?.limit) params.append("limit", options.limit.toString());
|
|
142
|
+
if (options?.offset) params.append("offset", options.offset.toString());
|
|
143
|
+
if (options?.status) params.append("status", options.status);
|
|
144
|
+
const query = params.toString();
|
|
145
|
+
const endpoint = `/api/transactions${query ? `?${query}` : ""}`;
|
|
146
|
+
return this.request(endpoint, {
|
|
147
|
+
method: "GET"
|
|
148
|
+
});
|
|
149
|
+
}
|
|
151
150
|
/**
|
|
152
151
|
* Rotate encryption key (generates new key, revokes old one after 24h)
|
|
153
152
|
*/
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
153
|
+
async rotateEncryptionKey() {
|
|
154
|
+
return this.request("/api/keys/rotate", {
|
|
155
|
+
method: "POST"
|
|
156
|
+
});
|
|
157
|
+
}
|
|
159
158
|
/**
|
|
160
159
|
* Get account information
|
|
161
160
|
*/
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
// walletBalance: number;
|
|
166
|
-
// pendingBalance: number;
|
|
167
|
-
// availableBalance: number;
|
|
168
|
-
// }> {
|
|
169
|
-
// return this.request('/v1/account', { method: 'GET' });
|
|
170
|
-
// }
|
|
161
|
+
async getAccountInfo() {
|
|
162
|
+
return this.request("/api/account", { method: "GET" });
|
|
163
|
+
}
|
|
171
164
|
/**
|
|
172
165
|
* Initiate withdrawal
|
|
173
166
|
*/
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
167
|
+
async createWithdrawal(request) {
|
|
168
|
+
if (!request.amount) throw new Error("amount is required");
|
|
169
|
+
if (!request.phoneNumber) throw new Error("phoneNumber is required");
|
|
170
|
+
if (request.amount < 100) throw new Error("Minimum withdrawal is 100 RWF");
|
|
171
|
+
if (request.amount > 2e6) throw new Error("Maximum withdrawal is 2,000,000 RWF");
|
|
172
|
+
const encryptedPayload = this.encrypt(request);
|
|
173
|
+
return this.request("/api/withdrawals", {
|
|
174
|
+
method: "POST",
|
|
175
|
+
headers: {
|
|
176
|
+
"x-encrypted": "true"
|
|
177
|
+
},
|
|
178
|
+
body: JSON.stringify(encryptedPayload)
|
|
179
|
+
});
|
|
180
|
+
}
|
|
188
181
|
/**
|
|
189
182
|
* List withdrawals
|
|
190
183
|
*/
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
// const endpoint = `/v1/withdrawals${query ? `?${query}` : ''}`;
|
|
200
|
-
// return this.request(endpoint, { method: 'GET' });
|
|
201
|
-
// }
|
|
184
|
+
async listWithdrawals(options) {
|
|
185
|
+
const params = new URLSearchParams();
|
|
186
|
+
if (options?.limit) params.append("limit", options.limit.toString());
|
|
187
|
+
if (options?.offset) params.append("offset", options.offset.toString());
|
|
188
|
+
const query = params.toString();
|
|
189
|
+
const endpoint = `/api/withdrawals${query ? `?${query}` : ""}`;
|
|
190
|
+
return this.request(endpoint, { method: "GET" });
|
|
191
|
+
}
|
|
202
192
|
};
|
|
203
193
|
function createClient(config) {
|
|
204
194
|
return new PataLinkClient(config);
|