538 lines
25 KiB
PHP
538 lines
25 KiB
PHP
<?php
|
|
/**
|
|
* @package AKSRT
|
|
*/
|
|
|
|
namespace AKSRT\Content;
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
trait PayHelpers {
|
|
|
|
|
|
|
|
/**
|
|
* Cashier preview payload.
|
|
*
|
|
* @return array|\WP_Error
|
|
*/
|
|
public function preview(int $post_id, int $user_id = 0, string $coupon_code = '', string $price_type = '') {
|
|
$post = get_post($post_id);
|
|
if (!($post instanceof \WP_Post) || $post->post_status !== 'publish') {
|
|
return new \WP_Error('aksrt_invalid', __('内容不存在', 'aksrt'), ['status' => 404]);
|
|
}
|
|
|
|
$meta = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($post) : [];
|
|
$pay_type = \AKSRT\Core\DomainModel::normalize_pay_type((string) ($meta['pay_type'] ?? \AKSRT\Core\DomainModel::PAY_TYPE_NO));
|
|
if ($pay_type === \AKSRT\Core\DomainModel::PAY_TYPE_NO) {
|
|
return new \WP_Error('aksrt_not_paid', __('该内容无需购买', 'aksrt'), ['status' => 400]);
|
|
}
|
|
|
|
$uid = $user_id > 0 ? $user_id : get_current_user_id();
|
|
$priced = function_exists('aksrt_get_user_pay_price')
|
|
? aksrt_get_user_pay_price($post, $uid)
|
|
: ['amount' => '0.00', 'base' => '0.00', 'modo' => '0', 'currency' => '¥', 'is_free' => true, 'pay_limit' => 0, 'vip_level' => 0, 'original' => '0.00', 'vip1' => '0.00', 'vip2' => '0.00'];
|
|
|
|
$pay_modo = (($priced['modo'] ?? '0') === 'points') ? 'points' : '0';
|
|
|
|
// Optional overbuy override for preview display.
|
|
if ($price_type === 'download_limit_over') {
|
|
$over_minor = $this->parse_asset_minor($meta['download_limit_over_price'] ?? ($pay_modo === 'points' ? 0 : '0'), $pay_modo);
|
|
if ($over_minor > 0) {
|
|
$priced['amount'] = $this->asset_value($over_minor, $pay_modo);
|
|
$priced['base'] = $this->asset_value($over_minor, $pay_modo);
|
|
$priced['is_free'] = false;
|
|
}
|
|
}
|
|
|
|
$pay_limit = (int) ($priced['pay_limit'] ?? 0);
|
|
$vip_level = (int) ($priced['vip_level'] ?? 0);
|
|
$amount_minor = $this->parse_asset_minor($priced['amount'] ?? ($pay_modo === 'points' ? 0 : '0'), $pay_modo);
|
|
$base_minor = $this->parse_asset_minor($priced['base'] ?? $priced['amount'] ?? ($pay_modo === 'points' ? 0 : '0'), $pay_modo);
|
|
$over_minor = $this->parse_asset_minor($meta['download_limit_over_price'] ?? ($pay_modo === 'points' ? 0 : '0'), $pay_modo);
|
|
|
|
$list_minor = $amount_minor;
|
|
$rebate_discount_minor = 0;
|
|
$coupon_discount_minor = 0;
|
|
$coupon_payload = null;
|
|
$coupon_error = '';
|
|
$referrer_active = false;
|
|
$referrer_id = 0;
|
|
$rebate_price_minor = 0;
|
|
$payable_minor = $amount_minor;
|
|
|
|
$coupon_allowed = $pay_modo !== 'points' && $this->is_coupon_allowed($post_id, $meta);
|
|
$coupon_desc = '';
|
|
if ($coupon_allowed) {
|
|
$coupon_desc = trim((string) ($meta['coupon_desc'] ?? ''));
|
|
if ($coupon_desc === '' && class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'get_content_coupon_settings')) {
|
|
$coupon_desc = (string) (\AKSRT\Settings\Settings::get_content_coupon_settings()['desc'] ?? '');
|
|
}
|
|
}
|
|
|
|
if ($pay_modo !== 'points') {
|
|
$discounted = $this->apply_cash_discounts($priced, $post, $meta, $uid, $coupon_code, true);
|
|
if (is_wp_error($discounted)) {
|
|
// Preview: soft-fail coupon (show error, keep base payable without coupon).
|
|
$coupon_error = $discounted->get_error_message();
|
|
$discounted = $this->apply_cash_discounts($priced, $post, $meta, $uid, '', true);
|
|
}
|
|
if (!is_wp_error($discounted)) {
|
|
$list_minor = (int) ($discounted['list_minor'] ?? $amount_minor);
|
|
$rebate_discount_minor = (int) ($discounted['rebate_discount_minor'] ?? 0);
|
|
$coupon_discount_minor = (int) ($discounted['coupon_discount_minor'] ?? 0);
|
|
$coupon_payload = $discounted['coupon'] ?? null;
|
|
$payable_minor = (int) ($discounted['payable_minor'] ?? $amount_minor);
|
|
$referrer_id = (int) ($discounted['referrer_id'] ?? 0);
|
|
$referrer_active = $referrer_id > 0;
|
|
$rebate_price_minor = (int) ($discounted['rebate_price_minor'] ?? 0);
|
|
$amount_minor = $payable_minor;
|
|
}
|
|
}
|
|
|
|
$has_paid = false;
|
|
$paid_order = null;
|
|
if ($uid > 0 && class_exists('\AKSRT\Payment\Order')) {
|
|
$has_paid = \AKSRT\Payment\Order::get_instance()->has_paid_access(
|
|
$uid,
|
|
\AKSRT\Core\DomainModel::ORDER_PRODUCT_RESOURCE,
|
|
$post_id
|
|
);
|
|
if ($has_paid && function_exists('aksrt_get_user_content_paid_order')) {
|
|
$paid_order = aksrt_get_user_content_paid_order($post_id, $uid);
|
|
}
|
|
}
|
|
if (!$has_paid && $uid <= 0 && function_exists('aksrt_get_guest_content_paid_order')) {
|
|
$guest_order = aksrt_get_guest_content_paid_order($post_id);
|
|
if ($guest_order) {
|
|
$has_paid = true;
|
|
$paid_order = $guest_order;
|
|
}
|
|
}
|
|
|
|
$pending_order = null;
|
|
if ($uid > 0 && !$has_paid && class_exists('\AKSRT\Payment\OrderRepository')) {
|
|
$pending_order = $this->find_pending_content_order($uid, $post_id);
|
|
}
|
|
|
|
$need_vip = $pay_limit > 0 && $vip_level < $pay_limit;
|
|
$allow_guest = function_exists('aksrt_allow_guest_content_pay') && aksrt_allow_guest_content_pay($post);
|
|
$currency = (string) ($priced['currency'] ?? ($pay_modo === 'points'
|
|
? (class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_currency_name() : __('积分', 'aksrt'))
|
|
: '¥'));
|
|
|
|
// Points cashier uses Credits; cash cashier uses wallet (元). Keep both for UI.
|
|
$points_balance = $uid > 0 ? \AKSRT\Ledger\LedgerService::get_instance()->balance($uid, \AKSRT\Ledger\AssetType::POINTS) : 0;
|
|
$wallet_minor = $uid > 0 ? \AKSRT\Ledger\LedgerService::get_instance()->balance($uid, \AKSRT\Ledger\AssetType::BALANCE) : 0;
|
|
$wallet_balance = \AKSRT\Ledger\AssetAmount::decimal_from_minor($wallet_minor);
|
|
$balance = $pay_modo === 'points' ? $points_balance : $wallet_balance;
|
|
$methods = $this->available_payment_methods($pay_modo, $uid > 0);
|
|
|
|
$amount_label = $this->format_amount_label($amount_minor, $pay_modo, $currency);
|
|
$base_label = $this->format_amount_label($base_minor, $pay_modo, $currency);
|
|
$list_label = $this->format_amount_label($list_minor, $pay_modo, $currency);
|
|
$over_label = $over_minor > 0 ? $this->format_amount_label($over_minor, $pay_modo, $currency) : '';
|
|
|
|
$vip_page = function_exists('aksrt_get_user_center_tab_url')
|
|
? aksrt_get_user_center_tab_url('credits')
|
|
: (function_exists('aksrt_get_user_center_url')
|
|
? aksrt_get_user_center_url()
|
|
: home_url('/'));
|
|
|
|
$free_limit = $uid > 0 && function_exists('aksrt_get_user_free_download_limit')
|
|
? aksrt_get_user_free_download_limit($uid) : 0;
|
|
$free_used = $uid > 0 && function_exists('aksrt_get_user_free_downloaded_number')
|
|
? aksrt_get_user_free_downloaded_number($uid) : 0;
|
|
$quota_over = $free_limit > 0 && $free_used >= $free_limit;
|
|
|
|
$payable_amount = $this->asset_value($payable_minor, $pay_modo);
|
|
$balance_enough = $pay_modo === 'points'
|
|
? ($points_balance >= $payable_minor)
|
|
: ($wallet_minor >= $payable_minor);
|
|
|
|
return [
|
|
'post_id' => $post_id,
|
|
'title' => get_the_title($post),
|
|
'permalink' => get_permalink($post),
|
|
'pay_type' => $pay_type,
|
|
'pay_type_label' => \AKSRT\Core\DomainModel::pay_type_labels()[$pay_type] ?? $pay_type,
|
|
'pay_modo' => $pay_modo,
|
|
'pay_limit' => $pay_limit,
|
|
'vip_level' => $vip_level,
|
|
'amount' => $this->asset_value($amount_minor, $pay_modo),
|
|
'base' => $this->asset_value($base_minor, $pay_modo),
|
|
'list_amount' => $this->asset_value($list_minor, $pay_modo),
|
|
'list_amount_label' => $list_label,
|
|
'payable' => $payable_amount,
|
|
'original' => $this->asset_value($this->parse_asset_minor($priced['original'] ?? ($pay_modo === 'points' ? 0 : '0'), $pay_modo), $pay_modo),
|
|
'vip1' => $this->asset_value($this->parse_asset_minor($priced['vip1'] ?? ($pay_modo === 'points' ? 0 : '0'), $pay_modo), $pay_modo),
|
|
'vip2' => $this->asset_value($this->parse_asset_minor($priced['vip2'] ?? ($pay_modo === 'points' ? 0 : '0'), $pay_modo), $pay_modo),
|
|
'currency' => $currency,
|
|
'amount_label' => $amount_label,
|
|
'base_label' => $base_label,
|
|
'is_free' => !empty($priced['is_free']) || $amount_minor <= 0,
|
|
'has_paid_access' => $has_paid,
|
|
'paid_order' => $paid_order ? [
|
|
'order_number' => (string) ($paid_order['order_number'] ?? ''),
|
|
'paid_at' => (string) ($paid_order['paid_at'] ?? $paid_order['created_at'] ?? ''),
|
|
'amount' => (string) ($paid_order['currency'] ?? '') === 'points'
|
|
? (int) ($paid_order['amount'] ?? 0)
|
|
: (string) ($paid_order['amount'] ?? '0.00'),
|
|
'currency' => (string) ($paid_order['currency'] ?? ''),
|
|
'payment_method' => (string) ($paid_order['payment_method'] ?? ''),
|
|
] : null,
|
|
'pending_order' => $pending_order ? [
|
|
'order_number' => (string) ($pending_order['order_number'] ?? ''),
|
|
'amount' => (string) ($pending_order['amount'] ?? '0.00'),
|
|
'payment_method' => (string) ($pending_order['payment_method'] ?? ''),
|
|
'created_at' => (string) ($pending_order['created_at'] ?? ''),
|
|
] : null,
|
|
'need_login' => $uid <= 0 && !$allow_guest,
|
|
'allow_guest' => $allow_guest,
|
|
'need_vip' => $need_vip,
|
|
'need_purchase' => !$has_paid && !$need_vip && $amount_minor > 0,
|
|
'logged_in' => $uid > 0,
|
|
'balance' => $balance,
|
|
'wallet_balance' => $wallet_balance,
|
|
'points_balance' => $points_balance,
|
|
'balance_enough' => $balance_enough,
|
|
'payment_methods' => $methods,
|
|
'login_url' => wp_login_url(get_permalink($post)),
|
|
'vip_url' => $vip_page,
|
|
'credits_url' => $vip_page,
|
|
'download_limit_over_price' => $this->asset_value($over_minor, $pay_modo),
|
|
'download_limit_over_label' => $over_label,
|
|
'allow_limit_over' => $over_minor > 0,
|
|
'quota_over' => $quota_over,
|
|
'free_limit' => $free_limit,
|
|
'free_used' => $free_used,
|
|
'coupon_allowed' => $coupon_allowed,
|
|
'coupon_desc' => $coupon_desc,
|
|
'coupon_code' => $coupon_code,
|
|
'coupon' => $coupon_payload,
|
|
'coupon_error' => $coupon_error,
|
|
'rebate_discount' => $this->asset_value($rebate_discount_minor, $pay_modo),
|
|
'rebate_discount_label' => $rebate_discount_minor > 0 ? ('-' . $this->format_amount_label($rebate_discount_minor, $pay_modo, $currency)) : '',
|
|
'coupon_discount' => $this->asset_value($coupon_discount_minor, $pay_modo),
|
|
'coupon_discount_label' => $coupon_discount_minor > 0 ? ('-' . $this->format_amount_label($coupon_discount_minor, $pay_modo, $currency)) : '',
|
|
'referrer_active' => $referrer_active,
|
|
'discounts' => [
|
|
'list_amount' => $this->asset_value($list_minor, $pay_modo),
|
|
'rebate_discount' => $this->asset_value($rebate_discount_minor, $pay_modo),
|
|
'coupon_discount' => $this->asset_value($coupon_discount_minor, $pay_modo),
|
|
'total_discount' => $this->asset_value(max(0, $list_minor - $payable_minor), $pay_modo),
|
|
'payable' => $this->asset_value($payable_minor, $pay_modo),
|
|
'rebate_price' => $this->asset_value($rebate_price_minor, $pay_modo),
|
|
],
|
|
// legacy: points_deduction runtime hard-off; card_pass not a content payment method.
|
|
'points_deduction_allowed' => function_exists('aksrt_is_allow_points_deduction')
|
|
? (bool) aksrt_is_allow_points_deduction('content')
|
|
: false,
|
|
'card_pass_allowed' => function_exists('aksrt_is_content_card_pass_pay_allowed')
|
|
? (bool) aksrt_is_content_card_pass_pay_allowed($post)
|
|
: false,
|
|
'price_type' => $price_type,
|
|
];
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* @return array{post:\WP_Post,meta:array,priced:array}|\WP_Error
|
|
*/
|
|
private function assert_purchasable(int $post_id, int $user_id, string $price_type = '') {
|
|
$post = get_post($post_id);
|
|
if (!($post instanceof \WP_Post) || $post->post_status !== 'publish') {
|
|
return new \WP_Error('aksrt_invalid', __('内容不存在', 'aksrt'), ['status' => 404]);
|
|
}
|
|
|
|
$meta = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($post) : [];
|
|
$pay_type = \AKSRT\Core\DomainModel::normalize_pay_type((string) ($meta['pay_type'] ?? \AKSRT\Core\DomainModel::PAY_TYPE_NO));
|
|
if ($pay_type === \AKSRT\Core\DomainModel::PAY_TYPE_NO && $price_type !== 'download_limit_over') {
|
|
return new \WP_Error('aksrt_not_paid', __('该内容无需购买', 'aksrt'), ['status' => 400]);
|
|
}
|
|
if ($pay_type === \AKSRT\Core\DomainModel::PAY_TYPE_VIP) {
|
|
return new \WP_Error('aksrt_need_vip', __('该内容为 VIP 专属,请开通会员。', 'aksrt'), ['status' => 403]);
|
|
}
|
|
|
|
$pay_modo_meta = (($meta['pay_modo'] ?? '0') === 'points') ? 'points' : '0';
|
|
$allow_guest = function_exists('aksrt_allow_guest_content_pay') && aksrt_allow_guest_content_pay($post);
|
|
if ($user_id <= 0) {
|
|
if ($pay_modo_meta === 'points' || !$allow_guest) {
|
|
return new \WP_Error('aksrt_need_login', __('请先登录后再购买。', 'aksrt'), [
|
|
'status' => 401,
|
|
'login_url' => wp_login_url(get_permalink($post)),
|
|
]);
|
|
}
|
|
}
|
|
|
|
$priced = function_exists('aksrt_get_user_pay_price')
|
|
? aksrt_get_user_pay_price($post, $user_id)
|
|
: ['amount' => '0.00', 'modo' => '0', 'pay_limit' => 0, 'vip_level' => 0, 'is_free' => true, 'base' => '0.00'];
|
|
|
|
// Free daily quota overbuy: override amount, ignore VIP discount (legacy).
|
|
if ($price_type === 'download_limit_over') {
|
|
$over_minor = $this->parse_asset_minor($meta['download_limit_over_price'] ?? ($pay_modo_meta === 'points' ? 0 : '0'), $pay_modo_meta);
|
|
if ($over_minor <= 0) {
|
|
return new \WP_Error('aksrt_no_overbuy', __('未开放超额下载购买。', 'aksrt'), ['status' => 400]);
|
|
}
|
|
$priced['amount'] = $this->asset_value($over_minor, $pay_modo_meta);
|
|
$priced['base'] = $this->asset_value($over_minor, $pay_modo_meta);
|
|
$priced['is_free'] = false;
|
|
$priced['vip_amount'] = null;
|
|
}
|
|
|
|
$pay_limit = (int) ($priced['pay_limit'] ?? 0);
|
|
$vip_level = (int) ($priced['vip_level'] ?? 0);
|
|
if ($pay_limit > 0 && $vip_level < $pay_limit && $price_type !== 'download_limit_over') {
|
|
return new \WP_Error('aksrt_need_vip', sprintf(
|
|
/* translators: %d: required VIP level */
|
|
__('该资源仅限 VIP%s 及以上会员购买/下载。', 'aksrt'),
|
|
max(1, $pay_limit)
|
|
), [
|
|
'status' => 403,
|
|
'pay_limit' => $pay_limit,
|
|
]);
|
|
}
|
|
|
|
if ($user_id > 0 && class_exists('\AKSRT\Payment\Order') && \AKSRT\Payment\Order::get_instance()->has_paid_access(
|
|
$user_id,
|
|
\AKSRT\Core\DomainModel::ORDER_PRODUCT_RESOURCE,
|
|
$post_id
|
|
)) {
|
|
return new \WP_Error('aksrt_already_paid', __('您已购买该内容。', 'aksrt'), [
|
|
'status' => 200,
|
|
'has_paid_access' => true,
|
|
'reload' => true,
|
|
]);
|
|
}
|
|
if ($user_id <= 0 && function_exists('aksrt_guest_has_paid_access') && aksrt_guest_has_paid_access($post_id)) {
|
|
return new \WP_Error('aksrt_already_paid', __('您已购买该内容。', 'aksrt'), [
|
|
'status' => 200,
|
|
'has_paid_access' => true,
|
|
'reload' => true,
|
|
]);
|
|
}
|
|
|
|
return [
|
|
'post' => $post,
|
|
'meta' => $meta,
|
|
'priced' => $priced,
|
|
];
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @return array<int, array{id:string,name:string}>
|
|
*/
|
|
private function available_payment_methods(string $pay_modo, bool $logged_in = true): array {
|
|
if ($pay_modo === 'points') {
|
|
return [
|
|
['id' => 'points', 'name' => __('积分支付', 'aksrt')],
|
|
];
|
|
}
|
|
|
|
if (function_exists('aksrt_list_cash_payment_methods')) {
|
|
return aksrt_list_cash_payment_methods($logged_in);
|
|
}
|
|
|
|
return $logged_in
|
|
? [['id' => 'balance', 'name' => __('站内余额', 'aksrt')]]
|
|
: [['id' => 'alipay', 'name' => __('支付宝', 'aksrt')]];
|
|
}
|
|
|
|
|
|
|
|
|
|
private function is_coupon_allowed(int $post_id, array $meta): bool {
|
|
if (!empty($meta['coupon_s'])) {
|
|
return true;
|
|
}
|
|
if (function_exists('aksrt_is_content_coupon_allowed')) {
|
|
return aksrt_is_content_coupon_allowed($post_id);
|
|
}
|
|
if (class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'get_content_coupon_settings')) {
|
|
return !empty(\AKSRT\Settings\Settings::get_content_coupon_settings()['enabled']);
|
|
}
|
|
if (class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'get_paid_content_settings')) {
|
|
return !empty(\AKSRT\Settings\Settings::get_paid_content_settings()['coupon_post_s']);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @return array{id:int,ratio:int}
|
|
*/
|
|
private function resolve_referrer_context(int $user_id): array {
|
|
$empty = ['id' => 0, 'ratio' => 0];
|
|
if (!class_exists('\AKSRT\Settings\Settings') || !method_exists('\AKSRT\Settings\Settings', 'get_payment_affiliate_settings')) {
|
|
return $empty;
|
|
}
|
|
$aff = \AKSRT\Settings\Settings::get_payment_affiliate_settings();
|
|
if (empty($aff['enabled'])) {
|
|
return $empty;
|
|
}
|
|
$referrer_id = class_exists('\AKSRT\Payment\ReferralAttribution')
|
|
? \AKSRT\Payment\ReferralAttribution::get_instance()->resolve($user_id)
|
|
: 0;
|
|
if ($referrer_id <= 0) {
|
|
return $empty;
|
|
}
|
|
|
|
$ratio = (int) \AKSRT\Settings\Settings::affiliate_ratio_for_user($referrer_id);
|
|
if ($ratio <= 0) {
|
|
return $empty;
|
|
}
|
|
return ['id' => $referrer_id, 'ratio' => $ratio];
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Cash discount chain: VIP-priced list → 推广折扣 → coupon → payable + commission snapshot.
|
|
*
|
|
* @param array<string,mixed> $priced
|
|
* @param array<string,mixed> $meta
|
|
* @return array|\WP_Error
|
|
*/
|
|
private function apply_cash_discounts(array $priced, \WP_Post $post, array $meta, int $user_id, string $coupon_code = '', bool $soft_coupon = false) {
|
|
$list_minor = $this->parse_asset_minor($priced['amount'] ?? '0', '0');
|
|
$pay_minor = $list_minor;
|
|
$rebate_discount_minor = 0;
|
|
$coupon_discount_minor = 0;
|
|
$coupon_meta = [];
|
|
|
|
$ref = $this->resolve_referrer_context($user_id);
|
|
$referrer_id = (int) ($ref['id'] ?? 0);
|
|
$ratio = max(0, min(100, (int) ($ref['ratio'] ?? 0)));
|
|
|
|
// 1) Buyer-facing 推广折扣 (fixed).
|
|
$fixed_minor = $this->parse_asset_minor($meta['pay_rebate_discount'] ?? '0', '0');
|
|
if ($referrer_id > 0 && $ratio > 0 && $fixed_minor > 0 && $pay_minor > 0) {
|
|
$rebate_discount_minor = min($pay_minor, $fixed_minor);
|
|
$pay_minor -= $rebate_discount_minor;
|
|
}
|
|
|
|
// 2) Coupon after rebate discount.
|
|
$coupon_code = sanitize_text_field($coupon_code);
|
|
if ($coupon_code !== '') {
|
|
if (!$this->is_coupon_allowed((int) $post->ID, $meta)) {
|
|
$err = new \WP_Error('aksrt_coupon_not_allowed', __('当前内容不支持优惠码。', 'aksrt'), ['status' => 400]);
|
|
if (!$soft_coupon) {
|
|
return $err;
|
|
}
|
|
} elseif (!class_exists('\AKSRT\Payment\Coupon')) {
|
|
$err = new \WP_Error('aksrt_coupon_unavailable', __('优惠券系统不可用。', 'aksrt'), ['status' => 500]);
|
|
if (!$soft_coupon) {
|
|
return $err;
|
|
}
|
|
} else {
|
|
$result = \AKSRT\Payment\Coupon::get_instance()->validate(
|
|
$coupon_code,
|
|
\AKSRT\Ledger\AssetAmount::decimal_from_minor($pay_minor),
|
|
[
|
|
'user_id' => max(0, $user_id),
|
|
'product_ids' => [(int) $post->ID],
|
|
'context' => 'content',
|
|
]
|
|
);
|
|
if (empty($result['valid'])) {
|
|
$err = new \WP_Error(
|
|
'aksrt_coupon_error',
|
|
(string) ($result['message'] ?? __('优惠码不可用。', 'aksrt')),
|
|
['status' => 400]
|
|
);
|
|
if (!$soft_coupon) {
|
|
return $err;
|
|
}
|
|
} else {
|
|
$coupon_discount_minor = min($pay_minor, max(0, (int) ($result['discount_minor'] ?? 0)));
|
|
$pay_minor -= $coupon_discount_minor;
|
|
$c = is_array($result['coupon'] ?? null) ? $result['coupon'] : [];
|
|
$coupon_meta = [
|
|
'id' => (int) ($c['id'] ?? 0),
|
|
'code' => (string) ($c['code'] ?? $coupon_code),
|
|
'type' => (string) ($c['type'] ?? ''),
|
|
'value' => $c['value'] ?? 0,
|
|
'discount' => \AKSRT\Ledger\AssetAmount::decimal_from_minor($coupon_discount_minor),
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
$payable_minor = max(0, $pay_minor);
|
|
$rebate_price_minor = 0;
|
|
if ($referrer_id > 0 && $ratio > 0 && $payable_minor > 0) {
|
|
$rebate_price_minor = \AKSRT\Ledger\AssetAmount::percentage($payable_minor, $ratio);
|
|
}
|
|
|
|
return [
|
|
'list_amount' => \AKSRT\Ledger\AssetAmount::decimal_from_minor($list_minor),
|
|
'list_minor' => $list_minor,
|
|
'rebate_discount' => \AKSRT\Ledger\AssetAmount::decimal_from_minor($rebate_discount_minor),
|
|
'rebate_discount_minor' => $rebate_discount_minor,
|
|
'coupon_discount' => \AKSRT\Ledger\AssetAmount::decimal_from_minor($coupon_discount_minor),
|
|
'coupon_discount_minor' => $coupon_discount_minor,
|
|
'coupon' => $coupon_meta,
|
|
'payable' => \AKSRT\Ledger\AssetAmount::decimal_from_minor($payable_minor),
|
|
'payable_minor' => $payable_minor,
|
|
'referrer_id' => $referrer_id,
|
|
'rebate_price' => \AKSRT\Ledger\AssetAmount::decimal_from_minor($rebate_price_minor),
|
|
'rebate_price_minor' => $rebate_price_minor,
|
|
'ratio' => $ratio,
|
|
];
|
|
}
|
|
|
|
|
|
|
|
private function format_amount_label(int $minor, string $pay_modo, string $currency): string {
|
|
if ($minor <= 0) {
|
|
return (string) __('免费', 'aksrt');
|
|
}
|
|
$amount = $this->asset_value($minor, $pay_modo);
|
|
$n = function_exists('aksrt_format_price') ? aksrt_format_price($amount) : (string) $amount;
|
|
if ($pay_modo === 'points') {
|
|
return $n . ' ' . $currency;
|
|
}
|
|
return $currency . $n;
|
|
}
|
|
|
|
private function parse_asset_minor($value, string $pay_modo): int {
|
|
if ($pay_modo === 'points') {
|
|
if (is_int($value)) {
|
|
return max(0, $value);
|
|
}
|
|
if (!is_string($value) || !preg_match('/^(0|[1-9]\d*)$/', trim($value))) {
|
|
return 0;
|
|
}
|
|
$value = trim($value);
|
|
if (strlen($value) > strlen((string) PHP_INT_MAX)
|
|
|| (strlen($value) === strlen((string) PHP_INT_MAX) && strcmp($value, (string) PHP_INT_MAX) > 0)
|
|
) {
|
|
return 0;
|
|
}
|
|
return \AKSRT\Ledger\AssetAmount::points((int) $value)->minor();
|
|
}
|
|
|
|
if (!is_string($value)) {
|
|
return 0;
|
|
}
|
|
try {
|
|
return \AKSRT\Ledger\AssetAmount::balance_minor($value);
|
|
} catch (\InvalidArgumentException $e) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
private function asset_value(int $minor, string $pay_modo) {
|
|
return $pay_modo === 'points'
|
|
? $minor
|
|
: \AKSRT\Ledger\AssetAmount::decimal_from_minor($minor);
|
|
}
|
|
|
|
}
|