activemerchant 1.25.0 → 1.26.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 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