activemerchant 1.13.0 → 1.14.0

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