Skip to content

Conversation

@xecdev
Copy link
Collaborator

@xecdev xecdev commented Dec 24, 2025

This PR implements #2 by adding WooCommerce support and some required misc refactors.

Test Plan:

  • Install the plugin
  • Install the WooCommerce plugin
  • Update your Payment Triggers to include Currency and Value (or just copy-paste the config from the Paywall Settings)
  • Go to WooCommerce Settings > Payments > Enable PayButton Payment Gateway
  • Add a product in Woo
  • Open the shop and buy a product

Summary by CodeRabbit

  • New Features

    • PayButton added as a WooCommerce gateway and Blocks payment method; appears on order/thank-you pages.
    • New paybutton overlay UI showing “Verifying Payment...” during verification.
  • Improvements

    • Automatic order-status polling to confirm payments and refresh receipts.
    • Broader per-field admin address validation; generator input no longer shows inline validation.
    • Site setting to disable the sticky header; CSS now applies when that class is present.
    • Transaction handling hardened to reduce duplicate/replay issues.
  • UI

    • Dashboard button now links to WooCommerce Payments settings; admin buttons have a reusable disabled style.

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

@coderabbitai
Copy link

coderabbitai bot commented Dec 24, 2025

📝 Walkthrough

Walkthrough

Adds a WooCommerce gateway and Blocks integration, centralizes transaction/signature logic in a new service class, implements order-status polling with a verification overlay, refactors address validation to multi-target feedback, and introduces a sitewide sticky-header toggle with body-class control.

Changes

Cohort / File(s) Summary
Sticky header & admin UI
assets/css/sticky-header.css, templates/admin/paywall-settings.php, includes/class-paybutton-public.php, includes/class-paybutton-admin.php
Scoped top padding to .pb-has-sticky-header; added paybutton_hide_sticky_header admin setting and body_class filter; early-exit when sticky header disabled; admin enqueue/screen logic adjusted; redirects use wp_safe_redirect.
Address validation / generator
assets/js/addressValidator.bundle.js, assets/js/paybutton-generator.js
Reworked validator to validate multiple inputs with per-input result spans and shared validate function; removed the generator's inline admin-address validation block.
WooCommerce gateway
includes/woocommerce/class-wc-gateway-paybutton.php, paybutton.php
New WC_Gateway_PayButton implementing gateway settings, admin validation, process_payment, thank-you payload output, frontend script localize, availability checks, and registration + HPOS declaration.
WooCommerce Blocks support & frontend block script
assets/js/paybutton-blocks.js, includes/woocommerce/class-paybutton-blocks-support.php, paybutton.php
New Blocks label/content script and WC_PayButton_Blocks_Support to register payment-method data with Blocks PaymentMethodRegistry; registers wc-paybutton-blocks script handle.
Transaction service & AJAX refactor
includes/class-paybutton-transactions.php, includes/class-paybutton-ajax.php
New PayButton_Transactions class (signature verification, secure tokens, token consumption, price/unit validation, paywall parsing, login/unlock inserts); AJAX handlers refactored to call these helpers; added check_order_status(); removed several private helpers.
Order-status polling / Woo frontend integration
assets/js/paybutton-woo.js, includes/class-paybutton-ajax.php
New paybutton-woo.js initializes PayButton on order pages, shows overlay on close when payment initiated, and polls paybutton_check_order_status every 3s; AJAX handler queries Woo order status and triggers reload when paid.
Overlay template & footer output
templates/public/paybutton-overlay.php, includes/class-paybutton-public.php, assets/js/paybutton-woo.js
Added overlay template (spinner + "Verifying Payment..."), output via wp_footer hook at priority 5, and JS to show/hide during verification.
Activator & DB schema changes
includes/class-paybutton-activator.php
Disabled old migration call; converted tx_hash_idx to UNIQUE on both paybutton_paywall_unlocked and paybutton_logins.
Admin/dashboard tweaks & docs
templates/admin/dashboard.php, templates/admin/paywall-settings.php
Replaced dashboard placeholder with conditional WooCommerce Payments button; added sticky header admin checkbox; updated sample JSON docs in Public Key section.
Admin styles
assets/css/paybutton-admin.css
Added reusable .paybutton-disabled class for a global disabled button appearance.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Browser as Customer (Browser)
    participant WC as WooCommerce (Order page)
    participant PayButtonJS as PayButton JS (paybutton-woo.js)
    participant AJAX as WP AJAX (paybutton_check_order_status)
    participant OrderDB as WooCommerce Order DB

    Browser->>WC: Load order-received (renders PayButton container with data-config)
    WC->>PayButtonJS: Initialize PayButton.render(config)
    PayButtonJS->>Browser: Show payment UI
    Browser->>PayButtonJS: Complete payment (onSuccess)
    PayButtonJS->>AJAX: Start polling with order_id
    loop every 3s
        AJAX->>OrderDB: Query order status by ID
        OrderDB-->>AJAX: Return status
        alt status is paid/processing/completed
            AJAX-->>PayButtonJS: Respond success -> instruct reload
        else
            AJAX-->>PayButtonJS: Respond still pending
        end
    end
    Browser->>WC: Reload -> show receipt (payment confirmed)
Loading
sequenceDiagram
    autonumber
    participant Blocks as Woo Blocks UI
    participant Registry as Payment Method Registry
    participant Support as WC_PayButton_Blocks_Support
    participant Gateway as WC_Gateway_PayButton

    Blocks->>Registry: Request payment method registration
    Registry->>Support: Register support class
    Support->>Gateway: Query active gateway & settings
    Gateway-->>Support: Return title, icons, description, supports
    Support-->>Registry: Provide payment method data
    Registry-->>Blocks: Render PayButton option in checkout UI
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

enhancement (behind the scenes)

Suggested reviewers

  • Klakurka

Poem

🐰 I hopped through commits with code in my paw,
Tokens tucked safe and headers a-faw.
Buttons now whisper to Woo through the night,
Polls hum and reload when the ledger says right.
A tiny rabbit cheers: the checkout’s in sight!

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.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title 'Implement Woo Payment Gateway' directly and concisely describes the main objective of the PR: adding WooCommerce payment gateway support.
✨ 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 feat/implement-woo-payment-gateway

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

❤️ Share

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

@xecdev xecdev self-assigned this Dec 24, 2025
@xecdev xecdev added the Feature label Dec 24, 2025
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: 5

🧹 Nitpick comments (7)
assets/js/addressValidator.bundle.js (1)

754-843: Consider defensive programming improvements for the multi-target validation logic.

The validation logic is well-structured but could benefit from a few robustness improvements:

  1. Line 777: After checking if (!addressInput), the function returns early, which is good. However, consider logging or tracking which targets failed to initialize for debugging purposes.

  2. Line 804: The querySelector for the save button could fail silently. Consider handling the case where the button doesn't exist more explicitly.

  3. Line 814 & 822: Using '0px' and '3px' as string values for marginBottom is valid but inconsistent. Consider using numeric values or a consistent format.

  4. Line 817-818: When the input is empty, the save button is disabled. Verify this is the intended behavior for the WooCommerce settings page where administrators might want to clear an address.

🔎 Suggested improvements
     targets.forEach(target => {
         const addressInput = document.getElementById(target.input);
-        if (!addressInput) return;
+        if (!addressInput) {
+            console.warn(`PayButton: Address input "${target.input}" not found for context "${target.context}"`);
+            return;
+        }
 
         // Create a unique span ID for this input
         const resultSpanId = target.input + '_validation_result';
         
         // Find or create a span for validation feedback.
         let resultSpan = document.getElementById(resultSpanId);
         
         if (!resultSpan) {
             resultSpan = document.createElement('span');
             resultSpan.id = resultSpanId;
             
             // STYLING UPDATES
             resultSpan.style.display = 'block'; 
             resultSpan.style.fontWeight = 'bold';
-            // Add spacing below the text so it doesn't touch the input
-            resultSpan.style.marginBottom = '5px'; 
+            resultSpan.style.marginBottom = '5px';
             
             // PLACEMENT FIX (ABOVE INPUT BOX)
-            // This inserts the resultSpan immediately BEFORE the addressInput element.
-            // On the Generator page, this will place it between the Label and the Input.
             addressInput.parentNode.insertBefore(resultSpan, addressInput);
         }
 
         // Select the "Save Changes" button if applicable
         let saveButton = null;
         if (target.btnName) {
             saveButton = document.querySelector(`button[name="${target.btnName}"]`);
+            if (!saveButton) {
+                console.warn(`PayButton: Save button "${target.btnName}" not found for context "${target.context}"`);
+            }
         }
includes/class-paybutton-public.php (1)

384-389: Consider adding file existence check for template loading.

The output_paybutton_overlay method loads the template without verifying it exists first. While the load_public_template method will fail if the file is missing, adding an explicit check would provide better error handling and debugging information.

🔎 Suggested improvement
 public function output_paybutton_overlay() {
+    $template_path = PAYBUTTON_PLUGIN_DIR . 'templates/public/paybutton-overlay.php';
+    if ( ! file_exists( $template_path ) ) {
+        error_log( 'PayButton: Overlay template not found at ' . $template_path );
+        return;
+    }
     $this->load_public_template( 'paybutton-overlay' );
 }
assets/js/paybutton-blocks.js (1)

1-76: Consider adding safety checks and extracting inline styles.

The WooCommerce Blocks integration is well-structured, but could benefit from a few improvements:

  1. Missing global checks: The IIFE assumes window.wc and window.wp exist. If these scripts fail to load, this will cause a runtime error.

  2. Line 36-44: The pipe separator styling is defined inline. Consider extracting these styles to a CSS class for maintainability.

  3. Line 70: canMakePayment: () => true always returns true without any validation. Consider adding checks for required settings (e.g., gateway enabled, address configured).

  4. Lines 26-33 & 47-54: Image styling is duplicated. Consider extracting to a shared style object or CSS class.

🔎 Suggested improvements
+(function( wc, wp ) {
+    'use strict';
+    
+    // Safety check for required globals
+    if ( typeof wc === 'undefined' || typeof wp === 'undefined' ) {
+        console.error( 'PayButton Blocks: Required dependencies (wc, wp) not loaded' );
+        return;
+    }
+    
     const { registerPaymentMethod } = wc.wcBlocksRegistry;
     const { getSetting } = wc.wcSettings;
     const { decodeEntities } = wp.htmlEntities;
     const { createElement } = wp.element;

     const settings = getSetting( 'paybutton_data', {} );
     
     const labelText = decodeEntities( settings.title || 'PayButton' );
+    
+    // Shared image styles
+    const imageStyle = { maxHeight: '30px', objectFit: 'contain' };

     // Create a Custom Label Component (Dual Icons)
     const LabelIconOnly = () => {
         return createElement(
             'span', 
             { 
                 style: { 
                     display: 'flex', 
                     alignItems: 'center', 
                     width: '100%',
                 } 
             },
             // 1. The PayButton Image
             settings.icon ? createElement( 'img', { 
                 src: settings.icon, 
                 alt: labelText,
-                style: { 
-                    maxHeight: '30px', 
-                    objectFit: 'contain'
-                } 
+                style: imageStyle
             } ) : null,

-            // 2. The Pipeline Separator (Only shows if BOTH icons exist)
-            (settings.icon && settings.icon2) ? createElement( 'span', {
-                style: {
-                    margin: '0 10px', // Spacing around the pipe
-                    color: '#ccc',    // Light gray color
-                    fontSize: '24px', // Size of the pipe
-                    lineHeight: '1',
-                    fontWeight: '300'
-                }
-            }, '|' ) : null,
+            // 2. Separator (use CSS class instead)
+            (settings.icon && settings.icon2) ? createElement( 'span', {
+                className: 'paybutton-blocks-separator'
+            }, '|' ) : null,

             // 3. The eCash Image
             settings.icon2 ? createElement( 'img', { 
                 src: settings.icon2, 
                 alt: 'eCash',
-                style: { 
-                    maxHeight: '24px', 
-                    objectFit: 'contain'
-                } 
+                style: { maxHeight: '24px', objectFit: 'contain' }
             } ) : null,

             // 4. Fallback: If no icons are found, show text
             (!settings.icon && !settings.icon2) ? createElement( 'span', null, labelText ) : null
         );
     };

     const Content = () => {
         return createElement( 'div', null, decodeEntities( settings.description || '' ) );
     };

     registerPaymentMethod( {
         name: 'paybutton',
         label: createElement( LabelIconOnly ),
         content: createElement( Content ),
         edit: createElement( Content ),
-        canMakePayment: () => true,
+        canMakePayment: () => {
+            // Add validation if needed
+            return true;
+        },
         ariaLabel: labelText,
         supports: {
             features: settings.supports,
         },
     } );
 })( window.wc, window.wp );
includes/woocommerce/class-wc-gateway-paybutton.php (1)

176-189: Consider adding JSON encoding flags for security.

When encoding the config for the data attribute, consider using JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT flags to prevent potential XSS through specially crafted attribute values.

🔎 Proposed fix
-        echo '<div class="paybutton-woo-container" data-config="' . esc_attr( json_encode( $config ) ) . '" style="margin: 20px 0;"></div>';
+        echo '<div class="paybutton-woo-container" data-config="' . esc_attr( wp_json_encode( $config ) ) . '" style="margin: 20px 0;"></div>';

Using wp_json_encode() is preferred in WordPress as it handles encoding errors gracefully and applies appropriate flags.

includes/woocommerce/class-paybutton-blocks-support.php (1)

36-46: Consider using dynamic versioning for cache busting.

The script version is hardcoded as '1.0.0'. Consider using the plugin version constant or filemtime() for better cache invalidation during development and updates.

🔎 Proposed fix
         wp_register_script(
             'wc-paybutton-blocks',
             PAYBUTTON_PLUGIN_URL . 'assets/js/paybutton-blocks.js',
             array( 'wc-blocks-registry', 'wc-settings', 'wp-element', 'wp-html-entities' ),
-            '1.0.0',
+            filemtime( PAYBUTTON_PLUGIN_DIR . 'assets/js/paybutton-blocks.js' ),
             true
         );
includes/class-paybutton-ajax.php (2)

357-361: Unused variables $tx_amount and $tx_timestamp - consider removing or using.

As flagged by static analysis, these variables are retrieved from POST but never used in mark_payment_successful(). If they're not needed for this flow, remove them to avoid confusion.

🔎 Proposed fix - if not needed
         $post_id      = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0;
         $tx_hash      = isset( $_POST['tx_hash'] ) ? sanitize_text_field( $_POST['tx_hash'] ) : '';
-        $tx_amount    = isset( $_POST['tx_amount'] ) ? (float) $_POST['tx_amount'] : 0.0;
-        $tx_timestamp = isset( $_POST['tx_timestamp'] ) ? sanitize_text_field( $_POST['tx_timestamp'] ) : '';
         // NEW: Address passed from front-end if user is not logged in
         $user_address = isset( $_POST['user_address'] ) ? sanitize_text_field( $_POST['user_address'] ) : '';

626-644: Redundancy confirmed—simplify to just is_paid().

The is_paid() method already returns true for both processing and completed statuses (its default behavior via wc_get_is_paid_statuses()). The explicit status checks are indeed redundant.

Simplify to:

if ( $order && $order->is_paid() ) {
    wp_send_json_success();
}

Note: Behavior is controlled by the filterable woocommerce_order_is_paid_statuses filter, so verify this matches your expected order statuses if custom filters are in use.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4932e57 and a289db4.

⛔ Files ignored due to path filters (1)
  • assets/icons/eCash.png is excluded by !**/*.png
📒 Files selected for processing (14)
  • assets/css/sticky-header.css
  • assets/js/addressValidator.bundle.js
  • assets/js/paybutton-blocks.js
  • assets/js/paybutton-generator.js
  • assets/js/paybutton-woo.js
  • includes/class-paybutton-admin.php
  • includes/class-paybutton-ajax.php
  • includes/class-paybutton-public.php
  • includes/class-paybutton-transactions.php
  • includes/woocommerce/class-paybutton-blocks-support.php
  • includes/woocommerce/class-wc-gateway-paybutton.php
  • paybutton.php
  • templates/admin/paywall-settings.php
  • templates/public/paybutton-overlay.php
💤 Files with no reviewable changes (1)
  • assets/js/paybutton-generator.js
🧰 Additional context used
🧬 Code graph analysis (4)
includes/woocommerce/class-paybutton-blocks-support.php (1)
includes/woocommerce/class-wc-gateway-paybutton.php (1)
  • is_available (195-201)
includes/woocommerce/class-wc-gateway-paybutton.php (3)
includes/class-paybutton-admin.php (1)
  • __construct (14-26)
includes/class-paybutton-ajax.php (1)
  • __construct (29-61)
includes/class-paybutton-public.php (1)
  • __construct (26-38)
paybutton.php (1)
includes/woocommerce/class-paybutton-blocks-support.php (1)
  • WC_PayButton_Blocks_Support (12-64)
includes/class-paybutton-ajax.php (1)
includes/class-paybutton-transactions.php (6)
  • verify_signature (19-52)
  • record_login_tx_if_new (212-245)
  • get_paywall_requirements (255-300)
  • validate_price_and_unit (144-162)
  • insert_unlock_if_new (168-205)
  • consume_row_and_attach_token (64-137)
🪛 PHPMD (2.15.0)
includes/class-paybutton-ajax.php

357-357: Avoid unused local variables such as '$tx_amount'. (undefined)

(UnusedLocalVariable)


358-358: Avoid unused local variables such as '$tx_timestamp'. (undefined)

(UnusedLocalVariable)

🔇 Additional comments (32)
assets/css/sticky-header.css (1)

120-122: LGTM! Proper scoping of sticky header padding.

Excellent change to scope the padding-top to only when the pb-has-sticky-header class is present. This provides better control over the sticky header behavior and prevents unintended layout shifts when the header is disabled.

templates/admin/paywall-settings.php (2)

140-157: LGTM! Properly implemented sticky header toggle.

The new sticky header disable checkbox is well-implemented:

  • Proper use of esc_html_e() for translation and escaping
  • Correct use of checked() helper with default value
  • Clear user-facing description
  • Consistent with existing settings patterns

278-278: Documentation update looks good.

The addition of the "value": <value> line to the sample JSON payload improves the documentation clarity.

includes/class-paybutton-public.php (2)

168-170: LGTM! Clean early return for disabled sticky header.

The early return pattern when paybutton_hide_sticky_header is enabled is clean and prevents unnecessary processing.


390-398: LGTM! Proper body class filtering logic.

The filter_body_classes method correctly adds the pb-has-sticky-header class conditionally based on the option value, which aligns with the CSS changes in sticky-header.css.

includes/class-paybutton-admin.php (2)

88-88: LGTM! Security improvement with wp_safe_redirect.

Excellent change from wp_redirect to wp_safe_redirect. This prevents potential open redirect vulnerabilities by validating the redirect URL against the allowed hosts list.


347-350: LGTM! Proper option persistence.

The new paybutton_hide_sticky_header option is saved correctly using the checkbox pattern (isset check converts to '1' or '0').

assets/js/paybutton-woo.js (2)

9-12: LGTM! Safe JSON parsing with error handling.

The defensive JSON parsing with try-catch and early return is appropriate for handling potentially malformed configuration data.


50-54: Verify user experience during payment verification.

When the PayButton modal closes after payment initiation, the overlay displays "Verifying Payment...". Consider the following UX improvements:

  1. What happens if the user navigates away or closes the tab during verification?
  2. Should there be a "Cancel" or "Check Status Later" option?
  3. Consider adding a timeout message or manual refresh option if polling takes too long.
templates/public/paybutton-overlay.php (1)

1-13: All CSS classes are properly defined and the spinner animation is complete.

The classes paybutton_overlay, paybutton_overlay_inner, paybutton_overlay_content, and paybutton_overlay_spinner are fully defined in assets/css/paywall-styles.css with complete styling and animations. The .paybutton_overlay_spinner includes a proper animated border-based spinner with the paybutton_spin keyframe animation (rotating 360 degrees infinitely).

The inline style="display:none;" is intentionally placed in the template for initial state visibility, with JavaScript toggling the display property when needed. This is a valid approach and does not require changes.

paybutton.php (5)

27-33: LGTM - HPOS compatibility properly declared.

The High-Performance Order Storage (HPOS) compatibility declaration is correctly implemented with the class_exists guard to prevent errors when WooCommerce is not installed.


44-48: LGTM - Conditional gateway file loading.

Proper guard with file_exists() before requiring the WooCommerce gateway file, ensuring graceful behavior when the file is missing.


79-87: LGTM - Gateway registration with class guard.

The gateway registration correctly checks class_exists('WC_Gateway_PayButton') before adding to the gateways array, preventing fatal errors when WooCommerce is not active.


89-107: LGTM - Blocks support registration with proper guards.

The implementation correctly:

  1. Hooks into woocommerce_blocks_payment_method_type_registration
  2. Checks for AbstractPaymentMethodType class existence
  3. Verifies the block support file exists before requiring
  4. Checks WC_PayButton_Blocks_Support class exists before instantiation

114-114: Good security improvement using wp_safe_redirect.

Using wp_safe_redirect() instead of wp_redirect() ensures the redirect URL is validated against the allowed hosts list, preventing potential open redirect vulnerabilities.

includes/woocommerce/class-wc-gateway-paybutton.php (3)

9-15: LGTM - Safe gateway initialization pattern.

Hooking into plugins_loaded and checking class_exists('WC_Payment_Gateway') before defining the class is the correct approach to ensure WooCommerce is fully loaded.


80-107: LGTM - Secure admin options processing.

Good implementation with:

  1. Proper WooCommerce settings nonce verification
  2. Sanitization of posted address
  3. Preventing gateway enablement without a wallet address configured

206-232: LGTM - Admin panel rendering with proper escaping.

Good use of esc_html(), esc_attr(), and esc_html__() for output escaping. The transaction hash is properly escaped when building the explorer URL.

includes/woocommerce/class-paybutton-blocks-support.php (3)

12-17: LGTM - Blocks support class structure.

The class correctly extends AbstractPaymentMethodType with a matching $name property. The empty initialize() method is acceptable as no initialization is required.


23-31: LGTM - Delegated availability check.

Correctly delegates the active state check to the gateway's is_available() method, ensuring consistency between classic and blocks checkout.


51-63: LGTM - Payment method data for blocks checkout.

Correctly provides fallback values when gateway is unavailable and includes both primary and secondary icon paths for the checkout UI.

includes/class-paybutton-transactions.php (6)

19-52: LGTM - Ed25519 signature verification.

The implementation correctly:

  1. Validates inputs are non-empty
  2. Handles DER-wrapped public keys (44 bytes → 32 bytes)
  3. Converts payload encoding for consistent verification
  4. Uses sodium_crypto_sign_verify_detached() for verification

59-62: LGTM - Secure token generation.

Uses random_bytes() with base64url encoding, providing cryptographically secure tokens suitable for replay protection.


144-162: LGTM - Price validation with epsilon tolerance.

The implementation correctly handles floating-point comparison with a reasonable epsilon of 0.05 and performs case-insensitive unit comparison.


168-205: LGTM - Idempotent unlock insertion.

Properly checks for existing transaction by tx_hash before inserting, preventing duplicate records.


212-245: LGTM - Idempotent login transaction recording.

Correctly prevents duplicate login records by checking both wallet_address and tx_hash.


255-300: LGTM - Paywall requirements parsing with fallbacks.

Good implementation that:

  1. Parses the first [paywalled_content] shortcode
  2. Extracts price and unit attributes
  3. Falls back to plugin options when attributes are missing
  4. Properly sanitizes and normalizes values
includes/class-paybutton-ajax.php (5)

57-60: LGTM - Order status polling endpoint registration.

Correctly registers both privileged and non-privileged AJAX actions for order status polling, consistent with the plugin's authentication model.


127-131: LGTM - Centralized signature verification.

Good refactor to use PayButton_Transactions::verify_signature() for consistent cryptographic verification across the plugin.


139-157: LGTM - Payload binding validation.

Good security hardening by verifying that the OP_RETURN value and transaction hash are cryptographically bound to the signed payload, preventing payload manipulation attacks.


191-228: LGTM - WooCommerce payment processing branch.

Solid implementation with:

  1. Idempotency check via is_paid()
  2. Fiat value validation with epsilon tolerance
  3. Informative order notes for underpayment and successful payment
  4. Proper meta data storage for transaction details

602-604: LGTM - Early return for hidden sticky header.

Good UX consideration to return empty HTML immediately when the sticky header is configured to be hidden, avoiding unnecessary template processing.

@xecdev xecdev linked an issue Dec 24, 2025 that may be closed by this pull request
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

♻️ Duplicate comments (2)
includes/class-paybutton-admin.php (1)

119-129: Hardcoded WooCommerce screen ID may break on translated installations.

The concern about 'woocommerce_page_wc-settings' potentially breaking on translated WordPress installations was already raised in a previous review. The recommended fix is to use sanitize_title( __( 'WooCommerce', 'woocommerce' ) ) . '_page_wc-settings' for the comparison.

includes/woocommerce/class-wc-gateway-paybutton.php (1)

112-123: Previous concern addressed - now using wc_reduce_stock_levels().

The deprecated $order->reduce_order_stock() has been correctly replaced with wc_reduce_stock_levels($order_id).

🧹 Nitpick comments (1)
includes/woocommerce/class-wc-gateway-paybutton.php (1)

188-189: Prefer wp_json_encode() over json_encode() for WordPress consistency.

wp_json_encode() handles encoding errors gracefully and is the WordPress-recommended approach.

🔎 Proposed fix
         echo '<h2>Complete your payment</h2>';
-        echo '<div class="paybutton-woo-container" data-config="' . esc_attr( json_encode( $config ) ) . '" style="margin: 20px 0;"></div>';
+        echo '<div class="paybutton-woo-container" data-config="' . esc_attr( wp_json_encode( $config ) ) . '" style="margin: 20px 0;"></div>';
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a289db4 and 7c24eec.

📒 Files selected for processing (3)
  • includes/class-paybutton-admin.php
  • includes/class-paybutton-transactions.php
  • includes/woocommerce/class-wc-gateway-paybutton.php
🔇 Additional comments (16)
includes/class-paybutton-admin.php (4)

88-88: Good security improvement using wp_safe_redirect.

This is the correct function to use for admin redirects as it validates the destination against the allowed hosts list, preventing open redirect vulnerabilities.


131-143: LGTM!

The refactor to use $screen->id for page detection is more reliable and consistent with the approach used for other pages.


145-145: LGTM!

Consistent use of screen ID-based detection for the generator page.


343-347: LGTM!

The new option follows the established pattern for persisting checkbox values in this file.

includes/class-paybutton-transactions.php (5)

59-62: LGTM!

Secure token generation using random_bytes() with base64url encoding is appropriate for this use case.


82-94: Column whitelist now correctly includes all required columns.

The previous concern about missing pb_paywall_user_wallet_address and post_id columns has been addressed.


151-169: LGTM!

The tolerance-based price validation allowing overpayment and case-insensitive unit comparison is appropriate for cryptocurrency payment flows.


219-252: LGTM!

Implementation correctly records login transactions with duplicate checking. Same TOCTOU consideration applies as noted for insert_unlock_if_new.


262-307: LGTM!

Clean implementation using shortcode_parse_atts() for parsing and proper fallback to plugin options.

includes/woocommerce/class-wc-gateway-paybutton.php (7)

17-42: LGTM!

Standard WooCommerce gateway initialization with appropriate hooks for admin options, thank-you page, scripts, and admin panel rendering.


47-75: LGTM!

Form fields are properly defined with appropriate types, defaults, and descriptions.


80-107: LGTM!

Good defensive validation preventing gateway activation without a configured wallet address, with proper nonce verification.


128-153: LGTM!

Good conditional script loading only when needed on the order-received page. Consider using a plugin version constant instead of hardcoded '1.0' on line 145 for cache-busting consistency.


195-201: LGTM!

Proper availability check ensuring the gateway only shows when enabled and configured with a wallet address.


206-232: LGTM!

Clean admin panel rendering with proper escaping and useful transaction details including explorer link.


12-15: LGTM!

Defining the gateway class inside the plugins_loaded callback is the correct pattern to ensure WC_Payment_Gateway is available before extending it.

@xecdev xecdev requested a review from Klakurka December 24, 2025 17:16
Copy link
Member

@Klakurka Klakurka left a comment

Choose a reason for hiding this comment

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

Should this button be working? Image

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 (1)
includes/class-paybutton-admin.php (1)

119-129: Translation-safe screen ID recommended (duplicate concern).

The hardcoded 'woocommerce_page_wc-settings' screen ID can break on translated WordPress installations where the menu slug changes. The past review comment on this code suggested using a computed screen ID for better translation safety.

While get_current_screen() is correctly used here, consider the suggested approach from the previous review if you need to support multilingual admin interfaces.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 425d5d0 and 3ad7ebe.

📒 Files selected for processing (2)
  • includes/class-paybutton-admin.php
  • templates/admin/dashboard.php
🔇 Additional comments (3)
includes/class-paybutton-admin.php (3)

88-88: Good security improvement.

Using wp_safe_redirect() instead of wp_redirect() ensures redirects are restricted to whitelisted hosts, preventing potential open redirect vulnerabilities.


195-196: LGTM!

The WooCommerce Payments URL correctly points to the standard WooCommerce checkout settings page and properly integrates with the dashboard template changes.


345-348: The form field exists and is properly configured in the settings template. The checkbox at lines 146-150 of templates/admin/paywall-settings.php has the correct name attribute paybutton_hide_sticky_header and the option retrieval uses get_option() with the same key. The save logic at lines 345-348 correctly handles the checkbox submission.

@xecdev
Copy link
Collaborator Author

xecdev commented Dec 25, 2025

@Klakurka Enabled the 'WooCommerce Payments' button in the dashboard.
image

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

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6959652 and 1b4dd53.

📒 Files selected for processing (1)
  • templates/admin/dashboard.php

@xecdev
Copy link
Collaborator Author

xecdev commented Dec 25, 2025

Added a check for the WooCommerce Payments button, to check if Woo is installed, if not the button will be disabled and will show an alert to install Woo first.
image

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

🧹 Nitpick comments (1)
includes/class-paybutton-admin.php (1)

164-176: WooCommerce settings detection improved but could be more translation-safe.

The dual-check approach ($hook_suffix OR $screen->base) is better than relying solely on $hook_suffix. However, both checks still use the hardcoded 'woocommerce_page_wc-settings' string, which can change on translated WordPress installations.

A previous review comment suggested using get_current_screen()->id === sanitize_title( __( 'WooCommerce', 'woocommerce' ) ) . '_page_wc-settings' for full translation safety. The current implementation partially addresses the concern with the fallback check but isn't fully translation-safe.

Based on learnings from past review comments, consider implementing the fully translation-safe approach:

🔎 Suggested translation-safe approach
-        // 3) WooCommerce → Settings → Payments (wallet address field)
-        if (
-            $hook_suffix === 'woocommerce_page_wc-settings'
-            || ( $screen && $screen->base === 'woocommerce_page_wc-settings' )
-        ) {
+        // 3) WooCommerce → Settings → Payments (wallet address field)
+        $wc_screen_id = $screen ? sanitize_title( __( 'WooCommerce', 'woocommerce' ) ) . '_page_wc-settings' : '';
+        if (
+            $hook_suffix === 'woocommerce_page_wc-settings'
+            || ( $screen && $screen->base === 'woocommerce_page_wc-settings' )
+            || ( $screen && $screen->id === $wc_screen_id )
+        ) {
             wp_enqueue_script(
                 'address-validator',
                 PAYBUTTON_PLUGIN_URL . 'assets/js/addressValidator.bundle.js',
                 array(),
                 '2.0.0',
                 true
             );
         }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b4dd53 and a4ef747.

📒 Files selected for processing (3)
  • assets/css/paybutton-admin.css
  • includes/class-paybutton-admin.php
  • templates/admin/dashboard.php
🚧 Files skipped from review as they are similar to previous changes (1)
  • templates/admin/dashboard.php
🔇 Additional comments (5)
assets/css/paybutton-admin.css (1)

85-89: LGTM! Clean utility class for disabled states.

The new .paybutton-disabled class provides a reusable way to indicate disabled states across the UI. The opacity and cursor values are appropriate and provide clear visual feedback to users.

includes/class-paybutton-admin.php (4)

88-88: Good security improvement using wp_safe_redirect.

Switching from wp_redirect to wp_safe_redirect adds URL validation to prevent potential open redirect vulnerabilities. This aligns with WordPress security best practices.


119-121: Good defensive programming with screen detection.

The function_exists check prevents potential fatal errors when get_current_screen() might not be available, such as during early admin hooks or AJAX requests.


202-204: Proper WooCommerce detection using standard approach.

Using class_exists('WooCommerce') is the recommended and standard way to detect WooCommerce installation. This aligns with WordPress and WooCommerce best practices.


352-356: LGTM! Consistent checkbox option handling.

The sticky header toggle persistence follows the same pattern as other checkbox options in this function (e.g., paybutton_hide_comments_until_unlocked at line 286). The implementation is consistent and correct.

@xecdev
Copy link
Collaborator Author

xecdev commented Dec 25, 2025

Ready for review.

@xecdev xecdev requested a review from Klakurka December 25, 2025 05:23
@xecdev xecdev changed the title Feat/implement-woo-payment-gateway Implement Woo Payment Gateway Dec 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WooCommerce support

3 participants