This guide covers how to integrate mavi pay into a Laravel application. You will use the mavi pay REST API directly with Laravel's HTTP client to create checkouts, verify webhooks, and manage customer entitlements.
No additional package is required. Laravel's built-in HTTP client handles all API communication. Add your mavi pay credentials to your .env file:
MAVI_PAY_ACCESS_TOKEN=your_api_key_here
MAVI_PAY_WEBHOOK_SECRET=your_webhook_secret_here
MAVI_PAY_BASE_URL=https://api.mavifinans.shAdd the config values to config/services.php:
// config/services.php
'mavi_pay' => [
'access_token' => env('MAVI_PAY_ACCESS_TOKEN'),
'webhook_secret' => env('MAVI_PAY_WEBHOOK_SECRET'),
'base_url' => env('MAVI_PAY_BASE_URL', 'https://api.mavifinans.sh'),
],Create a service class to wrap mavi pay API calls.
// app/Services/MaviPayService.php
namespace App\Services;
use Illuminate\Support\Facades\Http;
class MaviPayService
{
private string $baseUrl;
private string $accessToken;
public function __construct()
{
$this->baseUrl = config('services.mavi_pay.base_url');
$this->accessToken = config('services.mavi_pay.access_token');
}
private function client()
{
return Http::withToken($this->accessToken)
->baseUrl($this->baseUrl . '/api/v1');
}
public function listProducts()
{
return $this->client()->get('/products')->json();
}
public function createCheckout(string $productId, string $customerEmail, string $successUrl)
{
return $this->client()->post('/checkouts', [
'product_id' => $productId,
'customer_email' => $customerEmail,
'success_url' => $successUrl,
])->json();
}
public function getCustomerOrders(string $customerId)
{
return $this->client()->get('/orders', [
'customer_id' => $customerId,
])->json();
}
public function getSubscriptions(string $customerId)
{
return $this->client()->get('/subscriptions', [
'customer_id' => $customerId,
])->json();
}
}Register the service in AppServiceProvider or use it directly via dependency injection.
// app/Http/Controllers/CheckoutController.php
namespace App\Http\Controllers;
use App\Services\MaviPayService;
use Illuminate\Http\Request;
class CheckoutController extends Controller
{
public function __construct(private MaviPayService $maviPay) {}
public function create(Request $request)
{
$request->validate([
'product_id' => 'required|string',
]);
$checkout = $this->maviPay->createCheckout(
productId: $request->product_id,
customerEmail: $request->user()->email,
successUrl: route('checkout.success'),
);
return redirect($checkout['url']);
}
}Create a controller to receive and verify mavi pay webhook events.
// app/Http/Controllers/MaviPayWebhookController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class MaviPayWebhookController extends Controller
{
public function handle(Request $request)
{
$payload = $request->getContent();
$signature = $request->header('x-mavi-pay-signature');
$secret = config('services.mavi_pay.webhook_secret');
$expected = hash_hmac('sha256', $payload, $secret);
if (!hash_equals($expected, $signature)) {
return response()->json(['error' => 'Invalid signature'], 401);
}
$event = json_decode($payload, true);
match ($event['type']) {
'checkout.completed' => $this->handleCheckoutCompleted($event['data']),
'subscription.cancelled' => $this->handleSubscriptionCancelled($event['data']),
default => Log::info('Unhandled mavi pay event: ' . $event['type']),
};
return response()->json(['received' => true]);
}
private function handleCheckoutCompleted(array $data): void
{
// Grant access, send confirmation, update user record
Log::info('Checkout completed', [
'order_id' => $data['order_id'],
'customer_email' => $data['customer_email'],
]);
}
private function handleSubscriptionCancelled(array $data): void
{
// Revoke access at end of billing period
Log::info('Subscription cancelled', [
'customer_id' => $data['customer_id'],
]);
}
}// routes/web.php
use App\Http\Controllers\MaviPayWebhookController;
Route::post('/webhooks/mavi-pay', [MaviPayWebhookController::class, 'handle'])
->withoutMiddleware([\App\Http\Middleware\VerifyCsrfToken::class]);Exclude the webhook route from CSRF verification since the request comes from mavi pay servers, not from a browser form.
Create a middleware to protect routes behind a purchase check.
// app/Http/Middleware/RequireMaviPayPurchase.php
namespace App\Http\Middleware;
use App\Services\MaviPayService;
use Closure;
use Illuminate\Http\Request;
class RequireMaviPayPurchase
{
public function __construct(private MaviPayService $maviPay) {}
public function handle(Request $request, Closure $next, string $productId)
{
$user = $request->user();
$customerId = $user->mavi_pay_customer_id;
if (!$customerId) {
return redirect('/pricing');
}
$orders = $this->maviPay->getCustomerOrders($customerId);
$hasAccess = collect($orders['items'])->contains(function ($order) use ($productId) {
return $order['product_id'] === $productId
&& in_array($order['status'], ['paid', 'active']);
});
if (!$hasAccess) {
return redirect('/pricing');
}
return $next($request);
}
}Use it on routes:
Route::get('/premium', [PremiumController::class, 'index'])
->middleware('mavi-pay-purchase:prod_xxxxxxxx');// app/Http/Controllers/ProductController.php
namespace App\Http\Controllers;
use App\Services\MaviPayService;
class ProductController extends Controller
{
public function __construct(private MaviPayService $maviPay) {}
public function index()
{
$products = $this->maviPay->listProducts();
return view('products.index', [
'products' => $products['items'],
]);
}
}MAVI_PAY_ACCESS_TOKEN=your_api_key_here
MAVI_PAY_WEBHOOK_SECRET=your_webhook_secret_here
MAVI_PAY_BASE_URL=https://api.mavifinans.sh