package payment import ( "context" "errors" "time" "git.apinb.com/heqiapp/platforms/backend/api/internal/config" "git.apinb.com/heqiapp/platforms/backend/api/internal/impl" "git.apinb.com/heqiapp/platforms/backend/api/internal/models" "gorm.io/gorm" ) const ( StatusPending = 10 StatusPaid = 23 StatusClosed = 30 ) type CreateInput struct { RequestNo, BusinessType, BusinessIdentity, UserIdentity string Channel, PayType, Subject, OpenID string Amount int64 } // Create 创建幂等支付单并向渠道请求客户端调起参数。 func Create(ctx context.Context, input CreateInput) (models.PaymentOrder, error) { if input.Amount <= 0 || input.RequestNo == "" || input.BusinessIdentity == "" || input.UserIdentity == "" { return models.PaymentOrder{}, gorm.ErrInvalidData } var existing models.PaymentOrder if err := impl.DBService.Where("business_type = ? AND request_no = ?", input.BusinessType, input.RequestNo).First(&existing).Error; err == nil { if existing.BusinessIdentity != input.BusinessIdentity || existing.Amount != input.Amount || existing.Channel != input.Channel || existing.PayType != input.PayType { return existing, errors.New("idempotency conflict") } return existing, nil } order := models.PaymentOrder{Entity: models.Entity{Identity: models.NewIdentity(), Status: 1}, PaymentStatus: StatusPending, PaymentNo: "PAY" + time.Now().Format("20060102150405.000000"), RequestNo: input.RequestNo, BusinessType: input.BusinessType, BusinessIdentity: input.BusinessIdentity, UserIdentity: input.UserIdentity, MerchantIdentity: "platform", Channel: input.Channel, PayType: input.PayType, Amount: input.Amount, Subject: input.Subject, ExpiresAt: time.Now().Add(time.Duration(config.Spec.Payment.ExpireMinutes) * time.Minute)} args, err := createChannelOrder(ctx, order, input.OpenID) if err != nil { return order, err } order.ClientArgs = args if err = impl.DBService.Create(&order).Error; err != nil { return order, err } return order, nil } // PublicResponse 仅返回客户端调起支付所需的非密钥参数。 func PublicResponse(order models.PaymentOrder) map[string]any { response := map[string]any{"identity": order.Identity, "payment_no": order.PaymentNo, "payment_status": order.PaymentStatus, "channel": order.Channel, "pay_type": order.PayType, "amount": order.Amount, "client_args": order.ClientArgs, "expires_at": order.ExpiresAt} if order.Channel == "wechat" && order.PayType == "app" { response["app_id"] = config.Spec.Payment.Wechat.AppAppID } return response }