Skip to content
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: Add ACH payment method #3616

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Prev Previous commit
Next Next commit
wip
  • Loading branch information
suejung-sentry committed Jan 1, 2025
commit 1a8560e476c92488e32ab933a65a4df08494dbf9
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from 'react'

Check failure on line 1 in src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/Address/AddressCard.tsx

GitHub Actions / Run Lint

'useState' is defined but never used. Allowed unused vars must match /^_/u
import { z } from 'zod'

import {
@@ -6,9 +6,9 @@
SubscriptionDetailSchema,
} from 'services/account'
import A from 'ui/A'
import Button from 'ui/Button'

Check failure on line 9 in src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/Address/AddressCard.tsx

GitHub Actions / Run Lint

'A' is defined but never used. Allowed unused vars must match /^_/u
import Icon from 'ui/Icon'

Check failure on line 11 in src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/Address/AddressCard.tsx

GitHub Actions / Run Lint

'Icon' is defined but never used. Allowed unused vars must match /^_/u
import AddressForm from './AddressForm'
import { cn } from 'shared/utils/cn'

@@ -32,7 +32,7 @@
const billingDetails =
subscriptionDetail?.defaultPaymentMethod?.billingDetails

const isAddressSameAsPrimary = true // TODO
const isAddressSameAsPrimary = false // TODO

