recurrente 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 716ce762602e9f49f4b374526b10bec58c0c792c
4
+ data.tar.gz: 6a5d4fc2d65dceb23234190ee5cf0aab2c47c039
5
+ SHA512:
6
+ metadata.gz: c23c626585fac94966144472e3935ebd2eac7a7e220f5a3bf6a9c622f3f183eccf23846c7995039645a6321b5129a17efe19dd8d2fd21d03afbd5b63564e97f5
7
+ data.tar.gz: f56e9a9196fb92763aba00020866b07fa95c75e0c4998fb6c5555e230b68d58710042c90f1c10fe7a55f95cd4aa3840d8c99b397bc8fc9e36bfa623e8fb227a5
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in recurrente.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 gabosarmiento
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # Recurrente
2
+
3
+ This is a work in progress that seeks to explore the Payulatam's recurring payments api.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'recurrente'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install recurrente
20
+
21
+ ## Usage
22
+
23
+ This is divided into 5 sections in the same way that is explained in Payu's website.
24
+
25
+ 1. Plans
26
+ 2. Suscriptors
27
+ 3. Credit Cards
28
+ 4. Suscriptions
29
+ 5. Extra charges
30
+
31
+ Before anything you'll need to change the test data within an initializer in the config folder in order to replace these constants:
32
+
33
+ # TEST DATA
34
+ API_URL = "http://stg.api.payulatam.com/payments-api/rest/v4.3"
35
+ API_KEY = "6u39nqhq8ftd0hlvnjfs66eh8c"
36
+ API_LOGIN = "11959c415b33d0c"
37
+ MERCHANT_ID = "500238"
38
+ ACCOUNT = "500538"
39
+
40
+ Notice that the API_URL is pointing to http instead of using a secure protocol. This is just for testing purposes.
41
+
42
+ ## COVERED ENDPOINTS
43
+
44
+ Everything is contained in the same ruby file because I wanted to see every method in one place. Clearly this needs improvement but so far this is what I got.
45
+
46
+ ### PLANS
47
+ #### 1.1 GET - Mostrar todos los planes
48
+ #### 1.2 POST - Crear un nuevo Plan
49
+ #### 1.3 GET - Consultar un plan existente
50
+ #### 1.4 PUT - Actualizar un plan existente
51
+ #### 1.5 DELETE - Borrar un Plan existente
52
+
53
+ ### SUSCRIPTORS
54
+ #### 2.1 POST - Crear un Suscriptor
55
+ #### 2.2 GET - Buscar un Suscriptor
56
+ #### 2.3 PUT - Actualizar un Suscriptor
57
+ #### 2.4 DELETE - Borrar un Suscriptor
58
+
59
+ ### CREDIT CARDS
60
+ #### 3.1 POST - Crear una Tarjeta de Credito a un Suscriptor
61
+ #### 3.2 GET - Buscar una tarjeta de crédito
62
+ #### 3.3 GET - Buscar tarjetas de crédito de un usuario
63
+ #### 3.4 DELETE - Eliminar una tarjeta de crédito
64
+
65
+ ### SUSCRIPTIONS
66
+ #### 4.1 POST - Crear suscripción
67
+ ##### 4.1.1 CON TODOS LOS ELEMENTOS NUEVOS
68
+ ##### 4.1.2 CON TODOS LOS ELEMENTOS EXISTENTES
69
+ ##### 4.1.3 PLAN Y SUSCRIPTOR YA CREADOS Y UNA TARJETA NUEVA
70
+ ##### 4.1.4 CLIENTE Y TARJETA YA CREADOS, CON PLAN NUEVO
71
+ #### 4.2 PUT - Update Suscription Credit Card
72
+ #### 4.3 GET - Buscar una Suscripción
73
+ #### 4.4 GET - Buscar las suscripciones de un Cliente
74
+ #### 4.5 POST - Cancelar una Suscripción. Status CANCELLED
75
+
76
+ ### CHARGES
77
+ #### 5.1 POST - Crear un cargo adicional
78
+ #### 5.2 PUT - Actualizar un cargo adicional
79
+ #### 5.3 GET - Buscar un Cargo Extra por ID
80
+ #### 5.4 GET - Buscar un Cargo Extra por Suscripcion
81
+ #### 5.5 POST - Borrar un cargo de la suscripción
82
+
83
+ ## Development
84
+
85
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
86
+
87
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
88
+
89
+ ## Contributing
90
+
91
+ Bug reports and pull requests are welcome on GitHub at https://github.com/gabosarmiento/recurrente.
92
+
93
+
94
+ ## License
95
+
96
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
97
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "recurrente"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,3 @@
1
+ module Recurrente
2
+ VERSION = "0.1.0"
3
+ end
data/lib/recurrente.rb ADDED
@@ -0,0 +1,917 @@
1
+ require "recurrente/version"
2
+ require "httparty"
3
+ require 'json'
4
+ require 'base64'
5
+
6
+ # TEST DATA
7
+ API_URL = "http://stg.api.payulatam.com/payments-api/rest/v4.3"
8
+ API_KEY = "6u39nqhq8ftd0hlvnjfs66eh8c"
9
+ API_LOGIN = "11959c415b33d0c"
10
+ MERCHANT_ID = "500238"
11
+ ACCOUNT = "500538"
12
+
13
+
14
+ module Recurrente
15
+ include HTTParty
16
+ base_uri API_URL
17
+ headers = {
18
+ 'Content-type' => 'application/json;charset=UTF-8',
19
+ 'Accept' => 'application/json',
20
+ 'Accept-language' => 'es'
21
+ }
22
+ # No es necesario encodear con base64 ya que httparty lo hace por debajo
23
+ basic_auth API_LOGIN, API_KEY
24
+ default_timeout 4 # hard timeout after 4 seconds
25
+
26
+ attr_accessor :auth
27
+ @auth = "Basic " + Base64.encode64("#{API_LOGIN}:#{API_KEY}")
28
+ # Una buena practica de consumir una API es definir los timeouts que podrían
29
+ # acumularse y tumbar la pagina al tener los servidores atentidendo las peticiones
30
+ # HTTParty sacará un Net::OpenTimeout si no puede conectarse al servidor y un
31
+ # Net::ReadTimeout si al leer la respuesta del servidor se cuelga.
32
+
33
+ def self.handle_timeouts
34
+ begin
35
+ yield
36
+ rescue Net::OpenTimeout, Net::ReadTimeout
37
+ {}
38
+ end
39
+ end
40
+
41
+ ########################################################################
42
+ #  ****** 1 - PLANES ****** #
43
+ #########################################################################
44
+ # Mediante esta funcionalidad puedes crear el plan de pagos de una #
45
+ # una suscripción con las debidas restricciones y su recurrencia #
46
+ #########################################################################
47
+
48
+ # 1.1 GET - Mostrar todos los planes
49
+ def self.find_plans
50
+ handle_timeouts do
51
+ response = get("/plans")
52
+ JSON.pretty_generate(response.parsed_response)
53
+ end
54
+ end
55
+
56
+ # 1.2 POST - Crear un nuevo Plan
57
+ def self.create_plan(codigo, descripcion, intervalo, cantidad_intervalos, moneda, valor, impuesto, base_impuesto, reintentos, dias_entre_reintentos, cobros,periodo_de_gracia)
58
+ #######################################################################################################################
59
+ # Recurrente.create_plan("Netflix-001", "Plan semanal", "WEEK", "1", "COP", "14000", "0", "0", "2", "2", "12", "2") #
60
+ # codigo Es el nombre con el que se manipula el plan. #
61
+ # descripcion Es la descripción del Plan #
62
+ # intervalo Es la frecuencia con la que se repite el cobro: DAY, WEEK, MONTH, YEAR. #
63
+ # cantidad_intervalos Es lo que define cada cuanto se realiza el cobro de la suscripción #
64
+ # moneda Es el código para definir las divisas según el estándar internacional ISO 4217 #
65
+ # valor Es el valor del plan #
66
+ # base_impuesto Es el valor para calcular la devolución del impuesto #
67
+ # impuesto Es el valor del impuesto #
68
+ # reintentos Es la cantidad de intentos antes de ser rechazado el pago #
69
+ # cobros Es la cantidad máxima de pagos que espera el plan #
70
+ # periodo de gracia Es la cantidad máxima de pagos pendientes antes de cancelada #
71
+ # dias_entre_reintentos Es la cantidad de días de espera entre los reintentos. #
72
+ #######################################################################################################################
73
+
74
+ handle_timeouts do
75
+ headers = {
76
+ 'Content-type' => 'application/json;charset=UTF-8',
77
+ 'Accept' => 'application/json',
78
+ 'Accept-language' => 'es',
79
+ 'Authorization' => @auth
80
+ }
81
+ params = {
82
+ "accountId": ACCOUNT,
83
+ "planCode": codigo,
84
+ "description": descripcion,
85
+ "interval": intervalo,
86
+ "intervalCount": cantidad_intervalos ,
87
+ "maxPaymentsAllowed": cobros,
88
+ "paymentAttemptsDelay": dias_entre_reintentos,
89
+ "maxPaymentAttempts": reintentos,
90
+ "maxPendingPayments": periodo_de_gracia,
91
+ "additionalValues": [
92
+ {
93
+ "name": "PLAN_VALUE",
94
+ "value": valor,
95
+ "currency": moneda
96
+ },
97
+ {
98
+ "name": "PLAN_TAX",
99
+ "value": impuesto,
100
+ "currency": moneda
101
+ },
102
+ {
103
+ "name": "PLAN_TAX_RETURN_BASE",
104
+ "value": base_impuesto,
105
+ "currency": moneda
106
+ }
107
+ ]
108
+ }
109
+ # Es necesario incluir los encabezados de otra forma lo identifica de otro typo distinto a json
110
+
111
+
112
+ response = post("/plans", body: params.to_json, headers: headers)
113
+
114
+ case response.code
115
+ when 200..202
116
+ puts JSON.pretty_generate(response.parsed_response)
117
+ retrieve_plan response
118
+ when 404
119
+ response.message
120
+ when 500..600
121
+ puts "OMG ERROR #{response.code}"
122
+ else
123
+ response
124
+ end
125
+ end # <-- Timeout Block
126
+ end
127
+
128
+ # 1.3 GET - Consultar un plan existente
129
+ def self.find_plan(plan_code)
130
+ handle_timeouts do
131
+ response = get("/plans" + "/#{plan_code}")
132
+ puts JSON.pretty_generate(response.parsed_response)
133
+ response
134
+ end
135
+ end
136
+
137
+ # 1.4 PUT - Actualizar un plan existente
138
+
139
+ def self.update_plan(plan_code, codigo, descripcion, valor, impuesto, base_impuesto, reintentos, periodo_de_gracia, dias_entre_reintentos)
140
+ # Atributos eliminados cuenta, intervalo, cantidad de intervalos, cobros, moneda
141
+ handle_timeouts do
142
+ headers = {
143
+ 'Content-type' => 'application/json;charset=UTF-8',
144
+ 'Accept' => 'application/json',
145
+ 'Accept-language' => 'es',
146
+ 'Authorization' => @auth
147
+ }
148
+ params = {
149
+ "planCode": codigo,
150
+ "description": descripcion,
151
+ "paymentAttemptsDelay": dias_entre_reintentos,
152
+ "maxPaymentAttempts": reintentos,
153
+ "maxPendingPayments": periodo_de_gracia,
154
+ "additionalValues": [
155
+ {
156
+ "name": "PLAN_VALUE",
157
+ "value": valor
158
+ },
159
+ {
160
+ "name": "PLAN_TAX",
161
+ "value": impuesto
162
+ },
163
+ {
164
+ "name": "PLAN_TAX_RETURN_BASE",
165
+ "value": base_impuesto
166
+ }
167
+ ]
168
+ }
169
+ # Es necesario incluir los encabezados de otra forma lo identifica de otro typo distinto a json
170
+
171
+
172
+ response = put("/plans" + "/#{plan_code}", body: params.to_json, headers: headers)
173
+
174
+
175
+ case response.code
176
+ when 200..202
177
+ puts JSON.pretty_generate(response.parsed_response)
178
+ retrieve_plan response
179
+ when 404
180
+ response.message
181
+ when 500..600
182
+ puts "OMG ERROR #{response.code}"
183
+ else
184
+ response
185
+ end
186
+ end # <-- Timeout block
187
+ end
188
+
189
+ # 1.5 DELETE - Borrar un Plan existente
190
+
191
+ def self.delete_plan(plan_code)
192
+ handle_timeouts do
193
+ response = delete("/plans" + "/#{plan_code}")
194
+ end
195
+ end
196
+
197
+ #####################################################################
198
+ #  ****** 2 - SUSCRIPTORES ****** #
199
+ #####################################################################
200
+ # Un cliente es la unidad que identifica quien será el beneficiario #
201
+ # de un producto o servicio prestado y que se encuentra asociado a #
202
+ # un plan de pagos recurrentes. #
203
+ #####################################################################
204
+
205
+ # 2.1 POST - Crear un Suscriptor
206
+ def self.create_suscriptor(name,email)
207
+ handle_timeouts do
208
+ headers = {
209
+ 'Content-type' => 'application/json;charset=UTF-8',
210
+ 'Accept' => 'application/json',
211
+ 'Accept-language' => 'es',
212
+ 'Authorization' => @auth
213
+ }
214
+
215
+ params = {
216
+ "fullName": name,
217
+ "email": email
218
+ }
219
+
220
+ response = post('/customers', body: params.to_json, headers: headers)
221
+
222
+ case response.code
223
+ when 200..202
224
+ puts response
225
+ puts JSON.pretty_generate(response.parsed_response)
226
+ retrieve_customer response
227
+ when 404
228
+ response.message
229
+ when 500..600
230
+ puts "OMG ERROR #{response.code}"
231
+ else
232
+ response
233
+ end
234
+ end
235
+ end
236
+
237
+ # 2.2 GET - Buscar un Suscriptor
238
+ def self.find_suscriptor(customer_id)
239
+ handle_timeouts do
240
+ response = get("/customers" + "/#{customer_id}")
241
+
242
+ case response.code
243
+ when 200..202
244
+ puts JSON.pretty_generate(response.parsed_response)
245
+ response.parsed_response["customer"] # Retorna el hash del suscriptor
246
+ when 404
247
+ response.message
248
+ when 500..600
249
+ puts "OMG ERROR #{response.code}"
250
+ else
251
+ response
252
+ end
253
+ end
254
+ end
255
+
256
+ # 2.3 PUT - Actualizar un Suscriptor
257
+ def self.update_suscriptor(customer_id,name,email)
258
+ handle_timeouts do
259
+ headers = {
260
+ 'Content-type' => 'application/json;charset=UTF-8',
261
+ 'Accept' => 'application/json',
262
+ 'Accept-language' => 'es',
263
+ 'Authorization' => @auth
264
+ }
265
+
266
+ params = {
267
+ "fullName": name,
268
+ "email": email
269
+ }
270
+
271
+ response = put('/customers'+ "/#{customer_id}", body: params.to_json, headers: headers)
272
+
273
+
274
+ case response.code
275
+ when 200..202
276
+ puts JSON.pretty_generate(response.parsed_response)
277
+ retrieve_customer response
278
+ when 404
279
+ response.message
280
+ when 500..600
281
+ puts "OMG ERROR #{response.code}"
282
+ else
283
+ response
284
+ end
285
+ end
286
+ end
287
+ # 2.4 DELETE - Borrar un Suscriptor
288
+ def self.delete_suscriptor(customer_id)
289
+ handle_timeouts do
290
+ response = delete("/customers" + "/#{customer_id}")
291
+ case response.code
292
+ when 200..202
293
+ puts response
294
+ response.parsed_response["response"]["description"]
295
+ when 404
296
+ response.message
297
+ when 500..600
298
+ puts "OMG ERROR #{response.code}"
299
+ else
300
+ response
301
+ end
302
+ end
303
+ end
304
+
305
+ #########################################################################
306
+ #  ****** 3 - TARJETAS DE CREDITO ****** #
307
+ #########################################################################
308
+ # Es el medio de pago con el cual se relaciona un Plan y un Pagador, #
309
+ # se encuentra compuesto por el número de tarjeta de crédito #
310
+ # (el cual será tokenizado para almacenar los datos de forma segura), #
311
+ # la fecha de vencimiento de la tarjeta y algunos datos adicionales #
312
+ # de dirección. #
313
+ #########################################################################
314
+
315
+ # 3.1 POST - Crear una Tarjeta de Credito a un Suscriptor
316
+
317
+ def self.create_card(customer_id, numero , exp_mes, exp_ano ,tipo, nombre, documento, dir_linea1, dir_linea2 , dir_linea3 , departamento, ciudad, pais, codigo_postal, telefono)
318
+ # Recurrente.add_customer_card(cliente, "371449635398431","01", "2018","AMEX","Pablo Picasso","1020304050","Calle Falsa","123","Patio 1","Bogotá","Bogotá D.C.", "CO", "110221", "3103456789")
319
+
320
+ #############################################################################################################
321
+ # customer_id Campo para la URL no va en la petición. Código de identificación del cliente. #
322
+ # numero Número de la tarjeta Ej: "4242424242424242" #
323
+ # exp_mes Mes de expiración - Mínimo 1 máximo 12. Ej: "01" #
324
+ # exp_ano Año de expiración de la tarjeta. Ej: "2018" #
325
+ # tipo Franquicia de la tarjeta VISA, AMEX, DINERS, MASTERCARD. Ej: "VISA" #
326
+ # nombre Nombre del tarjeta habiente: Ej: "Pedrito Fernandez" #
327
+ # documento Documento de identificación. Ej: "1020304050" #
328
+ # dir_linea1 Línea 1 de dirección opcional de correspondencia. Ej: "Calle Falsa" #
329
+ # dir_linea2 Línea 2 de dirección opcional de correspondencia. Ej: "123" #
330
+ # dir_linea3 Línea 3 de dirección opcional de correspondencia. Ej: "Patio trasero" #
331
+ # departamento Nombre de departamento. Ej: "Bogotá" #
332
+ # ciudad Nombre de ciudad. Ej: "Bogotá D.C." #
333
+ # pais Dos letras del país según el Código ISO 3166. Ej: "CO" #
334
+ # codigo_postal Código de la dirección #
335
+ # telefono Teléfono asociado con la dirección #
336
+ #############################################################################################################
337
+ handle_timeouts do
338
+ headers = {
339
+ 'Content-Type' => 'application/json; charset=UTF-8',
340
+ 'Accept' => "application/json",
341
+ 'Accept-language' => 'es',
342
+ 'Authorization' => @auth
343
+ }
344
+ params = {
345
+ "name": nombre,
346
+ "document": documento,
347
+ "number": numero,
348
+ "expMonth": exp_mes,
349
+ "expYear": exp_ano,
350
+ "type": tipo,
351
+ "address": {
352
+ "line1": dir_linea1,
353
+ "line2": dir_linea2,
354
+ "line3": dir_linea3,
355
+ "postalCode": codigo_postal,
356
+ "city": ciudad,
357
+ "state": departamento,
358
+ "country": pais,
359
+ "phone": telefono
360
+ }
361
+ }
362
+
363
+ response = post('/customers' + "/#{customer_id}/creditCards", body: JSON.generate(params), headers: headers)
364
+ case response.code
365
+ when 200
366
+ response
367
+ when 201
368
+ puts JSON.pretty_generate(response.parsed_response)
369
+ retrieve_credit_card response
370
+ when 404
371
+ response.message
372
+ when 500..600
373
+ puts "OMG ERROR #{response.code}"
374
+ else
375
+ response
376
+ end
377
+ end
378
+ end
379
+
380
+ # 3.2 GET - Buscar una tarjeta de crédito
381
+ def self.find_card(token)
382
+ handle_timeouts do
383
+ response = get("/creditCards" + "/#{token}")
384
+ puts JSON.pretty_generate(response.parsed_response)
385
+ case response.code
386
+ when 200
387
+ response.parsed_response["creditCard"]
388
+ else
389
+ response
390
+ end
391
+ end
392
+ end
393
+
394
+ # 3.3 GET - Buscar tarjetas de crédito de un usuario
395
+
396
+ def self.find_cards_by_suscriptor(customer_id)
397
+ handle_timeouts do
398
+ response = get("/customers"+ "/#{customer_id}/creditCards")
399
+ case response.code
400
+ when 200
401
+ puts JSON.pretty_generate(response.parsed_response)
402
+ response.parsed_response["creditCardListResponse"]["creditCards"]["creditCard"]
403
+ # Retorna el hash de las tarjetas de crédito
404
+ when 404
405
+ response.message
406
+ when 500..600
407
+ puts "OMG ERROR #{response.code}"
408
+ else
409
+ response
410
+ end
411
+ end
412
+ end
413
+
414
+ # 3.4 DELETE - Eliminar una tarjeta de crédito
415
+
416
+ def self.delete_card(customer_id, token)
417
+ handle_timeouts do
418
+ response = delete("/customers" + "/#{customer_id}" + '/creditCards' + "/#{token}")
419
+
420
+ case response.code
421
+ when 200..202
422
+ puts response
423
+ response.parsed_response["response"]["description"]
424
+ # Retorna la descripción
425
+ when 404
426
+ response.message
427
+ when 500..600
428
+ puts "OMG ERROR #{response.code}"
429
+ else
430
+ response
431
+ end
432
+ end
433
+ end
434
+
435
+ ###########################################################
436
+ #  ****** 4 - SUSCRIPCIONES ****** #
437
+ # Una suscripción es la relación entre un plan de pagos, #
438
+ # un pagador y una tarjeta de crédito y es el elemento #
439
+ # con el que se controla la ejecución de los cobros #
440
+ # correspondientes. #
441
+ ###########################################################
442
+
443
+ # 4.1 POST - Crear suscripción
444
+
445
+ # quantity Es la cantidad de Planes que adquiere con la suscripción
446
+ #  installments Es el número de cuotas en las que diferira cada pago
447
+ # trial_days Son los días de prueba
448
+ #
449
+
450
+ # 4.1.1 CON TODOS LOS ELEMENTOS NUEVOS
451
+ def self.create_suscription(params = {})
452
+ handle_timeouts do
453
+
454
+ headers = {
455
+ 'Content-type' => 'application/json;charset=UTF-8',
456
+ 'Accept' => 'application/json',
457
+ 'Accept-language' => 'es',
458
+ 'Authorization' => @auth
459
+ }
460
+ response = post('/subscriptions', body: params.to_json, headers: headers)
461
+ case response.code
462
+ when 200..202
463
+ puts response
464
+ retrieve_suscription response
465
+ when 404
466
+ response.message
467
+ when 500..600
468
+ puts "OMG ERROR #{response.code}"
469
+ else
470
+ response
471
+ end
472
+ end
473
+ end
474
+
475
+
476
+
477
+ # 4.1.2 CON TODOS LOS ELEMENTOS EXISTENTES
478
+
479
+ def self.create_suscription_all_existent(customer_id, plan_code, token, quantity, installments, trial_days)
480
+ #Recurrente.add_new_suscription(cliente,plan,token,1,1,0)
481
+ handle_timeouts do
482
+ params = {
483
+ "quantity": quantity,
484
+ "installments": installments,
485
+ "trialDays": trial_days,
486
+ "customer": {
487
+ "id": customer_id,
488
+ "creditCards": [
489
+ {
490
+ "token": token
491
+ }
492
+ ]
493
+ },
494
+ "plan": {
495
+ "planCode": plan_code
496
+ }
497
+ }
498
+ headers = {
499
+ 'Content-type' => 'application/json;charset=UTF-8',
500
+ 'Accept' => 'application/json',
501
+ 'Accept-language' => 'es',
502
+ 'Authorization' => @auth
503
+ }
504
+ response = post('/subscriptions', body: params.to_json, headers: headers)
505
+ case response.code
506
+ when 200..202
507
+ puts response
508
+ retrieve_suscription response
509
+ when 404
510
+ response.message
511
+ response
512
+ when 500..600
513
+ puts "OMG ERROR #{response.code}"
514
+ else
515
+ response
516
+ end
517
+ end
518
+ end
519
+
520
+ # 4.1.3 PLAN Y SUSCRIPTOR YA CREADOS Y UNA TARJETA NUEVA
521
+ def self.create_suscription_alternative_1(customer_id, plan_code, quantity, installments, trial_days, numero , exp_mes, exp_ano ,tipo, nombre, documento, dir_linea1, dir_linea2 , dir_linea3 , departamento, ciudad, pais, codigo_postal, telefono )
522
+ handle_timeouts do
523
+ params = {
524
+ "quantity": quantity,
525
+ "installments": installments,
526
+ "trialDays": trial_days,
527
+ "customer": {
528
+ "id": customer_id,
529
+ "creditCards": [
530
+ {
531
+ "name": nombre,
532
+ "document": documento,
533
+ "number": numero,
534
+ "expMonth": exp_mes,
535
+ "expYear": exp_ano,
536
+ "type": tipo,
537
+ "address": {
538
+ "line1": dir_linea1,
539
+ "line2": dir_linea2,
540
+ "line3": dir_linea3,
541
+ "postalCode": codigo_postal,
542
+ "city": ciudad,
543
+ "state": departamento,
544
+ "country": pais,
545
+ "phone": telefono
546
+ }
547
+ }
548
+ ]
549
+ },
550
+ "plan": {
551
+ "planCode": plan_code
552
+ }
553
+ }
554
+ headers = {
555
+ 'Content-type' => 'application/json;charset=UTF-8',
556
+ 'Accept' => 'application/json',
557
+ 'Accept-language' => 'es',
558
+ 'Authorization' => @auth
559
+ }
560
+ response = post('/subscriptions', body: params.to_json, headers: headers)
561
+ case response.code
562
+ when 200..202
563
+ puts response
564
+ retrieve_suscription response
565
+ when 404
566
+ response.message
567
+ when 500..600
568
+ puts "OMG ERROR #{response.code}"
569
+ else
570
+ response
571
+ end
572
+ end
573
+ end
574
+
575
+ # 4.1.4 CLIENTE Y TARJETA YA CREADOS, CON PLAN NUEVO
576
+ def self.create_suscription_alternative_2(customer_id, plan_code, token, quantity, installments, trial_days, codigo, descripcion, intervalo, cantidad_intervalos, moneda, valor, impuesto, base_impuesto, reintentos, dias_entre_reintentos, cobros,periodo_de_gracia)
577
+ handle_timeouts do
578
+ params = {
579
+ "installments": installments,
580
+ "trialDays": trial_days,
581
+ "customer": {
582
+ "id": customer_id,
583
+ "creditCards": [
584
+ {
585
+ "token": token
586
+ }
587
+ ]
588
+ },
589
+ "plan": {
590
+ "accountId": ACCOUNT,
591
+ "planCode": codigo,
592
+ "description": descripcion,
593
+ "interval": intervalo,
594
+ "intervalCount": cantidad_intervalos ,
595
+ "maxPaymentsAllowed": cobros,
596
+ "paymentAttemptsDelay": dias_entre_reintentos,
597
+ "maxPaymentAttempts": reintentos,
598
+ "maxPendingPayments": periodo_de_gracia,
599
+ "additionalValues": [
600
+ {
601
+ "name": "PLAN_VALUE",
602
+ "value": valor,
603
+ "currency": moneda
604
+ },
605
+ {
606
+ "name": "PLAN_TAX",
607
+ "value": impuesto,
608
+ "currency": moneda
609
+ },
610
+ {
611
+ "name": "PLAN_TAX_RETURN_BASE",
612
+ "value": base_impuesto,
613
+ "currency": moneda
614
+ }
615
+ ]
616
+ }
617
+ }
618
+ headers = {
619
+ 'Content-type' => 'application/json;charset=UTF-8',
620
+ 'Accept' => 'application/json',
621
+ 'Accept-language' => 'es',
622
+ 'Authorization' => @auth
623
+ }
624
+ response = post('/subscriptions', body: params.to_json, headers: headers)
625
+ case response.code
626
+ when 200..202
627
+ puts response
628
+ retrieve_suscription response
629
+ when 404
630
+ response.message
631
+ when 500..600
632
+ puts "OMG ERROR #{response.code}"
633
+ else
634
+ response
635
+ end
636
+ end
637
+ end
638
+
639
+ # 4.2 PUT - Update Suscription Credit Card
640
+
641
+ def self.update_suscription_card(suscription_id, token)
642
+ handle_timeouts do
643
+ headers = {
644
+ 'Content-type' => 'application/json;charset=UTF-8',
645
+ 'Accept' => 'application/json',
646
+ 'Accept-language' => 'es',
647
+ 'Authorization' => @auth
648
+ }
649
+ params = {
650
+ "creditCardToken": token
651
+ }
652
+ response = put('/subscriptions'+ "/#{suscription_id}", body: params.to_json, headers: headers)
653
+
654
+ case response.code
655
+ when 200..202
656
+ puts JSON.pretty_generate(response.parsed_response)
657
+ puts response.code.to_s + " " + response.message
658
+ response
659
+ when 404
660
+ response.message
661
+ when 500..600
662
+ puts "OMG ERROR #{response.code}"
663
+ else
664
+ response
665
+ end
666
+ end
667
+ end
668
+
669
+ # 4.3 GET - Buscar una Suscripción
670
+ def self.find_suscription(suscription_id)
671
+ handle_timeouts do
672
+ response = get("/subscriptions" + "/#{suscription_id}")
673
+
674
+ case response.code
675
+ when 200..202
676
+ puts JSON.pretty_generate(response.parsed_response)
677
+ puts response.code.to_s + " " + response.message
678
+ response
679
+ when 404
680
+ response.code.to_s + " " + response.message
681
+ when 500..600
682
+ puts "OMG ERROR #{response.code}"
683
+ else
684
+ response
685
+ end
686
+ end
687
+ end
688
+
689
+ # 4.4 GET - Buscar las suscripciones de un Cliente
690
+ def self.find_suscriptions_by_suscriptor(customer_id)
691
+ handle_timeouts do
692
+ response = get("/subscriptions" + "/?customerId=#{customer_id}")
693
+
694
+ case response.code
695
+ when 200..202
696
+ puts JSON.pretty_generate(response.parsed_response)
697
+ puts response.code.to_s + " " + response.message
698
+ response.parsed_response["subscriptionsListResponse"]["subscriptions"]
699
+ when 404
700
+ response.message
701
+ when 500..600
702
+ puts "OMG ERROR #{response.code}"
703
+ else
704
+ response
705
+ end
706
+ end
707
+ end
708
+
709
+ # 4.5 POST - Cancelar una Suscripción. Status CANCELLED
710
+
711
+ def self.cancel_suscription(suscription_id)
712
+ handle_timeouts do
713
+ response = delete("/subscriptions" + "/#{suscription_id}")
714
+
715
+ case response.code
716
+ when 200..202
717
+ puts response
718
+ response.parsed_response["response"]["description"]
719
+ when 404
720
+ response.message
721
+ when 500..600
722
+ puts "OMG ERROR #{response.code}"
723
+ else
724
+ response
725
+ end
726
+ end
727
+ end
728
+
729
+
730
+ ########################################################################
731
+ #  ****** 5 - CARGOS ADICIONALES ****** #
732
+ # Un cargo puede ser un cobro adicional o un descuento realizado #
733
+ # sobre el valor de uno de los pagos que conforman el plan de #
734
+ # pagos recurrentes. Estos solo afectan el siguiente cobro pendiente #
735
+ # y se ejecutan una única vez. #
736
+ ########################################################################
737
+
738
+ # 5.1 POST - Crear un cargo adicional
739
+
740
+ def self.create_charge(suscription_id, descripcion, valor, moneda, impuesto, base_impuesto )
741
+ handle_timeouts do
742
+ headers = {
743
+ 'Content-type' => 'application/json; charset=UTF-8',
744
+ 'Accept' => 'application/json',
745
+ 'Accept-language' => 'es',
746
+ 'Authorization' => @auth
747
+ }
748
+ params = {
749
+ "description": descripcion,
750
+ "additionalValues": [
751
+ {
752
+ "name": "ITEM_VALUE",
753
+ "value": valor,
754
+ "currency": moneda
755
+ },
756
+ {
757
+ "name": "ITEM_TAX",
758
+ "value": impuesto,
759
+ "currency": moneda
760
+ },
761
+ {
762
+ "name": "ITEM_TAX_RETURN_BASE",
763
+ "value": base_impuesto,
764
+ "currency": moneda
765
+ }
766
+ ]
767
+ }
768
+ response = post("/subscriptions" + "/#{suscription_id}" + "/recurringBillItems", body: params.to_json, headers: headers)
769
+ case response.code
770
+ when 200..202
771
+ puts JSON.pretty_generate(response.parsed_response)
772
+ retrieve_extra_charge response
773
+ when 404
774
+ response.message
775
+ when 500..600
776
+ puts "OMG ERROR #{response.code}"
777
+ else
778
+ response
779
+ end
780
+ end
781
+ end
782
+
783
+ # 5.2 PUT - Actualizar un cargo adicional
784
+
785
+ def self.update_charge(extra_charge_id, descripcion, valor, moneda, impuesto, base_impuesto )
786
+ handle_timeouts do
787
+ params = {
788
+ "description": descripcion,
789
+ "additionalValues": [
790
+ {
791
+ "name": "ITEM_VALUE",
792
+ "value": valor,
793
+ "currency": moneda
794
+ },
795
+ {
796
+ "name": "ITEM_TAX",
797
+ "value": impuesto,
798
+ "currency": moneda
799
+ },
800
+ {
801
+ "name": "ITEM_TAX_RETURN_BASE",
802
+ "value": base_impuesto,
803
+ "currency": moneda
804
+ }
805
+ ]
806
+ }
807
+
808
+ headers = {
809
+ 'Content-type' => 'application/json;charset=UTF-8',
810
+ 'Accept' => 'application/json',
811
+ 'Accept-language' => 'es',
812
+ 'Authorization' => @auth
813
+ }
814
+ put("/recurringBillItems" + "/#{extra_charge_id}", body: params.to_json, headers: headers)
815
+ case response.code
816
+ when 200..202
817
+ puts JSON.pretty_generate(response.parsed_response)
818
+ puts response.code.to_s + " " + response.message
819
+ response
820
+ when 404
821
+ response.message
822
+ when 500..600
823
+ puts "OMG ERROR #{response.code}"
824
+ else
825
+ response
826
+ end
827
+ end
828
+ end
829
+
830
+ # 5.3 GET - Buscar un Cargo Extra por ID
831
+ def self.find_charge_by_id(charge_id)
832
+ handle_timeouts do
833
+ response = get("/recurringBillItems" + "/#{charge_id}")
834
+
835
+ case response.code
836
+ when 200
837
+ puts JSON.pretty_generate(response.parsed_response)
838
+ puts response.code.to_s + " " + response.message
839
+ response.parsed_response["recurringBillItem"]
840
+ # Retorna el cargo extra
841
+ when 404
842
+ response.message
843
+ response
844
+ when 500..600
845
+ puts "OMG ERROR #{response.code}"
846
+ response
847
+ else
848
+ response
849
+ end
850
+ end
851
+ end
852
+
853
+ # 5.4 GET - Buscar un Cargo Extra por Suscripcion
854
+ def self.find_charge_by_suscription(suscription_id)
855
+ handle_timeouts do
856
+ response = get("/recurringBillItems" + "/?subscriptionId=#{suscription_id}")
857
+
858
+ case response.code
859
+ when 200
860
+ puts JSON.pretty_generate(response.parsed_response)
861
+ puts response.code.to_s + " " + response.message
862
+ response.parsed_response["recurringBillItemListResponse"]["recurringBillItems"]["recurringBillItem"]
863
+ # Retorna los cargos extra de la suscripción
864
+ when 404
865
+ response.message
866
+ when 500..600
867
+ puts "OMG ERROR #{response.code}"
868
+ else
869
+ response
870
+ end
871
+ end
872
+ end
873
+
874
+ # 5.5 POST - Borrar un cargo de la suscripción
875
+
876
+ def self.delete_charge(extra_charge_id)
877
+ handle_timeouts do
878
+ response = delete("/recurringBillItems" + "/#{extra_charge_id}")
879
+
880
+ case response.code
881
+ when 200..202
882
+ puts response
883
+ puts response.code.to_s + " " + response.message
884
+ response.parsed_response["response"]["description"]
885
+ when 404
886
+ response.message
887
+ when 500..600
888
+ puts "OMG ERROR #{response.code}"
889
+ else
890
+ response
891
+ end
892
+ end
893
+ end
894
+
895
+ private
896
+ def self.retrieve_plan(response)
897
+ response.parsed_response["planCode"]
898
+ end
899
+
900
+ def self.retrieve_customer(response)
901
+ response.parsed_response["id"]
902
+ end
903
+
904
+ def self.retrieve_credit_card(response)
905
+ response.parsed_response["token"]
906
+ end
907
+
908
+ def self.retrieve_suscription(response)
909
+ response.parsed_response["id"]
910
+ end
911
+
912
+ def self.retrieve_extra_charge(response)
913
+ response.parsed_response["id"]
914
+ end
915
+ end
916
+
917
+
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'recurrente/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "recurrente"
8
+ spec.version = Recurrente::VERSION
9
+ spec.authors = ["gabosarmiento"]
10
+ spec.email = ["gabrielsarmiento@gmail.com"]
11
+
12
+ spec.summary = %q{Un wrapper para el API de pagos recurrentes de Payulatam}
13
+ spec.description = %q{Una Gem de Ruby para consumir la API de payulatam.com api y manejar los pagos recurrentes}
14
+ spec.homepage = "https://github.com/gabosarmiento/recurrente.git"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.10"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+
25
+ # Dependency
26
+ spec.add_dependency "httparty"
27
+ spec.add_dependency "json"
28
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: recurrente
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - gabosarmiento
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-09-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: httparty
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: json
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Una Gem de Ruby para consumir la API de payulatam.com api y manejar los
70
+ pagos recurrentes
71
+ email:
72
+ - gabrielsarmiento@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - bin/console
83
+ - bin/setup
84
+ - lib/recurrente.rb
85
+ - lib/recurrente/version.rb
86
+ - recurrente.gemspec
87
+ homepage: https://github.com/gabosarmiento/recurrente.git
88
+ licenses:
89
+ - MIT
90
+ metadata: {}
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 2.4.6
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: Un wrapper para el API de pagos recurrentes de Payulatam
111
+ test_files: []