Changeset 3484181
- Timestamp:
- 03/16/2026 06:38:43 PM (3 weeks ago)
- Location:
- ask-my-content
- Files:
-
- 1 added
- 9 edited
-
assets/screenshot-4.png (added)
-
trunk/ask-my-content.php (modified) (4 diffs)
-
trunk/assets/js/amc-payments.js (modified) (10 diffs)
-
trunk/build/ask-my-content/block.json (modified) (1 diff)
-
trunk/build/blocks-manifest.php (modified) (1 diff)
-
trunk/includes/api.php (modified) (4 diffs)
-
trunk/includes/settings.php (modified) (1 diff)
-
trunk/includes/settings_payments.php (modified) (11 diffs)
-
trunk/readme.txt (modified) (3 diffs)
-
trunk/src/ask-my-content/block.json (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
ask-my-content/trunk/ask-my-content.php
r3465382 r3484181 4 4 * Plugin Name: Ask My Content - AI Q&A Chatbot 5 5 * Description: AI-powered Q&A chatbot, allowing users to ask questions and receive answers sourced from the site’s own posts and pages. 6 * Version: 0.9.06 * Version: 1.0.0 7 7 * Requires at least: 5.8 8 8 * Requires PHP: 7.4 … … 92 92 $settings_path = $base_dir . 'assets/js/askmyco-settings.js'; 93 93 94 $core_ver = file_exists($core_path) ? filemtime($core_path) : ' 0.9.0';95 $frontend_ver = file_exists($frontend_path) ? filemtime($frontend_path) : ' 0.9.0';96 $admin_init_ver = file_exists($admin_init_path) ? filemtime($admin_init_path) : ' 0.9.0';97 $style_ver = file_exists($style_path) ? filemtime($style_path) : ' 0.9.0';98 $settings_ver = file_exists($settings_path) ? filemtime($settings_path) : ' 0.9.0';94 $core_ver = file_exists($core_path) ? filemtime($core_path) : '1.0.0'; 95 $frontend_ver = file_exists($frontend_path) ? filemtime($frontend_path) : '1.0.0'; 96 $admin_init_ver = file_exists($admin_init_path) ? filemtime($admin_init_path) : '1.0.0'; 97 $style_ver = file_exists($style_path) ? filemtime($style_path) : '1.0.0'; 98 $settings_ver = file_exists($settings_path) ? filemtime($settings_path) : '1.0.0'; 99 99 100 100 $payments_path = $base_dir . 'assets/js/amc-payments.js'; 101 $payments_ver = file_exists($payments_path) ? filemtime($payments_path) : ' 0.9.0';101 $payments_ver = file_exists($payments_path) ? filemtime($payments_path) : '1.0.0'; 102 102 103 103 if (! wp_script_is($core_handle, 'registered')) { … … 261 261 262 262 $floating_path = plugin_dir_path(__FILE__) . 'assets/js/amc-floating.js'; 263 $floating_ver = file_exists($floating_path) ? filemtime($floating_path) : ' 0.9.0';263 $floating_ver = file_exists($floating_path) ? filemtime($floating_path) : '1.0.0'; 264 264 265 265 wp_enqueue_script( … … 417 417 $current_tab = 'settings'; 418 418 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only tab routing, no data mutation 419 if ( defined('ASKMYCO_PAYPAL_TAB') &&isset($_GET['tab']) && $_GET['tab'] === 'payments') {419 if (isset($_GET['tab']) && $_GET['tab'] === 'payments') { 420 420 $current_tab = 'payments'; 421 421 } -
ask-my-content/trunk/assets/js/amc-payments.js
r3464679 r3484181 70 70 var refreshBtn = document.getElementById('amc-pay-refresh'); 71 71 var refreshStatus = document.getElementById('amc-pay-refresh-status'); 72 var stripePayBtn = document.getElementById('amc-stripe-pay-btn'); 73 var stripeAmountInput = document.getElementById('amc-stripe-amount-usd'); 74 var stripeStatusEl = document.getElementById('amc-stripe-status'); 72 75 73 76 // Also bind the counters refresh button on payments tab (prefixed IDs) … … 94 97 } 95 98 99 function setStripeStatus(msg, isError) { 100 if (!stripeStatusEl) return; 101 stripeStatusEl.textContent = msg || ''; 102 stripeStatusEl.style.color = isError ? '#b32d2e' : '#1d2327'; 103 } 104 105 function updateEnvNotice(data) { 106 var notice = document.getElementById('amc-pay-env-notice'); 107 if (!notice) return; 108 var p = notice.querySelector('p'); 109 110 var stripeEnv = (data && data.stripe_env ? String(data.stripe_env) : '').toLowerCase().trim(); 111 var paypalEnv = (data && data.paypal_env ? String(data.paypal_env) : '').toLowerCase().trim(); 112 113 var msg = ''; 114 var type = ''; 115 if (stripeEnv && paypalEnv) { 116 if (stripeEnv !== paypalEnv) { 117 type = 'error'; 118 msg = 'Environment mismatch: Stripe is ' + stripeEnv + ' while PayPal is ' + paypalEnv + '.'; 119 } else if (stripeEnv === 'sandbox') { 120 type = 'warning'; 121 msg = 'Sandbox mode: Stripe and PayPal are configured for sandbox/testing.'; 122 } 123 } 124 125 notice.className = 'notice inline' + (type ? ' notice-' + type : ''); 126 if (p) p.textContent = msg; 127 notice.style.display = msg ? '' : 'none'; 128 } 129 96 130 /** 97 131 * Update counter elements (payments tab has prefixed IDs). … … 126 160 if (el && data.free_tier_usd !== undefined) el.textContent = formatUSD(data.free_tier_usd); 127 161 162 updateEnvNotice(data); 163 128 164 el = document.getElementById('amc-pay-balance'); 129 165 if (el && data.balance_due_usd !== undefined) { 130 var bal = parseFloat(data.balance_due_usd); 166 var rawBal = parseFloat(data.balance_due_usd); 167 var freeTier = parseFloat(data.free_tier_usd); 168 if (isNaN(freeTier)) freeTier = 0; 169 if (isNaN(rawBal)) return; 170 var bal = rawBal - freeTier; 131 171 var color = bal > 0 ? '#d63638' : '#00a32a'; 132 172 var label = bal > 0 ? 'Amount due' : 'Credit remaining / within free tier'; … … 178 218 // Sort newest first 179 219 payments.sort(function (a, b) { 180 var ta = a.pa ypal_payment_ts || a.received_at || '';181 var tb = b.pa ypal_payment_ts || b.received_at || '';220 var ta = a.paid_at || ''; 221 var tb = b.paid_at || ''; 182 222 return tb.localeCompare(ta); 183 223 }); … … 192 232 if (payments.length === 0) { 193 233 var row = document.createElement('tr'); 194 row.innerHTML = '<td colspan=" 7" style="text-align:center;">No payments recorded yet.</td>';234 row.innerHTML = '<td colspan="8" style="text-align:center;">No payments recorded yet.</td>'; 195 235 tbody.appendChild(row); 196 236 return; … … 198 238 199 239 payments.forEach(function (p) { 200 var ipn = p.raw_ipn || {};201 var txnId = p.txn_id || ipn.txn_id ||'—';202 var status = p.payment_status || ipn.payment_status ||'—';203 var gross = p.gross_amount !== undefined ? p.gross_amount : (ipn.mc_gross !== undefined ? ipn.mc_gross : '—');204 var fee = p.fee_amount !== undefined ? p.fee_amount : (ipn.mc_fee !== undefined ? ipn.mc_fee : '—');205 var currency = p.currency || ipn.mc_currency ||'USD';240 var provider = (p.provider || '').toLowerCase(); 241 var txnId = p.txn_id || '—'; 242 var status = p.payment_status || '—'; 243 var gross = p.gross_amount !== undefined ? p.gross_amount : '—'; 244 var fee = p.fee_amount !== undefined ? p.fee_amount : '—'; 245 var currency = p.currency || 'USD'; 206 246 var net; 207 247 if (p.net_amount !== undefined) { … … 213 253 } 214 254 215 var dateStr = formatDate(p.pa ypal_payment_ts || p.received_at || '');255 var dateStr = formatDate(p.paid_at || ''); 216 256 var grossDisplay = (!isNaN(parseFloat(gross))) ? formatUSD(gross) : gross; 217 257 var feeDisplay = (!isNaN(parseFloat(fee))) ? formatUSD(fee) : fee; … … 221 261 row.innerHTML = 222 262 '<td>' + escHtml(dateStr) + '</td>' + 263 '<td>' + escHtml(provider ? (provider.charAt(0).toUpperCase() + provider.slice(1)) : '—') + '</td>' + 223 264 '<td>' + escHtml(txnId) + '</td>' + 224 265 '<td>' + escHtml(status) + '</td>' + … … 282 323 } 283 324 325 async function createStripeCheckout() { 326 if (!stripePayBtn || !stripeAmountInput) { 327 return; 328 } 329 var amountUsd = parseFloat(stripeAmountInput.value); 330 if (isNaN(amountUsd) || amountUsd <= 0) { 331 setStripeStatus('Amount must be a valid dollar value.', true); 332 return; 333 } 334 335 var amountCents = Math.round(amountUsd * 100); 336 if (amountCents < 100 || amountCents > 50000) { 337 setStripeStatus('Amount must be between US $1.00 and US $500.00.', true); 338 return; 339 } 340 341 stripePayBtn.disabled = true; 342 setStripeStatus('Creating Stripe checkout session…', false); 343 344 var params = buildParams('askmyco_create_stripe_checkout'); 345 params.set('amount_cents', String(amountCents)); 346 var url = buildUrl(ajaxBase, 'askmyco_create_stripe_checkout'); 347 348 try { 349 var response = await fetch(url.toString(), { 350 method: 'POST', 351 credentials: 'same-origin', 352 headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, 353 body: params.toString() 354 }); 355 var json = await response.json(); 356 357 if (json && json.success && json.data && json.data.checkout_url) { 358 setStripeStatus('Redirecting to Stripe checkout…', false); 359 window.location.assign(json.data.checkout_url); 360 return; 361 } 362 363 var msg = (json && json.data && json.data.message) || 'Unable to create Stripe checkout session.'; 364 console.error('[ASKMYCO] Stripe checkout error payload:', json); 365 setStripeStatus(msg, true); 366 } catch (e) { 367 console.error('[ASKMYCO] Stripe checkout request failed:', e); 368 setStripeStatus(e && e.message ? e.message : 'Network error', true); 369 } finally { 370 stripePayBtn.disabled = false; 371 } 372 } 373 284 374 if (refreshBtn) { 285 375 refreshBtn.addEventListener('click', doRefresh); … … 288 378 counterRefreshBtn.addEventListener('click', doRefresh); 289 379 } 380 if (stripePayBtn) { 381 stripePayBtn.addEventListener('click', createStripeCheckout); 382 } 290 383 }); 291 384 })(); -
ask-my-content/trunk/build/ask-my-content/block.json
r3465382 r3484181 3 3 "apiVersion": 3, 4 4 "name": "amc/ask-my-content", 5 "version": " 0.9.0",5 "version": "1.0.0", 6 6 "title": "Ask My Content", 7 7 "category": "widgets", -
ask-my-content/trunk/build/blocks-manifest.php
r3465382 r3484181 6 6 'apiVersion' => 3, 7 7 'name' => 'amc/ask-my-content', 8 'version' => ' 0.9.0',8 'version' => '1.0.0', 9 9 'title' => 'Ask My Content', 10 10 'category' => 'widgets', -
ask-my-content/trunk/includes/api.php
r3465382 r3484181 100 100 101 101 /** 102 * Create a Stripe Checkout Session via backend proxy. 103 * Sends a raw JSON payload expected by backend: 104 * { website_id, site_url, amount } 105 * 106 * @param string $website_id 107 * @param string $site_url 108 * @param int $amount_cents 109 * @param string &$error 110 * @return array|null 111 */ 112 function askmyco_create_stripe_checkout_session(string $website_id, string $site_url, int $amount_cents, string &$error = ''): ?array 113 { 114 $url = trim((string) get_option('askmyco_api_url', '')); 115 $api_key = (string) get_option('askmyco_api_key', ''); 116 117 if ($url === '') { 118 $error = 'API URL is not configured'; 119 return null; 120 } 121 122 $default_timeout = 20; 123 $timeout_option = get_option('askmyco_api_timeout'); 124 if ($timeout_option !== false && is_numeric($timeout_option)) { 125 $default_timeout = max(1, (int) $timeout_option); 126 } 127 $timeout = (int) apply_filters('askmyco_backend_timeout', $default_timeout); 128 if ($timeout <= 0) { 129 $timeout = 20; 130 } 131 132 $endpoint = askmyco_build_api_endpoint('stripe/create-checkout-session'); 133 $payload = [ 134 'website_id' => $website_id, 135 'site_url' => $site_url, 136 'amount' => $amount_cents, 137 ]; 138 139 askmyco_debug_log(sprintf('[AMC] Stripe checkout create start endpoint=%s website_id=%s amount_cents=%d', $endpoint, $website_id, $amount_cents), 'info'); 140 141 $response = wp_remote_post($endpoint, [ 142 'headers' => [ 143 'Content-Type' => 'application/json', 144 'Authorization' => 'Bearer ' . $api_key, 145 ], 146 'body' => wp_json_encode($payload), 147 'blocking' => true, 148 'timeout' => $timeout, 149 ]); 150 151 if (is_wp_error($response)) { 152 $error = askmyco_backend_error_summary_from_response($response); 153 askmyco_debug_log('[AMC] Stripe checkout create failed (WP_Error): ' . $error, 'error'); 154 return null; 155 } 156 157 $code = (int) wp_remote_retrieve_response_code($response); 158 if ($code < 200 || $code >= 300) { 159 $error = askmyco_backend_error_summary_from_response($response); 160 askmyco_debug_log(sprintf('[AMC] Stripe checkout create failed HTTP %d: %s', $code, $error), 'error'); 161 return null; 162 } 163 164 $decoded = json_decode((string) wp_remote_retrieve_body($response), true); 165 if (! is_array($decoded)) { 166 $error = 'Invalid Stripe create-checkout-session response (not JSON object)'; 167 askmyco_debug_log('[AMC] Stripe checkout create failed: ' . $error, 'error'); 168 return null; 169 } 170 if (empty($decoded['checkout_url']) || ! is_string($decoded['checkout_url'])) { 171 $error = 'Stripe checkout URL missing in backend response'; 172 askmyco_debug_log('[AMC] Stripe checkout create failed: missing checkout_url', 'error'); 173 return null; 174 } 175 176 return $decoded; 177 } 178 179 /** 102 180 * Shared handler for chat query AJAX endpoints. 103 181 * Validates nonce and (optionally) admin capability, then proxies to backend. … … 327 405 328 406 /** 407 * Normalize one payment record from backend into a customer-facing shape. 408 * Keeps the plugin display logic independent from provider-specific raw payloads. 409 */ 410 function askmyco_normalize_payment_record(array $payment): array 411 { 412 $provider = strtolower((string) ($payment['provider'] ?? '')); 413 $txn_id = '—'; 414 415 if ($provider === 'stripe') { 416 $txn_id = (string) ($payment['stripe_payment_intent_id'] 417 ?? $payment['stripe_checkout_session_id'] 418 ?? $payment['stripe_event_id'] 419 ?? '—'); 420 } elseif ($provider === 'paypal') { 421 $txn_id = (string) ($payment['paypal_txn_id'] 422 ?? $payment['txn_id'] 423 ?? '—'); 424 } elseif (! empty($payment['txn_id'])) { 425 $txn_id = (string) $payment['txn_id']; 426 } 427 428 return [ 429 'provider' => $provider, 430 'provider_env' => (string) ($payment['provider_env'] ?? ''), 431 'paid_at' => (string) ($payment['paypal_payment_ts'] ?? $payment['received_at'] ?? ''), 432 'txn_id' => $txn_id, 433 'paypal_txn_id' => (string) ($payment['paypal_txn_id'] ?? ''), 434 'payment_status' => (string) ($payment['payment_status'] ?? ''), 435 'gross_amount' => $payment['gross_amount'] ?? '', 436 'fee_amount' => $payment['fee_amount'] ?? '', 437 'net_amount' => $payment['net_amount'] ?? '', 438 'currency' => (string) ($payment['currency'] ?? 'USD'), 439 'stripe_payment_intent_id' => (string) ($payment['stripe_payment_intent_id'] ?? ''), 440 'stripe_checkout_session_id' => (string) ($payment['stripe_checkout_session_id'] ?? ''), 441 'stripe_event_id' => (string) ($payment['stripe_event_id'] ?? ''), 442 ]; 443 } 444 445 /** 329 446 * Sanitize a payments array for client-side consumption. 330 * Strips raw IPN, payer email, and internal IDs.447 * Converts backend records to the normalized customer-facing shape. 331 448 */ 332 449 function askmyco_sanitize_payments_for_client(array $payments): array … … 335 452 foreach ($payments as $payment) { 336 453 if (! is_array($payment)) continue; 337 $ipn = isset($payment['raw_ipn']) && is_array($payment['raw_ipn']) ? $payment['raw_ipn'] : []; 338 $sanitized[] = [ 339 'paypal_payment_ts' => $payment['paypal_payment_ts'] ?? ($payment['received_at'] ?? ''), 340 'txn_id' => $payment['txn_id'] ?? ($ipn['txn_id'] ?? ''), 341 'payment_status' => $payment['payment_status'] ?? ($ipn['payment_status'] ?? ''), 342 'gross_amount' => $payment['gross_amount'] ?? ($ipn['mc_gross'] ?? ''), 343 'fee_amount' => $payment['fee_amount'] ?? ($ipn['mc_fee'] ?? ''), 344 'net_amount' => $payment['net_amount'] ?? '', 345 'currency' => $payment['currency'] ?? ($ipn['mc_currency'] ?? 'USD'), 346 ]; 454 $sanitized[] = askmyco_normalize_payment_record($payment); 347 455 } 348 456 return $sanitized; … … 396 504 397 505 wp_send_json_success(askmyco_sanitize_payments_for_client($data)); 506 } 507 508 /** 509 * AJAX: Create Stripe checkout session URL. 510 * Admin-only endpoint. 511 */ 512 add_action('wp_ajax_askmyco_create_stripe_checkout', 'askmyco_ajax_create_stripe_checkout'); 513 function askmyco_ajax_create_stripe_checkout() 514 { 515 if (! current_user_can('manage_options')) { 516 wp_send_json_error(['message' => 'Unauthorized'], 403); 517 } 518 check_ajax_referer('askmyco_admin_query', 'security'); 519 520 $amount_cents = isset($_POST['amount_cents']) ? (int) sanitize_text_field(wp_unslash($_POST['amount_cents'])) : 0; 521 if ($amount_cents < 100 || $amount_cents > 50000) { 522 wp_send_json_error(['message' => 'Amount must be between 100 and 50000 cents', 'code' => 'invalid_amount'], 400); 523 } 524 525 $website_id = (string) get_option('askmyco_site_uuid', ''); 526 if ($website_id === '') { 527 wp_send_json_error(['message' => 'Missing site UUID', 'code' => 'missing_site_uuid'], 500); 528 } 529 $site_url = (string) admin_url('admin.php?page=ask-my-content&tab=payments'); 530 askmyco_debug_log('[AMC] Stripe checkout callback base URL: ' . $site_url, 'debug'); 531 532 $error = ''; 533 $data = askmyco_create_stripe_checkout_session($website_id, $site_url, $amount_cents, $error); 534 if ($data === null) { 535 wp_send_json_error(['message' => $error ?: 'Unable to create Stripe checkout session', 'code' => 'stripe_create_failed'], 500); 536 } 537 if (empty($data['checkout_url'])) { 538 askmyco_debug_log('[AMC] Stripe checkout create failed: checkout_url missing in decoded response', 'error'); 539 wp_send_json_error(['message' => 'Checkout URL missing in backend response', 'code' => 'missing_checkout_url'], 502); 540 } 541 542 wp_send_json_success(['checkout_url' => (string) $data['checkout_url']]); 398 543 } 399 544 -
ask-my-content/trunk/includes/settings.php
r3464679 r3484181 25 25 function askmyco_settings_page() 26 26 { 27 $has_payments_tab = defined('ASKMYCO_PAYPAL_TAB');27 $has_payments_tab = true; 28 28 $current_tab = 'settings'; 29 29 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only tab routing, no data mutation -
ask-my-content/trunk/includes/settings_payments.php
r3464679 r3484181 5 5 } 6 6 7 // Payments Tab UI — only rendered when ASKMYCO_PAYPAL_TAB is defined.7 // Payments Tab UI. 8 8 // Shared fetch helpers live in api.php: askmyco_fetch_counters(), askmyco_fetch_payments(). 9 10 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only UI status message from redirect query parameter 11 $askmyco_payment_redirect = isset($_GET['ask-my-content-payment']) ? sanitize_text_field(wp_unslash($_GET['ask-my-content-payment'])) : ''; 12 $askmyco_paypal_enabled = defined('ASKMYCO_PAYPAL') ? (bool) constant('ASKMYCO_PAYPAL') : false; 9 13 10 14 // Fetch data … … 23 27 $askmyco_free_tier = $askmyco_billing['free_tier_usd'] ?? null; 24 28 $askmyco_balance_due = $askmyco_billing['balance_due_usd'] ?? null; 29 $askmyco_stripe_env = $askmyco_billing['stripe_env'] ?? ''; 25 30 $askmyco_paypal_env = $askmyco_billing['paypal_env'] ?? 'live'; 26 31 $askmyco_paypal_email = $askmyco_billing['paypal_business_email'] ?? ''; … … 30 35 // Counter options are already persisted by askmyco_fetch_counters(). 31 36 32 // Balance color 33 $askmyco_balance_float = $askmyco_balance_due !== null ? (float) $askmyco_balance_due : 0.0; 34 $askmyco_balance_color = $askmyco_balance_float > 0 ? '#d63638' : '#00a32a'; 35 $askmyco_balance_label = $askmyco_balance_float > 0 ? 'Amount due' : 'Credit remaining / within free tier'; 37 // Displayed balance applies current free tier locally: 38 // displayed_balance = balance_due_usd - free_tier_usd 39 $askmyco_balance_raw_float = $askmyco_balance_due !== null ? (float) $askmyco_balance_due : 0.0; 40 $askmyco_free_tier_float = $askmyco_free_tier !== null ? (float) $askmyco_free_tier : 0.0; 41 $askmyco_display_balance_float = $askmyco_balance_raw_float - $askmyco_free_tier_float; 42 $askmyco_balance_color = $askmyco_display_balance_float > 0 ? '#d63638' : '#00a32a'; 43 $askmyco_balance_label = $askmyco_display_balance_float > 0 ? 'Amount due' : 'Credit remaining / within free tier'; 44 45 // Stripe/PayPal environment notice 46 $askmyco_env_notice_type = ''; 47 $askmyco_env_notice_text = ''; 48 if (! $askmyco_billing_error) { 49 $askmyco_stripe_env_norm = strtolower(trim((string) $askmyco_stripe_env)); 50 $askmyco_paypal_env_norm = strtolower(trim((string) $askmyco_paypal_env)); 51 if ($askmyco_stripe_env_norm !== '' && $askmyco_paypal_env_norm !== '') { 52 if ($askmyco_stripe_env_norm !== $askmyco_paypal_env_norm) { 53 $askmyco_env_notice_type = 'error'; 54 $askmyco_env_notice_text = sprintf('Environment mismatch: Stripe is %s while PayPal is %s.', $askmyco_stripe_env_norm, $askmyco_paypal_env_norm); 55 } elseif ($askmyco_stripe_env_norm === 'sandbox') { 56 $askmyco_env_notice_type = 'warning'; 57 $askmyco_env_notice_text = 'Sandbox mode: Stripe and PayPal are configured for sandbox/testing.'; 58 } 59 } 60 } 36 61 37 62 // PayPal form action URL 38 63 $askmyco_paypal_url = ($askmyco_paypal_env === 'sandbox') 39 ? 'https:// www.sandbox.paypal.com/cgi-bin/webscr'40 : 'https:// www.paypal.com/cgi-bin/webscr';64 ? 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr' 65 : 'https://ipnpb.paypal.com/cgi-bin/webscr'; 41 66 42 67 // Suggested payment amount: max(free_tier, balance_due), minimum $1 … … 45 70 $askmyco_suggested_amount = number_format(max((float) $askmyco_free_tier, (float) $askmyco_balance_due, 1.00), 2, '.', ''); 46 71 } 72 $askmyco_stripe_default_amount_usd = '5.00'; 47 73 48 74 // Format last payment date … … 71 97 <?php askmyco_render_token_counters('amc-pay'); ?> 72 98 99 <?php if ($askmyco_payment_redirect === 'success') : ?> 100 <div class="notice notice-success inline"> 101 <p>Payment redirect SUCCESS. Credits will appear after webhook confirmation.</p> 102 </div> 103 <?php elseif ($askmyco_payment_redirect === 'cancel') : ?> 104 <div class="notice notice-warning inline"> 105 <p>Payment cancelled.</p> 106 </div> 107 <?php endif; ?> 108 73 109 <hr> 74 110 … … 105 141 </tr> 106 142 <tr> 107 <th scope="row" id="amc-pay-balance-label"><?php echo $askmyco_ balance_float > 0 ? 'Balance due' : 'Credit remaining'; ?></th>143 <th scope="row" id="amc-pay-balance-label"><?php echo $askmyco_display_balance_float > 0 ? 'Balance due' : 'Credit remaining'; ?></th> 108 144 <td id="amc-pay-balance" style="color: <?php echo esc_attr($askmyco_balance_color); ?>; font-weight: 600;"> 109 US $<?php echo esc_html(number_format(abs($askmyco_ balance_float), 2)); ?>145 US $<?php echo esc_html(number_format(abs($askmyco_display_balance_float), 2)); ?> 110 146 <span style="font-weight: normal; margin-left: 0.5em;">(<?php echo esc_html($askmyco_balance_label); ?>)</span> 111 147 </td> … … 119 155 <h2>Add Credit</h2> 120 156 121 <?php if ($askmyco_billing_error || empty($askmyco_paypal_email)) : ?> 122 <p class="description">Payment form is unavailable. Billing data could not be loaded.</p> 123 <?php else : ?> 124 <?php if ($askmyco_paypal_env === 'sandbox') : ?> 125 <div class="notice notice-warning inline" style="margin-bottom: 12px;"> 126 <p><strong>Sandbox mode:</strong> Payments are directed to the PayPal Sandbox environment for testing.</p> 127 </div> 157 <div 158 id="amc-pay-env-notice" 159 class="notice inline<?php echo $askmyco_env_notice_type ? ' notice-' . esc_attr($askmyco_env_notice_type) : ''; ?>" 160 style="<?php echo $askmyco_env_notice_text === '' ? 'display:none;' : ''; ?>margin-bottom:12px;"> 161 <p><?php echo esc_html($askmyco_env_notice_text); ?></p> 162 </div> 163 164 <h3 style="margin-top:0;">Stripe</h3> 165 <p class="description">Create a Stripe Checkout session and continue payment securely on Stripe.</p> 166 <p> 167 <label for="amc-stripe-amount-usd"><strong>Amount (USD):</strong></label> 168 <input 169 type="number" 170 min="1" 171 max="500" 172 step="0.01" 173 id="amc-stripe-amount-usd" 174 value="<?php echo esc_attr($askmyco_stripe_default_amount_usd); ?>" 175 style="width:120px; margin-left:8px;" /> 176 <button type="button" class="button button-primary" id="amc-stripe-pay-btn" style="margin-left:8px;">Pay with Stripe</button> 177 </p> 178 <p id="amc-stripe-status" class="description" style="margin-top:4px;"></p> 179 180 <?php if ($askmyco_paypal_enabled) : ?> 181 <h3>PayPal</h3> 182 183 <?php if ($askmyco_billing_error || empty($askmyco_paypal_email)) : ?> 184 <p class="description">Payment form is unavailable. Billing data could not be loaded.</p> 185 <?php else : ?> 186 <?php if ($askmyco_paypal_env === 'sandbox') : ?> 187 <div class="notice notice-warning inline" style="margin-bottom: 12px;"> 188 <p><strong>Sandbox mode:</strong> Payments are directed to the PayPal Sandbox environment for testing.</p> 189 </div> 190 <?php endif; ?> 191 192 <?php 193 askmyco_debug_log(sprintf( 194 'PayPal form rendered: env=%s action=%s business=%s custom=%s notify_url=%s amount=%s', 195 $askmyco_paypal_env, 196 $askmyco_paypal_url, 197 $askmyco_paypal_email, 198 $askmyco_website_id, 199 $askmyco_paypal_notify, 200 $askmyco_suggested_amount ?: '(user-entered)' 201 ), 'info'); 202 ?> 203 <form action="<?php echo esc_url($askmyco_paypal_url); ?>" method="post" target="_blank" style="margin-top: 8px;"> 204 <input type="hidden" name="cmd" value="_xclick"> 205 <input type="hidden" name="business" value="<?php echo esc_attr($askmyco_paypal_email); ?>"> 206 <input type="hidden" name="item_name" value="Ask My Content Credit"> 207 <?php if ($askmyco_suggested_amount !== '') : ?> 208 <input type="hidden" name="amount" value="<?php echo esc_attr($askmyco_suggested_amount); ?>"> 209 <?php endif; ?> 210 <input type="hidden" name="currency_code" value="USD"> 211 <input type="hidden" name="custom" value="<?php echo esc_attr($askmyco_website_id); ?>"> 212 <input type="hidden" name="notify_url" value="<?php echo esc_url($askmyco_paypal_notify); ?>"> 213 <input type="hidden" name="return" value="<?php echo esc_url(admin_url('admin.php?page=ask-my-content&tab=payments')); ?>"> 214 <input type="hidden" name="cancel_return" value="<?php echo esc_url(admin_url('admin.php?page=ask-my-content&tab=payments')); ?>"> 215 <?php if ($askmyco_suggested_amount !== '') : ?> 216 <p class="description" style="margin-bottom: 8px;"> 217 Suggested amount: <strong>US $<?php echo esc_html($askmyco_suggested_amount); ?></strong> 218 </p> 219 <?php endif; ?> 220 <input type="submit" class="button button-primary" value="Add Credit via PayPal"> 221 </form> 128 222 <?php endif; ?> 129 130 <?php131 askmyco_debug_log(sprintf(132 'PayPal form rendered: env=%s action=%s business=%s custom=%s notify_url=%s amount=%s',133 $askmyco_paypal_env,134 $askmyco_paypal_url,135 $askmyco_paypal_email,136 $askmyco_website_id,137 $askmyco_paypal_notify,138 $askmyco_suggested_amount ?: '(user-entered)'139 ), 'info');140 ?>141 <form action="<?php echo esc_url($askmyco_paypal_url); ?>" method="post" target="_blank" style="margin-top: 8px;">142 <input type="hidden" name="cmd" value="_xclick">143 <input type="hidden" name="business" value="<?php echo esc_attr($askmyco_paypal_email); ?>">144 <input type="hidden" name="item_name" value="Ask My Content Credit">145 <?php if ($askmyco_suggested_amount !== '') : ?>146 <input type="hidden" name="amount" value="<?php echo esc_attr($askmyco_suggested_amount); ?>">147 <?php endif; ?>148 <input type="hidden" name="currency_code" value="USD">149 <input type="hidden" name="custom" value="<?php echo esc_attr($askmyco_website_id); ?>">150 <input type="hidden" name="notify_url" value="<?php echo esc_url($askmyco_paypal_notify); ?>">151 <input type="hidden" name="return" value="<?php echo esc_url(admin_url('admin.php?page=ask-my-content&tab=payments')); ?>">152 <input type="hidden" name="cancel_return" value="<?php echo esc_url(admin_url('admin.php?page=ask-my-content&tab=payments')); ?>">153 <?php if ($askmyco_suggested_amount !== '') : ?>154 <p class="description" style="margin-bottom: 8px;">155 Suggested amount: <strong>US $<?php echo esc_html($askmyco_suggested_amount); ?></strong>156 </p>157 <?php endif; ?>158 <input type="submit" class="button button-primary" value="Add Credit via PayPal">159 </form>160 223 <?php endif; ?> 161 224 … … 181 244 <tr> 182 245 <th>Date</th> 246 <th>Provider</th> 183 247 <th>Transaction ID</th> 184 248 <th>Status</th> … … 199 263 200 264 foreach ($askmyco_payments as $askmyco_payment) : 265 $askmyco_payment = askmyco_normalize_payment_record($askmyco_payment); 201 266 $askmyco_pay_date = ''; 202 $askmyco_raw_ts = $askmyco_payment['pa ypal_payment_ts'] ?? $askmyco_payment['received_at'] ?? '';267 $askmyco_raw_ts = $askmyco_payment['paid_at'] ?? ''; 203 268 if ($askmyco_raw_ts) { 204 269 try { … … 212 277 } 213 278 214 $askmyco_txn_id = $askmyco_payment['txn_id'] ?? ($askmyco_payment['raw_ipn']['txn_id'] ?? '—'); 215 $askmyco_status = $askmyco_payment['payment_status'] ?? ($askmyco_payment['raw_ipn']['payment_status'] ?? '—'); 216 $askmyco_gross = $askmyco_payment['gross_amount'] ?? ($askmyco_payment['raw_ipn']['mc_gross'] ?? '—'); 217 $askmyco_fee = $askmyco_payment['fee_amount'] ?? ($askmyco_payment['raw_ipn']['mc_fee'] ?? '—'); 218 $askmyco_currency = $askmyco_payment['currency'] ?? ($askmyco_payment['raw_ipn']['mc_currency'] ?? 'USD'); 279 $askmyco_provider = strtolower((string) ($askmyco_payment['provider'] ?? '')); 280 $askmyco_txn_id = $askmyco_payment['txn_id'] ?? '—'; 281 $askmyco_status = $askmyco_payment['payment_status'] ?? '—'; 282 $askmyco_gross = $askmyco_payment['gross_amount'] ?? '—'; 283 $askmyco_fee = $askmyco_payment['fee_amount'] ?? '—'; 284 $askmyco_currency = $askmyco_payment['currency'] ?? 'USD'; 219 285 220 286 // Calculate net … … 234 300 <tr> 235 301 <td><?php echo esc_html($askmyco_pay_date); ?></td> 302 <td><?php echo esc_html($askmyco_provider !== '' ? ucfirst($askmyco_provider) : '—'); ?></td> 236 303 <td><?php echo esc_html($askmyco_txn_id); ?></td> 237 304 <td><?php echo esc_html($askmyco_status); ?></td> -
ask-my-content/trunk/readme.txt
r3465382 r3484181 5 5 Requires PHP: 7.4 6 6 Tested up to: 6.9 7 Stable tag: 0.9.07 Stable tag: 1.0.0 8 8 License: GPL-2.0-or-later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 153 153 1. Chatbot on a page, answering a question using site content. 154 154 155 2. Dashboard - Ask My Content admin page. 156 157 3. Block Editor - Ask My Content block with align, size and colors params. 155 2. Dashboard - `Ask My Content` admin page. 156 157 3. Block Editor - `Ask My Content` block with align, size and colors params. 158 159 4. Dashboard - `Ask My Content` - `Payments` tab. 158 160 159 161 == Changelog == 162 163 = 1.0.0 = 164 Added `Payments` tab to `Ask My Content Settings`, where you can check your usage and pay the balance (currently with Stripe) 160 165 161 166 = 0.9.0 = … … 203 208 == Upgrade Notice == 204 209 205 = 0.8.0 =206 Added a new optional floating chat launcher. Enable it in Dashboard -> Ask My Content after updating.210 = 1.0.0 = 211 Added `Payments` tab to `Ask My Content Settings`, where you can check your usage and pay the balance (currently with Stripe) -
ask-my-content/trunk/src/ask-my-content/block.json
r3465382 r3484181 3 3 "apiVersion": 3, 4 4 "name": "amc/ask-my-content", 5 "version": " 0.9.0",5 "version": "1.0.0", 6 6 "title": "Ask My Content", 7 7 "category": "widgets",
Note: See TracChangeset
for help on using the changeset viewer.