Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Omise Php Laravel Package

omise/omise-php

Official PHP client library for the Omise payment gateway. Charge cards, manage customers, refunds, transfers, and webhooks through a clean, object-oriented API that wraps Omise's REST endpoints. Installed via Composer and framework-agnostic, so it drops easily into Laravel or any PHP application.

Deep Wiki
Context7

Getting Started

Install it and read two files — the whole API surface is there:

composer require omise/omise-php
  • lib/Omise.php — bootstrap/configuration hook (key handling differs between v2 and v3).
  • lib/OmiseApiResource.php — the base class every resource inherits (retrieve, create, update, destroy, list helpers, HTTP client injection, API version pinning).

Everything else in lib/Omise/ is a ~50-line resource class (Charge.php, Customer.php, Token.php, Source.php, Refund.php, Dispute.php, Transfer.php…) that mostly just declares its endpoint path. So the "first use case" is always the same shape: configure keys → build a token/charge → inspect status.

Minimum viable boot (defensive across versions, because key setup changed between releases):

final class OmiseBootstrap
{
    public function __construct(string $public, string $secret, ?string $version = null)
    {
        if (method_exists(\Omise::class, 'initialize')) {
            \Omise::initialize($public, $secret, $version); // v3+
            return;
        }

        // Legacy: constants are read lazily on the first API call.
        defined('OMISE_PUBLIC_KEY')   || define('OMISE_PUBLIC_KEY', $public);
        defined('OMISE_SECRET_KEY')   || define('OMISE_SECRET_KEY', $secret);
        defined('OMISE_API_VERSION')  || define('OMISE_API_VERSION', $version ?? '2019-05-29');
    }
}

First charge, using a token minted client-side with omise.js (the only PCI-sane path):

$charge = OmiseCharge::create([
    'amount'   => 100_00,        // smallest currency unit, integer
    'currency' => 'thb',
    'card'     => $token,       // 'tokn_test_...' from the browser
]);

if ($charge['status'] === 'successful') {
    // fulfil the order, keyed by $charge['id']
}

$charge is an OmiseObject — ArrayAccess plus __get, so $charge['status'] and $charge->status both work.

Get test keys from the Omise/Opn dashboard, set OMISE_PUBLIC_KEY / OMISE_SECRET_KEY in .env, and keep the secret server-side only.

Implementation Patterns

Bind it once, wrap it, never call statics from controllers. There is no Laravel service provider or facade in this package — you write the ~40 lines of glue.

// config/omise.php
return [
    'public_key'  => env('OMISE_PUBLIC_KEY'),
    'secret_key'  => env('OMISE_SECRET_KEY'),
    'api_version' => env('OMISE_API_VERSION', '2019-05-29'),
];
// App\Providers\AppServiceProvider::register()
$this->app->singleton(OmiseBootstrap::class, fn () => new OmiseBootstrap(
    config('omise.public_key'), config('omise.secret_key'), config('omise.api_version'),
));

$this->app->bind(PaymentGateway::class, OmiseGateway::class);

Then put every SDK call behind your own PaymentGateway interface that returns your DTOs (ChargeResult, RefundResult), not OmiseObject. This is what makes the SDK swappable and testable — you fake the interface, not cURL.

Token charge (default flow). Browser does Omise.createToken('card', {...}) with the public key; server does OmiseCharge::create(['card' => $token, ...]). Your server never touches a PAN.

Customer + saved card. Create the customer with the token, charge later with customer + card:

OmiseCustomer::create(['email' => $email, 'description' => $user->id, 'card' => $token]);

OmiseCharge::create([
    'amount'   => $amount,
    'currency' => 'thb',
    'customer' => $customerId,   // 'cust_test_...'
    'card'     => $cardId,       // 'card_test_...'
]);

Card retrieve takes both ids (OmiseCard::retrieve($customerId, $cardId)) — check lib/Omise/Card.php for the exact signature in your version.

Sources for local payment methods (PromptPay, Alipay, TrueMoney, FPX, etc.):

$source = OmiseSource::create([
    'type' => 'promptpay', 'amount' => $amount, 'currency' => 'thb',
]);

$charge = OmiseCharge::create([
    'amount' => $amount, 'currency' => 'thb', 'source' => $source['id'],
]);

