package verification import ( verificationmodel "git.sendico.io/tech/sxchnge/product/verification/model" "strings" "time" "git.sendico.io/tech/sxchnge/shared/ratelimit/store" "go.mongodb.org/mongo-driver/v2/bson" ) type IssuanceLimit struct { ScopeKind string ScopeHash string Limit int WindowStart time.Time ExpiresAt time.Time } func NewIssuanceLimit(scopeKind, scopeValue string, limit int, window time.Duration, now time.Time) IssuanceLimit { windowStart := now.UTC().Truncate(window) return IssuanceLimit{ ScopeKind: strings.TrimSpace(scopeKind), ScopeHash: ratelimits.HashScope(scopeKind, scopeValue), Limit: limit, WindowStart: windowStart, ExpiresAt: windowStart.Add(window), } } type TokenKind = string const ( TokenKindOTP TokenKind = "otp" TokenKindLink TokenKind = "link" ) type Request struct { AccountRef bson.ObjectID Purpose verificationmodel.VerificationPurpose Target string ContextRef string Ttl time.Duration Kind TokenKind MaxRetries *int Cooldown *time.Duration IdempotencyKey *string // Optional key to make Create idempotent for retries. IssuanceLimits []IssuanceLimit } func (r *Request) WithIssuanceLimits(limits ...IssuanceLimit) *Request { r.IssuanceLimits = append([]IssuanceLimit(nil), limits...) return r } func newRequest(accountRef bson.ObjectID, purpose verificationmodel.VerificationPurpose, target string, kind TokenKind) *Request { return &Request{ AccountRef: accountRef, Purpose: purpose, Target: target, Kind: kind, Ttl: 15 * time.Minute, // default TTL for verification tokens } } func NewLinkRequest(accountRef bson.ObjectID, purpose verificationmodel.VerificationPurpose, target string) *Request { return newRequest(accountRef, purpose, target, TokenKindLink) } func NewOTPRequest(accountRef bson.ObjectID, purpose verificationmodel.VerificationPurpose, target string) *Request { return newRequest(accountRef, purpose, target, TokenKindOTP) } func (r *Request) WithTTL(ttl time.Duration) *Request { r.Ttl = ttl return r } func (r *Request) WithMaxRetries(maxRetries int) *Request { r.MaxRetries = &maxRetries return r } func (r *Request) WithCooldown(cooldown time.Duration) *Request { r.Cooldown = &cooldown return r } func (r *Request) WithIdempotencyKey(key string) *Request { normalized := strings.TrimSpace(key) if normalized == "" { r.IdempotencyKey = nil return r } r.IdempotencyKey = &normalized return r } func (r *Request) WithContextRef(contextRef string) *Request { r.ContextRef = strings.TrimSpace(contextRef) return r }