<?php

function cmsEnsurePaymentEventTable($db)
{
    return true;
}

function cmsPaymentNormalizeAmount($amount)
{
    $amount = trim((string) $amount);
    if (!preg_match('/^\d+(?:\.\d{1,2})?$/', $amount)) { return null; }
    return number_format((float) $amount, 2, '.', '');
}

function cmsPaymentAmountMatches($expected, $actual)
{
    $expected = cmsPaymentNormalizeAmount($expected);
    $actual = cmsPaymentNormalizeAmount($actual);
    return $expected !== null && $actual !== null && hash_equals($expected, $actual);
}

function cmsPaymentAmountToCents($amount)
{
    $normalized = cmsPaymentNormalizeAmount($amount);
    if ($normalized === null) { return null; }
    list($whole, $fraction) = explode('.', $normalized, 2);
    if (strlen($whole) > 12) { return null; }
    return ((int) $whole * 100) + (int) $fraction;
}

function cmsPaymentSiteUrl($db)
{
    $row = $db->fetch("SELECT config_value FROM {$db->table('config')} WHERE config_key = 'site_url' LIMIT 1");
    $url = rtrim(trim((string) ($row['config_value'] ?? '')), '/');
    $parts = parse_url($url);
    if (!$parts || !in_array(strtolower((string) ($parts['scheme'] ?? '')), ['http', 'https'], true) || empty($parts['host'])
        || isset($parts['user']) || isset($parts['pass']) || isset($parts['query']) || isset($parts['fragment'])) {
        return '';
    }
    return $url;
}

function cmsPaymentCallbackUrl($db, $action, $requireHttps = false)
{
    $siteUrl = cmsPaymentSiteUrl($db);
    if ($siteUrl === '' || ($requireHttps && stripos($siteUrl, 'https://') !== 0)) { return ''; }
    return $siteUrl . '/api/payment.php?action=' . rawurlencode((string) $action);
}

function cmsGetWechatPaymentConfig($db)
{
    $rows = $db->fetchAll(
        "SELECT config_key, config_value FROM {$db->table('config')} WHERE config_key LIKE 'wechat_%' OR config_key = 'site_url'"
    );
    $values = [];
    foreach ($rows as $row) { $values[$row['config_key']] = (string) $row['config_value']; }
    return [
        'enabled' => ($values['wechat_enabled'] ?? '0') === '1',
        'appid' => trim($values['wechat_appid'] ?? ''),
        'mchid' => trim($values['wechat_mchid'] ?? ''),
        'api_v3_key' => trim($values['wechat_apikey'] ?? ''),
        'merchant_private_key' => trim($values['wechat_sslkey'] ?? ''),
        'merchant_serial_no' => strtoupper(trim($values['wechat_serial_no'] ?? '')),
        'platform_certificate' => trim($values['wechat_platform_cert'] ?? ''),
        'notify_url' => cmsPaymentCallbackUrl($db, 'wechat_notify', true),
    ];
}

function cmsWechatPrivateKeyPem($key)
{
    $key = trim((string) $key);
    if ($key === '' || strpos($key, 'BEGIN') !== false) { return $key; }
    return "-----BEGIN PRIVATE KEY-----\n" . chunk_split(preg_replace('/\s+/', '', $key), 64, "\n") . "-----END PRIVATE KEY-----";
}

function cmsWechatCertificatePem($certificate)
{
    $certificate = trim((string) $certificate);
    if ($certificate === '' || strpos($certificate, 'BEGIN CERTIFICATE') !== false) { return $certificate; }
    return "-----BEGIN CERTIFICATE-----\n" . chunk_split(preg_replace('/\s+/', '', $certificate), 64, "\n") . "-----END CERTIFICATE-----";
}

function cmsWechatNormalizeSerial($serial)
{
    $serial = strtoupper(preg_replace('/[^0-9A-F]/i', '', (string) $serial));
    return ltrim($serial, '0') ?: '0';
}

function cmsWechatCertificateSerial($certificate)
{
    $parsed = openssl_x509_parse(cmsWechatCertificatePem($certificate));
    if (!is_array($parsed) || empty($parsed['serialNumberHex'])) { return ''; }
    return cmsWechatNormalizeSerial($parsed['serialNumberHex']);
}

function cmsWechatCertificateIsCurrent($certificate, $now = null)
{
    $parsed = openssl_x509_parse(cmsWechatCertificatePem($certificate));
    $now = $now === null ? time() : (int) $now;
    return is_array($parsed)
        && isset($parsed['validFrom_time_t'], $parsed['validTo_time_t'])
        && $now >= (int) $parsed['validFrom_time_t']
        && $now <= (int) $parsed['validTo_time_t'];
}

function cmsWechatBuildAuthorization(array $config, $method, $requestPath, $body, $timestamp = null, $nonce = null)
{
    $method = strtoupper(trim((string) $method));
    $requestPath = trim((string) $requestPath);
    $timestamp = $timestamp === null ? time() : (int) $timestamp;
    $nonce = $nonce === null ? bin2hex(random_bytes(16)) : (string) $nonce;
    $mchid = trim((string) ($config['mchid'] ?? ''));
    $serial = strtoupper(trim((string) ($config['merchant_serial_no'] ?? '')));
    if (!preg_match('/^[A-Z]+$/', $method) || strpos($requestPath, '/') !== 0) {
        throw new InvalidArgumentException('微信支付请求参数无效');
    }
    if (!preg_match('/^\d{6,32}$/', $mchid) || !preg_match('/^[0-9A-F]+$/', $serial)) {
        throw new InvalidArgumentException('微信支付商户号或证书序列号无效');
    }
    $privateKey = openssl_pkey_get_private(cmsWechatPrivateKeyPem($config['merchant_private_key'] ?? ''));
    if ($privateKey === false) { throw new InvalidArgumentException('微信支付商户私钥无效'); }

    $message = $method . "\n" . $requestPath . "\n" . $timestamp . "\n" . $nonce . "\n" . $body . "\n";
    if (!openssl_sign($message, $signature, $privateKey, OPENSSL_ALGO_SHA256)) {
        throw new RuntimeException('微信支付请求签名失败');
    }
    return sprintf(
        'WECHATPAY2-SHA256-RSA2048 mchid="%s",serial_no="%s",timestamp="%d",nonce_str="%s",signature="%s"',
        $mchid,
        $serial,
        $timestamp,
        $nonce,
        base64_encode($signature)
    );
}

function cmsCreateWechatNativePayment(array $config, $outTradeNo, $amount, $description)
{
    if (empty($config['enabled'])) { throw new RuntimeException('微信支付未启用'); }
    foreach (['appid', 'mchid', 'api_v3_key', 'merchant_private_key', 'merchant_serial_no', 'platform_certificate', 'notify_url'] as $key) {
        if (trim((string) ($config[$key] ?? '')) === '') { throw new RuntimeException('微信支付配置不完整：' . $key); }
    }
    if (strlen((string) $config['api_v3_key']) !== 32) { throw new RuntimeException('微信支付 APIv3 密钥必须为 32 字节'); }
    if (stripos((string) $config['notify_url'], 'https://') !== 0) { throw new RuntimeException('微信支付回调地址必须使用 HTTPS'); }
    if (cmsWechatCertificateSerial($config['platform_certificate']) === '' || !cmsWechatCertificateIsCurrent($config['platform_certificate'])) {
        throw new RuntimeException('微信支付平台证书无效或已过期');
    }
    $amountCents = cmsPaymentAmountToCents($amount);
    if ($amountCents === null || $amountCents <= 0) { throw new InvalidArgumentException('支付金额无效'); }
    $outTradeNo = trim((string) $outTradeNo);
    if (!preg_match('/^[0-9A-Za-z_-]{6,32}$/', $outTradeNo)) { throw new InvalidArgumentException('商户订单号无效'); }

    $path = '/v3/pay/transactions/native';
    $description = trim((string) $description);
    $description = function_exists('mb_substr') ? mb_substr($description, 0, 127, 'UTF-8') : substr($description, 0, 127);
    $payload = [
        'appid' => (string) $config['appid'],
        'mchid' => (string) $config['mchid'],
        'description' => $description,
        'out_trade_no' => $outTradeNo,
        'notify_url' => (string) $config['notify_url'],
        'amount' => ['total' => $amountCents, 'currency' => 'CNY'],
    ];
    $body = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);
    $authorization = cmsWechatBuildAuthorization($config, 'POST', $path, $body);

    $ch = curl_init('https://api.mch.weixin.qq.com' . $path);
    $options = [
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => $body,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2,
        CURLOPT_HTTPHEADER => [
            'Accept: application/json',
            'Content-Type: application/json',
            'Authorization: ' . $authorization,
            'User-Agent: EnterpriseCMS-WechatPay/1.0',
        ],
    ];
    if (defined('CURLOPT_PROTOCOLS') && defined('CURLPROTO_HTTPS')) { $options[CURLOPT_PROTOCOLS] = CURLPROTO_HTTPS; }
    curl_setopt_array($ch, $options);
    $response = curl_exec($ch);
    $curlError = curl_error($ch);
    $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    if ($response === false) { throw new RuntimeException('微信支付网络请求失败：' . $curlError); }
    $result = json_decode($response, true);
    if ($httpCode !== 200 || !is_array($result) || empty($result['code_url'])) {
        $message = is_array($result) ? ($result['message'] ?? '未知错误') : '响应格式无效';
        throw new RuntimeException('微信支付下单失败：' . $message);
    }
    return ['code_url' => (string) $result['code_url']];
}

function cmsWechatNotificationHeaders(array $server)
{
    return [
        'wechatpay-timestamp' => (string) ($server['HTTP_WECHATPAY_TIMESTAMP'] ?? ''),
        'wechatpay-nonce' => (string) ($server['HTTP_WECHATPAY_NONCE'] ?? ''),
        'wechatpay-signature' => (string) ($server['HTTP_WECHATPAY_SIGNATURE'] ?? ''),
        'wechatpay-serial' => (string) ($server['HTTP_WECHATPAY_SERIAL'] ?? ''),
    ];
}

function cmsVerifyWechatNotification($rawBody, array $headers, array $config, $now = null)
{
    $normalizedHeaders = [];
    foreach ($headers as $key => $value) { $normalizedHeaders[strtolower((string) $key)] = trim((string) $value); }
    foreach (['wechatpay-timestamp', 'wechatpay-nonce', 'wechatpay-signature', 'wechatpay-serial'] as $key) {
        if (($normalizedHeaders[$key] ?? '') === '') { return ['success' => false, 'message' => '微信支付通知头不完整']; }
    }
    $timestamp = $normalizedHeaders['wechatpay-timestamp'];
    $now = $now === null ? time() : (int) $now;
    if (!ctype_digit($timestamp) || abs($now - (int) $timestamp) > 300) {
        return ['success' => false, 'message' => '微信支付通知已过期'];
    }
    if (strlen((string) ($config['api_v3_key'] ?? '')) !== 32) {
        return ['success' => false, 'message' => '微信支付 APIv3 密钥无效'];
    }
    $certificate = cmsWechatCertificatePem($config['platform_certificate'] ?? '');
    $certificateSerial = cmsWechatCertificateSerial($certificate);
    if ($certificateSerial === '' || !cmsWechatCertificateIsCurrent($certificate, $now)
        || !hash_equals($certificateSerial, cmsWechatNormalizeSerial($normalizedHeaders['wechatpay-serial']))) {
        return ['success' => false, 'message' => '微信支付平台证书序列号不匹配'];
    }
    $signature = base64_decode($normalizedHeaders['wechatpay-signature'], true);
    if ($signature === false) { return ['success' => false, 'message' => '微信支付通知签名格式无效']; }
    $message = $timestamp . "\n" . $normalizedHeaders['wechatpay-nonce'] . "\n" . $rawBody . "\n";
    if (openssl_verify($message, $signature, $certificate, OPENSSL_ALGO_SHA256) !== 1) {
        return ['success' => false, 'message' => '微信支付通知验签失败'];
    }
    $envelope = json_decode($rawBody, true);
    if (!is_array($envelope) || empty($envelope['resource']) || !is_array($envelope['resource'])) {
        return ['success' => false, 'message' => '微信支付通知格式无效'];
    }
    return ['success' => true, 'message' => '验签通过', 'data' => $envelope];
}

