Skip to content

Commit 0f6d1c3

Browse files
add stripe payment element
1 parent f428955 commit 0f6d1c3

File tree

3 files changed

+107
-117
lines changed

3 files changed

+107
-117
lines changed

.env.development

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,3 @@ REACT_APP_MARKETING_BASE_URL=https://about.codecov.io
55
# REACT_APP_STRIPE_KEY=
66
# REACT_APP_LAUNCHDARKLY=
77
# REACT_APP_BAREMETRICS_TOKEN=
8-
REACT_APP_STRIPE_KEY=pk_test_514SJTOGlVGuVgOrkRgh7zxp3tQ7bX4CY6pnxxw6zRZZSoDVtUUjArPKC7oXeeIbJNICTqS7H88FRfwZnWMskPKxo00bAnu2i9I
9-
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_514SJTOGlVGuVgOrkRgh7zxp3tQ7bX4CY6pnxxw6zRZZSoDVtUUjArPKC7oXeeIbJNICTqS7H88FRfwZnWMskPKxo00bAnu2i9I

src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/EditPaymentMethod/EditPaymentMethod.tsx

Lines changed: 89 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
1-
import React, { useState } from 'react'
2-
import AddressForm from '../Address/AddressForm'
31
import {
42
Elements,
5-
CardElement,
6-
IbanElement,
7-
useStripe,
3+
PaymentElement,
84
useElements,
5+
useStripe,
96
} from '@stripe/react-stripe-js'
107
import { loadStripe } from '@stripe/stripe-js'
11-
import CreditCardForm from '../PaymentCard/CreditCardForm'
12-
import { RadioTileGroup } from 'ui/RadioTileGroup'
13-
import Icon from 'ui/Icon'
8+
import React, { useState } from 'react'
9+
10+
import Button from 'ui/Button'
11+
12+
import AddressForm from '../Address/AddressForm'
1413

15-
// Load your Stripe public key
16-
const stripePromise = loadStripe('your-publishable-key-here')
14+
// TODO - fetch from API
15+
const STRIPE_PUBLISHABLE_KEY = process.env.STRIPE_PUBLISHABLE_KEY || ''
16+
const MANUALLY_FETCHED_CLIENT_SECRET = process.env.STRIPE_CLIENT_SECRET || ''
17+
18+
const stripePromise = loadStripe(STRIPE_PUBLISHABLE_KEY)
1719

1820
interface PaymentFormProps {
1921
clientSecret: string
2022
}
2123

