collab: Save Customer name and billing address to Customer on checkout (#33385)

morgankrey created

We are collecting billing address and name on checkout now (for tax) but
we're not saving it back to the Customer level. Updating the Checkout
Session code to make`customer_update.address` equal to `auto`, instead
of the default `never`, as well as the same for `customer_update.name`.

Release Notes:

- N/A

Change summary

crates/collab/src/stripe_billing.rs                   | 16 ++
crates/collab/src/stripe_client.rs                    | 26 +++++
crates/collab/src/stripe_client/fake_stripe_client.rs |  9 +
crates/collab/src/stripe_client/real_stripe_client.rs | 57 ++++++++++++
crates/collab/src/tests/stripe_billing_tests.rs       | 29 ++++++
5 files changed, 125 insertions(+), 12 deletions(-)

Detailed changes

crates/collab/src/stripe_billing.rs 🔗

@@ -16,9 +16,9 @@ use crate::stripe_client::{
     StripeCreateCheckoutSessionLineItems, StripeCreateCheckoutSessionParams,
     StripeCreateCheckoutSessionSubscriptionData, StripeCreateMeterEventParams,
     StripeCreateMeterEventPayload, StripeCreateSubscriptionItems, StripeCreateSubscriptionParams,
-    StripeCustomerId, StripeMeter, StripePrice, StripePriceId, StripeSubscription,
-    StripeSubscriptionId, StripeSubscriptionTrialSettings,
-    StripeSubscriptionTrialSettingsEndBehavior,
+    StripeCustomerId, StripeCustomerUpdate, StripeCustomerUpdateAddress, StripeCustomerUpdateName,
+    StripeMeter, StripePrice, StripePriceId, StripeSubscription, StripeSubscriptionId,
+    StripeSubscriptionTrialSettings, StripeSubscriptionTrialSettingsEndBehavior,
     StripeSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod, UpdateSubscriptionItems,
     UpdateSubscriptionParams,
 };
@@ -247,6 +247,11 @@ impl StripeBilling {
         }]);
         params.success_url = Some(success_url);
         params.billing_address_collection = Some(StripeBillingAddressCollection::Required);
+        params.customer_update = Some(StripeCustomerUpdate {
+            address: Some(StripeCustomerUpdateAddress::Auto),
+            name: Some(StripeCustomerUpdateName::Auto),
+            shipping: None,
+        });
 
         let session = self.client.create_checkout_session(params).await?;
         Ok(session.url.context("no checkout session URL")?)
@@ -301,6 +306,11 @@ impl StripeBilling {
         }]);
         params.success_url = Some(success_url);
         params.billing_address_collection = Some(StripeBillingAddressCollection::Required);
+        params.customer_update = Some(StripeCustomerUpdate {
+            address: Some(StripeCustomerUpdateAddress::Auto),
+            name: Some(StripeCustomerUpdateName::Auto),
+            shipping: None,
+        });
 
         let session = self.client.create_checkout_session(params).await?;
         Ok(session.url.context("no checkout session URL")?)

crates/collab/src/stripe_client.rs 🔗

@@ -154,6 +154,31 @@ pub enum StripeBillingAddressCollection {
     Required,
 }
 
