nexio_activemerchant 0.2.8 → 0.3.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: 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