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 Laravel Package

aceraven777/laravel-paymaya

A Laravel package for integrating PayMaya (Maya) payments into your application. It offers configuration and API helpers to create payment requests, manage checkout flows, and handle PayMaya responses, making it easier to add PayMaya support to Laravel projects with less boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

Add the package and let Composer pull in the underlying PayMaya SDK:

composer require <vendor>/laravel-paymaya
php artisan vendor:publish --tag=paymaya-config

Set credentials in .env (never commit them):

PAYMAYA_PUBLIC_KEY=pk_test_...
PAYMAYA_SECRET_KEY=sk_test_...
PAYMAYA_SANDBOX=true

Look first at these three places:

  1. config/paymaya.php — base URL selection (sandbox vs. production), key names, timeout and retry settings.
  2. The package service provider — it registers the PayMaya client in the container; that binding is your primary extension point.
  3. composer.json / composer.lock — since v1.1.2 the PayMaya SDK base code arrives as a Composer dependency rather than being bundled. Confirm the resolved SDK version before debugging anything else.

First use case: create a checkout and redirect the customer.

public function pay(Order $order, PayMayaClient $paymaya)
{
    $checkout = $paymaya->checkouts()->create([
        'totalAmount' => [
            'value'    => $order->total_cents,
            'currency' => 'PHP',
        ],
        'buyer' => [
            'firstName' => $order->customer->first_name,
            'lastName'  => $order->customer->last_name,
        ],
        'requestReferenceNumber' => (string) $order->id,
        'redirectUrl' => [
            'success' => route('checkout.success', $order),
            'failure' => route('checkout.failure', $order),
            'cancel'  => route('checkout.cancel', $order),
        ],
    ]);

    $order->update(['paymaya_checkout_id' => $checkout['checkoutId']]);

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

Verify the round trip in sandbox with the test keys before touching production.

Implementation Patterns

Wrap the SDK, don't scatter it. Keep PayMayaClient calls inside an application-level service (App\Services\PayMayaGateway) that returns your own DTOs. When the SDK changes shape, you edit one file instead of forty controllers. Resolve the client from the container rather than calling new so you can swap in a fake during tests.

Persist identifiers, not payloads. Store checkoutId, paymentId, requestReferenceNumber and status on your orders/payments table. Webhook retries are normal; your handler must be idempotent — look up by the PayMaya ID, skip if already in a terminal state, and return 200 regardless so PayMaya stops retrying.

Treat webhooks as the source of truth, redirects as UX. The success redirect only means the browser came back. Fulfil the order from the webhook (or from an explicit "retrieve checkout" call) so a customer closing the tab mid-redirect doesn't leave a paid order unfulfilled.

Queue the webhook work. Validate the signature in the controller, persist the raw event, dispatch a job, return 200. Slow handlers cause PayMaya to retry and, eventually, to flag your endpoint.

Guard against double charges. Generate your own idempotency/reference key per attempt (order_id . ':' . attempt_uuid) and send it as requestReferenceNumber. If a network timeout happens after the request left your server, you need a way to reconcile without creating a second checkout.

Use a state machine. Model statuses explicitly (pending → authorized → captured → failed/refunded) and only allow legal transitions. Payment integrations rot fastest when status is a free-form string column.

Test against the sandbox with HTTP faking. Because the SDK is now a Composer dependency, Http::fake() on the SDK's underlying Guzzle client — or a container-bound fake client — lets you test failure branches (402, timeout, malformed response) without hitting the network.

Gotchas and Tips

  • v1.1.2 gotcha — remove any vendored SDK copy. If you previously pasted the PayMaya base code into app/ or packages/, you now have two definitions of the same classes. Symptom: Cannot declare class ... already in use, or worse, silent resolution of the old copy. Delete the vendored directory and composer dump-autoload.
  • Pin the SDK version. The package now depends on the SDK transitively, so a composer update can move it under you. Lock the constraint and read the SDK changelog on patch bumps — payment SDKs quietly change required fields.
  • Amounts are in minor units. 10000 is ₱100.00, not ₱10,000. This is the single most common sandbox-to-production bug; store amounts as integers end to end and format only at the view layer.
  • Sandbox and production keys are not interchangeable. Check that the base URL and key prefix agree (pk_test_/sk_test_ with sandbox). A mismatch often surfaces as an opaque 401 rather than a clear config error.
  • Config caching bites. After editing config/paymaya.php, run php artisan config:clear. Stale cached config is the usual cause of "it works locally but not on the server."
  • Redact secrets in logs. The SDK may log request bodies on error. Add a Monolog processor that scrubs Authorization, secretKey, and card-like fields before anything reaches your log driver.
  • Set explicit timeouts and retries. Default Guzzle timeouts are too long for a web request. Use a short timeout on the checkout call, and retry only on connection errors and 5xx — never blindly retry a POST that creates a charge.
  • Verify the webhook signature before parsing. Compare the signature header against an HMAC of the raw body (not the decoded array) using hash_equals. Reject unsigned requests with 401 and log the attempt.
  • Extension point: rebind the client. The service provider resolves the SDK client from the container. In tests or for multi-tenant setups, bind your own instance with per-tenant credentials rather than mutating config at runtime.
  • Debug order when something fails: (1) resolved SDK version from composer show, (2) sandbox/production base URL, (3) key prefix match, (4) raw request/response body from a redacted log, (5) whether the duplicate-class warning is present. That order resolves most integration failures in minutes.
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.
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names