| Server IP : 43.141.49.119 / Your IP : 113.219.202.44 Web Server : Apache System : Linux VM-8-5-opencloudos 6.6.34-9.oc9.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Jun 19 19:35:45 CST 2024 x86_64 User : www ( 1000) PHP Version : 8.1.27 Disable Function : passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /www/wwwroot/www.ucppt.com/wp-content/plugins/wpcom-member-pro/includes/ |
Upload File : |
<?php
namespace WPCOM\Member;
use WPCOM\Themer\Session;
use WPCOM\Themer\Plugin\Utils;
defined( 'ABSPATH' ) || exit;
class Order {
protected static $_order_table = 'wpcom_orders';
protected static $_order_item_table = 'wpcom_order_items';
function __construct() {
global $wpdb;
$wpdb->order_table = $wpdb->prefix . self::$_order_table;
$wpdb->order_item_table = $wpdb->prefix . self::$_order_item_table;
add_action('admin_menu', array($this, 'admin_menu'));
add_action('wpcom_themer_maybe_updated', function(){ $this->init_database(); });
add_action('wp_ajax_wpcom_setup_payment', array($this, 'setup_payment'));
add_action('wp_ajax_nopriv_wpcom_setup_payment', array($this, 'setup_payment'));
add_action('wp_ajax_wpcom_setup_order', array($this, 'setup_order'));
add_action('wp_ajax_nopriv_wpcom_setup_order', array($this, 'setup_order'));
add_action('wp_ajax_wpcom_update_order_qty', array($this, 'update_order_qty'));
add_action('wp_ajax_nopriv_wpcom_update_order_qty', array($this, 'update_order_qty'));
add_action('wp_ajax_wpcom_get_order_payment', array($this, 'get_order_payment'));
add_action('wp_ajax_nopriv_wpcom_get_order_payment', array($this, 'get_order_payment'));
add_action('wp_ajax_wpcom_checkout_payment', array($this, 'checkout_payment_page'));
add_action('wp_ajax_nopriv_wpcom_checkout_payment', array($this, 'checkout_payment_page'));
add_action('wp_ajax_wpcom_orders_export', array($this, 'orders_export'));
add_action('wp_ajax_wpcom_checkout_return', array($this, 'checkout_return_page'));
add_action('wp_ajax_nopriv_wpcom_checkout_return', array($this, 'checkout_return_page'));
add_action('wpcom_order_payment_complete', array($this, 'send_order_notify'));
add_filter('parent_file', array($this, 'parent_file_filter'));
add_filter('submenu_file', array($this, 'submenu_file_filter'));
add_filter('wpcom_member_order_metas', array($this, 'order_metas'), 1);
add_filter('wpcom_account_tabs', array($this, 'account_tabs'), 20);
add_filter('wpmx_localize_script', array($this, 'localize_script'));
add_action('rest_api_init', array($this, 'for_rest_api'), 20 );
add_action('wpcom_auto_close_unpaid_order', array($this, 'close_unpaid_order'));
}
public function admin_menu() {
$options = $GLOBALS['wpmx_options'];
add_submenu_page('wpcom-member-pro', '订单列表', '订单列表', 'manage_options', 'wpcom-orders', array(&$this, 'orders'), 10);
add_submenu_page('wpcom-orders', '编辑订单', '编辑订单', 'manage_options', 'wpcom-edit-order', array(&$this, 'edit_order'), 10);
if(isset($_GET['page']) && $_GET['page'] === 'wpcom-orders' && isset($_POST)){
$search = isset($_POST['s']) ? sanitize_text_field(trim($_POST['s'])) : '';
$args = [];
if($search !== '') $args['s'] = $search;
if(isset($_POST['filter_action']) && isset($_POST['order_type'])) $args['order_type'] = sanitize_text_field($_POST['order_type']);
if(!empty($args)){
wp_redirect(add_query_arg($args));
exit;
}
}
$days = isset($options['order_close']) && $options['order_close'] ? (int)$options['order_close'] : 3;
$days = $days && $days > 0 ? $days : 3;
if (is_numeric($days) && !wp_next_scheduled ( 'wpcom_auto_close_unpaid_order' )) wp_schedule_event(time(), 'hourly', 'wpcom_auto_close_unpaid_order');
}
function close_unpaid_order(){
global $wpdb;
$options = $GLOBALS['wpmx_options'];
$days = isset($options['order_close']) && $options['order_close'] ? (int)$options['order_close'] : 3;
$days = $days && $days > 0 ? $days : 3;
if(is_numeric($days)){
$time = current_time('U') - DAY_IN_SECONDS * $days;
$time = date( 'Y-m-d H:i:s', $time );
$orders = $this->get_orders(array(
'status' => 'unpaid',
'time' => $wpdb->prepare("< %s", $time),
'per_page' => 100
));
if($orders && is_array($orders) && count($orders) > 0){
foreach($orders as $order){
$wpdb->update($wpdb->order_table, array('status' => 'cancel'), array('ID' => $order->ID));
}
}
}else{
wp_clear_scheduled_hook('wpcom_auto_close_unpaid_order');
}
}
public function parent_file_filter($parent_file) {
global $_wp_real_parent_file, $plugin_page, $_wp_menu_nopriv;
if($plugin_page === 'wpcom-edit-order'){
$_wp_real_parent_file[$plugin_page] = 'wpcom-member-pro';
$_wp_menu_nopriv[$plugin_page] = 'wpcom-member-pro';
if ($parent_file === 'wpcom-orders') $parent_file = 'wpcom-member-pro';
}
return $parent_file;
}
public function submenu_file_filter($submenu_file) {
global $pagenow;
$screen = get_current_screen();
if ($pagenow == 'admin.php' && $screen->base == 'admin_page_wpcom-edit-order') {
$submenu_file = 'wpcom-orders';
}
return $submenu_file;
}
function for_rest_api(){
$this->register_api_routes();
}
public function register_api_routes(){
register_rest_route(
'wpcom/v1',
'/payment-notify/(?P<api>[a-zA-Z0-9_-]+)/(?P<id>[\d]+)',
[
[
'methods' => ['GET', 'POST'],
'callback' => [$this, 'notify_set_data'],
'args' => [
'api' => [
'description' => '支付接口',
'type' => 'string',
],
'id' => [
'description' => '订单号',
'type' => 'integer',
]
],
'permission_callback' => '__return_true'
]
]
);
// 用于 paypal webhook 异步通知
register_rest_route(
'wpcom/v1',
'/payment-notify/(?P<api>[a-zA-Z0-9_-]+)',
[
[
'methods' => ['GET', 'POST'],
'callback' => [$this, 'notify_set_data'],
'args' => [
'api' => [
'description' => '支付接口',
'type' => 'string',
]
],
'permission_callback' => '__return_true'
]
]
);
if(defined('WWA_VERSION')){
register_rest_route('wpcom/v1', '/order',
array(
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array($this, 'rest_order_status'),
'args' => array(
'order_id' => array(
'required' => true,
'type' => 'integer',
'validate_callback' => 'rest_validate_request_arg',
),
'nonce' => array(
'required' => true,
'type' => 'string',
'validate_callback' => 'rest_validate_request_arg',
)
),
'permission_callback' => array( $this, 'pay_permission_check' )
),
array(
'methods' => \WP_REST_Server::CREATABLE,
'callback' => array($this, 'rest_order'),
'args' => array(
'items' => array(
'required' => true,
'type' => 'array',
'validate_callback' => 'rest_validate_request_arg',
'items' => array(
'type' => 'object',
'properties' => array(
'title' => array(
'type' => 'string'
),
'number' => array(
'type' => 'integer',
'default' => 1
),
'price' => array(
'type' => 'string',
'required' => true
),
'type' => array(
'type' => 'string',
'required' => true
),
'type_id' => array(
'type' => 'string',
'required' => true
),
'url' => array(
'type' => 'string'
)
)
)
),
'total' => array(
'type' => 'string',
'required' => true
),
// 用户微信小程序支付
'code' => array(
'type' => 'string',
'required' => true
),
'sign' => array(
'type' => 'string'
),
'timestamp' => array(
'type' => 'integer'
),
'nonce' => array(
'required' => true,
'type' => 'string',
'validate_callback' => 'rest_validate_request_arg',
)
),
'permission_callback' => array( $this, 'pay_permission_check' )
)
)
);
}
}
function pay_permission_check($request){
if(isset($request['nonce']) && wp_verify_nonce($request['nonce'], 'wpcom_payment_form')){
return true;
}else{
return new \WP_Error( 'rest_pay_nonce_fail', '请求验证失败' );
}
}
function rest_order($request){
$type = isset($_SERVER['AppType']) ? $_SERVER['AppType'] : (isset($_SERVER['HTTP_APPTYPE']) ? $_SERVER['HTTP_APPTYPE'] : '');
$gateways = apply_filters('wpcom_payment_gateways', []);
$api = $type === 'swan' ? 'bdpay' : 'wxpay';
if($api === 'wxpay' && isset($gateways['wxpay-xunhu'])){
$api = 'wxpay-xunhu';
}
$_POST = array(
'nonce' => $request['nonce'],
'items' => stripslashes_deep($request['items']),
'total' => $request['total'],
'sign' => $request['sign'],
'timestamp' => $request['timestamp'],
'code' => $request['code'],
'gateway' => $api
);
// 低版本组装数据进行兼容
if(defined('WWA_VERSION') && version_compare(WWA_VERSION, '3.10.1', '<=')){
$_POST['timestamp'] = current_time('timestamp', 1);
$data = array(
'result' => 0,
'items' => $request['items'],
'timestamp' => $_POST['timestamp'],
'total' => $request['total']
);
$_POST['sign'] = self::make_sign($data);
}
$res = $this->setup_order(true);
if($res && isset($res['result']) && $res['result'] == 0 && $res['order_id']){
if (isset($gateways[$api])) {
do_action("wpcom_payment_{$api}_receipt_page", $res['order_id']);
}else{
$res['result'] = -9;
$res['msg'] = '支付接口异常';
}
}
return rest_ensure_response( $res );
}
function rest_order_status($request){
$res = [];
$order_id = isset($request['order_id']) && $request['order_id'] ? $request['order_id'] : '';
$order = $order_id ? Order::get_order($order_id) : '';
$user_id = get_current_user_id();
if ($order && isset($order->ID) && (!$order->user || ((int) $order->user === $user_id)) && $order->status === 'paid') {
$res['status'] = 'paid';
}
wp_send_json($res);
}
public function notify_set_data($request){
if($request['api']) {
$order = $request['id'] && is_numeric($request['id']) ? self::get_order($request['id']) : null;
$gateways = apply_filters('wpcom_payment_gateways', []);
$api = $request['api'];
if (isset($gateways[$api]) && $order && isset($order->ID)) {
do_action("wpcom_payment_{$api}_check_response", $request['id']);
} else if(isset($gateways[$api]) && $input = file_get_contents('php://input')){
do_action("wpcom_payment_{$api}_check_response", json_decode($input, true));
}
}
}
public function orders() {
if (!class_exists('Orders_List')) require_once WPCOM_MP_DIR . '/includes/orders-list.php';
$list = new Orders_List();
$list->prepare_items();?>
<div class="wrap">
<h1 class="wp-heading-inline">订单列表</h1>
<a href="<?php echo admin_url('admin.php?page=wpcom-edit-order'); ?>" class="page-title-action">添加订单</a>
<a href="javascript:;" class="page-title-action j-orders-export">导出</a>
<hr class="wp-header-end">
<?php $list->views();?>
<form method="post">
<?php
$list->search_box('搜索订单', 'messages');
$list->display();
?>
</form>
<?php $list->export();?>
</div>
<?php }
public function edit_order() {
$order_id = isset($_GET['id']) ? sanitize_text_field($_GET['id']) : 0;
require_once WPCOM_ADMIN_PATH . 'includes/class-utils.php';
Utils::panel_script();
$error = $this->save_order();
?>
<div class="wrap">
<h1 class="wp-heading-inline">
<?php echo $order_id ? '编辑' : '新增';?>订单
<a class="page-title-action" href="<?php echo admin_url('admin.php?page=wpcom-orders') ?>"><span class="dashicons dashicons-arrow-left-alt" style="line-height: inherit;width:1em;height:1em;font-size:inherit;"></span> 返回</a>
</h1>
<?php if ($error === false || (isset($_GET['status']) && $_GET['status'] === 'success')) { ?>
<div id="message" class="notice notice-success">
<p><strong>订单已更新。</strong></p>
<p>
<a href="<?php echo admin_url('admin.php?page=wpcom-orders'); ?>" style="text-decoration: none;">
<span class="dashicons dashicons-arrow-left-alt" style="line-height: inherit;width:1em;height:1em;font-size:inherit;"></span> 返回"订单列表"页面
</a>
</p>
</div>
<?php } else if ($error) { ?>
<div id="message" class="notice notice-error">
<p><strong>订单更新失败。</strong></p>
<p><?php echo $error; ?></p>
</div>
<?php } ?>
<form name="edit-order" id="edittag" method="post" action="" class="order-edit validate">
<input type="hidden" name="action" value="<?php echo $order_id ? 'edit' : 'new';?>" />
<input type="hidden" name="order_id" value="<?php echo $order_id; ?>" />
<?php wp_nonce_field('order-edit', 'order-edit-nonce'); ?>
<table class="form-table" role="presentation">
<tr id="wpcom-plugin-panel" class="wpcom-term-wrap">
<td colspan="2">
<term-panel :ready="ready" />
</td>
</tr>
</table>
<input type="submit" class="button button-primary" value="提交" />
</form>
<div style="display: none;"><?php wp_editor('EDITOR', 'WPCOM-EDITOR', Utils::editor_settings(array('textarea_name' => 'EDITOR-NAME'))); ?></div>
<script>
if (typeof _plugins_options === 'undefined') _plugins_options = [];
_plugins_options.push(<?php echo $this->get_order_metas(); ?>);
</script>
</div>
<?php }
function save_order() {
global $wpdb;
if (current_user_can('manage_options') && isset($_POST['order_id']) && $_POST['action'] && isset($_POST['wpcom_number'])) {
$data = map_deep(stripslashes_deep($_POST), 'sanitize_text_field');
$order_id = $data['order_id'];
$number = $data['wpcom_number'];
$user = $data['wpcom_user'];
$price = $data['wpcom_price'];
$status = $data['wpcom_status'];
$item_id = $data['wpcom_item_id'];
$item_title = $data['wpcom_item_title'];
$item_price = $data['wpcom_item_price'];
$item_type = $data['wpcom_item_type'];
$item_type_id = $data['wpcom_item_type_id'];
$item_data = stripslashes_deep($data['wpcom_item_data']);
// nonce
if (!wp_verify_nonce($data['order-edit-nonce'], 'order-edit')) {
return '表单校验失败,请刷新页面重试';
}
// price
if ($price === '' || !is_numeric($price)) {
return '订单合计价格填写错误';
}
// user
if ($user !== '' && !is_numeric($user)) {
return '用户ID格式错误:用户ID应是数字';
} else if (is_numeric($user) && $user > 0) {
$_user = get_user_by('ID', $user);
if (!$_user) {
return '用户ID错误:查询无此用户';
}
}
// 订单号
if ($number) {
$order = $this->get_order_by_number($number);
if ($order && $order->ID != $order_id) {
return '订单号已存在,请修改';
}
} else {
$number = $this->init_order_number();
}
// items
if (!$item_price || empty($item_price)) {
return '请添加订单项目';
} else {
foreach ($item_price as $i => $p) {
if ($item_title[$i] === '') {
return '订单项目标题不能为空';
} else if ($p === '' || !is_numeric($p)) {
return '订单项目【' . $item_title[$i] . '】价格填写错误';
}
}
}
$_order = array(
'number' => $number,
'price' => $price,
'user' => $user,
'status' => $status
);
if ($order_id && $order = Order::get_order($order_id)) {
$res = $wpdb->update($wpdb->order_table, $_order, array('ID' => $order_id));
$_status = $order->status;
if ($res !== false) {
$next = 1;
} else {
return '订单更新失败,请稍后再试!';
}
} else if ($order_id) {
return '订单错误,请稍后再试!';
} else if($data['action'] === 'new'){
$_status = 'unpaid';
$_order['time'] = current_time('mysql');
$_order['payment_gateway'] = '';
$format = array('%s', '%f', '%d', '%s', '%s', '%s');
$wpdb->insert($wpdb->order_table, $_order, $format);
$order_id = $wpdb->insert_id;
if ($order_id) {
$next = 1;
}
}
if (isset($next) && $next) {
$items = $this->get_order_items($order_id);
$ids = $items ? array_column($items, 'ID') : [];
if ($item_id && is_array($item_id)) {
// 根据提交信息依次保存更新
foreach ($item_id as $x => $_id) {
$_item = array(
'price' => $item_price[$x],
'title' => $item_title[$x],
'type' => $item_type[$x],
'type_id' => $item_type_id[$x]
);
$_item['data'] = apply_filters('wpcom_update_order_item_data', $item_data[$x], $_item);
if ($_id && in_array($_id, $ids)) {
$res = $wpdb->update($wpdb->order_item_table, $_item, array('ID' => $_id));
if ($res === false) {
return '更新订单项目失败';
}
} else { // 无ID项目(有ID但是不在当前订单内)为新添加
$_item['order_id'] = $order_id;
$format = array('%f', '%s', '%s', '%s', '%s', '%d');
$wpdb->insert($wpdb->order_item_table, $_item, $format);
$_item_id = $wpdb->insert_id;
if (!$_item_id) {
return '添加订单项目失败';
}
}
}
// 根据查询到的已有项目,检查未使用的到,删除
if (!empty($ids)) {
foreach ($ids as $_id) {
if (!in_array($_id, $item_id)) {
$wpdb->delete($wpdb->order_item_table, array('ID' => $_id));
}
}
}
} else {
return '更新订单出错,请稍后再试';
}
}
// 订单被设置为已支付
if(isset($_status) && $_status !== $_order['status'] && $_order['status'] === 'paid'){
self::payment_complete($order_id, isset($_order['payment_gateway']) ? $_order['payment_gateway'] : $order->payment_gateway);
}
if(isset($_status) && $_status !== $_order['status']) do_action('wpcom_order_status_changed', $_order['status'], $_status, $order_id);
if($data['action'] === 'new'){
wp_redirect(admin_url("admin.php?page=wpcom-edit-order&id=$order_id&status=success"));
exit;
}
return false;
}
}
function orders_export(){
global $wpdb;
$res = ['result' => 0];
if (current_user_can('manage_options')) {
$nonce = isset($_REQUEST['wpcom_orders_export_nonce']) ? sanitize_text_field($_REQUEST['wpcom_orders_export_nonce']) : '';
if(wp_verify_nonce($nonce, 'wpcom_orders_export')){
$status = isset($_REQUEST['status']) && $_REQUEST['status'] ? sanitize_text_field($_REQUEST['status']) : '';
$begin_time = isset($_REQUEST['begin_time']) && $_REQUEST['begin_time'] ? sanitize_text_field($_REQUEST['begin_time']) : '';
$end_time = isset($_REQUEST['end_time']) && $_REQUEST['end_time'] ? sanitize_text_field($_REQUEST['end_time']) : current_time('Y-m-d');
$ext = isset($_REQUEST['ext']) && $_REQUEST['ext'] ? sanitize_text_field($_REQUEST['ext']) : 'xlsv';
$time = '';
if($begin_time && $end_time) {
$time = $wpdb->prepare(">= %s AND `time` <= %s", $begin_time . ' 00:00:00', $end_time . ' 23:59:59');
}else if($begin_time) {
$time = $wpdb->prepare(">= %s", $begin_time . ' 00:00:00');
}else if($end_time) {
$time = $wpdb->prepare("<= %s", $end_time . ' 23:59:59');
}
$args = array(
'orderby' => 'time',
'per_page' => 0
);
if($status !== '') $args['status'] = $status;
if($time !== '') $args['time'] = $time;
$orders = $this->get_orders($args);
if($orders && !empty($orders)){
if(isset($_GET['action'])){
$lists = [
['订单号', '购买项目', '用户', '时间', '状态', '合计', '付款方式' ],
];
foreach($orders as $order){
$items = Order::get_order_items($order->ID);
$_items = '';
if ($items) {
foreach ($items as $item) {
if($_items !== '') $_items .= "\n";
$_items .= $item->title;
$_items .= ' - ¥'.$item->price;
}
}
$_user = '';
if(!$order->user) {
$_user = '-';
}else{
$user = get_user_by('ID', $order->user);
if($user && $user->ID){
$_user = $ext === 'csv' ? $user->display_name : '<a href="'.esc_url(get_author_posts_url($user->ID)).'">'.$user->display_name.'</a>';
}else{
$_user = '无效用户';
}
}
$payment_gateway = '';
if($order->payment_gateway){
$gateways = apply_filters('wpcom_payment_gateways', []);
if($gateways && isset($gateways[$order->payment_gateway])){
$payment_gateway = $gateways[$order->payment_gateway]->title;
}else{
$payment_gateway = $order->payment_gateway;
}
}
$lists[] = [
($ext === 'csv' ? "\t" : '') . $order->number,
$_items,
$_user,
($ext === 'csv' ? "\t" : '') . $order->time,
Order::get_order_status_display($order->status),
$order->price,
$payment_gateway
];
}
if($ext === 'csv'){
$output = fopen('php://output', 'w') or die("Can't open php://output");
header("Content-type:application/csv");
header('Content-Disposition:attachment; filename="Orders.csv"');
foreach ($lists as $list) {
fputcsv($output, $list);
}
fclose($output) or die("can't close php://output");
}else{
require_once WPCOM_MP_DIR . 'sdk/xlsx.php';
$xlsx = \Shuchkin\SimpleXLSXGen::fromArray( $lists );
$xlsx->downloadAs('Orders.xlsx');
}
exit;
}
}else{
$res['result'] = 1;
$res['msg'] = '未查询到符合条件的订单';
}
}else{
$res['result'] = -2;
$res['msg'] = '状态异常,建议刷新页面后重试!';
}
}else{
$res['result'] = -1;
$res['msg'] = '抱歉,当前用户没有操作权限';
}
wp_send_json($res);
}
function get_order_metas() {
require_once WPCOM_ADMIN_PATH . 'includes/class-utils.php';
$res = array('type' => 'taxonomy', 'tax' => 'orders');
$order_id = isset($_GET['id']) ? sanitize_text_field($_GET['id']) : 0;
$items = Order::get_order_items($order_id);
if ($order_id && $order = Order::get_order($order_id)) {
$res['options'] = array(
'number' => $order->number,
'price' => $order->price,
'user' => $order->user,
'status' => $order->status
);
$item_title = [];
$item_price = [];
$item_type = [];
$item_type_id = [];
$item_data = [];
$item_id = [];
if (is_array($items)) {
foreach ($items as $i => $item) {
$item_id[$i] = $item->ID;
$item_title[$i] = $item->title;
$item_price[$i] = $item->price;
$item_type[$i] = $item->type;
$item_type_id[$i] = $item->type_id;
$item_data[$i] = $item->data;
}
}
$res['options']['item_id'] = $item_id;
$res['options']['item_title'] = $item_title;
$res['options']['item_price'] = $item_price;
$res['options']['item_type'] = $item_type;
$res['options']['item_type_id'] = $item_type_id;
$res['options']['item_data'] = $item_data;
}else if(!isset($_GET['id'])){
$data = map_deep(stripslashes_deep($_POST), 'sanitize_text_field');
if(isset($data['wpcom_price'])){
$res['options'] = array(
'number' => $data['wpcom_number'],
'price' => $data['wpcom_price'],
'user' => $data['wpcom_user'],
'status' => $data['wpcom_status'],
'item_id' => $data['wpcom_item_id'],
'item_title' => $data['wpcom_item_title'],
'item_price' => $data['wpcom_item_price'],
'item_type' => $data['wpcom_item_type'],
'item_type_id' => $data['wpcom_item_type_id'],
'item_data' => $data['wpcom_item_data']
);
}else{
$res['options'] = array('number' => $this->init_order_number());
}
}
$plugin = $GLOBALS['wpcom_mp']->info;
$res['filters'] = apply_filters('wpcom_member_order_metas', []);
$res['ver'] = $plugin['ver'];
$res['plugin-id'] = $plugin['plugin_id'];
$res['plugin-slug'] = $plugin['slug'];
$res['framework_ver'] = Utils::framework_version($plugin['slug']);
$res = apply_filters('wpcom_init_order_options', $res, $order);
return wp_json_encode($res);
}
public function account_tabs($tabs){
$tabs[20] = array(
'slug' => 'orders-list',
'title' => '我的订单',
'icon' => 'order-circle'
);
$tabs[99991] = array(
'slug' => 'order',
'title' => '我的订单',
'icon' => 'order-circle',
'parent' => 'orders-list'
);
add_action('wpcom_account_tabs_orders-list', array($this, 'account_tabs_orders'));
add_action('wpcom_account_tabs_order', array($this, 'account_tabs_order'));
return $tabs;
}
public function account_tabs_orders(){
global $wpcom_member;
$user_id = get_current_user_id();
if($user_id){
$per_page = 10;
$paged = get_query_var('pageid') ? get_query_var('pageid') : 1;
$orders = $this->get_orders(array('user' => $user_id, 'per_page' => $per_page, 'paged' => $paged, 'orderby' => 'time'));
$all = $this->get_orders(array('user' => $user_id));
$total = $all && is_array($all) ? count($all) : 0;
$pages = ceil($total / $per_page);
}
$args = array(
'orders' => $orders,
'pages' => $pages,
'paged' => $paged
);
echo $wpcom_member->load_template('orders', $args);
}
public function account_tabs_order(){
global $wp_query, $wpcom_member;
$order_id = $wp_query->get('pageid');
if($order_id) {
$order = self::get_order($order_id);
$user_id = get_current_user_id();
if($order && $order->user != $user_id){ // 检查订单是否为当前用户的
$order = null;
}else{
$items = self::get_order_items($order_id);
}
}
$args = array(
'order' => $order ?? null,
'items' => $items ?? null
);
echo $wpcom_member->load_template('order', $args);
}
public function order_metas($metas) {
$metas['orders'] = array(
array(
'n' => 'number',
'l' => '订单号',
'd' => '留空则会自动生成,无特殊情况不建议修改'
),
array(
'n' => 'user',
'l' => '用户ID',
'd' => '购买用户ID,无特殊情况不建议修改'
),
array(
'n' => 'items',
'l' => '项目',
't' => 'rp',
'o' => array(
array(
'n' => 'item_id',
'l' => 'ID',
'hidden' => 1
),
array(
'n' => 'item_title',
'l' => '标题'
),
array(
'n' => 'item_price',
'l' => '价格',
'd' => '此处价格仅供参考,实际支付价格以<b>合计</b>选项价格为准'
),
array(
'n' => 'item_type',
'l' => '类型',
'd' => '用于和具体业务关联,不建议修改,如果无关联业务可为空,如果需要新建订单不会填写的话建议创建一个同类订单然后编辑参考对应选项'
),
array(
'n' => 'item_type_id',
'l' => '类型ID',
'd' => '用于和具体业务关联,不建议修改,如果无关联业务可为空,如果需要新建订单不会填写的话建议创建一个同类订单然后编辑参考对应选项'
),
array(
'n' => 'item_data',
't' => 'ta',
'rows' => 2,
'l' => '其他',
'd' => '部分订单类型会用到,不建议修改,如果需要新建订单不会填写的话建议创建一个同类订单然后编辑参考对应选项'
),
)
),
array(
'n' => 'price',
'l' => '合计',
'd' => '订单实际付款费用,可修改'
),
array(
'n' => 'status',
'l' => '状态',
't' => 's',
'o' => array(
'' => '请选择',
'unpaid' => '未付款',
'paid' => '已付款',
'refund' => '已退款',
'cancel' => '已取消'
)
)
);
return $metas;
}
public function setup_payment() {
$res = array('nonce' => wp_create_nonce('wpcom_payment_form'));
$res['gateways'] = apply_filters('wpcom_payment_gateways', []);
$res['help_url'] = wpcom_mp_options('help_url');
if(isset($_POST['order_type']) && $_POST['order_type'] === 'wallet'){
unset($res['gateways']['wallet']);
}else if($res['gateways'] && isset($res['gateways']['wallet'])) {
$res['gateways']['wallet']->balance = $res['gateways']['wallet']->get_balance();
}
if(array_key_exists('points', $res['gateways'])) unset($res['gateways']['points']); // 订单支付方式排除积分
wp_send_json($res);
}
public function setup_order($return = false) {
global $wpdb;
$res = array('result' => 0);
$nonce = isset($_POST['nonce']) ? sanitize_text_field($_POST['nonce']) : '';
$items = isset($_POST['items']) ? map_deep( stripslashes_deep($_POST['items']), 'sanitize_text_field' ) : [];
$total = isset($_POST['total']) ? sanitize_text_field($_POST['total']) : 0;
$order_id = isset($_POST['order_id']) ? sanitize_text_field($_POST['order_id']) : 0;
$gateway = isset($_POST['gateway']) ? sanitize_text_field($_POST['gateway']) : '';
$points = isset($_POST['points']) && $_POST['points'] == 1 ? 1 : 0;
$gateway = $points ? 'points' : $gateway;
$gateways = apply_filters('wpcom_payment_gateways', []);
// 保留data参数的HTML代码,系统sanitize_text_field会过滤
if($items && !empty($items)){
foreach($items as $i => $_item){
if(isset($_item['data']) && $_item['data'] && isset($_POST['items'][$i]) && isset($_POST['items'][$i]['data'])){
$data = stripslashes_deep($_POST['items'][$i]['data']);
if(is_array($data) && !empty($data)){
foreach($data as $k => $v){
$data[$k] = wp_kses_post($v);
}
}else{
$data = wp_kses_post($data);
}
$items[$i]['data'] = $data;
}
}
}
if ($points == 0 && (!$gateway || !isset($gateways[$gateway]) || $gateway === 'points')) {
$res['result'] = -5;
$res['msg'] = '支付网关错误,请检查选择的付款接口';
if($return) return $res;
wp_send_json($res);
}
if (!wp_verify_nonce($nonce, 'wpcom_payment_form')) {
$res['result'] = -6;
$res['msg'] = '支付请求校验失败,请稍后再试';
if($return) return $res;
wp_send_json($res);
}else if(!self::check_sign($_POST)){
$res['result'] = -7;
$res['msg'] = '订单信息校验失败,请检查订单信息';
if($return) return $res;
wp_send_json($res);
}
// 支付已有订单
if($order_id && $order = self::get_order($order_id)){
if($order->user && get_current_user_id() == $order->user){
$update = $points ? [] : array('payment_gateway' => $gateway); // 积分支付不更新支付方式,只能还是积分支付
if($order->price != $total && $total == self::get_total_price($items, $points)){// 价格不一样,数量变化
$update['price'] = $total;
$_items = $this->get_order_items($order_id);
if (is_array($_items) && !empty($_items)) {
foreach ($_items as $_item) {
foreach($items as $_item2){
if($_item2 && $_item2['ID'] == $_item->ID && isset($_item2['data'])){
$item_update = [
'data' => wp_json_encode($_item2['data'], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
];
$wpdb->update($wpdb->order_item_table, $item_update, array('ID' => $_item->ID));
break;
}
}
}
}
}else if($order->price != $total){ //价格不一样,重新计算也不一样
$res['result'] = -7;
$res['msg'] = '订单校验失败,请重试';
if($return) return $res;
wp_send_json($res);
}
// 检查库存
$items2 = $this->get_order_items($order_id);
$stock_check = true;
foreach($items2 as $item){
if(! apply_filters('wpcom_' . $item->type . '_stock_check', true, (array)$item)){
$stock_check = false;
break;
}
}
if(!$stock_check){
$res['result'] = -10;
$res['msg'] = '库存不足,无法购买';
}else{
$res = $wpdb->update($wpdb->order_table, $update, array('ID' => $order->ID));
$res = $points ? $this->process_points_payment($order_id) : $this->process_payment($order_id, $gateway);
if($return) $res['order_id'] = $order_id;
$res = apply_filters('wpcom_setup_exists_order', $res, $order, $items);
}
}else{
$res['result'] = -7;
$res['msg'] = '订单校验失败,请重试';
}
if($return) return $res;
wp_send_json($res);
}
$_total = 0;
$stock_check = true;
foreach ($items as $item) {
$price = number_format($item['price'], $points ? 0 : 2, '.', '');
if(is_numeric($item['price']) && is_numeric($price)){
$_total += (isset($item['number']) && $item['number'] ? $item['number'] : 1) * $price;
}else{
$price = new \WP_Error('item_price_error', '商品价格获取错误');
break;
}
if(!apply_filters('wpcom_' . $item['type'] . '_stock_check', true, $item)){
$stock_check = false;
break;
}
}
if(!$stock_check){
$res['result'] = -10;
$res['msg'] = '库存不足,无法购买';
}else if (is_wp_error($price)) {
$res['result'] = -2;
$res['msg'] = $price->get_error_message();
} else if ((float)$_total !== (float)$total) {
$res['result'] = -3;
$res['msg'] = '订单价格校验失败,请重试';
} else if ($_total === 0 || $_total === 0.00 || $total === '0' || $total === '0.00') {
$res['result'] = -5;
$res['msg'] = '当前商品价格暂不支持下单';
} else {
/**
* 订单价格,特殊业务场景需要对价格进行调整时备用
* @param number $total 合计价格
* @param array $items 购买项目
*
* @return number 返回价格
*/
$total = apply_filters('wpcom_order_price', $total, $items);
if (is_numeric($total)) {
// 创建订单
$order_id = $this->insert_order($total, $items, $gateway);
if ($order_id) { // 订单创建成功,调用支付接口支付
$res = $points ? $this->process_points_payment($order_id) : $this->process_payment($order_id, $gateway);
if($return) $res['order_id'] = $order_id;
} else {
$res['result'] = -1;
$res['msg'] = '创建订单失败,请稍后再试';
}
} else {
$res['result'] = -4;
$res['msg'] = '价格类型错误,请重试';
}
}
if($return) return $res;
wp_send_json($res);
}
function update_order_qty(){
$res = array('result' => 0);
$nonce = isset($_POST['nonce']) ? sanitize_text_field($_POST['nonce']) : '';
$items = isset($_POST['items']) ? map_deep( stripslashes_deep($_POST['items']), 'sanitize_text_field' ) : [];
$order_id = isset($_POST['order_id']) ? sanitize_text_field($_POST['order_id']) : 0;
$points = isset($_POST['points']) && $_POST['points'] == 1 ? 1 : 0;
// 保留data参数的HTML代码,系统sanitize_text_field会过滤
if($items && !empty($items)){
foreach($items as $i => $_item){
if(isset($_item['data']) && $_item['data'] && isset($_POST['items'][$i]) && isset($_POST['items'][$i]['data'])){
$data = stripslashes_deep($_POST['items'][$i]['data']);
if(is_array($data) && !empty($data)){
foreach($data as $k => $v){
$data[$k] = wp_kses_post($v);
}
}else{
$data = wp_kses_post($data);
}
$items[$i]['data'] = $data;
}
if(isset($_item['update_number'])){
unset($_POST['items'][$i]['update_number']);
}
}
}
if (!wp_verify_nonce($nonce, 'wpcom_payment_form')) {
$res['result'] = -6;
$res['msg'] = '当前请求校验失败,请稍后再试';
wp_send_json($res);
}else if(!self::check_sign($_POST)){
$res['result'] = -7;
$res['msg'] = '订单信息校验失败,请检查订单信息';
wp_send_json($res);
}else if($items){
foreach($items as $i => $item){
if(isset($item['update_number']) && $item['update_number']){
$items[$i]['number'] = $item['update_number'];
unset($items[$i]['update_number']);
if(isset($item['data']) && is_array($item['data'])){
$items[$i]['data']['quantity'] = $item['update_number'];
}else{
$items[$i]['data'] = ['quantity' => $item['update_number']];
}
$stock_check = apply_filters('wpcom_' . $item['type'] . '_stock_check', true, $item);
if(!$stock_check){
$res['result'] = -10;
$res['msg'] = '库存不足,无法购买';
wp_send_json($res);
}
}
}
$res['items'] = $items;
$res['total'] = self::get_total_price($items, $points);
$res['points'] = $points;
if($order_id) $res['order_id'] = $order_id;
$res = self::setup_order_data($res);
}
wp_send_json($res);
}
public function get_order_payment(){
$res = array('result' => 0);
$user = wp_get_current_user();
if (isset($user->ID) && $user->ID) {
$order_id = isset($_POST['order_id']) ? sanitize_text_field($_POST['order_id']) : 0;
if ($order_id && $order = self::get_order($order_id)) {
if($order->user != $user->ID){
$res['result'] = -2;
}else{
$points = $order->payment_gateway === 'points' ? 1 : 0;
$items = Order::get_order_items($order_id);
foreach($items as $i => $item){
$items[$i] = (array)$item;
if(isset($item->type) && $item->type){
$set_number = apply_filters('wpcom_order_'.$item->type.'_set_number', false);
if($set_number) {
$items[$i]['set_number'] = 1;
$data = $item->data ? json_decode($item->data, true) : '';
if($data && isset($data['quantity']) && $data['quantity']){
$items[$i]['number'] = $data['quantity'];
}
$items[$i]['price'] = number_format($items[$i]['price'], $points ? 0 : 2, '.', '');
$items[$i]['data'] = $data;
}
}
}
$res['total'] = number_format($order->price, $points ? 0 : 2, '.', '');
$res['items'] = $items;
$res['order_id'] = $order->ID;
if($points) $res['points'] = 1;
$res = Order::setup_order_data($res);
}
}
}else{
$res['result'] = -1;
}
wp_send_json($res);
}
public static function get_total_price($items, $points = 0){
$total = 0;
if(is_array($items) && !empty($items)){
foreach($items as $item){
$total += (isset($item['number']) && $item['number'] ? (int)$item['number'] : 1) * number_format($item['price'], $points ? 0 : 2, '.', '');
}
}
return number_format($total, $points ? 0 : 2, '.', '');
}
function insert_order($total, $items, $gateway = '') {
global $wpdb;
// 检查是否已经有同样的订单,避免重复创建订单
$order = $this->order_exists($total, $items);
$user_id = get_current_user_id();
if ($order) {
$_order = array(
'number' => $this->init_order_number($order->number),
'user' => $user_id,
'time' => current_time('mysql'),
'status' => 'unpaid',
'payment_gateway' => $gateway
);
$res = $wpdb->update($wpdb->order_table, $_order, array('ID' => $order->ID));
if ($res) return $order->ID;
} else {
$order = array(
'number' => $this->init_order_number(),
'price' => $total,
'user' => $user_id,
'time' => current_time('mysql'),
'status' => 'unpaid',
'payment_gateway' => $gateway
);
$format = array('%s', '%f', '%d', '%s', '%s', '%s');
$wpdb->insert($wpdb->order_table, $order, $format);
$order_id = $wpdb->insert_id;
$order_inserted = true;
// 订单创建成功,然后添加订单购买项目数据
if ($order_id) {
if(!$user_id && isset($GLOBALS['wpmx_options']['anonymous_order']) && $GLOBALS['wpmx_options']['anonymous_order'] == '1'){
$this->setup_anonymous_order($order_id);
}
foreach ($items as $item) {
$data = isset($item['data']) && $item['data'] ? $item['data'] : '';
$data = wp_json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$_item = array(
'order_id' => $order_id,
'price' => number_format($item['price'], 2, '.', ''),
'title' => isset($item['title']) ? $item['title'] : '',
'type' => isset($item['type']) ? $item['type'] : '',
'type_id' => isset($item['type_id']) ? $item['type_id'] : '',
'data' => $data
);
$format = array('%d', '%f', '%s', '%s', '%s', '%s');
$wpdb->insert($wpdb->order_item_table, $_item, $format);
$item_id = $wpdb->insert_id;
if (!$item_id) {
$order_inserted = false;
break;
}
}
} else {
$order_inserted = false;
}
do_action('wpcom_after_insert_order', $order_id, $order_inserted);
if ($order_inserted) return $order_id;
}
}
public static function delete_order($id){
global $wpdb;
if($id && is_numeric($id)){
$order = self::get_order($id);
if($wpdb->delete($wpdb->order_table, array('ID' => $id))){
do_action('wpcom_order_deleted', $id, $order);
$wpdb->delete($wpdb->order_item_table, array('order_id' => $id));
}
}
}
public static function update_order_number($order_id){
$order = self::get_order( $order_id );
if($order && $order->status && $order->status !== 'paid'){
global $wpdb;
$number = self::init_order_number();
return $wpdb->update($wpdb->order_table, array('number' => $number), array('ID' => $order_id));
}
}
private static function init_order_number($number='') {
if($number && preg_match('/^'.wp_date('Ymd', current_time('timestamp', 1)).'/', $number)) return $number;
$num = wp_date('YmdHis', current_time('timestamp', 1));
$microtime = explode('.', microtime(true));
$num = $num . $microtime[1];
$num = $num . rand(0, 9); // 添加1个随机数字
// 检查重复
$order = self::get_order_by_number($num);
if ($order) return self::init_order_number();
return $num;
}
static function get_order_by_number($num) {
global $wpdb;
$order_table = $wpdb->order_table;
$order = $wpdb->get_row($wpdb->prepare("SELECT * FROM `$order_table` WHERE number = %s", $num));
if ($order && $order->ID) return $order;
}
/**
* 执行付款操作,调用支付接口
*/
function process_payment($order_id, $gateway) {
$res = array('result' => 0);
$gateways = apply_filters('wpcom_payment_gateways', []);
if ($gateway && $gateway !== 'points' && isset($gateways[$gateway]) && $gateways[$gateway]->id) {
$payment = $gateways[$gateway]->process_payment($order_id);
if ($payment['result'] === 'success' && $payment['redirect']) {
$res['redirect'] = $payment['redirect'];
if (isset($payment['height'])) $res['height'] = $payment['height'];
if (isset($payment['modal-size'])) $res['modal-size'] = $payment['modal-size'];
$res['color'] = $gateways[$gateway]->color;
// 新窗口打开,需要补充弹框提示按钮链接
if (!$gateways[$gateway]->modal) {
$res['return_url'] = isset($payment['return_url']) ? $payment['return_url'] : self::get_checkout_return_url($order_id, $gateway);
$res['help_url'] = wpcom_mp_options('help_url');
}
} else {
if(isset($payment['msg'])) $res['msg'] = $payment['msg'];
$res['result'] = -5;
}
} else {
$res['result'] = -5;
}
if ($res['result'] !== 0 && !isset($res['msg'])) {
$res['msg'] = '支付网关错误,请检查选择的付款接口';
}
return $res;
}
/**
* 执行积分付款操作,调用积分支付
*/
function process_points_payment($order_id){
$res = array('result' => 0);
$gateways = apply_filters('wpcom_payment_gateways', []);
if(isset($gateways['points'])){
$res += $gateways['points']->process_payment($order_id);
$res['color'] = $gateways['points']->color;
}else {
$res['result'] = -5;
}
if ($res['result'] !== 0 && !isset($res['msg'])) {
$res['msg'] = '支付网关错误,请检查选择的付款接口';
}
return $res;
}
public static function get_orders($args) {
global $wpdb;
$order_table = $wpdb->order_table;
$where = '';
if($args && isset($args['user'])) $where .= ($where ? ' AND' : ' WHERE') . $wpdb->prepare(" `user` = %s", $args['user']);
if($args && isset($args['status'])) $where .= ($where ? ' AND' : ' WHERE') . $wpdb->prepare(" `status` = %s", $args['status']);
if($args && isset($args['time'])) $where .= ($where ? ' AND' : ' WHERE') . " `time` " . $args['time'];
$per_page = $args && isset($args['per_page']) ? (int)$args['per_page'] : 0;
$limit = '';
if($per_page && $per_page > 0){
$paged = $args && isset($args['paged']) ? $args['paged'] : 1;
$limit = $wpdb->prepare(" LIMIT %d, %d", ($paged-1) * $per_page, $per_page);
}
$_orderby = $args && isset($args['orderby']) ? esc_sql($args['orderby']) : '';
$orderby = '';
if($_orderby !== ''){
$order = $args && isset($args['order']) ? esc_sql($args['order']) : ' DESC';
$orderby = " ORDER BY $_orderby $order";
}
$orders = $wpdb->get_results("SELECT * FROM $order_table" . $where . $orderby . $limit);
if($orders && !is_wp_error($orders)) return $orders;
}
public static function get_order($id, $by = 'ID') {
global $wpdb;
$order_table = $wpdb->order_table;
$by = esc_sql($by);
$order = $wpdb->get_row($wpdb->prepare("SELECT * FROM `$order_table` WHERE `$by` = %s", $id));
if ($order) return $order;
}
public static function get_order_items($id) {
global $wpdb;
if (!$id) return false;
$items_table = $wpdb->order_item_table;
$items = $wpdb->get_results($wpdb->prepare("SELECT * FROM `$items_table` WHERE order_id = %s", $id));
if (is_array($items) && count($items) > 0) return $items;
}
public static function get_item_sales($type, $type_id){
global $wpdb;
if($type && $type_id){
$item_table = $wpdb->order_item_table;
$order_table = $wpdb->order_table;
$count = $wpdb->get_var($wpdb->prepare("SELECT count(O.ID) FROM (SELECT order_id FROM `$item_table` WHERE `type` = %s AND `type_id` = %s) I LEFT JOIN `$order_table` O ON I.order_id=O.id WHERE O.status='paid'", $type, $type_id));
if ($count) return $count;
}
return 0;
}
public static function is_item_paid($type, $type_id, $user_id=''){
global $wpdb;
$res = false;
if($type && $type_id){
$user_id = $user_id ?: get_current_user_id();
$item_table = $wpdb->order_item_table;
$order_table = $wpdb->order_table;
if($user_id){
$res = $wpdb->get_var($wpdb->prepare("SELECT O.ID FROM $item_table I RIGHT JOIN $order_table O ON I.order_id=O.ID WHERE O.user = %s AND I.type = %s AND I.type_id = %s AND O.status='paid'", $user_id, $type, $type_id));
if($res) return $res;
}else{ // 匿名订单
$orders = maybe_unserialize(Session::get('anonymous_orders'));
$orders = $orders ?: [];
if(is_array($orders) && !empty($orders)){
$orders = array_map('esc_sql', $orders);
$orders = array_filter($orders); // 过滤空值
$_orders = "'" . implode("','", $orders) . "'";
$res = $wpdb->get_var($wpdb->prepare("SELECT O.ID FROM $item_table I RIGHT JOIN $order_table O ON I.order_id=O.ID WHERE O.ID in ($_orders) AND I.type = %s AND I.type_id = %s AND O.status='paid'", $type, $type_id));
if($res) return $res;
}
}
}
return apply_filters('wpcom_is_item_paid', $res, $type, $type_id, $user_id);
}
public static function is_admin_free($post_id = 0){
global $wpmx_options;
if($wpmx_options && isset($wpmx_options['admin_free']) && $wpmx_options['admin_free'] == 1){
if(current_user_can('manage_options')){
return true;
}else if($post_id && $user_id = get_current_user_id()){
$author = get_post_field('post_author', $post_id);
return $user_id == $author;
}
}
return false;
}
public static function order_url($order_id){
$permalink_structure = get_option('permalink_structure');
$page = wpcom_subpage_url('order');
if ($permalink_structure) { // 默认链接规则
$url = trailingslashit($page) . $order_id;
}else{
$url = add_query_arg('pageid', $order_id, $page);
}
return $url;
}
public static function get_order_status_display($status){
$_status = array(
'paid' => '已付款',
'unpaid' => '未付款',
'refund' => '已退款',
'cancel' => '已取消'
);
if(isset($_status[$status])) return $_status[$status];
}
public static function payment_complete($order_id, $gateway){
global $wpdb;
$order_table = $wpdb->order_table;
$res = $wpdb->update($order_table, array('status' => 'paid', 'time' => current_time('mysql'), 'payment_gateway' => $gateway), array('ID' => $order_id));
if($res) {
$order = self::get_order($order_id);
// 订单支付完成后的常规操作,比如通知消息
do_action('wpcom_order_payment_complete', $order);
// 订单完成后对于具体某个产品类型的操作
$items = self::get_order_items($order_id);
if($items){
foreach($items as $item){
do_action("wpcom_{$item->type}_payment_complete", $item, $order);
}
}
}
return $res;
}
/**
* 订单重复检查,只检查24小时内的重复订单,不翻旧账,避免检查的数据过大影响性能
*/
private function order_exists($total, $items) {
global $wpdb;
if ($total && is_array($items) && count($items) > 0) {
$user = get_current_user_id();
$time = wp_date('Y-m-d H:i:s', current_time('timestamp', 1) - 24 * 60 * 60);
if($user){
$results = $wpdb->get_results($wpdb->prepare("SELECT * FROM `$wpdb->order_table` WHERE status = 'unpaid' AND user = %s AND price = %s AND time > %s", $user, $total, $time));
}else{ // 匿名用户
$orders = maybe_unserialize(Session::get('anonymous_orders'));
$orders = $orders ?: [];
if(is_array($orders) && !empty($orders)){
$orders = array_map('esc_sql', $orders);
$orders = array_filter($orders); // 过滤空值
$_orders = "'" . implode("','", $orders) . "'";
$results = $wpdb->get_results($wpdb->prepare("SELECT * FROM `$wpdb->order_table` WHERE status = 'unpaid' AND ID in ($_orders) AND price = %s AND time > %s", $total, $time));
}else{
return false;
}
}
if (is_array($results) && count($results) > 0) {
$_order = null;
foreach ($results as $order) {
$order_items = self::get_order_items($order->ID);
$check = false;
if ($order_items && count($order_items) === count($items)) {
$check = true;
foreach ($order_items as $item) {
$found = false;
foreach ($items as $i => $item2) {
if (
isset($item2['price']) && $item2['title'] === $item->title && $item2['price'] == $item->price &&
$item2['type'] === $item->type && $item2['type_id'] === $item->type_id
) {
$data = isset($item2['data']) && $item2['data'] ? $item2['data'] : '';
if ($data) $data = wp_json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
if ($data === $item->data) {
$found = true;
unset($items[$i]);
break;
}
}
}
if (!$found) {
$check = false;
break;
}
}
}
if ($check) {
$order->items = $order_items;
$_order = $order;
}
}
return $_order;
}
}
}
public static function get_checkout_payment_url($order_id) {
return $order_id ? admin_url('admin-ajax.php?action=wpcom_checkout_payment&order_id=' . $order_id) : false;
}
public static function get_checkout_return_url($order_id, $api = '') {
return $order_id ? admin_url('admin-ajax.php?action=wpcom_checkout_return&order_id=' . $order_id . ($api ? '&api='.$api : '')) : false;
}
public static function get_checkout_notify_url($order_id, $api) {
return get_rest_url(null, 'wpcom/v1/payment-notify/'.$api.'/'.$order_id);
}
public function send_order_notify($order){
// 通知用户
if($order && $order->user && $order->status === 'paid'){
$title = '订单支付成功';
$content = '亲爱的用户:你好!<br>';
$content .= '你的订单「<b>'.$order->number.'</b>」已完成支付!详情<a href="' . esc_url(self::order_url($order->ID)) . '" target="_blank">点击此处查看订单</a>。';
do_action('wpcom_add_notification', $order->user, $title, $content);
if(wpcom_mp_options('order_notify_user')){
$user = get_user_by('ID', $order->user);
if($user->user_email && !wpcom_is_empty_mail($user->user_email)){
$headers = array('Content-Type: text/html; charset=UTF-8');
if ( is_multisite() ) {
$site_name = get_network()->site_name;
} else {
$site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
}
wp_mail( $user->user_email, wp_specialchars_decode( '【' . $site_name . '】' . $title ), $content, $headers );
}
}
}
// 通知管理员
if($order && $order->status === 'paid' && wpcom_mp_options('order_notify_admin') && $admin_email = get_bloginfo ('admin_email')){
$headers = array('Content-Type: text/html; charset=UTF-8');
if ( is_multisite() ) {
$site_name = get_network()->site_name;
} else {
$site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
}
$title = '订单支付成功';
$content = '亲爱的管理员:你好!<br>';
$content .= '你的网站有新订单「<b>'.$order->number.'</b>」已完成支付!详情<a href="' . esc_url(admin_url('admin.php?page=wpcom-orders')) . '" target="_blank">点击此处查看订单</a>。';
wp_mail( $admin_email, wp_specialchars_decode( '【' . $site_name . '】' . $title ), $content, $headers );
}
}
public function setup_anonymous_order($order_id){
if($order_id){
$orders = maybe_unserialize(Session::get('anonymous_orders'));
$orders = $orders ?: [];
if(!in_array($order_id, $orders)) {
$orders[] = $order_id;
Session::set('anonymous_orders', maybe_serialize($orders), 2592000);
}
}
}
public function checkout_payment_page() {
$order_id = isset($_GET['order_id']) && $_GET['order_id'] ? sanitize_text_field($_GET['order_id']) : 0;
if(!$order_id && isset($_POST['total']) && isset($_POST['gateway']) && isset($_POST['nonce'])){
$res = $this->setup_order(1);
$gateways = apply_filters('wpcom_payment_gateways', []);
$api = sanitize_text_field($_POST['gateway']);
if($res && isset($res['order_id']) && $res['order_id'] && isset($_SERVER['HTTP_REFERER']) && $_SERVER['HTTP_REFERER']){
Session::set('_order_referer_' . $res['order_id'], urlencode($_SERVER['HTTP_REFERER']));
}
if ($api && (isset($gateways[$api]) || $api === 'points')) do_action("wpcom_payment_{$api}_submit_page", $res, map_deep( $_POST, 'sanitize_text_field' ));
wp_die();
}else{
$checked = true;
if (is_numeric($order_id)) {
$order = self::get_order($order_id);
if ($order && isset($order->ID)) {
$api = $order && $order->payment_gateway ? $order->payment_gateway : '';
$gateways = apply_filters('wpcom_payment_gateways', []);
if ($api && (isset($gateways[$api]) || $api === 'points')) do_action("wpcom_payment_{$api}_receipt_page", $order_id);
wp_die();
} else {
$checked = false;
}
} else {
$checked = false;
}
if ($checked === false) {
$msg = '参数错误,请传入正确的订单号';
if (function_exists('_default_wp_die_handler')) {
_default_wp_die_handler($msg);
} else {
wp_die($msg);
}
}
}
}
public function checkout_return_page() {
// 兼容易支付参数&转义的问题
if(isset($_GET['amp;api']) && ($_GET['amp;api'] === 'alipay-yzf' || $_GET['amp;api'] === 'wxpay-yzf')){
$_GET['api'] = $_GET['amp;api'];
$_GET['order_id'] = $_GET['amp;order_id'];
}
$order_id = isset($_GET['order_id']) && $_GET['order_id'] ? sanitize_text_field($_GET['order_id']) : 0;
if (is_numeric($order_id)) {
$order = self::get_order($order_id);
$user_id = get_current_user_id();
if ($order && isset($order->ID) && (!$order->user || ((int) $order->user === $user_id))) {
$api = isset($_GET['api']) && $_GET['api'] ? sanitize_text_field($_GET['api']) : ($order && $order->payment_gateway ? $order->payment_gateway : '');
$gateways = apply_filters('wpcom_payment_gateways', []);
if ($api && isset($gateways[$api])) do_action("wpcom_payment_{$api}_return_page", $order_id);
wp_die();
}
}
wp_die('fail');
}
public static function setup_order_data($data){
if($data && is_array($data)){
$data['timestamp'] = current_time('timestamp', 1);
$data['user_id'] = get_current_user_id();
$data['sign'] = self::make_sign($data);
}
return $data;
}
function localize_script($script){
if(isset($GLOBALS['wpmx_options']['anonymous_order']) && $GLOBALS['wpmx_options']['anonymous_order'] == '1'){
$script['anonymous_order'] = true;
}
return $script;
}
public static function make_sign($data){
if($data){
$key = wp_salt('secure_auth');
ksort($data);
$string = '';
foreach ($data as $k => $v){
if($k === 'key' || is_null($v) || $v === ''){continue;}
$v = self::loop_to_string($v);
$string .= $k . "=" . $v . "&";
}
$string = trim($string, "&");
//签名步骤二:在string后加入KEY
$string = $string . '&key=' . $key;
//签名步骤三:MD5加密或者HMAC-SHA256
$result = hash_hmac('sha256', $string, $key);
//签名步骤四:所有字符转为小写
$result = strtolower($result);
return $result;
}
}
public static function check_sign($data){
if($data && isset($data['sign']) && $data['sign']){
if(current_time('timestamp', 1) - $data['timestamp'] > 24*60*60) return false; // 24小时超时
$sign = $data['sign'];
$key = wp_salt('secure_auth');
$_data = array(
'result' => 0,
'items' => stripslashes_deep($data['items']),
'timestamp' => $data['timestamp'],
'user_id' => get_current_user_id(),
'total' => $data['total'],
);
if(isset($data['order_id']) && $data['order_id']) $_data['order_id'] = $data['order_id'];
if(isset($data['points'])) $_data['points'] = $data['points'];
ksort($_data);
$string = '';
foreach ($_data as $k => $v){
if($k === 'key' || is_null($v) || $v === ''){continue;}
$v = self::loop_to_string($v);
$string .= $k . "=" . $v . "&";
}
$string = trim($string, "&");
$string = $string . '&key=' . $key;
$result = hash_hmac('sha256', $string, $key);
return $sign === strtolower($result);
}
return false;
}
private static function loop_to_string($data){
if($data && (is_array($data) || is_object($data))){
$_data = [];
foreach($data as $i => $v){
if(is_array($v) || is_object($v)){
$_data[$i] = self::loop_to_string($v);
}else if(is_numeric($v)){
$_data[$i] = (string)$v;
}else{
$_data[$i] = $v;
}
}
$data = wp_json_encode($_data);
}else if(is_numeric($data)){
$data = (string)$data;
}
return $data;
}
/**
* 统计订单数量
* @param string $period 支持 'today', 'month', 'year', 'total'
* @param string $start 自定义开始时间(Y-m-d 或 Y-m-d H:i:s)
* @param string $end 自定义结束时间(Y-m-d 或 Y-m-d H:i:s)
* @return int
*/
public static function get_order_count($period = 'total', $start = '', $end = '') {
global $wpdb;
$cache_key = 'wpcom_order_count_' . substr(md5(serialize([$period, $start, $end])), 0, 12);
$cached = get_transient($cache_key);
if ($cached !== false) {
return $cached;
}
$order_table = $wpdb->order_table;
$where = ["status = 'paid'", "payment_gateway != 'points'"];
if ($start && $end) {
$where[] = $wpdb->prepare('time >= %s AND time <= %s', $start . (strlen($start) == 10 ? ' 00:00:00' : ''), $end . (strlen($end) == 10 ? ' 23:59:59' : ''));
} elseif ($start) {
$where[] = $wpdb->prepare('time >= %s', $start . (strlen($start) == 10 ? ' 00:00:00' : ''));
} elseif ($end) {
$where[] = $wpdb->prepare('time <= %s', $end . (strlen($end) == 10 ? ' 23:59:59' : ''));
} else {
switch ($period) {
case 'today':
$where[] = $wpdb->prepare('DATE(time) = %s', current_time('Y-m-d'));
break;
case 'month':
$where[] = $wpdb->prepare('YEAR(time) = %d AND MONTH(time) = %d', date_i18n('Y'), date_i18n('n'));
break;
case 'year':
$where[] = $wpdb->prepare('YEAR(time) = %d', date_i18n('Y'));
break;
case 'total':
default:
// 只统计全部已支付订单
break;
}
}
$sql = "SELECT COUNT(*) FROM $order_table WHERE " . implode(' AND ', $where);
$count = (int)$wpdb->get_var($sql);
// 缓存 10 分钟
set_transient($cache_key, $count, MINUTE_IN_SECONDS * 10);
return $count;
}
/**
* 统计订单收入
* @param string $period 支持 'today', 'month', 'year', 'total'
* @param string $start 自定义开始时间(Y-m-d 或 Y-m-d H:i:s)
* @param string $end 自定义结束时间(Y-m-d 或 Y-m-d H:i:s)
* @return float
*/
public static function get_order_income($period = 'total', $start = '', $end = '') {
global $wpdb;
$cache_key = 'wpcom_order_income_' . substr(md5(serialize([$period, $start, $end])), 0, 12);
$cached = get_transient($cache_key);
if ($cached !== false) {
return $cached;
}
$order_table = $wpdb->order_table;
$where = ["status = 'paid'", "payment_gateway != 'points'", "payment_gateway != 'wallet'"];
if ($start && $end) {
$where[] = $wpdb->prepare('time >= %s AND time <= %s', $start . (strlen($start) == 10 ? ' 00:00:00' : ''), $end . (strlen($end) == 10 ? ' 23:59:59' : ''));
} elseif ($start) {
$where[] = $wpdb->prepare('time >= %s', $start . (strlen($start) == 10 ? ' 00:00:00' : ''));
} elseif ($end) {
$where[] = $wpdb->prepare('time <= %s', $end . (strlen($end) == 10 ? ' 23:59:59' : ''));
} else {
switch ($period) {
case 'today':
$where[] = $wpdb->prepare('DATE(time) = %s', current_time('Y-m-d'));
break;
case 'month':
$where[] = $wpdb->prepare('YEAR(time) = %d AND MONTH(time) = %d', date_i18n('Y'), date_i18n('n'));
break;
case 'year':
$where[] = $wpdb->prepare('YEAR(time) = %d', date_i18n('Y'));
break;
case 'total':
default:
// 只统计全部已支付订单
break;
}
}
$sql = "SELECT SUM(price) FROM $order_table WHERE " . implode(' AND ', $where);
$income = $wpdb->get_var($sql);
$income = number_format($income ? (float)$income : 0, 2, '.', '');
// 缓存 10 分钟
set_transient($cache_key, $income, MINUTE_IN_SECONDS * 10);
return $income;
}
private function init_database() {
global $wpdb;
$order_table = $wpdb->order_table;
$order_item_table = $wpdb->order_item_table;
if ($wpdb->get_var("SHOW TABLES LIKE '$order_table'") != $order_table) {
$charset_collate = $wpdb->get_charset_collate();
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
$create_order_table_sql = "CREATE TABLE $order_table (" .
"ID BIGINT(20) NOT NULL auto_increment," .
"number BIGINT(20) NOT NULL," .
"price decimal(10,2) NOT NULL," .
"user BIGINT(20) NOT NULL," .
"time datetime NOT NULL," .
"status varchar(20) NOT NULL," .
"payment_gateway varchar(20)," .
"PRIMARY KEY (ID)) $charset_collate;";
dbDelta($create_order_table_sql);
$create_item_table_sql = "CREATE TABLE $order_item_table (" .
"ID BIGINT(20) NOT NULL auto_increment," .
"order_id BIGINT(20) NOT NULL," .
"price decimal(10,2) NOT NULL," .
"title longtext," .
"type varchar(20)," .
"type_id varchar(60)," .
"data longtext," .
"PRIMARY KEY (ID)) $charset_collate;";
dbDelta($create_item_table_sql);
}
}
}
class_alias(Order::class, 'WPCOM_Order');
new Order();