├── src ├── Exceptions │ ├── GatewayCodeException.php │ └── GatewayAddressException.php ├── Gateways │ ├── IPaymentStatus.php │ ├── Senagat.php │ ├── AltynAsyr.php │ ├── IRegistrationResult.php │ ├── PaymentStatus.php │ ├── Rysgalbank.php │ ├── RegistrationResult.php │ └── AbstractGateway.php ├── Resources │ └── lang │ │ ├── en │ │ └── app.php │ │ ├── ru │ │ └── app.php │ │ └── tm │ │ └── app.php ├── App │ ├── GatewayFactory.php │ └── GatewayManager.php ├── Facades │ └── GatewayFacade.php ├── Providers │ └── GatewayServiceProvider.php └── Config │ └── gateway.php ├── composer.json ├── docs └── uml │ └── ClassDiagram.puml └── README.md /src/Exceptions/GatewayCodeException.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'title' => 'Altyn Asyr', 6 | ], 7 | 'rysgal' =>[ 8 | 'title' => 'Rysgalbank' 9 | ], 10 | 'senagat' => [ 11 | 'title' => 'Senagat bank' 12 | ] 13 | ]; 14 | -------------------------------------------------------------------------------- /src/Resources/lang/ru/app.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'title' => 'Altyn Asyr', 6 | ], 7 | 'rysgal' =>[ 8 | 'title' => 'Rysgalbank' 9 | ], 10 | 'senagat' => [ 11 | 'title' => 'Senagat bank' 12 | ] 13 | ]; 14 | -------------------------------------------------------------------------------- /src/Resources/lang/tm/app.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'title' => 'Altyn Asyr', 6 | ], 7 | 'rysgal' =>[ 8 | 'title' => 'Rysgalbank' 9 | ], 10 | 'senagat' => [ 11 | 'title' => 'Senagat bank' 12 | ] 13 | ]; 14 | -------------------------------------------------------------------------------- /src/Gateways/Senagat.php: -------------------------------------------------------------------------------- 1 | success; 15 | } 16 | 17 | function getMessage(): string 18 | { 19 | return $this->message; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Gateways/Rysgalbank.php: -------------------------------------------------------------------------------- 1 | publishes([ 20 | __DIR__.'/../Config/gateway.php' => config_path('gateway.php'), 21 | ],'gateway'); 22 | 23 | $this->loadTranslationsFrom(__DIR__ . '/../Resources/lang', 'gateway'); 24 | } 25 | 26 | /** 27 | * Register services. 28 | * 29 | * @return void 30 | */ 31 | public function register(){ 32 | $loader = AliasLoader::getInstance(); 33 | $loader->alias('gateway', GatewayFacade::class); 34 | 35 | $this->app->singleton('gateway', function () { 36 | return new GatewayManager(); 37 | }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Gateways/RegistrationResult.php: -------------------------------------------------------------------------------- 1 | success; 32 | } 33 | 34 | function getMessage(): string 35 | { 36 | return $this->message; 37 | } 38 | 39 | function getRedirectUrl(): string 40 | { 41 | return $this->url; 42 | } 43 | 44 | function getOrderId(): string 45 | { 46 | return $this->orderId; 47 | } 48 | 49 | public function setErrorMessage(string $errorMessage) : void 50 | { 51 | $this->message = $errorMessage; 52 | } 53 | 54 | public function setOrderId(string $orderId) : void 55 | { 56 | $this->orderId = $orderId; 57 | } 58 | 59 | public function setFormUrl(string $formUrl) : void 60 | { 61 | $this->url = $formUrl; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "merdan/gateway-tm", 3 | "description": "A simple library that provides integration to local Bank payment processing services in Turkmenistan.", 4 | "type": "library", 5 | "require": { 6 | "php": "^8.1", 7 | "ext-json": "*", 8 | "illuminate/support": "^9.0|^10.0", 9 | "illuminate/translation": "^9.0|^10.0" 10 | }, 11 | "require-dev": { 12 | "orchestra/testbench": "*", 13 | "phpunit/phpunit": "^9.4" 14 | }, 15 | "license": "MIT", 16 | "authors": [ 17 | { 18 | "name": "Merdan Ahmetovich", 19 | "email": "merdan.m@gmail.com", 20 | "role": "Developer" 21 | } 22 | ], 23 | "keywords": [ 24 | "laravel payment", 25 | "laravel gateway", 26 | "altyn asyr", 27 | "rysgal bank", 28 | "senagat", 29 | "turkmen bank" 30 | ], 31 | "autoload": { 32 | "psr-4": { 33 | "Merdanio\\GatewayTM\\Payment\\": "src/" 34 | } 35 | }, 36 | "autoload-dev": { 37 | "psr-4": { 38 | "Merdanio\\GatewayTM\\Payment\\": "tests/" 39 | } 40 | }, 41 | "extra": { 42 | "laravel": { 43 | "providers": [ 44 | "Merdanio\\GatewayTM\\Payment\\Providers\\GatewayServiceProvider" 45 | ], 46 | "aliases": { 47 | "Gateway": "Merdanio\\GatewayTM\\Payment\\Facades\\GatewayFacade" 48 | } 49 | } 50 | }, 51 | "minimum-stability": "alpha" 52 | } 53 | -------------------------------------------------------------------------------- /docs/uml/ClassDiagram.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'https://plantuml.com/class-diagram 3 | skin rose 4 | 5 | title Classes - Class Diagram 6 | 7 | abstract class AbstractGateway{ 8 | #string code 9 | +string getCode() 10 | +string getTitle() 11 | +string getOrderId() 12 | +mixed getConfigData(string field) 13 | +string getRegistrationUrl() 14 | +string getStatusURL() 15 | +string apiURL() 16 | +void setAmount() 17 | +void setDescription(string description) 18 | +array orderParams(string $success_route, string $fail_route) 19 | 20 | } 21 | interface IPaymentStatus{ 22 | +getStatus(): bool 23 | +getMessage(): string 24 | } 25 | interface IRegistrationResult{ 26 | +isSuccessful() : bool 27 | +getMessage() : string 28 | +getRedirectUrl() : string 29 | +getOrderId() : string 30 | } 31 | class AltynAsyr{ 32 | #string code 33 | } 34 | class Senagat 35 | { 36 | #string code 37 | } 38 | class Rysgalbank 39 | { 40 | #string code 41 | } 42 | class GatewayManager{ 43 | +availableGates() : array 44 | +registerOrder() : IRegistrationResult 45 | +getOrderStatus() : IPaymentStatus 46 | } 47 | class GatewayFactory{ 48 | + create(string $code, string $order_id) : AbstractGateway 49 | } 50 | IPaymentStatus <|.. PaymentStatus 51 | IRegistrationResult <|.. RegistrationResult 52 | GatewayFactory - AbstractGateway : creates > 53 | AbstractGateway <|- AltynAsyr 54 | AbstractGateway <|- Senagat 55 | AbstractGateway <|- Rysgalbank 56 | GatewayManager ..> IRegistrationResult 57 | GatewayManager ..> IPaymentStatus 58 | AbstractGateway ..> PaymentStatus 59 | AbstractGateway ..> RegistrationResult 60 | GatewayManager ..> AbstractGateway 61 | GatewayManager ..> GatewayFactory 62 | 63 | @enduml 64 | -------------------------------------------------------------------------------- /src/Config/gateway.php: -------------------------------------------------------------------------------- 1 | [ 4 | 5 | /* 6 | * maximum waiting time for connection establishment with payment services 7 | */ 8 | "connect_timeout" => 60,//sec 9 | 10 | /* 11 | * maximum waiting time for response from payment services 12 | */ 13 | "timeout" => 60,//sec, 14 | 15 | /* 16 | * Determine if the ssl verification should be enabled. 17 | */ 18 | "verify" => true, 19 | ], 20 | /* 21 | * Payment Services 22 | */ 23 | "clients" => [ 24 | "altyn-asyr" => [ 25 | "code" => "altyn-asyr", 26 | "title" => "gateway::app.altyn-asyr.title", 27 | "class" => \Merdanio\GatewayTM\Payment\Gateways\AltynAsyr::class, 28 | "active" => true, 29 | "user" => env('ALTYN_ASYR_USER'), 30 | "password" => env('ALTYN_ASYR_PASSWORD'), 31 | "api" => env('ALTYN_ASYR_API'), 32 | "order_uri" => env('ALTYN_ASYR_ORDER_URI','register.do'), 33 | "status_uri" => env('ALTYN_ASYR_STATUS_URI','getOrderStatus.do') 34 | ], 35 | "rysgal" => [ 36 | "code" => "rysgal", 37 | "title" => "gateway::app.rysgal.title", 38 | "class" => \Merdanio\GatewayTM\Payment\Gateways\Rysgalbank::class, 39 | "active" => true, 40 | "user" => env('RYSGAL_USER'), 41 | "password" => env('RYSGAL_PASSWORD'), 42 | "api" => env('RYSGAL_API'), 43 | "order_uri" => env('RYSGAL_ORDER_URI','register.do'), 44 | "status_uri" => env('RYSGAL_STATUS_URI','getOrderStatus.do') 45 | ], 46 | "senagat" => [ 47 | "code" => "senagat", 48 | "title" => "gateway::app.senagat.title", 49 | "class" => \Merdanio\GatewayTM\Payment\Gateways\Senagat::class, 50 | "active" => true, 51 | "user" => env('SENAGAT_USER'), 52 | "password" => env('SENAGAT_PASSWORD'), 53 | "api" => env('SENAGAT_API'), 54 | "order_uri" => env('SENAGAT_ORDER_URI','register.do'), 55 | "status_uri" => env('SENAGAT_STATUS_URI','getOrderStatus.do') 56 | ] 57 | ] 58 | ]; 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gateway-TM 2 | 3 | A simple library that provides integration to local Bank payment processing services in Turkmenistan. 4 | 5 | ## Introduction 6 | 7 | Gateway-TM offers seamless integration with three primary payment services: Rysgal, AltynAsyr, and Senagat. This library facilitates the process of registering payment orders and checking their status. It has been designed to be easily extendable, allowing users to incorporate additional gateway services by extending the AbstractGateway class and implementing their custom class. This ensures a high level of flexibility in integrating new payment service types. 8 | ## Requirements 9 | 10 | - Laravel 9 or higher 11 | - PHP 8.1 or higher 12 | 13 | ## Installation & Instructions 14 | Default installation is via [Composer](https://getcomposer.org/). 15 | 16 | ```bash 17 | composer require merdan/gateway-tm 18 | ``` 19 | The package will automatically register itself. 20 | Add the Service Provider and alias manually to your 21 | `config/app` file in the `providers` section. 22 | 23 | ```php 24 | 'providers' => [ 25 | //... 26 | Merdanio\GatewayTM\Payment\Providers\GatewayServiceProvider::class, 27 | ] 28 | ``` 29 | ```php 30 | 'aliases' => Facade::defaultAliases()->merge([ 31 | // 'Example' => App\Facades\Example::class, 32 | 'Gateway' => Merdanio\GatewayTM\Payment\Facades\GatewayFacade::class 33 | ])->toArray(), 34 | ``` 35 | Publish the config 36 | 37 | ```bash 38 | php artisan vendor:publish --tag="gateway" 39 | ``` 40 | 41 | Please configure the following credentials in your .env file, which you will obtain from the bank. 42 | 43 | `ALTYN_ASYR_USER=` 44 | `ALTYN_ASYR_PASSWORD=` 45 | `ALTYN_ASYR_API=` 46 | `ALTYN_ASYR_ORDER_URI='register.do'` 47 | `ALTYN_ASYR_STATUS_URI='orderStatus.do'` 48 | 49 | `RYSGAL_USER=` 50 | `RYSGAL_PASSWORD=` 51 | `RYSGAL_API=` 52 | `RYSGAL_ORDER_URI='register.do'` 53 | `RYSGAL_STATUS_URI=` 54 | 55 | `SENAGAT_USER=` 56 | `SENAGAT_PASSWORD=` 57 | `SENAGAT_API=` 58 | `SENAGAT_ORDER_URI='register.do'` 59 | `SENAGAT_STATUS_URI='orderStatus.do'` 60 | 61 | ## Usage 62 | Add Gateway facade to your class, controller 63 | ```php 64 | use Gateway; 65 | 66 | class MyController extends Controller { 67 | 68 | public function index() { 69 | return Gateway::availableGates(); 70 | } 71 | } 72 | ``` 73 | ### Available payment providers 74 | 75 | ```php 76 | Gateway::availableGates(); 77 | ``` 78 | ### Register order 79 | ```php 80 | Gateway::registerOrder('rysgal', // providers code 81 | 'success_route_name', // route to return when payment is successful 82 | 'fail_route_name', // route to return when payment failed 83 | 14500, // payment amount 145 man. 84 | 'Example Ecommerse payment', // payment description 85 | 'ord-123' // payment order number 86 | ); 87 | ``` 88 | ### Check payment status 89 | ```php 90 | Gateway::getOrderStatus( 91 | 'rysgal', //providers code 92 | 'ord-123' //order number 93 | ); 94 | ``` 95 | 96 | ## License 97 | Is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT). 98 | -------------------------------------------------------------------------------- /src/App/GatewayManager.php: -------------------------------------------------------------------------------- 1 | $gatewayMethod['code'], 30 | 'title' => trans($gatewayMethod['title']), 31 | ]; 32 | } 33 | return $gateways; 34 | } 35 | 36 | /** 37 | * Registers Payment order to bank gateway 38 | * 39 | * @param string $code 40 | * @param int $amount 41 | * @param string $description 42 | * @param string $orderId 43 | * @return IRegistrationResult 44 | */ 45 | public function registerOrder(string $code, 46 | string $success_route_name, 47 | string $failure_route_name, 48 | int $amount, 49 | string $description, 50 | string $orderId) : IRegistrationResult 51 | { 52 | $gatewayClient = GatewayFactory::create($code,$orderId); 53 | 54 | $gatewayClient->setAmount($amount); 55 | $gatewayClient->setDescription($description); 56 | 57 | try { 58 | $response = Http::retry(3,300) 59 | ->asForm() 60 | ->post( 61 | $gatewayClient->getRegistrationURL(), 62 | $gatewayClient->orderParams($success_route_name,$failure_route_name) 63 | ); 64 | 65 | if($response->failed()){ 66 | $ex = $response->toException(); 67 | 68 | return new RegistrationResult(false, $ex->getMessage()); 69 | } 70 | 71 | return $gatewayClient->registrationResult($response->json()); 72 | }catch (\Exception $e){ 73 | return new RegistrationResult(false, $e->getMessage()); 74 | } 75 | 76 | } 77 | 78 | private function fail($e){ 79 | 80 | } 81 | 82 | /** 83 | * Get payment status of order from gateway 84 | * 85 | * @param string $code 86 | * @param string $orderId 87 | */ 88 | public function getOrderStatus(string $code, string $orderId) : IPaymentStatus 89 | { 90 | $gatewayClient = GatewayFactory::create($code,$orderId); 91 | 92 | try{ 93 | $response = Http::retry(3,300) 94 | ->asForm() 95 | ->post( 96 | $gatewayClient->getStatusURL(), 97 | $gatewayClient->statusParams($orderId) 98 | ); 99 | 100 | if($response->failed()){ 101 | $ex = $response->toException(); 102 | 103 | return new PaymentStatus(false, $ex->getMessage()); 104 | } 105 | 106 | return $gatewayClient->paymentStatus($response->json()); 107 | } 108 | catch (\Exception $e){ 109 | return new PaymentStatus(false, $e->getMessage()); 110 | } 111 | 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/Gateways/AbstractGateway.php: -------------------------------------------------------------------------------- 1 | code)) { 31 | throw new GatewayCodeException('Gateway code should be initialized.'); 32 | } 33 | 34 | return $this->code; 35 | } 36 | 37 | /** 38 | * Returns gateway title. 39 | * 40 | * @return string 41 | */ 42 | public function getTitle() : string 43 | { 44 | return $this->getConfigData('title'); 45 | } 46 | 47 | /** 48 | * Payment order id 49 | * 50 | * @return string 51 | */ 52 | public function getOrderId() : string 53 | { 54 | return $this->order_id; 55 | } 56 | 57 | /** 58 | * Retrieve information from gateway configuration. 59 | * 60 | * @param string $field 61 | * @return mixed 62 | */ 63 | public function getConfigData(string $field):mixed 64 | { 65 | return config('gateway.clients.' . $this->getCode() . '.' . $field); 66 | } 67 | 68 | /** 69 | * Payment order registration address 70 | * 71 | * @return string 72 | * @throws GatewayAddressException 73 | * @throws GatewayCodeException 74 | */ 75 | public function getRegistrationURL() : string 76 | { 77 | if( $order_uri = config('gateway.clients.' . $this->getCode() . '.order_uri' )){ 78 | return $this->apiURL().$order_uri; 79 | } 80 | 81 | throw new GatewayAddressException(); 82 | } 83 | 84 | /** 85 | * Payment order status address 86 | * 87 | * @return string 88 | * @throws GatewayAddressException 89 | * @throws GatewayCodeException 90 | */ 91 | public function getStatusURL() : string 92 | { 93 | if( $status_uri = config('gateway.clients.' . $this->getCode() . '.status_uri' )){ 94 | return $this->apiURL().$status_uri; 95 | } 96 | 97 | throw new GatewayAddressException(); 98 | } 99 | 100 | /** 101 | * Payment service API address 102 | * 103 | * @return string 104 | * @throws GatewayAddressException 105 | * @throws GatewayCodeException 106 | */ 107 | public function apiURL() : string 108 | { 109 | if($api = config('gateway.clients.' . $this->getCode() . '.api' )){ 110 | return $api; 111 | } 112 | 113 | throw new GatewayAddressException(); 114 | } 115 | 116 | /** 117 | * Sets Payment amount (tenge) 118 | * 119 | * @param int $amount 120 | */ 121 | public function setAmount(int $amount) : void 122 | { 123 | $this->amount = $amount; 124 | } 125 | 126 | /** 127 | * Sets order description 128 | * 129 | * @param string $description 130 | */ 131 | public function setDescription(string $description) : void 132 | { 133 | $this->description = $description; 134 | } 135 | 136 | /** 137 | * Params to be sent to gateway, to register order 138 | * 139 | * @param string $orderId 140 | * @return array[] 141 | */ 142 | public function orderParams(string $success_route, string $fail_route) : array 143 | { 144 | return [ 145 | 'userName' => $this->getConfigData('user'), 146 | 'password' => $this->getConfigData('password'), 147 | 'sessionTimeoutSecs' => config('gateway.http_client.timeout'), 148 | 'orderNumber' =>$this->order_id, 149 | 'currency' => 934, 150 | 'language' => 'ru', 151 | 'description'=> $this->description, 152 | 'amount' => $this->amount,// amount w kopeykah 153 | 'returnUrl' => route($success_route,['orderId' => $this->order_id]), 154 | 'failUrl' => route($fail_route,['orderId'=>$this->order_id]) 155 | ]; 156 | } 157 | 158 | /** 159 | * Params to be sent to gateway, to retrieve payment status 160 | * @param string $orderId 161 | * @return array[] 162 | */ 163 | public function statusParams(string $orderId) : array 164 | { 165 | return [ 166 | 'form_params' => [ 167 | 'userName' => $this->getConfigData('user'), 168 | 'password' => $this->getConfigData('password'), 169 | 'orderId' => $orderId, 170 | ] 171 | ]; 172 | } 173 | 174 | /** 175 | * Convert order registration response to RegistrationResult 176 | * @param array $data 177 | * @return IRegistrationResult 178 | */ 179 | public function registrationResult(array $data): IRegistrationResult 180 | { 181 | $result = new RegistrationResult(isset($data['orderId']) && isset($data['formUrl'])); 182 | 183 | if($result->isSuccessful()){ 184 | $result->setOrderId($data['orderId']); 185 | $result->setFormUrl($data['formUrl']); 186 | } 187 | elseif(isset($data['errorMessage'])){ 188 | $result->setErrorMessage($data['errorMessage']); 189 | } 190 | 191 | return $result; 192 | } 193 | 194 | public function paymentStatus(array $data) : IPaymentStatus 195 | { 196 | $status = $data['ErrorCode'] == 0 && isset($data['OrderStatus']) && $data['OrderStatus'] == 2; 197 | 198 | $result = new PaymentStatus($status,$data['ErrorMessage']); 199 | 200 | return $result; 201 | } 202 | } 203 | --------------------------------------------------------------------------------