Installation:
composer require veeqtoh/cashier-paystack
php artisan vendor:publish --provider="Veeqtoh\CashierPaystack\CashierPaystackServiceProvider" --tag="config"
php artisan migrate
Configure .env:
PAYSTACK_SECRET_KEY=your_paystack_secret_key
PAYSTACK_PUBLIC_KEY=your_paystack_public_key
PAYSTACK_BASE_URL=https://api.paystack.co
Make a Model Billable:
use Veeqtoh\CashierPaystack\Billable;
class User extends Authenticatable
{
use Billable;
}
First Use Case: Create a subscription for a user:
$user->newSubscription('main', 'monthly-plan-id')
->create($paymentMethodId);
config/cashier-paystack.php for customization.database/migrations/ for schema adjustments.Subscription Management:
$user->newSubscription('main', 'plan_code')
->withDays(30) // Trial period
->create($paymentMethodId);
$user->subscription('main')->swap('new_plan_code');
$user->subscription('main')->cancel();
Webhooks:
Handle Paystack events via CashierPaystackServiceProvider:
// In config/cashier-paystack.php
'webhook' => [
'enabled' => true,
'secret' => env('PAYSTACK_WEBHOOK_SECRET'),
'handler' => \App\Handlers\PaystackWebhookHandler::class,
],
Implement handleEvent() in your handler:
public function handleEvent($payload)
{
switch ($payload['event']) {
case 'charge.success':
// Logic for successful charge
break;
}
}
Payment Links: Generate pre-filled payment links:
$paymentLink = $user->createPaymentLink(
amount: 10000, // Amount in kobo (NGN)
reference: 'user_123_payment',
callback_url: route('payment.callback')
);
Laravel Cashier Compatibility:
Leverage existing Cashier methods (e.g., invoices(), subscriptions()) alongside Paystack-specific features.
$user->subscriptions()->where('ends_at', '>', now())->get();
Plan Management: Fetch Paystack plans dynamically:
$plans = \Veeqtoh\CashierPaystack\Facades\Paystack::getPlans();
Multi-Currency:
Configure default currency in .env:
PAYSTACK_CURRENCY=NGN
Override per subscription:
$user->newSubscription('main', 'plan_code')
->withCurrency('USD')
->create($paymentMethodId);
Webhook Verification:
verifyWebhook helper:
use Veeqtoh\CashierPaystack\Facades\Paystack;
$isValid = Paystack::verifyWebhook($payload, $signature);
PAYSTACK_WEBHOOK_SECRET in .env will break webhook handling.Currency Mismatch:
$amountInKobo = 10000; // NGN 100
Plan Code vs. Plan ID:
'monthly-premium') for subscriptions, not IDs.plan_id instead of plan_code will throw an error.Trial Periods:
withDays() matches Paystack’s trial settings.subscriptions table for trial_ends_at discrepancies.Log Webhook Payloads: Add logging in your webhook handler for debugging:
\Log::debug('Paystack Webhook Payload', ['payload' => $payload]);
Paystack API Errors:
Inspect the errors array in responses:
try {
$subscription = $user->newSubscription('main', 'plan_code')->create($paymentMethodId);
} catch (\Exception $e) {
\Log::error('Paystack Error:', $e->getMessage());
// Check $e->getResponse() for Paystack-specific errors
}
Custom Fields: Attach metadata to subscriptions:
$user->newSubscription('main', 'plan_code')
->withCustomFields(['custom_field' => 'value'])
->create($paymentMethodId);
Middleware: Protect subscription routes:
Route::middleware(['subscribed'])->group(function () {
// Routes requiring active subscriptions
});
Add to app/Http/Kernel.php:
protected $routeMiddleware = [
'subscribed' => \Veeqtoh\CashierPaystack\Middleware\EnsureSubscription::class,
];
Testing:
Use Paystack’s test mode (PAYSTACK_BASE_URL=https://test.paystack.co) and mock webhooks:
// In tests
$this->actingAs($user)
->post('/subscribe', [
'plan_code' => 'test-plan',
'payment_method' => 'test_card_id',
]);
Localization: Override Paystack’s default language:
// In config/cashier-paystack.php
'language' => 'fr', // French
How can I help you explore Laravel packages today?