refactor(autoload): migrate theme classes to Composer PSR-4
Move 288 types from global AKSRT_* + inc/classes manual requires into
namespace AKSRT\{Domain} under src/AKSRT/, wire composer.json psr-4,
and slim bootstrap to helpers/modules/hooks only. Split multi-class
widget/gateway files, update admin contract audit paths, and document
the layout in ADR/README.
This commit is contained in:
1 parent
8f349b40fb
commit
aaf458af1c
404 files changed
+13602
-7705
No files matched your search
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Content;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait PayRest {
|
||||
|
||||
|
||||
|
||||
public function register_routes(): void {
|
||||
register_rest_route('aksrt/v1', '/pay/content/(?P<id>\d+)', [
|
||||
[
|
||||
'methods' => 'GET',
|
||||
'callback' => [$this, 'rest_preview'],
|
||||
'permission_callback' => [$this, 'check_rest_nonce'],
|
||||
],
|
||||
[
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'rest_purchase'],
|
||||
'permission_callback' => [$this, 'check_rest_nonce'],
|
||||
],
|
||||
]);
|
||||
|
||||
register_rest_route('aksrt/v1', '/pay/content/order/(?P<order_number>[A-Za-z0-9_-]+)', [
|
||||
'methods' => 'GET',
|
||||
'callback' => [$this, 'rest_order_status'],
|
||||
'permission_callback' => [$this, 'check_rest_nonce'],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* REST nonce for logged-in users (wp_rest). Guests get 401 on purchase paths.
|
||||
*
|
||||
* @param \WP_REST_Request $request
|
||||
* @return true|\WP_Error
|
||||
*/
|
||||
public function check_rest_nonce(\WP_REST_Request $request) {
|
||||
$nonce = $request->get_header('x-wp-nonce');
|
||||
if (!$nonce) {
|
||||
return new \WP_Error('aksrt_rest_no_nonce', __('缺少安全令牌', 'aksrt'), ['status' => 403]);
|
||||
}
|
||||
if (!wp_verify_nonce($nonce, 'wp_rest')) {
|
||||
return new \WP_Error('aksrt_rest_bad_nonce', __('安全令牌已过期,请刷新页面', 'aksrt'), ['status' => 403]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* GET /pay/content/{id}
|
||||
* query: coupon_code?, price_type?
|
||||
*/
|
||||
public function rest_preview(\WP_REST_Request $request) {
|
||||
$post_id = (int) $request['id'];
|
||||
$uid = get_current_user_id();
|
||||
$coupon_code = sanitize_text_field((string) ($request->get_param('coupon_code') ?? $request->get_param('coupon') ?? ''));
|
||||
$price_type = sanitize_key((string) ($request->get_param('price_type') ?? ''));
|
||||
if ($price_type !== 'download_limit_over') {
|
||||
$price_type = '';
|
||||
}
|
||||
$preview = $this->preview($post_id, $uid, $coupon_code, $price_type);
|
||||
if (is_wp_error($preview)) {
|
||||
return $preview;
|
||||
}
|
||||
return new \WP_REST_Response($preview, 200);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* POST /pay/content/{id}
|
||||
* body: { mode?: points|cash, payment_method?: balance|alipay|..., price_type?: ''|download_limit_over, coupon_code?: string }
|
||||
*/
|
||||
public function rest_purchase(\WP_REST_Request $request) {
|
||||
$post_id = (int) $request['id'];
|
||||
$uid = get_current_user_id();
|
||||
|
||||
$params = $request->get_json_params();
|
||||
if (!is_array($params)) {
|
||||
$params = $request->get_params();
|
||||
}
|
||||
|
||||
$price_type = sanitize_key((string) ($params['price_type'] ?? ''));
|
||||
if ($price_type !== 'download_limit_over') {
|
||||
$price_type = '';
|
||||
}
|
||||
$coupon_code = sanitize_text_field((string) ($params['coupon_code'] ?? $params['coupon'] ?? ''));
|
||||
|
||||
// Guest allowed only for cash + pay_limit=0 + setting on.
|
||||
if ($uid <= 0) {
|
||||
$post = get_post($post_id);
|
||||
$allow_guest = function_exists('aksrt_allow_guest_content_pay') && aksrt_allow_guest_content_pay($post);
|
||||
if (!$allow_guest) {
|
||||
return new \WP_Error('aksrt_need_login', __('请先登录后再购买。', 'aksrt'), [
|
||||
'status' => 401,
|
||||
'login_url' => wp_login_url($post ? get_permalink($post) : home_url('/')),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$gate = $this->assert_purchasable($post_id, $uid, $price_type);
|
||||
if (is_wp_error($gate)) {
|
||||
return $gate;
|
||||
}
|
||||
|
||||
$priced = $gate['priced'];
|
||||
$pay_modo = (($priced['modo'] ?? '0') === 'points') ? 'points' : '0';
|
||||
|
||||
$mode = sanitize_key((string) ($params['mode'] ?? ''));
|
||||
if ($mode === '') {
|
||||
$mode = $pay_modo === 'points' ? 'points' : 'cash';
|
||||
}
|
||||
if ($mode === 'points' || $pay_modo === 'points') {
|
||||
if ($uid <= 0) {
|
||||
return new \WP_Error('aksrt_need_login', __('积分支付请先登录。', 'aksrt'), ['status' => 401]);
|
||||
}
|
||||
// Points path: ignore coupon (zibll parity).
|
||||
return $this->rest_wrap_result($this->create_and_pay_points($post_id, $uid, $price_type));
|
||||
}
|
||||
|
||||
$payment_method = sanitize_key((string) ($params['payment_method'] ?? 'balance'));
|
||||
if ($payment_method === '' || $payment_method === 'cash') {
|
||||
$payment_method = 'balance';
|
||||
}
|
||||
// Guests cannot use balance/points gateways.
|
||||
if ($uid <= 0 && in_array($payment_method, ['balance', 'points'], true)) {
|
||||
$payment_method = 'alipay';
|
||||
}
|
||||
return $this->rest_wrap_result($this->create_cash_order($post_id, $uid, $payment_method, $price_type, $coupon_code));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* GET /pay/content/order/{order_number}
|
||||
*/
|
||||
public function rest_order_status(\WP_REST_Request $request) {
|
||||
$uid = get_current_user_id();
|
||||
$order_number = sanitize_text_field((string) $request['order_number']);
|
||||
if ($order_number === '' || !class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
return new \WP_Error('aksrt_invalid', __('订单不存在', 'aksrt'), ['status' => 404]);
|
||||
}
|
||||
$repo = \AKSRT\Payment\OrderRepository::get_instance();
|
||||
if ($uid > 0) {
|
||||
$order = $repo->get_user_order($uid, $order_number);
|
||||
} else {
|
||||
$order = $repo->get($order_number);
|
||||
// Guest may only poll own guest cash orders (user_id=0): content/topic resource or plate_follow.
|
||||
$ptype = sanitize_key((string) ($order['product_type'] ?? ''));
|
||||
$guest_types = [
|
||||
\AKSRT\Core\DomainModel::ORDER_PRODUCT_RESOURCE,
|
||||
\AKSRT\Core\DomainModel::ORDER_PRODUCT_PLATE_FOLLOW,
|
||||
];
|
||||
if ($order && ((int) ($order['user_id'] ?? -1) !== 0 || !in_array($ptype, $guest_types, true))) {
|
||||
$order = null;
|
||||
}
|
||||
}
|
||||
if (!$order) {
|
||||
return new \WP_Error('aksrt_invalid', __('订单不存在', 'aksrt'), ['status' => 404]);
|
||||
}
|
||||
$status = (string) ($order['status'] ?? '');
|
||||
$paid = class_exists('\AKSRT\Core\DomainModel') && \AKSRT\Core\DomainModel::is_paid_order_status($status);
|
||||
if ($paid && $uid <= 0 && function_exists('aksrt_set_guest_pay_cookies')) {
|
||||
// Ensure cookie set even if notify raced ahead of browser cookie.
|
||||
aksrt_set_guest_pay_cookies($order);
|
||||
}
|
||||
return new \WP_REST_Response([
|
||||
'order_number' => $order_number,
|
||||
'status' => $status,
|
||||
'paid' => $paid,
|
||||
'amount' => (float) ($order['amount'] ?? 0),
|
||||
'currency' => (string) ($order['currency'] ?? ''),
|
||||
'product_id' => (int) ($order['product_id'] ?? 0),
|
||||
'product_type' => (string) ($order['product_type'] ?? ''),
|
||||
'reload' => $paid,
|
||||
], 200);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param array|\WP_Error $result
|
||||
* @return \WP_REST_Response|\WP_Error
|
||||
*/
|
||||
private function rest_wrap_result($result) {
|
||||
if (is_wp_error($result)) {
|
||||
return $result;
|
||||
}
|
||||
if (empty($result['success'])) {
|
||||
$code = sanitize_key((string) ($result['code'] ?? 'aksrt_pay_failed'));
|
||||
$status = (int) ($result['status'] ?? 400);
|
||||
return new \WP_Error($code !== '' ? $code : 'aksrt_pay_failed', (string) ($result['message'] ?? __('支付失败', 'aksrt')), array_merge(
|
||||
['status' => $status > 0 ? $status : 400],
|
||||
is_array($result['data'] ?? null) ? $result['data'] : []
|
||||
));
|
||||
}
|
||||
return new \WP_REST_Response($result, 200);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user