payu-latam 1.0.5 → 1.0.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4a19f18fd41efae6442d56239c139dcdf4677d1b7f8ca8f4e060ffb375359ab
4
- data.tar.gz: e9bf835e4c58a59ee2cd8bbf09612932280c2b1b6f754e91cacaf742340db6be
3
+ metadata.gz: bc5ce65d0810e098497cd40608afac89a03abb5dbc95d56bd14cedda3ff6684f
4
+ data.tar.gz: e0ddbaa78f080c1fb1a7401ae66dfec04aa96b625c008332e129757dbe6885cc
5
5
  SHA512:
6
- metadata.gz: 12f76424dc4b8debb1b511a0d9b6b3f4a9ced6f33c06799c00803170e91b014c71ed83140a0ef82818d1686ab6cd76f9b58019dd5649c454881d56c2e9357717
7
- data.tar.gz: 1e37cd9fd30a5f95051e55b39585de952c3a4b466bd608793a13c86fb3f48829d880f40b7559a1988511b8590ef3bef2eb65b9c22882e613421e0f393d0541a8
6
+ metadata.gz: f7c73c6e9c7dc40ae5301c9444fe8a72cd03c219ffc8304e3f23aa239c0d44acfac7df705dfbceae2678c3c2f70de5880a5421ad72ebd740a51c47712ec0e9ac
7
+ data.tar.gz: 9decca14c41d0d8250986b4500ebae9ef09ee8aba4f077d79f2099362813d96f1848128f18bbf6b2d461458b28e32e30b0429e0d8ae63c3a20a320bbe0012869
@@ -72,7 +72,7 @@ Metrics/ParameterLists:
72
72
  Max: 5
73
73
 
74
74
  Metrics/BlockLength:
75
- Max: 50
75
+ Max: 60
76
76
 
77
77
  Metrics/MethodLength:
78
78
  Max: 50
data/README.md CHANGED
@@ -20,17 +20,178 @@ PayU.configure do |config|
20
20
  config.api_key = "4Vj8eK4rloUd272L48hsrarnUA" # Replace with your own API key
21
21
  config.merchant_id = 508_029 # Replace with your own merchant ID
22
22
  config.test = true # Test mode
23
+ config.account_ids = {
24
+ AR: 512_322,
25
+ BR: 512_327,
26
+ CL: 512_325,
27
+ CO: 512_321,
28
+ MX: 512_324,
29
+ PA: 512_326,
30
+ PE: 512_323,
31
+ }
23
32
  end
24
33
 
25
34
  order = PayU::Order.new(
26
35
  amount: 20_000,
27
36
  currency: :COP,
28
- description: "Test PAYU"
37
+ description: "Test PAYU",
29
38
  )
30
39
 
40
+ # Form params for WebCheckout
31
41
  order.form.params
