xendit-fn 1.0.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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +442 -0
  3. package/lib/index.cjs +1588 -0
  4. package/lib/index.d.cts +9574 -0
  5. package/lib/index.d.ts +7 -0
  6. package/lib/index.d.ts.map +1 -0
  7. package/lib/index.esm.d.ts +9574 -0
  8. package/lib/index.esm.js +1544 -0
  9. package/lib/sdk/axios.d.ts +3 -0
  10. package/lib/sdk/axios.d.ts.map +1 -0
  11. package/lib/sdk/card/index.d.ts +2 -0
  12. package/lib/sdk/card/index.d.ts.map +1 -0
  13. package/lib/sdk/card/schema.d.ts +586 -0
  14. package/lib/sdk/card/schema.d.ts.map +1 -0
  15. package/lib/sdk/common.d.ts +28 -0
  16. package/lib/sdk/common.d.ts.map +1 -0
  17. package/lib/sdk/customer/index.d.ts +7 -0
  18. package/lib/sdk/customer/index.d.ts.map +1 -0
  19. package/lib/sdk/customer/schema.d.ts +5933 -0
  20. package/lib/sdk/customer/schema.d.ts.map +1 -0
  21. package/lib/sdk/ewallet/create.d.ts +173 -0
  22. package/lib/sdk/ewallet/create.d.ts.map +1 -0
  23. package/lib/sdk/ewallet/schema.d.ts +783 -0
  24. package/lib/sdk/ewallet/schema.d.ts.map +1 -0
  25. package/lib/sdk/index.d.ts +2201 -0
  26. package/lib/sdk/index.d.ts.map +1 -0
  27. package/lib/sdk/invoice/index.d.ts +8 -0
  28. package/lib/sdk/invoice/index.d.ts.map +1 -0
  29. package/lib/sdk/invoice/schema.d.ts +2198 -0
  30. package/lib/sdk/invoice/schema.d.ts.map +1 -0
  31. package/lib/sdk/payment-method/index.d.ts +7 -0
  32. package/lib/sdk/payment-method/index.d.ts.map +1 -0
  33. package/lib/sdk/payment-method/schema.d.ts +990 -0
  34. package/lib/sdk/payment-method/schema.d.ts.map +1 -0
  35. package/lib/utils/errors.d.ts +58 -0
  36. package/lib/utils/errors.d.ts.map +1 -0
  37. package/lib/utils/index.d.ts +6 -0
  38. package/lib/utils/index.d.ts.map +1 -0
  39. package/lib/utils/pagination.d.ts +134 -0
  40. package/lib/utils/pagination.d.ts.map +1 -0
  41. package/lib/utils/rate-limit.d.ts +90 -0
  42. package/lib/utils/rate-limit.d.ts.map +1 -0
  43. package/lib/utils/type-guards.d.ts +13 -0
  44. package/lib/utils/type-guards.d.ts.map +1 -0
  45. package/lib/utils/webhook.d.ts +101 -0
  46. package/lib/utils/webhook.d.ts.map +1 -0
  47. package/package.json +83 -0
