soap/laravel-omise
A Laravel wrapper for the Omise payment gateway that registers a service provider, facade, and config so you can manage charges, customers, cards, refunds, and webhooks through a clean, fluent API.
composer require soap/laravel-omise — Laravel's package auto-discovery registers the service provider and the Omise facade alias, so there is nothing to add to config/app.php on a modern skeleton.php artisan vendor:publish --tag=omise-config. If the tag has changed between versions, run plain php artisan vendor:publish and pick the provider from the list — this is the single fastest way to see what the package actually exposes.OMISE_PUBLIC_KEY, OMISE_SECRET_KEY and OMISE_API_VERSION (default 2019-05-29) to .env. Use pkey_test_… / skey_test_… locally; the key prefix is the only thing separating test from live, there is no separate sandbox host.config/omise.php for the knobs, then the Omise facade class in src/ for the resource accessors (charges(), customers(), tokens(), sources(), refunds(), transfers(), recipients(), schedules(), search()). The tests directory is usually the best source of real call shapes.omiseToken and calls Omise::charge(['amount' => 100000, 'currency' => 'thb', 'card' => $request->omiseToken]). Amounts are integers in the smallest currency unit — 100000 = ฿1,000.00.Omise facade is awkward to mock. Put charges behind your own PaymentGateway interface with a single charge(Order $order, string $token): ChargeResult method, bind it in a service provider, and swap in a fake for tests. This is the highest-leverage decision you make in day one.Omise::customers()->create([... 'card' => $token])), store the cust_… id on your user, and charge with 'customer' => $customerId thereafter. Never store the token itself — it is single-use.'source' => $sourceId, then redirect to $charge['authorize_uri'] and re-fetch the charge on return. Model this as a separate PendingCharge record rather than trying to fit it into the card flow.authorized and status on the returned charge. status === 'pending' plus a non-null authorize_uri means redirect; status === 'successful' means done. Always re-fetch by id after the return URL instead of trusting query parameters.web middleware group (CSRF will reject Omise's POST), return 200 immediately, and push the payload onto a queue. Omise does not sign webhooks the way Stripe does, so verify by re-fetching the charge/event from the API and comparing the amount and currency against your own order record before fulfilling anything.charge() signature doesn't expose it, drop to OmiseCharge::create($params, $publicKey, $secretKey) from the underlying SDK for that one call. Guard webhook handlers with a unique index on event_id / charge_id.Omise::charges()->all([...]) for the last 24h and settles anything your webhook missed. This is not optional for money.(int) ($total * 100) on a float will eventually produce an off-by-one-satang charge — use brick/money or integer arithmetic from the start.OmiseException subclasses: OmiseCardException, OmiseInvalidCardException, OmiseFailedCaptureException, plus OmiseNotFoundException (bad id), OmiseAuthenticationException (bad key) and OmiseApiConnectionException (timeout). Catch OmiseNotFoundException and OmiseAuthenticationException separately — they usually mean a bug or a config problem, not a declined card.$charge['failure_code'] and failure_message are what your support team will need. failure_code values are a stable enum; the message is not.config:cache is the classic "wrong keys in production" bug. The config file reads env() at boot; after changing .env on a cached deploy you must re-run php artisan config:cache. Same for Octane — restart the workers or the singleton client keeps the old credentials.card['number'], and full webhook bodies are not. Add a Log::shareContext / middleware sanitizer before you need it.^1.6, and if you depend on internals, diff v1.6.0...v1.6.1 yourself before deploying. Re-run your payment integration tests after the bump; a wrapper package this thin can change a method signature without a major version.How can I help you explore Laravel packages today?