Files
wordpress-theme-aksrt/inc/classes/class-aksrt-order.php
T
kate522 8e9e05c859 feat(pay): guest cash purchase with signed cookie unlock (zibll-style)
Allow pay_no_logged_in cash content orders (user_id=0), set aksrt_pay_* cookies
on paid, accept cookie in access state, and expose guest CTA on paywall/box.
2026-07-14 12:19:22 +08:00

148 lines
5.4 KiB
PHP

<?php
/**
* AKSRT Order facade.
*
* Keeps the public contract stable while delegating storage to
* `AKSRT_Order_Repository`.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
class AKSRT_Order {
public const STATUS_CREATED = AKSRT_Domain_Model::ORDER_STATUS_CREATED;
public const STATUS_PENDING = AKSRT_Domain_Model::ORDER_STATUS_PENDING;
public const STATUS_PAID = AKSRT_Domain_Model::ORDER_STATUS_PAID;
public const STATUS_DELIVERED = AKSRT_Domain_Model::ORDER_STATUS_DELIVERED;
public const STATUS_COMPLETED = AKSRT_Domain_Model::ORDER_STATUS_COMPLETED;
public const STATUS_CANCELLED = AKSRT_Domain_Model::ORDER_STATUS_CANCELLED;
public const STATUS_EXPIRED = AKSRT_Domain_Model::ORDER_STATUS_EXPIRED;
public const STATUS_REFUNDING = AKSRT_Domain_Model::ORDER_STATUS_REFUNDING;
public const STATUS_REFUNDED = AKSRT_Domain_Model::ORDER_STATUS_REFUNDED;
public const STATUS_PENDING_REVIEW = AKSRT_Domain_Model::ORDER_STATUS_PENDING_REVIEW;
private static $instance = null;
public static function get_instance(): self {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public static function get_valid_statuses(): array {
return AKSRT_Domain_Model::order_status_labels();
}
public static function can_transition(string $from, string $to): bool {
return AKSRT_Domain_Model::can_transition_order_status($from, $to);
}
public function init(): void {
add_action('wp_ajax_aksrt_order_list', [$this, 'ajax_list']);
add_action('wp_ajax_aksrt_order_update_status', [$this, 'ajax_update_status']);
add_action('wp_ajax_aksrt_order_stats', [$this, 'ajax_stats']);
}
public function create(int $user_id, string $product_type, int $product_id, float $amount, string $currency = 'CNY'): string {
// Guest content orders may use user_id=0 (Zibll pay_no_logged_in).
if ($user_id < 0 || $product_id < 0 || $amount < 0) {
return '';
}
return $this->repo()->create([
'user_id' => $user_id,
'product_type' => sanitize_key($product_type),
'product_id' => $product_id,
'amount' => $amount,
'currency' => sanitize_key($currency),
'status' => self::STATUS_PENDING,
]);
}
public function get(string $order_number): ?array {
return $this->repo()->get($order_number);
}
public function get_user_orders(int $user_id, array $args = []): array {
return $this->repo()->get_user_orders($user_id, $args);
}
public function update_status(string $order_number, string $new_status, array $extra = []): bool {
return $this->repo()->update_status($order_number, $new_status, $extra);
}
public function get_stats(int $user_id = 0): array {
return $this->repo()->get_stats($user_id);
}
public function has_paid_access(int $user_id, string $product_type, int $product_id): bool {
return $this->repo()->has_paid_access($user_id, $product_type, $product_id);
}
public function get_valid_vip_order(int $user_id): ?array {
return $this->repo()->get_valid_vip_order($user_id);
}
public function ajax_list(): void {
check_ajax_referer('aksrt_public_nonce', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => __('权限不足', 'aksrt')]);
}
$status = sanitize_key((string) ($_GET['status'] ?? ''));
$search = sanitize_text_field((string) ($_GET['search'] ?? ''));
$limit = max(1, min(100, (int) ($_GET['limit'] ?? 20)));
$offset = max(0, (int) ($_GET['offset'] ?? 0));
$orders = $this->repo()->get_orders_admin([
'status' => $status,
'limit' => $limit,
'offset' => $offset,
], $search);
wp_send_json_success([
'orders' => $orders,
'stats' => $this->get_stats(),
'total' => $this->repo()->get_orders_count(['status' => $status], $search),
]);
}
public function ajax_update_status(): void {
check_ajax_referer('aksrt_public_nonce', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => __('权限不足', 'aksrt')]);
}
$order_number = sanitize_text_field((string) ($_POST['order_number'] ?? ''));
$new_status = sanitize_key((string) ($_POST['status'] ?? ''));
$note = sanitize_textarea_field((string) ($_POST['note'] ?? ''));
if ($order_number === '' || $new_status === '') {
wp_send_json_error(['message' => __('参数无效', 'aksrt')]);
}
if ($this->update_status($order_number, $new_status, ['note' => $note, 'force' => true])) {
wp_send_json_success(['message' => __('订单状态已更新', 'aksrt')]);
}
wp_send_json_error(['message' => __('更新失败', 'aksrt')]);
}
public function ajax_stats(): void {
check_ajax_referer('aksrt_public_nonce', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => __('权限不足', 'aksrt')]);
}
wp_send_json_success($this->get_stats());
}
private function repo(): AKSRT_Order_Repository {
return AKSRT_Order_Repository::get_instance();
}
}