Files
zemailnator/routes/payment.php
idevakk 289baa1286 feat(payments): implement standard webhooks validation system
Add comprehensive webhook validation and processing system with Polar.sh integration:

  - Create built-in Standard Webhooks package following official specification
  - Implement HMAC-SHA256 signature validation with base64 encoding
  - Add webhook factory for multi-provider support (Polar, Stripe, generic)
  - Replace custom Polar webhook validation with Standard Webhooks implementation
  - Add proper exception handling with custom WebhookVerificationException
  - Support sandbox mode bypass for development environments
  - Update Polar provider to use database-driven configuration
  - Enhance webhook test suite with proper Standard Webhooks format
  - Add PaymentProvider model HasFactory trait for testing
  - Implement timestamp tolerance checking (±5 minutes) for replay protection
  - Support multiple signature versions and proper header validation

  This provides a secure, reusable webhook validation system that can be extended
  to other payment providers while maintaining full compliance with Standard
  Webhooks specification.

  BREAKING CHANGE: Polar webhook validation now uses Standard Webhooks format
  with headers 'webhook-id', 'webhook-timestamp', 'webhook-signature' instead of
  previous Polar-specific headers.
2025-12-06 22:49:54 -08:00

89 lines
4.2 KiB
PHP

<?php
use App\Http\Controllers\PaymentCancelController;
use App\Http\Controllers\PaymentController;
use App\Http\Controllers\PaymentProviderController;
use App\Http\Controllers\PaymentSuccessController;
use App\Http\Controllers\WebhookController;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Payment Routes
|--------------------------------------------------------------------------
*/
Route::prefix('payment')->name('payment.')->group(function () {
Route::get('/success', [PaymentSuccessController::class, 'show'])
->middleware(['auth', 'verified'])
->name('success');
Route::get('/cancel', [PaymentCancelController::class, 'show'])
->middleware(['auth', 'verified'])
->name('cancel');
// UNIFIED: Payment processing endpoints (new unified payment system)
Route::post('/checkout', [PaymentController::class, 'createCheckout'])->name('checkout');
Route::post('/subscribe', [PaymentController::class, 'createSubscription'])->name('subscribe');
Route::get('/methods', [PaymentController::class, 'getPaymentMethods'])->name('methods');
Route::get('/history', [PaymentController::class, 'getHistory'])->name('history');
});
// UNIFIED: Enhanced checkout routes with provider selection
Route::middleware(['auth', 'verified'])->prefix('checkout')->name('checkout.')->group(function () {
Route::get('/enhanced/{plan}/{provider}', [PaymentController::class, 'enhancedCheckout'])->name('enhanced');
Route::get('/trial/{plan}/{provider}', [PaymentController::class, 'trialCheckout'])->name('trial');
});
Route::prefix('webhook')->name('webhook.')->middleware(['webhook.rate_limit'])->group(function () {
// Unified webhook handler
Route::post('/{provider}', [WebhookController::class, 'handle'])->name('unified');
// Individual provider handlers (for direct provider-specific webhooks)
Route::post('/stripe', [WebhookController::class, 'stripe'])->name('stripe');
Route::post('/lemon-squeezy', [WebhookController::class, 'lemonSqueezy'])->name('lemon_squeezy');
Route::post('/polar', [WebhookController::class, 'polar'])->name('polar');
Route::post('/oxapay', [WebhookController::class, 'oxapay'])->name('oxapay');
Route::post('/crypto', [WebhookController::class, 'crypto'])->name('crypto');
// Legacy route for backward compatibility
Route::post('/payment/{provider}', [PaymentController::class, 'webhook'])->name('payment');
});
/*
|--------------------------------------------------------------------------
| Payment Provider Management Routes
|--------------------------------------------------------------------------
*/
Route::prefix('providers')->name('providers.')->group(function () {
// Provider status and configuration
Route::get('/', [PaymentProviderController::class, 'index'])->name('index');
Route::get('/{provider}', [PaymentProviderController::class, 'show'])->name('show');
Route::post('/{provider}/test', [PaymentProviderController::class, 'test'])->name('test');
Route::post('/{provider}/toggle', [PaymentProviderController::class, 'toggle'])->name('toggle');
Route::put('/{provider}/config', [PaymentProviderController::class, 'updateConfig'])->name('update.config');
Route::post('/refresh', [PaymentProviderController::class, 'refresh'])->name('refresh');
});
/*
|--------------------------------------------------------------------------
| Activation Key Routes
|--------------------------------------------------------------------------
*/
Route::prefix('activation-keys')->name('activation-keys.')->group(function () {
Route::post('/redeem', [PaymentProviderController::class, 'redeemActivationKey'])->name('redeem');
Route::get('/validate/{key}', [PaymentProviderController::class, 'validateActivationKey'])->name('validate');
});
/*
|--------------------------------------------------------------------------
| Crypto Payment Routes
|--------------------------------------------------------------------------
*/
Route::prefix('crypto')->name('crypto.')->group(function () {
Route::get('/rates/{crypto}', [PaymentProviderController::class, 'getCryptoRate'])->name('rates');
Route::get('/convert', [PaymentProviderController::class, 'convertUsdToCrypto'])->name('convert');
});