Files
freeshop/checkout.php
T
2026-08-14 10:55:06 +08:00

154 lines
7.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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];
}
}
// 积分支付可用性:仅当订单内所有商品都设置了积分价
$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 === '') {
$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;
// 本商城不收取现金,现金合计固定记 0;积分支付时抵扣并记流水
$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) {
// 商城已取消现金支付,价格字段保留为 0 供历史兼容
$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>
<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>
<button type="submit" class="btn btn-primary" style="width:100%;">提交订单</button>
</form>
</div>
</section>
<?php require 'includes/footer.php'; ?>