PHPackages                             hamoda-dev/moyasar-php - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. hamoda-dev/moyasar-php

ActiveLibrary[API Development](/categories/api)

hamoda-dev/moyasar-php
======================

A simple, expressive PHP client for the Moyasar payment gateway.

v0.0.3(1w ago)91292[1 PRs](https://github.com/hamoda-dev/moyasar-php/pulls)MITPHPCI passing

Since Apr 22Pushed 1w agoCompare

[ Source](https://github.com/hamoda-dev/moyasar-php)[ Packagist](https://packagist.org/packages/hamoda-dev/moyasar-php)[ RSS](/packages/hamoda-dev-moyasar-php/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (3)Dependencies (10)Versions (7)Used By (0)

Moyasar PHP
===========

[](#moyasar-php)

A simple, expressive PHP client for the [Moyasar](https://moyasar.com) payment gateway.

This package provides a clean interface for working with Moyasar's Invoices and Payments APIs in any PHP 8.2+ application. It's framework-agnostic and works great with Laravel, Symfony, or plain PHP.

Requirements
------------

[](#requirements)

- PHP **8.2+**
- [Saloon](https://docs.saloon.dev) `^4.0`
- [Saloon Pagination Plugin](https://docs.saloon.dev/docs/the-pagination-plugin) `^2.3`

Installation
------------

[](#installation)

Install via Composer:

```
composer require hamoda-dev/moyasar-php
```

That's it. No service providers to register, no config files to publish. The package is framework-agnostic — use it in Laravel, Symfony, Slim, or plain PHP.

Quick Start
-----------

[](#quick-start)

Grab your secret key from the [Moyasar dashboard](https://dashboard.moyasar.com), then:

```
use HamodaDev\Moyasar\Moyasar;
use HamodaDev\Moyasar\Invoice\DTO\CreateInvoiceDTO;

$moyasar = new Moyasar(
    baseUrl: 'https://api.moyasar.com/v1',
    apiKey:  getenv('MOYASAR_SECRET_KEY'),
);

// Create an invoice and send the customer to the hosted payment page
$invoice = $moyasar->invoice()->create(new CreateInvoiceDTO(
    amount:      2500,                 // 25.00 SAR — always in the smallest unit
    currency:    'SAR',
    description: 'Order #1234',
    callbackUrl: 'https://example.com/webhooks/moyasar',
));

header("Location: {$invoice->url}");
```

Three lines to take a payment. No Guzzle, no array-shuffling, no JSON decoding.

### Recommended Environment Setup

[](#recommended-environment-setup)

Keep credentials out of your code:

```
MOYASAR_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxx
MOYASAR_BASE_URL=https://api.moyasar.com/v1
```

Warning

**Never commit API keys to version control.** Use environment variables, a secrets manager, or your framework's config system. Treat your secret key like a password.

Tip

Moyasar issues separate **test** (`sk_test_...`) and **live** (`sk_live_...`) keys. Use the test key in development and staging — you can run real payment flows against test cards without charging anyone.

---

Invoices
--------

[](#invoices)

Invoices are the fastest way to accept a payment: you create one, send the customer to `invoice->url`, and Moyasar handles the entire checkout UI for you.

### Create an Invoice

[](#create-an-invoice)

```
use HamodaDev\Moyasar\Invoice\DTO\CreateInvoiceDTO;

$invoice = $moyasar->invoice()->create(new CreateInvoiceDTO(
    amount:      2500,
    currency:    'SAR',
    description: 'Order #1234',
    callbackUrl: 'https://example.com/webhooks/moyasar',
    successUrl:  'https://example.com/payment/success',
    backUrl:     'https://example.com/payment/cancel',
    metadata:    ['order_id' => '1234'],
));

echo $invoice->url;     // Hosted payment page — redirect your user here
echo $invoice->id;      // Store this alongside your order
echo $invoice->status;  // "initiated" for a fresh invoice
```

ParameterTypeRequiredWhat it's for`amount``int`YesSmallest currency unit (halalas for SAR, cents for USD)`currency``string`YesISO 4217 code (`SAR`, `USD`, ...)`description``string`YesShown to the customer on the payment page`callbackUrl``?string`NoWebhook URL — Moyasar POSTs here when payment status changes`successUrl``?string`NoWhere to redirect after a successful payment`backUrl``?string`NoWhere to redirect if the customer cancels`expiredAt``?string`NoISO 8601 — invoice auto-expires after this`metadata``?array`NoArbitrary key-value data — perfect for your internal IDsPrefer building DTOs from incoming request data? Use the array factory:

```
$dto = CreateInvoiceDTO::fromArray($request->validated());
$invoice = $moyasar->invoice()->create($dto);
```

### Retrieve an Invoice

[](#retrieve-an-invoice)

```
$invoice = $moyasar->invoice()->get('invoice_12345');

if ($invoice->status === 'paid') {
    // Fulfill the order
}
```

### List Invoices (with Pagination)

[](#list-invoices-with-pagination)

Moyasar returns invoices in pages. The SDK's paginator handles page-walking for you — no manual `?page=N` tracking:

```
$paginator = $moyasar->invoice()->list()->paginate($moyasar);

while ($paginator->hasMorePages()) {
    foreach ($paginator->items() as $invoice) {
        echo "{$invoice->id} — {$invoice->status}\n";
    }

    $paginator = $paginator->nextPage();
}
```

Tip

Every item yielded by `items()` is a fully-typed `InvoiceDTO`. Your IDE will autocomplete `->id`, `->status`, `->amount`, and every other field.

### Update an Invoice

[](#update-an-invoice)

Only `metadata` is updatable after creation — use this to attach internal context as your order progresses:

```
use HamodaDev\Moyasar\Invoice\DTO\UpdateInvoiceDTO;

$moyasar->invoice()->update('invoice_12345', new UpdateInvoiceDTO(
    metadata: [
        'order_id'     => '1234',
        'fulfilled_at' => now()->toIso8601String(),
    ],
));
```

### Bulk-Create Invoices

[](#bulk-create-invoices)

Need to send 50 invoices for a batch of orders? One request, one round-trip:

```
$result = $moyasar->invoice()->bulkCreate([
    new CreateInvoiceDTO(amount: 1000, currency: 'SAR', description: 'Invoice A'),
    new CreateInvoiceDTO(amount: 2000, currency: 'SAR', description: 'Invoice B'),
    new CreateInvoiceDTO(amount: 3500, currency: 'SAR', description: 'Invoice C'),
]);
```

### Cancel an Invoice

[](#cancel-an-invoice)

```
$invoice = $moyasar->invoice()->cancel('invoice_12345');
// $invoice->status === 'canceled'
```

### InvoiceDTO Reference

[](#invoicedto-reference)

PropertyTypeNotes`id``string`Unique identifier`status``string``initiated`, `pending`, `paid`, `expired`, `canceled``amount``int`Smallest currency unit`currency``string`ISO 4217`description``string``url``string`**Hosted payment page — redirect customers here**`amountFormat``string`e.g. `"25.00 SAR"``logoUrl``string`Your merchant logo`callbackUrl``?string``successUrl``?string``backUrl``?string``expiredAt``?string``createdAt``string``updatedAt``string``metadata``array``payments``array`Payment attempts linked to this invoiceNeed the raw Saloon response? It's always available:

```
$invoice  = $moyasar->invoice()->get('invoice_12345');
$response = $invoice->getResponse();

$response->status();   // 200
$response->headers();  // All response headers
$response->body();     // Raw JSON string
```

---

Payments
--------

[](#payments)

Invoices are great when you want Moyasar to host the checkout. **Payments** are for when you want full control — your own card form, your own UX, direct charges against a card or token.

Note

If you're collecting card details directly, make sure your integration is PCI-compliant. For most merchants, **tokenization** (using a saved `token` source) is safer and simpler than passing raw card numbers.

### Create a Payment

[](#create-a-payment)

```
use HamodaDev\Moyasar\Payment\DTO\CreatePaymentDTO;
use HamodaDev\Moyasar\Payment\DTO\Source\CreditCardSourceDTO;

$payment = $moyasar->payment()->create(new CreatePaymentDTO(
    amount:      10000,               // 100.00 SAR
    currency:    'SAR',
    description: 'Kindle Paperwhite',
    source: new CreditCardSourceDTO(
        name:                'John Doe',
        number:              '4111111111111111',
        month:               12,
        year:                2030,
        cvc:                 123,
        statementDescriptor: 'Century Store',
        threeDs:             true,
        manual:              false,
        saveCard:            false,
    ),
    callbackUrl: 'https://example.com/checkout/return',
    metadata:    ['cart_id' => 'cart_abc', 'customer_id' => '23432'],
    givenId:     'a1168bd1-47a4-4b97-8a50-dd5caaccacf2',
    applyCoupon: true,
));

echo $payment->status;  // "initiated", "paid", "authorized", ...
echo $payment->id;      // Store this with your order
```

**About 3D Secure:** when `threeDs: true`, the response may include a redirect URL the customer must visit to complete verification. Always check `$payment->status` and any redirect instructions returned by the API before assuming the payment succeeded.

**About manual capture:** setting `manual: true` authorizes the charge without capturing funds. Use this when you want to verify a payment now but only capture later (e.g. when you ship the item). See [Capture](#capture-an-authorized-payment) below.

#### Supported Source Types

[](#supported-source-types)

SourceDTOUse caseCredit/debit card`CreditCardSourceDTO`Direct card chargeApple PayPass a raw `array` as `source`Apple Pay token from the browser/appSTC PayPass a raw `array` as `source`STC Pay flowSaved token`CreditCardSourceDTO` with `token` setCharging a previously saved card### Fetch a Payment

[](#fetch-a-payment)

```
$payment = $moyasar->payment()->get('payment_12345');

if ($payment->status === 'paid') {
    // Mark the order as paid
}
```

### List Payments

[](#list-payments)

Same paginator API as invoices:

```
$paginator = $moyasar->payment()->list()->paginate($moyasar);

while ($paginator->hasMorePages()) {
    foreach ($paginator->items() as $payment) {
        echo "{$payment->id} — {$payment->status} — {$payment->amountFormat}\n";
    }

    $paginator = $paginator->nextPage();
}
```

### Update a Payment

[](#update-a-payment)

Update `description` or `metadata` after the fact — handy for enriching records once your internal workflow catches up:

```
use HamodaDev\Moyasar\Payment\DTO\UpdatePaymentDTO;

$moyasar->payment()->update('payment_12345', new UpdatePaymentDTO(
    description: 'Kindle Paperwhite — refurbished',
    metadata: [
        'cart_id'        => 'cart_abc',
        'customer_email' => 'john@example.com',
    ],
));
```

### Refund a Payment

[](#refund-a-payment)

Full refund:

```
$payment = $moyasar->payment()->refund('payment_12345');
```

Partial refund (amount in smallest currency unit):

```
$payment = $moyasar->payment()->refund('payment_12345', amount: 2500);  // Refund 25.00 SAR
```

### Capture an Authorized Payment

[](#capture-an-authorized-payment)

If you created the payment with `manual: true`, capture it when you're ready to actually charge the customer:

```
// Full capture
$payment = $moyasar->payment()->capture('payment_12345');

// Partial capture (e.g. only ship part of an order)
$payment = $moyasar->payment()->capture('payment_12345', amount: 5000);
```

### Void a Payment

[](#void-a-payment)

Cancel a payment **before** the funds settle in your bank account. Works on `paid`, `authorized`, or `captured` payments — as long as settlement hasn't happened yet.

```
$payment = $moyasar->payment()->void('payment_12345');
// $payment->status === 'voided'
```

Tip

**Void vs. refund:** void *prevents* the money from leaving the customer's account; refund *returns* money that's already moved. Void is cheaper and faster — always prefer it when available.

### Payment Status Reference

[](#payment-status-reference)

StatusWhat it means`initiated`Payment created, customer hasn't paid yet`paid`Payment succeeded — you can fulfill the order`failed`Payment failed — check `message` on the DTO for the reason`authorized`Card authorized but not charged — needs `capture()``captured`Authorized payment has been successfully captured`refunded`Payment refunded (full or partial)`voided`Payment canceled before settlement`verified`Card verified during tokenization (no charge made)---

Error Handling
--------------

[](#error-handling)

Saloon throws `RequestException` on any non-2xx response. The response object is attached, so you get full context:

```
use Saloon\Exceptions\Request\RequestException;
use Saloon\Exceptions\Request\FatalRequestException;

try {
    $payment = $moyasar->payment()->get('invalid_id');
} catch (RequestException $e) {
    $status = $e->getResponse()->status();
    $body   = $e->getResponse()->json();

    // Moyasar returns structured errors
    $type    = $body['type']    ?? null;  // e.g. "invalid_request_error"
    $message = $body['message'] ?? null;  // Human-readable summary
    $errors  = $body['errors']  ?? [];    // Field-level validation errors

    report($e);
} catch (FatalRequestException $e) {
    // Network failure — didn't even reach Moyasar
    report($e);
}
```

### Moyasar Error Types

[](#moyasar-error-types)

TypeMeaningWhat to do`invalid_request_error`You sent bad parametersCheck the `errors` field, fix, retry`authentication_error`Invalid API keyVerify your secret key and base URL`rate_limit_error`Too many requestsBack off and retry with exponential delay`api_connection_error`Couldn't reach MoyasarRetry with backoff`account_inactive_error`Account not activated for live paymentsContact Moyasar sales`3ds_auth_error`3D Secure failedAsk the customer to try again`api_error`Something else went wrongRetry; contact support if it persists### HTTP Status Codes

[](#http-status-codes)

CodeMeaning`200`Success`400`Bad request — missing or invalid parameters`401`Unauthorized — API key invalid`403`Forbidden — credentials lack permission`404`Resource not found`405`Method not allowed — account not activated for live`429`Rate limited`500` / `503`Moyasar server issue — retry laterWarning

Moyasar occasionally returns **`201` with a failure payload** (e.g. bank declines). Don't trust the status code alone — always inspect `$payment->status` or `$invoice->status` on the DTO.

---

Testing Your Integration
------------------------

[](#testing-your-integration)

Use Moyasar's [test cards](https://docs.moyasar.com/testing) with your `sk_test_...` key. A few quick patterns:

ScenarioCard numberSuccessful charge`4111 1111 1111 1111`Declined charge`4000 0000 0000 0002`3D Secure required`4000 0000 0000 3220`Always verify your webhook handler works end-to-end in `test` mode before flipping to live.

---

Contributing
------------

[](#contributing)

Bug reports and pull requests welcome. If you're adding a new Moyasar endpoint, please follow the existing Resource / Request / DTO pattern — consistency is why this SDK is pleasant to use.

License
-------

[](#license)

Moyasar PHP is open-sourced software licensed under the [MIT license](LICENSE).

Next Steps
----------

[](#next-steps)

See [TODO](./TODO.md)

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance98

Actively maintained with recent releases

Popularity21

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity28

Early-stage or recently created project

 Bus Factor1

Top contributor holds 52.9% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~40 days

Total

3

Last Release

13d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/220b782cf42d7a19b879b90823af25b757fe7a1edcfb41e7295fb0bb75d36018?d=identicon)[hamoda-dev](/maintainers/hamoda-dev)

---

Top Contributors

[![hamoda-dev](https://avatars.githubusercontent.com/u/45687193?v=4)](https://github.com/hamoda-dev "hamoda-dev (18 commits)")[![AbRahman-ra](https://avatars.githubusercontent.com/u/126220430?v=4)](https://github.com/AbRahman-ra "AbRahman-ra (16 commits)")

---

Tags

apilaravelmoyasarphpsdksymfony

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/hamoda-dev-moyasar-php/health.svg)

```
[![Health](https://phpackages.com/badges/hamoda-dev-moyasar-php/health.svg)](https://phpackages.com/packages/hamoda-dev-moyasar-php)
```

###  Alternatives

[myoutdeskllc/salesforce-php

salesforce library for php8+

1579.5k](/packages/myoutdeskllc-salesforce-php)[codebar-ag/laravel-docuware

DocuWare integration with Laravel

1123.7k](/packages/codebar-ag-laravel-docuware)[sandorian/moneybird-api-php

Moneybird API client for PHP

148.2k](/packages/sandorian-moneybird-api-php)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
