package integration import ( "context" "encoding/json" "fmt" ) // Enqueue inserts a pending core.outbox row — the transactional dispatch // handoff ratified in design.md Decision 4 for the integration-extraction // change: a domain write and its outbox row commit atomically inside the // caller's Postgres transaction, and a separate poller (e.g. the Stripe // outbox drainer, internal/integrations/stripe/workflows's // PollIntegrationOutbox) executes the action against the external provider // later. // // This is the only supported write path into core.outbox; core code must // not INSERT into it directly (see Decision 4's "concession to // coherence"). It replaces the raw `INSERT INTO core.outbox` statements // formerly hand-written in internal/server/operator_billing.go and // internal/integrations/stripe/store/ensure_customer.go. // // db accepts a *sql.DB or a *sql.Tx via this package's DBTX interface // (generated by sqlc for the provider-registry querier but equally // satisfied by both). Pass a *sql.Tx to keep the enqueue atomic with the // caller's other writes in the same transaction — the pattern both // converted call sites rely on. A single logical action (e.g. "create the // product, then its price") that needs more than one outbox row is two // separate Enqueue calls against the same *sql.Tx, not a batch API: this // keeps the helper's shape simple and the atomicity guarantee is already // provided by the shared transaction. // // payload is marshaled to JSON here — pass a struct or map, not // pre-marshaled bytes. providerKey and actionType are the outbox's // existing free-form vocabulary (e.g. "stripe" / "create_stripe_customer"); // this helper does not validate or constrain that vocabulary (the Stripe // action-type vocabulary baked into core is the payments-seam follow-up // noted in design.md Decision 4, out of scope here). func Enqueue(ctx context.Context, db DBTX, providerKey, actionType string, payload any) error { payloadJSON, err := json.Marshal(payload) if err != nil { return fmt.Errorf("marshal outbox payload for %s/%s: %w", providerKey, actionType, err) } if _, err := db.ExecContext(ctx, `INSERT INTO core.outbox (provider, action_type, payload) VALUES ($1, $2, $3)`, providerKey, actionType, payloadJSON, ); err != nil { return fmt.Errorf("enqueue %s/%s outbox entry: %w", providerKey, actionType, err) } return nil }