use rust_decimal_macros::dec;
use crate::{order::Order, Config, Database, Error, ErrorKind, Result, User};
impl super::Payment {
pub async fn checkout_session(
&mut self,
db: &Database,
config: &Config,
client: &stripe::Client,
) -> Result<String> {
let order = db.get::<Order>(self.order)?;
let mut user = db.get::<User>(order.user)?;
if let Some(session_id) = &self.stripe_session_id {
if let Ok(session) =
stripe::CheckoutSession::retrieve(&client, &session_id.parse().unwrap(), &[]).await
{
if let Some(url) = session.url {
return Ok(url);
}
}
}
let customer_id = if let Some(ref customer_id) = user.stripe_customer_id {
let mut customer =
stripe::Customer::retrieve(&client, &customer_id.parse().unwrap(), &[]).await?;
let mut update = stripe::UpdateCustomer::default();
if let Some(name) = customer.name {
if name != user.name {
update.name = Some(&user.name);
}
}
if let Some(email) = customer.email {
if email != user.email {
update.email = Some(&user.email);
}
}
stripe::Customer::update(&client, &customer_id.parse().unwrap(), update).await?;
customer_id.to_string()
} else {
let customer = stripe::Customer::create(
&client,
stripe::CreateCustomer {
name: Some(&user.name),
email: Some(&user.email),
metadata: Some(std::collections::HashMap::from([(
String::from("async-stripe"),
String::from("true"),
)])),
..Default::default()
},
)
.await?;
user.stripe_customer_id = Some(customer.id.to_string());
customer.id.to_string()
};
db.set(&user)?;
let mut stripe_prices = vec![];
for product in order.items {
let product_name = product.to_string();
let product_id = product.id.to_string();
let stripe_product = {
let mut create_product = stripe::CreateProduct::new(&product_name);
create_product.id = Some(&product_id);
create_product.metadata = Some(std::collections::HashMap::from([(
String::from("async-stripe"),
String::from("true"),
)]));
stripe::Product::create(&client, create_product)
.await
.unwrap()
};
let price = {
let mut create_price = stripe::CreatePrice::new(stripe::Currency::USD);
create_price.product = Some(stripe::IdOrCreate::Id(&product_id));
create_price.metadata = Some(std::collections::HashMap::from([(
String::from("async-stripe"),
String::from("true"),
)]));
use rust_decimal::prelude::ToPrimitive;
create_price.unit_amount =
Some((product.cost() * dec!(100)).to_i64().ok_or(Error::new(
ErrorKind::Other("failed converting price decimal".to_string()),
))?);
create_price.expand = &["product"];
stripe::Price::create(&client, create_price).await.unwrap()
};
stripe_prices.push(price);
}
let checkout_session = {
let mut params = stripe::CreateCheckoutSession::new();
let url = format!("https://{}", config.domain);
params.cancel_url = Some(&url);
params.success_url = Some(&url);
params.customer = Some(customer_id.parse().unwrap());
params.mode = Some(stripe::CheckoutSessionMode::Payment);
params.line_items = Some(
stripe_prices
.iter()
.map(|price| stripe::CreateCheckoutSessionLineItems {
quantity: Some(1),
price: Some(price.id.to_string()),
..Default::default()
})
.collect::<Vec<_>>(),
);
stripe::CheckoutSession::create(&client, params).await?
};
self.stripe_session_id = Some(checkout_session.id.to_string());
db.set(self)?;
if let Some(url) = checkout_session.url {
Ok(url)
} else {
Err(ErrorKind::Other(format!("failed getting stripe checkout session url")).into())
}
}
}