@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,53 @@
1
+ import { type PaymentFailureEvidence } from "./paymentFailureEvidence.js";
2
+ import type { PaymentExecutionResult } from "./donorContracts.js";
3
+ /**
4
+ * Normalization of a refusal the provider throws instead of returning.
5
+ *
6
+ * Split from the execution adapter because it is a different job: the adapter
7
+ * creates intents and reports what came back, this reads an exception and
8
+ * decides whether it is evidence of a refusal at all — the decision that keeps a
9
+ * possibly-paid charge out of the terminal `failed` path.
10
+ */
11
+ /**
12
+ * The provider's error type for an issuer refusal, spelled exactly as the SDK
13
+ * sets it. Exported so the decline branch and its proofs cannot drift apart.
14
+ */
15
+ export declare const PROVIDER_DECLINE_ERROR_TYPE = "StripeCardError";
16
+ /** What the adapter learned from a refusal: the fact itself plus its correlation handle. */
17
+ export interface ExecutionDecline {
18
+ diagnosticFailureEvidence?: PaymentFailureEvidence;
19
+ decline: NonNullable<PaymentExecutionResult["providerDecline"]>;
20
+ /** The intent the provider had already created, when the refusal names one. */
21
+ providerAttemptId: string | null;
22
+ providerStatus: string | null;
23
+ }
24
+ /** The half of an execution result a refusal determines; the adapter owns the rest. */
25
+ type DeclinedExecutionFacts = Omit<PaymentExecutionResult, "provider" | "requestPayload">;
26
+ /**
27
+ * Reads an issuer refusal out of a thrown provider error, or returns null.
28
+ *
29
+ * Deliberately narrow: only the provider's own refusal type qualifies. Codes
30
+ * alone would not — an infrastructure error can also carry a `code`, and
31
+ * treating one as a decline would apply a terminal `failed` result to a charge
32
+ * that may have gone through.
33
+ */
34
+ export declare function declineFromError(error: unknown): ExecutionDecline | null;
35
+ /**
36
+ * Turns a refusal into attempt facts, DELIBERATELY without a correlation handle.
37
+ *
38
+ * `attemptStatus` stays non-terminal by contract — only the control plane makes
39
+ * terminal transitions, and it does so from `providerDecline`.
40
+ *
41
+ * The refused intent's id is reported ONLY inside `responsePayload`, never as
42
+ * `providerAttemptId`. That is not an oversight: event ingest matches a callback
43
+ * to an attempt by `provider_attempt_id`, and the control plane's failed branch
44
+ * has no guard against applying `failed` to an already-failed intent. So a
45
+ * correlation handle would let the provider's later failure callback re-apply the
46
+ * same refusal under its own idempotency key — advancing the retry ladder a
47
+ * second rung, rewriting the failure reason, and sending the customer a second
48
+ * dunning email minutes after the first. Until that branch is idempotent
49
+ * (planned PR-0c), a refusal this rail already reported must stay uncorrelated,
50
+ * and the callback must stay inert.
51
+ */
52
+ export declare function declinedExecutionFacts(declined: ExecutionDecline): DeclinedExecutionFacts;
53
+ export {};
@@ -0,0 +1,107 @@
1
+ import { stripeFailureEvidence } from "./stripeFailureEvidence.js";
2
+ import { failureEvidencePayload } from "./paymentFailureEvidence.js";
3
+ import { declineCodeHints } from "./declineFailureHints.js";
4
+ /**
5
+ * Normalization of a refusal the provider throws instead of returning.
6
+ *
7
+ * Split from the execution adapter because it is a different job: the adapter
8
+ * creates intents and reports what came back, this reads an exception and
9
+ * decides whether it is evidence of a refusal at all — the decision that keeps a
10
+ * possibly-paid charge out of the terminal `failed` path.
11
+ */
12
+ /**
13
+ * The provider's error type for an issuer refusal, spelled exactly as the SDK
14
+ * sets it. Exported so the decline branch and its proofs cannot drift apart.
15
+ */
16
+ export const PROVIDER_DECLINE_ERROR_TYPE = "StripeCardError";
17
+ /** Used when the provider refuses without naming a code of its own. */
18
+ const UNSPECIFIED_DECLINE_CODE = "provider_declined";
19
+ /**
20
+ * Reads an issuer refusal out of a thrown provider error, or returns null.
21
+ *
22
+ * Deliberately narrow: only the provider's own refusal type qualifies. Codes
23
+ * alone would not — an infrastructure error can also carry a `code`, and
24
+ * treating one as a decline would apply a terminal `failed` result to a charge
25
+ * that may have gone through.
26
+ */
27
+ export function declineFromError(error) {
28
+ if (!error || typeof error !== "object")
29
+ return null;
30
+ const raw = error;
31
+ if (raw.type !== PROVIDER_DECLINE_ERROR_TYPE)
32
+ return null;
33
+ const intent = raw.payment_intent && typeof raw.payment_intent === "object"
34
+ ? raw.payment_intent
35
+ : null;
36
+ const declineCode = readOptionalString(raw, "decline_code");
37
+ const adviceCode = readOptionalString(raw, "advice_code");
38
+ // Translated here, where the code vocabulary is owned, so the control plane
39
+ // classifies the refusal without ever seeing an acquirer code.
40
+ const neutralReasonHints = declineCodeHints(declineCode);
41
+ const evidence = stripeFailureEvidence(raw, { source: "execution" });
42
+ return {
43
+ ...(evidence ? { diagnosticFailureEvidence: evidence } : {}),
44
+ decline: {
45
+ code: readOptionalString(raw, "code") ?? UNSPECIFIED_DECLINE_CODE,
46
+ // Reserved for a payer bank that cannot hold a reusable mandate at all.
47
+ // A refused charge on an already-registered method proves the opposite,
48
+ // so this rail never claims it.
49
+ mandateUnsupported: false,
50
+ ...(declineCode ? { declineCode } : {}),
51
+ ...(adviceCode ? { adviceCode } : {}),
52
+ ...(neutralReasonHints.length > 0 ? { neutralReasonHints } : {}),
53
+ },
54
+ providerAttemptId: intent ? readOptionalString(intent, "id") ?? null : null,
55
+ providerStatus: intent ? readOptionalString(intent, "status") ?? null : null,
56
+ };
57
+ }
58
+ /**
59
+ * Turns a refusal into attempt facts, DELIBERATELY without a correlation handle.
60
+ *
61
+ * `attemptStatus` stays non-terminal by contract — only the control plane makes
62
+ * terminal transitions, and it does so from `providerDecline`.
63
+ *
64
+ * The refused intent's id is reported ONLY inside `responsePayload`, never as
65
+ * `providerAttemptId`. That is not an oversight: event ingest matches a callback
66
+ * to an attempt by `provider_attempt_id`, and the control plane's failed branch
67
+ * has no guard against applying `failed` to an already-failed intent. So a
68
+ * correlation handle would let the provider's later failure callback re-apply the
69
+ * same refusal under its own idempotency key — advancing the retry ladder a
70
+ * second rung, rewriting the failure reason, and sending the customer a second
71
+ * dunning email minutes after the first. Until that branch is idempotent
72
+ * (planned PR-0c), a refusal this rail already reported must stay uncorrelated,
73
+ * and the callback must stay inert.
74
+ */
75
+ export function declinedExecutionFacts(declined) {
76
+ const { decline, providerAttemptId, providerStatus } = declined;
77
+ return {
78
+ providerAttemptId: null,
79
+ providerSessionId: null,
80
+ attemptStatus: "processing",
81
+ nextActionKind: null,
82
+ providerDecline: decline,
83
+ clientSecret: null,
84
+ customerProviderRef: null,
85
+ paymentMethodRef: null,
86
+ // This rail already reported the refusal synchronously; the callback that
87
+ // repeats it must not be waited for, and must not be able to land twice.
88
+ webhookExpected: false,
89
+ recoveryRequired: false,
90
+ responsePayload: {
91
+ // Forensics only. This payload is stored on the attempt but nothing joins
92
+ // on it, so an operator can still trace the refused intent by hand without
93
+ // the ingest matcher ever finding it.
94
+ providerStatus,
95
+ providerAttemptId,
96
+ providerDeclined: true,
97
+ ...failureEvidencePayload(declined.diagnosticFailureEvidence),
98
+ // Codes only, never the provider's message: that text is free-form and can
99
+ // carry payer-identifying detail, and this payload is persisted and logged.
100
+ providerErrorCodes: [decline.code, decline.declineCode, decline.adviceCode].filter((code) => Boolean(code)),
101
+ },
102
+ };
103
+ }
104
+ function readOptionalString(value, key) {
105
+ const raw = value[key];
106
+ return typeof raw === "string" && raw.length > 0 ? raw : undefined;
107
+ }
@@ -0,0 +1,16 @@
1
+ import type { PaymentAdapter, PaymentAdapterCapabilities } from "../contracts/index.js";
2
+ import { type StripeSandboxClient } from "./stripeSandboxPaymentExecutionAdapter.js";
3
+ import { type StripePaymentIntentReader } from "./stripePaymentReconciliationProvider.js";
4
+ /** Host-owned client, credentials, SDK construction and mode policy. clock returns UTC seconds. */
5
+ export interface StripePaymentAdapterConfig {
6
+ client: Omit<StripeSandboxClient, "createSetupIntent"> & Partial<Pick<StripeSandboxClient, "createSetupIntent">> & StripePaymentIntentReader;
7
+ webhookSigningSecrets: readonly string[];
8
+ mode: "live" | "test";
9
+ supportedCurrencies: readonly string[];
10
+ toleranceSeconds?: number;
11
+ clock?: () => number;
12
+ }
13
+ /** Observed protocol capabilities; account activation remains host-owned. */
14
+ export declare const stripePaymentCapabilities: Readonly<PaymentAdapterCapabilities>;
15
+ /** Explicit construction is the only activation point; importing performs no I/O. */
16
+ export declare function createStripePaymentAdapter(config: StripePaymentAdapterConfig): PaymentAdapter;
@@ -0,0 +1,239 @@
1
+ import { createHash } from "node:crypto";
2
+ import { classifyPaymentFailure, isPaymentRetryAdviceCode } from "@openlup/core/payment";
3
+ import { createStripeSandboxPaymentExecutionAdapter } from "./stripeSandboxPaymentExecutionAdapter.js";
4
+ import { createStripePaymentReconciliationProvider } from "./stripePaymentReconciliationProvider.js";
5
+ import { normalizeStripeWebhookEvent } from "./stripeWebhookNormalizer.js";
6
+ import { verifyStripeSignature } from "./webhookSignature.js";
7
+ import { declineCodeHints } from "./declineFailureHints.js";
8
+ /** Observed protocol capabilities; account activation remains host-owned. */
9
+ export const stripePaymentCapabilities = Object.freeze({
10
+ providerKind: "stripe",
11
+ captureFlows: Object.freeze([Object.freeze({ kind: "card_payment", handoff: "client-confirmed", methodFamilies: Object.freeze(["card", "wallet"]) })]),
12
+ requiresStoredMandateEvidence: false,
13
+ assessMandate: () => ({ chargeable: true }),
14
+ unattendedChargeFlow: "off_session_payment",
15
+ payerContext: Object.freeze({ requiresPayerBlock: false, customerRefFallsBackToContactEmail: false }),
16
+ methodHealth: Object.freeze({ requiresCustomerRef: true, requiredMethodKind: null, requiresPayerContact: false }),
17
+ terminalOutcomeReporting: Object.freeze({ kind: "status_field", silenceBecomesSuspectAfterMinutes: 360 }),
18
+ mandateUpsertIsSubscriptionScoped: false,
19
+ /** declared by the host through the adapter config */
20
+ supportedCurrencies: Object.freeze([]),
21
+ unattended: "supported", readback: "supported", close: "unsupported", exactAbsence: "unsupported",
22
+ refundObservation: "supported", refundCreate: "unsupported", authorizationCaptureSplit: "unsupported",
23
+ });
24
+ const refusal = (reason) => ({ kind: "refusal", reason });
25
+ /** Explicit construction is the only activation point; importing performs no I/O. */
26
+ export function createStripePaymentAdapter(config) {
27
+ if (config.mode !== "live" && config.mode !== "test")
28
+ throw new Error("Invalid payment mode");
29
+ const declaredCurrencies = config.supportedCurrencies;
30
+ if (!Array.isArray(declaredCurrencies) || declaredCurrencies.length === 0
31
+ || ![...declaredCurrencies].every(currency => typeof currency === "string" && currency.length === 3 && /^[A-Z]{3}$/u.test(currency))) {
32
+ throw new Error("Invalid supported currencies");
33
+ }
34
+ const supportedCurrencies = Object.freeze([...declaredCurrencies]);
35
+ const tolerance = config.toleranceSeconds ?? 300;
36
+ if (!Number.isFinite(tolerance) || tolerance < 0)
37
+ throw new Error("Invalid signature tolerance");
38
+ const secrets = [...config.webhookSigningSecrets];
39
+ const now = config.clock ?? (() => Math.floor(Date.now() / 1000));
40
+ const execution = createStripeSandboxPaymentExecutionAdapter({ client: {
41
+ createPaymentIntent: config.client.createPaymentIntent.bind(config.client),
42
+ createSetupIntent: inputSetup,
43
+ ensureCustomer: config.client.ensureCustomer?.bind(config.client),
44
+ } });
45
+ function inputSetup(...args) {
46
+ if (!config.client.createSetupIntent)
47
+ throw new Error("Method setup unsupported by injected client");
48
+ return config.client.createSetupIntent(...args);
49
+ }
50
+ const reader = createStripePaymentReconciliationProvider(config.client);
51
+ const validateInput = input => {
52
+ if (input.mode !== config.mode || !["initial", "recovery", "renewal-unattended"].includes(input.purpose)
53
+ || !input.attemptRef || !input.intentRef || !input.fingerprint || !input.providerIdempotencyKey
54
+ || !Number.isSafeInteger(input.money.amountMinor) || input.money.amountMinor <= 0
55
+ || !supportedCurrencies.includes(input.money.currency)
56
+ || input.customerAuthorization !== undefined
57
+ || (input.purpose === "renewal-unattended" && !input.storedMethod?.reference)) {
58
+ throw new Error("Unsupported payment input");
59
+ }
60
+ };
61
+ const read = async (input) => {
62
+ const base = { identity: input.identity, expectedMoney: input.expectedMoney, purpose: input.purpose };
63
+ if (input.identity.provider !== "stripe" || !input.identity.providerPaymentRef)
64
+ return { ...base, kind: "unsupported" };
65
+ try {
66
+ // Bind the response to the requested remote object before translating status.
67
+ const intent = await config.client.retrievePaymentIntent(input.identity.providerPaymentRef);
68
+ if (intent.id !== input.identity.providerPaymentRef || intent.amount !== input.expectedMoney.amountMinor
69
+ || intent.currency?.toUpperCase() !== input.expectedMoney.currency.toUpperCase()) {
70
+ return { ...base, kind: "unknown", reason: "indeterminate" };
71
+ }
72
+ const status = await createStripePaymentReconciliationProvider({ retrievePaymentIntent: async () => intent })
73
+ .readPayment({ providerPaymentId: input.identity.providerPaymentRef, purpose: input.purpose });
74
+ return readback(base, status);
75
+ }
76
+ catch {
77
+ return { ...base, kind: "unknown", reason: "indeterminate" };
78
+ }
79
+ };
80
+ return {
81
+ capabilities: { ...stripePaymentCapabilities, supportedCurrencies, close: config.client.cancelPaymentIntent ? "supported" : "unsupported" },
82
+ attemptPort: {
83
+ validateInput,
84
+ async execute(input) {
85
+ validateInput(input);
86
+ const result = await execution.execute({
87
+ idempotencyKey: input.attemptRef, providerIdempotencyKey: input.providerIdempotencyKey,
88
+ providerRequestFingerprint: input.fingerprint, paymentIntentId: input.intentRef,
89
+ amountMinor: input.money.amountMinor, currency: input.money.currency,
90
+ mode: input.purpose === "renewal-unattended" ? "subscription_cycle" : "one_time",
91
+ orderRef: input.intentRef,
92
+ providerFlow: input.purpose === "renewal-unattended" ? "off_session_payment" : "one_time_payment",
93
+ paymentMethodRef: input.storedMethod?.reference,
94
+ });
95
+ return {
96
+ outcome: result.providerDecline
97
+ ? { kind: "decline", refusal: neutralDecline(result.providerDecline) }
98
+ : { kind: "attempt", status: result.attemptStatus },
99
+ // A synchronous issuer decline is not proof of a pre-dispatch refusal.
100
+ dispatch: result.providerAttemptId ? { kind: "dispatched", providerPaymentRef: result.providerAttemptId } : { kind: "unknown" },
101
+ ...(result.clientSecret ? { handoff: { kind: "client-confirmed", payload: result.clientSecret } } : {}),
102
+ diagnostics: { webhookExpected: result.webhookExpected === true, recoveryRequired: result.recoveryRequired === true },
103
+ };
104
+ },
105
+ },
106
+ verifyPort: {
107
+ async verifyAndNormalize(input) {
108
+ const header = Object.entries(input.headers).find(([key]) => key.toLowerCase() === "stripe-signature")?.[1];
109
+ if (typeof header !== "string")
110
+ return refusal("signature");
111
+ const parts = Object.fromEntries(header.split(",").map(part => part.split("=")).filter(([key, value]) => key && value));
112
+ const timestamp = Number(parts.t);
113
+ const current = now();
114
+ if (!parts.t || !parts.v1 || !Number.isFinite(timestamp) || !Number.isFinite(current))
115
+ return refusal("signature");
116
+ // The preserved verifier supplies HMAC and comparison; this layer owns configurable freshness.
117
+ if (Math.abs(current - timestamp) > tolerance)
118
+ return refusal("stale");
119
+ const rawBody = Buffer.from(input.rawBody).toString("utf8");
120
+ if (!Buffer.from(rawBody).equals(Buffer.from(input.rawBody)))
121
+ return refusal("malformed");
122
+ if (!verifyStripeSignature({ rawBody, signatureHeader: header, endpointSecret: secrets, now: () => timestamp }))
123
+ return refusal("signature");
124
+ try {
125
+ const raw = JSON.parse(rawBody);
126
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
127
+ return refusal("malformed");
128
+ const event = raw;
129
+ if (typeof event.livemode === "boolean" && event.livemode !== (config.mode === "live"))
130
+ return refusal("unsupported-event");
131
+ return mapVerifiedStripeEvent(event);
132
+ }
133
+ catch {
134
+ return refusal("malformed");
135
+ }
136
+ },
137
+ },
138
+ readbackPort: {
139
+ readPayment: read,
140
+ ...(reader.closePayment ? { close: async (input) => {
141
+ const base = { identity: input.identity, expectedMoney: input.expectedMoney, purpose: input.purpose };
142
+ if (input.identity.provider !== "stripe" || !input.identity.providerPaymentRef)
143
+ return { ...base, kind: "unsupported" };
144
+ try {
145
+ // Check configured money and object identity before a destructive provider operation.
146
+ const before = await read(input);
147
+ if (before.kind === "unknown" || before.kind === "unsupported")
148
+ return before;
149
+ await reader.closePayment({ providerPaymentId: input.identity.providerPaymentRef });
150
+ return read(input);
151
+ }
152
+ catch {
153
+ return { ...base, kind: "unknown", reason: "indeterminate" };
154
+ }
155
+ } } : {}),
156
+ },
157
+ };
158
+ }
159
+ function neutralDecline(decline) {
160
+ const hints = decline.neutralReasonHints ?? [];
161
+ const classified = classifyPaymentFailure({ adviceCode: decline.adviceCode, neutralReasonHints: hints });
162
+ return { code: classified.failureClass, hints,
163
+ ...(decline.adviceCode && isPaymentRetryAdviceCode(decline.adviceCode) ? { adviceCode: decline.adviceCode } : {}) };
164
+ }
165
+ function readback(base, status) {
166
+ if (status.status === "unknown" || status.amountMinor === null || !Number.isSafeInteger(status.amountMinor) || !status.currency) {
167
+ return { ...base, kind: "unknown", reason: "indeterminate" };
168
+ }
169
+ return { ...base, kind: status.status, observedMoney: { amountMinor: status.amountMinor, currency: status.currency }, providerOccurredAt: status.occurredAt };
170
+ }
171
+ /** Preserve donor money classifications in the neutral evidence union. */
172
+ function mapStripeOutcome(eventType) {
173
+ switch (eventType) {
174
+ case "payment.succeeded": return "succeeded";
175
+ case "payment.failed": return "failed";
176
+ case "payment.refunded": return "refunded";
177
+ case "payment.requires_action": return "pending";
178
+ case "payment.disputed": return "disputed";
179
+ default: return null;
180
+ }
181
+ }
182
+ function mapVerifiedStripeEvent(raw) {
183
+ let normalized;
184
+ try {
185
+ normalized = normalizeStripeWebhookEvent(raw);
186
+ }
187
+ catch (error) {
188
+ return refusal(error instanceof Error && error.message.startsWith("Unsupported Stripe webhook event:") ? "unsupported-event" : "malformed");
189
+ }
190
+ const object = raw.data.object;
191
+ const occurred = typeof raw.created === "number" && Number.isFinite(raw.created)
192
+ ? normalized.occurredAt : typeof raw.created === "string" && Number.isFinite(Date.parse(raw.created)) ? normalized.occurredAt : null;
193
+ const metadata = object.metadata && typeof object.metadata === "object" ? object.metadata : {};
194
+ const intentRef = typeof metadata.paymentIntentId === "string" ? metadata.paymentIntentId : null;
195
+ const identity = { provider: "stripe", providerEventId: normalized.providerEventId,
196
+ providerOccurredAt: occurred };
197
+ let result;
198
+ if (normalized.methodLifecycle || normalized.eventType.startsWith("setup.")) {
199
+ const lifecycle = normalized.methodLifecycle;
200
+ const method = lifecycle?.providerMethodRef ?? (typeof object.payment_method === "string" ? object.payment_method
201
+ : object.payment_method && typeof object.payment_method === "object" ? object.payment_method.id : null);
202
+ if (typeof method !== "string" || !method)
203
+ return refusal("unsupported-event");
204
+ const outcomes = {
205
+ method_registered: "registered", method_updated: "updated", method_revoked: "revoked", method_expired: "expired", method_suspended: "suspended",
206
+ "setup.succeeded": "registered", "setup.failed": "failed", "setup.requires_action": "requires-action",
207
+ };
208
+ const outcome = outcomes[lifecycle?.kind ?? normalized.eventType];
209
+ if (!outcome)
210
+ return refusal("unsupported-event");
211
+ result = { ...identity, kind: "method", outcome, methodRef: method,
212
+ setupRef: typeof raw.type === "string" && raw.type.startsWith("setup_intent.") ? normalized.providerPaymentId : null, intentRef,
213
+ // Missing provider time must not turn the donor's epoch fallback into a lifecycle fact.
214
+ ...(lifecycle && occurred !== null ? { methodLifecycle: lifecycle } : {}) };
215
+ }
216
+ else {
217
+ const outcome = mapStripeOutcome(normalized.eventType);
218
+ if (!outcome)
219
+ return refusal("unsupported-event");
220
+ const money = Number.isSafeInteger(normalized.amountMinor) && normalized.amountMinor > 0 && normalized.currency
221
+ ? { amountMinor: normalized.amountMinor, currency: normalized.currency } : null;
222
+ const snapshot = normalized.rawPayload.stripeRefundSnapshot;
223
+ if (outcome === "refunded" && (!snapshot || !money))
224
+ return refusal("malformed");
225
+ result = { ...identity, kind: "money", providerPaymentRef: normalized.providerPaymentId, intentRef, attemptRef: null, outcome, money,
226
+ ...(snapshot && money ? { refund: { kind: "cumulative-snapshot", operationId: snapshot.chargeId,
227
+ originalPaymentMoney: money, refundedMoney: { amountMinor: snapshot.amountRefundedMinor, currency: money.currency } } } : {}),
228
+ ...(outcome === "failed" && object.last_payment_error && typeof object.last_payment_error === "object"
229
+ ? { refusal: neutralDecline({ code: "provider_declined", mandateUnsupported: false,
230
+ neutralReasonHints: declineCodeHints(object.last_payment_error.decline_code),
231
+ adviceCode: typeof object.last_payment_error.advice_code === "string"
232
+ ? object.last_payment_error.advice_code : undefined }) } : {}),
233
+ };
234
+ }
235
+ // The donor normalizer constructs this filtered carrier, never the raw body.
236
+ // Keep its reader dialect and provenance outside normalized event identity.
237
+ return { ...result, fingerprint: createHash("sha256").update(JSON.stringify(result)).digest("hex"),
238
+ providerAuditPayload: normalized.rawPayload };
239
+ }
@@ -0,0 +1,26 @@
1
+ import { type StripeIntentLike } from "./stripeFailureEvidence.js";
2
+ type Reference = string | {
3
+ id: string;
4
+ } | null;
5
+ interface StripeIntentShape {
6
+ id: string;
7
+ status: string;
8
+ customer?: Reference;
9
+ payment_method?: Reference;
10
+ latest_charge?: Reference;
11
+ client_secret?: string | null;
12
+ next_action?: {
13
+ type?: string | null;
14
+ } | null;
15
+ amount?: number;
16
+ amount_received?: number;
17
+ currency?: string;
18
+ last_payment_error?: {
19
+ code?: string | null;
20
+ message?: string | null;
21
+ type?: string | null;
22
+ } | null;
23
+ }
24
+ export declare function mapStripeIntent(intent: StripeIntentShape, knownCardOnlyRequest?: boolean, source?: "execution" | "readback"): StripeIntentLike;
25
+ export declare function mapSetupIntent(intent: StripeIntentShape): StripeIntentLike;
26
+ export {};
@@ -0,0 +1,51 @@
1
+ import { stripeFailureEvidence } from "./stripeFailureEvidence.js";
2
+ export function mapStripeIntent(intent, knownCardOnlyRequest = false, source = "readback") {
3
+ const customer = typeof intent.customer === "string" ? intent.customer : intent.customer?.id ?? null;
4
+ const paymentMethod = typeof intent.payment_method === "string"
5
+ ? intent.payment_method
6
+ : intent.payment_method?.id ?? null;
7
+ const latestCharge = typeof intent.latest_charge === "string"
8
+ ? intent.latest_charge
9
+ : intent.latest_charge?.id ?? null;
10
+ const mapped = {
11
+ id: intent.id,
12
+ status: intent.status,
13
+ client_secret: intent.client_secret,
14
+ customer,
15
+ payment_method: paymentMethod,
16
+ latest_charge: latestCharge,
17
+ next_action: intent.next_action ? { type: intent.next_action.type ?? null } : null,
18
+ };
19
+ if (typeof intent.amount === "number")
20
+ mapped.amount = intent.amount;
21
+ if (typeof intent.amount_received === "number")
22
+ mapped.amount_received = intent.amount_received;
23
+ if (typeof intent.currency === "string")
24
+ mapped.currency = intent.currency;
25
+ if (intent.last_payment_error) {
26
+ mapped.last_payment_error = {
27
+ code: intent.last_payment_error.code ?? null,
28
+ message: intent.last_payment_error.message ?? null,
29
+ type: intent.last_payment_error.type ?? null,
30
+ };
31
+ }
32
+ const evidence = stripeFailureEvidence(intent, { source, knownCardOnlyRequest });
33
+ if (evidence)
34
+ mapped.diagnosticFailureEvidence = evidence;
35
+ return mapped;
36
+ }
37
+ export function mapSetupIntent(intent) {
38
+ const customer = typeof intent.customer === "string" ? intent.customer : intent.customer?.id ?? null;
39
+ const paymentMethod = typeof intent.payment_method === "string"
40
+ ? intent.payment_method
41
+ : intent.payment_method?.id ?? null;
42
+ return {
43
+ id: intent.id,
44
+ status: intent.status,
45
+ client_secret: intent.client_secret,
46
+ customer,
47
+ payment_method: paymentMethod,
48
+ latest_charge: null,
49
+ next_action: intent.next_action ? { type: intent.next_action.type ?? null } : null,
50
+ };
51
+ }
@@ -0,0 +1,10 @@
1
+ import type { PaymentMethodReplacementFacts } from "@openlup/core/payment";
2
+ import type { NormalizedProviderPaymentWebhook } from "./donorContracts.js";
3
+ import type { PaymentMethodLifecycleEvent } from "@openlup/core/payment";
4
+ export interface ProviderWebhookWithMethodLifecycle extends NormalizedProviderPaymentWebhook {
5
+ methodLifecycle?: PaymentMethodLifecycleEvent | null;
6
+ }
7
+ export declare const METHOD_SCHEME_SNAPSHOT_KEY = "methodScheme";
8
+ export declare const METHOD_LAST_DIGITS_SNAPSHOT_KEY = "methodLastDigits";
9
+ export declare const METHOD_EXPIRES_AT_SNAPSHOT_KEY = "methodExpiresAt";
10
+ export declare function methodFactSnapshotKeys(facts: PaymentMethodReplacementFacts | null | undefined): Record<string, unknown>;
@@ -0,0 +1,12 @@
1
+ export const METHOD_SCHEME_SNAPSHOT_KEY = "methodScheme";
2
+ export const METHOD_LAST_DIGITS_SNAPSHOT_KEY = "methodLastDigits";
3
+ export const METHOD_EXPIRES_AT_SNAPSHOT_KEY = "methodExpiresAt";
4
+ export function methodFactSnapshotKeys(facts) {
5
+ if (!facts)
6
+ return {};
7
+ return {
8
+ ...(facts.schemeLabel ? { [METHOD_SCHEME_SNAPSHOT_KEY]: facts.schemeLabel } : {}),
9
+ ...(facts.lastDigits ? { [METHOD_LAST_DIGITS_SNAPSHOT_KEY]: facts.lastDigits } : {}),
10
+ ...(facts.expiresAt ? { [METHOD_EXPIRES_AT_SNAPSHOT_KEY]: facts.expiresAt } : {}),
11
+ };
12
+ }
@@ -0,0 +1,26 @@
1
+ import type { PaymentRecoveryMethod, PaymentRecoveryOperation } from "@openlup/core/payment";
2
+ export type FailureObservationSource = "execution" | "webhook" | "readback";
3
+ export type FailureObservationDisposition = "present" | "absent" | "unreadable" | "read_failed" | "read_timeout";
4
+ /** Protected provider facts, not a customer message or a retry-policy input. */
5
+ export interface PaymentFailureEvidence {
6
+ version: 1;
7
+ source: FailureObservationSource;
8
+ disposition: FailureObservationDisposition;
9
+ refusalVerified: boolean;
10
+ providerPaymentId: string | null;
11
+ providerChargeId: string | null;
12
+ code: string | null;
13
+ declineCode: string | null;
14
+ adviceCode: string | null;
15
+ adviceOrigin: "provider" | "inferred" | "unknown";
16
+ operation: PaymentRecoveryOperation | null;
17
+ method: PaymentRecoveryMethod | null;
18
+ methodSource: "provider" | "execution_contract" | "unknown";
19
+ }
20
+ /** Bounded machine tokens only. Free-form response fields never cross this seam. */
21
+ export declare function evidenceToken(value: unknown): string | null;
22
+ export declare function evidenceRecord(value: unknown): Record<string, unknown>;
23
+ /** Explicit construction prevents accidental payload expansion by object spreading. */
24
+ export declare function serializeFailureEvidence(input: PaymentFailureEvidence): PaymentFailureEvidence;
25
+ /** Persist beside legacy fields; never merge the diagnostic codes into them. */
26
+ export declare function failureEvidencePayload(evidence: PaymentFailureEvidence | null | undefined): Record<string, unknown>;
@@ -0,0 +1,33 @@
1
+ /** Bounded machine tokens only. Free-form response fields never cross this seam. */
2
+ export function evidenceToken(value) {
3
+ return typeof value === "string" && /^[a-zA-Z0-9_:.-]{1,96}$/.test(value) ? value : null;
4
+ }
5
+ export function evidenceRecord(value) {
6
+ return value !== null && typeof value === "object" && !Array.isArray(value)
7
+ ? value : {};
8
+ }
9
+ /** Explicit construction prevents accidental payload expansion by object spreading. */
10
+ export function serializeFailureEvidence(input) {
11
+ const method = input.method;
12
+ const kind = evidenceToken(method?.kind);
13
+ const recoveryMethodKey = evidenceToken(method?.recoveryMethodKey);
14
+ return {
15
+ version: 1,
16
+ source: input.source,
17
+ disposition: input.disposition,
18
+ refusalVerified: input.refusalVerified,
19
+ providerPaymentId: evidenceToken(input.providerPaymentId),
20
+ providerChargeId: evidenceToken(input.providerChargeId),
21
+ code: evidenceToken(input.code),
22
+ declineCode: evidenceToken(input.declineCode),
23
+ adviceCode: evidenceToken(input.adviceCode),
24
+ adviceOrigin: input.adviceOrigin,
25
+ operation: input.operation,
26
+ method: kind && recoveryMethodKey && method ? { kind, recoveryMethodKey, interaction: method.interaction } : null,
27
+ methodSource: kind && recoveryMethodKey ? input.methodSource : "unknown",
28
+ };
29
+ }
30
+ /** Persist beside legacy fields; never merge the diagnostic codes into them. */
31
+ export function failureEvidencePayload(evidence) {
32
+ return evidence ? { failureEvidence: serializeFailureEvidence(evidence) } : {};
33
+ }
@@ -0,0 +1,41 @@
1
+ import { type PaymentRecoveryEvidence } from "@openlup/core/payment";
2
+ import { type FailureObservationSource, type PaymentFailureEvidence } from "./paymentFailureEvidence.js";
3
+ export interface StripeIntentLike {
4
+ id: string;
5
+ status: string;
6
+ amount?: number | null;
7
+ amount_received?: number | null;
8
+ currency?: string | null;
9
+ client_secret?: string | null;
10
+ customer?: string | null;
11
+ payment_method?: string | null;
12
+ latest_charge?: string | null;
13
+ next_action?: {
14
+ type?: string | null;
15
+ } | null;
16
+ last_payment_error?: {
17
+ code?: string | null;
18
+ message?: string | null;
19
+ type?: string | null;
20
+ decline_code?: string | null;
21
+ advice_code?: string | null;
22
+ } | null;
23
+ /** Kept separate: populating legacy fine fields would change renewal classification. */
24
+ diagnosticFailureEvidence?: PaymentFailureEvidence;
25
+ }
26
+ /** Reads only one current PaymentIntent/error, never an arbitrary historical Charge. */
27
+ export declare function stripeFailureEvidence(input: unknown, options: {
28
+ source: FailureObservationSource;
29
+ confirmedFailureEvent?: boolean;
30
+ knownCardOnlyRequest?: boolean;
31
+ }): PaymentFailureEvidence | null;
32
+ /** UI evidence only: does not call or alter the shared failure taxonomy. */
33
+ export declare function normalizeStripeFailureEvidence(input: PaymentFailureEvidence): PaymentRecoveryEvidence;
34
+ /** Preserve the existing webhook projection and append only failure diagnostics. */
35
+ export declare function stripeWebhookPayload(input: {
36
+ eventId: string;
37
+ eventType: string;
38
+ providerPaymentId: string;
39
+ amountMinor: number | null;
40
+ currency: string | null;
41
+ }, object: unknown): Record<string, unknown>;