Documentation
¶
Overview ¶
Package activation owns the paid-order activation workflow and implements its billing-domain stages: the recharge wallet credit, the referral commission and the final settlement. Each stage is idempotent (inbox marker or status CAS); Workflow sequences them with the stages other modules run. The task adapter only decodes a message and invokes the billing facade, and only the facade may reach this package.
Index ¶
- Variables
- type Deps
- type LegacyGuestCache
- type Notifier
- type Orders
- type ProfileReader
- type Service
- func (s *Service) ActivateRecharge(ctx context.Context, orderNo string) (int64, error)
- func (s *Service) FinalizeOrder(ctx context.Context, orderNo string) error
- func (s *Service) RefundUnfulfillable(ctx context.Context, orderNo string) error
- func (s *Service) RefundUnfulfillableToAccount(ctx context.Context, orderNo string, accountID int64) error
- func (s *Service) SettleOrderCommission(ctx context.Context, orderNo string, buyerID int64) error
- func (s *Service) UnfulfillableRefunded(ctx context.Context, orderNo string) (bool, error)
- type Store
- type SubscriptionFulfiller
- type Workflow
- type WorkflowDeps
- type WorkflowOrders
Constants ¶
This section is empty.
Variables ¶
var ErrInvalidOrderStatus = errors.New("invalid order status")
ErrInvalidOrderStatus reports a lost Paid->Finished CAS: the order left the Paid state underneath the settlement.
Functions ¶
This section is empty.
Types ¶
type Deps ¶
type Deps struct {
Orders Orders
// Store carries the billing-scoped transactions, the wallet view and
// the inbox markers.
Store Store
// Profiles resolves the buyer's referrer and the referrer's commission
// settings from the identity domain.
Profiles ProfileReader
// InvitePolicy snapshots the runtime-mutable site-wide referral
// fallback (percentage, first-purchase-only).
InvitePolicy func() (percentage uint8, onlyFirstPurchase bool)
}
Deps declares the subdomain's dependencies; the module facade forwards them from the composition root.
type LegacyGuestCache ¶ added in v1.20.3
type Orders ¶ added in v1.21.0
type Orders interface {
FindOneByOrderNo(ctx context.Context, orderNo string) (*order.Order, error)
}
Orders reads the order an activation stage works on.
type ProfileReader ¶
ProfileReader is the read-only identity port resolving referral settings; the legacy user repository satisfies it structurally.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service is the activation entry point used by the billing facade.
func NewService ¶
func (*Service) ActivateRecharge ¶
ActivateRecharge credits the order's amount to the buyer's wallet exactly once and returns the post-credit balance (re-read on replays) for the caller's notification.
func (*Service) FinalizeOrder ¶
FinalizeOrder is the billing-domain settlement: coupon accounting and the Paid -> Finished transition (which appends the order.fulfilled outbox event) commit atomically. Losing the status CAS rolls the coupon count back, so it stays exactly-once.
Known transitional window: an admin closing a Paid order between the fulfillment stage and this CAS leaves the fulfillment committed while the order ends Closed. The pre-split code had the same conflict resolved by row locks; compensation for admin closes of paid orders is a billing concern tracked in ADR-001 step 2.
func (*Service) RefundUnfulfillable ¶ added in v1.21.0
RefundUnfulfillable returns what a paid order collected when the subscription domain refuses to fulfil it for good (its subscription was refunded, stopped or handed to a payment provider between checkout and payment): the payment goes back to the buyer's balance and the gift credit the order held to the gift balance, each with the ledger entry a refund writes, a reserved coupon use is released as a close releases it, and the order ends Closed with its event. Everything commits in one billing transaction with the inbox marker, so a redelivered activation refunds once; the order row lock serializes concurrent deliveries.
func (*Service) RefundUnfulfillableToAccount ¶ added in v1.21.0
func (s *Service) RefundUnfulfillableToAccount(ctx context.Context, orderNo string, accountID int64) error
RefundUnfulfillableToAccount refunds a paid guest order whose identity already belongs to account accountID, so the order can open no account of its own: the buyer who paid twice under one identity, or paid for an identity registered meanwhile. The order is bound to that account and its payment returned to the account's wallet in the one transaction that closes it, so a redelivery finds it closed and refunded, never bound but unpaid back.
func (*Service) SettleOrderCommission ¶
SettleOrderCommission credits the referral commission for a purchase or renewal exactly once. The inbox marker also covers the "no commission applies" outcome so replays skip the referrer lock entirely.
type Store ¶
type Store interface {
InBillingTx(ctx context.Context, fn func(repository.BillingStore) error) error
Inbox() repository.InboxRepo
Wallet() repository.WalletRepo
}
Store is the narrow persistence surface the activation stages need; the repository store satisfies it structurally.
type SubscriptionFulfiller ¶ added in v1.20.3
type SubscriptionFulfiller interface {
FulfillPaidOrder(context.Context, string) (*subscription.FulfillmentOutcome, error)
}
type Workflow ¶ added in v1.20.3
type Workflow struct {
// contains filtered or unexported fields
}
Workflow activates a paid order: it has identity create a guest buyer's account, credits a recharge or has subscription fulfil the order, settles the referral commission, finalizes the order and sends the notices. Every stage is idempotent, so a failed activation is retried from the start. A fulfillment the subscription domain refuses for good ends the order with a refund instead (see RefundUnfulfillable).
func NewWorkflow ¶ added in v1.20.3
func NewWorkflow(deps WorkflowDeps, stages *Service) *Workflow
type WorkflowDeps ¶ added in v1.20.3
type WorkflowDeps struct {
Orders WorkflowOrders
Profiles ProfileReader
GuestAccounts identity.GuestAccounts
// GuestIdentities is the identity port the guest account stage checks
// the guest's mailbox against before an account is created, the same
// port the guest purchase checks; nil skips the check.
GuestIdentities portal.GuestAccountReader
Subscriptions SubscriptionFulfiller
LegacyGuestCache LegacyGuestCache
Notifications Notifier
NotificationsEnabled func() bool
}
type WorkflowOrders ¶ added in v1.21.0
type WorkflowOrders interface {
FindOneByOrderNo(ctx context.Context, orderNo string) (*order.Order, error)
Update(ctx context.Context, data *order.Order) error
}
WorkflowOrders is the order persistence the workflow uses outside its stages: it reads the paid order and binds a guest order to its account.