Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion assets/js/addressValidator.bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,7 @@

document.addEventListener('DOMContentLoaded', () => {
// Find the wallet address input field by its ID.
const addressInput = document.getElementById('pb_paywall_admin_wallet_address');
const addressInput = document.getElementById('paybutton_admin_wallet_address');
if (!addressInput) return;

// Find or create a span for validation feedback.
Expand Down
28 changes: 21 additions & 7 deletions includes/class-paybutton-activator.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,27 @@ public static function activate() {
}

private static function migrate_old_option() {
$old_value = get_option( 'paybutton_paywall_ecash_address', '' );
$new_value = get_option( 'pb_paywall_admin_wallet_address', '' );

// If old_value is present and new_value is empty, copy old to new and remove old.
if ( ! empty( $old_value ) && empty( $new_value ) ) {
update_option( 'pb_paywall_admin_wallet_address', $old_value );
delete_option( 'paybutton_paywall_ecash_address' );
// --- 1. admin wallet address ---
$old_admin_addr = get_option( 'pb_paywall_admin_wallet_address', '' );
$new_admin_addr = get_option( 'paybutton_admin_wallet_address', '' );
if ( ! empty( $old_admin_addr ) && empty( $new_admin_addr ) ) {
update_option( 'paybutton_admin_wallet_address', $old_admin_addr );
delete_option( 'pb_paywall_admin_wallet_address' );
}

// --- 2. unlocked‑indicator colours ---
$bg_old = get_option( 'unlocked_indicator_bg_color', '' );
$bg_new = get_option( 'paybutton_unlocked_indicator_bg_color', '' );
if ( ! empty( $bg_old ) && empty( $bg_new ) ) {
update_option( 'paybutton_unlocked_indicator_bg_color', $bg_old );
delete_option( 'unlocked_indicator_bg_color' );
}

$txt_old = get_option( 'unlocked_indicator_text_color', '' );
$txt_new = get_option( 'paybutton_unlocked_indicator_text_color', '' );
if ( ! empty( $txt_old ) && empty( $txt_new ) ) {
update_option( 'paybutton_unlocked_indicator_text_color', $txt_old );
delete_option( 'unlocked_indicator_text_color' );
}
}

Expand Down
18 changes: 9 additions & 9 deletions includes/class-paybutton-admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public function handle_save_settings() {
current_user_can( 'manage_options' )
) {
$this->save_settings();
wp_cache_delete( 'pb_paywall_admin_wallet_address', 'options' );
wp_cache_delete( 'paybutton_admin_wallet_address', 'options' );
wp_redirect( admin_url( 'admin.php?page=paybutton-paywall&settings-updated=true' ) );
exit;
}
Expand Down Expand Up @@ -198,7 +198,7 @@ public function paywall_settings_page() {

$args = array(
'settings_saved' => $settings_saved,
'admin_wallet_address' => get_option( 'pb_paywall_admin_wallet_address', '' ),
'admin_wallet_address' => get_option( 'paybutton_admin_wallet_address', '' ),
'default_price' => get_option( 'paybutton_paywall_default_price', 5.5 ),
'current_unit' => get_option( 'paybutton_paywall_unit', 'XEC' ),
'btn_text' => get_option( 'paybutton_text', 'Pay to Unlock' ),
Expand Down Expand Up @@ -230,7 +230,7 @@ public function admin_notice_missing_wallet_address() {
return;
}

$address = get_option('pb_paywall_admin_wallet_address', '');
$address = get_option('paybutton_admin_wallet_address', '');
if (empty($address)) {
echo '<div class="notice notice-error">';
echo '<p><strong>NOTICE:</strong> Please set your wallet address in <a href="' . esc_url(admin_url('admin.php?page=paybutton-paywall')) . '">Paywall Settings</a>. If you don\'t have an address yet, create a wallet using <a href="https://cashtab.com" target="_blank">Cashtab</a>, <a href="https://www.bitcoinabc.org/electrum/" target="_blank">Electrum ABC</a> or <a href="https://electroncash.org/" target="_blank">Electron Cash</a>.</p>';
Expand All @@ -242,7 +242,7 @@ public function admin_notice_missing_wallet_address() {
* Save settings submitted via the Paywall Settings page.
*/
private function save_settings() {
$address = sanitize_text_field( $_POST['pb_paywall_admin_wallet_address'] );
$address = sanitize_text_field( $_POST['paybutton_admin_wallet_address'] );
$unit = sanitize_text_field( $_POST['unit'] );
$raw_price = floatval( $_POST['default_price'] );
$button_text = sanitize_text_field( $_POST['paybutton_text'] );
Expand All @@ -251,14 +251,14 @@ private function save_settings() {
$color_secondary = sanitize_hex_color( $_POST['paybutton_color_secondary'] );
$color_tertiary = sanitize_hex_color( $_POST['paybutton_color_tertiary'] );
$hide_comments = isset( $_POST['paybutton_hide_comments_until_unlocked'] ) ? '1' : '0';
$unlocked_indicator_bg_color = sanitize_hex_color( $_POST['unlocked_indicator_bg_color'] );
$unlocked_indicator_text_color = sanitize_hex_color( $_POST['unlocked_indicator_text_color'] );
$paybutton_unlocked_indicator_bg_color = sanitize_hex_color( $_POST['paybutton_unlocked_indicator_bg_color'] );
$paybutton_unlocked_indicator_text_color = sanitize_hex_color( $_POST['paybutton_unlocked_indicator_text_color'] );

if ( $unit === 'XEC' && $raw_price < 5.5 ) {
$raw_price = 5.5;
}

update_option( 'pb_paywall_admin_wallet_address', $address );
update_option( 'paybutton_admin_wallet_address', $address );
update_option( 'paybutton_paywall_unit', $unit );
update_option( 'paybutton_paywall_default_price', $raw_price );
update_option( 'paybutton_text', $button_text );
Expand All @@ -278,8 +278,8 @@ private function save_settings() {
// New unlocked content indicator option:
update_option( 'paybutton_scroll_to_unlocked', isset( $_POST['paybutton_scroll_to_unlocked'] ) ? '1' : '0' );
// Default to #007bff for background, #ffffff for text
update_option('unlocked_indicator_bg_color', $unlocked_indicator_bg_color ?: '#007bff');
update_option('unlocked_indicator_text_color', $unlocked_indicator_text_color ?: '#ffffff');
update_option('paybutton_unlocked_indicator_bg_color', $paybutton_unlocked_indicator_bg_color ?: '#007bff');
update_option('paybutton_unlocked_indicator_text_color', $paybutton_unlocked_indicator_text_color ?: '#ffffff');

// Save the blacklist
if ( isset( $_POST['paybutton_blacklist'] ) ) {
Expand Down
73 changes: 49 additions & 24 deletions includes/class-paybutton-ajax.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,49 @@ public function __construct() {
* It validates the request using a cryptographic signature to ensure authenticity.
*/
public function payment_trigger() {
/* Note to reviewers:
* This endpoint is called by PayButton.org’s server.
* A wp_nonce cannot be used here (no WP session).
* We instead verify a cryptographic Ed25519 signature, which guarantees authenticity.
/* Note to reviewers:
* A wp_nonce cannot be used here (no WP session).
* We instead verify a cryptographic Ed25519 signature, which guarantees authenticity.
* The PayButton server POSTS JSON (`Content‑Type: application/json`), so `$_POST`
* is empty and we must read php://input and sanitize data.
* Reading the raw body – capped at 4 KB (DoS protection) so we never process an
* arbitrary‑size payload
*/
// Read the raw request body
$raw_post_data = file_get_contents('php://input');
$max_bytes = 4096; // 4 KB is more than enough
$raw_post_data = file_get_contents(
'php://input',
false,
null,
0,
$max_bytes + 1 // read one extra byte → oversize flag
);

// Decode JSON data
$json_data = json_decode($raw_post_data, true);
if (!$json_data || !isset($json_data['signature']['signature']) || !isset($json_data['signature']['payload'])) {
wp_send_json_error(['message' => 'Invalid JSON format or missing signature.']);
if ( strlen( $raw_post_data ) > $max_bytes ) {
wp_send_json_error( array( 'message' => 'Payload too large.' ), 413 );
return;
}

//Decode JSON and copy ONLY the fields we actually use

$json = json_decode( $raw_post_data, true );

if ( ! is_array( $json ) ) {
wp_send_json_error( array( 'message' => 'Malformed JSON.' ), 400 );
return;
}

$signature = $json['signature']['signature'] ?? '';
$payload = $json['signature']['payload'] ?? ''; // This is the signed data
$post_id_raw = $json['post_id']['rawMessage'] ?? 0;
$tx_hash_raw = $json['tx_hash'] ?? '';
$tx_amount_raw = $json['tx_amount'] ?? '';
$ts_raw = $json['tx_timestamp'] ?? 0;
$user_addr_raw = $json['user_address'][0] ?? '';

unset( $json ); // discard the rest immediately

if ( empty( $signature ) || empty( $payload ) || empty( $post_id_raw ) || empty( $user_addr_raw ) ) {
wp_send_json_error( array( 'message' => 'Required fields missing.' ), 400 );
return;
}

Expand All @@ -68,28 +99,22 @@ public function payment_trigger() {
return;
}

// Extract signature and payload from nested JSON
$signature = $json_data['signature']['signature'];
$payload = $json_data['signature']['payload']; // This is the signed data

// Verify the signature
$verification_result = $this->verify_signature($payload, $signature, $public_key);
if (!$verification_result) {
wp_send_json_error(['message' => 'Signature verification failed.']);
return;
}

// Extract post_id from 'post_id' -> 'rawMessage'
$post_id = isset($json_data['post_id']['rawMessage']) ? intval($json_data['post_id']['rawMessage']) : 0;

// Extract transaction details
$tx_hash = $json_data['tx_hash'] ?? '';
$tx_amount = $json_data['tx_amount'] ?? '';
$tx_timestamp = $json_data['tx_timestamp'] ?? '';
$user_address = $json_data['user_address'][0] ?? '';

//Sanitize data
$post_id = intval( $post_id_raw );
$tx_hash = sanitize_text_field( $tx_hash_raw );
$tx_amount = sanitize_text_field( $tx_amount_raw );
$tx_timestamp = intval( $ts_raw );
$user_address = sanitize_text_field( $user_addr_raw );

// Convert timestamp to MySQL datetime
$mysql_timestamp = is_numeric($tx_timestamp) ? gmdate('Y-m-d H:i:s', intval($tx_timestamp)) : '0000-00-00 00:00:00';
$mysql_timestamp = $tx_timestamp ? gmdate('Y-m-d H:i:s', $tx_timestamp) : '0000-00-00 00:00:00';

if ($post_id > 0 && !empty($user_address)) {
$is_logged_in = 0;
Expand Down
16 changes: 8 additions & 8 deletions includes/class-paybutton-public.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ public function enqueue_public_assets() {
wp_enqueue_style( 'paywall-styles', PAYBUTTON_PLUGIN_URL . 'assets/css/paywall-styles.css', array(), '1.0' );

// Read the admin-chosen colors for the unlocked content indicator from options
$indicator_bg_color = get_option('unlocked_indicator_bg_color', '#007bff');
$indicator_text_color = get_option('unlocked_indicator_text_color', '#ffffff');
$indicator_bg_color = get_option('paybutton_unlocked_indicator_bg_color', '#007bff');
$indicator_text_color = get_option('paybutton_unlocked_indicator_text_color', '#ffffff');

// Add inline CSS variables.
$custom_css = "
Expand All @@ -59,7 +59,7 @@ public function enqueue_public_assets() {
--pb-unlocked-text: {$indicator_text_color};
}
";
wp_add_inline_style( 'paybutton-sticky-header', $custom_css );
wp_add_inline_style( 'paybutton-sticky-header', esc_attr( $custom_css ) );

// Enqueue the PayButton core script.
wp_enqueue_script(
Expand Down Expand Up @@ -113,7 +113,7 @@ public function enqueue_public_assets() {
'nonce' => wp_create_nonce( 'paybutton_paywall_nonce' ),
'isUserLoggedIn' => ! empty( $_SESSION['pb_paywall_user_wallet_address'] ) ? 1 : 0,
'userAddress' => ! empty( $_SESSION['pb_paywall_user_wallet_address'] ) ? sanitize_text_field( $_SESSION['pb_paywall_user_wallet_address'] ) : '',
'defaultAddress' => get_option( 'pb_paywall_admin_wallet_address', '' ),
'defaultAddress' => get_option( 'paybutton_admin_wallet_address', '' ),
'scrollToUnlocked' => get_option( 'paybutton_scroll_to_unlocked', '1' ),
) );
}
Expand All @@ -129,12 +129,12 @@ public function paybutton_generator_shortcode( $atts, $content = null ) {
$decoded = [];
}

// Safely encode the config for a data attribute
$encodedConfig = esc_attr( wp_json_encode( $decoded ) );
// Encode the config for a data attribute
$encodedConfig = wp_json_encode( $decoded );

ob_start();
?>
<div class="paybutton-shortcode-container" data-config="<?php echo $encodedConfig; ?>"></div>
<div class="paybutton-shortcode-container" data-config="<?php echo esc_attr($encodedConfig); ?>"></div>
<?php
return ob_get_clean();
}
Expand Down Expand Up @@ -189,7 +189,7 @@ public function paybutton_paywall_shortcode( $atts, $content = null ) {

$atts = shortcode_atts( array(
'price' => $default_price,
'address' => get_option( 'pb_paywall_admin_wallet_address', '' ),
'address' => get_option( 'paybutton_admin_wallet_address', '' ),
'unit' => $default_unit,
'button_text' => $default_text,
'hover_text' => $default_hover,
Expand Down
20 changes: 0 additions & 20 deletions paybutton.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,26 +50,6 @@
session_start();
}

/**
* Migrate any existing session data from the old user-wallet key
* ("cashtab_ecash_address") to the new key ("pb_paywall_user_wallet_address").
*/
if ( ! empty( $_SESSION['cashtab_ecash_address'] ) ) {
$_SESSION['pb_paywall_user_wallet_address'] = $_SESSION['cashtab_ecash_address'];
unset( $_SESSION['cashtab_ecash_address'] );
}

/**
* Migrate any existing cookie data from the old cookie
* ("cashtab_ecash_address") to the new cookie name
* ("pb_paywall_user_wallet_address") for session usage.
*/
if ( ! empty( $_COOKIE['pb_paywall_user_wallet_address'] ) ) {
$_SESSION['pb_paywall_user_wallet_address'] = sanitize_text_field( $_COOKIE['pb_paywall_user_wallet_address'] );
} elseif ( ! empty( $_COOKIE['cashtab_ecash_address'] ) ) {
$_SESSION['pb_paywall_user_wallet_address'] = sanitize_text_field( $_COOKIE['cashtab_ecash_address'] );
}

// Initialize admin functionality if in admin area.
if ( is_admin() ) {
new PayButton_Admin();
Expand Down
4 changes: 2 additions & 2 deletions templates/admin/dashboard.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@
<div class="paybutton-dashboard-buttons">
<!-- Button 1: Generate Button -->
<div class="paybutton-dashboard-button">
<a href="<?php echo $generate_button_url; ?>" class="button button-primary paybutton-dashboard-link">
<a href="<?php echo esc_url( $generate_button_url ); ?>" class="button button-primary paybutton-dashboard-link">
PayButton Generator
</a>
</div>
<!-- Button 2: Paywall Settings -->
<div class="paybutton-dashboard-button">
<a href="<?php echo $paywall_settings_url; ?>" class="button button-primary paybutton-dashboard-link">
<a href="<?php echo esc_url( $paywall_settings_url ); ?>" class="button button-primary paybutton-dashboard-link">
Paywall Settings
</a>
</div>
Expand Down
2 changes: 1 addition & 1 deletion templates/admin/paybutton-generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

//Get admin's wallet address from paywall settings
$admin_address = get_option( 'pb_paywall_admin_wallet_address', '' );
$admin_address = get_option( 'paybutton_admin_wallet_address', '' );
?>

<div class="wrap">
Expand Down
20 changes: 10 additions & 10 deletions templates/admin/paywall-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
<?php wp_nonce_field( 'paybutton_paywall_settings', 'paybutton_settings_nonce' ); ?>
<table class="form-table">
<tr>
<th scope="row"><label for="pb_paywall_admin_wallet_address">Wallet Address (required)</label></th>
<th scope="row"><label for="paybutton_admin_wallet_address">Wallet Address (required)</label></th>
<td>
<!-- Using the new $admin_wallet_address variable -->
<input type="text" name="pb_paywall_admin_wallet_address" id="pb_paywall_admin_wallet_address" class="regular-text" value="<?php echo esc_attr( $admin_wallet_address ); ?>" required>
<input type="text" name="paybutton_admin_wallet_address" id="paybutton_admin_wallet_address" class="regular-text" value="<?php echo esc_attr( $admin_wallet_address ); ?>" required>
<!-- This span will be populated by our bundled address validator JS -->
<span id="adminAddressValidationResult"></span>
<p class="description">Enter your wallet address to receive paywall payments.</p>
Expand Down Expand Up @@ -82,27 +82,27 @@
<tbody id="unlockedIndicatorColors">
<tr>
<th scope="row">
<label for="unlocked_indicator_bg_color">Background Color</label>
<label for="paybutton_unlocked_indicator_bg_color">Background Color</label>
</th>
<td>
<input type="color" name="unlocked_indicator_bg_color" id="unlocked_indicator_bg_color"
value="<?php echo esc_attr( get_option('unlocked_indicator_bg_color', '#007bff') ); ?>">
<input type="color" name="paybutton_unlocked_indicator_bg_color" id="paybutton_unlocked_indicator_bg_color"
value="<?php echo esc_attr( get_option('paybutton_unlocked_indicator_bg_color', '#007bff') ); ?>">
<button type="button"
onclick="document.getElementById('unlocked_indicator_bg_color').value = '#007bff';">
onclick="document.getElementById('paybutton_unlocked_indicator_bg_color').value = '#007bff';">
Reset
</button>
<p class="description">Controls the background color of the indicator.</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="unlocked_indicator_text_color">Text Color</label>
<label for="paybutton_unlocked_indicator_text_color">Text Color</label>
</th>
<td>
<input type="color" name="unlocked_indicator_text_color" id="unlocked_indicator_text_color"
value="<?php echo esc_attr( get_option('unlocked_indicator_text_color', '#ffffff') ); ?>">
<input type="color" name="paybutton_unlocked_indicator_text_color" id="paybutton_unlocked_indicator_text_color"
value="<?php echo esc_attr( get_option('paybutton_unlocked_indicator_text_color', '#ffffff') ); ?>">
<button type="button"
onclick="document.getElementById('unlocked_indicator_text_color').value = '#ffffff';">
onclick="document.getElementById('paybutton_unlocked_indicator_text_color').value = '#ffffff';">
Reset
</button>
<p class="description">Controls the text color of the indicator.</p>
Expand Down
Loading