4.6 KiB
4.6 KiB
Billing System - Invoice-Based Architecture
Overview
The billing system now uses a dual-table architecture separating orders (ongoing services) from invoices (payment records).
Database Tables
1. ogp_billing_services
Purpose: Available game server packages/products
Key Fields:
service_id- Unique identifierservice_name- Display nameremote_server_id- Target server(s)price_monthly,price_year- Pricing tiersenabled- Availability flag
2. ogp_billing_orders (formerly just cart items)
Purpose: Active game server instances (ongoing services)
Key Fields:
order_id- Unique identifieruser_id- Ownerservice_id- Product referencehome_id- Panel game home ID (after provisioning)home_name- Server namestatus- Current state (see Status Flow below)order_date- When createdend_date- Expiration datepayment_txid- Last payment transactionpaid_ts- Last payment timestamp
Status Values:
in-cart- User added to cart, not yet paidpaid- Payment received, awaiting provisioninginstalled- ✅ Server provisioned and runningsuspended- Server stopped due to non-paymentexpired- Service endedrenew- Renewal pending in cart
3. ogp_billing_invoices (NEW)
Purpose: Payment records (one invoice per payment)
Key Fields:
invoice_id- Unique identifierorder_id- Links to the server orderuser_id- Customercustomer_name- Full namecustomer_email- Email addressamount- Total duecurrency- USD, EUR, etc.status-unpaidorpaidinvoice_date- When createddue_date- Payment deadlinepaid_date- When paidpayment_txid- PayPal/Stripe transaction IDpayment_method- PayPal, Stripe, etc.description- Invoice line itemsinvoice_duration- Billing period (month/year)qty- Quantity/duration multiplier
Workflow
Initial Purchase
- User selects game server package → Creates row in
billing_orders(status:in-cart) - System creates
billing_invoicesentry (status:unpaid, linked to order_id) - Cart page shows unpaid invoices
- User pays → Invoice status becomes
paid, order status becomespaid - Provisioning happens → Order status becomes
installed - Server is active until
end_date
Renewal Process
- User clicks "Renew" on active server (My Account page)
- System creates NEW invoice in
billing_invoices(status:unpaid, same order_id) - Cart shows the unpaid renewal invoice
- User pays → Invoice status becomes
paid - Order
end_dateis extended by the renewal period
Cron Automation (cron-shop.php)
The cron job checks invoice status to manage servers:
7 days before expiration:
- Check if order has unpaid invoice for upcoming period
- If NO unpaid invoice exists → Create one (status:
unpaid) - Email customer about upcoming renewal
On expiration (end_date reached):
- Check if order has unpaid invoice
- If YES → Suspend server (stop, disable FTP, unassign from user)
- Order status →
suspended
7 days after suspension:
- If still unpaid → Delete server permanently
- Order status →
expired
Key Advantages
- Clear Payment History: Each invoice represents one payment
- Audit Trail: Can track when/how much each renewal cost
- Flexible Pricing: Can adjust price per renewal (discounts, promotions)
- Multi-Payment Support: One order can have many invoices
- Accurate Status: Order status reflects server state, invoice status reflects payment
- No Race Conditions: Webhook updates invoice, provisioning updates order
Cart Logic
Cart page displays:
- All invoices with
status = 'unpaid'for the current user - Groups by order_id to show which server each invoice is for
- Total amount = SUM of all unpaid invoice amounts
After payment:
- Invoice
status→paid - Invoice
paid_date→ NOW() - Invoice
payment_txid→ transaction ID from PayPal/Stripe - Order
status→paid(if new order) orend_dateextended (if renewal)
My Account Logic
Show Invoices Section:
- Group invoices by status (unpaid, paid, overdue)
- Display invoice_date, amount, status
- Link to view invoice details
Show Current Servers Section:
- Display orders with
status = 'installed' - Show end_date (expiration)
- "Renew" button creates new invoice
Migration Notes
- Run
migration_to_invoices.sqlon existing installations - Creates
billing_invoicestable - Adds missing columns to
billing_orders - Migrates existing paid orders to have invoices
- Removes obsolete
billing_cartstable