activemerchant 1.80.0 → 1.81.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 +4 -4
- data/CHANGELOG +17 -0
- data/lib/active_merchant/billing/gateways/adyen.rb +1 -0
- data/lib/active_merchant/billing/gateways/authorize_net.rb +4 -0
- data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +5 -3
- data/lib/active_merchant/billing/gateways/beanstream.rb +1 -0
- data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +1 -2
- data/lib/active_merchant/billing/gateways/ct_payment.rb +267 -0
- data/lib/active_merchant/billing/gateways/cyber_source.rb +7 -3
- data/lib/active_merchant/billing/gateways/first_pay.rb +3 -2
- data/lib/active_merchant/billing/gateways/firstdata_e4_v27.rb +444 -0
- data/lib/active_merchant/billing/gateways/global_collect.rb +9 -17
- data/lib/active_merchant/billing/gateways/mercury.rb +1 -1
- data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +6 -6
- data/lib/active_merchant/billing/gateways/pin.rb +1 -0
- data/lib/active_merchant/billing/gateways/safe_charge.rb +1 -1
- data/lib/active_merchant/billing/gateways/stripe.rb +1 -0
- data/lib/active_merchant/connection.rb +0 -13
- data/lib/active_merchant/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a3a23a8a15d84e0c61a12458a7198886b2ab517
|
4
|
+
data.tar.gz: 256e0471468a250883d3b57eb8af909c4ae20a94
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 95115d4f69d651b30e3129af8bb20b37af280d4de58c3b4a9fa29954c5bc605b4c0226c3555d08ec2c7f2b41b7cdae16081f634199adf22e5caa69605d733ceb
|
7
|
+
data.tar.gz: b711f96170b72145fa4ef79b79778c26c4396a0d51fdfb4af1aaeb7e05a56a7dd7c8a9bd8f03fe4ac3cf08a3a86944c9925a373206ff34a937526c2f4db305e8
|
data/CHANGELOG
CHANGED
@@ -1,6 +1,22 @@
|
|
1
1
|
= ActiveMerchant CHANGELOG
|
2
2
|
|
3
3
|
== HEAD
|
4
|
+
* GlobalCollect: Don't overwrite contactDetails [curiousepic] #2915
|
5
|
+
* Pin Payments: Pass reference for statement desc [curiousepic] #2919
|
6
|
+
* FirstData: introduce v27 gateway [shasum] #2912
|
7
|
+
* Stripe: Fix contactless magstripe support [abhiin1947] #2917
|
8
|
+
* ANET: Expose full response code [curiousepic] #2924
|
9
|
+
* Global Collect: Fix customer data field structure [curiousepic] #2929
|
10
|
+
* Adyen: Set Default Name for Apple Pay Transactions [nfarve] #2930
|
11
|
+
* Beanstream: Update to use api key with login credentials [nfarve] #2934
|
12
|
+
* CT Payments: Fix a typo in the live URL scheme [bpollack] #2936
|
13
|
+
* CyberSource: Don't throw exceptions on HTML responses [bpollack] #2937
|
14
|
+
* CyberSource: Remove extraneous parameter blocking echecks [chriscz] #2861
|
15
|
+
* FirstPay: Update Fields For Recurring Payments [nfarve] #2940
|
16
|
+
* Remove unused handle_response method [bl] #2309
|
17
|
+
* Barclaycard Smartpay: bump API version to v30 [bpollack] #2941
|
18
|
+
* Safecharge: Remove duplicate supported country [curiousepic]
|
19
|
+
* Payflow Express: Use SHIPTONAME instead of `full_name` for shipping address [filipebarcos] #2945
|
4
20
|
|
5
21
|
== Version 1.80.0 (July 4, 2018)
|
6
22
|
* Default SSL min_version to TLS 1.1 to comply with June 30 PCI DSS deadline [bdewater] #2909
|
@@ -34,6 +50,7 @@
|
|
34
50
|
* Redsys: Fix payments with cc token [Leonardo Diez] #2586
|
35
51
|
* Redsys: Missing cardnumber params in xml_signed_fields [nerburish] #2628
|
36
52
|
* Bogus: allow authorizing with a tokenized card [Azdaroth] #2703
|
53
|
+
* CT Payment: Add new gateway [nfarve] #2911
|
37
54
|
|
38
55
|
== Version 1.79.2 (June 2, 2018)
|
39
56
|
* Fix Gateway#max_version= overwriting min_version [bdewater]
|
@@ -179,6 +179,7 @@ module ActiveMerchant #:nodoc:
|
|
179
179
|
}
|
180
180
|
|
181
181
|
card.delete_if{|k,v| v.blank? }
|
182
|
+
card[:holderName] ||= 'Not Provided' if credit_card.is_a?(NetworkTokenizationCreditCard)
|
182
183
|
requires!(card, :expiryMonth, :expiryYear, :holderName, :number)
|
183
184
|
post[:card] = card
|
184
185
|
end
|
@@ -863,6 +863,10 @@ module ActiveMerchant
|
|
863
863
|
(empty?(element.content) ? nil : element.content)
|
864
864
|
end
|
865
865
|
|
866
|
+
response[:full_response_code] = if(element = doc.at_xpath('//messages/message/code'))
|
867
|
+
(empty?(element.content) ? nil : element.content)
|
868
|
+
end
|
869
|
+
|
866
870
|
response
|
867
871
|
end
|
868
872
|
|
@@ -13,6 +13,8 @@ module ActiveMerchant #:nodoc:
|
|
13
13
|
self.homepage_url = 'https://www.barclaycardsmartpay.com/'
|
14
14
|
self.display_name = 'Barclaycard Smartpay'
|
15
15
|
|
16
|
+
API_VERSION = 'v30'
|
17
|
+
|
16
18
|
def initialize(options = {})
|
17
19
|
requires!(options, :company, :merchant, :password)
|
18
20
|
super
|
@@ -222,11 +224,11 @@ module ActiveMerchant #:nodoc:
|
|
222
224
|
def build_url(action)
|
223
225
|
case action
|
224
226
|
when 'store'
|
225
|
-
"#{test? ? self.test_url : self.live_url}/Recurring/
|
227
|
+
"#{test? ? self.test_url : self.live_url}/Recurring/#{API_VERSION}/storeToken"
|
226
228
|
when 'finalize3ds'
|
227
|
-
"#{test? ? self.test_url : self.live_url}/Payment/
|
229
|
+
"#{test? ? self.test_url : self.live_url}/Payment/#{API_VERSION}/authorise3d"
|
228
230
|
else
|
229
|
-
"#{test? ? self.test_url : self.live_url}/Payment
|
231
|
+
"#{test? ? self.test_url : self.live_url}/Payment/#{API_VERSION}/#{action}"
|
230
232
|
end
|
231
233
|
end
|
232
234
|
|
@@ -199,6 +199,7 @@ module ActiveMerchant #:nodoc:
|
|
199
199
|
transcript.
|
200
200
|
gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
|
201
201
|
gsub(/(&?password=)[^&\s]*(&?)/, '\1[FILTERED]\2').
|
202
|
+
gsub(/(&?passcode=)[^&\s]*(&?)/, '\1[FILTERED]\2').
|
202
203
|
gsub(/(&?trnCardCvd=)\d*(&?)/, '\1[FILTERED]\2').
|
203
204
|
gsub(/(&?trnCardNumber=)\d*(&?)/, '\1[FILTERED]\2')
|
204
205
|
end
|
@@ -156,7 +156,6 @@ module ActiveMerchant #:nodoc:
|
|
156
156
|
|
157
157
|
def capture(money, authorization, options = {})
|
158
158
|
reference, _, _ = split_auth(authorization)
|
159
|
-
|
160
159
|
post = {}
|
161
160
|
add_amount(post, money)
|
162
161
|
add_reference(post, reference)
|
@@ -313,7 +312,6 @@ module ActiveMerchant #:nodoc:
|
|
313
312
|
post[:serviceVersion] = SP_SERVICE_VERSION
|
314
313
|
post[:responseFormat] = 'QS'
|
315
314
|
post[:cardValidation] = (options[:cardValidation].to_i == 1) || '0'
|
316
|
-
|
317
315
|
post[:operationType] = options[:operationType] || options[:operation] || secure_profile_action(:new)
|
318
316
|
post[:customerCode] = options[:billing_id] || options[:vault_id] || false
|
319
317
|
post[:status] = options[:status]
|
@@ -462,6 +460,7 @@ module ActiveMerchant #:nodoc:
|
|
462
460
|
params[:username] = @options[:user] if @options[:user]
|
463
461
|
params[:password] = @options[:password] if @options[:password]
|
464
462
|
params[:merchant_id] = @options[:login]
|
463
|
+
params[:passcode] = @options[:api_key]
|
465
464
|
end
|
466
465
|
params[:vbvEnabled] = '0'
|
467
466
|
params[:scEnabled] = '0'
|
@@ -0,0 +1,267 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
3
|
+
class CtPaymentGateway < Gateway
|
4
|
+
self.test_url = 'https://test.ctpaiement.ca/v1/'
|
5
|
+
self.live_url = 'https://www.ctpaiement.com/v1/'
|
6
|
+
|
7
|
+
self.supported_countries = ['US', 'CA']
|
8
|
+
self.default_currency = 'CAD'
|
9
|
+
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club]
|
10
|
+
|
11
|
+
self.homepage_url = 'http://www.ct-payment.com/'
|
12
|
+
self.display_name = 'CT Payment'
|
13
|
+
|
14
|
+
STANDARD_ERROR_CODE_MAPPING = {
|
15
|
+
'14' => STANDARD_ERROR_CODE[:invalid_number],
|
16
|
+
'05' => STANDARD_ERROR_CODE[:card_declined],
|
17
|
+
'M6' => STANDARD_ERROR_CODE[:card_declined],
|
18
|
+
'9068' => STANDARD_ERROR_CODE[:incorrect_number],
|
19
|
+
'9067' => STANDARD_ERROR_CODE[:incorrect_number]
|
20
|
+
}
|
21
|
+
CARD_BRAND = {
|
22
|
+
'american_express' => 'A',
|
23
|
+
'master' => 'M',
|
24
|
+
'diners_club' => 'I',
|
25
|
+
'visa' => 'V',
|
26
|
+
'discover' => 'O'
|
27
|
+
}
|
28
|
+
|
29
|
+
def initialize(options={})
|
30
|
+
requires!(options, :api_key, :company_number, :merchant_number)
|
31
|
+
super
|
32
|
+
end
|
33
|
+
|
34
|
+
def purchase(money, payment, options={})
|
35
|
+
requires!(options, :order_id)
|
36
|
+
post = {}
|
37
|
+
add_terminal_number(post, options)
|
38
|
+
add_money(post, money)
|
39
|
+
add_operator_id(post, options)
|
40
|
+
add_invoice(post, money, options)
|
41
|
+
add_payment(post, payment)
|
42
|
+
add_address(post, payment, options)
|
43
|
+
add_customer_data(post, options)
|
44
|
+
|
45
|
+
payment.is_a?(String) ? commit('purchaseWithToken', post) : commit('purchase', post)
|
46
|
+
end
|
47
|
+
|
48
|
+
def authorize(money, payment, options={})
|
49
|
+
requires!(options, :order_id)
|
50
|
+
post = {}
|
51
|
+
add_money(post, money)
|
52
|
+
add_terminal_number(post, options)
|
53
|
+
add_operator_id(post, options)
|
54
|
+
add_invoice(post, money, options)
|
55
|
+
add_payment(post, payment)
|
56
|
+
add_address(post, payment, options)
|
57
|
+
add_customer_data(post, options)
|
58
|
+
|
59
|
+
payment.is_a?(String) ? commit('preAuthorizationWithToken', post) : commit('preAuthorization', post)
|
60
|
+
end
|
61
|
+
|
62
|
+
def capture(money, authorization, options={})
|
63
|
+
requires!(options, :order_id)
|
64
|
+
post = {}
|
65
|
+
add_invoice(post, money, options)
|
66
|
+
add_money(post, money)
|
67
|
+
add_customer_data(post, options)
|
68
|
+
transaction_number, authorization_number, invoice_number = split_authorization(authorization)
|
69
|
+
post[:OriginalTransactionNumber] = transaction_number
|
70
|
+
post[:OriginalAuthorizationNumber] = authorization_number
|
71
|
+
post[:OriginalInvoiceNumber] = invoice_number
|
72
|
+
|
73
|
+
commit('completion', post)
|
74
|
+
end
|
75
|
+
|
76
|
+
def refund(money, authorization, options={})
|
77
|
+
requires!(options, :order_id)
|
78
|
+
post = {}
|
79
|
+
add_invoice(post, money, options)
|
80
|
+
add_money(post, money)
|
81
|
+
add_customer_data(post, options)
|
82
|
+
transaction_number, _, invoice_number = split_authorization(authorization)
|
83
|
+
post[:OriginalTransactionNumber] = transaction_number
|
84
|
+
post[:OriginalInvoiceNumber] = invoice_number
|
85
|
+
|
86
|
+
commit('refundWithoutCard', post)
|
87
|
+
end
|
88
|
+
|
89
|
+
def credit(money, payment, options={})
|
90
|
+
requires!(options, :order_id)
|
91
|
+
post = {}
|
92
|
+
add_terminal_number(post, options)
|
93
|
+
add_money(post, money)
|
94
|
+
add_operator_id(post, options)
|
95
|
+
add_invoice(post, money, options)
|
96
|
+
add_payment(post, payment)
|
97
|
+
add_address(post, payment, options)
|
98
|
+
add_customer_data(post, options)
|
99
|
+
|
100
|
+
payment.is_a?(String) ? commit('refundWithToken', post) : commit('refund', post)
|
101
|
+
end
|
102
|
+
|
103
|
+
def void(authorization, options={})
|
104
|
+
post = {}
|
105
|
+
post[:InputType] = 'I'
|
106
|
+
post[:LanguageCode] = 'E'
|
107
|
+
transaction_number, _, invoice_number = split_authorization(authorization)
|
108
|
+
post[:OriginalTransactionNumber] = transaction_number
|
109
|
+
post[:OriginalInvoiceNumber] = invoice_number
|
110
|
+
add_operator_id(post, options)
|
111
|
+
add_customer_data(post, options)
|
112
|
+
|
113
|
+
commit('void', post)
|
114
|
+
end
|
115
|
+
|
116
|
+
def verify(credit_card, options={})
|
117
|
+
requires!(options, :order_id)
|
118
|
+
post = {}
|
119
|
+
add_terminal_number(post, options)
|
120
|
+
add_operator_id(post, options)
|
121
|
+
add_invoice(post,0, options)
|
122
|
+
add_payment(post, credit_card)
|
123
|
+
add_customer_data(post, options)
|
124
|
+
|
125
|
+
commit('verifyAccount', post)
|
126
|
+
end
|
127
|
+
|
128
|
+
def store(credit_card, options={})
|
129
|
+
requires!(options, :email)
|
130
|
+
post = {
|
131
|
+
LanguageCode: 'E',
|
132
|
+
Name: credit_card.name.rjust(50, ' '),
|
133
|
+
Email: options[:email].rjust(240, ' ')
|
134
|
+
}
|
135
|
+
add_operator_id(post, options)
|
136
|
+
add_payment(post, credit_card)
|
137
|
+
add_customer_data(post, options)
|
138
|
+
|
139
|
+
commit('recur/AddUser', post)
|
140
|
+
end
|
141
|
+
|
142
|
+
def supports_scrubbing?
|
143
|
+
true
|
144
|
+
end
|
145
|
+
|
146
|
+
def scrub(transcript)
|
147
|
+
transcript.
|
148
|
+
gsub(%r((&?auth-api-key=)[^&]*)i, '\1[FILTERED]').
|
149
|
+
gsub(%r((&?payload=)[a-zA-Z%0-9=]+)i, '\1[FILTERED]').
|
150
|
+
gsub(%r((&?token:)[^&]*)i, '\1[FILTERED]').
|
151
|
+
gsub(%r((&?cardNumber:)[^&]*)i, '\1[FILTERED]')
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
def add_terminal_number(post, options)
|
157
|
+
post[:MerchantTerminalNumber] = options[:merchant_terminal_number] || ' ' * 5
|
158
|
+
end
|
159
|
+
|
160
|
+
def add_money(post, money)
|
161
|
+
post[:Amount] = money.to_s.rjust(11,'0')
|
162
|
+
end
|
163
|
+
|
164
|
+
def add_operator_id(post, options)
|
165
|
+
post[:OperatorID] = options[:operator_id] || '0' * 8
|
166
|
+
end
|
167
|
+
|
168
|
+
def add_customer_data(post, options)
|
169
|
+
post[:CustomerNumber] = options[:customer_number] || '0' * 8
|
170
|
+
end
|
171
|
+
|
172
|
+
def add_address(post, creditcard, options)
|
173
|
+
if address = options[:billing_address] || options[:address]
|
174
|
+
post[:CardHolderAddress] = ("#{address[:address1]} #{address[:address2]}").rjust(20, ' ')
|
175
|
+
post[:CardHolderPostalCode] = address[:zip].gsub(/\s+/, '').rjust(9, ' ')
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def add_invoice(post, money, options)
|
180
|
+
post[:CurrencyCode] = options[:currency] || (currency(money) if money)
|
181
|
+
post[:InvoiceNumber] = options[:order_id].rjust(12,'0')
|
182
|
+
post[:InputType] = 'I'
|
183
|
+
post[:LanguageCode] = 'E'
|
184
|
+
end
|
185
|
+
|
186
|
+
def add_payment(post, payment)
|
187
|
+
if payment.is_a?(String)
|
188
|
+
post[:Token] = split_authorization(payment)[3].strip
|
189
|
+
else
|
190
|
+
post[:CardType] = CARD_BRAND[payment.brand] || ' '
|
191
|
+
post[:CardNumber] = payment.number.rjust(40,' ')
|
192
|
+
post[:ExpirationDate] = expdate(payment)
|
193
|
+
post[:Cvv2Cvc2Number] = payment.verification_value
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def parse(body)
|
198
|
+
JSON.parse(body)
|
199
|
+
end
|
200
|
+
|
201
|
+
def split_authorization(authorization)
|
202
|
+
authorization.split(';')
|
203
|
+
end
|
204
|
+
|
205
|
+
def commit_raw(action, parameters)
|
206
|
+
url = (test? ? test_url : live_url) + action
|
207
|
+
response = parse(ssl_post(url, post_data(action, parameters)))
|
208
|
+
|
209
|
+
final_response = Response.new(
|
210
|
+
success_from(response),
|
211
|
+
message_from(response),
|
212
|
+
response,
|
213
|
+
authorization: authorization_from(response),
|
214
|
+
avs_result: AVSResult.new(code: response['avsStatus']),
|
215
|
+
cvv_result: CVVResult.new(response['cvv2Cvc2Status']),
|
216
|
+
test: test?,
|
217
|
+
error_code: error_code_from(response)
|
218
|
+
)
|
219
|
+
end
|
220
|
+
|
221
|
+
def commit(action, parameters)
|
222
|
+
if action == 'void'
|
223
|
+
commit_raw(action, parameters)
|
224
|
+
else
|
225
|
+
MultiResponse.run(true) do |r|
|
226
|
+
r.process { commit_raw(action, parameters)}
|
227
|
+
r.process {
|
228
|
+
split_auth = split_authorization(r.authorization)
|
229
|
+
auth = (action.include?('recur')? split_auth[4] : split_auth[0])
|
230
|
+
action.include?('recur') ? commit_raw('recur/ack', {ID: auth}) : commit_raw('ack', {TransactionNumber: auth})
|
231
|
+
}
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def success_from(response)
|
237
|
+
return true if response['returnCode'] == ' 00'
|
238
|
+
return true if response['returnCode'] == 'true'
|
239
|
+
return true if response['recurReturnCode'] == ' 00'
|
240
|
+
return false
|
241
|
+
end
|
242
|
+
|
243
|
+
def message_from(response)
|
244
|
+
response['errorDescription'] || (response['terminalDisp'].strip if response['terminalDisp'])
|
245
|
+
end
|
246
|
+
|
247
|
+
def authorization_from(response)
|
248
|
+
"#{response['transactionNumber']};#{response['authorizationNumber']};"\
|
249
|
+
"#{response['invoiceNumber']};#{response['token']};#{response['id']}"
|
250
|
+
end
|
251
|
+
|
252
|
+
def post_data(action, parameters = {})
|
253
|
+
parameters[:CompanyNumber] = @options[:company_number]
|
254
|
+
parameters[:MerchantNumber] = @options[:merchant_number]
|
255
|
+
parameters = parameters.collect do |key, value|
|
256
|
+
"#{key}=#{value}" unless (value.nil? || value.empty?)
|
257
|
+
end.join('&')
|
258
|
+
payload = Base64.strict_encode64(parameters)
|
259
|
+
"auth-api-key=#{@options[:api_key]}&payload=#{payload}".strip
|
260
|
+
end
|
261
|
+
|
262
|
+
def error_code_from(response)
|
263
|
+
STANDARD_ERROR_CODE_MAPPING[response['returnCode'].strip || response['recurReturnCode'.strip]] unless success_from(response)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
@@ -356,7 +356,7 @@ module ActiveMerchant #:nodoc:
|
|
356
356
|
add_subscription(xml, options)
|
357
357
|
if options[:setup_fee]
|
358
358
|
if card_brand(payment_method) == 'check'
|
359
|
-
add_check_service(xml
|
359
|
+
add_check_service(xml)
|
360
360
|
else
|
361
361
|
add_purchase_service(xml, payment_method, options)
|
362
362
|
add_payment_network_token(xml) if network_tokenization?(payment_method)
|
@@ -708,9 +708,13 @@ module ActiveMerchant #:nodoc:
|
|
708
708
|
# Response object
|
709
709
|
def commit(request, action, amount, options)
|
710
710
|
begin
|
711
|
-
|
711
|
+
raw_response = ssl_post(test? ? self.test_url : self.live_url, build_request(request, options))
|
712
712
|
rescue ResponseError => e
|
713
|
-
|
713
|
+
raw_response = e.response.body
|
714
|
+
end
|
715
|
+
|
716
|
+
begin
|
717
|
+
response = parse(raw_response)
|
714
718
|
rescue REXML::ParseException => e
|
715
719
|
response = { message: e.to_s }
|
716
720
|
end
|
@@ -93,8 +93,9 @@ module ActiveMerchant #:nodoc:
|
|
93
93
|
post[:card_exp] = expdate(payment)
|
94
94
|
post[:cvv2] = payment.verification_value
|
95
95
|
post[:recurring] = options[:recurring] if options[:recurring]
|
96
|
-
post[:
|
97
|
-
post[:
|
96
|
+
post[:recurring_start_date] = options[:recurring_start_date] if options[:recurring_start_date]
|
97
|
+
post[:recurring_end_date] = options[:recurring_end_date] if options[:recurring_end_date]
|
98
|
+
post[:recurring_type] = options[:recurring_type] if options[:recurring_type]
|
98
99
|
end
|
99
100
|
|
100
101
|
def add_reference(post, action, money, authorization)
|
@@ -0,0 +1,444 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
3
|
+
class FirstdataE4V27Gateway < Gateway
|
4
|
+
self.test_url = 'https://api.demo.globalgatewaye4.firstdata.com/transaction/v27'
|
5
|
+
self.live_url = 'https://api.globalgatewaye4.firstdata.com/transaction/v27'
|
6
|
+
|
7
|
+
TRANSACTIONS = {
|
8
|
+
sale: '00',
|
9
|
+
authorization: '01',
|
10
|
+
verify: '05',
|
11
|
+
capture: '32',
|
12
|
+
void: '33',
|
13
|
+
credit: '34',
|
14
|
+
store: '05'
|
15
|
+
}
|
16
|
+
|
17
|
+
SUCCESS = 'true'
|
18
|
+
|
19
|
+
SENSITIVE_FIELDS = [:cvdcode, :expiry_date, :card_number]
|
20
|
+
|
21
|
+
BRANDS = {
|
22
|
+
:visa => 'Visa',
|
23
|
+
:master => 'Mastercard',
|
24
|
+
:american_express => 'American Express',
|
25
|
+
:jcb => 'JCB',
|
26
|
+
:discover => 'Discover'
|
27
|
+
}
|
28
|
+
|
29
|
+
DEFAULT_ECI = '07'
|
30
|
+
|
31
|
+
self.supported_cardtypes = BRANDS.keys
|
32
|
+
self.supported_countries = ['CA', 'US']
|
33
|
+
self.default_currency = 'USD'
|
34
|
+
self.homepage_url = 'http://www.firstdata.com'
|
35
|
+
self.display_name = 'FirstData Global Gateway e4 v27'
|
36
|
+
|
37
|
+
STANDARD_ERROR_CODE_MAPPING = {
|
38
|
+
# Bank error codes: https://support.payeezy.com/hc/en-us/articles/203730509-First-Data-Global-Gateway-e4-Bank-Response-Codes
|
39
|
+
'201' => STANDARD_ERROR_CODE[:incorrect_number],
|
40
|
+
'531' => STANDARD_ERROR_CODE[:invalid_cvc],
|
41
|
+
'503' => STANDARD_ERROR_CODE[:invalid_cvc],
|
42
|
+
'811' => STANDARD_ERROR_CODE[:invalid_cvc],
|
43
|
+
'605' => STANDARD_ERROR_CODE[:invalid_expiry_date],
|
44
|
+
'522' => STANDARD_ERROR_CODE[:expired_card],
|
45
|
+
'303' => STANDARD_ERROR_CODE[:card_declined],
|
46
|
+
'530' => STANDARD_ERROR_CODE[:card_declined],
|
47
|
+
'401' => STANDARD_ERROR_CODE[:call_issuer],
|
48
|
+
'402' => STANDARD_ERROR_CODE[:call_issuer],
|
49
|
+
'501' => STANDARD_ERROR_CODE[:pickup_card],
|
50
|
+
# Ecommerce error codes: https://support.payeezy.com/hc/en-us/articles/203730499-eCommerce-Response-Codes-ETG-e4-Transaction-Gateway-Codes
|
51
|
+
'22' => STANDARD_ERROR_CODE[:invalid_number],
|
52
|
+
'25' => STANDARD_ERROR_CODE[:invalid_expiry_date],
|
53
|
+
'31' => STANDARD_ERROR_CODE[:incorrect_cvc],
|
54
|
+
'44' => STANDARD_ERROR_CODE[:incorrect_zip],
|
55
|
+
'42' => STANDARD_ERROR_CODE[:processing_error]
|
56
|
+
}
|
57
|
+
|
58
|
+
def initialize(options = {})
|
59
|
+
requires!(options, :login, :password, :key_id, :hmac_key)
|
60
|
+
@options = options
|
61
|
+
|
62
|
+
super
|
63
|
+
end
|
64
|
+
|
65
|
+
def authorize(money, credit_card_or_store_authorization, options = {})
|
66
|
+
commit(:authorization, build_sale_or_authorization_request(money, credit_card_or_store_authorization, options))
|
67
|
+
end
|
68
|
+
|
69
|
+
def purchase(money, credit_card_or_store_authorization, options = {})
|
70
|
+
commit(:sale, build_sale_or_authorization_request(money, credit_card_or_store_authorization, options))
|
71
|
+
end
|
72
|
+
|
73
|
+
def capture(money, authorization, options = {})
|
74
|
+
commit(:capture, build_capture_or_credit_request(money, authorization, options))
|
75
|
+
end
|
76
|
+
|
77
|
+
def void(authorization, options = {})
|
78
|
+
commit(:void, build_capture_or_credit_request(money_from_authorization(authorization), authorization, options))
|
79
|
+
end
|
80
|
+
|
81
|
+
def refund(money, authorization, options = {})
|
82
|
+
commit(:credit, build_capture_or_credit_request(money, authorization, options))
|
83
|
+
end
|
84
|
+
|
85
|
+
def verify(credit_card, options = {})
|
86
|
+
commit(:verify, build_sale_or_authorization_request(0, credit_card, options))
|
87
|
+
end
|
88
|
+
|
89
|
+
# Tokenize a credit card with TransArmor
|
90
|
+
#
|
91
|
+
# The TransArmor token and other card data necessary for subsequent
|
92
|
+
# transactions is stored in the response's +authorization+ attribute.
|
93
|
+
# The authorization string may be passed to +authorize+ and +purchase+
|
94
|
+
# instead of a +ActiveMerchant::Billing::CreditCard+ instance.
|
95
|
+
#
|
96
|
+
# TransArmor support must be explicitly activated on your gateway
|
97
|
+
# account by FirstData. If your authorization string is empty, contact
|
98
|
+
# FirstData support for account setup assistance.
|
99
|
+
#
|
100
|
+
# https://support.payeezy.com/hc/en-us/articles/203731189-TransArmor-Tokenization
|
101
|
+
def store(credit_card, options = {})
|
102
|
+
commit(:store, build_store_request(credit_card, options), credit_card)
|
103
|
+
end
|
104
|
+
|
105
|
+
def verify_credentials
|
106
|
+
response = void('0')
|
107
|
+
response.message != 'Unauthorized Request. Bad or missing credentials.'
|
108
|
+
end
|
109
|
+
|
110
|
+
def supports_scrubbing?
|
111
|
+
true
|
112
|
+
end
|
113
|
+
|
114
|
+
def scrub(transcript)
|
115
|
+
transcript
|
116
|
+
.gsub(%r((<Card_Number>).+(</Card_Number>)), '\1[FILTERED]\2')
|
117
|
+
.gsub(%r((<CVDCode>).+(</CVDCode>)), '\1[FILTERED]\2')
|
118
|
+
.gsub(%r((<Password>).+(</Password>))i, '\1[FILTERED]\2')
|
119
|
+
.gsub(%r((<CAVV>).+(</CAVV>)), '\1[FILTERED]\2')
|
120
|
+
.gsub(%r((CARD NUMBER\s+: )#+\d+), '\1[FILTERED]')
|
121
|
+
end
|
122
|
+
|
123
|
+
def supports_network_tokenization?
|
124
|
+
true
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def build_request(action, body)
|
130
|
+
xml = Builder::XmlMarkup.new
|
131
|
+
|
132
|
+
xml.instruct!
|
133
|
+
xml.tag! 'Transaction', xmlns: 'http://secure2.e-xact.com/vplug-in/transaction/rpc-enc/encodedTypes' do
|
134
|
+
add_credentials(xml)
|
135
|
+
add_transaction_type(xml, action)
|
136
|
+
xml << body
|
137
|
+
end
|
138
|
+
|
139
|
+
xml.target!
|
140
|
+
end
|
141
|
+
|
142
|
+
def build_sale_or_authorization_request(money, credit_card_or_store_authorization, options)
|
143
|
+
xml = Builder::XmlMarkup.new
|
144
|
+
|
145
|
+
add_amount(xml, money, options)
|
146
|
+
|
147
|
+
if credit_card_or_store_authorization.is_a? String
|
148
|
+
add_credit_card_token(xml, credit_card_or_store_authorization, options)
|
149
|
+
else
|
150
|
+
add_credit_card(xml, credit_card_or_store_authorization, options)
|
151
|
+
end
|
152
|
+
|
153
|
+
add_address(xml, options)
|
154
|
+
add_customer_data(xml, options)
|
155
|
+
add_invoice(xml, options)
|
156
|
+
add_tax_fields(xml, options)
|
157
|
+
add_level_3(xml, options)
|
158
|
+
|
159
|
+
xml.target!
|
160
|
+
end
|
161
|
+
|
162
|
+
def build_capture_or_credit_request(money, identification, options)
|
163
|
+
xml = Builder::XmlMarkup.new
|
164
|
+
|
165
|
+
add_identification(xml, identification)
|
166
|
+
add_amount(xml, money, options)
|
167
|
+
add_customer_data(xml, options)
|
168
|
+
add_card_authentication_data(xml, options)
|
169
|
+
|
170
|
+
xml.target!
|
171
|
+
end
|
172
|
+
|
173
|
+
def build_store_request(credit_card, options)
|
174
|
+
xml = Builder::XmlMarkup.new
|
175
|
+
|
176
|
+
add_credit_card(xml, credit_card, options)
|
177
|
+
add_address(xml, options)
|
178
|
+
add_customer_data(xml, options)
|
179
|
+
|
180
|
+
xml.target!
|
181
|
+
end
|
182
|
+
|
183
|
+
def add_credentials(xml)
|
184
|
+
xml.tag! 'ExactID', @options[:login]
|
185
|
+
xml.tag! 'Password', @options[:password]
|
186
|
+
end
|
187
|
+
|
188
|
+
def add_transaction_type(xml, action)
|
189
|
+
xml.tag! 'Transaction_Type', TRANSACTIONS[action]
|
190
|
+
end
|
191
|
+
|
192
|
+
def add_identification(xml, identification)
|
193
|
+
authorization_num, transaction_tag, _ = identification.split(';')
|
194
|
+
|
195
|
+
xml.tag! 'Authorization_Num', authorization_num
|
196
|
+
xml.tag! 'Transaction_Tag', transaction_tag
|
197
|
+
end
|
198
|
+
|
199
|
+
def add_amount(xml, money, options)
|
200
|
+
currency_code = options[:currency] || default_currency
|
201
|
+
xml.tag! 'DollarAmount', localized_amount(money, currency_code)
|
202
|
+
xml.tag! 'Currency', currency_code
|
203
|
+
end
|
204
|
+
|
205
|
+
def add_credit_card(xml, credit_card, options)
|
206
|
+
if credit_card.respond_to?(:track_data) && credit_card.track_data.present?
|
207
|
+
xml.tag! 'Track1', credit_card.track_data
|
208
|
+
xml.tag! 'Ecommerce_Flag', 'R'
|
209
|
+
else
|
210
|
+
xml.tag! 'Card_Number', credit_card.number
|
211
|
+
xml.tag! 'Expiry_Date', expdate(credit_card)
|
212
|
+
xml.tag! 'CardHoldersName', credit_card.name
|
213
|
+
xml.tag! 'CardType', card_type(credit_card.brand)
|
214
|
+
|
215
|
+
add_credit_card_eci(xml, credit_card, options)
|
216
|
+
add_credit_card_verification_strings(xml, credit_card, options)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def add_credit_card_eci(xml, credit_card, options)
|
221
|
+
eci = if credit_card.is_a?(NetworkTokenizationCreditCard) && credit_card.source == :apple_pay && card_brand(credit_card) == 'discover'
|
222
|
+
# Discover requires any Apple Pay transaction, regardless of in-app
|
223
|
+
# or web, and regardless of the ECI contained in the PKPaymentToken,
|
224
|
+
# to have an ECI value explicitly of 04.
|
225
|
+
'04'
|
226
|
+
else
|
227
|
+
(credit_card.respond_to?(:eci) ? credit_card.eci : nil) || options[:eci] || DEFAULT_ECI
|
228
|
+
end
|
229
|
+
|
230
|
+
xml.tag! 'Ecommerce_Flag', eci.to_s =~ /^[0-9]+$/ ? eci.to_s.rjust(2, '0') : eci
|
231
|
+
end
|
232
|
+
|
233
|
+
def add_credit_card_verification_strings(xml, credit_card, options)
|
234
|
+
if credit_card.is_a?(NetworkTokenizationCreditCard)
|
235
|
+
add_network_tokenization_credit_card(xml, credit_card)
|
236
|
+
else
|
237
|
+
if credit_card.verification_value?
|
238
|
+
xml.tag! 'CVD_Presence_Ind', '1'
|
239
|
+
xml.tag! 'CVDCode', credit_card.verification_value
|
240
|
+
end
|
241
|
+
|
242
|
+
add_card_authentication_data(xml, options)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def add_network_tokenization_credit_card(xml, credit_card)
|
247
|
+
case card_brand(credit_card).to_sym
|
248
|
+
when :american_express
|
249
|
+
cryptogram = Base64.decode64(credit_card.payment_cryptogram)
|
250
|
+
xml.tag!('XID', Base64.encode64(cryptogram[20...40]))
|
251
|
+
xml.tag!('CAVV', Base64.encode64(cryptogram[0...20]))
|
252
|
+
else
|
253
|
+
xml.tag!('XID', credit_card.transaction_id) if credit_card.transaction_id
|
254
|
+
xml.tag!('CAVV', credit_card.payment_cryptogram)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def add_card_authentication_data(xml, options)
|
259
|
+
xml.tag! 'CAVV', options[:cavv]
|
260
|
+
xml.tag! 'XID', options[:xid]
|
261
|
+
end
|
262
|
+
|
263
|
+
def add_credit_card_token(xml, store_authorization, options)
|
264
|
+
params = store_authorization.split(';')
|
265
|
+
credit_card = CreditCard.new(
|
266
|
+
:brand => params[1],
|
267
|
+
:first_name => params[2],
|
268
|
+
:last_name => params[3],
|
269
|
+
:month => params[4],
|
270
|
+
:year => params[5])
|
271
|
+
|
272
|
+
xml.tag! 'TransarmorToken', params[0]
|
273
|
+
xml.tag! 'Expiry_Date', expdate(credit_card)
|
274
|
+
xml.tag! 'CardHoldersName', credit_card.name
|
275
|
+
xml.tag! 'CardType', card_type(credit_card.brand)
|
276
|
+
add_card_authentication_data(xml, options)
|
277
|
+
end
|
278
|
+
|
279
|
+
def add_customer_data(xml, options)
|
280
|
+
xml.tag! 'Customer_Ref', options[:customer] if options[:customer]
|
281
|
+
xml.tag! 'Client_IP', options[:ip] if options[:ip]
|
282
|
+
xml.tag! 'Client_Email', options[:email] if options[:email]
|
283
|
+
end
|
284
|
+
|
285
|
+
def add_address(xml, options)
|
286
|
+
if (address = options[:billing_address] || options[:address])
|
287
|
+
xml.tag! 'Address' do
|
288
|
+
xml.tag! 'Address1', address[:address1]
|
289
|
+
xml.tag! 'Address2', address[:address2] if address[:address2]
|
290
|
+
xml.tag! 'City', address[:city]
|
291
|
+
xml.tag! 'State', address[:state]
|
292
|
+
xml.tag! 'Zip', address[:zip]
|
293
|
+
xml.tag! 'CountryCode', address[:country]
|
294
|
+
end
|
295
|
+
xml.tag! 'ZipCode', address[:zip]
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
def add_invoice(xml, options)
|
300
|
+
xml.tag! 'Reference_No', options[:order_id]
|
301
|
+
xml.tag! 'Reference_3', options[:description] if options[:description]
|
302
|
+
end
|
303
|
+
|
304
|
+
def add_tax_fields(xml, options)
|
305
|
+
xml.tag! 'Tax1Amount', options[:tax1_amount] if options[:tax1_amount]
|
306
|
+
xml.tag! 'Tax1Number', options[:tax1_number] if options[:tax1_number]
|
307
|
+
end
|
308
|
+
|
309
|
+
def add_level_3(xml, options)
|
310
|
+
xml.tag!('Level3') { |x| x << options[:level_3] } if options[:level_3]
|
311
|
+
end
|
312
|
+
|
313
|
+
def expdate(credit_card)
|
314
|
+
"#{format(credit_card.month, :two_digits)}#{format(credit_card.year, :two_digits)}"
|
315
|
+
end
|
316
|
+
|
317
|
+
def card_type(credit_card_brand)
|
318
|
+
BRANDS[credit_card_brand.to_sym] if credit_card_brand
|
319
|
+
end
|
320
|
+
|
321
|
+
def commit(action, data, credit_card = nil)
|
322
|
+
url = (test? ? self.test_url : self.live_url)
|
323
|
+
request = build_request(action, data)
|
324
|
+
begin
|
325
|
+
response = parse(ssl_post(url, request, headers('POST', url, request)))
|
326
|
+
rescue ResponseError => e
|
327
|
+
response = parse_error(e.response)
|
328
|
+
end
|
329
|
+
|
330
|
+
Response.new(successful?(response), message_from(response), response,
|
331
|
+
:test => test?,
|
332
|
+
:authorization => successful?(response) ? response_authorization(action, response, credit_card) : '',
|
333
|
+
:avs_result => {:code => response[:avs]},
|
334
|
+
:cvv_result => response[:cvv2],
|
335
|
+
:error_code => standard_error_code(response)
|
336
|
+
)
|
337
|
+
end
|
338
|
+
|
339
|
+
def headers(method, url, request)
|
340
|
+
content_type = 'application/xml'
|
341
|
+
content_digest = Digest::SHA1.hexdigest(request)
|
342
|
+
sending_time = Time.now.utc.iso8601
|
343
|
+
payload = [method, content_type, content_digest, sending_time, url.split('.com')[1]].join("\n")
|
344
|
+
hmac = OpenSSL::HMAC.digest('sha1', @options[:hmac_key], payload)
|
345
|
+
encoded = Base64.strict_encode64(hmac)
|
346
|
+
|
347
|
+
{
|
348
|
+
'x-gge4-date' => sending_time,
|
349
|
+
'x-gge4-content-sha1' => content_digest,
|
350
|
+
'Authorization' => 'GGE4_API ' + @options[:key_id].to_s + ':' + encoded,
|
351
|
+
'Accepts' => content_type,
|
352
|
+
'Content-Type' => content_type
|
353
|
+
}
|
354
|
+
end
|
355
|
+
|
356
|
+
def successful?(response)
|
357
|
+
response[:transaction_approved] == SUCCESS
|
358
|
+
end
|
359
|
+
|
360
|
+
def response_authorization(action, response, credit_card)
|
361
|
+
if action == :store
|
362
|
+
store_authorization_from(response, credit_card)
|
363
|
+
else
|
364
|
+
authorization_from(response)
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
def authorization_from(response)
|
369
|
+
if response[:authorization_num] && response[:transaction_tag]
|
370
|
+
[
|
371
|
+
response[:authorization_num],
|
372
|
+
response[:transaction_tag],
|
373
|
+
(response[:dollar_amount].to_f * 100).round
|
374
|
+
].join(';')
|
375
|
+
else
|
376
|
+
''
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
def store_authorization_from(response, credit_card)
|
381
|
+
if response[:transarmor_token].present?
|
382
|
+
[
|
383
|
+
response[:transarmor_token],
|
384
|
+
credit_card.brand,
|
385
|
+
credit_card.first_name,
|
386
|
+
credit_card.last_name,
|
387
|
+
credit_card.month,
|
388
|
+
credit_card.year
|
389
|
+
].map { |value| value.to_s.tr(';', '') }.join(';')
|
390
|
+
else
|
391
|
+
raise StandardError, "TransArmor support is not enabled on your #{display_name} account"
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
def money_from_authorization(auth)
|
396
|
+
_, _, amount = auth.split(/;/, 3)
|
397
|
+
amount.to_i
|
398
|
+
end
|
399
|
+
|
400
|
+
def message_from(response)
|
401
|
+
if(response[:faultcode] && response[:faultstring])
|
402
|
+
response[:faultstring]
|
403
|
+
elsif(response[:error_number] && response[:error_number] != '0')
|
404
|
+
response[:error_description]
|
405
|
+
else
|
406
|
+
result = (response[:exact_message] || '')
|
407
|
+
result << " - #{response[:bank_message]}" if response[:bank_message].present?
|
408
|
+
result
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
def parse_error(error)
|
413
|
+
{
|
414
|
+
:transaction_approved => 'false',
|
415
|
+
:error_number => error.code,
|
416
|
+
:error_description => error.body,
|
417
|
+
:ecommerce_error_code => error.body.gsub(/[^\d]/, '')
|
418
|
+
}
|
419
|
+
end
|
420
|
+
|
421
|
+
def standard_error_code(response)
|
422
|
+
STANDARD_ERROR_CODE_MAPPING[response[:bank_resp_code] || response[:ecommerce_error_code]]
|
423
|
+
end
|
424
|
+
|
425
|
+
def parse(xml)
|
426
|
+
response = {}
|
427
|
+
xml = REXML::Document.new(xml)
|
428
|
+
|
429
|
+
if (root = REXML::XPath.first(xml, '//TransactionResult'))
|
430
|
+
parse_elements(response, root)
|
431
|
+
end
|
432
|
+
|
433
|
+
SENSITIVE_FIELDS.each { |key| response.delete(key) }
|
434
|
+
response
|
435
|
+
end
|
436
|
+
|
437
|
+
def parse_elements(response, root)
|
438
|
+
root.elements.to_a.each do |node|
|
439
|
+
response[node.name.gsub(/EXact/, 'Exact').underscore.to_sym] = (node.text || '').strip
|
440
|
+
end
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
end
|
@@ -4,7 +4,7 @@ module ActiveMerchant #:nodoc:
|
|
4
4
|
self.display_name = 'GlobalCollect'
|
5
5
|
self.homepage_url = 'http://www.globalcollect.com/'
|
6
6
|
|
7
|
-
self.test_url = 'https://api-
|
7
|
+
self.test_url = 'https://eu.sandbox.api-ingenico.com/'
|
8
8
|
self.live_url = 'https://api.globalcollect.com/'
|
9
9
|
|
10
10
|
self.supported_countries = ['AD', 'AE', 'AG', 'AI', 'AL', 'AM', 'AO', 'AR', 'AS', 'AT', 'AU', 'AW', 'AX', 'AZ', 'BA', 'BB', 'BD', 'BE', 'BF', 'BG', 'BH', 'BI', 'BJ', 'BL', 'BM', 'BN', 'BO', 'BQ', 'BR', 'BS', 'BT', 'BW', 'BY', 'BZ', 'CA', 'CC', 'CD', 'CF', 'CH', 'CI', 'CK', 'CL', 'CM', 'CN', 'CO', 'CR', 'CU', 'CV', 'CW', 'CX', 'CY', 'CZ', 'DE', 'DJ', 'DK', 'DM', 'DO', 'DZ', 'EC', 'EE', 'EG', 'ER', 'ES', 'ET', 'FI', 'FJ', 'FK', 'FM', 'FO', 'FR', 'GA', 'GB', 'GD', 'GE', 'GF', 'GH', 'GI', 'GL', 'GM', 'GN', 'GP', 'GQ', 'GR', 'GS', 'GT', 'GU', 'GW', 'GY', 'HK', 'HN', 'HR', 'HT', 'HU', 'ID', 'IE', 'IL', 'IM', 'IN', 'IS', 'IT', 'JM', 'JO', 'JP', 'KE', 'KG', 'KH', 'KI', 'KM', 'KN', 'KR', 'KW', 'KY', 'KZ', 'LA', 'LB', 'LC', 'LI', 'LK', 'LR', 'LS', 'LT', 'LU', 'LV', 'MA', 'MC', 'MD', 'ME', 'MF', 'MG', 'MH', 'MK', 'MM', 'MN', 'MO', 'MP', 'MQ', 'MR', 'MS', 'MT', 'MU', 'MV', 'MW', 'MX', 'MY', 'MZ', 'NA', 'NC', 'NE', 'NG', 'NI', 'NL', 'NO', 'NP', 'NR', 'NU', 'NZ', 'OM', 'PA', 'PE', 'PF', 'PG', 'PH', 'PL', 'PN', 'PS', 'PT', 'PW', 'QA', 'RE', 'RO', 'RS', 'RU', 'RW', 'SA', 'SB', 'SC', 'SE', 'SG', 'SH', 'SI', 'SJ', 'SK', 'SL', 'SM', 'SN', 'SR', 'ST', 'SV', 'SZ', 'TC', 'TD', 'TG', 'TH', 'TJ', 'TL', 'TM', 'TN', 'TO', 'TR', 'TT', 'TV', 'TW', 'TZ', 'UA', 'UG', 'US', 'UY', 'UZ', 'VC', 'VE', 'VG', 'VI', 'VN', 'WF', 'WS', 'ZA', 'ZM', 'ZW']
|
@@ -140,9 +140,6 @@ module ActiveMerchant #:nodoc:
|
|
140
140
|
end
|
141
141
|
|
142
142
|
def add_customer_data(post, options, payment = nil)
|
143
|
-
post['order']['customer'] = {
|
144
|
-
'merchantCustomerId' => options[:customer]
|
145
|
-
}
|
146
143
|
if payment
|
147
144
|
post['order']['customer']['personalInformation'] = {
|
148
145
|
'name' => {
|
@@ -151,16 +148,11 @@ module ActiveMerchant #:nodoc:
|
|
151
148
|
}
|
152
149
|
}
|
153
150
|
end
|
154
|
-
post['order']['
|
155
|
-
|
156
|
-
|
157
|
-
post['order']['contactDetails'] = {
|
158
|
-
'emailAddress' => options[:email]
|
159
|
-
}
|
151
|
+
post['order']['customer']['merchantCustomerId'] = options[:customer] if options[:customer]
|
152
|
+
post['order']['customer']['companyInformation']['name'] = options[:company] if options[:company]
|
153
|
+
post['order']['customer']['contactDetails']['emailAddress'] = options[:email] if options[:email]
|
160
154
|
if address = options[:billing_address] || options[:address]
|
161
|
-
post['order']['contactDetails'] =
|
162
|
-
'phoneNumber' => address[:phone]
|
163
|
-
}
|
155
|
+
post['order']['customer']['contactDetails']['phoneNumber'] = address[:phone] if address[:phone]
|
164
156
|
end
|
165
157
|
end
|
166
158
|
|
@@ -169,10 +161,10 @@ module ActiveMerchant #:nodoc:
|
|
169
161
|
post['customer']['address'] = {
|
170
162
|
'countryCode' => address[:country]
|
171
163
|
}
|
172
|
-
post['customer']['contactDetails'] =
|
173
|
-
|
174
|
-
'phoneNumber'
|
175
|
-
|
164
|
+
post['customer']['contactDetails']['emailAddress'] = options[:email] if options[:email]
|
165
|
+
if address = options[:billing_address] || options[:address]
|
166
|
+
post['customer']['contactDetails']['phoneNumber'] = address[:phone] if address[:phone]
|
167
|
+
end
|
176
168
|
end
|
177
169
|
end
|
178
170
|
|
@@ -103,7 +103,7 @@ module ActiveMerchant #:nodoc:
|
|
103
103
|
xml.tag! 'Transaction' do
|
104
104
|
xml.tag! 'TranType', 'Credit'
|
105
105
|
xml.tag! 'TranCode', action
|
106
|
-
if options[:allow_partial_auth] &&
|
106
|
+
if options[:allow_partial_auth] && ['PreAuth', 'Sale'].include?(action)
|
107
107
|
xml.tag! 'PartialAuth', 'Allow'
|
108
108
|
end
|
109
109
|
add_invoice(xml, options[:order_id], nil, options)
|
@@ -4,26 +4,26 @@ module ActiveMerchant #:nodoc:
|
|
4
4
|
def email
|
5
5
|
@params['e_mail']
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
def full_name
|
9
9
|
"#{@params['name']} #{@params['lastname']}"
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
def token
|
13
13
|
@params['token']
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
def payer_id
|
17
17
|
@params['payer_id']
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
# Really the shipping country, but it is all the information provided
|
21
21
|
def payer_country
|
22
22
|
address['country']
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
def address
|
26
|
-
{ 'name' => full_name,
|
26
|
+
{ 'name' => @params['shiptoname'] || full_name,
|
27
27
|
'company' => nil,
|
28
28
|
'address1' => @params['street'],
|
29
29
|
'address2' => @params['shiptostreet2'] || @params['street2'],
|
@@ -114,6 +114,7 @@ module ActiveMerchant #:nodoc:
|
|
114
114
|
|
115
115
|
def add_invoice(post, options)
|
116
116
|
post[:description] = options[:description] || 'Active Merchant Purchase'
|
117
|
+
post[:reference] = options[:reference] if options[:reference]
|
117
118
|
end
|
118
119
|
|
119
120
|
def add_capture(post, options)
|
@@ -6,7 +6,7 @@ module ActiveMerchant #:nodoc:
|
|
6
6
|
self.test_url = 'https://process.sandbox.safecharge.com/service.asmx/Process'
|
7
7
|
self.live_url = 'https://process.safecharge.com/service.asmx/Process'
|
8
8
|
|
9
|
-
self.supported_countries = ['AT', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'GR', 'ES', 'FI', 'FR', 'HR', 'HU', 'IE', 'IS', 'IT', 'LI', 'LT', 'LU', 'LV', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SE', '
|
9
|
+
self.supported_countries = ['AT', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'GR', 'ES', 'FI', 'FR', 'HR', 'HU', 'IE', 'IS', 'IT', 'LI', 'LT', 'LU', 'LV', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SE', 'SI', 'SK', 'GB', 'US']
|
10
10
|
self.default_currency = 'USD'
|
11
11
|
self.supported_cardtypes = [:visa, :master]
|
12
12
|
|
@@ -397,6 +397,7 @@ module ActiveMerchant #:nodoc:
|
|
397
397
|
if emv_payment?(creditcard)
|
398
398
|
add_emv_creditcard(post, creditcard.icc_data)
|
399
399
|
post[:card][:read_method] = 'contactless' if creditcard.read_method == 'contactless'
|
400
|
+
post[:card][:read_method] = 'contactless_magstripe_mode' if creditcard.read_method == 'contactless_magstripe'
|
400
401
|
if creditcard.encrypted_pin_cryptogram.present? && creditcard.encrypted_pin_ksn.present?
|
401
402
|
post[:card][:encrypted_pin] = creditcard.encrypted_pin_cryptogram
|
402
403
|
post[:card][:encrypted_pin_key_id] = creditcard.encrypted_pin_ksn
|
@@ -177,19 +177,6 @@ module ActiveMerchant
|
|
177
177
|
end
|
178
178
|
end
|
179
179
|
|
180
|
-
def handle_response(response)
|
181
|
-
if @ignore_http_status then
|
182
|
-
return response.body
|
183
|
-
else
|
184
|
-
case response.code.to_i
|
185
|
-
when 200...300
|
186
|
-
response.body
|
187
|
-
else
|
188
|
-
raise ResponseError.new(response)
|
189
|
-
end
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
180
|
def debug(message, tag = nil)
|
194
181
|
log(:debug, message, tag)
|
195
182
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activemerchant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.81.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tobias Luetke
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-08-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -204,6 +204,7 @@ files:
|
|
204
204
|
- lib/active_merchant/billing/gateways/conekta.rb
|
205
205
|
- lib/active_merchant/billing/gateways/creditcall.rb
|
206
206
|
- lib/active_merchant/billing/gateways/credorax.rb
|
207
|
+
- lib/active_merchant/billing/gateways/ct_payment.rb
|
207
208
|
- lib/active_merchant/billing/gateways/culqi.rb
|
208
209
|
- lib/active_merchant/billing/gateways/cyber_source.rb
|
209
210
|
- lib/active_merchant/billing/gateways/data_cash.rb
|
@@ -226,6 +227,7 @@ files:
|
|
226
227
|
- lib/active_merchant/billing/gateways/first_giving.rb
|
227
228
|
- lib/active_merchant/billing/gateways/first_pay.rb
|
228
229
|
- lib/active_merchant/billing/gateways/firstdata_e4.rb
|
230
|
+
- lib/active_merchant/billing/gateways/firstdata_e4_v27.rb
|
229
231
|
- lib/active_merchant/billing/gateways/flo2cash.rb
|
230
232
|
- lib/active_merchant/billing/gateways/flo2cash_simple.rb
|
231
233
|
- lib/active_merchant/billing/gateways/forte.rb
|