Skip to content

Pull Request: Refactoring Invoice Module (Enhance Directory Structure, Naming, Data Types, Type Casting & Logic Flow)#6

Open
AbRahman-ra wants to merge 11 commits into
mainfrom
ref/types
Open

Pull Request: Refactoring Invoice Module (Enhance Directory Structure, Naming, Data Types, Type Casting & Logic Flow)#6
AbRahman-ra wants to merge 11 commits into
mainfrom
ref/types

Conversation

@AbRahman-ra

Copy link
Copy Markdown
Collaborator

Pull Request: Refactoring Invoice Module (Enhance Directory Structure, Naming, Data Types, Type Casting & Logic Flow)

Description

This pull request generally add static type enhancements for the Invoice module. Inspired from Java's Jackson, an object mapper that casts JSON/XML objects to POJOs (Plain Old Java Objects).

We are doing a "minor" version of that, so that we can cast Moyasar Invoice Response to a PHP Object, in addition, we are taking advantage of PHP built-in data types and proper OO Principles, so that we have a better design and more predictable objects

One more issue that this PR has enhances is the naming; Calling a "DTO" from client side is not a good practice, since DTO are usually referring to the mappers inside the client's application. Thus, it was necessary to rename something like "CreateInvoiceDTO" into something meaningful from the client point of view, such as "CreateInvoiceRequest"

Since this change is a breaking one, I have updated invoice test cases with the new structure such that we still have a passing pipeline. In addition, I updated README, CONTRIBUTING & TODO so that we have a more accurate, detailed and well formatted documentation

  • Breaking Change ✅
  • New version: v1.0.0

Directory Structure

We are now classifying the project into

  • Modules (Invoice, Payment. etc...)
  • Shared Kernel (Shared), anything used by all modules or the client will be here

Tip

Note: Only Invocie modules is done, other modules are still on the old architecture

Each Module will have 2 main directories

  • Internal (What's hidden from client/other modules)
    • Usecases (Create, Update, etc...)
    • Core (Shared, Business Related Logic: Validators, Cosntants. etc...)
  • Shared (What's exposed to client/other modules)

Usecases

  • Create Invoice
  • Bulk Create Invoice
  • List Invoices (with filter)
  • Get Invoice By Id
  • Update Invoice
  • Cancel Invoice

Flow

  • User create RequestDto

Tip

Note: For getting invoice by id, or canceling invoice, you don't need to create a request dto, just the invoice id as a string

Warning

In previous version, you would multiply the invoice amount by the currency minor unit (e.g, to issue an ivoice with 25 SAR, you need to pass 2500). THIS IS NOT NEEDED ANYMORE, the package will do that internally

// OLD WAY ❌
use HamodaDev\Moyasar\Moyasar;
use HamodaDev\Moyasar\Invoice\DTO\CreateInvoiceDTO;

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

$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',
)); // retuns array

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

// ================================================
// NEW WAY ✅
use HamodaDev\Moyasar\Moyasar;
use HamodaDev\Moyasar\Invoice\Shared\DTOs\Requests\CreateInvoiceRequest;
use HamodaDev\Moyasar\Shared\Const\Currency;

$moyasar = new Moyasar(
    baseUrl: getenv('MOYASAR_BASE_URL'),
    apiKey:  getenv('MOYASAR_SECRET_KEY'),
);

$apiResponse = $moyasar->invoice()->create(new CreateInvoiceRequest(
    amount: 25, // Not 2500
    currency: Currency::SAR, // casting currency to an enum
    description: 'Order #1234',
    callbackUrl: 'https://example.com/webhooks/moyasar',
)); // returns ApiResponse<InvoiceSuccessfulResponse>
  • After creating the request dto, the request is sent
  • If the returned response is successful, the response will be validated against the specified validator
    • If the validation failed at any step, an InvalidArgumentException will be thrown
    • Otherwise ApiResponse<Resposne> will be returned
  • If the retuned response is NOT successful, ApiResponse<null> will be returned, however, the resposne body as string as well as the status code will be included in the returned ApiResponse instance for the user to handle them

Tip

We highly encourage to check the retuned response first

$apiResponse = $moyasar->invoice()->create(...);

if ($apiResponse->isSuccessful()) {
    // ...
}

Other Notes

  • README, CONTRIBUTING, TODO and invoice test cases are updated according to the new implementation

@AbRahman-ra AbRahman-ra added the enhancement New feature or request label Jun 7, 2026
@AbRahman-ra AbRahman-ra requested a review from hamoda-dev June 7, 2026 08:42
@AbRahman-ra AbRahman-ra self-assigned this Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant