-
Notifications
You must be signed in to change notification settings - Fork 331
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(checkout-flow): handle credit card functionality #8005
Changes from 13 commits
2e6ac36
09d8139
0b4fa1d
148ade9
4774a28
2b812da
1821c86
ac90eb4
fc3a218
b7c1288
b8bf22c
70d82c3
f6b3a11
252ee0f
dccf7c2
f353acc
d54a298
bfb3c44
2dc08ee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,11 +3,17 @@ import styled from '@emotion/styled' | |
import {PaymentElement, useStripe, useElements} from '@stripe/react-stripe-js' | ||
import PrimaryButton from '../../../../components/PrimaryButton' | ||
import {PALETTE} from '../../../../styles/paletteV3' | ||
import Confetti from '../../../../components/Confetti' | ||
import UpgradeToTeamTierMutation from '../../../../mutations/UpgradeToTeamTierMutation' | ||
import useAtmosphere from '../../../../hooks/useAtmosphere' | ||
import useMutationProps from '../../../../hooks/useMutationProps' | ||
|
||
const ButtonBlock = styled('div')({ | ||
display: 'flex', | ||
justifyContent: 'center', | ||
paddingTop: 16, | ||
wrap: 'nowrap', | ||
flexDirection: 'column', | ||
width: '100%' | ||
}) | ||
|
||
|
@@ -20,56 +26,65 @@ const StyledForm = styled('form')({ | |
alignItems: 'space-between' | ||
}) | ||
|
||
const PaymentWrapper = styled('div')({ | ||
height: 160 | ||
}) | ||
|
||
const UpgradeButton = styled(PrimaryButton)<{isDisabled: boolean}>(({isDisabled}) => ({ | ||
background: isDisabled ? PALETTE.SLATE_200 : PALETTE.SKY_500, | ||
color: isDisabled ? PALETTE.SLATE_600 : PALETTE.WHITE, | ||
boxShadow: 'none', | ||
marginTop: 16, | ||
width: '100%', | ||
elevation: 0, | ||
'&:hover': { | ||
'&:hover, &:focus': { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a side note, the "Plans" panel does not have any focus behaviour which makes it impossible to use with just the keyboard There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that would be nice! I've created an issue to work on that: #8085 |
||
boxShadow: 'none', | ||
background: isDisabled ? PALETTE.SLATE_200 : PALETTE.SKY_600 | ||
} | ||
})) | ||
|
||
export default function BillingForm() { | ||
type Props = { | ||
orgId: string | ||
} | ||
|
||
const BillingForm = (props: Props) => { | ||
const {orgId} = props | ||
const stripe = useStripe() | ||
const elements = useElements() | ||
const [isLoading, setIsLoading] = useState(false) | ||
const [isPaymentSuccessful, setIsPaymentSuccessful] = useState(false) | ||
const atmosphere = useAtmosphere() | ||
const {onError} = useMutationProps() | ||
|
||
// TODO: implement in: https://github.com/ParabolInc/parabol/issues/7693 | ||
// look at: https://stripe.com/docs/payments/quickstart | ||
const handleSubmit = async () => { | ||
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { | ||
e.preventDefault() | ||
if (!stripe || !elements) return | ||
setIsLoading(true) | ||
const {setupIntent, error} = await stripe.confirmSetup({ | ||
elements, | ||
redirect: 'if_required' | ||
}) | ||
setIsLoading(false) | ||
if (error) return | ||
const {payment_method: paymentMethodId, status} = setupIntent | ||
if (status === 'succeeded' && typeof paymentMethodId === 'string') { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. -1 What about if not? If I test with a declining card 4000000000000002 or with a 3D Secure card 4000002760003184 and decline, then there is no message at all There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, updated! By the way, 3D Secure cards won't create subscriptions in this PR. It'll be handled in #7864 |
||
setIsPaymentSuccessful(true) | ||
const handleCompleted = () => {} | ||
nickoferrall marked this conversation as resolved.
Show resolved
Hide resolved
|
||
UpgradeToTeamTierMutation( | ||
atmosphere, | ||
{orgId, paymentMethodId}, | ||
{onError, onCompleted: handleCompleted} | ||
) | ||
} | ||
} | ||
|
||
if (!stripe || !elements) return null | ||
|
||
return ( | ||
<StyledForm id='payment-form' onSubmit={handleSubmit}> | ||
<PaymentWrapper> | ||
<PaymentElement | ||
id='payment-element' | ||
options={{ | ||
layout: 'tabs', | ||
fields: { | ||
billingDetails: { | ||
address: 'never' | ||
} | ||
} | ||
}} | ||
/> | ||
</PaymentWrapper> | ||
<PaymentElement id='payment-element' options={{layout: 'tabs'}} /> | ||
<ButtonBlock> | ||
<UpgradeButton size='medium' isDisabled={isLoading || !stripe || !elements} type={'submit'}> | ||
{'Upgrade'} | ||
</UpgradeButton> | ||
</ButtonBlock> | ||
<Confetti active={isPaymentSuccessful} /> | ||
nickoferrall marked this conversation as resolved.
Show resolved
Hide resolved
|
||
</StyledForm> | ||
) | ||
} | ||
|
||
export default BillingForm |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import graphql from 'babel-plugin-relay/macro' | ||
import {commitMutation} from 'react-relay' | ||
import {StandardMutation} from '../types/relayMutations' | ||
import {CreateSetupIntentMutation as TCreateSetupIntentMutation} from '../__generated__/CreateSetupIntentMutation.graphql' | ||
|
||
const mutation = graphql` | ||
mutation CreateSetupIntentMutation { | ||
createSetupIntent { | ||
... on ErrorPayload { | ||
error { | ||
message | ||
} | ||
} | ||
... on CreateSetupIntentSuccess { | ||
clientSecret | ||
} | ||
} | ||
} | ||
` | ||
|
||
const CreateSetupIntentMutation: StandardMutation<TCreateSetupIntentMutation> = ( | ||
atmosphere, | ||
variables, | ||
{onError, onCompleted} | ||
) => { | ||
return commitMutation<TCreateSetupIntentMutation>(atmosphere, { | ||
mutation, | ||
variables, | ||
onCompleted, | ||
onError | ||
}) | ||
} | ||
|
||
export default CreateSetupIntentMutation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SetupIntent is used for subscriptions whereas createPaymentIntent is used for one-off payments