@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,126 @@
1
+ import type { PaymentFailureHint } from "@openlup/core/payment";
2
+ import type { DeclineCodeReading } from "./declineFailureHints.js";
3
+ import type { TpayHttpClient } from "./tpayHttpClient.js";
4
+ /**
5
+ * What this rail is allowed to conclude from a refusal, and from which evidence.
6
+ *
7
+ * Two rails ask the same question — "which refusal code does this transaction
8
+ * stand on" — the reconciliation poll and the execution path the moment the
9
+ * provider says no, so the scan lives here once rather than twice against the
10
+ * same payload shape. The message-shape heuristic lives here too, as the weakest
11
+ * grade of evidence and the last one consulted.
12
+ */
13
+ /**
14
+ * Short on purpose. This read only EXPLAINS a refusal the provider has already
15
+ * decided; the attempt is finalized either way, so every millisecond spent here
16
+ * is pure added latency inside the money path. Well below the transport client's
17
+ * own request timeout, so the deadline that fires is this one.
18
+ */
19
+ export declare const DECLINE_CODE_READBACK_TIMEOUT_MS = 3000;
20
+ export interface RefusalRow {
21
+ row: Record<string, unknown>;
22
+ code: string;
23
+ }
24
+ /**
25
+ * The last row carrying a refusal code. Last rather than first: a payer may retry
26
+ * inside their banking app, so the most recent row describes the charge's current
27
+ * standing. Asserted only from an explicit code — never from silence or elapsed
28
+ * time — so a charge genuinely still running is never turned terminal. The row
29
+ * travels back with the code because a caller may need its date; a caller that
30
+ * does not can ignore it.
31
+ */
32
+ export declare function lastRefusalRow(rows: unknown, codeKey: string): RefusalRow | null;
33
+ /**
34
+ * The refusal code the provider's own attempt log stands on, or `null`.
35
+ *
36
+ * FAIL-OPEN BY CONSTRUCTION. A rejected request, a malformed body, a client that
37
+ * never settles, an attempt log without codes — every one of them resolves to
38
+ * `null`, which is the caller's "read nothing" branch. This function cannot
39
+ * throw and cannot block past its deadline, because the only thing it can add is
40
+ * a better explanation of a refusal that is already final. Anything stronger
41
+ * would put a provider read on the critical path of terminalizing an attempt,
42
+ * and a stalled terminalization is a stalled subscription cycle.
43
+ */
44
+ export interface AttemptDeclineObservation {
45
+ /** Diagnostic-only quarantine; code remains the unchanged legacy classifier input. */
46
+ identityMismatch?: true;
47
+ code: string | null;
48
+ disposition: "present" | "absent" | "unreadable" | "read_failed" | "read_timeout";
49
+ }
50
+ /** Compatibility accessor: the existing classifier still sees exactly code or null. */
51
+ export declare function readAttemptDeclineCode(client: Pick<TpayHttpClient, "getTransaction">, transactionId: string, timeoutMs?: number): Promise<string | null>;
52
+ export declare function attemptDeclineObservation(transaction: unknown): AttemptDeclineObservation;
53
+ /** One bounded read; diagnostic failure never changes terminalization or legacy code. */
54
+ export declare function readAttemptDeclineObservation(client: Pick<TpayHttpClient, "getTransaction">, transactionId: string, timeoutMs?: number): Promise<AttemptDeclineObservation>;
55
+ /** Legacy attribution remains a separate field with unchanged precedence. */
56
+ export declare function declineEvidenceSource(input: {
57
+ attemptCode: string | null;
58
+ decidingAttemptReading: DeclineCodeReading | null;
59
+ mandateUnsupported: boolean;
60
+ reading: DeclineCodeReading;
61
+ errorCode: string;
62
+ }): string;
63
+ /** Whether a reading says anything at all, as opposed to `indeterminate`. */
64
+ export declare function states(reading: DeclineCodeReading): boolean;
65
+ /**
66
+ * Whether an observation of this flow may conclude anything about the payer's
67
+ * BANK being able to hold a mandate.
68
+ *
69
+ * Only a mandate REGISTRATION can answer that question. A refused charge on an
70
+ * already-registered mandate proves the bank held one, so a capability code
71
+ * arriving there says something else — which one, nobody here knows.
72
+ *
73
+ * `observedFlow` is `null` for an observer that cannot establish the flow at
74
+ * all, and that is a real answer rather than a missing argument: the
75
+ * reconciliation poll sees a transaction readback, and the flow is not a column
76
+ * of the claim it works from. Unknown is fail-closed for a capability claim, so
77
+ * such an observer abstains. Parameterized rather than hardcoded per rail, so a
78
+ * rail that later LEARNS its flow starts stating the claim by passing it, with
79
+ * no second copy of this rule to keep in step.
80
+ */
81
+ export declare function mandateCapabilityReadable(observedFlow: string | null): boolean;
82
+ /**
83
+ * The hints one observation is allowed to assert from a reading.
84
+ *
85
+ * The reading table is a reading of a CODE and knows nothing about the charge
86
+ * the code arrived on; this is where the observation's own context is applied
87
+ * to it. Both rails funnel through here, because a refusal that classifies one
88
+ * way when the execution path sees it and another way when the poll sees it is
89
+ * a race rather than a verdict — and the retry ladder cannot be allowed to
90
+ * terminate on a race.
91
+ *
92
+ * Narrow on purpose: only the mandate-capability hint depends on context. Every
93
+ * other hint states something about the charge itself and crosses untouched.
94
+ *
95
+ * The reconciliation rail passes `null` here and therefore always abstains from
96
+ * that one hint: it reads a transaction back, and a readback cannot tell a
97
+ * refused mandate REGISTRATION from a refused charge on a mandate the bank
98
+ * already holds. Abstaining is what it costs to have one verdict per refusal;
99
+ * the claim stays reachable from evidence that is not a guess, which on a
100
+ * renewal is the preflight reason this deployment writes itself.
101
+ */
102
+ export declare function assertableHints(hints: readonly PaymentFailureHint[] | undefined, observedFlow: string | null): readonly PaymentFailureHint[] | undefined;
103
+ /**
104
+ * FALLBACK ONLY, since the 2026-08-26 incident. Whether a decline means "this
105
+ * bank cannot hold a mandate" — the one cause a retry can never fix, and the
106
+ * only one worth telling a buyer to use a card.
107
+ *
108
+ * The deciding signal is now the refusal code in the transaction's attempt log,
109
+ * read back by `readAttemptDeclineCode`. This heuristic runs only when that read
110
+ * yields no code, or a code the reading table cannot read. It exists because the
111
+ * create response's own `errorCode` is generic (`payment_failed` covers a
112
+ * mistyped code, an expired code and an incapable bank alike), and
113
+ * `payIdEligible` is absent on EVERY rejection, not just this one — so absence
114
+ * alone would brand a fat-fingered code as "your bank does not support
115
+ * recurring". Reading the message is the last resort it always should have been:
116
+ * only its shape is consulted, and the text itself is never returned or stored,
117
+ * because it is free-form and can carry payer-identifying detail.
118
+ *
119
+ * Restricted to mandate REGISTRATION: a failed charge on an already-registered
120
+ * mandate proves the bank can hold one, so bank capability is never the answer
121
+ * there.
122
+ *
123
+ * Anything uncertain falls through to a generic decline, whose copy is never
124
+ * wrong — only less specific.
125
+ */
126
+ export declare function isMandateRefusal(flow: string, payIdEligible: boolean | null, errorMessage: string): boolean;
@@ -0,0 +1,177 @@
1
+ /**
2
+ * What this rail is allowed to conclude from a refusal, and from which evidence.
3
+ *
4
+ * Two rails ask the same question — "which refusal code does this transaction
5
+ * stand on" — the reconciliation poll and the execution path the moment the
6
+ * provider says no, so the scan lives here once rather than twice against the
7
+ * same payload shape. The message-shape heuristic lives here too, as the weakest
8
+ * grade of evidence and the last one consulted.
9
+ */
10
+ /**
11
+ * Short on purpose. This read only EXPLAINS a refusal the provider has already
12
+ * decided; the attempt is finalized either way, so every millisecond spent here
13
+ * is pure added latency inside the money path. Well below the transport client's
14
+ * own request timeout, so the deadline that fires is this one.
15
+ */
16
+ export const DECLINE_CODE_READBACK_TIMEOUT_MS = 3_000;
17
+ /**
18
+ * The last row carrying a refusal code. Last rather than first: a payer may retry
19
+ * inside their banking app, so the most recent row describes the charge's current
20
+ * standing. Asserted only from an explicit code — never from silence or elapsed
21
+ * time — so a charge genuinely still running is never turned terminal. The row
22
+ * travels back with the code because a caller may need its date; a caller that
23
+ * does not can ignore it.
24
+ */
25
+ export function lastRefusalRow(rows, codeKey) {
26
+ const entries = Array.isArray(rows) ? rows : [];
27
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
28
+ const row = asRecord(entries[index]);
29
+ const raw = row[codeKey];
30
+ if (typeof raw === "string" && raw.trim())
31
+ return { row, code: raw.trim() };
32
+ }
33
+ return null;
34
+ }
35
+ /** Compatibility accessor: the existing classifier still sees exactly code or null. */
36
+ export async function readAttemptDeclineCode(client, transactionId, timeoutMs = DECLINE_CODE_READBACK_TIMEOUT_MS) {
37
+ return (await readAttemptDeclineObservation(client, transactionId, timeoutMs)).code;
38
+ }
39
+ export function attemptDeclineObservation(transaction) {
40
+ const record = asRecord(transaction);
41
+ const payments = asRecord(record.payments);
42
+ const attempts = payments.attempts;
43
+ const code = lastRefusalRow(attempts, "paymentErrorCode")?.code ?? null;
44
+ const malformed = transaction === null || typeof transaction !== "object" || Array.isArray(transaction)
45
+ || (record.payments !== undefined && (record.payments === null || typeof record.payments !== "object" || Array.isArray(record.payments)))
46
+ || (attempts !== undefined && !Array.isArray(attempts))
47
+ || (Array.isArray(attempts) && attempts.some((row) => {
48
+ const item = asRecord(row);
49
+ return row === null || typeof row !== "object" || Array.isArray(row)
50
+ || (item.paymentErrorCode !== undefined && item.paymentErrorCode !== null && typeof item.paymentErrorCode !== "string");
51
+ }));
52
+ return { code, disposition: malformed ? "unreadable" : code ? "present" : "absent" };
53
+ }
54
+ /** One bounded read; diagnostic failure never changes terminalization or legacy code. */
55
+ export async function readAttemptDeclineObservation(client, transactionId, timeoutMs = DECLINE_CODE_READBACK_TIMEOUT_MS) {
56
+ if (!transactionId)
57
+ return { code: null, disposition: "absent" };
58
+ let deadline;
59
+ try {
60
+ return await Promise.race([
61
+ Promise.resolve().then(() => client.getTransaction(transactionId)).then((transaction) => {
62
+ const observation = attemptDeclineObservation(transaction);
63
+ const actualId = asRecord(transaction).transactionId ?? asRecord(transaction).id;
64
+ return typeof actualId === "string" && actualId.trim() !== transactionId
65
+ ? { ...observation, disposition: "unreadable", identityMismatch: true } : observation;
66
+ }, () => ({ code: null, disposition: "read_failed" })),
67
+ new Promise((resolve) => {
68
+ deadline = setTimeout(() => resolve({ code: null, disposition: "read_timeout" }), timeoutMs);
69
+ }),
70
+ ]);
71
+ }
72
+ catch {
73
+ return { code: null, disposition: "read_failed" };
74
+ }
75
+ finally {
76
+ if (deadline !== undefined)
77
+ clearTimeout(deadline);
78
+ }
79
+ }
80
+ /** Legacy attribution remains a separate field with unchanged precedence. */
81
+ export function declineEvidenceSource(input) {
82
+ return input.decidingAttemptReading !== null ? `attempt_code:${input.attemptCode}`
83
+ : input.mandateUnsupported ? "prose_regex"
84
+ : states(input.reading) ? `error_code:${input.errorCode}`
85
+ : input.attemptCode !== null ? `attempt_code:${input.attemptCode}` : "none";
86
+ }
87
+ /** Whether a reading says anything at all, as opposed to `indeterminate`. */
88
+ export function states(reading) {
89
+ return reading.hints !== undefined || reading.adviceCode !== undefined;
90
+ }
91
+ /** The one flow in which this provider is asked to REGISTER a reusable mandate. */
92
+ const MANDATE_REGISTRATION_FLOW = "blik_recurring_activation";
93
+ /**
94
+ * Whether an observation of this flow may conclude anything about the payer's
95
+ * BANK being able to hold a mandate.
96
+ *
97
+ * Only a mandate REGISTRATION can answer that question. A refused charge on an
98
+ * already-registered mandate proves the bank held one, so a capability code
99
+ * arriving there says something else — which one, nobody here knows.
100
+ *
101
+ * `observedFlow` is `null` for an observer that cannot establish the flow at
102
+ * all, and that is a real answer rather than a missing argument: the
103
+ * reconciliation poll sees a transaction readback, and the flow is not a column
104
+ * of the claim it works from. Unknown is fail-closed for a capability claim, so
105
+ * such an observer abstains. Parameterized rather than hardcoded per rail, so a
106
+ * rail that later LEARNS its flow starts stating the claim by passing it, with
107
+ * no second copy of this rule to keep in step.
108
+ */
109
+ export function mandateCapabilityReadable(observedFlow) {
110
+ return observedFlow === MANDATE_REGISTRATION_FLOW;
111
+ }
112
+ /**
113
+ * The hints one observation is allowed to assert from a reading.
114
+ *
115
+ * The reading table is a reading of a CODE and knows nothing about the charge
116
+ * the code arrived on; this is where the observation's own context is applied
117
+ * to it. Both rails funnel through here, because a refusal that classifies one
118
+ * way when the execution path sees it and another way when the poll sees it is
119
+ * a race rather than a verdict — and the retry ladder cannot be allowed to
120
+ * terminate on a race.
121
+ *
122
+ * Narrow on purpose: only the mandate-capability hint depends on context. Every
123
+ * other hint states something about the charge itself and crosses untouched.
124
+ *
125
+ * The reconciliation rail passes `null` here and therefore always abstains from
126
+ * that one hint: it reads a transaction back, and a readback cannot tell a
127
+ * refused mandate REGISTRATION from a refused charge on a mandate the bank
128
+ * already holds. Abstaining is what it costs to have one verdict per refusal;
129
+ * the claim stays reachable from evidence that is not a guess, which on a
130
+ * renewal is the preflight reason this deployment writes itself.
131
+ */
132
+ export function assertableHints(hints, observedFlow) {
133
+ if (mandateCapabilityReadable(observedFlow))
134
+ return hints;
135
+ return hints?.filter((hint) => hint !== "mandateUnsupported");
136
+ }
137
+ /**
138
+ * FALLBACK ONLY, since the 2026-08-26 incident. Whether a decline means "this
139
+ * bank cannot hold a mandate" — the one cause a retry can never fix, and the
140
+ * only one worth telling a buyer to use a card.
141
+ *
142
+ * The deciding signal is now the refusal code in the transaction's attempt log,
143
+ * read back by `readAttemptDeclineCode`. This heuristic runs only when that read
144
+ * yields no code, or a code the reading table cannot read. It exists because the
145
+ * create response's own `errorCode` is generic (`payment_failed` covers a
146
+ * mistyped code, an expired code and an incapable bank alike), and
147
+ * `payIdEligible` is absent on EVERY rejection, not just this one — so absence
148
+ * alone would brand a fat-fingered code as "your bank does not support
149
+ * recurring". Reading the message is the last resort it always should have been:
150
+ * only its shape is consulted, and the text itself is never returned or stored,
151
+ * because it is free-form and can carry payer-identifying detail.
152
+ *
153
+ * Restricted to mandate REGISTRATION: a failed charge on an already-registered
154
+ * mandate proves the bank can hold one, so bank capability is never the answer
155
+ * there.
156
+ *
157
+ * Anything uncertain falls through to a generic decline, whose copy is never
158
+ * wrong — only less specific.
159
+ */
160
+ export function isMandateRefusal(flow, payIdEligible, errorMessage) {
161
+ if (!mandateCapabilityReadable(flow))
162
+ return false;
163
+ if (payIdEligible === true)
164
+ return false;
165
+ const normalized = errorMessage
166
+ .normalize("NFKD")
167
+ .replace(/\p{Diacritic}/gu, "")
168
+ .toLowerCase();
169
+ const namesMandate = /alias|payid|powtarzaln|recurring/.test(normalized);
170
+ const deniesCapability = /nie\s+(?:umozliwia|obsluguje|wspiera|pozwala)|brak\s+(?:mozliwosci|obslugi|wsparcia)|unsupported|not\s+supported|does\s+not\s+support|cannot|can't|unable/.test(normalized);
171
+ return namesMandate && deniesCapability;
172
+ }
173
+ function asRecord(value) {
174
+ return value && typeof value === "object" && !Array.isArray(value)
175
+ ? value
176
+ : {};
177
+ }
@@ -0,0 +1,60 @@
1
+ export type TpayTransactionFailurePhase = "oauth" | "transaction_dispatch" | "response_decode";
2
+ /**
3
+ * What is known about the PROVIDER's side after a failed create-transaction.
4
+ *
5
+ * `not_dispatched` - the transaction POST was never invoked.
6
+ * `refused` - it was invoked, and the provider's own answer proves it created
7
+ * nothing. Strictly weaker evidence than `not_dispatched`, and deliberately a
8
+ * separate value: the two are proved by different facts and are accepted by
9
+ * different control-plane reopen modes.
10
+ * `unknown` - anything else. A transaction may exist.
11
+ */
12
+ export type TpayTransactionDispatchState = "not_dispatched" | "refused" | "unknown";
13
+ export type TpayTransactionFailureCode = "tpay_oauth_failed" | "tpay_oauth_timeout" | "tpay_oauth_invalid_response" | "tpay_request_encode_failed" | "tpay_request_deadline_exhausted" | "tpay_request_refused" | "tpay_request_timeout" | "tpay_request_failed" | "tpay_invalid_response" | "tpay_transport_failed";
14
+ export interface TpayTransactionDispatchFailure {
15
+ phase: TpayTransactionFailurePhase;
16
+ code: TpayTransactionFailureCode;
17
+ dispatchState: TpayTransactionDispatchState;
18
+ failureDiagnostic?: TpayHttpFailureDiagnostic | null;
19
+ }
20
+ export interface TpayTrustedPreDispatchFailure {
21
+ phase: "oauth" | "transaction_dispatch";
22
+ code: "tpay_oauth_failed" | "tpay_oauth_timeout" | "tpay_oauth_invalid_response" | "tpay_request_encode_failed" | "tpay_request_deadline_exhausted";
23
+ dispatchState: "not_dispatched";
24
+ }
25
+ /**
26
+ * The provider answered the create-transaction by refusing the request document
27
+ * itself. One shape only, because only one thing is provable here: no
28
+ * transaction was created.
29
+ */
30
+ export interface TpayTrustedRefusalFailure {
31
+ phase: "response_decode";
32
+ code: "tpay_request_refused";
33
+ dispatchState: "refused";
34
+ }
35
+ /**
36
+ * Deliberately excludes the transport exception and provider response. This
37
+ * crosses the money boundary, so consumers may log only the typed facts.
38
+ */
39
+ export declare class TpayTransactionDispatchError extends Error {
40
+ readonly phase: TpayTransactionFailurePhase;
41
+ readonly code: TpayTransactionFailureCode;
42
+ readonly dispatchState: TpayTransactionDispatchState;
43
+ readonly failureDiagnostic: TpayHttpFailureDiagnostic | null;
44
+ constructor(failure: TpayTransactionDispatchFailure);
45
+ }
46
+ export declare function tpayTransactionFailureCode(error: unknown, fallback: TpayTransactionFailureCode): TpayTransactionFailureCode;
47
+ /**
48
+ * Reads back the refusal the dispatch boundary already decided rather than
49
+ * re-deriving it: by this point the raw body is gone and only the typed verdict
50
+ * survives, which is exactly what may cross the money boundary.
51
+ */
52
+ export declare function tpayTrustedRefusalFailure(error: unknown): TpayTrustedRefusalFailure | null;
53
+ export declare function tpayTrustedPreDispatchFailure(error: unknown): TpayTrustedPreDispatchFailure | null;
54
+ export declare function tpayTransactionDispatchBoundary<Token, Request, Response, Result>(input: {
55
+ authenticate: () => Promise<Token>;
56
+ prepare: (token: Token) => Request;
57
+ dispatch: (request: Request) => Promise<Response>;
58
+ decode: (response: Response) => Promise<Result>;
59
+ }): Promise<Result>;
60
+ import { type TpayHttpFailureDiagnostic } from "./tpayHttpFailure.js";
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Deliberately excludes the transport exception and provider response. This
3
+ * crosses the money boundary, so consumers may log only the typed facts.
4
+ */
5
+ export class TpayTransactionDispatchError extends Error {
6
+ phase;
7
+ code;
8
+ dispatchState;
9
+ failureDiagnostic;
10
+ constructor(failure) {
11
+ super(`tpay_transaction_${failure.dispatchState}:${failure.phase}:${failure.code}`);
12
+ this.name = "TpayTransactionDispatchError";
13
+ this.phase = failure.phase;
14
+ this.code = failure.code;
15
+ this.dispatchState = failure.dispatchState;
16
+ this.failureDiagnostic = tpayHttpFailureDiagnostic({ failureDiagnostic: failure.failureDiagnostic });
17
+ }
18
+ }
19
+ export function tpayTransactionFailureCode(error, fallback) {
20
+ const candidate = error && typeof error === "object" && "code" in error
21
+ ? error.code
22
+ : null;
23
+ return isTpayTransactionFailureCode(candidate) ? candidate : fallback;
24
+ }
25
+ /**
26
+ * Reads back the refusal the dispatch boundary already decided rather than
27
+ * re-deriving it: by this point the raw body is gone and only the typed verdict
28
+ * survives, which is exactly what may cross the money boundary.
29
+ */
30
+ export function tpayTrustedRefusalFailure(error) {
31
+ if (!(error instanceof TpayTransactionDispatchError))
32
+ return null;
33
+ return error.dispatchState === "refused"
34
+ && error.phase === "response_decode"
35
+ && error.code === "tpay_request_refused"
36
+ ? { phase: "response_decode", code: "tpay_request_refused", dispatchState: "refused" }
37
+ : null;
38
+ }
39
+ export function tpayTrustedPreDispatchFailure(error) {
40
+ if (!(error instanceof TpayTransactionDispatchError) || error.dispatchState !== "not_dispatched")
41
+ return null;
42
+ const validPhase = error.phase === "oauth" || error.phase === "transaction_dispatch";
43
+ const validCode = error.code === "tpay_oauth_failed" || error.code === "tpay_oauth_timeout" || error.code === "tpay_oauth_invalid_response" ||
44
+ error.code === "tpay_request_encode_failed" || error.code === "tpay_request_deadline_exhausted";
45
+ return validPhase && validCode ? { phase: error.phase, code: error.code, dispatchState: error.dispatchState } : null;
46
+ }
47
+ export async function tpayTransactionDispatchBoundary(input) {
48
+ let token;
49
+ try {
50
+ token = await input.authenticate();
51
+ }
52
+ catch (error) {
53
+ throw new TpayTransactionDispatchError({
54
+ phase: "oauth",
55
+ code: tpayOAuthFailureCode(error),
56
+ dispatchState: "not_dispatched",
57
+ });
58
+ }
59
+ let request;
60
+ try {
61
+ request = input.prepare(token);
62
+ }
63
+ catch (error) {
64
+ throw new TpayTransactionDispatchError({
65
+ phase: "transaction_dispatch",
66
+ code: tpayTransactionFailureCode(error, "tpay_request_encode_failed"),
67
+ dispatchState: "not_dispatched",
68
+ });
69
+ }
70
+ let response;
71
+ try {
72
+ response = await input.dispatch(request);
73
+ }
74
+ catch (error) {
75
+ throw new TpayTransactionDispatchError({
76
+ phase: "transaction_dispatch",
77
+ code: tpayTransactionFailureCode(error, "tpay_transport_failed"),
78
+ dispatchState: "unknown",
79
+ });
80
+ }
81
+ try {
82
+ return await input.decode(response);
83
+ }
84
+ catch (error) {
85
+ // A refusal of the request document is the one answer that proves the
86
+ // provider created nothing. Everything else reaching here - an unparseable
87
+ // 200, a 5xx, a 409, an opaque 400 - may sit on a real transaction and
88
+ // stays `unknown`, which keeps the attempt fenced until reconciliation.
89
+ if (error instanceof TpayHttpError && error.refusedBeforeTransaction) {
90
+ throw new TpayTransactionDispatchError({
91
+ phase: "response_decode",
92
+ code: "tpay_request_refused",
93
+ dispatchState: "refused",
94
+ failureDiagnostic: tpayHttpFailureDiagnostic(error),
95
+ });
96
+ }
97
+ throw new TpayTransactionDispatchError({
98
+ phase: "response_decode",
99
+ code: tpayTransactionFailureCode(error, "tpay_invalid_response"),
100
+ dispatchState: "unknown",
101
+ failureDiagnostic: tpayHttpFailureDiagnostic(error),
102
+ });
103
+ }
104
+ }
105
+ function isTpayTransactionFailureCode(value) {
106
+ return value === "tpay_oauth_failed" ||
107
+ value === "tpay_oauth_timeout" ||
108
+ value === "tpay_oauth_invalid_response" ||
109
+ value === "tpay_request_encode_failed" ||
110
+ value === "tpay_request_deadline_exhausted" ||
111
+ value === "tpay_request_refused" ||
112
+ value === "tpay_request_timeout" ||
113
+ value === "tpay_request_failed" ||
114
+ value === "tpay_invalid_response" ||
115
+ value === "tpay_transport_failed";
116
+ }
117
+ function tpayOAuthFailureCode(error) {
118
+ const code = tpayTransactionFailureCode(error, "tpay_oauth_failed");
119
+ return code === "tpay_invalid_response" ? "tpay_oauth_invalid_response" : code;
120
+ }
121
+ import { tpayHttpFailureDiagnostic, TpayHttpError, } from "./tpayHttpFailure.js";
@@ -0,0 +1,20 @@
1
+ import type { PaymentRecoveryEvidence } from "@openlup/core/payment";
2
+ import { type FailureObservationSource, type PaymentFailureEvidence } from "./paymentFailureEvidence.js";
3
+ import { type AttemptDeclineObservation } from "./tpayDeclineEvidence.js";
4
+ import type { DeclineCodeReading } from "./declineFailureHints.js";
5
+ export declare function tpayFailureEvidence(input: {
6
+ source: FailureObservationSource;
7
+ providerPaymentId: string | null;
8
+ refusalVerified: boolean;
9
+ flow: string | null;
10
+ observation: AttemptDeclineObservation;
11
+ errorCode?: string | null;
12
+ reading?: DeclineCodeReading;
13
+ }): PaymentFailureEvidence;
14
+ /** Numeric code hypotheses are not promoted into new customer-facing facts. */
15
+ export declare function normalizeTpayFailureEvidence(input: PaymentFailureEvidence): PaymentRecoveryEvidence;
16
+ /** Existing reconciliation classification; moved intact, never fed new evidence. */
17
+ export declare function refusalCapture(refusal: {
18
+ code: string;
19
+ reading: DeclineCodeReading;
20
+ }, failureReasonKey: string): Record<string, unknown>;
@@ -0,0 +1,61 @@
1
+ import { serializeFailureEvidence } from "./paymentFailureEvidence.js";
2
+ import { assertableHints } from "./tpayDeclineEvidence.js";
3
+ import { classifyDecline } from "./classifyDecline.js";
4
+ /** Known execution construction, never a conclusion drawn from the PSP name. */
5
+ function methodFor(flow) {
6
+ if (flow === "blik_one_time" || flow === "blik_recurring_activation") {
7
+ return { kind: "blik", recoveryMethodKey: "blik", interaction: "new_instrument" };
8
+ }
9
+ if (flow === "blik_one_click" || flow === "blik_recurring_saved") {
10
+ return { kind: "blik", recoveryMethodKey: "blik_one_click", interaction: "stored_instrument" };
11
+ }
12
+ return flow === "pbl_one_time"
13
+ ? { kind: "bank_transfer", recoveryMethodKey: "transfer", interaction: "new_instrument" } : null;
14
+ }
15
+ export function tpayFailureEvidence(input) {
16
+ const method = methodFor(input.flow);
17
+ const advice = input.observation.identityMismatch ? null : input.reading?.adviceCode ?? null;
18
+ const operation = input.flow === "blik_recurring_activation"
19
+ ? "recurring_setup" : method?.interaction === "stored_instrument"
20
+ ? "stored_method_payment" : method ? "one_time_payment" : null;
21
+ return serializeFailureEvidence({
22
+ version: 1, source: input.source, disposition: input.observation.disposition,
23
+ refusalVerified: input.refusalVerified, providerPaymentId: input.providerPaymentId, providerChargeId: null,
24
+ code: input.errorCode ?? null, declineCode: input.observation.identityMismatch ? null : input.observation.code,
25
+ adviceCode: advice,
26
+ // This table is explicitly inferred; it must never masquerade as issuer advice.
27
+ adviceOrigin: advice ? "inferred" : "unknown",
28
+ operation, method, methodSource: method ? "execution_contract" : "unknown",
29
+ });
30
+ }
31
+ /** Numeric code hypotheses are not promoted into new customer-facing facts. */
32
+ export function normalizeTpayFailureEvidence(input) {
33
+ const evidence = serializeFailureEvidence(input);
34
+ // The operation is context, not a cause: a mistyped or expired BLIK code is
35
+ // refused inside a mandate registration too. Only the persisted mandate
36
+ // decision (`failure_reason`) may say the agreement itself was refused.
37
+ return {
38
+ refusalVerified: evidence.refusalVerified,
39
+ cause: "generic_decline",
40
+ certainty: "unknown", disclosure: "safe",
41
+ method: evidence.method, operation: evidence.operation, advice: null,
42
+ };
43
+ }
44
+ /** Existing reconciliation classification; moved intact, never fed new evidence. */
45
+ export function refusalCapture(refusal, failureReasonKey) {
46
+ const hints = assertableHints(refusal.reading.hints, null) ?? [];
47
+ const classification = classifyDecline({
48
+ code: refusal.code,
49
+ mandateUnsupported: false,
50
+ declineCode: refusal.code,
51
+ ...(refusal.reading.adviceCode ? { adviceCode: refusal.reading.adviceCode } : {}),
52
+ ...(hints.length > 0 ? { neutralReasonHints: hints } : {}),
53
+ }, { failureReasonKey });
54
+ return {
55
+ declineCode: refusal.code,
56
+ adviceCode: refusal.reading.adviceCode ?? null,
57
+ neutralReasonHints: hints,
58
+ failureClass: classification.failureClass,
59
+ failureClassDecidedBy: classification.decidedBy,
60
+ };
61
+ }
@@ -0,0 +1,52 @@
1
+ import { type TpayTransactionCreateInput, type TpayTransactionCreated } from "./tpayTransactionPayload.js";
2
+ export { TpayHttpError } from "./tpayHttpFailure.js";
3
+ export interface TpayFetchLike {
4
+ (url: string, init?: {
5
+ method?: string;
6
+ headers?: Record<string, string>;
7
+ body?: string;
8
+ signal?: AbortSignal;
9
+ }): Promise<{
10
+ ok: boolean;
11
+ status: number;
12
+ json(): Promise<unknown>;
13
+ text(): Promise<string>;
14
+ }>;
15
+ }
16
+ export interface TpayHttpClientOptions {
17
+ baseUrl: string;
18
+ clientId: string;
19
+ clientSecret: string;
20
+ timeoutMs?: number;
21
+ fetchImpl?: TpayFetchLike;
22
+ now?: () => number;
23
+ }
24
+ export type { TpayAliasInput, TpayPayerInput, TpayPaymentError, TpayTransactionCreateInput, TpayTransactionCreated, } from "./tpayTransactionPayload.js";
25
+ export interface TpayPaymentChannel {
26
+ id: string;
27
+ name: string;
28
+ fullName: string;
29
+ available: boolean;
30
+ onlinePayment: boolean;
31
+ instantRedirection: boolean;
32
+ groups: Array<{
33
+ id: number;
34
+ name: string;
35
+ }>;
36
+ }
37
+ export interface TpayHttpClient {
38
+ createTransaction(input: TpayTransactionCreateInput): Promise<TpayTransactionCreated>;
39
+ getTransaction(transactionId: string): Promise<TpayTransactionReadback>;
40
+ listPaymentChannels(): Promise<TpayPaymentChannel[]>;
41
+ }
42
+ export interface TpayTransactionReadback extends Record<string, unknown> {
43
+ transactionId: string;
44
+ title: string | null;
45
+ status: string;
46
+ transactionPaymentUrl: string | null;
47
+ amount: number | null;
48
+ currency: string | null;
49
+ requestId: string | null;
50
+ payments: Record<string, unknown>;
51
+ }
52
+ export declare function createTpayHttpClient({ baseUrl, clientId, clientSecret, timeoutMs, fetchImpl, now, }: TpayHttpClientOptions): TpayHttpClient;