conekta-motion 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 246f61805b3130690f81854eb536a0156f31819e
4
+ data.tar.gz: 8ec2be3b78d4eb18804b848830641e51564c38ff
5
+ SHA512:
6
+ metadata.gz: e0697b4b45f3d2c364056318982cac2003cbeb4800dbbedc7d2de10d66159f1ac5142dab970c1cdc7259de1fa1362f56c29578d79f12afe4834664f9d6a50a25
7
+ data.tar.gz: 5640c419d77616eb2df4d51a7f6960cb6e672a1e69f5ea93e1753f20b34e69742a6cb7081ff26c2d6f5b41c142fe13101e4fc33b7ac7da8909158f502450ef53
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # ConektaMotion
2
+ A Rubymotion gem to use mexican payment gateway Conekta from a Rubymotion iOS
3
+ app.
@@ -0,0 +1,23 @@
1
+ unless defined?(Motion::Project::Config)
2
+ raise "The MotionKit gem must be required within a RubyMotion project Rakefile."
3
+ end
4
+ require 'dbt'
5
+ require 'afmotion'
6
+
7
+ Motion::Project::App.setup do |app|
8
+ core_lib = File.join(File.dirname(__FILE__), 'conekta-motion')
9
+ constants_lib = File.dirname(__FILE__)
10
+
11
+ constants_file = File.join(constants_lib, 'constants.rb')
12
+ app.files.insert(0, constants_file) if File.exist?(constants_file)
13
+
14
+ # scans app.files until it finds app/ (the default)
15
+ # if found, it inserts just before those files, otherwise it
16
+ # will insert to the end of the list
17
+ insert_point = app.files.find_index { |file| file =~ /^(?:\.\/)?app\// } || 0
18
+ Dir.glob(File.join(core_lib, '**/*.rb')).reverse.each do |file|
19
+ app.files.insert(insert_point, file)
20
+ end
21
+
22
+ DBT.analyze(app)
23
+ end
@@ -0,0 +1,96 @@
1
+ module ConektaMotion
2
+ class Card
3
+ attr_accessor :number, :name, :cvc, :expiration_month,
4
+ :expiration_year
5
+ attr_reader :errors
6
+
7
+ def initialize(number = nil, name = nil, cvc = nil,
8
+ expiration_month = nil, expiration_year = nil)
9
+ self.number = number
10
+ self.name = name
11
+ self.cvc = cvc
12
+ self.expiration_month = expiration_month
13
+ self.expiration_year = expiration_year
14
+
15
+ @errors = {}
16
+ end
17
+
18
+ def to_hash
19
+ {
20
+ card: {
21
+ name: name.to_s,
22
+ number: clean_number.to_s,
23
+ cvc: cvc.to_s,
24
+ exp_month: expiration_month.to_s,
25
+ exp_year: expiration_year.to_s
26
+ }
27
+ }
28
+ end
29
+
30
+ def last_four
31
+ clean_number.reverse[0..3].reverse
32
+ end
33
+
34
+ def valid?
35
+ @errors = {}
36
+
37
+ @errors[:number] = 'Número de tarjeta inválida' if brand.nil? || !valid_card?
38
+
39
+ @errors[:name] = 'Nombre de tarjetahabiente inválido' if name.nil? || name.empty?
40
+
41
+ @errors[:cvc] = 'Código de seguridad inválido' if !valid_cvc?
42
+
43
+ @errors[:expiration_month] = 'Mes de expiración inválido' if !valid_expiration_month?
44
+
45
+ @errors[:expiration_year] = 'Año de expiración inválido' if !valid_expiration_year?
46
+
47
+ @errors.keys.count == 0
48
+ end
49
+
50
+ def brand
51
+ _number = clean_number
52
+
53
+ return :dinners if _number.length == 14 && _number =~ /^3(0[0-5]|[68])/ # 300xxx-305xxx, 36xxxx, 38xxxx
54
+ return :amex if _number.length == 15 && _number =~ /^3[47]/ # 34xxxx, 37xxxx
55
+ return :visa if [13,16].include?(_number.length) && _number =~ /^4/ # 4xxxxx
56
+ return :master if _number.length == 16 && _number =~ /^5[1-5]/ # 51xxxx-55xxxx
57
+ return :discover if _number.length == 16 && _number =~ /^6011/ # 6011xx
58
+ nil
59
+ end
60
+
61
+ private
62
+ def clean_number
63
+ return '' if number.nil?
64
+ number.gsub(/\D/, '')
65
+ end
66
+
67
+ def valid_card?
68
+ reversed_number = clean_number.reverse
69
+
70
+ relative_number = { '0' => 0, '1' => 2, '2' => 4, '3' => 6, '4' => 8, '5' => 1, '6' => 3, '7' => 5, '8' => 7, '9' => 9 }
71
+
72
+ accumulator = 0
73
+
74
+ reversed_number.split('').each_with_index do |digit, index|
75
+ accumulator += (index % 2 == 0) ? digit.to_i : relative_number[digit]
76
+ end
77
+
78
+ accumulator % 10 == 0
79
+ end
80
+
81
+ def valid_cvc?
82
+ return false if cvc.nil? || !(cvc =~ /^\d{3,4}/) || !(3..4).include?(cvc.length)
83
+ true
84
+ end
85
+
86
+ def valid_expiration_month?
87
+ return false if expiration_month.nil? || !(expiration_month =~ /^\d{2}/) || expiration_month.length != 2 || !(1..12).include?(expiration_month.to_i)
88
+ true
89
+ end
90
+
91
+ def valid_expiration_year?
92
+ return false if expiration_year.nil? || !(expiration_year =~ /^\d{2}/) || expiration_year.length != 2 || !(expiration_year.to_i > 0)
93
+ true
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,3 @@
1
+ module ConektaMotion
2
+ ::CM = ConektaMotion unless defined?(::CM)
3
+ end
@@ -0,0 +1,44 @@
1
+ module ConektaMotion
2
+ class Conekta
3
+ def initialize(api_key = API_KEY)
4
+ @api_key = api_key
5
+ end
6
+
7
+ def tokenize_card(card, &block)
8
+ web_client.post('/tokens', card.to_hash) do |response|
9
+ token = nil
10
+
11
+ if response.failure?
12
+ unless response.error.is_a?(NSURLError)
13
+ token = Token.init_from response.object
14
+ end
15
+ else
16
+ token = Token.init_from response.object
17
+ end
18
+
19
+ block.call token
20
+ end
21
+ end
22
+
23
+ private
24
+ def encoded_api_key
25
+ [@api_key].pack("m0")
26
+ end
27
+
28
+ def web_client
29
+ key = encoded_api_key
30
+
31
+ @@web_client ||= begin
32
+ AFMotion::Client.build('https://api.conekta.io') do
33
+ request_serializer :json
34
+ response_serializer :json
35
+
36
+ header 'Content-type', 'application/json'
37
+ header 'Accept', 'application/vnd.conekta-v0.3.0+json'
38
+ header 'Authorization', "Basic #{key}"
39
+ header 'Conekta-Client-User-Agent', '{"agent": "Conekta Conekta iOS SDK"}' #'ConektaMotion iOS', seems that Conekta only accepts this agent.
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,28 @@
1
+ module ConektaMotion
2
+ class Token
3
+ attr_reader :token_id, :error
4
+
5
+ def initialize(token_id, error)
6
+ @token_id = token_id
7
+ @error = error
8
+ end
9
+
10
+ def valid?
11
+ error.nil?
12
+ end
13
+
14
+ class << self
15
+ def init_from(response)
16
+ token_id = nil
17
+ error = nil
18
+
19
+ if response
20
+ token_id = response[:id] if response[:object] == 'token'
21
+ error = response[:message_to_purchaser] if response[:object] == 'error'
22
+ end
23
+
24
+ Token.new token_id, error
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module ConektaMotion
2
+ VERSION = '0.1.1'
3
+ end
data/lib/constants.rb ADDED
@@ -0,0 +1,3 @@
1
+ module ConektaMotion
2
+ API_KEY = 'key_DzVhxySdJAfmboDePSVUCuQ'
3
+ end
data/spec/card_spec.rb ADDED
@@ -0,0 +1,212 @@
1
+ describe 'Card' do
2
+ before do
3
+ @card = ConektaMotion::Card.new
4
+ end
5
+
6
+ describe 'card properties' do
7
+ it 'accepts card number' do
8
+ card = CM::Card.new '4242 4242 4242 4242'
9
+
10
+ card.number.should == '4242 4242 4242 4242'
11
+ end
12
+
13
+ it 'accepts card holder name' do
14
+ card = CM::Card.new '4242 4242 4242 4242',
15
+ 'Juan Perez'
16
+
17
+ card.name.should == 'Juan Perez'
18
+ end
19
+
20
+ it 'accepts CVC code' do
21
+ card = CM::Card.new '4242 4242 4242 4242',
22
+ 'Juan Perez', '001'
23
+
24
+ card.cvc.should == '001'
25
+ end
26
+
27
+ it 'accepts expiration month' do
28
+ card = CM::Card.new '4242 4242 4242 4242',
29
+ 'Juan Perez', '001', '01'
30
+
31
+ card.expiration_month.should == '01'
32
+ end
33
+
34
+ it 'accepts expiration year' do
35
+ card = CM::Card.new '4242 4242 4242 4242',
36
+ 'Juan Perez', '001', '01', '14'
37
+
38
+ card.expiration_year.should == '14'
39
+ end
40
+ end
41
+
42
+ describe 'card validation' do
43
+ describe 'card number' do
44
+ it 'is valid' do
45
+ @card.number = '4242 4242 4242 4242'
46
+ @card.valid?
47
+
48
+ @card.errors.has_key?(:number).should == false
49
+ end
50
+
51
+ it 'is invalid due wrong number' do
52
+ @card.number = '8242 4242'
53
+ @card.valid?
54
+
55
+ @card.errors.has_key?(:number).should == true
56
+ @card.errors[:number].should == 'Número de tarjeta inválida'
57
+ end
58
+
59
+ it 'is invalid due do not pass lunh validation' do
60
+ @card.number = '4242 4242 4242 8282'
61
+ @card.valid?
62
+
63
+ @card.errors.has_key?(:number).should == true
64
+ @card.errors[:number].should == 'Número de tarjeta inválida'
65
+ end
66
+ end
67
+
68
+ describe 'holder name' do
69
+ it 'is valid' do
70
+ @card.name = 'Juan Perez'
71
+ @card.valid?
72
+
73
+ @card.errors.has_key?(:name).should == false
74
+ end
75
+
76
+ it 'is invalid' do
77
+ @card.name = ''
78
+ @card.valid?
79
+
80
+ @card.errors.has_key?(:name).should == true
81
+ @card.errors[:name].should == 'Nombre de tarjetahabiente inválido'
82
+ end
83
+ end
84
+
85
+ describe 'cvc' do
86
+ it 'is valid' do
87
+ @card.cvc = '090'
88
+ @card.valid?
89
+
90
+ @card.errors.has_key?(:cvc).should == false
91
+ end
92
+
93
+ it 'is invalid if empty' do
94
+ @card.cvc = nil
95
+ @card.valid?
96
+
97
+ @card.errors.has_key?(:cvc).should == true
98
+ @card.errors[:cvc].should == 'Código de seguridad inválido'
99
+ end
100
+
101
+ it 'is invalid wrong number ' do
102
+ @card.cvc = '90'
103
+ @card.valid?
104
+
105
+ @card.errors.has_key?(:cvc).should == true
106
+ @card.errors[:cvc].should == 'Código de seguridad inválido'
107
+ end
108
+ end
109
+ end
110
+
111
+ describe 'expiration month' do
112
+ it 'is valid' do
113
+ @card.expiration_month = '01'
114
+ @card.valid?
115
+
116
+ @card.errors.has_key?(:expiration_month).should == false
117
+ end
118
+
119
+ it 'is invalid wrong value' do
120
+ @card.expiration_month = 'a1'
121
+ @card.valid?
122
+
123
+ @card.errors.has_key?(:expiration_month).should == true
124
+ @card.errors[:expiration_month].should == 'Mes de expiración inválido'
125
+ end
126
+
127
+ it 'is invalid when wrong month number' do
128
+ @card.expiration_month = '13'
129
+ @card.valid?
130
+
131
+ @card.errors.has_key?(:expiration_month).should == true
132
+ @card.errors[:expiration_month].should == 'Mes de expiración inválido'
133
+ end
134
+ end
135
+
136
+ describe 'expiration year' do
137
+ it 'is valid' do
138
+ @card.expiration_year = '17'
139
+ @card.valid?
140
+
141
+ @card.errors.has_key?(:expiration_year).should == false
142
+ end
143
+
144
+ it 'is invalid wrong value' do
145
+ @card.expiration_year = '1b'
146
+ @card.valid?
147
+
148
+ @card.errors.has_key?(:expiration_year).should == true
149
+ @card.errors[:expiration_year].should == 'Año de expiración inválido'
150
+ end
151
+ end
152
+
153
+ describe 'card brand' do
154
+ it 'detects visa cards' do
155
+ @card.number = '4035330730728765'
156
+
157
+ @card.brand.should == :visa
158
+ end
159
+
160
+ it 'detects mastercard cards' do
161
+ @card.number = '5352081557347062'
162
+
163
+ @card.brand.should == :master
164
+ end
165
+
166
+ it 'detects amex cards' do
167
+ @card.number = '342411430263950'
168
+
169
+ @card.brand.should == :amex
170
+ end
171
+
172
+ it 'detects dinners cards' do
173
+ @card.number = '38520000023237'
174
+
175
+ @card.brand.should == :dinners
176
+ end
177
+
178
+ it 'detects discover cards' do
179
+ @card.number = '6011111111111117'
180
+
181
+ @card.brand.should == :discover
182
+ end
183
+ end
184
+
185
+ describe '#to_hash' do
186
+ it 'serialize card data' do
187
+ @card.number = '4242 4242 4242 4242'
188
+ @card.name = 'Juan Perez'
189
+ @card.cvc = '090'
190
+ @card.expiration_month = '02'
191
+ @card.expiration_year = '18'
192
+
193
+ @card.to_hash.should == { card:
194
+ {
195
+ name: "Juan Perez",
196
+ number: "4242424242424242",
197
+ cvc: "090",
198
+ exp_month: "02",
199
+ exp_year: "18"
200
+ }
201
+ }
202
+ end
203
+ end
204
+
205
+ describe '#last_four' do
206
+ it 'returns card number last four digits' do
207
+ @card.number = '4242 4242 4242 4242'
208
+
209
+ @card.last_four.should == '4242'
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,28 @@
1
+ describe 'Conekta' do
2
+ extend WebStub::SpecHelpers
3
+
4
+ before do
5
+ disable_network_access!
6
+ @conekta = ConektaMotion::Conekta.new
7
+ end
8
+
9
+ context '#tokenize' do
10
+ it 'should tokenize a valid card' do
11
+ stub_request(:post, 'https://api.conekta.io/tokens').
12
+ to_return(json: { id: 'tok_pWv5DipgPvbabidg4', livemode: false, used: false, object: 'token' })
13
+
14
+ card = CM::Card.new '4242 4242 4242 4242', 'Juan Perez',
15
+ '090', '02', '18'
16
+
17
+ @conekta.tokenize_card card do |token|
18
+ @token = token
19
+ resume
20
+ end
21
+
22
+ wait_max 1.0 do
23
+ @token.nil?.should == false
24
+ @token.valid?.should == true
25
+ end
26
+ end
27
+ end
28
+ end
data/spec/main_spec.rb ADDED
@@ -0,0 +1,9 @@
1
+ describe "Application 'conekta-motion'" do
2
+ # before do
3
+ # @app = UIApplication.sharedApplication
4
+ # end
5
+ #
6
+ # it "has one window" do
7
+ # @app.windows.size.should == 1
8
+ # end
9
+ end
@@ -0,0 +1,21 @@
1
+ describe 'Token' do
2
+ describe 'parse' do
3
+ it 'ok response' do
4
+ response = { id: 'tok_pWv5DipgPvbabidg4', livemode: false, used: false, object: 'token' }
5
+
6
+ token = CM::Token.init_from response
7
+
8
+ token.valid?.should == true
9
+ token.token_id.should == 'tok_pWv5DipgPvbabidg4'
10
+ end
11
+
12
+ it 'error response' do
13
+ response = { type: nil, message_to_purchaser: "The card number is invalid.", object: "error", code: "invalid_number", message: "The card number is invalid.", param: "number" }
14
+
15
+ token = CM::Token.init_from response
16
+
17
+ token.valid?.should == false
18
+ token.error.should == 'The card number is invalid.'
19
+ end
20
+ end
21
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: conekta-motion
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Mario A. Chavez
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dbt
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: afmotion
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: cocoapods
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: motion-cocoapods
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: |2
70
+ ConektaMotion allows your Rubymotion app to tokenize a Bank Card with Conekta.io payment gateway.
71
+ email:
72
+ - mario.chavez@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - README.md
78
+ - lib/conekta-motion.rb
79
+ - lib/conekta-motion/card.rb
80
+ - lib/conekta-motion/conekta-motion.rb
81
+ - lib/conekta-motion/conekta.rb
82
+ - lib/conekta-motion/token.rb
83
+ - lib/conekta-motion/version.rb
84
+ - lib/constants.rb
85
+ - spec/card_spec.rb
86
+ - spec/conekta_spec.rb
87
+ - spec/main_spec.rb
88
+ - spec/token_spec.rb
89
+ homepage: https://github.com/mariochavez/conekta-motion
90
+ licenses:
91
+ - MIT
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.4.5
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Rubymotion library to use payment gateway Conekta.io
113
+ test_files:
114
+ - spec/card_spec.rb
115
+ - spec/conekta_spec.rb
116
+ - spec/main_spec.rb
117
+ - spec/token_spec.rb