-
Notifications
You must be signed in to change notification settings - Fork 0
use checkout widget #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughReplaces 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 Changes
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this 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
sellerandchainIdare truthy. You may want to validate thatselleris 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 checkingevent.source.The current validation checks
event.originandevent.data.source, which is good. However, for stronger security, also verify thatevent.sourcematches the iframe'scontentWindow. This prevents other iframes or windows fromthirdweb.comfrom 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: Removeconsole.logfrom 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: Removeconsole.errorfrom 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 memoizingbuildIframeUrlto prevent unnecessary recalculations.Currently
buildIframeUrl()is called on every render. While not a significant performance issue, memoizing it withuseMemowould 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.
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (7)
.gitignoreincludes/class-thirdweb-blocks-support.phpincludes/class-thirdweb-payment-gateway.phppackage.jsonsrc/checkout-block/ThirdwebCheckout.tsxsrc/checkout-block/index.tsxthirdweb-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.tsxsrc/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.phpthirdweb-woocommerce-checkout.phpincludes/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
thirdwebdependency 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 forchainIdensures 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 andesc_url()for XSS prevention. The conditionaltokenAddressinclusion 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.
| 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; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
There was a problem hiding this 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$orderparameter and potential type mismatch on chain lookup.
- The
$orderparameter is never used - remove it or use it for logging/context.- 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: Returningtrueon 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
trueallows orders to complete without on-chain verification. Combined with the unused$verifiedresult, this means orders are never actually blocked by failed verification.Consider returning
falseand letting orders remain pending for manual review when verification cannot be completed.
🧹 Nitpick comments (2)
src/checkout-block/ThirdwebCheckout.tsx (2)
44-44:iframeRefis 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
useCallbackis 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.
📒 Files selected for processing (2)
includes/class-thirdweb-payment-gateway.phpsrc/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 viahttp_build_query(). The conditional tokenAddress inclusion is handled correctly.
139-145: Validation entirely delegated to frontend/widget.Returning
trueunconditionally 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_dataarray correctly handles the Blocks format, with appropriatesanitize_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
URLSearchParamsAPI handles encoding correctly, and the0xprefix check ontokenAddressis a sensible guard against invalid values.
74-120: LGTM! Secure postMessage handling with origin verification.The implementation correctly:
- Validates
event.originagainst thirdweb domains- Checks the
sourcefield 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
txHashhandling aligns with the backend's flexible processing.
| 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 | ||
| ) | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this 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
paymentMethodDataonly containsthirdweb_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 unusediframeRef.The
iframeRefis 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 memoizingbuildIframeUrlto 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 usinguseMemo.🔎 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 HTMLheightattribute expects a number. Either use a numeric value or move to thestyleprop.🔎 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.
⛔ Files ignored due to path filters (1)
thirdweb-woocommerce-checkout.zipis excluded by!**/*.zip
📒 Files selected for processing (3)
includes/class-thirdweb-blocks-support.phpincludes/class-thirdweb-payment-gateway.phpsrc/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.phpincludes/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
clientIdand 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
clientIdand 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 withesc_url()are appropriate. The conditional inclusion oftokenAddressmirrors 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, andmessagefields; extractingdata.transactionHashordata.txHashwill result inundefinedvalues.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.
| 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), | ||
| ]; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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:
- No transaction hash recorded - Orders have no audit trail linking to blockchain transactions
- No on-chain verification - The server trusts frontend confirmation without RPC validation
- 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:
- Capturing and recording the transaction hash from the widget
- Implementing async verification via a webhook or cron job
- Setting orders to
on-holdstatus 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.
| 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']); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, locate and examine the target file
find . -name "class-thirdweb-payment-gateway.php" -type fRepository: 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.phpRepository: 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.phpRepository: 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.phpRepository: 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.phpRepository: 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.phpRepository: 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.phpRepository: 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 -nRepository: 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 -50Repository: 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 -20Repository: 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 -50Repository: 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 phpRepository: 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/nullRepository: 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 -100Repository: 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 -10Repository: 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.phpRepository: 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.tsxRepository: 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 phpRepository: 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 -50Repository: 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 tsRepository: 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.tsxRepository: 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 phpRepository: 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().
Summary by CodeRabbit
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.