22-
const PaymentForm: React.FC<PaymentFormProps> = ({ clientSecret }) => {
24+
const PaymentForm: React.FC<PaymentFormProps> = () => {
2325
const stripe = useStripe()
2426
const elements = useElements()
25-
const [paymentMethod, setPaymentMethod] = useState<'card' | 'bank'>('card')
2627
const [isSubmitting, setIsSubmitting] = useState(false)
2728
const [errorMessage, setErrorMessage] = useState<string | null>(null)
2829

@@ -37,21 +38,11 @@ const PaymentForm: React.FC<PaymentFormProps> = ({ clientSecret }) => {
3738
return
3839
}
3940

40-
const paymentElement = elements.getElement(
41-
paymentMethod === 'card' ? CardElement : IbanElement
42-
)
43-
44-
if (!paymentElement) {
45-
setErrorMessage('Payment element is missing.')
46-
setIsSubmitting(false)
47-
return
48-
}
49-
50-
// Confirm payment based on selected method
5141
const { error } = await stripe.confirmPayment({
5242
elements,
5343
confirmParams: {
54-
return_url: 'https://your-website.com/order-complete', // Redirect URL
44+
// eslint-disable-next-line camelcase
45+
return_url: 'https://codecov.io',
5546
},
5647
})
5748

@@ -64,73 +55,50 @@ const PaymentForm: React.FC<PaymentFormProps> = ({ clientSecret }) => {
6455
}
6556

6657
return (
67-
<form onSubmit={handleSubmit} className="space-y-4">
68-
<h2 className="text-lg font-bold">Choose Payment Method</h2>
69-
70-
<RadioTileGroup
71-
value={paymentMethod}
72-
onValueChange={(value: 'card' | 'bank') => setPaymentMethod(value)}
73-
className="flex-row"
74-
>
75-
<RadioTileGroup.Item value="card" data-testid="card-radio">
76-
<RadioTileGroup.Label>
77-
<div className="flex items-center gap-2">
78-
<Icon name="checkCircle"></Icon>
79-
Card
80-
</div>
81-
</RadioTileGroup.Label>
82-
</RadioTileGroup.Item>
83-
<RadioTileGroup.Item value="bank" data-testid="bank-radio">
84-
<RadioTileGroup.Label>
85-
<div className="flex items-center gap-2">
86-
<Icon name="checkCircle"></Icon>
87-
Bank Account
88-
</div>
89-
</RadioTileGroup.Label>
90-
</RadioTileGroup.Item>
91-
</RadioTileGroup>
92-
93-
{/* Payment Element */}
94-
{paymentMethod === 'card' && (
95-
<div>
96-
<h3 className="font-semibold">Card Details</h3>
97-
<CreditCardForm
98-
closeForm={function (): void {
99-
throw new Error('Function not implemented.')
100-
}}
101-
provider={''}
102-
owner={''}
103-
/>
104-
</div>
105-
)}
106-
107-
{paymentMethod === 'bank' && (
108-
<div>
109-
<h3 className="font-semibold">Bank Account Details</h3>
110-
<IbanElement
111-
className="border p-2 rounded"
112-
options={{
113-
supportedCountries: ['SEPA'], // Specify the supported countries
114-
}}
115-
/>
116-
</div>
117-
)}
58+
<div>
59+
<PaymentElement
60+
options={{
61+
layout: 'tabs',
62+
defaultValues: {
63+
billingDetails: {
64+
name: 'John Doe',
65+
},
66+
},
67+
}}
68+
/>
69+
<div className="mb-8 mt-4 flex gap-1">
70+
<Button
71+
hook="submit-address-update"
72+
type="submit"
73+
variant="primary"
74+
disabled={isSubmitting} // TODO - handle
75+
onClick={handleSubmit}
76+
to={undefined}
77+
>
78+
Save
79+
</Button>
80+
<Button
81+
type="button"
82+
hook="cancel-address-update"
83+
variant="plain"
84+
// disabled={isLoading}
85+
onClick={() => console.log('TODO - implement me')} // TODO - implement me
86+
to={undefined}
87+
>
88+
Cancel
89+
</Button>
90+
</div>
11891

11992
{errorMessage && <div className="text-red-500">{errorMessage}</div>}
120-
</form>
93+
</div>
12194
)
12295
}
12396

124-
// Wrapper Component to provide Stripe Elements
12597
const PaymentPage: React.FC<{ clientSecret: string }> = ({ clientSecret }) => {
126-
// if (!clientSecret) {
127-
// return <div>Loading...</div>
128-
// }
129-
13098
const options = {
13199
clientSecret,
132100
appearance: {
133-
theme: 'stripe',
101+
theme: 'stripe' as const,
134102
},
135103
}
136104

@@ -145,20 +113,48 @@ interface EditablePaymentMethodProps {
145113
clientSecret: string
146114
}
147115

148-
const EditPaymentMethod: React.FC<EditablePaymentMethodProps> = ({
149-
clientSecret,
150-
}) => {
116+
const EditPaymentMethod: React.FC<EditablePaymentMethodProps> = () => {
117+
const clientSecret = MANUALLY_FETCHED_CLIENT_SECRET // TODO - fetch from API
118+
119+
const [activeTab, setActiveTab] = useState<'primary' | 'secondary'>('primary')
120+
151121
return (
152122
<div className="flex flex-col gap-4 p-4">
153123
<h3 className="font-semibold">Edit payment method</h3>
154-
<PaymentPage clientSecret={clientSecret} />
155-
<AddressForm
156-
closeForm={function (): void {
157-
throw new Error('TODO')
158-
}}
159-
provider={''}
160-
owner={''}
161-
/>
124+
<div>
125+
{/* Tabs for Primary and Secondary Payment Methods */}
126+
<div className="ml-2 flex border-b border-ds-gray-tertiary">
127+
{['primary', 'secondary'].map((tab) => (
128+
<button
129+
key={tab}
130+
className={`py-2 ${tab === 'primary' ? 'mr-4' : ''} ${
131+
activeTab === tab
132+
? 'border-b-2 border-ds-gray-octonary font-semibold text-ds-gray-octonary'
133+
: 'text-ds-gray-quinary hover:border-b-2 hover:border-ds-gray-quinary'
134+
}`}
135+
onClick={() => setActiveTab(tab as 'primary' | 'secondary')}
136+
>
137+
{tab === 'primary' ? 'Primary' : 'Secondary'} Payment Method
138+
</button>
139+
))}
140+
</div>
141+
142+
{/* Payment Details for the selected tab */}
143+
<div className="m-4">
144+
{activeTab === 'primary' && (
145+
<div>
146+
<PaymentPage clientSecret={clientSecret} />
147+
<AddressForm closeForm={() => {}} provider={''} owner={''} />
148+
</div>
149+
)}
150+
{activeTab === 'secondary' && (
151+
<div>
152+
<PaymentPage clientSecret={clientSecret} />
153+
<AddressForm closeForm={() => {}} provider={''} owner={''} />
154+
</div>
155+
)}
156+
</div>
157+
</div>
162158
</div>
163159
)
164160
}

src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/PaymentMethod/PaymentMethod.tsx

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { z } from 'zod'
22

33
import { SubscriptionDetailSchema } from 'services/account'
4+
import Button from 'ui/Button'
45
import { ExpandableSection } from 'ui/ExpandableSection'
56

67
import AddressCard from '../Address/AddressCard'
78
import PaymentCard from '../PaymentCard'
8-
import Button from 'ui/Button'
99

1010
function PaymentMethod({
1111
heading,
@@ -25,12 +25,8 @@ function PaymentMethod({
2525
owner: string
2626
}) {
2727
const isAdmin = true // TODO
28-
2928
const isCreditCard = subscriptionDetail?.defaultPaymentMethod?.card // TODO
3029

31-
console.log(subscriptionDetail)
32-
33-
console.log(isEditMode)
3430
return (
3531
<div>
3632
<ExpandableSection className="m-0 border-0" defaultOpen={isPrimary}>
@@ -47,14 +43,14 @@ function PaymentMethod({
4743
) : null}
4844
<div className="flex">
4945
{/* Payment method summary */}
50-
<PaymentCard
51-
className="w-2/5 flex-1"
52-
isEditMode={isEditMode}
53-
setEditMode={setEditMode}
54-
subscriptionDetail={subscriptionDetail}
55-
provider={provider}
56-
owner={owner}
57-
/>
46+
<PaymentCard
47+
className="w-2/5 flex-1"
48+
isEditMode={isEditMode}
49+
setEditMode={setEditMode}
50+
subscriptionDetail={subscriptionDetail}
51+
provider={provider}
52+
owner={owner}
53+
/>
5854
{/* Cardholder name */}
5955
<div className="mx-4 w-1/5 border-x border-ds-gray-tertiary px-4">
6056
<h4 className="mb-2 font-semibold">
@@ -63,20 +59,20 @@ function PaymentMethod({
6359
<p>N/A</p>
6460
</div>
6561
{/* Address */}
66-
<AddressCard
67-
className="flex-1"
68-
isEditMode={isEditMode}
69-
setEditMode={setEditMode}
70-
subscriptionDetail={subscriptionDetail}
71-
provider={provider}
72-
owner={owner}
73-
/>
62+
<AddressCard
63+
className="flex-1"
64+
isEditMode={isEditMode}
65+
setEditMode={setEditMode}
66+
subscriptionDetail={subscriptionDetail}
67+
provider={provider}
68+
owner={owner}
69+
/>
7470
</div>
7571
{!isPrimary ? (
7672
<Button
7773
hook="button"
7874
disabled={!isAdmin}
79-
onClick={() => console.log('TODO - implement me')}
75+
onClick={() => setEditMode(true)}
8076
className="mt-4"
8177
>
8278
Set as primary

0 commit comments

Comments
 (0)