More billing fixes (#4431)

* Only update the PaymentMethod ID if not using placeholder ID

* comment

* Create Anrok transactions for all charges

* Fix comment

* Prefer using payment method's address rather than customer address

* chore: query cache, clippy, fmt

* Retrieve stripe address from PM

* chore: query cache, clippy, fmt

* fmt

* bring back the query cache
This commit is contained in:
François-Xavier Talbot
2025-09-27 23:37:30 +01:00
committed by GitHub
parent bb9ce52c9d
commit 3f55711f9e

View File

@@ -199,14 +199,7 @@ pub async fn index_subscriptions(
) )
})?; })?;
let Ok(customer_id): Result<stripe::CustomerId, _> = let customer_id = stripe_customer_id.parse().map_err(|e| ApiError::InvalidInput(format!("Charge's Stripe customer ID was invalid ({e})")))?;
stripe_customer_id.parse()
else {
return Err(ApiError::InvalidInput(
"Charge's Stripe customer ID was invalid"
.to_owned(),
));
};
let customer = stripe::Customer::retrieve( let customer = stripe::Customer::retrieve(
&stripe_client, &stripe_client,
@@ -215,13 +208,12 @@ pub async fn index_subscriptions(
) )
.await?; .await?;
let Some(stripe_address) = customer.address else { customer.address.ok_or_else(|| {
return Err(ApiError::InvalidInput( ApiError::InvalidInput(
"Stripe customer had no address".to_owned(), "Stripe customer had no address"
)); .to_owned(),
}; )
})?
stripe_address
}; };
let customer_address = let customer_address =
@@ -272,12 +264,13 @@ pub async fn index_subscriptions(
// The price of the subscription has changed, we need to insert a notification // The price of the subscription has changed, we need to insert a notification
// for this. // for this.
let Some(subscription_id) = charge.subscription_id let subscription_id =
else { charge.subscription_id.ok_or_else(|| {
return Err(ApiError::InvalidInput( ApiError::InvalidInput(
"Charge has no subscription ID".to_owned(), "Charge has no subscription ID"
)); .to_owned(),
}; )
})?;
NotificationBuilder { NotificationBuilder {
body: NotificationBody::TaxNotification { body: NotificationBody::TaxNotification {
@@ -361,20 +354,75 @@ pub async fn index_subscriptions(
) )
})?; })?;
let stripe_customer_id = DBUser::get_id(c.user_id, &pg, &redis) let customer_address = 'a: {
.await? let stripe_id: stripe::PaymentIntentId = c
.ok_or_else(|| { .payment_platform_id
ApiError::from(DatabaseError::Database( .as_ref()
sqlx::Error::RowNotFound, .and_then(|x| x.parse().ok())
)) .ok_or_else(|| {
})
.and_then(|user| {
user.stripe_customer_id.ok_or_else(|| {
ApiError::InvalidInput( ApiError::InvalidInput(
"User has no Stripe customer ID".to_owned(), "Charge has no payment platform ID".to_owned(),
) )
}) })?;
})?;
// Attempt retrieving the address via the payment intent's payment method
let pi = stripe::PaymentIntent::retrieve(
&stripe_client,
&stripe_id,
&["payment_method"],
)
.await?;
let pi_stripe_address = pi
.payment_method
.and_then(|x| x.into_object())
.and_then(|x| x.billing_details.address);
match pi_stripe_address {
Some(address) => break 'a address,
None => {
warn!("PaymentMethod had no address");
}
};
let stripe_customer_id =
DBUser::get_id(c.user_id, &pg, &redis)
.await?
.ok_or_else(|| {
ApiError::from(DatabaseError::Database(
sqlx::Error::RowNotFound,
))
})
.and_then(|user| {
user.stripe_customer_id.ok_or_else(|| {
ApiError::InvalidInput(
"User has no Stripe customer ID"
.to_owned(),
)
})
})?;
let customer_id =
stripe_customer_id.parse().map_err(|e| {
ApiError::InvalidInput(format!(
"Charge's Stripe customer ID was invalid ({e})"
))
})?;
let customer = stripe::Customer::retrieve(
&stripe_client,
&customer_id,
&[],
)
.await?;
customer.address.ok_or_else(|| {
ApiError::InvalidInput(
"Stripe customer had no address".to_owned(),
)
})?
};
let tax_id = let tax_id =
DBProductsTaxIdentifier::get_price(c.price_id, &pg) DBProductsTaxIdentifier::get_price(c.price_id, &pg)
@@ -383,30 +431,6 @@ pub async fn index_subscriptions(
DatabaseError::Database(sqlx::Error::RowNotFound) DatabaseError::Database(sqlx::Error::RowNotFound)
})?; })?;
let Ok(customer_id): Result<stripe::CustomerId, _> =
stripe_customer_id.parse()
else {
return Err(ApiError::InvalidInput(
"Charge's Stripe customer ID was invalid".to_owned(),
));
};
let customer = stripe::Customer::retrieve(
&stripe_client,
&customer_id,
&[],
)
.await?;
let Some(stripe_address) = customer.address else {
return Err(ApiError::InvalidInput(
"Stripe customer had no address".to_owned(),
));
};
let customer_address =
anrok::Address::from_stripe_address(&stripe_address);
let tax_platform_id = let tax_platform_id =
anrok::transaction_id_stripe_pi(&payment_intent_id); anrok::transaction_id_stripe_pi(&payment_intent_id);
@@ -417,7 +441,10 @@ pub async fn index_subscriptions(
.create_or_update_txn(&anrok::Transaction { .create_or_update_txn(&anrok::Transaction {
id: tax_platform_id.clone(), id: tax_platform_id.clone(),
fields: anrok::TransactionFields { fields: anrok::TransactionFields {
customer_address, customer_address:
anrok::Address::from_stripe_address(
&customer_address,
),
currency_code: c.currency_code.clone(), currency_code: c.currency_code.clone(),
accounting_time: c.due, accounting_time: c.due,
accounting_time_zone: accounting_time_zone: