Misc fixes: cleanup templates and supporting updates
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# WooNooW Feature Roadmap - 2025
|
# WooNooW Feature Roadmap - 2025
|
||||||
|
|
||||||
**Last Updated**: December 31, 2025
|
**Last Updated**: June 1, 2026
|
||||||
**Status**: Active Development
|
**Status**: Active Development
|
||||||
|
|
||||||
This document outlines the comprehensive feature roadmap for WooNooW, building upon existing infrastructure.
|
This document outlines the comprehensive feature roadmap for WooNooW, building upon existing infrastructure.
|
||||||
@@ -301,66 +301,59 @@ class AffiliateTracker {
|
|||||||
### Overview
|
### Overview
|
||||||
Recurring product subscriptions with flexible billing cycles.
|
Recurring product subscriptions with flexible billing cycles.
|
||||||
|
|
||||||
### Status: **Planning** 🔵
|
### Status: **Shipped** ✅
|
||||||
|
|
||||||
### What's Already Built
|
### What's Already Built
|
||||||
- ✅ Product management
|
- ✅ Product management
|
||||||
- ✅ Order system
|
- ✅ Order system
|
||||||
- ✅ Payment gateways
|
- ✅ Payment gateways
|
||||||
- ✅ Notification system
|
- ✅ Notification system
|
||||||
|
- ✅ Database tables (`wp_woonoow_subscriptions`, `wp_woonoow_subscription_orders`) — schema below reflects actual shipped columns
|
||||||
|
- ✅ Per-gateway auto-renew capability table (kill-switchable)
|
||||||
|
- ✅ Pause/resume/cancel/early-renew customer UI
|
||||||
|
- ✅ Admin list with bulk actions, search, and per-status filter
|
||||||
|
- ✅ Renewal cron (`process_renewals`, `check_expirations`, `send_reminders`, `retry_unpaid_renewals`)
|
||||||
|
|
||||||
### What's Needed
|
### Schema (as shipped)
|
||||||
|
|
||||||
#### 1. Database Tables
|
|
||||||
```sql
|
```sql
|
||||||
wp_woonoow_subscriptions (id, customer_id, product_id, status, billing_period, billing_interval, price, next_payment_date, start_date, end_date, trial_end_date)
|
wp_woonoow_subscriptions (
|
||||||
wp_woonoow_subscription_orders (id, subscription_id, order_id, payment_status, created_at)
|
id, user_id, order_id, product_id, variation_id, status,
|
||||||
|
billing_period, billing_interval, recurring_amount,
|
||||||
|
start_date, trial_end_date, next_payment_date, end_date, last_payment_date,
|
||||||
|
payment_method, payment_meta, cancel_reason,
|
||||||
|
pause_count, failed_payment_count, reminder_sent_at
|
||||||
|
)
|
||||||
|
wp_woonoow_subscription_orders (
|
||||||
|
id, subscription_id, order_id, order_type ENUM 'parent'|'renewal'|'switch'|'resubscribe'
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 2. Product Meta
|
Note: the column is `user_id`, not `customer_id` — the original spec used the
|
||||||
Add subscription options to product:
|
WC-style "customer" naming, but WP schema reserves `customer` for the legacy
|
||||||
- Is subscription product (checkbox)
|
WP customer user role and the column was renamed before the first migration
|
||||||
- Billing period (daily, weekly, monthly, yearly)
|
shipped.
|
||||||
- Billing interval (e.g., 2 for every 2 months)
|
|
||||||
- Trial period (days)
|
|
||||||
|
|
||||||
#### 3. Renewal System
|
### Customer Dashboard
|
||||||
```php
|
|
||||||
class SubscriptionRenewal {
|
|
||||||
|
|
||||||
// WP-Cron daily job
|
|
||||||
public function process_renewals() {
|
|
||||||
$due_subscriptions = $this->get_due_subscriptions();
|
|
||||||
|
|
||||||
foreach ($due_subscriptions as $subscription) {
|
|
||||||
// Create renewal order
|
|
||||||
// Process payment
|
|
||||||
// Update next payment date
|
|
||||||
// Send notification
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 4. Customer Dashboard
|
|
||||||
**Route**: `/account/subscriptions`
|
**Route**: `/account/subscriptions`
|
||||||
- Active subscriptions list
|
- Active subscriptions list
|
||||||
- Pause/resume subscription
|
- Pause/resume subscription (capped at `max_pause_count` setting, default 3)
|
||||||
- Cancel subscription
|
- Cancel subscription
|
||||||
- Update payment method
|
- Update payment method
|
||||||
- View billing history
|
- View billing history
|
||||||
- Change billing cycle
|
- Change billing cycle
|
||||||
|
|
||||||
#### 5. Admin UI
|
### Admin UI
|
||||||
**Route**: `/products/subscriptions`
|
**Route**: `/subscriptions`
|
||||||
- All subscriptions list
|
- All subscriptions list with checkbox + bulk actions (cancel, CSV export)
|
||||||
- Filter by status
|
- Free-text search by id / email / display name
|
||||||
- View subscription details
|
- Per-status filter
|
||||||
- Manual renewal
|
- View subscription details (per-gateway auto-renew badge, pause count)
|
||||||
|
- Renew Now (creates manual order) or Charge Now (forces auto-debit, M2)
|
||||||
- Cancel/refund
|
- Cancel/refund
|
||||||
|
|
||||||
### Priority: **Low** 🟢
|
### Priority: ~~Low~~ Shipped ✅
|
||||||
### Effort: 4-5 weeks
|
### Effort: ~~4-5 weeks~~ Shipped
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
import { api } from '@/lib/api';
|
import { api } from '@/lib/api';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { __ } from '@/lib/i18n';
|
import { __ } from '@/lib/i18n';
|
||||||
|
import { toast } from 'sonner';
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
"types": [],
|
"types": [],
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": { "@/*": ["./src/*"] },
|
"paths": { "@/*": ["./src/*"] },
|
||||||
"ignoreDeprecations": "6.0"
|
"ignoreDeprecations": "5.0"
|
||||||
},
|
},
|
||||||
"include": ["src"]
|
"include": ["src"]
|
||||||
}
|
}
|
||||||
@@ -352,6 +352,21 @@ class EmailRenderer
|
|||||||
'payment_link' => $data['payment_link'] ?? '',
|
'payment_link' => $data['payment_link'] ?? '',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// O1 — Derive `billing_schedule` (e.g. "Every 3 Months") and
|
||||||
|
// `payment_method_title` (e.g. "Stripe" rather than the raw
|
||||||
|
// gateway id "stripe"). The data is on the subscription row but
|
||||||
|
// isn't pre-formatted. We rebuild both so email templates can
|
||||||
|
// show the merchant-friendly string without duplicating the
|
||||||
|
// pluralization + lookup logic.
|
||||||
|
$sub_variables['billing_schedule'] = self::format_billing_schedule(
|
||||||
|
isset($sub->billing_period) ? (string) $sub->billing_period : '',
|
||||||
|
isset($sub->billing_interval) ? (int) $sub->billing_interval : 1
|
||||||
|
);
|
||||||
|
$sub_variables['payment_method_title'] = self::resolve_payment_method_title(
|
||||||
|
isset($sub->payment_method) ? (string) $sub->payment_method : '',
|
||||||
|
$data['order'] ?? null
|
||||||
|
);
|
||||||
|
|
||||||
// Get product name if not already set
|
// Get product name if not already set
|
||||||
if (!isset($variables['product_name']) && isset($data['product']) && $data['product'] instanceof \WC_Product) {
|
if (!isset($variables['product_name']) && isset($data['product']) && $data['product'] instanceof \WC_Product) {
|
||||||
$sub_variables['product_name'] = $data['product']->get_name();
|
$sub_variables['product_name'] = $data['product']->get_name();
|
||||||
@@ -381,6 +396,57 @@ class EmailRenderer
|
|||||||
return apply_filters('woonoow_email_variables', $variables, $event_id, $data);
|
return apply_filters('woonoow_email_variables', $variables, $event_id, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* O1 — Format a billing schedule string like "Every 3 Months" from raw
|
||||||
|
* period and interval columns. Mirrors the controller's
|
||||||
|
* `enrich_subscription()` math so email templates show the same string
|
||||||
|
* the customer sees in the SPA. Falls back to the period string itself
|
||||||
|
* if the period is unknown.
|
||||||
|
*/
|
||||||
|
public static function format_billing_schedule($period, $interval)
|
||||||
|
{
|
||||||
|
$period_labels = [
|
||||||
|
'day' => __('day', 'woonoow'),
|
||||||
|
'week' => __('week', 'woonoow'),
|
||||||
|
'month' => __('month', 'woonoow'),
|
||||||
|
'year' => __('year', 'woonoow'),
|
||||||
|
];
|
||||||
|
$interval = max(1, (int) $interval);
|
||||||
|
$period_label = $period_labels[$period] ?? $period;
|
||||||
|
if ($interval > 1) {
|
||||||
|
$period_label .= 's';
|
||||||
|
}
|
||||||
|
return sprintf(__('Every %s%s', 'woonoow'), $interval, $period_label);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* O1 — Resolve a human-friendly payment method title from a stored gateway
|
||||||
|
* id. Order of preference:
|
||||||
|
* 1. The order's `payment_method_title` (most accurate; set by gateway
|
||||||
|
* at checkout — e.g. "PayPal — Visa ending in 1234")
|
||||||
|
* 2. The registered WC gateway's `get_title()` (e.g. "Stripe")
|
||||||
|
* 3. The raw id
|
||||||
|
*/
|
||||||
|
public static function resolve_payment_method_title($gateway_id, $order = null)
|
||||||
|
{
|
||||||
|
if ($order instanceof \WC_Order) {
|
||||||
|
$title = $order->get_payment_method_title();
|
||||||
|
if (!empty($title)) {
|
||||||
|
return $title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($gateway_id !== '' && function_exists('WC') && WC()->payment_gateways()) {
|
||||||
|
$gateways = WC()->payment_gateways()->payment_gateways();
|
||||||
|
if (isset($gateways[$gateway_id]) && method_exists($gateways[$gateway_id], 'get_title')) {
|
||||||
|
$title = $gateways[$gateway_id]->get_title();
|
||||||
|
if (!empty($title)) {
|
||||||
|
return $title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $gateway_id;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse [card] tags and convert to HTML
|
* Parse [card] tags and convert to HTML
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,375 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Notification Template Provider
|
|
||||||
*
|
|
||||||
* Manages notification templates for all channels.
|
|
||||||
*
|
|
||||||
* @package WooNooW\Core\Notifications
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace WooNooW\Core\Notifications;
|
|
||||||
|
|
||||||
use WooNooW\Email\DefaultTemplates as EmailDefaultTemplates;
|
|
||||||
|
|
||||||
class TemplateProvider {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Option key for storing templates
|
|
||||||
*/
|
|
||||||
const OPTION_KEY = 'woonoow_notification_templates';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all templates
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function get_templates() {
|
|
||||||
$templates = get_option(self::OPTION_KEY, []);
|
|
||||||
|
|
||||||
// Merge with defaults
|
|
||||||
$defaults = self::get_default_templates();
|
|
||||||
|
|
||||||
return array_merge($defaults, $templates);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get template for specific event and channel
|
|
||||||
*
|
|
||||||
* @param string $event_id Event ID
|
|
||||||
* @param string $channel_id Channel ID
|
|
||||||
* @param string $recipient_type Recipient type ('customer' or 'staff')
|
|
||||||
* @return array|null
|
|
||||||
*/
|
|
||||||
public static function get_template($event_id, $channel_id, $recipient_type = 'customer') {
|
|
||||||
$templates = self::get_templates();
|
|
||||||
|
|
||||||
$key = "{$recipient_type}_{$event_id}_{$channel_id}";
|
|
||||||
|
|
||||||
if (isset($templates[$key])) {
|
|
||||||
return $templates[$key];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return default if exists
|
|
||||||
$defaults = self::get_default_templates();
|
|
||||||
|
|
||||||
if (isset($defaults[$key])) {
|
|
||||||
return $defaults[$key];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save template
|
|
||||||
*
|
|
||||||
* @param string $event_id Event ID
|
|
||||||
* @param string $channel_id Channel ID
|
|
||||||
* @param array $template Template data
|
|
||||||
* @param string $recipient_type Recipient type ('customer' or 'staff')
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function save_template($event_id, $channel_id, $template, $recipient_type = 'customer') {
|
|
||||||
$templates = get_option(self::OPTION_KEY, []);
|
|
||||||
|
|
||||||
$key = "{$recipient_type}_{$event_id}_{$channel_id}";
|
|
||||||
|
|
||||||
$templates[$key] = [
|
|
||||||
'event_id' => $event_id,
|
|
||||||
'channel_id' => $channel_id,
|
|
||||||
'recipient_type' => $recipient_type,
|
|
||||||
'subject' => $template['subject'] ?? '',
|
|
||||||
'body' => $template['body'] ?? '',
|
|
||||||
'variables' => $template['variables'] ?? [],
|
|
||||||
'updated_at' => current_time('mysql'),
|
|
||||||
];
|
|
||||||
|
|
||||||
return update_option(self::OPTION_KEY, $templates);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete template (revert to default)
|
|
||||||
*
|
|
||||||
* @param string $event_id Event ID
|
|
||||||
* @param string $channel_id Channel ID
|
|
||||||
* @param string $recipient_type Recipient type ('customer' or 'staff')
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function delete_template($event_id, $channel_id, $recipient_type = 'customer') {
|
|
||||||
$templates = get_option(self::OPTION_KEY, []);
|
|
||||||
|
|
||||||
$key = "{$recipient_type}_{$event_id}_{$channel_id}";
|
|
||||||
|
|
||||||
if (isset($templates[$key])) {
|
|
||||||
unset($templates[$key]);
|
|
||||||
return update_option(self::OPTION_KEY, $templates);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get WooCommerce email template content
|
|
||||||
*
|
|
||||||
* @param string $email_id WooCommerce email ID
|
|
||||||
* @return array|null
|
|
||||||
*/
|
|
||||||
private static function get_wc_email_template($email_id) {
|
|
||||||
if (!function_exists('WC')) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$mailer = \WC()->mailer();
|
|
||||||
$emails = $mailer->get_emails();
|
|
||||||
|
|
||||||
if (isset($emails[$email_id])) {
|
|
||||||
$email = $emails[$email_id];
|
|
||||||
return [
|
|
||||||
'subject' => $email->get_subject(),
|
|
||||||
'heading' => $email->get_heading(),
|
|
||||||
'enabled' => $email->is_enabled(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get default templates
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function get_default_templates() {
|
|
||||||
$templates = [];
|
|
||||||
|
|
||||||
// Get all events from EventRegistry (single source of truth)
|
|
||||||
$all_events = EventRegistry::get_all_events();
|
|
||||||
|
|
||||||
// Get email templates from DefaultTemplates
|
|
||||||
$allEmailTemplates = EmailDefaultTemplates::get_all_templates();
|
|
||||||
|
|
||||||
foreach ($all_events as $event) {
|
|
||||||
$event_id = $event['id'];
|
|
||||||
$recipient_type = $event['recipient_type'];
|
|
||||||
// Get template body from the new clean markdown source
|
|
||||||
$body = $allEmailTemplates[$recipient_type][$event_id] ?? '';
|
|
||||||
$subject = EmailDefaultTemplates::get_default_subject($recipient_type, $event_id);
|
|
||||||
|
|
||||||
// If template doesn't exist, create a simple fallback
|
|
||||||
if (empty($body)) {
|
|
||||||
$body = "[card]\n\n## Notification\n\nYou have a new notification about {$event_id}.\n\n[/card]";
|
|
||||||
$subject = __('Notification from {store_name}', 'woonoow');
|
|
||||||
}
|
|
||||||
|
|
||||||
$templates["{$recipient_type}_{$event_id}_email"] = [
|
|
||||||
'event_id' => $event_id,
|
|
||||||
'channel_id' => 'email',
|
|
||||||
'recipient_type' => $recipient_type,
|
|
||||||
'subject' => $subject,
|
|
||||||
'body' => $body,
|
|
||||||
'variables' => self::get_variables_for_event($event_id),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add push notification templates
|
|
||||||
$templates['staff_order_placed_push'] = [
|
|
||||||
'event_id' => 'order_placed',
|
|
||||||
'channel_id' => 'push',
|
|
||||||
'recipient_type' => 'staff',
|
|
||||||
'subject' => __('New Order #{order_number}', 'woonoow'),
|
|
||||||
'body' => __('New order from {customer_name} - {order_total}', 'woonoow'),
|
|
||||||
'variables' => self::get_order_variables(),
|
|
||||||
];
|
|
||||||
$templates['customer_order_processing_push'] = [
|
|
||||||
'event_id' => 'order_processing',
|
|
||||||
'channel_id' => 'push',
|
|
||||||
'recipient_type' => 'customer',
|
|
||||||
'subject' => __('Order Processing', 'woonoow'),
|
|
||||||
'body' => __('Your order #{order_number} is being processed', 'woonoow'),
|
|
||||||
'variables' => self::get_order_variables(),
|
|
||||||
];
|
|
||||||
$templates['customer_order_completed_push'] = [
|
|
||||||
'event_id' => 'order_completed',
|
|
||||||
'channel_id' => 'push',
|
|
||||||
'recipient_type' => 'customer',
|
|
||||||
'subject' => __('Order Completed', 'woonoow'),
|
|
||||||
'body' => __('Your order #{order_number} has been completed!', 'woonoow'),
|
|
||||||
'variables' => self::get_order_variables(),
|
|
||||||
];
|
|
||||||
$templates['staff_order_cancelled_push'] = [
|
|
||||||
'event_id' => 'order_cancelled',
|
|
||||||
'channel_id' => 'push',
|
|
||||||
'recipient_type' => 'staff',
|
|
||||||
'subject' => __('Order Cancelled', 'woonoow'),
|
|
||||||
'body' => __('Order #{order_number} has been cancelled', 'woonoow'),
|
|
||||||
'variables' => self::get_order_variables(),
|
|
||||||
];
|
|
||||||
$templates['customer_order_refunded_push'] = [
|
|
||||||
'event_id' => 'order_refunded',
|
|
||||||
'channel_id' => 'push',
|
|
||||||
'recipient_type' => 'customer',
|
|
||||||
'subject' => __('Order Refunded', 'woonoow'),
|
|
||||||
'body' => __('Your order #{order_number} has been refunded', 'woonoow'),
|
|
||||||
'variables' => self::get_order_variables(),
|
|
||||||
];
|
|
||||||
$templates['staff_low_stock_push'] = [
|
|
||||||
'event_id' => 'low_stock',
|
|
||||||
'channel_id' => 'push',
|
|
||||||
'recipient_type' => 'staff',
|
|
||||||
'subject' => __('Low Stock Alert', 'woonoow'),
|
|
||||||
'body' => __('{product_name} is running low on stock', 'woonoow'),
|
|
||||||
'variables' => self::get_product_variables(),
|
|
||||||
];
|
|
||||||
$templates['staff_out_of_stock_push'] = [
|
|
||||||
'event_id' => 'out_of_stock',
|
|
||||||
'channel_id' => 'push',
|
|
||||||
'recipient_type' => 'staff',
|
|
||||||
'subject' => __('Out of Stock Alert', 'woonoow'),
|
|
||||||
'body' => __('{product_name} is now out of stock', 'woonoow'),
|
|
||||||
'variables' => self::get_product_variables(),
|
|
||||||
];
|
|
||||||
$templates['customer_new_customer_push'] = [
|
|
||||||
'event_id' => 'new_customer',
|
|
||||||
'channel_id' => 'push',
|
|
||||||
'recipient_type' => 'customer',
|
|
||||||
'subject' => __('Welcome!', 'woonoow'),
|
|
||||||
'body' => __('Welcome to {store_name}, {customer_name}!', 'woonoow'),
|
|
||||||
'variables' => self::get_customer_variables(),
|
|
||||||
];
|
|
||||||
$templates['customer_customer_note_push'] = [
|
|
||||||
'event_id' => 'customer_note',
|
|
||||||
'channel_id' => 'push',
|
|
||||||
'recipient_type' => 'customer',
|
|
||||||
'subject' => __('Order Note Added', 'woonoow'),
|
|
||||||
'body' => __('A note has been added to order #{order_number}', 'woonoow'),
|
|
||||||
'variables' => self::get_order_variables(),
|
|
||||||
];
|
|
||||||
|
|
||||||
return $templates;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get variables for a specific event
|
|
||||||
*
|
|
||||||
* @param string $event_id Event ID
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private static function get_variables_for_event($event_id) {
|
|
||||||
// Product events
|
|
||||||
if (in_array($event_id, ['low_stock', 'out_of_stock'])) {
|
|
||||||
return self::get_product_variables();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Customer events (but not order-related)
|
|
||||||
if ($event_id === 'new_customer') {
|
|
||||||
return self::get_customer_variables();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscription events
|
|
||||||
if (strpos($event_id, 'subscription_') === 0) {
|
|
||||||
return self::get_subscription_variables();
|
|
||||||
}
|
|
||||||
|
|
||||||
// All other events are order-related
|
|
||||||
return self::get_order_variables();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get available order variables
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function get_order_variables() {
|
|
||||||
return [
|
|
||||||
'order_number' => __('Order Number', 'woonoow'),
|
|
||||||
'order_total' => __('Order Total', 'woonoow'),
|
|
||||||
'order_status' => __('Order Status', 'woonoow'),
|
|
||||||
'order_date' => __('Order Date', 'woonoow'),
|
|
||||||
'order_url' => __('Order URL', 'woonoow'),
|
|
||||||
'order_items_list' => __('Order Items (formatted list)', 'woonoow'),
|
|
||||||
'order_items_table' => __('Order Items (formatted table)', 'woonoow'),
|
|
||||||
'payment_method' => __('Payment Method', 'woonoow'),
|
|
||||||
'payment_url' => __('Payment URL (for pending payments)', 'woonoow'),
|
|
||||||
'shipping_method' => __('Shipping Method', 'woonoow'),
|
|
||||||
'tracking_number' => __('Tracking Number', 'woonoow'),
|
|
||||||
'refund_amount' => __('Refund Amount', 'woonoow'),
|
|
||||||
'customer_name' => __('Customer Name', 'woonoow'),
|
|
||||||
'customer_email' => __('Customer Email', 'woonoow'),
|
|
||||||
'customer_phone' => __('Customer Phone', 'woonoow'),
|
|
||||||
'billing_address' => __('Billing Address', 'woonoow'),
|
|
||||||
'shipping_address' => __('Shipping Address', 'woonoow'),
|
|
||||||
'store_name' => __('Store Name', 'woonoow'),
|
|
||||||
'store_url' => __('Store URL', 'woonoow'),
|
|
||||||
'store_email' => __('Store Email', 'woonoow'),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get available product variables
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function get_product_variables() {
|
|
||||||
return [
|
|
||||||
'product_name' => __('Product Name', 'woonoow'),
|
|
||||||
'product_sku' => __('Product SKU', 'woonoow'),
|
|
||||||
'product_url' => __('Product URL', 'woonoow'),
|
|
||||||
'stock_quantity' => __('Stock Quantity', 'woonoow'),
|
|
||||||
'store_name' => __('Store Name', 'woonoow'),
|
|
||||||
'store_url' => __('Store URL', 'woonoow'),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get available customer variables
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function get_customer_variables() {
|
|
||||||
return [
|
|
||||||
'customer_name' => __('Customer Name', 'woonoow'),
|
|
||||||
'customer_email' => __('Customer Email', 'woonoow'),
|
|
||||||
'customer_phone' => __('Customer Phone', 'woonoow'),
|
|
||||||
'store_name' => __('Store Name', 'woonoow'),
|
|
||||||
'store_url' => __('Store URL', 'woonoow'),
|
|
||||||
'store_email' => __('Store Email', 'woonoow'),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get available subscription variables
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function get_subscription_variables() {
|
|
||||||
return [
|
|
||||||
'subscription_id' => __('Subscription ID', 'woonoow'),
|
|
||||||
'subscription_status' => __('Subscription Status', 'woonoow'),
|
|
||||||
'product_name' => __('Product Name', 'woonoow'),
|
|
||||||
'billing_period' => __('Billing Period (e.g., Monthly)', 'woonoow'),
|
|
||||||
'recurring_amount' => __('Recurring Amount', 'woonoow'),
|
|
||||||
'next_payment_date' => __('Next Payment Date', 'woonoow'),
|
|
||||||
'end_date' => __('Subscription End Date', 'woonoow'),
|
|
||||||
'cancel_reason' => __('Cancellation Reason', 'woonoow'),
|
|
||||||
'customer_name' => __('Customer Name', 'woonoow'),
|
|
||||||
'customer_email' => __('Customer Email', 'woonoow'),
|
|
||||||
'store_name' => __('Store Name', 'woonoow'),
|
|
||||||
'store_url' => __('Store URL', 'woonoow'),
|
|
||||||
'my_account_url' => __('My Account URL', 'woonoow'),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace variables in template
|
|
||||||
*
|
|
||||||
* @param string $content Content with variables
|
|
||||||
* @param array $data Data to replace variables
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function replace_variables($content, $data) {
|
|
||||||
foreach ($data as $key => $value) {
|
|
||||||
$content = str_replace('{' . $key . '}', $value, $content);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -338,7 +338,7 @@ class TemplateOverride
|
|||||||
if (is_wc_endpoint_url('order-pay')) {
|
if (is_wc_endpoint_url('order-pay')) {
|
||||||
global $wp;
|
global $wp;
|
||||||
$order_id = $wp->query_vars['order-pay'];
|
$order_id = $wp->query_vars['order-pay'];
|
||||||
wp_redirect($build_route('order-pay/' . $order_id), 302);
|
wp_redirect($build_route('checkout/pay/' . $order_id), 302);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user