function cmsDecryptWechatResource(array $resource, $apiV3Key)
{
    if (($resource['algorithm'] ?? 'AEAD_AES_256_GCM') !== 'AEAD_AES_256_GCM' || strlen((string) $apiV3Key) !== 32) {
        return false;
    }
    $ciphertext = base64_decode((string) ($resource['ciphertext'] ?? ''), true);
    $nonce = (string) ($resource['nonce'] ?? '');
    $associatedData = (string) ($resource['associated_data'] ?? '');
    if ($ciphertext === false || strlen($ciphertext) <= 16 || strlen($nonce) !== 12) { return false; }
    $tag = substr($ciphertext, -16);
    $plaintext = openssl_decrypt(
        substr($ciphertext, 0, -16),
        'aes-256-gcm',
        $apiV3Key,
        OPENSSL_RAW_DATA,
        $nonce,
        $tag,
        $associatedData
    );
    if ($plaintext === false) { return false; }
    $data = json_decode($plaintext, true);
    return is_array($data) ? $data : false;
}

function cmsVerifyAlipayNotification(array $params, array $config)
{
    foreach (['sign', 'sign_type', 'app_id', 'out_trade_no', 'trade_no', 'total_amount', 'trade_status'] as $key) {
        if (trim((string) ($params[$key] ?? '')) === '') {
            return ['success' => false, 'message' => '支付宝通知缺少字段：' . $key];
        }
    }
    if (($params['sign_type'] ?? '') !== 'RSA2') {
        return ['success' => false, 'message' => '支付宝通知签名类型无效'];
    }
    if (empty($config['app_id']) || !hash_equals((string) $config['app_id'], (string) $params['app_id'])) {
        return ['success' => false, 'message' => '支付宝通知 AppID 不匹配'];
    }
    $notifiedAmount = cmsPaymentNormalizeAmount($params['total_amount']);
    if ($notifiedAmount === null || (float) $notifiedAmount <= 0) {
        return ['success' => false, 'message' => '支付宝通知金额无效'];
    }

    require_once dirname(__DIR__) . '/alipay/AopSdk.php';
    require_once dirname(__DIR__) . '/alipay/pagepay/service/AlipayTradeService.php';
    try {
        $service = new AlipayTradeService($config);
        if (!$service->check($params)) {
            return ['success' => false, 'message' => '支付宝通知验签失败'];
        }
    } catch (Throwable $e) {
        error_log('Alipay notification verification failed: ' . $e->getMessage());
        return ['success' => false, 'message' => '支付宝通知验签异常'];
    }
    return ['success' => true, 'message' => '验签通过'];
}

function cmsPaymentEventExists($db, $transactionId, $businessType, $outTradeNo, $provider = 'alipay')
{
    $event = $db->fetch(
        "SELECT business_type, out_trade_no FROM {$db->table('payment_event')} WHERE provider = ? AND transaction_id = ? LIMIT 1",
        [$provider, $transactionId]
    );
    return $event
        && hash_equals((string) $event['business_type'], (string) $businessType)
        && hash_equals((string) $event['out_trade_no'], (string) $outTradeNo);
}

function cmsInsertWechatPaymentEvent($db, array $transaction, $businessType, $businessId, $payloadHash)
{
    $amountCents = (int) ($transaction['amount']['total'] ?? 0);
    return $db->insert('payment_event', [
        'provider' => 'wechat',
        'event_type' => (string) ($transaction['trade_state'] ?? ''),
        'business_type' => $businessType,
        'business_id' => (int) $businessId,
        'out_trade_no' => (string) ($transaction['out_trade_no'] ?? ''),
        'transaction_id' => (string) ($transaction['transaction_id'] ?? ''),
        'amount' => sprintf('%d.%02d', intdiv($amountCents, 100), $amountCents % 100),
        'payload_hash' => (string) $payloadHash,
        'status' => 'success',
        'create_time' => time(),
        'processed_time' => time(),
    ]);
}

function cmsProcessWechatNotification($db, $rawBody, array $headers, array $config)
{
    $verified = cmsVerifyWechatNotification($rawBody, $headers, $config);
    if (!$verified['success']) { return $verified; }
    $transaction = cmsDecryptWechatResource($verified['data']['resource'], $config['api_v3_key'] ?? '');
    if ($transaction === false) { return ['success' => false, 'message' => '微信支付通知解密失败']; }
    foreach (['appid', 'mchid', 'out_trade_no', 'transaction_id', 'trade_state'] as $key) {
        if (trim((string) ($transaction[$key] ?? '')) === '') { return ['success' => false, 'message' => '微信支付交易字段不完整']; }
    }
    if (!hash_equals((string) ($config['appid'] ?? ''), (string) $transaction['appid'])
        || !hash_equals((string) ($config['mchid'] ?? ''), (string) $transaction['mchid'])) {
        return ['success' => false, 'message' => '微信支付商户信息不匹配'];
    }
    if ($transaction['trade_state'] !== 'SUCCESS') { return ['success' => true, 'message' => '无需处理的交易状态']; }
    $amountCents = $transaction['amount']['total'] ?? null;
    if (!is_int($amountCents) || $amountCents <= 0 || ($transaction['amount']['currency'] ?? 'CNY') !== 'CNY') {
        return ['success' => false, 'message' => '微信支付金额无效'];
    }

    cmsEnsurePaymentEventTable($db);
    $outTradeNo = (string) $transaction['out_trade_no'];
    $transactionId = (string) $transaction['transaction_id'];
    $payloadHash = hash('sha256', (string) $rawBody);
    $db->beginTransaction();
    try {
        $recharge = $db->fetch("SELECT * FROM {$db->table('recharge')} WHERE trade_no = ? FOR UPDATE", [$outTradeNo]);
        if ($recharge) {
            if (cmsPaymentEventExists($db, $transactionId, 'recharge', $outTradeNo, 'wechat')) {
                $db->commit();
                return ['success' => true, 'message' => '充值通知已处理'];
            }
            if ((string) $recharge['pay_type'] !== 'wechat' || cmsPaymentAmountToCents($recharge['amount']) !== $amountCents) {
                throw new UnexpectedValueException('微信支付充值记录不匹配');
            }
            if ((int) $recharge['status'] === 1) {
                $db->commit();
                return ['success' => true, 'message' => '充值已入账'];
            }
            if ((int) $recharge['status'] !== 0) { throw new UnexpectedValueException('充值状态不允许入账'); }
            cmsInsertWechatPaymentEvent($db, $transaction, 'recharge', (int) $recharge['id'], $payloadHash);
            if ($db->update('recharge', ['status' => 1], 'id = ? AND status = 0', [(int) $recharge['id']]) !== 1) {
                throw new UnexpectedValueException('充值记录已被处理');
            }
            $updated = $db->query("UPDATE {$db->table('member')} SET balance = balance + ? WHERE id = ?", [$recharge['amount'], (int) $recharge['member_id']]);
            if ($updated->rowCount() !== 1) { throw new UnexpectedValueException('充值会员不存在'); }
            $member = $db->fetch("SELECT balance FROM {$db->table('member')} WHERE id = ?", [(int) $recharge['member_id']]);
            $now = time();
            $db->insert('balance_log', [
                'member_id' => (int) $recharge['member_id'],
                'type' => 'recharge',
                'amount' => $recharge['amount'],
                'balance' => $member['balance'] ?? 0,
                'note' => '微信充值：' . $transactionId,
                'remark' => '微信充值：' . $transactionId,
                'add_time' => $now,
                'create_time' => $now,
            ]);
            $db->commit();
            return ['success' => true, 'message' => '充值入账成功'];
        }

        $order = $db->fetch("SELECT * FROM {$db->table('order')} WHERE order_sn = ? FOR UPDATE", [$outTradeNo]);
        if (!$order) { throw new UnexpectedValueException('支付业务不存在'); }
        if (cmsPaymentEventExists($db, $transactionId, 'order', $outTradeNo, 'wechat')) {
            $db->commit();
            return ['success' => true, 'message' => '订单通知已处理'];
        }
        $expectedAmount = $order['pay_price'] ?? $order['total_price'] ?? $order['total_amount'] ?? 0;
        if (cmsPaymentAmountToCents($expectedAmount) !== $amountCents) { throw new UnexpectedValueException('微信支付金额与订单不一致'); }
        if ((int) $order['status'] === 1) {
            $db->commit();
            return ['success' => true, 'message' => '订单已支付'];
        }
        if ((int) $order['status'] !== 0) { throw new UnexpectedValueException('订单状态不允许支付'); }
        cmsInsertWechatPaymentEvent($db, $transaction, 'order', (int) $order['id'], $payloadHash);
        $updated = $db->update('order', [
            'status' => 1,
            'pay_price' => cmsPaymentNormalizeAmount($expectedAmount),
            'pay_type' => 1,
            'pay_time' => time(),
            'update_time' => time(),
        ], 'id = ? AND status = 0', [(int) $order['id']]);
        if ($updated !== 1) { throw new UnexpectedValueException('订单已被处理'); }
        $db->commit();
        return ['success' => true, 'message' => '订单支付成功'];
    } catch (Throwable $e) {
        if ($db->getPdo()->inTransaction()) { $db->rollBack(); }
        if (cmsPaymentEventExists($db, $transactionId, 'recharge', $outTradeNo, 'wechat')
            || cmsPaymentEventExists($db, $transactionId, 'order', $outTradeNo, 'wechat')) {
            return ['success' => true, 'message' => '微信支付通知已处理'];
        }
        error_log('Wechat payment processing failed: ' . $e->getMessage());
        return ['success' => false, 'message' => $e->getMessage()];
    }
}

