use base64::{engine::general_purpose::STANDARD, Engine};
use rocket::{figment::providers::Env, response::status::BadRequest, serde::Deserialize, Config};
use std::sync::LazyLock;
const STRIPE_API: &str = "https://api.stripe.com";
const CHECKOUT_ENDPOINT: &str = "/v1/checkout/sessions";
static STRIPE_CONFIG: LazyLock<rocket::figment::Figment> = LazyLock::new(|| {
Config::figment()
.select("stripe")
.merge(Env::prefixed("FORMULATE_STRIPE_"))
});
#[derive(Deserialize)]
#[serde(crate = "rocket::serde")]
struct StripeConfig {
secret_key: Option<String>,
}
#[derive(Debug, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct StripeCheckoutLineItem {
#[serde(alias = "id")]
pub _id: String,
pub currency: String,
pub amount_total: u32,
pub amount_subtotal: u32,
pub description: String,
pub quantity: u32,
}
#[derive(Debug, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct StripeCheckoutLineItems {
pub data: Vec<StripeCheckoutLineItem>,
}
impl StripeCheckoutLineItems {
pub fn get_line_items(&self) -> Vec<(&String, u32, &String, u32, u32)> {
self.data
.iter()
.map(|item| {
(
&item.description,
item.quantity,
&item.currency,
item.amount_subtotal,
item.amount_total,
)
})
.collect()
}
}
fn get_basic_auth_header(user: &str, pass: &str) -> String {
let creds = String::from(user) + ":" + pass;
String::from("Basic ") + &STANDARD.encode(creds.as_bytes())
}
pub fn find_order_info(line_item_id: &str) -> Result<StripeCheckoutLineItems, BadRequest<String>> {
let config: StripeConfig = match STRIPE_CONFIG.extract::<StripeConfig>() {
Ok(config) => config,
Err(err) => return Err(BadRequest(err.to_string())),
};
if let Some(secret_key) = config.secret_key {
let api_url = format!("{STRIPE_API}{CHECKOUT_ENDPOINT}/{line_item_id}/line_items");
let result = match ureq::get(&api_url)
.set("Authorization", &get_basic_auth_header(&secret_key, ""))
.call()
{
Err(ureq::Error::Status(code, _)) => {
return if code == 401 {
Err(BadRequest(
"Error fetching product info. Try setting the correct secret key."
.to_owned(),
))
} else {
Err(BadRequest("Error fetching product info.".to_owned()))
}
}
Err(err) => return Err(BadRequest(format!("Error fetching product info: {}", err))),
Ok(res) => res,
};
match result.into_json::<StripeCheckoutLineItems>() {
Err(err) => Err(BadRequest(format!(
"Encountered error while parsing product info from JSON: {}",
err
))),
Ok(res) => Ok(res),
}
} else {
Err(BadRequest(
"Stripe secret key needs to be configured in order to find product information."
.to_owned(),
))
}
}