@open-mercato/core 0.6.6-develop.6373.1.5bece5409d → 0.6.6-develop.6374.1.49e692558f

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.
@@ -1,4 +1,4 @@
1
- [build:core] found 3430 entry points
1
+ [build:core] found 3432 entry points
2
2
  [build:core] built successfully
3
3
  [build:core:generated] found 186 entry points
4
4
  [build:core:generated] built successfully
@@ -3,6 +3,7 @@ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
3
3
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
4
  import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
5
5
  import { GatewayTransaction } from "../../../data/entities.js";
6
+ import { readGatewayMetadata, readWebhookLog } from "../../../lib/transaction-fields.js";
6
7
  import { paymentGatewaysTag } from "../../openapi.js";
7
8
  const metadata = {
8
9
  path: "/payment_gateways/transactions/[id]",
@@ -72,8 +73,8 @@ async function GET(req, { params }) {
72
73
  redirectUrl: transaction.redirectUrl ?? null,
73
74
  amount: transaction.amount,
74
75
  currencyCode: transaction.currencyCode,
75
- gatewayMetadata: transaction.gatewayMetadata ?? null,
76
- webhookLog: Array.isArray(transaction.webhookLog) ? transaction.webhookLog : [],
76
+ gatewayMetadata: transaction.gatewayMetadata == null ? null : readGatewayMetadata(transaction.gatewayMetadata),
77
+ webhookLog: readWebhookLog(transaction.webhookLog),
77
78
  lastWebhookAt: toIsoString(transaction.lastWebhookAt),
78
79
  lastPolledAt: toIsoString(transaction.lastPolledAt),
79
80
  expiresAt: toIsoString(transaction.expiresAt),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/payment_gateways/api/transactions/%5Bid%5D/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { IntegrationLogService } from '../../../../integrations/lib/log-service'\nimport { GatewayTransaction } from '../../../data/entities'\nimport { paymentGatewaysTag } from '../../openapi'\n\nexport const metadata = {\n path: '/payment_gateways/transactions/[id]',\n GET: { requireAuth: true, requireFeatures: ['payment_gateways.view'] },\n}\n\nfunction toIsoString(value: unknown): string | null {\n if (!value) return null\n if (value instanceof Date) return value.toISOString()\n if (typeof value === 'string') {\n const parsed = new Date(value)\n if (!Number.isNaN(parsed.getTime())) return parsed.toISOString()\n return value\n }\n const parsed = new Date(value as string | number)\n return Number.isNaN(parsed.getTime()) ? null : parsed.toISOString()\n}\n\nexport async function GET(req: Request, { params }: { params: Promise<{ id: string }> | { id: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const resolvedParams = await params\n const transactionId = resolvedParams?.id\n if (!transactionId) {\n return NextResponse.json({ error: 'Transaction id is required' }, { status: 400 })\n }\n\n const { resolve } = await createRequestContainer()\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n const em = resolve('em') as EntityManager\n const integrationLogService = resolve('integrationLogService') as IntegrationLogService\n const transaction = await findOneWithDecryption(\n em,\n GatewayTransaction,\n {\n id: transactionId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n\n if (!transaction) {\n return NextResponse.json({ error: 'Transaction not found' }, { status: 404 })\n }\n\n const integrationId = `gateway_${transaction.providerKey}`\n const { items: logRows } = await integrationLogService.query(\n {\n integrationId,\n entityType: 'payment_transaction',\n entityId: transactionId,\n page: 1,\n pageSize: 100,\n },\n scope,\n )\n\n return NextResponse.json({\n transaction: {\n id: transaction.id,\n paymentId: transaction.paymentId,\n providerKey: transaction.providerKey,\n providerSessionId: transaction.providerSessionId ?? null,\n gatewayPaymentId: transaction.gatewayPaymentId ?? null,\n gatewayRefundId: transaction.gatewayRefundId ?? null,\n unifiedStatus: transaction.unifiedStatus,\n gatewayStatus: transaction.gatewayStatus ?? null,\n redirectUrl: transaction.redirectUrl ?? null,\n amount: transaction.amount,\n currencyCode: transaction.currencyCode,\n gatewayMetadata: transaction.gatewayMetadata ?? null,\n webhookLog: Array.isArray(transaction.webhookLog) ? transaction.webhookLog : [],\n lastWebhookAt: toIsoString(transaction.lastWebhookAt),\n lastPolledAt: toIsoString(transaction.lastPolledAt),\n expiresAt: toIsoString(transaction.expiresAt),\n createdAt: toIsoString(transaction.createdAt),\n updatedAt: toIsoString(transaction.updatedAt),\n },\n logs: logRows.map((row) => ({\n id: row.id,\n integrationId: row.integrationId,\n runId: row.runId ?? null,\n scopeEntityType: row.scopeEntityType ?? null,\n scopeEntityId: row.scopeEntityId ?? null,\n level: row.level,\n message: row.message,\n code: row.code ?? null,\n payload: row.payload ?? null,\n createdAt: toIsoString(row.createdAt),\n })),\n })\n}\n\nexport const openApi = {\n tags: [paymentGatewaysTag],\n summary: 'Get payment transaction details',\n methods: {\n GET: {\n summary: 'Get payment transaction details',\n tags: [paymentGatewaysTag],\n responses: [\n { status: 200, description: 'Payment transaction details' },\n { status: 404, description: 'Transaction not found' },\n ],\n },\n },\n}\n\nexport default GET\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { IntegrationLogService } from '../../../../integrations/lib/log-service'\nimport { GatewayTransaction } from '../../../data/entities'\nimport { readGatewayMetadata, readWebhookLog } from '../../../lib/transaction-fields'\nimport { paymentGatewaysTag } from '../../openapi'\n\nexport const metadata = {\n path: '/payment_gateways/transactions/[id]',\n GET: { requireAuth: true, requireFeatures: ['payment_gateways.view'] },\n}\n\nfunction toIsoString(value: unknown): string | null {\n if (!value) return null\n if (value instanceof Date) return value.toISOString()\n if (typeof value === 'string') {\n const parsed = new Date(value)\n if (!Number.isNaN(parsed.getTime())) return parsed.toISOString()\n return value\n }\n const parsed = new Date(value as string | number)\n return Number.isNaN(parsed.getTime()) ? null : parsed.toISOString()\n}\n\nexport async function GET(req: Request, { params }: { params: Promise<{ id: string }> | { id: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const resolvedParams = await params\n const transactionId = resolvedParams?.id\n if (!transactionId) {\n return NextResponse.json({ error: 'Transaction id is required' }, { status: 400 })\n }\n\n const { resolve } = await createRequestContainer()\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n const em = resolve('em') as EntityManager\n const integrationLogService = resolve('integrationLogService') as IntegrationLogService\n const transaction = await findOneWithDecryption(\n em,\n GatewayTransaction,\n {\n id: transactionId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n\n if (!transaction) {\n return NextResponse.json({ error: 'Transaction not found' }, { status: 404 })\n }\n\n const integrationId = `gateway_${transaction.providerKey}`\n const { items: logRows } = await integrationLogService.query(\n {\n integrationId,\n entityType: 'payment_transaction',\n entityId: transactionId,\n page: 1,\n pageSize: 100,\n },\n scope,\n )\n\n return NextResponse.json({\n transaction: {\n id: transaction.id,\n paymentId: transaction.paymentId,\n providerKey: transaction.providerKey,\n providerSessionId: transaction.providerSessionId ?? null,\n gatewayPaymentId: transaction.gatewayPaymentId ?? null,\n gatewayRefundId: transaction.gatewayRefundId ?? null,\n unifiedStatus: transaction.unifiedStatus,\n gatewayStatus: transaction.gatewayStatus ?? null,\n redirectUrl: transaction.redirectUrl ?? null,\n amount: transaction.amount,\n currencyCode: transaction.currencyCode,\n gatewayMetadata: transaction.gatewayMetadata == null ? null : readGatewayMetadata(transaction.gatewayMetadata),\n webhookLog: readWebhookLog(transaction.webhookLog),\n lastWebhookAt: toIsoString(transaction.lastWebhookAt),\n lastPolledAt: toIsoString(transaction.lastPolledAt),\n expiresAt: toIsoString(transaction.expiresAt),\n createdAt: toIsoString(transaction.createdAt),\n updatedAt: toIsoString(transaction.updatedAt),\n },\n logs: logRows.map((row) => ({\n id: row.id,\n integrationId: row.integrationId,\n runId: row.runId ?? null,\n scopeEntityType: row.scopeEntityType ?? null,\n scopeEntityId: row.scopeEntityId ?? null,\n level: row.level,\n message: row.message,\n code: row.code ?? null,\n payload: row.payload ?? null,\n createdAt: toIsoString(row.createdAt),\n })),\n })\n}\n\nexport const openApi = {\n tags: [paymentGatewaysTag],\n summary: 'Get payment transaction details',\n methods: {\n GET: {\n summary: 'Get payment transaction details',\n tags: [paymentGatewaysTag],\n responses: [\n { status: 200, description: 'Payment transaction details' },\n { status: 404, description: 'Transaction not found' },\n ],\n },\n },\n}\n\nexport default GET\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAE7B,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,6BAA6B;AAEtC,SAAS,0BAA0B;AACnC,SAAS,qBAAqB,sBAAsB;AACpD,SAAS,0BAA0B;AAE5B,MAAM,WAAW;AAAA,EACtB,MAAM;AAAA,EACN,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,uBAAuB,EAAE;AACvE;AAEA,SAAS,YAAY,OAA+B;AAClD,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,iBAAiB,KAAM,QAAO,MAAM,YAAY;AACpD,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAMA,UAAS,IAAI,KAAK,KAAK;AAC7B,QAAI,CAAC,OAAO,MAAMA,QAAO,QAAQ,CAAC,EAAG,QAAOA,QAAO,YAAY;AAC/D,WAAO;AAAA,EACT;AACA,QAAM,SAAS,IAAI,KAAK,KAAwB;AAChD,SAAO,OAAO,MAAM,OAAO,QAAQ,CAAC,IAAI,OAAO,OAAO,YAAY;AACpE;AAEA,eAAsB,IAAI,KAAc,EAAE,OAAO,GAAyD;AACxG,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,YAAY,CAAC,KAAK,OAAO;AAClC,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AAEA,QAAM,iBAAiB,MAAM;AAC7B,QAAM,gBAAgB,gBAAgB;AACtC,MAAI,CAAC,eAAe;AAClB,WAAO,aAAa,KAAK,EAAE,OAAO,6BAA6B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACnF;AAEA,QAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,QAAM,QAAQ,EAAE,gBAAgB,KAAK,OAAiB,UAAU,KAAK,SAAS;AAC9E,QAAM,KAAK,QAAQ,IAAI;AACvB,QAAM,wBAAwB,QAAQ,uBAAuB;AAC7D,QAAM,cAAc,MAAM;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,gBAAgB,MAAM;AAAA,MACtB,UAAU,MAAM;AAAA,MAChB,WAAW;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,MAAI,CAAC,aAAa;AAChB,WAAO,aAAa,KAAK,EAAE,OAAO,wBAAwB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC9E;AAEA,QAAM,gBAAgB,WAAW,YAAY,WAAW;AACxD,QAAM,EAAE,OAAO,QAAQ,IAAI,MAAM,sBAAsB;AAAA,IACrD;AAAA,MACE;AAAA,MACA,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA;AAAA,EACF;AAEA,SAAO,aAAa,KAAK;AAAA,IACvB,aAAa;AAAA,MACX,IAAI,YAAY;AAAA,MAChB,WAAW,YAAY;AAAA,MACvB,aAAa,YAAY;AAAA,MACzB,mBAAmB,YAAY,qBAAqB;AAAA,MACpD,kBAAkB,YAAY,oBAAoB;AAAA,MAClD,iBAAiB,YAAY,mBAAmB;AAAA,MAChD,eAAe,YAAY;AAAA,MAC3B,eAAe,YAAY,iBAAiB;AAAA,MAC5C,aAAa,YAAY,eAAe;AAAA,MACxC,QAAQ,YAAY;AAAA,MACpB,cAAc,YAAY;AAAA,MAC1B,iBAAiB,YAAY,mBAAmB,OAAO,OAAO,oBAAoB,YAAY,eAAe;AAAA,MAC7G,YAAY,eAAe,YAAY,UAAU;AAAA,MACjD,eAAe,YAAY,YAAY,aAAa;AAAA,MACpD,cAAc,YAAY,YAAY,YAAY;AAAA,MAClD,WAAW,YAAY,YAAY,SAAS;AAAA,MAC5C,WAAW,YAAY,YAAY,SAAS;AAAA,MAC5C,WAAW,YAAY,YAAY,SAAS;AAAA,IAC9C;AAAA,IACA,MAAM,QAAQ,IAAI,CAAC,SAAS;AAAA,MAC1B,IAAI,IAAI;AAAA,MACR,eAAe,IAAI;AAAA,MACnB,OAAO,IAAI,SAAS;AAAA,MACpB,iBAAiB,IAAI,mBAAmB;AAAA,MACxC,eAAe,IAAI,iBAAiB;AAAA,MACpC,OAAO,IAAI;AAAA,MACX,SAAS,IAAI;AAAA,MACb,MAAM,IAAI,QAAQ;AAAA,MAClB,SAAS,IAAI,WAAW;AAAA,MACxB,WAAW,YAAY,IAAI,SAAS;AAAA,IACtC,EAAE;AAAA,EACJ,CAAC;AACH;AAEO,MAAM,UAAU;AAAA,EACrB,MAAM,CAAC,kBAAkB;AAAA,EACzB,SAAS;AAAA,EACT,SAAS;AAAA,IACP,KAAK;AAAA,MACH,SAAS;AAAA,MACT,MAAM,CAAC,kBAAkB;AAAA,MACzB,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,8BAA8B;AAAA,QAC1D,EAAE,QAAQ,KAAK,aAAa,wBAAwB;AAAA,MACtD;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,gBAAQ;",
6
6
  "names": ["parsed"]
7
7
  }
@@ -0,0 +1,16 @@
1
+ const defaultEncryptionMaps = [
2
+ {
3
+ entityId: "payment_gateways:gateway_transaction",
4
+ fields: [
5
+ { field: "client_secret" },
6
+ { field: "gateway_metadata" },
7
+ { field: "webhook_log" }
8
+ ]
9
+ }
10
+ ];
11
+ var encryption_default = defaultEncryptionMaps;
12
+ export {
13
+ encryption_default as default,
14
+ defaultEncryptionMaps
15
+ };
16
+ //# sourceMappingURL=encryption.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/payment_gateways/encryption.ts"],
4
+ "sourcesContent": ["import type { ModuleEncryptionMap } from '@open-mercato/shared/modules/encryption'\n\n// Gateway transactions persist provider secrets and raw provider payloads. Encrypt the client\n// secret plus the JSONB columns that carry provider/session data and webhook history at rest.\n// Lookup-critical columns (provider_key, provider_session_id, unified_status, gateway_payment_id,\n// gateway_refund_id) are intentionally left in plaintext so status filters and session lookups keep\n// working without a deterministic hash. The JSONB fields decrypt back to strings (entity fields are\n// never auto-parsed \u2014 see issue #1810), so consumers MUST normalize them via lib/transaction-fields.\nexport const defaultEncryptionMaps: ModuleEncryptionMap[] = [\n {\n entityId: 'payment_gateways:gateway_transaction',\n fields: [\n { field: 'client_secret' },\n { field: 'gateway_metadata' },\n { field: 'webhook_log' },\n ],\n },\n]\n\nexport default defaultEncryptionMaps\n"],
5
+ "mappings": "AAQO,MAAM,wBAA+C;AAAA,EAC1D;AAAA,IACE,UAAU;AAAA,IACV,QAAQ;AAAA,MACN,EAAE,OAAO,gBAAgB;AAAA,MACzB,EAAE,OAAO,mBAAmB;AAAA,MAC5B,EAAE,OAAO,cAAc;AAAA,IACzB;AAAA,EACF;AACF;AAEA,IAAO,qBAAQ;",
6
+ "names": []
7
+ }
@@ -6,6 +6,7 @@ import { conflict } from "@open-mercato/shared/lib/crud/errors";
6
6
  import { GatewayTransaction } from "../data/entities.js";
7
7
  import { canApplyManualAction, isValidTransition } from "./status-machine.js";
8
8
  import { emitPaymentGatewayEvent } from "../events.js";
9
+ import { readGatewayMetadata, readWebhookLog } from "./transaction-fields.js";
9
10
  function assertManualActionAllowed(action, transaction) {
10
11
  const current = transaction.unifiedStatus;
11
12
  if (!canApplyManualAction(action, current)) {
@@ -160,7 +161,7 @@ function createPaymentGatewayService(deps) {
160
161
  credentials
161
162
  });
162
163
  const statusChanged = applyAdapterResultStatus("capture", transaction, result.status);
163
- transaction.gatewayMetadata = { ...transaction.gatewayMetadata, captureResult: result.providerData };
164
+ transaction.gatewayMetadata = { ...readGatewayMetadata(transaction.gatewayMetadata), captureResult: result.providerData };
164
165
  await em.flush();
165
166
  if (statusChanged) {
166
167
  await emitStatusEvent(result.status, {
@@ -200,7 +201,7 @@ function createPaymentGatewayService(deps) {
200
201
  });
201
202
  const statusChanged = applyAdapterResultStatus("refund", transaction, result.status);
202
203
  transaction.gatewayRefundId = result.refundId;
203
- transaction.gatewayMetadata = { ...transaction.gatewayMetadata, refundResult: result.providerData };
204
+ transaction.gatewayMetadata = { ...readGatewayMetadata(transaction.gatewayMetadata), refundResult: result.providerData };
204
205
  await em.flush();
205
206
  if (statusChanged) {
206
207
  await emitStatusEvent(result.status, {
@@ -276,7 +277,7 @@ function createPaymentGatewayService(deps) {
276
277
  const previousStatus = transaction.unifiedStatus;
277
278
  transaction.unifiedStatus = status.status;
278
279
  transaction.gatewayStatus = status.status;
279
- transaction.gatewayMetadata = { ...transaction.gatewayMetadata, statusResult: status.providerData ?? null };
280
+ transaction.gatewayMetadata = { ...readGatewayMetadata(transaction.gatewayMetadata), statusResult: status.providerData ?? null };
280
281
  transaction.lastPolledAt = /* @__PURE__ */ new Date();
281
282
  await em.flush();
282
283
  await emitStatusEvent(status.status, {
@@ -314,10 +315,10 @@ function createPaymentGatewayService(deps) {
314
315
  transaction.gatewayStatus = update.providerStatus;
315
316
  }
316
317
  if (update.providerData) {
317
- transaction.gatewayMetadata = { ...transaction.gatewayMetadata, ...update.providerData };
318
+ transaction.gatewayMetadata = { ...readGatewayMetadata(transaction.gatewayMetadata), ...update.providerData };
318
319
  }
319
320
  if (update.webhookEvent) {
320
- const webhookLog = Array.isArray(transaction.webhookLog) ? transaction.webhookLog : [];
321
+ const webhookLog = readWebhookLog(transaction.webhookLog);
321
322
  webhookLog.push({
322
323
  eventType: update.webhookEvent.eventType,
323
324
  receivedAt: update.webhookEvent.receivedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/payment_gateways/lib/gateway-service.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n getGatewayAdapter,\n type CreateSessionInput,\n type CreateSessionResult,\n type CaptureResult,\n type RefundResult,\n type CancelResult,\n type GatewayPaymentStatus,\n type PaymentGatewayPresentationRequest,\n type UnifiedPaymentStatus,\n} from '@open-mercato/shared/modules/payment_gateways/types'\nimport type { CredentialsService } from '../../integrations/lib/credentials-service'\nimport type { IntegrationStateService } from '../../integrations/lib/state-service'\nimport type { IntegrationLogService } from '../../integrations/lib/log-service'\nimport { conflict } from '@open-mercato/shared/lib/crud/errors'\nimport { GatewayTransaction } from '../data/entities'\nimport { canApplyManualAction, isValidTransition, type ManualGatewayAction } from './status-machine'\nimport { emitPaymentGatewayEvent } from '../events'\n\nfunction assertManualActionAllowed(action: ManualGatewayAction, transaction: GatewayTransaction): void {\n const current = transaction.unifiedStatus as UnifiedPaymentStatus\n if (!canApplyManualAction(action, current)) {\n throw conflict(`Cannot ${action} a payment in status \"${current}\"`)\n }\n}\n\nfunction applyAdapterResultStatus(\n action: ManualGatewayAction,\n transaction: GatewayTransaction,\n resultStatus: UnifiedPaymentStatus,\n): boolean {\n const current = transaction.unifiedStatus as UnifiedPaymentStatus\n if (resultStatus === current) return false\n if (!isValidTransition(current, resultStatus)) {\n throw conflict(\n `Gateway returned status \"${resultStatus}\" which is not a valid transition from \"${current}\" for ${action}`,\n )\n }\n transaction.unifiedStatus = resultStatus\n return true\n}\n\nexport interface PaymentGatewayServiceDeps {\n em: EntityManager\n integrationCredentialsService: CredentialsService\n integrationStateService?: IntegrationStateService\n integrationLogService?: IntegrationLogService\n}\n\nexport interface CreatePaymentSessionInput {\n providerKey: string\n paymentId: string\n orderId?: string\n amount: number\n currencyCode: string\n captureMethod?: 'automatic' | 'manual'\n paymentTypes?: string[]\n description?: string\n successUrl?: string\n cancelUrl?: string\n metadata?: Record<string, unknown>\n presentation?: PaymentGatewayPresentationRequest\n organizationId: string\n tenantId: string\n}\n\nexport function createPaymentGatewayService(deps: PaymentGatewayServiceDeps) {\n const { em, integrationCredentialsService, integrationLogService } = deps\n\n async function findTransactionOrThrow(\n transactionId: string,\n scope: { organizationId: string; tenantId: string },\n ): Promise<GatewayTransaction> {\n const transaction = await findOneWithDecryption(\n em,\n GatewayTransaction,\n {\n id: transactionId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!transaction) {\n throw new Error('Transaction not found')\n }\n return transaction\n }\n\n function readProviderSessionId(transaction: GatewayTransaction): string {\n if (typeof transaction.providerSessionId === 'string' && transaction.providerSessionId.trim().length > 0) {\n return transaction.providerSessionId\n }\n throw new Error('Transaction is missing provider session id')\n }\n\n async function emitStatusEvent(status: UnifiedPaymentStatus, payload: Record<string, unknown>) {\n type PaymentGatewayEventId = Parameters<typeof emitPaymentGatewayEvent>[0]\n const eventMap: Partial<Record<UnifiedPaymentStatus, PaymentGatewayEventId>> = {\n authorized: 'payment_gateways.payment.authorized',\n captured: 'payment_gateways.payment.captured',\n failed: 'payment_gateways.payment.failed',\n refunded: 'payment_gateways.payment.refunded',\n cancelled: 'payment_gateways.payment.cancelled',\n }\n const eventId = eventMap[status]\n if (!eventId) return\n await emitPaymentGatewayEvent(eventId, payload)\n }\n\n async function writeTransactionLog(\n providerKey: string,\n scope: { organizationId: string; tenantId: string },\n transactionId: string,\n level: 'info' | 'warn' | 'error',\n message: string,\n payload?: Record<string, unknown> | null,\n code?: string | null,\n ) {\n if (!integrationLogService) return\n await integrationLogService.write({\n integrationId: `gateway_${providerKey}`,\n scopeEntityType: 'payment_transaction',\n scopeEntityId: transactionId,\n level,\n message,\n code,\n payload: payload ?? null,\n }, scope)\n }\n\n async function resolveAdapterAndCredentials(providerKey: string, scope: { organizationId: string; tenantId: string }) {\n const integrationId = `gateway_${providerKey}`\n const selectedVersion = deps.integrationStateService\n ? await deps.integrationStateService.resolveApiVersion(integrationId, scope)\n : undefined\n const adapter = getGatewayAdapter(providerKey, selectedVersion)\n if (!adapter) {\n throw new Error(\n selectedVersion\n ? `No gateway adapter registered for provider: ${providerKey} (version: ${selectedVersion})`\n : `No gateway adapter registered for provider: ${providerKey}`,\n )\n }\n const credentials = await integrationCredentialsService.resolve(integrationId, scope) ?? {}\n\n return { adapter, credentials }\n }\n\n return {\n async createPaymentSession(input: CreatePaymentSessionInput): Promise<{ transaction: GatewayTransaction; session: CreateSessionResult }> {\n const scope = { organizationId: input.organizationId, tenantId: input.tenantId }\n const { adapter, credentials } = await resolveAdapterAndCredentials(input.providerKey, scope)\n\n const sessionInput: CreateSessionInput = {\n paymentId: input.paymentId,\n orderId: input.orderId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n amount: input.amount,\n currencyCode: input.currencyCode,\n captureMethod: input.captureMethod,\n paymentTypes: input.paymentTypes,\n description: input.description,\n successUrl: input.successUrl,\n cancelUrl: input.cancelUrl,\n metadata: input.metadata,\n presentation: input.presentation,\n credentials,\n }\n\n const session = await adapter.createSession(sessionInput)\n\n const transaction = em.create(GatewayTransaction, {\n paymentId: input.paymentId,\n providerKey: input.providerKey,\n providerSessionId: session.sessionId,\n unifiedStatus: session.status,\n redirectUrl: session.redirectUrl\n ?? (session.clientSession?.type === 'redirect' ? session.clientSession.redirectUrl : null),\n clientSecret: session.clientSecret ?? null,\n amount: String(input.amount),\n currencyCode: input.currencyCode,\n gatewayMetadata: {\n ...(session.providerData ?? {}),\n ...(session.clientSession ? { clientSession: session.clientSession } : {}),\n },\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n await em.persist(transaction).flush()\n await emitPaymentGatewayEvent('payment_gateways.session.created', {\n transactionId: transaction.id,\n paymentId: transaction.paymentId,\n providerKey: transaction.providerKey,\n status: transaction.unifiedStatus,\n organizationId: transaction.organizationId,\n tenantId: transaction.tenantId,\n })\n await writeTransactionLog(\n transaction.providerKey,\n scope,\n transaction.id,\n 'info',\n 'Payment session created',\n {\n paymentId: transaction.paymentId,\n providerSessionId: transaction.providerSessionId,\n status: transaction.unifiedStatus,\n amount: input.amount,\n currencyCode: input.currencyCode,\n },\n )\n\n return { transaction, session }\n },\n\n async capturePayment(transactionId: string, amount: number | undefined, scope: { organizationId: string; tenantId: string }): Promise<CaptureResult> {\n const transaction = await findTransactionOrThrow(transactionId, scope)\n assertManualActionAllowed('capture', transaction)\n const { adapter, credentials } = await resolveAdapterAndCredentials(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n )\n\n const result = await adapter.capture({\n sessionId: readProviderSessionId(transaction),\n amount,\n credentials,\n })\n\n const statusChanged = applyAdapterResultStatus('capture', transaction, result.status)\n transaction.gatewayMetadata = { ...transaction.gatewayMetadata, captureResult: result.providerData }\n await em.flush()\n if (statusChanged) {\n await emitStatusEvent(result.status, {\n transactionId: transaction.id,\n paymentId: transaction.paymentId,\n providerKey: transaction.providerKey,\n organizationId: transaction.organizationId,\n tenantId: transaction.tenantId,\n })\n }\n await writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n 'info',\n 'Payment captured',\n {\n amount: amount ?? null,\n status: result.status,\n capturedAmount: result.capturedAmount,\n },\n )\n\n return result\n },\n\n async refundPayment(\n transactionId: string,\n amount: number | undefined,\n reason: string | undefined,\n scope: { organizationId: string; tenantId: string },\n ): Promise<RefundResult> {\n const transaction = await findTransactionOrThrow(transactionId, scope)\n assertManualActionAllowed('refund', transaction)\n const { adapter, credentials } = await resolveAdapterAndCredentials(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n )\n\n const result = await adapter.refund({\n sessionId: readProviderSessionId(transaction),\n amount,\n reason,\n credentials,\n })\n\n const statusChanged = applyAdapterResultStatus('refund', transaction, result.status)\n transaction.gatewayRefundId = result.refundId\n transaction.gatewayMetadata = { ...transaction.gatewayMetadata, refundResult: result.providerData }\n await em.flush()\n if (statusChanged) {\n await emitStatusEvent(result.status, {\n transactionId: transaction.id,\n paymentId: transaction.paymentId,\n providerKey: transaction.providerKey,\n organizationId: transaction.organizationId,\n tenantId: transaction.tenantId,\n })\n }\n await writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n 'info',\n 'Payment refunded',\n {\n amount: amount ?? null,\n reason: reason ?? null,\n status: result.status,\n refundId: result.refundId,\n },\n )\n\n return result\n },\n\n async cancelPayment(\n transactionId: string,\n reason: string | undefined,\n scope: { organizationId: string; tenantId: string },\n ): Promise<CancelResult> {\n const transaction = await findTransactionOrThrow(transactionId, scope)\n assertManualActionAllowed('cancel', transaction)\n const { adapter, credentials } = await resolveAdapterAndCredentials(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n )\n\n const result = await adapter.cancel({\n sessionId: readProviderSessionId(transaction),\n reason,\n credentials,\n })\n\n const statusChanged = applyAdapterResultStatus('cancel', transaction, result.status)\n await em.flush()\n if (statusChanged) {\n await emitStatusEvent(result.status, {\n transactionId: transaction.id,\n paymentId: transaction.paymentId,\n providerKey: transaction.providerKey,\n organizationId: transaction.organizationId,\n tenantId: transaction.tenantId,\n })\n }\n await writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n 'info',\n 'Payment cancelled',\n {\n reason: reason ?? null,\n status: result.status,\n },\n )\n\n return result\n },\n\n async getPaymentStatus(transactionId: string, scope: { organizationId: string; tenantId: string }): Promise<GatewayPaymentStatus> {\n const transaction = await findTransactionOrThrow(transactionId, scope)\n const { adapter, credentials } = await resolveAdapterAndCredentials(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n )\n\n const status = await adapter.getStatus({\n sessionId: readProviderSessionId(transaction),\n credentials,\n })\n\n if (status.status !== transaction.unifiedStatus && isValidTransition(transaction.unifiedStatus as UnifiedPaymentStatus, status.status)) {\n const previousStatus = transaction.unifiedStatus\n transaction.unifiedStatus = status.status\n transaction.gatewayStatus = status.status\n transaction.gatewayMetadata = { ...transaction.gatewayMetadata, statusResult: status.providerData ?? null }\n transaction.lastPolledAt = new Date()\n await em.flush()\n await emitStatusEvent(status.status, {\n transactionId: transaction.id,\n paymentId: transaction.paymentId,\n providerKey: transaction.providerKey,\n previousStatus,\n organizationId: transaction.organizationId,\n tenantId: transaction.tenantId,\n })\n await writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n 'info',\n 'Payment status updated by poller',\n {\n previousStatus,\n nextStatus: status.status,\n },\n )\n }\n\n return status\n },\n\n async syncTransactionStatus(transactionId: string, update: {\n unifiedStatus: UnifiedPaymentStatus\n providerStatus?: string\n providerData?: Record<string, unknown>\n webhookEvent?: {\n eventType: string\n idempotencyKey: string\n processed: boolean\n receivedAt?: string\n }\n }, scope: { organizationId: string; tenantId: string }): Promise<void> {\n const transaction = await findTransactionOrThrow(transactionId, scope)\n const currentStatus = transaction.unifiedStatus as UnifiedPaymentStatus\n const canTransition = isValidTransition(currentStatus, update.unifiedStatus)\n const shouldApplyStatus = canTransition && update.unifiedStatus !== currentStatus\n const previousStatus = transaction.unifiedStatus\n if (shouldApplyStatus) {\n transaction.unifiedStatus = update.unifiedStatus\n }\n if (update.providerStatus) {\n transaction.gatewayStatus = update.providerStatus\n }\n if (update.providerData) {\n transaction.gatewayMetadata = { ...transaction.gatewayMetadata, ...update.providerData }\n }\n if (update.webhookEvent) {\n const webhookLog = Array.isArray(transaction.webhookLog) ? transaction.webhookLog : []\n webhookLog.push({\n eventType: update.webhookEvent.eventType,\n receivedAt: update.webhookEvent.receivedAt ?? new Date().toISOString(),\n idempotencyKey: update.webhookEvent.idempotencyKey,\n unifiedStatus: update.unifiedStatus,\n processed: update.webhookEvent.processed,\n })\n transaction.webhookLog = webhookLog\n }\n transaction.lastWebhookAt = new Date()\n await em.flush()\n if (shouldApplyStatus) {\n await emitStatusEvent(update.unifiedStatus, {\n transactionId: transaction.id,\n paymentId: transaction.paymentId,\n providerKey: transaction.providerKey,\n previousStatus,\n organizationId: transaction.organizationId,\n tenantId: transaction.tenantId,\n })\n }\n await writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n shouldApplyStatus ? 'info' : 'warn',\n shouldApplyStatus ? 'Payment status synchronized from webhook' : 'Webhook received with no status transition',\n {\n previousStatus,\n nextStatus: update.unifiedStatus,\n providerStatus: update.providerStatus ?? null,\n eventType: update.webhookEvent?.eventType ?? null,\n idempotencyKey: update.webhookEvent?.idempotencyKey ?? null,\n },\n )\n },\n\n async findTransaction(id: string, scope: { organizationId: string; tenantId: string }): Promise<GatewayTransaction | null> {\n return findOneWithDecryption(\n em,\n GatewayTransaction,\n {\n id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n },\n\n async findTransactionBySessionId(\n providerSessionId: string,\n scope: { organizationId: string; tenantId: string },\n providerKey?: string,\n ): Promise<GatewayTransaction | null> {\n return findOneWithDecryption(\n em,\n GatewayTransaction,\n {\n providerSessionId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n ...(providerKey ? { providerKey } : {}),\n },\n undefined,\n scope,\n )\n },\n\n async listTransactionsForStatusPolling(scope?: {\n organizationId?: string\n tenantId?: string\n providerKey?: string\n limit?: number\n }): Promise<GatewayTransaction[]> {\n const where: Record<string, unknown> = {\n unifiedStatus: { $in: ['pending', 'authorized', 'partially_captured'] },\n deletedAt: null,\n }\n if (scope?.organizationId) where.organizationId = scope.organizationId\n if (scope?.tenantId) where.tenantId = scope.tenantId\n if (scope?.providerKey) where.providerKey = scope.providerKey\n\n return findWithDecryption(\n em,\n GatewayTransaction,\n where,\n {\n orderBy: { updatedAt: 'asc' },\n limit: scope?.limit ?? 100,\n },\n scope,\n )\n },\n }\n}\n\nexport type PaymentGatewayService = ReturnType<typeof createPaymentGatewayService>\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n getGatewayAdapter,\n type CreateSessionInput,\n type CreateSessionResult,\n type CaptureResult,\n type RefundResult,\n type CancelResult,\n type GatewayPaymentStatus,\n type PaymentGatewayPresentationRequest,\n type UnifiedPaymentStatus,\n} from '@open-mercato/shared/modules/payment_gateways/types'\nimport type { CredentialsService } from '../../integrations/lib/credentials-service'\nimport type { IntegrationStateService } from '../../integrations/lib/state-service'\nimport type { IntegrationLogService } from '../../integrations/lib/log-service'\nimport { conflict } from '@open-mercato/shared/lib/crud/errors'\nimport { GatewayTransaction } from '../data/entities'\nimport { canApplyManualAction, isValidTransition, type ManualGatewayAction } from './status-machine'\nimport { emitPaymentGatewayEvent } from '../events'\nimport { readGatewayMetadata, readWebhookLog } from './transaction-fields'\n\nfunction assertManualActionAllowed(action: ManualGatewayAction, transaction: GatewayTransaction): void {\n const current = transaction.unifiedStatus as UnifiedPaymentStatus\n if (!canApplyManualAction(action, current)) {\n throw conflict(`Cannot ${action} a payment in status \"${current}\"`)\n }\n}\n\nfunction applyAdapterResultStatus(\n action: ManualGatewayAction,\n transaction: GatewayTransaction,\n resultStatus: UnifiedPaymentStatus,\n): boolean {\n const current = transaction.unifiedStatus as UnifiedPaymentStatus\n if (resultStatus === current) return false\n if (!isValidTransition(current, resultStatus)) {\n throw conflict(\n `Gateway returned status \"${resultStatus}\" which is not a valid transition from \"${current}\" for ${action}`,\n )\n }\n transaction.unifiedStatus = resultStatus\n return true\n}\n\nexport interface PaymentGatewayServiceDeps {\n em: EntityManager\n integrationCredentialsService: CredentialsService\n integrationStateService?: IntegrationStateService\n integrationLogService?: IntegrationLogService\n}\n\nexport interface CreatePaymentSessionInput {\n providerKey: string\n paymentId: string\n orderId?: string\n amount: number\n currencyCode: string\n captureMethod?: 'automatic' | 'manual'\n paymentTypes?: string[]\n description?: string\n successUrl?: string\n cancelUrl?: string\n metadata?: Record<string, unknown>\n presentation?: PaymentGatewayPresentationRequest\n organizationId: string\n tenantId: string\n}\n\nexport function createPaymentGatewayService(deps: PaymentGatewayServiceDeps) {\n const { em, integrationCredentialsService, integrationLogService } = deps\n\n async function findTransactionOrThrow(\n transactionId: string,\n scope: { organizationId: string; tenantId: string },\n ): Promise<GatewayTransaction> {\n const transaction = await findOneWithDecryption(\n em,\n GatewayTransaction,\n {\n id: transactionId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!transaction) {\n throw new Error('Transaction not found')\n }\n return transaction\n }\n\n function readProviderSessionId(transaction: GatewayTransaction): string {\n if (typeof transaction.providerSessionId === 'string' && transaction.providerSessionId.trim().length > 0) {\n return transaction.providerSessionId\n }\n throw new Error('Transaction is missing provider session id')\n }\n\n async function emitStatusEvent(status: UnifiedPaymentStatus, payload: Record<string, unknown>) {\n type PaymentGatewayEventId = Parameters<typeof emitPaymentGatewayEvent>[0]\n const eventMap: Partial<Record<UnifiedPaymentStatus, PaymentGatewayEventId>> = {\n authorized: 'payment_gateways.payment.authorized',\n captured: 'payment_gateways.payment.captured',\n failed: 'payment_gateways.payment.failed',\n refunded: 'payment_gateways.payment.refunded',\n cancelled: 'payment_gateways.payment.cancelled',\n }\n const eventId = eventMap[status]\n if (!eventId) return\n await emitPaymentGatewayEvent(eventId, payload)\n }\n\n async function writeTransactionLog(\n providerKey: string,\n scope: { organizationId: string; tenantId: string },\n transactionId: string,\n level: 'info' | 'warn' | 'error',\n message: string,\n payload?: Record<string, unknown> | null,\n code?: string | null,\n ) {\n if (!integrationLogService) return\n await integrationLogService.write({\n integrationId: `gateway_${providerKey}`,\n scopeEntityType: 'payment_transaction',\n scopeEntityId: transactionId,\n level,\n message,\n code,\n payload: payload ?? null,\n }, scope)\n }\n\n async function resolveAdapterAndCredentials(providerKey: string, scope: { organizationId: string; tenantId: string }) {\n const integrationId = `gateway_${providerKey}`\n const selectedVersion = deps.integrationStateService\n ? await deps.integrationStateService.resolveApiVersion(integrationId, scope)\n : undefined\n const adapter = getGatewayAdapter(providerKey, selectedVersion)\n if (!adapter) {\n throw new Error(\n selectedVersion\n ? `No gateway adapter registered for provider: ${providerKey} (version: ${selectedVersion})`\n : `No gateway adapter registered for provider: ${providerKey}`,\n )\n }\n const credentials = await integrationCredentialsService.resolve(integrationId, scope) ?? {}\n\n return { adapter, credentials }\n }\n\n return {\n async createPaymentSession(input: CreatePaymentSessionInput): Promise<{ transaction: GatewayTransaction; session: CreateSessionResult }> {\n const scope = { organizationId: input.organizationId, tenantId: input.tenantId }\n const { adapter, credentials } = await resolveAdapterAndCredentials(input.providerKey, scope)\n\n const sessionInput: CreateSessionInput = {\n paymentId: input.paymentId,\n orderId: input.orderId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n amount: input.amount,\n currencyCode: input.currencyCode,\n captureMethod: input.captureMethod,\n paymentTypes: input.paymentTypes,\n description: input.description,\n successUrl: input.successUrl,\n cancelUrl: input.cancelUrl,\n metadata: input.metadata,\n presentation: input.presentation,\n credentials,\n }\n\n const session = await adapter.createSession(sessionInput)\n\n const transaction = em.create(GatewayTransaction, {\n paymentId: input.paymentId,\n providerKey: input.providerKey,\n providerSessionId: session.sessionId,\n unifiedStatus: session.status,\n redirectUrl: session.redirectUrl\n ?? (session.clientSession?.type === 'redirect' ? session.clientSession.redirectUrl : null),\n clientSecret: session.clientSecret ?? null,\n amount: String(input.amount),\n currencyCode: input.currencyCode,\n gatewayMetadata: {\n ...(session.providerData ?? {}),\n ...(session.clientSession ? { clientSession: session.clientSession } : {}),\n },\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n await em.persist(transaction).flush()\n await emitPaymentGatewayEvent('payment_gateways.session.created', {\n transactionId: transaction.id,\n paymentId: transaction.paymentId,\n providerKey: transaction.providerKey,\n status: transaction.unifiedStatus,\n organizationId: transaction.organizationId,\n tenantId: transaction.tenantId,\n })\n await writeTransactionLog(\n transaction.providerKey,\n scope,\n transaction.id,\n 'info',\n 'Payment session created',\n {\n paymentId: transaction.paymentId,\n providerSessionId: transaction.providerSessionId,\n status: transaction.unifiedStatus,\n amount: input.amount,\n currencyCode: input.currencyCode,\n },\n )\n\n return { transaction, session }\n },\n\n async capturePayment(transactionId: string, amount: number | undefined, scope: { organizationId: string; tenantId: string }): Promise<CaptureResult> {\n const transaction = await findTransactionOrThrow(transactionId, scope)\n assertManualActionAllowed('capture', transaction)\n const { adapter, credentials } = await resolveAdapterAndCredentials(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n )\n\n const result = await adapter.capture({\n sessionId: readProviderSessionId(transaction),\n amount,\n credentials,\n })\n\n const statusChanged = applyAdapterResultStatus('capture', transaction, result.status)\n transaction.gatewayMetadata = { ...readGatewayMetadata(transaction.gatewayMetadata), captureResult: result.providerData }\n await em.flush()\n if (statusChanged) {\n await emitStatusEvent(result.status, {\n transactionId: transaction.id,\n paymentId: transaction.paymentId,\n providerKey: transaction.providerKey,\n organizationId: transaction.organizationId,\n tenantId: transaction.tenantId,\n })\n }\n await writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n 'info',\n 'Payment captured',\n {\n amount: amount ?? null,\n status: result.status,\n capturedAmount: result.capturedAmount,\n },\n )\n\n return result\n },\n\n async refundPayment(\n transactionId: string,\n amount: number | undefined,\n reason: string | undefined,\n scope: { organizationId: string; tenantId: string },\n ): Promise<RefundResult> {\n const transaction = await findTransactionOrThrow(transactionId, scope)\n assertManualActionAllowed('refund', transaction)\n const { adapter, credentials } = await resolveAdapterAndCredentials(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n )\n\n const result = await adapter.refund({\n sessionId: readProviderSessionId(transaction),\n amount,\n reason,\n credentials,\n })\n\n const statusChanged = applyAdapterResultStatus('refund', transaction, result.status)\n transaction.gatewayRefundId = result.refundId\n transaction.gatewayMetadata = { ...readGatewayMetadata(transaction.gatewayMetadata), refundResult: result.providerData }\n await em.flush()\n if (statusChanged) {\n await emitStatusEvent(result.status, {\n transactionId: transaction.id,\n paymentId: transaction.paymentId,\n providerKey: transaction.providerKey,\n organizationId: transaction.organizationId,\n tenantId: transaction.tenantId,\n })\n }\n await writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n 'info',\n 'Payment refunded',\n {\n amount: amount ?? null,\n reason: reason ?? null,\n status: result.status,\n refundId: result.refundId,\n },\n )\n\n return result\n },\n\n async cancelPayment(\n transactionId: string,\n reason: string | undefined,\n scope: { organizationId: string; tenantId: string },\n ): Promise<CancelResult> {\n const transaction = await findTransactionOrThrow(transactionId, scope)\n assertManualActionAllowed('cancel', transaction)\n const { adapter, credentials } = await resolveAdapterAndCredentials(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n )\n\n const result = await adapter.cancel({\n sessionId: readProviderSessionId(transaction),\n reason,\n credentials,\n })\n\n const statusChanged = applyAdapterResultStatus('cancel', transaction, result.status)\n await em.flush()\n if (statusChanged) {\n await emitStatusEvent(result.status, {\n transactionId: transaction.id,\n paymentId: transaction.paymentId,\n providerKey: transaction.providerKey,\n organizationId: transaction.organizationId,\n tenantId: transaction.tenantId,\n })\n }\n await writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n 'info',\n 'Payment cancelled',\n {\n reason: reason ?? null,\n status: result.status,\n },\n )\n\n return result\n },\n\n async getPaymentStatus(transactionId: string, scope: { organizationId: string; tenantId: string }): Promise<GatewayPaymentStatus> {\n const transaction = await findTransactionOrThrow(transactionId, scope)\n const { adapter, credentials } = await resolveAdapterAndCredentials(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n )\n\n const status = await adapter.getStatus({\n sessionId: readProviderSessionId(transaction),\n credentials,\n })\n\n if (status.status !== transaction.unifiedStatus && isValidTransition(transaction.unifiedStatus as UnifiedPaymentStatus, status.status)) {\n const previousStatus = transaction.unifiedStatus\n transaction.unifiedStatus = status.status\n transaction.gatewayStatus = status.status\n transaction.gatewayMetadata = { ...readGatewayMetadata(transaction.gatewayMetadata), statusResult: status.providerData ?? null }\n transaction.lastPolledAt = new Date()\n await em.flush()\n await emitStatusEvent(status.status, {\n transactionId: transaction.id,\n paymentId: transaction.paymentId,\n providerKey: transaction.providerKey,\n previousStatus,\n organizationId: transaction.organizationId,\n tenantId: transaction.tenantId,\n })\n await writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n 'info',\n 'Payment status updated by poller',\n {\n previousStatus,\n nextStatus: status.status,\n },\n )\n }\n\n return status\n },\n\n async syncTransactionStatus(transactionId: string, update: {\n unifiedStatus: UnifiedPaymentStatus\n providerStatus?: string\n providerData?: Record<string, unknown>\n webhookEvent?: {\n eventType: string\n idempotencyKey: string\n processed: boolean\n receivedAt?: string\n }\n }, scope: { organizationId: string; tenantId: string }): Promise<void> {\n const transaction = await findTransactionOrThrow(transactionId, scope)\n const currentStatus = transaction.unifiedStatus as UnifiedPaymentStatus\n const canTransition = isValidTransition(currentStatus, update.unifiedStatus)\n const shouldApplyStatus = canTransition && update.unifiedStatus !== currentStatus\n const previousStatus = transaction.unifiedStatus\n if (shouldApplyStatus) {\n transaction.unifiedStatus = update.unifiedStatus\n }\n if (update.providerStatus) {\n transaction.gatewayStatus = update.providerStatus\n }\n if (update.providerData) {\n transaction.gatewayMetadata = { ...readGatewayMetadata(transaction.gatewayMetadata), ...update.providerData }\n }\n if (update.webhookEvent) {\n const webhookLog = readWebhookLog(transaction.webhookLog)\n webhookLog.push({\n eventType: update.webhookEvent.eventType,\n receivedAt: update.webhookEvent.receivedAt ?? new Date().toISOString(),\n idempotencyKey: update.webhookEvent.idempotencyKey,\n unifiedStatus: update.unifiedStatus,\n processed: update.webhookEvent.processed,\n })\n transaction.webhookLog = webhookLog\n }\n transaction.lastWebhookAt = new Date()\n await em.flush()\n if (shouldApplyStatus) {\n await emitStatusEvent(update.unifiedStatus, {\n transactionId: transaction.id,\n paymentId: transaction.paymentId,\n providerKey: transaction.providerKey,\n previousStatus,\n organizationId: transaction.organizationId,\n tenantId: transaction.tenantId,\n })\n }\n await writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n shouldApplyStatus ? 'info' : 'warn',\n shouldApplyStatus ? 'Payment status synchronized from webhook' : 'Webhook received with no status transition',\n {\n previousStatus,\n nextStatus: update.unifiedStatus,\n providerStatus: update.providerStatus ?? null,\n eventType: update.webhookEvent?.eventType ?? null,\n idempotencyKey: update.webhookEvent?.idempotencyKey ?? null,\n },\n )\n },\n\n async findTransaction(id: string, scope: { organizationId: string; tenantId: string }): Promise<GatewayTransaction | null> {\n return findOneWithDecryption(\n em,\n GatewayTransaction,\n {\n id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n },\n\n async findTransactionBySessionId(\n providerSessionId: string,\n scope: { organizationId: string; tenantId: string },\n providerKey?: string,\n ): Promise<GatewayTransaction | null> {\n return findOneWithDecryption(\n em,\n GatewayTransaction,\n {\n providerSessionId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n ...(providerKey ? { providerKey } : {}),\n },\n undefined,\n scope,\n )\n },\n\n async listTransactionsForStatusPolling(scope?: {\n organizationId?: string\n tenantId?: string\n providerKey?: string\n limit?: number\n }): Promise<GatewayTransaction[]> {\n const where: Record<string, unknown> = {\n unifiedStatus: { $in: ['pending', 'authorized', 'partially_captured'] },\n deletedAt: null,\n }\n if (scope?.organizationId) where.organizationId = scope.organizationId\n if (scope?.tenantId) where.tenantId = scope.tenantId\n if (scope?.providerKey) where.providerKey = scope.providerKey\n\n return findWithDecryption(\n em,\n GatewayTransaction,\n where,\n {\n orderBy: { updatedAt: 'asc' },\n limit: scope?.limit ?? 100,\n },\n scope,\n )\n },\n }\n}\n\nexport type PaymentGatewayService = ReturnType<typeof createPaymentGatewayService>\n"],
5
+ "mappings": "AACA,SAAS,uBAAuB,0BAA0B;AAC1D;AAAA,EACE;AAAA,OASK;AAIP,SAAS,gBAAgB;AACzB,SAAS,0BAA0B;AACnC,SAAS,sBAAsB,yBAAmD;AAClF,SAAS,+BAA+B;AACxC,SAAS,qBAAqB,sBAAsB;AAEpD,SAAS,0BAA0B,QAA6B,aAAuC;AACrG,QAAM,UAAU,YAAY;AAC5B,MAAI,CAAC,qBAAqB,QAAQ,OAAO,GAAG;AAC1C,UAAM,SAAS,UAAU,MAAM,yBAAyB,OAAO,GAAG;AAAA,EACpE;AACF;AAEA,SAAS,yBACP,QACA,aACA,cACS;AACT,QAAM,UAAU,YAAY;AAC5B,MAAI,iBAAiB,QAAS,QAAO;AACrC,MAAI,CAAC,kBAAkB,SAAS,YAAY,GAAG;AAC7C,UAAM;AAAA,MACJ,4BAA4B,YAAY,2CAA2C,OAAO,SAAS,MAAM;AAAA,IAC3G;AAAA,EACF;AACA,cAAY,gBAAgB;AAC5B,SAAO;AACT;AA0BO,SAAS,4BAA4B,MAAiC;AAC3E,QAAM,EAAE,IAAI,+BAA+B,sBAAsB,IAAI;AAErE,iBAAe,uBACb,eACA,OAC6B;AAC7B,UAAM,cAAc,MAAM;AAAA,MACxB;AAAA,MACA;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,gBAAgB,MAAM;AAAA,QACtB,UAAU,MAAM;AAAA,QAChB,WAAW;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,QAAI,CAAC,aAAa;AAChB,YAAM,IAAI,MAAM,uBAAuB;AAAA,IACzC;AACA,WAAO;AAAA,EACT;AAEA,WAAS,sBAAsB,aAAyC;AACtE,QAAI,OAAO,YAAY,sBAAsB,YAAY,YAAY,kBAAkB,KAAK,EAAE,SAAS,GAAG;AACxG,aAAO,YAAY;AAAA,IACrB;AACA,UAAM,IAAI,MAAM,4CAA4C;AAAA,EAC9D;AAEA,iBAAe,gBAAgB,QAA8B,SAAkC;AAE7F,UAAM,WAAyE;AAAA,MAC7E,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,WAAW;AAAA,IACb;AACA,UAAM,UAAU,SAAS,MAAM;AAC/B,QAAI,CAAC,QAAS;AACd,UAAM,wBAAwB,SAAS,OAAO;AAAA,EAChD;AAEA,iBAAe,oBACb,aACA,OACA,eACA,OACA,SACA,SACA,MACA;AACA,QAAI,CAAC,sBAAuB;AAC5B,UAAM,sBAAsB,MAAM;AAAA,MAChC,eAAe,WAAW,WAAW;AAAA,MACrC,iBAAiB;AAAA,MACjB,eAAe;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS,WAAW;AAAA,IACtB,GAAG,KAAK;AAAA,EACV;AAEA,iBAAe,6BAA6B,aAAqB,OAAqD;AACpH,UAAM,gBAAgB,WAAW,WAAW;AAC5C,UAAM,kBAAkB,KAAK,0BACzB,MAAM,KAAK,wBAAwB,kBAAkB,eAAe,KAAK,IACzE;AACJ,UAAM,UAAU,kBAAkB,aAAa,eAAe;AAC9D,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI;AAAA,QACR,kBACI,+CAA+C,WAAW,cAAc,eAAe,MACvF,+CAA+C,WAAW;AAAA,MAChE;AAAA,IACF;AACA,UAAM,cAAc,MAAM,8BAA8B,QAAQ,eAAe,KAAK,KAAK,CAAC;AAE1F,WAAO,EAAE,SAAS,YAAY;AAAA,EAChC;AAEA,SAAO;AAAA,IACL,MAAM,qBAAqB,OAA8G;AACvI,YAAM,QAAQ,EAAE,gBAAgB,MAAM,gBAAgB,UAAU,MAAM,SAAS;AAC/E,YAAM,EAAE,SAAS,YAAY,IAAI,MAAM,6BAA6B,MAAM,aAAa,KAAK;AAE5F,YAAM,eAAmC;AAAA,QACvC,WAAW,MAAM;AAAA,QACjB,SAAS,MAAM;AAAA,QACf,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM;AAAA,QACtB,QAAQ,MAAM;AAAA,QACd,cAAc,MAAM;AAAA,QACpB,eAAe,MAAM;AAAA,QACrB,cAAc,MAAM;AAAA,QACpB,aAAa,MAAM;AAAA,QACnB,YAAY,MAAM;AAAA,QAClB,WAAW,MAAM;AAAA,QACjB,UAAU,MAAM;AAAA,QAChB,cAAc,MAAM;AAAA,QACpB;AAAA,MACF;AAEA,YAAM,UAAU,MAAM,QAAQ,cAAc,YAAY;AAExD,YAAM,cAAc,GAAG,OAAO,oBAAoB;AAAA,QAChD,WAAW,MAAM;AAAA,QACjB,aAAa,MAAM;AAAA,QACnB,mBAAmB,QAAQ;AAAA,QAC3B,eAAe,QAAQ;AAAA,QACvB,aAAa,QAAQ,gBACf,QAAQ,eAAe,SAAS,aAAa,QAAQ,cAAc,cAAc;AAAA,QACvF,cAAc,QAAQ,gBAAgB;AAAA,QACtC,QAAQ,OAAO,MAAM,MAAM;AAAA,QAC3B,cAAc,MAAM;AAAA,QACpB,iBAAiB;AAAA,UACf,GAAI,QAAQ,gBAAgB,CAAC;AAAA,UAC7B,GAAI,QAAQ,gBAAgB,EAAE,eAAe,QAAQ,cAAc,IAAI,CAAC;AAAA,QAC1E;AAAA,QACA,gBAAgB,MAAM;AAAA,QACtB,UAAU,MAAM;AAAA,MAClB,CAAC;AACD,YAAM,GAAG,QAAQ,WAAW,EAAE,MAAM;AACpC,YAAM,wBAAwB,oCAAoC;AAAA,QAChE,eAAe,YAAY;AAAA,QAC3B,WAAW,YAAY;AAAA,QACvB,aAAa,YAAY;AAAA,QACzB,QAAQ,YAAY;AAAA,QACpB,gBAAgB,YAAY;AAAA,QAC5B,UAAU,YAAY;AAAA,MACxB,CAAC;AACD,YAAM;AAAA,QACJ,YAAY;AAAA,QACZ;AAAA,QACA,YAAY;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,UACE,WAAW,YAAY;AAAA,UACvB,mBAAmB,YAAY;AAAA,UAC/B,QAAQ,YAAY;AAAA,UACpB,QAAQ,MAAM;AAAA,UACd,cAAc,MAAM;AAAA,QACtB;AAAA,MACF;AAEA,aAAO,EAAE,aAAa,QAAQ;AAAA,IAChC;AAAA,IAEA,MAAM,eAAe,eAAuB,QAA4B,OAA6E;AACnJ,YAAM,cAAc,MAAM,uBAAuB,eAAe,KAAK;AACrE,gCAA0B,WAAW,WAAW;AAChD,YAAM,EAAE,SAAS,YAAY,IAAI,MAAM;AAAA,QACrC,YAAY;AAAA,QACZ,EAAE,gBAAgB,YAAY,gBAAgB,UAAU,YAAY,SAAS;AAAA,MAC/E;AAEA,YAAM,SAAS,MAAM,QAAQ,QAAQ;AAAA,QACnC,WAAW,sBAAsB,WAAW;AAAA,QAC5C;AAAA,QACA;AAAA,MACF,CAAC;AAED,YAAM,gBAAgB,yBAAyB,WAAW,aAAa,OAAO,MAAM;AACpF,kBAAY,kBAAkB,EAAE,GAAG,oBAAoB,YAAY,eAAe,GAAG,eAAe,OAAO,aAAa;AACxH,YAAM,GAAG,MAAM;AACf,UAAI,eAAe;AACjB,cAAM,gBAAgB,OAAO,QAAQ;AAAA,UACnC,eAAe,YAAY;AAAA,UAC3B,WAAW,YAAY;AAAA,UACvB,aAAa,YAAY;AAAA,UACzB,gBAAgB,YAAY;AAAA,UAC5B,UAAU,YAAY;AAAA,QACxB,CAAC;AAAA,MACH;AACA,YAAM;AAAA,QACJ,YAAY;AAAA,QACZ,EAAE,gBAAgB,YAAY,gBAAgB,UAAU,YAAY,SAAS;AAAA,QAC7E,YAAY;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,UACE,QAAQ,UAAU;AAAA,UAClB,QAAQ,OAAO;AAAA,UACf,gBAAgB,OAAO;AAAA,QACzB;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,cACJ,eACA,QACA,QACA,OACuB;AACvB,YAAM,cAAc,MAAM,uBAAuB,eAAe,KAAK;AACrE,gCAA0B,UAAU,WAAW;AAC/C,YAAM,EAAE,SAAS,YAAY,IAAI,MAAM;AAAA,QACrC,YAAY;AAAA,QACZ,EAAE,gBAAgB,YAAY,gBAAgB,UAAU,YAAY,SAAS;AAAA,MAC/E;AAEA,YAAM,SAAS,MAAM,QAAQ,OAAO;AAAA,QAClC,WAAW,sBAAsB,WAAW;AAAA,QAC5C;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAED,YAAM,gBAAgB,yBAAyB,UAAU,aAAa,OAAO,MAAM;AACnF,kBAAY,kBAAkB,OAAO;AACrC,kBAAY,kBAAkB,EAAE,GAAG,oBAAoB,YAAY,eAAe,GAAG,cAAc,OAAO,aAAa;AACvH,YAAM,GAAG,MAAM;AACf,UAAI,eAAe;AACjB,cAAM,gBAAgB,OAAO,QAAQ;AAAA,UACnC,eAAe,YAAY;AAAA,UAC3B,WAAW,YAAY;AAAA,UACvB,aAAa,YAAY;AAAA,UACzB,gBAAgB,YAAY;AAAA,UAC5B,UAAU,YAAY;AAAA,QACxB,CAAC;AAAA,MACH;AACA,YAAM;AAAA,QACJ,YAAY;AAAA,QACZ,EAAE,gBAAgB,YAAY,gBAAgB,UAAU,YAAY,SAAS;AAAA,QAC7E,YAAY;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,UACE,QAAQ,UAAU;AAAA,UAClB,QAAQ,UAAU;AAAA,UAClB,QAAQ,OAAO;AAAA,UACf,UAAU,OAAO;AAAA,QACnB;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,cACJ,eACA,QACA,OACuB;AACvB,YAAM,cAAc,MAAM,uBAAuB,eAAe,KAAK;AACrE,gCAA0B,UAAU,WAAW;AAC/C,YAAM,EAAE,SAAS,YAAY,IAAI,MAAM;AAAA,QACrC,YAAY;AAAA,QACZ,EAAE,gBAAgB,YAAY,gBAAgB,UAAU,YAAY,SAAS;AAAA,MAC/E;AAEA,YAAM,SAAS,MAAM,QAAQ,OAAO;AAAA,QAClC,WAAW,sBAAsB,WAAW;AAAA,QAC5C;AAAA,QACA;AAAA,MACF,CAAC;AAED,YAAM,gBAAgB,yBAAyB,UAAU,aAAa,OAAO,MAAM;AACnF,YAAM,GAAG,MAAM;AACf,UAAI,eAAe;AACjB,cAAM,gBAAgB,OAAO,QAAQ;AAAA,UACnC,eAAe,YAAY;AAAA,UAC3B,WAAW,YAAY;AAAA,UACvB,aAAa,YAAY;AAAA,UACzB,gBAAgB,YAAY;AAAA,UAC5B,UAAU,YAAY;AAAA,QACxB,CAAC;AAAA,MACH;AACA,YAAM;AAAA,QACJ,YAAY;AAAA,QACZ,EAAE,gBAAgB,YAAY,gBAAgB,UAAU,YAAY,SAAS;AAAA,QAC7E,YAAY;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,UACE,QAAQ,UAAU;AAAA,UAClB,QAAQ,OAAO;AAAA,QACjB;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,iBAAiB,eAAuB,OAAoF;AAChI,YAAM,cAAc,MAAM,uBAAuB,eAAe,KAAK;AACrE,YAAM,EAAE,SAAS,YAAY,IAAI,MAAM;AAAA,QACrC,YAAY;AAAA,QACZ,EAAE,gBAAgB,YAAY,gBAAgB,UAAU,YAAY,SAAS;AAAA,MAC/E;AAEA,YAAM,SAAS,MAAM,QAAQ,UAAU;AAAA,QACrC,WAAW,sBAAsB,WAAW;AAAA,QAC5C;AAAA,MACF,CAAC;AAED,UAAI,OAAO,WAAW,YAAY,iBAAiB,kBAAkB,YAAY,eAAuC,OAAO,MAAM,GAAG;AACtI,cAAM,iBAAiB,YAAY;AACnC,oBAAY,gBAAgB,OAAO;AACnC,oBAAY,gBAAgB,OAAO;AACnC,oBAAY,kBAAkB,EAAE,GAAG,oBAAoB,YAAY,eAAe,GAAG,cAAc,OAAO,gBAAgB,KAAK;AAC/H,oBAAY,eAAe,oBAAI,KAAK;AACpC,cAAM,GAAG,MAAM;AACf,cAAM,gBAAgB,OAAO,QAAQ;AAAA,UACnC,eAAe,YAAY;AAAA,UAC3B,WAAW,YAAY;AAAA,UACvB,aAAa,YAAY;AAAA,UACzB;AAAA,UACA,gBAAgB,YAAY;AAAA,UAC5B,UAAU,YAAY;AAAA,QACxB,CAAC;AACD,cAAM;AAAA,UACJ,YAAY;AAAA,UACZ,EAAE,gBAAgB,YAAY,gBAAgB,UAAU,YAAY,SAAS;AAAA,UAC7E,YAAY;AAAA,UACZ;AAAA,UACA;AAAA,UACA;AAAA,YACE;AAAA,YACA,YAAY,OAAO;AAAA,UACrB;AAAA,QACF;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,sBAAsB,eAAuB,QAUhD,OAAoE;AACrE,YAAM,cAAc,MAAM,uBAAuB,eAAe,KAAK;AACrE,YAAM,gBAAgB,YAAY;AAClC,YAAM,gBAAgB,kBAAkB,eAAe,OAAO,aAAa;AAC3E,YAAM,oBAAoB,iBAAiB,OAAO,kBAAkB;AACpE,YAAM,iBAAiB,YAAY;AACnC,UAAI,mBAAmB;AACrB,oBAAY,gBAAgB,OAAO;AAAA,MACrC;AACA,UAAI,OAAO,gBAAgB;AACzB,oBAAY,gBAAgB,OAAO;AAAA,MACrC;AACA,UAAI,OAAO,cAAc;AACvB,oBAAY,kBAAkB,EAAE,GAAG,oBAAoB,YAAY,eAAe,GAAG,GAAG,OAAO,aAAa;AAAA,MAC9G;AACA,UAAI,OAAO,cAAc;AACvB,cAAM,aAAa,eAAe,YAAY,UAAU;AACxD,mBAAW,KAAK;AAAA,UACd,WAAW,OAAO,aAAa;AAAA,UAC/B,YAAY,OAAO,aAAa,eAAc,oBAAI,KAAK,GAAE,YAAY;AAAA,UACrE,gBAAgB,OAAO,aAAa;AAAA,UACpC,eAAe,OAAO;AAAA,UACtB,WAAW,OAAO,aAAa;AAAA,QACjC,CAAC;AACD,oBAAY,aAAa;AAAA,MAC3B;AACA,kBAAY,gBAAgB,oBAAI,KAAK;AACrC,YAAM,GAAG,MAAM;AACf,UAAI,mBAAmB;AACrB,cAAM,gBAAgB,OAAO,eAAe;AAAA,UAC1C,eAAe,YAAY;AAAA,UAC3B,WAAW,YAAY;AAAA,UACvB,aAAa,YAAY;AAAA,UACzB;AAAA,UACA,gBAAgB,YAAY;AAAA,UAC5B,UAAU,YAAY;AAAA,QACxB,CAAC;AAAA,MACH;AACA,YAAM;AAAA,QACJ,YAAY;AAAA,QACZ,EAAE,gBAAgB,YAAY,gBAAgB,UAAU,YAAY,SAAS;AAAA,QAC7E,YAAY;AAAA,QACZ,oBAAoB,SAAS;AAAA,QAC7B,oBAAoB,6CAA6C;AAAA,QACjE;AAAA,UACE;AAAA,UACA,YAAY,OAAO;AAAA,UACnB,gBAAgB,OAAO,kBAAkB;AAAA,UACzC,WAAW,OAAO,cAAc,aAAa;AAAA,UAC7C,gBAAgB,OAAO,cAAc,kBAAkB;AAAA,QACzD;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,gBAAgB,IAAY,OAAyF;AACzH,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,UACE;AAAA,UACA,gBAAgB,MAAM;AAAA,UACtB,UAAU,MAAM;AAAA,UAChB,WAAW;AAAA,QACb;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,2BACJ,mBACA,OACA,aACoC;AACpC,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,UACE;AAAA,UACA,gBAAgB,MAAM;AAAA,UACtB,UAAU,MAAM;AAAA,UAChB,WAAW;AAAA,UACX,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;AAAA,QACvC;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,iCAAiC,OAKL;AAChC,YAAM,QAAiC;AAAA,QACrC,eAAe,EAAE,KAAK,CAAC,WAAW,cAAc,oBAAoB,EAAE;AAAA,QACtE,WAAW;AAAA,MACb;AACA,UAAI,OAAO,eAAgB,OAAM,iBAAiB,MAAM;AACxD,UAAI,OAAO,SAAU,OAAM,WAAW,MAAM;AAC5C,UAAI,OAAO,YAAa,OAAM,cAAc,MAAM;AAElD,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,UACE,SAAS,EAAE,WAAW,MAAM;AAAA,UAC5B,OAAO,OAAO,SAAS;AAAA,QACzB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,17 @@
1
+ import { parseDecryptedFieldValue } from "@open-mercato/shared/lib/encryption/tenantDataEncryptionService";
2
+ function normalizeDecryptedValue(value) {
3
+ return typeof value === "string" ? parseDecryptedFieldValue(value) : value;
4
+ }
5
+ function readGatewayMetadata(value) {
6
+ const parsed = normalizeDecryptedValue(value);
7
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
8
+ }
9
+ function readWebhookLog(value) {
10
+ const parsed = normalizeDecryptedValue(value);
11
+ return Array.isArray(parsed) ? parsed : [];
12
+ }
13
+ export {
14
+ readGatewayMetadata,
15
+ readWebhookLog
16
+ };
17
+ //# sourceMappingURL=transaction-fields.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/payment_gateways/lib/transaction-fields.ts"],
4
+ "sourcesContent": ["import { parseDecryptedFieldValue } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport type { GatewayTransaction } from '../data/entities'\n\nexport type WebhookLogEntry = NonNullable<GatewayTransaction['webhookLog']>[number]\n\n// The tenant encryption layer encrypts gateway_metadata and webhook_log as JSON-serialized blobs,\n// but decryption returns the raw decrypted string (entity fields are never auto-parsed to objects \u2014\n// issue #1810). These helpers restore the structured shape consumers expect, mirroring the\n// normalization used by messages (resolveMessageActionData) and integrations (normalizeCredentials).\nfunction normalizeDecryptedValue(value: unknown): unknown {\n return typeof value === 'string' ? parseDecryptedFieldValue(value) : value\n}\n\nexport function readGatewayMetadata(value: unknown): Record<string, unknown> {\n const parsed = normalizeDecryptedValue(value)\n return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {}\n}\n\nexport function readWebhookLog(value: unknown): WebhookLogEntry[] {\n const parsed = normalizeDecryptedValue(value)\n return Array.isArray(parsed) ? (parsed as WebhookLogEntry[]) : []\n}\n"],
5
+ "mappings": "AAAA,SAAS,gCAAgC;AASzC,SAAS,wBAAwB,OAAyB;AACxD,SAAO,OAAO,UAAU,WAAW,yBAAyB,KAAK,IAAI;AACvE;AAEO,SAAS,oBAAoB,OAAyC;AAC3E,QAAM,SAAS,wBAAwB,KAAK;AAC5C,SAAO,WAAW,QAAQ,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,IACxE,SACD,CAAC;AACP;AAEO,SAAS,eAAe,OAAmC;AAChE,QAAM,SAAS,wBAAwB,KAAK;AAC5C,SAAO,MAAM,QAAQ,MAAM,IAAK,SAA+B,CAAC;AAClE;",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.6.6-develop.6373.1.5bece5409d",
3
+ "version": "0.6.6-develop.6374.1.49e692558f",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -246,16 +246,16 @@
246
246
  "zod": "^4.4.3"
247
247
  },
248
248
  "peerDependencies": {
249
- "@open-mercato/ai-assistant": "0.6.6-develop.6373.1.5bece5409d",
250
- "@open-mercato/shared": "0.6.6-develop.6373.1.5bece5409d",
251
- "@open-mercato/ui": "0.6.6-develop.6373.1.5bece5409d",
249
+ "@open-mercato/ai-assistant": "0.6.6-develop.6374.1.49e692558f",
250
+ "@open-mercato/shared": "0.6.6-develop.6374.1.49e692558f",
251
+ "@open-mercato/ui": "0.6.6-develop.6374.1.49e692558f",
252
252
  "react": "^19.0.0",
253
253
  "react-dom": "^19.0.0"
254
254
  },
255
255
  "devDependencies": {
256
- "@open-mercato/ai-assistant": "0.6.6-develop.6373.1.5bece5409d",
257
- "@open-mercato/shared": "0.6.6-develop.6373.1.5bece5409d",
258
- "@open-mercato/ui": "0.6.6-develop.6373.1.5bece5409d",
256
+ "@open-mercato/ai-assistant": "0.6.6-develop.6374.1.49e692558f",
257
+ "@open-mercato/shared": "0.6.6-develop.6374.1.49e692558f",
258
+ "@open-mercato/ui": "0.6.6-develop.6374.1.49e692558f",
259
259
  "@testing-library/dom": "^10.4.1",
260
260
  "@testing-library/jest-dom": "^6.9.1",
261
261
  "@testing-library/react": "^16.3.1",
@@ -5,6 +5,7 @@ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
5
5
  import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
6
6
  import type { IntegrationLogService } from '../../../../integrations/lib/log-service'
7
7
  import { GatewayTransaction } from '../../../data/entities'
8
+ import { readGatewayMetadata, readWebhookLog } from '../../../lib/transaction-fields'
8
9
  import { paymentGatewaysTag } from '../../openapi'
9
10
 
10
11
  export const metadata = {
@@ -82,8 +83,8 @@ export async function GET(req: Request, { params }: { params: Promise<{ id: stri
82
83
  redirectUrl: transaction.redirectUrl ?? null,
83
84
  amount: transaction.amount,
84
85
  currencyCode: transaction.currencyCode,
85
- gatewayMetadata: transaction.gatewayMetadata ?? null,
86
- webhookLog: Array.isArray(transaction.webhookLog) ? transaction.webhookLog : [],
86
+ gatewayMetadata: transaction.gatewayMetadata == null ? null : readGatewayMetadata(transaction.gatewayMetadata),
87
+ webhookLog: readWebhookLog(transaction.webhookLog),
87
88
  lastWebhookAt: toIsoString(transaction.lastWebhookAt),
88
89
  lastPolledAt: toIsoString(transaction.lastPolledAt),
89
90
  expiresAt: toIsoString(transaction.expiresAt),
@@ -0,0 +1,20 @@
1
+ import type { ModuleEncryptionMap } from '@open-mercato/shared/modules/encryption'
2
+
3
+ // Gateway transactions persist provider secrets and raw provider payloads. Encrypt the client
4
+ // secret plus the JSONB columns that carry provider/session data and webhook history at rest.
5
+ // Lookup-critical columns (provider_key, provider_session_id, unified_status, gateway_payment_id,
6
+ // gateway_refund_id) are intentionally left in plaintext so status filters and session lookups keep
7
+ // working without a deterministic hash. The JSONB fields decrypt back to strings (entity fields are
8
+ // never auto-parsed — see issue #1810), so consumers MUST normalize them via lib/transaction-fields.
9
+ export const defaultEncryptionMaps: ModuleEncryptionMap[] = [
10
+ {
11
+ entityId: 'payment_gateways:gateway_transaction',
12
+ fields: [
13
+ { field: 'client_secret' },
14
+ { field: 'gateway_metadata' },
15
+ { field: 'webhook_log' },
16
+ ],
17
+ },
18
+ ]
19
+
20
+ export default defaultEncryptionMaps
@@ -18,6 +18,7 @@ import { conflict } from '@open-mercato/shared/lib/crud/errors'
18
18
  import { GatewayTransaction } from '../data/entities'
19
19
  import { canApplyManualAction, isValidTransition, type ManualGatewayAction } from './status-machine'
20
20
  import { emitPaymentGatewayEvent } from '../events'
21
+ import { readGatewayMetadata, readWebhookLog } from './transaction-fields'
21
22
 
22
23
  function assertManualActionAllowed(action: ManualGatewayAction, transaction: GatewayTransaction): void {
23
24
  const current = transaction.unifiedStatus as UnifiedPaymentStatus
@@ -234,7 +235,7 @@ export function createPaymentGatewayService(deps: PaymentGatewayServiceDeps) {
234
235
  })
235
236
 
236
237
  const statusChanged = applyAdapterResultStatus('capture', transaction, result.status)
237
- transaction.gatewayMetadata = { ...transaction.gatewayMetadata, captureResult: result.providerData }
238
+ transaction.gatewayMetadata = { ...readGatewayMetadata(transaction.gatewayMetadata), captureResult: result.providerData }
238
239
  await em.flush()
239
240
  if (statusChanged) {
240
241
  await emitStatusEvent(result.status, {
@@ -283,7 +284,7 @@ export function createPaymentGatewayService(deps: PaymentGatewayServiceDeps) {
283
284
 
284
285
  const statusChanged = applyAdapterResultStatus('refund', transaction, result.status)
285
286
  transaction.gatewayRefundId = result.refundId
286
- transaction.gatewayMetadata = { ...transaction.gatewayMetadata, refundResult: result.providerData }
287
+ transaction.gatewayMetadata = { ...readGatewayMetadata(transaction.gatewayMetadata), refundResult: result.providerData }
287
288
  await em.flush()
288
289
  if (statusChanged) {
289
290
  await emitStatusEvent(result.status, {
@@ -371,7 +372,7 @@ export function createPaymentGatewayService(deps: PaymentGatewayServiceDeps) {
371
372
  const previousStatus = transaction.unifiedStatus
372
373
  transaction.unifiedStatus = status.status
373
374
  transaction.gatewayStatus = status.status
374
- transaction.gatewayMetadata = { ...transaction.gatewayMetadata, statusResult: status.providerData ?? null }
375
+ transaction.gatewayMetadata = { ...readGatewayMetadata(transaction.gatewayMetadata), statusResult: status.providerData ?? null }
375
376
  transaction.lastPolledAt = new Date()
376
377
  await em.flush()
377
378
  await emitStatusEvent(status.status, {
@@ -421,10 +422,10 @@ export function createPaymentGatewayService(deps: PaymentGatewayServiceDeps) {
421
422
  transaction.gatewayStatus = update.providerStatus
422
423
  }
423
424
  if (update.providerData) {
424
- transaction.gatewayMetadata = { ...transaction.gatewayMetadata, ...update.providerData }
425
+ transaction.gatewayMetadata = { ...readGatewayMetadata(transaction.gatewayMetadata), ...update.providerData }
425
426
  }
426
427
  if (update.webhookEvent) {
427
- const webhookLog = Array.isArray(transaction.webhookLog) ? transaction.webhookLog : []
428
+ const webhookLog = readWebhookLog(transaction.webhookLog)
428
429
  webhookLog.push({
429
430
  eventType: update.webhookEvent.eventType,
430
431
  receivedAt: update.webhookEvent.receivedAt ?? new Date().toISOString(),
@@ -0,0 +1,24 @@
1
+ import { parseDecryptedFieldValue } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'
2
+ import type { GatewayTransaction } from '../data/entities'
3
+
4
+ export type WebhookLogEntry = NonNullable<GatewayTransaction['webhookLog']>[number]
5
+
6
+ // The tenant encryption layer encrypts gateway_metadata and webhook_log as JSON-serialized blobs,
7
+ // but decryption returns the raw decrypted string (entity fields are never auto-parsed to objects —
8
+ // issue #1810). These helpers restore the structured shape consumers expect, mirroring the
9
+ // normalization used by messages (resolveMessageActionData) and integrations (normalizeCredentials).
10
+ function normalizeDecryptedValue(value: unknown): unknown {
11
+ return typeof value === 'string' ? parseDecryptedFieldValue(value) : value
12
+ }
13
+
14
+ export function readGatewayMetadata(value: unknown): Record<string, unknown> {
15
+ const parsed = normalizeDecryptedValue(value)
16
+ return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)
17
+ ? (parsed as Record<string, unknown>)
18
+ : {}
19
+ }
20
+
21
+ export function readWebhookLog(value: unknown): WebhookLogEntry[] {
22
+ const parsed = normalizeDecryptedValue(value)
23
+ return Array.isArray(parsed) ? (parsed as WebhookLogEntry[]) : []
24
+ }