php-commerce/routes/api.php
Claude 0685429c74
feat: provisioning API endpoints and service documentation
Implement the provisioning API endpoints referenced in routes/api.php
and add comprehensive PHPDoc to service classes missing documentation.

Provisioning API (Issue #15):
- ProductApiController: ping, product listing, product lookup by SKU
- EntitlementApiController: create, show, suspend, unsuspend, cancel, renew
- Uncomment and activate provisioning route group with commerce.api middleware
- Register commerce.api and commerce.matrix middleware aliases in Boot.php

Service documentation (Issue #14):
- CreditNoteService: lifecycle, FIFO ordering, state machine
- RefundService: gateway orchestration, eligibility, transaction safety
- SubscriptionService: lifecycle, proration, fixed-day billing periods
- CouponService: sanitisation, validation, Orderable polymorphism
- InvoiceService: PDF generation, storage, email delivery

Fixes #14
Fixes #15

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:34:57 +00:00

102 lines
6 KiB
PHP

<?php
declare(strict_types=1);
use Core\Mod\Commerce\Controllers\Api\CommerceController;
use Core\Mod\Commerce\Controllers\Api\EntitlementApiController;
use Core\Mod\Commerce\Controllers\Api\ProductApiController;
use Core\Mod\Commerce\Controllers\Webhooks\BTCPayWebhookController;
use Core\Mod\Commerce\Controllers\Webhooks\StripeWebhookController;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Commerce API Routes
|--------------------------------------------------------------------------
|
| API routes for the Commerce module including payment webhooks,
| billing management, and provisioning endpoints.
|
*/
// ─────────────────────────────────────────────────────────────────────────────
// Payment Webhooks (no auth - uses signature verification + IP-based rate limiting)
// ─────────────────────────────────────────────────────────────────────────────
Route::prefix('webhooks')->group(function () {
// Rate limiting is handled per-IP in the controllers via WebhookRateLimiter
// This provides better protection than global throttle middleware:
// - Per-IP limits (60/min default, 300/min for trusted gateway IPs)
// - Different limits per gateway
// - Proper 429 responses with Retry-After headers
Route::post('/btcpay', [BTCPayWebhookController::class, 'handle'])
->name('api.webhook.btcpay');
Route::post('/stripe', [StripeWebhookController::class, 'handle'])
->name('api.webhook.stripe');
});
// ─────────────────────────────────────────────────────────────────────────────
// Commerce Provisioning API (Bearer token auth)
// ─────────────────────────────────────────────────────────────────────────────
Route::middleware('commerce.api')->prefix('provisioning')->group(function () {
Route::get('/ping', [ProductApiController::class, 'ping'])->name('api.commerce.ping');
Route::get('/products', [ProductApiController::class, 'index'])->name('api.commerce.products');
Route::get('/products/{code}', [ProductApiController::class, 'show'])->name('api.commerce.products.show');
Route::post('/entitlements', [EntitlementApiController::class, 'store'])->name('api.commerce.entitlements.store');
Route::get('/entitlements/{id}', [EntitlementApiController::class, 'show'])->name('api.commerce.entitlements.show');
Route::post('/entitlements/{id}/suspend', [EntitlementApiController::class, 'suspend'])->name('api.commerce.entitlements.suspend');
Route::post('/entitlements/{id}/unsuspend', [EntitlementApiController::class, 'unsuspend'])->name('api.commerce.entitlements.unsuspend');
Route::post('/entitlements/{id}/cancel', [EntitlementApiController::class, 'cancel'])->name('api.commerce.entitlements.cancel');
Route::post('/entitlements/{id}/renew', [EntitlementApiController::class, 'renew'])->name('api.commerce.entitlements.renew');
});
// ─────────────────────────────────────────────────────────────────────────────
// Commerce Billing API (authenticated + verified)
// ─────────────────────────────────────────────────────────────────────────────
Route::middleware(['auth', 'verified'])->prefix('commerce')->group(function () {
// ── Read-only endpoints ──────────────────────────────────────────────
// Billing overview
Route::get('/billing', [CommerceController::class, 'billing'])
->name('api.commerce.billing');
// Orders
Route::get('/orders', [CommerceController::class, 'orders'])
->name('api.commerce.orders.index');
Route::get('/orders/{order}', [CommerceController::class, 'showOrder'])
->name('api.commerce.orders.show');
// Invoices
Route::get('/invoices', [CommerceController::class, 'invoices'])
->name('api.commerce.invoices.index');
Route::get('/invoices/{invoice}', [CommerceController::class, 'showInvoice'])
->name('api.commerce.invoices.show');
Route::get('/invoices/{invoice}/download', [CommerceController::class, 'downloadInvoice'])
->name('api.commerce.invoices.download');
// Subscription (read)
Route::get('/subscription', [CommerceController::class, 'subscription'])
->name('api.commerce.subscription');
// Usage
Route::get('/usage', [CommerceController::class, 'usage'])
->name('api.commerce.usage');
// ── State-changing endpoints (rate-limited) ──────────────────────────
Route::middleware('throttle:6,1')->group(function () {
// Subscription management
Route::post('/cancel', [CommerceController::class, 'cancelSubscription'])
->name('api.commerce.cancel');
Route::post('/resume', [CommerceController::class, 'resumeSubscription'])
->name('api.commerce.resume');
// Plan changes
Route::post('/upgrade/preview', [CommerceController::class, 'previewUpgrade'])
->name('api.commerce.upgrade.preview');
Route::post('/upgrade', [CommerceController::class, 'executeUpgrade'])
->name('api.commerce.upgrade');
});
});