activemerchant 1.80.0 → 1.81.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|