feat(server): webhook 薄壳化——保留 X-Pangolin HMAC 协议,铸码走库单事务原语(#codes-lib)
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
libcodes "github.com/wangjia/codes"
|
||||
"github.com/wangjia/pangolin/server/internal/apierr"
|
||||
)
|
||||
|
||||
@@ -132,25 +134,22 @@ func (h *WebhookHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
hash := Hash(canonical)
|
||||
|
||||
// --- Idempotent code insertion ---
|
||||
// --- Idempotent code insertion (via shared lib primitives) ---
|
||||
ctx := r.Context()
|
||||
|
||||
planID, err := h.store.GetPlanID(ctx, PlanCode(strings.ToLower(payload.Plan)))
|
||||
// 未知 plan 先拒(行为不变,旧 webhook.go:138-142)。
|
||||
if _, err := h.store.GetPlanID(ctx, PlanCode(strings.ToLower(payload.Plan))); err != nil {
|
||||
apierr.WriteJSON(w, http.StatusBadRequest, apierr.ErrBadRequest)
|
||||
return
|
||||
}
|
||||
ent, err := libcodes.NewDurationEntitlement(strings.ToLower(payload.Plan), payload.DurationDays)
|
||||
if err != nil {
|
||||
apierr.WriteJSON(w, http.StatusBadRequest, apierr.ErrBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Create batch (one per webhook call; the store may aggregate externally).
|
||||
batchID, err := h.store.CreateBatch(ctx, ChannelStore, "webhook", payload.Note)
|
||||
if err != nil {
|
||||
apierr.WriteJSON(w, http.StatusInternalServerError, apierr.ErrInternal)
|
||||
return
|
||||
}
|
||||
|
||||
err = h.store.CreateCode(ctx, hash, planID, payload.DurationDays, batchID)
|
||||
if err == ErrDuplicate {
|
||||
// Same code_hash already exists – idempotent, do not error.
|
||||
err = h.store.MintOne(ctx, hash, ent, ChannelStore, "webhook", payload.Note)
|
||||
if errors.Is(err, ErrDuplicate) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_ = json.NewEncoder(w).Encode(map[string]string{"status": "already_exists"})
|
||||
|
||||
Reference in New Issue
Block a user