feat(forum): multi-gateway cashier for pay-visibility topics
Replace balance-only AJAX topic purchase with AKSRT_Topic_Pay REST cashier (resource orders + Payment_Gateway), reuse cms-download modal, and unlock on aksrt_order_paid.
This commit is contained in:
1 parent
3f06675b8c
commit
cea22daae7
13 files changed
+768
-262
No files matched your search
@@ -408,121 +408,6 @@ trait AKSRT_Forum_Topic_Service_Part_3 {
|
||||
return $this->forum->success(__('解锁成功', 'aksrt'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cash/balance purchase for VISIBILITY_PAY topics.
|
||||
*
|
||||
* Reuses AKSRT_Order product_type=resource + balance credits conversion
|
||||
* (1 CNY = 100 credits, same as AKSRT_Balance_Gateway).
|
||||
*
|
||||
* @return array{success:bool,message:string}
|
||||
*/
|
||||
public function purchase_topic_with_balance(int $user_id, int $topic_id): array {
|
||||
$post = get_post($topic_id);
|
||||
if (!$post || $post->post_type !== 'aksrt_forum_topic') {
|
||||
return $this->forum->error(__('主题不存在', 'aksrt'));
|
||||
}
|
||||
|
||||
if ($user_id <= 0) {
|
||||
return $this->forum->error(__('请先登录', 'aksrt'));
|
||||
}
|
||||
|
||||
$visibility = (string) get_post_meta($topic_id, '_aksrt_visibility', true);
|
||||
if ($visibility !== AKSRT_Forum::VISIBILITY_PAY) {
|
||||
return $this->forum->error(__('该主题不是付费可见内容', 'aksrt'));
|
||||
}
|
||||
|
||||
if ($this->has_user_paid_topic($user_id, $topic_id)) {
|
||||
return $this->forum->success(__('已购买', 'aksrt'));
|
||||
}
|
||||
|
||||
$price = $this->resolve_topic_pay_price($topic_id, $user_id);
|
||||
if (!empty($price['is_free'])) {
|
||||
$this->mark_topic_unlocked($user_id, $topic_id);
|
||||
return $this->forum->success(__('VIP 免费解锁成功', 'aksrt'), [
|
||||
'amount' => 0,
|
||||
'free' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
$amount = (float) ($price['amount'] ?? 0);
|
||||
if ($amount <= 0) {
|
||||
return $this->forum->error(__('价格未设置', 'aksrt'));
|
||||
}
|
||||
|
||||
if (!class_exists('AKSRT_Order') || !class_exists('AKSRT_Credits')) {
|
||||
return $this->forum->error(__('支付系统不可用', 'aksrt'));
|
||||
}
|
||||
|
||||
$product_type = class_exists('AKSRT_Domain_Model')
|
||||
? AKSRT_Domain_Model::ORDER_PRODUCT_RESOURCE
|
||||
: 'resource';
|
||||
|
||||
$order_number = AKSRT_Order::get_instance()->create($user_id, $product_type, $topic_id, $amount, 'CNY');
|
||||
if ($order_number === '') {
|
||||
return $this->forum->error(__('创建订单失败', 'aksrt'));
|
||||
}
|
||||
|
||||
// Align with AKSRT_Balance_Gateway: 1 CNY = 100 credits.
|
||||
$credits_cost = (int) ceil($amount * 100);
|
||||
$balance = AKSRT_Credits::get_balance($user_id);
|
||||
if ($balance < $credits_cost) {
|
||||
AKSRT_Order::get_instance()->update_status($order_number, AKSRT_Order::STATUS_CANCELLED, [
|
||||
'note' => __('余额不足,自动取消', 'aksrt'),
|
||||
]);
|
||||
return $this->forum->error(sprintf(
|
||||
__('余额不足,需要 %d 积分(约 ¥%s),当前余额 %d', 'aksrt'),
|
||||
$credits_cost,
|
||||
number_format($amount, 2, '.', ''),
|
||||
$balance
|
||||
));
|
||||
}
|
||||
|
||||
$deducted = AKSRT_Credits::deduct(
|
||||
$user_id,
|
||||
$credits_cost,
|
||||
'forum_topic_pay',
|
||||
[
|
||||
'topic_id' => $topic_id,
|
||||
'order' => $order_number,
|
||||
],
|
||||
sprintf(__('购买帖子「%s」', 'aksrt'), get_the_title($topic_id))
|
||||
);
|
||||
|
||||
if (is_wp_error($deducted) || $deducted === false) {
|
||||
AKSRT_Order::get_instance()->update_status($order_number, AKSRT_Order::STATUS_CANCELLED, [
|
||||
'note' => __('扣款失败,自动取消', 'aksrt'),
|
||||
]);
|
||||
$msg = is_wp_error($deducted) ? $deducted->get_error_message() : __('扣款失败', 'aksrt');
|
||||
return $this->forum->error($msg ?: __('扣款失败', 'aksrt'));
|
||||
}
|
||||
|
||||
$paid = AKSRT_Order::get_instance()->update_status($order_number, AKSRT_Order::STATUS_PAID, [
|
||||
'payment_method' => 'balance',
|
||||
'payment_transaction_id' => 'BAL_' . $order_number,
|
||||
'note' => sprintf(__('论坛付费主题余额支付 %d 积分', 'aksrt'), $credits_cost),
|
||||
]);
|
||||
|
||||
if (!$paid) {
|
||||
// Best-effort refund so user is not charged without access.
|
||||
AKSRT_Credits::add(
|
||||
$user_id,
|
||||
$credits_cost,
|
||||
'forum_topic_pay_refund',
|
||||
['topic_id' => $topic_id, 'order' => $order_number],
|
||||
sprintf(__('购买失败退款:%s', 'aksrt'), get_the_title($topic_id))
|
||||
);
|
||||
return $this->forum->error(__('订单确认失败,已尝试退回积分', 'aksrt'));
|
||||
}
|
||||
|
||||
$this->mark_topic_unlocked($user_id, $topic_id);
|
||||
|
||||
return $this->forum->success(__('购买成功', 'aksrt'), [
|
||||
'order_number' => $order_number,
|
||||
'amount' => $amount,
|
||||
'credits_cost' => $credits_cost,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve cash price for a pay-visibility topic.
|
||||
*
|
||||
|
||||
@@ -263,25 +263,4 @@ trait AKSRT_Forum_Topic_Service_Part_4 {
|
||||
wp_send_json_error(['message' => $result['message'] ?? __('解锁失败', 'aksrt')]);
|
||||
}
|
||||
}
|
||||
|
||||
public function ajax_purchase_topic(): void {
|
||||
check_ajax_referer('aksrt_forum_action', '_nonce');
|
||||
|
||||
$user_id = get_current_user_id();
|
||||
if ($user_id <= 0) {
|
||||
wp_send_json_error(['message' => __('请先登录', 'aksrt')]);
|
||||
}
|
||||
|
||||
$topic_id = (int) ($_POST['topic_id'] ?? 0);
|
||||
if ($topic_id <= 0) {
|
||||
wp_send_json_error(['message' => __('主题不存在', 'aksrt')]);
|
||||
}
|
||||
|
||||
$result = $this->purchase_topic_with_balance($user_id, $topic_id);
|
||||
if (!empty($result['success'])) {
|
||||
wp_send_json_success($result);
|
||||
} else {
|
||||
wp_send_json_error(['message' => $result['message'] ?? __('购买失败', 'aksrt')]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user