@open-mercato/core 0.6.7-develop.6696.1.4236709f7e → 0.6.7-develop.6725.1.07a748cfc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/auth/api/admin/nav.js +14 -4
  3. package/dist/modules/auth/api/admin/nav.js.map +2 -2
  4. package/dist/modules/auth/api/users/route.js +8 -19
  5. package/dist/modules/auth/api/users/route.js.map +2 -2
  6. package/dist/modules/auth/frontend/login.js +4 -1
  7. package/dist/modules/auth/frontend/login.js.map +2 -2
  8. package/dist/modules/auth/lib/backendChrome.js +31 -14
  9. package/dist/modules/auth/lib/backendChrome.js.map +2 -2
  10. package/dist/modules/customers/api/companies/[id]/route.js +3 -4
  11. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  12. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +3 -2
  13. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
  14. package/dist/modules/customers/api/utils.js +12 -18
  15. package/dist/modules/customers/api/utils.js.map +2 -2
  16. package/dist/modules/customers/cli.js +11 -4
  17. package/dist/modules/customers/cli.js.map +2 -2
  18. package/dist/modules/customers/components/detail/ActiveDealCard.js +2 -1
  19. package/dist/modules/customers/components/detail/ActiveDealCard.js.map +2 -2
  20. package/dist/modules/customers/components/detail/CompanyKpiBar.js +6 -5
  21. package/dist/modules/customers/components/detail/CompanyKpiBar.js.map +2 -2
  22. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +4 -2
  23. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  24. package/dist/modules/customers/components/detail/dashboard/helpers.js +4 -3
  25. package/dist/modules/customers/components/detail/dashboard/helpers.js.map +2 -2
  26. package/dist/modules/customers/components/formConfig.js +14 -10
  27. package/dist/modules/customers/components/formConfig.js.map +2 -2
  28. package/dist/modules/customers/lib/dealStatus.js +38 -0
  29. package/dist/modules/customers/lib/dealStatus.js.map +7 -0
  30. package/dist/modules/dashboards/api/widgets/data/batch/route.js +4 -2
  31. package/dist/modules/dashboards/api/widgets/data/batch/route.js.map +2 -2
  32. package/dist/modules/dashboards/api/widgets/data/route.js +20 -2
  33. package/dist/modules/dashboards/api/widgets/data/route.js.map +2 -2
  34. package/dist/modules/dashboards/lib/aggregations.js +76 -40
  35. package/dist/modules/dashboards/lib/aggregations.js.map +2 -2
  36. package/dist/modules/dashboards/lib/exactDecimal.js +69 -0
  37. package/dist/modules/dashboards/lib/exactDecimal.js.map +7 -0
  38. package/dist/modules/dashboards/services/widgetDataService.js +228 -5
  39. package/dist/modules/dashboards/services/widgetDataService.js.map +2 -2
  40. package/dist/modules/feature_toggles/components/FeatureGuard.js +3 -2
  41. package/dist/modules/feature_toggles/components/FeatureGuard.js.map +2 -2
  42. package/dist/modules/feature_toggles/components/hooks/staleTime.js +5 -0
  43. package/dist/modules/feature_toggles/components/hooks/staleTime.js.map +7 -0
  44. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagBoolean.js +5 -2
  45. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagBoolean.js.map +2 -2
  46. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagJson.js +5 -2
  47. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagJson.js.map +2 -2
  48. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagNumber.js +5 -2
  49. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagNumber.js.map +2 -2
  50. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagString.js +5 -2
  51. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagString.js.map +2 -2
  52. package/dist/modules/inbox_ops/api/proposals/route.js +16 -1
  53. package/dist/modules/inbox_ops/api/proposals/route.js.map +2 -2
  54. package/dist/modules/inbox_ops/subscribers/search-reindex-proposal-created.js +32 -0
  55. package/dist/modules/inbox_ops/subscribers/search-reindex-proposal-created.js.map +7 -0
  56. package/dist/modules/integrations/widgets/injection/external-ids/widget.client.js +0 -5
  57. package/dist/modules/integrations/widgets/injection/external-ids/widget.client.js.map +2 -2
  58. package/dist/modules/integrations/widgets/injection/external-ids/widget.js +14 -0
  59. package/dist/modules/integrations/widgets/injection/external-ids/widget.js.map +7 -0
  60. package/dist/modules/messages/lib/searchLookup.js +8 -16
  61. package/dist/modules/messages/lib/searchLookup.js.map +2 -2
  62. package/dist/modules/payment_gateways/api/sessions/route.js +5 -0
  63. package/dist/modules/payment_gateways/api/sessions/route.js.map +2 -2
  64. package/dist/modules/payment_gateways/di.js +17 -1
  65. package/dist/modules/payment_gateways/di.js.map +2 -2
  66. package/dist/modules/payment_gateways/lib/gateway-service.js +8 -0
  67. package/dist/modules/payment_gateways/lib/gateway-service.js.map +2 -2
  68. package/dist/modules/payment_gateways/lib/order-amount-reconciliation.js +58 -0
  69. package/dist/modules/payment_gateways/lib/order-amount-reconciliation.js.map +7 -0
  70. package/dist/modules/query_index/api/openapi.js +13 -1
  71. package/dist/modules/query_index/api/openapi.js.map +2 -2
  72. package/dist/modules/query_index/api/status.js +165 -105
  73. package/dist/modules/query_index/api/status.js.map +2 -2
  74. package/dist/modules/query_index/components/QueryIndexesTable.js +128 -41
  75. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  76. package/dist/modules/query_index/lib/batch.js +11 -4
  77. package/dist/modules/query_index/lib/batch.js.map +2 -2
  78. package/dist/modules/query_index/lib/document.js +12 -5
  79. package/dist/modules/query_index/lib/document.js.map +2 -2
  80. package/dist/modules/query_index/lib/engine.js +36 -4
  81. package/dist/modules/query_index/lib/engine.js.map +2 -2
  82. package/dist/modules/query_index/lib/indexer.js +1 -1
  83. package/dist/modules/query_index/lib/indexer.js.map +2 -2
  84. package/dist/modules/query_index/lib/search-tokens.js +7 -4
  85. package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
  86. package/dist/modules/sales/api/documents/factory.js +14 -3
  87. package/dist/modules/sales/api/documents/factory.js.map +2 -2
  88. package/dist/modules/sales/commands/documents.js +22 -1
  89. package/dist/modules/sales/commands/documents.js.map +2 -2
  90. package/dist/modules/sales/components/useSalesChannelsEnabled.js +2 -35
  91. package/dist/modules/sales/components/useSalesChannelsEnabled.js.map +2 -2
  92. package/dist/modules/sales/data/validators.js +25 -4
  93. package/dist/modules/sales/data/validators.js.map +2 -2
  94. package/dist/modules/sales/di.js +7 -0
  95. package/dist/modules/sales/di.js.map +2 -2
  96. package/dist/modules/sales/services/paymentOrderTotalResolver.js +52 -0
  97. package/dist/modules/sales/services/paymentOrderTotalResolver.js.map +7 -0
  98. package/package.json +7 -7
  99. package/src/modules/auth/api/admin/nav.ts +24 -4
  100. package/src/modules/auth/api/users/route.ts +8 -28
  101. package/src/modules/auth/frontend/login.tsx +4 -1
  102. package/src/modules/auth/lib/backendChrome.tsx +51 -13
  103. package/src/modules/customers/api/companies/[id]/route.ts +3 -4
  104. package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +3 -2
  105. package/src/modules/customers/api/utils.ts +12 -31
  106. package/src/modules/customers/cli.ts +11 -4
  107. package/src/modules/customers/components/detail/ActiveDealCard.tsx +2 -1
  108. package/src/modules/customers/components/detail/CompanyKpiBar.tsx +6 -5
  109. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +4 -0
  110. package/src/modules/customers/components/detail/dashboard/helpers.ts +4 -3
  111. package/src/modules/customers/components/formConfig.tsx +12 -8
  112. package/src/modules/customers/lib/dealStatus.ts +48 -0
  113. package/src/modules/dashboards/api/widgets/data/batch/route.ts +4 -0
  114. package/src/modules/dashboards/api/widgets/data/route.ts +21 -0
  115. package/src/modules/dashboards/lib/aggregations.ts +122 -43
  116. package/src/modules/dashboards/lib/exactDecimal.ts +96 -0
  117. package/src/modules/dashboards/services/widgetDataService.ts +342 -4
  118. package/src/modules/feature_toggles/components/FeatureGuard.tsx +9 -2
  119. package/src/modules/feature_toggles/components/hooks/staleTime.ts +5 -0
  120. package/src/modules/feature_toggles/components/hooks/useFeatureFlagBoolean.ts +13 -1
  121. package/src/modules/feature_toggles/components/hooks/useFeatureFlagJson.ts +12 -3
  122. package/src/modules/feature_toggles/components/hooks/useFeatureFlagNumber.ts +10 -1
  123. package/src/modules/feature_toggles/components/hooks/useFeatureFlagString.ts +10 -1
  124. package/src/modules/inbox_ops/api/proposals/route.ts +20 -1
  125. package/src/modules/inbox_ops/subscribers/search-reindex-proposal-created.ts +39 -0
  126. package/src/modules/integrations/widgets/injection/external-ids/widget.client.tsx +0 -5
  127. package/src/modules/integrations/widgets/injection/external-ids/widget.ts +13 -0
  128. package/src/modules/messages/lib/searchLookup.ts +11 -34
  129. package/src/modules/payment_gateways/api/sessions/route.ts +5 -0
  130. package/src/modules/payment_gateways/di.ts +28 -2
  131. package/src/modules/payment_gateways/i18n/de.json +3 -0
  132. package/src/modules/payment_gateways/i18n/en.json +3 -0
  133. package/src/modules/payment_gateways/i18n/es.json +3 -0
  134. package/src/modules/payment_gateways/i18n/pl.json +3 -0
  135. package/src/modules/payment_gateways/lib/gateway-service.ts +14 -0
  136. package/src/modules/payment_gateways/lib/order-amount-reconciliation.ts +93 -0
  137. package/src/modules/query_index/api/openapi.ts +12 -0
  138. package/src/modules/query_index/api/status.ts +238 -136
  139. package/src/modules/query_index/components/QueryIndexesTable.tsx +192 -46
  140. package/src/modules/query_index/i18n/de.json +5 -3
  141. package/src/modules/query_index/i18n/en.json +5 -3
  142. package/src/modules/query_index/i18n/es.json +5 -3
  143. package/src/modules/query_index/i18n/pl.json +5 -3
  144. package/src/modules/query_index/lib/batch.ts +11 -4
  145. package/src/modules/query_index/lib/document.ts +35 -4
  146. package/src/modules/query_index/lib/engine.ts +53 -5
  147. package/src/modules/query_index/lib/indexer.ts +1 -1
  148. package/src/modules/query_index/lib/search-tokens.ts +13 -4
  149. package/src/modules/sales/api/documents/factory.ts +28 -3
  150. package/src/modules/sales/commands/documents.ts +26 -2
  151. package/src/modules/sales/components/useSalesChannelsEnabled.ts +2 -48
  152. package/src/modules/sales/data/validators.ts +50 -5
  153. package/src/modules/sales/di.ts +9 -0
  154. package/src/modules/sales/services/paymentOrderTotalResolver.ts +67 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/payment_gateways/lib/gateway-service.ts"],
