ugh, I hate this part of the project

This commit is contained in:
Brandon Thomas committed 2022-09-10 04:26:24 -04:00
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;
@@ -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()),
@@ -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 => {
}
_ => {},
}
@@ -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(())
}
@@ -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(())
}
@@ -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(())
}
@@ -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(())
}
@@ -0,0 +1,3 @@
pub mod customer_subscription_created_handler;
pub mod customer_subscription_deleted_handler;
pub mod customer_subscription_updated_handler;
@@ -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;