@classytic/revenue 1.0.2 → 1.0.6

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 CHANGED
@@ -126,7 +126,14 @@ revenue.on('*', (event) => {
126
126
  ### Validation (Zod v4)
127
127
 
128
128
  ```typescript
129
- import { CreatePaymentSchema, validate, safeValidate } from '@classytic/revenue';
129
+ import {
130
+ CreatePaymentSchema,
131
+ PaymentEntrySchema,
132
+ CurrentPaymentInputSchema,
133
+ validate,
134
+ safeValidate,
135
+ validateSplitPayments,
136
+ } from '@classytic/revenue';
130
137
 
131
138
  // Validate input (throws on error)
132
139
  const payment = validate(CreatePaymentSchema, userInput);
@@ -136,6 +143,16 @@ const result = safeValidate(CreatePaymentSchema, userInput);
136
143
  if (!result.success) {
137
144
  console.log(result.error.issues);
138
145
  }
146
+
147
+ // Split payment validation
148
+ const splitResult = safeValidate(CurrentPaymentInputSchema, {
149
+ amount: 50000,
150
+ method: 'split',
151
+ payments: [
152
+ { method: 'cash', amount: 25000 },
153
+ { method: 'bkash', amount: 25000 },
154
+ ],
155
+ });
139
156
  ```
140
157
 
141
158
  ---
@@ -403,6 +420,7 @@ export const Transaction = mongoose.model('Transaction', transactionSchema);
403
420
  | `holdSchema` | Escrow hold/release | `hold: holdSchema` |
404
421
  | `splitSchema` | Multi-party splits | `splits: [splitSchema]` |
405
422
  | `currentPaymentSchema` | For Order/Subscription models | `currentPayment: currentPaymentSchema` |
423
+ | `paymentEntrySchema` | Individual payment in split payments | Used within `currentPaymentSchema.payments` |
406
424
 
407
425
  **Usage:** Import and use as nested objects (NOT spread):
408
426
 
@@ -517,6 +535,102 @@ const pending = await Transaction.find({
517
535
 
518
536
  ---
519
537
 
538
+ ## Multi-Payment Method Support (POS)
539
+
540
+ For POS scenarios where customers pay using multiple methods (e.g., cash + bank + mobile wallet):
541
+
542
+ ### Schema Structure
543
+
544
+ ```typescript
545
+ import { currentPaymentSchema, paymentEntrySchema } from '@classytic/revenue';
546
+
547
+ // currentPaymentSchema now supports a `payments` array for split payments
548
+ const orderSchema = new mongoose.Schema({
549
+ currentPayment: currentPaymentSchema,
550
+ // ...
551
+ });
552
+ ```
553
+
554
+ ### Single Payment (Backward Compatible)
555
+
556
+ ```typescript
557
+ // Traditional single-method payment
558
+ currentPayment: {
559
+ amount: 50000, // 500 BDT in paisa
560
+ method: 'cash',
561
+ status: 'verified',
562
+ verifiedAt: new Date(),
563
+ verifiedBy: cashierId,
564
+ }
565
+ ```
566
+
567
+ ### Split Payment (Multiple Methods)
568
+
569
+ ```typescript
570
+ // Customer pays 500 BDT using: 100 cash + 100 bank + 300 bKash
571
+ currentPayment: {
572
+ amount: 50000, // Total: 500 BDT
573
+ method: 'split',
574
+ status: 'verified',
575
+ payments: [
576
+ { method: 'cash', amount: 10000 }, // 100 BDT
577
+ { method: 'bank_transfer', amount: 10000, reference: 'TRF123' }, // 100 BDT
578
+ { method: 'bkash', amount: 30000, reference: 'TRX456', details: { walletNumber: '01712345678' } }, // 300 BDT
579
+ ],
580
+ verifiedAt: new Date(),
581
+ verifiedBy: cashierId,
582
+ }
583
+ ```
584
+
585
+ ### Validation
586
+
587
+ ```typescript
588
+ import {
589
+ CurrentPaymentInputSchema,
590
+ PaymentEntrySchema,
591
+ validateSplitPayments,
592
+ safeValidate,
593
+ } from '@classytic/revenue';
594
+
595
+ // Zod validation (automatically validates totals match)
596
+ const result = safeValidate(CurrentPaymentInputSchema, paymentInput);
597
+ if (!result.success) {
598
+ console.log(result.error.issues); // "Split payments total must equal the transaction amount"
599
+ }
600
+
601
+ // Helper function for runtime validation
602
+ const isValid = validateSplitPayments({
603
+ amount: 50000,
604
+ payments: [
605
+ { amount: 10000 },
606
+ { amount: 10000 },
607
+ { amount: 30000 },
608
+ ],
609
+ }); // true - totals match
610
+ ```
611
+
612
+ ### TypeScript Types
613
+
614
+ ```typescript
615
+ import type { PaymentEntry, CurrentPayment } from '@classytic/revenue';
616
+
617
+ const entry: PaymentEntry = {
618
+ method: 'bkash',
619
+ amount: 30000,
620
+ reference: 'TRX456',
621
+ details: { walletNumber: '01712345678' },
622
+ };
623
+
624
+ const payment: CurrentPayment = {
625
+ amount: 50000,
626
+ method: 'split',
627
+ status: 'verified',
628
+ payments: [entry],
629
+ };
630
+ ```
631
+
632
+ ---
633
+
520
634
  ## Building Custom Providers
521
635
 
522
636
  ```typescript
@@ -659,15 +773,70 @@ import type {
659
773
  ProviderCapabilities,
660
774
  RevenueEvents,
661
775
  MonetizationCreateParams,
776
+ // Multi-payment types
777
+ PaymentEntry,
778
+ CurrentPayment,
779
+ PaymentEntryInput,
780
+ CurrentPaymentInput,
662
781
  } from '@classytic/revenue';
663
782
  ```
664
783
 
784
+ ### Type Guards
785
+
786
+ Runtime type checking for all enum values:
787
+
788
+ ```typescript
789
+ import {
790
+ isTransactionType,
791
+ isTransactionStatus,
792
+ isPaymentStatus,
793
+ isSubscriptionStatus,
794
+ isMonetizationType,
795
+ isHoldStatus,
796
+ isSplitType,
797
+ } from '@classytic/revenue';
798
+
799
+ // Validate and narrow types at runtime
800
+ if (isTransactionStatus(userInput)) {
801
+ // userInput is narrowed to TransactionStatusValue
802
+ console.log('Valid status:', userInput);
803
+ }
804
+
805
+ // Useful for API input validation
806
+ function processPayment(status: unknown) {
807
+ if (!isPaymentStatus(status)) {
808
+ throw new Error('Invalid payment status');
809
+ }
810
+ // status is now typed as PaymentStatusValue
811
+ }
812
+ ```
813
+
814
+ **Available type guards:**
815
+
816
+ | Guard | Validates |
817
+ |-------|-----------|
818
+ | `isTransactionType` | `'income'` \| `'expense'` |
819
+ | `isTransactionStatus` | `'pending'` \| `'verified'` \| `'completed'` \| ... |
820
+ | `isLibraryCategory` | `'subscription'` \| `'purchase'` |
821
+ | `isPaymentStatus` | `'pending'` \| `'succeeded'` \| `'failed'` \| ... |
822
+ | `isPaymentGatewayType` | `'manual'` \| `'automatic'` |
823
+ | `isGatewayType` | `'redirect'` \| `'direct'` \| `'webhook'` |
824
+ | `isSubscriptionStatus` | `'active'` \| `'paused'` \| `'cancelled'` \| ... |
825
+ | `isPlanKey` | `'monthly'` \| `'yearly'` \| `'one_time'` \| ... |
826
+ | `isMonetizationType` | `'subscription'` \| `'purchase'` |
827
+ | `isHoldStatus` | `'held'` \| `'released'` \| `'partially_released'` \| ... |
828
+ | `isReleaseReason` | `'completed'` \| `'cancelled'` \| `'refunded'` \| ... |
829
+ | `isHoldReason` | `'escrow'` \| `'dispute'` \| `'verification'` \| ... |
830
+ | `isSplitType` | `'platform_commission'` \| `'affiliate_commission'` \| ... |
831
+ | `isSplitStatus` | `'pending'` \| `'processed'` \| `'failed'` |
832
+ | `isPayoutMethod` | `'bank_transfer'` \| `'wallet'` \| `'manual'` |
833
+
665
834
  ---
666
835
 
667
836
  ## Testing
668
837
 
669
838
  ```bash
670
- # Run all tests (84 tests)
839
+ # Run all tests (196 tests)
671
840
  npm test
672
841
 
673
842
  # Run integration tests (requires MongoDB)
@@ -1,5 +1,5 @@
1
1
  import { R as Result } from './retry-80lBCmSe.js';
2
- import { d as TransactionDocument, ac as TransactionTypeOptions, ad as FieldUpdateValidationResult, L as Logger, C as CommissionInfo, o as SplitRule, p as SplitInfo, ab as CommissionWithSplitsOptions, h as MonetizationTypeValue, a6 as PeriodRangeParams, a7 as PeriodRangeResult, a8 as ProratedAmountParams, a9 as DurationResult, e as SubscriptionDocument, aa as SubscriptionEntity } from './index-ChVD3P9k.js';
2
+ import { d as TransactionDocument, ae as TransactionTypeOptions, af as FieldUpdateValidationResult, L as Logger, C as CommissionInfo, q as SplitRule, r as SplitInfo, ad as CommissionWithSplitsOptions, j as MonetizationTypeValue, a8 as PeriodRangeParams, a9 as PeriodRangeResult, aa as ProratedAmountParams, ab as DurationResult, e as SubscriptionDocument, ac as SubscriptionEntity } from './index-C5SsOrV0.js';
3
3
 
4
4
  /**
5
5
  * Money Utility - Integer-safe currency handling
@@ -1,5 +1,5 @@
1
- import { M as MonetizationService, P as PaymentService, T as TransactionService, E as EscrowService, C as Container } from '../index-BnJWVXuw.js';
2
- import { d as TransactionDocument, e as SubscriptionDocument, a as MongooseModel, z as HooksRegistry, w as PaymentProviderInterface } from '../index-ChVD3P9k.js';
1
+ import { M as MonetizationService, P as PaymentService, T as TransactionService, E as EscrowService, C as Container } from '../index-BnEXsnLJ.js';
2
+ import { d as TransactionDocument, e as SubscriptionDocument, a as MongooseModel, B as HooksRegistry, y as PaymentProviderInterface } from '../index-C5SsOrV0.js';
3
3
  import { PaymentResult, RefundResult, PaymentProvider } from '../providers/index.js';
4
4
  import { x as RetryConfig, R as Result } from '../retry-80lBCmSe.js';
5
5
  export { E as Err, O as Ok, g as all, e as err, f as flatMap, a as isErr, i as isOk, m as map, c as mapErr, h as match, o as ok, t as tryCatch, d as tryCatchSync, u as unwrap, b as unwrapOr } from '../retry-80lBCmSe.js';
@@ -1086,14 +1086,37 @@ var TRANSACTION_TYPE = {
1086
1086
  INCOME: "income",
1087
1087
  EXPENSE: "expense"
1088
1088
  };
1089
+ var TRANSACTION_TYPE_VALUES = Object.values(
1090
+ TRANSACTION_TYPE
1091
+ );
1089
1092
  var TRANSACTION_STATUS = {
1093
+ PENDING: "pending",
1094
+ PAYMENT_INITIATED: "payment_initiated",
1095
+ PROCESSING: "processing",
1096
+ REQUIRES_ACTION: "requires_action",
1090
1097
  VERIFIED: "verified",
1091
1098
  COMPLETED: "completed",
1092
- CANCELLED: "cancelled"};
1099
+ FAILED: "failed",
1100
+ CANCELLED: "cancelled",
1101
+ EXPIRED: "expired",
1102
+ REFUNDED: "refunded",
1103
+ PARTIALLY_REFUNDED: "partially_refunded"
1104
+ };
1105
+ var TRANSACTION_STATUS_VALUES = Object.values(
1106
+ TRANSACTION_STATUS
1107
+ );
1093
1108
  var LIBRARY_CATEGORIES = {
1094
1109
  SUBSCRIPTION: "subscription",
1095
1110
  PURCHASE: "purchase"
1096
1111
  };
1112
+ var LIBRARY_CATEGORY_VALUES = Object.values(
1113
+ LIBRARY_CATEGORIES
1114
+ );
1115
+ new Set(TRANSACTION_TYPE_VALUES);
1116
+ new Set(
1117
+ TRANSACTION_STATUS_VALUES
1118
+ );
1119
+ new Set(LIBRARY_CATEGORY_VALUES);
1097
1120
 
1098
1121
  // src/utils/category-resolver.ts
1099
1122
  function resolveCategory(entity, monetizationType, categoryMappings = {}) {
@@ -1163,6 +1186,10 @@ var MONETIZATION_TYPES = {
1163
1186
  PURCHASE: "purchase",
1164
1187
  SUBSCRIPTION: "subscription"
1165
1188
  };
1189
+ var MONETIZATION_TYPE_VALUES = Object.values(
1190
+ MONETIZATION_TYPES
1191
+ );
1192
+ new Set(MONETIZATION_TYPE_VALUES);
1166
1193
 
1167
1194
  // src/services/monetization.service.ts
1168
1195
  var MonetizationService = class {
@@ -2162,23 +2189,66 @@ var TransactionService = class {
2162
2189
 
2163
2190
  // src/enums/escrow.enums.ts
2164
2191
  var HOLD_STATUS = {
2192
+ PENDING: "pending",
2165
2193
  HELD: "held",
2166
2194
  RELEASED: "released",
2167
2195
  CANCELLED: "cancelled",
2196
+ EXPIRED: "expired",
2168
2197
  PARTIALLY_RELEASED: "partially_released"
2169
2198
  };
2199
+ var HOLD_STATUS_VALUES = Object.values(HOLD_STATUS);
2170
2200
  var RELEASE_REASON = {
2171
- PAYMENT_VERIFIED: "payment_verified"};
2201
+ PAYMENT_VERIFIED: "payment_verified",
2202
+ MANUAL_RELEASE: "manual_release",
2203
+ AUTO_RELEASE: "auto_release",
2204
+ DISPUTE_RESOLVED: "dispute_resolved"
2205
+ };
2206
+ var RELEASE_REASON_VALUES = Object.values(
2207
+ RELEASE_REASON
2208
+ );
2172
2209
  var HOLD_REASON = {
2173
- PAYMENT_VERIFICATION: "payment_verification"};
2210
+ PAYMENT_VERIFICATION: "payment_verification",
2211
+ FRAUD_CHECK: "fraud_check",
2212
+ MANUAL_REVIEW: "manual_review",
2213
+ DISPUTE: "dispute",
2214
+ COMPLIANCE: "compliance"
2215
+ };
2216
+ var HOLD_REASON_VALUES = Object.values(HOLD_REASON);
2217
+ new Set(HOLD_STATUS_VALUES);
2218
+ new Set(RELEASE_REASON_VALUES);
2219
+ new Set(HOLD_REASON_VALUES);
2174
2220
 
2175
2221
  // src/enums/split.enums.ts
2176
2222
  var SPLIT_TYPE = {
2223
+ PLATFORM_COMMISSION: "platform_commission",
2224
+ AFFILIATE_COMMISSION: "affiliate_commission",
2225
+ REFERRAL_COMMISSION: "referral_commission",
2226
+ PARTNER_COMMISSION: "partner_commission",
2177
2227
  CUSTOM: "custom"
2178
2228
  };
2229
+ var SPLIT_TYPE_VALUES = Object.values(SPLIT_TYPE);
2179
2230
  var SPLIT_STATUS = {
2180
2231
  PENDING: "pending",
2181
- PAID: "paid"};
2232
+ DUE: "due",
2233
+ PAID: "paid",
2234
+ WAIVED: "waived",
2235
+ CANCELLED: "cancelled"
2236
+ };
2237
+ var SPLIT_STATUS_VALUES = Object.values(
2238
+ SPLIT_STATUS
2239
+ );
2240
+ var PAYOUT_METHOD = {
2241
+ BANK_TRANSFER: "bank_transfer",
2242
+ MOBILE_WALLET: "mobile_wallet",
2243
+ PLATFORM_BALANCE: "platform_balance",
2244
+ CRYPTO: "crypto",
2245
+ CHECK: "check",
2246
+ MANUAL: "manual"
2247
+ };
2248
+ var PAYOUT_METHOD_VALUES = Object.values(PAYOUT_METHOD);
2249
+ new Set(SPLIT_TYPE_VALUES);
2250
+ new Set(SPLIT_STATUS_VALUES);
2251
+ new Set(PAYOUT_METHOD_VALUES);
2182
2252
 
2183
2253
  // src/utils/commission-split.ts
2184
2254
  function calculateSplits(amount, splitRules = [], gatewayFeeRate = 0) {