payu_payments 0.0.1

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.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
@@ -0,0 +1 @@
1
+ payu_payments
@@ -0,0 +1 @@
1
+ 1.9.3-p194
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in payu_payments.gemspec
4
+ gemspec
@@ -0,0 +1,9 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rspec do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/payu_payments/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
9
+
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 CristianV
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,178 @@
1
+ # PayuPayments
2
+
3
+ A wrapper for the [PayuLatam.com](http://www.payulatam.com/) payment gateway, it include the
4
+ managment of clients, plans, subscriptions and creditCards.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'payu_payments'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install payu_payments
19
+
20
+ ## Usage
21
+
22
+ All the clases work as a simple CRUD objects, the models also have a
23
+ basic attributes validations.
24
+
25
+ ## Configuration
26
+
27
+ To use the gem you need to set the keys available on your account
28
+
29
+
30
+ ```ruby
31
+ PayuPayments.config do |config|
32
+ config.api_key = "xxxxxxxxxxxxxx"
33
+ config.api_login = "xxxxxxx"
34
+ config.merchant_id = "123456"
35
+ config.account = "7890"
36
+ config.mode = "development" # or production to point to production end-point
37
+ end
38
+
39
+ ```
40
+
41
+
42
+ ### Clients
43
+
44
+ To perform transactions with the API an entity representing the
45
+ custormer needs to be created, this entity is de Client and can be
46
+ created and updated using the PayuPayments::Client class.
47
+
48
+ ```ruby
49
+ @client = PayuPayments::Client.new(:fullName => "john Doe", :email => "johndoe@gmail.com")
50
+
51
+ # or
52
+
53
+ @client = PayuPayments::Client.new
54
+ @client.fullName = "john Doe"
55
+ @client.email = "johndoe@gmail.com"
56
+
57
+ @client.save
58
+ ```
59
+ You can also retrieve clients from the API if you know it's id
60
+
61
+ ```ruby
62
+
63
+ @client = PayuPayments::Client.find(123)
64
+ @client.fullName = "New name"
65
+ @client.save
66
+
67
+ ```
68
+
69
+
70
+ ### Credit Cards
71
+
72
+ You can store a tokenized credit card on the gateway tekenization
73
+ service by adding it to a client, in that way the credit card will be
74
+ stored directly in the PCI compliant gateway and can be used to charge the
75
+ client in future transactiosn without requiring to fill the credit card
76
+ form again.
77
+
78
+ ```ruby
79
+ @client = PayuPayments::Client.find(123)
80
+
81
+ creditCard: {
82
+ name: "Sample User Name",
83
+ document: "1020304050",
84
+ number: "4242424242424242",
85
+ expMonth: "01",
86
+ expYear: "2020",
87
+ type: "VISA",
88
+ address: {
89
+ line1: "Address Name",
90
+ line2: "17 25",
91
+ line3: "Of 301",
92
+ postalCode: "00000",
93
+ city: "City Name",
94
+ state: "State Name",
95
+ country: "CO",
96
+ phone: "300300300"
97
+ }
98
+ }
99
+
100
+ credit_card = @client.add_credit_card(creditCard)
101
+
102
+ ### To list the credit cards from a client
103
+
104
+ @client.credit_cards
105
+ ```
106
+
107
+ ### Plans
108
+
109
+ To create a recurring payment model you need to create plans, plans
110
+ describe the different ways that you can sell your suscription based
111
+ products, here you describe the product how much you will charge for it an
112
+ how you want to charge for it
113
+
114
+ ```ruby
115
+ planParams = {
116
+ plan: {
117
+ planCode: "sample-plan-code-001",
118
+ description: "Sample Plan 001",
119
+ accountId: "1",
120
+ intervalCount: "1",
121
+ interval: "MONTH",
122
+ maxPaymentsAllowed: "12",
123
+ maxPaymentAttempts: "3",
124
+ paymentAttemptsDelay: "1",
125
+ maxPendingPayments: "0",
126
+ trialDays: "30",
127
+ additionalValues: {
128
+ additionalValue: [{ name: "PLAN_VALUE",
129
+ value: "20000",
130
+ currency: "COP"
131
+ },
132
+ {
133
+ name: "PLAN_TAX",
134
+ value: "1600",
135
+ currency: "COP"
136
+ }]
137
+ }
138
+ }
139
+ }
140
+ plan = PayuPayments::Plan.new(planParams)
141
+ plan.save
142
+
143
+ # or
144
+ plan = PayuPayments::Plan.create(planParams)
145
+ ```
146
+ ### Subscriptions
147
+
148
+ Subscription will glue all together to create a subscription based
149
+ products, with a suscription you will bind a
150
+ client with a tokenized credit card with a plan,
151
+
152
+ ```ruby
153
+ PayuPayments::Subscription.create(subscription: {
154
+ quantity: "1",
155
+ installments: "1",
156
+ trialDays: "15",
157
+ customer: {
158
+ id: @client.id,
159
+ creditCards: {
160
+ creditCard: { "token": credit_card.token }
161
+ }
162
+ },
163
+ plan: { "planCode": plan.planCode }
164
+ })
165
+
166
+ # to check a client susbscriptions
167
+ @client.subscriptions
168
+
169
+ ```
170
+
171
+
172
+ ## Contributing
173
+
174
+ 1. Fork it ( http://github.com/<my-github-username>/payu_payments/fork )
175
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
176
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
177
+ 4. Push to the branch (`git push origin my-new-feature`)
178
+ 5. Create new Pull Request
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env rake
2
+ $LOAD_PATH << File.expand_path("../lib", __FILE__)
3
+ Dir.chdir(File.expand_path("..", __FILE__))
4
+
5
+ require 'payu_payments'
6
+ require "bundler/gem_tasks"
7
+ require 'rspec/core/rake_task'
8
+
9
+ RSpec::Core::RakeTask.new('spec')
10
+
11
+ desc "Payupayments console"
12
+ task :console do
13
+ require 'irb'
14
+ require 'irb/completion'
15
+ require 'payu_payments'
16
+ require 'pry'
17
+
18
+ ARGV.clear
19
+ IRB.start
20
+ end
21
+
22
+
23
+ # If you want to make this the default task
24
+ task :default => :spec
@@ -0,0 +1,34 @@
1
+ require "payu_payments/version"
2
+
3
+ %w{ model caller client plan credit_card subscription recurring_bill_item }.each do |f|
4
+ require "payu_payments/#{f}"
5
+ end
6
+
7
+
8
+ module PayuPayments
9
+
10
+ def self.config(&block)
11
+ @configuration ||= Configuration.new
12
+
13
+ unless block.nil?
14
+ yield @configuration
15
+ else
16
+ @configuration.config
17
+ end
18
+ end
19
+
20
+ class Configuration
21
+ attr_accessor :api_key, :api_login, :merchant_id, :account, :mode, :show_log
22
+ def initialize
23
+ @show_log = false
24
+ end
25
+ def config
26
+ {:api_key => @api_key,
27
+ :api_login => @api_login,
28
+ :merchant_id => @merchant_id,
29
+ :account => @account,
30
+ :show_log => @show_log,
31
+ :mode => @mode}
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,60 @@
1
+ require 'httparty'
2
+ require "base64"
3
+ require 'ostruct'
4
+
5
+ module PayuPayments
6
+ class Caller
7
+ include ::HTTParty
8
+ include Model
9
+
10
+ API = "https://api.payulatam.com"
11
+ API_SANDBOX = "https://stg.api.payulatam.com"
12
+
13
+ attr_accessor :access, :base, :resource, :errors
14
+ format :json
15
+ alias :attr :base
16
+
17
+ def set_base_uri
18
+ self.class.base_uri(PayuPayments.config[:mode] == "production" ? API : API_SANDBOX)
19
+ end
20
+
21
+ def initialize(params={})
22
+ @access = PayuPayments.config
23
+ self.set_base_uri
24
+ self.class.debug_output $stdout if @access[:show_log]
25
+
26
+ @base = OpenStruct.new
27
+ base.marshal_load params
28
+ @errors = []
29
+ end
30
+
31
+ def http_call(type, url, params={})
32
+ if ["post", "put"].include? type
33
+ headers = { 'Accept' => "application/json",
34
+ 'Content-Type' => 'application/json; charset=UTF-8',
35
+ 'Authorization' => "Basic #{basic_auth}"}
36
+ resp = self.class.send(type, url, :body => params.to_json, :verify => (access[:mode] == "production"), :headers => headers)
37
+ else
38
+ headers = { 'Accept' => "application/json",
39
+ 'Authorization' => "Basic #{basic_auth}"}
40
+ resp = self.class.send(type, url, :query => params, :verify => (access[:mode] == "production"), :headers => headers)
41
+ end
42
+
43
+ respond_with = (resp == "" or resp.nil?) ? {} : resp.inject({ }) { |h, (k,v)| h[k.to_sym] = v; h }
44
+
45
+ if resp.code.to_s.match(/2\d\d/)
46
+ respond_with
47
+ else
48
+ {"type" => respond_with[:type], "description" => respond_with[:description]}
49
+ end
50
+ end
51
+
52
+
53
+ private
54
+
55
+ def basic_auth
56
+ Base64.encode64("#{access[:api_login]}:#{access[:api_key]}")
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,42 @@
1
+ module PayuPayments
2
+ class Client < Caller
3
+
4
+ # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
5
+ # Client attributes from Payu documentation
6
+ # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
7
+ #
8
+ # id
9
+ # ullName
10
+ # emai
11
+ # creditCards
12
+ # subscriptions
13
+
14
+ def initialize(params={})
15
+ super
16
+ @resource = "customers"
17
+ end
18
+
19
+ def add_credit_card(params)
20
+ cc = CreditCard.new(params)
21
+ cc.attr.customerId = self.base.id
22
+ cc.save
23
+ end
24
+
25
+ def subscriptions
26
+ self.base.subscriptions.map do |sub|
27
+ subscription = Subscription.new
28
+ subscription.base.marshal_load(sub)
29
+ subscription
30
+ end
31
+ end
32
+
33
+ def credit_cards
34
+ self.base.creditCards.each.map do |sub|
35
+ cc = CreditCard.new
36
+ cc.base.marshal_load(sub)
37
+ cc
38
+ end
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,78 @@
1
+ module PayuPayments
2
+
3
+ class CreditCard < Caller
4
+
5
+ # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
6
+ # Credit Card attributes from Payu documentation
7
+ # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
8
+ #
9
+ # Token Longitud = [0,36] String Token asociado a una tarjeta de crédito.
10
+ # customerId Longitud = #[0,64] String Código de identificación del cliente al cual pertenece la tarjeta de crédito
11
+ # Number Longitud = [13,20] # Patrón = [0-9]*{13,20} String Número de la tarjeta de crédito
12
+ # expMonth Min = 1,#Máx = 12 Integer Mes de expiración de la tarjeta de crédito.
13
+ # expYear Min= 0,
14
+ # Max = 2999 Integer Año de expiración de la tarjeta de crédito.
15
+ # Si el valor es de dos dígitos corresponde al año comprendido entre 2000 (00) y 2099 (99).
16
+ # Si el valor es de más de dos dígitos se toma literal, siendo el año 2000 como mínimo posible.
17
+ # Type Enumeration Corresponde a la franquicia o tipo de tarjeta de crédito. Los posibles valores son:
18
+ # VISA, AMEX, DINERS, MASTERCARD, ELO, SHOPPING, NARANJA, CABAL y ARGENCARD.
19
+ # Name 255 String Nombre del tarjeta habiente.
20
+ # document Longitud = #[5, 30] String Número del documento de identificad del tarjeta habiente.
21
+ # address - Address Dirección de correspondencia del tarjeta habiente asociado con la tarjeta de crédito.
22
+ # issuerBank 255 String Nombre del banco emisor de la tarjeta de crédito.
23
+ #
24
+ # City Longitud = [0, 50] String Nombre de la ciudad.
25
+ # country Longitud = [2, 2] String Código ISO 3166 (2 letras – Alpha 2) del país de la dirección.
26
+ #Ver países soportados
27
+ # linea1 Longitud = [0, 100] String Primera línea de la dirección.
28
+ # linea2 Longitud = [0, 100] String Segunda línea de la dirección o número de la dirección.
29
+ # linea3 Longitud = [0, 100] String Tercera línea de la dirección o complemento de la dirección.
30
+ # phone Longitud = [0, 20] String Teléfono asociado a la dirección.
31
+ # postalCode Longitud = [0, 20] String Código postal de la dirección.
32
+ # State Longitud = [0, 40] String Nombre del estado de la dirección.
33
+
34
+ def initialize(params={})
35
+ super
36
+ @resource = "creditCards"
37
+ end
38
+
39
+ def save
40
+ if valid?
41
+ @url = new? ? "#{API_PATH}/customers/#{attr.customerId}/#{@resource}" : "#{API_PATH}/#{@resource}/#{base.id}"
42
+ super
43
+ else
44
+ false
45
+ end
46
+ end
47
+
48
+ def self.create(customer_id, params)
49
+ url = "#{API_PATH}/customers/#{attr.customerId}/#{@resource}"
50
+ self.base.marshal_load params
51
+ resp = http_call("post", url, attr.marshal_dump)
52
+ base.marshal_load resp
53
+ end
54
+
55
+ def destroy(customer_id, id)
56
+ customer_id = self.attr.customerId
57
+ id = self.attr.id
58
+ @url = "#{API_PATH}/customers/#{customer_id}/#{@resource}/#{id}}"
59
+ super
60
+ end
61
+
62
+ def valid?
63
+ self.validate
64
+ self.errors.count == 0
65
+ end
66
+
67
+ def validate
68
+ self.errors = []
69
+ [:customerId, :number, :expMonth, :expYear, :type, :document, :address, :name].each do |field|
70
+ validate_presence_of(field)
71
+ end
72
+
73
+ validate_lenght_of(:expYear, 4)
74
+ end
75
+
76
+ end
77
+
78
+ end
@@ -0,0 +1,88 @@
1
+ module Model
2
+ API_PATH = "/payments-api/rest/v4.3"
3
+
4
+ def self.included(base)
5
+ base.send :extend, ClassMethods
6
+ end
7
+
8
+ def save
9
+ verb = new? ? "post" : "put"
10
+ @url ||= new? ? "#{API_PATH}/#{@resource}" : "#{API_PATH}/#{@resource}/#{base.id}"
11
+ resp = http_call(verb, @url, base.marshal_dump)
12
+
13
+ if resp.is_a?(Array)
14
+ error = {field: resp[0], message: resp[1]}
15
+ self.errors << error
16
+ false
17
+ else
18
+ base.marshal_load resp
19
+ true
20
+ end
21
+ end
22
+
23
+ def load
24
+ resp = http_call("get", "#{API_PATH}/#{@resource}/#{self.attr.id}")
25
+ base.marshal_load resp
26
+ end
27
+
28
+ def destroy
29
+ @id ||= self.attr.id
30
+ @url ||= "#{API_PATH}/#{@resource}/#{@id}"
31
+ resp = http_call("delete", @url)
32
+ base.marshal_load resp unless resp.is_a? Array
33
+ resp
34
+ end
35
+
36
+ def new?
37
+ base.id.nil?
38
+ end
39
+
40
+ def method_missing(method_name, *arguments, &block)
41
+ if base.marshal_dump.include?(method_name.to_s.strip.to_sym) || method_name.match(/.*=$/)
42
+ base.send(method_name.to_s.strip, *arguments, &block)
43
+ else
44
+ super
45
+ end
46
+ end
47
+
48
+
49
+ # xxxxxxxxxxxxxxxxx validations xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
50
+
51
+ def validate_presence_of(field)
52
+ if (self.attr.send(field.to_s).nil? or self.attr.send(field.to_s) == "")
53
+ error = {}
54
+ error[:field] = field
55
+ error[:message] = "#{field} can't be blank"
56
+ self.errors << error
57
+ end
58
+ end
59
+
60
+ def validate_lenght_of(field, lenght)
61
+ unless self.attr.send(field.to_s).nil?
62
+ if self.attr.send(field.to_s).length != lenght
63
+ error = {}
64
+ error[:field] = field
65
+ error[:message] = "lenght of #{field} should be #{lenght}"
66
+ self.errors << error
67
+ end
68
+ end
69
+ end
70
+
71
+ # Class Methods
72
+ module ClassMethods
73
+ def find(id)
74
+ resp = self.new
75
+ json = resp.http_call("get", "#{API_PATH}/#{resp.resource}/#{id}")
76
+
77
+ unless json["type"] == "NOT_FOUND"
78
+ self.new json
79
+ else
80
+ nil
81
+ end
82
+ end
83
+
84
+ def create(params)
85
+ http_call("post", "#{API_PATH}/#{@resource}", base.marshal_dump)
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,72 @@
1
+ module PayuPayments
2
+ class Plan < Caller
3
+
4
+ # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
5
+ # Plan attributes from Payu documentation
6
+ # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
7
+ #
8
+ # id 36 String Identificador del plan en la plataforma de PayU.
9
+ # planCode 255 String Código único que el comercio le asigna al plan para su posterior identificación. Este código de plan no se puede volver a repetir aun cuando el plan sea borrado.
10
+ # description 255 String Descripción del plan.
11
+ # accountId Integer Identificador de la cuenta del comercio al cual se asocia el plan.
12
+ # interval 5 String Intervalo que define cada cuanto se realiza el cobro de la suscripción. Los valores posibles son:
13
+ # DAY, WEEK, MONTH y YEAR.
14
+ # intervalCount Integer Cantidad del intervalo que define cada cuanto se realiza el cobro de la suscripción.
15
+ # additionalValue.currency 3 String Código ISO 4217 de la moneda del valor asociado al plan.
16
+ # additionalValue.name 255 String Nombre del campo del valor asociado al plan. Los valores posibles son:
17
+ # PLAN_VALUE: valor total de plan.
18
+ # PLAN_TAX_VALUE: valor de los impuestos asociados al valor del plan.
19
+ # PLAN_TAX_RETURN_BASE: valor base para el retorno de impuestos asociados al plan.
20
+ # additionalValue.value Decimal Valor del plan, impuesto o base de retorno de acuerdo a plan.additionalValue.name.
21
+ # trialDays Integer Cantidad de días de prueba de la suscripción.
22
+ # maxPaymentAttempts Máx. 3 Integer Cantidad total de reintentos para cada pago rechazado de la suscripción.
23
+ # maxPaymentsAllowed Integer Cantidad total de pagos de la suscripción.
24
+ # maxPendingPayments Integer Cantidad máxima de pagos pendientes que puede tener una suscripción antes de ser cancelada.
25
+ # paymentAttemptsDelay Integer Cantidad de días de espera entre los reintentos de pago de la suscripción.
26
+
27
+ def initialize(params={})
28
+ super
29
+ @resource = "plans"
30
+ end
31
+
32
+ def save
33
+ verb = new? ? "post" : "put"
34
+ url = new? ? "#{API_PATH}/#{@resource}" : "#{API_PATH}/#{@resource}/#{base.planCode}"
35
+ not_allowed_for_update = [:maxPaymentsAllowed, :trialDays, :id, :intervalCount, :interval, :accountId]
36
+
37
+ params = base.marshal_dump
38
+ not_allowed_for_update.each {|x| params.delete(x) } unless new?
39
+ resp = http_call(verb, url, params)
40
+
41
+ base.marshal_load resp
42
+ end
43
+
44
+ def self.create_base_month_plan(name, value, custom_options={})
45
+ params = {
46
+ planCode: name,
47
+ description: "#{name} plan",
48
+ accountId: PayuPayments.config[:account],
49
+ intervalCount: 1,
50
+ interval: "MONTH",
51
+ maxPaymentsAllowed: 2000,
52
+ maxPaymentAttempts: 3,
53
+ paymentAttemptsDelay: 5,
54
+ maxPendingPayments: 0,
55
+ trialDays: 0,
56
+ additionalValues: [{
57
+ name: "PLAN_VALUE",
58
+ value: value,
59
+ currency: "COP"
60
+ }]
61
+ }
62
+ params.merge!(custom_options)
63
+ self.new params
64
+ end
65
+
66
+
67
+ def destroy
68
+ @id = self.attr.planCode
69
+ super
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,30 @@
1
+ module PayuPayments
2
+
3
+ class RecurringBillItem < Caller
4
+ def initialize(params={})
5
+ super
6
+ @resource = "recurringBillItems"
7
+ end
8
+
9
+ def save
10
+ verb = new? ? "post" : "put"
11
+ url = new? ? "#{API_PATH}/subscriptions/#{base.subscription_id}/#{@resource}" : "#{API_PATH}/#{@resource}/#{base.id}"
12
+ resp = http_call(verb, url, base.marshal_dump)
13
+ base.marshal_load resp
14
+ end
15
+
16
+ def create(params)
17
+ url = "#{API_PATH}/subscriptions/#{attr.subscription_id}/#{@resource}"
18
+ resp = http_call(verb, url, base.marshal_dump)
19
+ base.marshal_load resp
20
+ end
21
+
22
+ def get_recurring_bill_items
23
+ url = "#{API_PATH}/#{@resource}/params"
24
+ resp = http_call(verb, url, base.marshal_dump)
25
+ base.marshal_load resp
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,28 @@
1
+ module PayuPayments
2
+
3
+ class Subscription < Caller
4
+
5
+ # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
6
+ # Subscription attributes from Payu documentation
7
+ # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
8
+ #
9
+ # id 36 String Identificador de la suscripción en la plataforma PayU.
10
+ # quantity - Integer Cantidad de planes a adquirir con la suscripción.
11
+ # installments - Integer Número de cuotas en las que se diferirá cada cobro de la suscripción.
12
+ # trialDays - Integer Días de prueba que téndra la suscripción sin generar cobros.
13
+ # customer - Customer Cliente asociado a la suscripción.
14
+ # customer.credidcards - CreditCard Tarjeta de crédito asociada al cliente.
15
+ # plan - Plan Plan asociado a la suscripción.
16
+
17
+ def initialize(params={})
18
+ super
19
+ @resource = "subscriptions"
20
+ end
21
+
22
+ def add_extra_charges(params={})
23
+ url = "#{API_PATH}/#{self.attr.id}/recurringBillItems"
24
+ http_call("put", url, params)
25
+ end
26
+ end
27
+
28
+ end
@@ -0,0 +1,3 @@
1
+ module PayuPayments
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'payu_payments/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "payu_payments"
8
+ spec.version = PayuPayments::VERSION
9
+ spec.authors = ["CristianV"]
10
+ spec.email = ["cristian@kommit.co"]
11
+ spec.summary = %q{Payulatam payments gatewat API gem}
12
+ spec.description = %q{Payulatam payments gatewat API gem}
13
+ spec.homepage = "https://github.com/kommitters/payu_payments"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ # Dependency
22
+ spec.add_dependency "httparty"
23
+
24
+ # Development
25
+ spec.add_development_dependency "bundler", "~> 1.5"
26
+ spec.add_development_dependency "rake"
27
+ spec.add_development_dependency "rspec"
28
+ spec.add_development_dependency "guard-rspec"
29
+ spec.add_development_dependency "pry"
30
+ end
@@ -0,0 +1,108 @@
1
+ require 'spec_helper'
2
+
3
+ module PayuPayments
4
+
5
+ describe Caller do
6
+
7
+ it "Should set a base container to hold attributes" do
8
+ call = Caller.new
9
+ expect(call.base).to be_an_instance_of(OpenStruct)
10
+ end
11
+
12
+ it "should get access credentials from config" do
13
+ ::PayuPayments.config do |config|
14
+ config.api_key = "6u39nqhq8ftd0hlvnjfs66eh8c"
15
+ config.api_login = "11959c415b33d0c"
16
+ config.merchant_id = "500238"
17
+ config.account = "5009"
18
+ config.mode = "development"
19
+ end
20
+
21
+ call = Caller.new
22
+ expect(call.access[:api_key]).to eq("6u39nqhq8ftd0hlvnjfs66eh8c")
23
+ expect(call.access[:api_login]).to eq("11959c415b33d0c")
24
+ expect(call.access[:merchant_id]).to eq("500238")
25
+ expect(call.access[:account]).to eq("5009")
26
+ expect(call.access[:mode]).to eq("development")
27
+ end
28
+
29
+ it "Should construct a base object with the initial params" do
30
+ call = Caller.new(:name => "jhon", :last_name => "Doe", :number => 123)
31
+ expect(call.base.name).to eq("jhon")
32
+ expect(call.base.last_name).to eq("Doe")
33
+ expect(call.base.number).to eq(123)
34
+ end
35
+
36
+ context "validations" do
37
+
38
+ it "should be able to validate the lenght of a fiekd" do
39
+ call = Caller.new(:name => "jhon")
40
+ call.validate_lenght_of(:name, 5)
41
+ expect(call.errors.count).to eq(1)
42
+ expect(call.errors.first[:message]).to eq("lenght of name should be 5")
43
+ end
44
+
45
+ end
46
+
47
+
48
+
49
+ context "performing http calls" do
50
+ let (:resp) {a = {"valor1" => "hola"}; a.stub(:code => "200"); a}
51
+ before :each do
52
+ ::PayuPayments.config do |config|
53
+ config.api_key = "6u39nqhq8ftd0hlvnjfs66eh8c"
54
+ config.api_login = "11959c415b33d0c"
55
+ config.merchant_id = "500238"
56
+ config.account = "5009"
57
+ config.mode = "development"
58
+ end
59
+ end
60
+
61
+ it "should be able to perform POST calls" do
62
+ attr = {fullName: "john doe", email: "jhon@doe.com"}
63
+ call = Caller.new(attr)
64
+ call.stub(:basic_auth => "abc123")
65
+ headers = { 'Accept' => "application/json",
66
+ 'Content-Type' => 'application/json; charset=UTF-8',
67
+ 'Authorization' => "Basic abc123"}
68
+ Caller.should_receive(:send).with("post", "/someurl", :body => attr.to_json, :verify => false, :headers => headers).and_return(resp)
69
+ call.http_call("post", "/someurl", attr)
70
+ end
71
+
72
+ it "should be able to perform PUT calls" do
73
+ attr = {fullName: "john doe", email: "jhon@doe.com"}
74
+ call = Caller.new(attr)
75
+ call.stub(:basic_auth => "abc123")
76
+ headers = { 'Accept' => "application/json",
77
+ 'Content-Type' => 'application/json; charset=UTF-8',
78
+ 'Authorization' => "Basic abc123"}
79
+ Caller.should_receive(:send).with("put", "/someurl", :body => attr.to_json, :verify => false, :headers => headers).and_return(resp)
80
+ call.http_call("put", "/someurl", attr)
81
+ end
82
+
83
+
84
+ it "should be able to perform GET calls" do
85
+ attr = {fullName: "john doe", email: "jhon@doe.com"}
86
+ call = Caller.new(attr)
87
+ call.stub(:basic_auth => "abc123")
88
+ headers = { 'Accept' => "application/json",
89
+ 'Authorization' => "Basic abc123"}
90
+ Caller.should_receive(:send).with("get", "/someurl", :query => attr, :verify => false, :headers => headers).and_return(resp)
91
+ call.http_call("get", "/someurl", attr)
92
+ end
93
+
94
+ it "should be able to perform DELETE calls" do
95
+ attr = {fullName: "john doe", email: "jhon@doe.com"}
96
+ call = Caller.new(attr)
97
+ call.stub(:basic_auth => "abc123")
98
+ headers = { 'Accept' => "application/json",
99
+ 'Authorization' => "Basic abc123"}
100
+ Caller.should_receive(:send).with("delete", "/someurl", :query => attr, :verify => false, :headers => headers).and_return(resp)
101
+ call.http_call("delete", "/someurl", attr)
102
+ end
103
+ end
104
+
105
+ end
106
+
107
+
108
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ module PayuPayments
4
+
5
+ describe Client do
6
+ it "should be able to create a client" do
7
+ client = Client.new(:fullName => "John Doe", :email => "johndoe@gmail.com")
8
+ client.should_receive(:http_call).with("post", "/payments-api/rest/v4.3/customers", {:fullName => "John Doe", :email => "johndoe@gmail.com"}).and_return(:fullName => "John Doe", :email => "johndoe@gmail.com")
9
+ client.save
10
+ end
11
+
12
+ it "should be able to find a client with its id" do
13
+ call = Client.new
14
+ call.should_receive(:http_call).with("get","/payments-api/rest/v4.3/customers/1").and_return(:fullName => "John Doe", :email => "johndoe@gmail.com", :id => 1)
15
+ Client.should_receive(:new).twice.and_return(call)
16
+ client = Client.find(1)
17
+ expect(client).to be_an_instance_of(Client)
18
+ end
19
+
20
+ it "should save a credit card related to the client" do
21
+ client = Client.new(:id => "123", :fullName => "John Doe", :email => "johndoe@gmail.com")
22
+ cc = CreditCard.new
23
+ CreditCard.should_receive(:new).and_return(cc)
24
+ cc.stub(:save => true)
25
+ client.add_credit_card({number: "1234", :name => "John Doe"})
26
+ end
27
+
28
+ it "should be able to load credit cards" do
29
+ response = {
30
+ id: "2mkls9xekm",
31
+ fullName: "Pedro Perez",
32
+ email: "pperez@payulatam.com",
33
+ creditCards: [
34
+ {
35
+ token: "da2224a9-58b7-482a-9866-199de911c23f",
36
+ customerId: "2mkls9xekm",
37
+ number: "************4242",
38
+ name: "Usuario Prueba",
39
+ type: "VISA",
40
+ address: {
41
+ line1: "Street 93B",
42
+ line2: "17 25",
43
+ line3: "Office 301",
44
+ city: "Bogota",
45
+ country: "CO",
46
+ postalCode: "00000",
47
+ phone: "300300300"
48
+ }
49
+ }
50
+ ]
51
+ }
52
+ Client.should_receive(:find).and_return(Client.new(response))
53
+ client = Client.find("2mkls9xekm")
54
+ expect(client.base.id).to eq("2mkls9xekm")
55
+ expect(client.credit_cards[0].attr.number).to eql("************4242")
56
+ end
57
+
58
+ it "should be able to load subscriptions" do
59
+ response = {
60
+ id: "2mkls9xekm",
61
+ fullName: "Pedro Perez",
62
+ email: "pperez@payulatam.com",
63
+ subscriptions: [
64
+ {
65
+ id: "2mlhk3qxji",
66
+ quantity: "1",
67
+ installments: "1",
68
+ currentPeriodStart: "2013-08-30T10:46:41.477-05:00",
69
+ currentPeriodEnd: "2013-09-29T10:46:41.477-05:00",
70
+ plan: {
71
+ id: "414215a2-c990-4525-ba84-072181988d09",
72
+ planCode: "PLAN-REST-16",
73
+ description: "Plan rest test",
74
+ accountId: "1",
75
+ intervalCount: "1",
76
+ interval: "MONTH",
77
+ additionalValues: [
78
+ {
79
+ name: "PLAN_VALUE",
80
+ value: "20000",
81
+ currency: "COP"
82
+ }]
83
+ }
84
+ }
85
+ ]
86
+ }
87
+ Client.should_receive(:find).and_return(Client.new(response))
88
+ client = Client.find("2mkls9xekm")
89
+ expect(client.base.id).to eq("2mkls9xekm")
90
+ expect(client.subscriptions[0].attr.id).to eql("2mlhk3qxji")
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+ describe PayuPayments do
3
+
4
+ context "Configuration" do
5
+ it "should be able to set configuration keys" do
6
+ PayuPayments.config do |config|
7
+ config.api_key = "xxx"
8
+ config.merchant_id = "123"
9
+ config.account = "abc123"
10
+ end
11
+ end
12
+
13
+ it "should be able to read configuration keys" do
14
+ PayuPayments.config[:api_key].should == "xxx"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ module PayuPayments
4
+
5
+ describe CreditCard do
6
+ it "should be able to validate a credit card" do
7
+ cc = CreditCard.new
8
+ expect(cc.valid?).to eq(false)
9
+ end
10
+
11
+ it "should tell me where the errors are" do
12
+ cc = CreditCard.new
13
+ cc.valid?
14
+ expect(cc.errors.count).to be > 0
15
+ expect(cc.errors.first[:field]).to eql(:customerId)
16
+ expect(cc.errors.first[:message]).to eql("customerId can't be blank")
17
+ end
18
+
19
+ it "should not allow to save a credit card with erros" do
20
+ expect(CreditCard.new.save).to be(false)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ module PayuPayments
4
+ describe Plan do
5
+
6
+ it "should be able to create a plan" do
7
+ plan = {
8
+ plan: {
9
+ planCode: "sample-plan-code-001",
10
+ description: "Sample Plan 001",
11
+ accountId: "1",
12
+ intervalCount: "1",
13
+ interval: "MONTH",
14
+ maxPaymentsAllowed: "12",
15
+ maxPaymentAttempts: "3",
16
+ paymentAttemptsDelay: "1",
17
+ maxPendingPayments: "0",
18
+ trialDays: "30",
19
+ additionalValues: {
20
+ additionalValue: [{ name: "PLAN_VALUE",
21
+ value: "20000",
22
+ currency: "COP"
23
+ },
24
+ {
25
+ name: "PLAN_TAX",
26
+ value: "1600",
27
+ currency: "COP"
28
+ }]
29
+ }
30
+ }
31
+ }
32
+ my_plan = Plan.new(plan)
33
+ my_plan.should_receive(:http_call).with("post", "/payments-api/rest/v4.3/plans", plan).and_return(plan)
34
+ my_plan.save
35
+
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,8 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require 'payu_payments'
5
+
6
+ RSpec.configure do |config|
7
+ # some (optional) config here
8
+ end
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: payu_payments
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - CristianV
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-03-01 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: httparty
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '1.5'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '1.5'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: guard-rspec
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: pry
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: Payulatam payments gatewat API gem
111
+ email:
112
+ - cristian@kommit.co
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - .gitignore
118
+ - .rspec
119
+ - .ruby-gemset
120
+ - .ruby-version
121
+ - Gemfile
122
+ - Guardfile
123
+ - LICENSE.txt
124
+ - README.md
125
+ - Rakefile
126
+ - lib/payu_payments.rb
127
+ - lib/payu_payments/caller.rb
128
+ - lib/payu_payments/client.rb
129
+ - lib/payu_payments/credit_card.rb
130
+ - lib/payu_payments/model.rb
131
+ - lib/payu_payments/plan.rb
132
+ - lib/payu_payments/recurring_bill_item.rb
133
+ - lib/payu_payments/subscription.rb
134
+ - lib/payu_payments/version.rb
135
+ - payu_payments.gemspec
136
+ - spec/caller_spec.rb
137
+ - spec/client_spec.rb
138
+ - spec/config_spec.rb
139
+ - spec/credit_card_spec.rb
140
+ - spec/plan_spec.rb
141
+ - spec/spec_helper.rb
142
+ homepage: https://github.com/kommitters/payu_payments
143
+ licenses:
144
+ - MIT
145
+ post_install_message:
146
+ rdoc_options: []
147
+ require_paths:
148
+ - lib
149
+ required_ruby_version: !ruby/object:Gem::Requirement
150
+ none: false
151
+ requirements:
152
+ - - ! '>='
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ required_rubygems_version: !ruby/object:Gem::Requirement
156
+ none: false
157
+ requirements:
158
+ - - ! '>='
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ requirements: []
162
+ rubyforge_project:
163
+ rubygems_version: 1.8.23
164
+ signing_key:
165
+ specification_version: 3
166
+ summary: Payulatam payments gatewat API gem
167
+ test_files:
168
+ - spec/caller_spec.rb
169
+ - spec/client_spec.rb
170
+ - spec/config_spec.rb
171
+ - spec/credit_card_spec.rb
172
+ - spec/plan_spec.rb
173
+ - spec/spec_helper.rb