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

Php Library Laravel Package

paymob/php-library

A PHP library for integrating Paymob payments into your application, with convenient wrappers for authentication, order creation, payment key generation, and transaction handling so you can accept online payments with less boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire up credentials

composer require paymob/php-library

The package is a thin wrapper around Paymob's REST API (auth → order → payment key → iframe, plus HMAC verification), so treat it as an HTTP client, not a full checkout abstraction. Before writing any code, collect these four values from the Paymob dashboard and put them in .env:

PAYMOB_API_KEY=
PAYMOB_INTEGRATION_ID=      # one per method: card, wallet, kiosk
PAYMOB_IFRAME_ID=
PAYMOB_HMAC_SECRET=

Where to look first

  1. src/ — usually a single Paymob.php class with static helpers (AuthenticationRequest, OrderRegistration, PaymentKeyRequest, verifyPayment / responseCallback). Skim it end-to-end; it's small enough that reading it is faster than reading docs.
  2. composer.json — note whether it depends on Guzzle or uses cURL directly, and which PHP version it requires. If it pins an old Guzzle, you may have a conflict with the rest of your app.
  3. The README.md for the callback payload shape — the field names there are what you'll be validating with HMAC.

First use case: card checkout redirect

The canonical happy path is: authenticate, register an order, request a payment key, then redirect the customer to Paymob's iframe with that key. Build this as a one-off controller first, confirm a sandbox test card succeeds, and only then refactor into a service class. Getting the redirect flow working end-to-end (including the callback) is the single largest hurdle; everything else is variation.

Implementation Patterns

Wrap the static API in a service

The library's static methods are awkward to test and inject. Wrap them behind a PaymobGateway class bound in the container, with config injected from .env:

class PaymobGateway
{
    public function __construct(private array $config) {}

    public function checkout(Cart $cart): string
    {
        $auth   = Paymob::AuthenticationRequest($this->config['api_key']);
        $order  = Paymob::OrderRegistration($auth->token, $this->orderPayload($cart));
        $key    = Paymob::PaymentKeyRequest($auth->token, [
            'order_id' => $order->id,
            'amount_cents' => $cart->totalInCents(),
            'integration_id' => $this->config['integration_id'],
            'billing_data' => $this->billingData($cart),
        ]);
        return $key->token; // append to iframe URL on the frontend
    }
}

Cache the auth token

Authentication returns a token valid for one hour. Calling it on every checkout adds a round trip and risks rate limits. Cache it in the Laravel cache keyed by API key with a TTL of ~55 minutes, and bust the cache on a 401.

Route by payment method, not by one integration ID

Card, wallet, and kiosk each have their own integration ID and their own iframe/redirect behavior. Model this as an enum on your Payment record and resolve the correct integration ID at payment-key time. Don't hardcode a single integration ID across methods — the failure mode is a confusing 4xx at payment key creation.

Two callback endpoints, different purposes

Paymob sends two things: a callback (browser redirect to your redirect_url) and a webhook (server-to-server to your webhook_url). The redirect is for UX only. The webhook is your source of truth for order state. Implement both, but only mutate order state from the webhook handler; the redirect handler should just show a "processing" page. This protects you when a user closes the tab before redirect.

Always verify HMAC

Both transaction callbacks and order callbacks carry an hmac parameter computed over a specific concatenation of fields. Use the library's verify helper if present; otherwise implement it explicitly. Never mark an order paid based on the redirect query string alone.

public function handleWebhook(Request $request)
{
    if (! Paymob::verifyPayment($request->all(), config('paymob.hmac'))) {
        abort(403);
    }
    // idempotent state transition, keyed on merchant_order_id
}

Make the webhook idempotent

Paymob retries webhooks. Key your state transition on the order.merchant_order_id you assigned when registering the order (not on the Paymob order ID, which you don't control). Use DB::transaction + a unique constraint or updateOrCreate, and return 200 quickly. If you return non-2xx, expect retries and duplicate processing.

Store both IDs

Persist paymob_order_id (returned from order registration) alongside your own order. You'll need it for reconciliation, refunds, and voiding via the API later — refunds are not part of the checkout flow but are typically supported through the same auth token.

Gotchas and Tips

Amounts are in cents, always integers. amount_cents for a 150.00 EGP order is 15000. Passing a float or a formatted string silently breaks reconciliation. Do the conversion at the boundary and store the integer.

HMAC field order differs between transaction and order callbacks. The concatenation order for a transaction callback is not the same as for an order/refund callback. If HMAC verification fails in production but worked in sandbox, this is almost certainly why — check which callback type you're validating. Log the raw payload and the string you concatenated before hashing.

Sandbox vs production is a base-URL switch, not a key switch. The library commonly reads the base URL from config. Confirm you're not shipping the sandbox URL; a very common failure is a production API key hitting the sandbox host and getting a 401 that looks like bad credentials.

billing_data requires a fixed set of fields — first_name, last_name, email, phone_number, street, city, country, etc. Paymob is strict about these even for digital goods. If you're missing them, order registration succeeds but payment key creation fails. Build a mapping from your user model once and reuse it.

Auth tokens expire mid-flow. If a user opens checkout and pays 90 minutes later (common with wallet flows), the payment key may already be invalid. Generate payment keys close to the moment of redirect, not at page load, and surface a clean "session expired, try again" state rather than a raw Paymob error.

Error responses are JSON strings, not exceptions. The library often returns decoded responses and leaves HTTP error handling to you. Wrap calls in a helper that checks success / HTTP status and throws a domain exception, otherwise you'll be debugging null properties from a failed response object.

Callback URL must be publicly reachable and excluded from CSRF. Register the webhook route with ->withoutMiddleware(['csrf']) (or in $except) and make sure it's not behind auth middleware. Locally, use a tunnel (ngrok, expose) during development — Paymob cannot reach localhost, and the redirect flow will appear to work while the webhook silently never arrives.

Test cards don't cover the interesting cases. Sandbox success paths are easy; deliberately test the declined/3DS/expired states, since those exercise the branches where your state machine will leak (order stuck "pending" forever, user sees success page but webhook says declined).

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin