activemerchant 1.13.0 → 1.14.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.
- data.tar.gz.sig +0 -0
- data/CHANGELOG +19 -0
- data/CONTRIBUTORS +14 -2
- data/README.rdoc +2 -0
- data/lib/active_merchant/billing/credit_card.rb +4 -4
- data/lib/active_merchant/billing/gateway.rb +1 -1
- data/lib/active_merchant/billing/gateways/authorize_net.rb +10 -5
- data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +133 -11
- data/lib/active_merchant/billing/gateways/barclays_epdq.rb +1 -1
- data/lib/active_merchant/billing/gateways/beanstream.rb +39 -2
- data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +64 -26
- data/lib/active_merchant/billing/gateways/bogus.rb +21 -3
- data/lib/active_merchant/billing/gateways/cyber_source.rb +5 -1
- data/lib/active_merchant/billing/gateways/data_cash.rb +1 -1
- data/lib/active_merchant/billing/gateways/efsnet.rb +1 -1
- data/lib/active_merchant/billing/gateways/epay.rb +5 -1
- data/lib/active_merchant/billing/gateways/eway.rb +4 -0
- data/lib/active_merchant/billing/gateways/eway_managed.rb +231 -0
- data/lib/active_merchant/billing/gateways/federated_canada.rb +6 -7
- data/lib/active_merchant/billing/gateways/first_pay.rb +7 -2
- data/lib/active_merchant/billing/gateways/iridium.rb +1 -1
- data/lib/active_merchant/billing/gateways/jetpay.rb +5 -2
- data/lib/active_merchant/billing/gateways/linkpoint.rb +6 -1
- data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +6 -4
- data/lib/active_merchant/billing/gateways/merchant_ware.rb +1 -1
- data/lib/active_merchant/billing/gateways/moneris.rb +1 -1
- data/lib/active_merchant/billing/gateways/netaxept.rb +6 -1
- data/lib/active_merchant/billing/gateways/ogone.rb +1 -1
- data/lib/active_merchant/billing/gateways/orbital.rb +317 -0
- data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +46 -0
- data/lib/active_merchant/billing/gateways/paybox_direct.rb +1 -1
- data/lib/active_merchant/billing/gateways/payflow.rb +1 -1
- data/lib/active_merchant/billing/gateways/payflow_express.rb +6 -1
- data/lib/active_merchant/billing/gateways/payment_express.rb +6 -1
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +7 -2
- data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +11 -7
- data/lib/active_merchant/billing/gateways/plugnpay.rb +1 -1
- data/lib/active_merchant/billing/gateways/psigate.rb +1 -1
- data/lib/active_merchant/billing/gateways/qbms.rb +1 -1
- data/lib/active_merchant/billing/gateways/quantum.rb +6 -1
- data/lib/active_merchant/billing/gateways/quickpay.rb +6 -1
- data/lib/active_merchant/billing/gateways/realex.rb +196 -72
- data/lib/active_merchant/billing/gateways/sage_pay.rb +7 -2
- data/lib/active_merchant/billing/gateways/secure_pay_au.rb +38 -2
- data/lib/active_merchant/billing/gateways/smart_ps.rb +2 -2
- data/lib/active_merchant/billing/gateways/trust_commerce.rb +7 -2
- data/lib/active_merchant/billing/gateways/verifi.rb +1 -1
- data/lib/active_merchant/billing/gateways/worldpay.rb +280 -0
- data/lib/active_merchant/billing/integrations/moneybookers/helper.rb +0 -1
- data/lib/active_merchant/billing/integrations/return.rb +6 -1
- data/lib/active_merchant/billing/integrations/sage_pay_form/notification.rb +6 -0
- data/lib/active_merchant/billing/integrations/sage_pay_form/return.rb +5 -1
- data/lib/active_merchant/common/connection.rb +15 -0
- data/lib/active_merchant/common/posts_data.rb +2 -0
- data/lib/active_merchant/common/utils.rb +4 -0
- data/lib/active_merchant/version.rb +1 -1
- metadata +8 -4
- metadata.gz.sig +0 -0
@@ -105,8 +105,8 @@ module ActiveMerchant #:nodoc:
|
|
105
105
|
commit(action, post)
|
106
106
|
end
|
107
107
|
|
108
|
-
#
|
109
|
-
def
|
108
|
+
# Refunding requires a new order_id to passed in, as well as a description
|
109
|
+
def refund(money, identification, options = {})
|
110
110
|
requires!(options, :order_id, :description)
|
111
111
|
|
112
112
|
post = {}
|
@@ -117,6 +117,11 @@ module ActiveMerchant #:nodoc:
|
|
117
117
|
|
118
118
|
commit(:credit, post)
|
119
119
|
end
|
120
|
+
|
121
|
+
def credit(money, identification, options = {})
|
122
|
+
deprecated CREDIT_DEPRECATION_MESSAGE
|
123
|
+
refund(money, identification, options)
|
124
|
+
end
|
120
125
|
|
121
126
|
private
|
122
127
|
def add_reference(post, identification)
|
@@ -33,7 +33,7 @@ module ActiveMerchant #:nodoc:
|
|
33
33
|
:authorization => 10,
|
34
34
|
:capture => 11,
|
35
35
|
:void => 6,
|
36
|
-
:
|
36
|
+
:refund => 4
|
37
37
|
}
|
38
38
|
|
39
39
|
SUCCESS_CODES = [ '00', '08', '11', '16', '77' ]
|
@@ -49,9 +49,32 @@ module ActiveMerchant #:nodoc:
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def purchase(money, credit_card, options = {})
|
52
|
+
requires!(options, :order_id)
|
52
53
|
commit :purchase, build_purchase_request(money, credit_card, options)
|
53
54
|
end
|
54
55
|
|
56
|
+
def authorize(money, credit_card, options = {})
|
57
|
+
requires!(options, :order_id)
|
58
|
+
commit :authorization, build_purchase_request(money, credit_card, options)
|
59
|
+
end
|
60
|
+
|
61
|
+
def capture(money, reference)
|
62
|
+
commit :capture, build_reference_request(money, reference)
|
63
|
+
end
|
64
|
+
|
65
|
+
def refund(money, reference)
|
66
|
+
commit :refund, build_reference_request(money, reference)
|
67
|
+
end
|
68
|
+
|
69
|
+
def credit(money, reference)
|
70
|
+
deprecated CREDIT_DEPRECATION_MESSAGE
|
71
|
+
refund(money, reference)
|
72
|
+
end
|
73
|
+
|
74
|
+
def void(reference)
|
75
|
+
commit :void, build_reference_request(nil, reference)
|
76
|
+
end
|
77
|
+
|
55
78
|
private
|
56
79
|
|
57
80
|
def build_purchase_request(money, credit_card, options)
|
@@ -70,6 +93,19 @@ module ActiveMerchant #:nodoc:
|
|
70
93
|
xml.target!
|
71
94
|
end
|
72
95
|
|
96
|
+
def build_reference_request(money, reference)
|
97
|
+
xml = Builder::XmlMarkup.new
|
98
|
+
|
99
|
+
transaction_id, order_id, preauth_id, original_amount = reference.split("*")
|
100
|
+
xml.tag! 'amount', (money ? amount(money) : original_amount)
|
101
|
+
xml.tag! 'currency', options[:currency] || currency(money)
|
102
|
+
xml.tag! 'txnID', transaction_id
|
103
|
+
xml.tag! 'purchaseOrderNo', order_id
|
104
|
+
xml.tag! 'preauthID', preauth_id
|
105
|
+
|
106
|
+
xml.target!
|
107
|
+
end
|
108
|
+
|
73
109
|
def build_request(action, body)
|
74
110
|
xml = Builder::XmlMarkup.new
|
75
111
|
xml.instruct!
|
@@ -115,7 +151,7 @@ module ActiveMerchant #:nodoc:
|
|
115
151
|
end
|
116
152
|
|
117
153
|
def authorization_from(response)
|
118
|
-
response[:txn_id]
|
154
|
+
[response[:txn_id], response[:purchase_order_no], response[:preauth_id], response[:amount]].join('*')
|
119
155
|
end
|
120
156
|
|
121
157
|
def message_from(response)
|
@@ -69,10 +69,10 @@ module ActiveMerchant #:nodoc:
|
|
69
69
|
commit('credit', money, post)
|
70
70
|
end
|
71
71
|
|
72
|
-
def refund(auth, options = {})
|
72
|
+
def refund(money, auth, options = {})
|
73
73
|
post = {}
|
74
74
|
add_transaction(post, auth)
|
75
|
-
commit('refund',
|
75
|
+
commit('refund', money, post)
|
76
76
|
end
|
77
77
|
|
78
78
|
|
@@ -187,9 +187,9 @@ module ActiveMerchant #:nodoc:
|
|
187
187
|
commit('postauth', parameters)
|
188
188
|
end
|
189
189
|
|
190
|
-
#
|
190
|
+
# refund() allows you to return money to a card that was previously billed. You need to supply the amount, in cents or a money object,
|
191
191
|
# that you want to refund, and a TC transid for the transaction that you are refunding.
|
192
|
-
def
|
192
|
+
def refund(money, identification, options = {})
|
193
193
|
parameters = {
|
194
194
|
:amount => amount(money),
|
195
195
|
:transid => identification
|
@@ -197,6 +197,11 @@ module ActiveMerchant #:nodoc:
|
|
197
197
|
|
198
198
|
commit('credit', parameters)
|
199
199
|
end
|
200
|
+
|
201
|
+
def credit(money, identification, options = {})
|
202
|
+
deprecated CREDIT_DEPRECATION_MESSAGE
|
203
|
+
refund(money, identification, options)
|
204
|
+
end
|
200
205
|
|
201
206
|
# void() clears an existing authorization and releases the reserved fund
|
202
207
|
# s back to the cardholder. The TC API refers to this transaction as a
|
@@ -87,7 +87,7 @@ module ActiveMerchant #:nodoc:
|
|
87
87
|
|
88
88
|
def credit(money, credit_card_or_authorization, options = {})
|
89
89
|
if credit_card_or_authorization.is_a?(String)
|
90
|
-
|
90
|
+
deprecated CREDIT_DEPRECATION_MESSAGE
|
91
91
|
refund(money, credit_card_or_authorization, options)
|
92
92
|
else
|
93
93
|
sale_authorization_or_credit_template(:credit, money, credit_card_or_authorization, options)
|
@@ -0,0 +1,280 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
3
|
+
class WorldpayGateway < Gateway
|
4
|
+
TEST_URL = 'https://secure-test.wp3.rbsworldpay.com/jsp/merchant/xml/paymentService.jsp'
|
5
|
+
LIVE_URL = 'https://secure.wp3.rbsworldpay.com/jsp/merchant/xml/paymentService.jsp'
|
6
|
+
|
7
|
+
self.default_currency = 'GBP'
|
8
|
+
self.money_format = :cents
|
9
|
+
self.supported_countries = ['HK', 'US', 'GB', 'AU']
|
10
|
+
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
11
|
+
self.homepage_url = 'http://www.worldpay.com/'
|
12
|
+
self.display_name = 'WorldPay'
|
13
|
+
|
14
|
+
CARD_CODES = {
|
15
|
+
'visa' => 'VISA-SSL',
|
16
|
+
'master' => 'ECMC-SSL',
|
17
|
+
'discover' => 'DISCOVER-SSL',
|
18
|
+
'american_express' => 'AMEX-SSL',
|
19
|
+
}
|
20
|
+
|
21
|
+
def initialize(options = {})
|
22
|
+
requires!(options, :login, :password)
|
23
|
+
@options = options
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
def purchase(money, payment_method, options = {})
|
28
|
+
response = MultiResponse.new
|
29
|
+
response << authorize(money, payment_method, options)
|
30
|
+
response << capture(money, response.authorization, :authorization_validated => true) if response.success?
|
31
|
+
response
|
32
|
+
end
|
33
|
+
|
34
|
+
def authorize(money, payment_method, options = {})
|
35
|
+
requires!(options, :order_id)
|
36
|
+
commit 'authorize', build_authorization_request(money, payment_method, options)
|
37
|
+
end
|
38
|
+
|
39
|
+
def capture(money, authorization, options = {})
|
40
|
+
response = MultiResponse.new
|
41
|
+
response << inquire(authorization, options) unless options[:authorization_validated]
|
42
|
+
response << commit('capture', build_capture_request(money, authorization, options)) if response.success?
|
43
|
+
response
|
44
|
+
end
|
45
|
+
|
46
|
+
def void(authorization, options = {})
|
47
|
+
response = MultiResponse.new
|
48
|
+
response << inquire(authorization, options)
|
49
|
+
response << commit('cancel', build_void_request(authorization, options)) if response.success?
|
50
|
+
response
|
51
|
+
end
|
52
|
+
|
53
|
+
def refund(money, authorization, options = {})
|
54
|
+
response = MultiResponse.new
|
55
|
+
response << inquire(authorization, options)
|
56
|
+
response << commit('refund', build_refund_request(money, authorization, options)) if response.success?
|
57
|
+
response
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def inquire(authorization, options={})
|
63
|
+
commit 'inquiry', build_order_inquiry_request(authorization, options)
|
64
|
+
end
|
65
|
+
|
66
|
+
def build_request
|
67
|
+
xml = Builder::XmlMarkup.new :indent => 2
|
68
|
+
xml.instruct!
|
69
|
+
xml.declare! :DOCTYPE, :paymentService, :PUBLIC, "-//WorldPay//DTD WorldPay PaymentService v1//EN", "http://dtd.wp3.rbsworldpay.com/paymentService_v1.dtd"
|
70
|
+
xml.tag! 'paymentService', 'version' => "1.4", 'merchantCode' => @options[:login] do
|
71
|
+
yield xml
|
72
|
+
end
|
73
|
+
xml.target!
|
74
|
+
end
|
75
|
+
|
76
|
+
def build_order_modify_request(authorization)
|
77
|
+
build_request do |xml|
|
78
|
+
xml.tag! 'modify' do
|
79
|
+
xml.tag! 'orderModification', 'orderCode' => authorization do
|
80
|
+
yield xml
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def build_order_inquiry_request(authorization, options)
|
87
|
+
build_request do |xml|
|
88
|
+
xml.tag! 'inquiry' do
|
89
|
+
xml.tag! 'orderInquiry', 'orderCode' => authorization
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def build_authorization_request(money, payment_method, options)
|
95
|
+
build_request do |xml|
|
96
|
+
xml.tag! 'submit' do
|
97
|
+
xml.tag! 'order', {'orderCode' => options[:order_id], 'installationId' => @options[:inst_id]}.reject{|_,v| !v} do
|
98
|
+
xml.description(options[:description].blank? ? "Purchase" : options[:description])
|
99
|
+
add_amount(xml, money, options)
|
100
|
+
if options[:order_content]
|
101
|
+
xml.tag! 'orderContent' do
|
102
|
+
xml.cdata! options[:order_content]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
add_payment_method(xml, money, payment_method, options)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def build_capture_request(money, authorization, options)
|
112
|
+
build_order_modify_request(authorization) do |xml|
|
113
|
+
xml.tag! 'capture' do
|
114
|
+
time = Time.now
|
115
|
+
xml.tag! 'date', 'dayOfMonth' => time.day, 'month' => time.month, 'year'=> time.year
|
116
|
+
add_amount(xml, money, options)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def build_void_request(authorization, options)
|
122
|
+
build_order_modify_request(authorization) do |xml|
|
123
|
+
xml.tag! 'cancel'
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def build_refund_request(money, authorization, options)
|
128
|
+
build_order_modify_request(authorization) do |xml|
|
129
|
+
xml.tag! 'refund' do
|
130
|
+
add_amount(xml, money, options)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def add_amount(xml, money, options)
|
136
|
+
xml.tag! 'amount',
|
137
|
+
:value => amount(money),
|
138
|
+
'currencyCode' => (options[:currency] || currency(money)),
|
139
|
+
'exponent' => 2
|
140
|
+
end
|
141
|
+
|
142
|
+
def add_payment_method(xml, amount, payment_method, options)
|
143
|
+
if payment_method.is_a?(String)
|
144
|
+
xml.tag! 'payAsOrder', 'orderCode' => payment_method do
|
145
|
+
add_amount(xml, amount, options)
|
146
|
+
end
|
147
|
+
else
|
148
|
+
xml.tag! 'paymentDetails' do
|
149
|
+
xml.tag! CARD_CODES[card_brand(payment_method)] do
|
150
|
+
xml.tag! 'cardNumber', payment_method.number
|
151
|
+
xml.tag! 'expiryDate' do
|
152
|
+
xml.tag! 'date', 'month' => format(payment_method.month, :two_digits), 'year' => format(payment_method.year, :four_digits)
|
153
|
+
end
|
154
|
+
|
155
|
+
xml.tag! 'cardHolderName', payment_method.name
|
156
|
+
xml.tag! 'cvc', payment_method.verification_value
|
157
|
+
|
158
|
+
add_address(xml, 'cardAddress', (options[:billing_address] || options[:address]))
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def add_address(xml, element, address)
|
165
|
+
return if address.nil?
|
166
|
+
|
167
|
+
xml.tag! element do
|
168
|
+
xml.tag! 'address' do
|
169
|
+
if m = /^\s*([^\s]+)\s+(.+)$/.match(address[:name])
|
170
|
+
xml.tag! 'firstName', m[1]
|
171
|
+
xml.tag! 'lastName', m[2]
|
172
|
+
end
|
173
|
+
if m = /^\s*(\d+)\s+(.+)$/.match(address[:address1])
|
174
|
+
xml.tag! 'street', m[2]
|
175
|
+
house_number = m[1]
|
176
|
+
else
|
177
|
+
xml.tag! 'street', address[:address1]
|
178
|
+
end
|
179
|
+
xml.tag! 'houseName', address[:address2] if address[:address2]
|
180
|
+
xml.tag! 'houseNumber', house_number if house_number.present?
|
181
|
+
xml.tag! 'postalCode', (address[:zip].present? ? address[:zip] : "0000")
|
182
|
+
xml.tag! 'city', address[:city] if address[:city]
|
183
|
+
xml.tag! 'state', (address[:state].present? ? address[:state] : 'N/A')
|
184
|
+
xml.tag! 'countryCode', address[:country]
|
185
|
+
xml.tag! 'telephoneNumber', address[:phone] if address[:phone]
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def parse(action, xml)
|
191
|
+
parse_element({:action => action}, REXML::Document.new(xml))
|
192
|
+
end
|
193
|
+
|
194
|
+
def parse_element(raw, node)
|
195
|
+
node.attributes.each do |k, v|
|
196
|
+
raw["#{node.name.underscore}_#{k.underscore}".to_sym] = v
|
197
|
+
end
|
198
|
+
if node.has_elements?
|
199
|
+
raw[node.name.underscore.to_sym] = true unless node.name.blank?
|
200
|
+
node.elements.each{|e| parse_element(raw, e) }
|
201
|
+
else
|
202
|
+
raw[node.name.underscore.to_sym] = node.text unless node.text.nil?
|
203
|
+
end
|
204
|
+
raw
|
205
|
+
end
|
206
|
+
|
207
|
+
def commit(action, request)
|
208
|
+
xmr = ssl_post((test? ? TEST_URL : LIVE_URL),
|
209
|
+
request,
|
210
|
+
'Content-Type' => 'text/xml',
|
211
|
+
'Authorization' => encoded_credentials)
|
212
|
+
|
213
|
+
raw = parse(action, xmr)
|
214
|
+
|
215
|
+
Response.new(
|
216
|
+
success_from(raw),
|
217
|
+
message_from(raw),
|
218
|
+
raw,
|
219
|
+
:authorization => authorization_from(raw),
|
220
|
+
:test => test?)
|
221
|
+
|
222
|
+
rescue ActiveMerchant::ResponseError => e
|
223
|
+
if e.response.code.to_s == "401"
|
224
|
+
return Response.new(false, "Invalid credentials", {}, :test => test?)
|
225
|
+
else
|
226
|
+
raise e
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def success_from(raw)
|
231
|
+
(raw[:last_event] == "AUTHORISED" ||
|
232
|
+
raw[:ok].present?)
|
233
|
+
end
|
234
|
+
|
235
|
+
def message_from(raw)
|
236
|
+
(raw[:iso8583_return_code_description] ||
|
237
|
+
raw[:error] ||
|
238
|
+
"SUCCESS")
|
239
|
+
end
|
240
|
+
|
241
|
+
def authorization_from(raw)
|
242
|
+
pair = raw.detect{|k,v| k.to_s =~ /_order_code$/}
|
243
|
+
(pair ? pair.last : nil)
|
244
|
+
end
|
245
|
+
|
246
|
+
def encoded_credentials
|
247
|
+
credentials = "#{@options[:login]}:#{@options[:password]}"
|
248
|
+
"Basic #{[credentials].pack('m').strip}"
|
249
|
+
end
|
250
|
+
|
251
|
+
class MultiResponse < Response
|
252
|
+
attr_reader :responses
|
253
|
+
|
254
|
+
def initialize
|
255
|
+
@responses = []
|
256
|
+
end
|
257
|
+
|
258
|
+
def <<(response)
|
259
|
+
if response.is_a?(MultiResponse)
|
260
|
+
response.responses.each{|r| @responses << r}
|
261
|
+
else
|
262
|
+
@responses << response
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def success?
|
267
|
+
@responses.all?{|r| r.success?}
|
268
|
+
end
|
269
|
+
|
270
|
+
%w(params message test authorization avs_result cvv_result test? fraud_review?).each do |m|
|
271
|
+
class_eval %(
|
272
|
+
def #{m}
|
273
|
+
@responses.last.#{m}
|
274
|
+
end
|
275
|
+
)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
@@ -19,6 +19,12 @@ module ActiveMerchant #:nodoc:
|
|
19
19
|
status_code == 'OK'
|
20
20
|
end
|
21
21
|
|
22
|
+
# Was the transaction cancelled?
|
23
|
+
# Unfortunately, we can't distinguish "user abort" from "idle too long".
|
24
|
+
def cancelled?
|
25
|
+
status_code == 'ABORT'
|
26
|
+
end
|
27
|
+
|
22
28
|
# Text version of #complete?, since we don't support Pending.
|
23
29
|
def status
|
24
30
|
complete? ? 'Completed' : 'Failed'
|