multivendor-wallet 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2173 @@
1
+ import { PrimaryGeneratedColumn, Column, BeforeInsert, BeforeUpdate, Entity } from 'typeorm';
2
+ import 'reflect-metadata';
3
+ import { Get, Authorized, QueryParam, Res, Param, Post, Body, Req, JsonController } from 'routing-controllers';
4
+ import { IsString, IsNotEmpty, IsInt, Min, IsNumber, IsOptional } from 'class-validator';
5
+ import { Router } from 'express';
6
+
7
+ var __defProp = Object.defineProperty;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
10
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
11
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
12
+ }) : x)(function(x) {
13
+ if (typeof require !== "undefined") return require.apply(this, arguments);
14
+ throw Error('Dynamic require of "' + x + '" is not supported');
15
+ });
16
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
17
+
18
+ // src/errors.ts
19
+ var _WalletError = class _WalletError extends Error {
20
+ constructor(message) {
21
+ super(message);
22
+ this.name = "WalletError";
23
+ }
24
+ };
25
+ __name(_WalletError, "WalletError");
26
+ var WalletError = _WalletError;
27
+ var _WalletConfigError = class _WalletConfigError extends WalletError {
28
+ constructor(message) {
29
+ super(message);
30
+ this.name = "WalletConfigError";
31
+ }
32
+ };
33
+ __name(_WalletConfigError, "WalletConfigError");
34
+ var WalletConfigError = _WalletConfigError;
35
+ var _InsufficientBalanceError = class _InsufficientBalanceError extends WalletError {
36
+ constructor(message = "Insufficient available balance") {
37
+ super(message);
38
+ this.name = "InsufficientBalanceError";
39
+ }
40
+ };
41
+ __name(_InsufficientBalanceError, "InsufficientBalanceError");
42
+ var InsufficientBalanceError = _InsufficientBalanceError;
43
+ var _HoldNotFoundError = class _HoldNotFoundError extends WalletError {
44
+ constructor(message = "Active hold not found for reference") {
45
+ super(message);
46
+ this.name = "HoldNotFoundError";
47
+ }
48
+ };
49
+ __name(_HoldNotFoundError, "HoldNotFoundError");
50
+ var HoldNotFoundError = _HoldNotFoundError;
51
+ var _DuplicateOperationError = class _DuplicateOperationError extends WalletError {
52
+ constructor(message, existingEntry) {
53
+ super(message);
54
+ __publicField(this, "existingEntry");
55
+ this.name = "DuplicateOperationError";
56
+ this.existingEntry = existingEntry;
57
+ }
58
+ };
59
+ __name(_DuplicateOperationError, "DuplicateOperationError");
60
+ var DuplicateOperationError = _DuplicateOperationError;
61
+ var _InvalidAmountError = class _InvalidAmountError extends WalletError {
62
+ constructor(message = "Amount must be a positive integer in minor units") {
63
+ super(message);
64
+ this.name = "InvalidAmountError";
65
+ }
66
+ };
67
+ __name(_InvalidAmountError, "InvalidAmountError");
68
+ var InvalidAmountError = _InvalidAmountError;
69
+
70
+ // src/types.ts
71
+ var WalletAccountType = /* @__PURE__ */ (function(WalletAccountType2) {
72
+ WalletAccountType2["CUSTOMER"] = "customer";
73
+ WalletAccountType2["VENDOR"] = "vendor";
74
+ WalletAccountType2["PLATFORM"] = "platform";
75
+ return WalletAccountType2;
76
+ })({});
77
+ var LedgerEntryType = /* @__PURE__ */ (function(LedgerEntryType2) {
78
+ LedgerEntryType2["CREDIT"] = "credit";
79
+ LedgerEntryType2["DEBIT"] = "debit";
80
+ LedgerEntryType2["HOLD"] = "hold";
81
+ LedgerEntryType2["RELEASE"] = "release";
82
+ LedgerEntryType2["CANCEL_HOLD"] = "cancel_hold";
83
+ LedgerEntryType2["TRANSFER"] = "transfer";
84
+ return LedgerEntryType2;
85
+ })({});
86
+ var HoldStatus = /* @__PURE__ */ (function(HoldStatus2) {
87
+ HoldStatus2["ACTIVE"] = "active";
88
+ HoldStatus2["RELEASED"] = "released";
89
+ HoldStatus2["CANCELLED"] = "cancelled";
90
+ return HoldStatus2;
91
+ })({});
92
+
93
+ // src/client.ts
94
+ var _WalletLedgerClient = class _WalletLedgerClient {
95
+ constructor(store, config = {}) {
96
+ __publicField(this, "store");
97
+ __publicField(this, "config");
98
+ this.store = store;
99
+ this.config = config;
100
+ }
101
+ async getOrCreateAccount(type, ownerId, currency, ctx) {
102
+ return this.store.runInTransaction((tx) => tx.getOrCreateAccount(type, ownerId, currency ?? this.config.defaultCurrency ?? "INR"), ctx);
103
+ }
104
+ async getBalance(accountId, ctx) {
105
+ return this.store.runInTransaction(async (tx) => {
106
+ const account = await tx.getAccountById(accountId);
107
+ if (!account) {
108
+ throw new Error(`Wallet account ${accountId} not found`);
109
+ }
110
+ return this.toBalance(account);
111
+ }, ctx);
112
+ }
113
+ async credit(params, ctx) {
114
+ this.assertPositiveAmount(params.amount);
115
+ return this.store.runInTransaction((tx) => this.creditInTx(tx, params), ctx);
116
+ }
117
+ async debit(params, ctx) {
118
+ this.assertPositiveAmount(params.amount);
119
+ return this.store.runInTransaction((tx) => this.debitInTx(tx, params), ctx);
120
+ }
121
+ async hold(params, ctx) {
122
+ this.assertPositiveAmount(params.amount);
123
+ return this.store.runInTransaction((tx) => this.holdInTx(tx, params), ctx);
124
+ }
125
+ async release(params, ctx) {
126
+ return this.store.runInTransaction((tx) => this.releaseInTx(tx, params), ctx);
127
+ }
128
+ async cancelHold(params, ctx) {
129
+ return this.store.runInTransaction((tx) => this.cancelHoldInTx(tx, params), ctx);
130
+ }
131
+ async transfer(params, ctx) {
132
+ this.assertPositiveAmount(params.amount);
133
+ return this.store.runInTransaction(async (tx) => {
134
+ const debit = await this.debitInTx(tx, {
135
+ accountId: params.fromAccountId,
136
+ amount: params.amount,
137
+ idempotencyKey: `${params.idempotencyKey}:debit`,
138
+ reference: params.reference,
139
+ metadata: params.metadata
140
+ });
141
+ const credit = await this.creditInTx(tx, {
142
+ accountId: params.toAccountId,
143
+ amount: params.amount,
144
+ idempotencyKey: `${params.idempotencyKey}:credit`,
145
+ reference: params.reference,
146
+ metadata: params.metadata
147
+ });
148
+ return {
149
+ debit,
150
+ credit
151
+ };
152
+ }, ctx);
153
+ }
154
+ async getTransactions(params, ctx) {
155
+ const limit = params.limit ?? 50;
156
+ const offset = params.offset ?? 0;
157
+ return this.store.runInTransaction((tx) => tx.listLedgerEntries(params.accountId, limit, offset), ctx);
158
+ }
159
+ async creditInTx(tx, params) {
160
+ const existing = await tx.findLedgerByIdempotencyKey(params.idempotencyKey);
161
+ if (existing) {
162
+ const account = await tx.getAccountById(existing.accountId);
163
+ if (!account) {
164
+ throw new Error(`Wallet account ${existing.accountId} not found`);
165
+ }
166
+ return {
167
+ entry: existing,
168
+ account
169
+ };
170
+ }
171
+ await tx.lockAccount(params.accountId);
172
+ const updated = await tx.updateBalances(params.accountId, {
173
+ availableDelta: params.amount
174
+ });
175
+ const entry = await tx.appendLedgerEntry({
176
+ accountId: params.accountId,
177
+ entryType: LedgerEntryType.CREDIT,
178
+ amount: params.amount,
179
+ balanceAfterAvailable: updated.availableBalance,
180
+ balanceAfterHeld: updated.heldBalance,
181
+ idempotencyKey: params.idempotencyKey,
182
+ referenceType: params.reference?.referenceType,
183
+ referenceId: params.reference?.referenceId,
184
+ metadata: params.metadata
185
+ });
186
+ return {
187
+ entry,
188
+ account: updated
189
+ };
190
+ }
191
+ async debitInTx(tx, params) {
192
+ const existing = await tx.findLedgerByIdempotencyKey(params.idempotencyKey);
193
+ if (existing) {
194
+ const account = await tx.getAccountById(existing.accountId);
195
+ if (!account) {
196
+ throw new Error(`Wallet account ${existing.accountId} not found`);
197
+ }
198
+ return {
199
+ entry: existing,
200
+ account
201
+ };
202
+ }
203
+ const locked = await tx.lockAccount(params.accountId);
204
+ if (locked.availableBalance < params.amount) {
205
+ throw new InsufficientBalanceError();
206
+ }
207
+ const updated = await tx.updateBalances(params.accountId, {
208
+ availableDelta: -params.amount
209
+ });
210
+ const entry = await tx.appendLedgerEntry({
211
+ accountId: params.accountId,
212
+ entryType: LedgerEntryType.DEBIT,
213
+ amount: params.amount,
214
+ balanceAfterAvailable: updated.availableBalance,
215
+ balanceAfterHeld: updated.heldBalance,
216
+ idempotencyKey: params.idempotencyKey,
217
+ referenceType: params.reference?.referenceType,
218
+ referenceId: params.reference?.referenceId,
219
+ metadata: params.metadata
220
+ });
221
+ return {
222
+ entry,
223
+ account: updated
224
+ };
225
+ }
226
+ async holdInTx(tx, params) {
227
+ const existing = await tx.findLedgerByIdempotencyKey(params.idempotencyKey);
228
+ if (existing) {
229
+ const account = await tx.getAccountById(existing.accountId);
230
+ const hold2 = await tx.findHoldByReference(params.reference);
231
+ if (!account) {
232
+ throw new Error(`Wallet account ${existing.accountId} not found`);
233
+ }
234
+ return {
235
+ entry: existing,
236
+ account,
237
+ hold: hold2 ?? void 0
238
+ };
239
+ }
240
+ const existingHold = await tx.findHoldByReference(params.reference);
241
+ if (existingHold?.status === HoldStatus.ACTIVE) {
242
+ throw new DuplicateOperationError("Hold already exists for reference", existingHold);
243
+ }
244
+ const source = await tx.lockAccount(params.sourceAccountId);
245
+ if (source.availableBalance < params.amount) {
246
+ throw new InsufficientBalanceError("Insufficient platform balance for hold");
247
+ }
248
+ await tx.updateBalances(params.sourceAccountId, {
249
+ availableDelta: -params.amount
250
+ });
251
+ const targetUpdated = await tx.updateBalances(params.targetAccountId, {
252
+ heldDelta: params.amount
253
+ });
254
+ const hold = await tx.upsertHold({
255
+ sourceAccountId: params.sourceAccountId,
256
+ targetAccountId: params.targetAccountId,
257
+ amount: params.amount,
258
+ status: HoldStatus.ACTIVE,
259
+ referenceType: params.reference.referenceType,
260
+ referenceId: params.reference.referenceId,
261
+ idempotencyKey: params.idempotencyKey
262
+ });
263
+ const sourceAfterHold = await tx.getAccountById(params.sourceAccountId);
264
+ if (!sourceAfterHold) {
265
+ throw new Error(`Wallet account ${params.sourceAccountId} not found`);
266
+ }
267
+ await tx.appendLedgerEntry({
268
+ accountId: params.sourceAccountId,
269
+ entryType: LedgerEntryType.HOLD,
270
+ amount: params.amount,
271
+ balanceAfterAvailable: sourceAfterHold.availableBalance,
272
+ balanceAfterHeld: sourceAfterHold.heldBalance,
273
+ idempotencyKey: `${params.idempotencyKey}:source`,
274
+ referenceType: params.reference.referenceType,
275
+ referenceId: params.reference.referenceId,
276
+ metadata: params.metadata
277
+ });
278
+ const entry = await tx.appendLedgerEntry({
279
+ accountId: params.targetAccountId,
280
+ entryType: LedgerEntryType.HOLD,
281
+ amount: params.amount,
282
+ balanceAfterAvailable: targetUpdated.availableBalance,
283
+ balanceAfterHeld: targetUpdated.heldBalance,
284
+ idempotencyKey: params.idempotencyKey,
285
+ referenceType: params.reference.referenceType,
286
+ referenceId: params.reference.referenceId,
287
+ metadata: params.metadata
288
+ });
289
+ return {
290
+ entry,
291
+ account: targetUpdated,
292
+ hold
293
+ };
294
+ }
295
+ async releaseInTx(tx, params) {
296
+ const existing = await tx.findLedgerByIdempotencyKey(params.idempotencyKey);
297
+ if (existing) {
298
+ const account = await tx.getAccountById(existing.accountId);
299
+ const hold2 = await tx.findHoldByReference(params.reference);
300
+ if (!account) {
301
+ throw new Error(`Wallet account ${existing.accountId} not found`);
302
+ }
303
+ return {
304
+ entry: existing,
305
+ account,
306
+ hold: hold2 ?? void 0
307
+ };
308
+ }
309
+ const hold = await this.requireActiveHold(tx, params.reference);
310
+ await tx.lockAccount(hold.targetAccountId);
311
+ const targetUpdated = await tx.updateBalances(hold.targetAccountId, {
312
+ heldDelta: -hold.amount,
313
+ availableDelta: hold.amount
314
+ });
315
+ const releasedHold = await tx.upsertHold({
316
+ ...hold,
317
+ status: HoldStatus.RELEASED
318
+ });
319
+ const entry = await tx.appendLedgerEntry({
320
+ accountId: hold.targetAccountId,
321
+ entryType: LedgerEntryType.RELEASE,
322
+ amount: hold.amount,
323
+ balanceAfterAvailable: targetUpdated.availableBalance,
324
+ balanceAfterHeld: targetUpdated.heldBalance,
325
+ idempotencyKey: params.idempotencyKey,
326
+ referenceType: params.reference.referenceType,
327
+ referenceId: params.reference.referenceId,
328
+ metadata: params.metadata
329
+ });
330
+ return {
331
+ entry,
332
+ account: targetUpdated,
333
+ hold: releasedHold
334
+ };
335
+ }
336
+ async cancelHoldInTx(tx, params) {
337
+ const existing = await tx.findLedgerByIdempotencyKey(params.idempotencyKey);
338
+ if (existing) {
339
+ const account = await tx.getAccountById(existing.accountId);
340
+ const hold2 = await tx.findHoldByReference(params.reference);
341
+ if (!account) {
342
+ throw new Error(`Wallet account ${existing.accountId} not found`);
343
+ }
344
+ return {
345
+ entry: existing,
346
+ account,
347
+ hold: hold2 ?? void 0
348
+ };
349
+ }
350
+ const hold = await this.requireActiveHold(tx, params.reference);
351
+ await tx.lockAccount(hold.sourceAccountId);
352
+ await tx.lockAccount(hold.targetAccountId);
353
+ const sourceUpdated = await tx.updateBalances(hold.sourceAccountId, {
354
+ availableDelta: hold.amount
355
+ });
356
+ const targetUpdated = await tx.updateBalances(hold.targetAccountId, {
357
+ heldDelta: -hold.amount
358
+ });
359
+ const cancelledHold = await tx.upsertHold({
360
+ ...hold,
361
+ status: HoldStatus.CANCELLED
362
+ });
363
+ await tx.appendLedgerEntry({
364
+ accountId: hold.sourceAccountId,
365
+ entryType: LedgerEntryType.CANCEL_HOLD,
366
+ amount: hold.amount,
367
+ balanceAfterAvailable: sourceUpdated.availableBalance,
368
+ balanceAfterHeld: sourceUpdated.heldBalance,
369
+ idempotencyKey: `${params.idempotencyKey}:source`,
370
+ referenceType: params.reference.referenceType,
371
+ referenceId: params.reference.referenceId,
372
+ metadata: params.metadata
373
+ });
374
+ const entry = await tx.appendLedgerEntry({
375
+ accountId: hold.targetAccountId,
376
+ entryType: LedgerEntryType.CANCEL_HOLD,
377
+ amount: hold.amount,
378
+ balanceAfterAvailable: targetUpdated.availableBalance,
379
+ balanceAfterHeld: targetUpdated.heldBalance,
380
+ idempotencyKey: params.idempotencyKey,
381
+ referenceType: params.reference.referenceType,
382
+ referenceId: params.reference.referenceId,
383
+ metadata: params.metadata
384
+ });
385
+ return {
386
+ entry,
387
+ account: targetUpdated,
388
+ hold: cancelledHold
389
+ };
390
+ }
391
+ toBalance(account) {
392
+ return {
393
+ available: account.availableBalance,
394
+ held: account.heldBalance,
395
+ total: account.availableBalance + account.heldBalance
396
+ };
397
+ }
398
+ assertPositiveAmount(amount) {
399
+ if (!Number.isInteger(amount) || amount <= 0) {
400
+ throw new InvalidAmountError();
401
+ }
402
+ }
403
+ async requireActiveHold(tx, reference) {
404
+ const hold = await tx.findHoldByReference(reference);
405
+ if (!hold || hold.status !== HoldStatus.ACTIVE) {
406
+ throw new HoldNotFoundError();
407
+ }
408
+ return hold;
409
+ }
410
+ };
411
+ __name(_WalletLedgerClient, "WalletLedgerClient");
412
+ var WalletLedgerClient = _WalletLedgerClient;
413
+
414
+ // src/currency.ts
415
+ function toMinorUnit(amount) {
416
+ return Math.round(amount * 100);
417
+ }
418
+ __name(toMinorUnit, "toMinorUnit");
419
+ function fromMinorUnit(amount) {
420
+ return amount / 100;
421
+ }
422
+ __name(fromMinorUnit, "fromMinorUnit");
423
+
424
+ // src/hooks/order.ts
425
+ function isWalletOnlyPayment(paymentMethod) {
426
+ return String(paymentMethod ?? "").trim().toLowerCase() === "wallet";
427
+ }
428
+ __name(isWalletOnlyPayment, "isWalletOnlyPayment");
429
+ function createOrderHooks(runtime) {
430
+ const { client, platformOwnerId, flags } = runtime;
431
+ return {
432
+ async onOrderPaid(ctx) {
433
+ if (!flags.enabled || !flags.ledgerWrites) {
434
+ return;
435
+ }
436
+ const txCtx = {
437
+ nativeTx: ctx.nativeTx
438
+ };
439
+ const platform = await client.getOrCreateAccount(WalletAccountType.PLATFORM, platformOwnerId, ctx.currency, txCtx);
440
+ const orderTotalPaise = toMinorUnit(ctx.orderTotalRupees);
441
+ const shouldDebitCustomer = isWalletOnlyPayment(ctx.paymentMethod) && ctx.customerId && !ctx.skipCustomerDebit;
442
+ if (shouldDebitCustomer) {
443
+ const debitRupees = ctx.walletAmountRupees ?? ctx.orderTotalRupees;
444
+ const customer = await client.getOrCreateAccount(WalletAccountType.CUSTOMER, ctx.customerId, ctx.currency, txCtx);
445
+ await client.debit({
446
+ accountId: customer.id,
447
+ amount: toMinorUnit(debitRupees),
448
+ idempotencyKey: `wallet:payment:${ctx.paymentNumber}`,
449
+ reference: {
450
+ referenceType: "order",
451
+ referenceId: String(ctx.orderId)
452
+ }
453
+ }, txCtx);
454
+ }
455
+ await client.credit({
456
+ accountId: platform.id,
457
+ amount: orderTotalPaise,
458
+ idempotencyKey: `payment:${ctx.paymentNumber}`,
459
+ reference: {
460
+ referenceType: "order",
461
+ referenceId: String(ctx.orderId)
462
+ }
463
+ }, txCtx);
464
+ for (const line of ctx.lines) {
465
+ const vendorNetRupees = line.commissionBaseRupees - line.commissionRupees;
466
+ if (vendorNetRupees <= 0) {
467
+ continue;
468
+ }
469
+ const vendor = await client.getOrCreateAccount(WalletAccountType.VENDOR, line.vendorId, ctx.currency, txCtx);
470
+ await client.hold({
471
+ sourceAccountId: platform.id,
472
+ targetAccountId: vendor.id,
473
+ amount: toMinorUnit(vendorNetRupees),
474
+ idempotencyKey: `order:${ctx.orderId}:hold:vendorOrder:${line.vendorOrderId}`,
475
+ reference: {
476
+ referenceType: "vendor_order",
477
+ referenceId: String(line.vendorOrderId)
478
+ }
479
+ }, txCtx);
480
+ }
481
+ },
482
+ async onLineDelivered(ctx) {
483
+ if (!flags.enabled || !flags.ledgerWrites) {
484
+ return;
485
+ }
486
+ await client.release({
487
+ reference: {
488
+ referenceType: "vendor_order",
489
+ referenceId: String(ctx.vendorOrderId)
490
+ },
491
+ idempotencyKey: `vendorOrder:${ctx.vendorOrderId}:release`
492
+ }, ctx.nativeTx != null ? {
493
+ nativeTx: ctx.nativeTx
494
+ } : void 0);
495
+ }
496
+ };
497
+ }
498
+ __name(createOrderHooks, "createOrderHooks");
499
+
500
+ // src/hooks/refund.ts
501
+ function isWalletPayment(paymentMethod) {
502
+ return String(paymentMethod ?? "").trim().toLowerCase() === "wallet";
503
+ }
504
+ __name(isWalletPayment, "isWalletPayment");
505
+ function walletCreditRupees(ctx) {
506
+ if (ctx.walletRefundRupees != null && ctx.walletRefundRupees > 0) {
507
+ return ctx.walletRefundRupees;
508
+ }
509
+ return isWalletPayment(ctx.paymentMethod) ? ctx.lineTotalRupees : 0;
510
+ }
511
+ __name(walletCreditRupees, "walletCreditRupees");
512
+ function idempotencyPrefix(ctx) {
513
+ const scope = ctx.idempotencyScope ?? String(ctx.orderProductId);
514
+ return ctx.refundReason === "return" ? `return:${scope}` : `refund:${ctx.orderProductId}`;
515
+ }
516
+ __name(idempotencyPrefix, "idempotencyPrefix");
517
+ function createRefundHooks(runtime) {
518
+ const { client, platformOwnerId, flags } = runtime;
519
+ return {
520
+ async onRefund(ctx) {
521
+ if (!flags.enabled || !flags.ledgerWrites) {
522
+ await ctx.processExternalRefund();
523
+ return;
524
+ }
525
+ const keyPrefix = idempotencyPrefix(ctx);
526
+ if (!ctx.skipEscrow) {
527
+ await client.cancelHold({
528
+ reference: {
529
+ referenceType: "vendor_order",
530
+ referenceId: String(ctx.vendorOrderId)
531
+ },
532
+ idempotencyKey: `${keyPrefix}:cancel_hold`
533
+ });
534
+ }
535
+ const refundPaise = toMinorUnit(ctx.lineTotalRupees);
536
+ if (refundPaise > 0) {
537
+ const platform = await client.getOrCreateAccount(WalletAccountType.PLATFORM, platformOwnerId, ctx.currency);
538
+ const balance = await client.getBalance(platform.id);
539
+ if (balance.available >= refundPaise) {
540
+ await client.debit({
541
+ accountId: platform.id,
542
+ amount: refundPaise,
543
+ idempotencyKey: `${keyPrefix}:platform_debit`,
544
+ reference: {
545
+ referenceType: "order_product",
546
+ referenceId: String(ctx.orderProductId)
547
+ }
548
+ });
549
+ }
550
+ const creditRupees = walletCreditRupees(ctx);
551
+ if (creditRupees > 0 && ctx.customerId) {
552
+ const customer = await client.getOrCreateAccount(WalletAccountType.CUSTOMER, ctx.customerId, ctx.currency);
553
+ await client.credit({
554
+ accountId: customer.id,
555
+ amount: toMinorUnit(creditRupees),
556
+ idempotencyKey: `${keyPrefix}:customer_credit`,
557
+ reference: {
558
+ referenceType: "order",
559
+ referenceId: String(ctx.orderId ?? ctx.orderProductId)
560
+ }
561
+ });
562
+ }
563
+ }
564
+ await ctx.processExternalRefund();
565
+ }
566
+ };
567
+ }
568
+ __name(createRefundHooks, "createRefundHooks");
569
+
570
+ // src/hooks/settlement.ts
571
+ function createSettlementHooks(runtime) {
572
+ const { client, flags } = runtime;
573
+ return {
574
+ async onSettlementDebit(ctx) {
575
+ if (!flags.enabled || !flags.settlementDebit) {
576
+ return;
577
+ }
578
+ const txCtx = {
579
+ nativeTx: ctx.nativeTx
580
+ };
581
+ const amountPaise = toMinorUnit(ctx.amountRupees);
582
+ const vendorAccount = await client.getOrCreateAccount(WalletAccountType.VENDOR, ctx.vendorId, ctx.currency, txCtx);
583
+ const balance = await client.getBalance(vendorAccount.id, txCtx);
584
+ if (balance.available < amountPaise) {
585
+ throw new Error(`Insufficient vendor wallet balance for settlement (vendor ${ctx.vendorId})`);
586
+ }
587
+ await client.debit({
588
+ accountId: vendorAccount.id,
589
+ amount: amountPaise,
590
+ idempotencyKey: `settlement:${ctx.settlementId}:debit:${ctx.vendorId}`,
591
+ reference: {
592
+ referenceType: "settlement",
593
+ referenceId: String(ctx.settlementId)
594
+ }
595
+ }, txCtx);
596
+ }
597
+ };
598
+ }
599
+ __name(createSettlementHooks, "createSettlementHooks");
600
+
601
+ // src/plugin-instance.ts
602
+ var activePlugin;
603
+ var activeConnection;
604
+ function bindWalletPlugin(plugin) {
605
+ activePlugin = plugin;
606
+ }
607
+ __name(bindWalletPlugin, "bindWalletPlugin");
608
+ function bindWalletConnection(connection) {
609
+ activeConnection = connection;
610
+ }
611
+ __name(bindWalletConnection, "bindWalletConnection");
612
+ function resolveWalletPlugin() {
613
+ if (!activePlugin) {
614
+ throw new Error("Wallet plugin is not registered. Call registerWalletPlugin() during app bootstrap.");
615
+ }
616
+ return activePlugin;
617
+ }
618
+ __name(resolveWalletPlugin, "resolveWalletPlugin");
619
+ function resolveWalletConnection() {
620
+ if (!activeConnection?.manager) {
621
+ throw new Error("Wallet database connection is not registered. Pass host TypeORM Connection as dataSource.");
622
+ }
623
+ return activeConnection;
624
+ }
625
+ __name(resolveWalletConnection, "resolveWalletConnection");
626
+ function _ts_decorate(decorators, target, key, desc) {
627
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
628
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
629
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
630
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
631
+ }
632
+ __name(_ts_decorate, "_ts_decorate");
633
+ function _ts_metadata(k, v) {
634
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
635
+ }
636
+ __name(_ts_metadata, "_ts_metadata");
637
+ function nowTimestamp() {
638
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
639
+ }
640
+ __name(nowTimestamp, "nowTimestamp");
641
+ var _WalletAccountEntity = class _WalletAccountEntity {
642
+ constructor() {
643
+ __publicField(this, "id");
644
+ __publicField(this, "ownerType");
645
+ __publicField(this, "ownerId");
646
+ __publicField(this, "currency");
647
+ __publicField(this, "availableBalance");
648
+ __publicField(this, "heldBalance");
649
+ __publicField(this, "createdBy");
650
+ __publicField(this, "createdDate");
651
+ __publicField(this, "modifiedBy");
652
+ __publicField(this, "modifiedDate");
653
+ }
654
+ setCreated() {
655
+ this.createdDate = nowTimestamp();
656
+ this.modifiedDate = nowTimestamp();
657
+ }
658
+ setModified() {
659
+ this.modifiedDate = nowTimestamp();
660
+ }
661
+ };
662
+ __name(_WalletAccountEntity, "WalletAccountEntity");
663
+ var WalletAccountEntity = _WalletAccountEntity;
664
+ _ts_decorate([
665
+ PrimaryGeneratedColumn({
666
+ name: "id"
667
+ }),
668
+ _ts_metadata("design:type", Number)
669
+ ], WalletAccountEntity.prototype, "id", void 0);
670
+ _ts_decorate([
671
+ Column({
672
+ name: "owner_type",
673
+ length: 32
674
+ }),
675
+ _ts_metadata("design:type", String)
676
+ ], WalletAccountEntity.prototype, "ownerType", void 0);
677
+ _ts_decorate([
678
+ Column({
679
+ name: "owner_id"
680
+ }),
681
+ _ts_metadata("design:type", Number)
682
+ ], WalletAccountEntity.prototype, "ownerId", void 0);
683
+ _ts_decorate([
684
+ Column({
685
+ name: "currency",
686
+ length: 8,
687
+ default: "INR"
688
+ }),
689
+ _ts_metadata("design:type", String)
690
+ ], WalletAccountEntity.prototype, "currency", void 0);
691
+ _ts_decorate([
692
+ Column({
693
+ name: "available_balance",
694
+ type: "bigint",
695
+ default: 0
696
+ }),
697
+ _ts_metadata("design:type", String)
698
+ ], WalletAccountEntity.prototype, "availableBalance", void 0);
699
+ _ts_decorate([
700
+ Column({
701
+ name: "held_balance",
702
+ type: "bigint",
703
+ default: 0
704
+ }),
705
+ _ts_metadata("design:type", String)
706
+ ], WalletAccountEntity.prototype, "heldBalance", void 0);
707
+ _ts_decorate([
708
+ Column({
709
+ name: "created_by",
710
+ nullable: true
711
+ }),
712
+ _ts_metadata("design:type", Number)
713
+ ], WalletAccountEntity.prototype, "createdBy", void 0);
714
+ _ts_decorate([
715
+ Column({
716
+ name: "created_date",
717
+ nullable: true
718
+ }),
719
+ _ts_metadata("design:type", String)
720
+ ], WalletAccountEntity.prototype, "createdDate", void 0);
721
+ _ts_decorate([
722
+ Column({
723
+ name: "modified_by",
724
+ nullable: true
725
+ }),
726
+ _ts_metadata("design:type", Number)
727
+ ], WalletAccountEntity.prototype, "modifiedBy", void 0);
728
+ _ts_decorate([
729
+ Column({
730
+ name: "modified_date",
731
+ nullable: true
732
+ }),
733
+ _ts_metadata("design:type", String)
734
+ ], WalletAccountEntity.prototype, "modifiedDate", void 0);
735
+ _ts_decorate([
736
+ BeforeInsert(),
737
+ _ts_metadata("design:type", Function),
738
+ _ts_metadata("design:paramtypes", []),
739
+ _ts_metadata("design:returntype", void 0)
740
+ ], WalletAccountEntity.prototype, "setCreated", null);
741
+ _ts_decorate([
742
+ BeforeUpdate(),
743
+ _ts_metadata("design:type", Function),
744
+ _ts_metadata("design:paramtypes", []),
745
+ _ts_metadata("design:returntype", void 0)
746
+ ], WalletAccountEntity.prototype, "setModified", null);
747
+ WalletAccountEntity = _ts_decorate([
748
+ Entity("wallet_account")
749
+ ], WalletAccountEntity);
750
+ var _WalletLedgerEntryEntity = class _WalletLedgerEntryEntity {
751
+ constructor() {
752
+ __publicField(this, "id");
753
+ __publicField(this, "accountId");
754
+ __publicField(this, "entryType");
755
+ __publicField(this, "amount");
756
+ __publicField(this, "balanceAfterAvailable");
757
+ __publicField(this, "balanceAfterHeld");
758
+ __publicField(this, "idempotencyKey");
759
+ __publicField(this, "referenceType");
760
+ __publicField(this, "referenceId");
761
+ __publicField(this, "metadata");
762
+ __publicField(this, "createdBy");
763
+ __publicField(this, "createdDate");
764
+ __publicField(this, "modifiedBy");
765
+ __publicField(this, "modifiedDate");
766
+ }
767
+ setCreated() {
768
+ this.createdDate = nowTimestamp();
769
+ this.modifiedDate = nowTimestamp();
770
+ }
771
+ };
772
+ __name(_WalletLedgerEntryEntity, "WalletLedgerEntryEntity");
773
+ var WalletLedgerEntryEntity = _WalletLedgerEntryEntity;
774
+ _ts_decorate([
775
+ PrimaryGeneratedColumn({
776
+ name: "id"
777
+ }),
778
+ _ts_metadata("design:type", Number)
779
+ ], WalletLedgerEntryEntity.prototype, "id", void 0);
780
+ _ts_decorate([
781
+ Column({
782
+ name: "account_id"
783
+ }),
784
+ _ts_metadata("design:type", Number)
785
+ ], WalletLedgerEntryEntity.prototype, "accountId", void 0);
786
+ _ts_decorate([
787
+ Column({
788
+ name: "entry_type",
789
+ length: 32
790
+ }),
791
+ _ts_metadata("design:type", String)
792
+ ], WalletLedgerEntryEntity.prototype, "entryType", void 0);
793
+ _ts_decorate([
794
+ Column({
795
+ name: "amount",
796
+ type: "bigint"
797
+ }),
798
+ _ts_metadata("design:type", String)
799
+ ], WalletLedgerEntryEntity.prototype, "amount", void 0);
800
+ _ts_decorate([
801
+ Column({
802
+ name: "balance_after_available",
803
+ type: "bigint"
804
+ }),
805
+ _ts_metadata("design:type", String)
806
+ ], WalletLedgerEntryEntity.prototype, "balanceAfterAvailable", void 0);
807
+ _ts_decorate([
808
+ Column({
809
+ name: "balance_after_held",
810
+ type: "bigint"
811
+ }),
812
+ _ts_metadata("design:type", String)
813
+ ], WalletLedgerEntryEntity.prototype, "balanceAfterHeld", void 0);
814
+ _ts_decorate([
815
+ Column({
816
+ name: "idempotency_key",
817
+ length: 255,
818
+ unique: true
819
+ }),
820
+ _ts_metadata("design:type", String)
821
+ ], WalletLedgerEntryEntity.prototype, "idempotencyKey", void 0);
822
+ _ts_decorate([
823
+ Column({
824
+ name: "reference_type",
825
+ length: 64,
826
+ nullable: true
827
+ }),
828
+ _ts_metadata("design:type", String)
829
+ ], WalletLedgerEntryEntity.prototype, "referenceType", void 0);
830
+ _ts_decorate([
831
+ Column({
832
+ name: "reference_id",
833
+ length: 128,
834
+ nullable: true
835
+ }),
836
+ _ts_metadata("design:type", String)
837
+ ], WalletLedgerEntryEntity.prototype, "referenceId", void 0);
838
+ _ts_decorate([
839
+ Column({
840
+ name: "metadata",
841
+ type: "json",
842
+ nullable: true
843
+ }),
844
+ _ts_metadata("design:type", typeof Record === "undefined" ? Object : Record)
845
+ ], WalletLedgerEntryEntity.prototype, "metadata", void 0);
846
+ _ts_decorate([
847
+ Column({
848
+ name: "created_by",
849
+ nullable: true
850
+ }),
851
+ _ts_metadata("design:type", Number)
852
+ ], WalletLedgerEntryEntity.prototype, "createdBy", void 0);
853
+ _ts_decorate([
854
+ Column({
855
+ name: "created_date",
856
+ nullable: true
857
+ }),
858
+ _ts_metadata("design:type", String)
859
+ ], WalletLedgerEntryEntity.prototype, "createdDate", void 0);
860
+ _ts_decorate([
861
+ Column({
862
+ name: "modified_by",
863
+ nullable: true
864
+ }),
865
+ _ts_metadata("design:type", Number)
866
+ ], WalletLedgerEntryEntity.prototype, "modifiedBy", void 0);
867
+ _ts_decorate([
868
+ Column({
869
+ name: "modified_date",
870
+ nullable: true
871
+ }),
872
+ _ts_metadata("design:type", String)
873
+ ], WalletLedgerEntryEntity.prototype, "modifiedDate", void 0);
874
+ _ts_decorate([
875
+ BeforeInsert(),
876
+ _ts_metadata("design:type", Function),
877
+ _ts_metadata("design:paramtypes", []),
878
+ _ts_metadata("design:returntype", void 0)
879
+ ], WalletLedgerEntryEntity.prototype, "setCreated", null);
880
+ WalletLedgerEntryEntity = _ts_decorate([
881
+ Entity("wallet_ledger_entry")
882
+ ], WalletLedgerEntryEntity);
883
+ var _WalletHoldEntity = class _WalletHoldEntity {
884
+ constructor() {
885
+ __publicField(this, "id");
886
+ __publicField(this, "sourceAccountId");
887
+ __publicField(this, "targetAccountId");
888
+ __publicField(this, "amount");
889
+ __publicField(this, "status");
890
+ __publicField(this, "referenceType");
891
+ __publicField(this, "referenceId");
892
+ __publicField(this, "idempotencyKey");
893
+ __publicField(this, "createdBy");
894
+ __publicField(this, "createdDate");
895
+ __publicField(this, "modifiedBy");
896
+ __publicField(this, "modifiedDate");
897
+ }
898
+ setCreated() {
899
+ this.createdDate = nowTimestamp();
900
+ this.modifiedDate = nowTimestamp();
901
+ }
902
+ setModified() {
903
+ this.modifiedDate = nowTimestamp();
904
+ }
905
+ };
906
+ __name(_WalletHoldEntity, "WalletHoldEntity");
907
+ var WalletHoldEntity = _WalletHoldEntity;
908
+ _ts_decorate([
909
+ PrimaryGeneratedColumn({
910
+ name: "id"
911
+ }),
912
+ _ts_metadata("design:type", Number)
913
+ ], WalletHoldEntity.prototype, "id", void 0);
914
+ _ts_decorate([
915
+ Column({
916
+ name: "source_account_id"
917
+ }),
918
+ _ts_metadata("design:type", Number)
919
+ ], WalletHoldEntity.prototype, "sourceAccountId", void 0);
920
+ _ts_decorate([
921
+ Column({
922
+ name: "target_account_id"
923
+ }),
924
+ _ts_metadata("design:type", Number)
925
+ ], WalletHoldEntity.prototype, "targetAccountId", void 0);
926
+ _ts_decorate([
927
+ Column({
928
+ name: "amount",
929
+ type: "bigint"
930
+ }),
931
+ _ts_metadata("design:type", String)
932
+ ], WalletHoldEntity.prototype, "amount", void 0);
933
+ _ts_decorate([
934
+ Column({
935
+ name: "status",
936
+ length: 16,
937
+ default: "active"
938
+ }),
939
+ _ts_metadata("design:type", String)
940
+ ], WalletHoldEntity.prototype, "status", void 0);
941
+ _ts_decorate([
942
+ Column({
943
+ name: "reference_type",
944
+ length: 64
945
+ }),
946
+ _ts_metadata("design:type", String)
947
+ ], WalletHoldEntity.prototype, "referenceType", void 0);
948
+ _ts_decorate([
949
+ Column({
950
+ name: "reference_id",
951
+ length: 128
952
+ }),
953
+ _ts_metadata("design:type", String)
954
+ ], WalletHoldEntity.prototype, "referenceId", void 0);
955
+ _ts_decorate([
956
+ Column({
957
+ name: "idempotency_key",
958
+ length: 255
959
+ }),
960
+ _ts_metadata("design:type", String)
961
+ ], WalletHoldEntity.prototype, "idempotencyKey", void 0);
962
+ _ts_decorate([
963
+ Column({
964
+ name: "created_by",
965
+ nullable: true
966
+ }),
967
+ _ts_metadata("design:type", Number)
968
+ ], WalletHoldEntity.prototype, "createdBy", void 0);
969
+ _ts_decorate([
970
+ Column({
971
+ name: "created_date",
972
+ nullable: true
973
+ }),
974
+ _ts_metadata("design:type", String)
975
+ ], WalletHoldEntity.prototype, "createdDate", void 0);
976
+ _ts_decorate([
977
+ Column({
978
+ name: "modified_by",
979
+ nullable: true
980
+ }),
981
+ _ts_metadata("design:type", Number)
982
+ ], WalletHoldEntity.prototype, "modifiedBy", void 0);
983
+ _ts_decorate([
984
+ Column({
985
+ name: "modified_date",
986
+ nullable: true
987
+ }),
988
+ _ts_metadata("design:type", String)
989
+ ], WalletHoldEntity.prototype, "modifiedDate", void 0);
990
+ _ts_decorate([
991
+ BeforeInsert(),
992
+ _ts_metadata("design:type", Function),
993
+ _ts_metadata("design:paramtypes", []),
994
+ _ts_metadata("design:returntype", void 0)
995
+ ], WalletHoldEntity.prototype, "setCreated", null);
996
+ _ts_decorate([
997
+ BeforeUpdate(),
998
+ _ts_metadata("design:type", Function),
999
+ _ts_metadata("design:paramtypes", []),
1000
+ _ts_metadata("design:returntype", void 0)
1001
+ ], WalletHoldEntity.prototype, "setModified", null);
1002
+ WalletHoldEntity = _ts_decorate([
1003
+ Entity("wallet_hold")
1004
+ ], WalletHoldEntity);
1005
+
1006
+ // src/hooks/reconciliation.ts
1007
+ async function runWalletReconciliation() {
1008
+ const parityDeltas = await findVendorPaymentHoldParityDeltas();
1009
+ const cacheMismatches = await findCacheLedgerMismatches();
1010
+ return {
1011
+ parityDeltas,
1012
+ cacheMismatches
1013
+ };
1014
+ }
1015
+ __name(runWalletReconciliation, "runWalletReconciliation");
1016
+ async function findVendorPaymentHoldParityDeltas() {
1017
+ const rows = await resolveWalletConnection().query(`
1018
+ SELECT
1019
+ vo.order_id AS orderId,
1020
+ vp.vendor_order_id AS vendorOrderId,
1021
+ vp.amount AS vendorPaymentAmount,
1022
+ wh.amount AS holdAmount,
1023
+ wh.status AS holdStatus
1024
+ FROM vendor_payment vp
1025
+ INNER JOIN vendor_orders vo ON vo.vendor_order_id = vp.vendor_order_id
1026
+ LEFT JOIN wallet_hold wh
1027
+ ON wh.reference_type = 'vendor_order'
1028
+ AND wh.reference_id = CAST(vp.vendor_order_id AS CHAR)
1029
+ WHERE vp.vendor_order_id IS NOT NULL
1030
+ `);
1031
+ const deltas = [];
1032
+ for (const row of rows) {
1033
+ const vendorPaymentRupees = Number(row.vendorPaymentAmount) || 0;
1034
+ const holdPaise = row.holdAmount != null ? Number(row.holdAmount) : 0;
1035
+ const holdRupees = fromMinorUnit(holdPaise);
1036
+ const delta = Math.abs(vendorPaymentRupees - holdRupees);
1037
+ if (delta > 0.01 && row.holdStatus === "active") {
1038
+ deltas.push({
1039
+ orderId: Number(row.orderId),
1040
+ vendorOrderId: Number(row.vendorOrderId),
1041
+ vendorPaymentRupees,
1042
+ holdPaise,
1043
+ deltaRupees: delta
1044
+ });
1045
+ }
1046
+ }
1047
+ return deltas;
1048
+ }
1049
+ __name(findVendorPaymentHoldParityDeltas, "findVendorPaymentHoldParityDeltas");
1050
+ async function findCacheLedgerMismatches() {
1051
+ const connection = resolveWalletConnection();
1052
+ const accounts = await connection.getRepository(WalletAccountEntity).find();
1053
+ const mismatches = [];
1054
+ for (const account of accounts) {
1055
+ const lastEntry = await connection.getRepository(WalletLedgerEntryEntity).findOne({
1056
+ where: {
1057
+ accountId: account.id
1058
+ },
1059
+ order: {
1060
+ id: "DESC"
1061
+ }
1062
+ });
1063
+ if (!lastEntry) {
1064
+ continue;
1065
+ }
1066
+ const cachedAvailable = Number(account.availableBalance);
1067
+ const cachedHeld = Number(account.heldBalance);
1068
+ const ledgerAvailable = Number(lastEntry.balanceAfterAvailable);
1069
+ const ledgerHeld = Number(lastEntry.balanceAfterHeld);
1070
+ if (cachedAvailable !== ledgerAvailable || cachedHeld !== ledgerHeld) {
1071
+ mismatches.push({
1072
+ accountId: account.id,
1073
+ cachedAvailable,
1074
+ cachedHeld,
1075
+ ledgerAvailable,
1076
+ ledgerHeld
1077
+ });
1078
+ }
1079
+ }
1080
+ return mismatches;
1081
+ }
1082
+ __name(findCacheLedgerMismatches, "findCacheLedgerMismatches");
1083
+
1084
+ // src/hooks/index.ts
1085
+ function createLifecycleHooks(runtime) {
1086
+ return {
1087
+ ...createOrderHooks(runtime),
1088
+ ...createSettlementHooks(runtime),
1089
+ ...createRefundHooks(runtime)
1090
+ };
1091
+ }
1092
+ __name(createLifecycleHooks, "createLifecycleHooks");
1093
+ function _ts_decorate2(decorators, target, key, desc) {
1094
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1095
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1096
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1097
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1098
+ }
1099
+ __name(_ts_decorate2, "_ts_decorate");
1100
+ function _ts_metadata2(k, v) {
1101
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1102
+ }
1103
+ __name(_ts_metadata2, "_ts_metadata");
1104
+ function _ts_param(paramIndex, decorator) {
1105
+ return function(target, key) {
1106
+ decorator(target, key, paramIndex);
1107
+ };
1108
+ }
1109
+ __name(_ts_param, "_ts_param");
1110
+ var _a;
1111
+ var WalletCreditDebitRequest = (_a = class {
1112
+ constructor() {
1113
+ __publicField(this, "ownerType");
1114
+ __publicField(this, "ownerId");
1115
+ __publicField(this, "amount");
1116
+ __publicField(this, "currency");
1117
+ __publicField(this, "reason");
1118
+ }
1119
+ }, __name(_a, "WalletCreditDebitRequest"), _a);
1120
+ _ts_decorate2([
1121
+ IsString(),
1122
+ IsNotEmpty(),
1123
+ _ts_metadata2("design:type", typeof WalletAccountType === "undefined" ? Object : WalletAccountType)
1124
+ ], WalletCreditDebitRequest.prototype, "ownerType", void 0);
1125
+ _ts_decorate2([
1126
+ IsInt(),
1127
+ Min(0),
1128
+ _ts_metadata2("design:type", Number)
1129
+ ], WalletCreditDebitRequest.prototype, "ownerId", void 0);
1130
+ _ts_decorate2([
1131
+ IsNumber(),
1132
+ Min(0.01),
1133
+ _ts_metadata2("design:type", Number)
1134
+ ], WalletCreditDebitRequest.prototype, "amount", void 0);
1135
+ _ts_decorate2([
1136
+ IsOptional(),
1137
+ IsString(),
1138
+ _ts_metadata2("design:type", String)
1139
+ ], WalletCreditDebitRequest.prototype, "currency", void 0);
1140
+ _ts_decorate2([
1141
+ IsOptional(),
1142
+ IsString(),
1143
+ _ts_metadata2("design:type", String)
1144
+ ], WalletCreditDebitRequest.prototype, "reason", void 0);
1145
+ var _AdminWalletController = class _AdminWalletController {
1146
+ get plugin() {
1147
+ return resolveWalletPlugin();
1148
+ }
1149
+ assertEnabled(response) {
1150
+ if (!this.plugin.isEnabled()) {
1151
+ response.status(404).send({
1152
+ status: 0,
1153
+ message: "Wallet feature is disabled"
1154
+ });
1155
+ return false;
1156
+ }
1157
+ return true;
1158
+ }
1159
+ async listAccounts(limit = 50, offset = 0, response) {
1160
+ if (!this.assertEnabled(response)) {
1161
+ return;
1162
+ }
1163
+ const rows = await resolveWalletConnection().getRepository(WalletAccountEntity).find({
1164
+ take: limit,
1165
+ skip: offset,
1166
+ order: {
1167
+ id: "DESC"
1168
+ }
1169
+ });
1170
+ response.status(200).send({
1171
+ status: 1,
1172
+ data: rows.map((row) => ({
1173
+ id: row.id,
1174
+ ownerType: row.ownerType,
1175
+ ownerId: row.ownerId,
1176
+ currency: row.currency,
1177
+ available: fromMinorUnit(Number(row.availableBalance)),
1178
+ held: fromMinorUnit(Number(row.heldBalance)),
1179
+ total: fromMinorUnit(Number(row.availableBalance) + Number(row.heldBalance))
1180
+ }))
1181
+ });
1182
+ }
1183
+ async listTransactions(accountId, limit = 50, offset = 0, response) {
1184
+ if (!this.assertEnabled(response)) {
1185
+ return;
1186
+ }
1187
+ const entries = await this.plugin.client.getTransactions({
1188
+ accountId,
1189
+ limit,
1190
+ offset
1191
+ });
1192
+ response.status(200).send({
1193
+ status: 1,
1194
+ data: entries.map((e) => ({
1195
+ ...e,
1196
+ amount: fromMinorUnit(e.amount),
1197
+ balanceAfterAvailable: fromMinorUnit(e.balanceAfterAvailable),
1198
+ balanceAfterHeld: fromMinorUnit(e.balanceAfterHeld)
1199
+ }))
1200
+ });
1201
+ }
1202
+ async credit(body, request, response) {
1203
+ if (!this.assertEnabled(response)) {
1204
+ return;
1205
+ }
1206
+ const currency = body.currency || "INR";
1207
+ const account = await this.plugin.client.getOrCreateAccount(body.ownerType, body.ownerId, currency);
1208
+ const result = await this.plugin.client.credit({
1209
+ accountId: account.id,
1210
+ amount: toMinorUnit(body.amount),
1211
+ idempotencyKey: `admin:credit:${body.ownerType}:${body.ownerId}:${Date.now()}`,
1212
+ reference: {
1213
+ referenceType: "admin_adjustment",
1214
+ referenceId: String(request.user?.userId || 0)
1215
+ },
1216
+ metadata: {
1217
+ reason: body.reason
1218
+ }
1219
+ });
1220
+ await this.emitAdminMutation({
1221
+ event: "wallet_admin_credit",
1222
+ actorId: request.user?.userId,
1223
+ actorRole: "admin",
1224
+ amount: body.amount,
1225
+ after: {
1226
+ accountId: account.id,
1227
+ entryId: result.entry.id
1228
+ }
1229
+ });
1230
+ response.status(200).send({
1231
+ status: 1,
1232
+ message: "Wallet credited successfully",
1233
+ data: {
1234
+ accountId: account.id,
1235
+ available: fromMinorUnit(result.account.availableBalance)
1236
+ }
1237
+ });
1238
+ }
1239
+ async debit(body, request, response) {
1240
+ if (!this.assertEnabled(response)) {
1241
+ return;
1242
+ }
1243
+ const currency = body.currency || "INR";
1244
+ const account = await this.plugin.client.getOrCreateAccount(body.ownerType, body.ownerId, currency);
1245
+ const result = await this.plugin.client.debit({
1246
+ accountId: account.id,
1247
+ amount: toMinorUnit(body.amount),
1248
+ idempotencyKey: `admin:debit:${body.ownerType}:${body.ownerId}:${Date.now()}`,
1249
+ reference: {
1250
+ referenceType: "admin_adjustment",
1251
+ referenceId: String(request.user?.userId || 0)
1252
+ },
1253
+ metadata: {
1254
+ reason: body.reason
1255
+ }
1256
+ });
1257
+ await this.emitAdminMutation({
1258
+ event: "wallet_admin_debit",
1259
+ actorId: request.user?.userId,
1260
+ actorRole: "admin",
1261
+ amount: body.amount,
1262
+ after: {
1263
+ accountId: account.id,
1264
+ entryId: result.entry.id
1265
+ }
1266
+ });
1267
+ response.status(200).send({
1268
+ status: 1,
1269
+ message: "Wallet debited successfully",
1270
+ data: {
1271
+ accountId: account.id,
1272
+ available: fromMinorUnit(result.account.availableBalance)
1273
+ }
1274
+ });
1275
+ }
1276
+ async emitAdminMutation(event) {
1277
+ const handler = this.plugin.onAdminMutation;
1278
+ if (handler) {
1279
+ await handler(event);
1280
+ }
1281
+ }
1282
+ };
1283
+ __name(_AdminWalletController, "AdminWalletController");
1284
+ var AdminWalletController = _AdminWalletController;
1285
+ _ts_decorate2([
1286
+ Get("/accounts"),
1287
+ Authorized([
1288
+ "admin"
1289
+ ]),
1290
+ _ts_param(0, QueryParam("limit")),
1291
+ _ts_param(1, QueryParam("offset")),
1292
+ _ts_param(2, Res()),
1293
+ _ts_metadata2("design:type", Function),
1294
+ _ts_metadata2("design:paramtypes", [
1295
+ Number,
1296
+ Number,
1297
+ Object
1298
+ ]),
1299
+ _ts_metadata2("design:returntype", Promise)
1300
+ ], AdminWalletController.prototype, "listAccounts", null);
1301
+ _ts_decorate2([
1302
+ Get("/transactions/:accountId"),
1303
+ Authorized([
1304
+ "admin"
1305
+ ]),
1306
+ _ts_param(0, Param("accountId")),
1307
+ _ts_param(1, QueryParam("limit")),
1308
+ _ts_param(2, QueryParam("offset")),
1309
+ _ts_param(3, Res()),
1310
+ _ts_metadata2("design:type", Function),
1311
+ _ts_metadata2("design:paramtypes", [
1312
+ Number,
1313
+ Number,
1314
+ Number,
1315
+ Object
1316
+ ]),
1317
+ _ts_metadata2("design:returntype", Promise)
1318
+ ], AdminWalletController.prototype, "listTransactions", null);
1319
+ _ts_decorate2([
1320
+ Post("/credit"),
1321
+ Authorized([
1322
+ "admin"
1323
+ ]),
1324
+ _ts_param(0, Body({
1325
+ validate: true
1326
+ })),
1327
+ _ts_param(1, Req()),
1328
+ _ts_param(2, Res()),
1329
+ _ts_metadata2("design:type", Function),
1330
+ _ts_metadata2("design:paramtypes", [
1331
+ typeof WalletCreditDebitRequest === "undefined" ? Object : WalletCreditDebitRequest,
1332
+ Object,
1333
+ Object
1334
+ ]),
1335
+ _ts_metadata2("design:returntype", Promise)
1336
+ ], AdminWalletController.prototype, "credit", null);
1337
+ _ts_decorate2([
1338
+ Post("/debit"),
1339
+ Authorized([
1340
+ "admin"
1341
+ ]),
1342
+ _ts_param(0, Body({
1343
+ validate: true
1344
+ })),
1345
+ _ts_param(1, Req()),
1346
+ _ts_param(2, Res()),
1347
+ _ts_metadata2("design:type", Function),
1348
+ _ts_metadata2("design:paramtypes", [
1349
+ typeof WalletCreditDebitRequest === "undefined" ? Object : WalletCreditDebitRequest,
1350
+ Object,
1351
+ Object
1352
+ ]),
1353
+ _ts_metadata2("design:returntype", Promise)
1354
+ ], AdminWalletController.prototype, "debit", null);
1355
+ AdminWalletController = _ts_decorate2([
1356
+ JsonController("/admin/wallet")
1357
+ ], AdminWalletController);
1358
+ function _ts_decorate3(decorators, target, key, desc) {
1359
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1360
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1361
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1362
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1363
+ }
1364
+ __name(_ts_decorate3, "_ts_decorate");
1365
+ function _ts_metadata3(k, v) {
1366
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1367
+ }
1368
+ __name(_ts_metadata3, "_ts_metadata");
1369
+ function _ts_param2(paramIndex, decorator) {
1370
+ return function(target, key) {
1371
+ decorator(target, key, paramIndex);
1372
+ };
1373
+ }
1374
+ __name(_ts_param2, "_ts_param");
1375
+ var _CustomerWalletController = class _CustomerWalletController {
1376
+ get plugin() {
1377
+ return resolveWalletPlugin();
1378
+ }
1379
+ assertEnabled(response) {
1380
+ if (!this.plugin.isEnabled()) {
1381
+ response.status(404).send({
1382
+ status: 0,
1383
+ message: "Wallet feature is disabled"
1384
+ });
1385
+ return false;
1386
+ }
1387
+ return true;
1388
+ }
1389
+ async getBalance(request, response) {
1390
+ if (!this.assertEnabled(response)) {
1391
+ return;
1392
+ }
1393
+ const customerId = request.user?.id ?? 0;
1394
+ const account = await this.plugin.client.getOrCreateAccount(WalletAccountType.CUSTOMER, customerId, "INR");
1395
+ const balance = await this.plugin.client.getBalance(account.id);
1396
+ response.status(200).send({
1397
+ status: 1,
1398
+ data: {
1399
+ available: fromMinorUnit(balance.available),
1400
+ held: fromMinorUnit(balance.held),
1401
+ total: fromMinorUnit(balance.total),
1402
+ currency: account.currency
1403
+ }
1404
+ });
1405
+ }
1406
+ async getTransactions(request, limit = 50, offset = 0, response) {
1407
+ if (!this.assertEnabled(response)) {
1408
+ return;
1409
+ }
1410
+ const customerId = request.user?.id ?? 0;
1411
+ const account = await this.plugin.client.getOrCreateAccount(WalletAccountType.CUSTOMER, customerId, "INR");
1412
+ const entries = await this.plugin.client.getTransactions({
1413
+ accountId: account.id,
1414
+ limit,
1415
+ offset
1416
+ });
1417
+ response.status(200).send({
1418
+ status: 1,
1419
+ data: entries.map((e) => ({
1420
+ id: e.id,
1421
+ entryType: e.entryType,
1422
+ amount: fromMinorUnit(e.amount),
1423
+ referenceType: e.referenceType,
1424
+ referenceId: e.referenceId,
1425
+ createdAt: e.createdAt
1426
+ }))
1427
+ });
1428
+ }
1429
+ };
1430
+ __name(_CustomerWalletController, "CustomerWalletController");
1431
+ var CustomerWalletController = _CustomerWalletController;
1432
+ _ts_decorate3([
1433
+ Get("/balance"),
1434
+ Authorized("customer"),
1435
+ _ts_param2(0, Req()),
1436
+ _ts_param2(1, Res()),
1437
+ _ts_metadata3("design:type", Function),
1438
+ _ts_metadata3("design:paramtypes", [
1439
+ Object,
1440
+ Object
1441
+ ]),
1442
+ _ts_metadata3("design:returntype", Promise)
1443
+ ], CustomerWalletController.prototype, "getBalance", null);
1444
+ _ts_decorate3([
1445
+ Get("/transactions"),
1446
+ Authorized("customer"),
1447
+ _ts_param2(0, Req()),
1448
+ _ts_param2(1, QueryParam("limit")),
1449
+ _ts_param2(2, QueryParam("offset")),
1450
+ _ts_param2(3, Res()),
1451
+ _ts_metadata3("design:type", Function),
1452
+ _ts_metadata3("design:paramtypes", [
1453
+ Object,
1454
+ Number,
1455
+ Number,
1456
+ Object
1457
+ ]),
1458
+ _ts_metadata3("design:returntype", Promise)
1459
+ ], CustomerWalletController.prototype, "getTransactions", null);
1460
+ CustomerWalletController = _ts_decorate3([
1461
+ JsonController("/customer/wallet")
1462
+ ], CustomerWalletController);
1463
+ function _ts_decorate4(decorators, target, key, desc) {
1464
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1465
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1466
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1467
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1468
+ }
1469
+ __name(_ts_decorate4, "_ts_decorate");
1470
+ function _ts_metadata4(k, v) {
1471
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1472
+ }
1473
+ __name(_ts_metadata4, "_ts_metadata");
1474
+ function _ts_param3(paramIndex, decorator) {
1475
+ return function(target, key) {
1476
+ decorator(target, key, paramIndex);
1477
+ };
1478
+ }
1479
+ __name(_ts_param3, "_ts_param");
1480
+ var _VendorWalletController = class _VendorWalletController {
1481
+ get plugin() {
1482
+ return resolveWalletPlugin();
1483
+ }
1484
+ assertEnabled(response) {
1485
+ if (!this.plugin.isEnabled()) {
1486
+ response.status(404).send({
1487
+ status: 0,
1488
+ message: "Wallet feature is disabled"
1489
+ });
1490
+ return false;
1491
+ }
1492
+ return true;
1493
+ }
1494
+ async getBalance(request, response) {
1495
+ if (!this.assertEnabled(response)) {
1496
+ return;
1497
+ }
1498
+ const vendorId = request.user?.vendorId ?? 0;
1499
+ const account = await this.plugin.client.getOrCreateAccount(WalletAccountType.VENDOR, vendorId, "INR");
1500
+ const balance = await this.plugin.client.getBalance(account.id);
1501
+ response.status(200).send({
1502
+ status: 1,
1503
+ data: {
1504
+ available: fromMinorUnit(balance.available),
1505
+ held: fromMinorUnit(balance.held),
1506
+ total: fromMinorUnit(balance.total),
1507
+ currency: account.currency
1508
+ }
1509
+ });
1510
+ }
1511
+ async getTransactions(request, limit = 50, offset = 0, response) {
1512
+ if (!this.assertEnabled(response)) {
1513
+ return;
1514
+ }
1515
+ const vendorId = request.user?.vendorId ?? 0;
1516
+ const account = await this.plugin.client.getOrCreateAccount(WalletAccountType.VENDOR, vendorId, "INR");
1517
+ const entries = await this.plugin.client.getTransactions({
1518
+ accountId: account.id,
1519
+ limit,
1520
+ offset
1521
+ });
1522
+ response.status(200).send({
1523
+ status: 1,
1524
+ data: entries.map((e) => ({
1525
+ id: e.id,
1526
+ entryType: e.entryType,
1527
+ amount: fromMinorUnit(e.amount),
1528
+ referenceType: e.referenceType,
1529
+ referenceId: e.referenceId,
1530
+ createdAt: e.createdAt
1531
+ }))
1532
+ });
1533
+ }
1534
+ };
1535
+ __name(_VendorWalletController, "VendorWalletController");
1536
+ var VendorWalletController = _VendorWalletController;
1537
+ _ts_decorate4([
1538
+ Get("/balance"),
1539
+ Authorized("vendor"),
1540
+ _ts_param3(0, Req()),
1541
+ _ts_param3(1, Res()),
1542
+ _ts_metadata4("design:type", Function),
1543
+ _ts_metadata4("design:paramtypes", [
1544
+ Object,
1545
+ Object
1546
+ ]),
1547
+ _ts_metadata4("design:returntype", Promise)
1548
+ ], VendorWalletController.prototype, "getBalance", null);
1549
+ _ts_decorate4([
1550
+ Get("/transactions"),
1551
+ Authorized("vendor"),
1552
+ _ts_param3(0, Req()),
1553
+ _ts_param3(1, QueryParam("limit")),
1554
+ _ts_param3(2, QueryParam("offset")),
1555
+ _ts_param3(3, Res()),
1556
+ _ts_metadata4("design:type", Function),
1557
+ _ts_metadata4("design:paramtypes", [
1558
+ Object,
1559
+ Number,
1560
+ Number,
1561
+ Object
1562
+ ]),
1563
+ _ts_metadata4("design:returntype", Promise)
1564
+ ], VendorWalletController.prototype, "getTransactions", null);
1565
+ VendorWalletController = _ts_decorate4([
1566
+ JsonController("/vendor/wallet")
1567
+ ], VendorWalletController);
1568
+ function createExpressWalletRouter(plugin, _basePath = "/api") {
1569
+ const router = Router();
1570
+ const disabled = /* @__PURE__ */ __name((_req, res) => {
1571
+ res.status(404).json({
1572
+ status: 0,
1573
+ message: "Wallet feature is disabled"
1574
+ });
1575
+ }, "disabled");
1576
+ const guard = /* @__PURE__ */ __name((req, res, next) => {
1577
+ if (!plugin.isEnabled()) {
1578
+ disabled(req, res);
1579
+ return;
1580
+ }
1581
+ next();
1582
+ }, "guard");
1583
+ router.use(guard);
1584
+ router.get("/customer/wallet/balance", async (req, res) => {
1585
+ try {
1586
+ const customerId = Number(req.user?.id ?? 0);
1587
+ const account = await plugin.client.getOrCreateAccount(WalletAccountType.CUSTOMER, customerId, "INR");
1588
+ const balance = await plugin.client.getBalance(account.id);
1589
+ res.json({
1590
+ status: 1,
1591
+ data: {
1592
+ available: fromMinorUnit(balance.available),
1593
+ held: fromMinorUnit(balance.held),
1594
+ total: fromMinorUnit(balance.total),
1595
+ currency: account.currency
1596
+ }
1597
+ });
1598
+ } catch (e) {
1599
+ res.status(500).json({
1600
+ status: 0,
1601
+ message: e.message
1602
+ });
1603
+ }
1604
+ });
1605
+ router.get("/vendor/wallet/balance", async (req, res) => {
1606
+ try {
1607
+ const vendorId = Number(req.user?.vendorId ?? 0);
1608
+ const account = await plugin.client.getOrCreateAccount(WalletAccountType.VENDOR, vendorId, "INR");
1609
+ const balance = await plugin.client.getBalance(account.id);
1610
+ res.json({
1611
+ status: 1,
1612
+ data: {
1613
+ available: fromMinorUnit(balance.available),
1614
+ held: fromMinorUnit(balance.held),
1615
+ total: fromMinorUnit(balance.total),
1616
+ currency: account.currency
1617
+ }
1618
+ });
1619
+ } catch (e) {
1620
+ res.status(500).json({
1621
+ status: 0,
1622
+ message: e.message
1623
+ });
1624
+ }
1625
+ });
1626
+ return router;
1627
+ }
1628
+ __name(createExpressWalletRouter, "createExpressWalletRouter");
1629
+
1630
+ // src/http/plugin-http.ts
1631
+ function buildWalletHttp(plugin, config) {
1632
+ if (!config || config.mode === "none" || !plugin.isEnabled()) {
1633
+ return {};
1634
+ }
1635
+ if (config.mode === "routing-controllers") {
1636
+ return {
1637
+ controllers: [
1638
+ AdminWalletController,
1639
+ CustomerWalletController,
1640
+ VendorWalletController
1641
+ ]
1642
+ };
1643
+ }
1644
+ if (config.mode === "express-router") {
1645
+ const router = createExpressWalletRouter(plugin, config.basePath ?? "/api");
1646
+ if (config.app) {
1647
+ config.app.use(config.basePath ?? "/api", router);
1648
+ }
1649
+ return {
1650
+ expressRouter: router
1651
+ };
1652
+ }
1653
+ return {};
1654
+ }
1655
+ __name(buildWalletHttp, "buildWalletHttp");
1656
+
1657
+ // src/payment/types.ts
1658
+ var PaymentProviderWallet = "wallet";
1659
+
1660
+ // src/payment/WalletPaymentAdapter.ts
1661
+ function createWalletPaymentAdapter(plugin) {
1662
+ return {
1663
+ provider: PaymentProviderWallet,
1664
+ getPublicCredential() {
1665
+ return plugin.isCheckoutEnabled() ? "wallet" : null;
1666
+ },
1667
+ async createPayment(input) {
1668
+ if (!plugin.isCheckoutEnabled()) {
1669
+ throw new Error("Wallet checkout is not enabled");
1670
+ }
1671
+ const resolveCustomer = plugin.resolveOrderCustomerId;
1672
+ if (!resolveCustomer) {
1673
+ throw new Error("resolveOrderCustomerId is not configured on wallet plugin");
1674
+ }
1675
+ const customerId = await resolveCustomer(input.orderId);
1676
+ if (!customerId) {
1677
+ throw new Error("Order customer not found for wallet payment");
1678
+ }
1679
+ const client = plugin.client;
1680
+ const customerAccount = await client.getOrCreateAccount(WalletAccountType.CUSTOMER, customerId, input.currency);
1681
+ const balance = await client.getBalance(customerAccount.id);
1682
+ const amountPaise = toMinorUnit(input.amount);
1683
+ if (balance.available < amountPaise) {
1684
+ throw new Error("Insufficient wallet balance");
1685
+ }
1686
+ return {
1687
+ providerPaymentId: `wallet-order-${input.orderId}`,
1688
+ provider: PaymentProviderWallet,
1689
+ raw: {
1690
+ customerAccountId: customerAccount.id,
1691
+ amountPaise
1692
+ }
1693
+ };
1694
+ },
1695
+ async verifyPayment(providerPaymentId) {
1696
+ return {
1697
+ providerPaymentId,
1698
+ status: "succeeded",
1699
+ amount: 0,
1700
+ currency: "INR",
1701
+ provider: PaymentProviderWallet,
1702
+ raw: {}
1703
+ };
1704
+ },
1705
+ async fetchPaymentStatus(providerPaymentId) {
1706
+ return this.verifyPayment(providerPaymentId);
1707
+ },
1708
+ async resolveProviderPaymentId(orderId) {
1709
+ return `wallet-order-${orderId}`;
1710
+ },
1711
+ async createRefund(_input) {
1712
+ throw new Error("Wallet refunds are handled via wallet hooks.onRefund");
1713
+ },
1714
+ verifyWebhook(_payload, _signature) {
1715
+ return {
1716
+ isValid: false
1717
+ };
1718
+ }
1719
+ };
1720
+ }
1721
+ __name(createWalletPaymentAdapter, "createWalletPaymentAdapter");
1722
+
1723
+ // src/typeorm/store.ts
1724
+ function toNumber(value) {
1725
+ if (value == null) {
1726
+ return 0;
1727
+ }
1728
+ return Number(value);
1729
+ }
1730
+ __name(toNumber, "toNumber");
1731
+ function toLedgerAccount(row) {
1732
+ return {
1733
+ id: row.id,
1734
+ ownerType: row.ownerType,
1735
+ ownerId: row.ownerId,
1736
+ currency: row.currency,
1737
+ availableBalance: toNumber(row.availableBalance),
1738
+ heldBalance: toNumber(row.heldBalance)
1739
+ };
1740
+ }
1741
+ __name(toLedgerAccount, "toLedgerAccount");
1742
+ function toLedgerHold(row) {
1743
+ return {
1744
+ id: row.id,
1745
+ sourceAccountId: row.sourceAccountId,
1746
+ targetAccountId: row.targetAccountId,
1747
+ amount: toNumber(row.amount),
1748
+ status: row.status,
1749
+ referenceType: row.referenceType,
1750
+ referenceId: row.referenceId,
1751
+ idempotencyKey: row.idempotencyKey,
1752
+ createdAt: new Date(row.createdDate)
1753
+ };
1754
+ }
1755
+ __name(toLedgerHold, "toLedgerHold");
1756
+ function toLedgerEntry(row) {
1757
+ return {
1758
+ id: row.id,
1759
+ accountId: row.accountId,
1760
+ entryType: row.entryType,
1761
+ amount: toNumber(row.amount),
1762
+ balanceAfterAvailable: toNumber(row.balanceAfterAvailable),
1763
+ balanceAfterHeld: toNumber(row.balanceAfterHeld),
1764
+ idempotencyKey: row.idempotencyKey,
1765
+ referenceType: row.referenceType,
1766
+ referenceId: row.referenceId,
1767
+ metadata: row.metadata,
1768
+ createdAt: new Date(row.createdDate)
1769
+ };
1770
+ }
1771
+ __name(toLedgerEntry, "toLedgerEntry");
1772
+ function isEntityManager(value) {
1773
+ return value != null && typeof value === "object" && typeof value.getRepository === "function";
1774
+ }
1775
+ __name(isEntityManager, "isEntityManager");
1776
+ var _a2;
1777
+ var TypeOrmWalletStoreTx = (_a2 = class {
1778
+ constructor(em) {
1779
+ __publicField(this, "em");
1780
+ this.em = em;
1781
+ }
1782
+ async getOrCreateAccount(type, ownerId, currency) {
1783
+ const repo = this.em.getRepository(WalletAccountEntity);
1784
+ let row = await repo.findOne({
1785
+ where: {
1786
+ ownerType: type,
1787
+ ownerId,
1788
+ currency
1789
+ }
1790
+ });
1791
+ if (!row) {
1792
+ row = repo.create({
1793
+ ownerType: type,
1794
+ ownerId,
1795
+ currency,
1796
+ availableBalance: "0",
1797
+ heldBalance: "0"
1798
+ });
1799
+ row = await repo.save(row);
1800
+ }
1801
+ return toLedgerAccount(row);
1802
+ }
1803
+ async lockAccount(accountId) {
1804
+ const row = await this.em.getRepository(WalletAccountEntity).createQueryBuilder("wa").setLock("pessimistic_write").where("wa.id = :id", {
1805
+ id: accountId
1806
+ }).getOne();
1807
+ if (!row) {
1808
+ throw new Error(`Wallet account ${accountId} not found`);
1809
+ }
1810
+ return toLedgerAccount(row);
1811
+ }
1812
+ async getAccountById(accountId) {
1813
+ const row = await this.em.getRepository(WalletAccountEntity).findOne({
1814
+ where: {
1815
+ id: accountId
1816
+ }
1817
+ });
1818
+ return row ? toLedgerAccount(row) : null;
1819
+ }
1820
+ async appendLedgerEntry(entry) {
1821
+ const existing = await this.findLedgerByIdempotencyKey(entry.idempotencyKey);
1822
+ if (existing) {
1823
+ return existing;
1824
+ }
1825
+ const repo = this.em.getRepository(WalletLedgerEntryEntity);
1826
+ try {
1827
+ const row = await repo.save(repo.create({
1828
+ accountId: entry.accountId,
1829
+ entryType: entry.entryType,
1830
+ amount: String(entry.amount),
1831
+ balanceAfterAvailable: String(entry.balanceAfterAvailable),
1832
+ balanceAfterHeld: String(entry.balanceAfterHeld),
1833
+ idempotencyKey: entry.idempotencyKey,
1834
+ referenceType: entry.referenceType,
1835
+ referenceId: entry.referenceId,
1836
+ metadata: entry.metadata
1837
+ }));
1838
+ return toLedgerEntry(row);
1839
+ } catch (error) {
1840
+ const err = error;
1841
+ const code = err?.code || err?.errno;
1842
+ if (code === "ER_DUP_ENTRY" || code === 1062) {
1843
+ const dup = await this.findLedgerByIdempotencyKey(entry.idempotencyKey);
1844
+ if (dup) {
1845
+ return dup;
1846
+ }
1847
+ throw new DuplicateOperationError("Duplicate idempotency key", null);
1848
+ }
1849
+ throw error;
1850
+ }
1851
+ }
1852
+ async updateBalances(accountId, deltas) {
1853
+ const row = await this.em.getRepository(WalletAccountEntity).createQueryBuilder("wa").setLock("pessimistic_write").where("wa.id = :id", {
1854
+ id: accountId
1855
+ }).getOne();
1856
+ if (!row) {
1857
+ throw new Error(`Wallet account ${accountId} not found`);
1858
+ }
1859
+ const available = toNumber(row.availableBalance) + (deltas.availableDelta ?? 0);
1860
+ const held = toNumber(row.heldBalance) + (deltas.heldDelta ?? 0);
1861
+ if (available < 0 || held < 0) {
1862
+ throw new Error("Balance cannot be negative");
1863
+ }
1864
+ row.availableBalance = String(available);
1865
+ row.heldBalance = String(held);
1866
+ const saved = await this.em.getRepository(WalletAccountEntity).save(row);
1867
+ return toLedgerAccount(saved);
1868
+ }
1869
+ async findHoldByReference(ref) {
1870
+ const row = await this.em.getRepository(WalletHoldEntity).findOne({
1871
+ where: {
1872
+ referenceType: ref.referenceType,
1873
+ referenceId: ref.referenceId
1874
+ },
1875
+ order: {
1876
+ id: "DESC"
1877
+ }
1878
+ });
1879
+ return row ? toLedgerHold(row) : null;
1880
+ }
1881
+ async upsertHold(hold) {
1882
+ const repo = this.em.getRepository(WalletHoldEntity);
1883
+ let row;
1884
+ if (hold.id != null) {
1885
+ row = await repo.findOne({
1886
+ where: {
1887
+ id: hold.id
1888
+ }
1889
+ });
1890
+ }
1891
+ if (!row) {
1892
+ row = await repo.findOne({
1893
+ where: {
1894
+ referenceType: hold.referenceType,
1895
+ referenceId: hold.referenceId
1896
+ }
1897
+ });
1898
+ }
1899
+ if (!row) {
1900
+ row = repo.create({
1901
+ sourceAccountId: hold.sourceAccountId,
1902
+ targetAccountId: hold.targetAccountId,
1903
+ amount: String(hold.amount),
1904
+ status: hold.status,
1905
+ referenceType: hold.referenceType,
1906
+ referenceId: hold.referenceId,
1907
+ idempotencyKey: hold.idempotencyKey
1908
+ });
1909
+ } else {
1910
+ row.sourceAccountId = hold.sourceAccountId;
1911
+ row.targetAccountId = hold.targetAccountId;
1912
+ row.amount = String(hold.amount);
1913
+ row.status = hold.status;
1914
+ row.idempotencyKey = hold.idempotencyKey;
1915
+ }
1916
+ const saved = await repo.save(row);
1917
+ return toLedgerHold(saved);
1918
+ }
1919
+ async findLedgerByIdempotencyKey(key) {
1920
+ const row = await this.em.getRepository(WalletLedgerEntryEntity).findOne({
1921
+ where: {
1922
+ idempotencyKey: key
1923
+ }
1924
+ });
1925
+ return row ? toLedgerEntry(row) : null;
1926
+ }
1927
+ async listLedgerEntries(accountId, limit, offset) {
1928
+ const rows = await this.em.getRepository(WalletLedgerEntryEntity).find({
1929
+ where: {
1930
+ accountId
1931
+ },
1932
+ order: {
1933
+ id: "DESC"
1934
+ },
1935
+ take: limit,
1936
+ skip: offset
1937
+ });
1938
+ return rows.map(toLedgerEntry);
1939
+ }
1940
+ }, __name(_a2, "TypeOrmWalletStoreTx"), _a2);
1941
+ var _TypeOrmWalletStore = class _TypeOrmWalletStore {
1942
+ constructor(connection) {
1943
+ __publicField(this, "connection");
1944
+ this.connection = connection;
1945
+ }
1946
+ async runInTransaction(fn, ctx) {
1947
+ if (isEntityManager(ctx?.nativeTx)) {
1948
+ return fn(new TypeOrmWalletStoreTx(ctx.nativeTx));
1949
+ }
1950
+ return this.connection.manager.transaction(async (em) => fn(new TypeOrmWalletStoreTx(em)));
1951
+ }
1952
+ };
1953
+ __name(_TypeOrmWalletStore, "TypeOrmWalletStore");
1954
+ var TypeOrmWalletStore = _TypeOrmWalletStore;
1955
+
1956
+ // src/typeorm/migrations/CreateWalletTables.ts
1957
+ async function indexExists(queryRunner, tableName, indexName) {
1958
+ const rows = await queryRunner.query(`SELECT COUNT(1) AS c
1959
+ FROM information_schema.statistics
1960
+ WHERE table_schema = DATABASE()
1961
+ AND table_name = ?
1962
+ AND index_name = ?`, [
1963
+ tableName,
1964
+ indexName
1965
+ ]);
1966
+ return Number(rows[0]?.c ?? 0) > 0;
1967
+ }
1968
+ __name(indexExists, "indexExists");
1969
+ var _CreateWalletTables1790000000000 = class _CreateWalletTables1790000000000 {
1970
+ async up(queryRunner) {
1971
+ if (!await queryRunner.hasTable("wallet_account")) {
1972
+ await queryRunner.query(`
1973
+ CREATE TABLE \`wallet_account\` (
1974
+ \`id\` int NOT NULL AUTO_INCREMENT,
1975
+ \`owner_type\` varchar(32) NOT NULL,
1976
+ \`owner_id\` int NOT NULL,
1977
+ \`currency\` varchar(8) NOT NULL DEFAULT 'INR',
1978
+ \`available_balance\` bigint NOT NULL DEFAULT 0,
1979
+ \`held_balance\` bigint NOT NULL DEFAULT 0,
1980
+ \`created_by\` int NULL,
1981
+ \`created_date\` datetime NULL,
1982
+ \`modified_by\` int NULL,
1983
+ \`modified_date\` datetime NULL,
1984
+ PRIMARY KEY (\`id\`)
1985
+ ) ENGINE=InnoDB
1986
+ `);
1987
+ }
1988
+ if (!await indexExists(queryRunner, "wallet_account", "IDX_wallet_account_owner_currency")) {
1989
+ await queryRunner.query(`
1990
+ CREATE UNIQUE INDEX \`IDX_wallet_account_owner_currency\`
1991
+ ON \`wallet_account\` (\`owner_type\`, \`owner_id\`, \`currency\`)
1992
+ `);
1993
+ }
1994
+ if (!await queryRunner.hasTable("wallet_ledger_entry")) {
1995
+ await queryRunner.query(`
1996
+ CREATE TABLE \`wallet_ledger_entry\` (
1997
+ \`id\` int NOT NULL AUTO_INCREMENT,
1998
+ \`account_id\` int NOT NULL,
1999
+ \`entry_type\` varchar(32) NOT NULL,
2000
+ \`amount\` bigint NOT NULL,
2001
+ \`balance_after_available\` bigint NOT NULL,
2002
+ \`balance_after_held\` bigint NOT NULL,
2003
+ \`idempotency_key\` varchar(255) NOT NULL,
2004
+ \`reference_type\` varchar(64) NULL,
2005
+ \`reference_id\` varchar(128) NULL,
2006
+ \`metadata\` json NULL,
2007
+ \`created_by\` int NULL,
2008
+ \`created_date\` datetime NULL,
2009
+ \`modified_by\` int NULL,
2010
+ \`modified_date\` datetime NULL,
2011
+ PRIMARY KEY (\`id\`),
2012
+ UNIQUE INDEX \`UQ_wallet_ledger_idempotency_key\` (\`idempotency_key\`)
2013
+ ) ENGINE=InnoDB
2014
+ `);
2015
+ }
2016
+ if (!await indexExists(queryRunner, "wallet_ledger_entry", "IDX_wallet_ledger_account_id")) {
2017
+ await queryRunner.query(`
2018
+ CREATE INDEX \`IDX_wallet_ledger_account_id\`
2019
+ ON \`wallet_ledger_entry\` (\`account_id\`)
2020
+ `);
2021
+ }
2022
+ if (!await queryRunner.hasTable("wallet_hold")) {
2023
+ await queryRunner.query(`
2024
+ CREATE TABLE \`wallet_hold\` (
2025
+ \`id\` int NOT NULL AUTO_INCREMENT,
2026
+ \`source_account_id\` int NOT NULL,
2027
+ \`target_account_id\` int NOT NULL,
2028
+ \`amount\` bigint NOT NULL,
2029
+ \`status\` varchar(16) NOT NULL DEFAULT 'active',
2030
+ \`reference_type\` varchar(64) NOT NULL,
2031
+ \`reference_id\` varchar(128) NOT NULL,
2032
+ \`idempotency_key\` varchar(255) NOT NULL,
2033
+ \`created_by\` int NULL,
2034
+ \`created_date\` datetime NULL,
2035
+ \`modified_by\` int NULL,
2036
+ \`modified_date\` datetime NULL,
2037
+ PRIMARY KEY (\`id\`)
2038
+ ) ENGINE=InnoDB
2039
+ `);
2040
+ }
2041
+ if (!await indexExists(queryRunner, "wallet_hold", "IDX_wallet_hold_reference")) {
2042
+ await queryRunner.query(`
2043
+ CREATE INDEX \`IDX_wallet_hold_reference\`
2044
+ ON \`wallet_hold\` (\`reference_type\`, \`reference_id\`)
2045
+ `);
2046
+ }
2047
+ }
2048
+ async down(queryRunner) {
2049
+ if (await queryRunner.hasTable("wallet_hold")) {
2050
+ await queryRunner.query("DROP TABLE `wallet_hold`");
2051
+ }
2052
+ if (await queryRunner.hasTable("wallet_ledger_entry")) {
2053
+ await queryRunner.query("DROP TABLE `wallet_ledger_entry`");
2054
+ }
2055
+ if (await queryRunner.hasTable("wallet_account")) {
2056
+ await queryRunner.query("DROP TABLE `wallet_account`");
2057
+ }
2058
+ }
2059
+ };
2060
+ __name(_CreateWalletTables1790000000000, "CreateWalletTables1790000000000");
2061
+ var CreateWalletTables1790000000000 = _CreateWalletTables1790000000000;
2062
+
2063
+ // src/typeorm/metadata.ts
2064
+ function getWalletOrmMetadata() {
2065
+ return {
2066
+ entities: [
2067
+ WalletAccountEntity,
2068
+ WalletLedgerEntryEntity,
2069
+ WalletHoldEntity
2070
+ ],
2071
+ migrations: [
2072
+ CreateWalletTables1790000000000
2073
+ ]
2074
+ };
2075
+ }
2076
+ __name(getWalletOrmMetadata, "getWalletOrmMetadata");
2077
+
2078
+ // src/plugin-types.ts
2079
+ var WALLET_PLUGIN_TOKEN = "multivendor-wallet.plugin";
2080
+
2081
+ // src/plugin.ts
2082
+ function scheduleReconciliation(config, onRun) {
2083
+ if (!config.reconciliation?.enabled || !config.flags.enabled) {
2084
+ return void 0;
2085
+ }
2086
+ const cronExpr = config.reconciliation.cron ?? "0 2 * * *";
2087
+ let intervalId;
2088
+ try {
2089
+ const cron = __require("node-cron");
2090
+ return cron.schedule(cronExpr, () => {
2091
+ void onRun();
2092
+ });
2093
+ } catch {
2094
+ intervalId = setInterval(() => {
2095
+ void onRun();
2096
+ }, 24 * 60 * 60 * 1e3);
2097
+ return {
2098
+ stop: /* @__PURE__ */ __name(() => clearInterval(intervalId), "stop")
2099
+ };
2100
+ }
2101
+ }
2102
+ __name(scheduleReconciliation, "scheduleReconciliation");
2103
+ var _a3;
2104
+ var WalletPluginImpl = (_a3 = class {
2105
+ constructor(config, store) {
2106
+ __publicField(this, "client");
2107
+ __publicField(this, "hooks");
2108
+ __publicField(this, "flags");
2109
+ __publicField(this, "http");
2110
+ __publicField(this, "payment");
2111
+ __publicField(this, "onAdminMutation");
2112
+ __publicField(this, "resolveOrderCustomerId");
2113
+ __publicField(this, "cronTask");
2114
+ this.flags = config.flags;
2115
+ this.onAdminMutation = config.onAdminMutation;
2116
+ this.resolveOrderCustomerId = config.resolveOrderCustomerId;
2117
+ this.client = new WalletLedgerClient(store, {
2118
+ defaultCurrency: config.defaultCurrency ?? "INR",
2119
+ platformOwnerId: config.platformOwnerId
2120
+ });
2121
+ this.hooks = createLifecycleHooks({
2122
+ client: this.client,
2123
+ platformOwnerId: config.platformOwnerId,
2124
+ defaultCurrency: config.defaultCurrency ?? "INR",
2125
+ flags: config.flags
2126
+ });
2127
+ this.http = buildWalletHttp(this, config.http);
2128
+ this.payment = {
2129
+ createWalletPaymentAdapter: /* @__PURE__ */ __name(() => createWalletPaymentAdapter(this), "createWalletPaymentAdapter")
2130
+ };
2131
+ if (config.reconciliation?.enabled && config.flags.enabled) {
2132
+ this.cronTask = scheduleReconciliation(config, async () => {
2133
+ const report = await runWalletReconciliation();
2134
+ if (report.parityDeltas.length > 0 || report.cacheMismatches.length > 0) {
2135
+ config.reconciliation?.onMismatch?.(report);
2136
+ config.reconciliation?.onLog?.("warn", "wallet reconciliation mismatch", report);
2137
+ } else {
2138
+ config.reconciliation?.onLog?.("info", "wallet reconciliation ok");
2139
+ }
2140
+ });
2141
+ }
2142
+ }
2143
+ isEnabled() {
2144
+ return this.flags.enabled;
2145
+ }
2146
+ isLedgerWritesEnabled() {
2147
+ return this.flags.enabled && this.flags.ledgerWrites;
2148
+ }
2149
+ isSettlementDebitEnabled() {
2150
+ return this.flags.enabled && this.flags.settlementDebit;
2151
+ }
2152
+ isCheckoutEnabled() {
2153
+ return this.flags.enabled && this.flags.checkout;
2154
+ }
2155
+ async shutdown() {
2156
+ this.cronTask?.stop();
2157
+ }
2158
+ }, __name(_a3, "WalletPluginImpl"), _a3);
2159
+ function registerWalletPlugin(config) {
2160
+ if (config.dataSource == null) {
2161
+ throw new Error("WalletPluginConfig.dataSource is required (host TypeORM Connection).");
2162
+ }
2163
+ bindWalletConnection(config.dataSource);
2164
+ const store = new TypeOrmWalletStore(config.dataSource);
2165
+ const plugin = new WalletPluginImpl(config, store);
2166
+ bindWalletPlugin(plugin);
2167
+ return plugin;
2168
+ }
2169
+ __name(registerWalletPlugin, "registerWalletPlugin");
2170
+
2171
+ export { AdminWalletController, CreateWalletTables1790000000000, CustomerWalletController, DuplicateOperationError, HoldNotFoundError, HoldStatus, InsufficientBalanceError, InvalidAmountError, LedgerEntryType, PaymentProviderWallet, TypeOrmWalletStore, VendorWalletController, WALLET_PLUGIN_TOKEN, WalletAccountEntity, WalletAccountType, WalletConfigError, WalletError, WalletHoldEntity, WalletLedgerClient, WalletLedgerEntryEntity, createExpressWalletRouter, createWalletPaymentAdapter, fromMinorUnit, getWalletOrmMetadata, registerWalletPlugin, toMinorUnit };
2172
+ //# sourceMappingURL=index.js.map
2173
+ //# sourceMappingURL=index.js.map