Skip to content

Conversation

@chedieck
Copy link
Collaborator

@chedieck chedieck commented Dec 4, 2025

Related to #

Description

Fixes issue where clicking the QR code or the button to pay too early would use the payment info without the paymentId

Test plan

Summary by CodeRabbit

  • Bug Fixes

    • Prevented QR code button from being clickable during loading to avoid unintended interactions
    • Corrected donation URL parameter handling for accurate donation amounts and tracking
  • Improvements

    • Made widget configuration properties more flexible by allowing optional settings
  • Chores

    • Updated package versions to 5.1.2

✏️ Tip: You can customize this high-level summary in your review settings.

Greptile Overview

Greptile Summary

Prevented early QR code copying and button clicks before the paymentId loads, ensuring payment URLs always include the complete payment information. Made success and disabled props optional for better API flexibility.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The fix correctly addresses the race condition by checking qrLoading state (which includes isWaitingForPaymentId) before allowing user interactions. The changes are minimal, focused, and consistent with existing patterns in the codebase. The props made optional have sensible defaults already in place.
  • No files require special attention

Important Files Changed

File Analysis

Filename Score Overview
react/lib/components/Widget/Widget.tsx 5/5 Made success and disabled props optional, added qrLoading check to prevent QR copy and button clicks before paymentId loads

Sequence Diagram

sequenceDiagram
    participant User
    participant Widget
    participant API
    participant QRCode
    participant Button

    Note over Widget: Component Mounts
    Widget->>Widget: Initialize (loading=true)
    
    alt isChild = false AND !disablePaymentId
        Widget->>API: createPayment(amount, address)
        Note over Widget: isWaitingForPaymentId = true
        Note over Widget: qrLoading = true
        API-->>Widget: paymentId
        Widget->>Widget: setPaymentId(paymentId)
        Note over Widget: isWaitingForPaymentId = false
    end
    
    Widget->>Widget: getAddressBalance()
    Widget->>Widget: setLoading(false)
    Note over Widget: qrLoading = false (if paymentId loaded)
    
    alt User clicks QR Code
        User->>QRCode: Click
        QRCode->>Widget: handleQrCodeClick()
        alt qrLoading = true
            Widget-->>User: No action (prevented)
            Note over Widget: FIX: Prevents copying incomplete URL
        else qrLoading = false
            Widget->>Widget: copyToClipboard(url with paymentId)
            Widget-->>User: "Payment copied!"
        end
    end
    
    alt User clicks Button
        User->>Button: Click
        Button->>Widget: handleButtonClick()
        alt disabled OR qrLoading = true
            Widget-->>User: Button disabled (no action)
            Note over Widget: FIX: Prevents navigation with incomplete URL
        else qrLoading = false
            Widget->>Widget: Navigate to url with paymentId
        end
    end
Loading

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 4, 2025

Walkthrough

Version bumped to 5.1.2 across package.json files. Widget component props success and disabled made optional. QrLoading state now integrated into button disable logic and click handler. Donation URL parameter logic updated for conditional parameter appending based on donation applicability.

Changes

Cohort / File(s) Change Summary
Package version bumps
package.json, paybutton/package.json, react/package.json
Version incremented from 5.1.1 to 5.1.2 across root and workspace packages
Widget component enhancements
react/lib/components/Widget/Widget.tsx
WidgetProps success and disabled properties made optional; handleQrCodeClick now short-circuits when qrLoading is true; button rendering disables on qrLoading; donation URL resolution now conditionally appends donation parameters (addr and amount) when applicable

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

  • Package version bumps are cosmetic and trivial
  • Widget component changes are straightforward conditional logic additions and prop signature updates
  • No complex interdependencies or branching logic requiring deep analysis

Possibly related PRs

Suggested labels

bug

Poem

🐰 A patch hops by, version five-point-two,
Props now optional, the component's renewed,
QR loading checks keep buttons in line,
Donations dance conditional—everything's fine! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description includes the main issue being fixed but lacks essential details like the 'Related to #' issue reference and a concrete test plan. Complete the 'Related to #' reference with the issue number and provide a detailed test plan describing how to verify the fix (e.g., reproduction steps, expected behavior).
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main fix: preventing early clicks on QR code/pay button, which aligns with the code changes that add checks for qrLoading state.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/wait-for-qr-code-to-copy-and-pay

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

4 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

Copy link
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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
react/lib/components/Widget/Widget.tsx (1)

949-955: Add qrLoading to the handleQrCodeClick dependency array

The callback reads qrLoading but doesn't include it in the dependency array [disabled, to, url, setCopied, setRecentlyCopied]. This creates a stale closure where the callback retains an outdated qrLoading value from when it was memoized. While the component will eventually re-render and recreate the callback, the callback won't immediately respond to qrLoading state changes within that render cycle, potentially causing unexpected behavior when loading state flips to false.

Add qrLoading to the dependencies:

-  const handleQrCodeClick = useCallback((): void => {
-    if (disabled || to === undefined || qrLoading) return
-    if (!url || !copyToClipboard(url)) return
-    setCopied(true)
-    setRecentlyCopied(true)
-  }, [disabled, to, url, setCopied, setRecentlyCopied])
+  const handleQrCodeClick = useCallback((): void => {
+    if (disabled || to === undefined || qrLoading) return
+    if (!url || !copyToClipboard(url)) return
+    setCopied(true)
+    setRecentlyCopied(true)
+  }, [disabled, to, url, qrLoading, setCopied, setRecentlyCopied])

