Skip to content

Conversation

@Yash094
Copy link
Member

@Yash094 Yash094 commented Dec 22, 2025

Summary by CodeRabbit

  • New Features

    • Switched to a hosted iframe checkout widget with in-page messaging and a refreshed UI (taller frame, success banner).
  • Improvements

    • Simplified setup: no Client ID/account required; wallet-first onboarding and network selection.
    • Theme now defaults to dark and is always applied to the widget.
    • Token selection: leave empty to accept any stablecoin; optional token parameter respected.
    • Payment flow now relies on the hosted widget for confirmation and simplified validation.

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

@coderabbitai
Copy link

coderabbitai bot commented Dec 22, 2025

Walkthrough

Replaces the client-side CheckoutWidget and Client ID flow with a hosted iframe checkout; removes clientId usage across admin/frontend/backend, defaults theme to dark, updates frontend block to use postMessage with the iframe, removes RPC-based on-chain verification in favor of widget-confirmed flows, and removes the thirdweb dependency from package.json while adding .pnpm-store/ to .gitignore.

Changes

Cohort / File(s) Summary
Gitignore & Package
\.gitignore, package.json
Added .pnpm-store/ to .gitignore. Removed the thirdweb entry from package.json dependencies (now empty).
Blocks support helper
includes/class-thirdweb-blocks-support.php
Removed Client ID from payment payload; changed default theme retrieval to get_setting('theme', 'dark'); payload/comment updates to reflect iframe widget (no Client ID).
Payment Gateway Backend
includes/class-thirdweb-payment-gateway.php
Removed client_id handling and admin field; payment_fields now renders an iframe URL with chain, amount, seller and optional tokenAddress (always includes theme); process_payment refactored to accept Blocks payload and complete via widget-confirmed flow (no tx_hash reliance); verify_transaction/RPC-based verification removed.
Checkout Block UI
src/checkout-block/ThirdwebCheckout.tsx, src/checkout-block/index.tsx
Replaced embedded CheckoutWidget + ThirdwebProvider with a hosted iframe; builds iframe URL from chain, amount, seller and optional tokenAddress; adds postMessage listener for success/error and verifies origin/source; removed settings.clientId, changed theme typing; canMakePayment no longer requires clientId.
Admin Onboarding & Docs
thirdweb-woocommerce-checkout.php
Updated admin notice/onboarding: removed requirement for thirdweb account and Client ID; instructs admins to use a wallet address and preferred network; adds “No thirdweb account needed!” messaging.

Sequence Diagram

sequenceDiagram
    actor User
    participant Frontend as Checkout Block (WP)
    participant Iframe as Hosted Checkout (iframe)
    participant Backend as WooCommerce Gateway
    participant RPC as Blockchain RPC

    User->>Frontend: Start checkout
    Frontend->>Iframe: Load iframe URL (chain, amount, seller[, tokenAddress, theme])
    Iframe->>User: Prompt wallet interaction / submit tx
    Iframe->>Frontend: postMessage({type: "success", ...}) or postMessage({type: "error", message})
    Frontend->>Frontend: Validate message origin & source
    alt success with txHash
        Frontend->>Backend: process_payment (txHash, chainId, metadata)
        Backend->>RPC: Query tx status for chain
        RPC-->>Backend: TX confirmed / pending / error
        Backend-->>Frontend: Payment result
        Frontend->>User: Show success banner / redirect
    else success without txHash (widget-confirmed)
        Frontend->>Backend: process_payment (Blocks payload, no txHash)
        Backend-->>Frontend: Acknowledge completion
        Frontend->>User: Show success banner / redirect
    end
    alt error
        Frontend->>User: Display error message
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'use checkout widget' directly describes the main change: replacing the embedded CheckoutWidget with a hosted checkout widget via iframe across the codebase.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ 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 yash/use-checkout-widget

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

Copy link

@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

🧹 Nitpick comments (5)
src/checkout-block/index.tsx (1)

81-84: Consider validating seller wallet address format.

The current check only verifies that seller and chainId are truthy. You may want to validate that seller is a valid Ethereum address to prevent the widget from loading with an invalid configuration.

🔎 Proposed enhancement
 const canMakePayment = () => {
     // Verify required settings are configured (no Client ID needed for iframe widget)
-    return !!(settings.seller && settings.chainId);
+    const isValidAddress = /^0x[a-fA-F0-9]{40}$/.test(settings.seller);
+    return !!(isValidAddress && settings.chainId);
 };
src/checkout-block/ThirdwebCheckout.tsx (4)

71-104: Strengthen postMessage validation by checking event.source.

The current validation checks event.origin and event.data.source, which is good. However, for stronger security, also verify that event.source matches the iframe's contentWindow. This prevents other iframes or windows from thirdweb.com from spoofing messages.