32
42
  ```
33
43
 
44
+ ## Resources
45
+
46
+ ### Order
47
+
48
+ The base object to build a transaction.
49
+
50
+ ```ruby
51
+ #<PayU::Order:0x007f95818e5c10
52
+ @account_id=nil,
53
+ @amount=0.3e1,
54
+ @cc_number="************0086",
55
+ @currency=:USD,
56
+ @description=nil,
57
+ @email="test@test.com",
58
+ @extra_1="extra 1",
59
+ @extra_2="extra 2",
60
+ @extra_3="extra 3",
61
+ @payment_method="VISA",
62
+ @payment_method_code=2,
63
+ @reference_code="2205229",
64
+ @response_code=1,
65
+ @response_message="APPROVED",
66
+ @status_code=4,
67
+ @tax=nil,
68
+ @tax_return_base=nil,
69
+ @transaction_id="957aa79c-135d-413e-b7fc-aaaa0000cccc">
70
+
71
+ order.form # Form object
72
+ order.approved? # Transaction was approved
73
+ order.declined? # Transaction was declined
74
+ order.error? # Transaction failed
75
+ order.pending? # Transaction is pending
76
+ order.expired? # Transaction expired
77
+ ```
78
+
79
+ ### Confirmation
80
+
81
+ A confirmation object from the confirmation Webhook (POST when transaction is finalized)
82
+
83
+ ```ruby
84
+ #<PayU::Confirmation:0x007f95818e5cb0
85
+ @order=#<PayU::Order:0x007f95818e5c10…> # Order object
86
+ @signature="cc3c8101113b056bc4b5b3d289dbb106", # Signature to verify
87
+ @signer=#<PayU::Signer::Confirmation:0x007f95822e3b40…>> # Signer to verify signature
88
+
89
+ confirmation.valid? # Signature is valid
90
+ ```
91
+
92
+ ### Response
93
+
94
+ A response object from the response Webhook (GET when the user leaves the payment page and is sent back to the commerce)
95
+
96
+ ```ruby
97
+ #<PayU::Order:0x007f8a33bf7e48
98
+ @order=#<PayU::Order:0x007f8a33bf7e48…> # Order object
99
+ @signature="cc3c8101113b056bc4b5b3d289dbb106", # Signature to verify
100
+ @signer=#<PayU::Signer::Response:0x007f8a33bdf9d8…>> # Signer to verify signature
101
+
102
+ order.valid? # Signature is valid
103
+ ```
104
+
105
+ ### Customer
106
+
107
+ ```ruby
108
+ PayU::Customer.create(
109
+ name: "Sample User Name",
110
+ email: "sample@sample.com",
111
+ )
112
+ ```
113
+
114
+ ### Credit card
115
+
116
+ ```ruby
117
+ PayU::CreditCard.create(
118
+ name: "Sample User Name",
119
+ document: "1020304050",
120
+ number: "4242424242424242",
121
+ exp_month: 12,
122
+ exp_year: 20,
123
+ type: "VISA",
124
+ customer_id: 4ef04ef0aaaa,
125
+ )
126
+ ```
127
+
128
+ ### Plan
129
+
130
+ ```ruby
131
+ PayU::Plan.create(
132
+ account_id: 512_321,
133
+ code: SecureRandom.hex(8),
134
+ description: "Plan",
135
+ interval: "MONTH",
136
+ interval_count: 1,
137
+ max_payments_allowed: 12,
138
+ payment_attempts_delay: 1,
139
+ data: {PLAN_VALUE: 20_000, PLAN_TAX: 0, PLAN_TAX_RETURN_BASE: 0},
140
+ currency: :COP,
141
+ )
142
+ ```
143
+
144
+ ### Subscription
145
+
146
+ Subscription object that ties a customer, a plan, and a credit card.
147
+
148
+ ```ruby
149
+ #<PayU::Subscription:0x007fe0de8e4790
150
+ @current_period_end=2019-03-26 23:59:59 -0500,
151
+ @current_period_start=2019-02-27 00:00:00 -0500,
152
+ @customer=
153
+ #<PayU::Customer:0x007fe0de8d43e0
154
+ @credit_cards=
155
+ [#<PayU::CreditCard:0x007fe0de8ce288
156
+ @address={},
157
+ @customer_id=nil,
158
+ @document=nil,
159
+ @exp_month=nil,
160
+ @exp_year=nil,
161
+ @name=nil,
162
+ @number=nil,
163
+ @token="db092e5e-65aa-48c6-92b1-aaaa0000cccc",
164
+ @type=nil>],
165
+ @email="sample@sample.com",
166
+ @id="4ef04ef0aaaa",
167
+ @name="Sample User Name">,
168
+ @delivery_address={},
169
+ @extra_1=nil,
170
+ @extra_2=nil,
171
+ @id="44f09r1p5561",
172
+ @immediate_payment=true,
173
+ @installments=1,
174
+ @notify_url="https://example.com",
175
+ @plan=
176
+ #<PayU::Plan:0x007fe0de8d7a68
177
+ @account_id=512321,
178
+ @code="4689fe81dd927aae",
179
+ @currency=:COP,
180
+ @data={"PLAN_TAX_RETURN_BASE"=>0, "PLAN_TAX"=>0, "PLAN_VALUE"=>20000},
181
+ @description="Plan",
182
+ @id="6da3e0b5-cfb6-430d-93ef-aaaa0000cccc",
183
+ @interval=:MONTH,
184
+ @interval_count=1,
185
+ @max_payment_attempts=nil,
186
+ @max_payments_allowed=nil,
187
+ @max_pending_payments=nil,
188
+ @payment_attempts_delay=nil,
189
+ @trial_days=nil>,
190
+ @quantity=1,
191
+ @trial_days=nil>
192
+ ```
193
+
194
+
34
195
  ## Sandbox
35
196
 
36
197
  - Sandbox endpoint
@@ -1,10 +1,14 @@
1
1
  require "bigdecimal"
2
+ require "faraday"
2
3
  require "virtus"
3
4
 
4
5
  require "pay_u/version"
5
6
 
6
7
  require "pay_u/constants"
7
8
  require "pay_u/configuration"
9
+ require "pay_u/errors"
10
+ require "pay_u/util/string"
11
+
8
12
  require "pay_u/signer/base"
9
13
  require "pay_u/signer/form"
10
14
  require "pay_u/signer/response"
@@ -13,3 +17,10 @@ require "pay_u/order"
13
17
  require "pay_u/form"
14
18
  require "pay_u/response"
15
19
  require "pay_u/confirmation"
20
+
21
+ require "pay_u/client"
22
+ require "pay_u/resource"
23
+ require "pay_u/plan"
24
+ require "pay_u/credit_card"
25
+ require "pay_u/customer"
26
+ require "pay_u/subscription"
@@ -0,0 +1,87 @@
1
+ require "base64"
2
+
3
+ class PayU::Client
4
+ include Singleton
5
+
6
+ def initialize
7
+ @client ||= Faraday.new(url: PayU.configuration.api_url) do |connection|
8
+ connection.request :url_encoded
9
+ connection.response :logger if PayU.configuration.test?
10
+ connection.adapter Faraday.default_adapter
11
+ connection.basic_auth "pRRXKOl8ikMmt9u", "4Vj8eK4rloUd272L48hsrarnUA"
12
+ end
13
+ end
14
+
15
+
16
+ def get(url)
17
+ response = @client.get(&perform_request(url))
18
+
19
+ process_response(response)
20
+ end
21
+
22
+
23
+ def post(url, params: {})
24
+ response = @client.post(&perform_request_with_params(url, params))
25
+
26
+ process_response(response)
27
+ end
28
+
29
+
30
+ def put(url, params: {})
31
+ response = @client.put(&perform_request_with_params(url, params))
32
+
33
+ process_response(response)
34
+ end
35
+
36
+
37
+ def delete(url)
38
+ response = @client.delete(&perform_request(url))
39
+
40
+ response.success?
41
+ end
42
+
43
+
44
+ private def perform_request(url)
45
+ proc do |request|
46
+ request.url "payments-api/#{url}"
47
+ request.headers = request.headers.merge(default_headers)
48
+ end
49
+ end
50
+
51
+
52
+ private def perform_request_with_params(url, params)
53
+ proc do |request|
54
+ request.url "payments-api/#{url}"
55
+ request.headers = request.headers.merge(default_headers)
56
+ request.body = params.delete_if { |_, value| value.nil? }.to_json
57
+ end
58
+ end
59
+
60
+
61
+ private def process_response(response)
62
+ raise PayU::UnauthorizedError, "Unauthorized" if !response.success? && response.status == 401
63
+
64
+ body = JSON.parse(response.body)
65
+
66
+ return raise_error(response) unless response.success?
67
+
68
+ body
69
+ end
70
+
71
+ private def raise_error(response)
72
+ body = JSON.parse(response.body)
73
+
74
+ error_message = body["errorList"] ? body["errorList"].join : body["description"]
75
+
76
+ raise PayU::NotFoundError, error_message if response.status == 404
77
+ raise PayU::Error, error_message
78
+ end
79
+
80
+
81
+ private def default_headers
82
+ {
83
+ "Content-Type" => "application/json; charset=utf-8",
84
+ "Accept" => "application/json",
85
+ }
86
+ end
87
+ end
@@ -8,6 +8,8 @@ module PayU
8
8
  attribute :test, Boolean
9
9
  attribute :response_url, String
10
10
  attribute :confirmation_url, String
11
+ attribute :api_url, String
12
+ attribute :webcheckout_url, String
11
13
 
12
14
  SANDBOX_API_KEY = "4Vj8eK4rloUd272L48hsrarnUA".freeze
13
15
  SANDBOX_MERCHANT_ID = 508_029
@@ -28,6 +30,8 @@ module PayU
28
30
  self.merchant_id = ENV.fetch("PAYU_MERCHANT_ID", SANDBOX_MERCHANT_ID) if merchant_id.nil?
29
31
  self.test = ENV.fetch("PAYU_TEST", true) if test.nil?
30
32
  self.account_ids = SANDBOX_ACCOUNT_IDS if account_ids.nil?
33
+ self.api_url = test ? PayU::TEST_API_URL : PayU::LIVE_API_URL
34
+ self.webcheckout_url = test ? PayU::TEST_WEBCHECKOUT_URL : PayU::LIVE_WEBCHECKOUT_URL
31
35
  end
32
36
 
33
37
 
@@ -1,4 +1,6 @@
1
1
  module PayU
2
- TEST_URL = "https://sandbox.checkout.payulatam.com/ppp-web-gateway-payu/".freeze
3
- LIVE_URL = "https://checkout.payulatam.com/ppp-web-gateway-payu/".freeze
2
+ TEST_API_URL = "https://sandbox.api.payulatam.com/".freeze
3
+ LIVE_API_URL = "https://api.payulatam.com/".freeze
4
+ TEST_WEBCHECKOUT_URL = "https://sandbox.checkout.payulatam.com/ppp-web-gateway-payu/".freeze
5
+ LIVE_WEBCHECKOUT_URL = "https://checkout.payulatam.com/ppp-web-gateway-payu/".freeze
4
6
  end
@@ -0,0 +1,35 @@
1
+ class PayU::CreditCard
2
+ include Virtus.model
3
+ include PayU::Resource
4
+
5
+ ENDPOINT = "rest/v#{PayU::API_VERSION}/creditCards".freeze
6
+
7
+ attribute :token, String
8
+ attribute :name, String
9
+ attribute :document, String
10
+ attribute :number, String
11
+ attribute :exp_month, Integer
12
+ attribute :exp_year, Integer
13
+ attribute :type, String
14
+ attribute :address, Hash
15
+ attribute :customer_id, String
16
+
17
+ def to_params
18
+ {
19
+ name: name,
20
+ document: document,
21
+ number: number,
22
+ expMonth: exp_month,
23
+ expYear: exp_year,
24
+ type: type,
25
+ address: address,
26
+ }
27
+ end
28
+
29
+
30
+ def create_url
31
+ raise StandardError, "Missing customer id" unless customer_id
32
+
33
+ "#{PayU::Customer::ENDPOINT}/#{customer_id}/creditCards"
34
+ end
35
+ end
@@ -0,0 +1,34 @@
1
+ class PayU::Customer
2
+ include Virtus.model
3
+ include PayU::Resource
4
+
5
+ ENDPOINT = "rest/v#{PayU::API_VERSION}/customers".freeze
6
+
7
+ attribute :id, String
8
+ attribute :name, String
9
+ attribute :email, String
10
+ attribute :credit_cards, Array[PayU::CreditCard]
11
+
12
+ def self.new_from_api(params)
13
+ customer = super(params)
14
+
15
+ customer.name = params["fullName"]
16
+
17
+ if params["creditCards"]
18
+ customer.credit_cards = params["creditCards"].map do |credit_card|
19
+ PayU::CreditCard.new_from_api(credit_card)
20
+ end
21
+ end
22
+
23
+ customer
24
+ end
25
+
26
+
27
+ def to_params
28
+ {
29
+ fullName: name,
30
+ email: email,
31
+ creditCards: credit_cards.map(&:to_params),
32
+ }
33
+ end
34
+ end
@@ -0,0 +1,7 @@
1
+ module PayU
2
+ class Error < StandardError; end
3
+
4
+ class NotFoundError < Error; end
5
+
6
+ class UnauthorizedError < Error; end
7
+ end
@@ -18,7 +18,7 @@ class PayU::Form
18
18
 
19
19
  def params
20
20
  {
21
- action: order.test? ? PayU::TEST_URL : PayU::LIVE_URL,
21
+ action: PayU.configuration.webcheckout_url,
22
22
  fields: {
23
23
  merchantId: order.merchant_id,
24
24
  accountId: order.account_id,
@@ -0,0 +1,67 @@
1
+ class PayU::Plan
2
+ include Virtus.model
3
+ include PayU::Resource
4
+
5
+ ENDPOINT = "rest/v#{PayU::API_VERSION}/plans".freeze
6
+
7
+ attribute :account_id, Integer
8
+ attribute :id, String
9
+ attribute :code, String
10
+ attribute :description, String
11
+ attribute :interval, Symbol
12
+ attribute :interval_count, Integer
13
+ attribute :max_payments_allowed, Integer
14
+ attribute :max_payment_attempts, Integer
15
+ attribute :payment_attempts_delay, Integer
16
+ attribute :max_pending_payments, Integer
17
+ attribute :trial_days, Integer
18
+ attribute :data, Hash
19
+ attribute :currency, Symbol
20
+
21
+ def self.new_from_api(params)
22
+ plan = super(params)
23
+
24
+ plan.code = params["planCode"]
25
+ plan.currency = params["additionalValues"].first["currency"].to_s
26
+ plan.data = params["additionalValues"].inject({}) do |memo, hash|
27
+ memo.merge(hash["name"].to_s => hash["value"])
28
+ end
29
+
30
+ plan
31
+ end
32
+
33
+
34
+ def to_params
35
+ {
36
+ accountId: account_id,
37
+ planCode: code,
38
+ description: description,
39
+ interval: interval,
40
+ intervalCount: interval_count,
41
+ maxPaymentsAllowed: max_payments_allowed,
42
+ paymentAttemptsDelay: payment_attempts_delay,
43
+ additionalValues: data.map do |name, value|
44
+ {name: name, value: value, currency: currency.to_s}
45
+ end,
46
+ }
47
+ end
48
+
49
+
50
+ def to_update_params
51
+ {
52
+ planCode: code,
53
+ description: description,
54
+ paymentAttemptsDelay: payment_attempts_delay,
55
+ maxPendingPayments: max_pending_payments,
56
+ maxPaymentAttempts: max_payment_attempts,
57
+ additionalValues: data.map do |name, value|
58
+ {name: name, value: value, currency: currency.to_s}
59
+ end,
60
+ }
61
+ end
62
+
63
+
64
+ private def identifier
65
+ code
66
+ end
67
+ end
@@ -0,0 +1,81 @@
1
+ module PayU::Resource
2
+ def self.included(base)
3
+ base.extend(ClassMethods)
4
+ end
5
+
6
+
7
+ def save
8
+ return update if id
9
+
10
+ self.attributes = self.class.create(attributes).attributes
11
+
12
+ true
13
+ end
14
+
15
+
16
+ def delete
17
+ self.class.client.delete url
18
+ end
19
+
20
+
21
+ def create_url
22
+ self.class::ENDPOINT
23
+ end
24
+
25
+
26
+ def to_update_params
27
+ to_params
28
+ end
29
+
30
+
31
+ private def update
32
+ response = self.class.client.put url, params: to_update_params
33
+ self.attributes = attributes.merge(self.class.new_from_api(response).attributes)
34
+ end
35
+
36
+
37
+ private def identifier
38
+ id
39
+ end
40
+
41
+
42
+ private def url
43
+ "#{self.class::ENDPOINT}/#{identifier}"
44
+ end
45
+
46
+
47
+ module ClassMethods
48
+ def client
49
+ @client ||= PayU::Client.instance
50
+ end
51
+
52
+
53
+ def new_from_api(params)
54
+ resource = new
55
+
56
+ resource.attributes = params.inject({}) do |memo, (key, value)|
57
+ local_key = key.to_underscore.to_sym
58
+
59
+ resource.respond_to?(local_key) ? memo.merge(local_key => value) : memo
60
+ end
61
+
62
+ resource
63
+ end
64
+
65
+
66
+ def retrieve(identifier)
67
+ response = client.get "#{self::ENDPOINT}/#{identifier}"
68
+
69
+ new_from_api(response)
70
+ end
71
+
72
+
73
+ def create(params)
74
+ resource = new(params)
75
+
76
+ response = client.post resource.create_url, params: resource.to_params
77
+
78
+ new_from_api(response)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,57 @@
1
+ class PayU::Subscription
2
+ include Virtus.model
3
+ include PayU::Resource
4
+
5
+ ENDPOINT = "rest/v#{PayU::API_VERSION}/subscriptions".freeze
6
+
7
+ attribute :id, String
8
+ attribute :quantity, Integer
9
+ attribute :installments, Integer
10
+ attribute :trial_days, Integer
11
+ attribute :current_period_start, Time
12
+ attribute :current_period_end, Time
13
+ attribute :immediate_payment, Boolean
14
+ attribute :extra_1, String
15
+ attribute :extra_2, String
16
+ attribute :delivery_address, Hash
17
+ attribute :notify_url, String
18
+ attribute :plan, PayU::Plan
19
+ attribute :customer, PayU::Customer
20
+
21
+ def self.new_from_api(params)
22
+ subscription = super(params)
23
+
24
+ subscription.extra_1 = params["extra1"]
25
+ subscription.extra_2 = params["extra2"]
26
+ subscription.current_period_start = Time.at(params["currentPeriodStart"] / 1000)
27
+ subscription.current_period_end = Time.at(params["currentPeriodEnd"] / 1000)
28
+
29
+ subscription.plan = PayU::Plan.new_from_api(params["plan"])
30
+ subscription.customer = PayU::Customer.new_from_api(params["customer"])
31
+
32
+ subscription
33
+ end
34
+
35
+
36
+ def to_params
37
+ {
38
+ quantity: quantity,
39
+ installments: installments,
40
+ trialDays: trial_days,
41
+ immediatePayment: immediate_payment,
42
+ extra1: extra_1,
43
+ extra2: extra_2,
44
+ customer: customer.to_params,
45
+ plan: plan.to_params,
46
+ deliveryAddress: delivery_address,
47
+ notifyUrl: notify_url,
48
+ }
49
+ end
50
+
51
+
52
+ def to_update_params
53
+ {
54
+ creditCardToken: customer.credit_cards.first.token,
55
+ }
56
+ end
57
+ end
@@ -0,0 +1,12 @@
1
+ class String
2
+ def to_underscore
3
+ gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
4
+ end
5
+
6
+
7
+ def to_camel
8
+ split("_").inject([]) do |memo, char|
9
+ memo.push(memo.empty? ? char : char.capitalize)
10
+ end.join
11
+ end
12
+ end
@@ -1,3 +1,4 @@
1
1
  module PayU
2
- VERSION = "1.0.5".freeze
2
+ VERSION = "1.0.6".freeze
3
+ API_VERSION = "4.9".freeze
3
4
  end
@@ -13,6 +13,7 @@ Gem::Specification.new do |spec|
13
13
  spec.homepage = "https://slangapp.com"
14
14
  spec.license = "MIT"
15
15
 
16
+ spec.add_dependency "faraday", "~> 0.15"
16
17
  spec.add_dependency "virtus", "~> 1.0"
17
18
 
18
19
  spec.add_development_dependency "bundler", "~> 1.15"
@@ -0,0 +1,12 @@
1
+ module Fixtures
2
+ def self.credit_card
3
+ {
4
+ name: "Sample User Name",
5
+ document: "1020304050",
6
+ number: "4242424242424242",
7
+ exp_month: 12,
8
+ exp_year: 20,
9
+ type: "VISA",
10
+ }
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ module Fixtures
2
+ def self.plan
3
+ {
4
+ account_id: 512_321,
5
+ code: SecureRandom.hex(8),
6
+ description: "Plan",
7
+ interval: "MONTH",
8
+ interval_count: 1,
9
+ max_payments_allowed: 12,
10
+ payment_attempts_delay: 1,
11
+ data: {PLAN_VALUE: 20_000, PLAN_TAX: 0, PLAN_TAX_RETURN_BASE: 0},
12
+ currency: :COP,
13
+ }
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ {
2
+ "id": "a74277gzfljs",
3
+ "fullName": "Sample User Name",
4
+ "email": "sample@sample.com"
5
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "id": "0bbc2f6c-c12e-41a3-b261-60fc83014fe1",
3
+ "planCode": "{{code}}",
4
+ "description": "Plan",
5
+ "accountId": "512321",
6
+ "intervalCount": 1,
7
+ "interval": "MONTH",
8
+ "maxPaymentsAllowed": 12,
9
+ "maxPaymentAttempts": 0,
10
+ "paymentAttemptsDelay": 1,
11
+ "maxPendingPayments": 0,
12
+ "trialDays": 0,
13
+ "additionalValues": [
14
+ { "name": "PLAN_TAX", "value": 0, "currency": "COP" },
15
+ { "name": "PLAN_VALUE", "value": 20000, "currency": "COP" },
16
+ { "name": "PLAN_TAX_RETURN_BASE", "value": 0, "currency": "COP" }
17
+ ]
18
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "type": "NOT_FOUND",
3
+ "description": "Subscription plan not found [non-existent]"
4
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "id": "44f09r1p5561",
3
+ "plan": {
4
+ "id": "6da3e0b5-cfb6-430d-93ef-f654eda54e16",
5
+ "planCode": "{{code}}",
6
+ "description": "Plan",
7
+ "accountId": "512321",
8
+ "intervalCount": 1,
9
+ "interval": "MONTH",
10
+ "additionalValues": [
11
+ { "name": "PLAN_TAX_RETURN_BASE", "value": 0, "currency": "COP" },
12
+ { "name": "PLAN_TAX", "value": 0, "currency": "COP" },
13
+ { "name": "PLAN_VALUE", "value": 20000, "currency": "COP" }
14
+ ]
15
+ },
16
+ "customer": {
17
+ "id": "4ef0aqk8kipn",
18
+ "fullName": "Sample User Name",
19
+ "email": "sample@sample.com",
20
+ "creditCards": [{ "token": "db092e5e-65aa-48c6-92b1-1b2581e93015" }]
21
+ },
22
+ "quantity": "1",
23
+ "installments": "1",
24
+ "currentPeriodStart": 1551243600000,
25
+ "currentPeriodEnd": 1553662799000,
26
+ "immediatePayment": true,
27
+ "deliveryAddress": {},
28
+ "notifyUrl": "https://example.com"
29
+ }
@@ -0,0 +1,35 @@
1
+ module Helpers
2
+ def stub_subscription_request
3
+ stub_request(:post, /#{PayU::Subscription::ENDPOINT}/)
4
+ .to_return do |request|
5
+ {body: File.new("./spec/fixtures/responses/subscription.json").read
6
+ .gsub("{{code}}", JSON.parse(request.body)["plan"]["planCode"])}
7
+ end
8
+ end
9
+
10
+
11
+ def stub_plan_request
12
+ stub_request(:any, /#{PayU::Plan::ENDPOINT}/)
13
+ .to_return do |request|
14
+ code = if request.method == :post
15
+ JSON.parse(request.body)["planCode"]
16
+ else
17
+ request.uri.path.split("/").last
18
+ end
19
+
20
+ {body: File.new("./spec/fixtures/responses/plan.json").read.gsub("{{code}}", code)}
21
+ end
22
+ end
23
+
24
+
25
+ def stub_customer_request
26
+ stub_request(:post, /#{PayU::Customer::ENDPOINT}$/)
27
+ .to_return(body: File.new("./spec/fixtures/responses/customer.json"))
28
+ end
29
+
30
+
31
+ def stub_credit_card_request
32
+ stub_request(:post, %r{#{PayU::Customer::ENDPOINT}/.+/creditCards})
33
+ .to_return(body: {token: SecureRandom.uuid}.to_json)
34
+ end
35
+ end
@@ -3,6 +3,10 @@ require "spec_helper"
3
3
  RSpec.describe PayU::Configuration do
4
4
  let(:default_api_key) { "4Vj8eK4rloUd272L48hsrarnUA" }
5
5
 
6
+ after do
7
+ PayU.reset
8
+ end
9
+
6
10
  it "assigns defaults" do
7
11
  expect(PayU.configuration.api_key).to eq(default_api_key)
8
12
  expect(PayU.configuration.test).to be_truthy
@@ -1,5 +1,4 @@
1
1
  require "spec_helper"
2
- require "fixtures/confirmation"
3
2
 
4
3
  RSpec.describe PayU::Confirmation do
5
4
  it "creates object from callback" do
@@ -0,0 +1,21 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe PayU::CreditCard do
4
+ before do
5
+ stub_customer_request
6
+
7
+ stub_credit_card_request
8
+ end
9
+
10
+ it "creates credit card token" do
11
+ customer = PayU::Customer.create(
12
+ name: "Sample User Name",
13
+ email: "sample@sample.com",
14
+ )
15
+ credit_card = PayU::CreditCard.create(Fixtures.credit_card.merge(customer_id: customer.id))
16
+
17
+ expect(credit_card.token).to(
18
+ match(/\b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}\b/),
19
+ )
20
+ end
21
+ end
@@ -1,10 +1,6 @@
1
1
  require "spec_helper"
2
2
 
3
3
  RSpec.describe PayU::Form do
4
- before do
5
- PayU.reset
6
- end
7
-
8
4
  # Example from http://developers.payulatam.com/es/web_checkout/integration.html
9
5
  it "validates signature" do
10
6
  order = PayU::Order.new(
@@ -0,0 +1,64 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe PayU::Plan do
4
+ before do
5
+ stub_request(:any, /#{PayU::Plan::ENDPOINT}/)
6
+ .to_return do |request|
7
+ code = if request.method == :post
8
+ JSON.parse(request.body)["planCode"]
9
+ else
10
+ request.uri.path.split("/").last
11
+ end
12
+
13
+ {body: File.new("./spec/fixtures/responses/plan.json").read.gsub("{{code}}", code)}
14
+ end
15
+
16
+ stub_request(:get, %r{#{PayU::Plan::ENDPOINT}/non-existent}).to_return(
17
+ status: 404,
18
+ body: File.new("./spec/fixtures/responses/plan_not_found.json"),
19
+ )
20
+ end
21
+
22
+ it "creates plan" do
23
+ plan = PayU::Plan.create(Fixtures.plan)
24
+
25
+ expect(plan.id).to match(/\b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}\b/)
26
+ end
27
+
28
+ it "retrieves plan" do
29
+ code = "pro-#{SecureRandom.hex(4)}"
30
+ plan = PayU::Plan.create(Fixtures.plan.merge(code: code))
31
+
32
+ found_plan = PayU::Plan.retrieve(plan.code)
33
+
34
+ expect(found_plan.id).to match(plan.id)
35
+ end
36
+
37
+ it "updates plan" do
38
+ WebMock.disable!
39
+ plan = PayU::Plan.create(Fixtures.plan)
40
+ id = plan.id
41
+ description = "Description"
42
+
43
+ plan.description = description
44
+ plan.save
45
+
46
+ expect(plan.id).to eq(id)
47
+ expect(plan.description).to eq(description)
48
+
49
+ WebMock.enable!
50
+ end
51
+
52
+ it "deletes plan" do
53
+ WebMock.disable!
54
+ plan = PayU::Plan.create(Fixtures.plan)
55
+
56
+ plan.delete
57
+
58
+ expect do
59
+ PayU::Plan.retrieve(plan.code)
60
+ end.to raise_exception(PayU::NotFoundError, /Subscription plan not found/)
61
+
62
+ WebMock.enable!
63
+ end
64
+ end
@@ -1,5 +1,4 @@
1
1
  require "spec_helper"
2
- require "fixtures/response"
3
2
 
4
3
  RSpec.describe PayU::Response do
5
4
  it "creates object from callback" do
@@ -0,0 +1,58 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe PayU::CreditCard do
4
+ before do
5
+ stub_subscription_request
6
+
7
+ stub_plan_request
8
+ end
9
+
10
+ context "new plan" do
11
+ it "creates subscripition" do
12
+ plan_params = Fixtures.plan
13
+ subscription = PayU::Subscription.create(
14
+ subscription_params.merge(plan: plan_params),
15
+ )
16
+
17
+ expect(subscription.id).not_to be_nil
18
+ expect(subscription.customer.name).to eq(customer_params[:name])
19
+ expect(subscription.customer.email).to eq(customer_params[:email])
20
+ expect(subscription.plan.code).to eq(plan_params[:code])
21
+ end
22
+ end
23
+
24
+ context "existing plan" do
25
+ it "creates subscripition" do
26
+ plan = PayU::Plan.create(Fixtures.plan)
27
+ subscription = PayU::Subscription.create(subscription_params.merge(plan: {code: plan.code}))
28
+
29
+ expect(subscription.id).not_to be_nil
30
+ expect(subscription.customer.name).to eq(customer_params[:name])
31
+ expect(subscription.customer.email).to eq(customer_params[:email])
32
+ expect(subscription.plan.code).to eq(plan.code)
33
+ end
34
+ end
35
+
36
+ private def subscription_params
37
+ {
38
+ quantity: 1,
39
+ installments: 1,
40
+ trial_days: 0,
41
+ immediate_payment: true,
42
+ notify_url: "https://example.com",
43
+ customer: customer_params,
44
+ }
45
+ end
46
+
47
+ private def customer_params
48
+ {
49
+ name: "Sample User Name",
50
+ email: "sample@sample.com",
51
+ credit_cards: credit_cards_params,
52
+ }
53
+ end
54
+
55
+ private def credit_cards_params
56
+ [Fixtures.credit_card]
57
+ end
58
+ end
@@ -1,5 +1,6 @@
1
1
  require "simplecov"
2
2
  require "coveralls"
3
+ require "webmock/rspec"
3
4
 
4
5
  SimpleCov.formatters = [
5
6
  SimpleCov::Formatter::HTMLFormatter,
@@ -13,6 +14,9 @@ require "pry"
13
14
  require "bundler/setup"
14
15
  require "pay_u"
15
16
 
17
+ Dir["./spec/fixtures/*.rb"].each { |fixture| require fixture }
18
+ Dir["./spec/helpers/*.rb"].each { |fixture| require fixture }
19
+
16
20
  if ENV["CIRCLE_ARTIFACTS"]
17
21
  dir = File.join(ENV["CIRCLE_ARTIFACTS"], "coverage")
18
22
  SimpleCov.coverage_dir(dir)
@@ -28,4 +32,6 @@ RSpec.configure do |config|
28
32
  config.expect_with :rspec do |c|
29
33
  c.syntax = :expect
30
34
  end
35
+
36
+ config.include Helpers
31
37
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: payu-latam
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Slang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-23 00:00:00.000000000 Z
11
+ date: 2019-04-30 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.15'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.15'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: virtus
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -181,24 +195,42 @@ files:
181
195
  - bin/console
182
196
  - bin/setup
183
197
  - lib/pay_u.rb
198
+ - lib/pay_u/client.rb
184
199
  - lib/pay_u/configuration.rb
185
200
  - lib/pay_u/confirmation.rb
186
201
  - lib/pay_u/constants.rb
202
+ - lib/pay_u/credit_card.rb
203
+ - lib/pay_u/customer.rb
204
+ - lib/pay_u/errors.rb
187
205
  - lib/pay_u/form.rb
188
206
  - lib/pay_u/order.rb
207
+ - lib/pay_u/plan.rb
208
+ - lib/pay_u/resource.rb
189
209
  - lib/pay_u/response.rb
190
210
  - lib/pay_u/signer/base.rb
191
211
  - lib/pay_u/signer/confirmation.rb
192
212
  - lib/pay_u/signer/form.rb
193
213
  - lib/pay_u/signer/response.rb
214
+ - lib/pay_u/subscription.rb
215
+ - lib/pay_u/util/string.rb
194
216
  - lib/pay_u/version.rb
195
217
  - payu-latam.gemspec
196
218
  - spec/fixtures/confirmation.rb
219
+ - spec/fixtures/credit_card.rb
220
+ - spec/fixtures/plan.rb
197
221
  - spec/fixtures/response.rb
222
+ - spec/fixtures/responses/customer.json
223
+ - spec/fixtures/responses/plan.json
224
+ - spec/fixtures/responses/plan_not_found.json
225
+ - spec/fixtures/responses/subscription.json
226
+ - spec/helpers/stub.rb
198
227
  - spec/pay_u/configuration_spec.rb
199
228
  - spec/pay_u/confirmation_spec.rb
229
+ - spec/pay_u/credit_card_spec.rb
200
230
  - spec/pay_u/form_spec.rb
231
+ - spec/pay_u/plan_spec.rb
201
232
  - spec/pay_u/response_spec.rb
233
+ - spec/pay_u/subscription_spec.rb
202
234
  - spec/pay_u_spec.rb
203
235
  - spec/spec_helper.rb
204
236
  homepage: https://slangapp.com
@@ -227,10 +259,20 @@ specification_version: 4
227
259
  summary: Ruby bindings for the PayU Latam API
228
260
  test_files:
229
261
  - spec/fixtures/confirmation.rb
262
+ - spec/fixtures/credit_card.rb
263
+ - spec/fixtures/plan.rb
230
264
  - spec/fixtures/response.rb
265
+ - spec/fixtures/responses/customer.json
266
+ - spec/fixtures/responses/plan.json
267
+ - spec/fixtures/responses/plan_not_found.json
268
+ - spec/fixtures/responses/subscription.json
269
+ - spec/helpers/stub.rb
231
270
  - spec/pay_u/configuration_spec.rb
232
271
  - spec/pay_u/confirmation_spec.rb
272
+ - spec/pay_u/credit_card_spec.rb
233
273
  - spec/pay_u/form_spec.rb
274
+ - spec/pay_u/plan_spec.rb
234
275
  - spec/pay_u/response_spec.rb
276
+ - spec/pay_u/subscription_spec.rb
235
277
  - spec/pay_u_spec.rb
236
278
  - spec/spec_helper.rb