feat: 通用授权后台、离线证、Waffo 支付页与仪表盘加强

支持多产品计划展示、LICOFF 离线签发、支付独立配置与调试,以及分析看板;同步更新 README 与集成文档。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Logan.LiandCursor committed 2026-08-12 20:25:46 +08:00
1 parent 0966375076
commit 8cc2c83f9d
41 files changed
+3177 -332

No files matched your search

@@ -146,7 +146,7 @@ func (s *OrderService) CreateOrder(req dto.CreateOrderRequest, clientIP, userAge
var checkoutURL string
if gateway.Gateway == "waffo" {
url, err := payment.BuildCheckoutURL(
payment.LoadWaffoConfig(),
payment.LoadWaffoConfig(s.db),
order.OrderNo,
strings.TrimSpace(req.DeviceID),
req.CustomerEmail,
@@ -311,12 +311,40 @@ func (s *OrderService) ClaimPurchase(req dto.ClaimPurchaseRequest) (*dto.ClaimPu
}, nil
}
// resolveWebhookOrder finds local order by merchant external id, Waffo order id, or buyer email. // Logan.Li
func (s *OrderService) resolveWebhookOrder(req dto.PaymentWebhookRequest) (model.Order, error) {
var order model.Order
if no := strings.TrimSpace(req.OrderNo); no != "" {
if err := s.db.Where("order_no = ?", no).First(&order).Error; err == nil {
return order, nil
}
// Hosted-store mistaken: Waffo ORD_ put in OrderNo — try gateway column. // Logan.Li
if err := s.db.Where("gateway_order_no = ?", no).First(&order).Error; err == nil {
return order, nil
}
}
if g := strings.TrimSpace(req.GatewayOrderNo); g != "" {
if err := s.db.Where("gateway_order_no = ?", g).First(&order).Error; err == nil {
return order, nil
}
}
if email := strings.TrimSpace(req.CustomerEmail); email != "" {
var customer model.Customer
if err := s.db.Where("email = ?", email).First(&customer).Error; err == nil {
if err := s.db.Where("customer_id = ? AND status = ?", customer.ID, "pending").
Order("created_at DESC").First(&order).Error; err == nil {
return order, nil
}
}
}
return order, errors.New("订单不存在")
}
// PaymentWebhook 支付回调(幂等 + 事务) // Logan.Li
func (s *OrderService) PaymentWebhook(req dto.PaymentWebhookRequest, clientIP string) error {
// 幂等检查 // Logan.Li
var order model.Order
if err := s.db.Where("order_no = ?", req.OrderNo).First(&order).Error; err != nil {
return errors.New("订单不存在")
order, err := s.resolveWebhookOrder(req)
if err != nil {
return err
}
if order.Status == "paid" || order.Status == "refunded" {
return nil // 已处理,幂等返回