WORKFLOW.md (6951B)
1 # GNU Taler Turnstile Workflow 2 3 This document explains the complete workflow of the Taler Turnstile 4 WordPress plugin. 5 6 ## Setup Phase 7 8 ### 1. Plugin Installation 9 - Admin uploads and activates the plugin 10 - Default options are created (post type = 'post', grant_access_on_error = false) 11 12 ### 2. Backend Configuration 13 **Settings > Taler Turnstile** 14 - Admin enters payment backend URL (must end with `/`) 15 - Admin enters access token (must start with `secret-token:`) 16 - Settings are validated against the actual backend 17 - Backend connection is tested via HTTP requests to public 18 /config endpoint to see if we have a merchant backend, and to 19 "GET /private/orders" to see if the access token is valid. 20 21 ### 3. Post Type Selection 22 - Admin selects which post types should support paid content 23 - When enabled: price category meta field is automatically added 24 - When disabled: price category meta field is automatically removed 25 - Field appears as a meta box in the post editor sidebar 26 27 ### 4. Subscription Price Configuration 28 **Settings > Taler Subscriptions** 29 - Page only accessible after backend is configured as we need 30 to download the list of subscription types from the backend 31 - Lists all subscription types from the backend 32 - Admin sets purchase price for each subscription in each currency 33 - Empty price = subscription cannot be purchased in that currency 34 35 ### 5. Price Category Creation 36 **Taler Prices > Add New** 37 - Admin creates named price categories (e.g., "Premium", "Standard") 38 - For each subscription type and currency combination, set the access price 39 - Empty price = cannot pay with that combination 40 - Zero price = subscription grants full access without payment 41 42 ## Content Publishing Phase 43 44 ### 6. Content Creation 45 - Editor creates/edits a post of an enabled post type 46 - In the sidebar meta box, selects a price category 47 - Saves the post 48 - The price category ID is stored as post meta 49 50 ## Visitor Access Phase 51 52 ### 7. Initial Page View 53 54 When a visitor views a protected post: 55 56 ``` 57 1. WordPress loads the post 58 2. Content filter is triggered 59 3. Taler_Content_Filter::filter_content() checks: 60 61 - Is this a singular post view? (not archive/search) 62 - Is Turnstile enabled for this post type? 63 - Is the post have a price category and is thus not gratis? 64 - Does visitor lack a subscription granting zero-price access? 65 - Does visitor's session not already have access? 66 67 If all checks pass → proceed to paywall logic 68 If any check fails → show full content 69 ``` 70 71 ### 8. Order Management 72 73 **Check for existing order:** 74 ``` 75 - Look in session: $_SESSION['taler_turnstile_node_orders'][$post_id] 76 - If found: 77 - Query backend for order status 78 - If paid → grant session access, show full content 79 - If expired → ignore, create new order 80 - If still valid → reuse existing order 81 - If not found or invalid: 82 - Create new order via backend API 83 - Store order info in session 84 ``` 85 86 **Order Creation:** 87 ``` 88 POST /private/orders 89 { 90 "order": { 91 "version": 1, 92 "choices": [...], // From price category 93 "summary": "Access to: Post Title", 94 "fulfillment_url": "https://example.com/post-slug/", 95 "pay_deadline": {...} 96 }, 97 "session_id": "hashed-session-id", 98 "create_token": false 99 } 100 101 Response: { "order_id": "..." } 102 ``` 103 104 ### 9. Paywall Display 105 106 Content is replaced with: 107 ```html 108 <div class="taler-turnstile-paywall"> 109 <!-- Post excerpt --> 110 <div class="taler-turnstile-excerpt">...</div> 111 112 <!-- Payment interface --> 113 <div class="taler-payment-container"> 114 <!-- QR Code section --> 115 <div class="taler-qr-section"> 116 <div class="taler-turnstile-qr-code-container" 117 data-payment-url="..." 118 data-session-id="..."> 119 <!-- QR code generated by JavaScript --> 120 </div> 121 </div> 122 123 <!-- Button section --> 124 <div class="taler-button-section"> 125 <a href="https://$BACKEND/orders/$ORDER_ID" class="taler-pay-button"> 126 Pay with GNU Taler 127 </a> 128 </div> 129 </div> 130 </div> 131 ``` 132 133 ### 10. QR Code Generation 134 135 **Client-side (payment-button.js):** 136 ```javascript 137 1. Find all .taler-turnstile-qr-code-container elements 138 2. Extract payment-url and session-id from data attributes 139 3. Convert HTTP URL to Taler URI format: 140 - https://$BACKEND/orders/$ORDER_ID → taler://pay/$BACKEND/$ORDER_ID/$SESSION_ID 141 - http://$BACKEND/orders/$ORDER_ID → taler+http://pay/$BACKEND/$ORDER_ID/$SESSION_ID 142 4. Generate QR code using QRCode.js 143 ``` 144 145 ### 11. Payment Polling 146 147 **Automatic polling starts:** 148 ```javascript 149 function pollPaymentStatus(paymentUrl, sessionId) { 150 // Long-poll with 30-second timeout 151 GET paymentUrl?timeout_ms=30000&session_id=sessionId 152 153 Responses: 154 - 200/202: Payment complete → reload page 155 - 402: Payment pending → continue polling 156 - Timeout: Network timeout → retry polling 157 - Error: Wait 5 seconds → retry polling 158 } 159 ``` 160 161 **Polling prevents:** 162 - Rapid loops (enforces minimum delay between requests) 163 - Server hammering (uses long-polling with timeout_ms) 164 - Infinite errors (retries with backoff on non-402 errors) 165 166 ### 12. Payment Completion 167 168 **When visitor pays via wallet:** 169 ``` 170 1. Taler wallet communicates with backend 171 2. Backend marks order as paid 172 3. Backend may issue subscription token to wallet 173 4. Next poll request returns 200 OK 174 5. JavaScript detects 200 OK and reloads page 175 ``` 176 177 **On page reload:** 178 ``` 179 1. filter_content() runs again 180 2. Finds existing order in session 181 3. Checks order status with backend 182 4. Backend returns: paid=true, subscription info 183 5. Plugin grants session access: $_SESSION['taler_turnstile_access'][$post_id] = true 184 6. If subscription purchased: $_SESSION['taler_turnstile_subscriptions'][$slug] = expiration 185 7. Full content is displayed 186 ``` 187 188 ## Session State Management 189 190 ### Access Tracking 191 ```php 192 $_SESSION['taler_turnstile_access'] = [ 193 123 => true, // Post ID 123 has been paid for 194 456 => true, // Post ID 456 has been paid for 195 ]; 196 ``` 197 198 ### Subscription Tracking 199 ```php 200 $_SESSION['taler_turnstile_subscriptions'] = [ 201 'premium' => 1735689600, // Expires at Unix timestamp 202 'basic' => 1733097600, 203 ]; 204 ``` 205 206 ### Order Tracking 207 ```php 208 $_SESSION['taler_turnstile_node_orders'] = [ 209 123 => [ 210 'order_id' => 'ABC123', 211 'payment_url' => 'https://...', 212 'session_id' => 'hashed-id', 213 'order_expiration' => 1733011200, 214 'paid' => false, 215 ], 216 ]; 217 ``` 218 219 ## Error Handling 220 221 ### Backend Unavailable 222 ``` 223 If grant_access_on_error = true: 224 - Show full content (fail open) 225 - Log warning 226 227 If grant_access_on_error = false: 228 - Show error message (fail closed) 229 - Do not show content 230 ``` 231 232 ### Payment Polling Errors 233 ``` 234 - Non-402 HTTP errors: Wait 5 seconds, retry 235 - Network timeouts: Wait for full timeout period, retry 236 - 402 responses: Continue polling immediately 237 - Never give up polling (visitor may pay at any time) 238 ``` 239 240 ## Security Considerations 241 242 ### Session Security 243 - Session IDs are hashed (SHA-256) before sending to backend 244 - Backend cannot correlate sessions across sites