文件
2026-08-16 17:03:10 +08:00

174 行
8.5 KiB
PHP

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
<?php
require __DIR__ . '/includes/init.php';
requireLogin('checkout.php');
$pageTitle = '结算';
$user = currentUser();
$cart = $_SESSION['cart'] ?? [];
$items = [];
$total = 0;
if (!empty($cart)) {
$ids = array_keys($cart);
$ph = implode(',', array_fill(0, count($ids), '?'));
$stmt = db()->prepare('SELECT * FROM ' . tn('products') . ' WHERE id IN (' . $ph . ') AND status = 1');
$stmt->execute($ids);
foreach ($stmt->fetchAll() as $p) {
$qty = (int) $cart[$p['id']];
if ($qty > $p['stock']) $qty = $p['stock'];
$pointsSub = (!empty($p['points_price']) ? (int)$p['points_price'] * $qty : 0);
$items[] = ['p' => $p, 'qty' => $qty, 'points_sub' => $pointsSub];
}
}
$requireRealname = false;
$agreements = [];
foreach ($items as $it) {
if (!empty($it['p']['require_realname'])) {
$requireRealname = true;
}
$ag = getProductAgreement($it['p']);
if (!in_array($ag, $agreements, true)) {
$agreements[] = $ag;
}
}
$userRealnameOk = $requireRealname ? isRealnameVerified($user['id']) : true;
$pointsElg = !empty($items);
$pointsTotal = 0;
foreach ($items as $it) {
$pp = (int) ($it['p']['points_price'] ?? 0);
if ($pp <= 0) { $pointsElg = false; break; }
$pointsTotal += $pp * $it['qty'];
}
if ($pointsTotal <= 0) { $pointsElg = false; }
$userPoints = 0;
if (isLoggedIn()) {
$userPoints = getUserPoints($user['id']);
}
$err = '';
if (empty($items)) {
redirect('cart.php');
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
verifyCsrf();
$contact = trim($_POST['contact'] ?? '');
$contactEmail = trim($_POST['contact_email'] ?? '');
$address = trim($_POST['address'] ?? '');
$note = trim($_POST['note'] ?? '');
if ($contact === '') $err = '请填写联系人';
elseif ($address === '') $err = '请填写收货地址';
elseif ($contactEmail === '') $contactEmail = $user['email'];
elseif (!filter_var($contactEmail, FILTER_VALIDATE_EMAIL)) $err = '联系邮箱格式不正确';
if ($err === '') {
$payType = ($pointsElg && ($_POST['pay_type'] ?? '') === 'points') ? 'points' : 'direct';
if ($payType === 'points') {
if ($userPoints < $pointsTotal) {
$err = '积分不足,当前 ' . $userPoints . ' 分,本单需 ' . $pointsTotal . ' 分';
}
}
}
if ($err === '' && $requireRealname && !$userRealnameOk) {
$err = '购物车中包含需实名认证的商品,请先完成实名认证后再下单。';
}
if ($err === '' && empty($_POST['agree'])) {
$err = '请阅读并勾选「已阅读并同意购买协议」后再提交订单。';
}
if ($err === '') {
$pdo = db();
try {
$pdo->beginTransaction();
foreach ($items as $it) {
$chk = $pdo->prepare('SELECT stock FROM ' . tn('products') . ' WHERE id = ? FOR UPDATE');
$chk->execute([$it['p']['id']]);
$row = $chk->fetch();
if (!$row || $row['stock'] < $it['qty']) {
throw new Exception('「' . $it['p']['name'] . '」库存不足,请返回购物车调整');
}
}
$maxPeriod = 0;
foreach ($items as $it) {
$maxPeriod = max($maxPeriod, (int) ($it['p']['period_days'] ?? 0));
}
$expiresAt = $maxPeriod > 0 ? date('Y-m-d H:i:s', strtotime("+$maxPeriod days")) : null;
$orderTotal = 0;
$pointsUsed = 0;
if ($payType === 'points') {
$pointsUsed = $pointsTotal;
$pdo->prepare('UPDATE ' . tn('users') . ' SET points = points - ? WHERE id = ? AND points >= ?')
->execute([$pointsUsed, $user['id'], $pointsUsed]);
$bal = getUserPoints($user['id']);
$pdo->prepare('INSERT INTO ' . tn('points_log') . ' (user_id,type,amount,balance,remark,created_at) VALUES (?,?,?,?,?,NOW())')
->execute([$user['id'], 'purchase', -$pointsUsed, $bal, '积分兑换:' . $orderNo]);
}
$orderNo = 'FNW' . date('Ymd') . strtoupper(substr(md5(uniqid($user['id'], true)), 0, 10));
$pdo->prepare('INSERT INTO ' . tn('orders') . ' (order_no,user_id,total,status,contact,contact_email,address,note,period_days,expires_at,pay_type,points_used,created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,NOW())')
->execute([$orderNo, $user['id'], $orderTotal, 'paid', $contact, $contactEmail, $address, $note, $maxPeriod, $expiresAt, $payType, $pointsUsed]);
$orderId = $pdo->lastInsertId();
foreach ($items as $it) {
$pdo->prepare('INSERT INTO ' . tn('order_items') . ' (order_id,product_id,name,price,qty,subtotal,period_days) VALUES (?,?,?,?,?,?,?)')
->execute([$orderId, $it['p']['id'], $it['p']['name'], 0, $it['qty'], 0, (int) ($it['p']['period_days'] ?? 0)]);
$pdo->prepare('UPDATE ' . tn('products') . ' SET stock = stock - ? WHERE id = ?')
->execute([$it['qty'], $it['p']['id']]);
}
$pdo->commit();
$_SESSION['cart'] = [];
redirect('myorders.php?paid=' . urlencode($orderNo));
} catch (Exception $e) {
$pdo->rollBack();
$err = $e->getMessage();
}
}
}
require 'includes/header.php';
?>
<section class="section">
<h2 class="sec-title"><i class="fas fa-credit-card"></i> 订单结算</h2>
<?php if ($requireRealname && !$userRealnameOk): ?>
<div class="realname-warn">
<i class="fas fa-id-card"></i>
<span>本单包含需实名认证的商品,请先 <a href="realname.php">完成实名认证</a> 后再提交订单。</span>
</div>
<?php endif; ?>
<div class="checkout">
<div class="co-items">
<h3>商品清单</h3>
<?php foreach ($items as $it): ?>
<div class="co-row">
<span><?= h($it['p']['name']) ?> × <?= $it['qty'] ?></span>
<span><?= $it['points_sub'] > 0 ? (int)$it['points_sub'] . ' 积分' : '直接下单' ?></span>
</div>
<?php endforeach; ?>
</div>
<form method="post" action="" class="co-form">
<?= csrfField() ?>
<h3>收货信息</h3>
<?php if ($err): ?><p class="form-err"><?= h($err) ?></p><?php endif; ?>
<label>联系人<input type="text" name="contact" value="<?= h($_POST['contact'] ?? $user['username']) ?>" required></label>
<label>联系邮箱<input type="email" name="contact_email" value="<?= h($_POST['contact_email'] ?? $user['email']) ?>" placeholder="用于接收订单/发货通知" required></label>
<label>收货地址<input type="text" name="address" value="<?= h($_POST['address'] ?? '') ?>" required></label>
<label>备注<input type="text" name="note" value="<?= h($_POST['note'] ?? '') ?>" placeholder="选填"></label>
<?php if ($pointsElg): ?>
<div class="co-pay">
<h3>支付方式</h3>
<input type="hidden" name="pay_type" value="points">
<p class="pay-opt"><i class="fas fa-coins"></i> 积分支付(<?= $pointsTotal ?> 积分)<span class="muted" style="margin-left:10px;font-size:.85em;">当前积分 <?= (int)$userPoints ?></span></p>
</div>
<?php else: ?>
<input type="hidden" name="pay_type" value="direct">
<?php endif; ?>
<p class="co-tip">下单后如有积分价格要求,用积分支付;无积分价格,直接支付即可(发工单即代表您已同意资源领取/续期规定)。</p>
<?php if (!empty($agreements)): ?>
<div class="agreement-box">
<h4><i class="fas fa-file-contract"></i> 购买协议(下单前请仔细阅读)</h4>
<?php foreach ($agreements as $ag): ?>
<div class="agreement-text"><?= nl2br(h($ag)) ?></div>
<?php endforeach; ?>
<label class="agree-check">
<input type="checkbox" name="agree" value="1" required>
我已阅读并同意上述《购买协议》,承诺合规使用所购商品,绝不用于搭建违规网站、VPN 翻墙服务等违法违规用途。
</label>
</div>
<?php endif; ?>
<button type="submit" class="btn btn-primary" style="width:100%;">提交订单</button>
</form>
</div>
</section>
<?php require 'includes/footer.php'; ?>