mirror of
https://github.com/mozilla/fxa.git
synced 2025-12-13 20:36:41 +01:00
Merge pull request #19778 from mozilla/PAY-3417-remove-name-as-it-appears-on-card-field
fix(payments-next): "Name as it appears on credit card" field does not appear prior to entering credit card number
This commit is contained in:
@@ -44,7 +44,6 @@ export const CheckoutCustomerDataFactory = (
|
||||
override?: Partial<CheckoutCustomerData>
|
||||
): CheckoutCustomerData => ({
|
||||
locale: faker.helpers.arrayElement(['en-US', 'de', 'es', 'fr-FR']),
|
||||
displayName: faker.person.fullName(),
|
||||
...override,
|
||||
});
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import Stripe from 'stripe';
|
||||
|
||||
export type CheckoutCustomerData = {
|
||||
locale: string;
|
||||
displayName: string;
|
||||
};
|
||||
|
||||
export type FinishCart = {
|
||||
|
||||
@@ -164,7 +164,6 @@ export class CheckoutService {
|
||||
customer = await this.customerManager.create({
|
||||
uid,
|
||||
email,
|
||||
displayName: customerData.displayName,
|
||||
taxAddress,
|
||||
});
|
||||
|
||||
|
||||
@@ -120,7 +120,6 @@ describe('CustomerManager', () => {
|
||||
const result = await customerManager.create({
|
||||
uid: faker.string.uuid(),
|
||||
email: faker.internet.email(),
|
||||
displayName: faker.person.fullName(),
|
||||
taxAddress: taxAddress,
|
||||
});
|
||||
|
||||
|
||||
@@ -45,10 +45,9 @@ export class CustomerManager {
|
||||
async create(args: {
|
||||
uid: string;
|
||||
email: string;
|
||||
displayName: string;
|
||||
taxAddress?: TaxAddress;
|
||||
}) {
|
||||
const { uid, email, displayName, taxAddress } = args;
|
||||
const { uid, email, taxAddress } = args;
|
||||
|
||||
const shipping = taxAddress
|
||||
? {
|
||||
@@ -62,7 +61,6 @@ export class CustomerManager {
|
||||
|
||||
const customer = await this.stripeClient.customersCreate({
|
||||
email,
|
||||
name: displayName || '',
|
||||
description: uid,
|
||||
metadata: {
|
||||
[STRIPE_CUSTOMER_METADATA.Userid]: uid,
|
||||
|
||||
@@ -1477,7 +1477,6 @@ describe('SubscriptionManagementService', () => {
|
||||
const mockPaymentMethod = StripeResponseFactory(
|
||||
StripePaymentMethodFactory()
|
||||
);
|
||||
const mockFullName = faker.person.fullName();
|
||||
|
||||
jest
|
||||
.spyOn(accountCustomerManager, 'getAccountCustomerByUid')
|
||||
@@ -1486,8 +1485,7 @@ describe('SubscriptionManagementService', () => {
|
||||
|
||||
await subscriptionManagementService.setDefaultStripePaymentDetails(
|
||||
mockCustomer.id,
|
||||
mockPaymentMethod.id,
|
||||
mockFullName
|
||||
mockPaymentMethod.id
|
||||
);
|
||||
|
||||
expect(customerManager.update).toHaveBeenCalledWith(
|
||||
@@ -1496,7 +1494,6 @@ describe('SubscriptionManagementService', () => {
|
||||
invoice_settings: {
|
||||
default_payment_method: mockPaymentMethod.id,
|
||||
},
|
||||
name: mockFullName,
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -1513,7 +1510,6 @@ describe('SubscriptionManagementService', () => {
|
||||
subscriptionManagementService.setDefaultStripePaymentDetails(
|
||||
mockAccountCustomer.uid,
|
||||
'pm_12345',
|
||||
'john doe'
|
||||
)
|
||||
).rejects.toBeInstanceOf(SetDefaultPaymentAccountCustomerMissingStripeId);
|
||||
});
|
||||
|
||||
@@ -940,7 +940,6 @@ export class SubscriptionManagementService {
|
||||
async setDefaultStripePaymentDetails(
|
||||
uid: string,
|
||||
paymentMethodId: string,
|
||||
fullName: string
|
||||
) {
|
||||
const accountCustomer =
|
||||
await this.accountCustomerManager.getAccountCustomerByUid(uid);
|
||||
@@ -952,8 +951,7 @@ export class SubscriptionManagementService {
|
||||
await this.customerManager.update(accountCustomer.stripeCustomerId, {
|
||||
invoice_settings: {
|
||||
default_payment_method: paymentMethodId,
|
||||
},
|
||||
name: fullName,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ export const checkoutCartWithStripe = async (
|
||||
confirmationTokenId: string,
|
||||
customerData: {
|
||||
locale: string;
|
||||
displayName: string;
|
||||
},
|
||||
attribution: SubscriptionAttributionParams,
|
||||
sessionUid?: string
|
||||
|
||||
@@ -9,13 +9,11 @@ import { getApp } from '../nestapp/app';
|
||||
export const setDefaultStripePaymentDetails = async (
|
||||
uid: string,
|
||||
paymentMethodId: string,
|
||||
fullName: string
|
||||
) => {
|
||||
const actionsService = getApp().getActionsService();
|
||||
|
||||
return await actionsService.setDefaultStripePaymentDetails({
|
||||
uid,
|
||||
paymentMethodId,
|
||||
fullName,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
## Checkout Form
|
||||
|
||||
next-new-user-submit = Subscribe Now
|
||||
next-payment-validate-name-error = Please enter your full name
|
||||
|
||||
next-pay-with-heading-paypal = Pay with { -brand-paypal }
|
||||
|
||||
# Label for the Full Name input
|
||||
payment-name-label = Name as it appears on your card
|
||||
payment-name-placeholder = Full Name
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
'use client';
|
||||
|
||||
import { Localized, useLocalization } from '@fluent/react';
|
||||
import { Localized, } from '@fluent/react';
|
||||
import { PayPalButtons } from '@paypal/react-paypal-js';
|
||||
import * as Form from '@radix-ui/react-form';
|
||||
import Stripe from 'stripe';
|
||||
@@ -97,7 +97,6 @@ export function CheckoutForm({
|
||||
sessionUid,
|
||||
sessionEmail,
|
||||
}: CheckoutFormProps) {
|
||||
const { l10n } = useLocalization();
|
||||
const elements = useElements();
|
||||
const router = useRouter();
|
||||
const stripe = useStripe();
|
||||
@@ -113,8 +112,6 @@ export function CheckoutForm({
|
||||
const [isPaymentElementLoading, setIsPaymentElementLoading] = useState(true);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [stripeFieldsComplete, setStripeFieldsComplete] = useState(false);
|
||||
const [fullName, setFullName] = useState('');
|
||||
const [hasFullNameError, setHasFullNameError] = useState(false);
|
||||
const [selectedPaymentMethod, setSelectedPaymentMethod] = useState('');
|
||||
const [isSavedPaymentMethod, setIsSavedPaymentMethod] = useState(
|
||||
!!cart?.paymentInfo?.type
|
||||
@@ -123,7 +120,6 @@ export function CheckoutForm({
|
||||
const linkAuthOptions = sessionEmail
|
||||
? { defaultValues: { email: sessionEmail } }
|
||||
: {};
|
||||
const [isNotCard, setIsNotCard] = useState(false);
|
||||
|
||||
const engageGlean = useCallbackOnce(() => {
|
||||
recordEmitterEventAction(
|
||||
@@ -155,10 +151,6 @@ export function CheckoutForm({
|
||||
const isNewCardSelected =
|
||||
event?.value?.type === 'card' && !hasSavedPaymentMethod;
|
||||
|
||||
const selectedType = event?.value?.type || '';
|
||||
const isNotCardType = selectedType !== 'card';
|
||||
setIsNotCard(isNotCardType);
|
||||
|
||||
setShowLinkAuthElement(isNewCardSelected && hasSavedPaymentMethod);
|
||||
|
||||
setSelectedPaymentMethod(event?.value?.type || '');
|
||||
@@ -172,13 +164,6 @@ export function CheckoutForm({
|
||||
|
||||
const showPayPalButton = selectedPaymentMethod === 'external_paypal';
|
||||
const isStripe = cart?.paymentInfo?.type !== 'external_paypal';
|
||||
const showFullNameInput =
|
||||
!isPaymentElementLoading &&
|
||||
!showPayPalButton &&
|
||||
!isSavedPaymentMethod &&
|
||||
selectedPaymentMethod === 'card' &&
|
||||
!isNotCard;
|
||||
const nonStripeFieldsComplete = !showFullNameInput || !!fullName;
|
||||
|
||||
const submitHandler = async (
|
||||
event: React.SyntheticEvent<HTMLFormElement>
|
||||
@@ -220,16 +205,6 @@ export function CheckoutForm({
|
||||
return;
|
||||
}
|
||||
|
||||
if (showFullNameInput) {
|
||||
setHasFullNameError(!fullName);
|
||||
if (!fullName) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
setHasFullNameError(false);
|
||||
}
|
||||
|
||||
// Trigger form validation and wallet collection
|
||||
const { error: submitError } = await elements.submit();
|
||||
if (submitError) {
|
||||
@@ -248,7 +223,6 @@ export function CheckoutForm({
|
||||
? {
|
||||
payment_method_data: {
|
||||
billing_details: {
|
||||
name: fullName,
|
||||
email: sessionEmail || undefined,
|
||||
},
|
||||
},
|
||||
@@ -291,7 +265,6 @@ export function CheckoutForm({
|
||||
confirmationToken.id,
|
||||
{
|
||||
locale,
|
||||
displayName: fullName,
|
||||
},
|
||||
getAttributionParams(searchParams),
|
||||
sessionUid
|
||||
@@ -329,55 +302,6 @@ export function CheckoutForm({
|
||||
}
|
||||
onClick={() => setShowConsentError(true)}
|
||||
>
|
||||
{showFullNameInput && (
|
||||
<>
|
||||
<Localized id="next-new-user-card-title">
|
||||
<h3 className="font-semibold text-grey-600 text-start">
|
||||
Enter your card information
|
||||
</h3>
|
||||
</Localized>
|
||||
<Form.Field
|
||||
name="name"
|
||||
serverInvalid={hasFullNameError}
|
||||
className="my-6"
|
||||
>
|
||||
<Form.Label className="text-grey-400 block mb-1 text-start">
|
||||
<Localized id="payment-name-label">
|
||||
Name as it appears on your card
|
||||
</Localized>
|
||||
</Form.Label>
|
||||
<Form.Control asChild>
|
||||
<input
|
||||
className="w-full border rounded-md border-black/30 p-3 placeholder:text-grey-500 placeholder:font-normal focus:border focus:!border-black/30 focus:!shadow-[0_0_0_3px_rgba(10,132,255,0.3)] focus-visible:outline-none data-[invalid=true]:border-alert-red data-[invalid=true]:text-alert-red data-[invalid=true]:shadow-inputError"
|
||||
type="text"
|
||||
data-testid="name"
|
||||
placeholder={l10n.getString(
|
||||
'payment-name-placeholder',
|
||||
{},
|
||||
'Full Name'
|
||||
)}
|
||||
readOnly={!formEnabled}
|
||||
tabIndex={formEnabled ? 0 : -1}
|
||||
value={fullName}
|
||||
onChange={(e) => {
|
||||
setFullName(e.target.value);
|
||||
setHasFullNameError(!e.target.value);
|
||||
}}
|
||||
aria-required
|
||||
/>
|
||||
</Form.Control>
|
||||
{hasFullNameError && (
|
||||
<Form.Message asChild>
|
||||
<Localized id="next-payment-validate-name-error">
|
||||
<p className="mt-1 text-alert-red" role="alert">
|
||||
Please enter your name
|
||||
</p>
|
||||
</Localized>
|
||||
</Form.Message>
|
||||
)}
|
||||
</Form.Field>
|
||||
</>
|
||||
)}
|
||||
{cart?.paymentInfo?.type === 'external_paypal' ? (
|
||||
<div className="bg-white rounded-lg border border-[#e6e6e6] shadow-stripeBox">
|
||||
<h3 className="p-4 text-sm text-[#0570de] font-semibold">Saved</h3>
|
||||
@@ -470,8 +394,7 @@ export function CheckoutForm({
|
||||
variant={ButtonVariant.Primary}
|
||||
aria-disabled={
|
||||
!formEnabled ||
|
||||
(isStripe &&
|
||||
!(stripeFieldsComplete && nonStripeFieldsComplete)) ||
|
||||
(isStripe && !stripeFieldsComplete) ||
|
||||
loading
|
||||
}
|
||||
>
|
||||
|
||||
@@ -42,8 +42,6 @@ export function PaymentMethodManagement({
|
||||
const [isReady, setIsReady] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [isInputNewCardDetails, setIsInputNewCardDetails] = useState(false);
|
||||
const [fullName, setFullName] = useState('');
|
||||
const [hasFullNameError, setHasFullNameError] = useState(false);
|
||||
const [isNonDefaultCardSelected, setIsNonDefaultCardSelected] =
|
||||
useState(false);
|
||||
const [isNonCardSelected, setIsNonCardSelected] = useState(false);
|
||||
@@ -68,7 +66,6 @@ export function PaymentMethodManagement({
|
||||
if (event.value.type !== 'card') {
|
||||
setIsNonCardSelected(true);
|
||||
setIsInputNewCardDetails(false);
|
||||
setHasFullNameError(false);
|
||||
if (!!event.value.payment_method) {
|
||||
if (event.value.payment_method.id !== defaultPaymentMethodId) {
|
||||
setIsNonDefaultCardSelected(true);
|
||||
@@ -82,10 +79,6 @@ export function PaymentMethodManagement({
|
||||
|
||||
if (event.value.type === 'card' && !event.value.payment_method) {
|
||||
setIsInputNewCardDetails(true);
|
||||
|
||||
if (event.complete) {
|
||||
setHasFullNameError(fullName.length === 0);
|
||||
}
|
||||
} else if (event.value.type === 'card' && !!event.value.payment_method) {
|
||||
setIsInputNewCardDetails(false);
|
||||
|
||||
@@ -117,8 +110,7 @@ export function PaymentMethodManagement({
|
||||
uid ?? '',
|
||||
typeof response.setupIntent.payment_method === 'string'
|
||||
? response.setupIntent.payment_method
|
||||
: (response.setupIntent.payment_method?.id ?? ''),
|
||||
fullName
|
||||
: (response.setupIntent.payment_method?.id ?? '')
|
||||
);
|
||||
} else {
|
||||
throw new Error('We could not confirm your payment method');
|
||||
@@ -132,11 +124,6 @@ export function PaymentMethodManagement({
|
||||
return;
|
||||
}
|
||||
|
||||
if (isInputNewCardDetails && !fullName) {
|
||||
setHasFullNameError(true);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
@@ -154,7 +141,6 @@ export function PaymentMethodManagement({
|
||||
params: {
|
||||
payment_method_data: {
|
||||
billing_details: {
|
||||
name: fullName,
|
||||
email: sessionEmail || undefined,
|
||||
},
|
||||
},
|
||||
@@ -206,56 +192,6 @@ export function PaymentMethodManagement({
|
||||
onSubmit={handleSubmit}
|
||||
className="px-4 tablet:px-0"
|
||||
>
|
||||
{isInputNewCardDetails && (
|
||||
<>
|
||||
<Localized id="next-new-user-card-title">
|
||||
<h2 className="font-semibold text-grey-600 text-start mt-6">
|
||||
Enter your card information
|
||||
</h2>
|
||||
</Localized>
|
||||
<Form.Field
|
||||
name="name"
|
||||
serverInvalid={hasFullNameError}
|
||||
className="my-6"
|
||||
>
|
||||
<Form.Label className="text-grey-400 block mb-1 text-start">
|
||||
<Localized id="payment-name-label">
|
||||
Name as it appears on your card
|
||||
</Localized>
|
||||
</Form.Label>
|
||||
<Form.Control asChild>
|
||||
<input
|
||||
className="w-full border rounded-md border-black/30 p-3 placeholder:text-grey-500 placeholder:font-normal focus:border focus:!border-black/30 focus:!shadow-[0_0_0_3px_rgba(10,132,255,0.3)] focus-visible:outline-none data-[invalid=true]:border-alert-red data-[invalid=true]:text-alert-red data-[invalid=true]:shadow-inputError"
|
||||
type="text"
|
||||
data-testid="name"
|
||||
placeholder={l10n.getString(
|
||||
'payment-name-placeholder',
|
||||
{},
|
||||
'Full Name'
|
||||
)}
|
||||
value={fullName}
|
||||
onChange={(e) => {
|
||||
setFullName(e.target.value);
|
||||
setHasFullNameError(!e.target.value);
|
||||
}}
|
||||
aria-required
|
||||
/>
|
||||
</Form.Control>
|
||||
{hasFullNameError && (
|
||||
<Form.Message asChild>
|
||||
<Localized id="next-payment-validate-name-error">
|
||||
<p
|
||||
className="mt-1 text-alert-red font-normal"
|
||||
role="alert"
|
||||
>
|
||||
Please enter your full name
|
||||
</p>
|
||||
</Localized>
|
||||
</Form.Message>
|
||||
)}
|
||||
</Form.Field>
|
||||
</>
|
||||
)}
|
||||
<Form.Field name="payment">
|
||||
<Form.Control asChild>
|
||||
<div
|
||||
@@ -297,10 +233,10 @@ export function PaymentMethodManagement({
|
||||
type="submit"
|
||||
variant={ButtonVariant.Primary}
|
||||
aria-disabled={
|
||||
!stripe || !isComplete || isLoading || hasFullNameError
|
||||
!stripe || !isComplete || isLoading
|
||||
}
|
||||
disabled={
|
||||
!stripe || !isComplete || isLoading || hasFullNameError
|
||||
!stripe || !isComplete || isLoading
|
||||
}
|
||||
>
|
||||
{isLoading ? (
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
## Payment Section
|
||||
|
||||
next-new-user-card-title = Enter your card information
|
||||
@@ -480,7 +480,7 @@ export class NextJSActionsService {
|
||||
cartId: string;
|
||||
version: number;
|
||||
confirmationTokenId: string;
|
||||
customerData: { locale: string; displayName: string };
|
||||
customerData: { locale: string;};
|
||||
attribution: SubscriptionAttributionParams;
|
||||
sessionUid?: string;
|
||||
}) {
|
||||
@@ -878,12 +878,10 @@ export class NextJSActionsService {
|
||||
async setDefaultStripePaymentDetails(args: {
|
||||
uid: string;
|
||||
paymentMethodId: string;
|
||||
fullName: string;
|
||||
}) {
|
||||
return await this.subscriptionManagementService.setDefaultStripePaymentDetails(
|
||||
args.uid,
|
||||
args.paymentMethodId,
|
||||
args.fullName
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,9 +49,6 @@ export class CheckoutCartWithStripeActionAttributionData {
|
||||
export class CheckoutCartWithStripeActionCustomerData {
|
||||
@IsString()
|
||||
locale!: string;
|
||||
|
||||
@IsString()
|
||||
displayName!: string;
|
||||
}
|
||||
|
||||
export class CheckoutCartWithStripeActionArgs {
|
||||
|
||||
@@ -10,7 +10,4 @@ export class SetDefaultStripePaymentDetailsActionArgs {
|
||||
|
||||
@IsString()
|
||||
paymentMethodId!: string;
|
||||
|
||||
@IsString()
|
||||
fullName!: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user