use async_trait::async_trait;
use serde_json::Value;
use std::collections::BTreeMap;
use thiserror::Error;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Money {
pub minor_units: i64,
pub currency: String,
}
impl Money {
#[must_use]
pub fn new(minor_units: i64, currency: impl Into<String>) -> Self {
Self {
minor_units,
currency: currency.into(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LineItem {
pub name: String,
pub amount: Money,
pub quantity: u32,
}
#[derive(Debug, Clone)]
pub struct CheckoutRequest {
pub customer_ref: Option<String>,
pub customer_email: Option<String>,
pub line: LineItem,
pub success_url: String,
pub cancel_url: String,
pub metadata: BTreeMap<String, String>,
pub idempotency_key: String,
}
#[derive(Debug, Clone)]
pub struct SubscriptionCheckoutRequest {
pub customer_ref: Option<String>,
pub customer_email: Option<String>,
pub price_ref: String,
pub trial_days: Option<u32>,
pub success_url: String,
pub cancel_url: String,
pub metadata: BTreeMap<String, String>,
pub idempotency_key: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CheckoutSession {
pub id: String,
pub url: String,
}
#[derive(Debug, Clone)]
pub struct ConnectAccountLinkRequest {
pub account_ref: Option<String>,
pub refresh_url: String,
pub return_url: String,
pub idempotency_key: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ConnectAccountLink {
pub account_id: String,
pub url: String,
}
#[derive(Debug, Clone)]
pub struct TransferCharge {
pub customer_ref: Option<String>,
pub amount: Money,
pub destination_account: String,
pub application_fee: Money,
pub metadata: BTreeMap<String, String>,
pub idempotency_key: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Charge {
pub id: String,
pub status: String,
}
#[derive(Debug, Clone)]
pub struct RefundRequest {
pub payment_ref: String,
pub amount: Option<Money>,
pub idempotency_key: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Refund {
pub id: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WebhookEvent {
pub id: String,
pub kind: String,
pub data: Value,
}
#[derive(Debug, Clone, Error)]
pub enum PaymentsError {
#[error("payments are not configured")]
NotConfigured,
#[error("webhook signature verification failed: {0}")]
SignatureInvalid(String),
#[error("payments request rejected: {0}")]
Rejected(String),
#[error("payments request failed, retryable: {0}")]
Transient(String),
}
#[async_trait]
pub trait Payments: Send + Sync {
async fn create_checkout(
&self,
request: &CheckoutRequest,
) -> Result<CheckoutSession, PaymentsError>;
async fn create_subscription_checkout(
&self,
request: &SubscriptionCheckoutRequest,
) -> Result<CheckoutSession, PaymentsError>;
async fn create_connect_account_link(
&self,
request: &ConnectAccountLinkRequest,
) -> Result<ConnectAccountLink, PaymentsError>;
async fn charge_with_transfer(&self, request: &TransferCharge)
-> Result<Charge, PaymentsError>;
async fn refund(&self, request: &RefundRequest) -> Result<Refund, PaymentsError>;
async fn verify_webhook(
&self,
signature_header: &str,
body: &[u8],
) -> Result<WebhookEvent, PaymentsError>;
}