diff --git a/docs/docs.go b/docs/docs.go index 7a46e285..91af347b 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -589,6 +589,65 @@ const docTemplate = `{ } } }, + "/api/v1/projects/{id}/pending-payment": { + "get": { + "description": "只返回指定项目下当前用户已有且未过期的待支付订单,不重新占用库存或刷新有效期", + "produces": [ + "application/json" + ], + "tags": [ + "payment" + ], + "summary": "获取当前用户的待支付订单", + "parameters": [ + { + "type": "string", + "description": "项目ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/payment.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/payment.PendingPaymentResponseData" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/payment.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/project.ProjectResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/payment.Response" + } + } + } + } + }, "/api/v1/projects/{id}/receivers": { "get": { "consumes": [ @@ -977,6 +1036,29 @@ const docTemplate = `{ } } }, + "payment.PendingPaymentResponseData": { + "type": "object", + "properties": { + "amount": { + "type": "string" + }, + "has_pending": { + "type": "boolean" + }, + "pay_url": { + "type": "string" + } + } + }, + "payment.Response": { + "type": "object", + "properties": { + "data": {}, + "error_msg": { + "type": "string" + } + } + }, "project.CreateProjectRequestBody": { "type": "object", "required": [ diff --git a/docs/swagger.json b/docs/swagger.json index 04f9deae..8ff4ed27 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -580,6 +580,65 @@ } } }, + "/api/v1/projects/{id}/pending-payment": { + "get": { + "description": "只返回指定项目下当前用户已有且未过期的待支付订单,不重新占用库存或刷新有效期", + "produces": [ + "application/json" + ], + "tags": [ + "payment" + ], + "summary": "获取当前用户的待支付订单", + "parameters": [ + { + "type": "string", + "description": "项目ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/payment.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/payment.PendingPaymentResponseData" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/payment.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/project.ProjectResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/payment.Response" + } + } + } + } + }, "/api/v1/projects/{id}/receivers": { "get": { "consumes": [ @@ -968,6 +1027,29 @@ } } }, + "payment.PendingPaymentResponseData": { + "type": "object", + "properties": { + "amount": { + "type": "string" + }, + "has_pending": { + "type": "boolean" + }, + "pay_url": { + "type": "string" + } + } + }, + "payment.Response": { + "type": "object", + "properties": { + "data": {}, + "error_msg": { + "type": "string" + } + } + }, "project.CreateProjectRequestBody": { "type": "object", "required": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 8461a3c5..e31a2652 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -170,6 +170,21 @@ definitions: error_msg: type: string type: object + payment.PendingPaymentResponseData: + properties: + amount: + type: string + has_pending: + type: boolean + pay_url: + type: string + type: object + payment.Response: + properties: + data: {} + error_msg: + type: string + type: object project.CreateProjectRequestBody: properties: allow_same_ip: @@ -772,6 +787,42 @@ paths: $ref: '#/definitions/project.ProjectResponse' tags: - project + /api/v1/projects/{id}/pending-payment: + get: + description: 只返回指定项目下当前用户已有且未过期的待支付订单,不重新占用库存或刷新有效期 + parameters: + - description: 项目ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/payment.Response' + - properties: + data: + $ref: '#/definitions/payment.PendingPaymentResponseData' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/payment.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/project.ProjectResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/payment.Response' + summary: 获取当前用户的待支付订单 + tags: + - payment /api/v1/projects/{id}/receivers: get: consumes: diff --git a/frontend/components/common/receive/ReceiveContent.tsx b/frontend/components/common/receive/ReceiveContent.tsx index 4d7edc94..389194eb 100644 --- a/frontend/components/common/receive/ReceiveContent.tsx +++ b/frontend/components/common/receive/ReceiveContent.tsx @@ -17,6 +17,7 @@ import {ReceiveVerify, ReceiveVerifyRef} from '@/components/common/receive/Recei import services from '@/lib/services'; import {BasicUserInfo} from '@/lib/services/core'; import {GetProjectResponseData} from '@/lib/services/project'; +import {PendingPaymentData} from '@/lib/services/payment'; import {formatDate, formatDateTimeWithSeconds, copyToClipboard} from '@/lib/utils'; import {motion} from 'motion/react'; import {Separator} from '@/components/ui/separator'; @@ -46,7 +47,11 @@ interface ReceiveButtonProps { user: BasicUserInfo | null; currentTime: Date; isVerifying: boolean; + isCheckingPendingPayment: boolean; + isContinuingPayment: boolean; + pendingPayment: PendingPaymentData | null; onReceive: () => void; + onContinuePayment: () => void; } /** @@ -57,11 +62,17 @@ const ReceiveButton = ({ user, currentTime, isVerifying, + isCheckingPendingPayment, + isContinuingPayment, + pendingPayment, onReceive, + onContinuePayment, }: ReceiveButtonProps) => { const now = currentTime; const startTime = new Date(project.start_time); const endTime = new Date(project.end_time); + const priceNum = Number(project.price || '0'); + const isPaid = priceNum > 0; if (now < startTime) { const timeRemaining = getTimeRemainingText(startTime, now); @@ -82,6 +93,28 @@ const ReceiveButton = ({ ); } + if (isPaid && project.available_items_count <= 0 && isCheckingPendingPayment) { + return ( + + ); + } + + if (pendingPayment?.has_pending && pendingPayment.pay_url) { + return ( + + ); + } + if (project.available_items_count <= 0) { return (