ic_papi_guard/guards/
any.rs

1//! Accepts any payment that the vendor accepts.
2
3use candid::{CandidType, Deserialize, Principal};
4use ic_papi_api::{
5    caller::{CallerPaysIcrc2Tokens, PatronPaysIcrc2Cycles, PatronPaysIcrc2Tokens, TokenAmount},
6    PaymentError, PaymentType,
7};
8
9use super::{
10    attached_cycles::AttachedCyclesPayment,
11    caller_pays_icrc2_cycles::CallerPaysIcrc2CyclesPaymentGuard,
12    caller_pays_icrc2_tokens::CallerPaysIcrc2TokensPaymentGuard,
13    patron_pays_icrc2_cycles::PatronPaysIcrc2CyclesPaymentGuard,
14    patron_pays_icrc2_tokens::PatronPaysIcrc2TokensPaymentGuard, PaymentGuardTrait,
15};
16
17/// A guard that accepts a user-specified payment type, providing the vendor supports it.
18pub struct PaymentGuard<const CAP: usize> {
19    pub supported: [VendorPaymentConfig; CAP],
20}
21
22/// Vendor payment configuration, including details that may not necessarily be shared with the customer.
23#[derive(Debug, Clone, Eq, PartialEq)]
24pub enum VendorPaymentConfig {
25    /// Cycles are received by the vendor canister.
26    AttachedCycles,
27    /// Cycles are received by the vendor canister.
28    CallerPaysIcrc2Cycles,
29    /// Cycles are received by the vendor canister.
30    PatronPaysIcrc2Cycles,
31    /// The caller pays tokens to the vendor's main account on the chosen ledger.
32    CallerPaysIcrc2Tokens { ledger: Principal },
33    /// A patron pays tokens to a subaccount belonging to the vendor on the chosen ledger.
34    /// - The vendor needs to move the tokens to their main account.
35    PatronPaysIcrc2Tokens { ledger: Principal },
36}
37
38/// A user's requested payment type paired with a vendor's configuration.
39#[derive(Debug, Clone, Eq, PartialEq, CandidType, Deserialize)]
40pub enum PaymentWithConfig {
41    AttachedCycles,
42    CallerPaysIcrc2Cycles,
43    PatronPaysIcrc2Cycles(PatronPaysIcrc2Cycles),
44    CallerPaysIcrc2Tokens(CallerPaysIcrc2Tokens),
45    PatronPaysIcrc2Tokens(PatronPaysIcrc2Tokens),
46}
47
48impl<const CAP: usize> PaymentGuard<CAP> {
49    pub async fn deduct(&self, payment: PaymentType, fee: TokenAmount) -> Result<(), PaymentError> {
50        let payment_config = self
51            .config(payment)
52            .ok_or(PaymentError::UnsupportedPaymentType)?;
53        match payment_config {
54            PaymentWithConfig::AttachedCycles => AttachedCyclesPayment {}.deduct(fee).await,
55            PaymentWithConfig::CallerPaysIcrc2Cycles => {
56                CallerPaysIcrc2CyclesPaymentGuard {}.deduct(fee).await
57            }
58            PaymentWithConfig::PatronPaysIcrc2Cycles(patron) => {
59                PatronPaysIcrc2CyclesPaymentGuard { patron }
60                    .deduct(fee)
61                    .await
62            }
63            PaymentWithConfig::CallerPaysIcrc2Tokens(CallerPaysIcrc2Tokens { ledger }) => {
64                CallerPaysIcrc2TokensPaymentGuard { ledger }
65                    .deduct(fee)
66                    .await
67            }
68            PaymentWithConfig::PatronPaysIcrc2Tokens(payment_type) => {
69                PatronPaysIcrc2TokensPaymentGuard {
70                    ledger: payment_type.ledger,
71                    patron: payment_type.patron,
72                }
73                .deduct(fee)
74                .await
75            }
76        }
77    }
78}
79impl<const CAP: usize> PaymentGuard<CAP> {
80    /// Find the vendor configuration for the offered payment type.
81    #[must_use]
82    pub fn config(&self, payment: PaymentType) -> Option<PaymentWithConfig> {
83        match payment {
84            PaymentType::AttachedCycles => self
85                .supported
86                .iter()
87                .find(|&x| *x == VendorPaymentConfig::AttachedCycles)
88                .map(|_| PaymentWithConfig::AttachedCycles),
89            PaymentType::CallerPaysIcrc2Cycles => self
90                .supported
91                .iter()
92                .find(|&x| *x == VendorPaymentConfig::CallerPaysIcrc2Cycles)
93                .map(|_| PaymentWithConfig::CallerPaysIcrc2Cycles),
94            PaymentType::PatronPaysIcrc2Cycles(patron) => self
95                .supported
96                .iter()
97                .find(|&x| *x == VendorPaymentConfig::PatronPaysIcrc2Cycles)
98                .map(|_| PaymentWithConfig::PatronPaysIcrc2Cycles(patron)),
99            PaymentType::CallerPaysIcrc2Tokens(payment_type) => self
100                .supported
101                .iter()
102                .find(|&x| {
103                    *x == VendorPaymentConfig::CallerPaysIcrc2Tokens {
104                        ledger: payment_type.ledger,
105                    }
106                })
107                .map(|_| PaymentWithConfig::CallerPaysIcrc2Tokens(payment_type)),
108            PaymentType::PatronPaysIcrc2Tokens(payment_type) => self
109                .supported
110                .iter()
111                .find(|&x| {
112                    *x == VendorPaymentConfig::PatronPaysIcrc2Tokens {
113                        ledger: payment_type.ledger,
114                    }
115                })
116                .map(|_| PaymentWithConfig::PatronPaysIcrc2Tokens(payment_type)),
117            _ => None,
118        }
119    }
120}