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.
Files changed (58) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG +19 -0
  3. data/CONTRIBUTORS +14 -2
  4. data/README.rdoc +2 -0
  5. data/lib/active_merchant/billing/credit_card.rb +4 -4
  6. data/lib/active_merchant/billing/gateway.rb +1 -1
  7. data/lib/active_merchant/billing/gateways/authorize_net.rb +10 -5
  8. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +133 -11
  9. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +1 -1
  10. data/lib/active_merchant/billing/gateways/beanstream.rb +39 -2
  11. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +64 -26
  12. data/lib/active_merchant/billing/gateways/bogus.rb +21 -3
  13. data/lib/active_merchant/billing/gateways/cyber_source.rb +5 -1
  14. data/lib/active_merchant/billing/gateways/data_cash.rb +1 -1
  15. data/lib/active_merchant/billing/gateways/efsnet.rb +1 -1
  16. data/lib/active_merchant/billing/gateways/epay.rb +5 -1
  17. data/lib/active_merchant/billing/gateways/eway.rb +4 -0
  18. data/lib/active_merchant/billing/gateways/eway_managed.rb +231 -0
  19. data/lib/active_merchant/billing/gateways/federated_canada.rb +6 -7
  20. data/lib/active_merchant/billing/gateways/first_pay.rb +7 -2
  21. data/lib/active_merchant/billing/gateways/iridium.rb +1 -1
  22. data/lib/active_merchant/billing/gateways/jetpay.rb +5 -2
  23. data/lib/active_merchant/billing/gateways/linkpoint.rb +6 -1
  24. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +6 -4
  25. data/lib/active_merchant/billing/gateways/merchant_ware.rb +1 -1
  26. data/lib/active_merchant/billing/gateways/moneris.rb +1 -1
  27. data/lib/active_merchant/billing/gateways/netaxept.rb +6 -1
  28. data/lib/active_merchant/billing/gateways/ogone.rb +1 -1
  29. data/lib/active_merchant/billing/gateways/orbital.rb +317 -0
  30. data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +46 -0
  31. data/lib/active_merchant/billing/gateways/paybox_direct.rb +1 -1
  32. data/lib/active_merchant/billing/gateways/payflow.rb +1 -1
  33. data/lib/active_merchant/billing/gateways/payflow_express.rb +6 -1
  34. data/lib/active_merchant/billing/gateways/payment_express.rb +6 -1
  35. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +7 -2
  36. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +11 -7
  37. data/lib/active_merchant/billing/gateways/plugnpay.rb +1 -1
  38. data/lib/active_merchant/billing/gateways/psigate.rb +1 -1
  39. data/lib/active_merchant/billing/gateways/qbms.rb +1 -1
  40. data/lib/active_merchant/billing/gateways/quantum.rb +6 -1
  41. data/lib/active_merchant/billing/gateways/quickpay.rb +6 -1
  42. data/lib/active_merchant/billing/gateways/realex.rb +196 -72
  43. data/lib/active_merchant/billing/gateways/sage_pay.rb +7 -2
  44. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +38 -2
  45. data/lib/active_merchant/billing/gateways/smart_ps.rb +2 -2
  46. data/lib/active_merchant/billing/gateways/trust_commerce.rb +7 -2
  47. data/lib/active_merchant/billing/gateways/verifi.rb +1 -1
  48. data/lib/active_merchant/billing/gateways/worldpay.rb +280 -0
  49. data/lib/active_merchant/billing/integrations/moneybookers/helper.rb +0 -1
  50. data/lib/active_merchant/billing/integrations/return.rb +6 -1
  51. data/lib/active_merchant/billing/integrations/sage_pay_form/notification.rb +6 -0
  52. data/lib/active_merchant/billing/integrations/sage_pay_form/return.rb +5 -1
  53. data/lib/active_merchant/common/connection.rb +15 -0
  54. data/lib/active_merchant/common/posts_data.rb +2 -0
  55. data/lib/active_merchant/common/utils.rb +4 -0
  56. data/lib/active_merchant/version.rb +1 -1
  57. metadata +8 -4
  58. metadata.gz.sig +0 -0
@@ -105,8 +105,8 @@ module ActiveMerchant #:nodoc:
105
105
  commit(action, post)
106
106
  end
107
107
 
108
- # Crediting requires a new order_id to passed in, as well as a description
109
- def credit(money, identification, options = {})
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
- :credit => 4
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', options.delete(:amount), post)
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
- # credit() allows you to return money to a card that was previously billed. You need to supply the amount, in cents or a money object,
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 credit(money, identification, options = {})
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
- warn CREDIT_DEPRECATION_MESSAGE
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
@@ -40,7 +40,6 @@ module ActiveMerchant #:nodoc:
40
40
 
41
41
  add_field('merchant_fields', 'platform')
42
42
  add_field('platform', application_id)
43
- puts @fields.inspect
44
43
  end
45
44
  end
46
45
  end
@@ -14,7 +14,12 @@ module ActiveMerchant #:nodoc:
14
14
  def success?
15
15
  true
16
16
  end
17
-
17
+
18
+ # Not cancelled by default. Overridden in the child class.
19
+ def cancelled?
20
+ false
21
+ end
22
+
18
23
  def message
19
24
 
20
25
  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'