Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/react/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@godaddy/react",
"private": false,
"version": "1.0.58",
"version": "1.0.59",
"type": "module",
"types": "./dist/index.d.ts",
"files": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ const razorpayPaymentMethods = {
},
};

// Airo-style create: explicit card-only paymentMethods; discovery still fills
// paymentProviderConfiguration.razorpay (and may leave razorpay null / omitted).
const cardOnlyPaymentMethods = {
card: { processor: 'godaddy', checkoutTypes: ['standard'] },
razorpay: null,
};

describe('Razorpay configuration gating', () => {
it('hides Razorpay when paymentProviderConfiguration.razorpay is missing', async () => {
renderCheckout({
Expand Down Expand Up @@ -50,4 +57,34 @@ describe('Razorpay configuration gating', () => {
screen.getByRole('button', { name: /razorpay/i })
).toBeInTheDocument();
});

it('shows Razorpay when paymentMethods.razorpay is null but configured is true', async () => {
renderCheckout({
sessionOverrides: {
paymentMethods: cardOnlyPaymentMethods,
paymentProviderConfiguration: { razorpay: { configured: true } },
} as never,
});
await waitForCheckoutReady();

expect(
screen.getByRole('button', { name: /razorpay/i })
).toBeInTheDocument();
});

it('shows Razorpay when paymentMethods omits razorpay but configured is true', async () => {
renderCheckout({
sessionOverrides: {
paymentMethods: {
card: { processor: 'godaddy', checkoutTypes: ['standard'] },
},
paymentProviderConfiguration: { razorpay: { configured: true } },
} as never,
});
await waitForCheckoutReady();

expect(
screen.getByRole('button', { name: /razorpay/i })
).toBeInTheDocument();
});
});
65 changes: 45 additions & 20 deletions packages/react/src/components/checkout/payment/payment-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,14 @@ export function PaymentForm(
paymentMethod as PaymentMethodValue
);
// TEMP FOR TESTING — DO NOT COMMIT: mirrors the availablePaymentMethods
// bypass below. session.paymentMethods.paypal can be null even when PayPal
// is actually configured (only paymentProviderConfiguration.paypal gets
// resolved by discovery on a session created with explicit paymentMethods
// input). Without this, useGetSelectedPaymentMethod returns null for
// PayPal, so getCheckoutButton() bails out at `if (!methodConfig) return
// null` and the "Pay now" button never renders — even though PayPal is
// selectable in the accordion thanks to the bypass below.
// bypass below. session.paymentMethods.paypal / razorpay can be null even
// when the provider is actually configured (only
// paymentProviderConfiguration gets resolved by discovery on a session
// created with explicit paymentMethods input). Without this,
// useGetSelectedPaymentMethod returns null, so getCheckoutButton() bails
// out at `if (!methodConfig) return null` and the "Pay now" button never
// renders — even though the method is selectable in the accordion thanks
// to the bypass below.
const methodConfig =
rawMethodConfig ??
(paymentMethod === PaymentMethodType.PAYPAL &&
Expand All @@ -142,7 +143,14 @@ export function PaymentForm(
processor: PaymentProvider.PAYPAL,
checkoutTypes: [CheckoutType.STANDARD],
}
: null);
: paymentMethod === PaymentMethodType.RAZORPAY &&
razorpayConfig?.configured === true
? {
type: PaymentMethodType.RAZORPAY as PaymentMethodValue,
processor: PaymentProvider.RAZORPAY,
checkoutTypes: [CheckoutType.STANDARD],
}
: null);
const { isPoyntLoaded } = useLoadPoyntCollect();

const [pazeSupported, setPazeSupported] = useState<boolean | null>(null);
Expand Down Expand Up @@ -280,25 +288,40 @@ export function PaymentForm(

const availablePaymentMethods = React.useMemo(() => {
if (!configuredPaymentMethods) return [];
return Object.keys(configuredPaymentMethods).filter(key => {
// TEMP FOR TESTING — DO NOT COMMIT: when callers send explicit
// paymentMethods (e.g. card-only), discovery still fills
// paymentProviderConfiguration but leaves paymentMethods.paypal /
// razorpay null. GraphQL may omit those null keys entirely, so ensure
// the provider key is present whenever its public config exists.
const methodKeys = new Set(Object.keys(configuredPaymentMethods));
if (paypalConfig?.clientId?.trim()) {
methodKeys.add(PaymentMethodType.PAYPAL);
}
if (razorpayConfig?.configured === true) {
methodKeys.add(PaymentMethodType.RAZORPAY);
}
return [...methodKeys].filter(key => {
const method = configuredPaymentMethods[key as PaymentMethodValue];

// TEMP FOR TESTING — DO NOT COMMIT: session.paymentMethods.paypal can
// be null on a session created with explicit paymentMethods input
// (only paymentProviderConfiguration gets resolved by discovery in
// that case). Only treat a null paypal method as "standard" when real
// PayPal SDK config actually exists. (Razorpay's equivalent signal,
// paymentProviderConfiguration.razorpay.configured, is checked in the
// stable gating block below instead.)
// TEMP FOR TESTING — DO NOT COMMIT: session.paymentMethods.paypal /
// razorpay can be null on a session created with explicit
// paymentMethods input (only paymentProviderConfiguration gets
// resolved by discovery in that case). Treat a null method as
// "standard" when the provider's public config actually exists.
const isPayPalWithRealConfig =
key === PaymentMethodType.PAYPAL && !!paypalConfig?.clientId?.trim();
const isRazorpayWithRealConfig =
key === PaymentMethodType.RAZORPAY &&
razorpayConfig?.configured === true;
const hasProviderConfigBypass =
isPayPalWithRealConfig || isRazorpayWithRealConfig;
const effectiveCheckoutTypes =
method?.checkoutTypes ??
(isPayPalWithRealConfig ? [CheckoutType.STANDARD] : undefined);
(hasProviderConfigBypass ? [CheckoutType.STANDARD] : undefined);

const baseCheck =
PAYMENT_METHOD_ICONS[key as PaymentMethodValue] &&
(method || isPayPalWithRealConfig) &&
(method || hasProviderConfigBypass) &&
Array.isArray(effectiveCheckoutTypes) &&
effectiveCheckoutTypes.includes(CheckoutType.STANDARD);

Expand Down Expand Up @@ -330,10 +353,12 @@ export function PaymentForm(
}

// Razorpay requires checkout-api to have resolved a working merchant
// account before the button is offered.
// account before the button is offered. Mirror PayPal: allow through
// when paymentMethods.razorpay is null but
// paymentProviderConfiguration.razorpay.configured is true.
if (
key === PaymentMethodType.RAZORPAY &&
method?.processor === PaymentProvider.RAZORPAY
(method?.processor === PaymentProvider.RAZORPAY || !method)
) {
return baseCheck && razorpayConfig?.configured === true;
}
Expand Down