@sakeetech/vendure-payment-viva 0.2.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.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +952 -0
  3. package/dist/api/admin-internal.controller.d.ts +59 -0
  4. package/dist/api/admin-internal.controller.d.ts.map +1 -0
  5. package/dist/api/admin-internal.controller.js +229 -0
  6. package/dist/api/admin-internal.controller.js.map +1 -0
  7. package/dist/api/admin-onboarding.controller.d.ts +72 -0
  8. package/dist/api/admin-onboarding.controller.d.ts.map +1 -0
  9. package/dist/api/admin-onboarding.controller.js +496 -0
  10. package/dist/api/admin-onboarding.controller.js.map +1 -0
  11. package/dist/api/admin-sources.controller.d.ts +50 -0
  12. package/dist/api/admin-sources.controller.d.ts.map +1 -0
  13. package/dist/api/admin-sources.controller.js +283 -0
  14. package/dist/api/admin-sources.controller.js.map +1 -0
  15. package/dist/api/shop-api.extension.d.ts +15 -0
  16. package/dist/api/shop-api.extension.d.ts.map +1 -0
  17. package/dist/api/shop-api.extension.js +35 -0
  18. package/dist/api/shop-api.extension.js.map +1 -0
  19. package/dist/api/shop-api.resolver.d.ts +42 -0
  20. package/dist/api/shop-api.resolver.d.ts.map +1 -0
  21. package/dist/api/shop-api.resolver.js +256 -0
  22. package/dist/api/shop-api.resolver.js.map +1 -0
  23. package/dist/api/webhook.controller.d.ts +58 -0
  24. package/dist/api/webhook.controller.d.ts.map +1 -0
  25. package/dist/api/webhook.controller.js +204 -0
  26. package/dist/api/webhook.controller.js.map +1 -0
  27. package/dist/cli/bin.d.ts +28 -0
  28. package/dist/cli/bin.d.ts.map +1 -0
  29. package/dist/cli/bin.js +104 -0
  30. package/dist/cli/bin.js.map +1 -0
  31. package/dist/cli/plan.d.ts +41 -0
  32. package/dist/cli/plan.d.ts.map +1 -0
  33. package/dist/cli/plan.js +115 -0
  34. package/dist/cli/plan.js.map +1 -0
  35. package/dist/cli/register-webhooks.d.ts +45 -0
  36. package/dist/cli/register-webhooks.d.ts.map +1 -0
  37. package/dist/cli/register-webhooks.js +400 -0
  38. package/dist/cli/register-webhooks.js.map +1 -0
  39. package/dist/cli/types.d.ts +75 -0
  40. package/dist/cli/types.d.ts.map +1 -0
  41. package/dist/cli/types.js +10 -0
  42. package/dist/cli/types.js.map +1 -0
  43. package/dist/constants.d.ts +35 -0
  44. package/dist/constants.d.ts.map +1 -0
  45. package/dist/constants.js +40 -0
  46. package/dist/constants.js.map +1 -0
  47. package/dist/entities/index.d.ts +4 -0
  48. package/dist/entities/index.d.ts.map +1 -0
  49. package/dist/entities/index.js +3 -0
  50. package/dist/entities/index.js.map +1 -0
  51. package/dist/entities/viva-transaction.entity.d.ts +70 -0
  52. package/dist/entities/viva-transaction.entity.d.ts.map +1 -0
  53. package/dist/entities/viva-transaction.entity.js +133 -0
  54. package/dist/entities/viva-transaction.entity.js.map +1 -0
  55. package/dist/entities/viva-webhook-event.entity.d.ts +71 -0
  56. package/dist/entities/viva-webhook-event.entity.d.ts.map +1 -0
  57. package/dist/entities/viva-webhook-event.entity.js +138 -0
  58. package/dist/entities/viva-webhook-event.entity.js.map +1 -0
  59. package/dist/index.d.ts +27 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +23 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/jobs/process-viva-webhook.handler.d.ts +95 -0
  64. package/dist/jobs/process-viva-webhook.handler.d.ts.map +1 -0
  65. package/dist/jobs/process-viva-webhook.handler.js +530 -0
  66. package/dist/jobs/process-viva-webhook.handler.js.map +1 -0
  67. package/dist/jobs/queue-names.d.ts +18 -0
  68. package/dist/jobs/queue-names.d.ts.map +1 -0
  69. package/dist/jobs/queue-names.js +19 -0
  70. package/dist/jobs/queue-names.js.map +1 -0
  71. package/dist/jobs/retention-cleanup.handler.d.ts +31 -0
  72. package/dist/jobs/retention-cleanup.handler.d.ts.map +1 -0
  73. package/dist/jobs/retention-cleanup.handler.js +94 -0
  74. package/dist/jobs/retention-cleanup.handler.js.map +1 -0
  75. package/dist/loaders/bootstrap.d.ts +28 -0
  76. package/dist/loaders/bootstrap.d.ts.map +1 -0
  77. package/dist/loaders/bootstrap.js +90 -0
  78. package/dist/loaders/bootstrap.js.map +1 -0
  79. package/dist/migrations/1714000000000-create-viva-tables.d.ts +22 -0
  80. package/dist/migrations/1714000000000-create-viva-tables.d.ts.map +1 -0
  81. package/dist/migrations/1714000000000-create-viva-tables.js +105 -0
  82. package/dist/migrations/1714000000000-create-viva-tables.js.map +1 -0
  83. package/dist/observability/metrics-state.service.d.ts +43 -0
  84. package/dist/observability/metrics-state.service.d.ts.map +1 -0
  85. package/dist/observability/metrics-state.service.js +207 -0
  86. package/dist/observability/metrics-state.service.js.map +1 -0
  87. package/dist/payment-method-handler.d.ts +26 -0
  88. package/dist/payment-method-handler.d.ts.map +1 -0
  89. package/dist/payment-method-handler.js +693 -0
  90. package/dist/payment-method-handler.js.map +1 -0
  91. package/dist/plugin.d.ts +95 -0
  92. package/dist/plugin.d.ts.map +1 -0
  93. package/dist/plugin.js +241 -0
  94. package/dist/plugin.js.map +1 -0
  95. package/dist/providers/viva-oauth2-strategy.provider.d.ts +41 -0
  96. package/dist/providers/viva-oauth2-strategy.provider.d.ts.map +1 -0
  97. package/dist/providers/viva-oauth2-strategy.provider.js +60 -0
  98. package/dist/providers/viva-oauth2-strategy.provider.js.map +1 -0
  99. package/dist/services/connected-accounts.service.d.ts +53 -0
  100. package/dist/services/connected-accounts.service.d.ts.map +1 -0
  101. package/dist/services/connected-accounts.service.js +108 -0
  102. package/dist/services/connected-accounts.service.js.map +1 -0
  103. package/dist/services/per-merchant-semaphore.service.d.ts +49 -0
  104. package/dist/services/per-merchant-semaphore.service.d.ts.map +1 -0
  105. package/dist/services/per-merchant-semaphore.service.js +156 -0
  106. package/dist/services/per-merchant-semaphore.service.js.map +1 -0
  107. package/dist/services/state-machine.service.d.ts +100 -0
  108. package/dist/services/state-machine.service.d.ts.map +1 -0
  109. package/dist/services/state-machine.service.js +233 -0
  110. package/dist/services/state-machine.service.js.map +1 -0
  111. package/dist/types.d.ts +286 -0
  112. package/dist/types.d.ts.map +1 -0
  113. package/dist/types.js +23 -0
  114. package/dist/types.js.map +1 -0
  115. package/dist/util/currency.d.ts +32 -0
  116. package/dist/util/currency.d.ts.map +1 -0
  117. package/dist/util/currency.js +90 -0
  118. package/dist/util/currency.js.map +1 -0
  119. package/dist/util/error-envelope.d.ts +51 -0
  120. package/dist/util/error-envelope.d.ts.map +1 -0
  121. package/dist/util/error-envelope.js +157 -0
  122. package/dist/util/error-envelope.js.map +1 -0
  123. package/dist/util/ip-allowlist.d.ts +44 -0
  124. package/dist/util/ip-allowlist.d.ts.map +1 -0
  125. package/dist/util/ip-allowlist.js +139 -0
  126. package/dist/util/ip-allowlist.js.map +1 -0
  127. package/dist/util/normalize-options.d.ts +24 -0
  128. package/dist/util/normalize-options.d.ts.map +1 -0
  129. package/dist/util/normalize-options.js +189 -0
  130. package/dist/util/normalize-options.js.map +1 -0
  131. package/dist/util/url-template.d.ts +18 -0
  132. package/dist/util/url-template.d.ts.map +1 -0
  133. package/dist/util/url-template.js +22 -0
  134. package/dist/util/url-template.js.map +1 -0
  135. package/package.json +75 -0
