@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,126 @@
1
+ import { paymentReconciliationFingerprint, paymentTruthFingerprint, } from "./paymentTruth.js";
2
+ const INGEST = `SELECT public.payment_truth_ingest_event(
3
+ $1,$2,$3,$4::uuid,$5,$6,$7,$8,$9,$10,$11::jsonb,$12,$13,$14) AS response`;
4
+ const RECONCILE = `SELECT public.payment_truth_record_reconciliation(
5
+ $1::uuid,$2,$3,$4,$5,$6,$7,$8,$9::jsonb,$10,$11,$12) AS response`;
6
+ const SWEEP = "SELECT public.payment_truth_sweep_open_events($1,$2) AS response";
7
+ const READ = "SELECT public.payment_truth_readback($1::uuid,false) AS response";
8
+ /** Direct-PG caller for the provider-neutral payment truth public forward. */
9
+ export function createPostgresPaymentTruthPort(executor, options) {
10
+ const dunningTemplateSlug = required(options.dunningTemplateSlug, "payment_truth_dunning_template_required");
11
+ const recoveryUrlPath = required(options.recoveryUrlPath, "payment_truth_recovery_path_required");
12
+ const recoveryTemplateSlug = required(options.recoveryTemplateSlug, "payment_truth_recovery_template_required");
13
+ return {
14
+ async ingestEvent(input) {
15
+ const response = first(await executor.query(INGEST, [
16
+ input.sourceEventId,
17
+ paymentTruthFingerprint(input),
18
+ input.idempotencyKey,
19
+ input.settlementIntentId,
20
+ input.outcome,
21
+ input.amountMinor,
22
+ input.currency,
23
+ input.occurredAt,
24
+ input.failure?.classification ?? null,
25
+ input.failure?.reason ?? null,
26
+ JSON.stringify(input.evidence),
27
+ dunningTemplateSlug,
28
+ recoveryUrlPath,
29
+ recoveryTemplateSlug,
30
+ ]));
31
+ return readback(response?.response);
32
+ },
33
+ async recordReconciliation(input) {
34
+ const response = first(await executor.query(RECONCILE, [
35
+ input.eventId,
36
+ input.idempotencyKey,
37
+ paymentReconciliationFingerprint(input),
38
+ input.evidenceStatus,
39
+ input.outcome,
40
+ input.occurredAt,
41
+ input.failure?.classification ?? null,
42
+ input.failure?.reason ?? null,
43
+ JSON.stringify(input.evidence),
44
+ dunningTemplateSlug,
45
+ recoveryUrlPath,
46
+ recoveryTemplateSlug,
47
+ ]));
48
+ return readback(response?.response);
49
+ },
50
+ async sweepOpenEvents(input) {
51
+ const response = record(first(await executor.query(SWEEP, [input.now, input.limit]))?.response);
52
+ return {
53
+ claimed: integer(response.claimed),
54
+ settledIgnored: integer(response.settledIgnored),
55
+ eventIds: Array.isArray(response.eventIds)
56
+ ? response.eventIds.filter((value) => typeof value === "string")
57
+ : [],
58
+ };
59
+ },
60
+ async readEvent(eventId) {
61
+ const value = first(await executor.query(READ, [eventId]))?.response;
62
+ return value === null || value === undefined ? null : readback(value);
63
+ },
64
+ };
65
+ }
66
+ function readback(value) {
67
+ const root = record(value);
68
+ const event = record(root.event);
69
+ const effects = record(root.effects);
70
+ const financial = record(root.financial);
71
+ const outcome = event.outcome;
72
+ const state = event.state;
73
+ if (typeof event.id !== "string"
74
+ || typeof event.sourceEventId !== "string"
75
+ || typeof event.fingerprint !== "string"
76
+ || (state !== "open" && state !== "settled")
77
+ || (outcome !== "captured" && outcome !== "refused" && outcome !== "indeterminate")
78
+ || typeof event.replayed !== "boolean"
79
+ || typeof effects.paymentResultRecorded !== "boolean"
80
+ || typeof effects.dunningOpened !== "boolean"
81
+ || typeof effects.accountingRequested !== "boolean"
82
+ || !Number.isSafeInteger(financial.amountMinor)
83
+ || typeof financial.currency !== "string")
84
+ throw new Error("payment_truth_readback_invalid");
85
+ return {
86
+ event: {
87
+ id: event.id,
88
+ sourceEventId: event.sourceEventId,
89
+ fingerprint: event.fingerprint,
90
+ state,
91
+ outcome,
92
+ replayed: event.replayed,
93
+ },
94
+ effects: {
95
+ paymentResultRecorded: effects.paymentResultRecorded,
96
+ dunningOpened: effects.dunningOpened,
97
+ accountingRequested: effects.accountingRequested,
98
+ },
99
+ financial: {
100
+ settlementStatus: nullableString(financial.settlementStatus),
101
+ orderStatus: nullableString(financial.orderStatus),
102
+ subscriptionStatus: nullableString(financial.subscriptionStatus),
103
+ accountingStatus: nullableString(financial.accountingStatus),
104
+ amountMinor: financial.amountMinor,
105
+ currency: financial.currency,
106
+ },
107
+ };
108
+ }
109
+ function first(result) {
110
+ return result.rows[0] ?? null;
111
+ }
112
+ function record(value) {
113
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
114
+ }
115
+ function integer(value) {
116
+ return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : 0;
117
+ }
118
+ function nullableString(value) {
119
+ return typeof value === "string" ? value : null;
120
+ }
121
+ function required(value, error) {
122
+ const trimmed = value.trim();
123
+ if (!trimmed)
124
+ throw new Error(error);
125
+ return trimmed;
126
+ }
@@ -0,0 +1,6 @@
1
+ import type { PaymentExecutionBaseInput, PaymentExecutionBaseResult } from "@openlup/core/payment";
2
+ import type { PreparedAttemptExecutionInput, PreparedAttemptExecutionResult, PreparedAttemptFailurePolicy } from "./contracts/preparedAttempt.js";
3
+ /** Interactive ordering only; the caller binds nominal trust and native errors. */
4
+ export declare function executePreparedAttemptMechanism<I extends PaymentExecutionBaseInput, R extends PaymentExecutionBaseResult & {
5
+ nextActionKind: N | null;
6
+ }, P extends string, N extends string, E extends Error>(input: PreparedAttemptExecutionInput<I, R, P, N>, policy: PreparedAttemptFailurePolicy<E, N>): Promise<PreparedAttemptExecutionResult<R>>;
@@ -0,0 +1,70 @@
1
+ import { finalizePreparedProviderAttempt } from "./preparedAttemptFinalization.js";
2
+ /** Interactive ordering only; the caller binds nominal trust and native errors. */
3
+ export async function executePreparedAttemptMechanism(input, policy) {
4
+ // Reject deterministic local input/config errors before persisting a durable
5
+ // attempt. Only an actual PSP dispatch may create an indeterminate attempt.
6
+ input.executionPort.validateInput?.(input.executionInput);
7
+ const preparedAttempt = await input.paymentPort.prepareProviderAttempt({
8
+ idempotencyKey: input.prepareIdempotencyKey,
9
+ paymentIntentId: input.paymentIntentId,
10
+ provider: input.provider,
11
+ providerIdempotencyKey: input.providerIdempotencyKey,
12
+ providerRequestFingerprint: input.providerRequestFingerprint,
13
+ providerFlow: input.providerFlow,
14
+ paymentMethodRef: input.paymentMethodRef ?? null,
15
+ requestPayload: input.prepareRequestPayload,
16
+ });
17
+ if (preparedAttempt.replayed) {
18
+ throw policy.replayError(preparedAttempt);
19
+ }
20
+ let execution;
21
+ try {
22
+ execution = await input.executionPort.execute(input.executionInput);
23
+ input.validateExecution?.(execution);
24
+ }
25
+ catch (error) {
26
+ if (policy.isTrustedNoCharge(error)) {
27
+ policy.logRetry(error, preparedAttempt);
28
+ try {
29
+ execution = await input.executionPort.execute(input.executionInput);
30
+ input.validateExecution?.(execution);
31
+ }
32
+ catch (retryError) {
33
+ if (policy.isTrustedNoCharge(retryError)) {
34
+ throw policy.exhaustedError(retryError, error, preparedAttempt);
35
+ }
36
+ throw policy.executionError(retryError, preparedAttempt);
37
+ }
38
+ }
39
+ else {
40
+ throw policy.executionError(error, preparedAttempt);
41
+ }
42
+ }
43
+ let finalizedAttempt;
44
+ try {
45
+ const finalizeInput = {
46
+ idempotencyKey: input.finalizeIdempotencyKey,
47
+ paymentIntentId: input.paymentIntentId,
48
+ paymentAttemptId: preparedAttempt.paymentAttemptId,
49
+ providerIdempotencyKey: input.providerIdempotencyKey,
50
+ providerRequestFingerprint: input.providerRequestFingerprint,
51
+ providerAttemptId: execution.providerAttemptId,
52
+ providerSessionId: execution.providerSessionId,
53
+ attemptStatus: execution.attemptStatus,
54
+ nextActionKind: execution.nextActionKind,
55
+ requestPayload: execution.requestPayload,
56
+ responsePayload: execution.responsePayload,
57
+ };
58
+ finalizedAttempt = await finalizePreparedProviderAttempt(() => input.paymentPort.finalizeProviderAttempt(finalizeInput));
59
+ }
60
+ catch (error) {
61
+ throw policy.finalizationError(error, preparedAttempt);
62
+ }
63
+ return {
64
+ execution,
65
+ attempt: {
66
+ paymentAttemptId: finalizedAttempt.paymentAttemptId,
67
+ status: finalizedAttempt.status,
68
+ },
69
+ };
70
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Replays one local idempotent finalize after its response is lost. The caller
3
+ * closes over one immutable payload, so this cannot issue another PSP request.
4
+ */
5
+ export declare function finalizePreparedProviderAttempt<T>(finalize: () => Promise<T>): Promise<T>;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Replays one local idempotent finalize after its response is lost. The caller
3
+ * closes over one immutable payload, so this cannot issue another PSP request.
4
+ */
5
+ export async function finalizePreparedProviderAttempt(finalize) {
6
+ try {
7
+ return await finalize();
8
+ }
9
+ catch {
10
+ return finalize();
11
+ }
12
+ }
@@ -0,0 +1,10 @@
1
+ /** Direct truth graph only; unsupported operations require a separate store. */
2
+ export declare const paymentSchemaDescriptor: {
3
+ readonly truth: "supported";
4
+ readonly settlement: "supported";
5
+ readonly laterOnlyCycle: "supported";
6
+ readonly preparedDurableStore: "unsupported";
7
+ readonly methodSetup: "unsupported";
8
+ readonly absenceReopen: "unsupported";
9
+ readonly cumulativeRefund: "unsupported";
10
+ };
@@ -0,0 +1,10 @@
1
+ /** Direct truth graph only; unsupported operations require a separate store. */
2
+ export const paymentSchemaDescriptor = {
3
+ truth: "supported",
4
+ settlement: "supported",
5
+ laterOnlyCycle: "supported",
6
+ preparedDurableStore: "unsupported",
7
+ methodSetup: "unsupported",
8
+ absenceReopen: "unsupported",
9
+ cumulativeRefund: "unsupported",
10
+ };
@@ -0,0 +1,20 @@
1
+ /** Sparse projections of canonical units, never an install chain. */
2
+ export declare const PAYMENT_MIGRATIONS: readonly [{
3
+ readonly id: "20260810180000_payment_settlement_rail";
4
+ readonly catalogPath: "db/platform/migrations/20260810180000_payment_settlement_rail.sql";
5
+ readonly position: 10;
6
+ readonly sha256: "af153aadedc09c07b25f69300d335d8928526cad126646c1b9bf25341f1c2d0e";
7
+ readonly url: URL;
8
+ }, {
9
+ readonly id: "20260815150000_payment_truth_reconciliation";
10
+ readonly catalogPath: "db/platform/migrations/20260815150000_payment_truth_reconciliation.sql";
11
+ readonly position: 35;
12
+ readonly sha256: "dd38a601edc2f43f259eefc2cdb9af14fd37fe792a7f82e58c351bd373a88905";
13
+ readonly url: URL;
14
+ }, {
15
+ readonly id: "20260826100000_late_payment_shifts_the_subscription_cycle";
16
+ readonly catalogPath: "db/platform/migrations/20260826100000_late_payment_shifts_the_subscription_cycle.sql";
17
+ readonly position: 56;
18
+ readonly sha256: "992540b0f49713d604859926624b3bef09d5075b68b604f069dcbd688328e452";
19
+ readonly url: URL;
20
+ }];
@@ -0,0 +1,6 @@
1
+ /** Sparse projections of canonical units, never an install chain. */
2
+ export const PAYMENT_MIGRATIONS = [
3
+ { id: "20260810180000_payment_settlement_rail", catalogPath: "db/platform/migrations/20260810180000_payment_settlement_rail.sql", position: 10, sha256: "af153aadedc09c07b25f69300d335d8928526cad126646c1b9bf25341f1c2d0e", url: new URL("../../sql/20260810180000_payment_settlement_rail.sql", import.meta.url) },
4
+ { id: "20260815150000_payment_truth_reconciliation", catalogPath: "db/platform/migrations/20260815150000_payment_truth_reconciliation.sql", position: 35, sha256: "dd38a601edc2f43f259eefc2cdb9af14fd37fe792a7f82e58c351bd373a88905", url: new URL("../../sql/20260815150000_payment_truth_reconciliation.sql", import.meta.url) },
5
+ { id: "20260826100000_late_payment_shifts_the_subscription_cycle", catalogPath: "db/platform/migrations/20260826100000_late_payment_shifts_the_subscription_cycle.sql", position: 56, sha256: "992540b0f49713d604859926624b3bef09d5075b68b604f069dcbd688328e452", url: new URL("../../sql/20260826100000_late_payment_shifts_the_subscription_cycle.sql", import.meta.url) },
6
+ ];
@@ -0,0 +1,46 @@
1
+ import { type PaymentExecutionProviderDecline, type PaymentFailureClassification } from "@openlup/core/payment";
2
+ export declare const DECLINE_FAILURE_REASONS: {
3
+ /** The payer's bank cannot register a reusable mandate — offer a card instead. */
4
+ readonly mandateUnsupported: "blik_recurring_unsupported_bank";
5
+ /** Declined for a reason the provider did not distinguish. */
6
+ readonly generic: "provider_declined";
7
+ };
8
+ /** Maps a provider refusal to the stable code persisted as `failure_reason`. */
9
+ export declare function declineFailureReason(decline: PaymentExecutionProviderDecline): string;
10
+ /**
11
+ * The one place a refused payment gets a class, for every rail that consumes a
12
+ * synchronous decline: interactive checkout, checkout recovery, and the
13
+ * off-session renewal cron.
14
+ *
15
+ * Provider-agnostic on purpose. Every code the adapter understood has already
16
+ * been translated into `adviceCode` and `neutralReasonHints` by the adapter that
17
+ * owns the vocabulary, so nothing here needs to know which provider refused.
18
+ *
19
+ * The one reading this seam adds is the mandate flag. `mandateUnsupported` is a
20
+ * neutral capability statement any adapter may set, and it is what produces the
21
+ * scheme-named failure reason below; asserting the matching hint is what turns
22
+ * that reason into `mandate_dead` instead of leaving it unclassified. Doing it
23
+ * here rather than per adapter means a second adapter setting the same flag gets
24
+ * the same class without repeating the mapping.
25
+ *
26
+ * DISPLAY-LOAD-BEARING, and NOT a cadence input. Three live readers turn the
27
+ * class this seam decides into what the payer is told: `dunningFailureClassPort`
28
+ * reads it off the case, `customerAccountActionRequiredReadModel` selects it into
29
+ * the account surface, and `paymentRecoverySetupHandler` renders it as the cause
30
+ * sentence on the recovery page. What still does NOT read it is the retry ladder:
31
+ * no cadence, retry decision or scheduling branch consults the class, and that
32
+ * wave stays gated on validating this taxonomy against the historical corpus.
33
+ *
34
+ * `failureReasonKey` overrides the reason this seam would derive itself, and is
35
+ * supplied by a rail that persists a DIFFERENT reason string with the attempt:
36
+ * the reconciliation rail writes its own adapter-prefixed key, not
37
+ * `provider_declined`. That key is the classifier's weakest evidence tier, so
38
+ * the override changes a class only where the rail's own key is mapped; what it
39
+ * always changes is `decidedBy`, and that is the point. The historical-corpus
40
+ * replay reproduces a verdict from the stored attempt row, so the key named in
41
+ * the telemetry has to be the key actually on that row rather than one this seam
42
+ * invented for itself.
43
+ */
44
+ export declare function classifyDecline(decline: PaymentExecutionProviderDecline, options?: {
45
+ failureReasonKey?: string;
46
+ }): PaymentFailureClassification;
@@ -0,0 +1,58 @@
1
+ import { classifyPaymentFailure } from "@openlup/core/payment";
2
+ export const DECLINE_FAILURE_REASONS = {
3
+ /** The payer's bank cannot register a reusable mandate — offer a card instead. */
4
+ mandateUnsupported: "blik_recurring_unsupported_bank",
5
+ /** Declined for a reason the provider did not distinguish. */
6
+ generic: "provider_declined",
7
+ };
8
+ /** Maps a provider refusal to the stable code persisted as `failure_reason`. */
9
+ export function declineFailureReason(decline) {
10
+ return decline.mandateUnsupported
11
+ ? DECLINE_FAILURE_REASONS.mandateUnsupported
12
+ : DECLINE_FAILURE_REASONS.generic;
13
+ }
14
+ /**
15
+ * The one place a refused payment gets a class, for every rail that consumes a
16
+ * synchronous decline: interactive checkout, checkout recovery, and the
17
+ * off-session renewal cron.
18
+ *
19
+ * Provider-agnostic on purpose. Every code the adapter understood has already
20
+ * been translated into `adviceCode` and `neutralReasonHints` by the adapter that
21
+ * owns the vocabulary, so nothing here needs to know which provider refused.
22
+ *
23
+ * The one reading this seam adds is the mandate flag. `mandateUnsupported` is a
24
+ * neutral capability statement any adapter may set, and it is what produces the
25
+ * scheme-named failure reason below; asserting the matching hint is what turns
26
+ * that reason into `mandate_dead` instead of leaving it unclassified. Doing it
27
+ * here rather than per adapter means a second adapter setting the same flag gets
28
+ * the same class without repeating the mapping.
29
+ *
30
+ * DISPLAY-LOAD-BEARING, and NOT a cadence input. Three live readers turn the
31
+ * class this seam decides into what the payer is told: `dunningFailureClassPort`
32
+ * reads it off the case, `customerAccountActionRequiredReadModel` selects it into
33
+ * the account surface, and `paymentRecoverySetupHandler` renders it as the cause
34
+ * sentence on the recovery page. What still does NOT read it is the retry ladder:
35
+ * no cadence, retry decision or scheduling branch consults the class, and that
36
+ * wave stays gated on validating this taxonomy against the historical corpus.
37
+ *
38
+ * `failureReasonKey` overrides the reason this seam would derive itself, and is
39
+ * supplied by a rail that persists a DIFFERENT reason string with the attempt:
40
+ * the reconciliation rail writes its own adapter-prefixed key, not
41
+ * `provider_declined`. That key is the classifier's weakest evidence tier, so
42
+ * the override changes a class only where the rail's own key is mapped; what it
43
+ * always changes is `decidedBy`, and that is the point. The historical-corpus
44
+ * replay reproduces a verdict from the stored attempt row, so the key named in
45
+ * the telemetry has to be the key actually on that row rather than one this seam
46
+ * invented for itself.
47
+ */
48
+ export function classifyDecline(decline, options = {}) {
49
+ const asserted = [...(decline.neutralReasonHints ?? [])];
50
+ if (decline.mandateUnsupported)
51
+ asserted.push("mandateUnsupported");
52
+ return classifyPaymentFailure({
53
+ adviceCode: decline.adviceCode,
54
+ declineCode: decline.declineCode,
55
+ neutralReasonHints: asserted,
56
+ failureReasonKey: options.failureReasonKey ?? declineFailureReason(decline),
57
+ });
58
+ }
@@ -0,0 +1,46 @@
1
+ import type { PaymentFailureHint } from "@openlup/core/payment";
2
+ /**
3
+ * This adapter's own translation from its opaque refusal codes to the neutral
4
+ * hints the failure taxonomy accepts.
5
+ *
6
+ * The table lives here, in the adapter that owns the codes, and never in the
7
+ * neutral kernel: the kernel classifies evidence, and this file is what turns
8
+ * one acquirer's vocabulary into that evidence.
9
+ *
10
+ * It follows the published scheme research collected for the dunning programme.
11
+ * The class these hints produce is DISPLAY-load-bearing: it reaches the payer as
12
+ * the sentence explaining why the charge could not happen. It is still not a
13
+ * cadence input (nothing in the retry ladder branches on it), so a wrong row
14
+ * here costs a wrong sentence, not a wrong charge.
15
+ *
16
+ * Precedence note: this table is consulted only when the refusal carries NO
17
+ * issuer advice code. Advice is the verdict of the party that would refuse the
18
+ * next attempt, so it outranks anything read locally.
19
+ *
20
+ * Two deliberate silences, both from the research:
21
+ * - `do_not_honor` and `generic_decline` get NO entry. They are the issuer's
22
+ * way of declining without saying why, and they arrive on refusals that are
23
+ * dead and on refusals that clear on the next attempt alike. Guessing either
24
+ * way would invent a verdict; falling through to `indeterminate` is honest.
25
+ * - `restricted_card` IS mapped, to `credentialDead`, and the caveat is
26
+ * recorded rather than hidden: the research reads it as "this instrument may
27
+ * not be used for this transaction", which the payer's bank can lift, so the
28
+ * instrument is not necessarily finished. It is mapped anyway because every
29
+ * reading of it agrees that repeating the identical charge unattended cannot
30
+ * clear it, and that is the whole content of the class.
31
+ *
32
+ * The same caveat applies to `card_not_supported`, `currency_not_supported` and
33
+ * `transaction_not_allowed`: the credential may be perfectly alive and the
34
+ * refusal still final for the charge as submitted. `credentialDead` is chosen
35
+ * for the CLASS it yields, not as a claim about the plastic.
36
+ */
37
+ export declare const DECLINE_CODE_HINT_TABLE: Readonly<Record<string, readonly PaymentFailureHint[]>>;
38
+ /**
39
+ * The hints this adapter asserts for one refusal code. Own-property lookup, so
40
+ * an inherited key (`constructor`, `toString`) is not a mapping.
41
+ *
42
+ * The table itself is exported too, for a reader that holds a stored code and
43
+ * has no adapter in the loop: the historical-corpus replay hands it straight to
44
+ * the kernel's `declineCodeHints` option.
45
+ */
46
+ export declare function declineCodeHints(declineCode: string | undefined): readonly PaymentFailureHint[];
@@ -0,0 +1,87 @@
1
+ /**
2
+ * This adapter's own translation from its opaque refusal codes to the neutral
3
+ * hints the failure taxonomy accepts.
4
+ *
5
+ * The table lives here, in the adapter that owns the codes, and never in the
6
+ * neutral kernel: the kernel classifies evidence, and this file is what turns
7
+ * one acquirer's vocabulary into that evidence.
8
+ *
9
+ * It follows the published scheme research collected for the dunning programme.
10
+ * The class these hints produce is DISPLAY-load-bearing: it reaches the payer as
11
+ * the sentence explaining why the charge could not happen. It is still not a
12
+ * cadence input (nothing in the retry ladder branches on it), so a wrong row
13
+ * here costs a wrong sentence, not a wrong charge.
14
+ *
15
+ * Precedence note: this table is consulted only when the refusal carries NO
16
+ * issuer advice code. Advice is the verdict of the party that would refuse the
17
+ * next attempt, so it outranks anything read locally.
18
+ *
19
+ * Two deliberate silences, both from the research:
20
+ * - `do_not_honor` and `generic_decline` get NO entry. They are the issuer's
21
+ * way of declining without saying why, and they arrive on refusals that are
22
+ * dead and on refusals that clear on the next attempt alike. Guessing either
23
+ * way would invent a verdict; falling through to `indeterminate` is honest.
24
+ * - `restricted_card` IS mapped, to `credentialDead`, and the caveat is
25
+ * recorded rather than hidden: the research reads it as "this instrument may
26
+ * not be used for this transaction", which the payer's bank can lift, so the
27
+ * instrument is not necessarily finished. It is mapped anyway because every
28
+ * reading of it agrees that repeating the identical charge unattended cannot
29
+ * clear it, and that is the whole content of the class.
30
+ *
31
+ * The same caveat applies to `card_not_supported`, `currency_not_supported` and
32
+ * `transaction_not_allowed`: the credential may be perfectly alive and the
33
+ * refusal still final for the charge as submitted. `credentialDead` is chosen
34
+ * for the CLASS it yields, not as a claim about the plastic.
35
+ */
36
+ export const DECLINE_CODE_HINT_TABLE = Object.freeze({
37
+ approve_with_id: Object.freeze(["transient"]),
38
+ authentication_required: Object.freeze(["authenticationRequired"]),
39
+ card_not_supported: Object.freeze(["credentialDead"]),
40
+ card_velocity_exceeded: Object.freeze(["limitExceeded"]),
41
+ currency_not_supported: Object.freeze(["credentialDead"]),
42
+ expired_card: Object.freeze(["credentialDead"]),
43
+ fraudulent: Object.freeze(["credentialDead"]),
44
+ highest_risk_level: Object.freeze(["credentialDead"]),
45
+ incorrect_address: Object.freeze(["dataInvalid"]),
46
+ incorrect_cvc: Object.freeze(["dataInvalid"]),
47
+ incorrect_number: Object.freeze(["dataInvalid"]),
48
+ incorrect_zip: Object.freeze(["dataInvalid"]),
49
+ insufficient_funds: Object.freeze(["transient"]),
50
+ invalid_account: Object.freeze(["credentialDead"]),
51
+ invalid_amount: Object.freeze(["dataInvalid"]),
52
+ invalid_cvc: Object.freeze(["dataInvalid"]),
53
+ invalid_expiry_month: Object.freeze(["dataInvalid"]),
54
+ invalid_expiry_year: Object.freeze(["dataInvalid"]),
55
+ invalid_number: Object.freeze(["dataInvalid"]),
56
+ issuer_not_available: Object.freeze(["transient"]),
57
+ lost_card: Object.freeze(["credentialDead"]),
58
+ merchant_blacklist: Object.freeze(["credentialDead"]),
59
+ new_account_information_available: Object.freeze(["credentialDead"]),
60
+ pickup_card: Object.freeze(["credentialDead"]),
61
+ pin_try_exceeded: Object.freeze(["credentialDead"]),
62
+ processing_error: Object.freeze(["transient"]),
63
+ reenter_transaction: Object.freeze(["transient"]),
64
+ restricted_card: Object.freeze(["credentialDead"]),
65
+ revocation_of_all_authorizations: Object.freeze(["credentialDead"]),
66
+ revocation_of_authorization: Object.freeze(["credentialDead"]),
67
+ stolen_card: Object.freeze(["credentialDead"]),
68
+ stop_payment_order: Object.freeze(["credentialDead"]),
69
+ transaction_not_allowed: Object.freeze(["credentialDead"]),
70
+ try_again_later: Object.freeze(["transient"]),
71
+ withdrawal_count_limit_exceeded: Object.freeze(["limitExceeded"]),
72
+ });
73
+ /**
74
+ * The hints this adapter asserts for one refusal code. Own-property lookup, so
75
+ * an inherited key (`constructor`, `toString`) is not a mapping.
76
+ *
77
+ * The table itself is exported too, for a reader that holds a stored code and
78
+ * has no adapter in the loop: the historical-corpus replay hands it straight to
79
+ * the kernel's `declineCodeHints` option.
80
+ */
81
+ export function declineCodeHints(declineCode) {
82
+ if (declineCode === undefined)
83
+ return [];
84
+ return Object.prototype.hasOwnProperty.call(DECLINE_CODE_HINT_TABLE, declineCode)
85
+ ? DECLINE_CODE_HINT_TABLE[declineCode]
86
+ : [];
87
+ }
@@ -0,0 +1,120 @@
1
+ /** Private structural contracts for the unchanged execution and observation slices. */
2
+ import type { PaymentExecutionBaseInput, PaymentExecutionBaseResult } from "@openlup/core/payment";
3
+ import type { ProviderReadPurpose } from "../contracts/index.js";
4
+ export type PaymentProviderFlow = "one_time_payment" | "setup_reusable_method" | "off_session_payment";
5
+ export declare const PAYMENT_NEXT_ACTION_KINDS: readonly ["redirect", "3ds_challenge", "sca_required"];
6
+ export type PaymentNextActionKind = (typeof PAYMENT_NEXT_ACTION_KINDS)[number];
7
+ export interface PaymentExecutionInput extends PaymentExecutionBaseInput {
8
+ orderRef: string;
9
+ providerFlow?: PaymentProviderFlow;
10
+ customerRef?: string;
11
+ paymentMethodRef?: string;
12
+ saveForFutureUse?: boolean;
13
+ clientId?: string;
14
+ }
15
+ export interface PaymentExecutionResult extends PaymentExecutionBaseResult {
16
+ nextActionKind: PaymentNextActionKind | null;
17
+ clientSecret?: string | null;
18
+ customerProviderRef?: string | null;
19
+ paymentMethodRef?: string | null;
20
+ webhookExpected?: boolean;
21
+ recoveryRequired?: boolean;
22
+ }
23
+ export interface PaymentExecutionPort {
24
+ execute(input: PaymentExecutionInput): Promise<PaymentExecutionResult>;
25
+ }
26
+ export type PaymentWebhookEventType = "payment.succeeded" | "payment.failed" | "payment.refunded" | "payment.requires_action" | "payment.disputed" | "setup.succeeded" | "setup.failed" | "setup.requires_action";
27
+ export interface CanonicalPaymentEvent {
28
+ provider_event_id: string;
29
+ event_type: PaymentWebhookEventType;
30
+ payment_provider_id: string;
31
+ amount_minor?: number;
32
+ next_action_kind?: PaymentNextActionKind;
33
+ raw_payload: Record<string, unknown>;
34
+ }
35
+ export interface NormalizedProviderPaymentWebhook {
36
+ provider: string;
37
+ providerEventId: string;
38
+ eventType: PaymentWebhookEventType;
39
+ providerPaymentId: string;
40
+ paymentIntentId?: string | null;
41
+ paymentAttemptId?: string | null;
42
+ amountMinor?: number | null;
43
+ currency?: string | null;
44
+ occurredAt: string;
45
+ refundKind?: "full" | "partial" | null;
46
+ rawPayload: Record<string, unknown>;
47
+ reusableMethod?: {
48
+ clientId: string;
49
+ subscriptionId?: string | null;
50
+ providerCustomerRef?: string | null;
51
+ providerMethodRef: string;
52
+ providerMandateRef?: string | null;
53
+ methodKind: "card";
54
+ status: "active";
55
+ consentSnapshot?: Record<string, unknown>;
56
+ } | null;
57
+ }
58
+ export interface ProviderReconciliationStatus {
59
+ status: "succeeded" | "failed" | "pending" | "unknown";
60
+ providerStatus: string;
61
+ occurredAt: string | null;
62
+ failureReason: string | null;
63
+ amountMinor: number | null;
64
+ currency: string | null;
65
+ rawPayload: Record<string, unknown>;
66
+ }
67
+ export interface ClaimedPaymentAttempt {
68
+ paymentAttemptId: string;
69
+ paymentIntentId: string;
70
+ paymentId: string;
71
+ orderId: string;
72
+ subscriptionId: string | null;
73
+ subscriptionCycleId: string | null;
74
+ provider: string;
75
+ providerPaymentId: string | null;
76
+ providerAttemptId: string | null;
77
+ providerSessionId: string | null;
78
+ attemptStatus: string;
79
+ intentStatus: string;
80
+ amountMinor: number;
81
+ currency: string;
82
+ orderMode: "one_time" | "subscription_cycle";
83
+ cycleRetryAttempt: number;
84
+ cycleNextRetryAt: string | null;
85
+ localUpdatedAt: string;
86
+ }
87
+ export type ProviderRecoveryAction = {
88
+ kind: "provider_embedded";
89
+ provider: string;
90
+ clientSecret: string;
91
+ };
92
+ export interface PaymentProviderRecoveryProvider {
93
+ resolvePaymentReference(input: {
94
+ providerAttemptId: string | null;
95
+ intentProviderPaymentId: string | null;
96
+ providerSessionId: string | null;
97
+ }): string | null;
98
+ readPayment(input: {
99
+ providerPaymentId: string;
100
+ attempt?: ClaimedPaymentAttempt;
101
+ purpose?: ProviderReadPurpose;
102
+ }): Promise<ProviderReconciliationStatus>;
103
+ readRecoveryPayment(input: {
104
+ providerPaymentId: string;
105
+ attempt: ClaimedPaymentAttempt;
106
+ purpose?: ProviderReadPurpose;
107
+ }): Promise<{
108
+ status: ProviderReconciliationStatus;
109
+ identityMatches: boolean;
110
+ configuredMoneyMatches: boolean;
111
+ manualReviewRequired: boolean;
112
+ clientAction: ProviderRecoveryAction | null;
113
+ }>;
114
+ closePayment?(input: {
115
+ providerPaymentId: string;
116
+ }): Promise<ProviderReconciliationStatus>;
117
+ findPaymentByLocalIntent?(input: {
118
+ attempt: ClaimedPaymentAttempt;
119
+ }): Promise<"found" | "absent">;
120
+ }
@@ -0,0 +1 @@
1
+ export const PAYMENT_NEXT_ACTION_KINDS = ["redirect", "3ds_challenge", "sca_required"];