equidna / stag-herd
Multi-provider payment webhook verification and processing for Laravel.
Installs: 1
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/equidna/stag-herd
Requires
- php: ^8.1
- equidna/laravel-toolkit: ^1.0
- illuminate/support: ^10.0|^11.0|^12.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.91
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.5
- squizlabs/php_codesniffer: ^4.0
This package is auto-updated.
Last update: 2025-12-15 22:17:46 UTC
README
Multi-provider payment webhook verification and processing for Laravel applications.
Features
- Multi-Provider Support: PayPal, Stripe (Google Pay), Mercado Pago, Openpay, Clip, Conekta, Kueski Pay
- Webhook Verification: Cryptographic signature validation for all providers
- Idempotency: Prevents duplicate webhook processing
- Event-Driven: Dispatches Laravel events for payment state changes
- Configurable: Extensible handler system via configuration
- Decoupled: Clean contracts for integration with any Laravel application
Installation
composer require equidna/stag-herd
Configuration
Publish the configuration file:
php artisan vendor:publish --tag=stag-herd-config
Environment Variables
Add the following to your .env:
# Enable/Disable Payment Methods STRIPE_ENABLED=true PAYPAL_ENABLED=true MERCADOPAGO_ENABLED=false CONEKTA_ENABLED=false KUESKI_ENABLED=false OPENPAY_ENABLED=false CLIP_ENABLED=false CASH_PAYMENT_ENABLED=true # Webhook Secrets STRIPE_WEBHOOK_SECRET=whsec_... PAYPAL_WEBHOOK_ID=... MERCADOPAGO_WEBHOOK_SECRET=... CONEKTA_WEBHOOK_SECRET=... KUESKI_WEBHOOK_SECRET=... OPENPAY_WEBHOOK_SECRET=... # PayPal Configuration PAYPAL_SANDBOX=true PAYPAL_KEY=... PAYPAL_SECRET=... # Clip Configuration CLIP_API_KEY=...
Payment Model
Configure your Payment Eloquent model in config/stag-herd.php:
'payment_model' => 'App\Models\Finance\Payment',
Custom Payment Methods
Payment methods from the package are registered automatically. To add custom handlers, configure them in config/stag-herd.php:
'custom_methods' => [ 'CLIENT_CREDIT' => [ 'handler' => 'App\Classes\Payment\Handlers\ClientCreditHandler', 'description' => 'Linea de crédito cliente', 'enabled' => true, ], 'GIFT' => [ 'handler' => 'App\Classes\Payment\Handlers\GiftHandler', 'description' => 'Regalo', 'enabled' => true, ], ],
Usage
Requesting a Payment
use Equidna\StagHerd\Payment\Payment; $payment = Payment::request( amount: 100.00, method: 'PAYPAL', order: $order, // Must implement PayableOrder contract method_data: (object) ['return_url' => 'https://...'] ); // Access payment link $paymentLink = $payment->getPaymentModel()->link;
Implementing Contracts
Your application must implement the required contracts:
PayableOrder
use Equidna\StagHerd\Contracts\PayableOrder; class Order implements PayableOrder { public function getID(): int|string { return $this->id_order; } public function getClient(): PayableClient { return $this->client; } public function getStatus(): string { return $this->status; } public function processPayment($payment): void { /* Update order */ } public static function fromID(int|string $id): static { /* Load order */ } }
PayableClient
use Equidna\StagHerd\Contracts\PayableClient; class Client implements PayableClient { public function getID(): int|string { return $this->id_client; } public function getName(): string { return $this->name; } public function getEmail(): string { return $this->email; } }
Custom Payment Handlers
Extend PaymentHandler for custom payment methods:
namespace App\Payment\Handlers; use Equidna\StagHerd\Payment\Handlers\PaymentHandler; use stdClass; class CustomHandler extends PaymentHandler { public const PAYMENT_METHOD = 'CUSTOM_METHOD'; public function requestPayment(): stdClass { $result = parent::requestPayment(); // Custom logic $result->method_id = 'custom-' . uniqid(); $result->result = 'PENDING'; return $result; } protected function validatePayment($paymentModel): stdClass { $result = parent::validatePayment($paymentModel); // Validation logic $result->result = 'APPROVED'; return $result; } }
Webhook Routes
Webhooks are automatically registered with the configured prefix (default: stag-herd):
POST /stag-herd/stripePOST /stag-herd/paypalPOST /stag-herd/mercado-pagoPOST /stag-herd/conektaPOST /stag-herd/kueskiPOST /stag-herd/openpay
Webhooks
This package verifies webhook signatures for each provider using their official schemes. Ensure you send the raw request body to the verifier and configure secrets.
Stripe
- Header:
Stripe-Signature: t=<timestamp>,v1=<signature> - Signature:
HMAC-SHA256("<timestamp>.<raw_body>", STRIPE_WEBHOOK_SECRET) - Tolerance: default 300 seconds; events outside tolerance are rejected.
- Idempotency: the event
idis deduplicated to prevent reprocessing.
Mercado Pago
- Headers:
X-Signature,X-Request-Id - Signature parts:
ts=<unix_ts>,v1=<signature> - Manifest:
id:<data.id>;request-id:<X-Request-Id>;ts:<ts>; - Signature:
HMAC-SHA256(manifest, MERCADOPAGO_WEBHOOK_SECRET) data.idis taken from query/body JSONdata.idorid.
Conekta
- Header:
Digest: sha-256=<base64_hmac> - Signature:
base64(HMAC-SHA256(raw_body, CONEKTA_WEBHOOK_SECRET)) - Secret source:
config('stag-herd.conekta.secret')(populate fromCONEKTA_WEBHOOK_SECRET). - Event id: body JSON
idwhen present.
Kueski Pay
- Headers:
X-Kueski-Signature,X-Kueski-Timestamp - Signature:
HMAC-SHA256("<timestamp>" . raw_body, KUESKI_WEBHOOK_SECRET) - Event id: body JSON
event_idorid.
Openpay
- Header:
Verification-Signature: t=<timestamp>,v1=<signature>(orSignature-Digest) - Signature:
HMAC-SHA256("<timestamp>.<raw_body>", OPENPAY_WEBHOOK_SECRET) - Event id: body JSON
event_idorid.
Idempotency
WebhookVerifier::checkIdempotency(eventId, provider, ttl)returns true for duplicates and stores new events via cache.- Configure a persistent cache backend in production to retain deduplication keys.
Examples
POST /webhook HTTP/1.1 Stripe-Signature: t=1710000000,v1=abcdef... Content-Type: application/json {"id":"evt_123","type":"charge.succeeded"}
POST /webhook HTTP/1.1 X-Signature: ts=1710000000,v1=abcdef... X-Request-Id: req-123 Content-Type: application/json {"data":{"id":"123"}}
POST /webhook HTTP/1.1 Digest: sha-256=abc123base64= Content-Type: application/json {"id":"evt_123","type":"charge.paid"}
POST /webhook HTTP/1.1 X-Kueski-Timestamp: 1710000000 X-Kueski-Signature: abcdef... Content-Type: application/json {"event_id":"evt_ksk","id":"ksk_123"}
POST /webhook HTTP/1.1 Verification-Signature: t=1710000000,v1=abcdef... Content-Type: application/json {"event_id":"evt_op","id":"op_123"}
Events
Listen to payment events in your application:
use Equidna\StagHerd\Events\PaymentApproved; use Illuminate\Support\Facades\Event; Event::listen(PaymentApproved::class, function (PaymentApproved $event) { $payment = $event->payment; // Handle approved payment });
Testing
composer test
Static Analysis
composer phpstan
License
MIT License. See LICENSE file for details.