Accrue.Jobs.DunningSweeper (accrue v0.3.0)

Copy Markdown View Source

Oban cron worker for BILL-15 / D4-02 dunning grace-period overlay.

Stripe Smart Retries owns the retry cadence. Accrue owns a thin grace-period overlay on top: once past_due_since is older than the configured grace_days, this worker asks the processor facade to move the subscription to the terminal action (:unpaid or :canceled) and stamps dunning_sweep_attempted_at on success.

Canonicality (D2-29)

This worker NEVER flips the local subscription.status. It only:

  • Calls Accrue.Processor.update_subscription/3 to ask the processor facade to transition the row.
  • Stamps dunning_sweep_attempted_at AFTER a successful processor call so the same row is not retried on the next tick.
  • Records a dunning.terminal_action_requested audit event in accrue_events.

The actual local status flip happens when Stripe echoes the change back via customer.subscription.updated, which the Accrue.Webhook.DefaultHandler picks up and projects.

Host wiring

Accrue does not start its own Oban instance. The host app must wire the cron themselves:

config :my_app, Oban,
  queues: [accrue_dunning: 2],
  plugins: [
    {Oban.Plugins.Cron,
     crontab: [{"*/15 * * * *", Accrue.Jobs.DunningSweeper}]}
  ]

Failure handling

A processor error on any row logs a warning and returns false WITHOUT stamping dunning_sweep_attempted_at, so the next cron tick picks the same row up again. max_attempts: 3 on the worker bounds per-tick retries.

Summary

Functions

Runs one sweep tick. Returns {:ok, count} where count is the number of subscriptions successfully transitioned this tick.

Functions

sweep()

@spec sweep() :: {:ok, non_neg_integer()}

Runs one sweep tick. Returns {:ok, count} where count is the number of subscriptions successfully transitioned this tick.