-
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| # Dependencies | ||
| node_modules/ | ||
| vendor/ | ||
| .pnpm-store/ | ||
|
|
||
| # Build output | ||
| build/ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,14 +32,11 @@ 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'); | ||
| $this->theme = $this->get_option('theme', 'dark'); | ||
|
|
||
| // Save settings hook | ||
| add_action('woocommerce_update_options_payment_gateways_' . $this->id, [$this, 'process_admin_options']); | ||
|
|
@@ -69,23 +66,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' => [ | ||
|
|
@@ -108,20 +92,20 @@ 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, | ||
| 'description' => __('Choose the theme for the checkout widget. Default is dark.', 'thirdweb-wc'), | ||
| 'default' => 'dark', | ||
| 'options' => [ | ||
| 'light' => __('Light', 'thirdweb-wc'), | ||
| 'dark' => __('Dark', 'thirdweb-wc'), | ||
| 'light' => __('Light', 'thirdweb-wc'), | ||
| ], | ||
| 'desc_tip' => true, | ||
| ], | ||
| ]; | ||
| } | ||
|
|
@@ -134,28 +118,39 @@ 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; | ||
| } | ||
|
|
||
| // Add theme (default is dark, but allow override) | ||
| $params['theme'] = $this->theme; | ||
|
|
||
| // Hidden field to store transaction hash | ||
| echo '<input type="hidden" name="thirdweb_tx_hash" id="thirdweb_tx_hash" value="" />'; | ||
| $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>'; | ||
| } | ||
|
|
||
| /** | ||
| * Validate payment fields | ||
| * | ||
| * Note: For WooCommerce Blocks, validation happens in the React component. | ||
| * This method is mainly for legacy checkout. | ||
| */ | ||
| public function validate_fields() { | ||
| if (empty($_POST['thirdweb_tx_hash'])) { | ||
| wc_add_notice(__('Please complete the payment.', 'thirdweb-wc'), 'error'); | ||
| return false; | ||
| } | ||
| // Always return true - validation is handled by: | ||
| // 1. React component for Blocks (checks paymentComplete before allowing submission) | ||
| // 2. Frontend JavaScript for legacy checkout | ||
| return true; | ||
| } | ||
|
|
||
|
|
@@ -164,82 +159,52 @@ public function validate_fields() { | |
| */ | ||
| 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), | ||
| ]; | ||
| } | ||
|
Comment on lines
160
to
195
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical security concern: Payment completes without any transaction verification. The Key risks:
The previous implementation had Consider at minimum:
🔎 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. |
||
|
|
||
| /** | ||
| * Get config for frontend | ||
| */ | ||
| public function get_frontend_config() { | ||
| return [ | ||
| 'clientId' => $this->client_id, | ||
| 'seller' => $this->seller_wallet, | ||
| 'chainId' => (int) $this->chain_id, | ||
| 'tokenAddress' => $this->token_address, | ||
| 'theme' => $this->theme, | ||
| 'title' => $this->title, | ||
| 'description' => $this->description, | ||
| 'theme' => $this->theme ?: 'light', | ||
| ]; | ||
| } | ||
| } | ||
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:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 131
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 2627
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 68
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 1424
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 150
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 68
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 68
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 255
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 2713
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 281
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 68
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 1934
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 1680
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 68
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 70
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 3779
🏁 Script executed:
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.phpRepository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 7679
🏁 Script executed:
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
Why (brief)
Minimal example (classic Checkout page)
Example:
process_payment( $order_id ) or continue processing. ?>Notes / best practices (bullet)
References
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 5812
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 68
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 2152
🏁 Script executed:
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.tsxRepository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 3584
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 68
🏁 Script executed:
Repository: thirdweb-dev/woocommerce-thirdweb-checkout
Length of output: 68
Add server-side RPC verification before marking orders as complete.
The code accesses
$_POSTdata 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().