@open-mercato/core 0.4.7-develop-0a657b411f → 0.4.7-develop-e249d3e7d0

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 (278) hide show
  1. package/dist/generated/entities/carrier_shipment/index.js +37 -0
  2. package/dist/generated/entities/carrier_shipment/index.js.map +7 -0
  3. package/dist/generated/entities/gateway_transaction/index.js +47 -0
  4. package/dist/generated/entities/gateway_transaction/index.js.map +7 -0
  5. package/dist/generated/entities/webhook_processed_event/index.js +17 -0
  6. package/dist/generated/entities/webhook_processed_event/index.js.map +7 -0
  7. package/dist/generated/entities.ids.generated.js +10 -1
  8. package/dist/generated/entities.ids.generated.js.map +2 -2
  9. package/dist/generated/entity-fields-registry.js +6 -0
  10. package/dist/generated/entity-fields-registry.js.map +2 -2
  11. package/dist/modules/data_sync/api/runs/[id]/cancel.js +14 -5
  12. package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
  13. package/dist/modules/data_sync/backend/data-sync/page.meta.js +2 -2
  14. package/dist/modules/data_sync/backend/data-sync/page.meta.js.map +1 -1
  15. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +37 -12
  16. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +2 -2
  17. package/dist/modules/directory/api/get/tenants/lookup.js +1 -0
  18. package/dist/modules/directory/api/get/tenants/lookup.js.map +2 -2
  19. package/dist/modules/integrations/api/[id]/route.js +38 -11
  20. package/dist/modules/integrations/api/[id]/route.js.map +2 -2
  21. package/dist/modules/integrations/api/logs/route.js +52 -26
  22. package/dist/modules/integrations/api/logs/route.js.map +2 -2
  23. package/dist/modules/integrations/api/route.js +37 -7
  24. package/dist/modules/integrations/api/route.js.map +2 -2
  25. package/dist/modules/integrations/api/umes-read.js +121 -0
  26. package/dist/modules/integrations/api/umes-read.js.map +7 -0
  27. package/dist/modules/integrations/backend/integrations/[id]/page.js +715 -183
  28. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
  29. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +30 -9
  30. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
  31. package/dist/modules/integrations/backend/integrations/detail-page-widgets.js +46 -0
  32. package/dist/modules/integrations/backend/integrations/detail-page-widgets.js.map +7 -0
  33. package/dist/modules/integrations/backend/integrations/page.js +78 -62
  34. package/dist/modules/integrations/backend/integrations/page.js.map +2 -2
  35. package/dist/modules/integrations/backend/integrations/page.meta.js +2 -2
  36. package/dist/modules/integrations/backend/integrations/page.meta.js.map +1 -1
  37. package/dist/modules/integrations/setup.js +2 -2
  38. package/dist/modules/integrations/setup.js.map +2 -2
  39. package/dist/modules/payment_gateways/acl.js +12 -0
  40. package/dist/modules/payment_gateways/acl.js.map +7 -0
  41. package/dist/modules/payment_gateways/api/cancel/route.js +55 -0
  42. package/dist/modules/payment_gateways/api/cancel/route.js.map +7 -0
  43. package/dist/modules/payment_gateways/api/capture/route.js +55 -0
  44. package/dist/modules/payment_gateways/api/capture/route.js.map +7 -0
  45. package/dist/modules/payment_gateways/api/interceptors.js +24 -0
  46. package/dist/modules/payment_gateways/api/interceptors.js.map +7 -0
  47. package/dist/modules/payment_gateways/api/openapi.js +5 -0
  48. package/dist/modules/payment_gateways/api/openapi.js.map +7 -0
  49. package/dist/modules/payment_gateways/api/refund/route.js +56 -0
  50. package/dist/modules/payment_gateways/api/refund/route.js.map +7 -0
  51. package/dist/modules/payment_gateways/api/sessions/route.js +74 -0
  52. package/dist/modules/payment_gateways/api/sessions/route.js.map +7 -0
  53. package/dist/modules/payment_gateways/api/status/route.js +66 -0
  54. package/dist/modules/payment_gateways/api/status/route.js.map +7 -0
  55. package/dist/modules/payment_gateways/api/transactions/[id]/route.js +118 -0
  56. package/dist/modules/payment_gateways/api/transactions/[id]/route.js.map +7 -0
  57. package/dist/modules/payment_gateways/api/transactions/route.js +113 -0
  58. package/dist/modules/payment_gateways/api/transactions/route.js.map +7 -0
  59. package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +136 -0
  60. package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +7 -0
  61. package/dist/modules/payment_gateways/backend/payment-gateways/page.js +496 -0
  62. package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +7 -0
  63. package/dist/modules/payment_gateways/backend/payment-gateways/page.meta.js +23 -0
  64. package/dist/modules/payment_gateways/backend/payment-gateways/page.meta.js.map +7 -0
  65. package/dist/modules/payment_gateways/data/enrichers.js +5 -0
  66. package/dist/modules/payment_gateways/data/enrichers.js.map +7 -0
  67. package/dist/modules/payment_gateways/data/entities.js +131 -0
  68. package/dist/modules/payment_gateways/data/entities.js.map +7 -0
  69. package/dist/modules/payment_gateways/data/validators.js +57 -0
  70. package/dist/modules/payment_gateways/data/validators.js.map +7 -0
  71. package/dist/modules/payment_gateways/di.js +16 -0
  72. package/dist/modules/payment_gateways/di.js.map +7 -0
  73. package/dist/modules/payment_gateways/events.js +21 -0
  74. package/dist/modules/payment_gateways/events.js.map +7 -0
  75. package/dist/modules/payment_gateways/i18n/en.js +6 -0
  76. package/dist/modules/payment_gateways/i18n/en.js.map +7 -0
  77. package/dist/modules/payment_gateways/i18n/pl.js +6 -0
  78. package/dist/modules/payment_gateways/i18n/pl.js.map +7 -0
  79. package/dist/modules/payment_gateways/index.js +9 -0
  80. package/dist/modules/payment_gateways/index.js.map +7 -0
  81. package/dist/modules/payment_gateways/lib/gateway-service.js +378 -0
  82. package/dist/modules/payment_gateways/lib/gateway-service.js.map +7 -0
  83. package/dist/modules/payment_gateways/lib/queue.js +17 -0
  84. package/dist/modules/payment_gateways/lib/queue.js.map +7 -0
  85. package/dist/modules/payment_gateways/lib/status-machine.js +29 -0
  86. package/dist/modules/payment_gateways/lib/status-machine.js.map +7 -0
  87. package/dist/modules/payment_gateways/lib/webhook-processor.js +88 -0
  88. package/dist/modules/payment_gateways/lib/webhook-processor.js.map +7 -0
  89. package/dist/modules/payment_gateways/lib/webhook-utils.js +42 -0
  90. package/dist/modules/payment_gateways/lib/webhook-utils.js.map +7 -0
  91. package/dist/modules/payment_gateways/migrations/Migration20260305122155.js +19 -0
  92. package/dist/modules/payment_gateways/migrations/Migration20260305122155.js.map +7 -0
  93. package/dist/modules/payment_gateways/setup.js +13 -0
  94. package/dist/modules/payment_gateways/setup.js.map +7 -0
  95. package/dist/modules/payment_gateways/widgets/injection-table.js +7 -0
  96. package/dist/modules/payment_gateways/widgets/injection-table.js.map +7 -0
  97. package/dist/modules/payment_gateways/workers/status-poller.js +44 -0
  98. package/dist/modules/payment_gateways/workers/status-poller.js.map +7 -0
  99. package/dist/modules/payment_gateways/workers/webhook-processor.js +20 -0
  100. package/dist/modules/payment_gateways/workers/webhook-processor.js.map +7 -0
  101. package/dist/modules/sales/data/enrichers.js +72 -0
  102. package/dist/modules/sales/data/enrichers.js.map +7 -0
  103. package/dist/modules/sales/lib/makeSalesLineRoute.js +3 -0
  104. package/dist/modules/sales/lib/makeSalesLineRoute.js.map +2 -2
  105. package/dist/modules/sales/widgets/injection/payment-gateway-config-field/widget.js +29 -0
  106. package/dist/modules/sales/widgets/injection/payment-gateway-config-field/widget.js.map +7 -0
  107. package/dist/modules/sales/widgets/injection/payment-gateway-status-column/widget.js +23 -0
  108. package/dist/modules/sales/widgets/injection/payment-gateway-status-column/widget.js.map +7 -0
  109. package/dist/modules/sales/widgets/injection-table.js +13 -1
  110. package/dist/modules/sales/widgets/injection-table.js.map +2 -2
  111. package/dist/modules/shipping_carriers/acl.js +10 -0
  112. package/dist/modules/shipping_carriers/acl.js.map +7 -0
  113. package/dist/modules/shipping_carriers/api/cancel/route.js +55 -0
  114. package/dist/modules/shipping_carriers/api/cancel/route.js.map +7 -0
  115. package/dist/modules/shipping_carriers/api/interceptors.js +21 -0
  116. package/dist/modules/shipping_carriers/api/interceptors.js.map +7 -0
  117. package/dist/modules/shipping_carriers/api/openapi.js +5 -0
  118. package/dist/modules/shipping_carriers/api/openapi.js.map +7 -0
  119. package/dist/modules/shipping_carriers/api/rates/route.js +55 -0
  120. package/dist/modules/shipping_carriers/api/rates/route.js.map +7 -0
  121. package/dist/modules/shipping_carriers/api/shipments/route.js +61 -0
  122. package/dist/modules/shipping_carriers/api/shipments/route.js.map +7 -0
  123. package/dist/modules/shipping_carriers/api/tracking/route.js +58 -0
  124. package/dist/modules/shipping_carriers/api/tracking/route.js.map +7 -0
  125. package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js +119 -0
  126. package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js.map +7 -0
  127. package/dist/modules/shipping_carriers/data/enrichers.js +82 -0
  128. package/dist/modules/shipping_carriers/data/enrichers.js.map +7 -0
  129. package/dist/modules/shipping_carriers/data/entities.js +80 -0
  130. package/dist/modules/shipping_carriers/data/entities.js.map +7 -0
  131. package/dist/modules/shipping_carriers/data/validators.js +49 -0
  132. package/dist/modules/shipping_carriers/data/validators.js.map +7 -0
  133. package/dist/modules/shipping_carriers/di.js +15 -0
  134. package/dist/modules/shipping_carriers/di.js.map +7 -0
  135. package/dist/modules/shipping_carriers/events.js +19 -0
  136. package/dist/modules/shipping_carriers/events.js.map +7 -0
  137. package/dist/modules/shipping_carriers/i18n/en.js +11 -0
  138. package/dist/modules/shipping_carriers/i18n/en.js.map +7 -0
  139. package/dist/modules/shipping_carriers/i18n/pl.js +11 -0
  140. package/dist/modules/shipping_carriers/i18n/pl.js.map +7 -0
  141. package/dist/modules/shipping_carriers/index.js +9 -0
  142. package/dist/modules/shipping_carriers/index.js.map +7 -0
  143. package/dist/modules/shipping_carriers/lib/adapter-registry.js +29 -0
  144. package/dist/modules/shipping_carriers/lib/adapter-registry.js.map +7 -0
  145. package/dist/modules/shipping_carriers/lib/adapter.js +1 -0
  146. package/dist/modules/shipping_carriers/lib/adapter.js.map +7 -0
  147. package/dist/modules/shipping_carriers/lib/queue.js +17 -0
  148. package/dist/modules/shipping_carriers/lib/queue.js.map +7 -0
  149. package/dist/modules/shipping_carriers/lib/shipping-service.js +155 -0
  150. package/dist/modules/shipping_carriers/lib/shipping-service.js.map +7 -0
  151. package/dist/modules/shipping_carriers/lib/status-sync.js +37 -0
  152. package/dist/modules/shipping_carriers/lib/status-sync.js.map +7 -0
  153. package/dist/modules/shipping_carriers/migrations/Migration20260305170000.js +16 -0
  154. package/dist/modules/shipping_carriers/migrations/Migration20260305170000.js.map +7 -0
  155. package/dist/modules/shipping_carriers/setup.js +13 -0
  156. package/dist/modules/shipping_carriers/setup.js.map +7 -0
  157. package/dist/modules/shipping_carriers/widgets/injection/create-shipment-button/widget.js +25 -0
  158. package/dist/modules/shipping_carriers/widgets/injection/create-shipment-button/widget.js.map +7 -0
  159. package/dist/modules/shipping_carriers/widgets/injection/tracking-column/widget.js +23 -0
  160. package/dist/modules/shipping_carriers/widgets/injection/tracking-column/widget.js.map +7 -0
  161. package/dist/modules/shipping_carriers/widgets/injection/tracking-status-badge/widget.js +40 -0
  162. package/dist/modules/shipping_carriers/widgets/injection/tracking-status-badge/widget.js.map +7 -0
  163. package/dist/modules/shipping_carriers/widgets/injection-table.js +24 -0
  164. package/dist/modules/shipping_carriers/widgets/injection-table.js.map +7 -0
  165. package/dist/modules/shipping_carriers/workers/status-poller.js +21 -0
  166. package/dist/modules/shipping_carriers/workers/status-poller.js.map +7 -0
  167. package/dist/modules/shipping_carriers/workers/webhook-processor.js +54 -0
  168. package/dist/modules/shipping_carriers/workers/webhook-processor.js.map +7 -0
  169. package/dist/modules/translations/api/get/locales.js +1 -0
  170. package/dist/modules/translations/api/get/locales.js.map +2 -2
  171. package/dist/modules/translations/api/put/locales.js +1 -0
  172. package/dist/modules/translations/api/put/locales.js.map +2 -2
  173. package/generated/entities/carrier_shipment/index.ts +17 -0
  174. package/generated/entities/gateway_transaction/index.ts +22 -0
  175. package/generated/entities/webhook_processed_event/index.ts +7 -0
  176. package/generated/entities.ids.generated.ts +10 -1
  177. package/generated/entity-fields-registry.ts +6 -0
  178. package/jest.config.cjs +1 -0
  179. package/package.json +5 -2
  180. package/src/modules/auth/i18n/de.json +1 -0
  181. package/src/modules/auth/i18n/en.json +1 -0
  182. package/src/modules/auth/i18n/es.json +1 -0
  183. package/src/modules/auth/i18n/pl.json +1 -0
  184. package/src/modules/data_sync/api/runs/[id]/cancel.ts +18 -5
  185. package/src/modules/data_sync/backend/data-sync/page.meta.ts +2 -2
  186. package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +50 -12
  187. package/src/modules/directory/api/get/tenants/lookup.ts +1 -0
  188. package/src/modules/integrations/AGENTS.md +31 -0
  189. package/src/modules/integrations/api/[id]/route.ts +38 -11
  190. package/src/modules/integrations/api/logs/route.ts +53 -27
  191. package/src/modules/integrations/api/route.ts +31 -1
  192. package/src/modules/integrations/api/umes-read.ts +177 -0
  193. package/src/modules/integrations/backend/integrations/[id]/page.tsx +902 -202
  194. package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +43 -9
  195. package/src/modules/integrations/backend/integrations/detail-page-widgets.ts +74 -0
  196. package/src/modules/integrations/backend/integrations/page.meta.ts +2 -2
  197. package/src/modules/integrations/backend/integrations/page.tsx +65 -54
  198. package/src/modules/integrations/i18n/de.json +15 -0
  199. package/src/modules/integrations/i18n/en.json +15 -0
  200. package/src/modules/integrations/i18n/es.json +15 -0
  201. package/src/modules/integrations/i18n/pl.json +15 -0
  202. package/src/modules/integrations/setup.ts +2 -2
  203. package/src/modules/payment_gateways/acl.ts +8 -0
  204. package/src/modules/payment_gateways/api/cancel/route.ts +56 -0
  205. package/src/modules/payment_gateways/api/capture/route.ts +56 -0
  206. package/src/modules/payment_gateways/api/interceptors.ts +22 -0
  207. package/src/modules/payment_gateways/api/openapi.ts +1 -0
  208. package/src/modules/payment_gateways/api/refund/route.ts +57 -0
  209. package/src/modules/payment_gateways/api/sessions/route.ts +76 -0
  210. package/src/modules/payment_gateways/api/status/route.ts +69 -0
  211. package/src/modules/payment_gateways/api/transactions/[id]/route.ts +123 -0
  212. package/src/modules/payment_gateways/api/transactions/route.ts +120 -0
  213. package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +161 -0
  214. package/src/modules/payment_gateways/backend/payment-gateways/page.meta.ts +19 -0
  215. package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +660 -0
  216. package/src/modules/payment_gateways/data/enrichers.ts +8 -0
  217. package/src/modules/payment_gateways/data/entities.ts +106 -0
  218. package/src/modules/payment_gateways/data/validators.ts +67 -0
  219. package/src/modules/payment_gateways/di.ts +26 -0
  220. package/src/modules/payment_gateways/events.ts +17 -0
  221. package/src/modules/payment_gateways/i18n/de.json +77 -0
  222. package/src/modules/payment_gateways/i18n/en.json +77 -0
  223. package/src/modules/payment_gateways/i18n/en.ts +4 -0
  224. package/src/modules/payment_gateways/i18n/es.json +77 -0
  225. package/src/modules/payment_gateways/i18n/pl.json +77 -0
  226. package/src/modules/payment_gateways/i18n/pl.ts +4 -0
  227. package/src/modules/payment_gateways/index.ts +5 -0
  228. package/src/modules/payment_gateways/lib/gateway-service.ts +486 -0
  229. package/src/modules/payment_gateways/lib/queue.ts +19 -0
  230. package/src/modules/payment_gateways/lib/status-machine.ts +28 -0
  231. package/src/modules/payment_gateways/lib/webhook-processor.ts +133 -0
  232. package/src/modules/payment_gateways/lib/webhook-utils.ts +52 -0
  233. package/src/modules/payment_gateways/migrations/.snapshot-open-mercato.json +373 -0
  234. package/src/modules/payment_gateways/migrations/Migration20260305122155.ts +20 -0
  235. package/src/modules/payment_gateways/setup.ts +11 -0
  236. package/src/modules/payment_gateways/widgets/injection-table.ts +9 -0
  237. package/src/modules/payment_gateways/workers/status-poller.ts +58 -0
  238. package/src/modules/payment_gateways/workers/webhook-processor.ts +30 -0
  239. package/src/modules/sales/data/enrichers.ts +120 -0
  240. package/src/modules/sales/lib/makeSalesLineRoute.ts +3 -0
  241. package/src/modules/sales/widgets/injection/payment-gateway-config-field/widget.ts +28 -0
  242. package/src/modules/sales/widgets/injection/payment-gateway-status-column/widget.ts +22 -0
  243. package/src/modules/sales/widgets/injection-table.ts +12 -0
  244. package/src/modules/shipping_carriers/acl.ts +6 -0
  245. package/src/modules/shipping_carriers/api/cancel/route.ts +53 -0
  246. package/src/modules/shipping_carriers/api/interceptors.ts +19 -0
  247. package/src/modules/shipping_carriers/api/openapi.ts +1 -0
  248. package/src/modules/shipping_carriers/api/rates/route.ts +53 -0
  249. package/src/modules/shipping_carriers/api/shipments/route.ts +59 -0
  250. package/src/modules/shipping_carriers/api/tracking/route.ts +56 -0
  251. package/src/modules/shipping_carriers/api/webhook/[provider]/route.ts +134 -0
  252. package/src/modules/shipping_carriers/data/enrichers.ts +89 -0
  253. package/src/modules/shipping_carriers/data/entities.ts +60 -0
  254. package/src/modules/shipping_carriers/data/validators.ts +48 -0
  255. package/src/modules/shipping_carriers/di.ts +20 -0
  256. package/src/modules/shipping_carriers/events.ts +16 -0
  257. package/src/modules/shipping_carriers/i18n/de.json +7 -0
  258. package/src/modules/shipping_carriers/i18n/en.json +7 -0
  259. package/src/modules/shipping_carriers/i18n/en.ts +7 -0
  260. package/src/modules/shipping_carriers/i18n/es.json +7 -0
  261. package/src/modules/shipping_carriers/i18n/pl.json +7 -0
  262. package/src/modules/shipping_carriers/i18n/pl.ts +7 -0
  263. package/src/modules/shipping_carriers/index.ts +5 -0
  264. package/src/modules/shipping_carriers/lib/adapter-registry.ts +33 -0
  265. package/src/modules/shipping_carriers/lib/adapter.ts +93 -0
  266. package/src/modules/shipping_carriers/lib/queue.ts +19 -0
  267. package/src/modules/shipping_carriers/lib/shipping-service.ts +204 -0
  268. package/src/modules/shipping_carriers/lib/status-sync.ts +38 -0
  269. package/src/modules/shipping_carriers/migrations/Migration20260305170000.ts +14 -0
  270. package/src/modules/shipping_carriers/setup.ts +11 -0
  271. package/src/modules/shipping_carriers/widgets/injection/create-shipment-button/widget.ts +24 -0
  272. package/src/modules/shipping_carriers/widgets/injection/tracking-column/widget.ts +22 -0
  273. package/src/modules/shipping_carriers/widgets/injection/tracking-status-badge/widget.tsx +44 -0
  274. package/src/modules/shipping_carriers/widgets/injection-table.ts +22 -0
  275. package/src/modules/shipping_carriers/workers/status-poller.ts +33 -0
  276. package/src/modules/shipping_carriers/workers/webhook-processor.ts +79 -0
  277. package/src/modules/translations/api/get/locales.ts +1 -0
  278. package/src/modules/translations/api/put/locales.ts +1 -0
