├── .github ├── FUNDING.md └── workflows │ └── php.yml ├── src ├── Facades │ ├── RedirectsDefault.php │ ├── Transbank.php │ ├── Webpay.php │ ├── WebpayMall.php │ └── OneclickMall.php ├── EventDispatcher.php ├── Listeners │ └── SaveTransactionToken.php ├── Http │ └── Middleware │ │ └── EndpointProtect.php └── LarabankerServiceProvider.php ├── phpunit.xml ├── LICENSE ├── composer.json ├── config └── larabanker.php └── README.md /.github/FUNDING.md: -------------------------------------------------------------------------------- 1 | # Help me support this package 2 | 3 | ko_fi: DarkGhostHunter 4 | custom: ['https://paypal.me/darkghosthunter'] -------------------------------------------------------------------------------- /src/Facades/RedirectsDefault.php: -------------------------------------------------------------------------------- 1 | make('config')->get("larabanker.redirects.$service") 20 | ); 21 | } 22 | } -------------------------------------------------------------------------------- /src/Facades/Transbank.php: -------------------------------------------------------------------------------- 1 | dispatcher->dispatch($event); 30 | } 31 | } -------------------------------------------------------------------------------- /src/Listeners/SaveTransactionToken.php: -------------------------------------------------------------------------------- 1 | store->put(Str::finish($this->prefix, '|') . $event->response->getToken(), true, 300); 31 | } 32 | } -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | src/ 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | tests 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2018] [Italo Israel Baeza Cabrera] 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 | -------------------------------------------------------------------------------- /src/Http/Middleware/EndpointProtect.php: -------------------------------------------------------------------------------- 1 | enabled && !$this->check($request), 404); 36 | 37 | return $next($request); 38 | } 39 | 40 | /** 41 | * Checks if the token was issued. If it is, it removes it from the cache. 42 | * 43 | * @param \Illuminate\Http\Request $request 44 | * @return bool 45 | */ 46 | public function check(Request $request): bool 47 | { 48 | $token = $request->input('token_ws') ?? $request->input('TBK_TOKEN'); 49 | 50 | return $token && app('cache')->store($this->store)->pull(Str::finish($this->prefix, '|') . $token); 51 | } 52 | } -------------------------------------------------------------------------------- /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: PHP Composer 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | test: 9 | 10 | runs-on: ubuntu-latest 11 | strategy: 12 | fail-fast: true 13 | matrix: 14 | php: [8.0, 8.1] 15 | laravel: [8.*] 16 | dependency-version: [prefer-stable, prefer-lowest] 17 | include: 18 | - laravel: 8.* 19 | testbench: ^6.23.2 20 | 21 | name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} - ${{ matrix.dependency-version }} 22 | 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@v2 26 | 27 | - name: Setup PHP 28 | uses: shivammathur/setup-php@v2 29 | with: 30 | php-version: ${{ matrix.php }} 31 | extensions: mbstring, intl 32 | coverage: xdebug 33 | 34 | - name: Cache dependencies 35 | uses: actions/cache@v2 36 | with: 37 | path: ~/.composer/cache/files 38 | key: ${{ runner.os }}-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} 39 | restore-keys: ${{ runner.os }}-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer- 40 | 41 | - name: Install dependencies 42 | run: | 43 | composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-progress --no-update 44 | composer update --${{ matrix.dependency-version }} --prefer-dist --no-progress --no-suggest 45 | 46 | - name: Run Tests 47 | run: composer run-script test 48 | 49 | - name: Upload Coverage to Coveralls 50 | env: 51 | COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} 52 | COVERALLS_SERVICE_NAME: github 53 | run: | 54 | rm -rf composer.* vendor/ 55 | composer require php-coveralls/php-coveralls 56 | vendor/bin/php-coveralls -------------------------------------------------------------------------------- /src/Facades/Webpay.php: -------------------------------------------------------------------------------- 1 | =8.0", 21 | "ext-json": "*", 22 | "illuminate/http": "^8.0", 23 | "illuminate/events": "^8.0", 24 | "illuminate/config": "^8.0", 25 | "illuminate/support": "^8.0", 26 | "illuminate/view": "^8.0", 27 | "guzzlehttp/guzzle": "^7.4", 28 | "darkghosthunter/transbank": "^v2.1.3" 29 | }, 30 | "require-dev": { 31 | "phpunit/phpunit": "^9.5.4", 32 | "mockery/mockery": "^1.4.3", 33 | "orchestra/testbench": "^6.16" 34 | }, 35 | "autoload": { 36 | "psr-4": { 37 | "DarkGhostHunter\\Larabanker\\": "src/" 38 | } 39 | }, 40 | "autoload-dev": { 41 | "psr-4": { 42 | "Tests\\": "tests/" 43 | } 44 | }, 45 | "scripts": { 46 | "test": "vendor/bin/phpunit --coverage-clover build/logs/clover.xml", 47 | "test-coverage": "vendor/bin/phpunit --coverage-html coverage" 48 | }, 49 | "config": { 50 | "sort-packages": true, 51 | "process-timeout": 900 52 | }, 53 | "extra": { 54 | "laravel": { 55 | "providers": [ 56 | "DarkGhostHunter\\Larabanker\\LarabankerServiceProvider" 57 | ], 58 | "aliases": { 59 | "Transbank": "DarkGhostHunter\\Larabanker\\Facades\\Transbank", 60 | "Webpay": "DarkGhostHunter\\Larabanker\\Facades\\Webpay", 61 | "WebpayMall": "DarkGhostHunter\\Larabanker\\Facades\\WebpayMall", 62 | "OneclickMall": "DarkGhostHunter\\Larabanker\\Facades\\OneclickMall" 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Facades/OneclickMall.php: -------------------------------------------------------------------------------- 1 | env('TRANSBANK_ENV', 'integration'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Credentials 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you put each of the credentials for each service you use. These are 26 | | not required if you're using this SDK with fake transactions, but once 27 | | you set the `production` environment these will be mandatory to use. 28 | | 29 | */ 30 | 31 | 'credentials' => [ 32 | 'webpay' => [ 33 | 'key' => env('WEBPAY_KEY'), 34 | 'secret' => env('WEBPAY_SECRET'), 35 | ], 36 | 'webpayMall' => [ 37 | 'key' => env('WEBPAY_MALL_KEY'), 38 | 'secret' => env('WEBPAY_MALL_SECRET'), 39 | ], 40 | 'oneclickMall' => [ 41 | 'key' => env('ONECLICK_MALL_KEY'), 42 | 'secret' => env('ONECLICK_MALL_SECRET'), 43 | ], 44 | ], 45 | 46 | /* 47 | |-------------------------------------------------------------------------- 48 | | Defaults redirects 49 | |-------------------------------------------------------------------------- 50 | | 51 | | Once you create a transaction in Transbank and the user completes it the 52 | | servers will redirect the user browser to your site. These URLs will be 53 | | injected when the user is redirected when you use each service Facade. 54 | | 55 | */ 56 | 57 | 'redirects' => [ 58 | 'webpay' => 'transbank.webpay', 59 | 'webpayMall' => 'transbank.webpayMall', 60 | 'oneclickMall' => 'transbank.oneclickMall', 61 | ], 62 | 63 | 64 | /* 65 | |-------------------------------------------------------------------------- 66 | | Brute-force protection 67 | |-------------------------------------------------------------------------- 68 | | 69 | | To avoid brute-force attacks to your Transbank endpoints, you can use the 70 | | protection on these endpoints using the `larabanker.protect` middleware. 71 | | To enable it, set the `protect` key or the `TRANSBANK_PROTECT` to true. 72 | | 73 | */ 74 | 75 | 'protect' => env('TRANSBANK_PROTECT', false), 76 | 'cache' => null, 77 | 'cache_prefix' => env('TRANSBANK_PROTECT_PREFIX', 'transbank|token'), 78 | ]; 79 | -------------------------------------------------------------------------------- /src/LarabankerServiceProvider.php: -------------------------------------------------------------------------------- 1 | mergeConfigFrom(__DIR__.'/../config/larabanker.php', 'larabanker'); 30 | 31 | $this->app->singleton( 32 | Transbank::class, 33 | static function (Application $app): Transbank { 34 | $transbank = new Transbank( 35 | new Container(), 36 | $app->make('log'), 37 | new EventDispatcher($app->make('events')), 38 | new Connector($app->make(Client::class), $factory = new Psr17Factory(), $factory) 39 | ); 40 | 41 | $config = $app->make('config'); 42 | 43 | if ($config->get('larabanker.enable', $app->environment('production'))) { 44 | $transbank->toProduction($config->get('larabanker.credentials')); 45 | } 46 | 47 | return $transbank; 48 | }); 49 | 50 | $this->app->singleton(Webpay::class, static function (Application $app): Webpay { 51 | return $app->make(Transbank::class)->webpay(); 52 | }); 53 | 54 | $this->app->singleton(WebpayMall::class, static function (Application $app): WebpayMall { 55 | return $app->make(Transbank::class)->webpayMall(); 56 | }); 57 | 58 | $this->app->singleton(OneclickMall::class, static function (Application $app): OneclickMall { 59 | return $app->make(Transbank::class)->oneclickMall(); 60 | }); 61 | 62 | $this->app->singleton( 63 | Http\Middleware\EndpointProtect::class, 64 | static function (Application $app): Http\Middleware\EndpointProtect { 65 | $config = $app->make('config'); 66 | 67 | return new Http\Middleware\EndpointProtect( 68 | $config->get('larabanker.protect'), 69 | $config->get('larabanker.cache') ?? $config->get('cache.default'), 70 | $config->get('larabanker.cache_prefix', 'transbank|token') 71 | ); 72 | }); 73 | 74 | $this->app->bind( 75 | Listeners\SaveTransactionToken::class, 76 | static function (Application $app): Listeners\SaveTransactionToken { 77 | $config = $app->make('config'); 78 | 79 | return new Listeners\SaveTransactionToken( 80 | $app->make('cache')->store($config->get('larabanker.cache')), 81 | $config->get('larabanker.cache_prefix') 82 | ); 83 | }); 84 | } 85 | 86 | /** 87 | * Bootstrap any application services. 88 | * 89 | * @param \Illuminate\Contracts\Events\Dispatcher $dispatcher 90 | * @param \Illuminate\Contracts\Config\Repository $config 91 | * @param \Illuminate\Routing\Router $router 92 | * @return void 93 | */ 94 | public function boot(Router $router, Repository $config, Dispatcher $dispatcher): void 95 | { 96 | $router->aliasMiddleware('larabanker.protect', Http\Middleware\EndpointProtect::class); 97 | 98 | if ($config->get('larabanker.protect')) { 99 | $dispatcher->listen(TransactionCreated::class, Listeners\SaveTransactionToken::class); 100 | } 101 | 102 | if ($this->app->runningInConsole()) { 103 | $this->publishes([ 104 | __DIR__.'/../config/larabanker.php' => $this->app->configPath('larabanker.php') 105 | ], 'config'); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Paul Felberbauer - Unsplash (UL) #-idNOBU5k_80](https://images.unsplash.com/photo-1591030434469-3d78c7b17820?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1280&h=400&q=80) 2 | 3 | [![Latest Stable Version](https://poser.pugx.org/darkghosthunter/larabanker/v/stable)](https://packagist.org/packages/darkghosthunter/larabanker) [![License](https://poser.pugx.org/darkghosthunter/larabanker/license)](https://packagist.org/packages/darkghosthunter/larabanker) ![](https://img.shields.io/packagist/php-v/darkghosthunter/larabanker.svg) [![Coverage Status](https://coveralls.io/repos/github/DarkGhostHunter/Larabanker/badge.svg?branch=master)](https://coveralls.io/github/DarkGhostHunter/Larabanker?branch=master) [![Laravel Octane Compatible](https://img.shields.io/badge/Laravel%20Octane-Compatible-success?style=flat&logo=laravel)](https://github.com/laravel/octane) 4 | 5 | # Larabanker - Transbank for Laravel 6 | 7 | This package connects the non-official [Transbank SDK](https://github.com/DarkGhostHunter/Transbank/) into your Laravel Application. 8 | 9 | ## Requirements 10 | 11 | * PHP >= 8.0 12 | * Laravel 8.x 13 | 14 | > Check older releases for older Laravel versions. 15 | 16 | ## Installation 17 | 18 | Call composer and require it into your application. 19 | 20 | ```bash 21 | composer require darkghosthunter/larabanker 22 | ``` 23 | 24 | ## Documentation 25 | 26 | This package mimics the Transbank SDK, so you should check the documentation of these services in Transbank Developer's site (in spanish). 27 | 28 | - [Webpay](https://www.transbankdevelopers.cl/documentacion/webpay-plus#webpay-plus) 29 | - [Webpay Mall](https://www.transbankdevelopers.cl/documentacion/webpay-plus#webpay-plus-mall) 30 | - [Oneclick Mall](https://www.transbankdevelopers.cl/documentacion/oneclick) 31 | 32 | ## Quickstart 33 | 34 | To start using Transbank services, you can use the included `Webpay`, `WebpayMall` and `Oneclick` facades and the `redirect()` method, which use minimum parameters and returns a ready-made `GET` redirect to Transbank. 35 | 36 | ```php 37 | use DarkGhostHunter\Larabanker\Facades\Webpay; 38 | 39 | return Webpay::redirect('buy-order#1230', 1000); 40 | ``` 41 | 42 | Alternatively, you can still have total control to create transactions using the facades. 43 | 44 | ```php 45 | use DarkGhostHunter\Larabanker\Facades\Webpay; 46 | 47 | $response = Webpay::create('buyOrder#1230', 1000, route('payment')); 48 | 49 | return redirect()->away($response, 303); 50 | ``` 51 | 52 | > Since API 1.2, Transbank services support `GET` redirects. There is no longer need to use views with Javascript redirection. 53 | 54 | Redirects are made using [default route names](#dealing-with-post-and-session-destruction) that centralizes the payment endpoint. 55 | 56 | ## Dealing with POST and Session destruction 57 | 58 | Laravel sets cookies as `SameSite: lax` by default. This means that the session is destroyed when a payment fails or is aborted. This happens because Transbank redirects using a `POST` method to your application without cookies, forcing Laravel to [recreate a new empty session](https://github.com/laravel/framework/issues/31442). 59 | 60 | To avoid this, you should use the same path to receive the response from Transbank, but using a different controller for `GET` or `POST`. Larabanker conveniently uses one route name for each of Transbank services redirection points, which will be hit once the payment process ends. 61 | 62 | | Service | URL | Name | Your hypothetical route | 63 | |---------------|--------------|--------------------------|------------------------------------------------| 64 | | Webpay | Return URL | `transbank.webpay` | `http://yourappurl.com/transbank/webpay` | 65 | | Webpay Mall | Return URL | `transbank.webpayMall` | `http://yourappurl.com/transbank/webpayMall` | 66 | | Oneclick Mall | Response URL | `transbank.oneclickMall` | `http://yourappurl.com/transbank/oneclickMall` | 67 | 68 | You're free to [change these Route names in the config file](#redirection). Be sure to add your controllers for these routes to process the incoming response from Transbank. 69 | 70 | In this example, we will disable the `web` middleware to avoid creating a new session, and return a view with a generic failure message. 71 | 72 | ```php 73 | use \DarkGhostHunter\Larabanker\Facades\Webpay; 74 | use \Illuminate\Support\Facades\Route; 75 | 76 | Route::get('transbank/webpay', function (Request $request) { 77 | $transaction = Webpay::commit($request->input('token_ws')); 78 | 79 | return view('payment.processed')->with('transaction', $transaction); 80 | })->name('transbank.webpay'); 81 | 82 | Route::post('transaction/webpay, function (Request $request) { 83 | return view('payment.failed'); 84 | })->withoutMiddleware('web'); 85 | ``` 86 | 87 | ## Configuration 88 | 89 | While Larabanker is made to use conveniently without setting too much, you can go deeper by publishing the configuration file: 90 | 91 | php artisan vendor:publish --provider="DarkGhostHunter\Larabanker\ServiceProvider" 92 | 93 | You will receive the `larabanker.php` config file with the following contents: 94 | 95 | ```php 96 | env('TRANSBANK_ENV', 'integration'), 100 | 'credentials' => [ 101 | // ... 102 | ], 103 | 'redirects' => [ 104 | 'webpay' => 'transbank.webpay', 105 | 'webpayMall' => 'transbank.webpayMall', 106 | 'oneclickMall' => 'transbank.oneclickMall', 107 | ], 108 | 'protect' => env('TRANSBANK_PROTECT', false), 109 | 'cache' => null, 110 | 'cache_prefix' => env('TRANSBANK_PROTECT_PREFIX', 'transbank|token') 111 | ]; 112 | ``` 113 | 114 | Don't worry, we will explain each important part one by one. 115 | 116 | ### Environment & Credentials 117 | 118 | ```php 119 | env('TRANSBANK_ENV', 'integration'), 123 | 'credentials' => [ 124 | 'webpay' => [ 125 | 'key' => env('WEBPAY_KEY'), 126 | 'secret' => env('WEBPAY_SECRET'), 127 | ], 128 | 'webpayMall' => [ 129 | 'key' => env('WEBPAY_MALL_KEY'), 130 | 'secret' => env('WEBPAY_MALL_SECRET'), 131 | ], 132 | 'oneclickMall' => [ 133 | 'key' => env('ONECLICK_MALL_KEY'), 134 | 'secret' => env('ONECLICK_MALL_SECRET'), 135 | ], 136 | ], 137 | ]; 138 | ``` 139 | 140 | By default, the package uses the `integration` environment, which makes fake transactions. 141 | 142 | To use the `production` environment, which will make all transactions real, set the environment as `production` **explicitly**: 143 | 144 | ```dotenv 145 | TRANSBANK_ENV=production 146 | ``` 147 | 148 | Additionally, you must add your Transbank credentials for your services, which will be issued directly to you, for the service(s) you have contracted. You can do it easily in your `.env` file. 149 | 150 | ```dotenv 151 | WEBPAY_KEY=5000000001 152 | WEBPAY_SECRET=dKVhq1WGt_XapIYirTXNyUKoWTDFfxaEV63-O5jcsdw 153 | ``` 154 | 155 | ### Redirection 156 | 157 | ```php 158 | env('APP_URL'), 162 | 'redirects' => [ 163 | 'webpay' => 'transbank.webpay', 164 | 'webpayMall' => 'transbank.webpayMall', 165 | 'oneclickMall' => 'transbank.oneclickMall', 166 | ], 167 | ]; 168 | ``` 169 | 170 | Only when using the `Webpay`, `WebpayMall` and `OneclickMall` facades, you will be able to skip issuing the `$returnUrl` or `$responseUrl` values to the transaction creation, letting Larabanker to use the defaults issued in your config file. 171 | 172 | ```php 173 | use DarkGhostHunter\Larabanker\Facades\Webpay; 174 | 175 | $response = Webpay::create('myOrder#16548', 1000); 176 | ``` 177 | 178 | ### Endpoint Protection 179 | 180 | ```php 181 | return [ 182 | 'protect' => env('TRANSBANK_PROTECT', false), 183 | 'cache' => null, 184 | 'cache_prefix' => env('TRANSBANK_PROTECT_PREFIX', 'transbank|token') 185 | ]; 186 | ``` 187 | 188 | Disabled by default, this package offers a brute-force attack protection middleware, `larabank.protect`, for return URL. These return URLs are your application endpoints that Transbank services will redirect the user to using a `GET` or `POST` request. 189 | 190 | If it's disabled, the middleware won't verify the token. 191 | 192 | ```php 193 | use \Illuminate\Support\Facades\Route; 194 | use \App\Http\Controllers\WebpayController; 195 | 196 | Route::post('/transbank/webpay', [WebpayController::class, 'receivePayment']) 197 | ->middleware('larabank.protect'); 198 | ``` 199 | 200 | It uses the cache to save the transaction token for 5 minutes, so if the token is no longer valid, the whole response is aborted. You can change the cache store and prefix with `cache` and `cache_prefix`, respectively. 201 | 202 | > This works for receiving the redirection from Transbank on Webpay, Webpay Mall and Oneclick Mall services. 203 | 204 | ## License 205 | 206 | This package is open-sourced software licensed under the MIT license. 207 | 208 | `Redcompra`, `Webpay`, `Onepay`, `Patpass` and `tbk` are trademarks of [Transbank S.A.](https://www.transbank.cl/) 209 | 210 | This package is not developed, approved, supported nor endorsed by Transbank S.A., nor by a natural person or company linked directly or indirectly by Transbank S.A. 211 | --------------------------------------------------------------------------------