feat(views): 添加订单确认页面并更新图标引用
- 添加新的订单确认页面模板 (order_confirm.html) - 启用 remixicon 图标库的样式引用 - 将成功图标从 ri-check-line 更新为 ri-check-double-fill- 添加 CLAUDE.md 开发指南文件 - 添加 Claude Code 本地设置文件
This commit is contained in:
9
.claude/settings.local.json
Normal file
9
.claude/settings.local.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(cat:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
}
|
||||
}
|
||||
117
CLAUDE.md
Normal file
117
CLAUDE.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Build and Run
|
||||
```bash
|
||||
# Build for production (Linux AMD64)
|
||||
./build.sh
|
||||
|
||||
# Development mode
|
||||
go run main.go
|
||||
|
||||
# Docker build
|
||||
docker build -t kami_shop . -f deploy/Dockerfile
|
||||
```
|
||||
|
||||
### Testing
|
||||
```bash
|
||||
# Run all tests
|
||||
go test ./...
|
||||
|
||||
# Run specific test file
|
||||
go test ./internal/service/scan_shop_test.go
|
||||
|
||||
# Run tests with verbose output
|
||||
go test -v ./...
|
||||
```
|
||||
|
||||
### Docker Deployment
|
||||
```bash
|
||||
# Build and deploy with Docker Compose
|
||||
export VERSION=latest
|
||||
docker-compose -f deploy/docker-compose.yaml up -d
|
||||
```
|
||||
|
||||
## Project Architecture
|
||||
|
||||
This is a **payment processing platform** built with Go and Beego framework, supporting multiple payment methods including WeChat Pay, Alipay, JD.com, Taobao, Pinduoduo, Apple Pay, Walmart, and various gift cards.
|
||||
|
||||
### Key Components
|
||||
|
||||
**Application Structure**:
|
||||
- `main.go` - Application entry point with OpenTelemetry initialization
|
||||
- `internal/controllers/` - HTTP handlers for payment processing
|
||||
- `internal/service/` - Business logic layer
|
||||
- `internal/models/` - Data models and database operations
|
||||
- `internal/routers/` - Route definitions
|
||||
- `views/` - HTML templates for payment pages (30+ payment method templates)
|
||||
- `conf/` - Configuration files (app.conf for production, app.local.conf for development)
|
||||
|
||||
**External Dependencies**:
|
||||
- **Database**: MySQL (development: `juhe_pay`, production: `kami`)
|
||||
- **Cache**: Redis for session management and caching
|
||||
- **Gateway Service**: `http://localhost:12309` (development) / `http://127.0.0.1:12309` (production)
|
||||
- **Partial Service**: `http://localhost:12310` (development) / `http://127.0.0.1:12310` (production)
|
||||
|
||||
### Configuration
|
||||
|
||||
**Development** (`conf/app.local.conf`):
|
||||
- Port: 12305, Host: localhost
|
||||
- Database: MySQL on localhost:3306
|
||||
- Redis: localhost:6379
|
||||
- Run mode: dev
|
||||
|
||||
**Production** (`conf/app.conf`):
|
||||
- Port: 12305, Host: 0.0.0.0
|
||||
- Database: MySQL on 127.0.0.1:3306
|
||||
- Redis: redis:6379 (Docker service)
|
||||
- Run mode: prod
|
||||
|
||||
### Technology Stack
|
||||
|
||||
- **Framework**: Beego v2.3.7
|
||||
- **Language**: Go 1.23.0
|
||||
- **Observability**: Complete OpenTelemetry stack (tracing, metrics, logging)
|
||||
- **Logging**: Zap structured logging
|
||||
- **Concurrency**: Goroutine pooling with ants/v2
|
||||
- **Security**: AES encryption, Edwards25519 cryptographic operations
|
||||
|
||||
### Development Patterns
|
||||
|
||||
**Code Organization**:
|
||||
- Controllers handle HTTP requests and validation
|
||||
- Service layer implements business logic
|
||||
- Models manage data persistence
|
||||
- Utils provide common functionality
|
||||
- Schema defines request/response structures
|
||||
|
||||
**Testing Strategy**:
|
||||
- Unit tests focused on service layer
|
||||
- Test files follow `*_test.go` naming convention
|
||||
- Use standard Go testing package
|
||||
|
||||
### Payment Processing Flow
|
||||
|
||||
1. Order creation through controller endpoints
|
||||
2. Service layer processes payment logic
|
||||
3. Integration with external payment gateways
|
||||
4. Notification handling for payment callbacks
|
||||
5. Template-based payment page generation
|
||||
6. Profit margin calculation and reporting
|
||||
|
||||
### Deployment
|
||||
|
||||
**Docker Environment**:
|
||||
- Multi-stage build using golang:1.23 builder
|
||||
- Alpine Linux runtime image
|
||||
- Volume mounts for configuration and logs
|
||||
- Network: 1panel-network
|
||||
- Health checks and automatic restarts
|
||||
|
||||
**CI/CD**:
|
||||
- Drone CI pipeline with SSH deployment
|
||||
- Docker image registry push
|
||||
- Production server deployment via Docker Compose
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.23 AS builder
|
||||
FROM golang:1.24 AS builder
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
|
||||
63
go.mod
63
go.mod
@@ -1,41 +1,42 @@
|
||||
module shop
|
||||
|
||||
go 1.23.0
|
||||
go 1.24.0
|
||||
|
||||
toolchain go1.23.6
|
||||
toolchain go1.24.6
|
||||
|
||||
require github.com/beego/beego/v2 v2.3.7
|
||||
require github.com/beego/beego/v2 v2.3.8
|
||||
|
||||
require (
|
||||
github.com/go-sql-driver/mysql v1.9.2
|
||||
github.com/duke-git/lancet/v2 v2.3.7
|
||||
github.com/go-sql-driver/mysql v1.9.3
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/panjf2000/ants/v2 v2.11.3
|
||||
github.com/widuu/gojson v0.0.0-20170212122013-7da9d2cd949b
|
||||
go.opentelemetry.io/contrib/bridges/otelzap v0.10.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0
|
||||
go.opentelemetry.io/otel v1.35.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.11.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0
|
||||
go.opentelemetry.io/otel/log v0.11.0
|
||||
go.opentelemetry.io/otel/sdk v1.35.0
|
||||
go.opentelemetry.io/otel/sdk/log v0.11.0
|
||||
go.opentelemetry.io/otel/trace v1.35.0
|
||||
go.opentelemetry.io/contrib/bridges/otelzap v0.13.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0
|
||||
go.opentelemetry.io/otel v1.38.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0
|
||||
go.opentelemetry.io/otel/log v0.14.0
|
||||
go.opentelemetry.io/otel/sdk v1.38.0
|
||||
go.opentelemetry.io/otel/sdk/log v0.14.0
|
||||
go.opentelemetry.io/otel/trace v1.38.0
|
||||
go.uber.org/zap v1.27.0
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
@@ -46,19 +47,19 @@ require (
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.8.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/net v0.35.0 // indirect
|
||||
golang.org/x/sync v0.13.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/grpc v1.71.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
golang.org/x/crypto v0.42.0 // indirect
|
||||
golang.org/x/net v0.44.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250922171735-9219d122eba9 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9 // indirect
|
||||
google.golang.org/grpc v1.75.1 // indirect
|
||||
google.golang.org/protobuf v1.36.9 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
138
go.sum
138
go.sum
@@ -1,34 +1,36 @@
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/beego/beego/v2 v2.3.7 h1:z4btKtjU/rfp5BiYHkGD2QPjK9i1E9GH+I7vfhn6Agk=
|
||||
github.com/beego/beego/v2 v2.3.7/go.mod h1:5cqHsOHJIxkq44tBpRvtDe59GuVRVv/9/tyVDxd5ce4=
|
||||
github.com/beego/beego/v2 v2.3.8 h1:wplhB1pF4TxR+2SS4PUej8eDoH4xGfxuHfS7wAk9VBc=
|
||||
github.com/beego/beego/v2 v2.3.8/go.mod h1:8vl9+RrXqvodrl9C8yivX1e6le6deCK6RWeq8R7gTTg=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/duke-git/lancet/v2 v2.3.7 h1:nnNBA9KyoqwbPm4nFmEFVIbXeAmpqf6IDCH45+HHHNs=
|
||||
github.com/duke-git/lancet/v2 v2.3.7/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
|
||||
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
|
||||
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
|
||||
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
@@ -41,8 +43,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.27 h1:drZCnuvf37yPfs95E5jd9s3XhdVWLal+6BOK6qrv6IU=
|
||||
github.com/mattn/go-sqlite3 v1.14.27/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
@@ -59,70 +61,78 @@ github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA
|
||||
github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02 h1:v9ezJDHA1XGxViAUSIoO/Id7Fl63u6d0YmsAm+/p2hs=
|
||||
github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02/go.mod h1:RF16/A3L0xSa0oSERcnhd8Pu3IXSDZSK2gmGIMsttFE=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/widuu/gojson v0.0.0-20170212122013-7da9d2cd949b h1:ieRJ8K7QAPWWltEOv7rzMruuPd7gbeAqTaBFhUECIy0=
|
||||
github.com/widuu/gojson v0.0.0-20170212122013-7da9d2cd949b/go.mod h1:9W1pyetRkwXqjR9tjOSrSuhGHBK0EqXoQSwWbhBHHwA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/bridges/otelzap v0.10.0 h1:ojdSRDvjrnm30beHOmwsSvLpoRF40MlwNCA+Oo93kXU=
|
||||
go.opentelemetry.io/contrib/bridges/otelzap v0.10.0/go.mod h1:oTTm4g7NEtHSV2i/0FeVdPaPgUIZPfQkFbq0vbzqnv0=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.11.0 h1:C/Wi2F8wEmbxJ9Kuzw/nhP+Z9XaHYMkyDmXy6yR2cjw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.11.0/go.mod h1:0Lr9vmGKzadCTgsiBydxr6GEZ8SsZ7Ks53LzjWG5Ar4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0 h1:0NIXxOCFx+SKbhCVxwl3ETG8ClLPAa0KuKV6p3yhxP8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0/go.mod h1:ChZSJbbfbl/DcRZNc9Gqh6DYGlfjw4PvO1pEOZH1ZsE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
|
||||
go.opentelemetry.io/otel/log v0.11.0 h1:c24Hrlk5WJ8JWcwbQxdBqxZdOK7PcP/LFtOtwpDTe3Y=
|
||||
go.opentelemetry.io/otel/log v0.11.0/go.mod h1:U/sxQ83FPmT29trrifhQg+Zj2lo1/IPN1PF6RTFqdwc=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/log v0.11.0 h1:7bAOpjpGglWhdEzP8z0VXc4jObOiDEwr3IYbhBnjk2c=
|
||||
go.opentelemetry.io/otel/sdk/log v0.11.0/go.mod h1:dndLTxZbwBstZoqsJB3kGsRPkpAgaJrWfQg3lhlHFFY=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/bridges/otelzap v0.13.0 h1:aBKdhLVieqvwWe9A79UHI/0vgp2t/s2euY8X59pGRlw=
|
||||
go.opentelemetry.io/contrib/bridges/otelzap v0.13.0/go.mod h1:SYqtxLQE7iINgh6WFuVi2AI70148B8EI35DSk0Wr8m4=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 h1:QQqYw3lkrzwVsoEX0w//EhH/TCnpRdEenKBOOEIMjWc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0/go.mod h1:gSVQcr17jk2ig4jqJ2DX30IdWH251JcNAecvrqTxH1s=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 h1:Oe2z/BCg5q7k4iXC3cqJxKYg0ieRiOqF0cecFYdPTwk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0/go.mod h1:ZQM5lAJpOsKnYagGg/zV2krVqTtaVdYdDkhMoX6Oalg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4=
|
||||
go.opentelemetry.io/otel/log v0.14.0 h1:2rzJ+pOAZ8qmZ3DDHg73NEKzSZkhkGIua9gXtxNGgrM=
|
||||
go.opentelemetry.io/otel/log v0.14.0/go.mod h1:5jRG92fEAgx0SU/vFPxmJvhIuDU9E1SUnEQrMlJpOno=
|
||||
go.opentelemetry.io/otel/log/logtest v0.14.0 h1:BGTqNeluJDK2uIHAY8lRqxjVAYfqgcaTbVk1n3MWe5A=
|
||||
go.opentelemetry.io/otel/log/logtest v0.14.0/go.mod h1:IuguGt8XVP4XA4d2oEEDMVDBBCesMg8/tSGWDjuKfoA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/log v0.14.0 h1:JU/U3O7N6fsAXj0+CXz21Czg532dW2V4gG1HE/e8Zrg=
|
||||
go.opentelemetry.io/otel/sdk/log v0.14.0/go.mod h1:imQvII+0ZylXfKU7/wtOND8Hn4OpT3YUoIgqJVksUkM=
|
||||
go.opentelemetry.io/otel/sdk/log/logtest v0.14.0 h1:Ijbtz+JKXl8T2MngiwqBlPaHqc4YCaP/i13Qrow6gAM=
|
||||
go.opentelemetry.io/otel/sdk/log/logtest v0.14.0/go.mod h1:dCU8aEL6q+L9cYTqcVOk8rM9Tp8WdnHOPLiBgp0SGOA=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/proto/otlp v1.8.0 h1:fRAZQDcAFHySxpJ1TwlA1cJ4tvcrw7nXl9xWWC8N5CE=
|
||||
go.opentelemetry.io/proto/otlp v1.8.0/go.mod h1:tIeYOeNBU4cvmPqpaji1P+KbB4Oloai8wN4rWzRrFF0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
|
||||
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
|
||||
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw=
|
||||
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250922171735-9219d122eba9 h1:jm6v6kMRpTYKxBRrDkYAitNJegUeO1Mf3Kt80obv0gg=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250922171735-9219d122eba9/go.mod h1:LmwNphe5Afor5V3R5BppOULHOnt2mCIf+NxMd4XiygE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9 h1:V1jCN2HBa8sySkR5vLcCSqJSTMv093Rw9EJefhQGP7M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
|
||||
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
|
||||
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
||||
@@ -19,6 +19,10 @@ func GetMMValue() (gUrl string) {
|
||||
return fmt.Sprintf("%s/gateway/getAllowedMM", GetGateway())
|
||||
}
|
||||
|
||||
func GetMerchantOrder() (gUrl string) {
|
||||
return fmt.Sprintf("%s/gateway/merchant/query", GetGateway())
|
||||
}
|
||||
|
||||
func GetPortalUrl() (url string) {
|
||||
url = env.Get("portalAddr", url)
|
||||
if url != "" {
|
||||
|
||||
@@ -2,6 +2,7 @@ package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/url"
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
"shop/internal/models"
|
||||
"shop/internal/models/merchant_deploy"
|
||||
"shop/internal/schema/request"
|
||||
"shop/internal/schema/response"
|
||||
"shop/internal/service"
|
||||
"shop/internal/traceRouter"
|
||||
"shop/internal/utils"
|
||||
@@ -33,6 +35,14 @@ type HomeAction struct {
|
||||
web.Controller
|
||||
}
|
||||
|
||||
func (c *HomeAction) OrderConfirm() {
|
||||
c.Data["amount"] = c.GetString("amount")
|
||||
c.Data["orderId"] = c.GetString("orderId")
|
||||
c.Data["returnUrl"] = c.GetString("returnUrl")
|
||||
c.Data["payKey"] = c.GetString("payKey")
|
||||
c.TplName = "order_confirm.html"
|
||||
}
|
||||
|
||||
// ShowHome /*加载首页及数据*/
|
||||
func (c *HomeAction) ShowHome() {
|
||||
ctx, span := traceRouter.CreateSpan(c.Ctx.Request.Context(), "span", "首页")
|
||||
@@ -69,21 +79,50 @@ func (c *HomeAction) ShowHome() {
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["sign"] = sign
|
||||
resp, err := client.Get(ctx, config.GetMMValue(), map[string]string{
|
||||
"productCode": m.ProductCode,
|
||||
"showMMValue": fmt.Sprintf("%f", m.ShowMMValue),
|
||||
"payKey": m.PayKey,
|
||||
})
|
||||
if err != nil {
|
||||
merchantInfo := service.GetMerchantByPasskey(ctx, m.PayKey)
|
||||
if merchantInfo.Id == 0 {
|
||||
flash := web.NewFlash()
|
||||
traceRouter.Logger.WithContext(ctx).Error("签名错误,请检查签名是否正确!", zap.String("sign", err.Error()))
|
||||
flash.Error("获取面额失败!")
|
||||
flash.Error("商户不存在,请联系客服")
|
||||
flash.Store(&c.Controller)
|
||||
c.Redirect("/error.html", 302)
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["sign"] = sign
|
||||
queryData := request.QueryOrder{
|
||||
Appkey: m.PayKey,
|
||||
OrderNo: m.OrderNo,
|
||||
Timestamp: time.Now().Unix(),
|
||||
}
|
||||
queryData.Sign = utils.GetMD5SignMF(queryData.ToStrMap(), merchantInfo.MerchantSecret)
|
||||
//判断订单状态
|
||||
if resp, err2 := client.PostWithFormData(ctx, config.GetMerchantOrder(), map[string]string{}, queryData.ToMap()); err2 == nil {
|
||||
respData := response.MerchantQueryResp{}
|
||||
_ = json.Unmarshal([]byte(resp), &respData)
|
||||
switch respData.Data.Status {
|
||||
case "success":
|
||||
str := "/ok.html" + "?orderId=" + respData.Data.OrderNo + "&amount=" + respData.Data.Amount +
|
||||
"&returnUrl=" + returnUrl
|
||||
c.Ctx.Output.Header("Location", str)
|
||||
c.Redirect("/ok.html", 302)
|
||||
return
|
||||
case "fail":
|
||||
str := "/error.html" + "?orderId=" + respData.Data.OrderNo + "&amount=" + respData.Data.Amount +
|
||||
"&returnUrl=" + returnUrl + "&errorMsg=" + respData.Data.CardReturnData
|
||||
c.Ctx.Output.Header("Location", str)
|
||||
c.Redirect(str, 302)
|
||||
case "wait":
|
||||
str := "/order_confirm.html" + "?orderId=" + respData.Data.OrderNo + "&returnUrl=" + returnUrl +
|
||||
"&payKey=" + m.PayKey + "&amount=" + respData.Data.Amount
|
||||
c.Ctx.Output.Header("Location", str)
|
||||
c.Redirect(str, 302)
|
||||
}
|
||||
}
|
||||
resp, err := client.Get(ctx, config.GetMMValue(), map[string]string{
|
||||
"productCode": m.ProductCode,
|
||||
"showMMValue": fmt.Sprintf("%f", m.ShowMMValue),
|
||||
"payKey": m.PayKey,
|
||||
})
|
||||
r, err := client.Unmarshal(resp)
|
||||
if err != nil {
|
||||
flash := web.NewFlash()
|
||||
@@ -175,15 +214,6 @@ func (c *HomeAction) ShowHome() {
|
||||
Timestamp: time.Now().Unix(),
|
||||
}
|
||||
|
||||
merchantInfo := service.GetMerchantByPasskey(ctx, m.PayKey)
|
||||
if merchantInfo.Id == 0 {
|
||||
flash := web.NewFlash()
|
||||
flash.Error("商户不存在,请联系客服")
|
||||
flash.Store(&c.Controller)
|
||||
c.Redirect("/error.html", 302)
|
||||
return
|
||||
}
|
||||
|
||||
requestOrder.Sign = utils.GetMD5SignMF(requestOrder.ToStrMap(), merchantInfo.MerchantSecret)
|
||||
//创建订单
|
||||
response, err := client.Post(ctx, service.CreateOrderHost, map[string]string{}, requestOrder.ToMap())
|
||||
@@ -290,18 +320,107 @@ func (c *HomeAction) Notify() {
|
||||
// ShowOK /*加载首页及数据*/
|
||||
func (c *HomeAction) ShowOK() {
|
||||
//取值
|
||||
orderNo := c.GetString("orderNo")
|
||||
faceMM := c.GetString("faceMM")
|
||||
c.Data["orderNo"] = orderNo
|
||||
c.Data["faceMM"] = faceMM
|
||||
c.Data["orderId"] = c.GetString("orderId")
|
||||
c.Data["amount"] = c.GetString("faceMM")
|
||||
c.Data["returnUrl"], _ = web.AppConfig.String("returnUrl")
|
||||
c.TplName = "ok.html"
|
||||
}
|
||||
|
||||
func (c *HomeAction) ErrorPage() {
|
||||
flash := web.ReadFromRequest(&c.Controller)
|
||||
e := flash.Data["error"]
|
||||
c.Data["error"] = e
|
||||
c.Data["orderId"] = c.GetString("orderId")
|
||||
c.Data["amount"] = c.GetString("amount")
|
||||
c.Data["errorReason"] = c.GetString("errorMsg")
|
||||
c.Data["returnUrl"], _ = web.AppConfig.String("returnUrl")
|
||||
c.TplName = "error.html"
|
||||
}
|
||||
|
||||
// QueryOrderStatus 查询订单状态
|
||||
func (c *HomeAction) QueryOrderStatus() {
|
||||
ctx, span := traceRouter.CreateSpan(c.Ctx.Request.Context(), "span", "查询订单状态")
|
||||
defer span.End()
|
||||
|
||||
orderNo := c.GetString("orderNo")
|
||||
payKey := c.GetString("payKey")
|
||||
returnUrl := c.GetString("returnUrl")
|
||||
|
||||
if orderNo == "" || payKey == "" {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 400,
|
||||
"msg": "订单编号或支付密钥不能为空",
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
merchantInfo := service.GetMerchantByPasskey(ctx, payKey)
|
||||
if merchantInfo.Id == 0 {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 400,
|
||||
"msg": "商户不存在",
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
queryData := request.QueryOrder{
|
||||
Appkey: payKey,
|
||||
OrderNo: orderNo,
|
||||
Timestamp: time.Now().Unix(),
|
||||
}
|
||||
queryData.Sign = utils.GetMD5SignMF(queryData.ToStrMap(), merchantInfo.MerchantSecret)
|
||||
|
||||
// 判断订单状态
|
||||
if resp, err2 := client.PostWithFormData(ctx, config.GetMerchantOrder(), map[string]string{}, queryData.ToMap()); err2 == nil {
|
||||
respData := response.MerchantQueryResp{}
|
||||
_ = json.Unmarshal([]byte(resp), &respData)
|
||||
|
||||
switch respData.Data.Status {
|
||||
case "success":
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 200,
|
||||
"msg": "支付成功",
|
||||
"data": map[string]interface{}{
|
||||
"status": "success",
|
||||
"orderNo": respData.Data.OrderNo,
|
||||
"amount": respData.Data.Amount,
|
||||
"returnUrl": returnUrl,
|
||||
},
|
||||
}
|
||||
case "fail":
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 200,
|
||||
"msg": "支付失败",
|
||||
"data": map[string]interface{}{
|
||||
"status": "fail",
|
||||
"orderNo": respData.Data.OrderNo,
|
||||
"amount": respData.Data.Amount,
|
||||
"errorReason": respData.Data.CardReturnData,
|
||||
"returnUrl": returnUrl,
|
||||
},
|
||||
}
|
||||
case "wait":
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 200,
|
||||
"msg": "等待支付",
|
||||
"data": map[string]interface{}{
|
||||
"status": "wait",
|
||||
},
|
||||
}
|
||||
default:
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 200,
|
||||
"msg": "处理中",
|
||||
"data": map[string]interface{}{
|
||||
"status": "processing",
|
||||
},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 500,
|
||||
"msg": "查询订单状态失败",
|
||||
}
|
||||
}
|
||||
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ func (c *PayController) Pay() {
|
||||
defer span.End()
|
||||
|
||||
flash := web.NewFlash()
|
||||
// ctx := context.Background()
|
||||
returnUrl := strings.TrimSpace(c.GetString("returnUrl"))
|
||||
orderNo := strings.TrimSpace(c.GetString("orderId"))
|
||||
productCode := strings.TrimSpace(c.GetString("productCode"))
|
||||
@@ -38,8 +37,10 @@ func (c *PayController) Pay() {
|
||||
if orderNo == "" {
|
||||
flash.Error("订单号为空")
|
||||
flash.Store(&c.Controller)
|
||||
c.Ctx.Output.Header("Location", "/error.html")
|
||||
c.Redirect("/error.html", 302)
|
||||
str := "/error.html" + "?orderId=" + orderNo + "&amount=" + "0" +
|
||||
"&returnUrl=" + returnUrl + "&errorMsg=" + "订单号为空!"
|
||||
c.Ctx.Output.Header("Location", str)
|
||||
c.Redirect(str, 302)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -47,42 +48,48 @@ func (c *PayController) Pay() {
|
||||
if !c.judgeAmount(faceMM) {
|
||||
flash.Error("金额有误")
|
||||
flash.Store(&c.Controller)
|
||||
c.Ctx.Output.Header("Location", "/error.html")
|
||||
c.Redirect("/error.html", 302)
|
||||
str := "/error.html" + "?orderId=" + orderNo + "&amount=" + faceMM +
|
||||
"&returnUrl=" + returnUrl + "&errorMsg=" + "金额有误!"
|
||||
c.Ctx.Output.Header("Location", str)
|
||||
c.Redirect(str, 302)
|
||||
return
|
||||
}
|
||||
|
||||
m := models.OrderParams{}
|
||||
m.Decrypt(ctx, sign)
|
||||
if time.Since(time.Unix(m.GeneratedTime, 0)).Hours() > float64(m.Duration) {
|
||||
flash.Error("订单超时!")
|
||||
flash.Store(&c.Controller)
|
||||
c.Ctx.Output.Header("Location", "/error.html")
|
||||
c.Redirect("/error.html", 302)
|
||||
str := "/error.html" + "?orderId=" + orderNo + "&amount=" + faceMM +
|
||||
"&returnUrl=" + returnUrl + "&errorMsg=" + "订单超时!"
|
||||
c.Ctx.Output.Header("Location", str)
|
||||
c.Redirect(str, 302)
|
||||
return
|
||||
}
|
||||
|
||||
if m.PayKey == "" {
|
||||
flash.Error("支付秘钥错误!")
|
||||
flash.Store(&c.Controller)
|
||||
c.Ctx.Output.Header("Location", "/error.html")
|
||||
c.Redirect("/error.html", 302)
|
||||
str := "/error.html" + "?orderId=" + orderNo + "&amount=" + faceMM +
|
||||
"&returnUrl=" + returnUrl + "&errorMsg=" + "支付秘钥错误!"
|
||||
c.Ctx.Output.Header("Location", str)
|
||||
c.Redirect(str, 302)
|
||||
return
|
||||
}
|
||||
|
||||
if m.NotifyUrl == "" {
|
||||
flash.Error("通知地址为空!")
|
||||
flash.Store(&c.Controller)
|
||||
c.Ctx.Output.Header("Location", "/error.html")
|
||||
c.Redirect("/error.html", 302)
|
||||
str := "/error.html" + "?orderId=" + orderNo + "&amount=" + faceMM +
|
||||
"&returnUrl=" + returnUrl + "&errorMsg=" + "通知地址为空!"
|
||||
c.Ctx.Output.Header("Location", str)
|
||||
c.Redirect(str, 302)
|
||||
return
|
||||
}
|
||||
|
||||
if m.OrderNo == "" {
|
||||
flash.Error("订单号为空!")
|
||||
flash.Store(&c.Controller)
|
||||
c.Ctx.Output.Header("Location", "/error.html")
|
||||
c.Redirect("/error.html", 302)
|
||||
str := "/error.html" + "?orderId=" + orderNo + "&amount=" + faceMM +
|
||||
"&returnUrl=" + returnUrl + "&errorMsg=" + "订单号为空!"
|
||||
c.Ctx.Output.Header("Location", str)
|
||||
c.Redirect(str, 302)
|
||||
return
|
||||
}
|
||||
pp := map[string]string{
|
||||
@@ -92,10 +99,11 @@ func (c *PayController) Pay() {
|
||||
}
|
||||
marshal, err := json.Marshal(&pp)
|
||||
if err != nil {
|
||||
flash.Error("卡密数据解析错误!")
|
||||
flash.Store(&c.Controller)
|
||||
c.Ctx.Output.Header("Location", "/error.html")
|
||||
c.Redirect("/error.html", 302)
|
||||
str := "/error.html" + "?orderId=" + orderNo + "&amount=" + faceMM +
|
||||
"&returnUrl=" + returnUrl + "&errorMsg=" + "卡密数据解析错误!"
|
||||
c.Ctx.Output.Header("Location", str)
|
||||
c.Redirect(str, 302)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -113,10 +121,7 @@ func (c *PayController) Pay() {
|
||||
|
||||
res := scanShop.Shop(ctx, m.PayKey)
|
||||
if res.Code == 200 {
|
||||
str := "/ok.html" + "?orderNo=" + orderNo + "&faceMM=" + faceMM
|
||||
if returnUrl != "" {
|
||||
str += "&returnUrl=" + returnUrl
|
||||
}
|
||||
str := "/ok.html" + "?orderId=" + orderNo + "&amount=" + faceMM + "&returnUrl=" + returnUrl
|
||||
c.Ctx.Output.Header("Location", str)
|
||||
c.Redirect(str, 302)
|
||||
return
|
||||
@@ -124,15 +129,10 @@ func (c *PayController) Pay() {
|
||||
|
||||
flash.Error(res.Msg)
|
||||
flash.Store(&c.Controller)
|
||||
str := "/error.html"
|
||||
if returnUrl != "" {
|
||||
str += "?returnUrl=" + returnUrl
|
||||
}
|
||||
str := "/error.html" + "?orderId=" + orderNo + "&amount=" + faceMM +
|
||||
"&returnUrl=" + returnUrl + "&errorMsg=" + "不存在这样的支付类型!"
|
||||
c.Ctx.Output.Header("Location", str)
|
||||
c.Redirect(str, 302)
|
||||
flash.Error("不存在这样的支付类型")
|
||||
flash.Store(&c.Controller)
|
||||
c.Redirect("/error.html", 302)
|
||||
}
|
||||
|
||||
// PayWithJson 后端接口支付
|
||||
|
||||
@@ -21,7 +21,9 @@ func init() {
|
||||
web.Router("/pay.html", &controllers.PayController{}, "*:Pay")
|
||||
web.Router("/pay", &controllers.PayController{}, "*:PayWithJson")
|
||||
web.Router("/error.html", &controllers.HomeAction{}, "*:ErrorPage")
|
||||
web.Router("/order-confirm.html", &controllers.HomeAction{}, "*:OrderConfirm")
|
||||
web.Router("/ok.html", &controllers.HomeAction{}, "*:ShowOK")
|
||||
web.Router("/shop/notify", &controllers.HomeAction{}, "*:Notify")
|
||||
web.Router("/order/pay/original/jd", &controllers.PayController{}, "*:PayOriginalJD")
|
||||
web.Router("/api/query-order-status", &controllers.HomeAction{}, "*:QueryOrderStatus")
|
||||
}
|
||||
|
||||
34
internal/schema/request/merchant.go
Normal file
34
internal/schema/request/merchant.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package request
|
||||
|
||||
import "strconv"
|
||||
|
||||
// "appKey": m.PayKey,
|
||||
// "orderNo": m.OrderNo,
|
||||
// "timestamp": time.Now().Unix(),
|
||||
|
||||
type QueryOrder struct {
|
||||
Appkey string `json:"appkey" valid:"Required;"`
|
||||
OrderNo string `json:"orderNo" valid:"Required;"`
|
||||
Timestamp int64 `json:"timestamp" valid:"Required;"`
|
||||
Sign string `json:"sign" valid:"Required;"`
|
||||
}
|
||||
|
||||
func (q *QueryOrder) ToMap() map[string]interface{} {
|
||||
data := map[string]interface{}{
|
||||
"appKey": q.Appkey,
|
||||
"orderNo": q.OrderNo,
|
||||
"timestamp": q.Timestamp,
|
||||
"sign": q.Sign,
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (q *QueryOrder) ToStrMap() map[string]string {
|
||||
data := map[string]string{
|
||||
"appKey": q.Appkey,
|
||||
"orderNo": q.OrderNo,
|
||||
"timestamp": strconv.FormatInt(q.Timestamp, 10),
|
||||
"sign": q.Sign,
|
||||
}
|
||||
return data
|
||||
}
|
||||
16
internal/schema/response/merchant.go
Normal file
16
internal/schema/response/merchant.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package response
|
||||
|
||||
// MerchantQueryResp 是一个泛型响应结构体,可以用于任何数据类型
|
||||
type MerchantQueryResp struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
OrderNo string `json:"orderNo"`
|
||||
CardNo string `json:"cardNo"`
|
||||
CardPwd string `json:"cardPwd"`
|
||||
Status string `json:"status"`
|
||||
FaceVal float64 `json:"faceVal"`
|
||||
Amount string `json:"amount"`
|
||||
CardReturnData string `json:"cardReturnData"`
|
||||
} `json:"data"`
|
||||
}
|
||||
@@ -54,7 +54,6 @@ func (c *ScanShopController) Shop(ctx context.Context, payKey string) *ResponseJ
|
||||
merchantInfo := GetMerchantByPasskey(ctx, payKey)
|
||||
c.Params["sign"] = utils.GetMD5SignMF(c.Params, merchantInfo.MerchantSecret)
|
||||
|
||||
traceRouter.Logger.WithContext(ctx).Info(fmt.Sprintf("requestUrl %s", reqUrl), zap.Any("params", c.Params))
|
||||
req := httplib.NewBeegoRequestWithCtx(ctx, reqUrl, "POST").
|
||||
SetTimeout(30*time.Second, 30*time.Second).
|
||||
SetTransport(otelhttp.NewTransport(http.DefaultTransport))
|
||||
@@ -64,6 +63,7 @@ func (c *ScanShopController) Shop(ctx context.Context, payKey string) *ResponseJ
|
||||
}
|
||||
|
||||
response, err := req.String()
|
||||
traceRouter.Logger.WithContext(ctx).Info("请求参数", zap.String("请求地址", reqUrl), zap.Any("请求参数", c.Params))
|
||||
if err != nil {
|
||||
traceRouter.Logger.WithContext(ctx).Error(fmt.Sprintf("err %s", err.Error()))
|
||||
responseJSON.Code = -1
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"shop/internal/traceRouter"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
"go.uber.org/zap"
|
||||
|
||||
@@ -32,8 +33,7 @@ func Get(ctx context.Context, baseUrl string, params map[string]string) (result
|
||||
return "", err
|
||||
}
|
||||
realUrl.RawQuery = urlParams.Encode()
|
||||
traceRouter.Logger.WithContext(ctx).Info("请求地址", zap.String("url", realUrl.String()))
|
||||
req, err := http.NewRequestWithContext(context.WithoutCancel(ctx), "GET", realUrl.String(), nil)
|
||||
req, err := http.NewRequestWithContext(context.Background(), "GET", realUrl.String(), nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -55,7 +55,7 @@ func Get(ctx context.Context, baseUrl string, params map[string]string) (result
|
||||
}
|
||||
|
||||
func Post(ctx context.Context, baseUrl string, params map[string]string, data map[string]interface{}) (result string, err error) {
|
||||
ctx, span := traceRouter.CreateSpan(context.WithoutCancel(ctx), "通讯", "Post")
|
||||
ctx, span := traceRouter.CreateSpan(context.Background(), "通讯", "Post")
|
||||
defer span.End()
|
||||
urlParams := url.Values{}
|
||||
for k, v := range params {
|
||||
@@ -74,6 +74,29 @@ func Post(ctx context.Context, baseUrl string, params map[string]string, data ma
|
||||
return response, err
|
||||
}
|
||||
|
||||
func PostWithFormData(ctx context.Context, baseUrl string, params map[string]string, data map[string]interface{}) (result string, err error) {
|
||||
ctx, span := traceRouter.CreateSpan(ctx, "通讯", "Post")
|
||||
defer span.End()
|
||||
urlParams := url.Values{}
|
||||
for k, v := range params {
|
||||
urlParams.Add(k, v)
|
||||
}
|
||||
realUrl, err := url.Parse(baseUrl)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
realUrl.RawQuery = urlParams.Encode()
|
||||
req := httplib.NewBeegoRequestWithCtx(ctx, realUrl.String(), "POST").
|
||||
SetTransport(otelhttp.NewTransport(http.DefaultTransport)).
|
||||
SetTimeout(30*time.Second, 30*time.Second)
|
||||
for s, i := range data {
|
||||
req.Param(s, convertor.ToString(i))
|
||||
}
|
||||
response, err := req.String()
|
||||
traceRouter.Logger.WithContext(ctx).Info("请求地址", zap.String("url", realUrl.String()), zap.String("response", response))
|
||||
return response, err
|
||||
}
|
||||
|
||||
func Unmarshal(s string) (Response, error) {
|
||||
var r Response
|
||||
err := json.Unmarshal([]byte(s), &r)
|
||||
|
||||
2938
static/css/remixicon.css
Normal file
2938
static/css/remixicon.css
Normal file
File diff suppressed because it is too large
Load Diff
0
static/fonts/.gitkeep
Normal file
0
static/fonts/.gitkeep
Normal file
454
views/error.html
454
views/error.html
@@ -6,189 +6,399 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>支付失败</title>
|
||||
<!-- 引入字体 -->
|
||||
<link rel="stylesheet" href="../static/css/index-anxin.css">
|
||||
<!-- 引入图标库 -->
|
||||
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/remixicon/4.6.0/remixicon.min.css">
|
||||
<!-- 引入动画库 -->
|
||||
<link rel="stylesheet" href="../static/css/animate.min.css">
|
||||
<!-- 引入图标库 -->
|
||||
<link rel="stylesheet" href="../static/css/index-anxin.css">
|
||||
<style>
|
||||
/* 全局样式和重置 */
|
||||
:root {
|
||||
--error-red: #ff3b30;
|
||||
--error-light-red: rgba(255, 59, 48, 0.15);
|
||||
--apple-white: #ffffff;
|
||||
--apple-gray: #8e8e93;
|
||||
--apple-light-gray: #f5f5f7;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body,
|
||||
html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Noto Sans SC', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background: #ffffff;
|
||||
/* 主背景改为白色 */
|
||||
color: #333333;
|
||||
/* 主要文字颜色改为深色 */
|
||||
font-family: 'SF Pro Text', 'SF Pro Display', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background: var(--apple-white);
|
||||
color: #1d1d1f;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
/* 防止滚动 */
|
||||
}
|
||||
|
||||
/* 容器样式 */
|
||||
.container {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
padding: 40px 30px;
|
||||
/* 增加上下内边距 */
|
||||
max-width: 400px;
|
||||
/* 限制最大宽度 */
|
||||
width: 90%;
|
||||
background: linear-gradient(135deg, #FF8E8E 0%, #FF6B6B 100%);
|
||||
/* 容器红色渐变背景 */
|
||||
color: #ffffff;
|
||||
/* 容器内文字颜色改为白色 */
|
||||
border-radius: 20px;
|
||||
/* 圆角调整 */
|
||||
box-shadow: 0 10px 25px rgba(255, 107, 107, 0.3);
|
||||
/* 调整阴影以适应白色背景 */
|
||||
/* backdrop-filter: blur(10px); */
|
||||
/* 移除毛玻璃效果 */
|
||||
/* border: 1px solid rgba(255, 255, 255, 0.2); */
|
||||
/* 移除边框 */
|
||||
animation: fadeIn 1s ease-out;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* 错误图标样式 */
|
||||
/* 背景效果 */
|
||||
.bg-shapes {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bg-shape {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
filter: blur(70px);
|
||||
}
|
||||
|
||||
.bg-shape:nth-child(1) {
|
||||
top: -10%;
|
||||
left: -10%;
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
background: linear-gradient(to right, rgba(255, 59, 48, 0.2), rgba(255, 59, 48, 0));
|
||||
animation: float 15s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.bg-shape:nth-child(2) {
|
||||
bottom: -15%;
|
||||
right: -10%;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
background: linear-gradient(to left, rgba(255, 59, 48, 0.15), rgba(255, 59, 48, 0));
|
||||
animation: float 20s ease-in-out infinite reverse;
|
||||
}
|
||||
|
||||
.bg-shape:nth-child(3) {
|
||||
top: 40%;
|
||||
left: 60%;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
background: linear-gradient(to top, rgba(255, 59, 48, 0.1), rgba(255, 59, 48, 0));
|
||||
animation: float 18s ease-in-out infinite 2s;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0% {
|
||||
transform: translate(0, 0) rotate(0deg);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate(30px, 20px) rotate(5deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(0, 0) rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* 玻璃态效果 */
|
||||
.glass {
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.05);
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
/* 错误图标容器 */
|
||||
.error-icon-container {
|
||||
position: relative;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin: 0 auto 30px;
|
||||
}
|
||||
|
||||
/* 错误图标 */
|
||||
.error-icon {
|
||||
font-size: 70px;
|
||||
/* 图标大小调整 */
|
||||
color: #ffffff;
|
||||
/* 图标颜色改为白色,因为背景是红色 */
|
||||
margin-bottom: 20px;
|
||||
/* text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); */
|
||||
/* 移除阴影 */
|
||||
animation: bounceIn 1s ease;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: var(--error-red);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: white;
|
||||
font-size: 50px;
|
||||
box-shadow: 0 10px 25px rgba(255, 59, 48, 0.3);
|
||||
animation: scaleIn 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
|
||||
}
|
||||
|
||||
/* 镭射效果 */
|
||||
.laser-effect {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
background: radial-gradient(circle, rgba(255, 59, 48, 0.8) 0%, rgba(255, 59, 48, 0) 70%);
|
||||
border-radius: 50%;
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.laser-effect:nth-child(2) {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
animation-delay: 0.5s;
|
||||
}
|
||||
|
||||
.laser-effect:nth-child(3) {
|
||||
width: 210px;
|
||||
height: 210px;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
/* 标题样式 */
|
||||
h1 {
|
||||
font-size: 26px;
|
||||
/* 字体稍大 */
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 15px;
|
||||
color: #ffffff;
|
||||
/* 标题颜色改为白色 */
|
||||
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.1);
|
||||
color: #1d1d1f;
|
||||
font-family: 'SF Pro Display', sans-serif;
|
||||
animation: fadeInUp 0.8s ease forwards;
|
||||
animation-delay: 0.3s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* 描述文本样式 */
|
||||
p {
|
||||
font-size: 16px;
|
||||
font-size: 17px;
|
||||
font-weight: 400;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 35px;
|
||||
/* 增加底部间距 */
|
||||
color: #ffffff;
|
||||
/* 描述文本颜色改为白色 */
|
||||
opacity: 0.95;
|
||||
/* 略微提高不透明度 */
|
||||
line-height: 1.5;
|
||||
margin-bottom: 30px;
|
||||
color: var(--apple-gray);
|
||||
animation: fadeInUp 0.8s ease forwards;
|
||||
animation-delay: 0.5s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* 错误信息卡片 */
|
||||
.error-card {
|
||||
padding: 25px;
|
||||
margin-bottom: 25px;
|
||||
text-align: left;
|
||||
animation: fadeInUp 0.8s ease forwards;
|
||||
animation-delay: 0.7s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.error-detail {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 15px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.error-detail:last-child {
|
||||
margin-bottom: 0;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
color: var(--apple-gray);
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
font-weight: 500;
|
||||
color: #1d1d1f;
|
||||
}
|
||||
|
||||
.error-reason {
|
||||
color: var(--error-red);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 按钮样式 */
|
||||
.btn-container {
|
||||
animation: fadeInUp 0.8s ease forwards;
|
||||
animation-delay: 0.9s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 12px 35px;
|
||||
/* 增加左右内边距 */
|
||||
background: #ffffff;
|
||||
/* 按钮背景改为白色 */
|
||||
color: #FF6B6B;
|
||||
/* 按钮文字颜色改为红色 */
|
||||
font-size: 17px;
|
||||
/* 字体稍大 */
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
border-radius: 12px;
|
||||
/* 圆角调整 */
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
border: none;
|
||||
/* 移除边框 */
|
||||
box-shadow: 0 5px 15px rgba(255, 107, 107, 0.3);
|
||||
/* 调整阴影 */
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 12px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
margin-bottom: 15px;
|
||||
font-family: 'SF Pro Text', sans-serif;
|
||||
}
|
||||
|
||||
/* 按钮悬停/点击效果 */
|
||||
.btn:hover {
|
||||
background: #fff5f5;
|
||||
/* 悬停时淡红色背景 */
|
||||
box-shadow: 0 7px 18px rgba(255, 107, 107, 0.4);
|
||||
/* 悬停时阴影变化 */
|
||||
.btn-primary {
|
||||
background: var(--error-red);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(255, 59, 48, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #d70015;
|
||||
transform: translateY(-2px);
|
||||
/* 轻微上移 */
|
||||
box-shadow: 0 6px 20px rgba(255, 59, 48, 0.4);
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
background: #ffeaea;
|
||||
/* 点击时更深的淡红色 */
|
||||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
/* 点击时模拟按下效果 */
|
||||
transform: translateY(1px);
|
||||
/* 轻微下移 */
|
||||
.btn-secondary {
|
||||
background: transparent;
|
||||
color: var(--error-red);
|
||||
border: 2px solid var(--error-red);
|
||||
}
|
||||
|
||||
/* 定义动画 */
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background: var(--error-light-red);
|
||||
}
|
||||
|
||||
@keyframes bounceIn {
|
||||
/* 动画定义 */
|
||||
@keyframes scaleIn {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
opacity: 0;
|
||||
transform: scale(0.3) translateY(-50px);
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1.1) translateY(10px);
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: scale(0.9) translateY(-5px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1) translateY(0);
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: translate(-50%, -50%) scale(0.8);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(-50%, -50%) scale(1.2);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
0% {
|
||||
transform: translateY(20px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 订单编号样式 */
|
||||
.order-id {
|
||||
font-size: 14px;
|
||||
color: var(--apple-gray);
|
||||
margin-bottom: 20px;
|
||||
word-break: break-all;
|
||||
animation: fadeInUp 0.8s ease forwards;
|
||||
animation-delay: 0.7s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* 安全提示 */
|
||||
.security-note {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
font-size: 14px;
|
||||
color: var(--apple-gray);
|
||||
animation: fadeInUp 0.8s ease forwards;
|
||||
animation-delay: 1.1s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.security-note i {
|
||||
margin-right: 6px;
|
||||
font-size: 16px;
|
||||
color: var(--error-red);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container animate__animated animate__fadeIn">
|
||||
<!-- 错误图标 -->
|
||||
<div class="error-icon animate__animated animate__bounceIn">
|
||||
<i class="ri-close-circle-fill"></i>
|
||||
</div>
|
||||
<!-- 错误标题 -->
|
||||
<h1>支付失败</h1>
|
||||
<!-- 错误描述 -->
|
||||
<p>抱歉,您的支付未能成功处理。<br>{{.error}}<br>请检查您的支付信息或稍后再试。</p>
|
||||
<!-- 操作按钮 -->
|
||||
<!-- <a href="#" class="btn animate__animated animate__pulse animate__infinite animate__slower">重试</a> -->
|
||||
<!-- 可以添加返回按钮 -->
|
||||
<!-- <a href="#" class="btn" style="margin-left: 15px;">返回首页</a> -->
|
||||
<!-- 背景效果 -->
|
||||
<div class="bg-shapes">
|
||||
<div class="bg-shape"></div>
|
||||
<div class="bg-shape"></div>
|
||||
<div class="bg-shape"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 可选的 JavaScript 交互
|
||||
// 例如,按钮点击事件处理
|
||||
document.querySelector('.btn').addEventListener('click', function (e) {
|
||||
e.preventDefault(); // 阻止默认链接行为
|
||||
// 添加重试逻辑或页面跳转
|
||||
alert('正在尝试重新支付...');
|
||||
// window.location.href = 'retry-payment-url'; // 跳转到重试链接
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
<div class="container">
|
||||
<!-- 错误图标 -->
|
||||
<div class="error-icon-container">
|
||||
<div class="laser-effect"></div>
|
||||
<div class="laser-effect"></div>
|
||||
<div class="laser-effect"></div>
|
||||
<div class="error-icon">
|
||||
<i class="ri-close-circle-fill"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 错误标题 -->
|
||||
<h1>支付失败</h1>
|
||||
|
||||
<!-- 错误描述 -->
|
||||
<p>抱歉,您的支付未能成功处理。</p>
|
||||
|
||||
<!-- 订单编号 -->
|
||||
<div class="order-id">
|
||||
订单编号:{{.orderId}}
|
||||
</div>
|
||||
|
||||
<!-- 错误信息卡片 -->
|
||||
<div class="error-card glass">
|
||||
<div class="error-detail">
|
||||
<span class="detail-label">订单金额</span>
|
||||
<span class="detail-value" style="font-size: 18px; color: var(--error-red);">¥{{.amount}}</span>
|
||||
</div>
|
||||
<div class="error-detail">
|
||||
<span class="detail-label">失败原因</span>
|
||||
<span class="detail-value error-reason">{{.errorReason}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 安全提示 -->
|
||||
<div class="security-note">
|
||||
<i class="ri-shield-warning-line"></i>
|
||||
<span>您的支付信息安全</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -10,7 +10,7 @@
|
||||
href="https://fonts.googleapis.com/css2?family=SF+Pro+Display:wght@400;500;600;700&family=SF+Pro+Text:wght@400;500;600&display=swap"
|
||||
rel="stylesheet">
|
||||
<!-- 引入图标库 -->
|
||||
<!-- <link rel="stylesheet" href="../static/css/remixicon.min.css"> -->
|
||||
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/remixicon/4.6.0/remixicon.min.css">
|
||||
<!-- 引入动画库 -->
|
||||
<link rel="stylesheet" href="../static/css/animate.min.css">
|
||||
<!-- 引入图标库 -->
|
||||
@@ -293,9 +293,10 @@
|
||||
.order-id {
|
||||
font-size: 14px;
|
||||
color: var(--apple-gray);
|
||||
margin-top: 30px;
|
||||
margin-bottom: 20px;
|
||||
word-break: break-all;
|
||||
animation: fadeInUp 0.8s ease forwards;
|
||||
animation-delay: 0.9s;
|
||||
animation-delay: 0.7s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@@ -335,7 +336,7 @@
|
||||
<div class="laser-effect"></div>
|
||||
<div class="laser-effect"></div>
|
||||
<div class="success-icon">
|
||||
<i class="ri-check-line"></i>
|
||||
<i class="ri-check-fill"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -345,17 +346,22 @@
|
||||
<!-- 成功描述 -->
|
||||
<p>您的卡密已提交完成,请耐心等待处理结果。感谢您的配合</p>
|
||||
|
||||
<!-- 订单编号 -->
|
||||
<div class="order-id">
|
||||
订单编号:{{.orderId}}
|
||||
</div>
|
||||
|
||||
<!-- 交易信息卡片 -->
|
||||
<div class="transaction-card glass">
|
||||
<div class="transaction-detail">
|
||||
<span class="detail-label">交易状态</span>
|
||||
<span class="detail-value" style="color: var(--success-green); display: flex; align-items: center;">
|
||||
<span class="completed-icon"><i class="ri-check-line"></i></span>卡密已提交
|
||||
<span class="completed-icon"><i class="ri-check-fill"></i></span>卡密已提交
|
||||
</span>
|
||||
</div>
|
||||
<div class="transaction-detail">
|
||||
<span class="detail-label">交易金额</span>
|
||||
<span class="detail-value" style="font-size: 18px; color: #1d1d1f;">¥{{.faceMM}}</span>
|
||||
<span class="detail-value" style="font-size: 18px; color: #1d1d1f;">¥{{.amount}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
488
views/order_confirm.html
Normal file
488
views/order_confirm.html
Normal file
@@ -0,0 +1,488 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>订单确认</title>
|
||||
<!-- 引入字体 -->
|
||||
<!-- 引入图标库 -->
|
||||
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/remixicon/4.6.0/remixicon.min.css">
|
||||
<!-- 引入动画库 -->
|
||||
<link rel="stylesheet" href="../static/css/animate.min.css">
|
||||
<!-- 引入图标库 -->
|
||||
<link rel="stylesheet" href="../static/css/index-anxin.css">
|
||||
<style>
|
||||
/* 全局样式和重置 */
|
||||
:root {
|
||||
--primary-blue: #007aff;
|
||||
--primary-light-blue: rgba(0, 122, 255, 0.15);
|
||||
--apple-white: #ffffff;
|
||||
--apple-gray: #8e8e93;
|
||||
--apple-light-gray: #f5f5f7;
|
||||
--border-color: #e5e5ea;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body,
|
||||
html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'SF Pro Text', 'SF Pro Display', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background: var(--apple-white);
|
||||
color: #1d1d1f;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 容器样式 */
|
||||
.container {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
padding: 40px 30px;
|
||||
max-width: 400px;
|
||||
width: 90%;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* 背景效果 */
|
||||
.bg-shapes {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bg-shape {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
filter: blur(70px);
|
||||
}
|
||||
|
||||
.bg-shape:nth-child(1) {
|
||||
top: -10%;
|
||||
left: -10%;
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
background: linear-gradient(to right, rgba(0, 122, 255, 0.2), rgba(0, 122, 255, 0));
|
||||
animation: float 15s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.bg-shape:nth-child(2) {
|
||||
bottom: -15%;
|
||||
right: -10%;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
background: linear-gradient(to left, rgba(0, 122, 255, 0.15), rgba(0, 122, 255, 0));
|
||||
animation: float 20s ease-in-out infinite reverse;
|
||||
}
|
||||
|
||||
.bg-shape:nth-child(3) {
|
||||
top: 40%;
|
||||
left: 60%;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
background: linear-gradient(to top, rgba(0, 122, 255, 0.1), rgba(0, 122, 255, 0));
|
||||
animation: float 18s ease-in-out infinite 2s;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0% {
|
||||
transform: translate(0, 0) rotate(0deg);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate(30px, 20px) rotate(5deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(0, 0) rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* 玻璃态效果 */
|
||||
.glass {
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.05);
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
/* 确认图标容器 */
|
||||
.confirm-icon-container {
|
||||
position: relative;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin: 0 auto 30px;
|
||||
}
|
||||
|
||||
/* 确认图标 */
|
||||
.confirm-icon {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: var(--primary-blue);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: white;
|
||||
font-size: 40px;
|
||||
box-shadow: 0 10px 25px rgba(0, 122, 255, 0.3);
|
||||
animation: scaleIn 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
|
||||
}
|
||||
|
||||
/* 镭射效果 */
|
||||
.laser-effect {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
background: radial-gradient(circle, rgba(0, 122, 255, 0.8) 0%, rgba(0, 122, 255, 0) 70%);
|
||||
border-radius: 50%;
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.laser-effect:nth-child(2) {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
animation-delay: 0.5s;
|
||||
}
|
||||
|
||||
.laser-effect:nth-child(3) {
|
||||
width: 210px;
|
||||
height: 210px;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
/* 标题样式 */
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 15px;
|
||||
color: #1d1d1f;
|
||||
font-family: 'SF Pro Display', sans-serif;
|
||||
animation: fadeInUp 0.8s ease forwards;
|
||||
animation-delay: 0.3s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* 描述文本样式 */
|
||||
p {
|
||||
font-size: 17px;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 30px;
|
||||
color: var(--apple-gray);
|
||||
animation: fadeInUp 0.8s ease forwards;
|
||||
animation-delay: 0.5s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* 订单信息卡片 */
|
||||
.order-card {
|
||||
padding: 25px;
|
||||
margin-bottom: 25px;
|
||||
text-align: left;
|
||||
animation: fadeInUp 0.8s ease forwards;
|
||||
animation-delay: 0.7s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.order-detail {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 15px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.order-detail:last-child {
|
||||
margin-bottom: 0;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
color: var(--apple-gray);
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
font-weight: 500;
|
||||
color: #1d1d1f;
|
||||
}
|
||||
|
||||
/* 按钮样式 */
|
||||
.btn-container {
|
||||
animation: fadeInUp 0.8s ease forwards;
|
||||
animation-delay: 0.9s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
margin-bottom: 15px;
|
||||
font-family: 'SF Pro Text', sans-serif;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--primary-blue);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(0, 122, 255, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #0051d5;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(0, 122, 255, 0.4);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: transparent;
|
||||
color: var(--primary-blue);
|
||||
border: 2px solid var(--primary-blue);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: var(--primary-light-blue);
|
||||
}
|
||||
|
||||
/* 动画定义 */
|
||||
@keyframes scaleIn {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: translate(-50%, -50%) scale(0.8);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(-50%, -50%) scale(1.2);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
0% {
|
||||
transform: translateY(20px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 订单编号样式 */
|
||||
.order-id {
|
||||
font-size: 14px;
|
||||
color: var(--apple-gray);
|
||||
margin-bottom: 20px;
|
||||
word-break: break-all;
|
||||
animation: fadeInUp 0.8s ease forwards;
|
||||
animation-delay: 0.7s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* 状态标签 */
|
||||
.status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4px 12px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
|
||||
/* 旋转动画 */
|
||||
.animate-spin {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.status-pending {
|
||||
background: rgba(255, 149, 0, 0.15);
|
||||
color: #ff9500;
|
||||
}
|
||||
|
||||
.status-completed {
|
||||
background: rgba(52, 199, 89, 0.15);
|
||||
color: #34c759;
|
||||
}
|
||||
|
||||
.status-processing {
|
||||
background: rgba(0, 122, 255, 0.15);
|
||||
color: #007aff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- 背景效果 -->
|
||||
<div class="bg-shapes">
|
||||
<div class="bg-shape"></div>
|
||||
<div class="bg-shape"></div>
|
||||
<div class="bg-shape"></div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<!-- 确认图标 -->
|
||||
<div class="confirm-icon-container">
|
||||
<div class="laser-effect"></div>
|
||||
<div class="laser-effect"></div>
|
||||
<div class="laser-effect"></div>
|
||||
<div class="confirm-icon">
|
||||
<i class="ri-loader-4-line animate-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 确认标题 -->
|
||||
<h1>订单确认</h1>
|
||||
|
||||
<!-- 确认描述 -->
|
||||
<p>请稍候,我们正在确认您的订单。完成后我们将通知您。</p>
|
||||
|
||||
<!-- 订单编号 -->
|
||||
<div class="order-id">
|
||||
订单编号:{{.orderId}}
|
||||
</div>
|
||||
|
||||
<!-- 订单信息卡片 -->
|
||||
<div class="order-card glass">
|
||||
<div class="order-detail">
|
||||
<span class="detail-label">订单金额</span>
|
||||
<span class="detail-value" style="font-size: 18px; color: var(--primary-blue);">¥{{.amount}}</span>
|
||||
</div>
|
||||
<div class="order-detail">
|
||||
<span class="detail-label">订单状态</span>
|
||||
<span class="detail-value">
|
||||
<span class="status-badge status-processing">处理中</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 订单状态查询参数
|
||||
const orderParams = {
|
||||
orderNo: '{{.orderId}}',
|
||||
payKey: '{{.payKey}}',
|
||||
returnUrl: '{{.returnUrl}}'
|
||||
};
|
||||
|
||||
// 检查订单状态
|
||||
async function checkOrderStatus() {
|
||||
try {
|
||||
const response = await fetch('/api/query-order-status', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: new URLSearchParams(orderParams)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.code === 200) {
|
||||
const data = result.data;
|
||||
|
||||
switch (data.status) {
|
||||
case 'success':
|
||||
// 支付成功,跳转到成功页面
|
||||
let successUrl = '/ok.html?orderNo=' + data.orderNo + '&faceMM=' + data.amount;
|
||||
if (data.returnUrl) {
|
||||
successUrl += '&returnUrl=' + encodeURIComponent(data.returnUrl);
|
||||
}
|
||||
window.location.href = successUrl;
|
||||
break;
|
||||
|
||||
case 'fail':
|
||||
// 支付失败,跳转到失败页面
|
||||
let errorUrl = '/error.html?orderNo=' + data.orderNo + '&faceMM=' + data.amount + '&errorReason=' + encodeURIComponent(data.errorReason || '支付失败');
|
||||
if (data.returnUrl) {
|
||||
errorUrl += '&returnUrl=' + encodeURIComponent(data.returnUrl);
|
||||
}
|
||||
window.location.href = errorUrl;
|
||||
break;
|
||||
|
||||
case 'wait':
|
||||
case 'processing':
|
||||
// 继续等待,不做处理
|
||||
console.log('订单状态:', data.status);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
console.error('查询订单状态失败:', result.msg);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('请求失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 每3秒检查一次订单状态
|
||||
const statusInterval = setInterval(checkOrderStatus, 3000);
|
||||
|
||||
// 页面卸载时清除定时器
|
||||
window.addEventListener('beforeunload', () => {
|
||||
clearInterval(statusInterval);
|
||||
});
|
||||
|
||||
// 页面加载完成后立即检查一次
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
checkOrderStatus();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user