@open-mercato/gateway-stripe 0.7.1-develop.7183.1.db9678eeb8 → 0.7.1-develop.7185.1.0f280ef1f1
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.
|
@@ -63,14 +63,17 @@ async function handle(job, ctx) {
|
|
|
63
63
|
unifiedStatus
|
|
64
64
|
});
|
|
65
65
|
} catch (error) {
|
|
66
|
-
const message = error instanceof Error ? error.message : "
|
|
66
|
+
const message = error instanceof Error ? error.message : "Unknown webhook processing error";
|
|
67
67
|
if (scope) {
|
|
68
68
|
await releaseWebhookClaim(em, event.idempotencyKey, "stripe", scope);
|
|
69
69
|
await integrationLogService.write({
|
|
70
70
|
integrationId: "gateway_stripe",
|
|
71
71
|
level: "error",
|
|
72
|
-
message
|
|
73
|
-
|
|
72
|
+
// The cause belongs in the message, not only in `payload`: the payload is
|
|
73
|
+
// the durable record and never leaves the database, so an operator paged by
|
|
74
|
+
// the reported error would otherwise learn only that a webhook failed.
|
|
75
|
+
message: `Stripe webhook processing failed: ${message}`,
|
|
76
|
+
code: "gateway_stripe.webhook_processing_failed",
|
|
74
77
|
payload: {
|
|
75
78
|
error: message,
|
|
76
79
|
eventType: event.eventType,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/gateway_stripe/workers/webhook-processor.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'\nimport type { WebhookEvent } from '@open-mercato/shared/modules/payment_gateways/types'\nimport { isTrustedWebhookDispatch } from '@open-mercato/shared/lib/queue/dispatchOrigin'\nimport type { IntegrationLogService } from '@open-mercato/core/modules/integrations/lib/log-service'\nimport type { PaymentGatewayService } from '@open-mercato/core/modules/payment_gateways/lib/gateway-service'\nimport { claimWebhookProcessing, releaseWebhookClaim } from '@open-mercato/core/modules/payment_gateways/lib/webhook-utils'\nimport { mapWebhookEventToStatus, mapStripeStatus } from '../lib/status-map'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('gateway_stripe').child({ component: 'webhook-processor' })\n\ntype WebhookJobPayload = {\n providerKey: string\n event: WebhookEvent\n transactionId?: string | null\n scope?: {\n organizationId: string\n tenantId: string\n } | null\n}\n\ntype HandlerContext = JobContext & {\n resolve: <T = unknown>(name: string) => T\n}\n\nexport const metadata: WorkerMeta = {\n queue: 'stripe-webhook',\n id: 'gateway-stripe:webhook-processor',\n concurrency: 5,\n}\n\nfunction readSessionIdFromEvent(event: WebhookEvent): string | null {\n const id = event.data.id\n if (typeof id === 'string' && id.trim().length > 0) return id.trim()\n const paymentIntent = event.data.payment_intent\n if (typeof paymentIntent === 'string' && paymentIntent.trim().length > 0) return paymentIntent.trim()\n return null\n}\n\nexport default async function handle(job: QueuedJob<WebhookJobPayload>, ctx: HandlerContext): Promise<void> {\n // Fail closed on untrusted dispatch origins (#5213): only jobs enqueued by the\n // inbound webhook route (signature verified against per-tenant credentials) may\n // drive Stripe payment state. Scheduler-dispatched or unmarked jobs are dropped.\n if (!isTrustedWebhookDispatch(job.payload)) {\n logger.error('Dropping webhook job with missing or untrusted dispatch origin', {\n eventType: job.payload?.event?.eventType,\n transactionId: job.payload?.transactionId ?? null,\n })\n return\n }\n\n const em = ctx.resolve<EntityManager>('em')\n const paymentGatewayService = ctx.resolve<PaymentGatewayService>('paymentGatewayService')\n const integrationLogService = ctx.resolve<IntegrationLogService>('integrationLogService')\n const event = job.payload.event\n // Scope MUST come from the trusted enqueuer (a signature-verified transaction), never from\n // attacker-controlled `event.data.metadata` \u2014 that fallback was the cross-tenant-write vector.\n const scope = job.payload.scope ?? null\n\n try {\n let transaction = job.payload.transactionId && scope\n ? await paymentGatewayService.findTransaction(job.payload.transactionId, scope)\n : null\n if (!transaction) {\n const sessionId = readSessionIdFromEvent(event)\n if (!sessionId || !scope) return\n transaction = await paymentGatewayService.findTransactionBySessionId(sessionId, scope, 'stripe')\n }\n if (!transaction) return\n\n const transactionScope = { organizationId: transaction.organizationId, tenantId: transaction.tenantId }\n const log = integrationLogService.scoped('gateway_stripe', transactionScope)\n const claimed = await claimWebhookProcessing(em, event.idempotencyKey, 'stripe', transactionScope, event.eventType)\n if (!claimed) {\n await log.info('Duplicate Stripe webhook skipped', {\n eventType: event.eventType,\n idempotencyKey: event.idempotencyKey,\n transactionId: transaction.id,\n })\n return\n }\n\n const eventStatus = mapWebhookEventToStatus(event.eventType)\n const providerStatus = typeof event.data.status === 'string' ? event.data.status : ''\n const unifiedStatus = eventStatus ?? mapStripeStatus(providerStatus)\n\n if (unifiedStatus !== 'unknown') {\n await paymentGatewayService.syncTransactionStatus(transaction.id, {\n unifiedStatus,\n providerStatus: event.eventType,\n providerData: event.data,\n }, transactionScope)\n }\n\n await log.info('Stripe webhook processed', {\n eventType: event.eventType,\n transactionId: transaction.id,\n unifiedStatus,\n })\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : '
|
|
5
|
-
"mappings": "AAGA,SAAS,gCAAgC;AAGzC,SAAS,wBAAwB,2BAA2B;AAC5D,SAAS,yBAAyB,uBAAuB;AACzD,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,gBAAgB,EAAE,MAAM,EAAE,WAAW,oBAAoB,CAAC;AAgB/E,MAAM,WAAuB;AAAA,EAClC,OAAO;AAAA,EACP,IAAI;AAAA,EACJ,aAAa;AACf;AAEA,SAAS,uBAAuB,OAAoC;AAClE,QAAM,KAAK,MAAM,KAAK;AACtB,MAAI,OAAO,OAAO,YAAY,GAAG,KAAK,EAAE,SAAS,EAAG,QAAO,GAAG,KAAK;AACnE,QAAM,gBAAgB,MAAM,KAAK;AACjC,MAAI,OAAO,kBAAkB,YAAY,cAAc,KAAK,EAAE,SAAS,EAAG,QAAO,cAAc,KAAK;AACpG,SAAO;AACT;AAEA,eAAO,OAA8B,KAAmC,KAAoC;AAI1G,MAAI,CAAC,yBAAyB,IAAI,OAAO,GAAG;AAC1C,WAAO,MAAM,kEAAkE;AAAA,MAC7E,WAAW,IAAI,SAAS,OAAO;AAAA,MAC/B,eAAe,IAAI,SAAS,iBAAiB;AAAA,IAC/C,CAAC;AACD;AAAA,EACF;AAEA,QAAM,KAAK,IAAI,QAAuB,IAAI;AAC1C,QAAM,wBAAwB,IAAI,QAA+B,uBAAuB;AACxF,QAAM,wBAAwB,IAAI,QAA+B,uBAAuB;AACxF,QAAM,QAAQ,IAAI,QAAQ;AAG1B,QAAM,QAAQ,IAAI,QAAQ,SAAS;AAEnC,MAAI;AACF,QAAI,cAAc,IAAI,QAAQ,iBAAiB,QAC3C,MAAM,sBAAsB,gBAAgB,IAAI,QAAQ,eAAe,KAAK,IAC5E;AACJ,QAAI,CAAC,aAAa;AAChB,YAAM,YAAY,uBAAuB,KAAK;AAC9C,UAAI,CAAC,aAAa,CAAC,MAAO;AAC1B,oBAAc,MAAM,sBAAsB,2BAA2B,WAAW,OAAO,QAAQ;AAAA,IACjG;AACA,QAAI,CAAC,YAAa;AAElB,UAAM,mBAAmB,EAAE,gBAAgB,YAAY,gBAAgB,UAAU,YAAY,SAAS;AACtG,UAAM,MAAM,sBAAsB,OAAO,kBAAkB,gBAAgB;AAC3E,UAAM,UAAU,MAAM,uBAAuB,IAAI,MAAM,gBAAgB,UAAU,kBAAkB,MAAM,SAAS;AAClH,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,KAAK,oCAAoC;AAAA,QACjD,WAAW,MAAM;AAAA,QACjB,gBAAgB,MAAM;AAAA,QACtB,eAAe,YAAY;AAAA,MAC7B,CAAC;AACD;AAAA,IACF;AAEA,UAAM,cAAc,wBAAwB,MAAM,SAAS;AAC3D,UAAM,iBAAiB,OAAO,MAAM,KAAK,WAAW,WAAW,MAAM,KAAK,SAAS;AACnF,UAAM,gBAAgB,eAAe,gBAAgB,cAAc;AAEnE,QAAI,kBAAkB,WAAW;AAC/B,YAAM,sBAAsB,sBAAsB,YAAY,IAAI;AAAA,QAChE;AAAA,QACA,gBAAgB,MAAM;AAAA,QACtB,cAAc,MAAM;AAAA,MACtB,GAAG,gBAAgB;AAAA,IACrB;AAEA,UAAM,IAAI,KAAK,4BAA4B;AAAA,MACzC,WAAW,MAAM;AAAA,MACjB,eAAe,YAAY;AAAA,MAC3B;AAAA,IACF,CAAC;AAAA,EACH,SAAS,OAAgB;AACvB,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,QAAI,OAAO;AACT,YAAM,oBAAoB,IAAI,MAAM,gBAAgB,UAAU,KAAK;AACnE,YAAM,sBAAsB,MAAM;AAAA,QAChC,eAAe;AAAA,QACf,OAAO;AAAA,
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'\nimport type { WebhookEvent } from '@open-mercato/shared/modules/payment_gateways/types'\nimport { isTrustedWebhookDispatch } from '@open-mercato/shared/lib/queue/dispatchOrigin'\nimport type { IntegrationLogService } from '@open-mercato/core/modules/integrations/lib/log-service'\nimport type { PaymentGatewayService } from '@open-mercato/core/modules/payment_gateways/lib/gateway-service'\nimport { claimWebhookProcessing, releaseWebhookClaim } from '@open-mercato/core/modules/payment_gateways/lib/webhook-utils'\nimport { mapWebhookEventToStatus, mapStripeStatus } from '../lib/status-map'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('gateway_stripe').child({ component: 'webhook-processor' })\n\ntype WebhookJobPayload = {\n providerKey: string\n event: WebhookEvent\n transactionId?: string | null\n scope?: {\n organizationId: string\n tenantId: string\n } | null\n}\n\ntype HandlerContext = JobContext & {\n resolve: <T = unknown>(name: string) => T\n}\n\nexport const metadata: WorkerMeta = {\n queue: 'stripe-webhook',\n id: 'gateway-stripe:webhook-processor',\n concurrency: 5,\n}\n\nfunction readSessionIdFromEvent(event: WebhookEvent): string | null {\n const id = event.data.id\n if (typeof id === 'string' && id.trim().length > 0) return id.trim()\n const paymentIntent = event.data.payment_intent\n if (typeof paymentIntent === 'string' && paymentIntent.trim().length > 0) return paymentIntent.trim()\n return null\n}\n\nexport default async function handle(job: QueuedJob<WebhookJobPayload>, ctx: HandlerContext): Promise<void> {\n // Fail closed on untrusted dispatch origins (#5213): only jobs enqueued by the\n // inbound webhook route (signature verified against per-tenant credentials) may\n // drive Stripe payment state. Scheduler-dispatched or unmarked jobs are dropped.\n if (!isTrustedWebhookDispatch(job.payload)) {\n logger.error('Dropping webhook job with missing or untrusted dispatch origin', {\n eventType: job.payload?.event?.eventType,\n transactionId: job.payload?.transactionId ?? null,\n })\n return\n }\n\n const em = ctx.resolve<EntityManager>('em')\n const paymentGatewayService = ctx.resolve<PaymentGatewayService>('paymentGatewayService')\n const integrationLogService = ctx.resolve<IntegrationLogService>('integrationLogService')\n const event = job.payload.event\n // Scope MUST come from the trusted enqueuer (a signature-verified transaction), never from\n // attacker-controlled `event.data.metadata` \u2014 that fallback was the cross-tenant-write vector.\n const scope = job.payload.scope ?? null\n\n try {\n let transaction = job.payload.transactionId && scope\n ? await paymentGatewayService.findTransaction(job.payload.transactionId, scope)\n : null\n if (!transaction) {\n const sessionId = readSessionIdFromEvent(event)\n if (!sessionId || !scope) return\n transaction = await paymentGatewayService.findTransactionBySessionId(sessionId, scope, 'stripe')\n }\n if (!transaction) return\n\n const transactionScope = { organizationId: transaction.organizationId, tenantId: transaction.tenantId }\n const log = integrationLogService.scoped('gateway_stripe', transactionScope)\n const claimed = await claimWebhookProcessing(em, event.idempotencyKey, 'stripe', transactionScope, event.eventType)\n if (!claimed) {\n await log.info('Duplicate Stripe webhook skipped', {\n eventType: event.eventType,\n idempotencyKey: event.idempotencyKey,\n transactionId: transaction.id,\n })\n return\n }\n\n const eventStatus = mapWebhookEventToStatus(event.eventType)\n const providerStatus = typeof event.data.status === 'string' ? event.data.status : ''\n const unifiedStatus = eventStatus ?? mapStripeStatus(providerStatus)\n\n if (unifiedStatus !== 'unknown') {\n await paymentGatewayService.syncTransactionStatus(transaction.id, {\n unifiedStatus,\n providerStatus: event.eventType,\n providerData: event.data,\n }, transactionScope)\n }\n\n await log.info('Stripe webhook processed', {\n eventType: event.eventType,\n transactionId: transaction.id,\n unifiedStatus,\n })\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : 'Unknown webhook processing error'\n if (scope) {\n await releaseWebhookClaim(em, event.idempotencyKey, 'stripe', scope)\n await integrationLogService.write({\n integrationId: 'gateway_stripe',\n level: 'error',\n // The cause belongs in the message, not only in `payload`: the payload is\n // the durable record and never leaves the database, so an operator paged by\n // the reported error would otherwise learn only that a webhook failed.\n message: `Stripe webhook processing failed: ${message}`,\n code: 'gateway_stripe.webhook_processing_failed',\n payload: {\n error: message,\n eventType: event.eventType,\n transactionId: job.payload.transactionId ?? null,\n },\n }, scope)\n } else {\n logger.error('Stripe webhook processing failed', {\n eventType: event.eventType,\n transactionId: job.payload.transactionId ?? null,\n err: error,\n })\n }\n throw error\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/gateway-stripe",
|
|
3
|
-
"version": "0.7.1-develop.
|
|
3
|
+
"version": "0.7.1-develop.7185.1.0f280ef1f1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -62,21 +62,21 @@
|
|
|
62
62
|
}
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@open-mercato/core": "0.7.1-develop.
|
|
66
|
-
"@open-mercato/queue": "0.7.1-develop.
|
|
67
|
-
"@open-mercato/ui": "0.7.1-develop.
|
|
65
|
+
"@open-mercato/core": "0.7.1-develop.7185.1.0f280ef1f1",
|
|
66
|
+
"@open-mercato/queue": "0.7.1-develop.7185.1.0f280ef1f1",
|
|
67
|
+
"@open-mercato/ui": "0.7.1-develop.7185.1.0f280ef1f1",
|
|
68
68
|
"@stripe/react-stripe-js": "^6.8.2",
|
|
69
69
|
"@stripe/stripe-js": "^9.14.0",
|
|
70
70
|
"stripe": "^22.3.1"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
73
|
"@mikro-orm/postgresql": "^7.0.14",
|
|
74
|
-
"@open-mercato/shared": "0.7.1-develop.
|
|
74
|
+
"@open-mercato/shared": "0.7.1-develop.7185.1.0f280ef1f1",
|
|
75
75
|
"react": "^19.0.0",
|
|
76
76
|
"react-dom": "^19.0.0"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
|
-
"@open-mercato/shared": "0.7.1-develop.
|
|
79
|
+
"@open-mercato/shared": "0.7.1-develop.7185.1.0f280ef1f1",
|
|
80
80
|
"@types/jest": "^30.0.0",
|
|
81
81
|
"@types/react": "^19.2.18",
|
|
82
82
|
"@types/react-dom": "^19.2.5",
|
|
@@ -99,14 +99,17 @@ export default async function handle(job: QueuedJob<WebhookJobPayload>, ctx: Han
|
|
|
99
99
|
unifiedStatus,
|
|
100
100
|
})
|
|
101
101
|
} catch (error: unknown) {
|
|
102
|
-
const message = error instanceof Error ? error.message : '
|
|
102
|
+
const message = error instanceof Error ? error.message : 'Unknown webhook processing error'
|
|
103
103
|
if (scope) {
|
|
104
104
|
await releaseWebhookClaim(em, event.idempotencyKey, 'stripe', scope)
|
|
105
105
|
await integrationLogService.write({
|
|
106
106
|
integrationId: 'gateway_stripe',
|
|
107
107
|
level: 'error',
|
|
108
|
-
message
|
|
109
|
-
|
|
108
|
+
// The cause belongs in the message, not only in `payload`: the payload is
|
|
109
|
+
// the durable record and never leaves the database, so an operator paged by
|
|
110
|
+
// the reported error would otherwise learn only that a webhook failed.
|
|
111
|
+
message: `Stripe webhook processing failed: ${message}`,
|
|
112
|
+
code: 'gateway_stripe.webhook_processing_failed',
|
|
110
113
|
payload: {
|
|
111
114
|
error: message,
|
|
112
115
|
eventType: event.eventType,
|