sebdesign/laravel-viva-payments
Laravel integration for Viva Payments (Viva Wallet), giving you a clean, expressive API to create payment orders, handle transactions, and add the gateway to your Laravel apps. Includes configuration, facades, and support for the Viva Wallet Smart Checkout flow.
composer require sebdesign/laravel-viva-payments, then php artisan vendor:publish --tag=config (package auto-discovers its service provider and Viva facade). Everything you can tune lives in config/viva.php — read it top to bottom before writing code, since it defines the environment switch, credentials, and defaults.sourceCode) if you're doing Smart/Native Checkout. Put them in .env (VIVA_ENVIRONMENT=demo, VIVA_MERCHANT_ID, VIVA_API_KEY, VIVA_CLIENT_ID, VIVA_CLIENT_SECRET, VIVA_SOURCE_CODE) and never commit them.config/viva.php → the Viva facade → the service classes under src/ (orders, transactions, webhooks, paymentSources, and their legacy equivalents). Services are the API surface; the facade is only a locator.php artisan tinker, fetch something you already know exists in the demo environment (e.g. an order or transaction by its code/ID). This proves credentials, environment, and token acquisition in one shot before you touch checkout or webhooks.// AppServiceProvider::boot()
use GuzzleHttp\RequestOptions;
use Sebdesign\VivaPayments\Facades\Viva;
Viva::withRequestOptions([
RequestOptions::TIMEOUT => 30,
RequestOptions::PROXY => 'http://proxy.example.com:8080',
]);
Viva:: calls through controllers. Create a small VivaGateway/PaymentService injected via the container; you get a single seam for logging, retries, correlation IDs, and tests. It also isolates you from method renames between major versions.bootstrap/app.php/VerifyCsrfToken, verify the payload with the verification key from the dashboard, return 200 immediately, and dispatch a queued job for the actual work. Viva retries deliveries, so make the handler idempotent — key off the order code / transaction ID and store a processed marker.orderCode, transactionId, status, and amount when you create an order and again on each webhook. You will need to reconcile, refund, and support customers long after the API response is gone.Viva::withRequestOptions(), which in turn overrides whatever Guzzle defaults the package config supplies. Use the global setter for infrastructure-wide settings (proxy, CA bundle, baseline timeout) and per-request options only for genuinely slow endpoints such as reports or large transaction listings.AppServiceProvider::boot(), guarded by environment if proxy/CA settings differ between demo and production. Don't call withRequestOptions() inside controllers — it's global state on a shared client.Http::fake() won't intercept it. Fake at your own gateway boundary instead, or inject a Guzzle client with a MockHandler through the container binding.withRequestOptions() is sticky global state. It merges into the client for all subsequent calls. Under Octane or in long-lived queue workers, set it once at boot — do not mutate it per request, and be aware there's no obvious per-request reset, so a leaked timeout will follow every later call.1050 is €10.50. Casting floats into order amounts silently produces wrong charges; keep an integer cast and validate at the boundary.VIVA_ENVIRONMENT first, then clear the cached access token — the package caches it for roughly the token lifetime, so rotated keys can appear to "not work" until the cache expires.Viva::fake(). There's no first-party test double; plan for that before you write a test suite.sourceCode is the most common cause of checkout creation failures — it's account-specific and differs per environment.RequestOptions::DEBUG => true (via withRequestOptions() or a per-call option) dumps full request/response including auth headers. Never leave it on in production logs. For less invasive tracing, add a logging middleware to the underlying client through a container rebind — the client is resolvable, and this is the cleanest extension point in the package.How can I help you explore Laravel packages today?