commit 7a7a9759cafe7a2358bb5f4916cda0cd6091563d
Author: aaron <>
Date: Sun Aug 10 12:20:27 2025 +0800
first commit
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..e4e243a
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,11 @@
+# 环境变量配置文件
+# UPay 配置
+UPAY_APP_ID=E7c4dss9
+UPAY_APP_SECRET=Hwc56INsabRau2yn
+
+# 数据库配置
+DB_PATH=./database/shop.db
+
+# 服务器配置
+PORT=3000
+NODE_ENV=development
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d4218e6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,94 @@
+# Dependencies
+node_modules/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Coverage directory used by tools like istanbul
+coverage/
+
+# Dependency directories
+jspm_packages/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# Environment variables
+.env
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+# Database files
+*.db
+*.sqlite
+database/*.db
+
+# Logs
+logs/
+*.log
+
+# Runtime data
+tmp/
+temp/
+
+# macOS
+.DS_Store
+.DS_Store?
+._*
+.Spotlight-V100
+.Trashes
+ehthumbs.db
+Thumbs.db
+
+# Windows
+Thumbs.db
+ehthumbs.db
+Desktop.ini
+
+# Linux
+*~
+
+# Editor directories and files
+.vscode/
+.idea/
+*.swp
+*.swo
+*~
+
+# OS generated files
+.DS_Store
+.DS_Store?
+._*
+.Spotlight-V100
+.Trashes
+ehthumbs.db
+Thumbs.db
+
+# Payment config files (if any)
+config/payment-keys.json
+config/secrets.json
+payment-config.json
+
+# Backup files
+*.bak
+*.backup
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..eca666a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,117 @@
+# USDT 支付商城
+
+这是一个基于 Node.js 和 UPay API 的 USDT 支付商城系统。
+
+## 功能特性
+
+- 📱 现代化响应式设计
+- 💰 USDT 加密货币支付
+- 🛒 简洁的购买流程
+- 📦 订单管理系统
+- 🔒 安全的支付处理
+- 📊 实时支付状态跟踪
+
+## 技术栈
+
+- **后端**: Node.js + Express
+- **数据库**: SQLite3
+- **支付**: UPay API (USDT支付)
+- **前端**: 原生 JavaScript + CSS3
+- **样式**: CSS Grid + Flexbox
+
+## 安装和运行
+
+1. 克隆项目并安装依赖:
+```bash
+npm install
+```
+
+2. 配置 UPay API:
+```bash
+# 设置环境变量
+export UPAY_APP_ID=your-upay-app-id
+export UPAY_APP_SECRET=your-upay-app-secret
+```
+
+3. 启动服务器:
+```bash
+# 开发模式
+npm run dev
+
+# 生产模式
+npm start
+```
+
+4. 访问 http://localhost:3000
+
+## 项目结构
+
+```
+myusdtshop/
+├── server.js # 主服务器文件
+├── package.json # 项目配置
+├── database/ # SQLite 数据库文件
+├── public/ # 前端静态文件
+│ ├── index.html # 主页面
+│ ├── css/
+│ │ └── style.css # 样式文件
+│ ├── js/
+│ │ └── main.js # JavaScript 逻辑
+│ └── images/ # 图片资源
+```
+
+## API 接口
+
+- `GET /api/products` - 获取产品信息
+- `POST /api/orders` - 创建订单
+- `POST /api/payment/create` - 创建支付
+- `POST /api/payment/callback` - 支付回调
+- `GET /api/orders/:order_id` - 获取订单状态
+
+## 使用说明
+
+### 配置 UPay
+
+1. 在 [UPay](https://upay.ink) 注册商户账户
+2. 获取 App ID 和 App Secret
+3. 设置环境变量或修改 server.js 中的配置
+
+### 自定义产品
+
+修改 server.js 中的 `PRODUCTS` 对象:
+
+```javascript
+const PRODUCTS = {
+ 'your-product-id': {
+ name: '您的产品名称',
+ price: 99.99,
+ description: '产品描述'
+ }
+};
+```
+
+### 支付流程
+
+1. 用户选择产品和数量
+2. 填写收货信息
+3. 创建订单
+4. 跳转到支付页面完成 USDT 支付
+5. 系统通过回调自动确认支付状态
+
+## UPay 集成特性
+
+- 支持 USDT 直接转账到商户钱包
+- 实时支付状态回调通知
+- MD5 签名验证确保安全
+- 支持多种支付状态处理
+
+## 安全注意事项
+
+- 请勿将 App Secret 提交到版本控制
+- 生产环境请使用 HTTPS
+- 定期备份数据库
+- 验证所有支付回调签名
+
+## 许可证
+
+ISC License
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..dda5dd5
--- /dev/null
+++ b/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "usdt-shop",
+ "version": "1.0.0",
+ "description": "USDT Payment Product Sales Website",
+ "main": "server.js",
+ "scripts": {
+ "start": "node server.js",
+ "dev": "nodemon server.js"
+ },
+ "dependencies": {
+ "express": "^4.18.2",
+ "sqlite3": "^5.1.6",
+ "axios": "^1.6.0",
+ "cors": "^2.8.5",
+ "body-parser": "^1.20.2",
+ "crypto": "^1.0.1"
+ },
+ "devDependencies": {
+ "nodemon": "^3.0.1"
+ },
+ "keywords": ["usdt", "payment", "shop", "nowpayments"],
+ "author": "Aaron",
+ "license": "ISC"
+}
\ No newline at end of file
diff --git a/public/admin.html b/public/admin.html
new file mode 100644
index 0000000..e10344b
--- /dev/null
+++ b/public/admin.html
@@ -0,0 +1,116 @@
+
+
+
+
+
+ 订单管理系统
+ 管理所有订单信息和发货状态
+
+
+
+
+
总订单数
+ 0
+
+
+
已支付未发货
+ 0
+
+
+
已发货
+ 0
+
+
+
+
+
+
+
+
+
+
+ | 订单号 |
+ 客户信息 |
+ 产品 |
+ 金额 |
+ 支付状态 |
+ 发货状态 |
+ 创建时间 |
+ 操作 |
+
+
+
+
+ | 加载中... |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/css/admin.css b/public/css/admin.css
new file mode 100644
index 0000000..f071409
--- /dev/null
+++ b/public/css/admin.css
@@ -0,0 +1,455 @@
+/* 订单管理页面样式 */
+
+/* 管理员导航 - 已移除 */
+
+/* 统计卡片 */
+.stats-section {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 20px;
+ margin-bottom: 30px;
+}
+
+.stat-card {
+ background: linear-gradient(145deg, #1a1a1a, #2d2d2d);
+ padding: 25px;
+ border-radius: 15px;
+ text-align: center;
+ border: 1px solid #444;
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
+ position: relative;
+ overflow: hidden;
+}
+
+.stat-card::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 3px;
+ background: linear-gradient(90deg, #ffd700, #ffed4a);
+}
+
+.stat-card h3 {
+ color: #b0b0b0;
+ font-size: 1.1em;
+ margin-bottom: 10px;
+ font-weight: 500;
+}
+
+.stat-card span {
+ color: #ffd700;
+ font-size: 2.2em;
+ font-weight: bold;
+ display: block;
+ text-shadow: 0 0 20px rgba(255, 215, 0, 0.3);
+}
+
+/* 订单表格区域 */
+.orders-section {
+ background: linear-gradient(145deg, #1a1a1a, #2d2d2d);
+ border-radius: 15px;
+ padding: 30px;
+ border: 1px solid #444;
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
+}
+
+.section-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 25px;
+ padding-bottom: 15px;
+ border-bottom: 1px solid #333;
+ flex-wrap: wrap;
+ gap: 15px;
+}
+
+.section-header h2 {
+ color: #ffffff;
+ font-size: 1.5em;
+ margin: 0;
+}
+
+.header-controls {
+ display: flex;
+ align-items: center;
+ gap: 15px;
+ flex-wrap: wrap;
+}
+
+.search-box {
+ display: flex;
+ gap: 10px;
+}
+
+.search-box input {
+ padding: 10px 15px;
+ background: #0a0a0a;
+ border: 2px solid #444;
+ border-radius: 8px;
+ color: #e0e0e0;
+ width: 250px;
+ font-size: 14px;
+}
+
+.search-box input:focus {
+ outline: none;
+ border-color: #ffd700;
+ box-shadow: 0 0 15px rgba(255, 215, 0, 0.2);
+}
+
+.search-btn {
+ padding: 10px 18px;
+ background: linear-gradient(135deg, #ffd700, #ffed4a);
+ color: #000;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ font-weight: bold;
+ transition: all 0.3s ease;
+}
+
+.search-btn:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 5px 15px rgba(255, 215, 0, 0.3);
+}
+
+.section-header select {
+ padding: 10px 15px;
+ background: #0a0a0a;
+ border: 2px solid #444;
+ border-radius: 8px;
+ color: #e0e0e0;
+ font-size: 14px;
+ min-width: 120px;
+}
+
+.section-header select:focus {
+ outline: none;
+ border-color: #ffd700;
+}
+
+/* 表格容器 */
+.orders-table-container {
+ overflow-x: auto;
+ background: #0a0a0a;
+ border-radius: 10px;
+ border: 1px solid #333;
+}
+
+.orders-table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 14px;
+}
+
+.orders-table th {
+ background: linear-gradient(145deg, #2d2d2d, #3d3d3d);
+ color: #ffd700;
+ padding: 15px 12px;
+ text-align: left;
+ font-weight: bold;
+ border-bottom: 2px solid #444;
+ position: sticky;
+ top: 0;
+ z-index: 10;
+}
+
+.orders-table td {
+ padding: 12px;
+ border-bottom: 1px solid #333;
+ color: #e0e0e0;
+ vertical-align: middle;
+}
+
+.orders-table tbody tr {
+ transition: background-color 0.3s ease;
+}
+
+.orders-table tbody tr:hover {
+ background-color: rgba(255, 215, 0, 0.05);
+}
+
+.loading {
+ text-align: center;
+ color: #b0b0b0;
+ font-style: italic;
+ padding: 50px !important;
+}
+
+/* 状态标签 */
+.status-badge {
+ display: inline-block;
+ padding: 4px 10px;
+ border-radius: 12px;
+ font-size: 12px;
+ font-weight: bold;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+.status-pending {
+ background: rgba(255, 193, 7, 0.2);
+ color: #ffc107;
+ border: 1px solid #ffc107;
+}
+
+.status-finished {
+ background: rgba(40, 167, 69, 0.2);
+ color: #28a745;
+ border: 1px solid #28a745;
+}
+
+.status-shipped {
+ background: rgba(0, 123, 255, 0.2);
+ color: #007bff;
+ border: 1px solid #007bff;
+}
+
+.status-completed {
+ background: rgba(108, 117, 125, 0.2);
+ color: #6c757d;
+ border: 1px solid #6c757d;
+}
+
+.status-failed {
+ background: rgba(220, 53, 69, 0.2);
+ color: #dc3545;
+ border: 1px solid #dc3545;
+}
+
+/* 操作按钮 */
+.action-btn {
+ padding: 6px 12px;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 12px;
+ font-weight: bold;
+ margin-right: 5px;
+ margin-bottom: 4px;
+ transition: all 0.3s ease;
+ display: inline-block;
+}
+
+.btn-copy {
+ background: linear-gradient(135deg, #3498db, #2980b9);
+ color: #fff;
+ border: 1px solid #3498db;
+}
+
+.btn-copy:hover {
+ background: linear-gradient(135deg, #2980b9, #1f639a);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(52, 152, 219, 0.4);
+}
+
+.btn-ship {
+ background: linear-gradient(135deg, #ffd700, #ffed4a);
+ color: #000;
+ border: 1px solid #ffd700;
+}
+
+.btn-ship:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(255, 215, 0, 0.4);
+}
+
+/* 订单详情模态框 */
+.order-detail-content {
+ color: #e0e0e0;
+}
+
+.detail-section {
+ margin-bottom: 25px;
+ padding: 20px;
+ background: linear-gradient(145deg, #0a0a0a, #1a1a1a);
+ border-radius: 10px;
+ border: 1px solid #333;
+}
+
+.detail-section h4 {
+ color: #ffd700;
+ margin-bottom: 15px;
+ font-size: 1.1em;
+ padding-bottom: 8px;
+ border-bottom: 1px solid #333;
+}
+
+.detail-row {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 10px;
+ padding: 8px 0;
+}
+
+.detail-row:last-child {
+ margin-bottom: 0;
+}
+
+.detail-label {
+ font-weight: bold;
+ color: #b0b0b0;
+}
+
+.detail-value {
+ color: #e0e0e0;
+}
+
+/* 发货表单 */
+.shipping-form .form-group {
+ margin-bottom: 20px;
+}
+
+.shipping-form label {
+ display: block;
+ margin-bottom: 8px;
+ color: #ffffff;
+ font-weight: 600;
+}
+
+.shipping-form input,
+.shipping-form select,
+.shipping-form textarea {
+ width: 100%;
+ padding: 12px;
+ background: #0a0a0a;
+ border: 2px solid #444;
+ border-radius: 8px;
+ color: #e0e0e0;
+ font-size: 14px;
+}
+
+.shipping-form input:focus,
+.shipping-form select:focus,
+.shipping-form textarea:focus {
+ outline: none;
+ border-color: #ffd700;
+ box-shadow: 0 0 15px rgba(255, 215, 0, 0.2);
+}
+
+.form-actions {
+ display: flex;
+ gap: 15px;
+ justify-content: flex-end;
+ margin-top: 25px;
+}
+
+.btn-primary {
+ padding: 12px 25px;
+ background: linear-gradient(135deg, #ffd700, #ffed4a);
+ color: #000;
+ border: none;
+ border-radius: 8px;
+ font-weight: bold;
+ cursor: pointer;
+ transition: all 0.3s ease;
+}
+
+.btn-primary:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 8px 20px rgba(255, 215, 0, 0.4);
+}
+
+.btn-secondary {
+ padding: 12px 25px;
+ background: linear-gradient(145deg, #333, #444);
+ color: #e0e0e0;
+ border: 1px solid #555;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+}
+
+.btn-secondary:hover {
+ background: linear-gradient(145deg, #444, #555);
+ transform: translateY(-2px);
+}
+
+/* 复制按钮样式 */
+.copy-btn {
+ background: none;
+ border: none;
+ color: #ffd700;
+ cursor: pointer;
+ font-size: 12px;
+ margin-left: 8px;
+ padding: 2px 4px;
+ border-radius: 3px;
+ transition: all 0.3s ease;
+ opacity: 0.7;
+}
+
+.copy-btn:hover {
+ opacity: 1;
+ background: rgba(255, 215, 0, 0.1);
+ transform: scale(1.1);
+}
+
+/* 复制成功提示 */
+.copy-toast {
+ position: fixed;
+ top: 20px;
+ right: 20px;
+ background: linear-gradient(135deg, #00ff88, #00cc6a);
+ color: #000;
+ padding: 12px 20px;
+ border-radius: 8px;
+ font-weight: bold;
+ font-size: 14px;
+ z-index: 9999;
+ box-shadow: 0 8px 25px rgba(0, 255, 136, 0.3);
+ animation: slideIn 0.3s ease-out;
+}
+
+@keyframes slideIn {
+ from {
+ transform: translateX(100%);
+ opacity: 0;
+ }
+ to {
+ transform: translateX(0);
+ opacity: 1;
+ }
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+ .stats-section {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .section-header {
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .header-controls {
+ justify-content: space-between;
+ }
+
+ .search-box input {
+ width: 180px;
+ }
+
+ .orders-table {
+ font-size: 12px;
+ }
+
+ .orders-table th,
+ .orders-table td {
+ padding: 8px 6px;
+ }
+
+ .action-btn {
+ padding: 4px 8px;
+ font-size: 11px;
+ }
+
+ .copy-btn {
+ font-size: 10px;
+ margin-left: 4px;
+ }
+}
\ No newline at end of file
diff --git a/public/css/spinner.css b/public/css/spinner.css
new file mode 100644
index 0000000..3142828
--- /dev/null
+++ b/public/css/spinner.css
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/public/css/style.css b/public/css/style.css
new file mode 100644
index 0000000..acd1164
--- /dev/null
+++ b/public/css/style.css
@@ -0,0 +1,611 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: 'Arial', 'Microsoft YaHei', sans-serif;
+ line-height: 1.6;
+ color: #e0e0e0;
+ background: #0a0a0a;
+ min-height: 100vh;
+}
+
+.container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+main {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 40px;
+ align-items: start;
+ position: relative;
+}
+
+/* 订单管理按钮 */
+.admin-floating-btn {
+ position: fixed;
+ top: 30px;
+ right: 30px;
+ z-index: 1000;
+ background: linear-gradient(135deg, #ffd700, #ffed4a);
+ color: #000;
+ border: none;
+ border-radius: 50px;
+ padding: 12px 25px;
+ font-size: 14px;
+ font-weight: bold;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ box-shadow: 0 4px 15px rgba(255, 215, 0, 0.3);
+}
+
+.admin-floating-btn:hover {
+ transform: translateY(-3px);
+ box-shadow: 0 8px 25px rgba(255, 215, 0, 0.5);
+}
+
+/* 产品卡片 */
+.product-section {
+ background: linear-gradient(145deg, #1a1a1a 0%, #2d2d2d 100%);
+ border-radius: 20px;
+ padding: 30px;
+ box-shadow:
+ 0 20px 40px rgba(0, 0, 0, 0.4),
+ inset 0 1px 0 rgba(255, 255, 255, 0.1);
+ border: 1px solid #333;
+}
+
+.product-card {
+ text-align: center;
+}
+
+.product-image {
+ width: 100%;
+ height: 280px;
+ background: linear-gradient(135deg, #333 0%, #444 100%);
+ border-radius: 15px;
+ margin-bottom: 25px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+ border: 2px solid #444;
+ position: relative;
+}
+
+.product-image::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: linear-gradient(45deg, transparent 49%, rgba(255, 215, 0, 0.1) 50%, transparent 51%);
+ pointer-events: none;
+}
+
+.product-image img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ object-position: center;
+ border-radius: 10px;
+}
+
+.product-info h2 {
+ color: #ffffff;
+ margin-bottom: 15px;
+ font-size: 2em;
+ font-weight: 600;
+}
+
+.product-description {
+ color: #b0b0b0;
+ margin-bottom: 25px;
+ line-height: 1.6;
+ font-size: 1.1em;
+}
+
+.price {
+ font-size: 2.2em;
+ font-weight: bold;
+ margin-bottom: 20px;
+ background: linear-gradient(45deg, #ffd700, #ffed4a);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ text-shadow: 0 0 20px rgba(255, 215, 0, 0.3);
+}
+
+.price .currency,
+.price .unit {
+ font-size: 0.7em;
+ color: #888;
+}
+
+/* 订单表单 */
+.order-section {
+ background: linear-gradient(145deg, #1a1a1a 0%, #2d2d2d 100%);
+ border-radius: 20px;
+ padding: 30px;
+ box-shadow:
+ 0 20px 40px rgba(0, 0, 0, 0.4),
+ inset 0 1px 0 rgba(255, 255, 255, 0.1);
+ border: 1px solid #333;
+}
+
+.form-group {
+ margin-bottom: 25px;
+}
+
+.form-group label {
+ display: block;
+ margin-bottom: 8px;
+ font-weight: 600;
+ color: #ffffff;
+ font-size: 1.1em;
+}
+
+.form-group input,
+.form-group textarea {
+ width: 100%;
+ padding: 15px;
+ border: 2px solid #444;
+ border-radius: 10px;
+ font-size: 16px;
+ background: #0a0a0a;
+ color: #e0e0e0;
+ transition: all 0.3s ease;
+}
+
+.form-group input:focus,
+.form-group textarea:focus {
+ outline: none;
+ border-color: #ffd700;
+ box-shadow: 0 0 20px rgba(255, 215, 0, 0.2);
+ background: #1a1a1a;
+}
+
+.form-group input::placeholder,
+.form-group textarea::placeholder {
+ color: #666;
+}
+
+/* 数量控制 */
+.quantity-controls {
+ display: flex;
+ align-items: center;
+ gap: 15px;
+ margin-bottom: 20px;
+ justify-content: center;
+}
+
+.quantity-controls button {
+ width: 45px;
+ height: 45px;
+ border: 2px solid #ffd700;
+ background: linear-gradient(145deg, #1a1a1a, #2d2d2d);
+ color: #ffd700;
+ border-radius: 50%;
+ font-size: 20px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ font-weight: bold;
+}
+
+.quantity-controls button:hover {
+ background: linear-gradient(145deg, #ffd700, #ffed4a);
+ color: #000;
+ transform: scale(1.05);
+ box-shadow: 0 8px 20px rgba(255, 215, 0, 0.3);
+}
+
+.quantity-controls input {
+ width: 80px;
+ text-align: center;
+ margin: 0;
+ font-weight: bold;
+ font-size: 18px;
+}
+
+.total-price {
+ font-size: 1.3em;
+ font-weight: bold;
+ text-align: center;
+ padding: 15px;
+ background: linear-gradient(145deg, #0a0a0a, #1a1a1a);
+ border-radius: 10px;
+ border: 2px solid #333;
+ background: linear-gradient(45deg, #ffd700, #ffed4a);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+/* 按钮 */
+.order-btn {
+ width: 100%;
+ padding: 18px;
+ background: linear-gradient(135deg, #ffd700 0%, #ffed4a 100%);
+ color: #000;
+ border: none;
+ border-radius: 12px;
+ font-size: 18px;
+ font-weight: bold;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ position: relative;
+ overflow: hidden;
+}
+
+.order-btn::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: -100%;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
+ transition: left 0.5s;
+}
+
+.order-btn:hover::before {
+ left: 100%;
+}
+
+.order-btn:hover {
+ transform: translateY(-3px);
+ box-shadow: 0 15px 35px rgba(255, 215, 0, 0.4);
+}
+
+.order-btn:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ transform: none;
+}
+
+/* 模态框 */
+.modal {
+ position: fixed;
+ z-index: 1000;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0, 0, 0, 0.8);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ backdrop-filter: blur(5px);
+}
+
+.modal-content {
+ background: linear-gradient(145deg, #1a1a1a, #2d2d2d);
+ padding: 0;
+ border-radius: 20px;
+ width: 90%;
+ max-width: 500px;
+ max-height: 90vh;
+ overflow: hidden;
+ border: 1px solid #444;
+ box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5);
+}
+
+.modal-header {
+ background: linear-gradient(135deg, #ffd700 0%, #ffed4a 100%);
+ color: #000;
+ padding: 25px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.modal-header h3 {
+ margin: 0;
+ font-size: 1.4em;
+ font-weight: bold;
+}
+
+.close {
+ color: #000;
+ font-size: 28px;
+ font-weight: bold;
+ cursor: pointer;
+ transition: opacity 0.3s;
+}
+
+.close:hover {
+ opacity: 0.7;
+}
+
+.modal-body {
+ padding: 30px;
+ background: #1a1a1a;
+}
+
+.order-summary {
+ margin-bottom: 30px;
+}
+
+.order-summary h4 {
+ color: #ffffff;
+ margin-bottom: 20px;
+ font-size: 1.3em;
+ font-weight: 600;
+}
+
+.summary-item {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 12px;
+ padding: 10px 0;
+ border-bottom: 1px solid #333;
+ color: #e0e0e0;
+}
+
+.summary-item:last-child {
+ border-bottom: none;
+ font-weight: bold;
+ color: #ffd700;
+ font-size: 1.1em;
+}
+
+.payment-section {
+ text-align: center;
+ padding: 25px;
+ background: linear-gradient(145deg, #0a0a0a, #1a1a1a);
+ border-radius: 12px;
+ border: 1px solid #333;
+}
+
+.payment-section p {
+ color: #b0b0b0;
+ margin-bottom: 20px;
+ font-size: 1.1em;
+}
+
+.pay-btn {
+ background: linear-gradient(135deg, #ffd700, #ffed4a);
+ color: #000;
+ border: none;
+ padding: 15px 35px;
+ border-radius: 10px;
+ font-size: 16px;
+ font-weight: bold;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+}
+
+.pay-btn:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 10px 25px rgba(255, 215, 0, 0.4);
+}
+
+/* 支付状态 */
+.payment-status {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: #0a0a0a;
+ z-index: 2000;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.status-content {
+ text-align: center;
+ padding: 50px;
+ background: linear-gradient(145deg, #1a1a1a, #2d2d2d);
+ border-radius: 20px;
+ border: 1px solid #444;
+ box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5);
+}
+
+.status-icon {
+ width: 100px;
+ height: 100px;
+ margin: 0 auto 30px;
+}
+
+.loading-spinner {
+ width: 100px;
+ height: 100px;
+ border: 6px solid #333;
+ border-top: 6px solid #ffd700;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+.status-content h3 {
+ color: #ffffff;
+ margin-bottom: 15px;
+ font-size: 2em;
+}
+
+.status-content p {
+ color: #b0b0b0;
+ margin-bottom: 30px;
+ font-size: 1.2em;
+}
+
+.order-info {
+ background: linear-gradient(145deg, #0a0a0a, #1a1a1a);
+ padding: 25px;
+ border-radius: 12px;
+ margin-bottom: 30px;
+ border: 1px solid #333;
+}
+
+.order-info p {
+ margin-bottom: 12px;
+ color: #e0e0e0;
+ font-size: 1.1em;
+}
+
+.status-btn {
+ background: linear-gradient(135deg, #ffd700, #ffed4a);
+ color: #000;
+ border: none;
+ padding: 15px 35px;
+ border-radius: 10px;
+ font-size: 16px;
+ font-weight: bold;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+}
+
+.status-btn:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 10px 25px rgba(255, 215, 0, 0.4);
+}
+
+/* 成功/失败图标 */
+.success-icon {
+ color: #00ff88;
+ font-size: 100px;
+ text-shadow: 0 0 30px rgba(0, 255, 136, 0.5);
+}
+
+.error-icon {
+ color: #ff4757;
+ font-size: 100px;
+ text-shadow: 0 0 30px rgba(255, 71, 87, 0.5);
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+ main {
+ grid-template-columns: 1fr;
+ gap: 30px;
+ }
+
+ .product-section,
+ .order-section {
+ padding: 25px;
+ }
+
+ .modal-content {
+ width: 95%;
+ margin: 10px;
+ }
+
+ .modal-body {
+ padding: 25px;
+ }
+
+ .status-content {
+ padding: 30px;
+ margin: 20px;
+ }
+
+ .admin-floating-btn {
+ top: 20px;
+ right: 20px;
+ padding: 10px 20px;
+ font-size: 12px;
+ }
+}
+
+/* 管理员登录表单样式 */
+.login-form .form-group {
+ margin-bottom: 20px;
+}
+
+.login-form label {
+ display: block;
+ margin-bottom: 8px;
+ color: #ffffff;
+ font-weight: 600;
+}
+
+.login-form input {
+ width: 100%;
+ padding: 12px 15px;
+ background: #0a0a0a;
+ border: 2px solid #444;
+ border-radius: 8px;
+ color: #e0e0e0;
+ font-size: 16px;
+ transition: all 0.3s ease;
+}
+
+.login-form input:focus {
+ outline: none;
+ border-color: #ffd700;
+ box-shadow: 0 0 15px rgba(255, 215, 0, 0.2);
+ background: #1a1a1a;
+}
+
+.login-form .form-actions {
+ display: flex;
+ gap: 15px;
+ justify-content: flex-end;
+ margin-top: 25px;
+}
+
+.login-form .btn-primary {
+ padding: 12px 25px;
+ background: linear-gradient(135deg, #ffd700, #ffed4a);
+ color: #000;
+ border: none;
+ border-radius: 8px;
+ font-weight: bold;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+}
+
+.login-form .btn-primary:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 8px 20px rgba(255, 215, 0, 0.4);
+}
+
+.login-form .btn-secondary {
+ padding: 12px 25px;
+ background: linear-gradient(145deg, #333, #444);
+ color: #e0e0e0;
+ border: 1px solid #555;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+}
+
+.login-form .btn-secondary:hover {
+ background: linear-gradient(145deg, #444, #555);
+ transform: translateY(-2px);
+}
+
+.error-message {
+ color: #ff4757;
+ font-size: 14px;
+ margin-top: 10px;
+ padding: 8px 12px;
+ background: rgba(255, 71, 87, 0.1);
+ border: 1px solid #ff4757;
+ border-radius: 6px;
+ text-align: center;
+}
\ No newline at end of file
diff --git a/public/images/image01.jpg b/public/images/image01.jpg
new file mode 100644
index 0000000..14ebe9f
Binary files /dev/null and b/public/images/image01.jpg differ
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..7fd3027
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,163 @@
+
+
+
+
+
+
USDT 商城
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
高级产品
+
这是我们的高级产品,质量优秀
+
+ $
+ 99.99
+ USDT
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
订单信息
+
+ 订单号:
+
+
+
+ 产品:
+
+
+
+ 数量:
+
+
+
+ 总价:
+
+
+
+ 收货地址:
+
+
+
+
+
请使用USDT(TRC20)完成支付
+
+
+
+
+
+
+
+
+
+
+
支付处理中...
+
请完成USDT支付,我们正在确认您的交易
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/js/admin.js b/public/js/admin.js
new file mode 100644
index 0000000..b50b704
--- /dev/null
+++ b/public/js/admin.js
@@ -0,0 +1,443 @@
+// 订单管理页面脚本
+let currentOrders = [];
+let currentOrderId = null;
+
+// DOM 元素
+const ordersTableBody = document.getElementById('ordersTableBody');
+const searchInput = document.getElementById('searchOrder');
+const statusFilter = document.getElementById('statusFilter');
+const orderDetailModal = document.getElementById('orderDetailModal');
+const shippingModal = document.getElementById('shippingModal');
+
+// 页面加载时初始化
+document.addEventListener('DOMContentLoaded', function() {
+ loadOrders();
+});
+
+// 加载订单列表
+async function loadOrders(search = '', status = '') {
+ try {
+ showLoading();
+
+ const params = new URLSearchParams();
+ if (search) params.append('search', search);
+ if (status) params.append('status', status);
+
+ const response = await fetch(`/api/admin/orders?${params}`);
+ const data = await response.json();
+
+ if (response.ok) {
+ currentOrders = data.orders;
+ updateOrdersTable(data.orders);
+ updateStats(data.stats);
+ } else {
+ throw new Error(data.error || '加载订单失败');
+ }
+ } catch (error) {
+ console.error('Load orders error:', error);
+ showError('加载订单失败: ' + error.message);
+ }
+}
+
+// 更新订单表格
+function updateOrdersTable(orders) {
+ if (!orders || orders.length === 0) {
+ ordersTableBody.innerHTML = '
| 暂无订单数据 |
';
+ return;
+ }
+
+ ordersTableBody.innerHTML = orders.map(order => `
+
+ |
+ ${order.order_id}
+ |
+
+
+ ${order.customer_name}
+
+ ${order.customer_phone || '未提供电话'}
+ ${order.shipping_address ? (order.shipping_address.length > 30 ? order.shipping_address.substring(0, 30) + '...' : order.shipping_address) : '未提供地址'}
+
+ |
+
+
+ ${order.product_name}
+ 数量: ${order.quantity}
+
+ |
+
+ $${order.total_amount.toFixed(2)}
+ |
+
+
+ ${getStatusText(order.payment_status, 'payment')}
+
+ |
+
+
+ ${getStatusText(order.shipping_status, 'shipping')}
+
+ |
+
+ ${formatDate(order.created_at)}
+ |
+
+
+ |
+
+ `).join('');
+}
+
+// 更新统计数据
+function updateStats(stats) {
+ if (stats) {
+ const totalElement = document.getElementById('totalOrders');
+ const pendingElement = document.getElementById('pendingOrders');
+ const shippedElement = document.getElementById('shippedOrders');
+
+ if (totalElement) totalElement.textContent = stats.total || 0;
+ if (pendingElement) pendingElement.textContent = stats.pending_ship || 0;
+ if (shippedElement) shippedElement.textContent = stats.shipped || 0;
+ }
+}
+
+// 搜索订单
+function searchOrders() {
+ const search = searchInput.value.trim();
+ const status = statusFilter.value;
+ loadOrders(search, status);
+}
+
+// 筛选订单
+function filterOrders() {
+ const search = searchInput.value.trim();
+ const status = statusFilter.value;
+ loadOrders(search, status);
+}
+
+// 显示订单详情
+async function showOrderDetail(orderId) {
+ try {
+ const response = await fetch(`/api/orders/${orderId}`);
+ const order = await response.json();
+
+ if (response.ok) {
+ const detailContent = document.getElementById('orderDetailContent');
+ detailContent.innerHTML = `
+
+
基本信息
+
+ 订单号:
+ ${order.order_id}
+
+
+ 支付ID:
+ ${order.payment_id || '未设置'}
+
+
+ 创建时间:
+ ${formatDate(order.created_at)}
+
+
+ 更新时间:
+ ${formatDate(order.updated_at)}
+
+
+
+
+
客户信息
+
+ 姓名:
+ ${order.customer_name}
+
+
+ 邮箱:
+ ${order.customer_email || '未提供'}
+
+
+ 电话:
+ ${order.customer_phone || '未提供'}
+
+
+ 收货地址:
+
+ ${order.shipping_address}
+
+
+
+
+
+
+
商品信息
+
+ 产品名称:
+ ${order.product_name}
+
+
+ 单价:
+ $${order.unit_price.toFixed(2)}
+
+
+ 数量:
+ ${order.quantity}
+
+
+ 总金额:
+ $${order.total_amount.toFixed(2)}
+
+
+
+
+
状态信息
+
+ 支付状态:
+
+
+ ${getStatusText(order.payment_status, 'payment')}
+
+
+
+
+ 发货状态:
+
+
+ ${getStatusText(order.shipping_status, 'shipping')}
+
+
+
+ ${order.tracking_number ? `
+
+ 快递单号:
+ ${order.tracking_number}
+
` : ''}
+ ${order.shipping_notes ? `
+
+ 发货备注:
+ ${order.shipping_notes}
+
` : ''}
+
+ `;
+
+ orderDetailModal.style.display = 'flex';
+ } else {
+ throw new Error(order.error || '获取订单详情失败');
+ }
+ } catch (error) {
+ console.error('Show order detail error:', error);
+ alert('获取订单详情失败: ' + error.message);
+ }
+}
+
+// 复制订单收货信息
+function copyOrderInfo(orderId) {
+ const order = currentOrders.find(o => o.order_id === orderId);
+ if (!order) return;
+
+ // 组合收货信息:姓名, 电话, 地址
+ let infoText = order.customer_name;
+ if (order.customer_phone) {
+ infoText += ', ' + order.customer_phone;
+ }
+ infoText += ', ' + order.shipping_address;
+
+ // 复制到剪贴板
+ copyText(infoText);
+}
+
+// 显示发货模态框
+function showShippingModal(orderId) {
+ currentOrderId = orderId;
+ const order = currentOrders.find(o => o.order_id === orderId);
+ if (!order) return;
+
+ // 清空表单
+ document.getElementById('trackingNumber').value = '';
+ document.getElementById('shippingNotes').value = '';
+
+ // 显示模态框
+ shippingModal.style.display = 'flex';
+ document.getElementById('trackingNumber').focus();
+}
+
+// 关闭发货模态框
+function closeShippingModal() {
+ shippingModal.style.display = 'none';
+ currentOrderId = null;
+}
+
+// 确认发货
+async function confirmShipping() {
+ const trackingNumber = document.getElementById('trackingNumber').value.trim();
+ const shippingNotes = document.getElementById('shippingNotes').value.trim();
+
+ if (!trackingNumber) {
+ alert('请输入运单号');
+ document.getElementById('trackingNumber').focus();
+ return;
+ }
+
+ if (!currentOrderId) {
+ alert('订单ID错误');
+ return;
+ }
+
+ try {
+ const response = await fetch(`/api/admin/orders/${currentOrderId}/shipping`, {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ shipping_status: 'shipped',
+ tracking_number: trackingNumber,
+ shipping_notes: shippingNotes
+ })
+ });
+
+ const result = await response.json();
+
+ if (response.ok) {
+ alert('订单已成功发货!');
+ closeShippingModal();
+ loadOrders(); // 重新加载订单列表
+ } else {
+ throw new Error(result.error || '发货失败');
+ }
+ } catch (error) {
+ console.error('Shipping error:', error);
+ alert('发货失败: ' + error.message);
+ }
+}
+
+// 确认并标记为已发货
+function confirmMarkAsShipped(orderId) {
+ if (confirm('确认标记此订单为已发货吗?')) {
+ markAsShipped(orderId);
+ }
+}
+
+// 一键标记为已发货
+async function markAsShipped(orderId) {
+ try {
+ const response = await fetch(`/api/admin/orders/${orderId}/shipping`, {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ shipping_status: 'shipped'
+ })
+ });
+
+ const result = await response.json();
+
+ if (response.ok) {
+ alert('订单已标记为发货!');
+ loadOrders(); // 重新加载订单列表
+ } else {
+ throw new Error(result.error || '标记发货失败');
+ }
+ } catch (error) {
+ console.error('Mark as shipped error:', error);
+ alert('标记发货失败: ' + error.message);
+ }
+}
+
+// 复制文本到剪贴板
+async function copyText(text) {
+ try {
+ await navigator.clipboard.writeText(text);
+ // 显示复制成功提示
+ showCopySuccess();
+ } catch (err) {
+ // 降级方案:使用传统方法
+ const textArea = document.createElement('textarea');
+ textArea.value = text;
+ document.body.appendChild(textArea);
+ textArea.select();
+ document.execCommand('copy');
+ document.body.removeChild(textArea);
+ showCopySuccess();
+ }
+}
+
+// 显示复制成功提示
+function showCopySuccess() {
+ // 创建提示元素
+ const toast = document.createElement('div');
+ toast.className = 'copy-toast';
+ toast.textContent = '已复制到剪贴板';
+ document.body.appendChild(toast);
+
+ // 3秒后移除提示
+ setTimeout(() => {
+ if (toast.parentNode) {
+ toast.parentNode.removeChild(toast);
+ }
+ }, 3000);
+}
+
+// 关闭订单详情模态框
+function closeOrderDetail() {
+ orderDetailModal.style.display = 'none';
+}
+
+// 获取状态文本
+function getStatusText(status, type) {
+ if (type === 'payment') {
+ switch(status) {
+ case 'pending': return '未支付';
+ case 'finished': return '已支付';
+ case 'failed': return '支付失败';
+ case 'confirming': return '确认中';
+ default: return status || '未知';
+ }
+ } else if (type === 'shipping') {
+ switch(status) {
+ case 'pending': return '未发货';
+ case 'shipped': return '已发货';
+ default: return status || '未知';
+ }
+ }
+ return status || '未知';
+}
+
+// 显示加载状态
+function showLoading() {
+ ordersTableBody.innerHTML = '
| 加载中... |
';
+}
+
+// 显示错误信息
+function showError(message) {
+ ordersTableBody.innerHTML = `
| 错误: ${message} |
`;
+}
+
+// 格式化日期
+function formatDate(dateString) {
+ if (!dateString) return '未设置';
+ const date = new Date(dateString);
+ return date.toLocaleString('zh-CN', {
+ year: 'numeric',
+ month: '2-digit',
+ day: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit'
+ });
+}
+
+// 键盘事件监听
+document.addEventListener('keydown', function(e) {
+ if (e.key === 'Escape') {
+ closeOrderDetail();
+ closeShippingModal();
+ }
+});
+
+// 搜索框回车事件
+searchInput.addEventListener('keypress', function(e) {
+ if (e.key === 'Enter') {
+ searchOrders();
+ }
+});
\ No newline at end of file
diff --git a/public/js/main.js b/public/js/main.js
new file mode 100644
index 0000000..8032ca8
--- /dev/null
+++ b/public/js/main.js
@@ -0,0 +1,369 @@
+// 全局变量
+let currentProduct = null;
+let currentOrder = null;
+
+// DOM 元素
+const productPrice = document.getElementById('product-price');
+const quantityInput = document.getElementById('quantity');
+const totalPriceElement = document.getElementById('total-price');
+const orderForm = document.getElementById('orderForm');
+const orderModal = document.getElementById('orderModal');
+const paymentStatus = document.getElementById('paymentStatus');
+
+// 初始化
+document.addEventListener('DOMContentLoaded', function() {
+ loadProductData();
+ setupEventListeners();
+});
+
+// 加载产品数据
+async function loadProductData() {
+ try {
+ const response = await fetch('/api/products');
+ const products = await response.json();
+
+ // 使用第一个产品作为展示产品
+ const productId = Object.keys(products)[0];
+ currentProduct = { id: productId, ...products[productId] };
+
+ // 更新界面
+ document.getElementById('product-name').textContent = currentProduct.name;
+ document.getElementById('product-description').textContent = currentProduct.description;
+ document.getElementById('product-price').textContent = currentProduct.price.toFixed(2);
+
+ updateTotalPrice();
+ } catch (error) {
+ console.error('加载产品数据失败:', error);
+ alert('加载产品信息失败,请刷新页面重试');
+ }
+}
+
+// 设置事件监听器
+function setupEventListeners() {
+ // 数量控制
+ document.getElementById('decrease-qty').addEventListener('click', () => {
+ const current = parseInt(quantityInput.value) || 1;
+ if (current > 1) {
+ quantityInput.value = current - 1;
+ updateTotalPrice();
+ }
+ });
+
+ document.getElementById('increase-qty').addEventListener('click', () => {
+ const current = parseInt(quantityInput.value) || 1;
+ if (current < 999) {
+ quantityInput.value = current + 1;
+ updateTotalPrice();
+ }
+ });
+
+ // 数量输入变化
+ quantityInput.addEventListener('input', updateTotalPrice);
+ quantityInput.addEventListener('change', validateQuantity);
+
+ // 表单提交
+ orderForm.addEventListener('submit', handleOrderSubmit);
+
+ // 模态框控制
+ document.getElementById('closeModal').addEventListener('click', closeOrderModal);
+ document.getElementById('pay-now-btn').addEventListener('click', handlePayNow);
+
+ // 支付状态检查
+ document.getElementById('check-status-btn').addEventListener('click', checkPaymentStatus);
+
+ // 点击模态框外部关闭
+ orderModal.addEventListener('click', (e) => {
+ if (e.target === orderModal) {
+ closeOrderModal();
+ }
+ });
+}
+
+// 更新总价
+function updateTotalPrice() {
+ if (!currentProduct) return;
+
+ const quantity = parseInt(quantityInput.value) || 1;
+ const total = (currentProduct.price * quantity).toFixed(2);
+ totalPriceElement.textContent = `$${total}`;
+}
+
+// 验证数量
+function validateQuantity() {
+ const value = parseInt(quantityInput.value);
+ if (isNaN(value) || value < 1) {
+ quantityInput.value = 1;
+ } else if (value > 999) {
+ quantityInput.value = 999;
+ }
+ updateTotalPrice();
+}
+
+// 处理订单提交
+async function handleOrderSubmit(e) {
+ e.preventDefault();
+
+ if (!currentProduct) {
+ alert('产品信息加载中,请稍后重试');
+ return;
+ }
+
+ // 获取表单数据
+ const formData = new FormData(orderForm);
+ const orderData = {
+ product_id: currentProduct.id,
+ quantity: parseInt(formData.get('quantity')),
+ customer_name: formData.get('customer_name').trim(),
+ customer_email: formData.get('customer_email').trim(),
+ customer_phone: formData.get('customer_phone').trim(),
+ shipping_address: formData.get('shipping_address').trim()
+ };
+
+ // 验证必填字段
+ if (!orderData.customer_name || !orderData.shipping_address) {
+ alert('请填写姓名和收货地址');
+ return;
+ }
+
+ // 禁用提交按钮
+ const submitBtn = document.getElementById('submit-order');
+ const btnText = submitBtn.querySelector('.btn-text');
+ const btnLoading = submitBtn.querySelector('.btn-loading');
+
+ submitBtn.disabled = true;
+ btnText.style.display = 'none';
+ btnLoading.style.display = 'inline';
+
+ try {
+ // 创建订单
+ const response = await fetch('/api/orders', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(orderData)
+ });
+
+ const result = await response.json();
+
+ if (result.success) {
+ currentOrder = {
+ ...orderData,
+ order_id: result.order_id,
+ total_amount: result.total_amount
+ };
+
+ showOrderModal();
+ } else {
+ throw new Error(result.error || '订单创建失败');
+ }
+ } catch (error) {
+ console.error('创建订单失败:', error);
+ alert('创建订单失败: ' + error.message);
+ } finally {
+ // 恢复提交按钮
+ submitBtn.disabled = false;
+ btnText.style.display = 'inline';
+ btnLoading.style.display = 'none';
+ }
+}
+
+// 显示订单确认模态框
+function showOrderModal() {
+ if (!currentOrder) return;
+
+ // 填充订单信息
+ document.getElementById('modal-order-id').textContent = currentOrder.order_id;
+ document.getElementById('modal-product-name').textContent = currentProduct.name;
+ document.getElementById('modal-quantity').textContent = currentOrder.quantity;
+ document.getElementById('modal-total-price').textContent = `$${currentOrder.total_amount.toFixed(2)} USDT`;
+ document.getElementById('modal-address').textContent = currentOrder.shipping_address;
+
+ orderModal.style.display = 'flex';
+}
+
+// 关闭订单模态框
+function closeOrderModal() {
+ orderModal.style.display = 'none';
+}
+
+// 处理支付
+async function handlePayNow() {
+ if (!currentOrder) return;
+
+ try {
+ // 创建支付
+ const response = await fetch('/api/payment/create', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ order_id: currentOrder.order_id
+ })
+ });
+
+ const result = await response.json();
+
+ if (result.success) {
+ // 检查是否为手动支付模式
+ if (result.manual_mode) {
+ // 关闭模态框,跳转到手动支付页面
+ closeOrderModal();
+ window.location.href = result.payment_url;
+ } else {
+ // 关闭模态框,直接跳转到UPay支付页面
+ closeOrderModal();
+
+ // 显示跳转提示
+ const jumpTip = document.createElement('div');
+ jumpTip.innerHTML = `
+
+
+
正在跳转到支付页面...
+
请在新页面完成USDT支付
+
+
+
+
+ `;
+ document.body.appendChild(jumpTip);
+
+ // 3秒后跳转到UPay支付页面
+ setTimeout(() => {
+ window.location.href = result.payment_url;
+ }, 3000);
+ }
+ } else {
+ throw new Error(result.error || '创建支付失败');
+ }
+ } catch (error) {
+ console.error('创建支付失败:', error);
+ alert('创建支付失败: ' + error.message);
+ }
+}
+
+// 显示支付状态页面
+function showPaymentStatus() {
+ if (!currentOrder) return;
+
+ document.getElementById('status-order-id').textContent = currentOrder.order_id;
+ document.getElementById('status-amount').textContent = currentOrder.total_amount.toFixed(2);
+
+ paymentStatus.style.display = 'flex';
+
+ // 开始监听支付状态
+ startPaymentStatusCheck();
+}
+
+// 开始支付状态检查
+function startPaymentStatusCheck() {
+ // 每10秒检查一次支付状态
+ const checkInterval = setInterval(async () => {
+ const status = await checkPaymentStatus();
+ if (status === 'finished' || status === 'failed') {
+ clearInterval(checkInterval);
+ }
+ }, 10000);
+}
+
+// 检查支付状态
+async function checkPaymentStatus() {
+ if (!currentOrder) return;
+
+ try {
+ const response = await fetch(`/api/orders/${currentOrder.order_id}`);
+ const order = await response.json();
+
+ const statusIcon = document.getElementById('status-icon');
+ const statusTitle = document.getElementById('status-title');
+ const statusMessage = document.getElementById('status-message');
+
+ switch (order.payment_status) {
+ case 'finished':
+ statusIcon.innerHTML = '
✓
';
+ statusTitle.textContent = '支付成功!';
+ statusMessage.textContent = '您的订单已确认,我们将尽快处理并发货。';
+ break;
+ case 'failed':
+ case 'expired':
+ statusIcon.innerHTML = '
✗
';
+ statusTitle.textContent = '支付失败';
+ statusMessage.textContent = '支付未完成或已过期,请重新下单。';
+ break;
+ case 'confirming':
+ statusTitle.textContent = '支付确认中...';
+ statusMessage.textContent = '我们已收到您的支付,正在等待区块链确认。';
+ break;
+ default:
+ statusTitle.textContent = '等待支付...';
+ statusMessage.textContent = '请完成USDT支付,我们正在等待您的交易。';
+ }
+
+ return order.payment_status;
+ } catch (error) {
+ console.error('检查支付状态失败:', error);
+ return null;
+ }
+}
+
+// 工具函数:验证邮箱
+function validateEmail(email) {
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ return emailRegex.test(email);
+}
+
+// 工具函数:验证电话号码
+function validatePhone(phone) {
+ const phoneRegex = /^[\d\s\-\+\(\)]+$/;
+ return phoneRegex.test(phone);
+}
+
+// 管理员登录相关函数
+function showAdminLogin() {
+ document.getElementById('adminLoginModal').style.display = 'flex';
+ document.getElementById('adminPassword').focus();
+}
+
+function closeAdminLogin() {
+ document.getElementById('adminLoginModal').style.display = 'none';
+ document.getElementById('adminPassword').value = '';
+ document.getElementById('loginError').style.display = 'none';
+}
+
+function verifyAdminPassword() {
+ const password = document.getElementById('adminPassword').value;
+ const errorDiv = document.getElementById('loginError');
+
+ if (password === '223388') {
+ // 密码正确,跳转到管理页面
+ window.location.href = '/admin.html';
+ } else {
+ // 密码错误
+ errorDiv.textContent = '密码错误,请重新输入';
+ errorDiv.style.display = 'block';
+ document.getElementById('adminPassword').value = '';
+ document.getElementById('adminPassword').focus();
+ }
+}
+
+// 键盘事件监听
+document.addEventListener('keydown', function(e) {
+ if (e.key === 'Escape') {
+ closeAdminLogin();
+ }
+});
+
+// 管理员登录密码框回车事件
+document.addEventListener('DOMContentLoaded', function() {
+ const adminPasswordInput = document.getElementById('adminPassword');
+ if (adminPasswordInput) {
+ adminPasswordInput.addEventListener('keypress', function(e) {
+ if (e.key === 'Enter') {
+ verifyAdminPassword();
+ }
+ });
+ }
+});
\ No newline at end of file
diff --git a/public/manual-payment.html b/public/manual-payment.html
new file mode 100644
index 0000000..e2cfbd5
--- /dev/null
+++ b/public/manual-payment.html
@@ -0,0 +1,222 @@
+
+
+
+
+
+
手动支付确认 - USDT商城
+
+
+
+
+
+
手动支付确认
+
由于UPay API暂时不可用,请手动确认支付状态
+
+
+
支付信息
+
+ 订单号:
+ -
+
+
+ 支付金额:
+ - USDT
+
+
+ 当前状态:
+ 待支付
+
+
+
+
+
模拟支付操作
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/success.html b/public/success.html
new file mode 100644
index 0000000..fd9f869
--- /dev/null
+++ b/public/success.html
@@ -0,0 +1,159 @@
+
+
+
+
+
+
支付成功 - USDT商城
+
+
+
+
+
+
+
支付成功!
+
感谢您的购买,我们已收到您的USDT支付
+
+
+
订单详情
+
+ 订单号:
+ -
+
+
+ 支付状态:
+ 已完成
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..c45105a
--- /dev/null
+++ b/server.js
@@ -0,0 +1,384 @@
+const express = require('express');
+const cors = require('cors');
+const bodyParser = require('body-parser');
+const sqlite3 = require('sqlite3').verbose();
+const path = require('path');
+const crypto = require('crypto');
+const axios = require('axios');
+
+const app = express();
+const PORT = process.env.PORT || 3000;
+
+// 中间件
+app.use(cors());
+app.use(bodyParser.json());
+app.use(bodyParser.urlencoded({ extended: true }));
+app.use(express.static('public'));
+
+// 数据库初始化
+const db = new sqlite3.Database('./database/shop.db');
+
+// 创建订单表
+db.serialize(() => {
+ db.run(`CREATE TABLE IF NOT EXISTS orders (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ order_id TEXT UNIQUE NOT NULL,
+ product_name TEXT NOT NULL,
+ quantity INTEGER NOT NULL,
+ unit_price REAL NOT NULL,
+ total_amount REAL NOT NULL,
+ customer_name TEXT NOT NULL,
+ customer_email TEXT,
+ customer_phone TEXT,
+ shipping_address TEXT NOT NULL,
+ payment_id TEXT,
+ payment_status TEXT DEFAULT 'pending',
+ shipping_status TEXT DEFAULT 'pending',
+ tracking_number TEXT,
+ shipping_notes TEXT,
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
+ )`);
+});
+
+// UPay 配置
+const UPAY_APP_ID = process.env.UPAY_APP_ID || 'E7c4dss9';
+const UPAY_APP_SECRET = process.env.UPAY_APP_SECRET || 'Hwc56INsabRau2yn';
+const UPAY_API_URL = 'https://api.upay.ink/v1/api/open';
+// const UPAY_API_URL = 'https://api-test.upay.ink/v1/api/open';
+
+// 产品配置
+const PRODUCTS = {
+ 'premium-product': {
+ name: '比特币彩票抽奖机',
+ price: 100,
+ description: '这是我们的比特币彩票抽奖机,质量优秀'
+ }
+};
+
+// 路由
+app.get('/', (req, res) => {
+ res.sendFile(path.join(__dirname, 'public', 'index.html'));
+});
+
+// 获取产品信息
+app.get('/api/products', (req, res) => {
+ res.json(PRODUCTS);
+});
+
+// 创建订单
+app.post('/api/orders', async (req, res) => {
+ try {
+ const {
+ product_id,
+ quantity,
+ customer_name,
+ customer_email,
+ customer_phone,
+ shipping_address
+ } = req.body;
+
+ // 验证产品
+ if (!PRODUCTS[product_id]) {
+ return res.status(400).json({ error: '无效的产品ID' });
+ }
+
+ const product = PRODUCTS[product_id];
+ const total_amount = product.price * quantity;
+ const order_id = 'ORDER_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
+
+ // 保存订单到数据库
+ const stmt = db.prepare(`
+ INSERT INTO orders (order_id, product_name, quantity, unit_price, total_amount,
+ customer_name, customer_email, customer_phone, shipping_address)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
+ `);
+
+ stmt.run([
+ order_id, product.name, quantity, product.price, total_amount,
+ customer_name, customer_email, customer_phone, shipping_address
+ ], function(err) {
+ if (err) {
+ console.error(err);
+ return res.status(500).json({ error: '订单创建失败' });
+ }
+
+ res.json({
+ success: true,
+ order_id,
+ total_amount,
+ message: '订单创建成功'
+ });
+ });
+
+ stmt.finalize();
+ } catch (error) {
+ console.error(error);
+ res.status(500).json({ error: '服务器错误' });
+ }
+});
+
+// UPay 签名生成函数
+function generateUpaySignature(params, appSecret) {
+ // 1. 过滤需要签名的参数,排除signature字段
+ const signParams = {};
+ Object.keys(params).forEach(key => {
+ if (key !== 'signature' && params[key] !== null && params[key] !== undefined && params[key] !== '') {
+ signParams[key] = params[key];
+ }
+ });
+
+ // 2. 按ASCII字典序排序参数
+ const sortedKeys = Object.keys(signParams).sort();
+
+ // 3. 拼接参数字符串 key1=value1&key2=value2
+ let signStr = '';
+ sortedKeys.forEach((key, index) => {
+ signStr += `${key}=${signParams[key]}`;
+ if (index < sortedKeys.length - 1) {
+ signStr += '&';
+ }
+ });
+
+ // 4. 在末尾追加&appSecret=密钥
+ signStr += `&appSecret=${appSecret}`;
+
+ console.log('Signature string:', signStr);
+
+ // 5. MD5加密并转大写
+ return crypto.createHash('md5').update(signStr, 'utf8').digest('hex').toUpperCase();
+}
+
+// 创建支付
+app.post('/api/payment/create', async (req, res) => {
+ try {
+ const { order_id } = req.body;
+
+ // 从数据库获取订单信息
+ db.get('SELECT * FROM orders WHERE order_id = ?', [order_id], async (err, order) => {
+ if (err || !order) {
+ return res.status(404).json({ error: '订单未找到' });
+ }
+
+ // 创建 UPay 支付订单
+ const paymentData = {
+ appId: UPAY_APP_ID,
+ merchantOrderNo: order_id,
+ chainType: '1', // USDT TRC20
+ fiatAmount: order.total_amount.toFixed(2),
+ fiatCurrency: 'USD',
+ notifyUrl: `${req.protocol}://${req.get('host')}/api/payment/callback`
+ };
+
+ // 生成签名
+ paymentData.signature = generateUpaySignature(paymentData, UPAY_APP_SECRET);
+
+ try {
+ const response = await axios.post(`${UPAY_API_URL}/order/apply`, paymentData, {
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ });
+
+ console.log('UPay API Response:', response.data);
+
+ if (response.data.code === '1' && response.data.message === 'success') {
+ // 更新订单支付信息
+ db.run(
+ 'UPDATE orders SET payment_id = ?, updated_at = CURRENT_TIMESTAMP WHERE order_id = ?',
+ [response.data.data.orderNo, order_id]
+ );
+
+ res.json({
+ success: true,
+ payment_url: response.data.data.payUrl,
+ payment_id: response.data.data.orderNo,
+ order_no: response.data.data.orderNo
+ });
+ } else {
+ throw new Error(response.data.message || '创建支付订单失败');
+ }
+ } catch (error) {
+ console.error('UPay API Error:', error.response?.data || error.message);
+ // 如果API失败,返回手动支付页面
+ const manualPaymentUrl = `/manual-payment.html?order_id=${order_id}`;
+ res.json({
+ success: true,
+ payment_url: manualPaymentUrl,
+ payment_id: 'MANUAL_' + order_id,
+ message: 'API不可用,请使用手动支付模式',
+ manual_mode: true
+ });
+ }
+ });
+ } catch (error) {
+ console.error(error);
+ res.status(500).json({ error: '服务器错误' });
+ }
+});
+
+// UPay 支付回调验证
+function verifyUpayCallback(params, signature, appSecret) {
+ const expectedSignature = generateUpaySignature(params, appSecret);
+ console.log('Expected signature:', expectedSignature);
+ console.log('Received signature:', signature);
+ return expectedSignature === signature;
+}
+
+// 支付回调
+app.post('/api/payment/callback', (req, res) => {
+ try {
+ console.log('Received callback:', req.body);
+ const callbackData = req.body;
+ const { signature, merchantOrderNo, orderNo, status } = callbackData;
+
+ // 验证签名
+ const paramsForSign = { ...callbackData };
+ delete paramsForSign.signature;
+
+ if (!verifyUpayCallback(paramsForSign, signature, UPAY_APP_SECRET)) {
+ console.error('UPay callback signature verification failed');
+ return res.status(400).send('FAIL');
+ }
+
+ // 映射支付状态
+ let paymentStatus = 'pending';
+ switch (status.toString()) {
+ case '1': // 订单完成
+ paymentStatus = 'finished';
+ break;
+ case '2': // 订单超时
+ case '3': // 订单失败
+ paymentStatus = 'failed';
+ break;
+ case '0': // 处理中
+ default:
+ paymentStatus = 'pending';
+ }
+
+ // 更新订单支付状态
+ db.run(
+ 'UPDATE orders SET payment_status = ?, payment_id = ?, updated_at = CURRENT_TIMESTAMP WHERE order_id = ?',
+ [paymentStatus, orderNo, merchantOrderNo],
+ (err) => {
+ if (err) {
+ console.error('Database update error:', err);
+ return res.status(500).send('FAIL');
+ }
+
+ console.log(`Order ${merchantOrderNo} payment status updated to: ${paymentStatus}`);
+ res.send('OK');
+ }
+ );
+ } catch (error) {
+ console.error('Callback error:', error);
+ res.status(500).send('FAIL');
+ }
+});
+
+// 获取订单状态
+app.get('/api/orders/:order_id', (req, res) => {
+ const { order_id } = req.params;
+
+ db.get('SELECT * FROM orders WHERE order_id = ?', [order_id], (err, order) => {
+ if (err || !order) {
+ return res.status(404).json({ error: '订单未找到' });
+ }
+
+ res.json(order);
+ });
+});
+
+// 获取所有订单列表(管理员)
+app.get('/api/admin/orders', (req, res) => {
+ const { status, search, limit = 50, offset = 0 } = req.query;
+
+ let query = 'SELECT * FROM orders';
+ let params = [];
+ let conditions = [];
+
+ if (status) {
+ conditions.push('payment_status = ? OR shipping_status = ?');
+ params.push(status, status);
+ }
+
+ if (search) {
+ conditions.push('(order_id LIKE ? OR customer_name LIKE ? OR customer_email LIKE ?)');
+ const searchTerm = `%${search}%`;
+ params.push(searchTerm, searchTerm, searchTerm);
+ }
+
+ if (conditions.length > 0) {
+ query += ' WHERE ' + conditions.join(' AND ');
+ }
+
+ query += ' ORDER BY created_at DESC LIMIT ? OFFSET ?';
+ params.push(parseInt(limit), parseInt(offset));
+
+ db.all(query, params, (err, orders) => {
+ if (err) {
+ console.error('Database error:', err);
+ return res.status(500).json({ error: '查询订单失败' });
+ }
+
+ // 获取订单统计
+ db.all(`
+ SELECT
+ COUNT(*) as total,
+ SUM(CASE WHEN payment_status = 'finished' AND shipping_status = 'pending' THEN 1 ELSE 0 END) as pending_ship,
+ SUM(CASE WHEN shipping_status = 'shipped' THEN 1 ELSE 0 END) as shipped,
+ SUM(CASE WHEN shipping_status = 'completed' THEN 1 ELSE 0 END) as completed
+ FROM orders
+ `, [], (err, stats) => {
+ if (err) {
+ console.error('Stats error:', err);
+ return res.json({ orders, stats: null });
+ }
+
+ res.json({
+ orders,
+ stats: stats[0] || { total: 0, pending_ship: 0, shipped: 0, completed: 0 }
+ });
+ });
+ });
+});
+
+// 更新订单发货状态
+app.put('/api/admin/orders/:order_id/shipping', (req, res) => {
+ const { order_id } = req.params;
+ const { shipping_status, tracking_number, shipping_notes } = req.body;
+
+ if (!['pending', 'shipped', 'completed'].includes(shipping_status)) {
+ return res.status(400).json({ error: '无效的发货状态' });
+ }
+
+ db.run(
+ `UPDATE orders SET
+ shipping_status = ?,
+ tracking_number = ?,
+ shipping_notes = ?,
+ updated_at = CURRENT_TIMESTAMP
+ WHERE order_id = ?`,
+ [shipping_status, tracking_number || null, shipping_notes || null, order_id],
+ function(err) {
+ if (err) {
+ console.error('Update shipping error:', err);
+ return res.status(500).json({ error: '更新发货状态失败' });
+ }
+
+ if (this.changes === 0) {
+ return res.status(404).json({ error: '订单未找到' });
+ }
+
+ res.json({
+ success: true,
+ message: '发货状态更新成功',
+ changes: this.changes
+ });
+ }
+ );
+});
+
+app.listen(PORT, () => {
+ console.log(`服务器运行在 http://localhost:${PORT}`);
+});
\ No newline at end of file