mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 17:38:10 +00:00
endpoints wip
This commit is contained in:
1 parent
a752c64a99
commit
671ac9310f
8 files changed
+359
-140
No files matched your search
+54
@@ -0,0 +1,54 @@
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const CREATE_NEW_USER_ACCOUNT_AND_SUBSCRIPTION_CHECKOUT_URL_PATH: &str = "/v1/stripe_artcraft/user_signup/subscription_checkout";
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCreateSubscriptionCheckoutWithUserSignupRequest {
|
||||
/// The (non-Stripe) internal identifier for the product or subscription.
|
||||
/// This will be translated into a Stripe identifier.
|
||||
pub plan: Option<ArtcraftSubscriptionSlug>,
|
||||
|
||||
pub cadence: Option<PlanBillingCadence>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema, Debug, Copy, Clone)]
|
||||
pub enum PlanBillingCadence {
|
||||
#[serde(rename = "monthly")]
|
||||
Monthly,
|
||||
|
||||
#[serde(rename = "yearly")]
|
||||
Yearly,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCreateSubscriptionCheckoutWithUserSignupResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// The checkout session URL.
|
||||
pub stripe_checkout_redirect_url: String,
|
||||
|
||||
/// If a user account was created, these are the details.
|
||||
pub generated_user: Option<UserDetails>,
|
||||
|
||||
/// If a session was created, these are the details.
|
||||
pub session: Option<UserDetails>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UserDetails {
|
||||
/// The generated username.
|
||||
pub username: String,
|
||||
|
||||
/// The generated display name.
|
||||
pub display_name: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct SessionDetails {
|
||||
/// A signed session that can be sent as a header, bypassing cookies.
|
||||
/// This is useful for API clients that don't support cookies or Google
|
||||
/// browsers killing cross-domain cookies.
|
||||
pub signed_session: String,
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod create_create_new_user_account_and_subscription_checkout;
|
||||
pub mod create_credits_pack_checkout;
|
||||
pub mod create_subscription_checkout;
|
||||
pub mod customer_portal_cancel_plan;
|
||||
|
||||
+8
-140
@@ -24,7 +24,6 @@ use user_traits_component::traits::internal_session_cache_purge::InternalSession
|
||||
|
||||
//use utoipa::ToSchema;
|
||||
|
||||
// /// Create a Stripe Checkout session and return the redirect URL in Json.
|
||||
// #[utoipa::path(
|
||||
// get,
|
||||
// tag = "Stripe (Artcraft)",
|
||||
@@ -36,6 +35,10 @@ use user_traits_component::traits::internal_session_cache_purge::InternalSession
|
||||
// (status = 200, description = "Success Delete", body = CreateCheckoutSessionSuccessResponse),
|
||||
// ),
|
||||
// )]
|
||||
|
||||
|
||||
/// Create a Stripe Checkout session *with* user signup and return the redirect URL in Json.
|
||||
/// If the user is already logged in, we just attach it to that record instead.
|
||||
pub async fn stripe_artcraft_create_subscription_session_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<StripeArtcraftCreateSubscriptionCheckoutRequest>,
|
||||
@@ -82,145 +85,10 @@ pub async fn stripe_artcraft_create_subscription_session_handler(
|
||||
// NB: Our integration relies on an internal user token being present.
|
||||
let user_metadata = match maybe_user_metadata {
|
||||
None => return Err(CommonWebError::NotAuthorized),
|
||||
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_active_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.
|
||||
})?;
|
||||
|
||||
if maybe_active_subscription.is_some() {
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(
|
||||
"user already has an active subscription plan; use the portal to update the plan"
|
||||
.to_string()))
|
||||
}
|
||||
|
||||
// NB: This works, but it feels really weird to have the customer's old email shown in a
|
||||
// completely immutable state in the stripe checkout form.
|
||||
//
|
||||
// let maybe_inactive_subscription = find_possibly_inactive_first_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 ({}) possibly existing inactive subscription: {:?}",
|
||||
// &user_metadata.user_token_typed, err);
|
||||
// CommonWebError::ServerError // NB: This was probably *our* fault.
|
||||
// })?;
|
||||
// let maybe_existing_inactive_stripe_customer_id = maybe_inactive_subscription.as_ref()
|
||||
// .map(|sub| sub.stripe_customer_id.as_str());
|
||||
|
||||
let mut maybe_existing_stripe_customer_id = None;
|
||||
|
||||
let result = find_user_stripe_customer_link_using_connection(
|
||||
&user_metadata.user_token_typed,
|
||||
PaymentsNamespace::Artcraft,
|
||||
&mut mysql_connection
|
||||
).await;
|
||||
|
||||
// NB: Fail silently.
|
||||
if let Ok(Some(link)) = result {
|
||||
maybe_existing_stripe_customer_id = CustomerId::from_str(&link.stripe_customer_id).ok();
|
||||
} else if let Err(err) = result {
|
||||
warn!("Error looking up user's ({}) existing stripe customer link: {:?}", &user_metadata.user_token_typed, err);
|
||||
}
|
||||
|
||||
let success_url = stripe_config.checkout_success_url.clone();
|
||||
let cancel_url = stripe_config.checkout_cancel_url.clone();
|
||||
|
||||
let checkout_session = {
|
||||
// `client_reference_id`
|
||||
// Stripe Docs:
|
||||
// A unique string to reference the Checkout Session.
|
||||
// This can be a customer ID, a cart ID, or similar, and can be used to reconcile the session
|
||||
// with your internal systems.
|
||||
//
|
||||
// Our Notes:
|
||||
// This gets reported back in the Checkout Session (and related webhooks) as
|
||||
// `client_reference_id`. Passing the same ID on multiple checkouts does not unify or
|
||||
// cross-correlate customers and only seems to be metadata for the checkout session itself.
|
||||
// This is probably only useful for tracking checkout session engagement.
|
||||
//params.client_reference_id = Some("SOME_INTERNAL_ID");
|
||||
|
||||
// `customer_email`
|
||||
// Stripe Docs:
|
||||
// If provided, this value will be used when the Customer object is created. If not provided,
|
||||
// customers will be asked to enter their email address. Use this parameter to prefill
|
||||
// customer data if you already have an email on file. To access information about the
|
||||
// customer once a session is complete, use the customer field.
|
||||
//
|
||||
// Our Notes:
|
||||
// This does not look up previous customers with the same email and will not unify or
|
||||
// cross-correlate customers. By default the field will be un-editable in the checkout flow
|
||||
// if this is specified.
|
||||
//params.customer_email = Some("email@example.com");
|
||||
|
||||
let mut metadata = HashMap::new();
|
||||
|
||||
metadata.insert(STRIPE_ARTCRAFT_METADATA_USER_TOKEN.to_string(), user_metadata.user_token.to_string());
|
||||
|
||||
if let Some(username) = user_metadata.username.as_deref() {
|
||||
metadata.insert(STRIPE_ARTCRAFT_METADATA_USERNAME.to_string(), username.to_string());
|
||||
}
|
||||
|
||||
if let Some(user_email) = user_metadata.user_email.as_deref() {
|
||||
metadata.insert(STRIPE_ARTCRAFT_METADATA_EMAIL.to_string(), user_email.to_string());
|
||||
}
|
||||
|
||||
let mut checkout_builder = CreateCheckoutSession::new()
|
||||
.success_url(&success_url)
|
||||
.cancel_url(&cancel_url)
|
||||
.mode(CheckoutSessionMode::Subscription)
|
||||
.line_items(vec![
|
||||
CreateCheckoutSessionLineItems {
|
||||
price: Some(price_id.to_string()),
|
||||
quantity: Some(1),
|
||||
..Default::default()
|
||||
}
|
||||
])
|
||||
.saved_payment_method_options(CreateCheckoutSessionSavedPaymentMethodOptions {
|
||||
allow_redisplay_filters: Some(vec![
|
||||
CreateCheckoutSessionSavedPaymentMethodOptionsAllowRedisplayFilters::Always,
|
||||
CreateCheckoutSessionSavedPaymentMethodOptionsAllowRedisplayFilters::Limited,
|
||||
CreateCheckoutSessionSavedPaymentMethodOptionsAllowRedisplayFilters::Unspecified,
|
||||
]),
|
||||
// The user can choose to tick a checkbox that saves their card for redisplay later.
|
||||
payment_method_save: Some(CreateCheckoutSessionSavedPaymentMethodOptionsPaymentMethodSave::Enabled),
|
||||
})
|
||||
.allow_promotion_codes(true) // Allow promo codes / coupons
|
||||
.automatic_tax(CreateCheckoutSessionAutomaticTax {
|
||||
enabled: true, // This will ask for the customer's location
|
||||
liability: None,
|
||||
})
|
||||
.metadata(metadata.clone())
|
||||
.subscription_data(CreateCheckoutSessionSubscriptionData {
|
||||
metadata: Some(metadata),
|
||||
..Default::default()
|
||||
})
|
||||
;
|
||||
|
||||
if let Some(customer_id) = maybe_existing_stripe_customer_id {
|
||||
info!("Adding existing stripe customer id to checkout session: {}", customer_id.as_str());
|
||||
checkout_builder = checkout_builder.customer(customer_id);
|
||||
}
|
||||
|
||||
let checkout_session = checkout_builder
|
||||
.send(&stripe_config.client)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Stripe Error: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
checkout_session
|
||||
Some(user_metadata) => {
|
||||
|
||||
user_exists_case
|
||||
},
|
||||
};
|
||||
|
||||
let url = checkout_session.url.ok_or(CommonWebError::ServerError)?;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod stripe_artcraft_create_subscription_checkout_with_user_signup_handler;
|
||||
pub (super) mod user_exists_case;
|
||||
pub (super) mod user_creation_case;
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
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::checkout_with_user_signup::user_creation_case::user_creation_case;
|
||||
use crate::endpoints::checkout_with_user_signup::user_exists_case::user_exists_case;
|
||||
use crate::utils::artcraft_stripe_config::ArtcraftStripeConfigWithClient;
|
||||
use crate::utils::common_web_error::CommonWebError;
|
||||
use actix_web::web::{Data, Json};
|
||||
use actix_web::{web, HttpRequest};
|
||||
use artcraft_api_defs::stripe_artcraft::create_create_new_user_account_and_subscription_checkout::{PlanBillingCadence, StripeArtcraftCreateSubscriptionCheckoutWithUserSignupRequest, StripeArtcraftCreateSubscriptionCheckoutWithUserSignupResponse};
|
||||
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_stripe_customer_links::find_user_stripe_customer_link::find_user_stripe_customer_link_using_connection;
|
||||
use mysql_queries::queries::users::user_subscriptions::find_possibly_inactive_first_subscription_for_owner_user::find_possibly_inactive_first_subscription_for_owner_user_using_connection;
|
||||
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;
|
||||
use stripe_checkout::checkout_session::{CreateCheckoutSession, CreateCheckoutSessionAutomaticTax, CreateCheckoutSessionLineItems, CreateCheckoutSessionSavedPaymentMethodOptions, CreateCheckoutSessionSavedPaymentMethodOptionsAllowRedisplayFilters, CreateCheckoutSessionSavedPaymentMethodOptionsPaymentMethodSave, CreateCheckoutSessionSubscriptionData};
|
||||
use stripe_checkout::CheckoutSessionMode;
|
||||
use stripe_core::CustomerId;
|
||||
use user_traits_component::traits::internal_session_cache_purge::InternalSessionCachePurge;
|
||||
// /// Create a new user account and Stripe Checkout session and return the redirect URL in Json.
|
||||
// #[utoipa::path(
|
||||
// get,
|
||||
// tag = "Stripe (Artcraft)",
|
||||
// path = "/v1/stripe_artcraft/user_signup/subscription_checkout",
|
||||
// params(
|
||||
// ("request" = CreateCheckoutSessionRequest, description = "Payload for Request"),
|
||||
// ),
|
||||
// responses(
|
||||
// (status = 200, description = "Success Delete", body = CreateCheckoutSessionSuccessResponse),
|
||||
// ),
|
||||
// )]
|
||||
|
||||
/// Create a Stripe Checkout session *with* user signup and return the redirect URL in Json.
|
||||
/// If the user is already logged in, we just attach it to that record instead.
|
||||
pub async fn stripe_artcraft_create_checkout_with_user_signup_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<StripeArtcraftCreateSubscriptionCheckoutWithUserSignupRequest>,
|
||||
stripe_config: Data<ArtcraftStripeConfigWithClient>,
|
||||
server_environment: Data<ServerEnvironment>,
|
||||
internal_user_lookup: Data<dyn InternalUserLookup>,
|
||||
internal_session_cache_purge: Data<dyn InternalSessionCachePurge>,
|
||||
mysql_pool: Data<MySqlPool>,
|
||||
) -> Result<Json<StripeArtcraftCreateSubscriptionCheckoutWithUserSignupResponse>, CommonWebError>
|
||||
{
|
||||
let slug = match request.plan {
|
||||
None => return Err(CommonWebError::BadInputWithSimpleMessage("no plan supplied".to_string())),
|
||||
Some(slug) => slug,
|
||||
};
|
||||
|
||||
let cadence = match request.cadence {
|
||||
None => return Err(CommonWebError::BadInputWithSimpleMessage("no cadence supplied".to_string())),
|
||||
Some(cadence) => cadence,
|
||||
};
|
||||
|
||||
let plan = get_artcraft_subscription_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 price_id = match cadence {
|
||||
PlanBillingCadence::Monthly => plan.monthly_price_id.clone(),
|
||||
PlanBillingCadence::Yearly => plan.yearly_price_id.clone(),
|
||||
};
|
||||
|
||||
let maybe_user_metadata = internal_user_lookup
|
||||
.lookup_user_from_http_request_and_mysql_connection(&http_request, &mut mysql_connection)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error looking up user: {:?}", err);
|
||||
CommonWebError::ServerError // NB: This was probably *our* fault.
|
||||
})?;
|
||||
|
||||
let checkout_session = match maybe_user_metadata {
|
||||
None => {
|
||||
info!("Creating new user, then creating checkout session...");
|
||||
user_creation_case(
|
||||
&price_id,
|
||||
&mut mysql_connection,
|
||||
&stripe_config,
|
||||
).await?
|
||||
},
|
||||
Some(user_metadata) => {
|
||||
info!("Creating checkout session for user: {:?}", user_metadata.user_token_typed);
|
||||
user_exists_case(
|
||||
&price_id,
|
||||
&user_metadata,
|
||||
&mut mysql_connection,
|
||||
&stripe_config,
|
||||
).await?
|
||||
},
|
||||
};
|
||||
|
||||
let url = checkout_session.url.ok_or(CommonWebError::ServerError)?;
|
||||
|
||||
// Best effort to delete Redis session cache
|
||||
internal_session_cache_purge.best_effort_purge_session_cache(&http_request);
|
||||
|
||||
Ok(Json(StripeArtcraftCreateSubscriptionCheckoutWithUserSignupResponse {
|
||||
success: true,
|
||||
stripe_checkout_redirect_url: url,
|
||||
generated_user: None,
|
||||
session: None,
|
||||
}))
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
use crate::utils::artcraft_stripe_config::ArtcraftStripeConfigWithClient;
|
||||
use crate::utils::common_web_error::CommonWebError;
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::MySql;
|
||||
use stripe_shared::CheckoutSession;
|
||||
|
||||
pub (super) async fn user_creation_case(
|
||||
price_id: &str,
|
||||
mysql_connection: &mut PoolConnection<MySql>,
|
||||
stripe_config: &ArtcraftStripeConfigWithClient,
|
||||
) -> Result<CheckoutSession, CommonWebError> {
|
||||
Ok(())
|
||||
}
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
use crate::configs::stripe_artcraft_metadata_keys::{STRIPE_ARTCRAFT_METADATA_EMAIL, STRIPE_ARTCRAFT_METADATA_USERNAME, STRIPE_ARTCRAFT_METADATA_USER_TOKEN};
|
||||
use crate::utils::artcraft_stripe_config::ArtcraftStripeConfigWithClient;
|
||||
use crate::utils::common_web_error::CommonWebError;
|
||||
use actix_web::web::Data;
|
||||
use component_traits::traits::internal_user_lookup::UserMetadata;
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use log::{error, info, warn};
|
||||
use mysql_queries::queries::users::user_stripe_customer_links::find_user_stripe_customer_link::find_user_stripe_customer_link_using_connection;
|
||||
use mysql_queries::queries::users::user_subscriptions::find_subscription_for_owner_user::find_subscription_for_owner_user_using_connection;
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::MySql;
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use stripe_checkout::checkout_session::{CreateCheckoutSession, CreateCheckoutSessionAutomaticTax, CreateCheckoutSessionLineItems, CreateCheckoutSessionSavedPaymentMethodOptions, CreateCheckoutSessionSavedPaymentMethodOptionsAllowRedisplayFilters, CreateCheckoutSessionSavedPaymentMethodOptionsPaymentMethodSave, CreateCheckoutSessionSubscriptionData};
|
||||
use stripe_shared::{CheckoutSession, CheckoutSessionMode, CustomerId, PriceId};
|
||||
|
||||
pub (super) async fn user_exists_case(
|
||||
price_id: &str,
|
||||
user_metadata: &UserMetadata,
|
||||
mysql_connection: &mut PoolConnection<MySql>,
|
||||
stripe_config: &ArtcraftStripeConfigWithClient,
|
||||
) -> Result<CheckoutSession, CommonWebError> {
|
||||
|
||||
// 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_active_subscription = find_subscription_for_owner_user_using_connection(
|
||||
&user_metadata.user_token_typed,
|
||||
PaymentsNamespace::Artcraft,
|
||||
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.
|
||||
})?;
|
||||
|
||||
if maybe_active_subscription.is_some() {
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(
|
||||
"user already has an active subscription plan; use the portal to update the plan"
|
||||
.to_string()))
|
||||
}
|
||||
|
||||
// NB: This works, but it feels really weird to have the customer's old email shown in a
|
||||
// completely immutable state in the stripe checkout form.
|
||||
//
|
||||
// let maybe_inactive_subscription = find_possibly_inactive_first_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 ({}) possibly existing inactive subscription: {:?}",
|
||||
// &user_metadata.user_token_typed, err);
|
||||
// CommonWebError::ServerError // NB: This was probably *our* fault.
|
||||
// })?;
|
||||
// let maybe_existing_inactive_stripe_customer_id = maybe_inactive_subscription.as_ref()
|
||||
// .map(|sub| sub.stripe_customer_id.as_str());
|
||||
|
||||
let mut maybe_existing_stripe_customer_id = None;
|
||||
|
||||
let result = find_user_stripe_customer_link_using_connection(
|
||||
&user_metadata.user_token_typed,
|
||||
PaymentsNamespace::Artcraft,
|
||||
mysql_connection
|
||||
).await;
|
||||
|
||||
// NB: Fail silently.
|
||||
if let Ok(Some(link)) = result {
|
||||
maybe_existing_stripe_customer_id = CustomerId::from_str(&link.stripe_customer_id).ok();
|
||||
} else if let Err(err) = result {
|
||||
warn!("Error looking up user's ({}) existing stripe customer link: {:?}", &user_metadata.user_token_typed, err);
|
||||
}
|
||||
|
||||
let success_url = stripe_config.checkout_success_url.clone();
|
||||
let cancel_url = stripe_config.checkout_cancel_url.clone();
|
||||
|
||||
let checkout_session = {
|
||||
// `client_reference_id`
|
||||
// Stripe Docs:
|
||||
// A unique string to reference the Checkout Session.
|
||||
// This can be a customer ID, a cart ID, or similar, and can be used to reconcile the session
|
||||
// with your internal systems.
|
||||
//
|
||||
// Our Notes:
|
||||
// This gets reported back in the Checkout Session (and related webhooks) as
|
||||
// `client_reference_id`. Passing the same ID on multiple checkouts does not unify or
|
||||
// cross-correlate customers and only seems to be metadata for the checkout session itself.
|
||||
// This is probably only useful for tracking checkout session engagement.
|
||||
//params.client_reference_id = Some("SOME_INTERNAL_ID");
|
||||
|
||||
// `customer_email`
|
||||
// Stripe Docs:
|
||||
// If provided, this value will be used when the Customer object is created. If not provided,
|
||||
// customers will be asked to enter their email address. Use this parameter to prefill
|
||||
// customer data if you already have an email on file. To access information about the
|
||||
// customer once a session is complete, use the customer field.
|
||||
//
|
||||
// Our Notes:
|
||||
// This does not look up previous customers with the same email and will not unify or
|
||||
// cross-correlate customers. By default the field will be un-editable in the checkout flow
|
||||
// if this is specified.
|
||||
//params.customer_email = Some("email@example.com");
|
||||
|
||||
let mut metadata = HashMap::new();
|
||||
|
||||
metadata.insert(STRIPE_ARTCRAFT_METADATA_USER_TOKEN.to_string(), user_metadata.user_token.to_string());
|
||||
|
||||
if let Some(username) = user_metadata.username.as_deref() {
|
||||
metadata.insert(STRIPE_ARTCRAFT_METADATA_USERNAME.to_string(), username.to_string());
|
||||
}
|
||||
|
||||
if let Some(user_email) = user_metadata.user_email.as_deref() {
|
||||
metadata.insert(STRIPE_ARTCRAFT_METADATA_EMAIL.to_string(), user_email.to_string());
|
||||
}
|
||||
|
||||
let mut checkout_builder = CreateCheckoutSession::new()
|
||||
.success_url(&success_url)
|
||||
.cancel_url(&cancel_url)
|
||||
.mode(CheckoutSessionMode::Subscription)
|
||||
.line_items(vec![
|
||||
CreateCheckoutSessionLineItems {
|
||||
price: Some(price_id.to_string()),
|
||||
quantity: Some(1),
|
||||
..Default::default()
|
||||
}
|
||||
])
|
||||
.saved_payment_method_options(CreateCheckoutSessionSavedPaymentMethodOptions {
|
||||
allow_redisplay_filters: Some(vec![
|
||||
CreateCheckoutSessionSavedPaymentMethodOptionsAllowRedisplayFilters::Always,
|
||||
CreateCheckoutSessionSavedPaymentMethodOptionsAllowRedisplayFilters::Limited,
|
||||
CreateCheckoutSessionSavedPaymentMethodOptionsAllowRedisplayFilters::Unspecified,
|
||||
]),
|
||||
// The user can choose to tick a checkbox that saves their card for redisplay later.
|
||||
payment_method_save: Some(CreateCheckoutSessionSavedPaymentMethodOptionsPaymentMethodSave::Enabled),
|
||||
})
|
||||
.allow_promotion_codes(true) // Allow promo codes / coupons
|
||||
.automatic_tax(CreateCheckoutSessionAutomaticTax {
|
||||
enabled: true, // This will ask for the customer's location
|
||||
liability: None,
|
||||
})
|
||||
.metadata(metadata.clone())
|
||||
.subscription_data(CreateCheckoutSessionSubscriptionData {
|
||||
metadata: Some(metadata),
|
||||
..Default::default()
|
||||
})
|
||||
;
|
||||
|
||||
if let Some(customer_id) = maybe_existing_stripe_customer_id {
|
||||
info!("Adding existing stripe customer id to checkout session: {}", customer_id.as_str());
|
||||
checkout_builder = checkout_builder.customer(customer_id);
|
||||
}
|
||||
|
||||
let checkout_session = checkout_builder
|
||||
.send(&stripe_config.client)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Stripe Error: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
checkout_session
|
||||
};
|
||||
|
||||
|
||||
Ok(checkout_session)
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod checkout;
|
||||
pub mod checkout_with_user_signup;
|
||||
pub mod customer_portal;
|
||||
pub mod webhook;
|
||||
Reference in new issue
Block a user