Then redirect the user to $charge['authorize_uri'] (QR image / bank page) and wait for the webhook.

Auth-only + capture. Pass 'capture' => false; the charge comes back authorized. Capture with $charge->capture() (or OmiseCharge::capture($id)), void instead if you need to release. Do it before the authorization window lapses.

Refunds. $charge->refund(['amount' => $partial]), or OmiseRefund::create($chargeId, [...]). Track cumulative refunds yourself against $charge['refunds'] — the API will reject over-refunds, but you want a friendly error first.

Webhooks in Laravel. Omise does not sign the payload in the classic API, so treat the body as a hint only:

Route::post('/webhooks/omise', OmiseWebhookController::class)
    ->withoutMiddleware([VerifyCsrfToken::class]);

public function __invoke(Request $request)
{
    $event = OmiseEvent::retrieve($request->input('key')); // authoritative re-fetch
    ProcessOmiseEvent::dispatch($event['id'])->onQueue('payments');

    return response()->noContent();
}

Return 2xx fast, do the work in a job, and make the handler idempotent keyed on $charge['id'] (webhooks retry).

Testing. If your version exposes an injectable HTTP client (OmiseApiResource::$httpClient / setHttpClient() in v3), stub that. Otherwise the interface-wrapper above is the practical seam. Keep one sandbox integration test that hits the real test API.

Gotchas and Tips

  • Amounts are integers in the smallest unit — 100.00 THB is 10000. Do not blanket-multiply by 100: JPY, KRW and friends are zero-decimal in Omise. Use Money::toMinor() and store integers everywhere; currency must be lowercase ISO-4217.
  • Declines are not exceptions. A declined card returns HTTP 200 with status: 'failed' and a failure_code / failure_message. Always branch on status (successful, failed, pending, reversed, expired, authorized) before writing your own catch.
  • Exceptions are for transport/auth/validation — OmiseAuthenticationFailureException, OmiseNotFoundException, OmiseBadRequestException, OmiseApiConnectionError, etc. Map them in app/Exceptions/Handler.php (or your gateway) so a gateway blip doesn't 500 the checkout page.
  • pending ≠ successful. 3-D Secure and redirect sources leave status: pending with an authorize_uri. Redirect the user, then confirm via webhook — never treat the initial create response as payment.
  • Static, global key state is the big Laravel trap. Keys live on statics/constants, so under Octane, Horizon, or queues the first configured key set wins for the process lifetime. Multi-tenant or multi-currency-account setups need separate processes, or explicit re-initialisation per job — and constants can only be define()d once.
  • Tokens are single-use. Retrying a charge with the same token fails; re-tokenise on the client or store the card on a customer first.
  • No idempotency key in older releases. A cURL timeout after the API accepted the charge gives you a duplicate on retry. Build your own idempotency table keyed on your order id, and check whether your installed version supports Idempotency-Key before relying on the SDK.
  • Pin OMISE_API_VERSION. The SDK default can lag the API; an unpinned upgrade silently changes response shapes.
  • Classmap autoload, PHP 5-era code. No PSR-4, no strict types, no typed signatures — IDE autocompletion and PHPStan are weak. Keep declare(strict_types=1) in your code, convert OmiseObjects into DTOs at the boundary, and don't type-hint against SDK classes in your domain.
  • json_encode($charge) may not give you what you expect — nested values are themselves OmiseObjects. Convert explicitly (recursively) before logging or caching.
  • cURL + bundled CA bundle. The library ships its own cert file; behind corporate proxies or on stale cURL/Windows you'll see OmiseApiConnectionError from TLS failures, not auth errors. Inject a custom HTTP client if you need proxy settings or request tracing.
  • Never log full responses. Even token flows can echo card metadata; scrub before shipping to your log stack.
  • Enforce test keys in non-production — nothing in the SDK stops a .env copy-paste from charging real cards in staging.
  • Extension points worth knowing: OmiseApiResource::$httpClient / setHttpClient() for instrumentation and retries, OmiseApiResource subclassing for custom resources, and the resource classes' static retrieve() (with no id) plus limit/offset/from/to for list endpoints and reconciliation jobs.
  • Disputes need evidence upload via OmiseDispute::update() with documents — plan a UI for it early; the dispute window is short.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle