nexio_activemerchant 0.2.8 → 0.3.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: b4be2109f95bb02681d52e4eba82df056b917147789135cddd63a9c824305e20
4
- data.tar.gz: d12fb306b4071d6b6edace569875566da64a4aee4e639e8d8bca21ea80656f7f
3
+ metadata.gz: bdff69e02a5eb6fe1375f42e86dac022aa561861d4c2f8191d1a873c01c0883e
4
+ data.tar.gz: 29dc4a0b999a16d10187c652cb29aa994fe4842771af4fe15933dcf54feb8cc2
5
5
  SHA512:
6
- metadata.gz: bc116a66f2c7365cbcd784fd81219b33566356bd558746048d47a5edad75f15467530eb80ed8052b7d601e629400a2740b5321a4c77570620918bc9b0cbf6e47
7
- data.tar.gz: e96a8b271d7a9d95dc545e91cd03a09b7cf91f3e242840236ae5dd1bd54a8f3dc945de117b02fe5bf3f59fcf0bd4cad34dab683e8500d7ee54841c4ab3dd35db
6
+ metadata.gz: 02f2accd48380497c5f74a274d95ee4cbdff355d99867b561fafbdd6f26ca1ac2e9e2f9ff8432430dcd99554ce0fc791e439ca48ede54e9f2777b414a2dca9fa
7
+ data.tar.gz: 9ade7bb542af8389190b715b70c1e06512bbd06a828b697964aba05c136fa262e87dcabbec67210133962bcbba7a966064e181f2fadec1da041b9b8129f10ccb
@@ -0,0 +1,51 @@
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
+ resp = commit('token', post)
22
+ return unless resp.success?
23
+
24
+ OneTimeToken.new(
25
+ resp.params['token'],
26
+ resp.params['expressIFrameUrl'],
27
+ map_urls(resp.params['redirectUrls']),
28
+ map_urls(resp.params['buttonIFrameUrls'])
29
+ )
30
+ end
31
+
32
+ def purchase(money, payment, options = {})
33
+ post = build_payload(options)
34
+ add_invoice(post, money, options)
35
+ if payment.is_a?(Spree::PaymentSource)
36
+ post[:apm] = { token: payment.gateway_payment_profile_id }
37
+ else
38
+ post[:apm] = { token: payment }
39
+ end
40
+ add_order_data(post, options)
41
+ commit('process', post)
42
+ end
43
+
44
+ private
45
+
46
+ def map_urls(list)
47
+ list.each_with_object({}) { |item, acc| acc[item['paymentMethod']] = item['url'] }
48
+ end
49
+ end
50
+ end
51
+ end
@@ -4,56 +4,24 @@ require 'json'
4
4
 
5
5
  module ActiveMerchant
6
6
  module Billing
7
- class NexioGateway < Gateway
7
+ class NexioBaseGateway < Gateway
8
8
  self.test_url = 'https://api.nexiopaysandbox.com'
9
9
  self.live_url = 'https://api.nexiopay.com'
10
10
 
11
11
  self.supported_countries = %w[CA US]
12
12
  self.default_currency = 'USD'
13
13
  self.supported_cardtypes = %i[visa master american_express discover]
14
-
15
14
  self.homepage_url = 'https://nex.io'
16
- self.display_name = 'Nexio'
17
-
18
- STANDARD_ERROR_CODE_MAPPING = {}.freeze
15
+ self.abstract_class = true
19
16
 
20
- OneTimeToken = Struct.new(:token, :expiration, :fraud_url)
17
+ class_attribute :base_path
18
+ self.base_path = ''
21
19
 
22
20
  def initialize(options = {})
23
21
  requires!(options, :merchant_id, :auth_token)
24
22
  super
25
23
  end
26
24
 
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
25
  def capture(money, authorization, _options = {})
58
26
  commit('capture', { id: authorization, data: { amount: amount(money).to_f } })
59
27
  end
@@ -67,13 +35,6 @@ module ActiveMerchant
67
35
  commit('void', { id: authorization })
68
36
  end
