Skip to content

Commit 0340859

Browse files
add stripe payment element
1 parent f428955 commit 0340859

File tree

3 files changed

+109
-117
lines changed

3 files changed

+109
-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: 91 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,31 @@
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 =
16+
'pk_test_514SJTOGlVGuVgOrkRgh7zxp3tQ7bX4CY6pnxxw6zRZZSoDVtUUjArPKC7oXeeIbJNICTqS7H88FRfwZnWMskPKxo00bAnu2i9I'
17+
const MANUALLY_FETCHED_CLIENT_SECRET =
18+
'seti_1QcfpCGlVGuVgOrkS4ABlEkc_secret_RVhDX8wtSc5T4NSHJZxeeiCzumvt8r8'
19+
20+
const stripePromise = loadStripe(STRIPE_PUBLISHABLE_KEY)
1721

1822
interface PaymentFormProps {
1923
clientSecret: string
2024
}
2125

22-
const PaymentForm: React.FC<PaymentFormProps> = ({ clientSecret }) => {
26+
const PaymentForm: React.FC<PaymentFormProps> = () => {
2327
const stripe = useStripe()
2428
const elements = useElements()
25-
const [paymentMethod, setPaymentMethod] = useState<'card' | 'bank'>('card')
2629
const [isSubmitting, setIsSubmitting] = useState(false)
2730
const [errorMessage, setErrorMessage] = useState<string | null>(null)
2831

@@ -37,21 +40,11 @@ const PaymentForm: React.FC<PaymentFormProps> = ({ clientSecret }) => {
3740
return
3841
}
3942

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
5143
const { error } = await stripe.confirmPayment({
5244
elements,
5345
confirmParams: {
54-
return_url: 'https://your-website.com/order-complete', // Redirect URL
46+
// eslint-disable-next-line camelcase
47+
return_url: 'https://codecov.io',
5548
},
5649
})
5750

@@ -64,73 +57,50 @@ const PaymentForm: React.FC<PaymentFormProps> = ({ clientSecret }) => {
6457
}
6558

6659
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-
)}
60+
<div>
61+
<PaymentElement
62+
options={{
63+
layout: 'tabs',
64+
defaultValues: {
65+
billingDetails: {
66+
name: 'John Doe',
67+
},
68+
},
69+
}}
70+
/>
71+
<div className="mb-8 mt-4 flex gap-1">
72+
<Button
73+
hook="submit-address-update"
74+
type="submit"
75+
variant="primary"
76+
disabled={isSubmitting} // TODO - handle loading state
77+
onClick={handleSubmit}
78+
to={undefined}
79+
>
80+
Save
81+
</Button>
82+
<Button
83+
type="button"
84+
hook="cancel-address-update"
85+
variant="plain"
86+
// disabled={isLoading}
87+
onClick={() => console.log('TODO - implement me')} // TODO - implement me
88+
to={undefined}
89+
>
90+
Cancel
91+
</Button>
92+
</div>
11893

11994
{errorMessage && <div className="text-red-500">{errorMessage}</div>}
120-
</form>
95+
</div>
12196
)
12297
}
12398

124-
// Wrapper Component to provide Stripe Elements
12599
const PaymentPage: React.FC<{ clientSecret: string }> = ({ clientSecret }) => {
126-
// if (!clientSecret) {
127-
// return <div>Loading...</div>
128-
// }
129-
130100
const options = {
131101
clientSecret,
132102
appearance: {
133-
theme: 'stripe',
103+
theme: 'stripe' as const,
134104
},
135105
}
136106

@@ -145,20 +115,48 @@ interface EditablePaymentMethodProps {
145115
clientSecret: string
146116
}
147117

148-
const EditPaymentMethod: React.FC<EditablePaymentMethodProps> = ({
149-
clientSecret,
150-
}) => {
118+
const EditPaymentMethod: React.FC<EditablePaymentMethodProps> = () => {
119+
const clientSecret = MANUALLY_FETCHED_CLIENT_SECRET // TODO - fetch from API
120+
121+
const [activeTab, setActiveTab] = useState<'primary' | 'secondary'>('primary')
122+
151123
return (
152124
<div className="flex flex-col gap-4 p-4">
153125
<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-
/>
126+
<div>
127+
{/* Tabs for Primary and Secondary Payment Methods */}
128+
<div className="ml-2 flex border-b border-ds-gray-tertiary">
129+
{['primary', 'secondary'].map((tab) => (
130+
<button
131+
key={tab}
132+
className={`py-2 ${tab === 'primary' ? 'mr-4' : ''} ${
133+
activeTab === tab
134+
? 'border-b-2 border-ds-gray-octonary font-semibold text-ds-gray-octonary'
135+
: 'text-ds-gray-quinary hover:border-b-2 hover:border-ds-gray-quinary'
136+
}`}
137+
onClick={() => setActiveTab(tab as 'primary' | 'secondary')}
138+
>
139+
{tab === 'primary' ? 'Primary' : 'Secondary'} Payment Method
140+
</button>
141+
))}
142+
</div>
143+
144+
{/* Payment Details for the selected tab */}
145+
<div className="m-4">
146+
{activeTab === 'primary' && (
147+
<div>
148+
<PaymentPage clientSecret={clientSecret} />
149+
<AddressForm closeForm={() => {}} provider={''} owner={''} />
150+
</div>
151+
)}
152+
{activeTab === 'secondary' && (
153+
<div>
154+
<PaymentPage clientSecret={clientSecret} />
155+
<AddressForm closeForm={() => {}} provider={''} owner={''} />
156+
</div>
157+
)}
158+
</div>
159+
</div>
162160
</div>
163161
)
164162
}

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)