package/lib/index.cjs ADDED
@@ -0,0 +1,1588 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+
3
+ var axios = require('axios');
4
+ var zod = require('zod');
5
+ var crypto = require('crypto');
6
+
7
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
+
9
+ var axios__default = /*#__PURE__*/_interopDefault(axios);
10
+
11
+ const createAxiosInstance = (config)=>axios__default.default.create({
12
+ ...config,
13
+ headers: {
14
+ Accept: "application/json",
15
+ "Content-Type": "application/json",
16
+ common: {
17
+ Accept: "application/json",
18
+ "Content-Type": "application/json"
19
+ },
20
+ ...config?.headers
21
+ },
22
+ baseURL: "https://api.xendit.co"
23
+ });
24
+
25
+ /**
26
+ * Rate limiter implementation using token bucket algorithm
27
+ */ class RateLimiter {
28
+ /**
29
+ * Refill tokens based on elapsed time
30
+ */ refillTokens() {
31
+ const now = Date.now();
32
+ const timePassed = now - this.lastRefill;
33
+ const tokensToAdd = timePassed / this.config.windowMs * this.config.maxRequests;
34
+ this.tokens = Math.min(this.config.maxRequests, this.tokens + tokensToAdd);
35
+ this.lastRefill = now;
36
+ }
37
+ /**
38
+ * Check if a request can be made
39
+ */ canMakeRequest() {
40
+ this.refillTokens();
41
+ return this.tokens >= 1;
42
+ }
43
+ /**
44
+ * Consume a token for a request
45
+ */ consumeToken() {
46
+ this.refillTokens();
47
+ if (this.tokens >= 1) {
48
+ this.tokens -= 1;
49
+ return true;
50
+ }
51
+ return false;
52
+ }
53
+ /**
54
+ * Get time until next token is available
55
+ */ getWaitTime() {
56
+ if (this.canMakeRequest()) {
57
+ return 0;
58
+ }
59
+ const tokensNeeded = 1 - this.tokens;
60
+ return tokensNeeded / this.config.maxRequests * this.config.windowMs;
61
+ }
62
+ /**
63
+ * Wait for a token to become available
64
+ */ async waitForToken() {
65
+ const waitTime = this.getWaitTime();
66
+ if (waitTime > 0) {
67
+ await this.sleep(waitTime);
68
+ }
69
+ }
70
+ /**
71
+ * Sleep for specified milliseconds
72
+ */ sleep(ms) {
73
+ return new Promise((resolve)=>setTimeout(resolve, ms));
74
+ }
75
+ constructor(config = {}){
76
+ this.config = {
77
+ maxRequests: config.maxRequests ?? 100,
78
+ windowMs: config.windowMs ?? 60000,
79
+ requestDelayMs: config.requestDelayMs ?? 0,
80
+ maxRetries: config.maxRetries ?? 3,
81
+ baseRetryDelayMs: config.baseRetryDelayMs ?? 1000,
82
+ maxRetryDelayMs: config.maxRetryDelayMs ?? 30000
83
+ };
84
+ this.tokens = this.config.maxRequests;
85
+ this.lastRefill = Date.now();
86
+ }
87
+ }
88
+ /**
89
+ * Axios interceptor for rate limiting
90
+ */ function createRateLimitInterceptor(rateLimiter) {
91
+ return {
92
+ request: async (config)=>{
93
+ // Wait for token availability
94
+ await rateLimiter.waitForToken();
95
+ // Consume token
96
+ rateLimiter.consumeToken();
97
+ // Apply request delay if configured
98
+ const delay = rateLimiter.config.requestDelayMs;
99
+ if (delay > 0) {
100
+ await rateLimiter.sleep(delay);
101
+ }
102
+ return config;
103
+ },
104
+ response: (response)=>{
105
+ // Check for rate limit headers and adjust if needed
106
+ const rateLimitRemaining = response.headers["x-ratelimit-remaining"];
107
+ if (rateLimitRemaining !== undefined && Number(rateLimitRemaining) === 0) {
108
+ console.warn("Rate limit reached, requests will be throttled");
109
+ }
110
+ return response;
111
+ },
112
+ responseError: async (error)=>{
113
+ // Handle rate limit errors (HTTP 429)
114
+ if (error.response?.status === 429) {
115
+ const retryAfter = error.response.headers["retry-after"];
116
+ const delay = retryAfter ? Number(retryAfter) * 1000 : rateLimiter.config.baseRetryDelayMs;
117
+ console.warn(`Rate limited, retrying after ${delay}ms`);
118
+ await rateLimiter.sleep(delay);
119
+ // Don't automatically retry here, let the retry interceptor handle it
120
+ return Promise.reject(error);
121
+ }
122
+ return Promise.reject(error);
123
+ }
124
+ };
125
+ }
126
+ /**
127
+ * Basic retry interceptor - simplified for compatibility
128
+ */ function createRetryInterceptor(_config = {}) {
129
+ return async (error)=>{
130
+ // For now, just log retryable errors and reject
131
+ // This can be enhanced in the future with proper retry logic
132
+ if (isRetryableError(error)) {
133
+ console.warn("Request failed with retryable error:", error.message);
134
+ }
135
+ return Promise.reject(error);
136
+ };
137
+ }
138
+ /**
139
+ * Check if an error is retryable
140
+ */ function isRetryableError(error) {
141
+ // No response means network error, which is retryable
142
+ if (!error.response) {
143
+ return true;
144
+ }
145
+ // Specific status codes that are retryable
146
+ const retryableStatusCodes = [
147
+ 408,
148
+ 429,
149
+ 500,
150
+ 502,
151
+ 503,
152
+ 504
153
+ ];
154
+ return retryableStatusCodes.includes(error.response.status);
155
+ }
156
+ /**
157
+ * Setup rate limiting and retry logic for an Axios instance
158
+ */ function setupRateLimit(axiosInstance, config = {}) {
159
+ const rateLimiter = new RateLimiter(config);
160
+ const rateLimitInterceptor = createRateLimitInterceptor(rateLimiter);
161
+ const retryInterceptor = createRetryInterceptor(config);
162
+ // Add request interceptor for rate limiting
163
+ axiosInstance.interceptors.request.use(rateLimitInterceptor.request, (error)=>Promise.reject(error));
164
+ // Add response interceptors
165
+ axiosInstance.interceptors.response.use(rateLimitInterceptor.response, rateLimitInterceptor.responseError);
166
+ // Add retry interceptor (should be last)
167
+ axiosInstance.interceptors.response.use((response)=>response, retryInterceptor);
168
+ }
169
+ /**
170
+ * Create a rate-limited axios instance
171
+ */ function createRateLimitedAxios(baseURL, apiKey, config = {}) {
172
+ const axiosInstance = axios__default.default.create({
173
+ baseURL,
174
+ headers: {
175
+ Authorization: `Basic ${Buffer.from(`${apiKey}:`).toString("base64")}`,
176
+ "Content-Type": "application/json"
177
+ }
178
+ });
179
+ setupRateLimit(axiosInstance, config);
180
+ return axiosInstance;
181
+ }
182
+
183
+ const XenditErrorSchema = zod.z.object({
184
+ error_code: zod.z.string(),
185
+ message: zod.z.string(),
186
+ errors: zod.z.array(zod.z.object({
187
+ field: zod.z.string().optional(),
188
+ message: zod.z.string()
189
+ })).optional()
190
+ });
191
+ class XenditApiError extends Error {
192
+ constructor(message, code, statusCode, details){
193
+ super(message);
194
+ this.name = "XenditApiError";
195
+ this.code = code;
196
+ this.statusCode = statusCode;
197
+ this.details = details;
198
+ // Maintain proper stack trace for where our error was thrown
199
+ if (Error.captureStackTrace) {
200
+ Error.captureStackTrace(this, XenditApiError);
201
+ }
202
+ }
203
+ }
204
+ class ValidationError extends Error {
205
+ constructor(message, validationErrors, field){
206
+ super(message);
207
+ this.name = "ValidationError";
208
+ this.field = field;
209
+ this.validationErrors = validationErrors;
210
+ if (Error.captureStackTrace) {
211
+ Error.captureStackTrace(this, ValidationError);
212
+ }
213
+ }
214
+ }
215
+ class AuthenticationError extends XenditApiError {
216
+ constructor(message = "Authentication failed"){
217
+ super(message, "AUTHENTICATION_ERROR", 401);
218
+ this.name = "AuthenticationError";
219
+ }
220
+ }
221
+ class NotFoundError extends XenditApiError {
222
+ constructor(message = "Resource not found"){
223
+ super(message, "NOT_FOUND_ERROR", 404);
224
+ this.name = "NotFoundError";
225
+ }
226
+ }
227
+ class RateLimitError extends XenditApiError {
228
+ constructor(message = "Rate limit exceeded"){
229
+ super(message, "RATE_LIMIT_ERROR", 429);
230
+ this.name = "RateLimitError";
231
+ }
232
+ }
233
+ const handleAxiosError = (error)=>{
234
+ if (error.response?.data) {
235
+ const parsed = XenditErrorSchema.safeParse(error.response.data);
236
+ if (parsed.success) {
237
+ throw new XenditApiError(parsed.data.message, parsed.data.error_code, error.response.status, parsed.data.errors ? {
238
+ errors: parsed.data.errors
239
+ } : undefined);
240
+ }
241
+ }
242
+ throw new XenditApiError(error.message || "Unknown API error", error.code || "UNKNOWN_ERROR", error.response?.status, error.response?.data);
243
+ };
244
+ const validateInput = (schema, data, fieldName)=>{
245
+ const result = schema.safeParse(data);
246
+ if (!result.success) {
247
+ throw new ValidationError(`Validation failed${fieldName ? ` for field ${fieldName}` : ""}`, result.error.issues, fieldName);
248
+ }
249
+ return result.data;
250
+ };
251
+
252
+ const PhoneSchema = zod.z.string().min(7).max(15).refine((value)=>value.startsWith("+"));
253
+ const CountrySchema = zod.z.union([
254
+ zod.z.literal("PH"),
255
+ zod.z.literal("ID"),
256
+ zod.z.literal("MY"),
257
+ zod.z.literal("TH"),
258
+ zod.z.literal("VN")
259
+ ]);
260
+ const CurrencySchema = zod.z.union([
261
+ zod.z.literal("PHP"),
262
+ zod.z.literal("IDR"),
263
+ zod.z.literal("MYR"),
264
+ zod.z.literal("THB"),
265
+ zod.z.literal("VND")
266
+ ]);
267
+ zod.z.object({
268
+ given_names: zod.z.string(),
269
+ surname: zod.z.string().optional(),
270
+ email: zod.z.string().email().optional(),
271
+ mobile_number: PhoneSchema.optional(),
272
+ phone_number: PhoneSchema.optional()
273
+ });
274
+
275
+ zod.z.union([
276
+ zod.z.literal("INDIVIDUAL"),
277
+ zod.z.literal("BUSINESS")
278
+ ]);
279
+ const IndividualDetailSchema = zod.z.object({
280
+ given_names: zod.z.string(),
281
+ surname: zod.z.string().optional(),
282
+ nationality: zod.z.string().optional(),
283
+ place_of_birth: zod.z.string().optional(),
284
+ date_of_birth: zod.z.string().optional(),
285
+ gender: zod.z.union([
286
+ zod.z.literal("MALE"),
287
+ zod.z.literal("FEMALE"),
288
+ zod.z.literal("OTHER")
289
+ ]).optional(),
290
+ employment: zod.z.object({
291
+ employer_name: zod.z.string(),
292
+ nature_of_business: zod.z.string(),
293
+ role_description: zod.z.string()
294
+ }).optional()
295
+ });
296
+ zod.z.union([
297
+ zod.z.literal("CORPORATION"),
298
+ zod.z.literal("SOLE_PROPRIETOR"),
299
+ zod.z.literal("PARTNERSHIP"),
300
+ zod.z.literal("COOPERATIVE"),
301
+ zod.z.literal("TRUST"),
302
+ zod.z.literal("NON_PROFIT"),
303
+ zod.z.literal("GOVERNMENT")
304
+ ]);
305
+ const BusinessDetailSchema = zod.z.object({
306
+ business_name: zod.z.string(),
307
+ trading_name: zod.z.string().optional(),
308
+ business_type: zod.z.string()
309
+ });
310
+ const AddressSchema = zod.z.object({
311
+ street_line1: zod.z.string().nullable().optional(),
312
+ street_line2: zod.z.string().nullable().optional(),
313
+ city: zod.z.string().nullable().optional(),
314
+ province_state: zod.z.string().nullable().optional(),
315
+ postal_code: zod.z.string().nullable().optional(),
316
+ country: zod.z.string(),
317
+ category: zod.z.string().nullable().optional(),
318
+ is_primary: zod.z.boolean().nullable().optional()
319
+ });
320
+ const AccountTypeSchema = zod.z.union([
321
+ zod.z.literal("BANK_ACCOUNT"),
322
+ zod.z.literal("EWALLET"),
323
+ zod.z.literal("CREDIT_CARD"),
324
+ zod.z.literal("PAY_LATER"),
325
+ zod.z.literal("OTC"),
326
+ zod.z.literal("QR_CODE"),
327
+ zod.z.literal("SOCIAL_MEDIA")
328
+ ]);
329
+ const BankAccountSchema = zod.z.object({
330
+ account_number: zod.z.string(),
331
+ account_holder_name: zod.z.string(),
332
+ swift_code: zod.z.string().optional(),
333
+ account_type: zod.z.string().optional(),
334
+ account_details: zod.z.string().optional(),
335
+ currency: zod.z.string().optional()
336
+ });
337
+ const EWalletAccountSchema = zod.z.object({
338
+ account_number: zod.z.string(),
339
+ account_holder_name: zod.z.string(),
340
+ currency: zod.z.string().optional()
341
+ });
342
+ const CreditCardAccountSchema = zod.z.object({
343
+ token_id: zod.z.string()
344
+ });
345
+ const OTCAccountSchema = zod.z.object({
346
+ payment_code: zod.z.string(),
347
+ expires_at: zod.z.string().optional()
348
+ });
349
+ const QrAccountSchema = zod.z.object({
350
+ qr_string: zod.z.string()
351
+ });
352
+ const PayLaterAccountSchema = zod.z.object({
353
+ account_id: zod.z.string(),
354
+ account_holder_name: zod.z.string().optional(),
355
+ currency: CurrencySchema.optional()
356
+ });
357
+ const SocialMediaAccountSchema = zod.z.object({
358
+ account_id: zod.z.string(),
359
+ account_handle: zod.z.string().optional()
360
+ });
361
+ const PropertiesSchema = zod.z.discriminatedUnion("type", [
362
+ zod.z.object({
363
+ type: zod.z.literal("BANK_ACCOUNT"),
364
+ properties: BankAccountSchema
365
+ }),
366
+ zod.z.object({
367
+ type: zod.z.literal("EWALLET"),
368
+ properties: EWalletAccountSchema
369
+ }),
370
+ zod.z.object({
371
+ type: zod.z.literal("CREDIT_CARD"),
372
+ properties: CreditCardAccountSchema
373
+ }),
374
+ zod.z.object({
375
+ type: zod.z.literal("OTC"),
376
+ properties: OTCAccountSchema
377
+ }),
378
+ zod.z.object({
379
+ type: zod.z.literal("QR_CODE"),
380
+ properties: QrAccountSchema
381
+ }),
382
+ zod.z.object({
383
+ type: zod.z.literal("PAY_LATER"),
384
+ properties: PayLaterAccountSchema
385
+ }),
386
+ zod.z.object({
387
+ type: zod.z.literal("SOCIAL_MEDIA"),
388
+ properties: SocialMediaAccountSchema
389
+ })
390
+ ]);
391
+ const IdentityAccountSchema = zod.z.object({
392
+ type: AccountTypeSchema,
393
+ company: zod.z.string().nullable().optional(),
394
+ description: zod.z.string().nullable().optional(),
395
+ country: zod.z.string().nullable().optional(),
396
+ properties: PropertiesSchema
397
+ });
398
+ const KYCDocumentSchema = zod.z.object({
399
+ type: zod.z.string(),
400
+ sub_type: zod.z.string(),
401
+ country: zod.z.string(),
402
+ document_name: zod.z.string(),
403
+ document_number: zod.z.string(),
404
+ expires_at: zod.z.null(),
405
+ holder_name: zod.z.string(),
406
+ document_images: zod.z.array(zod.z.string())
407
+ });
408
+ const CommonCustomerResourceSchema = zod.z.object({
409
+ individual_detail: IndividualDetailSchema.optional(),
410
+ business_detail: BusinessDetailSchema.optional(),
411
+ email: zod.z.string().email().optional(),
412
+ mobile_number: PhoneSchema.optional(),
413
+ phone_number: PhoneSchema.optional(),
414
+ hashed_phone_number: zod.z.string().nullable().optional(),
415
+ addresses: zod.z.array(AddressSchema).optional(),
416
+ identity_accounts: zod.z.array(IdentityAccountSchema).optional(),
417
+ kyc_documents: zod.z.array(KYCDocumentSchema).optional(),
418
+ description: zod.z.string().nullable().optional(),
419
+ date_of_registration: zod.z.string().nullable().optional(),
420
+ domicile_of_registration: zod.z.string().nullable().optional(),
421
+ metadata: zod.z.object({}).nullable().optional()
422
+ });
423
+ // Create a discriminated union for customer types
424
+ const CustomerSchema = zod.z.discriminatedUnion("type", [
425
+ zod.z.object({
426
+ type: zod.z.literal("INDIVIDUAL"),
427
+ reference_id: zod.z.string(),
428
+ individual_detail: IndividualDetailSchema,
429
+ business_detail: zod.z.undefined().optional(),
430
+ email: zod.z.string().email().optional(),
431
+ mobile_number: PhoneSchema.optional(),
432
+ phone_number: PhoneSchema.optional(),
433
+ hashed_phone_number: zod.z.string().nullable().optional(),
434
+ addresses: zod.z.array(AddressSchema).optional(),
435
+ identity_accounts: zod.z.array(IdentityAccountSchema).optional(),
436
+ kyc_documents: zod.z.array(KYCDocumentSchema).optional(),
437
+ description: zod.z.string().nullable().optional(),
438
+ date_of_registration: zod.z.string().nullable().optional(),
439
+ domicile_of_registration: zod.z.string().nullable().optional(),
440
+ metadata: zod.z.object({}).nullable().optional()
441
+ }),
442
+ zod.z.object({
443
+ type: zod.z.literal("BUSINESS"),
444
+ reference_id: zod.z.string(),
445
+ individual_detail: zod.z.undefined().optional(),
446
+ business_detail: BusinessDetailSchema,
447
+ email: zod.z.string().email().optional(),
448
+ mobile_number: PhoneSchema.optional(),
449
+ phone_number: PhoneSchema.optional(),
450
+ hashed_phone_number: zod.z.string().nullable().optional(),
451
+ addresses: zod.z.array(AddressSchema).optional(),
452
+ identity_accounts: zod.z.array(IdentityAccountSchema).optional(),
453
+ kyc_documents: zod.z.array(KYCDocumentSchema).optional(),
454
+ description: zod.z.string().nullable().optional(),
455
+ date_of_registration: zod.z.string().nullable().optional(),
456
+ domicile_of_registration: zod.z.string().nullable().optional(),
457
+ metadata: zod.z.object({}).nullable().optional()
458
+ })
459
+ ]);
460
+ const GetCustomerSchema = zod.z.object({
461
+ id: zod.z.string()
462
+ });
463
+ // Create CustomerResourceSchema by extending both discriminated union options
464
+ const CustomerResourceSchema = zod.z.discriminatedUnion("type", [
465
+ zod.z.object({
466
+ type: zod.z.literal("INDIVIDUAL"),
467
+ id: zod.z.string(),
468
+ reference_id: zod.z.string(),
469
+ individual_detail: IndividualDetailSchema,
470
+ business_detail: zod.z.undefined().optional(),
471
+ email: zod.z.string().email().optional(),
472
+ mobile_number: PhoneSchema.optional(),
473
+ phone_number: PhoneSchema.optional(),
474
+ hashed_phone_number: zod.z.string().nullable().optional(),
475
+ addresses: zod.z.array(AddressSchema).optional(),
476
+ identity_accounts: zod.z.array(IdentityAccountSchema).optional(),
477
+ kyc_documents: zod.z.array(KYCDocumentSchema).optional(),
478
+ description: zod.z.string().nullable().optional(),
479
+ date_of_registration: zod.z.string().nullable().optional(),
480
+ domicile_of_registration: zod.z.string().nullable().optional(),
481
+ metadata: zod.z.object({}).nullable().optional(),
482
+ created: zod.z.string().datetime(),
483
+ updated: zod.z.string().datetime()
484
+ }),
485
+ zod.z.object({
486
+ type: zod.z.literal("BUSINESS"),
487
+ id: zod.z.string(),
488
+ reference_id: zod.z.string(),
489
+ individual_detail: zod.z.undefined().optional(),
490
+ business_detail: BusinessDetailSchema,
491
+ email: zod.z.string().email().optional(),
492
+ mobile_number: PhoneSchema.optional(),
493
+ phone_number: PhoneSchema.optional(),
494
+ hashed_phone_number: zod.z.string().nullable().optional(),
495
+ addresses: zod.z.array(AddressSchema).optional(),
496
+ identity_accounts: zod.z.array(IdentityAccountSchema).optional(),
497
+ kyc_documents: zod.z.array(KYCDocumentSchema).optional(),
498
+ description: zod.z.string().nullable().optional(),
499
+ date_of_registration: zod.z.string().nullable().optional(),
500
+ domicile_of_registration: zod.z.string().nullable().optional(),
501
+ metadata: zod.z.object({}).nullable().optional(),
502
+ created: zod.z.string().datetime(),
503
+ updated: zod.z.string().datetime()
504
+ })
505
+ ]);
506
+ const GetCustomerByRefIdSchema = zod.z.object({
507
+ reference_id: zod.z.string()
508
+ });
509
+ zod.z.object({
510
+ data: zod.z.array(CustomerResourceSchema),
511
+ hasMore: zod.z.boolean()
512
+ });
513
+ const UpdateParamsSchema = zod.z.object({
514
+ id: zod.z.string(),
515
+ payload: CommonCustomerResourceSchema
516
+ });
517
+
518
+ const createCustomer = async (params, axiosInstance, config)=>{
519
+ try {
520
+ const validatedParams = validateInput(CustomerSchema, params, "customer params");
521
+ const response = await axiosInstance.post(config?.url ?? "/customers", validatedParams, config);
522
+ // Note: Actual API response might not match discriminated union exactly
523
+ // For production use, consider making the schema more flexible
524
+ return response.data;
525
+ } catch (error) {
526
+ if (error instanceof Error && error.name === "AxiosError") {
527
+ handleAxiosError(error);
528
+ }
529
+ throw error;
530
+ }
531
+ };
532
+ const getCustomerId = async (params, axiosInstance, config)=>{
533
+ try {
534
+ const validatedParams = validateInput(GetCustomerSchema, params, "get customer params");
535
+ const response = await axiosInstance.get(config?.url ?? `/customers/${validatedParams.id}`, config);
536
+ // Note: Actual API response might not match discriminated union exactly
537
+ return response.data;
538
+ } catch (error) {
539
+ if (error instanceof Error && error.name === "AxiosError") {
540
+ handleAxiosError(error);
541
+ }
542
+ throw error;
543
+ }
544
+ };
545
+ const getCustomerRefId = async (params, axiosInstance, config)=>{
546
+ try {
547
+ const validatedParams = validateInput(GetCustomerByRefIdSchema, params, "get customer by ref params");
548
+ const response = await axiosInstance.get(config?.url ?? `/customers?reference_id=${validatedParams.reference_id}`, config);
549
+ // Note: Actual API response might not match discriminated union exactly
550
+ return response.data;
551
+ } catch (error) {
552
+ if (error instanceof Error && error.name === "AxiosError") {
553
+ handleAxiosError(error);
554
+ }
555
+ throw error;
556
+ }
557
+ };
558
+ const updateCustomer = async (params, axiosInstance, config)=>{
559
+ try {
560
+ const validatedParams = validateInput(UpdateParamsSchema, params, "update customer params");
561
+ const response = await axiosInstance.patch(config?.url ?? `/customers/${validatedParams.id}`, validatedParams.payload, config);
562
+ // Note: Actual API response might not match discriminated union exactly
563
+ return response.data;
564
+ } catch (error) {
565
+ if (error instanceof Error && error.name === "AxiosError") {
566
+ handleAxiosError(error);
567
+ }
568
+ throw error;
569
+ }
570
+ };
571
+
572
+ const createEwalletCharge = async (params, axiosInstance, config)=>(await axiosInstance.post(config?.url ?? "/ewallets/charges", params, config)).data;
573
+ const getEwalletCharge = async (params, axiosInstance, config)=>(await axiosInstance.get(config?.url ?? `/ewallets/charges/${params.id}`, config)).data;
574
+
575
+ // Payment Method Types
576
+ const PaymentMethodTypeSchema = zod.z.union([
577
+ zod.z.literal("CARD"),
578
+ zod.z.literal("BANK_ACCOUNT"),
579
+ zod.z.literal("EWALLET"),
580
+ zod.z.literal("OVER_THE_COUNTER"),
581
+ zod.z.literal("VIRTUAL_ACCOUNT"),
582
+ zod.z.literal("QR_CODE")
583
+ ]);
584
+ // Payment Method Status
585
+ const PaymentMethodStatusSchema = zod.z.union([
586
+ zod.z.literal("ACTIVE"),
587
+ zod.z.literal("INACTIVE"),
588
+ zod.z.literal("PENDING"),
589
+ zod.z.literal("EXPIRED"),
590
+ zod.z.literal("FAILED")
591
+ ]);
592
+ // Card Properties
593
+ const CardPropertiesSchema = zod.z.object({
594
+ card_last_four: zod.z.string(),
595
+ card_expiry_month: zod.z.string(),
596
+ card_expiry_year: zod.z.string(),
597
+ network: zod.z.string(),
598
+ country: CountrySchema.optional(),
599
+ issuer: zod.z.string().optional(),
600
+ type: zod.z.union([
601
+ zod.z.literal("CREDIT"),
602
+ zod.z.literal("DEBIT")
603
+ ]).optional(),
604
+ currency: CurrencySchema.optional()
605
+ });
606
+ // Bank Account Properties
607
+ const BankAccountPropertiesSchema = zod.z.object({
608
+ account_number: zod.z.string(),
609
+ account_holder_name: zod.z.string(),
610
+ bank_code: zod.z.string(),
611
+ account_type: zod.z.string().optional(),
612
+ currency: CurrencySchema
613
+ });
614
+ // E-wallet Properties
615
+ const EwalletPropertiesSchema = zod.z.object({
616
+ account_details: zod.z.string(),
617
+ currency: CurrencySchema
618
+ });
619
+ // Payment Method Properties (Discriminated Union)
620
+ zod.z.discriminatedUnion("type", [
621
+ zod.z.object({
622
+ type: zod.z.literal("CARD"),
623
+ card: CardPropertiesSchema
624
+ }),
625
+ zod.z.object({
626
+ type: zod.z.literal("BANK_ACCOUNT"),
627
+ bank_account: BankAccountPropertiesSchema
628
+ }),
629
+ zod.z.object({
630
+ type: zod.z.literal("EWALLET"),
631
+ ewallet: EwalletPropertiesSchema
632
+ })
633
+ ]);
634
+ // Create Payment Method Request
635
+ const CreatePaymentMethodSchema = zod.z.object({
636
+ type: PaymentMethodTypeSchema,
637
+ country: CountrySchema.optional(),
638
+ reusability: zod.z.union([
639
+ zod.z.literal("ONE_TIME_USE"),
640
+ zod.z.literal("MULTIPLE_USE")
641
+ ]),
642
+ description: zod.z.string().optional(),
643
+ reference_id: zod.z.string().optional(),
644
+ metadata: zod.z.record(zod.z.unknown()).optional(),
645
+ // Specific properties based on type
646
+ card: zod.z.object({
647
+ currency: CurrencySchema.optional(),
648
+ channel_properties: zod.z.object({
649
+ success_return_url: zod.z.string().url().optional(),
650
+ failure_return_url: zod.z.string().url().optional()
651
+ }).optional()
652
+ }).optional(),
653
+ bank_account: zod.z.object({
654
+ currency: CurrencySchema,
655
+ channel_properties: zod.z.object({
656
+ account_mobile_number: zod.z.string().optional(),
657
+ card_last_four: zod.z.string().optional(),
658
+ card_expiry_month: zod.z.string().optional(),
659
+ card_expiry_year: zod.z.string().optional(),
660
+ account_email: zod.z.string().email().optional()
661
+ }).optional()
662
+ }).optional(),
663
+ ewallet: zod.z.object({
664
+ channel_code: zod.z.string(),
665
+ channel_properties: zod.z.object({
666
+ success_return_url: zod.z.string().url().optional(),
667
+ failure_return_url: zod.z.string().url().optional(),
668
+ cancel_return_url: zod.z.string().url().optional()
669
+ }).optional()
670
+ }).optional()
671
+ });
672
+ // Update Payment Method Request
673
+ const UpdatePaymentMethodSchema = zod.z.object({
674
+ description: zod.z.string().optional(),
675
+ reference_id: zod.z.string().optional(),
676
+ status: PaymentMethodStatusSchema.optional(),
677
+ metadata: zod.z.record(zod.z.unknown()).optional()
678
+ });
679
+ // Payment Method Resource
680
+ const PaymentMethodResourceSchema = zod.z.object({
681
+ id: zod.z.string(),
682
+ type: PaymentMethodTypeSchema,
683
+ country: CountrySchema.optional(),
684
+ business_id: zod.z.string(),
685
+ customer_id: zod.z.string().optional(),
686
+ reference_id: zod.z.string().optional(),
687
+ description: zod.z.string().optional(),
688
+ status: PaymentMethodStatusSchema,
689
+ reusability: zod.z.union([
690
+ zod.z.literal("ONE_TIME_USE"),
691
+ zod.z.literal("MULTIPLE_USE")
692
+ ]),
693
+ actions: zod.z.array(zod.z.object({
694
+ action: zod.z.string(),
695
+ url: zod.z.string().url().optional(),
696
+ url_type: zod.z.string().optional(),
697
+ method: zod.z.string().optional()
698
+ })).optional(),
699
+ metadata: zod.z.record(zod.z.unknown()).optional(),
700
+ billing_information: zod.z.object({
701
+ country: CountrySchema.optional(),
702
+ street_line1: zod.z.string().optional(),
703
+ street_line2: zod.z.string().optional(),
704
+ city: zod.z.string().optional(),
705
+ province_state: zod.z.string().optional(),
706
+ postal_code: zod.z.string().optional()
707
+ }).optional(),
708
+ failure_code: zod.z.string().nullable().optional(),
709
+ created: zod.z.string().datetime(),
710
+ updated: zod.z.string().datetime(),
711
+ // Type-specific properties
712
+ card: CardPropertiesSchema.optional(),
713
+ bank_account: BankAccountPropertiesSchema.optional(),
714
+ ewallet: EwalletPropertiesSchema.optional()
715
+ });
716
+ // Get Payment Method Request
717
+ const GetPaymentMethodSchema = zod.z.object({
718
+ id: zod.z.string()
719
+ });
720
+ // List Payment Methods Request
721
+ const ListPaymentMethodsSchema = zod.z.object({
722
+ id: zod.z.array(zod.z.string()).optional(),
723
+ type: zod.z.array(PaymentMethodTypeSchema).optional(),
724
+ status: zod.z.array(PaymentMethodStatusSchema).optional(),
725
+ reusability: zod.z.union([
726
+ zod.z.literal("ONE_TIME_USE"),
727
+ zod.z.literal("MULTIPLE_USE")
728
+ ]).optional(),
729
+ customer_id: zod.z.string().optional(),
730
+ reference_id: zod.z.string().optional(),
731
+ after_id: zod.z.string().optional(),
732
+ before_id: zod.z.string().optional(),
733
+ limit: zod.z.number().min(1).max(100).default(10).optional()
734
+ });
735
+ // List Payment Methods Response
736
+ zod.z.object({
737
+ data: zod.z.array(PaymentMethodResourceSchema),
738
+ has_more: zod.z.boolean(),
739
+ links: zod.z.object({
740
+ href: zod.z.string(),
741
+ rel: zod.z.string(),
742
+ method: zod.z.string()
743
+ }).array()
744
+ });
745
+ // Update Payment Method Params
746
+ const UpdatePaymentMethodParamsSchema = zod.z.object({
747
+ id: zod.z.string(),
748
+ payload: UpdatePaymentMethodSchema
749
+ });
750
+
751
+ const createPaymentMethod = async (params, axiosInstance, config)=>{
752
+ try {
753
+ const validatedParams = validateInput(CreatePaymentMethodSchema, params, "payment method params");
754
+ const response = await axiosInstance.post(config?.url ?? "/v2/payment_methods", validatedParams, config);
755
+ // Note: Actual API response handling - relaxed validation for production flexibility
756
+ return response.data;
757
+ } catch (error) {
758
+ if (error instanceof Error && error.name === "AxiosError") {
759
+ handleAxiosError(error);
760
+ }
761
+ throw error;
762
+ }
763
+ };
764
+ const getPaymentMethod = async (params, axiosInstance, config)=>{
765
+ try {
766
+ const validatedParams = validateInput(GetPaymentMethodSchema, params, "get payment method params");
767
+ const response = await axiosInstance.get(config?.url ?? `/v2/payment_methods/${validatedParams.id}`, config);
768
+ return response.data;
769
+ } catch (error) {
770
+ if (error instanceof Error && error.name === "AxiosError") {
771
+ handleAxiosError(error);
772
+ }
773
+ throw error;
774
+ }
775
+ };
776
+ const listPaymentMethods = async (params, axiosInstance, config)=>{
777
+ try {
778
+ const validatedParams = params ? validateInput(ListPaymentMethodsSchema, params, "list payment methods params") : {};
779
+ const queryParams = new URLSearchParams();
780
+ if (validatedParams.id) {
781
+ validatedParams.id.forEach((id)=>queryParams.append("id[]", id));
782
+ }
783
+ if (validatedParams.type) {
784
+ validatedParams.type.forEach((type)=>queryParams.append("type[]", type));
785
+ }
786
+ if (validatedParams.status) {
787
+ validatedParams.status.forEach((status)=>queryParams.append("status[]", status));
788
+ }
789
+ if (validatedParams.reusability) {
790
+ queryParams.append("reusability", validatedParams.reusability);
791
+ }
792
+ if (validatedParams.customer_id) {
793
+ queryParams.append("customer_id", validatedParams.customer_id);
794
+ }
795
+ if (validatedParams.reference_id) {
796
+ queryParams.append("reference_id", validatedParams.reference_id);
797
+ }
798
+ if (validatedParams.after_id) {
799
+ queryParams.append("after_id", validatedParams.after_id);
800
+ }
801
+ if (validatedParams.before_id) {
802
+ queryParams.append("before_id", validatedParams.before_id);
803
+ }
804
+ if (validatedParams.limit) {
805
+ queryParams.append("limit", validatedParams.limit.toString());
806
+ }
807
+ const queryString = queryParams.toString();
808
+ const url = queryString ? `/v2/payment_methods?${queryString}` : "/v2/payment_methods";
809
+ const response = await axiosInstance.get(config?.url ?? url, config);
810
+ return response.data;
811
+ } catch (error) {
812
+ if (error instanceof Error && error.name === "AxiosError") {
813
+ handleAxiosError(error);
814
+ }
815
+ throw error;
816
+ }
817
+ };
818
+ const updatePaymentMethod = async (params, axiosInstance, config)=>{
819
+ try {
820
+ const validatedParams = validateInput(UpdatePaymentMethodParamsSchema, params, "update payment method params");
821
+ const response = await axiosInstance.patch(config?.url ?? `/v2/payment_methods/${validatedParams.id}`, validatedParams.payload, config);
822
+ return response.data;
823
+ } catch (error) {
824
+ if (error instanceof Error && error.name === "AxiosError") {
825
+ handleAxiosError(error);
826
+ }
827
+ throw error;
828
+ }
829
+ };
830
+
831
+ // Invoice Status
832
+ const InvoiceStatusSchema = zod.z.union([
833
+ zod.z.literal("PENDING"),
834
+ zod.z.literal("PAID"),
835
+ zod.z.literal("SETTLED"),
836
+ zod.z.literal("EXPIRED")
837
+ ]);
838
+ // Payer Email
839
+ zod.z.object({
840
+ email: zod.z.string().email()
841
+ });
842
+ // Invoice Item
843
+ const InvoiceItemSchema = zod.z.object({
844
+ name: zod.z.string(),
845
+ quantity: zod.z.number().positive(),
846
+ price: zod.z.number().positive(),
847
+ category: zod.z.string().optional(),
848
+ url: zod.z.string().url().optional()
849
+ });
850
+ // Customer Notification Preference
851
+ const CustomerNotificationPreferenceSchema = zod.z.object({
852
+ invoice_created: zod.z.array(zod.z.union([
853
+ zod.z.literal("whatsapp"),
854
+ zod.z.literal("sms"),
855
+ zod.z.literal("email")
856
+ ])).optional(),
857
+ invoice_reminder: zod.z.array(zod.z.union([
858
+ zod.z.literal("whatsapp"),
859
+ zod.z.literal("sms"),
860
+ zod.z.literal("email")
861
+ ])).optional(),
862
+ invoice_paid: zod.z.array(zod.z.union([
863
+ zod.z.literal("whatsapp"),
864
+ zod.z.literal("sms"),
865
+ zod.z.literal("email")
866
+ ])).optional(),
867
+ invoice_expired: zod.z.array(zod.z.union([
868
+ zod.z.literal("whatsapp"),
869
+ zod.z.literal("sms"),
870
+ zod.z.literal("email")
871
+ ])).optional()
872
+ });
873
+ // Customer Details
874
+ const CustomerDetailsSchema = zod.z.object({
875
+ customer_name: zod.z.string().optional(),
876
+ customer_email: zod.z.string().email().optional(),
877
+ customer_phone: zod.z.string().optional(),
878
+ billing_address: zod.z.object({
879
+ first_name: zod.z.string().optional(),
880
+ last_name: zod.z.string().optional(),
881
+ address: zod.z.string().optional(),
882
+ city: zod.z.string().optional(),
883
+ postal_code: zod.z.string().optional(),
884
+ phone: zod.z.string().optional(),
885
+ country_code: CountrySchema.optional()
886
+ }).optional(),
887
+ shipping_address: zod.z.object({
888
+ first_name: zod.z.string().optional(),
889
+ last_name: zod.z.string().optional(),
890
+ address: zod.z.string().optional(),
891
+ city: zod.z.string().optional(),
892
+ postal_code: zod.z.string().optional(),
893
+ phone: zod.z.string().optional(),
894
+ country_code: CountrySchema.optional()
895
+ }).optional()
896
+ });
897
+ // Fee Details
898
+ const FeeSchema = zod.z.object({
899
+ type: zod.z.string(),
900
+ value: zod.z.number()
901
+ });
902
+ // Available Bank and E-wallet
903
+ const AvailableBankSchema = zod.z.object({
904
+ bank_code: zod.z.string(),
905
+ collection_type: zod.z.string(),
906
+ bank_branch: zod.z.string(),
907
+ transfer_amount: zod.z.number(),
908
+ bank_account_number: zod.z.string(),
909
+ account_holder_name: zod.z.string(),
910
+ identity_amount: zod.z.number().optional()
911
+ });
912
+ const AvailableEwalletSchema = zod.z.object({
913
+ ewallet_type: zod.z.string()
914
+ });
915
+ const AvailableRetailOutletSchema = zod.z.object({
916
+ retail_outlet_name: zod.z.string()
917
+ });
918
+ // Create Invoice Schema
919
+ const CreateInvoiceSchema = zod.z.object({
920
+ external_id: zod.z.string(),
921
+ payer_email: zod.z.string().email(),
922
+ description: zod.z.string(),
923
+ amount: zod.z.number().positive(),
924
+ invoice_duration: zod.z.number().positive().optional(),
925
+ callback_virtual_account_id: zod.z.string().optional(),
926
+ should_exclude_credit_card: zod.z.boolean().optional(),
927
+ should_send_email: zod.z.boolean().optional(),
928
+ customer_name: zod.z.string().optional(),
929
+ customer_email: zod.z.string().email().optional(),
930
+ customer_phone: zod.z.string().optional(),
931
+ customer: CustomerDetailsSchema.optional(),
932
+ customer_notification_preference: CustomerNotificationPreferenceSchema.optional(),
933
+ success_redirect_url: zod.z.string().url().optional(),
934
+ failure_redirect_url: zod.z.string().url().optional(),
935
+ payment_methods: zod.z.array(zod.z.string()).optional(),
936
+ mid_label: zod.z.string().optional(),
937
+ should_authenticate_credit_card: zod.z.boolean().optional(),
938
+ currency: CurrencySchema.optional(),
939
+ items: zod.z.array(InvoiceItemSchema).optional(),
940
+ fixed_va: zod.z.boolean().optional(),
941
+ reminder_time_unit: zod.z.union([
942
+ zod.z.literal("days"),
943
+ zod.z.literal("hours"),
944
+ zod.z.literal("minutes")
945
+ ]).optional(),
946
+ reminder_time: zod.z.number().optional(),
947
+ locale: zod.z.string().optional(),
948
+ fees: zod.z.array(FeeSchema).optional(),
949
+ metadata: zod.z.record(zod.z.unknown()).optional()
950
+ });
951
+ // Update Invoice Schema
952
+ const UpdateInvoiceSchema = zod.z.object({
953
+ should_send_email: zod.z.boolean().optional(),
954
+ customer_name: zod.z.string().optional(),
955
+ customer_email: zod.z.string().email().optional(),
956
+ customer_phone: zod.z.string().optional(),
957
+ customer: CustomerDetailsSchema.optional(),
958
+ customer_notification_preference: CustomerNotificationPreferenceSchema.optional(),
959
+ success_redirect_url: zod.z.string().url().optional(),
960
+ failure_redirect_url: zod.z.string().url().optional(),
961
+ items: zod.z.array(InvoiceItemSchema).optional(),
962
+ metadata: zod.z.record(zod.z.unknown()).optional()
963
+ });
964
+ // Invoice Resource
965
+ const InvoiceResourceSchema = zod.z.object({
966
+ id: zod.z.string(),
967
+ external_id: zod.z.string(),
968
+ user_id: zod.z.string(),
969
+ status: InvoiceStatusSchema,
970
+ merchant_name: zod.z.string(),
971
+ merchant_profile_picture_url: zod.z.string().url(),
972
+ amount: zod.z.number(),
973
+ payer_email: zod.z.string().email(),
974
+ description: zod.z.string(),
975
+ expiry_date: zod.z.string().datetime(),
976
+ invoice_url: zod.z.string().url(),
977
+ should_exclude_credit_card: zod.z.boolean(),
978
+ should_send_email: zod.z.boolean(),
979
+ created: zod.z.string().datetime(),
980
+ updated: zod.z.string().datetime(),
981
+ currency: CurrencySchema,
982
+ paid_amount: zod.z.number().optional(),
983
+ credit_card_charge_id: zod.z.string().optional(),
984
+ payment_method: zod.z.string().optional(),
985
+ payment_channel: zod.z.string().optional(),
986
+ payment_destination: zod.z.string().optional(),
987
+ payment_id: zod.z.string().optional(),
988
+ paid_at: zod.z.string().datetime().optional(),
989
+ bank_code: zod.z.string().optional(),
990
+ ewallet_type: zod.z.string().optional(),
991
+ on_demand_link: zod.z.string().url().optional(),
992
+ recurring_payment_id: zod.z.string().optional(),
993
+ // Customer information
994
+ customer_name: zod.z.string().optional(),
995
+ customer_email: zod.z.string().email().optional(),
996
+ customer_phone: zod.z.string().optional(),
997
+ customer: CustomerDetailsSchema.optional(),
998
+ customer_notification_preference: CustomerNotificationPreferenceSchema.optional(),
999
+ // URLs
1000
+ success_redirect_url: zod.z.string().url().optional(),
1001
+ failure_redirect_url: zod.z.string().url().optional(),
1002
+ // Items and fees
1003
+ items: zod.z.array(InvoiceItemSchema).optional(),
1004
+ fees: zod.z.array(FeeSchema).optional(),
1005
+ // Available payment methods
1006
+ available_banks: zod.z.array(AvailableBankSchema).optional(),
1007
+ available_ewallets: zod.z.array(AvailableEwalletSchema).optional(),
1008
+ available_retail_outlets: zod.z.array(AvailableRetailOutletSchema).optional(),
1009
+ available_paylaters: zod.z.array(zod.z.object({
1010
+ paylater_type: zod.z.string()
1011
+ })).optional(),
1012
+ available_qr_codes: zod.z.array(zod.z.object({
1013
+ qr_code_type: zod.z.string()
1014
+ })).optional(),
1015
+ available_direct_debits: zod.z.array(zod.z.object({
1016
+ direct_debit_type: zod.z.string()
1017
+ })).optional(),
1018
+ should_authenticate_credit_card: zod.z.boolean().optional(),
1019
+ metadata: zod.z.record(zod.z.unknown()).optional()
1020
+ });
1021
+ // Get Invoice Schema
1022
+ const GetInvoiceSchema = zod.z.object({
1023
+ invoice_id: zod.z.string()
1024
+ });
1025
+ // List Invoices Schema
1026
+ const ListInvoicesSchema = zod.z.object({
1027
+ statuses: zod.z.array(InvoiceStatusSchema).optional(),
1028
+ limit: zod.z.number().min(1).max(100).optional(),
1029
+ created_after: zod.z.string().datetime().optional(),
1030
+ created_before: zod.z.string().datetime().optional(),
1031
+ paid_after: zod.z.string().datetime().optional(),
1032
+ paid_before: zod.z.string().datetime().optional(),
1033
+ expired_after: zod.z.string().datetime().optional(),
1034
+ expired_before: zod.z.string().datetime().optional(),
1035
+ last_invoice: zod.z.string().optional(),
1036
+ client_types: zod.z.array(zod.z.string()).optional(),
1037
+ payment_channels: zod.z.array(zod.z.string()).optional(),
1038
+ on_demand_link: zod.z.string().optional(),
1039
+ recurring_payment_id: zod.z.string().optional()
1040
+ });
1041
+ // List Invoices Response
1042
+ zod.z.object({
1043
+ has_more: zod.z.boolean(),
1044
+ data: zod.z.array(InvoiceResourceSchema)
1045
+ });
1046
+ // Update Invoice Params
1047
+ const UpdateInvoiceParamsSchema = zod.z.object({
1048
+ invoice_id: zod.z.string(),
1049
+ payload: UpdateInvoiceSchema
1050
+ });
1051
+ // Expire Invoice Schema
1052
+ const ExpireInvoiceSchema = zod.z.object({
1053
+ invoice_id: zod.z.string()
1054
+ });
1055
+
1056
+ const createInvoice = async (params, axiosInstance, config)=>{
1057
+ try {
1058
+ const validatedParams = validateInput(CreateInvoiceSchema, params, "invoice params");
1059
+ const response = await axiosInstance.post(config?.url ?? "/v2/invoices", validatedParams, config);
1060
+ return response.data;
1061
+ } catch (error) {
1062
+ if (error instanceof Error && error.name === "AxiosError") {
1063
+ handleAxiosError(error);
1064
+ }
1065
+ throw error;
1066
+ }
1067
+ };
1068
+ const getInvoice = async (params, axiosInstance, config)=>{
1069
+ try {
1070
+ const validatedParams = validateInput(GetInvoiceSchema, params, "get invoice params");
1071
+ const response = await axiosInstance.get(config?.url ?? `/v2/invoices/${validatedParams.invoice_id}`, config);
1072
+ return response.data;
1073
+ } catch (error) {
1074
+ if (error instanceof Error && error.name === "AxiosError") {
1075
+ handleAxiosError(error);
1076
+ }
1077
+ throw error;
1078
+ }
1079
+ };
1080
+ const listInvoices = async (params, axiosInstance, config)=>{
1081
+ try {
1082
+ const validatedParams = params ? validateInput(ListInvoicesSchema, params, "list invoices params") : {};
1083
+ const queryParams = new URLSearchParams();
1084
+ if (validatedParams.statuses) {
1085
+ validatedParams.statuses.forEach((status)=>queryParams.append("statuses[]", status));
1086
+ }
1087
+ if (validatedParams.limit) {
1088
+ queryParams.append("limit", validatedParams.limit.toString());
1089
+ }
1090
+ if (validatedParams.created_after) {
1091
+ queryParams.append("created_after", validatedParams.created_after);
1092
+ }
1093
+ if (validatedParams.created_before) {
1094
+ queryParams.append("created_before", validatedParams.created_before);
1095
+ }
1096
+ if (validatedParams.paid_after) {
1097
+ queryParams.append("paid_after", validatedParams.paid_after);
1098
+ }
1099
+ if (validatedParams.paid_before) {
1100
+ queryParams.append("paid_before", validatedParams.paid_before);
1101
+ }
1102
+ if (validatedParams.expired_after) {
1103
+ queryParams.append("expired_after", validatedParams.expired_after);
1104
+ }
1105
+ if (validatedParams.expired_before) {
1106
+ queryParams.append("expired_before", validatedParams.expired_before);
1107
+ }
1108
+ if (validatedParams.last_invoice) {
1109
+ queryParams.append("last_invoice", validatedParams.last_invoice);
1110
+ }
1111
+ if (validatedParams.client_types) {
1112
+ validatedParams.client_types.forEach((type)=>queryParams.append("client_types[]", type));
1113
+ }
1114
+ if (validatedParams.payment_channels) {
1115
+ validatedParams.payment_channels.forEach((channel)=>queryParams.append("payment_channels[]", channel));
1116
+ }
1117
+ if (validatedParams.on_demand_link) {
1118
+ queryParams.append("on_demand_link", validatedParams.on_demand_link);
1119
+ }
1120
+ if (validatedParams.recurring_payment_id) {
1121
+ queryParams.append("recurring_payment_id", validatedParams.recurring_payment_id);
1122
+ }
1123
+ const queryString = queryParams.toString();
1124
+ const url = queryString ? `/v2/invoices?${queryString}` : "/v2/invoices";
1125
+ const response = await axiosInstance.get(config?.url ?? url, config);
1126
+ return response.data;
1127
+ } catch (error) {
1128
+ if (error instanceof Error && error.name === "AxiosError") {
1129
+ handleAxiosError(error);
1130
+ }
1131
+ throw error;
1132
+ }
1133
+ };
1134
+ const updateInvoice = async (params, axiosInstance, config)=>{
1135
+ try {
1136
+ const validatedParams = validateInput(UpdateInvoiceParamsSchema, params, "update invoice params");
1137
+ const response = await axiosInstance.patch(config?.url ?? `/v2/invoices/${validatedParams.invoice_id}`, validatedParams.payload, config);
1138
+ return response.data;
1139
+ } catch (error) {
1140
+ if (error instanceof Error && error.name === "AxiosError") {
1141
+ handleAxiosError(error);
1142
+ }
1143
+ throw error;
1144
+ }
1145
+ };
1146
+ const expireInvoice = async (params, axiosInstance, config)=>{
1147
+ try {
1148
+ const validatedParams = validateInput(ExpireInvoiceSchema, params, "expire invoice params");
1149
+ const response = await axiosInstance.post(config?.url ?? `/invoices/${validatedParams.invoice_id}/expire`, {}, config);
1150
+ return response.data;
1151
+ } catch (error) {
1152
+ if (error instanceof Error && error.name === "AxiosError") {
1153
+ handleAxiosError(error);
1154
+ }
1155
+ throw error;
1156
+ }
1157
+ };
1158
+
1159
+ const btoa = (string)=>{
1160
+ if (typeof window === "undefined") {
1161
+ return Buffer.from(string).toString("base64");
1162
+ }
1163
+ return window.btoa(string);
1164
+ };
1165
+ const createFn = (fn, axiosInstance)=>{
1166
+ return (data)=>fn(data, axiosInstance);
1167
+ };
1168
+ const Xendit = (key, options = {})=>{
1169
+ const axiosInstance = createAxiosInstance({
1170
+ headers: {
1171
+ Authorization: `Basic ${btoa(key + ":")}`
1172
+ }
1173
+ });
1174
+ // Apply rate limiting if configured
1175
+ if (options.rateLimit) {
1176
+ setupRateLimit(axiosInstance, options.rateLimit);
1177
+ }
1178
+ if (key.includes("development")) {
1179
+ console.log("👾 You are on → TEST MODE");
1180
+ }
1181
+ return {
1182
+ customer: {
1183
+ create: createFn(createCustomer, axiosInstance),
1184
+ getById: createFn(getCustomerId, axiosInstance),
1185
+ getByRefId: createFn(getCustomerRefId, axiosInstance),
1186
+ update: createFn(updateCustomer, axiosInstance)
1187
+ },
1188
+ ewallet: {
1189
+ charge: createFn(createEwalletCharge, axiosInstance),
1190
+ get: createFn(getEwalletCharge, axiosInstance)
1191
+ },
1192
+ paymentMethod: {
1193
+ create: createFn(createPaymentMethod, axiosInstance),
1194
+ get: createFn(getPaymentMethod, axiosInstance),
1195
+ list: (params)=>listPaymentMethods(params, axiosInstance),
1196
+ update: createFn(updatePaymentMethod, axiosInstance)
1197
+ },
1198
+ invoice: {
1199
+ create: createFn(createInvoice, axiosInstance),
1200
+ get: createFn(getInvoice, axiosInstance),
1201
+ list: (params)=>listInvoices(params, axiosInstance),
1202
+ update: createFn(updateInvoice, axiosInstance),
1203
+ expire: createFn(expireInvoice, axiosInstance)
1204
+ }
1205
+ };
1206
+ };
1207
+
1208
+ // Type guard for phone numbers
1209
+ function isValidPhone(value) {
1210
+ return typeof value === "string" && value.startsWith("+") && value.length >= 8 && value.length <= 15;
1211
+ }
1212
+ // Type guard for country codes
1213
+ function isValidCountry(value) {
1214
+ return [
1215
+ "PH",
1216
+ "ID",
1217
+ "MY",
1218
+ "TH",
1219
+ "VN"
1220
+ ].includes(value);
1221
+ }
1222
+ // Type guard for currency codes
1223
+ function isValidCurrency(value) {
1224
+ return [
1225
+ "PHP",
1226
+ "IDR",
1227
+ "MYR",
1228
+ "THB",
1229
+ "VND"
1230
+ ].includes(value);
1231
+ }
1232
+ // Type guard for customer type
1233
+ function isValidCustomerType(value) {
1234
+ return [
1235
+ "INDIVIDUAL",
1236
+ "BUSINESS"
1237
+ ].includes(value);
1238
+ }
1239
+ // Type guard for checkout method
1240
+ function isValidCheckoutMethod(value) {
1241
+ return [
1242
+ "ONE_TIME_PAYMENT",
1243
+ "TOKENIZED_PAYMENT"
1244
+ ].includes(value);
1245
+ }
1246
+ // Generic type guard for checking if value is not null or undefined
1247
+ function isNotNullOrUndefined(value) {
1248
+ return value !== null && value !== undefined;
1249
+ }
1250
+ // Type guard for checking if value is a valid URL
1251
+ function isValidUrl(value) {
1252
+ try {
1253
+ new URL(value);
1254
+ return true;
1255
+ } catch {
1256
+ return false;
1257
+ }
1258
+ }
1259
+ // Type guard for checking if value is a valid email
1260
+ function isValidEmail(value) {
1261
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1262
+ return emailRegex.test(value);
1263
+ }
1264
+ // Type guard for checking if value is a valid date string
1265
+ function isValidDateString(value) {
1266
+ const date = new Date(value);
1267
+ return !isNaN(date.getTime()) && value === date.toISOString();
1268
+ }
1269
+
1270
+ // Webhook Event Types
1271
+ const WebhookEventTypeSchema = zod.z.union([
1272
+ zod.z.literal("invoice.paid"),
1273
+ zod.z.literal("invoice.expired"),
1274
+ zod.z.literal("payment.succeeded"),
1275
+ zod.z.literal("payment.failed"),
1276
+ zod.z.literal("ewallet.charge.succeeded"),
1277
+ zod.z.literal("ewallet.charge.pending"),
1278
+ zod.z.literal("ewallet.charge.failed"),
1279
+ zod.z.literal("payment_method.activate"),
1280
+ zod.z.literal("payment_method.expire"),
1281
+ zod.z.literal("customer.created"),
1282
+ zod.z.literal("customer.updated")
1283
+ ]);
1284
+ // Base Webhook Event Schema
1285
+ const WebhookEventSchema = zod.z.object({
1286
+ id: zod.z.string(),
1287
+ event: WebhookEventTypeSchema,
1288
+ api_version: zod.z.string(),
1289
+ created: zod.z.string().datetime(),
1290
+ business_id: zod.z.string(),
1291
+ data: zod.z.record(zod.z.unknown())
1292
+ });
1293
+ /**
1294
+ * Verify webhook signature from Xendit
1295
+ * @param options Verification options
1296
+ * @returns true if signature is valid, false otherwise
1297
+ */ function verifyWebhookSignature(options) {
1298
+ const { callbackToken, receivedToken } = options;
1299
+ // Simple token comparison for Xendit webhooks
1300
+ return callbackToken === receivedToken;
1301
+ }
1302
+ /**
1303
+ * Advanced webhook signature verification using HMAC
1304
+ * @param options Verification options with HMAC
1305
+ * @returns true if signature is valid, false otherwise
1306
+ */ function verifyWebhookHmac(options) {
1307
+ const { secret, requestBody, signature } = options;
1308
+ const body = typeof requestBody === "string" ? requestBody : requestBody.toString("utf8");
1309
+ const expectedSignature = crypto.createHmac("sha256", secret).update(body).digest("hex");
1310
+ return `sha256=${expectedSignature}` === signature;
1311
+ }
1312
+ /**
1313
+ * Parse and validate webhook event
1314
+ * @param rawEvent Raw webhook event data
1315
+ * @returns Parsed and validated webhook event
1316
+ */ function parseWebhookEvent(rawEvent) {
1317
+ const result = WebhookEventSchema.safeParse(rawEvent);
1318
+ if (!result.success) {
1319
+ throw new Error(`Invalid webhook event format: ${result.error.message}`);
1320
+ }
1321
+ return result.data;
1322
+ }
1323
+ /**
1324
+ * Handle webhook events with type-safe handlers
1325
+ * @param event Webhook event
1326
+ * @param handlers Event handlers
1327
+ */ async function handleWebhookEvent(event, handlers) {
1328
+ const handler = handlers[event.event];
1329
+ if (handler) {
1330
+ await handler(event);
1331
+ } else {
1332
+ console.warn(`No handler found for webhook event: ${event.event}`);
1333
+ }
1334
+ }
1335
+ /**
1336
+ * Create a webhook processor with built-in verification
1337
+ */ function createWebhookProcessor(options) {
1338
+ return {
1339
+ /**
1340
+ * Process a webhook request
1341
+ */ async processWebhook (requestBody, headers, handlers) {
1342
+ try {
1343
+ // Verify signature
1344
+ if (options.callbackToken) {
1345
+ const receivedToken = headers["x-callback-token"] || headers["X-Callback-Token"];
1346
+ if (!receivedToken || !verifyWebhookSignature({
1347
+ callbackToken: options.callbackToken,
1348
+ requestBody,
1349
+ receivedToken
1350
+ })) {
1351
+ return {
1352
+ success: false,
1353
+ error: "Invalid webhook signature"
1354
+ };
1355
+ }
1356
+ }
1357
+ if (options.hmacSecret) {
1358
+ const signature = headers["x-xendit-signature"] || headers["X-Xendit-Signature"];
1359
+ if (!signature || !verifyWebhookHmac({
1360
+ secret: options.hmacSecret,
1361
+ requestBody,
1362
+ signature
1363
+ })) {
1364
+ return {
1365
+ success: false,
1366
+ error: "Invalid HMAC signature"
1367
+ };
1368
+ }
1369
+ }
1370
+ // Parse event
1371
+ const body = typeof requestBody === "string" ? requestBody : requestBody.toString("utf8");
1372
+ const rawEvent = JSON.parse(body);
1373
+ const event = parseWebhookEvent(rawEvent);
1374
+ // Handle event
1375
+ await handleWebhookEvent(event, handlers);
1376
+ return {
1377
+ success: true
1378
+ };
1379
+ } catch (error) {
1380
+ return {
1381
+ success: false,
1382
+ error: error instanceof Error ? error.message : "Unknown error"
1383
+ };
1384
+ }
1385
+ }
1386
+ };
1387
+ }
1388
+
1389
+ // Generic pagination response schema
1390
+ const PaginationMetaSchema = zod.z.object({
1391
+ has_more: zod.z.boolean(),
1392
+ after_id: zod.z.string().optional(),
1393
+ before_id: zod.z.string().optional(),
1394
+ total_count: zod.z.number().optional()
1395
+ });
1396
+ const PaginatedResponseSchema = (itemSchema)=>zod.z.object({
1397
+ data: zod.z.array(itemSchema),
1398
+ has_more: zod.z.boolean(),
1399
+ after_id: zod.z.string().optional(),
1400
+ before_id: zod.z.string().optional(),
1401
+ total_count: zod.z.number().optional()
1402
+ });
1403
+ /**
1404
+ * Helper to build pagination query parameters
1405
+ */ function buildPaginationParams(options) {
1406
+ const params = {};
1407
+ if (options.limit) {
1408
+ params.limit = options.limit.toString();
1409
+ }
1410
+ if (options.after_id) {
1411
+ params.after_id = options.after_id;
1412
+ }
1413
+ if (options.before_id) {
1414
+ params.before_id = options.before_id;
1415
+ }
1416
+ return params;
1417
+ }
1418
+ /**
1419
+ * Generic paginated API fetcher
1420
+ */ async function fetchPaginated(axiosInstance, endpoint, itemSchema, options = {}) {
1421
+ const params = buildPaginationParams(options);
1422
+ const response = await axiosInstance.get(endpoint, {
1423
+ params
1424
+ });
1425
+ const paginatedSchema = PaginatedResponseSchema(itemSchema);
1426
+ const result = paginatedSchema.parse(response.data);
1427
+ return result;
1428
+ }
1429
+ /**
1430
+ * Auto-paginate through all pages and return all items
1431
+ */ async function fetchAllPages(axiosInstance, endpoint, itemSchema, options = {}) {
1432
+ const { limit = 10, maxPages = 100, maxItems = Infinity, ...paginationOptions } = options;
1433
+ let allItems = [];
1434
+ let currentAfter = paginationOptions.after_id;
1435
+ let pageCount = 0;
1436
+ while(pageCount < maxPages && allItems.length < maxItems){
1437
+ const response = await fetchPaginated(axiosInstance, endpoint, itemSchema, {
1438
+ ...paginationOptions,
1439
+ limit,
1440
+ after_id: currentAfter
1441
+ });
1442
+ allItems = allItems.concat(response.data);
1443
+ pageCount++;
1444
+ // Stop if we've reached the maxItems limit
1445
+ if (allItems.length >= maxItems) {
1446
+ allItems = allItems.slice(0, maxItems);
1447
+ break;
1448
+ }
1449
+ // Stop if there are no more pages
1450
+ if (!response.has_more) {
1451
+ break;
1452
+ }
1453
+ // Update cursor for next page
1454
+ currentAfter = response.after_id;
1455
+ }
1456
+ return allItems;
1457
+ }
1458
+ /**
1459
+ * Create a paginator iterator for streaming through pages
1460
+ */ function createPaginator(axiosInstance, endpoint, itemSchema, initialOptions = {}) {
1461
+ let currentOptions = {
1462
+ ...initialOptions
1463
+ };
1464
+ let exhausted = false;
1465
+ return {
1466
+ /**
1467
+ * Get the next page
1468
+ */ async next () {
1469
+ if (exhausted) {
1470
+ return {
1471
+ value: {},
1472
+ done: true
1473
+ };
1474
+ }
1475
+ const response = await fetchPaginated(axiosInstance, endpoint, itemSchema, currentOptions);
1476
+ // Update options for next call
1477
+ if (response.has_more && response.after_id) {
1478
+ currentOptions.after_id = response.after_id;
1479
+ } else {
1480
+ exhausted = true;
1481
+ }
1482
+ return {
1483
+ value: response,
1484
+ done: !response.has_more
1485
+ };
1486
+ },
1487
+ /**
1488
+ * Reset the paginator to start from the beginning
1489
+ */ reset (options = {}) {
1490
+ currentOptions = {
1491
+ ...initialOptions,
1492
+ ...options
1493
+ };
1494
+ exhausted = false;
1495
+ },
1496
+ /**
1497
+ * Check if there are more pages available
1498
+ */ hasMore () {
1499
+ return !exhausted;
1500
+ }
1501
+ };
1502
+ }
1503
+ /**
1504
+ * Async iterator for easy for-await-of usage
1505
+ */ async function* iteratePages(axiosInstance, endpoint, itemSchema, options = {}) {
1506
+ const paginator = createPaginator(axiosInstance, endpoint, itemSchema, options);
1507
+ while(paginator.hasMore()){
1508
+ const { value, done } = await paginator.next();
1509
+ if (done) break;
1510
+ yield value;
1511
+ }
1512
+ }
1513
+ /**
1514
+ * Async iterator for individual items across all pages
1515
+ */ async function* iterateItems(axiosInstance, endpoint, itemSchema, options = {}) {
1516
+ let itemCount = 0;
1517
+ const maxItems = options.maxItems || Infinity;
1518
+ for await (const page of iteratePages(axiosInstance, endpoint, itemSchema, options)){
1519
+ for (const item of page.data){
1520
+ if (itemCount >= maxItems) {
1521
+ return;
1522
+ }
1523
+ yield item;
1524
+ itemCount++;
1525
+ }
1526
+ }
1527
+ }
1528
+ function buildSearchParams(options) {
1529
+ const params = buildPaginationParams(options);
1530
+ if (options.query) {
1531
+ params.query = options.query;
1532
+ }
1533
+ if (options.sort_by) {
1534
+ params.sort_by = options.sort_by;
1535
+ }
1536
+ if (options.sort_direction) {
1537
+ params.sort_direction = options.sort_direction;
1538
+ }
1539
+ // Add filter parameters
1540
+ if (options.filters) {
1541
+ Object.entries(options.filters).forEach(([key, value])=>{
1542
+ if (value !== undefined && value !== null) {
1543
+ params[key] = String(value);
1544
+ }
1545
+ });
1546
+ }
1547
+ return params;
1548
+ }
1549
+
1550
+ exports.AuthenticationError = AuthenticationError;
1551
+ exports.NotFoundError = NotFoundError;
1552
+ exports.PaginatedResponseSchema = PaginatedResponseSchema;
1553
+ exports.PaginationMetaSchema = PaginationMetaSchema;
1554
+ exports.RateLimitError = RateLimitError;
1555
+ exports.RateLimiter = RateLimiter;
1556
+ exports.ValidationError = ValidationError;
1557
+ exports.WebhookEventSchema = WebhookEventSchema;
1558
+ exports.WebhookEventTypeSchema = WebhookEventTypeSchema;
1559
+ exports.Xendit = Xendit;
1560
+ exports.XenditApiError = XenditApiError;
1561
+ exports.XenditErrorSchema = XenditErrorSchema;
1562
+ exports.buildPaginationParams = buildPaginationParams;
1563
+ exports.buildSearchParams = buildSearchParams;
1564
+ exports.createPaginator = createPaginator;
1565
+ exports.createRateLimitInterceptor = createRateLimitInterceptor;
1566
+ exports.createRateLimitedAxios = createRateLimitedAxios;
1567
+ exports.createRetryInterceptor = createRetryInterceptor;
1568
+ exports.createWebhookProcessor = createWebhookProcessor;
1569
+ exports.fetchAllPages = fetchAllPages;
1570
+ exports.fetchPaginated = fetchPaginated;
1571
+ exports.handleAxiosError = handleAxiosError;
1572
+ exports.handleWebhookEvent = handleWebhookEvent;
1573
+ exports.isNotNullOrUndefined = isNotNullOrUndefined;
1574
+ exports.isValidCheckoutMethod = isValidCheckoutMethod;
1575
+ exports.isValidCountry = isValidCountry;
1576
+ exports.isValidCurrency = isValidCurrency;
1577
+ exports.isValidCustomerType = isValidCustomerType;
1578
+ exports.isValidDateString = isValidDateString;
1579
+ exports.isValidEmail = isValidEmail;
1580
+ exports.isValidPhone = isValidPhone;
1581
+ exports.isValidUrl = isValidUrl;
1582
+ exports.iterateItems = iterateItems;
1583
+ exports.iteratePages = iteratePages;
1584
+ exports.parseWebhookEvent = parseWebhookEvent;
1585
+ exports.setupRateLimit = setupRateLimit;
1586
+ exports.validateInput = validateInput;
1587
+ exports.verifyWebhookHmac = verifyWebhookHmac;
1588
+ exports.verifyWebhookSignature = verifyWebhookSignature;