@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
@@ -0,0 +1,157 @@
1
+ import type { PaymentMethodLifecycleEvent } from "@openlup/core/payment";
2
+ import type { PaymentMethodHandoff, PaymentRefusalEnvelope } from "./adapter.js";
3
+ /** Configured integer-minor money; currency bytes pass through unchanged. */
4
+ export type PaymentMoney = {
5
+ amountMinor: number;
6
+ currency: string;
7
+ };
8
+ /** Exact attempt identity for readback, close and absence lookups. */
9
+ export type PaymentAttemptIdentity = {
10
+ provider: string;
11
+ /** Null permits an exact intent lookup when dispatch acknowledgement was lost. */
12
+ providerPaymentRef: string | null;
13
+ intentRef: string;
14
+ attemptRef: string;
15
+ };
16
+ type VerifiedIdentity = {
17
+ provider: string;
18
+ /** Adapter-derived; includes outcome/status class, never transaction identity alone. */
19
+ providerEventId: string;
20
+ /** Provider-asserted time or null; never substitute receipt time. */
21
+ providerOccurredAt: string | null;
22
+ /** Stable normalized evidence fingerprint; excludes receipt time and secrets. */
23
+ fingerprint: string;
24
+ /** Adapter-filtered provider facts for host audit/repair readers. Never raw bodies,
25
+ * transient secrets, handoffs or customer authorization; excluded from identity/fingerprint.
26
+ * Typed evidence takes precedence over overlapping audit facts. */
27
+ providerAuditPayload?: Readonly<Record<string, unknown>>;
28
+ };
29
+ /** Original payment money and stable operation identity accompany every reversal. */
30
+ export type PaymentRefundObservation = {
31
+ operationId: string;
32
+ originalPaymentMoney: PaymentMoney;
33
+ } & ({
34
+ kind: "cumulative-snapshot";
35
+ refundedMoney: PaymentMoney;
36
+ } | {
37
+ kind: "event-delta";
38
+ refundedMoney: PaymentMoney;
39
+ } | {
40
+ kind: "full-reversal";
41
+ reason: "refund" | "chargeback";
42
+ });
43
+ /** Type-only policy: require-evidence is default; legacy behavior needs explicit opt-in. */
44
+ export type RefundEvidencePolicy = "require-evidence" | "legacy-assume-full";
45
+ /** Verified money facts; authorization alone is never settled money. */
46
+ export type VerifiedPaymentEvidence = VerifiedIdentity & {
47
+ kind: "money";
48
+ providerPaymentRef: string;
49
+ intentRef: string | null;
50
+ attemptRef: string | null;
51
+ outcome: "succeeded" | "failed" | "pending" | "refunded" | "disputed";
52
+ money: PaymentMoney | null;
53
+ refusal?: PaymentRefusalEnvelope;
54
+ refund?: PaymentRefundObservation;
55
+ };
56
+ /**
57
+ * Method lifecycle facts, separate from the money-outcome union; setup is never money success.
58
+ * Outcomes follow the core method lifecycle kinds plus setup failure and required customer action.
59
+ */
60
+ export type VerifiedMethodEvidence = VerifiedIdentity & {
61
+ kind: "method";
62
+ outcome: "registered" | "updated" | "revoked" | "expired" | "suspended" | "failed" | "requires-action";
63
+ methodLifecycle?: PaymentMethodLifecycleEvent;
64
+ /** Registration facts only. The host resolves client/subscription ownership. */
65
+ reusableMethod?: {
66
+ providerCustomerRef?: string | null;
67
+ providerMethodRef: string;
68
+ providerMandateRef?: string | null;
69
+ methodKind: string;
70
+ status: "pending_verification" | "active" | "inactive" | "expired" | "revoked";
71
+ };
72
+ methodRef: string;
73
+ setupRef: string | null;
74
+ intentRef: string | null;
75
+ };
76
+ /** Verification uses injected I/O and original bytes; never reserialize rawBody. */
77
+ export interface PaymentVerifyNormalizePort {
78
+ verifyAndNormalize(input: {
79
+ rawBody: Uint8Array;
80
+ headers: Readonly<Record<string, string | readonly string[] | undefined>>;
81
+ /** Freshness only; never part of identity or fingerprint. */
82
+ receivedAt: string;
83
+ }): Promise<VerifiedPaymentEvidence | VerifiedMethodEvidence | {
84
+ kind: "refusal";
85
+ reason: "signature" | "stale" | "malformed" | "unsupported-event";
86
+ }>;
87
+ }
88
+ /** Existing purpose bytes are preserved for reference reconciliation callers. */
89
+ export type ProviderReadPurpose = "active_checkout" | "recovery";
90
+ /** Absence requires a complete bounded lookup of exact identity and configured money. */
91
+ export type PaymentAbsenceEnvelope = {
92
+ identity: PaymentAttemptIdentity;
93
+ expectedMoney: PaymentMoney;
94
+ } & ({
95
+ kind: "absent";
96
+ evidence: {
97
+ exact: true;
98
+ complete: true;
99
+ lookupReference: string;
100
+ };
101
+ } | {
102
+ kind: "unknown";
103
+ reason: "timeout" | "truncated" | "indeterminate";
104
+ } | {
105
+ kind: "unsupported";
106
+ });
107
+ /** Unknown is neither absence nor unsupported. Timeout/truncation stays unknown. */
108
+ export type PaymentReadbackEnvelope = {
109
+ identity: PaymentAttemptIdentity;
110
+ expectedMoney: PaymentMoney;
111
+ purpose: ProviderReadPurpose;
112
+ } & ({
113
+ kind: "succeeded" | "failed" | "pending";
114
+ observedMoney: PaymentMoney;
115
+ providerOccurredAt: string | null;
116
+ refusal?: PaymentRefusalEnvelope;
117
+ handoff?: PaymentMethodHandoff;
118
+ } | {
119
+ kind: "unknown";
120
+ reason: "timeout" | "truncated" | "indeterminate";
121
+ } | {
122
+ kind: "absent";
123
+ evidence: Extract<PaymentAbsenceEnvelope, {
124
+ kind: "absent";
125
+ }>["evidence"];
126
+ } | {
127
+ kind: "unsupported";
128
+ });
129
+ /** Exact readback of one attempt; a browser return or provider silence is never money truth. */
130
+ export interface PaymentReadbackPort {
131
+ readPayment(input: {
132
+ identity: PaymentAttemptIdentity;
133
+ expectedMoney: PaymentMoney;
134
+ purpose: ProviderReadPurpose;
135
+ }): Promise<PaymentReadbackEnvelope>;
136
+ /** Present only when capabilities declare close supported; returns the post-close readback. */
137
+ close?(input: {
138
+ identity: PaymentAttemptIdentity;
139
+ expectedMoney: PaymentMoney;
140
+ purpose: ProviderReadPurpose;
141
+ }): Promise<PaymentReadbackEnvelope>;
142
+ }
143
+ /** Present only when capabilities declare exact absence supported. */
144
+ export interface PaymentAbsencePort {
145
+ proveAbsence(input: {
146
+ identity: PaymentAttemptIdentity;
147
+ expectedMoney: PaymentMoney;
148
+ }): Promise<PaymentAbsenceEnvelope>;
149
+ }
150
+ /** Transport-neutral acknowledgement; hosts write their own HTTP response. */
151
+ export interface PaymentIngressPort {
152
+ receive(input: Parameters<PaymentVerifyNormalizePort["verifyAndNormalize"]>[0]): Promise<{
153
+ status: 200 | 400 | 409 | 503;
154
+ data: Record<string, unknown>;
155
+ }>;
156
+ }
157
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ export type { PreparedAttemptPrepareInput, PreparedAttemptPersistenceResult, PreparedAttemptFinalizeInput, PreparedAttemptPersistencePort, PreparedAttemptExecutionPort, PreparedAttemptExecutionInput, PreparedAttemptFailurePolicy, PreparedAttemptExecutionResult, } from "./preparedAttempt.js";
2
+ export type { TransientSecret, PaymentMethodHandoff, PaymentRefusalEnvelope, PaymentExecutionEnvelope, PaymentAttemptPort, PaymentAdapterCapabilities, PaymentAdapter, } from "./adapter.js";
3
+ export type { PaymentRefundObservation, RefundEvidencePolicy, VerifiedPaymentEvidence, VerifiedMethodEvidence, PaymentVerifyNormalizePort, ProviderReadPurpose, PaymentAbsenceEnvelope, PaymentReadbackEnvelope, PaymentMoney, PaymentAttemptIdentity, PaymentReadbackPort, PaymentAbsencePort, } from "./evidence.js";
4
+ export type { PaymentAdapterConformanceSubject, PaymentAdapterConformanceEvidence } from "./conformance.js";
5
+ export type { PaymentTruthOutcome, PaymentTruthRefusalClass, PaymentSourceEvidence, PaymentTruthEvent, PaymentReconciliationResult, PaymentTruthReadback, PaymentTruthPort, CanonicalPaymentSource, } from "./truth.js";
6
+ export type { PaymentIngressPort } from "./evidence.js";
7
+ export type { ProviderReconciliationStatusKind, ProviderReconciliationStatus, PaymentProviderReconciliationProvider, ClaimedPaymentAttempt, PaymentProviderReconciliationPort, InteractivePreparedAttemptManualReconciliationPort, ManualInteractivePreparedAttemptAbsenceEvidence, InteractivePreparedAttemptReopenResult, PaymentProviderReconciliationWorkerResult, PaymentProviderReconciliationWorkerInput, SilenceWindowLookup, PaymentVerifyReadPort, VerifiableAttemptSnapshot, PaymentVerifyNowResult, } from "./reconciliation.js";
8
+ export type { PostgresPaymentTruthOptions } from "./truth.js";
9
+ export type { ActivePaymentActionAuthority, CheckoutActivePaymentActionResolver, CheckoutRecoveryPaymentResolver, CheckoutRecoveryPaymentResolution, PaymentRecoveryAction, PaymentRepairMethodPort } from "./repair.js";
10
+ export type { PaymentServicesOptions, PaymentServices } from "./repair.js";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,69 @@
1
+ import type { PaymentAttemptStatus, PaymentExecutionBaseInput, PaymentExecutionBaseResult } from "@openlup/core/payment";
2
+ /** Durable preparation facts; provider admission belongs to the caller. */
3
+ export interface PreparedAttemptPrepareInput<P extends string = string> {
4
+ idempotencyKey: string;
5
+ paymentIntentId: string;
6
+ provider: P;
7
+ providerIdempotencyKey: string;
8
+ providerRequestFingerprint: string;
9
+ providerFlow: string;
10
+ paymentMethodRef?: string | null;
11
+ requestPayload: Record<string, unknown>;
12
+ }
13
+ export interface PreparedAttemptPersistenceResult<N extends string = string> {
14
+ paymentAttemptId: string;
15
+ status: PaymentAttemptStatus;
16
+ replayed: boolean;
17
+ providerAttemptId: string | null;
18
+ providerSessionId: string | null;
19
+ nextActionKind: N | null;
20
+ }
21
+ export interface PreparedAttemptFinalizeInput<N extends string = string> extends Pick<PaymentExecutionBaseResult, "providerAttemptId" | "providerSessionId" | "attemptStatus" | "requestPayload" | "responsePayload"> {
22
+ idempotencyKey: string;
23
+ paymentIntentId: string;
24
+ paymentAttemptId: string;
25
+ providerIdempotencyKey: string;
26
+ providerRequestFingerprint: string;
27
+ nextActionKind: N | null;
28
+ }
29
+ /** Only persistence capabilities needed by interactive attempt ordering. */
30
+ export interface PreparedAttemptPersistencePort<P extends string = string, N extends string = string> {
31
+ prepareProviderAttempt(input: PreparedAttemptPrepareInput<P>): Promise<PreparedAttemptPersistenceResult<N>>;
32
+ finalizeProviderAttempt(input: PreparedAttemptFinalizeInput<N>): Promise<PreparedAttemptPersistenceResult<N>>;
33
+ }
34
+ export interface PreparedAttemptExecutionPort<I extends PaymentExecutionBaseInput, R extends PaymentExecutionBaseResult> {
35
+ validateInput?(input: I): void;
36
+ execute(input: I): Promise<R>;
37
+ }
38
+ /** Exact execution extensions and original input/port references cross this seam. */
39
+ export interface PreparedAttemptExecutionInput<I extends PaymentExecutionBaseInput, R extends PaymentExecutionBaseResult, P extends string, N extends string> {
40
+ paymentPort: PreparedAttemptPersistencePort<P, N>;
41
+ executionPort: PreparedAttemptExecutionPort<I, R>;
42
+ provider: P;
43
+ paymentIntentId: string;
44
+ executionInput: I;
45
+ prepareIdempotencyKey: string;
46
+ finalizeIdempotencyKey: string;
47
+ providerIdempotencyKey: string;
48
+ providerRequestFingerprint: string;
49
+ providerFlow: string;
50
+ paymentMethodRef?: string | null;
51
+ prepareRequestPayload: Record<string, unknown>;
52
+ validateExecution?: (execution: R) => void;
53
+ }
54
+ /** Synchronous, call-scoped native trust, diagnostics and error construction. */
55
+ export interface PreparedAttemptFailurePolicy<E extends Error, N extends string> {
56
+ replayError(prepared: PreparedAttemptPersistenceResult<N>): Error;
57
+ isTrustedNoCharge(error: unknown): error is E;
58
+ logRetry(error: E, prepared: PreparedAttemptPersistenceResult<N>): void;
59
+ exhaustedError(error: E, first: E, prepared: PreparedAttemptPersistenceResult<N>): E;
60
+ executionError(error: unknown, prepared: PreparedAttemptPersistenceResult<N>): Error;
61
+ finalizationError(error: unknown, prepared: PreparedAttemptPersistenceResult<N>): Error;
62
+ }
63
+ export interface PreparedAttemptExecutionResult<R extends PaymentExecutionBaseResult> {
64
+ execution: R;
65
+ attempt: {
66
+ paymentAttemptId: string;
67
+ status: PaymentAttemptStatus;
68
+ };
69
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,303 @@
1
+ import type { ProviderReadPurpose } from "./evidence.js";
2
+ export type ReconciliationProviderKind = string;
3
+ export type ProviderReconciliationStatusKind = "succeeded" | "failed" | "pending" | "unknown";
4
+ export interface ProviderReconciliationStatus {
5
+ status: ProviderReconciliationStatusKind;
6
+ providerStatus: string;
7
+ occurredAt: string | null;
8
+ failureReason: string | null;
9
+ amountMinor: number | null;
10
+ currency: string | null;
11
+ rawPayload: Record<string, unknown>;
12
+ }
13
+ /**
14
+ * Who is asking, and therefore what an unresolved provider state MEANS.
15
+ *
16
+ * The same read answers two different questions. Reconciliation — the caller
17
+ * that names no purpose — asks "is this attempt over", long after the buyer
18
+ * left, and an intent still waiting for its first payment method is over. A
19
+ * buyer standing in a live checkout asks "is this attempt settled yet", and the
20
+ * same state means the opposite: nobody has been asked anything.
21
+ *
22
+ * ⛔ Omitting the purpose keeps the reconciliation reading. Terminalizing real
23
+ * abandonment is the more expensive of the two errors to lose, so it is the
24
+ * behaviour a caller gets by default rather than one it must opt into.
25
+ */
26
+ export interface PaymentProviderReconciliationProvider {
27
+ readPayment(input: {
28
+ providerPaymentId: string;
29
+ attempt?: ClaimedPaymentAttempt;
30
+ purpose?: ProviderReadPurpose;
31
+ }): Promise<ProviderReconciliationStatus>;
32
+ /** Atomically close an incomplete provider object before minting a replacement. */
33
+ closePayment?(input: {
34
+ providerPaymentId: string;
35
+ }): Promise<ProviderReconciliationStatus>;
36
+ /** Exact provider-side absence only; missing local acknowledgement is not
37
+ * absence proof and must never authorize another charge. */
38
+ findPaymentByLocalIntent?(input: {
39
+ attempt: ClaimedPaymentAttempt;
40
+ }): Promise<"found" | "absent">;
41
+ }
42
+ export interface ClaimedPaymentAttempt {
43
+ paymentAttemptId: string;
44
+ paymentIntentId: string;
45
+ paymentId: string;
46
+ orderId: string;
47
+ subscriptionId: string | null;
48
+ subscriptionCycleId: string | null;
49
+ provider: ReconciliationProviderKind;
50
+ providerPaymentId: string | null;
51
+ providerAttemptId: string | null;
52
+ providerSessionId: string | null;
53
+ attemptStatus: string;
54
+ intentStatus: string;
55
+ amountMinor: number;
56
+ currency: string;
57
+ orderMode: "one_time" | "subscription_cycle";
58
+ cycleRetryAttempt: number;
59
+ cycleNextRetryAt: string | null;
60
+ localUpdatedAt: string;
61
+ }
62
+ export type ManualInteractivePreparedAttemptAbsenceEvidence = {
63
+ providerAbsenceConfirmed: true;
64
+ watchdogEvidence: true;
65
+ evidenceCode: "operator_verified_absence" | "operator_documented_absence";
66
+ };
67
+ export interface InteractivePreparedAttemptReopenResult {
68
+ paymentAttemptId: string;
69
+ paymentIntentId: string;
70
+ paymentAttemptStatus: "failed";
71
+ paymentIntentStatus: "failed";
72
+ replayed: boolean;
73
+ }
74
+ export interface PaymentProviderReconciliationPort {
75
+ claimPreparedAttempts(input: {
76
+ now: string;
77
+ staleAfterSeconds: number;
78
+ limit: number;
79
+ claimKey: string;
80
+ }): Promise<ClaimedPaymentAttempt[]>;
81
+ claimStaleAttempts(input: {
82
+ now: string;
83
+ staleAfterSeconds: number;
84
+ limit: number;
85
+ claimKey: string;
86
+ }): Promise<ClaimedPaymentAttempt[]>;
87
+ recordEvidence(input: {
88
+ idempotencyKey: string;
89
+ provider: ReconciliationProviderKind;
90
+ providerPaymentId: string | null;
91
+ paymentIntentId: string;
92
+ paymentAttemptId: string;
93
+ localStatus: string;
94
+ providerStatus: string;
95
+ correctionStatus: "observed" | "corrected" | "ignored" | "failed";
96
+ checkedAt: string;
97
+ payload: Record<string, unknown>;
98
+ }): Promise<{
99
+ replayed: boolean;
100
+ }>;
101
+ applyTerminalResult(input: {
102
+ idempotencyKey: string;
103
+ expectedOrderId: string;
104
+ expectedPaymentIntentId: string;
105
+ expectedPaymentAttemptId: string;
106
+ expectedPaymentId: string;
107
+ provider: ReconciliationProviderKind;
108
+ providerPaymentId: string;
109
+ localStatus: string;
110
+ providerStatus: string;
111
+ resultStatus: "succeeded" | "failed";
112
+ occurredAt: string;
113
+ failureReason: string | null;
114
+ /**
115
+ * The class the reading adapter already decided for this refusal, persisted
116
+ * beside the refusal itself on both the attempt and the dunning case the
117
+ * terminal write opens.
118
+ *
119
+ * OPTIONAL, and absence is a real state rather than a default: a caller that
120
+ * omits it writes exactly what it wrote before this field existed, because
121
+ * the RPC coalesces it to NULL and the SQL treats a NULL class as "stamp
122
+ * nothing" rather than "stamp NULL". Two of this port's three consumers
123
+ * (`paymentVerifyNowService`, `checkoutRecoveryPaymentResolver`) resolve a
124
+ * payment without classifying it and are pinned to stay classification-absent.
125
+ *
126
+ * Shape mirrors `applyPaymentResultRpc` deliberately, so the synchronous
127
+ * decline rail and this polling rail hand the control plane the same object.
128
+ */
129
+ failureClassification?: {
130
+ failureClass: string;
131
+ decidedBy: string;
132
+ } | null;
133
+ checkedAt: string;
134
+ payload: Record<string, unknown>;
135
+ }): Promise<{
136
+ replayed: boolean;
137
+ paymentResult: {
138
+ paymentIntentId: string;
139
+ paymentAttemptId: string;
140
+ paymentId: string;
141
+ orderId: string;
142
+ status: string;
143
+ kind: string;
144
+ replayed: boolean;
145
+ };
146
+ correctionStatus: "corrected" | "ignored";
147
+ subscriptionWebhookDunning: {
148
+ opened: boolean;
149
+ replayed: boolean;
150
+ caseId: string | null;
151
+ } | null;
152
+ }>;
153
+ reopenPreparedAttemptAfterAbsence(input: {
154
+ idempotencyKey: string;
155
+ paymentAttemptId: string;
156
+ expectedPaymentIntentId: string;
157
+ expectedSubscriptionCycleId: string;
158
+ operatorRef: string;
159
+ absenceCheckedAt: string;
160
+ nextRetryAt: string;
161
+ absenceEvidence: Record<string, unknown>;
162
+ }): Promise<{
163
+ replayed: boolean;
164
+ }>;
165
+ /**
166
+ * Optional because the automatic reconciliation worker and its provider
167
+ * neutral fixtures must not acquire an operator-only interactive capability.
168
+ * A caller that selects this rail must require the narrow interface below.
169
+ */
170
+ reopenInteractivePreparedAttemptAfterAbsence?(input: {
171
+ idempotencyKey: string;
172
+ paymentAttemptId: string;
173
+ expectedPaymentIntentId: string;
174
+ expectedOrderId: string;
175
+ expectedSubscriptionId: string | null;
176
+ expectedSubscriptionCycleId: string | null;
177
+ operatorRef: string;
178
+ absenceCheckedAt: string;
179
+ absenceEvidence: ManualInteractivePreparedAttemptAbsenceEvidence;
180
+ }): Promise<InteractivePreparedAttemptReopenResult>;
181
+ }
182
+ /**
183
+ * Service-role operator boundary for an interactive checkout, distinct from
184
+ * the renewal retry RPC. It terminalizes the local prepared attempt after a
185
+ * human/provider absence confirmation; it never schedules dunning or cron.
186
+ */
187
+ export interface InteractivePreparedAttemptManualReconciliationPort extends PaymentProviderReconciliationPort {
188
+ reopenInteractivePreparedAttemptAfterAbsence(input: {
189
+ idempotencyKey: string;
190
+ paymentAttemptId: string;
191
+ expectedPaymentIntentId: string;
192
+ expectedOrderId: string;
193
+ expectedSubscriptionId: string | null;
194
+ expectedSubscriptionCycleId: string | null;
195
+ operatorRef: string;
196
+ absenceCheckedAt: string;
197
+ absenceEvidence: ManualInteractivePreparedAttemptAbsenceEvidence;
198
+ }): Promise<InteractivePreparedAttemptReopenResult>;
199
+ }
200
+ export interface PaymentProviderReconciliationWorkerResult {
201
+ ok: boolean;
202
+ checked: number;
203
+ corrected: number;
204
+ succeeded: number;
205
+ failed: number;
206
+ pending: number;
207
+ unknown: number;
208
+ ignored: number;
209
+ dunningOpened: number;
210
+ replayed: number;
211
+ providerCalls: number;
212
+ preparedWithoutProviderAck: number;
213
+ preparedAttemptsReopened: number;
214
+ manualReview: number;
215
+ /**
216
+ * Attempts whose rail has answered non-terminally for longer than that rail
217
+ * declares normal. Counted, never acted on: elapsed time is not evidence
218
+ * about money, so this surfaces a case for a human rather than resolving it.
219
+ */
220
+ silenceOverdue: number;
221
+ amountCurrencyMismatches: number;
222
+ failures: number;
223
+ skipped: boolean;
224
+ reason?: string;
225
+ }
226
+ export interface PaymentProviderReconciliationWorkerInput {
227
+ port: PaymentProviderReconciliationPort;
228
+ providers: Partial<Record<ReconciliationProviderKind, PaymentProviderReconciliationProvider>>;
229
+ now?: string;
230
+ staleAfterSeconds?: number;
231
+ batchSize?: number;
232
+ claimKey?: string;
233
+ /**
234
+ * When true, prepared attempts whose provider proves absence (see
235
+ * PaymentProviderReconciliationProvider.findPaymentByLocalIntent) and that
236
+ * are at least MIN_PREPARED_ABSENCE_REOPEN_AGE_SECONDS old are automatically
237
+ * reopened for cron retry via the operator RPC. Default false — the
238
+ * stranded-attempt loop stays operator-manual unless the flag is on.
239
+ */
240
+ autoReopenPreparedAbsence?: boolean;
241
+ /**
242
+ * Published rail capabilities, read to learn how long a non-terminal answer
243
+ * from each rail is still normal. Optional: without it no attempt is ever
244
+ * counted overdue, so omitting it preserves today's behaviour exactly.
245
+ */
246
+ capabilities?: SilenceWindowLookup;
247
+ }
248
+ /**
249
+ * The narrow slice of the capability registry this worker reads.
250
+ *
251
+ * Declared here rather than importing the whole registry type so the worker
252
+ * depends on the one question it asks - "when does this rail's silence stop
253
+ * being normal" - and not on the shape of every unattended-charge capability.
254
+ */
255
+ export interface SilenceWindowLookup {
256
+ get(providerKind: string): {
257
+ terminalOutcomeReporting?: {
258
+ silenceBecomesSuspectAfterMinutes: number;
259
+ };
260
+ } | null;
261
+ }
262
+ export interface VerifiableAttemptSnapshot {
263
+ orderId: string;
264
+ orderClientId: string;
265
+ orderMode: string;
266
+ paymentIntentId: string;
267
+ intentStatus: string;
268
+ intentProviderPaymentId: string | null;
269
+ paymentAttemptId: string;
270
+ paymentId: string;
271
+ attemptStatus: string;
272
+ provider: string;
273
+ providerAttemptId: string | null;
274
+ providerSessionId: string | null;
275
+ amountMinor: number;
276
+ currency: string;
277
+ localUpdatedAt: string;
278
+ }
279
+ export interface PaymentVerifyReadPort {
280
+ readVerifiableAttempt(input: {
281
+ orderId: string;
282
+ paymentIntentId: string;
283
+ }): Promise<VerifiableAttemptSnapshot | null>;
284
+ }
285
+ export type PaymentVerifyNowResult = {
286
+ status: string;
287
+ verified: false;
288
+ applied: false;
289
+ providerPaymentId: string | null;
290
+ attempt: ClaimedPaymentAttempt | null;
291
+ } | {
292
+ status: "unavailable" | "pending" | "manual_review";
293
+ verified: boolean;
294
+ applied: false;
295
+ providerPaymentId: string | null;
296
+ attempt: ClaimedPaymentAttempt | null;
297
+ } | {
298
+ status: "succeeded" | "failed";
299
+ verified: true;
300
+ applied: boolean;
301
+ providerPaymentId: string;
302
+ attempt: ClaimedPaymentAttempt;
303
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,92 @@
1
+ import type { PaymentMethodHandoff } from "./adapter.js";
2
+ import type { ClaimedPaymentAttempt, PaymentProviderReconciliationProvider, ProviderReconciliationStatus } from "./reconciliation.js";
3
+ import type { ProviderReadPurpose } from "./evidence.js";
4
+ export type CheckoutRecoveryPaymentResolution = {
5
+ kind: "paid";
6
+ } | {
7
+ kind: "retry_new";
8
+ } | {
9
+ kind: "resume_existing";
10
+ clientAction: PaymentRecoveryAction;
11
+ } | {
12
+ kind: "awaiting_provider";
13
+ } | {
14
+ kind: "manual_review";
15
+ };
16
+ export interface ProviderPaymentReferenceInput {
17
+ providerAttemptId: string | null;
18
+ providerSessionId: string | null;
19
+ intentProviderPaymentId: string | null;
20
+ }
21
+ export type PaymentRecoveryAction = {
22
+ provider: string;
23
+ } & PaymentMethodHandoff;
24
+ export interface ProviderRecoveryRead {
25
+ status: ProviderReconciliationStatus;
26
+ identityMatches: boolean;
27
+ configuredMoneyMatches: boolean;
28
+ manualReviewRequired: boolean;
29
+ clientAction: PaymentRecoveryAction | null;
30
+ }
31
+ /**
32
+ * Recovery is an explicit capability, not an optional method on generic
33
+ * reconciliation providers. One read returns both PSP truth and any transient
34
+ * buyer action, so a redeem never calls the provider twice.
35
+ */
36
+ export interface PaymentProviderRecoveryProvider extends PaymentProviderReconciliationProvider {
37
+ resolvePaymentReference(input: ProviderPaymentReferenceInput): string | null;
38
+ readRecoveryPayment(input: {
39
+ providerPaymentId: string;
40
+ attempt: ClaimedPaymentAttempt;
41
+ purpose?: ProviderReadPurpose;
42
+ }): Promise<ProviderRecoveryRead>;
43
+ }
44
+ export interface CheckoutRecoveryPaymentResolver {
45
+ resolve(input: {
46
+ orderId: string;
47
+ paymentIntentId: string | null;
48
+ intentStatus?: string | null;
49
+ }): Promise<CheckoutRecoveryPaymentResolution>;
50
+ }
51
+ export interface ActivePaymentActionAuthority {
52
+ orderId: string;
53
+ clientId: string;
54
+ paymentIntentId: string;
55
+ paymentAttemptId: string;
56
+ executionRail: string;
57
+ }
58
+ export interface CheckoutActivePaymentActionResolver {
59
+ readActiveAction(authority: ActivePaymentActionAuthority): Promise<PaymentRecoveryAction | null>;
60
+ }
61
+ export type CheckoutActionReadProvider = Pick<PaymentProviderRecoveryProvider, "resolvePaymentReference" | "readRecoveryPayment">;
62
+ /** Method collection only. Hosts authorize ownership before calling this port.
63
+ * The result never confirms payment, redeems a token or writes a subscription.
64
+ * Handoffs remain transient; unsupported must be shown as an explicit refusal.
65
+ */
66
+ export interface PaymentRepairMethodPort {
67
+ setupMethod(input: {
68
+ customer: string;
69
+ metadata: Record<string, string>;
70
+ idempotencyKey: string;
71
+ }): Promise<{
72
+ kind: "ready";
73
+ setupRef: string;
74
+ handoff: PaymentMethodHandoff;
75
+ replayed: boolean;
76
+ } | {
77
+ kind: "unsupported";
78
+ }>;
79
+ }
80
+ /** Host-selected durable truth scope. Unsupported schema never opens a store. */
81
+ export interface PaymentServicesOptions {
82
+ schema?: {
83
+ truth: "supported" | "unsupported";
84
+ };
85
+ openTruth?: () => Promise<{
86
+ port: import("./truth.js").PaymentTruthPort;
87
+ close(): Promise<void>;
88
+ }>;
89
+ }
90
+ export interface PaymentServices {
91
+ run<T>(work: (port: import("./truth.js").PaymentTruthPort) => Promise<T>): Promise<T>;
92
+ }
@@ -0,0 +1 @@
1
+ export {};