4
- "sourcesContent": ["import { randomUUID } from 'node:crypto'\nimport 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 GatewayAdapter,\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 { GatewayPaymentOperation, GatewaySessionInitialization, GatewayTransaction } from '../data/entities'\nimport { canApplyManualAction, isValidTransition, type ManualGatewayAction } from './status-machine'\nimport { emitPaymentGatewayEvent } from '../events'\nimport { readGatewayMetadata, readWebhookLog } from './transaction-fields'\nimport {\n alignCapturedAmountWithStatus,\n assertCaptureWithinRemaining,\n formatAmountUnits,\n parseAmountUnits,\n releaseCaptureAmount,\n reserveCaptureAmount,\n settleCapturedAmount,\n} from './capture-ledger'\nimport {\n completePaymentOperation,\n failPaymentOperation,\n preparePaymentOperation,\n} from './payment-operation-idempotency'\nimport {\n buildPaymentSessionOperationKey,\n claimPaymentSessionInitialization,\n findPaymentSessionInitialization,\n reclaimPaymentSessionInitialization,\n refreshPaymentSessionInitialization,\n releasePaymentSessionInitialization,\n} from './session-idempotency'\n\nconst PAYMENT_SESSION_CLAIM_STALE_MS = 30_000\nconst PAYMENT_SESSION_WAIT_INTERVAL_MS = 25\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(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 sessionClaimOptions?: {\n staleAfterMs?: number\n heartbeatIntervalMs?: number\n pollIntervalMs?: number\n }\n}\n\nexport interface CreatePaymentSessionInput {\n providerKey: string\n paymentId: string\n idempotencyKey?: 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 const claimStaleAfterMs = Math.max(1, deps.sessionClaimOptions?.staleAfterMs ?? PAYMENT_SESSION_CLAIM_STALE_MS)\n const claimHeartbeatIntervalMs = Math.max(\n 1,\n deps.sessionClaimOptions?.heartbeatIntervalMs ?? Math.floor(claimStaleAfterMs / 3),\n )\n const claimPollIntervalMs = Math.max(1, deps.sessionClaimOptions?.pollIntervalMs ?? PAYMENT_SESSION_WAIT_INTERVAL_MS)\n\n async function findTransactionOrThrow(\n transactionId: string,\n scope: { organizationId: string; tenantId: string },\n targetEm: EntityManager = em,\n ): Promise<GatewayTransaction> {\n const transaction = await findOneWithDecryption(\n targetEm,\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 type ManualOperationResult = CaptureResult | RefundResult | CancelResult\n\n async function executeManualOperation<TResult extends ManualOperationResult>(input: {\n action: ManualGatewayAction\n transactionId: string\n operationId?: string\n payload: Record<string, unknown>\n scope: { organizationId: string; tenantId: string }\n assertInitialAllowed?: (transaction: GatewayTransaction) => void\n beforeInvoke?: (context: { transaction: GatewayTransaction; operation: GatewayPaymentOperation }) => Promise<void>\n releaseOnFailure?: (context: {\n transaction: GatewayTransaction\n operation: GatewayPaymentOperation\n providerInvoked: boolean\n }) => Promise<void>\n invoke: (context: {\n adapter: GatewayAdapter\n credentials: Record<string, unknown>\n transaction: GatewayTransaction\n idempotencyKey: string\n }) => Promise<TResult>\n applyResult: (transaction: GatewayTransaction, result: TResult) => void\n afterCommit: (transaction: GatewayTransaction, result: TResult) => Promise<void>\n }): Promise<TResult> {\n const transaction = await findTransactionOrThrow(input.transactionId, input.scope)\n const prepared = await preparePaymentOperation({\n em,\n transactionId: transaction.id,\n providerKey: transaction.providerKey,\n action: input.action,\n operationId: input.operationId,\n payload: input.payload,\n scope: input.scope,\n assertInitialAllowed: () => {\n assertManualActionAllowed(input.action, transaction)\n input.assertInitialAllowed?.(transaction)\n },\n })\n if (prepared.kind === 'completed') {\n if (prepared.result.status !== transaction.unifiedStatus) {\n throw conflict(\n `Cannot replay ${input.action} because the payment status has changed since the operation completed`,\n )\n }\n return prepared.result as unknown as TResult\n }\n\n const operation = prepared.claim.record\n // Everything after this flips to true is post-settlement: the provider moved the money and the\n // completion transaction committed it. A later event or logging failure must not undo either,\n // so the failure path below only runs while the operation can still be rolled back.\n let settled = false\n // Flips once the provider call returned, so the failure path can tell \"the provider never\n // moved the money\" from \"the provider moved it and only our bookkeeping failed\" \u2014 the two\n // cases need opposite rollback decisions.\n let providerInvoked = false\n try {\n await input.beforeInvoke?.({ transaction, operation })\n const { adapter, credentials } = await resolveAdapterAndCredentials(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n )\n const result = await input.invoke({\n adapter,\n credentials,\n transaction,\n idempotencyKey: prepared.claim.providerIdempotencyKey,\n })\n providerInvoked = true\n const statusChanged = await completePaymentOperation(\n em,\n prepared.claim,\n result as unknown as Record<string, unknown>,\n async (tx) => {\n const current = await findTransactionOrThrow(input.transactionId, input.scope, tx)\n const changed = applyAdapterResultStatus(input.action, current, result.status)\n input.applyResult(current, result)\n return changed\n },\n )\n settled = true\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 input.afterCommit(transaction, result)\n return result\n } catch (error: unknown) {\n if (!settled) {\n if (input.releaseOnFailure) {\n await input.releaseOnFailure({ transaction, operation, providerInvoked }).catch(() => undefined)\n }\n await Promise.allSettled([failPaymentOperation(em, prepared.claim)])\n }\n throw error\n }\n }\n\n function createGatewayTransaction(\n manager: EntityManager,\n input: CreatePaymentSessionInput,\n session: CreateSessionResult,\n id?: string,\n ): GatewayTransaction {\n const data = {\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 // An automatic-capture session comes back already captured, so the ledger has to start\n // at the full amount \u2014 otherwise a later manual capture would look like the first one.\n capturedAmount: session.status === 'captured' ? String(input.amount) : '0',\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 deletedAt: null,\n }\n return id\n ? manager.create(GatewayTransaction, { id, ...data })\n : manager.create(GatewayTransaction, data)\n }\n\n function restoreSession(transaction: GatewayTransaction): CreateSessionResult {\n const metadata = readGatewayMetadata(transaction.gatewayMetadata)\n const { clientSession, ...providerData } = metadata\n return {\n sessionId: readProviderSessionId(transaction),\n status: transaction.unifiedStatus as UnifiedPaymentStatus,\n ...(transaction.clientSecret ? { clientSecret: transaction.clientSecret } : {}),\n ...(transaction.redirectUrl ? { redirectUrl: transaction.redirectUrl } : {}),\n ...(Object.keys(providerData).length > 0 ? { providerData } : {}),\n ...(clientSession && typeof clientSession === 'object'\n ? { clientSession: clientSession as CreateSessionResult['clientSession'] }\n : {}),\n }\n }\n\n async function recordCreatedSession(\n transaction: GatewayTransaction,\n input: CreatePaymentSessionInput,\n scope: { organizationId: string; tenantId: string },\n ): Promise<void> {\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\n function startClaimHeartbeat(\n ownership: { id: string; claimToken: string },\n scope: { organizationId: string; tenantId: string },\n ): () => Promise<void> {\n let stopped = false\n let timer: ReturnType<typeof setTimeout> | null = null\n let pendingRefresh = Promise.resolve()\n\n const schedule = () => {\n timer = setTimeout(() => {\n pendingRefresh = refreshPaymentSessionInitialization(em, ownership, scope, new Date())\n .then(\n (refreshed) => {\n if (refreshed && !stopped) schedule()\n },\n () => {\n if (!stopped) schedule()\n },\n )\n }, claimHeartbeatIntervalMs)\n }\n schedule()\n\n return async () => {\n stopped = true\n if (timer) clearTimeout(timer)\n await pendingRefresh\n }\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 idempotencyKey: input.idempotencyKey,\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 if (!input.idempotencyKey) {\n const session = await adapter.createSession(sessionInput)\n const transaction = createGatewayTransaction(em, input, session)\n await em.persist(transaction).flush()\n await recordCreatedSession(transaction, input, scope)\n return { transaction, session }\n }\n\n const operationKey = buildPaymentSessionOperationKey({\n idempotencyKey: input.idempotencyKey,\n paymentId: input.paymentId,\n providerKey: input.providerKey,\n scope,\n })\n while (true) {\n const existing = await findPaymentSessionInitialization(em, operationKey, input.providerKey, scope)\n if (existing?.gatewayTransactionId) {\n const transaction = await findOneWithDecryption(\n em.fork(),\n GatewayTransaction,\n {\n id: existing.gatewayTransactionId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!transaction) {\n throw new Error('Completed payment session is missing its gateway transaction')\n }\n return { transaction, session: restoreSession(transaction) }\n }\n\n const claimedAt = new Date()\n const staleBefore = new Date(claimedAt.getTime() - claimStaleAfterMs)\n const ownership = existing\n ? await reclaimPaymentSessionInitialization(em, existing, scope, claimedAt, staleBefore)\n : await claimPaymentSessionInitialization(em, operationKey, input.providerKey, scope, claimedAt)\n\n if (!ownership) {\n await new Promise((resolve) => setTimeout(resolve, claimPollIntervalMs))\n continue\n }\n\n let session: CreateSessionResult\n const stopHeartbeat = startClaimHeartbeat(ownership, scope)\n try {\n session = await adapter.createSession({ ...sessionInput, idempotencyKey: operationKey })\n } catch (error) {\n await releasePaymentSessionInitialization(em, ownership, scope).catch(() => undefined)\n throw error\n } finally {\n await stopHeartbeat()\n }\n\n const transactionId = randomUUID()\n const transaction = await em.fork().transactional(async (transactionEm) => {\n const finalizedRows = await transactionEm.nativeUpdate(\n GatewaySessionInitialization,\n {\n id: ownership.id,\n claimToken: ownership.claimToken,\n gatewayTransactionId: null,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n {\n gatewayTransactionId: transactionId,\n claimToken: null,\n claimedAt: null,\n updatedAt: new Date(),\n },\n )\n if (finalizedRows === 0) return null\n const created = createGatewayTransaction(transactionEm, input, session, transactionId)\n await transactionEm.persist(created).flush()\n return created\n })\n\n if (!transaction) {\n continue\n }\n await recordCreatedSession(transaction, input, scope)\n return { transaction, session }\n }\n },\n\n async capturePayment(\n transactionId: string,\n amount: number | undefined,\n scope: { organizationId: string; tenantId: string },\n operationId?: string,\n ): Promise<CaptureResult> {\n let reservedUnits: bigint | null = null\n let providerAmount = amount\n return executeManualOperation({\n action: 'capture',\n transactionId,\n operationId,\n payload: { amount: amount ?? null },\n scope,\n assertInitialAllowed: (transaction) => { assertCaptureWithinRemaining(transaction, amount) },\n beforeInvoke: async ({ transaction, operation }) => {\n const alreadyCapturedUnits = parseAmountUnits(transaction.capturedAmount)\n reservedUnits = await reserveCaptureAmount(em, { transaction, operation, amount, scope })\n // \"Capture the rest\" has to name the remaining amount once part of the authorization is\n // already captured, otherwise the provider would capture the full amount a second time.\n if (amount === undefined && alreadyCapturedUnits > 0n) {\n providerAmount = Number(formatAmountUnits(reservedUnits))\n }\n },\n // The slice goes back only while the provider has not been called yet. Once the capture\n // call returned, the money may already have moved and only the completion transaction\n // failed, so releasing would reopen the authorization for a fresh operation id and allow an\n // over-capture. Holding the reservation is the conservative outcome: a retry of this same\n // operation id reuses both the reservation and the provider idempotency key, so the provider\n // itself collapses the duplicate.\n releaseOnFailure: async ({ transaction, operation, providerInvoked }) => {\n if (reservedUnits === null) return\n if (providerInvoked) {\n await writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n 'warn',\n 'Capture reservation stays outstanding because the provider call already returned',\n { operationId: operation.operationId, reservedAmount: formatAmountUnits(reservedUnits) },\n )\n return\n }\n const released = await releaseCaptureAmount(em, { transactionId, operation, reservedUnits, scope })\n if (released) return\n await writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n 'warn',\n 'Capture reservation stays outstanding after a failed capture',\n { operationId: operation.operationId, reservedAmount: formatAmountUnits(reservedUnits) },\n )\n },\n invoke: ({ adapter, credentials, transaction, idempotencyKey }) => adapter.capture({\n sessionId: readProviderSessionId(transaction),\n amount: providerAmount,\n credentials,\n idempotencyKey,\n }),\n applyResult: (transaction, result) => {\n transaction.gatewayMetadata = {\n ...readGatewayMetadata(transaction.gatewayMetadata),\n captureResult: result.providerData,\n }\n if (reservedUnits !== null) settleCapturedAmount(transaction, reservedUnits, result.capturedAmount)\n },\n afterCommit: (transaction, result) => writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n 'info',\n 'Payment captured',\n { amount: amount ?? null, status: result.status, capturedAmount: result.capturedAmount },\n ),\n })\n },\n\n async refundPayment(\n transactionId: string,\n amount: number | undefined,\n reason: string | undefined,\n scope: { organizationId: string; tenantId: string },\n operationId?: string,\n ): Promise<RefundResult> {\n return executeManualOperation({\n action: 'refund',\n transactionId,\n operationId,\n payload: { amount: amount ?? null, reason: reason ?? null },\n scope,\n invoke: ({ adapter, credentials, transaction, idempotencyKey }) => adapter.refund({\n sessionId: readProviderSessionId(transaction),\n amount,\n reason,\n credentials,\n idempotencyKey,\n }),\n applyResult: (transaction, result) => {\n transaction.gatewayRefundId = result.refundId\n transaction.gatewayMetadata = {\n ...readGatewayMetadata(transaction.gatewayMetadata),\n refundResult: result.providerData,\n }\n },\n afterCommit: (transaction, result) => writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n 'info',\n 'Payment refunded',\n { amount: amount ?? null, reason: reason ?? null, status: result.status, refundId: result.refundId },\n ),\n })\n },\n\n async cancelPayment(\n transactionId: string,\n reason: string | undefined,\n scope: { organizationId: string; tenantId: string },\n operationId?: string,\n ): Promise<CancelResult> {\n return executeManualOperation({\n action: 'cancel',\n transactionId,\n operationId,\n payload: { reason: reason ?? null },\n scope,\n invoke: ({ adapter, credentials, transaction, idempotencyKey }) => adapter.cancel({\n sessionId: readProviderSessionId(transaction),\n reason,\n credentials,\n idempotencyKey,\n }),\n applyResult: () => {},\n afterCommit: (transaction, result) => writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n 'info',\n 'Payment cancelled',\n { reason: reason ?? null, status: result.status },\n ),\n })\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 alignCapturedAmountWithStatus(transaction, 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 alignCapturedAmountWithStatus(transaction, 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": "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",
4
+ "sourcesContent": ["import { randomUUID } from 'node:crypto'\nimport 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 GatewayAdapter,\n type GatewayPaymentStatus,\n type PaymentGatewayPresentationRequest,\n type PaymentOrderTotalResolver,\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 { GatewayPaymentOperation, GatewaySessionInitialization, GatewayTransaction } from '../data/entities'\nimport { canApplyManualAction, isValidTransition, type ManualGatewayAction } from './status-machine'\nimport { emitPaymentGatewayEvent } from '../events'\nimport { readGatewayMetadata, readWebhookLog } from './transaction-fields'\nimport { reconcileSessionAmountWithOrder } from './order-amount-reconciliation'\nimport {\n alignCapturedAmountWithStatus,\n assertCaptureWithinRemaining,\n formatAmountUnits,\n parseAmountUnits,\n releaseCaptureAmount,\n reserveCaptureAmount,\n settleCapturedAmount,\n} from './capture-ledger'\nimport {\n completePaymentOperation,\n failPaymentOperation,\n preparePaymentOperation,\n} from './payment-operation-idempotency'\nimport {\n buildPaymentSessionOperationKey,\n claimPaymentSessionInitialization,\n findPaymentSessionInitialization,\n reclaimPaymentSessionInitialization,\n refreshPaymentSessionInitialization,\n releasePaymentSessionInitialization,\n} from './session-idempotency'\n\nconst PAYMENT_SESSION_CLAIM_STALE_MS = 30_000\nconst PAYMENT_SESSION_WAIT_INTERVAL_MS = 25\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(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 * Optional seam supplied by the module that owns orders (`sales` by default).\n * When absent, session amounts cannot be reconciled and are accepted as-is.\n */\n paymentOrderTotalResolver?: PaymentOrderTotalResolver | null\n sessionClaimOptions?: {\n staleAfterMs?: number\n heartbeatIntervalMs?: number\n pollIntervalMs?: number\n }\n}\n\nexport interface CreatePaymentSessionInput {\n providerKey: string\n paymentId: string\n idempotencyKey?: 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 const claimStaleAfterMs = Math.max(1, deps.sessionClaimOptions?.staleAfterMs ?? PAYMENT_SESSION_CLAIM_STALE_MS)\n const claimHeartbeatIntervalMs = Math.max(\n 1,\n deps.sessionClaimOptions?.heartbeatIntervalMs ?? Math.floor(claimStaleAfterMs / 3),\n )\n const claimPollIntervalMs = Math.max(1, deps.sessionClaimOptions?.pollIntervalMs ?? PAYMENT_SESSION_WAIT_INTERVAL_MS)\n\n async function findTransactionOrThrow(\n transactionId: string,\n scope: { organizationId: string; tenantId: string },\n targetEm: EntityManager = em,\n ): Promise<GatewayTransaction> {\n const transaction = await findOneWithDecryption(\n targetEm,\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 type ManualOperationResult = CaptureResult | RefundResult | CancelResult\n\n async function executeManualOperation<TResult extends ManualOperationResult>(input: {\n action: ManualGatewayAction\n transactionId: string\n operationId?: string\n payload: Record<string, unknown>\n scope: { organizationId: string; tenantId: string }\n assertInitialAllowed?: (transaction: GatewayTransaction) => void\n beforeInvoke?: (context: { transaction: GatewayTransaction; operation: GatewayPaymentOperation }) => Promise<void>\n releaseOnFailure?: (context: {\n transaction: GatewayTransaction\n operation: GatewayPaymentOperation\n providerInvoked: boolean\n }) => Promise<void>\n invoke: (context: {\n adapter: GatewayAdapter\n credentials: Record<string, unknown>\n transaction: GatewayTransaction\n idempotencyKey: string\n }) => Promise<TResult>\n applyResult: (transaction: GatewayTransaction, result: TResult) => void\n afterCommit: (transaction: GatewayTransaction, result: TResult) => Promise<void>\n }): Promise<TResult> {\n const transaction = await findTransactionOrThrow(input.transactionId, input.scope)\n const prepared = await preparePaymentOperation({\n em,\n transactionId: transaction.id,\n providerKey: transaction.providerKey,\n action: input.action,\n operationId: input.operationId,\n payload: input.payload,\n scope: input.scope,\n assertInitialAllowed: () => {\n assertManualActionAllowed(input.action, transaction)\n input.assertInitialAllowed?.(transaction)\n },\n })\n if (prepared.kind === 'completed') {\n if (prepared.result.status !== transaction.unifiedStatus) {\n throw conflict(\n `Cannot replay ${input.action} because the payment status has changed since the operation completed`,\n )\n }\n return prepared.result as unknown as TResult\n }\n\n const operation = prepared.claim.record\n // Everything after this flips to true is post-settlement: the provider moved the money and the\n // completion transaction committed it. A later event or logging failure must not undo either,\n // so the failure path below only runs while the operation can still be rolled back.\n let settled = false\n // Flips once the provider call returned, so the failure path can tell \"the provider never\n // moved the money\" from \"the provider moved it and only our bookkeeping failed\" \u2014 the two\n // cases need opposite rollback decisions.\n let providerInvoked = false\n try {\n await input.beforeInvoke?.({ transaction, operation })\n const { adapter, credentials } = await resolveAdapterAndCredentials(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n )\n const result = await input.invoke({\n adapter,\n credentials,\n transaction,\n idempotencyKey: prepared.claim.providerIdempotencyKey,\n })\n providerInvoked = true\n const statusChanged = await completePaymentOperation(\n em,\n prepared.claim,\n result as unknown as Record<string, unknown>,\n async (tx) => {\n const current = await findTransactionOrThrow(input.transactionId, input.scope, tx)\n const changed = applyAdapterResultStatus(input.action, current, result.status)\n input.applyResult(current, result)\n return changed\n },\n )\n settled = true\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 input.afterCommit(transaction, result)\n return result\n } catch (error: unknown) {\n if (!settled) {\n if (input.releaseOnFailure) {\n await input.releaseOnFailure({ transaction, operation, providerInvoked }).catch(() => undefined)\n }\n await Promise.allSettled([failPaymentOperation(em, prepared.claim)])\n }\n throw error\n }\n }\n\n function createGatewayTransaction(\n manager: EntityManager,\n input: CreatePaymentSessionInput,\n session: CreateSessionResult,\n id?: string,\n ): GatewayTransaction {\n const data = {\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 // An automatic-capture session comes back already captured, so the ledger has to start\n // at the full amount \u2014 otherwise a later manual capture would look like the first one.\n capturedAmount: session.status === 'captured' ? String(input.amount) : '0',\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 deletedAt: null,\n }\n return id\n ? manager.create(GatewayTransaction, { id, ...data })\n : manager.create(GatewayTransaction, data)\n }\n\n function restoreSession(transaction: GatewayTransaction): CreateSessionResult {\n const metadata = readGatewayMetadata(transaction.gatewayMetadata)\n const { clientSession, ...providerData } = metadata\n return {\n sessionId: readProviderSessionId(transaction),\n status: transaction.unifiedStatus as UnifiedPaymentStatus,\n ...(transaction.clientSecret ? { clientSecret: transaction.clientSecret } : {}),\n ...(transaction.redirectUrl ? { redirectUrl: transaction.redirectUrl } : {}),\n ...(Object.keys(providerData).length > 0 ? { providerData } : {}),\n ...(clientSession && typeof clientSession === 'object'\n ? { clientSession: clientSession as CreateSessionResult['clientSession'] }\n : {}),\n }\n }\n\n async function recordCreatedSession(\n transaction: GatewayTransaction,\n input: CreatePaymentSessionInput,\n scope: { organizationId: string; tenantId: string },\n ): Promise<void> {\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\n function startClaimHeartbeat(\n ownership: { id: string; claimToken: string },\n scope: { organizationId: string; tenantId: string },\n ): () => Promise<void> {\n let stopped = false\n let timer: ReturnType<typeof setTimeout> | null = null\n let pendingRefresh = Promise.resolve()\n\n const schedule = () => {\n timer = setTimeout(() => {\n pendingRefresh = refreshPaymentSessionInitialization(em, ownership, scope, new Date())\n .then(\n (refreshed) => {\n if (refreshed && !stopped) schedule()\n },\n () => {\n if (!stopped) schedule()\n },\n )\n }, claimHeartbeatIntervalMs)\n }\n schedule()\n\n return async () => {\n stopped = true\n if (timer) clearTimeout(timer)\n await pendingRefresh\n }\n }\n\n return {\n async createPaymentSession(input: CreatePaymentSessionInput): Promise<{ transaction: GatewayTransaction; session: CreateSessionResult }> {\n const scope = { organizationId: input.organizationId, tenantId: input.tenantId }\n await reconcileSessionAmountWithOrder({\n orderId: input.orderId,\n amount: input.amount,\n currencyCode: input.currencyCode,\n scope,\n resolver: deps.paymentOrderTotalResolver,\n })\n const { adapter, credentials } = await resolveAdapterAndCredentials(input.providerKey, scope)\n\n const sessionInput: CreateSessionInput = {\n paymentId: input.paymentId,\n orderId: input.orderId,\n idempotencyKey: input.idempotencyKey,\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 if (!input.idempotencyKey) {\n const session = await adapter.createSession(sessionInput)\n const transaction = createGatewayTransaction(em, input, session)\n await em.persist(transaction).flush()\n await recordCreatedSession(transaction, input, scope)\n return { transaction, session }\n }\n\n const operationKey = buildPaymentSessionOperationKey({\n idempotencyKey: input.idempotencyKey,\n paymentId: input.paymentId,\n providerKey: input.providerKey,\n scope,\n })\n while (true) {\n const existing = await findPaymentSessionInitialization(em, operationKey, input.providerKey, scope)\n if (existing?.gatewayTransactionId) {\n const transaction = await findOneWithDecryption(\n em.fork(),\n GatewayTransaction,\n {\n id: existing.gatewayTransactionId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!transaction) {\n throw new Error('Completed payment session is missing its gateway transaction')\n }\n return { transaction, session: restoreSession(transaction) }\n }\n\n const claimedAt = new Date()\n const staleBefore = new Date(claimedAt.getTime() - claimStaleAfterMs)\n const ownership = existing\n ? await reclaimPaymentSessionInitialization(em, existing, scope, claimedAt, staleBefore)\n : await claimPaymentSessionInitialization(em, operationKey, input.providerKey, scope, claimedAt)\n\n if (!ownership) {\n await new Promise((resolve) => setTimeout(resolve, claimPollIntervalMs))\n continue\n }\n\n let session: CreateSessionResult\n const stopHeartbeat = startClaimHeartbeat(ownership, scope)\n try {\n session = await adapter.createSession({ ...sessionInput, idempotencyKey: operationKey })\n } catch (error) {\n await releasePaymentSessionInitialization(em, ownership, scope).catch(() => undefined)\n throw error\n } finally {\n await stopHeartbeat()\n }\n\n const transactionId = randomUUID()\n const transaction = await em.fork().transactional(async (transactionEm) => {\n const finalizedRows = await transactionEm.nativeUpdate(\n GatewaySessionInitialization,\n {\n id: ownership.id,\n claimToken: ownership.claimToken,\n gatewayTransactionId: null,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n {\n gatewayTransactionId: transactionId,\n claimToken: null,\n claimedAt: null,\n updatedAt: new Date(),\n },\n )\n if (finalizedRows === 0) return null\n const created = createGatewayTransaction(transactionEm, input, session, transactionId)\n await transactionEm.persist(created).flush()\n return created\n })\n\n if (!transaction) {\n continue\n }\n await recordCreatedSession(transaction, input, scope)\n return { transaction, session }\n }\n },\n\n async capturePayment(\n transactionId: string,\n amount: number | undefined,\n scope: { organizationId: string; tenantId: string },\n operationId?: string,\n ): Promise<CaptureResult> {\n let reservedUnits: bigint | null = null\n let providerAmount = amount\n return executeManualOperation({\n action: 'capture',\n transactionId,\n operationId,\n payload: { amount: amount ?? null },\n scope,\n assertInitialAllowed: (transaction) => { assertCaptureWithinRemaining(transaction, amount) },\n beforeInvoke: async ({ transaction, operation }) => {\n const alreadyCapturedUnits = parseAmountUnits(transaction.capturedAmount)\n reservedUnits = await reserveCaptureAmount(em, { transaction, operation, amount, scope })\n // \"Capture the rest\" has to name the remaining amount once part of the authorization is\n // already captured, otherwise the provider would capture the full amount a second time.\n if (amount === undefined && alreadyCapturedUnits > 0n) {\n providerAmount = Number(formatAmountUnits(reservedUnits))\n }\n },\n // The slice goes back only while the provider has not been called yet. Once the capture\n // call returned, the money may already have moved and only the completion transaction\n // failed, so releasing would reopen the authorization for a fresh operation id and allow an\n // over-capture. Holding the reservation is the conservative outcome: a retry of this same\n // operation id reuses both the reservation and the provider idempotency key, so the provider\n // itself collapses the duplicate.\n releaseOnFailure: async ({ transaction, operation, providerInvoked }) => {\n if (reservedUnits === null) return\n if (providerInvoked) {\n await writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n 'warn',\n 'Capture reservation stays outstanding because the provider call already returned',\n { operationId: operation.operationId, reservedAmount: formatAmountUnits(reservedUnits) },\n )\n return\n }\n const released = await releaseCaptureAmount(em, { transactionId, operation, reservedUnits, scope })\n if (released) return\n await writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n 'warn',\n 'Capture reservation stays outstanding after a failed capture',\n { operationId: operation.operationId, reservedAmount: formatAmountUnits(reservedUnits) },\n )\n },\n invoke: ({ adapter, credentials, transaction, idempotencyKey }) => adapter.capture({\n sessionId: readProviderSessionId(transaction),\n amount: providerAmount,\n credentials,\n idempotencyKey,\n }),\n applyResult: (transaction, result) => {\n transaction.gatewayMetadata = {\n ...readGatewayMetadata(transaction.gatewayMetadata),\n captureResult: result.providerData,\n }\n if (reservedUnits !== null) settleCapturedAmount(transaction, reservedUnits, result.capturedAmount)\n },\n afterCommit: (transaction, result) => writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n 'info',\n 'Payment captured',\n { amount: amount ?? null, status: result.status, capturedAmount: result.capturedAmount },\n ),\n })\n },\n\n async refundPayment(\n transactionId: string,\n amount: number | undefined,\n reason: string | undefined,\n scope: { organizationId: string; tenantId: string },\n operationId?: string,\n ): Promise<RefundResult> {\n return executeManualOperation({\n action: 'refund',\n transactionId,\n operationId,\n payload: { amount: amount ?? null, reason: reason ?? null },\n scope,\n invoke: ({ adapter, credentials, transaction, idempotencyKey }) => adapter.refund({\n sessionId: readProviderSessionId(transaction),\n amount,\n reason,\n credentials,\n idempotencyKey,\n }),\n applyResult: (transaction, result) => {\n transaction.gatewayRefundId = result.refundId\n transaction.gatewayMetadata = {\n ...readGatewayMetadata(transaction.gatewayMetadata),\n refundResult: result.providerData,\n }\n },\n afterCommit: (transaction, result) => writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n 'info',\n 'Payment refunded',\n { amount: amount ?? null, reason: reason ?? null, status: result.status, refundId: result.refundId },\n ),\n })\n },\n\n async cancelPayment(\n transactionId: string,\n reason: string | undefined,\n scope: { organizationId: string; tenantId: string },\n operationId?: string,\n ): Promise<CancelResult> {\n return executeManualOperation({\n action: 'cancel',\n transactionId,\n operationId,\n payload: { reason: reason ?? null },\n scope,\n invoke: ({ adapter, credentials, transaction, idempotencyKey }) => adapter.cancel({\n sessionId: readProviderSessionId(transaction),\n reason,\n credentials,\n idempotencyKey,\n }),\n applyResult: () => {},\n afterCommit: (transaction, result) => writeTransactionLog(\n transaction.providerKey,\n { organizationId: transaction.organizationId, tenantId: transaction.tenantId },\n transaction.id,\n 'info',\n 'Payment cancelled',\n { reason: reason ?? null, status: result.status },\n ),\n })\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 alignCapturedAmountWithStatus(transaction, 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 alignCapturedAmountWithStatus(transaction, 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": "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",
6
6
  "names": ["transaction"]
7
7
  }
@@ -0,0 +1,58 @@
1
+ import { conflict } from "@open-mercato/shared/lib/crud/errors";
2
+ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
3
+ import { createFallbackTranslator } from "@open-mercato/shared/lib/i18n/translate";
4
+ const AMOUNT_TOLERANCE = 1e-4;
5
+ function normalizeCurrencyCode(currencyCode) {
6
+ return currencyCode.trim().toUpperCase();
7
+ }
8
+ async function resolveConflictTranslator() {
9
+ try {
10
+ const { translate } = await resolveTranslations();
11
+ return translate;
12
+ } catch {
13
+ return createFallbackTranslator({});
14
+ }
15
+ }
16
+ function isPaymentOrderTotalResolver(candidate) {
17
+ return !!candidate && typeof candidate === "object" && typeof candidate.resolveOrderTotal === "function";
18
+ }
19
+ function assertSessionAmountMatchesOrderTotal(requested, orderTotal, translate) {
20
+ if (normalizeCurrencyCode(requested.currencyCode) !== normalizeCurrencyCode(orderTotal.currencyCode)) {
21
+ throw conflict(translate(
22
+ "payment_gateways.errors.sessionCurrencyMismatch",
23
+ "Payment session currency {currencyCode} does not match the currency of order {orderId}",
24
+ { currencyCode: normalizeCurrencyCode(requested.currencyCode), orderId: requested.orderId }
25
+ ));
26
+ }
27
+ if (Math.abs(requested.amount - orderTotal.amountDue) > AMOUNT_TOLERANCE) {
28
+ throw conflict(translate(
29
+ "payment_gateways.errors.sessionAmountMismatch",
30
+ "Payment session amount {amount} does not match the amount due for order {orderId}",
31
+ { amount: requested.amount, orderId: requested.orderId }
32
+ ));
33
+ }
34
+ }
35
+ async function reconcileSessionAmountWithOrder(input) {
36
+ const { orderId, resolver } = input;
37
+ if (!orderId || !resolver) return;
38
+ const orderTotal = await resolver.resolveOrderTotal(orderId, input.scope);
39
+ const translate = await resolveConflictTranslator();
40
+ if (!orderTotal) {
41
+ throw conflict(translate(
42
+ "payment_gateways.errors.sessionOrderNotFound",
43
+ "Order {orderId} was not found in the current scope",
44
+ { orderId }
45
+ ));
46
+ }
47
+ assertSessionAmountMatchesOrderTotal(
48
+ { orderId, amount: input.amount, currencyCode: input.currencyCode },
49
+ orderTotal,
50
+ translate
51
+ );
52
+ }
53
+ export {
54
+ assertSessionAmountMatchesOrderTotal,
55
+ isPaymentOrderTotalResolver,
56
+ reconcileSessionAmountWithOrder
57
+ };
58
+ //# sourceMappingURL=order-amount-reconciliation.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/payment_gateways/lib/order-amount-reconciliation.ts"],
4
+ "sourcesContent": ["import { conflict } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { createFallbackTranslator, type TranslateWithFallbackFn } from '@open-mercato/shared/lib/i18n/translate'\nimport type {\n PaymentGatewayScope,\n PaymentOrderTotal,\n PaymentOrderTotalResolver,\n} from '@open-mercato/shared/modules/payment_gateways/types'\n\n/** Amounts are stored as numeric(18,4); anything below that is rounding noise, not a mismatch. */\nconst AMOUNT_TOLERANCE = 0.0001\n\nfunction normalizeCurrencyCode(currencyCode: string): string {\n return currencyCode.trim().toUpperCase()\n}\n\n/**\n * Conflict copy is served from the module catalog on a request path. Contexts\n * without a registered module dictionary (CLI commands, workers, unit tests)\n * fall back to the English template shipped with each call, so a missing\n * dictionary degrades the wording of a rejection but never its outcome.\n */\nasync function resolveConflictTranslator(): Promise<TranslateWithFallbackFn> {\n try {\n const { translate } = await resolveTranslations()\n return translate\n } catch {\n return createFallbackTranslator({})\n }\n}\n\nexport function isPaymentOrderTotalResolver(candidate: unknown): candidate is PaymentOrderTotalResolver {\n return !!candidate\n && typeof candidate === 'object'\n && typeof (candidate as PaymentOrderTotalResolver).resolveOrderTotal === 'function'\n}\n\nexport function assertSessionAmountMatchesOrderTotal(\n requested: { orderId: string; amount: number; currencyCode: string },\n orderTotal: PaymentOrderTotal,\n translate: TranslateWithFallbackFn,\n): void {\n if (normalizeCurrencyCode(requested.currencyCode) !== normalizeCurrencyCode(orderTotal.currencyCode)) {\n throw conflict(translate(\n 'payment_gateways.errors.sessionCurrencyMismatch',\n 'Payment session currency {currencyCode} does not match the currency of order {orderId}',\n { currencyCode: normalizeCurrencyCode(requested.currencyCode), orderId: requested.orderId },\n ))\n }\n if (Math.abs(requested.amount - orderTotal.amountDue) > AMOUNT_TOLERANCE) {\n throw conflict(translate(\n 'payment_gateways.errors.sessionAmountMismatch',\n 'Payment session amount {amount} does not match the amount due for order {orderId}',\n { amount: requested.amount, orderId: requested.orderId },\n ))\n }\n}\n\n/**\n * Reconciles a caller-supplied session amount against the authoritative amount\n * due for the referenced order. Reconciliation is skipped when the request\n * references no order, or when no module registered a resolver (for example an\n * installation without the `sales` module) \u2014 there is nothing authoritative to\n * compare against in either case. A referenced order that does not resolve\n * inside the caller's tenant/organization scope is rejected exactly like an\n * unknown one, so the response never reveals whether it exists elsewhere.\n */\nexport async function reconcileSessionAmountWithOrder(input: {\n orderId?: string\n amount: number\n currencyCode: string\n scope: PaymentGatewayScope\n resolver?: PaymentOrderTotalResolver | null\n}): Promise<void> {\n const { orderId, resolver } = input\n if (!orderId || !resolver) return\n\n const orderTotal = await resolver.resolveOrderTotal(orderId, input.scope)\n const translate = await resolveConflictTranslator()\n if (!orderTotal) {\n throw conflict(translate(\n 'payment_gateways.errors.sessionOrderNotFound',\n 'Order {orderId} was not found in the current scope',\n { orderId },\n ))\n }\n\n assertSessionAmountMatchesOrderTotal(\n { orderId, amount: input.amount, currencyCode: input.currencyCode },\n orderTotal,\n translate,\n )\n}\n"],
5
+ "mappings": "AAAA,SAAS,gBAAgB;AACzB,SAAS,2BAA2B;AACpC,SAAS,gCAA8D;AAQvE,MAAM,mBAAmB;AAEzB,SAAS,sBAAsB,cAA8B;AAC3D,SAAO,aAAa,KAAK,EAAE,YAAY;AACzC;AAQA,eAAe,4BAA8D;AAC3E,MAAI;AACF,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,yBAAyB,CAAC,CAAC;AAAA,EACpC;AACF;AAEO,SAAS,4BAA4B,WAA4D;AACtG,SAAO,CAAC,CAAC,aACJ,OAAO,cAAc,YACrB,OAAQ,UAAwC,sBAAsB;AAC7E;AAEO,SAAS,qCACd,WACA,YACA,WACM;AACN,MAAI,sBAAsB,UAAU,YAAY,MAAM,sBAAsB,WAAW,YAAY,GAAG;AACpG,UAAM,SAAS;AAAA,MACb;AAAA,MACA;AAAA,MACA,EAAE,cAAc,sBAAsB,UAAU,YAAY,GAAG,SAAS,UAAU,QAAQ;AAAA,IAC5F,CAAC;AAAA,EACH;AACA,MAAI,KAAK,IAAI,UAAU,SAAS,WAAW,SAAS,IAAI,kBAAkB;AACxE,UAAM,SAAS;AAAA,MACb;AAAA,MACA;AAAA,MACA,EAAE,QAAQ,UAAU,QAAQ,SAAS,UAAU,QAAQ;AAAA,IACzD,CAAC;AAAA,EACH;AACF;AAWA,eAAsB,gCAAgC,OAMpC;AAChB,QAAM,EAAE,SAAS,SAAS,IAAI;AAC9B,MAAI,CAAC,WAAW,CAAC,SAAU;AAE3B,QAAM,aAAa,MAAM,SAAS,kBAAkB,SAAS,MAAM,KAAK;AACxE,QAAM,YAAY,MAAM,0BAA0B;AAClD,MAAI,CAAC,YAAY;AACf,UAAM,SAAS;AAAA,MACb;AAAA,MACA;AAAA,MACA,EAAE,QAAQ;AAAA,IACZ,CAAC;AAAA,EACH;AAEA;AAAA,IACE,EAAE,SAAS,QAAQ,MAAM,QAAQ,cAAc,MAAM,aAAa;AAAA,IAClE;AAAA,IACA;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -30,13 +30,25 @@ const queryIndexJobSchema = z.object({
30
30
  });
