@openlup/platform 0.20.0 → 0.21.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 (210) hide show
  1. package/CHANGELOG.md +62 -231
  2. package/README.md +172 -6
  3. package/dist/composition/composeBundle.d.ts +6 -0
  4. package/dist/composition/composeBundle.js +2 -0
  5. package/dist/composition/index.js +1 -0
  6. package/dist/payments/adapterConformance.d.ts +8 -0
  7. package/dist/payments/adapterConformance.js +109 -0
  8. package/dist/payments/bindings.d.ts +3 -0
  9. package/dist/payments/bindings.js +17 -0
  10. package/dist/payments/checkoutActivePaymentActionResolver.d.ts +7 -0
  11. package/dist/payments/checkoutActivePaymentActionResolver.js +77 -0
  12. package/dist/payments/checkoutRecoveryPaymentResolver.d.ts +8 -0
  13. package/dist/payments/checkoutRecoveryPaymentResolver.js +87 -0
  14. package/dist/payments/contracts/adapter.d.ts +113 -0
  15. package/dist/payments/contracts/adapter.js +1 -0
  16. package/dist/payments/contracts/conformance.d.ts +155 -0
  17. package/dist/payments/contracts/conformance.js +1 -0
  18. package/dist/payments/contracts/evidence.d.ts +157 -0
  19. package/dist/payments/contracts/evidence.js +1 -0
  20. package/dist/payments/contracts/index.d.ts +10 -0
  21. package/dist/payments/contracts/index.js +1 -0
  22. package/dist/payments/contracts/preparedAttempt.d.ts +69 -0
  23. package/dist/payments/contracts/preparedAttempt.js +1 -0
  24. package/dist/payments/contracts/reconciliation.d.ts +303 -0
  25. package/dist/payments/contracts/reconciliation.js +1 -0
  26. package/dist/payments/contracts/repair.d.ts +92 -0
  27. package/dist/payments/contracts/repair.js +1 -0
  28. package/dist/payments/contracts/truth.d.ts +92 -0
  29. package/dist/payments/contracts/truth.js +1 -0
  30. package/dist/payments/eventConformanceRunner.d.ts +12 -0
  31. package/dist/payments/eventConformanceRunner.js +125 -0
  32. package/dist/payments/index.d.ts +16 -0
  33. package/dist/payments/index.js +16 -0
  34. package/dist/payments/paymentAttemptSilenceWindow.d.ts +10 -0
  35. package/dist/payments/paymentAttemptSilenceWindow.js +19 -0
  36. package/dist/payments/paymentMethodLifecycle.d.ts +11 -0
  37. package/dist/payments/paymentMethodLifecycle.js +37 -0
  38. package/dist/payments/paymentProviderReconciliationAbsenceReopen.d.ts +7 -0
  39. package/dist/payments/paymentProviderReconciliationAbsenceReopen.js +100 -0
  40. package/dist/payments/paymentProviderReconciliationEvidence.d.ts +36 -0
  41. package/dist/payments/paymentProviderReconciliationEvidence.js +91 -0
  42. package/dist/payments/paymentProviderReconciliationWorker.d.ts +4 -0
  43. package/dist/payments/paymentProviderReconciliationWorker.js +245 -0
  44. package/dist/payments/paymentTruth.d.ts +8 -0
  45. package/dist/payments/paymentTruth.js +125 -0
  46. package/dist/payments/paymentVerifyNowHandler.d.ts +27 -0
  47. package/dist/payments/paymentVerifyNowHandler.js +70 -0
  48. package/dist/payments/paymentVerifyNowService.d.ts +23 -0
  49. package/dist/payments/paymentVerifyNowService.js +93 -0
  50. package/dist/payments/paymentWebhookPostProcessing.d.ts +22 -0
  51. package/dist/payments/paymentWebhookPostProcessing.js +15 -0
  52. package/dist/payments/postgresPaymentTruth.d.ts +10 -0
  53. package/dist/payments/postgresPaymentTruth.js +126 -0
  54. package/dist/payments/preparedAttempt.d.ts +6 -0
  55. package/dist/payments/preparedAttempt.js +70 -0
  56. package/dist/payments/preparedAttemptFinalization.d.ts +5 -0
  57. package/dist/payments/preparedAttemptFinalization.js +12 -0
  58. package/dist/payments/schemaCapabilities.d.ts +10 -0
  59. package/dist/payments/schemaCapabilities.js +10 -0
  60. package/dist/payments/sql.d.ts +20 -0
  61. package/dist/payments/sql.js +6 -0
  62. package/dist/payments/stripe/classifyDecline.d.ts +46 -0
  63. package/dist/payments/stripe/classifyDecline.js +58 -0
  64. package/dist/payments/stripe/declineFailureHints.d.ts +46 -0
  65. package/dist/payments/stripe/declineFailureHints.js +87 -0
  66. package/dist/payments/stripe/donorContracts.d.ts +120 -0
  67. package/dist/payments/stripe/donorContracts.js +1 -0
  68. package/dist/payments/stripe/executionDecline.d.ts +53 -0
  69. package/dist/payments/stripe/executionDecline.js +107 -0
  70. package/dist/payments/stripe/index.d.ts +16 -0
  71. package/dist/payments/stripe/index.js +239 -0
  72. package/dist/payments/stripe/intentMapper.d.ts +26 -0
  73. package/dist/payments/stripe/intentMapper.js +51 -0
  74. package/dist/payments/stripe/methodFacts.d.ts +10 -0
  75. package/dist/payments/stripe/methodFacts.js +12 -0
  76. package/dist/payments/stripe/paymentFailureEvidence.d.ts +26 -0
  77. package/dist/payments/stripe/paymentFailureEvidence.js +33 -0
  78. package/dist/payments/stripe/stripeFailureEvidence.d.ts +41 -0
  79. package/dist/payments/stripe/stripeFailureEvidence.js +82 -0
  80. package/dist/payments/stripe/stripeIntentConfirmationEvidence.d.ts +28 -0
  81. package/dist/payments/stripe/stripeIntentConfirmationEvidence.js +32 -0
  82. package/dist/payments/stripe/stripePaymentReconciliationProvider.d.ts +35 -0
  83. package/dist/payments/stripe/stripePaymentReconciliationProvider.js +222 -0
  84. package/dist/payments/stripe/stripeRefundEvidence.d.ts +8 -0
  85. package/dist/payments/stripe/stripeRefundEvidence.js +17 -0
  86. package/dist/payments/stripe/stripeSandboxPaymentExecutionAdapter.d.ts +45 -0
  87. package/dist/payments/stripe/stripeSandboxPaymentExecutionAdapter.js +220 -0
  88. package/dist/payments/stripe/stripeWebhookNormalizer.d.ts +14 -0
  89. package/dist/payments/stripe/stripeWebhookNormalizer.js +243 -0
  90. package/dist/payments/stripe/webhookSignature.d.ts +14 -0
  91. package/dist/payments/stripe/webhookSignature.js +43 -0
  92. package/dist/payments/tpay/blikRecurringMapper.d.ts +37 -0
  93. package/dist/payments/tpay/blikRecurringMapper.js +24 -0
  94. package/dist/payments/tpay/capabilities.d.ts +3 -0
  95. package/dist/payments/tpay/capabilities.js +18 -0
  96. package/dist/payments/tpay/classifyDecline.d.ts +46 -0
  97. package/dist/payments/tpay/classifyDecline.js +58 -0
  98. package/dist/payments/tpay/declineFailureHints.d.ts +72 -0
  99. package/dist/payments/tpay/declineFailureHints.js +37 -0
  100. package/dist/payments/tpay/donorContracts.d.ts +140 -0
  101. package/dist/payments/tpay/donorContracts.js +1 -0
  102. package/dist/payments/tpay/index.d.ts +43 -0
  103. package/dist/payments/tpay/index.js +264 -0
  104. package/dist/payments/tpay/paymentFailureEvidence.d.ts +26 -0
  105. package/dist/payments/tpay/paymentFailureEvidence.js +33 -0
  106. package/dist/payments/tpay/preparedProviderAttempt.d.ts +22 -0
  107. package/dist/payments/tpay/preparedProviderAttempt.js +24 -0
  108. package/dist/payments/tpay/providerAttemptFailureDiagnostic.d.ts +3 -0
  109. package/dist/payments/tpay/providerAttemptFailureDiagnostic.js +95 -0
  110. package/dist/payments/tpay/tpayCreateTransactionInput.d.ts +25 -0
  111. package/dist/payments/tpay/tpayCreateTransactionInput.js +128 -0
  112. package/dist/payments/tpay/tpayDeclineEvidence.d.ts +126 -0
  113. package/dist/payments/tpay/tpayDeclineEvidence.js +177 -0
  114. package/dist/payments/tpay/tpayDispatchFailure.d.ts +60 -0
  115. package/dist/payments/tpay/tpayDispatchFailure.js +121 -0
  116. package/dist/payments/tpay/tpayFailureEvidence.d.ts +20 -0
  117. package/dist/payments/tpay/tpayFailureEvidence.js +61 -0
  118. package/dist/payments/tpay/tpayHttpClient.d.ts +52 -0
  119. package/dist/payments/tpay/tpayHttpClient.js +185 -0
  120. package/dist/payments/tpay/tpayHttpFailure.d.ts +57 -0
  121. package/dist/payments/tpay/tpayHttpFailure.js +143 -0
  122. package/dist/payments/tpay/tpayPaymentExecutionAdapter.d.ts +20 -0
  123. package/dist/payments/tpay/tpayPaymentExecutionAdapter.js +249 -0
  124. package/dist/payments/tpay/tpayPaymentReconciliationProvider.d.ts +5 -0
  125. package/dist/payments/tpay/tpayPaymentReconciliationProvider.js +244 -0
  126. package/dist/payments/tpay/tpayTransactionPayload.d.ts +120 -0
  127. package/dist/payments/tpay/tpayTransactionPayload.js +96 -0
  128. package/dist/payments/tpay/wallClockTime.d.ts +31 -0
  129. package/dist/payments/tpay/wallClockTime.js +57 -0
  130. package/dist/payments/tpay/webhookJwsVerifier.d.ts +17 -0
  131. package/dist/payments/tpay/webhookJwsVerifier.js +51 -0
  132. package/dist/payments/tpay/webhookNormalizer.d.ts +54 -0
  133. package/dist/payments/tpay/webhookNormalizer.js +200 -0
  134. package/dist/payments/verifiedEvidenceProjection.d.ts +12 -0
  135. package/dist/payments/verifiedEvidenceProjection.js +166 -0
  136. package/dist/payments/verifiedIngress.d.ts +119 -0
  137. package/dist/payments/verifiedIngress.js +220 -0
  138. package/package.json +22 -2
  139. package/release-gates.json +64 -3
  140. package/sql/20260810180000_payment_settlement_rail.sql +574 -0
  141. package/sql/20260815150000_payment_truth_reconciliation.sql +479 -0
  142. package/sql/20260826100000_late_payment_shifts_the_subscription_cycle.sql +215 -0
  143. package/src/composition/composeBundle.ts +9 -0
  144. package/src/composition/index.ts +1 -0
  145. package/src/payments/adapterConformance.ts +118 -0
  146. package/src/payments/bindings.ts +17 -0
  147. package/src/payments/checkoutActivePaymentActionResolver.ts +97 -0
  148. package/src/payments/checkoutRecoveryPaymentResolver.ts +108 -0
  149. package/src/payments/contracts/adapter.ts +96 -0
  150. package/src/payments/contracts/conformance.ts +171 -0
  151. package/src/payments/contracts/evidence.ts +142 -0
  152. package/src/payments/contracts/index.ts +54 -0
  153. package/src/payments/contracts/preparedAttempt.ts +86 -0
  154. package/src/payments/contracts/reconciliation.ts +296 -0
  155. package/src/payments/contracts/repair.ts +95 -0
  156. package/src/payments/contracts/truth.ts +108 -0
  157. package/src/payments/eventConformanceRunner.ts +149 -0
  158. package/src/payments/index.ts +21 -0
  159. package/src/payments/paymentAttemptSilenceWindow.ts +26 -0
  160. package/src/payments/paymentMethodLifecycle.ts +50 -0
  161. package/src/payments/paymentProviderReconciliationAbsenceReopen.ts +130 -0
  162. package/src/payments/paymentProviderReconciliationEvidence.ts +117 -0
  163. package/src/payments/paymentProviderReconciliationWorker.ts +291 -0
  164. package/src/payments/paymentTruth.ts +131 -0
  165. package/src/payments/paymentVerifyNowHandler.ts +94 -0
  166. package/src/payments/paymentVerifyNowService.ts +125 -0
  167. package/src/payments/paymentWebhookPostProcessing.ts +36 -0
  168. package/src/payments/postgresPaymentTruth.ts +149 -0
  169. package/src/payments/preparedAttempt.ts +90 -0
  170. package/src/payments/preparedAttemptFinalization.ts +13 -0
  171. package/src/payments/schemaCapabilities.ts +10 -0
  172. package/src/payments/sql.ts +6 -0
  173. package/src/payments/stripe/classifyDecline.ts +62 -0
  174. package/src/payments/stripe/declineFailureHints.ts +89 -0
  175. package/src/payments/stripe/donorContracts.ts +87 -0
  176. package/src/payments/stripe/executionDecline.ts +126 -0
  177. package/src/payments/stripe/index.ts +235 -0
  178. package/src/payments/stripe/intentMapper.ts +58 -0
  179. package/src/payments/stripe/methodFacts.ts +21 -0
  180. package/src/payments/stripe/paymentFailureEvidence.ts +58 -0
  181. package/src/payments/stripe/stripeFailureEvidence.ts +115 -0
  182. package/src/payments/stripe/stripeIntentConfirmationEvidence.ts +34 -0
  183. package/src/payments/stripe/stripePaymentReconciliationProvider.ts +263 -0
  184. package/src/payments/stripe/stripeRefundEvidence.ts +19 -0
  185. package/src/payments/stripe/stripeSandboxPaymentExecutionAdapter.ts +283 -0
  186. package/src/payments/stripe/stripeWebhookNormalizer.ts +277 -0
  187. package/src/payments/stripe/webhookSignature.ts +55 -0
  188. package/src/payments/tpay/blikRecurringMapper.ts +42 -0
  189. package/src/payments/tpay/capabilities.ts +21 -0
  190. package/src/payments/tpay/classifyDecline.ts +62 -0
  191. package/src/payments/tpay/declineFailureHints.ts +90 -0
  192. package/src/payments/tpay/donorContracts.ts +104 -0
  193. package/src/payments/tpay/index.ts +257 -0
  194. package/src/payments/tpay/paymentFailureEvidence.ts +58 -0
  195. package/src/payments/tpay/preparedProviderAttempt.ts +38 -0
  196. package/src/payments/tpay/providerAttemptFailureDiagnostic.ts +99 -0
  197. package/src/payments/tpay/tpayCreateTransactionInput.ts +158 -0
  198. package/src/payments/tpay/tpayDeclineEvidence.ts +233 -0
  199. package/src/payments/tpay/tpayDispatchFailure.ts +186 -0
  200. package/src/payments/tpay/tpayFailureEvidence.ts +75 -0
  201. package/src/payments/tpay/tpayHttpClient.ts +290 -0
  202. package/src/payments/tpay/tpayHttpFailure.ts +186 -0
  203. package/src/payments/tpay/tpayPaymentExecutionAdapter.ts +297 -0
  204. package/src/payments/tpay/tpayPaymentReconciliationProvider.ts +285 -0
  205. package/src/payments/tpay/tpayTransactionPayload.ts +207 -0
  206. package/src/payments/tpay/wallClockTime.ts +57 -0
  207. package/src/payments/tpay/webhookJwsVerifier.ts +82 -0
  208. package/src/payments/tpay/webhookNormalizer.ts +279 -0
  209. package/src/payments/verifiedEvidenceProjection.ts +179 -0
  210. package/src/payments/verifiedIngress.ts +317 -0
package/CHANGELOG.md CHANGED
@@ -4,10 +4,71 @@ Notable changes to this public-source workspace package are recorded here.
4
4
  “Unreleased” means no npm version carries the change yet; immutable OpenLup
5
5
  source previews may already ship some changes, and their release notes say
6
6
  which. Declaration snapshots track candidate drift, not a stable API or
7
- SemVer promise.
7
+ SemVer promise. This file keeps the current release and the two previous ones;
8
+ earlier releases are recorded verbatim in `packages/platform/CHANGELOG-HISTORY.md`
9
+ in the source repository.
8
10
 
9
11
  ## [Unreleased]
10
12
 
13
+ ## [0.21.0]
14
+
15
+ - Publishable on the npm `latest` dist-tag as `0.21.0`, from tag
16
+ `openlup-platform-v0.21.0`.
17
+ - Preview set: core/outbox unchanged, UI private `0.0.0`; published `0.20.0` size baselines, unchanged caps.
18
+
19
+ ### Cumulative M07 status: payment truth and repair surfaces complete for this wave
20
+
21
+ - Experimental payments/contracts/runtime: unchanged prepared ordering; neutral execution/evidence/readback/conformance, verified truth, first-receipt ingress, method lifecycle/reconciliation; host-owned persistence, translation/failure policy.
22
+ - Native PostgreSQL truth/settlement/later-only alignment; three unchanged sparse SQL projections, not an install chain. Refunds never cancel subscriptions.
23
+ - Repair/recovery/buyer verify inject rail capabilities; unknown rails refuse. Reference BFF retains mapping/schemas/ownership/auth order/transient callbacks. Browser/verify outcomes never prove payment success.
24
+ - Reference-application source (no new export), opt-in repair view: `commerce/paymentStatusClient`, `commerce/paymentVerifyClient`, `payment/components/PaymentStatusPoller`; stored paid alone confirms, redirects require HTTPS, collection/confirmation stay injected, unsupported setup is visible. Exact file/specifier guards refuse broad commerce/operator clients; source-import proof keeps default view unmounted.
25
+ - Truth services/one optional named binder use the host executor, no new schedule/executor. Selectors default-off: nothing activates by default. First-insert adapter fingerprint excludes receipt time/audit.
26
+ - Two experimental example adapter subpaths under `./payments/`, named in the README. Card-processor example: three frozen exports, injected host client/neutral ports; refund creation, split authorization/capture and exact absence unavailable; cancellation needs host support.
27
+ - Regional-processor example: host I/O, Model O aliases, verified observations, purpose-sensitive readback; check certificate validity/notification freshness. Both examples have no import I/O/default binding and await account TEST evidence.
28
+ - Example adapter advisory: mismatched nonzero refusal money stays unknown/manual; trusted document refusals mean customer data correction; configuration mismatches throw before I/O. Unpaid/transient no-dispatch classifications unchanged.
29
+ - Settling money missing/invalid/non-positive or blank currency refuses as amount mismatch; conformance requires positive money. Empty optional transients stop false leak refusals; JSON-escaped transients in diagnostics/readbacks/stored payloads and payer contact in execution results now refuse.
30
+ - Private types reuse canonical money/attempts; unused imports removed, runtime/API unchanged.
31
+
32
+ Migration: upgrade all `@openlup/*` `0.20.0` → `0.21.0` for exact pins. No schema migration; install the canonical catalogue, not sparse projections. Attempts add optional method selection/transient payer contact; contact is dispatch-only, never persisted/audited/logged/diagnosed. Missing adapter-required input requires exact before-dispatch `notDispatched`. Registration refs/kind/status require host `resolveMethodOwner` client/subscription ownership or are omitted. Lifecycle/normalized nested audit copies typed fields only; conformance scans all for secrets/contact. Adapter-filtered redacted audit never stores raw bodies; typed evidence overrides reader facts, audit bag fills untyped facts; identity/fingerprint exclude bag/receipt time. Other changes need no migration, and nothing rewires a host automatically.
33
+
34
+ Migration: F2 replaces assumed full refund with `require-evidence`: missing/null kind/amount/operation identity is durably recorded/acknowledged, not applied. Supply original money, stable operation identity and cumulative snapshot, event delta or explicit full reversal (`refund`/`chargeback`); full evidence stays valid. Former default needs `refundEvidencePolicy: "legacy-assume-full"`, also in the reference handler; no native parity implied.
35
+
36
+ Migration: D11 changes every caller of the reference payment control port (the selected verified ingress, the existing provider webhook handler and the captured reference checkout): apply/post-process redelivery using the first stored receipt instant, not redelivery time, so it cannot shift the cycle again. Replay reads winning receipt evidence and refuses (503) when that instant is unavailable. A legacy pre-fingerprint row takes the later clock once. Retain receipt evidence on upgrade; provider routes are not switched to opt-in ingress.
37
+
38
+ ### Known limitations
39
+
40
+ - Portable dunning repair has no managed twin: exhaustion still pauses active subscriptions there; retaining active status needs a managed forward.
41
+ - D8-1: native advisory v2 promo-code candidates/rejection details unavailable.
42
+ - D8-2: native mounted v2 issue/honor/fallback unavailable.
43
+ - D8-3: native acquisition-only mounted issuance/honor unavailable.
44
+ - D8-4: full native/managed eligibility equivalence unavailable.
45
+ - Bundle-1: independent DB human-only actor-kind enforcement absent natively; first in the native-backend parity round.
46
+ - Bundle-2: managed-equivalent actor-kind/email history absent natively.
47
+ - Bundle-3 closes injected-reader M05 mounted admin/feed readback only. Native new-purchase stock read absent despite P5a installed-store proof; deferred second after bundle-1 (owner 2026-10-21); readiness refuses.
48
+ - Bundle-4: incumbent real-PostgreSQL contention only; no exactly-once guarantee.
49
+ - Managed schema/pgTAP retain `44.404`; removal needs owner decision/forward; reference composition supplies it to app code.
50
+ - A1 offline only; account/hosted-time/owner TEST evidence unclaimed; `communications/klaviyo` verification-pending.
51
+ - M05 omitted opt-ins preserve defaults/outputs/read order/schedules/refusals/acceptance-token bytes/SQL.
52
+ - Native inventory adjustment unavailable; address writes refuse readiness. Flat-rate save needs selected port/Core readiness/applied-seed observation.
53
+ - Native shipping audit unavailable; handler swallows best-effort audit failure.
54
+ - Native flat-rate integer bounds (0 through host maximum; reference 100,000 minor units) use request schema, not DB CHECK.
55
+ - Managed ATP/forecast/dated-supply/pagination readback unproved/pending M06.
56
+ - Managed two-session locks/concurrency/uncovered compensation unproved/pending M06.
57
+ - Managed exact-prefix upgrade/ledger unproved/pending M06.
58
+ - Managed address idempotency/conflict/readback unproved/pending M06.
59
+ - Managed successful flat-rate read/save unproved/pending host authority.
60
+ - M06 stock-wait lifecycle/durable timing/staging controls retain C5/C6; busy/try-gate, continuity/flag/TTL transfers are dispositions, not PASS.
61
+ - Shipping national-registry fields/locale-specific `canonicalAddressKey` fold await M16 host market pack/key migration; reference shipping copy stays locale-specific until M16.
62
+ - Native durable prepared storage, method setup, absence reopen and cumulative refunds unavailable; no production installation parity claimed.
63
+ - Typed refusal-envelope forwarding unavailable (`failureReason` only); reusable registration needs host ownership resolution.
64
+ - Incomplete chargebacks stay refunds/unapplied: reclassification bypasses evidence validation into dispute apply; limited operator recovery.
65
+ - Audit compatibility still detects `chargeId`; refund operation identity/original money supply no typed charge identity.
66
+ - M07 follow-up: repair poll can clear host redirect; post-timeout verify never restarts polling; unlocalized poller; fixture builds/never calls handler.
67
+ - M07 follow-up: reference status route wrong-rail witness.
68
+ - M07 follow-up: explicit reference-route refusal for unsupported method setup.
69
+ - M07 follow-up: deployment-neutral disabled-verify reason.
70
+ - M07 follow-up: reference card normalizer missing-amount path; package settling evidence already requires positive money.
71
+
11
72
  ## [0.20.0]
12
73
 
13
74
  - Publishable on the npm `latest` dist-tag as `0.20.0`, from tag
@@ -153,233 +214,3 @@ Question (2026-10-21): "The platform package's root docs (README + CHANGELOG) wi
153
214
  Owner's answer: "Raise to 49,152 B (Recommended)"
154
215
  Option description shown to the owner: "Same figure as core. Unblocks the next few releases; the CHANGELOG grows every release, so this cap would otherwise block each one."
155
216
  Recorded as an owner decision taken through the coordinator's question on 2026-10-21.
156
-
157
- ## [0.18.0]
158
-
159
- - Publishable on the npm `latest` dist-tag as `0.18.0`, from tag
160
- `openlup-platform-v0.18.0`.
161
-
162
- - M06 P4: retain named compatibility shims, restrict inventory smoke evidence to package-owned tests, neutralize synthetic transport fixtures and clarify inventory documentation; no runtime or API change.
163
-
164
- ### 0.17.0 → 0.18.0 set scope
165
-
166
- - Prepare the complete development-preview set at `0.18.0`: core and outbox are
167
- unchanged republications; platform includes every change on main in this set.
168
- - M05 adds catalog foundation/contracts, draft validation, product/lifecycle DTOs,
169
- authority/GTIN/quantity helpers, media and source-pack operations,
170
- fixed-bundle read/admin/feed logic, native envelope/draft stores and an inert
171
- catalog contribution; pricing adds the neutral quote foundation, authority,
172
- subscription policy and assignment token, quote/offer projection, opt-in
173
- acquisition terms and promise consent; promotions includes advisory, preview, acceptance,
174
- admin/native reads and sweep with explicit host bindings; lifecycle/publication
175
- contracts alone do not provide a publisher or native lifecycle writes.
176
- - M05 P6: pricing and bundle comments are corrected, VAT arithmetic delegates to the
177
- existing helper and moved test fixtures are neutral with assertions unchanged;
178
- no API change.
179
- - Reference-application source: opt-in catalog, product, basket, builder and admin UI consumes explicit
180
- data/locale/media/lifecycle inputs; basket edits invalidate promise consent and
181
- minor-money display uses the currency exponent.
182
- - Shipped catalog SQL projects unchanged canonical positions 1/8/16/19/58/59/61,
183
- promotions projects 47/48 and inventory projects 27/33; native catalog factories
184
- retain the existing store bodies and references preserve their named exports.
185
- - A1 adds 17 neutral marketing/consent declarations, the three-function `communications/resend`
186
- entry, the `communications/klaviyo` marketing adapter and lazy opt-in reference bindings with
187
- invocation-time admission; default routes, schedules and handlers stay empty.
188
- Existing providers remain assignable; existing neutral entries keep their runtime
189
- behaviour (the declarations above are additive), and the maintained reference
190
- adapter and default wiring are unchanged.
191
- Authenticated raw webhooks and GET-only delivery reconciliation retain host
192
- ownership of persistence and admission; no SDK or default sender is added.
193
- `communications/klaviyo` profile projection, negative consent readback,
194
- account-wide deletion acknowledgement and authoritative consent push stay
195
- verification-pending until the host supplies owner TEST verification.
196
- Audience-scoped deletion and deletion status are unsupported; newsletter sync
197
- refuses. No flow control, historical import, suppression edits or automatic
198
- mutation retry is added.
199
- - Consent push grants stay audience-scoped; only withdrawals may be account-wide.
200
- The capability declares `grantEffect`; accepted/acknowledged results require
201
- `reconcileRequired`. Conformance keeps grants unsettled until settlement and
202
- fresh readback. Withdrawals bypass grant-caused fences/quarantine but retain
203
- same-key withdrawal fences and account cooling. No push factory, activation,
204
- callback, retry or subpath is added.
205
- - M06 in progress: inventory and shipping entries are early surfaces; module not complete
206
- - The merged M06 surfaces include inventory contracts/admin/native reads, injected
207
- new-purchase availability, reservation policy/maintenance and dated forecast
208
- calculation with required provider injection, plus shipping selection, address
209
- and rate handlers with required host validation.
210
- - @openlup/platform size growth: added M05 catalog, pricing and promotions implementations, native stores and SQL projections, A1 communications adapters and consent contracts, and the merged early M06 inventory/shipping surfaces with their declarations and documentation.
211
-
212
- Migration: upgrade the entire `@openlup/*` set from `0.17.0` to `0.18.0` for exact
213
- core peer pins; new module adoption remains explicit and experimental.
214
- Catalog and promotions server entries are adapters: their declarations require
215
- selected optional `pg` peers, while runtime imports and injected executors work
216
- without them and their contracts entries remain non-adapters. Supply host schemas, authorization,
217
- responders and policies through the documented bindings. Acquisition requires
218
- an explicit `AcquisitionFinancialPolicy` with no default; absence refuses with
219
- `acquisition_terms_policy_unconfigured`, and reference activation remains off.
220
- Bind resolved terms from the final optional issue/honor callback argument for
221
- acquisition-aware acceptance; existing callbacks remain assignable. Stop
222
- assigning `NegativeFeedback.kind` or `.outcome`, now readonly. Reference-application
223
- source threshold classifiers require the host's explicit threshold. Generated enum declaration
224
- ordering changes preserve member values and runtime behavior. Catalog adds
225
- `createPostgresBundleCatalogStore`, `createPostgresCatalogDraftStore` and
226
- `createPostgresCatalogSkuEnvelopeReadPort`, plus `BUNDLE_MIGRATIONS` (16/19) and
227
- `CATALOG_MIGRATIONS` (1/8/58/59/61); composition adds
228
- `createCatalogContribution` and `CatalogContributionInput`, with no public export
229
- removed or renamed. Existing named reference imports remain compatibility paths.
230
- The native writer, pooled transaction binding and transport remain reference-owned;
231
- browser schemas stay local. Core remains the promotions evaluator; no claim writer
232
- is added, and claim writing and mounted acceptance retain their owners. Reference
233
- shipping selection, pickup and stock UI mechanics and browser validation stay local.
234
- Install the complete canonical migration manifest, never sparse package SQL
235
- projections as an installation chain; this preparation adds no new SQL migration.
236
- A1 hosts retain credentials, identity/authority, atomic storage, environment,
237
- egress and schedule ownership; acknowledged/accepted does not establish remote
238
- effect and local consent remains authoritative.
239
-
240
- ### Known limitations
241
-
242
- - D8-1: native advisory v2 promo-code candidates and rejection details are not supported on the native backend.
243
- - D8-2: native mounted v2 code issue/honor/fallback is not supported on the native backend.
244
- - D8-3: acquisition-only mounted issuance/honor is not supported on the native backend.
245
- - D8-4: full native/managed eligibility equivalence is not supported on the native backend.
246
- - Bundle-1: independent database human-only actor-kind enforcement is not supported on the native backend and is the first item of a future native-backend parity round.
247
- - Bundle-2: managed-equivalent actor-kind/email history is not supported on the native backend.
248
- - Bundle-3 is closed for M05 mounted admin/feed readback through the merged availability builder with an injected stock reader; the real native stock read remains M06 P5a work.
249
- - Bundle-4 establishes only incumbent real-PostgreSQL contention characterization and provides no exactly-once guarantee.
250
- - The managed baseline schema and its pgTAP tests retain the incumbent legacy value `44.404`; removing it needs a forward migration and an owner decision, while reference composition supplies that value to application code.
251
- - A1 evidence is offline only: account verification, hosted-time behaviour and owner TEST certification are not claimed, and the `communications/klaviyo` adapter's capabilities stay verification-pending.
252
-
253
- ## [0.17.0]
254
-
255
- - Publishable on the npm `latest` dist-tag as `0.17.0`, from tag
256
- `openlup-platform-v0.17.0`.
257
-
258
- - Add experimental `./communications` and browser-safe
259
- `./communications/contracts`, plus generic capped raw-body, hex-HMAC and
260
- constant-time comparison helpers in the existing `./http` entry. Compose neutral
261
- transport/results/capture, rendering/content, independent Auth verification,
262
- receipt/observation, polling/reconciliation, consent and membership sync engines.
263
- Provider clients, protocol/key mapping, copy and activation remain host-owned.
264
- - Require explicit egress admission for supported injected network senders.
265
- Guarded transports validate fresh host decisions, redirect sink sends and
266
- suppress drops before network entry; provider names grant no admission.
267
- Capture remains local acceptance. Preserve the maintained reference transport
268
- through its trusted composition bridge and keep Auth/provider verification separate.
269
- - Add Polish/English invoice, role, opt-out and inquiry default-content factories
270
- with whole-copy injection. Preserve implicit Polish invoice and existing role
271
- expectations; EN invoice is construction-time opt-in. Reference opt-out page
272
- copy, EN legacy/error text, document language and escaping change deliberately.
273
- Pure inquiry rendering activates no sender. See the
274
- [B1/B2 expectation inventory](../../docs/platform/plans/oss-m04-communications-triggers.md#b1-egress-boundary).
275
- - Add an opt-in immutable composition-time purpose registry with mandatory
276
- classification and finite legacy defaults. Registration grants no consent,
277
- provider activation or managed schema permission; custom-purpose persistence
278
- stays with the host. Add neutral newsletter processing and marketing ports
279
- without a runner or a new public purpose.
280
- - Add four bounded native store groups: delivery observation/readback, existing-rail
281
- membership sync, operator notification recipients and durable consent decisions.
282
- Append CREATE-only direct catalogue position 70,
283
- `20261019200000_communications_native_operations.sql`, and its exact sparse
284
- projection. Standard factory lanes refuse unsafe effective privileges.
285
- Sync uses `public.outbox_events`; the sidecar is not another executor.
286
- - Compose selected native readback and atomic signed-token preferences with Core
287
- readiness, explicit provider/binding identities and host-supplied clocks and
288
- schedules. Add distinct opt-in operator evidence and installed substitute-provider
289
- proof. Captured, network-accepted and membership-synced are not delivered.
290
- Native consent excludes managed 16-hour / 2-day / 4-week caps and legacy
291
- contact-touching, and is not a send-time fence. No default cadence, deployment
292
- activation, full native general sender or UI package capability is added.
293
- [T07 destinations](../../docs/platform/plans/oss-m04-communications-triggers.md#t07-destination-matrix)
294
- remain explicitly open with their owning modules.
295
- - @openlup/platform size growth: added the reviewed neutral communications runtime and browser content contracts, bounded native stores and SQL projection, and their API/adoption documentation.
296
-
297
- Migration: `0.16.0` did not export communications. Upgrade the complete package
298
- set to `0.17.0` for the exact core peers, then explicitly select and bind the new
299
- experimental capability. Existing non-communications entries retain their contracts.
300
- The host retains renderer/copy, environment, origin, egress, provider protocol,
301
- credentials and schedule ownership. The generic HTTP helpers do not replace the
302
- independent Auth Standard Webhooks or provider-specific verification protocols.
303
-
304
- Install the complete selected direct canonical catalogue through position 70
305
- before selecting native stores. `COMMUNICATIONS_MIGRATIONS` projects positions
306
- 2, 9, 20 and 70; never reapply these sparse files over an existing canonical
307
- history or treat them as a standalone chain. Preserve managed/private lineage
308
- and custom-purpose persistence: registering a purpose does not expand managed
309
- CHECKs. Thread the same registered purpose union and registry through selected
310
- policy, token, newsletter and store bindings; omitted arguments retain finite
311
- legacy defaults and existing signing bytes.
312
-
313
- `registerNativeSyncWorkInTransaction` is a trusted host transaction seam. Its
314
- caller-owned executor must already hold the lane's safe effective privileges;
315
- this function does not check those privileges. The caller owns the transaction
316
- and must commit or roll back permission, sidecar and existing-rail effects
317
- atomically. Use a safely admitted lane; the helper grants no provider or claim
318
- authority. Standard native sync factory transactions check
319
- `public.communication_native_sync_state`, `public.outbox_events` and
320
- `public.communication_recipient_consents`.
321
-
322
- Custom native stores must provide binding-scoped `findByMessageId`, bounded
323
- `listEvidence` (1–100) and atomic `registerPreference` as declared by the new
324
- surface. Supply explicit provider/binding identities and full catalogue admission.
325
- Hosts preserving older opt-out page bytes retain their own presentation binding;
326
- token purposes, verification and existing HTTP status/header semantics remain.
327
- See the [package composition guidance](README.md#communications-import).
328
-
329
- Growth reading: +61.4% against the retained initial `0.16.0` baseline (256,973
330
- packed bytes), measured before S1 notes at 414,808 bytes; final S1 measurements
331
- are recorded in the programme evidence. No cap is changed. Baseline evidence
332
- (2026-10-20): the published `0.16.0` tarball is 257,434 packed bytes, SHA256
333
- `030756175d61279aab0ae7b26b27783e08467ec93673f5623d19d0b7fc436463`.
334
- S1 option B retains the initial release-gates baseline rather than replacing it
335
- with this observed registry identity.
336
-
337
- ## [0.16.0]
338
-
339
- - Publishable on the npm `latest` dist-tag as `0.16.0`, from tag
340
- `openlup-platform-v0.16.0`.
341
-
342
- - Add the experimental identity capability at `./identity` and browser-safe
343
- `./identity/contracts`: role-based authorization, actor-scoped profiles,
344
- bounded normalized contact matching, unconfirmed soft accounts with explicit
345
- email-trust policies, and configured-origin magic-link redirect resolution.
346
- - Add operator invitations with transactional pending acceptance, expiry,
347
- cancellation and revoked re-invitation for direct stores, and immediate active
348
- membership with repeat/concurrent recovery for managed stores. Direct operator
349
- login uses single-use email challenges, operator-audience sessions, live
350
- membership authorization, explicit first-administrator bootstrap and bounded
351
- challenge pruning. An opt-in OIDC access-token verifier provides pinned issuer
352
- discovery, bounded rotating JWKS, strict claim validation and deterministic
353
- issuer/subject principals for direct composition.
354
- - Add versioned personal-data contributors, identity export and transactional
355
- direct erasure with contributor vetoes, challenge/invitation cleanup,
356
- pseudonymization, guarded operator revocation and idempotent receipts. Managed
357
- export reports incomplete coverage; managed erasure refuses with
358
- `MANAGED_PRIVACY_ERASURE_UNAVAILABLE` without mutation. Issued tokens may remain
359
- valid until expiry.
360
- - Add seven byte-identical identity SQL projections and host-selected module
361
- schema readiness. The projections retain canonical migration identities and
362
- positions; they are not a standalone installation chain.
363
-
364
- Migration: identity adoption is opt-in; upgrade the complete package set together
365
- for the exact `@openlup/core` `0.16.0` peer pin. Supply the selected bundle's schema
366
- inventory, clients and delivery bindings, an explicit email-trust policy, exact
367
- redirect origins, a distinct operator audience and a deployment policy for
368
- admin, operator and privacy routes. Managed soft-account provisioning requires
369
- an email-trust envelope with automatic confirmation disabled and all enabled
370
- non-email providers declared email-verifying. Install the complete direct
371
- catalogue through `20261018190000_identity_privacy_erasures` before enabling
372
- identity; never reapply projections to an existing canonical history. Managed
373
- adopters need their committed chain's
374
- `20261018160000_identity_service_role_capabilities.sql` forward for contact linking
375
- and operator membership capabilities, and must supply their own erasure path.
376
-
377
- ## [0.15.0]
378
-
379
- - Publishable on the npm `latest` dist-tag as `0.15.0`, from tag
380
- `openlup-platform-v0.15.0`.
381
-
382
- - First publication of `@openlup/platform`, the server substrate for an OpenLup application, with one module subpath each for `http`, `bff`, `scheduler`, `blob`, `data`, `migrations`, `job-ledger`, `kernel` and `composition`.
383
- - Transport-neutral scheduled jobs: a `run(context)` contract, an empty job registry, a lease-aware runner and an authenticated HTTP cron entry, with a constant-time cron secret comparison.
384
- - Per-capability bundle readiness that refuses an unknown bundle and reports every missing scheduler, lock and blob setting together.
385
- - Blob capability selection with filesystem, Vercel Blob, Supabase Storage and S3 bindings; `pg`, `@types/pg` and `@aws-sdk/client-s3` are optional peers, and an adapter refuses a missing peer with `OPENLUP_E_PEER_MISSING`.
package/README.md CHANGED
@@ -4,18 +4,18 @@ Development preview. This package is part of the publishable set with core and o
4
4
 
5
5
  It holds the runnable server substrate an OpenLup application composes: the Node HTTP runtime, BFF dispatch, scheduling and job leases, blob storage, data gateways, the node-postgres migration runner and bundle composition. Ports and their contracts stay in `@openlup/core/platform-runtime`.
6
6
 
7
- For the prepared `0.20.0` set, read the [adjacent-release migration and known
8
- limitations](CHANGELOG.md#0200) before selecting the opt-in M05 parity seams or
9
- M06 composition bindings. M06 remains incomplete: inventory and shipping entries
7
+ For the prepared `0.21.0` set, read the [adjacent-release migration and known
8
+ limitations](CHANGELOG.md#0210) before selecting the opt-in M05 parity seams or
9
+ M06 composition bindings, or the M07 payment selectors. M07 payment truth is complete for this wave; its selectors are default-off. M06 remains incomplete: inventory and shipping entries
10
10
  are development-preview surfaces, with managed-host proof classes pending in the
11
11
  [M06 parity ledger](../../docs/platform/plans/oss-m06-stock-reservations-delivery.md#parity-pending-rows).
12
- The size gate compares the prepared `0.20.0` set with the published `0.19.0` tarball;
12
+ The size gate compares the prepared `0.21.0` set with the published `0.20.0` tarball;
13
13
  preparation, offline adapter evidence and a passing size cap grant no publication
14
14
  or provider-account certification.
15
15
 
16
16
  ## Package surface
17
17
 
18
- `release-gates.json` declares kind `rail`: this foundation provides runtime infrastructure over core ports, matching the architecture's rail responsibility. Its twenty-five experimental entries each bind a declaration snapshot and package-owned smoke tests. The API checker reads this same inventory.
18
+ `release-gates.json` declares kind `rail`: this foundation provides runtime infrastructure over core ports, matching the architecture's rail responsibility. Its experimental entries each bind a declaration snapshot and package-owned smoke tests. The API checker reads this same inventory.
19
19
 
20
20
  | Export | Contents |
21
21
  | --- | --- |
@@ -32,6 +32,9 @@ or provider-account certification.
32
32
  | `./identity/contracts` | Experimental capability: browser-safe customer auth port, value types and provider parser |
33
33
  | `./communications` | Experimental existing public consent/status, newsletter sync, Auth email hook, transport contracts/capture, observation/polling orchestration, standard webhook verification and delivery receipts; host-selected rendering, egress, protocol mapping and control policy |
34
34
  | `./communications/contracts` | Pure renderer, block builders, copy-injected commerce/subscription factories and email/newsletter/Auth/intake/receipt types; no Zod, Node or provider runtime imports |
35
+ | `./payments/stripe` | Opt-in Node payment example with an injected structural client, verified observations and purpose-sensitive readback; no SDK dependency or default activation. |
36
+ | `./payments/tpay` | Opt-in Node payment example with injected HTTP/certificate I/O, Model O aliases and verified observations; no default activation. |
37
+ | `./payments` | Experimental prepared-attempt ordering with injected persistence, execution and failure policy; contracts at `./payments/contracts`. |
35
38
  | `./inventory` | Experimental schemas/ports, handlers, native stores, projection, injected forecast and SQL descriptor; `./inventory/contracts` DTOs. |
36
39
  | `./catalog` | Experimental neutral catalog schemas, drafts, reads, media source packs and publication ports |
37
40
  | `./catalog/contracts` | Dependency-free catalog DTOs, media resolver, authority and canonicalization helpers |
@@ -771,7 +774,7 @@ for both-bundle commands, the committed managed grants and installation limits.
771
774
 
772
775
  ## Communications import
773
776
 
774
- For adoption from `0.16.0`, follow the [0.17.0 Migration guidance](CHANGELOG.md#0170)
777
+ For adoption from `0.16.0`, follow the [0.17.0 Migration guidance](CHANGELOG-HISTORY.md#0170) (source repository)
775
778
  for the complete package set, canonical SQL installation and host-owned bindings.
776
779
 
777
780
  M04 P1 relocates the existing public implementation without changing its reference
@@ -1052,3 +1055,166 @@ opt-in browser profile adds no npm entry or UI package export.
1052
1055
  Source contributors record notable changes in package changelog fragments;
1053
1056
  release preparation assembles the shipped CHANGELOG. Follow the
1054
1057
  [public contribution guide](https://github.com/openlup/openlup/blob/main/CONTRIBUTING.md#development-preview-checks).
1058
+
1059
+ ## Payments prepared attempts
1060
+
1061
+ `@openlup/platform/payments` exports `executePreparedAttemptMechanism`: validate before durable preparation, refuse prepared replay, dispatch once unless the host proves no charge, and retry one immutable local finalization. The host owns keys, fingerprints, nominal error trust and persistence. An unknown execution never authorizes redispatch. The reference facade retains its existing binding.
1062
+
1063
+ `./payments/contracts` is type-only: execution/dispatch, transient method handoff, capability overlay, verified money/method evidence, bounded readback/absence, neutral refusals, refund observations and multi-subject conformance fixtures. Verification uses receipt time for freshness; ingress starts processing at receipt time and reuses durable first receipt on redelivery. Event identity includes outcome class. Transient authorization and opaque handoffs never enter persisted diagnostics. Capability support is explicit; timeout/truncation is unknown, never exact absence. Refunds require explicit evidence by default; `legacy-assume-full` is an explicit host opt-in. See the [M07 ledger](../../docs/platform/plans/oss-m07-payments-money-truth.md).
1064
+
1065
+ The payments entry also exports verified ingress, canonical truth fingerprints, method lifecycle/refusal handoff, reconciliation/verify-now and three runner-independent conformance functions. A new internal boundary projects verified envelopes into the contributed processing carrier: original bytes reach verification unchanged, receipt time starts processing, and durable first receipt overrides redelivery time. Full-reversal chargebacks use the existing dispute path. Snapshot and delta observations preserve original payment money; missing observation, required money or identity refuses before terminal application. Missing money, amounts that are not safe positive integers, or blank currencies on succeeded, refunded or disputed adapter evidence use the existing amount-mismatch refusal before ingestion or terminal application; ingress returns the existing processing-failure response. Failed and pending evidence may still omit money. Hosts own durable stores and response transport. Conformance requires safe positive integer amounts and nonblank currencies on settling money and checks exact identity/money, capability/port agreement and transient-secret exclusion; its evidence contains no handoff. Real-store durability still requires a selected-store proof.
1066
+
1067
+ `createPostgresPaymentTruthPort` accepts the platform data executor structural
1068
+ shape and `PostgresPaymentTruthOptions` from the contracts entry. It preserves
1069
+ the native truth ingest, reconciliation, sweep and readback SQL. `PAYMENT_MIGRATIONS`
1070
+ projects canonical units 10/35/56 byte for byte, with path, position, SHA-256 and
1071
+ asset URL; install the complete catalogue, never those three files alone.
1072
+ `paymentSchemaDescriptor` admits direct truth, settlement and later-only cycle
1073
+ alignment, and explicitly refuses prepared durable storage, method setup,
1074
+ absence reopen and cumulative refund. Refunds are purely financial. Seven real
1075
+ PostgreSQL cases cover fresh/prefix installs, replay/conflict, unknown readback,
1076
+ lost acknowledgement, rollback, denied access, dunning/recovery, concurrency and
1077
+ later-only dates; this does not claim managed-store parity or host activation.
1078
+
1079
+ Attempt input adds optional method selection with opaque, persistable channelRef
1080
+ and transient payer email/name. Payer contact is dispatch-only: never persist,
1081
+ audit, log or diagnose it. Missing adapter-required input must refuse before
1082
+ dispatch with exact `notDispatched` evidence. Conformance scans the execution
1083
+ and complete projected stored payload for transient values. Nested normalized
1084
+ evidence copies only declared typed fields, including nested facts; unknown
1085
+ extra properties are excluded. Incomplete chargeback evidence remains stored as
1086
+ a refund and unapplied, a declared operator recovery limit: reclassification
1087
+ would apply the donor dispute path without checking completeness.
1088
+
1089
+ `createCheckoutActivePaymentActionResolver` authorizes the exact owner, order,
1090
+ intent, attempt and execution rail, reads the provider once, then rechecks the
1091
+ complete canonical attempt. Hosts inject synchronous `railHandoffKinds` from
1092
+ adapter capture capabilities; an unknown rail supplies an empty list. Actions
1093
+ carry the provider address and the existing transient `PaymentMethodHandoff`.
1094
+ Pending evidence alone may expose a handoff; terminal, mismatched, unknown or
1095
+ raced evidence refuses it. `createCheckoutRecoveryPaymentResolver` preserves
1096
+ paid/retry/resume/wait/manual-review ordering without creating a new obligation.
1097
+ `createCommercePaymentVerifyHandler` validates the existing identity triple,
1098
+ checks stored ownership before readback, and uses the shared HTTP/responder
1099
+ contracts. A host may inject its responder to preserve request references.
1100
+
1101
+ `PaymentRepairMethodPort` collects a method only; it grants no token redemption,
1102
+ subscription change or payment success. Unsupported setup is explicit. Method
1103
+ evidence may carry neutral registration refs/kind/status. Ingress projects them
1104
+ only with `resolveMethodOwner` supplied by the host; adapter-supplied client or
1105
+ subscription fields never confer ownership. Without host ownership resolution,
1106
+ registration is not projected. Lifecycle carriers copy typed facts only.
1107
+ The existing refusal handoff accepts a failure-reason string, not a typed
1108
+ adapter refusal envelope; envelope forwarding remains unsupported.
1109
+
1110
+ ### Payment service composition
1111
+
1112
+ `createPaymentServices` accepts `PaymentServicesOptions` and returns
1113
+ `PaymentServices` or null when truth schema/capability is unavailable. The host
1114
+ supplies `openTruth`, which opens one port and its asynchronous close operation.
1115
+ `run` closes that scope after success or failure. Construction performs no I/O.
1116
+ Use the existing `createBundleComposer` payments binder for named registration;
1117
+ omitting it leaves the host cold. Schema declarations do not prove installation:
1118
+ retain the existing readiness/schema checks before admitting work. Existing jobs
1119
+ and routes keep their owners; this binding adds no schedule or event handler.
1120
+
1121
+ The reference direct truth binding injects its existing executor, templates and
1122
+ recovery path. The separate verified-ingress selector is explicit and default-off;
1123
+ unknown selections refuse. Its projected carrier includes the adapter fingerprint,
1124
+ so the reference store records stable identity on the first receipt independently
1125
+ of audit payload or clock. On replay the store reads the first stored instant;
1126
+ application and follow-up work reuse it. Missing durable clock evidence refuses
1127
+ rather than advancing a cycle. This D11 behaviour change does not mount a provider
1128
+ route or enable an adapter; hosts must explicitly wire the selected ingress.
1129
+
1130
+ ### Opt-in payment example
1131
+
1132
+ Import `createStripePaymentAdapter`, `type StripePaymentAdapterConfig` and
1133
+ `stripePaymentCapabilities` from `@openlup/platform/payments/stripe`. Supply a
1134
+ structural client, `webhookSigningSecrets`, explicit `mode`, required
1135
+ `supportedCurrencies` (a non-empty list of upper-case three-letter codes) and optionally
1136
+ `toleranceSeconds` (default 300) and `clock` (UTC seconds). The host constructs
1137
+ and configures the SDK, associates saved methods with customers and confirms
1138
+ off-session requests. Interactive methods follow the declared card/wallet offer.
1139
+ Importing creates no I/O or default binding. The adapter advertises the host
1140
+ currency list as a frozen copy; the frozen exported descriptor keeps an empty list.
1141
+ Account-specific readiness remains pending TEST verification. Exact absence,
1142
+ refund creation and split authorization/capture are unsupported. Cancellation
1143
+ is enabled only when the injected client supplies it. See the
1144
+ [A2 ledger](../../docs/platform/plans/oss-a2-payment-adapters.md) for the current
1145
+ preview boundary and the host's responsibilities.
1146
+
1147
+ Verified dispute events retain their distinct money outcome and original payment
1148
+ identity. Audit payloads carry only donor-filtered facts: the source event type,
1149
+ refund charge snapshot and failure-evidence provenance when present. They exclude
1150
+ raw bodies, client handoffs and customer authorization and do not affect event
1151
+ IDs or fingerprints. The offline adapter conformance witness uses two distinct
1152
+ intents and attempts with separate money and provider references.
1153
+
1154
+ Method evidence carries the donor lifecycle and replacement facts through M07
1155
+ projection when provider time is present. Without provider time it retains the
1156
+ method outcome and omits the lifecycle instead of inventing a transition time.
1157
+
1158
+ ### Opt-in bank and code payment example
1159
+
1160
+ Import `createTpayPaymentAdapter`, `type TpayPaymentAdapterConfig` and
1161
+ `tpayPaymentCapabilities` from `@openlup/platform/payments/tpay`. Supply `http`
1162
+ with explicit `baseUrl`, `clientId`, `clientSecret`, `fetchImpl` and optional
1163
+ `timeoutMs`; `urls` with `notificationUrl`, `successUrl`, `errorUrl`;
1164
+ `modelOActivationEnabled`, `mode`, and optional UTC-seconds `clock`.
1165
+ `notification` requires `rootCertificatePem`, `allowedCertificateUrlPrefix`
1166
+ (HTTPS directory ending in `/`) and `resolveCertificate`. The resolver owns
1167
+ redirect/egress policy. Both certificates must be valid at verification time.
1168
+
1169
+ Verify certificate validity and notification freshness using original raw bytes.
1170
+ A signed protected-header `iat` supplies UTC-seconds provider time; absent time
1171
+ stays null. `maxNotificationAgeSeconds` defaults to seven days, a generous
1172
+ assumption pending confirmation of the provider retry schedule. Signed issue time
1173
+ more than `maxClockSkewSeconds` (default 300) after receipt refuses as stale;
1174
+ both freshness limits must be finite and nonnegative. Host dedupe
1175
+ uses exact event identity plus stable fingerprint when time is absent. The age
1176
+ and future-skew limits apply only when the signed header carries a provider issue time. Receipt
1177
+ time is used only for freshness. Transaction identity includes outcome class;
1178
+ method identity includes event/alias plus outcome.
1179
+
1180
+ Bank redirects, one-time codes and unattended Model O alias charges are supported
1181
+ when the host supplies the M07 P3 contract payer and method selection inputs.
1182
+ Supply transient `payer: { email, name? }` on every
1183
+ attempt, including renewals. An omitted or blank name uses the donor
1184
+ neutral display label `Customer`. Select `methodSelection: { family: "bank-redirect",
1185
+ channelRef }` for the bank channel, or `{ family: "one-time-code" }` with
1186
+ `customerAuthorization` as a `TransientSecret`. A `renewal-unattended` attempt
1187
+ uses `storedMethod.reference` and its Model O mandate. Missing payer/channel/code
1188
+ and Model M or unknown mandates refuse before any dispatch with neutral
1189
+ data-correction evidence. Payer and authorization never cross the return boundary
1190
+ in diagnostics or request/response payloads. Readback uses the returned API
1191
+ transaction ID; notification titles remain distinct. The donor's typed no-charge
1192
+ errors return an exact `notDispatched` envelope: proven request-document refusals
1193
+ require customer data correction; OAuth, encoding and exhausted pre-send deadlines
1194
+ remain transient retryable refusals. Host mode/purpose/currency mismatches throw before I/O.
1195
+ A renewal the provider refuses for data validation is classified as needing customer data correction: the host should prompt the customer to update payment details, and the bundled retry schedule keeps its normal retries unless the host treats that class as terminating. The subscription stays active; nothing is paused or suspended by this classification. The contributed adopter applies this classification only when reopening a checkout; the example applies it to every purpose.
1196
+ Every other execution error propagates unchanged as ambiguous.
1197
+
1198
+ The exported capability descriptor and its nested records and arrays are frozen.
1199
+ Currencyless money notifications preserve the paid amount and bind to the adapter's single declared currency; multiple declared currencies refuse as malformed.
1200
+ Alias method evidence preserves the donor lifecycle through M07 projection with
1201
+ signed provider time; absent provider time leaves the lifecycle field omitted.
1202
+ The adapter offers its declared market currency; alias registration and recurring
1203
+ cards are not offered by this factory. Account offers
1204
+ remain `verification_pending` until owner TEST readback. Chargeback observations carry full reversal,
1205
+ original money and stable operation identity; refunded/chargeback readback stays
1206
+ unknown for manual review, including historical attempt/error refusals in either
1207
+ readback purpose. Pending and refused readback report validated configured money,
1208
+ including when the paid amount is zero or absent; a refused result with mismatched
1209
+ nonzero paid money stays unknown for manual review. Succeeded readback requires the donor's
1210
+ observed paid amount to equal expected money; underpayment and overpayment remain
1211
+ unknown for manual review. There is no close or exact-absence port. Refund
1212
+ creation and authorization/capture separation are unsupported. See the
1213
+ [A2 ledger](../../docs/platform/plans/oss-a2-payment-adapters.md#o-p2-boundary-and-named-deltas).
1214
+
1215
+ Verified audit payloads retain only donor-filtered transaction and alias facts,
1216
+ including `eventKind`, alias `event` and `aliasType` when supplied. Missing failure
1217
+ provenance and failure prose stay absent. Payer, authorization, certificates and
1218
+ raw bodies never enter the evidence; audit facts do not affect event identity or
1219
+ fingerprints. The offline M07 P2 conformance witness uses two distinct intents,
1220
+ attempts, amounts and provider references, with replay and refusal boundaries.
@@ -1,7 +1,11 @@
1
1
  import type { AnalyticsPort, BlobStoragePort, DataGatewayPort, HttpRuntimePort, MigrationRunnerPort, SchedulerPort, TransactionalRuntimePort } from "@openlup/core/platform-runtime";
2
2
  import { type PlatformBundleDescriptor, type PlatformBundleId } from "../kernel/index.js";
3
+ interface PaymentServices {
4
+ run<T>(work: (port: unknown) => Promise<T>): Promise<T>;
5
+ }
3
6
  type Env = Record<string, string | undefined>;
4
7
  export interface PlatformBundlePorts {
8
+ payments?: PaymentServices | null;
5
9
  http: HttpRuntimePort | null;
6
10
  scheduler: SchedulerPort | null;
7
11
  blob: BlobStoragePort | null;
@@ -21,6 +25,8 @@ export interface PlatformBundle {
21
25
  * transactional binder also receives the bundle's data capability.
22
26
  */
23
27
  export interface PlatformBundleBinders {
28
+ /** Explicit host selection only; no default payment executor. */
29
+ payments?: (env: Env) => PaymentServices | null;
24
30
  /**
25
31
  * The supabase-js `createClient` used by the default Supabase Storage blob binder. Declared
26
32
  * structurally here so the composition entry's declarations reference no data-module type.
@@ -39,6 +39,8 @@ export function createBundleComposer(binders = {}) {
39
39
  analytics: binders.analytics?.(capabilities.analytics, env) ?? null,
40
40
  transactional: binders.transactional?.(capabilities.transactional, capabilities.data, env) ?? null,
41
41
  };
42
+ if (binders.payments)
43
+ ports.payments = binders.payments(env);
42
44
  ports.scheduler = binders.scheduler?.(capabilities.scheduler, env) ?? null;
43
45
  return { id, descriptor, ports };
44
46
  };
@@ -1,3 +1,4 @@
1
+ // The existing composer accepts one explicit payments binder; default hosts stay cold.
1
2
  export { createBundleComposer, } from "./composeBundle.js";
2
3
  export { bindNodeHttpPort, createScheduledEntrypointResolver } from "./httpBinding.js";
3
4
  export { createNodeHost, } from "./nodeHost.js";
@@ -0,0 +1,8 @@
1
+ import type { PaymentAdapterConformanceEvidence, PaymentAdapterConformanceSubject, PaymentVerifyNormalizePort } from "./contracts/index.js";
2
+ /** Runner-independent adapter proof. Verification fixtures are owned by each adapter. */
3
+ export declare function runPaymentAdapterConformance(subjects: readonly (PaymentAdapterConformanceSubject & {
4
+ verification: readonly {
5
+ input: Parameters<PaymentVerifyNormalizePort["verifyAndNormalize"]>[0];
6
+ expected: Awaited<ReturnType<PaymentVerifyNormalizePort["verifyAndNormalize"]>>;
7
+ }[];
8
+ })[]): Promise<PaymentAdapterConformanceEvidence>;