phpcorelab / payment-gateways
Provider-agnostic PHP payment gateway library. Unified API for Razorpay, PhonePe, PayU, Paytm, Juspay and Cashfree.
Requires
- php: ^8.1
- guzzlehttp/guzzle: ^7.0
- psr/log: ^3.0
Requires (Dev)
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.0
- squizlabs/php_codesniffer: ^3.7
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Provider-agnostic PHP payment gateway library.
A unified, strongly-typed API for Indian payment gateways: Razorpay, Cashfree, PhonePe, PayU, Juspay, and Paytm.
Switch payment gateways instantly with a single configuration line — zero changes to your checkout flow or business logic.
Table of Contents
- Key Features
- Requirements
- Installation
- Supported Providers
- Architecture & How It Works
- Configuration
- Usage Guide
- Advanced Features
- Framework Integrations
- DTO Reference
- Exceptions & Error Handling
- Development & Testing
- License
Key Features
- Unified Interface: Same methods (
createOrder,verifyPayment,refund,handleWebhook) work identically across all supported gateways. - Zero Heavy SDKs: Lightweight implementation built directly on Guzzle 7 and native cryptography without bulky vendor SDK dependencies.
- Strictly Typed & DTO Driven: Typed data transfer objects (
OrderPayload,OrderResult,PaymentResult,RefundResult,PaymentEvent,PaymentStatus) for clean, predictable code and IDE autocompletion. - Provider Hot-Swapping: Switch active providers dynamically in runtime or per request based on customer preference, routing rules, or gateway health.
- Secure Webhook Verification: Built-in HMAC-SHA256 and checksum verification per provider to secure your webhook endpoints against replay and tampering attacks.
- Extensible Registry: Register proprietary gateways or custom in-house aggregators without modifying library core.
- PSR-3 Logging: Plug in Monolog, Laravel Log, or any PSR-3 logger to track orders, provider switches, and transaction events.
Requirements
- PHP: ^8.1
- Extensions:
json,curl,hash,openssl - Dependencies:
guzzlehttp/guzzle: ^7.0,psr/log: ^3.0
Installation
composer require phpcorelab/payment-gateways
Supported Providers
| Provider | Integration Type | Create Order | Verify Payment | Refund | Webhook Verification | API Version / Spec |
|---|---|---|---|---|---|---|
| Razorpay | SDK Modal / Standard | ✅ | ✅ (HMAC-SHA256) | ✅ | ✅ (HMAC-SHA256) | Orders & Payments API v1 |
| Cashfree | SDK / Web Checkout | ✅ | ✅ (API Poll / ID match) | ✅ | ✅ (HMAC-SHA256) | PG API v2023-08-01 |
| PhonePe | Hosted / Custom | ✅ | ✅ (Checksum SHA256) | ✅ | ✅ (Checksum SHA256) | Standard PG v1 |
| PayU | Form POST / Hosted | ✅ | ✅ (Reverse SHA512) | ✅ | ✅ (Reverse SHA512) | Web Checkout / PostService |
| Juspay | Express Checkout / SDK | ✅ | ✅ (Order Status API) | ✅ | ✅ (HTTP Basic Auth) | Express Checkout / Session |
| Paytm | All-in-One SDK | 🚧 (Stub ready) | 🚧 | 🚧 | 🚧 | JWT / Transaction API |
Architecture & How It Works
The library isolates gateway-specific quirks behind a clean unified sequence:
[ Frontend ] [ Backend (Your App) ] [ Payment Provider ]
| | |
|--- 1. POST /checkout/order --------->| |
| |--- gateway->createOrder() ------>|
| |<-- OrderResult ------------------|
|<-- 2. Return { sdkPayload / url } ---| |
| |
|--- 3. Launch SDK Modal / Redirect to Provider ------------------------->|
|<-- 4. User completes payment on Provider UI <---------------------------|
| |
|--- 5. POST /checkout/verify (data) ->| |
| |--- gateway->verifyPayment() ---->|
| |<-- PaymentResult ----------------|
|<-- 6. Confirmed ({ status: SUCCESS })| |
| |
| |<=== Inbound Webhook Event ========|
| |--- gateway->handleWebhook() -----|
| | (Verifies signature & parses) |
Configuration
Environment Variables (.env)
Copy the required keys to your .env file:
# Active Gateway & Environment ('sandbox' or 'live') ACTIVE_PROVIDER=razorpay ENVIRONMENT=sandbox # ─── Razorpay ─────────────────────────────────────────────── RAZORPAY_SANDBOX_KEY_ID=rzp_test_xxxxxxxxxxxxxx RAZORPAY_SANDBOX_KEY_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx RAZORPAY_LIVE_KEY_ID=rzp_live_xxxxxxxxxxxxxx RAZORPAY_LIVE_KEY_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # ─── Cashfree ─────────────────────────────────────────────── CASHFREE_SANDBOX_APP_ID=TESTxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx CASHFREE_SANDBOX_SECRET_KEY=cfsk_ma_test_xxxxxxxxxxxxxxxxxxxxxxxx CASHFREE_LIVE_APP_ID=your-live-app-id CASHFREE_LIVE_SECRET_KEY=your-live-secret-key # ─── PhonePe ──────────────────────────────────────────────── PHONEPE_SANDBOX_MERCHANT_ID=PGTESTPAYUAT PHONEPE_SANDBOX_SALT_KEY=099eb0cd-02cf-4dc2-a4f3-4b12a1d9d5e5 PHONEPE_SANDBOX_SALT_INDEX=1 PHONEPE_LIVE_MERCHANT_ID=YOUR_LIVE_MERCHANT_ID PHONEPE_LIVE_SALT_KEY=your-live-salt-key PHONEPE_LIVE_SALT_INDEX=1 # ─── PayU ─────────────────────────────────────────────────── PAYU_SANDBOX_MERCHANT_KEY=gtKFFx PAYU_SANDBOX_MERCHANT_SALT=eCwWELxi PAYU_LIVE_MERCHANT_KEY=your-live-key PAYU_LIVE_MERCHANT_SALT=your-live-salt # ─── Paytm ────────────────────────────────────────────────── PAYTM_SANDBOX_MID=YOUR_SANDBOX_MID PAYTM_SANDBOX_MERCHANT_KEY=your-sandbox-key PAYTM_LIVE_MID=YOUR_LIVE_MID PAYTM_LIVE_MERCHANT_KEY=your-live-key # ─── Juspay ───────────────────────────────────────────────── JUSPAY_SANDBOX_API_KEY=your-sandbox-api-key JUSPAY_SANDBOX_MERCHANT_ID=your-sandbox-merchant-id JUSPAY_SANDBOX_WEBHOOK_USERNAME=your-webhook-user JUSPAY_SANDBOX_WEBHOOK_PASSWORD=your-webhook-pass JUSPAY_LIVE_API_KEY=your-live-api-key JUSPAY_LIVE_MERCHANT_ID=your-live-merchant-id JUSPAY_LIVE_WEBHOOK_USERNAME=your-webhook-user JUSPAY_LIVE_WEBHOOK_PASSWORD=your-webhook-pass
GatewayConfig Setup
Create a GatewayConfig instance using array configuration:
use PHPCoreLab\PaymentGateways\Core\GatewayConfig; use PHPCoreLab\PaymentGateways\PaymentGateway; $config = GatewayConfig::fromArray([ 'active_provider' => $_ENV['ACTIVE_PROVIDER'] ?? 'razorpay', 'environment' => $_ENV['ENVIRONMENT'] ?? 'sandbox', // 'sandbox' | 'live' 'providers' => [ 'razorpay' => [ 'sandbox_key_id' => $_ENV['RAZORPAY_SANDBOX_KEY_ID'], 'sandbox_key_secret' => $_ENV['RAZORPAY_SANDBOX_KEY_SECRET'], 'live_key_id' => $_ENV['RAZORPAY_LIVE_KEY_ID'], 'live_key_secret' => $_ENV['RAZORPAY_LIVE_KEY_SECRET'], ], 'cashfree' => [ 'sandbox_app_id' => $_ENV['CASHFREE_SANDBOX_APP_ID'], 'sandbox_secret_key' => $_ENV['CASHFREE_SANDBOX_SECRET_KEY'], 'live_app_id' => $_ENV['CASHFREE_LIVE_APP_ID'], 'live_secret_key' => $_ENV['CASHFREE_LIVE_SECRET_KEY'], 'api_version' => '2023-08-01', // optional, defaults to 2023-08-01 ], 'phonepe' => [ 'sandbox_merchant_id' => $_ENV['PHONEPE_SANDBOX_MERCHANT_ID'], 'sandbox_salt_key' => $_ENV['PHONEPE_SANDBOX_SALT_KEY'], 'sandbox_salt_index' => (int) ($_ENV['PHONEPE_SANDBOX_SALT_INDEX'] ?? 1), 'live_merchant_id' => $_ENV['PHONEPE_LIVE_MERCHANT_ID'], 'live_salt_key' => $_ENV['PHONEPE_LIVE_SALT_KEY'], 'live_salt_index' => (int) ($_ENV['PHONEPE_LIVE_SALT_INDEX'] ?? 1), ], 'payu' => [ 'sandbox_merchant_key' => $_ENV['PAYU_SANDBOX_MERCHANT_KEY'], 'sandbox_merchant_salt' => $_ENV['PAYU_SANDBOX_MERCHANT_SALT'], 'live_merchant_key' => $_ENV['PAYU_LIVE_MERCHANT_KEY'], 'live_merchant_salt' => $_ENV['PAYU_LIVE_MERCHANT_SALT'], ], 'juspay' => [ 'sandbox_api_key' => $_ENV['JUSPAY_SANDBOX_API_KEY'], 'sandbox_merchant_id' => $_ENV['JUSPAY_SANDBOX_MERCHANT_ID'], 'sandbox_webhook_username' => $_ENV['JUSPAY_SANDBOX_WEBHOOK_USERNAME'], 'sandbox_webhook_password' => $_ENV['JUSPAY_SANDBOX_WEBHOOK_PASSWORD'], 'live_api_key' => $_ENV['JUSPAY_LIVE_API_KEY'], 'live_merchant_id' => $_ENV['JUSPAY_LIVE_MERCHANT_ID'], 'live_webhook_username' => $_ENV['JUSPAY_LIVE_WEBHOOK_USERNAME'], 'live_webhook_password' => $_ENV['JUSPAY_LIVE_WEBHOOK_PASSWORD'], ], 'paytm' => [ 'sandbox_mid' => $_ENV['PAYTM_SANDBOX_MID'], 'sandbox_merchant_key' => $_ENV['PAYTM_SANDBOX_MERCHANT_KEY'], 'live_mid' => $_ENV['PAYTM_LIVE_MID'], 'live_merchant_key' => $_ENV['PAYTM_LIVE_MERCHANT_KEY'], ], ], ]); $gateway = new PaymentGateway($config);
Usage Guide
1. Create an Order
Amounts are always specified in paise (100 paise = 1 INR).
use PHPCoreLab\PaymentGateways\DTOs\OrderPayload; $order = $gateway->createOrder(new OrderPayload( orderId: 'ORD-2026-001', amountPaisa: 49900, // ₹499.00 in paise currency: 'INR', customerName: 'Aman Roy', customerEmail: 'aman@example.com', customerPhone: '9876543210', returnUrl: 'https://yourdomain.com/payment/callback', cancelUrl: 'https://yourdomain.com/payment/cancel', description: 'Pro Membership Subscription', meta: ['user_id' => '1029'], )); // Send response to your frontend: // $order->orderId -> Merchant order ID ('ORD-2026-001') // $order->providerOrderId -> Provider reference ID (e.g. 'order_MNV...' or '12345') // $order->paymentUrl -> Direct redirect URL (PhonePe, PayU, Juspay) // $order->sdkPayload -> Parameters for client-side SDKs (Razorpay, Cashfree, Juspay)
2. Frontend Checkout Flows
A. Razorpay (Standard Checkout Modal)
Include <script src="https://checkout.razorpay.com/v1/checkout.js"></script>:
const options = { ...sdkPayload, handler: function (response) { // Forward verification payload to backend fetch('/payment/confirm', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ order_id: response.razorpay_order_id, razorpay_order_id: response.razorpay_order_id, razorpay_payment_id: response.razorpay_payment_id, razorpay_signature: response.razorpay_signature, }), }).then(r => r.json()).then(handlePaymentResult); }, }; const rzp = new Razorpay(options); rzp.open();
B. Cashfree (JS SDK v3 Checkout)
Include <script src="https://sdk.cashfree.com/js/v3/cashfree.js"></script>:
const cashfree = Cashfree({ mode: sdkPayload.environment === "live" ? "production" : "sandbox", }); cashfree.checkout({ paymentSessionId: sdkPayload.payment_session_id, redirectTarget: "_modal", // or "_self" to redirect }).then((result) => { if (result.error) { console.error("Cashfree checkout error:", result.error); } if (result.paymentDetails) { // Confirm payment status with your backend fetch('/payment/confirm', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ order_id: sdkPayload.order_id, }), }); } });
C. PhonePe / PayU / Juspay (Redirect Flow)
For redirect-based gateways, redirect the user directly to $order->paymentUrl:
if (paymentUrl) { window.location.href = paymentUrl; }
3. Verify Payment
When the customer completes the payment and is redirected back to your returnUrl (or after client SDK completion), pass the response parameters to verifyPayment():
use PHPCoreLab\PaymentGateways\DTOs\PaymentStatus; // Pass the merchant order ID and all payload parameters from request $result = $gateway->verifyPayment($orderId, $_POST ?: json_decode(file_get_contents('php://input'), true)); if ($result->isSuccessful()) { // Payment confirmed! $providerPaymentId = $result->providerPaymentId; // Gateway payment ID $bankRef = $result->providerRef; // RRN / Bank UTR $amountPaisa = $result->amountPaisa; // Captured amount // Fulfill order in database... } else { $reason = $result->failureReason; // Log failure or prompt user to retry... }
4. Process Refunds
Initiate full or partial refunds:
// Pass gateway payment ID (or order ID depending on provider) and amount in paise $refundResult = $gateway->refund($result->providerPaymentId, 49900); if ($refundResult->success) { echo "Refund initiated successfully! Refund ID: " . $refundResult->refundId; } else { echo "Refund failed: " . $refundResult->message; }
5. Handle Webhooks
Webhooks notify your application asynchronously of payment completion, failures, and refunds. The library automatically validates cryptographic signatures:
use PHPCoreLab\PaymentGateways\DTOs\PaymentStatus; use PHPCoreLab\PaymentGateways\Exceptions\WebhookVerificationException; try { $rawBody = file_get_contents('php://input'); $headers = getallheaders(); $event = $gateway->handleWebhook($rawBody, $headers); switch ($event->status) { case PaymentStatus::Success: // Mark order as paid: $event->orderId, $event->providerPaymentId break; case PaymentStatus::Failed: // Mark order as failed: $event->failureReason break; case PaymentStatus::Refunded: // Record refund break; } http_response_code(200); echo json_encode(['status' => 'ok']); } catch (WebhookVerificationException $e) { // Signature mismatch - reject request! http_response_code(400); echo json_encode(['error' => $e->getMessage()]); }
Advanced Features
Runtime Provider Switching
Switch active gateways on the fly (e.g. for fallback routing, multi-currency routing, or customer selection) without reinstantiating the gateway:
// Switch to Cashfree dynamically $gateway->switchProvider('cashfree'); // Now create order or verify with Cashfree $order = $gateway->createOrder($payload); // Switch back to Razorpay $gateway->switchProvider('razorpay');
Custom & Mock Provider Registration
Easily register a custom provider or a test mock at runtime:
use PHPCoreLab\PaymentGateways\Contracts\PaymentProviderInterface; use PHPCoreLab\PaymentGateways\DTOs\OrderPayload; use PHPCoreLab\PaymentGateways\DTOs\OrderResult; use PHPCoreLab\PaymentGateways\DTOs\PaymentResult; use PHPCoreLab\PaymentGateways\DTOs\RefundResult; use PHPCoreLab\PaymentGateways\DTOs\PaymentEvent; class CustomGatewayAdapter implements PaymentProviderInterface { public function createOrder(OrderPayload $payload): OrderResult { /* ... */ } public function verifyPayment(string $orderId, array $data): PaymentResult { /* ... */ } public function refund(string $providerPaymentId, int $amountPaisa): RefundResult { /* ... */ } public function parseWebhook(string $rawBody, array $headers): PaymentEvent { /* ... */ } public function getName(): string { return 'custom_gateway'; } } // Register as an instance or closure factory $gateway->registerProvider('custom_gateway', new CustomGatewayAdapter()); // Activate it $gateway->switchProvider('custom_gateway');
PSR-3 Logging Support
Pass any PSR-3 compatible logger (e.g., Monolog, Laravel Log) to monitor payment events:
use Monolog\Logger; use Monolog\Handler\StreamHandler; $logger = new Logger('payment'); $logger->pushHandler(new StreamHandler(__DIR__ . '/payments.log')); $gateway = new PaymentGateway($config, $logger);
Inspecting Available Providers
$providers = $gateway->availableProviders(); // ['razorpay', 'cashfree', 'phonepe', 'payu', 'paytm', 'juspay'] $currentProvider = $gateway->activeProvider()->getName(); // 'razorpay' $env = $gateway->getEnvironment(); // Environment::Sandbox or Environment::Live
Framework Integrations
Laravel
1. Configuration (config/payment-gateways.php)
return [ 'active_provider' => env('ACTIVE_PROVIDER', 'razorpay'), 'environment' => env('ENVIRONMENT', 'sandbox'), 'providers' => [ 'razorpay' => [ 'sandbox_key_id' => env('RAZORPAY_SANDBOX_KEY_ID'), 'sandbox_key_secret' => env('RAZORPAY_SANDBOX_KEY_SECRET'), 'live_key_id' => env('RAZORPAY_LIVE_KEY_ID'), 'live_key_secret' => env('RAZORPAY_LIVE_KEY_SECRET'), ], 'cashfree' => [ 'sandbox_app_id' => env('CASHFREE_SANDBOX_APP_ID'), 'sandbox_secret_key' => env('CASHFREE_SANDBOX_SECRET_KEY'), 'live_app_id' => env('CASHFREE_LIVE_APP_ID'), 'live_secret_key' => env('CASHFREE_LIVE_SECRET_KEY'), ], 'phonepe' => [ 'sandbox_merchant_id' => env('PHONEPE_SANDBOX_MERCHANT_ID'), 'sandbox_salt_key' => env('PHONEPE_SANDBOX_SALT_KEY'), 'sandbox_salt_index' => env('PHONEPE_SANDBOX_SALT_INDEX', 1), 'live_merchant_id' => env('PHONEPE_LIVE_MERCHANT_ID'), 'live_salt_key' => env('PHONEPE_LIVE_SALT_KEY'), 'live_salt_index' => env('PHONEPE_LIVE_SALT_INDEX', 1), ], // ... other providers ], ];
2. Service Provider (app/Providers/AppServiceProvider.php)
use PHPCoreLab\PaymentGateways\PaymentGateway; use PHPCoreLab\PaymentGateways\Core\GatewayConfig; use Illuminate\Support\Facades\Log; public function register(): void { $this->app->singleton(PaymentGateway::class, function () { $config = GatewayConfig::fromArray(config('payment-gateways')); return new PaymentGateway($config, Log::channel('daily')); }); }
3. Controller (PaymentController.php)
namespace App\Http\Controllers; use Illuminate\Http\Request; use PHPCoreLab\PaymentGateways\PaymentGateway; use PHPCoreLab\PaymentGateways\DTOs\OrderPayload; use PHPCoreLab\PaymentGateways\DTOs\PaymentStatus; class PaymentController extends Controller { public function __construct(private PaymentGateway $gateway) {} public function createOrder(Request $request) { $order = $this->gateway->createOrder(new OrderPayload( orderId: 'ORD-' . uniqid(), amountPaisa: $request->integer('amount') * 100, customerName: $request->user()->name, customerEmail: $request->user()->email, customerPhone: $request->user()->phone, returnUrl: route('payment.callback'), )); return response()->json($order); } public function confirmPayment(Request $request, string $orderId) { $result = $this->gateway->verifyPayment($orderId, $request->all()); if ($result->isSuccessful()) { return response()->json(['status' => 'success']); } return response()->json(['status' => 'failed', 'reason' => $result->failureReason], 422); } public function webhook(Request $request) { $event = $this->gateway->handleWebhook($request->getContent(), $request->headers->all()); if ($event->status === PaymentStatus::Success) { // Update order status in database } return response()->json(['status' => 'ok']); } }
Note: For Laravel, exclude
/payment/webhookfrom CSRF protection inbootstrap/app.php(Laravel 11+) orapp/Http/Middleware/VerifyCsrfToken.php(Laravel 10 and earlier).
Vanilla PHP
<?php require_once __DIR__ . '/vendor/autoload.php'; use PHPCoreLab\PaymentGateways\PaymentGateway; use PHPCoreLab\PaymentGateways\Core\GatewayConfig; use PHPCoreLab\PaymentGateways\DTOs\OrderPayload; $config = GatewayConfig::fromArray([ 'active_provider' => 'cashfree', 'environment' => 'sandbox', 'providers' => [ 'cashfree' => [ 'sandbox_app_id' => 'YOUR_APP_ID', 'sandbox_secret_key' => 'YOUR_SECRET_KEY', ], ], ]); $gateway = new PaymentGateway($config); $result = $gateway->createOrder(new OrderPayload( orderId: 'ORDER_' . time(), amountPaisa: 19900, )); header('Content-Type: application/json'); echo json_encode($result);
DTO Reference
OrderPayload
| Property | Type | Default | Description |
|---|---|---|---|
orderId |
string |
required | Your unique order reference ID |
amountPaisa |
int |
required | Amount in paise (100 paise = ₹1.00) |
currency |
string |
'INR' |
ISO currency code |
customerName |
?string |
null |
Customer's full name |
customerEmail |
?string |
null |
Customer's email address |
customerPhone |
?string |
null |
Customer's phone number |
returnUrl |
?string |
null |
URL redirected to after payment |
cancelUrl |
?string |
null |
URL redirected to if cancelled |
description |
?string |
null |
Note/description of the order |
meta |
array |
[] |
Arbitrary custom metadata |
OrderResult
| Property | Type | Description |
|---|---|---|
orderId |
string |
Your original order ID |
providerOrderId |
string |
Gateway's order ID (e.g. order_abc or 12345) |
amountPaisa |
int |
Order amount in paise |
currency |
string |
Order currency code |
paymentUrl |
?string |
Hosted checkout/redirect URL (if applicable) |
sdkPayload |
?array |
Configuration payload for client SDKs |
raw |
array |
Full raw gateway response |
PaymentResult
| Property | Type | Description |
|---|---|---|
orderId |
string |
Your order reference ID |
providerOrderId |
string |
Gateway's order identifier |
providerPaymentId |
string |
Gateway's unique payment identifier |
status |
PaymentStatus |
Enum: Success, Failed, Pending, Expired, Refunded, Cancelled |
amountPaisa |
int |
Captured amount in paise |
providerRef |
?string |
Bank reference number (RRN, UTR) |
failureReason |
?string |
Failure message from provider if failed |
raw |
array |
Complete raw verification response |
isSuccessful() |
bool |
Helper method returns true if status is Success |
RefundResult
| Property | Type | Description |
|---|---|---|
refundId |
string |
Unique refund reference ID |
success |
bool |
Whether the refund was accepted |
message |
?string |
Provider status description or note |
raw |
array |
Complete raw refund response |
PaymentEvent (Webhook)
| Property | Type | Description |
|---|---|---|
orderId |
string |
Associated merchant order ID |
providerOrderId |
string |
Associated gateway order ID |
providerPaymentId |
string |
Associated gateway payment ID |
status |
PaymentStatus |
Normalized transaction status |
amountPaisa |
int |
Transaction amount in paise |
providerRef |
?string |
Bank reference number (UTR / RRN) |
failureReason |
?string |
Error or decline message if failed |
raw |
array |
Full decoded webhook payload |
Exceptions & Error Handling
All library exceptions extend PHPCoreLab\PaymentGateways\Exceptions\PaymentGatewayException:
PaymentGatewayException (RuntimeException)
├── ProviderException
├── WebhookVerificationException
└── ProviderNotFoundException
| Exception | Cause | Properties |
|---|---|---|
ProviderException |
Gateway API HTTP error or rejection | $e->provider, $e->responseBody |
WebhookVerificationException |
Invalid webhook signature or tampering detected | $e->getMessage() |
ProviderNotFoundException |
Specified provider is not registered in registry | $e->getMessage() |
use PHPCoreLab\PaymentGateways\Exceptions\ProviderException; use PHPCoreLab\PaymentGateways\Exceptions\WebhookVerificationException; try { $result = $gateway->verifyPayment($orderId, $data); } catch (WebhookVerificationException $e) { // Signature manipulation or forged callback } catch (ProviderException $e) { // API failure from provider echo "Gateway [{$e->provider}] returned error: " . $e->getMessage(); print_r($e->responseBody); }
Development & Testing
Running the Test Suite
composer test
Static Analysis (PHPStan Level 8)
composer analyse
Code Style (PSR-12)
composer cs
License
MIT License — Copyright (c) PHPCoreLab.
See LICENSE for details.