69
37
 
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
38
  def supports_scrubbing?
78
39
  false
79
40
  end
@@ -82,28 +43,19 @@ module ActiveMerchant
82
43
  transcript
83
44
  end
84
45
 
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
46
  def set_webhooks(data)
97
47
  post = { merchantId: options[:merchant_id].to_s }
98
48
  if data.is_a?(String)
99
49
  post[:webhooks] = {
100
50
  TRANSACTION_AUTHORIZED: { url: data },
101
- TRANSACTION_CAPTURED: { url: data }
51
+ TRANSACTION_CAPTURED: { url: data },
52
+ TRANSACTION_SETTLED: { url: data }
102
53
  }
103
54
  else
104
55
  webhooks = {}
105
56
  webhooks[:TRANSACTION_AUTHORIZED] = { url: data[:authorized] } if data.key?(:authorized)
106
57
  webhooks[:TRANSACTION_CAPTURED] = { url: data[:captured] } if data.key?(:captured)
58
+ webhooks[:TRANSACTION_SETTLED] = { url: data[:settled] } if data.key?(:settled)
107
59
  post[:webhooks] = webhooks
108
60
  end
109
61
  commit('webhook', post)
@@ -113,13 +65,18 @@ module ActiveMerchant
113
65
  commit('secret', { merchantId: options[:merchant_id].to_s }).params['secret']
114
66
  end
115
67
 
116
- def get_transaction(id)
117
- parse(ssl_get(action_url("/transaction/v3/paymentId/#{id}"), base_headers))
118
- rescue ResponseError => e
119
- end
120
-
121
68
  private
122
69
 
70
+ def build_payload(params)
71
+ result = params.fetch(:payload, {}).deep_dup
72
+ result[:data] ||= {}
73
+ result[:data][:customer] ||= {}
74
+ result[:processingOptions] ||= {}
75
+ result[:processingOptions][:merchantId] ||= options[:merchant_id]
76
+ result[:processingOptions][:verboseResponse] = true if test?
77
+ result
78
+ end
79
+
123
80
  def add_invoice(post, money, options)
124
81
  post[:data][:amount] = amount(money).to_f
125
82
  add_currency(post, options)
@@ -134,11 +91,13 @@ module ActiveMerchant
134
91
  case customer
135
92
  when String
136
93
  post[:data][:customer][:email] = customer
94
+ post[:data][:customer][:customerRef] = customer
137
95
  when Hash
138
96
  post[:data][:customer].merge!({
139
97
  firstName: customer[:first_name],
140
98
  lastName: customer[:last_name],
141
- email: customer[:email]
99
+ email: customer[:email],
100
+ customerRef: customer[:email]
142
101
  })
143
102
  end
144
103
  end
@@ -176,67 +135,10 @@ module ActiveMerchant
176
135
  AddressOne: :address1, AddressTwo: :address2, City: :city,
177
136
  Country: :country, Phone: :phone, Postal: :zip, State: :state
178
137
  }.each do |suffix, key|
179
- post[:data][:customer]["#{prefix}#{suffix}"] = data[key].to_s
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"
138
+ post[:data][:customer]["#{prefix}#{suffix}"] = data[key]
231
139
  end
232
140
  end
233
141
 
234
- def parse(body)
235
- JSON.parse(body)
236
- rescue StandardError
237
- {}
238
- end
239
-
240
142
  def commit(action, parameters)
241
143
  payload = parse(ssl_post(commit_action_url(action, parameters), post_data(action, parameters), base_headers))
242
144
 
@@ -262,20 +164,14 @@ module ActiveMerchant
262
164
  )
263
165
  end
264
166
 
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)
167
+ def post_data(_action, post = {})
168
+ JSON.dump(post)
275
169
  end
276
170
 
277
- def post_data(_action, parameters = {})
278
- { merchantId: options[:merchant_id] }.merge(parameters).to_json
171
+ def parse(body)
172
+ JSON.parse(body)
173
+ rescue StandardError
174
+ {}
279
175
  end
280
176
 
281
177
  def commit_action_url(action, _parameters)
