1616using OneOf . Types ;
1717using Stripe ;
1818using Customer = Stripe . Customer ;
19+ using PaymentMethod = Bit . Core . Billing . Payment . Models . PaymentMethod ;
1920using Subscription = Stripe . Subscription ;
2021
2122namespace Bit . Core . Billing . Premium . Commands ;
@@ -38,7 +39,7 @@ public interface ICreatePremiumCloudHostedSubscriptionCommand
3839 /// <returns>A billing command result indicating success or failure with appropriate error details.</returns>
3940 Task < BillingCommandResult < None > > Run (
4041 User user ,
41- TokenizedPaymentMethod paymentMethod ,
42+ PaymentMethod paymentMethod ,
4243 BillingAddress billingAddress ,
4344 short additionalStorageGb ) ;
4445}
@@ -60,7 +61,7 @@ public class CreatePremiumCloudHostedSubscriptionCommand(
6061
6162 public Task < BillingCommandResult < None > > Run (
6263 User user ,
63- TokenizedPaymentMethod paymentMethod ,
64+ PaymentMethod paymentMethod ,
6465 BillingAddress billingAddress ,
6566 short additionalStorageGb ) => HandleAsync < None > ( async ( ) =>
6667 {
@@ -74,6 +75,7 @@ public Task<BillingCommandResult<None>> Run(
7475 return new BadRequest ( "Additional storage must be greater than 0." ) ;
7576 }
7677
78+ // Note: A customer will already exist if the customer has purchased account credits.
7779 var customer = string . IsNullOrEmpty ( user . GatewayCustomerId )
7880 ? await CreateCustomerAsync ( user , paymentMethod , billingAddress )
7981 : await subscriberService . GetCustomerOrThrow ( user , new CustomerGetOptions { Expand = _expand } ) ;
@@ -82,18 +84,31 @@ public Task<BillingCommandResult<None>> Run(
8284
8385 var subscription = await CreateSubscriptionAsync ( user . Id , customer , additionalStorageGb > 0 ? additionalStorageGb : null ) ;
8486
85- switch ( paymentMethod )
86- {
87- case { Type : TokenizablePaymentMethodType . PayPal }
88- when subscription . Status == StripeConstants . SubscriptionStatus . Incomplete :
89- case { Type : not TokenizablePaymentMethodType . PayPal }
90- when subscription . Status == StripeConstants . SubscriptionStatus . Active :
87+ paymentMethod . Switch (
88+ tokenized =>
89+ {
90+ // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
91+ switch ( tokenized )
92+ {
93+ case { Type : TokenizablePaymentMethodType . PayPal }
94+ when subscription . Status == StripeConstants . SubscriptionStatus . Incomplete :
95+ case { Type : not TokenizablePaymentMethodType . PayPal }
96+ when subscription . Status == StripeConstants . SubscriptionStatus . Active :
97+ {
98+ user . Premium = true ;
99+ user . PremiumExpirationDate = subscription . GetCurrentPeriodEnd ( ) ;
100+ break ;
101+ }
102+ }
103+ } ,
104+ nonTokenized =>
105+ {
106+ if ( subscription . Status == StripeConstants . SubscriptionStatus . Active )
91107 {
92108 user . Premium = true ;
93109 user . PremiumExpirationDate = subscription . GetCurrentPeriodEnd ( ) ;
94- break ;
95110 }
96- }
111+ } ) ;
97112
98113 user . Gateway = GatewayType . Stripe ;
99114 user . GatewayCustomerId = customer . Id ;
@@ -109,9 +124,15 @@ public Task<BillingCommandResult<None>> Run(
109124 } ) ;
110125
111126 private async Task < Customer > CreateCustomerAsync ( User user ,
112- TokenizedPaymentMethod paymentMethod ,
127+ PaymentMethod paymentMethod ,
113128 BillingAddress billingAddress )
114129 {
130+ if ( paymentMethod . IsNonTokenized )
131+ {
132+ _logger . LogError ( "Cannot create customer for user ({UserID}) using non-tokenized payment method. The customer should already exist" , user . Id ) ;
133+ throw new BillingException ( ) ;
134+ }
135+
115136 var subscriberName = user . SubscriberName ( ) ;
116137 var customerCreateOptions = new CustomerCreateOptions
117138 {
@@ -153,13 +174,14 @@ private async Task<Customer> CreateCustomerAsync(User user,
153174
154175 var braintreeCustomerId = "" ;
155176
177+ // We have checked that the payment method is tokenized, so we can safely cast it.
156178 // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
157- switch ( paymentMethod . Type )
179+ switch ( paymentMethod . AsT0 . Type )
158180 {
159181 case TokenizablePaymentMethodType . BankAccount :
160182 {
161183 var setupIntent =
162- ( await stripeAdapter . SetupIntentList ( new SetupIntentListOptions { PaymentMethod = paymentMethod . Token } ) )
184+ ( await stripeAdapter . SetupIntentList ( new SetupIntentListOptions { PaymentMethod = paymentMethod . AsT0 . Token } ) )
163185 . FirstOrDefault ( ) ;
164186
165187 if ( setupIntent == null )
@@ -173,19 +195,19 @@ private async Task<Customer> CreateCustomerAsync(User user,
173195 }
174196 case TokenizablePaymentMethodType . Card :
175197 {
176- customerCreateOptions . PaymentMethod = paymentMethod . Token ;
177- customerCreateOptions . InvoiceSettings . DefaultPaymentMethod = paymentMethod . Token ;
198+ customerCreateOptions . PaymentMethod = paymentMethod . AsT0 . Token ;
199+ customerCreateOptions . InvoiceSettings . DefaultPaymentMethod = paymentMethod . AsT0 . Token ;
178200 break ;
179201 }
180202 case TokenizablePaymentMethodType . PayPal :
181203 {
182- braintreeCustomerId = await subscriberService . CreateBraintreeCustomer ( user , paymentMethod . Token ) ;
204+ braintreeCustomerId = await subscriberService . CreateBraintreeCustomer ( user , paymentMethod . AsT0 . Token ) ;
183205 customerCreateOptions . Metadata [ BraintreeCustomerIdKey ] = braintreeCustomerId ;
184206 break ;
185207 }
186208 default :
187209 {
188- _logger . LogError ( "Cannot create customer for user ({UserID}) using payment method type ({PaymentMethodType}) as it is not supported" , user . Id , paymentMethod . Type . ToString ( ) ) ;
210+ _logger . LogError ( "Cannot create customer for user ({UserID}) using payment method type ({PaymentMethodType}) as it is not supported" , user . Id , paymentMethod . AsT0 . Type . ToString ( ) ) ;
189211 throw new BillingException ( ) ;
190212 }
191213 }
@@ -203,18 +225,21 @@ private async Task<Customer> CreateCustomerAsync(User user,
203225 async Task Revert ( )
204226 {
205227 // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
206- switch ( paymentMethod . Type )
228+ if ( paymentMethod . IsTokenized )
207229 {
208- case TokenizablePaymentMethodType . BankAccount :
209- {
210- await setupIntentCache . RemoveSetupIntentForSubscriber ( user . Id ) ;
211- break ;
212- }
213- case TokenizablePaymentMethodType . PayPal when ! string . IsNullOrEmpty ( braintreeCustomerId ) :
214- {
215- await braintreeGateway . Customer . DeleteAsync ( braintreeCustomerId ) ;
216- break ;
217- }
230+ switch ( paymentMethod . AsT0 . Type )
231+ {
232+ case TokenizablePaymentMethodType . BankAccount :
233+ {
234+ await setupIntentCache . RemoveSetupIntentForSubscriber ( user . Id ) ;
235+ break ;
236+ }
237+ case TokenizablePaymentMethodType . PayPal when ! string . IsNullOrEmpty ( braintreeCustomerId ) :
238+ {
239+ await braintreeGateway . Customer . DeleteAsync ( braintreeCustomerId ) ;
240+ break ;
241+ }
242+ }
218243 }
219244 }
220245 }
0 commit comments