package payments import ( "context" "fmt" "github.com/google/uuid" ) // OrderResult is what CreateOrder returns to the transport: the created order id and the details a // provider launch payload needs — the amount to charge and a human title for the payment. type OrderResult struct { OrderID uuid.UUID Amount Money Title string } // CreateOrder opens a pending order to fund a chip pack in the execution context's payment method, // tagged with the provider that will settle it. It gate-checks the context (trusted, not the // VK-iOS spend freeze) and that the method's funding segment is attached, prices the pack in the // method's currency, then writes the order. The caller enforces any account-level precondition // (e.g. the direct email anchor, D36) before calling — payments holds no cross-schema identity // knowledge. func (s *Service) CreateOrder(ctx context.Context, accountID uuid.UUID, cxt Context, present []Source, productID uuid.UUID, provider string) (OrderResult, error) { if !cxt.Trusted() || cxt.vkFrozen() { return OrderResult{}, ErrUntrusted } method := cxt.Kind if !has(present, method) { return OrderResult{}, ErrUntrusted // the funding segment is not attached to the account } pack, err := s.store.loadPackForOrder(ctx, productID, method) if err != nil { return OrderResult{}, err } orderID, err := uuid.NewV7() if err != nil { return OrderResult{}, fmt.Errorf("payments: order id: %w", err) } o := newOrder{ orderID: orderID, accountID: accountID, platform: string(method), productID: productID, amount: pack.price, origin: method, provider: provider, } if err := s.store.createOrder(ctx, o, s.clock()); err != nil { return OrderResult{}, err } return OrderResult{OrderID: orderID, Amount: pack.price, Title: pack.title}, nil } // Fund credits a paid order into its funded segment exactly once, from a verified provider callback // — the single writer for every rail. It matches the order, verifies the paid amount, appends the // fund ledger row (idempotent on (provider, provider_payment_id)), credits the balance and marks // the order paid. A duplicate callback returns AlreadyCredited without a second credit; a valid // callback is honoured even on an expired order (§9/D23). func (s *Service) Fund(ctx context.Context, orderID uuid.UUID, provider, providerPaymentID string, paid Money) (FundOutcome, error) { return s.store.fund(ctx, orderID, provider, providerPaymentID, paid, s.clock()) } // ExpireOrders marks pending orders older than the configured lifetime as expired, returning how // many were swept. It backs the periodic pending reaper; expiry is cosmetic (a late valid callback // still credits — see Fund). func (s *Service) ExpireOrders(ctx context.Context) (int, error) { ttl, err := s.store.orderTTL(ctx) if err != nil { return 0, err } return s.store.expirePending(ctx, ttl, s.clock()) }