@@ -0,0 +1,486 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+ import { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
3
+ import {
4
+ getGatewayAdapter,
5
+ type CreateSessionInput,
6
+ type CreateSessionResult,
7
+ type CaptureResult,
8
+ type RefundResult,
9
+ type CancelResult,
10
+ type GatewayPaymentStatus,
11
+ type UnifiedPaymentStatus,
12
+ } from '@open-mercato/shared/modules/payment_gateways/types'
13
+ import type { CredentialsService } from '../../integrations/lib/credentials-service'
14
+ import type { IntegrationStateService } from '../../integrations/lib/state-service'
15
+ import type { IntegrationLogService } from '../../integrations/lib/log-service'
16
+ import { GatewayTransaction } from '../data/entities'
17
+ import { isValidTransition } from './status-machine'
18
+ import { emitPaymentGatewayEvent } from '../events'
19
+
20
+ export interface PaymentGatewayServiceDeps {
21
+ em: EntityManager
22
+ integrationCredentialsService: CredentialsService
23
+ integrationStateService?: IntegrationStateService
24
+ integrationLogService?: IntegrationLogService
25
+ }
26
+
27
+ export interface CreatePaymentSessionInput {
28
+ providerKey: string
29
+ paymentId: string
30
+ orderId?: string
31
+ amount: number
32
+ currencyCode: string
33
+ captureMethod?: 'automatic' | 'manual'
34
+ description?: string
35
+ successUrl?: string
36
+ cancelUrl?: string
37
+ metadata?: Record<string, unknown>
38
+ organizationId: string
39
+ tenantId: string
40
+ }
41
+
42
+ export function createPaymentGatewayService(deps: PaymentGatewayServiceDeps) {
43
+ const { em, integrationCredentialsService, integrationLogService } = deps
44
+
45
+ async function findTransactionOrThrow(
46
+ transactionId: string,
47
+ scope: { organizationId: string; tenantId: string },
48
+ ): Promise<GatewayTransaction> {
49
+ const transaction = await findOneWithDecryption(
50
+ em,
51
+ GatewayTransaction,
52
+ {
53
+ id: transactionId,
54
+ organizationId: scope.organizationId,
55
+ tenantId: scope.tenantId,
56
+ deletedAt: null,
57
+ },
58
+ undefined,
59
+ scope,
60
+ )
61
+ if (!transaction) {
62
+ throw new Error('Transaction not found')
63
+ }
64
+ return transaction
65
+ }
66
+
67
+ function readProviderSessionId(transaction: GatewayTransaction): string {
68
+ if (typeof transaction.providerSessionId === 'string' && transaction.providerSessionId.trim().length > 0) {
69
+ return transaction.providerSessionId
70
+ }
71
+ throw new Error('Transaction is missing provider session id')
72
+ }
73
+
74
+ async function emitStatusEvent(status: UnifiedPaymentStatus, payload: Record<string, unknown>) {
75
+ type PaymentGatewayEventId = Parameters<typeof emitPaymentGatewayEvent>[0]
76
+ const eventMap: Partial<Record<UnifiedPaymentStatus, PaymentGatewayEventId>> = {
77
+ authorized: 'payment_gateways.payment.authorized',
78
+ captured: 'payment_gateways.payment.captured',
79
+ failed: 'payment_gateways.payment.failed',
80
+ refunded: 'payment_gateways.payment.refunded',
81
+ cancelled: 'payment_gateways.payment.cancelled',
82
+ }
83
+ const eventId = eventMap[status]
84
+ if (!eventId) return
85
+ await emitPaymentGatewayEvent(eventId, payload)
86
+ }
87
+
88
+ async function writeTransactionLog(
89
+ providerKey: string,
90
+ scope: { organizationId: string; tenantId: string },
91
+ transactionId: string,
92
+ level: 'info' | 'warn' | 'error',
93
+ message: string,
94
+ payload?: Record<string, unknown> | null,
95
+ code?: string | null,
96
+ ) {
97
+ if (!integrationLogService) return
98
+ await integrationLogService.write({
99
+ integrationId: `gateway_${providerKey}`,
100
+ scopeEntityType: 'payment_transaction',
101
+ scopeEntityId: transactionId,
102
+ level,
103
+ message,
104
+ code,
105
+ payload: payload ?? null,
106
+ }, scope)
107
+ }
108
+
109
+ async function resolveAdapterAndCredentials(providerKey: string, scope: { organizationId: string; tenantId: string }) {
110
+ const integrationId = `gateway_${providerKey}`
111
+ const selectedVersion = deps.integrationStateService
112
+ ? await deps.integrationStateService.resolveApiVersion(integrationId, scope)
113
+ : undefined
114
+ const adapter = getGatewayAdapter(providerKey, selectedVersion)
115
+ if (!adapter) {
116
+ throw new Error(
117
+ selectedVersion
118
+ ? `No gateway adapter registered for provider: ${providerKey} (version: ${selectedVersion})`
119
+ : `No gateway adapter registered for provider: ${providerKey}`,
120
+ )
121
+ }
122
+ const credentials = await integrationCredentialsService.resolve(integrationId, scope) ?? {}
123
+
124
+ return { adapter, credentials }
125
+ }
126
+
127
+ return {
128
+ async createPaymentSession(input: CreatePaymentSessionInput): Promise<{ transaction: GatewayTransaction; session: CreateSessionResult }> {
129
+ const scope = { organizationId: input.organizationId, tenantId: input.tenantId }
130
+ const { adapter, credentials } = await resolveAdapterAndCredentials(input.providerKey, scope)
131
+
132
+ const sessionInput: CreateSessionInput = {
133
+ paymentId: input.paymentId,
134
+ orderId: input.orderId,
135
+ tenantId: input.tenantId,
136
+ organizationId: input.organizationId,
137
+ amount: input.amount,
138
+ currencyCode: input.currencyCode,
139
+ captureMethod: input.captureMethod,
140
+ description: input.description,
141
+ successUrl: input.successUrl,
142
+ cancelUrl: input.cancelUrl,
143
+ metadata: input.metadata,
144
+ credentials,
145
+ }
146
+
147
+ const session = await adapter.createSession(sessionInput)
148
+
149
+ const transaction = em.create(GatewayTransaction, {
150
+ paymentId: input.paymentId,
151
+ providerKey: input.providerKey,
152
+ providerSessionId: session.sessionId,
153
+ unifiedStatus: session.status,
154
+ redirectUrl: session.redirectUrl ?? null,
155
+ clientSecret: null,
156
+ amount: String(input.amount),
157
+ currencyCode: input.currencyCode,
158
+ gatewayMetadata: session.providerData ?? null,
159
+ organizationId: input.organizationId,
160
+ tenantId: input.tenantId,
161
+ })
162
+ await em.persistAndFlush(transaction)
163
+ await emitPaymentGatewayEvent('payment_gateways.session.created', {
164
+ transactionId: transaction.id,
165
+ paymentId: transaction.paymentId,
166
+ providerKey: transaction.providerKey,
167
+ status: transaction.unifiedStatus,
168
+ organizationId: transaction.organizationId,
169
+ tenantId: transaction.tenantId,
170
+ })
171
+ await writeTransactionLog(
172
+ transaction.providerKey,
173
+ scope,
174
+ transaction.id,
175
+ 'info',
176
+ 'Payment session created',
177
+ {
178
+ paymentId: transaction.paymentId,
179
+ providerSessionId: transaction.providerSessionId,
180
+ status: transaction.unifiedStatus,
181
+ amount: input.amount,
182
+ currencyCode: input.currencyCode,
183
+ },
184
+ )
185
+
186
+ return { transaction, session }
187
+ },
188
+
189
+ async capturePayment(transactionId: string, amount: number | undefined, scope: { organizationId: string; tenantId: string }): Promise<CaptureResult> {
190
+ const transaction = await findTransactionOrThrow(transactionId, scope)
191
+ const { adapter, credentials } = await resolveAdapterAndCredentials(
192
+ transaction.providerKey,
193
+ { organizationId: transaction.organizationId, tenantId: transaction.tenantId },
194
+ )
195
+
196
+ const result = await adapter.capture({
197
+ sessionId: readProviderSessionId(transaction),
198
+ amount,
199
+ credentials,
200
+ })
201
+
202
+ transaction.unifiedStatus = result.status
203
+ transaction.gatewayMetadata = { ...transaction.gatewayMetadata, captureResult: result.providerData }
204
+ await em.flush()
205
+ await emitStatusEvent(result.status, {
206
+ transactionId: transaction.id,
207
+ paymentId: transaction.paymentId,
208
+ providerKey: transaction.providerKey,
209
+ organizationId: transaction.organizationId,
210
+ tenantId: transaction.tenantId,
211
+ })
212
+ await writeTransactionLog(
213
+ transaction.providerKey,
214
+ { organizationId: transaction.organizationId, tenantId: transaction.tenantId },
215
+ transaction.id,
216
+ 'info',
217
+ 'Payment captured',
218
+ {
219
+ amount: amount ?? null,
220
+ status: result.status,
221
+ capturedAmount: result.capturedAmount,
222
+ },
223
+ )
224
+
225
+ return result
226
+ },
227
+
228
+ async refundPayment(
229
+ transactionId: string,
230
+ amount: number | undefined,
231
+ reason: string | undefined,
232
+ scope: { organizationId: string; tenantId: string },
233
+ ): Promise<RefundResult> {
234
+ const transaction = await findTransactionOrThrow(transactionId, scope)
235
+ const { adapter, credentials } = await resolveAdapterAndCredentials(
236
+ transaction.providerKey,
237
+ { organizationId: transaction.organizationId, tenantId: transaction.tenantId },
238
+ )
239
+
240
+ const result = await adapter.refund({
241
+ sessionId: readProviderSessionId(transaction),
242
+ amount,
243
+ reason,
244
+ credentials,
245
+ })
246
+
247
+ transaction.unifiedStatus = result.status
248
+ transaction.gatewayRefundId = result.refundId
249
+ transaction.gatewayMetadata = { ...transaction.gatewayMetadata, refundResult: result.providerData }
250
+ await em.flush()
251
+ await emitStatusEvent(result.status, {
252
+ transactionId: transaction.id,
253
+ paymentId: transaction.paymentId,
254
+ providerKey: transaction.providerKey,
255
+ organizationId: transaction.organizationId,
256
+ tenantId: transaction.tenantId,
257
+ })
258
+ await writeTransactionLog(
259
+ transaction.providerKey,
260
+ { organizationId: transaction.organizationId, tenantId: transaction.tenantId },
261
+ transaction.id,
262
+ 'info',
263
+ 'Payment refunded',
264
+ {
265
+ amount: amount ?? null,
266
+ reason: reason ?? null,
267
+ status: result.status,
268
+ refundId: result.refundId,
269
+ },
270
+ )
271
+
272
+ return result
273
+ },
274
+
275
+ async cancelPayment(
276
+ transactionId: string,
277
+ reason: string | undefined,
278
+ scope: { organizationId: string; tenantId: string },
279
+ ): Promise<CancelResult> {
280
+ const transaction = await findTransactionOrThrow(transactionId, scope)
281
+ const { adapter, credentials } = await resolveAdapterAndCredentials(
282
+ transaction.providerKey,
283
+ { organizationId: transaction.organizationId, tenantId: transaction.tenantId },
284
+ )
285
+
286
+ const result = await adapter.cancel({
287
+ sessionId: readProviderSessionId(transaction),
288
+ reason,
289
+ credentials,
290
+ })
291
+
292
+ transaction.unifiedStatus = result.status
293
+ await em.flush()
294
+ await emitStatusEvent(result.status, {
295
+ transactionId: transaction.id,
296
+ paymentId: transaction.paymentId,
297
+ providerKey: transaction.providerKey,
298
+ organizationId: transaction.organizationId,
299
+ tenantId: transaction.tenantId,
300
+ })
301
+ await writeTransactionLog(
302
+ transaction.providerKey,
303
+ { organizationId: transaction.organizationId, tenantId: transaction.tenantId },
304
+ transaction.id,
305
+ 'info',
306
+ 'Payment cancelled',
307
+ {
308
+ reason: reason ?? null,
309
+ status: result.status,
310
+ },
311
+ )
312
+
313
+ return result
314
+ },
315
+
316
+ async getPaymentStatus(transactionId: string, scope: { organizationId: string; tenantId: string }): Promise<GatewayPaymentStatus> {
317
+ const transaction = await findTransactionOrThrow(transactionId, scope)
318
+ const { adapter, credentials } = await resolveAdapterAndCredentials(
319
+ transaction.providerKey,
320
+ { organizationId: transaction.organizationId, tenantId: transaction.tenantId },
321
+ )
322
+
323
+ const status = await adapter.getStatus({
324
+ sessionId: readProviderSessionId(transaction),
325
+ credentials,
326
+ })
327
+
328
+ if (status.status !== transaction.unifiedStatus && isValidTransition(transaction.unifiedStatus as UnifiedPaymentStatus, status.status)) {
329
+ const previousStatus = transaction.unifiedStatus
330
+ transaction.unifiedStatus = status.status
331
+ transaction.gatewayStatus = status.status
332
+ transaction.gatewayMetadata = { ...transaction.gatewayMetadata, statusResult: status.providerData ?? null }
333
+ transaction.lastPolledAt = new Date()
334
+ await em.flush()
335
+ await emitStatusEvent(status.status, {
336
+ transactionId: transaction.id,
337
+ paymentId: transaction.paymentId,
338
+ providerKey: transaction.providerKey,
339
+ previousStatus,
340
+ organizationId: transaction.organizationId,
341
+ tenantId: transaction.tenantId,
342
+ })
343
+ await writeTransactionLog(
344
+ transaction.providerKey,
345
+ { organizationId: transaction.organizationId, tenantId: transaction.tenantId },
346
+ transaction.id,
347
+ 'info',
348
+ 'Payment status updated by poller',
349
+ {
350
+ previousStatus,
351
+ nextStatus: status.status,
352
+ },
353
+ )
354
+ }
355
+
356
+ return status
357
+ },
358
+
359
+ async syncTransactionStatus(transactionId: string, update: {
360
+ unifiedStatus: UnifiedPaymentStatus
361
+ providerStatus?: string
362
+ providerData?: Record<string, unknown>
363
+ webhookEvent?: {
364
+ eventType: string
365
+ idempotencyKey: string
366
+ processed: boolean
367
+ receivedAt?: string
368
+ }
369
+ }, scope: { organizationId: string; tenantId: string }): Promise<void> {
370
+ const transaction = await findTransactionOrThrow(transactionId, scope)
371
+ const currentStatus = transaction.unifiedStatus as UnifiedPaymentStatus
372
+ const canTransition = isValidTransition(currentStatus, update.unifiedStatus)
373
+ const shouldApplyStatus = canTransition && update.unifiedStatus !== currentStatus
374
+ const previousStatus = transaction.unifiedStatus
375
+ if (shouldApplyStatus) {
376
+ transaction.unifiedStatus = update.unifiedStatus
377
+ }
378
+ if (update.providerStatus) {
379
+ transaction.gatewayStatus = update.providerStatus
380
+ }
381
+ if (update.providerData) {
382
+ transaction.gatewayMetadata = { ...transaction.gatewayMetadata, ...update.providerData }
383
+ }
384
+ if (update.webhookEvent) {
385
+ const webhookLog = Array.isArray(transaction.webhookLog) ? transaction.webhookLog : []
386
+ webhookLog.push({
387
+ eventType: update.webhookEvent.eventType,
388
+ receivedAt: update.webhookEvent.receivedAt ?? new Date().toISOString(),
389
+ idempotencyKey: update.webhookEvent.idempotencyKey,
390
+ unifiedStatus: update.unifiedStatus,
391
+ processed: update.webhookEvent.processed,
392
+ })
393
+ transaction.webhookLog = webhookLog
394
+ }
395
+ transaction.lastWebhookAt = new Date()
396
+ await em.flush()
397
+ if (shouldApplyStatus) {
398
+ await emitStatusEvent(update.unifiedStatus, {
399
+ transactionId: transaction.id,
400
+ paymentId: transaction.paymentId,
401
+ providerKey: transaction.providerKey,
402
+ previousStatus,
403
+ organizationId: transaction.organizationId,
404
+ tenantId: transaction.tenantId,
405
+ })
406
+ }
407
+ await writeTransactionLog(
408
+ transaction.providerKey,
409
+ { organizationId: transaction.organizationId, tenantId: transaction.tenantId },
410
+ transaction.id,
411
+ shouldApplyStatus ? 'info' : 'warn',
412
+ shouldApplyStatus ? 'Payment status synchronized from webhook' : 'Webhook received with no status transition',
413
+ {
414
+ previousStatus,
415
+ nextStatus: update.unifiedStatus,
416
+ providerStatus: update.providerStatus ?? null,
417
+ eventType: update.webhookEvent?.eventType ?? null,
418
+ idempotencyKey: update.webhookEvent?.idempotencyKey ?? null,
419
+ },
420
+ )
421
+ },
422
+
423
+ async findTransaction(id: string, scope: { organizationId: string; tenantId: string }): Promise<GatewayTransaction | null> {
424
+ return findOneWithDecryption(
425
+ em,
426
+ GatewayTransaction,
427
+ {
428
+ id,
429
+ organizationId: scope.organizationId,
430
+ tenantId: scope.tenantId,
431
+ deletedAt: null,
432
+ },
433
+ undefined,
434
+ scope,
435
+ )
436
+ },
437
+
438
+ async findTransactionBySessionId(
439
+ providerSessionId: string,
440
+ scope: { organizationId: string; tenantId: string },
441
+ providerKey?: string,
442
+ ): Promise<GatewayTransaction | null> {
443
+ return findOneWithDecryption(
444
+ em,
445
+ GatewayTransaction,
446
+ {
447
+ providerSessionId,
448
+ organizationId: scope.organizationId,
449
+ tenantId: scope.tenantId,
450
+ deletedAt: null,
451
+ ...(providerKey ? { providerKey } : {}),
452
+ },
453
+ undefined,
454
+ scope,
455
+ )
456
+ },
457
+
458
+ async listTransactionsForStatusPolling(scope?: {
459
+ organizationId?: string
460
+ tenantId?: string
461
+ providerKey?: string
462
+ limit?: number
463
+ }): Promise<GatewayTransaction[]> {
464
+ const where: Record<string, unknown> = {
465
+ unifiedStatus: { $in: ['pending', 'authorized', 'partially_captured'] },
466
+ deletedAt: null,
467
+ }
468
+ if (scope?.organizationId) where.organizationId = scope.organizationId
469
+ if (scope?.tenantId) where.tenantId = scope.tenantId
470
+ if (scope?.providerKey) where.providerKey = scope.providerKey
471
+
472
+ return findWithDecryption(
473
+ em,
474
+ GatewayTransaction,
475
+ where,
476
+ {
477
+ orderBy: { updatedAt: 'asc' },
478
+ limit: scope?.limit ?? 100,
479
+ },
480
+ scope,
481
+ )
482
+ },
483
+ }
484
+ }
485
+
486
+ export type PaymentGatewayService = ReturnType<typeof createPaymentGatewayService>
@@ -0,0 +1,19 @@
1
+ import { createQueue, type Queue } from '@open-mercato/queue'
2
+ import { getRedisUrl } from '@open-mercato/shared/lib/redis/connection'
3
+
4
+ const queues = new Map<string, Queue<Record<string, unknown>>>()
5
+
6
+ export function getPaymentGatewayQueue(queueName: string): Queue<Record<string, unknown>> {
7
+ const existing = queues.get(queueName)
8
+ if (existing) return existing
9
+
10
+ const created = process.env.QUEUE_STRATEGY === 'async'
11
+ ? createQueue<Record<string, unknown>>(queueName, 'async', {
12
+ connection: { url: getRedisUrl('QUEUE') },
13
+ concurrency: Math.max(1, Number.parseInt(process.env.PAYMENT_GATEWAY_QUEUE_CONCURRENCY ?? '5', 10) || 5),
14
+ })
15
+ : createQueue<Record<string, unknown>>(queueName, 'local')
16
+
17
+ queues.set(queueName, created)
18
+ return created
19
+ }
@@ -0,0 +1,28 @@
1
+ import type { UnifiedPaymentStatus } from '@open-mercato/shared/modules/payment_gateways/types'
2
+
3
+ const VALID_TRANSITIONS: Record<string, UnifiedPaymentStatus[]> = {
4
+ pending: ['authorized', 'captured', 'failed', 'expired', 'cancelled'],
5
+ authorized: ['captured', 'partially_captured', 'cancelled', 'failed'],
6
+ captured: ['refunded', 'partially_refunded'],
7
+ partially_captured: ['captured', 'refunded', 'partially_refunded', 'cancelled'],
8
+ partially_refunded: ['refunded'],
9
+ // Terminal states: refunded, cancelled, failed, expired — no valid transitions out
10
+ }
11
+
12
+ export const TERMINAL_STATUSES: Set<UnifiedPaymentStatus> = new Set([
13
+ 'refunded',
14
+ 'cancelled',
15
+ 'failed',
16
+ 'expired',
17
+ ])
18
+
19
+ export function isValidTransition(from: UnifiedPaymentStatus, to: UnifiedPaymentStatus): boolean {
20
+ if (from === to) return false
21
+ const allowed = VALID_TRANSITIONS[from]
22
+ if (!allowed) return false
23
+ return allowed.includes(to)
24
+ }
25
+
26
+ export function isTerminalStatus(status: UnifiedPaymentStatus): boolean {
27
+ return TERMINAL_STATUSES.has(status)
28
+ }
@@ -0,0 +1,133 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+ import { getGatewayAdapter, type WebhookEvent } from '@open-mercato/shared/modules/payment_gateways/types'
3
+ import type { IntegrationLogService } from '../../integrations/lib/log-service'
4
+ import type { PaymentGatewayService } from './gateway-service'
5
+ import { claimWebhookProcessing, releaseWebhookClaim } from './webhook-utils'
6
+
7
+ export type PaymentGatewayWebhookJobPayload = {
8
+ providerKey: string
9
+ event: WebhookEvent
10
+ transactionId?: string | null
11
+ scope?: {
12
+ organizationId: string
13
+ tenantId: string
14
+ } | null
15
+ }
16
+
17
+ type PaymentGatewayWebhookProcessorDeps = {
18
+ em: EntityManager
19
+ paymentGatewayService: PaymentGatewayService
20
+ integrationLogService: IntegrationLogService
21
+ }
22
+
23
+ function readSessionIdFromEvent(event: WebhookEvent): string | null {
24
+ const id = event.data.id
25
+ if (typeof id === 'string' && id.trim().length > 0) return id.trim()
26
+ const paymentIntent = event.data.payment_intent
27
+ if (typeof paymentIntent === 'string' && paymentIntent.trim().length > 0) return paymentIntent.trim()
28
+ return null
29
+ }
30
+
31
+ function readScopeFromEvent(event: WebhookEvent): { organizationId: string; tenantId: string } | null {
32
+ const metadata = event.data.metadata
33
+ if (!metadata || typeof metadata !== 'object') return null
34
+
35
+ const metadataRecord = metadata as Record<string, unknown>
36
+ const organizationId = typeof metadataRecord.organizationId === 'string'
37
+ ? metadataRecord.organizationId.trim()
38
+ : ''
39
+ const tenantId = typeof metadataRecord.tenantId === 'string'
40
+ ? metadataRecord.tenantId.trim()
41
+ : ''
42
+
43
+ if (!organizationId || !tenantId) return null
44
+ return { organizationId, tenantId }
45
+ }
46
+
47
+ async function writeTransactionLog(
48
+ integrationLogService: IntegrationLogService,
49
+ providerKey: string,
50
+ scope: { organizationId: string; tenantId: string },
51
+ transactionId: string,
52
+ level: 'info' | 'warn' | 'error',
53
+ message: string,
54
+ payload?: Record<string, unknown>,
55
+ ) {
56
+ await integrationLogService.write({
57
+ integrationId: `gateway_${providerKey}`,
58
+ scopeEntityType: 'payment_transaction',
59
+ scopeEntityId: transactionId,
60
+ level,
61
+ message,
62
+ payload: payload ?? null,
63
+ }, scope)
64
+ }
65
+
66
+ export async function processPaymentGatewayWebhookJob(
67
+ deps: PaymentGatewayWebhookProcessorDeps,
68
+ payload: PaymentGatewayWebhookJobPayload,
69
+ ): Promise<void> {
70
+ const { em, paymentGatewayService, integrationLogService } = deps
71
+ const { providerKey, event } = payload
72
+ const scopedPayload = payload.scope ?? readScopeFromEvent(event)
73
+
74
+ let transaction = payload.transactionId && scopedPayload
75
+ ? await paymentGatewayService.findTransaction(payload.transactionId, scopedPayload)
76
+ : null
77
+
78
+ if (!transaction) {
79
+ const sessionId = readSessionIdFromEvent(event)
80
+ if (sessionId && scopedPayload) {
81
+ transaction = await paymentGatewayService.findTransactionBySessionId(sessionId, scopedPayload, providerKey)
82
+ }
83
+ }
84
+ if (!transaction) return
85
+
86
+ const scope = { organizationId: transaction.organizationId, tenantId: transaction.tenantId }
87
+ const claimed = await claimWebhookProcessing(em, event.idempotencyKey, providerKey, scope, event.eventType)
88
+ if (!claimed) {
89
+ await writeTransactionLog(integrationLogService, providerKey, scope, transaction.id, 'info', 'Duplicate payment gateway webhook skipped', {
90
+ eventType: event.eventType,
91
+ idempotencyKey: event.idempotencyKey,
92
+ })
93
+ return
94
+ }
95
+
96
+ try {
97
+ const adapter = getGatewayAdapter(providerKey)
98
+ if (!adapter) {
99
+ await writeTransactionLog(integrationLogService, providerKey, scope, transaction.id, 'warn', 'Missing payment gateway adapter for webhook event', {
100
+ providerKey,
101
+ eventType: event.eventType,
102
+ })
103
+ return
104
+ }
105
+
106
+ const providerStatus = typeof event.data.status === 'string' ? event.data.status : ''
107
+ const unifiedStatus = adapter.mapStatus(providerStatus, event.eventType)
108
+ await writeTransactionLog(integrationLogService, providerKey, scope, transaction.id, 'info', 'Payment gateway webhook received', {
109
+ eventType: event.eventType,
110
+ providerStatus,
111
+ unifiedStatus,
112
+ })
113
+
114
+ await paymentGatewayService.syncTransactionStatus(transaction.id, {
115
+ unifiedStatus,
116
+ providerStatus: event.eventType,
117
+ providerData: event.data,
118
+ webhookEvent: {
119
+ eventType: event.eventType,
120
+ idempotencyKey: event.idempotencyKey,
121
+ processed: true,
122
+ },
123
+ }, scope)
124
+
125
+ await writeTransactionLog(integrationLogService, providerKey, scope, transaction.id, 'info', 'Payment gateway webhook processed', {
126
+ eventType: event.eventType,
127
+ unifiedStatus,
128
+ })
129
+ } catch (error: unknown) {
130
+ await releaseWebhookClaim(em, event.idempotencyKey, providerKey, scope)
131
+ throw error
132
+ }
133
+ }