31
31
  const queryIndexStatusItemSchema = z.object({
32
32
  entityId: z.string(),
33
+ /** Human label. Code-defined entities have no label registry, so this mirrors `entityId`. */
33
34
  label: z.string(),
34
35
  baseCount: z.number().int().nonnegative().nullable(),
35
36
  indexCount: z.number().int().nonnegative().nullable(),
36
37
  vectorCount: z.number().int().nonnegative().nullable().optional(),
38
+ /** The entity declares `buildSource`, i.e. it is configured for vector search. */
37
39
  vectorEnabled: z.boolean().optional(),
40
+ /** Additive: vector indexing can actually run (auto-indexing on, provider reachable, tenant opted in). */
41
+ vectorIndexingActive: z.boolean().optional(),
42
+ fulltextCount: z.number().int().nonnegative().nullable().optional(),
43
+ fulltextEnabled: z.boolean().optional(),
44
+ /** Additive: the entity has active custom field definitions in the current scope. */
45
+ hasCustomFields: z.boolean().optional(),
46
+ /** Aggregate health: query index in sync AND, when vector-configured, vector coverage complete. */
38
47
  ok: z.boolean(),
39
- job: queryIndexJobSchema
48
+ /** Additive: query index alone is in sync with the base table, independent of vector/fulltext. */
49
+ queryIndexOk: z.boolean().optional(),
50
+ job: queryIndexJobSchema,
51
+ refreshedAt: z.string().datetime().nullable().optional()
40
52
  });
