subscription/includes/class-yoone-api.php

533 lines
16 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* API类
*
* @package YooneSubscriptions
*/
if (!defined('ABSPATH')) {
exit;
}
/**
* Yoone_API类
* 处理REST API端点和内部API调用
*/
class Yoone_API {
/**
* API版本
*/
const API_VERSION = 'v1';
/**
* API命名空间
*/
const API_NAMESPACE = 'yoone-subscriptions/v1';
/**
* 构造函数
*/
public function __construct() {
add_action('rest_api_init', array($this, 'register_routes'));
}
/**
* 注册REST API路由
*/
public function register_routes() {
// 订阅相关端点
register_rest_route(self::API_NAMESPACE, '/subscriptions', array(
'methods' => 'GET',
'callback' => array($this, 'get_subscriptions'),
'permission_callback' => array($this, 'check_permissions'),
));
register_rest_route(self::API_NAMESPACE, '/subscriptions/(?P<id>\d+)', array(
'methods' => 'GET',
'callback' => array($this, 'get_subscription'),
'permission_callback' => array($this, 'check_permissions'),
));
register_rest_route(self::API_NAMESPACE, '/subscriptions/(?P<id>\d+)', array(
'methods' => 'PUT',
'callback' => array($this, 'update_subscription'),
'permission_callback' => array($this, 'check_permissions'),
));
register_rest_route(self::API_NAMESPACE, '/subscriptions/(?P<id>\d+)/pause', array(
'methods' => 'POST',
'callback' => array($this, 'pause_subscription'),
'permission_callback' => array($this, 'check_permissions'),
));
register_rest_route(self::API_NAMESPACE, '/subscriptions/(?P<id>\d+)/resume', array(
'methods' => 'POST',
'callback' => array($this, 'resume_subscription'),
'permission_callback' => array($this, 'check_permissions'),
));
register_rest_route(self::API_NAMESPACE, '/subscriptions/(?P<id>\d+)/cancel', array(
'methods' => 'POST',
'callback' => array($this, 'cancel_subscription'),
'permission_callback' => array($this, 'check_permissions'),
));
// 混装产品相关端点
register_rest_route(self::API_NAMESPACE, '/bundles', array(
'methods' => 'GET',
'callback' => array($this, 'get_bundles'),
'permission_callback' => array($this, 'check_permissions'),
));
register_rest_route(self::API_NAMESPACE, '/bundles/(?P<id>\d+)', array(
'methods' => 'GET',
'callback' => array($this, 'get_bundle'),
'permission_callback' => array($this, 'check_permissions'),
));
register_rest_route(self::API_NAMESPACE, '/bundles/(?P<id>\d+)/price', array(
'methods' => 'POST',
'callback' => array($this, 'calculate_bundle_price'),
'permission_callback' => '__return_true',
));
// 支付相关端点
register_rest_route(self::API_NAMESPACE, '/payment/tokens', array(
'methods' => 'GET',
'callback' => array($this, 'get_payment_tokens'),
'permission_callback' => array($this, 'check_permissions'),
));
register_rest_route(self::API_NAMESPACE, '/payment/tokens/(?P<id>\d+)', array(
'methods' => 'DELETE',
'callback' => array($this, 'delete_payment_token'),
'permission_callback' => array($this, 'check_permissions'),
));
// Webhook端点
register_rest_route(self::API_NAMESPACE, '/webhook/moneris', array(
'methods' => 'POST',
'callback' => array($this, 'handle_moneris_webhook'),
'permission_callback' => '__return_true',
));
}
/**
* 检查权限
*/
public function check_permissions($request) {
return current_user_can('manage_woocommerce') || current_user_can('edit_shop_orders');
}
/**
* 获取订阅列表
*/
public function get_subscriptions($request) {
$customer_id = $request->get_param('customer_id');
$status = $request->get_param('status');
$page = $request->get_param('page') ?: 1;
$per_page = $request->get_param('per_page') ?: 10;
global $wpdb;
$where_conditions = array('1=1');
$params = array();
if ($customer_id) {
$where_conditions[] = 'customer_id = %d';
$params[] = $customer_id;
}
if ($status) {
$where_conditions[] = 'status = %s';
$params[] = $status;
}
$where_clause = implode(' AND ', $where_conditions);
$offset = ($page - 1) * $per_page;
$query = $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}yoone_subscriptions
WHERE {$where_clause}
ORDER BY created_at DESC
LIMIT %d OFFSET %d",
array_merge($params, array($per_page, $offset))
);
$subscriptions = $wpdb->get_results($query);
return rest_ensure_response($subscriptions);
}
/**
* 获取单个订阅
*/
public function get_subscription($request) {
$id = $request->get_param('id');
global $wpdb;
$subscription = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}yoone_subscriptions WHERE id = %d",
$id
));
if (!$subscription) {
return new WP_Error('subscription_not_found', '订阅不存在', array('status' => 404));
}
// 获取订阅项目
$items = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}yoone_subscription_items WHERE subscription_id = %d",
$id
));
$subscription->items = $items;
return rest_ensure_response($subscription);
}
/**
* 更新订阅
*/
public function update_subscription($request) {
$id = $request->get_param('id');
$data = $request->get_json_params();
global $wpdb;
$allowed_fields = array('status', 'next_payment_date', 'billing_period', 'billing_interval');
$update_data = array();
foreach ($allowed_fields as $field) {
if (isset($data[$field])) {
$update_data[$field] = $data[$field];
}
}
if (empty($update_data)) {
return new WP_Error('no_data', '没有提供更新数据', array('status' => 400));
}
$update_data['updated_at'] = current_time('mysql');
$result = $wpdb->update(
$wpdb->prefix . 'yoone_subscriptions',
$update_data,
array('id' => $id),
array('%s'),
array('%d')
);
if ($result === false) {
return new WP_Error('update_failed', '更新失败', array('status' => 500));
}
return rest_ensure_response(array('success' => true, 'message' => '订阅更新成功'));
}
/**
* 暂停订阅
*/
public function pause_subscription($request) {
$id = $request->get_param('id');
global $wpdb;
$result = $wpdb->update(
$wpdb->prefix . 'yoone_subscriptions',
array(
'status' => 'paused',
'updated_at' => current_time('mysql')
),
array('id' => $id),
array('%s', '%s'),
array('%d')
);
if ($result === false) {
return new WP_Error('pause_failed', '暂停失败', array('status' => 500));
}
// 记录日志
Yoone_Logger::info("订阅 {$id} 已暂停", array('subscription_id' => $id));
return rest_ensure_response(array('success' => true, 'message' => '订阅已暂停'));
}
/**
* 恢复订阅
*/
public function resume_subscription($request) {
$id = $request->get_param('id');
global $wpdb;
$result = $wpdb->update(
$wpdb->prefix . 'yoone_subscriptions',
array(
'status' => 'active',
'updated_at' => current_time('mysql')
),
array('id' => $id),
array('%s', '%s'),
array('%d')
);
if ($result === false) {
return new WP_Error('resume_failed', '恢复失败', array('status' => 500));
}
// 记录日志
Yoone_Logger::info("订阅 {$id} 已恢复", array('subscription_id' => $id));
return rest_ensure_response(array('success' => true, 'message' => '订阅已恢复'));
}
/**
* 取消订阅
*/
public function cancel_subscription($request) {
$id = $request->get_param('id');
global $wpdb;
$result = $wpdb->update(
$wpdb->prefix . 'yoone_subscriptions',
array(
'status' => 'cancelled',
'end_date' => current_time('mysql'),
'updated_at' => current_time('mysql')
),
array('id' => $id),
array('%s', '%s', '%s'),
array('%d')
);
if ($result === false) {
return new WP_Error('cancel_failed', '取消失败', array('status' => 500));
}
// 记录日志
Yoone_Logger::info("订阅 {$id} 已取消", array('subscription_id' => $id));
return rest_ensure_response(array('success' => true, 'message' => '订阅已取消'));
}
/**
* 获取混装产品列表
*/
public function get_bundles($request) {
global $wpdb;
$bundles = $wpdb->get_results(
"SELECT * FROM {$wpdb->prefix}yoone_bundles WHERE status = 'active' ORDER BY created_at DESC"
);
return rest_ensure_response($bundles);
}
/**
* 获取单个混装产品
*/
public function get_bundle($request) {
$id = $request->get_param('id');
global $wpdb;
$bundle = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}yoone_bundles WHERE id = %d",
$id
));
if (!$bundle) {
return new WP_Error('bundle_not_found', '混装产品不存在', array('status' => 404));
}
// 获取混装项目
$items = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}yoone_bundle_items WHERE bundle_id = %d ORDER BY sort_order",
$id
));
$bundle->items = $items;
return rest_ensure_response($bundle);
}
/**
* 计算混装产品价格
*/
public function calculate_bundle_price($request) {
$bundle_id = $request->get_param('id');
$quantities = $request->get_param('quantities') ?: array();
global $wpdb;
// 获取混装产品信息
$bundle = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}yoone_bundles WHERE id = %d AND status = 'active'",
$bundle_id
));
if (!$bundle) {
return new WP_Error('bundle_not_found', '混装产品不存在', array('status' => 404));
}
// 获取混装项目
$items = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}yoone_bundle_items WHERE bundle_id = %d",
$bundle_id
));
$total_price = 0;
$item_details = array();
foreach ($items as $item) {
$product = wc_get_product($item->product_id);
if (!$product) {
continue;
}
$quantity = isset($quantities[$item->product_id]) ? intval($quantities[$item->product_id]) : $item->quantity;
$price = $product->get_price();
$subtotal = $price * $quantity;
$total_price += $subtotal;
$item_details[] = array(
'product_id' => $item->product_id,
'name' => $product->get_name(),
'price' => $price,
'quantity' => $quantity,
'subtotal' => $subtotal
);
}
// 应用折扣
$discount_amount = 0;
if ($bundle->discount_type === 'percentage') {
$discount_amount = $total_price * ($bundle->discount_value / 100);
} elseif ($bundle->discount_type === 'fixed') {
$discount_amount = $bundle->discount_value;
}
$final_price = max(0, $total_price - $discount_amount);
return rest_ensure_response(array(
'bundle_id' => $bundle_id,
'original_price' => $total_price,
'discount_amount' => $discount_amount,
'final_price' => $final_price,
'items' => $item_details
));
}
/**
* 获取支付令牌
*/
public function get_payment_tokens($request) {
$customer_id = $request->get_param('customer_id') ?: get_current_user_id();
global $wpdb;
$tokens = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}yoone_payment_tokens
WHERE customer_id = %d AND (expires_at IS NULL OR expires_at > NOW())
ORDER BY is_default DESC, created_at DESC",
$customer_id
));
return rest_ensure_response($tokens);
}
/**
* 删除支付令牌
*/
public function delete_payment_token($request) {
$id = $request->get_param('id');
global $wpdb;
$result = $wpdb->delete(
$wpdb->prefix . 'yoone_payment_tokens',
array('id' => $id),
array('%d')
);
if ($result === false) {
return new WP_Error('delete_failed', '删除失败', array('status' => 500));
}
return rest_ensure_response(array('success' => true, 'message' => '支付令牌已删除'));
}
/**
* 处理Moneris Webhook
*/
public function handle_moneris_webhook($request) {
$body = $request->get_body();
$headers = $request->get_headers();
// 验证webhook签名
if (!$this->verify_webhook_signature($body, $headers)) {
return new WP_Error('invalid_signature', '无效的签名', array('status' => 401));
}
$data = json_decode($body, true);
if (!$data) {
return new WP_Error('invalid_data', '无效的数据', array('status' => 400));
}
// 处理不同类型的webhook事件
switch ($data['event_type']) {
case 'payment_success':
$this->handle_payment_success($data);
break;
case 'payment_failed':
$this->handle_payment_failed($data);
break;
case 'subscription_cancelled':
$this->handle_subscription_cancelled($data);
break;
default:
Yoone_Logger::warning('未知的webhook事件类型: ' . $data['event_type']);
}
return rest_ensure_response(array('success' => true));
}
/**
* 验证webhook签名
*/
private function verify_webhook_signature($body, $headers) {
// 这里应该实现Moneris的签名验证逻辑
// 暂时返回true实际使用时需要根据Moneris文档实现
return true;
}
/**
* 处理支付成功
*/
private function handle_payment_success($data) {
// 实现支付成功处理逻辑
Yoone_Logger::info('收到支付成功webhook', $data);
}
/**
* 处理支付失败
*/
private function handle_payment_failed($data) {
// 实现支付失败处理逻辑
Yoone_Logger::warning('收到支付失败webhook', $data);
}
/**
* 处理订阅取消
*/
private function handle_subscription_cancelled($data) {
// 实现订阅取消处理逻辑
Yoone_Logger::info('收到订阅取消webhook', $data);
}
}