Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Dependencies
node_modules/
vendor/
.pnpm-store/

# Build output
build/
Expand Down
4 changes: 1 addition & 3 deletions includes/class-thirdweb-blocks-support.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,10 @@ public function get_payment_method_data() {
'description' => $this->get_setting('description'),
'supports' => $this->get_supported_features(),

// thirdweb configuration
'clientId' => $this->get_setting('client_id'),
// thirdweb configuration (no Client ID needed for iframe widget)
'seller' => $this->get_setting('seller_wallet'),
'chainId' => (int) $this->get_setting('chain_id'),
'tokenAddress' => $this->get_setting('token_address'),
'theme' => $this->get_setting('theme') ?: 'light',

// Icons/branding
'icon' => THIRDWEB_WC_PLUGIN_URL . 'assets/icon.svg',
Expand Down
81 changes: 36 additions & 45 deletions includes/class-thirdweb-payment-gateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,10 @@ public function __construct() {
$this->description = $this->get_option('description');
$this->enabled = $this->get_option('enabled');

// thirdweb specific settings
// Use .env value as default if WooCommerce setting is empty
$env_client_id = thirdweb_wc_get_env('THIRDWEB_CLIENT_ID', '');
$this->client_id = $this->get_option('client_id') ?: $env_client_id;
// thirdweb specific settings (no Client ID needed for iframe widget)
$this->seller_wallet = $this->get_option('seller_wallet');
$this->chain_id = $this->get_option('chain_id');
$this->token_address = $this->get_option('token_address');
$this->theme = $this->get_option('theme');

// Save settings hook
add_action('woocommerce_update_options_payment_gateways_' . $this->id, [$this, 'process_admin_options']);
Expand Down Expand Up @@ -69,23 +65,10 @@ public function init_form_fields() {
'description' => __('Payment method description shown at checkout', 'thirdweb-wc'),
'default' => __('Pay securely with USDC, USDT or other stablecoins from any wallet.', 'thirdweb-wc'),
],
'client_id' => [
'title' => __('thirdweb Client ID', 'thirdweb-wc'),
'type' => 'text',
'description' => sprintf(
__('Get your Client ID from <a href="%s" target="_blank">thirdweb Dashboard</a>. Create a new project if you haven\'t already. Can also be set via .env file for development.', 'thirdweb-wc'),
'https://thirdweb.com/dashboard'
),
'default' => thirdweb_wc_get_env('THIRDWEB_CLIENT_ID', ''),
'placeholder' => __('e.g., abc123def456...', 'thirdweb-wc'),
],
'seller_wallet' => [
'title' => __('Seller Wallet Address', 'thirdweb-wc'),
'type' => 'text',
'description' => sprintf(
__('Your project wallet address that will receive all payments. Get this from your <a href="%s" target="_blank">thirdweb project dashboard</a>.', 'thirdweb-wc'),
'https://thirdweb.com/dashboard'
),
'description' => __('Your wallet address that will receive all payments. Use any Ethereum-compatible wallet (MetaMask, Coinbase Wallet, etc.).', 'thirdweb-wc'),
'default' => '',
'placeholder' => __('0x...', 'thirdweb-wc'),
'custom_attributes' => [
Expand All @@ -108,21 +91,10 @@ public function init_form_fields() {
'token_address' => [
'title' => __('Token Address (Optional)', 'thirdweb-wc'),
'type' => 'text',
'description' => __('USDC/USDT contract address for the chain above. Make sure the token address matches your selected chain. Leave empty to accept the native token (ETH, MATIC, etc.). Default is USDC on Base (chain 8453).', 'thirdweb-wc'),
'description' => __('USDC/USDT contract address for the chain above. Make sure the token address matches your selected chain. Leave empty to accept any stablecoin. Default is USDC on Base (chain 8453).', 'thirdweb-wc'),
'default' => '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base
'placeholder' => __('0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', 'thirdweb-wc'),
],
'theme' => [
'title' => __('Widget Theme', 'thirdweb-wc'),
'type' => 'select',
'description' => __('Choose the color theme for the checkout widget. Light theme is recommended for most sites.', 'thirdweb-wc'),
'default' => 'light',
'desc_tip' => true,
'options' => [
'light' => __('Light', 'thirdweb-wc'),
'dark' => __('Dark', 'thirdweb-wc'),
],
],
];
}

Expand All @@ -134,15 +106,24 @@ public function payment_fields() {
echo wpautop(wptexturize($this->description));
}

// Container for React to mount the CheckoutWidget
echo '<div id="thirdweb-checkout-widget"
data-client-id="' . esc_attr($this->client_id) . '"
data-seller="' . esc_attr($this->seller_wallet) . '"
data-chain-id="' . esc_attr($this->chain_id) . '"
data-token-address="' . esc_attr($this->token_address) . '"
data-amount="' . esc_attr(WC()->cart->get_total('edit')) . '"
data-currency="' . esc_attr(get_woocommerce_currency()) . '">
</div>';
// Container for iframe checkout widget (legacy checkout)
$amount = WC()->cart->get_total('edit');
$params = [
'chain' => $this->chain_id,
'amount' => $amount,
'seller' => $this->seller_wallet,
];

// Only add tokenAddress if provided
if (!empty($this->token_address)) {
$params['tokenAddress'] = $this->token_address;
}

$iframe_url = 'https://thirdweb.com/bridge/checkout-widget?' . http_build_query($params);

echo '<div id="thirdweb-checkout-widget">';
echo '<iframe src="' . esc_url($iframe_url) . '" height="700px" width="100%" style="border: 0;" title="thirdweb Checkout Widget"></iframe>';
echo '</div>';

// Hidden field to store transaction hash
echo '<input type="hidden" name="thirdweb_tx_hash" id="thirdweb_tx_hash" value="" />';
Expand Down Expand Up @@ -199,8 +180,17 @@ public function process_payment($order_id) {
* 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;
// 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';

$response = wp_remote_post($rpc_url, [
'headers' => ['Content-Type' => 'application/json'],
Expand All @@ -210,10 +200,13 @@ private function verify_transaction($tx_hash, $order) {
'params' => [$tx_hash],
'id' => 1,
]),
'timeout' => 10,
]);

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.


$body = json_decode(wp_remote_retrieve_body($response), true);
Expand All @@ -233,13 +226,11 @@ private function verify_transaction($tx_hash, $order) {
*/
public function get_frontend_config() {
return [
'clientId' => $this->client_id,
'seller' => $this->seller_wallet,
'chainId' => (int) $this->chain_id,
'tokenAddress' => $this->token_address,
'title' => $this->title,
'description' => $this->description,
'theme' => $this->theme ?: 'light',
];
}
}
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@
"@wordpress/scripts": "^31.1.0",
"typescript": "^5.9.3"
},
"dependencies": {
"thirdweb": "^5.116.1"
},
"dependencies": {},
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
Expand Down
Loading