@open-mercato/core 0.6.7-develop.6569.1.c06dee4866 → 0.6.7-develop.6573.1.28649ddec6

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 (45) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/gateway_payment_operation/index.js +35 -0
  3. package/dist/generated/entities/gateway_payment_operation/index.js.map +7 -0
  4. package/dist/generated/entities.ids.generated.js +1 -0
  5. package/dist/generated/entities.ids.generated.js.map +2 -2
  6. package/dist/generated/entity-fields-registry.js +18 -0
  7. package/dist/generated/entity-fields-registry.js.map +2 -2
  8. package/dist/modules/directory/commands/organizations.js +1 -1
  9. package/dist/modules/directory/commands/organizations.js.map +2 -2
  10. package/dist/modules/payment_gateways/api/cancel/route.js +2 -1
  11. package/dist/modules/payment_gateways/api/cancel/route.js.map +2 -2
  12. package/dist/modules/payment_gateways/api/capture/route.js +2 -1
  13. package/dist/modules/payment_gateways/api/capture/route.js.map +2 -2
  14. package/dist/modules/payment_gateways/api/refund/route.js +2 -1
  15. package/dist/modules/payment_gateways/api/refund/route.js.map +2 -2
  16. package/dist/modules/payment_gateways/data/entities.js +67 -0
  17. package/dist/modules/payment_gateways/data/entities.js.map +2 -2
  18. package/dist/modules/payment_gateways/data/validators.js +6 -3
  19. package/dist/modules/payment_gateways/data/validators.js.map +2 -2
  20. package/dist/modules/payment_gateways/lib/gateway-service.js +145 -111
  21. package/dist/modules/payment_gateways/lib/gateway-service.js.map +2 -2
  22. package/dist/modules/payment_gateways/lib/payment-operation-idempotency.js +183 -0
  23. package/dist/modules/payment_gateways/lib/payment-operation-idempotency.js.map +7 -0
  24. package/dist/modules/payment_gateways/migrations/Migration20260709220735_payment_gateways.js +13 -0
  25. package/dist/modules/payment_gateways/migrations/Migration20260709220735_payment_gateways.js.map +7 -0
  26. package/dist/modules/sales/api/quotes/accept/route.js +1 -1
  27. package/dist/modules/sales/api/quotes/accept/route.js.map +2 -2
  28. package/dist/modules/sales/api/quotes/public/[token]/route.js +0 -3
  29. package/dist/modules/sales/api/quotes/public/[token]/route.js.map +2 -2
  30. package/generated/entities/gateway_payment_operation/index.ts +16 -0
  31. package/generated/entities.ids.generated.ts +1 -0
  32. package/generated/entity-fields-registry.ts +18 -0
  33. package/package.json +7 -7
  34. package/src/modules/directory/commands/organizations.ts +1 -1
  35. package/src/modules/payment_gateways/api/cancel/route.ts +1 -0
  36. package/src/modules/payment_gateways/api/capture/route.ts +1 -0
  37. package/src/modules/payment_gateways/api/refund/route.ts +1 -0
  38. package/src/modules/payment_gateways/data/entities.ts +59 -0
  39. package/src/modules/payment_gateways/data/validators.ts +3 -0
  40. package/src/modules/payment_gateways/lib/gateway-service.ts +168 -117
  41. package/src/modules/payment_gateways/lib/payment-operation-idempotency.ts +238 -0
  42. package/src/modules/payment_gateways/migrations/.snapshot-open-mercato.json +331 -1
  43. package/src/modules/payment_gateways/migrations/Migration20260709220735_payment_gateways.ts +12 -0
  44. package/src/modules/sales/api/quotes/accept/route.ts +1 -1
  45. package/src/modules/sales/api/quotes/public/[token]/route.ts +4 -9
@@ -30,16 +30,19 @@ const createSessionSchema = z.object({
30
30
  });
