@openlup/platform 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +272 -278
- package/README.md +195 -6
- package/dist/composition/composeBundle.d.ts +13 -0
- package/dist/composition/composeBundle.js +4 -0
- package/dist/composition/fulfillmentContribution.d.ts +21 -0
- package/dist/composition/fulfillmentContribution.js +19 -0
- package/dist/composition/index.d.ts +1 -0
- package/dist/composition/index.js +2 -0
- package/dist/fulfillment/adapterConformance.d.ts +3 -0
- package/dist/fulfillment/adapterConformance.js +223 -0
- package/dist/fulfillment/bindings.d.ts +3 -0
- package/dist/fulfillment/bindings.js +29 -0
- package/dist/fulfillment/commerceFulfillmentHandlers.d.ts +29 -0
- package/dist/fulfillment/commerceFulfillmentHandlers.js +184 -0
- package/dist/fulfillment/commerceFulfillmentReadModel.d.ts +86 -0
- package/dist/fulfillment/commerceFulfillmentReadModel.js +131 -0
- package/dist/fulfillment/contracts/alignment.d.ts +25 -0
- package/dist/fulfillment/contracts/alignment.js +1 -0
- package/dist/fulfillment/contracts/bindings.d.ts +51 -0
- package/dist/fulfillment/contracts/bindings.js +1 -0
- package/dist/fulfillment/contracts/capabilities.d.ts +29 -0
- package/dist/fulfillment/contracts/capabilities.js +1 -0
- package/dist/fulfillment/contracts/conformance.d.ts +93 -0
- package/dist/fulfillment/contracts/conformance.js +1 -0
- package/dist/fulfillment/contracts/customerAlignment.d.ts +6 -0
- package/dist/fulfillment/contracts/customerAlignment.js +1 -0
- package/dist/fulfillment/contracts/dispatch.d.ts +105 -0
- package/dist/fulfillment/contracts/dispatch.js +1 -0
- package/dist/fulfillment/contracts/index.d.ts +18 -0
- package/dist/fulfillment/contracts/index.js +1 -0
- package/dist/fulfillment/contracts/label.d.ts +35 -0
- package/dist/fulfillment/contracts/label.js +1 -0
- package/dist/fulfillment/contracts/nativeUnsupported.d.ts +6 -0
- package/dist/fulfillment/contracts/nativeUnsupported.js +1 -0
- package/dist/fulfillment/contracts/observation.d.ts +52 -0
- package/dist/fulfillment/contracts/observation.js +1 -0
- package/dist/fulfillment/contracts/operator.d.ts +64 -0
- package/dist/fulfillment/contracts/operator.js +1 -0
- package/dist/fulfillment/contracts/read.d.ts +110 -0
- package/dist/fulfillment/contracts/read.js +1 -0
- package/dist/fulfillment/contracts/readback.d.ts +38 -0
- package/dist/fulfillment/contracts/readback.js +1 -0
- package/dist/fulfillment/contracts/replacement.d.ts +34 -0
- package/dist/fulfillment/contracts/replacement.js +1 -0
- package/dist/fulfillment/contracts/store.d.ts +86 -0
- package/dist/fulfillment/contracts/store.js +1 -0
- package/dist/fulfillment/contracts/sync.d.ts +170 -0
- package/dist/fulfillment/contracts/sync.js +1 -0
- package/dist/fulfillment/currentFulfillmentParcel.d.ts +2 -0
- package/dist/fulfillment/currentFulfillmentParcel.js +1 -0
- package/dist/fulfillment/delayReadModel.d.ts +11 -0
- package/dist/fulfillment/delayReadModel.js +45 -0
- package/dist/fulfillment/deliveryAlignmentReadModel.d.ts +4 -0
- package/dist/fulfillment/deliveryAlignmentReadModel.js +12 -0
- package/dist/fulfillment/dispatchProjection.d.ts +7 -0
- package/dist/fulfillment/dispatchProjection.js +18 -0
- package/dist/fulfillment/fulfillmentObservationEffects.d.ts +66 -0
- package/dist/fulfillment/fulfillmentObservationEffects.js +36 -0
- package/dist/fulfillment/handlerSchemas.d.ts +647 -0
- package/dist/fulfillment/handlerSchemas.js +213 -0
- package/dist/fulfillment/handlerSupport.d.ts +33 -0
- package/dist/fulfillment/handlerSupport.js +25 -0
- package/dist/fulfillment/index.d.ts +37 -0
- package/dist/fulfillment/index.js +37 -0
- package/dist/fulfillment/labelLifecycle.d.ts +24 -0
- package/dist/fulfillment/labelLifecycle.js +9 -0
- package/dist/fulfillment/lowStockEvidenceHandler.d.ts +36 -0
- package/dist/fulfillment/lowStockEvidenceHandler.js +46 -0
- package/dist/fulfillment/migrations.d.ts +27 -0
- package/dist/fulfillment/migrations.js +8 -0
- package/dist/fulfillment/nativeUnsupported.d.ts +10 -0
- package/dist/fulfillment/nativeUnsupported.js +18 -0
- package/dist/fulfillment/normalizedFulfillmentFacts.d.ts +2 -0
- package/dist/fulfillment/normalizedFulfillmentFacts.js +1 -0
- package/dist/fulfillment/observationProjection.d.ts +11 -0
- package/dist/fulfillment/observationProjection.js +37 -0
- package/dist/fulfillment/omnipack/capabilities.d.ts +3 -0
- package/dist/fulfillment/omnipack/capabilities.js +16 -0
- package/dist/fulfillment/omnipack/client.d.ts +37 -0
- package/dist/fulfillment/omnipack/client.js +162 -0
- package/dist/fulfillment/omnipack/index.d.ts +52 -0
- package/dist/fulfillment/omnipack/index.js +349 -0
- package/dist/fulfillment/omnipack/mappers.d.ts +73 -0
- package/dist/fulfillment/omnipack/mappers.js +177 -0
- package/dist/fulfillment/omnipack/observation.d.ts +21 -0
- package/dist/fulfillment/omnipack/observation.js +57 -0
- package/dist/fulfillment/omnipack/omnipackDispatchPayload.d.ts +36 -0
- package/dist/fulfillment/omnipack/omnipackDispatchPayload.js +63 -0
- package/dist/fulfillment/omnipack/omnipackOutboundOrderPayload.d.ts +83 -0
- package/dist/fulfillment/omnipack/omnipackOutboundOrderPayload.js +113 -0
- package/dist/fulfillment/omnipack/omnipackProductPackPayload.d.ts +11 -0
- package/dist/fulfillment/omnipack/omnipackProductPackPayload.js +38 -0
- package/dist/fulfillment/omnipack/omnipackProductPayload.d.ts +18 -0
- package/dist/fulfillment/omnipack/omnipackProductPayload.js +51 -0
- package/dist/fulfillment/omnipack/omnipackReconciliationPayload.d.ts +1 -0
- package/dist/fulfillment/omnipack/omnipackReconciliationPayload.js +3 -0
- package/dist/fulfillment/omnipack/omnipackStatusVocabulary.d.ts +4 -0
- package/dist/fulfillment/omnipack/omnipackStatusVocabulary.js +87 -0
- package/dist/fulfillment/omnipack/omnipackWebhookPayload.d.ts +9 -0
- package/dist/fulfillment/omnipack/omnipackWebhookPayload.js +76 -0
- package/dist/fulfillment/omnipack/productSyncMappers.d.ts +18 -0
- package/dist/fulfillment/omnipack/productSyncMappers.js +43 -0
- package/dist/fulfillment/omnipack/providerError.d.ts +25 -0
- package/dist/fulfillment/omnipack/providerError.js +47 -0
- package/dist/fulfillment/omnipack/trackingReferences.d.ts +16 -0
- package/dist/fulfillment/omnipack/trackingReferences.js +52 -0
- package/dist/fulfillment/paidFulfillmentRecovery.d.ts +67 -0
- package/dist/fulfillment/paidFulfillmentRecovery.js +154 -0
- package/dist/fulfillment/paidHandoff.d.ts +27 -0
- package/dist/fulfillment/paidHandoff.js +86 -0
- package/dist/fulfillment/paidHandoffPorts.d.ts +24 -0
- package/dist/fulfillment/paidHandoffPorts.js +3 -0
- package/dist/fulfillment/postgresDeliveryAlignment.d.ts +4 -0
- package/dist/fulfillment/postgresDeliveryAlignment.js +12 -0
- package/dist/fulfillment/postgresDispatchStore.d.ts +16 -0
- package/dist/fulfillment/postgresDispatchStore.js +44 -0
- package/dist/fulfillment/postgresEvidenceReadPort.d.ts +25 -0
- package/dist/fulfillment/postgresEvidenceReadPort.js +45 -0
- package/dist/fulfillment/postgresPaidFulfillmentRecovery.d.ts +25 -0
- package/dist/fulfillment/postgresPaidFulfillmentRecovery.js +26 -0
- package/dist/fulfillment/postgresPaidHandoffStore.d.ts +4 -0
- package/dist/fulfillment/postgresPaidHandoffStore.js +10 -0
- package/dist/fulfillment/postgresShipmentSpine.d.ts +63 -0
- package/dist/fulfillment/postgresShipmentSpine.js +232 -0
- package/dist/fulfillment/postgresStockSyncPort.d.ts +8 -0
- package/dist/fulfillment/postgresStockSyncPort.js +63 -0
- package/dist/fulfillment/productReconciliationEvidenceHandler.d.ts +36 -0
- package/dist/fulfillment/productReconciliationEvidenceHandler.js +50 -0
- package/dist/fulfillment/providerCapabilities.d.ts +9 -0
- package/dist/fulfillment/providerCapabilities.js +17 -0
- package/dist/fulfillment/providerCommandLedger.d.ts +143 -0
- package/dist/fulfillment/providerCommandLedger.js +14 -0
- package/dist/fulfillment/providerDispatchAcceptance.d.ts +26 -0
- package/dist/fulfillment/providerDispatchAcceptance.js +23 -0
- package/dist/fulfillment/providerDispatchError.d.ts +6 -0
- package/dist/fulfillment/providerDispatchError.js +5 -0
- package/dist/fulfillment/providerDispatchWorker.d.ts +31 -0
- package/dist/fulfillment/providerDispatchWorker.js +172 -0
- package/dist/fulfillment/providerProductSyncWorker.d.ts +28 -0
- package/dist/fulfillment/providerProductSyncWorker.js +122 -0
- package/dist/fulfillment/providerReconciliationError.d.ts +9 -0
- package/dist/fulfillment/providerReconciliationError.js +17 -0
- package/dist/fulfillment/providerReconciliationTerminalSkip.d.ts +2 -0
- package/dist/fulfillment/providerReconciliationTerminalSkip.js +6 -0
- package/dist/fulfillment/providerReconciliationTimestamp.d.ts +1 -0
- package/dist/fulfillment/providerReconciliationTimestamp.js +27 -0
- package/dist/fulfillment/providerReconciliationWorker.d.ts +82 -0
- package/dist/fulfillment/providerReconciliationWorker.js +151 -0
- package/dist/fulfillment/providerStockAvailability.d.ts +13 -0
- package/dist/fulfillment/providerStockAvailability.js +51 -0
- package/dist/fulfillment/providerStockSyncWorker.d.ts +37 -0
- package/dist/fulfillment/providerStockSyncWorker.js +186 -0
- package/dist/fulfillment/providerTrackingReferences.d.ts +15 -0
- package/dist/fulfillment/providerTrackingReferences.js +23 -0
- package/dist/fulfillment/replacementRefusals.d.ts +5 -0
- package/dist/fulfillment/replacementRefusals.js +38 -0
- package/dist/fulfillment/replacementShipmentHandler.d.ts +5 -0
- package/dist/fulfillment/replacementShipmentHandler.js +46 -0
- package/dist/fulfillment/schemaCapabilities.d.ts +13 -0
- package/dist/fulfillment/schemaCapabilities.js +7 -0
- package/dist/fulfillment/shipmentsOverviewHandler.d.ts +16 -0
- package/dist/fulfillment/shipmentsOverviewHandler.js +54 -0
- package/dist/fulfillment/statusMap.d.ts +2 -0
- package/dist/fulfillment/statusMap.js +1 -0
- package/dist/fulfillment/trackingProgress.d.ts +1 -0
- package/dist/fulfillment/trackingProgress.js +1 -0
- package/dist/fulfillment/verifiedIngress.d.ts +52 -0
- package/dist/fulfillment/verifiedIngress.js +66 -0
- package/dist/payments/adapterConformance.d.ts +8 -0
- package/dist/payments/adapterConformance.js +109 -0
- package/dist/payments/bindings.d.ts +3 -0
- package/dist/payments/bindings.js +17 -0
- package/dist/payments/checkoutActivePaymentActionResolver.d.ts +7 -0
- package/dist/payments/checkoutActivePaymentActionResolver.js +77 -0
- package/dist/payments/checkoutRecoveryPaymentResolver.d.ts +8 -0
- package/dist/payments/checkoutRecoveryPaymentResolver.js +87 -0
- package/dist/payments/contracts/adapter.d.ts +113 -0
- package/dist/payments/contracts/adapter.js +1 -0
- package/dist/payments/contracts/conformance.d.ts +155 -0
- package/dist/payments/contracts/conformance.js +1 -0
- package/dist/payments/contracts/evidence.d.ts +157 -0
- package/dist/payments/contracts/evidence.js +1 -0
- package/dist/payments/contracts/index.d.ts +10 -0
- package/dist/payments/contracts/index.js +1 -0
- package/dist/payments/contracts/preparedAttempt.d.ts +69 -0
- package/dist/payments/contracts/preparedAttempt.js +1 -0
- package/dist/payments/contracts/reconciliation.d.ts +303 -0
- package/dist/payments/contracts/reconciliation.js +1 -0
- package/dist/payments/contracts/repair.d.ts +92 -0
- package/dist/payments/contracts/repair.js +1 -0
- package/dist/payments/contracts/truth.d.ts +92 -0
- package/dist/payments/contracts/truth.js +1 -0
- package/dist/payments/eventConformanceRunner.d.ts +12 -0
- package/dist/payments/eventConformanceRunner.js +125 -0
- package/dist/payments/index.d.ts +16 -0
- package/dist/payments/index.js +16 -0
- package/dist/payments/paymentAttemptSilenceWindow.d.ts +10 -0
- package/dist/payments/paymentAttemptSilenceWindow.js +19 -0
- package/dist/payments/paymentMethodLifecycle.d.ts +11 -0
- package/dist/payments/paymentMethodLifecycle.js +37 -0
- package/dist/payments/paymentProviderReconciliationAbsenceReopen.d.ts +7 -0
- package/dist/payments/paymentProviderReconciliationAbsenceReopen.js +100 -0
- package/dist/payments/paymentProviderReconciliationEvidence.d.ts +36 -0
- package/dist/payments/paymentProviderReconciliationEvidence.js +91 -0
- package/dist/payments/paymentProviderReconciliationWorker.d.ts +4 -0
- package/dist/payments/paymentProviderReconciliationWorker.js +245 -0
- package/dist/payments/paymentTruth.d.ts +8 -0
- package/dist/payments/paymentTruth.js +125 -0
- package/dist/payments/paymentVerifyNowHandler.d.ts +27 -0
- package/dist/payments/paymentVerifyNowHandler.js +70 -0
- package/dist/payments/paymentVerifyNowService.d.ts +23 -0
- package/dist/payments/paymentVerifyNowService.js +93 -0
- package/dist/payments/paymentWebhookPostProcessing.d.ts +22 -0
- package/dist/payments/paymentWebhookPostProcessing.js +15 -0
- package/dist/payments/postgresPaymentTruth.d.ts +10 -0
- package/dist/payments/postgresPaymentTruth.js +126 -0
- package/dist/payments/preparedAttempt.d.ts +6 -0
- package/dist/payments/preparedAttempt.js +70 -0
- package/dist/payments/preparedAttemptFinalization.d.ts +5 -0
- package/dist/payments/preparedAttemptFinalization.js +12 -0
- package/dist/payments/schemaCapabilities.d.ts +10 -0
- package/dist/payments/schemaCapabilities.js +10 -0
- package/dist/payments/sql.d.ts +20 -0
- package/dist/payments/sql.js +6 -0
- package/dist/payments/stripe/classifyDecline.d.ts +46 -0
- package/dist/payments/stripe/classifyDecline.js +58 -0
- package/dist/payments/stripe/declineFailureHints.d.ts +46 -0
- package/dist/payments/stripe/declineFailureHints.js +87 -0
- package/dist/payments/stripe/donorContracts.d.ts +120 -0
- package/dist/payments/stripe/donorContracts.js +1 -0
- package/dist/payments/stripe/executionDecline.d.ts +53 -0
- package/dist/payments/stripe/executionDecline.js +107 -0
- package/dist/payments/stripe/index.d.ts +41 -0
- package/dist/payments/stripe/index.js +264 -0
- package/dist/payments/stripe/intentMapper.d.ts +26 -0
- package/dist/payments/stripe/intentMapper.js +51 -0
- package/dist/payments/stripe/methodFacts.d.ts +10 -0
- package/dist/payments/stripe/methodFacts.js +12 -0
- package/dist/payments/stripe/paymentFailureEvidence.d.ts +26 -0
- package/dist/payments/stripe/paymentFailureEvidence.js +33 -0
- package/dist/payments/stripe/stripeFailureEvidence.d.ts +41 -0
- package/dist/payments/stripe/stripeFailureEvidence.js +82 -0
- package/dist/payments/stripe/stripeIntentConfirmationEvidence.d.ts +28 -0
- package/dist/payments/stripe/stripeIntentConfirmationEvidence.js +32 -0
- package/dist/payments/stripe/stripePaymentReconciliationProvider.d.ts +35 -0
- package/dist/payments/stripe/stripePaymentReconciliationProvider.js +222 -0
- package/dist/payments/stripe/stripeRefundEvidence.d.ts +8 -0
- package/dist/payments/stripe/stripeRefundEvidence.js +17 -0
- package/dist/payments/stripe/stripeSandboxPaymentExecutionAdapter.d.ts +45 -0
- package/dist/payments/stripe/stripeSandboxPaymentExecutionAdapter.js +220 -0
- package/dist/payments/stripe/stripeWebhookNormalizer.d.ts +14 -0
- package/dist/payments/stripe/stripeWebhookNormalizer.js +243 -0
- package/dist/payments/stripe/webhookSignature.d.ts +14 -0
- package/dist/payments/stripe/webhookSignature.js +43 -0
- package/dist/payments/tpay/blikRecurringMapper.d.ts +37 -0
- package/dist/payments/tpay/blikRecurringMapper.js +24 -0
- package/dist/payments/tpay/capabilities.d.ts +3 -0
- package/dist/payments/tpay/capabilities.js +18 -0
- package/dist/payments/tpay/classifyDecline.d.ts +46 -0
- package/dist/payments/tpay/classifyDecline.js +58 -0
- package/dist/payments/tpay/declineFailureHints.d.ts +72 -0
- package/dist/payments/tpay/declineFailureHints.js +37 -0
- package/dist/payments/tpay/donorContracts.d.ts +140 -0
- package/dist/payments/tpay/donorContracts.js +1 -0
- package/dist/payments/tpay/index.d.ts +73 -0
- package/dist/payments/tpay/index.js +294 -0
- package/dist/payments/tpay/paymentFailureEvidence.d.ts +26 -0
- package/dist/payments/tpay/paymentFailureEvidence.js +33 -0
- package/dist/payments/tpay/preparedProviderAttempt.d.ts +22 -0
- package/dist/payments/tpay/preparedProviderAttempt.js +24 -0
- package/dist/payments/tpay/providerAttemptFailureDiagnostic.d.ts +3 -0
- package/dist/payments/tpay/providerAttemptFailureDiagnostic.js +95 -0
- package/dist/payments/tpay/tpayCreateTransactionInput.d.ts +25 -0
- package/dist/payments/tpay/tpayCreateTransactionInput.js +128 -0
- package/dist/payments/tpay/tpayDeclineEvidence.d.ts +126 -0
- package/dist/payments/tpay/tpayDeclineEvidence.js +177 -0
- package/dist/payments/tpay/tpayDispatchFailure.d.ts +60 -0
- package/dist/payments/tpay/tpayDispatchFailure.js +121 -0
- package/dist/payments/tpay/tpayFailureEvidence.d.ts +20 -0
- package/dist/payments/tpay/tpayFailureEvidence.js +61 -0
- package/dist/payments/tpay/tpayHttpClient.d.ts +52 -0
- package/dist/payments/tpay/tpayHttpClient.js +185 -0
- package/dist/payments/tpay/tpayHttpFailure.d.ts +57 -0
- package/dist/payments/tpay/tpayHttpFailure.js +143 -0
- package/dist/payments/tpay/tpayPaymentExecutionAdapter.d.ts +20 -0
- package/dist/payments/tpay/tpayPaymentExecutionAdapter.js +249 -0
- package/dist/payments/tpay/tpayPaymentReconciliationProvider.d.ts +5 -0
- package/dist/payments/tpay/tpayPaymentReconciliationProvider.js +244 -0
- package/dist/payments/tpay/tpayTransactionPayload.d.ts +120 -0
- package/dist/payments/tpay/tpayTransactionPayload.js +96 -0
- package/dist/payments/tpay/wallClockTime.d.ts +31 -0
- package/dist/payments/tpay/wallClockTime.js +57 -0
- package/dist/payments/tpay/webhookJwsVerifier.d.ts +17 -0
- package/dist/payments/tpay/webhookJwsVerifier.js +51 -0
- package/dist/payments/tpay/webhookNormalizer.d.ts +54 -0
- package/dist/payments/tpay/webhookNormalizer.js +200 -0
- package/dist/payments/verifiedEvidenceProjection.d.ts +12 -0
- package/dist/payments/verifiedEvidenceProjection.js +166 -0
- package/dist/payments/verifiedIngress.d.ts +119 -0
- package/dist/payments/verifiedIngress.js +220 -0
- package/package.json +37 -2
- package/release-gates.json +105 -3
- package/sql/20260808210000_fulfillment_shipment_rail.sql +810 -0
- package/sql/20260810180000_payment_settlement_rail.sql +574 -0
- package/sql/20260815150000_payment_truth_reconciliation.sql +479 -0
- package/sql/20260819150000_fulfillment_shipment_sequence.sql +98 -0
- package/sql/20260826100000_late_payment_shifts_the_subscription_cycle.sql +215 -0
- package/sql/20261022080000_fulfillment_command_ledger.sql +786 -0
- package/sql/20261022090000_fulfillment_delivery_alignment.sql +1106 -0
- package/src/composition/composeBundle.ts +13 -0
- package/src/composition/fulfillmentContribution.ts +28 -0
- package/src/composition/index.ts +2 -0
- package/src/fulfillment/adapterConformance.ts +195 -0
- package/src/fulfillment/bindings.ts +26 -0
- package/src/fulfillment/commerceFulfillmentHandlers.ts +263 -0
- package/src/fulfillment/commerceFulfillmentReadModel.ts +257 -0
- package/src/fulfillment/contracts/alignment.ts +13 -0
- package/src/fulfillment/contracts/bindings.ts +30 -0
- package/src/fulfillment/contracts/capabilities.ts +15 -0
- package/src/fulfillment/contracts/conformance.ts +61 -0
- package/src/fulfillment/contracts/customerAlignment.ts +4 -0
- package/src/fulfillment/contracts/dispatch.ts +58 -0
- package/src/fulfillment/contracts/index.ts +18 -0
- package/src/fulfillment/contracts/label.ts +9 -0
- package/src/fulfillment/contracts/nativeUnsupported.ts +6 -0
- package/src/fulfillment/contracts/observation.ts +18 -0
- package/src/fulfillment/contracts/operator.ts +31 -0
- package/src/fulfillment/contracts/read.ts +42 -0
- package/src/fulfillment/contracts/readback.ts +11 -0
- package/src/fulfillment/contracts/replacement.ts +15 -0
- package/src/fulfillment/contracts/store.ts +26 -0
- package/src/fulfillment/contracts/sync.ts +61 -0
- package/src/fulfillment/currentFulfillmentParcel.ts +2 -0
- package/src/fulfillment/delayReadModel.ts +60 -0
- package/src/fulfillment/deliveryAlignmentReadModel.ts +15 -0
- package/src/fulfillment/dispatchProjection.ts +20 -0
- package/src/fulfillment/fulfillmentObservationEffects.ts +86 -0
- package/src/fulfillment/handlerSchemas.ts +255 -0
- package/src/fulfillment/handlerSupport.ts +25 -0
- package/src/fulfillment/index.ts +38 -0
- package/src/fulfillment/labelLifecycle.ts +11 -0
- package/src/fulfillment/lowStockEvidenceHandler.ts +60 -0
- package/src/fulfillment/migrations.ts +8 -0
- package/src/fulfillment/nativeUnsupported.ts +23 -0
- package/src/fulfillment/normalizedFulfillmentFacts.ts +2 -0
- package/src/fulfillment/observationProjection.ts +41 -0
- package/src/fulfillment/omnipack/capabilities.ts +18 -0
- package/src/fulfillment/omnipack/client.ts +258 -0
- package/src/fulfillment/omnipack/index.ts +312 -0
- package/src/fulfillment/omnipack/mappers.ts +261 -0
- package/src/fulfillment/omnipack/observation.ts +61 -0
- package/src/fulfillment/omnipack/omnipackDispatchPayload.ts +93 -0
- package/src/fulfillment/omnipack/omnipackOutboundOrderPayload.ts +194 -0
- package/src/fulfillment/omnipack/omnipackProductPackPayload.ts +47 -0
- package/src/fulfillment/omnipack/omnipackProductPayload.ts +67 -0
- package/src/fulfillment/omnipack/omnipackReconciliationPayload.ts +7 -0
- package/src/fulfillment/omnipack/omnipackStatusVocabulary.ts +103 -0
- package/src/fulfillment/omnipack/omnipackWebhookPayload.ts +73 -0
- package/src/fulfillment/omnipack/productSyncMappers.ts +74 -0
- package/src/fulfillment/omnipack/providerError.ts +64 -0
- package/src/fulfillment/omnipack/trackingReferences.ts +77 -0
- package/src/fulfillment/paidFulfillmentRecovery.ts +252 -0
- package/src/fulfillment/paidHandoff.ts +122 -0
- package/src/fulfillment/paidHandoffPorts.ts +21 -0
- package/src/fulfillment/postgresDeliveryAlignment.ts +14 -0
- package/src/fulfillment/postgresDispatchStore.ts +55 -0
- package/src/fulfillment/postgresEvidenceReadPort.ts +59 -0
- package/src/fulfillment/postgresPaidFulfillmentRecovery.ts +29 -0
- package/src/fulfillment/postgresPaidHandoffStore.ts +11 -0
- package/src/fulfillment/postgresShipmentSpine.ts +321 -0
- package/src/fulfillment/postgresStockSyncPort.ts +65 -0
- package/src/fulfillment/productReconciliationEvidenceHandler.ts +64 -0
- package/src/fulfillment/providerCapabilities.ts +18 -0
- package/src/fulfillment/providerCommandLedger.ts +53 -0
- package/src/fulfillment/providerDispatchAcceptance.ts +31 -0
- package/src/fulfillment/providerDispatchError.ts +7 -0
- package/src/fulfillment/providerDispatchWorker.ts +131 -0
- package/src/fulfillment/providerProductSyncWorker.ts +154 -0
- package/src/fulfillment/providerReconciliationError.ts +20 -0
- package/src/fulfillment/providerReconciliationTerminalSkip.ts +8 -0
- package/src/fulfillment/providerReconciliationTimestamp.ts +34 -0
- package/src/fulfillment/providerReconciliationWorker.ts +150 -0
- package/src/fulfillment/providerStockAvailability.ts +60 -0
- package/src/fulfillment/providerStockSyncWorker.ts +222 -0
- package/src/fulfillment/providerTrackingReferences.ts +31 -0
- package/src/fulfillment/replacementRefusals.ts +43 -0
- package/src/fulfillment/replacementShipmentHandler.ts +47 -0
- package/src/fulfillment/schemaCapabilities.ts +7 -0
- package/src/fulfillment/shipmentsOverviewHandler.ts +73 -0
- package/src/fulfillment/statusMap.ts +2 -0
- package/src/fulfillment/trackingProgress.ts +1 -0
- package/src/fulfillment/verifiedIngress.ts +80 -0
- package/src/payments/adapterConformance.ts +118 -0
- package/src/payments/bindings.ts +17 -0
- package/src/payments/checkoutActivePaymentActionResolver.ts +97 -0
- package/src/payments/checkoutRecoveryPaymentResolver.ts +108 -0
- package/src/payments/contracts/adapter.ts +96 -0
- package/src/payments/contracts/conformance.ts +171 -0
- package/src/payments/contracts/evidence.ts +142 -0
- package/src/payments/contracts/index.ts +54 -0
- package/src/payments/contracts/preparedAttempt.ts +86 -0
- package/src/payments/contracts/reconciliation.ts +296 -0
- package/src/payments/contracts/repair.ts +95 -0
- package/src/payments/contracts/truth.ts +108 -0
- package/src/payments/eventConformanceRunner.ts +149 -0
- package/src/payments/index.ts +21 -0
- package/src/payments/paymentAttemptSilenceWindow.ts +26 -0
- package/src/payments/paymentMethodLifecycle.ts +50 -0
- package/src/payments/paymentProviderReconciliationAbsenceReopen.ts +130 -0
- package/src/payments/paymentProviderReconciliationEvidence.ts +117 -0
- package/src/payments/paymentProviderReconciliationWorker.ts +291 -0
- package/src/payments/paymentTruth.ts +131 -0
- package/src/payments/paymentVerifyNowHandler.ts +94 -0
- package/src/payments/paymentVerifyNowService.ts +125 -0
- package/src/payments/paymentWebhookPostProcessing.ts +36 -0
- package/src/payments/postgresPaymentTruth.ts +149 -0
- package/src/payments/preparedAttempt.ts +90 -0
- package/src/payments/preparedAttemptFinalization.ts +13 -0
- package/src/payments/schemaCapabilities.ts +10 -0
- package/src/payments/sql.ts +6 -0
- package/src/payments/stripe/classifyDecline.ts +62 -0
- package/src/payments/stripe/declineFailureHints.ts +89 -0
- package/src/payments/stripe/donorContracts.ts +87 -0
- package/src/payments/stripe/executionDecline.ts +126 -0
- package/src/payments/stripe/index.ts +265 -0
- package/src/payments/stripe/intentMapper.ts +58 -0
- package/src/payments/stripe/methodFacts.ts +21 -0
- package/src/payments/stripe/paymentFailureEvidence.ts +58 -0
- package/src/payments/stripe/stripeFailureEvidence.ts +115 -0
- package/src/payments/stripe/stripeIntentConfirmationEvidence.ts +34 -0
- package/src/payments/stripe/stripePaymentReconciliationProvider.ts +263 -0
- package/src/payments/stripe/stripeRefundEvidence.ts +19 -0
- package/src/payments/stripe/stripeSandboxPaymentExecutionAdapter.ts +283 -0
- package/src/payments/stripe/stripeWebhookNormalizer.ts +277 -0
- package/src/payments/stripe/webhookSignature.ts +55 -0
- package/src/payments/tpay/blikRecurringMapper.ts +42 -0
- package/src/payments/tpay/capabilities.ts +21 -0
- package/src/payments/tpay/classifyDecline.ts +62 -0
- package/src/payments/tpay/declineFailureHints.ts +90 -0
- package/src/payments/tpay/donorContracts.ts +104 -0
- package/src/payments/tpay/index.ts +293 -0
- package/src/payments/tpay/paymentFailureEvidence.ts +58 -0
- package/src/payments/tpay/preparedProviderAttempt.ts +38 -0
- package/src/payments/tpay/providerAttemptFailureDiagnostic.ts +99 -0
- package/src/payments/tpay/tpayCreateTransactionInput.ts +158 -0
- package/src/payments/tpay/tpayDeclineEvidence.ts +233 -0
- package/src/payments/tpay/tpayDispatchFailure.ts +186 -0
- package/src/payments/tpay/tpayFailureEvidence.ts +75 -0
- package/src/payments/tpay/tpayHttpClient.ts +290 -0
- package/src/payments/tpay/tpayHttpFailure.ts +186 -0
- package/src/payments/tpay/tpayPaymentExecutionAdapter.ts +297 -0
- package/src/payments/tpay/tpayPaymentReconciliationProvider.ts +285 -0
- package/src/payments/tpay/tpayTransactionPayload.ts +207 -0
- package/src/payments/tpay/wallClockTime.ts +57 -0
- package/src/payments/tpay/webhookJwsVerifier.ts +82 -0
- package/src/payments/tpay/webhookNormalizer.ts +279 -0
- package/src/payments/verifiedEvidenceProjection.ts +179 -0
- 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,41 @@
|
|
|
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;
|
|
17
|
+
/**
|
|
18
|
+
* @experimental Reference-application support surface; may change.
|
|
19
|
+
* Reference consumer: `server/adapters/stripe/stripeAdapterFactory.ts`.
|
|
20
|
+
*/
|
|
21
|
+
export { createStripeSandboxPaymentExecutionAdapter } from "./stripeSandboxPaymentExecutionAdapter.js";
|
|
22
|
+
/**
|
|
23
|
+
* @experimental Reference-application support surface; may change.
|
|
24
|
+
* Reference consumer: `server/runtime/paymentProviderReadbackRegistry.ts`.
|
|
25
|
+
*/
|
|
26
|
+
export { createStripePaymentReconciliationProvider } from "./stripePaymentReconciliationProvider.js";
|
|
27
|
+
/**
|
|
28
|
+
* @experimental Reference-application support surface; may change.
|
|
29
|
+
* Reference consumer: `server/infra/stripe/stripeApiClient.ts`.
|
|
30
|
+
*/
|
|
31
|
+
export { mapStripeIntent } from "./intentMapper.js";
|
|
32
|
+
/**
|
|
33
|
+
* @experimental Reference-application support surface; may change.
|
|
34
|
+
* Reference consumer: `server/infra/stripe/stripeApiClient.ts`.
|
|
35
|
+
*/
|
|
36
|
+
export { mapSetupIntent } from "./intentMapper.js";
|
|
37
|
+
/**
|
|
38
|
+
* @experimental Reference-application support surface; may change.
|
|
39
|
+
* Reference consumer: `server/adapters/paymentRecoveryGuidance.ts`.
|
|
40
|
+
*/
|
|
41
|
+
export { normalizeStripeFailureEvidence } from "./stripeFailureEvidence.js";
|
|
@@ -0,0 +1,264 @@
|
|
|
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
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* @experimental Reference-application support surface; may change.
|
|
242
|
+
* Reference consumer: `server/adapters/stripe/stripeAdapterFactory.ts`.
|
|
243
|
+
*/
|
|
244
|
+
export { createStripeSandboxPaymentExecutionAdapter } from "./stripeSandboxPaymentExecutionAdapter.js";
|
|
245
|
+
/**
|
|
246
|
+
* @experimental Reference-application support surface; may change.
|
|
247
|
+
* Reference consumer: `server/runtime/paymentProviderReadbackRegistry.ts`.
|
|
248
|
+
*/
|
|
249
|
+
export { createStripePaymentReconciliationProvider } from "./stripePaymentReconciliationProvider.js";
|
|
250
|
+
/**
|
|
251
|
+
* @experimental Reference-application support surface; may change.
|
|
252
|
+
* Reference consumer: `server/infra/stripe/stripeApiClient.ts`.
|
|
253
|
+
*/
|
|
254
|
+
export { mapStripeIntent } from "./intentMapper.js";
|
|
255
|
+
/**
|
|
256
|
+
* @experimental Reference-application support surface; may change.
|
|
257
|
+
* Reference consumer: `server/infra/stripe/stripeApiClient.ts`.
|
|
258
|
+
*/
|
|
259
|
+
export { mapSetupIntent } from "./intentMapper.js";
|
|
260
|
+
/**
|
|
261
|
+
* @experimental Reference-application support surface; may change.
|
|
262
|
+
* Reference consumer: `server/adapters/paymentRecoveryGuidance.ts`.
|
|
263
|
+
*/
|
|
264
|
+
export { normalizeStripeFailureEvidence } from "./stripeFailureEvidence.js";
|
|
@@ -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
|
+
}
|