correlate 0.3.0

correlate is a standalone server that listens for Stripe webhook events and sends notification emails about successful orders.
use super::{CHECKOUT_ENDPOINT, STRIPE_API};
use crate::stripe::checkout::StripeMode;
use rocket::serde::Deserialize;
use ureq::{http::Response, Body, Error};

/// Line item info for a Stripe Checkout order
#[derive(Debug, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct CheckoutLineItem {
    price: String,
    quantity: u32,
}

impl CheckoutLineItem {
    pub fn new(price: String, quantity: u32) -> Self {
        CheckoutLineItem { price, quantity }
    }
}

#[derive(Debug, Deserialize)]
#[serde(crate = "rocket::serde", rename_all = "snake_case")]
pub enum CheckoutCustomerCreation {
    Always,
    IfRequired,
}

/// Create a Stripe Checkout session
#[derive(Debug, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct CreateCheckoutSession {
    success_url: String,
    cancel_url: String,
    customer_creation: CheckoutCustomerCreation,
    mode: StripeMode,
    line_items: Vec<CheckoutLineItem>,
}

impl CreateCheckoutSession {
    pub fn new(
        success_url: String,
        cancel_url: String,
        customer_creation: CheckoutCustomerCreation,
        mode: StripeMode,
        line_items: Vec<CheckoutLineItem>,
    ) -> Self {
        CreateCheckoutSession {
            success_url,
            cancel_url,
            customer_creation,
            mode,
            line_items,
        }
    }

    pub fn send_to_stripe(&self, stripe_secret_key: &str) -> Result<Response<Body>, Error> {
        // Stripe API endpoint for creating checkout sessions
        let api_url = format!("{STRIPE_API}{CHECKOUT_ENDPOINT}");

        // Convert your struct to a format suitable for URL-encoded form submission
        let mut form_data = vec![
            ("success_url".to_string(), self.success_url.to_string()),
            ("cancel_url".to_string(), self.cancel_url.to_string()),
            (
                "customer_creation".to_string(),
                match self.customer_creation {
                    CheckoutCustomerCreation::Always => "always".to_string(),
                    CheckoutCustomerCreation::IfRequired => "if_required".to_string(),
                },
            ),
            (
                "mode".to_string(),
                match self.mode {
                    StripeMode::Payment => "payment".to_string(),
                    StripeMode::Subscription => "subscription".to_string(),
                    StripeMode::Setup => "setup".to_string(),
                },
            ),
        ];

        // Add line items in the format Stripe expects
        for (index, item) in self.line_items.iter().enumerate() {
            form_data.push((format!("line_items[{}][price]", index), item.price.clone()));
            form_data.push((
                format!("line_items[{}][quantity]", index),
                item.quantity.to_string(),
            ));
        }
        let form_data_refs: Vec<(&str, &str)> = form_data
            .iter()
            .map(|(k, v)| (k.as_str(), v.as_str()))
            .collect();

        // Send the request with URL-encoded form data and authorization
        ureq::post(&api_url)
            .header("Authorization", &format!("Bearer {}", stripe_secret_key))
            .send_form(form_data_refs)
    }
}