Skip to content

fix(billing): add missing ThreeDSecurePopup to PaymentPopup#3028

Merged
baktun14 merged 4 commits intomainfrom
fix/billing-3d-secure-popup
Mar 31, 2026
Merged

fix(billing): add missing ThreeDSecurePopup to PaymentPopup#3028
baktun14 merged 4 commits intomainfrom
fix/billing-3d-secure-popup

Conversation

@baktun14
Copy link
Copy Markdown
Contributor

@baktun14 baktun14 commented Mar 31, 2026

Why

Fixes CON-162

When the /payment page was removed in favor of /billing (#2846), the ThreeDSecurePopup component was not carried over to the new PaymentPopup. The use3DSecure hook was wired up and start3DSecure() is called correctly, but the popup that performs the Stripe 3D Secure authentication never rendered — causing silent payment failures for cards requiring 3DS verification (e.g., certain EU cards).

What

  • Import and render ThreeDSecurePopup in PaymentPopup component
  • Add it to DEPENDENCIES for testability
  • Add two tests verifying the popup renders when 3DS data is present and does not render when absent
  • Expand the use3DSecure mock in tests to return the full hook interface

Summary by CodeRabbit

  • New Features

    • Added a 3D Secure authentication flow to the payment popup; when 3DS is active the main payment UI is hidden and a dedicated 3DS dialog appears with success/error handling.
    • Payment intents now explicitly include additional payment method types (e.g., card and link).
  • Behavioral Change

    • Updated 3DS confirmation to support conditional redirects and an explicit return URL during authentication.
  • Tests

    • Added rendering tests verifying 3DS dialog visibility and that the main payment popup is hidden during 3DS.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 31, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0857ca44-d066-4199-b38f-ced6b7d55310

📥 Commits

Reviewing files that changed from the base of the PR and between e76453b and 1db891f.

📒 Files selected for processing (1)
  • apps/deploy-web/src/components/shared/PaymentMethodForm/ThreeDSecureModal.tsx

📝 Walkthrough

Walkthrough

PaymentPopup now integrates a 3D Secure flow: it renders a ThreeDSecurePopup when threeDSecure.threeDSData exists, hides the main popup while 3DS is open, wires 3DS success/error handlers, and updates tests/mocks to cover the new UI states.

Changes

Cohort / File(s) Summary
PaymentPopup Component
apps/deploy-web/src/components/billing-usage/PaymentPopup/PaymentPopup.tsx
Added ThreeDSecurePopup to DEPENDENCIES; wrapped return in a fragment; main popup open changed to open && !threeDSecure.isOpen; renders d.ThreeDSecurePopup when threeDSecure.threeDSData?.clientSecret exists; wired handle3DSSuccess/handle3DSError and passed clientSecret, paymentIntentId, paymentMethodId, plus static title/description/success/error messages.
PaymentPopup Tests & Mocks
apps/deploy-web/src/components/billing-usage/PaymentPopup/PaymentPopup.spec.tsx
Extended setup() with threeDSecureIsOpen and threeDSData; mocked use3DSecure to return isOpen, threeDSData, handle3DSSuccess, handle3DSError, isLoading, and start3DSecure; added MockThreeDSecurePopup rendering data-testid="three-d-secure-popup"; added tests asserting 3DS popup visibility and that main popup is hidden during 3DS.
Stripe Service & Tests
apps/api/src/billing/services/stripe/stripe.service.ts, apps/api/src/billing/services/stripe/stripe.service.spec.ts
Changed PaymentIntent creation to pass payment_method_types: ["card", "link"] instead of automatic_payment_methods configuration; updated unit test expectation to match new parameters.
ThreeDSecure Modal
apps/deploy-web/src/components/shared/PaymentMethodForm/ThreeDSecureModal.tsx
Replaced stripe.confirmCardPayment(clientSecret, ...) with stripe.confirmPayment({ clientSecret, confirmParams: { payment_method: ..., return_url: window.location.href }, redirect: "if_required" }); retained result handling and error flow.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant Popup as PaymentPopup
    participant Hook as use3DSecure
    participant ThreeDS as ThreeDSecurePopup

    User->>Popup: open payment popup
    Popup->>Hook: start or query 3DS state
    Hook-->>Popup: { isOpen, threeDSData (clientSecret...), handle3DSSuccess, handle3DSError }
    alt threeDSecure is open with data
        Popup->>Popup: set main popup open = open && !isOpen
        Popup->>ThreeDS: render with clientSecret, paymentIntentId, paymentMethodId, handlers
        ThreeDS->>Hook: call onSuccess()/onError()
        Hook-->>Popup: invoke handle3DSSuccess / handle3DSError
    else not open
        Popup->>User: show main payment UI
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hop by the payment gate tonight,
A secret key tucked snug and tight,
When 3DS rises, the main view hides,
I test and munch through edge-case tides,
Cheers — a carrot for each passing flight. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the primary change: adding the missing ThreeDSecurePopup component to PaymentPopup.
Description check ✅ Passed The description provides clear context (issue reference CON-162), explains the problem, and lists all changes made including component imports, tests, and mock updates.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/billing-3d-secure-popup

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 31, 2026

Codecov Report

❌ Patch coverage is 92.30769% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 58.90%. Comparing base (62b82ac) to head (1db891f).
⚠️ Report is 2 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...onents/billing-usage/PaymentPopup/PaymentPopup.tsx 96.00% 1 Missing ⚠️
...nts/shared/PaymentMethodForm/ThreeDSecureModal.tsx 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3028      +/-   ##
==========================================
- Coverage   59.69%   58.90%   -0.80%     
==========================================
  Files        1032      994      -38     
  Lines       24231    23337     -894     
  Branches     6009     5876     -133     
==========================================
- Hits        14465    13746     -719     
+ Misses       8518     8355     -163     
+ Partials     1248     1236      -12     
Flag Coverage Δ *Carryforward flag
api 81.27% <ø> (+<0.01%) ⬆️
deploy-web 43.33% <92.30%> (+0.04%) ⬆️
log-collector ?
notifications 86.06% <ø> (ø) Carriedforward from e76453b
provider-console 81.48% <ø> (ø) Carriedforward from e76453b
provider-proxy 85.93% <ø> (ø) Carriedforward from e76453b
tx-signer ?

*This pull request uses carry forward flags. Click here to find out more.

Files with missing lines Coverage Δ
.../api/src/billing/services/stripe/stripe.service.ts 73.84% <ø> (ø)
...onents/billing-usage/PaymentPopup/PaymentPopup.tsx 97.11% <96.00%> (+0.08%) ⬆️
...nts/shared/PaymentMethodForm/ThreeDSecureModal.tsx 4.34% <0.00%> (+4.34%) ⬆️

... and 40 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/deploy-web/src/components/billing-usage/PaymentPopup/PaymentPopup.spec.tsx`:
- Around line 616-621: The test "does not render ThreeDSecurePopup when 3DS data
is null" currently passes threeDSecureIsOpen: false which allows the popup to be
absent for the wrong reason; update the setup call in that spec to pass
threeDSecureIsOpen: true while keeping threeDSData: null so the test isolates
the null-data branch and ensures ThreeDSecurePopup is not rendered when
threeDSData is null (look for the it(...) block and the setup({ open:,
threeDSecureIsOpen:, threeDSData: }) invocation).
- Around line 120-123: The MockThreeDSecurePopup component currently types its
props as any; replace that with an explicit props shape (e.g., an interface or
type) that declares isOpen: boolean and clientSecret: string and use it for the
component signature (MockThreeDSecurePopup) so the TSX test file no longer uses
any for props and correctly types the mock's inputs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3361572d-e6e9-4b18-bfdf-ada5f02c7d97

📥 Commits

Reviewing files that changed from the base of the PR and between 62b82ac and 8f1a426.

📒 Files selected for processing (2)
  • apps/deploy-web/src/components/billing-usage/PaymentPopup/PaymentPopup.spec.tsx
  • apps/deploy-web/src/components/billing-usage/PaymentPopup/PaymentPopup.tsx

Comment thread apps/deploy-web/src/components/billing-usage/PaymentPopup/PaymentPopup.spec.tsx Outdated
The ThreeDSecurePopup component was not carried over when the /payment
page was removed in favor of /billing. The use3DSecure hook was wired up
but the popup never rendered, causing silent payment failures for cards
requiring 3D Secure verification.

Fixes CON-162
@baktun14 baktun14 force-pushed the fix/billing-3d-secure-popup branch from 8f1a426 to a1cac9a Compare March 31, 2026 18:50
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
apps/deploy-web/src/components/billing-usage/PaymentPopup/PaymentPopup.spec.tsx (2)

616-624: ⚠️ Potential issue | 🟡 Minor

Isolate the null-data branch in the negative render test.

Line 619 sets threeDSecureIsOpen: false, so the popup can be absent even if data exists. Set it to true so this test specifically validates the threeDSData: null path.

Suggested fix
     it("does not render ThreeDSecurePopup when 3DS data is null", () => {
       setup({
         open: true,
-        threeDSecureIsOpen: false,
+        threeDSecureIsOpen: true,
         threeDSData: null
       });

       expect(screen.queryByTestId("three-d-secure-popup")).not.toBeInTheDocument();
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/deploy-web/src/components/billing-usage/PaymentPopup/PaymentPopup.spec.tsx`
around lines 616 - 624, The test "does not render ThreeDSecurePopup when 3DS
data is null" currently sets threeDSecureIsOpen: false which lets the popup be
absent for the wrong reason; update the call to setup in PaymentPopup.spec.tsx
so threeDSecureIsOpen is true while threeDSData is null (i.e., call setup({
open: true, threeDSecureIsOpen: true, threeDSData: null })) to isolate and
assert the null-data branch for the ThreeDSecurePopup render behavior.

120-123: ⚠️ Potential issue | 🟠 Major

Replace any props on MockThreeDSecurePopup with explicit types.

Line 120 still uses any for props in a TSX file; please type isOpen and clientSecret explicitly.

Suggested fix
+type MockThreeDSecurePopupProps = {
+  isOpen: boolean;
+  clientSecret: string;
+};
+
-const MockThreeDSecurePopup = ({ isOpen, clientSecret }: any) => {
+const MockThreeDSecurePopup = ({ isOpen, clientSecret }: MockThreeDSecurePopupProps) => {
   if (!isOpen) return null;
   return <div data-testid="three-d-secure-popup" data-client-secret={clientSecret} />;
 };

As per coding guidelines: **/*.{ts,tsx,js}: Never use type any or cast to type any. Always define the proper TypeScript types.

#!/bin/bash
# Verify explicit any usage remains in this spec file
rg -nP --type=tsx '\b:\s*any\b|\bas\s+any\b' apps/deploy-web/src/components/billing-usage/PaymentPopup/PaymentPopup.spec.tsx
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/deploy-web/src/components/billing-usage/PaymentPopup/PaymentPopup.spec.tsx`
around lines 120 - 123, MockThreeDSecurePopup currently types its props as any;
replace that with explicit types by defining the props shape (e.g., interface or
inline type) and using it on the component: type the isOpen prop as boolean and
clientSecret as string | undefined (or string | null if tests pass null), then
update the function signature for MockThreeDSecurePopup to use that type instead
of any (e.g., MockThreeDSecurePopup = ({ isOpen, clientSecret }: { isOpen:
boolean; clientSecret?: string }) => ...). Ensure the typed names match the
existing identifiers so tests using data-testid="three-d-secure-popup" and
data-client-secret continue to work.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@apps/deploy-web/src/components/billing-usage/PaymentPopup/PaymentPopup.spec.tsx`:
- Around line 616-624: The test "does not render ThreeDSecurePopup when 3DS data
is null" currently sets threeDSecureIsOpen: false which lets the popup be absent
for the wrong reason; update the call to setup in PaymentPopup.spec.tsx so
threeDSecureIsOpen is true while threeDSData is null (i.e., call setup({ open:
true, threeDSecureIsOpen: true, threeDSData: null })) to isolate and assert the
null-data branch for the ThreeDSecurePopup render behavior.
- Around line 120-123: MockThreeDSecurePopup currently types its props as any;
replace that with explicit types by defining the props shape (e.g., interface or
inline type) and using it on the component: type the isOpen prop as boolean and
clientSecret as string | undefined (or string | null if tests pass null), then
update the function signature for MockThreeDSecurePopup to use that type instead
of any (e.g., MockThreeDSecurePopup = ({ isOpen, clientSecret }: { isOpen:
boolean; clientSecret?: string }) => ...). Ensure the typed names match the
existing identifiers so tests using data-testid="three-d-secure-popup" and
data-client-secret continue to work.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bc02fd60-f9fa-4078-a3ba-f6e3cf450727

📥 Commits

Reviewing files that changed from the base of the PR and between 8f1a426 and a1cac9a.

📒 Files selected for processing (2)
  • apps/deploy-web/src/components/billing-usage/PaymentPopup/PaymentPopup.spec.tsx
  • apps/deploy-web/src/components/billing-usage/PaymentPopup/PaymentPopup.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/deploy-web/src/components/billing-usage/PaymentPopup/PaymentPopup.tsx

Comment thread apps/deploy-web/src/components/billing-usage/PaymentPopup/PaymentPopup.spec.tsx Outdated
- Type MockThreeDSecurePopup props explicitly instead of `any`
- Move MockThreeDSecurePopup to bottom of file as non-essential detail
- Set threeDSecureIsOpen: true in null-data test to isolate the correct branch
Replace automatic_payment_methods with payment_method_types: ["card", "link"]
to match createSetupIntent and createTestCharge. The automatic_payment_methods
option with allow_redirects: "never" rejected link-type payment methods.
Replace confirmCardPayment with confirmPayment in ThreeDSecureModal so
both card and link payment methods can complete 3DS authentication.
The card-specific Stripe.js method rejected link-type PMs with
"does not match the expected type card".

Also restore payment_method_types: ["card", "link"] in createPaymentIntent
to match createSetupIntent and createTestCharge.
@baktun14 baktun14 added this pull request to the merge queue Mar 31, 2026
Merged via the queue into main with commit 3e73933 Mar 31, 2026
53 of 54 checks passed
@baktun14 baktun14 deleted the fix/billing-3d-secure-popup branch March 31, 2026 23:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants