7.0 KiB
Stripe Checkout Integration
This document describes the Stripe Checkout integration for the Laravel Shop package.
Configuration
Enable Stripe
Add the following to your .env file:
SHOP_STRIPE_ENABLED=true
STRIPE_KEY=pk_test_...
STRIPE_SECRET=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
Configure Services
In your config/services.php:
'stripe' => [
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
'webhook_secret' => env('STRIPE_WEBHOOK_SECRET'),
],
Price Configuration
All products and product prices must have a stripe_price_id before they can be used in Stripe Checkout.
Setting Stripe Price ID
$product = Product::find($id);
$price = $product->defaultPrice()->first();
$price->update(['stripe_price_id' => 'price_...']);
Creating a Checkout Session
API Endpoint
POST /api/shop/stripe/checkout/{cartId}
Example Request
curl -X POST https://your-domain.com/api/shop/stripe/checkout/cart-uuid-here \
-H "Authorization: Bearer YOUR_TOKEN"
Example Response
{
"session_id": "cs_test_...",
"url": "https://checkout.stripe.com/c/pay/cs_test_..."
}
Redirect the user to the url to complete payment.
Handling Success/Cancel
Success URL
GET /api/shop/stripe/success?session_id={SESSION_ID}&cart_id={CART_ID}
When payment is successful:
- Cart status is updated to
CONVERTED - Cart's
converted_atis set - ProductPurchases are updated with:
status→COMPLETEDcharge_id→ Stripe Payment Intent IDamount_paid→ Amount from Stripe (in dollars, converted from cents)
Cancel URL
GET /api/shop/stripe/cancel?cart_id={CART_ID}
When payment is cancelled, the cart remains in ACTIVE status and the user can try again.
Webhook Handler
Webhook URL
POST /api/shop/stripe/webhook
Supported Events
The webhook handler processes the following Stripe events:
checkout.session.completed- Updates cart to converted, updates purchasescheckout.session.async_payment_succeeded- Same as completedcheckout.session.async_payment_failed- Logs failurecharge.succeeded- Updates purchases with charge infocharge.failed- Marks purchases asFAILEDpayment_intent.succeeded- Updates purchasespayment_intent.payment_failed- Marks purchases asFAILED
Configuring Webhook in Stripe
- Go to Stripe Dashboard → Developers → Webhooks
- Click "Add endpoint"
- Enter your webhook URL:
https://your-domain.com/api/shop/stripe/webhook - Select events to listen to (or select "receive all events")
- Copy the signing secret and add it to your
.envasSTRIPE_WEBHOOK_SECRET
Route Customization
Disabling Stripe Routes
The Stripe routes are automatically registered if:
config('shop.stripe.enabled')istrue- Routes haven't already been defined in your Laravel app
You can manually define routes in your application's route files to override the default behavior.
Custom Routes Example
// routes/web.php or routes/api.php
use Blax\Shop\Http\Controllers\StripeCheckoutController;
use Blax\Shop\Http\Controllers\StripeWebhookController;
Route::post('custom/stripe/checkout/{cartId}', [StripeCheckoutController::class, 'createCheckoutSession'])
->name('shop.stripe.checkout');
Route::get('custom/stripe/success', [StripeCheckoutController::class, 'success'])
->name('shop.stripe.success');
Route::get('custom/stripe/cancel', [StripeCheckoutController::class, 'cancel'])
->name('shop.stripe.cancel');
Route::post('custom/stripe/webhook', [StripeWebhookController::class, 'handleWebhook'])
->name('shop.stripe.webhook');
ProductPurchase Updates
The webhook handler automatically updates ProductPurchase records with charge information if the columns exist:
charge_id- Stripe Payment Intent IDamount_paid- Amount paid in dollars
These fields are automatically populated from the fillable array on the ProductPurchase model.
Error Handling
Missing Stripe Price ID
If a cart item doesn't have a stripe_price_id, the checkout session creation will fail with:
{
"error": "Item 'Product Name' is missing a Stripe price ID"
}
Stripe API Errors
All Stripe API errors are caught and logged. The response will include:
{
"error": "Failed to create checkout session: [error message]"
}
Pool Products with MayBePoolProduct Trait
Pool-related methods have been moved to the MayBePoolProduct trait to keep the Product model cleaner.
Using Pool Methods
All pool methods work the same way, they're just now in a trait:
$pool = Product::find($poolId);
// Check if pool
$pool->isPool(); // returns bool
// Get available quantity
$pool->getAvailableQuantity($from, $until);
// Get pool max quantity
$pool->getPoolMaxQuantity($from, $until);
// Claim pool stock
$pool->claimPoolStock($quantity, $reference, $from, $until);
// Release pool stock
$pool->releasePoolStock($reference);
// Pricing methods
$pool->getLowestPoolPrice();
$pool->getHighestPoolPrice();
$pool->getPoolPriceRange();
$pool->setPoolPricingStrategy('lowest'); // 'lowest', 'highest', 'average'
// Validation
$pool->validatePoolConfiguration();
// Availability methods
$pool->getPoolAvailabilityCalendar($start, $end, $quantity);
$pool->getSingleItemsAvailability($from, $until);
$pool->isPoolAvailable($from, $until, $quantity);
$pool->getPoolAvailablePeriods($start, $end, $quantity, $minDays);
Benefits of Trait
- Cleaner Product model
- Pool functionality can be used by other models in the future
- Better separation of concerns
- Easier testing and maintenance
Example Usage Flow
// 1. Create a product with Stripe price
$product = Product::create([...]);
$price = ProductPrice::create([
'purchasable_id' => $product->id,
'purchasable_type' => Product::class,
'stripe_price_id' => 'price_1234567890',
'unit_amount' => 2000, // $20.00 in cents
'is_default' => true,
]);
// 2. Add to cart
$cart = auth()->user()->currentCart();
$cart->addToCart($product, 1);
// 3. Create Stripe checkout session
$response = Http::post('/api/shop/stripe/checkout/' . $cart->id);
$checkoutUrl = $response->json('url');
// 4. Redirect user to Stripe
return redirect($checkoutUrl);
// 5. Stripe redirects back to success URL
// 6. Webhook processes payment
// 7. Cart is converted, purchases are completed
Testing
Mock Stripe in your tests:
use Stripe\Stripe;
use Stripe\Checkout\Session as StripeSession;
// Mock Stripe session creation
Stripe::setApiKey('sk_test_fake');
StripeSession::create([...]); // Use test mode
Security Considerations
- Always verify webhook signatures - Set
STRIPE_WEBHOOK_SECRETin production - Use HTTPS - Stripe requires HTTPS for webhooks
- Validate cart ownership - Ensure users can only checkout their own carts
- Test mode first - Use Stripe test keys during development
- Monitor webhooks - Check Stripe Dashboard for webhook delivery issues