commit b36819e17f09384551a3c7b4566b8d276e692c29
parent 6f38c6bc653a664391fb9bf543f5ae95b8b38eec
Author: Christian Grothoff <christian@grothoff.org>
Date: Sun, 2 Nov 2025 13:26:39 +0100
clean up code a bit
Diffstat:
4 files changed, 71 insertions(+), 58 deletions(-)
diff --git a/README.md b/README.md
@@ -1,10 +1,12 @@
# GNU Taler Turnstile for WordPress
-A WordPress plugin that adds price fields to posts and requires payment via GNU Taler for access.
+A WordPress plugin that adds price fields to posts and requires
+payment via GNU Taler for access.
## Description
-GNU Taler Turnstile enables content creators to monetize their WordPress content using the GNU Taler payment system. The plugin allows you to:
+GNU Taler Turnstile enables authors to monetize their WordPress
+articles using the GNU Taler payment system. The plugin allows you to:
- Set prices for individual posts/pages
- Create price categories with different pricing tiers
@@ -14,44 +16,52 @@ GNU Taler Turnstile enables content creators to monetize their WordPress content
## Installation
-1. Download `qrcode.min.js` from https://github.com/davidshimjs/qrcodejs and place it in `assets/js/qrcode.min.js`
-2. Upload the `taler-turnstile` folder to the `/wp-content/plugins/` directory
-3. Activate the plugin through the 'Plugins' menu in WordPress
-4. Configure your GNU Taler merchant backend in Settings > Taler Turnstile
-5. Create price categories under Taler Prices
+1. Upload the `taler-turnstile` folder to the `/wp-content/plugins/` directory
+2. Activate the plugin through the `Plugins` menu in WordPress
+3. Configure your GNU Taler merchant backend in `Settings` > `Taler Turnstile`
+4. Create price categories under `Taler Prices`
## Configuration
### Basic Settings
-1. Navigate to **Settings > Taler Turnstile**
+0. Make sure you have the credentials of your Taler merchant backend at hand
+1. Navigate to `Settings` > `Taler Turnstile`
2. Select which post types should support paid content
3. Configure your payment backend URL (must end with `/`)
4. Enter your access token (must start with `secret-token:`)
5. Optionally enable "Disable Turnstile on Error" for graceful degradation
6. Save settings - this will automatically add price category fields to selected post types
-### Subscription Prices
+### Subscription Prices (optional)
After configuring the basic settings:
-1. Navigate to **Settings > Taler Subscriptions**
-2. Set prices for each subscription type in different currencies
+0. Make sure you have subscriptions configured in the Taler merchant backend
+1. Navigate to `Settings` > `Taler Subscriptions`
+2. Set prices for buying each subscription type in different currencies
3. Leave fields empty to prevent purchasing that subscription with that currency
4. Save subscription prices
### Price Categories
-1. Navigate to **Taler Prices**
+1. Navigate to `Taler Prices`
2. Click "Add New" to create a price category
-3. Set prices for different subscription types and currencies
+3. Set prices for users without a subscription and for different subscription types and currencies
+ (leave blank to not allow buying with that currency, use 0 if the article is 100% included
+ in the subscription).
4. Save the category
-5. Assign price categories to individual posts/pages via the meta box in the editor
+
+### Sell articles
+
+1. Select `Edit` on an individual post or page you want to monetize
+2. Assign a price category to the individual post/page via the meta box in the editor
+3. Save the article
## Requirements
- WordPress 5.0 or higher
-- PHP 7.4 or higher
+- PHP 8.2 or higher
- GNU Taler merchant backend instance
## File Structure
@@ -83,20 +93,20 @@ taler-turnstile/
## Features
- **Content Paywall**: Automatically restricts access to paid content, showing excerpt + payment button
+- **Payment Link**: Opens the payment dialog in the merchant backend, which can automatically trigger the Taler WebExtension wallet
- **QR Code Payment**: Generates QR codes for easy mobile wallet payments
- **Payment Polling**: Real-time payment status checking with automatic page reload on completion
-- **Session Management**: Tracks paid access and active subscriptions per visitor session
+- **Session Management**: Tracks paid access and active subscriptions in per visitor session
- **Flexible Pricing**: Set prices per subscription type and currency
-- **Multiple Currencies**: Support for EUR, USD, CHF, and more (fetched from backend)
+- **Multiple Currencies**: Support for EUR, USD, CHF, and more (fetched from GNU Taler merchant backend)
- **Subscription Support**: Offer subscription-based access with token families
-- **Subscription Pricing**: Configure prices for purchasing subscriptions
+- **Subscription Pricing**: Configure prices for purchasing subscriptions (user can buy article or subscribe and pay discounted price for the article)
- **Zero-Price Subscriptions**: Automatically grant access for subscriptions with zero price
- **Post Type Support**: Enable paid content for any public post type
-- **Dynamic Field Management**: Automatically adds/removes price category fields when post types are enabled/disabled
+- **Dynamic Field Management**: Automatically adds/removes price category fields when post types are enabled/disabled2
- **Meta Box Integration**: Easy price category selection in the post editor
- **Error Handling**: Graceful degradation when backend is unavailable
- **Cache Control**: Automatically disables caching for protected content
-- **WordPress Standards**: Follows WordPress coding standards and best practices
- **Caching**: Efficient caching of backend data using WordPress transients
## How It Works
@@ -105,19 +115,27 @@ taler-turnstile/
1. **View Protected Content**: When a visitor views a post with a price category assigned, they see:
- An excerpt/teaser of the content
- - A payment button with QR code
- - Payment options for different currencies and subscriptions
+ - A payment button with QR code and a link for in-browser payments with GNU Taler
2. **Make Payment**: The visitor can:
- Scan the QR code with their Taler wallet app
- Click the payment button to open their wallet
+ - They will see payment options:
+ + pay full amount for individual article
+ + pay discounted amount with subscriptions (if they already have
+ a subscription that results in a discount)
+ + buy subscription
- Choose their preferred payment option (currency and subscription type)
+ - If they are already a subscriber with a discounted price of zero,
+ the wallet will automatically choose and execute that option
3. **Automatic Access**: Once payment is completed:
- The page automatically refreshes (via polling)
- Full content is displayed
- Access is stored in the session
- If a subscription was purchased, it's tracked for future visits
+ in this session and user only needs the wallet again after the
+ session ends
### For Administrators
@@ -157,8 +175,9 @@ For issues and questions:
## License
-This plugin is licensed under GPL v3 or later.
+This plugin is licensed under GNU GPL v3 or later.
## Credits
-Based on the GNU Taler Turnstile module for Drupal.
+The code is based on an initial AI-assisted transformation of
+the GNU Taler Turnstile module for Drupal.
diff --git a/WORKFLOW.md b/WORKFLOW.md
@@ -1,6 +1,7 @@
# GNU Taler Turnstile Workflow
-This document explains the complete workflow of the Taler Turnstile WordPress plugin.
+This document explains the complete workflow of the Taler Turnstile
+WordPress plugin.
## Setup Phase
@@ -13,7 +14,9 @@ This document explains the complete workflow of the Taler Turnstile WordPress pl
- Admin enters payment backend URL (must end with `/`)
- Admin enters access token (must start with `secret-token:`)
- Settings are validated against the actual backend
-- Backend connection is tested via HTTP requests
+- Backend connection is tested via HTTP requests to public
+ /config endpoint to see if we have a merchant backend, and to
+ "GET /private/orders" to see if the access token is valid.
### 3. Post Type Selection
- Admin selects which post types should support paid content
@@ -23,7 +26,8 @@ This document explains the complete workflow of the Taler Turnstile WordPress pl
### 4. Subscription Price Configuration
**Settings > Taler Subscriptions**
-- Page only accessible after backend is configured
+- Page only accessible after backend is configured as we need
+ to download the list of subscription types from the backend
- Lists all subscription types from the backend
- Admin sets purchase price for each subscription in each currency
- Empty price = subscription cannot be purchased in that currency
@@ -51,18 +55,17 @@ When a visitor views a protected post:
```
1. WordPress loads the post
-2. the_content filter is triggered
-3. Taler_Content_Filter::filter_content() runs
+2. Content filter is triggered
+3. Taler_Content_Filter::filter_content() checks:
- Checks performed:
- Is this a singular post view? (not archive/search)
- - Is this post type enabled?
- - Does the post have a price category?
- - Does visitor have a subscription granting zero-price access?
- - Does visitor's session already have access?
+ - Is Turnstile enabled for this post type?
+ - Is the post have a price category and is thus not gratis?
+ - Does visitor lack a subscription granting zero-price access?
+ - Does visitor's session not already have access?
- If all checks pass → show full content
- If any check fails → proceed to paywall logic
+ If all checks pass → proceed to paywall logic
+ If any check fails → show full content
```
### 8. Order Management
@@ -119,7 +122,7 @@ Content is replaced with:
<!-- Button section -->
<div class="taler-button-section">
- <a href="taler://pay/..." class="taler-pay-button">
+ <a href="https://$BACKEND/orders/$ORDER_ID" class="taler-pay-button">
Pay with GNU Taler
</a>
</div>
@@ -134,10 +137,9 @@ Content is replaced with:
1. Find all .taler-turnstile-qr-code-container elements
2. Extract payment-url and session-id from data attributes
3. Convert HTTP URL to Taler URI format:
- - https://backend/orders/ABC → taler://pay/backend/ABC/session-id
- - http://backend/orders/ABC → taler+http://pay/backend/ABC/session-id
+ - https://$BACKEND/orders/$ORDER_ID → taler://pay/$BACKEND/$ORDER_ID/$SESSION_ID
+ - http://$BACKEND/orders/$ORDER_ID → taler+http://pay/$BACKEND/$ORDER_ID/$SESSION_ID
4. Generate QR code using QRCode.js
-5. Update button href to use Taler URI
```
### 11. Payment Polling
@@ -167,9 +169,9 @@ function pollPaymentStatus(paymentUrl, sessionId) {
```
1. Taler wallet communicates with backend
2. Backend marks order as paid
-3. Backend may issue subscription token
+3. Backend may issue subscription token to wallet
4. Next poll request returns 200 OK
-5. JavaScript detects 200 and reloads page
+5. JavaScript detects 200 OK and reloads page
```
**On page reload:**
@@ -227,13 +229,6 @@ If grant_access_on_error = false:
- Do not show content
```
-### Order Creation Fails
-```
-- Check grant_access_on_error setting
-- Either show error or grant access
-- Log detailed error with HTTP status
-```
-
### Payment Polling Errors
```
- Non-402 HTTP errors: Wait 5 seconds, retry
@@ -247,4 +242,3 @@ If grant_access_on_error = false:
### Session Security
- Session IDs are hashed (SHA-256) before sending to backend
- Backend cannot correlate sessions across sites
-- Session data i
-\ No newline at end of file
diff --git a/admin/class-admin-settings.php b/admin/class-admin-settings.php
@@ -16,7 +16,6 @@ class Taler_Turnstile_Admin_Settings {
}
public function register_settings() {
- // Register settings
register_setting('taler_turnstile_settings', 'taler_turnstile_enabled_post_types', array(
'type' => 'array',
'sanitize_callback' => array($this, 'sanitize_post_types')
@@ -56,7 +55,6 @@ class Taler_Turnstile_Admin_Settings {
'taler_turnstile_settings'
);
- // Add settings fields
add_settings_field(
'enabled_post_types',
__('Enabled Post Types', 'taler-turnstile'),
@@ -264,6 +262,9 @@ class Taler_Turnstile_Admin_Settings {
}
private function validate_http_status($http_status) {
+ if ($http_status !== 200 && $http_status !== 204) {
+ return;
+ }
$messages = array(
502 => __('Bad gateway (502) trying to access the merchant backend', 'taler-turnstile'),
500 => __('Internal server error (500) of the merchant backend reported', 'taler-turnstile'),
@@ -280,7 +281,7 @@ class Taler_Turnstile_Admin_Settings {
$messages[$http_status],
'error'
);
- } elseif ($http_status !== 200 && $http_status !== 204) {
+ } else {
add_settings_error(
'taler_turnstile_settings',
'backend_error',
diff --git a/admin/class-subscription-prices-admin.php b/admin/class-subscription-prices-admin.php
@@ -58,11 +58,12 @@ class Taler_Subscription_Prices_Admin {
return;
}
- // Check if backend is configured
$backend_url = get_option('taler_turnstile_payment_backend_url');
$access_token = get_option('taler_turnstile_access_token');
- if (empty($backend_url) || empty($access_token)) {
+ if (empty($backend_url) || empty($access_token) ||
+ (0 == Taler_Merchant_API::check_access($backend_url,
+ $access_token))) {
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
@@ -82,7 +83,6 @@ class Taler_Subscription_Prices_Admin {
return;
}
- // Get subscriptions and currencies
$subscriptions = Taler_Merchant_API::get_subscriptions();
$currencies = Taler_Merchant_API::get_currencies();
@@ -182,7 +182,7 @@ class Taler_Subscription_Prices_Admin {
<?php endforeach; ?>
<p class="submit">
- <input type="submit" name="submit" id="submit" class="button button-primary" value="<?php esc_attr_e('Save Subscription Prices', 'taler-turnstile'); ?>">
+ <input type="submit" name="submit" id="submit" class="button button-primary" value="<?php esc_attr_e('Save', 'taler-turnstile'); ?>">
</p>
</form>
</div>