79 lines
2.6 KiB
Go
79 lines
2.6 KiB
Go
package service
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"affiliate_dash/internal/model"
|
|
)
|
|
|
|
func (s *DeliveryService) authorizeDeliveryLink(order *model.FulfillmentOrder, auth DeliveryLinkAuth) error {
|
|
if order == nil {
|
|
return newDeliveryHTTPError(http.StatusNotFound, "订单不存在")
|
|
}
|
|
if auth.Exp <= 0 || strings.TrimSpace(auth.Sign) == "" {
|
|
return newDeliveryHTTPError(http.StatusForbidden, "链接参数缺失")
|
|
}
|
|
if order.DeliveryLinkRevokedAt != nil {
|
|
return newDeliveryHTTPError(http.StatusForbidden, "发货链接已作废")
|
|
}
|
|
// 订单已取消后链接立即失效,防止已退款订单仍通过链接查看或继续发货。
|
|
if normalizeOrderStatus(order) == model.OrderStatusCancelled {
|
|
return newDeliveryHTTPError(http.StatusForbidden, "订单已取消,发货链接不可用")
|
|
}
|
|
if order.DeliveryLinkExpiresAt == nil {
|
|
return newDeliveryHTTPError(http.StatusForbidden, "发货链接未生成")
|
|
}
|
|
expiresAt := order.DeliveryLinkExpiresAt.UTC().Truncate(time.Second)
|
|
linkExpires := time.Unix(auth.Exp, 0).UTC()
|
|
if !linkExpires.Equal(expiresAt) {
|
|
return newDeliveryHTTPError(http.StatusForbidden, "发货链接已失效")
|
|
}
|
|
if time.Now().UTC().After(linkExpires) {
|
|
return newDeliveryHTTPError(http.StatusForbidden, "发货链接已过期")
|
|
}
|
|
expected := s.signDeliveryLink(order.OrderNo, auth.Exp)
|
|
if !hmac.Equal([]byte(strings.ToLower(strings.TrimSpace(auth.Sign))), []byte(expected)) {
|
|
return newDeliveryHTTPError(http.StatusForbidden, "发货链接签名无效")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *DeliveryService) buildDeliveryLinkResult(orderNo string, expiresAt time.Time, requestBaseURL string) *DeliveryLinkResult {
|
|
expiresAt = expiresAt.UTC()
|
|
exp := expiresAt.Unix()
|
|
sign := s.signDeliveryLink(orderNo, exp)
|
|
return &DeliveryLinkResult{
|
|
OrderNo: orderNo,
|
|
DeliveryURL: s.buildDeliveryURL(orderNo, exp, sign, requestBaseURL),
|
|
ExpiresAt: expiresAt,
|
|
Exp: exp,
|
|
Sign: sign,
|
|
}
|
|
}
|
|
|
|
func (s *DeliveryService) buildDeliveryURL(orderNo string, exp int64, sign, requestBaseURL string) string {
|
|
path := fmt.Sprintf("/delivery/%s/%s?exp=%d&sign=%s", url.PathEscape(s.channel), url.PathEscape(orderNo), exp, url.QueryEscape(sign))
|
|
base := strings.TrimRight(s.linkBaseURL, "/")
|
|
if base == "" {
|
|
base = strings.TrimRight(requestBaseURL, "/")
|
|
}
|
|
if base == "" {
|
|
return path
|
|
}
|
|
return base + path
|
|
}
|
|
|
|
func (s *DeliveryService) signDeliveryLink(orderNo string, exp int64) string {
|
|
mac := hmac.New(sha256.New, []byte(s.linkSecret))
|
|
_, _ = mac.Write([]byte(orderNo + "|" + strconv.FormatInt(exp, 10)))
|
|
return hex.EncodeToString(mac.Sum(nil))
|
|
}
|