nexio_activemerchant 0.2.5 → 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: ce3933d8c3bbc17e45b003dc16033a9f0d4802b1baac54b09b938a8bfaf09c85
4
- data.tar.gz: af7a7d9c97bf5e10daa9b4f873e62b72f031169332c4846c7e3200f90bc97775
3
+ metadata.gz: bdff69e02a5eb6fe1375f42e86dac022aa561861d4c2f8191d1a873c01c0883e
4
+ data.tar.gz: 29dc4a0b999a16d10187c652cb29aa994fe4842771af4fe15933dcf54feb8cc2
5
5
  SHA512:
6
- metadata.gz: fcfa1414b66c46fcbaf64f3e0d3bc95b3c6bd4097325370084c4696282f4d659b7146d1f43d9991c38f4090a6223594fe2ed1c2ca1c8c62c40a10fe48937cd54
7
- data.tar.gz: 12695b194b5180d20fe6961ee48f3cf0697823e3a2ea08360c16a5202075d8c24c8e4aa16caceb50f6c1f584b1d43476db640bc3cd1af78fc3c06dab69a04b40
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
@@ -172,74 +131,14 @@ module ActiveMerchant
172
131
  def add_address(post, data, prefix)
173
132
  return post if data.blank?
174
133
 
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
- }
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
134
  {
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
- }
216
- end
217
- end
218
-
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"
135
+ AddressOne: :address1, AddressTwo: :address2, City: :city,
136
+ Country: :country, Phone: :phone, Postal: :zip, State: :state
137
+ }.each do |suffix, key|
138
+ post[:data][:customer]["#{prefix}#{suffix}"] = data[key]
234
139
  end
235
140
  end
236
141
 
237
- def parse(body)
238
- JSON.parse(body)
239
- rescue StandardError
240
- {}
241
- end
242
-
243
142
  def commit(action, parameters)
244
143
  payload = parse(ssl_post(commit_action_url(action, parameters), post_data(action, parameters), base_headers))
245
144
 
@@ -265,20 +164,14 @@ module ActiveMerchant
265
164
  )
266
165
  end
267
166
 
268
- def response_status(action, payload)
269
- case action
270
- when 'process' then authorization_from(payload).present?
271
- else
272
- true
273
- end
274
- end
275
-
276
- def authorization_from(payload)
277
- payload.fetch('id', nil)
167
+ def post_data(_action, post = {})
168
+ JSON.dump(post)
278
169
  end
279
170
 
280
- def post_data(_action, parameters = {})
281
- { merchantId: options[:merchant_id] }.merge(parameters).to_json
171
+ def parse(body)
172
+ JSON.parse(body)
173
+ rescue StandardError
174
+ {}
282
175
  end
283
176
 
284
177
  def commit_action_url(action, _parameters)
@@ -286,7 +179,7 @@ module ActiveMerchant
286
179
  when 'webhook' then '/webhook/v3/config'
287
180
  when 'secret' then '/webhook/v3/secret'
288
181
  else
289
- "/pay/v3/#{action}"
182
+ "#{self.class.base_path}/#{action}"
290
183
  end
291
184
  action_url(path)
292
185
  end
@@ -295,6 +188,22 @@ module ActiveMerchant
295
188
  "#{test? ? test_url : live_url}#{path}"
296
189
  end
297
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
+
298
207
  def build_avs_result(data)
299
208
  return if data.blank?
300
209
 
@@ -306,14 +215,6 @@ module ActiveMerchant
306
215
 
307
216
  CVVResult.new(data.fetch('gatewayMessage', {}).fetch('cvvresponse', nil))
308
217
  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
218
  end
318
219
  end
319
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.5'
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.5
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-09-10 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