Skip to content

Refactor webhook handler to use dedicated WebhookService #331

@piffio

Description

@piffio

Context

The billing webhook handler (src/api/billing/webhook.rs) currently calls repositories directly with production-hardened logic including:

  • Idempotency checks (webhook_already_processed(), mark_webhook_processed())
  • ProductRepository lookups for plan name resolution from Polar price_id
  • Multiple Polar event variants with field name variations (customer_id vs customerId, external_id vs externalId)
  • Granular error handling (400/401/503 status codes)

BillingService::process_webhook_event() exists but is a simpler abstraction that doesn't replicate this complexity. Refactoring to use it would be a regression.

Task

Create a dedicated WebhookService to handle all webhook-specific logic:

  1. New service: src/services/webhook_service.rs

    • verify_webhook_signature() — signature verification
    • process_webhook_event() — main event dispatcher with idempotency
    • handle_subscription_created() — subscription creation logic
    • handle_subscription_updated() — subscription update logic
    • handle_subscription_cancelled() — subscription cancellation logic
    • Helper methods for billing account resolution, plan mapping, etc.
  2. Refactor handler: src/api/billing/webhook.rs

    • Delegate to WebhookService for business logic
    • Keep HTTP-specific concerns (request parsing, response formatting, error status mapping) at handler level
  3. Clean up: Remove unused webhook methods from BillingService (already done in recent commit)

Benefits

  • Consistent architecture: all business logic in service layer
  • Reusability: webhook logic can be used by cron job handlers or other consumers
  • Testability: easier to unit test webhook logic in isolation

Notes

  • The current webhook handler is production-hardened and should not be changed in a way that reduces reliability
  • Ensure idempotency and all event variant handling is preserved
  • Consider whether trigger_downgrade_job() belongs in WebhookService (for scheduled downgrades) or a separate CronService

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    Status

    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions