Skip to content

Refunds are not fully implemented: refunded amounts are not tracked as negative earnings for P/L #193

Description

@v0l

Problem

VM refunds are only partially implemented. The admin API already exposes:

  • GET /api/admin/v1/vms/{id}/refund — calculates the pro-rated refund amount via PricingEngine::calculate_vm_refund_amount_from_date
  • POST /api/admin/v1/vms/{id}/refund — queues a WorkJob::ProcessVmRefund job

But the worker-side handler is a stub that just bails:

// lnvps_api/src/worker.rs
WorkJob::ProcessVmRefund { .. } => {
    // TODO: Implement the actual refund processing logic
    bail!("Refund processing is not yet implemented");
}

Even once the money is actually returned to the customer, there is currently no way to represent a refund in accounting/reporting. All payments live in the subscription_payment table (the legacy vm_payment table was dropped in migration 20260716130000_drop_vm_payment.sql):

  • SubscriptionPaymentType (lnvps_db/src/model.rs) only has Purchase, Renewal, and Upgrade — no Refund variant.
  • subscription_payment.amount (and tax) are BIGINT UNSIGNED / u64, so a refund cannot be stored as a negative amount.
  • The admin time-series/company reports (lnvps_api_admin/src/admin/reports.rs) sum paid payment amounts, so refunded money would still show up as revenue, overstating earnings.

Requirements

When a VM is (partially) refunded, we need to track that the money was returned to the customer so that P/L is correct — i.e. the refund must appear as a minus on the earnings:

  1. Implement the actual refund processing in the ProcessVmRefund worker job (Lightning invoice payout, and/or manual refund recording for fiat methods).
  2. Represent the refund as an accounting record, e.g.:
    • add a SubscriptionPaymentType::Refund variant, and
    • either store a signed amount or treat Refund-type payment rows as negative when aggregating; link the refund to the original payment(s)/subscription/VM.
  3. Update reports (time-series report, company earnings) to subtract refunds from net/tax totals for the period in which the refund happened.
  4. Delete/deprovision the refunded VM as part of the flow — a refunded VM is deleted, so no expires adjustment is needed; refund_from_date only drives the pro-rata amount calculation.
  5. Migration for any schema changes (new enum value, optional link column to the refunded payment).

Acceptance criteria

  • Processing a refund records a payment row (or equivalent) that reduces reported earnings by the refunded amount (incl. tax handling).
  • Reports for the refund period show the reduced net/tax totals.
  • Refund job no longer bails; failures are surfaced via job feedback.

Metadata

Metadata

Assignees

No one assigned

    Labels

    databaseMigration or schema changesenhancementNew feature or requestpaymentsPayment/invoice logic

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions