advancingu/stripe-subscription-bundle
Laravel bundle for managing Stripe subscriptions and billing flows. Provides helpers for plans, customers, trials, cancellations, and webhook handling, aiming to simplify common subscription tasks and integrate Stripe into your app with minimal setup.
Installation Add the bundle via Composer:
composer require advancingu/stripe-subscription-bundle
Register the bundle in config/bundles.php:
return [
// ...
Advancingu\StripeSubscriptionBundle\StripeSubscriptionBundle::class => ['all' => true],
];
Configuration Publish the default config:
php artisan vendor:publish --provider="Advancingu\StripeSubscriptionBundle\StripeSubscriptionBundle" --tag="config"
Update .env with your Stripe keys:
STRIPE_SECRET_KEY=your_secret_key
STRIPE_WEBHOOK_SECRET=your_webhook_secret
First Use Case: Create a Subscription
Use the SubscriptionService in a controller:
use Advancingu\StripeSubscriptionBundle\Service\SubscriptionService;
public function createSubscription(Request $request, SubscriptionService $subscriptionService)
{
$customer = $subscriptionService->createCustomer($request->input('email'), $request->input('payment_method'));
$subscription = $subscriptionService->createSubscription($customer->id, 'price_id_here');
return response()->json($subscription);
}
Webhook Setup
Add the webhook route in routes/web.php:
Route::post('/stripe/webhook', [StripeWebhookController::class, 'handleWebhook']);
Ensure the StripeWebhookController is implemented (see Implementation Patterns).
Customer Management
$customer = $subscriptionService->createCustomer($email, $paymentMethodId);
$customer = $subscriptionService->updateCustomer($customerId, ['name' => 'New Name']);
$customer = $subscriptionService->getCustomer($customerId);
Subscription Lifecycle
$subscription = $subscriptionService->createSubscription($customerId, 'price_id', [
'trial_period_days' => 7,
]);
$subscriptionService->cancelSubscription($subscriptionId);
$subscriptionService->resumeSubscription($subscriptionId);
$subscriptionService->updateSubscription($subscriptionId, 'new_price_id');
Webhook Handling
use Advancingu\StripeSubscriptionBundle\Controller\StripeWebhookController;
class StripeWebhookController extends AbstractStripeWebhookController
{
protected function handleEvent($event)
{
switch ($event->type) {
case 'invoice.payment_succeeded':
// Handle successful payment
break;
case 'invoice.payment_failed':
// Handle failed payment
break;
case 'customer.subscription.deleted':
// Handle subscription cancellation
break;
}
}
}
getEventHandlers() in your controller to customize event responses:
protected function getEventHandlers()
{
return [
'invoice.payment_succeeded' => [$this, 'handlePaymentSucceeded'],
'customer.subscription.deleted' => [$this, 'handleSubscriptionDeleted'],
];
}
Integration with Laravel Models
use Advancingu\StripeSubscriptionBundle\Traits\HasStripeSubscription;
class User extends Model
{
use HasStripeSubscription;
}
Then access subscriptions via:
$user->stripeSubscription()->create($priceId);
Testing
stripe-php mocking libraries or the bundle’s test utilities:
$this->mockStripeService()->shouldReceive('createCustomer')->once()->andReturn($mockCustomer);
Webhook Verification
STRIPE_WEBHOOK_SECRET is incorrect or missing..env and ensure the route is protected:
public function handleWebhook(Request $request)
{
$payload = $request->getContent();
$sigHeader = $request->header('Stripe-Signature');
$event = \Stripe\Webhook::constructEvent($payload, $sigHeader, config('stripe.webhook_secret'));
}
Customer ID Mismatch
id directly as Stripe’s customer field can cause conflicts if IDs are reused or deleted.stripe_customer_id) in your model to store Stripe’s customer ID.Subscription Sync Delays
$subscriptionService->syncAllSubscriptions();
Price ID Hardcoding
price_id in controllers or services reduces flexibility.$price = $subscriptionService->getPrice('product_id', ['interval' => 'month']);
$subscription = $subscriptionService->createSubscription($customerId, $price->id);
Error Handling
price_id) may not be caught gracefully.try {
$subscription = $subscriptionService->createSubscription($customerId, $priceId);
} catch (\Stripe\Exception\ApiErrorException $e) {
Log::error('Stripe error: ' . $e->getMessage());
throw new \Exception('Failed to create subscription');
}
Configuration Overrides
Customize the bundle’s behavior via config/stripe_subscription.php:
'webhook_endpoint' => '/custom/webhook/endpoint',
'default_currency' => 'eur',
'sync_cron_frequency' => 'daily', // 'daily', 'weekly', or custom
Logging Enable debug logging for Stripe events:
'logging' => [
'enabled' => true,
'channel' => 'stripe',
],
Configure the channel in config/logging.php.
Testing Webhooks Locally Use Stripe CLI to test webhooks:
stripe listen --forward-to localhost:8000/stripe/webhook
Extending the Bundle
SubscriptionService:
class CustomSubscriptionService extends SubscriptionService
{
public function customCreateSubscription($customerId, $priceId, array $options = [])
{
$options['metadata']['custom_field'] = 'value';
return parent::createSubscription($customerId, $priceId, $options);
}
}
event(new SubscriptionUpdated($subscription));
Performance
syncAllSubscriptions() sparingly (e.g., during off-peak hours) to avoid rate limits.$price = Cache::remember("stripe_price_{$priceId}", now()->addHours(1), function () use ($priceId) {
return $subscriptionService->getPrice($priceId);
});
Migration Notes
$subscriptionService->migrateSubscription($oldSubscriptionData);
How can I help you explore Laravel packages today?