50 lines
2.1 KiB
Go
50 lines
2.1 KiB
Go
// Package recovery is the v0 abandoned-cart-recovery seam.
|
|
//
|
|
// The contract is intentionally narrow: a Scanner finds candidates whose last
|
|
// mutation is within an "abandoned" window and that have BOTH an attachable
|
|
// contact (email and/or push tokens) and items in the cart, then hands them
|
|
// to a Notifier. The Notifier decides what to do — for now a logging shim.
|
|
//
|
|
// The seam intentionally avoids:
|
|
//
|
|
// - Email/push-token delivery (lives in the notifier impl).
|
|
// - Idempotency tracking ("did we already notify?"). v0: scanner windows
|
|
// are sized so a cart falls in once per detection threshold. Real
|
|
// idempotency is the next pass — see suggest_followups / plan-commerce-
|
|
// maturity C3.
|
|
// - Cross-pod coordination. Each cart pod owns its own shard of event logs
|
|
// (replicated read cache, no leader), so per-pod scanning is correct.
|
|
// A future AMQP fan-out of `cart.recovery.candidate` would let a single
|
|
// dedicated notifier consume instead — out of scope here.
|
|
package recovery
|
|
|
|
import (
|
|
"context"
|
|
|
|
"git.k6n.net/mats/go-cart-actor/pkg/cart"
|
|
)
|
|
|
|
// RecoveryCandidate is the projectable "we'd remind this shopper" shape.
|
|
// Fields surface just enough for a future email template / push payload; the
|
|
// underlying grain carries everything else.
|
|
type RecoveryCandidate struct {
|
|
CartID cart.CartId
|
|
UserID string // empty if the cart hasn't been linked to a profile
|
|
Email string
|
|
PushTokens []cart.PushToken
|
|
ItemCount int
|
|
TotalIncVat int64 // minor units (money.Cents == int64)
|
|
Currency string
|
|
// LastChange is best-effort: the scanner reads the event-log file's
|
|
// modification time. A pending notifier shouldn't use it as the canonical
|
|
// "when was this cart touched?" wall clock — see cart.LastChange.
|
|
LastChangeUnix int64
|
|
}
|
|
|
|
// Notifier is the seam a real email/push implementation plugs into. The
|
|
// LoggingNotifier is the v0 default. The cart service ships the seam even
|
|
// before a real send exists so call-sites and tracking can be wired once.
|
|
type Notifier interface {
|
|
NotifyAbandoned(ctx context.Context, c RecoveryCandidate) error
|
|
}
|