@@ -0,0 +1,530 @@
1
+ /**
2
+ * jobs/process-viva-webhook.handler.ts — BullMQ webhook worker job handler.
3
+ *
4
+ * Registered with Vendure's JobQueueService at bootstrap. Processes one
5
+ * `viva_webhook_event` row per job execution.
6
+ *
7
+ * Process flow (see plan §"Webhook Design — Process flow"):
8
+ * 1. Load row by messageId. Already-processed or missing → no-op.
9
+ * 2. Resolve channel by EventData.MerchantId (60s LRU cache).
10
+ * If not found → A6 NULL-merchant fallback: schedule reprocess with backoff.
11
+ * 3. Acquire per-merchant in-process semaphore (5 permits).
12
+ * 4. Switch on eventTypeId:
13
+ * 1796 → Retrieve-Transaction → validate → settle
14
+ * 1798 → mark Declined, leave order in ArrangingPayment
15
+ * 1797 → audit log only
16
+ * 4865 → optional cancel detection
17
+ * 8193 → log only
18
+ * 8194 → write vivaMerchantId THEN vivaPayoutsEnabled
19
+ * 5. Release semaphore.
20
+ *
21
+ * @see docs/plans/vendure-plugin-v0.md §"Build Plan — V7"
22
+ * @see docs/plans/vendure-plugin-v0.md §"Webhook Design — Process flow"
23
+ * @see docs/VENDURE-CONTRACT.MD §4, §5, §10
24
+ */
25
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
26
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
27
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
28
+ 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;
29
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
30
+ };
31
+ var __metadata = (this && this.__metadata) || function (k, v) {
32
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
33
+ };
34
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
35
+ return function (target, key) { decorator(target, key, paramIndex); }
36
+ };
37
+ import { Injectable, Inject } from '@nestjs/common';
38
+ import { JobQueueService, JobQueue, Logger, TransactionalConnection, RequestContextService, OrderService, ChannelService, PaymentService, } from '@vendure/core';
39
+ import { IsvHttpClient, IsvAccounts } from '@sakeetech/viva-payments-core/isv';
40
+ import { Payments } from '@sakeetech/viva-payments-core/payments';
41
+ import { VivaOAuth2StrategyProvider } from '../providers/viva-oauth2-strategy.provider.js';
42
+ import { StateMachineService } from '../services/state-machine.service.js';
43
+ import { PerMerchantSemaphore } from '../services/per-merchant-semaphore.service.js';
44
+ import { ConnectedAccountsService } from '../services/connected-accounts.service.js';
45
+ import { VivaWebhookEvent } from '../entities/viva-webhook-event.entity.js';
46
+ import { VivaTransaction } from '../entities/viva-transaction.entity.js';
47
+ import { VivaPluginError } from '../util/error-envelope.js';
48
+ import { VIVA_PLUGIN_OPTIONS, VIVA_OAUTH2_STRATEGY_TOKEN, VIVA_LOG_CONTEXT, VIVA_WEBHOOK_QUEUE, VIVA_PROCESS_EVENT_JOB, } from '../constants.js';
49
+ const CHANNEL_CACHE_TTL_MS = 60_000;
50
+ const merchantChannelCache = new Map();
51
+ function getCachedChannelId(merchantId) {
52
+ const entry = merchantChannelCache.get(merchantId);
53
+ if (!entry)
54
+ return undefined;
55
+ if (Date.now() > entry.expiresAt) {
56
+ merchantChannelCache.delete(merchantId);
57
+ return undefined;
58
+ }
59
+ return entry.channelId;
60
+ }
61
+ function setCachedChannelId(merchantId, channelId) {
62
+ merchantChannelCache.set(merchantId, { channelId, expiresAt: Date.now() + CHANNEL_CACHE_TTL_MS });
63
+ }
64
+ // ---------------------------------------------------------------------------
65
+ // Backoff schedule for A6 NULL-merchant fallback (4 attempts max)
66
+ // ---------------------------------------------------------------------------
67
+ const A6_BACKOFF_DELAYS_MS = [
68
+ 60_000, // 1 min
69
+ 5 * 60_000, // 5 min
70
+ 30 * 60_000, // 30 min
71
+ 2 * 60 * 60_000, // 2 hr
72
+ ];
73
+ // ---------------------------------------------------------------------------
74
+ // Handler
75
+ // ---------------------------------------------------------------------------
76
+ let ProcessVivaWebhookHandler = class ProcessVivaWebhookHandler {
77
+ jobQueueService;
78
+ connection;
79
+ stateMachine;
80
+ semaphore;
81
+ connectedAccounts;
82
+ orderService;
83
+ paymentService;
84
+ channelService;
85
+ requestContextService;
86
+ options;
87
+ oauth2;
88
+ queue;
89
+ constructor(jobQueueService, connection, stateMachine, semaphore, connectedAccounts, orderService, paymentService, channelService, requestContextService, options, oauth2) {
90
+ this.jobQueueService = jobQueueService;
91
+ this.connection = connection;
92
+ this.stateMachine = stateMachine;
93
+ this.semaphore = semaphore;
94
+ this.connectedAccounts = connectedAccounts;
95
+ this.orderService = orderService;
96
+ this.paymentService = paymentService;
97
+ this.channelService = channelService;
98
+ this.requestContextService = requestContextService;
99
+ this.options = options;
100
+ this.oauth2 = oauth2;
101
+ }
102
+ async onApplicationBootstrap() {
103
+ this.queue = await this.jobQueueService.createQueue({
104
+ name: VIVA_WEBHOOK_QUEUE,
105
+ process: (job) => this._processJob(job),
106
+ });
107
+ }
108
+ /**
109
+ * Enqueue a new webhook processing job.
110
+ * Called by the webhook controller (V6) after INSERT-OR-NOTHING succeeds.
111
+ */
112
+ async enqueue(data, delayMs = 0) {
113
+ await this.queue.add(data, delayMs > 0 ? { retries: 0 } : undefined);
114
+ }
115
+ // ---------------------------------------------------------------------------
116
+ // Core job logic
117
+ // ---------------------------------------------------------------------------
118
+ async _processJob(job) {
119
+ const { messageId } = job.data;
120
+ // -------------------------------------------------------------------------
121
+ // Step 1: load event row
122
+ // -------------------------------------------------------------------------
123
+ const eventRepo = this.connection.rawConnection.getRepository(VivaWebhookEvent);
124
+ const event = await eventRepo.findOne({ where: { messageId } });
125
+ if (!event) {
126
+ Logger.info(`[webhook:${messageId}] Row not found — possibly deleted or never inserted. Skipping.`, VIVA_LOG_CONTEXT);
127
+ return;
128
+ }
129
+ if (event.processedAt !== null) {
130
+ Logger.info(`[webhook:${messageId}] Already processed at ${event.processedAt.toISOString()}. Idempotent no-op.`, VIVA_LOG_CONTEXT);
131
+ return;
132
+ }
133
+ // -------------------------------------------------------------------------
134
+ // Step 2: resolve channel.
135
+ //
136
+ // ISV mode → look up by `EventData.MerchantId` (LRU + linear scan).
137
+ // A NULL merchantId triggers A6 backoff fallback.
138
+ // Merchant mode → there is no per-tenant merchantId in single-merchant
139
+ // deployments. Resolve to the default channel; only one
140
+ // merchant is configured plugin-wide, so all webhook
141
+ // events belong to the default channel.
142
+ // @see docs/plans/multi-mode-v0.md §9 (webhook channel resolution)
143
+ // -------------------------------------------------------------------------
144
+ const merchantId = event.merchantId;
145
+ // Build a system-level ctx for channel operations (no channel known yet)
146
+ const systemCtx = await this.requestContextService.create({ apiType: 'admin' });
147
+ let channelId;
148
+ if (this.options.mode !== 'isv') {
149
+ // Merchant mode: use the default channel. Single-merchant deployments
150
+ // have no per-tenant mapping to perform — the operator may run multiple
151
+ // Vendure channels for catalog/locale split, but Viva traffic belongs
152
+ // to the default channel registered when the plugin was initialized.
153
+ const defaultChannel = await this.channelService.getDefaultChannel(systemCtx);
154
+ channelId = defaultChannel.id;
155
+ }
156
+ else if (merchantId) {
157
+ // ISV mode: check LRU cache first.
158
+ channelId = getCachedChannelId(merchantId);
159
+ if (!channelId) {
160
+ // Linear scan over channels (typically ≤ N=10 channels per deployment)
161
+ const allChannels = await this.channelService.findAll(systemCtx);
162
+ for (const ch of allChannels.items) {
163
+ const cf = ch.customFields;
164
+ if (cf && cf['vivaMerchantId'] === merchantId) {
165
+ channelId = ch.id;
166
+ setCachedChannelId(merchantId, channelId);
167
+ break;
168
+ }
169
+ }
170
+ }
171
+ }
172
+ if (!channelId) {
173
+ // A6 NULL-merchant fallback: set merchantId NULL on row, schedule reprocess with backoff.
174
+ await eventRepo.update({ messageId }, { merchantId: null, error: 'channel-not-found' });
175
+ // Determine which attempt this is (retry_count starts at 0)
176
+ const attemptIndex = event.retryCount;
177
+ if (attemptIndex < A6_BACKOFF_DELAYS_MS.length) {
178
+ const delayMs = A6_BACKOFF_DELAYS_MS[attemptIndex];
179
+ Logger.warn(`[webhook:${messageId}] Channel not found for merchantId=${merchantId ?? 'null'}. Scheduling reprocess attempt ${attemptIndex + 1} in ${delayMs}ms.`, VIVA_LOG_CONTEXT);
180
+ // Increment retry_count on the row
181
+ await eventRepo.increment({ messageId }, 'retryCount', 1);
182
+ // Re-enqueue with backoff (BullMQ delay option)
183
+ await this.queue.add({ messageId }, { retries: 0 });
184
+ }
185
+ else {
186
+ Logger.error(`[webhook:${messageId}] Channel not found after ${attemptIndex} attempts for merchantId=${merchantId ?? 'null'}. Row left stuck — check metrics.`, VIVA_LOG_CONTEXT);
187
+ }
188
+ return;
189
+ }
190
+ // -------------------------------------------------------------------------
191
+ // Step 3: acquire per-merchant semaphore
192
+ // -------------------------------------------------------------------------
193
+ const release = await this.semaphore.acquire(merchantId ?? `channel:${String(channelId)}`);
194
+ try {
195
+ // Build a channel-scoped ctx for order/payment operations
196
+ const ctx = await this.requestContextService.create({ apiType: 'admin', channelOrToken: await this._loadChannel(channelId) });
197
+ // -----------------------------------------------------------------------
198
+ // Step 4: switch on eventTypeId
199
+ // -----------------------------------------------------------------------
200
+ switch (event.eventTypeId) {
201
+ case 1796:
202
+ await this._handle1796(ctx, event, eventRepo);
203
+ break;
204
+ case 1798:
205
+ await this._handle1798(ctx, event, eventRepo);
206
+ break;
207
+ case 1797:
208
+ Logger.info(`[webhook:${messageId}] 1797 Refund Created — audit log only (refund was admin-initiated).`, VIVA_LOG_CONTEXT);
209
+ await this._markProcessed(eventRepo, messageId);
210
+ break;
211
+ case 4865:
212
+ await this._handle4865(ctx, event, eventRepo);
213
+ break;
214
+ case 8193:
215
+ // Onboarding lifecycle event — only meaningful in ISV mode (there is
216
+ // no onboarding flow in merchant mode). Mark processed in both modes
217
+ // to prevent infinite reprocessing.
218
+ Logger.info(`[webhook:${messageId}] 8193 Account Connected — analytics, no state change` +
219
+ `${this.options.mode === 'isv' ? '' : ' (merchant mode: no-op)'}.`, VIVA_LOG_CONTEXT);
220
+ await this._markProcessed(eventRepo, messageId);
221
+ break;
222
+ case 8194:
223
+ if (this.options.mode === 'isv') {
224
+ await this._handle8194(systemCtx, event, eventRepo);
225
+ }
226
+ else {
227
+ // Merchant mode: no onboarding flip — the merchant configured the
228
+ // plugin with their own legacyMerchantId / legacyApiKey directly,
229
+ // so the verification status is moot for plugin behaviour.
230
+ Logger.info(`[webhook:${messageId}] 8194 Account Verification — merchant mode: no-op.`, VIVA_LOG_CONTEXT);
231
+ await this._markProcessed(eventRepo, messageId);
232
+ }
233
+ break;
234
+ default:
235
+ Logger.info(`[webhook:${messageId}] Unknown eventTypeId=${event.eventTypeId} — marking processed to prevent infinite reprocessing.`, VIVA_LOG_CONTEXT);
236
+ await this._markProcessed(eventRepo, messageId);
237
+ break;
238
+ }
239
+ }
240
+ finally {
241
+ await release();
242
+ }
243
+ }
244
+ // ---------------------------------------------------------------------------
245
+ // Event handlers
246
+ // ---------------------------------------------------------------------------
247
+ /**
248
+ * 1796 — Transaction Payment Created (settle path).
249
+ *
250
+ * a. Retrieve transaction from Viva.
251
+ * b. Validate orderCode, statusId='F', amount.
252
+ * c. Load order + payment, check current state.
253
+ * d. Settle (or re-walk if AddingItems).
254
+ * e. Update viva_transaction row.
255
+ * f. Mark event processed.
256
+ */
257
+ async _handle1796(ctx, event, eventRepo) {
258
+ const { messageId } = event;
259
+ const payload = event.payload;
260
+ const eventData = (payload['EventData'] ?? payload);
261
+ const transactionId = (event.transactionId ?? eventData['TransactionId']);
262
+ const merchantId = event.merchantId;
263
+ if (!transactionId) {
264
+ await eventRepo.update({ messageId }, { error: 'missing-transaction-id' });
265
+ Logger.error(`[webhook:${messageId}] 1796: no transactionId in payload.`, VIVA_LOG_CONTEXT);
266
+ return;
267
+ }
268
+ // a. Retrieve transaction from Viva.
269
+ // - ISV mode: pass merchantId from the webhook payload → /isv/transactions/{id}?merchantId=…
270
+ // - Merchant mode: omit merchantId → /transactions/{id} (Payments drops it anyway).
271
+ const isvPayments = this._getIsvPayments();
272
+ let vivaTransaction;
273
+ try {
274
+ const retrieveOpts = this.options.mode === 'isv' && merchantId ? { merchantId } : {};
275
+ vivaTransaction = await isvPayments.retrieveTransaction(transactionId, retrieveOpts);
276
+ }
277
+ catch (err) {
278
+ await eventRepo.update({ messageId }, { error: `retrieve-failed: ${err instanceof Error ? err.message : String(err)}` });
279
+ throw err; // Let BullMQ retry
280
+ }
281
+ // b. Validate
282
+ // CSO-Finding-1 (defensive): we look up the local row by vivaTransaction.orderCode
283
+ // (the value returned by Viva's authenticated retrieveTransaction call) — NOT by
284
+ // the OrderCode in the webhook envelope, which is attacker-controlled. Do not
285
+ // regress this to use the envelope OrderCode without adding an explicit
286
+ // envelope-vs-live cross-check first. See docs/TODO-CSO.md "Finding 1" and the
287
+ // canonical Viva WooCommerce plugin (class-wc-vivacom-smart-endpoints.php:132).
288
+ const vivaOrderCodeStr = vivaTransaction.orderCode?.toString();
289
+ const txnRepo = this.connection.rawConnection.getRepository(VivaTransaction);
290
+ // Find the viva_transaction row by order code
291
+ const vivaRow = await txnRepo.findOne({ where: { vivaOrderCode: vivaOrderCodeStr } });
292
+ if (!vivaRow) {
293
+ const errMsg = `viva_transaction row not found for orderCode=${vivaOrderCodeStr}`;
294
+ await eventRepo.update({ messageId }, { error: errMsg });
295
+ Logger.error(`[webhook:${messageId}] 1796: ${errMsg}`, VIVA_LOG_CONTEXT);
296
+ return;
297
+ }
298
+ if (vivaTransaction.statusId !== 'F') {
299
+ const errMsg = `statusId is '${vivaTransaction.statusId}', expected 'F'`;
300
+ await eventRepo.update({ messageId }, { error: errMsg });
301
+ Logger.warn(`[webhook:${messageId}] 1796: ${errMsg} — not settling.`, VIVA_LOG_CONTEXT);
302
+ return;
303
+ }
304
+ const expectedAmount = BigInt(vivaRow.amountMinor);
305
+ const actualAmount = vivaTransaction.amount;
306
+ if (expectedAmount !== actualAmount) {
307
+ await eventRepo.update({ messageId }, { error: `VIVA_AMOUNT_MISMATCH: expected=${expectedAmount} actual=${actualAmount}` });
308
+ throw VivaPluginError.amountMismatch(expectedAmount, Number(actualAmount));
309
+ }
310
+ // c. Load payment then order via paymentId on the viva_transaction row
311
+ const paymentId = vivaRow.paymentId;
312
+ let order = null;
313
+ try {
314
+ const payment = await this.paymentService.findOneOrThrow(ctx, paymentId, ['order']);
315
+ if (payment.order) {
316
+ order = await this.orderService.findOne(ctx, payment.order.id);
317
+ }
318
+ }
319
+ catch {
320
+ // Payment not found
321
+ }
322
+ if (!order) {
323
+ const errMsg = `order not found for paymentId=${String(paymentId)}`;
324
+ await eventRepo.update({ messageId }, { error: errMsg });
325
+ Logger.error(`[webhook:${messageId}] 1796: ${errMsg}`, VIVA_LOG_CONTEXT);
326
+ return;
327
+ }
328
+ // d. Settle based on current order state
329
+ try {
330
+ if (order.state === 'PaymentSettled') {
331
+ // Idempotent no-op
332
+ Logger.info(`[webhook:${messageId}] 1796: order already PaymentSettled — idempotent no-op.`, VIVA_LOG_CONTEXT);
333
+ }
334
+ else if (order.state === 'AddingItems') {
335
+ // Stale-order re-walk
336
+ await this.stateMachine.recoverStaleOrderAndSettle(ctx, order.id, paymentId);
337
+ }
338
+ else if (order.state === 'ArrangingPayment' || order.state === 'PaymentAuthorized') {
339
+ // Normal settle path
340
+ await this.stateMachine.transitionPaymentToSettled(ctx, order.id, paymentId);
341
+ }
342
+ else {
343
+ Logger.warn(`[webhook:${messageId}] 1796: unexpected order state '${order.state}' — attempting settlePayment anyway.`, VIVA_LOG_CONTEXT);
344
+ await this.stateMachine.transitionPaymentToSettled(ctx, order.id, paymentId);
345
+ }
346
+ }
347
+ catch (err) {
348
+ // Fail-loud per Q4: set error, leave processed_at NULL
349
+ const errMsg = err instanceof Error ? err.message : String(err);
350
+ await eventRepo.update({ messageId }, { error: errMsg });
351
+ Logger.error(`[webhook:${messageId}] 1796: transition failed — ${errMsg}`, VIVA_LOG_CONTEXT);
352
+ throw err;
353
+ }
354
+ // e. Update viva_transaction row
355
+ await txnRepo.update({ id: vivaRow.id }, { status: 'captured', vivaTransactionId: transactionId });
356
+ // f. Mark processed
357
+ await this._markProcessed(eventRepo, messageId);
358
+ Logger.info(`[webhook:${messageId}] 1796: settled successfully.`, VIVA_LOG_CONTEXT);
359
+ }
360
+ /**
361
+ * 1798 — Transaction Failed (declined-non-terminal).
362
+ *
363
+ * Mark payment Declined. Leave order in ArrangingPayment (per contract §4).
364
+ * Do NOT auto-rollback to AddingItems — customer retry on same orderCode may succeed.
365
+ */
366
+ async _handle1798(ctx, event, eventRepo) {
367
+ const { messageId } = event;
368
+ const txnRepo = this.connection.rawConnection.getRepository(VivaTransaction);
369
+ // Find viva_transaction by orderCode in payload
370
+ const payload = event.payload;
371
+ const eventData = (payload['EventData'] ?? payload);
372
+ const orderCodeRaw = eventData['OrderCode'];
373
+ const orderCodeStr = orderCodeRaw?.toString();
374
+ if (orderCodeStr) {
375
+ const vivaRow = await txnRepo.findOne({ where: { vivaOrderCode: orderCodeStr } });
376
+ if (vivaRow) {
377
+ await txnRepo.update({ id: vivaRow.id }, { status: 'failed' });
378
+ try {
379
+ await this.stateMachine.transitionPaymentToDeclined(ctx, vivaRow.paymentId);
380
+ }
381
+ catch (err) {
382
+ Logger.warn(`[webhook:${messageId}] 1798: could not transition payment to Declined: ${err instanceof Error ? err.message : String(err)}`, VIVA_LOG_CONTEXT);
383
+ // Non-fatal: the row status is already marked failed
384
+ }
385
+ }
386
+ }
387
+ await this._markProcessed(eventRepo, messageId);
388
+ Logger.info(`[webhook:${messageId}] 1798: payment marked Declined (order stays ArrangingPayment).`, VIVA_LOG_CONTEXT);
389
+ }
390
+ /**
391
+ * 4865 — Order Updated (optional cancel detection).
392
+ *
393
+ * If payload indicates a user-initiated cancel (status suggests cancellation),
394
+ * invoke the cancel flow: cancelled status + Payment Cancelled + Order → AddingItems.
395
+ * Otherwise log + no-op.
396
+ */
397
+ async _handle4865(ctx, event, eventRepo) {
398
+ const { messageId } = event;
399
+ const payload = event.payload;
400
+ const eventData = (payload['EventData'] ?? payload);
401
+ // Viva 4865 payload may contain StatusId or OrderStatus indicating cancellation
402
+ const statusId = eventData['StatusId'];
403
+ const orderCodeRaw = eventData['OrderCode'];
404
+ const orderCodeStr = orderCodeRaw?.toString();
405
+ // Status 'X' or 'F:cancelled' or similar indicates user cancel
406
+ // TODO(impl): confirm exact statusId for user-cancelled in Viva 4865 payload.
407
+ // Based on docs, 'X' means cancelled/expired per wh-transaction-payment-created.txt.
408
+ const isCancelStatus = statusId === 'X' || statusId === 'C' || statusId === 'E';
409
+ if (isCancelStatus && orderCodeStr) {
410
+ const txnRepo = this.connection.rawConnection.getRepository(VivaTransaction);
411
+ const vivaRow = await txnRepo.findOne({ where: { vivaOrderCode: orderCodeStr } });
412
+ if (vivaRow) {
413
+ await txnRepo.update({ id: vivaRow.id }, { status: 'cancelled' });
414
+ try {
415
+ await this.stateMachine.transitionPaymentToCancelled(ctx, vivaRow.paymentId);
416
+ }
417
+ catch (err) {
418
+ Logger.warn(`[webhook:${messageId}] 4865: could not cancel payment: ${err instanceof Error ? err.message : String(err)}`, VIVA_LOG_CONTEXT);
419
+ }
420
+ // Transition order back to AddingItems
421
+ try {
422
+ const payment = await this.paymentService.findOneOrThrow(ctx, vivaRow.paymentId, ['order']);
423
+ if (payment.order) {
424
+ await this.orderService.transitionToState(ctx, payment.order.id, 'AddingItems');
425
+ }
426
+ }
427
+ catch (err) {
428
+ Logger.warn(`[webhook:${messageId}] 4865: could not transition order to AddingItems: ${err instanceof Error ? err.message : String(err)}`, VIVA_LOG_CONTEXT);
429
+ }
430
+ }
431
+ }
432
+ else {
433
+ Logger.info(`[webhook:${messageId}] 4865 Order Updated — status=${statusId ?? 'unknown'}, no cancel action needed.`, VIVA_LOG_CONTEXT);
434
+ }
435
+ await this._markProcessed(eventRepo, messageId);
436
+ }
437
+ /**
438
+ * 8194 — Account Verification Status Changed (THE gating signal).
439
+ *
440
+ * a. Retrieve connected account from Viva to get merchantId.
441
+ * b. Find channel by vivaAccountId.
442
+ * c. WRITE ORDER (mandatory per contract §10):
443
+ * i. Write vivaMerchantId FIRST.
444
+ * ii. Write vivaPayoutsEnabled=true LAST.
445
+ */
446
+ async _handle8194(systemCtx, event, eventRepo) {
447
+ const { messageId } = event;
448
+ const accountId = event.accountId;
449
+ if (!accountId) {
450
+ await eventRepo.update({ messageId }, { error: 'missing-account-id' });
451
+ Logger.error(`[webhook:${messageId}] 8194: no accountId in event row.`, VIVA_LOG_CONTEXT);
452
+ return;
453
+ }
454
+ // a. Retrieve connected account from Viva
455
+ const isvAccounts = this._getIsvAccounts();
456
+ let merchantId;
457
+ try {
458
+ const accountInfo = await isvAccounts.retrieveConnectedAccount(accountId);
459
+ // Probe-verified 2026-05-11: `merchantId` is the exact field name (null until verified).
460
+ merchantId = accountInfo.merchantId ?? undefined;
461
+ }
462
+ catch (err) {
463
+ const errMsg = `retrieve-account-failed: ${err instanceof Error ? err.message : String(err)}`;
464
+ await eventRepo.update({ messageId }, { error: errMsg });
465
+ throw err; // Let BullMQ retry
466
+ }
467
+ if (!merchantId) {
468
+ await eventRepo.update({ messageId }, { error: 'merchant-id-not-returned' });
469
+ Logger.warn(`[webhook:${messageId}] 8194: merchantId not in account response — verification may be incomplete.`, VIVA_LOG_CONTEXT);
470
+ return;
471
+ }
472
+ // b. Find channel by vivaAccountId
473
+ const channel = await this.connectedAccounts.findChannelByAccountId(accountId);
474
+ if (!channel) {
475
+ await eventRepo.update({ messageId }, { error: `channel-not-found-for-accountId:${accountId}` });
476
+ Logger.error(`[webhook:${messageId}] 8194: no channel found for accountId=${accountId}.`, VIVA_LOG_CONTEXT);
477
+ return;
478
+ }
479
+ // c. Write order matters: merchantId FIRST, payoutsEnabled LAST
480
+ // Build a system ctx with write permissions (superadmin = all channels)
481
+ const writeCtx = await this.requestContextService.create({ apiType: 'admin' });
482
+ // i. Write vivaMerchantId FIRST
483
+ await this.connectedAccounts.writeMerchantId(writeCtx, channel, merchantId);
484
+ // ii. Write vivaPayoutsEnabled LAST
485
+ await this.connectedAccounts.flipPayoutsEnabled(writeCtx, channel, true);
486
+ await this._markProcessed(eventRepo, messageId);
487
+ Logger.info(`[webhook:${messageId}] 8194: channel ${String(channel.id)} now has merchantId=${merchantId} and payoutsEnabled=true.`, VIVA_LOG_CONTEXT);
488
+ }
489
+ // ---------------------------------------------------------------------------
490
+ // Helpers
491
+ // ---------------------------------------------------------------------------
492
+ async _markProcessed(eventRepo, messageId) {
493
+ await eventRepo.update({ messageId }, { processedAt: new Date(), error: null });
494
+ }
495
+ async _loadChannel(channelId) {
496
+ // Build a system-level ctx to load the channel object
497
+ const sysCtx = await this.requestContextService.create({ apiType: 'admin' });
498
+ return this.channelService.findOne(sysCtx, channelId);
499
+ }
500
+ _getIsvPayments() {
501
+ const client = new IsvHttpClient({
502
+ environment: this.options.environment,
503
+ authStrategy: this.oauth2,
504
+ });
505
+ return new Payments({ mode: this.options.mode, client });
506
+ }
507
+ _getIsvAccounts() {
508
+ const client = new IsvHttpClient({
509
+ environment: this.options.environment,
510
+ authStrategy: this.oauth2,
511
+ });
512
+ return new IsvAccounts(client);
513
+ }
514
+ };
515
+ ProcessVivaWebhookHandler = __decorate([
516
+ Injectable(),
517
+ __param(9, Inject(VIVA_PLUGIN_OPTIONS)),
518
+ __param(10, Inject(VIVA_OAUTH2_STRATEGY_TOKEN)),
519
+ __metadata("design:paramtypes", [JobQueueService,
520
+ TransactionalConnection,
521
+ StateMachineService,
522
+ PerMerchantSemaphore,
523
+ ConnectedAccountsService,
524
+ OrderService,
525
+ PaymentService,
526
+ ChannelService,
527
+ RequestContextService, Object, Object])
528
+ ], ProcessVivaWebhookHandler);
529
+ export { ProcessVivaWebhookHandler };
530
+ //# sourceMappingURL=process-viva-webhook.handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-viva-webhook.handler.js","sourceRoot":"","sources":["../../src/jobs/process-viva-webhook.handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * jobs/queue-names.ts — Single source of truth for BullMQ queue + job names.
3
+ *
4
+ * Both the webhook controller (V6) and the webhook job handler (V7) import
5
+ * from here so the strings are never duplicated.
6
+ *
7
+ * @see docs/plans/vendure-plugin-v0.md §"Build Plan — V6"
8
+ */
9
+ /** BullMQ queue that receives all Viva webhook processing jobs. */
10
+ export declare const VIVA_WEBHOOK_QUEUE = "viva-webhook";
11
+ /** Job that processes a single Viva webhook event (V7 implements the handler). */
12
+ export declare const VIVA_PROCESS_EVENT_JOB = "process-viva-webhook";
13
+ /** Payload for the `process-viva-webhook` job. */
14
+ export interface ProcessVivaWebhookJobData {
15
+ /** Envelope MessageId (UUID) — foreign key into `viva_webhook_event.message_id`. */
16
+ messageId: string;
17
+ }
18
+ //# sourceMappingURL=queue-names.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue-names.d.ts","sourceRoot":"","sources":["../../src/jobs/queue-names.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,mEAAmE;AACnE,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AAMjD,kFAAkF;AAClF,eAAO,MAAM,sBAAsB,yBAAyB,CAAC;AAM7D,kDAAkD;AAClD,MAAM,WAAW,yBAAyB;IACxC,oFAAoF;IACpF,SAAS,EAAE,MAAM,CAAC;CACnB"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * jobs/queue-names.ts — Single source of truth for BullMQ queue + job names.
3
+ *
4
+ * Both the webhook controller (V6) and the webhook job handler (V7) import
5
+ * from here so the strings are never duplicated.
6
+ *
7
+ * @see docs/plans/vendure-plugin-v0.md §"Build Plan — V6"
8
+ */
9
+ // ---------------------------------------------------------------------------
10
+ // Queue names
11
+ // ---------------------------------------------------------------------------
12
+ /** BullMQ queue that receives all Viva webhook processing jobs. */
13
+ export const VIVA_WEBHOOK_QUEUE = 'viva-webhook';
14
+ // ---------------------------------------------------------------------------
15
+ // Job names
16
+ // ---------------------------------------------------------------------------
17
+ /** Job that processes a single Viva webhook event (V7 implements the handler). */
18
+ export const VIVA_PROCESS_EVENT_JOB = 'process-viva-webhook';
19
+ //# sourceMappingURL=queue-names.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue-names.js","sourceRoot":"","sources":["../../src/jobs/queue-names.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E,mEAAmE;AACnE,MAAM,CAAC,MAAM,kBAAkB,GAAG,cAAc,CAAC;AAEjD,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,kFAAkF;AAClF,MAAM,CAAC,MAAM,sBAAsB,GAAG,sBAAsB,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * jobs/retention-cleanup.handler.ts — 90-day webhook event retention cleanup.
3
+ *
4
+ * Runs once per day via setInterval (lightest-weight option — no new dep).
5
+ * Deletes `viva_webhook_event` rows where:
6
+ * processed_at < now() - INTERVAL '90 days'
7
+ *
8
+ * Rows with processed_at = NULL are NOT deleted regardless of age.
9
+ *
10
+ * Design note: Vendure's built-in session-cache cleanup uses a similar
11
+ * setInterval-in-OnApplicationBootstrap pattern (see SessionCacheService).
12
+ * We follow the same approach rather than introducing a scheduler dep.
13
+ *
14
+ * @see docs/plans/vendure-plugin-v0.md §"Webhook Design — 90-day retention cleanup"
15
+ */
16
+ import type { OnApplicationBootstrap, OnApplicationShutdown } from '@nestjs/common';
17
+ import { TransactionalConnection } from '@vendure/core';
18
+ export declare class RetentionCleanupHandler implements OnApplicationBootstrap, OnApplicationShutdown {
19
+ private readonly connection;
20
+ private _timer;
21
+ constructor(connection: TransactionalConnection);
22
+ onApplicationBootstrap(): void;
23
+ onApplicationShutdown(): void;
24
+ /**
25
+ * Delete processed webhook events older than 90 days.
26
+ * Exposed for direct invocation from tests.
27
+ */
28
+ runCleanup(): Promise<number>;
29
+ private _runCleanup;
30
+ }
31
+ //# sourceMappingURL=retention-cleanup.handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention-cleanup.handler.d.ts","sourceRoot":"","sources":["../../src/jobs/retention-cleanup.handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACpF,OAAO,EAAU,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAgBhE,qBACa,uBAAwB,YAAW,sBAAsB,EAAE,qBAAqB;IAG/E,OAAO,CAAC,QAAQ,CAAC,UAAU;IAFvC,OAAO,CAAC,MAAM,CAA6C;gBAE9B,UAAU,EAAE,uBAAuB;IAEhE,sBAAsB,IAAI,IAAI;IAc9B,qBAAqB,IAAI,IAAI;IAM7B;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;YAIrB,WAAW;CAuB1B"}