31
31
  const captureSchema = z.object({
32
32
  transactionId: z.string().uuid(),
33
- amount: z.number().positive().optional()
33
+ amount: z.number().positive().optional(),
34
+ operationId: z.string().trim().min(1).max(200).optional()
34
35
  });
35
36
  const refundSchema = z.object({
36
37
  transactionId: z.string().uuid(),
37
38
  amount: z.number().positive().optional(),
38
- reason: z.string().max(200).optional()
39
+ reason: z.string().max(200).optional(),
40
+ operationId: z.string().trim().min(1).max(200).optional()
39
41
  });
40
42
  const cancelSchema = z.object({
41
43
  transactionId: z.string().uuid(),
42
- reason: z.string().max(200).optional()
44
+ reason: z.string().max(200).optional(),
45
+ operationId: z.string().trim().min(1).max(200).optional()
43
46
  });
44
47
  const getStatusSchema = z.object({
45
48
  transactionId: z.string().uuid()
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/payment_gateways/data/validators.ts"],
4
- "sourcesContent": ["import { z } from 'zod'\n\nconst unifiedPaymentStatusSchema = z.enum([\n 'pending',\n 'authorized',\n 'captured',\n 'partially_captured',\n 'refunded',\n 'partially_refunded',\n 'cancelled',\n 'failed',\n 'expired',\n 'unknown',\n])\n\nexport const createSessionSchema = z.object({\n providerKey: z.string().min(1),\n paymentMethodId: z.string().uuid().optional(),\n orderId: z.string().uuid().optional(),\n amount: z.number().positive(),\n currencyCode: z.string().min(3).max(3),\n captureMethod: z.enum(['automatic', 'manual']).default('automatic'),\n description: z.string().max(500).optional(),\n successUrl: z.string().url().optional(),\n cancelUrl: z.string().url().optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n presentation: z.object({\n mode: z.enum(['auto', 'embedded', 'redirect']).optional(),\n rendererKey: z.string().min(1).optional(),\n rendererSettings: z.record(z.string(), z.unknown()).optional(),\n }).optional(),\n})\n\nexport type CreateSessionPayload = z.infer<typeof createSessionSchema>\n\nexport const captureSchema = z.object({\n transactionId: z.string().uuid(),\n amount: z.number().positive().optional(),\n})\n\nexport type CapturePayload = z.infer<typeof captureSchema>\n\nexport const refundSchema = z.object({\n transactionId: z.string().uuid(),\n amount: z.number().positive().optional(),\n reason: z.string().max(200).optional(),\n})\n\nexport type RefundPayload = z.infer<typeof refundSchema>\n\nexport const cancelSchema = z.object({\n transactionId: z.string().uuid(),\n reason: z.string().max(200).optional(),\n})\n\nexport type CancelPayload = z.infer<typeof cancelSchema>\n\nexport const getStatusSchema = z.object({\n transactionId: z.string().uuid(),\n})\n\nexport type GetStatusPayload = z.infer<typeof getStatusSchema>\n\nexport const listTransactionsQuerySchema = z.object({\n page: z.coerce.number().int().min(1).default(1),\n pageSize: z.coerce.number().int().min(1).max(100).default(20),\n search: z.string().trim().max(200).optional(),\n providerKey: z.string().trim().min(1).max(100).optional(),\n status: unifiedPaymentStatusSchema.optional(),\n})\n\nexport type ListTransactionsQuery = z.infer<typeof listTransactionsQuerySchema>\n"],
5
- "mappings": "AAAA,SAAS,SAAS;AAElB,MAAM,6BAA6B,EAAE,KAAK;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,MAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,iBAAiB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC5C,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACpC,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;AAAA,EACrC,eAAe,EAAE,KAAK,CAAC,aAAa,QAAQ,CAAC,EAAE,QAAQ,WAAW;AAAA,EAClE,aAAa,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EAC1C,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,EACtC,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,EACrC,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS;AAAA,EACrD,cAAc,EAAE,OAAO;AAAA,IACrB,MAAM,EAAE,KAAK,CAAC,QAAQ,YAAY,UAAU,CAAC,EAAE,SAAS;AAAA,IACxD,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,IACxC,kBAAkB,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS;AAAA,EAC/D,CAAC,EAAE,SAAS;AACd,CAAC;AAIM,MAAM,gBAAgB,EAAE,OAAO;AAAA,EACpC,eAAe,EAAE,OAAO,EAAE,KAAK;AAAA,EAC/B,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AACzC,CAAC;AAIM,MAAM,eAAe,EAAE,OAAO;AAAA,EACnC,eAAe,EAAE,OAAO,EAAE,KAAK;AAAA,EAC/B,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvC,QAAQ,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AACvC,CAAC;AAIM,MAAM,eAAe,EAAE,OAAO;AAAA,EACnC,eAAe,EAAE,OAAO,EAAE,KAAK;AAAA,EAC/B,QAAQ,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AACvC,CAAC;AAIM,MAAM,kBAAkB,EAAE,OAAO;AAAA,EACtC,eAAe,EAAE,OAAO,EAAE,KAAK;AACjC,CAAC;AAIM,MAAM,8BAA8B,EAAE,OAAO;AAAA,EAClD,MAAM,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;AAAA,EAC9C,UAAU,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE;AAAA,EAC5D,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EAC5C,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACxD,QAAQ,2BAA2B,SAAS;AAC9C,CAAC;",
4
+ "sourcesContent": ["import { z } from 'zod'\n\nconst unifiedPaymentStatusSchema = z.enum([\n 'pending',\n 'authorized',\n 'captured',\n 'partially_captured',\n 'refunded',\n 'partially_refunded',\n 'cancelled',\n 'failed',\n 'expired',\n 'unknown',\n])\n\nexport const createSessionSchema = z.object({\n providerKey: z.string().min(1),\n paymentMethodId: z.string().uuid().optional(),\n orderId: z.string().uuid().optional(),\n amount: z.number().positive(),\n currencyCode: z.string().min(3).max(3),\n captureMethod: z.enum(['automatic', 'manual']).default('automatic'),\n description: z.string().max(500).optional(),\n successUrl: z.string().url().optional(),\n cancelUrl: z.string().url().optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n presentation: z.object({\n mode: z.enum(['auto', 'embedded', 'redirect']).optional(),\n rendererKey: z.string().min(1).optional(),\n rendererSettings: z.record(z.string(), z.unknown()).optional(),\n }).optional(),\n})\n\nexport type CreateSessionPayload = z.infer<typeof createSessionSchema>\n\nexport const captureSchema = z.object({\n transactionId: z.string().uuid(),\n amount: z.number().positive().optional(),\n operationId: z.string().trim().min(1).max(200).optional(),\n})\n\nexport type CapturePayload = z.infer<typeof captureSchema>\n\nexport const refundSchema = z.object({\n transactionId: z.string().uuid(),\n amount: z.number().positive().optional(),\n reason: z.string().max(200).optional(),\n operationId: z.string().trim().min(1).max(200).optional(),\n})\n\nexport type RefundPayload = z.infer<typeof refundSchema>\n\nexport const cancelSchema = z.object({\n transactionId: z.string().uuid(),\n reason: z.string().max(200).optional(),\n operationId: z.string().trim().min(1).max(200).optional(),\n})\n\nexport type CancelPayload = z.infer<typeof cancelSchema>\n\nexport const getStatusSchema = z.object({\n transactionId: z.string().uuid(),\n})\n\nexport type GetStatusPayload = z.infer<typeof getStatusSchema>\n\nexport const listTransactionsQuerySchema = z.object({\n page: z.coerce.number().int().min(1).default(1),\n pageSize: z.coerce.number().int().min(1).max(100).default(20),\n search: z.string().trim().max(200).optional(),\n providerKey: z.string().trim().min(1).max(100).optional(),\n status: unifiedPaymentStatusSchema.optional(),\n})\n\nexport type ListTransactionsQuery = z.infer<typeof listTransactionsQuerySchema>\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAElB,MAAM,6BAA6B,EAAE,KAAK;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,MAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,iBAAiB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC5C,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACpC,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;AAAA,EACrC,eAAe,EAAE,KAAK,CAAC,aAAa,QAAQ,CAAC,EAAE,QAAQ,WAAW;AAAA,EAClE,aAAa,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EAC1C,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,EACtC,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,EACrC,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS;AAAA,EACrD,cAAc,EAAE,OAAO;AAAA,IACrB,MAAM,EAAE,KAAK,CAAC,QAAQ,YAAY,UAAU,CAAC,EAAE,SAAS;AAAA,IACxD,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,IACxC,kBAAkB,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS;AAAA,EAC/D,CAAC,EAAE,SAAS;AACd,CAAC;AAIM,MAAM,gBAAgB,EAAE,OAAO;AAAA,EACpC,eAAe,EAAE,OAAO,EAAE,KAAK;AAAA,EAC/B,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvC,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAC1D,CAAC;AAIM,MAAM,eAAe,EAAE,OAAO;AAAA,EACnC,eAAe,EAAE,OAAO,EAAE,KAAK;AAAA,EAC/B,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvC,QAAQ,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACrC,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAC1D,CAAC;AAIM,MAAM,eAAe,EAAE,OAAO;AAAA,EACnC,eAAe,EAAE,OAAO,EAAE,KAAK;AAAA,EAC/B,QAAQ,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACrC,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAC1D,CAAC;AAIM,MAAM,kBAAkB,EAAE,OAAO;AAAA,EACtC,eAAe,EAAE,OAAO,EAAE,KAAK;AACjC,CAAC;AAIM,MAAM,8BAA8B,EAAE,OAAO;AAAA,EAClD,MAAM,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;AAAA,EAC9C,UAAU,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE;AAAA,EAC5D,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EAC5C,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACxD,QAAQ,2BAA2B,SAAS;AAC9C,CAAC;",
6
6
  "names": []
7
7
  }
@@ -8,6 +8,11 @@ import { GatewaySessionInitialization, GatewayTransaction } from "../data/entiti
8
8
  import { canApplyManualAction, isValidTransition } from "./status-machine.js";
9
9
  import { emitPaymentGatewayEvent } from "../events.js";
10
10
  import { readGatewayMetadata, readWebhookLog } from "./transaction-fields.js";
11
+ import {
12
+ completePaymentOperation,
13
+ failPaymentOperation,
14
+ preparePaymentOperation
15
+ } from "./payment-operation-idempotency.js";
11
16
  import {
12
17
  buildPaymentSessionOperationKey,
13
18
  claimPaymentSessionInitialization,
@@ -27,6 +32,7 @@ function assertManualActionAllowed(action, transaction) {
27
32
  function applyAdapterResultStatus(action, transaction, resultStatus) {
28
33
  const current = transaction.unifiedStatus;
29
34
  if (resultStatus === current) return false;
35
+ if (isValidTransition(resultStatus, current)) return false;
30
36
  if (!isValidTransition(current, resultStatus)) {
31
37
  throw conflict(
32
38
  `Gateway returned status "${resultStatus}" which is not a valid transition from "${current}" for ${action}`
@@ -43,9 +49,9 @@ function createPaymentGatewayService(deps) {
43
49
  deps.sessionClaimOptions?.heartbeatIntervalMs ?? Math.floor(claimStaleAfterMs / 3)
44
50
  );
45
51
  const claimPollIntervalMs = Math.max(1, deps.sessionClaimOptions?.pollIntervalMs ?? PAYMENT_SESSION_WAIT_INTERVAL_MS);
46
- async function findTransactionOrThrow(transactionId, scope) {
52
+ async function findTransactionOrThrow(transactionId, scope, targetEm = em) {
47
53
  const transaction = await findOneWithDecryption(
48
- em,
54
+ targetEm,
49
55
  GatewayTransaction,
50
56
  {
51
57
  id: transactionId,
@@ -103,6 +109,64 @@ function createPaymentGatewayService(deps) {
103
109
  const credentials = await integrationCredentialsService.resolve(integrationId, scope) ?? {};
104
110
  return { adapter, credentials };
105
111
  }
112
+ async function executeManualOperation(input) {
113
+ const transaction = await findTransactionOrThrow(input.transactionId, input.scope);
114
+ const prepared = await preparePaymentOperation({
115
+ em,
116
+ transactionId: transaction.id,
117
+ providerKey: transaction.providerKey,
118
+ action: input.action,
119
+ operationId: input.operationId,
120
+ payload: input.payload,
121
+ scope: input.scope,
122
+ assertInitialAllowed: () => assertManualActionAllowed(input.action, transaction)
123
+ });
124
+ if (prepared.kind === "completed") {
125
+ if (prepared.result.status !== transaction.unifiedStatus) {
126
+ throw conflict(
127
+ `Cannot replay ${input.action} because the payment status has changed since the operation completed`
128
+ );
129
+ }
130
+ return prepared.result;
131
+ }
132
+ try {
133
+ const { adapter, credentials } = await resolveAdapterAndCredentials(
134
+ transaction.providerKey,
135
+ { organizationId: transaction.organizationId, tenantId: transaction.tenantId }
136
+ );
137
+ const result = await input.invoke({
138
+ adapter,
139
+ credentials,
140
+ transaction,
141
+ idempotencyKey: prepared.claim.providerIdempotencyKey
142
+ });
143
+ const statusChanged = await completePaymentOperation(
144
+ em,
145
+ prepared.claim,
146
+ result,
147
+ async (tx) => {
148
+ const current = await findTransactionOrThrow(input.transactionId, input.scope, tx);
149
+ const changed = applyAdapterResultStatus(input.action, current, result.status);
150
+ input.applyResult(current, result);
151
+ return changed;
152
+ }
153
+ );
154
+ if (statusChanged) {
155
+ await emitStatusEvent(result.status, {
156
+ transactionId: transaction.id,
157
+ paymentId: transaction.paymentId,
158
+ providerKey: transaction.providerKey,
159
+ organizationId: transaction.organizationId,
160
+ tenantId: transaction.tenantId
161
+ });
162
+ }
163
+ await input.afterCommit(transaction, result);
164
+ return result;
165
+ } catch (error) {
166
+ await Promise.allSettled([failPaymentOperation(em, prepared.claim)]);
167
+ throw error;
168
+ }
169
+ }
106
170
  function createGatewayTransaction(manager, input, session, id) {
107
171
  const data = {
108
172
  paymentId: input.paymentId,
@@ -283,120 +347,90 @@ function createPaymentGatewayService(deps) {
283
347
  return { transaction, session };
284
348
  }
285
349
  },
286
- async capturePayment(transactionId, amount, scope) {
287
- const transaction = await findTransactionOrThrow(transactionId, scope);
288
- assertManualActionAllowed("capture", transaction);
289
- const { adapter, credentials } = await resolveAdapterAndCredentials(
290
- transaction.providerKey,
291
- { organizationId: transaction.organizationId, tenantId: transaction.tenantId }
292
- );
293
- const result = await adapter.capture({
294
- sessionId: readProviderSessionId(transaction),
295
- amount,
296
- credentials
350
+ async capturePayment(transactionId, amount, scope, operationId) {
351
+ return executeManualOperation({
352
+ action: "capture",
353
+ transactionId,
354
+ operationId,
355
+ payload: { amount: amount ?? null },
356
+ scope,
357
+ invoke: ({ adapter, credentials, transaction, idempotencyKey }) => adapter.capture({
358
+ sessionId: readProviderSessionId(transaction),
359
+ amount,
360
+ credentials,
361
+ idempotencyKey
362
+ }),
363
+ applyResult: (transaction, result) => {
364
+ transaction.gatewayMetadata = {
365
+ ...readGatewayMetadata(transaction.gatewayMetadata),
366
+ captureResult: result.providerData
367
+ };
368
+ },
369
+ afterCommit: (transaction, result) => writeTransactionLog(
370
+ transaction.providerKey,
371
+ { organizationId: transaction.organizationId, tenantId: transaction.tenantId },
372
+ transaction.id,
373
+ "info",
374
+ "Payment captured",
375
+ { amount: amount ?? null, status: result.status, capturedAmount: result.capturedAmount }
376
+ )
297
377
  });
298
- const statusChanged = applyAdapterResultStatus("capture", transaction, result.status);
299
- transaction.gatewayMetadata = { ...readGatewayMetadata(transaction.gatewayMetadata), captureResult: result.providerData };
300
- await em.flush();
301
- if (statusChanged) {
302
- await emitStatusEvent(result.status, {
303
- transactionId: transaction.id,
304
- paymentId: transaction.paymentId,
305
- providerKey: transaction.providerKey,
306
- organizationId: transaction.organizationId,
307
- tenantId: transaction.tenantId
308
- });
309
- }
310
- await writeTransactionLog(
311
- transaction.providerKey,
312
- { organizationId: transaction.organizationId, tenantId: transaction.tenantId },
313
- transaction.id,
314
- "info",
315
- "Payment captured",
316
- {
317
- amount: amount ?? null,
318
- status: result.status,
319
- capturedAmount: result.capturedAmount
320
- }
321
- );
322
- return result;
323
378
  },
324
- async refundPayment(transactionId, amount, reason, scope) {
325
- const transaction = await findTransactionOrThrow(transactionId, scope);
326
- assertManualActionAllowed("refund", transaction);
327
- const { adapter, credentials } = await resolveAdapterAndCredentials(
328
- transaction.providerKey,
329
- { organizationId: transaction.organizationId, tenantId: transaction.tenantId }
330
- );
331
- const result = await adapter.refund({
332
- sessionId: readProviderSessionId(transaction),
333
- amount,
334
- reason,
335
- credentials
379
+ async refundPayment(transactionId, amount, reason, scope, operationId) {
380
+ return executeManualOperation({
381
+ action: "refund",
382
+ transactionId,
383
+ operationId,
384
+ payload: { amount: amount ?? null, reason: reason ?? null },
385
+ scope,
386
+ invoke: ({ adapter, credentials, transaction, idempotencyKey }) => adapter.refund({
387
+ sessionId: readProviderSessionId(transaction),
388
+ amount,
389
+ reason,
390
+ credentials,
391
+ idempotencyKey
392
+ }),
393
+ applyResult: (transaction, result) => {
394
+ transaction.gatewayRefundId = result.refundId;
395
+ transaction.gatewayMetadata = {
396
+ ...readGatewayMetadata(transaction.gatewayMetadata),
397
+ refundResult: result.providerData
398
+ };
399
+ },
400
+ afterCommit: (transaction, result) => writeTransactionLog(
401
+ transaction.providerKey,
402
+ { organizationId: transaction.organizationId, tenantId: transaction.tenantId },
403
+ transaction.id,
404
+ "info",
405
+ "Payment refunded",
406
+ { amount: amount ?? null, reason: reason ?? null, status: result.status, refundId: result.refundId }
407
+ )
336
408
  });
337
- const statusChanged = applyAdapterResultStatus("refund", transaction, result.status);
338
- transaction.gatewayRefundId = result.refundId;
339
- transaction.gatewayMetadata = { ...readGatewayMetadata(transaction.gatewayMetadata), refundResult: result.providerData };
340
- await em.flush();
341
- if (statusChanged) {
342
- await emitStatusEvent(result.status, {
343
- transactionId: transaction.id,
344
- paymentId: transaction.paymentId,
345
- providerKey: transaction.providerKey,
346
- organizationId: transaction.organizationId,
347
- tenantId: transaction.tenantId
348
- });
349
- }
350
- await writeTransactionLog(
351
- transaction.providerKey,
352
- { organizationId: transaction.organizationId, tenantId: transaction.tenantId },
353
- transaction.id,
354
- "info",
355
- "Payment refunded",
356
- {
357
- amount: amount ?? null,
358
- reason: reason ?? null,
359
- status: result.status,
360
- refundId: result.refundId
361
- }
362
- );
363
- return result;
364
409
  },
365
- async cancelPayment(transactionId, reason, scope) {
366
- const transaction = await findTransactionOrThrow(transactionId, scope);
367
- assertManualActionAllowed("cancel", transaction);
368
- const { adapter, credentials } = await resolveAdapterAndCredentials(
369
- transaction.providerKey,
370
- { organizationId: transaction.organizationId, tenantId: transaction.tenantId }
371
- );
372
- const result = await adapter.cancel({
373
- sessionId: readProviderSessionId(transaction),
374
- reason,
375
- credentials
410
+ async cancelPayment(transactionId, reason, scope, operationId) {
411
+ return executeManualOperation({
412
+ action: "cancel",
413
+ transactionId,
414
+ operationId,
415
+ payload: { reason: reason ?? null },
416
+ scope,
417
+ invoke: ({ adapter, credentials, transaction, idempotencyKey }) => adapter.cancel({
418
+ sessionId: readProviderSessionId(transaction),
419
+ reason,
420
+ credentials,
421
+ idempotencyKey
422
+ }),
423
+ applyResult: () => {
424
+ },
425
+ afterCommit: (transaction, result) => writeTransactionLog(
426
+ transaction.providerKey,
427
+ { organizationId: transaction.organizationId, tenantId: transaction.tenantId },
428
+ transaction.id,
429
+ "info",
430
+ "Payment cancelled",
431
+ { reason: reason ?? null, status: result.status }
432
+ )
376
433
  });
377
- const statusChanged = applyAdapterResultStatus("cancel", transaction, result.status);
378
- await em.flush();
379
- if (statusChanged) {
380
- await emitStatusEvent(result.status, {
381
- transactionId: transaction.id,
382
- paymentId: transaction.paymentId,
383
- providerKey: transaction.providerKey,
384
- organizationId: transaction.organizationId,
385
- tenantId: transaction.tenantId
386
- });
387
- }
388
- await writeTransactionLog(
389
- transaction.providerKey,
390
- { organizationId: transaction.organizationId, tenantId: transaction.tenantId },
391
- transaction.id,
392
- "info",
393
- "Payment cancelled",
394
- {
395
- reason: reason ?? null,
396
- status: result.status
397
- }
398
- );
399
- return result;
400
434
  },
401
435
  async getPaymentStatus(transactionId, scope) {
402
436
  const transaction = await findTransactionOrThrow(transactionId, scope);
@@ -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 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 { 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 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(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 ): 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 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 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(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": "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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 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 { 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 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 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: () => assertManualActionAllowed(input.action, transaction),\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 try {\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 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 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 await Promise.allSettled([failPaymentOperation(em, prepared.claim)])\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 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 return executeManualOperation({\n action: 'capture',\n transactionId,\n operationId,\n payload: { amount: amount ?? null },\n scope,\n invoke: ({ adapter, credentials, transaction, idempotencyKey }) => adapter.capture({\n sessionId: readProviderSessionId(transaction),\n amount,\n credentials,\n idempotencyKey,\n }),\n applyResult: (transaction, result) => {\n transaction.gatewayMetadata = {\n ...readGatewayMetadata(transaction.gatewayMetadata),\n captureResult: 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 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 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": "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",
6
6
  "names": ["transaction"]
7
7
  }