Migrate vm_payment to subscriptions payment system#114
Open
Conversation
Rename VmCostPlanIntervalType → IntervalType and ApiVmCostPlanIntervalType → ApiIntervalType across the codebase to decouple interval types from vm_cost_plan in preparation for the subscription payment migration.
- Migration 20260302151134: re-add interval_amount/interval_type to subscription, add time_value/metadata to subscription_payment, add subscription_id FK to vm table - Add VmRenewal=3 and VmUpgrade=4 to SubscriptionType - Add Upgrade=2 to SubscriptionPaymentType - Add interval_amount/interval_type to Subscription struct - Add time_value/metadata to SubscriptionPayment and SubscriptionPaymentWithCompany - Add subscription_id to Vm struct - Fix subscription_payment_paid() transaction bug; implement VM path (extend by time_value seconds) and regular path (read interval from subscription) - Add get_vm_by_subscription() and list_vm_subscription_payments() to LNVpsDbBase trait, MySQL impl, and MockDb - Update insert_vm/update_vm SQL to include subscription_id - Propagate new fields through admin and user-facing API models - Add interval_amount/interval_type to AdminCreateSubscriptionRequest and AdminSubscriptionInfo
- Add migrate_vm_subscriptions binary: creates a Subscription + SubscriptionLineItem(VmRenewal) for each VM without subscription_id. Standard VMs use cost plan interval; custom VMs default to 1 Month. Supports --dry-run for preview. Idempotent: already-linked VMs skipped. - Fix insert_subscription, insert_subscription_with_line_items, and update_subscription SQL to bind interval_amount and interval_type. - Register migrate_vm_subscriptions in lnvps_api_admin/Cargo.toml.
- vm.subscription_id is now u64 (not Option); migration added to make it NOT NULL after data backfill - provision() and provision_custom() create Subscription + SubscriptionLineItem(VmRenewal) before inserting the VM - CostResult::Existing now holds SubscriptionPayment; deduplication checks list_vm_subscription_payments instead of list_vm_payment - price_to_payment_with_type creates SubscriptionPayment using vm.subscription_id; inserts via insert_subscription_payment - renew(), renew_intervals(), renew_amount() return SubscriptionPayment - create_upgrade_payment() uses SubscriptionPaymentType::Upgrade and stores UpgradeConfig in metadata as JSON Value - auto_renew_via_nwc() returns SubscriptionPayment - handle_upgrade() accepts SubscriptionPayment, reads metadata field - Lightning and Revolut webhook handlers updated to look up SubscriptionPayment by ID, mark paid via subscription_payment_paid, and find VM via get_vm_by_subscription - API routes v1_renew_vm, v1_get_payment, v1_get_payment_invoice, v1_payment_history, v1_vm_upgrade all use SubscriptionPayment - ApiVmPayment::from_subscription_payment and ApiInvoiceItem::from_subscription_payment added - Mock insert_subscription and insert_subscription_with_line_items fixed to actually persist data - Test helpers create proper subscriptions for VM fixture data
Instead of vm.subscription_id (VM -> subscription), VMs now carry subscription_line_item_id (VM -> line item), matching the existing ip_range_subscription -> subscription_line_item pattern. This allows a single subscription to contain a VM renewal line item, additional IP allocations, and other products, all billed together. The subscription is reached by traversing: vm -> line_item -> subscription. - Rename vm.subscription_id -> vm.subscription_line_item_id (NOT NULL) - Merge 20260302154256 NOT NULL migration into 20260302151134 - Add fk_vm_subscription_line_item FK to subscription_line_item(id) - Add get_vm_by_line_item() to DB trait (primary lookup) - Add get_vm_by_subscription() joining through line_item for payment handlers - insert_subscription_with_line_items() now returns (subscription_id, Vec<line_item_id>) - provision() and provision_custom() store the line_item_id on the VM - price_to_payment_with_type() resolves subscription_id via line_item lookup - SubscriptionType::VmRenewal/VmUpgrade kept as discriminants on line items - MockDb default now pre-populates subscription id=1 + line_item id=1 - migrate_vm_subscriptions binary updated to set subscription_line_item_id
Instead of calling list_companies() and picking the first result, look up the region (template.region_id / pricing.region_id) and use region.company_id. This is the correct relationship: template -> region -> company.
…st live - Add is_setup BIT(1) NOT NULL DEFAULT 0 to subscription table (in 20260302151134 migration) — set to 1 when first payment is confirmed - Replace payment history scan (has_paid) with subscription.is_setup to determine Purchase vs Renewal payment type - In renew_subscription(), always compute VmRenewal line item cost via PricingEngine::get_vm_cost_for_intervals — never use the stored amount, which is stale for standard VMs and zero for custom VMs - subscription_payment_paid() now sets is_setup = 1 alongside is_active = 1 - Add is_setup to AdminSubscriptionInfo and all Subscription struct literals
- renew_subscription() now takes intervals: u32 parameter - VmRenewal line items use get_vm_cost_for_intervals(vm.id, method, intervals) - Non-VM line items multiply stored amount by intervals - renew_intervals() passes its intervals arg through instead of ignoring it - v1_renew_subscription API endpoint passes intervals=1 (non-VM subscriptions don't support multi-interval renewal via that endpoint yet)
…and provision subscription creation
…ion_payment migration
…ements - Add 20260303114230_fix_referral_constraint_names.sql to rename anonymous FK constraints on referral/referral_payout tables to explicit names, fixing mysqldump re-import collisions - Add VmForMigration struct and LNVpsDbMysql helper methods (list_vm_ids_without_subscription, get_vm_for_migration, set_vm_subscription_line_item, pool) to allow migration binary to operate on VMs with NULL subscription_line_item_id - Rework migrate_vm_subscriptions binary to use concrete DB type for VM reads/updates, avoiding Vm struct decode failures on NULL columns
…ment records - Phase 1 now migrates ALL VMs including deleted ones (was previously skipping deleted VMs, leaving 863 VMs without subscriptions) - Phase 2 backfills every vm_payment row into subscription_payment, preserving all fields including encrypted external_data (copied as raw string without decryption via new VmPaymentRaw struct) - PaymentType::Renewal → SubscriptionPaymentType::Renewal - PaymentType::Upgrade → SubscriptionPaymentType::Upgrade - upgrade_params JSON string → metadata serde_json::Value - time_value u64 (0=none) → Option<u64> - Both phases are idempotent (re-run is safe) - Validated against production backup: 1078 subscriptions created, 2180 payments backfilled, 0 errors
…und-trip The previous approach went through insert_subscription_payment which re-encrypted external_data using the local key. On production the data is already encrypted with the production key and must be copied verbatim. - Add insert_subscription_payment_raw() to LNVpsDbMysql for raw byte copy - Add list_subscription_payment_ids_for_subscription() for idempotency checking without decrypting external_data - Add VmPaymentRaw struct with external_data as plain String - Migration binary now uses raw insert for all payment backfilling
- renew_subscription: use NewPaymentInfo directly from get_vm_cost_for_intervals instead of re-passing already-converted BTC amounts through get_amount_and_rate (double-conversion bug causing ~65000x inflated BTC amounts) - renew_subscription: set time_value on created SubscriptionPayment from summed NewPaymentInfo.time_value (was always None, so vm.expires was never extended) - insert_subscription_payment / update_subscription_payment: add missing time_value, processing_fee, metadata columns to SQL (were silently dropped) - subscription_payment_paid: also UPDATE vm.expires by time_value seconds, not only subscription.expires (vm expiry was never extended on payment confirmation) - get_vm_cost_for_intervals: drop time_value from dedup key; match unpaid renewal by subscription+method+type only (time_value changes each call as vm.expires advances, so the old match always missed and created duplicate payments) - next_template_expire / get_custom_vm_cost / get_template_vm_cost: clamp base to max(vm.expires, now) so expired VMs get correct time_value and new_expiry - Skip data migrations when running in API-only mode (--mode api) - Add time_value and amount sanity assertions to all renew tests - Mock subscription_payment_paid now also updates vm.expires; test updated to insert VM and assert both subscription and VM expiry are extended
…e dedup - Add list_pending_vm_subscription_payments to DB trait, MySQL impl, and mock: returns only unpaid payments whose expires > NOW(), so callers never see stale expired invoices - get_vm_cost_for_intervals: use list_pending_vm_subscription_payments instead of list_vm_subscription_payments + in-memory filter; removes the redundant p.expires > Utc::now() check from the find predicate - invoice.rs / revolut.rs: use list_pending_vm_subscription_payments when cancelling other pending upgrade payments; drop now-redundant !p.is_paid filter - Add two tests: dedup reuses a valid unpaid payment (Existing), and dedup ignores an expired unpaid payment and returns New instead
After a VM upgrade (both standard→custom and custom→custom paths), the SubscriptionLineItem.amount was never updated, leaving the displayed subscription renewal cost stale. - In convert_to_custom_template: compute new base-currency cost via PricingEngine::get_custom_vm_cost_amount and set it on the line item - Add update_line_item_cost_for_custom_vm helper on LNVpsProvisioner - Call the helper from the process_vm_upgrade worker step after updating an existing custom template's specs - Add regression tests for both code paths
The migration tool created subscriptions with is_active=true for all VMs including deleted ones. Add the deleted column to VmForMigration and its backing SQL query, then set is_active=!vm.deleted when building the Subscription record.
All 12 list endpoints that previously fetched the full table into memory and paginated in Rust (skip/take) now use LIMIT/OFFSET in SQL with a separate COUNT(*) for the total. New paginated DB trait methods added: - list_cost_plans_paginated - list_custom_pricing_paginated (optional region_id + enabled filters) - list_subscriptions_paginated (optional user_id filter) - list_subscription_payments_paginated - list_available_ip_space_paginated (optional is_available/is_reserved/registry filters) - list_ip_space_pricing_by_space_paginated - list_ip_range_subscriptions_by_space_paginated (optional user_id + is_active filters) - list_payment_method_configs_paginated - list_roles_paginated Endpoints updated: v1_list_ip_space, v1_list_subscriptions, v1_list_subscription_payments, admin_list_cost_plans, admin_list_custom_pricing, admin_list_ip_space, admin_list_ip_space_pricing, admin_list_ip_space_subscriptions, admin_list_payment_methods, admin_list_subscriptions, admin_list_subscription_payments, admin_list_roles
…1-13) - Add list_expiring_subscriptions, list_expired_subscriptions, deactivate_subscription to DB trait/MySQL/mock - Implement all ip_range_subscription mock methods; add ip_range_subscriptions field to MockDb - Add WorkJob::CheckSubscriptions; schedule at 30s interval alongside CheckVms - Add check_subscriptions() + handle_subscription_state() to Worker: expiring-soon NWC auto-renewal, non-VM deactivation on expiry, grace-period notification - Add vm_expires() helper: resolves subscription.expires via line item; falls back to vm.expires - handle_vm_state now uses vm_expires() for stop/delete decisions - Remove VM-specific NWC auto-renewal from handle_vm_state (now owned by handle_subscription_state)
…on (increment 15)
…ipeline (increments 16-17) - Add PaymentCompletionHandler trait, VmPaymentCompletionHandler, NonVmPaymentCompletionHandler - Add make_completion_handler dispatcher selecting by subscription_type - Extract complete_payment(db, payment, handler, cancel_fn) free function - Refactor NodeInvoiceHandler: replace mark_payment_paid(vm_id) with complete(payment) - Refactor RevolutPaymentHandler: remove duplicated VM history logging; use complete_payment - Remove VmHistoryLogger from both handler structs (now owned by VmPaymentCompletionHandler) - admin_complete_subscription_payment: add missing CheckSubscriptions WorkJob dispatch - Update invoice.rs tests to use new complete() API
- Implement StripePaymentHandler: listen() on WEBHOOK_BRIDGE, verify signature, handle payment_intent.succeeded, cancel competing upgrade PaymentIntents - Wire into listen_all_payments behind #[cfg(feature = "stripe")] - Add /api/v1/webhook/stripe route - Add test: non-VM (IpRange) payment complete dispatches CheckSubscriptions not CheckVm - 204 unit tests pass
Replace make_completion_handler (single handler for whole payment) with CompositeLineItemHandler that iterates every subscription line item and fires a type-specific handler for each one independently. A subscription with a VM + IP range now correctly runs both VmPaymentCompletionHandler and IpRangePaymentCompletionHandler. Add get_vm_by_subscription_line_item to DB trait/MySQL/mock. Add IpRangePaymentCompletionHandler (dispatches CheckSubscriptions). Handle AsnSponsoring/DnsHosting variants (CheckSubscriptions stub). Remove Box<dyn PaymentCompletionHandler> in favour of concrete CompositeLineItemHandler.
Add src/subscription/ module with a single SubscriptionLineItemHandler trait: on_payment() - called by payment pipeline after mark-paid on_expiring_soon() - called by lifecycle worker on_expired() - called by lifecycle worker on_grace_period_exceeded() - called by lifecycle worker VmLineItemHandler: history logging, CheckVm/ProcessVmUpgrade dispatch, on_expired/on_grace_period_exceeded dispatch CheckVm for hypervisor pickup IpRangeLineItemHandler: on_payment dispatches CheckSubscriptions, on_expired deactivates ip_range_subscription rows directly payments/mod.rs: complete_payment now calls line_item_handler per line item instead of the old PaymentCompletionHandler / CompositeLineItemHandler stack. Removes handle_upgrade free function (inlined into VmLineItemHandler). worker.rs handle_subscription_state: replaces all inline product branching (has_vm checks, deactivate_subscription call, etc.) with per-line-item calls to the appropriate handler. Single place to add new product types.
…f truth Schema migration: DROP COLUMN expires, auto_renewal_enabled FROM vm All expiry decisions now read subscription.expires exclusively. Auto-renewal reads subscription.auto_renewal_enabled exclusively. Changes: - lnvps_db: remove Vm.expires/auto_renewal_enabled fields; fix insert/update SQL - subscription_payment_paid: remove vm.expires write (no longer needed) - vm_payment_paid: remove vm.expires update - list_expired_vms: rewritten to join subscription table - worker vm_expires(): returns Option<DateTime> from subscription; no fallback - worker check_vms: is_new_vm detection uses subscription.is_setup = false - worker StartVm guard: checks subscription expiry - worker patch_firewall guard: checks subscription expiry - subscription/vm.rs: vm_expires_before reads from subscription - capacity: filters unpaid VMs by subscription.is_setup = false - admin extend_vm: updates subscription.expires instead of vm.expires - admin vm_ip_assignments: checks subscription.is_setup + subscription.expires - admin model AdminVmInfo: serves subscription.expires/auto_renewal_enabled - admin AdminRefundAmountInfo: serves subscription.expires - user API ApiVmStatus: serves subscription.expires/auto_renewal_enabled - api/routes.rs set_auto_renewal: updates subscription.auto_renewal_enabled - pricing engine: vm_subscription_expires() helper; next_template_expire takes base DateTime - pricing/capacity tests: set subscription.expires instead of vm.expires
Contributor
There was a problem hiding this comment.
Pull request overview
This PR migrates VM payments to the unified subscriptions payment system, removing VM-specific payment/expiry fields and introducing a generalized subscription lifecycle + payment completion pipeline across payment methods (Lightning/Revolut/Stripe).
Changes:
- Replace
vm_paymentusage withsubscription_payment(including upgrade metadata/time_value support) and link VMs viavm.subscription_line_item_id. - Generalize lifecycle enforcement with a
CheckSubscriptionsworker job and per-line-item handlers. - Add DB-level pagination across multiple admin/user list endpoints and introduce a VM→subscription migration tool.
Reviewed changes
Copilot reviewed 52 out of 55 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| work/vm-payment-to-subscription.md | Updates the migration plan/status and documents phases/tasks for the vm_payment → subscription_payment migration. |
| lnvps_db/src/mysql.rs | Implements new subscription/VM linkage queries, payment updates, company-enriched payment queries, and multiple paginated list endpoints. |
| lnvps_db/src/model.rs | Renames interval type, moves VM billing link to subscription_line_item_id, expands subscription/payment models, adds migration-only projections. |
| lnvps_db/src/lib.rs | Extends DB trait with pagination methods and new subscription/VM/payment lookup APIs. |
| lnvps_db/src/admin.rs | Updates admin DB trait (pagination + subscription_payment reporting). |
| lnvps_db/migrations/20260304000000_drop_vm_expires.sql | Drops vm.expires and vm.auto_renewal_enabled to shift expiry/auto-renewal to subscriptions. |
| lnvps_db/migrations/20260302151134_vm_subscription_link.sql | Adds subscription interval fields, payment metadata/time_value, and VM→subscription_line_item linkage. |
| lnvps_api_common/src/work/mod.rs | Adds WorkJob::CheckSubscriptions and integrates it into Display/skip logic. |
| lnvps_api_common/src/vm_history.rs | Removes logging of vm.expires and updates tests for new VM model shape. |
| lnvps_api_common/src/pricing.rs | Switches VM expiry calculations to subscription expiry; updates dedupe to use pending subscription payments. |
| lnvps_api_common/src/model.rs | Renames API interval enum and changes VM status expiry to Option<DateTime<Utc>> sourced from subscription. |
| lnvps_api_common/src/capacity.rs | Changes capacity filtering to rely on subscription setup state instead of VM expiry. |
| lnvps_api_admin/src/bin/migrate_vm_subscriptions.rs | Adds a standalone migration binary to backfill subscriptions and copy vm_payment rows into subscription_payment. |
| lnvps_api_admin/src/bin/generate_demo_data.rs | Updates demo data generation to use IntervalType and new VM model fields. |
| lnvps_api_admin/src/admin/vms.rs | Updates admin VM payment endpoints and manual completion to use subscription_payment + subscription expiry. |
| lnvps_api_admin/src/admin/vm_templates.rs | Updates default interval type enum name in template creation. |
| lnvps_api_admin/src/admin/vm_ip_assignments.rs | Switches “new/expired VM” checks to subscription is_setup / expires. |
| lnvps_api_admin/src/admin/subscriptions.rs | Adds DB-level pagination for subscriptions/payments and dispatches CheckSubscriptions after manual completion. |
| lnvps_api_admin/src/admin/roles.rs | Uses DB-level pagination for roles listing. |
| lnvps_api_admin/src/admin/reports.rs | Adapts time-series report model to optional VM/host/region fields from subscription payments. |
| lnvps_api_admin/src/admin/payment_methods.rs | Uses DB-level pagination for payment method configs listing. |
| lnvps_api_admin/src/admin/model.rs | Updates admin API models for subscription-driven expiry + enriched subscription/payment fields. |
| lnvps_api_admin/src/admin/ip_space.rs | Moves IP space/pricing/subscription listings to DB-level pagination. |
| lnvps_api_admin/src/admin/custom_pricing.rs | Moves custom pricing listing to DB-level pagination. |
| lnvps_api_admin/src/admin/cost_plans.rs | Moves cost plan listing to DB-level pagination. |
| lnvps_api_admin/Cargo.toml | Registers the new migrate_vm_subscriptions binary. |
| lnvps_api/src/worker.rs | Adds subscription lifecycle loop, VM expiry resolution via subscription, and schedules CheckSubscriptions. |
| lnvps_api/src/subscription/vm.rs | Adds VM subscription line item handler for payment/lifecycle hooks (history + work dispatch). |
| lnvps_api/src/subscription/mod.rs | Introduces generic subscription line-item handler abstraction and factory. |
| lnvps_api/src/subscription/ip_range.rs | Adds IP Range subscription line item handler for activation/deactivation lifecycle. |
| lnvps_api/src/provisioner/rollback_tests.rs | Updates provisioner tests for new VM model fields. |
| lnvps_api/src/provisioner/retry_tests.rs | Updates provisioner tests for new VM model fields. |
| lnvps_api/src/provisioner/lnvps.rs | Creates subscriptions during VM provisioning, rewrites renewal/upgrade payment creation to subscription_payment, updates upgrade flows. |
| lnvps_api/src/provisioner/integration_retry_tests.rs | Updates integration retry tests for new VM model fields. |
| lnvps_api/src/payments/stripe.rs | Implements Stripe webhook completion via centralized subscription_payment pipeline. |
| lnvps_api/src/payments/revolut.rs | Refactors Revolut completion to centralized subscription_payment pipeline + upgrade cancellation. |
| lnvps_api/src/payments/mod.rs | Adds centralized complete_payment pipeline and wires Stripe handler startup. |
| lnvps_api/src/payments/invoice.rs | Refactors Lightning invoice completion to centralized subscription_payment pipeline and updates tests. |
| lnvps_api/src/mocks.rs | Updates imports/types impacted by interval enum rename. |
| lnvps_api/src/lib.rs | Exposes the new subscription module. |
| lnvps_api/src/host/mod.rs | Updates host tests for new VM model fields. |
| lnvps_api/src/data_migration/ip6_init.rs | Uses subscription expiry instead of VM expiry when skipping expired VMs. |
| lnvps_api/src/bin/api.rs | Moves data migrations to worker mode and schedules CheckSubscriptions. |
| lnvps_api/src/api/webhook.rs | Adds Stripe webhook route behind feature flag. |
| lnvps_api/src/api/subscriptions.rs | Adds DB-level pagination and stores interval on subscriptions. |
| lnvps_api/src/api/routes.rs | Migrates VM renew/payment/invoice/history endpoints to subscription_payment and subscription-based auto-renewal. |
| lnvps_api/src/api/model.rs | Adds from_subscription_payment conversions for invoices and VM payment responses. |
| lnvps_api/src/api/legal.rs | Includes VM subscription types in agreement generation. |
| lnvps_api/src/api/ip_space.rs | Uses DB-level pagination for IP space listing. |
| docs/agents/migrations.md | Documents the migration steps and the new migration binary. |
| docs/agents/api-guidelines.md | Adds a guideline requiring DB-level pagination for list endpoints. |
| docs/agents-common | Updates subproject reference. |
| API_CHANGELOG.md | Documents API changes for VM payments/subscriptions and interval enum rename. |
| .github/workflows/build.yml | Adds workflow_dispatch inputs and conditional Docker build selection logic. |
Comments suppressed due to low confidence (3)
docs/agents/migrations.md:1
- The documented invocation uses a
--database-urlflag, but the newmigrate_vm_subscriptionsbinary's CLI (perArgs) only supports--configand--dry-run. Update the docs to match the actual CLI (or add the documented flag). As written, following these instructions will fail.
# Database Migrations
lnvps_api_common/src/capacity.rs:1
- This introduces an N+1 query pattern: for each VM on a host, it performs up to 2 additional DB calls (line item + subscription). For busy hosts this will be a noticeable latency multiplier. Prefer a DB-level query that returns only VMs whose linked subscription has
is_setup = 1(e.g., a join between vm → subscription_line_item → subscription with the host filter), or batch-load the needed subscriptions/line items.
lnvps_api_common/src/capacity.rs:1 - This introduces an N+1 query pattern: for each VM on a host, it performs up to 2 additional DB calls (line item + subscription). For busy hosts this will be a noticeable latency multiplier. Prefer a DB-level query that returns only VMs whose linked subscription has
is_setup = 1(e.g., a join between vm → subscription_line_item → subscription with the host filter), or batch-load the needed subscriptions/line items.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Replace LNVpsProvisioner with a unified SubscriptionHandler that owns the VmProvisioner and pricing engine, so all API routes, payment handlers, DVM, and worker code reference a single cohesive entry point. Rename provisioner modules (lnvps → vm, lnvps_network → vm_network), consolidate MockVmHost into DummyVmHost, and unify SubscriptionType variants (VmRenewal/VmUpgrade → Vps).
…ng e2e ports
- Add scripts/run-e2e.sh: handles full e2e lifecycle (start docker, wait
for LND, create per-run DB, patch configs, build+start APIs, run tests,
teardown)
- lnvps_e2e/src/db.rs: per-run database isolation (lnvps_e2e_{run_id}),
create/drop helpers, LNVPS_E2E_RUN_ID and LNVPS_DB_BASE_URL support
- lnvps_e2e/src/lifecycle.rs: drop test database at end of lifecycle test
- docker-compose.e2e.yaml: use ports 3377/6399 (DB/Redis) to avoid
conflicting with the dev compose stack (3376/6398)
- .github/e2e/{api,admin}-config.yaml: update to ports 3377/6399
- .github/workflows/e2e.yml: delegate entirely to run-e2e.sh
- docs/agents/e2e-tests.md: update running instructions and CI docs
…missing revolut config Also handle graceful error when get_vm_by_subscription fails during upgrade payment completion — log a warning instead of propagating an error that would mislead callers into thinking the payment was not completed.
…-> = 3) subscription_type 4 was incorrectly included in several VM-related joins, causing ambiguous or wrong results when fetching VMs by subscription.
…ination Avoids fetching all payments just to get the total count in the admin VM payments endpoint.
… errors Replaces unwrap() calls when deserialising Revolut/Stripe external_data. Payment history endpoint now always uses the paginated query path.
Previously the early return inside the nostr-nwc cfg block caused the expiry notification to be skipped for users without NWC configured. Now uses an auto_renewed flag so the fallback notification always fires when auto-renewal was not attempted.
…xpiry, grace period, renewal) - test_payment_activates_subscription_and_queues_vm - test_on_expired_stops_vm - test_on_grace_period_exceeded_deletes_vm - test_renew_after_expiry_extends_expires Also exposes DummyVmHost as pub(crate) and adds MockVmHost type alias.
…est docs build.yml conditions were inverted — builds only ran on workflow_dispatch. Now triggers on push and pull_request as well. build-and-test.md clarifies docker compose requirement before running tests.
…er infra - API_CHANGELOG.md: add entries for post-2026-03-03 changes (VmStatus.expires nullable, Stripe support, LNURL payment method, SubscriptionPayment.processing_fee and Upgrade payment type, console WebSocket, VM payment pagination, expiry notification fix, and retract false admin VM renew claim) - API_DOCUMENTATION.md: fix VmStatus.expires to optional; add processing_fee and Upgrade variant to SubscriptionPayment; add lnurl to PaymentMethod; document WebSocket console endpoint; document VM payment pagination; remove duplicate incorrect Price definition in IP space section - ADMIN_API_ENDPOINTS.md: document subscription field on AdminVmInfo; add company_base_currency, time_value, metadata to AdminSubscriptionPaymentInfo; add upgrade to SubscriptionPaymentType enum inline schemas; fix AdminVmInfo expires to nullable - lnvps_e2e: add LND integration, worker lifecycle tests, expanded DB helpers - lnvps_db: migration to drop legacy vm.created/expires columns - various: e2e infra improvements (wait-for-lnd, docker-compose, run-e2e script)
…yment-to-subscription
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
vm_paymentintosubscription_paymentso there is a single unified payment tablevm.subscription_idwork/vm-payment-to-subscription.mdThis PR will be built up incrementally. First commit: rename
VmCostPlanIntervalType→IntervalTypeandApiVmCostPlanIntervalType→ApiIntervalTypeto decouple interval types fromvm_cost_plan.