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