@openlup/platform 0.20.0 → 0.22.0

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 (458) hide show
  1. package/CHANGELOG.md +272 -278
  2. package/README.md +195 -6
  3. package/dist/composition/composeBundle.d.ts +13 -0
  4. package/dist/composition/composeBundle.js +4 -0
  5. package/dist/composition/fulfillmentContribution.d.ts +21 -0
  6. package/dist/composition/fulfillmentContribution.js +19 -0
  7. package/dist/composition/index.d.ts +1 -0
  8. package/dist/composition/index.js +2 -0
  9. package/dist/fulfillment/adapterConformance.d.ts +3 -0
  10. package/dist/fulfillment/adapterConformance.js +223 -0
  11. package/dist/fulfillment/bindings.d.ts +3 -0
  12. package/dist/fulfillment/bindings.js +29 -0
  13. package/dist/fulfillment/commerceFulfillmentHandlers.d.ts +29 -0
  14. package/dist/fulfillment/commerceFulfillmentHandlers.js +184 -0
  15. package/dist/fulfillment/commerceFulfillmentReadModel.d.ts +86 -0
  16. package/dist/fulfillment/commerceFulfillmentReadModel.js +131 -0
  17. package/dist/fulfillment/contracts/alignment.d.ts +25 -0
  18. package/dist/fulfillment/contracts/alignment.js +1 -0
  19. package/dist/fulfillment/contracts/bindings.d.ts +51 -0
  20. package/dist/fulfillment/contracts/bindings.js +1 -0
  21. package/dist/fulfillment/contracts/capabilities.d.ts +29 -0
  22. package/dist/fulfillment/contracts/capabilities.js +1 -0
  23. package/dist/fulfillment/contracts/conformance.d.ts +93 -0
  24. package/dist/fulfillment/contracts/conformance.js +1 -0
  25. package/dist/fulfillment/contracts/customerAlignment.d.ts +6 -0
  26. package/dist/fulfillment/contracts/customerAlignment.js +1 -0
  27. package/dist/fulfillment/contracts/dispatch.d.ts +105 -0
  28. package/dist/fulfillment/contracts/dispatch.js +1 -0
  29. package/dist/fulfillment/contracts/index.d.ts +18 -0
  30. package/dist/fulfillment/contracts/index.js +1 -0
  31. package/dist/fulfillment/contracts/label.d.ts +35 -0
  32. package/dist/fulfillment/contracts/label.js +1 -0
  33. package/dist/fulfillment/contracts/nativeUnsupported.d.ts +6 -0
  34. package/dist/fulfillment/contracts/nativeUnsupported.js +1 -0
  35. package/dist/fulfillment/contracts/observation.d.ts +52 -0
  36. package/dist/fulfillment/contracts/observation.js +1 -0
  37. package/dist/fulfillment/contracts/operator.d.ts +64 -0
  38. package/dist/fulfillment/contracts/operator.js +1 -0
  39. package/dist/fulfillment/contracts/read.d.ts +110 -0
  40. package/dist/fulfillment/contracts/read.js +1 -0
  41. package/dist/fulfillment/contracts/readback.d.ts +38 -0
  42. package/dist/fulfillment/contracts/readback.js +1 -0
  43. package/dist/fulfillment/contracts/replacement.d.ts +34 -0
  44. package/dist/fulfillment/contracts/replacement.js +1 -0
  45. package/dist/fulfillment/contracts/store.d.ts +86 -0
  46. package/dist/fulfillment/contracts/store.js +1 -0
  47. package/dist/fulfillment/contracts/sync.d.ts +170 -0
  48. package/dist/fulfillment/contracts/sync.js +1 -0
  49. package/dist/fulfillment/currentFulfillmentParcel.d.ts +2 -0
  50. package/dist/fulfillment/currentFulfillmentParcel.js +1 -0
  51. package/dist/fulfillment/delayReadModel.d.ts +11 -0
  52. package/dist/fulfillment/delayReadModel.js +45 -0
  53. package/dist/fulfillment/deliveryAlignmentReadModel.d.ts +4 -0
  54. package/dist/fulfillment/deliveryAlignmentReadModel.js +12 -0
  55. package/dist/fulfillment/dispatchProjection.d.ts +7 -0
  56. package/dist/fulfillment/dispatchProjection.js +18 -0
  57. package/dist/fulfillment/fulfillmentObservationEffects.d.ts +66 -0
  58. package/dist/fulfillment/fulfillmentObservationEffects.js +36 -0
  59. package/dist/fulfillment/handlerSchemas.d.ts +647 -0
  60. package/dist/fulfillment/handlerSchemas.js +213 -0
  61. package/dist/fulfillment/handlerSupport.d.ts +33 -0
  62. package/dist/fulfillment/handlerSupport.js +25 -0
  63. package/dist/fulfillment/index.d.ts +37 -0
  64. package/dist/fulfillment/index.js +37 -0
  65. package/dist/fulfillment/labelLifecycle.d.ts +24 -0
  66. package/dist/fulfillment/labelLifecycle.js +9 -0
  67. package/dist/fulfillment/lowStockEvidenceHandler.d.ts +36 -0
  68. package/dist/fulfillment/lowStockEvidenceHandler.js +46 -0
  69. package/dist/fulfillment/migrations.d.ts +27 -0
  70. package/dist/fulfillment/migrations.js +8 -0
  71. package/dist/fulfillment/nativeUnsupported.d.ts +10 -0
  72. package/dist/fulfillment/nativeUnsupported.js +18 -0
  73. package/dist/fulfillment/normalizedFulfillmentFacts.d.ts +2 -0
  74. package/dist/fulfillment/normalizedFulfillmentFacts.js +1 -0
  75. package/dist/fulfillment/observationProjection.d.ts +11 -0
  76. package/dist/fulfillment/observationProjection.js +37 -0
  77. package/dist/fulfillment/omnipack/capabilities.d.ts +3 -0
  78. package/dist/fulfillment/omnipack/capabilities.js +16 -0
  79. package/dist/fulfillment/omnipack/client.d.ts +37 -0
  80. package/dist/fulfillment/omnipack/client.js +162 -0
  81. package/dist/fulfillment/omnipack/index.d.ts +52 -0
  82. package/dist/fulfillment/omnipack/index.js +349 -0
  83. package/dist/fulfillment/omnipack/mappers.d.ts +73 -0
  84. package/dist/fulfillment/omnipack/mappers.js +177 -0
  85. package/dist/fulfillment/omnipack/observation.d.ts +21 -0
  86. package/dist/fulfillment/omnipack/observation.js +57 -0
  87. package/dist/fulfillment/omnipack/omnipackDispatchPayload.d.ts +36 -0
  88. package/dist/fulfillment/omnipack/omnipackDispatchPayload.js +63 -0
  89. package/dist/fulfillment/omnipack/omnipackOutboundOrderPayload.d.ts +83 -0
  90. package/dist/fulfillment/omnipack/omnipackOutboundOrderPayload.js +113 -0
  91. package/dist/fulfillment/omnipack/omnipackProductPackPayload.d.ts +11 -0
  92. package/dist/fulfillment/omnipack/omnipackProductPackPayload.js +38 -0
  93. package/dist/fulfillment/omnipack/omnipackProductPayload.d.ts +18 -0
  94. package/dist/fulfillment/omnipack/omnipackProductPayload.js +51 -0
  95. package/dist/fulfillment/omnipack/omnipackReconciliationPayload.d.ts +1 -0
  96. package/dist/fulfillment/omnipack/omnipackReconciliationPayload.js +3 -0
  97. package/dist/fulfillment/omnipack/omnipackStatusVocabulary.d.ts +4 -0
  98. package/dist/fulfillment/omnipack/omnipackStatusVocabulary.js +87 -0
  99. package/dist/fulfillment/omnipack/omnipackWebhookPayload.d.ts +9 -0
  100. package/dist/fulfillment/omnipack/omnipackWebhookPayload.js +76 -0
  101. package/dist/fulfillment/omnipack/productSyncMappers.d.ts +18 -0
  102. package/dist/fulfillment/omnipack/productSyncMappers.js +43 -0
  103. package/dist/fulfillment/omnipack/providerError.d.ts +25 -0
  104. package/dist/fulfillment/omnipack/providerError.js +47 -0
  105. package/dist/fulfillment/omnipack/trackingReferences.d.ts +16 -0
  106. package/dist/fulfillment/omnipack/trackingReferences.js +52 -0
  107. package/dist/fulfillment/paidFulfillmentRecovery.d.ts +67 -0
  108. package/dist/fulfillment/paidFulfillmentRecovery.js +154 -0
  109. package/dist/fulfillment/paidHandoff.d.ts +27 -0
  110. package/dist/fulfillment/paidHandoff.js +86 -0
  111. package/dist/fulfillment/paidHandoffPorts.d.ts +24 -0
  112. package/dist/fulfillment/paidHandoffPorts.js +3 -0
  113. package/dist/fulfillment/postgresDeliveryAlignment.d.ts +4 -0
  114. package/dist/fulfillment/postgresDeliveryAlignment.js +12 -0
  115. package/dist/fulfillment/postgresDispatchStore.d.ts +16 -0
  116. package/dist/fulfillment/postgresDispatchStore.js +44 -0
  117. package/dist/fulfillment/postgresEvidenceReadPort.d.ts +25 -0
  118. package/dist/fulfillment/postgresEvidenceReadPort.js +45 -0
  119. package/dist/fulfillment/postgresPaidFulfillmentRecovery.d.ts +25 -0
  120. package/dist/fulfillment/postgresPaidFulfillmentRecovery.js +26 -0
  121. package/dist/fulfillment/postgresPaidHandoffStore.d.ts +4 -0
  122. package/dist/fulfillment/postgresPaidHandoffStore.js +10 -0
  123. package/dist/fulfillment/postgresShipmentSpine.d.ts +63 -0
  124. package/dist/fulfillment/postgresShipmentSpine.js +232 -0
  125. package/dist/fulfillment/postgresStockSyncPort.d.ts +8 -0
  126. package/dist/fulfillment/postgresStockSyncPort.js +63 -0
  127. package/dist/fulfillment/productReconciliationEvidenceHandler.d.ts +36 -0
  128. package/dist/fulfillment/productReconciliationEvidenceHandler.js +50 -0
  129. package/dist/fulfillment/providerCapabilities.d.ts +9 -0
  130. package/dist/fulfillment/providerCapabilities.js +17 -0
  131. package/dist/fulfillment/providerCommandLedger.d.ts +143 -0
  132. package/dist/fulfillment/providerCommandLedger.js +14 -0
  133. package/dist/fulfillment/providerDispatchAcceptance.d.ts +26 -0
  134. package/dist/fulfillment/providerDispatchAcceptance.js +23 -0
  135. package/dist/fulfillment/providerDispatchError.d.ts +6 -0
  136. package/dist/fulfillment/providerDispatchError.js +5 -0
  137. package/dist/fulfillment/providerDispatchWorker.d.ts +31 -0
  138. package/dist/fulfillment/providerDispatchWorker.js +172 -0
  139. package/dist/fulfillment/providerProductSyncWorker.d.ts +28 -0
  140. package/dist/fulfillment/providerProductSyncWorker.js +122 -0
  141. package/dist/fulfillment/providerReconciliationError.d.ts +9 -0
  142. package/dist/fulfillment/providerReconciliationError.js +17 -0
  143. package/dist/fulfillment/providerReconciliationTerminalSkip.d.ts +2 -0
  144. package/dist/fulfillment/providerReconciliationTerminalSkip.js +6 -0
  145. package/dist/fulfillment/providerReconciliationTimestamp.d.ts +1 -0
  146. package/dist/fulfillment/providerReconciliationTimestamp.js +27 -0
  147. package/dist/fulfillment/providerReconciliationWorker.d.ts +82 -0
  148. package/dist/fulfillment/providerReconciliationWorker.js +151 -0
  149. package/dist/fulfillment/providerStockAvailability.d.ts +13 -0
  150. package/dist/fulfillment/providerStockAvailability.js +51 -0
  151. package/dist/fulfillment/providerStockSyncWorker.d.ts +37 -0
  152. package/dist/fulfillment/providerStockSyncWorker.js +186 -0
  153. package/dist/fulfillment/providerTrackingReferences.d.ts +15 -0
  154. package/dist/fulfillment/providerTrackingReferences.js +23 -0
  155. package/dist/fulfillment/replacementRefusals.d.ts +5 -0
  156. package/dist/fulfillment/replacementRefusals.js +38 -0
  157. package/dist/fulfillment/replacementShipmentHandler.d.ts +5 -0
  158. package/dist/fulfillment/replacementShipmentHandler.js +46 -0
  159. package/dist/fulfillment/schemaCapabilities.d.ts +13 -0
  160. package/dist/fulfillment/schemaCapabilities.js +7 -0
  161. package/dist/fulfillment/shipmentsOverviewHandler.d.ts +16 -0
  162. package/dist/fulfillment/shipmentsOverviewHandler.js +54 -0
  163. package/dist/fulfillment/statusMap.d.ts +2 -0
  164. package/dist/fulfillment/statusMap.js +1 -0
  165. package/dist/fulfillment/trackingProgress.d.ts +1 -0
  166. package/dist/fulfillment/trackingProgress.js +1 -0
  167. package/dist/fulfillment/verifiedIngress.d.ts +52 -0
  168. package/dist/fulfillment/verifiedIngress.js +66 -0
  169. package/dist/payments/adapterConformance.d.ts +8 -0
  170. package/dist/payments/adapterConformance.js +109 -0
  171. package/dist/payments/bindings.d.ts +3 -0
  172. package/dist/payments/bindings.js +17 -0
  173. package/dist/payments/checkoutActivePaymentActionResolver.d.ts +7 -0
  174. package/dist/payments/checkoutActivePaymentActionResolver.js +77 -0
  175. package/dist/payments/checkoutRecoveryPaymentResolver.d.ts +8 -0
  176. package/dist/payments/checkoutRecoveryPaymentResolver.js +87 -0
  177. package/dist/payments/contracts/adapter.d.ts +113 -0
  178. package/dist/payments/contracts/adapter.js +1 -0
  179. package/dist/payments/contracts/conformance.d.ts +155 -0
  180. package/dist/payments/contracts/conformance.js +1 -0
  181. package/dist/payments/contracts/evidence.d.ts +157 -0
  182. package/dist/payments/contracts/evidence.js +1 -0
  183. package/dist/payments/contracts/index.d.ts +10 -0
  184. package/dist/payments/contracts/index.js +1 -0
  185. package/dist/payments/contracts/preparedAttempt.d.ts +69 -0
  186. package/dist/payments/contracts/preparedAttempt.js +1 -0
  187. package/dist/payments/contracts/reconciliation.d.ts +303 -0
  188. package/dist/payments/contracts/reconciliation.js +1 -0
  189. package/dist/payments/contracts/repair.d.ts +92 -0
  190. package/dist/payments/contracts/repair.js +1 -0
  191. package/dist/payments/contracts/truth.d.ts +92 -0
  192. package/dist/payments/contracts/truth.js +1 -0
  193. package/dist/payments/eventConformanceRunner.d.ts +12 -0
  194. package/dist/payments/eventConformanceRunner.js +125 -0
  195. package/dist/payments/index.d.ts +16 -0
  196. package/dist/payments/index.js +16 -0
  197. package/dist/payments/paymentAttemptSilenceWindow.d.ts +10 -0
  198. package/dist/payments/paymentAttemptSilenceWindow.js +19 -0
  199. package/dist/payments/paymentMethodLifecycle.d.ts +11 -0
  200. package/dist/payments/paymentMethodLifecycle.js +37 -0
  201. package/dist/payments/paymentProviderReconciliationAbsenceReopen.d.ts +7 -0
  202. package/dist/payments/paymentProviderReconciliationAbsenceReopen.js +100 -0
  203. package/dist/payments/paymentProviderReconciliationEvidence.d.ts +36 -0
  204. package/dist/payments/paymentProviderReconciliationEvidence.js +91 -0
  205. package/dist/payments/paymentProviderReconciliationWorker.d.ts +4 -0
  206. package/dist/payments/paymentProviderReconciliationWorker.js +245 -0
  207. package/dist/payments/paymentTruth.d.ts +8 -0
  208. package/dist/payments/paymentTruth.js +125 -0
  209. package/dist/payments/paymentVerifyNowHandler.d.ts +27 -0
  210. package/dist/payments/paymentVerifyNowHandler.js +70 -0
  211. package/dist/payments/paymentVerifyNowService.d.ts +23 -0
  212. package/dist/payments/paymentVerifyNowService.js +93 -0
  213. package/dist/payments/paymentWebhookPostProcessing.d.ts +22 -0
  214. package/dist/payments/paymentWebhookPostProcessing.js +15 -0
  215. package/dist/payments/postgresPaymentTruth.d.ts +10 -0
  216. package/dist/payments/postgresPaymentTruth.js +126 -0
  217. package/dist/payments/preparedAttempt.d.ts +6 -0
  218. package/dist/payments/preparedAttempt.js +70 -0
  219. package/dist/payments/preparedAttemptFinalization.d.ts +5 -0
  220. package/dist/payments/preparedAttemptFinalization.js +12 -0
  221. package/dist/payments/schemaCapabilities.d.ts +10 -0
  222. package/dist/payments/schemaCapabilities.js +10 -0
  223. package/dist/payments/sql.d.ts +20 -0
  224. package/dist/payments/sql.js +6 -0
  225. package/dist/payments/stripe/classifyDecline.d.ts +46 -0
  226. package/dist/payments/stripe/classifyDecline.js +58 -0
  227. package/dist/payments/stripe/declineFailureHints.d.ts +46 -0
  228. package/dist/payments/stripe/declineFailureHints.js +87 -0
  229. package/dist/payments/stripe/donorContracts.d.ts +120 -0
  230. package/dist/payments/stripe/donorContracts.js +1 -0
  231. package/dist/payments/stripe/executionDecline.d.ts +53 -0
  232. package/dist/payments/stripe/executionDecline.js +107 -0
  233. package/dist/payments/stripe/index.d.ts +41 -0
  234. package/dist/payments/stripe/index.js +264 -0
  235. package/dist/payments/stripe/intentMapper.d.ts +26 -0
  236. package/dist/payments/stripe/intentMapper.js +51 -0
  237. package/dist/payments/stripe/methodFacts.d.ts +10 -0
  238. package/dist/payments/stripe/methodFacts.js +12 -0
  239. package/dist/payments/stripe/paymentFailureEvidence.d.ts +26 -0
  240. package/dist/payments/stripe/paymentFailureEvidence.js +33 -0
  241. package/dist/payments/stripe/stripeFailureEvidence.d.ts +41 -0
  242. package/dist/payments/stripe/stripeFailureEvidence.js +82 -0
  243. package/dist/payments/stripe/stripeIntentConfirmationEvidence.d.ts +28 -0
  244. package/dist/payments/stripe/stripeIntentConfirmationEvidence.js +32 -0
  245. package/dist/payments/stripe/stripePaymentReconciliationProvider.d.ts +35 -0
  246. package/dist/payments/stripe/stripePaymentReconciliationProvider.js +222 -0
  247. package/dist/payments/stripe/stripeRefundEvidence.d.ts +8 -0
  248. package/dist/payments/stripe/stripeRefundEvidence.js +17 -0
  249. package/dist/payments/stripe/stripeSandboxPaymentExecutionAdapter.d.ts +45 -0
  250. package/dist/payments/stripe/stripeSandboxPaymentExecutionAdapter.js +220 -0
  251. package/dist/payments/stripe/stripeWebhookNormalizer.d.ts +14 -0
  252. package/dist/payments/stripe/stripeWebhookNormalizer.js +243 -0
  253. package/dist/payments/stripe/webhookSignature.d.ts +14 -0
  254. package/dist/payments/stripe/webhookSignature.js +43 -0
  255. package/dist/payments/tpay/blikRecurringMapper.d.ts +37 -0
  256. package/dist/payments/tpay/blikRecurringMapper.js +24 -0
  257. package/dist/payments/tpay/capabilities.d.ts +3 -0
  258. package/dist/payments/tpay/capabilities.js +18 -0
  259. package/dist/payments/tpay/classifyDecline.d.ts +46 -0
  260. package/dist/payments/tpay/classifyDecline.js +58 -0
  261. package/dist/payments/tpay/declineFailureHints.d.ts +72 -0
  262. package/dist/payments/tpay/declineFailureHints.js +37 -0
  263. package/dist/payments/tpay/donorContracts.d.ts +140 -0
  264. package/dist/payments/tpay/donorContracts.js +1 -0
  265. package/dist/payments/tpay/index.d.ts +73 -0
  266. package/dist/payments/tpay/index.js +294 -0
  267. package/dist/payments/tpay/paymentFailureEvidence.d.ts +26 -0
  268. package/dist/payments/tpay/paymentFailureEvidence.js +33 -0
  269. package/dist/payments/tpay/preparedProviderAttempt.d.ts +22 -0
  270. package/dist/payments/tpay/preparedProviderAttempt.js +24 -0
  271. package/dist/payments/tpay/providerAttemptFailureDiagnostic.d.ts +3 -0
  272. package/dist/payments/tpay/providerAttemptFailureDiagnostic.js +95 -0
  273. package/dist/payments/tpay/tpayCreateTransactionInput.d.ts +25 -0
  274. package/dist/payments/tpay/tpayCreateTransactionInput.js +128 -0
  275. package/dist/payments/tpay/tpayDeclineEvidence.d.ts +126 -0
  276. package/dist/payments/tpay/tpayDeclineEvidence.js +177 -0
  277. package/dist/payments/tpay/tpayDispatchFailure.d.ts +60 -0
  278. package/dist/payments/tpay/tpayDispatchFailure.js +121 -0
  279. package/dist/payments/tpay/tpayFailureEvidence.d.ts +20 -0
  280. package/dist/payments/tpay/tpayFailureEvidence.js +61 -0
  281. package/dist/payments/tpay/tpayHttpClient.d.ts +52 -0
  282. package/dist/payments/tpay/tpayHttpClient.js +185 -0
  283. package/dist/payments/tpay/tpayHttpFailure.d.ts +57 -0
  284. package/dist/payments/tpay/tpayHttpFailure.js +143 -0
  285. package/dist/payments/tpay/tpayPaymentExecutionAdapter.d.ts +20 -0
  286. package/dist/payments/tpay/tpayPaymentExecutionAdapter.js +249 -0
  287. package/dist/payments/tpay/tpayPaymentReconciliationProvider.d.ts +5 -0
  288. package/dist/payments/tpay/tpayPaymentReconciliationProvider.js +244 -0
  289. package/dist/payments/tpay/tpayTransactionPayload.d.ts +120 -0
  290. package/dist/payments/tpay/tpayTransactionPayload.js +96 -0
  291. package/dist/payments/tpay/wallClockTime.d.ts +31 -0
  292. package/dist/payments/tpay/wallClockTime.js +57 -0
  293. package/dist/payments/tpay/webhookJwsVerifier.d.ts +17 -0
  294. package/dist/payments/tpay/webhookJwsVerifier.js +51 -0
  295. package/dist/payments/tpay/webhookNormalizer.d.ts +54 -0
  296. package/dist/payments/tpay/webhookNormalizer.js +200 -0
  297. package/dist/payments/verifiedEvidenceProjection.d.ts +12 -0
  298. package/dist/payments/verifiedEvidenceProjection.js +166 -0
  299. package/dist/payments/verifiedIngress.d.ts +119 -0
  300. package/dist/payments/verifiedIngress.js +220 -0
  301. package/package.json +37 -2
  302. package/release-gates.json +105 -3
  303. package/sql/20260808210000_fulfillment_shipment_rail.sql +810 -0
  304. package/sql/20260810180000_payment_settlement_rail.sql +574 -0
  305. package/sql/20260815150000_payment_truth_reconciliation.sql +479 -0
  306. package/sql/20260819150000_fulfillment_shipment_sequence.sql +98 -0
  307. package/sql/20260826100000_late_payment_shifts_the_subscription_cycle.sql +215 -0
  308. package/sql/20261022080000_fulfillment_command_ledger.sql +786 -0
  309. package/sql/20261022090000_fulfillment_delivery_alignment.sql +1106 -0
  310. package/src/composition/composeBundle.ts +13 -0
  311. package/src/composition/fulfillmentContribution.ts +28 -0
  312. package/src/composition/index.ts +2 -0
  313. package/src/fulfillment/adapterConformance.ts +195 -0
  314. package/src/fulfillment/bindings.ts +26 -0
  315. package/src/fulfillment/commerceFulfillmentHandlers.ts +263 -0
  316. package/src/fulfillment/commerceFulfillmentReadModel.ts +257 -0
  317. package/src/fulfillment/contracts/alignment.ts +13 -0
  318. package/src/fulfillment/contracts/bindings.ts +30 -0
  319. package/src/fulfillment/contracts/capabilities.ts +15 -0
  320. package/src/fulfillment/contracts/conformance.ts +61 -0
  321. package/src/fulfillment/contracts/customerAlignment.ts +4 -0
  322. package/src/fulfillment/contracts/dispatch.ts +58 -0
  323. package/src/fulfillment/contracts/index.ts +18 -0
  324. package/src/fulfillment/contracts/label.ts +9 -0
  325. package/src/fulfillment/contracts/nativeUnsupported.ts +6 -0
  326. package/src/fulfillment/contracts/observation.ts +18 -0
  327. package/src/fulfillment/contracts/operator.ts +31 -0
  328. package/src/fulfillment/contracts/read.ts +42 -0
  329. package/src/fulfillment/contracts/readback.ts +11 -0
  330. package/src/fulfillment/contracts/replacement.ts +15 -0
  331. package/src/fulfillment/contracts/store.ts +26 -0
  332. package/src/fulfillment/contracts/sync.ts +61 -0
  333. package/src/fulfillment/currentFulfillmentParcel.ts +2 -0
  334. package/src/fulfillment/delayReadModel.ts +60 -0
  335. package/src/fulfillment/deliveryAlignmentReadModel.ts +15 -0
  336. package/src/fulfillment/dispatchProjection.ts +20 -0
  337. package/src/fulfillment/fulfillmentObservationEffects.ts +86 -0
  338. package/src/fulfillment/handlerSchemas.ts +255 -0
  339. package/src/fulfillment/handlerSupport.ts +25 -0
  340. package/src/fulfillment/index.ts +38 -0
  341. package/src/fulfillment/labelLifecycle.ts +11 -0
  342. package/src/fulfillment/lowStockEvidenceHandler.ts +60 -0
  343. package/src/fulfillment/migrations.ts +8 -0
  344. package/src/fulfillment/nativeUnsupported.ts +23 -0
  345. package/src/fulfillment/normalizedFulfillmentFacts.ts +2 -0
  346. package/src/fulfillment/observationProjection.ts +41 -0
  347. package/src/fulfillment/omnipack/capabilities.ts +18 -0
  348. package/src/fulfillment/omnipack/client.ts +258 -0
  349. package/src/fulfillment/omnipack/index.ts +312 -0
  350. package/src/fulfillment/omnipack/mappers.ts +261 -0
  351. package/src/fulfillment/omnipack/observation.ts +61 -0
  352. package/src/fulfillment/omnipack/omnipackDispatchPayload.ts +93 -0
  353. package/src/fulfillment/omnipack/omnipackOutboundOrderPayload.ts +194 -0
  354. package/src/fulfillment/omnipack/omnipackProductPackPayload.ts +47 -0
  355. package/src/fulfillment/omnipack/omnipackProductPayload.ts +67 -0
  356. package/src/fulfillment/omnipack/omnipackReconciliationPayload.ts +7 -0
  357. package/src/fulfillment/omnipack/omnipackStatusVocabulary.ts +103 -0
  358. package/src/fulfillment/omnipack/omnipackWebhookPayload.ts +73 -0
  359. package/src/fulfillment/omnipack/productSyncMappers.ts +74 -0
  360. package/src/fulfillment/omnipack/providerError.ts +64 -0
  361. package/src/fulfillment/omnipack/trackingReferences.ts +77 -0
  362. package/src/fulfillment/paidFulfillmentRecovery.ts +252 -0
  363. package/src/fulfillment/paidHandoff.ts +122 -0
  364. package/src/fulfillment/paidHandoffPorts.ts +21 -0
  365. package/src/fulfillment/postgresDeliveryAlignment.ts +14 -0
  366. package/src/fulfillment/postgresDispatchStore.ts +55 -0
  367. package/src/fulfillment/postgresEvidenceReadPort.ts +59 -0
  368. package/src/fulfillment/postgresPaidFulfillmentRecovery.ts +29 -0
  369. package/src/fulfillment/postgresPaidHandoffStore.ts +11 -0
  370. package/src/fulfillment/postgresShipmentSpine.ts +321 -0
  371. package/src/fulfillment/postgresStockSyncPort.ts +65 -0
  372. package/src/fulfillment/productReconciliationEvidenceHandler.ts +64 -0
  373. package/src/fulfillment/providerCapabilities.ts +18 -0
  374. package/src/fulfillment/providerCommandLedger.ts +53 -0
  375. package/src/fulfillment/providerDispatchAcceptance.ts +31 -0
  376. package/src/fulfillment/providerDispatchError.ts +7 -0
  377. package/src/fulfillment/providerDispatchWorker.ts +131 -0
  378. package/src/fulfillment/providerProductSyncWorker.ts +154 -0
  379. package/src/fulfillment/providerReconciliationError.ts +20 -0
  380. package/src/fulfillment/providerReconciliationTerminalSkip.ts +8 -0
  381. package/src/fulfillment/providerReconciliationTimestamp.ts +34 -0
  382. package/src/fulfillment/providerReconciliationWorker.ts +150 -0
  383. package/src/fulfillment/providerStockAvailability.ts +60 -0
  384. package/src/fulfillment/providerStockSyncWorker.ts +222 -0
  385. package/src/fulfillment/providerTrackingReferences.ts +31 -0
  386. package/src/fulfillment/replacementRefusals.ts +43 -0
  387. package/src/fulfillment/replacementShipmentHandler.ts +47 -0
  388. package/src/fulfillment/schemaCapabilities.ts +7 -0
  389. package/src/fulfillment/shipmentsOverviewHandler.ts +73 -0
  390. package/src/fulfillment/statusMap.ts +2 -0
  391. package/src/fulfillment/trackingProgress.ts +1 -0
  392. package/src/fulfillment/verifiedIngress.ts +80 -0
  393. package/src/payments/adapterConformance.ts +118 -0
  394. package/src/payments/bindings.ts +17 -0
  395. package/src/payments/checkoutActivePaymentActionResolver.ts +97 -0
  396. package/src/payments/checkoutRecoveryPaymentResolver.ts +108 -0
  397. package/src/payments/contracts/adapter.ts +96 -0
  398. package/src/payments/contracts/conformance.ts +171 -0
  399. package/src/payments/contracts/evidence.ts +142 -0
  400. package/src/payments/contracts/index.ts +54 -0
  401. package/src/payments/contracts/preparedAttempt.ts +86 -0
  402. package/src/payments/contracts/reconciliation.ts +296 -0
  403. package/src/payments/contracts/repair.ts +95 -0
  404. package/src/payments/contracts/truth.ts +108 -0
  405. package/src/payments/eventConformanceRunner.ts +149 -0
  406. package/src/payments/index.ts +21 -0
  407. package/src/payments/paymentAttemptSilenceWindow.ts +26 -0
  408. package/src/payments/paymentMethodLifecycle.ts +50 -0
  409. package/src/payments/paymentProviderReconciliationAbsenceReopen.ts +130 -0
  410. package/src/payments/paymentProviderReconciliationEvidence.ts +117 -0
  411. package/src/payments/paymentProviderReconciliationWorker.ts +291 -0
  412. package/src/payments/paymentTruth.ts +131 -0
  413. package/src/payments/paymentVerifyNowHandler.ts +94 -0
  414. package/src/payments/paymentVerifyNowService.ts +125 -0
  415. package/src/payments/paymentWebhookPostProcessing.ts +36 -0
  416. package/src/payments/postgresPaymentTruth.ts +149 -0
  417. package/src/payments/preparedAttempt.ts +90 -0
  418. package/src/payments/preparedAttemptFinalization.ts +13 -0
  419. package/src/payments/schemaCapabilities.ts +10 -0
  420. package/src/payments/sql.ts +6 -0
  421. package/src/payments/stripe/classifyDecline.ts +62 -0
  422. package/src/payments/stripe/declineFailureHints.ts +89 -0
  423. package/src/payments/stripe/donorContracts.ts +87 -0
  424. package/src/payments/stripe/executionDecline.ts +126 -0
  425. package/src/payments/stripe/index.ts +265 -0
  426. package/src/payments/stripe/intentMapper.ts +58 -0
  427. package/src/payments/stripe/methodFacts.ts +21 -0
  428. package/src/payments/stripe/paymentFailureEvidence.ts +58 -0
  429. package/src/payments/stripe/stripeFailureEvidence.ts +115 -0
  430. package/src/payments/stripe/stripeIntentConfirmationEvidence.ts +34 -0
  431. package/src/payments/stripe/stripePaymentReconciliationProvider.ts +263 -0
  432. package/src/payments/stripe/stripeRefundEvidence.ts +19 -0
  433. package/src/payments/stripe/stripeSandboxPaymentExecutionAdapter.ts +283 -0
  434. package/src/payments/stripe/stripeWebhookNormalizer.ts +277 -0
  435. package/src/payments/stripe/webhookSignature.ts +55 -0
  436. package/src/payments/tpay/blikRecurringMapper.ts +42 -0
  437. package/src/payments/tpay/capabilities.ts +21 -0
  438. package/src/payments/tpay/classifyDecline.ts +62 -0
  439. package/src/payments/tpay/declineFailureHints.ts +90 -0
  440. package/src/payments/tpay/donorContracts.ts +104 -0
  441. package/src/payments/tpay/index.ts +293 -0
  442. package/src/payments/tpay/paymentFailureEvidence.ts +58 -0
  443. package/src/payments/tpay/preparedProviderAttempt.ts +38 -0
  444. package/src/payments/tpay/providerAttemptFailureDiagnostic.ts +99 -0
  445. package/src/payments/tpay/tpayCreateTransactionInput.ts +158 -0
  446. package/src/payments/tpay/tpayDeclineEvidence.ts +233 -0
  447. package/src/payments/tpay/tpayDispatchFailure.ts +186 -0
  448. package/src/payments/tpay/tpayFailureEvidence.ts +75 -0
  449. package/src/payments/tpay/tpayHttpClient.ts +290 -0
  450. package/src/payments/tpay/tpayHttpFailure.ts +186 -0
  451. package/src/payments/tpay/tpayPaymentExecutionAdapter.ts +297 -0
  452. package/src/payments/tpay/tpayPaymentReconciliationProvider.ts +285 -0
  453. package/src/payments/tpay/tpayTransactionPayload.ts +207 -0
  454. package/src/payments/tpay/wallClockTime.ts +57 -0
  455. package/src/payments/tpay/webhookJwsVerifier.ts +82 -0
  456. package/src/payments/tpay/webhookNormalizer.ts +279 -0
  457. package/src/payments/verifiedEvidenceProjection.ts +179 -0
  458. package/src/payments/verifiedIngress.ts +317 -0
@@ -0,0 +1,479 @@
1
+ -- Provider-neutral payment-event truth and reconciliation over the published
2
+ -- settlement, dunning and accounting rails.
3
+ --
4
+ -- WHAT THIS FORWARD SHIPS. One event ledger, one reconciliation-evidence
5
+ -- ledger and four entry points: ingest, reconcile, sweep-open and readback.
6
+ -- Event identity plus a canonical fingerprint is the replay boundary. A
7
+ -- captured event delegates to `commerce_record_settlement` and
8
+ -- `accounting_document_request_from_paid_order`; a refused event delegates to
9
+ -- the same settlement result and `dunning_lifecycle_open_case`. Indeterminate
10
+ -- and failed reconciliation evidence remain open. The event row records only
11
+ -- references to effects owned by those existing rails.
12
+ --
13
+ -- DELIBERATE BOUNDARIES. There is no provider enum, signature, attempt,
14
+ -- mandate, browser callback, retry cadence, payment state machine or accounting
15
+ -- document lifecycle here. Source references are opaque, external payment
16
+ -- references are never invented, currency is a required three-character datum,
17
+ -- and all amounts are minor units. Dunning notification templates and recovery
18
+ -- paths are composition inputs. A capture can close an existing case through
19
+ -- the existing recovery verb, but this forward owns neither the case nor its
20
+ -- ladder. All routines are invoker-rights with fixed search paths, matching the
21
+ -- public platform catalogue.
22
+
23
+ CREATE TABLE public.payment_truth_events (
24
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
25
+ source_event_id text NOT NULL,
26
+ event_fingerprint text NOT NULL,
27
+ ingest_idempotency_key text NOT NULL,
28
+ settlement_intent_id uuid NOT NULL REFERENCES public.commerce_settlement_intents(id) ON DELETE RESTRICT,
29
+ state text NOT NULL DEFAULT 'open',
30
+ outcome text NOT NULL,
31
+ amount_minor bigint NOT NULL,
32
+ currency_code text NOT NULL,
33
+ failure_class text,
34
+ failure_reason text,
35
+ source_evidence jsonb NOT NULL,
36
+ settlement_transition_id uuid REFERENCES public.commerce_settlement_transitions(id) ON DELETE RESTRICT,
37
+ dunning_case_id uuid REFERENCES public.subscription_dunning_cases(id) ON DELETE SET NULL,
38
+ accounting_document_id uuid REFERENCES public.accounting_documents(id) ON DELETE SET NULL,
39
+ occurred_at timestamptz NOT NULL,
40
+ settled_at timestamptz,
41
+ last_reconciled_at timestamptz,
42
+ created_at timestamptz NOT NULL DEFAULT now(),
43
+ updated_at timestamptz NOT NULL DEFAULT now(),
44
+ CONSTRAINT payment_truth_events_source_event_id_key UNIQUE (source_event_id),
45
+ CONSTRAINT payment_truth_events_ingest_idempotency_key_key UNIQUE (ingest_idempotency_key),
46
+ CONSTRAINT payment_truth_events_source_event_id_check CHECK (btrim(source_event_id) <> ''),
47
+ CONSTRAINT payment_truth_events_event_fingerprint_check CHECK (event_fingerprint ~ '^[0-9a-f]{64}$'),
48
+ CONSTRAINT payment_truth_events_state_check CHECK (state IN ('open', 'settled')),
49
+ CONSTRAINT payment_truth_events_outcome_check CHECK (outcome IN ('captured', 'refused', 'indeterminate')),
50
+ CONSTRAINT payment_truth_events_amount_check CHECK (amount_minor > 0),
51
+ CONSTRAINT payment_truth_events_currency_check CHECK (currency_code ~ '^[A-Z]{3}$'),
52
+ CONSTRAINT payment_truth_events_evidence_check CHECK (jsonb_typeof(source_evidence) = 'object'),
53
+ CONSTRAINT payment_truth_events_failure_class_check CHECK (
54
+ failure_class IS NULL OR failure_class IN (
55
+ 'authorization_refused', 'insufficient_funds', 'method_unavailable',
56
+ 'risk_refused', 'unknown_refusal'
57
+ )
58
+ ),
59
+ CONSTRAINT payment_truth_events_refusal_check CHECK (
60
+ (outcome = 'refused' AND failure_class IS NOT NULL)
61
+ OR (outcome <> 'refused' AND failure_class IS NULL AND failure_reason IS NULL)
62
+ ),
63
+ CONSTRAINT payment_truth_events_settled_at_check CHECK ((state = 'settled') = (settled_at IS NOT NULL)),
64
+ CONSTRAINT payment_truth_events_transition_check CHECK (
65
+ (state = 'settled') = (settlement_transition_id IS NOT NULL)
66
+ ),
67
+ CONSTRAINT payment_truth_events_accounting_check CHECK (
68
+ accounting_document_id IS NULL OR (state = 'settled' AND outcome = 'captured')
69
+ ),
70
+ CONSTRAINT payment_truth_events_dunning_check CHECK (
71
+ dunning_case_id IS NULL OR (state = 'settled' AND outcome = 'refused')
72
+ )
73
+ );
74
+
75
+ CREATE INDEX idx_payment_truth_events_open
76
+ ON public.payment_truth_events (occurred_at, id)
77
+ WHERE state = 'open';
78
+
79
+ CREATE TABLE public.payment_truth_reconciliation_evidence (
80
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
81
+ event_id uuid NOT NULL REFERENCES public.payment_truth_events(id) ON DELETE CASCADE,
82
+ idempotency_key text NOT NULL,
83
+ evidence_fingerprint text NOT NULL,
84
+ evidence_status text NOT NULL,
85
+ outcome text NOT NULL,
86
+ failure_class text,
87
+ failure_reason text,
88
+ source_evidence jsonb NOT NULL,
89
+ occurred_at timestamptz NOT NULL,
90
+ applied_at timestamptz,
91
+ created_at timestamptz NOT NULL DEFAULT now(),
92
+ CONSTRAINT payment_truth_reconciliation_idempotency_key_key UNIQUE (idempotency_key),
93
+ CONSTRAINT payment_truth_reconciliation_fingerprint_check CHECK (evidence_fingerprint ~ '^[0-9a-f]{64}$'),
94
+ CONSTRAINT payment_truth_reconciliation_status_check CHECK (evidence_status IN ('observed', 'failed')),
95
+ CONSTRAINT payment_truth_reconciliation_outcome_check CHECK (outcome IN ('captured', 'refused', 'indeterminate')),
96
+ CONSTRAINT payment_truth_reconciliation_evidence_check CHECK (jsonb_typeof(source_evidence) = 'object'),
97
+ CONSTRAINT payment_truth_reconciliation_failure_class_check CHECK (
98
+ failure_class IS NULL OR failure_class IN (
99
+ 'authorization_refused', 'insufficient_funds', 'method_unavailable',
100
+ 'risk_refused', 'unknown_refusal'
101
+ )
102
+ ),
103
+ CONSTRAINT payment_truth_reconciliation_failed_open_check CHECK (
104
+ evidence_status <> 'failed' OR outcome = 'indeterminate'
105
+ ),
106
+ CONSTRAINT payment_truth_reconciliation_refusal_check CHECK (
107
+ (outcome = 'refused' AND failure_class IS NOT NULL)
108
+ OR (outcome <> 'refused' AND failure_class IS NULL AND failure_reason IS NULL)
109
+ )
110
+ );
111
+
112
+ CREATE INDEX idx_payment_truth_reconciliation_event
113
+ ON public.payment_truth_reconciliation_evidence (event_id, occurred_at DESC);
114
+
115
+ CREATE FUNCTION public.payment_truth_readback(
116
+ p_event_id uuid,
117
+ p_replayed boolean DEFAULT false
118
+ )
119
+ RETURNS jsonb
120
+ LANGUAGE sql
121
+ STABLE
122
+ SET search_path = public, pg_catalog
123
+ AS $$
124
+ SELECT jsonb_build_object(
125
+ 'event', jsonb_build_object(
126
+ 'id', event.id,
127
+ 'sourceEventId', event.source_event_id,
128
+ 'fingerprint', event.event_fingerprint,
129
+ 'state', event.state,
130
+ 'outcome', event.outcome,
131
+ 'replayed', p_replayed
132
+ ),
133
+ 'effects', jsonb_build_object(
134
+ 'paymentResultRecorded', event.settlement_transition_id IS NOT NULL,
135
+ 'dunningOpened', event.dunning_case_id IS NOT NULL,
136
+ 'accountingRequested', event.accounting_document_id IS NOT NULL
137
+ ),
138
+ 'financial', jsonb_build_object(
139
+ 'settlementStatus', settlement.status,
140
+ 'orderStatus', order_row.status,
141
+ 'subscriptionStatus', subscription.status,
142
+ 'accountingStatus', document.status,
143
+ 'amountMinor', event.amount_minor,
144
+ 'currency', event.currency_code
145
+ )
146
+ )
147
+ FROM public.payment_truth_events event
148
+ JOIN public.commerce_settlement_intents settlement ON settlement.id = event.settlement_intent_id
149
+ JOIN public.commerce_orders order_row ON order_row.id = settlement.order_id
150
+ LEFT JOIN public.subscription_cycles cycle ON cycle.id = order_row.subscription_cycle_id
151
+ LEFT JOIN public.subscriptions subscription ON subscription.id = cycle.subscription_id
152
+ LEFT JOIN public.accounting_documents document ON document.id = event.accounting_document_id
153
+ WHERE event.id = p_event_id;
154
+ $$;
155
+
156
+ CREATE FUNCTION public.payment_truth_apply_terminal(
157
+ p_event_id uuid,
158
+ p_idempotency_key text,
159
+ p_outcome text,
160
+ p_occurred_at timestamptz,
161
+ p_failure_class text,
162
+ p_failure_reason text,
163
+ p_source_evidence jsonb,
164
+ p_dunning_template_slug text,
165
+ p_recovery_url_path text,
166
+ p_recovery_template_slug text
167
+ )
168
+ RETURNS void
169
+ LANGUAGE plpgsql
170
+ SET search_path = public, pg_catalog
171
+ AS $$
172
+ DECLARE
173
+ v_event public.payment_truth_events%ROWTYPE;
174
+ v_intent public.commerce_settlement_intents%ROWTYPE;
175
+ v_order public.commerce_orders%ROWTYPE;
176
+ v_cycle public.subscription_cycles%ROWTYPE;
177
+ v_subscription public.subscriptions%ROWTYPE;
178
+ v_settlement jsonb;
179
+ v_dunning jsonb;
180
+ v_accounting jsonb;
181
+ v_positions jsonb;
182
+ v_open_case_id uuid;
183
+ v_transition_id uuid;
184
+ v_dunning_case_id uuid;
185
+ v_accounting_document_id uuid;
186
+ BEGIN
187
+ SELECT * INTO v_event FROM public.payment_truth_events WHERE id = p_event_id FOR UPDATE;
188
+ IF NOT FOUND THEN RAISE EXCEPTION 'payment_truth_event_not_found' USING ERRCODE = '22023'; END IF;
189
+ IF v_event.state = 'settled' THEN RETURN; END IF;
190
+ IF p_outcome NOT IN ('captured', 'refused') THEN
191
+ RAISE EXCEPTION 'payment_truth_terminal_outcome_invalid' USING ERRCODE = '22023';
192
+ END IF;
193
+ IF p_outcome = 'refused' AND p_failure_class IS NULL THEN
194
+ RAISE EXCEPTION 'payment_truth_refusal_classification_required' USING ERRCODE = '22023';
195
+ END IF;
196
+ IF p_failure_class IS NOT NULL AND p_failure_class NOT IN (
197
+ 'authorization_refused', 'insufficient_funds', 'method_unavailable',
198
+ 'risk_refused', 'unknown_refusal'
199
+ ) THEN
200
+ RAISE EXCEPTION 'payment_truth_failure_class_invalid' USING ERRCODE = '22023';
201
+ END IF;
202
+
203
+ SELECT * INTO v_intent
204
+ FROM public.commerce_settlement_intents WHERE id = v_event.settlement_intent_id;
205
+ SELECT * INTO v_order FROM public.commerce_orders WHERE id = v_intent.order_id;
206
+ IF v_intent.amount_minor IS DISTINCT FROM v_event.amount_minor
207
+ OR v_intent.currency_code IS DISTINCT FROM v_event.currency_code
208
+ THEN
209
+ RAISE EXCEPTION 'payment_truth_settlement_money_mismatch' USING ERRCODE = '22023';
210
+ END IF;
211
+
212
+ v_settlement := public.commerce_record_settlement(
213
+ p_idempotency_key || ':settlement',
214
+ v_event.settlement_intent_id,
215
+ CASE WHEN p_outcome = 'captured' THEN 'succeeded' ELSE 'failed' END,
216
+ NULL,
217
+ jsonb_build_object(
218
+ 'paymentTruthEventId', v_event.id,
219
+ 'sourceEvidence', COALESCE(p_source_evidence, '{}'::jsonb),
220
+ 'reason', p_failure_reason,
221
+ 'failureClass', p_failure_class
222
+ ),
223
+ p_occurred_at
224
+ );
225
+ v_transition_id := (v_settlement->>'transitionId')::uuid;
226
+
227
+ IF p_outcome = 'captured' THEN
228
+ SELECT COALESCE(jsonb_agg(jsonb_build_object(
229
+ 'description', 'order position ' || item.line_ordinal::text,
230
+ 'quantity', item.quantity,
231
+ 'unitGrossMinor', item.unit_amount_minor,
232
+ 'totalGrossMinor', item.line_amount_minor,
233
+ 'taxRateBps', 0
234
+ ) ORDER BY item.line_ordinal), '[]'::jsonb)
235
+ INTO v_positions
236
+ FROM public.commerce_order_items item
237
+ WHERE item.order_id = v_order.id;
238
+
239
+ v_accounting := public.accounting_document_request_from_paid_order(
240
+ p_idempotency_key || ':accounting',
241
+ v_order.id,
242
+ v_positions,
243
+ v_event.amount_minor,
244
+ jsonb_build_object(
245
+ 'paymentTruthEventId', v_event.id,
246
+ 'currency', v_event.currency_code,
247
+ 'sourceEvidence', COALESCE(p_source_evidence, '{}'::jsonb)
248
+ ),
249
+ p_occurred_at
250
+ );
251
+ v_accounting_document_id := (v_accounting->'document'->>'id')::uuid;
252
+
253
+ IF v_order.subscription_cycle_id IS NOT NULL THEN
254
+ SELECT id INTO v_open_case_id
255
+ FROM public.subscription_dunning_cases
256
+ WHERE cycle_id = v_order.subscription_cycle_id AND status = 'open'
257
+ FOR UPDATE;
258
+ IF v_open_case_id IS NOT NULL THEN
259
+ PERFORM public.dunning_lifecycle_record_recovery(
260
+ v_open_case_id, p_occurred_at, p_recovery_template_slug
261
+ );
262
+ END IF;
263
+ END IF;
264
+ ELSE
265
+ IF v_order.subscription_cycle_id IS NOT NULL THEN
266
+ SELECT * INTO v_cycle
267
+ FROM public.subscription_cycles WHERE id = v_order.subscription_cycle_id;
268
+ SELECT * INTO v_subscription
269
+ FROM public.subscriptions WHERE id = v_cycle.subscription_id;
270
+ v_dunning := public.dunning_lifecycle_open_case(
271
+ 'payment-truth:dunning:' || v_cycle.id::text,
272
+ v_subscription.id,
273
+ v_cycle.id,
274
+ v_subscription.client_id,
275
+ 1,
276
+ NULL,
277
+ p_failure_class,
278
+ p_failure_reason,
279
+ p_occurred_at,
280
+ v_event.amount_minor,
281
+ v_event.currency_code,
282
+ p_dunning_template_slug,
283
+ p_recovery_url_path
284
+ );
285
+ v_dunning_case_id := (v_dunning->>'caseId')::uuid;
286
+ END IF;
287
+ END IF;
288
+
289
+ UPDATE public.payment_truth_events
290
+ SET state = 'settled',
291
+ outcome = p_outcome,
292
+ failure_class = CASE WHEN p_outcome = 'refused' THEN p_failure_class ELSE NULL END,
293
+ failure_reason = CASE WHEN p_outcome = 'refused' THEN p_failure_reason ELSE NULL END,
294
+ settlement_transition_id = v_transition_id,
295
+ dunning_case_id = v_dunning_case_id,
296
+ accounting_document_id = v_accounting_document_id,
297
+ settled_at = p_occurred_at,
298
+ updated_at = p_occurred_at
299
+ WHERE id = v_event.id;
300
+ END;
301
+ $$;
302
+
303
+ CREATE FUNCTION public.payment_truth_ingest_event(
304
+ p_source_event_id text,
305
+ p_event_fingerprint text,
306
+ p_idempotency_key text,
307
+ p_settlement_intent_id uuid,
308
+ p_outcome text,
309
+ p_amount_minor bigint,
310
+ p_currency_code text,
311
+ p_occurred_at timestamptz,
312
+ p_failure_class text,
313
+ p_failure_reason text,
314
+ p_source_evidence jsonb,
315
+ p_dunning_template_slug text,
316
+ p_recovery_url_path text,
317
+ p_recovery_template_slug text
318
+ )
319
+ RETURNS jsonb
320
+ LANGUAGE plpgsql
321
+ SET search_path = public, pg_catalog
322
+ AS $$
323
+ DECLARE
324
+ v_event public.payment_truth_events%ROWTYPE;
325
+ BEGIN
326
+ IF p_source_event_id IS NULL OR btrim(p_source_event_id) = ''
327
+ OR p_event_fingerprint !~ '^[0-9a-f]{64}$'
328
+ OR p_idempotency_key IS NULL OR char_length(btrim(p_idempotency_key)) < 8
329
+ OR p_outcome NOT IN ('captured', 'refused', 'indeterminate')
330
+ OR p_amount_minor IS NULL OR p_amount_minor < 1
331
+ OR p_currency_code !~ '^[A-Z]{3}$'
332
+ OR p_source_evidence IS NULL OR jsonb_typeof(p_source_evidence) <> 'object'
333
+ OR (p_outcome = 'refused') IS DISTINCT FROM (p_failure_class IS NOT NULL)
334
+ OR (p_failure_class IS NOT NULL AND p_failure_class NOT IN (
335
+ 'authorization_refused', 'insufficient_funds', 'method_unavailable',
336
+ 'risk_refused', 'unknown_refusal'
337
+ ))
338
+ THEN
339
+ RAISE EXCEPTION 'payment_truth_event_invalid' USING ERRCODE = '22023';
340
+ END IF;
341
+
342
+ INSERT INTO public.payment_truth_events (
343
+ source_event_id, event_fingerprint, ingest_idempotency_key,
344
+ settlement_intent_id, state, outcome, amount_minor, currency_code,
345
+ failure_class, failure_reason, source_evidence, occurred_at, updated_at
346
+ ) VALUES (
347
+ p_source_event_id, p_event_fingerprint, p_idempotency_key,
348
+ p_settlement_intent_id, 'open', p_outcome, p_amount_minor, p_currency_code,
349
+ p_failure_class, p_failure_reason, p_source_evidence, p_occurred_at, p_occurred_at
350
+ )
351
+ ON CONFLICT ON CONSTRAINT payment_truth_events_source_event_id_key DO NOTHING
352
+ RETURNING * INTO v_event;
353
+
354
+ IF NOT FOUND THEN
355
+ SELECT * INTO v_event
356
+ FROM public.payment_truth_events
357
+ WHERE source_event_id = p_source_event_id
358
+ FOR UPDATE;
359
+ IF v_event.event_fingerprint IS DISTINCT FROM p_event_fingerprint THEN
360
+ RAISE EXCEPTION 'payment_truth_event_fingerprint_conflict' USING ERRCODE = '23505';
361
+ END IF;
362
+ RETURN public.payment_truth_readback(v_event.id, true);
363
+ END IF;
364
+
365
+ IF p_outcome IN ('captured', 'refused') THEN
366
+ PERFORM public.payment_truth_apply_terminal(
367
+ v_event.id, p_idempotency_key, p_outcome, p_occurred_at,
368
+ p_failure_class, p_failure_reason, p_source_evidence,
369
+ p_dunning_template_slug, p_recovery_url_path, p_recovery_template_slug
370
+ );
371
+ END IF;
372
+ RETURN public.payment_truth_readback(v_event.id, false);
373
+ END;
374
+ $$;
375
+
376
+ CREATE FUNCTION public.payment_truth_record_reconciliation(
377
+ p_event_id uuid,
378
+ p_idempotency_key text,
379
+ p_evidence_fingerprint text,
380
+ p_evidence_status text,
381
+ p_outcome text,
382
+ p_occurred_at timestamptz,
383
+ p_failure_class text,
384
+ p_failure_reason text,
385
+ p_source_evidence jsonb,
386
+ p_dunning_template_slug text,
387
+ p_recovery_url_path text,
388
+ p_recovery_template_slug text
389
+ )
390
+ RETURNS jsonb
391
+ LANGUAGE plpgsql
392
+ SET search_path = public, pg_catalog
393
+ AS $$
394
+ DECLARE
395
+ v_event public.payment_truth_events%ROWTYPE;
396
+ v_evidence public.payment_truth_reconciliation_evidence%ROWTYPE;
397
+ BEGIN
398
+ IF p_idempotency_key IS NULL OR char_length(btrim(p_idempotency_key)) < 8
399
+ OR p_evidence_fingerprint !~ '^[0-9a-f]{64}$'
400
+ OR p_evidence_status NOT IN ('observed', 'failed')
401
+ OR p_outcome NOT IN ('captured', 'refused', 'indeterminate')
402
+ OR (p_evidence_status = 'failed' AND p_outcome <> 'indeterminate')
403
+ OR (p_outcome = 'refused') IS DISTINCT FROM (p_failure_class IS NOT NULL)
404
+ OR (p_failure_class IS NOT NULL AND p_failure_class NOT IN (
405
+ 'authorization_refused', 'insufficient_funds', 'method_unavailable',
406
+ 'risk_refused', 'unknown_refusal'
407
+ ))
408
+ OR p_source_evidence IS NULL OR jsonb_typeof(p_source_evidence) <> 'object'
409
+ THEN
410
+ RAISE EXCEPTION 'payment_truth_reconciliation_invalid' USING ERRCODE = '22023';
411
+ END IF;
412
+
413
+ SELECT * INTO v_event FROM public.payment_truth_events WHERE id = p_event_id FOR UPDATE;
414
+ IF NOT FOUND THEN RAISE EXCEPTION 'payment_truth_event_not_found' USING ERRCODE = '22023'; END IF;
415
+
416
+ INSERT INTO public.payment_truth_reconciliation_evidence (
417
+ event_id, idempotency_key, evidence_fingerprint, evidence_status,
418
+ outcome, failure_class, failure_reason, source_evidence, occurred_at
419
+ ) VALUES (
420
+ p_event_id, p_idempotency_key, p_evidence_fingerprint, p_evidence_status,
421
+ p_outcome, p_failure_class, p_failure_reason, p_source_evidence, p_occurred_at
422
+ )
423
+ ON CONFLICT ON CONSTRAINT payment_truth_reconciliation_idempotency_key_key DO NOTHING
424
+ RETURNING * INTO v_evidence;
425
+
426
+ IF NOT FOUND THEN
427
+ SELECT * INTO v_evidence
428
+ FROM public.payment_truth_reconciliation_evidence
429
+ WHERE idempotency_key = p_idempotency_key;
430
+ IF v_evidence.event_id IS DISTINCT FROM p_event_id
431
+ OR v_evidence.evidence_fingerprint IS DISTINCT FROM p_evidence_fingerprint
432
+ THEN
433
+ RAISE EXCEPTION 'payment_truth_reconciliation_fingerprint_conflict' USING ERRCODE = '23505';
434
+ END IF;
435
+ RETURN public.payment_truth_readback(v_event.id, true);
436
+ END IF;
437
+
438
+ UPDATE public.payment_truth_events
439
+ SET last_reconciled_at = p_occurred_at, updated_at = p_occurred_at
440
+ WHERE id = v_event.id;
441
+
442
+ IF v_event.state = 'open'
443
+ AND p_evidence_status = 'observed'
444
+ AND p_outcome IN ('captured', 'refused')
445
+ THEN
446
+ PERFORM public.payment_truth_apply_terminal(
447
+ v_event.id, p_idempotency_key, p_outcome, p_occurred_at,
448
+ p_failure_class, p_failure_reason, p_source_evidence,
449
+ p_dunning_template_slug, p_recovery_url_path, p_recovery_template_slug
450
+ );
451
+ UPDATE public.payment_truth_reconciliation_evidence
452
+ SET applied_at = p_occurred_at WHERE id = v_evidence.id;
453
+ END IF;
454
+
455
+ RETURN public.payment_truth_readback(v_event.id, false);
456
+ END;
457
+ $$;
458
+
459
+ CREATE FUNCTION public.payment_truth_sweep_open_events(
460
+ p_now timestamptz,
461
+ p_limit integer
462
+ )
463
+ RETURNS jsonb
464
+ LANGUAGE sql
465
+ SET search_path = public, pg_catalog
466
+ AS $$
467
+ WITH open_events AS (
468
+ SELECT id FROM public.payment_truth_events
469
+ WHERE state = 'open' AND occurred_at <= p_now
470
+ ORDER BY occurred_at, id
471
+ LIMIT GREATEST(COALESCE(p_limit, 1), 1)
472
+ FOR UPDATE SKIP LOCKED
473
+ )
474
+ SELECT jsonb_build_object(
475
+ 'claimed', count(*)::integer,
476
+ 'settledIgnored', (SELECT count(*)::integer FROM public.payment_truth_events WHERE state = 'settled'),
477
+ 'eventIds', COALESCE(jsonb_agg(id ORDER BY id), '[]'::jsonb)
478
+ ) FROM open_events;
479
+ $$;
@@ -0,0 +1,98 @@
1
+ -- Public platform shipment rail: an order may hold more than one shipment.
2
+ -- Three columns, two rules, one constraint swap, one index, no behaviour.
3
+ --
4
+ -- WHAT THIS FORWARD SHIPS. `fulfillment_shipments` carried `UNIQUE (order_id)`, which said that an
5
+ -- order is fulfilled once and only once. That is true of the ordinary case and false of the case
6
+ -- every shop eventually meets: the parcel was lost, arrived damaged, or came back undelivered, and
7
+ -- the operator has to send another one. This forward replaces the single-column key with
8
+ -- `UNIQUE (order_id, sequence_no)` and gives a second shipment the three facts it needs to be more
9
+ -- than an accidental duplicate -- its ordinal within the order, the shipment it stands in for, and
10
+ -- why it was sent.
11
+ --
12
+ -- WHY IT IS A SECOND SHIPMENT AND NOT A SECOND ORDER. The buyer bought once. Making the replacement
13
+ -- a new order would produce a second document trail, a second settlement and a second identifier
14
+ -- the buyer never agreed to; every one of those is a defect a support desk then has to undo. The
15
+ -- order stays the commercial fact and the shipment becomes the repeatable one, which is the same
16
+ -- direction the lifecycle already points: shipments already carry their own state machine, their own
17
+ -- external references and their own operation ledger, all keyed on the shipment rather than on the
18
+ -- order.
19
+ --
20
+ -- WHY THE ORDINAL RATHER THAN A FLAG. A boolean `is_replacement` cannot answer the third question --
21
+ -- the second replacement -- and a timestamp cannot be made unique without also making it a key.
22
+ -- `sequence_no` is derivable, orderable and stable, so a caller can compute the external identifier
23
+ -- for a shipment without inventing one, and the unique key above refuses the same ordinal twice.
24
+ --
25
+ -- WHY THE SHAPE RULE IS NOT OPTIONAL. Dropping a unique constraint gives up a real defence: today a
26
+ -- stray second insert for one order is refused by the database. The shape rule is what is put in its
27
+ -- place. An ordinal of 0 is the original and must carry neither a predecessor nor a reason; an
28
+ -- ordinal above 0 must carry both. A stray insert that simply omits the ordinal therefore still
29
+ -- takes 0 and still collides, exactly as it did before this forward, and writing a second shipment
30
+ -- on purpose now means filling three columns consistently.
31
+ --
32
+ -- WHY IT CHANGES NOTHING TODAY. `sequence_no` takes 0 by column default, so the new key is
33
+ -- constraint-for-constraint as strict as the one it replaces for every row that exists. No function
34
+ -- in this catalogue writes an ordinal, and none needs a repair to read one: every lifecycle function
35
+ -- is keyed on the shipment identifier, the notification suppressor is an `EXISTS`, and the retention
36
+ -- reads that join a shipment to its order either sit inside `SELECT EXISTS (...)` or collapse under
37
+ -- an order-scoped idempotency key. This forward is therefore the transition and only the transition.
38
+ --
39
+ -- NOT VALID on both rules follows this catalogue's convention for a constraint added to a table that
40
+ -- may already carry rows: every write from this statement on is checked, no blocking scan is taken,
41
+ -- and every pre-existing row satisfies the rule trivially because its ordinal is 0 and both new
42
+ -- columns are NULL.
43
+ --
44
+ -- WHAT THIS FORWARD DELIBERATELY DOES NOT SHIP:
45
+ -- * no way to create the second shipment. `fulfillment_create_shipment` replays through the
46
+ -- operation ledger and opens exactly one shipment per idempotency key; it is untouched, and
47
+ -- until an operator command is authored nothing can reach an ordinal above 0;
48
+ -- * no external identifier scheme. What a deployment calls the replacement at its integration is
49
+ -- derivable from the ordinal, and deriving it is the host application's business;
50
+ -- * no refund, return or restock consequence. Sending a replacement says nothing about the money;
51
+ -- * no reader. Nothing in this catalogue selects the three columns, and the resolution order the
52
+ -- index carries is a hint for a future reader, not a policy this forward enforces.
53
+
54
+ ALTER TABLE public.fulfillment_shipments
55
+ ADD COLUMN sequence_no smallint NOT NULL DEFAULT 0,
56
+ ADD COLUMN replaces_shipment_id uuid,
57
+ ADD COLUMN replacement_reason text;
58
+
59
+ -- SET NULL rather than CASCADE: erasing a superseded shipment must not erase the record that its
60
+ -- successor was sent, and the ordinal keeps the successor legible on its own.
61
+ ALTER TABLE public.fulfillment_shipments
62
+ ADD CONSTRAINT fulfillment_shipments_replaces_shipment_id_fkey
63
+ FOREIGN KEY (replaces_shipment_id) REFERENCES public.fulfillment_shipments(id)
64
+ ON DELETE SET NULL NOT VALID;
65
+
66
+ ALTER TABLE public.fulfillment_shipments
67
+ ADD CONSTRAINT fulfillment_shipments_replacement_reason_check CHECK (
68
+ replacement_reason IS NULL
69
+ OR replacement_reason IN ('damaged', 'lost', 'returned_undelivered', 'other')
70
+ ) NOT VALID;
71
+
72
+ -- The rule that replaces the guarantee the dropped key was giving.
73
+ ALTER TABLE public.fulfillment_shipments
74
+ ADD CONSTRAINT fulfillment_shipments_replacement_shape_check CHECK (
75
+ (sequence_no = 0 AND replaces_shipment_id IS NULL AND replacement_reason IS NULL)
76
+ OR (sequence_no > 0 AND replaces_shipment_id IS NOT NULL AND replacement_reason IS NOT NULL)
77
+ ) NOT VALID;
78
+
79
+ ALTER TABLE public.fulfillment_shipments
80
+ DROP CONSTRAINT fulfillment_shipments_order_id_key;
81
+
82
+ -- The unique key swap is this migration's whole point and cannot be expressed without
83
+ -- building a unique index. It is spelled as a unique INDEX rather than a UNIQUE CONSTRAINT
84
+ -- because that is this repository's established shape for in-transaction uniqueness -- it
85
+ -- enforces identically and needs only the one accepted ignore. CONCURRENTLY is unavailable:
86
+ -- the migration runner wraps each file in a transaction, which is what the squawk gate assumes.
87
+ -- The build cannot find a duplicate: `sequence_no` is NOT NULL DEFAULT 0, so every
88
+ -- pre-existing row backfills to the same ordinal and the new key is exactly as strict as the
89
+ -- one it replaces.
90
+ -- squawk-ignore require-concurrent-index-creation
91
+ CREATE UNIQUE INDEX IF NOT EXISTS fulfillment_shipments_order_id_sequence_no_key
92
+ ON public.fulfillment_shipments (order_id, sequence_no);
93
+ COMMENT ON COLUMN public.fulfillment_shipments.sequence_no IS
94
+ 'Ordinal of this shipment within its order. 0 is the original; a replacement takes the next free ordinal. Externally derivable and stable; the buyer never sees it.';
95
+ COMMENT ON COLUMN public.fulfillment_shipments.replaces_shipment_id IS
96
+ 'The shipment this one stands in for. This is the linkage truth; never recover it by parsing an external identifier.';
97
+ COMMENT ON COLUMN public.fulfillment_shipments.replacement_reason IS
98
+ 'Why the replacement was sent: damaged, lost, returned_undelivered, other. NULL on the original.';