├── .gitignore ├── example ├── sturcture_zaincash_package.png ├── Payment │ └── InitialPaymentRequest.php ├── routes │ └── api.php ├── waad_logo.svg └── Controllers │ └── Api │ └── PaymentController.php ├── src ├── Traits │ ├── Makeable.php │ ├── HttpClientRequests.php │ ├── Initialable.php │ └── getSetAttributes.php ├── Facades │ └── ZainCash.php ├── Services │ ├── Rules │ │ └── Hexadecimal.php │ ├── HttpClient.php │ ├── JWT.php │ ├── ValidationProcessing.php │ ├── ValidationProcessingOtp.php │ └── Validations.php ├── ZainCashServiceProvider.php ├── BaseZainCash.php └── ZainCash.php ├── resources └── lang │ ├── ar │ └── zaincash.php │ └── en │ └── zaincash.php ├── composer.json ├── config └── zaincash.php └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /.history 3 | composer.lock 4 | -------------------------------------------------------------------------------- /example/sturcture_zaincash_package.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waadmawlood/zaincash/HEAD/example/sturcture_zaincash_package.png -------------------------------------------------------------------------------- /src/Traits/Makeable.php: -------------------------------------------------------------------------------- 1 | 'id'], config("zaincash.language", "ar")); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /example/Payment/InitialPaymentRequest.php: -------------------------------------------------------------------------------- 1 | ["required", "numeric", "min:1000"], 18 | "serviceType" => ["required", "string", "max:254"], 19 | "orderId" => ["required", "string", "max:512"], 20 | ]; 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/Services/HttpClient.php: -------------------------------------------------------------------------------- 1 | withHeaders($headers)->withOptions(['verify' => $verify])->asForm()->post($url, $data); 19 | return $response; 20 | } catch (RequestException $e) { 21 | return [ 22 | 'error' => $e->getMessage(), 23 | 'status' => $e->response->status(), 24 | ]; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /example/routes/api.php: -------------------------------------------------------------------------------- 1 | prefix('payment')->as('payment.')->group(function () { 18 | Route::post('create/request/transaction', 'PaymentController@initialTransaction'); 19 | Route::get('check/transaction/{id}', 'PaymentController@checkTransaction'); 20 | Route::post('proccessing/transaction/{id}', 'PaymentController@processingTransaction'); 21 | Route::post('pay/transaction/{id}', 'PaymentController@payTransaction'); 22 | Route::post('cancel/transaction/{id}', 'PaymentController@cancelTransaction'); 23 | }); 24 | -------------------------------------------------------------------------------- /src/Services/JWT.php: -------------------------------------------------------------------------------- 1 | 'JWT', 'alg' => $alg]); 18 | 19 | // Encode the payload 20 | $payload = json_encode($data); 21 | 22 | // Base64 encode the header and payload 23 | $base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header)); 24 | $base64UrlPayload = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload)); 25 | 26 | // Create the signature 27 | $signature = hash_hmac('sha256', $base64UrlHeader . '.' . $base64UrlPayload, $secret, true); 28 | $base64UrlSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature)); 29 | 30 | // Combine the encoded header, payload, and signature with periods 31 | $jwt = $base64UrlHeader . '.' . $base64UrlPayload . '.' . $base64UrlSignature; 32 | 33 | return $jwt; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /resources/lang/ar/zaincash.php: -------------------------------------------------------------------------------- 1 | "يجب عليك تحديد المبلغ.", 5 | "amount_numeric" => "يجب أن يكون المبلغ رقمًا.", 6 | 'amount_min' => 'يجب أن يكون المبلغ على الأقل :min دينار عراقي.', 7 | "msisdn_regex" => "رقم الهاتف (Msisdn) غير صالح. يجب أن يكون مكونًا من 13 رقمًا. مثال: 9647813881805.", 8 | "serviceType_required" => "يجب عليك تحديد نوع الخدمة (على سبيل المثال: كتاب ، سفر ، ألعاب ، إلخ).", 9 | "serviceType_max" => "يجب ألا يتجاوز نوع الخدمة 254 حرفًا.", 10 | "orderId_required" => "يجب عليك تحديد معرف الطلب الذي يعمل كمعرف للوصف (على سبيل المثال: 1515616313).", 11 | "orderId_max" => "يجب ألا يتجاوز معرف الطلب 512 حرفًا.", 12 | "id_required" => "يجب عليك تحديد معرف العملية.", 13 | "id_hexadecimal" => "يجب أن يكون :attribute سلسلة هكساديسمال صالحة.", 14 | "phonenumber_required" => "يجب عليك تحديد رقم الهاتف.", 15 | "phonenumber_regex" => "رقم الهاتف غير صالح. يجب أن يكون مكونًا من 13 رقمًا. مثال: 9647813881805.", 16 | "pin_required" => "يجب عليك تحديد رمز التحقق.", 17 | "pin_max" => "رمز التحقق يجب ألا يتجاوز 254 حرفًا.", 18 | "opt_required" => "يجب عليك تحديد رمز التحقق الثنائي.", 19 | "opt_max" => "رمز التحقق الثنائي يجب ألا يتجاوز 10 أحرف.", 20 | ]; 21 | -------------------------------------------------------------------------------- /resources/lang/en/zaincash.php: -------------------------------------------------------------------------------- 1 | "You must specify the amount.", 5 | "amount_numeric" => "The amount must be a number.", 6 | 'amount_min' => 'Amount must be at least :min IQD.', 7 | "msisdn_regex" => "The Msisdn phone number is invalid. must be 13 digits. Example: 9647813881805.", 8 | "serviceType_required" => "You must specify the Service Type (e.g., Book, Travel, Gaming, etc).", 9 | "serviceType_max" => "The Service Type must not be greater than 254 characters.", 10 | "orderId_required" => "You must specify the Order ID, which acts as a recipe ID (e.g., 1515616313).", 11 | "orderId_max" => "The Order ID must not be greater than 512 characters.", 12 | "id_required" => "You must specify the Transaction ID.", 13 | "id_hexadecimal" => "The :attribute must be a valid hexadecimal string.", 14 | "phonenumber_required" => "You must specify the phone number.", 15 | "phonenumber_regex" => "The phonenumber is invalid. must be 13 digits. Example: 9647813881805.", 16 | "pin_required" => "You must specify the PIN.", 17 | "pin_max" => "The PIN must not be greater than 254 characters.", 18 | "opt_required" => "You must specify the OTP.", 19 | "opt_max" => "The OTP must not be greater than 10 characters.", 20 | ]; 21 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "waad/zaincash", 3 | "description": "ZainCash payment Integration Gateway package API for Laravel", 4 | "type": "library", 5 | "license": "MIT", 6 | "keywords": [ 7 | "Waad Mawlood", 8 | "ZainCash", 9 | "Zain Cash", 10 | "payment Integration", 11 | "payment Gateway", 12 | "Iraqi Dinar", 13 | "Zain Cash Laravel", 14 | "ZainCash Laravel", 15 | "Laravel Zain Cash", 16 | "Laravel ZainCash", 17 | "ZainCash API", 18 | "API ZainCash" 19 | ], 20 | "authors": [ 21 | { 22 | "name": "Waad Mawlood", 23 | "email": "waad_mawlood@outlook.com", 24 | "homepage": "https://waad.netlify.app", 25 | "role": "Developer" 26 | } 27 | ], 28 | "require": { 29 | "php": "^8.2", 30 | "laravel/framework": "^12.0" 31 | }, 32 | "autoload": { 33 | "psr-4": { 34 | "Waad\\ZainCash\\": "src/" 35 | } 36 | }, 37 | "config": { 38 | "sort-packages": true 39 | }, 40 | "minimum-stability": "dev", 41 | "prefer-stable": true, 42 | "extra": { 43 | "laravel": { 44 | "providers": [ 45 | "Waad\\ZainCash\\ZainCashServiceProvider" 46 | ], 47 | "aliases": { 48 | "ZainCash": "Waad\\ZainCash\\Facades\\ZainCash" 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/ZainCashServiceProvider.php: -------------------------------------------------------------------------------- 1 | loadTranslationsFrom(__DIR__.'/../resources/lang', 'zaincash'); 15 | 16 | if (!$this->app->runningInConsole()) { 17 | return; 18 | } 19 | 20 | // Publish the configuration file 21 | $this->publishes([ 22 | __DIR__ . '/../config/zaincash.php' => config_path('zaincash.php'), 23 | ], 'zaincash'); 24 | 25 | // Publish the language files 26 | $this->publishes([ 27 | __DIR__.'/../resources/lang' => resource_path('lang/vendor/zaincash'), 28 | ], 'zaincash'); 29 | 30 | } 31 | 32 | /** 33 | * Register the application services. 34 | */ 35 | public function register(): void 36 | { 37 | $this->app->bind('waad-zaincash', function () { 38 | return new ZainCash(); 39 | }); 40 | 41 | $this->app->singleton(\Waad\ZainCash\Helpers\Validations::class); 42 | $this->app->singleton(\Waad\ZainCash\Helpers\ValidationProcessing::class); 43 | $this->app->singleton(\Waad\ZainCash\Helpers\ValidationProcessingOtp::class); 44 | $this->app->singleton(\Waad\ZainCash\Helpers\JWT::class); 45 | $this->app->singleton(\Waad\ZainCash\Helpers\HttpClient::class); 46 | 47 | $this->mergeConfigFrom(__DIR__ . '/../config/zaincash.php', 'zaincash'); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Services/ValidationProcessing.php: -------------------------------------------------------------------------------- 1 | lang = $lang; 21 | if (blank($lang)) 22 | $this->lang = config("zaincash.language", "ar"); 23 | 24 | $validator = Validator::make([ 25 | "id" => $transactionID, 26 | "phonenumber" => $phonenumber, 27 | "pin" => $pin, 28 | ], $this->validationRules(), $this->validationMessages()); 29 | 30 | if ($validator->fails()) { 31 | return $this->prepareOutput(true, $validator->errors()->first()); 32 | } 33 | 34 | return $this->prepareOutput(false); 35 | } 36 | 37 | private function validationRules(): array 38 | { 39 | return [ 40 | "id" => ["required", new Hexadecimal], 41 | "phonenumber" => ["required", "regex:/^[0-9]{13}$/"], 42 | "pin" => ["required", "string", "max:254"], 43 | ]; 44 | } 45 | 46 | private function validationMessages(): array 47 | { 48 | return [ 49 | "id.required" => trans('zaincash::zaincash.id_required', [], $this->lang), 50 | "phonenumber.required" => trans('zaincash::zaincash.phonenumber_required', [], $this->lang), 51 | "phonenumber.regex" => trans('zaincash::zaincash.phonenumber_regex', [], $this->lang), 52 | "pin.required" => trans('zaincash::zaincash.pin_required', [], $this->lang), 53 | "pin.max" => trans('zaincash::zaincash.pin_max', [], $this->lang), 54 | ]; 55 | } 56 | 57 | private function prepareOutput(bool $isError, string $message = "Successfull"): mixed 58 | { 59 | return json_decode(json_encode(["error" => $isError, "message" => $message]), false); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Services/ValidationProcessingOtp.php: -------------------------------------------------------------------------------- 1 | lang = $lang; 22 | if (blank($lang)) 23 | $this->lang = config("zaincash.language", "ar"); 24 | 25 | $validator = Validator::make([ 26 | "id" => $transactionID, 27 | "phonenumber" => $phonenumber, 28 | "pin" => $pin, 29 | "opt" => $otp, 30 | ], $this->validationRules(), $this->validationMessages()); 31 | 32 | if ($validator->fails()) { 33 | return $this->prepareOutput(true, $validator->errors()->first()); 34 | } 35 | 36 | return $this->prepareOutput(false); 37 | } 38 | 39 | private function validationRules(): array 40 | { 41 | return [ 42 | "id" => ["required", new Hexadecimal], 43 | "phonenumber" => ["required", "regex:/^[0-9]{13}$/"], 44 | "pin" => ["required", "string", "max:254"], 45 | "opt" => ["required", "string", "max:10"], 46 | ]; 47 | } 48 | 49 | private function validationMessages(): array 50 | { 51 | return [ 52 | "id.required" => trans('zaincash::zaincash.id_required', [], $this->lang), 53 | "phonenumber.required" => trans('zaincash::zaincash.phonenumber_required', [], $this->lang), 54 | "phonenumber.regex" => trans('zaincash::zaincash.phonenumber_regex', [], $this->lang), 55 | "pin.required" => trans('zaincash::zaincash.pin_required', [], $this->lang), 56 | "pin.max" => trans('zaincash::zaincash.pin_max', [], $this->lang), 57 | "opt.required" => trans('zaincash::zaincash.opt_required', [], $this->lang), 58 | "opt.max" => trans('zaincash::zaincash.opt_max', [], $this->lang), 59 | ]; 60 | } 61 | 62 | private function prepareOutput(bool $isError, string $message = "Successfull"): mixed 63 | { 64 | return json_decode(json_encode(["error" => $isError, "message" => $message]), false); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Services/Validations.php: -------------------------------------------------------------------------------- 1 | lang = $lang; 24 | if (blank($lang)) 25 | $this->lang = config("zaincash.language", "ar"); 26 | 27 | $this->minAmount = $minAmount; 28 | if (is_null($minAmount)) 29 | $this->minAmount = config("zaincash.min_amount", 1000); 30 | 31 | $validator = Validator::make([ 32 | "amount" => $amount, 33 | "msisdn" => $msisdn, 34 | "serviceType" => $serviceType, 35 | "orderId" => $orderId, 36 | ], $this->validationRules(), $this->validationMessages()); 37 | 38 | if ($validator->fails()) { 39 | return $this->prepareOutput(true, $validator->errors()->first()); 40 | } 41 | 42 | return $this->prepareOutput(false); 43 | } 44 | 45 | private function validationRules(): array 46 | { 47 | return [ 48 | "amount" => ["required", "numeric", "min:{$this->minAmount}"], 49 | "msisdn" => ["required", "regex:/^[0-9]{13}$/"], 50 | "serviceType" => ["required", "string", "max:254"], 51 | "orderId" => ["required", "string", "max:512"], 52 | ]; 53 | } 54 | 55 | private function validationMessages(): array 56 | { 57 | return [ 58 | "amount.required" => trans('zaincash::zaincash.amount_required', [], $this->lang), 59 | "amount.numeric" => trans('zaincash::zaincash.amount_numeric', [], $this->lang), 60 | "amount.min" => trans('zaincash::zaincash.amount_min', ['min' => $this->minAmount], $this->lang), 61 | "msisdn.regex" => trans('zaincash::zaincash.msisdn_regex', [], $this->lang), 62 | "serviceType.required" => trans('zaincash::zaincash.serviceType_required', [], $this->lang), 63 | "serviceType.max" => trans('zaincash::zaincash.serviceType_max', [], $this->lang), 64 | "orderId.required" => trans('zaincash::zaincash.orderId_required', [], $this->lang), 65 | "orderId.max" => trans('zaincash::zaincash.orderId_max', [], $this->lang), 66 | ]; 67 | } 68 | 69 | private function prepareOutput(bool $isError, string $message = "Successfull"): mixed 70 | { 71 | return json_decode(json_encode(["error" => $isError, "message" => $message]), false); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/BaseZainCash.php: -------------------------------------------------------------------------------- 1 | orderId = $this->getConfig("prefix_order_id") . $orderId; 40 | } 41 | 42 | $this->initial(); 43 | } 44 | 45 | protected function bodyPostRequest($token, $language, $merchantId) 46 | { 47 | return [ 48 | 'lang' => $language, 49 | ...$this->bodyPostRequestCheckTransaction($token, $merchantId) 50 | ]; 51 | } 52 | 53 | protected function bodyPostRequestCheckTransaction($token, $merchantId) 54 | { 55 | return [ 56 | 'merchantId' => $merchantId, 57 | 'token' => urlencode($token), 58 | ]; 59 | } 60 | 61 | protected function createToken($amount, $serviceType, $msisdn, $orderId, $redirectUrl, $secret) 62 | { 63 | $data = [ 64 | "amount" => $amount, 65 | "serviceType" => $serviceType, 66 | "msisdn" => $msisdn, 67 | "orderId" => $orderId, 68 | "redirectUrl" => $redirectUrl, 69 | "iat" => time(), 70 | "exp" => time() + 60 * 60 * 4, 71 | ]; 72 | 73 | return app(\Waad\ZainCash\Services\JWT::class)->encode($data, $secret); 74 | } 75 | 76 | protected function createTokenCheck(string $transactionID, string $msisdn, string $secret) 77 | { 78 | $data = [ 79 | "id" => $transactionID, 80 | "msisdn" => $msisdn, 81 | "iat" => time(), 82 | "exp" => time() + 60 * 60 * 4, 83 | ]; 84 | 85 | return app(\Waad\ZainCash\Services\JWT::class)->encode($data, $secret); 86 | } 87 | 88 | protected function getConfig(string $arrtibute, string $default = null) 89 | { 90 | return config("zaincash.$arrtibute", $default); 91 | } 92 | 93 | protected function jsonDecodeObject($array, $isReturnArray = false) 94 | { 95 | return json_decode(json_encode($array), $isReturnArray); 96 | } 97 | 98 | protected function getCheckResponseId($parseResponse) 99 | { 100 | if (blank($parseResponse->id ?? null)) { 101 | throw new \Exception($parseResponse->err->msg); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Traits/HttpClientRequests.php: -------------------------------------------------------------------------------- 1 | bodyPostRequest($token, $this->getLanguage(), $this->getMerchantId()); 14 | 15 | return app(\Waad\ZainCash\Services\HttpClient::class) 16 | ->httpPost( 17 | url: $this->getTUrl(), 18 | data: $body, 19 | timeout: $this->getTimeout(), 20 | verify: $this->getVerifySsl() 21 | ); 22 | } 23 | 24 | /** 25 | * @param string $token 26 | * @return @return \Psr\Http\Message\ResponseInterface|\Illuminate\Http\Client\Response|array 27 | */ 28 | protected function sendRequestCheckTransaction(string $token) 29 | { 30 | $body = $this->bodyPostRequestCheckTransaction($token, $this->getMerchantId()); 31 | return app(\Waad\ZainCash\Services\HttpClient::class) 32 | ->httpPost( 33 | url: $this->getCUrl(), 34 | data: $body, 35 | timeout: $this->getTimeout(), 36 | verify: $this->getVerifySsl() 37 | ); 38 | } 39 | 40 | /** 41 | * @param string $phonenumber 42 | * @param string $pin 43 | * @return @return \Psr\Http\Message\ResponseInterface|\Illuminate\Http\Client\Response|array 44 | */ 45 | protected function sendRequestProcessingTransaction(string $phonenumber, string $pin) 46 | { 47 | return app(\Waad\ZainCash\Services\HttpClient::class) 48 | ->httpPost( 49 | url: $this->getProcessingUrl(), 50 | data: [ 51 | 'id' => $this->getTransactionID(), 52 | 'phonenumber' => $phonenumber, 53 | 'pin' => $pin, 54 | ], 55 | timeout: $this->getTimeout(), 56 | verify: $this->getVerifySsl() 57 | ); 58 | } 59 | 60 | /** 61 | * @param string $phonenumber 62 | * @param string $pin 63 | * @param string $otp 64 | * @return @return \Psr\Http\Message\ResponseInterface|\Illuminate\Http\Client\Response|array 65 | */ 66 | protected function sendRequestPayTransaction(string $phonenumber, string $pin, string $otp) 67 | { 68 | return app(\Waad\ZainCash\Services\HttpClient::class) 69 | ->httpPost( 70 | url: $this->getProcessingOtpUrl(), 71 | data: [ 72 | 'id' => $this->getTransactionID(), 73 | 'phonenumber' => $phonenumber, 74 | 'pin' => $pin, 75 | 'otp' => $otp, 76 | ], 77 | timeout: $this->getTimeout(), 78 | verify: $this->getVerifySsl() 79 | ); 80 | } 81 | 82 | /** 83 | * @return @return \Psr\Http\Message\ResponseInterface|\Illuminate\Http\Client\Response|array 84 | */ 85 | protected function sendRequestCancelTransaction() 86 | { 87 | return app(\Waad\ZainCash\Services\HttpClient::class) 88 | ->httpPost( 89 | url: $this->getCancelUrl(), 90 | data: [ 91 | 'id' => $this->getTransactionID(), 92 | 'type' => 'MERCHANT_PAYMENT' 93 | ], 94 | timeout: $this->getTimeout(), 95 | verify: $this->getVerifySsl() 96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /example/waad_logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/Controllers/Api/PaymentController.php: -------------------------------------------------------------------------------- 1 | setAmount($request->amount) 24 | // ->setServiceType('Book') 25 | // ->setOrderId(Str::random(36)) 26 | // ->setIsTest(true) 27 | // ->setIsReturnArray(true); 28 | // return response()->json($zainCashPayment->createTransaction()); 29 | 30 | // with facade 31 | $zainCashPayment = ZainCash::setAmount($request->amount) 32 | ->setServiceType('Book') 33 | ->setOrderId(Str::random(36)) 34 | ->setIsTest(true) 35 | ->setIsReturnArray(true); 36 | 37 | return response()->json($zainCashPayment->createTransaction()); 38 | } 39 | 40 | /** 41 | * Check Transaction ID 42 | * 43 | * @param string $id 44 | * @return \Illuminate\Http\JsonResponse 45 | */ 46 | public function checkTransaction(string $id) 47 | { 48 | // without facade 49 | // use Waad\ZainCash\ZainCash; 50 | // $zainCashPayment = ZainCash::make() 51 | // ->setTransactionID($id) 52 | // ->setIsRedirect(false) 53 | // ->setIsTest(true) 54 | // ->setIsReturnArray(true); 55 | // return response()->json($zainCashPayment->checkTransaction()); 56 | 57 | // with facade 58 | $zainCashPayment = ZainCash::setTransactionID($id) 59 | ->setIsRedirect(false) 60 | ->setIsTest(true) 61 | ->setIsReturnArray(true); 62 | 63 | return response()->json($zainCashPayment->checkTransaction()); 64 | } 65 | 66 | /** 67 | * Processing Transaction 68 | * 69 | * @param string $id 70 | * @return \Illuminate\Http\JsonResponse 71 | */ 72 | public function processingTransaction(string $id) 73 | { 74 | // without facade 75 | // use Waad\ZainCash\ZainCash; 76 | // $zainCashPayment = ZainCash::make() 77 | // ->setTransactionID($id) 78 | // ->setIsRedirect(false) 79 | // ->setIsTest(true) 80 | // ->setIsReturnArray(true); 81 | // return response()->json($zainCashPayment->processingTransaction("9647802999569", '1234')); 82 | 83 | // with facade 84 | $zainCashPayment = ZainCash::setTransactionID($id) 85 | ->setIsRedirect(false) 86 | ->setIsTest(true) 87 | ->setIsReturnArray(true); 88 | 89 | return response()->json($zainCashPayment->processingTransaction("9647802999569", '1234')); 90 | } 91 | 92 | /** 93 | * Pay Transaction 94 | * 95 | * @param string $id 96 | * @return \Illuminate\Http\JsonResponse 97 | */ 98 | public function payTransaction(string $id) 99 | { 100 | // without facade 101 | // use Waad\ZainCash\ZainCash; 102 | // $zainCashPayment = ZainCash::make() 103 | // ->setTransactionID($id) 104 | // ->setIsRedirect(false) 105 | // ->setIsTest(true) 106 | // ->setIsReturnArray(true); 107 | // return response()->json($zainCashPayment->payTransaction("9647802999569", '1234', '1111')); 108 | 109 | // with facade 110 | $zainCashPayment = ZainCash::setTransactionID($id) 111 | ->setIsRedirect(false) 112 | ->setIsTest(true) 113 | ->setIsReturnArray(true); 114 | 115 | return response()->json($zainCashPayment->payTransaction("9647802999569", '1234', '1111')); 116 | } 117 | 118 | /** 119 | * Cancel Transaction 120 | * 121 | * @param string $id 122 | * @return \Illuminate\Http\JsonResponse 123 | */ 124 | public function cancelTransaction(string $id) 125 | { 126 | // without facade 127 | // use Waad\ZainCash\ZainCash; 128 | // $zainCashPayment = ZainCash::make() 129 | // ->setTransactionID($id) 130 | // ->setIsRedirect(false) 131 | // ->setIsTest(true) 132 | // ->setIsReturnArray(true); 133 | // return response()->json($zainCashPayment->cancelTransaction()); 134 | 135 | // with facade 136 | $zainCashPayment = ZainCash::setTransactionID($id) 137 | ->setIsRedirect(false) 138 | ->setIsTest(true) 139 | ->setIsReturnArray(true); 140 | 141 | return response()->json($zainCashPayment->cancelTransaction()); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/Traits/Initialable.php: -------------------------------------------------------------------------------- 1 | getIsTest())) { 14 | $this->setIsTest($this->getConfig("test")); 15 | } 16 | 17 | // Set the Min Amount. 18 | if (is_null($this->getMinAmount())) { 19 | $this->setMinAmount($this->getConfig("min_amount")); 20 | } 21 | 22 | // Set the MSISDN. 23 | if (blank($this->getMsisdn())) { 24 | $this->setMsisdn($this->getConfig("msisdn")); 25 | } 26 | 27 | // Set the secret. 28 | if (blank($this->getSecret())) { 29 | $this->setSecret($this->getConfig("secret")); 30 | } 31 | 32 | // Set the merchant ID. 33 | if (blank($this->getMerchantId())) { 34 | $this->setMerchantId($this->getConfig("merchant_id")); 35 | } 36 | 37 | // Set the language. 38 | if (blank($this->getLanguage())) { 39 | $this->setLanguage($this->getConfig("language")); 40 | } 41 | 42 | // Set the redirection status. 43 | if (is_null($this->getIsRedirect())) { 44 | $this->setIsRedirect($this->getConfig("is_redirect")); 45 | } 46 | 47 | // Set the timeout request. 48 | if (is_null($this->getTimeout())) { 49 | $this->setTimeout($this->getConfig("timeout")); 50 | } 51 | 52 | // Set the verify SSL. 53 | if (is_null($this->getVerifySsl())) { 54 | $this->setVerifySsl($this->getConfig("verify_ssl")); 55 | } 56 | 57 | // Set the URLs. 58 | $this->initailUrls(); 59 | } 60 | 61 | protected function initailUrls(bool $force = false): void 62 | { 63 | // Set the base URL. 64 | if (($this->getIsTest() && blank($this->getBaseUrl())) || ($this->getIsTest() && $force)) { 65 | $this->setBaseUrl($this->getConfig("test_url")); 66 | } 67 | 68 | if ((!$this->getIsTest() && blank($this->getBaseUrl())) || (!$this->getIsTest() && $force)) { 69 | $this->setBaseUrl($this->getConfig("live_url")); 70 | } 71 | 72 | // Add a trailing slash to the base URL if it is not already present. 73 | if (filled($this->getBaseUrl()) && substr($this->getBaseUrl(), -1) !== "/") { 74 | $this->setBaseUrl($this->getBaseUrl() . "/"); 75 | } 76 | 77 | // Set the URLs. 78 | if ($force || blank($this->getTUrl())) $this->setTUrl($this->getBaseUrl() . "transaction/init"); 79 | if ($force || blank($this->getCUrl())) $this->setCUrl($this->getBaseUrl() . "transaction/get"); 80 | if ($force || blank($this->getRUrl())) $this->setRUrl($this->getBaseUrl() . "transaction/pay?id="); 81 | if ($force || blank($this->getProcessingUrl())) $this->setProcessingUrl($this->getBaseUrl() . "transaction/processing"); 82 | if ($force || blank($this->getProcessingOtpUrl())) $this->setProcessingOtpUrl($this->getBaseUrl() . "transaction/processingOTP?type=MERCHANT_PAYMENT"); 83 | if ($force || blank($this->getCancelUrl())) $this->setCancelUrl($this->getBaseUrl() . "transaction/cancel"); 84 | } 85 | 86 | /** 87 | * Validation Create Request 88 | * 89 | * @throws \Exception 90 | */ 91 | public function validationCreateRequest(): void 92 | { 93 | $validator = app(\Waad\ZainCash\Services\Validations::class)->validator( 94 | $this->getAmount(), 95 | $this->getMsisdn(), 96 | $this->getServiceType(), 97 | $this->getOrderId(), 98 | $this->getLanguage(), 99 | $this->getMinAmount() 100 | ); 101 | 102 | if ($validator->error) { 103 | throw new \Exception($validator->message); 104 | } 105 | } 106 | 107 | /** 108 | * Validation Processing Step 109 | * 110 | * @throws \Exception 111 | */ 112 | public function validationProcessing(string $phonenumber, string $pin): void 113 | { 114 | $validator = app(\Waad\ZainCash\Services\ValidationProcessing::class)->validator( 115 | $this->getTransactionID(), 116 | $phonenumber, 117 | $pin, 118 | $this->getLanguage() 119 | ); 120 | 121 | if ($validator->error) { 122 | throw new \Exception($validator->message); 123 | } 124 | } 125 | 126 | /** 127 | * Validation Processing OTP Step 128 | * 129 | * @throws \Exception 130 | */ 131 | public function validationProcessingOtp(string $phonenumber, string $pin, string $otp): void 132 | { 133 | $validator = app(\Waad\ZainCash\Services\ValidationProcessingOtp::class)->validator( 134 | $this->getTransactionID(), 135 | $phonenumber, 136 | $pin, 137 | $otp, 138 | $this->getLanguage() 139 | ); 140 | 141 | if ($validator->error) { 142 | throw new \Exception($validator->message); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /config/zaincash.php: -------------------------------------------------------------------------------- 1 | env('ZAINCASH_MSISDN', '9647835077893'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Merchant ID 23 | |-------------------------------------------------------------------------- 24 | | 25 | | You can request a Merchant ID from ZainCash's support. 26 | | The current 'merchantid' is for test purposes and is only working in the test environment. 27 | | To use ZainCash in the production environment, replace this value with your actual Merchant ID. 28 | */ 29 | 'merchant_id' => env('ZAINCASH_MERCHANT_ID', '5ffacf6612b5777c6d44266f'), 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Secret Hash 34 | |-------------------------------------------------------------------------- 35 | | 36 | | This secret is used to decode and encode JWT during requests. 37 | | It must be requested from ZainCash. 38 | | The current secret is for test purposes and only works in the test environment. 39 | | Replace it with your actual secret for production. 40 | */ 41 | 'secret' => env('ZAINCASH_SECRET', '$2y$10$hBbAZo2GfSSvyqAyV2SaqOfYewgYpfR1O19gIh4SqyGWdmySZYPuS'), 42 | 43 | /* 44 | |-------------------------------------------------------------------------- 45 | | Test Environment Mode 46 | |-------------------------------------------------------------------------- 47 | | 48 | | Specify the environment for using the ZainCash API. 49 | | Set 'test' to false for the test environment or true for the live environment 50 | | after you have obtained all credentials from ZainCash. 51 | */ 52 | 'test' => env('ZAINCASH_TEST', true), 53 | 'test_url' => env('ZAINCASH_TEST_URL', 'https://test.zaincash.iq/'), 54 | 'live_url' => env('ZAINCASH_LIVE_URL', 'https://api.zaincash.iq/'), 55 | 56 | /* 57 | |-------------------------------------------------------------------------- 58 | | Language 59 | |-------------------------------------------------------------------------- 60 | | 61 | | Set the language for the ZainCash payment page. 62 | | Use 'ar' for Arabic or 'en' for English. 63 | */ 64 | 'language' => env('ZAINCASH_LANGUAGE', 'ar'), 65 | 66 | /* 67 | |-------------------------------------------------------------------------- 68 | | Prefix Order ID 69 | |-------------------------------------------------------------------------- 70 | | 71 | | prefix_order, you can use it to help you in tagging transactions with your website IDs. 72 | | If you have no order numbers in your website, you can leave it as a prefix or an identifier. 73 | | Example: 'palestine_free_' will result in order IDs like "wa3d_xxxxxxx". 74 | */ 75 | 'prefix_order_id' => env('ZAINCASH_PREFIX_ORDER_ID', 'wa3d_'), 76 | 77 | /* 78 | |-------------------------------------------------------------------------- 79 | | IS Redirect & Redirect URL 80 | |-------------------------------------------------------------------------- 81 | | 82 | | 'is_redirect' is used to specify whether or not to redirect to the ZainCash payment page. 83 | | 'is_redirect' if was `false` will ZainCash return a Transaction ID to the backend. 84 | | 'is_redirect' if was `true`, redirection after request to https://api.zaincash.iq/transaction/pay?id={TransactionId}. 85 | */ 86 | 'is_redirect' => env('ZAINCASH_IS_REDIRECT', false), 87 | 88 | /* 89 | |-------------------------------------------------------------------------- 90 | | Minimum Amount 91 | |-------------------------------------------------------------------------- 92 | | 93 | | Set the minimum amount for a valid transaction in Iraqi Dinar (IQD). 94 | | Transactions with amounts less than this value will be considered invalid. 95 | */ 96 | 'min_amount' => env('ZAINCASH_MIN_AMOUNT', 250), 97 | 98 | /* 99 | |-------------------------------------------------------------------------- 100 | | timeout Request (in seconds) 101 | |-------------------------------------------------------------------------- 102 | | 103 | | Set the timeout for the request to ZainCash's API. 104 | | The default value is 10 seconds. 105 | | make it 0 (zero) for unlimited. 106 | */ 107 | 'timeout' => env('ZAINCASH_TIMEOUT', 10), 108 | 109 | /* 110 | |-------------------------------------------------------------------------- 111 | | Verify SSL 112 | |-------------------------------------------------------------------------- 113 | | 114 | | Set the verify SSL for the request to ZainCash's API. 115 | | The default value is true. 116 | | make it false for disable verify SSL (not recommended). 117 | | if it is true and you used the `http` protocol so will get an error. so make it false. 118 | */ 119 | 'verify_ssl' => env('ZAINCASH_VERIFY_SSL', true), 120 | ]; 121 | -------------------------------------------------------------------------------- /src/Traits/getSetAttributes.php: -------------------------------------------------------------------------------- 1 | amount; 10 | } 11 | 12 | public function setAmount(int|float $amount): self 13 | { 14 | $this->amount = $amount; 15 | return $this; 16 | } 17 | 18 | public function getMinAmount(): int|float|null 19 | { 20 | return $this->minAmount; 21 | } 22 | 23 | public function setMinAmount(int|float $minAmount): self 24 | { 25 | $this->minAmount = $minAmount; 26 | return $this; 27 | } 28 | 29 | public function getServiceType(): string|null 30 | { 31 | return $this->serviceType; 32 | } 33 | 34 | public function setServiceType(string $serviceType): self 35 | { 36 | $this->serviceType = $serviceType; 37 | return $this; 38 | } 39 | 40 | public function getOrderId(): string|null 41 | { 42 | return $this->orderId; 43 | } 44 | 45 | public function setOrderId(string $orderId): self 46 | { 47 | $this->orderId = $this->getConfig("prefix_order_id") . $orderId; 48 | return $this; 49 | } 50 | 51 | public function getMsisdn(): string|null 52 | { 53 | return $this->msisdn; 54 | } 55 | 56 | public function setMsisdn(string $msisdn): self 57 | { 58 | $this->msisdn = $msisdn; 59 | return $this; 60 | } 61 | 62 | public function getSecret(): string|null 63 | { 64 | return $this->secret; 65 | } 66 | 67 | public function setSecret(string $secret): self 68 | { 69 | $this->secret = $secret; 70 | return $this; 71 | } 72 | 73 | public function getMerchantId(): string|null 74 | { 75 | return $this->merchantId; 76 | } 77 | 78 | public function setMerchantId(string $merchantId): self 79 | { 80 | $this->merchantId = $merchantId; 81 | return $this; 82 | } 83 | 84 | public function getIsTest(): bool|null 85 | { 86 | return $this->isTest; 87 | } 88 | 89 | public function setIsTest(bool $isTest): self 90 | { 91 | $this->isTest = $isTest; 92 | $this->initailUrls(true); 93 | return $this; 94 | } 95 | 96 | public function getLanguage(): string|null 97 | { 98 | return $this->language; 99 | } 100 | 101 | public function setLanguage(string $language): self 102 | { 103 | $this->language = $language; 104 | return $this; 105 | } 106 | 107 | public function getBaseUrl(): string|null 108 | { 109 | return $this->baseUrl; 110 | } 111 | 112 | public function setBaseUrl($baseUrl): self 113 | { 114 | $this->baseUrl = $baseUrl; 115 | return $this; 116 | } 117 | 118 | public function getIsRedirect(): bool|null 119 | { 120 | return $this->isRedirect; 121 | } 122 | 123 | public function setIsRedirect(bool $isRedirect): self 124 | { 125 | $this->isRedirect = $isRedirect; 126 | return $this; 127 | } 128 | 129 | public function getTUrl(): string|null 130 | { 131 | return $this->tUrl; 132 | } 133 | 134 | public function setTUrl(string $tUrl): self 135 | { 136 | $this->tUrl = $tUrl; 137 | return $this; 138 | } 139 | 140 | public function getCUrl(): string|null 141 | { 142 | return $this->cUrl; 143 | } 144 | 145 | public function setCUrl(string $cUrl): self 146 | { 147 | $this->cUrl = $cUrl; 148 | return $this; 149 | } 150 | 151 | public function getRUrl(): string|null 152 | { 153 | return $this->rUrl; 154 | } 155 | 156 | public function setRUrl(string $rUrl): self 157 | { 158 | $this->rUrl = $rUrl; 159 | return $this; 160 | } 161 | 162 | 163 | public function getProcessingUrl(): string|null 164 | { 165 | return $this->processingUrl; 166 | } 167 | 168 | public function setProcessingUrl(string $processingUrl): self 169 | { 170 | $this->processingUrl = $processingUrl; 171 | return $this; 172 | } 173 | 174 | 175 | public function getProcessingOtpUrl(): string|null 176 | { 177 | return $this->processingOtpUrl; 178 | } 179 | 180 | public function setProcessingOtpUrl(string $processingOtpUrl): self 181 | { 182 | $this->processingOtpUrl = $processingOtpUrl; 183 | return $this; 184 | } 185 | 186 | public function getCancelUrl(): string|null 187 | { 188 | return $this->cancelUrl; 189 | } 190 | 191 | public function setCancelUrl(string $cancelUrl): self 192 | { 193 | $this->cancelUrl = $cancelUrl; 194 | return $this; 195 | } 196 | 197 | public function getTransactionID(): string|null 198 | { 199 | return $this->transactionID; 200 | } 201 | 202 | public function setTransactionID(?string $transactionID = null): self 203 | { 204 | $this->transactionID = $transactionID; 205 | return $this; 206 | } 207 | 208 | public function getIsReturnArray(): bool 209 | { 210 | return $this->isReturnArray; 211 | } 212 | 213 | public function setIsReturnArray(bool $isReturnArray = false): self 214 | { 215 | $this->isReturnArray = $isReturnArray; 216 | return $this; 217 | } 218 | 219 | public function getTimeout(): int|null 220 | { 221 | return $this->timeout; 222 | } 223 | 224 | public function setTimeout(int $timeout): self 225 | { 226 | $this->timeout = $timeout; 227 | return $this; 228 | } 229 | 230 | public function getVerifySsl(): bool|null 231 | { 232 | return $this->verifySsl; 233 | } 234 | 235 | public function setVerifySsl(bool $verifySsl): self 236 | { 237 | $this->verifySsl = $verifySsl; 238 | return $this; 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /src/ZainCash.php: -------------------------------------------------------------------------------- 1 | validationCreateRequest(); 22 | 23 | // Create Token JWT 24 | $token = $this->createToken( 25 | $this->getAmount(), 26 | $this->getServiceType(), 27 | $this->getMsisdn(), 28 | $this->getOrderId(), 29 | $this->getIsRedirect() ? $this->getRUrl() : '', 30 | $this->getSecret() 31 | ); 32 | 33 | // Creates a transaction HTTP client using the provided token. 34 | $response = $this->createTransactionHttpClient($token); 35 | 36 | // Check if the response is not 200 37 | if (!$response->ok()) { 38 | throw new RequestException($response); 39 | } 40 | 41 | // Parse the response to Object StdClass 42 | $parseResponse = json_decode($response->body()); 43 | 44 | // Check if the response has error 45 | $this->getCheckResponseId($parseResponse); 46 | $transactionID = $parseResponse->id; 47 | 48 | // Redirect to ZainCash payment page or return transaction id 49 | return $this->getIsRedirect() ? 50 | redirect()->away($this->getRUrl() . $transactionID) : 51 | ($this->getIsReturnArray() ? 52 | $this->jsonDecodeObject($parseResponse, true) : 53 | $parseResponse); 54 | } 55 | 56 | /** 57 | * Check Transaction ID 58 | * 59 | * @throws RequestException 60 | * @return array|\stdClass 61 | */ 62 | public function checkTransaction() 63 | { 64 | // Create Token JWT 65 | $token = $this->createTokenCheck( 66 | $this->getTransactionId(), 67 | $this->getMsisdn(), 68 | $this->getSecret() 69 | ); 70 | 71 | // Check Transaction HTTP client using the provided token. 72 | $response = $this->sendRequestCheckTransaction($token); 73 | 74 | // Check if the response is not success 75 | if (!$response->ok()) { 76 | throw new RequestException($response); 77 | } 78 | 79 | // Parse the response to Object StdClass or Array 80 | $parseResponse = json_decode($response->body(), $this->getIsReturnArray()); 81 | 82 | return $parseResponse; 83 | } 84 | 85 | 86 | /** 87 | * Processing Transaction 88 | * 89 | * @param string $phonenumber 90 | * @param string $pin 91 | * @param string|null $transactionID 92 | * @throws RequestException 93 | * @return array|\stdClass 94 | */ 95 | public function processingTransaction(string $phonenumber, string $pin, $transactionID = null) 96 | { 97 | // Set Transaction ID if not set 98 | if (blank($this->getTransactionID())) { 99 | $this->setTransactionID($transactionID); 100 | } 101 | 102 | // Validation Processing Step 103 | $this->validationProcessing($phonenumber, $pin); 104 | 105 | // Processing Transaction HTTP client using the provided phone number and pin. 106 | $response = $this->sendRequestProcessingTransaction($phonenumber, $pin); 107 | 108 | // Check if the response is not success 109 | if (!$response->ok()) { 110 | throw new RequestException($response); 111 | } 112 | 113 | // Parse the response to Object StdClass or Array 114 | $parseResponse = json_decode($response->body(), $this->getIsReturnArray()); 115 | 116 | return $parseResponse; 117 | } 118 | 119 | 120 | /** 121 | * Pay Transaction 122 | * 123 | * @param string $phonenumber 124 | * @param string $pin 125 | * @param string $otp 126 | * @param string|null $transactionID 127 | * @throws RequestException 128 | * @return array|\stdClass 129 | */ 130 | public function payTransaction($phonenumber, $pin, $otp, $transactionID = null) 131 | { 132 | // Set Transaction ID if not set 133 | if (blank($this->getTransactionID())) { 134 | $this->setTransactionID($transactionID); 135 | } 136 | 137 | // Validation Processing OTP Step 138 | $this->validationProcessingOtp($phonenumber, $pin, $otp); 139 | 140 | // Pay Transaction HTTP client using the provided phone number, pin and otp. 141 | $response = $this->sendRequestPayTransaction($phonenumber, $pin, $otp); 142 | 143 | // Check if the response is not success 144 | if (!$response->ok()) { 145 | throw new RequestException($response); 146 | } 147 | 148 | // Parse the response to Object StdClass or Array 149 | $parseResponse = json_decode($response->body(), $this->getIsReturnArray()); 150 | 151 | return $parseResponse; 152 | } 153 | 154 | /** 155 | * Cancel Transaction 156 | * 157 | * @param string|null $transactionID 158 | * @throws RequestException 159 | * @return array|\stdClass 160 | */ 161 | public function cancelTransaction($transactionID = null) 162 | { 163 | // Set Transaction ID if not set 164 | if (blank($this->getTransactionID())) { 165 | $this->setTransactionID($transactionID); 166 | } 167 | 168 | // Cancel Transaction HTTP client using the provided transaction id. 169 | $response = $this->sendRequestCancelTransaction(); 170 | 171 | // Check if the response is not success 172 | if (!$response->ok()) { 173 | throw new RequestException($response); 174 | } 175 | 176 | // Parse the response to Object StdClass or Array 177 | $parseResponse = json_decode($response->body(), $this->getIsReturnArray()); 178 | 179 | return $parseResponse; 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # ZainCash Integration API Laravel Package 2 | 3 |

4 | 5 |

6 | 7 | This is a Laravel package to integrate ZainCash payment gateway API. For local financial transactions in Iraqi dinars inside Iraq. This package is based on the official ZainCash API documentation. You can find the official documentation [(https://docs.zaincash.iq)](https://docs.zaincash.iq). 8 | 9 |

10 | Total Downloads 11 | Latest Stable Version 12 | License 13 |

14 | 15 | ## 🎀 Requirements 16 | | Package Version | Laravel Version | PHP Version | 17 | |-----------------|-----------------|--------------| 18 | | 6.x | 6.x | 7.2+ | 19 | | 7.x | 7.x | 7.2.5+ | 20 | | 8.x | 8.x | 7.3+ \| 8.0+ | 21 | | 9.x | 9.x | 8.0+ | 22 | | 10.x | 10.x | 8.1+ | 23 | | 11.x | 11.x | 8.2+ | 24 | | 12.x | 12.x | 8.2+ | 25 | 26 | 27 | ## 📌 Installation 28 | ```bash 29 | composer require waad/zaincash 30 | ``` 31 | 32 | 33 | 34 | 49 | 50 |
51 | 52 | publish config file to `config/zaincash.php` 53 | ```bash 54 | php artisan vendor:publish --tag="zaincash" 55 | ``` 56 | 57 | update config zaincash in `config/zaincash.php` or from `.env` file 58 | 59 | 60 | | Key | Type | Default | Description | 61 | |-----|------|---------|-------------| 62 | | msisdn | string | 9647835077893 | The mobile phone number for your wallet, provided by Zain Cash. Example format: 9647835077893. | 63 | | merchant_id | string | 5ffacf6612b5777c6d44266f | The Merchant ID obtained from ZainCash. | 64 | | secret | string | $2y$10$h................ | The secret used to decode and encode JWT during requests. Obtain from ZainCash. | 65 | | test | bool | true | Specify the environment for using the ZainCash API. Set 'test' to false for the live environment after obtaining all credentials from ZainCash. | 66 | | test_url | string | https://test.zaincash.iq/ | URL for the ZainCash test environment. | 67 | | live_url | string | https://api.zaincash.iq/ | URL for the ZainCash live environment. | 68 | | language | string | ar | Set the language for the ZainCash payment page. Use 'ar' for Arabic or 'en' for English. | 69 | | prefix_order_id | string | wa3d_ | Prefix for the order ID. | 70 | | is_redirect | bool | false | Specify whether or not to redirect to the ZainCash payment page. If false, ZainCash returns a Transaction ID to the backend. If true, redirection after the request. | 71 | | min_amount | int | 250 | Set the minimum amount for a valid transaction in Iraqi Dinar (IQD). Transactions with amounts less than this value will be considered invalid. | 72 | | timeout | int | 10 | Set the timeout for the request in seconds. | 73 | | verify_ssl | bool | true | Set the verify SSL for the request to ZainCash's API. | 74 | 75 | 76 |
77 | 78 | Configurations to `.env` file 79 | ```env 80 | ZAINCASH_MSISDN=9647835077893 # Test Credentials 81 | ZAINCASH_MERCHANT_ID=5ffacf6612b5777c6d44266f # Test Credentials 82 | ZAINCASH_SECRET=$2y$10$h................ # Test Credentials 83 | ZAINCASH_TEST=true # default true 84 | ZAINCASH_PREFIX_ORDER_ID=wa3d_ # default wa3d_ 85 | ZAINCASH_LANGUAGE=ar # optional default ar 86 | ZAINCASH_IS_REDIRECT=false # optional default false 87 | ZAINCASH_MIN_AMOUNT=250 # optional default 250 88 | ZAINCASH_TEST_URL=https://test.zaincash.iq/ # optional 89 | ZAINCASH_LIVE_URL=https://api.zaincash.iq/ # optional 90 | ZAINCASH_TIMEOUT=10 # optional 91 | ZAINCASH_VERIFY_SSL=true # optional 92 | ``` 93 | 94 | 95 | ```bash 96 | php artisan optimize 97 | ``` 98 | 99 |
100 |
101 | 102 | 103 | ## 🍔 Usage 104 | 105 | use `ZainCash` Facade or Class example 106 | 107 |
108 |
109 | Facade 110 | 111 | ```php 112 | amount) 135 | ->setServiceType('Book') 136 | ->setOrderId(Str::random(36)) 137 | ->setIsTest(true) 138 | ->setIsReturnArray(true); 139 | 140 | return response()->json( 141 | $zainCashPayment->createTransaction() 142 | ); 143 | } 144 | } 145 | ``` 146 | 147 |
148 | 149 |
150 | Class 151 | 152 | ```php 153 | setAmount($request->amount) 176 | ->setServiceType('Book') 177 | ->setOrderId(Str::random(36)) 178 | ->setIsTest(true) 179 | ->setIsReturnArray(true); 180 | 181 | return response()->json( 182 | $zainCashPayment->createTransaction() 183 | ); 184 | } 185 | } 186 | ``` 187 | 188 |
189 |
190 | 191 |
192 | 193 | 194 | #### - `Getter` And `Setter` Attributes Table 195 | 196 | 197 | | Attribute |Important| Type | Getter | Setter | Default | 198 | |-------------------|--|------------------|---------------------------------------|----------------------------------------|----------| 199 | | amount |🟢| int-float-null | `getAmount()` | `setAmount($amount)` | - | 200 | | serviceType |🟢| string-null | `getServiceType()` | `setServiceType($serviceType)` | - | 201 | | orderId |🟢| string-int-float-null | `getOrderId()` | `setOrderId($orderId)` | - | 202 | | transactionID |🟢| string-null | `getTransactionID()` | `setTransactionID($transactionID)` | - | 203 | | isReturnArray |🟢| bool | `getIsReturnArray()` | `setIsReturnArray($isReturnArray)` | false | 204 | | minAmount |🔴| int-float-null | `getMinAmount()` | `setMinAmount($minAmount)` | - | 205 | | msisdn |🔴| string-null | `getMsisdn()` | `setMsisdn($msisdn)` | - | 206 | | secret |🔴| string-null | `getSecret()` | `setSecret($secret)` | - | 207 | | merchantId |🔴| string-null | `getMerchantId()` | `setMerchantId($merchantId)` | - | 208 | | isTest |🔴| bool-null | `getIsTest()` | `setIsTest($isTest)` | - | 209 | | language |🔴| string-null | `getLanguage()` | `setLanguage($language)` | - | 210 | | baseUrl |🔴| string-null | `getBaseUrl()` | `setBaseUrl($baseUrl)` | - | 211 | | isRedirect |🔴| bool | `getIsRedirect()` | `setIsRedirect($isRedirect)` | - | 212 | | tUrl |🔴| string-null | `getTUrl()` | `setTUrl($tUrl)` | - | 213 | | cUrl |🔴| string-null | `getCUrl()` | `setCUrl($cUrl)` | - | 214 | | rUrl |🔴| string-null | `getRUrl()` | `setRUrl($rUrl)` | - | 215 | | processingUrl |🔴| string-null | `getProcessingUrl()` | `setProcessingUrl($processingUrl)` | - | 216 | | processingOtpUrl |🔴| string-null | `getProcessingOtpUrl()` | `setProcessingOtpUrl($processingOtpUrl)` | - | 217 | | cancelUrl |🔴| string-null | `getCancelUrl()` | `setCancelUrl($cancelUrl)` | - | 218 | | timeout |🔴| int-null | `getTimeout()` | `setTimeout($timeout)` | - | 219 | | verifySsl |🔴| bool-null | `getVerifySsl()` | `setVerifySsl($verifySsl)` | - | 220 | 221 | ⚠️ `Important` column means that this attribute is constantly used and has no default value. On the contrary, we can change it, but it will take the default value from `config/zaincash.php`. 222 | 223 | 224 |
225 | 226 | 227 | ### - **Steps from create a transaction To Complete Payment** 228 | 229 | #### **Step 1 - Create a transaction** 230 | 231 | ```php 232 | $zainCashPayment = ZainCash::make() 233 | ->setAmount($request->amount) 234 | ->setServiceType('Book') 235 | ->setOrderId(Str::random(36)) 236 | ->setIsTest(true); 237 | ``` 238 | 239 | - `setAmount($amount)` : Set the amount of the transaction in Iraqi Dinar (IQD). The amount must be greater than or equal to the minimum amount specified in the configuration file. 240 | - `setServiceType($serviceType)` : Set the service type for the transaction. The service type must be one of the following: `Book`, `Food`, `Grocery`, `Pharmacy`, `Transportation`, `Other`. 241 | - `setOrderId($orderId)` : Set the order ID for the transaction. The order ID must be unique for each transaction. 242 | - `setIsTest($isTest)` : Set the environment for using the ZainCash API. Set `true` for the test environment and `false` for the live environment. 243 | - `setIsReturnArray(bool)` : Set the return type for the transaction. Set `true` to return an **array** and `false` to return an **object stdClass**. The default value is `false`. 244 | - `createTransaction()` : Create a transaction and return the transaction Details (**array** or **object stdClass**). 245 | 246 |
247 | 248 | ```php 249 | $transaction = $zainCashPayment->createTransaction(); 250 | ``` 251 |
252 | 253 | Response example 254 | ```json 255 | { 256 | "source": "web", 257 | "type": "MERCHANT_PAYMENT", 258 | "amount": "1000", 259 | "to": "5ffacf6612b5777c6d44266f", 260 | "serviceType": "Book", 261 | "lang": "ar", 262 | "orderId": "wa3d_Q9IpdkNw7EVypwLRuE2PDDoVLA4FPhAjhlyO", 263 | "currencyConversion": {}, 264 | "referenceNumber": "RGUR9Q", 265 | "credit": false, 266 | "status": "pending", 267 | "reversed": false, 268 | "createdAt": "2023-11-18T08:24:32.467Z", 269 | "updatedAt": "2023-11-18T08:24:32.467Z", 270 | "id": "655874c00227c4d2ec58f710" 271 | } 272 | ``` 273 | 274 | ```php 275 | // if return array use ->setIsReturnArray(true); 276 | 277 | $transactionId = $transaction['id']; 278 | 279 | // if return object use ->setIsReturnArray(false); -- default 280 | $transactionId = $transaction->id; 281 | ``` 282 | 283 |
284 |
285 | 286 | 287 | #### **Step 2 - Check a transaction** 288 | 289 | ```php 290 | $zainCashPayment = ZainCash::make() 291 | ->setTransactionID($transactionID) 292 | ->setIsReturnArray(true); 293 | ``` 294 | 295 | - `setTransactionID($transactionID)` : Set the transaction ID for the transaction. 296 | - `setIsReturnArray(bool)` : Set the return type for the transaction. Set `true` to return an **array** and `false` to return an **object stdClass**. The default value is `false`. 297 | - `checkTransaction()` : Check the transaction and return the transaction details (**array** or **stdClass**). 298 | 299 |
300 | 301 | ```php 302 | $transactionDetails = $zainCashPayment->checkTransaction(); 303 | ``` 304 |
305 | 306 | Response Example dependent by status: 307 | 308 | ```json 309 | { 310 | "to": { 311 | "name": "Karrar", 312 | "msisdn": "9647835077893", 313 | "currency": "IQD", 314 | "deleted": false, 315 | "pay_by_reference": "1", 316 | "createdAt": "2021-01-10T09:56:54.180Z", 317 | "updatedAt": "2021-12-22T13:01:02.531Z", 318 | "id": "5ffacf6612b5777c6d44266f" 319 | }, 320 | "source": "web", 321 | "type": "MERCHANT_PAYMENT", 322 | "amount": "1000", 323 | "serviceType": "Book", 324 | "lang": "ar", 325 | "orderId": "wa3d_2eTDlz8umPE3DwtocL5O8Xpe10yLH4pepci2", 326 | "currencyConversion": [], 327 | "referenceNumber": "MDJN1I", 328 | "credit": false, 329 | "status": "pending", // pending, pending_otp, completed, failed, cancel 330 | "reversed": false, 331 | "createdAt": "2023-11-18T11:37:16.574Z", 332 | "updatedAt": "2023-11-18T11:37:16.574Z", 333 | "id": "6558a1ec0227c4d2ec58f717" 334 | } 335 | ***************************************** 336 | { 337 | "to": { 338 | .... 339 | }, 340 | ... 341 | "status": "pending_otp", // <--- status pending_otp after processing 342 | "sofOwnerId": 18482, 343 | "traveldiscount": "10000", 344 | "from": "9647802999569", 345 | "onCustomerFees": "50000000", 346 | "onMerchantFees": "0", 347 | "totalFees": 500, 348 | ... 349 | } 350 | ***************************************** 351 | { 352 | "to": { 353 | ... 354 | }, 355 | ... 356 | "status": "completed", // <--- status completed after complete (Payment) 357 | "sofOwnerId": 18482, 358 | "traveldiscount": "1000", 359 | "from": "9647802999569", 360 | "onCustomerFees": "50000000", 361 | "onMerchantFees": "0", 362 | "totalFees": 500, 363 | "operationDate": "2023-11-22T18:23:52.270Z", 364 | "operationId": "1173300", 365 | ... 366 | } 367 | ***************************************** 368 | { 369 | "to": { 370 | ... 371 | }, 372 | ... 373 | "status": "failed", // <--- status failed that mean there is (expiration or a wrong) 374 | "sofOwnerId": 18482, 375 | "traveldiscount": "9900", 376 | "from": "9647802999569", 377 | "onCustomerFees": "50000000", 378 | "onMerchantFees": "0", 379 | "totalFees": 500, 380 | "due": "Not enough credit on balance", 381 | ... 382 | } 383 | ***************************************** 384 | { 385 | "to": { 386 | ... 387 | }, 388 | ... 389 | "status": "cancel", // <--- status cancel it mean the user cancel the transaction 390 | "sofOwnerId": 18482, 391 | "traveldiscount": "10000", 392 | "from": "9647802999569", 393 | "onCustomerFees": "50000000", 394 | "onMerchantFees": "0", 395 | "totalFees": 500, 396 | "due": "transaction_already_submitted", 397 | ... 398 | } 399 | ``` 400 | 401 | 402 | ```php 403 | // if return array use ->setIsReturnArray(true); 404 | 405 | $status = $transactionDetails['status']; 406 | $name = $transactionDetails['to']['name']; 407 | 408 | // if return object use ->setIsReturnArray(false); -- default 409 | $status = $transactionDetails->status; 410 | $name = $transactionDetails->to->name; 411 | ``` 412 | 413 |
414 |
415 | 416 | 417 | #### **Step 3 - Processing a transaction** 418 | 419 | ```php 420 | $zainCashPayment = ZainCash::make() 421 | ->setTransactionID($transactionID) 422 | ->setIsReturnArray(true); 423 | ``` 424 | 425 | - `setTransactionID($transactionID)` : Set the transaction ID for the transaction. 426 | - `setIsReturnArray(bool)` : Set the return type for the transaction. Set `true` to return an **array** and `false` to return an **object stdClass**. The default value is `false`. 427 | - `processingTransaction($phonenumber, $pin)` : Processing the transaction and return the transaction details (**array** or **object stdClass**). 428 | 429 |
430 | 431 | ```php 432 | $processingDetails = $zainCashPayment->processingTransaction("9647802999569", '1234'); 433 | ``` 434 | 435 |
436 | 437 | Response Example dependent by success: 438 | 439 | ```json 440 | { 441 | "success": 1, 442 | "transactionid": "655883cd0227c4d2ec58f712", 443 | "initialAmount": "1000", 444 | "totalFees": 500, 445 | "discount": "1000", 446 | "total": 1500, 447 | "onCustomerFees": "50000000", 448 | "onMerchantFees": "0" 449 | } 450 | ******************************************** 451 | { 452 | "success": 0, 453 | "error": "العملية قد قدمت من قبل" 454 | } 455 | ******************************************** 456 | { 457 | "success": 0, 458 | "error": "رقم المحفظة-الهاتف أو الرمز السري غير صحيح" 459 | } 460 | ``` 461 | 462 | ```php 463 | // if return array use ->setIsReturnArray(true); 464 | 465 | $success = $processingDetails['success']; 466 | 467 | // if return object use ->setIsReturnArray(false); -- default 468 | $success = $processingDetails->success; 469 | ``` 470 | 471 |
472 |
473 | 474 | 475 | #### **Step 4 - Complete a transaction** 476 | 477 | ```php 478 | $zainCashPayment = ZainCash::make() 479 | ->setTransactionID($transactionID) 480 | ->setIsReturnArray(true); 481 | ``` 482 | 483 | - `setTransactionID($transactionID)` : Set the transaction ID for the transaction. 484 | - `setIsReturnArray(bool)` : Set the return type for the transaction. Set `true` to return an **array** and `false` to return an **object stdClass**. The default value is `false`. 485 | - `payTransaction($phonenumber, $pin, $otp)` : Complete pay the transaction and return the transaction details (**array** or **object stdClass**). 486 | 487 | 488 |
489 | 490 | ```php 491 | $payDetails = $zainCashPayment->payTransaction("9647802999569", '1234', '1111'); 492 | ``` 493 | 494 |
495 | 496 | Response Example dependent by success: 497 | 498 | ```json 499 | { 500 | "success": 1, 501 | "msg": "succesful_transaction" 502 | } 503 | ******************************************** 504 | { 505 | "success": 0, 506 | "msg": "You have entered an incorrect OTP. In the next try, please enter the correct PIN delivered to your mobile by SMS " 507 | } 508 | ******************************************** 509 | { 510 | "success": 0, 511 | "msg": "Not enough credit on balance" 512 | } 513 | ``` 514 | 515 | ```php 516 | // if return array use ->setIsReturnArray(true); 517 | 518 | $success = $payDetails['success']; 519 | 520 | // if return object use ->setIsReturnArray(false); -- default 521 | $success = $payDetails->success; 522 | ``` 523 | 524 |
525 |
526 | 527 | #### **Step 5 - Cancel a transaction** 528 | 529 | ```php 530 | $zainCashPayment = ZainCash::make() 531 | ->setTransactionID($transactionID) 532 | ->setIsReturnArray(true); 533 | ``` 534 | 535 | - `setTransactionID($transactionID)` : Set the transaction ID for the transaction. 536 | - `setIsReturnArray(bool)` : Set the return type for the transaction. Set `true` to return an **array** and `false` to return an **object stdClass**. The default value is `false`. 537 | - `cancelTransaction()` : Cancel the transaction and return the transaction details (**array** or **object stdClass**). 538 | 539 |
540 | 541 | ```php 542 | $cancelDetails = $zainCashPayment->cancelTransaction(); 543 | ``` 544 | 545 |
546 | 547 | Response Example dependent by success: 548 | 549 | ```json 550 | { 551 | "success":0, 552 | "msg":"لقد قمت بالغاء العملية" 553 | } 554 | ************************************ 555 | { 556 | "success":0, 557 | "msg":"العملية قد قدمت من قبل" 558 | } 559 | ``` 560 | 561 | ```php 562 | // if return array use ->setIsReturnArray(true); 563 | 564 | $success = $cancelDetails['success']; 565 | 566 | // if return object use ->setIsReturnArray(false); -- default 567 | $success = $cancelDetails->success; 568 | ``` 569 | 570 |
571 |
572 | 573 | 574 | 575 | # 🧔 Author 576 | 577 |
578 |

579 | 580 |

581 |

582 | Author: Waad Mawlood 583 |

584 |

585 | Email: waad_mawlood@outlook.com 586 |

587 |
588 | 589 | # ⚖️ License 590 | The MIT License (MIT). Please see [MIT license](https://opensource.org/licenses/MIT) for more information. 591 | --------------------------------------------------------------------------------