@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.
- package/LICENSE +21 -0
- package/README.md +952 -0
- package/dist/api/admin-internal.controller.d.ts +59 -0
- package/dist/api/admin-internal.controller.d.ts.map +1 -0
- package/dist/api/admin-internal.controller.js +229 -0
- package/dist/api/admin-internal.controller.js.map +1 -0
- package/dist/api/admin-onboarding.controller.d.ts +72 -0
- package/dist/api/admin-onboarding.controller.d.ts.map +1 -0
- package/dist/api/admin-onboarding.controller.js +496 -0
- package/dist/api/admin-onboarding.controller.js.map +1 -0
- package/dist/api/admin-sources.controller.d.ts +50 -0
- package/dist/api/admin-sources.controller.d.ts.map +1 -0
- package/dist/api/admin-sources.controller.js +283 -0
- package/dist/api/admin-sources.controller.js.map +1 -0
- package/dist/api/shop-api.extension.d.ts +15 -0
- package/dist/api/shop-api.extension.d.ts.map +1 -0
- package/dist/api/shop-api.extension.js +35 -0
- package/dist/api/shop-api.extension.js.map +1 -0
- package/dist/api/shop-api.resolver.d.ts +42 -0
- package/dist/api/shop-api.resolver.d.ts.map +1 -0
- package/dist/api/shop-api.resolver.js +256 -0
- package/dist/api/shop-api.resolver.js.map +1 -0
- package/dist/api/webhook.controller.d.ts +58 -0
- package/dist/api/webhook.controller.d.ts.map +1 -0
- package/dist/api/webhook.controller.js +204 -0
- package/dist/api/webhook.controller.js.map +1 -0
- package/dist/cli/bin.d.ts +28 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +104 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/plan.d.ts +41 -0
- package/dist/cli/plan.d.ts.map +1 -0
- package/dist/cli/plan.js +115 -0
- package/dist/cli/plan.js.map +1 -0
- package/dist/cli/register-webhooks.d.ts +45 -0
- package/dist/cli/register-webhooks.d.ts.map +1 -0
- package/dist/cli/register-webhooks.js +400 -0
- package/dist/cli/register-webhooks.js.map +1 -0
- package/dist/cli/types.d.ts +75 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +10 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/constants.d.ts +35 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +40 -0
- package/dist/constants.js.map +1 -0
- package/dist/entities/index.d.ts +4 -0
- package/dist/entities/index.d.ts.map +1 -0
- package/dist/entities/index.js +3 -0
- package/dist/entities/index.js.map +1 -0
- package/dist/entities/viva-transaction.entity.d.ts +70 -0
- package/dist/entities/viva-transaction.entity.d.ts.map +1 -0
- package/dist/entities/viva-transaction.entity.js +133 -0
- package/dist/entities/viva-transaction.entity.js.map +1 -0
- package/dist/entities/viva-webhook-event.entity.d.ts +71 -0
- package/dist/entities/viva-webhook-event.entity.d.ts.map +1 -0
- package/dist/entities/viva-webhook-event.entity.js +138 -0
- package/dist/entities/viva-webhook-event.entity.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/jobs/process-viva-webhook.handler.d.ts +95 -0
- package/dist/jobs/process-viva-webhook.handler.d.ts.map +1 -0
- package/dist/jobs/process-viva-webhook.handler.js +530 -0
- package/dist/jobs/process-viva-webhook.handler.js.map +1 -0
- package/dist/jobs/queue-names.d.ts +18 -0
- package/dist/jobs/queue-names.d.ts.map +1 -0
- package/dist/jobs/queue-names.js +19 -0
- package/dist/jobs/queue-names.js.map +1 -0
- package/dist/jobs/retention-cleanup.handler.d.ts +31 -0
- package/dist/jobs/retention-cleanup.handler.d.ts.map +1 -0
- package/dist/jobs/retention-cleanup.handler.js +94 -0
- package/dist/jobs/retention-cleanup.handler.js.map +1 -0
- package/dist/loaders/bootstrap.d.ts +28 -0
- package/dist/loaders/bootstrap.d.ts.map +1 -0
- package/dist/loaders/bootstrap.js +90 -0
- package/dist/loaders/bootstrap.js.map +1 -0
- package/dist/migrations/1714000000000-create-viva-tables.d.ts +22 -0
- package/dist/migrations/1714000000000-create-viva-tables.d.ts.map +1 -0
- package/dist/migrations/1714000000000-create-viva-tables.js +105 -0
- package/dist/migrations/1714000000000-create-viva-tables.js.map +1 -0
- package/dist/observability/metrics-state.service.d.ts +43 -0
- package/dist/observability/metrics-state.service.d.ts.map +1 -0
- package/dist/observability/metrics-state.service.js +207 -0
- package/dist/observability/metrics-state.service.js.map +1 -0
- package/dist/payment-method-handler.d.ts +26 -0
- package/dist/payment-method-handler.d.ts.map +1 -0
- package/dist/payment-method-handler.js +693 -0
- package/dist/payment-method-handler.js.map +1 -0
- package/dist/plugin.d.ts +95 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +241 -0
- package/dist/plugin.js.map +1 -0
- package/dist/providers/viva-oauth2-strategy.provider.d.ts +41 -0
- package/dist/providers/viva-oauth2-strategy.provider.d.ts.map +1 -0
- package/dist/providers/viva-oauth2-strategy.provider.js +60 -0
- package/dist/providers/viva-oauth2-strategy.provider.js.map +1 -0
- package/dist/services/connected-accounts.service.d.ts +53 -0
- package/dist/services/connected-accounts.service.d.ts.map +1 -0
- package/dist/services/connected-accounts.service.js +108 -0
- package/dist/services/connected-accounts.service.js.map +1 -0
- package/dist/services/per-merchant-semaphore.service.d.ts +49 -0
- package/dist/services/per-merchant-semaphore.service.d.ts.map +1 -0
- package/dist/services/per-merchant-semaphore.service.js +156 -0
- package/dist/services/per-merchant-semaphore.service.js.map +1 -0
- package/dist/services/state-machine.service.d.ts +100 -0
- package/dist/services/state-machine.service.d.ts.map +1 -0
- package/dist/services/state-machine.service.js +233 -0
- package/dist/services/state-machine.service.js.map +1 -0
- package/dist/types.d.ts +286 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +23 -0
- package/dist/types.js.map +1 -0
- package/dist/util/currency.d.ts +32 -0
- package/dist/util/currency.d.ts.map +1 -0
- package/dist/util/currency.js +90 -0
- package/dist/util/currency.js.map +1 -0
- package/dist/util/error-envelope.d.ts +51 -0
- package/dist/util/error-envelope.d.ts.map +1 -0
- package/dist/util/error-envelope.js +157 -0
- package/dist/util/error-envelope.js.map +1 -0
- package/dist/util/ip-allowlist.d.ts +44 -0
- package/dist/util/ip-allowlist.d.ts.map +1 -0
- package/dist/util/ip-allowlist.js +139 -0
- package/dist/util/ip-allowlist.js.map +1 -0
- package/dist/util/normalize-options.d.ts +24 -0
- package/dist/util/normalize-options.d.ts.map +1 -0
- package/dist/util/normalize-options.js +189 -0
- package/dist/util/normalize-options.js.map +1 -0
- package/dist/util/url-template.d.ts +18 -0
- package/dist/util/url-template.d.ts.map +1 -0
- package/dist/util/url-template.js +22 -0
- package/dist/util/url-template.js.map +1 -0
- 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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;;;;;;;;;;;;;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAEpD,OAAO,EACL,eAAe,EACf,QAAQ,EACR,MAAM,EACN,uBAAuB,EACvB,qBAAqB,EACrB,YAAY,EACZ,cAAc,EACd,cAAc,GACf,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAC/E,OAAO,EAAE,QAAQ,EAAE,MAAM,wCAAwC,CAAC;AAElE,OAAO,EAAE,0BAA0B,EAAE,MAAM,+CAA+C,CAAC;AAE3F,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,+CAA+C,CAAC;AACrF,OAAO,EAAE,wBAAwB,EAAE,MAAM,2CAA2C,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AAYzB,MAAM,oBAAoB,GAAG,MAAM,CAAC;AACpC,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAsB,CAAC;AAE3D,SAAS,kBAAkB,CAAC,UAAkB;IAC5C,MAAM,KAAK,GAAG,oBAAoB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACnD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;QACjC,oBAAoB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACxC,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,KAAK,CAAC,SAAS,CAAC;AACzB,CAAC;AAED,SAAS,kBAAkB,CAAC,UAAkB,EAAE,SAA0B;IACxE,oBAAoB,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,oBAAoB,EAAE,CAAC,CAAC;AACpG,CAAC;AAED,8EAA8E;AAC9E,kEAAkE;AAClE,8EAA8E;AAE9E,MAAM,oBAAoB,GAAa;IACrC,MAAM,EAAW,SAAS;IAC1B,CAAC,GAAG,MAAM,EAAO,SAAS;IAC1B,EAAE,GAAG,MAAM,EAAM,SAAS;IAC1B,CAAC,GAAG,EAAE,GAAG,MAAM,EAAE,QAAQ;CAC1B,CAAC;AAEF,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAGvE,IAAM,yBAAyB,GAA/B,MAAM,yBAAyB;IAIjB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IAC6B;IACO;IAb/C,KAAK,CAAuC;IAEpD,YACmB,eAAgC,EAChC,UAAmC,EACnC,YAAiC,EACjC,SAA+B,EAC/B,iBAA2C,EAC3C,YAA0B,EAC1B,cAA8B,EAC9B,cAA8B,EAC9B,qBAA4C,EACf,OAAiC,EAC1B,MAA0B;QAV9D,oBAAe,GAAf,eAAe,CAAiB;QAChC,eAAU,GAAV,UAAU,CAAyB;QACnC,iBAAY,GAAZ,YAAY,CAAqB;QACjC,cAAS,GAAT,SAAS,CAAsB;QAC/B,sBAAiB,GAAjB,iBAAiB,CAA0B;QAC3C,iBAAY,GAAZ,YAAY,CAAc;QAC1B,mBAAc,GAAd,cAAc,CAAgB;QAC9B,mBAAc,GAAd,cAAc,CAAgB;QAC9B,0BAAqB,GAArB,qBAAqB,CAAuB;QACf,YAAO,GAAP,OAAO,CAA0B;QAC1B,WAAM,GAAN,MAAM,CAAoB;IAC9E,CAAC;IAEJ,KAAK,CAAC,sBAAsB;QAC1B,IAAI,CAAC,KAAK,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,WAAW,CAA4B;YAC7E,IAAI,EAAE,kBAAkB;YACxB,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC;SACxC,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO,CAAC,IAA+B,EAAE,OAAO,GAAG,CAAC;QACxD,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACvE,CAAC;IAED,8EAA8E;IAC9E,iBAAiB;IACjB,8EAA8E;IAEtE,KAAK,CAAC,WAAW,CAAC,GAAmC;QAC3D,MAAM,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;QAE/B,4EAA4E;QAC5E,yBAAyB;QACzB,4EAA4E;QAC5E,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;QAChF,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;QAEhE,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC,YAAY,SAAS,iEAAiE,EAAE,gBAAgB,CAAC,CAAC;YACtH,OAAO;QACT,CAAC;QAED,IAAI,KAAK,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;YAC/B,MAAM,CAAC,IAAI,CAAC,YAAY,SAAS,0BAA0B,KAAK,CAAC,WAAW,CAAC,WAAW,EAAE,qBAAqB,EAAE,gBAAgB,CAAC,CAAC;YACnI,OAAO;QACT,CAAC;QAED,4EAA4E;QAC5E,2BAA2B;QAC3B,EAAE;QACF,2EAA2E;QAC3E,oEAAoE;QACpE,yEAAyE;QACzE,0EAA0E;QAC1E,uEAAuE;QACvE,0DAA0D;QAC1D,qFAAqF;QACrF,4EAA4E;QAC5E,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;QAEpC,yEAAyE;QACzE,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QAEhF,IAAI,SAAsC,CAAC;QAE3C,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YAChC,sEAAsE;YACtE,wEAAwE;YACxE,sEAAsE;YACtE,qEAAqE;YACrE,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;YAC9E,SAAS,GAAG,cAAc,CAAC,EAAqB,CAAC;QACnD,CAAC;aAAM,IAAI,UAAU,EAAE,CAAC;YACtB,mCAAmC;YACnC,SAAS,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;YAE3C,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,uEAAuE;gBACvE,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;gBACjE,KAAK,MAAM,EAAE,IAAI,WAAW,CAAC,KAAK,EAAE,CAAC;oBACnC,MAAM,EAAE,GAAI,EAAU,CAAC,YAAmD,CAAC;oBAC3E,IAAI,EAAE,IAAI,EAAE,CAAC,gBAAgB,CAAC,KAAK,UAAU,EAAE,CAAC;wBAC9C,SAAS,GAAG,EAAE,CAAC,EAAqB,CAAC;wBACrC,kBAAkB,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;wBAC1C,MAAM;oBACR,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,0FAA0F;YAC1F,MAAM,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;YAExF,4DAA4D;YAC5D,MAAM,YAAY,GAAG,KAAK,CAAC,UAAU,CAAC;YACtC,IAAI,YAAY,GAAG,oBAAoB,CAAC,MAAM,EAAE,CAAC;gBAC/C,MAAM,OAAO,GAAG,oBAAoB,CAAC,YAAY,CAAE,CAAC;gBACpD,MAAM,CAAC,IAAI,CACT,YAAY,SAAS,sCAAsC,UAAU,IAAI,MAAM,kCAAkC,YAAY,GAAG,CAAC,OAAO,OAAO,KAAK,EACpJ,gBAAgB,CACjB,CAAC;gBACF,mCAAmC;gBACnC,MAAM,SAAS,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC;gBAC1D,gDAAgD;gBAChD,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;YACtD,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,KAAK,CACV,YAAY,SAAS,6BAA6B,YAAY,4BAA4B,UAAU,IAAI,MAAM,mCAAmC,EACjJ,gBAAgB,CACjB,CAAC;YACJ,CAAC;YACD,OAAO;QACT,CAAC;QAED,4EAA4E;QAC5E,yCAAyC;QACzC,4EAA4E;QAC5E,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,IAAI,WAAW,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAE3F,IAAI,CAAC;YACH,0DAA0D;YAC1D,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YAE9H,0EAA0E;YAC1E,gCAAgC;YAChC,0EAA0E;YAC1E,QAAQ,KAAK,CAAC,WAAW,EAAE,CAAC;gBAE1B,KAAK,IAAI;oBACP,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;oBAC9C,MAAM;gBAER,KAAK,IAAI;oBACP,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;oBAC9C,MAAM;gBAER,KAAK,IAAI;oBACP,MAAM,CAAC,IAAI,CAAC,YAAY,SAAS,sEAAsE,EAAE,gBAAgB,CAAC,CAAC;oBAC3H,MAAM,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;oBAChD,MAAM;gBAER,KAAK,IAAI;oBACP,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;oBAC9C,MAAM;gBAER,KAAK,IAAI;oBACP,qEAAqE;oBACrE,qEAAqE;oBACrE,oCAAoC;oBACpC,MAAM,CAAC,IAAI,CACT,YAAY,SAAS,uDAAuD;wBAC1E,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,yBAAyB,GAAG,EACpE,gBAAgB,CACjB,CAAC;oBACF,MAAM,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;oBAChD,MAAM;gBAER,KAAK,IAAI;oBACP,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;wBAChC,MAAM,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;oBACtD,CAAC;yBAAM,CAAC;wBACN,kEAAkE;wBAClE,kEAAkE;wBAClE,2DAA2D;wBAC3D,MAAM,CAAC,IAAI,CACT,YAAY,SAAS,qDAAqD,EAC1E,gBAAgB,CACjB,CAAC;wBACF,MAAM,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;oBAClD,CAAC;oBACD,MAAM;gBAER;oBACE,MAAM,CAAC,IAAI,CACT,YAAY,SAAS,yBAAyB,KAAK,CAAC,WAAW,wDAAwD,EACvH,gBAAgB,CACjB,CAAC;oBACF,MAAM,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;oBAChD,MAAM;YACV,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,MAAM,OAAO,EAAE,CAAC;QAClB,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,iBAAiB;IACjB,8EAA8E;IAE9E;;;;;;;;;OASG;IACK,KAAK,CAAC,WAAW,CACvB,GAAQ,EACR,KAAuB,EACvB,SAAyE;QAEzE,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;QAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAkC,CAAC;QACzD,MAAM,SAAS,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,OAAO,CAA4B,CAAC;QAC/E,MAAM,aAAa,GAAG,CAAC,KAAK,CAAC,aAAa,IAAK,SAAS,CAAC,eAAe,CAAwB,CAAC,CAAC;QAClG,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;QAEpC,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAC,CAAC;YAC3E,MAAM,CAAC,KAAK,CAAC,YAAY,SAAS,sCAAsC,EAAE,gBAAgB,CAAC,CAAC;YAC5F,OAAO;QACT,CAAC;QAED,qCAAqC;QACrC,6FAA6F;QAC7F,oFAAoF;QACpF,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QAC3C,IAAI,eAAqE,CAAC;QAC1E,IAAI,CAAC;YACH,MAAM,YAAY,GAChB,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,KAAK,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClE,eAAe,GAAG,MAAM,WAAW,CAAC,mBAAmB,CAAC,aAAoB,EAAE,YAAY,CAAC,CAAC;QAC9F,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,oBAAoB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;YACzH,MAAM,GAAG,CAAC,CAAC,mBAAmB;QAChC,CAAC;QAED,cAAc;QACd,mFAAmF;QACnF,iFAAiF;QACjF,8EAA8E;QAC9E,wEAAwE;QACxE,+EAA+E;QAC/E,gFAAgF;QAChF,MAAM,gBAAgB,GAAG,eAAe,CAAC,SAAS,EAAE,QAAQ,EAAE,CAAC;QAC/D,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC;QAE7E,8CAA8C;QAC9C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,aAAa,EAAE,gBAAgB,EAAS,EAAE,CAAC,CAAC;QAE7F,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,MAAM,GAAG,gDAAgD,gBAAgB,EAAE,CAAC;YAClF,MAAM,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YACzD,MAAM,CAAC,KAAK,CAAC,YAAY,SAAS,WAAW,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC;YACzE,OAAO;QACT,CAAC;QAED,IAAI,eAAe,CAAC,QAAQ,KAAK,GAAG,EAAE,CAAC;YACrC,MAAM,MAAM,GAAG,gBAAgB,eAAe,CAAC,QAAQ,iBAAiB,CAAC;YACzE,MAAM,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YACzD,MAAM,CAAC,IAAI,CAAC,YAAY,SAAS,WAAW,MAAM,kBAAkB,EAAE,gBAAgB,CAAC,CAAC;YACxF,OAAO;QACT,CAAC;QAED,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACnD,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,CAAC;QAC5C,IAAI,cAAc,KAAK,YAAY,EAAE,CAAC;YACpC,MAAM,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,kCAAkC,cAAc,WAAW,YAAY,EAAE,EAAE,CAAC,CAAC;YAC5H,MAAM,eAAe,CAAC,cAAc,CAAC,cAAc,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;QAC7E,CAAC;QAED,uEAAuE;QACvE,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACpC,IAAI,KAAK,GAAwD,IAAI,CAAC;QACtE,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,GAAG,EAAE,SAAgB,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;YAC3F,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;gBAClB,KAAK,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACjE,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,oBAAoB;QACtB,CAAC;QAED,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,MAAM,GAAG,iCAAiC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;YACpE,MAAM,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YACzD,MAAM,CAAC,KAAK,CAAC,YAAY,SAAS,WAAW,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC;YACzE,OAAO;QACT,CAAC;QAED,yCAAyC;QACzC,IAAI,CAAC;YACH,IAAI,KAAK,CAAC,KAAK,KAAK,gBAAgB,EAAE,CAAC;gBACrC,mBAAmB;gBACnB,MAAM,CAAC,IAAI,CAAC,YAAY,SAAS,0DAA0D,EAAE,gBAAgB,CAAC,CAAC;YACjH,CAAC;iBAAM,IAAI,KAAK,CAAC,KAAK,KAAK,aAAa,EAAE,CAAC;gBACzC,sBAAsB;gBACtB,MAAM,IAAI,CAAC,YAAY,CAAC,0BAA0B,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;YAC/E,CAAC;iBAAM,IAAI,KAAK,CAAC,KAAK,KAAK,kBAAkB,IAAI,KAAK,CAAC,KAAK,KAAK,mBAAmB,EAAE,CAAC;gBACrF,qBAAqB;gBACrB,MAAM,IAAI,CAAC,YAAY,CAAC,0BAA0B,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;YAC/E,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CACT,YAAY,SAAS,mCAAmC,KAAK,CAAC,KAAK,sCAAsC,EACzG,gBAAgB,CACjB,CAAC;gBACF,MAAM,IAAI,CAAC,YAAY,CAAC,0BAA0B,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;YAC/E,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,uDAAuD;YACvD,MAAM,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAChE,MAAM,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YACzD,MAAM,CAAC,KAAK,CAAC,YAAY,SAAS,+BAA+B,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC;YAC7F,MAAM,GAAG,CAAC;QACZ,CAAC;QAED,iCAAiC;QACjC,MAAM,OAAO,CAAC,MAAM,CAClB,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,EAClB,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB,EAAE,aAAa,EAAE,CACzD,CAAC;QAEF,oBAAoB;QACpB,MAAM,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAChD,MAAM,CAAC,IAAI,CAAC,YAAY,SAAS,+BAA+B,EAAE,gBAAgB,CAAC,CAAC;IACtF,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,WAAW,CACvB,GAAQ,EACR,KAAuB,EACvB,SAAyE;QAEzE,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;QAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC;QAE7E,gDAAgD;QAChD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAkC,CAAC;QACzD,MAAM,SAAS,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,OAAO,CAA4B,CAAC;QAC/E,MAAM,YAAY,GAAG,SAAS,CAAC,WAAW,CAAgC,CAAC;QAC3E,MAAM,YAAY,GAAG,YAAY,EAAE,QAAQ,EAAE,CAAC;QAE9C,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,aAAa,EAAE,YAAY,EAAS,EAAE,CAAC,CAAC;YACzF,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;gBAC/D,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,YAAY,CAAC,2BAA2B,CAAC,GAAG,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;gBAC9E,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,CAAC,IAAI,CAAC,YAAY,SAAS,qDAAqD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;oBAC5J,qDAAqD;gBACvD,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAChD,MAAM,CAAC,IAAI,CAAC,YAAY,SAAS,iEAAiE,EAAE,gBAAgB,CAAC,CAAC;IACxH,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,WAAW,CACvB,GAAQ,EACR,KAAuB,EACvB,SAAyE;QAEzE,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;QAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAkC,CAAC;QACzD,MAAM,SAAS,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,OAAO,CAA4B,CAAC;QAE/E,gFAAgF;QAChF,MAAM,QAAQ,GAAG,SAAS,CAAC,UAAU,CAAuB,CAAC;QAC7D,MAAM,YAAY,GAAG,SAAS,CAAC,WAAW,CAAgC,CAAC;QAC3E,MAAM,YAAY,GAAG,YAAY,EAAE,QAAQ,EAAE,CAAC;QAE9C,+DAA+D;QAC/D,8EAA8E;QAC9E,qFAAqF;QACrF,MAAM,cAAc,GAAG,QAAQ,KAAK,GAAG,IAAI,QAAQ,KAAK,GAAG,IAAI,QAAQ,KAAK,GAAG,CAAC;QAEhF,IAAI,cAAc,IAAI,YAAY,EAAE,CAAC;YACnC,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC;YAC7E,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,aAAa,EAAE,YAAY,EAAS,EAAE,CAAC,CAAC;YAEzF,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;gBAClE,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,YAAY,CAAC,4BAA4B,CAAC,GAAG,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;gBAC/E,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,CAAC,IAAI,CAAC,YAAY,SAAS,qCAAqC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;gBAC9I,CAAC;gBACD,uCAAuC;gBACvC,IAAI,CAAC;oBACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,SAAgB,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;oBACnG,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;wBAClB,MAAM,IAAI,CAAC,YAAY,CAAC,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,aAAoB,CAAC,CAAC;oBACzF,CAAC;gBACH,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,CAAC,IAAI,CAAC,YAAY,SAAS,sDAAsD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;gBAC/J,CAAC;YACH,CAAC;QACH,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,YAAY,SAAS,iCAAiC,QAAQ,IAAI,SAAS,4BAA4B,EAAE,gBAAgB,CAAC,CAAC;QACzI,CAAC;QAED,MAAM,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAClD,CAAC;IAED;;;;;;;;OAQG;IACK,KAAK,CAAC,WAAW,CACvB,SAAc,EACd,KAAuB,EACvB,SAAyE;QAEzE,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;QAC5B,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;QAElC,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC;YACvE,MAAM,CAAC,KAAK,CAAC,YAAY,SAAS,oCAAoC,EAAE,gBAAgB,CAAC,CAAC;YAC1F,OAAO;QACT,CAAC;QAED,0CAA0C;QAC1C,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QAC3C,IAAI,UAA8B,CAAC;QACnC,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,MAAM,WAAW,CAAC,wBAAwB,CAAC,SAAgB,CAAC,CAAC;YACjF,yFAAyF;YACzF,UAAU,GAAG,WAAW,CAAC,UAAU,IAAI,SAAS,CAAC;QACnD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,MAAM,GAAG,4BAA4B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YAC9F,MAAM,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YACzD,MAAM,GAAG,CAAC,CAAC,mBAAmB;QAChC,CAAC;QAED,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC,CAAC;YAC7E,MAAM,CAAC,IAAI,CAAC,YAAY,SAAS,8EAA8E,EAAE,gBAAgB,CAAC,CAAC;YACnI,OAAO;QACT,CAAC;QAED,mCAAmC;QACnC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;QAC/E,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,mCAAmC,SAAS,EAAE,EAAE,CAAC,CAAC;YACjG,MAAM,CAAC,KAAK,CAAC,YAAY,SAAS,0CAA0C,SAAS,GAAG,EAAE,gBAAgB,CAAC,CAAC;YAC5G,OAAO;QACT,CAAC;QAED,gEAAgE;QAChE,wEAAwE;QACxE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QAE/E,gCAAgC;QAChC,MAAM,IAAI,CAAC,iBAAiB,CAAC,eAAe,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;QAE5E,oCAAoC;QACpC,MAAM,IAAI,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QAEzE,MAAM,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAChD,MAAM,CAAC,IAAI,CACT,YAAY,SAAS,mBAAmB,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,uBAAuB,UAAU,2BAA2B,EACtH,gBAAgB,CACjB,CAAC;IACJ,CAAC;IAED,8EAA8E;IAC9E,UAAU;IACV,8EAA8E;IAEtE,KAAK,CAAC,cAAc,CAC1B,SAAyE,EACzE,SAAiB;QAEjB,MAAM,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAClF,CAAC;IAEO,KAAK,CAAC,YAAY,CAAC,SAA0B;QACnD,sDAAsD;QACtD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QAC7E,OAAO,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,EAAE,SAAgB,CAAC,CAAC;IAC/D,CAAC;IAEO,eAAe;QACrB,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC;YAC/B,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW;YACrC,YAAY,EAAE,IAAI,CAAC,MAAM;SAC1B,CAAC,CAAC;QACH,OAAO,IAAI,QAAQ,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3D,CAAC;IAEO,eAAe;QACrB,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC;YAC/B,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW;YACrC,YAAY,EAAE,IAAI,CAAC,MAAM;SAC1B,CAAC,CAAC;QACH,OAAO,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;CACF,CAAA;AAvgBY,yBAAyB;IADrC,UAAU,EAAE;IAcR,WAAA,MAAM,CAAC,mBAAmB,CAAC,CAAA;IAC3B,YAAA,MAAM,CAAC,0BAA0B,CAAC,CAAA;qCAVD,eAAe;QACpB,uBAAuB;QACrB,mBAAmB;QACtB,oBAAoB;QACZ,wBAAwB;QAC7B,YAAY;QACV,cAAc;QACd,cAAc;QACP,qBAAqB;GAZpD,yBAAyB,CAugBrC"}
|
|
@@ -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"}
|