feat(pay): points-to-VIP exchange and remaining-days upgrade pricing
Add pay_vip_points_exchange packages (level|points|days|title), REST redeem for non-VIP users, unit|daily upgrade product for residual VIP1 days, and UI.
This commit is contained in:
1 parent
ba6ae854f8
commit
9eaddce457
6 files changed
+504
-66
No files matched your search
@@ -541,3 +541,22 @@
|
|||||||
color: #9ca3af;
|
color: #9ca3af;
|
||||||
text-decoration: line-through;
|
text-decoration: line-through;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.uc-vip-shop--points {
|
||||||
|
border-color: rgba(16, 185, 129, 0.25);
|
||||||
|
}
|
||||||
|
.uc-vip-shop__balance {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #047857;
|
||||||
|
margin: 0 0 10px;
|
||||||
|
}
|
||||||
|
.uc-vip-shop__item.is-disabled {
|
||||||
|
opacity: 0.55;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.uc-vip-shop__item.is-disabled:hover {
|
||||||
|
transform: none;
|
||||||
|
box-shadow: none;
|
||||||
|
border-color: var(--color-line, #e5e7eb);
|
||||||
|
}
|
||||||
+45
-1
@@ -318,7 +318,49 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ===== VIP 开通 =====
|
|
||||||
|
// ===== VIP 积分兑换 =====
|
||||||
|
function initVipPointsExchange() {
|
||||||
|
var root = document.querySelector('[data-aksrt-vip-points]');
|
||||||
|
if (!root) return;
|
||||||
|
var tip = root.querySelector('[data-aksrt-vip-points-tip]');
|
||||||
|
var busy = false;
|
||||||
|
function setTip(text, isError) {
|
||||||
|
if (!tip) return;
|
||||||
|
tip.textContent = text || '';
|
||||||
|
tip.classList.toggle('is-error', !!isError);
|
||||||
|
}
|
||||||
|
root.addEventListener('click', function (e) {
|
||||||
|
var btn = e.target.closest('[data-aksrt-vip-points-buy]');
|
||||||
|
if (!btn || busy || btn.disabled) return;
|
||||||
|
e.preventDefault();
|
||||||
|
var packageId = parseInt(btn.getAttribute('data-package-id') || '0', 10);
|
||||||
|
if (!packageId) return;
|
||||||
|
busy = true;
|
||||||
|
btn.classList.add('is-loading');
|
||||||
|
setTip('正在兑换…');
|
||||||
|
api('/pay/vip/points-exchange', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ package_id: packageId })
|
||||||
|
}).then(function (r) {
|
||||||
|
busy = false;
|
||||||
|
btn.classList.remove('is-loading');
|
||||||
|
if (!r.ok || (r.json && r.json.code && !r.json.success)) {
|
||||||
|
setTip((r.json && r.json.message) || '兑换失败', true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var data = (r.json && r.json.data) ? r.json.data : r.json;
|
||||||
|
setTip((data && data.message) || '兑换成功,正在刷新…');
|
||||||
|
window.location.reload();
|
||||||
|
}).catch(function () {
|
||||||
|
busy = false;
|
||||||
|
btn.classList.remove('is-loading');
|
||||||
|
setTip('网络错误,请重试', true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== VIP 开通 =====
|
||||||
function initVipShop() {
|
function initVipShop() {
|
||||||
var root = document.querySelector('[data-aksrt-vip-shop]');
|
var root = document.querySelector('[data-aksrt-vip-shop]');
|
||||||
if (!root) return;
|
if (!root) return;
|
||||||
@@ -405,10 +447,12 @@
|
|||||||
initSignin();
|
initSignin();
|
||||||
initInvite();
|
initInvite();
|
||||||
initVipShop();
|
initVipShop();
|
||||||
|
initVipPointsExchange();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
initSignin();
|
initSignin();
|
||||||
initInvite();
|
initInvite();
|
||||||
initVipShop();
|
initVipShop();
|
||||||
|
initVipPointsExchange();
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
@@ -107,7 +107,12 @@ class AKSRT_Admin_Schema_Payment {
|
|||||||
['key' => 'pay_user_vip_desc', 'label' => '会员说明', 'type' => 'textarea', 'default' => ''],
|
['key' => 'pay_user_vip_desc', 'label' => '会员说明', 'type' => 'textarea', 'default' => ''],
|
||||||
]],
|
]],
|
||||||
['key' => 'packages', 'title' => '会员套餐', 'fields' => [
|
['key' => 'packages', 'title' => '会员套餐', 'fields' => [
|
||||||
['key' => 'payment_vip_packages', 'label' => '套餐列表', 'type' => 'textarea', 'default' => "30|30|月卡\n365|365|年卡"],
|
['key' => 'payment_vip_packages', 'label' => '套餐列表', 'type' => 'textarea', 'default' => "30|30|月卡\n365|365|年卡", 'help' => '每行:现金价|天数|标题(天数为 0 表示永久)'],
|
||||||
|
]],
|
||||||
|
['key' => 'points_exchange', 'title' => '积分兑换会员', 'fields' => [
|
||||||
|
['key' => 'pay_vip_points_exchange_s', 'label' => '启用积分兑换', 'type' => 'switch', 'default' => false],
|
||||||
|
['key' => 'payment_vip_points_packages', 'label' => '兑换套餐', 'type' => 'textarea', 'default' => "1|500|30|积分月卡\n1|5000|0|积分永久", 'dependsOn' => ['key' => 'pay_vip_points_exchange_s', 'value' => true], 'help' => '每行:等级|积分|天数|标题(天数 0=永久;仅未开通会员可兑)'],
|
||||||
|
['key' => 'pay_vip_points_exchange_desc', 'label' => '兑换说明', 'type' => 'textarea', 'default' => '', 'dependsOn' => ['key' => 'pay_vip_points_exchange_s', 'value' => true]],
|
||||||
]],
|
]],
|
||||||
['key' => 'benefit', 'title' => '下载权益', 'fields' => [
|
['key' => 'benefit', 'title' => '下载权益', 'fields' => [
|
||||||
['key' => 'pay_download_limit', 'label' => '普通用户下载限制', 'type' => 'number', 'default' => 0, 'min' => 0, 'max' => 9999],
|
['key' => 'pay_download_limit', 'label' => '普通用户下载限制', 'type' => 'number', 'default' => 0, 'min' => 0, 'max' => 9999],
|
||||||
@@ -133,7 +138,7 @@ class AKSRT_Admin_Schema_Payment {
|
|||||||
['key' => 'upgrade', 'title' => '升级设置', 'fields' => [
|
['key' => 'upgrade', 'title' => '升级设置', 'fields' => [
|
||||||
['key' => 'vip_upgrade', 'label' => '允许升级', 'type' => 'switch', 'default' => true],
|
['key' => 'vip_upgrade', 'label' => '允许升级', 'type' => 'switch', 'default' => true],
|
||||||
['key' => 'vip_upgrade_desc', 'label' => '升级说明', 'type' => 'textarea', 'default' => '', 'dependsOn' => ['key' => 'vip_upgrade', 'value' => true]],
|
['key' => 'vip_upgrade_desc', 'label' => '升级说明', 'type' => 'textarea', 'default' => '', 'dependsOn' => ['key' => 'vip_upgrade', 'value' => true]],
|
||||||
['key' => 'vip_upgrade_product', 'label' => '升级产品', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'vip_upgrade', 'value' => true]],
|
['key' => 'vip_upgrade_product', 'label' => '升级产品', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'vip_upgrade', 'value' => true], 'help' => '固定价:价格|天数|标题;按剩余天数:unit|每日单价|标题'],
|
||||||
]],
|
]],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ class AKSRT_Vip_Service {
|
|||||||
'permission_callback' => [$this, 'check_rest_nonce'],
|
'permission_callback' => [$this, 'check_rest_nonce'],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
register_rest_route('aksrt/v1', '/pay/vip/points-exchange', [
|
||||||
|
'methods' => 'POST',
|
||||||
|
'callback' => [$this, 'rest_points_exchange'],
|
||||||
|
'permission_callback' => [$this, 'check_rest_nonce'],
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -103,13 +109,34 @@ class AKSRT_Vip_Service {
|
|||||||
return new WP_REST_Response($result, 200);
|
return new WP_REST_Response($result, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /pay/vip/points-exchange
|
||||||
|
* body: { package_id: int } // points package id = 5000 + index
|
||||||
|
*/
|
||||||
|
public function rest_points_exchange(WP_REST_Request $request) {
|
||||||
|
$uid = get_current_user_id();
|
||||||
|
if ($uid <= 0) {
|
||||||
|
return new WP_Error('aksrt_need_login', __('请先登录后再兑换。', 'aksrt'), ['status' => 401]);
|
||||||
|
}
|
||||||
|
$params = $request->get_json_params();
|
||||||
|
if (!is_array($params)) {
|
||||||
|
$params = $request->get_params();
|
||||||
|
}
|
||||||
|
$package_id = (int) ($params['package_id'] ?? 0);
|
||||||
|
$result = $this->exchange_with_points($uid, $package_id);
|
||||||
|
if (is_wp_error($result)) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
return new WP_REST_Response($result, 200);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<string,mixed>
|
* @return array<string,mixed>
|
||||||
*/
|
*/
|
||||||
public function get_catalog(int $user_id = 0): array {
|
public function get_catalog(int $user_id = 0): array {
|
||||||
$vip = class_exists('AKSRT_Settings')
|
$vip = class_exists('AKSRT_Settings')
|
||||||
? AKSRT_Settings::get_payment_vip_settings()
|
? AKSRT_Settings::get_payment_vip_settings()
|
||||||
: ['enabled' => false, 'packages_raw' => '', 'levels' => [], 'renew' => [], 'upgrade' => []];
|
: ['enabled' => false, 'packages_raw' => '', 'levels' => [], 'renew' => [], 'upgrade' => [], 'points_exchange' => []];
|
||||||
|
|
||||||
$level = function_exists('aksrt_get_user_vip_level') ? aksrt_get_user_vip_level($user_id) : 0;
|
$level = function_exists('aksrt_get_user_vip_level') ? aksrt_get_user_vip_level($user_id) : 0;
|
||||||
$expires = $user_id > 0 ? (int) get_user_meta($user_id, 'aksrt_vip_expires', true) : 0;
|
$expires = $user_id > 0 ? (int) get_user_meta($user_id, 'aksrt_vip_expires', true) : 0;
|
||||||
@@ -118,23 +145,23 @@ class AKSRT_Vip_Service {
|
|||||||
$levels = is_array($vip['levels'] ?? null) ? $vip['levels'] : [];
|
$levels = is_array($vip['levels'] ?? null) ? $vip['levels'] : [];
|
||||||
$renew = is_array($vip['renew'] ?? null) ? $vip['renew'] : [];
|
$renew = is_array($vip['renew'] ?? null) ? $vip['renew'] : [];
|
||||||
$upgrade = is_array($vip['upgrade'] ?? null) ? $vip['upgrade'] : [];
|
$upgrade = is_array($vip['upgrade'] ?? null) ? $vip['upgrade'] : [];
|
||||||
|
$points_ex = is_array($vip['points_exchange'] ?? null) ? $vip['points_exchange'] : [];
|
||||||
$renew_enabled = !isset($renew['enabled']) || !empty($renew['enabled']);
|
$renew_enabled = !isset($renew['enabled']) || !empty($renew['enabled']);
|
||||||
$upgrade_enabled = !isset($upgrade['enabled']) || !empty($upgrade['enabled']);
|
$upgrade_enabled = !isset($upgrade['enabled']) || !empty($upgrade['enabled']);
|
||||||
|
$points_enabled = !empty($points_ex['enabled'])
|
||||||
|
&& (!class_exists('AKSRT_Settings') || AKSRT_Settings::credits_enabled());
|
||||||
|
|
||||||
$level_packages = [];
|
$level_packages = [];
|
||||||
foreach ([1, 2] as $lv) {
|
foreach ([1, 2] as $lv) {
|
||||||
if (empty($levels[$lv]['enabled'])) {
|
if (empty($levels[$lv]['enabled'])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Permanent VIP at same/higher level: only show higher-level upgrade packages.
|
|
||||||
if ($is_permanent && $lv <= $level) {
|
if ($is_permanent && $lv <= $level) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Existing VIP without renew: hide same-level packages.
|
|
||||||
if ($level > 0 && $lv === $level && !$renew_enabled) {
|
if ($level > 0 && $lv === $level && !$renew_enabled) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Existing VIP without upgrade: hide higher-level packages.
|
|
||||||
if ($level > 0 && $lv > $level && !$upgrade_enabled) {
|
if ($level > 0 && $lv > $level && !$upgrade_enabled) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -144,8 +171,6 @@ class AKSRT_Vip_Service {
|
|||||||
foreach ($packages as $idx => $pkg) {
|
foreach ($packages as $idx => $pkg) {
|
||||||
$base_price = (float) $pkg['price'];
|
$base_price = (float) $pkg['price'];
|
||||||
$days = (int) $pkg['days'];
|
$days = (int) $pkg['days'];
|
||||||
// Permanent members don't renew timed packages at same level (already filtered).
|
|
||||||
// Non-permanent same-level: renew pricing.
|
|
||||||
$action = 'pay';
|
$action = 'pay';
|
||||||
if ($level > 0 && $lv === $level) {
|
if ($level > 0 && $lv === $level) {
|
||||||
$action = 'renew';
|
$action = 'renew';
|
||||||
@@ -159,57 +184,34 @@ class AKSRT_Vip_Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$items[] = [
|
$items[] = [
|
||||||
'package_id' => $this->encode_package_id($lv, $idx + 1),
|
'package_id' => $this->encode_package_id($lv, $idx + 1),
|
||||||
'level' => $lv,
|
'level' => $lv,
|
||||||
'index' => $idx + 1,
|
'index' => $idx + 1,
|
||||||
'action' => $action,
|
'action' => $action,
|
||||||
'price' => (float) $priced['price'],
|
'price' => (float) $priced['price'],
|
||||||
'base_price' => $base_price,
|
'base_price' => $base_price,
|
||||||
'show_price' => (float) ($priced['show_price'] ?? 0),
|
'show_price' => (float) ($priced['show_price'] ?? 0),
|
||||||
'discount_tag' => (string) ($priced['tag'] ?? ''),
|
'discount_tag' => (string) ($priced['tag'] ?? ''),
|
||||||
'days' => $days,
|
'days' => $days,
|
||||||
'title' => (string) $pkg['title'],
|
'title' => (string) $pkg['title'],
|
||||||
'price_label' => $this->format_cash_label((float) $priced['price']),
|
'price_label' => $this->format_cash_label((float) $priced['price']),
|
||||||
'show_price_label' => !empty($priced['show_price'])
|
'show_price_label' => !empty($priced['show_price'])
|
||||||
? $this->format_cash_label((float) $priced['show_price'])
|
? $this->format_cash_label((float) $priced['show_price'])
|
||||||
: '',
|
: '',
|
||||||
'days_label' => ($days <= 0)
|
'days_label' => ($days <= 0)
|
||||||
? __('永久', 'aksrt')
|
? __('永久', 'aksrt')
|
||||||
: sprintf(
|
: sprintf(__('%d 天', 'aksrt'), $days),
|
||||||
/* translators: %d: days */
|
'cta_label' => $action === 'renew'
|
||||||
__('%d 天', 'aksrt'),
|
|
||||||
$days
|
|
||||||
),
|
|
||||||
'cta_label' => $action === 'renew'
|
|
||||||
? __('立即续费', 'aksrt')
|
? __('立即续费', 'aksrt')
|
||||||
: ($action === 'upgrade' ? __('立即升级', 'aksrt') : __('立即开通', 'aksrt')),
|
: ($action === 'upgrade' ? __('立即升级', 'aksrt') : __('立即开通', 'aksrt')),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional dedicated upgrade permanent product line from settings.upgrade.product
|
// Dedicated upgrade product: fixed or remaining-days unit price.
|
||||||
// Format: price|days|title (single package for VIP2 upgrade).
|
|
||||||
if ($level === 1 && $lv === 2 && $upgrade_enabled) {
|
if ($level === 1 && $lv === 2 && $upgrade_enabled) {
|
||||||
$upgrade_pkg = $this->parse_upgrade_product_line((string) ($upgrade['product'] ?? ''));
|
$upgrade_items = $this->build_upgrade_items($user_id, $expires, $is_permanent, (string) ($upgrade['product'] ?? ''));
|
||||||
if ($upgrade_pkg) {
|
foreach ($upgrade_items as $ui) {
|
||||||
$items[] = [
|
$items[] = $ui;
|
||||||
'package_id' => $this->encode_package_id(2, 901),
|
|
||||||
'level' => 2,
|
|
||||||
'index' => 901,
|
|
||||||
'action' => 'upgrade',
|
|
||||||
'price' => (float) $upgrade_pkg['price'],
|
|
||||||
'base_price' => (float) $upgrade_pkg['price'],
|
|
||||||
'show_price' => 0.0,
|
|
||||||
'discount_tag' => __('升级专享', 'aksrt'),
|
|
||||||
'days' => (int) $upgrade_pkg['days'],
|
|
||||||
'title' => (string) $upgrade_pkg['title'],
|
|
||||||
'price_label' => $this->format_cash_label((float) $upgrade_pkg['price']),
|
|
||||||
'show_price_label' => '',
|
|
||||||
'days_label' => ((int) $upgrade_pkg['days'] <= 0)
|
|
||||||
? __('永久', 'aksrt')
|
|
||||||
: sprintf(__('%d 天', 'aksrt'), (int) $upgrade_pkg['days']),
|
|
||||||
'cta_label' => __('立即升级', 'aksrt'),
|
|
||||||
'is_upgrade_product' => true,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +226,14 @@ class AKSRT_Vip_Service {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$points_packages = [];
|
||||||
|
if ($points_enabled && $level <= 0) {
|
||||||
|
$points_packages = $this->get_points_exchange_packages($vip, $levels);
|
||||||
|
}
|
||||||
|
|
||||||
|
$balance = ($user_id > 0 && class_exists('AKSRT_Credits')) ? AKSRT_Credits::balance($user_id) : 0;
|
||||||
$methods = $this->available_payment_methods();
|
$methods = $this->available_payment_methods();
|
||||||
|
$currency_name = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_currency_name() : __('积分', 'aksrt');
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'enabled' => !empty($vip['enabled']),
|
'enabled' => !empty($vip['enabled']),
|
||||||
@@ -234,11 +243,18 @@ class AKSRT_Vip_Service {
|
|||||||
'user_expires' => $expires,
|
'user_expires' => $expires,
|
||||||
'user_is_permanent' => $is_permanent,
|
'user_is_permanent' => $is_permanent,
|
||||||
'user_expires_label' => $this->format_expires_label($expires, $level),
|
'user_expires_label' => $this->format_expires_label($expires, $level),
|
||||||
|
'user_points' => $balance,
|
||||||
|
'currency_name' => $currency_name,
|
||||||
'renew_enabled' => $renew_enabled,
|
'renew_enabled' => $renew_enabled,
|
||||||
'upgrade_enabled' => $upgrade_enabled,
|
'upgrade_enabled' => $upgrade_enabled,
|
||||||
'renew_desc' => (string) ($renew['desc'] ?? ''),
|
'renew_desc' => (string) ($renew['desc'] ?? ''),
|
||||||
'upgrade_desc' => (string) ($upgrade['desc'] ?? ''),
|
'upgrade_desc' => (string) ($upgrade['desc'] ?? ''),
|
||||||
'level_packages' => $level_packages,
|
'level_packages' => $level_packages,
|
||||||
|
'points_exchange' => [
|
||||||
|
'enabled' => $points_enabled && $level <= 0,
|
||||||
|
'desc' => (string) ($points_ex['desc'] ?? ''),
|
||||||
|
'packages' => $points_packages,
|
||||||
|
],
|
||||||
'payment_methods' => $methods,
|
'payment_methods' => $methods,
|
||||||
'vip_url' => function_exists('aksrt_get_user_center_tab_url')
|
'vip_url' => function_exists('aksrt_get_user_center_tab_url')
|
||||||
? aksrt_get_user_center_tab_url('credits')
|
? aksrt_get_user_center_tab_url('credits')
|
||||||
@@ -301,17 +317,29 @@ class AKSRT_Vip_Service {
|
|||||||
$action = 'upgrade';
|
$action = 'upgrade';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dedicated upgrade product (index 901).
|
// Dedicated upgrade product (index 901 fixed, 902 unit remaining days).
|
||||||
if ($index === 901) {
|
if ($index === 901 || $index === 902) {
|
||||||
$upgrade_pkg = $this->parse_upgrade_product_line((string) ($upgrade['product'] ?? ''));
|
$upgrade_items = $this->build_upgrade_items($user_id, $expires, $is_permanent, (string) ($upgrade['product'] ?? ''));
|
||||||
if (!$upgrade_pkg || $level !== 2) {
|
$match = null;
|
||||||
|
foreach ($upgrade_items as $ui) {
|
||||||
|
if ((int) ($ui['package_id'] ?? 0) === $package_id) {
|
||||||
|
$match = $ui;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$match || $level !== 2) {
|
||||||
return new WP_Error('aksrt_invalid_package', __('升级套餐不存在', 'aksrt'), ['status' => 400]);
|
return new WP_Error('aksrt_invalid_package', __('升级套餐不存在', 'aksrt'), ['status' => 400]);
|
||||||
}
|
}
|
||||||
$pkg = $upgrade_pkg;
|
$pkg = [
|
||||||
$amount = round((float) $pkg['price'], 2);
|
'price' => (float) $match['price'],
|
||||||
$days = (int) $pkg['days'];
|
'days' => (int) $match['days'],
|
||||||
$base_price = $amount;
|
'title' => (string) $match['title'],
|
||||||
$discount_tag = __('升级专享', 'aksrt');
|
];
|
||||||
|
$amount = round((float) $match['price'], 2);
|
||||||
|
$days = (int) $match['days'];
|
||||||
|
$base_price = (float) ($match['base_price'] ?? $amount);
|
||||||
|
$discount_tag = (string) ($match['discount_tag'] ?? __('升级专享', 'aksrt'));
|
||||||
|
$action = 'upgrade';
|
||||||
} else {
|
} else {
|
||||||
$packages = $this->parse_packages((string) ($vip['packages_raw'] ?? ''));
|
$packages = $this->parse_packages((string) ($vip['packages_raw'] ?? ''));
|
||||||
$pkg = $packages[$index - 1] ?? null;
|
$pkg = $packages[$index - 1] ?? null;
|
||||||
@@ -561,6 +589,288 @@ class AKSRT_Vip_Service {
|
|||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Points exchange (non-VIP only, zibll pay_vip_points_exchange).
|
||||||
|
*
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function exchange_with_points(int $user_id, int $package_id) {
|
||||||
|
if ($user_id <= 0) {
|
||||||
|
return new WP_Error('aksrt_need_login', __('请先登录后再兑换。', 'aksrt'), ['status' => 401]);
|
||||||
|
}
|
||||||
|
if (!class_exists('AKSRT_Credits')) {
|
||||||
|
return new WP_Error('aksrt_credits_unavailable', __('积分系统不可用', 'aksrt'), ['status' => 500]);
|
||||||
|
}
|
||||||
|
if (class_exists('AKSRT_Settings') && !AKSRT_Settings::credits_enabled()) {
|
||||||
|
return new WP_Error('aksrt_credits_disabled', __('积分功能未启用', 'aksrt'), ['status' => 400]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$vip = class_exists('AKSRT_Settings')
|
||||||
|
? AKSRT_Settings::get_payment_vip_settings()
|
||||||
|
: [];
|
||||||
|
$points_ex = is_array($vip['points_exchange'] ?? null) ? $vip['points_exchange'] : [];
|
||||||
|
if (empty($points_ex['enabled'])) {
|
||||||
|
return new WP_Error('aksrt_points_exchange_disabled', __('积分兑换会员未启用', 'aksrt'), ['status' => 400]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user_level = function_exists('aksrt_get_user_vip_level') ? aksrt_get_user_vip_level($user_id) : 0;
|
||||||
|
if ($user_level > 0) {
|
||||||
|
return new WP_Error('aksrt_already_vip', __('您已是会员,无法使用积分兑换。', 'aksrt'), ['status' => 400]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$levels = is_array($vip['levels'] ?? null) ? $vip['levels'] : [];
|
||||||
|
$packages = $this->get_points_exchange_packages($vip, $levels);
|
||||||
|
$match = null;
|
||||||
|
foreach ($packages as $pkg) {
|
||||||
|
if ((int) ($pkg['package_id'] ?? 0) === $package_id) {
|
||||||
|
$match = $pkg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$match) {
|
||||||
|
return new WP_Error('aksrt_invalid_package', __('兑换套餐不存在', 'aksrt'), ['status' => 400]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$cost = (int) ($match['points'] ?? 0);
|
||||||
|
$level = (int) ($match['level'] ?? 1);
|
||||||
|
$days = (int) ($match['days'] ?? 0);
|
||||||
|
if ($cost <= 0) {
|
||||||
|
return new WP_Error('aksrt_invalid_price', __('兑换积分无效', 'aksrt'), ['status' => 400]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$balance = AKSRT_Credits::balance($user_id);
|
||||||
|
if ($balance < $cost) {
|
||||||
|
return new WP_Error('aksrt_credits_insufficient', __('积分不足,暂时无法兑换', 'aksrt'), [
|
||||||
|
'status' => 402,
|
||||||
|
'balance' => $balance,
|
||||||
|
'cost' => $cost,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!class_exists('AKSRT_Order') || !class_exists('AKSRT_Order_Repository')) {
|
||||||
|
return new WP_Error('aksrt_pay_unavailable', __('订单系统不可用', 'aksrt'), ['status' => 500]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$note = wp_json_encode([
|
||||||
|
'vip_pay' => 1,
|
||||||
|
'vip_pay_type' => 'points_exchange',
|
||||||
|
'level' => $level,
|
||||||
|
'days' => $days,
|
||||||
|
'points' => $cost,
|
||||||
|
'package_id' => $package_id,
|
||||||
|
'package_title' => (string) ($match['title'] ?? ''),
|
||||||
|
'action' => 'points_exchange',
|
||||||
|
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||||
|
|
||||||
|
$order_number = AKSRT_Order_Repository::get_instance()->create([
|
||||||
|
'user_id' => $user_id,
|
||||||
|
'product_type' => AKSRT_Domain_Model::ORDER_PRODUCT_VIP,
|
||||||
|
'product_id' => $package_id,
|
||||||
|
'amount' => (float) $cost,
|
||||||
|
'currency' => 'points',
|
||||||
|
'status' => AKSRT_Order::STATUS_PENDING,
|
||||||
|
'payment_method' => 'points',
|
||||||
|
'note' => is_string($note) ? $note : '',
|
||||||
|
'expires_at' => gmdate('Y-m-d H:i:s', time() + 30 * MINUTE_IN_SECONDS),
|
||||||
|
]);
|
||||||
|
if ($order_number === '') {
|
||||||
|
return new WP_Error('aksrt_order_create_failed', __('创建订单失败', 'aksrt'), ['status' => 500]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$deducted = AKSRT_Credits::deduct(
|
||||||
|
$user_id,
|
||||||
|
$cost,
|
||||||
|
'vip_points_exchange',
|
||||||
|
['order' => $order_number, 'level' => $level, 'days' => $days],
|
||||||
|
sprintf(__('积分兑换会员 · %s', 'aksrt'), (string) ($match['title'] ?? ''))
|
||||||
|
);
|
||||||
|
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 new WP_Error('aksrt_credits_deduct_failed', $msg ?: __('积分扣除失败', 'aksrt'), ['status' => 400]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$paid = AKSRT_Order::get_instance()->update_status($order_number, AKSRT_Order::STATUS_PAID, [
|
||||||
|
'payment_method' => 'points',
|
||||||
|
'payment_transaction_id' => 'points_' . $order_number,
|
||||||
|
'note' => sprintf(__('积分兑换会员 %d 积分。', 'aksrt'), $cost),
|
||||||
|
]);
|
||||||
|
if (!$paid) {
|
||||||
|
AKSRT_Credits::add(
|
||||||
|
$user_id,
|
||||||
|
$cost,
|
||||||
|
'vip_points_exchange_refund',
|
||||||
|
['order' => $order_number],
|
||||||
|
__('会员兑换失败退款', 'aksrt')
|
||||||
|
);
|
||||||
|
return new WP_Error('aksrt_order_confirm_failed', __('兑换确认失败,已尝试退回积分', 'aksrt'), ['status' => 500]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'data' => [
|
||||||
|
'order_number' => $order_number,
|
||||||
|
'points' => $cost,
|
||||||
|
'level' => $level,
|
||||||
|
'days' => $days,
|
||||||
|
'balance' => AKSRT_Credits::balance($user_id),
|
||||||
|
'message' => __('兑换成功', 'aksrt'),
|
||||||
|
'reload' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build upgrade package items (fixed and/or remaining-days unit).
|
||||||
|
*
|
||||||
|
* upgrade.product formats:
|
||||||
|
* - fixed: price|days|title
|
||||||
|
* - unit: unit|daily_price|title (price = remaining_days * daily_price, days = remaining)
|
||||||
|
*
|
||||||
|
* @return array<int, array<string,mixed>>
|
||||||
|
*/
|
||||||
|
private function build_upgrade_items(int $user_id, int $expires, bool $is_permanent, string $raw): array {
|
||||||
|
$raw = trim($raw);
|
||||||
|
if ($raw === '') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$items = [];
|
||||||
|
$lines = preg_split('/\r\n|\r|\n/', $raw) ?: [];
|
||||||
|
$fixed_i = 0;
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$line = trim((string) $line);
|
||||||
|
if ($line === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$parts = array_map('trim', explode('|', $line));
|
||||||
|
$mode = strtolower((string) ($parts[0] ?? ''));
|
||||||
|
|
||||||
|
if ($mode === 'unit') {
|
||||||
|
if ($is_permanent || $expires <= time()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$daily = isset($parts[1]) ? (float) $parts[1] : 0.0;
|
||||||
|
if ($daily <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$remaining = (int) ceil(max(1, ($expires - time()) / DAY_IN_SECONDS));
|
||||||
|
$price = round(max(0.01, $remaining * $daily), 2);
|
||||||
|
$title = sanitize_text_field((string) ($parts[2] ?? ''));
|
||||||
|
if ($title === '') {
|
||||||
|
$title = sprintf(__('按剩余 %d 天升级', 'aksrt'), $remaining);
|
||||||
|
}
|
||||||
|
$items[] = [
|
||||||
|
'package_id' => $this->encode_package_id(2, 902),
|
||||||
|
'level' => 2,
|
||||||
|
'index' => 902,
|
||||||
|
'action' => 'upgrade',
|
||||||
|
'price' => $price,
|
||||||
|
'base_price' => $price,
|
||||||
|
'show_price' => 0.0,
|
||||||
|
'discount_tag' => __('剩余天数', 'aksrt'),
|
||||||
|
'days' => $remaining,
|
||||||
|
'title' => $title,
|
||||||
|
'price_label' => $this->format_cash_label($price),
|
||||||
|
'show_price_label' => '',
|
||||||
|
'days_label' => sprintf(__('%d 天', 'aksrt'), $remaining),
|
||||||
|
'cta_label' => __('立即升级', 'aksrt'),
|
||||||
|
'is_upgrade_product' => true,
|
||||||
|
'upgrade_mode' => 'unit',
|
||||||
|
];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parsed = $this->parse_packages($line);
|
||||||
|
if (empty($parsed[0])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$fixed_i++;
|
||||||
|
// Only one fixed upgrade product id 901.
|
||||||
|
if ($fixed_i > 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$pkg = $parsed[0];
|
||||||
|
$items[] = [
|
||||||
|
'package_id' => $this->encode_package_id(2, 901),
|
||||||
|
'level' => 2,
|
||||||
|
'index' => 901,
|
||||||
|
'action' => 'upgrade',
|
||||||
|
'price' => (float) $pkg['price'],
|
||||||
|
'base_price' => (float) $pkg['price'],
|
||||||
|
'show_price' => 0.0,
|
||||||
|
'discount_tag' => __('升级专享', 'aksrt'),
|
||||||
|
'days' => (int) $pkg['days'],
|
||||||
|
'title' => (string) $pkg['title'],
|
||||||
|
'price_label' => $this->format_cash_label((float) $pkg['price']),
|
||||||
|
'show_price_label' => '',
|
||||||
|
'days_label' => ((int) $pkg['days'] <= 0)
|
||||||
|
? __('永久', 'aksrt')
|
||||||
|
: sprintf(__('%d 天', 'aksrt'), (int) $pkg['days']),
|
||||||
|
'cta_label' => __('立即升级', 'aksrt'),
|
||||||
|
'is_upgrade_product' => true,
|
||||||
|
'upgrade_mode' => 'fixed',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Points exchange packages: level|points|days|title
|
||||||
|
*
|
||||||
|
* @param array<string,mixed> $vip
|
||||||
|
* @param array<int,mixed> $levels
|
||||||
|
* @return array<int, array<string,mixed>>
|
||||||
|
*/
|
||||||
|
public function get_points_exchange_packages(array $vip, array $levels): array {
|
||||||
|
$points_ex = is_array($vip['points_exchange'] ?? null) ? $vip['points_exchange'] : [];
|
||||||
|
$raw = (string) ($points_ex['packages_raw'] ?? '');
|
||||||
|
$currency = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_currency_name() : __('积分', 'aksrt');
|
||||||
|
$out = [];
|
||||||
|
$lines = preg_split('/\r\n|\r|\n/', $raw) ?: [];
|
||||||
|
$i = 0;
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$line = trim((string) $line);
|
||||||
|
if ($line === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$parts = array_map('trim', explode('|', $line));
|
||||||
|
if (count($parts) < 3) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$lv = (int) ($parts[0] ?? 0);
|
||||||
|
$pts = (int) ($parts[1] ?? 0);
|
||||||
|
$days = (int) ($parts[2] ?? 0);
|
||||||
|
$title = sanitize_text_field((string) ($parts[3] ?? ''));
|
||||||
|
if ($lv < 1 || $lv > 2 || $pts <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (empty($levels[$lv]['enabled'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$i++;
|
||||||
|
if ($title === '') {
|
||||||
|
$lv_name = (string) ($levels[$lv]['name'] ?? ('VIP' . $lv));
|
||||||
|
$title = $days > 0
|
||||||
|
? sprintf(__('%1$s · %2$d天', 'aksrt'), $lv_name, $days)
|
||||||
|
: sprintf(__('%s · 永久', 'aksrt'), $lv_name);
|
||||||
|
}
|
||||||
|
$out[] = [
|
||||||
|
'package_id' => 5000 + $i,
|
||||||
|
'level' => $lv,
|
||||||
|
'index' => $i,
|
||||||
|
'points' => $pts,
|
||||||
|
'days' => max(0, $days),
|
||||||
|
'title' => $title,
|
||||||
|
'price_label' => $pts . ' ' . $currency,
|
||||||
|
'days_label' => $days > 0 ? sprintf(__('%d 天', 'aksrt'), $days) : __('永久', 'aksrt'),
|
||||||
|
'cta_label' => __('立即兑换', 'aksrt'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Single upgrade product line: price|days|title
|
* Single upgrade product line: price|days|title
|
||||||
*
|
*
|
||||||
@@ -571,10 +881,13 @@ class AKSRT_Vip_Service {
|
|||||||
if ($raw === '') {
|
if ($raw === '') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// Allow multiline; take first valid.
|
|
||||||
$lines = preg_split('/\r\n|\r|\n/', $raw) ?: [];
|
$lines = preg_split('/\r\n|\r|\n/', $raw) ?: [];
|
||||||
foreach ($lines as $line) {
|
foreach ($lines as $line) {
|
||||||
$parsed = $this->parse_packages((string) $line);
|
$line = trim((string) $line);
|
||||||
|
if ($line === '' || str_starts_with(strtolower($line), 'unit|')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$parsed = $this->parse_packages($line);
|
||||||
if (!empty($parsed[0])) {
|
if (!empty($parsed[0])) {
|
||||||
return $parsed[0];
|
return $parsed[0];
|
||||||
}
|
}
|
||||||
@@ -604,7 +917,6 @@ class AKSRT_Vip_Service {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Permanent same-level packages never make sense for renew.
|
|
||||||
if ($action === 'renew' && $is_permanent) {
|
if ($action === 'renew' && $is_permanent) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -623,7 +935,6 @@ class AKSRT_Vip_Service {
|
|||||||
$tag = '';
|
$tag = '';
|
||||||
|
|
||||||
if ($type === 'discount') {
|
if ($type === 'discount') {
|
||||||
// Settings store "8" meaning 8折 (80%) when <= 10; or percent when > 10.
|
|
||||||
$discount = (float) ($renew['discount'] ?? 8);
|
$discount = (float) ($renew['discount'] ?? 8);
|
||||||
if ($discount <= 0) {
|
if ($discount <= 0) {
|
||||||
$discount = 8;
|
$discount = 8;
|
||||||
@@ -648,7 +959,6 @@ class AKSRT_Vip_Service {
|
|||||||
$this->format_cash_label($reduce)
|
$this->format_cash_label($reduce)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// same
|
|
||||||
$show = 0.0;
|
$show = 0.0;
|
||||||
$tag = __('续费同价', 'aksrt');
|
$tag = __('续费同价', 'aksrt');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,6 +193,11 @@ class AKSRT_Settings_Payment_Runtime {
|
|||||||
'desc' => sanitize_textarea_field((string) ($settings['vip_upgrade_desc'] ?? '')),
|
'desc' => sanitize_textarea_field((string) ($settings['vip_upgrade_desc'] ?? '')),
|
||||||
'product' => sanitize_text_field((string) ($settings['vip_upgrade_product'] ?? '')),
|
'product' => sanitize_text_field((string) ($settings['vip_upgrade_product'] ?? '')),
|
||||||
],
|
],
|
||||||
|
'points_exchange' => [
|
||||||
|
'enabled' => !empty($settings['pay_vip_points_exchange_s']),
|
||||||
|
'desc' => sanitize_textarea_field((string) ($settings['pay_vip_points_exchange_desc'] ?? '')),
|
||||||
|
'packages_raw' => (string) ($settings['payment_vip_points_packages'] ?? "1|500|30|积分月卡\n1|5000|0|积分永久"),
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* User center credits: VIP membership card + purchase packages.
|
* User center credits: VIP membership card + purchase packages + points exchange.
|
||||||
*
|
*
|
||||||
* Expects extracted $context vars.
|
* Expects extracted $context vars.
|
||||||
*
|
*
|
||||||
@@ -41,12 +41,20 @@ if ($vip_level >= 1 && !empty($vip_settings['levels'][$vip_level]['name'])) {
|
|||||||
$packages = [];
|
$packages = [];
|
||||||
$renew_desc = '';
|
$renew_desc = '';
|
||||||
$upgrade_desc = '';
|
$upgrade_desc = '';
|
||||||
|
$points_exchange = ['enabled' => false, 'packages' => [], 'desc' => ''];
|
||||||
|
$user_points = 0;
|
||||||
|
$currency_name = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_currency_name() : __('积分', 'aksrt');
|
||||||
if (class_exists('AKSRT_Vip_Service')) {
|
if (class_exists('AKSRT_Vip_Service')) {
|
||||||
$catalog = AKSRT_Vip_Service::get_instance()->get_catalog($user_id);
|
$catalog = AKSRT_Vip_Service::get_instance()->get_catalog($user_id);
|
||||||
$packages = is_array($catalog['level_packages'] ?? null) ? $catalog['level_packages'] : [];
|
$packages = is_array($catalog['level_packages'] ?? null) ? $catalog['level_packages'] : [];
|
||||||
$methods = is_array($catalog['payment_methods'] ?? null) ? $catalog['payment_methods'] : [];
|
$methods = is_array($catalog['payment_methods'] ?? null) ? $catalog['payment_methods'] : [];
|
||||||
$renew_desc = (string) ($catalog['renew_desc'] ?? '');
|
$renew_desc = (string) ($catalog['renew_desc'] ?? '');
|
||||||
$upgrade_desc = (string) ($catalog['upgrade_desc'] ?? '');
|
$upgrade_desc = (string) ($catalog['upgrade_desc'] ?? '');
|
||||||
|
$points_exchange = is_array($catalog['points_exchange'] ?? null) ? $catalog['points_exchange'] : $points_exchange;
|
||||||
|
$user_points = (int) ($catalog['user_points'] ?? 0);
|
||||||
|
if (!empty($catalog['currency_name'])) {
|
||||||
|
$currency_name = (string) $catalog['currency_name'];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$methods = [['id' => 'balance', 'name' => __('站内余额', 'aksrt')]];
|
$methods = [['id' => 'balance', 'name' => __('站内余额', 'aksrt')]];
|
||||||
}
|
}
|
||||||
@@ -61,6 +69,7 @@ $shop_title = __('开通会员', 'aksrt');
|
|||||||
if ($vip_level > 0) {
|
if ($vip_level > 0) {
|
||||||
$shop_title = __('续费 / 升级会员', 'aksrt');
|
$shop_title = __('续费 / 升级会员', 'aksrt');
|
||||||
}
|
}
|
||||||
|
$points_pkgs = is_array($points_exchange['packages'] ?? null) ? $points_exchange['packages'] : [];
|
||||||
?>
|
?>
|
||||||
<div class="uc-vip-card<?php echo $is_vip_user ? ' uc-vip-card--active' : ''; ?>" data-aksrt-vip-panel>
|
<div class="uc-vip-card<?php echo $is_vip_user ? ' uc-vip-card--active' : ''; ?>" data-aksrt-vip-panel>
|
||||||
<div class="uc-vip-card__bg"></div>
|
<div class="uc-vip-card__bg"></div>
|
||||||
@@ -183,6 +192,52 @@ if ($vip_level > 0) {
|
|||||||
|
|
||||||
<p class="uc-vip-shop__tip" data-aksrt-vip-tip></p>
|
<p class="uc-vip-shop__tip" data-aksrt-vip-tip></p>
|
||||||
</div>
|
</div>
|
||||||
<?php elseif (!empty($vip_settings['help_text'])) : ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (!empty($points_exchange['enabled']) && $points_pkgs !== []) : ?>
|
||||||
|
<div class="uc-vip-shop uc-vip-shop--points" data-aksrt-vip-points>
|
||||||
|
<div class="uc-vip-shop__title">
|
||||||
|
<i class="fas fa-coins" aria-hidden="true"></i>
|
||||||
|
<span><?php esc_html_e('积分兑换会员', 'aksrt'); ?></span>
|
||||||
|
</div>
|
||||||
|
<div class="uc-vip-shop__balance">
|
||||||
|
<?php
|
||||||
|
printf(
|
||||||
|
/* translators: 1: points balance 2: currency name */
|
||||||
|
esc_html__('我的积分:%1$s %2$s', 'aksrt'),
|
||||||
|
esc_html((string) $user_points),
|
||||||
|
esc_html($currency_name)
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
<?php if (!empty($points_exchange['desc'])) : ?>
|
||||||
|
<p class="uc-vip-shop__note"><?php echo esc_html((string) $points_exchange['desc']); ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<div class="uc-vip-shop__grid">
|
||||||
|
<?php foreach ($points_pkgs as $item) :
|
||||||
|
$pid = (int) ($item['package_id'] ?? 0);
|
||||||
|
$cost = (int) ($item['points'] ?? 0);
|
||||||
|
$enough = $user_points >= $cost;
|
||||||
|
?>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="uc-vip-shop__item<?php echo $enough ? '' : ' is-disabled'; ?>"
|
||||||
|
data-aksrt-vip-points-buy
|
||||||
|
data-package-id="<?php echo esc_attr((string) $pid); ?>"
|
||||||
|
data-points="<?php echo esc_attr((string) $cost); ?>"
|
||||||
|
<?php disabled(!$enough); ?>
|
||||||
|
>
|
||||||
|
<span class="uc-vip-shop__item-title"><?php echo esc_html((string) ($item['title'] ?? '')); ?></span>
|
||||||
|
<span class="uc-vip-shop__item-days"><?php echo esc_html((string) ($item['days_label'] ?? '')); ?></span>
|
||||||
|
<span class="uc-vip-shop__item-price"><?php echo esc_html((string) ($item['price_label'] ?? '')); ?></span>
|
||||||
|
<span class="uc-vip-shop__item-cta">
|
||||||
|
<?php echo $enough ? esc_html__('立即兑换', 'aksrt') : esc_html__('积分不足', 'aksrt'); ?>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<p class="uc-vip-shop__tip" data-aksrt-vip-points-tip></p>
|
||||||
|
</div>
|
||||||
|
<?php elseif (empty($packages) && !empty($vip_settings['help_text'])) : ?>
|
||||||
<p class="uc-vip-shop__empty"><?php echo esc_html((string) $vip_settings['help_text']); ?></p>
|
<p class="uc-vip-shop__empty"><?php echo esc_html((string) $vip_settings['help_text']); ?></p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
Reference in new issue
Block a user