mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 01:38:44 +00:00
ugh, I hate this part of the project
This commit is contained in:
1 parent
f066658c3c
commit
e1d4c0f5f3
11 files changed
+166
-18
No files matched your search
@@ -0,0 +1,5 @@
|
||||
//! Keys that are used as metadata frequently.
|
||||
|
||||
pub const METADATA_USER_TOKEN : &'static str = "user_token";
|
||||
pub const METADATA_USERNAME : &'static str = "username";
|
||||
pub const METADATA_EMAIL : &'static str = "email";
|
||||
@@ -1 +1,2 @@
|
||||
pub mod common_metadata_keys;
|
||||
pub mod expand_customer_id;
|
||||
+25
-7
@@ -3,9 +3,12 @@ use container_common::anyhow_result::AnyhowResult;
|
||||
use crate::stripe::stripe_config::StripeConfig;
|
||||
use log::error;
|
||||
use std::collections::HashMap;
|
||||
use stripe::{CheckoutSession, CheckoutSessionMode, CreateCheckoutSession, CreateCheckoutSessionLineItems};
|
||||
use stripe::{CheckoutSession, CheckoutSessionMode, CreateCheckoutSession, CreateCheckoutSessionLineItems, CreateCheckoutSessionPaymentIntentData, CreateCheckoutSessionSubscriptionData};
|
||||
use crate::stripe::helpers::common_metadata_keys::{METADATA_EMAIL, METADATA_USER_TOKEN, METADATA_USERNAME};
|
||||
|
||||
// NB: These are "test" product IDs.
|
||||
// TODO: Pass these in via request; validate via a dynamically dispatched trait callable that can do
|
||||
// whatever the calling codebase needs
|
||||
pub const PRODUCT_FAKEYOU_BASIC_ID : &'static str = "prod_MMxi2J5y69VPbO";
|
||||
pub const PRODUCT_FAKEYOU_BASIC_PRICE_ID : &'static str = "price_1LeDnKEU5se17MekVr1iYYNf";
|
||||
|
||||
@@ -31,12 +34,6 @@ pub async fn stripe_create_checkout_session_shared(
|
||||
.as_deref()
|
||||
.ok_or(anyhow!("Checkout Cancel URL not configured"))?;
|
||||
|
||||
let mut metadata = HashMap::new();
|
||||
|
||||
if let Some(token) = user_token {
|
||||
metadata.insert("user_token".to_string(), token.to_string());
|
||||
}
|
||||
|
||||
let checkout_session = {
|
||||
let mut params = CreateCheckoutSession::new(
|
||||
cancel_url,
|
||||
@@ -45,6 +42,27 @@ pub async fn stripe_create_checkout_session_shared(
|
||||
|
||||
params.mode = Some(CheckoutSessionMode::Subscription);
|
||||
|
||||
let mut metadata = HashMap::new();
|
||||
|
||||
if let Some(token) = user_token {
|
||||
metadata.insert(METADATA_USER_TOKEN.to_string(), token.to_string());
|
||||
metadata.insert(METADATA_USERNAME.to_string(), token.to_string());
|
||||
metadata.insert(METADATA_EMAIL.to_string(), token.to_string());
|
||||
}
|
||||
|
||||
// NB: This metadata attaches to the subscription entity itself.
|
||||
// https://support.stripe.com/questions/using-metadata-with-checkout-sessions
|
||||
params.subscription_data = Some(CreateCheckoutSessionSubscriptionData {
|
||||
metadata,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// If not a subscription -
|
||||
//params.payment_intent_data = Some(CreateCheckoutSessionPaymentIntentData {
|
||||
// metadata: metadata.clone(),
|
||||
// ..Default::default()
|
||||
//});
|
||||
|
||||
params.line_items = Some(vec![
|
||||
CreateCheckoutSessionLineItems {
|
||||
price: Some(PRODUCT_FAKEYOU_BASIC_PRICE_ID.to_string()),
|
||||
|
||||
+42
-4
@@ -6,6 +6,8 @@ use actix_web::{web, HttpResponse, HttpRequest};
|
||||
use chrono::{DateTime, Utc};
|
||||
use crate::stripe::stripe_config::StripeConfig;
|
||||
use crate::stripe::webhook_event_handlers::checkout_session_completed_handler::checkout_session_completed_handler;
|
||||
use crate::stripe::webhook_event_handlers::customer_subscription::customer_subscription_created_handler::customer_subscription_created_handler;
|
||||
use crate::stripe::webhook_event_handlers::customer_subscription::customer_subscription_updated_handler::customer_subscription_updated_handler;
|
||||
use crate::stripe::webhook_event_handlers::invoice_paid_handler::invoice_paid_handler;
|
||||
use crate::stripe::webhook_event_handlers::stripe_webhook_error::StripeWebhookError;
|
||||
use http_server_common::request::get_request_header_optional::get_request_header_optional;
|
||||
@@ -16,6 +18,7 @@ use sqlx::MySqlPool;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use stripe::{EventObject, EventType, PaymentIntentStatus, Webhook};
|
||||
use crate::stripe::webhook_event_handlers::customer_subscription::customer_subscription_deleted_handler::customer_subscription_deleted_handler;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct StripeWebhookSuccessResponse {
|
||||
@@ -51,6 +54,13 @@ pub async fn stripe_webhook_handler(
|
||||
|
||||
warn!("Event type: {:?}", webhook_payload.event_type);
|
||||
|
||||
// Events seen (handled):
|
||||
//
|
||||
// CustomerSubscriptionCreated ------
|
||||
// CustomerSubscriptionUpdated -------
|
||||
//
|
||||
// Events seen (not yet handled):
|
||||
//
|
||||
// CheckoutSessionCompleted
|
||||
// ChargeSucceeded
|
||||
// PaymentMethodAttached
|
||||
@@ -58,9 +68,7 @@ pub async fn stripe_webhook_handler(
|
||||
// CustomerUpdated
|
||||
// InvoiceCreated
|
||||
// InvoiceFinalized
|
||||
// CustomerSubscriptionCreated ------
|
||||
// InvoiceUpdated
|
||||
// CustomerSubscriptionUpdated -------
|
||||
// InvoicePaymentSucceeded
|
||||
// PaymentIntentSucceeded
|
||||
// PaymentIntentCreated
|
||||
@@ -71,6 +79,9 @@ pub async fn stripe_webhook_handler(
|
||||
// NB: Whether this was test data or live data
|
||||
let is_production = webhook_payload.livemode;
|
||||
|
||||
// NB:
|
||||
// - "Webhook endpoints might occasionally receive the same event more than once."
|
||||
// - "Stripe does not guarantee delivery of events in the order in which they are generated."
|
||||
match webhook_payload.event_type {
|
||||
EventType::SubscriptionScheduleAborted => {}
|
||||
EventType::SubscriptionScheduleCanceled => {}
|
||||
@@ -80,11 +91,38 @@ pub async fn stripe_webhook_handler(
|
||||
EventType::SubscriptionScheduleReleased => {}
|
||||
EventType::SubscriptionScheduleUpdated => {}
|
||||
|
||||
// =============== CHECKOUT SESSIONS ===============
|
||||
|
||||
EventType::CheckoutSessionCompleted => {
|
||||
if let EventObject::CheckoutSession(checkout_session) = webhook_payload.data.object {
|
||||
let _r = checkout_session_completed_handler(checkout_session)?;
|
||||
}
|
||||
}
|
||||
|
||||
// =============== CUSTOMER SUBSCRIPTIONS ===============
|
||||
|
||||
EventType::CustomerSubscriptionCreated => {
|
||||
if let EventObject::Subscription(subscription) = webhook_payload.data.object {
|
||||
let _r = customer_subscription_created_handler(&subscription)?;
|
||||
}
|
||||
}
|
||||
EventType::CustomerSubscriptionUpdated => {
|
||||
if let EventObject::Subscription(subscription) = webhook_payload.data.object {
|
||||
let _r = customer_subscription_updated_handler(&subscription)?;
|
||||
}
|
||||
}
|
||||
EventType::CustomerSubscriptionDeleted => {
|
||||
if let EventObject::Subscription(subscription) = webhook_payload.data.object {
|
||||
let _r = customer_subscription_deleted_handler(&subscription)?;
|
||||
}
|
||||
}
|
||||
EventType::CustomerSubscriptionPendingUpdateApplied => {
|
||||
}
|
||||
EventType::CustomerSubscriptionPendingUpdateExpired => {
|
||||
}
|
||||
|
||||
// =============== INVOICES ===============
|
||||
|
||||
EventType::InvoiceCreated => {
|
||||
// TODO: We need to respond to this so we don't hold payments up by 72 hours!
|
||||
// See: https://stripe.com/docs/billing/subscriptions/webhooks
|
||||
@@ -94,12 +132,12 @@ pub async fn stripe_webhook_handler(
|
||||
let _r = invoice_paid_handler(&invoice)?;
|
||||
}
|
||||
}
|
||||
EventType::InvoicePaymentFailed => {
|
||||
}
|
||||
EventType::PaymentIntentSucceeded => {
|
||||
if let EventObject::PaymentIntent(payment_intent) = webhook_payload.data.object {
|
||||
}
|
||||
}
|
||||
EventType::InvoicePaymentFailed => {
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -1,6 +1,8 @@
|
||||
use log::{error, warn};
|
||||
use container_common::anyhow_result::AnyhowResult;
|
||||
use crate::stripe::helpers::expand_customer_id::expand_customer_id;
|
||||
use stripe::CheckoutSession;
|
||||
use crate::stripe::helpers::common_metadata_keys::METADATA_USER_TOKEN;
|
||||
use crate::stripe::webhook_event_handlers::stripe_webhook_error::StripeWebhookError;
|
||||
|
||||
// After the subscription signup succeeds, the customer returns to your website at the success_url,
|
||||
@@ -10,7 +12,6 @@ use crate::stripe::webhook_event_handlers::stripe_webhook_error::StripeWebhookEr
|
||||
// and send them to the customer portal to update their payment method.
|
||||
pub fn checkout_session_completed_handler(checkout_session: CheckoutSession) -> Result<(), StripeWebhookError> {
|
||||
|
||||
|
||||
let stripe_checkout_id = checkout_session.id.to_string();
|
||||
|
||||
// NB: We'll need this to send them to the "customer portal", which is how they can modify or cancel
|
||||
@@ -20,8 +21,10 @@ pub fn checkout_session_completed_handler(checkout_session: CheckoutSession) ->
|
||||
.map(|c| expand_customer_id(c));
|
||||
|
||||
// NB: Our internal user token.
|
||||
let maybe_user_token = checkout_session.metadata.get("user_token")
|
||||
let maybe_user_token = checkout_session.metadata.get(METADATA_USER_TOKEN)
|
||||
.map(|t| t.to_string());
|
||||
|
||||
error!(">>> checkout.session.completed: {:?}, {:?}", maybe_stripe_customer_id, maybe_user_token);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
use crate::stripe::helpers::common_metadata_keys::METADATA_USER_TOKEN;
|
||||
use crate::stripe::helpers::expand_customer_id::expand_customer_id;
|
||||
use crate::stripe::webhook_event_handlers::stripe_webhook_error::StripeWebhookError;
|
||||
use log::{error, warn};
|
||||
use stripe::Subscription;
|
||||
|
||||
/// Handle event type: 'customer.subscription.created'
|
||||
pub fn customer_subscription_created_handler(subscription: &Subscription) -> Result<(), StripeWebhookError> {
|
||||
|
||||
let stripe_subscription_id = subscription.id.to_string();
|
||||
|
||||
// NB: We'll need this to send them to the "customer portal", which is how they can modify or
|
||||
// cancel their subscriptions.
|
||||
let maybe_stripe_customer_id = subscription.customer
|
||||
.as_ref()
|
||||
.map(|c| expand_customer_id(c));
|
||||
|
||||
// NB: Our internal user token.
|
||||
let maybe_user_token = subscription.metadata.get(METADATA_USER_TOKEN)
|
||||
.map(|t| t.to_string());
|
||||
|
||||
error!(">>> customer.subscription.created: {:?}, {:?}, {:?}",
|
||||
stripe_subscription_id, maybe_stripe_customer_id, maybe_user_token);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
use crate::stripe::helpers::common_metadata_keys::METADATA_USER_TOKEN;
|
||||
use crate::stripe::helpers::expand_customer_id::expand_customer_id;
|
||||
use crate::stripe::webhook_event_handlers::stripe_webhook_error::StripeWebhookError;
|
||||
use log::{error, warn};
|
||||
use stripe::Subscription;
|
||||
|
||||
/// Handle event type: 'customer.subscription.deleted'
|
||||
pub fn customer_subscription_deleted_handler(subscription: &Subscription) -> Result<(), StripeWebhookError> {
|
||||
|
||||
let stripe_subscription_id = subscription.id.to_string();
|
||||
|
||||
// NB: We'll need this to send them to the "customer portal", which is how they can modify or
|
||||
// cancel their subscriptions.
|
||||
let maybe_stripe_customer_id = subscription.customer
|
||||
.as_ref()
|
||||
.map(|c| expand_customer_id(c));
|
||||
|
||||
// NB: Our internal user token.
|
||||
let maybe_user_token = subscription.metadata.get(METADATA_USER_TOKEN)
|
||||
.map(|t| t.to_string());
|
||||
|
||||
error!(">>> customer.subscription.deleted: {:?}, {:?}, {:?}",
|
||||
stripe_subscription_id, maybe_stripe_customer_id, maybe_user_token);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
use crate::stripe::helpers::common_metadata_keys::METADATA_USER_TOKEN;
|
||||
use crate::stripe::helpers::expand_customer_id::expand_customer_id;
|
||||
use crate::stripe::webhook_event_handlers::stripe_webhook_error::StripeWebhookError;
|
||||
use log::{error, warn};
|
||||
use stripe::Subscription;
|
||||
|
||||
/// Handle event type: 'customer.subscription.updated'
|
||||
pub fn customer_subscription_updated_handler(subscription: &Subscription) -> Result<(), StripeWebhookError> {
|
||||
|
||||
let stripe_subscription_id = subscription.id.to_string();
|
||||
|
||||
// NB: We'll need this to send them to the "customer portal", which is how they can modify or
|
||||
// cancel their subscriptions.
|
||||
let maybe_stripe_customer_id = subscription.customer
|
||||
.as_ref()
|
||||
.map(|c| expand_customer_id(c));
|
||||
|
||||
// NB: Our internal user token.
|
||||
let maybe_user_token = subscription.metadata.get(METADATA_USER_TOKEN)
|
||||
.map(|t| t.to_string());
|
||||
|
||||
error!(">>> customer.subscription.updated: {:?}, {:?}, {:?}",
|
||||
stripe_subscription_id, maybe_stripe_customer_id, maybe_user_token);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
pub mod customer_subscription_created_handler;
|
||||
pub mod customer_subscription_deleted_handler;
|
||||
pub mod customer_subscription_updated_handler;
|
||||
+3
-3
@@ -2,6 +2,7 @@ use container_common::anyhow_result::AnyhowResult;
|
||||
use crate::stripe::helpers::expand_customer_id::expand_customer_id;
|
||||
use log::warn;
|
||||
use stripe::Invoice;
|
||||
use crate::stripe::helpers::common_metadata_keys::METADATA_USER_TOKEN;
|
||||
use crate::stripe::webhook_event_handlers::stripe_webhook_error::StripeWebhookError;
|
||||
|
||||
// https://stripe.com/docs/billing/subscriptions/webhooks :
|
||||
@@ -29,11 +30,10 @@ pub fn invoice_paid_handler(invoice: &Invoice) -> Result<(), StripeWebhookError>
|
||||
.map(|c| expand_customer_id(c));
|
||||
|
||||
// NB: Our internal user token.
|
||||
let maybe_user_token = invoice.metadata.get("user_token")
|
||||
let maybe_user_token = invoice.metadata.get(METADATA_USER_TOKEN)
|
||||
.map(|t| t.to_string());
|
||||
|
||||
|
||||
warn!("invoice_paid: {:?}, {:?}, {:?}", paid_status, maybe_stripe_customer_id, maybe_user_token);
|
||||
warn!(">>> invoice.paid: {:?}, {:?}, {:?}", paid_status, maybe_stripe_customer_id, maybe_user_token);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
pub (crate) mod checkout_session_completed_handler;
|
||||
pub (crate) mod invoice_paid_handler;
|
||||
pub mod stripe_webhook_error;
|
||||
|
||||
pub (crate) mod checkout_session_completed_handler;
|
||||
pub (crate) mod customer_subscription;
|
||||
pub (crate) mod invoice_paid_handler;
|
||||
Reference in new issue
Block a user