Skip to content

Commit ce61254

Browse files
committed
Better handle other stripe status like "incomplete"
1 parent 443d01e commit ce61254

15 files changed

Lines changed: 178 additions & 33 deletions

File tree

landing/src/components/sections/subscribe/SubscribeForm.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { useEffect, useRef, useState } from 'react'
22
import { CardElement, injectStripe } from 'react-stripe-elements'
33

4-
import { constants, Plan } from '@brunolemos/devhub-core'
4+
import { constants, Plan, UserPlan } from '@brunolemos/devhub-core'
55
import { useAuth } from '../../../context/AuthContext'
66
import { useTheme } from '../../../context/ThemeContext'
77
import {
@@ -147,7 +147,10 @@ export const SubscribeForm = injectStripe<SubscribeFormProps>(
147147
return false
148148
}
149149

150-
const { data, errors } = await response.json()
150+
const { data, errors } = (await response.json()) as {
151+
data: { subscribeToPlan: UserPlan | null } | null
152+
errors: any[] | null
153+
}
151154

152155
if (!(data && data.subscribeToPlan) || (errors && errors[0])) {
153156
throw new Error(
@@ -163,12 +166,18 @@ export const SubscribeForm = injectStripe<SubscribeFormProps>(
163166

164167
mergeAuthData({ plan: data.subscribeToPlan })
165168

169+
if (data.subscribeToPlan.status === 'incomplete') {
170+
throw new Error('Please try a different credit card.')
171+
}
172+
166173
if (onSuccess) onSuccess()
167174
return true
168175
} catch (error) {
169176
console.error(error)
170177
setFormState({
171-
error: `Failed to execute payment. ${error.message}`,
178+
error:
179+
`Failed to execute payment. ${error.message}` +
180+
"\n\nAlso, please note we currently don't support Amex, Elo or Debit cards.",
172181
isSubmiting: false,
173182
})
174183
return false

landing/src/pages/AccountPage.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ export default function AccountPage(_props: AccountPageProps) {
8282
? ` (cancellation: ${new Date(
8383
authData.plan.cancelAt,
8484
).toDateString()})`
85+
: authData.plan.status === 'incomplete' ||
86+
authData.plan.status === 'incomplete_expired'
87+
? ' (failed to charge your card. please try with a different one)'
8588
: ''
8689
}`}
8790
</h2>

landing/src/pages/SubscribedPage.tsx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,32 @@ export default function SubscribedPage(_props: SubscribedPageProps) {
2424
<section id="subscribed" className="container">
2525
<div className="flex flex-col items-center m-auto text-center">
2626
<h1 className="mb-4 text-2xl sm:text-4xl whitespace-no-wrap">
27-
You are all set 🎉
27+
{authData.plan.status === 'incomplete' ||
28+
authData.plan.status === 'incomplete_expired'
29+
? 'Something went wrong'
30+
: 'You are all set 🎉'}
2831
</h1>
2932

30-
<h2 className="mb-4 text-xl sm:text-2xl">
31-
You've successfully subscribed to the{' '}
32-
<strong>{planInfo.label}</strong> plan
33-
</h2>
33+
{authData.plan.status === 'active' ||
34+
authData.plan.status === 'trialing' ? (
35+
<h2 className="mb-4 text-xl sm:text-2xl">
36+
You've successfully subscribed to the{' '}
37+
<strong>{planInfo.label}</strong> plan
38+
</h2>
39+
) : (
40+
<h2 className="mb-4 text-xl sm:text-2xl">
41+
You've subscribed to the <strong>{planInfo.label}</strong> plan,
42+
but your subscription status is{' '}
43+
<strong>{authData.plan.status}</strong>
44+
</h2>
45+
)}
46+
47+
{authData.plan.status === 'incomplete' ||
48+
authData.plan.status === 'incomplete_expired' ? (
49+
<p className="mb-8 text-default">
50+
Credit card charge failed. Please try again with another card.
51+
</p>
52+
) : null}
3453

3554
<p className="mb-8 text-default">
3655
You can now open DevHub or download it below. <br />

packages/components/src/components/columns/ColumnRenderer.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
isItemRead,
1717
isItemSaved,
1818
isNotificationPrivate,
19+
isPlanStatusValid,
1920
ThemeColors,
2021
} from '@devhub/core'
2122
import React, { useCallback, useRef } from 'react'
@@ -173,7 +174,7 @@ export const ColumnRenderer = React.memo((props: ColumnRendererProps) => {
173174
: undefined
174175
: (!(
175176
plan &&
176-
(plan.status === 'active' || plan.status === 'trialing') &&
177+
isPlanStatusValid(plan) &&
177178
plan.featureFlags.enablePrivateRepositories
178179
) &&
179180
(columnType === 'activity'

packages/components/src/components/modals/PricingModal.tsx

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ export function PricingModal(props: PricingModalProps) {
7272
plansToShow.find(p => p.id === userPlan.id)
7373
)
7474

75+
const showUserPlanAtTheTop =
76+
userPlan &&
77+
((userPlanDetails &&
78+
(userPlanDetails.amount ||
79+
(userPlanDetails.trialPeriodDays && !isPlanExpired(userPlan))) &&
80+
!userPlanStillExist) ||
81+
userPlan.cancelAt)
82+
7583
const [selectedPlanId, setSelectedPlanId] = useState<PlanID | undefined>(
7684
() =>
7785
(initialSelectedPlanId &&
@@ -152,6 +160,7 @@ export function PricingModal(props: PricingModalProps) {
152160
scrollToPlan(plan.id)
153161
}}
154162
plan={plan}
163+
showCurrentPlanDetails={!showUserPlanAtTheTop}
155164
showFeatures
156165
/>
157166
) : (
@@ -166,10 +175,16 @@ export function PricingModal(props: PricingModalProps) {
166175
scrollToPlan(plan.id)
167176
}}
168177
plan={plan}
178+
showCurrentPlanDetails={!showUserPlanAtTheTop}
169179
showFeatures
170180
/>
171181
),
172-
[selectedPlanId, highlightFeature, userPlan && userPlan.amount],
182+
[
183+
selectedPlanId,
184+
highlightFeature,
185+
userPlan && userPlan.amount,
186+
showUserPlanAtTheTop,
187+
],
173188
)
174189

175190
const CancelOrReactivateSubscriptionButton =
@@ -262,14 +277,6 @@ export function PricingModal(props: PricingModalProps) {
262277
</Button>
263278
)
264279

265-
const showUserPlanAtTheTop =
266-
userPlan &&
267-
((userPlanDetails &&
268-
(userPlanDetails.amount ||
269-
(userPlanDetails.trialPeriodDays && !isPlanExpired(userPlan))) &&
270-
!userPlanStillExist) ||
271-
userPlan.cancelAt)
272-
273280
return (
274281
<ModalColumn
275282
name="PRICING"
@@ -287,6 +294,7 @@ export function PricingModal(props: PricingModalProps) {
287294
banner={false}
288295
isPartOfAList={false}
289296
plan={userPlanDetails!}
297+
showCurrentPlanDetails
290298
width="100%"
291299
/>
292300
</View>

packages/components/src/components/modals/SettingsModal.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { allPlans, constants, freePlan } from '@devhub/core'
1+
import { allPlans, constants, freePlan, isPlanStatusValid } from '@devhub/core'
22
import React from 'react'
33
import { View } from 'react-native'
44

@@ -87,7 +87,11 @@ export const SettingsModal = React.memo((props: SettingsModalProps) => {
8787
(userPlan &&
8888
userPlan.status === 'active' &&
8989
userPlan.cancelAtPeriodEnd &&
90-
userPlan.cancelAt)
90+
userPlan.cancelAt) ||
91+
(userPlan &&
92+
userPlan.status &&
93+
(!isPlanStatusValid(userPlan) ||
94+
userPlan.status === 'incomplete'))
9195
) && (
9296
<>
9397
<Spacer width={contentPadding / 2} />

packages/components/src/components/modals/SubscribedModal.tsx

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ import React, { useEffect } from 'react'
33
import { View } from 'react-native'
44
import { useDispatch } from 'react-redux'
55

6+
import { useReduxState } from '../../hooks/use-redux-state'
67
import { Browser } from '../../libs/browser'
78
import { Platform } from '../../libs/platform'
89
import * as actions from '../../redux/actions'
10+
import * as selectors from '../../redux/selectors'
911
import { sharedStyles } from '../../styles/shared'
1012
import { contentPadding, normalTextSize } from '../../styles/variables'
1113
import { ModalColumn } from '../columns/ModalColumn'
@@ -22,10 +24,12 @@ export interface SubscribedModalProps {
2224
}
2325

2426
export function SubscribedModal(props: SubscribedModalProps) {
25-
const { planId, showBackButton } = props
27+
const { planId: _planId, showBackButton } = props
2628

2729
const dispatch = useDispatch()
30+
const userPlan = useReduxState(selectors.currentUserPlanSelector)
2831

32+
const planId = _planId || (userPlan && userPlan.id)
2933
const plan = planId && allPlans.find(p => p.id === planId)
3034

3135
useEffect(() => {
@@ -98,6 +102,18 @@ export function SubscribedModal(props: SubscribedModalProps) {
98102
</>
99103
)}
100104
plan
105+
{userPlan &&
106+
userPlan.status &&
107+
!(
108+
userPlan.status === 'active' || userPlan.status === 'trialing'
109+
) ? (
110+
<>
111+
{', '}but your subscription status is{' '}
112+
<ThemedText style={{ fontWeight: 'bold' }}>
113+
{userPlan.status}
114+
</ThemedText>
115+
</>
116+
) : null}
101117
</>
102118
) : freePlan && !freePlan.trialPeriodDays && plan ? (
103119
<>
@@ -106,6 +122,18 @@ export function SubscribedModal(props: SubscribedModalProps) {
106122
{`${plan.label || 'Free'}`.toLowerCase()}
107123
</ThemedText>{' '}
108124
plan
125+
{userPlan &&
126+
userPlan.status &&
127+
!(
128+
userPlan.status === 'active' || userPlan.status === 'trialing'
129+
) ? (
130+
<>
131+
{', '}but your subscription status is{' '}
132+
<ThemedText style={{ fontWeight: 'bold' }}>
133+
{userPlan.status}
134+
</ThemedText>
135+
</>
136+
) : null}
109137
</>
110138
) : (
111139
<>
@@ -114,7 +142,28 @@ export function SubscribedModal(props: SubscribedModalProps) {
114142
</>
115143
)}
116144
</ThemedText>
145+
117146
<Spacer height={contentPadding} />
147+
148+
{userPlan &&
149+
(userPlan.status === 'incomplete' ||
150+
userPlan.status === 'incomplete_expired') ? (
151+
<>
152+
<ThemedText
153+
color="foregroundColor"
154+
style={{
155+
lineHeight: normalTextSize + 4,
156+
fontSize: normalTextSize,
157+
textAlign: 'center',
158+
}}
159+
>
160+
Credit card charge failed. Please try again with another card.
161+
</ThemedText>
162+
163+
<Spacer height={contentPadding} />
164+
</>
165+
) : null}
166+
118167
<ThemedText
119168
color="foregroundColor"
120169
style={{

packages/components/src/components/modals/partials/PricingPlanBlock.tsx

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export interface PricingPlanBlockProps {
3232
isSelected?: boolean | undefined
3333
onSelect?: (() => void) | undefined
3434
plan: Plan
35+
showCurrentPlanDetails: boolean
3536
showFeatures?: boolean
3637
width?: string | number
3738
}
@@ -44,6 +45,7 @@ export function PricingPlanBlock(props: PricingPlanBlockProps) {
4445
isSelected,
4546
onSelect,
4647
plan,
48+
showCurrentPlanDetails,
4749
showFeatures,
4850
width = defaultPricingBlockWidth,
4951
} = props
@@ -90,7 +92,7 @@ export function PricingPlanBlock(props: PricingPlanBlockProps) {
9092

9193
if (
9294
estimatedMonthlyPrice !== plan.amount &&
93-
!(isMyPlan && userPlan && userPlan.cancelAt && !isPartOfAList)
95+
!(isMyPlan && userPlan && userPlan.cancelAt && showCurrentPlanDetails)
9496
) {
9597
footerText =
9698
footerText +
@@ -102,7 +104,7 @@ export function PricingPlanBlock(props: PricingPlanBlockProps) {
102104
)}`
103105
}
104106

105-
if (isMyPlan && userPlan && userPlan.cancelAt && !isPartOfAList) {
107+
if (isMyPlan && userPlan && userPlan.cancelAt && showCurrentPlanDetails) {
106108
footerText =
107109
footerText +
108110
`${footerText ? '\n\n' : ''}${getDateSmallText(userPlan.cancelAt, {
@@ -114,7 +116,27 @@ export function PricingPlanBlock(props: PricingPlanBlockProps) {
114116
futureSuffix: '',
115117
showSuffixOnFullDate: true,
116118
})}`
117-
} else if (isMyPlan && userPlan && userPlan.trialEndAt && !isPartOfAList) {
119+
} else if (
120+
isMyPlan &&
121+
userPlan &&
122+
userPlan.status &&
123+
(userPlan.status !== 'active' && userPlan.status !== 'trialing') &&
124+
showCurrentPlanDetails
125+
) {
126+
footerText =
127+
footerText +
128+
`${footerText ? '\n\n' : ''}Status: ${userPlan.status}${
129+
userPlan.status === 'incomplete' ||
130+
userPlan.status === 'incomplete_expired'
131+
? ' (failed to charge your card. please try again with a different one)'
132+
: ''
133+
}`
134+
} else if (
135+
isMyPlan &&
136+
userPlan &&
137+
userPlan.trialEndAt &&
138+
showCurrentPlanDetails
139+
) {
118140
footerText =
119141
footerText +
120142
`${footerText ? '\n\n' : ''}${

packages/components/src/components/modals/partials/SubscribeForm.web.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
constants,
44
formatPriceAndInterval,
55
freePlan,
6+
UserPlan,
67
} from '@devhub/core'
78
import axios from 'axios'
89
import React, { useEffect, useRef, useState } from 'react'
@@ -173,7 +174,10 @@ const SubscribeFormWithStripe = React.memo(
173174
}
174175

175176
try {
176-
const response = await axios.post(
177+
const response = await axios.post<{
178+
data: { subscribeToPlan: UserPlan | null } | null
179+
errors: any[] | null
180+
}>(
177181
constants.GRAPHQL_ENDPOINT,
178182
{
179183
query: `
@@ -253,12 +257,18 @@ const SubscribeFormWithStripe = React.memo(
253257

254258
dispatch(actions.updateUserData({ plan: data.subscribeToPlan }))
255259

260+
if (data.subscribeToPlan.status === 'incomplete') {
261+
throw new Error('Please try a different credit card.')
262+
}
263+
256264
if (onSubscribe) onSubscribe(plan.id)
257265
return true
258266
} catch (error) {
259267
console.error(error)
260268
setFormState({
261-
error: `Failed to execute payment. ${error.message}`,
269+
error:
270+
`Failed to execute payment. ${error.message}` +
271+
"\n\nAlso, please note we currently don't support Amex, Elo or Debit cards.",
262272
isSubmiting: false,
263273
})
264274
return false

0 commit comments

Comments
 (0)