@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,261 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mapOmnipackTrackingReferences,
|
|
3
|
+
type OmnipackTrackingReferenceEvidence,
|
|
4
|
+
} from "./trackingReferences.js";
|
|
5
|
+
|
|
6
|
+
export interface OmnipackOrderCreatedEvidence {
|
|
7
|
+
provider: "omnipack";
|
|
8
|
+
providerOrderId: string;
|
|
9
|
+
raw: Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface OmnipackProductCreatedEvidence {
|
|
13
|
+
provider: "omnipack";
|
|
14
|
+
sku: string;
|
|
15
|
+
raw: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface OmnipackStockEvidence {
|
|
19
|
+
provider: "omnipack";
|
|
20
|
+
sku: string;
|
|
21
|
+
totalQuantity: number;
|
|
22
|
+
forSaleQuantity: number;
|
|
23
|
+
reservedOrUnavailableQuantity: number;
|
|
24
|
+
stockTruth: "external_stock_master_with_local_reservations";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface OmnipackStockMovementEvidence {
|
|
28
|
+
provider: "omnipack";
|
|
29
|
+
sku: string;
|
|
30
|
+
occurredAt: string | null;
|
|
31
|
+
quantity: number;
|
|
32
|
+
operationType: string | null;
|
|
33
|
+
warehouseDocumentNumber: string | null;
|
|
34
|
+
lotNumber: string | null;
|
|
35
|
+
expirationDate: string | null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface OmnipackFulfilmentEvidence {
|
|
39
|
+
provider: "omnipack";
|
|
40
|
+
providerOrderId: string | null;
|
|
41
|
+
fulfilmentNumber: string;
|
|
42
|
+
externalNumber: string | null;
|
|
43
|
+
orderNumber: string | null;
|
|
44
|
+
createdAt: string | null;
|
|
45
|
+
updatedAt: string | null;
|
|
46
|
+
status: string | null;
|
|
47
|
+
subStatus: string | null;
|
|
48
|
+
trackingNumbers: string[];
|
|
49
|
+
trackingReferences: OmnipackTrackingReferenceEvidence[];
|
|
50
|
+
items: Array<{ sku: string; quantity: number; lotNumber: string | null; expirationDate: string | null }>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface OmnipackFulfilmentRequestEvidence {
|
|
54
|
+
provider: "omnipack";
|
|
55
|
+
warehouseDocumentNumber: string;
|
|
56
|
+
warehouseName: string | null;
|
|
57
|
+
state: string | null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface OmnipackWebhookEvidence {
|
|
61
|
+
provider: "omnipack";
|
|
62
|
+
event: string;
|
|
63
|
+
providerOrderId: string | null;
|
|
64
|
+
orderNumber: string | null;
|
|
65
|
+
fulfilmentNumber: string | null;
|
|
66
|
+
occurredAt: string | null;
|
|
67
|
+
trackingNumbers: string[];
|
|
68
|
+
trackingReferences: OmnipackTrackingReferenceEvidence[];
|
|
69
|
+
shippingMethods: string[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function mapOmnipackOrderCreatedResponse(body: unknown): OmnipackOrderCreatedEvidence {
|
|
73
|
+
const response = asRecord(body);
|
|
74
|
+
const providerOrderId = readString(response.orderId);
|
|
75
|
+
if (!providerOrderId) throw new Error("omnipack_order_response_invalid");
|
|
76
|
+
return {
|
|
77
|
+
provider: "omnipack",
|
|
78
|
+
providerOrderId,
|
|
79
|
+
raw: sanitizeOmnipackPayload(response),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Catalog load (test-readiness). The product/inbound responses are evidence only — we read
|
|
84
|
+
// back authoritative stock via getStock. We tolerate a sparse response body and fall back to
|
|
85
|
+
// the request-side sku/reference so an idempotent re-create still yields usable evidence.
|
|
86
|
+
export function mapOmnipackProductCreatedResponse(body: unknown, requestSku: string): OmnipackProductCreatedEvidence {
|
|
87
|
+
const response = asRecord(body);
|
|
88
|
+
return {
|
|
89
|
+
provider: "omnipack",
|
|
90
|
+
sku: readString(response.sku) || requestSku,
|
|
91
|
+
raw: sanitizeOmnipackPayload(response),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function mapOmnipackStockResponse(body: unknown): OmnipackStockEvidence[] {
|
|
96
|
+
const stockItems = readArray(asRecord(asRecord(body)._embedded).stockItems);
|
|
97
|
+
return stockItems.map((item) => {
|
|
98
|
+
const row = asRecord(item);
|
|
99
|
+
const totalQuantity = readNumber(row.totalQuantity);
|
|
100
|
+
const forSaleQuantity = readNumber(row.forSaleQuantity);
|
|
101
|
+
const unavailable = [
|
|
102
|
+
row.expireSoonQuantity,
|
|
103
|
+
row.faultyQuantity,
|
|
104
|
+
row.complaintsQuantity,
|
|
105
|
+
row.withheldQuantity,
|
|
106
|
+
row.packagingQuantity,
|
|
107
|
+
row.processingQuantity,
|
|
108
|
+
row.unpackingQuantity,
|
|
109
|
+
].map(readNumber).reduce((sum, value) => sum + value, 0);
|
|
110
|
+
return {
|
|
111
|
+
provider: "omnipack",
|
|
112
|
+
sku: readString(row.sku),
|
|
113
|
+
totalQuantity,
|
|
114
|
+
forSaleQuantity,
|
|
115
|
+
reservedOrUnavailableQuantity: unavailable,
|
|
116
|
+
stockTruth: "external_stock_master_with_local_reservations",
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function mapOmnipackStockMovementsResponse(body: unknown): OmnipackStockMovementEvidence[] {
|
|
122
|
+
return readArray(asRecord(body)._embedded).map((item) => {
|
|
123
|
+
const row = asRecord(item);
|
|
124
|
+
return {
|
|
125
|
+
provider: "omnipack",
|
|
126
|
+
sku: readString(row.sku),
|
|
127
|
+
occurredAt: readNullableString(row.occurredAt),
|
|
128
|
+
quantity: readNumber(row.quantity),
|
|
129
|
+
operationType: readNullableString(row.operationType),
|
|
130
|
+
warehouseDocumentNumber: readNullableString(row.warehouseDocumentNumber),
|
|
131
|
+
lotNumber: readNullableString(row.productLotNumber),
|
|
132
|
+
expirationDate: readNullableString(row.expirationDate),
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function mapOmnipackFulfilmentsResponse(body: unknown): OmnipackFulfilmentEvidence[] {
|
|
138
|
+
const fulfilments = readArray(asRecord(asRecord(body)._embedded).fulfilments);
|
|
139
|
+
return fulfilments.map((item) => {
|
|
140
|
+
const row = asRecord(item);
|
|
141
|
+
const trackingReferences = trackingReferencesFrom(row);
|
|
142
|
+
return {
|
|
143
|
+
provider: "omnipack",
|
|
144
|
+
providerOrderId: readNullableString(row.orderId) ?? readNullableString(row.providerOrderId),
|
|
145
|
+
fulfilmentNumber: readString(row.fulfilmentNumber),
|
|
146
|
+
externalNumber: readNullableString(row.externalNumber),
|
|
147
|
+
orderNumber: readNullableString(row.orderNumber),
|
|
148
|
+
createdAt: readNullableString(row.createdAt),
|
|
149
|
+
updatedAt: readNullableString(row.updatedAt),
|
|
150
|
+
status: readNullableString(row.status),
|
|
151
|
+
subStatus: readNullableString(row.subStatus),
|
|
152
|
+
trackingNumbers: trackingReferences.map((ref) => ref.trackingNumber),
|
|
153
|
+
trackingReferences,
|
|
154
|
+
items: readArray(row.items).map((rawItem) => {
|
|
155
|
+
const fulfilmentItem = asRecord(rawItem);
|
|
156
|
+
return {
|
|
157
|
+
sku: readString(fulfilmentItem.sku),
|
|
158
|
+
quantity: readNumber(fulfilmentItem.quantity),
|
|
159
|
+
lotNumber: readNullableString(fulfilmentItem.productLotNumber),
|
|
160
|
+
expirationDate: readNullableString(fulfilmentItem.expirationDate),
|
|
161
|
+
};
|
|
162
|
+
}),
|
|
163
|
+
};
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function mapOmnipackFulfilmentRequestsResponse(body: unknown): OmnipackFulfilmentRequestEvidence[] {
|
|
168
|
+
const requests = readArray(asRecord(asRecord(body)._embedded).fulfilmentRequests);
|
|
169
|
+
return requests.map((item) => {
|
|
170
|
+
const row = asRecord(item);
|
|
171
|
+
return {
|
|
172
|
+
provider: "omnipack",
|
|
173
|
+
warehouseDocumentNumber: readString(row.warehouseDocumentNumber),
|
|
174
|
+
warehouseName: readNullableString(row.warehouseName),
|
|
175
|
+
state: readNullableString(row.state),
|
|
176
|
+
};
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function mapOmnipackWebhookPayload(body: unknown, fallbackEvent = ""): OmnipackWebhookEvidence {
|
|
181
|
+
const payload = asRecord(body);
|
|
182
|
+
const shipments = readArray(payload.shipments).map(asRecord);
|
|
183
|
+
const trackingReferences = trackingReferencesFrom({ shipments });
|
|
184
|
+
return {
|
|
185
|
+
provider: "omnipack",
|
|
186
|
+
event: readString(payload.event) || fallbackEvent,
|
|
187
|
+
providerOrderId: readNullableString(payload.orderId) ?? readNullableString(payload.providerOrderId),
|
|
188
|
+
orderNumber: readNullableString(payload.orderNumber),
|
|
189
|
+
fulfilmentNumber: readNullableString(payload.fulfilmentNumber),
|
|
190
|
+
occurredAt: readNullableString(payload.occurredAt),
|
|
191
|
+
trackingNumbers: trackingReferences.map((ref) => ref.trackingNumber),
|
|
192
|
+
trackingReferences,
|
|
193
|
+
shippingMethods: shipments.map((shipment) => readString(shipment.shippingMethod)).filter(Boolean),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function sanitizeOmnipackPayload(input: Record<string, unknown>): Record<string, unknown> {
|
|
198
|
+
const blocked = new Set([
|
|
199
|
+
"authorization",
|
|
200
|
+
"Authorization",
|
|
201
|
+
"password",
|
|
202
|
+
"email",
|
|
203
|
+
"phone",
|
|
204
|
+
"firstName",
|
|
205
|
+
"lastName",
|
|
206
|
+
"name",
|
|
207
|
+
"address",
|
|
208
|
+
"street",
|
|
209
|
+
"streetNumber",
|
|
210
|
+
"houseNo",
|
|
211
|
+
"flatNo",
|
|
212
|
+
"city",
|
|
213
|
+
"postalCode",
|
|
214
|
+
]);
|
|
215
|
+
return sanitizeRecord(input, blocked);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function sanitizeRecord(input: Record<string, unknown>, blocked: Set<string>): Record<string, unknown> {
|
|
219
|
+
return Object.fromEntries(
|
|
220
|
+
Object.entries(input)
|
|
221
|
+
.filter(([key]) => !blocked.has(key))
|
|
222
|
+
.map(([key, value]) => [key, sanitizeValue(value, blocked)]),
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function sanitizeValue(input: unknown, blocked: Set<string>): unknown {
|
|
227
|
+
if (Array.isArray(input)) return input.map((value) => sanitizeValue(value, blocked));
|
|
228
|
+
if (input && typeof input === "object") return sanitizeRecord(input as Record<string, unknown>, blocked);
|
|
229
|
+
return input;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function asRecord(input: unknown): Record<string, unknown> {
|
|
233
|
+
return input && typeof input === "object" && !Array.isArray(input) ? input as Record<string, unknown> : {};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function readArray(input: unknown): unknown[] {
|
|
237
|
+
return Array.isArray(input) ? input : [];
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function readString(input: unknown): string {
|
|
241
|
+
return typeof input === "string" ? input : "";
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function readNullableString(input: unknown): string | null {
|
|
245
|
+
const value = readString(input);
|
|
246
|
+
return value || null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function readNumber(input: unknown): number {
|
|
250
|
+
return typeof input === "number" && Number.isFinite(input) ? input : 0;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function trackingReferencesFrom(row: Record<string, unknown>): OmnipackTrackingReferenceEvidence[] {
|
|
254
|
+
return mapOmnipackTrackingReferences({
|
|
255
|
+
shipments: readArray(row.shipments),
|
|
256
|
+
trackingNumbers: readArray(row.trackingNumbers),
|
|
257
|
+
readString,
|
|
258
|
+
readNullableString,
|
|
259
|
+
asRecord,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { parseNormalizedFulfillmentFact } from "@openlup/core/fulfillment";
|
|
3
|
+
import type { CanonicalLocalFulfillmentStatus, FulfillmentObservation, FulfillmentRefusal } from "../contracts/index.js";
|
|
4
|
+
import type { OmnipackWebhookEvidence } from "./mappers.js";
|
|
5
|
+
import type { OmnipackTrackingReferenceEvidence } from "./trackingReferences.js";
|
|
6
|
+
import { EVENT_TO_STATUS, type OmnipackWebhookRouteEvent } from "./omnipackWebhookPayload.js";
|
|
7
|
+
import { localStatusFor } from "./omnipackStatusVocabulary.js";
|
|
8
|
+
|
|
9
|
+
export const refusal = (reason: FulfillmentRefusal["reason"], phase: FulfillmentRefusal["phase"], retryable = false): FulfillmentRefusal => ({ reason, phase, retryable });
|
|
10
|
+
export const digest = (bytes: string | Uint8Array): string => createHash("sha256").update(bytes).digest("hex");
|
|
11
|
+
/** References are opaque identifiers, never arbitrary provider prose or URLs. */
|
|
12
|
+
export function reference(value: string | null | undefined): string | null {
|
|
13
|
+
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value) ? value : null;
|
|
14
|
+
}
|
|
15
|
+
export function timestamp(value: string | null): string | null {
|
|
16
|
+
return value && /^\d{4}-\d\d-\d\dT/u.test(value) && Number.isFinite(Date.parse(value)) ? new Date(value).toISOString() : null;
|
|
17
|
+
}
|
|
18
|
+
export function eventKind(value: string): OmnipackWebhookRouteEvent | null {
|
|
19
|
+
return Object.hasOwn(EVENT_TO_STATUS, value) ? value as OmnipackWebhookRouteEvent : null;
|
|
20
|
+
}
|
|
21
|
+
export function publicUrl(value: string | null, approve: (url: string) => boolean): string | null {
|
|
22
|
+
if (!value) return null;
|
|
23
|
+
try {
|
|
24
|
+
const url = new URL(value);
|
|
25
|
+
// Query-free URLs only: signing tokens and credentials never enter durable tracking.
|
|
26
|
+
return url.protocol === "https:" && !url.username && !url.password && !url.search && !url.hash && approve(url.href) ? url.href : null;
|
|
27
|
+
} catch { return null; }
|
|
28
|
+
}
|
|
29
|
+
function tracking(refs: OmnipackTrackingReferenceEvidence[], approve: (url: string) => boolean): FulfillmentObservation["trackingRefs"] {
|
|
30
|
+
return refs.flatMap(item => {
|
|
31
|
+
const trackingNumber = reference(item.trackingNumber);
|
|
32
|
+
return trackingNumber ? [{ trackingNumber, carrierRef: reference(item.carrier), serviceRef: reference(item.service), publicUrl: publicUrl(item.trackingUrl, approve) }] : [];
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export function observation(input: {
|
|
36
|
+
evidence: OmnipackWebhookEvidence; event: FulfillmentObservation["eventKind"]; status?: CanonicalLocalFulfillmentStatus;
|
|
37
|
+
receivedAt: string; rawDigest: string; evidenceRef: `evidence:${string}`;
|
|
38
|
+
source: "provider_webhook" | "provider_reconciliation"; fulfillmentRef?: string;
|
|
39
|
+
approveTrackingUrl: (url: string) => boolean;
|
|
40
|
+
}): FulfillmentObservation {
|
|
41
|
+
const evidence = input.evidence;
|
|
42
|
+
const externalRef = reference(evidence.providerOrderId);
|
|
43
|
+
const providerFulfillmentRef = reference(evidence.fulfilmentNumber);
|
|
44
|
+
const orderNumber = reference(evidence.orderNumber);
|
|
45
|
+
const fulfillmentRef = reference(input.fulfillmentRef) ?? providerFulfillmentRef ?? externalRef ?? orderNumber;
|
|
46
|
+
if (!fulfillmentRef) throw refusal("missing_correlation", "normalize");
|
|
47
|
+
const occurredAt = timestamp(evidence.occurredAt);
|
|
48
|
+
if (evidence.occurredAt && !occurredAt) throw refusal("malformed", "normalize");
|
|
49
|
+
const key = digest(JSON.stringify([input.event, externalRef ?? orderNumber, providerFulfillmentRef, occurredAt]));
|
|
50
|
+
const status = input.status ?? localStatusFor(EVENT_TO_STATUS[input.event as OmnipackWebhookRouteEvent]);
|
|
51
|
+
const fact = parseNormalizedFulfillmentFact({
|
|
52
|
+
schemaVersion: 1, mapperVersion: "1", factId: key, providerKind: "omnipack", fulfillmentReference: fulfillmentRef,
|
|
53
|
+
canonicalStatus: status, provenance: { source: input.source, authority: "provider_asserted" },
|
|
54
|
+
occurredAt, receivedAt: input.receivedAt,
|
|
55
|
+
rawEvent: { reference: input.evidenceRef, digestAlgorithm: "sha256", digest: input.rawDigest },
|
|
56
|
+
}, { isCanonicalStatus: (value): value is CanonicalLocalFulfillmentStatus =>
|
|
57
|
+
["provider_received", "picking", "packed", "in_transit", "delivered", "exception", "cancelled"].includes(value) });
|
|
58
|
+
return { fact, eventKind: input.event, eventKey: key, firstReceivedAt: input.receivedAt,
|
|
59
|
+
correlation: { orderRef: null, fulfillmentRef: reference(input.fulfillmentRef), externalRef, orderNumber, providerFulfillmentRef, parcelRef: null },
|
|
60
|
+
trackingRefs: tracking(evidence.trackingReferences, input.approveTrackingUrl) };
|
|
61
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
buildOmnipackOutboundOrderEvidence,
|
|
4
|
+
buildOmnipackOutboundOrderPayload,
|
|
5
|
+
type OmnipackOutboundOrderPayload,
|
|
6
|
+
type OmnipackOutboundDeliverySelection,
|
|
7
|
+
} from "./omnipackOutboundOrderPayload.js";
|
|
8
|
+
// Structural slice consumed by the donor codec; persistence and orchestration stay outside.
|
|
9
|
+
export interface OmnipackDispatchCandidate {
|
|
10
|
+
orderId: string;
|
|
11
|
+
orderNumber: string | null;
|
|
12
|
+
deliveryContact: {
|
|
13
|
+
revision: number;
|
|
14
|
+
recipientName: string | null;
|
|
15
|
+
contactEmail: string | null;
|
|
16
|
+
contactPhone: string | null;
|
|
17
|
+
line1: string;
|
|
18
|
+
city: string;
|
|
19
|
+
postalCode: string;
|
|
20
|
+
country: string;
|
|
21
|
+
selectedDelivery: OmnipackOutboundDeliverySelection | null;
|
|
22
|
+
} | null;
|
|
23
|
+
lines: Array<{ sku: string; quantity: number; productSnapshot: Record<string, unknown> }>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { buildOmnipackOutboundOrderEvidence as buildSanitizedEvidence };
|
|
27
|
+
|
|
28
|
+
export function buildOmnipackDispatchCommand(candidate: OmnipackDispatchCandidate) {
|
|
29
|
+
const providerPayload = buildOmnipackDispatchPayloadFromCandidate(candidate);
|
|
30
|
+
const sanitizedRequest = {
|
|
31
|
+
...buildOmnipackOutboundOrderEvidence(providerPayload),
|
|
32
|
+
deliveryContactRevision: candidate.deliveryContact?.revision ?? 0,
|
|
33
|
+
};
|
|
34
|
+
const serialized = serializeOmnipackDispatchPayload(providerPayload);
|
|
35
|
+
return {
|
|
36
|
+
providerPayload,
|
|
37
|
+
body: serialized.body,
|
|
38
|
+
sanitizedRequest,
|
|
39
|
+
// The hash commits to the request that may create a real provider effect.
|
|
40
|
+
// Only its digest is persisted; the PII-bearing input is never evidence.
|
|
41
|
+
requestFingerprint: serialized.requestFingerprint,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function buildOmnipackDispatchPayloadFromCandidate(
|
|
46
|
+
candidate: OmnipackDispatchCandidate,
|
|
47
|
+
): OmnipackOutboundOrderPayload {
|
|
48
|
+
const contact = candidate.deliveryContact;
|
|
49
|
+
if (!contact) throw new Error("omnipack_order_payload_missing_delivery_contact");
|
|
50
|
+
return buildOmnipackOutboundOrderPayload({
|
|
51
|
+
orderNumber: candidate.orderNumber ?? candidate.orderId,
|
|
52
|
+
deliverySelection: contact.selectedDelivery,
|
|
53
|
+
recipient: {
|
|
54
|
+
name: contact.recipientName ?? "",
|
|
55
|
+
email: contact.contactEmail,
|
|
56
|
+
phone: contact.contactPhone,
|
|
57
|
+
},
|
|
58
|
+
address: {
|
|
59
|
+
line1: contact.line1,
|
|
60
|
+
city: contact.city,
|
|
61
|
+
postalCode: contact.postalCode,
|
|
62
|
+
country: contact.country,
|
|
63
|
+
},
|
|
64
|
+
items: candidate.lines.map((line) => ({
|
|
65
|
+
sku: line.sku,
|
|
66
|
+
quantity: line.quantity,
|
|
67
|
+
lotNumber: nullableText(line.productSnapshot.lotNumber ?? line.productSnapshot.lotCode),
|
|
68
|
+
expirationDate: nullableText(line.productSnapshot.expirationDate ?? line.productSnapshot.expiresAt),
|
|
69
|
+
})),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function stableDispatchHash(value: unknown): string {
|
|
74
|
+
return serializeOmnipackDispatchPayload(value).requestFingerprint;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Materialize once: the digest and createOrder consume these exact UTF-8 bytes.
|
|
78
|
+
export function serializeOmnipackDispatchPayload(value: unknown): { body: string; requestFingerprint: string } {
|
|
79
|
+
const body = JSON.stringify(sortRecord(value));
|
|
80
|
+
return { body, requestFingerprint: createHash("sha256").update(body).digest("hex") };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function sortRecord(value: unknown): unknown {
|
|
84
|
+
if (Array.isArray(value)) return value.map(sortRecord);
|
|
85
|
+
if (!value || typeof value !== "object") return value;
|
|
86
|
+
return Object.fromEntries(
|
|
87
|
+
Object.entries(value as Record<string, unknown>).sort().map(([key, child]) => [key, sortRecord(child)]),
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function nullableText(value: unknown): string | null {
|
|
92
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
93
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
export type OmnipackOutboundDeliveryKind = "courier" | "parcel-locker";
|
|
2
|
+
|
|
3
|
+
export interface OmnipackOutboundOrderInput {
|
|
4
|
+
orderNumber: string;
|
|
5
|
+
description?: string | null;
|
|
6
|
+
additionalCustomerOrderNumber?: string | null;
|
|
7
|
+
deliverySelection: OmnipackOutboundDeliverySelection | null;
|
|
8
|
+
recipient: {
|
|
9
|
+
name: string;
|
|
10
|
+
email: string | null;
|
|
11
|
+
phone: string | null;
|
|
12
|
+
};
|
|
13
|
+
address: {
|
|
14
|
+
line1: string;
|
|
15
|
+
city: string;
|
|
16
|
+
postalCode: string;
|
|
17
|
+
country: string;
|
|
18
|
+
};
|
|
19
|
+
items: Array<{
|
|
20
|
+
sku: string;
|
|
21
|
+
quantity: number;
|
|
22
|
+
lotNumber?: string | null;
|
|
23
|
+
expirationDate?: string | null;
|
|
24
|
+
}>;
|
|
25
|
+
orderValue?: { amount: number; currency: string } | null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface OmnipackOutboundDeliverySelection {
|
|
29
|
+
providerKind?: string | null;
|
|
30
|
+
carrierKind?: string | null;
|
|
31
|
+
carrierCode?: string | null;
|
|
32
|
+
serviceCode?: string | null;
|
|
33
|
+
service?: string | null;
|
|
34
|
+
deliveryKind?: string | null;
|
|
35
|
+
kind?: string | null;
|
|
36
|
+
pickupPoint?: {
|
|
37
|
+
id?: string | null;
|
|
38
|
+
pointId?: string | null;
|
|
39
|
+
name?: string | null;
|
|
40
|
+
address?: {
|
|
41
|
+
line1?: string | null;
|
|
42
|
+
postalCode?: string | null;
|
|
43
|
+
city?: string | null;
|
|
44
|
+
country?: string | null;
|
|
45
|
+
} | null;
|
|
46
|
+
} | null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface OmnipackOutboundOrderPayload {
|
|
50
|
+
orderNumber: string;
|
|
51
|
+
description?: string;
|
|
52
|
+
additionalCustomerOrderNumber?: string;
|
|
53
|
+
items: Array<{
|
|
54
|
+
sku: string;
|
|
55
|
+
quantity: number;
|
|
56
|
+
lotNumber?: string;
|
|
57
|
+
expirationDate?: string;
|
|
58
|
+
}>;
|
|
59
|
+
shippingDetails: {
|
|
60
|
+
carrier: string;
|
|
61
|
+
service: string;
|
|
62
|
+
address: {
|
|
63
|
+
name: string;
|
|
64
|
+
street: string;
|
|
65
|
+
city: string;
|
|
66
|
+
postCode: string;
|
|
67
|
+
phone: string;
|
|
68
|
+
email?: string;
|
|
69
|
+
country: string;
|
|
70
|
+
};
|
|
71
|
+
pickUpPoint?: string;
|
|
72
|
+
};
|
|
73
|
+
orderValue?: { amount: number; currency: string };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export class OmnipackOutboundOrderPayloadError extends Error {
|
|
77
|
+
constructor(readonly code: string) {
|
|
78
|
+
super(code);
|
|
79
|
+
this.name = "OmnipackOutboundOrderPayloadError";
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function buildOmnipackOutboundOrderPayload(input: OmnipackOutboundOrderInput): OmnipackOutboundOrderPayload {
|
|
84
|
+
const orderNumber = requiredText(input.orderNumber, "omnipack_order_payload_missing_order_number");
|
|
85
|
+
const selection = input.deliverySelection;
|
|
86
|
+
const providerKind = optionalText(selection?.providerKind);
|
|
87
|
+
if (providerKind !== "omnipack") {
|
|
88
|
+
throw new OmnipackOutboundOrderPayloadError("omnipack_order_payload_missing_omnipack_delivery_selection");
|
|
89
|
+
}
|
|
90
|
+
const service = requiredText(selection?.serviceCode ?? selection?.service, "omnipack_order_payload_missing_service");
|
|
91
|
+
const carrier = service;
|
|
92
|
+
const deliveryKind = readDeliveryKind(selection);
|
|
93
|
+
const pickUpPoint = readPickUpPoint(deliveryKind, selection);
|
|
94
|
+
const items = input.items.map((item) => {
|
|
95
|
+
const lotNumber = optionalText(item.lotNumber);
|
|
96
|
+
const expirationDate = optionalText(item.expirationDate);
|
|
97
|
+
return {
|
|
98
|
+
sku: requiredText(item.sku, "omnipack_order_payload_missing_sku"),
|
|
99
|
+
quantity: requiredQuantity(item.quantity),
|
|
100
|
+
...(lotNumber ? { lotNumber } : {}),
|
|
101
|
+
...(expirationDate ? { expirationDate } : {}),
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
if (items.length === 0) {
|
|
105
|
+
throw new OmnipackOutboundOrderPayloadError("omnipack_order_payload_missing_items");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return compactRecord({
|
|
109
|
+
orderNumber,
|
|
110
|
+
description: optionalText(input.description),
|
|
111
|
+
additionalCustomerOrderNumber: optionalText(input.additionalCustomerOrderNumber),
|
|
112
|
+
items,
|
|
113
|
+
shippingDetails: compactRecord({
|
|
114
|
+
carrier,
|
|
115
|
+
service,
|
|
116
|
+
address: compactRecord({
|
|
117
|
+
name: requiredText(input.recipient.name, "omnipack_order_payload_missing_recipient_name"),
|
|
118
|
+
street: requiredText(input.address.line1, "omnipack_order_payload_missing_address_street"),
|
|
119
|
+
city: requiredText(input.address.city, "omnipack_order_payload_missing_address_city"),
|
|
120
|
+
postCode: requiredText(input.address.postalCode, "omnipack_order_payload_missing_address_post_code"),
|
|
121
|
+
phone: requiredText(input.recipient.phone, "omnipack_order_payload_missing_recipient_phone"),
|
|
122
|
+
email: optionalText(input.recipient.email),
|
|
123
|
+
country: requiredText(input.address.country, "omnipack_order_payload_missing_address_country"),
|
|
124
|
+
}),
|
|
125
|
+
pickUpPoint,
|
|
126
|
+
}),
|
|
127
|
+
orderValue: validOrderValue(input.orderValue),
|
|
128
|
+
}) as OmnipackOutboundOrderPayload;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function buildOmnipackOutboundOrderEvidence(payload: OmnipackOutboundOrderPayload): Record<string, unknown> {
|
|
132
|
+
return {
|
|
133
|
+
provider: "omnipack",
|
|
134
|
+
requestKind: "outbound_order",
|
|
135
|
+
orderNumber: payload.orderNumber,
|
|
136
|
+
carrier: payload.shippingDetails.carrier,
|
|
137
|
+
service: payload.shippingDetails.service,
|
|
138
|
+
pickUpPoint: payload.shippingDetails.pickUpPoint ?? null,
|
|
139
|
+
itemCount: payload.items.length,
|
|
140
|
+
items: payload.items.map((item) => ({
|
|
141
|
+
sku: item.sku,
|
|
142
|
+
quantity: item.quantity,
|
|
143
|
+
lotNumber: item.lotNumber ?? null,
|
|
144
|
+
expirationDate: item.expirationDate ?? null,
|
|
145
|
+
})),
|
|
146
|
+
stockTruth: "external_stock_master_with_local_reservations",
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function readDeliveryKind(selection: OmnipackOutboundDeliverySelection | null): OmnipackOutboundDeliveryKind {
|
|
151
|
+
const value = optionalText(selection?.deliveryKind ?? selection?.kind);
|
|
152
|
+
if (value === "courier" || value === "parcel-locker") return value;
|
|
153
|
+
throw new OmnipackOutboundOrderPayloadError("omnipack_order_payload_missing_delivery_kind");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function readPickUpPoint(
|
|
157
|
+
deliveryKind: OmnipackOutboundDeliveryKind,
|
|
158
|
+
selection: OmnipackOutboundDeliverySelection | null,
|
|
159
|
+
): string | undefined {
|
|
160
|
+
if (deliveryKind !== "parcel-locker") return undefined;
|
|
161
|
+
const point = optionalText(selection?.pickupPoint?.id ?? selection?.pickupPoint?.pointId);
|
|
162
|
+
if (!point) throw new OmnipackOutboundOrderPayloadError("omnipack_order_payload_missing_pickup_point");
|
|
163
|
+
return point;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function requiredText(value: unknown, code: string): string {
|
|
167
|
+
const text = optionalText(value);
|
|
168
|
+
if (!text) throw new OmnipackOutboundOrderPayloadError(code);
|
|
169
|
+
return text;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function optionalText(value: unknown): string | undefined {
|
|
173
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function requiredQuantity(value: unknown): number {
|
|
177
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
178
|
+
throw new OmnipackOutboundOrderPayloadError("omnipack_order_payload_invalid_quantity");
|
|
179
|
+
}
|
|
180
|
+
return value;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function validOrderValue(value: OmnipackOutboundOrderInput["orderValue"]): OmnipackOutboundOrderPayload["orderValue"] | undefined {
|
|
184
|
+
if (!value) return undefined;
|
|
185
|
+
if (typeof value.amount !== "number" || value.amount <= 0 || !Number.isFinite(value.amount)) {
|
|
186
|
+
throw new OmnipackOutboundOrderPayloadError("omnipack_order_payload_invalid_order_value");
|
|
187
|
+
}
|
|
188
|
+
const currency = requiredText(value.currency, "omnipack_order_payload_missing_order_value_currency");
|
|
189
|
+
return { amount: value.amount, currency };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function compactRecord<T extends Record<string, unknown>>(record: T): T {
|
|
193
|
+
return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined)) as T;
|
|
194
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Pure builder for the OmniPack product-pack request (POST /products/{sku}/packs). A pack is
|
|
2
|
+
// one EAN bound to a product: quantity === 1 is a sellable unit, quantity > 1 is a collective
|
|
3
|
+
// carton (N units behind one barcode). One SKU therefore maps to MANY packs (unit barcode / alternate barcode
|
|
4
|
+
// / collective carton). Mirrors the product/inbound payload convention (pure builder in
|
|
5
|
+
// server/_lib, wired through the client). The exact OmniPack request key names live behind the
|
|
6
|
+
// constants below so finalizing them against the (auth-gated) OmniPack product API docs is a
|
|
7
|
+
// one-line edit.
|
|
8
|
+
|
|
9
|
+
// Product-pack request key names (confirm against the OmniPack product-pack API docs).
|
|
10
|
+
const KEY_EAN = "ean";
|
|
11
|
+
const KEY_QUANTITY = "quantity";
|
|
12
|
+
|
|
13
|
+
export type OmnipackPackKind = "unit" | "collective";
|
|
14
|
+
|
|
15
|
+
export interface OmnipackProductPackInput {
|
|
16
|
+
ean: string;
|
|
17
|
+
quantity: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class OmnipackProductPackPayloadError extends Error {
|
|
21
|
+
constructor(readonly code: string) {
|
|
22
|
+
super(`omnipack_product_pack_payload_invalid: ${code}`);
|
|
23
|
+
this.name = "OmnipackProductPackPayloadError";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function requireNonEmpty(value: string | null | undefined, code: string): string {
|
|
28
|
+
const trimmed = value?.trim();
|
|
29
|
+
if (!trimmed) throw new OmnipackProductPackPayloadError(code);
|
|
30
|
+
return trimmed;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// A pack with quantity 1 is a sellable unit; quantity > 1 is a collective carton. Pure so the
|
|
34
|
+
// push worker and the DB layer derive the same `kind` from the same rule.
|
|
35
|
+
export function omnipackPackKindForQuantity(quantity: number): OmnipackPackKind {
|
|
36
|
+
return quantity > 1 ? "collective" : "unit";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Builds the product-pack payload. EAN is required (OmniPack matches physical goods by barcode);
|
|
40
|
+
// quantity must be a positive integer (the number of sellable units behind this EAN).
|
|
41
|
+
export function buildOmnipackProductPackPayload(input: OmnipackProductPackInput): Record<string, unknown> {
|
|
42
|
+
const ean = requireNonEmpty(input.ean, "missing_ean");
|
|
43
|
+
if (!Number.isInteger(input.quantity) || input.quantity <= 0) {
|
|
44
|
+
throw new OmnipackProductPackPayloadError("invalid_quantity");
|
|
45
|
+
}
|
|
46
|
+
return { [KEY_EAN]: ean, [KEY_QUANTITY]: input.quantity };
|
|
47
|
+
}
|