nitro_pay 0.1.0 → 1.0.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 +4 -4
- data/.gitignore +1 -1
- data/lib/generators/nitro_pay/install_generator.rb +16 -0
- data/lib/generators/templates/nitro_pay.rb +39 -0
- data/lib/nitro_pay.rb +91 -3
- data/lib/nitro_pay/array.rb +6 -0
- data/lib/nitro_pay/config/enums/brands.yml +45 -0
- data/lib/nitro_pay/config/enums/currencies.yml +35 -0
- data/lib/nitro_pay/config/enums/payment_methods.yml +20 -0
- data/lib/nitro_pay/config/enums/recurrence_periods.yml +18 -0
- data/lib/nitro_pay/config/enums/transaction_codes.yml +31 -0
- data/lib/nitro_pay/config/proxy.example.yml +4 -0
- data/lib/nitro_pay/connection.rb +173 -0
- data/lib/nitro_pay/currency.rb +76 -0
- data/lib/nitro_pay/hash.rb +55 -0
- data/lib/nitro_pay/status.rb +29 -0
- data/lib/nitro_pay/string.rb +17 -0
- data/lib/nitro_pay/transaction.rb +123 -0
- data/lib/nitro_pay/version.rb +5 -2
- data/modeling/API Routes.png +0 -0
- data/modeling/Sequence - APIClient.asta +0 -0
- data/modeling/Structure - ClassDiagram.asta +0 -0
- data/modeling/WorkFlow - LocalGEM.asta +0 -0
- data/modeling/WorkFlow - LocalGEM.png +0 -0
- data/modeling/useful_files/card_operators/cielo/test_cards.txt +11 -0
- data/modeling/useful_files/card_operators/cielo/test_keys.txt +3 -0
- data/spec/file_helper.rb +42 -0
- data/spec/helpers.rb +56 -0
- data/spec/nitro_pay/conversions_spec.rb +36 -0
- data/spec/nitro_pay/currency_spec.rb +154 -0
- data/spec/nitro_pay/status_spec.rb +15 -0
- data/spec/nitro_pay/transaction_spec.rb +584 -0
- data/spec/spec_helper.rb +20 -0
- metadata +39 -3
@@ -0,0 +1,76 @@
|
|
1
|
+
module NitroPay
|
2
|
+
# Currency Obj, useful methods to work with many different countries
|
3
|
+
class Currency
|
4
|
+
# Receive it amount in Decimal and convert to operator str, like 10,00 or 10.00 to 1000
|
5
|
+
def self.to_operator_str(amount)
|
6
|
+
# Check invalid entry
|
7
|
+
return nil if amount.nil?
|
8
|
+
return amount.to_s if amount.is_a?Integer
|
9
|
+
amount = format('%.2f', amount) if amount.to_s.float?
|
10
|
+
return amount if amount.is_a?(String) && amount.index('.').nil? && amount.index(',').nil?
|
11
|
+
|
12
|
+
if amount.is_a?String
|
13
|
+
return amount if amount.index('.').nil? && amount.index(',').nil?
|
14
|
+
|
15
|
+
amount = amount.remove('.')
|
16
|
+
amount = amount.remove(',')
|
17
|
+
|
18
|
+
return amount
|
19
|
+
end
|
20
|
+
|
21
|
+
# Convert from BigDecimal
|
22
|
+
if amount.is_a?BigDecimal
|
23
|
+
aux_amount_str = amount.to_s('F')
|
24
|
+
cents = aux_amount_str[aux_amount_str.index('.'), aux_amount_str.length]
|
25
|
+
|
26
|
+
# Check if there is a bug because the Decimal didn't recognize the second 0
|
27
|
+
aux_amount_str = "#{aux_amount_str}0"if cents.index('.')
|
28
|
+
|
29
|
+
return aux_amount_str.remove('.')
|
30
|
+
end
|
31
|
+
|
32
|
+
# Create the amount as String
|
33
|
+
amount.is_a?(String) ? amount_str = amount : amount_str = amount.to_s
|
34
|
+
amount_str_not_formatted = amount_str.remove('.').remove(',')
|
35
|
+
|
36
|
+
# Create the full value
|
37
|
+
cents_value = amount_str_not_formatted[amount_str_not_formatted.length-2, amount_str_not_formatted.length-1]
|
38
|
+
integer_value = amount_str_not_formatted[0, amount_str_not_formatted.length-2]
|
39
|
+
|
40
|
+
# The return constraint
|
41
|
+
"#{integer_value}#{cents_value}"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Receive it amount in Integer String (like 1000 that means 10,00 or 10.00) and convert to Decimal value
|
45
|
+
def self.to_decimal(amount)
|
46
|
+
# If it was passed a BigDecimal it must be passed to operator format
|
47
|
+
amount = Currency.to_operator_str(amount) if amount.is_a?BigDecimal
|
48
|
+
|
49
|
+
# Base vars
|
50
|
+
aux_amount = amount
|
51
|
+
cents_chars_counter = 2
|
52
|
+
|
53
|
+
# Building the currency str like BigDecimal understands
|
54
|
+
cents_str = aux_amount[aux_amount.length-cents_chars_counter..aux_amount.length]
|
55
|
+
integer_str = aux_amount[0, aux_amount.length-cents_chars_counter]
|
56
|
+
new_amount = "#{integer_str}.#{cents_str}"
|
57
|
+
|
58
|
+
BigDecimal.new new_amount
|
59
|
+
end
|
60
|
+
|
61
|
+
# Get it cents from operator amount formatted
|
62
|
+
def self.get_cents(amount)
|
63
|
+
aux = amount.to_s
|
64
|
+
cents_length = 2
|
65
|
+
aux[aux.length-cents_length, aux.length]
|
66
|
+
end
|
67
|
+
|
68
|
+
# Receive a numeric form operator format & retrieve it
|
69
|
+
def self.have_cents?(amount)
|
70
|
+
aux = "#{amount.to_f/100}" if amount.is_a?(String) || amount.is_a?(Integer)
|
71
|
+
aux = amount.to_s if amount.is_a?Float
|
72
|
+
cents = aux[aux.index('.')+1, aux.length]
|
73
|
+
cents.length == 2 ? true : false
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Override ruby Hash Obj
|
2
|
+
class Hash
|
3
|
+
# Attr to be external accessible
|
4
|
+
attr_accessor :get_url_params
|
5
|
+
|
6
|
+
# Convert string keys to symbol keys
|
7
|
+
def it_keys_to_sym
|
8
|
+
self.keys.each do |key|
|
9
|
+
self[key].it_keys_to_sym if self[key].is_a?(Hash)
|
10
|
+
self[(key.to_sym rescue key) || key] = self.delete(key)
|
11
|
+
end
|
12
|
+
|
13
|
+
return self
|
14
|
+
end
|
15
|
+
|
16
|
+
# Convert it keys to get params
|
17
|
+
def it_keys_to_get_param
|
18
|
+
self.it_keys_to_sym
|
19
|
+
self.get_url_params = '?' if self.get_url_params.nil?
|
20
|
+
|
21
|
+
self.keys.each do |key|
|
22
|
+
self.get_url_params = self.get_url_params+'&' unless self.get_url_params.length == 1
|
23
|
+
|
24
|
+
# Nested obj_attrs
|
25
|
+
if self[key].is_a?(Hash)
|
26
|
+
hash_name = key
|
27
|
+
hash_obj = self[key]
|
28
|
+
|
29
|
+
# Hash to GET URL
|
30
|
+
param = to_nested_get_param hash_name, hash_obj
|
31
|
+
end
|
32
|
+
|
33
|
+
self.get_url_params = self.get_url_params+param
|
34
|
+
end
|
35
|
+
|
36
|
+
# Remove the last char: &
|
37
|
+
return self.get_url_params[0..self.get_url_params.length-2]
|
38
|
+
end
|
39
|
+
|
40
|
+
# SetUp a hash to hash URL GET
|
41
|
+
def to_nested_get_param(hash_name, hash_obj)
|
42
|
+
# initial value
|
43
|
+
get_nested_params = ''
|
44
|
+
|
45
|
+
# foreach keys to mount the URL_PARAM
|
46
|
+
hash_obj.keys.each do |key|
|
47
|
+
key_param = hash_obj[key].to_nested_get_param key, hash_obj[key] if hash_obj[key].is_a?(Hash)
|
48
|
+
key_param = "#{hash_name}[#{key}]=#{hash_obj[key]}&" unless hash_obj[key].is_a?(Hash)
|
49
|
+
get_nested_params = get_nested_params+key_param
|
50
|
+
end
|
51
|
+
|
52
|
+
# return
|
53
|
+
get_nested_params
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module NitroPay
|
2
|
+
class Status < NitroPay::Connection
|
3
|
+
# Attrs
|
4
|
+
attr_accessor :message # API response message
|
5
|
+
attr_accessor :http_code # HTTP Code for the request
|
6
|
+
attr_accessor :api_code # Internal system response code
|
7
|
+
attr_accessor :response # the JSON retrieved
|
8
|
+
|
9
|
+
# Constructor
|
10
|
+
def initialize(params = {})
|
11
|
+
super # call it super initialize
|
12
|
+
self.path = 'status'
|
13
|
+
check_it
|
14
|
+
end
|
15
|
+
|
16
|
+
# Check it status and 'setup' it attrs
|
17
|
+
def check_it
|
18
|
+
self.path = 'status'
|
19
|
+
resp = get_request
|
20
|
+
hash_resp = JSON.parse(resp).it_keys_to_sym
|
21
|
+
self.http_code = resp.code
|
22
|
+
self.message = "EndPoint not response(connection error): #{self.url_requested}" if self.http_code != 200
|
23
|
+
self.message = hash_resp[:message] if self.http_code == 200
|
24
|
+
self.api_code = hash_resp[:api_code]
|
25
|
+
self.response = hash_resp
|
26
|
+
self
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module NitroPay
|
2
|
+
# Transaction Obj, but can Abstract nested objs like Costumer
|
3
|
+
class Transaction < NitroPay::Connection
|
4
|
+
attr_accessor :resp
|
5
|
+
attr_accessor :status # can be API Status: self.status = NitroPay::Status.new OR/AND Transaction Status
|
6
|
+
attr_accessor :sold_items
|
7
|
+
|
8
|
+
# Constructor
|
9
|
+
def initialize(params = {})
|
10
|
+
super # super init call
|
11
|
+
# Base redirect_link if test_env is set (so the redirect is just appended)
|
12
|
+
self.redirect_link = "#{self.end_point_versioned}/transactions" if params[:test_env]
|
13
|
+
end
|
14
|
+
|
15
|
+
# Return it RID easily
|
16
|
+
def rid
|
17
|
+
self.resp[:rid]
|
18
|
+
end
|
19
|
+
|
20
|
+
# Return it Purchase URL, to pay on the OperatorPage
|
21
|
+
def purchase_url
|
22
|
+
self.resp[:purchase_url]
|
23
|
+
end
|
24
|
+
|
25
|
+
# GET /api/transactions/:rid by it attr
|
26
|
+
def verify
|
27
|
+
auth_hash = {}
|
28
|
+
auth_hash[:auth] = self.request_params[:auth]
|
29
|
+
if self.rid.nil? then return {error:'RID not received'} else self.path = "transactions/#{self.rid}/#{auth_hash.it_keys_to_get_param}" end
|
30
|
+
return self.get_json_request
|
31
|
+
end
|
32
|
+
|
33
|
+
# POST /api/transactions/page return operator page URL, like the Cielo Page
|
34
|
+
def charge_page(full_resp=false)
|
35
|
+
custom_http_params
|
36
|
+
# SetUp redirect dynamic if is test
|
37
|
+
self.request_params[:transaction][:redirect_link] = "#{self.redirect_link}" if self.request_params[:transaction][:test_env]
|
38
|
+
|
39
|
+
# dynamic path (it is written when a specific method use it)
|
40
|
+
self.path = 'transactions/page'
|
41
|
+
|
42
|
+
# using json_request because need only the answer (do not use something like it HTTP Code)
|
43
|
+
self.resp = self.post_json_request unless full_resp
|
44
|
+
self.resp = self.post_request if full_resp
|
45
|
+
self.resp
|
46
|
+
end
|
47
|
+
|
48
|
+
# POST /api/transactions
|
49
|
+
def charge_store(full_resp=false)
|
50
|
+
custom_http_params
|
51
|
+
|
52
|
+
# dynamic path (it is written when a specific method use it)
|
53
|
+
self.path = 'transactions/store'
|
54
|
+
|
55
|
+
# using json_request because need only the answer (do not use something like it HTTP Code)
|
56
|
+
full_resp ? self.resp = self.post_request : self.resp = self.post_json_request
|
57
|
+
|
58
|
+
# return it received resp
|
59
|
+
self.resp
|
60
|
+
end
|
61
|
+
|
62
|
+
# Update the recurrence amount
|
63
|
+
def update_subscription(rid=nil, full_resp=false)
|
64
|
+
# SetUp
|
65
|
+
self.recurrent_rid = rid if rid
|
66
|
+
self.path = "transactions/#{self.recurrent_rid}/subscription"
|
67
|
+
|
68
|
+
# Perform the request
|
69
|
+
full_resp ? self.resp = self.put_request : self.resp = self.put_json_request
|
70
|
+
|
71
|
+
# return it received resp
|
72
|
+
self.resp
|
73
|
+
end
|
74
|
+
|
75
|
+
# Stop a recurrence based on it transaction rid
|
76
|
+
def unsubscribe(rid=nil, full_resp=false)
|
77
|
+
# SetUp
|
78
|
+
self.recurrent_rid = rid if rid
|
79
|
+
self.path = "transactions/#{self.recurrent_rid}/subscription/unsubscribe"
|
80
|
+
|
81
|
+
# Perform the request
|
82
|
+
full_resp ? self.resp = self.delete_request : self.resp = self.delete_json_request
|
83
|
+
|
84
|
+
# return it received resp
|
85
|
+
self.resp
|
86
|
+
end
|
87
|
+
|
88
|
+
# Return the payments executed for the purchase passed
|
89
|
+
def payment_history(rid=nil, full_resp=false)
|
90
|
+
# SetUp
|
91
|
+
self.recurrent_rid = rid if rid
|
92
|
+
self.path = "transactions/#{self.recurrent_rid}/subscription/payment_history"
|
93
|
+
self.path = "#{self.path}#{self.request_params.it_keys_to_get_param}"
|
94
|
+
|
95
|
+
# Perform the request
|
96
|
+
full_resp ? self.resp = self.get_request : self.resp = self.get_json_request
|
97
|
+
|
98
|
+
# return it received resp
|
99
|
+
self.resp
|
100
|
+
end
|
101
|
+
|
102
|
+
# Check if a subscription is up-to-date or have any pending
|
103
|
+
def up_to_date(rid=nil, full_resp=false)
|
104
|
+
# SetUp
|
105
|
+
self.recurrent_rid = rid if rid
|
106
|
+
|
107
|
+
# Create/customize the path & add the auth as param
|
108
|
+
self.path = "transactions/#{self.recurrent_rid}/subscription/up-to-date"
|
109
|
+
self.path = "#{self.path}#{self.request_params.it_keys_to_get_param}"
|
110
|
+
|
111
|
+
# Perform the request
|
112
|
+
full_resp ? self.resp = self.get_request : self.resp = self.get_json_request
|
113
|
+
|
114
|
+
# return it received resp
|
115
|
+
self.resp
|
116
|
+
end
|
117
|
+
|
118
|
+
# ================ STATIC methods ================
|
119
|
+
# GET /api/transactions/:rid by the rid passed
|
120
|
+
def self.find(rid)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/lib/nitro_pay/version.rb
CHANGED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,11 @@
|
|
1
|
+
BANDEIRA | Autenticação | Número Cartão | Validade | Cod. Segurança
|
2
|
+
VISA | OK | 4012001037141112 | 05/2018 | 123
|
3
|
+
MASTER | OK | 5453010000066167 | 05/2018 | 123
|
4
|
+
VISA | NO | 4012001038443335 | 05/2018 | 123
|
5
|
+
MASTER | NO | 5453010000066167 | 05/2018 | 123
|
6
|
+
AMERICAN EXP| NO | 376449047333005 | 05/2018 | 1234
|
7
|
+
ELO | NO | 6362970000457013 | 05/2018 | 123
|
8
|
+
DINNERS | NO | 36490102462661 | 05/2018 | 123
|
9
|
+
DISCOVER | NO | 6011020000245045 | 05/2018 | 123
|
10
|
+
JCB | NO | 3566007770004971 | 05/2018 | 123
|
11
|
+
AURA | NO |5078601912345600019| 05/2018 | 123
|
data/spec/file_helper.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module Helpers
|
2
|
+
def comment_proxy_yml
|
3
|
+
process_file :comment
|
4
|
+
end
|
5
|
+
|
6
|
+
def uncomment_proxy_yml
|
7
|
+
process_file :uncomment
|
8
|
+
end
|
9
|
+
|
10
|
+
def process_file action
|
11
|
+
file_path = './lib/nitro_pay/config/proxy.yml'
|
12
|
+
file = File.open(file_path, 'r+')
|
13
|
+
old_lines = file.readlines
|
14
|
+
new_lines = []
|
15
|
+
|
16
|
+
# Create the new lines
|
17
|
+
if action == :comment
|
18
|
+
old_lines.each do |old_line|
|
19
|
+
unless old_line[0] == '#'
|
20
|
+
old_line = '#' + old_line
|
21
|
+
end
|
22
|
+
|
23
|
+
new_lines << old_line
|
24
|
+
end
|
25
|
+
elsif action == :uncomment
|
26
|
+
old_lines.each do |old_line|
|
27
|
+
new_lines << old_line.remove('#')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
write_lines file_path, new_lines
|
32
|
+
end
|
33
|
+
|
34
|
+
# Write the new lines
|
35
|
+
def write_lines file_path, new_lines
|
36
|
+
File.open(file_path, 'w') do |file|
|
37
|
+
new_lines.each do |new_line|
|
38
|
+
file.puts new_line
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/spec/helpers.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'file_helper'
|
2
|
+
module Helpers
|
3
|
+
def http_success
|
4
|
+
200
|
5
|
+
end
|
6
|
+
|
7
|
+
def accessible? url
|
8
|
+
RestClient.proxy = NitroPay.proxy
|
9
|
+
resp = RestClient.get url
|
10
|
+
RestClient.proxy = nil
|
11
|
+
resp.code == 200 ? true : false
|
12
|
+
end
|
13
|
+
|
14
|
+
def fake_sold_items
|
15
|
+
sold_items = []
|
16
|
+
|
17
|
+
count = 0
|
18
|
+
items_amount = 3
|
19
|
+
|
20
|
+
until count == items_amount do
|
21
|
+
count = count+1
|
22
|
+
rand_count_departments = Random.new.rand(1..10)
|
23
|
+
sold_item = {
|
24
|
+
remote_id: Faker::Number.number(count),
|
25
|
+
name: Faker::Commerce.product_name,
|
26
|
+
description: Faker::Commerce.department(rand_count_departments, rand_count_departments==2)
|
27
|
+
}
|
28
|
+
|
29
|
+
sold_items << sold_item
|
30
|
+
end
|
31
|
+
|
32
|
+
sold_items
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_json url
|
36
|
+
resp = RestClient.get url
|
37
|
+
JSON.parse(resp).it_keys_to_sym
|
38
|
+
end
|
39
|
+
|
40
|
+
def page_transaction_mock(card_brand, amount, redir_link)
|
41
|
+
page = {}
|
42
|
+
page[:transaction] = NitroPay::Transaction.new({
|
43
|
+
card:{brand: card_brand},
|
44
|
+
clients:{name:Faker::Name.name, email:Faker::Internet.free_email, legal_id:CPF.generate},
|
45
|
+
amount: amount, # The last 2 numbers are the cents
|
46
|
+
redirect_link: redir_link # (* optional) used only for CieloPage
|
47
|
+
})
|
48
|
+
|
49
|
+
# Fake SoldItems added
|
50
|
+
page[:transaction].sold_items = fake_sold_items
|
51
|
+
|
52
|
+
# Perform BuyPage
|
53
|
+
page[:resp] = page[:transaction].charge_page
|
54
|
+
page
|
55
|
+
end
|
56
|
+
end
|