41
53
  const queryIndexErrorLogSchema = z.object({
42
54
  id: z.string(),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/query_index/api/openapi.ts"],
4
- "sourcesContent": ["import { z } from 'zod'\nimport { ENTITY_ID_PATTERN } from '@open-mercato/shared/lib/query/engine'\n\nexport const queryIndexTag = 'Query Index'\n\nexport const queryIndexErrorSchema = z.object({\n error: z.string(),\n}).passthrough()\n\nexport const queryIndexPartitionSchema = z.object({\n partitionIndex: z.number().int().nonnegative().nullable().optional(),\n partitionCount: z.number().int().positive().nullable().optional(),\n status: z.enum(['reindexing', 'purging', 'stalled', 'completed', 'failed']),\n startedAt: z.string().nullable().optional(),\n finishedAt: z.string().nullable().optional(),\n heartbeatAt: z.string().nullable().optional(),\n processedCount: z.number().int().nonnegative().nullable().optional(),\n totalCount: z.number().int().nonnegative().nullable().optional(),\n})\n\nexport const queryIndexJobSchema = z.object({\n status: z.enum(['idle', 'reindexing', 'purging', 'stalled', 'failed']),\n startedAt: z.string().nullable().optional(),\n finishedAt: z.string().nullable().optional(),\n heartbeatAt: z.string().nullable().optional(),\n processedCount: z.number().int().nonnegative().nullable().optional(),\n totalCount: z.number().int().nonnegative().nullable().optional(),\n partitions: z.array(queryIndexPartitionSchema).optional(),\n scope: queryIndexPartitionSchema.pick({\n status: true,\n processedCount: true,\n totalCount: true,\n })\n .nullable()\n .optional(),\n})\n\nexport const queryIndexStatusItemSchema = z.object({\n entityId: z.string(),\n label: z.string(),\n baseCount: z.number().int().nonnegative().nullable(),\n indexCount: z.number().int().nonnegative().nullable(),\n vectorCount: z.number().int().nonnegative().nullable().optional(),\n vectorEnabled: z.boolean().optional(),\n ok: z.boolean(),\n job: queryIndexJobSchema,\n})\n\nexport const queryIndexErrorLogSchema = z.object({\n id: z.string(),\n source: z.string(),\n handler: z.string(),\n entityType: z.string().nullable(),\n recordId: z.string().nullable(),\n tenantId: z.string().nullable(),\n organizationId: z.string().nullable(),\n message: z.string(),\n stack: z.string().nullable(),\n payload: z.unknown().nullable(),\n occurredAt: z.string(),\n})\n\nexport const queryIndexStatusLogSchema = z.object({\n id: z.string(),\n source: z.string(),\n handler: z.string(),\n level: z.enum(['info', 'warn']),\n entityType: z.string().nullable(),\n recordId: z.string().nullable(),\n tenantId: z.string().nullable(),\n organizationId: z.string().nullable(),\n message: z.string(),\n details: z.unknown().nullable(),\n occurredAt: z.string(),\n})\n\nexport const queryIndexStatusResponseSchema = z.object({\n items: z.array(queryIndexStatusItemSchema),\n errors: z.array(queryIndexErrorLogSchema),\n logs: z.array(queryIndexStatusLogSchema),\n})\n\nexport const queryIndexReindexRequestSchema = z.object({\n entityType: z.string().min(1).regex(ENTITY_ID_PATTERN),\n force: z.boolean().optional(),\n batchSize: z.number().int().positive().optional(),\n partitionCount: z.number().int().positive().optional(),\n partitionIndex: z.number().int().nonnegative().optional(),\n})\n\nexport const queryIndexPurgeRequestSchema = z.object({\n entityType: z.string().min(1),\n})\n\nexport const queryIndexOkSchema = z.object({\n ok: z.literal(true),\n})\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { z } from 'zod'\nimport { ENTITY_ID_PATTERN } from '@open-mercato/shared/lib/query/engine'\n\nexport const queryIndexTag = 'Query Index'\n\nexport const queryIndexErrorSchema = z.object({\n error: z.string(),\n}).passthrough()\n\nexport const queryIndexPartitionSchema = z.object({\n partitionIndex: z.number().int().nonnegative().nullable().optional(),\n partitionCount: z.number().int().positive().nullable().optional(),\n status: z.enum(['reindexing', 'purging', 'stalled', 'completed', 'failed']),\n startedAt: z.string().nullable().optional(),\n finishedAt: z.string().nullable().optional(),\n heartbeatAt: z.string().nullable().optional(),\n processedCount: z.number().int().nonnegative().nullable().optional(),\n totalCount: z.number().int().nonnegative().nullable().optional(),\n})\n\nexport const queryIndexJobSchema = z.object({\n status: z.enum(['idle', 'reindexing', 'purging', 'stalled', 'failed']),\n startedAt: z.string().nullable().optional(),\n finishedAt: z.string().nullable().optional(),\n heartbeatAt: z.string().nullable().optional(),\n processedCount: z.number().int().nonnegative().nullable().optional(),\n totalCount: z.number().int().nonnegative().nullable().optional(),\n partitions: z.array(queryIndexPartitionSchema).optional(),\n scope: queryIndexPartitionSchema.pick({\n status: true,\n processedCount: true,\n totalCount: true,\n })\n .nullable()\n .optional(),\n})\n\nexport const queryIndexStatusItemSchema = z.object({\n entityId: z.string(),\n /** Human label. Code-defined entities have no label registry, so this mirrors `entityId`. */\n label: z.string(),\n baseCount: z.number().int().nonnegative().nullable(),\n indexCount: z.number().int().nonnegative().nullable(),\n vectorCount: z.number().int().nonnegative().nullable().optional(),\n /** The entity declares `buildSource`, i.e. it is configured for vector search. */\n vectorEnabled: z.boolean().optional(),\n /** Additive: vector indexing can actually run (auto-indexing on, provider reachable, tenant opted in). */\n vectorIndexingActive: z.boolean().optional(),\n fulltextCount: z.number().int().nonnegative().nullable().optional(),\n fulltextEnabled: z.boolean().optional(),\n /** Additive: the entity has active custom field definitions in the current scope. */\n hasCustomFields: z.boolean().optional(),\n /** Aggregate health: query index in sync AND, when vector-configured, vector coverage complete. */\n ok: z.boolean(),\n /** Additive: query index alone is in sync with the base table, independent of vector/fulltext. */\n queryIndexOk: z.boolean().optional(),\n job: queryIndexJobSchema,\n refreshedAt: z.string().datetime().nullable().optional(),\n})\n\nexport const queryIndexErrorLogSchema = z.object({\n id: z.string(),\n source: z.string(),\n handler: z.string(),\n entityType: z.string().nullable(),\n recordId: z.string().nullable(),\n tenantId: z.string().nullable(),\n organizationId: z.string().nullable(),\n message: z.string(),\n stack: z.string().nullable(),\n payload: z.unknown().nullable(),\n occurredAt: z.string(),\n})\n\nexport const queryIndexStatusLogSchema = z.object({\n id: z.string(),\n source: z.string(),\n handler: z.string(),\n level: z.enum(['info', 'warn']),\n entityType: z.string().nullable(),\n recordId: z.string().nullable(),\n tenantId: z.string().nullable(),\n organizationId: z.string().nullable(),\n message: z.string(),\n details: z.unknown().nullable(),\n occurredAt: z.string(),\n})\n\nexport const queryIndexStatusResponseSchema = z.object({\n items: z.array(queryIndexStatusItemSchema),\n errors: z.array(queryIndexErrorLogSchema),\n logs: z.array(queryIndexStatusLogSchema),\n})\n\nexport const queryIndexReindexRequestSchema = z.object({\n entityType: z.string().min(1).regex(ENTITY_ID_PATTERN),\n force: z.boolean().optional(),\n batchSize: z.number().int().positive().optional(),\n partitionCount: z.number().int().positive().optional(),\n partitionIndex: z.number().int().nonnegative().optional(),\n})\n\nexport const queryIndexPurgeRequestSchema = z.object({\n entityType: z.string().min(1),\n})\n\nexport const queryIndexOkSchema = z.object({\n ok: z.literal(true),\n})\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }