payments tableUpdate your composer.json:
"require": {
"mostafax/payment-engine": "^2.0"
}
Run migrations:
php artisan vendor:publish --tag=payment-engine-migrations
php artisan migrate
Publish config:
php artisan vendor:publish --tag=payment-engine-config
Update .env — rename keys:
KNET_TRANSPORT_ID=...
KNET_TRANSPORT_PASSWORD=...
KNET_RESOURCE_KEY=...
KNET_SUCCESS_URL=https://yourapp.com/payment/knet/success
KNET_ERROR_URL=https://yourapp.com/payment/knet/error
Add to app/Http/Middleware/VerifyCsrfToken.php:
protected $except = [
'payment/*/success',
'payment/*/error',
'api/payment/webhook/*',
];
Update event listeners — replace old class references:
| Old | New |
|---|---|
| — | PaymentInitiated::class |
| — | PaymentCaptured::class |
| — | PaymentFailed::class |
| — | PaymentRecovered::class |
| — | PaymentReconciled::class |
Update service bindings in your app:
use Mostafax\PaymentEngine\Facades\PaymentEngine;
$result = PaymentEngine::initiate(InitiatePaymentDTO::fromArray([
'gateway' => 'knet',
'track_id' => (string) Str::ulid(),
'amount' => 25.500,
'currency' => 'KWD',
'success_url' => route('payment.return'),
'error_url' => route('payment.cancel'),
'metadata' => ['user_id' => auth()->id(), 'order_id' => $order->id],
]));
return redirect($result['redirect_url']);
CSRF_LABEL constant had a self-referential definition causing a fatal PHP error on boot'SUCCSS' typo in handleCallback() and inquire() caused all MyFatoorah payments to return Failed regardless of actual statusdetectMismatches() passed an unsaved ReconciliationReport (id=null) to reconcile(), which called persistItems() and triggered a null FK constraint violation — fixed by passing null and guarding persistItems() with a null check'fawry' case to idempotencyKey() (was falling through to json_encode($payload), producing unstable keys); added verifyFawrySignature() using same MD5 algorithm as FawryDriverfetchTransactions() ignored $from/$to and passed starting_after => null — replaced with period[date][from]/period[date][to] paramsRequestException; now catches GuzzleException (parent interface) to also handle connect timeouts, redirect loops, etc.initiate(): SHA-256 charge signature, hosted redirect + Fawry outlet referencehandleCallback(): MD5 callback signature verification, maps PAID → Capturedinquire(): GET /status/v2 with SHA-256 signature, live status checkfetchTransactions(): returns [] (Fawry has no batch-fetch endpoint)GatewayType::Fawry enum caseconfig/payment-engine.php: fawry gateway block with merchant_code, secure_key, return_url, verify_signatureInstallCommand: Fawry .env stubs appended on payment:installpayment:install command for zero-config setup (publish config, migrate, patch CSRF, append .env)composer.json version bumped to 2.1.0composer.json description and keywords updated to include Fawry/EgyptBreaking Change: Package renamed from
mostafax/knettomostafax/payment-engine. Namespace changed fromMostafax\KnettoMostafax\PaymentEngine. All database tables now use thepe_prefix (new migrations required).
PaymentDriverInterface — Strategy Pattern contract; new gateways require only this interfacepe_transactions — central ledger with ULID, tenant_id, status lifecycle timestampspe_transaction_events — append-only audit trail for every state changeTransactionRepository implementing TransactionRepositoryInterfaceTransactionStatus enum: initiated → pending → captured / failed / cancelled / refunded / reversed / unknownpe_webhook_payloads — raw HTTP body/headers persisted before processingpe_webhook_attempts — per-attempt success/failure with exception tracemax_attempts (default: 5)ProcessWebhookJob dispatched on dedicated payment-webhooks queueReconciliationEngine implementing ReconciliationInterfacepe_reconciliation_reports + pe_reconciliation_items tablesReconcileTransactionsJob for async background reconciliationPaymentReconciled event fired on completionRecoveryEngine::recoverStale() — scans pending transactions older than N minutes, queries gatewayRecoveryEngine::rebuildFromGateway() — creates missing internal records from gateway dataRecoverStaleTransactionsJob for background recovery runsPaymentRecovered event fired on successful recoveryBackfillEngine::sync() — full date-range sync from gatewayBackfillEngine::syncMissing() — gap-fill only (skips existing records)php artisan payment:sync knet
php artisan payment:sync knet --from=2026-01-01 --to=2026-01-31
php artisan payment:sync knet --missing
php artisan payment:reconcile knet --from=2026-01-01 --to=2026-01-31
php artisan payment:reconcile knet --async
php artisan payment:recover --minutes=30 --max=1000
PaymentInitiated — fired on new payment creationPaymentCaptured — fired on successful callback/webhookPaymentFailed — fired on error callback/webhookPaymentRefunded — fired on refund completionPaymentRecovered — fired on auto-recoveryPaymentReconciled — fired on reconciliation completionPOST /api/payment/initiate — initiate payment, returns redirect URLGET /api/payment/transactions — paginated transaction list with filtersGET /api/payment/transactions/{ulid} — single transaction with eventsPOST /api/payment/inquire/{trackId} — live gateway status checkPOST /api/payment/reconcile — trigger reconciliationGET /api/payment/reconciliation/reports — paginated reportsGET /api/payment/reconciliation/reports/{ulid} — report with itemsPOST /api/payment/webhook/{gateway} — receive push webhooks (Stripe, PayPal, Tap)ANY /payment/{gateway}/success — KNET / PayTabs success redirectANY /payment/{gateway}/error — KNET / PayTabs error redirecttenant_id column on transactions and reconciliation reportsconfig('payment-engine.tenant_resolver') callable for automatic tenant injectionforTenant() scopeInitiatePaymentDTO — replaces udf1-5 with typed metadata[]GatewayResponseDTO — normalized response across all gatewaysReconciliationResultDTO — structured reconciliation output with toArray() / fromArray()composer.json: package renamed mostafax/payment-engine, version 2.0.0Mostafax\PaymentEngine\payment-engine.php replaces scattered env() calls throughout codepe_ (was payments)declare(strict_types=1), PSR-12, final where applicableServiceProvider::boot() from dedicated route files (not inside register())Knet2.php — raw deprecated classKnetController.php — replaced by PaymentController + WebhookControllerKnetServiceProvider.php — replaced by PaymentEngineServiceProviderModels/Payment.php — replaced by Models/Transaction.phpRepositories/PaymentRepository.php — replaced by TransactionRepositoryRequests/KnetRequest.php — validation moved into controllersEnums/StatusEnum.php — replaced by Enums/TransactionStatus.phpServices/knet.php — replaced by Drivers/KnetDriver.phpvendor/ committed to git — removed, now in .gitignoreHow can I help you explore Laravel packages today?