@inkress/admin-sdk 1.1.42 → 1.1.44

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/dist/index.js CHANGED
@@ -1917,7 +1917,7 @@ const ORDER_FIELD_TYPES = {
1917
1917
  status_on: 'number',
1918
1918
  uid: 'string',
1919
1919
  cart_id: 'number',
1920
- currency_id: 'number',
1920
+ currency_code: 'string',
1921
1921
  customer_id: 'number',
1922
1922
  payment_link_id: 'number',
1923
1923
  billing_plan_id: 'number',
@@ -1945,7 +1945,7 @@ const PRODUCT_FIELD_TYPES = {
1945
1945
  tag_ids: 'array',
1946
1946
  uid: 'string',
1947
1947
  category_id: 'number',
1948
- currency_id: 'number',
1948
+ currency_code: 'string',
1949
1949
  user_id: 'number',
1950
1950
  inserted_at: 'date',
1951
1951
  updated_at: 'date',
@@ -2037,7 +2037,7 @@ const BILLING_PLAN_FIELD_TYPES = {
2037
2037
  payout_value_limit: 'number',
2038
2038
  payout_percentage_limit: 'number',
2039
2039
  uid: 'string',
2040
- currency_id: 'number',
2040
+ currency_code: 'string',
2041
2041
  payment_provider_id: 'number',
2042
2042
  inserted_at: 'date',
2043
2043
  updated_at: 'date',
@@ -2078,7 +2078,7 @@ const PAYMENT_LINK_FIELD_TYPES = {
2078
2078
  status: 'number',
2079
2079
  kind: 'number',
2080
2080
  customer_id: 'number',
2081
- currency_id: 'number',
2081
+ currency_code: 'string',
2082
2082
  order_id: 'number',
2083
2083
  inserted_at: 'date',
2084
2084
  updated_at: 'date',
@@ -2122,7 +2122,7 @@ const FINANCIAL_REQUEST_FIELD_TYPES = {
2122
2122
  merchant_id: 'number',
2123
2123
  requester_id: 'number',
2124
2124
  reviewer_id: 'number',
2125
- currency_id: 'number',
2125
+ currency_code: 'string',
2126
2126
  evidence_file_id: 'number',
2127
2127
  inserted_at: 'date',
2128
2128
  updated_at: 'date',
@@ -2216,7 +2216,6 @@ const FEE_FIELD_TYPES = {
2216
2216
  currency_code: 'string',
2217
2217
  hash: 'string',
2218
2218
  fee_set_id: 'number',
2219
- currency_id: 'number',
2220
2219
  user_id: 'number',
2221
2220
  inserted_at: 'date',
2222
2221
  updated_at: 'date',
@@ -3474,6 +3473,62 @@ class PublicResource {
3474
3473
  }
3475
3474
  }
3476
3475
 
3476
+ /**
3477
+ * KYC document requirements by entity type
3478
+ * These are the standard documents required for each type of business entity
3479
+ */
3480
+ const KYC_DOCUMENT_REQUIREMENTS = {
3481
+ personal: [
3482
+ 'Proof of Identity',
3483
+ 'Proof of Address',
3484
+ 'Proof of Bank Account Ownership',
3485
+ ],
3486
+ 'sole-trader': [
3487
+ 'Proof of Identity',
3488
+ 'Proof of Address',
3489
+ 'Proof of Bank Account Ownership',
3490
+ 'Business Certificate',
3491
+ 'Articles of Incorporation',
3492
+ ],
3493
+ llc: [
3494
+ 'Proof of Identity',
3495
+ 'Proof of Address',
3496
+ 'Proof of Bank Account Ownership',
3497
+ 'Business Certificate',
3498
+ 'Articles of Incorporation',
3499
+ 'Annual Return',
3500
+ 'Notice of Directors',
3501
+ 'Notice of Secretary',
3502
+ 'Tax Compliance Certificate',
3503
+ ],
3504
+ 'non-profit': [
3505
+ 'Proof of Identity',
3506
+ 'Proof of Address',
3507
+ 'Proof of Bank Account Ownership',
3508
+ 'Business Certificate',
3509
+ 'Articles of Incorporation',
3510
+ 'Annual Return',
3511
+ ],
3512
+ alumni: [
3513
+ 'Proof of Identity',
3514
+ 'Proof of Address',
3515
+ 'Proof of Bank Account Ownership',
3516
+ 'Business Certificate',
3517
+ 'Articles of Incorporation',
3518
+ 'Annual Return',
3519
+ ],
3520
+ other: [
3521
+ 'Proof of Identity',
3522
+ 'Proof of Address',
3523
+ 'Proof of Bank Account Ownership',
3524
+ 'Business Certificate',
3525
+ 'Articles of Incorporation',
3526
+ 'Annual Return',
3527
+ 'Notice of Directors',
3528
+ 'Notice of Secretary',
3529
+ 'Tax Compliance Certificate',
3530
+ ],
3531
+ };
3477
3532
  // Field type definitions for query validation
3478
3533
  const KYC_FIELD_TYPES = {
3479
3534
  id: 'number',
@@ -3554,6 +3609,185 @@ class KycResource {
3554
3609
  async updateBankInfo(data) {
3555
3610
  return this.client.post('/legal_requests', data);
3556
3611
  }
3612
+ // ============================================================================
3613
+ // KYC DOCUMENT REQUIREMENTS & STATUS
3614
+ // ============================================================================
3615
+ /**
3616
+ * Get required KYC documents for a specific entity type
3617
+ * This is a client-side method that doesn't make an API call
3618
+ *
3619
+ * @param entityType - The type of business entity
3620
+ * @returns Array of required document types
3621
+ *
3622
+ * @example
3623
+ * const docs = kyc.getRequiredDocuments('llc');
3624
+ * // Returns: ['Proof of Identity', 'Proof of Address', ...]
3625
+ */
3626
+ getRequiredDocuments(entityType) {
3627
+ return [...KYC_DOCUMENT_REQUIREMENTS[entityType]];
3628
+ }
3629
+ /**
3630
+ * Get all KYC document requirements (without making an API call)
3631
+ * Useful for displaying the full list in your application
3632
+ *
3633
+ * @returns Complete mapping of entity types to required documents
3634
+ *
3635
+ * @example
3636
+ * const allRequirements = kyc.getAllRequirements();
3637
+ * console.log(allRequirements.llc); // ['Proof of Identity', ...]
3638
+ */
3639
+ getAllRequirements() {
3640
+ return { ...KYC_DOCUMENT_REQUIREMENTS };
3641
+ }
3642
+ /**
3643
+ * Get KYC requirements and submission status for the authenticated merchant
3644
+ * Fetches all KYC requests and maps them to required documents
3645
+ *
3646
+ * @param entityType - The merchant's business entity type
3647
+ * @returns Complete KYC requirements with submission status
3648
+ *
3649
+ * @example
3650
+ * const status = await kyc.getRequirementsStatus('llc');
3651
+ * console.log(`Completion: ${status.completion_percentage}%`);
3652
+ * console.log(`Approved: ${status.total_approved}/${status.total_required}`);
3653
+ *
3654
+ * // Check individual document status
3655
+ * status.document_statuses.forEach(doc => {
3656
+ * console.log(`${doc.document_type}: ${doc.status || 'not submitted'}`);
3657
+ * });
3658
+ */
3659
+ async getRequirementsStatus(entityType) {
3660
+ // Get required documents for this entity type
3661
+ const requiredDocuments = this.getRequiredDocuments(entityType);
3662
+ // Fetch all KYC requests for the authenticated merchant
3663
+ const params = {
3664
+ kind: 'document_submission',
3665
+ };
3666
+ const response = await this.list(params);
3667
+ if (response.state === 'error' || !response.result) {
3668
+ return {
3669
+ state: 'error',
3670
+ };
3671
+ }
3672
+ const kycRequests = response.result.entries || [];
3673
+ // Map submitted documents
3674
+ const submittedDocs = new Map();
3675
+ kycRequests.forEach(request => {
3676
+ var _a;
3677
+ const docType = (_a = request.data) === null || _a === void 0 ? void 0 : _a.document_type;
3678
+ if (docType && requiredDocuments.includes(docType)) {
3679
+ // Keep the most recent submission for each document type
3680
+ const existing = submittedDocs.get(docType);
3681
+ if (!existing || new Date(request.inserted_at) > new Date(existing.inserted_at)) {
3682
+ submittedDocs.set(docType, request);
3683
+ }
3684
+ }
3685
+ });
3686
+ // Build document statuses
3687
+ const documentStatuses = requiredDocuments.map(docType => {
3688
+ var _a;
3689
+ const submission = submittedDocs.get(docType);
3690
+ if (!submission) {
3691
+ return {
3692
+ document_type: docType,
3693
+ required: true,
3694
+ submitted: false,
3695
+ };
3696
+ }
3697
+ // Convert integer status to string using translator
3698
+ // The API returns status as a number, but the type says it's a string
3699
+ const statusString = StatusTranslator.toStringWithoutContext(submission.status, 'legal_request');
3700
+ // Map to our simplified status types
3701
+ let status;
3702
+ if (statusString) {
3703
+ if (statusString === 'pending' || statusString === 'in_review') {
3704
+ status = 'pending';
3705
+ }
3706
+ else if (statusString === 'approved') {
3707
+ status = 'approved';
3708
+ }
3709
+ else if (statusString === 'rejected') {
3710
+ status = 'rejected';
3711
+ }
3712
+ }
3713
+ const reviewedAt = submission.updated_at !== submission.inserted_at
3714
+ ? submission.updated_at
3715
+ : undefined;
3716
+ return {
3717
+ document_type: docType,
3718
+ required: true,
3719
+ submitted: true,
3720
+ status,
3721
+ submitted_at: submission.inserted_at,
3722
+ reviewed_at: reviewedAt,
3723
+ rejection_reason: (_a = submission.data) === null || _a === void 0 ? void 0 : _a.rejection_reason,
3724
+ };
3725
+ });
3726
+ // Calculate statistics
3727
+ const totalRequired = requiredDocuments.length;
3728
+ const totalSubmitted = documentStatuses.filter(d => d.submitted).length;
3729
+ const totalApproved = documentStatuses.filter(d => d.status === 'approved').length;
3730
+ const totalRejected = documentStatuses.filter(d => d.status === 'rejected').length;
3731
+ const totalPending = documentStatuses.filter(d => d.status === 'pending').length;
3732
+ const completionPercentage = totalRequired > 0
3733
+ ? Math.round((totalApproved / totalRequired) * 100)
3734
+ : 0;
3735
+ const isComplete = totalApproved === totalRequired;
3736
+ const requirements = {
3737
+ entity_type: entityType,
3738
+ required_documents: requiredDocuments,
3739
+ document_statuses: documentStatuses,
3740
+ total_required: totalRequired,
3741
+ total_submitted: totalSubmitted,
3742
+ total_approved: totalApproved,
3743
+ total_rejected: totalRejected,
3744
+ total_pending: totalPending,
3745
+ completion_percentage: completionPercentage,
3746
+ is_complete: isComplete,
3747
+ };
3748
+ return {
3749
+ state: 'ok',
3750
+ result: requirements,
3751
+ };
3752
+ }
3753
+ /**
3754
+ * Check if all required documents have been approved for the authenticated merchant
3755
+ *
3756
+ * @param entityType - The merchant's business entity type
3757
+ * @returns True if all required documents are approved
3758
+ *
3759
+ * @example
3760
+ * const isComplete = await kyc.isKycComplete('llc');
3761
+ * if (isComplete) {
3762
+ * console.log('Merchant is fully verified!');
3763
+ * }
3764
+ */
3765
+ async isKycComplete(entityType) {
3766
+ var _a;
3767
+ const response = await this.getRequirementsStatus(entityType);
3768
+ return ((_a = response.result) === null || _a === void 0 ? void 0 : _a.is_complete) || false;
3769
+ }
3770
+ /**
3771
+ * Get list of missing (not submitted or rejected) documents for the authenticated merchant
3772
+ *
3773
+ * @param entityType - The merchant's business entity type
3774
+ * @returns Array of document types that need to be submitted or resubmitted
3775
+ *
3776
+ * @example
3777
+ * const missing = await kyc.getMissingDocuments('llc');
3778
+ * if (missing.length > 0) {
3779
+ * console.log('Please submit:', missing.join(', '));
3780
+ * }
3781
+ */
3782
+ async getMissingDocuments(entityType) {
3783
+ const response = await this.getRequirementsStatus(entityType);
3784
+ if (response.state === 'error' || !response.result) {
3785
+ return [];
3786
+ }
3787
+ return response.result.document_statuses
3788
+ .filter(doc => !doc.submitted || doc.status === 'rejected')
3789
+ .map(doc => doc.document_type);
3790
+ }
3557
3791
  }
3558
3792
 
3559
3793
  class PaymentLinksResource {
@@ -3842,6 +4076,15 @@ class FinancialRequestsResource {
3842
4076
  }
3843
4077
  }
3844
4078
 
4079
+ let crypto;
4080
+ try {
4081
+ if (typeof require !== 'undefined') {
4082
+ crypto = require('crypto');
4083
+ }
4084
+ }
4085
+ catch (_a) {
4086
+ // Fallback for environments without Node.js crypto
4087
+ }
3845
4088
  class WebhookUrlsResource {
3846
4089
  constructor(client) {
3847
4090
  this.client = client;
@@ -3901,6 +4144,160 @@ class WebhookUrlsResource {
3901
4144
  createQueryBuilder() {
3902
4145
  return new WebhookUrlQueryBuilder(this);
3903
4146
  }
4147
+ // ============================================================================
4148
+ // WEBHOOK VERIFICATION METHODS
4149
+ // ============================================================================
4150
+ /**
4151
+ * Verify webhook signature using HMAC SHA256
4152
+ * Inkress webhooks use the format: crypto.mac(:hmac, :sha256, secret, body) |> Base.encode64()
4153
+ */
4154
+ verifySignature(body, signature, secret) {
4155
+ if (!crypto) {
4156
+ throw new Error('Node.js crypto module not available. Cannot verify webhook signature.');
4157
+ }
4158
+ try {
4159
+ // Generate expected signature using HMAC SHA256
4160
+ const expectedSignature = crypto
4161
+ .createHmac('sha256', secret)
4162
+ .update(body, 'utf8')
4163
+ .digest('base64');
4164
+ // Use constant-time comparison to prevent timing attacks
4165
+ return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature));
4166
+ }
4167
+ catch (error) {
4168
+ return false;
4169
+ }
4170
+ }
4171
+ /**
4172
+ * Parse webhook payload from a string
4173
+ */
4174
+ parsePayload(payload) {
4175
+ try {
4176
+ const parsed = JSON.parse(payload);
4177
+ if (!parsed.id || !parsed.timestamp || !parsed.event) {
4178
+ throw new Error('Invalid webhook payload structure: missing required fields (id, timestamp, or event)');
4179
+ }
4180
+ return parsed;
4181
+ }
4182
+ catch (error) {
4183
+ if (error instanceof Error) {
4184
+ throw new Error(`Failed to parse webhook payload: ${error.message}`);
4185
+ }
4186
+ throw new Error('Failed to parse webhook payload');
4187
+ }
4188
+ }
4189
+ /**
4190
+ * Verify and parse an incoming webhook request
4191
+ * This method clones the request body, validates the signature, and returns the parsed payload
4192
+ *
4193
+ * @param request - The incoming HTTP request object with headers and body
4194
+ * @param secret - Your webhook secret for signature verification
4195
+ * @param options - Optional verification options (e.g., timestamp tolerance)
4196
+ * @returns Promise that resolves to the parsed webhook payload
4197
+ * @throws Error if signature verification fails or payload is invalid
4198
+ *
4199
+ * @example
4200
+ * ```typescript
4201
+ * // Express.js example
4202
+ * app.post('/webhooks', async (req, res) => {
4203
+ * try {
4204
+ * const payload = await sdk.webhookUrls.verifyRequest(
4205
+ * { headers: req.headers, body: req.body },
4206
+ * 'your-webhook-secret'
4207
+ * );
4208
+ *
4209
+ * // Process the webhook
4210
+ * console.log('Received webhook:', payload.event.type);
4211
+ *
4212
+ * res.status(200).json({ received: true });
4213
+ * } catch (error) {
4214
+ * console.error('Webhook verification failed:', error);
4215
+ * res.status(400).json({ error: error.message });
4216
+ * }
4217
+ * });
4218
+ * ```
4219
+ */
4220
+ async verifyRequest(request, secret, options) {
4221
+ // Extract signature from headers (case-insensitive)
4222
+ const signature = request.headers['x-inkress-webhook-signature'] ||
4223
+ request.headers['X-Inkress-Webhook-Signature'];
4224
+ if (!signature || typeof signature !== 'string') {
4225
+ throw new Error('Missing X-Inkress-Webhook-Signature header');
4226
+ }
4227
+ // Clone and ensure body is a string
4228
+ let body;
4229
+ if (typeof request.body === 'string') {
4230
+ body = request.body;
4231
+ }
4232
+ else if (request.body && typeof request.body === 'object') {
4233
+ body = JSON.stringify(request.body);
4234
+ }
4235
+ else {
4236
+ throw new Error('Invalid request body format: body must be a string or object');
4237
+ }
4238
+ // Verify signature
4239
+ const isValid = this.verifySignature(body, signature, secret);
4240
+ if (!isValid) {
4241
+ throw new Error('Webhook signature verification failed: signature does not match');
4242
+ }
4243
+ // Parse the payload
4244
+ const payload = this.parsePayload(body);
4245
+ // Optional: Verify timestamp tolerance
4246
+ if (options === null || options === void 0 ? void 0 : options.tolerance) {
4247
+ const currentTimestamp = Math.floor(Date.now() / 1000);
4248
+ const timeDifference = Math.abs(currentTimestamp - payload.timestamp);
4249
+ if (timeDifference > options.tolerance) {
4250
+ throw new Error(`Webhook timestamp outside tolerance window: ${timeDifference}s (max: ${options.tolerance}s)`);
4251
+ }
4252
+ }
4253
+ return payload;
4254
+ }
4255
+ /**
4256
+ * Verify webhook signature only (without parsing)
4257
+ * Useful for custom verification flows
4258
+ *
4259
+ * @param body - The raw webhook request body as a string
4260
+ * @param signature - The signature from X-Inkress-Webhook-Signature header
4261
+ * @param secret - Your webhook secret
4262
+ * @returns Promise that resolves to true if valid, rejects with error if invalid
4263
+ */
4264
+ async verify(body, signature, secret) {
4265
+ if (!this.verifySignature(body, signature, secret)) {
4266
+ throw new Error('Webhook signature verification failed');
4267
+ }
4268
+ return true;
4269
+ }
4270
+ /**
4271
+ * Generate webhook signature for testing
4272
+ * Matches Inkress signature generation: crypto.mac(:hmac, :sha256, secret, body) |> Base.encode64()
4273
+ *
4274
+ * @example
4275
+ * ```typescript
4276
+ * const testBody = JSON.stringify({ id: '123', timestamp: Date.now(), event: {...} });
4277
+ * const signature = sdk.webhookUrls.generateSignature(testBody, 'your-secret');
4278
+ * ```
4279
+ */
4280
+ generateSignature(body, secret) {
4281
+ if (!crypto) {
4282
+ throw new Error('Node.js crypto module not available. Cannot generate signature.');
4283
+ }
4284
+ return crypto
4285
+ .createHmac('sha256', secret)
4286
+ .update(body, 'utf8')
4287
+ .digest('base64');
4288
+ }
4289
+ /**
4290
+ * Extract event data from webhook payload with type safety
4291
+ *
4292
+ * @example
4293
+ * ```typescript
4294
+ * const payload = await sdk.webhookUrls.verifyRequest(request, secret);
4295
+ * const orderData = sdk.webhookUrls.extractEventData<Order>(payload);
4296
+ * ```
4297
+ */
4298
+ extractEventData(payload) {
4299
+ return payload.event.data;
4300
+ }
3904
4301
  }
3905
4302
 
3906
4303
  /**
@@ -4561,6 +4958,7 @@ exports.FinancialRequestQueryBuilder = FinancialRequestQueryBuilder;
4561
4958
  exports.HttpClient = HttpClient;
4562
4959
  exports.InkressApiError = InkressApiError;
4563
4960
  exports.InkressSDK = InkressSDK;
4961
+ exports.KYC_DOCUMENT_REQUIREMENTS = KYC_DOCUMENT_REQUIREMENTS;
4564
4962
  exports.MERCHANT_FIELD_TYPES = MERCHANT_FIELD_TYPES;
4565
4963
  exports.MerchantQueryBuilder = MerchantQueryBuilder;
4566
4964
  exports.ORDER_FIELD_TYPES = ORDER_FIELD_TYPES;