533 lines
16 KiB
PHP
533 lines
16 KiB
PHP
<?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);
|
||
}
|
||
} |