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

Laravel Paymaya Sdk Laravel Package

lloricode/laravel-paymaya-sdk

Laravel PayMaya SDK provides a Laravel-friendly wrapper around PayMaya’s PHP SDK, making it easier to configure credentials and integrate PayMaya checkout, payment links, and related payment APIs into your Laravel application.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish config

composer require lloricode/laravel-paymaya-sdk
php artisan vendor:publish --provider="Lloricode\Paymaya\PaymayaServiceProvider"

Add the credentials to .env (names should match config/paymaya.php — verify after publishing):

PAYMAYA_PUBLIC_KEY=pk-xxxxxxxx
PAYMAYA_SECRET_KEY=sk-xxxxxxxx
PAYMAYA_ENVIRONMENT=sandbox   # or production

Grab sandbox keys from the PayMaya/Maya developer dashboard. Sandbox and production keys are completely separate; a mismatch produces confusing auth errors rather than a clear "wrong environment" message.

Where to look first

  1. config/paymaya.php — the authoritative list of env keys, base URLs, and any timeout/retry settings.
  2. vendor/lloricode/laravel-paymaya-sdk/src/ — confirm the facade class, the service provider binding, and the method names on the client. This package is thin and lightly documented, so the source is the documentation.
  3. The facade (typically Lloricode\Paymaya\Facades\Paymaya) — this is your entry point for checkout, payments, and customers.

First use case: create a checkout and redirect

use Lloricode\Paymaya\Facades\Paymaya;

$checkout = Paymaya::checkout()->create([
    'totalAmount' => ['value' => '100.00', 'currency' => 'PHP'],
    'buyer'       => ['firstName' => 'Juan', 'lastName' => 'Dela Cruz'],
    'items'       => [[
        'name'     => 'Order #123',
        'quantity' => 1,
        'amount'   => ['value' => '100.00', 'currency' => 'PHP'],
        'totalAmount' => ['value' => '100.00', 'currency' => 'PHP'],
    ]],
    'redirectUrl' => [
        'success' => route('checkout.success'),
        'failure' => route('checkout.failure'),
        'cancel'  => route('checkout.cancel'),
    ],
    'requestReferenceNumber' => (string) $order->id,
]);

return redirect($checkout['redirectUrl']);

Persist $checkout['checkoutId'] and the reference number before redirecting — you need them to reconcile the return trip and any webhook.

Implementation Patterns

Wrap the SDK in a domain action, not in controllers. The SDK returns raw arrays, and payment payloads are wide. Put payload construction in a single CreateCheckoutAction / PaymayaCheckoutService class so the shape lives in one place and controllers stay thin:

final class CreateCheckout
{
    public function __invoke(Order $order): string
    {
        $checkout = Paymaya::checkout()->create($this->payloadFor($order));

        $order->payments()->create([
            'checkout_id'   => $checkout['checkoutId'],
            'reference_no'  => $order->id,
            'status'        => 'pending',
        ]);

        return $checkout['redirectUrl'];
    }
}

Always verify status server-side on the success callback. The success redirect carries a checkoutId query parameter, but the buyer controls that URL. Retrieve the checkout/payment from the API and confirm the amount and status before marking an order paid. Treat the redirect as a UX signal, not proof of payment.

Run webhook + redirect as idempotent paths into the same state machine. PayMaya will hit your webhook and redirect the buyer; they race. Both should call the same markPaid(Order, PaymentRef) method guarded by a DB transaction or updateOrCreate on the payment reference so double-processing is harmless.

Route::post('/webhooks/paymaya', PaymayaWebhookController::class)
    ->withoutMiddleware(['csrf']);

Poll or query for status when you have no webhook yet. During local development, hitting checkout/{id} or the payment endpoint on the success page is the fastest loop. Only move to webhooks once URLs are publicly reachable.

Design for multiple accounts if you're multi-tenant. A facade backed by a singleton reads config once. If different tenants have different merchant accounts, resolve a fresh client per tenant (e.g. app(PaymayaClient::class, ['config' => $tenantConfig]) if the package supports constructor config, otherwise config([...]) before resolving and forget the singleton) rather than relying on global config.

Test with the Laravel HTTP client when possible. If the SDK is built on Illuminate\Support\Facades\Http, this is enough:

Http::fake([
    '*/checkouts' => Http::response(['checkoutId' => 'abc', 'redirectUrl' => 'https://pg.test/x'], 200),
]);

$this->post('/checkout', ...)->assertRedirect('https://pg.test/x');

If it uses Guzzle directly, mock the underlying client class instead — but still assert on the outgoing payload; most integration bugs are wrong field names or amount formatting, not transport failures.

Model the payloads as DTOs. Amounts, buyer info, and item arrays are easy to mistype. A small CheckoutPayload::toArray() avoids the "works in Postman, fails in PHP" class of bug.

Gotchas and Tips

  • Amounts are decimal strings, not cents. Use number_format($cents / 100, 2, '.', '') and always emit two decimals ("100.00", not "100"). Sending an integer amount is a common 400.
  • requestReferenceNumber must be unique per checkout. Reusing an order ID after a failed/cancelled attempt will be rejected or collide when reconciling. Append an attempt suffix ("{$order->id}-2") and store the mapping.
  • Config caching bites. php artisan config:cache freezes the facade's config. Any runtime config(['paymaya.secret_key' => ...]) will silently do nothing until you re-resolve the client. Prefer per-request client instances over mutating config.
  • Callback URLs must be HTTPS and publicly reachable. localhost and http:// fail in the sandbox; use ngrok/Expose or a staging host. Verify the success URL actually comes back with checkoutId before debugging anything else.
  • Webhook signature verification is usually on you. PayMaya signs the raw body with a HMAC using your webhook secret. Read php://input before any middleware touches it, compare with hash_equals, and return 200 fast — a slow handler causes retries and duplicate deliveries.
  • Don't trust redirect query params for amounts. Cross-check totalAmount.value from the API response against your order total; a mismatch means tampering or a stale pending checkout.
  • Wrap calls in try/catch for connection/timeout errors. Payment endpoints can be slow; set a client timeout and surface a retryable error to the user rather than a 500. Distinguish transport errors from 4xx validation errors (usually a payload shape problem) and 401s (key/environment mismatch).
  • Redact secrets and card-adjacent data in logs. If you enable HTTP logging for debugging, scrub Authorization headers and buyer details; never log secret_key.
  • The package is niche and low-traffic (0 dependents, no stars). Pin an exact version in composer.json, check the composer.json require block for the Laravel/PHP versions it actually supports, and be prepared to vendor the relevant client class or bypass it with direct HTTP calls if it lags behind API changes. The underlying PayMaya REST API is stable; the wrapper is the fragile part.
  • Extension points are thin. If you need a new endpoint before the package adds it, don't patch vendor/. Either extend the client class and rebind it in AppServiceProvider, or call the API directly with Http::withToken(...). Keep the wrapper as a convenience, not a hard dependency in your domain layer.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource