@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,294 @@
1
+ import { createHash, X509Certificate } from "node:crypto";
2
+ import { classifyPaymentFailure, isPaymentRetryAdviceCode } from "@openlup/core/payment";
3
+ import { tpayPaymentCapabilities } from "./capabilities.js";
4
+ export { tpayPaymentCapabilities } from "./capabilities.js";
5
+ import { createTpayHttpClient } from "./tpayHttpClient.js";
6
+ import { createTpayPaymentExecutionAdapter } from "./tpayPaymentExecutionAdapter.js";
7
+ import { ProviderAttemptPreDispatchError } from "./preparedProviderAttempt.js";
8
+ import { createTpayPaymentReconciliationProvider } from "./tpayPaymentReconciliationProvider.js";
9
+ import { verifyTpayJwsSignature } from "./webhookJwsVerifier.js";
10
+ import { normalizeTpayWebhook, toNormalizedProviderTpayWebhook } from "./webhookNormalizer.js";
11
+ const refusal = (reason) => ({ kind: "refusal", reason });
12
+ /** Construction explicitly selects this adapter; the host owns account activation and I/O policy. */
13
+ export function createTpayPaymentAdapter(config) {
14
+ const capabilities = tpayPaymentCapabilities;
15
+ const notification = config.notification;
16
+ if (!notification?.rootCertificatePem || typeof notification.resolveCertificate !== "function")
17
+ throw new Error("Required certificate configuration missing");
18
+ const prefix = new URL(notification.allowedCertificateUrlPrefix);
19
+ if (prefix.protocol !== "https:" || prefix.username || prefix.password || prefix.search || prefix.hash
20
+ || !notification.allowedCertificateUrlPrefix.endsWith("/"))
21
+ throw new Error("Invalid certificate URL prefix");
22
+ const root = new X509Certificate(notification.rootCertificatePem);
23
+ const maxAge = notification.maxNotificationAgeSeconds ?? 7 * 24 * 60 * 60;
24
+ const maxClockSkew = notification.maxClockSkewSeconds ?? 300;
25
+ if (!Number.isFinite(maxAge) || maxAge < 0 || !Number.isFinite(maxClockSkew) || maxClockSkew < 0 || !["test", "live"].includes(config.mode)
26
+ || typeof config.http.fetchImpl !== "function" || !config.http.clientId || !config.http.clientSecret
27
+ || typeof config.modelOActivationEnabled !== "boolean")
28
+ throw new Error("Invalid payment configuration");
29
+ for (const url of [config.http.baseUrl, ...Object.values(config.urls)]) {
30
+ const parsed = new URL(url);
31
+ if (parsed.protocol !== "https:" || parsed.username || parsed.password)
32
+ throw new Error("Invalid payment URL");
33
+ }
34
+ const now = config.clock ?? (() => Date.now() / 1000);
35
+ const client = createTpayHttpClient({ ...config.http, now: () => now() * 1000 });
36
+ const execution = createTpayPaymentExecutionAdapter({ client, ...config.urls,
37
+ modelOActivationEnabled: config.modelOActivationEnabled });
38
+ const reconciliation = createTpayPaymentReconciliationProvider(client);
39
+ const validateHostInput = (input) => {
40
+ if (input.mode !== config.mode || !["initial", "recovery", "renewal-unattended"].includes(input.purpose)
41
+ || !capabilities.supportedCurrencies.includes(input.money.currency))
42
+ throw new Error("Unsupported payment input");
43
+ };
44
+ const donorInput = (input) => {
45
+ if (!input.attemptRef || !input.intentRef || !input.fingerprint || !input.providerIdempotencyKey
46
+ || !Number.isSafeInteger(input.money.amountMinor) || input.money.amountMinor <= 0
47
+ || !input.payer?.email || (input.payer.name !== undefined && typeof input.payer.name !== "string")) {
48
+ throw new Error("Invalid payment input");
49
+ }
50
+ const renewal = input.purpose === "renewal-unattended";
51
+ const family = input.methodSelection?.family;
52
+ if (renewal ? !input.storedMethod?.reference || input.storedMethod.mandate.recurringModel !== "O"
53
+ || input.customerAuthorization !== undefined
54
+ : family !== "bank-redirect" && family !== "one-time-code")
55
+ throw new Error("Unsupported payment flow");
56
+ if (!renewal && (family === "bank-redirect" ? !input.methodSelection?.channelRef || input.customerAuthorization !== undefined
57
+ : !input.customerAuthorization))
58
+ throw new Error("Required payment context missing");
59
+ return {
60
+ idempotencyKey: input.attemptRef, providerIdempotencyKey: input.providerIdempotencyKey,
61
+ providerRequestFingerprint: input.fingerprint, paymentIntentId: input.intentRef, orderRef: input.intentRef,
62
+ amountMinor: input.money.amountMinor, currency: input.money.currency,
63
+ mode: renewal ? "subscription_cycle" : "one_time",
64
+ payer: { email: input.payer.email, name: input.payer.name?.trim() ? input.payer.name : "Customer" },
65
+ ...(renewal ? { providerFlow: "recurring_charge", paymentMethodRef: input.storedMethod.reference,
66
+ paymentMethodAliasType: "PAYID", paymentMethodRecurringModel: "O" } : {
67
+ transientProviderInput: family === "bank-redirect"
68
+ ? { provider: "tpay", flow: "pbl_one_time", channelId: input.methodSelection.channelRef }
69
+ : { provider: "tpay", flow: "blik_one_time", blikToken: input.customerAuthorization },
70
+ }),
71
+ };
72
+ };
73
+ const validateInput = (input) => {
74
+ validateHostInput(input);
75
+ execution.validateInput?.(donorInput(input));
76
+ };
77
+ return {
78
+ capabilities,
79
+ attemptPort: {
80
+ validateInput,
81
+ async execute(input) {
82
+ validateHostInput(input);
83
+ let prepared;
84
+ try {
85
+ prepared = donorInput(input);
86
+ execution.validateInput?.(prepared);
87
+ }
88
+ catch {
89
+ const reason = { code: "fix_and_retry_customer_action", hints: ["dataInvalid"] };
90
+ return { outcome: { kind: "decline", refusal: reason },
91
+ dispatch: { kind: "notDispatched", evidence: { exact: true, phase: "before-dispatch", reason } }, diagnostics: {} };
92
+ }
93
+ let result;
94
+ try {
95
+ result = await execution.execute(prepared);
96
+ }
97
+ catch (error) {
98
+ if (!(error instanceof ProviderAttemptPreDispatchError))
99
+ throw error;
100
+ // A proven document refusal needs corrected data; other trusted no-charge failures can retry.
101
+ const hints = error.phase === "response_decode"
102
+ && error.code === "tpay_request_refused" && error.dispatchState === "refused" ? ["dataInvalid"] : ["transient"];
103
+ const reason = { code: classifyPaymentFailure({ neutralReasonHints: hints }).failureClass, hints };
104
+ return { outcome: { kind: "decline", refusal: reason },
105
+ dispatch: { kind: "notDispatched", evidence: { exact: true, phase: "before-dispatch", reason } }, diagnostics: {} };
106
+ }
107
+ // Only these neutral facts cross the boundary; donor payloads and transient payer/authorization stay local.
108
+ return {
109
+ outcome: result.providerDecline ? { kind: "decline", refusal: neutralDecline(result.providerDecline) }
110
+ : { kind: "attempt", status: result.attemptStatus },
111
+ // API transaction ID is required for readback; notification titles remain distinct.
112
+ dispatch: result.providerSessionId ? { kind: "dispatched", providerPaymentRef: result.providerSessionId } : { kind: "unknown" },
113
+ ...(result.nextActionKind === "redirect" && result.redirectUrl
114
+ ? { handoff: { kind: "redirect", url: result.redirectUrl } } : {}),
115
+ diagnostics: { webhookExpected: result.webhookExpected === true, recoveryRequired: result.recoveryRequired === true },
116
+ };
117
+ },
118
+ },
119
+ verifyPort: {
120
+ async verifyAndNormalize(input) {
121
+ const header = Object.entries(input.headers).find(([key]) => key.toLowerCase() === "x-jws-signature")?.[1];
122
+ if (typeof header !== "string")
123
+ return refusal("signature");
124
+ const rawBody = Buffer.from(input.rawBody).toString("utf8");
125
+ if (!Buffer.from(rawBody).equals(Buffer.from(input.rawBody)))
126
+ return refusal("malformed");
127
+ try {
128
+ const current = now() * 1000;
129
+ if (!validAt(root, current))
130
+ return refusal("signature");
131
+ const verified = await verifyTpayJwsSignature({ rawBody, signatureHeader: header,
132
+ rootCertificatePem: notification.rootCertificatePem, allowedX5uPrefix: notification.allowedCertificateUrlPrefix,
133
+ resolveCertificate: async (url) => {
134
+ const pem = await notification.resolveCertificate(url);
135
+ if (!validAt(new X509Certificate(pem), current))
136
+ throw new Error("Certificate outside validity window");
137
+ return pem;
138
+ } });
139
+ if (!verified)
140
+ return refusal("signature");
141
+ }
142
+ catch {
143
+ return refusal("signature");
144
+ }
145
+ try {
146
+ // Only a signed protected-header issued-at assertion supplies notification time.
147
+ const protectedHeader = JSON.parse(Buffer.from(header.split(".")[0], "base64url").toString("utf8"));
148
+ const asserted = protectedHeader.iat;
149
+ if (asserted !== undefined && (typeof asserted !== "number" || !Number.isFinite(asserted)
150
+ || !Number.isFinite(new Date(asserted * 1000).getTime())))
151
+ return refusal("malformed");
152
+ const occurred = typeof asserted === "number" ? new Date(asserted * 1000).toISOString() : null;
153
+ if (occurred !== null) {
154
+ const received = Date.parse(input.receivedAt) / 1000;
155
+ if (!Number.isFinite(received))
156
+ return refusal("malformed");
157
+ if (received - asserted > maxAge || asserted - received > maxClockSkew)
158
+ return refusal("stale");
159
+ }
160
+ return mapVerifiedTpayEvent(rawBody, occurred, config.mode, capabilities.supportedCurrencies);
161
+ }
162
+ catch {
163
+ return refusal("malformed");
164
+ }
165
+ },
166
+ },
167
+ readbackPort: {
168
+ async readPayment(input) {
169
+ const base = { identity: input.identity, expectedMoney: input.expectedMoney, purpose: input.purpose };
170
+ if (input.identity.provider !== "tpay" || !input.identity.providerPaymentRef)
171
+ return { ...base, kind: "unsupported" };
172
+ try {
173
+ const reading = await reconciliation.readRecoveryPayment({
174
+ providerPaymentId: input.identity.providerPaymentRef, purpose: input.purpose, attempt: input.expectedMoney,
175
+ });
176
+ const observed = reading.status;
177
+ if (!reading.identityMatches || !reading.configuredMoneyMatches || reading.manualReviewRequired
178
+ || !Number.isSafeInteger(input.expectedMoney.amountMinor) || input.expectedMoney.amountMinor <= 0
179
+ || observed.status === "unknown")
180
+ return unknown(base);
181
+ // Success and nonzero refused money must match; unpaid refusals retain the configured obligation.
182
+ if ((observed.status === "succeeded" || (observed.status === "failed" && observed.amountMinor !== null && observed.amountMinor !== 0))
183
+ && (observed.amountMinor !== input.expectedMoney.amountMinor
184
+ || observed.currency !== input.expectedMoney.currency.toUpperCase()))
185
+ return unknown(base);
186
+ return { ...base, kind: observed.status, observedMoney: { amountMinor: input.expectedMoney.amountMinor,
187
+ currency: input.expectedMoney.currency.toUpperCase() },
188
+ providerOccurredAt: observed.occurredAt };
189
+ }
190
+ catch {
191
+ return unknown(base);
192
+ }
193
+ },
194
+ },
195
+ };
196
+ }
197
+ function neutralDecline(decline) {
198
+ const hints = decline.neutralReasonHints ?? [];
199
+ return { code: classifyPaymentFailure({ adviceCode: decline.adviceCode, neutralReasonHints: hints }).failureClass, hints,
200
+ ...(decline.adviceCode && isPaymentRetryAdviceCode(decline.adviceCode) ? { adviceCode: decline.adviceCode } : {}) };
201
+ }
202
+ function validAt(certificate, instant) {
203
+ return Number.isFinite(instant) && Date.parse(certificate.validFrom) <= instant && instant <= Date.parse(certificate.validTo);
204
+ }
205
+ function unknown(base) {
206
+ return { ...base, kind: "unknown", reason: "indeterminate" };
207
+ }
208
+ /** All donor outcomes outside the current neutral union are refused here. */
209
+ function mapTpayOutcome(event) {
210
+ switch (event) {
211
+ case "payment.succeeded": return "succeeded";
212
+ case "payment.failed": return "failed";
213
+ case "payment.requires_action": return "pending";
214
+ case "payment.refunded": return "refunded";
215
+ default: return null;
216
+ }
217
+ }
218
+ function mapVerifiedTpayEvent(rawBody, occurred, mode, supportedCurrencies) {
219
+ const webhook = normalizeTpayWebhook(rawBody);
220
+ const event = webhook.event;
221
+ const payload = event.raw_payload;
222
+ if (payload.testMode !== null && payload.testMode !== undefined
223
+ && payload.testMode !== (mode === "test" ? "1" : "0"))
224
+ return refusal("unsupported-event");
225
+ const identity = { provider: "tpay", providerOccurredAt: occurred };
226
+ let evidence;
227
+ if (payload.eventKind === "blik_alias") {
228
+ const kinds = {
229
+ method_registered: "registered", method_updated: "updated", method_revoked: "revoked", method_expired: "expired",
230
+ };
231
+ const outcome = kinds[webhook.methodLifecycleKind ?? ""];
232
+ if (!outcome)
233
+ return refusal("unsupported-event");
234
+ const lifecycle = occurred !== null ? toNormalizedProviderTpayWebhook(webhook, occurred).methodLifecycle : null;
235
+ evidence = { ...identity, kind: "method", outcome, providerEventId: `${event.provider_event_id}:${outcome}`,
236
+ methodRef: event.payment_provider_id, setupRef: null, intentRef: event.payment_intent_id ?? null,
237
+ ...(lifecycle ? { methodLifecycle: lifecycle } : {}) };
238
+ }
239
+ else {
240
+ const outcome = mapTpayOutcome(event.event_type);
241
+ if (!outcome)
242
+ return refusal("unsupported-event");
243
+ const amount = event.amount_minor;
244
+ const reportedCurrency = payload.currency;
245
+ if (reportedCurrency == null && supportedCurrencies.length !== 1)
246
+ return refusal("malformed");
247
+ // Currency binds to the adapter's declared single supported currency because
248
+ // attempts in any other currency are refused before dispatch; this is not a silent default.
249
+ const currency = reportedCurrency ?? supportedCurrencies[0];
250
+ const money = Number.isSafeInteger(amount) && amount > 0 && typeof currency === "string" && /^[A-Za-z]{3}$/u.test(currency)
251
+ ? { amountMinor: amount, currency: currency.toUpperCase() } : null;
252
+ const originalAmount = typeof payload.transactionAmount === "string"
253
+ ? Math.round(Number(payload.transactionAmount.replace(",", ".")) * 100) : NaN;
254
+ if (outcome === "refunded" && (!money || !Number.isSafeInteger(originalAmount) || originalAmount <= 0))
255
+ return refusal("malformed");
256
+ evidence = { ...identity, kind: "money", providerEventId: `${event.payment_provider_id}:${outcome}`,
257
+ providerPaymentRef: event.payment_provider_id, intentRef: event.payment_intent_id ?? null, attemptRef: null, outcome, money,
258
+ ...(outcome === "refunded" && money ? { refund: { kind: "full-reversal", reason: "chargeback",
259
+ operationId: `${event.payment_provider_id}:chargeback`, originalPaymentMoney: { amountMinor: originalAmount, currency: money.currency } } } : {}),
260
+ };
261
+ }
262
+ return { ...evidence, fingerprint: createHash("sha256").update(JSON.stringify(evidence)).digest("hex"),
263
+ providerAuditPayload: payload };
264
+ }
265
+ /**
266
+ * @experimental Reference-application support surface; may change.
267
+ * Reference consumer: `server/adapters/tpay/tpayAdapterFactory.ts`.
268
+ */
269
+ export { createTpayPaymentExecutionAdapter } from "./tpayPaymentExecutionAdapter.js";
270
+ /**
271
+ * @experimental Reference-application support surface; may change.
272
+ * Reference consumer: `server/runtime/paymentProviderReadbackRegistry.ts`.
273
+ */
274
+ export { createTpayPaymentReconciliationProvider } from "./tpayPaymentReconciliationProvider.js";
275
+ /**
276
+ * @experimental Reference-application support surface; may change.
277
+ * Reference consumer: `server/bff/commerce/tpay-channels.ts`.
278
+ */
279
+ export { createTpayHttpClient } from "./tpayHttpClient.js";
280
+ /**
281
+ * @experimental Reference-application support surface; may change.
282
+ * Reference consumer: `server/adapters/paymentRecoveryGuidance.ts`.
283
+ */
284
+ export { normalizeTpayFailureEvidence } from "./tpayFailureEvidence.js";
285
+ /**
286
+ * @experimental Reference-application support surface; may change.
287
+ * Reference consumer: `server/adapters/supabase/payment/tpaySubscriptionActivation.ts`.
288
+ */
289
+ export { aliasValue } from "./tpayCreateTransactionInput.js";
290
+ /**
291
+ * @experimental Reference-application support surface; may change.
292
+ * Reference consumer: `server/shared/preparedProviderAttempt.ts`.
293
+ */
294
+ export { ProviderAttemptPreDispatchError } from "./preparedProviderAttempt.js";
@@ -0,0 +1,26 @@
1
+ import type { PaymentRecoveryMethod, PaymentRecoveryOperation } from "@openlup/core/payment";
2
+ export type FailureObservationSource = "execution" | "webhook" | "readback";
3
+ export type FailureObservationDisposition = "present" | "absent" | "unreadable" | "read_failed" | "read_timeout";
4
+ /** Protected provider facts, not a customer message or a retry-policy input. */
5
+ export interface PaymentFailureEvidence {
6
+ version: 1;
7
+ source: FailureObservationSource;
8
+ disposition: FailureObservationDisposition;
9
+ refusalVerified: boolean;
10
+ providerPaymentId: string | null;
11
+ providerChargeId: string | null;
12
+ code: string | null;
13
+ declineCode: string | null;
14
+ adviceCode: string | null;
15
+ adviceOrigin: "provider" | "inferred" | "unknown";
16
+ operation: PaymentRecoveryOperation | null;
17
+ method: PaymentRecoveryMethod | null;
18
+ methodSource: "provider" | "execution_contract" | "unknown";
19
+ }
20
+ /** Bounded machine tokens only. Free-form response fields never cross this seam. */
21
+ export declare function evidenceToken(value: unknown): string | null;
22
+ export declare function evidenceRecord(value: unknown): Record<string, unknown>;
23
+ /** Explicit construction prevents accidental payload expansion by object spreading. */
24
+ export declare function serializeFailureEvidence(input: PaymentFailureEvidence): PaymentFailureEvidence;
25
+ /** Persist beside legacy fields; never merge the diagnostic codes into them. */
26
+ export declare function failureEvidencePayload(evidence: PaymentFailureEvidence | null | undefined): Record<string, unknown>;
@@ -0,0 +1,33 @@
1
+ /** Bounded machine tokens only. Free-form response fields never cross this seam. */
2
+ export function evidenceToken(value) {
3
+ return typeof value === "string" && /^[a-zA-Z0-9_:.-]{1,96}$/.test(value) ? value : null;
4
+ }
5
+ export function evidenceRecord(value) {
6
+ return value !== null && typeof value === "object" && !Array.isArray(value)
7
+ ? value : {};
8
+ }
9
+ /** Explicit construction prevents accidental payload expansion by object spreading. */
10
+ export function serializeFailureEvidence(input) {
11
+ const method = input.method;
12
+ const kind = evidenceToken(method?.kind);
13
+ const recoveryMethodKey = evidenceToken(method?.recoveryMethodKey);
14
+ return {
15
+ version: 1,
16
+ source: input.source,
17
+ disposition: input.disposition,
18
+ refusalVerified: input.refusalVerified,
19
+ providerPaymentId: evidenceToken(input.providerPaymentId),
20
+ providerChargeId: evidenceToken(input.providerChargeId),
21
+ code: evidenceToken(input.code),
22
+ declineCode: evidenceToken(input.declineCode),
23
+ adviceCode: evidenceToken(input.adviceCode),
24
+ adviceOrigin: input.adviceOrigin,
25
+ operation: input.operation,
26
+ method: kind && recoveryMethodKey && method ? { kind, recoveryMethodKey, interaction: method.interaction } : null,
27
+ methodSource: kind && recoveryMethodKey ? input.methodSource : "unknown",
28
+ };
29
+ }
30
+ /** Persist beside legacy fields; never merge the diagnostic codes into them. */
31
+ export function failureEvidencePayload(evidence) {
32
+ return evidence ? { failureEvidence: serializeFailureEvidence(evidence) } : {};
33
+ }
@@ -0,0 +1,22 @@
1
+ export interface TrustedProviderAttemptPreDispatchFailure {
2
+ phase: "oauth" | "transaction_dispatch";
3
+ code: "tpay_oauth_failed" | "tpay_oauth_timeout" | "tpay_oauth_invalid_response" | "tpay_request_encode_failed" | "tpay_request_deadline_exhausted";
4
+ dispatchState: "not_dispatched";
5
+ }
6
+ /** Either proof the provider holds no transaction: the POST was never invoked, or it was and the
7
+ * provider refused the request document. Two closed shapes, so neither absorbs the other. */
8
+ export type TrustedProviderAttemptNoChargeFailure = TrustedProviderAttemptPreDispatchFailure | {
9
+ phase: "response_decode";
10
+ code: "tpay_request_refused";
11
+ dispatchState: "refused";
12
+ };
13
+ export declare class ProviderAttemptPreDispatchError extends Error {
14
+ readonly phase: TrustedProviderAttemptNoChargeFailure["phase"];
15
+ readonly code: TrustedProviderAttemptNoChargeFailure["code"];
16
+ readonly dispatchState: TrustedProviderAttemptNoChargeFailure["dispatchState"];
17
+ paymentAttemptId: string | null;
18
+ retryCount: number;
19
+ constructor(failure: TrustedProviderAttemptNoChargeFailure, paymentAttemptId?: string | null);
20
+ withPaymentAttempt(paymentAttemptId: string): this;
21
+ withRetryCount(retryCount: number): this;
22
+ }
@@ -0,0 +1,24 @@
1
+ export class ProviderAttemptPreDispatchError extends Error {
2
+ phase;
3
+ code;
4
+ dispatchState;
5
+ paymentAttemptId;
6
+ retryCount = 0;
7
+ constructor(failure, paymentAttemptId = null) {
8
+ const kind = failure.dispatchState === "refused" ? "provider_refused_pre_transaction" : "provider_pre_dispatch_failed";
9
+ super(`${kind}:${failure.phase}:${failure.code}`);
10
+ this.name = "ProviderAttemptPreDispatchError";
11
+ this.phase = failure.phase;
12
+ this.code = failure.code;
13
+ this.dispatchState = failure.dispatchState;
14
+ this.paymentAttemptId = paymentAttemptId;
15
+ }
16
+ withPaymentAttempt(paymentAttemptId) {
17
+ this.paymentAttemptId = paymentAttemptId;
18
+ return this;
19
+ }
20
+ withRetryCount(retryCount) {
21
+ this.retryCount = retryCount;
22
+ return this;
23
+ }
24
+ }
@@ -0,0 +1,3 @@
1
+ export declare function loggableProviderRequestId(value: unknown): value is string;
2
+ export declare function loggableProviderErrorCode(value: unknown): value is string;
3
+ export declare function loggableProviderFieldName(value: unknown): value is string;
@@ -0,0 +1,95 @@
1
+ const TPAY_REQUEST_ID = /^[a-f0-9]{20}$/;
2
+ const LOGGABLE_PROVIDER_ERROR_CODES = new Set([
3
+ "bank_group_does_not_exist",
4
+ "blik_token_not_present",
5
+ "cannot_create_transaction",
6
+ "channel_passed_with_group",
7
+ "channel_unavailable",
8
+ "channel_unavailable_for_source",
9
+ "configuration_error",
10
+ "country_not_valid",
11
+ "crc_not_unique",
12
+ "field_required",
13
+ "general",
14
+ "incorrect_content_type",
15
+ "internal_server_error",
16
+ "invalid_request_body",
17
+ "is_required",
18
+ "no_payment_data",
19
+ "not_array",
20
+ "not_natural_int",
21
+ "not_string",
22
+ "not_valid",
23
+ "partner_cannot_create_transaction",
24
+ "payment_failed",
25
+ "too_low",
26
+ "transaction_blocked",
27
+ "transaction_lock",
28
+ "ts:channel:amount_outside_range",
29
+ "ts:channel:both_channel_and_group_specified",
30
+ "ts:channel:channel_blocked",
31
+ "ts:channel:channel_disabled",
32
+ "ts:channel:channel_disabled_for_pos",
33
+ "ts:channel:collect_prohibited",
34
+ "ts:channel:group_disabled_for_pos",
35
+ "ts:channel:not_available",
36
+ "ts:channel:not_found",
37
+ "ts:channel:source_prohibited",
38
+ "ts:channel:surcharge_prohibited",
39
+ "ts:country:not_valid",
40
+ "ts:merchant:disabled",
41
+ "ts:merchant:is_partner",
42
+ "ts:merchant:not_found",
43
+ "ts:payer:phone:not_valid",
44
+ "ts:pos:not_found",
45
+ "ts:pos:result_url_not_overridable",
46
+ "ts:request:no_ip",
47
+ "ts:transaction:not_inserted",
48
+ "ts:transaction:payer_data_not_inserted",
49
+ "ts:transaction:payer_info_not_inserted",
50
+ "unexpected_field",
51
+ "unsupported_blik_token",
52
+ "url_override_is_disabled",
53
+ "wrong_alias",
54
+ "wrong_autopayment_alias",
55
+ "wrong_model_data_combination",
56
+ ]);
57
+ const LOGGABLE_PROVIDER_FIELD_NAMES = new Set([
58
+ "amount",
59
+ "currency",
60
+ "description",
61
+ "hiddenDescription",
62
+ "payer.email",
63
+ "payer.name",
64
+ "payer.phone",
65
+ "payer.address",
66
+ "payer.city",
67
+ "payer.country",
68
+ "payer.postalCode",
69
+ // This list carries FIELD NAMES ONLY - `ProviderAttemptFailureDiagnostic` has
70
+ // no field that holds a provider value - so these two entries log the literal
71
+ // strings, never a user agent or an IP address. Their absence is why the
72
+ // 2026-08-28 BLIK outage logged `fieldNames: []` and stayed unattributed for
73
+ // days: `readTpayHttpFailure` filters Tpay's returned `fieldName` through this
74
+ // set, so an unlisted name is silently dropped.
75
+ "payer.ip",
76
+ "payer.userAgent",
77
+ "pay.groupId",
78
+ "pay.channelId",
79
+ "pay.blikPaymentData.blikToken",
80
+ "pay.blikPaymentData.type",
81
+ "pay.blikPaymentData.aliases.value",
82
+ "pay.blikPaymentData.aliases.type",
83
+ "callbacks.notification.url",
84
+ "callbacks.payerUrls.success",
85
+ "callbacks.payerUrls.error",
86
+ ]);
87
+ export function loggableProviderRequestId(value) {
88
+ return typeof value === "string" && TPAY_REQUEST_ID.test(value);
89
+ }
90
+ export function loggableProviderErrorCode(value) {
91
+ return typeof value === "string" && LOGGABLE_PROVIDER_ERROR_CODES.has(value);
92
+ }
93
+ export function loggableProviderFieldName(value) {
94
+ return typeof value === "string" && LOGGABLE_PROVIDER_FIELD_NAMES.has(value);
95
+ }
@@ -0,0 +1,25 @@
1
+ import type { PaymentExecutionInput, TpayTransientProviderInput } from "./donorContracts.js";
2
+ import type { TpayPayerInput, TpayTransactionCreateInput } from "./tpayHttpClient.js";
3
+ export declare function buildCreateTransactionInput(input: {
4
+ input: PaymentExecutionInput;
5
+ transient: TpayTransientProviderInput;
6
+ payer: TpayPayerInput;
7
+ notificationUrl: string;
8
+ successUrl: string;
9
+ errorUrl: string;
10
+ aliasRegistrationOnCodeEnabled: boolean;
11
+ modelOActivationEnabled: boolean;
12
+ }): TpayTransactionCreateInput;
13
+ /**
14
+ * The autopayment model actually sent to Tpay.
15
+ *
16
+ * Once the Model O activation switch is on, every recurring activation registers
17
+ * as model O: it is the only model that permits a variable amount without the
18
+ * payer confirming each charge, which is what a configurable subscription needs.
19
+ * Model M would silently degrade every renewal into an approval prompt.
20
+ *
21
+ * Single source of truth on purpose — the new-activation switch and the alias
22
+ * payload must never disagree about which model this activation attempt is.
23
+ */
24
+ export declare function resolveRecurringModel(requested: "O" | "M" | undefined, modelOActivationEnabled: boolean): "O" | "M";
25
+ export declare function aliasValue(paymentIntentId: string): string;
@@ -0,0 +1,128 @@
1
+ const TPAY_BLIK_GROUP_ID = 150;
2
+ /**
3
+ * In-shell account return path (account terminal, localized client-side). When the
4
+ * checkout declares `returnContext: "account"`, the Tpay redirect/return targets
5
+ * this in-account terminal instead of the public `/checkout/payment`
6
+ * page — the origin is preserved, only the path is swapped. Mirrors how the
7
+ * public path is selected server-side and re-localized on the client.
8
+ */
9
+ const ACCOUNT_RETURN_PATH = "/account/order/status";
10
+ /**
11
+ * Swap the path of an origin-derived return URL to the in-account terminal while
12
+ * preserving the env/origin-driven host. `returnContext` defaults to public, so a
13
+ * caller that omits it gets the byte-identical public URL.
14
+ */
15
+ function returnUrlForContext(baseUrl, returnContext) {
16
+ if (returnContext !== "account")
17
+ return baseUrl;
18
+ const url = new URL(baseUrl);
19
+ url.pathname = ACCOUNT_RETURN_PATH;
20
+ return url.toString();
21
+ }
22
+ export function buildCreateTransactionInput(input) {
23
+ const { transient } = input;
24
+ const effectiveModel = resolveRecurringModel(transient.recurringModel, input.modelOActivationEnabled);
25
+ const shouldRegisterOneTimeAlias = transient.flow === "blik_one_time" && input.aliasRegistrationOnCodeEnabled;
26
+ const returnContext = input.input.returnContext;
27
+ const successUrl = returnUrlForContext(input.successUrl, returnContext);
28
+ const errorUrl = returnUrlForContext(input.errorUrl, returnContext);
29
+ return {
30
+ amount: input.input.amountMinor / 100,
31
+ currency: "PLN",
32
+ description: input.input.orderRef,
33
+ hiddenDescription: input.input.paymentIntentId,
34
+ payer: input.payer,
35
+ notificationUrl: input.notificationUrl,
36
+ successUrl: payerReturnUrl(successUrl, input.input),
37
+ errorUrl: payerReturnUrl(errorUrl, input.input),
38
+ groupId: transient.flow === "pbl_one_time" ? undefined : TPAY_BLIK_GROUP_ID,
39
+ channelId: transient.flow === "pbl_one_time" ? transient.channelId : undefined,
40
+ blikPaymentType: transient.flow === "recurring_charge" ||
41
+ (transient.flow === "blik_one_click" && (input.input.paymentMethodAliasType ?? "PAYID") === "PAYID")
42
+ ? 2
43
+ : undefined,
44
+ blikToken: transient.flow === "blik_one_time" || transient.flow === "blik_recurring_activation"
45
+ ? transient.blikToken
46
+ : undefined,
47
+ // Only meaningful while registering a PAYID mandate: it makes BLIK refuse an
48
+ // incapable bank outright instead of taking the money and silently never
49
+ // showing the mandate invitation. Never set for a plain one-off BLIK.
50
+ refuseNoPayId: transient.flow === "blik_recurring_activation" && effectiveModel === "O"
51
+ ? true
52
+ : undefined,
53
+ alias: transient.flow === "blik_recurring_activation"
54
+ ? {
55
+ value: aliasValue(input.input.paymentIntentId),
56
+ type: "PAYID",
57
+ label: "openlup",
58
+ ...(effectiveModel === "O" ? { recommendedAuthLevel: "NOCONFREQ" } : {}),
59
+ autopayment: { model: effectiveModel },
60
+ }
61
+ : shouldRegisterOneTimeAlias
62
+ ? { value: aliasValue(input.input.paymentIntentId), type: "UID", label: "openlup" }
63
+ : (transient.flow === "recurring_charge" || transient.flow === "blik_one_click") && input.input.paymentMethodRef
64
+ ? chargeAlias(input.input.paymentMethodRef, input.input.paymentMethodAliasType ?? "PAYID", input.input.paymentMethodRecurringModel)
65
+ : null,
66
+ simulatorContext: {
67
+ orderId: input.input.orderId,
68
+ paymentIntentId: input.input.paymentIntentId,
69
+ clientId: input.input.clientId,
70
+ providerAttemptKey: input.input.providerIdempotencyKey,
71
+ ...(returnContext === "account" ? { returnPath: ACCOUNT_RETURN_PATH } : {}),
72
+ },
73
+ };
74
+ }
75
+ /**
76
+ * Alias sent when charging an already-registered mandate.
77
+ *
78
+ * `noDelay` and `recommendedAuthLevel` are autopayment fields: they belong to a
79
+ * `PAYID` mandate under model O and are meaningless on a `UID` one-click alias,
80
+ * which carries no autopayment agreement at all.
81
+ *
82
+ * Keyed on the model the MANDATE was registered under, never on the switch that
83
+ * admits new Model O activations. Production may already hold model M mandates;
84
+ * charging those as model O would both contradict the payer's consent and risk
85
+ * rejection. Unknown or absent means not-model-O — fail closed, never guess.
86
+ */
87
+ function chargeAlias(value, type, registeredModel) {
88
+ const autopaymentCharge = registeredModel === "O" && type === "PAYID";
89
+ return {
90
+ value,
91
+ type,
92
+ // Needed on every model O charge, not only at registration: without them
93
+ // Tpay emails the payer an approval link instead of collecting, which is
94
+ // model M behaviour. Verified against production 2026-07-20.
95
+ ...(autopaymentCharge ? { noDelay: true, recommendedAuthLevel: "NOCONFREQ" } : {}),
96
+ };
97
+ }
98
+ /**
99
+ * The autopayment model actually sent to Tpay.
100
+ *
101
+ * Once the Model O activation switch is on, every recurring activation registers
102
+ * as model O: it is the only model that permits a variable amount without the
103
+ * payer confirming each charge, which is what a configurable subscription needs.
104
+ * Model M would silently degrade every renewal into an approval prompt.
105
+ *
106
+ * Single source of truth on purpose — the new-activation switch and the alias
107
+ * payload must never disagree about which model this activation attempt is.
108
+ */
109
+ export function resolveRecurringModel(requested, modelOActivationEnabled) {
110
+ if (modelOActivationEnabled)
111
+ return "O";
112
+ return requested ?? "M";
113
+ }
114
+ function payerReturnUrl(baseUrl, input) {
115
+ const url = new URL(baseUrl);
116
+ if (input.orderId)
117
+ url.searchParams.set("orderId", input.orderId);
118
+ url.searchParams.set("paymentIntentId", input.paymentIntentId);
119
+ if (input.clientId)
120
+ url.searchParams.set("clientId", input.clientId);
121
+ if (input.returnContext === "account" && input.subjectId)
122
+ url.searchParams.set("subject", input.subjectId);
123
+ url.searchParams.set("order", input.orderRef);
124
+ return url.toString();
125
+ }
126
+ export function aliasValue(paymentIntentId) {
127
+ return `openlup_${paymentIntentId}`;
128
+ }