Re-run tests to confirm QR behavior remains correct after this change.

🧹 Nitpick comments (2)
react/lib/components/Widget/Widget.tsx (2)

182-190: qrLoading integration generally solves the “too early click” issue for children

Introducing isWaitingForPaymentId and combining it with loading into qrLoading to gate:

  • QR visibility/animations,
  • the hint text (“Loading...” vs “Not yet ready for payment”),
  • and the main button’s disabled state (disabled || qrLoading)

makes it much harder for users to click before the widget is actually ready. For child widgets that rely on an externally supplied paymentId, this looks aligned with the PR objective.

One thing to verify: non‑child flows still allow clicking while an internally generated paymentId is being created. If that was also part of the original bug, you may want to reuse isWaitingForPaymentId or a similar flag on the non‑child path as well.

Also applies to: 261-270, 351-383, 656-680, 1263-1273


731-749: Donation visibility and multi-output URL construction look coherent, but consider small clarifications

The new helpers:

  • shouldShowDonationUI(amount, currencyType)
  • shouldApplyDonation(amount, currencyType)

and the updated donation URL logic in resolveUrl:

  • Only apply donation when donationEnabled and userDonationRate > 0.
  • Gate UI and URL behavior on DEFAULT_MINIMUM_DONATION_AMOUNT thresholds.
  • Use a base amount for the main address and an additional addr/amount pair for the donation output.

are logically consistent with a multi‑output payment format (base ?amount=... for the primary address, followed by &addr=<donationAddress>&amount=<donationAmount> for the donation).

Two small suggestions to improve clarity and reduce future mistakes:

  1. Make the multi-output semantics explicit in code comments

    The current code assumes consumers understand repeating addr/amount is intentional. A brief comment where you append the donation params would help future maintainers.

  2. Avoid duplicating the ?amount=${amount} string literal

    Both branches (if with donation and else without) build ?amount=${amount}. Extracting the common part (or building a params array first) would make it less error‑prone if you ever tweak the parameter name or behavior.

These are readability/maintainability concerns; behavior looks correct.

Also applies to: 761-833, 956-989, 1314-1405

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3356668 and 1d46dbe.

📒 Files selected for processing (4)
  • package.json (1 hunks)
  • paybutton/package.json (1 hunks)
  • react/lib/components/Widget/Widget.tsx (3 hunks)
  • react/package.json (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: Run tests
react/lib/components/Widget/Widget.tsx

[error] 1-1: Command failed: yarn test (in react directory).

react/package.json

[error] 1-1: Command failed: yarn test (in react directory).

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run build
🔇 Additional comments (6)
paybutton/package.json (1)

3-3: Version bump metadata looks consistent

Patch version updated to 5.1.2; this keeps the paybutton package in sync with the root/react packages and matches the bugfix nature of this PR.

package.json (1)

3-3: Root package version bump is appropriate

Root package.json is bumped to 5.1.2, matching sub-packages; good for a coordinated bugfix release.

react/package.json (1)

3-3: React package version bump is fine; fix failing tests before merge

The 5.1.2 version bump here is consistent with the rest of the workspace. However, CI indicates yarn test is currently failing in the react package, likely due to the Widget changes (types or behavior/snapshots). Please run cd react && yarn test, address any failures, and commit updated tests/snapshots before merging.

react/lib/components/Widget/Widget.tsx (3)

57-75: Making success/disabled optional in WidgetProps with sensible defaults

Changing success and disabled to optional in WidgetProps and defaulting success to false in the destructuring keeps the runtime behavior predictable while relaxing the public API. Internal disabled state still governs the widget, so omitting the prop is safe.

Please double‑check downstream TS usage/tests for any assumptions that these props are required (e.g., component instantiations in tests or storybook configs) and update them if necessary.

Also applies to: 128-178


314-323: PaymentId initialization effect is robust and avoids redundant API calls

The new useEffect that derives an effectiveAmount (from converted crypto, convertedCurrencyObj, or thisAmount), skips invalid/NaN values, memoizes the last effective amount via lastEffectiveAmountRef, and calls createPayment only when needed is a solid improvement:

  • It avoids hammering the API on every render/amount tweak.
  • It correctly waits for fiat conversion (isFiat(currency) && convertedCryptoAmount === undefined).
  • It no‑ops for children or when disablePaymentId / setPaymentId / to conditions make payment IDs irrelevant.

This logic looks correct and nicely contained.

Also applies to: 588-653


1188-1258: NumericFormat usage for editable amount looks sound

The NumericFormat configuration for editing the amount:

  • Tracks raw numeric values.value into draftAmount.
  • Applies sensible constraints in isAllowed (non‑negative, below MAX_AMOUNT[...], and within decimal precision from DECIMALS[...]).
  • Prevents redundant confirmation when the draft already matches the current amount (isSameAmount).
  • Integrates with applyDraftAmount to update the canonical amount and conversion state.

This is a good use of react-number-format for constrained numeric input.

@Klakurka Klakurka self-requested a review December 5, 2025 19:03
@Klakurka Klakurka added the bug Something isn't working label Dec 5, 2025
@Klakurka
Copy link
Member

Klakurka commented Dec 5, 2025

We'll go with 5.2.0 but I can do that.

@Klakurka Klakurka merged commit b7614f4 into master Dec 5, 2025
3 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Dec 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants