├── .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 |
579 |
580 |
582 | Author: Waad Mawlood 583 |
584 |585 | Email: waad_mawlood@outlook.com 586 |
587 |