diff --git a/.gitignore b/.gitignore index 657f2298ada..806ae574dd2 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ pkg Gemfile.lock Gemfile*.lock .rbx/ +*.gem \ No newline at end of file diff --git a/CHANGELOG b/CHANGELOG index 4185d018aa6..ab25c8fb8dd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,75 @@ = ActiveMerchant CHANGELOG +* Balanced: Add support for appears_on_statement_as [duff] +* Authorize.Net: Make already actioned responses failures [odorcicd] +* Add Payex gateway [atomgiant] +* Paymill: Fix authorizations [duff] +* Braintree Blue: Allow specifying the credit card token [ntalbott] +* Braintree Blue: Allow specifying the customer id [ntalbott] +* Braintree Blue: Scrub invalid emails and zips [ntalbott] +* Braintree Blue: Return :credit_card_token as a top level param [ntalbott] +* Braintree Blue: Allow unstoring just a credit card [ntalbott] +* Braintree Blue: #store adds cards to existing customers [ntalbott] +* USA ePay Advanced: Fix check handling [nearapogee] +* USA ePay Advanced: Fix credit card expiration handling [nearapogee] +* USA ePay Advanced: Fix handling of custom transaction responses for single items [nearapogee] +* USA ePay Advanced: Fix capture amount [nearapogee] +* NAB Transact: Fix merchant descriptor with capture/refund requests [nagash] +* Braintree Blue: Add custom_fields & device_data parameters [parallel588] +* Webpay: Add authorize & capture [keikubo] +* MerchantWarrior: Pass description [duff] +* Stripe: Separate email from description [duff] +* Add Payscout gateway [llopez] +* Merchant Warrior: Use billing_address [duff] +* Add SoEasyPay gateway [ir-soeasycorp] +* Bogus: Add check support [npverni] +* Payflow: Add Check support [crazyivan] +* eWay Rapid: Upgrade to 3.1 [atomgiant] + +== Version 1.42.2 (November 13th, 2013) + +* Renew public certificate + +== Version 1.42.1 (November 13th, 2013) + +* Signed version of 1.42.0 + +== Version 1.42.0 (November 13th, 2013) + +* Fix NoMethodError "tr" for params with dash [TimothyKlim] +* Authorize.Net: Add cardholder authentication options (CAVV) support [structure] +* CardStreamModern: Added better checks on inputs from the gateway [ExxKA] +* Stripe: Send :ip to the gateway instead of :browser_ip [f3ndot] +* Wirecard Page: new offsite gateway [mbretter] +* Mercury: Add support for requesting a token [kcdragon] +* Add App55 gateway [ianbutler55] +* UsaEpayTransaction: Support for split payments [GBH] +* Add Swipe Checkout gateway [matt-optimizerhq] +* Spreedly Core: Allow overriding the gateway token when running a transaction [hoenth] +* Spreedly Core: Add order_id [hoenth] +* Spreedly Core: Allow store without retain [hoenth] +* Stripe: Support multiple cards on account [pierre] +* Stripe: Add card_id parameter to unstore call [pierre] +* Remove usage of `uname -a` [ntalbott] +* Litle: Allow easier access to the response code [duff] +* Stripe: Add the option to pass a version header [odorcicd] +* Elavon: Update supported countries [duff] +* Add Raven PacNet gateway [llopez] +* BitPay: Fix BitPay issues and implement Notification#acknowledge [odorcicd] + +== Version 1.41.0 (October 24th, 2013) + +* Stripe: Payments won't fail when specifying a customer with a creditcard number [melari] +* Add Conekta gateway [leofischer] +* Wirecard: Add support for void and refund [duff] +* Orbital: Mandatory field fix [juicedM3, jduff] + +== Version 1.40.0 (October 18th, 2013) + +* Paymill: Revert Add support for specifying the :customer [melari] +* Quickpay: Make v7 of the API default [kvs] +* Bitpay: Add return [tahnok] + == Version 1.39.2 (October 10th, 2013) * Eway Rapid: Fix a bug with access codes that have equal signs in them [odorcic] @@ -1249,4 +1319,3 @@ value [jduff] * Credit card validation methods as static methods of the credit card object == PlanetArgon fork for integrating Merchant eSolutions gateway - diff --git a/CONTRIBUTORS b/CONTRIBUTORS index c3fd6412c43..d5adc5543c2 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -420,3 +420,31 @@ MoneyMovers (September 2013) Be2Bill (September 2013) * Michaël Hoste (MichaelHoste) + +Conekta (October 2013) + +* Leo Fischer (leofischer) + +App55 (November 2013) + +* (ianbutler55) + +Swipe Checkout (November 2013) + +* (matt-optimizerhq) + +Raven PacNet (November 2013) + +* Luis Lopez (llopez) + +Payex (November 2013) + +* Tom Davies (atomgiant) + +Payscout (December 2013) + +* Luis Lopez (llopez) + +SoEasyPay (December 2013) + +* Ivan Radovanovic (ir-soeasycorp) diff --git a/README.md b/README.md index 420f6573c1b..b4d8372fe89 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ For more in-depth documentation and tutorials, see [GettingStarted.md](GettingSt The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) contains a [table of features supported by each gateway](http://github.com/Shopify/active_merchant/wikis/gatewayfeaturematrix). +* [App55](https://www.app55.com/) - AU, BR, CA, CH, CL, CN, CO, CZ, DK, EU, GB, HK, HU, ID, IS, JP, KE, KR, MX, MY, NO, NZ, PH, PL, TH, TW, US, VN, ZA * [Authorize.Net CIM](http://www.authorize.net/) - US * [Authorize.Net](http://www.authorize.net/) - US, CA, GB * [Balanced](https://www.balancedpayments.com/) - US @@ -94,6 +95,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta * [CardSave](http://www.cardsave.net/) - GB * [CardStream](http://www.cardstream.com/) - GB * [CertoDirect](http://www.certodirect.com/) - BE, BG, CZ, DK, DE, EE, IE, EL, ES, FR, IT, CY, LV, LT, LU, HU, MT, NL, AT, PL, PT, RO, SI, SK, FI, SE, GB +* [Conekta](https://conekta.io) - MX * [CyberSource](http://www.cybersource.com) - US, BR, CA, CN, DK, FI, FR, DE, JP, MX, NO, SE, GB, SG * [DataCash](http://www.datacash.com/) - GB * [Efsnet](http://www.concordefsnet.com/) - US @@ -101,7 +103,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta * [ePay](http://epay.dk/) - DK, SE, NO * [EVO Canada](http://www.evocanada.com/) - CA * [eWAY](http://www.eway.com.au/) - AU, NZ, GB -* [eWAY Rapid 3.0](http://www.eway.com.au/) - AU +* [eWAY Rapid 3.1](http://www.eway.com.au/) - AU * [E-xact](http://www.e-xact.com) - CA, US * [Fat Zebra](https://www.fatzebra.com.au/) - AU * [Federated Canada](http://www.federatedcanada.com/) - CA @@ -142,6 +144,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta * [PayJunction](http://www.payjunction.com/) - US * [PaySecure](http://www.commsecure.com.au/paysecure.shtml) - AU * [Paybox Direct](http://www.paybox.com/) - FR +* [Payex](http://payex.com/) - DK, NO, SE * [PaymentExpress](http://www.paymentexpress.com/) - AU, CA, DE, ES, FR, GB, HK, IE, MY, NL, NZ, SG, US, ZA * [PAYMILL](https://paymill.com) - AD, AT, BE, BG, CH, CY, CZ, DE, DK, EE, ES, FI, FO, FR, GB, GI, GR, HU, IE, IL, IS, IT, LI, LT, LU, LV, MT, NL, NO, PL, PT, RO, SE, SI, SK, TR, VA * [PayPal Express Checkout](https://www.paypal.com/webapps/mpp/express-checkout) - US, CA, SG, AU @@ -151,6 +154,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta * [PayPal Payments Pro (UK)](https://www.paypal.com/uk/webapps/mpp/pro) - GB * [PayPal Website Payments Pro (CA)](https://www.paypal.com/cgi-bin/webscr?cmd=_wp-pro-overview-outside) - CA * [PayPal Express Checkout for Digital Goods](https://www.x.com/community/ppx/xspaces/digital_goods) - AU, CA, CN, FI, GB, ID, IN, IT, MY, NO, NZ, PH, PL, SE, SG, TH, VN +* [Payscout](http://www.payscout.com/) - US * [Paystation](http://paystation.co.nz) - NZ * [Pay Way](http://www.payway.com.au) - AU * [Pin](http://www.pin.net.au/) - AU @@ -160,6 +164,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta * [QuickBooks Merchant Services](http://payments.intuit.com/) - US * [Quantum Gateway](http://www.quantumgateway.com) - US * [Quickpay](http://quickpay.dk/) - DK, SE +* [Raven PacNet](http://www.pacnetservices.com/) - US * [Realex](http://www.realexpayments.com/) - IE, GB, FR, BE, NL, LU, IT * [Redsys](http://www.redsys.es/) - ES * [SagePay](http://www.sagepay.com) - GB, IE @@ -170,8 +175,10 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta * [SecurePay](http://www.securepay.com/) - US, CA, GB, AU * [SecurePayTech](http://www.securepaytech.com/) - NZ * [SkipJack](http://www.skipjack.com/) - US, CA +* [SoEasyPay](http://www.soeasypay.com/) - US, CA, AT, BE, BG, HR, CY, CZ, DK, EE, FI, FR, DE, GR, HU, IE, IT, LV, LT, LU, MT, NL, PL, PT, RO, SK, SI, ES, SE, GB, IS, NO, CH * [Spreedly](https://spreedly.com) - AD, AE, AT, AU, BD, BE, BG, BN, CA, CH, CY, CZ, DE, DK, EE, EG, ES, FI, FR, GB, GI, GR, HK, HU, ID, IE, IL, IM, IN, IS, IT, JO, KW, LB, LI, LK, LT, LU, LV, MC, MT, MU, MV, MX, MY, NL, NO, NZ, OM, PH, PL, PT, QA, RO, SA, SE, SG, SI, SK, SM, TR, TT, UM, US, VA, VN, ZA * [Stripe](https://stripe.com/) - US, CA, GB, AU, IE, FR, NL, BE, DE, ES +* [Swipe](https://www.swipehq.com/checkout) - CA, NZ * [TransFirst](http://www.transfirst.com/) - US * [NELiX TransaX](https://www.nelixtransax.com/) - US * [Transnational](http://www.tnbci.com/) - US diff --git a/RELEASING b/RELEASING index a22f2819530..cae01fe9c04 100644 --- a/RELEASING +++ b/RELEASING @@ -4,12 +4,5 @@ releasing Active Merchant 2. Check the Semantic Versioning page for info on how to version the new release: http://semver.org 3. Update the version of Active Merchant in lib/active_merchant/version.rb and in activemerchant.gemspec 4. Add a CHANGELOG entry for the new release with the date -5. Commit the changes with a commit message like "Packaging for release X.Y.Z" -6. Tag the release with the version (Leave REV blank for HEAD or provide a SHA) - $ git tag vX.Y.Z REV -7. Push out the changes - $ git push -8. Push out the tags - $ git push --tags -9. Publish the Gem to gemcutter - $ rake release GEM_PRIVATE_KEY=/path/to/private/key \ No newline at end of file +5. Release the gem to rubygems + $ GEM_PRIVATE_KEY=/path/to/private/key bundle exec rake release \ No newline at end of file diff --git a/Rakefile b/Rakefile index 5124db49d7f..fa64d35f8ea 100644 --- a/Rakefile +++ b/Rakefile @@ -1,4 +1,5 @@ $:.unshift File.expand_path('../lib', __FILE__) +require 'active_merchant/version' begin require 'bundler' @@ -10,14 +11,29 @@ end require 'rake' require 'rake/testtask' -require 'rubygems/package_task' require 'support/gateway_support' require 'support/ssl_verify' require 'support/outbound_hosts' +task :gem => :build +task :build do + raise "Please set a private key to sign the gem" unless ENV['GEM_PRIVATE_KEY'] + system "gem build activemerchant.gemspec" +end + +task :install => :build do + system "gem install activemerchant-#{ActiveMerchant::VERSION}.gem" +end + +task :release => :build do + system "git tag -a v#{ActiveMerchant::VERSION} -m 'Tagging #{ActiveMerchant::VERSION}'" + system "git push --tags" + system "gem push activemerchant-#{ActiveMerchant::VERSION}.gem" + system "rm activemerchant-#{ActiveMerchant::VERSION}.gem" +end + desc "Run the unit test suite" task :default => 'test:units' - task :test => 'test:units' namespace :test do @@ -35,28 +51,6 @@ namespace :test do t.libs << 'test' t.verbose = true end - -end - -desc "Delete tar.gz / zip" -task :cleanup => [ :clobber_package ] - -spec = eval(File.read('activemerchant.gemspec')) - -Gem::PackageTask.new(spec) do |p| - p.gem_spec = spec - p.need_tar = true - p.need_zip = true -end - -desc "Release the gems and docs to RubyForge" -task :release => [ 'gemcutter:publish' ] - -namespace :gemcutter do - desc "Publish to gemcutter" - task :publish => :package do - sh "gem push pkg/activemerchant-#{ActiveMerchant::VERSION}.gem" - end end namespace :gateways do diff --git a/activemerchant.gemspec b/activemerchant.gemspec index 152c2bca3d1..6d31a5299fc 100644 --- a/activemerchant.gemspec +++ b/activemerchant.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', '>= 2.3.14', '< 5.0.0') s.add_dependency('i18n', '~> 0.5') - s.add_dependency('money', '< 6.0.0') + s.add_dependency('money', '< 7.0.0') s.add_dependency('builder', '>= 2.1.2', '< 4.0.0') s.add_dependency('json', '~> 1.7') s.add_dependency('active_utils', '~> 2.0', '>= 2.0.1') diff --git a/gem-public_cert.pem b/gem-public_cert.pem index c2588d5c296..7d87bf88dbd 100644 --- a/gem-public_cert.pem +++ b/gem-public_cert.pem @@ -1,7 +1,7 @@ -----BEGIN CERTIFICATE----- -MIIDNjCCAh6gAwIBAgIBADANBgkqhkiG9w0BAQUFADBBMRMwEQYDVQQDDApjb2R5 +MIIDeDCCAmCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBBMRMwEQYDVQQDDApjb2R5 ZmF1c2VyMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZFgNj -b20wHhcNMDcwMjIyMTcyMTI3WhcNMDgwMjIyMTcyMTI3WjBBMRMwEQYDVQQDDApj +b20wHhcNMTMxMTEzMTk1NjE2WhcNMTQxMTEzMTk1NjE2WjBBMRMwEQYDVQQDDApj b2R5ZmF1c2VyMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZ FgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6T4Iqt5iWvAlU iXI6L8UO0URQhIC65X/gJ9hL/x4lwSl/ckVm/R/bPrJGmifT+YooFv824N3y/TIX @@ -9,12 +9,13 @@ iXI6L8UO0URQhIC65X/gJ9hL/x4lwSl/ckVm/R/bPrJGmifT+YooFv824N3y/TIX O3wqEjvW2L6VMozVfK1MfjL9IGgy0rCnl+2g4Gh4jDDpkLfnMG5CWI6cTCf3C1ye ytOpWgi0XpOEy8nQWcFmt/KCQ/kFfzBo4QxqJi54b80842EyvzWT9OB7Oew/CXZG F2yIHtiYxonz6N09vvSzq4CvEuisoUFLKZnktndxMEBKwJU3XeSHAbuS7ix40OKO -WKuI54fHAgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW -BBR9QQpefI3oDCAxiqJW/3Gg6jI6qjANBgkqhkiG9w0BAQUFAAOCAQEAs0lX26O+ -HpyMp7WL+SgZuM8k76AjfOHuKajl2GEn3S8pWYGpsa0xu07HtehJhKLiavrfUYeE -qlFtyYMUyOh6/1S2vfkH6VqjX7mWjoi7XKHW/99fkMS40B5SbN+ypAUst+6c5R84 -w390mjtLHpdDE6WQYhS6bFvBN53vK6jG3DLyCJc0K9uMQ7gdHWoxq7RnG92ncQpT -ThpRA+fky5Xt2Q63YJDnJpkYAz79QIama1enSnd4jslKzSl89JS2luq/zioPe/Us -hbyalWR1+HrhgPoSPq7nk+s2FQUBJ9UZFK1lgMzho/4fZgzJwbu+cO8SNuaLS/bj -hPaSTyVU0yCSnw== +WKuI54fHAgMBAAGjezB5MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW +BBR9QQpefI3oDCAxiqJW/3Gg6jI6qjAfBgNVHREEGDAWgRRjb2R5ZmF1c2VyQGdt +YWlsLmNvbTAfBgNVHRIEGDAWgRRjb2R5ZmF1c2VyQGdtYWlsLmNvbTANBgkqhkiG +9w0BAQUFAAOCAQEAYJgMj+RlvKSOcks29P76WE+Lexvq3eZBDxxgFHatACdq5Fis +MUEGiiHeLkR1KRTkvkXCos6CtZVUBVUsftueHmKA7adO2yhrjv4YhPTb/WZxWmQC +L59lMhnp9UcFJ0H7TkAiU1TvvXewdQPseX8Ayl0zRwD70RfhGh0LfFsKN0JGR4ZS +yZvtu7hS26h9KwIyo5N3nw7cKSLzT7KsV+s1C+rTjVCb3/JJA9yOe/SCj/Xyc+JW +ZJB9YPQZG+vWBdDSca3sUMtvFxpLUFwdKF5APSPOVnhbFJ3vSXY1ulP/R6XW9vnw +6kkQi2fHhU20ugMzp881Eixr+TjC0RvUerLG7g== -----END CERTIFICATE----- diff --git a/generators/integration/templates/notification.rb b/generators/integration/templates/notification.rb index 72416fbc6cf..4e66fb2f500 100644 --- a/generators/integration/templates/notification.rb +++ b/generators/integration/templates/notification.rb @@ -90,8 +90,8 @@ def acknowledge(authcode = nil) def parse(post) @raw = post.to_s for line in @raw.split('&') - key, value = *line.scan( %r{^([A-Za-z0-9_.]+)\=(.*)$} ).flatten - params[key] = CGI.unescape(value) + key, value = *line.scan( %r{^([A-Za-z0-9_.-]+)\=(.*)$} ).flatten + params[key] = CGI.unescape(value.to_s) if key.present? end end end diff --git a/lib/active_merchant/billing/base.rb b/lib/active_merchant/billing/base.rb index 719480c5f6c..f059de6b705 100644 --- a/lib/active_merchant/billing/base.rb +++ b/lib/active_merchant/billing/base.rb @@ -31,8 +31,15 @@ def self.mode=(mode) # # ActiveMerchant::Billing::Base.gateway('moneris').new def self.gateway(name) - raise NameError if (name_str = name.to_s.downcase).blank? - Billing.const_get("#{name_str}_gateway".camelize) + name_str = name.to_s.strip.downcase + + raise(ArgumentError, 'A gateway provider must be specified') if name_str.blank? + + begin + Billing.const_get("#{name_str}_gateway".camelize) + rescue + raise ArgumentError, "The specified gateway is not valid (#{name_str})" + end end # Return the matching integration module diff --git a/lib/active_merchant/billing/credit_card_methods.rb b/lib/active_merchant/billing/credit_card_methods.rb index fef4bf16c52..a7801819fcd 100644 --- a/lib/active_merchant/billing/credit_card_methods.rb +++ b/lib/active_merchant/billing/credit_card_methods.rb @@ -126,7 +126,7 @@ def valid_test_mode_card_number?(number) #:nodoc: %w[1 2 3 success failure error].include?(number.to_s) end - # Checks the validity of a card number by use of the the Luhn Algorithm. + # Checks the validity of a card number by use of the Luhn Algorithm. # Please see http://en.wikipedia.org/wiki/Luhn_algorithm for details. def valid_checksum?(number) #:nodoc: sum = 0 diff --git a/lib/active_merchant/billing/gateway.rb b/lib/active_merchant/billing/gateway.rb index 97b31208db5..39826a2d7ce 100644 --- a/lib/active_merchant/billing/gateway.rb +++ b/lib/active_merchant/billing/gateway.rb @@ -62,7 +62,7 @@ class Gateway include Utils DEBIT_CARDS = [ :switch, :solo ] - CURRENCIES_WITHOUT_FRACTIONS = [ 'JPY', 'HUF', 'TWD' ] + CURRENCIES_WITHOUT_FRACTIONS = [ 'JPY', 'HUF', 'TWD', 'ISK' ] CREDIT_DEPRECATION_MESSAGE = "Support for using credit to refund existing transactions is deprecated and will be removed from a future release of ActiveMerchant. Please use the refund method instead." cattr_reader :implementations @@ -161,7 +161,14 @@ def amount(money) def localized_amount(money, currency) amount = amount(money) - non_fractional_currency?(currency) ? amount.split('.').first : amount + + return amount unless non_fractional_currency?(currency) + + if self.money_format == :cents + sprintf("%.0f", amount.to_f / 100) + else + amount.split('.').first + end end def non_fractional_currency?(currency) diff --git a/lib/active_merchant/billing/gateways/app55.rb b/lib/active_merchant/billing/gateways/app55.rb new file mode 100644 index 00000000000..b775dcb8197 --- /dev/null +++ b/lib/active_merchant/billing/gateways/app55.rb @@ -0,0 +1,185 @@ +module ActiveMerchant #:nodoc: + module Billing #:nodoc: + class App55Gateway < Gateway + self.test_url = 'https://sandbox.app55.com/v1/' + self.live_url = 'https://api.app55.com/v1/' + + self.supported_countries = ['AU', 'BR', 'CA', 'CH', 'CL', 'CN', 'CO', 'CZ', 'DK', 'EU', 'GB', 'HK', 'HU', 'ID', 'IS', 'JP', 'KE', 'KR', 'MX', 'MY', 'NO', 'NZ', 'PH', 'PL', 'TH', 'TW', 'US', 'VN', 'ZA'] + self.supported_cardtypes = [:visa, :master, :american_express, :jcb, :maestro, :solo] + self.default_currency = 'UKP' + self.money_format = :dollars + self.homepage_url = 'https://www.app55.com/' + self.display_name = 'App55' + + # Create gateway + # + # options: + # :api_key - merchants App55 API Key + # :api_secret - merchants App55 Secret Key + def initialize(options = {}) + requires!(options, :api_key, :api_secret) + @api_key = options[:api_key] + @api_secret = options[:api_secret] + super + end + + # Make a purchase (authorize and commit) + # + # money - The monetary amount of the transaction in cents. + # payment_method - The CreditCard or the App55 card token. + # options - A standard ActiveMerchant options hash + def purchase(money, payment_method, options = {}) + authorize(money, payment_method, options.merge(commit: true)) + end + + # Authorize a transaction. + # + # money - The monetary amount of the transaction in cents. + # payment_method - The CreditCard or the App55 card token. + # options - A standard ActiveMerchant options hash + def authorize(money, payment_method, options = {}) + post = {} + add_creditcard(post, payment_method, options) + add_transaction(post, money, options) + + commit(:post, 'transaction', post) + end + + # Commit a pre-authorized transaction. + # + # money - The monetary amount of the transaction in cents. + # authorization - The App55 transaction id string. + # options - A standard ActiveMerchant options hash + def capture(money, authorization, options = {}) + commit(:post, "transaction/#{authorization}") + end + + private + + def add_customer_data(post, options) + metadata_options = [:description, :browser_ip, :user_agent, :referrer] + post.update(options.slice(*metadata_options)) + end + + def add_creditcard(post, creditcard, options) + card = {} + card[:number] = creditcard.number + card[:expiry] = ("%02d". % creditcard.month) + '/' + creditcard.year.to_s + card[:security_code] = creditcard.verification_value if creditcard.verification_value? + card[:holder_name] = creditcard.name if creditcard.name + add_address(card, options) + post[:card] = card + end + + def add_address(card, options) + return unless card && card.kind_of?(Hash) + address_hash = {} + if address = (options[:billing_address] || options[:address]) + address_hash[:street] = address[:address1] if address[:address1] + address_hash[:street2] = address[:address2] if address[:address2] + address_hash[:country] = address[:country] if address[:country] + address_hash[:postal_code] = address[:zip] if address[:zip] + address_hash[:city] = address[:city] if address[:city] + card[:address] = address_hash + end + end + + def add_transaction(post, money, options) + transaction = {} + add_amount(transaction, money, options) + transaction[:description] = (options[:description] || options[:email]) + transaction[:commit] = options[:commit] + post[:transaction] = transaction + end + + def add_amount(obj, money, options) + obj[:amount] = amount(money) + obj[:currency] = (options[:currency] || currency(money)) + end + + def parse(body) + JSON.parse(body) + rescue JSON::ParserError + json_error(raw_response) + end + + def commit(method, resource, parameters=nil, meta={}) + success = false + begin + raw_response = ssl_request( + method, + url(resource), + post_data(parameters), + headers + ) + response = parse(raw_response) + success = response.key?("sig") + rescue ResponseError => e + response = parse(e.response.body) + end + + Response.new( + success, + (success ? "OK" : response["error"]["message"]), + response, + test: test?, + authorization: authorization_from(response) + ) + end + + def authorization_from(response) + if response.key?("transaction") + response["transaction"]["id"] + elsif response.key?("card") + response["card"]["token"] + end + end + + def json_error(raw_response) + msg = "Invalid response from app55 server: Received: #{raw_response.inspect})" + { + "error" => { + "message" => msg + } + } + end + + def url(resource) + (test? ? self.test_url : self.live_url) + resource + end + + def post_data(params) + return nil unless params + + params.map do |key, value| + next if value.blank? + if value.is_a?(Hash) + h = {} + value.each do |k, v| + h["#{key}.#{k}"] = v unless v.blank? + end + post_data(h) + else + "#{key}=#{CGI.escape(value.to_s)}" + end + end.compact.join("&") + end + + def headers + @@ua ||= JSON.dump( + :bindings_version => ActiveMerchant::VERSION, + :lang => 'ruby', + :lang_version => "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})", + :platform => RUBY_PLATFORM, + :publisher => 'active_merchant' + ) + + { + "Authorization" => "Basic " + Base64.strict_encode64(@options[:api_key].to_s + ":" + @options[:api_secret].to_s), + "User-Agent" => "ActiveMerchantBindings/#{ActiveMerchant::VERSION}", + } + end + end + end +end + diff --git a/lib/active_merchant/billing/gateways/authorize_net.rb b/lib/active_merchant/billing/gateways/authorize_net.rb index fb2fada9f6f..dc98492d254 100644 --- a/lib/active_merchant/billing/gateways/authorize_net.rb +++ b/lib/active_merchant/billing/gateways/authorize_net.rb @@ -39,7 +39,7 @@ class AuthorizeNetGateway < Gateway APPROVED, DECLINED, ERROR, FRAUD_REVIEW = 1, 2, 3, 4 RESPONSE_CODE, RESPONSE_REASON_CODE, RESPONSE_REASON_TEXT, AUTHORIZATION_CODE = 0, 2, 3, 4 - AVS_RESULT_CODE, TRANSACTION_ID, CARD_CODE_RESPONSE_CODE = 5, 6, 38 + AVS_RESULT_CODE, TRANSACTION_ID, CARD_CODE_RESPONSE_CODE, CARDHOLDER_AUTH_CODE = 5, 6, 38, 39 self.default_currency = 'USD' @@ -51,6 +51,7 @@ class AuthorizeNetGateway < Gateway CARD_CODE_ERRORS = %w( N S ) AVS_ERRORS = %w( A E N R W Z ) AVS_REASON_CODES = %w(27 45) + TRANSACTION_ALREADY_ACTIONED = %w(310 311) AUTHORIZE_NET_ARB_NAMESPACE = 'AnetApi/xml/v1/schema/AnetApiSchema.xsd' @@ -199,7 +200,7 @@ def credit(money, identification, options = {}) # For example, to charge the customer once every three months the hash would be # +:interval => { :unit => :months, :length => 3 }+ (REQUIRED) # * :duration -- A hash containing keys for the :start_date the subscription begins (also the date the - # initial billing occurs) and the total number of billing :occurences or payments for the subscription. (REQUIRED) + # initial billing occurs) and the total number of billing :occurrences or payments for the subscription. (REQUIRED) def recurring(money, creditcard, options={}) requires!(options, :interval, :duration, :billing_address) requires!(options[:interval], :length, [:unit, :days, :months]) @@ -294,7 +295,7 @@ def commit(action, money, parameters) end def success?(response) - response[:response_code] == APPROVED + response[:response_code] == APPROVED && TRANSACTION_ALREADY_ACTIONED.exclude?(response[:response_reason_code]) end def fraud_review?(response) @@ -311,7 +312,8 @@ def parse(body) :avs_result_code => fields[AVS_RESULT_CODE], :transaction_id => fields[TRANSACTION_ID], :card_code => fields[CARD_CODE_RESPONSE_CODE], - :authorization_code => fields[AUTHORIZATION_CODE] + :authorization_code => fields[AUTHORIZATION_CODE], + :cardholder_authentication_code => fields[CARDHOLDER_AUTH_CODE] } results end @@ -383,6 +385,15 @@ def add_customer_data(post, options) if options.has_key? :ip post[:customer_ip] = options[:ip] end + + if options.has_key? :cardholder_authentication_value + post[:cardholder_authentication_value] = options[:cardholder_authentication_value] + end + + if options.has_key? :authentication_indicator + post[:authentication_indicator] = options[:authentication_indicator] + end + end # x_duplicate_window won't be sent by default, because sending it changes the response. diff --git a/lib/active_merchant/billing/gateways/authorize_net_cim.rb b/lib/active_merchant/billing/gateways/authorize_net_cim.rb index 39aaca66522..bc3c356f5d6 100644 --- a/lib/active_merchant/billing/gateways/authorize_net_cim.rb +++ b/lib/active_merchant/billing/gateways/authorize_net_cim.rb @@ -95,7 +95,7 @@ class AuthorizeNetCimGateway < Gateway # * :test -- +true+ or +false+. If true, perform transactions against the test server. # Otherwise, perform transactions against the production server. # * :test_requests -- +true+ or +false+. If true, perform transactions without the - # test flag. This is useful when you need to generate card declines, AVS or CVV erros. + # test flag. This is useful when you need to generate card declines, AVS or CVV errors. # Will hold the same value as :test by default. # * :delimiter -- The delimiter used in the direct response. Default is ',' (comma). def initialize(options = {}) @@ -340,7 +340,7 @@ def update_customer_shipping_address(options) # ==== Transaction # # * :type -- The type of transaction. Can be either :auth_only, :capture_only, :auth_capture, :prior_auth_capture, :refund or :void. (REQUIRED) - # * :amount -- The amount for the tranaction. Formatted with a decimal. For example "4.95" (CONDITIONAL) + # * :amount -- The amount for the transaction. Formatted with a decimal. For example "4.95" (CONDITIONAL) # - :type == :void (NOT USED) # - :type == :refund (OPTIONAL) # - :type == (:auth_only, :capture_only, :auth_capture, :prior_auth_capture) (REQUIRED) @@ -369,8 +369,8 @@ def update_customer_shipping_address(options) # - :type = (:prior_auth_capture) (OPTIONAL) # # ==== For :type == :refund only - # * :credit_card_number_masked -- (CONDITIONAL - requied for credit card refunds is :customer_profile_id AND :customer_payment_profile_id are missing) - # * :bank_routing_number_masked && :bank_account_number_masked -- (CONDITIONAL - requied for electronic check refunds is :customer_profile_id AND :customer_payment_profile_id are missing) (NOT ABLE TO TEST - I keep getting "ACH transactions are not accepted by this merchant." when trying to make a payment and, until that's possible I can't refund (wiseleyb@gmail.com)) + # * :credit_card_number_masked -- (CONDITIONAL - required for credit card refunds if :customer_profile_id AND :customer_payment_profile_id are missing) + # * :bank_routing_number_masked && :bank_account_number_masked -- (CONDITIONAL - required for electronic check refunds if :customer_profile_id AND :customer_payment_profile_id are missing) (NOT ABLE TO TEST - I keep getting "ACH transactions are not accepted by this merchant." when trying to make a payment and, until that's possible I can't refund (wiseleyb@gmail.com)) def create_customer_profile_transaction(options) requires!(options, :transaction) requires!(options[:transaction], :type) @@ -410,13 +410,13 @@ def create_customer_profile_transaction(options) # * :customer_profile_id -- The Customer Profile ID of the customer to use in this transaction. (CONDITIONAL :customer_payment_profile_id must be included if used) # * :customer_payment_profile_id -- The Customer Payment Profile ID of the Customer Payment Profile to use in this transaction. (CONDITIONAL :customer_profile_id must be included if used) # - # * :credit_card_number_masked -- Four Xs follwed by the last four digits of the credit card (CONDITIONAL - used if customer_profile_id and customer_payment_profile_id aren't given) + # * :credit_card_number_masked -- Four Xs followed by the last four digits of the credit card (CONDITIONAL - used if customer_profile_id and customer_payment_profile_id aren't given) # - # * :bank_routing_number_masked -- The last four gidits of the routing number to be refunded (CONDITIONAL - must be used with :bank_account_number_masked) - # * :bank_account_number_masked -- The last four digis of the bank account number to be refunded, Ex. XXXX1234 (CONDITIONAL - must be used with :bank_routing_number_masked) + # * :bank_routing_number_masked -- The last four digits of the routing number to be refunded (CONDITIONAL - must be used with :bank_account_number_masked) + # * :bank_account_number_masked -- The last four digits of the bank account number to be refunded, Ex. XXXX1234 (CONDITIONAL - must be used with :bank_routing_number_masked) # # * :tax - A hash containing tax information for the refund (OPTIONAL - :amount, :name (31 characters), :description (255 characters)) - # * :duty - A hash containting duty information for the refund (OPTIONAL - :amount, :name (31 characters), :description (255 characters)) + # * :duty - A hash containing duty information for the refund (OPTIONAL - :amount, :name (31 characters), :description (255 characters)) # * :shipping - A hash containing shipping information for the refund (OPTIONAL - :amount, :name (31 characters), :description (255 characters)) def create_customer_profile_transaction_for_refund(options) requires!(options, :transaction) diff --git a/lib/active_merchant/billing/gateways/balanced.rb b/lib/active_merchant/billing/gateways/balanced.rb index 63350ebb677..89496890c49 100644 --- a/lib/active_merchant/billing/gateways/balanced.rb +++ b/lib/active_merchant/billing/gateways/balanced.rb @@ -131,6 +131,7 @@ def authorize(money, credit_card, options = {}) post = {} post[:amount] = money post[:description] = options[:description] + post[:appears_on_statement_as] = options[:appears_on_statement_as] if options[:appears_on_statement_as] create_or_find_account(post, options) add_credit_card(post, credit_card, options) @@ -168,6 +169,7 @@ def purchase(money, credit_card, options = {}) post = {} post[:amount] = money post[:description] = options[:description] + post[:appears_on_statement_as] = options[:appears_on_statement_as] if options[:appears_on_statement_as] create_or_find_account(post, options) add_credit_card(post, credit_card, options) @@ -197,6 +199,7 @@ def capture(money, authorization, options = {}) post[:hold_uri] = authorization post[:amount] = money if money post[:description] = options[:description] if options[:description] + post[:appears_on_statement_as] = options[:appears_on_statement_as] if options[:appears_on_statement_as] post[:on_behalf_of_uri] = options[:on_behalf_of_uri] if options[:on_behalf_of_uri] create_transaction(:post, @debits_uri, post) @@ -213,6 +216,7 @@ def capture(money, authorization, options = {}) def void(authorization, options = {}) post = {} post[:is_void] = true + post[:appears_on_statement_as] = options[:appears_on_statement_as] if options[:appears_on_statement_as] create_transaction(:put, authorization, post) rescue Error => ex @@ -246,6 +250,7 @@ def refund(amount, debit_uri = "deprecated", options = {}) post[:debit_uri] = debit_uri post[:amount] = amount post[:description] = options[:description] + post[:appears_on_statement_as] = options[:appears_on_statement_as] if options[:appears_on_statement_as] create_transaction(:post, @refunds_uri, post) rescue Error => ex failed_response(ex.response) @@ -261,11 +266,17 @@ def store(credit_card, options = {}) post = {} account_uri = create_or_find_account(post, options) if credit_card.respond_to? :number - add_credit_card(post, credit_card, options) + card_uri = add_credit_card(post, credit_card, options) else - associate_card_to_account(account_uri, credit_card) - credit_card + card_uri = associate_card_to_account(account_uri, credit_card) end + + is_test = false + if @marketplace_uri + is_test = (@marketplace_uri.index("TEST") ? true : false) + end + + Response.new(true, "Card stored", {}, :test => is_test, :authorization => [card_uri, account_uri].compact.join(';')) rescue Error => ex failed_response(ex.response) end diff --git a/lib/active_merchant/billing/gateways/beanstream.rb b/lib/active_merchant/billing/gateways/beanstream.rb index edc3bb11f02..9b8680ac2ea 100644 --- a/lib/active_merchant/billing/gateways/beanstream.rb +++ b/lib/active_merchant/billing/gateways/beanstream.rb @@ -139,7 +139,7 @@ def store(credit_card, options = {}) commit(post, true) end - #can't actually delete a secure profile with the supplicaed API. This function sets the status of the profile to closed (C). + #can't actually delete a secure profile with the supplicated API. This function sets the status of the profile to closed (C). #Closed profiles will have to removed manually. def delete(vault_id) update(vault_id, false, {:status => "C"}) diff --git a/lib/active_merchant/billing/gateways/bogus.rb b/lib/active_merchant/billing/gateways/bogus.rb index 3a23de6e8b0..3d1bdd9cf61 100644 --- a/lib/active_merchant/billing/gateways/bogus.rb +++ b/lib/active_merchant/billing/gateways/bogus.rb @@ -7,67 +7,67 @@ class BogusGateway < Gateway SUCCESS_MESSAGE = "Bogus Gateway: Forced success" FAILURE_MESSAGE = "Bogus Gateway: Forced failure" ERROR_MESSAGE = "Bogus Gateway: Use CreditCard number ending in 1 for success, 2 for exception and anything else for error" - CREDIT_ERROR_MESSAGE = "Bogus Gateway: Use CreditCard number ending in 1 for success, 2 for exception and anything else for error" UNSTORE_ERROR_MESSAGE = "Bogus Gateway: Use trans_id ending in 1 for success, 2 for exception and anything else for error" CAPTURE_ERROR_MESSAGE = "Bogus Gateway: Use authorization number ending in 1 for exception, 2 for error and anything else for success" VOID_ERROR_MESSAGE = "Bogus Gateway: Use authorization number ending in 1 for exception, 2 for error and anything else for success" REFUND_ERROR_MESSAGE = "Bogus Gateway: Use trans_id number ending in 1 for exception, 2 for error and anything else for success" + CHECK_ERROR_MESSAGE = "Bogus Gateway: Use bank account number ending in 1 for success, 2 for exception and anything else for error" self.supported_countries = ['US'] self.supported_cardtypes = [:bogus] self.homepage_url = 'http://example.com' self.display_name = 'Bogus' - def authorize(money, credit_card_or_reference, options = {}) + def authorize(money, paysource, options = {}) money = amount(money) - case normalize(credit_card_or_reference) + case normalize(paysource) when /1$/ Response.new(true, SUCCESS_MESSAGE, {:authorized_amount => money}, :test => true, :authorization => AUTHORIZATION ) when /2$/ Response.new(false, FAILURE_MESSAGE, {:authorized_amount => money, :error => FAILURE_MESSAGE }, :test => true) else - raise Error, ERROR_MESSAGE + raise Error, error_message(paysource) end end - def purchase(money, credit_card_or_reference, options = {}) + def purchase(money, paysource, options = {}) money = amount(money) - case normalize(credit_card_or_reference) + case normalize(paysource) when /1$/, AUTHORIZATION Response.new(true, SUCCESS_MESSAGE, {:paid_amount => money}, :test => true, :authorization => AUTHORIZATION) when /2$/ Response.new(false, FAILURE_MESSAGE, {:paid_amount => money, :error => FAILURE_MESSAGE },:test => true) else - raise Error, ERROR_MESSAGE + raise Error, error_message(paysource) end end - def recurring(money, credit_card_or_reference, options = {}) + def recurring(money, paysource, options = {}) money = amount(money) - case normalize(credit_card_or_reference) + case normalize(paysource) when /1$/ Response.new(true, SUCCESS_MESSAGE, {:paid_amount => money}, :test => true) when /2$/ Response.new(false, FAILURE_MESSAGE, {:paid_amount => money, :error => FAILURE_MESSAGE },:test => true) else - raise Error, ERROR_MESSAGE + raise Error, error_message(paysource) end end - def credit(money, credit_card_or_reference, options = {}) - if credit_card_or_reference.is_a?(String) + def credit(money, paysource, options = {}) + if paysource.is_a?(String) deprecated CREDIT_DEPRECATION_MESSAGE - return refund(money, credit_card_or_reference, options) + return refund(money, paysource, options) end money = amount(money) - case normalize(credit_card_or_reference) + case normalize(paysource) when /1$/ Response.new(true, SUCCESS_MESSAGE, {:paid_amount => money}, :test => true ) when /2$/ Response.new(false, FAILURE_MESSAGE, {:paid_amount => money, :error => FAILURE_MESSAGE }, :test => true) else - raise Error, CREDIT_ERROR_MESSAGE + raise Error, error_message(paysource) end end @@ -106,14 +106,14 @@ def void(reference, options = {}) end end - def store(credit_card_or_reference, options = {}) - case normalize(credit_card_or_reference) + def store(paysource, options = {}) + case normalize(paysource) when /1$/ Response.new(true, SUCCESS_MESSAGE, {:billingid => '1'}, :test => true, :authorization => AUTHORIZATION) when /2$/ Response.new(false, FAILURE_MESSAGE, {:billingid => nil, :error => FAILURE_MESSAGE }, :test => true) else - raise Error, ERROR_MESSAGE + raise Error, error_message(paysource) end end @@ -130,11 +130,21 @@ def unstore(reference, options = {}) private - def normalize(credit_card_or_reference) - if credit_card_or_reference.respond_to?(:number) - credit_card_or_reference.number + def normalize(paysource) + if paysource.respond_to?(:account_number) && (paysource.try(:number).blank? || paysource.number.blank?) + paysource.account_number + elsif paysource.respond_to?(:number) + paysource.number else - credit_card_or_reference.to_s + paysource.to_s + end + end + + def error_message(paysource) + if paysource.respond_to?(:account_number) + CHECK_ERROR_MESSAGE + elsif paysource.respond_to?(:number) + ERROR_MESSAGE end end end diff --git a/lib/active_merchant/billing/gateways/braintree_blue.rb b/lib/active_merchant/billing/gateways/braintree_blue.rb index 656b8ec139c..05b68e2529c 100644 --- a/lib/active_merchant/billing/gateways/braintree_blue.rb +++ b/lib/active_merchant/billing/gateways/braintree_blue.rb @@ -111,26 +111,20 @@ def void(authorization, options = {}) end def store(creditcard, options = {}) - commit do - parameters = { - :first_name => creditcard.first_name, - :last_name => creditcard.last_name, - :email => options[:email], - :credit_card => { - :number => creditcard.number, - :cvv => creditcard.verification_value, - :expiration_month => creditcard.month.to_s.rjust(2, "0"), - :expiration_year => creditcard.year.to_s - } - } - result = @braintree_gateway.customer.create(merge_credit_card_options(parameters, options)) - Response.new(result.success?, message_from_result(result), - { - :braintree_customer => (customer_hash(result.customer) if result.success?), - :customer_vault_id => (result.customer.id if result.success?) - }, - :authorization => (result.customer.id if result.success?) - ) + if options[:customer].present? + MultiResponse.new.tap do |r| + customer_exists_response = nil + r.process{customer_exists_response = check_customer_exists(options[:customer])} + r.process do + if customer_exists_response.params["exists"] + add_credit_card_to_customer(creditcard, options) + else + add_customer_with_credit_card(creditcard, options) + end + end + end + else + add_customer_with_credit_card(creditcard, options) end end @@ -153,11 +147,11 @@ def update(vault_id, creditcard, options = {}) result = @braintree_gateway.customer.update(vault_id, :first_name => creditcard.first_name, :last_name => creditcard.last_name, - :email => options[:email], + :email => scrub_email(options[:email]), :credit_card => credit_card_params ) Response.new(result.success?, message_from_result(result), - :braintree_customer => (customer_hash(@braintree_gateway.customer.find(vault_id)) if result.success?), + :braintree_customer => (customer_hash(@braintree_gateway.customer.find(vault_id), :include_credit_cards) if result.success?), :customer_vault_id => (result.customer.id if result.success?) ) end @@ -165,7 +159,11 @@ def update(vault_id, creditcard, options = {}) def unstore(customer_vault_id, options = {}) commit do - @braintree_gateway.customer.delete(customer_vault_id) + if(!customer_vault_id && options[:credit_card_token]) + @braintree_gateway.credit_card.delete(options[:credit_card_token]) + else + @braintree_gateway.customer.delete(customer_vault_id) + end Response.new(true, "OK") end end @@ -173,6 +171,87 @@ def unstore(customer_vault_id, options = {}) private + def check_customer_exists(customer_vault_id) + commit do + begin + @braintree_gateway.customer.find(customer_vault_id) + ActiveMerchant::Billing::Response.new(true, "Customer found", {exists: true}, authorization: customer_vault_id) + rescue Braintree::NotFoundError + ActiveMerchant::Billing::Response.new(true, "Customer not found", {exists: false}) + end + end + end + + def add_customer_with_credit_card(creditcard, options) + commit do + parameters = { + :first_name => creditcard.first_name, + :last_name => creditcard.last_name, + :email => scrub_email(options[:email]), + :id => options[:customer], + :credit_card => { + :number => creditcard.number, + :cvv => creditcard.verification_value, + :expiration_month => creditcard.month.to_s.rjust(2, "0"), + :expiration_year => creditcard.year.to_s, + :token => options[:credit_card_token] + } + } + result = @braintree_gateway.customer.create(merge_credit_card_options(parameters, options)) + Response.new(result.success?, message_from_result(result), + { + :braintree_customer => (customer_hash(result.customer, :include_credit_cards) if result.success?), + :customer_vault_id => (result.customer.id if result.success?), + :credit_card_token => (result.customer.credit_cards[0].token if result.success?) + }, + :authorization => (result.customer.id if result.success?) + ) + end + end + + def add_credit_card_to_customer(credit_card, options) + commit do + parameters = { + customer_id: options[:customer], + token: options[:credit_card_token], + number: credit_card.number, + cvv: credit_card.verification_value, + expiration_month: credit_card.month.to_s.rjust(2, "0"), + expiration_year: credit_card.year.to_s, + } + parameters[:billing_address] = map_address(options[:billing_address]) if options[:billing_address] + + result = @braintree_gateway.credit_card.create(parameters) + ActiveMerchant::Billing::Response.new( + result.success?, + message_from_result(result), + { + customer_vault_id: (result.credit_card.customer_id if result.success?), + credit_card_token: (result.credit_card.token if result.success?) + }, + authorization: (result.credit_card.customer_id if result.success?) + ) + end + end + + def scrub_email(email) + return nil unless email.present? + return nil if ( + email !~ /^.+@[^\.]+(\.[^\.]+)+[a-z]$/i || + email =~ /\.(con|met)$/i + ) + email + end + + def scrub_zip(zip) + return nil unless zip.present? + return nil if( + zip.gsub(/[^a-z0-9]/i, '').length > 9 || + zip =~ /[^a-z0-9\- ]/i + ) + zip + end + def merge_credit_card_options(parameters, options) valid_options = {} options.each do |key, value| @@ -193,7 +272,7 @@ def map_address(address) :company => address[:company], :locality => address[:city], :region => address[:state], - :postal_code => address[:zip], + :postal_code => scrub_zip(address[:zip]), } if(address[:country] || address[:country_code_alpha2]) mapped[:country_code_alpha2] = (address[:country] || address[:country_code_alpha2]) @@ -281,25 +360,29 @@ def extract_refund_args(args) end end - def customer_hash(customer) - credit_cards = customer.credit_cards.map do |cc| - { - "bin" => cc.bin, - "expiration_date" => cc.expiration_date, - "token" => cc.token, - "last_4" => cc.last_4, - "card_type" => cc.card_type, - "masked_number" => cc.masked_number - } - end - - { + def customer_hash(customer, include_credit_cards=false) + hash = { "email" => customer.email, "first_name" => customer.first_name, "last_name" => customer.last_name, - "credit_cards" => credit_cards, "id" => customer.id } + + if include_credit_cards + hash["credit_cards"] = customer.credit_cards.map do |cc| + { + "bin" => cc.bin, + "expiration_date" => cc.expiration_date, + "token" => cc.token, + "last_4" => cc.last_4, + "card_type" => cc.card_type, + "masked_number" => cc.masked_number, + "token" => cc.token + } + end + end + + hash end def transaction_hash(transaction) @@ -365,7 +448,7 @@ def create_transaction_parameters(money, credit_card_or_vault_id, options) :order_id => options[:order_id], :customer => { :id => options[:store] == true ? "" : options[:store], - :email => options[:email] + :email => scrub_email(options[:email]) }, :options => { :store_in_vault => options[:store] ? true : false, @@ -373,6 +456,8 @@ def create_transaction_parameters(money, credit_card_or_vault_id, options) } } + parameters[:custom_fields] = options[:custom_fields] + parameters[:device_data] = options[:device_data] if options[:device_data] if merchant_account_id = (options[:merchant_account_id] || @merchant_account_id) parameters[:merchant_account_id] = merchant_account_id end diff --git a/lib/active_merchant/billing/gateways/card_stream_modern.rb b/lib/active_merchant/billing/gateways/card_stream_modern.rb index 1d3393fa22d..984de4b28b9 100644 --- a/lib/active_merchant/billing/gateways/card_stream_modern.rb +++ b/lib/active_merchant/billing/gateways/card_stream_modern.rb @@ -114,7 +114,8 @@ def parse(body) pairs = body.split("&") pairs.each do |pair| a = pair.split("=") - result[a[0].to_sym] = CGI.unescape(a[1]) + #Make sure there is a value, else set it to empty string + result[a[0].to_sym] = a[1] ? CGI.unescape(a[1]) : "" end result end diff --git a/lib/active_merchant/billing/gateways/conekta.rb b/lib/active_merchant/billing/gateways/conekta.rb new file mode 100644 index 00000000000..2389106acff --- /dev/null +++ b/lib/active_merchant/billing/gateways/conekta.rb @@ -0,0 +1,233 @@ +module ActiveMerchant #:nodoc: + module Billing #:nodoc: + class ConektaGateway < Gateway + self.live_url = 'https://api.conekta.io/' + + self.supported_countries = ['MX'] + self.supported_cardtypes = [:visa, :master] + self.homepage_url = 'https://conekta.io/' + self.display_name = 'Conekta Gateway' + self.money_format = :cents + self.default_currency = 'MXN' + + def initialize(options = {}) + requires!(options, :key) + options[:version] ||= '0.2.0' + super + end + + def purchase(money, payment_source, options = {}) + post = {} + + add_order(post, money, options) + add_payment_source(post, payment_source, options) + add_details_data(post, options) + + commit(:post, 'charges', post) + end + + def authorize(money, payment_source, options = {}) + post = {} + + add_order(post, money, options) + add_payment_source(post, payment_source, options) + add_details_data(post, options) + + post[:capture] = false + commit(:post, "charges", post) + end + + def capture(identifier, money, options = {}) + post = {} + + post[:order_id] = identifier + add_order(post, money, options) + + commit(:post, "charges/#{identifier}/capture", post) + end + + def refund(identifier, money, options) + post = {} + + post[:order_id] = identifier + add_order(post, money, options) + + commit(:post, "charges/#{identifier}/refund", post) + end + + def store(creditcard, options = {}) + post = {} + add_payment_source(post, creditcard, options) + post[:name] = options[:name] + post[:email] = options[:email] + + path = if options[:customer] + "customers/#{CGI.escape(options[:customer])}" + else + 'customers' + end + + commit(:post, path, post) + end + + def unstore(customer_id, options = {}) + commit(:delete, "customers/#{CGI.escape(customer_id)}", nil) + end + + private + + def add_order(post, money, options) + post[:description] = options[:description] + post[:reference_id] = options[:order_id] + post[:amount] = amount(money) + end + + def add_details_data(post, options) + details = {} + details[:name] = options[:customer] + details[:email] = options[:email] + details[:phone] = options[:phone] + details[:device_fingerprint] = options[:device_fingerprint] + details[:ip] = options[:ip] + add_billing_address(details, options) + add_line_items(details, options) + add_shipment(details, options) + + post[:details] = details + end + + def add_shipment(post, options) + shipment = {} + shipment[:carrier] = options[:carrier] + shipment[:service] = options[:service] + shipment[:tracking_number] = options[:tracking_number] + shipment[:price] = options[:price] + add_shipment_address(shipment, options) + post[:shipment] = shipment + end + + def add_shipment_address(post, options) + address = {} + address[:street1] = options[:address1] + address[:street2] = options[:address2] + address[:street3] = options[:address3] + address[:city] = options[:city] + address[:state] = options[:state] + address[:country] = options[:country] + address[:zip] = options[:zip] + post[:address] = address + end + + def add_line_items(post, options) + post[:line_items] = (options[:line_items] || []).collect do |line_item| + line_item + end + end + + def add_billing_address(post, options) + address = {} + address[:street1] = options[:address1] + address[:street2] = options[:address2] + address[:street3] = options[:address3] + address[:city] = options[:city] + address[:state] = options[:state] + address[:country] = options[:country] + address[:zip] = options[:zip] + address[:company_name] = options[:company_name] + address[:tax_id] = options[:tax_id] + address[:name] = options[:name] + address[:phone] = options[:phone] + address[:email] = options[:email] + post[:billing_address] = address + end + + def add_address(post, options) + address = {} + address[:street1] = options[:address1] + address[:street2] = options[:address2] + address[:street3] = options[:address3] + address[:city] = options[:city] + address[:state] = options[:state] + address[:country] = options[:country] + address[:zip] = options[:zip] + post[:address] = address + end + + def add_payment_source(post, payment_source, options) + if payment_source.kind_of?(String) + post[:card] = payment_source + elsif payment_source.respond_to?(:number) + card = {} + card[:name] = payment_source.name + card[:cvc] = payment_source.verification_value + card[:number] = payment_source.number + card[:exp_month] = "#{sprintf("%02d", payment_source.month)}" + card[:exp_year] = "#{"#{payment_source.year}"[-2, 2]}" + post[:card] = card + add_address(post[:card], options) + end + end + + def parse(body) + return {} unless body + JSON.parse(body) + end + + def headers(meta) + @@ua ||= JSON.dump({ + :bindings_version => ActiveMerchant::VERSION, + :lang => 'ruby', + :lang_version => "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})", + :platform => RUBY_PLATFORM, + :publisher => 'active_merchant' + }) + + { + "Accept" => "application/vnd.conekta-v#{options[:version]}+json", + "Authorization" => "Basic " + Base64.encode64("#{options[:key]}:"), + "RaiseHtmlError" => "false", + "User-Agent" => "Conekta ActiveMerchantBindings/#{ActiveMerchant::VERSION}", + "X-Conekta-Client-User-Agent" => @@ua, + "X-Conekta-Client-User-Metadata" => meta.to_json + } + end + + def commit(method, url, parameters, options = {}) + success = false + begin + raw_response = parse(ssl_request(method, live_url + url, (parameters ? parameters.to_query : nil), headers(options[:meta]))) + success = (raw_response.key?("object") && (raw_response["object"] != "error")) + rescue ResponseError => e + raw_response = response_error(e.response.body) + rescue JSON::ParserError + raw_response = json_error(raw_response) + end + + Response.new( + success, + raw_response["message"], + raw_response, + :test => test?, + :authorization => raw_response["id"] + ) + end + + def response_error(raw_response) + begin + parse(raw_response) + rescue JSON::ParserError + json_error(raw_response) + end + end + + def json_error(raw_response) + msg = 'Invalid response received from the Conekta API.' + msg += " (The raw response returned by the API was #{raw_response.inspect})" + { + "message" => msg + } + end + end + end +end + diff --git a/lib/active_merchant/billing/gateways/cyber_source.rb b/lib/active_merchant/billing/gateways/cyber_source.rb index f72480808d6..c959b66e309 100644 --- a/lib/active_merchant/billing/gateways/cyber_source.rb +++ b/lib/active_merchant/billing/gateways/cyber_source.rb @@ -24,7 +24,7 @@ module Billing #:nodoc: # CyberSource what kind of item you are selling. It is used when # calculating tax/VAT. # * All transactions use dollar values. - # * To process pinless debit cards throught the pinless debit card + # * To process pinless debit cards through the pinless debit card # network, your Cybersource merchant account must accept pinless # debit card payments. class CyberSourceGateway < Gateway @@ -102,7 +102,7 @@ class CyberSourceGateway < Gateway # :vat_reg_number => your VAT registration number # # :nexus => "WI CA QC" sets the states/provinces where you have a physical - # presense for tax purposes + # presence for tax purposes # # :ignore_avs => true don't want to use AVS so continue processing even # if AVS would have failed diff --git a/lib/active_merchant/billing/gateways/elavon.rb b/lib/active_merchant/billing/gateways/elavon.rb index ddc4559b937..d079eabca9c 100644 --- a/lib/active_merchant/billing/gateways/elavon.rb +++ b/lib/active_merchant/billing/gateways/elavon.rb @@ -36,7 +36,7 @@ class ElavonGateway < Gateway self.live_url = 'https://www.myvirtualmerchant.com/VirtualMerchant/process.do' self.display_name = 'Elavon MyVirtualMerchant' - self.supported_countries = ['US', 'CA'] + self.supported_countries = %w(US CA PR DE IE NO PL LU BE NL) self.supported_cardtypes = [:visa, :master, :american_express, :discover] self.homepage_url = 'http://www.elavon.com/' diff --git a/lib/active_merchant/billing/gateways/eway_rapid.rb b/lib/active_merchant/billing/gateways/eway_rapid.rb index 12616b3f320..380e4be0a4f 100644 --- a/lib/active_merchant/billing/gateways/eway_rapid.rb +++ b/lib/active_merchant/billing/gateways/eway_rapid.rb @@ -1,5 +1,4 @@ -require "nokogiri" -require "cgi" +require 'json' module ActiveMerchant #:nodoc: module Billing #:nodoc: @@ -11,7 +10,7 @@ class EwayRapidGateway < Gateway self.supported_countries = ["AU"] self.supported_cardtypes = [:visa, :master, :american_express, :diners_club] self.homepage_url = "http://www.eway.com.au/" - self.display_name = "eWAY Rapid 3.0" + self.display_name = "eWAY Rapid 3.1" self.default_currency = "AUD" def initialize(options = {}) @@ -19,203 +18,219 @@ def initialize(options = {}) super end - # Public: Run a purchase transaction. Treats the Rapid 3.0 transparent - # redirect as an API endpoint in order to conform to the standard - # ActiveMerchant #purchase API. + # Public: Run a purchase transaction. # - # amount - The monetary amount of the transaction in cents. - # options - A standard ActiveMerchant options hash: - # :order_id - A merchant-supplied identifier for the - # transaction (optional). - # :description - A merchant-supplied description of the - # transaction (optional). - # :currency - Three letter currency code for the - # transaction (default: "AUD") - # :billing_address - Standard ActiveMerchant address hash - # (optional). - # :shipping_address - Standard ActiveMerchant address hash - # (optional). - # :ip - The ip of the consumer initiating the - # transaction (optional). - # :application_id - A string identifying the application - # submitting the transaction - # (default: "https://github.com/Shopify/active_merchant") + # amount - The monetary amount of the transaction in cents. + # payment_method - The payment method or authorization token returned from store. + # options - A standard ActiveMerchant options hash: + # :transaction_type - One of: Purchase (default), MOTO + # or Recurring. For stored card payments (aka - TokenPayments), + # this must be either MOTO or Recurring. + # :order_id - A merchant-supplied identifier for the + # transaction (optional). + # :description - A merchant-supplied description of the + # transaction (optional). + # :currency - Three letter currency code for the + # transaction (default: "AUD") + # :billing_address - Standard ActiveMerchant address hash + # (optional). + # :shipping_address - Standard ActiveMerchant address hash + # (optional). + # :ip - The ip of the consumer initiating the + # transaction (optional). + # :application_id - A string identifying the application + # submitting the transaction + # (default: "https://github.com/Shopify/active_merchant") # - # Returns an ActiveMerchant::Billing::Response object + # Returns an ActiveMerchant::Billing::Response object where authorization is the Transaction ID on success def purchase(amount, payment_method, options={}) - MultiResponse.new.tap do |r| - # Rather than follow the redirect, we detect the 302 and capture the - # token out of the Location header in the run_purchase step. But we - # still need a placeholder url to pass to eWay, and that is what - # example.com is used for here. - r.process{setup_purchase(amount, options.merge(:redirect_url => "http://example.com/"))} - r.process{run_purchase(r.authorization, payment_method, r.params["formactionurl"])} - r.process{status(r.authorization)} - end + params = {} + add_metadata(params, options) + add_invoice(params, amount, options) + add_customer_data(params, options) + add_credit_card(params, payment_method, options) + commit(url_for('Transaction'), params) end - # Public: Acquire the token necessary to run a transparent redirect. + # Public: Refund a transaction. # - # amount - The monetary amount of the transaction in cents. - # options - A supplemented ActiveMerchant options hash: - # :redirect_url - The url to return the customer to after - # the transparent redirect is completed - # (required). - # :order_id - A merchant-supplied identifier for the - # transaction (optional). - # :description - A merchant-supplied description of the - # transaction (optional). - # :currency - Three letter currency code for the - # transaction (default: "AUD") - # :billing_address - Standard ActiveMerchant address hash - # (optional). - # :shipping_address - Standard ActiveMerchant address hash - # (optional). - # :ip - The ip of the consumer initiating the - # transaction (optional). - # :application_id - A string identifying the application - # submitting the transaction - # (default: "https://github.com/Shopify/active_merchant") + # money - The monetary amount of the transaction in cents + # identification - The transaction id which is returned in the + # authorization of the successful purchase transaction + # options - A standard ActiveMerchant options hash: + # :order_id - A merchant-supplied identifier for the + # transaction (optional). + # :description - A merchant-supplied description of the + # transaction (optional). + # :currency - Three letter currency code for the + # transaction (default: "AUD") + # :billing_address - Standard ActiveMerchant address hash + # (optional). + # :shipping_address - Standard ActiveMerchant address hash + # (optional). + # :ip - The ip of the consumer initiating the + # transaction (optional). + # :application_id - A string identifying the application + # submitting the transaction + # (default: "https://github.com/Shopify/active_merchant") # - # Returns an EwayRapidResponse object, which conforms to the - # ActiveMerchant::Billing::Response API, but also exposes #form_url. - def setup_purchase(amount, options={}) - requires!(options, :redirect_url) - request = build_xml_request("CreateAccessCodeRequest") do |doc| - add_metadata(doc, options) - add_invoice(doc, amount, options) - add_customer_data(doc, options) - end - - commit(url_for("CreateAccessCode"), request) + # Returns an ActiveMerchant::Billing::Response object + def refund(money, identification, options = {}) + params = {} + add_metadata(params, options) + add_invoice(params, money, options.merge(refund_transaction_id: identification)) + add_customer_data(params, options) + commit(url_for("Transaction/#{identification}/Refund"), params) end - # Public: Retrieve the status of a transaction. + # Public: Store card details and return a valid token # - # identification - The Eway Rapid 3.0 access code for the transaction - # (returned as the response.authorization by - # #setup_purchase). + # payment_method - The payment method or nil if :customer_token is provided + # options - A supplemented ActiveMerchant options hash: + # :order_id - A merchant-supplied identifier for the + # transaction (optional). + # :description - A merchant-supplied description of the + # transaction (optional). + # :billing_address - Standard ActiveMerchant address hash + # (required). + # :ip - The ip of the consumer initiating the + # transaction (optional). + # :application_id - A string identifying the application + # submitting the transaction + # (default: "https://github.com/Shopify/active_merchant") # - # Returns an EwayRapidResponse object. - def status(identification) - request = build_xml_request("GetAccessCodeResultRequest") do |doc| - doc.AccessCode identification - end - commit(url_for("GetAccessCodeResult"), request) + # Returns an ActiveMerchant::Billing::Response object where the authorization is the customer_token on success + def store(payment_method, options = {}) + requires!(options, :billing_address) + params = {} + add_metadata(params, options) + add_invoice(params, 0, options) + add_customer_data(params, options) + add_credit_card(params, payment_method, options) + params['Method'] = 'CreateTokenCustomer' + commit(url_for("Transaction"), params) end - # Public: Store card details and return a valid token + # Public: Update a customer's data # - # options - A supplemented ActiveMerchant options hash: - # :order_id - A merchant-supplied identifier for the - # transaction (optional). - # :billing_address - Standard ActiveMerchant address hash - # (required). - # :ip - The ip of the consumer initiating the - # transaction (optional). - # :application_id - A string identifying the application - # submitting the transaction - # (default: "https://github.com/Shopify/active_merchant") - def store(payment_method, options = {}) - requires!(options, :billing_address) - purchase(0, payment_method, options.merge(:request_method => "CreateTokenCustomer")) + # customer_token - The customer token returned in the authorization of + # a successful store transaction. + # payment_method - The payment method or nil if :customer_token is provided + # options - A supplemented ActiveMerchant options hash: + # :order_id - A merchant-supplied identifier for the + # transaction (optional). + # :description - A merchant-supplied description of the + # transaction (optional). + # :billing_address - Standard ActiveMerchant address hash + # (optional). + # :ip - The ip of the consumer initiating the + # transaction (optional). + # :application_id - A string identifying the application + # submitting the transaction + # (default: "https://github.com/Shopify/active_merchant") + # + # Returns an ActiveMerchant::Billing::Response object where the authorization is the customer_token on success + def update(customer_token, payment_method, options = {}) + params = {} + add_metadata(params, options) + add_invoice(params, 0, options) + add_customer_data(params, options) + add_credit_card(params, payment_method, options) + add_customer_token(params, customer_token) + params['Method'] = 'UpdateTokenCustomer' + commit(url_for("Transaction"), params) end private - def run_purchase(identification, payment_method, endpoint) - post = { - "accesscode" => identification - } - add_credit_card(post, payment_method) - - commit_form(endpoint, build_form_request(post), :identification => identification) - end - - def add_metadata(doc, options) - doc.RedirectUrl(options[:redirect_url]) - doc.CustomerIP options[:ip] if options[:ip] - doc.Method options[:request_method] || "ProcessPayment" - doc.DeviceID(options[:application_id] || application_id) + def add_metadata(params, options) + params['RedirectUrl'] = options[:redirect_url] || 'http://example.com' + params['CustomerIP'] = options[:ip] if options[:ip] + params['TransactionType'] = options[:transaction_type] || 'Purchase' + params['DeviceID'] = options[:application_id] || application_id end - def add_invoice(doc, money, options) - doc.Payment do - doc.TotalAmount amount(money) - doc.InvoiceReference options[:order_id] - doc.InvoiceDescription options[:description] - currency_code = (options[:currency] || currency(money) || default_currency) - doc.CurrencyCode currency_code + def add_invoice(params, money, options) + currency_code = options[:currency] || currency(money) + invoice = { + 'TotalAmount' => localized_amount(money, currency_code), + 'InvoiceReference' => options[:order_id], + 'InvoiceDescription' => options[:description], + 'CurrencyCode' => currency_code, + } + if options[:refund_transaction_id] + # must include the original transaction id for refunds + invoice['TransactionID'] = options[:refund_transaction_id] if options[:refund_transaction_id] + params['Refund'] = invoice + else + params['Payment'] = invoice end end - def add_customer_data(doc, options) - doc.Customer do - add_address(doc, (options[:billing_address] || options[:address]), {:email => options[:email]}) - end - doc.ShippingAddress do - add_address(doc, options[:shipping_address], {:skip_company => true}) - end + def add_customer_data(params, options) + params['Customer'] ||= {} + add_address(params['Customer'], (options[:billing_address] || options[:address]), {:email => options[:email]}) + params['ShippingAddress'] = {} + add_address(params['ShippingAddress'], options[:shipping_address], {:skip_company => true}) end - def add_address(doc, address, options={}) + def add_address(params, address, options={}) return unless address - if name = address[:name] - parts = name.split(/\s+/) - doc.FirstName parts.shift if parts.size > 1 - doc.LastName parts.join(" ") - end - doc.Title address[:title] - doc.CompanyName address[:company] unless options[:skip_company] - doc.Street1 address[:address1] - doc.Street2 address[:address2] - doc.City address[:city] - doc.State address[:state] - doc.PostalCode address[:zip] - doc.Country address[:country].to_s.downcase - doc.Phone address[:phone] - doc.Fax address[:fax] - doc.Email options[:email] - end - def add_credit_card(post, credit_card) - post["cardname"] = credit_card.name - post["cardnumber"] = credit_card.number - post["cardexpirymonth"] = credit_card.month - post["cardexpiryyear"] = credit_card.year - post["cardcvn"] = credit_card.verification_value + if address[:name] + parts = address[:name].split(/\s+/) + params['FirstName'] = parts.shift if parts.size > 1 + params['LastName'] = parts.join(" ") + end + params['Title'] = address[:title] + params['CompanyName'] = address[:company] unless options[:skip_company] + params['Street1'] = address[:address1] + params['Street2'] = address[:address2] + params['City'] = address[:city] + params['State'] = address[:state] + params['PostalCode'] = address[:zip] + params['Country'] = address[:country].to_s.downcase + params['Phone'] = address[:phone] + params['Fax'] = address[:fax] + params['Email'] = options[:email] end - def build_xml_request(root) - builder = Nokogiri::XML::Builder.new - builder.__send__(root) do |doc| - yield(doc) + def add_credit_card(params, credit_card, options) + return unless credit_card + params['Customer'] ||= {} + if credit_card.respond_to? :number + params['Method'] = 'ProcessPayment' + card_details = params['Customer']['CardDetails'] = {} + card_details['Name'] = credit_card.name + card_details['Number'] = credit_card.number + card_details['ExpiryMonth'] = "%02d" % (credit_card.month || 0) + card_details['ExpiryYear'] = "%02d" % (credit_card.year || 0) + card_details['CVN'] = credit_card.verification_value + else + params['Method'] = 'TokenPayment' + add_customer_token(params, credit_card) end - builder.to_xml end - def build_form_request(post) - request = [] - post.each do |key, value| - request << "EWAY_#{key.upcase}=#{CGI.escape(value.to_s)}" - end - request.join("&") + def add_customer_token(params, token) + params['Customer'] ||= {} + params['Customer']['TokenCustomerID'] = token end def url_for(action) - (test? ? test_url : live_url) + action + ".xml" + (test? ? test_url : live_url) + action end - def commit(url, request, form_post=false) + def commit(url, params) headers = { "Authorization" => ("Basic " + Base64.strict_encode64(@options[:login].to_s + ":" + @options[:password].to_s).chomp), - "Content-Type" => "text/xml" + "Content-Type" => "application/json" } - + request = params.to_json raw = parse(ssl_post(url, request, headers)) succeeded = success?(raw) - EwayRapidResponse.new( + ActiveMerchant::Billing::Response.new( succeeded, message_from(succeeded, raw), raw, @@ -225,56 +240,32 @@ def commit(url, request, form_post=false) :cvv_result => cvv_result_from(raw) ) rescue ActiveMerchant::ResponseError => e - return EwayRapidResponse.new(false, e.response.message, {:status_code => e.response.code}, :test => test?) + return ActiveMerchant::Billing::Response.new(false, e.response.message, {:status_code => e.response.code}, :test => test?) end - def commit_form(url, request, parameters) - http_response = raw_ssl_request(:post, url, request) - - success = (http_response.code.to_s == "302") - message = (success ? "Succeeded" : http_response.body) - authorization = parameters[:identification] if success - - Response.new(success, message, {:location => http_response["Location"]}, :authorization => authorization, :test => test?) - end - - def parse(xml) - response = {} - - doc = Nokogiri::XML(xml) - doc.root.xpath("*").each do |node| - if (node.elements.size == 0) - response[node.name.downcase.to_sym] = node.text - else - node.elements.each do |childnode| - name = "#{node.name.downcase}_#{childnode.name.downcase}" - response[name.to_sym] = childnode.text - end - end - end unless doc.root.nil? - - response + def parse(data) + JSON.parse(data) end def success?(response) - if response[:errors] + if response['Errors'] false - elsif response[:responsecode] == "00" + elsif response['ResponseCode'] == "00" true - elsif response[:transactionstatus] - (response[:transactionstatus] == "true") + elsif response['TransactionStatus'] + (response['TransactionStatus'] == true) else true end end def message_from(succeeded, response) - if response[:errors] - (MESSAGES[response[:errors]] || response[:errors]) - elsif response[:responsecode] - ActiveMerchant::Billing::EwayGateway::MESSAGES[response[:responsecode]] - elsif response[:responsemessage] - (MESSAGES[response[:responsemessage]] || response[:responsemessage]) + if response['Errors'] + (MESSAGES[response['Errors']] || response['Errors']) + elsif response['Responsecode'] + ActiveMerchant::Billing::EwayGateway::MESSAGES[response['ResponseCode']] + elsif response['ResponseMessage'] + (MESSAGES[response['ResponseMessage']] || response['ResponseMessage']) elsif succeeded "Succeeded" else @@ -283,11 +274,14 @@ def message_from(succeeded, response) end def authorization_from(response) - response[:accesscode] + # Note: TransactionID is always null for store requests, but TokenCustomerID is also sent back for purchase from + # stored card transactions so we give precendence to TransactionID + response['TransactionID'] || response['Customer']['TokenCustomerID'] end def avs_result_from(response) - code = case response[:verification_address] + verification = response['Verification'] || {} + code = case verification['Address'] when "Valid" "M" when "Invalid" @@ -299,7 +293,8 @@ def avs_result_from(response) end def cvv_result_from(response) - case response[:verification_cvn] + verification = response['Verification'] || {} + case verification['CVN'] when "Valid" "M" when "Invalid" @@ -309,16 +304,25 @@ def cvv_result_from(response) end end - class EwayRapidResponse < ActiveMerchant::Billing::Response - def form_url - params["formactionurl"] - end - end - MESSAGES = { + 'A2000' => 'Transaction Approved Successful', + 'A2008' => 'Honour With Identification Successful', + 'A2010' => 'Approved For Partial Amount Successful', + 'A2011' => 'Approved, VIP Successful', + 'A2016' => 'Approved, Update Track 3 Successful', + 'S5000' => 'System Error', + 'S5085' => 'Started 3dSecure', + 'S5086' => 'Routed 3dSecure', + 'S5087' => 'Completed 3dSecure', + 'S5088' => 'PayPal Transaction Created', + 'S5099' => 'Incomplete (Access Code in progress/incomplete)', + 'S5010' => 'Unknown error returned by gateway', 'V6000' => 'Validation error', 'V6001' => 'Invalid CustomerIP', 'V6002' => 'Invalid DeviceID', + 'V6003' => 'Invalid Request PartnerID', + 'V6004' => 'Invalid Request Method', + 'V6010' => 'Invalid TransactionType, account not certified for eCome only MOTO or Recurring available', 'V6011' => 'Invalid Payment TotalAmount', 'V6012' => 'Invalid Payment InvoiceDescription', 'V6013' => 'Invalid Payment InvoiceNumber', @@ -385,7 +389,8 @@ def form_url 'V6107' => 'Invalid EWAY_ACCESSCODE', 'V6108' => 'Invalid CustomerHostAddress', 'V6109' => 'Invalid UserAgent', - 'V6110' => 'Invalid EWAY_CARDNUMBER' + 'V6110' => 'Invalid EWAY_CARDNUMBER', + 'V6111' => 'Unauthorised API Access, Account Not PCI Certified' } end end diff --git a/lib/active_merchant/billing/gateways/litle.rb b/lib/active_merchant/billing/gateways/litle.rb index 83ce2bc28dc..afb742a09f2 100755 --- a/lib/active_merchant/billing/gateways/litle.rb +++ b/lib/active_merchant/billing/gateways/litle.rb @@ -171,11 +171,11 @@ def build_response(kind, litle_response, valid_responses=%w(000)) Response.new( valid_responses.include?(detail['response']), detail['message'], - { :litleOnlineResponse => response }, - :authorization => authorization_from(detail, kind), - :avs_result => { :code => fraud['avs'] }, - :cvv_result => fraud['cvv'], - :test => test? + { litleOnlineResponse: response, response_code: detail['response'] }, + authorization: authorization_from(detail, kind), + avs_result: { :code => fraud['avs'] }, + cvv_result: fraud['cvv'], + test: test? ) else Response.new(false, response['message'], :litleOnlineResponse => response, :test => test?) diff --git a/lib/active_merchant/billing/gateways/merchant_warrior.rb b/lib/active_merchant/billing/gateways/merchant_warrior.rb index 9ee22a3c3b1..8b22c837b68 100644 --- a/lib/active_merchant/billing/gateways/merchant_warrior.rb +++ b/lib/active_merchant/billing/gateways/merchant_warrior.rb @@ -75,7 +75,7 @@ def add_transaction(post, identification) end def add_address(post, options) - return unless(address = options[:address]) + return unless(address = (options[:billing_address] || options[:address])) post['customerName'] = address[:name] post['customerCountry'] = address[:country] @@ -86,7 +86,7 @@ def add_address(post, options) end def add_product(post, options) - post['transactionProduct'] = options[:transaction_product] + post['transactionProduct'] = options[:description] end def add_payment_method(post, payment_method) diff --git a/lib/active_merchant/billing/gateways/mercury.rb b/lib/active_merchant/billing/gateways/mercury.rb index 4f969015904..12faffb2c02 100644 --- a/lib/active_merchant/billing/gateways/mercury.rb +++ b/lib/active_merchant/billing/gateways/mercury.rb @@ -77,6 +77,11 @@ def void(authorization, options={}) commit('VoidSale', request) end + def store(credit_card, options={}) + request = build_card_lookup_request(credit_card, options) + commit('CardLookup', request) + end + private def build_non_authorized_request(action, money, credit_card, options) @@ -129,6 +134,23 @@ def build_authorized_request(action, money, authorization, credit_card, options) xml = xml.target! end + def build_card_lookup_request(credit_card, options) + xml = Builder::XmlMarkup.new + + xml.tag! "TStream" do + xml.tag! "Transaction" do + xml.tag! 'TranType', 'CardLookup' + xml.tag! 'RecordNo', 'RecordNumberRequested' + xml.tag! 'Frequency', 'OneTime' + + xml.tag! 'Memo', options[:description] + add_customer_data(xml, options) + add_credit_card(xml, credit_card, options) + end + end + xml.target! + end + def add_invoice(xml, invoice_no, ref_no, options) if /^\d+$/ !~ invoice_no.to_s raise ArgumentError.new("order_id '#{invoice_no}' is not numeric as required by Mercury") diff --git a/lib/active_merchant/billing/gateways/moneris.rb b/lib/active_merchant/billing/gateways/moneris.rb index 5ff1121c316..68588262f80 100644 --- a/lib/active_merchant/billing/gateways/moneris.rb +++ b/lib/active_merchant/billing/gateways/moneris.rb @@ -167,7 +167,7 @@ def crediting_params(authorization, options = {}) }.merge(options) end - # Splits an +authorization+ param and retrives the order id and + # Splits an +authorization+ param and retrieves the order id and # transaction number in that order. def split_authorization(authorization) if authorization.nil? || authorization.empty? || authorization !~ /;/ diff --git a/lib/active_merchant/billing/gateways/moneris_us.rb b/lib/active_merchant/billing/gateways/moneris_us.rb index 92feec27397..2113e166ce4 100644 --- a/lib/active_merchant/billing/gateways/moneris_us.rb +++ b/lib/active_merchant/billing/gateways/moneris_us.rb @@ -111,7 +111,7 @@ def crediting_params(authorization, options = {}) }.merge(options) end - # Splits an +authorization+ param and retrives the order id and + # Splits an +authorization+ param and retrieves the order id and # transaction number in that order. def split_authorization(authorization) if authorization.nil? || authorization.empty? || authorization !~ /;/ diff --git a/lib/active_merchant/billing/gateways/nab_transact.rb b/lib/active_merchant/billing/gateways/nab_transact.rb index 456d582b343..8e75cbf5943 100644 --- a/lib/active_merchant/billing/gateways/nab_transact.rb +++ b/lib/active_merchant/billing/gateways/nab_transact.rb @@ -127,11 +127,13 @@ def build_reference_request(money, reference, options) xml.tag! 'purchaseOrderNo', order_id xml.tag! 'preauthID', preauth_id + add_metadata(xml, options) + xml.target! end #Generate payment request XML - # - API is set to allow multiple Txn's but currentlu only allows one + # - API is set to allow multiple Txn's but currently only allows one # - txnSource = 23 - (XML) def build_request(action, body) xml = Builder::XmlMarkup.new diff --git a/lib/active_merchant/billing/gateways/netpay.rb b/lib/active_merchant/billing/gateways/netpay.rb index babf9542502..b950d33073b 100644 --- a/lib/active_merchant/billing/gateways/netpay.rb +++ b/lib/active_merchant/billing/gateways/netpay.rb @@ -18,7 +18,7 @@ module Billing #:nodoc: # transaction. After this, a refund should be performed instead. # # In addition to the regular ActiveMerchant transaction options, NETPAY - # also supports a `:mode` parameter. This allows testing to be peformed + # also supports a `:mode` parameter. This allows testing to be performed # in production and force specific results. # # * 'P' - Production diff --git a/lib/active_merchant/billing/gateways/orbital.rb b/lib/active_merchant/billing/gateways/orbital.rb index c26ebbac4a9..85eacc75a5e 100644 --- a/lib/active_merchant/billing/gateways/orbital.rb +++ b/lib/active_merchant/billing/gateways/orbital.rb @@ -80,6 +80,25 @@ class OrbitalGateway < Gateway "EUR" => '978' } + CURRENCY_EXPONENTS = { + "AUD" => '2', + "CAD" => '2', + "CZK" => '2', + "DKK" => '2', + "HKD" => '2', + "ICK" => '2', + "JPY" => '0', + "MXN" => '2', + "NZD" => '2', + "NOK" => '2', + "SGD" => '2', + "SEK" => '2', + "CHF" => '2', + "GBP" => '2', + "USD" => '2', + "EUR" => '2' + } + # INDUSTRY TYPES ECOMMERCE_TRANSACTION = 'EC' RECURRING_PAYMENT_TRANSACTION = 'RC' @@ -146,7 +165,7 @@ def initialize(options = {}) # A – Authorization request def authorize(money, creditcard, options = {}) order = build_new_order_xml(AUTH_ONLY, money, options) do |xml| - add_creditcard(xml, creditcard, options[:currency]) unless creditcard.nil? && options[:profile_txn] + add_creditcard(xml, creditcard, options[:currency]) add_address(xml, creditcard, options) if @options[:customer_profiles] add_customer_data(xml, options) @@ -159,7 +178,7 @@ def authorize(money, creditcard, options = {}) # AC – Authorization and Capture def purchase(money, creditcard, options = {}) order = build_new_order_xml(AUTH_AND_CAPTURE, money, options) do |xml| - add_creditcard(xml, creditcard, options[:currency]) unless creditcard.nil? && options[:profile_txn] + add_creditcard(xml, creditcard, options[:currency]) add_address(xml, creditcard, options) if @options[:customer_profiles] add_customer_data(xml, options) @@ -327,11 +346,13 @@ def add_customer_address(xml, options) end def add_creditcard(xml, creditcard, currency=nil) - xml.tag! :AccountNum, creditcard.number - xml.tag! :Exp, expiry_date(creditcard) + unless creditcard.nil? + xml.tag! :AccountNum, creditcard.number + xml.tag! :Exp, expiry_date(creditcard) + end xml.tag! :CurrencyCode, currency_code(currency) - xml.tag! :CurrencyExponent, '2' # Will need updating to support currencies such as the Yen. + xml.tag! :CurrencyExponent, currency_exponents(currency) # If you are trying to collect a Card Verification Number # (CardSecVal) for a Visa or Discover transaction, pass one of these values: @@ -342,17 +363,19 @@ def add_creditcard(xml, creditcard, currency=nil) # Null-fill this attribute OR # Do not submit the attribute at all. # - http://download.chasepaymentech.com/docs/orbital/orbital_gateway_xml_specification.pdf - if %w( visa discover ).include?(creditcard.brand) - xml.tag! :CardSecValInd, (creditcard.verification_value? ? '1' : '9') + unless creditcard.nil? + if %w( visa discover ).include?(creditcard.brand) + xml.tag! :CardSecValInd, (creditcard.verification_value? ? '1' : '9') + end + xml.tag! :CardSecVal, creditcard.verification_value if creditcard.verification_value? end - xml.tag! :CardSecVal, creditcard.verification_value if creditcard.verification_value? end def add_refund(xml, currency=nil) xml.tag! :AccountNum, nil xml.tag! :CurrencyCode, currency_code(currency) - xml.tag! :CurrencyExponent, '2' # Will need updating to support currencies such as the Yen. + xml.tag! :CurrencyExponent, currency_exponents(currency) end def add_managed_billing(xml, options) @@ -540,6 +563,10 @@ def currency_code(currency) CURRENCY_CODES[(currency || self.default_currency)].to_s end + def currency_exponents(currency) + CURRENCY_EXPONENTS[(currency || self.default_currency)].to_s + end + def expiry_date(credit_card) "#{format(credit_card.month, :two_digits)}#{format(credit_card.year, :two_digits)}" end diff --git a/lib/active_merchant/billing/gateways/pac_net_raven.rb b/lib/active_merchant/billing/gateways/pac_net_raven.rb new file mode 100644 index 00000000000..e6b9a8a1416 --- /dev/null +++ b/lib/active_merchant/billing/gateways/pac_net_raven.rb @@ -0,0 +1,187 @@ +module ActiveMerchant #:nodoc: + module Billing #:nodoc: + class PacNetRavenGateway < Gateway + self.test_url = 'https://demo.deepcovelabs.com/realtime/' + self.live_url = 'https://raven.pacnetservices.com/realtime/' + + self.supported_countries = ['US'] + self.supported_cardtypes = [:visa, :master, :american_express, :discover] + self.money_format = :cents + self.default_currency = 'USD' + self.homepage_url = 'http://www.pacnetservices.com/' + self.display_name = 'Raven PacNet' + + def initialize(options = {}) + requires!(options, :user, :secret, :prn) + super + end + + def authorize(money, creditcard, options = {}) + post = {} + add_creditcard(post, creditcard) + add_currency_code(post, money, options) + add_address(post, options) + post['PRN'] = @options[:prn] + + commit('cc_preauth', money, post) + end + + def purchase(money, creditcard, options = {}) + post = {} + add_currency_code(post, money, options) + add_creditcard(post, creditcard) + add_address(post, options) + post['PRN'] = @options[:prn] + + commit('cc_debit', money, post) + end + + def void(authorization, options = {}) + post = {} + post['TrackingNumber'] = authorization + post['PymtType'] = options[:pymt_type] || 'cc_debit' + + commit('void', nil, post) + end + + def capture(money, authorization, options = {}) + post = {} + post['PreauthNumber'] = authorization + post['PRN'] = @options[:prn] + add_currency_code(post, money, options) + + commit('cc_settle', money, post) + end + + def refund(money, template_number, options = {}) + post = {} + post['PRN'] = @options[:prn] + post['TemplateNumber'] = template_number + add_currency_code(post, money, options) + + commit('cc_refund', money, post) + end + + private + + def add_creditcard(post, creditcard) + post['CardNumber'] = creditcard.number + post['Expiry'] = expdate(creditcard) + post['CVV2'] = creditcard.verification_value if creditcard.verification_value + end + + def add_currency_code(post, money, options) + post['Currency'] = options[:currency] || currency(money) + end + + def add_address(post, options) + if address = options[:billing_address] || options[:address] + post['BillingStreetAddressLineOne'] = address[:address1].to_s + post['BillingStreetAddressLineFour'] = address[:address2].to_s + post['BillingPostalCode'] = address[:zip].to_s + end + end + + def parse(body) + Hash[body.split('&').map{|x| x.split('=').map{|x| CGI.unescape(x)}}] + end + + def commit(action, money, parameters) + parameters['Amount'] = amount(money) unless action == 'void' + + data = ssl_post url(action), post_data(action, parameters) + + response = parse(data) + response[:action] = action + + message = message_from(response) + + test_mode = test? || message =~ /TESTMODE/ + + Response.new(success?(response), message, response, + :test => test_mode, + :authorization => response['TrackingNumber'], + :fraud_review => fraud_review?(response), + :avs_result => { :postal_match => response['AVSPostalResponseCode'], :street_match => response['AVSAddressResponseCode'] }, + :cvv_result => response['CVV2ResponseCode'] + ) + end + + def url(action) + (test? ? self.test_url : self.live_url) + endpoint(action) + end + + def endpoint(action) + return 'void' if action == 'void' + 'submit' + end + + def fraud_review?(response) + false + end + + def success?(response) + if %w(cc_settle cc_debit cc_preauth cc_refund).include?(response[:action]) + !response['ApprovalCode'].nil? and response['ErrorCode'].nil? and response['Status'] == 'Approved' + elsif response[:action] = 'void' + !response['ApprovalCode'].nil? and response['ErrorCode'].nil? and response['Status'] == 'Voided' + end + end + + def message_from(response) + return response['Message'] if response['Message'] + + if response['Status'] == 'Approved' + "This transaction has been approved" + elsif response['Status'] == 'Declined' + "This transaction has been declined" + elsif response['Status'] == 'Voided' + "This transaction has been voided" + else + response['Status'] + end + end + + def post_data(action, parameters = {}) + post = {} + + post['PymtType'] = action + post['RAPIVersion'] = '2' + post['UserName'] = @options[:user] + post['Timestamp'] = timestamp + post['RequestID'] = request_id + post['Signature'] = signature(action, post, parameters) + + request = post.merge(parameters).collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&") + request + end + + def timestamp + Time.now.strftime("%Y-%m-%dT%H:%M:%S.Z") + end + + def request_id + (0...21).map{(65+rand(26)).chr}.join.downcase + end + + def signature(action, post, parameters = {}) + string = if %w(cc_settle cc_debit cc_preauth cc_refund).include?(action) + post['UserName'] + post['Timestamp'] + post['RequestID'] + post['PymtType'] + parameters['Amount'].to_s + parameters['Currency'] + elsif action == 'void' + post['UserName'] + post['Timestamp'] + post['RequestID'] + parameters['TrackingNumber'] + else + post['UserName'] + end + Digest::HMAC.hexdigest(string, @options[:secret], Digest::SHA1) + end + + def expdate(creditcard) + year = sprintf("%.4i", creditcard.year) + month = sprintf("%.2i", creditcard.month) + + "#{month}#{year[-2..-1]}" + end + end + end +end + diff --git a/lib/active_merchant/billing/gateways/pay_junction.rb b/lib/active_merchant/billing/gateways/pay_junction.rb index ceb2e184d93..ada083f48ca 100644 --- a/lib/active_merchant/billing/gateways/pay_junction.rb +++ b/lib/active_merchant/billing/gateways/pay_junction.rb @@ -73,16 +73,16 @@ module Billing #:nodoc: # # PayJunction Field ActiveMerchant Use # - # dc_logon provide as :login value to gateway instantation + # dc_logon provide as :login value to gateway instantiation # dc_password provide as :password value to gateway instantiation # # dc_name will be retrieved from credit_card.name - # dc_first_name :first_name on CreditCard object instantation - # dc_last_name :last_name on CreditCard object instantation - # dc_number :number on CreditCard object instantation - # dc_expiration_month :month on CreditCard object instantation - # dc_expiration_year :year on CreditCard object instantation - # dc_verification_number :verification_value on CC object instantation + # dc_first_name :first_name on CreditCard object instantiation + # dc_last_name :last_name on CreditCard object instantiation + # dc_number :number on CreditCard object instantiation + # dc_expiration_month :month on CreditCard object instantiation + # dc_expiration_year :year on CreditCard object instantiation + # dc_verification_number :verification_value on CC object instantiation # # dc_transaction_amount include as argument to method for your transaction type # dc_transaction_type do nothing, set by your transaction type diff --git a/lib/active_merchant/billing/gateways/payex.rb b/lib/active_merchant/billing/gateways/payex.rb new file mode 100644 index 00000000000..0aaf0f33bec --- /dev/null +++ b/lib/active_merchant/billing/gateways/payex.rb @@ -0,0 +1,402 @@ +require "nokogiri" +module ActiveMerchant #:nodoc: + module Billing #:nodoc: + class PayexGateway < Gateway + self.live_url = 'https://external.payex.com/' + self.test_url = 'https://test-external.payex.com/' + + self.money_format = :cents + self.supported_countries = ['SE', 'NO', 'DK'] + self.supported_cardtypes = [:visa, :master, :american_express, :discover] + self.homepage_url = 'http://payex.com/' + self.display_name = 'Payex' + self.default_currency = "EUR" + + # NOTE: the PurchaseCC uses a different url for test transactions + TEST_CONFINED_URL = 'https://test-confined.payex.com/' + + TRANSACTION_STATUS = { + sale: '0', + initialize: '1', + credit: '2', + authorize: '3', + cancel: '4', + failure: '5', + capture: '6', + } + + SOAP_ACTIONS = { + initialize: { name: 'Initialize8', url: 'pxorder/pxorder.asmx', xmlns: 'http://external.payex.com/PxOrder/' }, + purchasecc: { name: 'PurchaseCC', url: 'pxconfined/pxorder.asmx', xmlns: 'http://confined.payex.com/PxOrder/', confined: true}, + cancel: { name: 'Cancel2', url: 'pxorder/pxorder.asmx', xmlns: 'http://external.payex.com/PxOrder/' }, + capture: { name: 'Capture5', url: 'pxorder/pxorder.asmx', xmlns: 'http://external.payex.com/PxOrder/' }, + credit: { name: 'Credit5', url: 'pxorder/pxorder.asmx', xmlns: 'http://external.payex.com/PxOrder/' }, + create_agreement: { name: 'CreateAgreement3', url: 'pxagreement/pxagreement.asmx', xmlns: 'http://external.payex.com/PxAgreement/' }, + delete_agreement: { name: 'DeleteAgreement', url: 'pxagreement/pxagreement.asmx', xmlns: 'http://external.payex.com/PxAgreement/' }, + autopay: { name: 'AutoPay3', url: 'pxagreement/pxagreement.asmx', xmlns: 'http://external.payex.com/PxAgreement/' }, + } + + def initialize(options = {}) + requires!(options, :account, :encryption_key) + super + end + + # Public: Send an authorize Payex request + # + # amount - The monetary amount of the transaction in cents. + # payment_method - The Active Merchant payment method or the +store+ authorization for stored transactions. + # options - A standard ActiveMerchant options hash: + # :currency - Three letter currency code for the transaction (default: "EUR") + # :order_id - The unique order ID for this transaction (required). + # :product_number - The merchant product number (default: '1'). + # :description - The merchant description for this product (default: The :order_id). + # :ip - The client IP address (default: '127.0.0.1'). + # :vat - The vat amount (optional). + # + # Returns an ActiveMerchant::Billing::Response object + def authorize(amount, payment_method, options = {}) + requires!(options, :order_id) + amount = amount(amount) + if payment_method.respond_to?(:number) + # credit card authorization + MultiResponse.new.tap do |r| + r.process {send_initialize(amount, true, options)} + r.process {send_purchasecc(payment_method, r.params['orderref'])} + end + else + # stored authorization + send_autopay(amount, payment_method, true, options) + end + + end + + # Public: Send a purchase Payex request + # + # amount - The monetary amount of the transaction in cents. + # payment_method - The Active Merchant payment method or the +store+ authorization for stored transactions. + # options - A standard ActiveMerchant options hash: + # :currency - Three letter currency code for the transaction (default: "EUR") + # :order_id - The unique order ID for this transaction (required). + # :product_number - The merchant product number (default: '1'). + # :description - The merchant description for this product (default: The :order_id). + # :ip - The client IP address (default: '127.0.0.1'). + # :vat - The vat amount (optional). + # + # Returns an ActiveMerchant::Billing::Response object + def purchase(amount, payment_method, options = {}) + requires!(options, :order_id) + amount = amount(amount) + if payment_method.respond_to?(:number) + # credit card purchase + MultiResponse.new.tap do |r| + r.process {send_initialize(amount, false, options)} + r.process {send_purchasecc(payment_method, r.params['orderref'])} + end + else + # stored purchase + send_autopay(amount, payment_method, false, options) + end + end + + # Public: Capture money from a previously authorized transaction + # + # money - The amount to capture + # authorization - The authorization token from the authorization request + # + # Returns an ActiveMerchant::Billing::Response object + def capture(money, authorization, options = {}) + amount = amount(money) + send_capture(amount, authorization) + end + + # Public: Voids an authorize transaction + # + # authorization - The authorization returned from the successful authorize transaction. + # options - A standard ActiveMerchant options hash + # + # Returns an ActiveMerchant::Billing::Response object + def void(authorization, options={}) + send_cancel(authorization) + end + + # Public: Refunds a purchase transaction + # + # money - The amount to refund + # authorization - The authorization token from the purchase request. + # options - A standard ActiveMerchant options hash: + # :order_id - The unique order ID for this transaction (required). + # :vat_amount - The vat amount (optional). + # + # Returns an ActiveMerchant::Billing::Response object + def refund(money, authorization, options = {}) + requires!(options, :order_id) + amount = amount(money) + send_credit(authorization, amount, options) + end + + # Public: Stores a credit card and creates a Payex agreement with a customer + # + # creditcard - The credit card to store. + # options - A standard ActiveMerchant options hash: + # :order_id - The unique order ID for this transaction (required). + # :merchant_ref - A reference that links this agreement to something the merchant takes money for (default: '1') + # :currency - Three letter currency code for the transaction (default: "EUR") + # :product_number - The merchant product number (default: '1'). + # :description - The merchant description for this product (default: The :order_id). + # :ip - The client IP address (default: '127.0.0.1'). + # :max_amount - The maximum amount to allow to be charged (default: 100000). + # :vat - The vat amount (optional). + # + # Returns an ActiveMerchant::Billing::Response object where the authorization is set to the agreement_ref which is used for stored payments. + def store(creditcard, options = {}) + requires!(options, :order_id) + amount = amount(1) # 1 cent for authorization + MultiResponse.run(:first) do |r| + r.process {send_create_agreement(options)} + r.process {send_initialize(amount, true, options.merge({agreement_ref: r.authorization}))} + order_ref = r.params['orderref'] + r.process {send_purchasecc(creditcard, order_ref)} + end + end + + # Public: Unstores a customer's credit card and deletes their Payex agreement. + # + # authorization - The authorization token from the store request. + # + # Returns an ActiveMerchant::Billing::Response object + def unstore(authorization, options = {}) + send_delete_agreement(authorization) + end + + private + + def send_initialize(amount, is_auth, options = {}) + properties = { + accountNumber: @options[:account], + purchaseOperation: is_auth ? 'AUTHORIZATION' : 'SALE', + price: amount, + priceArgList: nil, + currency: (options[:currency] || default_currency), + vat: options[:vat] || 0, + orderID: options[:order_id], + productNumber: options[:product_number] || '1', + description: options[:description] || options[:order_id], + clientIPAddress: options[:client_ip_address] || '127.0.0.1', + clientIdentifier: nil, + additionalValues: nil, + externalID: nil, + returnUrl: 'http://example.net', # set to dummy value since this is not used but is required + view: 'CREDITCARD', + agreementRef: options[:agreement_ref], # this is used to attach a stored agreement to a transaction as part of the store card + cancelUrl: nil, + clientLanguage: nil + } + hash_fields = [:accountNumber, :purchaseOperation, :price, :priceArgList, :currency, :vat, :orderID, + :productNumber, :description, :clientIPAddress, :clientIdentifier, :additionalValues, + :externalID, :returnUrl, :view, :agreementRef, :cancelUrl, :clientLanguage] + add_request_hash(properties, hash_fields) + soap_action = SOAP_ACTIONS[:initialize] + request = build_xml_request(soap_action, properties) + commit(soap_action, request) + end + + def send_purchasecc(payment_method, order_ref) + properties = { + accountNumber: @options[:account], + orderRef: order_ref, + transactionType: 1, # online payment + cardNumber: payment_method.number, + cardNumberExpireMonth: "%02d" % payment_method.month, + cardNumberExpireYear: "%02d" % payment_method.year, + cardHolderName: payment_method.name, + cardNumberCVC: payment_method.verification_value + } + hash_fields = [:accountNumber, :orderRef, :transactionType, :cardNumber, :cardNumberExpireMonth, + :cardNumberExpireYear, :cardNumberCVC, :cardHolderName] + add_request_hash(properties, hash_fields) + + soap_action = SOAP_ACTIONS[:purchasecc] + request = build_xml_request(soap_action, properties) + commit(soap_action, request) + end + + def send_autopay(amount, authorization, is_auth, options = {}) + properties = { + accountNumber: @options[:account], + agreementRef: authorization, + price: amount, + productNumber: options[:product_number] || '1', + description: options[:description] || options[:order_id], + orderId: options[:order_id], + purchaseOperation: is_auth ? 'AUTHORIZATION' : 'SALE', + currency: (options[:currency] || default_currency), + } + hash_fields = [:accountNumber, :agreementRef, :price, :productNumber, :description, :orderId, :purchaseOperation, :currency] + add_request_hash(properties, hash_fields) + + soap_action = SOAP_ACTIONS[:autopay] + request = build_xml_request(soap_action, properties) + commit(soap_action, request) + end + + def send_capture(amount, transaction_number, options = {}) + properties = { + accountNumber: @options[:account], + transactionNumber: transaction_number, + amount: amount, + orderId: options[:order_id] || '', + vatAmount: options[:vat_amount] || 0, + additionalValues: '' + } + hash_fields = [:accountNumber, :transactionNumber, :amount, :orderId, :vatAmount, :additionalValues] + add_request_hash(properties, hash_fields) + + soap_action = SOAP_ACTIONS[:capture] + request = build_xml_request(soap_action, properties) + commit(soap_action, request) + end + + def send_credit(transaction_number, amount, options = {}) + properties = { + accountNumber: @options[:account], + transactionNumber: transaction_number, + amount: amount, + orderId: options[:order_id], + vatAmount: options[:vat_amount] || 0, + additionalValues: '' + } + hash_fields = [:accountNumber, :transactionNumber, :amount, :orderId, :vatAmount, :additionalValues] + add_request_hash(properties, hash_fields) + + soap_action = SOAP_ACTIONS[:credit] + request = build_xml_request(soap_action, properties) + commit(soap_action, request) + end + + def send_cancel(transaction_number) + properties = { + accountNumber: @options[:account], + transactionNumber: transaction_number, + } + hash_fields = [:accountNumber, :transactionNumber] + add_request_hash(properties, hash_fields) + + soap_action = SOAP_ACTIONS[:cancel] + request = build_xml_request(soap_action, properties) + commit(soap_action, request) + end + + def send_create_agreement(options) + properties = { + accountNumber: @options[:account], + merchantRef: options[:merchant_ref] || '1', + description: options[:description] || options[:order_id], + purchaseOperation: 'SALE', + maxAmount: options[:max_amount] || 100000, # default to 1,000 + notifyUrl: '', + startDate: options[:startDate] || '', + stopDate: options[:stopDate] || '' + } + hash_fields = [:accountNumber, :merchantRef, :description, :purchaseOperation, :maxAmount, :notifyUrl, :startDate, :stopDate] + add_request_hash(properties, hash_fields) + + soap_action = SOAP_ACTIONS[:create_agreement] + request = build_xml_request(soap_action, properties) + commit(soap_action, request) + end + + def send_delete_agreement(authorization) + properties = { + accountNumber: @options[:account], + agreementRef: authorization, + } + hash_fields = [:accountNumber, :agreementRef] + add_request_hash(properties, hash_fields) + + soap_action = SOAP_ACTIONS[:delete_agreement] + request = build_xml_request(soap_action, properties) + commit(soap_action, request) + end + + def url_for(soap_action) + base_url = test? ? (soap_action[:confined] ? TEST_CONFINED_URL : test_url) : live_url + File.join(base_url, soap_action[:url]) + end + + # this will add a hash to the passed in properties as required by Payex requests + def add_request_hash(properties, fields) + data = fields.map { |e| properties[e] } + data << @options[:encryption_key] + properties['hash_'] = Digest::MD5.hexdigest(data.join('')) + end + + def build_xml_request(soap_action, properties) + builder = Nokogiri::XML::Builder.new + builder.__send__('soap12:Envelope', {'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', + 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema', + 'xmlns:soap12' => 'http://www.w3.org/2003/05/soap-envelope'}) do |root| + root.__send__('soap12:Body') do |body| + body.__send__(soap_action[:name], xmlns: soap_action[:xmlns]) do |doc| + properties.each do |key, val| + doc.send(key, val) + end + end + end + end + builder.to_xml + end + + def parse(xml) + response = {} + + xmldoc = Nokogiri::XML(xml) + body = xmldoc.xpath("//soap:Body/*[1]")[0].inner_text + + doc = Nokogiri::XML(body) + + doc.root.xpath("*").each do |node| + if (node.elements.size == 0) + response[node.name.downcase.to_sym] = node.text + else + node.elements.each do |childnode| + name = "#{node.name.downcase}_#{childnode.name.downcase}" + response[name.to_sym] = childnode.text + end + end + end unless doc.root.nil? + + response + end + + # Commits all requests to the Payex soap endpoint + def commit(soap_action, request) + url = url_for(soap_action) + headers = { + 'Content-Type' => 'application/soap+xml; charset=utf-8', + 'Content-Length' => request.size.to_s + } + response = parse(ssl_post(url, request, headers)) + Response.new(success?(response), + message_from(response), + response, + test: test?, + authorization: build_authorization(response) + ) + end + + def build_authorization(response) + # agreementref is for the store transaction, everything else gets transactionnumber + response[:transactionnumber] || response[:agreementref] + end + + def success?(response) + response[:status_errorcode] == 'OK' && response[:transactionstatus] != TRANSACTION_STATUS[:failure] + end + + def message_from(response) + response[:status_description] + end + end + end +end + diff --git a/lib/active_merchant/billing/gateways/payflow.rb b/lib/active_merchant/billing/gateways/payflow.rb index 770c426d8fe..4f8c63271c9 100644 --- a/lib/active_merchant/billing/gateways/payflow.rb +++ b/lib/active_merchant/billing/gateways/payflow.rb @@ -19,21 +19,26 @@ def authorize(money, credit_card_or_reference, options = {}) commit(request, options) end - def purchase(money, credit_card_or_reference, options = {}) - request = build_sale_or_authorization_request(:purchase, money, credit_card_or_reference, options) + def purchase(money, funding_source, options = {}) + request = build_sale_or_authorization_request(:purchase, money, funding_source, options) commit(request, options) end - def credit(money, identification_or_credit_card, options = {}) - if identification_or_credit_card.is_a?(String) + def credit(money, funding_source, options = {}) + case + when funding_source.is_a?(String) deprecated CREDIT_DEPRECATION_MESSAGE # Perform referenced credit - refund(money, identification_or_credit_card, options) - else + refund(money, funding_source, options) + when funding_source.is_a?(CreditCard) # Perform non-referenced credit - request = build_credit_card_request(:credit, money, identification_or_credit_card, options) + request = build_credit_card_request(:credit, money, funding_source, options) + commit(request, options) + when funding_source.is_a?(Check) + request = build_check_request(:credit, money, funding_source, options) commit(request, options) + else raise ArgumentError, "Unsupported funding source provided" end end @@ -76,11 +81,15 @@ def express end private - def build_sale_or_authorization_request(action, money, credit_card_or_reference, options) - if credit_card_or_reference.is_a?(String) - build_reference_sale_or_authorization_request(action, money, credit_card_or_reference, options) - else - build_credit_card_request(action, money, credit_card_or_reference, options) + def build_sale_or_authorization_request(action, money, funding_source, options) + case + when funding_source.is_a?(String) + build_reference_sale_or_authorization_request(action, money, funding_source, options) + when funding_source.is_a?(CreditCard) + build_credit_card_request(action, money, funding_source, options) + when funding_source.is_a?(Check) + build_check_request(action, money, funding_source, options) + else raise ArgumentError, "Unsupported funding source provided" end end @@ -147,6 +156,31 @@ def build_credit_card_request(action, money, credit_card, options) xml.target! end + def build_check_request(action, money, check, options) + xml = Builder::XmlMarkup.new + xml.tag! TRANSACTIONS[action] do + xml.tag! 'PayData' do + xml.tag! 'Invoice' do + xml.tag! 'CustIP', options[:ip] unless options[:ip].blank? + xml.tag! 'InvNum', options[:order_id].to_s.gsub(/[^\w.]/, '') unless options[:order_id].blank? + xml.tag! 'Description', options[:description] unless options[:description].blank? + xml.tag! 'BillTo' do + xml.tag! 'Name', check.name + end + xml.tag! 'TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money) + end + xml.tag! 'Tender' do + xml.tag! 'ACH' do + xml.tag! 'AcctType', check.account_type == 'checking' ? 'C' : 'S' + xml.tag! 'AcctNum', check.account_number + xml.tag! 'ABA', check.routing_number + end + end + end + end + xml.target! + end + def add_credit_card(xml, credit_card) xml.tag! 'Card' do xml.tag! 'CardType', credit_card_type(credit_card) diff --git a/lib/active_merchant/billing/gateways/payment_express.rb b/lib/active_merchant/billing/gateways/payment_express.rb index 450b26e2661..290c593ca34 100644 --- a/lib/active_merchant/billing/gateways/payment_express.rb +++ b/lib/active_merchant/billing/gateways/payment_express.rb @@ -227,7 +227,7 @@ def add_address_verification_data(xml, options) end # The options hash may contain optional data which will be passed - # through the the specialized optional fields at PaymentExpress + # through the specialized optional fields at PaymentExpress # as follows: # # { diff --git a/lib/active_merchant/billing/gateways/paymill.rb b/lib/active_merchant/billing/gateways/paymill.rb index 12738d5ef78..3192d49ec3a 100644 --- a/lib/active_merchant/billing/gateways/paymill.rb +++ b/lib/active_merchant/billing/gateways/paymill.rb @@ -17,27 +17,11 @@ def initialize(options = {}) end def purchase(money, payment_method, options = {}) - case payment_method - when String - purchase_with_token(money, payment_method, options) - else - MultiResponse.run do |r| - r.process { save_card(payment_method) } - r.process { purchase_with_token(money, r.authorization, options) } - end - end + action_with_token(:purchase, money, payment_method, options) end def authorize(money, payment_method, options = {}) - case payment_method - when String - authorize_with_token(money, payment_method, options) - else - MultiResponse.run do |r| - r.process { save_card(payment_method) } - r.process { authorize_with_token(money, r.authorization, options) } - end - end + action_with_token(:authorize, money, payment_method, options) end def capture(money, authorization, options = {}) @@ -83,7 +67,7 @@ def commit(method, url, parameters=nil) raw_response = ssl_request(method, "https://api.paymill.com/v2/#{url}", post_data(parameters), headers) rescue ResponseError => e parsed = JSON.parse(e.response.body) - return Response.new(false, parsed['error'], parsed, {}) + return Response.new(false, response_message(parsed), parsed, {}) end response_from(raw_response) @@ -91,13 +75,13 @@ def commit(method, url, parameters=nil) def response_from(raw_response) parsed = JSON.parse(raw_response) - options = { :authorization => authorization_from(parsed), :test => (parsed['mode'] == 'test'), } - Response.new(true, 'Transaction approved', parsed, options) + succeeded = (parsed['data'] == []) || (parsed['data']['response_code'] == 20000) + Response.new(succeeded, response_message(parsed), parsed, options) end def authorization_from(parsed_response) @@ -110,13 +94,24 @@ def authorization_from(parsed_response) ].join(";") end + def action_with_token(action, money, payment_method, options) + case payment_method + when String + self.send("#{action}_with_token", money, payment_method, options) + else + MultiResponse.run do |r| + r.process { save_card(payment_method) } + r.process { self.send("#{action}_with_token", money, r.authorization, options) } + end + end + end + def purchase_with_token(money, card_token, options) post = {} add_amount(post, money, options) post[:token] = card_token post[:description] = options[:description] - post[:client] = options[:customer] commit(:post, 'transactions', post) end @@ -148,17 +143,12 @@ def save_card(credit_card) def response_for_save_from(raw_response) options = { :test => test? } - parsed = JSON.parse(raw_response.sub(/jsonPFunction\(/, '').sub(/\)\z/, '')) - if parsed['error'] - succeeded = false - message = parsed['error']['message'] - else - succeeded = parsed['transaction']['processing']['result'] == 'ACK' - message = parsed['transaction']['processing']['return']['message'] - options[:authorization] = parsed['transaction']['identification']['uniqueId'] if succeeded - end + parser = ResponseParser.new(raw_response, options) + parser.generate_response + end - Response.new(succeeded, message, parsed, options) + def parse_reponse(response) + JSON.parse(response.sub(/jsonPFunction\(/, '').sub(/\)\z/, '')) end def save_card_url @@ -184,6 +174,99 @@ def preauth(authorization) def transaction_id(authorization) authorization.split(';').first end + + RESPONSE_CODES = { + 10001 => "General undefined response.", + 10002 => "Still waiting on something.", + + 20000 => "General success response.", + + 40000 => "General problem with data.", + 40001 => "General problem with payment data.", + 40100 => "Problem with credit card data.", + 40101 => "Problem with cvv.", + 40102 => "Card expired or not yet valid.", + 40103 => "Limit exceeded.", + 40104 => "Card invalid.", + 40105 => "Expiry date not valid.", + 40106 => "Credit card brand required.", + 40200 => "Problem with bank account data.", + 40201 => "Bank account data combination mismatch.", + 40202 => "User authentication failed.", + 40300 => "Problem with 3d secure data.", + 40301 => "Currency / amount mismatch", + 40400 => "Problem with input data.", + 40401 => "Amount too low or zero.", + 40402 => "Usage field too long.", + 40403 => "Currency not allowed.", + + 50000 => "General problem with backend.", + 50001 => "Country blacklisted.", + 50100 => "Technical error with credit card.", + 50101 => "Error limit exceeded.", + 50102 => "Card declined by authorization system.", + 50103 => "Manipulation or stolen card.", + 50104 => "Card restricted.", + 50105 => "Invalid card configuration data.", + 50200 => "Technical error with bank account.", + 50201 => "Card blacklisted.", + 50300 => "Technical error with 3D secure.", + 50400 => "Decline because of risk issues.", + 50500 => "General timeout.", + 50501 => "Timeout on side of the acquirer.", + 50502 => "Risk management transaction timeout.", + 50600 => "Duplicate transaction." + } + + def response_message(parsed_response) + return parsed_response["error"] if parsed_response["error"] + return "Transaction approved." if (parsed_response['data'] == []) + + code = parsed_response["data"]["response_code"] + RESPONSE_CODES[code] || code.to_s + end + + + class ResponseParser + def initialize(raw_response="", options={}) + @raw_response = raw_response + @options = options + end + + def generate_response + parse_response + if parsed['error'] + handle_response_parse_error + else + handle_response_correct_parsing + end + + Response.new(succeeded, message, parsed, options) + end + + private + attr_reader :raw_response, :parsed, :succeeded, :message, :options + + def parse_response + @parsed = JSON.parse(raw_response.sub(/jsonPFunction\(/, '').sub(/\)\z/, '')) + end + + def handle_response_parse_error + @succeeded = false + @message = parsed['error']['message'] + end + + def handle_response_correct_parsing + @message = parsed['transaction']['processing']['return']['message'] + if @succeeded = is_ack? + @options[:authorization] = parsed['transaction']['identification']['uniqueId'] + end + end + + def is_ack? + parsed['transaction']['processing']['result'] == 'ACK' + end + end end end end diff --git a/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb b/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb index 74fb4d4c91b..2fad6c2128c 100644 --- a/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +++ b/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb @@ -242,7 +242,7 @@ def authorize_transaction(transaction_id, money, options = {}) commit 'DoAuthorization', build_do_authorize(transaction_id, money, options) end - # The ManagePendingTransactionStatus API operation accepts or denys a + # The ManagePendingTransactionStatus API operation accepts or denies a # pending transaction held by Fraud Management Filters. # # ==== Parameters: diff --git a/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb b/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb index 157037cb176..c70308ddd0d 100644 --- a/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +++ b/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb @@ -12,7 +12,7 @@ module PaypalRecurringApi # This transaction creates a recurring payment profile # ==== Parameters # - # * money -- The amount to be charged to the customer at each interval as an Integer value in cents. + # * amount -- The amount to be charged to the customer at each interval as an Integer value in cents. # * credit_card -- The CreditCard details for the transaction. # * options -- A hash of parameters. # diff --git a/lib/active_merchant/billing/gateways/payscout.rb b/lib/active_merchant/billing/gateways/payscout.rb new file mode 100644 index 00000000000..b56c391fcce --- /dev/null +++ b/lib/active_merchant/billing/gateways/payscout.rb @@ -0,0 +1,171 @@ +module ActiveMerchant #:nodoc: + module Billing #:nodoc: + class PayscoutGateway < Gateway + self.live_url = self.test_url = 'https://secure.payscout.com/api/transact.php' + + self.supported_countries = ['US'] + self.supported_cardtypes = [:visa, :master, :american_express, :discover] + self.default_currency = 'USD' + self.homepage_url = 'http://www.payscout.com/' + self.display_name = 'Payscout' + + self.ssl_version = 'SSLv3' + + def initialize(options = {}) + requires!(options, :username, :password) + super + end + + def authorize(money, creditcard, options = {}) + post = {} + add_invoice(post, options) + add_creditcard(post, creditcard) + add_currency(post, money, options) + add_address(post, options) + + commit('auth', money, post) + end + + def purchase(money, creditcard, options = {}) + post = {} + add_invoice(post, options) + add_creditcard(post, creditcard) + add_currency(post, money, options) + add_address(post, options) + + commit('sale', money, post) + end + + def capture(money, authorization, options = {}) + post = {} + post[:transactionid] = authorization + + commit('capture', money, post) + end + + + def refund(money, authorization, options = {}) + post = {} + post[:transactionid] = authorization + + commit('refund', money, post) + end + + def void(authorization, options = {}) + post = {} + post[:transactionid] = authorization + + commit('void', nil, post) + end + + private + + def add_address(post, options) + if address = options[:billing_address] || options[:address] + post[:address1] = address[:address1].to_s + post[:address2] = address[:address2].to_s + post[:city] = address[:city].to_s + post[:state] = (address[:state].blank? ? 'n/a' : address[:state]) + post[:zip] = address[:zip].to_s + post[:country] = address[:country].to_s + post[:phone] = address[:phone].to_s + post[:fax] = address[:fax].to_s + post[:email] = address[:email].to_s + end + + if address = options[:shipping_address] + post[:shipping_firstname] = address[:first_name].to_s + post[:shipping_lastname] = address[:last_name].to_s + post[:shipping_company] = address[:company].to_s + post[:shipping_address1] = address[:address1].to_s + post[:shipping_address2] = address[:address2].to_s + post[:shipping_city] = address[:city].to_s + post[:shipping_country] = address[:country].to_s + post[:shipping_state] = (address[:state].blank? ? 'n/a' : address[:state]) + post[:shipping_zip] = address[:zip].to_s + post[:shipping_email] = address[:email].to_s + end + end + + def add_currency(post, money, options) + post[:currency] = options[:currency] || currency(money) + end + + def add_invoice(post, options) + post[:orderdescription] = options[:description] + post[:orderid] = options[:order_id] + end + + def add_creditcard(post, creditcard) + post[:ccnumber] = creditcard.number + post[:cvv] = creditcard.verification_value if creditcard.verification_value? + post[:ccexp] = expdate(creditcard) + post[:firstname] = creditcard.first_name + post[:lastname] = creditcard.last_name + end + + def parse(body) + Hash[body.split('&').map{|x|x.split('=')}] + end + + def commit(action, money, parameters) + parameters[:amount] = amount(money) unless action == 'void' + url = (test? ? self.test_url : self.live_url) + data = ssl_post(url, post_data(action, parameters)) + + response = parse(data) + response[:action] = action + + message = message_from(response) + test_mode = (test? || message =~ /TESTMODE/) + Response.new(success?(response), message, response, + :test => test_mode, + :authorization => response['transactionid'], + :fraud_review => fraud_review?(response), + :avs_result => { :code => response['avsresponse'] }, + :cvv_result => response['cvvresponse'] + ) + end + + def message_from(response) + case response['response'] + when '1' + 'The transaction has been approved' + when '2' + 'The transaction has been declined' + when '3' + response['responsetext'] + else + 'There was an error processing the transaction' + end + end + + def fraud_review?(response) + false + end + + def success?(response) + (response['response'] == '1') + end + + def post_data(action, parameters = {}) + post = {} + + post[:username] = @options[:username] + post[:password] = @options[:password] + post[:type] = action + + request = post.merge(parameters).collect { |key, value| "#{key}=#{URI.escape(value.to_s)}" }.join("&") + request + end + + def expdate(creditcard) + year = sprintf("%.4i", creditcard.year) + month = sprintf("%.2i", creditcard.month) + + "#{month}#{year[-2..-1]}" + end + end + end +end + diff --git a/lib/active_merchant/billing/gateways/pin.rb b/lib/active_merchant/billing/gateways/pin.rb index 8e1c2ffd0be..26e7cce06b2 100644 --- a/lib/active_merchant/billing/gateways/pin.rb +++ b/lib/active_merchant/billing/gateways/pin.rb @@ -90,7 +90,7 @@ def add_creditcard(post, creditcard) :expiry_month => creditcard.month, :expiry_year => creditcard.year, :cvc => creditcard.verification_value, - :name => "#{creditcard.first_name} #{creditcard.last_name}" + :name => creditcard.name ) elsif creditcard.kind_of?(String) if creditcard =~ /^card_/ diff --git a/lib/active_merchant/billing/gateways/realex.rb b/lib/active_merchant/billing/gateways/realex.rb index 06ebdabb0c5..f4404710e7f 100644 --- a/lib/active_merchant/billing/gateways/realex.rb +++ b/lib/active_merchant/billing/gateways/realex.rb @@ -12,7 +12,7 @@ module Billing # login - The unique id of the merchant # password - The secret is used to digitally sign the request # account - This is an optional third part of the authentication process - # and is used if the merchant wishes do distuinguish cc traffic from the different sources + # and is used if the merchant wishes do distinguish cc traffic from the different sources # by using a different account. This must be created in advance # # the Realex team decided to make the orderid unique per request, diff --git a/lib/active_merchant/billing/gateways/redsys.rb b/lib/active_merchant/billing/gateways/redsys.rb index 583e025393c..114a3ca7d4e 100644 --- a/lib/active_merchant/billing/gateways/redsys.rb +++ b/lib/active_merchant/billing/gateways/redsys.rb @@ -49,7 +49,7 @@ class RedsysGateway < Gateway self.default_currency = 'EUR' self.money_format = :cents - # Not all card types may be actived by the bank! + # Not all card types may be activated by the bank! self.supported_cardtypes = [:visa, :master, :american_express, :jcb, :diners_club] # Homepage URL of the gateway for reference diff --git a/lib/active_merchant/billing/gateways/secure_pay_au.rb b/lib/active_merchant/billing/gateways/secure_pay_au.rb index dd552114edb..db05999c1a8 100644 --- a/lib/active_merchant/billing/gateways/secure_pay_au.rb +++ b/lib/active_merchant/billing/gateways/secure_pay_au.rb @@ -108,8 +108,10 @@ def unstore(identification, options = {}) def build_purchase_request(money, credit_card, options) xml = Builder::XmlMarkup.new - xml.tag! 'amount', amount(money) - xml.tag! 'currency', options[:currency] || currency(money) + currency = options[:currency] || currency(money) + + xml.tag! 'amount', localized_amount(money, currency) + xml.tag! 'currency', currency xml.tag! 'purchaseOrderNo', options[:order_id].to_s.gsub(/[ ']/, '') xml.tag! 'CreditCardInfo' do diff --git a/lib/active_merchant/billing/gateways/so_easy_pay.rb b/lib/active_merchant/billing/gateways/so_easy_pay.rb new file mode 100644 index 00000000000..2227a74ebe4 --- /dev/null +++ b/lib/active_merchant/billing/gateways/so_easy_pay.rb @@ -0,0 +1,194 @@ +module ActiveMerchant #:nodoc: + module Billing #:nodoc: + class SoEasyPayGateway < Gateway + self.live_url = self.test_url = 'https://secure.soeasypay.com/gateway.asmx' + self.money_format = :cents + + self.supported_countries = [ + 'US', 'CA', 'AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', + 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', + 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE', 'GB', + 'IS', 'NO', 'CH' + ] + self.supported_cardtypes = [:visa, :master, :american_express, :discover, :maestro, :jcb, :solo, :diners_club] + self.homepage_url = 'http://www.soeasypay.com/' + self.display_name = 'SoEasyPay' + + def initialize(options = {}) + requires!(options, :login, :password) + super + end + + def authorize(money, payment_source, options = {}) + if payment_source.respond_to?(:number) + commit('AuthorizeTransaction', do_authorization(money, payment_source, options), options) + else + commit('ReauthorizeTransaction', do_reauthorization(money, payment_source, options), options) + end + end + + def purchase(money, payment_source, options = {}) + if payment_source.respond_to?(:number) + commit('SaleTransaction', do_sale(money, payment_source, options), options) + else + commit('RebillTransaction', do_rebill(money, payment_source, options), options) + end + end + + def capture(money, authorization, options = {}) + commit('CaptureTransaction', do_capture(money, authorization, options), options) + end + + def refund(money, authorization, options={}) + commit('RefundTransaction', do_refund(money, authorization, options), options) + end + + def void(authorization, options={}) + commit('CancelTransaction', do_void(authorization, options), options) + end + + private + + def do_authorization(money, card, options) + build_soap('AuthorizeTransaction') do |soap| + fill_credentials(soap, options) + fill_order_info(soap, money, options) + fill_cardholder(soap, card, options) + fill_card(soap, card) + end + end + + def do_sale(money, card, options) + build_soap('SaleTransaction') do |soap| + fill_credentials(soap, options) + fill_order_info(soap, money, options) + fill_cardholder(soap, card, options) + fill_card(soap, card) + end + end + + def do_reauthorization(money, authorization, options) + build_soap('ReauthorizeTransaction') do |soap| + fill_credentials(soap, options) + fill_order_info(soap, money, options) + fill_transaction_id(soap, authorization) + end + end + + def do_rebill(money, authorization, options) + build_soap('RebillTransaction') do |soap| + fill_credentials(soap, options) + fill_order_info(soap, money, options) + fill_transaction_id(soap, authorization) + end + end + + def do_capture(money, authorization, options) + build_soap('CaptureTransaction') do |soap| + fill_credentials(soap, options) + fill_order_info(soap, money, options, :no_currency) + fill_transaction_id(soap, authorization) + end + end + + def do_refund(money, authorization, options) + build_soap('RefundTransaction') do |soap| + fill_credentials(soap, options) + fill_order_info(soap, money, options, :no_currency) + fill_transaction_id(soap, authorization) + end + end + + def do_void(authorization, options) + build_soap('CancelTransaction') do |soap| + fill_credentials(soap, options) + fill_transaction_id(soap, authorization) + end + end + + def fill_credentials(soap, options) + soap.tag!('websiteID', @options[:login].to_s) + soap.tag!('password', @options[:password].to_s) + end + + def fill_cardholder(soap, card, options) + ch_info = options[:billing_address] || options[:address] + + soap.tag!('customerIP',options[:ip].to_s) + name = card.name || ch_info[:name] + soap.tag!('cardHolderName', name.to_s) + address = ch_info[:address1] || '' + address << ch_info[:address2] if ch_info[:address2] + soap.tag!('cardHolderAddress', address.to_s) + soap.tag!('cardHolderZipcode', ch_info[:zip].to_s) + soap.tag!('cardHolderCity', ch_info[:city].to_s) + soap.tag!('cardHolderState', ch_info[:state].to_s) + soap.tag!('cardHolderCountryCode', ch_info[:country].to_s) + soap.tag!('cardHolderPhone', ch_info[:phone].to_s) + soap.tag!('cardHolderEmail', options[:email].to_s) + end + + def fill_transaction_id(soap, transaction_id) + soap.tag!('transactionID', transaction_id.to_s) + end + + def fill_card(soap, card) + soap.tag!('cardNumber', card.number.to_s) + soap.tag!('cardSecurityCode', card.verification_value.to_s) + soap.tag!('cardExpireMonth', card.month.to_s.rjust(2, "0")) + soap.tag!('cardExpireYear', card.year.to_s) + end + + def fill_order_info(soap, money, options, skip_currency=false) + soap.tag!('orderID', options[:order_id].to_s) + soap.tag!('orderDescription', "Order #{options[:order_id]}") + soap.tag!('amount', amount(money).to_s) + soap.tag!('currency', (options[:currency] || currency(money)).to_s) unless skip_currency + end + + def parse(response, action) + result = {} + document = REXML::Document.new(response) + response_element = document.root.get_elements("//[@xsi:type='tns:#{action}Response']").first + response_element.elements.each do |element| + result[element.name.underscore] = element.text + end + result + end + + def commit(soap_action, soap, options) + headers = {"SOAPAction" => "\"urn:Interface##{soap_action}\"", + "Content-Type" => "text/xml; charset=utf-8"} + response_string = ssl_post(test? ? self.test_url : self.live_url, soap, headers) + response = parse(response_string, soap_action) + return Response.new(response['errorcode'] == '000', + response['errormessage'], + response, + :test => test?, + :authorization => response['transaction_id']) + end + + def build_soap(request) + retval = Builder::XmlMarkup.new(:indent => 2) + retval.instruct!(:xml, :version => '1.0', :encoding => 'utf-8') + retval.tag!('soap:Envelope', { + 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', + 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema', + 'xmlns:soapenc' => 'http://schemas.xmlsoap.org/soap/encoding/', + 'xmlns:tns' => 'urn:Interface', + 'xmlns:types' => 'urn:Interface/encodedTypes', + 'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/'}) do + retval.tag!('soap:Body', {'soap:encodingStyle'=>'http://schemas.xmlsoap.org/soap/encoding/'}) do + retval.tag!("tns:#{request}") do + retval.tag!("#{request}Request", {'xsi:type'=>"tns:#{request}Request"}) do + yield retval + end + end + end + end + retval.target! + end + + end + end +end diff --git a/lib/active_merchant/billing/gateways/spreedly_core.rb b/lib/active_merchant/billing/gateways/spreedly_core.rb index bc1e50505ba..5f26aa80808 100644 --- a/lib/active_merchant/billing/gateways/spreedly_core.rb +++ b/lib/active_merchant/billing/gateways/spreedly_core.rb @@ -93,7 +93,8 @@ def void(authorization, options={}) # credit_card - The CreditCard to store # options - A standard ActiveMerchant options hash def store(credit_card, options={}) - save_card(true, credit_card, options) + retain = (options.has_key?(:retain) ? options[:retain] : true) + save_card(retain, credit_card, options) end # Public: Redact the CreditCard in Spreedly. This wipes the sensitive @@ -118,7 +119,7 @@ def save_card(retain, credit_card, options) def purchase_with_token(money, payment_method_token, options) request = auth_purchase_request(money, payment_method_token, options) - commit("gateways/#{@options[:gateway_token]}/purchase.xml", request) + commit("gateways/#{options[:gateway_token] || @options[:gateway_token]}/purchase.xml", request) end def authorize_with_token(money, payment_method_token, options) @@ -137,6 +138,7 @@ def auth_purchase_request(money, payment_method_token, options) def add_invoice(doc, money, options) doc.amount amount(money) doc.currency_code(options[:currency] || currency(money) || default_currency) + doc.order_id(options[:order_id]) end def add_credit_card(doc, credit_card, options) diff --git a/lib/active_merchant/billing/gateways/stripe.rb b/lib/active_merchant/billing/gateways/stripe.rb index 0e06a2bd4d9..cf2af37b42c 100644 --- a/lib/active_merchant/billing/gateways/stripe.rb +++ b/lib/active_merchant/billing/gateways/stripe.rb @@ -40,7 +40,7 @@ def authorize(money, creditcard, options = {}) post = create_post_for_auth_or_purchase(money, creditcard, options) post[:capture] = "false" - commit(:post, 'charges', post, generate_meta(options)) + commit(:post, 'charges', post, generate_options(options)) end # To create a charge on a card or a token, call @@ -53,7 +53,7 @@ def authorize(money, creditcard, options = {}) def purchase(money, creditcard, options = {}) post = create_post_for_auth_or_purchase(money, creditcard, options) - commit(:post, 'charges', post, generate_meta(options)) + commit(:post, 'charges', post, generate_options(options)) end def capture(money, authorization, options = {}) @@ -69,7 +69,7 @@ def void(identification, options = {}) def refund(money, identification, options = {}) post = {:amount => amount(money)} - commit_options = generate_meta(options) + commit_options = generate_options(options) MultiResponse.run(:first) do |r| r.process { commit(:post, "charges/#{CGI.escape(identification)}/refund", post, commit_options) } @@ -97,28 +97,42 @@ def refund_application_fee(money, identification, options = {}) commit(:post, "application_fees/#{CGI.escape(identification)}/refund", post, options) end + # Note: creating a new credit card will not change the customer's existing default credit card (use :set_default => true) def store(creditcard, options = {}) post = {} add_creditcard(post, creditcard, options) post[:description] = options[:description] post[:email] = options[:email] - path = if options[:customer] - "customers/#{CGI.escape(options[:customer])}" + commit_options = generate_options(options) + if options[:customer] + MultiResponse.run(:first) do |r| + r.process { commit(:post, "customers/#{CGI.escape(options[:customer])}/cards", post, commit_options) } + + return r unless options[:set_default] and r.success? and !r.params["id"].blank? + + r.process { update_customer(options[:customer], :default_card => r.params["id"]) } + end else - 'customers' + commit(:post, 'customers', post, commit_options) end - - commit(:post, path, post, generate_meta(options)) end def update(customer_id, creditcard, options = {}) - options = options.merge(:customer => customer_id) + options = options.merge(:customer => customer_id, :set_default => true) store(creditcard, options) end - def unstore(customer_id, options = {}) - commit(:delete, "customers/#{CGI.escape(customer_id)}", nil, generate_meta(options)) + def update_customer(customer_id, options = {}) + commit(:post, "customers/#{CGI.escape(customer_id)}", options, generate_options(options)) + end + + def unstore(customer_id, card_id = nil, options = {}) + if card_id.nil? + commit(:delete, "customers/#{CGI.escape(customer_id)}", nil, generate_options(options)) + else + commit(:delete, "customers/#{CGI.escape(customer_id)}/cards/#{CGI.escape(card_id)}", nil, generate_options(options)) + end end private @@ -127,9 +141,10 @@ def create_post_for_auth_or_purchase(money, creditcard, options) post = {} add_amount(post, money, options) add_creditcard(post, creditcard, options) - add_customer(post, options) + add_customer(post, creditcard, options) add_customer_data(post,options) - post[:description] = options[:description] || options[:email] + post[:description] = options[:description] + post[:metadata] = { email: options[:email] } if options[:email] add_flags(post, options) add_application_fee(post, options) post @@ -145,7 +160,7 @@ def add_application_fee(post, options) end def add_customer_data(post, options) - metadata_options = [:description,:browser_ip,:user_agent,:referrer] + metadata_options = [:description, :ip, :user_agent, :referrer] post.update(options.slice(*metadata_options)) post[:external_id] = options[:order_id] @@ -189,8 +204,8 @@ def add_creditcard(post, creditcard, options) end end - def add_customer(post, options) - post[:customer] = options[:customer] if options[:customer] + def add_customer(post, creditcard, options) + post[:customer] = options[:customer] if options[:customer] && !creditcard.respond_to?(:number) end def add_flags(post, options) @@ -224,6 +239,11 @@ def post_data(params) end.compact.join("&") end + def generate_options(raw_options) + options = generate_meta(raw_options) + options.merge!(raw_options.slice(:version, :key)) + end + def generate_meta(options) {:meta => {:ip => options[:ip]}} end @@ -234,18 +254,19 @@ def headers(options = {}) :lang => 'ruby', :lang_version => "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})", :platform => RUBY_PLATFORM, - :publisher => 'active_merchant', - :uname => (RUBY_PLATFORM =~ /linux|darwin/i ? `uname -a 2>/dev/null`.strip : nil) + :publisher => 'active_merchant' }) key = options[:key] || @api_key - { + headers = { "Authorization" => "Basic " + Base64.encode64(key.to_s + ":").strip, "User-Agent" => "Stripe/v1 ActiveMerchantBindings/#{ActiveMerchant::VERSION}", "X-Stripe-Client-User-Agent" => @@ua, "X-Stripe-Client-User-Metadata" => options[:meta].to_json } + headers.merge!("Stripe-Version" => options[:version]) if options[:version] + headers end def commit(method, url, parameters=nil, options = {}) diff --git a/lib/active_merchant/billing/gateways/swipe_checkout.rb b/lib/active_merchant/billing/gateways/swipe_checkout.rb new file mode 100644 index 00000000000..df235de2ad1 --- /dev/null +++ b/lib/active_merchant/billing/gateways/swipe_checkout.rb @@ -0,0 +1,158 @@ +require 'json' + +module ActiveMerchant #:nodoc: + module Billing #:nodoc: + class SwipeCheckoutGateway < Gateway + TRANSACTION_APPROVED_MSG = 'Transaction approved' + TRANSACTION_DECLINED_MSG = 'Transaction declined' + + LIVE_URLS = { + 'NZ' => 'https://api.swipehq.com', + 'CA' => 'https://api.swipehq.ca' + } + self.test_url = 'https://api.swipehq.com' + + TRANSACTION_API = '/createShopifyTransaction.php' + + self.supported_countries = %w[ NZ CA ] + self.default_currency = 'NZD' + self.supported_cardtypes = [:visa, :master] + self.homepage_url = 'https://www.swipehq.com/checkout' + self.display_name = 'Swipe Checkout' + self.money_format = :dollars + + # Swipe Checkout requires the merchant's email and API key for authorization. + # This can be found under Settings > API Credentials after logging in to your + # Swipe Checkout merchant console at https://merchant.swipehq.[com|ca] + # + # :region determines which Swipe URL is used, this can be one of "NZ" or "CA". + # Currently Swipe Checkout has New Zealand and Canadian domains (swipehq.com + # and swipehq.ca respectively). Merchants must use the region that they + # signed up in for authentication with their merchant ID and API key to succeed. + def initialize(options = {}) + requires!(options, :login, :api_key, :region) + super + end + + # Transfers funds immediately. + # Note that Swipe Checkout only supports purchase at this stage + def purchase(money, creditcard, options = {}) + post = {} + add_invoice(post, options) + add_creditcard(post, creditcard) + add_customer_data(post, creditcard, options) + add_amount(post, money, options) + + commit('sale', money, post) + end + + private + + def add_customer_data(post, creditcard, options) + post[:email] = options[:email] + post[:ip_address] = options[:ip] + + address = options[:billing_address] || options[:address] + return if address.nil? + + post[:company] = address[:company] + + # groups all names after the first into the last name param + post[:first_name], post[:last_name] = address[:name].split(' ', 2) + post[:address] = "#{address[:address1]}, #{address[:address2]}" + post[:city] = address[:city] + post[:country] = address[:country] + post[:mobile] = address[:phone] # API only has a "mobile" field, no "phone" + end + + def add_invoice(post, options) + # store shopping-cart order ID in Swipe for merchant's records + post[:td_user_data] = options[:order_id] if options[:order_id] + post[:td_item] = options[:description] if options[:description] + post[:td_description] = options[:description] if options[:description] + post[:item_quantity] = "1" + end + + def add_creditcard(post, creditcard) + post[:card_number] = creditcard.number + post[:card_type] = creditcard.brand + post[:name_on_card] = "#{creditcard.first_name} #{creditcard.last_name}" + post[:card_expiry] = expdate(creditcard) + post[:secure_number] = creditcard.verification_value + end + + def expdate(creditcard) + year = format(creditcard.year, :two_digits) + month = format(creditcard.month, :two_digits) + + "#{month}#{year}" + end + + def add_amount(post, money, options) + post[:amount] = money.to_s + + post[:currency] = (options[:currency] || currency(money)) + end + + def commit(action, money, parameters) + case action + when "sale" + begin + response = call_api(TRANSACTION_API, parameters) + + # response code and message params should always be present + code = response["response_code"] + message = response["message"] + + if code == 200 + result = response["data"]["result"] + success = (result == 'accepted' || (test? && result == 'test-accepted')) + + Response.new(success, + success ? + TRANSACTION_APPROVED_MSG : + TRANSACTION_DECLINED_MSG, + response, + :test => test? + ) + else + build_error_response(message, response) + end + rescue ResponseError => e + raw_response = e.response.body + build_error_response("ssl_post() with url #{url} raised ResponseError: #{e}") + rescue JSON::ParserError => e + msg = 'Invalid response received from the Swipe Checkout API. ' + + 'Please contact support@optimizerhq.com if you continue to receive this message.' + + " (Full error message: #{e})" + build_error_response(msg) + end + end + end + + def call_api(api, params=nil) + params ||= {} + params[:merchant_id] = @options[:login] + params[:api_key] = @options[:api_key] + + # ssl_post() returns the response body as a string on success, + # or raises a ResponseError exception on failure + JSON.parse(ssl_post(url(@options[:region], api), params.to_query)) + end + + def url(region, api) + ((test? ? self.test_url : LIVE_URLS[region]) + api) + end + + def build_error_response(message, params={}) + Response.new( + false, + message, + params, + :test => test? + ) + end + end + end +end + diff --git a/lib/active_merchant/billing/gateways/usa_epay_advanced.rb b/lib/active_merchant/billing/gateways/usa_epay_advanced.rb index 6b3faeb312b..acd48e2b367 100644 --- a/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +++ b/lib/active_merchant/billing/gateways/usa_epay_advanced.rb @@ -179,7 +179,6 @@ class UsaEpayAdvancedGateway < Gateway } #:nodoc: CHECK_DATA_OPTIONS = { - :check_number => [:integer, 'CheckNumber'], :drivers_license => [:string, 'DriversLicense'], :drivers_license_state => [:string, 'DriversLicenseState'], :record_type => [:string, 'RecordType'], @@ -339,9 +338,10 @@ def add_customer(options={}) commit(__method__, request) end - # Update a customer by replacing all of the customer details.. + # Update a customer by replacing all of the customer details. # - # Use quickUpdateCustomer to just update a few attributes. + # ==== Required + # * :customer_number -- customer to update # # ==== Options # * Same as add_customer @@ -355,7 +355,7 @@ def update_customer(options={}) # Enable a customer for recurring billing. # - # Note: Customer does not need to have all recurring paramerters to succeed. + # Note: Customer does not need to have all recurring parameters to succeed. # # ==== Required # * :customer_number @@ -454,7 +454,7 @@ def update_customer_payment_method(options={}) commit(__method__, request) end - # Delete one the payment methods beloning to a customer + # Delete one the payment methods belonging to a customer # # ==== Required # * :customer_number @@ -543,11 +543,11 @@ def run_customer_transaction(options={}) # will be returned in the response. # # ==== Options - # * :method -- credit_card or check + # * :payment_method -- credit_card or check # * :command -- sale, credit, void, creditvoid, authonly, capture, postauth, check, checkcredit; defaults to sale; only required for run_transaction when other than sale # * :reference_number -- for the original transaction; obtained by sale or authonly # * :authorization_code -- required for postauth; obtained offline - # * :ignore_duplicate -- set +true+ if you want to override the duplicate tranaction handling + # * :ignore_duplicate -- set +true+ if you want to override the duplicate transaction handling # * :account_holder -- name of account holder # * :customer_id -- merchant assigned id # * :customer_receipt -- set +true+ to email receipt to billing email address @@ -680,7 +680,7 @@ def refund_transaction(options={}) commit(__method__, request) end - # Override transaction flagged for mananager approval. + # Override transaction flagged for manager approval. # # Note: Checks only! # @@ -1166,7 +1166,7 @@ def build_capture_transaction(soap, options) soap.tag! "ns1:captureTransaction" do |soap| build_token soap, options build_tag soap, :integer, 'RefNum', options[:reference_number] - build_tag soap, :double, 'RefNum', amount(options[:amount]) + build_tag soap, :double, 'Amount', amount(options[:amount]) end end @@ -1244,16 +1244,18 @@ def build_credit_card_or_check(soap, payment_method) when payment_method[:method].kind_of?(ActiveMerchant::Billing::CreditCard) build_tag soap, :string, 'CardNumber', payment_method[:method].number build_tag soap, :string, 'CardExpiration', - "#{"%02d" % payment_method[:method].month}#{payment_method[:method].year}" + "#{"%02d" % payment_method[:method].month}#{payment_method[:method].year.to_s[-2..-1]}" if options[:billing_address] build_tag soap, :string, 'AvsStreet', options[:billing_address][:address1] build_tag soap, :string, 'AvsZip', options[:billing_address][:zip] end build_tag soap, :string, 'CardCode', payment_method[:method].verification_value when payment_method[:method].kind_of?(ActiveMerchant::Billing::Check) - build_tag soap, :string, 'Account', payment_method[:method].number + build_tag soap, :string, 'Account', payment_method[:method].account_number build_tag soap, :string, 'Routing', payment_method[:method].routing_number - build_tag soap, :string, 'AccountType', payment_method[:method].account_type.capitalize + unless payment_method[:method].account_type.nil? + build_tag soap, :string, 'AccountType', payment_method[:method].account_type.capitalize + end build_tag soap, :string, 'DriversLicense', options[:drivers_license] build_tag soap, :string, 'DriversLicenseState', options[:drivers_license_state] build_tag soap, :string, 'RecordType', options[:record_type] @@ -1323,8 +1325,7 @@ def build_transaction_detail(soap, options) def build_credit_card_data(soap, options) soap.CreditCardData 'xsi:type' => "ns1:CreditCardData" do |soap| build_tag soap, :string, 'CardNumber', options[:payment_method].number - build_tag soap, :string, 'CardExpiration', - "#{"%02d" % options[:payment_method].month}#{options[:payment_method].year}" + build_tag soap, :string, 'CardExpiration', build_card_expiration(options) if options[:billing_address] build_tag soap, :string, 'AvsStreet', options[:billing_address][:address1] build_tag soap, :string, 'AvsZip', options[:billing_address][:zip] @@ -1337,8 +1338,17 @@ def build_credit_card_data(soap, options) end end + def build_card_expiration(options) + month = options[:payment_method].month + year = options[:payment_method].year + unless month.nil? || year.nil? + "#{"%02d" % month}#{year.to_s[-2..-1]}" + end + end + def build_check_data(soap, options) soap.CheckData 'xsi:type' => "ns1:CheckData" do |soap| + build_tag soap, :integer, 'CheckNumber', options[:payment_method].number build_tag soap, :string, 'Account', options[:payment_method].account_number build_tag soap, :string, 'Routing', options[:payment_method].routing_number build_tag soap, :string, 'AccountType', options[:payment_method].account_type.capitalize @@ -1461,7 +1471,8 @@ def parse(action, soap) when :get_customer_payment_methods p['item'] when :get_transaction_custom - p['item'].inject({}) { |map, field| map[field['field']] = field['value']; map } + items = p['item'].kind_of?(Array) ? p['item'] : [p['item']] + items.inject({}) { |hash, item| hash[item['field']] = item['value']; hash } else p end diff --git a/lib/active_merchant/billing/gateways/usa_epay_transaction.rb b/lib/active_merchant/billing/gateways/usa_epay_transaction.rb index 8e5ec3e230c..67eb4ab3330 100644 --- a/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +++ b/lib/active_merchant/billing/gateways/usa_epay_transaction.rb @@ -2,20 +2,20 @@ module ActiveMerchant #:nodoc: module Billing #:nodoc: class UsaEpayTransactionGateway < Gateway - self.test_url = 'https://sandbox.usaepay.com/gate.php' - self.live_url = 'https://www.usaepay.com/gate.php' + self.live_url = 'https://www.usaepay.com/gate' + self.test_url = 'https://sandbox.usaepay.com/gate' - self.supported_cardtypes = [:visa, :master, :american_express] - self.supported_countries = ['US'] - self.homepage_url = 'http://www.usaepay.com/' - self.display_name = 'USA ePay' + self.supported_cardtypes = [:visa, :master, :american_express] + self.supported_countries = ['US'] + self.homepage_url = 'http://www.usaepay.com/' + self.display_name = 'USA ePay' TRANSACTIONS = { - :authorization => 'authonly', - :purchase => 'sale', - :capture => 'capture', - :refund => 'refund', - :void => 'void' + :authorization => 'cc:authonly', + :purchase => 'cc:sale', + :capture => 'cc:capture', + :refund => 'cc:refund', + :void => 'cc:void' } def initialize(options = {}) @@ -31,6 +31,7 @@ def authorize(money, credit_card, options = {}) add_credit_card(post, credit_card) add_address(post, credit_card, options) add_customer_data(post, options) + add_split_payments(post, options) commit(:authorization, post) end @@ -43,6 +44,7 @@ def purchase(money, credit_card, options = {}) add_credit_card(post, credit_card) add_address(post, credit_card, options) add_customer_data(post, options) + add_split_payments(post, options) commit(:purchase, post) end @@ -66,7 +68,7 @@ def void(authorization, options = {}) commit(:void, post) end - private + private def add_amount(post, money) post[:amount] = amount(money) @@ -108,16 +110,16 @@ def add_address(post, credit_card, options) def add_address_for_type(type, post, credit_card, address) prefix = address_key_prefix(type) - post[address_key(prefix, 'fname')] = credit_card.first_name - post[address_key(prefix, 'lname')] = credit_card.last_name - post[address_key(prefix, 'company')] = address[:company] unless address[:company].blank? - post[address_key(prefix, 'street')] = address[:address1] unless address[:address1].blank? - post[address_key(prefix, 'street2')] = address[:address2] unless address[:address2].blank? - post[address_key(prefix, 'city')] = address[:city] unless address[:city].blank? - post[address_key(prefix, 'state')] = address[:state] unless address[:state].blank? - post[address_key(prefix, 'zip')] = address[:zip] unless address[:zip].blank? - post[address_key(prefix, 'country')] = address[:country] unless address[:country].blank? - post[address_key(prefix, 'phone')] = address[:phone] unless address[:phone].blank? + post[address_key(prefix, 'fname')] = credit_card.first_name + post[address_key(prefix, 'lname')] = credit_card.last_name + post[address_key(prefix, 'company')] = address[:company] unless address[:company].blank? + post[address_key(prefix, 'street')] = address[:address1] unless address[:address1].blank? + post[address_key(prefix, 'street2')] = address[:address2] unless address[:address2].blank? + post[address_key(prefix, 'city')] = address[:city] unless address[:city].blank? + post[address_key(prefix, 'state')] = address[:state] unless address[:state].blank? + post[address_key(prefix, 'zip')] = address[:zip] unless address[:zip].blank? + post[address_key(prefix, 'country')] = address[:country] unless address[:country].blank? + post[address_key(prefix, 'phone')] = address[:phone] unless address[:phone].blank? end def address_key_prefix(type) @@ -132,15 +134,29 @@ def address_key(prefix, key) end def add_invoice(post, options) - post[:invoice] = options[:order_id] - post[:description] = options[:description] + post[:invoice] = options[:order_id] + post[:description] = options[:description] end def add_credit_card(post, credit_card) - post[:card] = credit_card.number - post[:cvv2] = credit_card.verification_value if credit_card.verification_value? + post[:card] = credit_card.number + post[:cvv2] = credit_card.verification_value if credit_card.verification_value? post[:expir] = expdate(credit_card) - post[:name] = credit_card.name + post[:name] = credit_card.name + end + + # see: http://wiki.usaepay.com/developer/transactionapi#split_payments + def add_split_payments(post, options) + return unless options[:split_payments].is_a?(Array) + options[:split_payments].each_with_index do |payment, index| + prefix = '%02d' % (index + 2) + post["#{prefix}key"] = payment[:key] + post["#{prefix}amount"] = amount(payment[:amount]) + post["#{prefix}description"] = payment[:description] + end + + # When blank it's 'Stop'. 'Continue' is another one + post['onError'] = options[:on_error] || 'Void' end def parse(body) @@ -151,32 +167,32 @@ def parse(body) end { - :status => fields['UMstatus'], - :auth_code => fields['UMauthCode'], - :ref_num => fields['UMrefNum'], - :batch => fields['UMbatch'], - :avs_result => fields['UMavsResult'], - :avs_result_code => fields['UMavsResultCode'], - :cvv2_result => fields['UMcvv2Result'], + :status => fields['UMstatus'], + :auth_code => fields['UMauthCode'], + :ref_num => fields['UMrefNum'], + :batch => fields['UMbatch'], + :avs_result => fields['UMavsResult'], + :avs_result_code => fields['UMavsResultCode'], + :cvv2_result => fields['UMcvv2Result'], :cvv2_result_code => fields['UMcvv2ResultCode'], :vpas_result_code => fields['UMvpasResultCode'], - :result => fields['UMresult'], - :error => fields['UMerror'], - :error_code => fields['UMerrorcode'], - :acs_url => fields['UMacsurl'], - :payload => fields['UMpayload'] + :result => fields['UMresult'], + :error => fields['UMerror'], + :error_code => fields['UMerrorcode'], + :acs_url => fields['UMacsurl'], + :payload => fields['UMpayload'] }.delete_if{|k, v| v.nil?} end def commit(action, parameters) - url = (self.test? ? self.test_url : self.live_url) - response = parse( ssl_post(url, post_data(action, parameters)) ) + url = (test? ? self.test_url : self.live_url) + response = parse(ssl_post(url, post_data(action, parameters))) Response.new(response[:status] == 'Approved', message_from(response), response, - :test => test?, - :authorization => response[:ref_num], - :cvv_result => response[:cvv2_result_code], - :avs_result => { :code => response[:avs_result_code] } + :test => test?, + :authorization => response[:ref_num], + :cvv_result => response[:cvv2_result_code], + :avs_result => { :code => response[:avs_result_code] } ) end @@ -191,7 +207,7 @@ def message_from(response) def post_data(action, parameters = {}) parameters[:command] = TRANSACTIONS[action] - parameters[:key] = @options[:login] + parameters[:key] = @options[:login] parameters[:software] = 'Active Merchant' parameters[:testmode] = (@options[:test] ? 1 : 0) diff --git a/lib/active_merchant/billing/gateways/webpay.rb b/lib/active_merchant/billing/gateways/webpay.rb index b08e8aa090f..de8d6e484dc 100644 --- a/lib/active_merchant/billing/gateways/webpay.rb +++ b/lib/active_merchant/billing/gateways/webpay.rb @@ -13,12 +13,10 @@ class WebpayGateway < StripeGateway self.homepage_url = 'https://webpay.jp/' self.display_name = 'WebPay' - def authorize(money, credit_card, options = {}) - raise NotImplementedError.new - end - - def capture(money, credit_card, options = {}) - raise NotImplementedError.new + def capture(money, authorization, options = {}) + post = {:amount => localized_amount(money)} + add_application_fee(post, options) + commit(:post, "charges/#{CGI.escape(authorization)}/capture", post) end def refund(money, identification, options = {}) @@ -48,8 +46,28 @@ def add_amount(post, money, options) post[:amount] = localized_amount(money, post[:currency].upcase) end - def add_customer(post, options) - post[:customer] = options[:customer] if options[:customer] && post[:card].blank? + def add_customer(post, creditcard, options) + post[:customer] = options[:customer] if options[:customer] && !creditcard.respond_to?(:number) + end + + def store(creditcard, options = {}) + post = {} + add_creditcard(post, creditcard, options) + post[:description] = options[:description] + post[:email] = options[:email] + + commit_options = generate_options(options) + if options[:customer] + MultiResponse.run(:first) do |r| + r.process { commit(:post, "customers/#{CGI.escape(options[:customer])}/", post, commit_options) } + + return r unless options[:set_default] and r.success? and !r.params["id"].blank? + + r.process { update_customer(options[:customer], :default_card => r.params["id"]) } + end + else + commit(:post, 'customers', post, commit_options) + end end def json_error(raw_response) @@ -68,8 +86,7 @@ def headers(options = {}) :lang => 'ruby', :lang_version => "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})", :platform => RUBY_PLATFORM, - :publisher => 'active_merchant', - :uname => (RUBY_PLATFORM =~ /linux|darwin/i ? `uname -a 2>/dev/null`.strip : nil) + :publisher => 'active_merchant' }) { diff --git a/lib/active_merchant/billing/gateways/wirecard.rb b/lib/active_merchant/billing/gateways/wirecard.rb index b1f84d536cc..b448bac75ee 100644 --- a/lib/active_merchant/billing/gateways/wirecard.rb +++ b/lib/active_merchant/billing/gateways/wirecard.rb @@ -3,10 +3,7 @@ module ActiveMerchant #:nodoc: module Billing #:nodoc: class WirecardGateway < Gateway - # Test server location self.test_url = 'https://c3-test.wirecard.com/secure/ssl-gateway' - - # Live server location self.live_url = 'https://c3.wirecard.com/secure/ssl-gateway' # The Namespaces are not really needed, because it just tells the System, that there's actually no namespace used. @@ -29,57 +26,51 @@ class WirecardGateway < Gateway # number 5551234 within area code 202 (country code 1). VALID_PHONE_FORMAT = /\+\d{1,3}(\(?\d{3}\)?)?\d{3}-\d{4}-\d{3}/ - # The countries the gateway supports merchants from as 2 digit ISO country codes + self.supported_cardtypes = [ :visa, :master, :american_express, :diners_club, :jcb, :switch ] self.supported_countries = %w(AD CY GI IM MT RO CH AT DK GR IT MC SM TR BE EE HU LV NL SK GB BG FI IS LI NO SI VA FR IL LT PL ES CZ DE IE LU PT SE) - - # Wirecard supports all major credit and debit cards: - # Visa, Mastercard, American Express, Diners Club, - # JCB, Switch, VISA Carte Bancaire, Visa Electron and UATP cards. - # They also support the latest anti-fraud systems such as Verified by Visa or Master Secure Code. - self.supported_cardtypes = [ - :visa, :master, :american_express, :diners_club, :jcb, :switch - ] - - # The homepage URL of the gateway self.homepage_url = 'http://www.wirecard.com' - - # The name of the gateway self.display_name = 'Wirecard' - - # The currency should normally be EUROs self.default_currency = 'EUR' - - # 100 is 1.00 Euro self.money_format = :cents + # Public: Create a new Wirecard gateway. + # + # options - A hash of options: + # :login - The username + # :password - The password + # :signature - The BusinessCaseSignature def initialize(options = {}) - # verify that username and password are supplied - requires!(options, :login, :password) - # unfortunately Wirecard also requires a BusinessCaseSignature in the XML request - requires!(options, :signature) + requires!(options, :login, :password, :signature) super end - # Authorization def authorize(money, creditcard, options = {}) options[:credit_card] = creditcard commit(:preauthorization, money, options) end - # Capture Authorization def capture(money, authorization, options = {}) options[:preauthorization] = authorization commit(:capture, money, options) end - # Purchase def purchase(money, creditcard, options = {}) options[:credit_card] = creditcard commit(:purchase, money, options) end - private + def void(identification, options = {}) + options[:preauthorization] = identification + commit(:reversal, nil, options) + end + + def refund(money, identification, options = {}) + options[:preauthorization] = identification + commit(:bookback, money, options) + end + + private def prepare_options_hash(options) result = @options.merge(options) setup_address_hash!(result) @@ -156,9 +147,11 @@ def add_transaction_data(xml, money, options) add_invoice(xml, money, options) add_creditcard(xml, options[:credit_card]) add_address(xml, options[:billing_address]) - when :capture + when :capture, :bookback xml.tag! 'GuWID', options[:preauthorization] add_amount(xml, money) + when :reversal + xml.tag! 'GuWID', options[:preauthorization] end end end @@ -239,7 +232,7 @@ def parse(xml) response end - # Parse the Element which containts all important information + # Parse the Element which contains all important information def parse_response(response, root) status = nil # get the root element for this Transaction diff --git a/lib/active_merchant/billing/integrations/bit_pay.rb b/lib/active_merchant/billing/integrations/bit_pay.rb index 53f0b31c39a..f6bd1a21117 100644 --- a/lib/active_merchant/billing/integrations/bit_pay.rb +++ b/lib/active_merchant/billing/integrations/bit_pay.rb @@ -1,5 +1,6 @@ require File.dirname(__FILE__) + '/bit_pay/helper.rb' require File.dirname(__FILE__) + '/bit_pay/notification.rb' +require File.dirname(__FILE__) + '/bit_pay/return.rb' module ActiveMerchant #:nodoc: module Billing #:nodoc: @@ -12,13 +13,17 @@ module BitPay mattr_accessor :invoicing_url self.invoicing_url = 'https://bitpay.com/api/invoice' - def self.notification(post) - Notification.new(post) + def self.notification(post, options = {}) + Notification.new(post, options) end def self.helper(order, account, options = {}) Helper.new(order, account, options) end + + def self.return(query_string, options = {}) + Return.new(query_string) + end end end end diff --git a/lib/active_merchant/billing/integrations/bit_pay/helper.rb b/lib/active_merchant/billing/integrations/bit_pay/helper.rb index b6753d8cb35..8284022c002 100644 --- a/lib/active_merchant/billing/integrations/bit_pay/helper.rb +++ b/lib/active_merchant/billing/integrations/bit_pay/helper.rb @@ -6,20 +6,14 @@ class Helper < ActiveMerchant::Billing::Integrations::Helper def initialize(order_id, account, options) super @account = account - add_field('orderID', order_id) - add_field('posData', options[:authcode]) - add_field('currency', options[:currency]) - add_field('fullNotifications', 'true') - add_field('transactionSpeed', options[:transactionSpeed] || "high") - add_field('address1', options[:address1]) - generate_invoice_id + add_field('posData', {'orderId' => order_id}.to_json) + add_field('fullNotifications', true) + add_field('transactionSpeed', 'high') end - # Replace with the real mapping mapping :amount, 'price' - - mapping :order, 'orderID' + mapping :order, 'orderID' mapping :currency, 'currency' mapping :customer, :first_name => 'buyerName', @@ -34,23 +28,23 @@ def initialize(order_id, account, options) :country => 'buyerCountry' mapping :notify_url, 'notificationURL' - mapping :return_url, 'returnURL' + mapping :return_url, 'redirectURL' mapping :id, 'id' - def generate_invoice_id - invoice_data = ssl_post(BitPay.invoicing_url) - - add_field('id', JSON.parse(invoice_data.body)['id']) - end - def form_method "GET" end + def form_fields + invoice = create_invoice + + {"id" => invoice['id']} + end + private - def ssl_post(url, options = {}) - uri = URI.parse(url) + def create_invoice + uri = URI.parse(BitPay.invoicing_url) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true @@ -59,7 +53,9 @@ def ssl_post(url, options = {}) request.body = @fields.to_json request.basic_auth @account, '' - http.request(request) + response = http.request(request) + JSON.parse(response.body) + rescue JSON::ParserError end end end diff --git a/lib/active_merchant/billing/integrations/bit_pay/notification.rb b/lib/active_merchant/billing/integrations/bit_pay/notification.rb index 4aa260347a0..e3e3e7d7999 100644 --- a/lib/active_merchant/billing/integrations/bit_pay/notification.rb +++ b/lib/active_merchant/billing/integrations/bit_pay/notification.rb @@ -6,13 +6,29 @@ module Integrations #:nodoc: module BitPay class Notification < ActiveMerchant::Billing::Integrations::Notification def complete? - status == "complete" + status == "Completed" end def transaction_id params['id'] end + def item_id + JSON.parse(params['posData'])['orderId'] + rescue JSON::ParserError + end + + def status + case params['status'] + when 'complete' + 'Completed' + when 'confirmed' + 'Pending' + when 'invalid' + 'Failed' + end + end + # When was this payment received by the client. def received_at params['invoiceTime'].to_i @@ -22,32 +38,34 @@ def currency params['currency'] end - def amount - params['price'] + def gross + params['price'].to_f end - # the money amount we received in X.2 decimal. - def btcPrice - params['btcPrice'].to_f - end + def acknowledge(authcode = nil) + uri = URI.parse("#{ActiveMerchant::Billing::Integrations::BitPay.invoicing_url}/#{transaction_id}") - def status - params['status'].downcase - end + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true - def acknowledge(authcode = nil) - authcode == params['posData'] + request = Net::HTTP::Get.new(uri.path) + request.basic_auth @options[:credential1], '' + + response = http.request(request) + + posted_json = JSON.parse(@raw).tap { |j| j.delete('currentTime') } + parse(response.body) + retrieved_json = JSON.parse(@raw).tap { |j| j.delete('currentTime') } + + posted_json == retrieved_json + rescue JSON::ParserError end private - - # Take the posted data and move the relevant data into a hash - def parse(post) - @raw = post.to_s - for line in @raw.split('&') - key, value = *line.scan( %r{^([A-Za-z0-9_.]+)\=(.*)$} ).flatten - params[key] = CGI.unescape(value) - end + def parse(body) + @raw = body + @params = JSON.parse(@raw) + rescue JSON::ParserError end end end diff --git a/lib/active_merchant/billing/integrations/bit_pay/return.rb b/lib/active_merchant/billing/integrations/bit_pay/return.rb new file mode 100644 index 00000000000..f7641a80dcb --- /dev/null +++ b/lib/active_merchant/billing/integrations/bit_pay/return.rb @@ -0,0 +1,10 @@ +module ActiveMerchant #:nodoc: + module Billing #:nodoc: + module Integrations #:nodoc: + module BitPay + class Return < ActiveMerchant::Billing::Integrations::Return + end + end + end + end +end diff --git a/lib/active_merchant/billing/integrations/ipay88.rb b/lib/active_merchant/billing/integrations/ipay88.rb index cc6b77f2c01..b47516428ad 100644 --- a/lib/active_merchant/billing/integrations/ipay88.rb +++ b/lib/active_merchant/billing/integrations/ipay88.rb @@ -10,6 +10,10 @@ def self.service_url "https://www.mobile88.com/epayment/entry.asp" end + def self.requery_url + "https://www.mobile88.com/epayment/enquiry.asp" + end + def self.return(query_string, options={}) Return.new(query_string, options) end diff --git a/lib/active_merchant/billing/integrations/ipay88/helper.rb b/lib/active_merchant/billing/integrations/ipay88/helper.rb index 123f660358b..e742e6194fc 100644 --- a/lib/active_merchant/billing/integrations/ipay88/helper.rb +++ b/lib/active_merchant/billing/integrations/ipay88/helper.rb @@ -46,12 +46,16 @@ def initialize(order, account, options = {}) add_field mappings[:signature], signature end + def amount_in_dollars + sprintf("%.2f", @amount_in_cents.to_f/100) + end + def amount=(money) @amount_in_cents = money.respond_to?(:cents) ? money.cents : money if money.is_a?(String) or @amount_in_cents.to_i < 0 raise ArgumentError, "money amount must be either a Money object or a positive integer in cents." end - add_field mappings[:amount], sprintf("%.2f", @amount_in_cents.to_f/100) + add_field mappings[:amount], amount_in_dollars end def currency(symbol) @@ -103,7 +107,7 @@ def sig_components components = [merchant_key] components << fields[mappings[:account]] components << fields[mappings[:order]] - components << amount_in_cents.to_s.gsub(/[.,]/, '') + components << amount_in_dollars.gsub(/[.,]/, '') components << fields[mappings[:currency]] components.join end diff --git a/lib/active_merchant/billing/integrations/ipay88/notification.rb b/lib/active_merchant/billing/integrations/ipay88/notification.rb new file mode 100644 index 00000000000..0badfc4b434 --- /dev/null +++ b/lib/active_merchant/billing/integrations/ipay88/notification.rb @@ -0,0 +1,103 @@ +module ActiveMerchant #:nodoc: + module Billing #:nodoc: + module Integrations #:nodoc: + module Ipay88 + class Notification < ActiveMerchant::Billing::Integrations::Notification + include ActiveMerchant::PostsData + + def status + params["Status"] == '1' ? 'Completed' : 'Failed' + end + + def complete? + status == 'Completed' + end + + def item_id + params["RefNo"] + end + + def gross + params["Amount"] + end + + def currency + params["Currency"] + end + + def account + params["MerchantCode"] + end + + def payment + params["PaymentId"].to_i + end + + def remark + params["Remark"] + end + + def transaction_id + params["TransId"] + end + + def auth_code + params["AuthCode"] + end + + def error + params["ErrDesc"] + end + + def signature + params["Signature"] + end + + def secure? + generated_signature == signature + end + + def success? + status == 'Completed' + end + + def acknowledge + secure? && success? && requery == "00" + end + + protected + + def generated_signature + Helper.sign(sig_components) + end + + def sig_components + components = [@options[:credential2]] + [:account, :payment, :item_id, :amount_in_cents, :currency].each do |i| + components << send(i) + end + components << params["Status"] + components.join + end + + def requery + data = { "MerchantCode" => account, "RefNo" => item_id, "Amount" => gross } + params = parameterize(data) + ssl_post Ipay88.requery_url, params, { "Content-Length" => params.size.to_s, "User-Agent" => "Active Merchant -- http://activemerchant.org" } + end + + private + + def parameterize(params) + params.reject { |k, v| v.blank? }.keys.sort.collect { |key| "#{key}=#{CGI.escape(params[key].to_s)}" }.join("&") + end + + def amount_in_cents + @amount_in_cents ||= (gross || "").gsub(/[.,]/, "") + end + end + end + end + end +end + diff --git a/lib/active_merchant/billing/integrations/ipay88/return.rb b/lib/active_merchant/billing/integrations/ipay88/return.rb index 14e1bc8334b..acb9de1f0dc 100644 --- a/lib/active_merchant/billing/integrations/ipay88/return.rb +++ b/lib/active_merchant/billing/integrations/ipay88/return.rb @@ -5,88 +5,22 @@ module Billing #:nodoc: module Integrations #:nodoc: module Ipay88 class Return < ActiveMerchant::Billing::Integrations::Return - include ActiveMerchant::PostsData - def account - params["MerchantCode"] - end - - def payment - params["PaymentId"].to_i - end - - def order - params["RefNo"] - end - - def amount - params["Amount"] - end - - def currency - params["Currency"] - end - - def remark - params["Remark"] - end - - def transaction - params["TransId"] - end - - def auth_code - params["AuthCode"] - end - - def status - params["Status"] - end - - def error - params["ErrDesc"] - end - - def signature - params["Signature"] - end - - def secure? - self.generated_signature == self.signature + def initialize(query_string, options = {}) + super + @notification = Notification.new(query_string, options) end def success? - self.secure? && self.requery == "00" && self.status == "1" - end - - protected - - def generated_signature - Helper.sign(self.sig_components) - end - - def sig_components - components = [@options[:credential2]] - [:account, :payment, :order, :amount_in_cents, :currency, :status].each do |i| - components << self.send(i) - end - components.join + params["Status"] == "1" end - def requery - data = { "MerchantCode" => self.account, "RefNo" => self.order, "Amount" => self.amount } - params = parameterize(data) - ssl_post Ipay88.service_url, params, { "Content-Length" => params.size.to_s, "User-Agent" => "Active Merchant -- http://activemerchant.org" } + def cancelled? + params["ErrDesc"] == 'Customer Cancel Transaction' end - private - - def parameterize(params) - params.reject { |k, v| v.blank? }.keys.sort.collect { |key| "#{key}=#{CGI.escape(params[key].to_s)}" }.join("&") - end - - def amount_in_cents - @amount_in_cents ||= (self.amount || "").gsub(/[.,]/, "") + def message + params["ErrDesc"] end end end diff --git a/lib/active_merchant/billing/integrations/notification.rb b/lib/active_merchant/billing/integrations/notification.rb index 56df8ee8aa1..815916b4c42 100644 --- a/lib/active_merchant/billing/integrations/notification.rb +++ b/lib/active_merchant/billing/integrations/notification.rb @@ -61,8 +61,8 @@ def test? def parse(post) @raw = post.to_s for line in @raw.split('&') - key, value = *line.scan( %r{^([A-Za-z0-9_.]+)\=(.*)$} ).flatten - params[key] = CGI.unescape(value) + key, value = *line.scan( %r{^([A-Za-z0-9_.-]+)\=(.*)$} ).flatten + params[key] = CGI.unescape(value.to_s) if key.present? end end end diff --git a/lib/active_merchant/billing/integrations/pay_fast/notification.rb b/lib/active_merchant/billing/integrations/pay_fast/notification.rb index 21c402570c1..01bb8bc841b 100644 --- a/lib/active_merchant/billing/integrations/pay_fast/notification.rb +++ b/lib/active_merchant/billing/integrations/pay_fast/notification.rb @@ -73,7 +73,7 @@ def gross params['amount_gross'] end - # The total in fees which was deducated from the amount. + # The total in fees which was deducted from the amount. def fee params['amount_fee'] end diff --git a/lib/active_merchant/billing/integrations/paypal/helper.rb b/lib/active_merchant/billing/integrations/paypal/helper.rb index c905ae75901..1f894672320 100644 --- a/lib/active_merchant/billing/integrations/paypal/helper.rb +++ b/lib/active_merchant/billing/integrations/paypal/helper.rb @@ -69,7 +69,7 @@ def shipping_address(params = {}) if params.has_key?(:phone) phone = params.delete(:phone).to_s - # Whipe all non digits + # Wipe all non digits phone.gsub!(/\D+/, '') if ['US', 'CA'].include?(country_code) && phone =~ /(\d{3})(\d{3})(\d{4})$/ diff --git a/lib/active_merchant/billing/integrations/payu_in.rb b/lib/active_merchant/billing/integrations/payu_in.rb index 74933f1c7c6..261bc93c9a5 100755 --- a/lib/active_merchant/billing/integrations/payu_in.rb +++ b/lib/active_merchant/billing/integrations/payu_in.rb @@ -27,15 +27,8 @@ def self.return(post, options = {}) Return.new(post, options) end - def self.checksum(merchant_id, secret_key, *payload_items ) - options = payload_items.pop if Hash === payload_items.last - options ||= {} - payload = if options[:reverse] then - payload_items.dup.push( merchant_id || "" ).unshift( secret_key || "" ).collect{ |x| x.to_s }.join("|") - else - payload_items.dup.unshift( merchant_id || "" ).push( secret_key || "" ).collect{ |x| x.to_s }.join("|") - end - Digest::SHA512.hexdigest( payload ) + def self.checksum(merchant_id, secret_key, payload_items ) + Digest::SHA512.hexdigest([merchant_id, *payload_items, secret_key].join("|")) end end end diff --git a/lib/active_merchant/billing/integrations/payu_in/helper.rb b/lib/active_merchant/billing/integrations/payu_in/helper.rb index 32048d309bc..6ac746e9015 100755 --- a/lib/active_merchant/billing/integrations/payu_in/helper.rb +++ b/lib/active_merchant/billing/integrations/payu_in/helper.rb @@ -4,10 +4,13 @@ module Integrations #:nodoc: module PayuIn class Helper < ActiveMerchant::Billing::Integrations::Helper + CHECKSUM_FIELDS = [ 'txnid', 'amount', 'productinfo', 'firstname', 'email', 'udf1', 'udf2', 'udf3', 'udf4', + 'udf5', 'udf6', 'udf7', 'udf8', 'udf9', 'udf10'] + mapping :amount, 'amount' mapping :account, 'key' mapping :order, 'txnid' - mapping :credential2, 'productinfo' + mapping :description, 'productinfo' mapping :customer, :first_name => 'firstname', :last_name => 'lastname', @@ -44,26 +47,25 @@ class Helper < ActiveMerchant::Billing::Integrations::Helper def initialize(order, account, options = {}) super + @options = options self.pg = 'CC' end def form_fields + sanitize_fields @fields.merge(mappings[:checksum] => generate_checksum) end - def generate_checksum( options = {} ) - checksum_fields = [ :order, :amount, :credential2, { :customer => [ :first_name, :email ] }, - { :user_defined => [ :var1, :var2, :var3, :var4, :var5, :var6, :var7, :var8, :var9, :var10 ] } ] - checksum_payload_items = checksum_fields.inject( [] ) do | items, field | - if Hash === field then - key = field.keys.first - field[key].inject( items ){ |s,x| items.push( @fields[ mappings[key][x] ] ) } - else - items.push( @fields[ mappings[field] ] ) - end + def generate_checksum + checksum_payload_items = CHECKSUM_FIELDS.map { |field| @fields[field] } + + PayuIn.checksum(@fields["key"], @options[:credential2], checksum_payload_items ) + end + + def sanitize_fields + ['address1', 'address2', 'city', 'state', 'country', 'productinfo', 'email', 'phone'].each do |field| + @fields[field].gsub!(/[^a-zA-Z0-9\-_@\/\s.]/, '') if @fields[field] end - checksum_payload_items.push( options ) - PayuIn.checksum(@fields["key"], @fields["productinfo"], *checksum_payload_items ) end end diff --git a/lib/active_merchant/billing/integrations/payu_in/notification.rb b/lib/active_merchant/billing/integrations/payu_in/notification.rb index 932c136e7d1..7881a02f9ca 100755 --- a/lib/active_merchant/billing/integrations/payu_in/notification.rb +++ b/lib/active_merchant/billing/integrations/payu_in/notification.rb @@ -15,18 +15,10 @@ def complete? end def status - @status ||= if checksum_ok? - if transaction_id.blank? - 'Invalid' - else - case transaction_status.downcase - when 'success' then 'Completed' - when 'failure' then 'Failed' - when 'pending' then 'Pending' - end - end - else - 'Tampered' + case transaction_status.downcase + when 'success' then 'Completed' + when 'failure' then 'Failed' + when 'pending' then 'Pending' end end @@ -36,7 +28,7 @@ def invoice_ok?( order_id ) # Order amount should be equal to gross - discount def amount_ok?( order_amount, order_discount = BigDecimal.new( '0.0' ) ) - BigDecimal.new( gross ) == order_amount && BigDecimal.new( discount ) == order_discount + BigDecimal.new( gross ) == order_amount && BigDecimal.new( discount.to_s ) == order_discount end # Status of transaction return from the PayU. List of possible values: @@ -129,10 +121,7 @@ def customer_address end def user_defined - return @user_defined if @user_defined - @user_defined = [] - 10.times{ |i| @user_defined.push( params[ "udf#{i+1}" ] ) } - @user_defined + @user_defined ||= 10.times.map { |i| params["udf#{i + 1}"] } end def checksum @@ -148,9 +137,9 @@ def acknowledge(authcode = nil) end def checksum_ok? - fields = user_defined.dup.push( customer_email, customer_first_name, product_info, gross, invoice, :reverse => true ) - fields.unshift( transaction_status ) - unless PayuIn.checksum(@merchant_id, @secret_key, *fields ) == checksum + checksum_fields = [transaction_status, *user_defined.reverse, customer_email, customer_first_name, product_info, gross, invoice] + + unless Digest::SHA512.hexdigest([@secret_key, *checksum_fields, @merchant_id].join("|")) == checksum @message = 'Return checksum not matching the data provided' return false end diff --git a/lib/active_merchant/billing/integrations/payu_in_paisa/helper.rb b/lib/active_merchant/billing/integrations/payu_in_paisa/helper.rb index d87c596d1c9..b46561c176e 100644 --- a/lib/active_merchant/billing/integrations/payu_in_paisa/helper.rb +++ b/lib/active_merchant/billing/integrations/payu_in_paisa/helper.rb @@ -7,7 +7,7 @@ class Helper < PayuIn::Helper mapping :service_provider, 'service_provider' def initialize(order, account, options = {}) - super order, account, options + super self.service_provider = 'payu_paisa' self.user_defined = { :var2 => order } end diff --git a/lib/active_merchant/billing/integrations/payu_in_paisa/notification.rb b/lib/active_merchant/billing/integrations/payu_in_paisa/notification.rb index df4bfb7eba8..9d930f4df6a 100644 --- a/lib/active_merchant/billing/integrations/payu_in_paisa/notification.rb +++ b/lib/active_merchant/billing/integrations/payu_in_paisa/notification.rb @@ -6,16 +6,6 @@ class Notification < PayuIn::Notification def item_id params['udf2'] end - - def checksum_ok? - fields = user_defined.reverse.push( customer_email, customer_first_name, product_info, gross, invoice, :reverse => true ) - fields.unshift( transaction_status ) - unless PayuIn.checksum(@merchant_id, @secret_key, *fields ) == checksum - @message = 'Return checksum not matching the data provided' - return false - end - true - end end end end diff --git a/lib/active_merchant/billing/integrations/payu_in_paisa/return.rb b/lib/active_merchant/billing/integrations/payu_in_paisa/return.rb index b5bfce0af52..8b22eb7b08a 100644 --- a/lib/active_merchant/billing/integrations/payu_in_paisa/return.rb +++ b/lib/active_merchant/billing/integrations/payu_in_paisa/return.rb @@ -5,7 +5,7 @@ module PayuInPaisa class Return < PayuIn::Return def initialize(query_string, options = {}) - super query_string, options + super @notification = Notification.new(query_string, options) end end diff --git a/lib/active_merchant/billing/integrations/two_checkout/helper.rb b/lib/active_merchant/billing/integrations/two_checkout/helper.rb index 077b9ae424d..4f4eac351fa 100644 --- a/lib/active_merchant/billing/integrations/two_checkout/helper.rb +++ b/lib/active_merchant/billing/integrations/two_checkout/helper.rb @@ -56,7 +56,7 @@ def customer(params = {}) end # Uses Pass Through Product Parameters to pass in lineitems. - # (must mark tanigble sales as shipped to settle the transaction) + # (must mark tangible sales as shipped to settle the transaction) def line_item(params = {}) add_field('mode', '2CO') (max_existing_line_item_id = form_fields.keys.map do |key| diff --git a/lib/active_merchant/billing/integrations/valitor/helper.rb b/lib/active_merchant/billing/integrations/valitor/helper.rb index 15630f659de..9a6aeafd214 100644 --- a/lib/active_merchant/billing/integrations/valitor/helper.rb +++ b/lib/active_merchant/billing/integrations/valitor/helper.rb @@ -50,7 +50,7 @@ def product(id, options={}) requires!(options, :amount, :description) options.assert_valid_keys([:description, :quantity, :amount, :discount]) - add_field("Vara_#{id}_Verd", format_amount(options[:amount])) + add_field("Vara_#{id}_Verd", format_amount(options[:amount], @fields[mappings[:currency]])) add_field("Vara_#{id}_Fjoldi", options[:quantity] || "1") add_field("Vara_#{id}_Lysing", options[:description]) if options[:description] @@ -76,8 +76,8 @@ def form_fields @fields.merge('RafraenUndirskrift' => signature) end - def format_amount(amount) - amount.to_f.round + def format_amount(amount, currency) + Gateway::CURRENCIES_WITHOUT_FRACTIONS.include?(currency) ? amount.to_f.round : sprintf("%.2f", amount) end end end diff --git a/lib/active_merchant/billing/integrations/valitor/response_fields.rb b/lib/active_merchant/billing/integrations/valitor/response_fields.rb index 636a0cc7081..29c8176a0a3 100644 --- a/lib/active_merchant/billing/integrations/valitor/response_fields.rb +++ b/lib/active_merchant/billing/integrations/valitor/response_fields.rb @@ -36,7 +36,7 @@ def received_at end def gross - "%0.2f" % params['Upphaed'].to_s + "%0.2f" % params['Upphaed'].to_s.sub(',', '.') end def card_type @@ -94,4 +94,4 @@ def acknowledge(authcode = nil) end end end -end \ No newline at end of file +end diff --git a/lib/active_merchant/billing/integrations/verkkomaksut/notification.rb b/lib/active_merchant/billing/integrations/verkkomaksut/notification.rb index aaaf5eee163..53ce8eaac3c 100644 --- a/lib/active_merchant/billing/integrations/verkkomaksut/notification.rb +++ b/lib/active_merchant/billing/integrations/verkkomaksut/notification.rb @@ -40,7 +40,7 @@ def status end end - # Acknowldges the payment. If the authcodes match, returns true. + # Acknowledges the payment. If the authcodes match, returns true. def acknowledge(authcode = nil) return_authcode = [params["ORDER_NUMBER"], params["TIMESTAMP"], params["PAID"], params["METHOD"], authcode].join("|") Digest::MD5.hexdigest(return_authcode).upcase == params["RETURN_AUTHCODE"] diff --git a/lib/active_merchant/billing/integrations/wirecard_checkout_page.rb b/lib/active_merchant/billing/integrations/wirecard_checkout_page.rb new file mode 100644 index 00000000000..c305a20f78f --- /dev/null +++ b/lib/active_merchant/billing/integrations/wirecard_checkout_page.rb @@ -0,0 +1,39 @@ +=begin + * Shop System Plugins - Terms of use + * + * This terms of use regulates warranty and liability between Wirecard Central Eastern Europe (subsequently referred to as WDCEE) and it's + * contractual partners (subsequently referred to as customer or customers) which are related to the use of plugins provided by WDCEE. + * + * The Plugin is provided by WDCEE free of charge for it's customers and must be used for the purpose of WDCEE's payment platform + * integration only. It explicitly is not part of the general contract between WDCEE and it's customer. The plugin has successfully been tested + * under specific circumstances which are defined as the shopsystem's standard configuration (vendor's delivery state). The Customer is + * responsible for testing the plugin's functionality before putting it into production environment. + * The customer uses the plugin at own risk. WDCEE does not guarantee it's full functionality neither does WDCEE assume liability for any + * disadvantage related to the use of this plugin. By installing the plugin into the shopsystem the customer agrees to the terms of use. + * Please do not use this plugin if you do not agree to the terms of use! +=end + +module ActiveMerchant #:nodoc: + module Billing #:nodoc: + module Integrations #:nodoc: + module WirecardCheckoutPage + autoload :Common, File.dirname(__FILE__) + '/wirecard_checkout_page/common.rb' + autoload :Helper, File.dirname(__FILE__) + '/wirecard_checkout_page/helper.rb' + autoload :Notification, File.dirname(__FILE__) + '/wirecard_checkout_page/notification.rb' + autoload :Return, File.dirname(__FILE__) + '/wirecard_checkout_page/return.rb' + + mattr_accessor :service_url + self.service_url = 'https://checkout.wirecard.com/page/init.php' + + def self.notification(post, options) + Notification.new(post, options) + end + + def self.return(postdata, options) + Return.new(postdata, options) + end + + end + end + end +end diff --git a/lib/active_merchant/billing/integrations/wirecard_checkout_page/common.rb b/lib/active_merchant/billing/integrations/wirecard_checkout_page/common.rb new file mode 100644 index 00000000000..88013a39e5b --- /dev/null +++ b/lib/active_merchant/billing/integrations/wirecard_checkout_page/common.rb @@ -0,0 +1,104 @@ +=begin + * Shop System Plugins - Terms of use + * + * This terms of use regulates warranty and liability between Wirecard Central Eastern Europe (subsequently referred to as WDCEE) and it's + * contractual partners (subsequently referred to as customer or customers) which are related to the use of plugins provided by WDCEE. + * + * The Plugin is provided by WDCEE free of charge for it's customers and must be used for the purpose of WDCEE's payment platform + * integration only. It explicitly is not part of the general contract between WDCEE and it's customer. The plugin has successfully been tested + * under specific circumstances which are defined as the shopsystem's standard configuration (vendor's delivery state). The Customer is + * responsible for testing the plugin's functionality before putting it into production enviroment. + * The customer uses the plugin at own risk. WDCEE does not guarantee it's full functionality neither does WDCEE assume liability for any + * disadvantage related to the use of this plugin. By installing the plugin into the shopsystem the customer agrees to the terms of use. + * Please do not use this plugin if you do not agree to the terms of use! +=end + +module ActiveMerchant #:nodoc: + module Billing #:nodoc: + module Integrations #:nodoc: + module WirecardCheckoutPage + module Common + + mattr_accessor :paymenttypes + self.paymenttypes = %w( + SELECT + CCARD + BANCONTACT_MISTERCASH + C2P + CCARD-MOTO + EKONTO + ELV + EPS + GIROPAY + IDL + INSTALLMENT + INSTANTBANK + INVOICE + MAESTRO + MONETA + MPASS + PRZELEWY24 + PAYPAL + PBX + POLI + PSC + QUICK + SKRILLDIRECT + SKRILLWALLET + SOFORTUEBERWEISUNG) + + def message + @message + end + + def verify_response(params, secret) + + logstr = '' + params.each { |key, value| + logstr += "#{key} #{value}\n" + } + + @paymentstate = 'FAILURE' + + unless params.has_key?('paymentState') + @message = "paymentState is missing" + return false + end + + if params['paymentState'] == 'SUCCESS' || params['paymentState'] == 'PENDING' + unless params.has_key?('responseFingerprint') + @message = "responseFingerprint is missing" + return false + end + + unless params.has_key?('responseFingerprintOrder') + @message = "responseFingerprintOrder is missing" + return false + end + + end + + if params['paymentState'] == 'SUCCESS' || params['paymentState'] == 'PENDING' + fields = params['responseFingerprintOrder'].split(",") + values = '' + fields.each { |f| + values += f == 'secret' ? secret : params[f] + } + + + if Digest::MD5.hexdigest(values) != params['responseFingerprint'] + @message = "responseFingerprint verification failed" + return false + end + end + + @paymentstate = params['paymentState'] + true + end + + end + + end + end + end +end diff --git a/lib/active_merchant/billing/integrations/wirecard_checkout_page/helper.rb b/lib/active_merchant/billing/integrations/wirecard_checkout_page/helper.rb new file mode 100644 index 00000000000..62ec0db4d15 --- /dev/null +++ b/lib/active_merchant/billing/integrations/wirecard_checkout_page/helper.rb @@ -0,0 +1,145 @@ +module ActiveMerchant #:nodoc: + module Billing #:nodoc: + module Integrations #:nodoc: + module WirecardCheckoutPage + class Helper < ActiveMerchant::Billing::Integrations::Helper + include Common + + PLUGIN_NAME = 'ActiveMerchant_WirecardCheckoutPage' + PLUGIN_VERSION = '1.0.0' + + # Replace with the real mapping + mapping :account, 'customerId' + mapping :amount, 'amount' + + mapping :order, 'xActiveMerchantOrderId' + + mapping :customer, :first_name => 'consumerBillingFirstName', + :last_name => 'consumerBillingLastName', + :email => 'consumerEmail', + :phone => 'consumerBillingPhone', + :ipaddress => 'consumerIpAddress', + :user_agent => 'consumerUserAgent', + :fax => 'consumerBillingFax', + :birthdate => 'consumerBirthDate' # mandatory for INVOICE and INSTALLMENT + + mapping :billing_address, :city => 'consumerBillingCity', + :address1 => 'consumerBillingAddress1', + :address2 => 'consumerBillingAddress2', + :state => 'consumerBillingState', + :zip => 'consumerBillingZipCode', + :country => 'consumerBillingCountry' + + mapping :shipping_address, :first_name => 'consumerShippingFirstName', + :last_name => 'consumerShippingLastName', + :address1 => 'consumerShippingAddress1', + :address2 => 'consumerShippingAddress2', + :city => 'consumerShippingCity', + :state => 'consumerShippingState', + :country => 'consumerShippingCountry', + :zip => 'consumerShippingZipCode', + :phone => 'consumerShippingPhone', + :fax => 'consumerShippingFax' + + mapping :currency, 'currency' + mapping :language, 'language' # language for displayed texts on payment page + + mapping :description, 'orderDescription' # unique description of the consumer's order in a human readable form + + mapping :shop_service_url, 'serviceUrl' # URL of your service page containing contact information + + mapping :notify_url, 'confirmUrl' + mapping :return_url, 'successUrl' + + # defaulting to return_url + mapping :cancel_return_url, 'cancelUrl' + mapping :pending_url, 'pendingUrl' + mapping :failure_url, 'failureUrl' + + # optional parameters + mapping :window_name, 'windowName' # window.name of browser window where payment page is opened + mapping :duplicate_request_check, 'duplicateRequestCheck' # check for duplicate requests done by your consumer + mapping :customer_statement, 'customerStatement' # text displayed on invoice of financial institution of your consumer + mapping :order_reference, 'orderReference' # unique order reference id sent from merchant to financial institution + mapping :display_text, 'displayText' # text displayed to your consumer within the payment page + mapping :image_url, 'imageUrl' # URL of your web shop where your web shop logo is located + mapping :max_retries, 'maxRetries' # maximum number of attempted payments for the same order + mapping :auto_deposit, 'autoDeposit' # enable automated debiting of payments + mapping :financial_institution, 'financialInstitution' # based on pre-selected payment type a sub-selection of financial institutions regarding to pre-selected payment type + + # not used + mapping :tax, '' + mapping :shipping, '' + + def initialize(order, customer_id, options = {}) + @paymenttype = options.delete(:paymenttype) + + raise "Unknown Paymenttype: " + @paymenttype if paymenttypes.find_index(@paymenttype) == nil + + @secret = options.delete(:secret) + @customer_id = customer_id + @shop_id = options.delete(:shop_id) + super + end + + def add_version(shop_name, shop_version) + add_field('pluginVersion', Base64.encode64(shop_name + ';' + shop_version + ';ActiveMerchant;' + PLUGIN_NAME + ';' + PLUGIN_VERSION)) + end + + def add_standard_fields + addfields = {} + addfields['shopId'] = @shop_id if !@shop_id.blank? + addfields['paymentType'] = @paymenttype + + addfields[mappings[:pending_url]] = @fields[mappings[:return_url]] unless @fields.has_key?(mappings[:pending_url]) + addfields[mappings[:cancel_return_url]] = @fields[mappings[:return_url]] unless @fields.has_key?(mappings[:cancel_return_url]) + addfields[mappings[:failure_url]] = @fields[mappings[:return_url]] unless @fields.has_key?(mappings[:failure_url]) + + addfields + end + + def add_request_fingerprint(fpfields) + addfields = {} + fingerprint_order = %w(secret) + fingerprint_values = @secret.to_s + fpfields.each { |key, value| + next if key == 'pluginVersion' + fingerprint_order.append key + fingerprint_values += value.to_s + } + + fingerprint_order.append 'requestFingerprintOrder' + fingerprint_values += fingerprint_order.join(',') + + addfields['requestFingerprintOrder'] = fingerprint_order.join(',') + addfields['requestFingerprint'] = Digest::MD5.hexdigest(fingerprint_values) + + return addfields + end + + def form_fields + result = {} + result.merge!(@fields) + result.merge!(add_standard_fields) + result.merge!(add_request_fingerprint(result)) + result + end + + def secret + @secret + end + + def customer_id + @customer_id + end + + def shop_id + @shop_id + end + + end + + end + end + end +end \ No newline at end of file diff --git a/lib/active_merchant/billing/integrations/wirecard_checkout_page/notification.rb b/lib/active_merchant/billing/integrations/wirecard_checkout_page/notification.rb new file mode 100644 index 00000000000..73590b84c92 --- /dev/null +++ b/lib/active_merchant/billing/integrations/wirecard_checkout_page/notification.rb @@ -0,0 +1,101 @@ +require 'net/http' + +module ActiveMerchant #:nodoc: + module Billing #:nodoc: + module Integrations #:nodoc: + module WirecardCheckoutPage + class Notification < ActiveMerchant::Billing::Integrations::Notification + include Common + + def complete? + @paymentstate == 'SUCCESS' + end + + def item_id + params['xActiveMerchantOrderId'] + end + + def transaction_id + params['orderNumber'] + end + + # When was this payment received by the client. + def received_at + nil + end + + # the money amount we received in X.2 decimal. + def gross + params['amount'] + end + + # Was this a test transaction? + def test? + false + end + + def status + case @paymentstate + when 'SUCCESS' + 'Completed' + when 'PENDING' + 'Pending' + when 'CANCEL' + 'Cancelled' + when 'FAILURE' + 'Failed' + else + 'Error' + end + end + + def status_code + @paymentstate + end + + + # Acknowledge the transaction to WirecardCheckoutPage. This method has to be called after a new + # apc arrives. WirecardCheckoutPage will verify that all the information we received are correct and will return a + # ok or a fail. + # + # Example: + # + # def ipn + # notify = WirecardCheckoutPageNotification.new(request.raw_post, options) + # + # if notify.acknowledge + # ... process order ... if notify.complete? + # else + # ... log possible hacking attempt ... + # end + def acknowledge + verify_response(params, @options[:secret]) + end + + def response(umessage = nil) + if @message || umessage + '' + else + '' + end + end + + def method_missing(method_id, *args) + return params[method_id.to_s] if params.has_key?(method_id.to_s) + end + + private + + # Take the posted data and move the relevant data into a hash + def parse(post) + @raw = post.to_s + for line in @raw.split('&') + key, value = *line.scan( %r{^([A-Za-z0-9_.]+)\=(.*)$} ).flatten + params[key] = CGI.unescape(value) + end + end + end + end + end + end +end diff --git a/lib/active_merchant/billing/integrations/wirecard_checkout_page/return.rb b/lib/active_merchant/billing/integrations/wirecard_checkout_page/return.rb new file mode 100644 index 00000000000..0e3944a67a5 --- /dev/null +++ b/lib/active_merchant/billing/integrations/wirecard_checkout_page/return.rb @@ -0,0 +1,35 @@ +module ActiveMerchant #:nodoc: + module Billing #:nodoc: + module Integrations #:nodoc: + module WirecardCheckoutPage + class Return < ActiveMerchant::Billing::Integrations::Return + include Common + + def initialize(postdata, options = {}) + @params = parse(postdata) + @options = options + verify_response(@params, options[:secret]) + end + + def success? + @paymentstate == 'SUCCESS' + end + + def cancelled? + @paymentstate == 'CANCEL' + end + + def pending? + @paymentstate == 'PENDING' + end + + def method_missing(method_id, *args) + return params[method_id.to_s] if params.has_key?(method_id.to_s) + end + + end + end + end + end +end + diff --git a/lib/active_merchant/billing/integrations/world_pay/helper.rb b/lib/active_merchant/billing/integrations/world_pay/helper.rb index 68ab9d911dd..cd80897cd80 100644 --- a/lib/active_merchant/billing/integrations/world_pay/helper.rb +++ b/lib/active_merchant/billing/integrations/world_pay/helper.rb @@ -58,7 +58,7 @@ def customer(params={}) end # Support for a MD5 hash of selected fields to prevent tampering - # For futher information read the tech note at the address below: + # For further information read the tech note at the address below: # http://support.worldpay.com/kb/integration_guides/junior/integration/help/tech_notes/sjig_tn_009.html def encrypt(secret, fields = [:amount, :currency, :account, :order]) signature_fields = fields.collect{ |field| mappings[field] } @@ -98,4 +98,4 @@ def combined_params(params={}) end end end -end \ No newline at end of file +end diff --git a/lib/active_merchant/version.rb b/lib/active_merchant/version.rb index 5a00f3ccb4b..69eda89b8a7 100644 --- a/lib/active_merchant/version.rb +++ b/lib/active_merchant/version.rb @@ -1,3 +1,3 @@ module ActiveMerchant - VERSION = "1.39.2" + VERSION = "1.42.2" end diff --git a/test/fixtures.yml b/test/fixtures.yml index 377ee656e79..598e9269d79 100644 --- a/test/fixtures.yml +++ b/test/fixtures.yml @@ -10,6 +10,11 @@ # # Paste any required PEM certificates after the pem key. # + +app55: + api_key: "QDtACYMCFqtuKOQ22QtCkGR1TzD7XM8i" + api_secret: "yT1FRpuusBIQoEBIfIQ8bPStkl7yzdTz" + authorize_net: login: X password: Y @@ -59,6 +64,7 @@ braintree_blue: merchant_id: X public_key: Y private_key: Z + merchant_account_id: A braintree_blue_with_processing_rules: merchant_id: X @@ -85,6 +91,9 @@ cyber_source: login: X password: Y +conekta: + key: 1tv5yJp3xnVZ7eK67m4h + data_cash: login: X password: Y @@ -119,10 +128,9 @@ eway_managed: username: 'test@eway.com.au' password: 'test123' -# Working credentials, no need to replace eway_rapid: - login: "F9802CIVgUgNaD8y/H52MBMnL5OvMoy4cYimpi1L/dCXeNNR6or3vLPoC9GjeLVdA7ymi+" - password: "sandbox1" + login: LOGIN + password: PASSWORD # Working credentials, no need to replace exact: @@ -328,6 +336,11 @@ pay_gate_xml: login: '10011021600' password: 'test' +payex: + account: ACCOUNT + # encryption key is generated via the PayEx Admin console + encryption_key: ENCRYPTION_KEY + payflow: login: LOGIN password: PASSWORD @@ -364,6 +377,10 @@ paypal_signature: password: PASSWORD signature: SIGNATURE +payu_in: + login: C0Dr8m + secret: 3sf0jURk + payway: username: password: @@ -456,6 +473,10 @@ psl_visa_debit_address: state: zip: +payscout: + username: demo + password: password + # Quickpay offers a test account. # To log in to the manager, use demo@quickpay.net and test234 quickpay: @@ -473,6 +494,11 @@ quantum: login: X password: Y +raven_pac_net: + user: ernest + secret: "all good men die young" + prn: 840033 + realex: login: X password: Y @@ -578,6 +604,10 @@ skipjack: login: X password: Y +so_easy_pay: + login: 110159 + password: b1fe9de37c234ef8fd53668377076687d6314dc8f6146023338d61b5969719e0 + # Working credentials, no need to replace spreedly_core: login: "4Y9bVkOCpYesPQOfDi7TXQyUw50" @@ -588,6 +618,12 @@ stripe: login: fee_refund_login: +# Working credentials, no need to replace +swipe_checkout: + login: 2077103073D8B5 + api_key: f2fe4efd5033edfaed9e4aad319ef4d34536a10eea07f90f182616d7216ae2b8 + region: NZ + trans_first: login: LOGIN password: PASSWORD @@ -651,3 +687,8 @@ iats_payments: bit_pay: api_key: 'rKrahSl7WRrYeKRUhGzbvW3nBzo0jG4FPaL8uPYaoPk' + +wirecard_checkout_page: + secret: B8AKTPWBRMNBV455FG6M2DANE99WU2 + shop_id: '' + paymenttype: IDL diff --git a/test/remote/gateways/remote_app55_test.rb b/test/remote/gateways/remote_app55_test.rb new file mode 100644 index 00000000000..b52a889f878 --- /dev/null +++ b/test/remote/gateways/remote_app55_test.rb @@ -0,0 +1,65 @@ +require 'test_helper' + +class RemoteApp55Test < Test::Unit::TestCase + def setup + @gateway = App55Gateway.new(fixtures(:app55)) + + @amount = 100 + @credit_card = credit_card('4111111111111111') + @duff_card = credit_card('400030001111222') + @customer = generate_unique_id + + @options = { + billing_address: address, + description: 'app55 active merchant remote test', + currency: "GBP" + } + end + + def test_successful_purchase + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_success response + assert_not_nil response.params["sig"] + assert_not_nil response.params["transaction"]["auth_code"] + assert_not_nil response.params["transaction"]["id"] + assert_equal response.params["transaction"]["id"], response.authorization + assert_equal @options[:description], response.params["transaction"]["description"] + assert_equal @options[:currency], response.params["transaction"]["currency"] + assert_equal "%.2f" % (@amount / 100), response.params["transaction"]["amount"] + end + + def test_unsuccessful_purchase + assert response = @gateway.purchase(@amount, @duff_card, @options) + assert_failure response + assert_equal "Invalid card number supplied.", response.message + end + + def test_authorize_and_capture + amount = @amount + assert response = @gateway.authorize(amount, @credit_card, @options) + assert_success response + assert response.params["transaction"] + assert_equal @options[:description], response.params["transaction"]["description"] + assert_equal @options[:currency], response.params["transaction"]["currency"] + assert_equal "%.2f" % (@amount / 100), response.params["transaction"]["amount"] + + assert capture = @gateway.capture(amount, response.authorization) + assert_success capture + end + + def test_failed_capture + assert response = @gateway.capture(@amount, '') + assert_failure response + assert !response.params["transaction"] + end + + def test_invalid_login + gateway = App55Gateway.new( + api_key: 'xNSACPYP9ZDUr4860gV9vqvR7TxmVMJP', + api_secret: 'bogus' + ) + assert response = gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert_equal 'The supplied API Secret does not appear to be valid.', response.message + end +end diff --git a/test/remote/gateways/remote_balanced_test.rb b/test/remote/gateways/remote_balanced_test.rb index 1ea8135dcf3..61a4cf135a8 100644 --- a/test/remote/gateways/remote_balanced_test.rb +++ b/test/remote/gateways/remote_balanced_test.rb @@ -42,6 +42,14 @@ def test_unsuccessful_purchase assert_match /Account Frozen/, response.message end + def test_passing_appears_on_statement + options = @options.merge(appears_on_statement_as: "Homer Electric") + assert response = @gateway.purchase(@amount, @credit_card, options) + + assert_success response + assert_equal "Homer Electric", response.params['appears_on_statement_as'] + end + def test_authorize_and_capture amount = @amount assert auth = @gateway.authorize(amount, @credit_card, @options) diff --git a/test/remote/gateways/remote_braintree_blue_test.rb b/test/remote/gateways/remote_braintree_blue_test.rb index 5929b5d5d21..ed5b90cc0eb 100644 --- a/test/remote/gateways/remote_braintree_blue_test.rb +++ b/test/remote/gateways/remote_braintree_blue_test.rb @@ -51,19 +51,12 @@ def test_successful_authorize_with_order_id assert_equal '123', response.params["braintree_transaction"]["order_id"] end - def test_successful_authorize_with_merchant_account_id - assert response = @gateway.authorize(@amount, @credit_card, :merchant_account_id => 'sandbox_credit_card_non_default') - assert_success response - assert_equal '1000 Approved', response.message - assert_equal 'sandbox_credit_card_non_default', response.params["braintree_transaction"]["merchant_account_id"] - end - def test_successful_purchase_using_vault_id assert response = @gateway.store(@credit_card) assert_success response assert_equal 'OK', response.message customer_vault_id = response.params["customer_vault_id"] - assert_match(/\A\d{6,7}\z/, customer_vault_id) + assert_match(/\A\d+\z/, customer_vault_id) assert response = @gateway.purchase(@amount, customer_vault_id) assert_success response @@ -77,7 +70,7 @@ def test_successful_purchase_using_vault_id_as_integer assert_success response assert_equal 'OK', response.message customer_vault_id = response.params["customer_vault_id"] - assert_match /\A\d{6,7}\z/, customer_vault_id + assert_match /\A\d+\z/, customer_vault_id assert response = @gateway.purchase(@amount, customer_vault_id.to_i) assert_success response @@ -93,13 +86,6 @@ def test_successful_validate_on_store assert_equal 'OK', response.message end - def test_successful_validate_on_store_with_verification_merchant_account - card = credit_card('4111111111111111', :verification_value => '101') - assert response = @gateway.store(card, :verify_card => true, :verification_merchant_account_id => 'sandbox_credit_card_non_default') - assert_success response - assert_equal 'OK', response.message - end - def test_failed_validate_on_store card = credit_card('4000111111111115', :verification_value => '200') assert response = @gateway.store(card, :verify_card => true) @@ -148,6 +134,40 @@ def test_successful_store_with_billing_address assert_equal purchase_response.params['braintree_transaction']['billing_details'], response_billing_details end + def test_successful_store_with_credit_card_token + credit_card = credit_card('5105105105105100') + credit_card_token = generate_unique_id + assert response = @gateway.store(credit_card, credit_card_token: credit_card_token) + assert_success response + assert_equal 'OK', response.message + assert_equal credit_card_token, response.params["braintree_customer"]["credit_cards"][0]["token"] + end + + def test_successful_store_with_new_customer_id + credit_card = credit_card('5105105105105100') + customer_id = generate_unique_id + assert response = @gateway.store(credit_card, customer: customer_id) + assert_success response + assert_equal 'OK', response.message + assert_equal customer_id, response.authorization + assert_equal customer_id, response.params["braintree_customer"]["id"] + end + + def test_successful_store_with_existing_customer_id + credit_card = credit_card('5105105105105100') + customer_id = generate_unique_id + assert response = @gateway.store(credit_card, customer: customer_id) + assert_success response + assert_equal 1, @braintree_backend.customer.find(customer_id).credit_cards.size + + assert response = @gateway.store(credit_card, customer: customer_id) + assert_success response + assert_equal 2, @braintree_backend.customer.find(customer_id).credit_cards.size + assert_equal customer_id, response.params["customer_vault_id"] + assert_equal customer_id, response.authorization + assert_not_nil response.params["credit_card_token"] + end + def test_successful_purchase assert response = @gateway.purchase(@amount, @credit_card, @options) assert_success response @@ -184,7 +204,7 @@ def test_transaction_fails_with_bad_avs_with_avs_rules ) ) - assert_failure response + assert_failure response, "This test will fail unless you specify a braintree_blue_with_processing_rules that has AVS required." assert_equal("Transaction declined - gateway rejected", response.message) assert_equal({'code' => nil, 'message' => nil, 'street_match' => 'N', 'postal_match' => 'N'}, response.avs_result) end @@ -205,7 +225,8 @@ def test_transaction_fails_with_bad_cvv_with_cvv_rules gateway = BraintreeGateway.new(fixtures(:braintree_blue_with_processing_rules)) assert response = gateway.purchase(@amount, credit_card('5105105105105100', :verification_value => '200')) - assert_failure response + assert_failure response, "This test will fail unless you specify a braintree_blue_with_processing_rules that has CVV required." + assert_equal("Transaction declined - gateway rejected", response.message) assert_equal({'code' => 'N', 'message' => ''}, response.cvv_result) end @@ -225,7 +246,7 @@ def test_purchase_with_store_using_random_customer_id ) assert_success response assert_equal '1000 Approved', response.message - assert_match(/\A\d{6,7}\z/, response.params["customer_vault_id"]) + assert_match(/\A\d+\z/, response.params["customer_vault_id"]) assert_equal '510510', response.params["braintree_transaction"]["vault_customer"]["credit_cards"][0]["bin"] assert_equal '510510', @braintree_backend.customer.find(response.params["customer_vault_id"]).credit_cards[0].bin end @@ -290,11 +311,9 @@ def test_unsuccessful_purchase_declined end def test_unsuccessful_purchase_validation_error - assert response = @gateway.purchase(@amount, @credit_card, - @options.merge(:email => "invalid_email") - ) + assert response = @gateway.purchase(@amount, credit_card('51051051051051000')) assert_failure response - assert_equal 'Email is an invalid format. (81604)', response.message + assert_match %r{Credit card number is invalid\. \(81715\)}, response.message assert_equal nil, response.params["braintree_transaction"] end @@ -351,7 +370,7 @@ def test_successful_add_to_vault_with_store_method assert response = @gateway.store(@credit_card) assert_success response assert_equal 'OK', response.message - assert_match(/\A\d{6,7}\z/, response.params["customer_vault_id"]) + assert_match(/\A\d+\z/, response.params["customer_vault_id"]) end def test_failed_add_to_vault @@ -362,7 +381,7 @@ def test_failed_add_to_vault assert_equal nil, response.params["customer_vault_id"] end - def test_unstore + def test_unstore_customer assert response = @gateway.store(@credit_card) assert_success response assert_equal 'OK', response.message @@ -371,33 +390,22 @@ def test_unstore assert_success delete_response end - def test_unstore_with_delete_method + def test_unstore_credit_card assert response = @gateway.store(@credit_card) assert_success response assert_equal 'OK', response.message - assert customer_vault_id = response.params["customer_vault_id"] - assert delete_response = @gateway.delete(customer_vault_id) + assert credit_card_token = response.params["credit_card_token"] + assert delete_response = @gateway.unstore(nil, credit_card_token: credit_card_token) assert_success delete_response end - def test_successful_credit - assert response = @gateway.credit(@amount, @credit_card, @options) - assert_success response - assert_equal '1002 Processed', response.message - assert_equal 'submitted_for_settlement', response.params["braintree_transaction"]["status"] - end - - def test_successful_credit_with_merchant_account_id - assert response = @gateway.credit(@amount, @credit_card, :merchant_account_id => 'sandbox_credit_card_non_default') + def test_unstore_with_delete_method + assert response = @gateway.store(@credit_card) assert_success response - assert_equal '1002 Processed', response.message - assert_equal 'submitted_for_settlement', response.params["braintree_transaction"]["status"] - end - - def test_failed_credit - assert response = @gateway.credit(@amount, credit_card('5105105105105101'), @options) - assert_failure response - assert_equal 'Credit card number is invalid. (81715)', response.message + assert_equal 'OK', response.message + assert customer_vault_id = response.params["customer_vault_id"] + assert delete_response = @gateway.delete(customer_vault_id) + assert_success delete_response end def test_successful_update @@ -411,7 +419,7 @@ def test_successful_update assert_success response assert_equal 'OK', response.message customer_vault_id = response.params["customer_vault_id"] - assert_match(/\A\d{6,7}\z/, customer_vault_id) + assert_match(/\A\d+\z/, customer_vault_id) assert_equal "old@example.com", response.params["braintree_customer"]["email"] assert_equal "Old First", response.params["braintree_customer"]["first_name"] assert_equal "Old Last", response.params["braintree_customer"]["last_name"] @@ -446,11 +454,10 @@ def test_failed_customer_update assert response = @gateway.update( customer_vault_id, - credit_card('5105105105105100'), - :email => "invalid-email" + credit_card('51051051051051001') ) assert_failure response - assert_equal 'Email is an invalid format. (81604)', response.message + assert_equal 'Credit card number is invalid. (81715)', response.message assert_equal nil, response.params["braintree_customer"] assert_equal nil, response.params["customer_vault_id"] end @@ -491,9 +498,43 @@ def test_failed_credit_card_update_on_verify end def test_customer_does_not_have_credit_card_failed_update - customer_without_credit_card = @braintree_backend.create - assert response = @gateway.update(customer_without_credit_card.id, credit_card('5105105105105100')) + customer_without_credit_card = @braintree_backend.customer.create + assert response = @gateway.update(customer_without_credit_card.customer.id, credit_card('5105105105105100')) assert_failure response assert_equal 'Braintree::NotFoundError', response.message end + + def test_successful_credit + assert response = @gateway.credit(@amount, @credit_card, @options) + assert_success response, "You must get credits enabled in your Sandbox account for this to pass." + assert_equal '1002 Processed', response.message + assert_equal 'submitted_for_settlement', response.params["braintree_transaction"]["status"] + end + + def test_failed_credit + assert response = @gateway.credit(@amount, credit_card('5105105105105101'), @options) + assert_failure response + assert_equal 'Credit card number is invalid. (81715)', response.message, "You must get credits enabled in your Sandbox account for this to pass" + end + + def test_successful_credit_with_merchant_account_id + assert response = @gateway.credit(@amount, @credit_card, :merchant_account_id => fixtures(:braintree_blue)[:merchant_account_id]) + assert_success response, "You must specify a valid :merchant_account_id key in your fixtures.yml AND get credits enabled in your Sandbox account for this to pass." + assert_equal '1002 Processed', response.message + assert_equal 'submitted_for_settlement', response.params["braintree_transaction"]["status"] + end + + def test_successful_authorize_with_merchant_account_id + assert response = @gateway.authorize(@amount, @credit_card, :merchant_account_id => fixtures(:braintree_blue)[:merchant_account_id]) + assert_success response, "You must specify a valid :merchant_account_id key in your fixtures.yml for this to pass." + assert_equal '1000 Approved', response.message + assert_equal fixtures(:braintree_blue)[:merchant_account_id], response.params["braintree_transaction"]["merchant_account_id"] + end + + def test_successful_validate_on_store_with_verification_merchant_account + card = credit_card('4111111111111111', :verification_value => '101') + assert response = @gateway.store(card, :verify_card => true, :verification_merchant_account_id => fixtures(:braintree_blue)[:merchant_account_id]) + assert_success response, "You must specify a valid :merchant_account_id key in your fixtures.yml for this to pass." + assert_equal 'OK', response.message + end end diff --git a/test/remote/gateways/remote_card_stream_modern_test.rb b/test/remote/gateways/remote_card_stream_modern_test.rb index 3b65bd9b279..ea2ea0dbfc7 100644 --- a/test/remote/gateways/remote_card_stream_modern_test.rb +++ b/test/remote/gateways/remote_card_stream_modern_test.rb @@ -13,14 +13,6 @@ def setup :brand => :american_express ) - @uk_maestro = credit_card('6759015050123445002', - :month => '12', - :year => '2014', - :issue_number => '0', - :verification_value => '309', - :brand => :switch - ) - @mastercard = credit_card('5301250070000191', :month => '12', :year => '2014', @@ -93,17 +85,6 @@ def setup :description => 'AM test purchase' } - @uk_maestro_options = { - :billing_address => { - :address1 => 'The Parkway', - :address2 => "5258 Larches Approach", - :city => "Hull", - :state => "North Humberside", - :zip => 'HU10 5OP' - }, - :order_id => generate_unique_id, - :description => 'AM test purchase' - } end def test_successful_visacreditcard_authorization_and_capture @@ -248,17 +229,11 @@ def test_declined_mastercard_purchase def test_expired_mastercard @mastercard.year = 2012 assert response = @gateway.purchase(142, @mastercard, @mastercard_options) - assert_equal 'INVALID CARDEXPIRYDATE', response.message + assert_equal 'CARD EXPIRED', response.message assert_failure response assert response.test? end - def test_successful_maestro_purchase - assert response = @gateway.purchase(142, @uk_maestro, @uk_maestro_options) - assert_equal 'APPROVED', response.message - assert_success response - end - def test_successful_amex_purchase assert response = @gateway.purchase(142, @amex, @amex_options) assert_equal 'APPROVED', response.message diff --git a/test/remote/gateways/remote_conekta_test.rb b/test/remote/gateways/remote_conekta_test.rb new file mode 100644 index 00000000000..e6cdf914817 --- /dev/null +++ b/test/remote/gateways/remote_conekta_test.rb @@ -0,0 +1,119 @@ +require 'test_helper' + +class RemoteConektaTest < Test::Unit::TestCase + def setup + @gateway = ConektaGateway.new(fixtures(:conekta)) + + @amount = 300 + + @credit_card = ActiveMerchant::Billing::CreditCard.new( + number: "4111111111111111", + verification_value: "183", + month: "01", + year: "2018", + first_name: "Mario F.", + last_name: "Moreno Reyes" + ) + + @declined_card = ActiveMerchant::Billing::CreditCard.new( + number: "4000000000000002", + verification_value: "183", + month: "01", + year: "2018", + first_name: "Mario F.", + last_name: "Moreno Reyes" + ) + + @options = { + description: 'Blue clip', + address1: "Rio Missisipi #123", + address2: "Paris", + city: "Guerrero", + country: "Mexico", + zip: "5555", + name: "Mario Reyes", + phone: "12345678", + carrier: "Estafeta" + } + end + + def test_successful_purchase + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_success response + assert_equal nil, response.message + end + + def test_unsuccessful_purchase + assert response = @gateway.purchase(@amount, @declined_card, @options) + assert_failure response + assert_equal "The card was declined", response.message + end + + def test_successful_refund + assert response = @gateway.purchase(@amount, @credit_card, @options) + @options[:order_id] = response.params["id"] + assert_success response + assert_equal nil, response.message + + assert response = @gateway.refund(response.authorization, @amount, @options) + assert_success response + assert_equal nil, response.message + end + + def test_unsuccessful_refund + assert response = @gateway.refund("1", @amount, @options) + assert_failure response + assert_equal "The charge does not exist or it is not suitable for this operation", response.message + end + + def test_successful_authorize + assert response = @gateway.authorize(@amount, @credit_card, @options) + assert_success response + assert_equal nil, response.message + end + + def test_unsuccessful_authorize + assert response = @gateway.authorize(@amount, @declined_card, @options) + assert_failure response + assert_equal "The card was declined", response.message + end + + def test_successful_capture + assert response = @gateway.authorize(@amount, @credit_card, @options) + assert_success response + assert_equal nil, response.message + + assert response = @gateway.capture(response.authorization, @amount, @options) + assert_success response + assert_equal nil, response.message + end + + def test_successful_store + assert response = @gateway.store(@credit_card, {name: "John Doe", email: "email@example.com"}) + assert_success response + assert_equal "customer", response.params["object"] + assert_equal "John Doe", response.params["name"] + assert_equal "email@example.com", response.params["email"] + end + + def test_successful_unstore + creation = @gateway.store(@credit_card, {name: "John Doe", email: "email@example.com"}) + assert response = @gateway.unstore(creation.params['id']) + assert_success response + assert_equal true, response.params["deleted"] + end + + def test_unsuccessful_capture + @options[:order_id] = "1" + assert response = @gateway.capture(@amount, @options) + assert_failure response + assert_equal "The charge does not exist or it is not suitable for this operation", response.message + end + + def test_invalid_login + gateway = ConektaGateway.new(key: 'invalid_token') + assert response = gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert_equal "Unrecognized authentication token", response.message + end +end diff --git a/test/remote/gateways/remote_eway_rapid_test.rb b/test/remote/gateways/remote_eway_rapid_test.rb index 1cce2cf232a..1c26a3271d1 100644 --- a/test/remote/gateways/remote_eway_rapid_test.rb +++ b/test/remote/gateways/remote_eway_rapid_test.rb @@ -5,7 +5,7 @@ def setup @gateway = EwayRapidGateway.new(fixtures(:eway_rapid)) @amount = 100 - @failed_amount = 105 + @failed_amount = -100 @credit_card = credit_card("4444333322221111") @options = { @@ -19,7 +19,7 @@ def setup def test_successful_purchase assert response = @gateway.purchase(@amount, @credit_card, @options) assert_success response - assert_equal "Transaction Approved", response.message + assert_equal "Transaction Approved Successful", response.message end def test_fully_loaded_purchase @@ -27,6 +27,7 @@ def test_fully_loaded_purchase :redirect_url => "http://awesomesauce.com", :ip => "0.0.0.0", :application_id => "Woohoo", + :transaction_type => "Purchase", :description => "Description", :order_id => "orderid1", :currency => "AUD", @@ -64,49 +65,61 @@ def test_fully_loaded_purchase def test_failed_purchase assert response = @gateway.purchase(@failed_amount, @credit_card, @options) assert_failure response - assert_equal "Do Not Honour", response.message + assert_equal "Invalid Payment TotalAmount", response.message end - def test_failed_setup_purchase - assert response = @gateway.setup_purchase(@amount, :redirect_url => "") - assert_failure response - assert_equal "V6047", response.message - end - - def test_failed_run_purchase - setup_response = @gateway.setup_purchase(@amount, @options) - assert_success setup_response + def test_successful_refund + # purchase + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_success response + assert_equal "Transaction Approved Successful", response.message - assert response = @gateway.send(:run_purchase, "bogus", @credit_card, setup_response.params["formactionurl"]) - assert_failure response - assert_match(%r{Access Code Invalid}, response.message) + # refund + assert response = @gateway.refund(@amount, response.authorization, @options) + assert_success response + assert_equal "Transaction Approved Successful", response.message end - def test_failed_status - setup_response = @gateway.setup_purchase(@failed_amount, @options) - assert_success setup_response - - assert run_response = @gateway.send(:run_purchase, setup_response.authorization, @credit_card, setup_response.params["formactionurl"]) - assert_success run_response - - response = @gateway.status(run_response.authorization) + def test_failed_refund + assert response = @gateway.refund(@amount, 'fakeid', @options) assert_failure response - assert_equal "Do Not Honour", response.message - assert_equal run_response.authorization, response.authorization + assert_equal "System Error", response.message end def test_successful_store @options[:billing_address].merge!(:title => "Dr.") assert response = @gateway.store(@credit_card, @options) assert_success response - assert_equal "Transaction Approved", response.message + assert_equal "Transaction Approved Successful", response.message end def test_failed_store @options[:billing_address].merge!(:country => nil) assert response = @gateway.store(@credit_card, @options) assert_failure response - assert_equal "V6044", response.message + assert_equal "V6044", response.params["Errors"] + assert_equal "Customer CountryCode Required", response.message + end + + def test_successful_update + @options[:billing_address].merge!(:title => "Dr.") + assert response = @gateway.store(@credit_card, @options) + assert_success response + assert_equal "Transaction Approved Successful", response.message + assert response = @gateway.update(response.authorization, @credit_card, @options) + assert_success response + assert_equal "Transaction Approved Successful", response.message + end + + def test_successful_store_purchase + @options[:billing_address].merge!(:title => "Dr.") + assert response = @gateway.store(@credit_card, @options) + assert_success response + assert_equal "Transaction Approved Successful", response.message + + assert response = @gateway.purchase(@amount, response.authorization, {transaction_type: 'MOTO'}) + assert_success response + assert_equal "Transaction Approved Successful", response.message end def test_invalid_login diff --git a/test/remote/gateways/remote_merchant_warrior_test.rb b/test/remote/gateways/remote_merchant_warrior_test.rb index dadef6c86a4..da81661c4ef 100644 --- a/test/remote/gateways/remote_merchant_warrior_test.rb +++ b/test/remote/gateways/remote_merchant_warrior_test.rb @@ -16,7 +16,7 @@ def setup ) @options = { - :address => { + :billing_address => { :name => 'Longbob Longsen', :country => 'AU', :state => 'Queensland', @@ -24,7 +24,7 @@ def setup :address1 => '123 test st', :zip => '4000' }, - :transaction_product => 'TestProduct' + :description => 'TestProduct' } end diff --git a/test/remote/gateways/remote_mercury_test.rb b/test/remote/gateways/remote_mercury_test.rb index 5fda0efb03c..254b4793f7a 100644 --- a/test/remote/gateways/remote_mercury_test.rb +++ b/test/remote/gateways/remote_mercury_test.rb @@ -71,6 +71,14 @@ def test_successful_purchase assert_equal "0.50", response.params["purchase"] end + def test_store + response = @gateway.store(@credit_card, @options) + + assert_success response + assert response.params.has_key?("record_no") + assert response.params['record_no'] != '' + end + def test_credit response = @gateway.credit(50, @credit_card, @options) diff --git a/test/remote/gateways/remote_pac_net_raven_test.rb b/test/remote/gateways/remote_pac_net_raven_test.rb new file mode 100644 index 00000000000..2f213280180 --- /dev/null +++ b/test/remote/gateways/remote_pac_net_raven_test.rb @@ -0,0 +1,207 @@ +require 'test_helper' + +class RemotePacNetRavenGatewayTest < Test::Unit::TestCase + def setup + @gateway = PacNetRavenGateway.new(fixtures(:raven_pac_net)) + + @amount = 100 + @credit_card = credit_card('4000000000000028') + @declined_card = credit_card('5100000000000040') + + @options = { + billing_address: address + } + end + + def test_successful_purchase + assert purchase = @gateway.purchase(@amount, @credit_card, @options) + assert_success purchase + assert purchase.params['ApprovalCode'] + assert purchase.params['TrackingNumber'] + assert_nil purchase.params['ErrorCode'] + assert_equal 'Approved', purchase.params['Status'] + assert_equal 'ok', purchase.params['RequestResult'] + assert_nil purchase.params['Message'] + assert_equal 'This transaction has been approved', purchase.message + end + + def test_invalid_credit_card_number_purchese + @credit_card = credit_card('0000') + assert purchase = @gateway.purchase(@amount, @credit_card, @options) + assert_failure purchase + assert_nil purchase.params['ApprovalCode'] + assert purchase.params['TrackingNumber'] + assert_equal 'invalid:cardNumber', purchase.params['ErrorCode'] + assert_equal 'Invalid:CardNumber', purchase.params['Status'] + assert_equal 'ok', purchase.params['RequestResult'] + assert_equal "Error processing transaction because CardNumber \"0000\" is not between 12 and 19 in length.", purchase.params['Message'] + end + + def test_expired_credit_card_purchese + @credit_card.month = 9 + @credit_card.year = 2012 + assert purchase = @gateway.purchase(@amount, @credit_card, @options) + assert_failure purchase + assert_nil purchase.params['ApprovalCode'] + assert purchase.params['TrackingNumber'] + assert_equal 'invalid:CustomerCardExpiryDate', purchase.params['ErrorCode'] + assert_equal 'Invalid:CustomerCardExpiryDate', purchase.params['Status'] + assert_equal 'ok', purchase.params['RequestResult'] + assert_equal "Invalid because the card expiry date (mmyy) \"0912\" is not a date in the future", purchase.params['Message'] + end + + def test_declined_purchese + assert purchase = @gateway.purchase(@amount, @declined_card, @options) + assert_failure purchase + assert_equal 'RepeatDeclined', purchase.message + end + + def test_successful_authorization + assert auth = @gateway.authorize(@amount, @credit_card, @options) + assert_success auth + assert auth.params['ApprovalCode'] + assert auth.params['TrackingNumber'] + assert_nil auth.params['ErrorCode'] + assert_nil auth.params['Message'] + assert_equal 'Approved', auth.params['Status'] + assert_equal 'ok', auth.params['RequestResult'] + assert_equal 'This transaction has been approved', auth.message + end + + def test_invalid_credit_card_number_authorization + @credit_card = credit_card('0000') + assert auth = @gateway.authorize(@amount, @credit_card, @options) + assert_failure auth + assert_nil auth.params['ApprovalCode'] + assert auth.params['TrackingNumber'] + assert_equal 'invalid:cardNumber', auth.params['ErrorCode'] + assert_equal 'Invalid:CardNumber', auth.params['Status'] + assert_equal 'ok', auth.params['RequestResult'] + assert_equal "Error processing transaction because CardNumber \"0000\" is not between 12 and 19 in length.", auth.params['Message'] + end + + def test_expired_credit_card_authorization + @credit_card.month = 9 + @credit_card.year = 2012 + assert auth = @gateway.authorize(@amount, @credit_card, @options) + assert_failure auth + assert_nil auth.params['ApprovalCode'] + assert auth.params['TrackingNumber'] + assert_equal 'invalid:CustomerCardExpiryDate', auth.params['ErrorCode'] + assert_equal 'Invalid:CustomerCardExpiryDate', auth.params['Status'] + assert_equal 'ok', auth.params['RequestResult'] + assert_equal "Invalid because the card expiry date (mmyy) \"0912\" is not a date in the future", auth.params['Message'] + end + + def test_declined_authorization + assert auth = @gateway.authorize(@amount, @declined_card, @options) + assert_failure auth + assert_equal 'RepeatDeclined', auth.message + end + + def test_successful_refund + assert purchase = @gateway.purchase(@amount, @credit_card, @options) + assert refund = @gateway.refund(@amount, purchase.authorization) + assert_success refund + assert refund.params['ApprovalCode'] + assert refund.params['TrackingNumber'] + assert_nil refund.params['ErrorCode'] + assert_equal 'Approved', refund.params['Status'] + assert_equal 'ok', refund.params['RequestResult'] + assert_nil refund.params['Message'] + assert_equal 'This transaction has been approved', refund.message + end + + def test_amount_greater_than_original_amount_refund + assert purchase = @gateway.purchase(100, @credit_card, @options) + assert_success purchase + + assert refund = @gateway.refund(200, purchase.authorization) + assert_failure refund + assert_nil refund.params['ApprovalCode'] + assert refund.params['TrackingNumber'] + assert_equal 'invalid:RefundAmountGreaterThanOriginalAmount', refund.params['ErrorCode'] + assert_equal 'Invalid:RefundAmountGreaterThanOriginalAmount', refund.params['Status'] + assert_equal 'ok', refund.params['RequestResult'] + assert_equal "Invalid because the payment amount cannot be greater than the original charge.", refund.params['Message'] + assert_equal 'Invalid because the payment amount cannot be greater than the original charge.', refund.message + end + + def test_purchase_and_void + purchase = @gateway.purchase(@amount, @credit_card, @options) + assert void = @gateway.void(purchase.authorization, {:pymt_type => purchase.params['PymtType']}) + assert_success void + assert void.params['ApprovalCode'] + assert void.params['TrackingNumber'] + assert_nil void.params['ErrorCode'] + assert_equal 'ok', void.params['RequestResult'] + assert_nil void.params['Message'] + assert_equal 'Voided', void.params['Status'] + assert_equal "This transaction has been voided", void.message + end + + def test_authorize_and_void + auth = @gateway.authorize(@amount, @credit_card, @options) + assert void = @gateway.void(auth.authorization) + assert_failure void + assert void.params['ApprovalCode'] + assert void.params['TrackingNumber'] + assert_equal 'error:canNotBeVoided', void.params['ErrorCode'] + assert_equal 'ok', void.params['RequestResult'] + assert_equal "Error processing transaction because the payment may not be voided.", void.params['Message'] + assert_equal 'Approved', void.params['Status'] + assert_equal "Error processing transaction because the payment may not be voided.", void.message + end + + def test_authorize_capture_and_void + auth = @gateway.authorize(@amount, @credit_card, @options) + assert capture = @gateway.capture(@amount, auth.authorization) + assert void = @gateway.void(capture.authorization, {:pymt_type => capture.params['PymtType']}) + assert_failure void + assert void.params['ApprovalCode'] + assert void.params['TrackingNumber'] + assert_equal 'error:canNotBeVoided', void.params['ErrorCode'] + assert_equal 'ok', void.params['RequestResult'] + assert_equal "Error processing transaction because the payment may not be voided.", void.params['Message'] + assert_equal 'Approved', void.params['Status'] + assert_equal "Error processing transaction because the payment may not be voided.", void.message + end + + def test_successful_capture + auth = @gateway.authorize(@amount, @credit_card, @options) + assert capture = @gateway.capture(@amount, auth.authorization) + assert_success capture + assert capture.params['ApprovalCode'] + assert capture.params['TrackingNumber'] + assert_nil capture.params['ErrorCode'] + assert_equal 'Approved', capture.params['Status'] + assert_equal 'ok', capture.params['RequestResult'] + assert_nil capture.params['Message'] + assert_equal 'This transaction has been approved', capture.message + end + + def test_invalid_preauth_number_capture + assert capture = @gateway.capture(@amount, '') + assert_failure capture + assert_nil capture.params['ApprovalCode'] + assert capture.params['TrackingNumber'] + assert_equal 'invalid:PreAuthNumber', capture.params['ErrorCode'] + assert_equal 'Invalid:PreauthNumber', capture.params['Status'] + assert_equal 'ok', capture.params['RequestResult'] + assert_equal "Error processing transaction because the pre-auth number \"0\" does not correspond to a pre-existing payment.", capture.params['Message'] + assert capture.message.include?('Error processing transaction because the pre-auth number') + end + + def test_insufficient_preauth_amount_capture + auth = @gateway.authorize(100, @credit_card, @options) + assert capture = @gateway.capture(200, auth.authorization) + assert_failure capture + assert_nil capture.params['ApprovalCode'] + assert capture.params['TrackingNumber'] + assert_equal 'rejected:PreauthAmountInsufficient', capture.params['ErrorCode'] + assert_equal 'Rejected:PreauthAmountInsufficient', capture.params['Status'] + assert_equal 'ok', capture.params['RequestResult'] + assert_equal "Invalid because the preauthorization amount 100 is insufficient", capture.params['Message'] + assert_equal 'Invalid because the preauthorization amount 100 is insufficient', capture.message + end +end diff --git a/test/remote/gateways/remote_pay_junction_test.rb b/test/remote/gateways/remote_pay_junction_test.rb index db8724e1811..4744ea59995 100644 --- a/test/remote/gateways/remote_pay_junction_test.rb +++ b/test/remote/gateways/remote_pay_junction_test.rb @@ -98,7 +98,7 @@ def test_successful_void end def test_successful_instant_purchase - # this takes advatange of the PayJunction feature where another + # this takes advantage of the PayJunction feature where another # transaction can be executed if you have the transaction ID of a # previous successful transaction. diff --git a/test/remote/gateways/remote_payex_test.rb b/test/remote/gateways/remote_payex_test.rb new file mode 100644 index 00000000000..5483a8f1eda --- /dev/null +++ b/test/remote/gateways/remote_payex_test.rb @@ -0,0 +1,120 @@ +require 'test_helper' + +class RemotePayexTest < Test::Unit::TestCase + + def setup + @gateway = PayexGateway.new(fixtures(:payex)) + + @amount = 1000 + # cvv 210, expire date 02/14 + @credit_card = credit_card('4581090329655682', verification_value: 210, month: 2, year: 14) + @declined_card = credit_card('4000300011112220') + + @options = { + :order_id => '1234', + } + end + + def test_successful_purchase + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_success response + assert_equal 'OK', response.message + end + + def test_unsuccessful_purchase + assert response = @gateway.purchase(@amount, @declined_card, @options) + assert_failure response + # we can't test for a message since the messages vary so much + assert_not_equal 'OK', response.message + end + + def test_authorize_and_capture + amount = @amount + assert response = @gateway.authorize(amount, @credit_card, @options) + assert_success response + assert_equal 'OK', response.message + + assert response.authorization + assert response = @gateway.capture(amount, response.authorization) + assert_success response + end + + def test_failed_capture + assert response = @gateway.capture(@amount, '1') + assert_failure response + assert_not_equal 'OK', response.message + assert_not_equal 'RecordNotFound', response.params[:status_errorcode] + end + + def test_authorization_and_void + assert response = @gateway.authorize(@amount, @credit_card, @options) + assert_success response + + assert response = @gateway.void(response.authorization) + assert_success response + end + + def test_unsuccessful_void + assert response = @gateway.void("1") + assert_failure response + assert_not_equal 'OK', response.message + assert_match /1/, response.message + end + + def test_successful_refund + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_success response + assert response.authorization + assert response = @gateway.refund(@amount - 200, response.authorization, order_id: '123') + assert_success response + end + + def test_unsuccessful_refund + assert response = @gateway.refund(@amount, "1", order_id: '123') + assert_failure response + assert_not_equal 'OK', response.message + assert_match /1/, response.message + end + + def test_successful_store_and_purchase + assert response = @gateway.store(@credit_card, @options) + assert_success response + assert_equal 'OK', response.message + + assert response = @gateway.purchase(@amount, response.authorization, @options.merge({order_id: '5678'})) + assert_success response + assert_equal 'OK', response.message + end + + def test_successful_store_and_authorize_and_capture + assert response = @gateway.store(@credit_card, @options) + assert_success response + assert_equal 'OK', response.message + + assert response = @gateway.authorize(@amount, response.authorization, @options.merge({order_id: '5678'})) + assert_success response + assert_equal 'OK', response.message + assert response.authorization + + assert response = @gateway.capture(@amount, response.authorization) + assert_success response + end + + def test_successful_unstore + assert response = @gateway.store(@credit_card, @options) + assert_equal 'OK', response.message + assert response = @gateway.unstore(response.authorization) + assert_success response + assert_equal 'OK', response.message + end + + def test_invalid_login + gateway = PayexGateway.new( + :account => '1', + :encryption_key => '1' + ) + assert response = gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert_not_equal 'OK', response.message + end +end diff --git a/test/remote/gateways/remote_payflow_test.rb b/test/remote/gateways/remote_payflow_test.rb index b3d49a939d3..0622c7957fd 100644 --- a/test/remote/gateways/remote_payflow_test.rb +++ b/test/remote/gateways/remote_payflow_test.rb @@ -5,17 +5,24 @@ def setup ActiveMerchant::Billing::Base.gateway_mode = :test @gateway = PayflowGateway.new(fixtures(:payflow)) - - @credit_card = credit_card('5105105105105100', + + @credit_card = credit_card( + '5105105105105100', :brand => 'master' ) - @options = { :billing_address => address, - :email => 'cody@example.com', - :customer => 'codyexample' - } + @options = { + :billing_address => address, + :email => 'cody@example.com', + :customer => 'codyexample' + } + + @check = check( + :routing_number => '111111118', + :account_number => '1234567801' + ) end - + def test_successful_purchase assert response = @gateway.purchase(100000, @credit_card, @options) assert_equal "Approved", response.message @@ -23,14 +30,28 @@ def test_successful_purchase assert response.test? assert_not_nil response.authorization end - + def test_declined_purchase assert response = @gateway.purchase(210000, @credit_card, @options) assert_equal 'Declined', response.message assert_failure response assert response.test? end - + + # Additional steps are required to enable ACH in a Payflow Pro account. + # See the "Payflow ACH Payment Service Guide" for more details: + # http://www.paypalobjects.com/webstatic/en_US/developer/docs/pdf/pp_achpayment_guide.pdf + # + # Also, when testing against the pilot-payflowpro.paypal.com endpoint, ACH must be enabled by Payflow support. + # This can be accomplished by sending an email to payflow-support@paypal.com with your Merchant Login. + def test_successful_ach_purchase + assert response = @gateway.purchase(50, @check) + assert_equal "Approved", response.message + assert_success response + assert response.test? + assert_not_nil response.authorization + end + def test_successful_authorization assert response = @gateway.authorize(100, @credit_card, @options) assert_equal "Approved", response.message @@ -47,23 +68,23 @@ def test_authorize_and_capture assert capture = @gateway.capture(100, auth.authorization) assert_success capture end - + def test_authorize_and_partial_capture assert auth = @gateway.authorize(100 * 2, @credit_card, @options) assert_success auth assert_equal 'Approved', auth.message assert auth.authorization - + assert capture = @gateway.capture(100, auth.authorization) assert_success capture end - + def test_failed_capture assert response = @gateway.capture(100, '999') assert_failure response assert_equal 'Invalid tender', response.message end - + def test_authorize_and_void assert auth = @gateway.authorize(100, @credit_card, @options) assert_success auth @@ -72,7 +93,7 @@ def test_authorize_and_void assert void = @gateway.void(auth.authorization) assert_success void end - + def test_invalid_login gateway = PayflowGateway.new( :login => '', @@ -82,27 +103,27 @@ def test_invalid_login assert_equal 'Invalid vendor account', response.message assert_failure response end - + def test_duplicate_request_id request_id = Digest::MD5.hexdigest(rand.to_s) @gateway.expects(:generate_unique_id).times(2).returns(request_id) - + response1 = @gateway.purchase(100, @credit_card, @options) assert response1.success? assert_nil response1.params['duplicate'] - + response2 = @gateway.purchase(100, @credit_card, @options) assert response2.success? assert response2.params['duplicate'] end - + def test_create_recurring_profile response = @gateway.recurring(1000, @credit_card, :periodicity => :monthly) assert_success response assert !response.params['profile_id'].blank? assert response.test? end - + def test_create_recurring_profile_with_invalid_date response = @gateway.recurring(1000, @credit_card, :periodicity => :monthly, :starting_at => Time.now) assert_failure response @@ -110,18 +131,18 @@ def test_create_recurring_profile_with_invalid_date assert response.params['profile_id'].blank? assert response.test? end - + def test_create_and_cancel_recurring_profile response = @gateway.recurring(1000, @credit_card, :periodicity => :monthly) assert_success response assert !response.params['profile_id'].blank? assert response.test? - + response = @gateway.cancel_recurring(response.params['profile_id']) assert_success response assert response.test? end - + def test_full_feature_set_for_recurring_profiles # Test add @options.update( @@ -137,7 +158,7 @@ def test_full_feature_set_for_recurring_profiles assert response.test? assert !response.params['profile_id'].blank? @recurring_profile_id = response.params['profile_id'] - + # Test modify @options.update( :periodicity => :monthly, @@ -150,19 +171,19 @@ def test_full_feature_set_for_recurring_profiles assert_equal "0", response.params['result'] assert_success response assert response.test? - + # Test inquiry - response = @gateway.recurring_inquiry(@recurring_profile_id) + response = @gateway.recurring_inquiry(@recurring_profile_id) assert_equal "0", response.params['result'] assert_success response assert response.test? - + # Test payment history inquiry response = @gateway.recurring_inquiry(@recurring_profile_id, :history => true) assert_equal '0', response.params['result'] assert_success response assert response.test? - + # Test cancel response = @gateway.cancel_recurring(@recurring_profile_id) assert_equal "Approved", response.params['message'] @@ -170,7 +191,7 @@ def test_full_feature_set_for_recurring_profiles assert_success response assert response.test? end - + # Note that this test will only work if you enable reference transactions!! def test_reference_purchase assert response = @gateway.purchase(10000, @credit_card, @options) @@ -178,60 +199,68 @@ def test_reference_purchase assert_success response assert response.test? assert_not_nil pn_ref = response.authorization - + # now another purchase, by reference assert response = @gateway.purchase(10000, pn_ref) assert_equal "Approved", response.message assert_success response assert response.test? end - + def test_recurring_with_initial_authorization - response = @gateway.recurring(1000, @credit_card, + response = @gateway.recurring(1000, @credit_card, :periodicity => :monthly, :initial_transaction => { - :brand => :authorization + :type => :authorization } ) - + assert_success response assert !response.params['profile_id'].blank? assert response.test? end - + def test_recurring_with_initial_authorization - response = @gateway.recurring(1000, @credit_card, + response = @gateway.recurring(1000, @credit_card, :periodicity => :monthly, :initial_transaction => { - :brand => :purchase, + :type => :purchase, :amount => 500 } ) - + assert_success response assert !response.params['profile_id'].blank? assert response.test? end - - def test_purchase_and_referenced_credit + + def test_purchase_and_refund amount = 100 - + assert purchase = @gateway.purchase(amount, @credit_card, @options) assert_success purchase assert_equal 'Approved', purchase.message assert !purchase.authorization.blank? - - assert credit = @gateway.credit(amount, purchase.authorization) + + assert credit = @gateway.refund(amount, purchase.authorization) assert_success credit end - - # The default security setting for Payflow Pro accounts is Allow + + # The default security setting for Payflow Pro accounts is Allow # non-referenced credits = No. # - # Non-referenced credits will fail with Result code 117 (failed the security + # Non-referenced credits will fail with Result code 117 (failed the security # check) unless Allow non-referenced credits = Yes in PayPal manager - def test_purchase_and_non_referenced_credit + def test_purchase_and_credit assert credit = @gateway.credit(100, @credit_card, @options) assert_success credit end + + def test_successful_ach_credit + assert response = @gateway.credit(50, @check) + assert_equal "Approved", response.message + assert_success response + assert response.test? + assert_not_nil response.authorization + end end diff --git a/test/remote/gateways/remote_paymill_test.rb b/test/remote/gateways/remote_paymill_test.rb index c4189267bc2..68c01b2e849 100644 --- a/test/remote/gateways/remote_paymill_test.rb +++ b/test/remote/gateways/remote_paymill_test.rb @@ -5,31 +5,44 @@ def setup @gateway = PaymillGateway.new(fixtures(:paymill)) @amount = 100 - @credit_card = credit_card('5105105105105100') + @credit_card = credit_card('5500000000000004') + @declined_card = credit_card('5105105105105100', month: 5, year: 2020) end def test_successful_purchase assert response = @gateway.purchase(@amount, @credit_card) assert_success response - assert_equal 'Transaction approved', response.message + assert_equal 'General success response.', response.message end - def test_failed_purchase_with_invalid_card + def test_failed_store_card_attempting_purchase @credit_card.number = '' assert response = @gateway.purchase(@amount, @credit_card) assert_failure response assert_equal 'Account or Bank Details Incorrect', response.message end + def test_failed_purchase + assert response = @gateway.purchase(@amount, @declined_card) + assert_failure response + assert_equal 'Card declined by authorization system.', response.message + end + def test_successful_authorize_and_capture assert response = @gateway.authorize(@amount, @credit_card) assert_success response - assert_equal 'Transaction approved', response.message + assert_equal 'General success response.', response.message assert response.authorization assert capture_response = @gateway.capture(@amount, response.authorization) assert_success capture_response - assert_equal 'Transaction approved', capture_response.message + assert_equal 'General success response.', capture_response.message + end + + def test_failed_authorize + assert response = @gateway.authorize(@amount, @declined_card) + assert_failure response + assert_equal 'Card declined by authorization system.', response.message end def test_failed_capture @@ -47,12 +60,12 @@ def test_failed_capture def test_successful_authorize_and_void assert response = @gateway.authorize(@amount, @credit_card) assert_success response - assert_equal 'Transaction approved', response.message + assert_equal 'General success response.', response.message assert response.authorization assert void_response = @gateway.void(response.authorization) assert_success void_response - assert_equal 'Transaction approved', void_response.message + assert_equal 'Transaction approved.', void_response.message end def test_successful_refund @@ -62,7 +75,7 @@ def test_successful_refund assert refund = @gateway.refund(@amount, response.authorization) assert_success refund - assert_equal 'Transaction approved', refund.message + assert_equal 'General success response.', refund.message end def test_failed_refund @@ -107,10 +120,4 @@ def test_successful_store_and_authorize assert_success authorize end - # Paymill doesn't yet offer a way to trigger a decline on a test account. - # def test_failed_purchase_with_declined_credit_card - # assert response = @gateway.purchase(@amount, @declined_card) - # assert_failure response - # assert_equal 'Unable to process the purchase transaction.', response.message - # end end diff --git a/test/remote/gateways/remote_payscout_test.rb b/test/remote/gateways/remote_payscout_test.rb new file mode 100644 index 00000000000..e3ad29ce708 --- /dev/null +++ b/test/remote/gateways/remote_payscout_test.rb @@ -0,0 +1,160 @@ +require 'test_helper' + +class RemotePayscoutTest < Test::Unit::TestCase + def setup + @gateway = PayscoutGateway.new(fixtures(:payscout)) + + @amount = 100 + @credit_card = credit_card('4111111111111111', verification_value: 999) + @declined_card = credit_card('34343') + + @options = { + :order_id => '1', + :description => 'Store Purchase', + :billing_address => address + } + end + + ########## Purchase ########## + + def test_cvv_fail_purchase + @credit_card = credit_card('4111111111111111') + assert response = @gateway.purchase(@amount, @credit_card, @options) + + + assert_success response + assert_equal 'The transaction has been approved', response.message + assert_equal 'N', response.cvv_result['code'] + end + + + def test_approved_purchase + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_success response + assert_equal 'The transaction has been approved', response.message + end + + def test_declined_purchase + @amount = 60 + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert_equal 'The transaction has been declined', response.message + end + + ########## Authorize ########## + + def test_approved_authorization + assert response = @gateway.authorize(@amount, @credit_card, @options) + assert_success response + assert_equal 'The transaction has been approved', response.message + end + + def test_declined_authorization + @amount = 60 + assert response = @gateway.authorize(@amount, @credit_card, @options) + assert_failure response + assert_equal 'The transaction has been declined', response.message + end + + ########## Capture ########## + + def test_approved_capture + amount = @amount + assert auth = @gateway.authorize(amount, @credit_card, @options) + assert_success auth + assert_equal 'The transaction has been approved', auth.message + assert auth.authorization + assert capture = @gateway.capture(amount, auth.authorization) + assert_success capture + end + + def test_invalid_amount_capture + amount = @amount + assert auth = @gateway.authorize(amount, @credit_card, @options) + assert_success auth + assert_equal 'The transaction has been approved', auth.message + assert auth.authorization + amount = 200 + assert capture = @gateway.capture(amount, auth.authorization) + assert_failure capture + assert_match 'The specified amount of 2.00 exceeds the authorization amount of 1.00', capture.message + end + + def test_not_found_transaction_id_capture + assert capture = @gateway.capture(@amount, '1234567890') + assert_failure capture + assert_match 'Transaction not found', capture.message + end + + def test_invalid_transaction_id_capture + assert capture = @gateway.capture(@amount, '') + assert_failure capture + assert_match 'Invalid Transaction ID', capture.message + end + + ########## Refund ########## + + def test_approved_refund + purchase = @gateway.purchase(@amount, @credit_card, @options) + assert refund = @gateway.refund(@amount, purchase.authorization) + assert_success refund + assert_equal "The transaction has been approved", refund.message + end + + def test_not_found_transaction_id_refund + assert refund = @gateway.refund(@amount, '1234567890') + assert_failure refund + assert_match "Transaction not found", refund.message + end + + def test_invalid_transaction_id_refund + assert refund = @gateway.refund(@amount, '') + assert_failure refund + assert_match "Invalid Transaction ID", refund.message + end + + def test_invalid_amount_refund + purchase = @gateway.purchase(@amount, @credit_card, @options) + assert refund = @gateway.refund(200, purchase.authorization) + assert_failure refund + assert_match "Refund amount may not exceed the transaction balance", refund.message + end + + ########## Void ########## + + def test_approved_void_purchase + purchase = @gateway.purchase(@amount, @credit_card, @options) + assert void = @gateway.void(purchase.authorization) + assert_success void + assert_equal "The transaction has been approved", void.message + end + + def test_approved_void_authorization + auth = @gateway.authorize(@amount, @credit_card, @options) + assert void = @gateway.void(auth.authorization) + assert_success void + assert_equal "The transaction has been approved", void.message + end + + def test_invalid_transaction_id_void + assert void = @gateway.void('') + assert_failure void + assert_match "Invalid Transaction ID", void.message + end + + def test_not_found_transaction_id_void + assert void = @gateway.void('1234567890') + assert_failure void + assert_match "Transaction not found", void.message + end + + def test_invalid_credentials + gateway = PayscoutGateway.new( + :username => 'xxx', + :password => 'xxx' + ) + assert response = gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert_equal 'Authentication Failed', response.message + end +end diff --git a/test/remote/gateways/remote_paystation_test.rb b/test/remote/gateways/remote_paystation_test.rb index 40b3a732080..b3277889c34 100644 --- a/test/remote/gateways/remote_paystation_test.rb +++ b/test/remote/gateways/remote_paystation_test.rb @@ -78,7 +78,7 @@ def test_authorize_and_capture end def test_capture_without_cvv - # for some merchant accounts, paystation requires you send through the card vertification value + # for some merchant accounts, paystation requires you send through the card verification value # on a capture request assert auth = @gateway.authorize(@successful_amount, @credit_card, @options.merge(:order_id => get_uid)) diff --git a/test/remote/gateways/remote_so_easy_pay_test.rb b/test/remote/gateways/remote_so_easy_pay_test.rb new file mode 100644 index 00000000000..8803620f610 --- /dev/null +++ b/test/remote/gateways/remote_so_easy_pay_test.rb @@ -0,0 +1,66 @@ +require 'test_helper' + +class RemoteSoEasyPayTest < Test::Unit::TestCase + + + def setup + @gateway = SoEasyPayGateway.new(fixtures(:so_easy_pay)) + + @amount = 100 + @credit_card = credit_card('4111111111111111', {:verification_value => '000', :month => '12', :year => '2015'}) + @declined_card = credit_card('4000300011112220') + + @options = { + :currency => 'EUR', + :ip => '192.168.19.123', + :email => 'test@blaha.com', + :order_id => generate_unique_id, + :billing_address => address, + :description => 'Store Purchase' + } + end + + def test_successful_purchase + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_success response + assert_equal 'Transaction successful', response.message + end + + def test_unsuccessful_purchase + assert response = @gateway.purchase(@amount, @declined_card, @options) + assert_failure response + end + + def test_authorize_and_capture + amount = @amount + assert auth = @gateway.authorize(amount, @credit_card, @options) + assert_success auth + assert auth.authorization + assert capture = @gateway.capture(amount, auth.authorization) + assert_success capture + end + + def test_failed_capture + assert response = @gateway.capture(@amount, '') + assert_failure response + end + + def test_successful_void + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_success response + + assert response = @gateway.void(response.authorization) + assert_success response + end + + def test_invalid_login + gateway = SoEasyPayGateway.new( + :login => 'one', + :password => 'wrong' + ) + assert response = gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert_equal 'Website verification failed, wrong websiteID or password', response.message + end +end + diff --git a/test/remote/gateways/remote_stripe_test.rb b/test/remote/gateways/remote_stripe_test.rb index 06c737cd1dc..f82fb5dbf5a 100644 --- a/test/remote/gateways/remote_stripe_test.rb +++ b/test/remote/gateways/remote_stripe_test.rb @@ -4,17 +4,18 @@ class RemoteStripeTest < Test::Unit::TestCase def setup @gateway = StripeGateway.new(fixtures(:stripe)) + @currency = fixtures(:stripe)["currency"] @amount = 100 + # You may have to update the currency, depending on your tenant @credit_card = credit_card('4242424242424242') @declined_card = credit_card('4000') @new_credit_card = credit_card('5105105105105100') @options = { - :currency => 'CAD', + :currency => @currency, :description => 'ActiveMerchant Test Purchase', - :email => 'wow@example.com', - :currency => 'CAD' + :email => 'wow@example.com' } end @@ -23,17 +24,8 @@ def test_successful_purchase assert_success response assert_equal "charge", response.params["object"] assert response.params["paid"] - end - - def test_purchase_description - assert response = @gateway.purchase(@amount, @credit_card, { :currency => 'CAD', :description => "TheDescription", :email => "email@example.com" }) - assert_equal "TheDescription", response.params["description"], "Use the description if it's specified." - - assert response = @gateway.purchase(@amount, @credit_card, { :currency => 'CAD', :email => "email@example.com" }) - assert_equal "email@example.com", response.params["description"], "Use the email if no description is specified." - - assert response = @gateway.purchase(@amount, @credit_card, { :currency => 'CAD' }) - assert_nil response.params["description"], "No description or email specified." + assert_equal "ActiveMerchant Test Purchase", response.params["description"] + assert_equal "wow@example.com", response.params["metadata"]["email"] end def test_unsuccessful_purchase @@ -46,6 +38,8 @@ def test_authorization_and_capture assert authorization = @gateway.authorize(@amount, @credit_card, @options) assert_success authorization assert !authorization.params["captured"] + assert_equal "ActiveMerchant Test Purchase", authorization.params["description"] + assert_equal "wow@example.com", authorization.params["metadata"]["email"] assert capture = @gateway.capture(@amount, authorization.authorization) assert_success capture @@ -89,7 +83,7 @@ def test_unsuccessful_refund end def test_successful_store - assert response = @gateway.store(@credit_card, {:currency => 'CAD', :description => "Active Merchant Test Customer", :email => "email@example.com"}) + assert response = @gateway.store(@credit_card, {:currency => @currency, :description => "Active Merchant Test Customer", :email => "email@example.com"}) assert_success response assert_equal "customer", response.params["object"] assert_equal "Active Merchant Test Customer", response.params["description"] @@ -103,17 +97,29 @@ def test_successful_update creation = @gateway.store(@credit_card, {:description => "Active Merchant Update Customer"}) assert response = @gateway.update(creation.params['id'], @new_credit_card) assert_success response - assert_equal "Active Merchant Update Customer", response.params["description"] - first_card = response.params["cards"]["data"].first - assert_equal response.params["default_card"], first_card["id"] + customer_response = response.responses.last + assert_equal "Active Merchant Update Customer", customer_response.params["description"] + first_card = customer_response.params["cards"]["data"].first + assert_equal customer_response.params["default_card"], first_card["id"] assert_equal @new_credit_card.last_digits, first_card["last4"] end def test_successful_unstore creation = @gateway.store(@credit_card, {:description => "Active Merchant Unstore Customer"}) - assert response = @gateway.unstore(creation.params['id']) + customer_id = creation.params['id'] + card_id = creation.params['cards']['data'].first['id'] + + # Unstore the card + assert response = @gateway.unstore(customer_id, card_id) assert_success response - assert_equal true, response.params["deleted"] + assert_equal card_id, response.params['id'] + assert_equal true, response.params['deleted'] + + # Unstore the customer + assert response = @gateway.unstore(customer_id) + assert_success response + assert_equal customer_id, response.params['id'] + assert_equal true, response.params['deleted'] end def test_successful_recurring @@ -173,4 +179,11 @@ def test_refund_partial_application_fee assert refund = @gateway.refund(@amount - 20, response.authorization, { :refund_fee_amount => 10 }) assert_success refund end + + def test_creditcard_purchase_with_customer + assert response = @gateway.purchase(@amount, @credit_card, @options.merge(:customer => '1234')) + assert_success response + assert_equal "charge", response.params["object"] + assert response.params["paid"] + end end diff --git a/test/remote/gateways/remote_swipe_checkout_test.rb b/test/remote/gateways/remote_swipe_checkout_test.rb new file mode 100644 index 00000000000..6299a506fd2 --- /dev/null +++ b/test/remote/gateways/remote_swipe_checkout_test.rb @@ -0,0 +1,69 @@ +require 'test_helper' + +class RemoteSwipeCheckoutTest < Test::Unit::TestCase + def setup + @gateway = SwipeCheckoutGateway.new(fixtures(:swipe_checkout)) + + @amount = 100 + @accepted_card = credit_card('1234123412341234') + @declined_card = credit_card('1111111111111111') + @invalid_card = credit_card('1000000000000000') + @empty_card = credit_card('') + + @options = { + order_id: '1', + billing_address: address, + description: 'Store Purchase' + } + end + + def test_successful_purchase + assert response = @gateway.purchase(@amount, @accepted_card, @options) + assert_success response + assert_equal 'Transaction approved', response.message + end + + def test_region_switching + assert response = @gateway.purchase(@amount, @accepted_card, @options.merge(:region => 'CA')) + assert_success response + assert_equal 'Transaction approved', response.message + end + + def test_unsuccessful_purchase + assert response = @gateway.purchase(@amount, @declined_card, @options) + assert_failure response + assert_equal 'Transaction declined', response.message + end + + def test_invalid_login + gateway = SwipeCheckoutGateway.new( + login: 'invalid', + api_key: 'invalid', + region: 'NZ' + ) + assert response = gateway.purchase(@amount, @accepted_card, @options) + assert_failure response + assert_equal 'Access Denied', response.message + end + + def test_invalid_card + # Note: Swipe Checkout transaction API returns declined if the card number + # is invalid, and "invalid card data" if the card number is empty + assert response = @gateway.purchase(@amount, @invalid_card, @options) + assert_failure response + assert_equal 'Transaction declined', response.message + assert_equal 200, response.params['response_code'] + end + + def test_empty_card + assert response = @gateway.purchase(@amount, @empty_card, @options) + assert_failure response + assert_equal 'Invalid card data', response.message + assert_equal 303, response.params['response_code'] + end + + def test_no_options + assert response = @gateway.purchase(@amount, @accepted_card, {}) + assert_success response + end +end diff --git a/test/remote/gateways/remote_usa_epay_advanced_test.rb b/test/remote/gateways/remote_usa_epay_advanced_test.rb index 440c9a350c3..2488438a814 100644 --- a/test/remote/gateways/remote_usa_epay_advanced_test.rb +++ b/test/remote/gateways/remote_usa_epay_advanced_test.rb @@ -2,24 +2,15 @@ require 'logger' class RemoteUsaEpayAdvancedTest < Test::Unit::TestCase - def setup - # Optional Logger Setup - # UsaEpayAdvancedGateway.logger = Logger.new('/tmp/usa_epay.log') - # UsaEpayAdvancedGateway.logger.level = Logger::DEBUG - - # Optional Wiredump Setup - # UsaEpayAdvancedGateway.wiredump_device = File.open('/tmp/usa_epay_dump.log', 'a+') - # UsaEpayAdvancedGateway.wiredump_device.sync = true - @gateway = UsaEpayAdvancedGateway.new(fixtures(:usa_epay_advanced)) @amount = 2111 - + @credit_card = ActiveMerchant::Billing::CreditCard.new( :number => '4000100011112224', - :month => 12, - :year => 12, + :month => 9, + :year => 14, :brand => 'visa', :verification_value => '123', :first_name => "Fred", @@ -28,8 +19,8 @@ def setup @bad_credit_card = ActiveMerchant::Billing::CreditCard.new( :number => '4000300011112220', - :month => 12, - :year => 12, + :month => 9, + :year => 14, :brand => 'visa', :verification_value => '999', :first_name => "Fred", @@ -37,19 +28,19 @@ def setup ) @check = ActiveMerchant::Billing::Check.new( - :number => '123456789', + :account_number => '123456789', :routing_number => '120450780', :account_type => 'checking', :first_name => "Fred", :last_name => "Flintstone" ) - + cc_method = [ - {:name => "My CC", :sort => 5, :method => @credit_card}, + {:name => "My CC", :sort => 5, :method => @credit_card}, {:name => "Other CC", :sort => 12, :method => @credit_card} ] - @options = { + @options = { :client_ip => '127.0.0.1', :billing_address => address, } @@ -86,6 +77,12 @@ def setup :amount => 10000 } + @run_transaction_check_options = { + :payment_method => @check, + :command => 'check', + :amount => 10000 + } + @run_sale_options = { :payment_method => @credit_card, :amount => 5000 @@ -97,12 +94,12 @@ def setup } payment_methods = [ - { + { :name => "My Visa", # optional :sort => 2, # optional :method => @credit_card }, - { + { :name => "My Checking", :method => @check } @@ -110,7 +107,7 @@ def setup end # Standard Gateway ================================================== - + def test_purchase assert response = @gateway.purchase(@amount, @credit_card, @options) assert_equal 'A', response.params['run_sale_return']['result_code'] @@ -130,7 +127,7 @@ def test_capture def test_void assert purchase = @gateway.purchase(@amount, @credit_card, @options.dup) - + assert credit = @gateway.void(purchase.authorization, @options) assert_equal 'true', credit.params['void_transaction_return'] end @@ -161,7 +158,7 @@ def test_invalid_login assert_failure response assert_equal 'Invalid software ID', response.message end - + # Customer ========================================================== def test_add_customer @@ -172,7 +169,7 @@ def test_add_customer def test_update_customer response = @gateway.add_customer(@options.merge(@customer_options)) customer_number = response.params['add_customer_return'] - + @options.merge!(@update_customer_options.merge!(:customer_number => customer_number)) response = @gateway.update_customer(@options) assert response.params['update_customer_return'] @@ -192,7 +189,7 @@ def test_enable_disable_customer def test_add_customer_payment_method response = @gateway.add_customer(@options.merge(@customer_options)) customer_number = response.params['add_customer_return'] - + @options.merge!(:customer_number => customer_number).merge!(@add_payment_options) response = @gateway.add_customer_payment_method(@options) assert response.params['add_customer_payment_method_return'] @@ -201,7 +198,7 @@ def test_add_customer_payment_method def test_add_customer_payment_method_verify response = @gateway.add_customer(@options.merge(@customer_options)) customer_number = response.params['add_customer_return'] - + @add_payment_options[:payment_method][:method] = @bad_credit_card @options.merge!(:customer_number => customer_number, :verify => true).merge!(@add_payment_options) response = @gateway.add_customer_payment_method(@options) @@ -211,7 +208,7 @@ def test_add_customer_payment_method_verify def test_get_customer_payment_methods response = @gateway.add_customer(@options.merge(@customer_options)) customer_number = response.params['add_customer_return'] - + response = @gateway.get_customer_payment_methods(:customer_number => customer_number) assert response.params['get_customer_payment_methods_return']['item'] end @@ -230,13 +227,14 @@ def test_get_customer_payment_method def test_update_customer_payment_method response = @gateway.add_customer(@options.merge(@customer_options)) customer_number = response.params['add_customer_return'] - + @options.merge!(:customer_number => customer_number).merge!(@add_payment_options) response = @gateway.add_customer_payment_method(@options) payment_method_id = response.params['add_customer_payment_method_return'] - update_payment_options = @add_payment_options[:payment_method].merge(:method_id => payment_method_id, + update_payment_options = @add_payment_options[:payment_method].merge(:method_id => payment_method_id, :name => "Updated Card.") + response = @gateway.update_customer_payment_method(update_payment_options) assert response.params['update_customer_payment_method_return'] end @@ -244,7 +242,7 @@ def test_update_customer_payment_method def test_delete_customer_payment_method response = @gateway.add_customer(@options.merge(@customer_options)) customer_number = response.params['add_customer_return'] - + @options.merge!(:customer_number => customer_number).merge!(@add_payment_options) response = @gateway.add_customer_payment_method(@options) id = response.params['add_customer_payment_method_return'] @@ -256,7 +254,7 @@ def test_delete_customer_payment_method def test_delete_customer response = @gateway.add_customer(@options.merge(@customer_options)) customer_number = response.params['add_customer_return'] - + response = @gateway.delete_customer(:customer_number => customer_number) assert response.params['delete_customer_return'] end @@ -276,6 +274,14 @@ def test_run_transaction @options.merge!(@run_transaction_options) response = @gateway.run_transaction(@options) assert response.params['run_transaction_return'] + assert response.success? + end + + def test_run_transaction_check + @options.merge!(@run_transaction_check_options) + response = @gateway.run_transaction(@options) + assert response.params['run_transaction_return'] + assert response.success? end def test_run_sale @@ -345,7 +351,7 @@ def test_refund_transaction assert response.params['refund_transaction_return'] end - # TODO how to test override_transction + # TODO how to test override_transaction def test_override_transaction options = @options.merge(@run_check_sale_options) response = @gateway.run_check_sale(options) @@ -413,9 +419,12 @@ def test_get_transaction_custom response = @gateway.run_sale(@options.merge(@run_sale_options)) reference_number = response.params['run_sale_return']['ref_num'] - response = @gateway.get_transaction_custom(:reference_number => reference_number, + response = @gateway.get_transaction_custom(:reference_number => reference_number, :fields => ['Response.StatusCode', 'Response.Status']) assert response.params['get_transaction_custom_return'] + response = @gateway.get_transaction_custom(:reference_number => reference_number, + :fields => ['Response.StatusCode']) + assert response.params['get_transaction_custom_return'] end def test_get_check_trace diff --git a/test/remote/gateways/remote_webpay_test.rb b/test/remote/gateways/remote_webpay_test.rb index 3aa0edf27ea..27645565d6a 100644 --- a/test/remote/gateways/remote_webpay_test.rb +++ b/test/remote/gateways/remote_webpay_test.rb @@ -48,6 +48,24 @@ def test_unsuccessful_purchase assert_equal 'Your card number is incorrect', response.message end + def test_authorization_and_capture + assert authorization = @gateway.authorize(@amount, @credit_card, @options) + assert_success authorization + assert !authorization.params["captured"] + + assert capture = @gateway.capture(@amount, authorization.authorization) + assert_success capture + end + + def test_authorization_and_void + assert authorization = @gateway.authorize(@amount, @credit_card, @options) + assert_success authorization + assert !authorization.params["captured"] + + assert void = @gateway.void(authorization.authorization) + assert_success void + end + def test_successful_void assert response = @gateway.purchase(@amount, @credit_card, @options) assert_success response diff --git a/test/remote/gateways/remote_wirecard_test.rb b/test/remote/gateways/remote_wirecard_test.rb index 0499974350e..34f715f053f 100644 --- a/test/remote/gateways/remote_wirecard_test.rb +++ b/test/remote/gateways/remote_wirecard_test.rb @@ -43,7 +43,7 @@ def test_successful_authorize_and_capture amount = @amount assert auth = @gateway.authorize(amount, @credit_card, @options) assert_success auth - assert auth.message[/THIS IS A DEMO/] + assert_match /THIS IS A DEMO/, auth.message assert auth.authorization assert capture = @gateway.capture(amount, auth.authorization, @options) assert_success capture @@ -52,7 +52,7 @@ def test_successful_authorize_and_capture def test_successful_authorize_and_partial_capture assert auth = @gateway.authorize(@amount, @credit_card, @options) assert_success auth - assert auth.message[/THIS IS A DEMO/] + assert_match /THIS IS A DEMO/, auth.message assert auth.authorization #Capture some of the authorized amount @@ -60,11 +60,30 @@ def test_successful_authorize_and_partial_capture assert_success capture end + def test_successful_void + assert response = @gateway.authorize(@amount, @credit_card, @options) + assert_success response + assert response.authorization + + assert void = @gateway.void(response.authorization) + assert_success void + assert_match /THIS IS A DEMO/, void.message + end + + def test_successful_refund + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_success response + assert response.authorization + + assert refund = @gateway.refund(@amount - 20, response.authorization) + assert_success refund + assert_match /THIS IS A DEMO/, refund.message + end + def test_successful_purchase assert response = @gateway.purchase(@amount, @credit_card, @options) - # puts response.message assert_success response - assert response.message[/THIS IS A DEMO/] + assert_match /THIS IS A DEMO/, response.message end def test_successful_purchase_with_german_address_german_state_and_german_phone @@ -110,6 +129,18 @@ def test_unauthorized_capture assert_equal "Could not find referenced transaction for GuWID 1234567890123456789012.", response.message end + def test_failed_refund + assert refund = @gateway.refund(@amount - 20, 'C428094138244444404448') + assert_failure refund + assert_match /Could not find referenced transaction/, refund.message + end + + def test_failed_void + assert void = @gateway.void('C428094138244444404448') + assert_failure void + assert_match /Could not find referenced transaction/, void.message + end + def test_invalid_login gateway = WirecardGateway.new(:login => '', :password => '', :signature => '') assert response = gateway.purchase(@amount, @credit_card, @options) diff --git a/test/remote/integrations/remote_payu_in_integration_test.rb b/test/remote/integrations/remote_payu_in_integration_test.rb index be8aa9c8547..72680842425 100644 --- a/test/remote/integrations/remote_payu_in_integration_test.rb +++ b/test/remote/integrations/remote_payu_in_integration_test.rb @@ -4,7 +4,7 @@ class RemotePayuInIntegrationTest < Test::Unit::TestCase include ActiveMerchant::Billing::Integrations def setup - @payu_in = PayuIn::Notification.new(http_raw_data, :credential1 => 'C0Dr8m', :credential2 => '3sf0jURk') + @payu_in = PayuIn::Notification.new(http_raw_data, :credential1 => 'merchant_id', :credential2 => 'secret') end def test_raw @@ -21,6 +21,6 @@ def test_raw private def http_raw_data - "mihpayid=403993715508030204&mode=CC&status=success&unmappedstatus=captured&key=C0Dr8m&txnid=4ba4afe87f7e73468f2a&amount=10.00&discount=0.00&addedon=2013-05-10 18 32 30&productinfo=Product Info&firstname=Payu-Admin&lastname=&address1=&address2=&city=&state=&country=&zipcode=&email=test@example.com&phone=1234567890&udf1=&udf2=&udf3=&udf4=&udf5=&udf6=&udf7=&udf8=&udf9=&udf10=&hash=ef0c1b509a42b802a4938c25dc9bb9efe40b75a7dfb8bde1a6f126fa1f86cee264c5e5a17e87db85150d6d8912eafda838416e669712f1989dcb9cbdb8c24219&field1=313069903923&field2=999999&field3=59117331831301&field4=-1&field5=&field6=&field7=&field8=&PG_TYPE=HDFC&bank_ref_num=59117331831301&bankcode=CC&error=E000&cardnum=512345XXXXXX2346&cardhash=766f0227cc4b4c5f773a04cb31d8d1c5be071dd8d08fe365ecf5e2e5c947546d" + "mihpayid=403993715508030204&mode=CC&status=success&unmappedstatus=captured&key=merchant_id&txnid=4ba4afe87f7e73468f2a&amount=10.00&discount=0.00&addedon=2013-05-10 18 32 30&productinfo=Product Info&firstname=Payu-Admin&lastname=&address1=&address2=&city=&state=&country=&zipcode=&email=test@example.com&phone=1234567890&udf1=&udf2=&udf3=&udf4=&udf5=&udf6=&udf7=&udf8=&udf9=&udf10=&hash=ef0c1b509a42b802a4938c25dc9bb9efe40b75a7dfb8bde1a6f126fa1f86cee264c5e5a17e87db85150d6d8912eafda838416e669712f1989dcb9cbdb8c24219&field1=313069903923&field2=999999&field3=59117331831301&field4=-1&field5=&field6=&field7=&field8=&PG_TYPE=HDFC&bank_ref_num=59117331831301&bankcode=CC&error=E000&cardnum=512345XXXXXX2346&cardhash=766f0227cc4b4c5f773a04cb31d8d1c5be071dd8d08fe365ecf5e2e5c947546d" end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 6b37b224c42..7f00a54a75a 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -94,23 +94,23 @@ def assert_false(boolean, message = nil) # A handy little assertion to check for a successful response: # # # Instead of - # assert_success response + # assert response.success? # # # DRY that up with # assert_success response # # A message will automatically show the inspection of the response # object if things go afoul. - def assert_success(response) + def assert_success(response, message=nil) clean_backtrace do - assert response.success?, "Response failed: #{response.inspect}" + assert response.success?, build_message(nil, "#{message + "\n" if message}Response expected to succeed: ", response) end end # The negative of +assert_success+ - def assert_failure(response) + def assert_failure(response, message=nil) clean_backtrace do - assert_false response.success?, "Response expected to fail: #{response.inspect}" + assert !response.success?, build_message(nil, "#{message + "\n" if message}Response expected to fail: ", response) end end diff --git a/test/unit/base_test.rb b/test/unit/base_test.rb index 2ec4f4721ac..5679e4e9816 100644 --- a/test/unit/base_test.rb +++ b/test/unit/base_test.rb @@ -18,18 +18,32 @@ def test_should_return_a_new_gateway_specified_by_symbol_name assert_equal LinkpointGateway, Base.gateway(:linkpoint) end - def test_should_raise_when_invalid_gateway_is_passed - assert_raise NameError do - Base.gateway(:nil) + def test_should_raise_when_nil_gateway_is_passed + e = assert_raise ArgumentError do + Base.gateway(nil) end + assert_equal 'A gateway provider must be specified', e.message + end - assert_raise NameError do + def test_should_raise_when_empty_gateway_is_passed + e = assert_raise ArgumentError do Base.gateway('') end + assert_equal 'A gateway provider must be specified', e.message + end - assert_raise NameError do + def test_should_raise_when_invalid_gateway_symbol_is_passed + e = assert_raise ArgumentError do Base.gateway(:hotdog) end + assert_equal 'The specified gateway is not valid (hotdog)', e.message + end + + def test_should_raise_when_invalid_gateway_string_is_passed + e = assert_raise ArgumentError do + Base.gateway('hotdog') + end + assert_equal 'The specified gateway is not valid (hotdog)', e.message end def test_should_return_an_integration_by_name diff --git a/test/unit/gateways/app55_test.rb b/test/unit/gateways/app55_test.rb new file mode 100644 index 00000000000..8623a0f6d89 --- /dev/null +++ b/test/unit/gateways/app55_test.rb @@ -0,0 +1,51 @@ +require 'test_helper' + +class App55Test < Test::Unit::TestCase + def setup + @gateway = App55Gateway.new( + api_key: 'ABC', + api_secret: 'DEF' + ) + + @credit_card = credit_card + @amount = 100 + + @options = { + billing_address: address, + description: 'app55 active merchant unit test' + } + end + + def test_successful_purchase + @gateway.expects(:ssl_request).returns(successful_purchase_response) + + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_instance_of Response, response + assert_success response + + assert_equal '130703144451_78313', response.authorization + assert response.test? + end + + def test_unsuccessful_purchase + @gateway.expects(:ssl_request).returns(failed_purchase_response) + + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert response.test? + end + + private + + def successful_purchase_response + <<-RESPONSE + {"sig":"TxjO6RNAQYstte69KYQu8zmxF_8=","transaction":{"id":"130703144451_78313","description":"app55 active merchant unit test","currency":"GBP","code":"succeeded","amount":"1.00","auth_code":"06603"},"ts":"20130703134451"} + RESPONSE + end + + def failed_purchase_response + <<-RESPONSE + {"error":{"message":"Invalid card number supplied.","type":"validation-error","code":197123}} + RESPONSE + end +end diff --git a/test/unit/gateways/authorize_net_test.rb b/test/unit/gateways/authorize_net_test.rb index db70bb2abfe..61b462bb675 100644 --- a/test/unit/gateways/authorize_net_test.rb +++ b/test/unit/gateways/authorize_net_test.rb @@ -101,6 +101,14 @@ def test_failed_authorization assert_equal '508141794', response.authorization end + def test_failed_already_actioned_capture + @gateway.expects(:ssl_post).returns(already_actioned_capture_response) + + assert response = @gateway.capture(50, '123456789') + assert_instance_of Response, response + assert_failure response + end + def test_add_address_outsite_north_america result = {} @@ -150,6 +158,14 @@ def test_add_duplicate_window_with_duplicate_window assert_equal 0, result[:duplicate_window] end + def test_add_cardholder_authentication_value + result = {} + params = {:cardholder_authentication_value => 'E0Mvq8AAABEiMwARIjNEVWZ3iJk=', :authentication_indicator => '2'} + @gateway.send(:add_customer_data, result, params) + assert_equal 'E0Mvq8AAABEiMwARIjNEVWZ3iJk=', result[:cardholder_authentication_value] + assert_equal '2', result[:authentication_indicator] + end + def test_purchase_is_valid_csv params = { :amount => '1.01' } @@ -186,6 +202,13 @@ def test_authorization_code_included_in_params assert_equal('d1GENk', response.params['authorization_code'] ) end + def test_cardholder_authorization_code_included_in_params + @gateway.expects(:ssl_post).returns(successful_purchase_response) + + response = @gateway.capture(50, '123456789') + assert_equal('2', response.params['cardholder_authentication_code'] ) + end + def test_capture_passing_extra_info response = stub_comms do @gateway.capture(50, '123456789', :description => "Yo", :order_id => "Sweetness") @@ -416,6 +439,10 @@ def failed_authorization_response '$2$,$1$,$1$,$This transaction was declined.$,$advE7f$,$Y$,$508141794$,$5b3fe66005f3da0ebe51$,$$,$1.00$,$CC$,$auth_only$,$$,$Longbob$,$Longsen$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$2860A297E0FE804BCB9EF8738599645C$,$P$,$2$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$' end + def already_actioned_capture_response + '$1$,$2$,$311$,$This transaction has already been captured.$,$$,$P$,$0$,$$,$$,$1.00$,$CC$,$credit$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$39265D8BA0CDD4F045B5F4129B2AAA01$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$' + end + def fraud_review_response "$4$,$$,$253$,$Thank you! For security reasons your order is currently being reviewed.$,$$,$X$,$0$,$$,$$,$1.00$,$$,$auth_capture$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$207BCBBF78E85CF174C87AE286B472D2$,$M$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$,$$" end diff --git a/test/unit/gateways/balanced_test.rb b/test/unit/gateways/balanced_test.rb index 174d12998ff..3b4fa9e5226 100644 --- a/test/unit/gateways/balanced_test.rb +++ b/test/unit/gateways/balanced_test.rb @@ -283,11 +283,13 @@ def test_store ) card_uri = '/v1/marketplaces/TEST-MP73SaFdpQePv9dOaG5wXOGO/cards/CC6r6kLUcxW3MxG3AmZoiuTf' + account_uri = '/v1/marketplaces/TEST-MP73SaFdpQePv9dOaG5wXOGO/accounts/AC5quPICW5qEHXac1KnjKGYu' assert response = @gateway.store(@credit_card, { :email=>'john.buyer@example.org' }) - assert_instance_of String, response - assert_equal card_uri, response + assert_instance_of Response, response + assert_success response + assert_equal "#{card_uri};#{account_uri}", response.authorization end def test_ensure_does_not_respond_to_credit diff --git a/test/unit/gateways/bogus_test.rb b/test/unit/gateways/bogus_test.rb index 87b6148d724..2c2b7798a06 100644 --- a/test/unit/gateways/bogus_test.rb +++ b/test/unit/gateways/bogus_test.rb @@ -1,8 +1,10 @@ require 'test_helper' class BogusTest < Test::Unit::TestCase - SUCCESS_PLACEHOLDER = '4444333322221111' - FAILURE_PLACEHOLDER = '4444333311112222' + CC_SUCCESS_PLACEHOLDER = '4444333322221111' + CC_FAILURE_PLACEHOLDER = '4444333311112222' + CHECK_SUCCESS_PLACEHOLDER = '111111111111' + CHECK_FAILURE_PLACEHOLDER = '222222222222' def setup @gateway = BogusGateway.new( @@ -10,58 +12,62 @@ def setup :password => 'bogus' ) - @creditcard = credit_card(SUCCESS_PLACEHOLDER) + @creditcard = credit_card(CC_SUCCESS_PLACEHOLDER) @response = ActiveMerchant::Billing::Response.new(true, "Transaction successful", :transid => BogusGateway::AUTHORIZATION) end def test_authorize - assert @gateway.authorize(1000, credit_card(SUCCESS_PLACEHOLDER)).success? - assert !@gateway.authorize(1000, credit_card(FAILURE_PLACEHOLDER)).success? - assert_raises(ActiveMerchant::Billing::Error) do + assert @gateway.authorize(1000, credit_card(CC_SUCCESS_PLACEHOLDER)).success? + assert !@gateway.authorize(1000, credit_card(CC_FAILURE_PLACEHOLDER)).success? + e = assert_raises(ActiveMerchant::Billing::Error) do @gateway.authorize(1000, credit_card('123')) end + assert_equal("Bogus Gateway: Use CreditCard number ending in 1 for success, 2 for exception and anything else for error", e.message) end def test_purchase - assert @gateway.purchase(1000, credit_card(SUCCESS_PLACEHOLDER)).success? - assert !@gateway.purchase(1000, credit_card(FAILURE_PLACEHOLDER)).success? - assert_raises(ActiveMerchant::Billing::Error) do + assert @gateway.purchase(1000, credit_card(CC_SUCCESS_PLACEHOLDER)).success? + assert !@gateway.purchase(1000, credit_card(CC_FAILURE_PLACEHOLDER)).success? + e = assert_raises(ActiveMerchant::Billing::Error) do @gateway.purchase(1000, credit_card('123')) end + assert_equal("Bogus Gateway: Use CreditCard number ending in 1 for success, 2 for exception and anything else for error", e.message) end def test_recurring - assert @gateway.recurring(1000, credit_card(SUCCESS_PLACEHOLDER)).success? - assert !@gateway.recurring(1000, credit_card(FAILURE_PLACEHOLDER)).success? - assert_raises(ActiveMerchant::Billing::Error) do + assert @gateway.recurring(1000, credit_card(CC_SUCCESS_PLACEHOLDER)).success? + assert !@gateway.recurring(1000, credit_card(CC_FAILURE_PLACEHOLDER)).success? + e = assert_raises(ActiveMerchant::Billing::Error) do @gateway.recurring(1000, credit_card('123')) end + assert_equal("Bogus Gateway: Use CreditCard number ending in 1 for success, 2 for exception and anything else for error", e.message) end def test_capture assert @gateway.capture(1000, '1337').success? assert @gateway.capture(1000, @response.params["transid"]).success? - assert !@gateway.capture(1000, FAILURE_PLACEHOLDER).success? + assert !@gateway.capture(1000, CC_FAILURE_PLACEHOLDER).success? assert_raises(ActiveMerchant::Billing::Error) do - @gateway.capture(1000, SUCCESS_PLACEHOLDER) + @gateway.capture(1000, CC_SUCCESS_PLACEHOLDER) end end def test_credit - assert @gateway.credit(1000, credit_card(SUCCESS_PLACEHOLDER)).success? - assert !@gateway.credit(1000, credit_card(FAILURE_PLACEHOLDER)).success? - assert_raises(ActiveMerchant::Billing::Error) do + assert @gateway.credit(1000, credit_card(CC_SUCCESS_PLACEHOLDER)).success? + assert !@gateway.credit(1000, credit_card(CC_FAILURE_PLACEHOLDER)).success? + e = assert_raises(ActiveMerchant::Billing::Error) do @gateway.credit(1000, credit_card('123')) end + assert_equal("Bogus Gateway: Use CreditCard number ending in 1 for success, 2 for exception and anything else for error", e.message) end def test_refund assert @gateway.refund(1000, '1337').success? assert @gateway.refund(1000, @response.params["transid"]).success? - assert !@gateway.refund(1000, FAILURE_PLACEHOLDER).success? + assert !@gateway.refund(1000, CC_FAILURE_PLACEHOLDER).success? assert_raises(ActiveMerchant::Billing::Error) do - @gateway.refund(1000, SUCCESS_PLACEHOLDER) + @gateway.refund(1000, CC_SUCCESS_PLACEHOLDER) end end @@ -76,18 +82,23 @@ def test_credit_uses_refund def test_void assert @gateway.void('1337').success? assert @gateway.void(@response.params["transid"]).success? - assert !@gateway.void(FAILURE_PLACEHOLDER).success? + assert !@gateway.void(CC_FAILURE_PLACEHOLDER).success? assert_raises(ActiveMerchant::Billing::Error) do - @gateway.void(SUCCESS_PLACEHOLDER) + @gateway.void(CC_SUCCESS_PLACEHOLDER) end end def test_store - @gateway.store(@creditcard) + assert @gateway.store(credit_card(CC_SUCCESS_PLACEHOLDER)).success? + assert !@gateway.store(credit_card(CC_FAILURE_PLACEHOLDER)).success? + e = assert_raises(ActiveMerchant::Billing::Error) do + @gateway.store(credit_card('123')) + end + assert_equal("Bogus Gateway: Use CreditCard number ending in 1 for success, 2 for exception and anything else for error", e.message) end def test_unstore - @gateway.unstore(SUCCESS_PLACEHOLDER) + @gateway.unstore(CC_SUCCESS_PLACEHOLDER) end def test_store_then_purchase @@ -102,4 +113,58 @@ def test_supported_countries def test_supported_card_types assert_equal [:bogus], BogusGateway.supported_cardtypes end + + def test_authorize_with_check + assert @gateway.authorize(1000, check(:account_number => CHECK_SUCCESS_PLACEHOLDER, :number => nil)).success? + assert !@gateway.authorize(1000, check(:account_number => CHECK_FAILURE_PLACEHOLDER, :number => nil)).success? + e = assert_raises(ActiveMerchant::Billing::Error) do + @gateway.authorize(1000, check(:account_number => '123', :number => nil)) + end + assert_equal("Bogus Gateway: Use bank account number ending in 1 for success, 2 for exception and anything else for error", e.message) + end + + def test_purchase_with_check + # use account number if number isn't given + assert @gateway.purchase(1000, check(:account_number => CHECK_SUCCESS_PLACEHOLDER, :number => nil)).success? + assert !@gateway.purchase(1000, check(:account_number => CHECK_FAILURE_PLACEHOLDER, :number => nil)).success? + # give priority to number over account_number if given + assert !@gateway.purchase(1000, check(:account_number => CHECK_SUCCESS_PLACEHOLDER, :number => CHECK_FAILURE_PLACEHOLDER)).success? + assert @gateway.purchase(1000, check(:account_number => CHECK_FAILURE_PLACEHOLDER, :number => CHECK_SUCCESS_PLACEHOLDER)).success? + e = assert_raises(ActiveMerchant::Billing::Error) do + @gateway.purchase(1000, check(:account_number => '123', :number => nil)) + end + assert_equal("Bogus Gateway: Use bank account number ending in 1 for success, 2 for exception and anything else for error", e.message) + end + + def test_recurring_with_check + assert @gateway.recurring(1000, check(:account_number => CHECK_SUCCESS_PLACEHOLDER, :number => nil)).success? + assert !@gateway.recurring(1000, check(:account_number => CHECK_FAILURE_PLACEHOLDER, :number => nil)).success? + e = assert_raises(ActiveMerchant::Billing::Error) do + @gateway.recurring(1000, check(:account_number => '123', :number => nil)) + end + assert_equal("Bogus Gateway: Use bank account number ending in 1 for success, 2 for exception and anything else for error", e.message) + end + + def test_store_with_check + assert @gateway.store(check(:account_number => CHECK_SUCCESS_PLACEHOLDER, :number => nil)).success? + assert !@gateway.store(check(:account_number => CHECK_FAILURE_PLACEHOLDER, :number => nil)).success? + e = assert_raises(ActiveMerchant::Billing::Error) do + @gateway.store(check(:account_number => '123', :number => nil)) + end + assert_equal("Bogus Gateway: Use bank account number ending in 1 for success, 2 for exception and anything else for error", e.message) + end + + def test_credit_with_check + assert @gateway.credit(1000, check(:account_number => CHECK_SUCCESS_PLACEHOLDER, :number => nil)).success? + assert !@gateway.credit(1000, check(:account_number => CHECK_FAILURE_PLACEHOLDER, :number => nil)).success? + e = assert_raises(ActiveMerchant::Billing::Error) do + @gateway.credit(1000, check(:account_number => '123', :number => nil)) + end + assert_equal("Bogus Gateway: Use bank account number ending in 1 for success, 2 for exception and anything else for error", e.message) + end + + def test_store_then_purchase_with_check + reference = @gateway.store(check(:account_number => CHECK_SUCCESS_PLACEHOLDER, :number => nil)) + assert @gateway.purchase(1000, reference.authorization).success? + end end diff --git a/test/unit/gateways/braintree_blue_test.rb b/test/unit/gateways/braintree_blue_test.rb index ff3ae73d0fb..9dc6725d5e3 100644 --- a/test/unit/gateways/braintree_blue_test.rb +++ b/test/unit/gateways/braintree_blue_test.rb @@ -87,8 +87,8 @@ def test_merchant_account_id_absent_if_not_provided end def test_store_with_verify_card_true - customer = mock( - :credit_cards => [], + customer = stub( + :credit_cards => [stub_everything], :email => 'email', :first_name => 'John', :last_name => 'Smith' @@ -106,9 +106,45 @@ def test_store_with_verify_card_true assert_equal response.params["customer_vault_id"], response.authorization end + def test_passes_email + customer = stub( + :credit_cards => [stub_everything], + :email => "bob@example.com", + :first_name => 'John', + :last_name => 'Smith', + id: "123" + ) + result = Braintree::SuccessfulResult.new(:customer => customer) + Braintree::CustomerGateway.any_instance.expects(:create).with do |params| + assert_equal "bob@example.com", params[:email] + params + end.returns(result) + + response = @gateway.store(credit_card("41111111111111111111"), :email => "bob@example.com") + assert_success response + end + + def test_scrubs_invalid_email + customer = stub( + :credit_cards => [stub_everything], + :email => nil, + :first_name => 'John', + :last_name => 'Smith', + :id => "123" + ) + result = Braintree::SuccessfulResult.new(:customer => customer) + Braintree::CustomerGateway.any_instance.expects(:create).with do |params| + assert_equal nil, params[:email] + params + end.returns(result) + + response = @gateway.store(credit_card("41111111111111111111"), :email => "bogus") + assert_success response + end + def test_store_with_verify_card_false - customer = mock( - :credit_cards => [], + customer = stub( + :credit_cards => [stub_everything], :email => 'email', :first_name => 'John', :last_name => 'Smith' @@ -128,7 +164,7 @@ def test_store_with_verify_card_false def test_store_with_billing_address_options customer_attributes = { - :credit_cards => [], + :credit_cards => [stub_everything], :email => 'email', :first_name => 'John', :last_name => 'Smith' @@ -141,7 +177,7 @@ def test_store_with_billing_address_options :zip => "60622", :country_name => "US" } - customer = mock(customer_attributes) + customer = stub(customer_attributes) customer.stubs(:id).returns('123') result = Braintree::SuccessfulResult.new(:customer => customer) Braintree::CustomerGateway.any_instance.expects(:create).with do |params| @@ -155,6 +191,73 @@ def test_store_with_billing_address_options @gateway.store(credit_card("41111111111111111111"), :billing_address => billing_address) end + def test_store_with_credit_card_token + customer = stub( + :email => 'email', + :first_name => 'John', + :last_name => 'Smith' + ) + customer.stubs(:id).returns('123') + + braintree_credit_card = stub_everything(token: "cctoken") + customer.stubs(:credit_cards).returns([braintree_credit_card]) + + result = Braintree::SuccessfulResult.new(:customer => customer) + Braintree::CustomerGateway.any_instance.expects(:create).with do |params| + assert_equal "cctoken", params[:credit_card][:token] + params + end.returns(result) + + response = @gateway.store(credit_card("41111111111111111111"), :credit_card_token => "cctoken") + assert_success response + assert_equal "cctoken", response.params["braintree_customer"]["credit_cards"][0]["token"] + assert_equal "cctoken", response.params["credit_card_token"] + end + + def test_store_with_customer_id + customer = stub( + :email => 'email', + :first_name => 'John', + :last_name => 'Smith', + :credit_cards => [stub_everything] + ) + customer.stubs(:id).returns("customerid") + + result = Braintree::SuccessfulResult.new(:customer => customer) + Braintree::CustomerGateway.any_instance.expects(:find). + with("customerid"). + raises(Braintree::NotFoundError) + Braintree::CustomerGateway.any_instance.expects(:create).with do |params| + assert_equal "customerid", params[:id] + params + end.returns(result) + + response = @gateway.store(credit_card("41111111111111111111"), :customer => "customerid") + assert_success response + assert_equal "customerid", response.params["braintree_customer"]["id"] + end + + def test_store_with_existing_customer_id + credit_card = stub( + customer_id: "customerid", + token: "cctoken" + ) + + result = Braintree::SuccessfulResult.new(credit_card: credit_card) + Braintree::CustomerGateway.any_instance.expects(:find).with("customerid") + Braintree::CreditCardGateway.any_instance.expects(:create).with do |params| + assert_equal "customerid", params[:customer_id] + assert_equal "41111111111111111111", params[:number] + params + end.returns(result) + + response = @gateway.store(credit_card("41111111111111111111"), customer: "customerid") + assert_success response + assert_nil response.params["braintree_customer"] + assert_equal "customerid", response.params["customer_vault_id"] + assert_equal "cctoken", response.params["credit_card_token"] + end + def test_update_with_cvv stored_credit_card = mock(:token => "token", :default? => true) customer = mock(:credit_cards => [stored_credit_card], :id => '123') @@ -171,8 +274,8 @@ def test_update_with_cvv end def test_update_with_verify_card_true - stored_credit_card = mock(:token => "token", :default? => true) - customer = mock(:credit_cards => [stored_credit_card], :id => '123') + stored_credit_card = stub(:token => "token", :default? => true) + customer = stub(:credit_cards => [stored_credit_card], :id => '123') Braintree::CustomerGateway.any_instance.stubs(:find).with('vault_id').returns(customer) BraintreeBlueGateway.any_instance.stubs(:customer_hash) @@ -268,6 +371,18 @@ def test_address_country_handling @gateway.purchase(100, credit_card("41111111111111111111"), :billing_address => {:country_code_numeric => 840}) end + def test_address_zip_handling + Braintree::TransactionGateway.any_instance.expects(:sale).with do |params| + (params[:billing][:postal_code] == "12345") + end.returns(braintree_result) + @gateway.purchase(100, credit_card("41111111111111111111"), :billing_address => {:zip => "12345"}) + + Braintree::TransactionGateway.any_instance.expects(:sale).with do |params| + (params[:billing][:postal_code] == nil) + end.returns(braintree_result) + @gateway.purchase(100, credit_card("41111111111111111111"), :billing_address => {:zip => "1234567890"}) + end + def test_passes_recurring_flag @gateway = BraintreeBlueGateway.new( :merchant_id => 'test', @@ -305,7 +420,7 @@ def test_default_logger_sets_warn_level_without_overwriting_global assert Braintree::Configuration.logger.level != Logger::DEBUG Braintree::Configuration.logger.level = Logger::DEBUG - # Re-instatiate a gateway to show it doesn't touch the global + # Re-instantiate a gateway to show it doesn't touch the global gateway = BraintreeBlueGateway.new( :merchant_id => 'test', :public_key => 'test', diff --git a/test/unit/gateways/conekta_test.rb b/test/unit/gateways/conekta_test.rb new file mode 100644 index 00000000000..753a466edf0 --- /dev/null +++ b/test/unit/gateways/conekta_test.rb @@ -0,0 +1,280 @@ +require 'test_helper' + +class ConektaTest < Test::Unit::TestCase + def setup + @gateway = ConektaGateway.new(:key => "1tv5yJp3xnVZ7eK67m4h") + + @amount = 300 + + @credit_card = ActiveMerchant::Billing::CreditCard.new( + :number => "4111111111111111", + :verification_value => "183", + :month => "01", + :year => "2018", + :first_name => "Mario F.", + :last_name => "Moreno Reyes" + ) + + @declined_card = ActiveMerchant::Billing::CreditCard.new( + :number => "4000000000000002", + :verification_value => "183", + :month => "01", + :year => "2018", + :first_name => "Mario F.", + :last_name => "Moreno Reyes" + ) + + @options = { + :description => 'Blue clip', + :success_url => "https://www.example.com/success", + :failure_url => "https://www.example.com/failure", + :address1 => "Rio Missisipi #123", + :address2 => "Paris", + :city => "Guerrero", + :country => "Mexico", + :zip => "5555", + :name => "Mario Reyes", + :phone => "12345678", + :carrier => "Estafeta" + } + end + + def test_successful_tokenized_purchase + @gateway.expects(:ssl_request).returns(successful_purchase_response) + assert response = @gateway.purchase(@amount, 'tok_xxxxxxxxxxxxxxxx', @options) + assert_instance_of Response, response + assert_success response + assert_equal nil, response.message + assert response.test? + end + + def test_successful_purchase + @gateway.expects(:ssl_request).returns(successful_purchase_response) + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_instance_of Response, response + assert_success response + assert_equal nil, response.message + assert response.test? + end + + def test_unsuccessful_purchase + @gateway.expects(:ssl_request).returns(failed_purchase_response) + assert response = @gateway.purchase(@amount, @declined_card, @options) + assert_failure response + assert response.test? + end + + def test_unsuccessful_refund + @gateway.expects(:ssl_request).returns(failed_refund_response) + assert response = @gateway.refund("1", @amount, @options) + assert_failure response + assert response.test? + end + + def test_successful_authorize + @gateway.expects(:ssl_request).returns(successful_authorize_response) + assert response = @gateway.authorize(@amount, @credit_card, @options) + assert_success response + assert_instance_of Response, response + assert_equal nil, response.message + assert response.test? + end + + def test_unsuccessful_authorize + @gateway.expects(:ssl_request).returns(failed_authorize_response) + assert response = @gateway.authorize(@amount, @declined_card, @options) + assert_failure response + assert response.test? + end + + def test_unsuccessful_capture + @gateway.expects(:ssl_request).returns(failed_purchase_response) + assert response = @gateway.capture("1", @amount, @options) + assert_failure response + assert response.test? + end + + def test_invalid_login + gateway = ConektaGateway.new(:key => 'invalid_token') + gateway.expects(:ssl_request).returns(failed_login_response) + assert response = gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert response.test? + end + + private + + def successful_purchase_response + { + 'id' => '521b859fcfc26c0f180002d9', + 'livemode' => false, + 'created_at' => 1377535391, + 'status' => 'pre_authorized', + 'currency' => 'MXN', + 'description' => 'Blue clip', + 'reference_id' => nil, + 'failure_code' => nil, + 'failure_message' => nil, + 'object' => 'charge', + 'amount' => 300, + 'processed_at' => nil, + 'fee' => 348, + 'card' => { + 'name' => 'Mario Reyes', + 'exp_month' => '01', + 'exp_year' => '18', + 'street2' => 'Paris', + 'street3' => 'nil', + 'city' => 'Guerrero', + 'zip' => '5555', + 'country' => 'Mexico', + 'brand' => 'VISA', + 'last4' => '1111', + 'object' => 'card', + 'fraud_response' => '3d_secure_required', + 'redirect_form' => { + 'url' => 'https => //eps.banorte.com/secure3d/Solucion3DSecure.htm', + 'action' => 'POST', + 'attributes' => { + 'MerchantId' => '7376961', + 'MerchantName' => 'GRUPO CONEKTAME', + 'MerchantCity' => 'EstadodeMexico', + 'Cert3D' => '03', + 'ClientId' => '60518', + 'Name' => '7376962', + 'Password' => 'fgt563j', + 'TransType' => 'PreAuth', + 'Mode' => 'Y', + 'E1' => 'qKNKjndV9emCxuKE1G4z', + 'E2' => '521b859fcfc26c0f180002d9', + 'E3' => 'Y', + 'ResponsePath' => 'https => //eps.banorte.com/RespuestaCC.jsp', + 'CardType' => 'VISA', + 'Card' => '4111111111111111', + 'Cvv2Indicator' => '1', + 'Cvv2Val' => '183', + 'Expires' => '01/18', + 'Total' => '3.0', + 'ForwardPath' => 'http => //localhost => 3000/charges/banorte_3d_secure_response', + 'auth_token' => 'qKNKjndV9emCxuKE1G4z' + } + } + } + }.to_json + end + + def failed_purchase_response + { + 'message' => 'The card was declined', + 'type' => 'invalid_parameter_error', + 'param' => '' + }.to_json + end + + def failed_bank_purchase_response + { + 'message' => 'The minimum purchase is 15 MXN pesos for bank transfer payments', + 'type' => 'invalid_parameter_error', + 'param' => '' + }.to_json + end + + def failed_refund_response + { + 'object' => 'error', + 'type' => 200, + 'message' => 'The charge does not exist or it is not suitable for this operation' + }.to_json + end + + def failed_void_response + { + 'object' => 'error', + 'type' => 200, + 'message' => 'The charge does not exist or it is not suitable for this operation' + }.to_json + end + + def successful_authorize_response + { + 'id' => '521b859fcfc26c0f180002d9', + 'livemode' => false, + 'created_at' => 1377535391, + 'status' => 'pre_authorized', + 'currency' => 'MXN', + 'description' => 'Blue clip', + 'reference_id' => nil, + 'failure_code' => nil, + 'failure_message' => nil, + 'object' => 'charge', + 'amount' => 300, + 'processed_at' => nil, + 'fee' => 348, + 'card' => { + 'name' => 'Mario Reyes', + 'exp_month' => '01', + 'exp_year' => '18', + 'street2' => 'Paris', + 'street3' => 'nil', + 'city' => 'Guerrero', + 'zip' => '5555', + 'country' => 'Mexico', + 'brand' => 'VISA', + 'last4' => '1111', + 'object' => 'card', + 'fraud_response' => '3d_secure_required', + 'redirect_form' => { + 'url' => 'https => //eps.banorte.com/secure3d/Solucion3DSecure.htm', + 'action' => 'POST', + 'attributes' => { + 'MerchantId' => '7376961', + 'MerchantName' => 'GRUPO CONEKTAME', + 'MerchantCity' => 'EstadodeMexico', + 'Cert3D' => '03', + 'ClientId' => '60518', + 'Name' => '7376962', + 'Password' => 'fgt563j', + 'TransType' => 'PreAuth', + 'Mode' => 'Y', + 'E1' => 'qKNKjndV9emCxuKE1G4z', + 'E2' => '521b859fcfc26c0f180002d9', + 'E3' => 'Y', + 'ResponsePath' => 'https => //eps.banorte.com/RespuestaCC.jsp', + 'CardType' => 'VISA', + 'Card' => '4111111111111111', + 'Cvv2Indicator' => '1', + 'Cvv2Val' => '183', + 'Expires' => '01/18', + 'Total' => '3.0', + 'ForwardPath' => 'http => //localhost => 3000/charges/banorte_3d_secure_response', + 'auth_token' => 'qKNKjndV9emCxuKE1G4z' + } + } + } + }.to_json + end + + def failed_authorize_response + { + 'message' => 'The card was declined', + 'type' => 'invalid_parameter_error', + 'param' => '' + }.to_json + end + + def failed_capture_response + { + 'object' => 'error', + 'type' => 200, + 'message' => 'The charge does not exist or it is not suitable for this operation' + }.to_json + end + + def failed_login_response + { + 'object' => 'error', + 'type' => 'authentication_error', + 'message' => 'Unrecognized authentication token' + }.to_json + end +end diff --git a/test/unit/gateways/elavon_test.rb b/test/unit/gateways/elavon_test.rb index b4c0054ca23..3ae26fb5c3d 100644 --- a/test/unit/gateways/elavon_test.rb +++ b/test/unit/gateways/elavon_test.rb @@ -97,10 +97,6 @@ def test_invalid_login assert_failure response end - def test_supported_countries - assert_equal ['US', 'CA'], ElavonGateway.supported_countries - end - def test_supported_card_types assert_equal [:visa, :master, :american_express, :discover], ElavonGateway.supported_cardtypes end diff --git a/test/unit/gateways/eway_rapid_test.rb b/test/unit/gateways/eway_rapid_test.rb index ab8a7eb246a..78063eef570 100644 --- a/test/unit/gateways/eway_rapid_test.rb +++ b/test/unit/gateways/eway_rapid_test.rb @@ -13,48 +13,48 @@ def setup @amount = 100 end - def test_purchase_calls_sub_methods - request = sequence("request") - @gateway.expects(:setup_purchase).with(@amount, {:order_id => 1, :redirect_url => "http://example.com/"}).returns(Response.new(true, "Success", {"formactionurl" => "url"}, :authorization => "auth1")).in_sequence(request) - @gateway.expects(:run_purchase).with("auth1", @credit_card, "url").returns(Response.new(true, "Success", {}, :authorization => "auth2")).in_sequence(request) - @gateway.expects(:status).with("auth2").returns(Response.new(true, "Success", {})).in_sequence(request) + def test_successful_purchase + response = stub_comms do + @gateway.purchase(@amount, @credit_card) + end.respond_with(successful_purchase_response) - response = @gateway.purchase(@amount, @credit_card, :order_id => 1) assert_success response + assert_equal "Transaction Approved Successful", response.message + assert_equal 10440187, response.authorization + assert response.test? end - def test_successful_setup_purchase - response = stub_comms do - @gateway.setup_purchase(@amount, :redirect_url => "http://bogus") - end.respond_with(successful_setup_purchase_response) + def test_localized_currency + stub_comms do + @gateway.purchase(100, @credit_card, :currency => 'CAD') + end.check_request do |endpoint, data, headers| + assert_match '"TotalAmount":"100"', data + end.respond_with(successful_purchase_response) - assert_success response - assert_equal "Succeeded", response.message - assert_equal( - "60CF3xWrFUQeDCEsJcA8zNHaspAT3CKpe-0DiqWjTYA3RZw1xhw2LU-BFCNYbr7eJt8KFaxCxmzYh9WDAYX8yIuYexTq0tC8i2kOt0dm0EV-mjxYEQ2YeHP2dazkSc7j58OiT", - response.authorization - ) - assert_equal "https://secure-au.sandbox.ewaypayments.com/Process", response.form_url - assert response.test? + stub_comms do + @gateway.purchase(100, @credit_card, :currency => 'JPY') + end.check_request do |endpoint, data, headers| + assert_match '"TotalAmount":"1"', data + end.respond_with(successful_purchase_response) end - def test_failed_setup_purchase + def test_failed_purchase response = stub_comms do - @gateway.setup_purchase(@amount, :redirect_url => "http://bogus") - end.respond_with(failed_setup_purchase_response) + @gateway.purchase(-100, @credit_card) + end.respond_with(failed_purchase_response) assert_failure response - assert_equal "RedirectURL Required", response.message + assert_equal "Invalid Payment TotalAmount", response.message assert_nil response.authorization assert response.test? end - def test_setup_purchase_with_all_options + def test_purchase_with_all_options response = stub_comms do - @gateway.setup_purchase(200, + @gateway.purchase(200, @credit_card, + :transaction_type => 'CustomTransactionType', :redirect_url => "http://awesomesauce.com", :ip => "0.0.0.0", - :request_method => "CustomRequest", :application_id => "Woohoo", :description => "Description", :order_id => "orderid1", @@ -88,158 +88,161 @@ def test_setup_purchase_with_all_options } ) end.check_request do |endpoint, data, headers| - assert_no_match(%r{#{@credit_card.number}}, data) - - assert_match(%r{RedirectUrl>http://awesomesauce.com<}, data) - assert_match(%r{CustomerIP>0.0.0.0<}, data) - assert_match(%r{Method>CustomRequest<}, data) - assert_match(%r{DeviceID>Woohoo<}, data) - - assert_match(%r{TotalAmount>200<}, data) - assert_match(%r{InvoiceDescription>Description<}, data) - assert_match(%r{InvoiceReference>orderid1<}, data) - assert_match(%r{CurrencyCode>INR<}, data) - - assert_match(%r{Title>Mr.<}, data) - assert_match(%r{FirstName>Jim<}, data) - assert_match(%r{LastName>Awesome Smith<}, data) - assert_match(%r{CompanyName>Awesome Co<}, data) - assert_match(%r{Street1>1234 My Street<}, data) - assert_match(%r{Street2>Apt 1<}, data) - assert_match(%r{City>Ottawa<}, data) - assert_match(%r{State>ON<}, data) - assert_match(%r{PostalCode>K1C2N6<}, data) - assert_match(%r{Country>ca<}, data) - assert_match(%r{Phone>\(555\)555-5555<}, data) - assert_match(%r{Fax>\(555\)555-6666<}, data) - assert_match(%r{Email>jim@example\.com<}, data) - - assert_match(%r{Title>Ms.<}, data) - assert_match(%r{LastName>Baker<}, data) + # assert_no_match(%r{#{@credit_card.number}}, data) + + assert_match(%r{"TransactionType":"CustomTransactionType"}, data) + assert_match(%r{"RedirectUrl":"http://awesomesauce.com"}, data) + assert_match(%r{"CustomerIP":"0.0.0.0"}, data) + assert_match(%r{"DeviceID":"Woohoo"}, data) + + assert_match(%r{"TotalAmount":"200"}, data) + assert_match(%r{"InvoiceDescription":"Description"}, data) + assert_match(%r{"InvoiceReference":"orderid1"}, data) + assert_match(%r{"CurrencyCode":"INR"}, data) + + assert_match(%r{"Title":"Mr."}, data) + assert_match(%r{"FirstName":"Jim"}, data) + assert_match(%r{"LastName":"Awesome Smith"}, data) + assert_match(%r{"CompanyName":"Awesome Co"}, data) + assert_match(%r{"Street1":"1234 My Street"}, data) + assert_match(%r{"Street2":"Apt 1"}, data) + assert_match(%r{"City":"Ottawa"}, data) + assert_match(%r{"State":"ON"}, data) + assert_match(%r{"PostalCode":"K1C2N6"}, data) + assert_match(%r{"Country":"ca"}, data) + assert_match(%r{"Phone":"\(555\)555-5555"}, data) + assert_match(%r{"Fax":"\(555\)555-6666"}, data) + assert_match(%r{"Email":"jim@example\.com"}, data) + + assert_match(%r{"Title":"Ms."}, data) + assert_match(%r{"LastName":"Baker"}, data) assert_no_match(%r{Elsewhere Inc.}, data) - assert_match(%r{Street1>4321 Their St.<}, data) - assert_match(%r{Street2>Apt 2<}, data) - assert_match(%r{City>Chicago<}, data) - assert_match(%r{State>IL<}, data) - assert_match(%r{PostalCode>60625<}, data) - assert_match(%r{Country>us<}, data) - assert_match(%r{Phone>1115555555<}, data) - assert_match(%r{Fax>1115556666<}, data) - assert_match(%r{Email>(\s+)?<}, data) - end.respond_with(successful_setup_purchase_response) + assert_match(%r{"Street1":"4321 Their St."}, data) + assert_match(%r{"Street2":"Apt 2"}, data) + assert_match(%r{"City":"Chicago"}, data) + assert_match(%r{"State":"IL"}, data) + assert_match(%r{"PostalCode":"60625"}, data) + assert_match(%r{"Country":"us"}, data) + assert_match(%r{"Phone":"1115555555"}, data) + assert_match(%r{"Fax":"1115556666"}, data) + assert_match(%r{"Email":null}, data) + end.respond_with(successful_purchase_response) assert_success response - assert_equal( - "60CF3xWrFUQeDCEsJcA8zNHaspAT3CKpe-0DiqWjTYA3RZw1xhw2LU-BFCNYbr7eJt8KFaxCxmzYh9WDAYX8yIuYexTq0tC8i2kOt0dm0EV-mjxYEQ2YeHP2dazkSc7j58OiT", - response.authorization - ) + assert_equal 10440187, response.authorization assert response.test? end - def test_successful_run_purchase - request_sequence = sequence("request") - @gateway.expects(:ssl_request).returns(successful_setup_purchase_response).in_sequence(request_sequence) - @gateway.expects(:raw_ssl_request).with( - :post, - "https://secure-au.sandbox.ewaypayments.com/Process", - all_of( - regexp_matches(%r{EWAY_ACCESSCODE=60CF3xWrFUQeDCEsJcA8zNHaspAT3CKpe-0DiqWjTYA3RZw1xhw2LU-BFCNYbr7eJt8KFaxCxmzYh9WDAYX8yIuYexTq0tC8i2kOt0dm0EV-mjxYEQ2YeHP2dazkSc7j58OiT}), - regexp_matches(%r{EWAY_CARDNAME=Longbob\+Longsen}), - regexp_matches(%r{EWAY_CARDNUMBER=#{@credit_card.number}}), - regexp_matches(%r{EWAY_CARDEXPIRYMONTH=#{@credit_card.month}}), - regexp_matches(%r{EWAY_CARDEXPIRYYEAR=#{@credit_card.year}}), - regexp_matches(%r{EWAY_CARDCVN=#{@credit_card.verification_value}}) - ), - anything - ).returns(successful_run_purchase_response).in_sequence(request_sequence) - @gateway.expects(:ssl_request).returns(successful_status_response).in_sequence(request_sequence) - - response = @gateway.purchase(@amount, @credit_card, :order_id => 1) + def test_successful_store + response = stub_comms do + @gateway.store(@credit_card, :billing_address => { + :title => "Mr.", + :name => "Jim Awesome Smith", + :company => "Awesome Co", + :address1 => "1234 My Street", + :address2 => "Apt 1", + :city => "Ottawa", + :state => "ON", + :zip => "K1C2N6", + :country => "CA", + :phone => "(555)555-5555", + :fax => "(555)555-6666" + }) + end.check_request do |endpoint, data, headers| + assert_match '"Method":"CreateTokenCustomer"', data + end.respond_with(successful_store_response) + assert_success response - assert_equal "Transaction Approved", response.message - assert_equal( - "60CF3sfH7-yvAsUAHrdIiGppPrQW7v7DMAXxKkaKwyrIUoqvUvK44XbK9G9HNbngIz_iwQpfmPT_duMgh2G0pXCX8i4z1RAmMHpUQwa6VrghV3Bx9rh_tojjym7LC_fE-eR97", - response.authorization - ) + assert_equal "Transaction Approved Successful", response.message + assert_equal 917224224772, response.authorization assert response.test? end - def test_failed_run_purchase - request_sequence = sequence("request") - @gateway.expects(:ssl_request).returns(successful_setup_purchase_response).in_sequence(request_sequence) - @gateway.expects(:raw_ssl_request).returns(failed_run_purchase_response).in_sequence(request_sequence) + def test_failed_store + response = stub_comms do + @gateway.store(@credit_card, :billing_address => {}) + end.respond_with(failed_store_response) - response = @gateway.purchase(@amount, @credit_card, :order_id => 1) assert_failure response - assert_match %r{Not Found}, response.message + assert_equal "Customer CountryCode Required", response.message assert_nil response.authorization assert response.test? end - def test_successful_status + def test_successful_update response = stub_comms do - @gateway.status("thetransauth") + @gateway.update('faketoken', nil) end.check_request do |endpoint, data, headers| - assert_match(%r{thetransauth}, data) - end.respond_with(successful_status_response) + assert_match '"Method":"UpdateTokenCustomer"', data + end.respond_with(successful_update_response) assert_success response - assert_equal( - "60CF3sfH7-yvAsUAHrdIiGppPrQW7v7DMAXxKkaKwyrIUoqvUvK44XbK9G9HNbngIz_iwQpfmPT_duMgh2G0pXCX8i4z1RAmMHpUQwa6VrghV3Bx9rh_tojjym7LC_fE-eR97", - response.authorization - ) + assert_equal "Transaction Approved Successful", response.message + assert_equal 916161208398, response.authorization assert response.test? - assert_equal "Transaction Approved", response.message - assert_equal "orderid1", response.params["invoicereference"] end - def test_failed_status + def test_successful_refund response = stub_comms do - @gateway.status("thetransauth") - end.respond_with(failed_status_response) + @gateway.refund(@amount, '1234567') + end.check_request do |endpoint, data, headers| + assert_match /Transaction\/1234567\/Refund$/, endpoint + json = JSON.parse(data) + assert_equal '100', json['Refund']['TotalAmount'] + assert_equal '1234567', json['Refund']['TransactionID'] + end.respond_with(successful_refund_response) + + assert_success response + assert_equal "Transaction Approved Successful", response.message + assert_equal 10488258, response.authorization + assert response.test? + end + + def test_failed_refund + response = stub_comms do + @gateway.refund(@amount, '1234567') + end.respond_with(failed_refund_response) assert_failure response - assert_equal( - "A1001WfAHR_QP8daLnG6fQLcadzuCBJbpIp-zsUL6FkQgUyY2MXwVA0etYvflPe_rDBiuOMV-BfTSGDKt7uU3E2bLUhsD1rrXwGT9BTPcOOH_Vh9jHDSn2inqk8udwQIRcxuc", - response.authorization - ) + assert_equal "System Error", response.message + assert_nil response.authorization assert response.test? - assert_equal "Do Not Honour", response.message - assert_equal "1", response.params["invoicereference"] end - def test_store_calls_sub_methods - options = { - :order_id => 1, - :billing_address => { - :name => "Jim Awesome Smith", - } - } - @gateway.expects(:purchase).with(0, @credit_card, options.merge(:request_method => "CreateTokenCustomer")) + def test_successful_stored_card_purchase + response = stub_comms do + @gateway.purchase(100, 'the_customer_token', transaction_type: 'MOTO') + end.check_request do |endpoint, data, headers| + assert_match '"Method":"TokenPayment"', data + assert_match '"TransactionType":"MOTO"', data + end.respond_with(successful_store_purchase_response) - @gateway.store(@credit_card, options) + assert_success response + assert_equal "Transaction Approved Successful", response.message + assert_equal 10440234, response.authorization + assert response.test? end def test_verification_results response = stub_comms do - @gateway.status("thetransauth") - end.respond_with(successful_status_response(:verification_status => "Valid")) + @gateway.purchase(100, @credit_card) + end.respond_with(successful_purchase_response(:verification_status => "Valid")) assert_success response assert_equal "M", response.cvv_result["code"] assert_equal "M", response.avs_result["code"] response = stub_comms do - @gateway.status("thetransauth") - end.respond_with(successful_status_response(:verification_status => "Invalid")) + @gateway.purchase(100, @credit_card) + end.respond_with(successful_purchase_response(:verification_status => "Invalid")) assert_success response assert_equal "N", response.cvv_result["code"] assert_equal "N", response.avs_result["code"] response = stub_comms do - @gateway.status("thetransauth") - end.respond_with(successful_status_response(:verification_status => "Unchecked")) + @gateway.purchase(100, @credit_card) + end.respond_with(successful_purchase_response(:verification_status => "Unchecked")) assert_success response assert_equal "P", response.cvv_result["code"] @@ -248,148 +251,452 @@ def test_verification_results private - def successful_setup_purchase_response + def successful_purchase_response(options = {}) + verification_status = options[:verification_status] || 0 + verification_status = %Q{"#{verification_status}"} if verification_status.is_a? String %( - - 60CF3xWrFUQeDCEsJcA8zNHaspAT3CKpe-0DiqWjTYA3RZw1xhw2LU-BFCNYbr7eJt8KFaxCxmzYh9WDAYX8yIuYexTq0tC8i2kOt0dm0EV-mjxYEQ2YeHP2dazkSc7j58OiT - - - - - <FirstName /> - <LastName /> - <CompanyName /> - <JobDescription /> - <Street1 /> - <Street2 /> - <City /> - <State /> - <PostalCode /> - <Country /> - <Email /> - <Phone /> - <Mobile /> - <Comments /> - <Fax /> - <Url /> - <CardNumber /> - <CardStartMonth /> - <CardStartYear /> - <CardIssueNumber /> - <CardName /> - <CardExpiryMonth /> - <CardExpiryYear /> - <IsActive>false</IsActive> - </Customer> - <Payment> - <TotalAmount>100</TotalAmount> - <InvoiceDescription>Store Purchase</InvoiceDescription> - <InvoiceReference>1</InvoiceReference> - <CurrencyCode>AUD</CurrencyCode> - </Payment> - <FormActionURL>https://secure-au.sandbox.ewaypayments.com/Process</FormActionURL> - </CreateAccessCodeResponse> + { + "AuthorisationCode": "763051", + "ResponseCode": "00", + "ResponseMessage": "A2000", + "TransactionID": 10440187, + "TransactionStatus": true, + "TransactionType": "Purchase", + "BeagleScore": 0, + "Verification": { + "CVN": #{verification_status}, + "Address": #{verification_status}, + "Email": #{verification_status}, + "Mobile": #{verification_status}, + "Phone": #{verification_status} + }, + "Customer": { + "CardDetails": { + "Number": "444433XXXXXX1111", + "Name": "Longbob Longsen", + "ExpiryMonth": "09", + "ExpiryYear": "14", + "StartMonth": null, + "StartYear": null, + "IssueNumber": null + }, + "TokenCustomerID": null, + "Reference": "", + "Title": "Mr.", + "FirstName": "Jim", + "LastName": "Smith", + "CompanyName": "Widgets Inc", + "JobDescription": "", + "Street1": "1234 My Street", + "Street2": "Apt 1", + "City": "Ottawa", + "State": "ON", + "PostalCode": "K1C2N6", + "Country": "ca", + "Email": "", + "Phone": "(555)555-5555", + "Mobile": "", + "Comments": "", + "Fax": "(555)555-6666", + "Url": "" + }, + "Payment": { + "TotalAmount": 100, + "InvoiceNumber": "", + "InvoiceDescription": "Store Purchase", + "InvoiceReference": "1", + "CurrencyCode": "AUD" + }, + "Errors": null + } ) end - def failed_setup_purchase_response + def failed_purchase_response %( - <CreateAccessCodeResponse> - <Errors>V6047</Errors> - <Customer> - <TokenCustomerID p3:nil="true" xmlns:p3="http://www.w3.org/2001/XMLSchema-instance" /> - <IsActive>false</IsActive> - </Customer> - <Payment> - <TotalAmount>100</TotalAmount> - <CurrencyCode>AUD</CurrencyCode> - </Payment> - </CreateAccessCodeResponse> + { + "AuthorisationCode": null, + "ResponseCode": null, + "ResponseMessage": null, + "TransactionID": null, + "TransactionStatus": null, + "TransactionType": "Purchase", + "BeagleScore": null, + "Verification": null, + "Customer": { + "CardDetails": { + "Number": "444433XXXXXX1111", + "Name": "Longbob Longsen", + "ExpiryMonth": "09", + "ExpiryYear": "2014", + "StartMonth": null, + "StartYear": null, + "IssueNumber": null + }, + "TokenCustomerID": null, + "Reference": null, + "Title": "Mr.", + "FirstName": "Jim", + "LastName": "Smith", + "CompanyName": "Widgets Inc", + "JobDescription": null, + "Street1": "1234 My Street", + "Street2": "Apt 1", + "City": "Ottawa", + "State": "ON", + "PostalCode": "K1C2N6", + "Country": "ca", + "Email": null, + "Phone": "(555)555-5555", + "Mobile": null, + "Comments": null, + "Fax": "(555)555-6666", + "Url": null + }, + "Payment": { + "TotalAmount": -100, + "InvoiceNumber": null, + "InvoiceDescription": "Store Purchase", + "InvoiceReference": "1", + "CurrencyCode": "AUD" + }, + "Errors": "V6011" + } ) end - def successful_status_response(options={}) - verification_status = (options[:verification_status] || "Unchecked") + def successful_store_response %( - <GetAccessCodeResultResponse> - <AccessCode>60CF3sfH7-yvAsUAHrdIiGppPrQW7v7DMAXxKkaKwyrIUoqvUvK44XbK9G9HNbngIz_iwQpfmPT_duMgh2G0pXCX8i4z1RAmMHpUQwa6VrghV3Bx9rh_tojjym7LC_fE-eR97</AccessCode> - <AuthorisationCode>957199</AuthorisationCode> - <ResponseCode>00</ResponseCode> - <ResponseMessage>A2000</ResponseMessage> - <InvoiceNumber /> - <InvoiceReference>orderid1</InvoiceReference> - <TotalAmount>100</TotalAmount> - <TransactionID>9942726</TransactionID> - <TransactionStatus>true</TransactionStatus> - <TokenCustomerID p2:nil=\"true\" xmlns:p2=\"http://www.w3.org/2001/XMLSchema-instance\" /> - <BeagleScore>0</BeagleScore> - <Options /> - <Verification> - <CVN>#{verification_status}</CVN> - <Address>#{verification_status}</Address> - <Email>#{verification_status}</Email> - <Mobile>#{verification_status}</Mobile> - <Phone>#{verification_status}</Phone> - </Verification> - </GetAccessCodeResultResponse> + { + "AuthorisationCode": null, + "ResponseCode": "00", + "ResponseMessage": "A2000", + "TransactionID": null, + "TransactionStatus": false, + "TransactionType": "Purchase", + "BeagleScore": null, + "Verification": { + "CVN": 0, + "Address": 0, + "Email": 0, + "Mobile": 0, + "Phone": 0 + }, + "Customer": { + "CardDetails": { + "Number": "444433XXXXXX1111", + "Name": "Longbob Longsen", + "ExpiryMonth": "09", + "ExpiryYear": "14", + "StartMonth": null, + "StartYear": null, + "IssueNumber": null + }, + "TokenCustomerID": 917224224772, + "Reference": "", + "Title": "Dr.", + "FirstName": "Jim", + "LastName": "Smith", + "CompanyName": "Widgets Inc", + "JobDescription": "", + "Street1": "1234 My Street", + "Street2": "Apt 1", + "City": "Ottawa", + "State": "ON", + "PostalCode": "K1C2N6", + "Country": "ca", + "Email": "", + "Phone": "(555)555-5555", + "Mobile": "", + "Comments": "", + "Fax": "(555)555-6666", + "Url": "" + }, + "Payment": { + "TotalAmount": 0, + "InvoiceNumber": "", + "InvoiceDescription": "Store Purchase", + "InvoiceReference": "1", + "CurrencyCode": "AUD" + }, + "Errors": null + } ) end - def failed_status_response + def failed_store_response %( - <GetAccessCodeResultResponse> - <AccessCode>A1001WfAHR_QP8daLnG6fQLcadzuCBJbpIp-zsUL6FkQgUyY2MXwVA0etYvflPe_rDBiuOMV-BfTSGDKt7uU3E2bLUhsD1rrXwGT9BTPcOOH_Vh9jHDSn2inqk8udwQIRcxuc</AccessCode> - <AuthorisationCode /> - <ResponseCode>05</ResponseCode> - <ResponseMessage>D4405</ResponseMessage> - <InvoiceNumber /> - <InvoiceReference>1</InvoiceReference> - <TotalAmount>105</TotalAmount> - <TransactionID>9942743</TransactionID> - <TransactionStatus>false</TransactionStatus> - <TokenCustomerID p2:nil=\"true\" xmlns:p2=\"http://www.w3.org/2001/XMLSchema-instance\" /> - <BeagleScore>0</BeagleScore> - <Options /> - <Verification> - <CVN>Unchecked</CVN> - <Address>Unchecked</Address> - <Email>Unchecked</Email> - <Mobile>Unchecked</Mobile> - <Phone>Unchecked</Phone> - </Verification> - </GetAccessCodeResultResponse> + { + "AuthorisationCode": null, + "ResponseCode": null, + "ResponseMessage": null, + "TransactionID": null, + "TransactionStatus": null, + "TransactionType": "Purchase", + "BeagleScore": null, + "Verification": null, + "Customer": { + "CardDetails": { + "Number": "444433XXXXXX1111", + "Name": "Longbob Longsen", + "ExpiryMonth": "09", + "ExpiryYear": "2014", + "StartMonth": null, + "StartYear": null, + "IssueNumber": null + }, + "TokenCustomerID": null, + "Reference": null, + "Title": "Mr.", + "FirstName": "Jim", + "LastName": "Smith", + "CompanyName": "Widgets Inc", + "JobDescription": null, + "Street1": "1234 My Street", + "Street2": "Apt 1", + "City": "Ottawa", + "State": "ON", + "PostalCode": "K1C2N6", + "Country": null, + "Email": null, + "Phone": "(555)555-5555", + "Mobile": null, + "Comments": null, + "Fax": "(555)555-6666", + "Url": null + }, + "Payment": { + "TotalAmount": 0, + "InvoiceNumber": null, + "InvoiceDescription": "Store Purchase", + "InvoiceReference": "1", + "CurrencyCode": "AUD" + }, + "Errors": "V6044" + } ) end - class MockResponse - attr_reader :code, :body - def initialize(code, body, headers={}) - @code, @body, @headers = code, body, headers - end + def successful_update_response + %( + { + "AuthorisationCode": null, + "ResponseCode": "00", + "ResponseMessage": "A2000", + "TransactionID": null, + "TransactionStatus": false, + "TransactionType": "Purchase", + "BeagleScore": null, + "Verification": { + "CVN": 0, + "Address": 0, + "Email": 0, + "Mobile": 0, + "Phone": 0 + }, + "Customer": { + "CardDetails": { + "Number": "444433XXXXXX1111", + "Name": "Longbob Longsen", + "ExpiryMonth": "09", + "ExpiryYear": "14", + "StartMonth": null, + "StartYear": null, + "IssueNumber": null + }, + "TokenCustomerID": 916161208398, + "Reference": "", + "Title": "Dr.", + "FirstName": "Jim", + "LastName": "Smith", + "CompanyName": "Widgets Inc", + "JobDescription": "", + "Street1": "1234 My Street", + "Street2": "Apt 1", + "City": "Ottawa", + "State": "ON", + "PostalCode": "K1C2N6", + "Country": "ca", + "Email": "", + "Phone": "(555)555-5555", + "Mobile": "", + "Comments": "", + "Fax": "(555)555-6666", + "Url": "" + }, + "Payment": { + "TotalAmount": 0, + "InvoiceNumber": "", + "InvoiceDescription": "Store Purchase", + "InvoiceReference": "1", + "CurrencyCode": "AUD" + }, + "Errors": null + } + ) + end - def [](header) - @headers[header] - end + def successful_refund_response + %( + { + "AuthorisationCode": "457313", + "ResponseCode": null, + "ResponseMessage": "A2000", + "TransactionID": 10488258, + "TransactionStatus": true, + "Verification": null, + "Customer": { + "CardDetails": { + "Number": null, + "Name": null, + "ExpiryMonth": null, + "ExpiryYear": null, + "StartMonth": null, + "StartYear": null, + "IssueNumber": null + }, + "TokenCustomerID": null, + "Reference": null, + "Title": null, + "FirstName": "Jim", + "LastName": "Smith", + "CompanyName": "Widgets Inc", + "JobDescription": null, + "Street1": "1234 My Street", + "Street2": "Apt 1", + "City": "Ottawa", + "State": "ON", + "PostalCode": "K1C2N6", + "Country": "ca", + "Email": null, + "Phone": "(555)555-5555", + "Mobile": null, + "Comments": null, + "Fax": "(555)555-6666", + "Url": null + }, + "Refund": { + "TransactionID": null, + "TotalAmount": 0, + "InvoiceNumber": null, + "InvoiceDescription": null, + "InvoiceReference": null, + "CurrencyCode": null + }, + "Errors": null + } + ) end - def successful_run_purchase_response - MockResponse.new( - 302, - %( - <html><head><title>Object moved -

Object moved to here.

- - ), - "Location" => "http://example.com/?AccessCode=60CF3xWrFUQeDCEsJcA8zNHaspAT3CKpe-0DiqWjTYA3RZw1xhw2LU-BFCNYbr7eJt8KFaxCxmzYh9WDAYX8yIuYexTq0tC8i2kOt0dm0EV-mjxYEQ2YeHP2dazkSc7j58OiT" + def failed_refund_response + %( + { + "AuthorisationCode": null, + "ResponseCode": null, + "ResponseMessage": null, + "TransactionID": null, + "TransactionStatus": false, + "Verification": null, + "Customer": { + "CardDetails": { + "Number": null, + "Name": null, + "ExpiryMonth": null, + "ExpiryYear": null, + "StartMonth": null, + "StartYear": null, + "IssueNumber": null + }, + "TokenCustomerID": null, + "Reference": null, + "Title": null, + "FirstName": "Jim", + "LastName": "Smith", + "CompanyName": "Widgets Inc", + "JobDescription": null, + "Street1": "1234 My Street", + "Street2": "Apt 1", + "City": "Ottawa", + "State": "ON", + "PostalCode": "K1C2N6", + "Country": "ca", + "Email": null, + "Phone": "(555)555-5555", + "Mobile": null, + "Comments": null, + "Fax": "(555)555-6666", + "Url": null + }, + "Refund": { + "TransactionID": null, + "TotalAmount": 0, + "InvoiceNumber": null, + "InvoiceDescription": null, + "InvoiceReference": null, + "CurrencyCode": null + }, + "Errors": "S5000" + } ) end - def failed_run_purchase_response - MockResponse.new( - 200, - %( - {"Message":"Not Found","Errors":null} - ) + def successful_store_purchase_response + %( + { + "AuthorisationCode": "232671", + "ResponseCode": "00", + "ResponseMessage": "A2000", + "TransactionID": 10440234, + "TransactionStatus": true, + "TransactionType": "MOTO", + "BeagleScore": 0, + "Verification": { + "CVN": 0, + "Address": 0, + "Email": 0, + "Mobile": 0, + "Phone": 0 + }, + "Customer": { + "CardDetails": { + "Number": "444433XXXXXX1111", + "Name": "Longbob Longsen", + "ExpiryMonth": "09", + "ExpiryYear": "14", + "StartMonth": "", + "StartYear": "", + "IssueNumber": "" + }, + "TokenCustomerID": 912056757740, + "Reference": "", + "Title": "Mr.", + "FirstName": "Jim", + "LastName": "Smith", + "CompanyName": "Widgets Inc", + "JobDescription": "", + "Street1": "1234 My Street, Apt 1", + "Street2": "", + "City": "Ottawa", + "State": "ON", + "PostalCode": "K1C2N6", + "Country": "ca", + "Email": "", + "Phone": "(555)555-5555", + "Mobile": "", + "Comments": "", + "Fax": "(555)555-6666", + "Url": "" + }, + "Payment": { + "TotalAmount": 100, + "InvoiceNumber": "", + "InvoiceDescription": "", + "InvoiceReference": "", + "CurrencyCode": "AUD" + }, + "Errors": null + } ) end + end diff --git a/test/unit/gateways/gateway_test.rb b/test/unit/gateways/gateway_test.rb index 2bef0185402..ef1557b0cf2 100644 --- a/test/unit/gateways/gateway_test.rb +++ b/test/unit/gateways/gateway_test.rb @@ -1,6 +1,14 @@ require 'test_helper' class GatewayTest < Test::Unit::TestCase + def setup + @gateway = Gateway.new + end + + def teardown + Gateway.money_format = :dollars + end + def test_should_detect_if_a_card_is_supported Gateway.supported_cardtypes = [:visa, :bogus] assert [:visa, :bogus].all? { |supported_cardtype| Gateway.supports?(supported_cardtype) } @@ -15,17 +23,17 @@ def test_should_gateway_uses_ssl_strict_checking_by_default def test_should_be_able_to_look_for_test_mode Base.gateway_mode = :test - assert Gateway.new.test? + assert @gateway.test? Base.gateway_mode = :production - assert_false Gateway.new.test? + assert_false @gateway.test? end def test_amount_style - assert_equal '10.34', Gateway.new.send(:amount, 1034) + assert_equal '10.34', @gateway.send(:amount, 1034) assert_raise(ArgumentError) do - Gateway.new.send(:amount, '10.34') + @gateway.send(:amount, '10.34') end end @@ -45,4 +53,24 @@ def test_setting_application_id_outside_the_class_definition assert_equal SimpleTestGateway.application_id, SubclassGateway.application_id end + + def test_localized_amount_should_not_modify_for_fractional_currencies + Gateway.money_format = :dollars + assert_equal '1.00', @gateway.send(:localized_amount, 100, 'CAD') + assert_equal '12.34', @gateway.send(:localized_amount, 1234, 'USD') + + Gateway.money_format = :cents + assert_equal '100', @gateway.send(:localized_amount, 100, 'CAD') + assert_equal '1234', @gateway.send(:localized_amount, 1234, 'USD') + end + + def test_localized_amount_should_ignore_money_format_for_non_fractional_currencies + Gateway.money_format = :dollars + assert_equal '1', @gateway.send(:localized_amount, 100, 'JPY') + assert_equal '12', @gateway.send(:localized_amount, 1234, 'HUF') + + Gateway.money_format = :cents + assert_equal '1', @gateway.send(:localized_amount, 100, 'JPY') + assert_equal '12', @gateway.send(:localized_amount, 1234, 'HUF') + end end diff --git a/test/unit/gateways/litle_test.rb b/test/unit/gateways/litle_test.rb index e3166870e8f..8ca342b3e0c 100755 --- a/test/unit/gateways/litle_test.rb +++ b/test/unit/gateways/litle_test.rb @@ -377,6 +377,7 @@ def test_auth_pass assert_equal 'successful', responseFrom.message assert_equal '1234;authorization', responseFrom.authorization assert_equal '1111222233334444', responseFrom.params['litleOnlineResponse']['authorizationResponse']['litleToken'] + assert_equal '000', responseFrom.params['response_code'] end def test_avs diff --git a/test/unit/gateways/nab_transact_test.rb b/test/unit/gateways/nab_transact_test.rb index 126d9cde318..09c4b5c7105 100644 --- a/test/unit/gateways/nab_transact_test.rb +++ b/test/unit/gateways/nab_transact_test.rb @@ -1,6 +1,8 @@ require 'test_helper' class NabTransactTest < Test::Unit::TestCase + include CommStub + def setup @gateway = NabTransactGateway.new( :login => 'login', @@ -16,7 +18,6 @@ def setup } end - def test_successful_purchase @gateway.expects(:ssl_post).with(&check_transaction_type(:purchase)).returns(successful_purchase_response) @@ -28,6 +29,18 @@ def test_successful_purchase assert response.test? end + def test_successful_purchase_with_merchant_descriptor + name, location = 'Active Merchant', 'USA' + + response = assert_metadata(name, location) do + response = @gateway.purchase(@amount, @credit_card, @options.merge(:merchant_name => name, :merchant_location => location)) + end + + assert response + assert_instance_of Response, response + assert_success response + end + def test_successful_authorize @gateway.expects(:ssl_post).with(&check_transaction_type(:authorization)).returns(successful_authorize_response) assert response = @gateway.authorize(@amount, @credit_card, @options) @@ -35,6 +48,18 @@ def test_successful_authorize assert response.test? end + def test_successful_authorize_with_merchant_descriptor + name, location = 'Active Merchant', 'USA' + + response = assert_metadata(name, location) do + response = @gateway.authorize(@amount, @credit_card, @options.merge(:merchant_name => name, :merchant_location => location)) + end + + assert response + assert_instance_of Response, response + assert_success response + end + def test_successful_capture @gateway.expects(:ssl_post).with(&check_transaction_type(:capture)).returns(successful_purchase_response) assert response = @gateway.capture(@amount, '009887*test*009887*200') @@ -42,6 +67,18 @@ def test_successful_capture assert response.test? end + def test_successful_capture_with_merchant_descriptor + name, location = 'Active Merchant', 'USA' + + response = assert_metadata(name, location) do + response = @gateway.capture(@amount, '009887*test*009887*200', @options.merge(:merchant_name => name, :merchant_location => location)) + end + + assert response + assert_instance_of Response, response + assert_success response + end + def test_unsuccessful_purchase @gateway.expects(:ssl_post).with(&check_transaction_type(:purchase)).returns(failed_purchase_response) @@ -74,6 +111,18 @@ def test_successful_refund assert_success @gateway.refund(@amount, "009887", {:order_id => '1'}) end + def test_successful_refund_with_merchant_descriptor + name, location = 'Active Merchant', 'USA' + + response = assert_metadata(name, location) do + response = @gateway.refund(@amount, '009887', {:order_id => '1', :merchant_name => name, :merchant_location => location}) + end + + assert response + assert_instance_of Response, response + assert_success response + end + def test_failed_refund @gateway.expects(:ssl_post).with(&check_transaction_type(:refund)).returns(failed_refund_response) @@ -91,6 +140,20 @@ def check_transaction_type(type) end end + def valid_metadata(name, location) + valid_metadata = <<-XML.gsub(/^\s{4}/,'').gsub(/\n/, '') + + XML + end + + def assert_metadata(name, location, &block) + stub_comms(@gateway, :ssl_request) do + block.call + end.check_request do |method, endpoint, data, headers| + metadata_matcher = Regexp.escape(valid_metadata(name, location)) + assert_match /#{metadata_matcher}/, data + end.respond_with(successful_purchase_response) + end def failed_login_response '504Invalid merchant ID' diff --git a/test/unit/gateways/orbital_test.rb b/test/unit/gateways/orbital_test.rb index 667b054ab22..99db6eceb75 100644 --- a/test/unit/gateways/orbital_test.rb +++ b/test/unit/gateways/orbital_test.rb @@ -22,6 +22,26 @@ def test_successful_purchase assert_equal '4A5398CF9B87744GG84A1D30F2F2321C66249416;1', response.authorization end + def test_currency_exponents + stub_comms do + @gateway.purchase(50, credit_card, :order_id => '1') + end.check_request do |endpoint, data, headers| + assert_match /2<\/CurrencyExponent>/, data + end.respond_with(successful_purchase_response) + + stub_comms do + @gateway.purchase(50, credit_card, :order_id => '1', :currency => 'CAD') + end.check_request do |endpoint, data, headers| + assert_match /2<\/CurrencyExponent>/, data + end.respond_with(successful_purchase_response) + + stub_comms do + @gateway.purchase(50, credit_card, :order_id => '1', :currency => 'JPY') + end.check_request do |endpoint, data, headers| + assert_match /0<\/CurrencyExponent>/, data + end.respond_with(successful_purchase_response) + end + def test_unauthenticated_response @gateway.expects(:ssl_post).returns(failed_purchase_response) @@ -259,6 +279,30 @@ def test_send_customer_data_when_customer_ref_is_provided assert_success response end + def test_currency_code_and_exponent_are_set_for_profile_purchase + @gateway.options[:customer_profiles] = true + response = stub_comms do + @gateway.purchase(50, nil, :order_id => 1, :customer_ref_num => @customer_ref_num) + end.check_request do |endpoint, data, headers| + assert_match(/ABC/, data) + assert_match(/124/, data) + assert_match(/2/, data) + end.respond_with(successful_purchase_response) + assert_success response + end + + def test_currency_code_and_exponent_are_set_for_profile_authorizations + @gateway.options[:customer_profiles] = true + response = stub_comms do + @gateway.authorize(50, nil, :order_id => 1, :customer_ref_num => @customer_ref_num) + end.check_request do |endpoint, data, headers| + assert_match(/ABC/, data) + assert_match(/124/, data) + assert_match(/2/, data) + end.respond_with(successful_purchase_response) + assert_success response + end + # K1C2N6 # 1234 My Street # Apt 1 diff --git a/test/unit/gateways/pac_net_raven_test.rb b/test/unit/gateways/pac_net_raven_test.rb new file mode 100644 index 00000000000..1727b63fb32 --- /dev/null +++ b/test/unit/gateways/pac_net_raven_test.rb @@ -0,0 +1,541 @@ +require 'test_helper' + +class PacNetRavenGatewayTest < Test::Unit::TestCase + def setup + @gateway = PacNetRavenGateway.new( + user: 'user', + secret: 'secret', + prn: 123456 + ) + + @credit_card = credit_card + @amount = 100 + + @options = { + billing_address: address + } + end + + def test_successful_authorization + @gateway.expects(:ssl_post).returns(successful_authorization_response) + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_instance_of Response, response + assert_success response + assert_equal 'This transaction has been approved', response.message + assert_equal '123456789', response.authorization + assert response.test? + end + + def test_invalid_credit_card_authorization + @gateway.expects(:ssl_post).returns(invalid_credit_card_response) + assert response = @gateway.authorize(@amount, @credit_card, @options) + assert_failure response + assert_equal "Error processing transaction because CardNumber is not between 12 and 19 in length", response.message + end + + def test_expired_credit_card_authorization + @gateway.expects(:ssl_post).returns(expired_credit_card_response) + assert response = @gateway.authorize(@amount, @credit_card, @options) + assert_failure response + assert_equal "Invalid because the card expiry date is not a date in the future", response.message + end + + def test_declined_authorization + @gateway.expects(:ssl_post).returns(declined_purchese_response) + assert response = @gateway.authorize(@amount, @credit_card, @options) + assert_failure response + assert_equal 'This transaction has been declined', response.message + end + + def test_successful_purchase + @gateway.expects(:ssl_post).returns(successful_purchase_response) + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_instance_of Response, response + assert_success response + assert_equal 'This transaction has been approved', response.message + assert_equal '123456789', response.authorization + assert response.test? + end + + def test_invalid_credit_card_number_purchese + @gateway.expects(:ssl_post).returns(invalid_credit_card_response) + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert_equal "Error processing transaction because CardNumber is not between 12 and 19 in length", response.message + end + + def test_expired_credit_card_purchese + @gateway.expects(:ssl_post).returns(expired_credit_card_response) + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert_equal "Invalid because the card expiry date is not a date in the future", response.message + end + + def test_declined_purchese + @gateway.expects(:ssl_post).returns(declined_purchese_response) + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert_equal 'This transaction has been declined', response.message + end + + def test_successful_capture + @gateway.expects(:ssl_post).returns(successful_capture_response) + assert response = @gateway.capture(@amount, '123456789') + assert_instance_of Response, response + assert_success response + assert_equal 'This transaction has been approved', response.message + assert_equal '123456789', response.authorization + assert response.test? + end + + def test_invalid_preauth_number_capture + @gateway.expects(:ssl_post).returns(invalid_preauth_number_response) + assert response = @gateway.capture(@amount, '') + assert_failure response + assert_equal 'Error processing transaction because the pre-auth number', response.message + end + + def test_insufficient_preauth_amount_capture + @gateway.expects(:ssl_post).returns(insufficient_preauth_amount_response) + assert response = @gateway.capture(200, '123456789') + assert_failure response + assert_equal 'Invalid because the preauthorization amount 100 is insufficient', response.message + end + + def test_successful_refund + @gateway.expects(:ssl_post).returns(successful_refund_response) + assert response = @gateway.refund(@amount, '123456789') + assert_success response + assert_equal 'This transaction has been approved', response.message + end + + def test_amount_greater_than_original_amount_refund + @gateway.expects(:ssl_post).returns(amount_greater_than_original_amount_refund_response) + assert response = @gateway.refund(200, '123456789') + assert_failure response + assert_equal 'Invalid because the payment amount cannot be greater than the original charge', response.message + end + + def test_successful_void + @gateway.expects(:ssl_post).returns(successful_void_response) + assert response = @gateway.void('123456789') + assert_success response + assert_equal "This transaction has been voided", response.message + end + + def test_failed_void + @gateway.expects(:ssl_post).returns(failed_void_response) + assert response = @gateway.void('123456789') + assert_failure response + assert_equal "Error processing transaction because the payment may not be voided", response.message + end + + def test_argument_error_prn + exception = assert_raises(ArgumentError){ + PacNetRavenGateway.new(:user => 'user', :secret => 'secret') + } + assert_equal 'Missing required parameter: prn', exception.message + end + + def test_argument_error_user + exception = assert_raises(ArgumentError){ + PacNetRavenGateway.new(:secret => 'secret', :prn => 123456) + } + assert_equal 'Missing required parameter: user', exception.message + end + + def test_argument_error_secret + exception = assert_raises(ArgumentError){ + PacNetRavenGateway.new(:user => 'user', :prn => 123456) + } + assert_equal 'Missing required parameter: secret', exception.message + end + + def test_add_address + result = {} + @gateway.send(:add_address, result, :billing_address => {:address1 => 'Address 1', :address2 => 'Address 2', :zip => 'ZIP'} ) + assert_equal ["BillingPostalCode", "BillingStreetAddressLineFour", "BillingStreetAddressLineOne"], result.stringify_keys.keys.sort + assert_equal 'ZIP', result['BillingPostalCode'] + assert_equal 'Address 2', result['BillingStreetAddressLineFour'] + assert_equal 'Address 1', result['BillingStreetAddressLineOne'] + end + + def test_add_creditcard + result = {} + @gateway.send(:add_creditcard, result, @credit_card) + assert_equal ["CVV2", "CardNumber", "Expiry"], result.stringify_keys.keys.sort + assert_equal @credit_card.number, result['CardNumber'] + assert_equal @gateway.send(:expdate, @credit_card), result['Expiry'] + assert_equal @credit_card.verification_value, result['CVV2'] + end + + def test_add_currency_code_default + result = {} + @gateway.send(:add_currency_code, result, 100, {}) + assert_equal 'USD', result['Currency'] + end + + def test_add_currency_code_from_options + result = {} + @gateway.send(:add_currency_code, result, 100, {currency: 'CAN'}) + assert_equal 'CAN', result['Currency'] + end + + def test_parse + result = @gateway.send(:parse, "key1=value1&key2=value2") + h = {'key1' => 'value1', 'key2' => 'value2'} + assert_equal h, result + end + + def test_endpoint_for_void + assert_equal 'void', @gateway.send(:endpoint, 'void') + end + + def test_endpoint_for_cc_debit + assert_equal 'submit', @gateway.send(:endpoint, 'cc_debit') + end + + def test_endpoint_for_cc_preauth + assert_equal 'submit', @gateway.send(:endpoint, 'cc_preauth') + end + + def test_endpoint_for_cc_settle + assert_equal 'submit', @gateway.send(:endpoint, 'cc_settle') + end + + def test_endpoint_for_cc_refund + assert_equal 'submit', @gateway.send(:endpoint, 'cc_refund') + end + + def test_success + assert @gateway.send(:success?, { + :action => 'cc_settle', + 'ApprovalCode' => '123456', + 'ErrorCode' => nil, + 'Status' => 'Approved' + }) + + refute @gateway.send(:success?, { + :action => 'cc_settle', + 'ApprovalCode' => nil, + 'ErrorCode' => 'SomeError', + 'Status' => 'SomeError' + }) + + assert @gateway.send(:success?, { + :action => 'cc_debit', + 'ApprovalCode' => '123456', + 'ErrorCode' => nil, + 'Status' => 'Approved' + }) + + refute @gateway.send(:success?, { + :action => 'cc_debit', + 'ApprovalCode' => nil, + 'ErrorCode' => 'SomeError', + 'Status' => 'SomeError' + }) + + assert @gateway.send(:success?, { + :action => 'cc_preauth', + 'ApprovalCode' => '123456', + 'ErrorCode' => nil, + 'Status' => 'Approved' + }) + + refute @gateway.send(:success?, { + :action => 'cc_preauth', + 'ApprovalCode' => nil, + 'ErrorCode' => 'SomeError', + 'Status' => 'SomeError' + }) + + assert @gateway.send(:success?, { + :action => 'cc_refund', + 'ApprovalCode' => '123456', + 'ErrorCode' => nil, + 'Status' => 'Approved' + }) + + refute @gateway.send(:success?, { + :action => 'cc_refund', + 'ApprovalCode' => nil, + 'ErrorCode' => 'SomeError', + 'Status' => 'SomeError' + }) + + assert @gateway.send(:success?, { + :action => 'void', + 'ApprovalCode' => '123456', + 'ErrorCode' => nil, + 'Status' => 'Voided' + }) + + refute @gateway.send(:success?, { + :action => 'void', + 'ApprovalCode' => nil, + 'ErrorCode' => 'SomeError', + 'Status' => 'SomeError' + }) + end + + def test_message_from_approved + assert_equal "This transaction has been approved", @gateway.send(:message_from, { + 'Status' => 'Approved', + 'Message'=> nil + }) + end + + def test_message_from_declined + assert_equal "This transaction has been declined", @gateway.send(:message_from, { + 'Status' => 'Declined', + 'Message'=> nil + }) + end + + def test_message_from_voided + assert_equal "This transaction has been voided", @gateway.send(:message_from, { + 'Status' => 'Voided', + 'Message'=> nil + }) + end + + def test_message_from_status + assert_equal "This is the message", @gateway.send(:message_from, { + 'Status' => 'SomeStatus', + 'Message'=> "This is the message" + }) + end + + def test_post_data + @gateway.stubs(:request_id => "wouykiikdvqbwwxueppby") + @gateway.stubs(:timestamp => "2013-10-08T14:31:54.Z") + + assert_equal "PymtType=cc_preauth&RAPIVersion=2&UserName=user&Timestamp=2013-10-08T14%3A31%3A54.Z&RequestID=wouykiikdvqbwwxueppby&Signature=7794efc8c0d39f0983edc10f778e6143ba13531d&CardNumber=4242424242424242&Expiry=0914&CVV2=123&Currency=USD&BillingStreetAddressLineOne=Address+1&BillingStreetAddressLineFour=Address+2&BillingPostalCode=ZIP123", + @gateway.send(:post_data, 'cc_preauth', { + 'CardNumber' => @credit_card.number, + 'Expiry' => @gateway.send(:expdate, @credit_card), + 'CVV2' => @credit_card.verification_value, + 'Currency' => 'USD', + 'BillingStreetAddressLineOne' => 'Address 1', + 'BillingStreetAddressLineFour' => 'Address 2', + 'BillingPostalCode' => 'ZIP123' + }) + end + + def test_signature_for_cc_preauth_action + assert_equal 'd5ff154d6631333c21d0c78975b3bf5d9ccd0ef8', @gateway.send(:signature, 'cc_preauth', { + 'UserName' => 'user', + 'Timestamp' => '2013-10-08T14:31:54.Z', + 'RequestID' => 'wouykiikdvqbwwxueppby', + 'PymtType' => 'cc_preauth' + }, { + 'Amount' => 100, + 'Currency' => 'USD', + 'TrackingNumber' => '123456789' + }) + end + + def test_signature_for_cc_settle_action + assert_equal 'c80cccf6c77438785726b5a447d5aed84738c6d1', @gateway.send(:signature, 'cc_settle', { + 'UserName' => 'user', + 'Timestamp' => '2013-10-08T14:31:54.Z', + 'RequestID' => 'wouykiikdvqbwwxueppby', + 'PymtType' => 'cc_settle' + }, { + 'Amount' => 100, + 'Currency' => 'USD', + 'TrackingNumber' => '123456789' + }) + end + + def test_signature_for_cc_debit_action + assert_equal 'b2a0eb307cfd092152d44b06a49a360feccdb1b9', @gateway.send(:signature, 'cc_debit', { + 'UserName' => 'user', + 'Timestamp' => '2013-10-08T14:31:54.Z', + 'RequestID' => 'wouykiikdvqbwwxueppby', + 'PymtType' => 'cc_debit' + }, { + 'Amount' => 100, + 'Currency' => 'USD', + 'TrackingNumber' => '123456789' + }) + end + + def test_signature_for_cc_refund_action + assert_equal '9b174f1ebf5763e4793a52027645ff5156fca2e3', @gateway.send(:signature, 'cc_refund', { + 'UserName' => 'user', + 'Timestamp' => '2013-10-08T14:31:54.Z', + 'RequestID' => 'wouykiikdvqbwwxueppby', + 'PymtType' => 'cc_refund' + }, { + 'Amount' => 100, + 'Currency' => 'USD', + 'TrackingNumber' => '123456789' + }) + end + + def test_signature_for_void_action + assert_equal '236d4a857ee2e8cfec851be250159367d2c7c52e', @gateway.send(:signature, 'void', { + 'UserName' => 'user', + 'Timestamp' => '2013-10-08T14:31:54.Z', + 'RequestID' => 'wouykiikdvqbwwxueppby' + }, { + 'Amount' => 100, + 'Currency' => 'USD', + 'TrackingNumber' => '123456789' + }) + end + + def test_expdate + @credit_card.year = 2015 + @credit_card.month = 9 + assert_equal "0915", @gateway.send(:expdate, @credit_card) + end + + private + + def failed_void_response + %w( + ApprovalCode= + ErrorCode=error:canNotBeVoided + Message=Error+processing+transaction+because+the+payment+may+not+be+voided + RequestNumber=603758541 + RequestResult=ok + Status=Approved + TrackingNumber=123456789 + ).join('&') + end + + def successful_authorization_response + %w( + ApprovalCode=123456 + ErrorCode= + Message= + RequestNumber=603758541 + RequestResult=ok + Status=Approved + TrackingNumber=123456789 + ).join('&') + end + + def successful_purchase_response + %w( + ApprovalCode=123456 + ErrorCode= + Message= + RequestNumber=603758541 + RequestResult=ok + Status=Approved + TrackingNumber=123456789 + ).join('&') + end + + def successful_capture_response + %w( + ApprovalCode=123456 + ErrorCode= + Message= + RequestNumber=603758541 + RequestResult=ok + Status=Approved + TrackingNumber=123456789 + ).join('&') + end + + def invalid_preauth_number_response + %w( + ApprovalCode= + ErrorCode=invalid:PreAuthNumber + Message=Error+processing+transaction+because+the+pre-auth+number + RequestNumber=603758541 + RequestResult=ok + Status=Invalid:PreauthNumber + TrackingNumber=123456789 + ).join('&') + end + + def insufficient_preauth_amount_response + %w( + ApprovalCode= + ErrorCode=rejected:PreauthAmountInsufficient + Message=Invalid+because+the+preauthorization+amount+100+is+insufficient + RequestNumber=603758541 + RequestResult=ok + Status=Rejected:PreauthAmountInsufficient + TrackingNumber=123456789 + ).join('&') + end + + def invalid_credit_card_response + %w( + ApprovalCode= + ErrorCode=invalid:cardNumber + Message=Error+processing+transaction+because+CardNumber+is+not+between+12+and+19+in+length + RequestNumber=603758541 + RequestResult=ok + Status=Invalid:CardNumber + TrackingNumber=123456789 + ).join('&') + end + + def expired_credit_card_response + %w( + ApprovalCode= + ErrorCode=invalid:CustomerCardExpiryDate + Message=Invalid+because+the+card+expiry+date+is+not+a+date+in+the+future + RequestNumber=603758541 + RequestResult=ok + Status=Invalid:CustomerCardExpiryDate + TrackingNumber=123456789 + ).join('&') + end + + def declined_purchese_response + %w( + ApprovalCode=123456 + ErrorCode= + Message= + RequestNumber=603758541 + RequestResult=ok + Status=Declined + TrackingNumber=123456789 + ).join('&') + end + + def successful_refund_response + %w( + ApprovalCode=123456 + ErrorCode= + Message= + RequestNumber=603758541 + RequestResult=ok + Status=Approved + TrackingNumber=123456789 + ).join('&') + end + + def amount_greater_than_original_amount_refund_response + %w( + ApprovalCode= + ErrorCode=invalid:RefundAmountGreaterThanOriginalAmount + Message=Invalid+because+the+payment+amount+cannot+be+greater+than+the+original+charge + RequestNumber=603758541 + RequestResult=ok + Status=Invalid:RefundAmountGreaterThanOriginalAmount + TrackingNumber=123456789 + ).join('&') + end + + def successful_void_response + %w( + ApprovalCode=123456 + ErrorCode= + Message= + RequestNumber=603758541 + RequestResult=ok + Status=Voided + TrackingNumber=123456789 + ).join('&') + end +end diff --git a/test/unit/gateways/payex_test.rb b/test/unit/gateways/payex_test.rb new file mode 100644 index 00000000000..b2e5a8a78ef --- /dev/null +++ b/test/unit/gateways/payex_test.rb @@ -0,0 +1,284 @@ +require 'test_helper' + +class PayexTest < Test::Unit::TestCase + def setup + @gateway = PayexGateway.new( + :account => 'account', + :encryption_key => 'encryption_key' + ) + + @credit_card = credit_card + @amount = 1000 + + @options = { + :order_id => '1234', + } + end + + def test_successful_purchase + @gateway.expects(:ssl_post).times(2).returns(successful_initialize_response, successful_purchase_response) + + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_instance_of MultiResponse, response + assert_success response + + assert_equal '2623681', response.authorization + assert response.test? + end + + def test_failed_purchase + @gateway.expects(:ssl_post).times(2).returns(successful_initialize_response, failed_purchase_response) + + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert response.test? + end + + def test_successful_authorize + @gateway.expects(:ssl_post).times(2).returns(successful_initialize_response, successful_authorize_response) + assert response = @gateway.authorize(@amount, @credit_card, @options) + assert_success response + assert response.authorization + assert_equal 'OK', response.message + assert_equal '2624653', response.authorization + assert response.test? + end + + def test_successful_capture + @gateway.expects(:ssl_post).returns(successful_capture_response) + assert response = @gateway.capture(@amount, 'fakeauth') + assert_success response + assert_equal '2624655', response.authorization + assert response.test? + end + + def test_failed_capture + @gateway.expects(:ssl_post).returns(failed_capture_response) + assert response = @gateway.capture(@amount, '1') + assert_failure response + assert_not_equal 'OK', response.message + assert_not_equal 'RecordNotFound', response.params[:status_errorcode] + assert response.test? + end + + def test_successful_void + @gateway.expects(:ssl_post).returns(successful_void_response) + + assert response = @gateway.void('fakeauth') + assert_success response + assert_equal '2624825', response.authorization + assert response.test? + end + + def test_unsuccessful_void + @gateway.expects(:ssl_post).returns(unsuccessful_void_response) + assert response = @gateway.void("1") + assert_failure response + assert_not_equal 'OK', response.message + assert_match /1/, response.message + assert response.test? + end + + def test_successful_refund + @gateway.expects(:ssl_post).returns(successful_refund_response) + assert response = @gateway.refund(@amount - 200, 'fakeauth', order_id: '123') + assert_success response + assert_equal '2624828', response.authorization + assert response.test? + end + + def test_unsuccessful_refund + @gateway.expects(:ssl_post).returns(unsuccessful_refund_response) + assert response = @gateway.refund(@amount, "1", order_id: '123') + assert_failure response + assert_not_equal 'OK', response.message + assert_match /1/, response.message + assert response.test? + end + + def test_successful_store + @gateway.expects(:ssl_post).times(3).returns(successful_store_response, successful_initialize_response, successful_purchase_response) + assert response = @gateway.store(@credit_card, @options.merge({merchant_ref: '9876'})) + assert_success response + assert_equal 'OK', response.message + assert_equal 'bcea4ac8d1f44640bff7a8c93caa249c', response.authorization + assert response.test? + end + + def test_successful_unstore + @gateway.expects(:ssl_post).returns(successful_unstore_response) + assert response = @gateway.unstore('fakeauth') + assert_success response + assert_equal 'OK', response.message + assert response.test? + end + + def test_successful_purchase_with_stored_card + @gateway.expects(:ssl_post).returns(successful_autopay_response) + assert response = @gateway.purchase(@amount, 'fakeauth', @options.merge({order_id: '5678'})) + assert_success response + assert_equal 'OK', response.message + assert_equal '2624657', response.authorization + assert response.test? + end + + private + + def successful_initialize_response + %q{ + + + + <?xml version="1.0" encoding="utf-8" ?><payex><header name="Payex Header v1.0"><id>09ef982cf2584d58bf4363dacd2ef127</id><date>2013-11-06 14:19:23</date></header><status><code>OK</code><description>OK</description><errorCode>OK</errorCode><paramName /><thirdPartyError /><thirdPartySubError /></status><orderRef>53681e74064a4621b93a6fcceba20c00</orderRef><sessionRef>7424a69d355c4cafa853ff49553b786f</sessionRef><redirectUrl>https://test-confined.payex.com/PxOrderCC.aspx?orderRef=53681e74064a4621b93a6fcceba20c00</redirectUrl></payex> + + + + } + end + + # Place raw successful response from gateway here + def successful_purchase_response + %q{ + + + + <?xml version="1.0" encoding="utf-8" ?><payex><header name="Payex Header v1.0"><id>45eca449c8b54b54ac8811d4c26f638d</id><date>2013-11-06 14:19:35</date></header><status><code>OK</code><description>OK</description><errorCode>OK</errorCode><paramName /><thirdPartyError /></status><transactionStatus>0</transactionStatus><transactionRef>750dc1438350481086abd0438bde0c23</transactionRef><transactionNumber>2623681</transactionNumber><orderId>1234</orderId><productId>4321</productId><paymentMethod>VISA</paymentMethod><productNumber>4321</productNumber><BankHash>00000001-4581-0903-5682-000000000000</BankHash><AuthenticatedStatus>None</AuthenticatedStatus><AuthenticatedWith>N</AuthenticatedWith></payex> + + + + } + end + + def failed_purchase_response + %q{ + + + + <?xml version="1.0" encoding="utf-8" ?><payex><header name="Payex Header v1.0"><id>131faa4301f74e91bf29e9749ad8f2a6</id><date>2013-11-06 14:40:18</date></header><status><code>ValidationError_InvalidParameter</code><errorCode>ValidationError_InvalidParameter</errorCode><description>Invalid parameter:expireDate + Parameter name: expireDate</description><paramName>expireDate</paramName><thirdPartyError /></status></payex> + + + + } + end + + def successful_authorize_response + %q{ + + + + <?xml version="1.0" encoding="utf-8" ?><payex><header name="Payex Header v1.0"><id>41ebf6488fdb42a79baf49d2ef9e7dc6</id><date>2013-11-06 14:43:01</date></header><status><code>OK</code><description>OK</description><errorCode>OK</errorCode><paramName /><thirdPartyError /></status><transactionStatus>3</transactionStatus><transactionRef>89cbf30161f442228cbca16ff7f886d4</transactionRef><transactionNumber>2624653</transactionNumber><orderId>1234</orderId><productId>4321</productId><paymentMethod>VISA</paymentMethod><productNumber>4321</productNumber><BankHash>00000001-4581-0903-5682-000000000000</BankHash><AuthenticatedStatus>None</AuthenticatedStatus><AuthenticatedWith>N</AuthenticatedWith></payex> + + + + } + end + + def successful_capture_response + %q{ + + + + <?xml version="1.0" encoding="utf-8" ?><payex><header name="Payex Header v1.0"><id>efe1a4572c9b4ed5a656d648bf7f9207</id><date>2013-11-06 14:43:03</date></header><status><code>OK</code><description>OK</description><errorCode>OK</errorCode><paramName /><thirdPartyError /><thirdPartySubError /></status><transactionStatus>6</transactionStatus><transactionNumber>2624655</transactionNumber><originalTransactionNumber>2624653</originalTransactionNumber></payex> + + + + } + end + + def failed_capture_response + %q{ + + + + <?xml version="1.0" encoding="utf-8" ?><payex><header name="Payex Header v1.0"><id>19706b063aad458aa9783563a4e5bbff</id><date>2013-11-06 14:53:34</date></header><status><code>ValidationError_Generic</code><description>1</description><errorCode>NoRecordFound</errorCode><paramName /><thirdPartyError /><thirdPartySubError /></status></payex> + + + + } + end + + def successful_void_response + %q{ + + + + <?xml version="1.0" encoding="utf-8" ?><payex><header name="Payex Header v1.0"><id>219b3a4b4ae0478482c75eba06d5a0dd</id><date>2013-11-06 14:56:48</date></header><status><code>OK</code><description>OK</description><errorCode>OK</errorCode><paramName /><thirdPartyError /><thirdPartySubError /></status><transactionStatus>4</transactionStatus><transactionNumber>2624825</transactionNumber><originalTransactionNumber>2624824</originalTransactionNumber></payex> + + + + } + end + + def unsuccessful_void_response + %q{ + + + + <?xml version="1.0" encoding="utf-8" ?><payex><header name="Payex Header v1.0"><id>220b395b966a43eb9d0c70109f6dadd3</id><date>2013-11-06 15:02:24</date></header><status><code>ValidationError_Generic</code><description>1</description><errorCode>Error_Generic</errorCode><paramName /><thirdPartyError /><thirdPartySubError /></status></payex> + + + + } + end + + def successful_refund_response + %q{ + + + + <?xml version="1.0" encoding="utf-8" ?><payex><header name="Payex Header v1.0"><id>a1e70daf3ed842ddb72e58a28f5cbc11</id><date>2013-11-06 14:57:54</date></header><status><code>OK</code><description>OK</description><errorCode>OK</errorCode><paramName /><thirdPartyError /><thirdPartySubError /></status><transactionStatus>2</transactionStatus><transactionNumber>2624828</transactionNumber><originalTransactionNumber>2624827</originalTransactionNumber></payex> + + + + } + end + + def unsuccessful_refund_response + %q{ + + + + <?xml version="1.0" encoding="utf-8" ?><payex><header name="Payex Header v1.0"><id>e82613f6c59d4e08a7163cd91c2b3ce5</id><date>2013-11-06 15:02:23</date></header><status><code>ValidationError_Generic</code><description>1</description><errorCode>Error_Generic</errorCode><paramName /><thirdPartyError /><thirdPartySubError /></status></payex> + + + + } + end + + def successful_store_response + %q{ + + + + <?xml version="1.0" encoding="utf-8" ?><payex><header name="Payex Header v1.0"><id>3c8063b7fcb64a449b715fe711f0a03f</id><date>2013-11-06 14:43:04</date></header><status><code>OK</code><description>OK</description><errorCode>OK</errorCode><paramName /><thirdPartyError /><thirdPartySubError /></status><agreementRef>bcea4ac8d1f44640bff7a8c93caa249c</agreementRef></payex> + + + + } + end + + def successful_unstore_response + %q{ + + + + <?xml version="1.0" encoding="utf-8" ?><payex><header name="Payex Header v1.0"><id>7935ca4c24c3467cb36c48a270557194</id><date>2013-11-06 15:06:54</date></header><status><code>OK</code><description>OK</description><errorCode>OK</errorCode><paramName /><thirdPartyError /><thirdPartySubError /></status><agreementRef>7e9c6342dc20459691d1abb027a3c8c0</agreementRef></payex> + + + + } + end + + def successful_autopay_response + %q{ + + + + <?xml version="1.0" encoding="utf-8" ?><payex><header name="Payex Header v1.0"><id>37a7164c16804af199b7d6aade0aa580</id><date>2013-11-06 14:43:09</date></header><status><code>OK</code><description>OK</description><errorCode>OK</errorCode><paramName /><thirdPartyError /><thirdPartySubError /></status><transactionStatus>3</transactionStatus><transactionRef>de2984e302da40b498afe5aced8cea7e</transactionRef><transactionNumber>2624657</transactionNumber><paymentMethod>VISA</paymentMethod><captureTokens><stan>1337</stan><terminalId>666</terminalId><transactionTime>11/6/2013 2:43:09 PM</transactionTime></captureTokens><pending>false</pending></payex> + + + + } + end +end diff --git a/test/unit/gateways/payflow_test.rb b/test/unit/gateways/payflow_test.rb index 9ee933154d0..685da48bd49 100644 --- a/test/unit/gateways/payflow_test.rb +++ b/test/unit/gateways/payflow_test.rb @@ -12,6 +12,7 @@ def setup @amount = 100 @credit_card = credit_card('4242424242424242') @options = { :billing_address => address.merge(:first_name => "Longbob", :last_name => "Longsen") } + @check = check( :name => 'Jim Smith' ) end def test_successful_authorization @@ -78,6 +79,18 @@ def test_cvv_result assert_equal 'M', response.cvv_result['code'] end + def test_ach_purchase + @gateway.expects(:ssl_post).with(anything, regexp_matches(/#{@check.account_number}<\//), anything).returns("") + @gateway.expects(:parse).returns({}) + @gateway.purchase(@amount, @check) + end + + def test_ach_credit + @gateway.expects(:ssl_post).with(anything, regexp_matches(/#{@check.account_number}<\//), anything).returns("") + @gateway.expects(:parse).returns({}) + @gateway.credit(@amount, @check) + end + def test_using_test_mode assert @gateway.test? end diff --git a/test/unit/gateways/paymill_test.rb b/test/unit/gateways/paymill_test.rb index 5417a0016e4..9c311960c7c 100644 --- a/test/unit/gateways/paymill_test.rb +++ b/test/unit/gateways/paymill_test.rb @@ -14,7 +14,7 @@ def test_successful_purchase assert response = @gateway.purchase(@amount, @credit_card) assert_success response assert_equal "tran_c94ba7df2dae8fd55028df41173c;", response.authorization - assert_equal "Transaction approved", response.message + assert_equal "General success response.", response.message assert_equal 20000, response.params['data']['response_code'] assert_equal 'pay_b8e6a28fc5e5e1601cdbefbaeb8a', response.params['data']['payment']['id'] assert_equal '5100', response.params['data']['payment']['last4'] @@ -23,7 +23,7 @@ def test_successful_purchase assert response.test? end - def test_failed_purchase_with_invalid_credit_card + def test_failed_store_card_attempting_purchase @gateway.expects(:raw_ssl_request).returns(failed_store_response) response = @gateway.purchase(@amount, @credit_card) assert_failure response @@ -31,6 +31,14 @@ def test_failed_purchase_with_invalid_credit_card assert_equal '000.100.201', response.params['transaction']['processing']['return']['code'] end + def test_failed_purchase + @gateway.stubs(:raw_ssl_request).returns(successful_store_response, failed_purchase_response) + response = @gateway.purchase(@amount, @credit_card) + assert_failure response + assert_equal 'Card declined by authorization system.', response.message + assert_equal 50102, response.params['data']['response_code'] + end + def test_invalid_login @gateway.stubs(:raw_ssl_request).returns(successful_store_response, failed_login_response) response = @gateway.purchase(@amount, @credit_card) @@ -52,10 +60,10 @@ def test_successful_authorize_and_capture assert_success response assert response.test? - assert_equal "tran_50fb13e10636cf1e59e13018d100;preauth_57c0c87ae3d193f66dc8", response.authorization - assert_equal "Transaction approved", response.message - assert_equal '5100', response.params['data']['payment']['last4'] - assert_equal 10001, response.params['data']['response_code'] + assert_equal "tran_4c612d5293e26d56d986eb89648c;preauth_fdf916cab73b97c4a139", response.authorization + assert_equal "General success response.", response.message + assert_equal '0004', response.params['data']['payment']['last4'] + assert_equal 20000, response.params['data']['response_code'] assert_nil response.avs_result["message"] assert_nil response.cvv_result["message"] @@ -64,7 +72,15 @@ def test_successful_authorize_and_capture assert_success response assert response.test? assert_equal 20000, response.params['data']['response_code'] - assert_equal "Transaction approved", response.message + assert_equal "General success response.", response.message + end + + def test_failed_authorize + @gateway.stubs(:raw_ssl_request).returns(successful_store_response, failed_authorize_response) + response = @gateway.authorize(@amount, @credit_card) + assert_failure response + assert_equal 'Card declined by authorization system.', response.message + assert_equal 50102, response.params['data']['response_code'] end def test_successful_authorize_and_void @@ -77,7 +93,7 @@ def test_successful_authorize_and_void response = @gateway.void(response.authorization) assert_success response assert response.test? - assert_equal "Transaction approved", response.message + assert_equal "Transaction approved.", response.message end def test_failed_capture @@ -101,7 +117,7 @@ def test_successful_refund assert_success refund assert response.test? - assert_equal 'Transaction approved', refund.message + assert_equal 'General success response.', refund.message assert_equal 'tran_89c8728e94273510afa99ab64e45', refund.params['data']['transaction']['id'] assert_equal 'refund_d02807f46181c0919016;', refund.authorization assert_equal 20000, refund.params['data']['response_code'] @@ -142,7 +158,7 @@ def test_successful_purchase_with_token assert response = @gateway.purchase(@amount, "token") assert_success response assert_equal "tran_c94ba7df2dae8fd55028df41173c;", response.authorization - assert_equal "Transaction approved", response.message + assert_equal "General success response.", response.message assert_equal 20000, response.params['data']['response_code'] assert_equal 'pay_b8e6a28fc5e5e1601cdbefbaeb8a', response.params['data']['payment']['id'] assert_equal '5100', response.params['data']['payment']['last4'] @@ -158,10 +174,10 @@ def test_successful_authorize_with_token assert_success response assert response.test? - assert_equal "tran_50fb13e10636cf1e59e13018d100;preauth_57c0c87ae3d193f66dc8", response.authorization - assert_equal "Transaction approved", response.message - assert_equal '5100', response.params['data']['payment']['last4'] - assert_equal 10001, response.params['data']['response_code'] + assert_equal "tran_4c612d5293e26d56d986eb89648c;preauth_fdf916cab73b97c4a139", response.authorization + assert_equal "General success response.", response.message + assert_equal '0004', response.params['data']['payment']['last4'] + assert_equal 20000, response.params['data']['response_code'] assert_nil response.avs_result["message"] assert_nil response.cvv_result["message"] end @@ -232,81 +248,244 @@ def successful_purchase_response JSON end + def failed_purchase_response + MockResponse.failed <<-JSON + { + "data":{ + "id":"tran_a432ce3b113cdd65b48e0d05db88", + "amount":"100", + "origin_amount":100, + "status":"failed", + "description":null, + "livemode":false, + "refunds":null, + "currency":"EUR", + "created_at":1385054845, + "updated_at":1385054845, + "response_code":50102, + "short_id":null, + "is_fraud":false, + "invoices":[ + + ], + "app_id":null, + "fees":[ + + ], + "payment":{ + "id":"pay_8b75b960574031979a880f98", + "type":"creditcard", + "client":"client_a69d8452d530ed20b297", + "card_type":"mastercard", + "country":null, + "expire_month":"5", + "expire_year":"2020", + "card_holder":"", + "last4":"5100", + "created_at":1385054844, + "updated_at":1385054845, + "app_id":null + }, + "client":{ + "id":"client_a69d8452d530ed20b297", + "email":null, + "description":null, + "created_at":1385054845, + "updated_at":1385054845, + "app_id":null, + "payment":[ + + ], + "subscription":null + }, + "preauthorization":null + }, + "mode":"test" + } + JSON + end + def successful_authorize_response MockResponse.succeeded <<-JSON - { "data":{ - "id":"tran_50fb13e10636cf1e59e13018d100", - "amount":"100", - "origin_amount":100, - "status":"preauth", - "description":null, - "livemode":false, - "refunds":null, - "currency":"EUR", - "created_at":1360787311, - "updated_at":1360787311, - "response_code":10001, - "invoices":[ - - ], - "payment":{ - "id":"pay_58e0662ef367027b2356f263e5aa", - "type":"creditcard", - "client":"client_9e4b7b0d61adc9a9e64e", - "card_type":"mastercard", - "country":null, - "expire_month":9, - "expire_year":2014, - "card_holder":null, - "last4":"5100", - "created_at":1360787310, - "updated_at":1360787311 - }, - "client":{ - "id":"client_9e4b7b0d61adc9a9e64e", - "email":null, - "description":null, - "created_at":1360787311, - "updated_at":1360787311, - "payment":[ + { + "data":{ + "id":"tran_4c612d5293e26d56d986eb89648c", + "amount":"100", + "origin_amount":100, + "status":"preauth", + "description":null, + "livemode":false, + "refunds":null, + "currency":"EUR", + "created_at":1385054035, + "updated_at":1385054035, + "response_code":20000, + "short_id":"7357.7357.7357", + "is_fraud":false, + "invoices":[ - ], - "subscription":null + ], + "app_id":null, + "fees":[ + + ], + "payment":{ + "id":"pay_f9ff269434185e0789106758", + "type":"creditcard", + "client":"client_d5179e1b6a8f596b19b9", + "card_type":"mastercard", + "country":null, + "expire_month":"9", + "expire_year":"2014", + "card_holder":"", + "last4":"0004", + "created_at":1385054033, + "updated_at":1385054035, + "app_id":null + }, + "client":{ + "id":"client_d5179e1b6a8f596b19b9", + "email":null, + "description":null, + "created_at":1385054035, + "updated_at":1385054035, + "app_id":null, + "payment":[ + + ], + "subscription":null + }, + "preauthorization":{ + "id":"preauth_fdf916cab73b97c4a139", + "amount":"100", + "currency":"EUR", + "status":"closed", + "livemode":false, + "created_at":1385054035, + "updated_at":1385054035, + "app_id":null, + "payment":{ + "id":"pay_f9ff269434185e0789106758", + "type":"creditcard", + "client":"client_d5179e1b6a8f596b19b9", + "card_type":"mastercard", + "country":null, + "expire_month":"9", + "expire_year":"2014", + "card_holder":"", + "last4":"0004", + "created_at":1385054033, + "updated_at":1385054035, + "app_id":null + }, + "client":{ + "id":"client_d5179e1b6a8f596b19b9", + "email":null, + "description":null, + "created_at":1385054035, + "updated_at":1385054035, + "app_id":null, + "payment":[ + + ], + "subscription":null + } + } }, - "preauthorization":{ - "id":"preauth_57c0c87ae3d193f66dc8", + "mode":"test" + } + JSON + end + + # Paymill returns an HTTP Status code of 200 for an auth failure. + def failed_authorize_response + MockResponse.succeeded <<-JSON + { + "data":{ + "id":"tran_e53189278c7250bfa15c9c580ff2", "amount":"100", - "status":"closed", + "origin_amount":100, + "status":"failed", + "description":null, "livemode":false, - "created_at":1360787311, - "updated_at":1360787311, + "refunds":null, + "currency":"EUR", + "created_at":1385054501, + "updated_at":1385054501, + "response_code":50102, + "short_id":null, + "is_fraud":false, + "invoices":[ + + ], + "app_id":null, + "fees":[ + + ], "payment":{ - "id":"pay_58e0662ef367027b2356f263e5aa", + "id":"pay_7bc2d73764f38040df934995", "type":"creditcard", - "client":"client_9e4b7b0d61adc9a9e64e", + "client":"client_531e6247ff900e734884", "card_type":"mastercard", "country":null, - "expire_month":9, - "expire_year":2014, - "card_holder":null, + "expire_month":"5", + "expire_year":"2020", + "card_holder":"", "last4":"5100", - "created_at":1360787310, - "updated_at":1360787311 - }, - "client":{ - "id":"client_9e4b7b0d61adc9a9e64e", + "created_at":1385054500, + "updated_at":1385054501, + "app_id":null + }, + "client":{ + "id":"client_531e6247ff900e734884", "email":null, "description":null, - "created_at":1360787311, - "updated_at":1360787311, + "created_at":1385054501, + "updated_at":1385054501, + "app_id":null, "payment":[ ], "subscription":null + }, + "preauthorization":{ + "id":"preauth_cfa6a29b4c679efee58b", + "amount":"100", + "currency":"EUR", + "status":"failed", + "livemode":false, + "created_at":1385054501, + "updated_at":1385054501, + "app_id":null, + "payment":{ + "id":"pay_7bc2d73764f38040df934995", + "type":"creditcard", + "client":"client_531e6247ff900e734884", + "card_type":"mastercard", + "country":null, + "expire_month":"5", + "expire_year":"2020", + "card_holder":"", + "last4":"5100", + "created_at":1385054500, + "updated_at":1385054501, + "app_id":null + }, + "client":{ + "id":"client_531e6247ff900e734884", + "email":null, + "description":null, + "created_at":1385054501, + "updated_at":1385054501, + "app_id":null, + "payment":[ + + ], + "subscription":null + } } - } - }, - "mode":"test" + }, + "mode":"test" } JSON end diff --git a/test/unit/gateways/payscout_test.rb b/test/unit/gateways/payscout_test.rb new file mode 100644 index 00000000000..0781da14568 --- /dev/null +++ b/test/unit/gateways/payscout_test.rb @@ -0,0 +1,538 @@ +require 'test_helper' + +class PayscoutTest < Test::Unit::TestCase + def setup + @gateway = PayscoutGateway.new( + :username => 'xxx', + :password => 'xxx' + ) + + @credit_card = credit_card + @amount = 100 + + @options = { + :order_id => '1', + :billing_address => address, + :description => 'Store Purchase' + } + end + + # Purchase + + def test_approved_puschase + @gateway.expects(:ssl_post).returns(approved_purchase_response) + + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_instance_of Response, response + assert_success response + + assert_equal '1234567891', response.authorization + assert_equal 'The transaction has been approved', response.message + assert response.test? + end + + def test_declined_puschase + @gateway.expects(:ssl_post).returns(declined_purchase_response) + + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_instance_of Response, response + assert_failure response + + assert_equal '1234567892', response.authorization + assert_equal 'The transaction has been declined', response.message + assert response.test? + end + + # Authorization + + def test_approved_authorization + @gateway.expects(:ssl_post).returns(approved_authorization_response) + + assert response = @gateway.authorize(@amount, @credit_card, @options) + assert_instance_of Response, response + assert_success response + + assert_equal '1234567890', response.authorization + assert_equal 'The transaction has been approved', response.message + assert response.test? + end + + def test_declined_authorization + @gateway.expects(:ssl_post).returns(declined_authorization_response) + + assert response = @gateway.authorize(@amount, @credit_card, @options) + assert_instance_of Response, response + assert_failure response + + assert_equal '1234567893', response.authorization + assert_equal 'The transaction has been declined', response.message + assert response.test? + end + + # Capture + + def test_approved_capture + @gateway.expects(:ssl_post).returns(approved_capture_response) + + assert response = @gateway.capture(@amount, '1234567894', @options) + assert_instance_of Response, response + assert_success response + + assert_equal '1234567894', response.authorization + assert_equal 'The transaction has been approved', response.message + assert response.test? + end + + def test_invalid_amount_capture + @gateway.expects(:ssl_post).returns(invalid_amount_capture_response) + + assert response = @gateway.capture(@amount, '1234567895', @options) + assert_instance_of Response, response + assert_failure response + + assert_equal '1234567895', response.authorization + assert_equal 'The+specified+amount+of+2.00+exceeds+the+authorization+amount+of+1.00', response.message + assert response.test? + end + + def test_not_found_transaction_id_capture + @gateway.expects(:ssl_post).returns(not_found_transaction_id_capture_response) + + assert capture = @gateway.capture(@amount, '1234567890') + assert_failure capture + assert_match 'Transaction+not+found', capture.message + end + + def test_invalid_transaction_id_capture + @gateway.expects(:ssl_post).returns(invalid_transaction_id_capture_response) + + assert capture = @gateway.capture(@amount, '') + assert_failure capture + assert_match 'Invalid+Transaction+ID', capture.message + end + + # Refund + + def test_approved_refund + @gateway.expects(:ssl_post).returns(approved_refund_response) + + assert refund = @gateway.refund(@amount, '1234567890') + assert_success refund + assert_equal "The transaction has been approved", refund.message + end + + def test_not_found_transaction_id_refund + @gateway.expects(:ssl_post).returns(not_found_transaction_id_refund_response) + + assert refund = @gateway.refund(@amount, '1234567890') + assert_failure refund + assert_match "Transaction+not+found", refund.message + end + + def test_invalid_transaction_id_refund + @gateway.expects(:ssl_post).returns(invalid_transaction_id_refund_response) + + assert refund = @gateway.refund(@amount, '') + assert_failure refund + assert_match "Invalid+Transaction+ID", refund.message + end + + def test_invalid_amount_refund + @gateway.expects(:ssl_post).returns(invalid_amount_refund_response) + + assert refund = @gateway.refund(200, '1234567890') + assert_failure refund + assert_match "Refund+amount+may+not+exceed+the+transaction+balance", refund.message + end + + # Void + + def test_approved_void_purchase + @gateway.expects(:ssl_post).returns(approved_void_purchase_response) + + assert void = @gateway.void('1234567890') + assert_success void + assert_equal "The transaction has been approved", void.message + end + + def test_approved_void_authorization + @gateway.expects(:ssl_post).returns(approved_void_authorization_response) + + assert void = @gateway.void('1234567890') + assert_success void + assert_equal "The transaction has been approved", void.message + end + + def test_invalid_transaction_id_void + @gateway.expects(:ssl_post).returns(invalid_transaction_id_void_response) + + assert void = @gateway.void('') + assert_failure void + assert_match "Invalid+Transaction+ID", void.message + end + + def test_not_found_transaction_id_void + @gateway.expects(:ssl_post).returns(not_found_transaction_id_void_response) + + assert void = @gateway.void('1234567890') + assert_failure void + assert_match "Transaction+not+found", void.message + end + + # Methods + + def test_billing_address + post = {} + address = address(email: 'example@example.com') + @gateway.send(:add_address, post, { billing_address: address }) + + assert_equal address[:address1], post[:address1] + assert_equal address[:address2], post[:address2] + assert_equal address[:city], post[:city] + assert_equal address[:state], post[:state] + assert_equal address[:zip], post[:zip] + assert_equal address[:country], post[:country] + assert_equal address[:phone], post[:phone] + assert_equal address[:fax], post[:fax] + assert_equal address[:email], post[:email] + end + + def test_shipping_address + post = {} + address = address(email: 'example@example.com', first_name: 'John', last_name: 'Doe') + @gateway.send(:add_address, post, { shipping_address: address }) + + assert_equal address[:first_name], post[:shipping_firstname] + assert_equal address[:last_name], post[:shipping_lastname] + assert_equal address[:company], post[:shipping_company] + assert_equal address[:address1], post[:shipping_address1] + assert_equal address[:address2], post[:shipping_address2] + assert_equal address[:city], post[:shipping_city] + assert_equal address[:country], post[:shipping_country] + assert_equal address[:state], post[:shipping_state] + assert_equal address[:zip], post[:shipping_zip] + assert_equal address[:email], post[:shipping_email] + end + + + def test_add_currency_from_options + post = {} + @gateway.send(:add_currency, post, 100, { currency: 'CAD' }) + + assert_equal 'CAD', post[:currency] + end + + def test_add_currency_from_money + post = {} + @gateway.send(:add_currency, post, 100, {}) + + assert_equal 'USD', post[:currency] + end + + def test_add_invoice + post = {} + options = {description: 'Order Description', order_id: '123'} + @gateway.send(:add_invoice, post, options) + + assert_equal 'Order Description', post[:orderdescription] + assert_equal '123', post[:orderid] + end + + def test_expdate + @credit_card = credit_card + @credit_card.year = 2015 + @credit_card.month = 8 + + assert_equal "0815", @gateway.send(:expdate, @credit_card) + end + + def test_add_creditcard + post = {} + @gateway.send(:add_creditcard, post, @credit_card) + + assert_equal @credit_card.number, post[:ccnumber] + assert_equal @credit_card.verification_value, post[:cvv] + assert_equal @gateway.send(:expdate, @credit_card), post[:ccexp] + assert_equal @credit_card.first_name, post[:firstname] + assert_equal @credit_card.last_name, post[:lastname] + end + + def test_parse + data = @gateway.send(:parse, approved_authorization_response) + + assert data.keys.include?('response') + assert data.keys.include?('responsetext') + assert data.keys.include?('authcode') + assert data.keys.include?('transactionid') + assert data.keys.include?('avsresponse') + assert data.keys.include?('cvvresponse') + assert data.keys.include?('orderid') + assert data.keys.include?('type') + assert data.keys.include?('response_code') + + assert_equal '1', data['response'] + assert_equal 'SUCCESS', data['responsetext'] + assert_equal '123456', data['authcode'] + assert_equal '1234567890', data['transactionid'] + assert_equal 'N', data['avsresponse'] + assert_equal 'M', data['cvvresponse'] + assert_equal '1', data['orderid'] + assert_equal 'auth', data['type'] + assert_equal '100', data['response_code'] + end + + def test_message_from_for_approved_response + assert_equal 'The transaction has been approved', @gateway.send(:message_from, {'response' => '1'}) + end + + def test_message_from_for_declined_response + assert_equal 'The transaction has been declined', @gateway.send(:message_from, {'response' => '2'}) + end + + def test_message_from_for_failed_response + assert_equal 'Error message', @gateway.send(:message_from, {'response' => '3', 'responsetext' => 'Error message'}) + end + + def test_success + assert @gateway.send(:success?, {'response' => '1'}) + refute @gateway.send(:success?, {'response' => '2'}) + refute @gateway.send(:success?, {'response' => '3'}) + end + + def test_post_data + parameters = {param1: 'value1', param2: 'value2'} + result = @gateway.send(:post_data, 'auth', parameters) + + assert_match "username=xxx", result + assert_match "password=xxx", result + assert_match "type=auth", result + assert_match "param1=value1", result + assert_match "param2=value2", result + end + + private + + def approved_authorization_response + %w( + response=1 + responsetext=SUCCESS + authcode=123456 + transactionid=1234567890 + avsresponse=N + cvvresponse=M + orderid=1 + type=auth + response_code=100 + ).join('&') + end + + def declined_authorization_response + %w( + response=2 + responsetext=DECLINE + authcode= + transactionid=1234567893 + avsresponse=N + cvvresponse=M + orderid=1 + type=auth + response_code=200 + ).join('&') + end + + def approved_purchase_response + %w( + response=1 + responsetext=SUCCESS + authcode=123456 + transactionid=1234567891 + avsresponse=N + cvvresponse=M + orderid=1 + type=sale + response_code=100 + ).join('&') + end + + def declined_purchase_response + %w( + response=2 + responsetext=DECLINE + authcode= + transactionid=1234567892 + avsresponse=N + cvvresponse=M + orderid=1 + type=sale + response_code=200 + ).join('&') + end + + def approved_capture_response + %w( + response=1 + responsetext=SUCCESS + authcode=123456 + transactionid=1234567894 + avsresponse=N + cvvresponse=M + orderid=1 + type=capture + response_code=100 + ).join('&') + end + + def invalid_amount_capture_response + %w( + response=3 + responsetext=The+specified+amount+of+2.00+exceeds+the+authorization+amount+of+1.00 + authcode= + transactionid=1234567895 + avsresponse=N + cvvresponse=M + orderid=1 + type=capture + response_code=300 + ).join('&') + end + + def not_found_transaction_id_capture_response + %w( + response=3 + responsetext=Transaction+not+found+REFID:4054576 + authcode= + transactionid= + avsresponse= + cvvresponse= + orderid=1 + type=capture + response_code=300 + ).join('&') + end + + def invalid_transaction_id_capture_response + %w( + response=3 + responsetext=Invalid+Transaction+ID+/+Object+ID+specified:++REFID:4054567 + authcode= + transactionid= + avsresponse= + cvvresponse= + orderid=1 + type=capture + response_code=300 + ).join('&') + end + + def approved_refund_response + %w( + response=1 + responsetext=SUCCESS + authcode= + transactionid=1234567896 + avsresponse= + cvvresponse= + orderid=1 + type=refund + response_code=100 + ).join('&') + end + + def not_found_transaction_id_refund_response + %w( + response=3 + responsetext=Transaction+not+found+REFID:4054576 + authcode= + transactionid= + avsresponse= + cvvresponse= + orderid=1 + type=refund + response_code=300 + ).join('&') + end + + def invalid_transaction_id_refund_response + %w( + response=3 + responsetext=Invalid+Transaction+ID+/+Object+ID+specified:++REFID:4054567 + authcode= + transactionid= + avsresponse= + cvvresponse= + orderid=1 + type=refund + response_code=300 + ).join('&') + end + + def invalid_amount_refund_response + %w( + response=3 + responsetext=Refund+amount+may+not+exceed+the+transaction+balance+REFID:4054562 + authcode= + transactionid= + avsresponse= + cvvresponse= + orderid=1 + type=refund + response_code=300 + ).join('&') + end + + def approved_void_purchase_response + %w( + response=1 + responsetext=Transaction+Void+Successful + authcode=123456 + transactionid=1234567896 + avsresponse= + cvvresponse= + orderid=1 + type=void + response_code=100 + ).join('&') + end + + def approved_void_authorization_response + %w( + response=1 + responsetext=Transaction+Void+Successful + authcode=123456 + transactionid=1234567896 + avsresponse= + cvvresponse= + orderid=1 + type=void + response_code=100 + ).join('&') + end + + def invalid_transaction_id_void_response + %w( + response=3 + responsetext=Invalid+Transaction+ID+/+Object+ID+specified:++REFID:4054572 + authcode= + transactionid= + avsresponse= + cvvresponse= + orderid=1 + type=void + response_code=300 + ).join('&') + end + + def not_found_transaction_id_void_response + %w( + response=3 + responsetext=Transaction+not+found+REFID:4054582 + authcode= + transactionid= + avsresponse= + cvvresponse= + orderid=1 + type=void + response_code=300 + ).join('&') + end +end diff --git a/test/unit/gateways/pin_test.rb b/test/unit/gateways/pin_test.rb index c8edf25f776..975edc2db8e 100644 --- a/test/unit/gateways/pin_test.rb +++ b/test/unit/gateways/pin_test.rb @@ -202,7 +202,7 @@ def test_add_creditcard assert_equal @credit_card.month, post[:card][:expiry_month] assert_equal @credit_card.year, post[:card][:expiry_year] assert_equal @credit_card.verification_value, post[:card][:cvc] - assert_equal "#{@credit_card.first_name} #{@credit_card.last_name}", post[:card][:name] + assert_equal @credit_card.name, post[:card][:name] end def test_add_creditcard_with_card_token diff --git a/test/unit/gateways/secure_pay_au_test.rb b/test/unit/gateways/secure_pay_au_test.rb index 54b49c753ba..8684697067d 100644 --- a/test/unit/gateways/secure_pay_au_test.rb +++ b/test/unit/gateways/secure_pay_au_test.rb @@ -1,6 +1,8 @@ require 'test_helper' class SecurePayAuTest < Test::Unit::TestCase + include CommStub + def setup @gateway = SecurePayAuGateway.new( :login => 'login', @@ -48,6 +50,20 @@ def test_successful_purchase assert response.test? end + def test_localized_currency + stub_comms do + @gateway.purchase(100, @credit_card, @options.merge(:currency => 'CAD')) + end.check_request do |endpoint, data, headers| + assert_match /100<\/amount>/, data + end.respond_with(successful_purchase_response) + + stub_comms do + @gateway.purchase(100, @credit_card, @options.merge(:currency => 'JPY')) + end.check_request do |endpoint, data, headers| + assert_match /1<\/amount>/, data + end.respond_with(successful_purchase_response) + end + def test_failed_purchase @gateway.expects(:ssl_post).returns(failed_purchase_response) diff --git a/test/unit/gateways/so_easy_pay_test.rb b/test/unit/gateways/so_easy_pay_test.rb new file mode 100644 index 00000000000..daa8a9f9d35 --- /dev/null +++ b/test/unit/gateways/so_easy_pay_test.rb @@ -0,0 +1,225 @@ +require 'test_helper' + +class SoEasyPayTest < Test::Unit::TestCase + def setup + @gateway = SoEasyPayGateway.new( + :login => 'login', + :password => 'password' + ) + + @credit_card = credit_card + @amount = 100 + + @options = { + :order_id => '1', + :billing_address => address, + :description => 'Store Purchase' + } + end + + def test_successful_purchase + @gateway.expects(:ssl_post).returns(successful_purchase_response) + + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_instance_of Response, response + assert_success response + + # Replace with authorization number from the successful response + assert_equal '1708978', response.authorization + assert response.test? + end + + def test_unsuccessful_request + @gateway.expects(:ssl_post).returns(failed_purchase_response) + + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert_equal '1708979', response.authorization + assert response.test? + end + + def test_successful_authorize + @gateway.expects(:ssl_post).returns(successful_authorize_response) + + assert response = @gateway.authorize(@amount, @credit_card, @options) + assert_instance_of Response, response + assert_success response + + assert_equal('1708980', response.authorization) + assert response.test? + end + + def test_successful_capture + @gateway.expects(:ssl_post).returns(successful_capture_response) + + assert response = @gateway.capture(1111, "1708980") + assert_instance_of Response, response + assert_success response + + assert_equal('1708981', response.authorization) + assert response.test? + end + + def test_successful_refund + @gateway.expects(:ssl_post).returns(successful_credit_response) + assert response = @gateway.refund(@amount, '1708978') + assert_instance_of Response, response + assert_success response + assert_equal 'Transaction successful', response.message + end + + def test_failed_refund + @gateway.expects(:ssl_post).returns(failed_credit_response) + + assert response = @gateway.refund(@amount, '1708978') + assert_instance_of Response, response + assert_failure response + assert_equal 'Card declined', response.message + end + + def test_do_not_depend_on_expiry_date_class + @gateway.stubs(:ssl_post).returns(successful_purchase_response) + @credit_card.expects(:expiry_date).never + + @gateway.purchase(@amount, @credit_card, @options) + end + + def test_use_ducktyping_for_credit_card + @gateway.expects(:ssl_post).returns(successful_purchase_response) + + credit_card = stub(:number => '4242424242424242', :verification_value => '123', :name => "Hans Tester", :year => 2012, :month => 1) + + assert_nothing_raised do + assert_success @gateway.purchase(@amount, credit_card, @options) + end + end + + private + + # Place raw successful response from gateway here + def successful_purchase_response + %( + + + + + + + 1708978 + 12 + Authorized + 000 + Transaction successful + K + NOSCORE + 0000 + **21 + 02/16 + VISA + + + ) + end + + # Place raw failed response from gateway here + def failed_purchase_response + %( + + + + + + + 1708979 + 12 + Not Authorized + 002 + Card declined + K + NOSCORE + 0000 + **21 + 02/16 + VISA + + + ) + end + + def successful_authorize_response + %( + + + + + + + 1708980 + 12 + Authorized + 000 + Transaction successful + K + NOSCORE + 0000 + **21 + 02/16 + VISA + + + ) + end + + def successful_capture_response + %( + + + + + + + 1708981 + Authorized + 000 + Transaction successful + + + ) + end + + def successful_credit_response + %( + + + + + + + 1708982 + Authorized + 000 + Transaction successful + + + ) + end + + def failed_credit_response + %( + + + + + + + 1708983 + Not Authorized + 002 + Card declined + + + ) + end + +end + diff --git a/test/unit/gateways/spreedly_core_test.rb b/test/unit/gateways/spreedly_core_test.rb index c2c3572e290..3f3bbb091b1 100644 --- a/test/unit/gateways/spreedly_core_test.rb +++ b/test/unit/gateways/spreedly_core_test.rb @@ -75,6 +75,16 @@ def test_failed_purchase_with_credit_card assert_equal '0957', response.params['payment_method_last_four_digits'] end + def test_purchase_without_gateway_token_option + @gateway.expects(:commit).with("gateways/token/purchase.xml", anything) + @gateway.purchase(@amount, @payment_method_token) + end + + def test_purchase_with_gateway_token_option + @gateway.expects(:commit).with("gateways/mynewtoken/purchase.xml", anything) + @gateway.purchase(@amount, @payment_method_token, gateway_token: 'mynewtoken') + end + def test_successful_authorize_with_token_and_capture @gateway.expects(:raw_ssl_request).returns(successful_authorize_response) response = @gateway.authorize(@amount, @payment_method_token) diff --git a/test/unit/gateways/stripe_test.rb b/test/unit/gateways/stripe_test.rb index 03898d4b2f5..9ef3407819c 100644 --- a/test/unit/gateways/stripe_test.rb +++ b/test/unit/gateways/stripe_test.rb @@ -16,6 +16,42 @@ def setup } end + def test_successful_new_customer_with_card + @gateway.expects(:ssl_request).returns(successful_new_customer_response) + + assert response = @gateway.store(@credit_card, @options) + assert_instance_of Response, response + assert_success response + + assert_equal 'cus_3sgheFxeBgTQ3M', response.authorization + assert response.test? + end + + def test_successful_new_card + @gateway.expects(:ssl_request).returns(successful_new_card_response) + + assert response = @gateway.store(@credit_card, @options.merge(:customer => 'cus_3sgheFxeBgTQ3M')) + assert_instance_of MultiResponse, response + assert_success response + + assert_equal 'card_483etw4er9fg4vF3sQdrt3FG', response.authorization + assert response.test? + end + + def test_successful_new_default_card + @gateway.expects(:ssl_request).twice.returns(successful_new_card_response, successful_new_customer_response) + + assert response = @gateway.store(@credit_card, @options.merge(:customer => 'cus_3sgheFxeBgTQ3M', :set_default => true)) + assert_instance_of MultiResponse, response + assert_success response + + assert_equal 'card_483etw4er9fg4vF3sQdrt3FG', response.authorization + assert_equal 2, response.responses.size + assert_equal 'card_483etw4er9fg4vF3sQdrt3FG', response.responses[0].authorization + assert_equal 'cus_3sgheFxeBgTQ3M', response.responses[1].authorization + assert response.test? + end + def test_successful_authorization @gateway.expects(:ssl_request).returns(successful_authorization_response) @@ -47,6 +83,18 @@ def test_successful_purchase assert response.test? end + def test_successful_purchase_with_token + response = stub_comms(@gateway, :ssl_request) do + @gateway.purchase(@amount, "tok_xxx") + end.check_request do |method, endpoint, data, headers| + assert_match(/card=tok_xxx/, data) + end.respond_with(successful_purchase_response) + + assert response + assert_instance_of Response, response + assert_success response + end + def test_successful_void @gateway.expects(:ssl_request).returns(successful_purchase_response(true)) @@ -182,7 +230,7 @@ def test_add_creditcard_with_token_and_track_data def test_add_customer post = {} - @gateway.send(:add_customer, post, {:customer => "test_customer"}) + @gateway.send(:add_customer, post, @creditcard, {:customer => "test_customer"}) assert_equal "test_customer", post[:customer] end @@ -204,7 +252,7 @@ def test_application_fee_is_submitted_for_capture def test_client_data_submitted_with_purchase stub_comms(@gateway, :ssl_request) do - updated_options = @options.merge({:description => "a test customer",:browser_ip => "127.127.127.127", :user_agent => "some browser", :order_id => "42", :email => "foo@wonderfullyfakedomain.com", :referrer =>"http://www.shopify.com"}) + updated_options = @options.merge({:description => "a test customer",:ip => "127.127.127.127", :user_agent => "some browser", :order_id => "42", :email => "foo@wonderfullyfakedomain.com", :referrer =>"http://www.shopify.com"}) @gateway.purchase(@amount,@credit_card,updated_options) end.check_request do |method, endpoint, data, headers| assert_match(/description=a\+test\+customer/, data) @@ -213,6 +261,7 @@ def test_client_data_submitted_with_purchase assert_match(/external_id=42/, data) assert_match(/referrer=http\%3A\%2F\%2Fwww\.shopify\.com/, data) assert_match(/payment_user_agent=Stripe\%2Fv1\+ActiveMerchantBindings\%2F\d+\.\d+\.\d+/, data) + assert_match(/metadata\[email\]=foo\%40wonderfullyfakedomain\.com/, data) end.respond_with(successful_purchase_response) end @@ -245,6 +294,14 @@ def test_metadata_header @gateway.purchase(@amount, @credit_card, @options.merge(:ip => '1.1.1.1')) end + def test_optional_version_header + @gateway.expects(:ssl_request).once.with {|method, url, post, headers| + headers && headers['Stripe-Version'] == '2013-10-29' + }.returns(successful_purchase_response) + + @gateway.purchase(@amount, @credit_card, @options.merge(:version => '2013-10-29')) + end + def test_track_data_and_traditional_should_be_mutually_exclusive stub_comms(@gateway, :ssl_request) do @gateway.purchase(@amount, @credit_card, @options) @@ -270,8 +327,89 @@ def test_address_is_included_with_card_data end.respond_with(successful_purchase_response) end + def generate_options_should_allow_key + assert_equal({:key => '12345'}, generate_options({:key => '12345'})) + end + private + # Create new customer and set default credit card + def successful_new_customer_response + <<-RESPONSE +{ + "object": "customer", + "created": 1383137317, + "id": "cus_3sgheFxeBgTQ3M", + "livemode": false, + "description": null, + "email": null, + "delinquent": false, + "metadata": {}, + "subscription": null, + "discount": null, + "account_balance": 0, + "cards": + { + "object": "list", + "count": 1, + "url": "/v1/customers/cus_3sgheFxeBgTQ3M/cards", + "data": + [ + { + "id": "card_483etw4er9fg4vF3sQdrt3FG", + "object": "card", + "last4": "4242", + "type": "Visa", + "exp_month": 11, + "exp_year": 2020, + "fingerprint": "5dgRQ3dVRGaQWDFb", + "customer": "cus_3sgheFxeBgTQ3M", + "country": "US", + "name": "John Doe", + "address_line1": null, + "address_line2": null, + "address_city": null, + "address_state": null, + "address_zip": null, + "address_country": null, + "cvc_check": null, + "address_line1_check": null, + "address_zip_check": null + } + ] + }, + "default_card": "card_483etw4er9fg4vF3sQdrt3FG" +} + RESPONSE + end + + def successful_new_card_response + <<-RESPONSE +{ + "id": "card_483etw4er9fg4vF3sQdrt3FG", + "livemode": false, + "object": "card", + "last4": "4242", + "type": "Visa", + "exp_month": 11, + "exp_year": 2020, + "fingerprint": "5dgRQ3dVRGaQWDFb", + "customer": "cus_3sgheFxeBgTQ3M", + "country": "US", + "name": "John Doe", + "address_line1": null, + "address_line2": null, + "address_city": null, + "address_state": null, + "address_zip": null, + "address_country": null, + "cvc_check": null, + "address_line1_check": null, + "address_zip_check": null +} + RESPONSE + end + def successful_authorization_response <<-RESPONSE { diff --git a/test/unit/gateways/swipe_checkout_test.rb b/test/unit/gateways/swipe_checkout_test.rb new file mode 100644 index 00000000000..e6c4e0da63a --- /dev/null +++ b/test/unit/gateways/swipe_checkout_test.rb @@ -0,0 +1,154 @@ +require 'test_helper' + +class SwipeCheckoutTest < Test::Unit::TestCase + def setup + @gateway = SwipeCheckoutGateway.new( + login: '0000000000000', + api_key: '0000000000000000000000000000000000000000000000000000000000000000', + region: 'NZ' + ) + + @credit_card = credit_card + @amount = 100 + + @options = { + order_id: '1', + billing_address: address, + description: 'Store Purchase' + } + end + + def test_supported_countries + assert @gateway.supported_countries == ['NZ', 'CA'] + end + + def test_successful_purchase + @gateway.expects(:ssl_post).returns(successful_purchase_response) + + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_instance_of Response, response + assert_success response + assert_equal 'Transaction approved', response.message + assert response.test? + end + + def test_successful_test_purchase + @gateway.expects(:ssl_post).returns(successful_test_purchase_response) + + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_instance_of Response, response + + assert_success response + assert response.test? + end + + def test_unsuccessful_purchase + @gateway.expects(:ssl_post).returns(failed_purchase_response) + + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert response.test? + end + + def test_unsuccessful_test_purchase + @gateway.expects(:ssl_post).returns(failed_test_purchase_response) + + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert response.test? + end + + def test_unsuccessful_request_invalid_card + @gateway.expects(:ssl_post).returns(failed_purchase_response_invalid_card) + + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert response.test? + end + + def test_unsuccessful_request_system_error + @gateway.expects(:ssl_post).returns(failed_purchase_response_system_error) + + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert response.test? + end + + def test_unsuccessful_request_incorrect_amount + @gateway.expects(:ssl_post).returns(failed_purchase_response_incorrect_amount) + + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert response.test? + end + + def test_unsuccessful_request_access_denied + @gateway.expects(:ssl_post).returns(failed_purchase_response_access_denied) + + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert response.test? + end + + def test_unsuccessful_request_not_enough_parameters + @gateway.expects(:ssl_post).returns(failed_purchase_response_not_enough_parameters) + + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert response.test? + end + + def test_unsuccessful_request_invalid_json_in_response + @gateway.expects(:ssl_post).returns(response_with_invalid_json) + + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_failure response + assert response.test? + end + + private + + def successful_purchase_response + '{"response_code": 200, "message": "OK", "data": {"tx_transaction_id": "00000000000000", "result": "accepted"}}' + end + + def successful_test_purchase_response + '{"response_code": 200, "message": "OK", "data": {"tx_transaction_id": "00000000000000", "result": "test-accepted"}}' + end + + def failed_purchase_response + '{"response_code": 200, "message": "OK", "data": {"tx_transaction_id": "00000000000000", "result": "declined"}}' + end + + def failed_test_purchase_response + '{"response_code": 200, "message": "OK", "data": {"tx_transaction_id": "00000000000000", "result": "test-declined"}}' + end + + def failed_purchase_response_invalid_card + build_failed_response 303, 'Invalid card data' + end + + def failed_purchase_response_system_error + build_failed_response 402, 'System error' + end + + def failed_purchase_response_incorrect_amount + build_failed_response 302, 'Incorrect amount' + end + + def failed_purchase_response_access_denied + build_failed_response 400, 'System error' + end + + def failed_purchase_response_not_enough_parameters + build_failed_response 403, 'Not enough parameters' + end + + def response_with_invalid_json + '{"response_code": ' + end + + def build_failed_response(code, message) + "{\"response_code\": #{code}, \"message\": \"#{message}\"}" + end +end diff --git a/test/unit/gateways/usa_epay_transaction_test.rb b/test/unit/gateways/usa_epay_transaction_test.rb index 681682e8ea2..933c76c6602 100644 --- a/test/unit/gateways/usa_epay_transaction_test.rb +++ b/test/unit/gateways/usa_epay_transaction_test.rb @@ -4,17 +4,35 @@ class UsaEpayTransactionTest < Test::Unit::TestCase include CommStub def setup - @gateway = UsaEpayTransactionGateway.new( - :login => 'LOGIN' - ) + @gateway = UsaEpayTransactionGateway.new(:login => 'LOGIN') @credit_card = credit_card('4242424242424242') @options = { - :billing_address => address, + :billing_address => address, :shipping_address => address } @amount = 100 end + + def test_urls + assert_equal 'https://www.usaepay.com/gate', UsaEpayTransactionGateway.live_url + assert_equal 'https://sandbox.usaepay.com/gate', UsaEpayTransactionGateway.test_url + end + + def test_request_url_live + gateway = UsaEpayTransactionGateway.new(:login => 'LOGIN', :test => false) + gateway.expects(:ssl_post). + with('https://www.usaepay.com/gate', purchase_request). + returns(successful_purchase_response) + assert response = gateway.purchase(@amount, @credit_card, @options) + end + + def test_request_url_test + @gateway.expects(:ssl_post). + with('https://sandbox.usaepay.com/gate', purchase_request). + returns(successful_purchase_response) + assert response = @gateway.purchase(@amount, @credit_card, @options) + end def test_successful_request @gateway.expects(:ssl_post).returns(successful_purchase_response) @@ -42,6 +60,42 @@ def test_successful_purchase_passing_extra_info end.respond_with(successful_purchase_response) assert_success response end + + def test_successful_purchase_split_payment + response = stub_comms do + @gateway.purchase(@amount, @credit_card, @options.merge( + :split_payments => [ + { :key => 'abc123', :amount => 199, :description => 'Second payee' }, + { :key => 'def456', :amount => 911, :description => 'Third payee' }, + ] + )) + end.check_request do |endpoint, data, headers| + assert_match /UM02key=abc123/, data + assert_match /UM02amount=1.99/, data + assert_match /UM02description=Second\+payee/, data + + assert_match /UM03key=def456/, data + assert_match /UM03amount=9.11/, data + assert_match /UM03description=Third\+payee/, data + + assert_match /UMonError=Void/, data + end.respond_with(successful_purchase_response) + assert_success response + end + + def test_successful_purchase_split_payment_with_custom_on_error + response = stub_comms do + @gateway.purchase(@amount, @credit_card, @options.merge( + :split_payments => [ + { :key => 'abc123', :amount => 199, :description => 'Second payee' } + ], + :on_error => 'Continue' + )) + end.check_request do |endpoint, data, headers| + assert_match /UMonError=Continue/, data + end.respond_with(successful_purchase_response) + assert_success response + end def test_address_key_prefix assert_equal 'bill', @gateway.send(:address_key_prefix, :billing) @@ -115,20 +169,20 @@ def test_does_not_raise_error_on_missing_values end end - private +private def assert_address(type, post) prefix = key_prefix(type) - assert_equal @credit_card.first_name, post[key(prefix, 'fname')] - assert_equal @credit_card.last_name, post[key(prefix, 'lname')] - assert_equal @options[:billing_address][:company], post[key(prefix, 'company')] + assert_equal @credit_card.first_name, post[key(prefix, 'fname')] + assert_equal @credit_card.last_name, post[key(prefix, 'lname')] + assert_equal @options[:billing_address][:company], post[key(prefix, 'company')] assert_equal @options[:billing_address][:address1], post[key(prefix, 'street')] assert_equal @options[:billing_address][:address2], post[key(prefix, 'street2')] - assert_equal @options[:billing_address][:city], post[key(prefix, 'city')] - assert_equal @options[:billing_address][:state], post[key(prefix, 'state')] - assert_equal @options[:billing_address][:zip], post[key(prefix, 'zip')] - assert_equal @options[:billing_address][:country], post[key(prefix, 'country')] - assert_equal @options[:billing_address][:phone], post[key(prefix, 'phone')] + assert_equal @options[:billing_address][:city], post[key(prefix, 'city')] + assert_equal @options[:billing_address][:state], post[key(prefix, 'state')] + assert_equal @options[:billing_address][:zip], post[key(prefix, 'zip')] + assert_equal @options[:billing_address][:country], post[key(prefix, 'country')] + assert_equal @options[:billing_address][:phone], post[key(prefix, 'phone')] end def key_prefix(type) @@ -138,6 +192,10 @@ def key_prefix(type) def key(prefix, key) @gateway.send(:address_key, prefix, key) end + + def purchase_request + "UMamount=1.00&UMinvoice=&UMdescription=&UMcard=4242424242424242&UMcvv2=123&UMexpir=0914&UMname=Longbob+Longsen&UMbillfname=Longbob&UMbilllname=Longsen&UMbillcompany=Widgets+Inc&UMbillstreet=1234+My+Street&UMbillstreet2=Apt+1&UMbillcity=Ottawa&UMbillstate=ON&UMbillzip=K1C2N6&UMbillcountry=CA&UMbillphone=%28555%29555-5555&UMshipfname=Longbob&UMshiplname=Longsen&UMshipcompany=Widgets+Inc&UMshipstreet=1234+My+Street&UMshipstreet2=Apt+1&UMshipcity=Ottawa&UMshipstate=ON&UMshipzip=K1C2N6&UMshipcountry=CA&UMshipphone=%28555%29555-5555&UMstreet=1234+My+Street&UMzip=K1C2N6&UMcommand=cc%3Asale&UMkey=LOGIN&UMsoftware=Active+Merchant&UMtestmode=0" + end def successful_purchase_response "UMversion=2.9&UMstatus=Approved&UMauthCode=001716&UMrefNum=55074409&UMavsResult=Address%3A%20Match%20%26%205%20Digit%20Zip%3A%20Match&UMavsResultCode=Y&UMcvv2Result=Match&UMcvv2ResultCode=M&UMresult=A&UMvpasResultCode=&UMerror=Approved&UMerrorcode=00000&UMcustnum=&UMbatch=596&UMisDuplicate=N&UMconvertedAmount=&UMconvertedAmountCurrency=840&UMconversionRate=&UMcustReceiptResult=No%20Receipt%20Sent&UMfiller=filled" diff --git a/test/unit/gateways/webpay_test.rb b/test/unit/gateways/webpay_test.rb index 1e6d9406c9c..87a08584fe7 100644 --- a/test/unit/gateways/webpay_test.rb +++ b/test/unit/gateways/webpay_test.rb @@ -1,6 +1,8 @@ require 'test_helper' class WebpayTest < Test::Unit::TestCase + include CommStub + def setup @gateway = WebpayGateway.new(:login => 'login') @@ -14,6 +16,25 @@ def setup } end + def test_successful_authorization + @gateway.expects(:ssl_request).returns(successful_authorization_response) + + assert response = @gateway.authorize(@amount, @credit_card, @options) + assert_instance_of Response, response + assert_success response + + assert_equal 'ch_test_charge', response.authorization + assert response.test? + end + + def test_successful_capture + @gateway.expects(:ssl_request).returns(successful_capture_response) + + assert response = @gateway.capture(@amount, "ch_test_charge") + assert_success response + assert response.test? + end + def test_successful_purchase @gateway.expects(:ssl_request).returns(successful_purchase_response) @@ -26,7 +47,7 @@ def test_successful_purchase assert response.test? end - def test_appropiate_purchase_amount + def test_appropriate_purchase_amount @gateway.expects(:ssl_request).returns(successful_purchase_response) response = @gateway.purchase(@amount, @credit_card, @options) @@ -36,6 +57,17 @@ def test_appropiate_purchase_amount assert_equal @amount / 100, response.params["amount"] end + def test_successful_purchase_with_token + response = stub_comms(@gateway, :ssl_request) do + @gateway.purchase(@amount, "tok_xxx") + end.check_request do |method, endpoint, data, headers| + assert_match(/card=tok_xxx/, data) + end.respond_with(successful_purchase_response) + + assert response + assert_instance_of Response, response + assert_success response + end def test_successful_void @gateway.expects(:ssl_request).returns(successful_purchase_response(true)) @@ -88,13 +120,13 @@ def test_invalid_raw_response def test_add_customer post = {} - @gateway.send(:add_customer, post, {:customer => "test_customer"}) + @gateway.send(:add_customer, post, 'card_token', {:customer => "test_customer"}) assert_equal "test_customer", post[:customer] end def test_doesnt_add_customer_if_card - post = { :card => 'foo' } - @gateway.send(:add_customer, post, {:customer => "test_customer"}) + post = {} + @gateway.send(:add_customer, post, @credit_card, {:customer => "test_customer"}) assert !post[:customer] end @@ -134,6 +166,66 @@ def test_metadata_header private + def successful_authorization_response + <<-RESPONSE +{ + "id": "ch_test_charge", + "object": "charge", + "created": 1309131571, + "livemode": false, + "paid": true, + "amount": 40000, + "currency": "jpy", + "refunded": false, + "fee": 0, + "fee_details": [], + "card": { + "country": "JP", + "exp_month": 9, + "exp_year": #{Time.now.year + 1}, + "last4": "4242", + "object": "card", + "type": "Visa" + }, + "captured": false, + "description": "ActiveMerchant Test Purchase", + "dispute": null, + "uncaptured": true, + "disputed": false +} + RESPONSE + end + + def successful_capture_response + <<-RESPONSE +{ + "id": "ch_test_charge", + "object": "charge", + "created": 1309131571, + "livemode": false, + "paid": true, + "amount": 40000, + "currency": "jpy", + "refunded": false, + "fee": 0, + "fee_details": [], + "card": { + "country": "JP", + "exp_month": 9, + "exp_year": #{Time.now.year + 1}, + "last4": "4242", + "object": "card", + "type": "Visa" + }, + "captured": true, + "description": "ActiveMerchant Test Purchase", + "dispute": null, + "uncaptured": false, + "disputed": false +} + RESPONSE + end + # Place raw successful response from gateway here def successful_purchase_response(refunded=false) <<-RESPONSE diff --git a/test/unit/gateways/wirecard_test.rb b/test/unit/gateways/wirecard_test.rb index 6a1a75218ac..61c2883a9e3 100644 --- a/test/unit/gateways/wirecard_test.rb +++ b/test/unit/gateways/wirecard_test.rb @@ -77,6 +77,32 @@ def test_successful_authorization_and_capture assert response.message[/this is a demo/i] end + def test_successful_refund + @gateway.expects(:ssl_post).returns(successful_purchase_response) + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_success response + assert_equal TEST_PURCHASE_GUWID, response.authorization + + @gateway.expects(:ssl_post).returns(successful_refund_response) + assert response = @gateway.refund(@amount - 30, response.authorization, @options) + assert_success response + assert response.test? + assert_match /All good!/, response.message + end + + def test_successful_void + @gateway.expects(:ssl_post).returns(successful_purchase_response) + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_success response + assert_equal TEST_PURCHASE_GUWID, response.authorization + + @gateway.expects(:ssl_post).returns(successful_void_response) + assert response = @gateway.void(response.authorization, @options) + assert_success response + assert response.test? + assert_match /Nice one!/, response.message + end + def test_successful_authorization_and_partial_capture @gateway.expects(:ssl_post).returns(successful_authorization_response) assert response = @gateway.authorize(@amount, @credit_card, @options) @@ -99,6 +125,20 @@ def test_unauthorized_capture assert response.message["Could not find referenced transaction for GuWID 1234567890123456789012."] end + def test_failed_refund + @gateway.expects(:ssl_post).returns(failed_refund_response) + assert response = @gateway.refund(@amount - 30, "TheIdentifcation", @options) + assert_failure response + assert_match /Not prudent/, response.message + end + + def test_failed_void + @gateway.expects(:ssl_post).returns(failed_void_response) + assert response = @gateway.refund(@amount - 30, "TheIdentifcation", @options) + assert_failure response + assert_match /Not gonna do it/, response.message + end + def test_no_error_if_no_state_is_provided_in_address options = @options.merge(:billing_address => @address_without_state) @gateway.expects(:ssl_post).returns(unauthorized_capture_response) @@ -272,6 +312,123 @@ def successful_purchase_response XML end + def successful_refund_response + <<-XML + + + + + + + + + 2a486b3ab747df694d5460c3cb444591 + + C898842138247065382261 + 424492 + All good! + INFO + ACK + 2013-10-22 21:37:33 + + + + + + + XML + end + + def failed_refund_response + <<-XML + + + + + + + + + 98680cbeee81d32e94a2b71397ffdf88 + + C999187138247102291030 + + INFO + NOK + + DATA_ERROR + 20080 + Not prudent + + 2013-10-22 21:43:42 + + + + + + + XML + end + + def successful_void_response + <<-XML + + + + + + + + + 5f1a2ab3fb2ed7a6aaa0eea74dc109e2 + + C907807138247383379288 + 802187 + Nice one! + INFO + ACK + 2013-10-22 22:30:33 + + + + + + + XML + end + + def failed_void_response + <<-XML + + + + + + + + + c11154e9395cf03c49bd68ec5c7087cc + + C941776138247400010330 + + INFO + NOK + + DATA_ERROR + 20080 + Not gonna do it + + 2013-10-22 22:33:20 + + + + + + + XML + end + + # Purchase failure def wrong_creditcard_purchase_response <<-XML diff --git a/test/unit/integrations/bit_pay_module_test.rb b/test/unit/integrations/bit_pay_module_test.rb index 0596d60e6c0..8cd7f204788 100644 --- a/test/unit/integrations/bit_pay_module_test.rb +++ b/test/unit/integrations/bit_pay_module_test.rb @@ -4,6 +4,10 @@ class BitPayModuleTest < Test::Unit::TestCase include ActiveMerchant::Billing::Integrations def test_notification_method - assert_instance_of BitPay::Notification, BitPay.notification('name=cody') + assert_instance_of BitPay::Notification, BitPay.notification('{"name":"cody"}', {}) + end + + def test_return_method + assert_instance_of BitPay::Return, BitPay.return('{"name":"cody"}', {}) end end diff --git a/test/unit/integrations/helpers/bit_pay_helper_test.rb b/test/unit/integrations/helpers/bit_pay_helper_test.rb index 5f8ee5f5986..8942ca2d56a 100644 --- a/test/unit/integrations/helpers/bit_pay_helper_test.rb +++ b/test/unit/integrations/helpers/bit_pay_helper_test.rb @@ -4,14 +4,12 @@ class BitPayHelperTest < Test::Unit::TestCase include ActiveMerchant::Billing::Integrations def setup - BitPay::Helper.any_instance.expects(:generate_invoice_id).once.returns(nil) - @helper = BitPay::Helper.new(1234, 'cody@example.com', :authcode => "foobar", :amount => 500, :currency => 'USD') + @helper = BitPay::Helper.new(1234, 'cody@example.com', :amount => 500, :currency => 'USD') end def test_basic_helper_fields assert_field 'orderID', "1234" assert_field 'price', "500" - assert_field 'posData', 'foobar' assert_field 'currency', 'USD' end @@ -46,4 +44,10 @@ def test_setting_invalid_address_field @helper.billing_address :street => 'My Street' assert_equal fields, @helper.fields end + + def test_form_fields_uses_invoice_id + Net::HTTP.any_instance.expects(:request).returns(stub(:body => '{"id": "98kui1gJ7FocK41gUaBZxG"}')) + + assert_equal '98kui1gJ7FocK41gUaBZxG', @helper.form_fields['id'] + end end diff --git a/test/unit/integrations/helpers/ipay88_helper_test.rb b/test/unit/integrations/helpers/ipay88_helper_test.rb index 675ba816728..84b475505a6 100644 --- a/test/unit/integrations/helpers/ipay88_helper_test.rb +++ b/test/unit/integrations/helpers/ipay88_helper_test.rb @@ -95,9 +95,19 @@ def test_invalid_amount_as_negative_integer_in_cents end def test_sig_components_amount_doesnt_include_decimal_points - @helper.amount = 0.5 - assert_equal "abcipay88merchcodeorder-50005MYR", @helper.send(:sig_components) - @helper.amount = 12.34 + @helper.amount = 50 + assert_equal "abcipay88merchcodeorder-500050MYR", @helper.send(:sig_components) + @helper.amount = 1234 assert_equal "abcipay88merchcodeorder-5001234MYR", @helper.send(:sig_components) + @helper.amount = 1000 + assert_equal "abcipay88merchcodeorder-5001000MYR", @helper.send(:sig_components) + @helper.amount = Money.new(90) + assert_equal "abcipay88merchcodeorder-500090MYR", @helper.send(:sig_components) + @helper.amount = Money.new(1000) + assert_equal "abcipay88merchcodeorder-5001000MYR", @helper.send(:sig_components) + end + + def test_sign_method + assert_equal "rq3VxZp9cjkiqiw4mHnZJH49MzQ=", Ipay88::Helper.sign("L3mn6Bpy4HM0605613619416109MYR") end end diff --git a/test/unit/integrations/helpers/payu_in_helper_test.rb b/test/unit/integrations/helpers/payu_in_helper_test.rb index 20d7f368319..2d210a59440 100644 --- a/test/unit/integrations/helpers/payu_in_helper_test.rb +++ b/test/unit/integrations/helpers/payu_in_helper_test.rb @@ -4,14 +4,13 @@ class PayuInHelperTest < Test::Unit::TestCase include ActiveMerchant::Billing::Integrations def setup - @helper = PayuIn::Helper.new( 'jh34h53kj4h5hj34kh5', 'C0Dr8m', :amount => '10.00', :credential2 => 'Product Info') + @helper = PayuIn::Helper.new( 'order_id', 'merchant_id', :amount => '10.00', :credential2 => 'secret_key') end def test_basic_helper_fields assert_equal '10.00', @helper.fields['amount'] - assert_equal 'C0Dr8m', @helper.fields['key'] - assert_equal 'jh34h53kj4h5hj34kh5', @helper.fields['txnid'] - assert_equal 'Product Info', @helper.fields['productinfo'] + assert_equal 'merchant_id', @helper.fields['key'] + assert_equal 'order_id', @helper.fields['txnid'] end def test_customer_fields @@ -56,11 +55,20 @@ def test_user_defined_fields end def test_add_checksum_method - options = { :mode => 'CC' } @helper.customer :first_name => 'Payu-Admin', :email => 'test@example.com' + @helper.description "Product Info" @helper.user_defined :var1 => 'var_one', :var2 => 'var_two', :var3 => 'var_three', :var4 => 'var_four', :var5 => 'var_five', :var6 => 'var_six', :var7 => 'var_seven', :var8 => 'var_eight', :var9 => 'var_nine', :var10 => 'var_ten' - assert_equal "032606d7fb5cfe357d9e6b358b4bb8db1d34e9dfa30f039cb7dec75ae6d77f7d1f67a58c123ea0ee358bf040554d5e3048066a369ae63888132e27c14e79ee5a", @helper.form_fields["hash"] + fields = ["txnid", "amount", "productinfo", "firstname", "email", "udf1", "udf2", "udf3", "udf4", "udf5", "udf6", "udf7", "udf8", "udf9", "udf10"].map { |field| @helper.fields[field] } + assert_equal Digest::SHA512.hexdigest(['merchant_id', *fields, 'secret_key'].join("|")), @helper.form_fields["hash"] + end + + def test_sanitize_fields_in_form_fields + @helper.description '{[Valid Description!]}' + @helper.form_fields + + assert_equal 'Valid Description', @helper.fields['productinfo'] + assert_nil @helper.fields['email'] end end diff --git a/test/unit/integrations/helpers/payu_in_paisa_helper_test.rb b/test/unit/integrations/helpers/payu_in_paisa_helper_test.rb index b4e9a2c2e5f..31e6e4ae4fd 100644 --- a/test/unit/integrations/helpers/payu_in_paisa_helper_test.rb +++ b/test/unit/integrations/helpers/payu_in_paisa_helper_test.rb @@ -4,14 +4,13 @@ class PayuInPaisaHelperTest < Test::Unit::TestCase include ActiveMerchant::Billing::Integrations def setup - @helper = PayuInPaisa::Helper.new( 'jh34h53kj4h5hj34kh5', 'C0Dr8m', :amount => '10.00', :credential2 => 'Product Info') + @helper = PayuInPaisa::Helper.new( 'order_id', 'merchant_id', :amount => '10.00', :credential2 => 'secret') end def test_basic_helper_fields assert_equal '10.00', @helper.fields['amount'] - assert_equal 'C0Dr8m', @helper.fields['key'] - assert_equal 'jh34h53kj4h5hj34kh5', @helper.fields['txnid'] - assert_equal 'Product Info', @helper.fields['productinfo'] + assert_equal 'merchant_id', @helper.fields['key'] + assert_equal 'order_id', @helper.fields['txnid'] end def test_customer_fields @@ -56,10 +55,12 @@ def test_user_defined_fields end def test_add_checksum_method - options = { :mode => 'CC' } @helper.customer :first_name => 'Payu-Admin', :email => 'test@example.com' @helper.user_defined :var1 => 'var_one', :var2 => 'var_two', :var3 => 'var_three', :var4 => 'var_four', :var5 => 'var_five', :var6 => 'var_six', :var7 => 'var_seven', :var8 => 'var_eight', :var9 => 'var_nine', :var10 => 'var_ten' + @helper.description 'Product Info' - assert_equal "032606d7fb5cfe357d9e6b358b4bb8db1d34e9dfa30f039cb7dec75ae6d77f7d1f67a58c123ea0ee358bf040554d5e3048066a369ae63888132e27c14e79ee5a", @helper.form_fields["hash"] + payload = 'merchant_id|order_id|10.00|Product Info|Payu-Admin|test@example.com|var_one|var_two|var_three|var_four|var_five|var_six|var_seven|var_eight|var_nine|var_ten|secret' + checksum = Digest::SHA512.hexdigest(payload) + assert_equal checksum, @helper.form_fields["hash"] end end diff --git a/test/unit/integrations/helpers/valitor_helper_test.rb b/test/unit/integrations/helpers/valitor_helper_test.rb index 64519cb9470..13a4b1e73cc 100644 --- a/test/unit/integrations/helpers/valitor_helper_test.rb +++ b/test/unit/integrations/helpers/valitor_helper_test.rb @@ -20,7 +20,7 @@ def test_basic_helper_fields assert_field 'Tilvisunarnumer', 'order-500' assert_field 'Gjaldmidill', 'USD' - assert_equal Digest::MD5.hexdigest(['123', '0', '1', '1000', '0', 'cody@example.com', 'order-500', 'USD'].join('')), + assert_equal Digest::MD5.hexdigest(['123', '0', '1', '1000.00', '0', 'cody@example.com', 'order-500', 'USD'].join('')), @helper.form_fields['RafraenUndirskrift'] end @@ -30,18 +30,18 @@ def test_products assert_field 'Vara_1_Lysing', 'one' assert_field 'Vara_1_Fjoldi', '2' - assert_field 'Vara_1_Verd', '100' + assert_field 'Vara_1_Verd', '100.00' assert_field 'Vara_1_Afslattur', '50' assert_field 'Vara_2_Lysing', 'two' assert_field 'Vara_2_Fjoldi', '1' - assert_field 'Vara_2_Verd', '200' + assert_field 'Vara_2_Verd', '200.00' assert_field 'Vara_2_Afslattur', '0' assert_equal Digest::MD5.hexdigest( ['123', '0', - '2', '100', '50', - '1', '200', '0', + '2', '100.00', '50', + '1', '200.00', '0', 'cody@example.com', 'order-500', 'USD'].join('')), @helper.form_fields['RafraenUndirskrift'] end @@ -100,7 +100,7 @@ def test_urls assert_equal Digest::MD5.hexdigest( ['123', '0', - '1', '1000', '0', + '1', '1000.00', '0', 'cody@example.com', 'order-500', 'http://example.com/return', 'http://example.com/notify', 'USD'].join('')), @helper.form_fields['RafraenUndirskrift'] end @@ -127,9 +127,15 @@ def test_misc_mappings assert_field 'Lang', 'en' end - def test_amount_gets_sent_without_decimals + def test_amount_gets_sent_without_decimals_for_non_decimal_currencies @helper = Valitor::Helper.new('order-500', 'cody@example.com', :currency => 'ISK', :credential2 => '123', :amount => 115.10) @helper.form_fields assert_field "Vara_1_Verd", '115' end + + def test_amount_gets_sent_with_decimals_for_decimal_currencies + @helper = Valitor::Helper.new('order-500', 'cody@example.com', :currency => 'USD', :credential2 => '123', :amount => 115.10) + @helper.form_fields + assert_field "Vara_1_Verd", '115.10' + end end diff --git a/test/unit/integrations/helpers/wirecard_checkout_page_helper_test.rb b/test/unit/integrations/helpers/wirecard_checkout_page_helper_test.rb new file mode 100644 index 00000000000..b90e56ef542 --- /dev/null +++ b/test/unit/integrations/helpers/wirecard_checkout_page_helper_test.rb @@ -0,0 +1,89 @@ +require 'test_helper' + +class WirecardCheckoutPageHelperTest < Test::Unit::TestCase + include ActiveMerchant::Billing::Integrations + + def setup + @options = fixtures(:wirecard_checkout_page) + @helper = WirecardCheckoutPage::Helper.new('13', 'D200001', @options) + + @helper.max_retries = 3 + @helper.auto_deposit = true + @helper.add_version('Some Shopsystem', '0.0.1') + + @helper.language 'de' + @helper.description 'Order Number 13' + @helper.shop_service_url 'http://www.example.com/imprint' + @helper.notify_url "https://www.example.com/payment/confirm" + @helper.return_url "http://www.example.com/payment/return" + @helper.cancel_return_url "http://www.example.com/payment/return" + @helper.pending_url "http://www.example.com/payment/return" + @helper.failure_url "http://www.example.com/payment/return" + end + + def test_basic_helper_fields + assert_field 'language', 'de' + assert_field 'orderDescription', 'Order Number 13' + assert_field 'serviceUrl', 'http://www.example.com/imprint' + assert_field 'autoDeposit', "true" + assert_field 'confirmUrl', "https://www.example.com/payment/confirm" + assert_field 'successUrl', "http://www.example.com/payment/return" + assert_field 'cancelUrl', "http://www.example.com/payment/return" + assert_field 'pendingUrl', "http://www.example.com/payment/return" + assert_field 'failureUrl', "http://www.example.com/payment/return" + assert_field 'maxRetries', "3" + assert @helper.secret == 'B8AKTPWBRMNBV455FG6M2DANE99WU2' + assert @helper.customer_id == 'D200001' + assert @helper.shop_id == '' + end + + def test_customer_fields + @helper.customer :first_name => 'Sepp', + :last_name => 'Maier', + :ipaddress => '127.0.0.1', + :user_agent => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0', + :email => 'foo@bar.com' + + assert_field 'consumerBillingFirstName', 'Sepp' + assert_field 'consumerBillingLastName', 'Maier' + assert_field 'consumerIpAddress', '127.0.0.1' + assert_field 'consumerUserAgent', 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0' + assert_field 'consumerEmail', 'foo@bar.com' + end + + def test_address_mapping + @helper.billing_address(:address1 => 'Daham 99', + :zip => '8010', + :city => 'Graz', + :state => 'Steiermark', + :country => 'Austria') + + assert_field 'consumerBillingAddress1', 'Daham 99' + assert_field 'consumerBillingZipCode', '8010' + assert_field 'consumerBillingCity', 'Graz' + assert_field 'consumerBillingState', 'Steiermark' + assert_field 'consumerBillingCountry', 'AT' + + @helper.shipping_address(:first_name => 'Arnold', + :last_name => 'Schwarzenegger', + :address1 => 'Broadway 128', + :city => 'Los Angeles', + :state => 'NY', + :country => 'USA', + :zip => '10890', + :phone => '192634520', + :fax => '1926345202') + + assert_field 'consumerShippingFirstName', 'Arnold' + assert_field 'consumerShippingLastName', 'Schwarzenegger' + assert_field 'consumerShippingAddress1', 'Broadway 128' + assert_field 'consumerShippingZipCode', '10890' + assert_field 'consumerShippingCity', 'Los Angeles' + assert_field 'consumerShippingState', 'NY' + assert_field 'consumerShippingCountry', 'US' + assert_field 'consumerShippingPhone', '192634520' + assert_field 'consumerShippingFax', '1926345202' + + end + +end diff --git a/test/unit/integrations/ipay88_module_test.rb b/test/unit/integrations/ipay88_module_test.rb index 3125714060c..005ad4792f1 100644 --- a/test/unit/integrations/ipay88_module_test.rb +++ b/test/unit/integrations/ipay88_module_test.rb @@ -10,4 +10,8 @@ def test_return_method def test_service_url assert_equal "https://www.mobile88.com/epayment/entry.asp", Ipay88.service_url end + + def test_requery_url + assert_equal "https://www.mobile88.com/epayment/enquiry.asp", Ipay88.requery_url + end end diff --git a/test/unit/integrations/notifications/bit_pay_notification_test.rb b/test/unit/integrations/notifications/bit_pay_notification_test.rb index 076c83eb8ca..5f55c145570 100644 --- a/test/unit/integrations/notifications/bit_pay_notification_test.rb +++ b/test/unit/integrations/notifications/bit_pay_notification_test.rb @@ -9,23 +9,61 @@ def setup def test_accessors assert @bit_pay.complete? - assert_equal "complete", @bit_pay.status - assert_equal "w1GRw1q86WPSUlT1r2cGsCZffrUM-KqT9fMFnbC9jo=", @bit_pay.transaction_id - assert_equal 0.0083, @bit_pay.btcPrice + assert_equal "Completed", @bit_pay.status + assert_equal "98kui1gJ7FocK41gUaBZxG", @bit_pay.transaction_id + assert_equal 10.00, @bit_pay.gross assert_equal "USD", @bit_pay.currency assert_equal 1370539476654, @bit_pay.received_at + assert_equal 123, @bit_pay.item_id + end + + def test_invalid_data + hash = JSON.parse(http_raw_data) + @bit_pay = BitPay::Notification.new('{"invalid":json}') + + assert @bit_pay.params.empty? + end + + def test_item_id_invalid_json + hash = JSON.parse(http_raw_data) + @bit_pay = BitPay::Notification.new(hash.merge('posData' => 'Invalid JSON').to_json) + + assert_nil @bit_pay.item_id end def test_compositions - assert_equal "1", @bit_pay.amount + assert_equal Money.new(1000, 'USD'), @bit_pay.amount + end + + def test_successful_acknowledgement + Net::HTTP.any_instance.expects(:request).returns(stub(:body => http_raw_data)) + assert @bit_pay.acknowledge + end + + def test_acknowledgement_error + Net::HTTP.any_instance.expects(:request).returns(stub(:body => '{"error":"Doesnt match"}')) + assert !@bit_pay.acknowledge end - def test_acknowledgement - assert @bit_pay.acknowledge('foobar') + def test_acknowledgement_invalid_json + Net::HTTP.any_instance.expects(:request).returns(stub(:body => '{invalid json')) + assert !@bit_pay.acknowledge end private def http_raw_data - "id=w1GRw1q86WPSUlT1r2cGsCZffrUM-KqT9fMFnbC9jo=&orderID=123&url=https://bitpay.com/invoice/w1GRw1q86WPSUlT1r2cGsCZffrUM-KqT9fMFnbC9jo=&status=complete&btcPrice=0.0083&price=1¤cy=USD&invoiceTime=1370539476654&expirationTime=1370540376654¤tTime=1370539573956&posData=foobar" + { + "id"=>"98kui1gJ7FocK41gUaBZxG", + "orderID"=>"123", + "url"=>"https://bitpay.com/invoice/98kui1gJ7FocK41gUaBZxG", + "status"=>"complete", + "btcPrice"=>"0.0295", + "price"=>"10.00", + "currency"=>"USD", + "invoiceTime"=>"1370539476654", + "expirationTime"=>"1370540376654", + "currentTime"=>"1370539573956", + "posData" => '{"orderId":123}' + }.to_json end end diff --git a/test/unit/integrations/notifications/ipay88_notification_test.rb b/test/unit/integrations/notifications/ipay88_notification_test.rb new file mode 100644 index 00000000000..80b89bfc78f --- /dev/null +++ b/test/unit/integrations/notifications/ipay88_notification_test.rb @@ -0,0 +1,107 @@ +require 'test_helper' + +class Ipay88NotificationTest < Test::Unit::TestCase + include ActiveMerchant::Billing::Integrations + + def setup + @ipay88 = build_notification(http_raw_data) + end + + def test_accessors + assert_equal "ipay88merchcode", @ipay88.account + assert_equal 6, @ipay88.payment + assert_equal "order-500", @ipay88.item_id + assert_equal "5.00", @ipay88.gross + assert_equal "MYR", @ipay88.currency + assert_equal "Remarkable", @ipay88.remark + assert_equal "12345", @ipay88.transaction_id + assert_equal "auth123", @ipay88.auth_code + assert_equal "Completed", @ipay88.status + assert_equal "Invalid merchant", @ipay88.error + assert_equal "bPlMszCBwxlfGX9ZkgmSfT+OeLQ=", @ipay88.signature + end + + def test_secure_request + assert @ipay88.secure? + end + + def test_success + assert @ipay88.success? + end + + def test_insecure_request + assert !build_notification(http_raw_data(:invalid_sig)).secure? + end + + def test_acknowledge + params = parameterize(payload) + @ipay88.expects(:ssl_post).with(Ipay88.requery_url, params, + { "Content-Length" => params.size.to_s, "User-Agent" => "Active Merchant -- http://activemerchant.org" } + ).returns("00") + + assert @ipay88.acknowledge + end + + def test_unsuccessful_acknowledge_due_to_signature + ipay = build_notification(http_raw_data(:invalid_sig)) + assert !ipay.acknowledge + end + + def test_unsuccessful_acknowledge_due_to_requery + params = parameterize(payload) + @ipay88.expects(:ssl_post).with(Ipay88.requery_url, params, + { "Content-Length" => params.size.to_s, "User-Agent" => "Active Merchant -- http://activemerchant.org" } + ).returns("Invalid parameters") + assert !@ipay88.acknowledge + end + + def test_unsuccessful_acknowledge_due_to_payment_failed + params = parameterize(payload) + ipay = build_notification(http_raw_data(:payment_failed)) + assert !ipay.acknowledge + end + + def test_unsuccessful_acknowledge_due_to_missing_amount + ipay = build_notification(http_raw_data(:missing_amount)) + assert !ipay.acknowledge + end + + private + def http_raw_data(mode=:success) + base = { "MerchantCode" => "ipay88merchcode", + "PaymentId" => 6, + "RefNo" => "order-500", + "Amount" => "5.00", + "Currency" => "MYR", + "Remark" => "Remarkable", + "TransId" => "12345", + "AuthCode" => "auth123", + "Status" => 1, + "ErrDesc" => "Invalid merchant" } + + case mode + when :success + parameterize(base.merge("Signature" => "bPlMszCBwxlfGX9ZkgmSfT+OeLQ=")) + when :invalid_sig + parameterize(base.merge("Signature" => "hacked")) + when :payment_failed + parameterize(base.merge("Status" => 0, "Signature" => "p8nXYcl/wytpNMzsf31O/iu/2EU=")) + when :missing_amount + parameterize(base.except("Amount")) + else + "" + end + end + + def payload + { "MerchantCode" => "ipay88merchcode", "RefNo" => "order-500", "Amount" => "5.00" } + end + + def parameterize(params) + params.reject{|k, v| v.blank?}.keys.sort.collect { |key| "#{key}=#{CGI.escape(params[key].to_s)}" }.join("&") + end + + def build_notification(data) + Ipay88::Notification.new(data, :credential2 => "apple") + end +end diff --git a/test/unit/integrations/notifications/notification_test.rb b/test/unit/integrations/notifications/notification_test.rb index 0e9d4a759ed..671a5e5da9e 100644 --- a/test/unit/integrations/notifications/notification_test.rb +++ b/test/unit/integrations/notifications/notification_test.rb @@ -16,6 +16,8 @@ def test_parse assert_equal "confirmed", @notification.params['address_status'] assert_equal "EVMXCLDZJV77Q", @notification.params['payer_id'] assert_equal "Completed", @notification.params['payment_status'] + assert_equal "ru", @notification.params['yamoney-lang'] + assert_equal '', @notification.params['empty_params'] assert_equal CGI.unescape("15%3A23%3A54+Apr+15%2C+2005+PDT"), @notification.params['payment_date'] end @@ -45,10 +47,10 @@ def test_valid_sender_always_true_when_no_ips private def http_raw_data - "mc_gross=500.00&address_status=confirmed&payer_id=EVMXCLDZJV77Q&tax=0.00&address_street=164+Waverley+Street&payment_date=15%3A23%3A54+Apr+15%2C+2005+PDT&payment_status=Completed&address_zip=K2P0V6&first_name=Tobias&mc_fee=15.05&address_country_code=CA&address_name=Tobias+Luetke¬ify_version=1.7&custom=&payer_status=unverified&business=tobi%40leetsoft.com&address_country=Canada&address_city=Ottawa&quantity=1&payer_email=tobi%40snowdevil.ca&verify_sign=AEt48rmhLYtkZ9VzOGAtwL7rTGxUAoLNsuf7UewmX7UGvcyC3wfUmzJP&txn_id=6G996328CK404320L&payment_type=instant&last_name=Luetke&address_state=Ontario&receiver_email=tobi%40leetsoft.com&payment_fee=&receiver_id=UQ8PDYXJZQD9Y&txn_type=web_accept&item_name=Store+Purchase&mc_currency=CAD&item_number=&test_ipn=1&payment_gross=&shipping=0.00" + "mc_gross=500.00&address_status=confirmed&payer_id=EVMXCLDZJV77Q&tax=0.00&address_street=164+Waverley+Street&payment_date=15%3A23%3A54+Apr+15%2C+2005+PDT&payment_status=Completed&address_zip=K2P0V6&first_name=Tobias&mc_fee=15.05&address_country_code=CA&address_name=Tobias+Luetke¬ify_version=1.7&custom=&payer_status=unverified&business=tobi%40leetsoft.com&address_country=Canada&address_city=Ottawa&quantity=1&payer_email=tobi%40snowdevil.ca&verify_sign=AEt48rmhLYtkZ9VzOGAtwL7rTGxUAoLNsuf7UewmX7UGvcyC3wfUmzJP&txn_id=6G996328CK404320L&payment_type=instant&last_name=Luetke&address_state=Ontario&receiver_email=tobi%40leetsoft.com&payment_fee=&receiver_id=UQ8PDYXJZQD9Y&txn_type=web_accept&item_name=Store+Purchase&mc_currency=CAD&item_number=&test_ipn=1&payment_gross=&shipping=0.00&yamoney-lang=ru&empty_params=" end def http_raw_data_with_period - "mc_gross=500.00&address_status=confirmed&payer_id=EVMXCLDZJV77Q&tax=0.00&address_street=164+Waverley+Street&payment_date=15%3A23%3A54+Apr+15%2C+2005+PDT&payment_status=Completed&address_zip=K2P0V6&first_name=Tobias&mc_fee=15.05&address_country_code=CA&address_name=Tobias+Luetke¬ify_version=1.7&custom=&payer_status=unverified&business=tobi%40leetsoft.com&address_country=Canada&address_city=Ottawa&quantity=1&payer_email=tobi%40snowdevil.ca&verify_sign=AEt48rmhLYtkZ9VzOGAtwL7rTGxUAoLNsuf7UewmX7UGvcyC3wfUmzJP&txn_id=6G996328CK404320L&payment_type=instant&last_name=Luetke&address_state=Ontario&receiver_email=tobi%40leetsoft.com&payment_fee=&receiver_id=UQ8PDYXJZQD9Y&txn_type=web_accept&item_name=Store+Purchase&mc_currency=CAD&item_number=&test_ipn=1&payment_gross=&shipping=0.00&checkout.x=clicked" + "mc_gross=500.00&address_status=confirmed&payer_id=EVMXCLDZJV77Q&tax=0.00&address_street=164+Waverley+Street&payment_date=15%3A23%3A54+Apr+15%2C+2005+PDT&payment_status=Completed&address_zip=K2P0V6&first_name=Tobias&mc_fee=15.05&address_country_code=CA&address_name=Tobias+Luetke¬ify_version=1.7&custom=&payer_status=unverified&business=tobi%40leetsoft.com&address_country=Canada&address_city=Ottawa&quantity=1&payer_email=tobi%40snowdevil.ca&verify_sign=AEt48rmhLYtkZ9VzOGAtwL7rTGxUAoLNsuf7UewmX7UGvcyC3wfUmzJP&txn_id=6G996328CK404320L&payment_type=instant&last_name=Luetke&address_state=Ontario&receiver_email=tobi%40leetsoft.com&payment_fee=&receiver_id=UQ8PDYXJZQD9Y&txn_type=web_accept&item_name=Store+Purchase&mc_currency=CAD&item_number=&test_ipn=1&payment_gross=&shipping=0.00&checkout.x=clicked&yamoney-lang=ru&empty_params=" end end diff --git a/test/unit/integrations/notifications/payu_in_notification_test.rb b/test/unit/integrations/notifications/payu_in_notification_test.rb index 7ca50e350e1..94f1a6772ce 100644 --- a/test/unit/integrations/notifications/payu_in_notification_test.rb +++ b/test/unit/integrations/notifications/payu_in_notification_test.rb @@ -4,7 +4,7 @@ class PayuInNotificationTest < Test::Unit::TestCase include ActiveMerchant::Billing::Integrations def setup - @payu = PayuIn::Notification.new(http_raw_data, :credential1 => 'C0Dr8m', :credential2 => '3sf0jURk') + @payu = PayuIn::Notification.new(http_raw_data, :credential1 => 'merchant_id', :credential2 => 'secret') end def test_accessors @@ -19,13 +19,13 @@ def test_accessors assert_equal true, @payu.amount_ok?(BigDecimal.new('10.00'),BigDecimal.new('0.00')) assert_equal "CC", @payu.type assert_equal "4ba4afe87f7e73468f2a", @payu.invoice - assert_equal "C0Dr8m", @payu.account + assert_equal "merchant_id", @payu.account assert_equal "0.00", @payu.discount assert_equal "test@example.com", @payu.customer_email assert_equal "1234567890", @payu.customer_phone assert_equal "Payu-Admin", @payu.customer_first_name assert_equal "", @payu.customer_last_name - assert_equal "ef0c1b509a42b802a4938c25dc9bb9efe40b75a7dfb8bde1a6f126fa1f86cee264c5e5a17e87db85150d6d8912eafda838416e669712f1989dcb9cbdb8c24219", @payu.checksum + assert_equal "d6a5544072d036dc422d1c6393a8da75233d5e30ffc848f11682f121d67cd80c0d4fed1067b99918b5a377b7dcf1c8c9c79975abdf9f444692b35bf34d494105", @payu.checksum assert_equal "E000", @payu.message assert_equal true, @payu.checksum_ok? end @@ -38,12 +38,8 @@ def test_acknowledgement assert @payu.acknowledge end - def test_respond_to_acknowledge - assert @payu.respond_to?(:acknowledge) - end - private def http_raw_data - "mihpayid=403993715508030204&mode=CC&status=success&unmappedstatus=captured&key=C0Dr8m&txnid=4ba4afe87f7e73468f2a&amount=10.00&discount=0.00&addedon=2013-05-10 18 32 30&productinfo=Product Info&firstname=Payu-Admin&lastname=&address1=&address2=&city=&state=&country=&zipcode=&email=test@example.com&phone=1234567890&udf1=&udf2=&udf3=&udf4=&udf5=&udf6=&udf7=&udf8=&udf9=&udf10=&hash=ef0c1b509a42b802a4938c25dc9bb9efe40b75a7dfb8bde1a6f126fa1f86cee264c5e5a17e87db85150d6d8912eafda838416e669712f1989dcb9cbdb8c24219&field1=313069903923&field2=999999&field3=59117331831301&field4=-1&field5=&field6=&field7=&field8=&PG_TYPE=HDFC&bank_ref_num=59117331831301&bankcode=CC&error=E000&cardnum=512345XXXXXX2346&cardhash=766f0227cc4b4c5f773a04cb31d8d1c5be071dd8d08fe365ecf5e2e5c947546d" + "mihpayid=403993715508030204&mode=CC&status=success&unmappedstatus=captured&key=merchant_id&txnid=4ba4afe87f7e73468f2a&amount=10.00&discount=0.00&addedon=2013-05-10 18 32 30&productinfo=Product Info&firstname=Payu-Admin&lastname=&address1=&address2=&city=&state=&country=&zipcode=&email=test@example.com&phone=1234567890&udf1=&udf2=&udf3=&udf4=&udf5=&udf6=&udf7=&udf8=&udf9=&udf10=&hash=d6a5544072d036dc422d1c6393a8da75233d5e30ffc848f11682f121d67cd80c0d4fed1067b99918b5a377b7dcf1c8c9c79975abdf9f444692b35bf34d494105&field1=313069903923&field2=999999&field3=59117331831301&field4=-1&field5=&field6=&field7=&field8=&PG_TYPE=HDFC&bank_ref_num=59117331831301&bankcode=CC&error=E000&cardnum=512345XXXXXX2346&cardhash=766f0227cc4b4c5f773a04cb31d8d1c5be071dd8d08fe365ecf5e2e5c947546d" end end diff --git a/test/unit/integrations/notifications/payu_in_paisa_notification_test.rb b/test/unit/integrations/notifications/payu_in_paisa_notification_test.rb index 19601829da5..40b749986ce 100644 --- a/test/unit/integrations/notifications/payu_in_paisa_notification_test.rb +++ b/test/unit/integrations/notifications/payu_in_paisa_notification_test.rb @@ -4,7 +4,7 @@ class PayuInPaisaNotificationTest < Test::Unit::TestCase include ActiveMerchant::Billing::Integrations def setup - @payu = PayuInPaisa::Notification.new(http_raw_data, :credential1 => 'C0Dr8m', :credential2 => '3sf0jURk') + @payu = PayuInPaisa::Notification.new(http_raw_data, :credential1 => 'merchant_id', :credential2 => 'secret') end def test_accessors @@ -19,13 +19,13 @@ def test_accessors assert_equal true, @payu.amount_ok?(BigDecimal.new('10.00'),BigDecimal.new('0.00')) assert_equal "CC", @payu.type assert_equal "4ba4afe87f7e73468f2a", @payu.invoice - assert_equal "C0Dr8m", @payu.account + assert_equal "merchant_id", @payu.account assert_equal "0.00", @payu.discount assert_equal "test@example.com", @payu.customer_email assert_equal "1234567890", @payu.customer_phone assert_equal "Payu-Admin", @payu.customer_first_name assert_equal "", @payu.customer_last_name - assert_equal "e35f67dc7232d12caa28b16ba31b509f62bdea1e930bb6766a4f71036cc1af34debb8afc0fdd89be50f0604c1e6bca7209dfffe6b3a893c575492edcab3444ee", @payu.checksum + assert_equal checksum, @payu.checksum assert_equal "E000", @payu.message assert_equal true, @payu.checksum_ok? end @@ -38,16 +38,16 @@ def test_acknowledgement assert @payu.acknowledge end - def test_respond_to_acknowledge - assert @payu.respond_to?(:acknowledge) - end - def test_item_id_gives_the_original_item_id assert 'original_item_id', @payu.item_id end private def http_raw_data - "mihpayid=403993715508030204&mode=CC&status=success&unmappedstatus=captured&key=C0Dr8m&txnid=4ba4afe87f7e73468f2a&amount=10.00&discount=0.00&addedon=2013-05-10 18 32 30&productinfo=Product Info&firstname=Payu-Admin&lastname=&address1=&address2=&city=&state=&country=&zipcode=&email=test@example.com&phone=1234567890&udf1=&udf2=original_item_id&udf3=&udf4=&udf5=&udf6=&udf7=&udf8=&udf9=&udf10=&hash=e35f67dc7232d12caa28b16ba31b509f62bdea1e930bb6766a4f71036cc1af34debb8afc0fdd89be50f0604c1e6bca7209dfffe6b3a893c575492edcab3444ee&field1=313069903923&field2=999999&field3=59117331831301&field4=-1&field5=&field6=&field7=&field8=&PG_TYPE=HDFC&bank_ref_num=59117331831301&bankcode=CC&error=E000&cardnum=512345XXXXXX2346&cardhash=766f0227cc4b4c5f773a04cb31d8d1c5be071dd8d08fe365ecf5e2e5c947546d" + "mihpayid=403993715508030204&mode=CC&status=success&unmappedstatus=captured&key=merchant_id&txnid=4ba4afe87f7e73468f2a&amount=10.00&discount=0.00&addedon=2013-05-10 18 32 30&productinfo=Product Info&firstname=Payu-Admin&lastname=&address1=&address2=&city=&state=&country=&zipcode=&email=test@example.com&phone=1234567890&udf1=&udf2=original_item_id&udf3=&udf4=&udf5=&udf6=&udf7=&udf8=&udf9=&udf10=&hash=#{checksum}&field1=313069903923&field2=999999&field3=59117331831301&field4=-1&field5=&field6=&field7=&field8=&PG_TYPE=HDFC&bank_ref_num=59117331831301&bankcode=CC&error=E000&cardnum=512345XXXXXX2346&cardhash=766f0227cc4b4c5f773a04cb31d8d1c5be071dd8d08fe365ecf5e2e5c947546d" + end + + def checksum + Digest::SHA512.hexdigest("secret|success|||||||||original_item_id||test@example.com|Payu-Admin|Product Info|10.00|4ba4afe87f7e73468f2a|merchant_id") end end diff --git a/test/unit/integrations/notifications/valitor_notification_test.rb b/test/unit/integrations/notifications/valitor_notification_test.rb index a49502e4108..4775abc9acb 100644 --- a/test/unit/integrations/notifications/valitor_notification_test.rb +++ b/test/unit/integrations/notifications/valitor_notification_test.rb @@ -33,6 +33,12 @@ def test_accessors assert !@notification.test? end + + def test_comma_delimited_notification + @notification = Valitor::Notification.new(http_raw_query_with_comma_delimited_currency) + + assert_equal "25.99", @notification.gross + end def test_acknowledge valid = Valitor.notification(http_raw_query, :credential2 => 'password') @@ -54,4 +60,8 @@ def test_test_mode def http_raw_query "Kortategund=VISA&KortnumerSidustu=9999&Dagsetning=21.01.2011&Heimildarnumer=123450&Faerslunumer=FÆRSLUNR: 0026237&VefverslunSalaID=2b969de3-6928-4fa7-a0d6-6dec63fec5c3&Tilvisunarnumer=order684afbb93730db2492a8fa2f3fedbcb9&RafraenUndirskriftSvar=03d859813eff711d6c8667b0caf5f5a5&Upphaed=100&Nafn=NAME&Heimilisfang=123 ADDRESS&Postnumer=98765&Stadur=CITY&Land=COUNTRY&Tolvupostfang=EMAIL@EXAMPLE.COM&Athugasemdir=COMMENTS&LeyfirEndurtoku=" end + + def http_raw_query_with_comma_delimited_currency + "Kortategund=VISA&KortnumerSidustu=9999&Dagsetning=21.01.2011&Heimildarnumer=123450&Faerslunumer=FÆRSLUNR: 0026237&VefverslunSalaID=2b969de3-6928-4fa7-a0d6-6dec63fec5c3&Tilvisunarnumer=order684afbb93730db2492a8fa2f3fedbcb9&RafraenUndirskriftSvar=03d859813eff711d6c8667b0caf5f5a5&Upphaed=25,99&Nafn=NAME&Heimilisfang=123 ADDRESS&Postnumer=98765&Stadur=CITY&Land=COUNTRY&Tolvupostfang=EMAIL@EXAMPLE.COM&Athugasemdir=COMMENTS&LeyfirEndurtoku=" + end end diff --git a/test/unit/integrations/notifications/wirecard_checkout_page_notification_test.rb b/test/unit/integrations/notifications/wirecard_checkout_page_notification_test.rb new file mode 100644 index 00000000000..8beedfb19ad --- /dev/null +++ b/test/unit/integrations/notifications/wirecard_checkout_page_notification_test.rb @@ -0,0 +1,33 @@ +require 'test_helper' + +class WirecardCheckoutPageNotificationTest < Test::Unit::TestCase + include ActiveMerchant::Billing::Integrations + + def setup + @options = fixtures(:wirecard_checkout_page) + @wirecard_checkout_page = WirecardCheckoutPage::Notification.new(http_raw_data, @options) + end + + def test_accessors + @wirecard_checkout_page.acknowledge + assert_equal nil, @wirecard_checkout_page.message + assert @wirecard_checkout_page.complete? + assert_equal "13", @wirecard_checkout_page.item_id + assert_equal "110.99", @wirecard_checkout_page.gross + assert_equal "EUR", @wirecard_checkout_page.currency + assert_equal "IDL", @wirecard_checkout_page.paymentType + end + + def test_send_acknowledgement + assert_equal '', @wirecard_checkout_page.response + end + + def test_respond_to_acknowledge + assert @wirecard_checkout_page.respond_to?(:acknowledge) + end + + private + def http_raw_data + "amount=110.99¤cy=EUR&paymentType=IDL&financialInstitution=INGBANK&language=de&orderNumber=9882408&paymentState=SUCCESS&utf8=%E2%9C%93&xActiveMerchantOrderId=13&consumerIpAddress=192.168.201.181&consumerUserAgent=Mozilla%2F5.0+%28X11%3B+Ubuntu%3B+Linux+x86_64%3B+rv%3A24.0%29+Gecko%2F20100101+Firefox%2F24.0&commit=Jetzt+bezahlen&idealConsumerName=Test+C%C3%B6ns%C3%BCmer+Utl%C3%B8psdato&idealConsumerBIC=RABONL2U&idealConsumerCity=RABONL2U&idealConsumerIBAN=NL17RABO0213698412&idealConsumerAccountNumber=NL17RABO0213698412&gatewayReferenceNumber=DGW_9882408_RN&gatewayContractNumber=DemoContractNumber123&avsResponseCode=X&avsResponseMessage=Demo+AVS+ResultMessage&responseFingerprintOrder=amount%2Ccurrency%2CpaymentType%2CfinancialInstitution%2Clanguage%2CorderNumber%2CpaymentState%2Cutf8%2CxActiveMerchantOrderId%2CconsumerIpAddress%2CconsumerUserAgent%2Ccommit%2CidealConsumerName%2CidealConsumerBIC%2CidealConsumerCity%2CidealConsumerIBAN%2CidealConsumerAccountNumber%2CgatewayReferenceNumber%2CgatewayContractNumber%2CavsResponseCode%2CavsResponseMessage%2Csecret%2CresponseFingerprintOrder&responseFingerprint=a15a4fceefcab5a41380f97079180d55" + end +end diff --git a/test/unit/integrations/payu_in_module_test.rb b/test/unit/integrations/payu_in_module_test.rb index f563f4a425b..53441994f9c 100644 --- a/test/unit/integrations/payu_in_module_test.rb +++ b/test/unit/integrations/payu_in_module_test.rb @@ -5,8 +5,8 @@ class PayuInModuleTest < Test::Unit::TestCase def setup ActiveMerchant::Billing::Base.integration_mode = :test - @merchant_id = 'C0Dr8m' - @secret_key = '3sf0jURk' + @merchant_id = 'merchant_id' + @secret_key = 'secret' end def test_service_url_method @@ -26,7 +26,8 @@ def test_notification_method end def test_checksum_method - payu_load = "4ba4afe87f7e73468f2a|10.00|Product Info|Payu-Admin|test@example.com||||||||||" - assert_equal "cd324f64891b07d95492a2fd80ae469092e302faa3d3df5ba1b829936fd7497b6e89c3e48fd70e2a131cdd4f17d14bc20f292e9408650c085bc3bedb32f44266", PayuIn.checksum(@merchant_id, @secret_key, payu_load) + payu_load = "order_id|10.00|Product Info|Payu-Admin|test@example.com||||||||||" + checksum = Digest::SHA512.hexdigest([@merchant_id, payu_load, @secret_key].join("|")) + assert_equal checksum, PayuIn.checksum(@merchant_id, @secret_key, payu_load.split("|", -1)) end end diff --git a/test/unit/integrations/payu_in_paisa_module_test.rb b/test/unit/integrations/payu_in_paisa_module_test.rb index 12e63f431b3..00e3235227b 100644 --- a/test/unit/integrations/payu_in_paisa_module_test.rb +++ b/test/unit/integrations/payu_in_paisa_module_test.rb @@ -9,17 +9,17 @@ def setup def test_service_url_method ActiveMerchant::Billing::Base.integration_mode = :test - assert_equal "https://test.payu.in/_payment.php", PayuIn.service_url + assert_equal "https://test.payu.in/_payment.php", PayuInPaisa.service_url ActiveMerchant::Billing::Base.integration_mode = :production - assert_equal "https://secure.payu.in/_payment.php", PayuIn.service_url + assert_equal "https://secure.payu.in/_payment.php", PayuInPaisa.service_url end def test_return_method - assert_instance_of PayuIn::Return, PayuIn.return('name=foo', {}) + assert_instance_of PayuInPaisa::Return, PayuInPaisa.return('name=foo', {}) end def test_notification_method - assert_instance_of PayuIn::Notification, PayuIn.notification('name=foo', {}) + assert_instance_of PayuInPaisa::Notification, PayuInPaisa.notification('name=foo', {}) end end diff --git a/test/unit/integrations/returns/ipay88_return_test.rb b/test/unit/integrations/returns/ipay88_return_test.rb index efae6dae385..e797905ae39 100644 --- a/test/unit/integrations/returns/ipay88_return_test.rb +++ b/test/unit/integrations/returns/ipay88_return_test.rb @@ -4,103 +4,23 @@ class Ipay88ReturnTest < Test::Unit::TestCase include ActiveMerchant::Billing::Integrations def setup - @ipay88 = build_return(http_raw_data) + @ipay = Ipay88::Return.new(http_raw_data, :credential2 => 'secret') end - def test_accessors - assert_equal "ipay88merchcode", @ipay88.account - assert_equal 6, @ipay88.payment - assert_equal "order-500", @ipay88.order - assert_equal "5.00", @ipay88.amount - assert_equal "MYR", @ipay88.currency - assert_equal "Remarkable", @ipay88.remark - assert_equal "12345", @ipay88.transaction - assert_equal "auth123", @ipay88.auth_code - assert_equal "1", @ipay88.status - assert_equal "Invalid merchant", @ipay88.error - assert_equal "bPlMszCBwxlfGX9ZkgmSfT+OeLQ=", @ipay88.signature + def test_success? + assert @ipay.success? end - def test_secure_request - assert @ipay88.secure? + def test_message_returns_error_description + assert_equal 'Customer Cancel Transaction', @ipay.message end - def test_insecure_request - assert !build_return(http_raw_data(:invalid_sig)).secure? - end - - def test_successful_return - params = parameterize(payload) - Ipay88::Return.any_instance.expects(:ssl_post).with(Ipay88.service_url, params, - { "Content-Length" => params.size.to_s, "User-Agent" => "Active Merchant -- http://activemerchant.org" } - ).returns("00") - - assert @ipay88.success? - end - - def test_unsuccessful_return_due_to_signature - ipay = build_return(http_raw_data(:invalid_sig)) - assert !ipay.success? - end - - def test_unsuccessful_return_due_to_requery - params = parameterize(payload) - Ipay88::Return.any_instance.expects(:ssl_post).with(Ipay88.service_url, params, - { "Content-Length" => params.size.to_s, "User-Agent" => "Active Merchant -- http://activemerchant.org" } - ).returns("Invalid parameters") - assert !@ipay88.success? - end - - def test_unsuccessful_return_due_to_payment_failed - params = parameterize(payload) - Ipay88::Return.any_instance.expects(:ssl_post).with(Ipay88.service_url, params, - { "Content-Length" => params.size.to_s, "User-Agent" => "Active Merchant -- http://activemerchant.org" } - ).returns("00") - ipay = build_return(http_raw_data(:payment_failed)) - assert !ipay.success? - end - - def test_unsuccessful_return_due_to_missing_amount - ipay = build_return(http_raw_data(:missing_amount)) - assert !ipay.success? + def test_cancelled + assert @ipay.cancelled? end private - def http_raw_data(mode=:success) - base = { "MerchantCode" => "ipay88merchcode", - "PaymentId" => 6, - "RefNo" => "order-500", - "Amount" => "5.00", - "Currency" => "MYR", - "Remark" => "Remarkable", - "TransId" => "12345", - "AuthCode" => "auth123", - "Status" => 1, - "ErrDesc" => "Invalid merchant" } - - case mode - when :success - parameterize(base.merge("Signature" => "bPlMszCBwxlfGX9ZkgmSfT+OeLQ=")) - when :invalid_sig - parameterize(base.merge("Signature" => "hacked")) - when :payment_failed - parameterize(base.merge("Status" => 0, "Signature" => "p8nXYcl/wytpNMzsf31O/iu/2EU=")) - when :missing_amount - parameterize(base.except("Amount")) - else - "" - end - end - - def payload - { "MerchantCode" => "ipay88merchcode", "RefNo" => "order-500", "Amount" => "5.00" } - end - - def parameterize(params) - params.reject{|k, v| v.blank?}.keys.sort.collect { |key| "#{key}=#{CGI.escape(params[key].to_s)}" }.join("&") - end - - def build_return(data) - Ipay88::Return.new(data, :credential2 => "apple") + def http_raw_data + "Amount=0.10&AuthCode=12345678&Currency=USD&ErrDesc=Customer Cancel Transaction&MerchantCode=M00001&PaymentId=1&RefNo=10000001&Remark=&Signature=RWAehzFtiNCKQWpXheazrCF33J4%3D&Status=1&TransId=T123456789" end end diff --git a/test/unit/integrations/returns/payu_in_paisa_return_test.rb b/test/unit/integrations/returns/payu_in_paisa_return_test.rb index 174a682e7c2..97baca37ec0 100644 --- a/test/unit/integrations/returns/payu_in_paisa_return_test.rb +++ b/test/unit/integrations/returns/payu_in_paisa_return_test.rb @@ -4,11 +4,11 @@ class PayuInPaisaReturnTest < Test::Unit::TestCase include ActiveMerchant::Billing::Integrations def setup - @payu = PayuInPaisa::Return.new(http_raw_data_success, :credential1 => 'C0Dr8m', :credential2 => '3sf0jURk') + @payu = PayuInPaisa::Return.new(http_raw_data_success, :credential1 => 'merchant_id', :credential2 => 'secret') end def setup_failed_return - @payu = PayuInPaisa::Return.new(http_raw_data_failure, :credential1 => 'C0Dr8m', :credential2 => '3sf0jURk') + @payu = PayuInPaisa::Return.new(http_raw_data_failure, :credential1 => 'merchant_id', :credential2 => 'secret') end def test_success @@ -38,7 +38,7 @@ def test_return_has_notification assert_equal 'CC', @payu.notification.type assert_equal 'INR', notification.currency assert_equal '4ba4afe87f7e73468f2a', notification.invoice - assert_equal 'C0Dr8m', notification.account + assert_equal 'merchant_id', notification.account assert_equal '10.00', notification.gross assert_equal '0.00', notification.discount assert_equal nil, notification.offer_description @@ -48,7 +48,7 @@ def test_return_has_notification assert_equal 'Payu-Admin', notification.customer_first_name assert_equal '', notification.customer_last_name assert_equal ["", "original_item_id", "", "", "", "", "", "", "", ""], notification.user_defined - assert_equal "e35f67dc7232d12caa28b16ba31b509f62bdea1e930bb6766a4f71036cc1af34debb8afc0fdd89be50f0604c1e6bca7209dfffe6b3a893c575492edcab3444ee", notification.checksum + assert_equal checksum, notification.checksum assert_equal 'E000', notification.message assert notification.checksum_ok? end @@ -56,11 +56,15 @@ def test_return_has_notification private def http_raw_data_success - "mihpayid=403993715508030204&mode=CC&status=success&unmappedstatus=captured&key=C0Dr8m&txnid=4ba4afe87f7e73468f2a&amount=10.00&discount=0.00&addedon=2013-05-10 18 32 30&productinfo=Product Info&firstname=Payu-Admin&lastname=&address1=&address2=&city=&state=&country=&zipcode=&email=test@example.com&phone=1234567890&udf1=&udf2=original_item_id&udf3=&udf4=&udf5=&udf6=&udf7=&udf8=&udf9=&udf10=&hash=e35f67dc7232d12caa28b16ba31b509f62bdea1e930bb6766a4f71036cc1af34debb8afc0fdd89be50f0604c1e6bca7209dfffe6b3a893c575492edcab3444ee&field1=313069903923&field2=999999&field3=59117331831301&field4=-1&field5=&field6=&field7=&field8=&PG_TYPE=HDFC&bank_ref_num=59117331831301&bankcode=CC&error=E000&cardnum=512345XXXXXX2346&cardhash=766f0227cc4b4c5f773a04cb31d8d1c5be071dd8d08fe365ecf5e2e5c947546d" + "mihpayid=403993715508030204&mode=CC&status=success&unmappedstatus=captured&key=merchant_id&txnid=4ba4afe87f7e73468f2a&amount=10.00&discount=0.00&addedon=2013-05-10 18 32 30&productinfo=Product Info&firstname=Payu-Admin&lastname=&address1=&address2=&city=&state=&country=&zipcode=&email=test@example.com&phone=1234567890&udf1=&udf2=original_item_id&udf3=&udf4=&udf5=&udf6=&udf7=&udf8=&udf9=&udf10=&hash=#{checksum}&field1=313069903923&field2=999999&field3=59117331831301&field4=-1&field5=&field6=&field7=&field8=&PG_TYPE=HDFC&bank_ref_num=59117331831301&bankcode=CC&error=E000&cardnum=512345XXXXXX2346&cardhash=766f0227cc4b4c5f773a04cb31d8d1c5be071dd8d08fe365ecf5e2e5c947546d" end def http_raw_data_failure - "mihpayid=403993715508030204&mode=CC&status=failure&unmappedstatus=failed&key=C0Dr8m&txnid=8ae1034d1abf47fde1cf&amount=10.00&discount=0.00&addedon=2013-05-13 11:09:20&productinfo=Product Info&firstname=Payu-Admin&lastname=&address1=&address2=&city=&state=&country=&zipcode=&email=test@example.com&phone=1234567890&udf1=&udf2=&udf3=&udf4=&udf5=&udf6=&udf7=&udf8=&udf9=&udf10=&hash=65774f82abe64cec54be31107529b2a3eef8f6a3f97a8cb81e9769f4394b890b0e7171f8988c4df3684e7f9f337035d0fe09a844da4b76e68dd643e8ac5e5c63&field1=&field2=&field3=&field4=&field5=!ERROR!-GV00103-Invalid BrandError Code: GV00103&field6=&field7=&field8=failed in enrollment&PG_TYPE=HDFC&bank_ref_num=&bankcode=CC&error=E201&cardnum=411111XXXXXX1111&cardhash=49c73d6c44f27f7ac71b439de842f91e27fcbc3b9ce9dfbcbf1ce9a8fe790c17" + "mihpayid=403993715508030204&mode=CC&status=failure&unmappedstatus=failed&key=merchant_id&txnid=8ae1034d1abf47fde1cf&amount=10.00&discount=0.00&addedon=2013-05-13 11:09:20&productinfo=Product Info&firstname=Payu-Admin&lastname=&address1=&address2=&city=&state=&country=&zipcode=&email=test@example.com&phone=1234567890&udf1=&udf2=&udf3=&udf4=&udf5=&udf6=&udf7=&udf8=&udf9=&udf10=&hash=65774f82abe64cec54be31107529b2a3eef8f6a3f97a8cb81e9769f4394b890b0e7171f8988c4df3684e7f9f337035d0fe09a844da4b76e68dd643e8ac5e5c63&field1=&field2=&field3=&field4=&field5=!ERROR!-GV00103-Invalid BrandError Code: GV00103&field6=&field7=&field8=failed in enrollment&PG_TYPE=HDFC&bank_ref_num=&bankcode=CC&error=E201&cardnum=411111XXXXXX1111&cardhash=49c73d6c44f27f7ac71b439de842f91e27fcbc3b9ce9dfbcbf1ce9a8fe790c17" + end + + def checksum + Digest::SHA512.hexdigest("secret|success|||||||||original_item_id||test@example.com|Payu-Admin|Product Info|10.00|4ba4afe87f7e73468f2a|merchant_id") end end diff --git a/test/unit/integrations/returns/payu_in_return_test.rb b/test/unit/integrations/returns/payu_in_return_test.rb index add0f355cfe..6e49d8f0e5d 100644 --- a/test/unit/integrations/returns/payu_in_return_test.rb +++ b/test/unit/integrations/returns/payu_in_return_test.rb @@ -4,11 +4,11 @@ class PayuInReturnTest < Test::Unit::TestCase include ActiveMerchant::Billing::Integrations def setup - @payu = PayuIn::Return.new(http_raw_data_success, :credential1 => 'C0Dr8m', :credential2 => '3sf0jURk') + @payu = PayuIn::Return.new(http_raw_data_success, :credential1 => 'merchant_id', :credential2 => 'secret') end def setup_failed_return - @payu = PayuIn::Return.new(http_raw_data_failure, :credential1 => 'C0Dr8m', :credential2 => '3sf0jURk') + @payu = PayuIn::Return.new(http_raw_data_failure, :credential1 => 'merchant_id', :credential2 => 'secret') end def test_success @@ -38,7 +38,7 @@ def test_return_has_notification assert_equal 'CC', @payu.notification.type assert_equal 'INR', notification.currency assert_equal '4ba4afe87f7e73468f2a', notification.invoice - assert_equal 'C0Dr8m', notification.account + assert_equal 'merchant_id', notification.account assert_equal '10.00', notification.gross assert_equal '0.00', notification.discount assert_equal nil, notification.offer_description @@ -48,7 +48,7 @@ def test_return_has_notification assert_equal 'Payu-Admin', notification.customer_first_name assert_equal '', notification.customer_last_name assert_equal ["", "", "", "", "", "", "", "", "", ""], notification.user_defined - assert_equal 'ef0c1b509a42b802a4938c25dc9bb9efe40b75a7dfb8bde1a6f126fa1f86cee264c5e5a17e87db85150d6d8912eafda838416e669712f1989dcb9cbdb8c24219', notification.checksum + assert_equal checksum, notification.checksum assert_equal 'E000', notification.message assert notification.checksum_ok? end @@ -56,11 +56,15 @@ def test_return_has_notification private def http_raw_data_success - "mihpayid=403993715508030204&mode=CC&status=success&unmappedstatus=captured&key=C0Dr8m&txnid=4ba4afe87f7e73468f2a&amount=10.00&discount=0.00&addedon=2013-05-10 18 32 30&productinfo=Product Info&firstname=Payu-Admin&lastname=&address1=&address2=&city=&state=&country=&zipcode=&email=test@example.com&phone=1234567890&udf1=&udf2=&udf3=&udf4=&udf5=&udf6=&udf7=&udf8=&udf9=&udf10=&hash=ef0c1b509a42b802a4938c25dc9bb9efe40b75a7dfb8bde1a6f126fa1f86cee264c5e5a17e87db85150d6d8912eafda838416e669712f1989dcb9cbdb8c24219&field1=313069903923&field2=999999&field3=59117331831301&field4=-1&field5=&field6=&field7=&field8=&PG_TYPE=HDFC&bank_ref_num=59117331831301&bankcode=CC&error=E000&cardnum=512345XXXXXX2346&cardhash=766f0227cc4b4c5f773a04cb31d8d1c5be071dd8d08fe365ecf5e2e5c947546d" + "mihpayid=403993715508030204&mode=CC&status=success&unmappedstatus=captured&key=merchant_id&txnid=4ba4afe87f7e73468f2a&amount=10.00&discount=0.00&addedon=2013-05-10 18 32 30&productinfo=Product Info&firstname=Payu-Admin&lastname=&address1=&address2=&city=&state=&country=&zipcode=&email=test@example.com&phone=1234567890&udf1=&udf2=&udf3=&udf4=&udf5=&udf6=&udf7=&udf8=&udf9=&udf10=&hash=#{checksum}&field1=313069903923&field2=999999&field3=59117331831301&field4=-1&field5=&field6=&field7=&field8=&PG_TYPE=HDFC&bank_ref_num=59117331831301&bankcode=CC&error=E000&cardnum=512345XXXXXX2346&cardhash=766f0227cc4b4c5f773a04cb31d8d1c5be071dd8d08fe365ecf5e2e5c947546d" end def http_raw_data_failure - "mihpayid=403993715508030204&mode=CC&status=failure&unmappedstatus=failed&key=C0Dr8m&txnid=8ae1034d1abf47fde1cf&amount=10.00&discount=0.00&addedon=2013-05-13 11:09:20&productinfo=Product Info&firstname=Payu-Admin&lastname=&address1=&address2=&city=&state=&country=&zipcode=&email=test@example.com&phone=1234567890&udf1=&udf2=&udf3=&udf4=&udf5=&udf6=&udf7=&udf8=&udf9=&udf10=&hash=65774f82abe64cec54be31107529b2a3eef8f6a3f97a8cb81e9769f4394b890b0e7171f8988c4df3684e7f9f337035d0fe09a844da4b76e68dd643e8ac5e5c63&field1=&field2=&field3=&field4=&field5=!ERROR!-GV00103-Invalid BrandError Code: GV00103&field6=&field7=&field8=failed in enrollment&PG_TYPE=HDFC&bank_ref_num=&bankcode=CC&error=E201&cardnum=411111XXXXXX1111&cardhash=49c73d6c44f27f7ac71b439de842f91e27fcbc3b9ce9dfbcbf1ce9a8fe790c17" + "mihpayid=403993715508030204&mode=CC&status=failure&unmappedstatus=failed&key=merchant_id&txnid=8ae1034d1abf47fde1cf&amount=10.00&discount=0.00&addedon=2013-05-13 11:09:20&productinfo=Product Info&firstname=Payu-Admin&lastname=&address1=&address2=&city=&state=&country=&zipcode=&email=test@example.com&phone=1234567890&udf1=&udf2=&udf3=&udf4=&udf5=&udf6=&udf7=&udf8=&udf9=&udf10=&hash=65774f82abe64cec54be31107529b2a3eef8f6a3f97a8cb81e9769f4394b890b0e7171f8988c4df3684e7f9f337035d0fe09a844da4b76e68dd643e8ac5e5c63&field1=&field2=&field3=&field4=&field5=!ERROR!-GV00103-Invalid BrandError Code: GV00103&field6=&field7=&field8=failed in enrollment&PG_TYPE=HDFC&bank_ref_num=&bankcode=CC&error=E201&cardnum=411111XXXXXX1111&cardhash=49c73d6c44f27f7ac71b439de842f91e27fcbc3b9ce9dfbcbf1ce9a8fe790c17" + end + + def checksum + Digest::SHA512.hexdigest("secret|success|||||||||||test@example.com|Payu-Admin|Product Info|10.00|4ba4afe87f7e73468f2a|merchant_id") end end diff --git a/test/unit/integrations/returns/wirecard_checkout_page_return_test.rb b/test/unit/integrations/returns/wirecard_checkout_page_return_test.rb new file mode 100644 index 00000000000..0dc022b0452 --- /dev/null +++ b/test/unit/integrations/returns/wirecard_checkout_page_return_test.rb @@ -0,0 +1,20 @@ +require 'test_helper' + +class WirecardCheckoutPageReturnTest < Test::Unit::TestCase + include ActiveMerchant::Billing::Integrations + + def setup + @options = fixtures(:wirecard_checkout_page) + @return = WirecardCheckoutPage::Return.new(http_raw_data, @options) + end + + def test_return + assert @return.success? + end + + private + def http_raw_data + "amount=110.99¤cy=EUR&paymentType=IDL&financialInstitution=INGBANK&language=de&orderNumber=9882408&paymentState=SUCCESS&utf8=%E2%9C%93&xActiveMerchantOrderId=13&consumerIpAddress=192.168.201.181&consumerUserAgent=Mozilla%2F5.0+%28X11%3B+Ubuntu%3B+Linux+x86_64%3B+rv%3A24.0%29+Gecko%2F20100101+Firefox%2F24.0&commit=Jetzt+bezahlen&idealConsumerName=Test+C%C3%B6ns%C3%BCmer+Utl%C3%B8psdato&idealConsumerBIC=RABONL2U&idealConsumerCity=RABONL2U&idealConsumerIBAN=NL17RABO0213698412&idealConsumerAccountNumber=NL17RABO0213698412&gatewayReferenceNumber=DGW_9882408_RN&gatewayContractNumber=DemoContractNumber123&avsResponseCode=X&avsResponseMessage=Demo+AVS+ResultMessage&responseFingerprintOrder=amount%2Ccurrency%2CpaymentType%2CfinancialInstitution%2Clanguage%2CorderNumber%2CpaymentState%2Cutf8%2CxActiveMerchantOrderId%2CconsumerIpAddress%2CconsumerUserAgent%2Ccommit%2CidealConsumerName%2CidealConsumerBIC%2CidealConsumerCity%2CidealConsumerIBAN%2CidealConsumerAccountNumber%2CgatewayReferenceNumber%2CgatewayContractNumber%2CavsResponseCode%2CavsResponseMessage%2Csecret%2CresponseFingerprintOrder&responseFingerprint=a15a4fceefcab5a41380f97079180d55" + end + +end diff --git a/test/unit/integrations/wirecard_checkout_page_module_test.rb b/test/unit/integrations/wirecard_checkout_page_module_test.rb new file mode 100644 index 00000000000..454d9752a9c --- /dev/null +++ b/test/unit/integrations/wirecard_checkout_page_module_test.rb @@ -0,0 +1,9 @@ +require 'test_helper' + +class WirecardCheckoutPageModuleTest < Test::Unit::TestCase + include ActiveMerchant::Billing::Integrations + + def test_notification_method + assert_instance_of WirecardCheckoutPage::Notification, WirecardCheckoutPage.notification('name=cody', {}) + end +end