+#[derive(Debug, PartialEq, Clone)]
+pub struct StripeCustomerUpdate {
+    pub address: Option<StripeCustomerUpdateAddress>,
+    pub name: Option<StripeCustomerUpdateName>,
+    pub shipping: Option<StripeCustomerUpdateShipping>,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum StripeCustomerUpdateAddress {
+    Auto,
+    Never,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum StripeCustomerUpdateName {
+    Auto,
+    Never,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum StripeCustomerUpdateShipping {
+    Auto,
+    Never,
+}
+
 #[derive(Debug, Default)]
 pub struct StripeCreateCheckoutSessionParams<'a> {
     pub customer: Option<&'a StripeCustomerId>,
@@ -164,6 +189,7 @@ pub struct StripeCreateCheckoutSessionParams<'a> {
     pub subscription_data: Option<StripeCreateCheckoutSessionSubscriptionData>,
     pub success_url: Option<&'a str>,
     pub billing_address_collection: Option<StripeBillingAddressCollection>,
+    pub customer_update: Option<StripeCustomerUpdate>,
 }
 
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]

crates/collab/src/stripe_client/fake_stripe_client.rs 🔗

@@ -12,9 +12,10 @@ use crate::stripe_client::{
     StripeCheckoutSessionMode, StripeCheckoutSessionPaymentMethodCollection, StripeClient,
     StripeCreateCheckoutSessionLineItems, StripeCreateCheckoutSessionParams,
     StripeCreateCheckoutSessionSubscriptionData, StripeCreateMeterEventParams,
-    StripeCreateSubscriptionParams, StripeCustomer, StripeCustomerId, StripeMeter, StripeMeterId,
-    StripePrice, StripePriceId, StripeSubscription, StripeSubscriptionId, StripeSubscriptionItem,
-    StripeSubscriptionItemId, UpdateCustomerParams, UpdateSubscriptionParams,
+    StripeCreateSubscriptionParams, StripeCustomer, StripeCustomerId, StripeCustomerUpdate,
+    StripeMeter, StripeMeterId, StripePrice, StripePriceId, StripeSubscription,
+    StripeSubscriptionId, StripeSubscriptionItem, StripeSubscriptionItemId, UpdateCustomerParams,
+    UpdateSubscriptionParams,
 };
 
 #[derive(Debug, Clone)]
@@ -36,6 +37,7 @@ pub struct StripeCreateCheckoutSessionCall {
     pub subscription_data: Option<StripeCreateCheckoutSessionSubscriptionData>,
     pub success_url: Option<String>,
     pub billing_address_collection: Option<StripeBillingAddressCollection>,
+    pub customer_update: Option<StripeCustomerUpdate>,
 }
 
 pub struct FakeStripeClient {
@@ -233,6 +235,7 @@ impl StripeClient for FakeStripeClient {
                 subscription_data: params.subscription_data,
                 success_url: params.success_url.map(|url| url.to_string()),
                 billing_address_collection: params.billing_address_collection,
+                customer_update: params.customer_update,
             });
 
         Ok(StripeCheckoutSession {

crates/collab/src/stripe_client/real_stripe_client.rs 🔗

@@ -22,10 +22,11 @@ use crate::stripe_client::{
     StripeCheckoutSessionPaymentMethodCollection, StripeClient,
     StripeCreateCheckoutSessionLineItems, StripeCreateCheckoutSessionParams,
     StripeCreateCheckoutSessionSubscriptionData, StripeCreateMeterEventParams,
-    StripeCreateSubscriptionParams, StripeCustomer, StripeCustomerId, StripeMeter, StripePrice,
-    StripePriceId, StripePriceRecurring, StripeSubscription, StripeSubscriptionId,
-    StripeSubscriptionItem, StripeSubscriptionItemId, StripeSubscriptionTrialSettings,
-    StripeSubscriptionTrialSettingsEndBehavior,
+    StripeCreateSubscriptionParams, StripeCustomer, StripeCustomerId, StripeCustomerUpdate,
+    StripeCustomerUpdateAddress, StripeCustomerUpdateName, StripeCustomerUpdateShipping,
+    StripeMeter, StripePrice, StripePriceId, StripePriceRecurring, StripeSubscription,
+    StripeSubscriptionId, StripeSubscriptionItem, StripeSubscriptionItemId,
+    StripeSubscriptionTrialSettings, StripeSubscriptionTrialSettingsEndBehavior,
     StripeSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod, UpdateCustomerParams,
     UpdateSubscriptionParams,
 };
@@ -446,6 +447,7 @@ impl<'a> TryFrom<StripeCreateCheckoutSessionParams<'a>> for CreateCheckoutSessio
             subscription_data: value.subscription_data.map(Into::into),
             success_url: value.success_url,
             billing_address_collection: value.billing_address_collection.map(Into::into),
+            customer_update: value.customer_update.map(Into::into),
             ..Default::default()
         })
     }
@@ -541,3 +543,50 @@ impl From<StripeBillingAddressCollection> for stripe::CheckoutSessionBillingAddr
         }
     }
 }
+
+impl From<StripeCustomerUpdateAddress> for stripe::CreateCheckoutSessionCustomerUpdateAddress {
+    fn from(value: StripeCustomerUpdateAddress) -> Self {
+        match value {
+            StripeCustomerUpdateAddress::Auto => {
+                stripe::CreateCheckoutSessionCustomerUpdateAddress::Auto
+            }
+            StripeCustomerUpdateAddress::Never => {
+                stripe::CreateCheckoutSessionCustomerUpdateAddress::Never
+            }
+        }
+    }
+}
+
+impl From<StripeCustomerUpdateName> for stripe::CreateCheckoutSessionCustomerUpdateName {
+    fn from(value: StripeCustomerUpdateName) -> Self {
+        match value {
+            StripeCustomerUpdateName::Auto => stripe::CreateCheckoutSessionCustomerUpdateName::Auto,
+            StripeCustomerUpdateName::Never => {
+                stripe::CreateCheckoutSessionCustomerUpdateName::Never
+            }
+        }
+    }
+}
+
+impl From<StripeCustomerUpdateShipping> for stripe::CreateCheckoutSessionCustomerUpdateShipping {
+    fn from(value: StripeCustomerUpdateShipping) -> Self {
+        match value {
+            StripeCustomerUpdateShipping::Auto => {
+                stripe::CreateCheckoutSessionCustomerUpdateShipping::Auto
+            }
+            StripeCustomerUpdateShipping::Never => {
+                stripe::CreateCheckoutSessionCustomerUpdateShipping::Never
+            }
+        }
+    }
+}
+
+impl From<StripeCustomerUpdate> for stripe::CreateCheckoutSessionCustomerUpdate {
+    fn from(value: StripeCustomerUpdate) -> Self {
+        stripe::CreateCheckoutSessionCustomerUpdate {
+            address: value.address.map(Into::into),
+            name: value.name.map(Into::into),
+            shipping: value.shipping.map(Into::into),
+        }
+    }
+}

crates/collab/src/tests/stripe_billing_tests.rs 🔗

@@ -8,8 +8,9 @@ use crate::stripe_billing::StripeBilling;
 use crate::stripe_client::{
     FakeStripeClient, StripeBillingAddressCollection, StripeCheckoutSessionMode,
     StripeCheckoutSessionPaymentMethodCollection, StripeCreateCheckoutSessionLineItems,
-    StripeCreateCheckoutSessionSubscriptionData, StripeCustomerId, StripeMeter, StripeMeterId,
-    StripePrice, StripePriceId, StripePriceRecurring, StripeSubscription, StripeSubscriptionId,
+    StripeCreateCheckoutSessionSubscriptionData, StripeCustomerId, StripeCustomerUpdate,
+    StripeCustomerUpdateAddress, StripeCustomerUpdateName, StripeMeter, StripeMeterId, StripePrice,
+    StripePriceId, StripePriceRecurring, StripeSubscription, StripeSubscriptionId,
     StripeSubscriptionItem, StripeSubscriptionItemId, StripeSubscriptionTrialSettings,
     StripeSubscriptionTrialSettingsEndBehavior,
     StripeSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod, UpdateSubscriptionItems,
@@ -431,6 +432,14 @@ async fn test_checkout_with_zed_pro() {
             call.billing_address_collection,
             Some(StripeBillingAddressCollection::Required)
         );
+        assert_eq!(
+            call.customer_update,
+            Some(StripeCustomerUpdate {
+                address: Some(StripeCustomerUpdateAddress::Auto),
+                name: Some(StripeCustomerUpdateName::Auto),
+                shipping: None,
+            })
+        );
     }
 }
 
@@ -516,6 +525,14 @@ async fn test_checkout_with_zed_pro_trial() {
             call.billing_address_collection,
             Some(StripeBillingAddressCollection::Required)
         );
+        assert_eq!(
+            call.customer_update,
+            Some(StripeCustomerUpdate {
+                address: Some(StripeCustomerUpdateAddress::Auto),
+                name: Some(StripeCustomerUpdateName::Auto),
+                shipping: None,
+            })
+        );
     }
 
     // Successful checkout with extended trial.
@@ -574,5 +591,13 @@ async fn test_checkout_with_zed_pro_trial() {
             call.billing_address_collection,
             Some(StripeBillingAddressCollection::Required)
         );
+        assert_eq!(
+            call.customer_update,
+            Some(StripeCustomerUpdate {
+                address: Some(StripeCustomerUpdateAddress::Auto),
+                name: Some(StripeCustomerUpdateName::Auto),
+                shipping: None,
+            })
+        );
     }
 }