commit c4ce4ecda87c69a6d9fb2479300501c9c589e7aa
parent d1c1b9c030e1f6168d1d9fb09c3d5dac49437b5a
Author: triesap <tyson@radroots.org>
Date: Fri, 19 Jun 2026 18:17:25 -0700
orders: slim SDK order runtime API
- remove post-agreement order workflow methods and exports
- expose agreement evidence on order status receipts
- align order lifecycle preflight with requested negotiation state
- update runtime and boundary tests for agreement-only status
Diffstat:
5 files changed, 420 insertions(+), 1993 deletions(-)
diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs
@@ -61,26 +61,21 @@ pub use crate::listings_runtime::{
#[cfg(feature = "runtime")]
pub use crate::orders_runtime::{
ORDER_CANCELLATION_OPERATION_KIND, ORDER_DECISION_OPERATION_KIND,
- ORDER_FULFILLMENT_UPDATE_OPERATION_KIND, ORDER_RECEIPT_RECORD_OPERATION_KIND,
ORDER_REVISION_DECISION_OPERATION_KIND, ORDER_REVISION_PROPOSAL_OPERATION_KIND,
ORDER_STATUS_DEFAULT_LIMIT, ORDER_STATUS_MAX_LIMIT, ORDER_SUBMIT_OPERATION_KIND,
OrderCancellationEnqueueRequest, OrderCancellationPlan, OrderCancellationPrepareRequest,
OrderCancellationReceipt, OrderDecisionEnqueueRequest, OrderDecisionPlan,
OrderDecisionPrepareRequest, OrderDecisionReceipt, OrderEvidenceIngestReceipt,
- OrderEvidenceIngestRequest, OrderFulfillmentStatusKind, OrderFulfillmentUpdateEnqueueRequest,
- OrderFulfillmentUpdatePlan, OrderFulfillmentUpdatePrepareRequest,
- OrderFulfillmentUpdateReceipt, OrderPaymentHandoffKind, OrderPaymentStateKind,
- OrderReceiptRecordEnqueueRequest, OrderReceiptRecordPlan, OrderReceiptRecordPrepareRequest,
- OrderReceiptRecordReceipt, OrderRequestEvidenceIngestReceipt,
+ OrderEvidenceIngestRequest, OrderRequestEvidenceIngestReceipt,
OrderRequestEvidenceIngestRequest, OrderRevisionDecisionEnqueueRequest,
OrderRevisionDecisionPlan, OrderRevisionDecisionPrepareRequest, OrderRevisionDecisionReceipt,
OrderRevisionProposalEnqueueRequest, OrderRevisionProposalPlan,
- OrderRevisionProposalPrepareRequest, OrderRevisionProposalReceipt, OrderSettlementStateKind,
- OrderStatusEligibility, OrderStatusEvidenceSummary, OrderStatusKind, OrderStatusNextActionKind,
- OrderStatusReceipt, OrderStatusRequest, OrderSubmitEnqueueRequest, OrderSubmitPlan,
- OrderSubmitPrepareRequest, OrderSubmitReceipt, OrderWorkflowEnqueueReceipt,
- OrderWorkflowIdempotencyReceipt, OrderWorkflowKind, OrderWorkflowPlan,
- OrderWorkflowRetryAdvice, SdkOrderStatusIssue, SdkOrderStatusIssueKind, SdkOrderStatusSource,
+ OrderRevisionProposalPrepareRequest, OrderRevisionProposalReceipt, OrderStatusEligibility,
+ OrderStatusEvidenceSummary, OrderStatusKind, OrderStatusNextActionKind, OrderStatusReceipt,
+ OrderStatusRequest, OrderSubmitEnqueueRequest, OrderSubmitPlan, OrderSubmitPrepareRequest,
+ OrderSubmitReceipt, OrderWorkflowEnqueueReceipt, OrderWorkflowIdempotencyReceipt,
+ OrderWorkflowKind, OrderWorkflowPlan, OrderWorkflowRetryAdvice, SdkOrderStatusIssue,
+ SdkOrderStatusIssueKind, SdkOrderStatusSource,
};
#[cfg(feature = "runtime")]
pub use crate::product_clients::{FarmsClient, ListingsClient, OrdersClient, SyncClient};
diff --git a/crates/sdk/src/order.rs b/crates/sdk/src/order.rs
@@ -33,20 +33,10 @@ pub struct RadrootsOrderRevisionDecisionDraft {
}
#[derive(Debug, Clone)]
-pub struct RadrootsOrderFulfillmentUpdateDraft {
- parts: WireEventParts,
-}
-
-#[derive(Debug, Clone)]
pub struct RadrootsOrderCancellationDraft {
parts: WireEventParts,
}
-#[derive(Debug, Clone)]
-pub struct RadrootsOrderReceiptDraft {
- parts: WireEventParts,
-}
-
impl RadrootsOrderRequestDraft {
pub fn as_wire_parts(&self) -> &WireEventParts {
&self.parts
@@ -87,16 +77,6 @@ impl RadrootsOrderRevisionDecisionDraft {
}
}
-impl RadrootsOrderFulfillmentUpdateDraft {
- pub fn as_wire_parts(&self) -> &WireEventParts {
- &self.parts
- }
-
- pub fn into_wire_parts(self) -> WireEventParts {
- self.parts
- }
-}
-
impl RadrootsOrderCancellationDraft {
pub fn as_wire_parts(&self) -> &WireEventParts {
&self.parts
@@ -107,16 +87,6 @@ impl RadrootsOrderCancellationDraft {
}
}
-impl RadrootsOrderReceiptDraft {
- pub fn as_wire_parts(&self) -> &WireEventParts {
- &self.parts
- }
-
- pub fn into_wire_parts(self) -> WireEventParts {
- self.parts
- }
-}
-
#[cfg(feature = "serde_json")]
pub fn build_order_request_draft(
listing_event: &RadrootsNostrEventPtr,
@@ -173,21 +143,6 @@ pub fn build_order_revision_decision_draft(
}
#[cfg(feature = "serde_json")]
-pub fn build_fulfillment_update_draft(
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &RadrootsOrderFulfillmentUpdate,
-) -> Result<RadrootsOrderFulfillmentUpdateDraft, EventEncodeError> {
- Ok(RadrootsOrderFulfillmentUpdateDraft {
- parts: radroots_events_codec::order::order_fulfillment_update_event_build(
- root_event_id,
- prev_event_id,
- payload,
- )?,
- })
-}
-
-#[cfg(feature = "serde_json")]
pub fn build_order_cancellation_draft(
root_event_id: &RadrootsEventId,
prev_event_id: &RadrootsEventId,
@@ -203,21 +158,6 @@ pub fn build_order_cancellation_draft(
}
#[cfg(feature = "serde_json")]
-pub fn build_buyer_receipt_draft(
- root_event_id: &RadrootsEventId,
- prev_event_id: &RadrootsEventId,
- payload: &RadrootsOrderReceipt,
-) -> Result<RadrootsOrderReceiptDraft, EventEncodeError> {
- Ok(RadrootsOrderReceiptDraft {
- parts: radroots_events_codec::order::order_receipt_event_build(
- root_event_id,
- prev_event_id,
- payload,
- )?,
- })
-}
-
-#[cfg(feature = "serde_json")]
pub fn parse_order_request(
event: &RadrootsNostrEvent,
) -> Result<RadrootsOrderEnvelope<RadrootsOrderRequest>, RadrootsOrderEnvelopeParseError> {
@@ -246,14 +186,6 @@ pub fn parse_order_revision_decision(
}
#[cfg(feature = "serde_json")]
-pub fn parse_fulfillment_update(
- event: &RadrootsNostrEvent,
-) -> Result<RadrootsOrderEnvelope<RadrootsOrderFulfillmentUpdate>, RadrootsOrderEnvelopeParseError>
-{
- radroots_events_codec::order::order_fulfillment_update_from_event(event)
-}
-
-#[cfg(feature = "serde_json")]
pub fn parse_order_cancellation(
event: &RadrootsNostrEvent,
) -> Result<RadrootsOrderEnvelope<RadrootsOrderCancellation>, RadrootsOrderEnvelopeParseError> {
@@ -261,13 +193,6 @@ pub fn parse_order_cancellation(
}
#[cfg(feature = "serde_json")]
-pub fn parse_buyer_receipt(
- event: &RadrootsNostrEvent,
-) -> Result<RadrootsOrderEnvelope<RadrootsOrderReceipt>, RadrootsOrderEnvelopeParseError> {
- radroots_events_codec::order::order_receipt_from_event(event)
-}
-
-#[cfg(feature = "serde_json")]
pub fn parse_listing_address(
listing_addr: &str,
) -> Result<RadrootsListingAddress, RadrootsIdParseError> {
diff --git a/crates/sdk/src/orders_runtime.rs b/crates/sdk/src/orders_runtime.rs
@@ -17,31 +17,27 @@ use radroots_events::{
draft::RadrootsFrozenEventDraft,
ids::{RadrootsEventId, RadrootsListingAddress, RadrootsOrderId, RadrootsPublicKey},
kinds::{
- KIND_ORDER_CANCELLATION, KIND_ORDER_DECISION, KIND_ORDER_FULFILLMENT_UPDATE,
- KIND_ORDER_PAYMENT_RECORD, KIND_ORDER_RECEIPT, KIND_ORDER_REQUEST,
- KIND_ORDER_REVISION_DECISION, KIND_ORDER_REVISION_PROPOSAL, KIND_ORDER_SETTLEMENT_DECISION,
+ KIND_ORDER_CANCELLATION, KIND_ORDER_DECISION, KIND_ORDER_REQUEST,
+ KIND_ORDER_REVISION_DECISION, KIND_ORDER_REVISION_PROPOSAL,
},
order::{
- RadrootsOrderCancellation, RadrootsOrderDecision, RadrootsOrderFulfillmentState,
- RadrootsOrderFulfillmentUpdate, RadrootsOrderReceipt, RadrootsOrderRequest,
- RadrootsOrderRevisionDecision, RadrootsOrderRevisionProposal,
+ RadrootsOrderCancellation, RadrootsOrderDecision, RadrootsOrderEconomics,
+ RadrootsOrderRequest, RadrootsOrderRevisionDecision, RadrootsOrderRevisionProposal,
},
};
#[cfg(feature = "runtime")]
use radroots_events_codec::order::{
- order_cancellation_from_event, order_decision_from_event, order_fulfillment_update_from_event,
- order_payment_record_from_event, order_receipt_from_event, order_request_from_event,
+ order_cancellation_from_event, order_decision_from_event, order_request_from_event,
order_revision_decision_from_event, order_revision_proposal_from_event,
- order_settlement_decision_from_event,
};
#[cfg(feature = "runtime")]
use radroots_events_codec::wire::{WireEventParts, to_frozen_draft};
#[cfg(feature = "runtime")]
use radroots_trade::order::{
- RadrootsOrderCanonicalizationError, RadrootsOrderIssue, RadrootsOrderPaymentState,
- RadrootsOrderProjection, RadrootsOrderProjectionQueryResult, RadrootsOrderSettlementState,
- RadrootsOrderStatus, RadrootsOrderStoreQueryError, canonicalize_order_decision_for_signer,
- canonicalize_order_request_for_signer, order_projection_query_for_order_id,
+ RadrootsOrderCanonicalizationError, RadrootsOrderIssue, RadrootsOrderProjection,
+ RadrootsOrderProjectionQueryResult, RadrootsOrderStatus, RadrootsOrderStoreQueryError,
+ canonicalize_order_decision_for_signer, canonicalize_order_request_for_signer,
+ order_projection_query_for_order_id,
};
#[cfg(feature = "runtime")]
use serde::ser::SerializeStruct;
@@ -60,10 +56,6 @@ pub const ORDER_REVISION_PROPOSAL_OPERATION_KIND: &str = "order.revision.proposa
pub const ORDER_REVISION_DECISION_OPERATION_KIND: &str = "order.revision.decision.v1";
#[cfg(feature = "runtime")]
pub const ORDER_CANCELLATION_OPERATION_KIND: &str = "order.cancellation.v1";
-#[cfg(feature = "runtime")]
-pub const ORDER_FULFILLMENT_UPDATE_OPERATION_KIND: &str = "order.fulfillment.update.v1";
-#[cfg(feature = "runtime")]
-pub const ORDER_RECEIPT_RECORD_OPERATION_KIND: &str = "order.receipt.record.v1";
#[cfg(feature = "runtime")]
const ORDER_REQUEST_CONTRACT_ID: &str = "radroots.order.request.v1";
@@ -75,10 +67,6 @@ const ORDER_REVISION_PROPOSAL_CONTRACT_ID: &str = "radroots.order.revision_propo
const ORDER_REVISION_DECISION_CONTRACT_ID: &str = "radroots.order.revision_decision.v1";
#[cfg(feature = "runtime")]
const ORDER_CANCELLATION_CONTRACT_ID: &str = "radroots.order.cancellation.v1";
-#[cfg(feature = "runtime")]
-const ORDER_FULFILLMENT_UPDATE_CONTRACT_ID: &str = "radroots.order.fulfillment_update.v1";
-#[cfg(feature = "runtime")]
-const ORDER_RECEIPT_CONTRACT_ID: &str = "radroots.order.receipt.v1";
#[cfg(feature = "runtime")]
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
@@ -90,8 +78,6 @@ pub enum OrderWorkflowKind {
RevisionProposal,
RevisionDecision,
Cancellation,
- FulfillmentUpdate,
- ReceiptRecord,
}
#[cfg(feature = "runtime")]
@@ -103,8 +89,6 @@ impl OrderWorkflowKind {
Self::RevisionProposal => ORDER_REVISION_PROPOSAL_OPERATION_KIND,
Self::RevisionDecision => ORDER_REVISION_DECISION_OPERATION_KIND,
Self::Cancellation => ORDER_CANCELLATION_OPERATION_KIND,
- Self::FulfillmentUpdate => ORDER_FULFILLMENT_UPDATE_OPERATION_KIND,
- Self::ReceiptRecord => ORDER_RECEIPT_RECORD_OPERATION_KIND,
}
}
@@ -115,8 +99,6 @@ impl OrderWorkflowKind {
Self::RevisionProposal => ORDER_REVISION_PROPOSAL_CONTRACT_ID,
Self::RevisionDecision => ORDER_REVISION_DECISION_CONTRACT_ID,
Self::Cancellation => ORDER_CANCELLATION_CONTRACT_ID,
- Self::FulfillmentUpdate => ORDER_FULFILLMENT_UPDATE_CONTRACT_ID,
- Self::ReceiptRecord => ORDER_RECEIPT_CONTRACT_ID,
}
}
}
@@ -1070,555 +1052,159 @@ pub struct OrderCancellationReceipt {
}
#[cfg(feature = "runtime")]
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
#[non_exhaustive]
-pub struct OrderFulfillmentUpdatePrepareRequest {
- pub actor: RadrootsActorContext,
- pub root_event: RadrootsNostrEventPtr,
- pub previous_event: RadrootsNostrEventPtr,
- pub fulfillment: RadrootsOrderFulfillmentUpdate,
- pub created_at: Option<RadrootsSdkTimestamp>,
-}
-
-#[cfg(feature = "runtime")]
-impl serde::Serialize for OrderFulfillmentUpdatePrepareRequest {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: serde::Serializer,
- {
- let mut state = serializer.serialize_struct("OrderFulfillmentUpdatePrepareRequest", 5)?;
- state.serialize_field("actor", &SdkActorContextJson(&self.actor))?;
- state.serialize_field("root_event", &self.root_event)?;
- state.serialize_field("previous_event", &self.previous_event)?;
- state.serialize_field("fulfillment", &self.fulfillment)?;
- state.serialize_field("created_at", &self.created_at)?;
- state.end()
- }
+pub struct OrderStatusRequest {
+ pub order_id: RadrootsOrderId,
+ pub limit: u32,
}
#[cfg(feature = "runtime")]
-impl OrderFulfillmentUpdatePrepareRequest {
- pub fn new(
- actor: RadrootsActorContext,
- root_event: RadrootsNostrEventPtr,
- previous_event: RadrootsNostrEventPtr,
- fulfillment: RadrootsOrderFulfillmentUpdate,
- ) -> Self {
+impl OrderStatusRequest {
+ pub fn new(order_id: RadrootsOrderId) -> Self {
Self {
- actor,
- root_event,
- previous_event,
- fulfillment,
- created_at: None,
+ order_id,
+ limit: ORDER_STATUS_DEFAULT_LIMIT,
}
}
- pub fn with_created_at(mut self, created_at: RadrootsSdkTimestamp) -> Self {
- self.created_at = Some(created_at);
+ pub fn parse(order_id: &str) -> Result<Self, RadrootsSdkError> {
+ RadrootsOrderId::parse(order_id)
+ .map(Self::new)
+ .map_err(|error| RadrootsSdkError::invalid_order_id(order_id, error.to_string()))
+ }
+
+ pub fn with_limit(mut self, limit: u32) -> Self {
+ self.limit = limit;
self
}
+
+ fn validate(&self) -> Result<(), RadrootsSdkError> {
+ if self.limit == 0 || self.limit > ORDER_STATUS_MAX_LIMIT {
+ return Err(RadrootsSdkError::order_status_limit_invalid(
+ self.limit,
+ 1,
+ ORDER_STATUS_MAX_LIMIT,
+ ));
+ }
+ Ok(())
+ }
}
#[cfg(feature = "runtime")]
-#[derive(Clone, Debug)]
-#[non_exhaustive]
-pub struct OrderFulfillmentUpdateEnqueueRequest {
- pub actor: RadrootsActorContext,
- pub root_event: RadrootsNostrEventPtr,
- pub previous_event: RadrootsNostrEventPtr,
- pub fulfillment: RadrootsOrderFulfillmentUpdate,
- pub target_relays: SdkRelayTargetPolicy,
- pub idempotency_key: Option<SdkIdempotencyKey>,
- pub created_at: Option<RadrootsSdkTimestamp>,
+#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
+pub struct OrderStatusReceipt {
+ pub order_id: RadrootsOrderId,
+ pub source: SdkOrderStatusSource,
+ pub found: bool,
+ pub event_count: usize,
+ pub limit_applied: u32,
+ pub status: OrderStatusKind,
+ pub lifecycle_terminal: bool,
+ pub listing_addr: Option<RadrootsListingAddress>,
+ pub buyer_pubkey: Option<RadrootsPublicKey>,
+ pub seller_pubkey: Option<RadrootsPublicKey>,
+ pub economics: Option<RadrootsOrderEconomics>,
+ pub evidence: OrderStatusEvidenceSummary,
+ pub eligibility: OrderStatusEligibility,
+ pub next_action: OrderStatusNextActionKind,
+ pub event_ids: Vec<RadrootsEventId>,
+ pub request_event_id: Option<RadrootsEventId>,
+ pub decision_event_id: Option<RadrootsEventId>,
+ pub agreement_event_id: Option<RadrootsEventId>,
+ pub pending_revision_event_id: Option<RadrootsEventId>,
+ pub cancellation_event_id: Option<RadrootsEventId>,
+ pub last_event_id: Option<RadrootsEventId>,
+ pub issues: Vec<SdkOrderStatusIssue>,
}
#[cfg(feature = "runtime")]
-impl serde::Serialize for OrderFulfillmentUpdateEnqueueRequest {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: serde::Serializer,
- {
- let mut state = serializer.serialize_struct("OrderFulfillmentUpdateEnqueueRequest", 7)?;
- state.serialize_field("actor", &SdkActorContextJson(&self.actor))?;
- state.serialize_field("root_event", &self.root_event)?;
- state.serialize_field("previous_event", &self.previous_event)?;
- state.serialize_field("fulfillment", &self.fulfillment)?;
- state.serialize_field("target_relays", &self.target_relays)?;
- state.serialize_field("idempotency_key", &self.idempotency_key)?;
- state.serialize_field("created_at", &self.created_at)?;
- state.end()
- }
+#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
+pub struct OrderStatusEvidenceSummary {
+ pub event_count: usize,
+ pub limit_applied: u32,
+ pub has_request: bool,
+ pub has_decision: bool,
+ pub has_agreement: bool,
+ pub has_pending_revision: bool,
+ pub has_cancellation: bool,
+ pub has_issues: bool,
}
#[cfg(feature = "runtime")]
-impl OrderFulfillmentUpdateEnqueueRequest {
- pub fn new(
- actor: RadrootsActorContext,
- root_event: RadrootsNostrEventPtr,
- previous_event: RadrootsNostrEventPtr,
- fulfillment: RadrootsOrderFulfillmentUpdate,
- target_relays: SdkRelayTargetPolicy,
- ) -> Self {
- Self {
- actor,
- root_event,
- previous_event,
- fulfillment,
- target_relays,
- idempotency_key: None,
- created_at: None,
- }
- }
-
- pub fn try_with_target_relays<I, S>(
- mut self,
- target_relays: I,
- policy: SdkRelayUrlPolicy,
- ) -> Result<Self, RadrootsSdkError>
- where
- I: IntoIterator<Item = S>,
- S: AsRef<str>,
- {
- self.target_relays = SdkRelayTargetPolicy::try_explicit(target_relays, policy)?;
- Ok(self)
- }
-
- pub fn with_idempotency_key(mut self, idempotency_key: SdkIdempotencyKey) -> Self {
- self.idempotency_key = Some(idempotency_key.into());
- self
- }
+#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
+pub struct OrderStatusEligibility {
+ pub can_decide: bool,
+ pub can_propose_revision: bool,
+ pub can_decide_revision: bool,
+ pub can_cancel: bool,
+}
- pub fn try_with_idempotency_key(
- mut self,
- idempotency_key: impl AsRef<str>,
- ) -> Result<Self, RadrootsSdkError> {
- self.idempotency_key = Some(SdkIdempotencyKey::new(idempotency_key)?);
- Ok(self)
- }
+#[cfg(feature = "runtime")]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
+#[serde(rename_all = "snake_case")]
+#[non_exhaustive]
+pub enum OrderStatusNextActionKind {
+ NoLocalOrder,
+ InspectEvidenceIssues,
+ AwaitSellerDecision,
+ DecideRevision,
+ Terminal,
+}
- pub fn with_created_at(mut self, created_at: RadrootsSdkTimestamp) -> Self {
- self.created_at = Some(created_at);
- self
- }
+#[cfg(feature = "runtime")]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
+#[serde(rename_all = "snake_case")]
+#[non_exhaustive]
+pub enum SdkOrderStatusSource {
+ LocalEventStore,
}
#[cfg(feature = "runtime")]
-#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
-pub struct OrderFulfillmentUpdatePlan {
- pub workflow: OrderWorkflowPlan,
- pub order_id: RadrootsOrderId,
- pub listing_addr: RadrootsListingAddress,
- pub buyer_pubkey: RadrootsPublicKey,
- pub seller_pubkey: RadrootsPublicKey,
- pub root_event_id: RadrootsEventId,
- pub previous_event_id: RadrootsEventId,
- pub expected_event_id: RadrootsEventId,
- pub frozen_draft: RadrootsFrozenEventDraft,
- pub created_at: RadrootsSdkTimestamp,
+#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
+#[serde(rename_all = "snake_case")]
+#[non_exhaustive]
+pub enum OrderStatusKind {
+ Missing,
+ Requested,
+ Accepted,
+ Declined,
+ Cancelled,
+ Invalid,
}
#[cfg(feature = "runtime")]
-#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
-pub struct OrderFulfillmentUpdateReceipt {
- pub workflow: OrderWorkflowEnqueueReceipt,
- pub order_id: RadrootsOrderId,
- pub listing_addr: RadrootsListingAddress,
- pub buyer_pubkey: RadrootsPublicKey,
- pub seller_pubkey: RadrootsPublicKey,
- pub root_event_id: RadrootsEventId,
- pub previous_event_id: RadrootsEventId,
- pub expected_event_id: RadrootsEventId,
- pub signed_event_id: RadrootsEventId,
- pub local_event_seq: i64,
- pub outbox_operation_id: i64,
- pub outbox_event_id: i64,
- pub state: SdkMutationState,
- pub idempotency_digest_prefix: Option<String>,
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct SdkOrderStatusIssue {
+ pub kind: SdkOrderStatusIssueKind,
+ pub event_ids: Vec<RadrootsEventId>,
}
#[cfg(feature = "runtime")]
-#[derive(Clone, Debug)]
-#[non_exhaustive]
-pub struct OrderReceiptRecordPrepareRequest {
- pub actor: RadrootsActorContext,
- pub root_event: RadrootsNostrEventPtr,
- pub previous_event: RadrootsNostrEventPtr,
- pub receipt: RadrootsOrderReceipt,
- pub created_at: Option<RadrootsSdkTimestamp>,
+impl SdkOrderStatusIssue {
+ fn new(kind: SdkOrderStatusIssueKind, event_ids: Vec<RadrootsEventId>) -> Self {
+ Self { kind, event_ids }
+ }
+
+ fn single(kind: SdkOrderStatusIssueKind, event_id: RadrootsEventId) -> Self {
+ Self::new(kind, vec![event_id])
+ }
+
+ pub fn code(&self) -> String {
+ self.kind.code()
+ }
}
#[cfg(feature = "runtime")]
-impl serde::Serialize for OrderReceiptRecordPrepareRequest {
+impl serde::Serialize for SdkOrderStatusIssue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
- let mut state = serializer.serialize_struct("OrderReceiptRecordPrepareRequest", 5)?;
- state.serialize_field("actor", &SdkActorContextJson(&self.actor))?;
- state.serialize_field("root_event", &self.root_event)?;
- state.serialize_field("previous_event", &self.previous_event)?;
- state.serialize_field("receipt", &self.receipt)?;
- state.serialize_field("created_at", &self.created_at)?;
- state.end()
- }
-}
-
-#[cfg(feature = "runtime")]
-impl OrderReceiptRecordPrepareRequest {
- pub fn new(
- actor: RadrootsActorContext,
- root_event: RadrootsNostrEventPtr,
- previous_event: RadrootsNostrEventPtr,
- receipt: RadrootsOrderReceipt,
- ) -> Self {
- Self {
- actor,
- root_event,
- previous_event,
- receipt,
- created_at: None,
- }
- }
-
- pub fn with_created_at(mut self, created_at: RadrootsSdkTimestamp) -> Self {
- self.created_at = Some(created_at);
- self
- }
-}
-
-#[cfg(feature = "runtime")]
-#[derive(Clone, Debug)]
-#[non_exhaustive]
-pub struct OrderReceiptRecordEnqueueRequest {
- pub actor: RadrootsActorContext,
- pub root_event: RadrootsNostrEventPtr,
- pub previous_event: RadrootsNostrEventPtr,
- pub receipt: RadrootsOrderReceipt,
- pub target_relays: SdkRelayTargetPolicy,
- pub idempotency_key: Option<SdkIdempotencyKey>,
- pub created_at: Option<RadrootsSdkTimestamp>,
-}
-
-#[cfg(feature = "runtime")]
-impl serde::Serialize for OrderReceiptRecordEnqueueRequest {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: serde::Serializer,
- {
- let mut state = serializer.serialize_struct("OrderReceiptRecordEnqueueRequest", 7)?;
- state.serialize_field("actor", &SdkActorContextJson(&self.actor))?;
- state.serialize_field("root_event", &self.root_event)?;
- state.serialize_field("previous_event", &self.previous_event)?;
- state.serialize_field("receipt", &self.receipt)?;
- state.serialize_field("target_relays", &self.target_relays)?;
- state.serialize_field("idempotency_key", &self.idempotency_key)?;
- state.serialize_field("created_at", &self.created_at)?;
- state.end()
- }
-}
-
-#[cfg(feature = "runtime")]
-impl OrderReceiptRecordEnqueueRequest {
- pub fn new(
- actor: RadrootsActorContext,
- root_event: RadrootsNostrEventPtr,
- previous_event: RadrootsNostrEventPtr,
- receipt: RadrootsOrderReceipt,
- target_relays: SdkRelayTargetPolicy,
- ) -> Self {
- Self {
- actor,
- root_event,
- previous_event,
- receipt,
- target_relays,
- idempotency_key: None,
- created_at: None,
- }
- }
-
- pub fn try_with_target_relays<I, S>(
- mut self,
- target_relays: I,
- policy: SdkRelayUrlPolicy,
- ) -> Result<Self, RadrootsSdkError>
- where
- I: IntoIterator<Item = S>,
- S: AsRef<str>,
- {
- self.target_relays = SdkRelayTargetPolicy::try_explicit(target_relays, policy)?;
- Ok(self)
- }
-
- pub fn with_idempotency_key(mut self, idempotency_key: SdkIdempotencyKey) -> Self {
- self.idempotency_key = Some(idempotency_key.into());
- self
- }
-
- pub fn try_with_idempotency_key(
- mut self,
- idempotency_key: impl AsRef<str>,
- ) -> Result<Self, RadrootsSdkError> {
- self.idempotency_key = Some(SdkIdempotencyKey::new(idempotency_key)?);
- Ok(self)
- }
-
- pub fn with_created_at(mut self, created_at: RadrootsSdkTimestamp) -> Self {
- self.created_at = Some(created_at);
- self
- }
-}
-
-#[cfg(feature = "runtime")]
-#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
-pub struct OrderReceiptRecordPlan {
- pub workflow: OrderWorkflowPlan,
- pub order_id: RadrootsOrderId,
- pub listing_addr: RadrootsListingAddress,
- pub buyer_pubkey: RadrootsPublicKey,
- pub seller_pubkey: RadrootsPublicKey,
- pub root_event_id: RadrootsEventId,
- pub previous_event_id: RadrootsEventId,
- pub expected_event_id: RadrootsEventId,
- pub frozen_draft: RadrootsFrozenEventDraft,
- pub created_at: RadrootsSdkTimestamp,
-}
-
-#[cfg(feature = "runtime")]
-#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
-pub struct OrderReceiptRecordReceipt {
- pub workflow: OrderWorkflowEnqueueReceipt,
- pub order_id: RadrootsOrderId,
- pub listing_addr: RadrootsListingAddress,
- pub buyer_pubkey: RadrootsPublicKey,
- pub seller_pubkey: RadrootsPublicKey,
- pub root_event_id: RadrootsEventId,
- pub previous_event_id: RadrootsEventId,
- pub expected_event_id: RadrootsEventId,
- pub signed_event_id: RadrootsEventId,
- pub local_event_seq: i64,
- pub outbox_operation_id: i64,
- pub outbox_event_id: i64,
- pub state: SdkMutationState,
- pub idempotency_digest_prefix: Option<String>,
-}
-
-#[cfg(feature = "runtime")]
-#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
-#[non_exhaustive]
-pub struct OrderStatusRequest {
- pub order_id: RadrootsOrderId,
- pub limit: u32,
-}
-
-#[cfg(feature = "runtime")]
-impl OrderStatusRequest {
- pub fn new(order_id: RadrootsOrderId) -> Self {
- Self {
- order_id,
- limit: ORDER_STATUS_DEFAULT_LIMIT,
- }
- }
-
- pub fn parse(order_id: &str) -> Result<Self, RadrootsSdkError> {
- RadrootsOrderId::parse(order_id)
- .map(Self::new)
- .map_err(|error| RadrootsSdkError::invalid_order_id(order_id, error.to_string()))
- }
-
- pub fn with_limit(mut self, limit: u32) -> Self {
- self.limit = limit;
- self
- }
-
- fn validate(&self) -> Result<(), RadrootsSdkError> {
- if self.limit == 0 || self.limit > ORDER_STATUS_MAX_LIMIT {
- return Err(RadrootsSdkError::order_status_limit_invalid(
- self.limit,
- 1,
- ORDER_STATUS_MAX_LIMIT,
- ));
- }
- Ok(())
- }
-}
-
-#[cfg(feature = "runtime")]
-#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
-pub struct OrderStatusReceipt {
- pub order_id: RadrootsOrderId,
- pub source: SdkOrderStatusSource,
- pub found: bool,
- pub event_count: usize,
- pub limit_applied: u32,
- pub status: OrderStatusKind,
- pub fulfillment_status: Option<OrderFulfillmentStatusKind>,
- pub payment_state: OrderPaymentStateKind,
- pub settlement_state: OrderSettlementStateKind,
- pub lifecycle_terminal: bool,
- pub evidence: OrderStatusEvidenceSummary,
- pub eligibility: OrderStatusEligibility,
- pub payment_handoff: OrderPaymentHandoffKind,
- pub next_action: OrderStatusNextActionKind,
- pub event_ids: Vec<RadrootsEventId>,
- pub request_event_id: Option<RadrootsEventId>,
- pub decision_event_id: Option<RadrootsEventId>,
- pub agreement_event_id: Option<RadrootsEventId>,
- pub pending_revision_event_id: Option<RadrootsEventId>,
- pub fulfillment_event_id: Option<RadrootsEventId>,
- pub cancellation_event_id: Option<RadrootsEventId>,
- pub receipt_event_id: Option<RadrootsEventId>,
- pub last_event_id: Option<RadrootsEventId>,
- pub issues: Vec<SdkOrderStatusIssue>,
-}
-
-#[cfg(feature = "runtime")]
-#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
-pub struct OrderStatusEvidenceSummary {
- pub event_count: usize,
- pub limit_applied: u32,
- pub has_request: bool,
- pub has_decision: bool,
- pub has_agreement: bool,
- pub has_pending_revision: bool,
- pub has_fulfillment: bool,
- pub has_cancellation: bool,
- pub has_receipt: bool,
- pub has_issues: bool,
-}
-
-#[cfg(feature = "runtime")]
-#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
-pub struct OrderStatusEligibility {
- pub can_decide: bool,
- pub can_propose_revision: bool,
- pub can_decide_revision: bool,
- pub can_cancel: bool,
- pub can_update_fulfillment: bool,
- pub can_record_receipt: bool,
-}
-
-#[cfg(feature = "runtime")]
-#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
-#[serde(rename_all = "snake_case")]
-#[non_exhaustive]
-pub enum OrderPaymentHandoffKind {
- NotReady,
- NotRequired,
- InPersonOrOffPlatformPending,
- InPersonOrOffPlatformRecorded,
- InPersonOrOffPlatformSettled,
- Rejected,
- Invalid,
-}
-
-#[cfg(feature = "runtime")]
-#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
-#[serde(rename_all = "snake_case")]
-#[non_exhaustive]
-pub enum OrderStatusNextActionKind {
- NoLocalOrder,
- InspectEvidenceIssues,
- AwaitSellerDecision,
- ArrangeInPersonOrOffPlatformPayment,
- DecideRevision,
- FulfillOrder,
- RecordReceipt,
- Terminal,
-}
-
-#[cfg(feature = "runtime")]
-#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
-#[serde(rename_all = "snake_case")]
-#[non_exhaustive]
-pub enum SdkOrderStatusSource {
- LocalEventStore,
-}
-
-#[cfg(feature = "runtime")]
-#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
-#[serde(rename_all = "snake_case")]
-#[non_exhaustive]
-pub enum OrderStatusKind {
- Missing,
- Requested,
- Accepted,
- Declined,
- Cancelled,
- Completed,
- Disputed,
- Invalid,
-}
-
-#[cfg(feature = "runtime")]
-#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
-#[serde(rename_all = "snake_case")]
-#[non_exhaustive]
-pub enum OrderFulfillmentStatusKind {
- AcceptedNotFulfilled,
- Preparing,
- ReadyForPickup,
- OutForDelivery,
- Delivered,
- SellerCancelled,
-}
-
-#[cfg(feature = "runtime")]
-#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
-#[serde(rename_all = "snake_case")]
-#[non_exhaustive]
-pub enum OrderPaymentStateKind {
- NotRecorded,
- Recorded,
- Settled,
- Rejected,
- Invalid,
-}
-
-#[cfg(feature = "runtime")]
-#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
-#[serde(rename_all = "snake_case")]
-#[non_exhaustive]
-pub enum OrderSettlementStateKind {
- NotRequired,
- Pending,
- Accepted,
- Rejected,
- Invalid,
-}
-
-#[cfg(feature = "runtime")]
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct SdkOrderStatusIssue {
- pub kind: SdkOrderStatusIssueKind,
- pub event_ids: Vec<RadrootsEventId>,
-}
-
-#[cfg(feature = "runtime")]
-impl SdkOrderStatusIssue {
- fn new(kind: SdkOrderStatusIssueKind, event_ids: Vec<RadrootsEventId>) -> Self {
- Self { kind, event_ids }
- }
-
- fn single(kind: SdkOrderStatusIssueKind, event_id: RadrootsEventId) -> Self {
- Self::new(kind, vec![event_id])
- }
-
- pub fn code(&self) -> String {
- self.kind.code()
- }
-}
-
-#[cfg(feature = "runtime")]
-impl serde::Serialize for SdkOrderStatusIssue {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: serde::Serializer,
- {
- let mut state = serializer.serialize_struct("SdkOrderStatusIssue", 3)?;
- state.serialize_field("code", &self.code())?;
- state.serialize_field("kind", &self.kind)?;
- state.serialize_field("event_ids", &self.event_ids)?;
+ let mut state = serializer.serialize_struct("SdkOrderStatusIssue", 3)?;
+ state.serialize_field("code", &self.code())?;
+ state.serialize_field("kind", &self.kind)?;
+ state.serialize_field("event_ids", &self.event_ids)?;
state.end()
}
}
@@ -1649,7 +1235,6 @@ pub enum SdkOrderStatusIssueKind {
DecisionInventoryCommitmentMismatch,
DecisionMissingReason,
ConflictingDecisions,
- RevisionProposalWithoutAcceptedDecision,
RevisionProposalPayloadInvalid,
RevisionProposalOrderIdMismatch,
RevisionProposalAuthorMismatch,
@@ -1672,20 +1257,6 @@ pub enum SdkOrderStatusIssueKind {
RevisionDecisionRootMismatch,
RevisionDecisionPreviousMismatch,
RevisionDecisionRevisionIdMismatch,
- FulfillmentWithoutAcceptedDecision,
- FulfillmentPayloadInvalid,
- FulfillmentOrderIdMismatch,
- FulfillmentAuthorMismatch,
- FulfillmentCounterpartyMismatch,
- FulfillmentBuyerMismatch,
- FulfillmentSellerMismatch,
- FulfillmentListingAddressInvalid,
- FulfillmentListingMismatch,
- FulfillmentRootMismatch,
- FulfillmentPreviousMismatch,
- FulfillmentStatusNotPublishable,
- FulfillmentUnsupportedTransition,
- ForkedFulfillments,
CancellationWithoutCancellableOrder,
CancellationPayloadInvalid,
CancellationOrderIdMismatch,
@@ -1697,57 +1268,6 @@ pub enum SdkOrderStatusIssueKind {
CancellationListingMismatch,
CancellationRootMismatch,
CancellationPreviousMismatch,
- CancellationAfterFulfillment,
- ReceiptWithoutEligibleFulfillment,
- ReceiptPayloadInvalid,
- ReceiptOrderIdMismatch,
- ReceiptAuthorMismatch,
- ReceiptCounterpartyMismatch,
- ReceiptBuyerMismatch,
- ReceiptSellerMismatch,
- ReceiptListingAddressInvalid,
- ReceiptListingMismatch,
- ReceiptRootMismatch,
- ReceiptPreviousMismatch,
- PaymentWithoutAcceptedAgreement,
- PaymentPayloadInvalid,
- PaymentOrderIdMismatch,
- PaymentAuthorMismatch,
- PaymentCounterpartyMismatch,
- PaymentBuyerMismatch,
- PaymentSellerMismatch,
- PaymentListingAddressInvalid,
- PaymentListingMismatch,
- PaymentRootMismatch,
- PaymentPreviousMismatch,
- PaymentAgreementMismatch,
- PaymentQuoteMismatch,
- PaymentQuoteVersionMismatch,
- PaymentEconomicsDigestMismatch,
- PaymentAmountMismatch,
- PaymentCurrencyMismatch,
- PaymentAfterCancellation,
- RevisionAfterPayment,
- DuplicatePayments,
- SettlementWithoutValidPayment,
- SettlementPayloadInvalid,
- SettlementOrderIdMismatch,
- SettlementAuthorMismatch,
- SettlementCounterpartyMismatch,
- SettlementBuyerMismatch,
- SettlementSellerMismatch,
- SettlementListingAddressInvalid,
- SettlementListingMismatch,
- SettlementRootMismatch,
- SettlementPreviousMismatch,
- SettlementPaymentEventMismatch,
- SettlementAgreementMismatch,
- SettlementQuoteMismatch,
- SettlementQuoteVersionMismatch,
- SettlementEconomicsDigestMismatch,
- SettlementAmountMismatch,
- SettlementCurrencyMismatch,
- DuplicateSettlements,
ForkedLifecycle,
}
@@ -1953,206 +1473,7 @@ impl<'sdk> OrdersClient<'sdk> {
let enqueue = enqueue_signed_workflow(
self.sdk,
SdkWorkflowEnqueueRequest {
- operation_kind: OrderWorkflowKind::Decision.operation_kind(),
- actor,
- frozen_draft: &plan.frozen_draft,
- target_relays,
- idempotency_key,
- },
- signer,
- )
- .await?;
- Ok(OrderDecisionReceipt {
- workflow: order_workflow_enqueue_receipt(
- OrderWorkflowKind::Decision,
- plan.expected_event_id.clone(),
- &enqueue,
- ),
- order_id: plan.order_id,
- listing_addr: plan.listing_addr,
- buyer_pubkey: plan.buyer_pubkey,
- seller_pubkey: plan.seller_pubkey,
- request_event_id: plan.request_event_id,
- expected_event_id: plan.expected_event_id,
- signed_event_id: enqueue.signed_event_id,
- local_event_seq: enqueue.local_event_seq,
- outbox_operation_id: enqueue.outbox_operation_id,
- outbox_event_id: enqueue.outbox_event_id,
- state: enqueue.state.into(),
- idempotency_digest_prefix: Some(enqueue.idempotency_digest_prefix),
- })
- }
-
- pub fn prepare_revision_proposal(
- &self,
- request: OrderRevisionProposalPrepareRequest,
- ) -> Result<OrderRevisionProposalPlan, RadrootsSdkError> {
- let created_at = self.resolved_created_at(request.created_at)?;
- order_revision_proposal_plan(
- &request.actor,
- request.root_event,
- request.previous_event,
- request.proposal,
- created_at,
- )
- }
-
- pub async fn enqueue_revision_proposal<S>(
- &self,
- request: OrderRevisionProposalEnqueueRequest,
- signer: &S,
- ) -> Result<OrderRevisionProposalReceipt, RadrootsSdkError>
- where
- S: RadrootsEventSigner + ?Sized,
- {
- let OrderRevisionProposalEnqueueRequest {
- actor,
- root_event,
- previous_event,
- proposal,
- target_relays,
- idempotency_key,
- created_at,
- } = request;
- let prepare_request = OrderRevisionProposalPrepareRequest {
- actor: actor.clone(),
- root_event,
- previous_event,
- proposal,
- created_at,
- };
- let plan = self.prepare_revision_proposal(prepare_request)?;
- self.enqueue_prepared_revision_proposal(
- &actor,
- plan,
- target_relays,
- idempotency_key,
- signer,
- )
- .await
- }
-
- pub async fn enqueue_prepared_revision_proposal<S>(
- &self,
- actor: &RadrootsActorContext,
- plan: OrderRevisionProposalPlan,
- target_relays: SdkRelayTargetPolicy,
- idempotency_key: Option<SdkIdempotencyKey>,
- signer: &S,
- ) -> Result<OrderRevisionProposalReceipt, RadrootsSdkError>
- where
- S: RadrootsEventSigner + ?Sized,
- {
- if !self
- .prepared_order_event_exists(&plan.expected_event_id)
- .await?
- {
- self.require_revision_proposal_preflight(&plan).await?;
- }
- let enqueue = enqueue_signed_workflow(
- self.sdk,
- SdkWorkflowEnqueueRequest {
- operation_kind: OrderWorkflowKind::RevisionProposal.operation_kind(),
- actor,
- frozen_draft: &plan.frozen_draft,
- target_relays,
- idempotency_key,
- },
- signer,
- )
- .await?;
- Ok(OrderRevisionProposalReceipt {
- workflow: order_workflow_enqueue_receipt(
- OrderWorkflowKind::RevisionProposal,
- plan.expected_event_id.clone(),
- &enqueue,
- ),
- order_id: plan.order_id,
- listing_addr: plan.listing_addr,
- buyer_pubkey: plan.buyer_pubkey,
- seller_pubkey: plan.seller_pubkey,
- root_event_id: plan.root_event_id,
- previous_event_id: plan.previous_event_id,
- expected_event_id: plan.expected_event_id,
- signed_event_id: enqueue.signed_event_id,
- local_event_seq: enqueue.local_event_seq,
- outbox_operation_id: enqueue.outbox_operation_id,
- outbox_event_id: enqueue.outbox_event_id,
- state: enqueue.state.into(),
- idempotency_digest_prefix: Some(enqueue.idempotency_digest_prefix),
- })
- }
-
- pub fn prepare_revision_decision(
- &self,
- request: OrderRevisionDecisionPrepareRequest,
- ) -> Result<OrderRevisionDecisionPlan, RadrootsSdkError> {
- let created_at = self.resolved_created_at(request.created_at)?;
- order_revision_decision_plan(
- &request.actor,
- request.root_event,
- request.previous_event,
- request.decision,
- created_at,
- )
- }
-
- pub async fn enqueue_revision_decision<S>(
- &self,
- request: OrderRevisionDecisionEnqueueRequest,
- signer: &S,
- ) -> Result<OrderRevisionDecisionReceipt, RadrootsSdkError>
- where
- S: RadrootsEventSigner + ?Sized,
- {
- let OrderRevisionDecisionEnqueueRequest {
- actor,
- root_event,
- previous_event,
- decision,
- target_relays,
- idempotency_key,
- created_at,
- } = request;
- let prepare_request = OrderRevisionDecisionPrepareRequest {
- actor: actor.clone(),
- root_event,
- previous_event,
- decision,
- created_at,
- };
- let plan = self.prepare_revision_decision(prepare_request)?;
- self.enqueue_prepared_revision_decision(
- &actor,
- plan,
- target_relays,
- idempotency_key,
- signer,
- )
- .await
- }
-
- pub async fn enqueue_prepared_revision_decision<S>(
- &self,
- actor: &RadrootsActorContext,
- plan: OrderRevisionDecisionPlan,
- target_relays: SdkRelayTargetPolicy,
- idempotency_key: Option<SdkIdempotencyKey>,
- signer: &S,
- ) -> Result<OrderRevisionDecisionReceipt, RadrootsSdkError>
- where
- S: RadrootsEventSigner + ?Sized,
- {
- if !self
- .prepared_order_event_exists(&plan.expected_event_id)
- .await?
- {
- self.require_revision_decision_preflight(&plan).await?;
- }
- let enqueue = enqueue_signed_workflow(
- self.sdk,
- SdkWorkflowEnqueueRequest {
- operation_kind: OrderWorkflowKind::RevisionDecision.operation_kind(),
+ operation_kind: OrderWorkflowKind::Decision.operation_kind(),
actor,
frozen_draft: &plan.frozen_draft,
target_relays,
@@ -2161,9 +1482,9 @@ impl<'sdk> OrdersClient<'sdk> {
signer,
)
.await?;
- Ok(OrderRevisionDecisionReceipt {
+ Ok(OrderDecisionReceipt {
workflow: order_workflow_enqueue_receipt(
- OrderWorkflowKind::RevisionDecision,
+ OrderWorkflowKind::Decision,
plan.expected_event_id.clone(),
&enqueue,
),
@@ -2171,8 +1492,7 @@ impl<'sdk> OrdersClient<'sdk> {
listing_addr: plan.listing_addr,
buyer_pubkey: plan.buyer_pubkey,
seller_pubkey: plan.seller_pubkey,
- root_event_id: plan.root_event_id,
- previous_event_id: plan.previous_event_id,
+ request_event_id: plan.request_event_id,
expected_event_id: plan.expected_event_id,
signed_event_id: enqueue.signed_event_id,
local_event_seq: enqueue.local_event_seq,
@@ -2183,57 +1503,63 @@ impl<'sdk> OrdersClient<'sdk> {
})
}
- pub fn prepare_cancellation(
+ pub fn prepare_revision_proposal(
&self,
- request: OrderCancellationPrepareRequest,
- ) -> Result<OrderCancellationPlan, RadrootsSdkError> {
+ request: OrderRevisionProposalPrepareRequest,
+ ) -> Result<OrderRevisionProposalPlan, RadrootsSdkError> {
let created_at = self.resolved_created_at(request.created_at)?;
- order_cancellation_plan(
+ order_revision_proposal_plan(
&request.actor,
request.root_event,
request.previous_event,
- request.cancellation,
+ request.proposal,
created_at,
)
}
- pub async fn enqueue_cancellation<S>(
+ pub async fn enqueue_revision_proposal<S>(
&self,
- request: OrderCancellationEnqueueRequest,
+ request: OrderRevisionProposalEnqueueRequest,
signer: &S,
- ) -> Result<OrderCancellationReceipt, RadrootsSdkError>
+ ) -> Result<OrderRevisionProposalReceipt, RadrootsSdkError>
where
S: RadrootsEventSigner + ?Sized,
{
- let OrderCancellationEnqueueRequest {
+ let OrderRevisionProposalEnqueueRequest {
actor,
root_event,
previous_event,
- cancellation,
+ proposal,
target_relays,
idempotency_key,
created_at,
} = request;
- let prepare_request = OrderCancellationPrepareRequest {
+ let prepare_request = OrderRevisionProposalPrepareRequest {
actor: actor.clone(),
root_event,
previous_event,
- cancellation,
+ proposal,
created_at,
};
- let plan = self.prepare_cancellation(prepare_request)?;
- self.enqueue_prepared_cancellation(&actor, plan, target_relays, idempotency_key, signer)
- .await
+ let plan = self.prepare_revision_proposal(prepare_request)?;
+ self.enqueue_prepared_revision_proposal(
+ &actor,
+ plan,
+ target_relays,
+ idempotency_key,
+ signer,
+ )
+ .await
}
- pub async fn enqueue_prepared_cancellation<S>(
+ pub async fn enqueue_prepared_revision_proposal<S>(
&self,
actor: &RadrootsActorContext,
- plan: OrderCancellationPlan,
+ plan: OrderRevisionProposalPlan,
target_relays: SdkRelayTargetPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
signer: &S,
- ) -> Result<OrderCancellationReceipt, RadrootsSdkError>
+ ) -> Result<OrderRevisionProposalReceipt, RadrootsSdkError>
where
S: RadrootsEventSigner + ?Sized,
{
@@ -2241,12 +1567,12 @@ impl<'sdk> OrdersClient<'sdk> {
.prepared_order_event_exists(&plan.expected_event_id)
.await?
{
- self.require_cancellation_preflight(&plan).await?;
+ self.require_revision_proposal_preflight(&plan).await?;
}
let enqueue = enqueue_signed_workflow(
self.sdk,
SdkWorkflowEnqueueRequest {
- operation_kind: OrderWorkflowKind::Cancellation.operation_kind(),
+ operation_kind: OrderWorkflowKind::RevisionProposal.operation_kind(),
actor,
frozen_draft: &plan.frozen_draft,
target_relays,
@@ -2255,9 +1581,9 @@ impl<'sdk> OrdersClient<'sdk> {
signer,
)
.await?;
- Ok(OrderCancellationReceipt {
+ Ok(OrderRevisionProposalReceipt {
workflow: order_workflow_enqueue_receipt(
- OrderWorkflowKind::Cancellation,
+ OrderWorkflowKind::RevisionProposal,
plan.expected_event_id.clone(),
&enqueue,
),
@@ -2277,46 +1603,46 @@ impl<'sdk> OrdersClient<'sdk> {
})
}
- pub fn prepare_fulfillment_update(
+ pub fn prepare_revision_decision(
&self,
- request: OrderFulfillmentUpdatePrepareRequest,
- ) -> Result<OrderFulfillmentUpdatePlan, RadrootsSdkError> {
+ request: OrderRevisionDecisionPrepareRequest,
+ ) -> Result<OrderRevisionDecisionPlan, RadrootsSdkError> {
let created_at = self.resolved_created_at(request.created_at)?;
- order_fulfillment_update_plan(
+ order_revision_decision_plan(
&request.actor,
request.root_event,
request.previous_event,
- request.fulfillment,
+ request.decision,
created_at,
)
}
- pub async fn enqueue_fulfillment_update<S>(
+ pub async fn enqueue_revision_decision<S>(
&self,
- request: OrderFulfillmentUpdateEnqueueRequest,
+ request: OrderRevisionDecisionEnqueueRequest,
signer: &S,
- ) -> Result<OrderFulfillmentUpdateReceipt, RadrootsSdkError>
+ ) -> Result<OrderRevisionDecisionReceipt, RadrootsSdkError>
where
S: RadrootsEventSigner + ?Sized,
{
- let OrderFulfillmentUpdateEnqueueRequest {
+ let OrderRevisionDecisionEnqueueRequest {
actor,
root_event,
previous_event,
- fulfillment,
+ decision,
target_relays,
idempotency_key,
created_at,
} = request;
- let prepare_request = OrderFulfillmentUpdatePrepareRequest {
+ let prepare_request = OrderRevisionDecisionPrepareRequest {
actor: actor.clone(),
root_event,
previous_event,
- fulfillment,
+ decision,
created_at,
};
- let plan = self.prepare_fulfillment_update(prepare_request)?;
- self.enqueue_prepared_fulfillment_update(
+ let plan = self.prepare_revision_decision(prepare_request)?;
+ self.enqueue_prepared_revision_decision(
&actor,
plan,
target_relays,
@@ -2326,14 +1652,14 @@ impl<'sdk> OrdersClient<'sdk> {
.await
}
- pub async fn enqueue_prepared_fulfillment_update<S>(
+ pub async fn enqueue_prepared_revision_decision<S>(
&self,
actor: &RadrootsActorContext,
- plan: OrderFulfillmentUpdatePlan,
+ plan: OrderRevisionDecisionPlan,
target_relays: SdkRelayTargetPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
signer: &S,
- ) -> Result<OrderFulfillmentUpdateReceipt, RadrootsSdkError>
+ ) -> Result<OrderRevisionDecisionReceipt, RadrootsSdkError>
where
S: RadrootsEventSigner + ?Sized,
{
@@ -2341,12 +1667,12 @@ impl<'sdk> OrdersClient<'sdk> {
.prepared_order_event_exists(&plan.expected_event_id)
.await?
{
- self.require_fulfillment_update_preflight(&plan).await?;
+ self.require_revision_decision_preflight(&plan).await?;
}
let enqueue = enqueue_signed_workflow(
self.sdk,
SdkWorkflowEnqueueRequest {
- operation_kind: OrderWorkflowKind::FulfillmentUpdate.operation_kind(),
+ operation_kind: OrderWorkflowKind::RevisionDecision.operation_kind(),
actor,
frozen_draft: &plan.frozen_draft,
target_relays,
@@ -2355,9 +1681,9 @@ impl<'sdk> OrdersClient<'sdk> {
signer,
)
.await?;
- Ok(OrderFulfillmentUpdateReceipt {
+ Ok(OrderRevisionDecisionReceipt {
workflow: order_workflow_enqueue_receipt(
- OrderWorkflowKind::FulfillmentUpdate,
+ OrderWorkflowKind::RevisionDecision,
plan.expected_event_id.clone(),
&enqueue,
),
@@ -2377,57 +1703,57 @@ impl<'sdk> OrdersClient<'sdk> {
})
}
- pub fn prepare_receipt_record(
+ pub fn prepare_cancellation(
&self,
- request: OrderReceiptRecordPrepareRequest,
- ) -> Result<OrderReceiptRecordPlan, RadrootsSdkError> {
+ request: OrderCancellationPrepareRequest,
+ ) -> Result<OrderCancellationPlan, RadrootsSdkError> {
let created_at = self.resolved_created_at(request.created_at)?;
- order_receipt_record_plan(
+ order_cancellation_plan(
&request.actor,
request.root_event,
request.previous_event,
- request.receipt,
+ request.cancellation,
created_at,
)
}
- pub async fn enqueue_receipt_record<S>(
+ pub async fn enqueue_cancellation<S>(
&self,
- request: OrderReceiptRecordEnqueueRequest,
+ request: OrderCancellationEnqueueRequest,
signer: &S,
- ) -> Result<OrderReceiptRecordReceipt, RadrootsSdkError>
+ ) -> Result<OrderCancellationReceipt, RadrootsSdkError>
where
S: RadrootsEventSigner + ?Sized,
{
- let OrderReceiptRecordEnqueueRequest {
+ let OrderCancellationEnqueueRequest {
actor,
root_event,
previous_event,
- receipt,
+ cancellation,
target_relays,
idempotency_key,
created_at,
} = request;
- let prepare_request = OrderReceiptRecordPrepareRequest {
+ let prepare_request = OrderCancellationPrepareRequest {
actor: actor.clone(),
root_event,
previous_event,
- receipt,
+ cancellation,
created_at,
};
- let plan = self.prepare_receipt_record(prepare_request)?;
- self.enqueue_prepared_receipt_record(&actor, plan, target_relays, idempotency_key, signer)
+ let plan = self.prepare_cancellation(prepare_request)?;
+ self.enqueue_prepared_cancellation(&actor, plan, target_relays, idempotency_key, signer)
.await
}
- pub async fn enqueue_prepared_receipt_record<S>(
+ pub async fn enqueue_prepared_cancellation<S>(
&self,
actor: &RadrootsActorContext,
- plan: OrderReceiptRecordPlan,
+ plan: OrderCancellationPlan,
target_relays: SdkRelayTargetPolicy,
idempotency_key: Option<SdkIdempotencyKey>,
signer: &S,
- ) -> Result<OrderReceiptRecordReceipt, RadrootsSdkError>
+ ) -> Result<OrderCancellationReceipt, RadrootsSdkError>
where
S: RadrootsEventSigner + ?Sized,
{
@@ -2435,12 +1761,12 @@ impl<'sdk> OrdersClient<'sdk> {
.prepared_order_event_exists(&plan.expected_event_id)
.await?
{
- self.require_receipt_record_preflight(&plan).await?;
+ self.require_cancellation_preflight(&plan).await?;
}
let enqueue = enqueue_signed_workflow(
self.sdk,
SdkWorkflowEnqueueRequest {
- operation_kind: OrderWorkflowKind::ReceiptRecord.operation_kind(),
+ operation_kind: OrderWorkflowKind::Cancellation.operation_kind(),
actor,
frozen_draft: &plan.frozen_draft,
target_relays,
@@ -2449,9 +1775,9 @@ impl<'sdk> OrdersClient<'sdk> {
signer,
)
.await?;
- Ok(OrderReceiptRecordReceipt {
+ Ok(OrderCancellationReceipt {
workflow: order_workflow_enqueue_receipt(
- OrderWorkflowKind::ReceiptRecord,
+ OrderWorkflowKind::Cancellation,
plan.expected_event_id.clone(),
&enqueue,
),
@@ -2528,22 +1854,6 @@ impl<'sdk> OrdersClient<'sdk> {
require_cancellation_state(plan, &query_result.projection)
}
- async fn require_fulfillment_update_preflight(
- &self,
- plan: &OrderFulfillmentUpdatePlan,
- ) -> Result<(), RadrootsSdkError> {
- let query_result = self.query_order_projection(&plan.order_id).await?;
- require_fulfillment_update_state(plan, &query_result.projection)
- }
-
- async fn require_receipt_record_preflight(
- &self,
- plan: &OrderReceiptRecordPlan,
- ) -> Result<(), RadrootsSdkError> {
- let query_result = self.query_order_projection(&plan.order_id).await?;
- require_receipt_record_state(plan, &query_result.projection)
- }
-
async fn query_order_projection(
&self,
order_id: &RadrootsOrderId,
@@ -2620,30 +1930,6 @@ fn parse_order_evidence(
.payload;
(payload.order_id, payload.listing_addr)
}
- KIND_ORDER_FULFILLMENT_UPDATE => {
- let payload = order_fulfillment_update_from_event(event)
- .map_err(order_evidence_parse_error)?
- .payload;
- (payload.order_id, payload.listing_addr)
- }
- KIND_ORDER_RECEIPT => {
- let payload = order_receipt_from_event(event)
- .map_err(order_evidence_parse_error)?
- .payload;
- (payload.order_id, payload.listing_addr)
- }
- KIND_ORDER_PAYMENT_RECORD => {
- let payload = order_payment_record_from_event(event)
- .map_err(order_evidence_parse_error)?
- .payload;
- (payload.order_id, payload.listing_addr)
- }
- KIND_ORDER_SETTLEMENT_DECISION => {
- let payload = order_settlement_decision_from_event(event)
- .map_err(order_evidence_parse_error)?
- .payload;
- (payload.order_id, payload.listing_addr)
- }
other => {
return Err(RadrootsSdkError::InvalidRequest {
message: format!("order evidence event kind {other} is not supported"),
@@ -2679,9 +1965,7 @@ impl OrderStatusReceipt {
query_result.limit_applied,
);
let eligibility = OrderStatusEligibility::from_projection(&projection);
- let payment_handoff = OrderPaymentHandoffKind::from_projection(&projection);
- let next_action =
- OrderStatusNextActionKind::from_projection(&projection, &eligibility, payment_handoff);
+ let next_action = OrderStatusNextActionKind::from_projection(&projection, &eligibility);
Self {
order_id: projection.order_id,
source: SdkOrderStatusSource::LocalEventStore,
@@ -2689,22 +1973,20 @@ impl OrderStatusReceipt {
event_count: query_result.event_count,
limit_applied: query_result.limit_applied,
status: projection.status.into(),
- fulfillment_status: projection.fulfillment_status.map(Into::into),
- payment_state: projection.payment.state.into(),
- settlement_state: projection.payment.settlement_state.into(),
lifecycle_terminal: projection.lifecycle_terminal,
+ listing_addr: projection.listing_addr,
+ buyer_pubkey: projection.buyer_pubkey,
+ seller_pubkey: projection.seller_pubkey,
+ economics: projection.economics,
evidence,
eligibility,
- payment_handoff,
next_action,
event_ids: query_result.event_ids,
request_event_id: projection.request_event_id,
decision_event_id: projection.decision_event_id,
agreement_event_id: projection.agreement_event_id,
pending_revision_event_id: projection.pending_revision_event_id,
- fulfillment_event_id: projection.fulfillment_event_id,
cancellation_event_id: projection.cancellation_event_id,
- receipt_event_id: projection.receipt_event_id,
last_event_id: projection.last_event_id,
issues: projection.issues.into_iter().map(Into::into).collect(),
}
@@ -2725,9 +2007,7 @@ impl OrderStatusEvidenceSummary {
has_decision: projection.decision_event_id.is_some(),
has_agreement: projection.agreement_event_id.is_some(),
has_pending_revision: projection.pending_revision_event_id.is_some(),
- has_fulfillment: projection.fulfillment_event_id.is_some(),
has_cancellation: projection.cancellation_event_id.is_some(),
- has_receipt: projection.receipt_event_id.is_some(),
has_issues: !projection.issues.is_empty(),
}
}
@@ -2739,72 +2019,16 @@ impl OrderStatusEligibility {
let clean = projection.issues.is_empty();
let open = clean && !projection.lifecycle_terminal;
let requested = projection.status == RadrootsOrderStatus::Requested;
- let accepted = projection.status == RadrootsOrderStatus::Accepted;
let has_pending_revision = projection.pending_revision_event_id.is_some();
- let has_fulfillment = projection.fulfillment_event_id.is_some();
- let fulfillment_terminal = matches!(
- projection.fulfillment_status,
- Some(
- RadrootsOrderFulfillmentState::Delivered
- | RadrootsOrderFulfillmentState::SellerCancelled
- )
- );
- let receipt_ready = matches!(
- projection.fulfillment_status,
- Some(
- RadrootsOrderFulfillmentState::ReadyForPickup
- | RadrootsOrderFulfillmentState::Delivered
- )
- );
- let revision_payment_open =
- projection.payment.state == RadrootsOrderPaymentState::NotRecorded;
Self {
- can_decide: open && requested && projection.decision_event_id.is_none(),
- can_propose_revision: open
- && accepted
- && !has_pending_revision
- && !has_fulfillment
- && revision_payment_open,
- can_decide_revision: open && accepted && has_pending_revision,
- can_cancel: open
- && matches!(
- projection.status,
- RadrootsOrderStatus::Requested | RadrootsOrderStatus::Accepted
- )
- && !has_pending_revision
- && !has_fulfillment,
- can_update_fulfillment: open
- && accepted
- && !has_pending_revision
- && !fulfillment_terminal,
- can_record_receipt: open
- && accepted
- && receipt_ready
- && projection.receipt_event_id.is_none(),
- }
- }
-}
-
-#[cfg(feature = "runtime")]
-impl OrderPaymentHandoffKind {
- fn from_projection(projection: &RadrootsOrderProjection) -> Self {
- if !projection.issues.is_empty() || projection.status == RadrootsOrderStatus::Invalid {
- return Self::Invalid;
- }
- match projection.status {
- RadrootsOrderStatus::Missing | RadrootsOrderStatus::Requested => Self::NotReady,
- RadrootsOrderStatus::Declined | RadrootsOrderStatus::Cancelled => Self::NotRequired,
- RadrootsOrderStatus::Accepted
- | RadrootsOrderStatus::Completed
- | RadrootsOrderStatus::Disputed => match projection.payment.state {
- RadrootsOrderPaymentState::NotRecorded => Self::InPersonOrOffPlatformPending,
- RadrootsOrderPaymentState::Recorded => Self::InPersonOrOffPlatformRecorded,
- RadrootsOrderPaymentState::Settled => Self::InPersonOrOffPlatformSettled,
- RadrootsOrderPaymentState::Rejected => Self::Rejected,
- RadrootsOrderPaymentState::Invalid => Self::Invalid,
- },
- RadrootsOrderStatus::Invalid => Self::Invalid,
+ can_decide: open
+ && requested
+ && projection.decision_event_id.is_none()
+ && !has_pending_revision,
+ can_propose_revision: open && requested && !has_pending_revision,
+ can_decide_revision: open && requested && has_pending_revision,
+ can_cancel: open && requested && !has_pending_revision,
}
}
}
@@ -2814,7 +2038,6 @@ impl OrderStatusNextActionKind {
fn from_projection(
projection: &RadrootsOrderProjection,
eligibility: &OrderStatusEligibility,
- payment_handoff: OrderPaymentHandoffKind,
) -> Self {
if projection.status == RadrootsOrderStatus::Missing {
return Self::NoLocalOrder;
@@ -2831,18 +2054,6 @@ impl OrderStatusNextActionKind {
if eligibility.can_decide_revision {
return Self::DecideRevision;
}
- if eligibility.can_record_receipt {
- return Self::RecordReceipt;
- }
- if matches!(
- payment_handoff,
- OrderPaymentHandoffKind::InPersonOrOffPlatformPending
- ) {
- return Self::ArrangeInPersonOrOffPlatformPayment;
- }
- if eligibility.can_update_fulfillment {
- return Self::FulfillOrder;
- }
Self::Terminal
}
}
@@ -2978,135 +2189,24 @@ fn order_revision_proposal_plan(
)?;
let created_at_nostr = created_at.try_into_nostr_created_at()?;
let order_id = proposal.order_id.clone();
- let listing_addr = proposal.listing_addr.clone();
- let buyer_pubkey = proposal.buyer_pubkey.clone();
- let seller_pubkey = proposal.seller_pubkey.clone();
- let draft =
- order::build_order_revision_proposal_draft(&root_event_id, &previous_event_id, &proposal)
- .map_err(|error| RadrootsSdkError::InvalidRequest {
- message: format!("order revision proposal draft encode failed: {error}"),
- })?;
- let (frozen_draft, expected_event_id) = freeze_order_workflow_draft(
- draft.into_wire_parts(),
- ORDER_REVISION_PROPOSAL_CONTRACT_ID,
- seller_pubkey.as_str(),
- created_at_nostr,
- "order revision proposal",
- )?;
- Ok(OrderRevisionProposalPlan {
- workflow: order_workflow_plan(
- OrderWorkflowKind::RevisionProposal,
- expected_event_id.clone(),
- created_at,
- ),
- order_id,
- listing_addr,
- buyer_pubkey,
- seller_pubkey,
- root_event_id,
- previous_event_id,
- expected_event_id,
- frozen_draft,
- created_at,
- })
-}
-
-#[cfg(feature = "runtime")]
-fn order_revision_decision_plan(
- actor: &RadrootsActorContext,
- root_event: RadrootsNostrEventPtr,
- previous_event: RadrootsNostrEventPtr,
- decision: RadrootsOrderRevisionDecision,
- created_at: RadrootsSdkTimestamp,
-) -> Result<OrderRevisionDecisionPlan, RadrootsSdkError> {
- require_buyer_actor(actor, "order.prepare_revision_decision")?;
- let root_event_id = order_reference_event_id(&root_event, "root")?;
- let previous_event_id = order_reference_event_id(&previous_event, "previous")?;
- if decision.buyer_pubkey.as_str() != actor.pubkey().as_str() {
- return Err(RadrootsSdkError::UnauthorizedActor {
- operation: "order.prepare_revision_decision".to_owned(),
- reason: "actor pubkey must match order buyer_pubkey".to_owned(),
- });
- }
- require_payload_event_refs(
- "order revision decision",
- &decision.root_event_id,
- &decision.prev_event_id,
- &root_event_id,
- &previous_event_id,
- )?;
- let created_at_nostr = created_at.try_into_nostr_created_at()?;
- let order_id = decision.order_id.clone();
- let listing_addr = decision.listing_addr.clone();
- let buyer_pubkey = decision.buyer_pubkey.clone();
- let seller_pubkey = decision.seller_pubkey.clone();
- let draft =
- order::build_order_revision_decision_draft(&root_event_id, &previous_event_id, &decision)
- .map_err(|error| RadrootsSdkError::InvalidRequest {
- message: format!("order revision decision draft encode failed: {error}"),
- })?;
- let (frozen_draft, expected_event_id) = freeze_order_workflow_draft(
- draft.into_wire_parts(),
- ORDER_REVISION_DECISION_CONTRACT_ID,
- buyer_pubkey.as_str(),
- created_at_nostr,
- "order revision decision",
- )?;
- Ok(OrderRevisionDecisionPlan {
- workflow: order_workflow_plan(
- OrderWorkflowKind::RevisionDecision,
- expected_event_id.clone(),
- created_at,
- ),
- order_id,
- listing_addr,
- buyer_pubkey,
- seller_pubkey,
- root_event_id,
- previous_event_id,
- expected_event_id,
- frozen_draft,
- created_at,
- })
-}
-
-#[cfg(feature = "runtime")]
-fn order_cancellation_plan(
- actor: &RadrootsActorContext,
- root_event: RadrootsNostrEventPtr,
- previous_event: RadrootsNostrEventPtr,
- cancellation: RadrootsOrderCancellation,
- created_at: RadrootsSdkTimestamp,
-) -> Result<OrderCancellationPlan, RadrootsSdkError> {
- require_buyer_actor(actor, "order.prepare_cancellation")?;
- let root_event_id = order_reference_event_id(&root_event, "root")?;
- let previous_event_id = order_reference_event_id(&previous_event, "previous")?;
- if cancellation.buyer_pubkey.as_str() != actor.pubkey().as_str() {
- return Err(RadrootsSdkError::UnauthorizedActor {
- operation: "order.prepare_cancellation".to_owned(),
- reason: "actor pubkey must match order buyer_pubkey".to_owned(),
- });
- }
- let created_at_nostr = created_at.try_into_nostr_created_at()?;
- let order_id = cancellation.order_id.clone();
- let listing_addr = cancellation.listing_addr.clone();
- let buyer_pubkey = cancellation.buyer_pubkey.clone();
- let seller_pubkey = cancellation.seller_pubkey.clone();
+ let listing_addr = proposal.listing_addr.clone();
+ let buyer_pubkey = proposal.buyer_pubkey.clone();
+ let seller_pubkey = proposal.seller_pubkey.clone();
let draft =
- order::build_order_cancellation_draft(&root_event_id, &previous_event_id, &cancellation)
+ order::build_order_revision_proposal_draft(&root_event_id, &previous_event_id, &proposal)
.map_err(|error| RadrootsSdkError::InvalidRequest {
- message: format!("order cancellation draft encode failed: {error}"),
- })?;
+ message: format!("order revision proposal draft encode failed: {error}"),
+ })?;
let (frozen_draft, expected_event_id) = freeze_order_workflow_draft(
draft.into_wire_parts(),
- ORDER_CANCELLATION_CONTRACT_ID,
- buyer_pubkey.as_str(),
+ ORDER_REVISION_PROPOSAL_CONTRACT_ID,
+ seller_pubkey.as_str(),
created_at_nostr,
- "order cancellation",
+ "order revision proposal",
)?;
- Ok(OrderCancellationPlan {
+ Ok(OrderRevisionProposalPlan {
workflow: order_workflow_plan(
- OrderWorkflowKind::Cancellation,
+ OrderWorkflowKind::RevisionProposal,
expected_event_id.clone(),
created_at,
),
@@ -3123,42 +2223,49 @@ fn order_cancellation_plan(
}
#[cfg(feature = "runtime")]
-fn order_fulfillment_update_plan(
+fn order_revision_decision_plan(
actor: &RadrootsActorContext,
root_event: RadrootsNostrEventPtr,
previous_event: RadrootsNostrEventPtr,
- fulfillment: RadrootsOrderFulfillmentUpdate,
+ decision: RadrootsOrderRevisionDecision,
created_at: RadrootsSdkTimestamp,
-) -> Result<OrderFulfillmentUpdatePlan, RadrootsSdkError> {
- require_seller_actor(actor, "order.prepare_fulfillment_update")?;
+) -> Result<OrderRevisionDecisionPlan, RadrootsSdkError> {
+ require_buyer_actor(actor, "order.prepare_revision_decision")?;
let root_event_id = order_reference_event_id(&root_event, "root")?;
let previous_event_id = order_reference_event_id(&previous_event, "previous")?;
- if fulfillment.seller_pubkey.as_str() != actor.pubkey().as_str() {
+ if decision.buyer_pubkey.as_str() != actor.pubkey().as_str() {
return Err(RadrootsSdkError::UnauthorizedActor {
- operation: "order.prepare_fulfillment_update".to_owned(),
- reason: "actor pubkey must match order seller_pubkey".to_owned(),
+ operation: "order.prepare_revision_decision".to_owned(),
+ reason: "actor pubkey must match order buyer_pubkey".to_owned(),
});
}
+ require_payload_event_refs(
+ "order revision decision",
+ &decision.root_event_id,
+ &decision.prev_event_id,
+ &root_event_id,
+ &previous_event_id,
+ )?;
let created_at_nostr = created_at.try_into_nostr_created_at()?;
- let order_id = fulfillment.order_id.clone();
- let listing_addr = fulfillment.listing_addr.clone();
- let buyer_pubkey = fulfillment.buyer_pubkey.clone();
- let seller_pubkey = fulfillment.seller_pubkey.clone();
+ let order_id = decision.order_id.clone();
+ let listing_addr = decision.listing_addr.clone();
+ let buyer_pubkey = decision.buyer_pubkey.clone();
+ let seller_pubkey = decision.seller_pubkey.clone();
let draft =
- order::build_fulfillment_update_draft(&root_event_id, &previous_event_id, &fulfillment)
+ order::build_order_revision_decision_draft(&root_event_id, &previous_event_id, &decision)
.map_err(|error| RadrootsSdkError::InvalidRequest {
- message: format!("order fulfillment update draft encode failed: {error}"),
- })?;
+ message: format!("order revision decision draft encode failed: {error}"),
+ })?;
let (frozen_draft, expected_event_id) = freeze_order_workflow_draft(
draft.into_wire_parts(),
- ORDER_FULFILLMENT_UPDATE_CONTRACT_ID,
- seller_pubkey.as_str(),
+ ORDER_REVISION_DECISION_CONTRACT_ID,
+ buyer_pubkey.as_str(),
created_at_nostr,
- "order fulfillment update",
+ "order revision decision",
)?;
- Ok(OrderFulfillmentUpdatePlan {
+ Ok(OrderRevisionDecisionPlan {
workflow: order_workflow_plan(
- OrderWorkflowKind::FulfillmentUpdate,
+ OrderWorkflowKind::RevisionDecision,
expected_event_id.clone(),
created_at,
),
@@ -3175,41 +2282,42 @@ fn order_fulfillment_update_plan(
}
#[cfg(feature = "runtime")]
-fn order_receipt_record_plan(
+fn order_cancellation_plan(
actor: &RadrootsActorContext,
root_event: RadrootsNostrEventPtr,
previous_event: RadrootsNostrEventPtr,
- receipt: RadrootsOrderReceipt,
+ cancellation: RadrootsOrderCancellation,
created_at: RadrootsSdkTimestamp,
-) -> Result<OrderReceiptRecordPlan, RadrootsSdkError> {
- require_buyer_actor(actor, "order.prepare_receipt_record")?;
+) -> Result<OrderCancellationPlan, RadrootsSdkError> {
+ require_buyer_actor(actor, "order.prepare_cancellation")?;
let root_event_id = order_reference_event_id(&root_event, "root")?;
let previous_event_id = order_reference_event_id(&previous_event, "previous")?;
- if receipt.buyer_pubkey.as_str() != actor.pubkey().as_str() {
+ if cancellation.buyer_pubkey.as_str() != actor.pubkey().as_str() {
return Err(RadrootsSdkError::UnauthorizedActor {
- operation: "order.prepare_receipt_record".to_owned(),
+ operation: "order.prepare_cancellation".to_owned(),
reason: "actor pubkey must match order buyer_pubkey".to_owned(),
});
}
let created_at_nostr = created_at.try_into_nostr_created_at()?;
- let order_id = receipt.order_id.clone();
- let listing_addr = receipt.listing_addr.clone();
- let buyer_pubkey = receipt.buyer_pubkey.clone();
- let seller_pubkey = receipt.seller_pubkey.clone();
- let draft = order::build_buyer_receipt_draft(&root_event_id, &previous_event_id, &receipt)
- .map_err(|error| RadrootsSdkError::InvalidRequest {
- message: format!("order receipt record draft encode failed: {error}"),
- })?;
+ let order_id = cancellation.order_id.clone();
+ let listing_addr = cancellation.listing_addr.clone();
+ let buyer_pubkey = cancellation.buyer_pubkey.clone();
+ let seller_pubkey = cancellation.seller_pubkey.clone();
+ let draft =
+ order::build_order_cancellation_draft(&root_event_id, &previous_event_id, &cancellation)
+ .map_err(|error| RadrootsSdkError::InvalidRequest {
+ message: format!("order cancellation draft encode failed: {error}"),
+ })?;
let (frozen_draft, expected_event_id) = freeze_order_workflow_draft(
draft.into_wire_parts(),
- ORDER_RECEIPT_CONTRACT_ID,
+ ORDER_CANCELLATION_CONTRACT_ID,
buyer_pubkey.as_str(),
created_at_nostr,
- "order receipt record",
+ "order cancellation",
)?;
- Ok(OrderReceiptRecordPlan {
+ Ok(OrderCancellationPlan {
workflow: order_workflow_plan(
- OrderWorkflowKind::ReceiptRecord,
+ OrderWorkflowKind::Cancellation,
expected_event_id.clone(),
created_at,
),
@@ -3384,6 +2492,14 @@ fn require_decision_request_evidence(
),
});
}
+ if let Some(pending_revision_event_id) = projection.pending_revision_event_id.as_ref() {
+ return Err(RadrootsSdkError::InvalidRequest {
+ message: format!(
+ "order decision for order {} cannot follow pending revision proposal {}",
+ plan.order_id, pending_revision_event_id
+ ),
+ });
+ }
if !projection.issues.is_empty() {
return Err(RadrootsSdkError::InvalidRequest {
message: format!(
@@ -3444,17 +2560,9 @@ fn require_revision_proposal_state(
previous_event_id: &plan.previous_event_id,
};
require_clean_lifecycle_projection(refs, projection)?;
- require_lifecycle_status(&refs, projection, RadrootsOrderStatus::Accepted)?;
+ require_lifecycle_status(&refs, projection, RadrootsOrderStatus::Requested)?;
require_no_lifecycle_terminal(&refs, projection)?;
- require_no_payment_for_revision(&refs, projection)?;
require_no_pending_revision(&refs, projection)?;
- if projection.fulfillment_event_id.is_some() {
- return Err(lifecycle_invalid(
- refs.operation,
- refs.order_id,
- "revision proposal requires order before fulfillment",
- ));
- }
require_lifecycle_previous_is_current(&refs, projection)
}
@@ -3473,7 +2581,7 @@ fn require_revision_decision_state(
previous_event_id: &plan.previous_event_id,
};
require_clean_lifecycle_projection(refs, projection)?;
- require_lifecycle_status(&refs, projection, RadrootsOrderStatus::Accepted)?;
+ require_lifecycle_status(&refs, projection, RadrootsOrderStatus::Requested)?;
require_no_lifecycle_terminal(&refs, projection)?;
require_pending_revision(&refs, projection)?;
require_lifecycle_previous_is_current(&refs, projection)
@@ -3494,102 +2602,18 @@ fn require_cancellation_state(
previous_event_id: &plan.previous_event_id,
};
require_clean_lifecycle_projection(refs, projection)?;
- if !matches!(
- projection.status,
- RadrootsOrderStatus::Requested | RadrootsOrderStatus::Accepted
- ) {
+ if projection.status != RadrootsOrderStatus::Requested {
return Err(lifecycle_invalid(
refs.operation,
refs.order_id,
format!(
- "cancellation requires requested or accepted local state; current state is {:?}",
+ "cancellation requires requested local state; current state is {:?}",
projection.status
),
));
}
require_no_lifecycle_terminal(&refs, projection)?;
require_no_pending_revision(&refs, projection)?;
- if projection.fulfillment_event_id.is_some() {
- return Err(lifecycle_invalid(
- refs.operation,
- refs.order_id,
- "cancellation requires order before fulfillment",
- ));
- }
- require_lifecycle_previous_is_current(&refs, projection)
-}
-
-#[cfg(feature = "runtime")]
-fn require_fulfillment_update_state(
- plan: &OrderFulfillmentUpdatePlan,
- projection: &RadrootsOrderProjection,
-) -> Result<(), RadrootsSdkError> {
- let refs = OrderLifecycleReferences {
- operation: "order fulfillment update",
- order_id: &plan.order_id,
- listing_addr: &plan.listing_addr,
- buyer_pubkey: &plan.buyer_pubkey,
- seller_pubkey: &plan.seller_pubkey,
- root_event_id: &plan.root_event_id,
- previous_event_id: &plan.previous_event_id,
- };
- require_clean_lifecycle_projection(refs, projection)?;
- require_lifecycle_status(&refs, projection, RadrootsOrderStatus::Accepted)?;
- require_no_lifecycle_terminal(&refs, projection)?;
- require_no_pending_revision(&refs, projection)?;
- if matches!(
- projection.fulfillment_status,
- Some(
- RadrootsOrderFulfillmentState::Delivered
- | RadrootsOrderFulfillmentState::SellerCancelled
- )
- ) {
- return Err(lifecycle_invalid(
- refs.operation,
- refs.order_id,
- "fulfillment update cannot follow terminal fulfillment status",
- ));
- }
- require_lifecycle_previous_is_current(&refs, projection)
-}
-
-#[cfg(feature = "runtime")]
-fn require_receipt_record_state(
- plan: &OrderReceiptRecordPlan,
- projection: &RadrootsOrderProjection,
-) -> Result<(), RadrootsSdkError> {
- let refs = OrderLifecycleReferences {
- operation: "order receipt record",
- order_id: &plan.order_id,
- listing_addr: &plan.listing_addr,
- buyer_pubkey: &plan.buyer_pubkey,
- seller_pubkey: &plan.seller_pubkey,
- root_event_id: &plan.root_event_id,
- previous_event_id: &plan.previous_event_id,
- };
- require_clean_lifecycle_projection(refs, projection)?;
- require_lifecycle_status(&refs, projection, RadrootsOrderStatus::Accepted)?;
- require_no_lifecycle_terminal(&refs, projection)?;
- if projection.fulfillment_event_id.as_ref() != Some(refs.previous_event_id) {
- return Err(lifecycle_invalid(
- refs.operation,
- refs.order_id,
- "receipt record requires previous event to be the current fulfillment event",
- ));
- }
- if !matches!(
- projection.fulfillment_status,
- Some(
- RadrootsOrderFulfillmentState::ReadyForPickup
- | RadrootsOrderFulfillmentState::Delivered
- )
- ) {
- return Err(lifecycle_invalid(
- refs.operation,
- refs.order_id,
- "receipt record requires ready-for-pickup or delivered fulfillment state",
- ));
- }
require_lifecycle_previous_is_current(&refs, projection)
}
@@ -3685,22 +2709,6 @@ fn require_no_lifecycle_terminal(
}
#[cfg(feature = "runtime")]
-fn require_no_payment_for_revision(
- refs: &OrderLifecycleReferences<'_>,
- projection: &RadrootsOrderProjection,
-) -> Result<(), RadrootsSdkError> {
- if projection.payment.state == RadrootsOrderPaymentState::NotRecorded {
- Ok(())
- } else {
- Err(lifecycle_invalid(
- refs.operation,
- refs.order_id,
- "revision proposal cannot follow recorded payment state",
- ))
- }
-}
-
-#[cfg(feature = "runtime")]
fn require_pending_revision(
refs: &OrderLifecycleReferences<'_>,
projection: &RadrootsOrderProjection,
@@ -3923,54 +2931,12 @@ impl From<RadrootsOrderStatus> for OrderStatusKind {
RadrootsOrderStatus::Accepted => Self::Accepted,
RadrootsOrderStatus::Declined => Self::Declined,
RadrootsOrderStatus::Cancelled => Self::Cancelled,
- RadrootsOrderStatus::Completed => Self::Completed,
- RadrootsOrderStatus::Disputed => Self::Disputed,
RadrootsOrderStatus::Invalid => Self::Invalid,
}
}
}
#[cfg(feature = "runtime")]
-impl From<RadrootsOrderFulfillmentState> for OrderFulfillmentStatusKind {
- fn from(status: RadrootsOrderFulfillmentState) -> Self {
- match status {
- RadrootsOrderFulfillmentState::AcceptedNotFulfilled => Self::AcceptedNotFulfilled,
- RadrootsOrderFulfillmentState::Preparing => Self::Preparing,
- RadrootsOrderFulfillmentState::ReadyForPickup => Self::ReadyForPickup,
- RadrootsOrderFulfillmentState::OutForDelivery => Self::OutForDelivery,
- RadrootsOrderFulfillmentState::Delivered => Self::Delivered,
- RadrootsOrderFulfillmentState::SellerCancelled => Self::SellerCancelled,
- }
- }
-}
-
-#[cfg(feature = "runtime")]
-impl From<RadrootsOrderPaymentState> for OrderPaymentStateKind {
- fn from(state: RadrootsOrderPaymentState) -> Self {
- match state {
- RadrootsOrderPaymentState::NotRecorded => Self::NotRecorded,
- RadrootsOrderPaymentState::Recorded => Self::Recorded,
- RadrootsOrderPaymentState::Settled => Self::Settled,
- RadrootsOrderPaymentState::Rejected => Self::Rejected,
- RadrootsOrderPaymentState::Invalid => Self::Invalid,
- }
- }
-}
-
-#[cfg(feature = "runtime")]
-impl From<RadrootsOrderSettlementState> for OrderSettlementStateKind {
- fn from(state: RadrootsOrderSettlementState) -> Self {
- match state {
- RadrootsOrderSettlementState::NotRequired => Self::NotRequired,
- RadrootsOrderSettlementState::Pending => Self::Pending,
- RadrootsOrderSettlementState::Accepted => Self::Accepted,
- RadrootsOrderSettlementState::Rejected => Self::Rejected,
- RadrootsOrderSettlementState::Invalid => Self::Invalid,
- }
- }
-}
-
-#[cfg(feature = "runtime")]
impl From<RadrootsOrderIssue> for SdkOrderStatusIssue {
fn from(issue: RadrootsOrderIssue) -> Self {
match issue {
@@ -4043,12 +3009,6 @@ impl From<RadrootsOrderIssue> for SdkOrderStatusIssue {
RadrootsOrderIssue::ConflictingDecisions { event_ids } => {
Self::new(SdkOrderStatusIssueKind::ConflictingDecisions, event_ids)
}
- RadrootsOrderIssue::RevisionProposalWithoutAcceptedDecision { event_id } => {
- Self::single(
- SdkOrderStatusIssueKind::RevisionProposalWithoutAcceptedDecision,
- event_id,
- )
- }
RadrootsOrderIssue::RevisionProposalPayloadInvalid { event_id } => Self::single(
SdkOrderStatusIssueKind::RevisionProposalPayloadInvalid,
event_id,
@@ -4137,56 +3097,6 @@ impl From<RadrootsOrderIssue> for SdkOrderStatusIssue {
SdkOrderStatusIssueKind::RevisionDecisionRevisionIdMismatch,
event_id,
),
- RadrootsOrderIssue::FulfillmentWithoutAcceptedDecision { event_id } => Self::single(
- SdkOrderStatusIssueKind::FulfillmentWithoutAcceptedDecision,
- event_id,
- ),
- RadrootsOrderIssue::FulfillmentPayloadInvalid { event_id } => {
- Self::single(SdkOrderStatusIssueKind::FulfillmentPayloadInvalid, event_id)
- }
- RadrootsOrderIssue::FulfillmentOrderIdMismatch { event_id } => Self::single(
- SdkOrderStatusIssueKind::FulfillmentOrderIdMismatch,
- event_id,
- ),
- RadrootsOrderIssue::FulfillmentAuthorMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::FulfillmentAuthorMismatch, event_id)
- }
- RadrootsOrderIssue::FulfillmentCounterpartyMismatch { event_id } => Self::single(
- SdkOrderStatusIssueKind::FulfillmentCounterpartyMismatch,
- event_id,
- ),
- RadrootsOrderIssue::FulfillmentBuyerMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::FulfillmentBuyerMismatch, event_id)
- }
- RadrootsOrderIssue::FulfillmentSellerMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::FulfillmentSellerMismatch, event_id)
- }
- RadrootsOrderIssue::FulfillmentListingAddressInvalid { event_id } => Self::single(
- SdkOrderStatusIssueKind::FulfillmentListingAddressInvalid,
- event_id,
- ),
- RadrootsOrderIssue::FulfillmentListingMismatch { event_id } => Self::single(
- SdkOrderStatusIssueKind::FulfillmentListingMismatch,
- event_id,
- ),
- RadrootsOrderIssue::FulfillmentRootMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::FulfillmentRootMismatch, event_id)
- }
- RadrootsOrderIssue::FulfillmentPreviousMismatch { event_id } => Self::single(
- SdkOrderStatusIssueKind::FulfillmentPreviousMismatch,
- event_id,
- ),
- RadrootsOrderIssue::FulfillmentStatusNotPublishable { event_id } => Self::single(
- SdkOrderStatusIssueKind::FulfillmentStatusNotPublishable,
- event_id,
- ),
- RadrootsOrderIssue::FulfillmentUnsupportedTransition { event_id } => Self::single(
- SdkOrderStatusIssueKind::FulfillmentUnsupportedTransition,
- event_id,
- ),
- RadrootsOrderIssue::ForkedFulfillments { event_ids } => {
- Self::new(SdkOrderStatusIssueKind::ForkedFulfillments, event_ids)
- }
RadrootsOrderIssue::CancellationWithoutCancellableOrder { event_id } => Self::single(
SdkOrderStatusIssueKind::CancellationWithoutCancellableOrder,
event_id,
@@ -4229,177 +3139,6 @@ impl From<RadrootsOrderIssue> for SdkOrderStatusIssue {
SdkOrderStatusIssueKind::CancellationPreviousMismatch,
event_id,
),
- RadrootsOrderIssue::CancellationAfterFulfillment { event_id } => Self::single(
- SdkOrderStatusIssueKind::CancellationAfterFulfillment,
- event_id,
- ),
- RadrootsOrderIssue::ReceiptWithoutEligibleFulfillment { event_id } => Self::single(
- SdkOrderStatusIssueKind::ReceiptWithoutEligibleFulfillment,
- event_id,
- ),
- RadrootsOrderIssue::ReceiptPayloadInvalid { event_id } => {
- Self::single(SdkOrderStatusIssueKind::ReceiptPayloadInvalid, event_id)
- }
- RadrootsOrderIssue::ReceiptOrderIdMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::ReceiptOrderIdMismatch, event_id)
- }
- RadrootsOrderIssue::ReceiptAuthorMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::ReceiptAuthorMismatch, event_id)
- }
- RadrootsOrderIssue::ReceiptCounterpartyMismatch { event_id } => Self::single(
- SdkOrderStatusIssueKind::ReceiptCounterpartyMismatch,
- event_id,
- ),
- RadrootsOrderIssue::ReceiptBuyerMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::ReceiptBuyerMismatch, event_id)
- }
- RadrootsOrderIssue::ReceiptSellerMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::ReceiptSellerMismatch, event_id)
- }
- RadrootsOrderIssue::ReceiptListingAddressInvalid { event_id } => Self::single(
- SdkOrderStatusIssueKind::ReceiptListingAddressInvalid,
- event_id,
- ),
- RadrootsOrderIssue::ReceiptListingMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::ReceiptListingMismatch, event_id)
- }
- RadrootsOrderIssue::ReceiptRootMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::ReceiptRootMismatch, event_id)
- }
- RadrootsOrderIssue::ReceiptPreviousMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::ReceiptPreviousMismatch, event_id)
- }
- RadrootsOrderIssue::PaymentWithoutAcceptedAgreement { event_id } => Self::single(
- SdkOrderStatusIssueKind::PaymentWithoutAcceptedAgreement,
- event_id,
- ),
- RadrootsOrderIssue::PaymentPayloadInvalid { event_id } => {
- Self::single(SdkOrderStatusIssueKind::PaymentPayloadInvalid, event_id)
- }
- RadrootsOrderIssue::PaymentOrderIdMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::PaymentOrderIdMismatch, event_id)
- }
- RadrootsOrderIssue::PaymentAuthorMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::PaymentAuthorMismatch, event_id)
- }
- RadrootsOrderIssue::PaymentCounterpartyMismatch { event_id } => Self::single(
- SdkOrderStatusIssueKind::PaymentCounterpartyMismatch,
- event_id,
- ),
- RadrootsOrderIssue::PaymentBuyerMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::PaymentBuyerMismatch, event_id)
- }
- RadrootsOrderIssue::PaymentSellerMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::PaymentSellerMismatch, event_id)
- }
- RadrootsOrderIssue::PaymentListingAddressInvalid { event_id } => Self::single(
- SdkOrderStatusIssueKind::PaymentListingAddressInvalid,
- event_id,
- ),
- RadrootsOrderIssue::PaymentListingMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::PaymentListingMismatch, event_id)
- }
- RadrootsOrderIssue::PaymentRootMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::PaymentRootMismatch, event_id)
- }
- RadrootsOrderIssue::PaymentPreviousMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::PaymentPreviousMismatch, event_id)
- }
- RadrootsOrderIssue::PaymentAgreementMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::PaymentAgreementMismatch, event_id)
- }
- RadrootsOrderIssue::PaymentQuoteMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::PaymentQuoteMismatch, event_id)
- }
- RadrootsOrderIssue::PaymentQuoteVersionMismatch { event_id } => Self::single(
- SdkOrderStatusIssueKind::PaymentQuoteVersionMismatch,
- event_id,
- ),
- RadrootsOrderIssue::PaymentEconomicsDigestMismatch { event_id } => Self::single(
- SdkOrderStatusIssueKind::PaymentEconomicsDigestMismatch,
- event_id,
- ),
- RadrootsOrderIssue::PaymentAmountMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::PaymentAmountMismatch, event_id)
- }
- RadrootsOrderIssue::PaymentCurrencyMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::PaymentCurrencyMismatch, event_id)
- }
- RadrootsOrderIssue::PaymentAfterCancellation { event_id } => {
- Self::single(SdkOrderStatusIssueKind::PaymentAfterCancellation, event_id)
- }
- RadrootsOrderIssue::RevisionAfterPayment { event_id } => {
- Self::single(SdkOrderStatusIssueKind::RevisionAfterPayment, event_id)
- }
- RadrootsOrderIssue::DuplicatePayments { event_ids } => {
- Self::new(SdkOrderStatusIssueKind::DuplicatePayments, event_ids)
- }
- RadrootsOrderIssue::SettlementWithoutValidPayment { event_id } => Self::single(
- SdkOrderStatusIssueKind::SettlementWithoutValidPayment,
- event_id,
- ),
- RadrootsOrderIssue::SettlementPayloadInvalid { event_id } => {
- Self::single(SdkOrderStatusIssueKind::SettlementPayloadInvalid, event_id)
- }
- RadrootsOrderIssue::SettlementOrderIdMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::SettlementOrderIdMismatch, event_id)
- }
- RadrootsOrderIssue::SettlementAuthorMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::SettlementAuthorMismatch, event_id)
- }
- RadrootsOrderIssue::SettlementCounterpartyMismatch { event_id } => Self::single(
- SdkOrderStatusIssueKind::SettlementCounterpartyMismatch,
- event_id,
- ),
- RadrootsOrderIssue::SettlementBuyerMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::SettlementBuyerMismatch, event_id)
- }
- RadrootsOrderIssue::SettlementSellerMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::SettlementSellerMismatch, event_id)
- }
- RadrootsOrderIssue::SettlementListingAddressInvalid { event_id } => Self::single(
- SdkOrderStatusIssueKind::SettlementListingAddressInvalid,
- event_id,
- ),
- RadrootsOrderIssue::SettlementListingMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::SettlementListingMismatch, event_id)
- }
- RadrootsOrderIssue::SettlementRootMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::SettlementRootMismatch, event_id)
- }
- RadrootsOrderIssue::SettlementPreviousMismatch { event_id } => Self::single(
- SdkOrderStatusIssueKind::SettlementPreviousMismatch,
- event_id,
- ),
- RadrootsOrderIssue::SettlementPaymentEventMismatch { event_id } => Self::single(
- SdkOrderStatusIssueKind::SettlementPaymentEventMismatch,
- event_id,
- ),
- RadrootsOrderIssue::SettlementAgreementMismatch { event_id } => Self::single(
- SdkOrderStatusIssueKind::SettlementAgreementMismatch,
- event_id,
- ),
- RadrootsOrderIssue::SettlementQuoteMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::SettlementQuoteMismatch, event_id)
- }
- RadrootsOrderIssue::SettlementQuoteVersionMismatch { event_id } => Self::single(
- SdkOrderStatusIssueKind::SettlementQuoteVersionMismatch,
- event_id,
- ),
- RadrootsOrderIssue::SettlementEconomicsDigestMismatch { event_id } => Self::single(
- SdkOrderStatusIssueKind::SettlementEconomicsDigestMismatch,
- event_id,
- ),
- RadrootsOrderIssue::SettlementAmountMismatch { event_id } => {
- Self::single(SdkOrderStatusIssueKind::SettlementAmountMismatch, event_id)
- }
- RadrootsOrderIssue::SettlementCurrencyMismatch { event_id } => Self::single(
- SdkOrderStatusIssueKind::SettlementCurrencyMismatch,
- event_id,
- ),
- RadrootsOrderIssue::DuplicateSettlements { event_ids } => {
- Self::new(SdkOrderStatusIssueKind::DuplicateSettlements, event_ids)
- }
RadrootsOrderIssue::ForkedLifecycle { event_ids } => {
Self::new(SdkOrderStatusIssueKind::ForkedLifecycle, event_ids)
}
diff --git a/crates/sdk/tests/orders_runtime.rs b/crates/sdk/tests/orders_runtime.rs
@@ -13,9 +13,8 @@ use radroots_events::{
draft::{RadrootsFrozenEventDraft, RadrootsSignedNostrEvent},
ids::{RadrootsEventId, RadrootsOrderId},
kinds::{
- KIND_LISTING, KIND_ORDER_CANCELLATION, KIND_ORDER_DECISION, KIND_ORDER_FULFILLMENT_UPDATE,
- KIND_ORDER_RECEIPT, KIND_ORDER_REQUEST, KIND_ORDER_REVISION_DECISION,
- KIND_ORDER_REVISION_PROPOSAL,
+ KIND_LISTING, KIND_ORDER_CANCELLATION, KIND_ORDER_DECISION, KIND_ORDER_REQUEST,
+ KIND_ORDER_REVISION_DECISION, KIND_ORDER_REVISION_PROPOSAL,
},
};
use radroots_nostr::prelude::{
@@ -28,22 +27,18 @@ use radroots_sdk::protocol::events::RadrootsNostrEventPtr;
use radroots_sdk::protocol::order::{
RadrootsListingAddress, RadrootsOrderCancellation, RadrootsOrderDecision,
RadrootsOrderDecisionOutcome, RadrootsOrderEconomicItem, RadrootsOrderEconomicLine,
- RadrootsOrderEconomics, RadrootsOrderFulfillmentState, RadrootsOrderFulfillmentUpdate,
- RadrootsOrderInventoryCommitment, RadrootsOrderItem, RadrootsOrderPricingBasis,
- RadrootsOrderReceipt, RadrootsOrderRequest, RadrootsOrderRevisionDecision,
+ RadrootsOrderEconomics, RadrootsOrderInventoryCommitment, RadrootsOrderItem,
+ RadrootsOrderPricingBasis, RadrootsOrderRequest, RadrootsOrderRevisionDecision,
RadrootsOrderRevisionOutcome, RadrootsOrderRevisionProposal,
};
use radroots_sdk::protocol::wire::WireEventParts;
use radroots_sdk::{
ORDER_CANCELLATION_OPERATION_KIND, ORDER_DECISION_OPERATION_KIND,
- ORDER_FULFILLMENT_UPDATE_OPERATION_KIND, ORDER_RECEIPT_RECORD_OPERATION_KIND,
ORDER_REVISION_DECISION_OPERATION_KIND, ORDER_REVISION_PROPOSAL_OPERATION_KIND,
ORDER_STATUS_DEFAULT_LIMIT, ORDER_STATUS_MAX_LIMIT, ORDER_SUBMIT_OPERATION_KIND,
OrderCancellationEnqueueRequest, OrderDecisionEnqueueRequest, OrderDecisionPrepareRequest,
- OrderEvidenceIngestRequest, OrderFulfillmentStatusKind, OrderFulfillmentUpdateEnqueueRequest,
- OrderPaymentHandoffKind, OrderPaymentStateKind, OrderReceiptRecordEnqueueRequest,
- OrderRequestEvidenceIngestRequest, OrderRevisionDecisionEnqueueRequest,
- OrderRevisionProposalEnqueueRequest, OrderSettlementStateKind, OrderStatusKind,
+ OrderEvidenceIngestRequest, OrderRequestEvidenceIngestRequest,
+ OrderRevisionDecisionEnqueueRequest, OrderRevisionProposalEnqueueRequest, OrderStatusKind,
OrderStatusNextActionKind, OrderStatusRequest, OrderSubmitEnqueueRequest,
OrderSubmitPrepareRequest, OrderWorkflowKind, PushOutboxEventState, PushOutboxRelayOutcomeKind,
PushOutboxRequest, RadrootsSdk, RadrootsSdkError, RadrootsSdkPartialLocalMutationFailure,
@@ -941,19 +936,6 @@ fn order_revision_decision(
}
}
-fn order_fulfillment_update(
- raw_order_id: &str,
- status: RadrootsOrderFulfillmentState,
-) -> RadrootsOrderFulfillmentUpdate {
- RadrootsOrderFulfillmentUpdate {
- order_id: order_id(raw_order_id),
- listing_addr: listing_address(),
- buyer_pubkey: BUYER_PUBLIC_KEY_HEX.parse().expect("buyer pubkey"),
- seller_pubkey: SELLER_PUBLIC_KEY_HEX.parse().expect("seller pubkey"),
- status,
- }
-}
-
fn order_cancellation(raw_order_id: &str) -> RadrootsOrderCancellation {
RadrootsOrderCancellation {
order_id: order_id(raw_order_id),
@@ -964,22 +946,6 @@ fn order_cancellation(raw_order_id: &str) -> RadrootsOrderCancellation {
}
}
-fn order_receipt_record(raw_order_id: &str, received: bool) -> RadrootsOrderReceipt {
- RadrootsOrderReceipt {
- order_id: order_id(raw_order_id),
- listing_addr: listing_address(),
- buyer_pubkey: BUYER_PUBLIC_KEY_HEX.parse().expect("buyer pubkey"),
- seller_pubkey: SELLER_PUBLIC_KEY_HEX.parse().expect("seller pubkey"),
- received,
- issue: if received {
- None
- } else {
- Some("missing one item".to_owned())
- },
- received_at: 1_785_000_000,
- }
-}
-
fn revision_economics() -> RadrootsOrderEconomics {
RadrootsOrderEconomics {
quote_id: "revision-quote-1".parse().expect("revision quote id"),
@@ -1841,33 +1807,19 @@ async fn order_decision_enqueue_rejects_existing_decision_state_before_mutation(
}
#[tokio::test]
-async fn order_revision_order_fulfillment_order_receipt_lifecycle_enqueue_updates_status() {
+async fn order_revision_lifecycle_accepts_proposal_and_finalizes_agreement() {
let (_tempdir, sdk, store) = directory_sdk_and_store().await;
- let request_event = signed_order_request_event("order-lifecycle-complete", 50);
+ let request_event = signed_order_request_event("order-lifecycle-agreement", 50);
let request_event_id = RadrootsEventId::parse(request_event.id.as_str()).expect("request id");
store
.ingest_event(RadrootsEventIngest::new(request_event.clone(), 5_000))
.await
.expect("ingest request");
- let decision_receipt = sdk
- .orders()
- .enqueue_decision(
- OrderDecisionEnqueueRequest::new(
- seller_actor(),
- request_event_ptr(&request_event),
- order_decision("order-lifecycle-complete"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
- )
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
- .expect("decision target relays"),
- &FixtureSigner::new(SELLER_SECRET_KEY_HEX),
- )
- .await
- .expect("enqueue decision");
+
let proposal = order_revision_proposal(
- "order-lifecycle-complete",
+ "order-lifecycle-agreement",
+ &request_event_id,
&request_event_id,
- &decision_receipt.signed_event_id,
);
let proposal_receipt = sdk
.orders()
@@ -1875,7 +1827,7 @@ async fn order_revision_order_fulfillment_order_receipt_lifecycle_enqueue_update
OrderRevisionProposalEnqueueRequest::new(
seller_actor(),
request_event_ptr(&request_event),
- order_event_ptr(&decision_receipt.signed_event_id),
+ request_event_ptr(&request_event),
proposal.clone(),
SdkRelayTargetPolicy::UseConfiguredRelays,
)
@@ -1941,94 +1893,13 @@ async fn order_revision_order_fulfillment_order_receipt_lifecycle_enqueue_update
KIND_ORDER_REVISION_DECISION
);
- let fulfillment_receipt = sdk
- .orders()
- .enqueue_fulfillment_update(
- OrderFulfillmentUpdateEnqueueRequest::new(
- seller_actor(),
- request_event_ptr(&request_event),
- order_event_ptr(&revision_decision_receipt.signed_event_id),
- order_fulfillment_update(
- "order-lifecycle-complete",
- RadrootsOrderFulfillmentState::ReadyForPickup,
- ),
- SdkRelayTargetPolicy::UseConfiguredRelays,
- )
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
- .expect("fulfillment target relays"),
- &FixtureSigner::new(SELLER_SECRET_KEY_HEX),
- )
- .await
- .expect("enqueue fulfillment");
- assert_eq!(
- outbox_operation_kind(&sdk, fulfillment_receipt.outbox_operation_id).await,
- ORDER_FULFILLMENT_UPDATE_OPERATION_KIND
- );
- assert_eq!(
- store
- .get_event(fulfillment_receipt.signed_event_id.as_str())
- .await
- .expect("fulfillment lookup")
- .expect("fulfillment")
- .kind,
- KIND_ORDER_FULFILLMENT_UPDATE
- );
-
- let receipt = sdk
- .orders()
- .enqueue_receipt_record(
- OrderReceiptRecordEnqueueRequest::new(
- buyer_actor(),
- request_event_ptr(&request_event),
- order_event_ptr(&fulfillment_receipt.signed_event_id),
- order_receipt_record("order-lifecycle-complete", true),
- SdkRelayTargetPolicy::UseConfiguredRelays,
- )
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
- .expect("receipt target relays"),
- &FixtureSigner::new(BUYER_SECRET_KEY_HEX),
- )
- .await
- .expect("enqueue receipt");
- assert_eq!(
- outbox_operation_kind(&sdk, receipt.outbox_operation_id).await,
- ORDER_RECEIPT_RECORD_OPERATION_KIND
- );
- assert_eq!(
- store
- .get_event(receipt.signed_event_id.as_str())
- .await
- .expect("receipt lookup")
- .expect("receipt")
- .kind,
- KIND_ORDER_RECEIPT
- );
-
let status = sdk
.orders()
- .status(status_request("order-lifecycle-complete"))
+ .status(status_request("order-lifecycle-agreement"))
.await
.expect("status");
- assert_eq!(status.status, OrderStatusKind::Completed);
- assert_eq!(status.event_count, 6);
- assert_eq!(
- status
- .fulfillment_event_id
- .as_ref()
- .map(RadrootsEventId::as_str),
- Some(fulfillment_receipt.signed_event_id.as_str())
- );
- assert_eq!(
- status
- .receipt_event_id
- .as_ref()
- .map(RadrootsEventId::as_str),
- Some(receipt.signed_event_id.as_str())
- );
- assert_eq!(
- status.fulfillment_status,
- Some(OrderFulfillmentStatusKind::ReadyForPickup)
- );
+ assert_eq!(status.status, OrderStatusKind::Accepted);
+ assert_eq!(status.event_count, 3);
assert_eq!(
status
.agreement_event_id
@@ -2036,25 +1907,31 @@ async fn order_revision_order_fulfillment_order_receipt_lifecycle_enqueue_update
.map(RadrootsEventId::as_str),
Some(revision_decision_receipt.signed_event_id.as_str())
);
+ assert!(status.decision_event_id.is_none());
+ assert!(status.cancellation_event_id.is_none());
assert_eq!(status.pending_revision_event_id, None);
- assert!(status.lifecycle_terminal);
+ assert_eq!(status.listing_addr, Some(listing_address()));
assert_eq!(
- status.payment_handoff,
- OrderPaymentHandoffKind::InPersonOrOffPlatformPending
+ status.buyer_pubkey.as_ref().map(ToString::to_string),
+ Some(BUYER_PUBLIC_KEY_HEX.to_owned())
);
+ assert_eq!(
+ status.seller_pubkey.as_ref().map(ToString::to_string),
+ Some(SELLER_PUBLIC_KEY_HEX.to_owned())
+ );
+ assert_eq!(status.economics, Some(revision_economics()));
+ assert!(status.lifecycle_terminal);
assert_eq!(status.next_action, OrderStatusNextActionKind::Terminal);
assert!(status.evidence.has_request);
- assert!(status.evidence.has_decision);
+ assert!(!status.evidence.has_decision);
assert!(status.evidence.has_agreement);
- assert!(status.evidence.has_fulfillment);
- assert!(status.evidence.has_receipt);
+ assert!(!status.evidence.has_pending_revision);
+ assert!(!status.evidence.has_cancellation);
assert!(!status.evidence.has_issues);
assert!(!status.eligibility.can_decide);
assert!(!status.eligibility.can_propose_revision);
assert!(!status.eligibility.can_decide_revision);
assert!(!status.eligibility.can_cancel);
- assert!(!status.eligibility.can_update_fulfillment);
- assert!(!status.eligibility.can_record_receipt);
assert!(status.issues.is_empty());
}
@@ -2067,25 +1944,10 @@ async fn order_revision_proposal_status_exposes_pending_and_blocks_follow_on_lif
.ingest_event(RadrootsEventIngest::new(request_event.clone(), 5_500))
.await
.expect("ingest request");
- let decision_receipt = sdk
- .orders()
- .enqueue_decision(
- OrderDecisionEnqueueRequest::new(
- seller_actor(),
- request_event_ptr(&request_event),
- order_decision("order-lifecycle-pending-revision"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
- )
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
- .expect("decision target relays"),
- &FixtureSigner::new(SELLER_SECRET_KEY_HEX),
- )
- .await
- .expect("enqueue decision");
let proposal = order_revision_proposal(
"order-lifecycle-pending-revision",
&request_event_id,
- &decision_receipt.signed_event_id,
+ &request_event_id,
);
let proposal_receipt = sdk
.orders()
@@ -2093,7 +1955,7 @@ async fn order_revision_proposal_status_exposes_pending_and_blocks_follow_on_lif
OrderRevisionProposalEnqueueRequest::new(
seller_actor(),
request_event_ptr(&request_event),
- order_event_ptr(&decision_receipt.signed_event_id),
+ request_event_ptr(&request_event),
proposal,
SdkRelayTargetPolicy::UseConfiguredRelays,
)
@@ -2109,15 +1971,9 @@ async fn order_revision_proposal_status_exposes_pending_and_blocks_follow_on_lif
.status(status_request("order-lifecycle-pending-revision"))
.await
.expect("status");
- assert_eq!(status.status, OrderStatusKind::Accepted);
- assert_eq!(status.event_count, 3);
- assert_eq!(
- status
- .agreement_event_id
- .as_ref()
- .map(RadrootsEventId::as_str),
- Some(decision_receipt.signed_event_id.as_str())
- );
+ assert_eq!(status.status, OrderStatusKind::Requested);
+ assert_eq!(status.event_count, 2);
+ assert!(status.agreement_event_id.is_none());
assert_eq!(
status
.pending_revision_event_id
@@ -2130,28 +1986,28 @@ async fn order_revision_proposal_status_exposes_pending_and_blocks_follow_on_lif
Some(proposal_receipt.signed_event_id.as_str())
);
assert!(status.issues.is_empty());
+ assert!(!status.eligibility.can_decide);
+ assert!(!status.eligibility.can_propose_revision);
+ assert!(status.eligibility.can_decide_revision);
+ assert!(!status.eligibility.can_cancel);
- let fulfillment_error = sdk
+ let decision_error = sdk
.orders()
- .enqueue_fulfillment_update(
- OrderFulfillmentUpdateEnqueueRequest::new(
+ .enqueue_decision(
+ OrderDecisionEnqueueRequest::new(
seller_actor(),
request_event_ptr(&request_event),
- order_event_ptr(&proposal_receipt.signed_event_id),
- order_fulfillment_update(
- "order-lifecycle-pending-revision",
- RadrootsOrderFulfillmentState::ReadyForPickup,
- ),
+ order_decision("order-lifecycle-pending-revision"),
SdkRelayTargetPolicy::UseConfiguredRelays,
)
.try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
- .expect("fulfillment target relays"),
+ .expect("decision target relays"),
&FixtureSigner::new(SELLER_SECRET_KEY_HEX),
)
.await
- .expect_err("pending revision blocks fulfillment");
+ .expect_err("pending revision blocks direct decision");
assert!(matches!(
- fulfillment_error,
+ decision_error,
RadrootsSdkError::InvalidRequest { .. }
));
@@ -2186,12 +2042,12 @@ async fn order_revision_proposal_status_exposes_pending_and_blocks_follow_on_lif
.await
.expect("event store status")
.total_events,
- 3
+ 2
);
}
#[tokio::test]
-async fn order_declined_revision_clears_pending_and_allows_follow_on_lifecycle() {
+async fn order_declined_revision_finalizes_declined_negotiation() {
let (_tempdir, sdk, store) = directory_sdk_and_store().await;
let request_event = signed_order_request_event("order-lifecycle-declined-revision", 56);
let request_event_id = RadrootsEventId::parse(request_event.id.as_str()).expect("request id");
@@ -2199,25 +2055,10 @@ async fn order_declined_revision_clears_pending_and_allows_follow_on_lifecycle()
.ingest_event(RadrootsEventIngest::new(request_event.clone(), 5_600))
.await
.expect("ingest request");
- let decision_receipt = sdk
- .orders()
- .enqueue_decision(
- OrderDecisionEnqueueRequest::new(
- seller_actor(),
- request_event_ptr(&request_event),
- order_decision("order-lifecycle-declined-revision"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
- )
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
- .expect("decision target relays"),
- &FixtureSigner::new(SELLER_SECRET_KEY_HEX),
- )
- .await
- .expect("enqueue decision");
let proposal = order_revision_proposal(
"order-lifecycle-declined-revision",
&request_event_id,
- &decision_receipt.signed_event_id,
+ &request_event_id,
);
let proposal_receipt = sdk
.orders()
@@ -2225,7 +2066,7 @@ async fn order_declined_revision_clears_pending_and_allows_follow_on_lifecycle()
OrderRevisionProposalEnqueueRequest::new(
seller_actor(),
request_event_ptr(&request_event),
- order_event_ptr(&decision_receipt.signed_event_id),
+ request_event_ptr(&request_event),
proposal.clone(),
SdkRelayTargetPolicy::UseConfiguredRelays,
)
@@ -2264,20 +2105,26 @@ async fn order_declined_revision_clears_pending_and_allows_follow_on_lifecycle()
.status(status_request("order-lifecycle-declined-revision"))
.await
.expect("status");
- assert_eq!(status.status, OrderStatusKind::Accepted);
- assert_eq!(status.event_count, 4);
+ assert_eq!(status.status, OrderStatusKind::Declined);
+ assert_eq!(status.event_count, 3);
+ assert!(status.agreement_event_id.is_none());
assert_eq!(
status
- .agreement_event_id
+ .pending_revision_event_id
.as_ref()
.map(RadrootsEventId::as_str),
- Some(decision_receipt.signed_event_id.as_str())
+ Some(proposal_receipt.signed_event_id.as_str())
);
- assert_eq!(status.pending_revision_event_id, None);
assert_eq!(
status.last_event_id.as_ref().map(RadrootsEventId::as_str),
Some(declined_revision_receipt.signed_event_id.as_str())
);
+ assert!(status.lifecycle_terminal);
+ assert_eq!(status.next_action, OrderStatusNextActionKind::Terminal);
+ assert!(!status.eligibility.can_decide);
+ assert!(!status.eligibility.can_propose_revision);
+ assert!(!status.eligibility.can_decide_revision);
+ assert!(!status.eligibility.can_cancel);
let second_decision = order_revision_decision(
&proposal,
@@ -2310,60 +2157,8 @@ async fn order_declined_revision_clears_pending_and_allows_follow_on_lifecycle()
.await
.expect("event store status")
.total_events,
- 4
- );
-
- let fulfillment_receipt = sdk
- .orders()
- .enqueue_fulfillment_update(
- OrderFulfillmentUpdateEnqueueRequest::new(
- seller_actor(),
- request_event_ptr(&request_event),
- order_event_ptr(&declined_revision_receipt.signed_event_id),
- order_fulfillment_update(
- "order-lifecycle-declined-revision",
- RadrootsOrderFulfillmentState::ReadyForPickup,
- ),
- SdkRelayTargetPolicy::UseConfiguredRelays,
- )
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
- .expect("fulfillment target relays"),
- &FixtureSigner::new(SELLER_SECRET_KEY_HEX),
- )
- .await
- .expect("enqueue fulfillment");
-
- let status = sdk
- .orders()
- .status(status_request("order-lifecycle-declined-revision"))
- .await
- .expect("status");
- assert_eq!(status.status, OrderStatusKind::Accepted);
- assert_eq!(status.event_count, 5);
- assert_eq!(
- status
- .agreement_event_id
- .as_ref()
- .map(RadrootsEventId::as_str),
- Some(decision_receipt.signed_event_id.as_str())
- );
- assert_eq!(status.pending_revision_event_id, None);
- assert_eq!(
- status
- .fulfillment_event_id
- .as_ref()
- .map(RadrootsEventId::as_str),
- Some(fulfillment_receipt.signed_event_id.as_str())
- );
- assert_eq!(
- status.last_event_id.as_ref().map(RadrootsEventId::as_str),
- Some(fulfillment_receipt.signed_event_id.as_str())
- );
- assert_eq!(
- status.fulfillment_status,
- Some(OrderFulfillmentStatusKind::ReadyForPickup)
+ 3
);
- assert!(status.issues.is_empty());
}
#[tokio::test]
@@ -2375,28 +2170,13 @@ async fn order_cancel_lifecycle_enqueue_updates_status() {
.ingest_event(RadrootsEventIngest::new(request_event.clone(), 6_000))
.await
.expect("ingest request");
- let decision_receipt = sdk
- .orders()
- .enqueue_decision(
- OrderDecisionEnqueueRequest::new(
- seller_actor(),
- request_event_ptr(&request_event),
- order_decision("order-lifecycle-cancel"),
- SdkRelayTargetPolicy::UseConfiguredRelays,
- )
- .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
- .expect("decision target relays"),
- &FixtureSigner::new(SELLER_SECRET_KEY_HEX),
- )
- .await
- .expect("enqueue decision");
let cancellation = sdk
.orders()
.enqueue_cancellation(
OrderCancellationEnqueueRequest::new(
buyer_actor(),
request_event_ptr(&request_event),
- order_event_ptr(&decision_receipt.signed_event_id),
+ request_event_ptr(&request_event),
order_cancellation("order-lifecycle-cancel"),
SdkRelayTargetPolicy::UseConfiguredRelays,
)
@@ -2410,10 +2190,7 @@ async fn order_cancel_lifecycle_enqueue_updates_status() {
.expect("enqueue cancellation");
assert_eq!(cancellation.root_event_id, request_event_id);
- assert_eq!(
- cancellation.previous_event_id,
- decision_receipt.signed_event_id
- );
+ assert_eq!(cancellation.previous_event_id, request_event_id);
assert_eq!(
outbox_operation_kind(&sdk, cancellation.outbox_operation_id).await,
ORDER_CANCELLATION_OPERATION_KIND
@@ -2433,7 +2210,7 @@ async fn order_cancel_lifecycle_enqueue_updates_status() {
OrderCancellationEnqueueRequest::new(
buyer_actor(),
request_event_ptr(&request_event),
- order_event_ptr(&decision_receipt.signed_event_id),
+ request_event_ptr(&request_event),
order_cancellation("order-lifecycle-cancel"),
SdkRelayTargetPolicy::UseConfiguredRelays,
)
@@ -2462,14 +2239,12 @@ async fn order_cancel_lifecycle_enqueue_updates_status() {
Some(cancellation.signed_event_id.as_str())
);
assert!(status.lifecycle_terminal);
- assert_eq!(status.payment_handoff, OrderPaymentHandoffKind::NotRequired);
assert_eq!(status.next_action, OrderStatusNextActionKind::Terminal);
assert!(status.evidence.has_request);
- assert!(status.evidence.has_decision);
+ assert!(!status.evidence.has_decision);
assert!(status.evidence.has_cancellation);
assert!(!status.evidence.has_issues);
assert!(!status.eligibility.can_cancel);
- assert!(!status.eligibility.can_update_fulfillment);
assert!(status.issues.is_empty());
}
@@ -2480,14 +2255,15 @@ async fn order_lifecycle_enqueue_rejects_invalid_state_before_mutation() {
let request_event_id = RadrootsEventId::parse(request_event.id.as_str()).expect("request id");
let missing = sdk
.orders()
- .enqueue_fulfillment_update(
- OrderFulfillmentUpdateEnqueueRequest::new(
+ .enqueue_revision_proposal(
+ OrderRevisionProposalEnqueueRequest::new(
seller_actor(),
request_event_ptr(&request_event),
- order_event_ptr(&request_event_id),
- order_fulfillment_update(
+ request_event_ptr(&request_event),
+ order_revision_proposal(
"order-lifecycle-invalid",
- RadrootsOrderFulfillmentState::ReadyForPickup,
+ &request_event_id,
+ &request_event_id,
),
SdkRelayTargetPolicy::UseConfiguredRelays,
)
@@ -2556,24 +2332,24 @@ async fn order_lifecycle_enqueue_rejects_invalid_state_before_mutation() {
RadrootsSdkError::InvalidRequest { .. }
));
- let receipt_error = sdk
+ let cancellation_error = sdk
.orders()
- .enqueue_receipt_record(
- OrderReceiptRecordEnqueueRequest::new(
+ .enqueue_cancellation(
+ OrderCancellationEnqueueRequest::new(
buyer_actor(),
request_event_ptr(&request_event),
order_event_ptr(&decision_receipt.signed_event_id),
- order_receipt_record("order-lifecycle-invalid", true),
+ order_cancellation("order-lifecycle-invalid"),
SdkRelayTargetPolicy::UseConfiguredRelays,
)
.try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public)
- .expect("receipt target relays"),
+ .expect("cancellation target relays"),
&FixtureSigner::new(BUYER_SECRET_KEY_HEX),
)
.await
- .expect_err("receipt without fulfillment");
+ .expect_err("cancellation after accepted agreement");
assert!(matches!(
- receipt_error,
+ cancellation_error,
RadrootsSdkError::InvalidRequest { .. }
));
assert_eq!(
@@ -2602,12 +2378,10 @@ async fn order_status_returns_not_found_for_missing_local_order() {
assert_eq!(receipt.limit_applied, ORDER_STATUS_DEFAULT_LIMIT);
assert!(receipt.event_ids.is_empty());
assert_eq!(receipt.status, OrderStatusKind::Missing);
- assert_eq!(receipt.payment_state, OrderPaymentStateKind::NotRecorded);
- assert_eq!(
- receipt.settlement_state,
- OrderSettlementStateKind::NotRequired
- );
- assert_eq!(receipt.payment_handoff, OrderPaymentHandoffKind::NotReady);
+ assert!(receipt.listing_addr.is_none());
+ assert!(receipt.buyer_pubkey.is_none());
+ assert!(receipt.seller_pubkey.is_none());
+ assert!(receipt.economics.is_none());
assert_eq!(receipt.next_action, OrderStatusNextActionKind::NoLocalOrder);
assert_eq!(receipt.evidence.event_count, 0);
assert_eq!(receipt.evidence.limit_applied, ORDER_STATUS_DEFAULT_LIMIT);
@@ -2615,7 +2389,8 @@ async fn order_status_returns_not_found_for_missing_local_order() {
assert!(!receipt.evidence.has_issues);
assert!(!receipt.eligibility.can_decide);
assert!(!receipt.eligibility.can_cancel);
- assert!(!receipt.eligibility.can_update_fulfillment);
+ assert!(!receipt.eligibility.can_propose_revision);
+ assert!(!receipt.eligibility.can_decide_revision);
assert!(receipt.issues.is_empty());
}
@@ -2678,15 +2453,18 @@ async fn order_status_contract_dtos_serialize_deterministically() {
assert_eq!(receipt_json["source"], "local_event_store");
assert_eq!(receipt_json["status"], "missing");
- assert_eq!(receipt_json["payment_state"], "not_recorded");
- assert_eq!(receipt_json["settlement_state"], "not_required");
- assert_eq!(receipt_json["payment_handoff"], "not_ready");
+ assert_eq!(receipt_json["listing_addr"], serde_json::Value::Null);
+ assert_eq!(receipt_json["buyer_pubkey"], serde_json::Value::Null);
+ assert_eq!(receipt_json["seller_pubkey"], serde_json::Value::Null);
+ assert_eq!(receipt_json["economics"], serde_json::Value::Null);
assert_eq!(receipt_json["next_action"], "no_local_order");
assert_eq!(receipt_json["evidence"]["event_count"], 0);
assert_eq!(receipt_json["evidence"]["limit_applied"], 25);
assert_eq!(receipt_json["evidence"]["has_request"], false);
assert_eq!(receipt_json["eligibility"]["can_decide"], false);
assert_eq!(receipt_json["eligibility"]["can_cancel"], false);
+ assert_eq!(receipt_json["eligibility"]["can_propose_revision"], false);
+ assert_eq!(receipt_json["eligibility"]["can_decide_revision"], false);
let issue = SdkOrderStatusIssue {
kind: SdkOrderStatusIssueKind::DecisionPayloadInvalid,
@@ -2758,16 +2536,19 @@ async fn order_status_projects_local_request_and_decision_events() {
receipt.last_event_id.as_ref().map(RadrootsEventId::as_str),
Some(decision_event.id.as_str())
);
- assert!(receipt.issues.is_empty());
- assert!(!receipt.lifecycle_terminal);
+ assert_eq!(receipt.listing_addr, Some(listing_address()));
assert_eq!(
- receipt.payment_handoff,
- OrderPaymentHandoffKind::InPersonOrOffPlatformPending
+ receipt.buyer_pubkey.as_ref().map(ToString::to_string),
+ Some(BUYER_PUBLIC_KEY_HEX.to_owned())
);
assert_eq!(
- receipt.next_action,
- OrderStatusNextActionKind::ArrangeInPersonOrOffPlatformPayment
+ receipt.seller_pubkey.as_ref().map(ToString::to_string),
+ Some(SELLER_PUBLIC_KEY_HEX.to_owned())
);
+ assert_eq!(receipt.economics, Some(economics()));
+ assert!(receipt.issues.is_empty());
+ assert!(receipt.lifecycle_terminal);
+ assert_eq!(receipt.next_action, OrderStatusNextActionKind::Terminal);
assert_eq!(receipt.evidence.event_count, 2);
assert!(receipt.evidence.has_request);
assert!(receipt.evidence.has_decision);
@@ -2775,11 +2556,9 @@ async fn order_status_projects_local_request_and_decision_events() {
assert!(!receipt.evidence.has_pending_revision);
assert!(!receipt.evidence.has_issues);
assert!(!receipt.eligibility.can_decide);
- assert!(receipt.eligibility.can_propose_revision);
+ assert!(!receipt.eligibility.can_propose_revision);
assert!(!receipt.eligibility.can_decide_revision);
- assert!(receipt.eligibility.can_cancel);
- assert!(receipt.eligibility.can_update_fulfillment);
- assert!(!receipt.eligibility.can_record_receipt);
+ assert!(!receipt.eligibility.can_cancel);
}
#[tokio::test]
diff --git a/crates/sdk/tests/source_boundary.rs b/crates/sdk/tests/source_boundary.rs
@@ -42,8 +42,6 @@ const FORBIDDEN_SDK_SOURCE_CONCEPTS: &[ForbiddenSdkConcept] = &[
const REQUIRED_ORDER_RUNTIME_EXPORTS: &[&str] = &[
"ORDER_CANCELLATION_OPERATION_KIND",
"ORDER_DECISION_OPERATION_KIND",
- "ORDER_FULFILLMENT_UPDATE_OPERATION_KIND",
- "ORDER_RECEIPT_RECORD_OPERATION_KIND",
"ORDER_REVISION_DECISION_OPERATION_KIND",
"ORDER_REVISION_PROPOSAL_OPERATION_KIND",
"ORDER_STATUS_DEFAULT_LIMIT",
@@ -59,17 +57,6 @@ const REQUIRED_ORDER_RUNTIME_EXPORTS: &[&str] = &[
"OrderDecisionReceipt",
"OrderEvidenceIngestReceipt",
"OrderEvidenceIngestRequest",
- "OrderFulfillmentStatusKind",
- "OrderFulfillmentUpdateEnqueueRequest",
- "OrderFulfillmentUpdatePlan",
- "OrderFulfillmentUpdatePrepareRequest",
- "OrderFulfillmentUpdateReceipt",
- "OrderPaymentHandoffKind",
- "OrderPaymentStateKind",
- "OrderReceiptRecordEnqueueRequest",
- "OrderReceiptRecordPlan",
- "OrderReceiptRecordPrepareRequest",
- "OrderReceiptRecordReceipt",
"OrderRequestEvidenceIngestReceipt",
"OrderRequestEvidenceIngestRequest",
"OrderRevisionDecisionEnqueueRequest",
@@ -80,7 +67,6 @@ const REQUIRED_ORDER_RUNTIME_EXPORTS: &[&str] = &[
"OrderRevisionProposalPlan",
"OrderRevisionProposalPrepareRequest",
"OrderRevisionProposalReceipt",
- "OrderSettlementStateKind",
"OrderStatusEligibility",
"OrderStatusEvidenceSummary",
"OrderStatusKind",
@@ -119,43 +105,57 @@ const REQUIRED_ORDERS_CLIENT_METHODS: &[&str] = &[
"pub fn prepare_cancellation(",
"pub async fn enqueue_cancellation<",
"pub async fn enqueue_prepared_cancellation<",
- "pub fn prepare_fulfillment_update(",
- "pub async fn enqueue_fulfillment_update<",
- "pub async fn enqueue_prepared_fulfillment_update<",
- "pub fn prepare_receipt_record(",
- "pub async fn enqueue_receipt_record<",
- "pub async fn enqueue_prepared_receipt_record<",
"pub async fn status(",
];
-const FORBIDDEN_PAYMENT_WRITE_PUBLIC_EXPORTS: &[&str] = &[
+const FORBIDDEN_ORDER_RUNTIME_PUBLIC_EXPORTS: &[&str] = &[
"CheckoutClient",
"EscrowClient",
"InvoiceClient",
- "OrderPaymentRecordEnqueueRequest",
- "OrderPaymentRecordPrepareRequest",
- "OrderPaymentRecordReceipt",
- "OrderSettlementDecisionEnqueueRequest",
- "OrderSettlementDecisionPrepareRequest",
- "OrderSettlementDecisionReceipt",
+ concat!("Order", "FulfillmentStatusKind"),
+ concat!("Order", "FulfillmentUpdateEnqueueRequest"),
+ concat!("Order", "FulfillmentUpdatePlan"),
+ concat!("Order", "FulfillmentUpdatePrepareRequest"),
+ concat!("Order", "FulfillmentUpdateReceipt"),
+ concat!("Order", "Payment", "HandoffKind"),
+ concat!("Order", "PaymentRecordEnqueueRequest"),
+ concat!("Order", "PaymentRecordPrepareRequest"),
+ concat!("Order", "Payment", "Record", "Receipt"),
+ concat!("Order", "Payment", "StateKind"),
+ concat!("Order", "ReceiptRecordEnqueueRequest"),
+ concat!("Order", "ReceiptRecordPlan"),
+ concat!("Order", "ReceiptRecordPrepareRequest"),
+ concat!("Order", "ReceiptRecord", "Receipt"),
+ concat!("Order", "SettlementDecisionEnqueueRequest"),
+ concat!("Order", "SettlementDecisionPrepareRequest"),
+ concat!("Order", "SettlementDecisionReceipt"),
+ concat!("Order", "Settlement", "StateKind"),
"PaymentClient",
"PaymentsClient",
"RefundClient",
"WalletClient",
+ "ORDER_FULFILLMENT_UPDATE_OPERATION_KIND",
"ORDER_PAYMENT_RECORD_OPERATION_KIND",
+ "ORDER_RECEIPT_RECORD_OPERATION_KIND",
"ORDER_SETTLEMENT_DECISION_OPERATION_KIND",
];
-const FORBIDDEN_PAYMENT_WRITE_ORDER_METHODS: &[&str] = &[
+const FORBIDDEN_ORDER_RUNTIME_METHODS: &[&str] = &[
"accept_settlement",
"checkout",
+ "enqueue_fulfillment",
"enqueue_payment",
+ "enqueue_receipt_record",
"enqueue_settlement",
"escrow",
+ "fulfillment",
"invoice",
"payment_provider",
+ "prepare_fulfillment",
"prepare_payment",
+ "prepare_receipt_record",
"prepare_settlement",
+ "receipt_record",
"record_payment",
"refund",
"reject_settlement",
@@ -210,7 +210,7 @@ fn migrated_runtime_tests_stay_on_product_runtime_boundary() {
}
#[test]
-fn payment_deferral_keeps_sdk_public_runtime_surface_passive_only() {
+fn agreement_order_runtime_excludes_post_agreement_surfaces() {
let lib_source = read_source(
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("src/lib.rs")
@@ -222,28 +222,17 @@ fn payment_deferral_keeps_sdk_public_runtime_surface_passive_only() {
.as_path(),
);
- for passive_export in [
- "OrderPaymentHandoffKind",
- "OrderPaymentStateKind",
- "OrderSettlementStateKind",
- ] {
- assert!(
- lib_source.contains(passive_export),
- "src/lib.rs must keep passive order payment status export `{passive_export}`"
- );
- }
-
- for forbidden in FORBIDDEN_PAYMENT_WRITE_PUBLIC_EXPORTS {
+ for forbidden in FORBIDDEN_ORDER_RUNTIME_PUBLIC_EXPORTS {
assert!(
!lib_source.contains(forbidden),
- "src/lib.rs must not expose deferred payment write surface `{forbidden}`"
+ "src/lib.rs must not expose unsupported order runtime surface `{forbidden}`"
);
}
- for forbidden in FORBIDDEN_PAYMENT_WRITE_ORDER_METHODS {
+ for forbidden in FORBIDDEN_ORDER_RUNTIME_METHODS {
assert!(
!order_source.contains(forbidden),
- "src/orders_runtime.rs must not add deferred payment write method or capability `{forbidden}`"
+ "src/orders_runtime.rs must not expose unsupported order runtime method or capability `{forbidden}`"
);
}
}