cafali-com/laravel-openpay
Composer 安装命令:
composer require cafali-com/laravel-openpay
包简介
A Laravel package for OpenPay (BBVA) payment gateway integration
README 文档
README
A Laravel package for integrating with the OpenPay (BBVA) payment gateway. Supports card payments, SPEI bank transfers, Paynet cash payments, subscriptions, payouts, and webhooks.
Installation
composer require cafali-com/laravel-openpay
Publish the config:
php artisan vendor:publish --tag="laravel-openpay-config"
Add your credentials to .env:
OPENPAY_MERCHANT_ID=your-merchant-id OPENPAY_PRIVATE_KEY=sk_xxx OPENPAY_PUBLIC_KEY=pk_xxx OPENPAY_SANDBOX=true OPENPAY_COUNTRY=mx OPENPAY_CURRENCY=MXN OPENPAY_WEBHOOK_SECRET=your-webhook-secret
Usage
Facade
use CafaliCom\LaravelOpenpay\Facades\Openpay; Openpay::charges()->createCardCharge([...]); Openpay::customers()->create([...]); Openpay::isSandbox(); // true Openpay::getPublicKey(); // for Openpay.js tokenization
Card Charges
use CafaliCom\LaravelOpenpay\Facades\Openpay; // Merchant-level charge (one-time, no saved customer) $charge = Openpay::charges()->createCardCharge([ 'source_id' => 'tok_xxx', // Token from Openpay.js 'amount' => 150.00, 'description' => 'Order #1234', 'device_session_id' => 'session_xxx', 'customer' => [ 'name' => 'Juan', 'last_name' => 'Perez', 'email' => 'juan@example.com', ], ]); // Customer-level charge (saved customer) $charge = Openpay::charges()->createCustomerCardCharge('cust_xxx', [ 'source_id' => 'tok_xxx', 'amount' => 150.00, 'description' => 'Order #1234', 'device_session_id' => 'session_xxx', ]);
Paynet (Cash at Convenience Stores)
$charge = Openpay::charges()->createStoreCharge([ 'amount' => 500.00, 'description' => 'Order #5678', 'due_date' => '2026-04-15T00:00:00', ]); // Customer receives a barcode reference number $reference = $charge->payment_method->reference; $barcodeUrl = $charge->payment_method->barcode_url;
SPEI (Bank Transfers)
$charge = Openpay::charges()->createBankCharge([ 'amount' => 2000.00, 'description' => 'Invoice #9012', 'due_date' => '2026-04-15T00:00:00', ]); // Customer receives a CLABE number for SPEI transfer $clabe = $charge->payment_method->clabe; $bankName = $charge->payment_method->name;
Customers
// Create $customer = Openpay::customers()->create([ 'name' => 'Maria', 'last_name' => 'Garcia', 'email' => 'maria@example.com', 'phone_number' => '5551234567', ]); // Find, Update, Delete $customer = Openpay::customers()->find('cust_xxx'); $customer = Openpay::customers()->update('cust_xxx', ['name' => 'Maria Elena']); Openpay::customers()->delete('cust_xxx'); // List with filters $customers = Openpay::customers()->list(['limit' => 10]);
Cards
// Add card to customer (using token from Openpay.js) $card = Openpay::cards()->createForCustomer('cust_xxx', [ 'token_id' => 'tok_xxx', 'device_session_id' => 'session_xxx', ]); // List and delete $cards = Openpay::cards()->listForCustomer('cust_xxx'); Openpay::cards()->deleteForCustomer('cust_xxx', 'card_xxx');
Subscriptions
// Create a plan $plan = Openpay::plans()->create([ 'amount' => 299.00, 'name' => 'Plan Premium', 'repeat_unit' => 'month', 'repeat_every' => 1, 'retry_times' => 3, 'status_after_retry' => 'cancelled', ]); // Subscribe a customer $subscription = Openpay::subscriptions()->create('cust_xxx', [ 'plan_id' => $plan->id, 'card_id' => 'card_xxx', ]); // Cancel Openpay::subscriptions()->cancel('cust_xxx', $subscription->id);
Payouts
// Payout to bank account $payout = Openpay::payouts()->create([ 'amount' => 5000.00, 'description' => 'Vendor payment', 'method' => 'bank_account', 'bank_account' => [ 'clabe' => '012345678901234567', 'holder_name' => 'Proveedor SA', ], ]);
Refunds
// Full refund $charge = Openpay::charges()->refund('trx_xxx'); // Partial refund $charge = Openpay::charges()->refund('trx_xxx', [ 'amount' => 50.00, 'description' => 'Partial refund', ]);
Billable Trait
Add the Billable trait to your User (or any model) for convenient payment methods:
use CafaliCom\LaravelOpenpay\Traits\Billable; class User extends Authenticatable { use Billable; }
Add the openpay_id column to your model's table:
Schema::table('users', function (Blueprint $table) { $table->string('openpay_id')->nullable()->index(); });
Then use it:
$user = User::find(1); // Create as OpenPay customer (idempotent) $customer = $user->createAsOpenpayCustomer(); // Charge their card $charge = $user->charge([ 'source_id' => 'tok_xxx', 'amount' => 150.00, 'description' => 'Order #1234', 'device_session_id' => 'session_xxx', ]); // Paynet cash charge $charge = $user->chargeAtStore([ 'amount' => 500.00, 'description' => 'Order #5678', ]); // SPEI bank transfer $charge = $user->chargeViaSpei([ 'amount' => 2000.00, 'description' => 'Invoice #9012', ]); // Subscribe to a plan $subscription = $user->subscribe([ 'plan_id' => 'plan_xxx', 'card_id' => 'card_xxx', ]); // Manage cards $card = $user->addCard(['token_id' => 'tok_xxx', 'device_session_id' => 'ses_xxx']); $cards = $user->cards(); $user->deleteCard('card_xxx');
Webhooks
The package registers a webhook endpoint at POST /openpay/webhooks automatically.
Events
Listen for these events in your EventServiceProvider or with Event::listen():
| Event | Fired when |
|---|---|
WebhookReceived |
Any valid webhook (always dispatched) |
VerificationReceived |
OpenPay sends a verification event during webhook activation, see Webhook Activation below |
ChargeCompleted |
A charge is completed |
ChargeFailed |
A charge fails |
ChargeRefunded |
A charge is refunded |
PayoutCompleted |
A payout is completed |
PayoutFailed |
A payout fails |
SpeiReceived |
A SPEI transfer is received |
SubscriptionChargeFailed |
A subscription charge fails |
use CafaliCom\LaravelOpenpay\Events\ChargeCompleted; Event::listen(ChargeCompleted::class, function ($event) { $chargeId = $event->charge['id']; $amount = $event->charge['amount']; // Update your order status, send confirmation, etc. });
Webhook Signature Verification
Set OPENPAY_WEBHOOK_SECRET in your .env to enable signature verification. The middleware will validate the X-Openpay-Webhook-Signature header automatically.
Webhook Activation
OpenPay's webhook activation is a two-step protocol that catches many integrations off guard:
- When a webhook is created, OpenPay POSTs to the webhook URL with
{"type": "verification", "verification_code": "XXXXXXXX"}. The endpoint must respond with HTTP 200 (this package does that automatically). - The merchant must then paste the
verification_codeinto the OpenPay dashboard's Verify form to activate the webhook. Until this is done, OpenPay marks the webhook as "Sin verificar" and will not deliver any other events.
This package surfaces the verification code through three layers so merchants can find it without grepping log files:
A. The VerificationReceived event, dispatched as soon as the package receives the verification ping:
use CafaliCom\LaravelOpenpay\Events\VerificationReceived; Event::listen(VerificationReceived::class, function ($event) { // Notify the merchant via email, Slack, in-app banner, etc. Mail::to('admin@example.com')->send( new OpenpayVerificationMail($event->verificationCode, $event->webhookId) ); });
B. The openpay_webhook_verifications table. A default listener persists every verification code with webhook_id, payload, and a used_at timestamp you can flip once the merchant has pasted the code:
# Publish + run the migration php artisan vendor:publish --provider="CafaliCom\\LaravelOpenpay\\OpenpayServiceProvider" php artisan migrate
Disable the default listener (e.g., if you only want the event for your own side effects):
OPENPAY_PERSIST_VERIFICATIONS=false
C. The openpay:webhook:verifications Artisan command for CLI access:
$ php artisan openpay:webhook:verifications +----+-------------------+----------------------+---------------------+---------+ | ID | Verification Code | Webhook ID | Received | Used | +----+-------------------+----------------------+---------------------+---------+ | 3 | pCKCHB3Q | wjjr4rwwiv5xpde3mrnf | 2026-05-26 02:07:10 | pending | +----+-------------------+----------------------+---------------------+---------+ Paste this into OpenPay's Verify form: pCKCHB3Q
Pass --all to include codes already marked as used, --limit=N to change the row cap (default 10).
Registering with OpenPay
Register your webhook URL in the OpenPay dashboard or via the API:
$openpay = app(\CafaliCom\LaravelOpenpay\Openpay::class); $webhook = $openpay->getClient()->webhooks->add([ 'url' => url('/openpay/webhooks'), 'event_types' => [ 'charge.completed', 'charge.failed', 'charge.refunded', 'payout.completed', 'payout.failed', 'spei.received', ], ]);
Client-Side Tokenization (Openpay.js)
For PCI compliance, card data should never touch your server. Use Openpay.js:
<script src="https://js.openpay.mx/openpay.v1.min.js"></script> <script> OpenPay.setId('{{ config("openpay.merchant_id") }}'); OpenPay.setApiKey('{{ config("openpay.public_key") }}'); OpenPay.setSandboxMode({{ config("openpay.sandbox") ? 'true' : 'false' }}); // Generate device session ID var deviceSessionId = OpenPay.deviceData.setup("payment-form", "deviceIdHiddenFieldName"); // Tokenize card OpenPay.token.create({ card_number: "4111111111111111", holder_name: "Juan Perez", expiration_year: "28", expiration_month: "12", cvv2: "110", }, onSuccess, onError); function onSuccess(response) { var tokenId = response.data.id; // Send this to your server } </script>
Testing
composer test
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 3
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-15