nexio_activemerchant 0.2.6 → 0.3.1

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: c71ca2a7f93f4b56d210272254bb2e5b42f21971a67dcdb4577b7676aeb77963
4
- data.tar.gz: 438164011b498f1d97be2d03e41ddfb5cd5d92c06ef66aa65cfdf7def2baafc9
3
+ metadata.gz: aed1e22c05eb961b28f55d0576219532c0b4077ed15f86bf39e33a45f59a42de
4
+ data.tar.gz: c9729141380772a443e1aca9bed8c88ce76c206dd46b834a9342499f242a3ea7
5
5
  SHA512:
6
- metadata.gz: fbcaea369894ec54d50b0aab65cdfa66fd17c97febbeaadd80b07a6c3b7165e97728a115eecfd60d144c0aefc030682fb6d5331946db14129f966636eb584f8f
7
- data.tar.gz: 418c3d334030134e369d897ac2a4a09b7226af458cc6972b2f90d0de1201250614baecc94b06a3f657ab4c6a52e4fdd5c287a8ef2c4a0f86f0bd62c23ca2d91d
6
+ metadata.gz: 2f8bb6a1e812b6f303a269908f997fd50ce308b219b956dceb74aadeb0f1b03d4f5b319d89af7125552d231b4ae24ae1b3624c6328a8fb0f608d367267f030e0
7
+ data.tar.gz: 88a4222587961c5a8d7d10c15c104aa2a75132e9425e8de7d3be64b9c0264c16fb5625ad0cea1dd8db52b58f7ddcfb7715a7ac044e06643eef36a9a640685440
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
@@ -172,74 +139,14 @@ module ActiveMerchant
172
139
  def add_address(post, data, prefix)
173
140
  return post if data.blank?
174
141
 
175
- post[:data][:customer].merge!({
176
- "#{prefix}AddressOne": data[:address1],
177
- "#{prefix}AddressTwo": data[:address2],
178
- "#{prefix}City": data[:city],
179
- "#{prefix}Country": data[:country],
180
- "#{prefix}Phone": data[:phone],
181
- "#{prefix}Postal": data[:zip],
182
- "#{prefix}State": data[:state]
183
- })
184
- end
185
-
186
- def add_payment(post, payment, options)
187
- post[:tokenex] = token_from(payment)
188
- if payment.is_a?(Spree::CreditCard)
189
- post[:card] = {
190
- cardHolderName: payment.name,
191
- cardType: payment.brand
192
- }.merge!(post.fetch(:card, {}))
193
- end
194
- post[:processingOptions] ||= {}
195
- post[:processingOptions][:merchantId] = self.options[:merchant_id].to_s
196
- post[:processingOptions][:saveCardToken] = options[:save_credit_card] if options.key?(:save_credit_card)
197
- end
198
-
199
- def token_from(payment)
200
- return { token: payment } if payment.is_a?(String)
201
-
202
142
  {
203
- token: payment.gateway_payment_profile_id,
204
- lastFour: payment.last_digits,
205
- cardType: payment.brand
206
- }
207
- end
208
-
209
- def add_card_data(post, options)
210
- if card = options[:card]
211
- post[:card] = {
212
- cardHolderName: card[:name],
213
- expirationMonth: card[:month],
214
- expirationYear: card[:year]
215
- }
143
+ AddressOne: :address1, AddressTwo: :address2, City: :city,
144
+ Country: :country, Phone: :phone, Postal: :zip, State: :state
145
+ }.each do |suffix, key|
146
+ post[:data][:customer]["#{prefix}#{suffix}"] = data[key]
216
147
  end
217
148
  end
218
149
 
219
- def add_card_details(post, payment, _options)
220
- if payment.is_a?(EncryptedNexioCard)
221
- raise ArgumentError, 'The provided card is invalid' unless payment.valid?
222
-
223
- post[:card] = {
224
- cardHolderName: payment.name,
225
- encryptedNumber: payment.encrypted_number,
226
- expirationMonth: payment.month,
227
- expirationYear: payment.short_year,
228
- cardType: payment.brand,
229
- securityCode: payment.verification_value
230
- }
231
- post[:token] = payment.one_time_token
232
- else
233
- raise ArgumentError, "Only #{EncryptedNexioCard} payment method is supported to store cards"
234
- end
235
- end
236
-
237
- def parse(body)
238
- JSON.parse(body)
239
- rescue StandardError
240
- {}
241
- end
242
-
243
150
  def commit(action, parameters)
244
151
  payload = parse(ssl_post(commit_action_url(action, parameters), post_data(action, parameters), base_headers))
245
152
 
@@ -265,29 +172,23 @@ module ActiveMerchant
265
172
  )
266
173
  end
267
174
 
268
- def response_status(action, payload)
269
- case action
270
- when 'process' then authorization_from(payload).present?
271
- else
272
- true
273
- end
175
+ def post_data(_action, post = {})
176
+ JSON.dump(post)
274
177
  end
275
178
 
276
- def authorization_from(payload)
277
- payload.fetch('id', nil)
278
- end
279
-
280
- def post_data(_action, parameters = {})
281
- { merchantId: options[:merchant_id] }.merge(parameters).to_json
179
+ def parse(body)
180
+ JSON.parse(body)
181
+ rescue StandardError
182
+ {}
282
183
  end
283
184
 
284
185
  def commit_action_url(action, _parameters)
285
186
  path = case action
286
- when 'webhook' then '/webhook/v3/config'
287
- when 'secret' then '/webhook/v3/secret'
288
- else
289
- "/pay/v3/#{action}"
290
- 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
291
192
  action_url(path)
292
193
  end
293
194
 
@@ -295,6 +196,24 @@ module ActiveMerchant
295
196
  "#{test? ? test_url : live_url}#{path}"
296
197
  end
297
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
+
298
217
  def build_avs_result(data)
299
218
  return if data.blank?
300
219
 
@@ -306,14 +225,6 @@ module ActiveMerchant
306
225
 
307
226
  CVVResult.new(data.fetch('gatewayMessage', {}).fetch('cvvresponse', nil))
308
227
  end
309
-
310
- def build_payload(params)
311
- { data: { customer: {} } }.merge!(params.fetch(:payload, {}))
312
- end
313
-
314
- def base_headers(custom = {})
315
- { Authorization: "Basic #{options[:auth_token]}" }
316
- end
317
228
  end
318
229
  end
319
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.6'
4
+ VERSION = '0.3.1'
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.6
4
+ version: 0.3.1
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