🔎 Proposed enhancement
     useEffect(() => {
         const handleMessage = (event: MessageEvent) => {
             // Verify that message is from thirdweb checkout widget iframe
             if (
                 event.origin === 'https://thirdweb.com' &&
-                event.data.source === 'checkout-widget'
+                event.data.source === 'checkout-widget' &&
+                iframeRef.current?.contentWindow === event.source
             ) {

79-79: Remove console.log from production code.

Debug logging should be removed or replaced with a conditional debug flag for production builds.

🔎 Proposed fix
                 if (event.data.type === 'success') {
-                    console.log('Purchase successful!', event.data);
-                    
                     // Extract transaction hash if available

92-92: Remove console.error from production code or use structured error handling.

Consider removing the console statement or using a debug utility that can be disabled in production.

🔎 Proposed fix
                 if (event.data.type === 'error') {
-                    console.error('Purchase failed with error:', event.data.message);
                     setError(event.data.message || 'Payment failed. Please try again.');

51-66: Consider memoizing buildIframeUrl to prevent unnecessary recalculations.

Currently buildIframeUrl() is called on every render. While not a significant performance issue, memoizing it with useMemo would be more idiomatic.

🔎 Proposed enhancement
-    // Build iframe URL
-    const buildIframeUrl = () => {
+    // Build iframe URL (memoized to prevent unnecessary recalculations)
+    const iframeUrl = React.useMemo(() => {
         const baseUrl = 'https://thirdweb.com/bridge/checkout-widget';
         const params = new URLSearchParams({
             chain: settings.chainId.toString(),
             amount: amount,
             seller: settings.seller,
         });
 
         // Add token address if provided
         if (settings.tokenAddress && settings.tokenAddress.startsWith('0x')) {
             params.append('tokenAddress', settings.tokenAddress);
         }
 
         return `${baseUrl}?${params.toString()}`;
-    };
+    }, [settings.chainId, amount, settings.seller, settings.tokenAddress]);

Then update the usage on line 153:

-                    src={buildIframeUrl()}
+                    src={iframeUrl}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4cda909 and 47f95a5.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (7)
  • .gitignore
  • includes/class-thirdweb-blocks-support.php
  • includes/class-thirdweb-payment-gateway.php
  • package.json
  • src/checkout-block/ThirdwebCheckout.tsx
  • src/checkout-block/index.tsx
  • thirdweb-woocommerce-checkout.php
🧰 Additional context used
📓 Path-based instructions (7)
**/checkout-block/index.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use @woocommerce/blocks-registry registerPaymentMethod() API to register payment methods in checkout blocks

Files:

  • src/checkout-block/index.tsx
**/checkout-block/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use getSetting('thirdweb_stablecoin_data') to retrieve PHP configuration in React components

Files:

  • src/checkout-block/index.tsx
  • src/checkout-block/ThirdwebCheckout.tsx
**/*.php

📄 CodeRabbit inference engine (CLAUDE.md)

Use PHP 7.4+ for WordPress plugin backend code

Files:

  • includes/class-thirdweb-blocks-support.php
  • thirdweb-woocommerce-checkout.php
  • includes/class-thirdweb-payment-gateway.php
**/class-thirdweb-blocks-support.php

📄 CodeRabbit inference engine (CLAUDE.md)

Enable React-based checkout blocks support via class-thirdweb-blocks-support.php

Files:

  • includes/class-thirdweb-blocks-support.php
**/ThirdwebCheckout.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

**/ThirdwebCheckout.tsx: Wrap thirdweb CheckoutWidget with WooCommerce integration in ThirdwebCheckout component
Capture transaction hash from CheckoutWidget's onSuccess callback and pass it to WooCommerce

Files:

  • src/checkout-block/ThirdwebCheckout.tsx
thirdweb-woocommerce-checkout.php

📄 CodeRabbit inference engine (CLAUDE.md)

Register webhook endpoint in main plugin entry file for payment confirmation from thirdweb

Files:

  • thirdweb-woocommerce-checkout.php
**/class-thirdweb-payment-gateway.php

📄 CodeRabbit inference engine (CLAUDE.md)

**/class-thirdweb-payment-gateway.php: Extend WC_Payment_Gateway class for payment gateway implementation
Perform transaction verification server-side via RPC for security, not client-side

Files:

  • includes/class-thirdweb-payment-gateway.php
🔇 Additional comments (6)
.gitignore (1)

4-4: LGTM!

Adding .pnpm-store/ is appropriate for projects using pnpm as the package manager.

package.json (1)

23-23: LGTM!

Removal of the thirdweb dependency aligns with the migration to an iframe-based hosted widget. The empty dependencies object is valid, though you could optionally remove the key entirely if no runtime dependencies are needed.

includes/class-thirdweb-blocks-support.php (1)

81-98: LGTM!

The get_payment_method_data() method correctly provides the essential configuration for the iframe-based widget. The explicit (int) cast for chainId ensures type consistency with the frontend.

thirdweb-woocommerce-checkout.php (1)

154-167: LGTM!

The updated onboarding instructions clearly communicate the simplified setup process with the wallet-centric flow. Proper escaping with esc_url() is maintained.

includes/class-thirdweb-payment-gateway.php (1)

109-126: LGTM - Legacy checkout iframe implementation.

The iframe URL construction properly uses http_build_query() for parameter encoding and esc_url() for XSS prevention. The conditional tokenAddress inclusion is correctly handled.

src/checkout-block/ThirdwebCheckout.tsx (1)

140-160: LGTM - Iframe implementation with proper permissions.

The iframe correctly uses allow="payment" for the Payment Request API, and the structure cleanly separates the widget, status messages, and supported tokens display.

Comment on lines 206 to 210
if (is_wp_error($response)) {
return false;
// If RPC fails, still allow the order (transaction hash is recorded)
// Admin can manually verify on block explorer
return true;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Security concern: Returning true on RPC failure allows unverified transactions.

When the RPC call fails, the function returns true, allowing orders to complete without verifying the payment actually occurred. This could allow orders to process without payment if the RPC endpoint is unreachable, rate-limited, or experiences issues.

Consider returning false and letting the order stay in a pending state for manual verification, or implementing a retry mechanism.

🔎 Proposed fix
         if (is_wp_error($response)) {
-            // If RPC fails, still allow the order (transaction hash is recorded)
-            // Admin can manually verify on block explorer
-            return true;
+            // If RPC fails, keep order pending for manual verification
+            // Log the error for admin awareness
+            error_log('thirdweb-wc: RPC verification failed for tx ' . $tx_hash . ': ' . $response->get_error_message());
+            return false;
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (is_wp_error($response)) {
return false;
// If RPC fails, still allow the order (transaction hash is recorded)
// Admin can manually verify on block explorer
return true;
}
if (is_wp_error($response)) {
// If RPC fails, keep order pending for manual verification
// Log the error for admin awareness
error_log('thirdweb-wc: RPC verification failed for tx ' . $tx_hash . ': ' . $response->get_error_message());
return false;
}
🤖 Prompt for AI Agents
In includes/class-thirdweb-payment-gateway.php around lines 206-210 the code
currently returns true when the RPC response is a WP_Error, which permits orders
to complete without verified on-chain payment; change the behavior to return
false so the order remains unconfirmed, and implement a simple retry loop (e.g.,
2-3 attempts with short backoff) when the RPC call fails, log the full error
details on each failure, and if retries exhaust set the order to a pending/held
state and trigger an admin notification for manual verification.

Copy link

@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: 1

♻️ Duplicate comments (2)
includes/class-thirdweb-payment-gateway.php (2)

214-225: Unused $order parameter and potential type mismatch on chain lookup.

  1. The $order parameter is never used - remove it or use it for logging/context.
  2. The chain_id lookup risk from the previous review still applies - cast to string for safety.
🔎 Proposed fix
-    private function verify_transaction($tx_hash, $order) {
+    private function verify_transaction($tx_hash) {
         // Use public RPC endpoint to verify the transaction
         // No Client ID needed - using public RPC endpoints
         $rpc_endpoints = [
             '1'      => 'https://eth.llamarpc.com', // Ethereum
             '8453'   => 'https://base.llamarpc.com', // Base
             '137'    => 'https://polygon.llamarpc.com', // Polygon
             '42161'  => 'https://arbitrum.llamarpc.com', // Arbitrum
             '10'     => 'https://optimism.llamarpc.com', // Optimism
         ];

-        $rpc_url = $rpc_endpoints[$this->chain_id] ?? 'https://eth.llamarpc.com';
+        $rpc_url = $rpc_endpoints[(string) $this->chain_id] ?? 'https://eth.llamarpc.com';

238-241: Security: Returning true on RPC failure allows unverified orders to complete.

This concern was raised in a previous review and remains valid. When RPC calls fail (network issues, rate limiting), returning true allows orders to complete without on-chain verification. Combined with the unused $verified result, this means orders are never actually blocked by failed verification.

Consider returning false and letting orders remain pending for manual review when verification cannot be completed.

🧹 Nitpick comments (2)
src/checkout-block/ThirdwebCheckout.tsx (2)

44-44: iframeRef is assigned but never used.

The ref is attached to the iframe but no code accesses iframeRef.current. If there's no planned use for it, consider removing to reduce code clutter.

🔎 Proposed fix
-import React, { useEffect, useCallback, useState, useRef } from 'react';
+import React, { useEffect, useState } from 'react';
...
-    const iframeRef = useRef<HTMLIFrameElement>(null);
...
                 <iframe
-                    ref={iframeRef}
                     src={buildIframeUrl()}

Also applies to: 174-176


7-7: Unused import: useCallback

useCallback is imported but never used in the component.

🔎 Proposed fix
-import React, { useEffect, useCallback, useState, useRef } from 'react';
+import React, { useEffect, useState, useRef } from 'react';
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 47f95a5 and 05b902f.

📒 Files selected for processing (2)
  • includes/class-thirdweb-payment-gateway.php
  • src/checkout-block/ThirdwebCheckout.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.php

📄 CodeRabbit inference engine (CLAUDE.md)

Use PHP 7.4+ for WordPress plugin backend code

Files:

  • includes/class-thirdweb-payment-gateway.php
**/class-thirdweb-payment-gateway.php

📄 CodeRabbit inference engine (CLAUDE.md)

**/class-thirdweb-payment-gateway.php: Extend WC_Payment_Gateway class for payment gateway implementation
Perform transaction verification server-side via RPC for security, not client-side

Files:

  • includes/class-thirdweb-payment-gateway.php
**/ThirdwebCheckout.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

**/ThirdwebCheckout.tsx: Wrap thirdweb CheckoutWidget with WooCommerce integration in ThirdwebCheckout component
Capture transaction hash from CheckoutWidget's onSuccess callback and pass it to WooCommerce

Files:

  • src/checkout-block/ThirdwebCheckout.tsx
**/checkout-block/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use getSetting('thirdweb_stablecoin_data') to retrieve PHP configuration in React components

Files:

  • src/checkout-block/ThirdwebCheckout.tsx
🪛 PHPMD (2.15.0)
includes/class-thirdweb-payment-gateway.php

183-183: Avoid unused local variables such as '$verified'. (undefined)

(UnusedLocalVariable)


214-214: Avoid unused parameters such as '$order'. (undefined)

(UnusedFormalParameter)

🔇 Additional comments (6)
includes/class-thirdweb-payment-gateway.php (3)

109-126: LGTM! Secure iframe integration for legacy checkout.

The URL is properly sanitized with esc_url() and parameters are safely encoded via http_build_query(). The conditional tokenAddress inclusion is handled correctly.


139-145: Validation entirely delegated to frontend/widget.

Returning true unconditionally means the backend trusts the frontend's payment confirmation. This is acceptable given thirdweb's widget confirms payment before signaling success, but note that there's no server-side fallback validation if the frontend is compromised.


158-177: LGTM! Proper handling of WooCommerce Blocks payment data format.

The iteration through payment_data array correctly handles the Blocks format, with appropriate sanitize_text_field() sanitization and fallback to legacy POST format.

src/checkout-block/ThirdwebCheckout.tsx (3)

51-66: LGTM! Safe URL construction with proper validation.

The URLSearchParams API handles encoding correctly, and the 0x prefix check on tokenAddress is a sensible guard against invalid values.


74-120: LGTM! Secure postMessage handling with origin verification.

The implementation correctly:

  • Validates event.origin against thirdweb domains
  • Checks the source field to confirm message type
  • Handles multiple possible transaction hash field names defensively
  • Properly cleans up the listener on unmount

125-161: LGTM! Proper WooCommerce Blocks payment registration.

The payment setup callback correctly handles all three states (complete, error, pending) and includes the appropriate payment method data. The optional txHash handling aligns with the backend's flexible processing.

Comment on lines 181 to 191
if (!empty($tx_hash)) {
// Try to verify on-chain (non-blocking)
$verified = $this->verify_transaction($tx_hash, $order);
$order->payment_complete($tx_hash);
$order->add_order_note(
sprintf(__('Stablecoin payment completed. Transaction: %s', 'thirdweb-wc'), $tx_hash)
sprintf(
__('Stablecoin payment completed via thirdweb checkout. Transaction: %s (Chain: %s)', 'thirdweb-wc'),
$tx_hash,
$chain_id ?: $this->chain_id
)
);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Verification result is ignored - orders complete regardless of verification outcome.

The $verified variable is assigned but never used. The order completes via payment_complete() immediately after, even if verify_transaction() returns false. This undermines the purpose of on-chain verification.

Consider acting on the verification result:

🔎 Proposed fix
         if (!empty($tx_hash)) {
-            // Try to verify on-chain (non-blocking)
-            $verified = $this->verify_transaction($tx_hash, $order);
-            $order->payment_complete($tx_hash);
-            $order->add_order_note(
-                sprintf(
-                    __('Stablecoin payment completed via thirdweb checkout. Transaction: %s (Chain: %s)', 'thirdweb-wc'),
-                    $tx_hash,
-                    $chain_id ?: $this->chain_id
-                )
-            );
+            // Verify on-chain before completing
+            $verified = $this->verify_transaction($tx_hash, $order);
+            if ($verified) {
+                $order->payment_complete($tx_hash);
+                $order->add_order_note(
+                    sprintf(
+                        __('Stablecoin payment verified and completed. Transaction: %s (Chain: %s)', 'thirdweb-wc'),
+                        $tx_hash,
+                        $chain_id ?: $this->chain_id
+                    )
+                );
+            } else {
+                // Mark as on-hold for manual verification
+                $order->update_status('on-hold', 
+                    sprintf(
+                        __('Transaction verification failed. Manual review needed. TX: %s', 'thirdweb-wc'),
+                        $tx_hash
+                    )
+                );
+            }
🧰 Tools
🪛 PHPMD (2.15.0)

183-183: Avoid unused local variables such as '$verified'. (undefined)

(UnusedLocalVariable)

🤖 Prompt for AI Agents
In includes/class-thirdweb-payment-gateway.php around lines 181 to 191 the
$verified result from verify_transaction($tx_hash, $order) is ignored and the
code calls $order->payment_complete($tx_hash) unconditionally; change the flow
so payment is only completed when $verified is truthy and otherwise do not call
payment_complete — instead add an order note indicating on-chain verification
failed and set the order to a failed/pending status (e.g.,
$order->update_status('failed', 'On-chain verification failed for transaction:
'.$tx_hash) or similar) so unsuccessful verifications are not marked paid.

Copy link

@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

Caution

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

⚠️ Outside diff range comments (1)
src/checkout-block/ThirdwebCheckout.tsx (1)

115-146: Payment data lacks transaction hash for server-side verification.

The paymentMethodData only contains thirdweb_chain_id. Without a transaction hash, the server cannot perform any verification of the payment. This contradicts the coding guideline: "Perform transaction verification server-side via RPC for security."

Consider including any transaction identifier the widget provides so the backend can verify the payment occurred on-chain.

🧹 Nitpick comments (3)
src/checkout-block/ThirdwebCheckout.tsx (3)

44-44: Remove unused iframeRef.

The iframeRef is created but never used for any DOM operations. Either remove it or document the intended use case.

🔎 Proposed fix
-    const iframeRef = useRef<HTMLIFrameElement>(null);

And update the iframe element:

                 <iframe
-                    ref={iframeRef}
                     src={buildIframeUrl()}

52-67: Consider memoizing buildIframeUrl to avoid unnecessary recalculations.

The function is called on every render. Since its dependencies (settings.chainId, amount, settings.seller, settings.theme, settings.tokenAddress) are stable, consider using useMemo.

🔎 Proposed refactor
-    // Build iframe URL
-    const buildIframeUrl = () => {
+    // Build iframe URL (memoized)
+    const iframeUrl = React.useMemo(() => {
         const baseUrl = 'https://thirdweb.com/bridge/checkout-widget';
         const params = new URLSearchParams({
             chain: settings.chainId.toString(),
             amount: amount,
             seller: settings.seller,
             theme: settings.theme || 'dark',
         });

         // Add token address if provided
         if (settings.tokenAddress && settings.tokenAddress.startsWith('0x')) {
             params.append('tokenAddress', settings.tokenAddress);
         }

         return `${baseUrl}?${params.toString()}`;
-    };
+    }, [settings.chainId, amount, settings.seller, settings.theme, settings.tokenAddress]);

Then update the iframe:

-                    src={buildIframeUrl()}
+                    src={iframeUrl}

159-167: Minor: Use numeric height or style prop for iframe.

The height="700px" attribute includes the unit, but HTML height attribute expects a number. Either use a numeric value or move to the style prop.

🔎 Proposed fix
                 <iframe
                     ref={iframeRef}
                     src={buildIframeUrl()}
-                    height="700px"
+                    height={700}
                     width="100%"
                     style={{ border: 0 }}
                     title="thirdweb Checkout Widget"
                     allow="payment"
                 />
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 05b902f and 29ae4f3.

⛔ Files ignored due to path filters (1)
  • thirdweb-woocommerce-checkout.zip is excluded by !**/*.zip
📒 Files selected for processing (3)
  • includes/class-thirdweb-blocks-support.php
  • includes/class-thirdweb-payment-gateway.php
  • src/checkout-block/ThirdwebCheckout.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.php

📄 CodeRabbit inference engine (CLAUDE.md)

Use PHP 7.4+ for WordPress plugin backend code

Files:

  • includes/class-thirdweb-blocks-support.php
  • includes/class-thirdweb-payment-gateway.php
**/class-thirdweb-blocks-support.php

📄 CodeRabbit inference engine (CLAUDE.md)

Enable React-based checkout blocks support via class-thirdweb-blocks-support.php

Files:

  • includes/class-thirdweb-blocks-support.php
**/ThirdwebCheckout.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

**/ThirdwebCheckout.tsx: Wrap thirdweb CheckoutWidget with WooCommerce integration in ThirdwebCheckout component
Capture transaction hash from CheckoutWidget's onSuccess callback and pass it to WooCommerce

Files:

  • src/checkout-block/ThirdwebCheckout.tsx
**/checkout-block/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use getSetting('thirdweb_stablecoin_data') to retrieve PHP configuration in React components

Files:

  • src/checkout-block/ThirdwebCheckout.tsx
**/class-thirdweb-payment-gateway.php

📄 CodeRabbit inference engine (CLAUDE.md)

**/class-thirdweb-payment-gateway.php: Extend WC_Payment_Gateway class for payment gateway implementation
Perform transaction verification server-side via RPC for security, not client-side

Files:

  • includes/class-thirdweb-payment-gateway.php
🔇 Additional comments (4)
includes/class-thirdweb-blocks-support.php (1)

87-91: LGTM - Configuration aligned with iframe widget approach.

The removal of clientId and default theme change to 'dark' are consistent with the gateway class and the new iframe-based checkout flow. The essential payment configuration (seller, chainId, tokenAddress) remains intact.

includes/class-thirdweb-payment-gateway.php (2)

35-39: Configuration changes look consistent.

The removal of clientId and default theme change to 'dark' align with the iframe widget approach across the codebase.


121-141: Legacy checkout iframe implementation looks correct.

The URL construction using http_build_query() and output escaping with esc_url() are appropriate. The conditional inclusion of tokenAddress mirrors the React component logic.

src/checkout-block/ThirdwebCheckout.tsx (1)

92-102: Proposed solution won't work—thirdweb CheckoutWidget iframe does not provide transaction hash in postMessage.

The coding guidelines require capturing transaction hash from the widget's success callback, but the thirdweb CheckoutWidget iframe postMessage API (as documented) does not include transaction hash in the success payload. The widget only sends source, type, and message fields; extracting data.transactionHash or data.txHash will result in undefined values.

To capture transaction details reliably, use thirdweb's server-side Bridge/Payments webhooks instead of relying on the client-side iframe postMessage.

Likely an incorrect or invalid review comment.

Comment on lines 160 to 195
public function process_payment($order_id) {
$order = wc_get_order($order_id);
$tx_hash = sanitize_text_field($_POST['thirdweb_tx_hash'] ?? '');

if (empty($tx_hash)) {
// Payment not yet completed - wait for webhook
$order->update_status('pending', __('Awaiting stablecoin payment confirmation.', 'thirdweb-wc'));

return [
'result' => 'success',
'redirect' => $this->get_return_url($order),
];
}

// Transaction hash provided - verify on-chain
if ($this->verify_transaction($tx_hash, $order)) {
$order->payment_complete($tx_hash);
$order->add_order_note(
sprintf(__('Stablecoin payment completed. Transaction: %s', 'thirdweb-wc'), $tx_hash)
);

WC()->cart->empty_cart();

return [
'result' => 'success',
'redirect' => $this->get_return_url($order),
];
}

wc_add_notice(__('Payment verification failed. Please try again.', 'thirdweb-wc'), 'error');
return ['result' => 'failure'];
}

/**
* Verify transaction on-chain
*/
private function verify_transaction($tx_hash, $order) {
// Use thirdweb RPC to verify the transaction
$rpc_url = 'https://' . $this->chain_id . '.rpc.thirdweb.com/' . $this->client_id;

$response = wp_remote_post($rpc_url, [
'headers' => ['Content-Type' => 'application/json'],
'body' => json_encode([
'jsonrpc' => '2.0',
'method' => 'eth_getTransactionReceipt',
'params' => [$tx_hash],
'id' => 1,
]),
]);

if (is_wp_error($response)) {
return false;
// Get chain ID from payment data (WooCommerce Blocks sends it in payment_data array)
$chain_id = $this->chain_id;

if (isset($_POST['payment_data']) && is_array($_POST['payment_data'])) {
foreach ($_POST['payment_data'] as $data) {
if (isset($data['key']) && $data['key'] === 'thirdweb_chain_id') {
$chain_id = sanitize_text_field($data['value'] ?? $this->chain_id);
break;
}
}
}

$body = json_decode(wp_remote_retrieve_body($response), true);
$receipt = $body['result'] ?? null;

if (!$receipt || $receipt['status'] !== '0x1') {
return false;

// Fallback to legacy format
if ($chain_id === $this->chain_id && isset($_POST['thirdweb_chain_id'])) {
$chain_id = sanitize_text_field($_POST['thirdweb_chain_id']);
}

// Additional verification: check recipient and amount in logs
// This is simplified - production code should decode transfer events
return true;
// Payment was completed via checkout widget - trust thirdweb's confirmation
$order->payment_complete();
$order->add_order_note(
sprintf(
__('Stablecoin payment completed via thirdweb checkout widget. Chain: %s', 'thirdweb-wc'),
$chain_id
)
);

WC()->cart->empty_cart();

return [
'result' => 'success',
'redirect' => $this->get_return_url($order),
];
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical security concern: Payment completes without any transaction verification.

The process_payment() method calls $order->payment_complete() without verifying that an actual blockchain transaction occurred. This contradicts the coding guideline: "Perform transaction verification server-side via RPC for security, not client-side."

Key risks:

  1. No transaction hash recorded - Orders have no audit trail linking to blockchain transactions
  2. No on-chain verification - The server trusts frontend confirmation without RPC validation
  3. Fraud potential - A malicious actor who can submit the payment form (e.g., via XSS or CSRF) could mark orders as paid without actual payment

The previous implementation had verify_transaction() (even if it had issues noted in past reviews). Removing it entirely significantly degrades security.

Consider at minimum:

  1. Capturing and recording the transaction hash from the widget
  2. Implementing async verification via a webhook or cron job
  3. Setting orders to on-hold status pending manual/automated verification
🔎 Suggested safer approach
     public function process_payment($order_id) {
         $order = wc_get_order($order_id);
         
         // Get chain ID from payment data
         $chain_id = $this->chain_id;
+        $tx_hash = null;
         
         if (isset($_POST['payment_data']) && is_array($_POST['payment_data'])) {
             foreach ($_POST['payment_data'] as $data) {
                 if (isset($data['key']) && $data['key'] === 'thirdweb_chain_id') {
                     $chain_id = sanitize_text_field($data['value'] ?? $this->chain_id);
-                    break;
+                }
+                if (isset($data['key']) && $data['key'] === 'thirdweb_tx_hash') {
+                    $tx_hash = sanitize_text_field($data['value'] ?? '');
                 }
             }
         }

-        // Payment was completed via checkout widget - trust thirdweb's confirmation
-        $order->payment_complete();
+        // Set order on-hold until transaction can be verified
+        $order->update_status('on-hold', 
+            sprintf(
+                __('Awaiting blockchain confirmation. Chain: %s, TX: %s', 'thirdweb-wc'),
+                $chain_id,
+                $tx_hash ?: 'pending'
+            )
+        );
+        
+        // Store TX hash for verification
+        if ($tx_hash) {
+            $order->update_meta_data('_thirdweb_tx_hash', $tx_hash);
+            $order->update_meta_data('_thirdweb_chain_id', $chain_id);
+            $order->save();
+        }

As per coding guidelines, transaction verification should be performed server-side via RPC for security.

Comment on lines +166 to 178
if (isset($_POST['payment_data']) && is_array($_POST['payment_data'])) {
foreach ($_POST['payment_data'] as $data) {
if (isset($data['key']) && $data['key'] === 'thirdweb_chain_id') {
$chain_id = sanitize_text_field($data['value'] ?? $this->chain_id);
break;
}
}
}

$body = json_decode(wp_remote_retrieve_body($response), true);
$receipt = $body['result'] ?? null;

if (!$receipt || $receipt['status'] !== '0x1') {
return false;

// Fallback to legacy format
if ($chain_id === $this->chain_id && isset($_POST['thirdweb_chain_id'])) {
$chain_id = sanitize_text_field($_POST['thirdweb_chain_id']);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the target file
find . -name "class-thirdweb-payment-gateway.php" -type f

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 131


🏁 Script executed:

# Once we find it, read the full method containing lines 166-178
cat -n includes/class-thirdweb-payment-gateway.php | sed -n '140,200p'

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 2627


🏁 Script executed:

# Search for nonce verification in the file
rg "wp_verify_nonce|check_admin_referer|wp_nonce_field" --type php includes/class-thirdweb-payment-gateway.php

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 68


🏁 Script executed:

# Search for the process_payment method definition to understand the full context
rg -n "function process_payment" --type php -A 30 includes/class-thirdweb-payment-gateway.php

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 1424


🏁 Script executed:

# Check class definition and parent class
rg -n "class.*Payment.*Gateway|extends.*WC_Payment_Gateway" --type php includes/class-thirdweb-payment-gateway.php

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 150


🏁 Script executed:

# Search for any nonce verification anywhere in the file
rg -n "nonce|referer|verify_nonce|check_admin" --type php includes/class-thirdweb-payment-gateway.php

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 68


🏁 Script executed:

# Check if there's any RPC verification or server-side validation
rg -n "rpc|RPC|verify.*transaction|server.*side" --type php includes/class-thirdweb-payment-gateway.php

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 68


🏁 Script executed:

# Search for where process_payment is called/hooked
rg -n "process_payment|add_action.*payment" --type php includes/class-thirdweb-payment-gateway.php

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 255


🏁 Script executed:

# Look at the full file structure to understand initialization and hooks
head -60 includes/class-thirdweb-payment-gateway.php | cat -n

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 2713


🏁 Script executed:

# Check if WooCommerce checkout form itself includes nonce protection
rg -n "woocommerce.*checkout|payment.*form|wp_nonce" --type php | head -50

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 281


🏁 Script executed:

# Look for any frontend files that might handle nonce
find . -name "*.js" -o -name "*.tsx" -o -name "*.jsx" | xargs grep -l "nonce\|referer" 2>/dev/null | head -20

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 68


🏁 Script executed:

# Search for how payment is initiated - check for checkout process
rg -n "checkout|payment.*process|form.*action" --type php includes/ | head -50

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 1934


🏁 Script executed:

# Check the full process_payment implementation and what happens after
cat -n includes/class-thirdweb-payment-gateway.php | sed -n '160,195p'

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 1680


🏁 Script executed:

# Search for any RPC calls or thirdweb API verification anywhere in the codebase
rg -n "rpc\|RPC\|thirdweb.*api\|verify.*transaction\|confirmTransaction" --type php

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 68


🏁 Script executed:

# Check if there's transaction verification happening in any files
find . -type f -name "*.php" | xargs grep -l "confirmTransaction\|verify.*payment\|rpc" 2>/dev/null

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 70


🏁 Script executed:

# Look at the blocks support file to understand how payment is handled there
cat -n includes/class-thirdweb-blocks-support.php | head -100

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 3779


🏁 Script executed:

# Check how WooCommerce Blocks passes data and if there's nonce handling in the JS/React side
find . -name "*.tsx" -o -name "*.ts" -o -name "*.jsx" -o -name "*.js" | xargs grep -l "payment\|nonce\|checkout" 2>/dev/null | head -10

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 181


🏁 Script executed:

# Search the main plugin file for hooks and initialization that might handle nonce
cat -n thirdweb-woocommerce-checkout.php

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 7679


🏁 Script executed:

# Check how WooCommerce Blocks handles checkout nonce verification
web_search "WooCommerce Blocks checkout nonce verification security"

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 136


🏁 Script executed:

# Check the ThirdwebCheckout React component to see if there's any verification happening
head -100 src/checkout-block/ThirdwebCheckout.tsx

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 3297


🌐 Web query:

WooCommerce payment gateway nonce verification security process_payment hook

💡 Result:

Short answer

  • Verify the checkout nonce before you actually process the charge (i.e., before running gateway process_payment). Use the standard WooCommerce checkout nonce name and WordPress functions: check for $_POST['woocommerce-process-checkout-nonce'] (or appropriate Store API/REST nonce for block/REST flows) and call wp_verify_nonce. See WooCommerce/WordPress docs below. [1][2]

Why (brief)

  • process_payment is executed as part of Checkout and typically expects a valid, authenticated request. If you run payment logic without verifying the nonce (and other server-side checks) you increase CSRF/forgery risk and may process unwanted requests. Also ensure the checkout page isn’t cached (cached nonces expire and break verification). [1][3]

Minimal example (classic Checkout page)

  • Put nonce verification early (e.g., in your gateway’s validate_fields() or hooked to woocommerce_checkout_process) and return error if invalid; only call process_payment when the request passed verification.

Example:

process_payment( $order_id ) or continue processing. ?>

Notes / best practices (bullet)

  • For Checkout block / Store API flows use the Store API nonce header ("Nonce") and wp_create_nonce('wc_store_api') / server-side validation per Store API docs. [1]
  • Don’t rely solely on nonce: require SSL, validate/sanitize all inputs, check order/cart state, and rate-limit/block bots (pre-check server-level protections) to reduce card-testing attacks. [1][4][5]
  • Ensure checkout page is excluded from page caching so nonces don’t get stale. [3][6]

References

  • WooCommerce Store API nonces / how to send and validate (Nonce header, wp_create_nonce('wc_store_api')). [1]
  • WooCommerce Payment Gateway API / creating gateways and checkout flow guidance. [2]
  • WooCommerce docs on WordPress nonces and caching implications. [3]
  • WooCommerce security best practices (hardening, validation). [5]
  • Article on preventing card-testing & validating checkout requests (blocking before gateway). [4]

🏁 Script executed:

# Check the React component more to understand the full payment flow
cat -n src/checkout-block/ThirdwebCheckout.tsx | sed -n '1,150p'

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 5812


🏁 Script executed:

# Search for any AJAX handlers or endpoints that might handle payment verification
rg -n "add_action.*ajax\|wp_ajax\|REST" --type php

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 68


🏁 Script executed:

# Check if there's any verification happening after process_payment or in callbacks
cat -n includes/class-thirdweb-payment-gateway.php | tail -50

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 2152


🏁 Script executed:

# Search the entire codebase for RPC calls or blockchain verification
rg -i "rpc\|blockchain\|verify.*transaction\|confirmTransaction\|getTransactionReceipt" --type php --type tsx --type ts

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 113


🏁 Script executed:

# Check the index.tsx to see the complete React component flow
cat -n src/checkout-block/index.tsx

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 3584


🏁 Script executed:

# Check if there's any Store API nonce or REST verification in the plugin
rg -i "store.*api|rest|nonce" --type php includes/

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 68


🏁 Script executed:

# Search for any AJAX or REST endpoints that might intercept the payment
rg -n "rest_api_init|register_rest_route|wp_ajax" --type php

Repository: thirdweb-dev/woocommerce-thirdweb-checkout

Length of output: 68


Add server-side RPC verification before marking orders as complete.

The code accesses $_POST data without explicit nonce verification. More critically, it violates the coding guideline requiring "transaction verification server-side via RPC for security, not client-side." The current implementation marks orders complete immediately upon receiving a postMessage from the thirdweb iframe (line 181), trusting the frontend signal without verifying the actual blockchain transaction via RPC. Implement server-side verification by calling RPC to confirm the transaction occurred on-chain before calling $order->payment_complete().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants