Coupon and promotion-code write surface.
Thin wrappers around the processor's coupon + promotion-code
endpoints. The local
accrue_coupons + accrue_promotion_codes tables are a thin
projection — the processor is canonical and Accrue mirrors only the
fields the admin LiveView needs.
Functions
create_coupon/2— creates a coupon via the processor, persists a local row, records a"coupon.created"event.create_promotion_code/2— creates a promotion code via the processor, persists a local row FK'd to the coupon, records a"promotion_code.created"event.apply_promotion_code/3— looks up a local promotion code by its customer-facingcode, validatesactive/expires_at/max_redemptions, then callsProcessor.update_subscription(sub_id, %{coupon: coupon_id}). Records a"coupon.applied"event on success.
All public functions follow the dual-API foo/n + foo!/n pattern.
Processor calls run inside Repo.transact/2 here because
coupon create is not SCA-capable — there's no asynchronous 3DS leg
to keep outside the transaction.
Summary
Functions
Attaches a promotion code to a subscription by looking up the local
%PromotionCode{} row by customer-facing code, validating
applicability, then calling the processor's update_subscription
with %{coupon: coupon_processor_id}.
Raising variant of apply_promotion_code/3.
Creates a coupon through the configured processor and persists a
local %Coupon{} row plus a "coupon.created" event.
Raising variant of create_coupon/2.
Creates a promotion code through the configured processor and
persists a local %PromotionCode{} row FK'd to the underlying
coupon. Records a "promotion_code.created" event.
Raising variant of create_promotion_code/2.
Types
@type apply_error() :: :not_found | :inactive | :expired | :max_redemptions_reached | :coupon_missing | term()
Functions
@spec apply_promotion_code(Accrue.Billing.Subscription.t(), String.t(), keyword()) :: {:ok, Accrue.Billing.Subscription.t()} | {:error, apply_error()}
Attaches a promotion code to a subscription by looking up the local
%PromotionCode{} row by customer-facing code, validating
applicability, then calling the processor's update_subscription
with %{coupon: coupon_processor_id}.
Returns {:ok, %Subscription{}} on success. On validation failure
returns {:error, :not_found | :inactive | :expired | :max_redemptions_reached} before making any processor call.
A "coupon.applied" event is recorded inside the same
Repo.transact/2 as the processor call on success.
@spec apply_promotion_code!(Accrue.Billing.Subscription.t(), String.t(), keyword()) :: Accrue.Billing.Subscription.t()
Raising variant of apply_promotion_code/3.
@spec create_coupon( map(), keyword() ) :: {:ok, Accrue.Billing.Coupon.t()} | {:error, term()}
Creates a coupon through the configured processor and persists a
local %Coupon{} row plus a "coupon.created" event.
params is a map of processor-shape attrs. Supply a caller-chosen
:id to pin a deterministic coupon id (required for the comp flow's
"accrue_comp_100_forever" seed coupon).
@spec create_coupon!( map(), keyword() ) :: Accrue.Billing.Coupon.t()
Raising variant of create_coupon/2.
@spec create_promotion_code( map(), keyword() ) :: {:ok, Accrue.Billing.PromotionCode.t()} | {:error, term()}
Creates a promotion code through the configured processor and
persists a local %PromotionCode{} row FK'd to the underlying
coupon. Records a "promotion_code.created" event.
params[:coupon] MUST be the processor-side coupon id (e.g.
"accrue_comp_100_forever" or "SUMMER25").
@spec create_promotion_code!( map(), keyword() ) :: Accrue.Billing.PromotionCode.t()
Raising variant of create_promotion_code/2.