\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[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 resource. $ptype = sanitize_key((string) ($order['product_type'] ?? '')); $guest_types = [ \AKSRT\Core\DomainModel::ORDER_PRODUCT_RESOURCE, ]; 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]); } if (class_exists('\AKSRT\Payment\Gateway')) { $order = \AKSRT\Payment\Gateway::get_instance()->reconcile_order($order_number, $order) ?: $order; } $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); } }