use std::collections::HashMap;
use std::future::Future;
use compact_str::CompactString;
use r402_facilitator::BoxFuture;
use r402_protocol::error::FacilitatorError;
use r402_protocol::network::ChainId;
use r402_protocol::payment::{
PaymentRequired, PaymentRequirements, ResourceInfo, SupportedPaymentKind, SupportedResponse,
};
use serde_json::{Map, Value};
use crate::hooks::{
SettleContext, SettleResultContext, VerifiedPaymentCanceledContext, WirePaymentPayload,
};
use crate::payment_flow::PaymentFlowConfig;
#[derive(Debug)]
#[non_exhaustive]
pub struct SchemePaymentRequiredContext<'a> {
pub requirements: &'a [PaymentRequirements],
pub payment_payload: Option<&'a WirePaymentPayload>,
pub resource: &'a ResourceInfo,
pub error: Option<&'a str>,
pub payment_required_response: &'a PaymentRequired,
pub supported: &'a SupportedResponse,
pub network: &'a ChainId,
}
impl<'a> SchemePaymentRequiredContext<'a> {
#[must_use]
pub const fn new(
requirements: &'a [PaymentRequirements],
resource: &'a ResourceInfo,
payment_required_response: &'a PaymentRequired,
supported: &'a SupportedResponse,
network: &'a ChainId,
) -> Self {
Self {
requirements,
payment_payload: None,
resource,
error: None,
payment_required_response,
supported,
network,
}
}
}
pub trait SchemeNetworkServer: Send + Sync {
fn scheme(&self) -> &str;
fn default_asset_transfer_method(&self) -> &str;
fn payment_flows(&self) -> &HashMap<String, PaymentFlowConfig>;
fn dynamic_extra_fields(&self) -> &[&str] {
&[]
}
fn enrich_payment_required_response<'a>(
&'a self,
_ctx: &'a SchemePaymentRequiredContext<'a>,
) -> impl Future<Output = Option<Vec<PaymentRequirements>>> + Send + 'a {
async { None }
}
fn enrich_settlement_payload<'a>(
&'a self,
_ctx: &'a SettleContext,
) -> impl Future<Output = Result<Option<Map<String, Value>>, FacilitatorError>> + Send + 'a
{
async { Ok(None) }
}
fn enrich_settlement_response<'a>(
&'a self,
_ctx: &'a SettleResultContext,
) -> impl Future<Output = Option<Map<String, Value>>> + Send + 'a {
async { None }
}
fn settle_on_cancel<'a>(
&'a self,
_ctx: &'a VerifiedPaymentCanceledContext,
) -> impl Future<Output = Option<PaymentRequirements>> + Send + 'a {
async { None }
}
fn settles_on_cancel(&self) -> bool {
false
}
fn validate_facilitator_support(
&self,
network: &ChainId,
kind: &SupportedPaymentKind,
facilitator_extensions: &[CompactString],
) -> Result<(), FacilitatorSupportError> {
let _ = (network, kind, facilitator_extensions);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum FacilitatorSupportError {
#[error("{scheme} on {network}: facilitator does not advertise this kind")]
KindMissing {
scheme: CompactString,
network: ChainId,
},
#[error("{scheme} on {network}: missing extra.feePayer")]
MissingFeePayer {
scheme: CompactString,
network: ChainId,
},
#[error("{scheme} on {network}: invalid extra.feePayer")]
InvalidFeePayer {
scheme: CompactString,
network: ChainId,
},
#[error("{scheme} on {network}: missing extra.receiverAuthorizer")]
MissingReceiverAuthorizer {
scheme: CompactString,
network: ChainId,
},
#[error("{scheme} on {network}: extra.receiverAuthorizer is the zero address")]
ZeroReceiverAuthorizer {
scheme: CompactString,
network: ChainId,
},
#[error("{scheme} on {network}: invalid extra.receiverAuthorizer")]
InvalidReceiverAuthorizer {
scheme: CompactString,
network: ChainId,
},
}
impl FacilitatorSupportError {
#[must_use]
pub const fn reason(&self) -> &'static str {
match self {
Self::KindMissing { .. } => "kind_missing",
Self::MissingFeePayer { .. } => "missing_fee_payer",
Self::InvalidFeePayer { .. } => "invalid_fee_payer",
Self::MissingReceiverAuthorizer { .. } => "missing_receiver_authorizer",
Self::ZeroReceiverAuthorizer { .. } => "zero_receiver_authorizer",
Self::InvalidReceiverAuthorizer { .. } => "invalid_receiver_authorizer",
}
}
}
pub trait DynSchemeNetworkServer: Send + Sync {
fn scheme(&self) -> &str;
fn default_asset_transfer_method(&self) -> &str;
fn payment_flows(&self) -> &HashMap<String, PaymentFlowConfig>;
fn dynamic_extra_fields(&self) -> &[&str];
fn enrich_payment_required_response<'a>(
&'a self,
ctx: &'a SchemePaymentRequiredContext<'a>,
) -> BoxFuture<'a, Option<Vec<PaymentRequirements>>>;
fn enrich_settlement_payload<'a>(
&'a self,
ctx: &'a SettleContext,
) -> BoxFuture<'a, Result<Option<Map<String, Value>>, FacilitatorError>>;
fn enrich_settlement_response<'a>(
&'a self,
ctx: &'a SettleResultContext,
) -> BoxFuture<'a, Option<Map<String, Value>>>;
fn settle_on_cancel<'a>(
&'a self,
ctx: &'a VerifiedPaymentCanceledContext,
) -> BoxFuture<'a, Option<PaymentRequirements>>;
fn settles_on_cancel(&self) -> bool;
fn validate_facilitator_support(
&self,
network: &ChainId,
kind: &SupportedPaymentKind,
facilitator_extensions: &[CompactString],
) -> Result<(), FacilitatorSupportError>;
}
impl<T: SchemeNetworkServer + ?Sized> DynSchemeNetworkServer for T {
fn scheme(&self) -> &str {
<Self as SchemeNetworkServer>::scheme(self)
}
fn default_asset_transfer_method(&self) -> &str {
<Self as SchemeNetworkServer>::default_asset_transfer_method(self)
}
fn payment_flows(&self) -> &HashMap<String, PaymentFlowConfig> {
<Self as SchemeNetworkServer>::payment_flows(self)
}
fn dynamic_extra_fields(&self) -> &[&str] {
<Self as SchemeNetworkServer>::dynamic_extra_fields(self)
}
fn enrich_payment_required_response<'a>(
&'a self,
ctx: &'a SchemePaymentRequiredContext<'a>,
) -> BoxFuture<'a, Option<Vec<PaymentRequirements>>> {
Box::pin(<Self as SchemeNetworkServer>::enrich_payment_required_response(self, ctx))
}
fn enrich_settlement_payload<'a>(
&'a self,
ctx: &'a SettleContext,
) -> BoxFuture<'a, Result<Option<Map<String, Value>>, FacilitatorError>> {
Box::pin(<Self as SchemeNetworkServer>::enrich_settlement_payload(
self, ctx,
))
}
fn enrich_settlement_response<'a>(
&'a self,
ctx: &'a SettleResultContext,
) -> BoxFuture<'a, Option<Map<String, Value>>> {
Box::pin(<Self as SchemeNetworkServer>::enrich_settlement_response(
self, ctx,
))
}
fn settle_on_cancel<'a>(
&'a self,
ctx: &'a VerifiedPaymentCanceledContext,
) -> BoxFuture<'a, Option<PaymentRequirements>> {
Box::pin(<Self as SchemeNetworkServer>::settle_on_cancel(self, ctx))
}
fn settles_on_cancel(&self) -> bool {
<Self as SchemeNetworkServer>::settles_on_cancel(self)
}
fn validate_facilitator_support(
&self,
network: &ChainId,
kind: &SupportedPaymentKind,
facilitator_extensions: &[CompactString],
) -> Result<(), FacilitatorSupportError> {
<Self as SchemeNetworkServer>::validate_facilitator_support(
self,
network,
kind,
facilitator_extensions,
)
}
}