function cmsInsertPaymentEvent($db, array $params, $businessType, $businessId)
{
    return $db->insert('payment_event', [
        'provider' => 'alipay',
        'event_type' => (string) $params['trade_status'],
        'business_type' => $businessType,
        'business_id' => intval($businessId),
        'out_trade_no' => (string) $params['out_trade_no'],
        'transaction_id' => (string) $params['trade_no'],
        'amount' => cmsPaymentNormalizeAmount($params['total_amount']),
        'payload_hash' => hash('sha256', json_encode($params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)),
        'status' => 'success',
        'create_time' => time(),
        'processed_time' => time(),
    ]);
}

function cmsMarkRechargePaidFromVerifiedQuery($db, $tradeNo)
{
    $tradeNo = trim((string) $tradeNo);
    if ($tradeNo === '') { return false; }
    $db->beginTransaction();
    try {
        $recharge = $db->fetch("SELECT * FROM {$db->table('recharge')} WHERE trade_no = ? FOR UPDATE", [$tradeNo]);
        if (!$recharge) { throw new UnexpectedValueException('充值记录不存在'); }
        if (intval($recharge['status']) === 1) {
            $db->commit();
            return true;
        }
        $updated = $db->update('recharge', ['status' => 1], 'id = ? AND status = 0', [intval($recharge['id'])]);
        if ($updated !== 1) { throw new UnexpectedValueException('充值记录已被处理'); }
        $balanceUpdated = $db->query(
            "UPDATE {$db->table('member')} SET balance = balance + ? WHERE id = ?",
            [$recharge['amount'], intval($recharge['member_id'])]
        );
        if ($balanceUpdated->rowCount() !== 1) { throw new UnexpectedValueException('充值会员不存在'); }
        $balance = $db->fetch("SELECT balance FROM {$db->table('member')} WHERE id = ?", [intval($recharge['member_id'])])['balance'] ?? 0;
        $now = time();
        $db->insert('balance_log', [
            'member_id' => intval($recharge['member_id']),
            'type' => 'recharge',
            'amount' => $recharge['amount'],
            'balance' => $balance,
            'note' => '支付宝充值：' . $tradeNo,
            'remark' => '支付宝充值：' . $tradeNo,
            'add_time' => $now,
            'create_time' => $now,
        ]);
        $db->commit();
        return true;
    } catch (Throwable $e) {
        if ($db->getPdo()->inTransaction()) { $db->rollBack(); }
        error_log('Verified recharge query processing failed: ' . $e->getMessage());
        return false;
    }
}

function cmsProcessAlipayRechargeNotification($db, array $params, array $config)
{
    $verified = cmsVerifyAlipayNotification($params, $config);
    if (!$verified['success']) { return $verified; }
    if (!in_array($params['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED'], true)) {
        return ['success' => true, 'message' => '无需处理的交易状态'];
    }

    cmsEnsurePaymentEventTable($db);
    $tradeNo = trim((string) $params['out_trade_no']);
    $transactionId = trim((string) $params['trade_no']);
    if (cmsPaymentEventExists($db, $transactionId, 'recharge', $tradeNo)) {
        return ['success' => true, 'message' => '充值通知已处理'];
    }

    $db->beginTransaction();
    try {
        $recharge = $db->fetch("SELECT * FROM {$db->table('recharge')} WHERE trade_no = ? FOR UPDATE", [$tradeNo]);
        if (!$recharge) { throw new UnexpectedValueException('充值记录不存在'); }
        if (!cmsPaymentAmountMatches($recharge['amount'], $params['total_amount'])) {
            throw new UnexpectedValueException('支付宝通知金额与充值记录不一致');
        }
        if (intval($recharge['status']) !== 0) {
            throw new UnexpectedValueException('充值记录状态不允许入账');
        }

        cmsInsertPaymentEvent($db, $params, 'recharge', intval($recharge['id']));
        $updated = $db->update('recharge', ['status' => 1], 'id = ? AND status = 0', [intval($recharge['id'])]);
        if ($updated !== 1) { throw new UnexpectedValueException('充值记录已被处理'); }
        $balanceUpdated = $db->query(
            "UPDATE {$db->table('member')} SET balance = balance + ? WHERE id = ?",
            [$recharge['amount'], intval($recharge['member_id'])]
        );
        if ($balanceUpdated->rowCount() !== 1) { throw new UnexpectedValueException('充值会员不存在'); }
        $member = $db->fetch("SELECT balance FROM {$db->table('member')} WHERE id = ?", [intval($recharge['member_id'])]);
        $now = time();
        $db->insert('balance_log', [
            'member_id' => intval($recharge['member_id']),
            'type' => 'recharge',
            'amount' => $recharge['amount'],
            'balance' => $member['balance'] ?? 0,
            'note' => '支付宝充值：' . $transactionId,
            'remark' => '支付宝充值：' . $transactionId,
            'add_time' => $now,
            'create_time' => $now,
        ]);
        $db->commit();
        return ['success' => true, 'message' => '充值入账成功'];
    } catch (Throwable $e) {
        if ($db->getPdo()->inTransaction()) { $db->rollBack(); }
        if (cmsPaymentEventExists($db, $transactionId, 'recharge', $tradeNo)) {
            return ['success' => true, 'message' => '充值通知已处理'];
        }
        error_log('Alipay recharge processing failed: ' . $e->getMessage());
        return ['success' => false, 'message' => $e->getMessage()];
    }
}

