paykit_lib/allowance/
evaluation_types.rs1use super::AllowanceTerms;
4use crate::{PaymentAmount, PaymentEndpointIdentifier, PaymentRequestTerms, Result};
5use chrono::{DateTime, Utc};
6
7#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
9pub enum AllowanceEvaluationBlock {
10 #[error("Payment Request terms are invalid")]
12 InvalidRequest,
13 #[error("Payment Request asset does not match the Allowance")]
15 AssetMismatch,
16 #[error("Payment Request amount is outside the Allowance range")]
18 AmountOutsideRange,
19 #[error("No Payment Endpoint Identifier is allowed by both request and Allowance")]
21 NoEligibleEndpoint,
22 #[error("Allowance is not active yet")]
24 NotActive,
25 #[error("Allowance has expired")]
27 Expired,
28 #[error("Allowance evaluation time is earlier than its watermark")]
30 ClockRollback,
31 #[error("Allowance usage contains a future admission time")]
33 FutureUsage,
34 #[error("Allowance usage asset does not match its terms")]
36 UsageAssetMismatch,
37 #[error("Allowance period {index} amount ceiling would be exceeded")]
39 PeriodAmountLimit {
40 index: usize,
42 },
43 #[error("Allowance period {index} payment count would be exceeded")]
45 PeriodCountLimit {
46 index: usize,
48 },
49 #[error("Allowance lifetime amount ceiling would be exceeded")]
51 LifetimeAmountLimit,
52 #[error("Allowance arithmetic or UTC boundary cannot be represented")]
54 ArithmeticOverflow,
55}
56
57impl AllowanceEvaluationBlock {
58 pub fn code(&self) -> &'static str {
60 match self {
61 Self::InvalidRequest => "invalid_request",
62 Self::AssetMismatch => "asset_mismatch",
63 Self::AmountOutsideRange => "amount_outside_range",
64 Self::NoEligibleEndpoint => "no_eligible_endpoint",
65 Self::NotActive => "not_active",
66 Self::Expired => "expired",
67 Self::ClockRollback => "clock_rollback",
68 Self::FutureUsage => "future_usage",
69 Self::UsageAssetMismatch => "usage_asset_mismatch",
70 Self::PeriodAmountLimit { .. } => "period_amount_limit",
71 Self::PeriodCountLimit { .. } => "period_count_limit",
72 Self::LifetimeAmountLimit => "lifetime_amount_limit",
73 Self::ArithmeticOverflow => "arithmetic_overflow",
74 }
75 }
76}
77
78#[derive(Clone, PartialEq, Eq)]
83pub struct AllowanceUsageEntry {
84 amount: PaymentAmount,
85 admitted_at: DateTime<Utc>,
86}
87
88impl std::fmt::Debug for AllowanceUsageEntry {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 f.write_str("AllowanceUsageEntry(<redacted>)")
91 }
92}
93
94impl AllowanceUsageEntry {
95 pub fn new(amount: PaymentAmount, admitted_at: DateTime<Utc>) -> Result<Self> {
97 amount.validate_with_label("Allowance usage amount")?;
98 if admitted_at.timestamp_subsec_nanos() >= 1_000_000_000 {
99 return Err(crate::PaykitError::Validation(
100 "Allowance usage time cannot represent a leap second".into(),
101 ));
102 }
103 Ok(Self {
104 amount,
105 admitted_at,
106 })
107 }
108
109 pub fn amount(&self) -> &PaymentAmount {
111 &self.amount
112 }
113
114 pub fn admitted_at(&self) -> DateTime<Utc> {
116 self.admitted_at
117 }
118}
119
120pub struct AllowanceEvaluationInput<'a> {
127 pub terms: &'a AllowanceTerms,
129 pub request: &'a PaymentRequestTerms,
131 pub trusted_time: DateTime<Utc>,
133 pub watermark: DateTime<Utc>,
135 pub usage: &'a [AllowanceUsageEntry],
137}
138
139#[derive(Clone, Debug, PartialEq, Eq)]
141pub struct AllowanceEvaluation {
142 pub eligible_payment_endpoint_identifiers: Vec<PaymentEndpointIdentifier>,
144}