@sakeetech/medusa-payment-viva 0.2.2
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 +816 -0
- package/dist/api/index.d.ts +15 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +22 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/middlewares.d.ts +27 -0
- package/dist/api/middlewares.d.ts.map +1 -0
- package/dist/api/middlewares.js +62 -0
- package/dist/api/middlewares.js.map +1 -0
- package/dist/api/viva/admin/_admin-auth.d.ts +26 -0
- package/dist/api/viva/admin/_admin-auth.d.ts.map +1 -0
- package/dist/api/viva/admin/_admin-auth.js +49 -0
- package/dist/api/viva/admin/_admin-auth.js.map +1 -0
- package/dist/api/viva/admin/_mode-gate.d.ts +28 -0
- package/dist/api/viva/admin/_mode-gate.d.ts.map +1 -0
- package/dist/api/viva/admin/_mode-gate.js +45 -0
- package/dist/api/viva/admin/_mode-gate.js.map +1 -0
- package/dist/api/viva/admin/connected-accounts/[id]/reconcile/route.d.ts +21 -0
- package/dist/api/viva/admin/connected-accounts/[id]/reconcile/route.d.ts.map +1 -0
- package/dist/api/viva/admin/connected-accounts/[id]/reconcile/route.js +93 -0
- package/dist/api/viva/admin/connected-accounts/[id]/reconcile/route.js.map +1 -0
- package/dist/api/viva/admin/connected-accounts/[id]/route.d.ts +18 -0
- package/dist/api/viva/admin/connected-accounts/[id]/route.d.ts.map +1 -0
- package/dist/api/viva/admin/connected-accounts/[id]/route.js +59 -0
- package/dist/api/viva/admin/connected-accounts/[id]/route.js.map +1 -0
- package/dist/api/viva/admin/connected-accounts/[id]/sources/route.d.ts +34 -0
- package/dist/api/viva/admin/connected-accounts/[id]/sources/route.d.ts.map +1 -0
- package/dist/api/viva/admin/connected-accounts/[id]/sources/route.js +234 -0
- package/dist/api/viva/admin/connected-accounts/[id]/sources/route.js.map +1 -0
- package/dist/api/viva/admin/connected-accounts/route.d.ts +19 -0
- package/dist/api/viva/admin/connected-accounts/route.d.ts.map +1 -0
- package/dist/api/viva/admin/connected-accounts/route.js +78 -0
- package/dist/api/viva/admin/connected-accounts/route.js.map +1 -0
- package/dist/api/viva/internal/auth-status/route.d.ts +19 -0
- package/dist/api/viva/internal/auth-status/route.d.ts.map +1 -0
- package/dist/api/viva/internal/auth-status/route.js +91 -0
- package/dist/api/viva/internal/auth-status/route.js.map +1 -0
- package/dist/api/viva/internal/metrics/route.d.ts +13 -0
- package/dist/api/viva/internal/metrics/route.d.ts.map +1 -0
- package/dist/api/viva/internal/metrics/route.js +48 -0
- package/dist/api/viva/internal/metrics/route.js.map +1 -0
- package/dist/api/viva/webhook/health/route.d.ts +16 -0
- package/dist/api/viva/webhook/health/route.d.ts.map +1 -0
- package/dist/api/viva/webhook/health/route.js +27 -0
- package/dist/api/viva/webhook/health/route.js.map +1 -0
- package/dist/api/viva/webhook/route.d.ts +57 -0
- package/dist/api/viva/webhook/route.d.ts.map +1 -0
- package/dist/api/viva/webhook/route.js +269 -0
- package/dist/api/viva/webhook/route.js.map +1 -0
- package/dist/cli/bin.d.ts +12 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +78 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/index.d.ts +12 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +14 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/plan.d.ts +51 -0
- package/dist/cli/plan.d.ts.map +1 -0
- package/dist/cli/plan.js +128 -0
- package/dist/cli/plan.js.map +1 -0
- package/dist/cli/register-webhooks.d.ts +54 -0
- package/dist/cli/register-webhooks.d.ts.map +1 -0
- package/dist/cli/register-webhooks.js +366 -0
- package/dist/cli/register-webhooks.js.map +1 -0
- package/dist/cli/types.d.ts +62 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +12 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/config.d.ts +158 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +236 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/loaders/viva-oauth2-strategy.d.ts +26 -0
- package/dist/loaders/viva-oauth2-strategy.d.ts.map +1 -0
- package/dist/loaders/viva-oauth2-strategy.js +58 -0
- package/dist/loaders/viva-oauth2-strategy.js.map +1 -0
- package/dist/migrations/Migration_20260425000001_init_viva_payments.d.ts +19 -0
- package/dist/migrations/Migration_20260425000001_init_viva_payments.d.ts.map +1 -0
- package/dist/migrations/Migration_20260425000001_init_viva_payments.js +136 -0
- package/dist/migrations/Migration_20260425000001_init_viva_payments.js.map +1 -0
- package/dist/migrations/Migration_20260425000002_allow_null_order_code.d.ts +31 -0
- package/dist/migrations/Migration_20260425000002_allow_null_order_code.d.ts.map +1 -0
- package/dist/migrations/Migration_20260425000002_allow_null_order_code.js +71 -0
- package/dist/migrations/Migration_20260425000002_allow_null_order_code.js.map +1 -0
- package/dist/migrations/Migration_20260425000003_webhook_retry_count.d.ts +18 -0
- package/dist/migrations/Migration_20260425000003_webhook_retry_count.d.ts.map +1 -0
- package/dist/migrations/Migration_20260425000003_webhook_retry_count.js +42 -0
- package/dist/migrations/Migration_20260425000003_webhook_retry_count.js.map +1 -0
- package/dist/migrations/Migration_20260425000004_webhook_error_and_nullable_merchant.d.ts +29 -0
- package/dist/migrations/Migration_20260425000004_webhook_error_and_nullable_merchant.d.ts.map +1 -0
- package/dist/migrations/Migration_20260425000004_webhook_error_and_nullable_merchant.js +74 -0
- package/dist/migrations/Migration_20260425000004_webhook_error_and_nullable_merchant.js.map +1 -0
- package/dist/models/index.d.ts +7 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +10 -0
- package/dist/models/index.js.map +1 -0
- package/dist/models/viva-tenant-merchant.d.ts +11 -0
- package/dist/models/viva-tenant-merchant.d.ts.map +1 -0
- package/dist/models/viva-tenant-merchant.js +54 -0
- package/dist/models/viva-tenant-merchant.js.map +1 -0
- package/dist/models/viva-transaction.d.ts +34 -0
- package/dist/models/viva-transaction.d.ts.map +1 -0
- package/dist/models/viva-transaction.js +104 -0
- package/dist/models/viva-transaction.js.map +1 -0
- package/dist/models/viva-webhook-event.d.ts +32 -0
- package/dist/models/viva-webhook-event.d.ts.map +1 -0
- package/dist/models/viva-webhook-event.js +88 -0
- package/dist/models/viva-webhook-event.js.map +1 -0
- package/dist/observability/config.d.ts +34 -0
- package/dist/observability/config.d.ts.map +1 -0
- package/dist/observability/config.js +57 -0
- package/dist/observability/config.js.map +1 -0
- package/dist/observability/index.d.ts +8 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +15 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/prom-metrics.d.ts +41 -0
- package/dist/observability/prom-metrics.d.ts.map +1 -0
- package/dist/observability/prom-metrics.js +219 -0
- package/dist/observability/prom-metrics.js.map +1 -0
- package/dist/providers/payment-provider.d.ts +19 -0
- package/dist/providers/payment-provider.d.ts.map +1 -0
- package/dist/providers/payment-provider.js +24 -0
- package/dist/providers/payment-provider.js.map +1 -0
- package/dist/resolvers/auth-strategy-factory.d.ts +42 -0
- package/dist/resolvers/auth-strategy-factory.d.ts.map +1 -0
- package/dist/resolvers/auth-strategy-factory.js +60 -0
- package/dist/resolvers/auth-strategy-factory.js.map +1 -0
- package/dist/resolvers/tenant-resolver.d.ts +104 -0
- package/dist/resolvers/tenant-resolver.d.ts.map +1 -0
- package/dist/resolvers/tenant-resolver.js +118 -0
- package/dist/resolvers/tenant-resolver.js.map +1 -0
- package/dist/service.d.ts +200 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +1003 -0
- package/dist/service.js.map +1 -0
- package/dist/subscribers/index.d.ts +5 -0
- package/dist/subscribers/index.d.ts.map +1 -0
- package/dist/subscribers/index.js +10 -0
- package/dist/subscribers/index.js.map +1 -0
- package/dist/subscribers/viva-webhook-event.d.ts +38 -0
- package/dist/subscribers/viva-webhook-event.d.ts.map +1 -0
- package/dist/subscribers/viva-webhook-event.js +133 -0
- package/dist/subscribers/viva-webhook-event.js.map +1 -0
- package/dist/workflows/cleanup-old-webhook-events.d.ts +39 -0
- package/dist/workflows/cleanup-old-webhook-events.d.ts.map +1 -0
- package/dist/workflows/cleanup-old-webhook-events.js +68 -0
- package/dist/workflows/cleanup-old-webhook-events.js.map +1 -0
- package/dist/workflows/index.d.ts +14 -0
- package/dist/workflows/index.d.ts.map +1 -0
- package/dist/workflows/index.js +19 -0
- package/dist/workflows/index.js.map +1 -0
- package/dist/workflows/per-tenant-semaphore.d.ts +47 -0
- package/dist/workflows/per-tenant-semaphore.d.ts.map +1 -0
- package/dist/workflows/per-tenant-semaphore.js +89 -0
- package/dist/workflows/per-tenant-semaphore.js.map +1 -0
- package/dist/workflows/process-webhook-event.d.ts +80 -0
- package/dist/workflows/process-webhook-event.d.ts.map +1 -0
- package/dist/workflows/process-webhook-event.js +280 -0
- package/dist/workflows/process-webhook-event.js.map +1 -0
- package/dist/workflows/reprocess-unresolved-tenants.d.ts +58 -0
- package/dist/workflows/reprocess-unresolved-tenants.d.ts.map +1 -0
- package/dist/workflows/reprocess-unresolved-tenants.js +121 -0
- package/dist/workflows/reprocess-unresolved-tenants.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* process-webhook-event.ts — Fetch-then-lock webhook processor (A3).
|
|
3
|
+
*
|
|
4
|
+
* Implements the A3 pattern:
|
|
5
|
+
* 1. Retrieve transaction from Viva API OUTSIDE any DB transaction.
|
|
6
|
+
* 2. BEGIN; SELECT ... FOR UPDATE; re-validate lattice; UPDATE; COMMIT.
|
|
7
|
+
* Never hold a row lock across network I/O.
|
|
8
|
+
*
|
|
9
|
+
* Handles:
|
|
10
|
+
* - TENANT_UNRESOLVED (A6): return early, do not write.
|
|
11
|
+
* - Onboarding events (8193/8194): update viva_tenant_merchant.verification_status.
|
|
12
|
+
* - Transaction events (1796/1797/1798/4865): fetch live state, apply lattice.
|
|
13
|
+
* - Backward/terminal transitions: mark processed_at, return applied:false.
|
|
14
|
+
* - Missing transaction row: mark processed_at, return NO_TRANSACTION_ROW.
|
|
15
|
+
*
|
|
16
|
+
* @see references/viva-docs/md/webhooks-for-payments.txt:248 (retrieve before update)
|
|
17
|
+
* @see references/viva-docs/md/isv-partner-program.txt:61 (P17 status lattice, A3, A6)
|
|
18
|
+
*/
|
|
19
|
+
import pg from 'pg';
|
|
20
|
+
import type { Payments } from '@sakeetech/viva-payments-core/payments';
|
|
21
|
+
import type { VivaWebhookEnvelope } from '@sakeetech/viva-payments-core/types';
|
|
22
|
+
import type { VivaEventTypeId } from '@sakeetech/viva-payments-core/webhooks';
|
|
23
|
+
import type { MetricsHook } from '@sakeetech/viva-payments-core/observability';
|
|
24
|
+
import type { VivaMode } from '../config.js';
|
|
25
|
+
export type { MetricsHook };
|
|
26
|
+
export interface ProcessWebhookInput {
|
|
27
|
+
/** viva_webhook_event_id (PK) */
|
|
28
|
+
eventId: string;
|
|
29
|
+
eventTypeId: VivaEventTypeId;
|
|
30
|
+
envelope: VivaWebhookEnvelope;
|
|
31
|
+
/** null when tenant resolution failed (A6) */
|
|
32
|
+
tenantId: string | null;
|
|
33
|
+
}
|
|
34
|
+
export interface ProcessWebhookContext {
|
|
35
|
+
pool: pg.Pool;
|
|
36
|
+
isvPayments: Payments;
|
|
37
|
+
logger: {
|
|
38
|
+
info(msg: string): void;
|
|
39
|
+
warn(msg: string): void;
|
|
40
|
+
error(msg: string): void;
|
|
41
|
+
};
|
|
42
|
+
/** S10 will inject a real implementation; defaults to no-op. */
|
|
43
|
+
metrics?: MetricsHook;
|
|
44
|
+
/**
|
|
45
|
+
* Plugin mode. When 'merchant', ISV-only events (8193/8194) are acknowledged
|
|
46
|
+
* (processed_at set, no side effects). When undefined, defaults to 'isv' for
|
|
47
|
+
* backward compatibility with callers that predate Phase 2 slice C.
|
|
48
|
+
*
|
|
49
|
+
* @see docs/plans/multi-mode-v0.md §6 (mode-surface gating)
|
|
50
|
+
*/
|
|
51
|
+
mode?: VivaMode;
|
|
52
|
+
}
|
|
53
|
+
export interface ProcessWebhookResult {
|
|
54
|
+
applied: boolean;
|
|
55
|
+
reason?: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Process a single viva_webhook_event row.
|
|
59
|
+
*
|
|
60
|
+
* Pure function: no Medusa-specific imports beyond types.
|
|
61
|
+
* All DB access via raw pg.Pool (A3 requirement: no ORM transaction during HTTP).
|
|
62
|
+
*
|
|
63
|
+
* @see references/viva-docs/md/webhooks-for-payments.txt:248 (A3 fetch-then-lock)
|
|
64
|
+
* @see references/viva-docs/md/isv-partner-program.txt:61 (A6 unresolved tenant)
|
|
65
|
+
*/
|
|
66
|
+
export declare function processWebhookEvent(input: ProcessWebhookInput, ctx: ProcessWebhookContext): Promise<ProcessWebhookResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Record a webhook processing failure on the event row.
|
|
69
|
+
*
|
|
70
|
+
* Writes a JSON envelope to viva_webhook_event.error and bumps retry_count.
|
|
71
|
+
* Leaves processed_at NULL so the job runner / reaper picks the row up again.
|
|
72
|
+
*
|
|
73
|
+
* Called from the subscriber when processWebhookEvent throws. Best-effort:
|
|
74
|
+
* never throws — a failure to record a failure must not mask the original
|
|
75
|
+
* error or stop the job runner from retrying.
|
|
76
|
+
*
|
|
77
|
+
* @see docs/ERRORS.md §6 (operator playbook reads this column)
|
|
78
|
+
*/
|
|
79
|
+
export declare function recordWebhookFailure(pool: pg.Pool, eventId: string, err: unknown): Promise<void>;
|
|
80
|
+
//# sourceMappingURL=process-webhook-event.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-webhook-event.d.ts","sourceRoot":"","sources":["../../src/workflows/process-webhook-event.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wCAAwC,CAAC;AACvE,OAAO,KAAK,EAAE,mBAAmB,EAAmE,MAAM,qCAAqC,CAAC;AAChJ,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AAQ9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6CAA6C,CAAC;AAE/E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAG7C,YAAY,EAAE,WAAW,EAAE,CAAC;AAE5B,MAAM,WAAW,mBAAmB;IAClC,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,eAAe,CAAC;IAC7B,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;IACd,WAAW,EAAE,QAAQ,CAAC;IACtB,MAAM,EAAE;QACN,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;KAC1B,CAAC;IACF,gEAAgE;IAChE,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,QAAQ,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,mBAAmB,EAC1B,GAAG,EAAE,qBAAqB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CA+C/B;AAiPD;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,IAAI,CAAC,CAuBf"}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* process-webhook-event.ts — Fetch-then-lock webhook processor (A3).
|
|
4
|
+
*
|
|
5
|
+
* Implements the A3 pattern:
|
|
6
|
+
* 1. Retrieve transaction from Viva API OUTSIDE any DB transaction.
|
|
7
|
+
* 2. BEGIN; SELECT ... FOR UPDATE; re-validate lattice; UPDATE; COMMIT.
|
|
8
|
+
* Never hold a row lock across network I/O.
|
|
9
|
+
*
|
|
10
|
+
* Handles:
|
|
11
|
+
* - TENANT_UNRESOLVED (A6): return early, do not write.
|
|
12
|
+
* - Onboarding events (8193/8194): update viva_tenant_merchant.verification_status.
|
|
13
|
+
* - Transaction events (1796/1797/1798/4865): fetch live state, apply lattice.
|
|
14
|
+
* - Backward/terminal transitions: mark processed_at, return applied:false.
|
|
15
|
+
* - Missing transaction row: mark processed_at, return NO_TRANSACTION_ROW.
|
|
16
|
+
*
|
|
17
|
+
* @see references/viva-docs/md/webhooks-for-payments.txt:248 (retrieve before update)
|
|
18
|
+
* @see references/viva-docs/md/isv-partner-program.txt:61 (P17 status lattice, A3, A6)
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.processWebhookEvent = processWebhookEvent;
|
|
22
|
+
exports.recordWebhookFailure = recordWebhookFailure;
|
|
23
|
+
const webhooks_1 = require("@sakeetech/viva-payments-core/webhooks");
|
|
24
|
+
const errors_1 = require("@sakeetech/viva-payments-core/errors");
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Main function
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
/**
|
|
29
|
+
* Process a single viva_webhook_event row.
|
|
30
|
+
*
|
|
31
|
+
* Pure function: no Medusa-specific imports beyond types.
|
|
32
|
+
* All DB access via raw pg.Pool (A3 requirement: no ORM transaction during HTTP).
|
|
33
|
+
*
|
|
34
|
+
* @see references/viva-docs/md/webhooks-for-payments.txt:248 (A3 fetch-then-lock)
|
|
35
|
+
* @see references/viva-docs/md/isv-partner-program.txt:61 (A6 unresolved tenant)
|
|
36
|
+
*/
|
|
37
|
+
async function processWebhookEvent(input, ctx) {
|
|
38
|
+
const { eventId, eventTypeId, envelope, tenantId } = input;
|
|
39
|
+
const { pool, isvPayments, logger, metrics, mode } = ctx;
|
|
40
|
+
const effectiveMode = mode ?? 'isv';
|
|
41
|
+
// ---- Mode gating for ISV-only onboarding events (8193 / 8194) ----
|
|
42
|
+
// In merchant mode, these events should not arrive — but if Viva ever sends
|
|
43
|
+
// one (e.g. mis-routed webhook), acknowledge it (markProcessed) without
|
|
44
|
+
// touching viva_tenant_merchant. The tenant unresolved branch below would
|
|
45
|
+
// also catch most of these, but gate on mode first so the log message is
|
|
46
|
+
// accurate and we don't increment the tenant_resolution_failures metric.
|
|
47
|
+
//
|
|
48
|
+
// @see docs/plans/multi-mode-v0.md §6 (mode-surface gating)
|
|
49
|
+
if ((0, webhooks_1.isOnboardingEvent)(eventTypeId) && effectiveMode === 'merchant') {
|
|
50
|
+
logger.warn(`[viva] ${eventTypeId} received in merchant mode — ignored (eventId=${eventId})`);
|
|
51
|
+
await markProcessed(pool, eventId);
|
|
52
|
+
return { applied: false, reason: 'MODE_GATED' };
|
|
53
|
+
}
|
|
54
|
+
// ---- A6: tenant unresolved ----
|
|
55
|
+
if (!tenantId) {
|
|
56
|
+
metrics?.counter('viva_tenant_resolution_failures_total', {
|
|
57
|
+
event_type_id: String(eventTypeId),
|
|
58
|
+
});
|
|
59
|
+
logger.warn(`[viva] processWebhookEvent: tenantId is null for eventId=${eventId} eventTypeId=${eventTypeId}. ` +
|
|
60
|
+
`Subscriber reprocess path (A6) will retry.`);
|
|
61
|
+
return { applied: false, reason: 'TENANT_UNRESOLVED' };
|
|
62
|
+
}
|
|
63
|
+
// ---- Onboarding events (8193 / 8194) — ISV mode only ----
|
|
64
|
+
if ((0, webhooks_1.isOnboardingEvent)(eventTypeId)) {
|
|
65
|
+
return processOnboardingEvent(eventId, eventTypeId, envelope, pool, logger);
|
|
66
|
+
}
|
|
67
|
+
// ---- Transaction events ----
|
|
68
|
+
if ((0, webhooks_1.isTransactionEvent)(eventTypeId)) {
|
|
69
|
+
return processTransactionEvent(eventId, eventTypeId, envelope, pool, isvPayments, logger, metrics);
|
|
70
|
+
}
|
|
71
|
+
// Unknown event type — mark processed
|
|
72
|
+
logger.warn(`[viva] processWebhookEvent: unknown eventTypeId=${eventTypeId} eventId=${eventId}. Marking processed.`);
|
|
73
|
+
await markProcessed(pool, eventId);
|
|
74
|
+
return { applied: false, reason: 'UNKNOWN_EVENT_TYPE' };
|
|
75
|
+
}
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// Onboarding event processing
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
async function processOnboardingEvent(eventId, eventTypeId, envelope, pool, logger) {
|
|
80
|
+
const eventData = envelope.EventData;
|
|
81
|
+
const connectedAccountId = eventData.ConnectedAccountId;
|
|
82
|
+
if (!connectedAccountId) {
|
|
83
|
+
logger.warn(`[viva] Onboarding event ${eventTypeId} eventId=${eventId}: no ConnectedAccountId in payload.`);
|
|
84
|
+
await markProcessed(pool, eventId);
|
|
85
|
+
return { applied: false, reason: 'NO_CONNECTED_ACCOUNT_ID' };
|
|
86
|
+
}
|
|
87
|
+
const client = await pool.connect();
|
|
88
|
+
try {
|
|
89
|
+
// 8194: update verification_status if present
|
|
90
|
+
if (eventTypeId === 8194) {
|
|
91
|
+
const verified = eventData.Verified;
|
|
92
|
+
if (verified !== undefined) {
|
|
93
|
+
await client.query(`UPDATE viva_tenant_merchant
|
|
94
|
+
SET verification_status = $1, updated_at = now()
|
|
95
|
+
WHERE connected_account_id = $2`, [verified ? 'verified' : 'unverified', connectedAccountId]);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
await markProcessed(pool, eventId);
|
|
99
|
+
logger.info(`[viva] Onboarding event ${eventTypeId} processed eventId=${eventId} connectedAccountId=${connectedAccountId}`);
|
|
100
|
+
return { applied: true };
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
client.release();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// Transaction event processing (A3 fetch-then-lock)
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
async function processTransactionEvent(eventId, eventTypeId, envelope, pool, isvPayments, logger, metrics) {
|
|
110
|
+
const eventData = envelope.EventData;
|
|
111
|
+
const vivaTransactionId = eventData.TransactionId;
|
|
112
|
+
const merchantId = eventData.MerchantId;
|
|
113
|
+
if (!vivaTransactionId) {
|
|
114
|
+
logger.warn(`[viva] Transaction event ${eventTypeId} eventId=${eventId}: no TransactionId in payload. Marking processed.`);
|
|
115
|
+
await markProcessed(pool, eventId);
|
|
116
|
+
return { applied: false, reason: 'NO_TRANSACTION_ID' };
|
|
117
|
+
}
|
|
118
|
+
// ---- A3 Step a: Fetch OUTSIDE any DB transaction ----
|
|
119
|
+
// Call Viva's Retrieve Transaction API before opening a lock.
|
|
120
|
+
// Per Viva docs: "before updating a transaction's status on your system,
|
|
121
|
+
// you SHOULD always retrieve its details from Viva".
|
|
122
|
+
// @see references/viva-docs/md/webhooks-for-payments.txt:248
|
|
123
|
+
let liveStatusLetter;
|
|
124
|
+
let liveRawPayload;
|
|
125
|
+
let liveOrderCode = null;
|
|
126
|
+
try {
|
|
127
|
+
const live = await isvPayments.retrieveTransaction(vivaTransactionId, merchantId ? { merchantId: merchantId } : {});
|
|
128
|
+
liveStatusLetter = live.statusId;
|
|
129
|
+
liveRawPayload = live;
|
|
130
|
+
liveOrderCode = live.orderCode != null ? String(live.orderCode) : null;
|
|
131
|
+
// CSO-Finding-1: cross-check live.orderCode against envelope OrderCode
|
|
132
|
+
// before any state mutation. Matches Viva's canonical WooCommerce plugin
|
|
133
|
+
// pattern (class-wc-vivacom-smart-endpoints.php:132). Without this an
|
|
134
|
+
// attacker holding any real paid transactionId could forge an envelope
|
|
135
|
+
// pointing at a victim's OrderCode and we would mark that row paid.
|
|
136
|
+
const envOrderCodeStr = eventData.OrderCode != null ? String(eventData.OrderCode) : null;
|
|
137
|
+
if (envOrderCodeStr && liveOrderCode && envOrderCodeStr !== liveOrderCode) {
|
|
138
|
+
logger.warn(`[viva] orderCode mismatch — envelope=${envOrderCodeStr} live=${liveOrderCode} ` +
|
|
139
|
+
`eventId=${eventId} txId=${vivaTransactionId}. Rejecting forged event.`);
|
|
140
|
+
metrics?.counter('viva_webhook_ordercode_mismatch_total', {
|
|
141
|
+
event_type_id: String(eventTypeId),
|
|
142
|
+
});
|
|
143
|
+
await markProcessed(pool, eventId);
|
|
144
|
+
return { applied: false, reason: 'ORDERCODE_MISMATCH' };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
if (err instanceof errors_1.VivaApiError) {
|
|
149
|
+
// Re-throw to let the subscriber retry via backoff
|
|
150
|
+
throw err;
|
|
151
|
+
}
|
|
152
|
+
logger.error(`[viva] retrieveTransaction failed for transactionId=${vivaTransactionId} eventId=${eventId}: ` +
|
|
153
|
+
`${err instanceof Error ? err.message : String(err)}`);
|
|
154
|
+
throw err;
|
|
155
|
+
}
|
|
156
|
+
// Map status letter → internal status
|
|
157
|
+
// liveStatusLetter comes from Viva API, cast to VivaStatusLetter after runtime validation.
|
|
158
|
+
// @see references/viva-docs/md/wh-transaction-payment-created.txt:398
|
|
159
|
+
const { status: derivedNext, claimSubstate } = (0, webhooks_1.mapStatusLetter)(liveStatusLetter);
|
|
160
|
+
// ---- A3 Step b: Lock + update inside DB transaction ----
|
|
161
|
+
const client = await pool.connect();
|
|
162
|
+
try {
|
|
163
|
+
await client.query('BEGIN');
|
|
164
|
+
// Find the transaction row by Viva's transactionId (stored in raw_payload or by looking up order code).
|
|
165
|
+
// We look up by viva_order_code when available, otherwise fall back to raw_payload lookup.
|
|
166
|
+
// The TransactionId in the webhook EventData is Viva's transaction UUID.
|
|
167
|
+
// viva_transaction stores our internal UUID as PK. We need to find the row
|
|
168
|
+
// that matches this specific Viva transaction.
|
|
169
|
+
//
|
|
170
|
+
// Strategy: look up by the OrderCode from the event data to find the row,
|
|
171
|
+
// then SELECT FOR UPDATE.
|
|
172
|
+
// After the mismatch check above, liveOrderCode is the verified source of
|
|
173
|
+
// truth (envelope OrderCode is attacker-controlled; live.orderCode is from
|
|
174
|
+
// Viva's authenticated retrieveTransaction call).
|
|
175
|
+
const orderCode = liveOrderCode;
|
|
176
|
+
let txRow = null;
|
|
177
|
+
if (orderCode) {
|
|
178
|
+
const result = await client.query(`SELECT viva_transaction_id, status, claim_substate, raw_payload
|
|
179
|
+
FROM viva_transaction
|
|
180
|
+
WHERE viva_order_code = $1
|
|
181
|
+
FOR UPDATE`, [orderCode]);
|
|
182
|
+
txRow = result.rows[0] ?? null;
|
|
183
|
+
}
|
|
184
|
+
if (!txRow) {
|
|
185
|
+
await client.query('ROLLBACK');
|
|
186
|
+
logger.warn(`[viva] No viva_transaction row found for orderCode=${orderCode ?? 'null'} ` +
|
|
187
|
+
`transactionId=${vivaTransactionId} eventId=${eventId}. Marking processed.`);
|
|
188
|
+
await markProcessed(pool, eventId);
|
|
189
|
+
return { applied: false, reason: 'NO_TRANSACTION_ROW' };
|
|
190
|
+
}
|
|
191
|
+
// Re-validate lattice
|
|
192
|
+
const transition = (0, webhooks_1.validateStatusTransition)(txRow.status, derivedNext);
|
|
193
|
+
if (!transition.ok) {
|
|
194
|
+
await client.query('ROLLBACK');
|
|
195
|
+
logger.warn(`[viva] Lattice rejected ${txRow.status} → ${derivedNext} ` +
|
|
196
|
+
`(reason=${transition.reason}) for txId=${txRow.viva_transaction_id} eventId=${eventId}. No-op.`);
|
|
197
|
+
metrics?.counter('viva_webhook_lattice_reject_total', {
|
|
198
|
+
reason: transition.reason,
|
|
199
|
+
event_type_id: String(eventTypeId),
|
|
200
|
+
});
|
|
201
|
+
// Still mark processed — we processed it and decided to no-op
|
|
202
|
+
await markProcessed(pool, eventId);
|
|
203
|
+
return { applied: false, reason: transition.reason };
|
|
204
|
+
}
|
|
205
|
+
// Apply the transition
|
|
206
|
+
await client.query(`UPDATE viva_transaction
|
|
207
|
+
SET status = $1,
|
|
208
|
+
claim_substate = $2,
|
|
209
|
+
raw_payload = $3,
|
|
210
|
+
updated_at = now()
|
|
211
|
+
WHERE viva_transaction_id = $4`, [
|
|
212
|
+
transition.next,
|
|
213
|
+
claimSubstate,
|
|
214
|
+
JSON.stringify(liveRawPayload),
|
|
215
|
+
txRow.viva_transaction_id,
|
|
216
|
+
]);
|
|
217
|
+
// Mark event processed
|
|
218
|
+
await client.query(`UPDATE viva_webhook_event SET processed_at = now() WHERE viva_webhook_event_id = $1`, [eventId]);
|
|
219
|
+
await client.query('COMMIT');
|
|
220
|
+
logger.info(`[viva] Transaction event ${eventTypeId} applied: ` +
|
|
221
|
+
`${txRow.status} → ${transition.next} for txId=${txRow.viva_transaction_id} eventId=${eventId}`);
|
|
222
|
+
return { applied: true };
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
await client.query('ROLLBACK').catch(() => undefined);
|
|
226
|
+
throw err;
|
|
227
|
+
}
|
|
228
|
+
finally {
|
|
229
|
+
client.release();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// ---------------------------------------------------------------------------
|
|
233
|
+
// Helpers
|
|
234
|
+
// ---------------------------------------------------------------------------
|
|
235
|
+
async function markProcessed(pool, eventId) {
|
|
236
|
+
const client = await pool.connect();
|
|
237
|
+
try {
|
|
238
|
+
await client.query(`UPDATE viva_webhook_event SET processed_at = now() WHERE viva_webhook_event_id = $1`, [eventId]);
|
|
239
|
+
}
|
|
240
|
+
finally {
|
|
241
|
+
client.release();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Record a webhook processing failure on the event row.
|
|
246
|
+
*
|
|
247
|
+
* Writes a JSON envelope to viva_webhook_event.error and bumps retry_count.
|
|
248
|
+
* Leaves processed_at NULL so the job runner / reaper picks the row up again.
|
|
249
|
+
*
|
|
250
|
+
* Called from the subscriber when processWebhookEvent throws. Best-effort:
|
|
251
|
+
* never throws — a failure to record a failure must not mask the original
|
|
252
|
+
* error or stop the job runner from retrying.
|
|
253
|
+
*
|
|
254
|
+
* @see docs/ERRORS.md §6 (operator playbook reads this column)
|
|
255
|
+
*/
|
|
256
|
+
async function recordWebhookFailure(pool, eventId, err) {
|
|
257
|
+
const envelope = {
|
|
258
|
+
name: err instanceof Error ? err.name : 'UnknownError',
|
|
259
|
+
message: err instanceof Error ? err.message : String(err),
|
|
260
|
+
code: err?.code,
|
|
261
|
+
stack: err instanceof Error ? err.stack : undefined,
|
|
262
|
+
at: new Date().toISOString(),
|
|
263
|
+
};
|
|
264
|
+
let client = null;
|
|
265
|
+
try {
|
|
266
|
+
client = await pool.connect();
|
|
267
|
+
await client.query(`UPDATE viva_webhook_event
|
|
268
|
+
SET error = $1,
|
|
269
|
+
retry_count = retry_count + 1
|
|
270
|
+
WHERE viva_webhook_event_id = $2`, [JSON.stringify(envelope), eventId]);
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
// swallow — never let failure-recording hide the original failure.
|
|
274
|
+
}
|
|
275
|
+
finally {
|
|
276
|
+
if (client)
|
|
277
|
+
client.release();
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
//# sourceMappingURL=process-webhook-event.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-webhook-event.js","sourceRoot":"","sources":["../../src/workflows/process-webhook-event.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* reprocess-unresolved-tenants.ts — A6 reprocess job.
|
|
3
|
+
*
|
|
4
|
+
* Polls for webhook events that arrived with tenant_id=NULL (unresolved merchant)
|
|
5
|
+
* and retries tenant resolution. If resolved: links transaction_id and re-emits
|
|
6
|
+
* 'viva.webhook.received' for the subscriber to process. If exhausted: marks
|
|
7
|
+
* processed_at and logs 'ABANDONED'.
|
|
8
|
+
*
|
|
9
|
+
* Uses the `retry_count` column added by Migration_20260425000003_webhook_retry_count.
|
|
10
|
+
*
|
|
11
|
+
* @see references/viva-docs/md/isv-partner-program.txt:61 (A6 tenant fallback)
|
|
12
|
+
*/
|
|
13
|
+
import pg from 'pg';
|
|
14
|
+
import type { MetricsHook } from './process-webhook-event.js';
|
|
15
|
+
export interface ReprocessUnresolvedCtx {
|
|
16
|
+
pool: pg.Pool;
|
|
17
|
+
/**
|
|
18
|
+
* Re-runs tenant resolution. Returns { tenantId } if resolved, null if still unresolved.
|
|
19
|
+
*/
|
|
20
|
+
resolveMerchant: (vivaMerchantId: string | null, connectedAccountId: string | null) => Promise<{
|
|
21
|
+
tenantId: string;
|
|
22
|
+
} | null>;
|
|
23
|
+
/**
|
|
24
|
+
* Optional event bus emit function. Injected by the subscriber/job runner.
|
|
25
|
+
* If absent, re-emission is skipped (test mode).
|
|
26
|
+
*/
|
|
27
|
+
emitEvent?: (eventName: string, data: Record<string, unknown>) => Promise<void>;
|
|
28
|
+
logger: {
|
|
29
|
+
info(msg: string): void;
|
|
30
|
+
warn(msg: string): void;
|
|
31
|
+
error(msg: string): void;
|
|
32
|
+
};
|
|
33
|
+
metrics?: MetricsHook;
|
|
34
|
+
/** Minimum age in seconds before an unresolved event is eligible for retry. Default: 30. */
|
|
35
|
+
minAgeSeconds?: number;
|
|
36
|
+
/** Maximum retry attempts before abandoning. Default: 24. */
|
|
37
|
+
maxRetries?: number;
|
|
38
|
+
}
|
|
39
|
+
export interface ReprocessResult {
|
|
40
|
+
retried: number;
|
|
41
|
+
resolved: number;
|
|
42
|
+
abandoned: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Retry tenant resolution for unprocessed webhook events with NULL tenant.
|
|
46
|
+
*
|
|
47
|
+
* Query: SELECT events WHERE processed_at IS NULL AND received_at < now() - $minAge
|
|
48
|
+
* AND retry_count < $maxRetries.
|
|
49
|
+
*
|
|
50
|
+
* Per-row:
|
|
51
|
+
* - If resolved: emit 'viva.webhook.received' and increment retry_count.
|
|
52
|
+
* - If still unresolved AND retry_count >= maxRetries - 1: mark processed_at (ABANDONED).
|
|
53
|
+
* - Otherwise: increment retry_count only.
|
|
54
|
+
*
|
|
55
|
+
* @see references/viva-docs/md/isv-partner-program.txt:61 (A6)
|
|
56
|
+
*/
|
|
57
|
+
export declare function reprocessUnresolvedTenants(ctx: ReprocessUnresolvedCtx): Promise<ReprocessResult>;
|
|
58
|
+
//# sourceMappingURL=reprocess-unresolved-tenants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reprocess-unresolved-tenants.d.ts","sourceRoot":"","sources":["../../src/workflows/reprocess-unresolved-tenants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAM9D,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;IACd;;OAEG;IACH,eAAe,EAAE,CACf,cAAc,EAAE,MAAM,GAAG,IAAI,EAC7B,kBAAkB,EAAE,MAAM,GAAG,IAAI,KAC9B,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IAC1C;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChF,MAAM,EAAE;QACN,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;KAC1B,CAAC;IACF,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,4FAA4F;IAC5F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAoBD;;;;;;;;;;;;GAYG;AACH,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,sBAAsB,GAC1B,OAAO,CAAC,eAAe,CAAC,CAoH1B"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* reprocess-unresolved-tenants.ts — A6 reprocess job.
|
|
4
|
+
*
|
|
5
|
+
* Polls for webhook events that arrived with tenant_id=NULL (unresolved merchant)
|
|
6
|
+
* and retries tenant resolution. If resolved: links transaction_id and re-emits
|
|
7
|
+
* 'viva.webhook.received' for the subscriber to process. If exhausted: marks
|
|
8
|
+
* processed_at and logs 'ABANDONED'.
|
|
9
|
+
*
|
|
10
|
+
* Uses the `retry_count` column added by Migration_20260425000003_webhook_retry_count.
|
|
11
|
+
*
|
|
12
|
+
* @see references/viva-docs/md/isv-partner-program.txt:61 (A6 tenant fallback)
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.reprocessUnresolvedTenants = reprocessUnresolvedTenants;
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Main function
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
/**
|
|
20
|
+
* Retry tenant resolution for unprocessed webhook events with NULL tenant.
|
|
21
|
+
*
|
|
22
|
+
* Query: SELECT events WHERE processed_at IS NULL AND received_at < now() - $minAge
|
|
23
|
+
* AND retry_count < $maxRetries.
|
|
24
|
+
*
|
|
25
|
+
* Per-row:
|
|
26
|
+
* - If resolved: emit 'viva.webhook.received' and increment retry_count.
|
|
27
|
+
* - If still unresolved AND retry_count >= maxRetries - 1: mark processed_at (ABANDONED).
|
|
28
|
+
* - Otherwise: increment retry_count only.
|
|
29
|
+
*
|
|
30
|
+
* @see references/viva-docs/md/isv-partner-program.txt:61 (A6)
|
|
31
|
+
*/
|
|
32
|
+
async function reprocessUnresolvedTenants(ctx) {
|
|
33
|
+
const { pool, resolveMerchant, emitEvent, logger, metrics, minAgeSeconds = 30, maxRetries = 24, } = ctx;
|
|
34
|
+
const result = { retried: 0, resolved: 0, abandoned: 0 };
|
|
35
|
+
// Fetch candidates: unresolved, old enough, not yet exhausted
|
|
36
|
+
const client = await pool.connect();
|
|
37
|
+
let rows = [];
|
|
38
|
+
try {
|
|
39
|
+
const qResult = await client.query(`SELECT viva_webhook_event_id, event_type_id, viva_merchant_id, connected_account_id,
|
|
40
|
+
transaction_id, retry_count, raw_payload
|
|
41
|
+
FROM viva_webhook_event
|
|
42
|
+
WHERE processed_at IS NULL
|
|
43
|
+
AND received_at < now() - make_interval(secs => $1)
|
|
44
|
+
AND retry_count < $2
|
|
45
|
+
ORDER BY received_at ASC
|
|
46
|
+
LIMIT 100`, [minAgeSeconds, maxRetries]);
|
|
47
|
+
rows = qResult.rows;
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
client.release();
|
|
51
|
+
}
|
|
52
|
+
for (const row of rows) {
|
|
53
|
+
result.retried++;
|
|
54
|
+
let resolved = null;
|
|
55
|
+
try {
|
|
56
|
+
resolved = await resolveMerchant(row.viva_merchant_id, row.connected_account_id);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
logger.error(`[viva] reprocessUnresolvedTenants: resolveMerchant threw for eventId=${row.viva_webhook_event_id}: ` +
|
|
60
|
+
`${err instanceof Error ? err.message : String(err)}`);
|
|
61
|
+
}
|
|
62
|
+
if (resolved) {
|
|
63
|
+
// Resolution succeeded — re-emit for subscriber processing
|
|
64
|
+
result.resolved++;
|
|
65
|
+
const updateClient = await pool.connect();
|
|
66
|
+
try {
|
|
67
|
+
await updateClient.query(`UPDATE viva_webhook_event SET retry_count = retry_count + 1 WHERE viva_webhook_event_id = $1`, [row.viva_webhook_event_id]);
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
updateClient.release();
|
|
71
|
+
}
|
|
72
|
+
if (emitEvent) {
|
|
73
|
+
await emitEvent('viva.webhook.received', {
|
|
74
|
+
eventId: row.viva_webhook_event_id,
|
|
75
|
+
tenantId: resolved.tenantId,
|
|
76
|
+
eventTypeId: row.event_type_id,
|
|
77
|
+
transactionId: row.transaction_id,
|
|
78
|
+
vivaMerchantId: row.viva_merchant_id,
|
|
79
|
+
connectedAccountId: row.connected_account_id,
|
|
80
|
+
}).catch((err) => {
|
|
81
|
+
logger.error(`[viva] reprocessUnresolvedTenants: emitEvent failed for eventId=${row.viva_webhook_event_id}: ` +
|
|
82
|
+
`${err instanceof Error ? err.message : String(err)}`);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
metrics?.counter('viva_tenant_resolution_retry_resolved_total', {
|
|
86
|
+
event_type_id: String(row.event_type_id),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
else if (row.retry_count + 1 >= maxRetries) {
|
|
90
|
+
// Exhausted retries — abandon
|
|
91
|
+
result.abandoned++;
|
|
92
|
+
const abandonClient = await pool.connect();
|
|
93
|
+
try {
|
|
94
|
+
await abandonClient.query(`UPDATE viva_webhook_event
|
|
95
|
+
SET processed_at = now(), retry_count = retry_count + 1
|
|
96
|
+
WHERE viva_webhook_event_id = $1`, [row.viva_webhook_event_id]);
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
abandonClient.release();
|
|
100
|
+
}
|
|
101
|
+
logger.warn(`[viva] ABANDONED webhook event ${row.viva_webhook_event_id} after ${row.retry_count + 1} retries. ` +
|
|
102
|
+
`viva_merchant_id=${row.viva_merchant_id ?? 'null'} eventTypeId=${row.event_type_id}. ` +
|
|
103
|
+
`Metric: viva_tenant_resolution_abandoned_total`);
|
|
104
|
+
metrics?.counter('viva_tenant_resolution_abandoned_total', {
|
|
105
|
+
event_type_id: String(row.event_type_id),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// Still unresolved, not yet exhausted — bump retry_count
|
|
110
|
+
const bumpClient = await pool.connect();
|
|
111
|
+
try {
|
|
112
|
+
await bumpClient.query(`UPDATE viva_webhook_event SET retry_count = retry_count + 1 WHERE viva_webhook_event_id = $1`, [row.viva_webhook_event_id]);
|
|
113
|
+
}
|
|
114
|
+
finally {
|
|
115
|
+
bumpClient.release();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=reprocess-unresolved-tenants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reprocess-unresolved-tenants.js","sourceRoot":"","sources":["../../src/workflows/reprocess-unresolved-tenants.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sakeetech/medusa-payment-viva",
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"description": "Medusa v2 payment provider for Viva Wallet (ISV multi-tenant). Wraps viva-payments-core.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/sakee-tech/vivawallet-npm-public.git",
|
|
9
|
+
"directory": "packages/medusa-payment-viva"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/sakee-tech/vivawallet-npm-public/tree/main/packages/medusa-payment-viva#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/sakee-tech/vivawallet-npm-public/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "commonjs",
|
|
16
|
+
"main": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"bin": {
|
|
19
|
+
"viva-register-webhooks": "./dist/cli/bin.js"
|
|
20
|
+
},
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"default": "./dist/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
],
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"uuid": "^9.0.1",
|
|
37
|
+
"@sakeetech/viva-payments-core": "0.2.2"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@medusajs/framework": "^2.0.0",
|
|
41
|
+
"@medusajs/types": "^2.0.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@medusajs/framework": "^2.0.0",
|
|
45
|
+
"@medusajs/types": "^2.0.0",
|
|
46
|
+
"@types/pg": "^8.11.10",
|
|
47
|
+
"@types/uuid": "^9.0.8",
|
|
48
|
+
"pg": "^8.13.3",
|
|
49
|
+
"typescript": "^5.7.2",
|
|
50
|
+
"undici": "^7.2.0",
|
|
51
|
+
"vitest": "^2.1.8"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=20.0.0"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsc -p tsconfig.json",
|
|
58
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
59
|
+
"test": "vitest run",
|
|
60
|
+
"test:watch": "vitest",
|
|
61
|
+
"clean": "rm -rf dist .tsbuildinfo coverage"
|
|
62
|
+
}
|
|
63
|
+
}
|