├── .gitignore ├── LICENSE ├── README.md ├── composer.json └── src ├── Abstracts └── AbstractService.php ├── Facade └── TapPayment.php ├── Publishing └── config.php ├── Resources └── Invoice.php ├── Services └── Charge.php ├── TapPaymentServiceProvider.php └── TapService.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | node_modules/ 3 | npm-debug.log 4 | 5 | # Laravel 4 specific 6 | bootstrap/compiled.php 7 | app/storage/ 8 | 9 | # Laravel 5 & Lumen specific 10 | public/storage 11 | public/hot 12 | storage/*.key 13 | .env.*.php 14 | .env.php 15 | .env 16 | Homestead.yaml 17 | Homestead.json 18 | 19 | # Rocketeer PHP task runner and deployment package. https://github.com/rocketeers/rocketeer 20 | .rocketeer/ 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 VM Development 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tap Payment package for Laravel 2 | 3 | Laravel package for https://www.tap.company 4 | 5 | ## Installation 6 | 7 | Add `vmdevelopment/laravel-tap-payments` to your `composer.json`. 8 | ``` 9 | "vmdevelopment/laravel-tap-payments": "dev-master" 10 | ``` 11 | 12 | Run `composer update` to pull down the latest version of package. 13 | 14 | OR simply run 15 | ``` 16 | composer require "vmdevelopment/laravel-tap-payments" 17 | ``` 18 | 19 | For laravel >=5.5 that's all. This package supports Laravel new Package Discovery. 20 | 21 | Otherwise you need to open up `/config/app.php` and add the service provider to your `providers` array. 22 | ```php 23 | 'providers' => [ 24 | \VMdevelopment\TapPayment\TapPaymentServiceProvider::class 25 | ] 26 | ``` 27 | 28 | Now add the alias. 29 | ```php 30 | 'aliases' => [ 31 | 'TapPayment' => \VMdevelopment\TapPayment\Facade\TapPayment::class 32 | ] 33 | ``` 34 | 35 | ## Configuration 36 | To publish config run 37 | ``` 38 | php artisan vendor:publish --provider="VMdevelopment\TapPayment\TapPaymentServiceProvider" 39 | ``` 40 | and modify the config file with your own information. File is located in `/config/tap-payment.php` 41 | 42 | you can use this environment variables in your `.env` file 43 | ``` 44 | TAP_PAYMENT_API_KEY=your_api_key 45 | ``` 46 | 47 | ## Current version Functions 48 | 49 | * `TapPayment::createCharge()` - Creating an ApiInvoice 50 | * `TapPayment::findCharge($id)` - Finding an ApiInvoice by ID 51 | 52 | ## Usage example 53 | 54 | ### Creating Charge(make payment) 55 | ```php 56 | use VMdevelopment\TapPayment\Facade\TapPayment; 57 | 58 | public function pay() 59 | { 60 | try{ 61 | 62 | $payment = TapPayment::createCharge(); 63 | 64 | $payment->setCustomerName( "John Doe" ); 65 | 66 | $payment->setCustomerPhone( "123456789" ); 67 | 68 | $payment->setDescription( "Some description" ); 69 | 70 | $payment->setAmount( 123 ); 71 | 72 | $payment->setCurrency( "KWD" ); 73 | 74 | $payment->setSource( "src_kw.knet" ); 75 | 76 | $payment->setRedirectUrl( "https://example.com" ); 77 | 78 | $payment->setPostUrl( "https://example.com" ); // if you are using post request to handle payment updates 79 | 80 | $payment->setMetaData( [ 'package' => json_encode( $package ) ] ); // if you want to send metadata 81 | 82 | $invoice = $payment->pay(); 83 | 84 | } catch( \Exception $exception ){ 85 | // your handling of request failure 86 | } 87 | 88 | $payment->isSuccess() // check if TapPayment has successfully handled request. 89 | } 90 | ``` 91 | ### Find ApiInvoice 92 | ```php 93 | public function check( $id ) 94 | { 95 | try{ 96 | 97 | $invoice = TapPayment::findCharge( $id );; 98 | 99 | } catch( \Exception $exception ){ 100 | // your handling of request failure 101 | } 102 | $invoice->checkHash($request->header('Hashstring')); // check hashstring to make sure that request comes from Tap 103 | $invoice->isSuccess(); // check if invoice is paid 104 | $invoice->isInitiated(); // check if invoice is unpaid yet 105 | } 106 | ``` -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vmdevelopment/laravel-tap-payments", 3 | "description": "Tap Payment REST API package for Laravel", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Vanand Mkrtchyan", 8 | "email": "vanand@vm-development.com", 9 | "homepage": "http://www.vm-development.com/" 10 | } 11 | ], 12 | "minimum-stability": "dev", 13 | "require": { 14 | "php": "^7.1", 15 | "ext-curl": "*", 16 | "ext-json": "*", 17 | "illuminate/support": "5.6.*|5.7.*|5.8.*", 18 | "guzzlehttp/guzzle": ">=6.0" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "VMdevelopment\\TapPayment\\": "src/" 23 | } 24 | }, 25 | "autoload-dev": { 26 | "psr-4": { 27 | "VMdevelopment\\TapPayment\\Tests\\": "tests/" 28 | } 29 | }, 30 | "extra": { 31 | "laravel": { 32 | "providers": [ 33 | "VMdevelopment\\TapPayment\\TapPaymentServiceProvider" 34 | ] 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Abstracts/AbstractService.php: -------------------------------------------------------------------------------- 1 | client = new Client(); 18 | } 19 | 20 | 21 | public function getPath() 22 | { 23 | return $this->base_path . $this->version . '/' . $this->endpoint; 24 | } 25 | } -------------------------------------------------------------------------------- /src/Facade/TapPayment.php: -------------------------------------------------------------------------------- 1 | env( 'TAP_PAYMENT_MODE', "live" ), 15 | 16 | /* 17 | |-------------------------------------------------------------------------- 18 | | TapPayment authentication details 19 | |-------------------------------------------------------------------------- 20 | | 21 | | "username" and "password" fields are mandatory for authorization feature. 22 | | "token" field must exist in the same array format as you receive from API. 23 | | For example 24 | | 25 | | "access_token" => "Your Token String", 26 | | 27 | | 28 | | 29 | */ 30 | 31 | "auth" => [ 32 | "api_key" => env( 'TAP_PAYMENT_API_KEY' ) 33 | ], 34 | 35 | 'customer' => [ 36 | 'requirements' => [ 37 | 'first_name', 38 | 'mobile', 39 | ] 40 | ] 41 | ]; -------------------------------------------------------------------------------- /src/Resources/Invoice.php: -------------------------------------------------------------------------------- 1 | attributes = (array)$data; 13 | } 14 | 15 | 16 | public function isSuccess() 17 | { 18 | return isset( $this->attributes['status'] ) && strtolower( $this->attributes['status'] ) == 'captured'; 19 | } 20 | 21 | 22 | public function isInitiated() 23 | { 24 | return isset( $this->attributes['status'] ) && strtolower( $this->attributes['status'] ) == 'initiated'; 25 | } 26 | 27 | 28 | public function getPaymetUrl() 29 | { 30 | return $this->attributes['transaction']['url'] ?? null; 31 | } 32 | 33 | 34 | public function getId() 35 | { 36 | return $this->attributes['id'] ?? null; 37 | } 38 | 39 | 40 | public function getMetaData() 41 | { 42 | return $this->attributes['metadata'] ?? null; 43 | } 44 | 45 | public function checkHash( $hash ) 46 | { 47 | $data = [ 48 | 'x_id' => $this->attributes['id'] ?? null, 49 | 'x_amount' => $this->attributes['amount'] ?? null, 50 | 'x_currency' => $this->attributes['currency'] ?? null, 51 | 'x_gateway_reference' => $this->attributes['reference']['gateway'] ?? null, 52 | 'x_payment_reference' => $this->attributes['reference']['payment'] ?? null, 53 | 'x_status' => $this->attributes['status'] ?? null, 54 | 'x_created' => $this->attributes['transaction']['created'] ?? null, 55 | ]; 56 | 57 | $decimals = $data['x_currency'] == 'KWD' ? 3 : 2; 58 | 59 | $data['x_amount'] = number_format( $data['x_amount'], $decimals, '.', '' ); 60 | 61 | $stringToHash = implode( 62 | '', 63 | array_map( 64 | function( $value, $key ) { 65 | return $key . $value; 66 | }, 67 | $data, 68 | array_keys( $data ) 69 | ) 70 | ); 71 | 72 | $key = config( 'tap-payment.auth.api_key' ); 73 | 74 | $hashedString = hash_hmac( 'sha256', $stringToHash, $key ); 75 | 76 | return $hashedString == $hash; 77 | } 78 | } -------------------------------------------------------------------------------- /src/Services/Charge.php: -------------------------------------------------------------------------------- 1 | attributes['id'] = $id; 20 | $this->setEndpoint( $id ); 21 | } 22 | parent::__construct(); 23 | } 24 | 25 | 26 | protected function setEndpoint( $endpoint ) 27 | { 28 | $this->endpoint .= $endpoint; 29 | } 30 | 31 | 32 | public function setAmount( $amount ) 33 | { 34 | $this->attributes['amount'] = $amount; 35 | } 36 | 37 | 38 | public function setCurrency( $currency ) 39 | { 40 | $this->attributes['currency'] = $currency; 41 | } 42 | 43 | 44 | public function setThreeDSecure( $threeDSecure ) 45 | { 46 | $this->attributes['threeDSecure'] = $threeDSecure; 47 | } 48 | 49 | 50 | public function setSave_card( $save_card ) 51 | { 52 | $this->attributes['save_card'] = $save_card; 53 | } 54 | 55 | 56 | public function setDescription( $description ) 57 | { 58 | $this->attributes['description'] = $description; 59 | } 60 | 61 | 62 | public function setCustomerName( $name ) 63 | { 64 | $name = explode( ' ', $name ); 65 | $this->attributes['customer']['first_name'] = array_shift( $name ); 66 | $this->attributes['customer']['last_name'] = implode( ' ', $name ); 67 | } 68 | 69 | 70 | public function setCustomerEmail( $email ) 71 | { 72 | $this->attributes['customer']['email'] = $email; 73 | } 74 | 75 | 76 | public function setCustomerPhone( $country_code, $phone ) 77 | { 78 | $this->attributes['customer']['phone'] = [ 79 | 'country_code' => $country_code, 80 | 'number' => $phone, 81 | ]; 82 | } 83 | 84 | 85 | public function setRedirectUrl( $url ) 86 | { 87 | $this->attributes['redirect']['url'] = $url; 88 | } 89 | 90 | 91 | public function setPostUrl( $url ) 92 | { 93 | $this->attributes['post']['url'] = $url; 94 | } 95 | 96 | 97 | public function setSource( $source ) 98 | { 99 | $this->attributes['source']['id'] = $source; 100 | } 101 | 102 | 103 | public function setMetaData( array $meta ) 104 | { 105 | $this->attributes['metadata'] = $meta; 106 | } 107 | 108 | 109 | public function setRawAttributes( array $attributes ) 110 | { 111 | $this->attributes = $attributes; 112 | } 113 | 114 | 115 | /** 116 | * @return mixed 117 | * @throws \GuzzleHttp\Exception\GuzzleException|\Exception 118 | */ 119 | public function find() 120 | { 121 | $this->setMethod( 'get' ); 122 | if ( 123 | $this->validateAttributes( 124 | [ 125 | 'id' => 'required' 126 | ] 127 | ) 128 | ) 129 | return $this->send(); 130 | } 131 | 132 | 133 | protected function setMethod( $method ) 134 | { 135 | $this->method = $method; 136 | } 137 | 138 | 139 | /** 140 | * @param $rules 141 | * 142 | * @return bool 143 | * @throws \Exception 144 | */ 145 | public function validateAttributes( array $rules, $messages = [] ) 146 | { 147 | $validator = Validator::make( $this->attributes, $rules, $messages ); 148 | 149 | if ( $validator->fails() ) 150 | throw new \Exception( $validator->errors()->first() ); 151 | 152 | return true; 153 | } 154 | 155 | 156 | /** 157 | * @return mixed 158 | * @throws \GuzzleHttp\Exception\GuzzleException|\Exception 159 | */ 160 | protected function send() 161 | { 162 | try { 163 | $response = $this->client->request( 164 | $this->method, 165 | $this->getPath(), 166 | [ 167 | 'form_params' => $this->attributes, 168 | 'headers' => [ 169 | 'Authorization' => 'Bearer ' . config( 'tap-payment.auth.api_key' ), 170 | 'Accept' => 'application/json', 171 | ] 172 | ] 173 | ); 174 | 175 | return new Invoice( json_decode( $response->getBody()->getContents(), true ) ); 176 | } 177 | catch ( \Throwable $exception ) { 178 | throw new \Exception( $exception->getMessage() ); 179 | } 180 | } 181 | 182 | 183 | /** 184 | * @return Invoice 185 | * @throws \GuzzleHttp\Exception\GuzzleException|\Exception 186 | */ 187 | public function pay() 188 | { 189 | $rules = [ 190 | 'id' => 'regex:/^$/i', 191 | 'amount' => 'required', 192 | 'currency' => 'required', 193 | 'source.id' => 'required', 194 | 'redirect.url' => 'required', 195 | ]; 196 | foreach ( config( 'tap-payment.customer.requirements' ) as $item ) { 197 | if ( $item == 'mobile' ) { 198 | $rules['customer.phone'] = 'required'; 199 | $rules['customer.phone.country_code'] = [ 'required', 'numeric' ]; 200 | $rules['customer.phone.number'] = [ 'required', 'numeric' ]; 201 | } else { 202 | $rules[ 'customer.' . $item ] = 'required'; 203 | } 204 | } 205 | 206 | if ( 207 | $this->validateAttributes( 208 | $rules, 209 | [ 210 | 'id.regex' => "ID should be empty when you create a new Charge." 211 | ] 212 | ) 213 | ) 214 | return $this->send(); 215 | } 216 | } -------------------------------------------------------------------------------- /src/TapPaymentServiceProvider.php: -------------------------------------------------------------------------------- 1 | publishes( 17 | [ 18 | __DIR__ . '/Publishing/config.php' => config_path( 'tap-payment.php' ), 19 | ] 20 | ); 21 | $this->mergeConfigFrom( 22 | __DIR__ . '/Publishing/config.php', 23 | 'tap-payment' 24 | ); 25 | } 26 | 27 | 28 | /** 29 | * Register TapPayment application services. 30 | * 31 | * @return void 32 | */ 33 | public function register() 34 | { 35 | $this->app->singleton( 36 | 'tap-payment', function() { 37 | return new TapService(); 38 | } 39 | ); 40 | } 41 | } -------------------------------------------------------------------------------- /src/TapService.php: -------------------------------------------------------------------------------- 1 | find(); 29 | } 30 | } --------------------------------------------------------------------------------