activemerchant 1.25.0 → 1.26.0

Sign up to get free protection for your applications and to get access to all the features.
data.tar.gz.sig CHANGED
Binary file
data/CHANGELOG CHANGED
@@ -1,5 +1,16 @@
1
1
  = ActiveMerchant CHANGELOG
2
2
 
3
+ == Version 1.26.0 (July 6, 2012)
4
+
5
+ * Orbital gateway: fix broken requests by ensuring the order of XML elements matches their DTD [Soleone]
6
+ * CyberSource gateway: clean up formatting [ntalbott]
7
+ * Netbilling gateway: Add refund/credit/void support [ntalbott]
8
+ * Add PayGate XML gateway [rubyisbeautiful]
9
+ * Add PayWay gateway [BenZhang]
10
+ * PayWay gateway: Tweaks to make more ActiveMerchant like [ntalbott]
11
+ * Netbilling gateway: Fix error handling [ntalbott]
12
+ * Netbilling gateway: Add refund/credit/void support [zenom, ntalbott]
13
+
3
14
  == Version 1.25.0 (July 3, 2012)
4
15
 
5
16
  * eWAY gateway: Add support for Diners Club cards [Soleone]
data/CONTRIBUTORS CHANGED
@@ -317,3 +317,11 @@ Metrics Global gateway (June 2012)
317
317
  EasyPay integration (July 2012)
318
318
 
319
319
  * Vasiliy Ermolovich (nashby)
320
+
321
+ PayGateXML gateway (July 2012)
322
+
323
+ * bryan (rubyisbeautiful)
324
+
325
+ PayWay gateway (July 2012)
326
+
327
+ * Ben Zhang (BenZhang)
data/README.md CHANGED
@@ -45,18 +45,18 @@ credit card details.
45
45
 
46
46
  require 'rubygems'
47
47
  require 'active_merchant'
48
-
48
+
49
49
  # Use the TrustCommerce test servers
50
50
  ActiveMerchant::Billing::Base.mode = :test
51
51
 
52
52
  gateway = ActiveMerchant::Billing::TrustCommerceGateway.new(
53
53
  :login => 'TestMerchant',
54
54
  :password => 'password')
55
-
55
+
56
56
  # ActiveMerchant accepts all amounts as Integer values in cents
57
57
  amount = 1000 # $10.00
58
-
59
- # The card verification value is also known as CVV2, CVC2, or CID
58
+
59
+ # The card verification value is also known as CVV2, CVC2, or CID
60
60
  credit_card = ActiveMerchant::Billing::CreditCard.new(
61
61
  :first_name => 'Bob',
62
62
  :last_name => 'Bobsen',
@@ -64,16 +64,16 @@ credit card details.
64
64
  :month => '8',
65
65
  :year => '2012',
66
66
  :verification_value => '123')
67
-
67
+
68
68
  # Validating the card automatically detects the card type
69
69
  if credit_card.valid?
70
70
  # Capture $10 from the credit card
71
71
  response = gateway.purchase(amount, credit_card)
72
-
72
+
73
73
  if response.success?
74
74
  puts "Successfully charged $#{sprintf("%.2f", amount / 100)} to the credit card #{credit_card.display_number}"
75
75
  else
76
- raise StandardError, response.message
76
+ raise StandardError, response.message
77
77
  end
78
78
  end
79
79
 
@@ -126,6 +126,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
126
126
  * [Optimal Payments](http://www.optimalpayments.com/) - CA, US, UK
127
127
  * [Orbital Paymentech](http://chasepaymentech.com/) - CA, US
128
128
  * [PayBox Direct](http://www.paybox.com) - FR
129
+ * [PayGate PayXML](http://paygate.co.za/) - US, ZA
129
130
  * [PayJunction](http://www.payjunction.com/) - US
130
131
  * [PaymentExpress](http://www.paymentexpress.com/) - AU, MY, NZ, SG, ZA, UK, US
131
132
  * [PayPal Express Checkout](https://www.paypal.com/cgi-bin/webscr?cmd=xpt/merchant/ExpressCheckoutIntro-outside) - US, CA, SG, AU
@@ -135,6 +136,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
135
136
  * [PayPal Express Checkout](https://www.paypal.com/cgi-bin/webscr?cmd=xpt/merchant/ExpressCheckoutIntro-outside) - US
136
137
  * [PayPal Website Payments Pro (US)](https://www.paypal.com/cgi-bin/webscr?cmd=_wp-pro-overview-outside) - US
137
138
  * [PaySecure](http://www.commsecure.com.au/paysecure.shtml) - AU
139
+ * [PayWay](https://www.payway.com.au) - AU
138
140
  * [Plug'n Pay](http://www.plugnpay.com/) - US
139
141
  * [Psigate](http://www.psigate.com/) - CA
140
142
  * [PSL Payment Solutions](http://www.paymentsolutionsltd.com/) - UK
@@ -1,39 +1,48 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
- # See the remote and mocked unit test files for example usage. Pay special attention to the contents of the options hash.
3
+ # See the remote and mocked unit test files for example usage. Pay special
4
+ # attention to the contents of the options hash.
4
5
  #
5
- # Initial setup instructions can be found in http://cybersource.com/support_center/implementation/downloads/soap_api/SOAP_toolkits.pdf
6
- #
7
- # Debugging
8
- # If you experience an issue with this gateway be sure to examine the transaction information from a general transaction search inside the CyberSource Business
9
- # Center for the full error messages including field names.
6
+ # Initial setup instructions can be found in
7
+ # http://cybersource.com/support_center/implementation/downloads/soap_api/SOAP_toolkits.pdf
8
+ #
9
+ # Debugging
10
+ # If you experience an issue with this gateway be sure to examine the
11
+ # transaction information from a general transaction search inside the
12
+ # CyberSource Business Center for the full error messages including field
13
+ # names.
10
14
  #
11
15
  # Important Notes
12
- # * AVS and CVV only work against the production server. You will always get back X for AVS and no response for CVV against the test server.
13
- # * Nexus is the list of states or provinces where you have a physical presence. Nexus is used to calculate tax. Leave blank to tax everyone.
14
- # * If you want to calculate VAT for overseas customers you must supply a registration number in the options hash as vat_reg_number.
15
- # * productCode is a value in the line_items hash that is used to tell CyberSource what kind of item you are selling. It is used when calculating tax/VAT.
16
+ # * AVS and CVV only work against the production server. You will always
17
+ # get back X for AVS and no response for CVV against the test server.
18
+ # * Nexus is the list of states or provinces where you have a physical
19
+ # presence. Nexus is used to calculate tax. Leave blank to tax everyone.
20
+ # * If you want to calculate VAT for overseas customers you must supply a
21
+ # registration number in the options hash as vat_reg_number.
22
+ # * productCode is a value in the line_items hash that is used to tell
23
+ # CyberSource what kind of item you are selling. It is used when
24
+ # calculating tax/VAT.
16
25
  # * All transactions use dollar values.
17
26
  class CyberSourceGateway < Gateway
18
27
  TEST_URL = 'https://ics2wstest.ic3.com/commerce/1.x/transactionProcessor'
19
28
  LIVE_URL = 'https://ics2ws.ic3.com/commerce/1.x/transactionProcessor'
20
29
 
21
30
  XSD_VERSION = "1.69"
22
-
31
+
23
32
  # visa, master, american_express, discover
24
33
  self.supported_cardtypes = [:visa, :master, :american_express, :discover]
25
34
  self.supported_countries = ['US']
26
35
  self.default_currency = 'USD'
27
36
  self.homepage_url = 'http://www.cybersource.com'
28
37
  self.display_name = 'CyberSource'
29
-
38
+
30
39
  # map credit card to the CyberSource expected representation
31
40
  @@credit_card_codes = {
32
41
  :visa => '001',
33
42
  :master => '002',
34
43
  :american_express => '003',
35
44
  :discover => '004'
36
- }
45
+ }
37
46
 
38
47
  # map response codes to something humans can read
39
48
  @@response_codes = {
@@ -45,17 +54,17 @@ module ActiveMerchant #:nodoc:
45
54
  :r152 => "The request was received, but a service timed out",
46
55
  :r200 => "The authorization request was approved by the issuing bank but declined by CyberSource because it did not pass the AVS check",
47
56
  :r201 => "The issuing bank has questions about the request",
48
- :r202 => "Expired card",
49
- :r203 => "General decline of the card",
50
- :r204 => "Insufficient funds in the account",
51
- :r205 => "Stolen or lost card",
52
- :r207 => "Issuing bank unavailable",
53
- :r208 => "Inactive card or card not authorized for card-not-present transactions",
54
- :r209 => "American Express Card Identifiction Digits (CID) did not match",
55
- :r210 => "The card has reached the credit limit",
56
- :r211 => "Invalid card verification number",
57
- :r221 => "The customer matched an entry on the processor's negative file",
58
- :r230 => "The authorization request was approved by the issuing bank but declined by CyberSource because it did not pass the card verification check",
57
+ :r202 => "Expired card",
58
+ :r203 => "General decline of the card",
59
+ :r204 => "Insufficient funds in the account",
60
+ :r205 => "Stolen or lost card",
61
+ :r207 => "Issuing bank unavailable",
62
+ :r208 => "Inactive card or card not authorized for card-not-present transactions",
63
+ :r209 => "American Express Card Identifiction Digits (CID) did not match",
64
+ :r210 => "The card has reached the credit limit",
65
+ :r211 => "Invalid card verification number",
66
+ :r221 => "The customer matched an entry on the processor's negative file",
67
+ :r230 => "The authorization request was approved by the issuing bank but declined by CyberSource because it did not pass the card verification check",
59
68
  :r231 => "Invalid account number",
60
69
  :r232 => "The card type is not accepted by the payment processor",
61
70
  :r233 => "General decline by the processor",
@@ -74,44 +83,48 @@ module ActiveMerchant #:nodoc:
74
83
  :r247 => "You requested a credit for a capture that was previously voided",
75
84
  :r250 => "The request was received, but a time-out occurred with the payment processor",
76
85
  :r254 => "Your CyberSource account is prohibited from processing stand-alone refunds",
77
- :r255 => "Your CyberSource account is not configured to process the service in the country you specified"
86
+ :r255 => "Your CyberSource account is not configured to process the service in the country you specified"
78
87
  }
79
88
 
80
- # These are the options that can be used when creating a new CyberSource Gateway object.
81
- #
82
- # :login => your username
89
+ # These are the options that can be used when creating a new CyberSource
90
+ # Gateway object.
83
91
  #
84
- # :password => the transaction key you generated in the Business Center
92
+ # :login => your username
93
+ #
94
+ # :password => the transaction key you generated in the Business Center
85
95
  #
86
96
  # :test => true sets the gateway to test mode
87
97
  #
88
- # :vat_reg_number => your VAT registration number
98
+ # :vat_reg_number => your VAT registration number
89
99
  #
90
- # :nexus => "WI CA QC" sets the states/provinces where you have a physical presense for tax purposes
100
+ # :nexus => "WI CA QC" sets the states/provinces where you have a physical
101
+ # presense for tax purposes
91
102
  #
92
- # :ignore_avs => true don't want to use AVS so continue processing even if AVS would have failed
103
+ # :ignore_avs => true don't want to use AVS so continue processing even
104
+ # if AVS would have failed
93
105
  #
94
- # :ignore_cvv => true don't want to use CVV so continue processing even if CVV would have failed
106
+ # :ignore_cvv => true don't want to use CVV so continue processing even
107
+ # if CVV would have failed
95
108
  def initialize(options = {})
96
109
  requires!(options, :login, :password)
97
110
  @options = options
98
111
  super
99
- end
112
+ end
100
113
 
101
114
  # Should run against the test servers or not?
102
115
  def test?
103
116
  @options[:test] || Base.gateway_mode == :test
104
117
  end
105
-
106
- # Request an authorization for an amount from CyberSource
118
+
119
+ # Request an authorization for an amount from CyberSource
107
120
  #
108
- # You must supply an :order_id in the options hash
121
+ # You must supply an :order_id in the options hash
109
122
  def authorize(money, creditcard_or_reference, options = {})
110
123
  requires!(options, :order_id)
111
124
  setup_address_hash(options)
112
125
  commit(build_auth_request(money, creditcard_or_reference, options), options )
113
126
  end
114
-
127
+
115
128
  def auth_reversal(money, identification, options = {})
116
129
  commit(build_auth_reversal_request(money, identification, options), options)
117
130
  end
@@ -123,13 +136,13 @@ module ActiveMerchant #:nodoc:
123
136
  end
124
137
 
125
138
  # Purchase is an auth followed by a capture
126
- # You must supply an order_id in the options hash
139
+ # You must supply an order_id in the options hash
127
140
  def purchase(money, creditcard_or_reference, options = {})
128
141
  requires!(options, :order_id)
129
142
  setup_address_hash(options)
130
143
  commit(build_purchase_request(money, creditcard_or_reference, options), options)
131
144
  end
132
-
145
+
133
146
  def void(identification, options = {})
134
147
  commit(build_void_request(identification, options), options)
135
148
  end
@@ -137,14 +150,15 @@ module ActiveMerchant #:nodoc:
137
150
  def refund(money, identification, options = {})
138
151
  commit(build_credit_request(money, identification, options), options)
139
152
  end
140
-
153
+
141
154
  def credit(money, identification, options = {})
142
155
  deprecated CREDIT_DEPRECATION_MESSAGE
143
156
  refund(money, identification, options)
144
157
  end
145
158
 
146
159
  # Stores a customer subscription/profile with type "on-demand".
147
- # To charge the card while creating a profile, pass options[:setup_fee] => money
160
+ # To charge the card while creating a profile, pass
161
+ # options[:setup_fee] => money
148
162
  def store(creditcard, options = {})
149
163
  requires!(options, :order_id)
150
164
  setup_address_hash(options)
@@ -170,11 +184,13 @@ module ActiveMerchant #:nodoc:
170
184
  commit(build_retrieve_subscription_request(reference, options), options)
171
185
  end
172
186
 
173
- # CyberSource requires that you provide line item information for tax calculations
174
- # If you do not have prices for each item or want to simplify the situation then pass in one fake line item that costs the subtotal of the order
187
+ # CyberSource requires that you provide line item information for tax
188
+ # calculations. If you do not have prices for each item or want to
189
+ # simplify the situation then pass in one fake line item that costs the
190
+ # subtotal of the order
191
+ #
192
+ # The line_item hash goes in the options hash and should look like
175
193
  #
176
- # The line_item hash goes in the options hash and should look like
177
- #
178
194
  # :line_items => [
179
195
  # {
180
196
  # :declared_value => '1',
@@ -197,17 +213,18 @@ module ActiveMerchant #:nodoc:
197
213
  def calculate_tax(creditcard, options)
198
214
  requires!(options, :line_items)
199
215
  setup_address_hash(options)
200
- commit(build_tax_calculation_request(creditcard, options), options)
216
+ commit(build_tax_calculation_request(creditcard, options), options)
201
217
  end
202
-
218
+
203
219
  private
204
220
 
205
- # Create all address hash key value pairs so that we still function if we were only provided with one or two of them
221
+ # Create all address hash key value pairs so that we still function if we
222
+ # were only provided with one or two of them
206
223
  def setup_address_hash(options)
207
224
  options[:billing_address] = options[:billing_address] || options[:address] || {}
208
225
  options[:shipping_address] = options[:shipping_address] || {}
209
226
  end
210
-
227
+
211
228
  def build_auth_request(money, creditcard_or_reference, options)
212
229
  xml = Builder::XmlMarkup.new :indent => 2
213
230
  add_creditcard_or_subscription(xml, money, creditcard_or_reference, options)
@@ -226,7 +243,7 @@ module ActiveMerchant #:nodoc:
226
243
  add_business_rules_data(xml)
227
244
  xml.target!
228
245
  end
229
-
246
+
230
247
  def build_capture_request(money, authorization, options)
231
248
  order_id, request_id, request_token = authorization.split(";")
232
249
  options[:order_id] = order_id
@@ -236,7 +253,7 @@ module ActiveMerchant #:nodoc:
236
253
  add_capture_service(xml, request_id, request_token)
237
254
  add_business_rules_data(xml)
238
255
  xml.target!
239
- end
256
+ end
240
257
 
241
258
  def build_purchase_request(money, creditcard_or_reference, options)
242
259
  xml = Builder::XmlMarkup.new :indent => 2
@@ -245,11 +262,11 @@ module ActiveMerchant #:nodoc:
245
262
  add_business_rules_data(xml)
246
263
  xml.target!
247
264
  end
248
-
265
+
249
266
  def build_void_request(identification, options)
250
267
  order_id, request_id, request_token = identification.split(";")
251
268
  options[:order_id] = order_id
252
-
269
+
253
270
  xml = Builder::XmlMarkup.new :indent => 2
254
271
  add_void_service(xml, request_id, request_token)
255
272
  xml.target!
@@ -267,11 +284,11 @@ module ActiveMerchant #:nodoc:
267
284
  def build_credit_request(money, identification, options)
268
285
  order_id, request_id, request_token = identification.split(";")
269
286
  options[:order_id] = order_id
270
-
287
+
271
288
  xml = Builder::XmlMarkup.new :indent => 2
272
289
  add_purchase_data(xml, money, true, options)
273
290
  add_credit_service(xml, request_id, request_token)
274
-
291
+
275
292
  xml.target!
276
293
  end
277
294
 
@@ -320,13 +337,13 @@ module ActiveMerchant #:nodoc:
320
337
  xml.tag! 'businessRules' do
321
338
  xml.tag!('ignoreAVSResult', 'true') if @options[:ignore_avs]
322
339
  xml.tag!('ignoreCVResult', 'true') if @options[:ignore_cvv]
323
- end
340
+ end
324
341
  end
325
-
342
+
326
343
  def add_line_item_data(xml, options)
327
344
  options[:line_items].each_with_index do |value, index|
328
345
  xml.tag! 'item', {'id' => index} do
329
- xml.tag! 'unitPrice', amount(value[:declared_value])
346
+ xml.tag! 'unitPrice', amount(value[:declared_value])
330
347
  xml.tag! 'quantity', value[:quantity]
331
348
  xml.tag! 'productCode', value[:code] || 'shipping_only'
332
349
  xml.tag! 'productName', value[:description]
@@ -334,7 +351,7 @@ module ActiveMerchant #:nodoc:
334
351
  end
335
352
  end
336
353
  end
337
-
354
+
338
355
  def add_merchant_data(xml, options)
339
356
  xml.tag! 'merchantID', @options[:login]
340
357
  xml.tag! 'merchantReferenceCode', options[:order_id]
@@ -346,7 +363,7 @@ module ActiveMerchant #:nodoc:
346
363
  def add_purchase_data(xml, money = 0, include_grand_total = false, options={})
347
364
  xml.tag! 'purchaseTotals' do
348
365
  xml.tag! 'currency', options[:currency] || currency(money)
349
- xml.tag!('grandTotalAmount', amount(money)) if include_grand_total
366
+ xml.tag!('grandTotalAmount', amount(money)) if include_grand_total
350
367
  end
351
368
  end
352
369
 
@@ -368,10 +385,10 @@ module ActiveMerchant #:nodoc:
368
385
  xml.tag! 'email', options[:email]
369
386
  xml.tag! 'driversLicenseNumber', options[:drivers_license_number] unless options[:drivers_license_number].blank?
370
387
  xml.tag! 'driversLicenseState', options[:drivers_license_state] unless options[:drivers_license_state].blank?
371
- end
388
+ end
372
389
  end
373
390
 
374
- def add_creditcard(xml, creditcard)
391
+ def add_creditcard(xml, creditcard)
375
392
  xml.tag! 'card' do
376
393
  xml.tag! 'accountNumber', creditcard.number
377
394
  xml.tag! 'expirationMonth', format(creditcard.month, :two_digits)
@@ -389,7 +406,7 @@ module ActiveMerchant #:nodoc:
389
406
  end
390
407
 
391
408
  def add_auth_service(xml)
392
- xml.tag! 'ccAuthService', {'run' => 'true'}
409
+ xml.tag! 'ccAuthService', {'run' => 'true'}
393
410
  end
394
411
 
395
412
  def add_capture_service(xml, request_id, request_token)
@@ -446,7 +463,7 @@ module ActiveMerchant #:nodoc:
446
463
 
447
464
  xml.tag! 'recurringSubscriptionInfo' do
448
465
  if reference
449
- reference_code, subscription_id, request_token = reference.split(";")
466
+ _, subscription_id, _ = reference.split(";")
450
467
  xml.tag! 'subscriptionID', subscription_id
451
468
  end
452
469
 
@@ -479,7 +496,7 @@ module ActiveMerchant #:nodoc:
479
496
  add_creditcard(xml, creditcard_or_reference)
480
497
  end
481
498
  end
482
-
499
+
483
500
  # Where we actually build the full SOAP request using builder
484
501
  def build_request(body, options)
485
502
  xml = Builder::XmlMarkup.new :indent => 2
@@ -500,25 +517,26 @@ module ActiveMerchant #:nodoc:
500
517
  end
501
518
  end
502
519
  end
503
- xml.target!
520
+ xml.target!
504
521
  end
505
-
506
- # Contact CyberSource, make the SOAP request, and parse the reply into a Response object
522
+
523
+ # Contact CyberSource, make the SOAP request, and parse the reply into a
524
+ # Response object
507
525
  def commit(request, options)
508
- response = parse(ssl_post(test? ? TEST_URL : LIVE_URL, build_request(request, options)))
509
-
510
- success = response[:decision] == "ACCEPT"
511
- message = @@response_codes[('r' + response[:reasonCode]).to_sym] rescue response[:message]
526
+ response = parse(ssl_post(test? ? TEST_URL : LIVE_URL, build_request(request, options)))
527
+
528
+ success = response[:decision] == "ACCEPT"
529
+ message = @@response_codes[('r' + response[:reasonCode]).to_sym] rescue response[:message]
512
530
  authorization = success ? [ options[:order_id], response[:requestID], response[:requestToken] ].compact.join(";") : nil
513
-
514
- Response.new(success, message, response,
515
- :test => test?,
531
+
532
+ Response.new(success, message, response,
533
+ :test => test?,
516
534
  :authorization => authorization,
517
535
  :avs_result => { :code => response[:avsCode] },
518
536
  :cvv_result => response[:cvCode]
519
537
  )
520
538
  end
521
-
539
+
522
540
  # Parse the SOAP response
523
541
  # Technique inspired by the Paypal Gateway
524
542
  def parse(xml)
@@ -526,19 +544,19 @@ module ActiveMerchant #:nodoc:
526
544
  xml = REXML::Document.new(xml)
527
545
  if root = REXML::XPath.first(xml, "//c:replyMessage")
528
546
  root.elements.to_a.each do |node|
529
- case node.name
547
+ case node.name
530
548
  when 'c:reasonCode'
531
549
  reply[:message] = reply(node.text)
532
550
  else
533
551
  parse_element(reply, node)
534
552
  end
535
553
  end
536
- elsif root = REXML::XPath.first(xml, "//soap:Fault")
554
+ elsif root = REXML::XPath.first(xml, "//soap:Fault")
537
555
  parse_element(reply, root)
538
556
  reply[:message] = "#{reply[:faultcode]}: #{reply[:faultstring]}"
539
557
  end
540
558
  return reply
541
- end
559
+ end
542
560
 
543
561
  def parse_element(reply, node)
544
562
  if node.has_elements?
@@ -547,12 +565,12 @@ module ActiveMerchant #:nodoc:
547
565
  if node.parent.name =~ /item/
548
566
  parent = node.parent.name + (node.parent.attributes["id"] ? "_" + node.parent.attributes["id"] : '')
549
567
  reply[(parent + '_' + node.name).to_sym] = node.text
550
- else
568
+ else
551
569
  reply[node.name.to_sym] = node.text
552
570
  end
553
571
  end
554
572
  return reply
555
573
  end
556
- end
557
- end
558
- end
574
+ end
575
+ end
576
+ end