function cmsProcessAlipayOrderNotification($db, array $params, array $config)
{
    $verified = cmsVerifyAlipayNotification($params, $config);
    if (!$verified['success']) { return $verified; }
    if (!in_array($params['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED'], true)) {
        return ['success' => true, 'message' => '无需处理的交易状态'];
    }

    cmsEnsurePaymentEventTable($db);
    $tradeNo = trim((string) $params['out_trade_no']);
    $transactionId = trim((string) $params['trade_no']);
    if (cmsPaymentEventExists($db, $transactionId, 'order', $tradeNo)) {
        return ['success' => true, 'message' => '订单通知已处理'];
    }

    $db->beginTransaction();
    try {
        $order = $db->fetch("SELECT * FROM {$db->table('order')} WHERE order_sn = ? FOR UPDATE", [$tradeNo]);
        if (!$order) { throw new UnexpectedValueException('订单不存在'); }
        $expectedAmount = $order['pay_price'] ?? $order['total_price'] ?? $order['total_amount'] ?? 0;
        if (!cmsPaymentAmountMatches($expectedAmount, $params['total_amount'])) {
            throw new UnexpectedValueException('支付宝通知金额与订单不一致');
        }
        if (intval($order['status']) !== 0) {
            throw new UnexpectedValueException('订单状态不允许支付');
        }

        cmsInsertPaymentEvent($db, $params, 'order', intval($order['id']));
        $updated = $db->update('order', [
            'status' => 1,
            'pay_price' => cmsPaymentNormalizeAmount($params['total_amount']),
            'pay_type' => 2,
            'pay_time' => time(),
            'update_time' => time(),
        ], 'id = ? AND status = 0', [intval($order['id'])]);
        if ($updated !== 1) { throw new UnexpectedValueException('订单已被处理'); }
        $db->commit();
        return ['success' => true, 'message' => '订单支付成功'];
    } catch (Throwable $e) {
        if ($db->getPdo()->inTransaction()) { $db->rollBack(); }
        if (cmsPaymentEventExists($db, $transactionId, 'order', $tradeNo)) {
            return ['success' => true, 'message' => '订单通知已处理'];
        }
        error_log('Alipay order processing failed: ' . $e->getMessage());
        return ['success' => false, 'message' => $e->getMessage()];
    }
}

function cmsPayOrderWithBalance($db, $memberId, $orderId)
{
    $memberId = intval($memberId);
    $orderId = intval($orderId);
    if ($memberId <= 0 || $orderId <= 0) {
        return ['success' => false, 'code' => 400, 'message' => '参数错误'];
    }

    $db->beginTransaction();
    try {
        $order = $db->fetch("SELECT * FROM {$db->table('order')} WHERE id = ? AND member_id = ? FOR UPDATE", [$orderId, $memberId]);
        if (!$order) { throw new UnexpectedValueException('订单不存在'); }
        if (intval($order['status']) !== 0) { throw new UnexpectedValueException('订单状态不允许支付'); }
        $amount = $order['pay_price'] ?? $order['total_price'] ?? $order['total_amount'] ?? 0;
        $amount = cmsPaymentNormalizeAmount($amount);
        if ($amount === null || (float) $amount <= 0) { throw new UnexpectedValueException('订单金额无效'); }

        $member = $db->fetch("SELECT balance FROM {$db->table('member')} WHERE id = ? FOR UPDATE", [$memberId]);
        if (!$member || cmsPaymentNormalizeAmount($member['balance']) === null) {
            throw new UnexpectedValueException('会员余额异常');
        }
        $deducted = $db->query(
            "UPDATE {$db->table('member')} SET balance = balance - ? WHERE id = ? AND balance >= ?",
            [$amount, $memberId, $amount]
        );
        if ($deducted->rowCount() !== 1) { throw new UnexpectedValueException('余额不足，请先充值'); }
        $updated = $db->update('order', [
            'status' => 1,
            'pay_price' => $amount,
            'pay_type' => 3,
            'pay_time' => time(),
            'update_time' => time(),
        ], 'id = ? AND status = 0', [$orderId]);
        if ($updated !== 1) { throw new UnexpectedValueException('订单已被处理'); }
        $balance = $db->fetch("SELECT balance FROM {$db->table('member')} WHERE id = ?", [$memberId])['balance'] ?? 0;
        $db->insert('balance_log', [
            'member_id' => $memberId,
            'type' => 'pay',
            'amount' => 0 - (float) $amount,
            'balance' => $balance,
            'remark' => '订单支付：' . $order['order_sn'],
            'create_time' => time(),
        ]);
        $db->commit();
        return ['success' => true, 'code' => 200, 'message' => '支付成功'];
    } catch (Throwable $e) {
        if ($db->getPdo()->inTransaction()) { $db->rollBack(); }
        $code = $e instanceof UnexpectedValueException ? 400 : 500;
        return ['success' => false, 'code' => $code, 'message' => $e->getMessage()];
    }
}
