nexio_activemerchant 0.2.7 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09b41171285a1391d8d99233c196e387bc1822c3efdbb8187eb8422764f1beae'
4
- data.tar.gz: 85f11b3a8a146594f3f0919a1f8b3aa2a6a5415c342b541931acea748ce81c94
3
+ metadata.gz: 270866bef6896178cdc8fcae4ae1c2e158bcfdca7106ae24a09d91b7823e15a0
4
+ data.tar.gz: d7d76d0a219146a0e8a7f37e68944836e693c1218fd8fad8fd75f4c1ed65b2a9
5
5
  SHA512:
6
- metadata.gz: '094f7c5d9058ef1ceb7ed58fc77f217c0190175a64f75a24027909d75003b3bd4b7da8812a3b972b62b864e5f123e63c38129856ad74f0d5483592cc803377c0'
7
- data.tar.gz: b82b1e74e01c35752e1f64e4949ccb038b841d62e32686590a58e3aba1ee2dd6959883fda3d0e9a7529130b86b216419f3983ee1b3ade95b0b93b18500a3fcae
6
+ metadata.gz: 395afe852039e7b35d972a2230e52cd77c938bb94ccb44f49cf14fff5734636cc5a7c4885d37d07b21d82fb67fe174cfb020611190ffd953ebb7e6cb8296bca0
7
+ data.tar.gz: 03651caa2f01c34336ac9f66e4c577c191b77dae6fe4f0f536f7801a066669c181c1cd86c0909a0ae29f8e1d8a92641d4eed9cd8ac0b52bf9dc43850577ed58b
data/.rubocop.yml CHANGED
@@ -11,6 +11,10 @@
11
11
 
12
12
  Gemspec/RequiredRubyVersion:
13
13
  Enabled: false
14
+ Metrics/AbcSize:
15
+ Enabled: false
16
+ Metrics/CyclomaticComplexity:
17
+ Enabled: false
14
18
  Metrics/ClassLength:
15
19
  Enabled: false
16
20
  Metrics/MethodLength:
@@ -19,3 +23,5 @@ Lint/AssignmentInCondition:
19
23
  Enabled: false
20
24
  Style/GuardClause:
21
25
  Enabled: false
26
+ Style/Documentation:
27
+ Enabled: false
@@ -3,7 +3,7 @@
3
3
  module ActiveMerchant
4
4
  module Billing
5
5
  class EncryptedNexioCard < CreditCard
6
- ALLOWED_CARD_BRANDS = %w(amex discover jcb mastercard visa).freeze
6
+ ALLOWED_CARD_BRANDS = %w[amex discover jcb mastercard visa].freeze
7
7
 
8
8
  attr_accessor :encrypted_number, :own_form, :one_time_token
9
9
 
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './nexio_base_gateway'
4
+
5
+ module ActiveMerchant
6
+ module Billing
7
+ class NexioApmGateway < NexioBaseGateway
8
+ self.display_name = 'Nexio AMP'
9
+ self.base_path = '/apm/v3'
10
+ self.abstract_class = false
11
+
12
+ OneTimeToken = Struct.new(:token, :iframe_url, :redirect_urls, :button_urls)
13
+
14
+ def generate_token(money, options = {})
15
+ post = build_payload(options)
16
+ add_invoice(post, money, options)
17
+ post[:data][:paymentMethod] = options[:payment_method] if options[:payment_method].present?
18
+ add_order_data(post, options)
19
+ post[:customerRedirectUrl] = options[:callback_url] if options[:callback_url].present?
20
+ post[:processingOptions][:saveRecurringToken] = true if options[:save_token]
21
+ post[:isAuthOnly] = true if options[:is_auth_only]
22
+ resp = commit('token', post)
23
+ return unless resp.success?
24
+
25
+ OneTimeToken.new(
26
+ resp.params['token'],
27
+ resp.params['expressIFrameUrl'],
28
+ map_urls(resp.params['redirectUrls']),
29
+ map_urls(resp.params['buttonIFrameUrls'])
30
+ )
31
+ end
32
+
33
+ def purchase(money, payment, options = {})
34
+ post = build_payload(options)
35
+ add_invoice(post, money, options)
36
+ post[:apm] = { token: payment.is_a?(Spree::PaymentSource) ? payment.gateway_payment_profile_id : payment }
37
+ add_order_data(post, options)
38
+ post[:isAuthOnly] = true if options[:is_auth_only]
39
+ commit('process', post)
40
+ end
41
+
42
+ def authorize(money, payment, options = {})
43
+ purchase(money, payment, options.merge(is_auth_only: true))
44
+ end
45
+
46
+ private
47
+
48
+ def map_urls(list)
49
+ list.each_with_object({}) { |item, acc| acc[item['paymentMethod']] = item['url'] }
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,59 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
+ require 'nexio_activemerchant/transaction'
4
5
 
