activemerchant 1.50.0 → 1.51.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 +39 -0
- data/README.md +6 -5
- data/lib/active_merchant/billing/credit_card.rb +22 -6
- data/lib/active_merchant/billing/gateway.rb +15 -1
- data/lib/active_merchant/billing/gateways/authorize_net.rb +413 -127
- data/lib/active_merchant/billing/gateways/banwire.rb +2 -2
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +4 -0
- data/lib/active_merchant/billing/gateways/card_stream.rb +18 -0
- data/lib/active_merchant/billing/gateways/cenpos.rb +6 -2
- data/lib/active_merchant/billing/gateways/checkout.rb +4 -6
- data/lib/active_merchant/billing/gateways/checkout_v2.rb +200 -0
- data/lib/active_merchant/billing/gateways/cyber_source.rb +19 -1
- data/lib/active_merchant/billing/gateways/dibs.rb +1 -1
- data/lib/active_merchant/billing/gateways/epay.rb +1 -1
- data/lib/active_merchant/billing/gateways/eway_rapid.rb +5 -5
- data/lib/active_merchant/billing/gateways/firstdata_e4.rb +4 -1
- data/lib/active_merchant/billing/gateways/garanti.rb +5 -1
- data/lib/active_merchant/billing/gateways/iats_payments.rb +29 -3
- data/lib/active_merchant/billing/gateways/litle.rb +12 -0
- data/lib/active_merchant/billing/gateways/paystation.rb +19 -23
- data/lib/active_merchant/billing/gateways/payu_in.rb +4 -0
- data/lib/active_merchant/billing/gateways/redsys.rb +4 -3
- data/lib/active_merchant/billing/gateways/s5.rb +3 -3
- data/lib/active_merchant/billing/gateways/sage.rb +8 -10
- data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +7 -5
- data/lib/active_merchant/billing/gateways/sage/sage_core.rb +3 -2
- data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +0 -5
- data/lib/active_merchant/billing/gateways/stripe.rb +33 -4
- data/lib/active_merchant/billing/gateways/wepay.rb +13 -6
- data/lib/active_merchant/billing/gateways/worldpay_online_payments.rb +1 -2
- data/lib/active_merchant/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 410fa6679494b0904c1bff664722c9b23c907e06
|
4
|
+
data.tar.gz: bf3827fcd65d592b51d9e5d06b28dfeec0e05ac2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 228a238436b76648adcfab724d7807fa75c1af50c5b975057da9461a64cf9863c1b1693c65e24ceb2250e9e3bebbadca189c7d07ff4bff9b45fcd79c2ae57d23
|
7
|
+
data.tar.gz: 0922949cb3d6db73cbb74324ae57865f4eb72bcdafaba4ce4cbf468558b8d7f514dabe74cec344ca43f7a4e98906c8eff82195f604219ff851492aa5f7fadab5
|
data/CHANGELOG
CHANGED
@@ -1,5 +1,42 @@
|
|
1
1
|
= ActiveMerchant CHANGELOG
|
2
2
|
|
3
|
+
|
4
|
+
== Version 1.51.0 (July 2, 2015)
|
5
|
+
|
6
|
+
* Garanti: Illegal character '&' parsing response [masaruhoshi]
|
7
|
+
* Stripe: Revert force USD for verify [duff]
|
8
|
+
* Litle: Surface XML validation errors in the response [jasonbosco]
|
9
|
+
* Litle: Pass the credit card verification value for tokenization (#store) requests, if one is set. [jasonbosco]
|
10
|
+
* S5: Make scrubbing regex less greedy [duff]
|
11
|
+
* CardStream: Add support for verify [anellis]
|
12
|
+
* Authorize.net: UTF-8 encode requests [duff]
|
13
|
+
* Banwire: Add default email [anellis]
|
14
|
+
* PayU India: Handle bad JSON [ntalbott]
|
15
|
+
* Dibs: Pass CVC param only if there's a value [bruno]
|
16
|
+
* Sage: Credit really is credit not refund [duff]
|
17
|
+
* Sage: Add ability to refund [duff]
|
18
|
+
* Cardstream: Add scrubbing [anellis]
|
19
|
+
* Litle: Add debt_repayment_flag [duff]
|
20
|
+
* iATS: Support ACH [rwdaigle]
|
21
|
+
* CheckoutV2: Add Gateway [anellis]
|
22
|
+
* CenPOS: Fix refund amount issue [duff]
|
23
|
+
* Add error_code mapping and error_code_from to gateway generator [jnormore]
|
24
|
+
* Stripe: Parse EMV ARC from error response [bizla]
|
25
|
+
* Redsys: Add MYR currency [agseco]
|
26
|
+
* Add "contactless" flag to credit card model [davidseal]
|
27
|
+
* Stripe: Add "contactless" flag support to gateway [davidseal]
|
28
|
+
* Add encrypted_pin data to credit card model [ryanbalsdon]
|
29
|
+
* Stripe: Add encrypted_pin support to gateway [ryanbalsdon]
|
30
|
+
* Stripe: Support mapping advanced decline codes to standard codes [abecevello]
|
31
|
+
* Epay: filter out invalid characters in returned URLs [dwradcliffe]
|
32
|
+
* Redsys: Strip leading zeroes from currency codes [agseco]
|
33
|
+
* Authorize.net: Add invoice information to refund [marquisong]
|
34
|
+
* Authorize.net: Add store ability [duff]
|
35
|
+
* Paystation: Add refund [mrezentes]
|
36
|
+
* Paystation: No longer require order_id everywhere [duff]
|
37
|
+
* Checkout: Support descriptor_name and descriptor_city [duff]
|
38
|
+
* Add supports_network_tokenization? to gateways [jnormore]
|
39
|
+
|
3
40
|
== Version 1.50.0 (June 1, 2015)
|
4
41
|
|
5
42
|
* Vanco: Add gateway [duff]
|
@@ -840,6 +877,8 @@
|
|
840
877
|
* Braintree Blue gateway: Always pass CVV on update [shayfrendt]
|
841
878
|
* eWAY gateway: Update docs. Require address [juggler]
|
842
879
|
* Cybersource gateway: Add support for subscriptions [fabiokr]
|
880
|
+
* WePay: Use better endpoint for recurring with no CVV [davidsantoso]
|
881
|
+
|
843
882
|
|
844
883
|
== Version 1.24.0 (June 8, 2012)
|
845
884
|
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Active Merchant
|
2
|
-
[](https://travis-ci.org/activemerchant/active_merchant)
|
3
|
+
[](https://codeclimate.com/github/activemerchant/active_merchant)
|
4
4
|
|
5
5
|
Active Merchant is an extraction from the ecommerce system [Shopify](http://www.shopify.com).
|
6
6
|
Shopify's requirements for a simple and unified API to access dozens of different payment
|
@@ -23,7 +23,7 @@ applications.
|
|
23
23
|
|
24
24
|
You can check out the latest source from git:
|
25
25
|
|
26
|
-
git clone git://github.com/
|
26
|
+
git clone git://github.com/activemerchant/active_merchant.git
|
27
27
|
|
28
28
|
### From RubyGems
|
29
29
|
|
@@ -76,11 +76,11 @@ end
|
|
76
76
|
```
|
77
77
|
|
78
78
|
For more in-depth documentation and tutorials, see [GettingStarted.md](GettingStarted.md) and the
|
79
|
-
[API documentation](http://www.rubydoc.info/github/
|
79
|
+
[API documentation](http://www.rubydoc.info/github/activemerchant/active_merchant/).
|
80
80
|
|
81
81
|
## Supported Payment Gateways
|
82
82
|
|
83
|
-
The [ActiveMerchant Wiki](http://github.com/
|
83
|
+
The [ActiveMerchant Wiki](http://github.com/activemerchant/active_merchant/wikis) contains a [table of features supported by each gateway](http://github.com/activemerchant/active_merchant/wikis/gatewayfeaturematrix).
|
84
84
|
|
85
85
|
* [App55](https://www.app55.com/) - AU, BR, CA, CH, CL, CN, CO, CZ, DK, EU, GB, HK, HU, ID, IS, JP, KE, KR, MX, MY, NO, NZ, PH, PL, TH, TW, US, VN, ZA
|
86
86
|
* [Authorize.Net CIM](http://www.authorize.net/) - US
|
@@ -156,6 +156,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
|
|
156
156
|
* [NETPAY Gateway](http://www.netpay.com.mx) - MX
|
157
157
|
* [NMI](http://nmi.com/) - US
|
158
158
|
* [Ogone](http://www.ogone.com/) - BE, DE, FR, NL, AT, CH
|
159
|
+
* [Omise](https://www.omise.co/) - TH
|
159
160
|
* [Openpay](Openpay) - MX
|
160
161
|
* [Optimal Payments](http://www.optimalpayments.com/) - CA, US, GB
|
161
162
|
* [Orbital Paymentech](http://chasepaymentech.com/) - US, CA
|
@@ -35,12 +35,13 @@ module ActiveMerchant #:nodoc:
|
|
35
35
|
#
|
36
36
|
# == Example Usage
|
37
37
|
# cc = CreditCard.new(
|
38
|
-
# :first_name
|
39
|
-
# :last_name
|
40
|
-
# :month
|
41
|
-
# :year
|
42
|
-
# :brand
|
43
|
-
# :number
|
38
|
+
# :first_name => 'Steve',
|
39
|
+
# :last_name => 'Smith',
|
40
|
+
# :month => '9',
|
41
|
+
# :year => '2017',
|
42
|
+
# :brand => 'visa',
|
43
|
+
# :number => '4242424242424242',
|
44
|
+
# :verification_value => '424'
|
44
45
|
# )
|
45
46
|
#
|
46
47
|
# cc.validate # => {}
|
@@ -158,6 +159,21 @@ module ActiveMerchant #:nodoc:
|
|
158
159
|
# @return [String]
|
159
160
|
attr_accessor :fallback_reason
|
160
161
|
|
162
|
+
# Returns or sets whether card-present card data has been read contactlessly.
|
163
|
+
#
|
164
|
+
# @return [true, false]
|
165
|
+
attr_accessor :contactless
|
166
|
+
|
167
|
+
# Returns the ciphertext of the card's encrypted PIN.
|
168
|
+
#
|
169
|
+
# @return [String]
|
170
|
+
attr_accessor :encrypted_pin_cryptogram
|
171
|
+
|
172
|
+
# Returns the Key Serial Number (KSN) of the card's encrypted PIN.
|
173
|
+
#
|
174
|
+
# @return [String]
|
175
|
+
attr_accessor :encrypted_pin_ksn
|
176
|
+
|
161
177
|
def type
|
162
178
|
ActiveMerchant.deprecated "CreditCard#type is deprecated and will be removed from a future release of ActiveMerchant. Please use CreditCard#brand instead."
|
163
179
|
brand
|
@@ -50,7 +50,7 @@ module ActiveMerchant #:nodoc:
|
|
50
50
|
#
|
51
51
|
# == Implmenting new gateways
|
52
52
|
#
|
53
|
-
# See the {ActiveMerchant Guide to Contributing}[https://github.com/
|
53
|
+
# See the {ActiveMerchant Guide to Contributing}[https://github.com/activemerchant/active_merchant/wiki/Contributing]
|
54
54
|
#
|
55
55
|
class Gateway
|
56
56
|
include PostsData
|
@@ -190,6 +190,10 @@ module ActiveMerchant #:nodoc:
|
|
190
190
|
raise RuntimeError.new("This gateway does not support scrubbing.")
|
191
191
|
end
|
192
192
|
|
193
|
+
def supports_network_tokenization?
|
194
|
+
false
|
195
|
+
end
|
196
|
+
|
193
197
|
protected # :nodoc: all
|
194
198
|
|
195
199
|
def normalize(field)
|
@@ -212,6 +216,16 @@ module ActiveMerchant #:nodoc:
|
|
212
216
|
})
|
213
217
|
end
|
214
218
|
|
219
|
+
def strip_invalid_xml_chars(xml)
|
220
|
+
begin
|
221
|
+
REXML::Document.new(xml)
|
222
|
+
rescue REXML::ParseException
|
223
|
+
xml = xml.gsub(/&(?!(?:[a-z]+|#[0-9]+|x[a-zA-Z0-9]+);)/, '&')
|
224
|
+
end
|
225
|
+
|
226
|
+
xml
|
227
|
+
end
|
228
|
+
|
215
229
|
private # :nodoc: all
|
216
230
|
|
217
231
|
def name
|
@@ -56,88 +56,58 @@ module ActiveMerchant #:nodoc:
|
|
56
56
|
|
57
57
|
APPLE_PAY_DATA_DESCRIPTOR = "COMMON.APPLE.INAPP.PAYMENT"
|
58
58
|
|
59
|
+
PAYMENT_METHOD_NOT_SUPPORTED_ERROR = "155"
|
60
|
+
|
59
61
|
def initialize(options={})
|
60
62
|
requires!(options, :login, :password)
|
61
63
|
super
|
62
64
|
end
|
63
65
|
|
64
66
|
def purchase(amount, payment, options = {})
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
add_invoice(xml, options)
|
73
|
-
add_customer_data(xml, payment, options)
|
74
|
-
add_market_type(xml, payment)
|
75
|
-
add_settings(xml, payment, options)
|
76
|
-
add_user_fields(xml, amount, options)
|
67
|
+
if payment.is_a?(String)
|
68
|
+
commit(:cim_purchase) do |xml|
|
69
|
+
add_cim_auth_purchase(xml, "profileTransAuthCapture", amount, payment, options)
|
70
|
+
end
|
71
|
+
else
|
72
|
+
commit(:purchase) do |xml|
|
73
|
+
add_auth_purchase(xml, "authCaptureTransaction", amount, payment, options)
|
77
74
|
end
|
78
75
|
end
|
79
76
|
end
|
80
77
|
|
81
78
|
def authorize(amount, payment, options={})
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
add_invoice(xml, options)
|
90
|
-
add_customer_data(xml, payment, options)
|
91
|
-
add_market_type(xml, payment)
|
92
|
-
add_settings(xml, payment, options)
|
93
|
-
add_user_fields(xml, amount, options)
|
79
|
+
if payment.is_a?(String)
|
80
|
+
commit(:cim_authorize) do |xml|
|
81
|
+
add_cim_auth_purchase(xml, "profileTransAuthOnly", amount, payment, options)
|
82
|
+
end
|
83
|
+
else
|
84
|
+
commit(:authorize) do |xml|
|
85
|
+
add_auth_purchase(xml, "authOnlyTransaction", amount, payment, options)
|
94
86
|
end
|
95
87
|
end
|
96
88
|
end
|
97
89
|
|
98
90
|
def capture(amount, authorization, options={})
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
xml.amount(amount(amount))
|
104
|
-
xml.refTransId(split_authorization(authorization)[0])
|
105
|
-
|
106
|
-
add_invoice(xml, options)
|
107
|
-
add_user_fields(xml, amount, options)
|
108
|
-
end
|
91
|
+
if auth_was_for_cim?(authorization)
|
92
|
+
cim_capture(amount, authorization, options)
|
93
|
+
else
|
94
|
+
normal_capture(amount, authorization, options)
|
109
95
|
end
|
110
96
|
end
|
111
97
|
|
112
98
|
def refund(amount, authorization, options={})
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
xml.amount(amount.nil? ? 0 : amount(amount))
|
118
|
-
xml.payment do
|
119
|
-
xml.creditCard do
|
120
|
-
xml.cardNumber(card_number || options[:card_number])
|
121
|
-
xml.expirationDate('XXXX')
|
122
|
-
end
|
123
|
-
end
|
124
|
-
xml.refTransId(transaction_id)
|
125
|
-
|
126
|
-
add_customer_data(xml, nil, options)
|
127
|
-
add_user_fields(xml, amount, options)
|
128
|
-
end
|
99
|
+
if auth_was_for_cim?(authorization)
|
100
|
+
cim_refund(amount, authorization, options)
|
101
|
+
else
|
102
|
+
normal_refund(amount, authorization, options)
|
129
103
|
end
|
130
104
|
end
|
131
105
|
|
132
106
|
def void(authorization, options={})
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
xml.refTransId(split_authorization(authorization)[0])
|
138
|
-
|
139
|
-
add_user_fields(xml, nil, options)
|
140
|
-
end
|
107
|
+
if auth_was_for_cim?(authorization)
|
108
|
+
cim_void(authorization, options)
|
109
|
+
else
|
110
|
+
normal_void(authorization, options)
|
141
111
|
end
|
142
112
|
end
|
143
113
|
|
@@ -146,7 +116,7 @@ module ActiveMerchant #:nodoc:
|
|
146
116
|
raise ArgumentError, "Reference credits are not supported. Please supply the original credit card or use the #refund method."
|
147
117
|
end
|
148
118
|
|
149
|
-
commit(
|
119
|
+
commit(:credit) do |xml|
|
150
120
|
add_order_id(xml, options)
|
151
121
|
xml.transactionRequest do
|
152
122
|
xml.transactionType('refundTransaction')
|
@@ -168,21 +138,179 @@ module ActiveMerchant #:nodoc:
|
|
168
138
|
end
|
169
139
|
end
|
170
140
|
|
141
|
+
def store(credit_card, options = {})
|
142
|
+
commit(:cim_store) do |xml|
|
143
|
+
xml.profile do
|
144
|
+
xml.merchantCustomerId(truncate(options[:merchant_customer_id], 20) || SecureRandom.hex(10))
|
145
|
+
xml.description(truncate(options[:description], 255)) unless empty?(options[:description])
|
146
|
+
xml.email(options[:email]) unless empty?(options[:email])
|
147
|
+
|
148
|
+
xml.paymentProfiles do
|
149
|
+
xml.customerType("individual")
|
150
|
+
add_billing_address(xml, credit_card, options)
|
151
|
+
add_shipping_address(xml, options, "shipToList")
|
152
|
+
xml.payment do
|
153
|
+
xml.creditCard do
|
154
|
+
xml.cardNumber(truncate(credit_card.number, 16))
|
155
|
+
xml.expirationDate(format(credit_card.year, :four_digits) + '-' + format(credit_card.month, :two_digits))
|
156
|
+
xml.cardCode(credit_card.verification_value) if credit_card.verification_value
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
171
164
|
def supports_scrubbing?
|
172
165
|
true
|
173
166
|
end
|
174
167
|
|
175
168
|
def scrub(transcript)
|
176
169
|
transcript.
|
170
|
+
gsub(%r((<transactionKey>).+(</transactionKey>)), '\1[FILTERED]\2').
|
177
171
|
gsub(%r((<cardNumber>).+(</cardNumber>)), '\1[FILTERED]\2').
|
178
|
-
gsub(%r((<cardCode>).+(</cardCode>)), '\1[FILTERED]\2')
|
172
|
+
gsub(%r((<cardCode>).+(</cardCode>)), '\1[FILTERED]\2').
|
173
|
+
gsub(%r((<track1>).+(</track1>)), '\1[FILTERED]\2').
|
174
|
+
gsub(%r((<track2>).+(</track2>)), '\1[FILTERED]\2').
|
175
|
+
gsub(%r((<cryptogram>).+(</cryptogram>)), '\1[FILTERED]\2')
|
176
|
+
end
|
177
|
+
|
178
|
+
def supports_network_tokenization?
|
179
|
+
card = Billing::NetworkTokenizationCreditCard.new({
|
180
|
+
:number => "4111111111111111",
|
181
|
+
:month => 12,
|
182
|
+
:year => 20,
|
183
|
+
:first_name => 'John',
|
184
|
+
:last_name => 'Smith',
|
185
|
+
:brand => 'visa',
|
186
|
+
:payment_cryptogram => 'EHuWW9PiBkWvqE5juRwDzAUFBAk='
|
187
|
+
})
|
188
|
+
|
189
|
+
request = post_data(:authorize) do |xml|
|
190
|
+
add_auth_purchase(xml, "authOnlyTransaction", 1, card, {})
|
191
|
+
end
|
192
|
+
raw_response = ssl_post(url, request, headers)
|
193
|
+
response = parse(:authorize, raw_response)
|
194
|
+
response[:response_reason_code].to_s != PAYMENT_METHOD_NOT_SUPPORTED_ERROR
|
179
195
|
end
|
180
196
|
|
181
197
|
private
|
182
198
|
|
199
|
+
def add_auth_purchase(xml, transaction_type, amount, payment, options)
|
200
|
+
add_order_id(xml, options)
|
201
|
+
xml.transactionRequest do
|
202
|
+
xml.transactionType(transaction_type)
|
203
|
+
xml.amount(amount(amount))
|
204
|
+
add_payment_source(xml, payment)
|
205
|
+
add_invoice(xml, options)
|
206
|
+
add_customer_data(xml, payment, options)
|
207
|
+
add_market_type(xml, payment)
|
208
|
+
add_settings(xml, payment, options)
|
209
|
+
add_user_fields(xml, amount, options)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def add_cim_auth_purchase(xml, transaction_type, amount, payment, options)
|
214
|
+
add_order_id(xml, options)
|
215
|
+
xml.transaction do
|
216
|
+
xml.send(transaction_type) do
|
217
|
+
xml.amount(amount(amount))
|
218
|
+
add_payment_source(xml, payment)
|
219
|
+
add_invoice(xml, options)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def cim_capture(amount, authorization, options)
|
225
|
+
commit(:cim_capture) do |xml|
|
226
|
+
add_order_id(xml, options)
|
227
|
+
xml.transaction do
|
228
|
+
xml.profileTransPriorAuthCapture do
|
229
|
+
xml.amount(amount(amount))
|
230
|
+
xml.transId(transaction_id_from(authorization))
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def normal_capture(amount, authorization, options)
|
237
|
+
commit(:capture) do |xml|
|
238
|
+
add_order_id(xml, options)
|
239
|
+
xml.transactionRequest do
|
240
|
+
xml.transactionType('priorAuthCaptureTransaction')
|
241
|
+
xml.amount(amount(amount))
|
242
|
+
xml.refTransId(transaction_id_from(authorization))
|
243
|
+
add_invoice(xml, options)
|
244
|
+
add_user_fields(xml, amount, options)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def cim_refund(amount, authorization, options)
|
250
|
+
transaction_id, card_number, _ = split_authorization(authorization)
|
251
|
+
|
252
|
+
commit(:cim_refund) do |xml|
|
253
|
+
add_order_id(xml, options)
|
254
|
+
xml.transaction do
|
255
|
+
xml.profileTransRefund do
|
256
|
+
xml.amount(amount(amount))
|
257
|
+
xml.creditCardNumberMasked(card_number)
|
258
|
+
add_invoice(xml, options)
|
259
|
+
xml.transId(transaction_id)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def normal_refund(amount, authorization, options)
|
266
|
+
transaction_id, card_number, _ = split_authorization(authorization)
|
267
|
+
|
268
|
+
commit(:refund) do |xml|
|
269
|
+
xml.transactionRequest do
|
270
|
+
xml.transactionType('refundTransaction')
|
271
|
+
xml.amount(amount.nil? ? 0 : amount(amount))
|
272
|
+
xml.payment do
|
273
|
+
xml.creditCard do
|
274
|
+
xml.cardNumber(card_number || options[:card_number])
|
275
|
+
xml.expirationDate('XXXX')
|
276
|
+
end
|
277
|
+
end
|
278
|
+
xml.refTransId(transaction_id)
|
279
|
+
|
280
|
+
add_invoice(xml, options)
|
281
|
+
add_customer_data(xml, nil, options)
|
282
|
+
add_user_fields(xml, amount, options)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def cim_void(authorization, options)
|
288
|
+
commit(:cim_void) do |xml|
|
289
|
+
add_order_id(xml, options)
|
290
|
+
xml.transaction do
|
291
|
+
xml.profileTransVoid do
|
292
|
+
xml.transId(transaction_id_from(authorization))
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
def normal_void(authorization, options)
|
299
|
+
commit(:void) do |xml|
|
300
|
+
add_order_id(xml, options)
|
301
|
+
xml.transactionRequest do
|
302
|
+
xml.transactionType('voidTransaction')
|
303
|
+
xml.refTransId(transaction_id_from(authorization))
|
304
|
+
add_user_fields(xml, nil, options)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
183
309
|
def add_payment_source(xml, source)
|
184
310
|
return unless source
|
185
|
-
if
|
311
|
+
if source.is_a?(String)
|
312
|
+
add_token_payment_method(xml, source)
|
313
|
+
elsif card_brand(source) == 'check'
|
186
314
|
add_check(xml, source)
|
187
315
|
elsif card_brand(source) == 'apple_pay'
|
188
316
|
add_apple_pay_payment_token(xml, source)
|
@@ -193,7 +321,7 @@ module ActiveMerchant #:nodoc:
|
|
193
321
|
|
194
322
|
def add_settings(xml, source, options)
|
195
323
|
xml.transactionSettings do
|
196
|
-
if card_brand(source) == "check" && options[:recurring]
|
324
|
+
if !source.is_a?(String) && card_brand(source) == "check" && options[:recurring]
|
197
325
|
xml.setting do
|
198
326
|
xml.settingName("recurringBilling")
|
199
327
|
xml.settingValue("true")
|
@@ -264,7 +392,12 @@ module ActiveMerchant #:nodoc:
|
|
264
392
|
end
|
265
393
|
end
|
266
394
|
|
267
|
-
|
395
|
+
def add_token_payment_method(xml, token)
|
396
|
+
customer_profile_id, customer_payment_profile_id, _ = split_authorization(token)
|
397
|
+
xml.customerProfileId(customer_profile_id)
|
398
|
+
xml.customerPaymentProfileId(customer_payment_profile_id)
|
399
|
+
end
|
400
|
+
|
268
401
|
def add_apple_pay_payment_token(xml, apple_pay_payment_token)
|
269
402
|
xml.payment do
|
270
403
|
xml.opaqueData do
|
@@ -275,7 +408,7 @@ module ActiveMerchant #:nodoc:
|
|
275
408
|
end
|
276
409
|
|
277
410
|
def add_market_type(xml, payment)
|
278
|
-
return if card_brand(payment) == 'check'
|
411
|
+
return if payment.is_a?(String) || card_brand(payment) == 'check' || card_brand(payment) == 'apple_pay'
|
279
412
|
if valid_track_data
|
280
413
|
xml.retail do
|
281
414
|
xml.marketType(MARKET_TYPE[:retail])
|
@@ -305,54 +438,63 @@ module ActiveMerchant #:nodoc:
|
|
305
438
|
end
|
306
439
|
|
307
440
|
def add_customer_data(xml, payment_source, options)
|
308
|
-
billing_address = options[:billing_address] || options[:address] || {}
|
309
|
-
shipping_address = options[:shipping_address] || options[:address] || {}
|
310
|
-
|
311
441
|
xml.customer do
|
312
442
|
xml.id(options[:customer]) unless empty?(options[:customer]) || options[:customer] !~ /^\d+$/
|
313
443
|
xml.email(options[:email]) unless empty?(options[:email])
|
314
444
|
end
|
315
445
|
|
446
|
+
add_billing_address(xml, payment_source, options)
|
447
|
+
add_shipping_address(xml, options)
|
448
|
+
|
449
|
+
xml.customerIP(options[:ip]) unless empty?(options[:ip])
|
450
|
+
|
451
|
+
xml.cardholderAuthentication do
|
452
|
+
xml.authenticationIndicator(options[:authentication_indicator])
|
453
|
+
xml.cardholderAuthenticationValue(options[:cardholder_authentication_value])
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
def add_billing_address(xml, payment_source, options)
|
458
|
+
address = options[:billing_address] || options[:address] || {}
|
459
|
+
|
316
460
|
xml.billTo do
|
317
|
-
first_name, last_name = names_from(payment_source,
|
461
|
+
first_name, last_name = names_from(payment_source, address, options)
|
318
462
|
xml.firstName(truncate(first_name, 50)) unless empty?(first_name)
|
319
463
|
xml.lastName(truncate(last_name, 50)) unless empty?(last_name)
|
320
464
|
|
321
|
-
xml.company(truncate(
|
322
|
-
xml.address(truncate(
|
323
|
-
xml.city(truncate(
|
324
|
-
xml.state(empty?(
|
325
|
-
xml.zip(truncate((
|
326
|
-
xml.country(truncate(
|
327
|
-
xml.phoneNumber(truncate(
|
328
|
-
xml.faxNumber(truncate(
|
329
|
-
end
|
330
|
-
|
331
|
-
unless shipping_address.blank?
|
332
|
-
xml.shipTo do
|
333
|
-
(first_name, last_name) = if shipping_address[:name]
|
334
|
-
shipping_address[:name].split
|
335
|
-
else
|
336
|
-
[shipping_address[:first_name], shipping_address[:last_name]]
|
337
|
-
end
|
338
|
-
xml.firstName(truncate(first_name, 50)) unless empty?(first_name)
|
339
|
-
xml.lastName(truncate(last_name, 50)) unless empty?(last_name)
|
340
|
-
|
341
|
-
xml.company(truncate(shipping_address[:company], 50)) unless empty?(shipping_address[:company])
|
342
|
-
xml.address(truncate(shipping_address[:address1], 60))
|
343
|
-
xml.city(truncate(shipping_address[:city], 40))
|
344
|
-
xml.state(truncate(shipping_address[:state], 40))
|
345
|
-
xml.zip(truncate(shipping_address[:zip], 20))
|
346
|
-
xml.country(truncate(shipping_address[:country], 60))
|
347
|
-
end
|
465
|
+
xml.company(truncate(address[:company], 50)) unless empty?(address[:company])
|
466
|
+
xml.address(truncate(address[:address1], 60))
|
467
|
+
xml.city(truncate(address[:city], 40))
|
468
|
+
xml.state(empty?(address[:state]) ? 'n/a' : truncate(address[:state], 40))
|
469
|
+
xml.zip(truncate((address[:zip] || options[:zip]), 20))
|
470
|
+
xml.country(truncate(address[:country], 60))
|
471
|
+
xml.phoneNumber(truncate(address[:phone], 25)) unless empty?(address[:phone])
|
472
|
+
xml.faxNumber(truncate(address[:fax], 25)) unless empty?(address[:fax])
|
348
473
|
end
|
474
|
+
end
|
349
475
|
|
350
|
-
|
476
|
+
def add_shipping_address(xml, options, root_node="shipTo")
|
477
|
+
address = options[:shipping_address] || options[:address]
|
478
|
+
return unless address
|
351
479
|
|
352
|
-
xml.
|
353
|
-
|
354
|
-
|
480
|
+
xml.send(root_node) do
|
481
|
+
first_name, last_name = if address[:name]
|
482
|
+
split_names(address[:name])
|
483
|
+
else
|
484
|
+
[address[:first_name], address[:last_name]]
|
485
|
+
end
|
486
|
+
|
487
|
+
xml.firstName(truncate(first_name, 50)) unless empty?(first_name)
|
488
|
+
xml.lastName(truncate(last_name, 50)) unless empty?(last_name)
|
489
|
+
|
490
|
+
xml.company(truncate(address[:company], 50)) unless empty?(address[:company])
|
491
|
+
xml.address(truncate(address[:address1], 60))
|
492
|
+
xml.city(truncate(address[:city], 40))
|
493
|
+
xml.state(truncate(address[:state], 40))
|
494
|
+
xml.zip(truncate(address[:zip], 20))
|
495
|
+
xml.country(truncate(address[:country], 60))
|
355
496
|
end
|
497
|
+
|
356
498
|
end
|
357
499
|
|
358
500
|
def add_order_id(xml, options)
|
@@ -367,17 +509,40 @@ module ActiveMerchant #:nodoc:
|
|
367
509
|
end
|
368
510
|
|
369
511
|
def names_from(payment_source, address, options)
|
370
|
-
if payment_source && !payment_source.is_a?(PaymentToken)
|
371
|
-
first_name, last_name = (address[:name]
|
512
|
+
if payment_source && !payment_source.is_a?(PaymentToken) && !payment_source.is_a?(String)
|
513
|
+
first_name, last_name = split_names(address[:name])
|
372
514
|
[(payment_source.first_name || first_name), (payment_source.last_name || last_name)]
|
373
515
|
else
|
374
516
|
[options[:first_name], options[:last_name]]
|
375
517
|
end
|
376
518
|
end
|
377
519
|
|
520
|
+
def split_names(full_name)
|
521
|
+
names = (full_name || "").split
|
522
|
+
last_name = names.pop
|
523
|
+
first_name = names.join(" ")
|
524
|
+
[first_name, last_name]
|
525
|
+
end
|
526
|
+
|
527
|
+
def headers
|
528
|
+
{ 'Content-Type' => 'text/xml' }
|
529
|
+
end
|
530
|
+
|
531
|
+
def url
|
532
|
+
test? ? test_url : live_url
|
533
|
+
end
|
534
|
+
|
535
|
+
def parse(action, raw_response)
|
536
|
+
if is_cim_action?(action)
|
537
|
+
parse_cim(raw_response)
|
538
|
+
else
|
539
|
+
parse_normal(action, raw_response)
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
378
543
|
def commit(action, &payload)
|
379
|
-
|
380
|
-
response = parse(action,
|
544
|
+
raw_response = ssl_post(url, post_data(action, &payload), headers)
|
545
|
+
response = parse(action, raw_response)
|
381
546
|
|
382
547
|
avs_result = AVSResult.new(code: response[:avs_result_code])
|
383
548
|
cvv_result = CVVResult.new(response[:card_code])
|
@@ -385,10 +550,10 @@ module ActiveMerchant #:nodoc:
|
|
385
550
|
Response.new(false, "Using a live Authorize.net account in Test Mode is not permitted.")
|
386
551
|
else
|
387
552
|
Response.new(
|
388
|
-
success_from(response),
|
389
|
-
message_from(response, avs_result, cvv_result),
|
553
|
+
success_from(action, response),
|
554
|
+
message_from(action, response, avs_result, cvv_result),
|
390
555
|
response,
|
391
|
-
authorization: authorization_from(response),
|
556
|
+
authorization: authorization_from(action, response),
|
392
557
|
test: test?,
|
393
558
|
avs_result: avs_result,
|
394
559
|
cvv_result: cvv_result,
|
@@ -398,19 +563,37 @@ module ActiveMerchant #:nodoc:
|
|
398
563
|
end
|
399
564
|
end
|
400
565
|
|
401
|
-
def
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
566
|
+
def is_cim_action?(action)
|
567
|
+
action.to_s.start_with?("cim")
|
568
|
+
end
|
569
|
+
|
570
|
+
def post_data(action)
|
571
|
+
Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
|
572
|
+
xml.send(root_for(action), 'xmlns' => 'AnetApi/xml/v1/schema/AnetApiSchema.xsd') do
|
573
|
+
add_authentication(xml)
|
408
574
|
yield(xml)
|
409
575
|
end
|
410
576
|
end.to_xml(indent: 0)
|
411
577
|
end
|
412
578
|
|
413
|
-
def
|
579
|
+
def root_for(action)
|
580
|
+
if action == :cim_store
|
581
|
+
"createCustomerProfileRequest"
|
582
|
+
elsif is_cim_action?(action)
|
583
|
+
"createCustomerProfileTransactionRequest"
|
584
|
+
else
|
585
|
+
"createTransactionRequest"
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
def add_authentication(xml)
|
590
|
+
xml.merchantAuthentication do
|
591
|
+
xml.name(@options[:login])
|
592
|
+
xml.transactionKey(@options[:password])
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
596
|
+
def parse_normal(action, body)
|
414
597
|
doc = Nokogiri::XML(body)
|
415
598
|
doc.remove_namespaces!
|
416
599
|
|
@@ -465,14 +648,50 @@ module ActiveMerchant #:nodoc:
|
|
465
648
|
response
|
466
649
|
end
|
467
650
|
|
468
|
-
def
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
651
|
+
def parse_cim(body)
|
652
|
+
response = {}
|
653
|
+
|
654
|
+
doc = Nokogiri::XML(body).remove_namespaces!
|
655
|
+
|
656
|
+
if (element = doc.at_xpath("//messages/message"))
|
657
|
+
response[:message_code] = element.at_xpath("code").content[/0*(\d+)$/, 1]
|
658
|
+
response[:message_text] = element.at_xpath("text").content.chomp('.')
|
659
|
+
end
|
660
|
+
|
661
|
+
response[:result_code] = if(element = doc.at_xpath("//messages/resultCode"))
|
662
|
+
(empty?(element.content) ? nil : element.content)
|
663
|
+
end
|
664
|
+
|
665
|
+
response[:test_request] = if(element = doc.at_xpath("//testRequest"))
|
666
|
+
(empty?(element.content) ? nil : element.content)
|
667
|
+
end
|
668
|
+
|
669
|
+
response[:customer_profile_id] = if(element = doc.at_xpath("//customerProfileId"))
|
670
|
+
(empty?(element.content) ? nil : element.content)
|
671
|
+
end
|
672
|
+
|
673
|
+
response[:customer_payment_profile_id] = if(element = doc.at_xpath("//customerPaymentProfileIdList/numericString"))
|
674
|
+
(empty?(element.content) ? nil : element.content)
|
675
|
+
end
|
676
|
+
|
677
|
+
response[:direct_response] = if(element = doc.at_xpath("//directResponse"))
|
678
|
+
(empty?(element.content) ? nil : element.content)
|
679
|
+
end
|
680
|
+
|
681
|
+
response.merge!(parse_direct_response_elements(response))
|
682
|
+
|
683
|
+
response
|
684
|
+
end
|
685
|
+
|
686
|
+
def success_from(action, response)
|
687
|
+
if action == :cim_store
|
688
|
+
response[:result_code] == "Ok"
|
689
|
+
else
|
690
|
+
response[:response_code] == APPROVED && TRANSACTION_ALREADY_ACTIONED.exclude?(response[:response_reason_code])
|
691
|
+
end
|
473
692
|
end
|
474
693
|
|
475
|
-
def message_from(response, avs_result, cvv_result)
|
694
|
+
def message_from(action, response, avs_result, cvv_result)
|
476
695
|
if response[:response_code] == DECLINED
|
477
696
|
if CARD_CODE_ERRORS.include?(cvv_result.code)
|
478
697
|
return cvv_result.message
|
@@ -481,30 +700,97 @@ module ActiveMerchant #:nodoc:
|
|
481
700
|
end
|
482
701
|
end
|
483
702
|
|
484
|
-
response[:response_reason_text]
|
703
|
+
response[:response_reason_text] || response[:message_text]
|
485
704
|
end
|
486
705
|
|
487
|
-
def authorization_from(response)
|
488
|
-
|
706
|
+
def authorization_from(action, response)
|
707
|
+
if action == :cim_store
|
708
|
+
[response[:customer_profile_id], response[:customer_payment_profile_id], action].join("#")
|
709
|
+
else
|
710
|
+
[response[:transaction_id], response[:account_number], action].join("#")
|
711
|
+
end
|
489
712
|
end
|
490
713
|
|
491
714
|
def split_authorization(authorization)
|
492
|
-
|
493
|
-
|
715
|
+
authorization.split("#")
|
716
|
+
end
|
717
|
+
|
718
|
+
def transaction_id_from(authorization)
|
719
|
+
transaction_id, _, _ = split_authorization(authorization)
|
720
|
+
transaction_id
|
494
721
|
end
|
495
722
|
|
496
723
|
def fraud_review?(response)
|
497
724
|
(response[:response_code] == FRAUD_REVIEW)
|
498
725
|
end
|
499
726
|
|
500
|
-
|
501
727
|
def using_live_gateway_in_test_mode?(response)
|
502
728
|
!test? && response[:test_request] == "1"
|
503
729
|
end
|
504
730
|
|
505
731
|
def map_error_code(response_code, response_reason_code)
|
506
|
-
STANDARD_ERROR_CODE_MAPPING[response_code
|
732
|
+
STANDARD_ERROR_CODE_MAPPING["#{response_code}#{response_reason_code}"]
|
733
|
+
end
|
734
|
+
|
735
|
+
def auth_was_for_cim?(authorization)
|
736
|
+
_, _, action = split_authorization(authorization)
|
737
|
+
action && is_cim_action?(action)
|
738
|
+
end
|
739
|
+
|
740
|
+
def parse_direct_response_elements(response)
|
741
|
+
params = response[:direct_response]
|
742
|
+
return {} unless params
|
743
|
+
|
744
|
+
parts = params.split(',')
|
745
|
+
{
|
746
|
+
response_code: parts[0].to_i,
|
747
|
+
response_subcode: parts[1],
|
748
|
+
response_reason_code: parts[2],
|
749
|
+
response_reason_text: parts[3],
|
750
|
+
approval_code: parts[4],
|
751
|
+
avs_result_code: parts[5],
|
752
|
+
transaction_id: parts[6],
|
753
|
+
invoice_number: parts[7],
|
754
|
+
order_description: parts[8],
|
755
|
+
amount: parts[9],
|
756
|
+
method: parts[10],
|
757
|
+
transaction_type: parts[11],
|
758
|
+
customer_id: parts[12],
|
759
|
+
first_name: parts[13],
|
760
|
+
last_name: parts[14],
|
761
|
+
company: parts[15],
|
762
|
+
address: parts[16],
|
763
|
+
city: parts[17],
|
764
|
+
state: parts[18],
|
765
|
+
zip_code: parts[19],
|
766
|
+
country: parts[20],
|
767
|
+
phone: parts[21],
|
768
|
+
fax: parts[22],
|
769
|
+
email_address: parts[23],
|
770
|
+
ship_to_first_name: parts[24],
|
771
|
+
ship_to_last_name: parts[25],
|
772
|
+
ship_to_company: parts[26],
|
773
|
+
ship_to_address: parts[27],
|
774
|
+
ship_to_city: parts[28],
|
775
|
+
ship_to_state: parts[29],
|
776
|
+
ship_to_zip_code: parts[30],
|
777
|
+
ship_to_country: parts[31],
|
778
|
+
tax: parts[32],
|
779
|
+
duty: parts[33],
|
780
|
+
freight: parts[34],
|
781
|
+
tax_exempt: parts[35],
|
782
|
+
purchase_order_number: parts[36],
|
783
|
+
md5_hash: parts[37],
|
784
|
+
card_code: parts[38],
|
785
|
+
cardholder_authentication_verification_response: parts[39],
|
786
|
+
account_number: parts[50] || '',
|
787
|
+
card_type: parts[51] || '',
|
788
|
+
split_tender_id: parts[52] || '',
|
789
|
+
requested_amount: parts[53] || '',
|
790
|
+
balance_on_card: parts[54] || '',
|
791
|
+
}
|
507
792
|
end
|
793
|
+
|
508
794
|
end
|
509
795
|
end
|
510
796
|
end
|