@@ -283,7 +179,7 @@ module ActiveMerchant
283
179
  when 'webhook' then '/webhook/v3/config'
284
180
  when 'secret' then '/webhook/v3/secret'
285
181
  else
286
- "/pay/v3/#{action}"
182
+ "#{self.class.base_path}/#{action}"
287
183
  end
288
184
  action_url(path)
289
185
  end
@@ -292,6 +188,22 @@ module ActiveMerchant
292
188
  "#{test? ? test_url : live_url}#{path}"
293
189
  end
294
190
 
191
+ def base_headers(custom = {})
192
+ { Authorization: "Basic #{options[:auth_token]}" }
193
+ end
194
+
195
+ def response_status(action, payload)
196
+ case action
197
+ when 'process' then %w(authOnlyPending authorizedPending pending authOnly settled).include?(payload['transactionStatus'])
198
+ else
199
+ true
200
+ end
201
+ end
202
+
203
+ def authorization_from(payload)
204
+ payload.fetch('id', nil)
205
+ end
206
+
295
207
  def build_avs_result(data)
296
208
  return if data.blank?
297
209
 
@@ -303,14 +215,6 @@ module ActiveMerchant
303
215
 
304
216
  CVVResult.new(data.fetch('gatewayMessage', {}).fetch('cvvresponse', nil))
305
217
  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
218
  end
315
219
  end
316
220
  end
@@ -0,0 +1,118 @@
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
+ def get_transaction(id)
59
+ parse(ssl_get(action_url("/transaction/v3/paymentId/#{id}"), base_headers))
60
+ rescue ResponseError => e
61
+ end
62
+
63
+ private
64
+
65
+ def add_payment(post, payment, options)
66
+ post[:tokenex] = token_from(payment)
67
+ if payment.is_a?(Spree::CreditCard)
68
+ post[:card] = {
69
+ cardHolderName: payment.name,
70
+ cardType: payment.brand
71
+ }.merge!(post.fetch(:card, {}))
72
+ end
73
+ post[:processingOptions][:saveCardToken] = options[:save_credit_card] if options.key?(:save_credit_card)
74
+ post[:processingOptions][:customerRedirectUrl] = options[:three_d_callback_url] if options[:three_d_callback_url].present?
75
+ post[:processingOptions][:check3ds] = options[:three_d_secure]
76
+ post[:processingOptions][:paymentType] = options[:payment_type] if options[:payment_type].present?
77
+ end
78
+
79
+ def token_from(payment)
80
+ return { token: payment } if payment.is_a?(String)
81
+
82
+ {
83
+ token: payment.gateway_payment_profile_id,
84
+ lastFour: payment.last_digits,
85
+ cardType: payment.brand
86
+ }
87
+ end
88
+
89
+ def add_card_data(post, options)
90
+ if card = options[:card]
91
+ post[:card] = {
92
+ cardHolderName: card[:name],
93
+ expirationMonth: card[:month],
94
+ expirationYear: card[:year]
95
+ }
96
+ end
97
+ end
98
+
99
+ def add_card_details(post, payment, _options)
100
+ if payment.is_a?(EncryptedNexioCard)
101
+ raise ArgumentError, 'The provided card is invalid' unless payment.valid?
102
+
103
+ post[:card] = {
104
+ cardHolderName: payment.name,
105
+ encryptedNumber: payment.encrypted_number,
106
+ expirationMonth: payment.month,
107
+ expirationYear: payment.short_year,
108
+ cardType: payment.brand,
109
+ securityCode: payment.verification_value
110
+ }
111
+ post[:token] = payment.one_time_token
112
+ else
113
+ raise ArgumentError, "Only #{EncryptedNexioCard} payment method is supported to store cards"
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NexioActivemerchant
4
- VERSION = '0.2.8'
4
+ VERSION = '0.3.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.8
4
+ version: 0.3.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-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemerchant
@@ -57,7 +57,9 @@ 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
62
64
  - lib/nexio_activemerchant/version.rb
63
65
  - nexio_activemerchant.gemspec