5
6
  module ActiveMerchant
6
7
  module Billing
7
- class NexioGateway < Gateway
8
+ class NexioBaseGateway < Gateway
8
9
  self.test_url = 'https://api.nexiopaysandbox.com'
9
10
  self.live_url = 'https://api.nexiopay.com'
10
11
 
11
12
  self.supported_countries = %w[CA US]
12
13
  self.default_currency = 'USD'
13
14
  self.supported_cardtypes = %i[visa master american_express discover]
14
-
15
15
  self.homepage_url = 'https://nex.io'
16
- self.display_name = 'Nexio'
17
-
18
- STANDARD_ERROR_CODE_MAPPING = {}.freeze
16
+ self.abstract_class = true
19
17
 
20
- OneTimeToken = Struct.new(:token, :expiration, :fraud_url)
18
+ class_attribute :base_path
19
+ self.base_path = ''
21
20
 
22
21
  def initialize(options = {})
23
22
  requires!(options, :merchant_id, :auth_token)
24
23
  super
25
24
  end
26
25
 
27
- def generate_token(options = {})
28
- post = build_payload(options)
29
- post[:data][:allowedCardTypes] = %w(amex discover jcb mastercard visa)
30
- add_currency(post, options)
31
- add_order_data(post, options)
32
- add_card_data(post, options)
33
- resp = commit('token', post)
34
- return unless resp.success?
35
-
36
- token, expiration, fraud_url = resp.params.values_at('token', 'expiration', 'fraudUrl')
37
- OneTimeToken.new(token, Time.parse(expiration), fraud_url)
38
- end
39
-
40
- def purchase(money, payment, options = {})
41
- post = build_payload(options)
42
- post[:processingOptions] ||= {}
43
- post[:processingOptions][:verboseResponse] = true if test?
44
- post[:processingOptions][:customerRedirectUrl] = options[:three_d_callback_url] if options.key?(:three_d_callback_url)
45
- post[:processingOptions][:check3ds] = options[:three_d_secure]
46
- post[:processingOptions][:paymentType] = options[:payment_type] if options.key?(:payment_type)
47
- add_invoice(post, money, options)
48
- add_payment(post, payment, options)
49
- add_order_data(post, options)
50
- commit('process', post)
51
- end
52
-
53
- def authorize(money, payment, options = {})
54
- purchase(money, payment, options.merge(payload: options.fetch(:payload, {}).merge(isAuthOnly: true)))
55
- end
56
-
57
26
  def capture(money, authorization, _options = {})
58
27
  commit('capture', { id: authorization, data: { amount: amount(money).to_f } })
59
28
  end
@@ -67,13 +36,6 @@ module ActiveMerchant
67
36
  commit('void', { id: authorization })
68
37
  end
69
38
 
70
- def verify(credit_card, options = {})
71
- MultiResponse.run(:use_first_response) do |r|
72
- r.process { authorize(100, credit_card, options) }
73
- r.process(:ignore_result) { void(r.authorization, options) }
74
- end
75
- end
76
-
77
39
  def supports_scrubbing?
78
40
  false
79
41
  end
@@ -82,28 +44,19 @@ module ActiveMerchant
82
44
  transcript
83
45
  end
84
46
 
85
- def store(payment, options = {})
86
- post = build_payload(options)
87
- add_card_details(post, payment, options)
88
- add_currency(post, options)
89
- add_order_data(post, options)
90
- resp = commit('saveCard', post)
91
- return unless resp.success?
92
-
93
- resp.params.fetch('token', {}).fetch('token', nil)
94
- end
95
-
96
- def set_webhooks(data)
47
+ def setup_webhooks(data)
97
48
  post = { merchantId: options[:merchant_id].to_s }
98
49
  if data.is_a?(String)
99
50
  post[:webhooks] = {
100
51
  TRANSACTION_AUTHORIZED: { url: data },
101
- TRANSACTION_CAPTURED: { url: data }
52
+ TRANSACTION_CAPTURED: { url: data },
53
+ TRANSACTION_SETTLED: { url: data }
102
54
  }
103
55
  else
104
56
  webhooks = {}
105
57
  webhooks[:TRANSACTION_AUTHORIZED] = { url: data[:authorized] } if data.key?(:authorized)
106
58
  webhooks[:TRANSACTION_CAPTURED] = { url: data[:captured] } if data.key?(:captured)
59
+ webhooks[:TRANSACTION_SETTLED] = { url: data[:settled] } if data.key?(:settled)
107
60
  post[:webhooks] = webhooks
108
61
  end
109
62
  commit('webhook', post)
@@ -114,12 +67,24 @@ module ActiveMerchant
114
67
  end
115
68
 
116
69
  def get_transaction(id)
117
- parse(ssl_get(action_url("/transaction/v3/paymentId/#{id}"), base_headers))
118
- rescue ResponseError => e
70
+ data = parse(ssl_get(action_url("/transaction/v3/paymentId/#{id}"), base_headers))
71
+ ::NexioActivemerchant::Transaction.new(data)
72
+ rescue ResponseError
73
+ nil
119
74
  end
120
75
 
121
76
  private
122
77
 
78
+ def build_payload(params)
79
+ result = params.fetch(:payload, {}).deep_dup
80
+ result[:data] ||= {}
81
+ result[:data][:customer] ||= {}
82
+ result[:processingOptions] ||= {}
83
+ result[:processingOptions][:merchantId] ||= options[:merchant_id]
84
+ result[:processingOptions][:verboseResponse] = true if test?
85
+ result
86
+ end
87
+
123
88
  def add_invoice(post, money, options)
124
89
  post[:data][:amount] = amount(money).to_f
125
90
  add_currency(post, options)
@@ -134,11 +99,13 @@ module ActiveMerchant
134
99
  case customer
135
100
  when String
136
101
  post[:data][:customer][:email] = customer
102
+ post[:data][:customer][:customerRef] = customer
137
103
  when Hash
138
104
  post[:data][:customer].merge!({
139
105
  firstName: customer[:first_name],
140
106
  lastName: customer[:last_name],
141
- email: customer[:email]
107
+ email: customer[:email],
108
+ customerRef: customer[:email]
142
109
  })
143
110
  end
144
111
  end
@@ -176,67 +143,10 @@ module ActiveMerchant
176
143
  AddressOne: :address1, AddressTwo: :address2, City: :city,
177
144
  Country: :country, Phone: :phone, Postal: :zip, State: :state
178
145
  }.each do |suffix, key|
179
- post[:data][:customer]["#{prefix}#{suffix}"] = data[key] if data[key].present?
180
- end
181
- end
182
-
183
- def add_payment(post, payment, options)
184
- post[:tokenex] = token_from(payment)
185
- if payment.is_a?(Spree::CreditCard)
186
- post[:card] = {
187
- cardHolderName: payment.name,
188
- cardType: payment.brand
189
- }.merge!(post.fetch(:card, {}))
190
- end
191
- post[:processingOptions] ||= {}
192
- post[:processingOptions][:merchantId] = self.options[:merchant_id].to_s
193
- post[:processingOptions][:saveCardToken] = options[:save_credit_card] if options.key?(:save_credit_card)
194
- end
195
-
196
- def token_from(payment)
197
- return { token: payment } if payment.is_a?(String)
198
-
199
- {
200
- token: payment.gateway_payment_profile_id,
201
- lastFour: payment.last_digits,
202
- cardType: payment.brand
203
- }
204
- end
205
-
206
- def add_card_data(post, options)
207
- if card = options[:card]
208
- post[:card] = {
209
- cardHolderName: card[:name],
210
- expirationMonth: card[:month],
211
- expirationYear: card[:year]
212
- }
213
- end
214
- end
215
-
216
- def add_card_details(post, payment, _options)
217
- if payment.is_a?(EncryptedNexioCard)
218
- raise ArgumentError, 'The provided card is invalid' unless payment.valid?
219
-
220
- post[:card] = {
221
- cardHolderName: payment.name,
222
- encryptedNumber: payment.encrypted_number,
223
- expirationMonth: payment.month,
224
- expirationYear: payment.short_year,
225
- cardType: payment.brand,
226
- securityCode: payment.verification_value
227
- }
228
- post[:token] = payment.one_time_token
229
- else
230
- raise ArgumentError, "Only #{EncryptedNexioCard} payment method is supported to store cards"
146
+ post[:data][:customer]["#{prefix}#{suffix}"] = data[key]
231
147
  end
232
148
  end
233
149
 
234
- def parse(body)
235
- JSON.parse(body)
236
- rescue StandardError
237
- {}
238
- end
239
-
240
150
  def commit(action, parameters)
241
151
  payload = parse(ssl_post(commit_action_url(action, parameters), post_data(action, parameters), base_headers))
242
152
 
@@ -262,29 +172,23 @@ module ActiveMerchant
262
172
  )
263
173
  end
264
174
 
265
- def response_status(action, payload)
266
- case action
267
- when 'process' then authorization_from(payload).present?
268
- else
269
- true
270
- end
271
- end
272
-
273
- def authorization_from(payload)
274
- payload.fetch('id', nil)
175
+ def post_data(_action, post = {})
176
+ JSON.dump(post)
275
177
  end
276
178
 
277
- def post_data(_action, parameters = {})
278
- { merchantId: options[:merchant_id] }.merge(parameters).to_json
179
+ def parse(body)
180
+ JSON.parse(body)
181
+ rescue StandardError
182
+ {}
279
183
  end
280
184
 
281
185
  def commit_action_url(action, _parameters)
282
186
  path = case action
283
- when 'webhook' then '/webhook/v3/config'
284
- when 'secret' then '/webhook/v3/secret'
285
- else
286
- "/pay/v3/#{action}"
287
- end
187
+ when 'webhook' then '/webhook/v3/config'
188
+ when 'secret' then '/webhook/v3/secret'
189
+ else
190
+ "#{self.class.base_path}/#{action}"
191
+ end
288
192
  action_url(path)
289
193
  end
290
194
 
@@ -292,6 +196,24 @@ module ActiveMerchant
292
196
  "#{test? ? test_url : live_url}#{path}"
293
197
  end
294
198
 
199
+ def base_headers(custom = {})
200
+ { Authorization: "Basic #{options[:auth_token]}" }.merge(custom)
201
+ end
202
+
203
+ SUCCESS_PROCESS_STATUSES = %w[authOnlyPending authorizedPending pending authOnly settled].freeze
204
+
205
+ def response_status(action, payload)
206
+ case action
207
+ when 'process' then SUCCESS_PROCESS_STATUSES.include?(payload['transactionStatus'])
208
+ else
209
+ true
210
+ end
211
+ end
212
+
213
+ def authorization_from(payload)
214
+ payload.fetch('id', nil)
215
+ end
216
+
295
217
  def build_avs_result(data)
296
218
  return if data.blank?
297
219
 
@@ -303,14 +225,6 @@ module ActiveMerchant
303
225
 
304
226
  CVVResult.new(data.fetch('gatewayMessage', {}).fetch('cvvresponse', nil))
305
227
  end
306
-
307
- def build_payload(params)
308
- { data: { customer: {} } }.merge!(params.fetch(:payload, {}))
309
- end
310
-
311
- def base_headers(custom = {})
312
- { Authorization: "Basic #{options[:auth_token]}" }
313
- end
314
228
  end
315
229
  end
316
230
  end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './nexio_base_gateway'
4
+
5
+ module ActiveMerchant
6
+ module Billing
7
+ class NexioGateway < NexioBaseGateway
8
+ self.display_name = 'Nexio'
9
+ self.base_path = '/pay/v3'
10
+ self.abstract_class = false
11
+
12
+ OneTimeToken = Struct.new(:token, :expiration, :fraud_url)
13
+
14
+ def generate_token(options = {})
15
+ post = build_payload(options)
16
+ post[:data][:allowedCardTypes] = %w[amex discover jcb mastercard visa]
17
+ add_currency(post, options)
18
+ add_order_data(post, options)
19
+ add_card_data(post, options)
20
+ resp = commit('token', post)
21
+ return unless resp.success?
22
+
23
+ token, expiration, fraud_url = resp.params.values_at('token', 'expiration', 'fraudUrl')
24
+ OneTimeToken.new(token, Time.parse(expiration), fraud_url)
25
+ end
26
+
27
+ def purchase(money, payment, options = {})
28
+ post = build_payload(options)
29
+ add_invoice(post, money, options)
30
+ add_payment(post, payment, options)
31
+ add_order_data(post, options)
32
+ commit('process', post)
33
+ end
34
+
35
+ def authorize(money, payment, options = {})
36
+ purchase(money, payment, options.merge(payload: options.fetch(:payload, {}).merge(isAuthOnly: true)))
37
+ end
38
+
39
+ def verify(credit_card, options = {})
40
+ MultiResponse.run(:use_first_response) do |r|
41
+ r.process { authorize(100, credit_card, options) }
42
+ r.process(:ignore_result) { void(r.authorization, options) }
43
+ end
44
+ end
45
+
46
+ def store(payment, options = {})
47
+ post = build_payload(options)
48
+ post[:merchantId] ||= options[:merchant_id]
49
+ add_card_details(post, payment, options)
50
+ add_currency(post, options)
51
+ add_order_data(post, options)
52
+ resp = commit('saveCard', post)
53
+ return unless resp.success?
54
+
55
+ resp.params.fetch('token', {}).fetch('token', nil)
56
+ end
57
+
58
+ private
59
+
60
+ def add_payment(post, payment, options)
61
+ post[:tokenex] = token_from(payment)
62
+ if payment.is_a?(Spree::CreditCard)
63
+ post[:card] = {
64
+ cardHolderName: payment.name,
65
+ cardType: payment.brand
66
+ }.merge!(post.fetch(:card, {}))
67
+ end
68
+ post[:processingOptions][:saveCardToken] = options[:save_credit_card] if options.key?(:save_credit_card)
69
+ if options[:three_d_callback_url].present?
70
+ post[:processingOptions][:customerRedirectUrl] = options[:three_d_callback_url]
71
+ end
72
+ post[:processingOptions][:check3ds] = options[:three_d_secure]
73
+ post[:processingOptions][:paymentType] = options[:payment_type] if options[:payment_type].present?
74
+ end
75
+
76
+ def token_from(payment)
77
+ return { token: payment } if payment.is_a?(String)
78
+
79
+ {
80
+ token: payment.gateway_payment_profile_id,
81
+ lastFour: payment.last_digits,
82
+ cardType: payment.brand
83
+ }
84
+ end
85
+
86
+ def add_card_data(post, options)
87
+ if card = options[:card]
88
+ post[:card] = {
89
+ cardHolderName: card[:name],
90
+ expirationMonth: card[:month],
91
+ expirationYear: card[:year]
92
+ }
93
+ end
94
+ end
95
+
96
+ def add_card_details(post, payment, _options)
97
+ if payment.is_a?(EncryptedNexioCard)
98
+ raise ArgumentError, 'The provided card is invalid' unless payment.valid?
99
+
100
+ post[:card] = {
101
+ cardHolderName: payment.name,
102
+ encryptedNumber: payment.encrypted_number,
103
+ expirationMonth: payment.month,
104
+ expirationYear: payment.short_year,
105
+ cardType: payment.brand,
106
+ securityCode: payment.verification_value
107
+ }
108
+ post[:token] = payment.one_time_token
109
+ else
110
+ raise ArgumentError, "Only #{EncryptedNexioCard} payment method is supported to store cards"
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NexioActivemerchant
4
+ Transaction = Struct.new(:data) do
5
+ def status
6
+ data['transactionStatus']
7
+ end
8
+
9
+ def amount
10
+ data['amount'].to_d
11
+ end
12
+ end
13
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NexioActivemerchant
4
- VERSION = '0.2.7'
4
+ VERSION = '0.4.0'
5
5
  end
@@ -3,7 +3,8 @@
3
3
  require 'activemerchant'
4
4
  require 'active_merchant/billing/rails'
5
5
  require 'active_merchant/billing/encrypted_nexio_card'
6
- require 'active_merchant/billing/gateways/nexio'
6
+ require 'active_merchant/billing/gateways/nexio_gateway'
7
+ require 'active_merchant/billing/gateways/nexio_apm_gateway'
7
8
  require 'nexio_activemerchant/version'
8
9
 
9
10
  module NexioActivemerchant
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nexio_activemerchant
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.7
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Whitespectre
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-10-08 00:00:00.000000000 Z
11
+ date: 2021-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemerchant
@@ -57,8 +57,11 @@ files:
57
57
  - bin/console
58
58
  - bin/setup
59
59
  - lib/active_merchant/billing/encrypted_nexio_card.rb
60
- - lib/active_merchant/billing/gateways/nexio.rb
60
+ - lib/active_merchant/billing/gateways/nexio_apm_gateway.rb
61
+ - lib/active_merchant/billing/gateways/nexio_base_gateway.rb
62
+ - lib/active_merchant/billing/gateways/nexio_gateway.rb
61
63
  - lib/nexio_activemerchant.rb
64
+ - lib/nexio_activemerchant/transaction.rb
62
65
  - lib/nexio_activemerchant/version.rb
63
66
  - nexio_activemerchant.gemspec
64
67
  homepage: https://github.com/whitespectre/nexio_activemerchant