mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
credits checkout from existing subscriber record
This commit is contained in:
1 parent
5cb06357fa
commit
e0400b651e
5 files changed
+50
-7
No files matched your search
Generated
+1
@@ -2292,6 +2292,7 @@ dependencies = [
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"tokens",
|
||||
"tokio",
|
||||
"user_traits_component",
|
||||
"workspace-build-acceleration",
|
||||
|
||||
+30
-3
@@ -1,6 +1,7 @@
|
||||
use crate::configs::credits_packs::get_artcraft_credits_pack_by_slug_and_env::get_artcraft_credits_pack_by_slug_and_env;
|
||||
use crate::configs::stripe_artcraft_metadata_keys::{STRIPE_ARTCRAFT_METADATA_EMAIL, STRIPE_ARTCRAFT_METADATA_USERNAME, STRIPE_ARTCRAFT_METADATA_USER_TOKEN};
|
||||
use crate::configs::subscriptions::get_artcraft_subscription_by_slug_and_env::get_artcraft_subscription_by_slug_and_env;
|
||||
use crate::endpoints::webhook::common::stripe_artcraft_webhook_error::StripeArtcraftWebhookError;
|
||||
use crate::utils::artcraft_stripe_config::ArtcraftStripeConfigWithClient;
|
||||
use crate::utils::common_web_error::CommonWebError;
|
||||
use actix_web::web::{Data, Json};
|
||||
@@ -11,7 +12,9 @@ use component_traits::traits::internal_user_lookup::InternalUserLookup;
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use log::{error, info, warn};
|
||||
use mysql_queries::queries::users::user_subscriptions::find_subscription_for_owner_user::find_subscription_for_owner_user_using_connection;
|
||||
use reusable_types::server_environment::ServerEnvironment;
|
||||
use sqlx::MySqlPool;
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
@@ -19,7 +22,6 @@ use stripe_checkout::checkout_session::{CreateCheckoutSession, CreateCheckoutSes
|
||||
use stripe_checkout::CheckoutSessionMode;
|
||||
use stripe_core::CustomerId;
|
||||
use user_traits_component::traits::internal_session_cache_purge::InternalSessionCachePurge;
|
||||
|
||||
//use utoipa::ToSchema;
|
||||
|
||||
// /// Create a Stripe Checkout session and return the redirect URL in Json.
|
||||
@@ -41,6 +43,7 @@ pub async fn stripe_artcraft_create_credits_pack_checkout_handler(
|
||||
server_environment: Data<ServerEnvironment>,
|
||||
internal_user_lookup: Data<dyn InternalUserLookup>,
|
||||
internal_session_cache_purge: Data<dyn InternalSessionCachePurge>,
|
||||
mysql_pool: Data<MySqlPool>,
|
||||
) -> Result<Json<StripeArtcraftCreateCreditsPackCheckoutResponse>, CommonWebError>
|
||||
{
|
||||
let slug = match request.credits_pack {
|
||||
@@ -50,8 +53,16 @@ pub async fn stripe_artcraft_create_credits_pack_checkout_handler(
|
||||
|
||||
let credits_pack = get_artcraft_credits_pack_by_slug_and_env(slug, **server_environment);
|
||||
|
||||
let mut mysql_connection = mysql_pool
|
||||
.acquire()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Could not acquire mysql connection: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
let maybe_user_metadata = internal_user_lookup
|
||||
.lookup_user_from_http_request(&http_request)
|
||||
.lookup_user_from_http_request_and_mysql_connection(&http_request, &mut mysql_connection)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error looking up user: {:?}", err);
|
||||
@@ -64,6 +75,21 @@ pub async fn stripe_artcraft_create_credits_pack_checkout_handler(
|
||||
Some(user_metadata) => user_metadata,
|
||||
};
|
||||
|
||||
// NB: Currently the stripe customer id field in the `users` table is only for FakeYou subscriptions,
|
||||
// so we need to look up any existing Artcraft subscription separately. This is needed to pre-fill
|
||||
// the Stripe billing form.
|
||||
let maybe_subscription = find_subscription_for_owner_user_using_connection(
|
||||
&user_metadata.user_token_typed,
|
||||
PaymentsNamespace::Artcraft,
|
||||
&mut mysql_connection
|
||||
).await.map_err(|err| {
|
||||
error!("Error looking up user's ({}) existing subscription: {:?}", &user_metadata.user_token_typed, err);
|
||||
CommonWebError::ServerError // NB: This was probably *our* fault.
|
||||
})?;
|
||||
|
||||
let maybe_existing_stripe_customer_id = maybe_subscription.as_ref()
|
||||
.map(|sub| sub.stripe_customer_id.as_str());
|
||||
|
||||
let success_url = stripe_config.checkout_success_url.clone();
|
||||
let cancel_url = stripe_config.checkout_cancel_url.clone();
|
||||
|
||||
@@ -135,7 +161,8 @@ pub async fn stripe_artcraft_create_credits_pack_checkout_handler(
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
if let Some(existing_stripe_customer_id) = user_metadata.maybe_existing_stripe_customer_id.as_deref() {
|
||||
if let Some(existing_stripe_customer_id) = maybe_existing_stripe_customer_id{
|
||||
info!("Adding existing stripe customer id to checkout session: {}", existing_stripe_customer_id);
|
||||
match CustomerId::from_str(existing_stripe_customer_id) {
|
||||
Ok(customer_id) => {
|
||||
checkout_builder = checkout_builder.customer(customer_id);
|
||||
|
||||
@@ -20,14 +20,15 @@ normal = ["workspace-build-acceleration"]
|
||||
workspace-build-acceleration.workspace = true
|
||||
|
||||
# Internal
|
||||
#url_config = { path = "../../../lib/url_config" }
|
||||
artcraft_api_defs.workspace = true
|
||||
enums.workspace = true
|
||||
errors.workspace = true
|
||||
http_server_common = { path = "../../../lib/deprecated/http_server_common" }
|
||||
mysql_queries = { path = "../../../schema/database/mysql_queries" }
|
||||
reusable_types = { path = "../../../schema/public/reusable_types" }
|
||||
tokens.workspace = true
|
||||
user_traits_component = { path = "../user_traits_component" }
|
||||
#url_config = { path = "../../../lib/url_config" }
|
||||
|
||||
# External
|
||||
actix-http.workspace = true
|
||||
|
||||
@@ -4,6 +4,9 @@ use std::fmt::{Display, Formatter};
|
||||
use actix_web::HttpRequest;
|
||||
use async_trait::async_trait;
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::MySql;
|
||||
use tokens::tokens::users::UserToken;
|
||||
//#[cfg(test)]
|
||||
//use mockall::automock;
|
||||
|
||||
@@ -35,9 +38,12 @@ impl Error for InternalUserLookupError {}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct UserMetadata {
|
||||
/// Internal system primary key identifier of the user.
|
||||
/// Internal system primary key identifier of the user, stringly typed.
|
||||
pub user_token: String,
|
||||
|
||||
/// Internal system primary key identifier of the user, strongly typed.
|
||||
pub user_token_typed: UserToken,
|
||||
|
||||
/// Internal system username for the user.
|
||||
/// We will associate this to Stripe objects if available.
|
||||
pub username: Option<String>,
|
||||
@@ -80,4 +86,6 @@ pub trait InternalUserLookup {
|
||||
/// Lookup a user's session details from an HTTP request, then return the
|
||||
/// relevant pieces for the Stripe integration.
|
||||
async fn lookup_user_from_http_request(&self, http_request: &HttpRequest) -> Result<Option<UserMetadata>, InternalUserLookupError>;
|
||||
|
||||
async fn lookup_user_from_http_request_and_mysql_connection(&self, http_request: &HttpRequest, mysql_connection: &mut PoolConnection<MySql>) -> Result<Option<UserMetadata>, InternalUserLookupError>;
|
||||
}
|
||||
@@ -4,7 +4,8 @@ use async_trait::async_trait;
|
||||
use chrono::Utc;
|
||||
use component_traits::traits::internal_user_lookup::{InternalUserLookup, InternalUserLookupError, SubscriptionKey, UserMetadata};
|
||||
use log::warn;
|
||||
use sqlx::MySqlPool;
|
||||
use sqlx::{MySql, MySqlPool};
|
||||
use sqlx::pool::PoolConnection;
|
||||
|
||||
/// A simple Actix injectable action
|
||||
#[derive(Clone)]
|
||||
@@ -33,8 +34,12 @@ impl InternalUserLookup for StripeInternalUserLookupImpl {
|
||||
InternalUserLookupError::ServerError
|
||||
})?;
|
||||
|
||||
self.lookup_user_from_http_request_and_mysql_connection(http_request, &mut mysql_connection).await
|
||||
}
|
||||
|
||||
async fn lookup_user_from_http_request_and_mysql_connection(&self, http_request: &HttpRequest, mysql_connection: &mut PoolConnection<MySql>) -> Result<Option<UserMetadata>, InternalUserLookupError> {
|
||||
let maybe_user_session = self.session_checker
|
||||
.maybe_get_user_session_extended_from_connection(&http_request, &mut mysql_connection)
|
||||
.maybe_get_user_session_extended_from_connection(&http_request, mysql_connection)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
@@ -47,6 +52,7 @@ impl InternalUserLookup for StripeInternalUserLookupImpl {
|
||||
None => Ok(None),
|
||||
Some(user_session) => Ok(Some(UserMetadata {
|
||||
user_token: user_session.user_token,
|
||||
user_token_typed: user_session.user_token_typed,
|
||||
username: Some(user_session.user.username),
|
||||
user_email: Some(user_session.user.email_address),
|
||||
maybe_existing_stripe_customer_id: user_session.premium.maybe_stripe_customer_id,
|
||||
|
||||
Reference in new issue
Block a user