@@ -15,6 +15,13 @@ import {
15
15
stripe ,
16
16
} from "~/utils/stripe.server" ;
17
17
18
+ const subscriptionTiersPriority : Record < TierId , number > = {
19
+ free : 0 ,
20
+ tier_1 : 1 , // plus
21
+ tier_2 : 2 , // team
22
+ custom : 3 , // Custom
23
+ } ;
24
+
18
25
export async function action ( { request } : ActionFunctionArgs ) {
19
26
try {
20
27
const payload = await request . text ( ) ;
@@ -29,6 +36,23 @@ export async function action({ request }: ActionFunctionArgs) {
29
36
const customInstallUsers = ( CUSTOM_INSTALL_CUSTOMERS ?? "" ) . split ( "," ) ;
30
37
const eventData = event . data . object as { customer : string } ;
31
38
const customerId = eventData . customer ;
39
+ const user = await db . user
40
+ . findFirstOrThrow ( {
41
+ where : { customerId } ,
42
+ include : {
43
+ tier : true ,
44
+ customTierLimit : true ,
45
+ } ,
46
+ } )
47
+ . catch ( ( cause ) => {
48
+ throw new ShelfError ( {
49
+ cause,
50
+ message : "No user found" ,
51
+ additionalData : { customerId } ,
52
+ label : "Stripe webhook" ,
53
+ status : 500 ,
54
+ } ) ;
55
+ } ) ;
32
56
33
57
/** We don't have to do anything in case if the user is custom install. */
34
58
if ( customInstallUsers . includes ( customerId ) ) {
@@ -160,11 +184,18 @@ export async function action({ request }: ActionFunctionArgs) {
160
184
status : 500 ,
161
185
} ) ;
162
186
}
187
+ /** Check whether the paused subscription is higher tier and the current one and only then cancel */
188
+ const pausedSubscriptionIsHigherTier =
189
+ subscriptionTiersPriority [ tierId as TierId ] >
190
+ subscriptionTiersPriority [ user . tierId ] ;
163
191
164
192
/** When its a trial subscription, update the tier of the user
165
193
* In that case we just set it back to free
166
194
*/
167
- if ( subscription . status === "paused" ) {
195
+ if (
196
+ subscription . status === "paused" &&
197
+ pausedSubscriptionIsHigherTier
198
+ ) {
168
199
await db . user
169
200
. update ( {
170
201
where : { customerId } ,
@@ -204,8 +235,15 @@ export async function action({ request }: ActionFunctionArgs) {
204
235
*
205
236
* We only update the tier if the subscription is not paused
206
237
* We only do it if the subscription is active because this event gets triggered when cancelling or pausing for example
238
+ * We only do it if the subscription is higher tier than the current subscription they have. The tier order is free -> plus -> team
207
239
*/
208
- if ( subscription . status === "active" ) {
240
+
241
+ /** Check whether the new subscription is higher tier and the current one and only then cancel */
242
+ const newSubscriptionIsHigherTier =
243
+ subscriptionTiersPriority [ tierId as TierId ] >
244
+ subscriptionTiersPriority [ user . tierId ] ;
245
+
246
+ if ( subscription . status === "active" && newSubscriptionIsHigherTier ) {
209
247
await db . user
210
248
. update ( {
211
249
where : { customerId } ,
@@ -229,33 +267,38 @@ export async function action({ request }: ActionFunctionArgs) {
229
267
230
268
case "customer.subscription.deleted" : {
231
269
// Occurs whenever a customer’s subscription ends.
232
- const subscription = event . data . object as Stripe . Subscription ;
233
- const customerId = subscription . customer as string ;
270
+ const { customerId, tierId } = await getDataFromStripeEvent ( event ) ;
234
271
235
- await db . user
236
- . update ( {
237
- where : { customerId } ,
238
- data : {
239
- tierId : TierId . free ,
240
- } ,
241
- } )
242
- . catch ( ( cause ) => {
243
- throw new ShelfError ( {
244
- cause,
245
- message : "Failed to delete user subscription" ,
246
- additionalData : { customerId, event } ,
247
- label : "Stripe webhook" ,
248
- status : 500 ,
272
+ /** Check whether the deleted subscription is higher tier and the current one and only then cancel */
273
+ const deletedSubscriptionIsHigherTier =
274
+ subscriptionTiersPriority [ tierId as TierId ] >
275
+ subscriptionTiersPriority [ user . tierId ] ;
276
+
277
+ if ( deletedSubscriptionIsHigherTier ) {
278
+ await db . user
279
+ . update ( {
280
+ where : { customerId } ,
281
+ data : {
282
+ tierId : TierId . free ,
283
+ } ,
284
+ } )
285
+ . catch ( ( cause ) => {
286
+ throw new ShelfError ( {
287
+ cause,
288
+ message : "Failed to delete user subscription" ,
289
+ additionalData : { customerId, event } ,
290
+ label : "Stripe webhook" ,
291
+ status : 500 ,
292
+ } ) ;
249
293
} ) ;
250
- } ) ;
294
+ }
251
295
252
296
return new Response ( null , { status : 200 } ) ;
253
297
}
254
298
255
299
case "customer.subscription.trial_will_end" : {
256
300
// Occurs three days before the trial period of a subscription is scheduled to end.
257
- const { customerId, tierId, subscription } =
258
- await getDataFromStripeEvent ( event ) ;
301
+ const { tierId, subscription } = await getDataFromStripeEvent ( event ) ;
259
302
260
303
if ( ! tierId ) {
261
304
throw new ShelfError ( {
@@ -271,20 +314,6 @@ export async function action({ request }: ActionFunctionArgs) {
271
314
subscription . trial_end && subscription . trial_start ;
272
315
273
316
if ( isTrialSubscription ) {
274
- const user = await db . user
275
- . findUniqueOrThrow ( {
276
- where : { customerId } ,
277
- } )
278
- . catch ( ( cause ) => {
279
- throw new ShelfError ( {
280
- cause,
281
- message : "No user found" ,
282
- additionalData : { customerId } ,
283
- label : "Stripe webhook" ,
284
- status : 500 ,
285
- } ) ;
286
- } ) ;
287
-
288
317
sendEmail ( {
289
318
to : user . email ,
290
319
subject : "Your shelf.nu free trial is ending soon" ,
0 commit comments