nedarta / yii2-omnipay
An elegant Omnipay payment processing extension for the Yii 2 framework.
Requires
- php: >=7.4
- omnipay/common: ^3.0
- yiisoft/yii2: ~2.0.0
Requires (Dev)
- phpunit/phpunit: ^9.5
README
A premium, elegant, and lightweight payment processing integration for the Yii 2 framework, built on top of the powerful Omnipay library. Configure payment gateways as standard Yii 2 components with clean, modern magic delegation.
Features
- ✨ One Component, One Gateway: Simple, modular architecture that integrates seamlessly into Yii 2's dependency injection container.
- 💳 Clean Magic Delegation: Call gateway methods (like
purchase,completePurchase, etc.) directly on the component. - 🛡️ Strict & Safe: Safe lazy-initialization guards and robust type validation to prevent runtime errors.
- ⚙️ Immutable Config & Reinitialization: Built to support reactive changes in dynamic execution contexts (e.g. CLI, tests).
- 🧪 Test-Driven: Structured to work seamlessly with PHPUnit.
Installation
Install the package via Composer:
composer require nedarta/yii2-omnipay
To use specific gateways, you should also require their respective Omnipay drivers:
# For Stripe composer require omnipay/stripe # For PayPal composer require omnipay/paypal
Configuration
Add the gateway components directly to your application configuration file (usually config/web.php or common/config/main.php):
return [ 'components' => [ 'stripe' => [ 'class' => 'nedarta\omnipay\OmniPayComponent', 'gateway' => 'Stripe', 'parameters' => [ 'apiKey' => 'sk_test_51Px...your_stripe_secret_key...', ], ], 'paypal' => [ 'class' => 'nedarta\omnipay\OmniPayComponent', 'gateway' => 'PayPal_Express', 'parameters' => [ 'username' => 'paypal-developer_api1.example.com', 'password' => 'ABC123XYZ456', 'signature' => 'AFcWxV21C7fd0xs3bSMuz153.4VeA1F-34zQv1F.c7', ], 'testMode' => true, // Optional global shortcut ], ], ];
Usage Examples
1. Stripe Checkout
Because of magic-method delegation, you can call all underlying gateway methods directly on the Yii 2 component (Yii::$app->stripe).
namespace app\controllers; use Yii; use yii\web\Controller; use yii\web\BadRequestHttpException; class PaymentController extends Controller { /** * Process a direct credit card charge via Stripe */ public function actionStripeCharge() { $request = Yii::$app->request; // Stripe token generated on the frontend by Stripe.js Elements $token = $request->post('stripeToken'); $amount = 29.99; // Amount in USD if (!$token) { throw new BadRequestHttpException('Missing stripe payment token.'); } try { // Direct call to Stripe gateway purchase() via magic method delegation $response = Yii::$app->stripe->purchase([ 'amount' => $amount, 'currency' => 'USD', 'token' => $token, 'description' => 'Order #1042 Payment', ])->send(); if ($response->isSuccessful()) { $transactionReference = $response->getTransactionReference(); Yii::$app->session->setFlash('success', "Payment successful! Ref: {$transactionReference}"); return $this->redirect(['site/success']); } elseif ($response->isRedirect()) { // Redirect to 3D Secure / off-site authentication if required return $response->redirect(); } else { $errorMessage = $response->getMessage(); Yii::$app->session->setFlash('error', "Payment failed: {$errorMessage}"); } } catch (\Exception $e) { Yii::$app->session->setFlash('error', "An error occurred: " . $e->getMessage()); } return $this->redirect(['site/checkout']); } }
2. PayPal Express Checkout Workflow
PayPal Express requires a redirection to PayPal's checkout page and a secondary step to capture the payment on return.
Step A: Initiate the PayPal Purchase
namespace app\controllers; use Yii; use yii\web\Controller; use yii\helpers\Url; class PaypalController extends Controller { /** * Start the PayPal Express Checkout flow */ public function actionCheckout() { $amount = 99.00; try { // Direct call to purchase() via magic method delegation on the paypal component $response = Yii::$app->paypal->purchase([ 'amount' => $amount, 'currency' => 'USD', 'description' => 'Premium Subscription - Annual Plan', 'returnUrl' => Url::to(['paypal/success'], true), 'cancelUrl' => Url::to(['paypal/cancel'], true), ])->send(); if ($response->isRedirect()) { // Redirect to PayPal checkout page return $response->redirect(); } else { Yii::$app->session->setFlash('error', $response->getMessage()); } } catch (\Exception $e) { Yii::$app->session->setFlash('error', "Could not initiate payment: " . $e->getMessage()); } return $this->redirect(['site/checkout']); } }
Step B: Capture and Complete the Purchase
/** * Complete the PayPal payment after successful return */ public function actionSuccess() { $request = Yii::$app->request; $token = $request->get('token'); $payerId = $request->get('PayerID'); try { // Direct call to completePurchase() via magic method delegation $response = Yii::$app->paypal->completePurchase([ 'amount' => 99.00, 'currency' => 'USD', 'transactionReference' => $token, 'payerId' => $payerId, ])->send(); if ($response->isSuccessful()) { $data = $response->getData(); $transactionId = $data['PAYMENTINFO_0_TRANSACTIONID'] ?? 'Unknown'; Yii::$app->session->setFlash('success', "PayPal payment completed successfully! Transaction ID: {$transactionId}"); return $this->redirect(['site/success']); } else { Yii::$app->session->setFlash('error', "Transaction completion failed: " . $response->getMessage()); } } catch (\Exception $e) { Yii::$app->session->setFlash('error', "An error occurred during verification: " . $e->getMessage()); } return $this->redirect(['site/checkout']); } /** * Handle user cancellation */ public function actionCancel() { Yii::$app->session->setFlash('info', 'You have cancelled the PayPal payment checkout process.'); return $this->redirect(['site/checkout']); }
Advanced Usage: Dynamic Reinitialization
In multi-tenant setups, SaaS environments, or instances where merchant credentials must be loaded dynamically from a database at runtime, you can reconfigure components on the fly.
1. Update Parameters Dynamically
To update configuration parameters for the current gateway:
Yii::$app->stripe->setParameters([ 'apiKey' => 'sk_test_dynamic_merchant_key...', ]);
2. Complete Gateway Rebuilding
To reconfigure the entire component dynamically (e.g., swapping gateway drivers, updating test mode flags, and resetting parameters all at once):
Yii::$app->stripe->reinitialize([ 'gateway' => 'PayPal_Express', 'testMode' => true, 'parameters' => [ 'username' => 'merchant-dynamic_api1.example.com', 'password' => 'SECRET_PWD_123', 'signature' => 'SIG_XYZ...', ], ]);
Note
The reinitialize() method uses strict safety validation. Attempting to assign unknown or read-only properties will throw a yii\base\InvalidConfigException immediately to prevent silent typos or configuration bugs in production.
Webhooks & CSRF Validation
When integrating payment gateways, they will send asynchronous payment update notifications via webhooks (e.g. Stripe webhooks or PayPal IPN notifications) as standard HTTP POST requests.
By default, Yii 2 validates CSRF tokens on all POST requests, which will block incoming gateway webhooks with an HTTP 400 Bad Request error. To resolve this, disable CSRF validation for your webhook action inside your payment controller:
namespace app\controllers; use Yii; use yii\web\Controller; class WebhookController extends Controller { /** * @inheritdoc */ public function beforeAction($action) { if ($action->id === 'stripe-webhook' || $action->id === 'paypal-ipn') { $this->enableCsrfValidation = false; } return parent::beforeAction($action); } public function actionStripeWebhook() { // Process Stripe webhook signature and handle notification } }
Running Tests
This library includes automated unit tests written in PHPUnit. To run tests, use the following command:
composer test
Tests are located in the /tests/ directory.
License
This project is licensed under the MIT License - see the LICENCE.md file for details.
Developed with ❤️ by Edgars Karlsons.