mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-02 16:38:14 +00:00
record whether webhook events had actions taken, idempotency flag
This commit is contained in:
1 parent
3fb26771de
commit
334fc3022e
17 files changed
+62
-27
No files matched your search
@@ -33,8 +33,12 @@ CREATE TABLE stripe_webhook_event_logs (
|
||||
|
||||
-- ========== INTERNAL SYSTEM METADATA ==========
|
||||
|
||||
-- Whether we handled the event
|
||||
event_was_handled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
-- Whether we handled the event with any sort of action
|
||||
action_was_taken BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
-- Whether we should ignore any future retries by Stripe replaying the event.
|
||||
-- This helps us treat events with idempotency.
|
||||
should_ignore_retry BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
-- Some event types may attach a user token
|
||||
maybe_user_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
+4
-2
@@ -98,7 +98,8 @@ pub async fn stripe_webhook_handler(
|
||||
maybe_user_token: None,
|
||||
maybe_event_entity_id: None,
|
||||
maybe_stripe_customer_id: None,
|
||||
event_was_handled: false
|
||||
action_was_taken: false,
|
||||
should_ignore_retry: false,
|
||||
};
|
||||
|
||||
// NB:
|
||||
@@ -298,7 +299,8 @@ pub async fn stripe_webhook_handler(
|
||||
stripe_event_created_at,
|
||||
stripe_is_production,
|
||||
maybe_user_token: webhook_summary.maybe_user_token,
|
||||
event_was_handled: webhook_summary.event_was_handled,
|
||||
action_was_taken: webhook_summary.action_was_taken,
|
||||
should_ignore_retry: webhook_summary.should_ignore_retry,
|
||||
};
|
||||
|
||||
query.insert(&mysql_pool)
|
||||
|
||||
+2
-1
@@ -26,6 +26,7 @@ pub fn charge_succeeded_handler(charge: &Charge) -> Result<StripeWebhookSummary,
|
||||
maybe_user_token: maybe_user_token,
|
||||
maybe_event_entity_id: Some(charge_id),
|
||||
maybe_stripe_customer_id: maybe_stripe_customer_id,
|
||||
event_was_handled: false,
|
||||
action_was_taken: false,
|
||||
should_ignore_retry: false,
|
||||
})
|
||||
}
|
||||
+2
-1
@@ -28,6 +28,7 @@ pub fn checkout_session_completed_handler(checkout_session: CheckoutSession) ->
|
||||
maybe_user_token: maybe_user_token,
|
||||
maybe_event_entity_id: Some(stripe_checkout_id),
|
||||
maybe_stripe_customer_id: maybe_stripe_customer_id,
|
||||
event_was_handled: false,
|
||||
action_was_taken: false,
|
||||
should_ignore_retry: false,
|
||||
})
|
||||
}
|
||||
+2
-1
@@ -20,6 +20,7 @@ pub fn customer_created_handler(customer: &Customer) -> Result<StripeWebhookSumm
|
||||
maybe_user_token: maybe_user_token,
|
||||
maybe_event_entity_id: Some(customer_id.clone()),
|
||||
maybe_stripe_customer_id: Some(customer_id.clone()),
|
||||
event_was_handled: false,
|
||||
action_was_taken: false,
|
||||
should_ignore_retry: false,
|
||||
})
|
||||
}
|
||||
+2
-1
@@ -19,6 +19,7 @@ pub fn customer_deleted_handler(customer: &Customer) -> Result<StripeWebhookSumm
|
||||
maybe_user_token: maybe_user_token,
|
||||
maybe_event_entity_id: Some(customer_id.clone()),
|
||||
maybe_stripe_customer_id: Some(customer_id.clone()),
|
||||
event_was_handled: false,
|
||||
action_was_taken: false,
|
||||
should_ignore_retry: false,
|
||||
})
|
||||
}
|
||||
+2
-1
@@ -19,6 +19,7 @@ pub fn customer_updated_handler(customer: &Customer) -> Result<StripeWebhookSumm
|
||||
maybe_user_token: maybe_user_token,
|
||||
maybe_event_entity_id: Some(customer_id.clone()),
|
||||
maybe_stripe_customer_id: Some(customer_id.clone()),
|
||||
event_was_handled: false,
|
||||
action_was_taken: false,
|
||||
should_ignore_retry: false,
|
||||
})
|
||||
}
|
||||
+6
-2
@@ -27,7 +27,8 @@ pub async fn customer_subscription_created_handler(
|
||||
maybe_user_token: summary.user_token.clone(),
|
||||
maybe_event_entity_id: Some(summary.stripe_subscription_id.clone()),
|
||||
maybe_stripe_customer_id: Some(summary.stripe_customer_id.clone()),
|
||||
event_was_handled: false,
|
||||
action_was_taken: false,
|
||||
should_ignore_retry: false,
|
||||
};
|
||||
|
||||
// NB: It's possible to receive events out of order.
|
||||
@@ -40,6 +41,7 @@ pub async fn customer_subscription_created_handler(
|
||||
})?;
|
||||
|
||||
if maybe_existing_subscription.is_some() {
|
||||
result.should_ignore_retry = true;
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
@@ -70,6 +72,8 @@ pub async fn customer_subscription_created_handler(
|
||||
StripeWebhookError::ServerError
|
||||
})?;
|
||||
|
||||
result.event_was_handled = true;
|
||||
result.action_was_taken = true;
|
||||
result.should_ignore_retry = true;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
+2
-1
@@ -21,6 +21,7 @@ pub async fn customer_subscription_deleted_handler(
|
||||
maybe_user_token: summary.user_token,
|
||||
maybe_event_entity_id: Some(summary.stripe_subscription_id),
|
||||
maybe_stripe_customer_id: Some(summary.stripe_customer_id),
|
||||
event_was_handled: false,
|
||||
action_was_taken: false,
|
||||
should_ignore_retry: false,
|
||||
})
|
||||
}
|
||||
+11
-4
@@ -21,8 +21,10 @@ pub async fn customer_subscription_updated_handler(
|
||||
StripeWebhookError::ServerError // NB: This was probably *our* fault.
|
||||
})?;
|
||||
|
||||
let mut should_process_update = true;
|
||||
|
||||
let mut skip_update = false;
|
||||
let mut action_was_taken = false;
|
||||
let mut should_ignore_retry = false;
|
||||
|
||||
// NB: It's possible to receive events out of order.
|
||||
let maybe_existing_subscription = get_subscription_by_stripe_id(&summary.stripe_subscription_id, &mysql_pool)
|
||||
@@ -36,7 +38,8 @@ pub async fn customer_subscription_updated_handler(
|
||||
match existing_subscription.maybe_stripe_subscription_status {
|
||||
Some(StripeSubscriptionStatus::Canceled) => {
|
||||
// NB: This is a terminal status and the subscription cannot be updated any further.
|
||||
skip_update = true;
|
||||
should_process_update = false;
|
||||
should_ignore_retry = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -44,7 +47,7 @@ pub async fn customer_subscription_updated_handler(
|
||||
|
||||
// TODO: record cancel_at (future_cancel_at), canceled_at, ended_at (if subscription ended, when it ended), start_date
|
||||
|
||||
if !skip_update {
|
||||
if should_process_update {
|
||||
let upsert = UpsertSubscriptionByStripeId {
|
||||
stripe_subscription_id: &summary.stripe_subscription_id,
|
||||
maybe_user_token: summary.user_token.as_deref(),
|
||||
@@ -69,12 +72,16 @@ pub async fn customer_subscription_updated_handler(
|
||||
error!("Mysql error: {:?}", err);
|
||||
StripeWebhookError::ServerError
|
||||
})?;
|
||||
|
||||
action_was_taken = true;
|
||||
should_ignore_retry = true;
|
||||
}
|
||||
|
||||
Ok(StripeWebhookSummary {
|
||||
maybe_user_token: summary.user_token,
|
||||
maybe_event_entity_id: Some(summary.stripe_subscription_id),
|
||||
maybe_stripe_customer_id: Some(summary.stripe_customer_id),
|
||||
event_was_handled: true,
|
||||
action_was_taken,
|
||||
should_ignore_retry,
|
||||
})
|
||||
}
|
||||
+2
-1
@@ -78,7 +78,8 @@ pub fn invoice_paid_handler(invoice: &Invoice) -> Result<StripeWebhookSummary, S
|
||||
maybe_user_token: None,
|
||||
maybe_event_entity_id: None,
|
||||
maybe_stripe_customer_id: None,
|
||||
event_was_handled: false
|
||||
action_was_taken: false,
|
||||
should_ignore_retry: false,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -26,6 +26,7 @@ pub fn invoice_payment_failed_handler(invoice: &Invoice) -> Result<StripeWebhook
|
||||
maybe_user_token: maybe_user_token,
|
||||
maybe_event_entity_id: Some(invoice_id),
|
||||
maybe_stripe_customer_id: maybe_stripe_customer_id,
|
||||
event_was_handled: false,
|
||||
action_was_taken: false,
|
||||
should_ignore_retry: false,
|
||||
})
|
||||
}
|
||||
+2
-1
@@ -26,6 +26,7 @@ pub fn invoice_payment_succeeded_handler(invoice: &Invoice) -> Result<StripeWebh
|
||||
maybe_user_token: maybe_user_token,
|
||||
maybe_event_entity_id: Some(invoice_id),
|
||||
maybe_stripe_customer_id: maybe_stripe_customer_id,
|
||||
event_was_handled: false,
|
||||
action_was_taken: false,
|
||||
should_ignore_retry: false,
|
||||
})
|
||||
}
|
||||
+2
-1
@@ -31,6 +31,7 @@ pub fn invoice_updated_handler(invoice: &Invoice) -> Result<StripeWebhookSummary
|
||||
maybe_user_token: maybe_user_token,
|
||||
maybe_event_entity_id: Some(invoice_id),
|
||||
maybe_stripe_customer_id: maybe_stripe_customer_id,
|
||||
event_was_handled: false,
|
||||
action_was_taken: false,
|
||||
should_ignore_retry: false,
|
||||
})
|
||||
}
|
||||
+2
-1
@@ -26,6 +26,7 @@ pub fn payment_intent_succeeded_handler(payment_intent: &PaymentIntent) -> Resul
|
||||
maybe_user_token: maybe_user_token,
|
||||
maybe_event_entity_id: Some(payment_intent_id),
|
||||
maybe_stripe_customer_id: maybe_stripe_customer_id,
|
||||
event_was_handled: false,
|
||||
action_was_taken: false,
|
||||
should_ignore_retry: false,
|
||||
})
|
||||
}
|
||||
+7
-3
@@ -11,7 +11,11 @@ pub struct StripeWebhookSummary {
|
||||
/// This is the stripe customer ID, if it was associated with the event.
|
||||
pub maybe_stripe_customer_id: Option<String>,
|
||||
|
||||
/// Whether we took action in response to the webhook.
|
||||
/// Not all event types have handlers yet.
|
||||
pub event_was_handled: bool,
|
||||
/// Whether we took any sort of action in response to the webhook.
|
||||
/// Not all event types have handlers yet, and even so, sometimes we may choose to do nothing.
|
||||
pub action_was_taken: bool,
|
||||
|
||||
/// Whether any retries should be dropped.
|
||||
/// This helps us handle event with idempotency in the event Stripe replays them (and it can!)
|
||||
pub should_ignore_retry: bool,
|
||||
}
|
||||
+6
-3
@@ -13,7 +13,8 @@ pub struct InsertStripeWebhookEventLog {
|
||||
pub stripe_event_created_at: NaiveDateTime,
|
||||
pub stripe_is_production: bool,
|
||||
pub maybe_user_token: Option<String>,
|
||||
pub event_was_handled: bool,
|
||||
pub action_was_taken: bool,
|
||||
pub should_ignore_retry: bool,
|
||||
}
|
||||
|
||||
impl InsertStripeWebhookEventLog {
|
||||
@@ -31,7 +32,8 @@ SET
|
||||
stripe_event_created_at = ?,
|
||||
stripe_is_production = ?,
|
||||
maybe_user_token = ?,
|
||||
event_was_handled = ?
|
||||
action_was_taken = ?,
|
||||
should_ignore_retry = ?
|
||||
"#,
|
||||
&self.stripe_event_id,
|
||||
&self.stripe_event_type,
|
||||
@@ -40,7 +42,8 @@ SET
|
||||
&self.stripe_event_created_at,
|
||||
self.stripe_is_production,
|
||||
&self.maybe_user_token,
|
||||
self.event_was_handled,
|
||||
self.action_was_taken,
|
||||
self.should_ignore_retry,
|
||||
);
|
||||
|
||||
let query_result = query.execute(mysql_pool).await;
|
||||
|
||||
Reference in new issue
Block a user