shopper/payment
Laravel payment package for Shopper: unified API to manage gateways, transactions, refunds, and payment statuses. Provides configurable drivers, events, and webhooks to integrate checkout flows with your app and keep payments in sync across providers.
Installation
composer require shopper/payment
Publish the config file:
php artisan vendor:publish --provider="Shopper\Payment\PaymentServiceProvider" --tag="config"
Configuration
Edit config/payment.php to define your preferred payment gateways (e.g., Stripe, PayPal, or custom providers). Example:
'gateways' => [
'stripe' => [
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
],
'paypal' => [
'client_id' => env('PAYPAL_CLIENT_ID'),
'secret' => env('PAYPAL_SECRET'),
],
],
First Use Case: Creating a Payment Resolve the payment service in a controller or service:
use Shopper\Payment\Facades\Payment;
public function createPayment()
{
$payment = Payment::create('stripe', [
'amount' => 1000, // $10.00
'currency' => 'USD',
'description' => 'Order #12345',
'metadata' => ['order_id' => 12345],
]);
return $payment->getCheckoutUrl(); // Redirect or return URL
}
Webhook Handling
Define a route for handling payment webhooks (e.g., POST /payment/webhook). Use the Payment::handleWebhook() method to process events:
public function handleWebhook(Request $request)
{
Payment::handleWebhook($request, function ($event) {
// Handle successful/failed payments
if ($event->type === 'payment.succeeded') {
// Update order status, send confirmation, etc.
}
});
}
Payment Creation & Processing
Payment::create($gateway, $data) to initialize a payment. The $data array should include:
amount (integer in cents)currency (ISO 3-letter code)description (optional)metadata (key-value pairs for tracking)payment_method_id if using tokens:
$payment = Payment::create('stripe', [
'amount' => 2000,
'currency' => 'USD',
'payment_method_id' => $token,
]);
Subscription Management
Payment::createSubscription($gateway, $data):
$subscription = Payment::createSubscription('stripe', [
'price_id' => 'price_123',
'customer_id' => 'cus_123',
]);
Payment::cancelSubscription($gateway, $subscriptionId).Refunds & Captures
Payment::refund('stripe', $paymentId, ['amount' => 500]);
Payment::capture('stripe', $paymentId, ['amount' => 1000]);
Webhook Events
handleWebhook():
Payment::handleWebhook($request, function ($event) {
switch ($event->type) {
case 'payment.succeeded':
// Fulfill order
break;
case 'payment.failed':
// Notify user
break;
case 'invoice.payment_succeeded':
// Update subscription
break;
}
});
Payment::validateWebhook($request).Laravel Events Dispatch custom events after payment actions:
event(new \App\Events\PaymentSucceeded($payment));
Middleware for Auth
Protect payment routes with Laravel middleware (e.g., auth:sanctum):
Route::post('/payment/webhook', [PaymentController::class, 'handleWebhook'])
->middleware('auth:sanctum');
Testing Use mock gateways in tests:
Payment::shouldReceive('create')->with('stripe', [...])->andReturn($mockPayment);
Logging
Enable debug logging in config/payment.php:
'debug' => env('APP_ENV') === 'local',
Custom Gateways
Extend the base Gateway class to support new providers:
namespace App\Providers;
use Shopper\Payment\Contracts\Gateway;
class CustomGateway implements Gateway {
public function create(array $data) { ... }
public function handleWebhook(array $payload) { ... }
}
Register in config/payment.php:
'gateways' => [
'custom' => \App\Providers\CustomGateway::class,
],
Gateway Configuration
env() variables for gateway keys/secrets..env.example to document required variables and validate config on boot:
if (!config('payment.gateways.stripe.key')) {
throw new \RuntimeException('Stripe key not configured.');
}
Webhook Validation
stripe-signature header).handleWebhook():
if (!Payment::validateWebhook($request)) {
abort(403, 'Invalid webhook signature');
}
Currency/Amount Mismatch
amount as dollars instead of cents (e.g., 10.00 vs. 1000).$validator = Validator::make($data, [
'amount' => 'required|integer|min:1',
]);
Idempotency
idempotency_key field in payment requests:
Payment::create('stripe', [
'amount' => 1000,
'idempotency_key' => uniqid(),
]);
Gateway-Specific Quirks
intent (e.g., sale, authorize) in the data array.payment_method_types (e.g., card) for payment methods.Enable Debug Mode
Set 'debug' => true in config/payment.php to log raw API responses.
Inspect Events Dump webhook payloads for debugging:
Payment::handleWebhook($request, function ($event) {
\Log::debug('Webhook event:', $event->toArray());
});
Test with Sandbox
Always test payments in sandbox mode (e.g., Stripe’s test cards: 4242 4242 4242 4242).
Check for Deprecations Monitor the package’s release notes for breaking changes (e.g., Stripe API version updates).
Custom Event Handling
Extend the PaymentEvent class to add provider-specific data:
namespace App\Events;
use Shopper\Payment\Events\PaymentEvent;
class CustomPaymentEvent extends PaymentEvent {
public function getCustomField() { ... }
}
Gateway Decorators Wrap gateways to add pre/post-processing:
Payment::extend('stripe', function ($app) {
return new class($app['payment.stripe']) {
public function create(array $data) {
// Add custom logic (e.g., logging)
return $this->gateway->create($data);
}
};
});
Service Provider Binding
Override the default service binding in your app’s AppServiceProvider:
public function register()
{
$this->app->bind(\Shopper\Payment\Contracts\Payment::class, function ($app) {
return new \App\Services\CustomPaymentService($app);
});
}
Macros for Facade
How can I help you explore Laravel packages today?