commit 91e2ea30aa990841a87341ac61bc5c03621dd167
parent 5d00f20e85338067b55f85d5b20085f8c4675305
Author: triesap <tyson@radroots.org>
Date: Fri, 19 Jun 2026 15:55:18 -0700
runtime: split order SDK status adapter
- move SDK status view mapping into an order submodule
- keep order status command entrypoint SDK-backed
- guard the status adapter against direct relay dependencies
- preserve existing order runtime behavior through validation
Diffstat:
3 files changed, 379 insertions(+), 275 deletions(-)
diff --git a/src/runtime/order.rs b/src/runtime/order.rs
@@ -1,5 +1,7 @@
#![allow(dead_code)]
+mod sdk_status;
+
use std::collections::{HashMap, HashSet};
use std::fs;
use std::path::{Path, PathBuf};
@@ -70,17 +72,15 @@ use radroots_replica_db_schema::trade_product::{
use radroots_sdk::{
OrderCancellationEnqueueRequest, OrderCancellationPrepareRequest, OrderCancellationReceipt,
OrderDecisionEnqueueRequest, OrderDecisionReceipt, OrderEvidenceIngestRequest,
- OrderFulfillmentStatusKind, OrderFulfillmentUpdateEnqueueRequest,
- OrderFulfillmentUpdatePrepareRequest, OrderFulfillmentUpdateReceipt, OrderPaymentStateKind,
- OrderReceiptRecordEnqueueRequest, OrderReceiptRecordPrepareRequest, OrderReceiptRecordReceipt,
- OrderRequestEvidenceIngestRequest, OrderRevisionDecisionEnqueueRequest,
- OrderRevisionDecisionPrepareRequest, OrderRevisionDecisionReceipt,
- OrderRevisionProposalEnqueueRequest, OrderRevisionProposalPrepareRequest,
- OrderRevisionProposalReceipt, OrderSettlementStateKind, OrderStatusKind, OrderStatusReceipt,
- OrderStatusRequest, OrderSubmitEnqueueRequest, OrderSubmitPlan, OrderSubmitPrepareRequest,
- OrderSubmitReceipt, PushOutboxEventReceipt, PushOutboxEventState, PushOutboxReceipt,
- PushOutboxRelayOutcomeKind, PushOutboxRequest, SdkMutationState, SdkOrderStatusIssue,
- SdkRelayTargetPolicy, SdkRelayUrlPolicy,
+ OrderFulfillmentUpdateEnqueueRequest, OrderFulfillmentUpdatePrepareRequest,
+ OrderFulfillmentUpdateReceipt, OrderReceiptRecordEnqueueRequest,
+ OrderReceiptRecordPrepareRequest, OrderReceiptRecordReceipt, OrderRequestEvidenceIngestRequest,
+ OrderRevisionDecisionEnqueueRequest, OrderRevisionDecisionPrepareRequest,
+ OrderRevisionDecisionReceipt, OrderRevisionProposalEnqueueRequest,
+ OrderRevisionProposalPrepareRequest, OrderRevisionProposalReceipt, OrderStatusRequest,
+ OrderSubmitEnqueueRequest, OrderSubmitPlan, OrderSubmitPrepareRequest, OrderSubmitReceipt,
+ PushOutboxEventReceipt, PushOutboxEventState, PushOutboxReceipt, PushOutboxRelayOutcomeKind,
+ PushOutboxRequest, SdkMutationState, SdkRelayTargetPolicy, SdkRelayUrlPolicy,
};
use radroots_sql_core::SqliteExecutor;
use radroots_trade::order::{
@@ -133,6 +133,8 @@ use crate::view::runtime::{
OrderStatusRevisionView, OrderStatusView, OrderSubmitView, OrderSummaryView, RelayFailureView,
};
+use self::sdk_status::sdk_order_status_view;
+
const ORDER_DRAFT_KIND: &str = "order_draft_v1";
const ORDER_SOURCE: &str = "local order drafts ยท local first";
const ORDER_APP_RECORD_SOURCE: &str = "app-authored shared local order records";
@@ -2335,266 +2337,6 @@ fn legacy_order_preflight_relay_status(
Ok(view)
}
-fn sdk_order_status_view(receipt: OrderStatusReceipt) -> OrderStatusView {
- let state = sdk_order_status_state(receipt.status).to_owned();
- let reducer_issues = receipt
- .issues
- .iter()
- .map(sdk_order_status_issue_view)
- .collect::<Vec<_>>();
- let reason = sdk_order_status_reason(receipt.status, receipt.order_id.as_str());
- let fulfillment = sdk_order_status_fulfillment_view(&receipt, reducer_issues.as_slice());
- let lifecycle = sdk_order_status_lifecycle_view(&receipt, reducer_issues.as_slice());
- let payment = Some(sdk_order_status_payment_view(
- &receipt,
- reducer_issues.as_slice(),
- ));
-
- OrderStatusView {
- state,
- source: ORDER_STATUS_SDK_SOURCE.to_owned(),
- order_id: receipt.order_id.to_string(),
- actor_context_source: ORDER_ACTOR_CONTEXT_SDK_LOCAL.to_owned(),
- request_event_id: sdk_event_id_string(receipt.request_event_id.as_ref()),
- decision_event_id: sdk_event_id_string(receipt.decision_event_id.as_ref()),
- agreement_event_id: sdk_order_status_agreement_event_id(&receipt),
- listing_event_id: None,
- listing_addr: None,
- buyer_pubkey: None,
- seller_pubkey: None,
- economics: None,
- last_event_id: sdk_event_id_string(receipt.last_event_id.as_ref()),
- revision: None,
- inventory: None,
- fulfillment,
- lifecycle: Some(lifecycle),
- payment,
- reducer_issues,
- target_relays: Vec::new(),
- connected_relays: Vec::new(),
- failed_relays: Vec::new(),
- fetched_count: 0,
- decoded_count: receipt.event_count,
- skipped_count: 0,
- reason,
- actions: Vec::new(),
- }
-}
-
-fn sdk_order_status_state(status: OrderStatusKind) -> &'static str {
- match status {
- OrderStatusKind::Missing => "missing",
- OrderStatusKind::Requested => "requested",
- OrderStatusKind::Accepted => "accepted",
- OrderStatusKind::Declined => "declined",
- OrderStatusKind::Cancelled => "cancelled",
- OrderStatusKind::Completed => "completed",
- OrderStatusKind::Disputed => "disputed",
- OrderStatusKind::Invalid => "invalid",
- _ => "unknown",
- }
-}
-
-fn sdk_order_status_reason(status: OrderStatusKind, order_id: &str) -> Option<String> {
- match status {
- OrderStatusKind::Missing => Some(format!("no local SDK order events matched `{order_id}`")),
- OrderStatusKind::Invalid => Some(format!(
- "local SDK order events for `{order_id}` failed reducer validation"
- )),
- _ => None,
- }
-}
-
-fn sdk_order_status_agreement_event_id(receipt: &OrderStatusReceipt) -> Option<String> {
- sdk_event_id_string(receipt.agreement_event_id.as_ref())
-}
-
-fn sdk_order_status_fulfillment_view(
- receipt: &OrderStatusReceipt,
- issues: &[OrderIssueView],
-) -> Option<OrderStatusFulfillmentView> {
- let fulfillment_issues = issues
- .iter()
- .filter(|issue| {
- issue.code.starts_with("fulfillment_") || issue.code == "forked_fulfillments"
- })
- .cloned()
- .collect::<Vec<_>>();
- if !fulfillment_issues.is_empty() {
- return Some(OrderStatusFulfillmentView {
- state: "invalid".to_owned(),
- event_id: sdk_event_id_string(receipt.fulfillment_event_id.as_ref()),
- root_event_id: sdk_event_id_string(receipt.request_event_id.as_ref()),
- prev_event_id: sdk_event_id_string(receipt.decision_event_id.as_ref()),
- terminal: false,
- inventory_released: false,
- issues: fulfillment_issues,
- });
- }
- let fulfillment_status = receipt.fulfillment_status?;
- Some(OrderStatusFulfillmentView {
- state: sdk_fulfillment_status_state(fulfillment_status).to_owned(),
- event_id: sdk_event_id_string(receipt.fulfillment_event_id.as_ref()),
- root_event_id: sdk_event_id_string(receipt.request_event_id.as_ref()),
- prev_event_id: sdk_event_id_string(receipt.decision_event_id.as_ref()),
- terminal: matches!(
- fulfillment_status,
- OrderFulfillmentStatusKind::Delivered | OrderFulfillmentStatusKind::SellerCancelled
- ),
- inventory_released: matches!(
- fulfillment_status,
- OrderFulfillmentStatusKind::SellerCancelled
- ),
- issues: Vec::new(),
- })
-}
-
-fn sdk_order_status_payment_view(
- receipt: &OrderStatusReceipt,
- issues: &[OrderIssueView],
-) -> OrderStatusPaymentView {
- let payment_issues = issues
- .iter()
- .filter(|issue| issue.code.starts_with("payment_") || issue.code.starts_with("settlement_"))
- .cloned()
- .collect::<Vec<_>>();
- OrderStatusPaymentView {
- state: sdk_payment_state(receipt.payment_state).to_owned(),
- settlement_state: sdk_settlement_state(receipt.settlement_state).to_owned(),
- payment_event_id: None,
- settlement_event_id: None,
- agreement_event_id: sdk_order_status_agreement_event_id(receipt),
- quote_id: None,
- quote_version: None,
- economics_digest: None,
- amount: None,
- currency: None,
- method: None,
- reference: None,
- paid_at: None,
- reason: None,
- issues: payment_issues,
- }
-}
-
-fn sdk_order_status_lifecycle_view(
- receipt: &OrderStatusReceipt,
- issues: &[OrderIssueView],
-) -> OrderStatusLifecycleView {
- let cancellation = receipt.cancellation_event_id.as_ref().map(|event_id| {
- OrderStatusLifecycleCancellationView {
- event_id: event_id.to_string(),
- root_event_id: sdk_event_id_string(receipt.request_event_id.as_ref()),
- prev_event_id: sdk_event_id_string(receipt.decision_event_id.as_ref()),
- reason: None,
- }
- });
- let receipt_view =
- receipt
- .receipt_event_id
- .as_ref()
- .map(|event_id| OrderStatusLifecycleReceiptView {
- event_id: event_id.to_string(),
- root_event_id: sdk_event_id_string(receipt.request_event_id.as_ref()),
- prev_event_id: sdk_event_id_string(receipt.fulfillment_event_id.as_ref()),
- received: matches!(receipt.status, OrderStatusKind::Completed),
- issue: None,
- received_at: None,
- });
-
- OrderStatusLifecycleView {
- phase: sdk_order_status_lifecycle_phase(receipt).to_owned(),
- terminal: receipt.lifecycle_terminal,
- event_id: sdk_event_id_string(receipt.last_event_id.as_ref()),
- root_event_id: sdk_event_id_string(receipt.request_event_id.as_ref()),
- prev_event_id: None,
- cancellation,
- receipt: receipt_view,
- settlement_required: !matches!(
- receipt.settlement_state,
- OrderSettlementStateKind::NotRequired
- ),
- settlement_reason: None,
- issues: issues.to_vec(),
- }
-}
-
-fn sdk_order_status_lifecycle_phase(receipt: &OrderStatusReceipt) -> &'static str {
- match receipt.status {
- OrderStatusKind::Missing => "missing",
- OrderStatusKind::Requested => "requested",
- OrderStatusKind::Accepted => match receipt.fulfillment_status {
- Some(OrderFulfillmentStatusKind::Preparing)
- | Some(OrderFulfillmentStatusKind::OutForDelivery) => "fulfillment_in_progress",
- Some(
- OrderFulfillmentStatusKind::ReadyForPickup
- | OrderFulfillmentStatusKind::Delivered
- | OrderFulfillmentStatusKind::SellerCancelled,
- ) => "fulfilled",
- Some(OrderFulfillmentStatusKind::AcceptedNotFulfilled) | None => "accepted",
- Some(_) => "accepted",
- },
- OrderStatusKind::Declined => "declined",
- OrderStatusKind::Cancelled => "cancelled",
- OrderStatusKind::Completed => "completed",
- OrderStatusKind::Disputed => "disputed",
- OrderStatusKind::Invalid => "invalid",
- _ => "unknown",
- }
-}
-
-fn sdk_fulfillment_status_state(status: OrderFulfillmentStatusKind) -> &'static str {
- match status {
- OrderFulfillmentStatusKind::AcceptedNotFulfilled => "accepted_not_fulfilled",
- OrderFulfillmentStatusKind::Preparing => "preparing",
- OrderFulfillmentStatusKind::ReadyForPickup => "ready_for_pickup",
- OrderFulfillmentStatusKind::OutForDelivery => "out_for_delivery",
- OrderFulfillmentStatusKind::Delivered => "delivered",
- OrderFulfillmentStatusKind::SellerCancelled => "seller_cancelled",
- _ => "unknown",
- }
-}
-
-fn sdk_payment_state(state: OrderPaymentStateKind) -> &'static str {
- match state {
- OrderPaymentStateKind::NotRecorded => "not_recorded",
- OrderPaymentStateKind::Recorded => "recorded",
- OrderPaymentStateKind::Settled => "settled",
- OrderPaymentStateKind::Rejected => "rejected",
- OrderPaymentStateKind::Invalid => "invalid",
- _ => "unknown",
- }
-}
-
-fn sdk_settlement_state(state: OrderSettlementStateKind) -> &'static str {
- match state {
- OrderSettlementStateKind::NotRequired => "not_required",
- OrderSettlementStateKind::Pending => "pending",
- OrderSettlementStateKind::Accepted => "accepted",
- OrderSettlementStateKind::Rejected => "rejected",
- OrderSettlementStateKind::Invalid => "invalid",
- _ => "unknown",
- }
-}
-
-fn sdk_order_status_issue_view(issue: &SdkOrderStatusIssue) -> OrderIssueView {
- let code = issue.code();
- OrderIssueView {
- code: code.clone(),
- field: "sdk_order_status".to_owned(),
- message: format!("SDK order status reported `{code}`"),
- event_ids: issue
- .event_ids
- .iter()
- .map(RadrootsEventId::to_string)
- .collect(),
- }
-}
-
-fn sdk_event_id_string(event_id: Option<&RadrootsEventId>) -> Option<String> {
- event_id.map(RadrootsEventId::to_string)
-}
-
enum OrderStatusRecord {
Request {
listing_event_id: Option<String>,
@@ -14006,8 +13748,10 @@ mod tests {
use radroots_nostr::prelude::{radroots_event_from_nostr, radroots_nostr_build_event};
use radroots_runtime_paths::RadrootsMigrationReport;
use radroots_sdk::{
- OrderPaymentStateKind, OrderSettlementStateKind, OrderStatusKind, OrderStatusReceipt,
- OrderSubmitPlan, RadrootsSdkTimestamp, SdkOrderStatusIssue, SdkOrderStatusIssueKind,
+ OrderPaymentHandoffKind, OrderPaymentStateKind, OrderSettlementStateKind,
+ OrderStatusEligibility, OrderStatusEvidenceSummary, OrderStatusKind,
+ OrderStatusNextActionKind, OrderStatusReceipt, OrderSubmitPlan, OrderWorkflowKind,
+ OrderWorkflowPlan, RadrootsSdkTimestamp, SdkOrderStatusIssue, SdkOrderStatusIssueKind,
SdkOrderStatusSource,
};
use radroots_secret_vault::RadrootsSecretBackend;
@@ -14112,11 +13856,20 @@ mod tests {
fixture.buyer_pubkey.as_str(),
)
.expect("frozen draft");
+ let expected_event_id = test_event_id_char('3');
+ let workflow_kind = OrderWorkflowKind::Submit;
OrderSubmitPlan {
+ workflow: OrderWorkflowPlan {
+ kind: workflow_kind,
+ operation_kind: workflow_kind.operation_kind(),
+ contract_id: workflow_kind.contract_id(),
+ expected_event_id: expected_event_id.clone(),
+ created_at: RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000),
+ },
order_id: test_order_id(fixture.order_id.as_str()),
listing_addr: test_listing_addr(fixture.listing_addr.as_str()),
listing_event_id: test_event_id(fixture.listing_event_id.as_str()),
- expected_event_id: test_event_id_char('3'),
+ expected_event_id,
frozen_draft,
created_at: RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000),
}
@@ -15861,6 +15614,28 @@ mod tests {
payment_state: OrderPaymentStateKind::NotRecorded,
settlement_state: OrderSettlementStateKind::NotRequired,
lifecycle_terminal: false,
+ evidence: OrderStatusEvidenceSummary {
+ event_count: 2,
+ limit_applied: 500,
+ has_request: true,
+ has_decision: true,
+ has_agreement: true,
+ has_pending_revision: false,
+ has_fulfillment: false,
+ has_cancellation: false,
+ has_receipt: false,
+ has_issues: false,
+ },
+ eligibility: OrderStatusEligibility {
+ can_decide: false,
+ can_propose_revision: true,
+ can_decide_revision: false,
+ can_cancel: true,
+ can_update_fulfillment: true,
+ can_record_receipt: false,
+ },
+ payment_handoff: OrderPaymentHandoffKind::InPersonOrOffPlatformPending,
+ next_action: OrderStatusNextActionKind::ArrangeInPersonOrOffPlatformPayment,
event_ids: vec![request_event_id.clone(), decision_event_id.clone()],
request_event_id: Some(request_event_id.clone()),
decision_event_id: Some(decision_event_id.clone()),
@@ -15923,6 +15698,28 @@ mod tests {
payment_state: OrderPaymentStateKind::NotRecorded,
settlement_state: OrderSettlementStateKind::NotRequired,
lifecycle_terminal: false,
+ evidence: OrderStatusEvidenceSummary {
+ event_count: 3,
+ limit_applied: 500,
+ has_request: true,
+ has_decision: true,
+ has_agreement: true,
+ has_pending_revision: false,
+ has_fulfillment: false,
+ has_cancellation: false,
+ has_receipt: false,
+ has_issues: false,
+ },
+ eligibility: OrderStatusEligibility {
+ can_decide: false,
+ can_propose_revision: true,
+ can_decide_revision: false,
+ can_cancel: true,
+ can_update_fulfillment: true,
+ can_record_receipt: false,
+ },
+ payment_handoff: OrderPaymentHandoffKind::InPersonOrOffPlatformPending,
+ next_action: OrderStatusNextActionKind::ArrangeInPersonOrOffPlatformPayment,
event_ids: vec![
request_event_id.clone(),
decision_event_id.clone(),
@@ -15975,6 +15772,28 @@ mod tests {
payment_state: OrderPaymentStateKind::NotRecorded,
settlement_state: OrderSettlementStateKind::NotRequired,
lifecycle_terminal: false,
+ evidence: OrderStatusEvidenceSummary {
+ event_count: 2,
+ limit_applied: 500,
+ has_request: false,
+ has_decision: false,
+ has_agreement: false,
+ has_pending_revision: false,
+ has_fulfillment: false,
+ has_cancellation: false,
+ has_receipt: false,
+ has_issues: true,
+ },
+ eligibility: OrderStatusEligibility {
+ can_decide: false,
+ can_propose_revision: false,
+ can_decide_revision: false,
+ can_cancel: false,
+ can_update_fulfillment: false,
+ can_record_receipt: false,
+ },
+ payment_handoff: OrderPaymentHandoffKind::Invalid,
+ next_action: OrderStatusNextActionKind::InspectEvidenceIssues,
event_ids: vec![request_event_id, fork_event_id.clone()],
request_event_id: None,
decision_event_id: None,
diff --git a/src/runtime/order/sdk_status.rs b/src/runtime/order/sdk_status.rs
@@ -0,0 +1,273 @@
+use radroots_events::ids::RadrootsEventId;
+use radroots_sdk::{
+ OrderFulfillmentStatusKind, OrderPaymentStateKind, OrderSettlementStateKind, OrderStatusKind,
+ OrderStatusReceipt, SdkOrderStatusIssue,
+};
+
+use crate::view::runtime::{
+ OrderIssueView, OrderStatusFulfillmentView, OrderStatusLifecycleCancellationView,
+ OrderStatusLifecycleReceiptView, OrderStatusLifecycleView, OrderStatusPaymentView,
+ OrderStatusView,
+};
+
+use super::{ORDER_ACTOR_CONTEXT_SDK_LOCAL, ORDER_STATUS_SDK_SOURCE};
+
+pub(super) fn sdk_order_status_view(receipt: OrderStatusReceipt) -> OrderStatusView {
+ let state = sdk_order_status_state(receipt.status).to_owned();
+ let reducer_issues = receipt
+ .issues
+ .iter()
+ .map(sdk_order_status_issue_view)
+ .collect::<Vec<_>>();
+ let reason = sdk_order_status_reason(receipt.status, receipt.order_id.as_str());
+ let fulfillment = sdk_order_status_fulfillment_view(&receipt, reducer_issues.as_slice());
+ let lifecycle = sdk_order_status_lifecycle_view(&receipt, reducer_issues.as_slice());
+ let payment = Some(sdk_order_status_payment_view(
+ &receipt,
+ reducer_issues.as_slice(),
+ ));
+
+ OrderStatusView {
+ state,
+ source: ORDER_STATUS_SDK_SOURCE.to_owned(),
+ order_id: receipt.order_id.to_string(),
+ actor_context_source: ORDER_ACTOR_CONTEXT_SDK_LOCAL.to_owned(),
+ request_event_id: sdk_event_id_string(receipt.request_event_id.as_ref()),
+ decision_event_id: sdk_event_id_string(receipt.decision_event_id.as_ref()),
+ agreement_event_id: sdk_order_status_agreement_event_id(&receipt),
+ listing_event_id: None,
+ listing_addr: None,
+ buyer_pubkey: None,
+ seller_pubkey: None,
+ economics: None,
+ last_event_id: sdk_event_id_string(receipt.last_event_id.as_ref()),
+ revision: None,
+ inventory: None,
+ fulfillment,
+ lifecycle: Some(lifecycle),
+ payment,
+ reducer_issues,
+ target_relays: Vec::new(),
+ connected_relays: Vec::new(),
+ failed_relays: Vec::new(),
+ fetched_count: 0,
+ decoded_count: receipt.event_count,
+ skipped_count: 0,
+ reason,
+ actions: Vec::new(),
+ }
+}
+
+fn sdk_order_status_state(status: OrderStatusKind) -> &'static str {
+ match status {
+ OrderStatusKind::Missing => "missing",
+ OrderStatusKind::Requested => "requested",
+ OrderStatusKind::Accepted => "accepted",
+ OrderStatusKind::Declined => "declined",
+ OrderStatusKind::Cancelled => "cancelled",
+ OrderStatusKind::Completed => "completed",
+ OrderStatusKind::Disputed => "disputed",
+ OrderStatusKind::Invalid => "invalid",
+ _ => "unknown",
+ }
+}
+
+fn sdk_order_status_reason(status: OrderStatusKind, order_id: &str) -> Option<String> {
+ match status {
+ OrderStatusKind::Missing => Some(format!("no local SDK order events matched `{order_id}`")),
+ OrderStatusKind::Invalid => Some(format!(
+ "local SDK order events for `{order_id}` failed reducer validation"
+ )),
+ _ => None,
+ }
+}
+
+fn sdk_order_status_agreement_event_id(receipt: &OrderStatusReceipt) -> Option<String> {
+ sdk_event_id_string(receipt.agreement_event_id.as_ref())
+}
+
+fn sdk_order_status_fulfillment_view(
+ receipt: &OrderStatusReceipt,
+ issues: &[OrderIssueView],
+) -> Option<OrderStatusFulfillmentView> {
+ let fulfillment_issues = issues
+ .iter()
+ .filter(|issue| {
+ issue.code.starts_with("fulfillment_") || issue.code == "forked_fulfillments"
+ })
+ .cloned()
+ .collect::<Vec<_>>();
+ if !fulfillment_issues.is_empty() {
+ return Some(OrderStatusFulfillmentView {
+ state: "invalid".to_owned(),
+ event_id: sdk_event_id_string(receipt.fulfillment_event_id.as_ref()),
+ root_event_id: sdk_event_id_string(receipt.request_event_id.as_ref()),
+ prev_event_id: sdk_event_id_string(receipt.decision_event_id.as_ref()),
+ terminal: false,
+ inventory_released: false,
+ issues: fulfillment_issues,
+ });
+ }
+ let fulfillment_status = receipt.fulfillment_status?;
+ Some(OrderStatusFulfillmentView {
+ state: sdk_fulfillment_status_state(fulfillment_status).to_owned(),
+ event_id: sdk_event_id_string(receipt.fulfillment_event_id.as_ref()),
+ root_event_id: sdk_event_id_string(receipt.request_event_id.as_ref()),
+ prev_event_id: sdk_event_id_string(receipt.decision_event_id.as_ref()),
+ terminal: matches!(
+ fulfillment_status,
+ OrderFulfillmentStatusKind::Delivered | OrderFulfillmentStatusKind::SellerCancelled
+ ),
+ inventory_released: matches!(
+ fulfillment_status,
+ OrderFulfillmentStatusKind::SellerCancelled
+ ),
+ issues: Vec::new(),
+ })
+}
+
+fn sdk_order_status_payment_view(
+ receipt: &OrderStatusReceipt,
+ issues: &[OrderIssueView],
+) -> OrderStatusPaymentView {
+ let payment_issues = issues
+ .iter()
+ .filter(|issue| issue.code.starts_with("payment_") || issue.code.starts_with("settlement_"))
+ .cloned()
+ .collect::<Vec<_>>();
+ OrderStatusPaymentView {
+ state: sdk_payment_state(receipt.payment_state).to_owned(),
+ settlement_state: sdk_settlement_state(receipt.settlement_state).to_owned(),
+ payment_event_id: None,
+ settlement_event_id: None,
+ agreement_event_id: sdk_order_status_agreement_event_id(receipt),
+ quote_id: None,
+ quote_version: None,
+ economics_digest: None,
+ amount: None,
+ currency: None,
+ method: None,
+ reference: None,
+ paid_at: None,
+ reason: None,
+ issues: payment_issues,
+ }
+}
+
+fn sdk_order_status_lifecycle_view(
+ receipt: &OrderStatusReceipt,
+ issues: &[OrderIssueView],
+) -> OrderStatusLifecycleView {
+ let cancellation = receipt.cancellation_event_id.as_ref().map(|event_id| {
+ OrderStatusLifecycleCancellationView {
+ event_id: event_id.to_string(),
+ root_event_id: sdk_event_id_string(receipt.request_event_id.as_ref()),
+ prev_event_id: sdk_event_id_string(receipt.decision_event_id.as_ref()),
+ reason: None,
+ }
+ });
+ let receipt_view =
+ receipt
+ .receipt_event_id
+ .as_ref()
+ .map(|event_id| OrderStatusLifecycleReceiptView {
+ event_id: event_id.to_string(),
+ root_event_id: sdk_event_id_string(receipt.request_event_id.as_ref()),
+ prev_event_id: sdk_event_id_string(receipt.fulfillment_event_id.as_ref()),
+ received: matches!(receipt.status, OrderStatusKind::Completed),
+ issue: None,
+ received_at: None,
+ });
+
+ OrderStatusLifecycleView {
+ phase: sdk_order_status_lifecycle_phase(receipt).to_owned(),
+ terminal: receipt.lifecycle_terminal,
+ event_id: sdk_event_id_string(receipt.last_event_id.as_ref()),
+ root_event_id: sdk_event_id_string(receipt.request_event_id.as_ref()),
+ prev_event_id: None,
+ cancellation,
+ receipt: receipt_view,
+ settlement_required: !matches!(
+ receipt.settlement_state,
+ OrderSettlementStateKind::NotRequired
+ ),
+ settlement_reason: None,
+ issues: issues.to_vec(),
+ }
+}
+
+fn sdk_order_status_lifecycle_phase(receipt: &OrderStatusReceipt) -> &'static str {
+ match receipt.status {
+ OrderStatusKind::Missing => "missing",
+ OrderStatusKind::Requested => "requested",
+ OrderStatusKind::Accepted => match receipt.fulfillment_status {
+ Some(OrderFulfillmentStatusKind::Preparing)
+ | Some(OrderFulfillmentStatusKind::OutForDelivery) => "fulfillment_in_progress",
+ Some(
+ OrderFulfillmentStatusKind::ReadyForPickup
+ | OrderFulfillmentStatusKind::Delivered
+ | OrderFulfillmentStatusKind::SellerCancelled,
+ ) => "fulfilled",
+ Some(OrderFulfillmentStatusKind::AcceptedNotFulfilled) | None => "accepted",
+ Some(_) => "accepted",
+ },
+ OrderStatusKind::Declined => "declined",
+ OrderStatusKind::Cancelled => "cancelled",
+ OrderStatusKind::Completed => "completed",
+ OrderStatusKind::Disputed => "disputed",
+ OrderStatusKind::Invalid => "invalid",
+ _ => "unknown",
+ }
+}
+
+fn sdk_fulfillment_status_state(status: OrderFulfillmentStatusKind) -> &'static str {
+ match status {
+ OrderFulfillmentStatusKind::AcceptedNotFulfilled => "accepted_not_fulfilled",
+ OrderFulfillmentStatusKind::Preparing => "preparing",
+ OrderFulfillmentStatusKind::ReadyForPickup => "ready_for_pickup",
+ OrderFulfillmentStatusKind::OutForDelivery => "out_for_delivery",
+ OrderFulfillmentStatusKind::Delivered => "delivered",
+ OrderFulfillmentStatusKind::SellerCancelled => "seller_cancelled",
+ _ => "unknown",
+ }
+}
+
+fn sdk_payment_state(state: OrderPaymentStateKind) -> &'static str {
+ match state {
+ OrderPaymentStateKind::NotRecorded => "not_recorded",
+ OrderPaymentStateKind::Recorded => "recorded",
+ OrderPaymentStateKind::Settled => "settled",
+ OrderPaymentStateKind::Rejected => "rejected",
+ OrderPaymentStateKind::Invalid => "invalid",
+ _ => "unknown",
+ }
+}
+
+fn sdk_settlement_state(state: OrderSettlementStateKind) -> &'static str {
+ match state {
+ OrderSettlementStateKind::NotRequired => "not_required",
+ OrderSettlementStateKind::Pending => "pending",
+ OrderSettlementStateKind::Accepted => "accepted",
+ OrderSettlementStateKind::Rejected => "rejected",
+ OrderSettlementStateKind::Invalid => "invalid",
+ _ => "unknown",
+ }
+}
+
+fn sdk_order_status_issue_view(issue: &SdkOrderStatusIssue) -> OrderIssueView {
+ let code = issue.code();
+ OrderIssueView {
+ code: code.clone(),
+ field: "sdk_order_status".to_owned(),
+ message: format!("SDK order status reported `{code}`"),
+ event_ids: issue
+ .event_ids
+ .iter()
+ .map(RadrootsEventId::to_string)
+ .collect(),
+ }
+}
+
+fn sdk_event_id_string(event_id: Option<&RadrootsEventId>) -> Option<String> {
+ event_id.map(RadrootsEventId::to_string)
+}
diff --git a/src/runtime/sdk.rs b/src/runtime/sdk.rs
@@ -446,6 +446,18 @@ mod tests {
required_tokens: &["OrderStatusRequest::parse", "session.sdk().orders().status"],
},
MigratedCliPathGuard {
+ label: "order SDK status adapter",
+ path: "src/runtime/order/sdk_status.rs",
+ start: "pub(super) fn sdk_order_status_view(",
+ end: "fn sdk_event_id_string(",
+ required_tokens: &[
+ "OrderStatusReceipt",
+ "OrderStatusView",
+ "OrderStatusLifecycleView",
+ "OrderStatusPaymentView",
+ ],
+ },
+ MigratedCliPathGuard {
label: "order submit",
path: "src/runtime/order.rs",
start: "fn prepare_order_submit_via_sdk(",
@@ -472,7 +484,7 @@ mod tests {
MigratedCliPathGuard {
label: "order lifecycle",
path: "src/runtime/order.rs",
- start: "fn prepare_order_revision_proposal_dry_run_via_sdk(",
+ start: "fn publish_order_revision(",
end: "fn publish_order_payment(",
required_tokens: &[
"prepare_revision_proposal(OrderRevisionProposalPrepareRequest::new",