├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json └── src ├── Facades └── Flow.php ├── Flow.php ├── FlowServiceProvider.php ├── config └── config.php └── keys └── flow.pubkey /.gitignore: -------------------------------------------------------------------------------- 1 | composer.phar 2 | composer.lock 3 | /vendor 4 | /tests 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | Todos los cambios notables en este proyecto se documentarán en este archivo. 3 | 4 | El formato se basa en [Mantenga un Changelog](http://keepachangelog.com/es-ES/1.0.0/) 5 | y este proyecto se adhiere al [Versionado Semántico](https://semver.org/lang/es/). 6 | 7 | ## [Unreleased] 8 | 9 | ## [1.2.2] - 2017-12-21 10 | ### Añadido 11 | - Se añade un registro de cambios al proyecto (CHANGELOG). 12 | 13 | ## [1.2.1] - 2017-12-20 14 | ### Añadido 15 | - Se añade la sección 16 | [Proyecto de demostración](https://github.com/cokecancino/laravel-flow#proyecto-de-demostraci%C3%B3n) 17 | al README. 18 | 19 | Es un repositorio de ejemplo del package implementado en Laravel 5.5 20 | con el objetivo de tener una mayor comprensión de cómo utilizarlo. 21 | 22 | ## [1.2] - 2017-12-20 23 | ### Añadido 24 | - Se actualiza la API de Flow a la versión 1.4 25 | 26 | - Se añade la opción Multicaja en la descripción del medio de pago 27 | en el archivo de configuración. 28 | 29 | - Se añade la posibilidad de definir la página de retorno en el 30 | archivo de configuración. 31 | 32 | > La página de retorno solo aplica al medio de pago Multicaja. 33 | Corresponde a la página donde volverá el cliente una vez que generó 34 | el cupón de pago. Recomendamos que dicha URL sea la página principal 35 | de tu tienda virtual. 36 | 37 | Archivo de configuración: `config/flow.php` 38 | 39 | ```php 40 | … 41 | /* 42 | |-------------------------------------------------------------------------- 43 | | Ingrese aquí la URL de su página de retorno 44 | |-------------------------------------------------------------------------- 45 | | 46 | | Valores posibles: 47 | | 'http://www.comercio.cl', 48 | | ['url' => 'flow/retorno'], 49 | | ['route' => 'flow.retorno'], 50 | | ['action' => 'FlowController@retorno'], 51 | | 52 | */ 53 | 54 | 'url_retorno' => ['url' => '/'], 55 | … 56 | ``` 57 | 58 | **Importante:** Debes volver a publicar el archivo de configuración 59 | para reflejar los cambios: 60 | ```sh 61 | $ php artisan vendor:publish --provider="CokeCancino\LaravelFlow\FlowServiceProvider" --force 62 | ``` 63 | 64 | ### Cambiado 65 | - Se han actualizado y mejorado los ejemplos de la sección 66 | [Utilización](https://github.com/cokecancino/laravel-flow#utilizaci%C3%B3n) 67 | en el README. 68 | 69 | ## [1.1.3] - 2016-11-11 70 | ### Cambiado 71 | - Se actualiza la URL "Excluye la protección CSRF" en el README. 72 | 73 | ## [1.1.2] - 2016-11-11 74 | ### Arreglado 75 | - Se corrige la URL "Kit de Integración de Flow" en el README. 76 | 77 | ## [1.1.1] - 2016-03-12 78 | ### Añadido 79 | - Se añade la sección 80 | [Utilización](https://github.com/cokecancino/laravel-flow#utilizaci%C3%B3n) 81 | al README, con el objetivo de ejemplificar las diferencias de su 82 | utilización dentro de Laravel. 83 | 84 | ## [1.1] - 2016-02-08 85 | ### Añadido 86 | - Se añade la posibilidad de ingresar URLs absolutas en las opciones 87 | del archivo de configuración. 88 | 89 | ## [1.0] - 2016-02-05 90 | ### Cambiado 91 | - Se ha traducido al español el README y la descripción de `composer.json`. 92 | 93 | ### Eliminado 94 | - Se elimina la tasa de comisión de Flow (variable `tasa_default`) de 95 | la API de Flow y del archivo de configuración, ya que ésta ahora se 96 | define desde el sitio web de Flow. 97 | 98 | ## [0.1.1] - 2016-02-05 99 | ### Añadido 100 | - Se añaden las instrucciones de instalación al README. 101 | 102 | ### Cambiado 103 | - Se mejora el formato de descripción del archivo de configuración. 104 | 105 | ### Arreglado 106 | - Se corrige una redundancia en `composer.json`. 107 | 108 | ## 0.1 - 2016-02-05 109 | ### Añadido 110 | - Se implementa la API de Flow v1.2 111 | 112 | [Unreleased]: https://github.com/cokecancino/laravel-flow/compare/1.2.2...HEAD 113 | [1.2.2]: https://github.com/cokecancino/laravel-flow/compare/1.2.1...1.2.2 114 | [1.2.1]: https://github.com/cokecancino/laravel-flow/compare/1.2...1.2.1 115 | [1.2]: https://github.com/cokecancino/laravel-flow/compare/1.1.3...1.2 116 | [1.1.3]: https://github.com/cokecancino/laravel-flow/compare/1.1.2...1.1.3 117 | [1.1.2]: https://github.com/cokecancino/laravel-flow/compare/1.1.1...1.1.2 118 | [1.1.1]: https://github.com/cokecancino/laravel-flow/compare/1.1...1.1.1 119 | [1.1]: https://github.com/cokecancino/laravel-flow/compare/1.0...1.1 120 | [1.0]: https://github.com/cokecancino/laravel-flow/compare/0.1.1...1.0 121 | [0.1.1]: https://github.com/cokecancino/laravel-flow/compare/0.1...0.1.1 122 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jorge Cancino 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 | # Laravel Flow 2 | 3 |

4 | Total Downloads 5 | Latest Stable Version 6 | License 7 |

8 | 9 | Laravel package para integrar pagos con [Flow](https://www.flow.cl/). 10 | 11 | > [!WARNING] 12 | > **Este package está deprecado y ya no recibe mantenimiento** 13 | > 14 | > En sus inicios, fue creado para implementar el, ahora antiguo, Kit de Integración de Flow en Laravel, ya que en ese 15 | > entonces no existía otra forma de integración. Actualmente, Flow proporciona 16 | > una [API REST](https://www.flow.cl/docs/api.html) actualizada, por lo que este package se ha vuelto innecesario y 17 | > potencialmente incompatible con las nuevas versiones de la API de Flow. Por esta razón, se recomienda encarecidamente 18 | > utilizar la nueva API REST de Flow para futuras integraciones. 19 | > 20 | > Agradezco a todos los que confiaron en este package para sus proyectos y comercios. Espero que la nueva API REST de 21 | > Flow proporcione una mejor experiencia y mayor flexibilidad en la integración de pagos. 22 | 23 | ## Compatibilidad 24 | 25 | Este package ha sido probado con las siguientes versiones de Laravel: 26 | 27 | - Laravel 5.1 28 | - Laravel 5.5 29 | 30 | Es probable que también sea compatible con otras versiones, pero no ha sido específicamente probado en ellas. 31 | 32 | ## Instalación 33 | 34 | 1. **Instalar a través de Composer** 35 | 36 | ```sh 37 | composer require cokecancino/laravel-flow 38 | ``` 39 | 40 | 2. **Agregar el Service Provider** 41 | 42 | En el archivo `config/app.php`, agregar la siguiente línea al array `providers`: 43 | 44 | ```php 45 | 'providers' => [ 46 | // ... 47 | CokeCancino\LaravelFlow\FlowServiceProvider::class, 48 | // ... 49 | ], 50 | ``` 51 | 52 | 3. **Agregar el alias** 53 | 54 | En el mismo archivo, agregar la siguiente línea al array `aliases`: 55 | 56 | ```php 57 | 'aliases' => [ 58 | // ... 59 | 'Flow' => CokeCancino\LaravelFlow\Facades\Flow::class, 60 | // ... 61 | ], 62 | ``` 63 | 64 | 4. **Publicar el archivo de configuración** 65 | 66 | ```sh 67 | php artisan vendor:publish --provider="CokeCancino\LaravelFlow\FlowServiceProvider" --force 68 | ``` 69 | 70 | 5. **Configura tu `.env` o modifica tu `config/flow.php`** 71 | 72 | ```env 73 | FLOW_URL_PAGO=http://flow.tuxpan.com/app/kpf/pago.php 74 | FLOW_COMERCIO=emailFlow@comercio.com 75 | ``` 76 | 77 | ## Guía de Uso 78 | 79 | A continuación, se muestran ejemplos de su uso en Laravel. 80 | 81 | > [!IMPORTANT] 82 | > [Excluye la protección CSRF](https://laravel.com/docs/master/csrf#csrf-excluding-uris) para las páginas de éxito, 83 | > fracaso, y confirmación, ya que Flow no sabrá qué token CSRF enviar a tus rutas. 84 | 85 | ### Formulario de compra 86 | 87 | View: `resources/views/index.blade.php` 88 | 89 | ```blade 90 | @extends('layouts._master') 91 | 92 | @section('content') 93 |
94 | 95 | Orden n°:
96 | Monto:
97 | Descripción:
98 | Email pagador (opcional):
99 |
100 | 101 |
102 | @endsection 103 | ``` 104 | 105 | ### Creando una nueva orden 106 | 107 | Controller: `Http/Controllers/FlowController.php` 108 | 109 | ```php 110 | $request->input('orden'), 130 | 'monto' => $request->input('monto'), 131 | 'concepto' => $request->input('concepto'), 132 | 'email_pagador' => $request->input('pagador'), 133 | 134 | // Opcional: Medio de Pago (Webpay = 1, Servipag = 2, Multicaja = 3, Todos = 9) 135 | //'medio_pago' => $request->input('medio_pago'), 136 | ]; 137 | 138 | // Genera una nueva orden de pago, Flow la firma y retorna un paquete de datos firmados 139 | $orden['flow_pack'] = Flow::new_order($orden['orden_compra'], $orden['monto'], $orden['concepto'], $orden['email_pagador']); 140 | 141 | // Si desea enviar el medio de pago usar la siguiente línea 142 | //$orden['flow_pack'] = Flow::new_order($orden['orden_compra'], $orden['monto'], $orden['concepto'], $orden['email_pagador'], $orden['medio_pago']); 143 | 144 | return view('orden', $orden); 145 | } 146 | // ... 147 | ``` 148 | 149 | View: `resources/views/orden.blade.php` 150 | 151 | ```blade 152 | @extends('layouts._master') 153 | 154 | @section('content') 155 | 156 | Confirme su orden antes de proceder al pago vía Flow
157 |
158 | Orden n°: {{ $orden_compra }}
159 | Monto: {{ $monto }}
160 | Descripción: {{ $concepto }}
161 | Email pagador (opcional): {{ $email_pagador }}
162 |
163 |
164 | 165 | 166 | 167 |
168 | @endsection 169 | ``` 170 | 171 | ### Página de éxito 172 | 173 | Controller: `Http/Controllers/FlowController.php` 174 | 175 | ```php 176 | // ... 177 | /** 178 | * Página de éxito del comercio 179 | * 180 | * Esta página será invocada por Flow cuando la transacción resulte exitosa 181 | * y el usuario presione el botón para retornar al comercio desde Flow. 182 | * 183 | * @return \Illuminate\View\View 184 | */ 185 | public function exito() 186 | { 187 | // Lee los datos enviados por Flow 188 | Flow::read_result(); 189 | 190 | // Recupera los datos enviados por Flow 191 | $orden = [ 192 | 'orden_compra' => Flow::getOrderNumber(), 193 | 'monto' => Flow::getAmount(), 194 | 'concepto' => Flow::getConcept(), 195 | 'email_pagador' => Flow::getPayer(), 196 | 'flow_orden' => Flow::getFlowNumber(), 197 | ]; 198 | 199 | return view('flow.exito', $orden); 200 | } 201 | // ... 202 | ``` 203 | 204 | View: `resources/views/flow/exito.blade.php` 205 | 206 | ```blade 207 | @extends('layouts._master') 208 | 209 | @section('content') 210 |

Página de éxito de comercio

211 | Su pago se ha realizado con éxito
212 |
213 | Orden de compra: {{ $orden_compra }}
214 | Monto: {{ $monto }}
215 | Descripción: {{ $concepto }}
216 | Pagador: {{ $email_pagador }}
217 | Flow orden n°: {{ $flow_orden }}
218 |
219 | Gracias por su compra 220 | @endsection 221 | ``` 222 | 223 | ### Página de fracaso 224 | 225 | Controller: `Http/Controllers/FlowController.php` 226 | 227 | ```php 228 | // ... 229 | /** 230 | * Página de fracaso del comercio 231 | * 232 | * Esta página será invocada por Flow cuando la transacción no se logre pagar 233 | * y el usuario presione el botón para retornar al comercio desde Flow. 234 | * 235 | * @return \Illuminate\View\View 236 | */ 237 | public function fracaso() 238 | { 239 | // Lee los datos enviados por Flow 240 | Flow::read_result(); 241 | 242 | // Recupera los datos enviados por Flow 243 | $orden = [ 244 | 'orden_compra' => Flow::getOrderNumber(), 245 | 'monto' => Flow::getAmount(), 246 | 'concepto' => Flow::getConcept(), 247 | 'email_pagador' => Flow::getPayer(), 248 | 'flow_orden' => Flow::getFlowNumber(), 249 | ]; 250 | 251 | return view('flow.fracaso', $orden); 252 | } 253 | // ... 254 | ``` 255 | 256 | View: `resources/views/flow/fracaso.blade.php` 257 | 258 | ```blade 259 | @extends('layouts._master') 260 | 261 | @section('content') 262 |

Página de fracaso de comercio

263 | Su pago ha sido rechazado
264 |
265 | Orden de compra: {{ $orden_compra }}
266 | Monto: {{ $monto }}
267 | Descripción: {{ $concepto }}
268 | Pagador: {{ $email_pagador }}
269 | Flow orden n°: {{ $flow_orden }}
270 |
271 | Intente nuevamente 272 | @endsection 273 | ``` 274 | 275 | ### Página de confirmación 276 | 277 | Controller: `Http/Controllers/FlowController.php` 278 | 279 | ```php 280 | // ... 281 | /** 282 | * Página de confirmación del comercio 283 | * 284 | * @return void 285 | */ 286 | public function confirmacion() 287 | { 288 | try { 289 | // Lee los datos enviados por Flow 290 | Flow::read_confirm(); 291 | } catch (Exception $e) { 292 | // Si hay un error responde false 293 | echo Flow::build_response(false); 294 | return; 295 | } 296 | 297 | // Recupera los valores de la orden 298 | $flow_status = Flow::getStatus(); // El resultado de la transacción (EXITO o FRACASO) 299 | $orden_numero = Flow::getOrderNumber(); // N° de orden del comercio 300 | $monto = Flow::getAmount(); // Monto de la transacción 301 | $orden_flow = Flow::getFlowNumber(); // Si $flow_status = 'EXITO' el n° de orden de Flow 302 | $pagador = Flow::getPayer(); // El email del pagador 303 | 304 | /** 305 | * Aquí puede validar la orden 306 | * 307 | * Si acepta la orden responder Flow::build_response(true) 308 | * Si rechaza la orden responder Flow::build_response(false) 309 | */ 310 | if ($flow_status == 'EXITO') { 311 | // La transacción fue aceptada por Flow 312 | // Aquí puede actualizar su información con los datos recibidos por Flow 313 | echo Flow::build_response(true); // Comercio acepta la transacción 314 | } else { 315 | // La transacción fue rechazada por Flow 316 | // Aquí puede actualizar su información con los datos recibidos por Flow 317 | echo Flow::build_response(false); // Comercio rechaza la transacción 318 | } 319 | } 320 | // ... 321 | ``` 322 | 323 | ### Routes 324 | 325 | ```php 326 | // ... 327 | Route::get('/', function () { 328 | return view('index'); 329 | }); 330 | Route::post('orden', 'FlowController@orden')->name('orden'); 331 | 332 | Route::post('flow/exito', 'FlowController@exito')->name('flow.exito'); 333 | Route::post('flow/fracaso', 'FlowController@fracaso')->name('flow.fracaso'); 334 | Route::post('flow/confirmacion', 'FlowController@confirmacion')->name('flow.confirmacion'); 335 | // ... 336 | ``` 337 | 338 | ## Licencia 339 | 340 | Este package está licenciado bajo los términos de la [Licencia MIT](LICENSE). 341 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cokecancino/laravel-flow", 3 | "description": "Laravel package para integrar pagos con Flow (https://www.flow.cl)", 4 | "keywords": ["laravel", "flow", "pagos", "webpay", "transbank", "servipag", "multicaja", "chile"], 5 | "homepage": "https://github.com/jorgecancinof/laravel-flow", 6 | "license": "MIT", 7 | "abandoned": true, 8 | "authors": [ 9 | { 10 | "name": "Jorge Cancino Flores", 11 | "email": "jorgecancinof@gmail.com" 12 | } 13 | ], 14 | "require": { 15 | "php": ">=5.4.0", 16 | "illuminate/support": "~5.0" 17 | }, 18 | "autoload": { 19 | "psr-4": { 20 | "CokeCancino\\LaravelFlow\\": "src/" 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Facades/Flow.php: -------------------------------------------------------------------------------- 1 | __construct(); 22 | } 23 | 24 | //Constructor de la clase 25 | function __construct() { 26 | //global $flow_medioPago; 27 | $this->order["OrdenNumero"] = ""; 28 | $this->order["Concepto"] = ""; 29 | $this->order["Monto"] = ""; 30 | $this->order["MedioPago"] = config('flow.medioPago'); 31 | $this->order["FlowNumero"] = ""; 32 | $this->order["Pagador"] = ""; 33 | $this->order["Status"] = ""; 34 | $this->order["Error"] = ""; 35 | } 36 | 37 | // Metodos SET 38 | 39 | /** 40 | * Set el número de Orden del comercio 41 | * 42 | * @param string $orderNumer El número de la Orden del Comercio 43 | * 44 | * @return bool (true/false) 45 | */ 46 | public function setOrderNumber($orderNumber) { 47 | if(!empty($orderNumber)) { 48 | $this->order["OrdenNumero"] = $orderNumber; 49 | } 50 | $this->flow_log("Asigna Orden N°: ". $this->order["OrdenNumero"], ''); 51 | return !empty($orderNumber); 52 | } 53 | 54 | /** 55 | * Set el concepto de pago 56 | * 57 | * @param string $concepto El concepto del pago 58 | * 59 | * @return bool (true/false) 60 | */ 61 | public function setConcept($concepto) { 62 | if(!empty($concepto)) { 63 | $this->order["Concepto"] = $concepto; 64 | } 65 | return !empty($concepto); 66 | } 67 | 68 | /** 69 | * Set el monto del pago 70 | * 71 | * @param string $monto El monto del pago 72 | * 73 | * @return bool (true/false) 74 | */ 75 | public function setAmount($monto) { 76 | if(!empty($monto)) { 77 | $this->order["Monto"] = $monto; 78 | } 79 | return !empty($monto); 80 | } 81 | 82 | /** 83 | * Set Medio de Pago, por default el Medio de Pago será el configurada en config.php 84 | * 85 | * @param string $medio El Medio de Pago de esta orden 86 | * 87 | * @return bool (true/false) 88 | */ 89 | public function setMedio($medio) { 90 | if(!empty($medio)) { 91 | $this->order["MedioPago"] = $medio; 92 | return TRUE; 93 | } else { 94 | return FALSE; 95 | } 96 | } 97 | 98 | /** 99 | * Set pagador, el email del pagador de esta orden 100 | * 101 | * @param string $email El email del pagador de la orden 102 | * 103 | * @return bool (true/false) 104 | */ 105 | public function setPagador($email) { 106 | if(!empty($email)) { 107 | $this->order["Pagador"] = $email; 108 | return TRUE; 109 | } else { 110 | return FALSE; 111 | } 112 | } 113 | 114 | 115 | // Metodos GET 116 | 117 | /** 118 | * Get el número de Orden del Comercio 119 | * 120 | * @return string el número de Orden del comercio 121 | */ 122 | public function getOrderNumber() { 123 | return $this->order["OrdenNumero"]; 124 | } 125 | 126 | /** 127 | * Get el concepto de Orden del Comercio 128 | * 129 | * @return string el concepto de Orden del comercio 130 | */ 131 | public function getConcept() { 132 | return $this->order["Concepto"]; 133 | } 134 | 135 | /** 136 | * Get el monto de Orden del Comercio 137 | * 138 | * @return string el monto de la Orden del comercio 139 | */ 140 | public function getAmount() { 141 | return $this->order["Monto"]; 142 | } 143 | 144 | /** 145 | * Get el Medio de Pago para de Orden del Comercio 146 | * 147 | * @return string el Medio de pago de esta Orden del comercio 148 | */ 149 | public function getMedio() { 150 | return $this->order["MedioPago"]; 151 | } 152 | 153 | /** 154 | * Get el estado de la Orden del Comercio 155 | * 156 | * @return string el estado de la Orden del comercio 157 | */ 158 | public function getStatus() { 159 | return $this->order["Status"]; 160 | } 161 | 162 | /** 163 | * Get el número de Orden de Flow 164 | * 165 | * @return string el número de la Orden de Flow 166 | */ 167 | public function getFlowNumber() { 168 | return $this->order["FlowNumero"]; 169 | } 170 | 171 | /** 172 | * Get el email del pagador de la Orden 173 | * 174 | * @return string el email del pagador de la Orden de Flow 175 | */ 176 | public function getPayer() { 177 | return $this->order["Pagador"]; 178 | } 179 | 180 | 181 | /** 182 | * Crea una nueva Orden para ser enviada a Flow 183 | * 184 | * @param string $orden_compra El número de Orden de Compra del Comercio 185 | * @param string $monto El monto de Orden de Compra del Comercio 186 | * @param string $concepto El concepto de Orden de Compra del Comercio 187 | * @param string $email_pagador El email del pagador de Orden de Compra del Comercio 188 | * @param mixed $medioPago El Medio de Pago (1,2,9) 189 | * 190 | * @return string flow_pack Paquete de datos firmados listos para ser enviados a Flow 191 | */ 192 | public function new_order($orden_compra, $monto, $concepto, $email_pagador, $medioPago = "Non") { 193 | //global $flow_medioPago; 194 | $this->flow_log("Iniciando nueva Orden", "new_order"); 195 | if(!isset($orden_compra,$monto,$concepto)) { 196 | $this->flow_log("Error: No se pasaron todos los parámetros obligatorios","new_order"); 197 | } 198 | if($medioPago == "Non") { 199 | $medioPago = config('flow.medioPago'); 200 | } 201 | if(!is_numeric($monto)) { 202 | $this->flow_log("Error: El parámetro monto de la orden debe ser numérico","new_order"); 203 | throw new Exception("El monto de la orden debe ser numérico"); 204 | } 205 | $this->order["OrdenNumero"] = $orden_compra; 206 | $this->order["Concepto"] = $concepto; 207 | $this->order["Monto"] = $monto; 208 | $this->order["MedioPago"] = $medioPago; 209 | $this->order["Pagador"] = $email_pagador; 210 | return $this->flow_pack(); 211 | } 212 | 213 | /** 214 | * Lee los datos enviados desde Flow a la página de confirmación del comercio 215 | * 216 | */ 217 | public function read_confirm() { 218 | if(!isset($_POST['response'])) { 219 | $this->flow_log("Respuesta Inválida", "read_confirm"); 220 | throw new Exception('Invalid response'); 221 | } 222 | $data = $_POST['response']; 223 | $params = array(); 224 | parse_str($data, $params); 225 | if(!isset($params['status'])) { 226 | $this->flow_log("Respuesta sin status", "read_confirm"); 227 | throw new Exception('Invalid response status'); 228 | } 229 | $this->order['Status'] = $params['status']; 230 | $this->flow_log("Lee Status: " . $params['status'], "read_confirm"); 231 | if (!isset($params['s'])) { 232 | $this->flow_log("Mensaje no tiene firma", "read_confirm"); 233 | throw new Exception('Invalid response (no signature)'); 234 | } 235 | if(!$this->flow_sign_validate($params['s'], $data)) { 236 | $this->flow_log("firma invalida", "read_confirm"); 237 | throw new Exception('Invalid signature from Flow'); 238 | } 239 | $this->flow_log("Firma verificada", "read_confirm"); 240 | if($params['status'] == "ERROR") { 241 | $this->flow_log("Error: " .$params['kpf_error'], "read_confirm"); 242 | $this->order["Error"] = $params['kpf_error']; 243 | return; 244 | } 245 | if(!isset($params['kpf_orden'])) { 246 | throw new Exception('Invalid response Orden number'); 247 | } 248 | $this->order['OrdenNumero'] = $params['kpf_orden']; 249 | $this->flow_log("Lee Numero Orden: " . $params['kpf_orden'], "read_confirm"); 250 | if(!isset($params['kpf_monto'])) { 251 | throw new Exception('Invalid response Amount'); 252 | } 253 | $this->order['Monto'] = $params['kpf_monto']; 254 | $this->flow_log("Lee Monto: " . $params['kpf_monto'], "read_confirm"); 255 | if(isset($params['kpf_flow_order'])) { 256 | $this->order['FlowNumero'] = $params['kpf_flow_order']; 257 | $this->flow_log("Lee Orden Flow: " . $params['kpf_flow_order'], "read_confirm"); 258 | } 259 | if(isset($params['kpf_pagador'])) { 260 | $this->order['Pagador'] = $params['kpf_pagador']; 261 | } 262 | 263 | } 264 | 265 | /** 266 | * Método para responder a Flow el resultado de la confirmación del comercio 267 | * 268 | * @param bool $result (true: Acepta el pago, false rechaza el pago) 269 | * 270 | * @return string paquete firmado para enviar la respuesta del comercio 271 | */ 272 | public function build_response($result){ 273 | //global $flow_comercio; 274 | $r = ($result) ? "ACEPTADO" : "RECHAZADO"; 275 | $data = array(); 276 | $data["status"] = $r; 277 | $data["c"] = config('flow.comercio'); 278 | $q = http_build_query($data); 279 | $s = $this->flow_sign($q); 280 | $this->flow_log("Orden N°: ".$this->order["OrdenNumero"]. " - Status: $r","flow_build_response"); 281 | return $q."&s=".$s; 282 | } 283 | 284 | /** 285 | * Método para recuperar los datos en la página de Exito o Fracaso del Comercio 286 | * 287 | */ 288 | public function read_result() { 289 | if(!isset($_POST['response'])) { 290 | $this->flow_log("Respuesta Inválida", "read_result"); 291 | throw new Exception('Invalid response'); 292 | } 293 | $data = $_POST['response']; 294 | $params = array(); 295 | parse_str($data, $params); 296 | if (!isset($params['s'])) { 297 | $this->flow_log("Mensaje no tiene firma", "read_result"); 298 | throw new Exception('Invalid response (no signature)'); 299 | } 300 | if(!$this->flow_sign_validate($params['s'], $data)) { 301 | $this->flow_log("firma invalida", "read_result"); 302 | throw new Exception('Invalid signature from Flow'); 303 | } 304 | //$this->order["Comision"] = $flow_tasa_default; 305 | $this->order["Status"] = ""; 306 | $this->order["Error"] = ""; 307 | $this->order['OrdenNumero'] = $params['kpf_orden']; 308 | $this->order['Concepto'] = $params['kpf_concepto']; 309 | $this->order['Monto'] = $params['kpf_monto']; 310 | $this->order["FlowNumero"] = $params["kpf_flow_order"]; 311 | $this->order["Pagador"] = $params["kpf_pagador"]; 312 | $this->flow_log("Datos recuperados Orden de Compra N°: " .$params['kpf_orden'], "read_result"); 313 | } 314 | 315 | /** 316 | * Registra en el Log de Flow 317 | * 318 | * @param string $message El mensaje a ser escrito en el log 319 | * @param string $type Identificador del mensaje 320 | * 321 | */ 322 | public function flow_log($message, $type) { 323 | //global $flow_logPath; 324 | $file = fopen(config('flow.logPath') . "/flowLog_" . date("Y-m-d") .".txt" , "a+"); 325 | fwrite($file, "[".date("Y-m-d H:i:s.u")." ".getenv('REMOTE_ADDR')." ".getenv('HTTP_X_FORWARDED_FOR')." - $type ] ".$message . PHP_EOL); 326 | fclose($file); 327 | } 328 | 329 | 330 | // Funciones Privadas 331 | private function flow_get_public_key_id() { 332 | //global $flow_keys; 333 | try { 334 | $fp = fopen(__DIR__."/keys/flow.pubkey", "r"); 335 | $pub_key = fread($fp, 8192); 336 | fclose($fp); 337 | return openssl_get_publickey($pub_key); 338 | } catch (Exception $e) { 339 | $this->flow_log("Error al intentar obtener la llave pública - Error-> " .$e->getMessage(), "flow_get_public_key_id"); 340 | throw new Exception($e->getMessage()); 341 | } 342 | } 343 | 344 | private function flow_get_private_key_id() { 345 | //global $flow_keys; 346 | try { 347 | $fp = fopen(config('flow.keys')."/comercio.pem", "r"); 348 | $priv_key = fread($fp, 8192); 349 | fclose($fp); 350 | return openssl_get_privatekey($priv_key); 351 | } catch (Exception $e) { 352 | $this->flow_log("Error al intentar obtener la llave privada - Error-> " .$e->getMessage(), "flow_get_private_key_id"); 353 | throw new Exception($e->getMessage()); 354 | } 355 | } 356 | 357 | private function flow_sign($data) { 358 | $priv_key_id = $this->flow_get_private_key_id(); 359 | if(! openssl_sign($data, $signature, $priv_key_id)) { 360 | $this->flow_log("No se pudo firmar", "flow_sign"); 361 | throw new Exception('It can not sign'); 362 | }; 363 | return base64_encode($signature); 364 | } 365 | 366 | private function flow_sign_validate($signature, $data) { 367 | 368 | $signature = base64_decode($signature); 369 | $response = explode("&s=", $data, 2); 370 | $response = $response[0]; 371 | 372 | $pub_key_id = $this->flow_get_public_key_id(); 373 | return (openssl_verify($response, $signature, $pub_key_id) == 1); 374 | } 375 | 376 | private function flow_pack() { 377 | //global $flow_comercio, $flow_url_exito, $flow_url_fracaso, $flow_url_confirmacion, $flow_tipo_integracion, $flow_url_retorno; 378 | $tipo_integracion = urlencode(config('flow.tipo_integracion')); 379 | $comercio = urlencode(config('flow.comercio')); 380 | $orden_compra = urlencode($this->order["OrdenNumero"]); 381 | $monto = urlencode($this->order["Monto"]); 382 | $medioPago = urlencode($this->order["MedioPago"]); 383 | $email = urlencode($this->order["Pagador"]); 384 | 385 | $hConcepto = htmlentities($this->order["Concepto"]); 386 | if (!$hConcepto) $hConcepto = htmlentities($concepto, ENT_COMPAT | ENT_HTML401, 'UTF-8'); 387 | if (!$hConcepto) $hConcepto = htmlentities($concepto, ENT_COMPAT | ENT_HTML401, 'ISO-8859-1'); 388 | if (!$hConcepto) $hConcepto = "Orden de Compra $orden_compra"; 389 | 390 | $concepto = urlencode($hConcepto); 391 | 392 | $url_exito = urlencode($this->generarUrl(config('flow.url_exito'))); 393 | $url_fracaso = urlencode($this->generarUrl(config('flow.url_fracaso'))); 394 | $url_confirmacion = urlencode($this->generarUrl(config('flow.url_confirmacion'))); 395 | $url_retorno = urlencode($this->generarUrl(config('flow.url_retorno'))); 396 | 397 | $p = "c=$comercio&oc=$orden_compra&mp=$medioPago&m=$monto&o=$concepto&ue=$url_exito&uf=$url_fracaso&uc=$url_confirmacion&ti=$tipo_integracion&e=$email&v=kit_1_4&ur=$url_retorno"; 398 | 399 | $signature = $this->flow_sign($p); 400 | $this->flow_log("Orden N°: ".$this->order["OrdenNumero"]. " -empaquetado correcto","flow_pack"); 401 | return $p."&s=$signature"; 402 | } 403 | 404 | /** 405 | * Genera una URL utilizando las funciones de Laravel 406 | * 407 | * @param mixed $url 408 | * @return string 409 | */ 410 | private function generarUrl($url) 411 | { 412 | if (is_array($url)) { 413 | if (array_key_exists('url', $url)) 414 | return url($url['url']); 415 | 416 | if (array_key_exists('route', $url)) 417 | return route($url['route']); 418 | 419 | if (array_key_exists('action', $url)) 420 | return action($url['action']); 421 | 422 | return ''; 423 | } else { 424 | return $url; 425 | } 426 | } 427 | } 428 | -------------------------------------------------------------------------------- /src/FlowServiceProvider.php: -------------------------------------------------------------------------------- 1 | publishes([ 24 | __DIR__.'/config/config.php' => config_path('flow.php'), 25 | ]); 26 | } 27 | 28 | /** 29 | * Register the service provider. 30 | * 31 | * @return void 32 | */ 33 | public function register() 34 | { 35 | $this->bindFlowClass(); 36 | } 37 | 38 | private function bindFlowClass() 39 | { 40 | $this->app->bind(Flow::class, function ($app) { 41 | return new Flow(); 42 | }); 43 | } 44 | 45 | /** 46 | * Get the services provided by the provider. 47 | * 48 | * @return array 49 | */ 50 | public function provides() 51 | { 52 | return [Flow::class]; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/config/config.php: -------------------------------------------------------------------------------- 1 | 'flow/exito'], 13 | | ['route' => 'flow.exito'], 14 | | ['action' => 'FlowController@exito'], 15 | | 16 | */ 17 | 18 | 'url_exito' => ['route' => 'flow.exito'], 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Ingrese aquí la URL de su página de fracaso 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Valores posibles: 26 | | 'http://www.comercio.cl/kpf/fracaso.php', 27 | | ['url' => 'flow/fracaso'], 28 | | ['route' => 'flow.fracaso'], 29 | | ['action' => 'FlowController@fracaso'], 30 | | 31 | */ 32 | 33 | 'url_fracaso' => ['route' => 'flow.fracaso'], 34 | 35 | /* 36 | |-------------------------------------------------------------------------- 37 | | Ingrese aquí la URL de su página de confirmación 38 | |-------------------------------------------------------------------------- 39 | | 40 | | Valores posibles: 41 | | 'http://www.comercio.cl/kpf/confirmacion.php', 42 | | ['url' => 'flow/confirmacion'], 43 | | ['route' => 'flow.confirmacion'], 44 | | ['action' => 'FlowController@confirmacion'], 45 | | 46 | */ 47 | 48 | 'url_confirmacion' => ['route' => 'flow.confirmacion'], 49 | 50 | /* 51 | |-------------------------------------------------------------------------- 52 | | Ingrese aquí la URL de su página de retorno 53 | |-------------------------------------------------------------------------- 54 | | 55 | | Valores posibles: 56 | | 'http://www.comercio.cl', 57 | | ['url' => 'flow/retorno'], 58 | | ['route' => 'flow.retorno'], 59 | | ['action' => 'FlowController@retorno'], 60 | | 61 | */ 62 | 63 | 'url_retorno' => ['url' => '/'], 64 | 65 | /* 66 | |-------------------------------------------------------------------------- 67 | | Ingrese aquí la página de pago de Flow 68 | |-------------------------------------------------------------------------- 69 | | 70 | | Ejemplo: 71 | | Sitio de pruebas = http://flow.tuxpan.com/app/kpf/pago.php 72 | | Sitio de producción = https://www.flow.cl/app/kpf/pago.php 73 | | 74 | */ 75 | 76 | 'url_pago' => env('FLOW_URL_PAGO', 'http://flow.tuxpan.com/app/kpf/pago.php'), 77 | 78 | /* 79 | |-------------------------------------------------------------------------- 80 | | Ingrese aquí la ruta (path) de su sitio en donde estará la llave privada 81 | |-------------------------------------------------------------------------- 82 | */ 83 | 84 | 'keys' => base_path('keys'), 85 | 86 | /* 87 | |-------------------------------------------------------------------------- 88 | | Ingrese aquí la ruta (path) de su sitio en donde estarán los archivos de logs 89 | |-------------------------------------------------------------------------- 90 | */ 91 | 92 | 'logPath' => storage_path('logs'), 93 | 94 | /* 95 | |-------------------------------------------------------------------------- 96 | | Ingrese aquí el email con el que está registrado en Flow 97 | |-------------------------------------------------------------------------- 98 | */ 99 | 100 | 'comercio' => env('FLOW_COMERCIO', 'emailFlow@comercio.com'), 101 | 102 | /* 103 | |-------------------------------------------------------------------------- 104 | | Ingrese aquí el medio de pago 105 | |-------------------------------------------------------------------------- 106 | | 107 | | Valores posibles: 108 | | Solo Webpay = 1 109 | | Solo Servipag = 2 110 | | Solo Multicaja = 3 111 | | Todos los medios de pago = 9 112 | | 113 | */ 114 | 115 | 'medioPago' => '9', 116 | 117 | /* 118 | |-------------------------------------------------------------------------- 119 | | Ingrese aquí el modo de acceso 120 | |-------------------------------------------------------------------------- 121 | | 122 | | Valores posibles: 123 | | Mostrar pasarela Flow = f 124 | | Ingresar directamente al medio de pago = d 125 | | 126 | */ 127 | 128 | 'tipo_integracion' => 'f', 129 | 130 | ]; 131 | -------------------------------------------------------------------------------- /src/keys/flow.pubkey: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAx0BVTE0HmmtEDfKydMLF 3 | YrfRsQPY6B8fYeNtl76GHLZyhSl3+MzYKdic9oJbvDm2Co9x6zNgUEzEA1RmjZ9H 4 | yVS5mJEj0VRM2Ydx2kfG33bjmwsWSoasPNnCeIvApYZWRVsKICIVrH5wUqky3Jku 5 | 4grLynflGO0rYYC+mOcqbr+9/tJ/bDX0fFcNFHNz1xRcca32A/jZXd2N4W80vPrD 6 | uUqy5uqaQEIX65EP1y/wmNaM6nK8WprgkX1Qi5xeN7ikaDEROiYLbZedz+FxqBOL 7 | nvETvFAmDWFjQeb0+ppDoA3dZPW7oebnyLALGSJVmia+Ig3OsKFmf6F67ygN+4R/ 8 | gJESLyS92kvGpbJGSc130FKt2wmLhO0YmFNzNF4s01hHgigVuVG6OqNdYCtvNbQH 9 | tIcBnUOUyma3Z0zKAH3lPW4nOljM9uNihG32UNlGeV9d/UmjqvVvLy5MbeEEsEw2 10 | LqXH9cGZVjDxt+Qq5y2Mw2/v0E9v+7CyTkUxhU6iY0xXSpajFnoIAQpCY/NhPGQu 11 | N9cFJxGY1EB7cVp8nZzZIWo7u9lsoEDqG93ugmqA/mFSsevn0qUTJinSbbxjiXyo 12 | 1hU4TLtGY9myYeljO5uoLn1Kps5950U/tilNh8LJALwVniJuO+E+CeQgLuD/lNAv 13 | vlOeisrct5CChy8/+tfL5fcCAwEAAQ== 14 | -----END PUBLIC KEY----- 15 | --------------------------------------------------------------------------------