nexio_activemerchant 0.2.7 → 0.4.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 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