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:
- Implement the actual refund processing in the
ProcessVmRefund worker job (Lightning invoice payout, and/or manual refund recording for fiat methods).
- 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.
- Update reports (time-series report, company earnings) to subtract refunds from net/tax totals for the period in which the refund happened.
- 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.
- 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.
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 viaPricingEngine::calculate_vm_refund_amount_from_datePOST /api/admin/v1/vms/{id}/refund— queues aWorkJob::ProcessVmRefundjobBut the worker-side handler is a stub that just bails:
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_paymenttable (the legacyvm_paymenttable was dropped in migration20260716130000_drop_vm_payment.sql):SubscriptionPaymentType(lnvps_db/src/model.rs) only hasPurchase,Renewal, andUpgrade— noRefundvariant.subscription_payment.amount(andtax) areBIGINT UNSIGNED/u64, so a refund cannot be stored as a negative amount.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:
ProcessVmRefundworker job (Lightning invoice payout, and/or manual refund recording for fiat methods).SubscriptionPaymentType::Refundvariant, andRefund-type payment rows as negative when aggregating; link the refund to the original payment(s)/subscription/VM.expiresadjustment is needed;refund_from_dateonly drives the pro-rata amount calculation.Acceptance criteria