Promote to full subscription, fmt + clippy
This commit is contained in:
parent
7a39f5853f
commit
09e89724de
15
apps/labrinth/.sqlx/query-1dea22589b0440cfeaf98b6869bdaad852d58c61cf2a1affb01acc4984d42341.json
generated
Normal file
15
apps/labrinth/.sqlx/query-1dea22589b0440cfeaf98b6869bdaad852d58c61cf2a1affb01acc4984d42341.json
generated
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n UPDATE users_redeemals\n SET status = $1\n WHERE\n status = $2\n AND NOW() - last_attempt > INTERVAL '5 minutes'\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Varchar",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "1dea22589b0440cfeaf98b6869bdaad852d58c61cf2a1affb01acc4984d42341"
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"db_name": "PostgreSQL",
|
"db_name": "PostgreSQL",
|
||||||
"query": "\n SELECT\n id, user_id, price_id, amount, currency_code, status, due, last_attempt,\n charge_type, subscription_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n subscription_interval AS \"subscription_interval?\",\n payment_platform,\n payment_platform_id AS \"payment_platform_id?\",\n parent_charge_id AS \"parent_charge_id?\",\n net AS \"net?\"\n FROM charges\n WHERE subscription_id = $1 AND (status = 'open' OR status = 'cancelled' OR status = 'failed')",
|
"query": "\n SELECT\n id, user_id, price_id, amount, currency_code, status, due, last_attempt,\n charge_type, subscription_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n subscription_interval AS \"subscription_interval?\",\n payment_platform,\n payment_platform_id AS \"payment_platform_id?\",\n parent_charge_id AS \"parent_charge_id?\",\n net AS \"net?\"\n FROM charges\n WHERE subscription_id = $1 AND (status = 'open' OR status = 'expiring' OR status = 'cancelled' OR status = 'failed')",
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
{
|
{
|
||||||
@ -102,5 +102,5 @@
|
|||||||
true
|
true
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"hash": "cf020daa52a1316e5f60d197196b880b72c0b2a576e470d9fd7182558103d055"
|
"hash": "2e18682890f7ec5a618991c2a4c77ca9546970f314f902a5197eb2d189cf81f7"
|
||||||
}
|
}
|
||||||
59
apps/labrinth/.sqlx/query-58ccda393820a272d72a3e41eccc5db30ab6ad0bb346caf781efdb5aab524286.json
generated
Normal file
59
apps/labrinth/.sqlx/query-58ccda393820a272d72a3e41eccc5db30ab6ad0bb346caf781efdb5aab524286.json
generated
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT * FROM users_redeemals WHERE status = $1 LIMIT $2",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "id",
|
||||||
|
"type_info": "Int4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "user_id",
|
||||||
|
"type_info": "Int8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "offer",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "redeemed",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 4,
|
||||||
|
"name": "status",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 5,
|
||||||
|
"name": "last_attempt",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 6,
|
||||||
|
"name": "n_attempts",
|
||||||
|
"type_info": "Int4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Int8"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "58ccda393820a272d72a3e41eccc5db30ab6ad0bb346caf781efdb5aab524286"
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"db_name": "PostgreSQL",
|
"db_name": "PostgreSQL",
|
||||||
"query": "\n INSERT INTO users_redeemals\n (user_id, offer, redeemed, status)\n VALUES ($1, $2, $3, $4)\n RETURNING id",
|
"query": "\n INSERT INTO users_redeemals\n (user_id, offer, redeemed, status, last_attempt, n_attempts)\n VALUES ($1, $2, $3, $4, $5, $6)\n RETURNING id",
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
{
|
{
|
||||||
@ -14,12 +14,14 @@
|
|||||||
"Int8",
|
"Int8",
|
||||||
"Varchar",
|
"Varchar",
|
||||||
"Timestamptz",
|
"Timestamptz",
|
||||||
"Varchar"
|
"Varchar",
|
||||||
|
"Timestamptz",
|
||||||
|
"Int4"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"nullable": [
|
"nullable": [
|
||||||
false
|
false
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"hash": "8e5e0eb3ac9e34c944e9e6eedd5143dd7bde0cebee0e8e669d9a784e96c76ade"
|
"hash": "7adff98b270adc4a48e2c8a89a32ca1b83104102190597f4cda05e6f1c1e8f26"
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"db_name": "PostgreSQL",
|
"db_name": "PostgreSQL",
|
||||||
"query": "\n UPDATE users_redeemals\n SET\n offer = $2,\n status = $3,\n redeemed = $4\n WHERE id = $1\n ",
|
"query": "\n UPDATE users_redeemals\n SET\n offer = $2,\n status = $3,\n redeemed = $4,\n last_attempt = $5,\n n_attempts = $6\n WHERE id = $1\n ",
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [],
|
"columns": [],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
@ -8,10 +8,12 @@
|
|||||||
"Int4",
|
"Int4",
|
||||||
"Varchar",
|
"Varchar",
|
||||||
"Varchar",
|
"Varchar",
|
||||||
"Timestamptz"
|
"Timestamptz",
|
||||||
|
"Timestamptz",
|
||||||
|
"Int4"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"nullable": []
|
"nullable": []
|
||||||
},
|
},
|
||||||
"hash": "e7937a0d98454477409746b2b3111ba5daab035afed0a044b32bdd4040105efd"
|
"hash": "8d61d1ecc5321e2ac8932ef99de0f77e49cced9c7726ea746392a5fcbe75f2f5"
|
||||||
}
|
}
|
||||||
18
apps/labrinth/.sqlx/query-e3f6fa7e5ec6dee4fcdff904b3e692dccd55372d9cc827a1d68361fd036bc183.json
generated
Normal file
18
apps/labrinth/.sqlx/query-e3f6fa7e5ec6dee4fcdff904b3e692dccd55372d9cc827a1d68361fd036bc183.json
generated
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n UPDATE users_redeemals\n SET\n status = $3,\n last_attempt = $4,\n n_attempts = $5\n WHERE id = $1 AND status = $2\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Int4",
|
||||||
|
"Text",
|
||||||
|
"Varchar",
|
||||||
|
"Timestamptz",
|
||||||
|
"Int4"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "e3f6fa7e5ec6dee4fcdff904b3e692dccd55372d9cc827a1d68361fd036bc183"
|
||||||
|
}
|
||||||
@ -197,7 +197,7 @@ impl DBCharge {
|
|||||||
) -> Result<Option<DBCharge>, DatabaseError> {
|
) -> Result<Option<DBCharge>, DatabaseError> {
|
||||||
let user_subscription_id = user_subscription_id.0;
|
let user_subscription_id = user_subscription_id.0;
|
||||||
let res = select_charges_with_predicate!(
|
let res = select_charges_with_predicate!(
|
||||||
"WHERE subscription_id = $1 AND (status = 'open' OR status = 'cancelled' OR status = 'failed')",
|
"WHERE subscription_id = $1 AND (status = 'open' OR status = 'expiring' OR status = 'cancelled' OR status = 'failed')",
|
||||||
user_subscription_id
|
user_subscription_id
|
||||||
)
|
)
|
||||||
.fetch_optional(exec)
|
.fetch_optional(exec)
|
||||||
|
|||||||
@ -288,6 +288,15 @@ impl DBProductPrice {
|
|||||||
.collect::<Result<Vec<_>, serde_json::Error>>()?)
|
.collect::<Result<Vec<_>, serde_json::Error>>()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_all_product_prices(
|
||||||
|
product_id: DBProductId,
|
||||||
|
exec: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
|
||||||
|
) -> Result<Vec<DBProductPrice>, DatabaseError> {
|
||||||
|
let res = Self::get_all_products_prices(&[product_id], exec).await?;
|
||||||
|
|
||||||
|
Ok(res.remove(&product_id).map(|x| x.1).unwrap_or_default())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_all_public_product_prices(
|
pub async fn get_all_public_product_prices(
|
||||||
product_id: DBProductId,
|
product_id: DBProductId,
|
||||||
exec: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
|
exec: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
|
||||||
|
|||||||
@ -59,7 +59,7 @@ impl Status {
|
|||||||
"pending" => Status::Pending,
|
"pending" => Status::Pending,
|
||||||
"processing" => Status::Processing,
|
"processing" => Status::Processing,
|
||||||
"processed" => Status::Processed,
|
"processed" => Status::Processed,
|
||||||
_ => Default::default(),
|
_ => Status::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -384,6 +384,7 @@ pub async fn edit_subscription(
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
if let Some(cancelled) = &edit_subscription.cancelled {
|
if let Some(cancelled) = &edit_subscription.cancelled {
|
||||||
|
// Notably, cannot cancel/uncancel expiring charges.
|
||||||
if !matches!(
|
if !matches!(
|
||||||
open_charge.status,
|
open_charge.status,
|
||||||
ChargeStatus::Open
|
ChargeStatus::Open
|
||||||
@ -408,21 +409,24 @@ pub async fn edit_subscription(
|
|||||||
|
|
||||||
if let Some(interval) = &edit_subscription.interval {
|
if let Some(interval) = &edit_subscription.interval {
|
||||||
if let Price::Recurring { intervals } = ¤t_price.prices {
|
if let Price::Recurring { intervals } = ¤t_price.prices {
|
||||||
if let Some(price) = intervals.get(interval) {
|
// For expiring charges, the interval is handled in the Product branch.
|
||||||
open_charge.subscription_interval = Some(*interval);
|
if open_charge.status != ChargeStatus::Expiring {
|
||||||
open_charge.amount = *price as i64;
|
if let Some(price) = intervals.get(interval) {
|
||||||
} else {
|
open_charge.subscription_interval = Some(*interval);
|
||||||
return Err(ApiError::InvalidInput(
|
open_charge.amount = *price as i64;
|
||||||
"Interval is not valid for this subscription!"
|
} else {
|
||||||
.to_string(),
|
return Err(ApiError::InvalidInput(
|
||||||
));
|
"Interval is not valid for this subscription!"
|
||||||
|
.to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let intent = if let Some(product_id) = &edit_subscription.product {
|
let intent = if let Some(product_id) = &edit_subscription.product {
|
||||||
let product_price =
|
let product_price =
|
||||||
product_item::DBProductPrice::get_all_public_product_prices(
|
product_item::DBProductPrice::get_all_product_prices(
|
||||||
(*product_id).into(),
|
(*product_id).into(),
|
||||||
&mut *transaction,
|
&mut *transaction,
|
||||||
)
|
)
|
||||||
@ -443,48 +447,14 @@ pub async fn edit_subscription(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let interval = open_charge.due - Utc::now();
|
// If the charge is an expiring charge, we need to create a payment
|
||||||
let duration = PriceDuration::Monthly;
|
// intent as if the user was subscribing to the product, as opposed
|
||||||
|
// to a proration.
|
||||||
|
if open_charge.status == ChargeStatus::Expiring {
|
||||||
|
// We need a new interval when promoting the charge.
|
||||||
|
let interval = edit_subscription.interval
|
||||||
|
.ok_or_else(|| ApiError::InvalidInput("You need to specify an interval when promoting an expiring charge.".to_owned()))?;
|
||||||
|
|
||||||
let current_amount = match ¤t_price.prices {
|
|
||||||
Price::OneTime { price } => *price,
|
|
||||||
Price::Recurring { intervals } => *intervals.get(&duration).ok_or_else(|| {
|
|
||||||
ApiError::InvalidInput(
|
|
||||||
"Could not find a valid price for the user's duration".to_string(),
|
|
||||||
)
|
|
||||||
})?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let amount = match &product_price.prices {
|
|
||||||
Price::OneTime { price } => *price,
|
|
||||||
Price::Recurring { intervals } => *intervals.get(&duration).ok_or_else(|| {
|
|
||||||
ApiError::InvalidInput(
|
|
||||||
"Could not find a valid price for the user's duration".to_string(),
|
|
||||||
)
|
|
||||||
})?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let complete = Decimal::from(interval.num_seconds())
|
|
||||||
/ Decimal::from(duration.duration().num_seconds());
|
|
||||||
let proration = (Decimal::from(amount - current_amount) * complete)
|
|
||||||
.floor()
|
|
||||||
.to_i32()
|
|
||||||
.ok_or_else(|| {
|
|
||||||
ApiError::InvalidInput(
|
|
||||||
"Could not convert proration to i32".to_string(),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// First branch: Plan downgrade, update future charge
|
|
||||||
// Second branch: For small transactions (under 30 cents), we make a loss on the
|
|
||||||
// proration due to fees. In these situations, just give it to them for free, because
|
|
||||||
// their next charge will be in a day or two anyway.
|
|
||||||
if current_amount > amount || proration < 30 {
|
|
||||||
open_charge.price_id = product_price.id;
|
|
||||||
open_charge.amount = amount as i64;
|
|
||||||
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let charge_id = generate_charge_id(&mut transaction).await?;
|
let charge_id = generate_charge_id(&mut transaction).await?;
|
||||||
|
|
||||||
let customer_id = get_or_create_customer(
|
let customer_id = get_or_create_customer(
|
||||||
@ -497,6 +467,15 @@ pub async fn edit_subscription(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
let new_price_value = match product_price.prices {
|
||||||
|
Price::OneTime { ref price } => *price,
|
||||||
|
Price::Recurring { ref intervals } => {
|
||||||
|
*intervals
|
||||||
|
.get(&interval)
|
||||||
|
.ok_or_else(|| ApiError::InvalidInput("Could not find a valid price for the specified duration".to_owned()))?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let currency = Currency::from_str(
|
let currency = Currency::from_str(
|
||||||
¤t_price.currency_code.to_lowercase(),
|
¤t_price.currency_code.to_lowercase(),
|
||||||
)
|
)
|
||||||
@ -505,7 +484,7 @@ pub async fn edit_subscription(
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut intent =
|
let mut intent =
|
||||||
CreatePaymentIntent::new(proration as i64, currency);
|
CreatePaymentIntent::new(new_price_value as i64, currency);
|
||||||
|
|
||||||
let mut metadata = HashMap::new();
|
let mut metadata = HashMap::new();
|
||||||
metadata.insert(
|
metadata.insert(
|
||||||
@ -526,15 +505,11 @@ pub async fn edit_subscription(
|
|||||||
);
|
);
|
||||||
metadata.insert(
|
metadata.insert(
|
||||||
"modrinth_subscription_interval".to_string(),
|
"modrinth_subscription_interval".to_string(),
|
||||||
open_charge
|
interval.as_str().to_string(),
|
||||||
.subscription_interval
|
|
||||||
.unwrap_or(PriceDuration::Monthly)
|
|
||||||
.as_str()
|
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
metadata.insert(
|
metadata.insert(
|
||||||
"modrinth_charge_type".to_string(),
|
"modrinth_charge_type".to_string(),
|
||||||
ChargeType::Proration.as_str().to_string(),
|
ChargeType::Subscription.as_str().to_string(),
|
||||||
);
|
);
|
||||||
|
|
||||||
intent.customer = Some(customer_id);
|
intent.customer = Some(customer_id);
|
||||||
@ -559,7 +534,133 @@ pub async fn edit_subscription(
|
|||||||
stripe::PaymentIntent::create(&stripe_client, intent)
|
stripe::PaymentIntent::create(&stripe_client, intent)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Some((proration, 0, intent))
|
Some((new_price_value, 0, intent))
|
||||||
|
} else {
|
||||||
|
// The charge is not an expiring charge, need to prorate.
|
||||||
|
|
||||||
|
let interval = open_charge.due - Utc::now();
|
||||||
|
let duration = PriceDuration::Monthly;
|
||||||
|
|
||||||
|
let current_amount = match ¤t_price.prices {
|
||||||
|
Price::OneTime { price } => *price,
|
||||||
|
Price::Recurring { intervals } => *intervals.get(&duration).ok_or_else(|| {
|
||||||
|
ApiError::InvalidInput(
|
||||||
|
"Could not find a valid price for the user's duration".to_string(),
|
||||||
|
)
|
||||||
|
})?,
|
||||||
|
};
|
||||||
|
|
||||||
|
let amount = match &product_price.prices {
|
||||||
|
Price::OneTime { price } => *price,
|
||||||
|
Price::Recurring { intervals } => *intervals.get(&duration).ok_or_else(|| {
|
||||||
|
ApiError::InvalidInput(
|
||||||
|
"Could not find a valid price for the user's duration".to_string(),
|
||||||
|
)
|
||||||
|
})?,
|
||||||
|
};
|
||||||
|
|
||||||
|
let complete = Decimal::from(interval.num_seconds())
|
||||||
|
/ Decimal::from(duration.duration().num_seconds());
|
||||||
|
let proration = (Decimal::from(amount - current_amount)
|
||||||
|
* complete)
|
||||||
|
.floor()
|
||||||
|
.to_i32()
|
||||||
|
.ok_or_else(|| {
|
||||||
|
ApiError::InvalidInput(
|
||||||
|
"Could not convert proration to i32".to_string(),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// First condition: Plan downgrade, update future charge
|
||||||
|
// Second condition: For small transactions (under 30 cents), we make a loss on the
|
||||||
|
// proration due to fees. In these situations, just give it to them for free, because
|
||||||
|
// their next charge will be in a day or two anyway.
|
||||||
|
if current_amount > amount || proration < 30 {
|
||||||
|
open_charge.price_id = product_price.id;
|
||||||
|
open_charge.amount = amount as i64;
|
||||||
|
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let charge_id =
|
||||||
|
generate_charge_id(&mut transaction).await?;
|
||||||
|
|
||||||
|
let customer_id = get_or_create_customer(
|
||||||
|
user.id,
|
||||||
|
user.stripe_customer_id.as_deref(),
|
||||||
|
user.email.as_deref(),
|
||||||
|
&stripe_client,
|
||||||
|
&pool,
|
||||||
|
&redis,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let currency = Currency::from_str(
|
||||||
|
¤t_price.currency_code.to_lowercase(),
|
||||||
|
)
|
||||||
|
.map_err(|_| {
|
||||||
|
ApiError::InvalidInput(
|
||||||
|
"Invalid currency code".to_string(),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut intent =
|
||||||
|
CreatePaymentIntent::new(proration as i64, currency);
|
||||||
|
|
||||||
|
let mut metadata = HashMap::new();
|
||||||
|
metadata.insert(
|
||||||
|
"modrinth_user_id".to_string(),
|
||||||
|
to_base62(user.id.0),
|
||||||
|
);
|
||||||
|
metadata.insert(
|
||||||
|
"modrinth_charge_id".to_string(),
|
||||||
|
to_base62(charge_id.0 as u64),
|
||||||
|
);
|
||||||
|
metadata.insert(
|
||||||
|
"modrinth_subscription_id".to_string(),
|
||||||
|
to_base62(subscription.id.0 as u64),
|
||||||
|
);
|
||||||
|
metadata.insert(
|
||||||
|
"modrinth_price_id".to_string(),
|
||||||
|
to_base62(product_price.id.0 as u64),
|
||||||
|
);
|
||||||
|
metadata.insert(
|
||||||
|
"modrinth_subscription_interval".to_string(),
|
||||||
|
open_charge
|
||||||
|
.subscription_interval
|
||||||
|
.unwrap_or(PriceDuration::Monthly)
|
||||||
|
.as_str()
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
metadata.insert(
|
||||||
|
"modrinth_charge_type".to_string(),
|
||||||
|
ChargeType::Proration.as_str().to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
intent.customer = Some(customer_id);
|
||||||
|
intent.metadata = Some(metadata);
|
||||||
|
intent.receipt_email = user.email.as_deref();
|
||||||
|
intent.setup_future_usage =
|
||||||
|
Some(PaymentIntentSetupFutureUsage::OffSession);
|
||||||
|
|
||||||
|
if let Some(payment_method) =
|
||||||
|
&edit_subscription.payment_method
|
||||||
|
{
|
||||||
|
let Ok(payment_method_id) =
|
||||||
|
PaymentMethodId::from_str(payment_method)
|
||||||
|
else {
|
||||||
|
return Err(ApiError::InvalidInput(
|
||||||
|
"Invalid payment method id".to_string(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
intent.payment_method = Some(payment_method_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
let intent =
|
||||||
|
stripe::PaymentIntent::create(&stripe_client, intent)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Some((proration, 0, intent))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -2292,7 +2393,9 @@ pub async fn index_subscriptions(pool: PgPool, redis: RedisPool) {
|
|||||||
// Try processing it.
|
// Try processing it.
|
||||||
let pending_redeemals = UserRedeemal::get_pending(&pool, 100).await?;
|
let pending_redeemals = UserRedeemal::get_pending(&pool, 100).await?;
|
||||||
for redeemal in pending_redeemals {
|
for redeemal in pending_redeemals {
|
||||||
if let Err(error) = try_process_user_redeemal(&pool, &redis, redeemal).await {
|
if let Err(error) =
|
||||||
|
try_process_user_redeemal(&pool, &redis, redeemal).await
|
||||||
|
{
|
||||||
warn!(%error, "Failed to process a redeemal.")
|
warn!(%error, "Failed to process a redeemal.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2408,7 +2511,7 @@ pub async fn try_process_user_redeemal(
|
|||||||
swap_mb: swap,
|
swap_mb: swap,
|
||||||
storage_mb: storage,
|
storage_mb: storage,
|
||||||
},
|
},
|
||||||
source: Default::default(),
|
source: crate::util::archon::Empty::default(),
|
||||||
region,
|
region,
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user