v2

package
v1.21.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2026 License: GPL-3.0 Imports: 26 Imported by: 0

Documentation

Overview

Package v2 implements the V2 order orchestration subdomain of the billing module: idempotent create-and-checkout, guest checkout capabilities, the SSE event-stream tickets and the event stream itself. Only the module facade may reach it.

Index

Constants

This section is empty.

Variables

View Source
var ErrIdempotencyKeyReused = errors.New("idempotency key reused with a different request")

ErrIdempotencyKeyReused is handled as HTTP 409 by the V2 handler. It is a distinct transport condition: the original order remains intact.

View Source
var ErrTooManyStreams = errors.New("too many concurrent event streams")

ErrTooManyStreams reports that the ticket already holds the maximum number of concurrent event streams.

Functions

This section is empty.

Types

type Broker added in v1.21.0

type Broker interface {
	Subscribe(ctx context.Context, orderNo string) (Subscription, error)
}

Broker subscribes to the publication of an order's events.

type Deps

type Deps struct {
	Orders       Orders
	Checkout     *checkout.Service
	Portal       *portal.Service
	JwtSecret    string
	CurrencyUnit func() string
	// GuestReplays bounds the replays of anonymous create requests.
	GuestReplays GuestReplayLimits
	// Stream serves the order event streams.
	Stream StreamDeps
}

Deps declares the orchestration's dependencies: sibling subdomains are invoked directly, never through the facade.

type EventReader added in v1.21.0

type EventReader interface {
	ListAfter(ctx context.Context, orderNo string, afterID int64, limit int) ([]*order.Event, error)
	EarliestID(ctx context.Context, orderNo string) (int64, error)
}

EventReader reads an order's durable events.

type EventSink added in v1.21.0

type EventSink interface {
	Event(id, name string, data []byte) error
	KeepAlive() error
}

EventSink receives the events of one stream, in order. The first call starts the stream; nothing is sent to the sink when the stream is refused.

type GuestReplayLimits added in v1.21.0

type GuestReplayLimits struct {
	PerKey ReplayLimiter
	PerIP  ReplayLimiter
}

GuestReplayLimits bound how often an anonymous create request may be replayed under an existing idempotency key. A replay proves the guest password against the order, so an unlimited replay is a password oracle for whoever holds the key; the limits apply per key and per client IP. A nil limiter applies no limit.

type Orders added in v1.21.0

type Orders interface {
	FindOneByOrderNo(ctx context.Context, orderNo string) (*order.Order, error)
	FindOneByIdempotencyKey(ctx context.Context, key string) (*order.Order, error)
}

Orders reads the orders the orchestration creates.

type RedisBroker added in v1.21.0

type RedisBroker struct {
	Client *redis.Client
}

RedisBroker wakes streams through the Redis channel the order event publisher broadcasts on.

func (RedisBroker) Subscribe added in v1.21.0

func (b RedisBroker) Subscribe(ctx context.Context, orderNo string) (Subscription, error)

Subscribe confirms the subscription before returning it, so a stream only counts on wake-ups Redis actually delivers.

type RedisLimiter added in v1.21.0

type RedisLimiter struct {
	Client *redis.Client
}

RedisLimiter counts a ticket's streams in Redis. During a Redis outage every stream is admitted: the event table can still sustain it, and the cap is a best-effort abuse control, not a reason to hide a paid order from its owner.

func (RedisLimiter) Acquire added in v1.21.0

func (l RedisLimiter) Acquire(ctx context.Context, ticket string, ttl time.Duration) (func(), bool)

type ReplayLimiter added in v1.21.0

type ReplayLimiter interface {
	Take(ctx context.Context, key string) (int, error)
}

ReplayLimiter grants a bounded number of permits per key and period; the auth rate limiter provides it, with its permit states.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service is the V2 orchestration.

func NewService

func NewService(deps Deps) *Service

func (*Service) AuthorizeEventStream

func (s *Service) AuthorizeEventStream(ctx context.Context, orderNo, ticket string) (dto.V2OrderSnapshot, time.Time, error)

AuthorizeEventStream validates the self-contained stream capability and returns the stream's initial snapshot together with the ticket expiry; the order entity never leaves the module.

func (*Service) Checkout

func (s *Service) Checkout(ctx context.Context, orderNo string, req *dto.V2CheckoutOrderRequest) (*dto.V2OrderResponse, error)

Checkout resumes the payment of a pending order.

func (*Service) CreateAndCheckout

func (s *Service) CreateAndCheckout(ctx context.Context, req *dto.V2CreateOrderRequest, idempotencyKey string) (*dto.V2OrderResponse, error)

CreateAndCheckout is the V2 orchestration boundary. Existing domain creators still own pricing, inventory and coupon policy; this method only gives them an idempotency context and immediately starts checkout.

func (*Service) EventTicket

func (s *Service) EventTicket(ctx context.Context, orderNo, checkoutToken string) (*dto.V2EventTicketResponse, error)

EventTicket mints a fresh stream ticket for the order.

func (*Service) GetOrder

func (s *Service) GetOrder(ctx context.Context, orderNo, checkoutToken string) (*dto.V2OrderResponse, error)

GetOrder returns the order's state snapshot with a fresh stream ticket.

func (*Service) Session

func (s *Service) Session(ctx context.Context, orderNo, checkoutToken string) (*dto.V2OrderSessionResponse, error)

Session exchanges the durable guest checkout capability for an ordinary session after activation has created the account, within the window and under the revocation rule the storefront applies. It is intentionally a separate JSON endpoint: a long-lived access token must never appear in a browser-visible EventSource URL or SSE event payload.

func (*Service) StreamEvents added in v1.21.0

func (s *Service) StreamEvents(ctx context.Context, req StreamRequest, sink EventSink) error

StreamEvents serves one order event stream until ctx ends or the ticket expires. The order is authorization, subscribe, replay, then live events: subscribing before the replay query closes the window in which a payment between the query and the subscription would be missed, and the event id makes the overlap of both safe to deliver twice.

type StreamDeps added in v1.21.0

type StreamDeps struct {
	Events  EventReader
	Broker  Broker
	Limiter StreamLimiter
	Timing  StreamTiming
}

StreamDeps serves the order event streams. The event table is the source of truth; the broker only wakes a stream quickly after an outbox publication, and a periodic catch-up keeps streams correct while the broker or a subscription is unavailable.

type StreamLimiter added in v1.21.0

type StreamLimiter interface {
	// Acquire takes one of the ticket's stream slots for at most ttl and
	// returns its release.
	Acquire(ctx context.Context, ticket string, ttl time.Duration) (release func(), ok bool)
}

StreamLimiter bounds the concurrent streams of one ticket.

type StreamRequest added in v1.21.0

type StreamRequest struct {
	OrderNo string
	Ticket  string
	AfterID int64
}

StreamRequest names the stream: the order, its ticket and the replay cursor (Last-Event-ID, or the after query parameter).

type StreamTiming added in v1.21.0

type StreamTiming struct {
	Heartbeat   time.Duration
	CatchUp     time.Duration
	Resubscribe time.Duration
}

StreamTiming paces a stream; zero values select the production pacing.

type Subscription added in v1.21.0

type Subscription interface {
	Wake() <-chan struct{}
	Close() error
}

Subscription wakes a stream when an order's events were published. Its channel closes when the subscription ends.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL