balance_enabled()) { return false; } if (class_exists('\AKSRT\Settings\PaymentRuntime')) { return \AKSRT\Settings\PaymentRuntime::is_gateway_enabled('balance'); } return true; } public function create_payment(array $order): array { $user_id = (int) ($order['user_id'] ?? 0); $order_number = (string) ($order['order_number'] ?? ''); if (!$this->balance_enabled()) { return ['success' => false, 'message' => __('余额功能未启用', 'aksrt')]; } if ($user_id <= 0) { return ['success' => false, 'message' => __('请先登录', 'aksrt')]; } global $wpdb; $table = \AKSRT\Core\DBMigrator::table('order'); $amount = $wpdb->get_var($wpdb->prepare( "SELECT amount FROM {$table} WHERE order_number = %s", $order_number )); try { $amount_minor = \AKSRT\Ledger\AssetAmount::balance(is_string($amount) ? $amount : '')->minor(); } catch (\InvalidArgumentException $e) { return ['success' => false, 'message' => __('支付金额无效', 'aksrt')]; } $transaction_code = 'BAL_' . $order_number; $guard = static function () use ($order_number, $user_id, $amount_minor) { global $wpdb; $table = \AKSRT\Core\DBMigrator::table('order'); $current = $wpdb->get_row($wpdb->prepare( "SELECT user_id, amount, status FROM {$table} WHERE order_number = %s FOR UPDATE", $order_number ), ARRAY_A); try { $current_minor = is_array($current) ? \AKSRT\Ledger\AssetAmount::balance((string) ($current['amount'] ?? ''))->minor() : -1; } catch (\InvalidArgumentException $e) { $current_minor = -1; } if (!is_array($current) || (int) ($current['user_id'] ?? 0) !== $user_id || $current_minor !== $amount_minor || (string) ($current['status'] ?? '') !== Order::STATUS_PENDING ) { return new \WP_Error('aksrt_order_confirm_failed', __('订单状态或金额已变化', 'aksrt'), ['status' => 409]); } return true; }; $result = \AKSRT\Ledger\LedgerService::get_instance()->debit( $user_id, \AKSRT\Ledger\AssetType::BALANCE, $amount_minor, \AKSRT\Ledger\OperationKey::deterministic('order-payment', [$order_number]), 'order_payment', ['type' => 'order', 'key' => $order_number], sprintf(__('订单 %s 支付', 'aksrt'), $order_number), $user_id, ['order_number' => $order_number], static function () use ($order_number, $transaction_code, $order) { $paid = OrderRepository::get_instance()->update_status($order_number, Order::STATUS_PAID, [ 'payment_method' => 'balance', 'payment_transaction_id' => $transaction_code, 'note' => (string) ($order['_ledger_paid_note'] ?? __('余额支付成功', 'aksrt')), 'require_transition' => true, ]); return $paid ? true : new \WP_Error('aksrt_order_confirm_failed', __('订单状态确认失败', 'aksrt'), ['status' => 409]); }, $guard ); if (is_wp_error($result)) { $error_data = $result->get_error_data(); return [ 'success' => false, 'code' => $result->get_error_code(), 'status' => is_array($error_data) ? (int) ($error_data['status'] ?? 500) : 500, 'message' => $result->get_error_message() ?: __('扣款失败', 'aksrt'), ]; } return [ 'success' => true, 'transaction_id' => $transaction_code, 'ledger_transaction_id' => $result->transaction_id, 'flow_number' => $result->flow_number, 'replayed' => $result->replayed, 'order_committed' => true, 'message' => __('余额支付成功', 'aksrt'), ]; } public function verify_callback(\WP_REST_Request $request): array { // Balance payment is synchronous — no callback needed return ['success' => false, 'message' => __('余额支付无需回调验证', 'aksrt')]; } public function query_status(string $order_number): array { $order_repo = OrderRepository::get_instance(); $order = $order_repo->get($order_number); if (!$order) { return ['success' => false, 'message' => __('订单不存在', 'aksrt')]; } return [ 'success' => true, 'status' => $order['status'], 'transaction_id' => $order['payment_transaction_id'] ?? '', ]; } public function refund(string $order_number, string $amount, string $request_id = ''): array { $order_repo = OrderRepository::get_instance(); $order = $order_repo->get($order_number); if (!$order) { return ['success' => false, 'message' => __('订单不存在', 'aksrt')]; } $user_id = (int) $order['user_id']; if (!$this->balance_enabled()) { return ['success' => false, 'message' => __('余额功能未启用', 'aksrt')]; } $request_id = sanitize_text_field($request_id); try { $amount_minor = \AKSRT\Ledger\AssetAmount::balance($amount)->minor(); if ($amount_minor <= 0) { return ['success' => false, 'message' => __('退款金额无效', 'aksrt')]; } $operation_key = \AKSRT\Ledger\OperationKey::deterministic('order-refund', [$request_id !== '' ? $request_id : $order_number]); $history = \AKSRT\Ledger\LedgerQuery::get_instance()->history(['operation_key' => $operation_key, 'page' => 1, 'per_page' => 1]); if (!empty($history['items'][0])) { $committed = $history['items'][0]; $refund_entry = null; foreach ((array) ($committed['entries'] ?? []) as $entry) { if ((int) ($entry['user_id'] ?? 0) === $user_id && (string) ($entry['asset'] ?? '') === \AKSRT\Ledger\AssetType::BALANCE->value && (string) ($entry['entry_type'] ?? '') === 'refund' ) { $refund_entry = $entry; break; } } if ((string) ($committed['reason'] ?? '') !== 'order_refund' || (string) ($committed['ref_type'] ?? '') !== 'order' || (string) ($committed['ref_key'] ?? '') !== $order_number || !is_array($refund_entry) || (int) ($refund_entry['delta_minor'] ?? 0) !== $amount_minor ) { return ['success' => false, 'message' => __('相同退款标识已用于其他请求', 'aksrt')]; } return [ 'success' => true, 'refund_id' => 'BAL_REF_' . $order_number, 'transaction_id' => (int) ($committed['id'] ?? 0), 'ledger_transaction_id' => (int) ($committed['id'] ?? 0), 'replayed' => true, 'balance' => \AKSRT\Ledger\LedgerService::get_instance()->balance_decimal($user_id), 'message' => __('退款成功', 'aksrt'), ]; } $commission = CommissionLedger::get_instance(); $commission_plan = $commission->prepare_full_refund($order_number); if (is_wp_error($commission_plan)) { return ['success' => false, 'message' => $commission_plan->get_error_message()]; } $guard = static function () use ($order_number, $user_id, $amount_minor, $commission, $commission_plan) { global $wpdb; $table = \AKSRT\Core\DBMigrator::table('order'); $current = $wpdb->get_row($wpdb->prepare( "SELECT user_id, amount, payment_method, status FROM {$table} WHERE order_number = %s FOR UPDATE", $order_number ), ARRAY_A); try { $order_minor = is_array($current) ? \AKSRT\Ledger\AssetAmount::balance((string) ($current['amount'] ?? ''))->minor() : -1; } catch (\InvalidArgumentException $e) { $order_minor = -1; } if (!is_array($current) || (int) ($current['user_id'] ?? 0) !== $user_id || sanitize_key((string) ($current['payment_method'] ?? '')) !== 'balance' || $order_minor !== $amount_minor || !in_array((string) ($current['status'] ?? ''), [Order::STATUS_PAID, Order::STATUS_REFUNDING], true) ) { return new \WP_Error('aksrt_order_refund_conflict', __('退款订单状态或金额已变化', 'aksrt'), ['status' => 409]); } return $commission->guard_full_refund($commission_plan); }; $mutation = static function () use ($order_number, $commission, $commission_plan) { $repo = OrderRepository::get_instance(); $current = $repo->get($order_number); if (!is_array($current)) { return new \WP_Error('aksrt_order_refund_failed', __('退款订单不存在', 'aksrt'), ['status' => 404]); } if ((string) ($current['status'] ?? '') !== Order::STATUS_REFUNDING && !$repo->update_status($order_number, Order::STATUS_REFUNDING, ['note' => __('余额退款处理中', 'aksrt')])) { return new \WP_Error('aksrt_order_refund_failed', __('退款状态更新失败', 'aksrt'), ['status' => 409]); } if (!$repo->update_status($order_number, Order::STATUS_REFUNDED, ['note' => __('余额退款已完成', 'aksrt')])) { return new \WP_Error('aksrt_order_refund_failed', __('退款状态更新失败', 'aksrt'), ['status' => 409]); } return $commission->apply_full_refund($commission_plan); }; $entries = [[ 'user_id' => $user_id, 'asset' => \AKSRT\Ledger\AssetType::BALANCE->value, 'delta_minor' => $amount_minor, 'entry_type' => 'refund', 'remark' => sprintf(__('订单 %s 退款', 'aksrt'), $order_number), ]]; if (!empty($commission_plan['active']) && (string) ($commission_plan['status'] ?? '') === 'settled') { $entries[] = [ 'user_id' => (int) $commission_plan['promoter_id'], 'asset' => \AKSRT\Ledger\AssetType::BALANCE->value, 'delta_minor' => -(int) $commission_plan['amount_minor'], 'entry_type' => 'commission_reversal', 'counterparty_user_id' => $user_id, 'remark' => sprintf(__('订单 %s 返佣退款冲正', 'aksrt'), $order_number), ]; } $result = \AKSRT\Ledger\LedgerService::get_instance()->post(new \AKSRT\Ledger\LedgerCommand( $operation_key, 'order_refund', $entries, 'order', $order_number, 0, ['request_id' => $request_id, 'commission_id' => (int) ($commission_plan['id'] ?? 0)] ), $guard, $mutation); if (is_wp_error($result)) { return ['success' => false, 'message' => $result->get_error_message() ?: __('退款失败', 'aksrt')]; } return [ 'success' => true, 'refund_id' => 'BAL_REF_' . $order_number, 'transaction_id' => $result->transaction_id, 'ledger_transaction_id' => $result->transaction_id, 'flow_number' => $result->flow_number, 'replayed' => $result->replayed, 'balance' => \AKSRT\Ledger\LedgerService::get_instance()->balance_decimal($user_id), 'message' => __('退款成功', 'aksrt'), ]; } catch (\Throwable $e) { return ['success' => false, 'message' => $e->getMessage()]; } } private function balance_enabled(): bool { if (!class_exists('\AKSRT\Settings\Settings')) { return false; } $settings = \AKSRT\Settings\Settings::get_payment_recharge_settings(); return !empty($settings['enabled']); } }