return (
<div className={cn('flex gap-2', className)}>
@@ -46,13 +46,11 @@
/>
)}
{!isEditMode && (
<>
<BillingInner
billingDetails={billingDetails}
setEditMode={setEditMode}
isAddressSameAsPrimary={isAddressSameAsPrimary}
/>
</>
<BillingInner
billingDetails={billingDetails}
setEditMode={setEditMode}
isAddressSameAsPrimary={isAddressSameAsPrimary}
/>
)}
</div>
)
@@ -69,12 +67,21 @@
setEditMode,
isAddressSameAsPrimary,
}: BillingInnerProps) {
const isEmptyAddress =
!billingDetails?.address?.line1 &&
!billingDetails?.address?.line2 &&
!billingDetails?.address?.city &&
!billingDetails?.address?.state &&
!billingDetails?.address?.postalCode

if (billingDetails) {
return (
<div>
<h4 className="mb-2 font-semibold">Billing address</h4>
{isAddressSameAsPrimary ? (
<p>Same as primary address</p>
) : isEmptyAddress ? (
<p>-</p>
) : (
<>
<p>{`${billingDetails.address?.line1 ?? ''} ${
@@ -105,7 +112,7 @@
hook="open-modal"
variant="primary"
onClick={() => setEditMode(true)}
to={undefined}

Check failure on line 115 in src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/Address/AddressCard.tsx

GitHub Actions / Test Runner #2 - Vitest

Unhandled error

TypeError: setEditMode is not a function ❯ onClick src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/Address/AddressCard.tsx:115:15 ❯ HTMLUnknownElement.callCallback node_modules/react-dom/cjs/react-dom.development.js:4164:14 ❯ HTMLUnknownElement.callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ HTMLUnknownElementImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ HTMLUnknownElementImpl.dispatchEvent node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17 ❯ HTMLUnknownElement.dispatchEvent node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:241:34 ❯ Object.invokeGuardedCallbackDev node_modules/react-dom/cjs/react-dom.development.js:4213:16 ❯ invokeGuardedCallback node_modules/react-dom/cjs/react-dom.development.js:4277:31 This error originated in "src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/Address/AddressCard.test.tsx" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. The latest test that might've caused the error is "doesn't render address info stuff anymore". It might mean one of the following: - The error was thrown, while Vitest was running this test. - If the error occurred after the test had been completed, this was the last documented test before it was thrown.

Check failure on line 115 in src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/Address/AddressCard.tsx

GitHub Actions / Test Runner #2 - Vitest

Unhandled error

TypeError: setEditMode is not a function ❯ onClick src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/Address/AddressCard.tsx:115:15 ❯ HTMLUnknownElement.callCallback node_modules/react-dom/cjs/react-dom.development.js:4164:14 ❯ HTMLUnknownElement.callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ HTMLUnknownElementImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ HTMLUnknownElementImpl.dispatchEvent node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17 ❯ HTMLUnknownElement.dispatchEvent node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:241:34 ❯ Object.invokeGuardedCallbackDev node_modules/react-dom/cjs/react-dom.development.js:4213:16 ❯ invokeGuardedCallback node_modules/react-dom/cjs/react-dom.development.js:4277:31 This error originated in "src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/Address/AddressCard.test.tsx" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. The latest test that might've caused the error is "renders the address form component". It might mean one of the following: - The error was thrown, while Vitest was running this test. - If the error occurred after the test had been completed, this was the last documented test before it was thrown.
disabled={false}
>
Set address
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
import Button from 'ui/Button'
import { useState } from 'react'
import A from 'ui/A'
import EditablePaymentMethod from './EditablePaymentMethod'
import EditablePaymentMethod from './EditPaymentMethod'

interface URLParams {
provider: string
@@ -32,8 +32,9 @@
console.log('iseditmode', isEditMode)

return (
<div className="flex flex-col border">
<div className="grid grid-cols-[1fr_auto] items-center gap-4 p-4">
<div className="flex flex-col divide-y border">
{/* Billing Details Section */}
<div className="flex items-center justify-between gap-4 p-4">
<div>
<h3 className="font-semibold">Billing details</h3>
<p className="pt-1 text-xs text-ds-gray-octonary">
@@ -50,22 +51,24 @@
onClick={() => setEditMode(true)}
variant="default"
disabled={!isAdmin}
className="flex-none"
>
Edit payment
</Button>
) : (
<Button
hook="button"
onClick={() => setEditMode(false)}
variant="danger"
variant="default"
disabled={!isAdmin}
className="flex-none"
>
Cancel edit
Back
</Button>
)}
</div>
{isEditMode ? (
<EditablePaymentMethod />

Check failure on line 71 in src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/BillingDetails.tsx

GitHub Actions / Upload Bundle Stats - Production

Property 'clientSecret' is missing in type '{}' but required in type 'EditablePaymentMethodProps'.

Check failure on line 71 in src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/BillingDetails.tsx

GitHub Actions / Run Type Checker

Property 'clientSecret' is missing in type '{}' but required in type 'EditablePaymentMethodProps'.

Check failure on line 71 in src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/BillingDetails.tsx

GitHub Actions / Upload Bundle Stats - Staging

Property 'clientSecret' is missing in type '{}' but required in type 'EditablePaymentMethodProps'.
) : (
<>
<EmailAddress />
Original file line number Diff line number Diff line change
@@ -22,11 +22,11 @@
const PaymentForm: React.FC<PaymentFormProps> = ({ clientSecret }) => {
const stripe = useStripe()
const elements = useElements()
const [paymentMethod, setPaymentMethod] = useState<'card' | 'bank'>('card')

Check failure on line 25 in src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/EditPaymentMethod/EditPaymentMethod.tsx

GitHub Actions / Run Lint

'clientSecret' is defined but never used. Allowed unused args must match /^_/u
const [isSubmitting, setIsSubmitting] = useState(false)
const [errorMessage, setErrorMessage] = useState<string | null>(null)

const handleSubmit = async (event: React.FormEvent) => {

Check failure on line 29 in src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/EditPaymentMethod/EditPaymentMethod.tsx

GitHub Actions / Run Lint

'isSubmitting' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u
event.preventDefault()
setIsSubmitting(true)
setErrorMessage(null)
@@ -38,7 +38,7 @@
}

const paymentElement = elements.getElement(
paymentMethod === 'card' ? CardElement : IbanElement

Check failure on line 41 in src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/EditPaymentMethod/EditPaymentMethod.tsx

GitHub Actions / Upload Bundle Stats - Production

No overload matches this call.

Check failure on line 41 in src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/EditPaymentMethod/EditPaymentMethod.tsx

GitHub Actions / Upload Bundle Stats - Staging

No overload matches this call.
)

if (!paymentElement) {
@@ -54,7 +54,7 @@
return_url: 'https://your-website.com/order-complete', // Redirect URL
},
})

Check failure on line 57 in src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/EditPaymentMethod/EditPaymentMethod.tsx

GitHub Actions / Run Lint

Identifier 'return_url' is not in camel case
if (error) {
setErrorMessage(error.message || 'An unexpected error occurred.')
setIsSubmitting(false)
@@ -135,7 +135,7 @@
}

return (
<Elements stripe={stripePromise} options={options}>

Check failure on line 138 in src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/EditPaymentMethod/EditPaymentMethod.tsx

GitHub Actions / Upload Bundle Stats - Production

Type '{ clientSecret: string; appearance: { theme: string; }; }' is not assignable to type 'StripeElementsOptions | undefined'.

Check failure on line 138 in src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/EditPaymentMethod/EditPaymentMethod.tsx

GitHub Actions / Upload Bundle Stats - Staging

Type '{ clientSecret: string; appearance: { theme: string; }; }' is not assignable to type 'StripeElementsOptions | undefined'.
<PaymentForm clientSecret={clientSecret} />
</Elements>
)
@@ -145,7 +145,7 @@
clientSecret: string
}

const EditablePaymentMethod: React.FC<EditablePaymentMethodProps> = ({
const EditPaymentMethod: React.FC<EditablePaymentMethodProps> = ({
clientSecret,
}) => {
return (
@@ -163,4 +163,4 @@
)
}

export default EditablePaymentMethod
export default EditPaymentMethod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './EditPaymentMethod'

This file was deleted.

Original file line number Diff line number Diff line change
@@ -28,21 +28,21 @@ function CardInformation({ subscriptionDetail, card }: CardInformationProps) {

return (
<div className="flex flex-col gap-2">
<div className="flex gap-2">
<div className="flex gap-1">
<img
className="h-auto w-16 self-center"
className="h-auto w-8 self-center"
alt="credit card logo"
src={typeCard?.logo}
/>
<div className="flex flex-col self-center">
<b className="tracking-widest">••••&nbsp;{card?.last4}</b>
<b>••••&nbsp;{card?.last4}</b>
</div>
</div>
<p className="text-ds-gray-quinary">
Expires {card?.expMonth}/{lastTwoDigits(card?.expYear)}
</p>
{nextBilling && (
<p className="text-sm text-ds-gray-quinary">
<p className="text-ds-gray-quinary">
Your next billing date is{' '}
<span className="text-ds-gray-octonary">{nextBilling}</span>.
</p>
Original file line number Diff line number Diff line change
@@ -32,50 +32,60 @@ function PaymentMethod({

console.log(isEditMode)
return (
<ExpandableSection>
<ExpandableSection.Trigger className="p-4">
<h3 className="font-semibold">{heading}</h3>
</ExpandableSection.Trigger>
<ExpandableSection.Content className="px-4 pb-4">
{!isPrimary ? (
<p className="pl-4 text-sm text-ds-gray-quaternary">
By default, if the primary payment fails, the secondary will be
charged automatically.
</p>
) : null}
<div className="flex gap-2">
<PaymentCard
className="flex-1"
isEditMode={isEditMode}
setEditMode={setEditMode}
subscriptionDetail={subscriptionDetail}
provider={provider}
owner={owner}
/>
<div className="flex-1 border-x border-ds-gray-tertiary px-4 pt-4">
<h4 className="font-semibold">{isCreditCard ? 'Cardholder name' : 'Full name'}</h4>
<p>N/A</p>
<div>
<ExpandableSection className="m-0 border-0" defaultOpen={isPrimary}>
<ExpandableSection.Trigger className="p-4">
<h3 className="font-semibold">{heading}</h3>
</ExpandableSection.Trigger>
<ExpandableSection.Content className="border-0 pt-0 text-xs">
<div className="pb-4 pl-4 pt-2">
{!isPrimary ? (
<p className="mb-6 text-ds-gray-quaternary">
By default, if the primary payment fails, the secondary will be
charged automatically.
</p>
) : null}
<div className="flex">
{/* Payment method summary */}
<PaymentCard
className="w-2/5 flex-1"
isEditMode={isEditMode}
setEditMode={setEditMode}
subscriptionDetail={subscriptionDetail}
provider={provider}
owner={owner}
/>
{/* Cardholder name */}
<div className="mx-4 w-1/5 border-x border-ds-gray-tertiary px-4">
<h4 className="mb-2 font-semibold">
{isCreditCard ? 'Cardholder name' : 'Full name'}
</h4>
<p>N/A</p>
</div>
{/* Address */}
<AddressCard
className="flex-1"
isEditMode={isEditMode}
setEditMode={setEditMode}
subscriptionDetail={subscriptionDetail}
provider={provider}
owner={owner}
/>
</div>
{!isPrimary ? (
<Button
hook="button"
disabled={!isAdmin}
onClick={() => console.log('TODO - implement me')}
className="mt-4"
>
Set as primary
</Button>
) : null}
</div>
<AddressCard
className="flex-1"
isEditMode={isEditMode}
setEditMode={setEditMode}
subscriptionDetail={subscriptionDetail}
provider={provider}
owner={owner}
/>
</div>
{!isPrimary ? (
<Button
hook="button"
disabled={!isAdmin}
onClick={() => console.log('TODO - implement me')}
>
Set as primary
</Button>
) : null}
</ExpandableSection.Content>
</ExpandableSection>
</ExpandableSection.Content>
</ExpandableSection>
</div>
)
}

3 changes: 2 additions & 1 deletion src/ui/Button/Button.tsx
Original file line number Diff line number Diff line change
@@ -134,7 +134,8 @@ function Button({
const className = cs(
baseClass,
{ [baseDisabledClasses]: !isLoading },
pickVariant(variant, isLoading)
pickVariant(variant, isLoading),
props.className
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what was this one for? :O

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had needed to add a flex-none for formatting on one of the buttons

)

const content = (
Loading
Oops, something went wrong.