├── .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 |
5 |
6 |
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 |
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 |
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 |
--------------------------------------------------------------------------------