diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000000..ffa03d0ef2a
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,23 @@
+name: CI
+on: [pull_request]
+jobs:
+ rspec:
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - { ruby: '3.0', rails: '6.0' }
+ - { ruby: '3.0', rails: '6.1' }
+ - { ruby: '3.0', rails: '7.0' }
+ - { ruby: '3.1', rails: '7.0' }
+ - { ruby: '3.2', rails: '7.0' }
+ runs-on: ubuntu-latest
+ env:
+ BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/rails.${{ matrix.rails }}.gemfile
+ steps:
+ - uses: actions/checkout@v2
+ - uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: ${{ matrix.ruby }}
+ bundler-cache: true
+ - run: bundle exec rake test:units
diff --git a/.travis.yml b/.travis.yml
index 65f71574a85..4c52ff03a48 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,26 +2,32 @@ language: ruby
script: "bundle exec rake test:units"
sudo: false
cache: bundler
+before_install:
+ # https://github.com/travis-ci/travis-ci/issues/8978#issuecomment-354036443
+ - gem update --system
+ - gem install bundler
rvm:
-- 2.0
-- 2.1
-- 2.2.3
+- 2.6.5
+- 2.5
+- 2.4
+- 2.3
gemfile:
-- Gemfile.rails32
-- Gemfile.rails40
-- Gemfile.rails41
+- Gemfile.rails60
+- Gemfile.rails52
+- Gemfile.rails51
+- Gemfile.rails50
- Gemfile.rails42
-- Gemfile.rails5
matrix:
exclude:
- - rvm: 2.0
- gemfile: Gemfile.rails5
- - rvm: 2.1
- gemfile: Gemfile.rails5
+ - rvm: 2.3
+ gemfile: Gemfile.rails60
+ - rvm: 2.4
+ gemfile: Gemfile.rails60
notifications:
email:
- - nathaniel@talbott.ws
+ on_success: never
+ on_failure: always
diff --git a/Appraisals b/Appraisals
new file mode 100644
index 00000000000..ad59b158074
--- /dev/null
+++ b/Appraisals
@@ -0,0 +1,5 @@
+%w[6.0 6.1 7.0].each do |version|
+ appraise "rails.#{version}" do
+ gem "activesupport", "~> #{version}.0"
+ end
+end
diff --git a/CHANGELOG b/CHANGELOG
index 7e547761644..c7c0dc1c696 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,36 +1,477 @@
= ActiveMerchant CHANGELOG
-* Moneris: add scrubbing support [bruno]
-* Litle: add scrubbing support [bruno]
-* Sage: Add support for scrubbing [bruno]
+== HEAD
+* Allow setting min/max SSL version for a connection on Ruby 2.5 [bdewater] #2775
+* Add `gateways:ssl:min_version` rake task to test upcoming TLS 1.0 deprecation deadline [bdewater] #2775
+* Remove support for Rails < 4.2, add support for Rails 5.2 and Ruby 2.5 [bdewater]
+* Spreedly: Support verify and find transactions [abarrak] #2798
+* Adyen: Support merchant-specific subdomains [curiousepic] #2799
+* Fix ENV based configuration of Net::Http for proxies [bbergstrom] #2800
+* ANET: Withhold cryptogram for credit [curiousepic] #2804
+* Borgun: Remove batch from request parameters [deedeelavinder] #2805
+* WorldPay: Remove Inquiry requests in verify transactions [nfarve] #2802
+* Credorax: Update tests [curiousepic] #2809
+* Braintree: Remove decimal for non-fractional currencies [nfarve] #2806
+* Realex: Add documented country support for US and CA [a-salty-strudel] #2810
+* Paymentez: Add `tax_percentage` optional parameter [deedeelavinder] #2814
+* Braintree: Add `skip_advanced_fraud_checking` optional parameter [deedeelavinder] #2811
+* SafeCharge: Additional gateway options [dtykocki] #2816
+* FirstPay: Handle missing billing addresses [dtykocki] #2822
+* Realex: Add ApplePay Support [nfarve] #2820
+* Checkout V2: Additional gateway options [dtykocki] #2821
+* CyberSource: Support 3ds validate request [curiousepic] #2823
+* Paymentez: Remove card tokenization step from authorize [dtykocki] #2825
+* WorldPay: Add 3DS [nfarve] #2819
+* EBANX: Interpolate authorization string [curiousepic] #2830
+* CyberSource: Support 3DS validation for authorize [curiousepic] #2832
+* Redsys: Fix ISO code for PLN [chopenhauer] #2831
+
+== Version 1.78.0 (March 29, 2018)
+* Litle: Add store for echecks [nfarve] #2779
+* Litle: Add Support for Echeck [nfarve] #2776
+* Orbital: Correct level 2 tax handling [deedeelavinder] #2729
+* Payeezy: Change determination method of endpoint for store request [deedeelavinder] #2731
+* Adyen: Return refusal_reason_raw when present [curiousepic] #2728
+* Payeezy: Update Store method [nfarve] #2733
+* CenPOS: Remove gzip encoding header [curiousepic] #2735
+* Mercado Pago: Allow binary_mode to be changed [nfarve] #2736
+* Stripe: Accept strings for refund_fee_amount [curiousepic] #2738
+* Orbital: Complete scrub test coverage [curiousepic] #2739
+* MIGS: Scrub sensitive data [curiousepic] #2740
+* Worldpay US: Scrub sensitive data [curiousepic] #2742
+* WorldPay: Remove Israel from supported country list [dtykocki] #2746
+* Optimal Payments: Scrub sensitive data [curiousepic] #2743
+* USA Epay Transaction: Scrub sensitive data [curiousepic] #2745
+* MIGS: Add unit test for scrub [curiousepic] #2747
+* Worldpay US: Fix bank account scrub [curiousepic] #2748
+* Litle: Add support for merchantData elements [dtykocki] #2751
+* Paymentez: Add support for purchasing and authorizatin with tokens [bpollack] #2753
+* Ingenico: Remove Trinidad and Tobego from supported country list [bpollack] #2754
+* Barclaycard Smartpay: Remove Georgia from supported country list [bpollack] #2755
+* Merchant Warrior: Remove requirement for state field [joshnuss] #2638
+* Wirecard: Adding missing DigiCert Global Root G2 Cert [filipebarcos] #2759
+* Redsys: Add support for CNY, IDR, INR, KRW and TWD [chopenhauer] #2761
+* Optimal Payments: Fix scrub for double escaping [curiousepic] #2763
+* Orbital: Scrub profile transactions [curiousepic] #2762
+* BlueSnap: Fix currency passing [curiousepic] #2765
+* Stripe: Support pickup_card decline code [dtykocki] #2764
+* Improve scrub testing for five gateways [curiousepic] #2767
+* Payflow: Support scrub [curiousepic] #2768
+* SecureNet: Support scrub [curiousepic] #2769
+* Payeezy: Update transaction method when using stored cards [dtykocki] #2770
+* Citrus Pay, DIBS, 1stPayGateway, Global Transport, NETbilling, Ogone, TNS: remove TLS 1.0 requirement [bdewater] #2774
+* CardStream: Default IP and customer country [dtykocki] #2773
+* Stripe: Support destination amount [dtykocki] #2777
+* GlobalCollect: Update supported country list [dtykocki] #2783
+* Adyen: Support store action [curiousepic] #2784
+* Psigate: Update Test URL and Card [nfarve] #2785
+* USA ePay Transaction: Support ACH/eChecks [curiousepic] #2786
+* PayU Latam: Support language parameter [dtykocki] #2787
+* Payflow: Pass OrderDesc field [curiousepic] #2789
+* Global Collect: Add arbitrary fraudField params [curiousepic] #2790
+* Paystation: Support verify action [curiousepic] #2793
+* Checkout V2: Return error codes in response [curiousepic] #2791
+* CardStream: Change refund to use REFUND_SALE [dtykocki] #2795
+* Spreedly: Scrub sensitive transaction data [abarrak] #2781
+* Stripe: Add `exchange_rate` parameter [WilsonChiang] #2796
+* Mundipagg: New Gateway Implementation [nfarve] #2791
+
+== Version 1.77.0 (January 31, 2018)
+* Authorize.net: Allow Transaction Id to be passed for refuds [nfarve] #2698
+* Forte: ensure unit tests are local-only [bpollack] #2696
+* Moneris US: ensure unit tests are local-only [bpollack] #2696
+* Payflow: Change Verify Method for Amex Cards [nfarve] #2693
+* Safe Charge: fix an issue with variable shadowing in the adapter [bpollack] #2697
+* Crashnet: add scrubbing support [bpollack] #2695
+* Barclays EPDQ: add scrubbing support [bpollack] #2695
+* Fat Zebra: add remote scrubbing test [bpollack] #2695
+* Clearhaus: add remote scrubbing test [bpollack] #2695
+* Borgun: add remote scrubbing test [bpollack] #2695
+* Stripe: Added support for the quickchip entry mode option [rbalsdon]
+* Ogone: Add tests for scrubbing [bpollack] #2700
+* Global Transport: Add scrubbing support [bpollack] #2700
+* HPS: Add scrubbing support [bpollack] #2700
+* FirstData E4: Improve scrubbing and add remote scrubbing test [bpollack] #2700
+* Elavon: Add scrubbing support [bpollack] #2700
+* Data Cash: Add scrubbing support [bpollack] #2700
+* Litle: Fix testing URL [wsmoak] #2673
+* Barclays ePDQ Extra Plus: Add missing Entrust root certificates [pacso] #2614
+* Moneris US: Add scrubbing support [bpollack] #2702
+* Mercury: Add scrubbing support [bpollack] #2702
+* Fat Zebra: Tweak remote scrubbing test [bpollack] #2704
+* Card Connect: Add new gateway [nfarve] #2706
+* Payeezy: Ensure store calls are properly scrubbed [dtykocki] #2709
+* Payeezy: Add unit test for scrubbing store call [dtykocki] #2710
+* Element: Correct URL used by store transactions [dtykocki] #2711
+* Borgun: Add support for specifying TerminalID [bpollack] #2712
+* Barclaycard Smartpay: 3DS Implementation [nfarve] #2714
+* Payeezy: Surface gateway_message on failure [curiousepic] #2717
+* Payment Express: Scrub merchant password [curiousepic] #2723
+* Stripe: Fix Partial Application Fee Refunds [curiousepic] #2713
+* GooglePay: Support network tokenized cards [joshnuss] #2725
+
+== Version 1.76.0 (January 3, 2018)
+* PayU Latam: Change default text for description [nfarve] #2669
+* Checkout V2: Allows AVS and CVV result details to come through on authorizations [deedeelavinder] #2650
+* Global Collect: Adds boolean option for pre_authorization [deeedeelavinder] #2651
+* Credorax: Pass Transaction Type field [curiousepic] #2653
+* Add CardProcess Gateway [bpollack] #2659
+* Safe Charge: Provision 3DS option for approved merchants [deedeelavinder] #2661
+* PayU Latam: Require payment_country on initialize [curiousepic] #2663
+* Adyen: Remove CVV as Required Field and Determines shopperInteraction [nfarve] #2665
+* SafeCharge: add support for VendorID, WebsiteID, and IP logging [bpollack] #2667
+* Safe Charge: Adds 3DS flag [deedeelavinder] #2668
+* CardProcess: Fix success? to always return true or false [bpollack] #2674
+* SagePay: Correct CVV, AVS codes for Sagepay [singhai0] #2670
+* PayU Latam: Count pending Voids as successful [curiousepic] #2677
+* Mercado Pago: Ensure acess tokens are URL escaped [bpollack] #2675
+* MiGS: Update hash format to SHA256 and restore remote tests [bpollack] #2676
+* MiGS: Support verify calls [bpollack] #2664
+* iATS: Fix Messages with Failure on iATS Server [nfarve] #2680
+* Barclaycard Smartpay: Correct repsonse for fraud rejects #2683
+* Adyen: Allow incomplete addresses in some situations [bpollack] #2684
+* Paymentez: Add new gateway [bpollack] #2685
+* PayU Latam: Provide a mechanism to override the amount in verify [dtykocki] #2688
+* Mercado Pago: Support X-Device-Session-ID Header [bpollack] #2689
+* Mercado Pago: Support arbitrary additional_info JSON [bpollack] #2691
+* FirstData E4: Override ECI value for Apple Pay transactions with Discover [jasonwebster] #2671
+* Quickbooks: Add payment context to Quickbooks charges and refunds [bdewater] #2694
+
+== Version 1.75.0 (November 9, 2017)
+* Barclaycard Smartpay: Clean up test options hashes [bpollack] #2632
+* Barclaycard Smartpay: Extra data fields for credits [bpollack] #2631
+* Cyber Source: Correctly passes subscriptionID for store [deedeelavinder] #2633
+* Ebanx: Pass fields for business person responsible [curiousepic] #2635
+* Ebanx: Support Colombian transactions [bpollack] #2636
+* FirstData E4 (Payeezy): Ensure numeric ECI values are zero padded [jasonwebster] #2630
+* Netbanx: Only send currency and billing_details for auths and sales [anotherjosmith] #2643
+* Netbanx: Revert "Fixes basic auth for netbanx by sending the account_number and api_key" [anotherjosmith] #2644
+* PayU Latam: Adds `partnerID`, adjusts phone preferences, allows empty `ip_address`, and adjusts for no `cvv` [deedeelavinder] #2634
+* Sage Payment Solutions: Scrub check info [curiousepic] #2639
+* Worldbank US: Allow using the backup URL [bpollack] #2641
+* Worldbank US: Allow using the backup URL per-request [bpollack] #2645
+
+== Version 1.74.0 (October 24, 2017)
+* Adyen: Update list of supported countries [dtykocki] #2600
+* Authorize.net CIM: Handle multiple error messages [amandapuff] #2537
+* Barclaycard Smartpay: Pass street and house_number fields, in addition to standard address [deedeelavinder] #2603
+* Barclaycard Smartpay: Use authorization pspReference for refunds [davidsantoso] #2599
+* Beanstream: Pass email fields without address [curiousepic] #2615
+* Beanstream: Support recurringPayment for auth, capture, and purchase transactions [dtykocki] #2617
+* Borgun: Add support for USD transactions [dtykocki] #2602
+* Borgun: Include currency code from split authorization for voids [dtykocki] #2605
+* Checkout V2: Expose AVS and CVV results for purchases [dtykocki] #2619
+* Credorax: Update response codes [curiousepic] #2595
+* CyberSource: Support 3DSecure requests [curiousepic] #2624
+* Ebanx: Pass person_type and name for stored cards [curiousepic] #2621
+* Ebanx: Support Store and person_type option [curiousepic] #2604
+* Elavon: Update endpoint URLs [curiousepic] #2608
+* Netbanx: Fix basic auth by sending the account_number and api_key [anotherjosmith] #2616
+* Payeezy: Adds support for store [deedeelavinder] #2591
+* PayU Latam: Set payment_country gateway attribute [curiousepic] #2611
+* Redsys: Support the DKK currency type [bpollack] #2618
+* WePay: Only send ip and device for non-recurring transactions [dtykocki] #2597
+
+== Version 1.73.0 (September 28, 2017)
+* Adyen: Use original authorization pspReference on Refunds [lyverovski] #2589
+* Braintree Blue: Explicitly require braintree-ruby version 2.78 [anotherjosmith]
+* FirstData E4: Scrub 3DS cryptogram [jasonwebster] #2596
+* PayHub: Replace single quotes with double quotes in error message [matthewheath] #2572
+* Wirecard: Format non-fractional currency amounts correctly [bdewater] #2594
+
+== Version 1.72.0 (September 20, 2017)
+* Adyen: Fix failing remote tests [dtykocki] #2584
+* Authorize.net: Remove numeric restriction on customer ID [dtykocki] #2579
+* Authorize.net: Restore default state value for non-US addresses [jasonwebster] #2563
+* Beanstream: Do not default state and zip with empty country [dtykocki] #2582
+* Braintree Blue: Add eci_indicator field for Apple Pay [davidsantoso] #2565
+* Conekta: Add guard clause for details fallbacks [curiousepic] #2573
+* Conekta: Pull required details from billing address [nfarve] #2568
+* DataCash: Enable refunding recurring transactions [davidsantoso] #2560
+* Ebanx: Adds Brazil Specific Parameters [nfarve] #2559
+* Kushki: Add support for refunds [dtykocki] #2575
+* MercadoPago: Additional tweaks for transaction requests [davidsantoso]
+* MercadoPago: Default to alphanumeric order_id [davidsantoso]
+* MercadoPago: Send diners_club cards as diners [davidsantoso] #2585
+* PayU Latam: Correctly condition buyer element fields [curiousepic] #2578
+* PayU Latam: Pass unique buyer fields and country requirements [curiousepic] #2570
+* Qvalent: Support general credit [curiousepic] #2558
+* SafeCharge: Update to Version 4.1.0 [nfarve] #2556
+* WePay: Don't default API version header [curiousepic] #2567
+* WePay: Don't require email for Store [curiousepic] #2588
+
+== Version 1.71.0 (August 22, 2017)
+* Bambora formerly Beanstream: Change casing on customerIp variable [aengusbates] #2551
+* Checkout V2: Add localized_amount support to add_invoice function [nicolas-maalouf-cko] #2452
+* Checkout V2: Add UAE to country list [shasum] #2548
+* Checkout V2: Fix success response code validation [nicolas-maalouf-cko] #2452
+* CreditCall: Only allow AVS when specified [curiousepic] #2549
+* CreditCall: Parse additional params from responses [nfarve] #2552
+* CreditCall: Parse more response params [nfavre] #2543
+* MercadoPago: Small tweaks to building requests [davidsantoso] #2555
+* Orbital: Support Network Tokenization Credit Cards [curiousepic] #2553
+* Orbital: Updgrade schema version to 7.1 [curiousepic] #2546
+* Remove HUF from default non-fractional currencies [curiousepic] #2538
+* Stripe: Add support for statement_address parameters for EMV transactions [malcolm-mergulhao] #2524
+* TransFirst Express: Don't send address2 without value [nfarve] #2545
+* TransFirst Express: Fix Optional Fields Being Passed Blank [nfarve] #2550
+* TransFirst: Fix partial refund [nfarve] #2541
+* Vantiv (Litle): Pass 3DS fields [curiousepic] #2536
+* Braintree Blue: Add phone to options [deedeelavinder] #2564
+
+== Version 1.70.0 (August 4, 2017)
+* Barclaycard Smartpay: Provider a default billing address house number [nfarve] #2520
+* FirstData E4: Fix duplicate XID and CAVV values in tokenized transactions [jasonwebster] #2529
+* FirstData E4: Loose XSD validation for Payeezy (FirstData E4) [jasonwebster] #2529
+* GlobalTransport: Support partial authorizations [dtykocki] #2511
+* Litle: Update schema and certification tests to v9.12 [curiousepic] #2522
+* Litle: Update urls and name to Vantiv [curiousepic] #2531
+* Mercado Pago: Add gateway support [davidsantoso] #2518
+* Orbital: Add support for level 2 data [dtykocki] #2515
+* PayU Latam: Pass DNI Number [curiousepic] #2517
+* Qvalent: Pass 3dSecure fields [curiousepic] #2508
+* SafeCharge: Correct UserID field name [curiousepic]
+* SafeCharge: Pass UserID field [curiousepic] #2507
+* AuthorizeNet: Allow Response Code 4 to be returned as successful [nfarve] #2530
+* Forte: Remove order number from captures in Forte Gateway [nfarve] #2532
+* PayU Latam: Add additional mandatory fields [deedeelavinder] #2528
+
+== Version 1.69.0 (July 12, 2017)
+* WePay: Add payer_rbits and transaction_rbits optional fields [davidsantoso]
+* Adyen: Use Active Merchant standard order_id option for reference [jasonwebster] #2483
+* Correct calculation for three-exponent currencies [curiousepic] #2486
+* SagePay: Use VPSTxId from authorization for refunds [dtykocki] #2489
+* Payflow: Move PAYPAL-NVP header option to a class attribute on the payment gateway [deuxpi] #2492
+* Optimal Payments: Pass CVD indicator accurately [curiousepic] #2491
+* SagePay: Make Repeat purchase if payment is a past authorization [curiousepic] #2495
+* Netbanx: map response errorCodes onto standard error code [iirving] #2456
+* Netbanx: Update supported countries and cardtypes [iirving] #2456
+* Barclaycard Smartpay: Support 0- and 3-exponent currencies [curiousepic] #2498
+* CyberSource: Fix XSD schema validation issues [jasonwebster] #2497
+* WorldPay: Support three-decimal currencies [curiousepic] #2501
+* NMI: Add first and lastname to echeck transactions [dtykocki] #2499
+* PayFlow: Add optional email field [davidsantoso] #2505
+* Worldpay: Support Credit on CFT-enabled merchant IDs [curiousepic] #2503
+* FirstPay: Add processor_id field [davidsantoso] #2506
+* Authorize.Net: Use two character default for billing state [dtykocki] #2496
+
+== Version 1.68.0 (June 27, 2017)
+* Authorize.Net: Return failed response if forced refund settlement fails [bizla] #2476
+* Authorize.net: Concatenate address1 and address2 [dtykocki] #2479
+* Braintree Blue: Braintree Blue: Add ECI indicator to Android Pay transactions [davidsantoso] #2474
+* Credorax: Support 0- and 3-exponent currencies [curiousepic]
+* Cybersource: update supported card types [bdewater] #2477
+* FirstData: Add a default network tokenization strategy for FirstData E4 [krystosterone] #2473
+* FirstPay: FirstPay: Update hostname and force TLSv1 minimum [davidsantoso] #2478
+* JetPay V2: Support store transactions and token based payments [shasum] #2475
+* Moneris: Add 3DS fields for decrypted Apple and Android Pay data [davidsantoso] #2457
+* Openpay: Send customer name and email in authorize and purchase [dtykocki] #2468
+* Payflow: Moved to name value pair (NVP) with payflow [jusleg] #2462
+* Payflow: Set PAYPAL_NVP header as optional [davidsantoso] #2480
+* QuickPay V10: Return last response for purchase and authorize [curiousepic] #2461
+* SafeCharge: Map billing address fields [davidsantoso] #2464
+* SafeCharge: Track currency from original transaction [davidsantoso] #2470
+* Support three-decimal currencies [curiousepic] #2466
+* Trexle: Add gateway support [hossamhossny] #2351
+
+== Version 1.67.0 (June 8, 2017)
+* Acapture: Pass 3D Secure fields [davidsantoso] #2451
+* Authorize.net: Pass Level 2 Data Fields [curiousepic] #2444
+* Credorax: Add 3D Secure authentication fields [davidsantoso] #2446
+* Ebanx: Add gateway support [davidsantoso] #2447
+* Ebanx: Reduce supported countries to Brazil and Mexico [davidsantoso]
+* FirstData Payeezy: Set default ECI value for auth/purchase transactions [jasonwebster] #2448
+* JetPay V2: Add new gateway [shasum] #2442
+* JetPay V2: Add optional tax data to capture calls [shasum] #2445
+* NMI: Add Network Tokenization support [shasum] #2431
+* Orbital: Pass soft descriptors from options hash [curiousepic]
+* Orbital: Update test and production urls [jcowhigjr] #2436
+* Payeezy: Add client_email field for telecheck [davidsantoso] #2455
+* Payeezy: Add customer_id_type and customer_id_number fields [davidsantoso] #2454
+* Quickpay V10: Fix store and token use for recurring payments [wsmoak] #2180
+* Elavon: Support custom fields [curiousepic] #2416
+* WePay: Support risk headers [shasum] #2419
+* WePay: Add Canada as supported country [shasum] #2419
+* Fat Zebra: Fix xid 3D Secure field [curiousepic]
+* SafeCharge: Mark support for European countries [curiousepic]
+* Checkout V2: Pass customer ip option [curiousepic]
+* Realex: Map AVS and CVV response codes [davidsantoso] #2424
+* Opp: Send disable3DSecure custom parameter if present [davidsantoso] #2432
+* SafeCharge: Map standard Active Merchant order_id field [davidsantoso] #2434
+* Payeezy: Default check number to 001 if not present [davidsantoso] #2439
+* Opp: Fix incorrect customParameter key to disable 3DS [davidsantoso]
+
+== Version 1.66.0 (May 4, 2017)
+* Support Rails 5.1 [jhawthorn] #2407
+* ProPay: Add Canada as supported country [davidsantoso]
+* ProPay: Add gateway support [davidsantoso] #2405
+* SafeCharge: Support credit transactions [shasum] #2404
+* WePay: Add scrub method [shasum] #2406
+* iVeri: Add gateway support [curiousepic] #2400
+* iVeri: Support 3DSecure data fields [davidsantoso] #2412
+* Opp: Fix transaction success criteria and clean up options [shasum] #2414
+
+== Version 1.65.0 (April 26, 2017)
+* Adyen: Add Adyen v18 gateway [adyenpayments] #2272
+* Authorize.Net: Force refund of unsettled payments via void [bizla] #2399
+* Barclays ePDQ: removed because it has been replaced by a new API [bdewater] #2331
+* Beanstream: Map ISO province codes for US and CA [shasum] #2396
+* Braintree Blue: Change :full_refund option to :force_full_refund_if_unsettled [bizla] #2403
+* Braintree Blue: Force refund of unsettled payments via void [bizla] #2398
+* Checkout V2: Fix sandbox URL [nicolas-maalouf-cko] #2391
+* Checkout V2: Fix success_from not properly checking two possible success codes [davidsantoso]
+* Cybersource: Rescue XML parse exception [shasum] #2380
+* GlobalCollect: Make message and error reporting more robust [curiousepic] #2370
+* GlobalCollect: Set REJECTED refunds as unsuccessful transactions [davidsantoso] #2365
+* GlobalCollect: Truncate firstName field to 15 characters [davidsantoso]
+* JetPay: Pass down authorization payment method token to refund a capture [davidsantoso]
+* Openpay: Support card points [shasum] #2401
+* Orbital: Don't send CVV indicator if CVV is not present [curiousepic] #2368
+* PayU LATAM: Fix incorrect capture method definition [davidsantoso]
+* Payeezy: Support dynamic soft descriptors [shasum] #2384
+* Pin: Add metadata optional field [shasum] #2363
+* Qvalent: Add soft descriptor fields. Add authorize, capture, and void [davidsantoso]
+* SafeCharge: Add gateway [davidsantoso]
+* SagePay: Support Repeat transactions [curiousepic] #2395
+* Stripe: Support custom application in X-Stripe-Client-User-Agent header [davidsantoso]
+* TransFirst Transaction Express: Support ACH [curiousepic] #2389
+* WePay: Support unique_id for idempotent transactions [shasum] #2367
+* Worldpay: Force refund of unsettled payments via void [bizla] #2402
+
+== Version 1.64.0 (March 6, 2017)
+* Authorize.net: Allow settings to be passed for CIM purchases [fwilkins] #2300
+* Authorize.net: Use new `unsupported_feature` standard error code [jasonwebster] #2322
+* Base Gateway: Add new `unsupported_feature` standard error code [jasonwebster] #2322
+* Braintree Blue: Pass cardholder_name with card [curiousepic] #2324
+* Braintree: Add Android Pay meta data fields [jknipp] #2347
+* CardStream: Add additional of currencies [shasum] #2337
+* Credorax: Return failure response reason [shasum] #2341
+* Digitzs: Add gateway [davidsantoso]
+* Digitzs: Remove merchant_id from gateway credentials [davidsantoso]
+* GlobalCollect: Pass options to Refund [curiousepic] #2330
+* Kushki: Add new gateway [shasum] #2326
+* Kushki: Remove body from void call [shasum] #2348
+* Linkpoint: Raise ArgumentError when trying to instantiate without `:pem` [jasonwebster] #2329
+* Omise: Enable Japan, JPY and JCB support [zdk] #2284
+* PayU LATAM: Count pending refunds as succeeded [curiousepic] #2336
+* PayU LATAM: Let Refund take amount value [curiousepic] #2334
+* Paymill: Send new required fields on tokenization requests [tschelabaumann] #2279
+* Revert "Authorize.net: Allow settings to be passed for CIM purchases" [curiousepic] #2339
+* Sage: Default billing state when outside US [shasum] #2340
+* Stripe: Remove idempotency key from verify [shasum] #2335
+* TransFirst Transaction Express: Don't send order_id with refunds [curiousepic] #2350
+* TransFirst Transaction Express: Fix improper AVS and CVV response code mapping [shasum] #2342
+* WePay: Update API version [shasum] #2349
+* USA ePay Advanced: Add quick_update_customer action [joshreeves] #2229
+
+== Version 1.63.0 (February 2, 2017)
+* Authorize.net: Add #unstore support [jimryan] #2293
+* AuthorizeNet: Fix line items quirk [shasum]
+* CardStream: Add dynamic descriptor option fields [curiousepic]
+* CardStream: Support PEN currency [shasum]
+* Culqi: Add new gateway [shasum]
+* CyberSource: Add Lebanon to supported countries [shasum]
+* Element: Add AVS and CVV codes to response [shasum]
+* Firstdata E4 (Payeezy): Set correct ECI value for card present swipes [jasonwebster] #2318
+* GlobalCollect: On purchase skip capture if not required [davidsantoso]
+* PaymentExpress: Update supported countries [shasum]
+* Remove leading or trailing whitespace from credit card name [davidsantoso]
+* Remove support for Ruby 2.0 [jasonwebster]
+* Secure Pay AU: Add scrubbing support to Secure Pay AU [bruno] #2253
+* Stripe: Fix error in handling of track-only contactless EMV data [jasonwebster]
+* Vanco: Update test URL [davidsantoso]
+* WePay: Build fee structure correctly [curiousepic]
+* WePay: Remove null address fields from request [davidsantoso]
+* WePay: Update WePay to API version 2016-12-07 [davidsantoso]
+* Wirecard: Send customer data in requests [davidsantoso]
+* Worldpay: Add session id attribute [shasum]
+* Worldpay: Do not default address when not provided [shasum]
+
+== Version 1.62.0 (December 5, 2016)
+* AuthorizeNet: Map to standard AVSResult codes [shasum]
+* CitrusPay: Add 3DSecureId field [davidsantoso]
+* CyberSource: Only get alpha2 country code when it's a known country [bruno] #2238
+* Fat Zebra: Add scrubbing to Fat Zebra gateway [bruno] #2037
+* Monei: Add US and CA as new supported countries [davidgf] #2209
+* NAB Transact: Add scrubbing to NAB Transact [bruno] #2038
+* iATS: Add scrubbing support to iATS [bruno] #2228
+* Stripe: Ensure ECI values for tokenized cards are padded [jasonwebster] #2250
+* Forte: Fix incorrect authorization_code response mapping [davidsantoso]
+* maxiPago: Send currency with request [curiousepic]
+* Credorax: Map order_id to field H9 [curiousepic]
+* Authorize.net: Remove duplicate country GB [shasum]
+* PayU Latam: Add processWithoutCvv2 field [shasum]
+* Fat Zebra: De-nest soft descriptor fields [curiousepic]
+* Credorax: Only pass c5 field for billing address1 [davidsantoso]
+* Orbital: Add support for CLP currency [curiousepic]
+* Authorize.net: Add line item fields and additional transaction settings [shasum]
+* Authorize.net: Pass through `header_email_receipt` [shasum]
+* Stripe: Scrub additional network tokenization related sensitive data [jasonwebster] #2251
+* Applying: Worldpay: Format non-fractional currency amounts correctly [jasonwebster] #2267
+
+== Version 1.61.0 (November 7, 2016)
* Add codes AQ, BQ, SX, and SS to list of countries and update SD numeric code [zxlin]
-* Openpay: Add support for verify [duff]
-* Clearhaus: Fix refund of captures [duff]
-* TNS: Try TLS v1 [duff]
-* PaypalExpress: Add SoftDescriptor field [talyssonoc]
-* MiGS: Handle IDR currency [curiousepic]
-* Migs: Add support for void [mohsenottello]
-* Jetpay: Support endpoint for Canada [shasum]
-* Telr: Add gateway support [curiousepic]
-* Moneris: Fix unit test stubs [shasum]
-* Stripe: Support new network tokenization API params [methodmissing]
-* Vanco: Improve handling of success determination [duff]
-* PayU Latam: Add new gateway [shasum]
-* Find countries if they are differently cased [curiousepic]
-* CyberSource: Look up alpha2 country code [curiousepic]
-* MONEI: Update supported countries list [davidgf]
-* CitrusPay: Add gateway [duff]
-* TNS and CitrusPay: Update to version 36 of the API [duff]
-* TNS and CitrusPay: Support scrub and verify_credentials [duff]
+* AuthorizeNet: Update supported countries list [shasum]
+* Barclay SmartPay: Add support for credit [shasum]
+* Barclaycard SmartPay: Update supported countries [shasum]
+* BluePay: Add Canada to supported countries list [shasum]
+* BlueSnap: Update countries list [shasum]
+* Braintree Blue: Add Android Pay support [mrezentes]
+* Braintree Blue: Add remote test to verify card token [shasum]
+* Braintree Blue: Get Android Pay tx id from payment method, not options [mrezentes]
* CardStream: Add MXN currency code [curiousepic]
+* CardStream: Set captureDelay to zero on purchase [davidsantoso]
+* CitrusPay: Add gateway [duff]
+* CitrusPay: Update URL to current API version [davidsantoso]
+* Clearhaus: Fix refund of captures [duff]
+* Clearhaus: Update list of non fractal currencies [curiousepic]
+* Clearhaus: Use localized amount [curiouspic]
+* Conekta: Add void action [MauricioMurga]
+* Credorax: Add gateway support [davidsantoso]
+* CyberSource, Paymill, Payflow: Add verify_credentials [duff]
* CyberSource: Combine auth_reversal with Void [curiousepic]
-* SagePay: Fix truncation [duff]
+* CyberSource: Increase merchant defined data fields [davidsantoso]
+* CyberSource: Look up alpha2 country code [curiousepic]
* CyberSource: Use localized_amount [curiousepic]
-* CyberSource, Paymill, Payflow: Add verify_credentials [duff]
+* Element: Pass order_id and shipping address [curiousepic]
+* Fat Zebra: Add cavv, xid, and sli fields [curiousepic]
+* Fat Zebra: Fix improper descriptor nesting [curiousepic]
+* Find countries if they are differently cased [curiousepic]
+* GlobalCollect: Update credit card brand list [curiousepic]
+* Jetpay: Support endpoint for Canada [shasum]
+* Linkpoint: Clean whitespace from PEM [curiousepic]
* Litle: Retain amount to send in auth reversals [curiousepic]
+* Litle: add scrubbing support [bruno]
+* MONEI: Update supported countries list [davidgf]
+* MiGS: Handle IDR currency [curiousepic]
+* Migs: Add support for void [mohsenottello]
* Migs: Support some additional fields [duff]
-* Clearhaus: Use localized amount [curiouspic]
+* Moneris: Fix unit test stubs [shasum]
+* Moneris: add scrubbing support [bruno]
+* NMI, FirstData: Support verify_credentials [curiousepic]
+* Openpay: Add support for verify [duff]
* PayJunctionV2: Add gateway support [shasum]
+* PayU Latam: Add new gateway [shasum]
+* PayU Latam: Update supported countries list [shasum]
+* Payflow: Update supported countries list [shasum]
+* PaypalExpress: Add SoftDescriptor field [talyssonoc]
+* Redsys: Added DOP and CRC currency [davidsantoso]
+* Sage: Add support for scrubbing [bruno]
+* SagePay: Fix truncation [duff]
+* SecurionPay: Update supported countries list [shasum]
+* Stripe: Increase authorize amount during verify [davidsantoso]
+* Stripe: Set minimum authorize amount depending on currency [davidsantoso]
+* Stripe: Support new network tokenization API params [methodmissing]
+* Stripe: Update supported countries list [shasum]
+* TNS and CitrusPay: Support scrub and verify_credentials [duff]
+* TNS and CitrusPay: Update to version 36 of the API [duff]
+* TNS: Try TLS v1 [duff]
+* Telr: Add gateway support [curiousepic]
+* TransFirsTransactionExpress: Remove blank cvv element [davidsantoso]
+* TransFirsTransactionExpress: Take into account blank string CVV [davidsantoso]
+* Vanco: Improve handling of success determination [duff]
+* Worldpay: Add hcgAdditionalData element [davidsantoso]
+* Worldpay: Report error code [curiousepic]
== Version 1.60.0 (July 4, 2016)
* Orbital: Fix CC num leak on profile calls [drewblas]
@@ -1506,7 +1947,7 @@ value [jduff]
* Support default Return class for all Integrations that don't use returns [Soleone]
* Add support for passing additional options when creating a Notification to all Integrations [Soleone]
* Update BraintreeBlue#refund to have consistent method signature [Jonathan Rudenberg]
-* Add rails/init.rb for gem campatability in Rails [Rūdolfs Ošiņš]
+* Add rails/init.rb for gem campatability in Rails [R?dolfs O?i??]
* Fix Paypal Express response parser [Jonathan Rudenberg]
* Braintree/Transax: Add tax field [wisq]
@@ -1822,7 +2263,7 @@ value [jduff]
* Update Secure Pay Au to meet specs for MessageInfo elements [cody]
* Add support for the Australian Secure Pay payment gateway [cody]
* Allow LinkPoint cancellations for recurring billing. [yanagimoto.shin]
-* Add support for Åland Islands to the country list [cody]
+* Add support for ?land Islands to the country list [cody]
== Version 1.3.1 (January 28, 2008)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5a4336f7a0e..7d7989b171d 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -27,12 +27,7 @@ When submitting a pull request to resolve an issue:
## Gateway Placement within Shopify
-The addition of your gateway to active_merchant does not guarantee placement within Shopify. In order to have your gateway considered, please send an email to payment-integrations@shopify.com with **Active_Merchant Integration** in the subject. Be sure to include:
-
-1. Name, URL & description of the payment provider you wish to integrate
-2. Markets served by this integration
-3. List of major supported payment methods
-4. Your most recent Certificate of PCI Compliance
+Placement within Shopify is available by invitation only at this time.
## Version/Release Management
diff --git a/Gemfile b/Gemfile
index 7c62a122f4e..81f68d6a75e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -5,5 +5,5 @@ gem 'jruby-openssl', :platforms => :jruby
group :test, :remote_test do
# gateway-specific dependencies, keeping these gems out of the gemspec
- gem 'braintree', '>= 2.50.0'
+ gem 'braintree', '>= 2.78.0'
end
diff --git a/Gemfile.rails42 b/Gemfile.rails42
index 7cf96452cdf..15a8eca94bf 100644
--- a/Gemfile.rails42
+++ b/Gemfile.rails42
@@ -5,7 +5,7 @@ gem 'jruby-openssl', :platforms => :jruby
group :test, :remote_test do
# gateway-specific dependencies, keeping these gems out of the gemspec
- gem 'braintree', '>= 2.50.0'
+ gem 'braintree', '~> 2.78.0'
end
-gem 'rails', '~> 4.2.0'
+gem 'activesupport', '~> 4.2.0'
diff --git a/Gemfile.rails41 b/Gemfile.rails50
similarity index 75%
rename from Gemfile.rails41
rename to Gemfile.rails50
index 6a44812d025..43c4386c7bc 100644
--- a/Gemfile.rails41
+++ b/Gemfile.rails50
@@ -5,7 +5,7 @@ gem 'jruby-openssl', :platforms => :jruby
group :test, :remote_test do
# gateway-specific dependencies, keeping these gems out of the gemspec
- gem 'braintree', '>= 2.50.0'
+ gem 'braintree', '~> 2.78.0'
end
-gem 'rails', '~> 4.1.0'
+gem 'activesupport', '~> 5.0.0'
diff --git a/Gemfile.rails5 b/Gemfile.rails51
similarity index 59%
rename from Gemfile.rails5
rename to Gemfile.rails51
index a7bec76b4bc..4d709a44830 100644
--- a/Gemfile.rails5
+++ b/Gemfile.rails51
@@ -5,9 +5,7 @@ gem 'jruby-openssl', :platforms => :jruby
group :test, :remote_test do
# gateway-specific dependencies, keeping these gems out of the gemspec
- gem 'braintree', '>= 2.50.0'
+ gem 'braintree', '~> 2.78.0'
end
-gem 'rails', github: 'rails/rails'
-gem 'arel', github: 'rails/arel'
-gem 'rack', github: 'rack/rack'
+gem 'activesupport', '~> 5.1.0'
diff --git a/Gemfile.rails52 b/Gemfile.rails52
new file mode 100644
index 00000000000..256a25a2b21
--- /dev/null
+++ b/Gemfile.rails52
@@ -0,0 +1,11 @@
+source 'https://rubygems.org'
+gemspec
+
+gem 'jruby-openssl', :platforms => :jruby
+
+group :test, :remote_test do
+ # gateway-specific dependencies, keeping these gems out of the gemspec
+ gem 'braintree', '~> 2.78.0'
+end
+
+gem 'activesupport', '~> 5.2.0.rc1'
diff --git a/Gemfile.rails32 b/Gemfile.rails60
similarity index 75%
rename from Gemfile.rails32
rename to Gemfile.rails60
index cc1430f5681..0b2d9a429e2 100644
--- a/Gemfile.rails32
+++ b/Gemfile.rails60
@@ -5,7 +5,7 @@ gem 'jruby-openssl', :platforms => :jruby
group :test, :remote_test do
# gateway-specific dependencies, keeping these gems out of the gemspec
- gem 'braintree', '>= 2.50.0'
+ gem 'braintree', '~> 2.78.0'
end
-gem 'rails', '~> 3.2.0'
+gem 'activesupport', '~> 6.0'
diff --git a/Gemfile.rails40 b/Gemfile.rails70
similarity index 75%
rename from Gemfile.rails40
rename to Gemfile.rails70
index 26f25a01cee..653f39f0f23 100644
--- a/Gemfile.rails40
+++ b/Gemfile.rails70
@@ -5,7 +5,7 @@ gem 'jruby-openssl', :platforms => :jruby
group :test, :remote_test do
# gateway-specific dependencies, keeping these gems out of the gemspec
- gem 'braintree', '>= 2.50.0'
+ gem 'braintree', '~> 2.78.0'
end
-gem 'rails', '~> 4.0.0'
+gem 'activesupport', '~> 7.0'
diff --git a/README.md b/README.md
index ef7b5e218eb..0a06769b915 100644
--- a/README.md
+++ b/README.md
@@ -91,7 +91,6 @@ The [ActiveMerchant Wiki](http://github.com/activemerchant/active_merchant/wikis
* [Bank Frick](http://www.bankfrickacquiring.com/) - LI, US
* [Banwire](http://www.banwire.com/) - MX
* [Barclays ePDQ Extra Plus](http://www.barclaycard.co.uk/business/accepting-payments/epdq-ecomm/) - GB
-* [Barclays ePDQ MPI](http://www.barclaycard.co.uk/business/accepting-payments/epdq-mpi/) - GB
* [Be2Bill](http://www.be2bill.com/) - FR
* [Beanstream.com](http://www.beanstream.com/) - CA, US
* [BluePay](http://www.bluepay.com/) - US
@@ -150,7 +149,7 @@ The [ActiveMerchant Wiki](http://github.com/activemerchant/active_merchant/wikis
* [Metrics Global](http://www.metricsglobal.com) - US
* [MasterCard Internet Gateway Service (MiGS)](http://mastercard.com/mastercardsps) - AU, AE, BD, BN, EG, HK, ID, IN, JO, KW, LB, LK, MU, MV, MY, NZ, OM, PH, QA, SA, SG, TT, VN
* [Modern Payments](http://www.modpay.com) - US
-* [MONEI](http://www.monei.net/) - ES
+* [MONEI](http://www.monei.net/) - AD, AT, BE, BG, CA, 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, US, VA
* [Moneris](http://www.moneris.com/) - CA
* [Moneris (US)](http://www.monerisusa.com/) - US
* [MoneyMovers](http://mmoa.us/) - US
@@ -162,7 +161,7 @@ The [ActiveMerchant Wiki](http://github.com/activemerchant/active_merchant/wikis
* [NETPAY Gateway](http://www.netpay.com.mx) - MX
* [NMI](http://nmi.com/) - US
* [Ogone](http://www.ogone.com/) - BE, DE, FR, NL, AT, CH
-* [Omise](https://www.omise.co/) - TH
+* [Omise](https://www.omise.co/) - TH, JP
* [Openpay](Openpay) - MX
* [Optimal Payments](http://www.optimalpayments.com/) - CA, US, GB
* [Orbital Paymentech](http://chasepaymentech.com/) - US, CA
@@ -210,7 +209,6 @@ The [ActiveMerchant Wiki](http://github.com/activemerchant/active_merchant/wikis
* [SecurePayTech](http://www.securepaytech.com/) - NZ
* [SecurionPay](https://securionpay.com/) - AD, AE, AF, AG, AI, AL, AM, AO, AR, AS, AT, AU, AW, AX, AZ, BA, BB, BD, BE, BF, BG, BH, BI, BJ, BL, BM, BN, BO, BR, BS, BT, BV, BW, BY, BZ, CA, CC, CD, CF, CG, CH, CI, CK, CL, CM, CN, CO, CR, CU, CV, CW, CX, CY, CZ, DE, DJ, DK, DM, DO, DZ, EC, EE, EG, EH, ER, ES, ET, FI, FJ, FK, FM, FO, FR, GA, GB, GD, GE, GF, GG, GH, GI, GL, GM, GN, GP, GQ, GR, GS, GT, GU, GW, GY, HK, HM, HN, HR, HT, HU, ID, IE, IL, IM, IN, IO, IQ, IR, IS, IT, JE, JM, JO, JP, KE, KG, KH, KI, KM, KN, KP, KR, KW, KY, KZ, LA, LB, LC, LI, LK, LR, LS, LT, LU, LV, LY, MA, MC, MD, ME, MF, MG, MH, MK, ML, MM, MN, MO, MP, MQ, MR, MS, MT, MU, MV, MW, MX, MY, MZ, NA, NC, NE, NF, NG, NI, NL, NO, NP, NR, NU, NZ, OM, PA, PE, PF, PG, PH, PK, PL, PM, PN, PR, PS, PT, PW, PY, QA, RE, RO, RS, RU, RW, SA, SB, SC, SD, SE, SG, SH, SI, SJ, SK, SL, SM, SN, SO, SR, ST, SV, SY, SZ, TC, TD, TF, TG, TH, TJ, TK, TL, TM, TN, TO, TR, TT, TV, TW, TZ, UA, UG, UM, US, UY, UZ, VA, VC, VE, VG, VI, VN, VU, WF, WS, YE, YT, ZA, ZM, ZW
* [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/) - AT, AU, BE, CA, CH, DE, DK, ES, FI, FR, GB, IE, IT, LU, NL, NO, SE, SG, US
* [Swipe](https://www.swipehq.com/checkout) - CA, NZ
diff --git a/Rakefile b/Rakefile
index 4852f416101..dea40508ff7 100644
--- a/Rakefile
+++ b/Rakefile
@@ -13,6 +13,7 @@ require 'rake'
require 'rake/testtask'
require 'support/gateway_support'
require 'support/ssl_verify'
+require 'support/ssl_version'
require 'support/outbound_hosts'
require 'bundler/gem_tasks'
@@ -28,14 +29,12 @@ task :test => 'test:units'
namespace :test do
Rake::TestTask.new(:units) do |t|
t.pattern = 'test/unit/**/*_test.rb'
- t.ruby_opts << '-rubygems -w'
t.libs << 'test'
t.verbose = true
end
Rake::TestTask.new(:remote) do |t|
t.pattern = 'test/remote/**/*_test.rb'
- t.ruby_opts << '-rubygems -w'
t.libs << 'test'
t.verbose = true
end
@@ -91,8 +90,15 @@ namespace :gateways do
end
end
- desc 'Test that gateways allow SSL verify_peer'
- task :ssl_verify do
- SSLVerify.new.test_gateways
+ namespace :ssl do
+ desc 'Test that gateways allow SSL verify_peer'
+ task :verify do
+ SSLVerify.new.test_gateways
+ end
+
+ desc 'Test gateways minimal SSL version connection'
+ task :min_version do
+ SSLVersion.new.test_gateways
+ end
end
end
diff --git a/activemerchant.gemspec b/activemerchant.gemspec
index 5c3221359d4..a26bf8bad5e 100644
--- a/activemerchant.gemspec
+++ b/activemerchant.gemspec
@@ -14,14 +14,14 @@ Gem::Specification.new do |s|
s.homepage = 'http://activemerchant.org/'
s.rubyforge_project = 'activemerchant'
- s.required_ruby_version = '>= 2'
+ s.required_ruby_version = '>= 2.3'
s.files = Dir['CHANGELOG', 'README.md', 'MIT-LICENSE', 'CONTRIBUTORS', 'lib/**/*', 'vendor/**/*']
s.require_path = 'lib'
s.has_rdoc = true if Gem::VERSION < '1.7.0'
- s.add_dependency('activesupport', '>= 3.2.14', '< 5.1')
+ s.add_dependency('activesupport', '>= 4.2', '< 8')
s.add_dependency('i18n', '>= 0.6.9')
s.add_dependency('builder', '>= 2.1.2', '< 4.0.0')
s.add_dependency('nokogiri', "~> 1.4")
diff --git a/gemfiles/rails.6.0.gemfile b/gemfiles/rails.6.0.gemfile
new file mode 100644
index 00000000000..b2b43299c16
--- /dev/null
+++ b/gemfiles/rails.6.0.gemfile
@@ -0,0 +1,14 @@
+source "https://rubygems.org"
+
+gem "activesupport", "~> 6.0"
+gem "rexml", ">= 3.2.4"
+
+gem 'jruby-openssl', :platforms => :jruby
+
+group :test, :remote_test do
+ # gateway-specific dependencies, keeping these gems out of the gemspec
+ gem 'braintree', '~> 2.78.0'
+ gem "psych", "< 4"
+end
+
+gemspec path: "../"
diff --git a/gemfiles/rails.6.1.gemfile b/gemfiles/rails.6.1.gemfile
new file mode 100644
index 00000000000..baccd0e6340
--- /dev/null
+++ b/gemfiles/rails.6.1.gemfile
@@ -0,0 +1,14 @@
+source "https://rubygems.org"
+
+gem "activesupport", "~> 6.1"
+gem "rexml", ">= 3.2.4"
+
+gem 'jruby-openssl', :platforms => :jruby
+
+group :test, :remote_test do
+ # gateway-specific dependencies, keeping these gems out of the gemspec
+ gem 'braintree', '~> 2.78.0'
+ gem "psych", "< 4"
+end
+
+gemspec path: "../"
diff --git a/gemfiles/rails.7.0.gemfile b/gemfiles/rails.7.0.gemfile
new file mode 100644
index 00000000000..e0374be21af
--- /dev/null
+++ b/gemfiles/rails.7.0.gemfile
@@ -0,0 +1,15 @@
+source "https://rubygems.org"
+
+gem "activesupport", "~> 7.0"
+gem "rexml", ">= 3.2.4"
+
+gem 'jruby-openssl', :platforms => :jruby
+
+group :test, :remote_test do
+ # gateway-specific dependencies, keeping these gems out of the gemspec
+ gem 'braintree', '~> 2.78.0'
+ gem "psych", "< 4"
+end
+
+
+gemspec path: "../"
diff --git a/generators/gateway/templates/remote_gateway_test.rb b/generators/gateway/templates/remote_gateway_test.rb
index f397794e4e6..08262a97c93 100644
--- a/generators/gateway/templates/remote_gateway_test.rb
+++ b/generators/gateway/templates/remote_gateway_test.rb
@@ -43,7 +43,7 @@ def test_successful_authorize_and_capture
assert capture = @gateway.capture(@amount, auth.authorization)
assert_success capture
- assert_equal 'REPLACE WITH SUCCESS MESSAGE', response.message
+ assert_equal 'REPLACE WITH SUCCESS MESSAGE', capture.message
end
def test_failed_authorize
diff --git a/lib/active_merchant.rb b/lib/active_merchant.rb
index 66de1834685..b19a4725b37 100644
--- a/lib/active_merchant.rb
+++ b/lib/active_merchant.rb
@@ -28,11 +28,6 @@
require 'active_support/core_ext/object/conversions'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/enumerable'
-
-if(!defined?(ActiveSupport::VERSION) || (ActiveSupport::VERSION::STRING < "4.1"))
- require 'active_support/core_ext/class/attribute_accessors'
-end
-
require 'active_support/core_ext/module/attribute_accessors'
require 'base64'
@@ -42,6 +37,7 @@
require 'rexml/document'
require 'timeout'
require 'socket'
+require 'openssl'
require 'active_merchant/network_connection_retries'
require 'active_merchant/connection'
diff --git a/lib/active_merchant/billing/credit_card.rb b/lib/active_merchant/billing/credit_card.rb
index 30f763c367d..91b211bc6fd 100644
--- a/lib/active_merchant/billing/credit_card.rb
+++ b/lib/active_merchant/billing/credit_card.rb
@@ -176,21 +176,20 @@ def requires_verification_value?
# @return [String]
attr_accessor :icc_data
- # Returns or sets a fallback reason for a EMV transaction whereby the customer's card entered a fallback scenario.
- # This can be an arbitrary string.
+ # Returns or sets information about the source of the card data.
#
# @return [String]
- attr_accessor :fallback_reason
-
- # Returns or sets whether card-present EMV data has been read contactlessly.
- #
- # @return [true, false]
- attr_accessor :contactless_emv
-
- # Returns or sets whether card-present magstripe data has been read contactlessly.
- #
- # @return [true, false]
- attr_accessor :contactless_magstripe
+ attr_accessor :read_method
+
+ READ_METHOD_DESCRIPTIONS = {
+ nil => 'A card reader was not used.',
+ 'fallback_no_chip' => 'Magstripe was read because the card has no chip.',
+ 'fallback_chip_error' => "Magstripe was read because the card's chip failed.",
+ 'contactless' => 'Data was read by a Contactless EMV kernel. Issuer script results are not available.',
+ 'contactless_magstripe' => 'Contactless data was read with a non-EMV protocol.',
+ 'contact' => 'Data was read using the EMV protocol. Issuer script results may follow.',
+ 'contact_quickchip' => 'Data was read by the Quickchip EMV kernel. Issuer script results are not available.',
+ }
# Returns the ciphertext of the card's encrypted PIN.
#
@@ -245,7 +244,7 @@ def last_name?
#
# @return [String] the full name of the card holder
def name
- [first_name, last_name].compact.join(' ')
+ "#{first_name} #{last_name}".strip
end
def name=(full_name)
diff --git a/lib/active_merchant/billing/credit_card_methods.rb b/lib/active_merchant/billing/credit_card_methods.rb
index a76a148fadd..8dc47186c1b 100644
--- a/lib/active_merchant/billing/credit_card_methods.rb
+++ b/lib/active_merchant/billing/credit_card_methods.rb
@@ -14,7 +14,9 @@ module CreditCardMethods
'dankort' => /^5019\d{12}$/,
'maestro' => /^(5[06-8]|6\d)\d{10,17}$/,
'forbrugsforeningen' => /^600722\d{10}$/,
- 'laser' => /^(6304|6706|6709|6771(?!89))\d{8}(\d{4}|\d{6,7})?$/
+ 'laser' => /^(6304|6706|6709|6771(?!89))\d{8}(\d{4}|\d{6,7})?$/,
+ 'sodexo' => /^(606071|603389|606070|606069|606068|600818)\d{8}$/,
+ 'vr' => /^(627416|637036)\d{8}$/
}
# http://www.barclaycard.co.uk/business/files/bin_rules.pdf
diff --git a/lib/active_merchant/billing/gateway.rb b/lib/active_merchant/billing/gateway.rb
index 46d89a46910..6a150f9f9bd 100644
--- a/lib/active_merchant/billing/gateway.rb
+++ b/lib/active_merchant/billing/gateway.rb
@@ -65,10 +65,10 @@ class Gateway
#
# :incorrect_number - Card number does not comply with ISO/IEC 7812 numbering standard
# :invalid_number - Card number was not matched by processor
- # :invalid_expiry_date - Expiry date deos not match correct formatting
+ # :invalid_expiry_date - Expiry date does not match correct formatting
# :invalid_cvc - Security codes does not match correct format (3-4 digits)
# :expired_card - Card number is expired
- # :incorrect_cvc - Secerity code was not matched by the processor
+ # :incorrect_cvc - Security code was not matched by the processor
# :incorrect_zip - Zip code is not in correct format
# :incorrect_address - Billing address info was not matched by the processor
# :incorrect_pin - Card PIN is incorrect
@@ -77,6 +77,9 @@ class Gateway
# :call_issuer - Transaction requires voice authentication, call issuer
# :pickup_card - Issuer requests that you pickup the card from merchant
# :test_mode_live_card - Card was declined. Request was in test mode, but used a non test card.
+ # :unsupported_feature - Transaction failed due to gateway or merchant
+ # configuration not supporting a feature used, such
+ # as network tokenization.
STANDARD_ERROR_CODE = {
:incorrect_number => 'incorrect_number',
@@ -93,7 +96,8 @@ class Gateway
:call_issuer => 'call_issuer',
:pickup_card => 'pick_up_card',
:config_error => 'config_error',
- :test_mode_live_card => 'test_mode_live_card'
+ :test_mode_live_card => 'test_mode_live_card',
+ :unsupported_feature => 'unsupported_feature',
}
cattr_reader :implementations
@@ -121,8 +125,9 @@ def generate_unique_id
class_attribute :supported_cardtypes
self.supported_cardtypes = []
- class_attribute :currencies_without_fractions
- self.currencies_without_fractions = %w(BIF BYR CLP CVE DJF GNF HUF ISK JPY KMF KRW PYG RWF UGX VND VUV XAF XOF XPF)
+ class_attribute :currencies_without_fractions, :currencies_with_three_decimal_places
+ self.currencies_without_fractions = %w(BIF BYR CLP CVE DJF GNF ISK JPY KMF KRW PYG RWF UGX VND VUV XAF XOF XPF)
+ self.currencies_with_three_decimal_places = %w()
class_attribute :homepage_url
class_attribute :display_name
@@ -259,15 +264,26 @@ def non_fractional_currency?(currency)
self.currencies_without_fractions.include?(currency.to_s)
end
+ def three_decimal_currency?(currency)
+ self.currencies_with_three_decimal_places.include?(currency.to_s)
+ end
+
def localized_amount(money, currency)
amount = amount(money)
- return amount unless non_fractional_currency?(currency)
-
- if self.money_format == :cents
- sprintf("%.0f", amount.to_f / 100)
- else
- amount.split('.').first
+ return amount unless non_fractional_currency?(currency) || three_decimal_currency?(currency)
+ if non_fractional_currency?(currency)
+ if self.money_format == :cents
+ sprintf("%.0f", amount.to_f / 100)
+ else
+ amount.split('.').first
+ end
+ elsif three_decimal_currency?(currency)
+ if self.money_format == :cents
+ amount.to_s
+ else
+ sprintf("%.3f", (amount.to_f / 10))
+ end
end
end
diff --git a/lib/active_merchant/billing/gateways/adyen.rb b/lib/active_merchant/billing/gateways/adyen.rb
new file mode 100644
index 00000000000..744b68e9da0
--- /dev/null
+++ b/lib/active_merchant/billing/gateways/adyen.rb
@@ -0,0 +1,292 @@
+module ActiveMerchant #:nodoc:
+ module Billing #:nodoc:
+ class AdyenGateway < Gateway
+
+ # we recommend setting up merchant-specific endpoints.
+ # https://docs.adyen.com/developers/api-manual#apiendpoints
+ self.test_url = 'https://pal-test.adyen.com/pal/servlet/Payment/v18'
+ self.live_url = 'https://pal-live.adyen.com/pal/servlet/Payment/v18'
+
+ self.supported_countries = ['AT','AU','BE','BG','BR','CH','CY','CZ','DE','DK','EE','ES','FI','FR','GB','GI','GR','HK','HU','IE','IS','IT','LI','LT','LU','LV','MC','MT','MX','NL','NO','PL','PT','RO','SE','SG','SK','SI','US']
+ self.default_currency = 'USD'
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :jcb, :dankort, :maestro, :discover]
+
+ self.money_format = :cents
+
+ self.homepage_url = 'https://www.adyen.com/'
+ self.display_name = 'Adyen'
+
+ STANDARD_ERROR_CODE_MAPPING = {
+ '101' => STANDARD_ERROR_CODE[:incorrect_number],
+ '103' => STANDARD_ERROR_CODE[:invalid_cvc],
+ '131' => STANDARD_ERROR_CODE[:incorrect_address],
+ '132' => STANDARD_ERROR_CODE[:incorrect_address],
+ '133' => STANDARD_ERROR_CODE[:incorrect_address],
+ '134' => STANDARD_ERROR_CODE[:incorrect_address],
+ '135' => STANDARD_ERROR_CODE[:incorrect_address],
+ }
+
+ def initialize(options={})
+ requires!(options, :username, :password, :merchant_account)
+ @username, @password, @merchant_account = options.values_at(:username, :password, :merchant_account)
+ super
+ end
+
+ def purchase(money, payment, options={})
+ MultiResponse.run do |r|
+ r.process{authorize(money, payment, options)}
+ r.process{capture(money, r.authorization, options)}
+ end
+ end
+
+ def authorize(money, payment, options={})
+ requires!(options, :order_id)
+ post = init_post(options)
+ add_invoice(post, money, options)
+ add_payment(post, payment)
+ add_extra_data(post, options)
+ add_shopper_interaction(post, payment, options)
+ add_address(post, options)
+ commit('authorise', post)
+ end
+
+ def capture(money, authorization, options={})
+ post = init_post(options)
+ add_invoice_for_modification(post, money, options)
+ add_reference(post, authorization, options)
+ commit('capture', post)
+ end
+
+ def refund(money, authorization, options={})
+ post = init_post(options)
+ add_invoice_for_modification(post, money, options)
+ add_original_reference(post, authorization, options)
+ commit('refund', post)
+ end
+
+ def void(authorization, options={})
+ post = init_post(options)
+ add_reference(post, authorization, options)
+ commit('cancel', post)
+ end
+
+ def store(credit_card, options={})
+ requires!(options, :order_id)
+ post = init_post(options)
+ add_invoice(post, 0, options)
+ add_payment(post, credit_card)
+ add_extra_data(post, options)
+ add_recurring_contract(post, options)
+ add_address(post, options)
+ commit('authorise', post)
+ end
+
+ def verify(credit_card, options={})
+ MultiResponse.run(:use_first_response) do |r|
+ r.process { authorize(0, credit_card, options) }
+ r.process(:ignore_result) { void(r.authorization, options) }
+ end
+ end
+
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
+ gsub(%r(("number\\?":\\?")[^"]*)i, '\1[FILTERED]').
+ gsub(%r(("cvc\\?":\\?")[^"]*)i, '\1[FILTERED]')
+ end
+
+ private
+
+ def add_extra_data(post, options)
+ post[:shopperEmail] = options[:shopper_email] if options[:shopper_email]
+ post[:shopperIP] = options[:shopper_ip] if options[:shopper_ip]
+ post[:shopperReference] = options[:shopper_reference] if options[:shopper_reference]
+ post[:fraudOffset] = options[:fraud_offset] if options[:fraud_offset]
+ post[:selectedBrand] = options[:selected_brand] if options[:selected_brand]
+ post[:deliveryDate] = options[:delivery_date] if options[:delivery_date]
+ post[:merchantOrderReference] = options[:merchant_order_reference] if options[:merchant_order_reference]
+ end
+
+ def add_shopper_interaction(post, payment, options={})
+ if payment.respond_to?(:verification_value) && payment.verification_value
+ shopper_interaction = "Ecommerce"
+ else
+ shopper_interaction = "ContAuth"
+ end
+
+ post[:shopperInteraction] = options[:shopper_interaction] || shopper_interaction
+ end
+
+ def add_address(post, options)
+ return unless post[:card] && post[:card].kind_of?(Hash)
+ if (address = options[:billing_address] || options[:address]) && address[:country]
+ post[:card][:billingAddress] = {}
+ post[:card][:billingAddress][:street] = address[:address1] || 'N/A'
+ post[:card][:billingAddress][:houseNumberOrName] = address[:address2] || 'N/A'
+ post[:card][:billingAddress][:postalCode] = address[:zip] if address[:zip]
+ post[:card][:billingAddress][:city] = address[:city] || 'N/A'
+ post[:card][:billingAddress][:stateOrProvince] = address[:state] if address[:state]
+ post[:card][:billingAddress][:country] = address[:country] if address[:country]
+ end
+ end
+
+ def add_invoice(post, money, options)
+ amount = {
+ value: amount(money),
+ currency: options[:currency] || currency(money)
+ }
+ post[:amount] = amount
+ end
+
+ def add_invoice_for_modification(post, money, options)
+ amount = {
+ value: amount(money),
+ currency: options[:currency] || currency(money)
+ }
+ post[:modificationAmount] = amount
+ end
+
+ def add_payment(post, payment)
+ if payment.is_a?(String)
+ _, _, recurring_detail_reference = payment.split("#")
+ post[:selectedRecurringDetailReference] = recurring_detail_reference
+ add_recurring_contract(post, options)
+ else
+ add_card(post, payment)
+ end
+ end
+
+ def add_card(post, credit_card)
+ card = {
+ expiryMonth: credit_card.month,
+ expiryYear: credit_card.year,
+ holderName: credit_card.name,
+ number: credit_card.number,
+ cvc: credit_card.verification_value
+ }
+
+ card.delete_if{|k,v| v.blank? }
+ requires!(card, :expiryMonth, :expiryYear, :holderName, :number)
+ post[:card] = card
+ end
+
+ def add_reference(post, authorization, options = {})
+ _, psp_reference, _ = authorization.split("#")
+ post[:originalReference] = single_reference(authorization) || psp_reference
+ end
+
+ def add_original_reference(post, authorization, options = {})
+ original_psp_reference, _, _ = authorization.split("#")
+ post[:originalReference] = single_reference(authorization) || original_psp_reference
+ end
+
+ def single_reference(authorization)
+ authorization if !authorization.include?("#")
+ end
+
+ def add_recurring_contract(post, options = {})
+ recurring = {
+ contract: "RECURRING"
+ }
+
+ post[:recurring] = recurring
+ end
+
+ def parse(body)
+ return {} if body.blank?
+ JSON.parse(body)
+ end
+
+ def commit(action, parameters)
+ begin
+ raw_response = ssl_post("#{url}/#{action.to_s}", post_data(action, parameters), request_headers)
+ response = parse(raw_response)
+ rescue ResponseError => e
+ raw_response = e.response.body
+ response = parse(raw_response)
+ end
+
+ success = success_from(action, response)
+ Response.new(
+ success,
+ message_from(action, response),
+ response,
+ authorization: authorization_from(action, parameters, response),
+ test: test?,
+ error_code: success ? nil : error_code_from(response)
+ )
+
+ end
+
+ def url
+ if test?
+ test_url
+ elsif @options[:subdomain]
+ "https://#{@options[:subdomain]}-pal-live.adyenpayments.com/pal/servlet/Payment/v18"
+ else
+ live_url
+ end
+ end
+
+ def basic_auth
+ Base64.strict_encode64("#{@username}:#{@password}")
+ end
+
+ def request_headers
+ {
+ "Content-Type" => "application/json",
+ "Authorization" => "Basic #{basic_auth}"
+ }
+ end
+
+ def success_from(action, response)
+ case action.to_s
+ when 'authorise'
+ ['Authorised', 'Received', 'RedirectShopper'].include?(response['resultCode'])
+ when 'capture', 'refund', 'cancel'
+ response['response'] == "[#{action}-received]"
+ else
+ false
+ end
+ end
+
+ def message_from(action, response)
+ return authorize_message_from(response) if action.to_s == 'authorise'
+ response['response'] || response['message']
+ end
+
+ def authorize_message_from(response)
+ if response['refusalReason'] && response['additionalData'] && response['additionalData']['refusalReasonRaw']
+ "#{response['refusalReason']} | #{response['additionalData']['refusalReasonRaw']}"
+ else
+ response['refusalReason'] || response['resultCode'] || response['message']
+ end
+ end
+
+ def authorization_from(action, parameters, response)
+ return nil if response['pspReference'].nil?
+ recurring = response['additionalData']['recurring.recurringDetailReference'] if response['additionalData']
+ "#{parameters[:originalReference]}##{response['pspReference']}##{recurring}"
+ end
+
+ def init_post(options = {})
+ post = {}
+ post[:merchantAccount] = options[:merchant_account] || @merchant_account
+ post[:reference] = options[:order_id] if options[:order_id]
+ post
+ end
+
+ def post_data(action, parameters = {})
+ JSON.generate(parameters)
+ end
+
+ def error_code_from(response)
+ STANDARD_ERROR_CODE_MAPPING[response['errorCode']]
+ 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 b139819ad04..3cc43832175 100644
--- a/lib/active_merchant/billing/gateways/authorize_net.rb
+++ b/lib/active_merchant/billing/gateways/authorize_net.rb
@@ -8,7 +8,7 @@ class AuthorizeNetGateway < Gateway
self.test_url = 'https://apitest.authorize.net/xml/v1/request.api'
self.live_url = 'https://api2.authorize.net/xml/v1/request.api'
- self.supported_countries = %w(AD AT AU BE BG CA CH CY CZ DE DK EE ES FI FR GB GB GI GR HU IE IS IT LI LT LU LV MC MT NL NO PL PT RO SE SI SK SM TR US VA)
+ self.supported_countries = %w(AD AT AU BE BG CA CH CY CZ DE DK EE ES FI FR GB GI GR HU IE IL IS IT LI LT LU LV MC MT NL NO PL PT RO SE SI SK SM TR US VA)
self.default_currency = 'USD'
self.money_format = :dollars
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb, :maestro]
@@ -16,25 +16,45 @@ class AuthorizeNetGateway < Gateway
self.homepage_url = 'http://www.authorize.net/'
self.display_name = 'Authorize.Net'
+ # Authorize.net has slightly different definitions for returned AVS codes
+ # that have been mapped to the closest equivalent AM standard AVSResult codes
+ # Authorize.net's descriptions noted below
+ STANDARD_AVS_CODE_MAPPING = {
+ 'A' => 'A', # Street Address: Match -- First 5 Digits of ZIP: No Match
+ 'B' => 'I', # Address not provided for AVS check or street address match, postal code could not be verified
+ 'E' => 'E', # AVS Error
+ 'G' => 'G', # Non U.S. Card Issuing Bank
+ 'N' => 'N', # Street Address: No Match -- First 5 Digits of ZIP: No Match
+ 'P' => 'I', # AVS not applicable for this transaction
+ 'R' => 'R', # Retry, System Is Unavailable
+ 'S' => 'S', # AVS Not Supported by Card Issuing Bank
+ 'U' => 'U', # Address Information For This Cardholder Is Unavailable
+ 'W' => 'W', # Street Address: No Match -- All 9 Digits of ZIP: Match
+ 'X' => 'X', # Street Address: Match -- All 9 Digits of ZIP: Match
+ 'Y' => 'Y', # Street Address: Match - First 5 Digits of ZIP: Match
+ 'Z' => 'Z' # Street Address: No Match - First 5 Digits of ZIP: Match
+ }
+
STANDARD_ERROR_CODE_MAPPING = {
- '36' => STANDARD_ERROR_CODE[:incorrect_number],
- '237' => STANDARD_ERROR_CODE[:invalid_number],
- '2315' => STANDARD_ERROR_CODE[:invalid_number],
- '37' => STANDARD_ERROR_CODE[:invalid_expiry_date],
- '2316' => STANDARD_ERROR_CODE[:invalid_expiry_date],
- '378' => STANDARD_ERROR_CODE[:invalid_cvc],
- '38' => STANDARD_ERROR_CODE[:expired_card],
- '2317' => STANDARD_ERROR_CODE[:expired_card],
- '244' => STANDARD_ERROR_CODE[:incorrect_cvc],
- '227' => STANDARD_ERROR_CODE[:incorrect_address],
'2127' => STANDARD_ERROR_CODE[:incorrect_address],
'22' => STANDARD_ERROR_CODE[:card_declined],
+ '227' => STANDARD_ERROR_CODE[:incorrect_address],
'23' => STANDARD_ERROR_CODE[:card_declined],
- '3153' => STANDARD_ERROR_CODE[:processing_error],
+ '2315' => STANDARD_ERROR_CODE[:invalid_number],
+ '2316' => STANDARD_ERROR_CODE[:invalid_expiry_date],
+ '2317' => STANDARD_ERROR_CODE[:expired_card],
'235' => STANDARD_ERROR_CODE[:processing_error],
+ '237' => STANDARD_ERROR_CODE[:invalid_number],
'24' => STANDARD_ERROR_CODE[:pickup_card],
+ '244' => STANDARD_ERROR_CODE[:incorrect_cvc],
'300' => STANDARD_ERROR_CODE[:config_error],
- '384' => STANDARD_ERROR_CODE[:config_error]
+ '3153' => STANDARD_ERROR_CODE[:processing_error],
+ '3155' => STANDARD_ERROR_CODE[:unsupported_feature],
+ '36' => STANDARD_ERROR_CODE[:incorrect_number],
+ '37' => STANDARD_ERROR_CODE[:invalid_expiry_date],
+ '378' => STANDARD_ERROR_CODE[:invalid_cvc],
+ '38' => STANDARD_ERROR_CODE[:expired_card],
+ '384' => STANDARD_ERROR_CODE[:config_error],
}
MARKET_TYPE = {
@@ -61,7 +81,7 @@ class AuthorizeNetGateway < Gateway
TRANSACTION_ALREADY_ACTIONED = %w(310 311)
CARD_CODE_ERRORS = %w(N S)
- AVS_ERRORS = %w(A E N R W Z)
+ AVS_ERRORS = %w(A E I N R W Z)
AVS_REASON_CODES = %w(27 45)
TRACKS = {
@@ -72,6 +92,7 @@ class AuthorizeNetGateway < Gateway
APPLE_PAY_DATA_DESCRIPTOR = "COMMON.APPLE.INAPP.PAYMENT"
PAYMENT_METHOD_NOT_SUPPORTED_ERROR = "155"
+ INELIGIBLE_FOR_ISSUING_CREDIT_ERROR = "54"
def initialize(options={})
requires!(options, :login, :password)
@@ -111,11 +132,20 @@ def capture(amount, authorization, options={})
end
def refund(amount, authorization, options={})
- if auth_was_for_cim?(authorization)
+ response = if auth_was_for_cim?(authorization)
cim_refund(amount, authorization, options)
else
normal_refund(amount, authorization, options)
end
+
+ return response if response.success?
+ return response unless options[:force_full_refund_if_unsettled]
+
+ if response.params["response_reason_code"] == INELIGIBLE_FOR_ISSUING_CREDIT_ERROR
+ void(authorization, options)
+ else
+ response
+ end
end
def void(authorization, options={})
@@ -137,8 +167,9 @@ def credit(amount, payment, options={})
xml.transactionType('refundTransaction')
xml.amount(amount(amount))
- add_payment_source(xml, payment)
- add_invoice(xml, options)
+ add_payment_source(xml, payment, :credit)
+ xml.refTransId(transaction_id_from(options[:transaction_id])) if options[:transaction_id]
+ add_invoice(xml, 'refundTransaction', options)
add_customer_data(xml, payment, options)
add_settings(xml, payment, options)
add_user_fields(xml, amount, options)
@@ -161,6 +192,12 @@ def store(credit_card, options = {})
end
end
+ def unstore(authorization)
+ customer_profile_id, _, _ = split_authorization(authorization)
+
+ delete_customer_profile(customer_profile_id)
+ end
+
def verify_credentials
response = commit(:verify_credentials) { }
response.success?
@@ -210,7 +247,12 @@ def add_auth_purchase(xml, transaction_type, amount, payment, options)
xml.transactionType(transaction_type)
xml.amount(amount(amount))
add_payment_source(xml, payment)
- add_invoice(xml, options)
+ add_invoice(xml, transaction_type, options)
+ add_tax_fields(xml, options)
+ add_duty_fields(xml, options)
+ add_shipping_fields(xml, options)
+ add_tax_exempt_status(xml, options)
+ add_po_number(xml, options)
add_customer_data(xml, payment, options)
add_market_type_device_type(xml, payment, options)
add_settings(xml, payment, options)
@@ -223,8 +265,12 @@ def add_cim_auth_purchase(xml, transaction_type, amount, payment, options)
xml.transaction do
xml.send(transaction_type) do
xml.amount(amount(amount))
+ add_tax_fields(xml, options)
+ add_shipping_fields(xml, options)
+ add_duty_fields(xml, options)
add_payment_source(xml, payment)
- add_invoice(xml, options)
+ add_invoice(xml, transaction_type, options)
+ add_tax_exempt_status(xml, options)
end
end
end
@@ -235,6 +281,9 @@ def cim_capture(amount, authorization, options)
xml.transaction do
xml.profileTransPriorAuthCapture do
xml.amount(amount(amount))
+ add_tax_fields(xml, options)
+ add_shipping_fields(xml, options)
+ add_duty_fields(xml, options)
xml.transId(transaction_id_from(authorization))
end
end
@@ -247,8 +296,13 @@ def normal_capture(amount, authorization, options)
xml.transactionRequest do
xml.transactionType('priorAuthCaptureTransaction')
xml.amount(amount(amount))
+ add_tax_fields(xml, options)
+ add_duty_fields(xml, options)
+ add_shipping_fields(xml, options)
+ add_tax_exempt_status(xml, options)
+ add_po_number(xml, options)
xml.refTransId(transaction_id_from(authorization))
- add_invoice(xml, options)
+ add_invoice(xml, "capture", options)
add_user_fields(xml, amount, options)
end
end
@@ -262,8 +316,11 @@ def cim_refund(amount, authorization, options)
xml.transaction do
xml.profileTransRefund do
xml.amount(amount(amount))
+ add_tax_fields(xml, options)
+ add_shipping_fields(xml, options)
+ add_duty_fields(xml, options)
xml.creditCardNumberMasked(card_number)
- add_invoice(xml, options)
+ add_invoice(xml, "profileTransRefund", options)
xml.transId(transaction_id)
end
end
@@ -285,7 +342,12 @@ def normal_refund(amount, authorization, options)
end
xml.refTransId(transaction_id)
- add_invoice(xml, options)
+ add_invoice(xml, 'refundTransaction', options)
+ add_tax_fields(xml, options)
+ add_duty_fields(xml, options)
+ add_shipping_fields(xml, options)
+ add_tax_exempt_status(xml, options)
+ add_po_number(xml, options)
add_customer_data(xml, nil, options)
add_user_fields(xml, amount, options)
end
@@ -313,7 +375,7 @@ def normal_void(authorization, options)
end
end
- def add_payment_source(xml, source)
+ def add_payment_source(xml, source, action = nil)
return unless source
if source.is_a?(String)
add_token_payment_method(xml, source)
@@ -322,10 +384,14 @@ def add_payment_source(xml, source)
elsif card_brand(source) == 'apple_pay'
add_apple_pay_payment_token(xml, source)
else
- add_credit_card(xml, source)
+ add_credit_card(xml, source, action)
end
end
+ def camel_case_lower(key)
+ String(key).split('_').inject([]){ |buffer,e| buffer.push(buffer.empty? ? e : e.capitalize) }.join
+ end
+
def add_settings(xml, source, options)
xml.transactionSettings do
if options[:recurring]
@@ -346,6 +412,24 @@ def add_settings(xml, source, options)
ActiveMerchant.deprecated "Using the duplicate_window class_attribute is deprecated. Use the transaction options hash instead."
set_duplicate_window(xml, self.class.duplicate_window)
end
+ if options[:email_customer]
+ xml.setting do
+ xml.settingName("emailCustomer")
+ xml.settingValue("true")
+ end
+ end
+ if options[:header_email_receipt]
+ xml.setting do
+ xml.settingName("headerEmailReceipt")
+ xml.settingValue(options[:header_email_receipt])
+ end
+ end
+ if options[:test_request]
+ xml.setting do
+ xml.settingName("testRequest")
+ xml.settingValue("1")
+ end
+ end
end
end
@@ -373,7 +457,7 @@ def add_user_fields(xml, amount, options)
end
end
- def add_credit_card(xml, credit_card)
+ def add_credit_card(xml, credit_card, action)
if credit_card.track_data
add_swipe_data(xml, credit_card)
else
@@ -384,7 +468,7 @@ def add_credit_card(xml, credit_card)
if credit_card.valid_card_verification_value?(credit_card.verification_value, credit_card.brand)
xml.cardCode(credit_card.verification_value)
end
- if credit_card.is_a?(NetworkTokenizationCreditCard)
+ if credit_card.is_a?(NetworkTokenizationCreditCard) && action != :credit
xml.cryptogram(credit_card.payment_cryptogram)
end
end
@@ -458,7 +542,7 @@ def add_check(xml, check)
def add_customer_data(xml, payment_source, options)
xml.customer do
- xml.id(options[:customer]) unless empty?(options[:customer]) || options[:customer] !~ /^\d+$/
+ xml.id(options[:customer]) unless empty?(options[:customer]) || options[:customer] !~ /^\w+$/
xml.email(options[:email]) unless empty?(options[:email])
end
@@ -478,13 +562,15 @@ def add_billing_address(xml, payment_source, options)
xml.billTo do
first_name, last_name = names_from(payment_source, address, options)
+ state = state_from(address, options)
+ full_address = "#{address[:address1]} #{address[:address2]}".strip
+
xml.firstName(truncate(first_name, 50)) unless empty?(first_name)
xml.lastName(truncate(last_name, 50)) unless empty?(last_name)
-
xml.company(truncate(address[:company], 50)) unless empty?(address[:company])
- xml.address(truncate(address[:address1], 60))
+ xml.address(truncate(full_address, 60))
xml.city(truncate(address[:city], 40))
- xml.state(empty?(address[:state]) ? 'n/a' : truncate(address[:state], 40))
+ xml.state(truncate(state, 40))
xml.zip(truncate((address[:zip] || options[:zip]), 20))
xml.country(truncate(address[:country], 60))
xml.phoneNumber(truncate(address[:phone], 25)) unless empty?(address[:phone])
@@ -502,12 +588,12 @@ def add_shipping_address(xml, options, root_node="shipTo")
else
[address[:first_name], address[:last_name]]
end
+ full_address = "#{address[:address1]} #{address[:address2]}".strip
xml.firstName(truncate(first_name, 50)) unless empty?(first_name)
xml.lastName(truncate(last_name, 50)) unless empty?(last_name)
-
xml.company(truncate(address[:company], 50)) unless empty?(address[:company])
- xml.address(truncate(address[:address1], 60))
+ xml.address(truncate(full_address, 60))
xml.city(truncate(address[:city], 40))
xml.state(truncate(address[:state], 40))
xml.zip(truncate(address[:zip], 20))
@@ -520,13 +606,68 @@ def add_order_id(xml, options)
xml.refId(truncate(options[:order_id], 20))
end
- def add_invoice(xml, options)
+ def add_invoice(xml, transaction_type, options)
xml.order do
xml.invoiceNumber(truncate(options[:order_id], 20))
xml.description(truncate(options[:description], 255))
+ xml.purchaseOrderNumber(options[:po_number]) if options[:po_number] && transaction_type.start_with?("profileTrans")
+ end
+
+ # Authorize.net API requires lineItems to be placed directly after order tag
+ if options[:line_items]
+ xml.lineItems do
+ options[:line_items].each do |line_item|
+ xml.lineItem do
+ line_item.each do |key, value|
+ xml.send(camel_case_lower(key), value)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ def add_tax_fields(xml, options)
+ tax = options[:tax]
+ if tax.is_a?(Hash)
+ xml.tax do
+ xml.amount(amount(tax[:amount].to_i))
+ xml.name(tax[:name])
+ xml.description(tax[:description])
+ end
+ end
+ end
+
+ def add_duty_fields(xml, options)
+ duty = options[:duty]
+ if duty.is_a?(Hash)
+ xml.duty do
+ xml.amount(amount(duty[:amount].to_i))
+ xml.name(duty[:name])
+ xml.description(duty[:description])
+ end
end
end
+ def add_shipping_fields(xml, options)
+ shipping = options[:shipping]
+ if shipping.is_a?(Hash)
+ xml.shipping do
+ xml.amount(amount(shipping[:amount].to_i))
+ xml.name(shipping[:name])
+ xml.description(shipping[:description])
+ end
+ end
+ end
+
+ def add_tax_exempt_status(xml, options)
+ xml.taxExempt(options[:tax_exempt]) if options[:tax_exempt]
+ end
+
+ def add_po_number(xml, options)
+ xml.poNumber(options[:po_number]) if options[:po_number]
+ end
+
def create_customer_payment_profile(credit_card, options)
commit(:cim_store_update) do |xml|
xml.customerProfileId options[:customer_profile_id]
@@ -566,6 +707,11 @@ def create_customer_profile(credit_card, options)
end
end
+ def delete_customer_profile(customer_profile_id)
+ commit(:cim_store_delete_customer) do |xml|
+ xml.customerProfileId(customer_profile_id)
+ end
+ end
def names_from(payment_source, address, options)
if payment_source && !payment_source.is_a?(PaymentToken) && !payment_source.is_a?(String)
@@ -576,6 +722,14 @@ def names_from(payment_source, address, options)
end
end
+ def state_from(address, options)
+ if ["US", "CA"].include?(address[:country])
+ address[:state] || 'NC'
+ else
+ address[:state] || 'n/a'
+ end
+ end
+
def headers
{ 'Content-Type' => 'text/xml' }
end
@@ -596,7 +750,8 @@ def commit(action, &payload)
raw_response = ssl_post(url, post_data(action, &payload), headers)
response = parse(action, raw_response)
- avs_result = AVSResult.new(code: response[:avs_result_code])
+ avs_result_code = response[:avs_result_code].upcase if response[:avs_result_code]
+ avs_result = AVSResult.new(code: STANDARD_AVS_CODE_MAPPING[avs_result_code])
cvv_result = CVVResult.new(response[:card_code])
if using_live_gateway_in_test_mode?(response)
Response.new(false, "Using a live Authorize.net account in Test Mode is not permitted.")
@@ -633,6 +788,8 @@ def root_for(action)
"createCustomerProfileRequest"
elsif action == :cim_store_update
"createCustomerPaymentProfileRequest"
+ elsif action == :cim_store_delete_customer
+ "deleteCustomerProfileRequest"
elsif action == :verify_credentials
"authenticateTestRequest"
elsif is_cim_action?(action)
@@ -748,7 +905,7 @@ def success_from(action, response)
if cim?(action) || (action == :verify_credentials)
response[:result_code] == "Ok"
else
- response[:response_code] == APPROVED && TRANSACTION_ALREADY_ACTIONED.exclude?(response[:response_reason_code])
+ [APPROVED, FRAUD_REVIEW].include?(response[:response_code]) && TRANSACTION_ALREADY_ACTIONED.exclude?(response[:response_reason_code])
end
end
@@ -777,7 +934,7 @@ def split_authorization(authorization)
end
def cim?(action)
- (action == :cim_store) || (action == :cim_store_update)
+ (action == :cim_store) || (action == :cim_store_update) || (action == :cim_store_delete_customer)
end
def transaction_id_from(authorization)
diff --git a/lib/active_merchant/billing/gateways/authorize_net_cim.rb b/lib/active_merchant/billing/gateways/authorize_net_cim.rb
index d4c59ad12a7..c89f482a089 100644
--- a/lib/active_merchant/billing/gateways/authorize_net_cim.rb
+++ b/lib/active_merchant/billing/gateways/authorize_net_cim.rb
@@ -858,7 +858,9 @@ def commit(action, request)
response_params = parse(action, xml)
- message = response_params['messages']['message']['text']
+ message_element= response_params["messages"]["message"]
+ first_error = message_element.is_a?(Array) ? message_element.first : message_element
+ message = first_error['text']
test_mode = @options[:test_requests] || message =~ /Test Mode/
success = response_params['messages']['result_code'] == 'Ok'
response_params['direct_response'] = parse_direct_response(response_params['direct_response']) if response_params['direct_response']
@@ -867,7 +869,7 @@ def commit(action, request)
response_options = {}
response_options[:test] = test_mode
response_options[:authorization] = transaction_id || response_params['customer_profile_id'] || (response_params['profile'] ? response_params['profile']['customer_profile_id'] : nil)
- response_options[:error_code] = response_params['messages']['message']['code'] unless success
+ response_options[:error_code] = first_error['code'] unless success
Response.new(success, message, response_params, response_options)
end
diff --git a/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb b/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb
index 4afc9329483..513821bd95e 100644
--- a/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb
+++ b/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb
@@ -4,8 +4,9 @@ class BarclaycardSmartpayGateway < Gateway
self.test_url = 'https://pal-test.barclaycardsmartpay.com/pal/servlet'
self.live_url = 'https://pal-live.barclaycardsmartpay.com/pal/servlet'
- self.supported_countries = ['AR', 'AT', 'BE', 'BR', 'CA', 'CH', 'CL', 'CN', 'CO', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'HK', 'ID', 'IE', 'IL', 'IN', 'IT', 'JP', 'KR', 'LU', 'MX', 'MY', 'NL', 'NO', 'PA', 'PE', 'PH', 'PL', 'PT', 'RU', 'SE', 'SG', 'TH', 'TR', 'TW', 'US', 'VN', 'ZA']
+ self.supported_countries = ['AL', 'AD', 'AM', 'AT', 'AZ', 'BY', 'BE', 'BA', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IS', 'IE', 'IT', 'KZ', 'LV', 'LI', 'LT', 'LU', 'MK', 'MT', 'MD', 'MC', 'ME', 'NL', 'NO', 'PL', 'PT', 'RO', 'RU', 'SM', 'RS', 'SK', 'SI', 'ES', 'SE', 'CH', 'TR', 'UA', 'GB', 'VA']
self.default_currency = 'EUR'
+ self.currencies_with_three_decimal_places = %w(BHD KWD OMR RSD TND)
self.money_format = :cents
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb, :dankort, :maestro]
@@ -32,15 +33,9 @@ def authorize(money, creditcard, options = {})
post = payment_request(money, options)
post[:amount] = amount_hash(money, options[:currency])
post[:card] = credit_card_hash(creditcard)
-
- if address = (options[:billing_address] || options[:address])
- post[:billingAddress] = address_hash(address)
- end
-
- if options[:shipping_address]
- post[:deliveryAddress] = address_hash(options[:shipping_address])
- end
-
+ post[:billingAddress] = billing_address_hash(options) if options[:billing_address]
+ post[:deliveryAddress] = shipping_address_hash(options) if options[:shipping_address]
+ add_3ds(post, options) if options[:execute_threed]
commit('authorise', post)
end
@@ -62,6 +57,18 @@ def refund(money, authorization, options = {})
commit('refund', post)
end
+ def credit(money, creditcard, options = {})
+ post = payment_request(money, options)
+ post[:amount] = amount_hash(money, options[:currency])
+ post[:card] = credit_card_hash(creditcard)
+ post[:dateOfBirth] = options[:date_of_birth] if options[:date_of_birth]
+ post[:entityType] = options[:entity_type] if options[:entity_type]
+ post[:nationality] = options[:nationality] if options[:nationality]
+ post[:shopperName] = options[:shopper_name] if options[:shopper_name]
+
+ commit('refundWithData', post)
+ end
+
def void(identification, options = {})
requires!(options, :order_id)
@@ -130,13 +137,15 @@ def commit(action, post)
response,
test: test?,
avs_result: AVSResult.new(:code => parse_avs_code(response)),
- authorization: response['recurringDetailReference'] || response['pspReference']
+ authorization: response['recurringDetailReference'] || authorization_from(post, response)
)
rescue ResponseError => e
case e.response.code
when '401'
return Response.new(false, 'Invalid credentials', {}, :test => test?)
+ when '403'
+ return Response.new(false, 'Not allowed', {}, :test => test?)
when '422'
return Response.new(false, 'Unprocessable Entity', {}, :test => test?)
when '500'
@@ -147,6 +156,13 @@ def commit(action, post)
raise
end
+ def authorization_from(parameters, response)
+ authorization = [parameters[:originalReference], response['pspReference']].compact
+
+ return nil if authorization.empty?
+ return authorization.join("#")
+ end
+
def parse_avs_code(response)
AVS_MAPPING[response["avsResult"][0..1].strip] if response["avsResult"]
end
@@ -196,8 +212,9 @@ def message_from(response)
end
def success_from(response)
- return true if response.has_key?('authCode')
return true if response['result'] == 'Success'
+ return true if response['resultCode'] == 'Authorised'
+ return true if response['resultCode'] == 'Received'
successful_responses = %w([capture-received] [cancel-received] [refund-received])
successful_responses.include?(response['response'])
end
@@ -206,30 +223,57 @@ def build_url(action)
case action
when 'store'
"#{test? ? self.test_url : self.live_url}/Recurring/v12/storeToken"
+ when 'finalize3ds'
+ "#{test? ? self.test_url : self.live_url}/Payment/v12/authorise3d"
else
"#{test? ? self.test_url : self.live_url}/Payment/v12/#{action}"
end
end
- def address_hash(address)
- full_address = "#{address[:address1]} #{address[:address2]}" if address
- street = address[:street] if address[:street]
- house = address[:houseNumberOrName] if address[:houseNumberOrName]
+ def billing_address_hash(options)
+ address = options[:address] || options[:billing_address] if options[:address] || options[:billing_address]
+ street = options[:street] || parse_street(address)
+ house = options[:house_number] || parse_house_number(address)
+ create_address_hash(address, house, street)
+ end
+
+ def shipping_address_hash(options)
+ address = options[:shipping_address]
+ street = options[:shipping_street] || parse_street(address)
+ house = options[:shipping_house_number] || parse_house_number(address)
+
+ create_address_hash(address, house, street)
+ end
+
+ def parse_street(address)
+ address_to_parse = "#{address[:address1]} #{address[:address2]}"
+ street = address[:street] || address_to_parse.split(/\s+/).keep_if { |x| x !~ /\d/ }.join(' ')
+ street.empty? ? "Not Provided" : street
+ end
+
+ def parse_house_number(address)
+ address_to_parse = "#{address[:address1]} #{address[:address2]}"
+ house = address[:houseNumberOrName] || address_to_parse.split(/\s+/).keep_if { |x| x =~ /\d/ }.join(' ')
+ house.empty? ? "Not Provided" : house
+ end
+
+ def create_address_hash(address, house, street)
hash = {}
+ hash[:houseNumberOrName] = house
+ hash[:street] = street
hash[:city] = address[:city] if address[:city]
- hash[:street] = street || full_address.split(/\s+/).keep_if { |x| x !~ /\d/ }.join(' ')
- hash[:houseNumberOrName] = house || full_address.split(/\s+/).keep_if { |x| x =~ /\d/ }.join(' ')
- hash[:postalCode] = address[:zip] if address[:zip]
hash[:stateOrProvince] = address[:state] if address[:state]
+ hash[:postalCode] = address[:zip] if address[:zip]
hash[:country] = address[:country] if address[:country]
hash
end
def amount_hash(money, currency)
+ currency = currency || currency(money)
hash = {}
- hash[:currency] = currency || currency(money)
- hash[:value] = amount(money) if money
+ hash[:currency] = currency
+ hash[:value] = localized_amount(money, currency) if money
hash
end
@@ -246,10 +290,14 @@ def credit_card_hash(creditcard)
def modification_request(reference, options)
hash = {}
hash[:merchantAccount] = @options[:merchant]
- hash[:originalReference] = reference if reference
+ hash[:originalReference] = psp_reference_from(reference)
hash.keep_if { |_, v| v }
end
+ def psp_reference_from(authorization)
+ authorization.nil? ? nil : authorization.split("#").first
+ end
+
def payment_request(money, options)
hash = {}
hash[:merchantAccount] = @options[:merchant]
@@ -267,6 +315,11 @@ def store_request(options)
hash[:shopperReference] = options[:customer] if options[:customer]
hash.keep_if { |_, v| v }
end
+
+ def add_3ds(post, options)
+ post[:additionalData] = { executeThreeD: 'true' }
+ post[:browserInfo] = { userAgent: options[:user_agent], acceptHeader: options[:accept_header] }
+ end
end
end
end
diff --git a/lib/active_merchant/billing/gateways/barclays_epdq.rb b/lib/active_merchant/billing/gateways/barclays_epdq.rb
deleted file mode 100644
index 4349f0726a7..00000000000
--- a/lib/active_merchant/billing/gateways/barclays_epdq.rb
+++ /dev/null
@@ -1,314 +0,0 @@
-module ActiveMerchant #:nodoc:
- module Billing #:nodoc:
- class BarclaysEpdqGateway < Gateway
- self.test_url = 'https://secure2.mde.epdq.co.uk:11500'
- self.live_url = 'https://secure2.epdq.co.uk:11500'
-
- self.supported_countries = ['GB']
- self.default_currency = 'GBP'
- self.supported_cardtypes = [:visa, :master, :american_express, :maestro, :switch ]
- self.money_format = :cents
- self.homepage_url = 'http://www.barclaycard.co.uk/business/accepting-payments/epdq-mpi/'
- self.display_name = 'Barclays ePDQ MPI'
-
- def initialize(options = {})
- requires!(options, :login, :password, :client_id)
- super
- end
-
- def authorize(money, creditcard, options = {})
- document = Document.new(self, @options) do
- add_order_form(options[:order_id]) do
- add_consumer(options) do
- add_creditcard(creditcard)
- end
- add_transaction(:PreAuth, money)
- end
- end
-
- commit(document)
- end
-
- def purchase(money, creditcard, options = {})
- # disable fraud checks if this is a repeat order:
- if options[:payment_number] && (options[:payment_number] > 1)
- no_fraud = true
- else
- no_fraud = options[:no_fraud]
- end
- document = Document.new(self, @options, :no_fraud => no_fraud) do
- add_order_form(options[:order_id], options[:group_id]) do
- add_consumer(options) do
- add_creditcard(creditcard)
- end
- add_transaction(:Auth, money, options)
- end
- end
- commit(document)
- end
-
- # authorization is your unique order ID, not the authorization
- # code returned by ePDQ
- def capture(money, authorization, options = {})
- document = Document.new(self, @options) do
- add_order_form(authorization) do
- add_transaction(:PostAuth, money)
- end
- end
-
- commit(document)
- end
-
- # authorization is your unique order ID, not the authorization
- # code returned by ePDQ
- def credit(money, creditcard_or_authorization, options = {})
- if creditcard_or_authorization.is_a?(String)
- ActiveMerchant.deprecated CREDIT_DEPRECATION_MESSAGE
- refund(money, creditcard_or_authorization, options)
- else
- credit_new_order(money, creditcard_or_authorization, options)
- end
- end
-
- def refund(money, authorization, options = {})
- credit_existing_order(money, authorization, options)
- end
-
- def void(authorization, options = {})
- document = Document.new(self, @options) do
- add_order_form(authorization) do
- add_transaction(:Void)
- end
- end
-
- commit(document)
- end
-
- private
- def credit_new_order(money, creditcard, options)
- document = Document.new(self, @options) do
- add_order_form do
- add_consumer(options) do
- add_creditcard(creditcard)
- end
- add_transaction(:Credit, money)
- end
- end
-
- commit(document)
- end
-
- def credit_existing_order(money, authorization, options)
- order_id, _ = authorization.split(":")
- document = Document.new(self, @options) do
- add_order_form(order_id) do
- add_transaction(:Credit, money)
- end
- end
-
- commit(document)
- end
-
- def parse(body)
- parser = Parser.new(body)
- response = parser.parse
- Response.new(response[:success], response[:message], response,
- :test => test?,
- :authorization => response[:authorization],
- :avs_result => response[:avsresponse],
- :cvv_result => response[:cvv_result],
- :order_id => response[:order_id],
- :raw_response => response[:raw_response]
- )
- end
-
- def commit(document)
- url = (test? ? self.test_url : self.live_url)
- data = ssl_post(url, document.to_xml)
- parse(data)
- end
-
- class Parser
- def initialize(response)
- @response = response
- end
-
- def parse
- require 'iconv' unless String.method_defined?(:encode)
- if String.method_defined?(:encode)
- doc = REXML::Document.new(@response.encode("UTF-8", "ISO-8859-1"))
- else
- ic = Iconv.new('UTF-8', 'ISO-8859-1')
- doc = REXML::Document.new(ic.iconv(@response))
- end
-
- auth_type = find(doc, "//Transaction/Type").to_s
-
- message = find(doc, "//Message/Text")
- if message.blank?
- message = find(doc, "//Transaction/CardProcResp/CcReturnMsg")
- end
-
- case auth_type
- when 'Credit', 'Void'
- success = find(doc, "//CcReturnMsg") == "Approved."
- else
- success = find(doc, "//Transaction/AuthCode").present?
- end
-
- {
- :success => success,
- :message => message,
- :transaction_id => find(doc, "//Transaction/Id"),
- :avs_result => find(doc, "//Transaction/AvsRespCode"),
- :cvv_result => find(doc, "//Transaction/Cvv2Resp"),
- :authorization => find(doc, "//OrderFormDoc/Id"),
- :raw_response => @response
- }
- end
-
- def find(doc, xpath)
- REXML::XPath.first(doc, xpath).try(:text)
- end
- end
-
- class Document
- attr_reader :type, :xml
-
- PAYMENT_INTERVALS = {
- :days => 'D',
- :months => 'M'
- }
-
- EPDQ_CARD_TYPES = {
- :visa => 1,
- :master => 2,
- :switch => 9,
- :maestro => 10,
- }
-
- def initialize(gateway, options = {}, document_options = {}, &block)
- @gateway = gateway
- @options = options
- @document_options = document_options
- @xml = Builder::XmlMarkup.new(:indent => 2)
- build(&block)
- end
-
- def to_xml
- @xml.target!
- end
-
- def build(&block)
- xml.instruct!(:xml, :version => '1.0')
- xml.EngineDocList do
- xml.DocVersion "1.0"
- xml.EngineDoc do
- xml.ContentType "OrderFormDoc"
- xml.User do
- xml.Name(@options[:login])
- xml.Password(@options[:password])
- xml.ClientId({ :DataType => "S32" }, @options[:client_id])
- end
- xml.Instructions do
- if @document_options[:no_fraud]
- xml.Pipeline "PaymentNoFraud"
- else
- xml.Pipeline "Payment"
- end
- end
- instance_eval(&block)
- end
- end
- end
-
- def add_order_form(order_id=nil, group_id=nil, &block)
- xml.OrderFormDoc do
- xml.Mode 'P'
- xml.Id(order_id) if order_id
- xml.GroupId(group_id) if group_id
- instance_eval(&block)
- end
- end
-
- def add_consumer(options=nil, &block)
- xml.Consumer do
- if options
- xml.Email(options[:email]) if options[:email]
- billing_address = options[:billing_address] || options[:address]
- if billing_address
- xml.BillTo do
- xml.Location do
- xml.Address do
- xml.Street1 billing_address[:address1]
- xml.Street2 billing_address[:address2]
- xml.City billing_address[:city]
- xml.StateProv billing_address[:state]
- xml.PostalCode billing_address[:zip]
- xml.Country billing_address[:country_code]
- end
- end
- end
- end
- end
- instance_eval(&block)
- end
- end
-
- def add_creditcard(creditcard)
- xml.PaymentMech do
- xml.CreditCard do
- xml.Type({ :DataType => 'S32' }, EPDQ_CARD_TYPES[creditcard.brand.to_sym])
- xml.Number creditcard.number
- xml.Expires({ :DataType => 'ExpirationDate', :Locale => 826 }, format_expiry_date(creditcard))
- if creditcard.verification_value.present?
- xml.Cvv2Indicator 1
- xml.Cvv2Val creditcard.verification_value
- else
- xml.Cvv2Indicator 5
- end
- xml.IssueNum(creditcard.issue_number) if creditcard.issue_number.present?
- end
- end
- end
-
- def add_transaction(auth_type, amount = nil, options = {})
- @auth_type = auth_type
- xml.Transaction do
- xml.Type @auth_type.to_s
- if options[:payment_number] && options[:payment_number] > 1
- xml.CardholderPresentCode({ :DataType => 'S32' }, 8)
- else
- xml.CardholderPresentCode({ :DataType => 'S32' }, 7)
- end
- if options[:payment_number]
- xml.PaymentNumber({ :DataType => 'S32' }, options[:payment_number])
- end
- if options[:total_payments]
- xml.TotalNumberPayments({ :DataType => 'S32' }, options[:total_payments])
- end
- if amount
- xml.CurrentTotals do
- xml.Totals do
- xml.Total({ :DataType => 'Money', :Currency => 826 }, amount)
- end
- end
- end
- end
- end
-
- # date must be formatted MM/YY
- def format_expiry_date(creditcard)
- month_str = "%02d" % creditcard.month
- if match = creditcard.year.to_s.match(/^\d{2}(\d{2})$/)
- year_str = "%02d" % match[1].to_i
- else
- year_str = "%02d" % creditcard.year
- end
- "#{month_str}/#{year_str}"
- end
- end
- end
- end
-end
-
diff --git a/lib/active_merchant/billing/gateways/beanstream.rb b/lib/active_merchant/billing/gateways/beanstream.rb
index f396a7d7d0f..d60d8a5d7ed 100644
--- a/lib/active_merchant/billing/gateways/beanstream.rb
+++ b/lib/active_merchant/billing/gateways/beanstream.rb
@@ -75,6 +75,7 @@ def authorize(money, source, options = {})
add_address(post, options)
add_transaction_type(post, :authorization)
add_customer_ip(post, options)
+ add_recurring_payment(post, options)
commit(post)
end
@@ -86,6 +87,7 @@ def purchase(money, source, options = {})
add_address(post, options)
add_transaction_type(post, purchase_action(source))
add_customer_ip(post, options)
+ add_recurring_payment(post, options)
commit(post)
end
diff --git a/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb b/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb
index cd7262a4afd..e60e9bd2646 100644
--- a/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb
+++ b/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb
@@ -1,6 +1,8 @@
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
module BeanstreamCore
+ include Empty
+
RECURRING_URL = 'https://www.beanstream.com/scripts/recurring_billing.asp'
SECURE_PROFILE_URL = 'https://www.beanstream.com/scripts/payment_profile.asp'
@@ -59,6 +61,72 @@ module BeanstreamCore
:cancel => 'C'
}
+ STATES = {
+ "ALBERTA" => "AB",
+ "BRITISH COLUMBIA" => "BC",
+ "MANITOBA" => "MB",
+ "NEW BRUNSWICK" => "NB",
+ "NEWFOUNDLAND AND LABRADOR" => "NL",
+ "NOVA SCOTIA" => "NS",
+ "ONTARIO" => "ON",
+ "PRINCE EDWARD ISLAND" => "PE",
+ "QUEBEC" => "QC",
+ "SASKATCHEWAN" => "SK",
+ "NORTHWEST TERRITORIES" => "NT",
+ "NUNAVUT" => "NU",
+ "YUKON" => "YT",
+ "ALABAMA" => "AL",
+ "ALASKA" => "AK",
+ "ARIZONA" => "AZ",
+ "ARKANSAS" => "AR",
+ "CALIFORNIA" => "CA",
+ "COLORADO" => "CO",
+ "CONNECTICUT" => "CT",
+ "DELAWARE" => "DE",
+ "FLORIDA" => "FL",
+ "GEORGIA" => "GA",
+ "HAWAII" => "HI",
+ "IDAHO" => "ID",
+ "ILLINOIS" => "IL",
+ "INDIANA" => "IN",
+ "IOWA" => "IA",
+ "KANSAS" => "KS",
+ "KENTUCKY" => "KY",
+ "LOUISIANA" => "LA",
+ "MAINE" => "ME",
+ "MARYLAND" => "MD",
+ "MASSACHUSETTS" => "MA",
+ "MICHIGAN" => "MI",
+ "MINNESOTA" => "MN",
+ "MISSISSIPPI" => "MS",
+ "MISSOURI" => "MO",
+ "MONTANA" => "MT",
+ "NEBRASKA" => "NE",
+ "NEVADA" => "NV",
+ "NEW HAMPSHIRE" => "NH",
+ "NEW JERSEY" => "NJ",
+ "NEW MEXICO" => "NM",
+ "NEW YORK" => "NY",
+ "NORTH CAROLINA" => "NC",
+ "NORTH DAKOTA" => "ND",
+ "OHIO" => "OH",
+ "OKLAHOMA" => "OK",
+ "OREGON" => "OR",
+ "PENNSYLVANIA" => "PA",
+ "RHODE ISLAND" => "RI",
+ "SOUTH CAROLINA" => "SC",
+ "SOUTH DAKOTA" => "SD",
+ "TENNESSEE" => "TN",
+ "TEXAS" => "TX",
+ "UTAH" => "UT",
+ "VERMONT" => "VT",
+ "VIRGINIA" => "VA",
+ "WASHINGTON" => "WA",
+ "WEST VIRGINIA" => "WV",
+ "WISCONSIN" => "WI",
+ "WYOMING" => "WY"
+ }
+
def self.included(base)
base.default_currency = 'CAD'
@@ -70,7 +138,7 @@ def self.included(base)
# The homepage URL of the gateway
base.homepage_url = 'http://www.beanstream.com/'
- base.live_url = 'https://www.beanstream.com/scripts/process_transaction.asp'
+ base.live_url = 'https://api.na.bambora.com/scripts/process_transaction.asp'
# The name of the gateway
base.display_name = 'Beanstream.com'
@@ -93,6 +161,7 @@ def capture(money, authorization, options = {})
add_amount(post, money)
add_reference(post, reference)
add_transaction_type(post, :capture)
+ add_recurring_payment(post, options)
commit(post)
end
@@ -111,6 +180,7 @@ def credit(money, source, options = {})
end
private
+
def purchase_action(source)
if source.is_a?(Check)
:check_purchase
@@ -120,7 +190,7 @@ def purchase_action(source)
end
def add_customer_ip(post, options)
- post[:customerIP] = options[:ip] if options[:ip]
+ post[:customerIp] = options[:ip] if options[:ip]
end
def void_action(original_transaction_type)
@@ -152,27 +222,29 @@ def add_reference(post, reference)
end
def add_address(post, options)
+ post[:ordEmailAddress] = options[:email] if options[:email]
+ post[:shipEmailAddress] = options[:shipping_email] || options[:email] if options[:email]
+
prepare_address_for_non_american_countries(options)
if billing_address = options[:billing_address] || options[:address]
post[:ordName] = billing_address[:name]
- post[:ordEmailAddress] = options[:email]
post[:ordPhoneNumber] = billing_address[:phone]
post[:ordAddress1] = billing_address[:address1]
post[:ordAddress2] = billing_address[:address2]
post[:ordCity] = billing_address[:city]
- post[:ordProvince] = billing_address[:state]
+ post[:ordProvince] = state_for(billing_address)
post[:ordPostalCode] = billing_address[:zip]
post[:ordCountry] = billing_address[:country]
end
+
if shipping_address = options[:shipping_address]
post[:shipName] = shipping_address[:name]
- post[:shipEmailAddress] = options[:email]
post[:shipPhoneNumber] = shipping_address[:phone]
post[:shipAddress1] = shipping_address[:address1]
post[:shipAddress2] = shipping_address[:address2]
post[:shipCity] = shipping_address[:city]
- post[:shipProvince] = shipping_address[:state]
+ post[:shipProvince] = state_for(shipping_address)
post[:shipPostalCode] = shipping_address[:zip]
post[:shipCountry] = shipping_address[:country]
post[:shippingMethod] = shipping_address[:shipping_method]
@@ -180,8 +252,13 @@ def add_address(post, options)
end
end
+ def state_for(address)
+ STATES[address[:state].upcase] || address[:state] if address[:state]
+ end
+
def prepare_address_for_non_american_countries(options)
[ options[:billing_address], options[:shipping_address] ].compact.each do |address|
+ next if empty?(address[:country])
unless ['US', 'CA'].include?(address[:country])
address[:state] = '--'
address[:zip] = '000000' unless address[:zip]
@@ -189,6 +266,10 @@ def prepare_address_for_non_american_countries(options)
end
end
+ def add_recurring_payment(post, options)
+ post[:recurringPayment] = true if options[:recurring].to_s == 'true'
+ end
+
def add_invoice(post, options)
post[:trnOrderNumber] = options[:order_id]
post[:trnComments] = options[:description]
@@ -391,4 +472,3 @@ def post_data(params, use_profile_api)
end
end
end
-
diff --git a/lib/active_merchant/billing/gateways/blue_pay.rb b/lib/active_merchant/billing/gateways/blue_pay.rb
index 571dec69bce..25966133876 100644
--- a/lib/active_merchant/billing/gateways/blue_pay.rb
+++ b/lib/active_merchant/billing/gateways/blue_pay.rb
@@ -43,7 +43,7 @@ class BluePayGateway < Gateway
'USUAL_DATE' => :undoc_usual_date, # Not found in the bp20rebadmin API doc.
}
- self.supported_countries = ['US']
+ self.supported_countries = ['US', 'CA']
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
self.homepage_url = 'http://www.bluepay.com/'
self.display_name = 'BluePay'
diff --git a/lib/active_merchant/billing/gateways/blue_snap.rb b/lib/active_merchant/billing/gateways/blue_snap.rb
index 14d199ff0ed..c2ee9385990 100644
--- a/lib/active_merchant/billing/gateways/blue_snap.rb
+++ b/lib/active_merchant/billing/gateways/blue_snap.rb
@@ -5,7 +5,7 @@ module Billing
class BlueSnapGateway < Gateway
self.test_url = "https://sandbox.bluesnap.com/services/2"
self.live_url = "https://ws.bluesnap.com/services/2"
- self.supported_countries = %w(US GB)
+ self.supported_countries = %w(US CA GB 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)
self.default_currency = 'USD'
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club, :maestro]
@@ -86,7 +86,7 @@ def capture(money, authorization, options={})
def refund(money, authorization, options={})
commit(:refund, :put) do |doc|
add_authorization(doc, authorization)
- add_amount(doc, money)
+ add_amount(doc, money, options)
add_order(doc, options)
end
end
@@ -140,7 +140,7 @@ def scrub(transcript)
def add_auth_purchase(doc, money, payment_method, options)
doc.send("recurring-transaction", options[:recurring] ? "RECURRING" : "ECOMMERCE")
add_order(doc, options)
- add_amount(doc, money)
+ add_amount(doc, money, options)
doc.send("transaction-fraud-info") do
doc.send("shopper-ip-address", options[:ip]) if options[:ip]
end
@@ -155,7 +155,7 @@ def add_auth_purchase(doc, money, payment_method, options)
end
end
- def add_amount(doc, money)
+ def add_amount(doc, money, options)
doc.amount(amount(money))
doc.currency(options[:currency] || currency(money))
end
@@ -203,11 +203,6 @@ def add_address(doc, options)
doc.zip(address[:zip]) if address[:zip]
end
- def add_invoice(post, money, options)
- post[:amount] = amount(money)
- post[:currency] = (options[:currency] || currency(money))
- end
-
def add_authorization(doc, authorization)
doc.send("transaction-id", authorization)
end
diff --git a/lib/active_merchant/billing/gateways/bogus.rb b/lib/active_merchant/billing/gateways/bogus.rb
index 2f5407c069d..f2d99d9ceb2 100644
--- a/lib/active_merchant/billing/gateways/bogus.rb
+++ b/lib/active_merchant/billing/gateways/bogus.rb
@@ -129,7 +129,7 @@ def authorize_emv(money, paysource, options = {})
def authorize_swipe(money, paysource, options = {})
money = amount(money)
case normalize(paysource)
- when /1$/
+ when /1$/, AUTHORIZATION
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, :error_code => STANDARD_ERROR_CODE[:processing_error])
diff --git a/lib/active_merchant/billing/gateways/borgun.rb b/lib/active_merchant/billing/gateways/borgun.rb
index ff329fb4b2c..f3892b9f4f8 100644
--- a/lib/active_merchant/billing/gateways/borgun.rb
+++ b/lib/active_merchant/billing/gateways/borgun.rb
@@ -26,7 +26,6 @@ def purchase(money, payment, options={})
post[:TransType] = '1'
add_invoice(post, money, options)
add_payment_method(post, payment)
-
commit('sale', post)
end
@@ -35,7 +34,6 @@ def authorize(money, payment, options={})
post[:TransType] = '5'
add_invoice(post, money, options)
add_payment_method(post, payment)
-
commit('authonly', post)
end
@@ -57,9 +55,10 @@ def refund(money, authorization, options={})
def void(authorization, options={})
post = {}
- # TransType and TrAmount must match original values from auth or purchase.
- _, _, _, _, _, transtype, tramount = split_authorization(authorization)
+ # TransType, TrAmount, and currency must match original values from auth or purchase.
+ _, _, _, _, _, transtype, tramount, currency = split_authorization(authorization)
post[:TransType] = transtype
+ options[:currency] = options[:currency] || CURRENCY_CODES.key(currency)
add_invoice(post, tramount.to_i, options)
add_reference(post, authorization)
commit('void', post)
@@ -80,10 +79,12 @@ def scrub(transcript)
CURRENCY_CODES = Hash.new{|h,k| raise ArgumentError.new("Unsupported currency for HDFC: #{k}")}
CURRENCY_CODES["ISK"] = "352"
CURRENCY_CODES["EUR"] = "978"
+ CURRENCY_CODES["USD"] = "840"
def add_invoice(post, money, options)
post[:TrAmount] = amount(money)
post[:TrCurrency] = CURRENCY_CODES[options[:currency] || currency(money)]
+ post[:TerminalID] = options[:terminal_id] || '1'
end
def add_payment_method(post, payment_method)
@@ -95,9 +96,8 @@ def add_payment_method(post, payment_method)
end
def add_reference(post, authorization)
- dateandtime, batch, transaction, rrn, authcode, _, _ = split_authorization(authorization)
+ dateandtime, batch, transaction, rrn, authcode, _, _, _ = split_authorization(authorization)
post[:DateAndTime] = dateandtime
- post[:Batch] = batch
post[:Transaction] = transaction
post[:RRN] = rrn
post[:AuthCode] = authcode
@@ -129,7 +129,6 @@ def commit(action, post)
post[:Version] = '1000'
post[:Processor] = @options[:processor]
post[:MerchantID] = @options[:merchant_id]
- post[:TerminalID] = 1
url = (test? ? test_url : live_url)
request = build_request(action, post)
@@ -166,13 +165,14 @@ def authorization_from(response)
response[:rrn],
response[:authcode],
response[:transtype],
- response[:tramount]
+ response[:tramount],
+ response[:trcurrency]
].join("|")
end
def split_authorization(authorization)
- dateandtime, batch, transaction, rrn, authcode, transtype, tramount = authorization.split("|")
- [dateandtime, batch, transaction, rrn, authcode, transtype, tramount]
+ dateandtime, batch, transaction, rrn, authcode, transtype, tramount, currency = authorization.split("|")
+ [dateandtime, batch, transaction, rrn, authcode, transtype, tramount, currency]
end
def headers
diff --git a/lib/active_merchant/billing/gateways/braintree/braintree_common.rb b/lib/active_merchant/billing/gateways/braintree/braintree_common.rb
index af47eae5a36..1d4fc3dec9b 100644
--- a/lib/active_merchant/billing/gateways/braintree/braintree_common.rb
+++ b/lib/active_merchant/billing/gateways/braintree/braintree_common.rb
@@ -5,6 +5,7 @@ def self.included(base)
base.homepage_url = 'http://www.braintreepaymentsolutions.com'
base.display_name = 'Braintree'
base.default_currency = 'USD'
+ base.currencies_without_fractions = %w(BIF CLP DJF GNF JPY KMF KRW LAK PYG RWF UGX VND VUV XAF XOF XPF)
end
def supports_scrubbing
diff --git a/lib/active_merchant/billing/gateways/braintree_blue.rb b/lib/active_merchant/billing/gateways/braintree_blue.rb
index ab6363b0474..7c0576ff37a 100644
--- a/lib/active_merchant/billing/gateways/braintree_blue.rb
+++ b/lib/active_merchant/billing/gateways/braintree_blue.rb
@@ -6,8 +6,8 @@
raise "Could not load the braintree gem. Use `gem install braintree` to install it."
end
-unless Braintree::Version::Major == 2 && Braintree::Version::Minor >= 4
- raise "Need braintree gem >= 2.4.0. Run `gem install braintree --version '~>2.4'` to get the correct version."
+unless Braintree::Version::Major == 2 && Braintree::Version::Minor >= 78
+ raise "Need braintree gem >= 2.78.0. Run `gem install braintree --version '~>2.78'` to get the correct version."
end
module ActiveMerchant #:nodoc:
@@ -42,6 +42,10 @@ class BraintreeBlueGateway < Gateway
self.display_name = 'Braintree (Blue Platform)'
+ ERROR_CODES = {
+ cannot_refund_if_unsettled: 91506
+ }
+
def initialize(options = {})
requires!(options, :merchant_id, :public_key, :private_key)
@merchant_account_id = options[:merchant_account_id]
@@ -74,7 +78,7 @@ def authorize(money, credit_card_or_vault_id, options = {})
def capture(money, authorization, options = {})
commit do
- result = @braintree_gateway.transaction.submit_for_settlement(authorization, amount(money).to_s)
+ result = @braintree_gateway.transaction.submit_for_settlement(authorization, localized_amount(money, options[:currency] || default_currency).to_s)
response_from_result(result)
end
end
@@ -90,11 +94,15 @@ def credit(money, credit_card_or_vault_id, options = {})
def refund(*args)
# legacy signature: #refund(transaction_id, options = {})
# new signature: #refund(money, transaction_id, options = {})
- money, transaction_id, _ = extract_refund_args(args)
- money = amount(money).to_s if money
+ money, transaction_id, options = extract_refund_args(args)
+ money = localized_amount(money, options[:currency] || default_currency).to_s if money
commit do
- response_from_result(@braintree_gateway.transaction.refund(transaction_id, money))
+ response = response_from_result(@braintree_gateway.transaction.refund(transaction_id, money))
+ return response if response.success?
+ return response unless options[:force_full_refund_if_unsettled]
+
+ void(transaction_id) if response.message =~ /#{ERROR_CODES[:cannot_refund_if_unsettled]}/
end
end
@@ -150,6 +158,8 @@ def update(vault_id, creditcard, options = {})
:first_name => creditcard.first_name,
:last_name => creditcard.last_name,
:email => scrub_email(options[:email]),
+ :phone => options[:phone] || (options[:billing_address][:phone] if options[:billing_address] &&
+ options[:billing_address][:phone]),
:credit_card => credit_card_params
)
Response.new(result.success?, message_from_result(result),
@@ -220,6 +230,8 @@ def add_customer_with_credit_card(creditcard, options)
:first_name => creditcard.first_name,
:last_name => creditcard.last_name,
:email => scrub_email(options[:email]),
+ :phone => options[:phone] || (options[:billing_address][:phone] if options[:billing_address] &&
+ options[:billing_address][:phone]),
:id => options[:customer],
}.merge credit_card_params
result = @braintree_gateway.customer.create(merge_credit_card_options(parameters, options))
@@ -304,7 +316,7 @@ def map_address(address)
:region => address[:state],
:postal_code => scrub_zip(address[:zip]),
}
- if(address[:country] || address[:country_code_alpha2])
+ if (address[:country] || address[:country_code_alpha2])
mapped[:country_code_alpha2] = (address[:country] || address[:country_code_alpha2])
elsif address[:country_name]
mapped[:country_name] = address[:country_name]
@@ -428,7 +440,7 @@ def create_transaction(transaction_type, money, credit_card_or_vault_id, options
end
def extract_refund_args(args)
- options = args.extract_options!
+ options = args.last.is_a?(Hash) ? args.pop : {}
# money, transaction_id, options
if args.length == 1 # legacy signature
@@ -443,6 +455,7 @@ def extract_refund_args(args)
def customer_hash(customer, include_credit_cards=false)
hash = {
"email" => customer.email,
+ "phone" => customer.phone,
"first_name" => customer.first_name,
"last_name" => customer.last_name,
"id" => customer.id
@@ -484,7 +497,8 @@ def transaction_hash(result)
customer_details = {
"id" => transaction.customer_details.id,
- "email" => transaction.customer_details.email
+ "email" => transaction.customer_details.email,
+ "phone" => transaction.customer_details.phone,
}
billing_details = {
@@ -516,6 +530,7 @@ def transaction_hash(result)
{
"order_id" => transaction.order_id,
+ "amount" => transaction.amount.to_s,
"status" => transaction.status,
"credit_card_details" => credit_card_details,
"customer_details" => customer_details,
@@ -529,19 +544,25 @@ def transaction_hash(result)
def create_transaction_parameters(money, credit_card_or_vault_id, options)
parameters = {
- :amount => amount(money).to_s,
+ :amount => localized_amount(money, options[:currency] || default_currency).to_s,
:order_id => options[:order_id],
:customer => {
:id => options[:store] == true ? "" : options[:store],
- :email => scrub_email(options[:email])
+ :email => scrub_email(options[:email]),
+ :phone => options[:phone] || (options[:billing_address][:phone] if options[:billing_address] &&
+ options[:billing_address][:phone])
},
:options => {
:store_in_vault => options[:store] ? true : false,
:submit_for_settlement => options[:submit_for_settlement],
- :hold_in_escrow => options[:hold_in_escrow]
+ :hold_in_escrow => options[:hold_in_escrow],
}
}
+ if options[:skip_advanced_fraud_checking]
+ parameters[:options].merge!({ :skip_advanced_fraud_checking => options[:skip_advanced_fraud_checking] })
+ end
+
parameters[:custom_fields] = options[:custom_fields]
parameters[:device_data] = options[:device_data] if options[:device_data]
parameters[:service_fee_amount] = options[:service_fee_amount] if options[:service_fee_amount]
@@ -573,8 +594,20 @@ def create_transaction_parameters(money, credit_card_or_vault_id, options)
:number => credit_card_or_vault_id.number,
:expiration_month => credit_card_or_vault_id.month.to_s.rjust(2, "0"),
:expiration_year => credit_card_or_vault_id.year.to_s,
- :cardholder_name => "#{credit_card_or_vault_id.first_name} #{credit_card_or_vault_id.last_name}",
- :cryptogram => credit_card_or_vault_id.payment_cryptogram
+ :cardholder_name => credit_card_or_vault_id.name,
+ :cryptogram => credit_card_or_vault_id.payment_cryptogram,
+ :eci_indicator => credit_card_or_vault_id.eci
+ }
+ elsif credit_card_or_vault_id.source == :android_pay
+ parameters[:android_pay_card] = {
+ :number => credit_card_or_vault_id.number,
+ :cryptogram => credit_card_or_vault_id.payment_cryptogram,
+ :expiration_month => credit_card_or_vault_id.month.to_s.rjust(2, "0"),
+ :expiration_year => credit_card_or_vault_id.year.to_s,
+ :google_transaction_id => credit_card_or_vault_id.transaction_id,
+ :source_card_type => credit_card_or_vault_id.brand,
+ :source_card_last_four => credit_card_or_vault_id.last_digits,
+ :eci_indicator => credit_card_or_vault_id.eci
}
end
else
@@ -582,7 +615,8 @@ def create_transaction_parameters(money, credit_card_or_vault_id, options)
:number => credit_card_or_vault_id.number,
:cvv => credit_card_or_vault_id.verification_value,
:expiration_month => credit_card_or_vault_id.month.to_s.rjust(2, "0"),
- :expiration_year => credit_card_or_vault_id.year.to_s
+ :expiration_year => credit_card_or_vault_id.year.to_s,
+ :cardholder_name => credit_card_or_vault_id.name
}
end
end
@@ -600,6 +634,14 @@ def create_transaction_parameters(money, credit_card_or_vault_id, options)
}
end
+ if options[:three_d_secure]
+ parameters[:three_d_secure_pass_thru] = {
+ cavv: options[:three_d_secure][:cavv],
+ eci_flag: options[:three_d_secure][:eci],
+ xid: options[:three_d_secure][:xid],
+ }
+ end
+
parameters
end
end
diff --git a/lib/active_merchant/billing/gateways/card_connect.rb b/lib/active_merchant/billing/gateways/card_connect.rb
new file mode 100644
index 00000000000..7fc67262623
--- /dev/null
+++ b/lib/active_merchant/billing/gateways/card_connect.rb
@@ -0,0 +1,286 @@
+module ActiveMerchant #:nodoc:
+ module Billing #:nodoc:
+ class CardConnectGateway < Gateway
+ self.test_url = 'https://fts.cardconnect.com:6443/cardconnect/rest/'
+ self.live_url = 'https://fts.cardconnect.com:8443/cardconnect/rest/'
+
+ self.supported_countries = ['US']
+ self.default_currency = 'USD'
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
+
+ self.homepage_url = 'https://cardconnect.com/'
+ self.display_name = 'Card Connect'
+
+ STANDARD_ERROR_CODE_MAPPING = {
+ '11' => STANDARD_ERROR_CODE[:card_declined],
+ '12' => STANDARD_ERROR_CODE[:incorrect_number],
+ '13' => STANDARD_ERROR_CODE[:incorrect_cvc],
+ '14' => STANDARD_ERROR_CODE[:incorrect_cvc],
+ '15' => STANDARD_ERROR_CODE[:invalid_expiry_date],
+ '16' => STANDARD_ERROR_CODE[:expired_card],
+ '17' => STANDARD_ERROR_CODE[:incorrect_zip],
+ '21' => STANDARD_ERROR_CODE[:config_error],
+ '22' => STANDARD_ERROR_CODE[:config_error],
+ '23' => STANDARD_ERROR_CODE[:config_error],
+ '24' => STANDARD_ERROR_CODE[:processing_error],
+ '25' => STANDARD_ERROR_CODE[:processing_error],
+ '27' => STANDARD_ERROR_CODE[:processing_error],
+ '28' => STANDARD_ERROR_CODE[:processing_error],
+ '29' => STANDARD_ERROR_CODE[:processing_error],
+ '31' => STANDARD_ERROR_CODE[:processing_error],
+ '32' => STANDARD_ERROR_CODE[:processing_error],
+ '33' => STANDARD_ERROR_CODE[:card_declined],
+ '34' => STANDARD_ERROR_CODE[:card_declined],
+ '35' => STANDARD_ERROR_CODE[:incorrect_zip],
+ '36' => STANDARD_ERROR_CODE[:processing_error],
+ '37' => STANDARD_ERROR_CODE[:incorrect_cvc],
+ '41' => STANDARD_ERROR_CODE[:processing_error],
+ '42' => STANDARD_ERROR_CODE[:processing_error],
+ '43' => STANDARD_ERROR_CODE[:processing_error],
+ '44' => STANDARD_ERROR_CODE[:config_error],
+ '61' => STANDARD_ERROR_CODE[:processing_error],
+ '62' => STANDARD_ERROR_CODE[:processing_error],
+ '63' => STANDARD_ERROR_CODE[:processing_error],
+ '64' => STANDARD_ERROR_CODE[:config_error],
+ '65' => STANDARD_ERROR_CODE[:processing_error],
+ '66' => STANDARD_ERROR_CODE[:processing_error],
+ '91' => STANDARD_ERROR_CODE[:processing_error],
+ '92' => STANDARD_ERROR_CODE[:processing_error],
+ '93' => STANDARD_ERROR_CODE[:processing_error],
+ '94' => STANDARD_ERROR_CODE[:processing_error],
+ '95' => STANDARD_ERROR_CODE[:config_error],
+ '96' => STANDARD_ERROR_CODE[:processing_error],
+ 'NU' => STANDARD_ERROR_CODE[:card_declined],
+ 'N3' => STANDARD_ERROR_CODE[:card_declined],
+ 'NJ' => STANDARD_ERROR_CODE[:card_declined],
+ '51' => STANDARD_ERROR_CODE[:card_declined],
+ 'C2' => STANDARD_ERROR_CODE[:incorrect_cvc],
+ '54' => STANDARD_ERROR_CODE[:expired_card],
+ '05' => STANDARD_ERROR_CODE[:card_declined],
+ '03' => STANDARD_ERROR_CODE[:config_error],
+ '60' => STANDARD_ERROR_CODE[:pickup_card]
+ }
+
+ def initialize(options = {})
+ requires!(options, :merchant_id, :username, :password)
+ require_valid_domain!(options, :domain)
+ super
+ end
+
+ def require_valid_domain!(options, param)
+ if options.key?(param)
+ raise ArgumentError.new('not a valid cardconnect domain') unless /\Dcardconnect.com:\d{1,}\D/ =~ options[param]
+ end
+ end
+
+ def purchase(money, payment, options = {})
+ if options[:po_number]
+ MultiResponse.run do |r|
+ r.process { authorize(money, payment, options) }
+ r.process { capture(money, r.authorization, options) }
+ end
+ else
+ post = {}
+ add_invoice(post, options)
+ add_money(post, money)
+ add_payment(post, payment)
+ add_currency(post, money, options)
+ add_address(post, options)
+ add_customer_data(post, options)
+ add_3DS(post, options)
+ post[:capture] = 'Y'
+ commit('auth', post)
+ end
+ end
+
+ def authorize(money, payment, options = {})
+ post = {}
+ add_money(post, money)
+ add_currency(post, money, options)
+ add_invoice(post, options)
+ add_payment(post, payment)
+ add_address(post, options)
+ add_customer_data(post, options)
+ add_3DS(post, options)
+ commit('auth', post)
+ end
+
+ def capture(money, authorization, options = {})
+ post = {}
+ add_money(post, money)
+ add_reference(post, authorization)
+ add_additional_data(post, options)
+ commit('capture', post)
+ end
+
+ def refund(money, authorization, options = {})
+ post = {}
+ add_money(post, money)
+ add_reference(post, authorization)
+ commit('refund', post)
+ end
+
+ def void(authorization, options = {})
+ post = {}
+ add_reference(post, authorization)
+ commit('void', post)
+ end
+
+ def verify(credit_card, options = {})
+ authorize(0, credit_card, options)
+ end
+
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript
+ .gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]')
+ .gsub(%r(("cvv2\\":\\")\d*), '\1[FILTERED]')
+ .gsub(%r(("merchid\\":\\")\d*), '\1[FILTERED]')
+ .gsub(%r((&?"account\\":\\")\d*), '\1[FILTERED]')
+ .gsub(%r((&?"token\\":\\")\d*), '\1[FILTERED]')
+ end
+
+ private
+
+ def add_customer_data(post, options)
+ post[:email] = options[:email] if options[:email]
+ end
+
+ def add_address(post, options)
+ if address = options[:billing_address] || options[:address]
+ post[:address] = address[:address1] if address[:address1]
+ post[:address].concat(" #{address[:address2]}") if address[:address2]
+ post[:city] = address[:city] if address[:city]
+ post[:region] = address[:state] if address[:state]
+ post[:country] = address[:country] if address[:country]
+ post[:postal] = address[:zip] if address[:zip]
+ post[:phone] = address[:phone] if address[:phone]
+ end
+ end
+
+ def add_money(post, money)
+ post[:amount] = amount(money)
+ end
+
+ def add_currency(post, money, options)
+ post[:currency] = (options[:currency] || currency(money))
+ end
+
+ def add_invoice(post, options)
+ post[:orderid] = options[:order_id]
+ post[:ecomind] = (options[:recurring] ? 'R' : 'E')
+ end
+
+ def add_payment(post, payment)
+ post[:name] = payment.name
+ if card_brand(payment) == 'check'
+ add_echeck(post, payment)
+ else
+ post[:account] = payment.number
+ post[:expiry] = expdate(payment)
+ post[:cvv2] = payment.verification_value
+ end
+ end
+
+ def add_echeck(post, payment)
+ post[:accttype] = 'ECHK'
+ post[:account] = payment.account_number
+ post[:bankaba] = payment.routing_number
+ end
+
+ def add_reference(post, authorization)
+ post[:retref] = authorization
+ end
+
+ def add_additional_data(post, options)
+ post[:ponumber] = options[:po_number]
+ post[:taxamnt] = options[:tax_amount] if options[:tax_amount]
+ post[:frtamnt] = options[:freight_amount] if options[:freight_amount]
+ post[:dutyamnt] = options[:duty_amount] if options[:duty_amount]
+ post[:orderdate] = options[:order_date] if options[:order_date]
+ post[:shipfromzip] = options[:ship_from_zip] if options[:ship_from_zip]
+ if (shipping_address = options[:shipping_address])
+ post[:shiptozip] = shipping_address[:zip]
+ post[:shiptocountry] = shipping_address[:country]
+ end
+ if options[:items]
+ post[:items] = options[:items].map do |item|
+ updated = {}
+ item.each_pair do |k, v|
+ updated.merge!(k.to_s.gsub(/_/, '') => v)
+ end
+ end
+ end
+ end
+
+ def add_3DS(post, options)
+ post[:secureflag] = options[:secure_flag] if options[:secure_flag]
+ post[:securevalue] = options[:secure_value] if options[:secure_value]
+ post[:securexid] = options[:secure_xid] if options[:secure_xid]
+ end
+
+ def headers
+ {
+ 'Authorization' => 'Basic ' + Base64.strict_encode64("#{@options[:username]}:#{@options[:password]}"),
+ 'Content-Type' => 'application/json'
+ }
+ end
+
+ def expdate(credit_card)
+ "#{format(credit_card.month, :two_digits)}#{format(credit_card.year, :two_digits)}"
+ end
+
+ def parse(body)
+ JSON.parse(body)
+ end
+
+ def url(action)
+ if test?
+ test_url + action
+ else
+ (@options[:domain] ? @options[:domain] : live_url) + action
+ end
+ end
+
+ def commit(action, parameters)
+ parameters[:merchid] = @options[:merchant_id]
+ url = url(action)
+ response = parse(ssl_request(:put, url, post_data(parameters), headers))
+
+ Response.new(
+ success_from(response),
+ message_from(response),
+ response,
+ authorization: authorization_from(response),
+ avs_result: AVSResult.new(code: response['avsresp']),
+ cvv_result: CVVResult.new(response['cvvresp']),
+ test: test?,
+ error_code: error_code_from(response)
+ )
+ end
+
+ def success_from(response)
+ response['respstat'] == 'A'
+ end
+
+ def message_from(response)
+ response['setlstat'] ? "#{response['resptext']} #{response['setlstat']}" : response['resptext']
+ end
+
+ def authorization_from(response)
+ response['retref']
+ end
+
+ def post_data(parameters = {})
+ parameters.to_json
+ end
+
+ def error_code_from(response)
+ STANDARD_ERROR_CODE_MAPPING[response['respcode']] unless success_from(response)
+ end
+ end
+ end
+end
diff --git a/lib/active_merchant/billing/gateways/card_stream.rb b/lib/active_merchant/billing/gateways/card_stream.rb
index a7d7d02ba45..fb4b5a0c3bc 100644
--- a/lib/active_merchant/billing/gateways/card_stream.rb
+++ b/lib/active_merchant/billing/gateways/card_stream.rb
@@ -14,23 +14,95 @@ class CardStreamGateway < Gateway
CURRENCY_CODES = {
"AED" => "784",
+ "ALL" => "008",
+ "AMD" => "051",
+ "ANG" => "532",
+ "ARS" => "032",
"AUD" => "036",
+ "AWG" => "533",
+ "BAM" => "977",
+ "BBD" => "052",
+ "BGN" => "975",
+ "BMD" => "060",
+ "BOB" => "068",
"BRL" => "986",
+ "BSD" => "044",
+ "BWP" => "072",
+ "BZD" => "084",
"CAD" => "124",
"CHF" => "756",
+ "CLP" => "152",
+ "CNY" => "156",
+ "COP" => "170",
+ "CRC" => "188",
"CZK" => "203",
"DKK" => "208",
+ "DOP" => "214",
+ "EGP" => "818",
"EUR" => "978",
"GBP" => "826",
+ "GEL" => "981",
+ "GIP" => "292",
+ "GTQ" => "320",
+ "GYD" => "328",
"HKD" => "344",
- "ICK" => "352",
+ "HNL" => "340",
+ "HRK" => "191",
+ "HUF" => "348",
+ "ISK" => "352",
+ "IDR" => "360",
+ "ILS" => "376",
+ "INR" => "356",
"JPY" => "392",
+ "JMD" => "388",
+ "KES" => "404",
+ "KRW" => "410",
+ "KYD" => "136",
+ "LBP" => "422",
+ "LKR" => "144",
+ "MAD" => "504",
+ "MVR" => "462",
+ "MWK" => "454",
"MXN" => "484",
+ "MYR" => "458",
+ "NAD" => "516",
+ "NGN" => "566",
+ "NIO" => "558",
"NOK" => "578",
+ "NPR" => "524",
"NZD" => "554",
+ "PAB" => "590",
+ "PEN" => "604",
+ "PGK" => "598",
+ "PHP" => "608",
+ "PKR" => "586",
+ "PLN" => "985",
+ "PYG" => "600",
+ "QAR" => "634",
+ "RON" => "946",
+ "RSD" => "941",
+ "RUB" => "643",
+ "RWF" => "646",
+ "SAR" => "682",
"SEK" => "752",
"SGD" => "702",
+ "SRD" => "968",
+ "THB" => "764",
+ "TND" => "788",
+ "TRY" => "949",
+ "TTD" => "780",
+ "TWD" => "901",
+ "TZS" => "834",
+ "UAH" => "980",
+ "UGX" => "800",
"USD" => "840",
+ "UYU" => "858",
+ "VND" => "704",
+ "WST" => "882",
+ "XAF" => "950",
+ "XCD" => "951",
+ "XOF" => "952",
+ "ZAR" => "710"
}
CVV_CODE = {
@@ -83,15 +155,18 @@ def authorize(money, credit_card_or_reference, options = {})
add_invoice(post, credit_card_or_reference, money, options)
add_credit_card_or_reference(post, credit_card_or_reference)
add_customer_data(post, options)
+ add_remote_address(post, options)
commit('SALE', post)
end
def purchase(money, credit_card_or_reference, options = {})
post = {}
+ add_pair(post, :captureDelay, 0)
add_amount(post, money, options)
add_invoice(post, credit_card_or_reference, money, options)
add_credit_card_or_reference(post, credit_card_or_reference)
add_customer_data(post, options)
+ add_remote_address(post, options)
commit('SALE', post)
end
@@ -99,6 +174,7 @@ def capture(money, authorization, options = {})
post = {}
add_pair(post, :xref, authorization)
add_pair(post, :amount, amount(money), :required => true)
+ add_remote_address(post, options)
commit('CAPTURE', post)
end
@@ -107,12 +183,23 @@ def refund(money, authorization, options = {})
post = {}
add_pair(post, :xref, authorization)
add_amount(post, money, options)
- commit('REFUND', post)
+ add_remote_address(post, options)
+ response = commit('REFUND_SALE', post)
+
+ return response if response.success?
+ return response unless options[:force_full_refund_if_unsettled]
+
+ if response.params["responseCode"] == "65541"
+ void(authorization, options)
+ else
+ response
+ end
end
def void(authorization, options = {})
post = {}
add_pair(post, :xref, authorization)
+ add_remote_address(post, options)
commit('CANCEL', post)
end
@@ -147,12 +234,17 @@ def add_customer_data(post, options)
add_pair(post, :customerAddress, "#{address[:address1]} #{address[:address2]}".strip)
add_pair(post, :customerPostCode, address[:zip])
add_pair(post, :customerPhone, options[:phone])
+ add_pair(post, :customerCountryCode, address[:country] || 'GB')
+ else
+ add_pair(post, :customerCountryCode, 'GB')
end
end
def add_invoice(post, credit_card_or_reference, money, options)
add_pair(post, :transactionUnique, options[:order_id], :required => true)
add_pair(post, :orderRef, options[:description] || options[:order_id], :required => true)
+ add_pair(post, :statementNarrative1, options[:merchant_name]) if options[:merchant_name]
+ add_pair(post, :statementNarrative2, options[:dynamic_descriptor]) if options[:dynamic_descriptor]
if credit_card_or_reference.respond_to?(:number)
if ['american_express', 'diners_club'].include?(card_brand(credit_card_or_reference).to_s)
add_pair(post, :item1Quantity, 1)
@@ -198,6 +290,10 @@ def add_threeds_required(post, options)
add_pair(post, :threeDSRequired, (options[:threeds_required] || @threeds_required) ? 'Y' : 'N')
end
+ def add_remote_address(post, options={})
+ add_pair(post, :remoteAddress, options[:ip] || '1.1.1.1')
+ end
+
def normalize_line_endings(str)
str.gsub(/%0D%0A|%0A%0D|%0D/, "%0A")
end
diff --git a/lib/active_merchant/billing/gateways/cardprocess.rb b/lib/active_merchant/billing/gateways/cardprocess.rb
new file mode 100644
index 00000000000..059ad175298
--- /dev/null
+++ b/lib/active_merchant/billing/gateways/cardprocess.rb
@@ -0,0 +1,254 @@
+module ActiveMerchant #:nodoc:
+ module Billing #:nodoc:
+ class CardprocessGateway < Gateway
+ self.test_url = 'https://test.vr-pay-ecommerce.de/v1/payments'
+ self.live_url = 'https://vr-pay-ecommerce.de/v1/payments'
+
+ self.supported_countries = %w[ BE BG CZ DK DE EE IE ES FR HR IT CY LV LT LU
+ MT HU NL AT PL PT RO SI SK FI SE GB IS LI NO
+ CH ME MK AL RS TR BA ]
+ self.default_currency = 'EUR'
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :jcb]
+
+ self.homepage_url = 'https://vr-pay-ecommerce.docs.oppwa.com/'
+ self.display_name = 'CardProcess VR-Pay'
+ self.money_format = :dollars
+
+ STANDARD_ERROR_CODE_MAPPING = {}
+
+ # Creates a new CardProcess Gateway
+ #
+ # The gateway requires a valid login, password, and entity ID
+ # to be passed in the +options+ hash.
+ #
+ # === Options
+ #
+ # * :user_id -- The CardProcess user ID
+ # * :password -- The CardProcess password
+ # * :entity_id -- The CardProcess channel or entity ID for any transactions
+ def initialize(options={})
+ requires!(options, :user_id, :password, :entity_id)
+ super
+ # This variable exists purely to allow remote tests to force error codes;
+ # the lack of a setter despite its usage is intentional.
+ @test_options = {}
+ end
+
+ def purchase(money, payment, options = {})
+ post = {}
+ add_invoice(post, money, options)
+ add_payment(post, payment)
+ add_address(post, payment, options)
+ add_customer_data(post, options)
+
+ commit('DB', post)
+ end
+
+ def authorize(money, payment, options = {})
+ post = {}
+ add_invoice(post, money, options)
+ add_payment(post, payment)
+ add_address(post, payment, options)
+ add_customer_data(post, options)
+
+ commit('PA', post)
+ end
+
+ def capture(money, authorization, options = {})
+ post = {
+ id: authorization
+ }
+ add_invoice(post, money, options)
+ commit('CP', post)
+ end
+
+ def refund(money, authorization, options = {})
+ post = {
+ id: authorization
+ }
+ add_invoice(post, money, options)
+ commit('RF', post)
+ end
+
+ def credit(money, payment, options = {})
+ post = {}
+ add_invoice(post, money, options)
+ add_payment(post, payment)
+ add_address(post, payment, options)
+ add_customer_data(post, options)
+
+ commit('CD', post)
+ end
+
+ def void(authorization, _options = {})
+ post = {
+ id: authorization
+ }
+ commit('RV', post)
+ end
+
+ def verify(credit_card, options = {})
+ MultiResponse.run do |r|
+ r.process { authorize(100, credit_card, options) }
+ r.process { void(r.authorization, options) }
+ end
+ end
+
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript
+ .gsub(%r{(authentication\.[^=]+=)[^&]+}, '\1[FILTERED]')
+ .gsub(%r{(card\.number=)\d+}, '\1[FILTERED]')
+ .gsub(%r{(cvv=)\d{3,4}}, '\1[FILTERED]\2')
+ end
+
+ private
+
+ def add_customer_data(post, options)
+ post['customer.ip'] = options[:ip] if options[:ip]
+ end
+
+ def add_address(post, _card, options)
+ if (address = options[:billing_address] || options[:address])
+ post[:billing] = hashify_address(address)
+ end
+
+ if (shipping = options[:shipping_address])
+ post[:shipping] = hashify_address(shipping)
+ end
+ end
+
+ def add_invoice(post, money, options)
+ return if money.nil?
+ post[:amount] = amount(money)
+ post[:currency] = (options[:currency] || currency(money))
+ post[:merchantInvoiceId] = options[:merchant_invoice_id] if options[:merchant_invoice_id]
+ post[:merchantTransactionId] = options[:merchant_transaction_id] if options[:merchant_transaction_id]
+ post[:transactionCategory] = options[:transaction_category] if options[:transaction_category]
+ end
+
+ def add_payment(post, payment)
+ return if payment.is_a?(String)
+ post[:paymentBrand] = payment.brand.upcase if payment.brand
+ post[:card] ||= {}
+ post[:card][:number] = payment.number
+ post[:card][:holder] = payment.name
+ post[:card][:expiryMonth] = sprintf('%02d', payment.month)
+ post[:card][:expiryYear] = sprintf('%02d', payment.year)
+ post[:card][:cvv] = payment.verification_value
+ end
+
+ def parse(body)
+ JSON.parse(body)
+ end
+
+ def commit(action, parameters)
+ url = (test? ? test_url : live_url)
+ if (id = parameters.delete(:id))
+ url += "/#{id}"
+ end
+
+ begin
+ raw_response = ssl_post(url, post_data(action, parameters.merge(@test_options)))
+ rescue ResponseError => e
+ raw_response = e.response.body
+ end
+ response = parse(raw_response)
+
+ Response.new(
+ success_from(response),
+ message_from(response),
+ response,
+ authorization: authorization_from(response),
+ avs_result: AVSResult.new(code: response['result']['avsResponse']),
+ cvv_result: CVVResult.new(response['result']['cvvResponse']),
+ test: test?,
+ error_code: error_code_from(response)
+ )
+ end
+
+ def success_from(response)
+ !(response['result']['code'] =~ /^(000\.000\.|000\.100\.1|000\.[36])/).nil?
+ end
+
+ def message_from(response)
+ response['result']['description']
+ end
+
+ def authorization_from(response)
+ response['id']
+ end
+
+ def post_data(action, parameters = {})
+ post = parameters.clone
+ post[:authentication] ||= {}
+ post[:authentication][:userId] = @options[:user_id]
+ post[:authentication][:password] = @options[:password]
+ post[:authentication][:entityId] = @options[:entity_id]
+ post[:paymentType] = action
+ dot_flatten_hash(post).map {|key, value| "#{key}=#{CGI.escape(value.to_s)}"}.join("&")
+ end
+
+ def error_code_from(response)
+ unless success_from(response)
+ case response['result']['code']
+ when '100.100.101'
+ STANDARD_ERROR_CODE[:incorrect_number]
+ when '100.100.303'
+ STANDARD_ERROR_CODE[:expired_card]
+ when /100\.100\.(201|301|305)/
+ STANDARD_ERROR_CODE[:invalid_expiry_date]
+ when /100.100.60[01]/
+ STANDARD_ERROR_CODE[:invalid_cvc]
+ when '800.100.151'
+ STANDARD_ERROR_CODE[:invalid_number]
+ when '800.100.153'
+ STANDARD_ERROR_CODE[:incorrect_cvc]
+ when /800.800.(102|302)/
+ STANDARD_ERROR_CODE[:incorrect_address]
+ when '800.800.202'
+ STANDARD_ERROR_CODE[:invalid_zip]
+ when '800.100.166'
+ STANDARD_ERROR_CODE[:incorrect_pin]
+ when '800.100.171'
+ STANDARD_ERROR_CODE[:pickup_card]
+ when /^(200|700)\./
+ STANDARD_ERROR_CODE[:config_error]
+ when /^(800\.[17]00|800\.800\.[123])/
+ STANDARD_ERROR_CODE[:card_declined]
+ when /^(900\.[1234]00)/
+ STANDARD_ERROR_CODE[:processing_error]
+ else
+ STANDARD_ERROR_CODE[:processing_error]
+ end
+ end
+ end
+
+ def hashify_address(address)
+ hash = {}
+ hash[:street1] = address[:address1] if address[:address1]
+ hash[:street2] = address[:address2] if address[:address2]
+ hash[:city] = address[:city] if address[:city]
+ hash[:state] = address[:state] if address[:state]
+ hash[:postcode] = address[:zip] if address[:zip]
+ hash[:country] = address[:country] if address[:country]
+ hash
+ end
+
+ def dot_flatten_hash(hash, prefix = '')
+ h = {}
+ hash.each_pair do |k, v|
+ if v.is_a?(Hash)
+ h.merge!(dot_flatten_hash(v, prefix + k.to_s + '.'))
+ else
+ h[prefix + k.to_s] = v
+ end
+ end
+ h
+ end
+ end
+ end
+end
diff --git a/lib/active_merchant/billing/gateways/cashnet.rb b/lib/active_merchant/billing/gateways/cashnet.rb
index df790f198ba..463d83e66cc 100644
--- a/lib/active_merchant/billing/gateways/cashnet.rb
+++ b/lib/active_merchant/billing/gateways/cashnet.rb
@@ -3,7 +3,8 @@ module Billing #:nodoc:
class CashnetGateway < Gateway
include Empty
- self.live_url = "https://commerce.cashnet.com/"
+ self.live_url = "https://commerce.cashnet.com/"
+ self.test_url = "https://train.cashnet.com/"
self.supported_countries = ["US"]
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
@@ -54,11 +55,22 @@ def refund(money, identification, options = {})
commit('REFUND', money, post)
end
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript
+ .gsub(%r{(password=)[^&]+}, '\1[FILTERED]')
+ .gsub(%r{(cardno=)[^&]+}, '\1[FILTERED]')
+ .gsub(%r{(cid=)[^&]+}, '\1[FILTERED]')
+ end
+
private
def commit(action, money, fields)
fields[:amount] = amount(money)
- url = live_url + CGI.escape(@options[:merchant_gateway_name])
+ url = (test? ? test_url : live_url) + CGI.escape(@options[:merchant_gateway_name])
raw_response = ssl_post(url, post_data(action, fields))
parsed_response = parse(raw_response)
diff --git a/lib/active_merchant/billing/gateways/cenpos.rb b/lib/active_merchant/billing/gateways/cenpos.rb
index cc208e39661..e019001d464 100644
--- a/lib/active_merchant/billing/gateways/cenpos.rb
+++ b/lib/active_merchant/billing/gateways/cenpos.rb
@@ -180,7 +180,7 @@ def commit(action, post)
def headers
{
- "Accept-Encoding" => "gzip,deflate",
+ "Accept-Encoding" => "identity",
"Content-Type" => "text/xml;charset=UTF-8",
"SOAPAction" => "http://tempuri.org/Transactional/ProcessCreditCard"
}
diff --git a/lib/active_merchant/billing/gateways/checkout.rb b/lib/active_merchant/billing/gateways/checkout.rb
index d52d4656fd4..c0ad0cb2ef6 100644
--- a/lib/active_merchant/billing/gateways/checkout.rb
+++ b/lib/active_merchant/billing/gateways/checkout.rb
@@ -5,7 +5,7 @@ module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class CheckoutGateway < Gateway
self.default_currency = 'USD'
- self.money_format = :decimals
+ self.money_format = :cents
self.supported_countries = ['AD', 'AT', 'BE', 'BG', 'CH', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FO', 'FI', 'FR', 'GB', 'GI', 'GL', 'GR', 'HR', 'HU', 'IE', 'IS', 'IL', 'IT', 'LI', 'LT', 'LU', 'LV', 'MC', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SE', 'SI', 'SM', 'SK', 'SJ', 'TR', 'VA']
self.supported_cardtypes = [:visa, :master, :american_express, :diners_club]
diff --git a/lib/active_merchant/billing/gateways/checkout_v2.rb b/lib/active_merchant/billing/gateways/checkout_v2.rb
index 571d96a49ee..b81c2f32a4f 100644
--- a/lib/active_merchant/billing/gateways/checkout_v2.rb
+++ b/lib/active_merchant/billing/gateways/checkout_v2.rb
@@ -4,9 +4,9 @@ class CheckoutV2Gateway < Gateway
self.display_name = "Checkout.com V2 Gateway"
self.homepage_url = "https://www.checkout.com/"
self.live_url = "https://api2.checkout.com/v2"
- self.test_url = "http://sandbox.checkout.com/api2/v2"
+ self.test_url = "https://sandbox.checkout.com/api2/v2"
- self.supported_countries = ['AD', 'AT', 'BE', 'BG', 'CH', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FO', 'FI', 'FR', 'GB', 'GI', 'GL', 'GR', 'HR', 'HU', 'IE', 'IS', 'IL', 'IT', 'LI', 'LT', 'LU', 'LV', 'MC', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SE', 'SI', 'SM', 'SK', 'SJ', 'TR', 'VA']
+ self.supported_countries = ['AD', 'AE', 'AT', 'BE', 'BG', 'CH', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FO', 'FI', 'FR', 'GB', 'GI', 'GL', 'GR', 'HR', 'HU', 'IE', 'IS', 'IL', 'IT', 'LI', 'LT', 'LU', 'LV', 'MC', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SE', 'SI', 'SM', 'SK', 'SJ', 'TR', 'VA']
self.default_currency = "USD"
self.money_format = :cents
self.supported_cardtypes = [:visa, :master, :american_express, :diners_club]
@@ -17,10 +17,15 @@ def initialize(options={})
end
def purchase(amount, payment_method, options={})
- MultiResponse.run do |r|
+ multi = MultiResponse.run do |r|
r.process { authorize(amount, payment_method, options) }
r.process { capture(amount, r.authorization, options) }
end
+
+ merged_params = multi.responses.map { |r| r.params }.reduce({}, :merge)
+ succeeded = success_from(merged_params)
+
+ response(:purchase, succeeded, merged_params)
end
def authorize(amount, payment_method, options={})
@@ -29,6 +34,7 @@ def authorize(amount, payment_method, options={})
add_invoice(post, amount, options)
add_payment_method(post, payment_method)
add_customer_data(post, options)
+ add_transaction_data(post, options)
commit(:authorize, post)
end
@@ -75,7 +81,7 @@ def scrub(transcript)
private
def add_invoice(post, money, options)
- post[:value] = amount(money)
+ post[:value] = localized_amount(money, options[:currency])
post[:trackId] = options[:order_id]
post[:currency] = options[:currency] || currency(money)
post[:descriptor] = {}
@@ -94,11 +100,12 @@ def add_payment_method(post, payment_method)
def add_customer_data(post, options)
post[:email] = options[:email] || "unspecified@example.com"
+ post[:customerIp] = options[:ip] if options[:ip]
address = options[:billing_address]
if(address && post[:card])
post[:card][:billingDetails] = {}
- post[:card][:billingDetails][:address1] = address[:address1]
- post[:card][:billingDetails][:address2] = address[:address2]
+ post[:card][:billingDetails][:addressLine1] = address[:address1]
+ post[:card][:billingDetails][:addressLine2] = address[:address2]
post[:card][:billingDetails][:city] = address[:city]
post[:card][:billingDetails][:state] = address[:state]
post[:card][:billingDetails][:country] = address[:country]
@@ -107,6 +114,12 @@ def add_customer_data(post, options)
end
end
+ def add_transaction_data(post, options={})
+ post[:cardOnFile] = true if options[:card_on_file] == true
+ post[:transactionIndicator] = options[:transaction_indicator] || 1
+ post[:previousChargeId] = options[:previous_charge_id] if options[:previous_charge_id]
+ end
+
def commit(action, post, authorization = nil)
begin
raw_response = ssl_post(url(post, action, authorization), post.to_json, headers)
@@ -117,6 +130,15 @@ def commit(action, post, authorization = nil)
end
succeeded = success_from(response)
+
+ response(action, succeeded, response)
+ end
+
+ def response(action, succeeded, response)
+ successful_response = succeeded && action == :purchase || action == :authorize
+ avs_result = successful_response ? avs_result(response) : nil
+ cvv_result = successful_response ? cvv_result(response) : nil
+
Response.new(
succeeded,
message_from(succeeded, response),
@@ -124,8 +146,9 @@ def commit(action, post, authorization = nil)
authorization: authorization_from(response),
error_code: error_code_from(succeeded, response),
test: test?,
- avs_result: avs_result(action, response),
- cvv_result: cvv_result(action, response))
+ avs_result: avs_result,
+ cvv_result: cvv_result
+ )
end
def headers
@@ -147,12 +170,12 @@ def base_url
test? ? test_url : live_url
end
- def avs_result(action, response)
- action == :purchase ? AVSResult.new(code: response["card"]["avsCheck"]) : nil
+ def avs_result(response)
+ response['card'] && response['card']['avsCheck'] ? AVSResult.new(code: response['card']['avsCheck']) : nil
end
- def cvv_result(action, response)
- action == :purchase ? CVVResult.new(response["card"]["cvvCheck"]) : nil
+ def cvv_result(response)
+ response['card'] && response['card']['cvvCheck'] ? CVVResult.new(response['card']['cvvCheck']) : nil
end
def parse(body)
@@ -165,7 +188,7 @@ def parse(body)
end
def success_from(response)
- response["responseCode"] == ("10000" || "10100")
+ (response["responseCode"] == "10000" && !response["responseMessage"].start_with?("40")) || response["responseCode"] == "10100"
end
def message_from(succeeded, response)
@@ -196,7 +219,14 @@ def authorization_from(raw)
end
def error_code_from(succeeded, response)
- succeeded ? nil : STANDARD_ERROR_CODE_MAPPING[response["responseCode"]]
+ return if succeeded
+ if response["errorCode"] && response["errorMessageCodes"]
+ "#{response["errorCode"]}: #{response["errorMessageCodes"].join(", ")}"
+ elsif response["errorCode"]
+ response["errorCode"]
+ else
+ STANDARD_ERROR_CODE_MAPPING[response["responseCode"]]
+ end
end
end
end
diff --git a/lib/active_merchant/billing/gateways/citrus_pay.rb b/lib/active_merchant/billing/gateways/citrus_pay.rb
index 22d7d51ecd1..f8661e23e1d 100644
--- a/lib/active_merchant/billing/gateways/citrus_pay.rb
+++ b/lib/active_merchant/billing/gateways/citrus_pay.rb
@@ -5,18 +5,17 @@ class CitrusPayGateway < Gateway
class_attribute :live_na_url, :live_ap_url, :test_na_url, :test_ap_url
- self.test_na_url = 'https://test-gateway.mastercard.com/api/rest/version/36/'
- self.test_ap_url = 'https://test-gateway.mastercard.com/api/rest/version/36/'
+ self.test_na_url = 'https://test-gateway.mastercard.com/api/rest/version/39/'
+ self.test_ap_url = 'https://test-gateway.mastercard.com/api/rest/version/39/'
- self.live_na_url = 'https://na-gateway.mastercard.com/api/rest/version/36/'
- self.live_ap_url = 'https://ap-gateway.mastercard.com/api/rest/version/36/'
+ self.live_na_url = 'https://na-gateway.mastercard.com/api/rest/version/39/'
+ self.live_ap_url = 'https://ap-gateway.mastercard.com/api/rest/version/39/'
self.display_name = 'Citrus Pay'
self.homepage_url = 'http://www.citruspay.com/'
self.supported_countries = %w(AR AU BR FR DE HK MX NZ SG GB US)
self.default_currency = 'USD'
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb, :maestro, :laser]
- self.ssl_version = :TLSv1
end
end
diff --git a/lib/active_merchant/billing/gateways/clearhaus.rb b/lib/active_merchant/billing/gateways/clearhaus.rb
index 44d5fed6107..93ee3c041fa 100644
--- a/lib/active_merchant/billing/gateways/clearhaus.rb
+++ b/lib/active_merchant/billing/gateways/clearhaus.rb
@@ -1,5 +1,3 @@
-require 'openssl'
-
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class ClearhausGateway < Gateway
@@ -10,7 +8,7 @@ class ClearhausGateway < Gateway
'HU', 'IS', 'IE', 'IT', 'LV', 'LI', 'LT', 'LU', 'MT', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'GB']
self.default_currency = 'EUR'
- self.currencies_without_fractions = %w(JPY)
+ self.currencies_without_fractions = %w(BIF BYR DJF GNF JPY KMF KRW PYG RWF VND VUV XAF XOF XPF)
self.supported_cardtypes = [:visa, :master]
self.homepage_url = 'https://www.clearhaus.com'
diff --git a/lib/active_merchant/billing/gateways/conekta.rb b/lib/active_merchant/billing/gateways/conekta.rb
index 0b6ad2b44bd..669edc313c2 100644
--- a/lib/active_merchant/billing/gateways/conekta.rb
+++ b/lib/active_merchant/billing/gateways/conekta.rb
@@ -12,7 +12,7 @@ class ConektaGateway < Gateway
def initialize(options = {})
requires!(options, :key)
- options[:version] ||= '0.3.0'
+ options[:version] ||= '1.0.0'
super
end
@@ -55,6 +55,11 @@ def refund(money, identifier, options)
commit(:post, "charges/#{identifier}/refund", post)
end
+ def void(identifier, options = {})
+ post = {}
+ commit(:post, "charges/#{identifier}/void", post)
+ end
+
def supports_scrubbing
true
end
@@ -77,16 +82,15 @@ def add_order(post, money, options)
def add_details_data(post, options)
details = {}
- details[:name] = options[:customer] if options[:customer]
+ details[:name] = options[:customer] || (options[:billing_address][:name] if options[:billing_address])
+ details[:phone] = options[:phone] || (options[:billing_address][:phone] if options[:billing_address])
details[:email] = options[:email] if options[:email]
- details[:phone] = options[:phone] if options[:phone]
- post[:device_fingerprint] = options[:device_fingerprint] if options[:device_fingerprint]
details[:ip] = options[:ip] if options[:ip]
add_billing_address(details, options)
add_line_items(details, options)
add_shipment(details, options)
-
post[:details] = details
+ post[:device_fingerprint] = options[:device_fingerprint] if options[:device_fingerprint]
end
def add_shipment(post, options)
diff --git a/lib/active_merchant/billing/gateways/creditcall.rb b/lib/active_merchant/billing/gateways/creditcall.rb
index c9ae198bfe4..c961f999d85 100644
--- a/lib/active_merchant/billing/gateways/creditcall.rb
+++ b/lib/active_merchant/billing/gateways/creditcall.rb
@@ -14,6 +14,32 @@ class CreditcallGateway < Gateway
self.homepage_url = 'https://www.creditcall.com'
self.display_name = 'Creditcall'
+
+ CVV_CODE = {
+ "matched" => "M",
+ "notmatched" => "N",
+ "notchecked" => "P",
+ "partialmatch" => "N"
+ }
+
+ AVS_CODE = {
+ "matched;matched" => "D",
+ "matched;notchecked" =>"B",
+ "matched;notmatched" => "A",
+ "matched;partialmatch" => "A",
+ "notchecked;matched" => "P",
+ "notchecked;notchecked" =>"I",
+ "notchecked;notmatched" => "I",
+ "notchecked;partialmatch" => "I",
+ "notmatched;matched" => "W",
+ "notmatched;notchecked" =>"C",
+ "notmatched;notmatched" => "C",
+ "notmatched;partialmatch" => "C",
+ "partialmatched;matched" => "W",
+ "partialmatched;notchecked" =>"C",
+ "partialmatched;notmatched" => "C",
+ "partialmatched;partialmatch" => "C"
+ }
def initialize(options={})
requires!(options, :terminal_id, :transaction_key)
@@ -26,11 +52,16 @@ def purchase(money, payment_method, options={})
r.process { capture(money, r.authorization, options) }
end
+ merged_params = multi_response.responses.map { |r| r.params }.reduce({}, :merge)
+
Response.new(
multi_response.primary_response.success?,
multi_response.primary_response.message,
- multi_response.primary_response.params,
+ merged_params,
authorization: multi_response.responses.first.authorization,
+ avs_result: AVSResult.new(code: avs_result_code_from(merged_params)),
+ cvv_result: CVVResult.new(cvv_result_code_from(merged_params)),
+ error_code: error_result_code_from(merged_params),
test: test?
)
end
@@ -92,6 +123,18 @@ def scrub(transcript)
private
+ def avs_result_code_from(params)
+ AVS_CODE["#{params['Address']};#{params['Zip']}"]
+ end
+
+ def cvv_result_code_from(params)
+ CVV_CODE[params["CSC"]]
+ end
+
+ def error_result_code_from(params)
+ params["ErrorCode"]
+ end
+
def build_xml_request
builder = Nokogiri::XML::Builder.new do |xml|
xml.Request(type: "CardEaseXML", version: "1.0.0") do
@@ -120,17 +163,22 @@ def add_terminal_details(xml, options={})
def add_card_details(xml, payment_method, options={})
xml.CardDetails do
- xml.Manual(type: "ecommerce") do
+ xml.Manual(type: manual_type(options)) do
xml.PAN payment_method.number
xml.ExpiryDate exp_date(payment_method)
xml.CSC payment_method.verification_value unless empty?(payment_method.verification_value)
end
- if address = options[:billing_address]
- xml.AdditionalVerification do
- xml.Address address[:address1]
- xml.Zip address[:zip]
- end
+ add_additional_verification(xml, options)
+ end
+ end
+
+ def add_additional_verification(xml, options)
+ return unless (options[:verify_zip].to_s == 'true') || (options[:verify_address].to_s == 'true')
+ if address = options[:billing_address]
+ xml.AdditionalVerification do
+ xml.Zip address[:zip] if options[:verify_zip].to_s == 'true'
+ xml.Address address[:address1] if options[:verify_address].to_s == 'true'
end
end
end
@@ -157,6 +205,16 @@ def parse(body)
end
end
+ node = xml.xpath("//Response/CardDetails")
+ node.children.each do |childnode|
+ if childnode.elements.empty?
+ response[childnode.name] = childnode.text
+ else
+ childnode_to_response(response, childnode)
+ end
+ end
+
+
response
end
@@ -179,8 +237,9 @@ def commit(parameters)
message_from(response),
response,
authorization: authorization_from(response),
- avs_result: AVSResult.new(code: response["some_avs_response_key"]),
- cvv_result: CVVResult.new(response["some_cvv_response_key"]),
+ avs_result: AVSResult.new(code: avs_result_code_from(response)),
+ cvv_result: CVVResult.new(cvv_result_code_from(response)),
+ error_code: error_result_code_from(response),
test: test?
)
end
@@ -205,6 +264,9 @@ def authorization_from(response)
response["CardEaseReference"]
end
+ def manual_type(options)
+ options[:manual_type] ? options[:manual_type] : "ecommerce"
+ end
end
end
end
diff --git a/lib/active_merchant/billing/gateways/credorax.rb b/lib/active_merchant/billing/gateways/credorax.rb
new file mode 100644
index 00000000000..1018b291b11
--- /dev/null
+++ b/lib/active_merchant/billing/gateways/credorax.rb
@@ -0,0 +1,345 @@
+module ActiveMerchant #:nodoc:
+ module Billing #:nodoc:
+ class CredoraxGateway < Gateway
+ class_attribute :test_url, :live_na_url, :live_eu_url
+
+ self.display_name = "Credorax Gateway"
+ self.homepage_url = "https://www.credorax.com/"
+
+ self.test_url = "https://intconsole.credorax.com/intenv/service/gateway"
+
+ # The live URL is assigned on a per merchant basis once certification has passed
+ # See the Credorax remote tests for the full certification test suite
+ #
+ # Once you have your assigned subdomain, you can override the live URL in your application via:
+ # ActiveMerchant::Billing::CredoraxGateway.live_url = "https://assigned-subdomain.credorax.net/crax_gate/service/gateway"
+ self.live_url = 'https://assigned-subdomain.credorax.net/crax_gate/service/gateway'
+
+ self.supported_countries = %w(DE GB FR IT ES PL NL BE GR CZ PT SE HU RS AT CH BG DK FI SK NO IE HR BA AL LT MK SI LV EE ME LU MT IS AD MC LI SM)
+ self.default_currency = "EUR"
+ self.currencies_without_fractions = %w(CLP JPY KRW PYG VND)
+ self.currencies_with_three_decimal_places = %w(BHD JOD KWD OMR RSD TND)
+
+ self.money_format = :cents
+ self.supported_cardtypes = [:visa, :master, :maestro]
+
+ RESPONSE_MESSAGES = {
+ "00" => "Approved or completed successfully",
+ "01" => "Refer to card issuer",
+ "02" => "Refer to card issuer special condition",
+ "03" => "Invalid merchant",
+ "04" => "Pick up card",
+ "05" => "Do not Honour",
+ "06" => "Error",
+ "07" => "Pick up card special condition",
+ "08" => "Honour with identification",
+ "09" => "Request in progress",
+ "10" => "Approved for partial amount",
+ "11" => "Approved (VIP)",
+ "12" => "Invalid transaction",
+ "13" => "Invalid amount",
+ "14" => "Invalid card number",
+ "15" => "No such issuer",
+ "16" => "Approved, update track 3",
+ "17" => "Customer cancellation",
+ "18" => "Customer dispute",
+ "19" => "Re-enter transaction",
+ "20" => "Invalid response",
+ "21" => "No action taken",
+ "22" => "Suspected malfunction",
+ "23" => "Unacceptable transaction fee",
+ "24" => "File update not supported by receiver",
+ "25" => "No such record",
+ "26" => "Duplicate record update, old record replaced",
+ "27" => "File update field edit error",
+ "28" => "File locked out while update",
+ "29" => "File update error, contact acquirer",
+ "30" => "Format error",
+ "31" => "Issuer signed-off",
+ "32" => "Completed partially",
+ "33" => "Pick-up, expired card",
+ "34" => "Suspect Fraud",
+ "35" => "Pick-up, card acceptor contact acquirer",
+ "36" => "Pick up, card restricted",
+ "37" => "Pick up, call acquirer security",
+ "38" => "Pick up, Allowable PIN tries exceeded",
+ "39" => "Transaction Not Allowed",
+ "40" => "Requested function not supported",
+ "41" => "Lost Card, Pickup",
+ "42" => "No universal account",
+ "43" => "Pick up, stolen card",
+ "44" => "No investment account",
+ "50" => "Do not renew",
+ "51" => "Not sufficient funds",
+ "52" => "No checking Account",
+ "53" => "No savings account",
+ "54" => "Expired card",
+ "55" => "Pin incorrect",
+ "56" => "No card record",
+ "57" => "Transaction not allowed for cardholder",
+ "58" => "Transaction not allowed for merchant",
+ "59" => "Suspected Fraud",
+ "60" => "Card acceptor contact acquirer",
+ "61" => "Exceeds withdrawal amount limit",
+ "62" => "Restricted card",
+ "63" => "Security violation",
+ "64" => "Wrong original amount",
+ "65" => "Activity count limit exceeded",
+ "66" => "Call acquirers security department",
+ "67" => "Card to be picked up at ATM",
+ "68" => "Response received too late.",
+ "70" => "Invalid transaction; contact card issuer",
+ "71" => "Decline PIN not changed",
+ "75" => "Pin tries exceeded",
+ "76" => "Wrong PIN, number of PIN tries exceeded",
+ "77" => "Wrong Reference No.",
+ "78" => "Record Not Found",
+ "79" => "Already reversed",
+ "80" => "Network error",
+ "81" => "Foreign network error / PIN cryptographic error",
+ "82" => "Time out at issuer system",
+ "83" => "Transaction failed",
+ "84" => "Pre-authorization timed out",
+ "85" => "No reason to decline",
+ "86" => "Cannot verify pin",
+ "87" => "Purchase amount only, no cashback allowed",
+ "88" => "MAC sync Error",
+ "89" => "Authentication failure",
+ "91" => "Issuer not available",
+ "92" => "Unable to route at acquirer Module",
+ "93" => "Cannot be completed, violation of law",
+ "94" => "Duplicate Transmission",
+ "95" => "Reconcile error / Auth Not found",
+ "96" => "System malfunction",
+ "R0" => "Stop Payment Order",
+ "R1" => "Revocation of Authorisation Order",
+ "R3" => "Revocation of all Authorisations Order"
+ }
+
+ def initialize(options={})
+ requires!(options, :merchant_id, :cipher_key)
+ super
+ end
+
+ def purchase(amount, payment_method, options={})
+ post = {}
+ add_invoice(post, amount, options)
+ add_payment_method(post, payment_method)
+ add_customer_data(post, options)
+ add_email(post, options)
+ add_3d_secure(post, options)
+ add_echo(post, options)
+ add_transaction_type(post, options)
+
+ commit(:purchase, post)
+ end
+
+ def authorize(amount, payment_method, options={})
+ post = {}
+ add_invoice(post, amount, options)
+ add_payment_method(post, payment_method)
+ add_customer_data(post, options)
+ add_email(post, options)
+ add_3d_secure(post, options)
+ add_echo(post, options)
+ add_transaction_type(post, options)
+
+ commit(:authorize, post)
+ end
+
+ def capture(amount, authorization, options={})
+ post = {}
+ add_invoice(post, amount, options)
+ add_reference(post, authorization)
+ add_customer_data(post, options)
+ add_echo(post, options)
+
+ commit(:capture, post)
+ end
+
+ def void(authorization, options={})
+ post = {}
+ add_customer_data(post, options)
+ reference_action = add_reference(post, authorization)
+ add_echo(post, options)
+ post[:a1] = generate_unique_id
+
+ commit(:void, post, reference_action)
+ end
+
+ def refund(amount, authorization, options={})
+ post = {}
+ add_invoice(post, amount, options)
+ add_reference(post, authorization)
+ add_customer_data(post, options)
+ add_echo(post, options)
+
+ commit(:refund, post)
+ end
+
+ def credit(amount, payment_method, options={})
+ post = {}
+ add_invoice(post, amount, options)
+ add_payment_method(post, payment_method)
+ add_customer_data(post, options)
+ add_email(post, options)
+ add_echo(post, options)
+ add_transaction_type(post, options)
+
+ commit(:credit, post)
+ end
+
+ def verify(credit_card, options={})
+ MultiResponse.run(:use_first_response) do |r|
+ r.process { authorize(100, credit_card, options) }
+ r.process(:ignore_result) { void(r.authorization, options) }
+ end
+ end
+
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r((b1=)\d+), '\1[FILTERED]').
+ gsub(%r((b5=)\d+), '\1[FILTERED]')
+ end
+
+ private
+
+ def add_invoice(post, money, options)
+ currency = options[:currency] || currency(money)
+
+ post[:a4] = localized_amount(money, currency)
+ post[:a1] = generate_unique_id
+ post[:a5] = currency
+ post[:h9] = options[:order_id]
+ end
+
+ CARD_TYPES = {
+ "visa" => '1',
+ "mastercard" => '2',
+ "maestro" => '9'
+ }
+
+ def add_payment_method(post, payment_method)
+ post[:c1] = payment_method.name
+ post[:b2] = CARD_TYPES[payment_method.brand] || ''
+ post[:b1] = payment_method.number
+ post[:b5] = payment_method.verification_value
+ post[:b4] = format(payment_method.year, :two_digits)
+ post[:b3] = format(payment_method.month, :two_digits)
+ end
+
+ def add_customer_data(post, options)
+ post[:d1] = options[:ip] || '127.0.0.1'
+ if (billing_address = options[:billing_address])
+ post[:c5] = billing_address[:address1]
+ post[:c7] = billing_address[:city]
+ post[:c10] = billing_address[:zip]
+ post[:c8] = billing_address[:state]
+ post[:c9] = billing_address[:country]
+ post[:c2] = billing_address[:phone]
+ end
+ end
+
+ def add_reference(post, authorization)
+ response_id, authorization_code, request_id, action = authorization.split(";")
+ post[:g2] = response_id
+ post[:g3] = authorization_code
+ post[:g4] = request_id
+ action || :authorize
+ end
+
+ def add_email(post, options)
+ post[:c3] = options[:email] || 'unspecified@example.com'
+ end
+
+ def add_3d_secure(post, options)
+ return unless options[:eci] && options[:xid]
+ post[:i8] = "#{options[:eci]}:#{(options[:cavv] || "none")}:#{options[:xid]}"
+ end
+
+ def add_echo(post, options)
+ # The d2 parameter is used during the certification process
+ # See remote tests for full certification test suite
+ post[:d2] = options[:echo] unless options[:echo].blank?
+ end
+
+ def add_transaction_type(post, options)
+ post[:a9] = options[:transaction_type] if options[:transaction_type]
+ end
+
+ ACTIONS = {
+ purchase: '1',
+ authorize: '2',
+ capture: '3',
+ authorize_void: '4',
+ refund: '5',
+ credit: '6',
+ purchase_void: '7',
+ refund_void: '8',
+ capture_void: '9'
+ }
+
+ def commit(action, params, reference_action = nil)
+ raw_response = ssl_post(url, post_data(action, params, reference_action))
+ response = parse(raw_response)
+
+ Response.new(
+ success_from(response),
+ message_from(response),
+ response,
+ authorization: "#{response["Z1"]};#{response["Z4"]};#{response["A1"]};#{action}",
+ avs_result: AVSResult.new(code: response["Z9"]),
+ cvv_result: CVVResult.new(response["Z14"]),
+ test: test?
+ )
+ end
+
+ def sign_request(params)
+ params = params.sort
+ params.each { |param| param[1] = param[1].gsub(/[<>()\\]/, ' ') }
+ values = params.map { |param| param[1].strip }
+ Digest::MD5.hexdigest(values.join + @options[:cipher_key])
+ end
+
+ def post_data(action, params, reference_action)
+ params.keys.each { |key| params[key] = params[key].to_s}
+ params[:M] = @options[:merchant_id]
+ params[:O] = request_action(action, reference_action)
+ params[:K] = sign_request(params)
+ params.map {|k, v| "#{k}=#{CGI.escape(v.to_s)}"}.join('&')
+ end
+
+ def request_action(action, reference_action)
+ if reference_action
+ ACTIONS["#{reference_action}_#{action}".to_sym]
+ else
+ ACTIONS[action]
+ end
+ end
+
+ def url
+ test? ? test_url : live_url
+ end
+
+ def parse(body)
+ Hash[CGI::parse(body).map{|k,v| [k.upcase,v.first]}]
+ end
+
+ def success_from(response)
+ response["Z2"] == "0"
+ end
+
+ def message_from(response)
+ if success_from(response)
+ "Succeeded"
+ else
+ RESPONSE_MESSAGES[response["Z6"]] || response["Z3"] || "Unable to read error message"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/active_merchant/billing/gateways/culqi.rb b/lib/active_merchant/billing/gateways/culqi.rb
new file mode 100644
index 00000000000..1b3f0e8f3a2
--- /dev/null
+++ b/lib/active_merchant/billing/gateways/culqi.rb
@@ -0,0 +1,279 @@
+require 'digest/md5'
+
+module ActiveMerchant #:nodoc:
+ module Billing #:nodoc:
+ # Important note:
+ # ===
+ # Culqi merchant accounts are configured for either purchase or auth/capture
+ # modes. This is configured by Culqi when setting up a merchant account and
+ # largely depends on the transaction acquiring bank. Be sure to understand how
+ # your account was configured prior to using this gateway.
+ class CulqiGateway < Gateway
+ self.display_name = "Culqi"
+ self.homepage_url = "https://www.culqi.com"
+
+ self.test_url = "https://staging.paymentz.com/transaction/"
+ self.live_url = "https://secure.culqi.com/transaction/"
+
+ self.supported_countries = ["PE"]
+ self.default_currency = "PEN"
+ self.money_format = :dollars
+ self.supported_cardtypes = [:visa, :master, :diners_club, :american_express]
+
+ def initialize(options={})
+ requires!(options, :merchant_id, :terminal_id, :secret_key)
+ super
+ end
+
+ def purchase(amount, payment_method, options={})
+ authorize(amount, payment_method, options)
+ end
+
+ def authorize(amount, payment_method, options={})
+ if payment_method.is_a?(String)
+ action = :tokenpay
+ else
+ action = :authorize
+ end
+ post = {}
+ add_credentials(post)
+ add_invoice(action, post, amount, options)
+ add_payment_method(post, payment_method, action, options)
+ add_customer_data(post, options)
+ add_checksum(action, post)
+
+ commit(action, post)
+ end
+
+ def capture(amount, authorization, options={})
+ action = :capture
+ post = {}
+ add_credentials(post)
+ add_invoice(action, post, amount, options)
+ add_reference(post, authorization)
+ add_checksum(action, post)
+
+ commit(action, post)
+ end
+
+ def void(authorization, options={})
+ action = :void
+ post = {}
+ add_credentials(post)
+ add_invoice(action, post, nil, options)
+ add_reference(post, authorization)
+ add_checksum(action, post)
+
+ commit(action, post)
+ end
+
+ def refund(amount, authorization, options={})
+ action = :refund
+ post = {}
+ add_credentials(post)
+ add_invoice(action, post, amount, options)
+ add_reference(post, authorization)
+ add_checksum(action, post)
+
+ commit(action, post)
+ end
+
+ def verify(credit_card, options={})
+ MultiResponse.run(:use_first_response) do |r|
+ r.process { authorize(1000, credit_card, options) }
+ r.process(:ignore_result) { void(r.authorization, options) }
+ end
+ end
+
+ def verify_credentials
+ response = void("0", order_id: "0")
+ response.message.include? "Transaction not found"
+ end
+
+ def store(credit_card, options={})
+ action = :tokenize
+ post = {}
+ post[:partnerid] = options[:partner_id] if options[:partner_id]
+ post[:cardholderid] = options[:cardholder_id] if options[:cardholder_id]
+ add_credentials(post)
+ add_payment_method(post, credit_card, action, options)
+ add_customer_data(post, options)
+ add_checksum(action, post)
+
+ commit(action, post)
+ end
+
+ def invalidate(authorization, options={})
+ action = :invalidate
+ post = {}
+ post[:partnerid] = options[:partner_id] if options[:partner_id]
+ add_credentials(post)
+ post[:token] = authorization
+ add_checksum(action, post)
+
+ commit(action, post)
+ end
+
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r((cardnumber=)\d+), '\1[FILTERED]').
+ gsub(%r((cvv=)\d+), '\1[FILTERED]')
+ end
+
+ private
+
+ def add_credentials(post)
+ post[:toid] = @options[:merchant_id]
+ post[:totype] = 'Culqi'
+ post[:terminalid] = @options[:terminal_id]
+ post[:language] = 'ENG'
+ end
+
+ def add_invoice(action, post, money, options)
+ case action
+ when :capture
+ post[:captureamount] = amount(money)
+ when :refund
+ post[:refundamount] = amount(money)
+ post[:reason] = 'none'
+ else
+ post[:amount] = amount(money)
+ end
+
+ post[:description] = options[:order_id]
+ post[:redirecturl] = options[:redirect_url] || 'http://www.example.com'
+ end
+
+ def add_payment_method(post, payment_method, action, options)
+ if payment_method.is_a?(String)
+ post[:token] = payment_method
+ post[:cvv] = options[:cvv] if options[:cvv]
+ else
+ post[:cardnumber] = payment_method.number
+ post[:cvv] = payment_method.verification_value
+ post[:firstname], post[:lastname] = payment_method.name.split(' ')
+ if action == :tokenize
+ post[:expirymonth] = format(payment_method.month, :two_digits)
+ post[:expiryyear] = format(payment_method.year, :four_digits)
+ else
+ post[:expiry_month] = format(payment_method.month, :two_digits)
+ post[:expiry_year] = format(payment_method.year, :four_digits)
+ end
+ end
+ end
+
+ def add_customer_data(post, options)
+ post[:emailaddr] = options[:email] || 'unspecified@example.com'
+ if (billing_address = options[:billing_address] || options[:address])
+ post[:street] = [billing_address[:address1], billing_address[:address2]].join(' ')
+ post[:city] = billing_address[:city]
+ post[:state] = billing_address[:state]
+ post[:countrycode] = billing_address[:country]
+ post[:zip] = billing_address[:zip]
+ post[:telno] = billing_address[:phone]
+ post[:telnocc] = options[:telephone_country_code] || '051'
+ end
+ end
+
+ def add_checksum(action, post)
+ checksum_elements = case action
+ when :capture; [post[:toid], post[:trackingid], post[:captureamount], @options[:secret_key]]
+ when :void; [post[:toid], post[:description], post[:trackingid], @options[:secret_key]]
+ when :refund; [post[:toid], post[:trackingid], post[:refundamount], @options[:secret_key]]
+ when :tokenize; [post[:partnerid], post[:cardnumber], post[:cvv], @options[:secret_key]]
+ when :invalidate; [post[:partnerid], post[:token], @options[:secret_key]]
+ else [post[:toid], post[:totype], post[:amount], post[:description], post[:redirecturl],
+ post[:cardnumber] || post[:token], @options[:secret_key]]
+ end
+
+ post[:checksum] = Digest::MD5.hexdigest(checksum_elements.compact.join('|'))
+ end
+
+ def add_reference(post, authorization)
+ post[:trackingid] = authorization
+ end
+
+ ACTIONS = {
+ authorize: "SingleCallGenericServlet",
+ capture: "SingleCallGenericCaptureServlet",
+ void: "SingleCallGenericVoid",
+ refund: "SingleCallGenericReverse",
+ tokenize: "SingleCallTokenServlet",
+ invalidate: "SingleCallInvalidateToken",
+ tokenpay: "SingleCallTokenTransaction",
+ }
+
+ def commit(action, params)
+ response = begin
+ parse(ssl_post(url + ACTIONS[action], post_data(action, params), headers))
+ rescue ResponseError => e
+ parse(e.response.body)
+ end
+
+ success = success_from(response)
+
+ Response.new(
+ success,
+ message_from(response),
+ response,
+ authorization: success ? authorization_from(response) : nil,
+ cvv_result: success ? cvvresult_from(response) : nil,
+ error_code: success ? nil : error_from(response),
+ test: test?
+ )
+ end
+
+ def headers
+ {
+ "Accept" => "application/json",
+ "Content-Type" => "application/x-www-form-urlencoded;charset=UTF-8"
+ }
+ end
+
+ def post_data(action, params)
+ params.map {|k, v| "#{k}=#{CGI.escape(v.to_s)}"}.join('&')
+ end
+
+ def url
+ test? ? test_url : live_url
+ end
+
+ def parse(body)
+ begin
+ JSON.parse(body)
+ rescue JSON::ParserError
+ message = "Invalid JSON response received from CulqiGateway. Please contact CulqiGateway if you continue to receive this message."
+ message += "(The raw response returned by the API was #{body.inspect})"
+ {
+ "status" => "N",
+ "statusdescription" => message
+ }
+ end
+ end
+
+ def success_from(response)
+ response['status'] == 'Y'
+ end
+
+ def message_from(response)
+ response['statusdescription'] || response['statusDescription']
+ end
+
+ def authorization_from(response)
+ response['trackingid'] || response['token']
+ end
+
+ def cvvresult_from(response)
+ CVVResult.new(response['cvvresult'])
+ end
+
+ def error_from(response)
+ response['resultcode']
+ 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 8175a78f429..85a1c08a0a0 100644
--- a/lib/active_merchant/billing/gateways/cyber_source.rb
+++ b/lib/active_merchant/billing/gateways/cyber_source.rb
@@ -26,8 +26,8 @@ class CyberSourceGateway < Gateway
XSD_VERSION = "1.121"
- self.supported_cardtypes = [:visa, :master, :american_express, :discover]
- self.supported_countries = %w(US BR CA CN DK FI FR DE JP MX NO SE GB SG)
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb, :switch, :dankort, :maestro]
+ self.supported_countries = %w(US BR CA CN DK FI FR DE JP MX NO SE GB SG LB)
self.default_currency = 'USD'
self.currencies_without_fractions = %w(JPY)
@@ -39,7 +39,12 @@ class CyberSourceGateway < Gateway
:visa => '001',
:master => '002',
:american_express => '003',
- :discover => '004'
+ :discover => '004',
+ :diners_club => '005',
+ :jcb => '007',
+ :switch => '024',
+ :dankort => '034',
+ :maestro => '042'
}
@@response_codes = {
@@ -253,6 +258,8 @@ def build_auth_request(money, creditcard_or_reference, options)
add_decision_manager_fields(xml, options)
add_mdd_fields(xml, options)
add_auth_service(xml, creditcard_or_reference, options)
+ add_threeds_services(xml, options)
+ add_payment_network_token(xml) if network_tokenization?(creditcard_or_reference)
add_business_rules_data(xml, creditcard_or_reference, options)
xml.target!
end
@@ -288,6 +295,8 @@ def build_purchase_request(money, payment_method_or_reference, options)
add_check_service(xml)
else
add_purchase_service(xml, payment_method_or_reference, options)
+ add_threeds_services(xml, options)
+ add_payment_network_token(xml) if network_tokenization?(payment_method_or_reference)
add_business_rules_data(xml, payment_method_or_reference, options) unless options[:pinless_debit_card]
end
xml.target!
@@ -350,6 +359,7 @@ def build_create_subscription_request(payment_method, options)
add_check_service(xml, options)
else
add_purchase_service(xml, payment_method, options)
+ add_payment_network_token(xml) if network_tokenization?(payment_method)
end
end
add_subscription_create_service(xml, options)
@@ -466,6 +476,8 @@ def add_creditcard(xml, creditcard)
end
def add_decision_manager_fields(xml, options)
+ return unless options[:decision_manager_enabled]
+
xml.tag! 'decisionManager' do
xml.tag! 'enabled', options[:decision_manager_enabled] if options[:decision_manager_enabled]
xml.tag! 'profile', options[:decision_manager_profile] if options[:decision_manager_profile]
@@ -473,8 +485,10 @@ def add_decision_manager_fields(xml, options)
end
def add_mdd_fields(xml, options)
+ return unless options.keys.any? { |key| key.to_s.start_with?("mdd_field") }
+
xml.tag! 'merchantDefinedData' do
- (1..20).each do |each|
+ (1..100).each do |each|
key = "mdd_field_#{each}".to_sym
xml.tag!("field#{each}", options[key]) if options[key]
end
@@ -498,7 +512,7 @@ def add_tax_service(xml)
def add_auth_service(xml, payment_method, options)
if network_tokenization?(payment_method)
- add_network_tokenization(xml, payment_method, options)
+ add_auth_network_tokenization(xml, payment_method, options)
else
xml.tag! 'ccAuthService', {'run' => 'true'}
end
@@ -508,7 +522,7 @@ def network_tokenization?(payment_method)
payment_method.is_a?(NetworkTokenizationCreditCard)
end
- def add_network_tokenization(xml, payment_method, options)
+ def add_auth_network_tokenization(xml, payment_method, options)
return unless network_tokenization?(payment_method)
case card_brand(payment_method).to_sym
@@ -534,9 +548,11 @@ def add_network_tokenization(xml, payment_method, options)
xml.tag!("xid", Base64.encode64(cryptogram[20...40]))
end
end
+ end
+ def add_payment_network_token(xml)
xml.tag! 'paymentNetworkToken' do
- xml.tag!('transactionType', "1")
+ xml.tag!('transactionType', '1')
end
end
@@ -602,7 +618,7 @@ def add_subscription(xml, options, reference = nil)
xml.tag! 'recurringSubscriptionInfo' do
if reference
- _, subscription_id, _ = reference.split(";")
+ subscription_id = reference.split(";")[6]
xml.tag! 'subscriptionID', subscription_id
end
@@ -651,9 +667,18 @@ def add_validate_pinless_debit_service(xml)
xml.tag!'pinlessDebitValidateService', {'run' => 'true'}
end
+ def add_threeds_services(xml, options)
+ xml.tag! 'payerAuthEnrollService', {'run' => 'true'} if options[:payer_auth_enroll_service]
+ if options[:payer_auth_validate_service]
+ xml.tag! 'payerAuthValidateService', {'run' => 'true'} do
+ xml.tag! 'signedPARes', options[:pares]
+ end
+ end
+ end
+
def lookup_country_code(country_field)
country_code = Country.find(country_field) rescue nil
- country_code.code(:alpha2)
+ country_code.code(:alpha2) if country_code
end
# Where we actually build the full SOAP request using builder
@@ -686,11 +711,14 @@ def commit(request, action, amount, options)
response = parse(ssl_post(test? ? self.test_url : self.live_url, build_request(request, options)))
rescue ResponseError => e
response = parse(e.response.body)
+ rescue REXML::ParseException => e
+ response = { message: e.to_s }
end
success = response[:decision] == "ACCEPT"
- message = @@response_codes[('r' + response[:reasonCode]).to_sym] rescue response[:message]
- authorization = success ? [ options[:order_id], response[:requestID], response[:requestToken], action, amount, options[:currency]].compact.join(";") : nil
+ message = response[:message]
+
+ authorization = success ? authorization_from(response, action, amount, options) : nil
Response.new(success, message, response,
:test => test?,
@@ -707,9 +735,10 @@ def parse(xml)
xml = REXML::Document.new(xml)
if root = REXML::XPath.first(xml, "//c:replyMessage")
root.elements.to_a.each do |node|
- case node.name
+ case node.expanded_name
when 'c:reasonCode'
- reply[:message] = reply(node.text)
+ reply[:reasonCode] = node.text
+ reply[:message] = reason_message(node.text)
else
parse_element(reply, node)
end
@@ -726,14 +755,24 @@ def parse_element(reply, node)
node.elements.each{|e| parse_element(reply, e) }
else
if node.parent.name =~ /item/
- parent = node.parent.name + (node.parent.attributes["id"] ? "_" + node.parent.attributes["id"] : '')
- reply[(parent + '_' + node.name).to_sym] = node.text
- else
- reply[node.name.to_sym] = node.text
+ parent = node.parent.name
+ parent += '_' + node.parent.attributes["id"] if node.parent.attributes["id"]
+ parent += '_'
end
+ reply["#{parent}#{node.name}".to_sym] ||= node.text
end
return reply
end
+
+ def reason_message(reason_code)
+ return if reason_code.blank?
+ @@response_codes[:"r#{reason_code}"]
+ end
+
+ def authorization_from(response, action, amount, options)
+ [options[:order_id], response[:requestID], response[:requestToken], action, amount,
+ options[:currency], response[:subscriptionID]].join(";")
+ end
end
end
end
diff --git a/lib/active_merchant/billing/gateways/data_cash.rb b/lib/active_merchant/billing/gateways/data_cash.rb
index bfc05f71959..6a23e86d009 100644
--- a/lib/active_merchant/billing/gateways/data_cash.rb
+++ b/lib/active_merchant/billing/gateways/data_cash.rb
@@ -77,6 +77,16 @@ def refund(money, reference, options = {})
commit(build_transaction_refund_request(money, reference))
end
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(/()\d+(<\/pan>)/i, '\1[FILTERED]\2').
+ gsub(/()\d+(<\/cv2>)/i, '\1[FILTERED]\2').
+ gsub(/().+(<\/password>)/i, '\1[FILTERED]\2')
+ end
private
@@ -168,6 +178,7 @@ def build_transaction_refund_request(money, authorization)
unless money.nil?
xml.tag! :TxnDetails do
xml.tag! :amount, amount(money)
+ xml.tag! :capturemethod, 'ecomm'
end
end
end
@@ -188,6 +199,7 @@ def build_credit_request(money, credit_card, options)
xml.tag! :TxnDetails do
xml.tag! :merchantreference, format_reference_number(options[:order_id])
xml.tag! :amount, amount(money)
+ xml.tag! :capturemethod, 'ecomm'
end
end
end
diff --git a/lib/active_merchant/billing/gateways/dibs.rb b/lib/active_merchant/billing/gateways/dibs.rb
index da5be718a3e..0121a7c6e34 100644
--- a/lib/active_merchant/billing/gateways/dibs.rb
+++ b/lib/active_merchant/billing/gateways/dibs.rb
@@ -9,7 +9,6 @@ class DibsGateway < Gateway
self.supported_countries = ["US", "FI", "NO", "SE", "GB"]
self.default_currency = "USD"
self.money_format = :cents
- self.ssl_version = :TLSv1
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
def initialize(options={})
diff --git a/lib/active_merchant/billing/gateways/digitzs.rb b/lib/active_merchant/billing/gateways/digitzs.rb
new file mode 100644
index 00000000000..d1eadd7ffd2
--- /dev/null
+++ b/lib/active_merchant/billing/gateways/digitzs.rb
@@ -0,0 +1,292 @@
+module ActiveMerchant #:nodoc:
+ module Billing #:nodoc:
+ class DigitzsGateway < Gateway
+ include Empty
+
+ self.test_url = 'https://beta.digitzsapi.com/sandbox'
+ self.live_url = 'https://beta.digitzsapi.com/v3'
+
+ self.supported_countries = ['US']
+ self.default_currency = 'USD'
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
+ self.money_format = :cents
+
+ self.homepage_url = 'https://digitzs.com'
+ self.display_name = 'Digitzs'
+
+ def initialize(options={})
+ requires!(options, :app_key, :api_key)
+ super
+ end
+
+ def purchase(money, payment, options={})
+ MultiResponse.run do |r|
+ r.process { commit("auth/token", app_token_request(options)) }
+ r.process { commit('payments', purchase_request(money, payment, options), options.merge({ app_token: app_token_from(r) })) }
+ end
+ end
+
+ def refund(money, authorization, options={})
+ MultiResponse.run do |r|
+ r.process { commit("auth/token", app_token_request(options)) }
+ r.process { commit('payments', refund_request(money, authorization, options), options.merge({ app_token: app_token_from(r) })) }
+ end
+ end
+
+ def store(payment, options = {})
+ MultiResponse.run do |r|
+ r.process { commit("auth/token", app_token_request(options)) }
+ options.merge!({ app_token: app_token_from(r) })
+
+ if options[:customer_id].present?
+ customer_id = check_customer_exists(options)
+
+ if customer_id
+ r.process { add_credit_card_to_customer(payment, options) }
+ else
+ r.process { add_customer_with_credit_card(payment, options) }
+ end
+ else
+ r.process { add_customer_with_credit_card(payment, options) }
+ end
+ end
+ end
+
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r((Authorization: Bearer ).+), '\1[FILTERED]').
+ gsub(%r((X-Api-Key: )\w+), '\1[FILTERED]').
+ gsub(%r((\"id\\\":\\\").+), '\1[FILTERED]').
+ gsub(%r((\"appKey\\\":\\\").+), '\1[FILTERED]').
+ gsub(%r((\"appToken\\\":\\\").+), '\1[FILTERED]').
+ gsub(%r((\"code\\\":\\\")\d+), '\1[FILTERED]').
+ gsub(%r((\"number\\\":\\\")\d+), '\1[FILTERED]')
+ end
+
+ private
+
+ def new_post
+ {
+ data: {
+ attributes: {}
+ }
+ }
+ end
+
+ def add_split(post, options)
+ return unless options[:payment_type] == "card_split" || options[:payment_type] == "token_split"
+
+ post[:data][:attributes][:split] = {
+ merchantId: options[:split_merchant_id],
+ amount: amount(options[:split_amount])
+ }
+ end
+
+ def add_payment(post, payment, options)
+ if payment.is_a? String
+ customer_id, token = split_authorization(payment)
+ post[:data][:attributes][:token] = {
+ customerId: customer_id,
+ tokenId: token
+ }
+ else
+ post[:data][:attributes][:card] = {
+ type: payment.brand,
+ holder: payment.name,
+ number: payment.number,
+ expiry: expdate(payment),
+ code: payment.verification_value
+ }
+ end
+ end
+
+ def add_transaction(post, money, options)
+ post[:data][:attributes][:transaction] = {
+ amount: amount(money),
+ currency: (options[:currency] || currency(money)),
+ invoice: options[:order_id] || generate_unique_id
+ }
+ end
+
+ def add_address(post, options)
+ if address = options[:billing_address] || options[:address]
+ post[:data][:attributes][:billingAddress] = {
+ line1: address[:address1] || "",
+ line2: address[:address2] || "",
+ city: address[:city] || "",
+ state: address[:state] || "",
+ zip: address[:zip] || "",
+ country: address["country"] || "USA"
+ }
+ end
+ end
+
+ def app_token_request(options)
+ post = new_post
+ post[:data][:type] = "auth"
+ post[:data][:attributes] = { appKey: @options[:app_key] }
+
+ post
+ end
+
+ def purchase_request(money, payment, options)
+ post = new_post
+ post[:data][:type] = "payments"
+ post[:data][:attributes][:merchantId] = options[:merchant_id]
+ post[:data][:attributes][:paymentType] = determine_payment_type(payment, options)
+ add_split(post, options)
+ add_payment(post, payment, options)
+ add_transaction(post, money, options)
+ add_address(post, options)
+
+ post
+ end
+
+ def refund_request(money, authorization, options)
+ post = new_post
+ post[:data][:type] = "payments"
+ post[:data][:attributes][:merchantId] = options[:merchant_id]
+ post[:data][:attributes][:paymentType] = "cardRefund"
+ post[:data][:attributes][:originalTransaction] = {id: authorization}
+ add_transaction(post, money, options)
+
+ post
+ end
+
+ def create_customer_request(payment, options)
+ post = new_post
+ post[:data][:type] = "customers"
+ post[:data][:attributes] = {
+ merchantId: options[:merchant_id],
+ name: payment.name,
+ externalId: "#{SecureRandom.hex(16)}"
+ }
+
+ post
+ end
+
+ def create_token_request(payment, options)
+ post = new_post
+ post[:data][:type] = "tokens"
+ post[:data][:attributes] = {
+ tokenType: "card",
+ customerId: options[:customer_id],
+ label: "Credit Card",
+ }
+ add_payment(post, payment, options)
+ add_address(post, options)
+
+ post
+ end
+
+ def check_customer_exists(options = {})
+ url = (test? ? test_url : live_url)
+ response = parse(ssl_get(url + "/customers/#{options[:customer_id]}", headers(options)))
+
+ return response.try(:[], "data").try(:[], "customerId") if success_from(response)
+ return nil
+ end
+
+ def add_credit_card_to_customer(payment, options = {})
+ commit('tokens', create_token_request(payment, options), options)
+ end
+
+ def add_customer_with_credit_card(payment, options = {})
+ customer_response = commit('customers', create_customer_request(payment, options), options)
+ options.merge!({customer_id: customer_response.authorization})
+ commit('tokens', create_token_request(payment, options), options)
+ end
+
+ def parse(body)
+ JSON.parse(body)
+ end
+
+ def commit(action, parameters, options={})
+ url = (test? ? test_url : live_url)
+ response = parse(ssl_post(url + "/#{action}", parameters.to_json, headers(options)))
+
+ Response.new(
+ success_from(response),
+ message_from(response),
+ response,
+ authorization: authorization_from(response),
+ avs_result: AVSResult.new(code: avs_result_from(response)),
+ cvv_result: CVVResult.new(cvv_result_from(response)),
+ test: test?,
+ error_code: error_code_from(response)
+ )
+ end
+
+ def success_from(response)
+ response["errors"].nil? && response["message"].nil?
+ end
+
+ def message_from(response)
+ return response["message"] if response["message"]
+ return "Success" if success_from(response)
+ response["errors"].map {|error_hash| error_hash["detail"] }.join(", ")
+ end
+
+ def authorization_from(response)
+ if customer_id = response.try(:[], "data").try(:[], "attributes").try(:[], "customerId")
+ "#{customer_id}|#{response.try(:[], "data").try(:[], "id")}"
+ else
+ response.try(:[], "data").try(:[], "id")
+ end
+ end
+
+ def avs_result_from(response)
+ response.try(:[], "data").try(:[], "attributes").try(:[], "transaction").try(:[], "avsResult")
+ end
+
+ def cvv_result_from(response)
+ response.try(:[], "data").try(:[], "attributes").try(:[], "transaction").try(:[], "codeResult")
+ end
+
+ def app_token_from(response)
+ response.params.try(:[], "data").try(:[], "attributes").try(:[], "appToken")
+ end
+
+ def headers(options)
+ headers = {
+ 'Content-Type' => 'application/json',
+ 'x-api-key' => @options[:api_key]
+ }
+
+ headers.merge!({"Authorization" => "Bearer #{options[:app_token]}"}) if options[:app_token]
+ headers
+ end
+
+ def error_code_from(response)
+ unless success_from(response)
+ response["errors"].nil? ? response["message"] : response["errors"].map {|error_hash| error_hash["code"] }.join(", ")
+ end
+ end
+
+ def split_authorization(authorization)
+ customer_id, token = authorization.split("|")
+ [customer_id, token]
+ end
+
+ def determine_payment_type(payment, options)
+ return "cardSplit" if options[:payment_type] == "card_split"
+ return "tokenSplit" if options[:payment_type] == "token_split"
+ return "token" if payment.is_a? String
+ "card"
+ end
+
+ def handle_response(response)
+ case response.code.to_i
+ when 200..499
+ response.body
+ else
+ raise ResponseError.new(response)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/active_merchant/billing/gateways/ebanx.rb b/lib/active_merchant/billing/gateways/ebanx.rb
new file mode 100644
index 00000000000..353bfd9bacb
--- /dev/null
+++ b/lib/active_merchant/billing/gateways/ebanx.rb
@@ -0,0 +1,296 @@
+module ActiveMerchant #:nodoc:
+ module Billing #:nodoc:
+ class EbanxGateway < Gateway
+ self.test_url = 'https://sandbox.ebanx.com/ws/'
+ self.live_url = 'https://api.ebanx.com/ws/'
+
+ self.supported_countries = ['BR', 'MX', 'CO']
+ self.default_currency = 'USD'
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club]
+
+ self.homepage_url = 'http://www.ebanx.com/'
+ self.display_name = 'Ebanx'
+
+ CARD_BRAND = {
+ visa: "visa",
+ master: "master_card",
+ american_express: "amex",
+ discover: "discover",
+ diners_club: "diners"
+ }
+
+ URL_MAP = {
+ purchase: "direct",
+ authorize: "direct",
+ capture: "capture",
+ refund: "refund",
+ void: "cancel",
+ store: "token"
+ }
+
+ HTTP_METHOD = {
+ purchase: :post,
+ authorize: :post,
+ capture: :get,
+ refund: :post,
+ void: :get,
+ store: :post
+ }
+
+ def initialize(options={})
+ requires!(options, :integration_key)
+ super
+ end
+
+ def purchase(money, payment, options={})
+ post = { payment: {} }
+ add_integration_key(post)
+ add_operation(post)
+ add_invoice(post, money, options)
+ add_customer_data(post, payment, options)
+ add_card_or_token(post, payment)
+ add_address(post, options)
+ add_customer_responsible_person(post, payment, options)
+
+ commit(:purchase, post)
+ end
+
+ def authorize(money, payment, options={})
+ post = { payment: {} }
+ add_integration_key(post)
+ add_operation(post)
+ add_invoice(post, money, options)
+ add_customer_data(post, payment, options)
+ add_card_or_token(post, payment)
+ add_address(post, options)
+ add_customer_responsible_person(post, payment, options)
+ post[:payment][:creditcard][:auto_capture] = false
+
+ commit(:authorize, post)
+ end
+
+ def capture(money, authorization, options={})
+ post = {}
+ add_integration_key(post)
+ post[:hash] = authorization
+ post[:amount] = amount(money)
+
+ commit(:capture, post)
+ end
+
+ def refund(money, authorization, options={})
+ post = {}
+ add_integration_key(post)
+ add_operation(post)
+ add_authorization(post, authorization)
+ post[:amount] = amount(money)
+ post[:description] = options[:description]
+
+ commit(:refund, post)
+ end
+
+ def void(authorization, options={})
+ post = {}
+ add_integration_key(post)
+ add_authorization(post, authorization)
+
+ commit(:void, post)
+ end
+
+ def store(credit_card, options={})
+ post = {}
+ add_integration_key(post)
+ add_payment_details(post, credit_card)
+ post[:country] = customer_country(options)
+
+ commit(:store, post)
+ end
+
+ def verify(credit_card, options={})
+ MultiResponse.run(:use_first_response) do |r|
+ r.process { authorize(100, credit_card, options) }
+ r.process(:ignore_result) { void(r.authorization, options) }
+ end
+ end
+
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(/(integration_key\\?":\\?")(\d*)/, '\1[FILTERED]').
+ gsub(/(card_number\\?":\\?")(\d*)/, '\1[FILTERED]').
+ gsub(/(card_cvv\\?":\\?")(\d*)/, '\1[FILTERED]')
+ end
+
+ private
+
+ def add_integration_key(post)
+ post[:integration_key] = @options[:integration_key].to_s
+ end
+
+ def add_operation(post)
+ post[:operation] = "request"
+ end
+
+ def add_authorization(post, authorization)
+ post[:hash] = authorization
+ end
+
+ def add_customer_data(post, payment, options)
+ post[:payment][:name] = customer_name(payment, options)
+ post[:payment][:email] = options[:email] || "unspecified@example.com"
+ post[:payment][:document] = options[:document]
+ post[:payment][:birth_date] = options[:birth_date] if options[:birth_date]
+ end
+
+ def add_customer_responsible_person(post, payment, options)
+ post[:payment][:person_type] = options[:person_type] if options[:person_type]
+ if options[:person_type] && options[:person_type].downcase == 'business'
+ post[:payment][:responsible] = {}
+ post[:payment][:responsible][:name] = options[:responsible_name] if options[:responsible_name]
+ post[:payment][:responsible][:document] = options[:responsible_document] if options[:responsible_document]
+ post[:payment][:responsible][:birth_date] = options[:responsible_birth_date] if options[:responsible_birth_date]
+ end
+ end
+
+ def add_address(post, options)
+ if address = options[:billing_address] || options[:address]
+ post[:payment][:address] = address[:address1].split[1..-1].join(" ") if address[:address1]
+ post[:payment][:street_number] = address[:address1].split.first if address[:address1]
+ post[:payment][:city] = address[:city]
+ post[:payment][:state] = address[:state]
+ post[:payment][:zipcode] = address[:zip]
+ post[:payment][:country] = address[:country].downcase
+ post[:payment][:phone_number] = address[:phone]
+ end
+ end
+
+ def add_invoice(post, money, options)
+ post[:payment][:amount_total] = amount(money)
+ post[:payment][:currency_code] = (options[:currency] || currency(money))
+ post[:payment][:merchant_payment_code] = options[:order_id]
+ post[:payment][:instalments] = options[:instalments] || 1
+ end
+
+ def add_card_or_token(post, payment)
+ if payment.is_a?(String)
+ payment, brand = payment.split("|")
+ end
+ post[:payment][:payment_type_code] = payment.is_a?(String) ? brand : CARD_BRAND[payment.brand.to_sym]
+ post[:payment][:creditcard] = payment_details(payment)
+ end
+
+ def add_payment_details(post, payment)
+ post[:payment_type_code] = CARD_BRAND[payment.brand.to_sym]
+ post[:creditcard] = payment_details(payment)
+ end
+
+ def payment_details(payment)
+ if payment.is_a?(String)
+ { token: payment }
+ else
+ {
+ card_number: payment.number,
+ card_name: payment.name,
+ card_due_date: "#{payment.month}/#{payment.year}",
+ card_cvv: payment.verification_value
+ }
+ end
+ end
+
+ def parse(body)
+ JSON.parse(body)
+ end
+
+ def commit(action, parameters)
+ url = url_for((test? ? test_url : live_url), action, parameters)
+ response = parse(ssl_request(HTTP_METHOD[action], url, post_data(action, parameters), {}))
+
+ success = success_from(action, response)
+
+ Response.new(
+ success,
+ message_from(response),
+ response,
+ authorization: authorization_from(action, parameters, response),
+ test: test?,
+ error_code: error_code_from(response, success)
+ )
+ end
+
+ def success_from(action, response)
+ if [:purchase, :capture, :refund].include?(action)
+ response.try(:[], "payment").try(:[], "status") == "CO"
+ elsif action == :authorize
+ response.try(:[], "payment").try(:[], "status") == "PE"
+ elsif action == :void
+ response.try(:[], "payment").try(:[], "status") == "CA"
+ elsif action == :store
+ response.try(:[], "status") == "SUCCESS"
+ else
+ false
+ end
+ end
+
+ def message_from(response)
+ return response["status_message"] if response["status"] == "ERROR"
+ response.try(:[], "payment").try(:[], "transaction_status").try(:[], "description")
+ end
+
+ def authorization_from(action, parameters, response)
+ if action == :store
+ "#{response.try(:[], "token")}|#{CARD_BRAND[parameters[:payment_type_code].to_sym]}"
+ else
+ response.try(:[], "payment").try(:[], "hash")
+ end
+ end
+
+ def post_data(action, parameters = {})
+ return nil if requires_http_get(action)
+ return convert_to_url_form_encoded(parameters) if action == :refund
+ "request_body=#{parameters.to_json}"
+ end
+
+ def url_for(hostname, action, parameters)
+ return "#{hostname}#{URL_MAP[action]}?#{convert_to_url_form_encoded(parameters)}" if requires_http_get(action)
+ "#{hostname}#{URL_MAP[action]}"
+ end
+
+ def requires_http_get(action)
+ return true if [:capture, :void].include?(action)
+ false
+ end
+
+ def convert_to_url_form_encoded(parameters)
+ parameters.map do |key, value|
+ next if value != false && value.blank?
+ "#{key}=#{value}"
+ end.compact.join("&")
+ end
+
+ def error_code_from(response, success)
+ unless success
+ return response["status_code"] if response["status"] == "ERROR"
+ response.try(:[], "payment").try(:[], "transaction_status").try(:[], "code")
+ end
+ end
+
+ def customer_country(options)
+ if country = options[:country] || (options[:billing_address][:country] if options[:billing_address])
+ country.downcase
+ end
+ end
+
+ def customer_name(payment, options)
+ address_name = options[:billing_address][:name] if options[:billing_address] && options[:billing_address][:name]
+ if payment.is_a?(String)
+ address_name || "Not Provided"
+ else
+ payment.name
+ end
+ end
+ end
+ end
+end
diff --git a/lib/active_merchant/billing/gateways/elavon.rb b/lib/active_merchant/billing/gateways/elavon.rb
index 655e8b58de6..2e160ed9343 100644
--- a/lib/active_merchant/billing/gateways/elavon.rb
+++ b/lib/active_merchant/billing/gateways/elavon.rb
@@ -2,40 +2,13 @@
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
- # = Elavon Virtual Merchant Gateway
- #
- # == Example use:
- #
- # gateway = ActiveMerchant::Billing::ElavonGateway.new(
- # :login => "my_virtual_merchant_id",
- # :password => "my_virtual_merchant_pin",
- # :user => "my_virtual_merchant_user_id" # optional
- # )
- #
- # # set up credit card obj as in main ActiveMerchant example
- # creditcard = ActiveMerchant::Billing::CreditCard.new(
- # :type => 'visa',
- # :number => '41111111111111111',
- # :month => 10,
- # :year => 2011,
- # :first_name => 'Bob',
- # :last_name => 'Bobsen'
- # )
- #
- # # run request
- # response = gateway.purchase(1000, creditcard) # authorize and capture 10 USD
- #
- # puts response.success? # Check whether the transaction was successful
- # puts response.message # Retrieve the message returned by Elavon
- # puts response.authorization # Retrieve the unique transaction ID returned by Elavon
- #
class ElavonGateway < Gateway
include Empty
class_attribute :test_url, :live_url, :delimiter, :actions
- self.test_url = 'https://demo.myvirtualmerchant.com/VirtualMerchantDemo/process.do'
- self.live_url = 'https://www.myvirtualmerchant.com/VirtualMerchant/process.do'
+ self.test_url = 'https://api.demo.convergepay.com/VirtualMerchantDemo/process.do'
+ self.live_url = 'https://api.convergepay.com/VirtualMerchant/process.do'
self.display_name = 'Elavon MyVirtualMerchant'
self.supported_countries = %w(US CA PR DE IE NO PL LU BE NL)
@@ -55,23 +28,11 @@ class ElavonGateway < Gateway
:update => 'CCUPDATETOKEN',
}
- # Initialize the Gateway
- #
- # The gateway requires that a valid login and password be passed
- # in the +options+ hash.
- #
- # ==== Options
- #
- # * :login -- Merchant ID
- # * :password -- PIN
- # * :user -- Specify a subuser of the account (optional)
- # * :test => +true+ or +false+ -- Force test transactions
def initialize(options = {})
requires!(options, :login, :password)
super
end
- # Make a purchase
def purchase(money, payment_method, options = {})
form = {}
add_salestax(form, options)
@@ -85,16 +46,9 @@ def purchase(money, payment_method, options = {})
add_customer_data(form, options)
add_test_mode(form, options)
add_ip(form, options)
- commit(:purchase, money, form)
+ commit(:purchase, money, form, options)
end
- # Authorize a credit card for a given amount.
- #
- # ==== Parameters
- # * money - The amount to be authorized as an Integer value in cents.
- # * credit_card - The CreditCard details for the transaction.
- # * options
- # * :billing_address - The billing address for the cardholder.
def authorize(money, creditcard, options = {})
form = {}
add_salestax(form, options)
@@ -104,16 +58,9 @@ def authorize(money, creditcard, options = {})
add_customer_data(form, options)
add_test_mode(form, options)
add_ip(form, options)
- commit(:authorize, money, form)
+ commit(:authorize, money, form, options)
end
- # Capture authorized funds from a credit card.
- #
- # ==== Parameters
- # * money - The amount to be captured as an Integer value in cents.
- # * authorization - The approval code returned from the initial authorization.
- # * options
- # * :credit_card - The CreditCard details from the initial transaction (required).
def capture(money, authorization, options = {})
form = {}
if options[:credit_card]
@@ -130,45 +77,23 @@ def capture(money, authorization, options = {})
add_partial_shipment_flag(form, options)
add_test_mode(form, options)
end
- commit(action, money, form)
- end
-
- # Refund a transaction.
- #
- # This transaction indicates to the gateway that
- # money should flow from the merchant to the customer.
- #
- # ==== Parameters
- #
- # * money -- The amount to be credited to the customer as an Integer value in cents.
- # * identification -- The ID of the original transaction against which the refund is being issued.
- # * options -- A hash of parameters.
+ commit(action, money, form, options)
+ end
+
def refund(money, identification, options = {})
form = {}
add_txn_id(form, identification)
add_test_mode(form, options)
- commit(:refund, money, form)
+ commit(:refund, money, form, options)
end
- # Void a previous transaction
- #
- # ==== Parameters
- #
- # * authorization - The authorization returned from the previous request.
def void(identification, options = {})
form = {}
add_txn_id(form, identification)
add_test_mode(form, options)
- commit(:void, nil, form)
+ commit(:void, nil, form, options)
end
- # Make a credit to a card. Use the refund method if you'd like to credit using
- # previous transaction
- #
- # ==== Parameters
- # * money - The amount to be credited as an Integer value in cents.
- # * creditcard - The credit card to be credited.
- # * options
def credit(money, creditcard, options = {})
if creditcard.is_a?(String)
raise ArgumentError, "Reference credits are not supported. Please supply the original credit card or use the #refund method."
@@ -180,7 +105,7 @@ def credit(money, creditcard, options = {})
add_address(form, options)
add_customer_data(form, options)
add_test_mode(form, options)
- commit(:credit, money, form)
+ commit(:credit, money, form, options)
end
def verify(credit_card, options = {})
@@ -198,7 +123,7 @@ def store(creditcard, options = {})
add_test_mode(form, options)
add_verification(form, options)
form[:add_token] = 'Y'
- commit(:store, nil, form)
+ commit(:store, nil, form, options)
end
def update(token, creditcard, options = {})
@@ -208,7 +133,18 @@ def update(token, creditcard, options = {})
add_address(form, options)
add_customer_data(form, options)
add_test_mode(form, options)
- commit(:update, nil, form)
+ commit(:update, nil, form, options)
+ end
+
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r((&?ssl_pin=)[^&]*)i, '\1[FILTERED]').
+ gsub(%r((&?ssl_card_number=)[^&\\n\r\n]*)i, '\1[FILTERED]').
+ gsub(%r((&?ssl_cvv2cvc2=)[^&]*)i, '\1[FILTERED]')
end
private
@@ -255,6 +191,11 @@ def add_customer_data(form, options)
form[:email] = truncate(options[:email], 100) unless empty?(options[:email])
form[:customer_code] = truncate(options[:customer], 10) unless empty?(options[:customer])
form[:customer_number] = options[:customer_number] unless empty?(options[:customer_number])
+ if options[:custom_fields]
+ options[:custom_fields].each do |key, value|
+ form[key.to_s] = value
+ end
+ end
end
def add_salestax(form, options)
@@ -313,11 +254,11 @@ def success?(response)
!response.has_key?('errorMessage')
end
- def commit(action, money, parameters)
+ def commit(action, money, parameters, options)
parameters[:amount] = amount(money)
parameters[:transaction_type] = self.actions[action]
- response = parse( ssl_post(test? ? self.test_url : self.live_url, post_data(parameters)) )
+ response = parse( ssl_post(test? ? self.test_url : self.live_url, post_data(parameters, options)) )
Response.new(response['result'] == '0', message_from(response), response,
:test => @options[:test] || test?,
@@ -327,21 +268,22 @@ def commit(action, money, parameters)
)
end
- def post_data(parameters)
+ def post_data(parameters, options)
result = preamble
result.merge!(parameters)
- result.collect { |key, value| post_data_string(key, value) }.join("&")
+ result.collect { |key, value| post_data_string(key, value, options) }.join("&")
end
- def post_data_string(key, value)
- if custom_field?(key)
+ def post_data_string(key, value, options)
+ if custom_field?(key, options)
"#{key}=#{CGI.escape(value.to_s)}"
else
"ssl_#{key}=#{CGI.escape(value.to_s)}"
end
end
- def custom_field?(field_name)
+ def custom_field?(field_name, options)
+ return true if options[:custom_fields] && options[:custom_fields].include?(field_name.to_sym)
field_name == :customer_number
end
diff --git a/lib/active_merchant/billing/gateways/element.rb b/lib/active_merchant/billing/gateways/element.rb
index bdc240f8645..d32c720cbf2 100644
--- a/lib/active_merchant/billing/gateways/element.rb
+++ b/lib/active_merchant/billing/gateways/element.rb
@@ -15,7 +15,7 @@ class ElementGateway < Gateway
self.display_name = 'Element'
SERVICE_TEST_URL = 'https://certservices.elementexpress.com/express.asmx'
- SERVICE_LIVE_URL = 'https://service.elementexpress.com/express.asmx'
+ SERVICE_LIVE_URL = 'https://services.elementexpress.com/express.asmx'
def initialize(options={})
requires!(options, :account_id, :account_token, :application_id, :acceptor_id, :application_name, :application_version)
@@ -29,7 +29,7 @@ def purchase(money, payment, options={})
xml.send(action, xmlns: "https://transaction.elementexpress.com") do
add_credentials(xml)
add_payment_method(xml, payment)
- add_transaction(xml, money)
+ add_transaction(xml, money, options)
add_terminal(xml, options)
add_address(xml, options)
end
@@ -43,7 +43,7 @@ def authorize(money, payment, options={})
xml.CreditCardAuthorization(xmlns: "https://transaction.elementexpress.com") do
add_credentials(xml)
add_payment_method(xml, payment)
- add_transaction(xml, money)
+ add_transaction(xml, money, options)
add_terminal(xml, options)
add_address(xml, options)
end
@@ -226,6 +226,17 @@ def add_address(xml, options)
xml.BillingPhone address[:phone_number] if address[:phone_number]
end
end
+ if shipping_address = options[:shipping_address]
+ xml.address do
+ xml.ShippingAddress1 shipping_address[:address1] if shipping_address[:address1]
+ xml.ShippingAddress2 shipping_address[:address2] if shipping_address[:address2]
+ xml.ShippingCity shipping_address[:city] if shipping_address[:city]
+ xml.ShippingState shipping_address[:state] if shipping_address[:state]
+ xml.ShippingZipcode shipping_address[:zip] if shipping_address[:zip]
+ xml.ShippingEmail shipping_address[:email] if shipping_address[:email]
+ xml.ShippingPhone shipping_address[:phone_number] if shipping_address[:phone_number]
+ end
+ end
end
def parse(xml)
@@ -263,6 +274,8 @@ def commit(action, xml, amount)
message_from(response),
response,
authorization: authorization_from(action, response, amount),
+ avs_result: success_from(response) ? avs_from(response) : nil,
+ cvv_result: success_from(response) ? cvv_from(response) : nil,
test: test?
)
end
@@ -283,6 +296,14 @@ def message_from(response)
response["expressresponsemessage"]
end
+ def avs_from(response)
+ AVSResult.new(code: response["card"]["avsresponsecode"]) if response["card"]
+ end
+
+ def cvv_from(response)
+ CVVResult.new(response["card"]["cvvresponsecode"]) if response["card"]
+ end
+
def split_authorization(authorization)
authorization.split("|")
end
diff --git a/lib/active_merchant/billing/gateways/fat_zebra.rb b/lib/active_merchant/billing/gateways/fat_zebra.rb
index bfbe69d5c70..c63b0c2d1d5 100644
--- a/lib/active_merchant/billing/gateways/fat_zebra.rb
+++ b/lib/active_merchant/billing/gateways/fat_zebra.rb
@@ -3,7 +3,7 @@
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class FatZebraGateway < Gateway
- self.live_url = "https://gateway.fatzebra.com.au/v1.0"
+ self.live_url = "https://gateway.fatzebra.com.au/v1.0"
self.test_url = "https://gateway.sandbox.fatzebra.com.au/v1.0"
self.supported_countries = ['AU']
@@ -14,23 +14,11 @@ class FatZebraGateway < Gateway
self.homepage_url = 'https://www.fatzebra.com.au/'
self.display_name = 'Fat Zebra'
- # Setup a new instance of the gateway.
- #
- # The options hash should include :username and :token
- # You can find your username and token at https://dashboard.fatzebra.com.au
- # Under the Your Account section
def initialize(options = {})
requires!(options, :username, :token)
super
end
- # To create a purchase on a credit card use:
- #
- # purchase(money, creditcard)
- #
- # To charge a tokenized card
- #
- # purchase(money, "abzy87u", :cvv => "123")
def purchase(money, creditcard, options = {})
post = {}
@@ -65,11 +53,6 @@ def capture(money, authorization, options = {})
commit(:post, "purchases/#{CGI.escape(authorization)}/capture", post)
end
- # Refund a transaction
- #
- # amount - Integer - the amount to refund
- # txn_id - String - the original transaction to be refunded
- # reference - String - your transaction reference
def refund(money, txn_id, options={})
post = {}
@@ -81,9 +64,6 @@ def refund(money, txn_id, options={})
commit(:post, "refunds", post)
end
- # Tokenize a credit card
- #
- # The token is returned in the Response#authorization
def store(creditcard, options={})
post = {}
add_creditcard(post, creditcard)
@@ -91,16 +71,25 @@ def store(creditcard, options={})
commit(:post, "credit_cards", post)
end
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
+ gsub(%r(("card_number\\?":\\?")[^"\\]*)i, '\1[FILTERED]').
+ gsub(%r(("cvv\\?":\\?")\d+), '\1[FILTERED]')
+ end
+
private
- # Add the money details to the request
def add_amount(post, money, options)
post[:currency] = (options[:currency] || currency(money))
post[:currency] = post[:currency].upcase if post[:currency]
post[:amount] = money
end
- # Add the credit card details to the request
def add_creditcard(post, creditcard, options = {})
if creditcard.respond_to?(:number)
post[:card_number] = creditcard.number
@@ -121,9 +110,12 @@ def add_creditcard(post, creditcard, options = {})
def add_extra_options(post, options)
extra = {}
+ extra[:ecm] = "32" if options[:recurring]
+ extra[:cavv] = options[:cavv] if options[:cavv]
+ extra[:xid] = options[:xid] if options[:xid]
+ extra[:sli] = options[:sli] if options[:sli]
extra[:name] = options[:merchant] if options[:merchant]
extra[:location] = options[:merchant_location] if options[:merchant_location]
- extra[:ecm] = "32" if options[:recurring]
post[:extra] = extra if extra.any?
end
@@ -135,7 +127,6 @@ def add_ip(post, options)
post[:customer_ip] = options[:ip] || "127.0.0.1"
end
- # Post the data to the gateway
def commit(method, uri, parameters=nil)
response = begin
parse(ssl_request(method, get_url(uri), parameters.to_json, headers))
@@ -180,7 +171,6 @@ def message_from(response)
end
end
- # Parse the returned JSON, if parse errors are raised then return a detailed error.
def parse(response)
begin
JSON.parse(response)
@@ -195,13 +185,11 @@ def parse(response)
end
end
- # Build the URL based on the AM mode and the URI
def get_url(uri)
base = test? ? self.test_url : self.live_url
base + "/" + uri
end
- # Builds the auth and U-A headers for the request
def headers
{
"Authorization" => "Basic " + Base64.strict_encode64(@options[:username].to_s + ":" + @options[:token].to_s).strip,
diff --git a/lib/active_merchant/billing/gateways/first_pay.rb b/lib/active_merchant/billing/gateways/first_pay.rb
index fdf83cb0167..c46353164b3 100644
--- a/lib/active_merchant/billing/gateways/first_pay.rb
+++ b/lib/active_merchant/billing/gateways/first_pay.rb
@@ -3,7 +3,7 @@
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class FirstPayGateway < Gateway
- self.live_url = 'https://secure.1stpaygateway.net/secure/gateway/xmlgateway.aspx'
+ self.live_url = 'https://secure.goemerchant.com/secure/gateway/xmlgateway.aspx'
self.supported_countries = ['US']
self.default_currency = 'USD'
@@ -66,18 +66,20 @@ def add_authentication(post, options)
def add_customer_data(post, options)
post[:owner_email] = options[:email] if options[:email]
post[:remote_ip_address] = options[:ip] if options[:ip]
+ post[:processor_id] = options[:processor_id] if options[:processor_id]
end
def add_address(post, creditcard, options)
- address = options[:billing_address] || options[:address]
- post[:owner_name] = address[:name]
- post[:owner_street] = address[:address1]
- post[:owner_street2] = address[:address2] if address[:address2]
- post[:owner_city] = address[:city]
- post[:owner_state] = address[:state]
- post[:owner_zip] = address[:zip]
- post[:owner_country] = address[:country]
- post[:owner_phone] = address[:phone] if address[:phone]
+ if address = options[:billing_address] || options[:address]
+ post[:owner_name] = address[:name]
+ post[:owner_street] = address[:address1]
+ post[:owner_street2] = address[:address2] if address[:address2]
+ post[:owner_city] = address[:city]
+ post[:owner_state] = address[:state]
+ post[:owner_zip] = address[:zip]
+ post[:owner_country] = address[:country]
+ post[:owner_phone] = address[:phone] if address[:phone]
+ end
end
def add_invoice(post, money, options)
diff --git a/lib/active_merchant/billing/gateways/firstdata_e4.rb b/lib/active_merchant/billing/gateways/firstdata_e4.rb
index 1aacb2fdff6..2644ecb53b1 100755
--- a/lib/active_merchant/billing/gateways/firstdata_e4.rb
+++ b/lib/active_merchant/billing/gateways/firstdata_e4.rb
@@ -34,6 +34,8 @@ class FirstdataE4Gateway < Gateway
E4_BRANDS = BRANDS.merge({:mastercard => "Mastercard"})
+ DEFAULT_ECI = "07"
+
self.supported_cardtypes = BRANDS.keys
self.supported_countries = ["CA", "US"]
self.default_currency = "USD"
@@ -129,14 +131,22 @@ def store(credit_card, options = {})
commit(:store, build_store_request(credit_card, options), credit_card)
end
+ def verify_credentials
+ response = void("0")
+ response.message != "Unauthorized Request. Bad or missing credentials."
+ end
+
def supports_scrubbing?
true
end
def scrub(transcript)
- transcript.
- gsub(%r(().+()), '\1[FILTERED]\2').
- gsub(%r(().+()), '\1[FILTERED]\2')
+ transcript
+ .gsub(%r(().+()), '\1[FILTERED]\2')
+ .gsub(%r(().+()), '\1[FILTERED]\2')
+ .gsub(%r(().+())i, '\1[FILTERED]\2')
+ .gsub(%r(().+()), '\1[FILTERED]\2')
+ .gsub(%r((Card Number : ).*\d)i, '\1[FILTERED]')
end
def supports_network_tokenization?
@@ -149,7 +159,7 @@ def build_request(action, body)
xml = Builder::XmlMarkup.new
xml.instruct!
- xml.tag! "Transaction" do
+ xml.tag! "Transaction", xmlns: "http://secure2.e-xact.com/vplug-in/transaction/rpc-enc/encodedTypes" do
add_credentials(xml)
add_transaction_type(xml, action)
xml << body
@@ -164,14 +174,13 @@ def build_sale_or_authorization_request(money, credit_card_or_store_authorizatio
add_amount(xml, money, options)
if credit_card_or_store_authorization.is_a? String
- add_credit_card_token(xml, credit_card_or_store_authorization)
+ add_credit_card_token(xml, credit_card_or_store_authorization, options)
else
add_credit_card(xml, credit_card_or_store_authorization, options)
end
add_customer_data(xml, options)
add_invoice(xml, options)
- add_card_authentication_data(xml, options)
add_tax_fields(xml, options)
add_level_3(xml, options)
@@ -221,19 +230,33 @@ def add_amount(xml, money, options)
end
def add_credit_card(xml, credit_card, options)
-
if credit_card.respond_to?(:track_data) && credit_card.track_data.present?
xml.tag! "Track1", credit_card.track_data
+ xml.tag! "Ecommerce_Flag", "R"
else
xml.tag! "Card_Number", credit_card.number
xml.tag! "Expiry_Date", expdate(credit_card)
xml.tag! "CardHoldersName", credit_card.name
xml.tag! "CardType", card_type(credit_card.brand)
+ add_credit_card_eci(xml, credit_card, options)
add_credit_card_verification_strings(xml, credit_card, options)
end
end
+ def add_credit_card_eci(xml, credit_card, options)
+ eci = if credit_card.is_a?(NetworkTokenizationCreditCard) && credit_card.source == :apple_pay && card_brand(credit_card) == "discover"
+ # Discover requires any Apple Pay transaction, regardless of in-app
+ # or web, and regardless of the ECI contained in the PKPaymentToken,
+ # to have an ECI value explicitly of 04.
+ "04"
+ else
+ (credit_card.respond_to?(:eci) ? credit_card.eci : nil) || options[:eci] || DEFAULT_ECI
+ end
+
+ xml.tag! "Ecommerce_Flag", eci.to_s =~ /^[0-9]+$/ ? eci.to_s.rjust(2, '0') : eci
+ end
+
def add_credit_card_verification_strings(xml, credit_card, options)
address = options[:billing_address] || options[:address]
if address
@@ -244,36 +267,34 @@ def add_credit_card_verification_strings(xml, credit_card, options)
if credit_card.is_a?(NetworkTokenizationCreditCard)
add_network_tokenization_credit_card(xml, credit_card)
- elsif credit_card.verification_value?
- xml.tag! "CVD_Presence_Ind", "1"
- xml.tag! "VerificationStr2", credit_card.verification_value
+ else
+ if credit_card.verification_value?
+ xml.tag! "CVD_Presence_Ind", "1"
+ xml.tag! "VerificationStr2", credit_card.verification_value
+ end
+
+ add_card_authentication_data(xml, options)
end
end
def add_network_tokenization_credit_card(xml, credit_card)
- xml.tag!("Ecommerce_Flag", credit_card.eci)
-
case card_brand(credit_card).to_sym
- when :visa
- xml.tag!("XID", credit_card.transaction_id) if credit_card.transaction_id
- xml.tag!("CAVV", credit_card.payment_cryptogram)
- when :mastercard
- xml.tag!("XID", credit_card.transaction_id) if credit_card.transaction_id
- xml.tag!("CAVV", credit_card.payment_cryptogram)
when :american_express
cryptogram = Base64.decode64(credit_card.payment_cryptogram)
xml.tag!("XID", Base64.encode64(cryptogram[20...40]))
xml.tag!("CAVV", Base64.encode64(cryptogram[0...20]))
+ else
+ xml.tag!("XID", credit_card.transaction_id) if credit_card.transaction_id
+ xml.tag!("CAVV", credit_card.payment_cryptogram)
end
end
def add_card_authentication_data(xml, options)
xml.tag! "CAVV", options[:cavv]
xml.tag! "XID", options[:xid]
- xml.tag! "Ecommerce_Flag", options[:eci]
end
- def add_credit_card_token(xml, store_authorization)
+ def add_credit_card_token(xml, store_authorization, options)
params = store_authorization.split(";")
credit_card = CreditCard.new(
:brand => params[1],
@@ -286,6 +307,7 @@ def add_credit_card_token(xml, store_authorization)
xml.tag! "Expiry_Date", expdate(credit_card)
xml.tag! "CardHoldersName", credit_card.name
xml.tag! "CardType", card_type(credit_card.brand)
+ add_card_authentication_data(xml, options)
end
def add_customer_data(xml, options)
diff --git a/lib/active_merchant/billing/gateways/forte.rb b/lib/active_merchant/billing/gateways/forte.rb
index d0d7b8cb092..fd50c83a3cc 100644
--- a/lib/active_merchant/billing/gateways/forte.rb
+++ b/lib/active_merchant/billing/gateways/forte.rb
@@ -46,9 +46,8 @@ def authorize(money, payment_method, options={})
def capture(money, authorization, options={})
post = {}
- add_invoice(post, options)
post[:transaction_id] = transaction_id_from(authorization)
- post[:authorization_code] = authorization_code_from(authorization)
+ post[:authorization_code] = authorization_code_from(authorization) || ""
post[:action] = "capture"
commit(:put, post)
@@ -211,7 +210,7 @@ def message_from(response)
end
def authorization_from(response)
- [response["transaction_id"], response["authorization_code"]].join("#")
+ [response.try(:[], "transaction_id"), response.try(:[], "response").try(:[], "authorization_code")].join("#")
end
def endpoint
diff --git a/lib/active_merchant/billing/gateways/global_collect.rb b/lib/active_merchant/billing/gateways/global_collect.rb
index b1f596aaab0..4c069323eb2 100644
--- a/lib/active_merchant/billing/gateways/global_collect.rb
+++ b/lib/active_merchant/billing/gateways/global_collect.rb
@@ -7,10 +7,7 @@ class GlobalCollectGateway < Gateway
self.test_url = "https://api-sandbox.globalcollect.com/"
self.live_url = "https://api.globalcollect.com/"
- self.supported_countries = %w(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)
+ self.supported_countries = ["AD", "AE", "AG", "AI", "AL", "AM", "AO", "AR", "AS", "AT", "AU", "AW", "AX", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BL", "BM", "BN", "BO", "BQ", "BR", "BS", "BT", "BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", "CU", "CV", "CW", "CX", "CY", "CZ", "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", "ER", "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR", "GA", "GB", "GD", "GE", "GF", "GH", "GI", "GL", "GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU", "GW", "GY", "HK", "HN", "HR", "HT", "HU", "ID", "IE", "IL", "IM", "IN", "IS", "IT", "JM", "JO", "JP", "KE", "KG", "KH", "KI", "KM", "KN", "KR", "KW", "KY", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", "LV", "MA", "MC", "MD", "ME", "MF", "MG", "MH", "MK", "MM", "MN", "MO", "MP", "MQ", "MR", "MS", "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", "NC", "NE", "NG", "NI", "NL", "NO", "NP", "NR", "NU", "NZ", "OM", "PA", "PE", "PF", "PG", "PH", "PL", "PN", "PS", "PT", "PW", "QA", "RE", "RO", "RS", "RU", "RW", "SA", "SB", "SC", "SE", "SG", "SH", "SI", "SJ", "SK", "SL", "SM", "SN", "SR", "ST", "SV", "SZ", "TC", "TD", "TG", "TH", "TJ", "TL", "TM", "TN", "TO", "TR", "TT", "TV", "TW", "TZ", "UA", "UG", "US", "UY", "UZ", "VC", "VE", "VG", "VI", "VN", "WF", "WS", "ZA", "ZM", "ZW"]
self.default_currency = "USD"
self.money_format = :cents
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
@@ -23,16 +20,18 @@ def initialize(options={})
def purchase(money, payment, options={})
MultiResponse.run do |r|
r.process { authorize(money, payment, options) }
- r.process { capture(money, r.authorization, options) }
+ r.process { capture(money, r.authorization, options) } unless capture_requested?(r)
end
end
def authorize(money, payment, options={})
post = nestable_hash
add_order(post, money, options)
- add_payment(post, payment)
+ add_payment(post, payment, options)
add_customer_data(post, options, payment)
add_address(post, payment, options)
+ add_creator_info(post, options)
+ add_fraud_fields(post, options)
commit(:authorize, post)
end
@@ -41,18 +40,21 @@ def capture(money, authorization, options={})
post = nestable_hash
add_order(post, money, options)
add_customer_data(post, options)
+ add_creator_info(post, options)
commit(:capture, post, authorization)
end
def refund(money, authorization, options={})
post = nestable_hash
- add_amount(post, money, options={})
+ add_amount(post, money, options)
add_refund_customer_data(post, options)
+ add_creator_info(post, options)
commit(:refund, post, authorization)
end
def void(authorization, options={})
post = nestable_hash
+ add_creator_info(post, options)
commit(:void, post, authorization)
end
@@ -80,7 +82,9 @@ def scrub(transcript)
"visa" => "1",
"american_express" => "2",
"master" => "3",
- "discover" => "128"
+ "discover" => "128",
+ "jcb" => "125",
+ "diners_club" => "132"
}
def add_order(post, money, options)
@@ -97,22 +101,35 @@ def add_order(post, money, options)
}
end
- def add_amount(post, money, options)
+ def add_creator_info(post, options)
+ post['sdkIdentifier'] = options[:sdk_identifier] if options[:sdk_identifier]
+ post['sdkCreator'] = options[:sdk_creator] if options[:sdk_creator]
+ post['integrator'] = options[:integrator] if options[:integrator]
+ post['shoppingCartExtension'] = {}
+ post['shoppingCartExtension']['creator'] = options[:creator] if options[:creator]
+ post['shoppingCartExtension']['name'] = options[:name] if options[:name]
+ post['shoppingCartExtension']['version'] = options[:version] if options[:version]
+ post['shoppingCartExtension']['extensionID'] = options[:extension_ID] if options[:extension_ID]
+ end
+
+ def add_amount(post, money, options={})
post["amountOfMoney"] = {
"amount" => amount(money),
"currencyCode" => options[:currency] || currency(money)
}
end
- def add_payment(post, payment)
+ def add_payment(post, payment, options)
year = format(payment.year, :two_digits)
month = format(payment.month, :two_digits)
expirydate = "#{month}#{year}"
+ pre_authorization = options[:pre_authorization] ? 'PRE_AUTHORIZATION' : 'FINAL_AUTHORIZATION'
post["cardPaymentMethodSpecificInput"] = {
"paymentProductId" => BRAND_MAP[payment.brand],
"skipAuthentication" => "true", # refers to 3DSecure
- "skipFraudService" => "true"
+ "skipFraudService" => "true",
+ "authorizationMode" => pre_authorization
}
post["cardPaymentMethodSpecificInput"]["card"] = {
"cvv" => payment.verification_value,
@@ -129,8 +146,8 @@ def add_customer_data(post, options, payment = nil)
if payment
post["order"]["customer"]["personalInformation"] = {
"name" => {
- "firstName" => payment.first_name,
- "surname" => payment.last_name
+ "firstName" => payment.first_name[0..14],
+ "surname" => payment.last_name[0..69]
}
}
end
@@ -188,6 +205,14 @@ def add_address(post, creditcard, options)
end
end
+ def add_fraud_fields(post, options)
+ fraud_fields = {}
+ fraud_fields.merge!(options[:fraud_fields]) if options[:fraud_fields]
+ fraud_fields.merge!({customerIpAddress: options[:ip]}) if options[:ip]
+
+ post["fraudFields"] = fraud_fields unless fraud_fields.empty?
+ end
+
def parse(body)
JSON.parse(body)
end
@@ -233,7 +258,7 @@ def commit(action, post, authorization = nil)
def headers(action, post, authorization = nil)
{
- "Content-type" => content_type,
+ "Content-Type" => content_type,
"Authorization" => auth_digest(action, post, authorization),
"Date" => date
}
@@ -260,14 +285,20 @@ def content_type
end
def success_from(response)
- !response["errorId"]
+ !response["errorId"] && response["status"] != "REJECTED"
end
def message_from(succeeded, response)
if succeeded
"Succeeded"
else
- response["errors"][0]["message"] || "Unable to read error message"
+ if errors = response["errors"]
+ errors.first.try(:[], "message")
+ elsif status = response["status"]
+ "Status: " + status
+ else
+ "No message available"
+ end
end
end
@@ -281,13 +312,23 @@ def authorization_from(succeeded, response)
def error_code_from(succeeded, response)
unless succeeded
- response["errors"][0]["code"] || "Unable to read error code"
+ if errors = response["errors"]
+ errors.first.try(:[], "code")
+ elsif status = response.try(:[], "statusOutput").try(:[], "statusCode")
+ status.to_s
+ else
+ "No error code available"
+ end
end
end
def nestable_hash
Hash.new {|h,k| h[k] = Hash.new(&h.default_proc) }
end
+
+ def capture_requested?(response)
+ response.params.try(:[], "payment").try(:[], "status") == "CAPTURE_REQUESTED"
+ end
end
end
end
diff --git a/lib/active_merchant/billing/gateways/global_transport.rb b/lib/active_merchant/billing/gateways/global_transport.rb
index d88f2feaee5..3ccd1159513 100644
--- a/lib/active_merchant/billing/gateways/global_transport.rb
+++ b/lib/active_merchant/billing/gateways/global_transport.rb
@@ -9,7 +9,6 @@ class GlobalTransportGateway < Gateway
self.supported_countries = %w(CA PR US)
self.default_currency = 'USD'
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
- self.ssl_version = :TLSv1
self.homepage_url = 'https://www.globalpaymentsinc.com'
self.display_name = 'Global Transport'
@@ -75,6 +74,17 @@ def verify(payment_method, options={})
commit('CardVerify', post, options)
end
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r((&?CardNum=)[^&]*)i, '\1[FILTERED]').
+ gsub(%r((&?CVNum=)[^&]*)i, '\1[FILTERED]').
+ gsub(%r((&?GlobalPassword=)[^&]*)i, '\1[FILTERED]')
+ end
+
private
def add_address(post, options)
@@ -109,6 +119,10 @@ def parse(body)
response[node.name.downcase.to_sym] = node.text
end
+ ext_data = Nokogiri::HTML.parse(response[:extdata])
+ response[:approved_amount] = ext_data.xpath("//approvedamount").text
+ response[:balance_due] = ext_data.xpath("//balancedue").text
+
response
end
@@ -140,7 +154,7 @@ def url
end
def success_from(response)
- (response[:result] == "0")
+ response[:result] == "0" || response[:result] == "200"
end
def message_from(response)
diff --git a/lib/active_merchant/billing/gateways/hps.rb b/lib/active_merchant/billing/gateways/hps.rb
index a6e79e8534f..6bd1141e9d8 100644
--- a/lib/active_merchant/billing/gateways/hps.rb
+++ b/lib/active_merchant/billing/gateways/hps.rb
@@ -73,6 +73,17 @@ def void(transaction_id, options={})
end
end
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r(()[^<]*(<\/hps:CardNbr>))i, '\1[FILTERED]\2').
+ gsub(%r(()[^<]*(<\/hps:CVV2>))i, '\1[FILTERED]\2').
+ gsub(%r(()[^<]*(<\/hps:SecretAPIKey>))i, '\1[FILTERED]\2')
+ end
+
private
def add_reference(xml, transaction_id)
@@ -218,7 +229,7 @@ def commit(action, &request)
data = build_request(action, &request)
response = begin
- parse(ssl_post((test? ? test_url : live_url), data, 'Content-type' => 'text/xml'))
+ parse(ssl_post((test? ? test_url : live_url), data, 'Content-Type' => 'text/xml'))
rescue ResponseError => e
parse(e.response.body)
end
diff --git a/lib/active_merchant/billing/gateways/iats_payments.rb b/lib/active_merchant/billing/gateways/iats_payments.rb
index b597a7d358d..e96f94d3f4f 100644
--- a/lib/active_merchant/billing/gateways/iats_payments.rb
+++ b/lib/active_merchant/billing/gateways/iats_payments.rb
@@ -75,6 +75,19 @@ def unstore(authorization, options = {})
commit(:unstore, post)
end
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r(().+()), '\1[FILTERED]\2').
+ gsub(%r(().+()), '\1[FILTERED]\2').
+ gsub(%r(().+()), '\1[FILTERED]\2').
+ gsub(%r(().+()), '\1[FILTERED]\2').
+ gsub(%r(().+()), '\1[FILTERED]\2')
+ end
+
private
def add_ip(post, options)
@@ -212,7 +225,7 @@ def recursively_parse_element(node, response)
end
def successful_result_message?(response)
- response[:authorization_result].start_with?('OK')
+ response[:authorization_result] ? response[:authorization_result].start_with?('OK') : false
end
def success_from(response)
@@ -220,7 +233,7 @@ def success_from(response)
end
def message_from(response)
- if(!successful_result_message?(response))
+ if !successful_result_message?(response) && response[:authorization_result]
return response[:authorization_result].strip
elsif(response[:status] == 'Failure')
return response[:errors]
diff --git a/lib/active_merchant/billing/gateways/iveri.rb b/lib/active_merchant/billing/gateways/iveri.rb
new file mode 100644
index 00000000000..a1ec20624cc
--- /dev/null
+++ b/lib/active_merchant/billing/gateways/iveri.rb
@@ -0,0 +1,251 @@
+require 'nokogiri'
+
+module ActiveMerchant #:nodoc:
+ module Billing #:nodoc:
+ class IveriGateway < Gateway
+ self.live_url = self.test_url = 'https://portal.nedsecure.co.za/iVeriWebService/Service.asmx'
+
+ self.supported_countries = ['US', 'ZA', 'GB']
+ self.default_currency = 'ZAR'
+ self.money_format = :cents
+ self.supported_cardtypes = [:visa, :master, :american_express]
+
+ self.homepage_url = 'http://www.iveri.com'
+ self.display_name = 'iVeri'
+
+ def initialize(options={})
+ requires!(options, :app_id, :cert_id)
+ super
+ end
+
+ def purchase(money, payment_method, options={})
+ post = build_vxml_request('Debit', options) do |xml|
+ add_auth_purchase_params(xml, money, payment_method, options)
+ end
+
+ commit(post)
+ end
+
+ def authorize(money, payment_method, options={})
+ post = build_vxml_request('Authorisation', options) do |xml|
+ add_auth_purchase_params(xml, money, payment_method, options)
+ end
+
+ commit(post)
+ end
+
+ def capture(money, authorization, options={})
+ post = build_vxml_request('Debit', options) do |xml|
+ add_authorization(xml, authorization, options)
+ end
+
+ commit(post)
+ end
+
+ def refund(money, authorization, options={})
+ post = build_vxml_request('Credit', options) do |xml|
+ add_amount(xml, money, options)
+ add_authorization(xml, authorization, options)
+ end
+
+ commit(post)
+ end
+
+ def void(authorization, options={})
+ post = build_vxml_request('Void', options) do |xml|
+ add_authorization(xml, authorization, options)
+ end
+
+ commit(post)
+ end
+
+ def verify(credit_card, options={})
+ authorize(0, credit_card, options)
+ end
+
+ def verify_credentials
+ void = void('', options)
+ return true if void.message == 'Missing OriginalMerchantTrace'
+ false
+ end
+
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r((CertificateID=\\\")[^\\]*), '\1[FILTERED]').
+ gsub(%r((<PAN>)[^&]*), '\1[FILTERED]').
+ gsub(%r((<CardSecurityCode>)[^&]*), '\1[FILTERED]')
+ end
+
+ private
+
+ def build_xml_envelope(vxml)
+ builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
+ xml[:soap].Envelope 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema', 'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/' do
+ xml[:soap].Body do
+ xml.Execute 'xmlns' => 'http://iveri.com/' do
+ xml.validateRequest 'true'
+ xml.protocol 'V_XML'
+ xml.protocolVersion '2.0'
+ xml.request vxml
+ end
+ end
+ end
+ end
+
+ builder.to_xml
+ end
+
+ def build_vxml_request(action, options)
+ builder = Nokogiri::XML::Builder.new do |xml|
+ xml.V_XML('Version' => '2.0', 'CertificateID' => @options[:cert_id], 'Direction' => 'Request') do
+ xml.Transaction('ApplicationID' => @options[:app_id], 'Command' => action, 'Mode' => mode) do
+ yield(xml)
+ end
+ end
+ end
+
+ builder.doc.root.to_xml
+ end
+
+ def add_auth_purchase_params(post, money, payment_method, options)
+ add_card_holder_authentication(post, options)
+ add_amount(post, money, options)
+ add_electronic_commerce_indicator(post, options)
+ add_payment_method(post, payment_method, options)
+ end
+
+ def add_amount(post, money, options)
+ post.Amount(amount(money))
+ post.Currency(options[:currency] || default_currency)
+ end
+
+ def add_electronic_commerce_indicator(post, options)
+ post.ElectronicCommerceIndicator(options[:eci]) if options[:eci]
+ end
+
+ def add_authorization(post, authorization, options)
+ post.MerchantReference(split_auth(authorization)[2])
+ post.TransactionIndex(split_auth(authorization)[1])
+ post.OriginalRequestID(split_auth(authorization)[0])
+ end
+
+ def add_payment_method(post, payment_method, options)
+ post.ExpiryDate("#{format(payment_method.month, :two_digits)}#{payment_method.year}")
+ add_new_reference(post, options)
+ post.CardSecurityCode(payment_method.verification_value)
+ post.PAN(payment_method.number)
+ end
+
+ def add_new_reference(post, options)
+ post.MerchantReference(options[:order_id] || generate_unique_id)
+ end
+
+ def add_card_holder_authentication(post, options)
+ post.CardHolderAuthenticationID(options[:xid]) if options[:xid]
+ post.CardHolderAuthenticationData(options[:cavv]) if options[:cavv]
+ end
+
+ def commit(post)
+ raw_response = begin
+ ssl_post(live_url, build_xml_envelope(post), headers(post))
+ rescue ActiveMerchant::ResponseError => e
+ e.response.body
+ end
+
+ parsed = parse(raw_response)
+ succeeded = success_from(parsed)
+
+ Response.new(
+ succeeded,
+ message_from(parsed, succeeded),
+ parsed,
+ authorization: authorization_from(parsed),
+ error_code: error_code_from(parsed, succeeded),
+ test: test?
+ )
+ end
+
+ def mode
+ test? ? 'Test' : 'Live'
+ end
+
+ def headers(post)
+ {
+ "Content-Type" => "text/xml; charset=utf-8",
+ "Content-Length" => post.size.to_s,
+ "SOAPAction" => "http://iveri.com/Execute"
+ }
+ end
+
+ def parse(body)
+ parsed = {}
+
+ vxml = Nokogiri::XML(body).remove_namespaces!.xpath("//Envelope/Body/ExecuteResponse/ExecuteResult").inner_text
+ doc = Nokogiri::XML(vxml)
+ doc.xpath("*").each do |node|
+ if (node.elements.empty?)
+ parsed[underscore(node.name)] = node.text
+ else
+ node.elements.each do |childnode|
+ parse_element(parsed, childnode)
+ end
+ end
+ end
+ parsed
+ end
+
+ def parse_element(parsed, node)
+ if !node.attributes.empty?
+ node.attributes.each do |a|
+ parsed[underscore(node.name)+ "_" + underscore(a[1].name)] = a[1].value
+ end
+ end
+
+ if !node.elements.empty?
+ node.elements.each {|e| parse_element(parsed, e) }
+ else
+ parsed[underscore(node.name)] = node.text
+ end
+ end
+
+ def success_from(response)
+ response['result_status'] == '0'
+ end
+
+ def message_from(response, succeeded)
+ if succeeded
+ "Succeeded"
+ else
+ response['result_description'] || response['result_acquirer_description']
+ end
+ end
+
+ def authorization_from(response)
+ "#{response['transaction_request_id']}|#{response['transaction_index']}|#{response['merchant_reference']}"
+ end
+
+ def split_auth(authorization)
+ request_id, transaction_index, merchant_reference = authorization.to_s.split('|')
+ [request_id, transaction_index, merchant_reference]
+ end
+
+ def error_code_from(response, succeeded)
+ unless succeeded
+ response['result_code']
+ end
+ end
+
+ def underscore(camel_cased_word)
+ camel_cased_word.to_s.gsub(/::/, '/').
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
+ tr("-", "_").
+ downcase
+ end
+ end
+ end
+end
diff --git a/lib/active_merchant/billing/gateways/jetpay.rb b/lib/active_merchant/billing/gateways/jetpay.rb
index 03fe5f2b828..9dfdefe2b2a 100644
--- a/lib/active_merchant/billing/gateways/jetpay.rb
+++ b/lib/active_merchant/billing/gateways/jetpay.rb
@@ -165,12 +165,15 @@ def authorize(money, credit_card, options = {})
end
def capture(money, reference, options = {})
- commit(money, build_capture_request(reference.split(";").first, money, options))
+ split_authorization = reference.split(";")
+ transaction_id = split_authorization[0]
+ token = split_authorization[3]
+ commit(money, build_capture_request(transaction_id, money, options), token)
end
def void(reference, options = {})
transaction_id, approval, amount, token = reference.split(";")
- commit(amount.to_i, build_void_request(amount.to_i, transaction_id, approval, token, options))
+ commit(amount.to_i, build_void_request(amount.to_i, transaction_id, approval, token, options), token)
end
def credit(money, transaction_id_or_card, options = {})
@@ -183,9 +186,9 @@ def credit(money, transaction_id_or_card, options = {})
end
def refund(money, reference, options = {})
- params = reference.split(";")
- transaction_id = params[0]
- token = params[3]
+ split_authorization = reference.split(";")
+ transaction_id = split_authorization[0]
+ token = split_authorization[3]
credit_card = options[:credit_card]
commit(money, build_credit_request('CREDIT', money, transaction_id, credit_card, token, options))
end
@@ -279,7 +282,7 @@ def build_credit_request(transaction_type, money, transaction_id, card, token, o
end
end
- def commit(money, request)
+ def commit(money, request, token = nil)
response = parse(ssl_post(url, request))
success = success?(response)
@@ -287,7 +290,7 @@ def commit(money, request)
success ? 'APPROVED' : message_from(response),
response,
:test => test?,
- :authorization => authorization_from(response, money),
+ :authorization => authorization_from(response, money, token),
:avs_result => { :code => response[:avs] },
:cvv_result => response[:cvv2]
)
@@ -330,9 +333,9 @@ def message_from(response)
ACTION_CODE_MESSAGES[response[:action_code]]
end
- def authorization_from(response, money)
+ def authorization_from(response, money, previous_token)
original_amount = amount(money) if money
- [ response[:transaction_id], response[:approval], original_amount, response[:token]].join(";")
+ [ response[:transaction_id], response[:approval], original_amount, (response[:token] || previous_token)].join(";")
end
def add_credit_card(xml, credit_card)
diff --git a/lib/active_merchant/billing/gateways/jetpay_v2.rb b/lib/active_merchant/billing/gateways/jetpay_v2.rb
new file mode 100644
index 00000000000..9ca46601a2f
--- /dev/null
+++ b/lib/active_merchant/billing/gateways/jetpay_v2.rb
@@ -0,0 +1,437 @@
+module ActiveMerchant #:nodoc:
+ module Billing #:nodoc:
+ class JetpayV2Gateway < Gateway
+ self.test_url = 'https://test1.jetpay.com/jetpay'
+ self.live_url = 'https://gateway20.jetpay.com/jetpay'
+
+ self.money_format = :cents
+ self.default_currency = 'USD'
+ self.supported_countries = ['US', 'CA']
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
+
+ self.homepage_url = 'http://www.jetpay.com'
+ self.display_name = 'JetPay'
+
+ API_VERSION = '2.2'
+
+ ACTION_CODE_MESSAGES = {
+ "000" => "Approved.",
+ "001" => "Refer to card issuer.",
+ "002" => "Refer to card issuer, special condition.",
+ "003" => "Invalid merchant or service provider.",
+ "004" => "Pick up card.",
+ "005" => "Do not honor.",
+ "006" => "Error.",
+ "007" => "Pick up card, special condition.",
+ "008" => "Honor with ID (Show ID).",
+ "010" => "Partial approval.",
+ "011" => "VIP approval.",
+ "012" => "Invalid transaction.",
+ "013" => "Invalid amount or exceeds maximum for card program.",
+ "014" => "Invalid account number (no such number).",
+ "015" => "No such issuer.",
+ "019" => "Re-enter Transaction.",
+ "021" => "No action taken (unable to back out prior transaction).",
+ "025" => "Transaction Not Found.",
+ "027" => "File update field edit error.",
+ "028" => "File is temporarily unavailable.",
+ "030" => "Format error.",
+ "039" => "No credit account.",
+ "041" => "Pick up card (lost card).",
+ "043" => "Pick up card (stolen card).",
+ "051" => "Insufficient funds.",
+ "052" => "No checking account.",
+ "053" => "Mp savomgs accpimt.",
+ "054" => "Expired Card.",
+ "055" => "Incorrect PIN.",
+ "057" => "Transaction not permitted to cardholder.",
+ "058" => "Transaction not allowed at terminal.",
+ "061" => "Exceeds withdrawal limit.",
+ "062" => "Restricted card (eg, Country Exclusion).",
+ "063" => "Security violation.",
+ "065" => "Activity count limit exceeded.",
+ "068" => "Response late.",
+ "070" => "Contact card issuer.",
+ "071" => "PIN not changed.",
+ "075" => "Allowable number of PIN-entry tries exceeded.",
+ "076" => "Unable to locate previous message (no matching retrieval reference number).",
+ "077" => "Repeat or reversal data are inconsistent with original message.",
+ "078" => "Blocked (first use), or non-existent account.",
+ "079" => "Key exchange validation failed.",
+ "080" => "Credit issuer unavailable or invalid date.",
+ "081" => "PIN cryptographic error found.",
+ "082" => "Negative online CVV results.",
+ "084" => "Invalid auth life cycle.",
+ "085" => "No reason to decline - CVV or AVS approved.",
+ "086" => "Cannot verify PIN.",
+ "087" => "Cashback not allowed.",
+ "089" => "Issuer Down.",
+ "091" => "Issuer Down.",
+ "092" => "Unable to route transaction.",
+ "093" => "Transaction cannot be completed - violation of law.",
+ "094" => "Duplicate transmission.",
+ "096" => "System error.",
+ "100" => "Deny.",
+ "101" => "Expired Card.",
+ "103" => "Deny - Invalid manual Entry 4DBC.",
+ "104" => "Deny - New card issued.",
+ "105" => "Deny - Account Cancelled.",
+ "106" => "Exceeded PIN Attempts.",
+ "107" => "Please Call Issuer.",
+ "109" => "Invalid merchant.",
+ "110" => "Invalid amount.",
+ "111" => "Invalid account.",
+ "115" => "Service not permitted.",
+ "117" => "Invalid PIN.",
+ "119" => "Card member not enrolled.",
+ "122" => "Invalid card (CID) security code.",
+ "125" => "Invalid effective date.",
+ "181" => "Format error.",
+ "182" => "Please wait.",
+ "183" => "Invalid currency code.",
+ "187" => "Deny - new card issued.",
+ "188" => "Deny - Expiration date required.",
+ "189" => "Deny - Cancelled or Closed Merchant/SE.",
+ "200" => "Deny - Pick up card.",
+ "400" => "Reversal accepted.",
+ "601" => "Reject - EMV Chip Declined Transaction.",
+ "602" => "Reject - Suspected Fraud.",
+ "603" => "Reject - Communications Error.",
+ "604" => "Reject - Insufficient Approval.",
+ "750" => "Velocity Check Fail.",
+ "899" => "Misc Decline.",
+ "900" => "Invalid Message Type.",
+ "901" => "Invalid Merchant ID.",
+ "903" => "Debit not supported.",
+ "904" => "Private label not supported.",
+ "905" => "Invalid card type.",
+ "906" => "Unit not active.",
+ "908" => "Manual card entry invalid.",
+ "909" => "Invalid track information.",
+ "911" => "Master merchant not found.",
+ "912" => "Invalid card format.",
+ "913" => "Invalid card type.",
+ "914" => "Invalid card length.",
+ "917" => "Expired card.",
+ "919" => "Invalid entry type.",
+ "920" => "Invalid amount.",
+ "921" => "Invalid messge format.",
+ "923" => "Invalid ABA.",
+ "924" => "Invalid DDA.",
+ "925" => "Invalid TID.",
+ "926" => "Invalid Password.",
+ "930" => "Invalid zipcode.",
+ "931" => "Invalid Address.",
+ "932" => "Invalid ZIP and Address.",
+ "933" => "Invalid CVV2.",
+ "934" => "Program Not Allowed.",
+ "935" => "Invalid Device/App.",
+ "940" => "Record Not Found.",
+ "941" => "Merchant ID error.",
+ "942" => "Refund Not Allowed.",
+ "943" => "Refund denied.",
+ "955" => "Invalid PIN block.",
+ "956" => "Invalid KSN.",
+ "958" => "Bad Status.",
+ "959" => "Seek Record limit exceeded.",
+ "960" => "Internal Key Database Error.",
+ "961" => "TRANS not Supported. Cash Disbursement required a specific MCC.",
+ "962" => "Invalid PIN key (Unknown KSN).",
+ "981" => "Invalid AVS.",
+ "987" => "Issuer Unavailable.",
+ "988" => "System error SD.",
+ "989" => "Database Error.",
+ "992" => "Transaction Timeout.",
+ "996" => "Bad Terminal ID.",
+ "997" => "Message rejected by association.",
+ "999" => "Communication failure",
+ nil => "No response returned (missing credentials?)."
+ }
+
+ def initialize(options = {})
+ requires!(options, :login)
+ super
+ end
+
+ def purchase(money, payment, options = {})
+ commit(money, build_sale_request(money, payment, options))
+ end
+
+ def authorize(money, payment, options = {})
+ commit(money, build_authonly_request(money, payment, options))
+ end
+
+ def capture(money, reference, options = {})
+ transaction_id, _, _, token = reference.split(";")
+ commit(money, build_capture_request(money, transaction_id, options), token)
+ end
+
+ def void(reference, options = {})
+ transaction_id, _, amount, token = reference.split(";")
+ commit(amount.to_i, build_void_request(amount.to_i, transaction_id, options), token)
+ end
+
+ def credit(money, payment, options = {})
+ commit(money, build_credit_request(money, nil, payment, options))
+ end
+
+ def refund(money, reference, options = {})
+ transaction_id, _, _, token = reference.split(";")
+ commit(money, build_credit_request(money, transaction_id, token, options), token)
+ end
+
+ def verify(credit_card, options = {})
+ authorize(0, credit_card, options)
+ end
+
+ def store(credit_card, options = {})
+ commit(nil, build_store_request(credit_card, options))
+ end
+
+ def supports_scrubbing
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
+ gsub(%r((>)\d+()), '\1[FILTERED]\2').
+ gsub(%r(()\d+()), '\1[FILTERED]\2')
+ end
+
+ private
+
+ def build_xml_request(transaction_type, options = {}, transaction_id = nil, &block)
+ xml = Builder::XmlMarkup.new
+ xml.tag! 'JetPay', 'Version' => API_VERSION do
+ # Basic values needed for any request
+ xml.tag! 'TerminalID', @options[:login]
+ xml.tag! 'TransactionType', transaction_type
+ xml.tag! 'TransactionID', transaction_id.nil? ? generate_unique_id.slice(0, 18) : transaction_id
+ xml.tag! 'Origin', options[:origin] || 'INTERNET'
+ xml.tag! 'IndustryInfo', 'Type' => options[:industry_info] || 'ECOMMERCE'
+ xml.tag! 'Application', (options[:application] || 'n/a'), {'Version' => options[:application_version] || '1.0'}
+ xml.tag! 'Device', (options[:device] || 'n/a'), {'Version' => options[:device_version] || '1.0'}
+ xml.tag! 'Library', 'VirtPOS SDK', 'Version' => '1.5'
+ xml.tag! 'Gateway', 'JetPay'
+ xml.tag! 'DeveloperID', options[:developer_id] || 'n/a'
+
+ if block_given?
+ yield xml
+ else
+ xml.target!
+ end
+ end
+ end
+
+ def build_sale_request(money, payment, options)
+ build_xml_request('SALE', options) do |xml|
+ add_payment(xml, payment)
+ add_addresses(xml, options)
+ add_customer_data(xml, options)
+ add_invoice_data(xml, options)
+ add_user_defined_fields(xml, options)
+ xml.tag! 'TotalAmount', amount(money)
+
+ xml.target!
+ end
+ end
+
+ def build_authonly_request(money, payment, options)
+ build_xml_request('AUTHONLY', options) do |xml|
+ add_payment(xml, payment)
+ add_addresses(xml, options)
+ add_customer_data(xml, options)
+ add_invoice_data(xml, options)
+ add_user_defined_fields(xml, options)
+ xml.tag! 'TotalAmount', amount(money)
+
+ xml.target!
+ end
+ end
+
+ def build_capture_request(money, transaction_id, options)
+ build_xml_request('CAPT', options, transaction_id) do |xml|
+ add_invoice_data(xml, options)
+ add_purchase_order(xml, options)
+ add_user_defined_fields(xml, options)
+ xml.tag! 'TotalAmount', amount(money)
+
+ xml.target!
+ end
+ end
+
+ def build_void_request(money, transaction_id, options)
+ build_xml_request('VOID', options, transaction_id) do |xml|
+ xml.tag! 'TotalAmount', amount(money)
+ xml.target!
+ end
+ end
+
+ def build_credit_request(money, transaction_id, payment, options)
+ build_xml_request('CREDIT', options, transaction_id) do |xml|
+ add_payment(xml, payment)
+ add_invoice_data(xml, options)
+ add_addresses(xml, options)
+ add_customer_data(xml, options)
+ add_user_defined_fields(xml, options)
+ xml.tag! 'TotalAmount', amount(money)
+
+ xml.target!
+ end
+ end
+
+ def build_store_request(credit_card, options)
+ build_xml_request('TOKENIZE', options) do |xml|
+ add_payment(xml, credit_card)
+ add_addresses(xml, options)
+ add_customer_data(xml, options)
+
+ xml.target!
+ end
+ end
+
+ def commit(money, request, token = nil)
+ response = parse(ssl_post(url, request))
+
+ success = success?(response)
+ Response.new(success,
+ success ? 'APPROVED' : message_from(response),
+ response,
+ :test => test?,
+ :authorization => authorization_from(response, money, token),
+ :avs_result => AVSResult.new(:code => response[:avs]),
+ :cvv_result => CVVResult.new(response[:cvv2]),
+ :error_code => success ? nil : error_code_from(response)
+ )
+ end
+
+ def url
+ test? ? test_url : live_url
+ end
+
+ def parse(body)
+ return {} if body.blank?
+
+ xml = REXML::Document.new(body)
+
+ response = {}
+ xml.root.elements.to_a.each do |node|
+ parse_element(response, node)
+ end
+ response
+ end
+
+ def parse_element(response, node)
+ if node.has_elements?
+ node.elements.each{|element| parse_element(response, element) }
+ else
+ response[node.name.underscore.to_sym] = node.text
+ end
+ end
+
+ def format_exp(value)
+ format(value, :two_digits)
+ end
+
+ def success?(response)
+ response[:action_code] == "000"
+ end
+
+ def message_from(response)
+ ACTION_CODE_MESSAGES[response[:action_code]]
+ end
+
+ def authorization_from(response, money, previous_token)
+ original_amount = amount(money) if money
+ [ response[:transaction_id], response[:approval], original_amount, (response[:token] || previous_token)].join(";")
+ end
+
+ def error_code_from(response)
+ response[:action_code]
+ end
+
+ def add_payment(xml, payment)
+ return unless payment
+
+ if payment.is_a? String
+ token = payment
+ _, _, _, token = payment.split(";") if payment.include? ";"
+ xml.tag! 'Token', token if token
+ else
+ add_credit_card(xml, payment)
+ end
+ end
+
+ def add_credit_card(xml, credit_card)
+ xml.tag! 'CardNum', credit_card.number, "CardPresent" => false, "Tokenize" => true
+ xml.tag! 'CardExpMonth', format_exp(credit_card.month)
+ xml.tag! 'CardExpYear', format_exp(credit_card.year)
+
+ if credit_card.first_name || credit_card.last_name
+ xml.tag! 'CardName', [credit_card.first_name,credit_card.last_name].compact.join(' ')
+ end
+
+ unless credit_card.verification_value.nil? || (credit_card.verification_value.length == 0)
+ xml.tag! 'CVV2', credit_card.verification_value
+ end
+ end
+
+ def add_addresses(xml, options)
+ if billing_address = options[:billing_address] || options[:address]
+ xml.tag! 'Billing' do
+ xml.tag! 'Address', [billing_address[:address1], billing_address[:address2]].compact.join(" ")
+ xml.tag! 'City', billing_address[:city]
+ xml.tag! 'StateProv', billing_address[:state]
+ xml.tag! 'PostalCode', billing_address[:zip]
+ xml.tag! 'Country', lookup_country_code(billing_address[:country])
+ xml.tag! 'Phone', billing_address[:phone]
+ xml.tag! 'Email', options[:email] if options[:email]
+ end
+ end
+
+ if shipping_address = options[:shipping_address]
+ xml.tag! 'Shipping' do
+ xml.tag! 'Name', shipping_address[:name]
+ xml.tag! 'Address', [shipping_address[:address1], shipping_address[:address2]].compact.join(" ")
+ xml.tag! 'City', shipping_address[:city]
+ xml.tag! 'StateProv', shipping_address[:state]
+ xml.tag! 'PostalCode', shipping_address[:zip]
+ xml.tag! 'Country', lookup_country_code(shipping_address[:country])
+ xml.tag! 'Phone', shipping_address[:phone]
+ end
+ end
+ end
+
+ def add_customer_data(xml, options)
+ xml.tag! 'UserIPAddress', options[:ip] if options[:ip]
+ end
+
+ def add_invoice_data(xml, options)
+ xml.tag! 'OrderNumber', options[:order_id] if options[:order_id]
+ if tax_amount = options[:tax_amount]
+ xml.tag! 'TaxAmount', tax_amount, {'ExemptInd' => options[:tax_exempt] || "false"}
+ end
+ end
+
+ def add_purchase_order(xml, options)
+ if purchase_order = options[:purchase_order]
+ xml.tag! 'Billing' do
+ xml.tag! 'CustomerPO', purchase_order
+ end
+ end
+ end
+
+ def add_user_defined_fields(xml, options)
+ xml.tag! 'UDField1', options[:ud_field_1] if options[:ud_field_1]
+ xml.tag! 'UDField2', options[:ud_field_2] if options[:ud_field_2]
+ xml.tag! 'UDField3', options[:ud_field_3] if options[:ud_field_3]
+ end
+
+ def lookup_country_code(code)
+ country = Country.find(code) rescue nil
+ country && country.code(:alpha3)
+ end
+ end
+ end
+end
diff --git a/lib/active_merchant/billing/gateways/kushki.rb b/lib/active_merchant/billing/gateways/kushki.rb
new file mode 100644
index 00000000000..ba0148b353f
--- /dev/null
+++ b/lib/active_merchant/billing/gateways/kushki.rb
@@ -0,0 +1,227 @@
+module ActiveMerchant #:nodoc:
+ module Billing #:nodoc:
+ class KushkiGateway < Gateway
+ self.display_name = "Kushki"
+ self.homepage_url = "https://www.kushkipagos.com"
+
+ self.test_url = "https://api-uat.kushkipagos.com/v1/"
+ self.live_url = "https://api.kushkipagos.com/v1/"
+
+ self.supported_countries = ["CO", "EC"]
+ self.default_currency = "USD"
+ self.money_format = :dollars
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club]
+
+ def initialize(options={})
+ requires!(options, :public_merchant_id, :private_merchant_id)
+ super
+ end
+
+ def purchase(amount, payment_method, options={})
+ MultiResponse.run() do |r|
+ r.process { tokenize(amount, payment_method, options) }
+ r.process { charge(amount, r.authorization, options) }
+ end
+ end
+
+ def refund(amount, authorization, options={})
+ action = "refund"
+
+ post = {}
+ post[:ticketNumber] = authorization
+
+ commit(action, post)
+ end
+
+ def void(authorization, options={})
+ action = "void"
+
+ post = {}
+ post[:ticketNumber] = authorization
+
+ commit(action, post)
+ end
+
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r((Private-Merchant-Id: )\d+), '\1[FILTERED]').
+ gsub(%r((\"card\\\":{\\\"number\\\":\\\")\d+), '\1[FILTERED]').
+ gsub(%r((\"cvv\\\":\\\")\d+), '\1[FILTERED]')
+ end
+
+ private
+
+ def tokenize(amount, payment_method, options)
+ action = "tokenize"
+
+ post = {}
+ add_invoice(action, post, amount, options)
+ add_payment_method(post, payment_method, options)
+
+ commit(action, post)
+ end
+
+ def charge(amount, authorization, options)
+ action = "charge"
+
+ post = {}
+ add_reference(post, authorization, options)
+ add_invoice(action, post, amount, options)
+
+ commit(action, post)
+ end
+
+ def add_invoice(action, post, money, options)
+ if action == "tokenize"
+ post[:totalAmount] = amount(money).to_f
+ post[:currency] = options[:currency] || currency(money)
+ post[:isDeferred] = false
+ else
+ sum = {}
+ sum[:currency] = options[:currency] || currency(money)
+ add_amount_defaults(sum, money, options)
+ add_amount_by_country(sum, options)
+ post[:amount] = sum
+ end
+ end
+
+ def add_amount_defaults(sum, money, options)
+ sum[:subtotalIva] = amount(money).to_f
+ sum[:iva] = 0
+ sum[:subtotalIva0] = 0
+
+ if sum[:currency] == "COP"
+ extra_taxes = {}
+ extra_taxes[:propina] = 0
+ extra_taxes[:tasaAeroportuaria] = 0
+ extra_taxes[:agenciaDeViaje] = 0
+ extra_taxes[:iac] = 0
+ sum[:extraTaxes] = extra_taxes
+ else
+ sum[:ice] = 0
+ end
+ end
+
+ def add_amount_by_country(sum, options)
+ if amount = options[:amount]
+ sum[:subtotalIva] = amount[:subtotal_iva].to_f if amount[:subtotal_iva]
+ sum[:iva] = amount[:iva].to_f if amount[:iva]
+ sum[:subtotalIva0] = amount[:subtotal_iva_0].to_f if amount[:subtotal_iva_0]
+ sum[:ice] = amount[:ice].to_f if amount[:ice]
+ if extra_taxes = amount[:extra_taxes] && sum[:currency] == "COP"
+ sum[:extraTaxes][:propina] = extra_taxes[:propina].to_f if extra_taxes[:propina]
+ sum[:extraTaxes][:tasaAeroportuaria] = extra_taxes[:tasa_aeroportuaria].to_f if extra_taxes[:tasa_aeroportuaria]
+ sum[:extraTaxes][:agenciaDeViaje] = extra_taxes[:agencia_de_viaje].to_f if extra_taxes[:agencia_de_viaje]
+ sum[:extraTaxes][:iac] = extra_taxes[:iac].to_f if extra_taxes[:iac]
+ end
+ end
+ end
+
+ def add_payment_method(post, payment_method, options)
+ card = {}
+ card[:number] = payment_method.number
+ card[:cvv] = payment_method.verification_value
+ card[:expiryMonth] = format(payment_method.month, :two_digits)
+ card[:expiryYear] = format(payment_method.year, :two_digits)
+ card[:name] = payment_method.name
+ post[:card] = card
+ end
+
+ def add_reference(post, authorization, options)
+ post[:token] = authorization
+ end
+
+ ENDPOINT = {
+ "tokenize" => "tokens",
+ "charge" => "charges",
+ "void" => "charges",
+ "refund" => "refund"
+ }
+
+ def commit(action, params)
+ response = begin
+ parse(ssl_invoke(action, params))
+ rescue ResponseError => e
+ parse(e.response.body)
+ end
+
+ success = success_from(response)
+
+ Response.new(
+ success,
+ message_from(success, response),
+ response,
+ authorization: success ? authorization_from(response) : nil,
+ error_code: success ? nil : error_from(response),
+ test: test?
+ )
+ end
+
+ def ssl_invoke(action, params)
+ if ["void", "refund"].include?(action)
+ ssl_request(:delete, url(action, params), nil, headers(action))
+ else
+ ssl_post(url(action, params), post_data(params), headers(action))
+ end
+ end
+
+ def headers(action)
+ hfields = {}
+ hfields["Public-Merchant-Id"] = @options[:public_merchant_id] if action == "tokenize"
+ hfields["Private-Merchant-Id"] = @options[:private_merchant_id] unless action == "tokenize"
+ hfields["Content-Type"] = "application/json"
+ hfields
+ end
+
+ def post_data(params)
+ params.to_json
+ end
+
+ def url(action, params)
+ base_url = test? ? test_url : live_url
+
+ if ["void", "refund"].include?(action)
+ base_url + ENDPOINT[action] + "/" + params[:ticketNumber].to_s
+ else
+ base_url + ENDPOINT[action]
+ end
+ end
+
+ def parse(body)
+ begin
+ JSON.parse(body)
+ rescue JSON::ParserError
+ message = "Invalid JSON response received from KushkiGateway. Please contact KushkiGateway if you continue to receive this message."
+ message += " (The raw response returned by the API was #{body.inspect})"
+ {
+ "message" => message
+ }
+ end
+ end
+
+ def success_from(response)
+ return true if response["token"] || response["ticketNumber"] || response["code"] == "K000"
+ end
+
+ def message_from(succeeded, response)
+ if succeeded
+ "Succeeded"
+ else
+ response["message"]
+ end
+ end
+
+ def authorization_from(response)
+ response["token"] || response["ticketNumber"]
+ end
+
+ def error_from(response)
+ response["code"]
+ end
+ end
+ end
+end
diff --git a/lib/active_merchant/billing/gateways/linkpoint.rb b/lib/active_merchant/billing/gateways/linkpoint.rb
index 72979ac5952..653cd8e3946 100644
--- a/lib/active_merchant/billing/gateways/linkpoint.rb
+++ b/lib/active_merchant/billing/gateways/linkpoint.rb
@@ -144,6 +144,8 @@ def initialize(options = {})
}.update(options)
raise ArgumentError, "You need to pass in your pem file using the :pem parameter or set it globally using ActiveMerchant::Billing::LinkpointGateway.pem_file = File.read( File.dirname(__FILE__) + '/../mycert.pem' ) or similar" if @options[:pem].blank?
+
+ @options[:pem].strip!
end
# Send a purchase request with periodic options
diff --git a/lib/active_merchant/billing/gateways/litle.rb b/lib/active_merchant/billing/gateways/litle.rb
index e46ab85a06b..14b3658b0d5 100644
--- a/lib/active_merchant/billing/gateways/litle.rb
+++ b/lib/active_merchant/billing/gateways/litle.rb
@@ -3,24 +3,18 @@
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class LitleGateway < Gateway
- SCHEMA_VERSION = '9.4'
+ SCHEMA_VERSION = '9.12'
- self.test_url = 'https://www.testlitle.com/sandbox/communicator/online'
- self.live_url = 'https://payments.litle.com/vap/communicator/online'
+ self.test_url = 'https://www.testvantivcnp.com/sandbox/communicator/online'
+ self.live_url = 'https://payments.vantivcnp.com/vap/communicator/online'
self.supported_countries = ['US']
self.default_currency = 'USD'
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
- self.homepage_url = 'http://www.litle.com/'
- self.display_name = 'Litle & Co.'
+ self.homepage_url = 'http://www.vantiv.com/'
+ self.display_name = 'Vantiv eCommerce'
- # Public: Create a new Litle gateway.
- #
- # options - A hash of options:
- # :login - The user.
- # :password - The password.
- # :merchant_id - The merchant id.
def initialize(options={})
requires!(options, :login, :password, :merchant_id)
super
@@ -29,23 +23,33 @@ def initialize(options={})
def purchase(money, payment_method, options={})
request = build_xml_request do |doc|
add_authentication(doc)
- doc.sale(transaction_attributes(options)) do
- add_auth_purchase_params(doc, money, payment_method, options)
+ if check?(payment_method)
+ doc.echeckSale(transaction_attributes(options)) do
+ add_echeck_purchase_params(doc, money, payment_method, options)
+ end
+ else
+ doc.sale(transaction_attributes(options)) do
+ add_auth_purchase_params(doc, money, payment_method, options)
+ end
end
end
-
- commit(:sale, request, money)
+ check?(payment_method) ? commit(:echeckSales, request, money) : commit(:sale, request, money)
end
def authorize(money, payment_method, options={})
request = build_xml_request do |doc|
add_authentication(doc)
- doc.authorization(transaction_attributes(options)) do
- add_auth_purchase_params(doc, money, payment_method, options)
+ if check?(payment_method)
+ doc.echeckVerification(transaction_attributes(options)) do
+ add_echeck_purchase_params(doc, money, payment_method, options)
+ end
+ else
+ doc.authorization(transaction_attributes(options)) do
+ add_auth_purchase_params(doc, money, payment_method, options)
+ end
end
end
-
- commit(:authorization, request, money)
+ check?(payment_method) ? commit(:echeckVerification, request, money) : commit(:authorization, request, money)
end
def capture(money, authorization, options={})
@@ -68,19 +72,24 @@ def credit(money, authorization, options = {})
refund(money, authorization, options)
end
- def refund(money, authorization, options={})
- transaction_id, _, _ = split_authorization(authorization)
-
+ def refund(money, payment, options={})
request = build_xml_request do |doc|
add_authentication(doc)
add_descriptor(doc, options)
- doc.credit(transaction_attributes(options)) do
- doc.litleTxnId(transaction_id)
- doc.amount(money) if money
+ doc.send(refund_type(payment), transaction_attributes(options)) do
+ if payment.is_a?(String)
+ transaction_id, kind, _ = split_authorization(payment)
+ doc.litleTxnId(transaction_id)
+ doc.amount(money) if money
+ elsif check?(payment)
+ add_echeck_purchase_params(doc, money, payment, options)
+ else
+ add_auth_purchase_params(doc, money, payment, options)
+ end
end
end
- commit(:credit, request)
+ commit(refund_type(payment), request)
end
def verify(creditcard, options = {})
@@ -111,6 +120,11 @@ def store(payment_method, options = {})
doc.orderId(truncate(options[:order_id], 24))
if payment_method.is_a?(String)
doc.paypageRegistrationId(payment_method)
+ elsif check?(payment_method)
+ doc.echeckForToken do
+ doc.accNum(payment_method.account_number)
+ doc.routingNum(payment_method.routing_number)
+ end
else
doc.accountNumber(payment_method.number)
doc.cardValidationNum(payment_method.verification_value) if payment_method.verification_value
@@ -130,6 +144,8 @@ def scrub(transcript)
gsub(%r(().+()), '\1[FILTERED]\2').
gsub(%r(().+()), '\1[FILTERED]\2').
gsub(%r(().+()), '\1[FILTERED]\2').
+ gsub(%r(().+()), '\1[FILTERED]\2').
+ gsub(%r(().+()), '\1[FILTERED]\2').
gsub(%r(().+()), '\1[FILTERED]\2').
gsub(%r(().+()), '\1[FILTERED]\2').
gsub(%r(().+()), '\1[FILTERED]\2').
@@ -137,6 +153,7 @@ def scrub(transcript)
end
private
+
CARD_TYPE = {
'visa' => 'VI',
'master' => 'MC',
@@ -165,7 +182,27 @@ def scrub(transcript)
}
def void_type(kind)
- (kind == 'authorization') ? :authReversal : :void
+ if kind == 'authorization'
+ :authReversal
+ elsif kind == 'echeckSales'
+ :echeckVoid
+ else
+ :void
+ end
+ end
+
+ def refund_type(payment)
+ transaction_id, kind, _ = split_authorization(payment)
+ if check?(payment) || kind == 'echeckSales'
+ :echeckCredit
+ else
+ :credit
+ end
+ end
+
+ def check?(payment_method)
+ return false if payment_method.is_a?(String)
+ card_brand(payment_method) == 'check'
end
def add_authentication(doc)
@@ -181,12 +218,32 @@ def add_auth_purchase_params(doc, money, payment_method, options)
add_order_source(doc, payment_method, options)
add_billing_address(doc, payment_method, options)
add_shipping_address(doc, payment_method, options)
- add_payment_method(doc, payment_method)
+ add_payment_method(doc, payment_method, options)
add_pos(doc, payment_method)
add_descriptor(doc, options)
+ add_merchant_data(doc, options)
add_debt_repayment(doc, options)
end
+ def add_merchant_data(doc, options={})
+ if options[:affiliate] || options[:campaign] || options[:merchant_grouping_id]
+ doc.merchantData do
+ doc.affiliate(options[:affiliate]) if options[:affiliate]
+ doc.campaign(options[:campaign]) if options[:campaign]
+ doc.merchantGroupingId(options[:merchant_grouping_id]) if options[:merchant_grouping_id]
+ end
+ end
+ end
+
+ def add_echeck_purchase_params(doc, money, payment_method, options)
+ doc.orderId(truncate(options[:order_id], 24))
+ doc.amount(money)
+ add_order_source(doc, payment_method, options)
+ add_billing_address(doc, payment_method, options)
+ add_payment_method(doc, payment_method, options)
+ add_descriptor(doc, options)
+ end
+
def add_descriptor(doc, options)
if options[:descriptor_name] || options[:descriptor_phone]
doc.customBilling do
@@ -200,7 +257,7 @@ def add_debt_repayment(doc, options)
doc.debtRepayment(true) if options[:debt_repayment] == true
end
- def add_payment_method(doc, payment_method)
+ def add_payment_method(doc, payment_method, options)
if payment_method.is_a?(String)
doc.token do
doc.litleToken(payment_method)
@@ -209,6 +266,13 @@ def add_payment_method(doc, payment_method)
doc.card do
doc.track(payment_method.track_data)
end
+ elsif check?(payment_method)
+ doc.echeck do
+ doc.accType(payment_method.account_type)
+ doc.accNum(payment_method.account_number)
+ doc.routingNum(payment_method.routing_number)
+ doc.checkNum(payment_method.number)
+ end
else
doc.card do
doc.type_(CARD_TYPE[payment_method.brand])
@@ -220,6 +284,11 @@ def add_payment_method(doc, payment_method)
doc.cardholderAuthentication do
doc.authenticationValue(payment_method.payment_cryptogram)
end
+ elsif options[:order_source] && options[:order_source].start_with?('3ds')
+ doc.cardholderAuthentication do
+ doc.authenticationValue(options[:cavv]) if options[:cavv]
+ doc.authenticationTransactionId(options[:xid]) if options[:xid]
+ end
end
end
end
@@ -228,7 +297,13 @@ def add_billing_address(doc, payment_method, options)
return if payment_method.is_a?(String)
doc.billToAddress do
- doc.name(payment_method.name)
+ if check?(payment_method)
+ doc.name(payment_method.name)
+ doc.firstName(payment_method.first_name)
+ doc.lastName(payment_method.last_name)
+ else
+ doc.name(payment_method.name)
+ end
doc.email(options[:email]) if options[:email]
add_address(doc, options[:billing_address])
@@ -261,6 +336,8 @@ def add_order_source(doc, payment_method, options)
doc.orderSource(options[:order_source])
elsif payment_method.is_a?(NetworkTokenizationCreditCard) && payment_method.source == :apple_pay
doc.orderSource('applepay')
+ elsif payment_method.is_a?(NetworkTokenizationCreditCard) && payment_method.source == :android_pay
+ doc.orderSource('androidpay')
elsif payment_method.respond_to?(:track_data) && payment_method.track_data.present?
doc.orderSource('retail')
else
diff --git a/lib/active_merchant/billing/gateways/mastercard.rb b/lib/active_merchant/billing/gateways/mastercard.rb
index 5e0df7fca90..ecec06dcff8 100644
--- a/lib/active_merchant/billing/gateways/mastercard.rb
+++ b/lib/active_merchant/billing/gateways/mastercard.rb
@@ -20,6 +20,7 @@ def authorize(amount, payment_method, options={})
add_reference(post, *new_authorization)
add_payment_method(post, payment_method)
add_customer_data(post, payment_method, options)
+ add_3dsecure_id(post, options)
commit('authorize', post)
end
@@ -29,6 +30,7 @@ def capture(amount, authorization, options={})
add_invoice(post, amount, options, :transaction)
add_reference(post, *next_authorization(authorization))
add_customer_data(post, nil, options)
+ add_3dsecure_id(post, options)
commit('capture', post)
end
@@ -160,6 +162,11 @@ def add_customer_data(post, payment_method, options)
post[:customer].merge!(customer)
end
+ def add_3dsecure_id(post, options)
+ return unless options[:threed_secure_id]
+ post.merge!({"3DSecureId" => options[:threed_secure_id]})
+ end
+
def country_code(country)
if country
country = ActiveMerchant::Country.find(country)
diff --git a/lib/active_merchant/billing/gateways/maxipago.rb b/lib/active_merchant/billing/gateways/maxipago.rb
index 6af203d8340..69ae9a8bd9f 100644
--- a/lib/active_merchant/billing/gateways/maxipago.rb
+++ b/lib/active_merchant/billing/gateways/maxipago.rb
@@ -37,7 +37,7 @@ def capture(money, authorization, options = {})
add_order_id(xml, authorization)
add_reference_num(xml, options)
xml.payment do
- add_amount(xml, money)
+ add_amount(xml, money, options)
end
end
end
@@ -54,7 +54,7 @@ def refund(money, authorization, options = {})
add_order_id(xml, authorization)
add_reference_num(xml, options)
xml.payment do
- add_amount(xml, money)
+ add_amount(xml, money, options)
end
end
end
@@ -163,7 +163,7 @@ def add_auth_purchase(xml, money, creditcard, options)
end
end
xml.payment do
- add_amount(xml, money)
+ add_amount(xml, money, options)
add_installments(xml, options)
end
add_billing_address(xml, creditcard, options)
@@ -173,8 +173,9 @@ def add_reference_num(xml, options)
xml.referenceNum(options[:order_id] || generate_unique_id)
end
- def add_amount(xml, money)
+ def add_amount(xml, money, options)
xml.chargeTotal(amount(money))
+ xml.currencyCode(options[:currency] || currency(money) || default_currency)
end
def add_processor_id(xml)
diff --git a/lib/active_merchant/billing/gateways/mercado_pago.rb b/lib/active_merchant/billing/gateways/mercado_pago.rb
new file mode 100644
index 00000000000..c7262d47bfd
--- /dev/null
+++ b/lib/active_merchant/billing/gateways/mercado_pago.rb
@@ -0,0 +1,262 @@
+module ActiveMerchant #:nodoc:
+ module Billing #:nodoc:
+ class MercadoPagoGateway < Gateway
+ self.live_url = self.test_url = 'https://api.mercadopago.com/v1'
+
+ self.supported_countries = ['AR', 'BR', 'CL', 'CO', 'MX', 'PE', 'UY']
+ self.supported_cardtypes = [:visa, :master, :american_express]
+
+ self.homepage_url = 'https://www.mercadopago.com/'
+ self.display_name = 'Mercado Pago'
+ self.money_format = :dollars
+
+ CARD_BRAND = {
+ "american_express" => "amex",
+ "diners_club" => "diners"
+ }
+
+ def initialize(options={})
+ requires!(options, :access_token)
+ super
+ end
+
+ def purchase(money, payment, options={})
+ MultiResponse.run do |r|
+ r.process { commit("tokenize", "card_tokens", card_token_request(money, payment, options)) }
+ options.merge!(card_brand: (CARD_BRAND[payment.brand] || payment.brand))
+ options.merge!(card_token: r.authorization.split("|").first)
+ r.process { commit("purchase", "payments", purchase_request(money, payment, options) ) }
+ end
+ end
+
+ def authorize(money, payment, options={})
+ MultiResponse.run do |r|
+ r.process { commit("tokenize", "card_tokens", card_token_request(money, payment, options)) }
+ options.merge!(card_brand: (CARD_BRAND[payment.brand] || payment.brand))
+ options.merge!(card_token: r.authorization.split("|").first)
+ r.process { commit("authorize", "payments", authorize_request(money, payment, options) ) }
+ end
+ end
+
+ def capture(money, authorization, options={})
+ post = {}
+ authorization, _ = authorization.split("|")
+ post[:capture] = true
+ post[:transaction_amount] = amount(money).to_f
+ commit("capture", "payments/#{authorization}", post)
+ end
+
+ def refund(money, authorization, options={})
+ post = {}
+ authorization, original_amount = authorization.split("|")
+ post[:amount] = amount(money).to_f if original_amount && original_amount.to_f > amount(money).to_f
+ commit("refund", "payments/#{authorization}/refunds", post)
+ end
+
+ def void(authorization, options={})
+ authorization, _ = authorization.split("|")
+ post = { status: "cancelled" }
+ commit("void", "payments/#{authorization}", post)
+ end
+
+ def verify(credit_card, options={})
+ MultiResponse.run(:use_first_response) do |r|
+ r.process { authorize(100, credit_card, options) }
+ r.process(:ignore_result) { void(r.authorization, options) }
+ end
+ end
+
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r((access_token=).*?([^\s]+)), '\1[FILTERED]').
+ gsub(%r((\"card_number\\\":\\\")\d+), '\1[FILTERED]').
+ gsub(%r((\"security_code\\\":\\\")\d+), '\1[FILTERED]')
+ end
+
+ private
+
+ def card_token_request(money, payment, options = {})
+ post = {}
+ post[:card_number] = payment.number
+ post[:security_code] = payment.verification_value
+ post[:expiration_month] = payment.month
+ post[:expiration_year] = payment.year
+ post[:cardholder] = {
+ name: payment.name,
+ identification: {
+ type: options[:cardholder_identification_type],
+ number: options[:cardholder_identification_number]
+ }
+ }
+ post
+ end
+
+ def purchase_request(money, payment, options = {})
+ post = {}
+ add_invoice(post, money, options)
+ add_payment(post, options)
+ add_additional_data(post, options)
+ add_customer_data(post, payment, options)
+ add_address(post, options)
+ post[:binary_mode] = (options[:binary_mode].nil? ? true : options[:binary_mode])
+ post
+ end
+
+ def authorize_request(money, payment, options = {})
+ post = purchase_request(money, payment, options)
+ post.merge!(capture: false)
+ post
+ end
+
+ def add_additional_data(post, options)
+ post[:sponsor_id] = options[:sponsor_id]
+ post[:device_id] = options[:device_id] if options[:device_id]
+ post[:additional_info] = {
+ ip_address: options[:ip_address]
+ }.merge(options[:additional_info] || {})
+
+
+ add_address(post, options)
+ add_shipping_address(post, options)
+ end
+
+ def add_customer_data(post, payment, options)
+ post[:payer] = {
+ email: options[:email],
+ first_name: payment.first_name,
+ last_name: payment.last_name
+ }
+ end
+
+ def add_address(post, options)
+ if address = (options[:billing_address] || options[:address])
+
+ post[:additional_info].merge!({
+ payer: {
+ address: {
+ zip_code: address[:zip],
+ street_name: "#{address[:address1]} #{address[:address2]}"
+ }
+ }
+ })
+ end
+ end
+
+ def add_shipping_address(post, options)
+ if address = options[:shipping_address]
+
+ post[:additional_info].merge!({
+ shipments: {
+ receiver_address: {
+ zip_code: address[:zip],
+ street_name: "#{address[:address1]} #{address[:address2]}"
+ }
+ }
+ })
+ end
+ end
+
+ def split_street_address(address1)
+ street_number = address1.split(" ").first
+
+ if street_name = address1.split(" ")[1..-1]
+ street_name = street_name.join(" ")
+ else
+ nil
+ end
+
+ [street_number, street_name]
+ end
+
+ def add_invoice(post, money, options)
+ post[:transaction_amount] = amount(money).to_f
+ post[:description] = options[:description]
+ post[:installments] = options[:installments] ? options[:installments].to_i : 1
+ post[:statement_descriptor] = options[:statement_descriptor] if options[:statement_descriptor]
+ post[:external_reference] = options[:order_id] || SecureRandom.hex(16)
+ end
+
+ def add_payment(post, options)
+ post[:token] = options[:card_token]
+ post[:payment_method_id] = options[:card_brand]
+ end
+
+ def parse(body)
+ JSON.parse(body)
+ end
+
+ def commit(action, path, parameters)
+ if ["capture", "void"].include?(action)
+ response = parse(ssl_request(:put, url(path), post_data(parameters), headers))
+ else
+ response = parse(ssl_post(url(path), post_data(parameters), headers(parameters)))
+ end
+
+ Response.new(
+ success_from(action, response),
+ message_from(response),
+ response,
+ authorization: authorization_from(response, parameters),
+ test: test?,
+ error_code: error_code_from(action, response)
+ )
+ end
+
+ def success_from(action, response)
+ if action == "refund"
+ response["error"].nil?
+ else
+ ["active", "approved", "authorized", "cancelled", "in_process"].include?(response["status"])
+ end
+ end
+
+ def message_from(response)
+ (response["status_detail"]) || (response["message"])
+ end
+
+ def authorization_from(response, params)
+ [response["id"], params[:transaction_amount]].join("|")
+ end
+
+ def post_data(parameters = {})
+ parameters.clone.tap { |p| p.delete(:device_id) }.to_json
+ end
+
+ def error_code_from(action, response)
+ unless success_from(action, response)
+ if cause = response["cause"]
+ cause.empty? ? nil : cause.first["code"]
+ else
+ response["status"]
+ end
+ end
+ end
+
+ def url(action)
+ full_url = (test? ? test_url : live_url)
+ full_url + "/#{action}?access_token=#{CGI.escape(@options[:access_token])}"
+ end
+
+ def headers(options = {})
+ headers = {
+ "Content-Type" => "application/json"
+ }
+ headers['X-Device-Session-ID'] = options[:device_id] if options[:device_id]
+ headers
+ end
+
+ def handle_response(response)
+ case response.code.to_i
+ when 200..499
+ response.body
+ else
+ raise ResponseError.new(response)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/active_merchant/billing/gateways/merchant_warrior.rb b/lib/active_merchant/billing/gateways/merchant_warrior.rb
index 90dff734d9b..30e34e4edb3 100644
--- a/lib/active_merchant/billing/gateways/merchant_warrior.rb
+++ b/lib/active_merchant/billing/gateways/merchant_warrior.rb
@@ -79,13 +79,13 @@ def add_address(post, options)
post['customerName'] = scrub_name(address[:name])
post['customerCountry'] = address[:country]
- post['customerState'] = address[:state]
+ post['customerState'] = address[:state] || 'N/A'
post['customerCity'] = address[:city]
post['customerAddress'] = address[:address1]
post['customerPostCode'] = address[:zip]
- post['customerIP'] = address[:ip]
- post['customerPhone'] = address[:phone]
- post['customerEmail'] = address[:email]
+ post['customerIP'] = address[:ip]
+ post['customerPhone'] = address[:phone]
+ post['customerEmail'] = address[:email]
end
def add_order_id(post, options)
diff --git a/lib/active_merchant/billing/gateways/mercury.rb b/lib/active_merchant/billing/gateways/mercury.rb
index 7af43962755..bda96b1ab04 100644
--- a/lib/active_merchant/billing/gateways/mercury.rb
+++ b/lib/active_merchant/billing/gateways/mercury.rb
@@ -81,6 +81,19 @@ def store(credit_card, options={})
commit('CardLookup', request)
end
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r(<), "<").
+ gsub(%r(>), ">").
+ gsub(%r(().*())i, '\1[FILTERED]\2').
+ gsub(%r(()(\d|x)*())i, '\1[FILTERED]\3').
+ gsub(%r(()\d*())i, '\1[FILTERED]\2')
+ end
+
private
def build_non_authorized_request(action, money, credit_card, options)
@@ -126,7 +139,7 @@ def build_authorized_request(action, money, authorization, credit_card, options)
xml.tag! 'TranInfo' do
xml.tag! "AuthCode", auth_code
xml.tag! "AcqRefData", acq_ref_data
- xml.tag! "ProcessData", process_data
+ xml.tag! "ProcessData", process_data
end
end
end
diff --git a/lib/active_merchant/billing/gateways/migs.rb b/lib/active_merchant/billing/gateways/migs.rb
index 55974e27407..7995805e1a5 100644
--- a/lib/active_merchant/billing/gateways/migs.rb
+++ b/lib/active_merchant/billing/gateways/migs.rb
@@ -1,7 +1,5 @@
require 'active_merchant/billing/gateways/migs/migs_codes'
-require 'digest/md5' # Used in add_secure_hash
-
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class MigsGateway < Gateway
@@ -121,6 +119,13 @@ def credit(money, authorization, options = {})
refund(money, authorization, options)
end
+ def verify(credit_card, options={})
+ MultiResponse.run do |r|
+ r.process { authorize(100, credit_card, options) }
+ r.process(:ignore_result) { void(r.authorization, options) }
+ end
+ end
+
# Checks the status of a previous transaction
# This can be useful when a response is not received due to network issues
#
@@ -187,7 +192,7 @@ def purchase_offsite_response(data)
response_hash = parse(data)
- expected_secure_hash = calculate_secure_hash(response_hash.reject{|k, v| k == :SecureHash}, @options[:secure_hash])
+ expected_secure_hash = calculate_secure_hash(response_hash, @options[:secure_hash])
unless response_hash[:SecureHash] == expected_secure_hash
raise SecurityError, "Secure Hash mismatch, response may be tampered with"
end
@@ -199,6 +204,18 @@ def test?
@options[:login].start_with?('TEST')
end
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r((&?CardNum=)\d*(&?)), '\1[FILTERED]\2').
+ gsub(%r((&?CardSecurityCode=)\d*(&?)), '\1[FILTERED]\2').
+ gsub(%r((&?AccessCode=)[^&]*(&?)), '\1[FILTERED]\2').
+ gsub(%r((&?Password=)[^&]*(&?)), '\1[FILTERED]\2')
+ end
+
private
def add_amount(post, money, options)
@@ -245,6 +262,7 @@ def parse(body)
end
def commit(post)
+ add_secure_hash(post) if @options[:secure_hash]
data = ssl_post self.merchant_hosted_url, post_data(post)
response_hash = parse(data)
response_object(response_hash)
@@ -290,12 +308,16 @@ def post_data(post)
def add_secure_hash(post)
post[:SecureHash] = calculate_secure_hash(post, @options[:secure_hash])
+ post[:SecureHashType] = 'SHA256'
end
def calculate_secure_hash(post, secure_hash)
- sorted_values = post.sort_by(&:to_s).map(&:last)
- input = secure_hash + sorted_values.join
- Digest::MD5.hexdigest(input).upcase
+ input = post
+ .reject { |k| %i[SecureHash SecureHashType].include?(k) }
+ .sort
+ .map { |(k, v)| "vpc_#{k}=#{v}" }
+ .join('&')
+ OpenSSL::HMAC.hexdigest('SHA256', [secure_hash].pack('H*'), input).upcase
end
end
end
diff --git a/lib/active_merchant/billing/gateways/monei.rb b/lib/active_merchant/billing/gateways/monei.rb
index 6892c116a8d..1b0cd6ba848 100755
--- a/lib/active_merchant/billing/gateways/monei.rb
+++ b/lib/active_merchant/billing/gateways/monei.rb
@@ -14,7 +14,7 @@ class MoneiGateway < Gateway
self.test_url = 'https://test.monei-api.net/payment/ctpe'
self.live_url = 'https://monei-api.net/payment/ctpe'
- self.supported_countries = ['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']
+ self.supported_countries = ['AD', 'AT', 'BE', 'BG', 'CA', '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', 'US', 'VA']
self.default_currency = 'EUR'
self.supported_cardtypes = [:visa, :master, :maestro, :jcb, :american_express]
diff --git a/lib/active_merchant/billing/gateways/moneris.rb b/lib/active_merchant/billing/gateways/moneris.rb
index 5449d553fb8..cb98b443d2a 100644
--- a/lib/active_merchant/billing/gateways/moneris.rb
+++ b/lib/active_merchant/billing/gateways/moneris.rb
@@ -181,21 +181,23 @@ def expdate(creditcard)
sprintf("%.4i", creditcard.year)[-2..-1] + sprintf("%.2i", creditcard.month)
end
- def add_payment_source(post, source, options)
- if source.is_a?(String)
- post[:data_key] = source
+ def add_payment_source(post, payment_method, options)
+ if payment_method.is_a?(String)
+ post[:data_key] = payment_method
post[:cust_id] = options[:customer]
else
- if source.respond_to?(:track_data) && source.track_data.present?
+ if payment_method.respond_to?(:track_data) && payment_method.track_data.present?
post[:pos_code] = '00'
- post[:track2] = source.track_data
+ post[:track2] = payment_method.track_data
else
- post[:pan] = source.number
- post[:expdate] = expdate(source)
- post[:cvd_value] = source.verification_value if source.verification_value?
- post[:cavv] = source.payment_cryptogram if source.is_a?(NetworkTokenizationCreditCard)
+ post[:pan] = payment_method.number
+ post[:expdate] = expdate(payment_method)
+ post[:cvd_value] = payment_method.verification_value if payment_method.verification_value?
+ post[:cavv] = payment_method.payment_cryptogram if payment_method.is_a?(NetworkTokenizationCreditCard)
+ post[:wallet_indicator] = wallet_indicator(payment_method.source.to_s) if payment_method.is_a?(NetworkTokenizationCreditCard)
+ post[:crypt_type] = (payment_method.eci || 7) if payment_method.is_a?(NetworkTokenizationCreditCard)
end
- post[:cust_id] = options[:customer] || source.name
+ post[:cust_id] = options[:customer] || payment_method.name
end
end
@@ -310,6 +312,12 @@ def cvd_element(cvd_value)
element
end
+ def wallet_indicator(token_source)
+ return 'APP' if token_source == 'apple_pay'
+ return 'ANP' if token_source == 'android_pay'
+ nil
+ end
+
def message_from(message)
return 'Unspecified error' if message.blank?
message.gsub(/[^\w]/, ' ').split.join(" ").capitalize
@@ -324,8 +332,8 @@ def actions
"indrefund" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type],
"completion" => [:order_id, :comp_amount, :txn_number, :crypt_type],
"purchasecorrection" => [:order_id, :txn_number, :crypt_type],
- "cavv_preauth" => [:order_id, :cust_id, :amount, :pan, :expdate, :cavv],
- "cavv_purchase" => [:order_id, :cust_id, :amount, :pan, :expdate, :cavv],
+ "cavv_preauth" => [:order_id, :cust_id, :amount, :pan, :expdate, :cavv, :crypt_type, :wallet_indicator],
+ "cavv_purchase" => [:order_id, :cust_id, :amount, :pan, :expdate, :cavv, :crypt_type, :wallet_indicator],
"transact" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type],
"Batchcloseall" => [],
"opentotals" => [:ecr_number],
diff --git a/lib/active_merchant/billing/gateways/moneris_us.rb b/lib/active_merchant/billing/gateways/moneris_us.rb
index 6f1ac826bc7..2986746cfe6 100644
--- a/lib/active_merchant/billing/gateways/moneris_us.rb
+++ b/lib/active_merchant/billing/gateways/moneris_us.rb
@@ -137,6 +137,17 @@ def update(data_key, credit_card, options = {})
commit('us_res_update_cc', post)
end
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r(()[^<]*())i, '\1[FILTERED]\2').
+ gsub(%r(()[^<]*())i, '\1[FILTERED]\2').
+ gsub(%r(()[^<]*())i, '\1[FILTERED]\2')
+ end
+
private # :nodoc: all
def expdate(creditcard)
diff --git a/lib/active_merchant/billing/gateways/mundipagg.rb b/lib/active_merchant/billing/gateways/mundipagg.rb
new file mode 100644
index 00000000000..1ec773acf8f
--- /dev/null
+++ b/lib/active_merchant/billing/gateways/mundipagg.rb
@@ -0,0 +1,289 @@
+module ActiveMerchant #:nodoc:
+ module Billing #:nodoc:
+ class MundipaggGateway < Gateway
+ self.live_url = 'https://api.mundipagg.com/core/v1'
+
+ self.supported_countries = ['US']
+ self.default_currency = 'USD'
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
+
+ self.homepage_url = 'https://www.mundipagg.com/'
+ self.display_name = 'Mundipagg'
+
+ STANDARD_ERROR_CODE_MAPPING = {
+ '400' => STANDARD_ERROR_CODE[:processing_error],
+ '401' => STANDARD_ERROR_CODE[:config_error],
+ '404' => STANDARD_ERROR_CODE[:processing_error],
+ '412' => STANDARD_ERROR_CODE[:processing_error],
+ '422' => STANDARD_ERROR_CODE[:processing_error],
+ '500' => STANDARD_ERROR_CODE[:processing_error]
+ }
+
+ STANDARD_ERROR_MESSAGE_MAPPING = {
+ '400' => 'Invalid request;',
+ '401' => 'Invalid API key;',
+ '404' => 'The requested resource does not exist;',
+ '412' => 'Valid parameters but request failed;',
+ '422' => 'Invalid parameters;',
+ '500' => 'An internal error occurred;'
+ }
+
+ def initialize(options={})
+ requires!(options, :api_key)
+ super
+ end
+
+ def purchase(money, payment, options={})
+ post = {}
+ add_invoice(post, money, options)
+ add_customer_data(post, options) unless payment.is_a?(String)
+ add_shipping_address(post, options)
+ add_payment(post, payment, options)
+
+ commit('sale', post)
+ end
+
+ def authorize(money, payment, options={})
+ post = {}
+ add_invoice(post, money, options)
+ add_customer_data(post, options) unless payment.is_a?(String)
+ add_shipping_address(post, options)
+ add_payment(post, payment, options)
+ add_capture_flag(post, payment)
+ commit('authonly', post)
+ end
+
+ def capture(money, authorization, options={})
+ post = {}
+ post[:code] = authorization
+ add_invoice(post, money, options)
+ commit('capture', post, authorization)
+ end
+
+ def refund(money, authorization, options={})
+ add_invoice(post={}, money, options)
+ commit('refund', post, authorization)
+ end
+
+ def void(authorization, options={})
+ commit('void', post=nil, authorization)
+ end
+
+ def store(payment, options={})
+ post = {}
+ options.update(name: payment.name)
+ options = add_customer(options) unless options[:customer_id]
+ add_payment(post, payment, options)
+ commit('store', post, options[:customer_id])
+ end
+
+ def verify(credit_card, options={})
+ MultiResponse.run(:use_first_response) do |r|
+ r.process { authorize(100, credit_card, options) }
+ r.process(:ignore_result) { void(r.authorization, options) }
+ end
+ end
+
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript
+ .gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]')
+ .gsub(%r(("cvv\\":\\")\d*), '\1[FILTERED]')
+ .gsub(%r((card\\":{\\"number\\":\\")\d*), '\1[FILTERED]')
+ end
+
+ private
+
+ def add_customer(options)
+ post = {}
+ post[:name] = options[:name]
+ customer = commit('customer', post)
+ options.update(customer_id: customer.authorization)
+ end
+
+ def add_customer_data(post, options)
+ post[:customer] = {}
+ post[:customer][:email] = options[:email]
+ end
+
+ def add_billing_address(options)
+ billing = {}
+ address = options[:billing_address] || options[:address]
+ billing[:street] = address[:address1].match(/\D+/)[0].strip if address[:address1]
+ billing[:number] = address[:address1].match(/\d+/)[0] if address[:address1]
+ billing[:compliment] = address[:address2] if address[:address2]
+ billing[:city] = address[:city] if address[:city]
+ billing[:state] = address[:state] if address[:state]
+ billing[:country] = address[:country] if address[:country]
+ billing[:zip_code] = address[:zip] if address[:zip]
+ billing[:neighborhood] = address[:neighborhood]
+ billing
+ end
+
+ def add_shipping_address(post, options)
+ if address = options[:shipping_address]
+ post[:address] = {}
+ post[:address][:street] = address[:address1].match(/\D+/)[0].strip if address[:address1]
+ post[:address][:number] = address[:address1].match(/\d+/)[0] if address[:address1]
+ post[:address][:compliment] = address[:address2] if address[:address2]
+ post[:address][:city] = address[:city] if address[:city]
+ post[:address][:state] = address[:state] if address[:state]
+ post[:address][:country] = address[:country] if address[:country]
+ post[:address][:zip_code] = address[:zip] if address[:zip]
+ end
+ end
+
+ def add_invoice(post, money, options)
+ post[:amount] = money
+ post[:currency] = (options[:currency] || currency(money))
+ end
+
+ def add_capture_flag(post, payment)
+ if voucher?(payment)
+ post[:payment][:voucher][:capture] = false
+ else
+ post[:payment][:credit_card][:capture] = false
+ end
+ end
+
+ def add_payment(post, payment, options)
+ post[:customer][:name] = payment.name if post[:customer]
+ post[:customer_id] = parse_auth(payment)[0] if payment.is_a?(String)
+ post[:payment] = {}
+ post[:payment][:gateway_affiliation_id] = @options[:gateway_id]
+ post[:payment][:metadata] = { mundipagg_payment_method_code: '1' } if test?
+ if voucher?(payment)
+ add_voucher(post, payment, options)
+ else
+ add_credit_card(post, payment, options)
+ end
+ end
+
+ def add_credit_card(post, payment, options)
+ post[:payment][:payment_method] = 'credit_card'
+ post[:payment][:credit_card] = {}
+ if payment.is_a?(String)
+ post[:payment][:credit_card][:card_id] = parse_auth(payment)[1]
+ else
+ post[:payment][:credit_card][:card] = {}
+ post[:payment][:credit_card][:card][:number] = payment.number
+ post[:payment][:credit_card][:card][:holder_name] = payment.name
+ post[:payment][:credit_card][:card][:exp_month] = payment.month
+ post[:payment][:credit_card][:card][:exp_year] = payment.year
+ post[:payment][:credit_card][:card][:cvv] = payment.verification_value
+ post[:payment][:credit_card][:card][:billing_address] = add_billing_address(options)
+ end
+ end
+
+ def add_voucher(post, payment, options)
+ post[:currency] = 'BRL'
+ post[:payment][:payment_method] = 'voucher'
+ post[:payment][:voucher] = {}
+ post[:payment][:voucher][:card] = {}
+ post[:payment][:voucher][:card][:number] = payment.number
+ post[:payment][:voucher][:card][:holder_name] = payment.name
+ post[:payment][:voucher][:card][:holder_document] = options[:holder_document]
+ post[:payment][:voucher][:card][:exp_month] = payment.month
+ post[:payment][:voucher][:card][:exp_year] = payment.year
+ post[:payment][:voucher][:card][:cvv] = payment.verification_value
+ post[:payment][:voucher][:card][:billing_address] = add_billing_address(options)
+ end
+
+ def voucher?(payment)
+ return false if payment.is_a?(String)
+ %w[sodexo vr].include? card_brand(payment)
+ end
+
+ def headers
+ {
+ 'Authorization' => 'Basic ' + Base64.strict_encode64("#{@options[:api_key]}:"),
+ 'Content-Type' => 'application/json',
+ 'Accept' => 'application/json'
+ }
+ end
+
+ def parse(body)
+ JSON.parse(body)
+ end
+
+ def url_for(action, auth = nil)
+ url = live_url
+ case action
+ when 'store'
+ "#{url}/customers/#{auth}/cards/"
+ when 'customer'
+ "#{url}/customers/"
+ when 'refund', 'void'
+ "#{url}/charges/#{auth}/"
+ when 'capture'
+ "#{url}/charges/#{auth}/capture/"
+ else
+ "#{url}/charges/"
+ end
+ end
+
+ def commit(action, parameters, auth = nil)
+ url = url_for(action, auth)
+ parameters.merge!(parameters[:payment][:credit_card].delete(:card)).delete(:payment) if action == 'store'
+ response = if %w[refund void].include? action
+ parse(ssl_request(:delete, url, post_data(parameters), headers))
+ else
+ parse(ssl_post(url, post_data(parameters), headers))
+ end
+
+ Response.new(
+ success_from(response),
+ message_from(response),
+ response,
+ authorization: authorization_from(response, action),
+ avs_result: AVSResult.new(code: response['some_avs_response_key']),
+ cvv_result: CVVResult.new(response['some_cvv_response_key']),
+ test: test?,
+ error_code: error_code_from(response)
+ )
+ rescue ResponseError => e
+ message = get_error_message(e)
+ return Response.new(
+ false,
+ "#{STANDARD_ERROR_MESSAGE_MAPPING[e.response.code]} #{message}",
+ parse(e.response.body),
+ test: test?,
+ error_code: STANDARD_ERROR_CODE_MAPPING[e.response.code],
+ )
+ end
+
+ def success_from(response)
+ %w[pending paid processing canceled active].include? response['status']
+ end
+
+ def get_error_message(error)
+ JSON.parse(error.response.body)['message']
+ end
+
+ def message_from(response)
+ return response['message'] if response['message']
+ return response['last_transaction']['acquirer_message'] if response['last_transaction']
+ end
+
+ def authorization_from(response, action)
+ return "#{response['customer']['id']}|#{response['id']}" if action == 'store'
+ response['id']
+ end
+
+ def parse_auth(auth)
+ auth.split('|')
+ end
+
+ def post_data(parameters = {})
+ parameters.to_json
+ end
+
+ def error_code_from(response)
+ STANDARD_ERROR_CODE[:processing_error] unless success_from(response)
+ end
+ end
+ end
+end
diff --git a/lib/active_merchant/billing/gateways/nab_transact.rb b/lib/active_merchant/billing/gateways/nab_transact.rb
index 40a33d32099..4c5f5102ca7 100644
--- a/lib/active_merchant/billing/gateways/nab_transact.rb
+++ b/lib/active_merchant/billing/gateways/nab_transact.rb
@@ -79,6 +79,18 @@ def unstore(identification, options = {})
commit_periodic(:deletecrn, build_unstore_request(identification, options))
end
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ return "" if transcript.blank?
+ transcript.
+ gsub(%r(()[^<]+(<))i, '\1[FILTERED]\2').
+ gsub(%r(()[^<]+(<))i, '\1[FILTERED]\2').
+ gsub(%r(()[^<]+(<))i, '\1[FILTERED]\2')
+ end
+
private
def add_metadata(xml, options)
diff --git a/lib/active_merchant/billing/gateways/netbanx.rb b/lib/active_merchant/billing/gateways/netbanx.rb
index 7c7ee9e2db1..1c1fdc227eb 100644
--- a/lib/active_merchant/billing/gateways/netbanx.rb
+++ b/lib/active_merchant/billing/gateways/netbanx.rb
@@ -5,16 +5,23 @@ class NetbanxGateway < Gateway
self.test_url = 'https://api.test.netbanx.com/'
self.live_url = 'https://api.netbanx.com/'
- self.supported_countries = ['CA', 'US', 'GB']
+ self.supported_countries = %w(AF AX AL DZ AS AD AO AI AQ AG AR AM AW AU AT AZ BS BH BD BB BY BE BZ BJ BM BT BO BQ BA BW BV BR IO BN BG BF BI KH CM CA CV KY CF TD CL CN CX CC CO KM CG CD CK CR CI HR CU CW CY CZ DK DJ DM DO EC EG SV GQ ER EE ET FK FO FJ FI FR GF PF TF GA GM GE DE GH GI GR GL GD GP GU GT GG GN GW GY HT HM HN HK HU IS IN ID IR IQ IE IM IL IT JM JP JE JO KZ KE KI KP KR KW KG LA LV LB LS LR LY LI LT LU MO MK MG MW MY MV ML MT MH MQ MR MU YT MX FM MD MC MN ME MS MA MZ MM NA NR NP NC NZ NI NE NG NU NF MP NO OM PK PW PS PA PG PY PE PH PN PL PT PR QA RE RO RU RW BL SH KN LC MF VC WS SM ST SA SN RS SC SL SG SX SK SI SB SO ZA GS SS ES LK PM SD SR SJ SZ SE CH SY TW TJ TZ TH NL TL TG TK TO TT TN TR TM TC TV UG UA AE GB US UM UY UZ VU VA VE VN VG VI WF EH YE ZM ZW)
self.default_currency = 'CAD'
- self.supported_cardtypes = [:visa, :master, :american_express, :discover]
+ self.supported_cardtypes = [
+ :american_express,
+ :diners_club,
+ :discover,
+ :jcb,
+ :master,
+ :maestro,
+ :visa
+ ]
+
self.money_format = :cents
self.homepage_url = 'https://processing.paysafe.com/'
self.display_name = 'Netbanx by PaySafe'
- STANDARD_ERROR_CODE_MAPPING = {}
-
def initialize(options={})
requires!(options, :account_number, :api_key)
super
@@ -24,7 +31,7 @@ def purchase(money, payment, options={})
post = {}
add_invoice(post, money, options)
add_settle_with_auth(post)
- add_payment(post, payment)
+ add_payment(post, payment, options)
commit(:post, 'auths', post)
end
@@ -32,7 +39,7 @@ def purchase(money, payment, options={})
def authorize(money, payment, options={})
post = {}
add_invoice(post, money, options)
- add_payment(post, payment)
+ add_payment(post, payment, options)
commit(:post, 'auths', post)
end
@@ -110,9 +117,6 @@ def add_settle_with_auth(post)
def add_customer_data(post, options)
post[:merchantCustomerId] = (options[:merchant_customer_id] || SecureRandom.uuid)
post[:locale] = options[:locale]
- # if options[:billing_address]
- # post[:address] = map_address(options[:billing_address])
- # end
end
def add_credit_card(post, credit_card, options = {})
@@ -128,13 +132,7 @@ def add_credit_card(post, credit_card, options = {})
def add_invoice(post, money, options)
post[:amount] = amount(money)
- post[:currencyCode] = options[:currency] if options[:currency]
add_order_id(post, options)
-
- if options[:billing_address]
- post[:billingDetails] = map_address(options[:billing_address])
- end
-
end
def add_payment(post, credit_card_or_reference, options = {})
@@ -146,6 +144,9 @@ def add_payment(post, credit_card_or_reference, options = {})
post[:card][:cvv] = credit_card_or_reference.verification_value
post[:card][:cardExpiry] = expdate(credit_card_or_reference)
end
+
+ post[:currencyCode] = options[:currency] if options[:currency]
+ post[:billingDetails] = map_address(options[:billing_address]) if options[:billing_address]
end
def expdate(credit_card)
@@ -192,6 +193,7 @@ def commit(method, uri, parameters)
message_from(success, response),
response,
:test => test?,
+ :error_code => error_code_from(response),
:authorization => authorization_from(success, get_url(uri), method, response)
)
end
@@ -236,10 +238,52 @@ def headers
{
'Accept' => 'application/json',
'Content-type' => 'application/json',
- 'Authorization' => "Basic #{Base64.strict_encode64(@options[:api_key].to_s).strip}",
+ 'Authorization' => "Basic #{Base64.strict_encode64(@options[:api_key].to_s)}",
'User-Agent' => "Netbanx-Paysafe v1.0/ActiveMerchant #{ActiveMerchant::VERSION}"
}
end
+
+ def error_code_from(response)
+ unless success_from(response)
+ case response['errorCode']
+ when '3002' then STANDARD_ERROR_CODE[:invalid_number] # You submitted an invalid card number or brand or combination of card number and brand with your request.
+ when '3004' then STANDARD_ERROR_CODE[:incorrect_zip] # The zip/postal code must be provided for an AVS check request.
+ when '3005' then STANDARD_ERROR_CODE[:incorrect_cvc] # You submitted an incorrect CVC value with your request.
+ when '3006' then STANDARD_ERROR_CODE[:expired_card] # You submitted an expired credit card number with your request.
+ when '3009' then STANDARD_ERROR_CODE[:card_declined] # Your request has been declined by the issuing bank.
+ when '3011' then STANDARD_ERROR_CODE[:card_declined] # Your request has been declined by the issuing bank because the card used is a restricted card. Contact the cardholder's credit card company for further investigation.
+ when '3012' then STANDARD_ERROR_CODE[:card_declined] # Your request has been declined by the issuing bank because the credit card expiry date submitted is invalid.
+ when '3013' then STANDARD_ERROR_CODE[:card_declined] # Your request has been declined by the issuing bank due to problems with the credit card account.
+ when '3014' then STANDARD_ERROR_CODE[:card_declined] # Your request has been declined - the issuing bank has returned an unknown response. Contact the card holder's credit card company for further investigation.
+ when '3015' then STANDARD_ERROR_CODE[:card_declined] # The bank has requested that you process the transaction manually by calling the cardholder's credit card company.
+ when '3016' then STANDARD_ERROR_CODE[:card_declined] # The bank has requested that you retrieve the card from the cardholder – it may be a lost or stolen card.
+ when '3017' then STANDARD_ERROR_CODE[:invalid_number] # You submitted an invalid credit card number with your request.
+ when '3022' then STANDARD_ERROR_CODE[:card_declined] # The card has been declined due to insufficient funds.
+ when '3023' then STANDARD_ERROR_CODE[:card_declined] # Your request has been declined by the issuing bank due to its proprietary card activity regulations.
+ when '3024' then STANDARD_ERROR_CODE[:card_declined] # Your request has been declined because the issuing bank does not permit the transaction for this card.
+ when '3032' then STANDARD_ERROR_CODE[:card_declined] # Your request has been declined by the issuing bank or external gateway because the card is probably in one of their negative databases.
+ when '3035' then STANDARD_ERROR_CODE[:card_declined] # Your request has been declined due to exceeded PIN attempts.
+ when '3036' then STANDARD_ERROR_CODE[:card_declined] # Your request has been declined due to an invalid issuer.
+ when '3037' then STANDARD_ERROR_CODE[:card_declined] # Your request has been declined because it is invalid.
+ when '3038' then STANDARD_ERROR_CODE[:card_declined] # Your request has been declined due to customer cancellation.
+ when '3039' then STANDARD_ERROR_CODE[:card_declined] # Your request has been declined due to an invalid authentication value.
+ when '3040' then STANDARD_ERROR_CODE[:card_declined] # Your request has been declined because the request type is not permitted on the card.
+ when '3041' then STANDARD_ERROR_CODE[:card_declined] # Your request has been declined due to a timeout.
+ when '3042' then STANDARD_ERROR_CODE[:card_declined] # Your request has been declined due to a cryptographic error.
+ when '3045' then STANDARD_ERROR_CODE[:invalid_expiry_date] # You submitted an invalid date format for this request.
+ when '3046' then STANDARD_ERROR_CODE[:card_declined] # The transaction was declined because the amount was set to zero.
+ when '3047' then STANDARD_ERROR_CODE[:card_declined] # The transaction was declined because the amount exceeds the floor limit.
+ when '3048' then STANDARD_ERROR_CODE[:card_declined] # The transaction was declined because the amount is less than the floor limit.
+ when '3049' then STANDARD_ERROR_CODE[:card_declined] # The bank has requested that you retrieve the card from the cardholder – the credit card has expired.
+ when '3050' then STANDARD_ERROR_CODE[:card_declined] # The bank has requested that you retrieve the card from the cardholder – fraudulent activity is suspected.
+ when '3051' then STANDARD_ERROR_CODE[:card_declined] # The bank has requested that you retrieve the card from the cardholder – contact the acquirer for more information.
+ when '3052' then STANDARD_ERROR_CODE[:card_declined] # The bank has requested that you retrieve the card from the cardholder – the credit card is restricted.
+ when '3053' then STANDARD_ERROR_CODE[:card_declined] # The bank has requested that you retrieve the card from the cardholder – please call the acquirer.
+ when '3054' then STANDARD_ERROR_CODE[:card_declined] # The transaction was declined due to suspected fraud.
+ else STANDARD_ERROR_CODE[:processing_error]
+ end
+ end
+ end
end
end
end
diff --git a/lib/active_merchant/billing/gateways/netbilling.rb b/lib/active_merchant/billing/gateways/netbilling.rb
index d0c1fee0167..32d48b71d6c 100644
--- a/lib/active_merchant/billing/gateways/netbilling.rb
+++ b/lib/active_merchant/billing/gateways/netbilling.rb
@@ -31,7 +31,6 @@ class NetbillingGateway < Gateway
self.display_name = 'NETbilling'
self.homepage_url = 'http://www.netbilling.com'
self.supported_countries = ['US']
- self.ssl_version = :TLSv1
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club]
def initialize(options = {})
diff --git a/lib/active_merchant/billing/gateways/nmi.rb b/lib/active_merchant/billing/gateways/nmi.rb
index 1384fc3ead8..3af76b0150f 100644
--- a/lib/active_merchant/billing/gateways/nmi.rb
+++ b/lib/active_merchant/billing/gateways/nmi.rb
@@ -101,6 +101,11 @@ def store(payment_method, options = {})
commit("add_customer", post)
end
+ def verify_credentials
+ response = void("0")
+ response.message != "Authentication Failed"
+ end
+
def supports_scrubbing?
true
end
@@ -111,7 +116,12 @@ def scrub(transcript)
gsub(%r((ccnumber=)\d+), '\1[FILTERED]').
gsub(%r((cvv=)\d+), '\1[FILTERED]').
gsub(%r((checkaba=)\d+), '\1[FILTERED]').
- gsub(%r((checkaccount=)\d+), '\1[FILTERED]')
+ gsub(%r((checkaccount=)\d+), '\1[FILTERED]').
+ gsub(%r((cryptogram=)[^&]+(&?)), '\1[FILTERED]\2')
+ end
+
+ def supports_network_tokenization?
+ true
end
private
@@ -130,8 +140,14 @@ def add_invoice(post, money, options)
def add_payment_method(post, payment_method, options)
if(payment_method.is_a?(String))
post[:customer_vault_id] = payment_method
+ elsif (payment_method.is_a?(NetworkTokenizationCreditCard))
+ post[:ccnumber] = payment_method.number
+ post[:ccexp] = exp_date(payment_method)
+ post[:token_cryptogram] = payment_method.payment_cryptogram
elsif(card_brand(payment_method) == 'check')
post[:payment] = 'check'
+ post[:firstname] = payment_method.first_name
+ post[:lastname] = payment_method.last_name
post[:checkname] = payment_method.name
post[:checkaba] = payment_method.routing_number
post[:checkaccount] = payment_method.account_number
diff --git a/lib/active_merchant/billing/gateways/ogone.rb b/lib/active_merchant/billing/gateways/ogone.rb
index 3700b334782..d0793a290fd 100644
--- a/lib/active_merchant/billing/gateways/ogone.rb
+++ b/lib/active_merchant/billing/gateways/ogone.rb
@@ -141,7 +141,6 @@ class OgoneGateway < Gateway
self.display_name = 'Ogone'
self.default_currency = 'EUR'
self.money_format = :cents
- self.ssl_version = :TLSv1
def initialize(options = {})
requires!(options, :login, :user, :password)
@@ -151,13 +150,12 @@ def initialize(options = {})
# Verify and reserve the specified amount on the account, without actually doing the transaction.
def authorize(money, payment_source, options = {})
post = {}
- action = (payment_source.brand == "mastercard") ? "PAU" : "RES"
add_invoice(post, options)
add_payment_source(post, payment_source, options)
add_address(post, payment_source, options)
add_customer_data(post, options)
add_money(post, money, options)
- commit(action, post)
+ commit('RES', post)
end
# Verify and transfer the specified amount.
@@ -334,6 +332,7 @@ def add_address(post, creditcard, options)
def add_invoice(post, options)
add_pair post, 'orderID', options[:order_id] || generate_unique_id[0...30]
add_pair post, 'COM', options[:description]
+ add_pair post, 'ORIG', options[:origin] if options[:origin]
end
def add_creditcard(post, creditcard)
diff --git a/lib/active_merchant/billing/gateways/omise.rb b/lib/active_merchant/billing/gateways/omise.rb
index 4af89999fab..a3c21bf2c10 100644
--- a/lib/active_merchant/billing/gateways/omise.rb
+++ b/lib/active_merchant/billing/gateways/omise.rb
@@ -14,18 +14,20 @@ class OmiseGateway < Gateway
self.live_url = self.test_url = API_URL
# Currency supported by Omise
- # * Thai Baht with Satang, i.e. 9000 => 90 THB
+ # * Thai Baht with Satang, 50000 (THB500.00)
+ # * Japanese Yen, 500 (JPY500)
self.default_currency = 'THB'
self.money_format = :cents
#Country supported by Omise
# * Thailand
- self.supported_countries = %w( TH )
+ self.supported_countries = %w( TH JP )
# Credit cards supported by Omise
# * VISA
# * MasterCard
- self.supported_cardtypes = [:visa, :master]
+ # * JCB
+ self.supported_cardtypes = [:visa, :master, :jcb]
# Omise main page
self.homepage_url = 'https://www.omise.co/'
@@ -39,8 +41,10 @@ class OmiseGateway < Gateway
#
# ==== Options
#
- # * :public_key -- Omise's public key (REQUIRED).
- # * :secret_key -- Omise's secret key (REQUIRED).
+ # * :public_key -- Omise's public key (REQUIRED).
+ # * :secret_key -- Omise's secret key (REQUIRED).
+ # * :api_version -- Omise's API Version (OPTIONAL), default version is '2014-07-27'
+ # See version at page https://dashboard.omise.co/api-version/edit
def initialize(options={})
requires!(options, :public_key, :secret_key)
diff --git a/lib/active_merchant/billing/gateways/openpay.rb b/lib/active_merchant/billing/gateways/openpay.rb
index aea2c4ff98f..811656cb075 100644
--- a/lib/active_merchant/billing/gateways/openpay.rb
+++ b/lib/active_merchant/billing/gateways/openpay.rb
@@ -113,6 +113,7 @@ def create_post_for_auth_or_purchase(money, creditcard, options)
post[:order_id] = options[:order_id]
post[:device_session_id] = options[:device_session_id]
post[:currency] = (options[:currency] || currency(money)).upcase
+ post[:use_card_points] = options[:use_card_points] if options[:use_card_points]
add_creditcard(post, creditcard, options)
post
end
@@ -129,10 +130,22 @@ def add_creditcard(post, creditcard, options)
holder_name: creditcard.name
}
add_address(card, options)
+ add_customer_data(post, creditcard, options)
post[:card] = card
end
end
+ def add_customer_data(post, creditcard, options)
+ if options[:email]
+ customer = {
+ name: creditcard.name || options[:name],
+ email: options[:email]
+ }
+ post[:customer] = customer
+ end
+ post
+ end
+
def add_address(card, options)
return unless card.kind_of?(Hash)
if address = (options[:billing_address] || options[:address])
diff --git a/lib/active_merchant/billing/gateways/opp.rb b/lib/active_merchant/billing/gateways/opp.rb
index 6c36a8963b2..2640ad7f925 100644
--- a/lib/active_merchant/billing/gateways/opp.rb
+++ b/lib/active_merchant/billing/gateways/opp.rb
@@ -1,26 +1,26 @@
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class OppGateway < Gateway
-# = Open Payment Platform
+ # = Open Payment Platform
#
- # The Open Payment Platform includes a powerful omni-channel transaction processing API,
- # enabling you to quickly and flexibly build new applications and services on the platform.
- #
- # This plugin enables connectivity to the Open Payment Platform for activemerchant.
+ # The Open Payment Platform includes a powerful omni-channel transaction processing API,
+ # enabling you to quickly and flexibly build new applications and services on the platform.
+ #
+ # This plugin enables connectivity to the Open Payment Platform for activemerchant.
#
# For any questions or comments please contact support@payon.com
#
# == Usage
#
# gateway = ActiveMerchant::Billing::OppGateway.new(
- # user_id: 'merchant user id',
+ # user_id: 'merchant user id',
# password: 'password',
- # entity_id: 'entity id',
+ # entity_id: 'entity id',
# )
#
# # set up credit card object as in main ActiveMerchant example
# creditcard = ActiveMerchant::Billing::CreditCard.new(
- # :type => 'visa',
+ # :type => 'visa',
# :number => '4242424242424242',
# :month => 8,
# :year => 2009,
@@ -30,7 +30,7 @@ class OppGateway < Gateway
#
# # Request: complete example, including address, billing address, shipping address
# complete_request_options = {
- # order_id: "your merchant/shop order id", # alternative is to set merchantInvoiceId
+ # order_id: "your merchant/shop order id", # alternative is to set merchantInvoiceId
# merchant_transaction_id: "your merchant/shop transaction id",
# address: address,
# description: 'Store Purchase - Books',
@@ -67,34 +67,34 @@ class OppGateway < Gateway
# ip: 101.102.103.104,
# },
# }
- #
+ #
# # Request: minimal example
# minimal_request_options = {
- # order_id: "your merchant/shop order id", # alternative is to set merchantInvoiceId
+ # order_id: "your merchant/shop order id", # alternative is to set merchantInvoiceId
# description: 'Store Purchase - Books',
# }
#
- # options =
+ # options =
# # run request
# response = gateway.purchase(754, creditcard, options) # charge 7,54 EUR
#
# response.success? # Check whether the transaction was successful
- # response.error_code # Retrieve the error message - it's mapped to Gateway::STANDARD_ERROR_CODE
+ # response.error_code # Retrieve the error message - it's mapped to Gateway::STANDARD_ERROR_CODE
# response.message # Retrieve the message returned by opp
# response.authorization # Retrieve the unique transaction ID returned by opp
# response.params['result']['code'] # Retrieve original return code returned by opp server
#
# == Errors
- # If transaction is not successful, response.error_code contains mapped to Gateway::STANDARD_ERROR_CODE error message.
- # Complete list of opp error codes can be viewed on https://docs.oppwa.com/
- # Because this list is much bigger than Gateway::STANDARD_ERROR_CODE, only fraction is mapped to Gateway::STANDARD_ERROR_CODE.
- # All other codes are mapped as Gateway::STANDARD_ERROR_CODE[:processing_error], so if this is the case,
+ # If transaction is not successful, response.error_code contains mapped to Gateway::STANDARD_ERROR_CODE error message.
+ # Complete list of opp error codes can be viewed on https://docs.oppwa.com/
+ # Because this list is much bigger than Gateway::STANDARD_ERROR_CODE, only fraction is mapped to Gateway::STANDARD_ERROR_CODE.
+ # All other codes are mapped as Gateway::STANDARD_ERROR_CODE[:processing_error], so if this is the case,
# you may check the original result code from OPP that can be found in response.params['result']['code']
- #
+ #
# == Special features
- # For purchase method risk check can be forced when options[:risk_workflow] = true
- # This will split (on OPP server side) the transaction into two separate transactions: authorize and capture,
- # but capture will be executed only if risk checks are successful.
+ # For purchase method risk check can be forced when options[:risk_workflow] = true
+ # This will split (on OPP server side) the transaction into two separate transactions: authorize and capture,
+ # but capture will be executed only if risk checks are successful.
#
# For testing you may use the test account details listed fixtures.yml under opp. It is important to note that there are two test modes available:
# options[:test_mode]='EXTERNAL' causes test transactions to be forwarded to the processor's test system for 'end-to-end' testing
@@ -102,10 +102,10 @@ class OppGateway < Gateway
# If no test_mode parameter is sent, test_mode=INTERNAL is the default behaviour.
#
# Billing Address, Shipping Address, Custom Parameters are supported as described under https://docs.oppwa.com/parameters
- # See complete example above for details.
+ # See complete example above for details.
#
# == Tokenization
- # When create_registration is set to true, the payment details will be stored and a token will be returned in registrationId response field,
+ # When create_registration is set to true, the payment details will be stored and a token will be returned in registrationId response field,
# which can subsequently be used to reference the stored payment.
self.test_url = 'https://test.oppwa.com/v1/payments'
@@ -113,7 +113,7 @@ class OppGateway < Gateway
self.supported_countries = %w(AD AI AG AR AU AT BS BB BE BZ BM BR BN BG CA HR CY CZ DK DM EE FI FR DE GR GD GY HK HU IS IN IL IT JP LV LI LT LU MY MT MX MC MS NL PA PL PT KN LC MF VC SM SG SK SI ZA ES SR SE CH TR GB US UY)
self.default_currency = 'EUR'
- self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :discover, :jcb, :maestro, :dankort]
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :discover, :jcb, :maestro, :dankort]
self.homepage_url = 'https://docs.oppwa.com'
self.display_name = 'Open Payment Platform'
@@ -125,7 +125,7 @@ def initialize(options={})
def purchase(money, payment, options={})
# debit
- execute_dbpa(options[:risk_workflow] ? 'PA.CP': 'DB',
+ execute_dbpa(options[:risk_workflow] ? 'PA.CP': 'DB',
money, payment, options)
end
@@ -133,7 +133,7 @@ def authorize(money, payment, options={})
# preauthorization PA
execute_dbpa('PA', money, payment, options)
end
-
+
def capture(money, authorization, options={})
# capture CP
execute_referencing('CP', money, authorization, options)
@@ -163,8 +163,6 @@ def supports_scrubbing?
def scrub(transcript)
transcript.
gsub(%r((authentication\.password=)\w+), '\1[FILTERED]').
- gsub(%r((authentication\.userId=)\w+), '\1[FILTERED]').
- gsub(%r((authentication\.entityId=)\w+), '\1[FILTERED]').
gsub(%r((card\.number=)\d+), '\1[FILTERED]').
gsub(%r((card\.cvv=)\d+), '\1[FILTERED]')
end
@@ -173,12 +171,13 @@ def scrub(transcript)
def execute_dbpa(txtype, money, payment, options)
post = {}
- post[:paymentType] = txtype
+ post[:paymentType] = txtype
add_invoice(post, money, options)
add_payment_method(post, payment, options)
add_address(post, options)
- add_customer_data(post, options)
+ add_customer_data(post, payment, options)
add_options(post, options)
+ add_3d_secure(post, options)
commit(post, nil, options)
end
@@ -189,76 +188,86 @@ def execute_referencing(txtype, money, authorization, options)
commit(post, authorization, options)
end
- def add_authentication(post)
- post[:authentication] = { entityId: @options[:entity_id], password: @options[:password], userId: @options[:user_id]}
+ def add_authentication(post)
+ post[:authentication] = { entityId: @options[:entity_id], password: @options[:password], userId: @options[:user_id]}
end
- def add_customer_data(post, options)
+ def add_customer_data(post, payment, options)
if options[:customer]
post[:customer] = {
merchantCustomerId: options[:customer][:merchant_customer_id],
- givenName: options[:customer][:givenname],
- surname: options[:customer][:surname],
+ givenName: options[:customer][:givenname] || payment.first_name,
+ surname: options[:customer][:surname] || payment.last_name,
birthDate: options[:customer][:birth_date],
phone: options[:customer][:phone],
mobile: options[:customer][:mobile],
- email: options[:customer][:email],
+ email: options[:customer][:email] || options[:email],
companyName: options[:customer][:company_name],
identificationDocType: options[:customer][:identification_doctype],
identificationDocId: options[:customer][:identification_docid],
- ip: options[:customer][:ip],
+ ip: options[:customer][:ip] || options[:ip]
}
end
end
def add_address(post, options)
- if billing_address = options[:billing_address]
+ if billing_address = options[:billing_address] || options[:address]
address(post, billing_address, 'billing')
end
if shipping_address = options[:shipping_address]
- address(post, billing_address, 'shipping')
+ address(post, shipping_address, 'shipping')
if shipping_address[:name]
- firstname, lastname = shipping_address[:name].split(' ')
- post[:shipping] = { givenName: firstname, surname: lastname }
- end
+ firstname, lastname = shipping_address[:name].split(' ')
+ post[:shipping] = { givenName: firstname, surname: lastname }
+ end
end
end
def address(post, address, prefix)
- post[prefix] = {
- street1: address[:address1],
- street2: address[:address2],
- city: address[:city],
- state: address[:state],
- postcode: address[:zip],
- country: address[:country],
- }
+ post[prefix] = {
+ street1: address[:address1],
+ street2: address[:address2],
+ city: address[:city],
+ state: address[:state],
+ postcode: address[:zip],
+ country: address[:country],
+ }
end
def add_invoice(post, money, options)
- post[:amount] = amount(money)
- post[:currency] = (currency(money) || @options[:currency]) if 'RV'!=(post[:paymentType])
- post[:descriptor] = options[:description] || options[:descriptor]
- post[:merchantInvoiceId] = options[:merchantInvoiceId] || options[:order_id]
- post[:merchantTransactionId] = options[:merchant_transaction_id]
+ post[:amount] = amount(money)
+ post[:currency] = options[:currency] || currency(money) unless post[:paymentType] == 'RV'
+ post[:descriptor] = options[:description] || options[:descriptor]
+ post[:merchantInvoiceId] = options[:merchantInvoiceId] || options[:order_id]
+ post[:merchantTransactionId] = options[:merchant_transaction_id] || generate_unique_id
end
def add_payment_method(post, payment, options)
- if options[:registrationId]
- #post[:recurringType] = 'REPEATED'
- post[:card] = {
- cvv: payment.verification_value,
- }
- else
- post[:paymentBrand] = payment.brand.upcase
- post[:card] = {
- holder: payment.name,
- number: payment.number,
- expiryMonth: "%02d" % payment.month,
- expiryYear: payment.year,
- cvv: payment.verification_value,
- }
- end
+ if options[:registrationId]
+ #post[:recurringType] = 'REPEATED'
+ post[:card] = {
+ cvv: payment.verification_value,
+ }
+ else
+ post[:paymentBrand] = payment.brand.upcase
+ post[:card] = {
+ holder: payment.name,
+ number: payment.number,
+ expiryMonth: "%02d" % payment.month,
+ expiryYear: payment.year,
+ cvv: payment.verification_value,
+ }
+ end
+ end
+
+ def add_3d_secure(post, options)
+ return unless options[:eci] && options[:cavv] && options[:xid]
+
+ post[:threeDSecure] = {
+ eci: options[:eci],
+ verificationId: options[:cavv],
+ xid: options[:xid]
+ }
end
def add_options(post, options)
@@ -266,36 +275,38 @@ def add_options(post, options)
post[:testMode] = options[:test_mode] if test? && options[:test_mode]
options.each {|key, value| post[key] = value if key.to_s.match('customParameters\[[a-zA-Z0-9\._]{3,64}\]') }
post['customParameters[SHOPPER_pluginId]'] = 'activemerchant'
+ post['customParameters[custom_disable3DSecure]'] = options[:disable_3d_secure] if options[:disable_3d_secure]
end
def build_url(url, authorization, options)
if options[:registrationId]
"#{url.gsub(/payments/, 'registrations')}/#{options[:registrationId]}/payments"
- elsif authorization
+ elsif authorization
"#{url}/#{authorization}"
else
url
end
end
-
+
def commit(post, authorization, options)
- url = (test? ? test_url : live_url)
+ url = build_url(test? ? test_url : live_url, authorization, options)
add_authentication(post)
post = flatten_hash(post)
- url = build_url(url, authorization, options)
- raw_response = raw_ssl_request(:post, url,
- post.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&"),
- "Content-Type" => "application/x-www-form-urlencoded;charset=UTF-8")
-
- success = success_from(raw_response)
- response = raw_response.body
- begin
- response = JSON.parse(response)
- rescue JSON::ParserError
- response = json_error(response)
+ response = begin
+ parse(
+ ssl_post(
+ url,
+ post.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&"),
+ "Content-Type" => "application/x-www-form-urlencoded;charset=UTF-8"
+ )
+ )
+ rescue ResponseError => e
+ parse(e.response.body)
end
+ success = success_from(response)
+
Response.new(
success,
message_from(response),
@@ -306,11 +317,34 @@ def commit(post, authorization, options)
)
end
- def success_from(raw_response)
- raw_response.code.to_i.between?(200,299)
+ def parse(body)
+ begin
+ JSON.parse(body)
+ rescue JSON::ParserError
+ json_error(body)
+ end
+ end
+
+ def json_error(body)
+ message = "Invalid response received #{body.inspect}"
+ { 'result' => {'description' => message, 'code' => 'unknown' } }
+ end
+
+ def success_from(response)
+ return false unless response['result']
+
+ success_regex = /^(000\.000\.|000\.100\.1|000\.[36])/
+
+ if success_regex =~ response['result']['code']
+ true
+ else
+ false
+ end
end
def message_from(response)
+ return 'Failed' unless response['result']
+
response['result']['description']
end
@@ -319,44 +353,20 @@ def authorization_from(response)
end
def error_code_from(response)
- case response['result']['code']
- when '100.100.101'
- Gateway::STANDARD_ERROR_CODE[:incorrect_number]
- when '100.400.317'
- Gateway::STANDARD_ERROR_CODE[:invalid_number]
- when '100.100.600', '100.100.601', '800.100.153', '800.100.192'
- Gateway::STANDARD_ERROR_CODE[:invalid_cvc]
- when '100.100.303'
- Gateway::STANDARD_ERROR_CODE[:expired_card]
- when '100.800.200', '100.800.201', '100.800.202', '800.800.202'
- Gateway::STANDARD_ERROR_CODE[:incorrect_zip]
- when '100.400.000', '100.400.086', '100.400.305', '800.400.150'
- Gateway::STANDARD_ERROR_CODE[:incorrect_address]
- when '800.100.159'
- Gateway::STANDARD_ERROR_CODE[:pickup_card]
- when '800.100.151', '800.100.158', '800.100.160'
- Gateway::STANDARD_ERROR_CODE[:card_declined]
- else
- Gateway::STANDARD_ERROR_CODE[:processing_error]
- end
+ response['result']['code']
end
-
- def json_error(raw_response)
- message = "Invalid response received #{raw_response.inspect}"
- { 'result' => {'description' => message, 'code' => 'unknown' } }
- end
-
+
def flatten_hash(hash)
hash.each_with_object({}) do |(k, v), h|
if v.is_a? Hash
flatten_hash(v).map do |h_k, h_v|
h["#{k}.#{h_k}".to_sym] = h_v
end
- else
+ else
h[k] = v
end
end
- end
+ end
end
end
end
diff --git a/lib/active_merchant/billing/gateways/optimal_payment.rb b/lib/active_merchant/billing/gateways/optimal_payment.rb
index 9604335381d..10c809fdc04 100644
--- a/lib/active_merchant/billing/gateways/optimal_payment.rb
+++ b/lib/active_merchant/billing/gateways/optimal_payment.rb
@@ -59,6 +59,17 @@ def capture(money, authorization, options = {})
commit('ccSettlement', money, options)
end
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r((%3CstorePwd%3E).*(%3C(%2F|/)storePwd%3E))i, '\1[FILTERED]\2').
+ gsub(%r((%3CcardNum%3E)\d*(%3C(%2F|/)cardNum%3E))i, '\1[FILTERED]\2').
+ gsub(%r((%3Ccvd%3E)\d*(%3C(%2F|/)cvd%3E))i, '\1[FILTERED]\2')
+ end
+
private
def parse_card_or_auth(card_or_auth, options)
@@ -259,9 +270,11 @@ def build_card(xml, opts)
if brand = card_type(@credit_card.brand)
xml.tag! 'cardType' , brand
end
- if @credit_card.verification_value
+ if @credit_card.verification_value?
xml.tag! 'cvdIndicator' , '1' # Value Provided
xml.tag! 'cvd' , @credit_card.verification_value
+ else
+ xml.tag! 'cvdIndicator' , '0'
end
end
end
diff --git a/lib/active_merchant/billing/gateways/orbital.rb b/lib/active_merchant/billing/gateways/orbital.rb
index ca1c98092d2..94143ac7eef 100644
--- a/lib/active_merchant/billing/gateways/orbital.rb
+++ b/lib/active_merchant/billing/gateways/orbital.rb
@@ -30,11 +30,11 @@ module Billing #:nodoc:
class OrbitalGateway < Gateway
include Empty
- API_VERSION = "5.6"
+ API_VERSION = "7.1"
POST_HEADERS = {
"MIME-Version" => "1.1",
- "Content-Type" => "application/PTI56",
+ "Content-Type" => "application/PTI#{API_VERSION.gsub(/\./, '')}",
"Content-transfer-encoding" => "text",
"Request-number" => '1',
"Document-type" => "Request",
@@ -65,11 +65,11 @@ class OrbitalGateway < Gateway
class_attribute :secondary_test_url, :secondary_live_url
- self.test_url = "https://orbitalvar1.paymentech.net/authorize"
- self.secondary_test_url = "https://orbitalvar2.paymentech.net/authorize"
+ self.test_url = "https://orbitalvar1.chasepaymentech.com/authorize"
+ self.secondary_test_url = "https://orbitalvar2.chasepaymentech.com/authorize"
- self.live_url = "https://orbital1.paymentech.net/authorize"
- self.secondary_live_url = "https://orbital2.paymentech.net/authorize"
+ self.live_url = "https://orbital1.chasepaymentech.com/authorize"
+ self.secondary_live_url = "https://orbital2.chasepaymentech.com/authorize"
self.supported_countries = ["US", "CA"]
self.default_currency = "CAD"
@@ -86,6 +86,7 @@ class OrbitalGateway < Gateway
"AUD" => '036',
"BRL" => '986',
"CAD" => '124',
+ "CLP" => '152',
"CZK" => '203',
"DKK" => '208',
"HKD" => '344',
@@ -106,6 +107,7 @@ class OrbitalGateway < Gateway
"AUD" => '2',
"BRL" => '2',
"CAD" => '2',
+ "CLP" => '2',
"CZK" => '2',
"DKK" => '2',
"HKD" => '2',
@@ -189,7 +191,7 @@ def initialize(options = {})
# A – Authorization request
def authorize(money, creditcard, options = {})
- order = build_new_order_xml(AUTH_ONLY, money, options) do |xml|
+ order = build_new_order_xml(AUTH_ONLY, money, creditcard, options) do |xml|
add_creditcard(xml, creditcard, options[:currency])
add_address(xml, creditcard, options)
if @options[:customer_profiles]
@@ -209,7 +211,7 @@ def verify(creditcard, options = {})
# AC – Authorization and Capture
def purchase(money, creditcard, options = {})
- order = build_new_order_xml(AUTH_AND_CAPTURE, money, options) do |xml|
+ order = build_new_order_xml(AUTH_AND_CAPTURE, money, creditcard, options) do |xml|
add_creditcard(xml, creditcard, options[:currency])
add_address(xml, creditcard, options)
if @options[:customer_profiles]
@@ -227,7 +229,7 @@ def capture(money, authorization, options = {})
# R – Refund request
def refund(money, authorization, options = {})
- order = build_new_order_xml(REFUND, money, options.merge(:authorization => authorization)) do |xml|
+ order = build_new_order_xml(REFUND, money, nil, options.merge(:authorization => authorization)) do |xml|
add_refund(xml, options[:currency])
xml.tag! :CustomerRefNum, options[:customer_ref_num] if @options[:customer_profiles] && options[:profile_txn]
end
@@ -304,8 +306,10 @@ def scrub(transcript)
gsub(%r(().+()), '\1[FILTERED]\2').
gsub(%r(().+()), '\1[FILTERED]\2').
gsub(%r(().+()), '\1[FILTERED]\2').
+ gsub(%r(().+()), '\1[FILTERED]\2').
gsub(%r(().+()), '\1[FILTERED]\2').
- gsub(%r(().+()), '\1[FILTERED]\2')
+ gsub(%r(().+()), '\1[FILTERED]\2').
+ gsub(%r(().+()), '\1[FILTERED]\2')
end
private
@@ -343,6 +347,43 @@ def add_soft_descriptors(xml, soft_desc)
xml.tag! :SDMerchantEmail, soft_desc.merchant_email if soft_desc.merchant_email
end
+ def add_soft_descriptors_from_hash(xml, soft_desc)
+ xml.tag! :SDMerchantName, soft_desc[:merchant_name] || nil
+ xml.tag! :SDProductDescription, soft_desc[:product_description] || nil
+ xml.tag! :SDMerchantCity, soft_desc[:merchant_city] || nil
+ xml.tag! :SDMerchantPhone, soft_desc[:merchant_phone] || nil
+ xml.tag! :SDMerchantURL, soft_desc[:merchant_url] || nil
+ xml.tag! :SDMerchantEmail, soft_desc[:merchant_email] || nil
+ end
+
+ def add_level_2_tax(xml, options={})
+ if (level_2 = options[:level_2_data])
+ xml.tag! :TaxInd, level_2[:tax_indicator] if [TAX_NOT_PROVIDED, TAX_INCLUDED, NON_TAXABLE_TRANSACTION].include?(level_2[:tax_indicator].to_i)
+ xml.tag! :Tax, level_2[:tax].to_i if level_2[:tax]
+ end
+ end
+
+ def add_level_2_advice_addendum(xml, options={})
+ if (level_2 = options[:level_2_data])
+ xml.tag! :AMEXTranAdvAddn1, byte_limit(level_2[:advice_addendum_1], 40) if level_2[:advice_addendum_1]
+ xml.tag! :AMEXTranAdvAddn2, byte_limit(level_2[:advice_addendum_2], 40) if level_2[:advice_addendum_2]
+ xml.tag! :AMEXTranAdvAddn3, byte_limit(level_2[:advice_addendum_3], 40) if level_2[:advice_addendum_3]
+ xml.tag! :AMEXTranAdvAddn4, byte_limit(level_2[:advice_addendum_4], 40) if level_2[:advice_addendum_4]
+ end
+ end
+
+ def add_level_2_purchase(xml, options={})
+ if (level_2 = options[:level_2_data])
+ xml.tag! :PCOrderNum, byte_limit(level_2[:purchase_order], 17) if level_2[:purchase_order]
+ xml.tag! :PCDestZip, byte_limit(format_address_field(level_2[:zip]), 10) if level_2[:zip]
+ xml.tag! :PCDestName, byte_limit(format_address_field(level_2[:name]), 30) if level_2[:name]
+ xml.tag! :PCDestAddress1, byte_limit(format_address_field(level_2[:address1]), 30) if level_2[:address1]
+ xml.tag! :PCDestAddress2, byte_limit(format_address_field(level_2[:address2]), 30) if level_2[:address2]
+ xml.tag! :PCDestCity, byte_limit(format_address_field(level_2[:city]), 20) if level_2[:city]
+ xml.tag! :PCDestState, byte_limit(format_address_field(level_2[:state]), 2) if level_2[:state]
+ end
+ end
+
def add_address(xml, creditcard, options)
if(address = (options[:billing_address] || options[:address]))
avs_supported = AVS_SUPPORTED_COUNTRIES.include?(address[:country].to_s) || empty?(address[:country])
@@ -415,13 +456,25 @@ def add_creditcard(xml, creditcard, currency=nil)
# Do not submit the attribute at all.
# - http://download.chasepaymentech.com/docs/orbital/orbital_gateway_xml_specification.pdf
unless creditcard.nil?
- if %w( visa discover ).include?(creditcard.brand)
- xml.tag! :CardSecValInd, (creditcard.verification_value? ? '1' : '9')
+ if creditcard.verification_value?
+ if %w( visa discover ).include?(creditcard.brand)
+ xml.tag! :CardSecValInd, '1'
+ end
+ xml.tag! :CardSecVal, creditcard.verification_value
end
- xml.tag! :CardSecVal, creditcard.verification_value if creditcard.verification_value?
end
end
+ def add_cdpt_eci_and_xid(xml, creditcard)
+ xml.tag! :AuthenticationECIInd, creditcard.eci
+ xml.tag! :XID, creditcard.transaction_id if creditcard.transaction_id
+ end
+
+ def add_cdpt_payment_cryptogram(xml, creditcard)
+ xml.tag! :DPANInd, 'Y'
+ xml.tag! :DigitalTokenCryptogram, creditcard.payment_cryptogram
+ end
+
def add_refund(xml, currency=nil)
xml.tag! :AccountNum, nil
@@ -525,7 +578,7 @@ def ip_authentication?
@options[:ip_authentication] == true
end
- def build_new_order_xml(action, money, parameters = {})
+ def build_new_order_xml(action, money, creditcard, parameters = {})
requires!(parameters, :order_id)
xml = xml_envelope
xml.tag! :Request do
@@ -548,14 +601,27 @@ def build_new_order_xml(action, money, parameters = {})
yield xml if block_given?
+ if creditcard.is_a?(NetworkTokenizationCreditCard)
+ add_cdpt_eci_and_xid(xml, creditcard)
+ end
+
xml.tag! :OrderID, format_order_id(parameters[:order_id])
xml.tag! :Amount, amount(money)
xml.tag! :Comments, parameters[:comments] if parameters[:comments]
+ add_level_2_tax(xml, parameters)
+ add_level_2_advice_addendum(xml, parameters)
+
# CustomerAni, AVSPhoneType and AVSDestPhoneType could be added here.
+ if creditcard.is_a?(NetworkTokenizationCreditCard)
+ add_cdpt_payment_cryptogram(xml, creditcard)
+ end
+
if parameters[:soft_descriptors].is_a?(OrbitalSoftDescriptors)
add_soft_descriptors(xml, parameters[:soft_descriptors])
+ elsif parameters[:soft_descriptors].is_a?(Hash)
+ add_soft_descriptors_from_hash(xml, parameters[:soft_descriptors])
end
set_recurring_ind(xml, parameters)
@@ -565,6 +631,8 @@ def build_new_order_xml(action, money, parameters = {})
tx_ref_num, _ = split_authorization(parameters[:authorization])
xml.tag! :TxRefNum, tx_ref_num
end
+
+ add_level_2_purchase(xml, parameters)
end
end
xml.target!
@@ -588,8 +656,11 @@ def build_mark_for_capture_xml(money, authorization, parameters = {})
add_xml_credentials(xml)
xml.tag! :OrderID, format_order_id(order_id)
xml.tag! :Amount, amount(money)
+ add_level_2_tax(xml, parameters)
add_bin_merchant_and_terminal(xml, parameters)
xml.tag! :TxRefNum, tx_ref_num
+ add_level_2_purchase(xml, parameters)
+ add_level_2_advice_addendum(xml, parameters)
end
end
xml.target!
diff --git a/lib/active_merchant/billing/gateways/pay_hub.rb b/lib/active_merchant/billing/gateways/pay_hub.rb
index fede5ca5c92..afd6be918c2 100644
--- a/lib/active_merchant/billing/gateways/pay_hub.rb
+++ b/lib/active_merchant/billing/gateways/pay_hub.rb
@@ -200,8 +200,8 @@ def response_error(raw_response)
def json_error(raw_response)
{
- error_message: 'Invalid response received from the Payhub API. Please contact wecare@payhub.com if you continue to receive this message.' +
- ' (The raw response returned by the API was #{raw_response.inspect})'
+ error_message: "Invalid response received from the Payhub API. Please contact wecare@payhub.com if you continue to receive this message." +
+ " (The raw response returned by the API was #{raw_response.inspect})"
}
end
diff --git a/lib/active_merchant/billing/gateways/paybox_direct.rb b/lib/active_merchant/billing/gateways/paybox_direct.rb
index b4fd36a97c4..c984cf11a4c 100644
--- a/lib/active_merchant/billing/gateways/paybox_direct.rb
+++ b/lib/active_merchant/billing/gateways/paybox_direct.rb
@@ -2,13 +2,14 @@ module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class PayboxDirectGateway < Gateway
class_attribute :live_url_backup
+ class_attribute :api_version
self.test_url = 'https://preprod-ppps.paybox.com/PPPS.php'
self.live_url = 'https://ppps.paybox.com/PPPS.php'
self.live_url_backup = 'https://ppps1.paybox.com/PPPS.php'
# Payment API Version
- API_VERSION = '00103'
+ self.api_version = '00103'
# Transactions hash
TRANSACTIONS = {
@@ -176,7 +177,7 @@ def message_from(response)
def post_data(action, parameters = {})
parameters.update(
- :version => API_VERSION,
+ :version => api_version,
:type => TRANSACTIONS[action.to_sym],
:dateq => Time.now.strftime('%d%m%Y%H%M%S'),
:numquestion => unique_id(parameters[:order_id]),
diff --git a/lib/active_merchant/billing/gateways/paybox_direct_plus.rb b/lib/active_merchant/billing/gateways/paybox_direct_plus.rb
new file mode 100644
index 00000000000..2b58cc869c6
--- /dev/null
+++ b/lib/active_merchant/billing/gateways/paybox_direct_plus.rb
@@ -0,0 +1,11 @@
+module ActiveMerchant #:nodoc:
+ module Billing #:nodoc:
+ class PayboxDirectPlusGateway < PayboxDirectGateway
+ # Payment API Version
+ self.api_version = '00104'
+
+ # The name of the gateway
+ self.display_name = 'Paybox Direct Plus'
+ end
+ end
+end
diff --git a/lib/active_merchant/billing/gateways/payeezy.rb b/lib/active_merchant/billing/gateways/payeezy.rb
index 1d6f72191ec..b26f74a710a 100644
--- a/lib/active_merchant/billing/gateways/payeezy.rb
+++ b/lib/active_merchant/billing/gateways/payeezy.rb
@@ -3,9 +3,9 @@ module Billing
class PayeezyGateway < Gateway
class_attribute :integration_url
- self.test_url = 'https://api-cert.payeezy.com/v1/transactions'
- self.integration_url = 'https://api-cat.payeezy.com/v1/transactions'
- self.live_url = 'https://api.payeezy.com/v1/transactions'
+ self.test_url = 'https://api-cert.payeezy.com/v1'
+ self.integration_url = 'https://api-cat.payeezy.com/v1'
+ self.live_url = 'https://api.payeezy.com/v1'
self.default_currency = 'USD'
self.money_format = :cents
@@ -31,12 +31,13 @@ def initialize(options = {})
end
def purchase(amount, payment_method, options = {})
- params = {transaction_type: 'purchase'}
+ params = payment_method.is_a?(String) ? { transaction_type: 'recurring' } : { transaction_type: 'purchase' }
add_invoice(params, options)
- add_payment_method(params, payment_method)
+ add_payment_method(params, payment_method, options)
add_address(params, options)
add_amount(params, amount, options)
+ add_soft_descriptors(params, options)
commit(params, options)
end
@@ -45,9 +46,10 @@ def authorize(amount, payment_method, options = {})
params = {transaction_type: 'authorize'}
add_invoice(params, options)
- add_payment_method(params, payment_method)
+ add_payment_method(params, payment_method, options)
add_address(params, options)
add_amount(params, amount, options)
+ add_soft_descriptors(params, options)
commit(params, options)
end
@@ -57,6 +59,7 @@ def capture(amount, authorization, options = {})
add_authorization_info(params, authorization)
add_amount(params, amount, options)
+ add_soft_descriptors(params, options)
commit(params, options)
end
@@ -70,6 +73,14 @@ def refund(amount, authorization, options = {})
commit(params, options)
end
+ def store(payment_method, options = {})
+ params = {transaction_type: 'store'}
+
+ add_creditcard_for_tokenization(params, payment_method, options)
+
+ commit(params, options)
+ end
+
def void(authorization, options = {})
params = {transaction_type: 'void'}
@@ -93,10 +104,17 @@ def supports_scrubbing?
def scrub(transcript)
transcript.
gsub(%r((Token: )(\w|-)+), '\1[FILTERED]').
+ gsub(%r((Apikey: )(\w|-)+), '\1[FILTERED]').
gsub(%r((\\?"card_number\\?":\\?")\d+), '\1[FILTERED]').
gsub(%r((\\?"cvv\\?":\\?")\d+), '\1[FILTERED]').
gsub(%r((\\?"account_number\\?":\\?")\d+), '\1[FILTERED]').
- gsub(%r((\\?"routing_number\\?":\\?")\d+), '\1[FILTERED]')
+ gsub(%r((\\?"routing_number\\?":\\?")\d+), '\1[FILTERED]').
+ gsub(%r((\\?card_number=)\d+(&?)), '\1[FILTERED]').
+ gsub(%r((\\?cvv=)\d+(&?)), '\1[FILTERED]').
+ gsub(%r((\\?apikey=)\w+(&?)), '\1[FILTERED]').
+ gsub(%r{(\\?"credit_card\.card_number\\?":)(\\?"[^"]+\\?")}, '\1[FILTERED]').
+ gsub(%r{(\\?"credit_card\.cvv\\?":)(\\?"[^"]+\\?")}, '\1[FILTERED]').
+ gsub(%r{(\\?"apikey\\?":)(\\?"[^"]+\\?")}, '\1[FILTERED]')
end
private
@@ -113,43 +131,85 @@ def add_authorization_info(params, authorization)
transaction_id, transaction_tag, method, _ = authorization.split('|')
params[:transaction_id] = transaction_id
params[:transaction_tag] = transaction_tag
- params[:method] = method
+ params[:method] = (method == 'token') ? 'credit_card' : method
+ end
+
+ def add_creditcard_for_tokenization(params, payment_method, options)
+ params[:apikey] = @options[:apikey]
+ params[:ta_token] = options[:ta_token]
+ params[:type] = 'FDToken'
+ params[:credit_card] = add_card_data(payment_method)
+ params[:auth] = 'false'
+ end
+
+ def is_store_action?(params)
+ params[:transaction_type] == 'store'
end
- def add_payment_method(params, payment_method)
+ def add_payment_method(params, payment_method, options)
if payment_method.is_a? Check
- add_echeck(params, payment_method)
+ add_echeck(params, payment_method, options)
+ elsif payment_method.is_a? String
+ add_token(params, payment_method, options)
else
add_creditcard(params, payment_method)
end
end
- def add_creditcard(params, creditcard)
- credit_card = {}
-
- credit_card[:type] = CREDIT_CARD_BRAND[creditcard.brand]
- credit_card[:cardholder_name] = creditcard.name
- credit_card[:card_number] = creditcard.number
- credit_card[:exp_date] = "#{format(creditcard.month, :two_digits)}#{format(creditcard.year, :two_digits)}"
- credit_card[:cvv] = creditcard.verification_value if creditcard.verification_value?
-
- params[:method] = 'credit_card'
- params[:credit_card] = credit_card
- end
-
- def add_echeck(params, echeck)
+ def add_echeck(params, echeck, options)
tele_check = {}
- tele_check[:check_number] = echeck.number
+ tele_check[:check_number] = echeck.number || "001"
tele_check[:check_type] = "P"
tele_check[:routing_number] = echeck.routing_number
tele_check[:account_number] = echeck.account_number
tele_check[:accountholder_name] = "#{echeck.first_name} #{echeck.last_name}"
+ tele_check[:customer_id_type] = options[:customer_id_type] if options[:customer_id_type]
+ tele_check[:customer_id_number] = options[:customer_id_number] if options[:customer_id_number]
+ tele_check[:client_email] = options[:client_email] if options[:client_email]
params[:method] = 'tele_check'
params[:tele_check] = tele_check
end
+ def add_token(params, payment_method, options)
+ token = {}
+ token[:token_type] = 'FDToken'
+
+ type, cardholder_name, exp_date, card_number = payment_method.split('|')
+
+ token[:token_data] = {}
+ token[:token_data][:type] = type
+ token[:token_data][:cardholder_name] = cardholder_name
+ token[:token_data][:value] = card_number
+ token[:token_data][:exp_date] = exp_date
+ token[:token_data][:cvv] = options[:cvv] if options[:cvv]
+
+ params[:method] = 'token'
+ params[:token] = token
+ end
+
+ def add_creditcard(params, creditcard)
+ credit_card = add_card_data(creditcard)
+
+ params[:method] = 'credit_card'
+ params[:credit_card] = credit_card
+ end
+
+ def add_card_data(payment_method)
+ card = {}
+ card[:type] = CREDIT_CARD_BRAND[payment_method.brand]
+ card[:cardholder_name] = payment_method.name
+ card[:card_number] = payment_method.number
+ card[:exp_date] = format_exp_date(payment_method.month, payment_method.year)
+ card[:cvv] = payment_method.verification_value if payment_method.verification_value?
+ card
+ end
+
+ def format_exp_date(month, year)
+ "#{format(month, :two_digits)}#{format(year, :two_digits)}"
+ end
+
def add_address(params, options)
address = options[:billing_address]
return unless address
@@ -169,26 +229,23 @@ def add_amount(params, money, options)
params[:amount] = amount(money)
end
+ def add_soft_descriptors(params, options)
+ params[:soft_descriptors] = options[:soft_descriptors] if options[:soft_descriptors]
+ end
+
def commit(params, options)
- url = if options[:integration]
- integration_url
- elsif test?
- test_url
- else
- live_url
- end
+ url = base_url(options) + endpoint(params)
if transaction_id = params.delete(:transaction_id)
url = "#{url}/#{transaction_id}"
end
begin
- body = params.to_json
- response = parse(ssl_post(url, body, headers(body)))
+ response = api_request(url, params)
rescue ResponseError => e
response = response_error(e.response.body)
rescue JSON::ParserError
- response = json_error(raw_response)
+ response = json_error(e.response.body)
end
Response.new(
@@ -203,17 +260,28 @@ def commit(params, options)
)
end
- def success_from(response)
- response['transaction_status'] == 'approved'
+ def base_url(options)
+ if options[:integration]
+ integration_url
+ elsif test?
+ test_url
+ else
+ live_url
+ end
end
- def authorization_from(params, response)
- [
- response['transaction_id'],
- response['transaction_tag'],
- params[:method],
- (response['amount'] && response['amount'].to_i)
- ].join('|')
+ def endpoint(params)
+ is_store_action?(params) ? '/transactions/tokens' : '/transactions'
+ end
+
+ def api_request(url, params)
+ body = params.to_json
+ parse(ssl_post(url, body, headers(body)))
+ end
+
+ def post_data(params)
+ return nil unless params
+ params.reject { |k, v| v.blank? }.collect { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join("&")
end
def generate_hmac(nonce, current_timestamp, payload)
@@ -246,19 +314,57 @@ def error_code(response, success)
response['Error'].to_h['messages'].to_a.map { |e| e['code'] }.join(', ')
end
+ def success_from(response)
+ if response['transaction_status']
+ response['transaction_status'] == 'approved'
+ elsif response['results']
+ response['results']['status'] == 'success'
+ elsif response['status']
+ response['status'] == 'success'
+ else
+ false
+ end
+ end
+
def handle_message(response, success)
- if success
+ if success && response['status'].present?
+ 'Token successfully created.'
+ elsif success
"#{response['gateway_message']} - #{response['bank_message']}"
elsif %w(401 403).include?(response['code'])
response['message']
elsif response.key?('Error')
response['Error']['messages'].first['description']
+ elsif response.key?('results')
+ response['results']['Error']['messages'].first['description']
elsif response.key?('error')
response['error']
elsif response.key?('fault')
response['fault'].to_h['faultstring']
else
- response['bank_message']
+ response['bank_message'] || response['gateway_message'] || 'Failure to successfully create token.'
+ end
+ end
+
+ def authorization_from(params, response)
+ if is_store_action?(params)
+ if success_from(response)
+ [
+ response['token']['type'],
+ response['token']['cardholder_name'],
+ response['token']['exp_date'],
+ response['token']['value']
+ ].join('|')
+ else
+ nil
+ end
+ else
+ [
+ response['transaction_id'],
+ response['transaction_tag'],
+ params[:method],
+ (response['amount'] && response['amount'].to_i)
+ ].join('|')
end
end
diff --git a/lib/active_merchant/billing/gateways/payflow.rb b/lib/active_merchant/billing/gateways/payflow.rb
index 817ff39712d..523d3c746c5 100644
--- a/lib/active_merchant/billing/gateways/payflow.rb
+++ b/lib/active_merchant/billing/gateways/payflow.rb
@@ -45,7 +45,14 @@ def refund(money, reference, options = {})
end
def verify(payment, options={})
- authorize(0, payment, options)
+ if credit_card_type(payment) == 'Amex'
+ MultiResponse.run(:use_first_response) do |r|
+ r.process { authorize(100, payment, options) }
+ r.process(:ignore_result) { void(r.authorization, options) }
+ end
+ else
+ authorize(0, payment, options)
+ end
end
def verify_credentials
@@ -93,8 +100,20 @@ def express
@express ||= PayflowExpressGateway.new(@options)
end
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r(()[^<]*()), '\1[FILTERED]\2').
+ gsub(%r(()[^<]*()), '\1[FILTERED]\2').
+ gsub(%r(()[^<]*()), '\1[FILTERED]\2').
+ gsub(%r(()[^<]*()), '\1[FILTERED]\2')
+ end
private
+
def build_sale_or_authorization_request(action, money, funding_source, options)
if funding_source.is_a?(String)
build_reference_sale_or_authorization_request(action, money, funding_source, options)
@@ -114,6 +133,7 @@ def build_reference_sale_or_authorization_request(action, money, reference, opti
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! 'OrderDesc', options[:order_desc] unless options[:order_desc].blank?
xml.tag! 'Comment', options[:comment] unless options[:comment].blank?
xml.tag!('ExtData', 'Name'=> 'COMMENT2', 'Value'=> options[:comment2]) unless options[:comment2].blank?
xml.tag! 'TaxAmt', options[:taxamt] unless options[:taxamt].blank?
@@ -145,6 +165,7 @@ def build_credit_card_request(action, money, credit_card, options)
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! 'OrderDesc', options[:order_desc] unless options[:order_desc].blank?
# Comment and Comment2 will show up in manager.paypal.com as Comment1 and Comment2
xml.tag! 'Comment', options[:comment] unless options[:comment].blank?
xml.tag!('ExtData', 'Name'=> 'COMMENT2', 'Value'=> options[:comment2]) unless options[:comment2].blank?
@@ -152,6 +173,7 @@ def build_credit_card_request(action, money, credit_card, options)
xml.tag! 'FreightAmt', options[:freightamt] unless options[:freightamt].blank?
xml.tag! 'DutyAmt', options[:dutyamt] unless options[:dutyamt].blank?
xml.tag! 'DiscountAmt', options[:discountamt] unless options[:discountamt].blank?
+ xml.tag! 'EMail', options[:email] unless options[:email].nil?
billing_address = options[:billing_address] || options[:address]
add_address(xml, 'BillTo', billing_address, options) if billing_address
@@ -176,6 +198,7 @@ def build_check_request(action, money, check, options)
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! 'OrderDesc', options[:order_desc] unless options[:order_desc].blank?
xml.tag! 'BillTo' do
xml.tag! 'Name', check.name
end
@@ -324,4 +347,3 @@ def build_response(success, message, response, options = {})
end
end
end
-
diff --git a/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb b/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb
index 195c6fb383f..225f746eaea 100644
--- a/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb
+++ b/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb
@@ -10,7 +10,7 @@ def self.included(base)
# Set the default partner to PayPal
base.partner = 'PayPal'
- base.supported_countries = ['US', 'CA', 'SG', 'AU']
+ base.supported_countries = ['US', 'CA', 'NZ', 'AU']
base.class_attribute :timeout
base.timeout = 60
@@ -25,6 +25,13 @@ def self.included(base)
# subsequent Responses will have a :duplicate parameter set in the params
# hash.
base.retry_safe = true
+
+ # Send Payflow requests to PayPal directly by activating the NVP protocol.
+ # Valid XMLPay documents may have issues being parsed correctly by
+ # Payflow but will be accepted by PayPal if a PAYPAL-NVP request header
+ # is declared.
+ base.class_attribute :use_paypal_nvp
+ base.use_paypal_nvp = false
end
XMLNS = 'http://www.paypal.com/XMLPay'
@@ -181,7 +188,7 @@ def parse_element(response, node)
end
def build_headers(content_length)
- {
+ headers = {
"Content-Type" => "text/xml",
"Content-Length" => content_length.to_s,
"X-VPS-Client-Timeout" => timeout.to_s,
@@ -189,9 +196,12 @@ def build_headers(content_length)
"X-VPS-VIT-Runtime-Version" => RUBY_VERSION,
"X-VPS-Request-ID" => SecureRandom.hex(16)
}
+
+ headers.merge!("PAYPAL-NVP" => "Y") if self.use_paypal_nvp
+ headers
end
- def commit(request_body, options = {})
+ def commit(request_body, options = {})
request = build_request(request_body, options)
headers = build_headers(request.size)
diff --git a/lib/active_merchant/billing/gateways/payment_express.rb b/lib/active_merchant/billing/gateways/payment_express.rb
index a5a95b7874a..f8a1cc41df2 100644
--- a/lib/active_merchant/billing/gateways/payment_express.rb
+++ b/lib/active_merchant/billing/gateways/payment_express.rb
@@ -16,7 +16,7 @@ class PaymentExpressGateway < Gateway
# However, regular accounts with DPS only support VISA and Mastercard
self.supported_cardtypes = [ :visa, :master, :american_express, :diners_club, :jcb ]
- self.supported_countries = %w[ AU CA DE ES FR GB HK IE MY NL NZ SG US ZA ]
+ self.supported_countries = %w[ AU FJ GB HK IE MY NZ PG SG US ]
self.homepage_url = 'http://www.paymentexpress.com/'
self.display_name = 'PaymentExpress'
@@ -128,7 +128,8 @@ def supports_scrubbing
def scrub(transcript)
transcript.
- gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
+ gsub(%r(().+()), '\1[FILTERED]\2').
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]\2').
gsub(%r(()\d+()), '\1[FILTERED]\2').
gsub(%r(()\d+()), '\1[FILTERED]\2')
end
diff --git a/lib/active_merchant/billing/gateways/paymentez.rb b/lib/active_merchant/billing/gateways/paymentez.rb
new file mode 100644
index 00000000000..5b8983d8d2f
--- /dev/null
+++ b/lib/active_merchant/billing/gateways/paymentez.rb
@@ -0,0 +1,276 @@
+require 'base64'
+require 'digest'
+
+module ActiveMerchant #:nodoc:
+ module Billing #:nodoc:
+ class PaymentezGateway < Gateway #:nodoc:
+ self.test_url = 'https://ccapi-stg.paymentez.com/v2/'
+ self.live_url = 'https://ccapi.paymentez.com/v2/'
+
+ self.supported_countries = %w[MX EC VE CO BR CL]
+ self.default_currency = 'USD'
+ self.supported_cardtypes = %i[visa master american_express diners_club]
+
+ self.homepage_url = 'https://secure.paymentez.com/'
+ self.display_name = 'Paymentez'
+
+ STANDARD_ERROR_CODE_MAPPING = {
+ 1 => :processing_error,
+ 6 => :card_declined,
+ 9 => :card_declined,
+ 10 => :processing_error,
+ 11 => :card_declined,
+ 12 => :config_error,
+ 13 => :config_error,
+ 19 => :invalid_cvc,
+ 20 => :config_error,
+ 21 => :card_declined,
+ 22 => :card_declined,
+ 23 => :card_declined,
+ 24 => :card_declined,
+ 25 => :card_declined,
+ 26 => :card_declined,
+ 27 => :card_declined,
+ 28 => :card_declined
+ }.freeze
+
+ CARD_MAPPING = {
+ 'visa' => 'vi',
+ 'master' => 'mc',
+ 'american_express' => 'ax',
+ 'diners_club' => 'di'
+ }.freeze
+
+ def initialize(options = {})
+ requires!(options, :application_code, :app_key)
+ super
+ end
+
+ def purchase(money, payment, options = {})
+ post = {}
+
+ add_invoice(post, money, options)
+ add_payment(post, payment)
+ add_customer_data(post, options)
+ action = payment.is_a?(String) ? 'debit' : 'debit_cc'
+
+ commit_transaction(action, post)
+ end
+
+ def authorize(money, payment, options = {})
+ post = {}
+
+ add_invoice(post, money, options)
+ add_payment(post, payment)
+ add_customer_data(post, options)
+
+ commit_transaction('authorize', post)
+ end
+
+ def capture(_money, authorization, _options = {})
+ post = { transaction: { id: authorization } }
+
+ commit_transaction('capture', post)
+ end
+
+ def refund(_money, authorization, options = {})
+ void(authorization, options)
+ end
+
+ def void(authorization, _options = {})
+ post = { transaction: { id: authorization } }
+ commit_transaction('refund', post)
+ end
+
+ def verify(credit_card, options = {})
+ MultiResponse.run do |r|
+ r.process { authorize(100, credit_card, options) }
+ r.process { void(r.authorization, options) }
+ end
+ end
+
+ def store(credit_card, options = {})
+ post = {}
+
+ add_customer_data(post, options)
+ add_payment(post, credit_card)
+
+ response = commit_card('add', post)
+ if !response.success? && !(token = extract_previous_card_token(response)).nil?
+ unstore(token, options)
+ response = commit_card('add', post)
+ end
+ response
+ end
+
+ def unstore(identification, options = {})
+ post = { card: { token: identification }, user: { id: options[:user_id] }}
+ commit_card('delete', post)
+ end
+
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript
+ .gsub(%r{(\\?"number\\?":)(\\?"[^"]+\\?")}, '\1[FILTERED]')
+ .gsub(%r{(\\?"cvc\\?":)(\\?"[^"]+\\?")}, '\1[FILTERED]')
+ .gsub(%r{(Auth-Token: )([A-Za-z0-9=]+)}, '\1[FILTERED]')
+ end
+
+ private
+
+ def add_customer_data(post, options)
+ requires!(options, :user_id, :email)
+ post[:user] ||= {}
+ post[:user][:id] = options[:user_id]
+ post[:user][:email] = options[:email]
+ post[:user][:ip_address] = options[:ip] if options[:ip]
+ post[:user][:fiscal_number] = options[:fiscal_number] if options[:fiscal_number]
+ end
+
+ def add_invoice(post, money, options)
+ post[:session_id] = options[:session_id] if options[:session_id]
+
+ post[:order] ||= {}
+ post[:order][:amount] = amount(money).to_f
+ post[:order][:vat] = options[:vat] if options[:vat]
+ post[:order][:dev_reference] = options[:dev_reference] if options[:dev_reference]
+ post[:order][:description] = options[:description] if options[:description]
+ post[:order][:discount] = options[:discount] if options[:discount]
+ post[:order][:installments] = options[:installments] if options[:installments]
+ post[:order][:installments_type] = options[:installments_type] if options[:installments_type]
+ post[:order][:taxable_amount] = options[:taxable_amount] if options[:taxable_amount]
+ post[:order][:tax_percentage] = options[:tax_percentage] if options[:tax_percentage]
+ end
+
+ def add_payment(post, payment)
+ post[:card] ||= {}
+ if payment.is_a?(String)
+ post[:card][:token] = payment
+ else
+ post[:card][:number] = payment.number
+ post[:card][:holder_name] = payment.name
+ post[:card][:expiry_month] = payment.month
+ post[:card][:expiry_year] = payment.year
+ post[:card][:cvc] = payment.verification_value
+ post[:card][:type] = CARD_MAPPING[payment.brand]
+ end
+ end
+
+ def parse(body)
+ JSON.parse(body)
+ end
+
+ def commit_raw(object, action, parameters)
+ url = "#{(test? ? test_url : live_url)}#{object}/#{action}"
+
+ begin
+ raw_response = ssl_post(url, post_data(parameters), headers)
+ rescue ResponseError => e
+ raw_response = e.response.body
+ end
+ parse(raw_response)
+ end
+
+ def commit_transaction(action, parameters)
+ response = commit_raw('transaction', action, parameters)
+ Response.new(
+ success_from(response),
+ message_from(response),
+ response,
+ authorization: authorization_from(response),
+ test: test?,
+ error_code: error_code_from(response)
+ )
+ end
+
+ def commit_card(action, parameters)
+ response = commit_raw('card', action, parameters)
+ Response.new(
+ card_success_from(response),
+ card_message_from(response),
+ response,
+ authorization: card_authorization_from(response),
+ test: test?,
+ error_code: card_error_code_from(response)
+ )
+ end
+
+ def headers
+ {
+ 'Auth-Token' => authentication_code,
+ 'Content-Type' => 'application/json'
+ }
+ end
+
+ def success_from(response)
+ !response.include?('error') && (response['status'] || response['transaction']['status']) == 'success'
+ end
+
+ def card_success_from(response)
+ return false if response.include?('error')
+ return true if response['message'] == 'card deleted'
+ response['card']['status'] == 'valid'
+ end
+
+ def message_from(response)
+ if success_from(response)
+ response['transaction'] && response['transaction']['message']
+ else
+ response['error'] && response['error']['type']
+ end
+ end
+
+ def card_message_from(response)
+ if !response.include?('error')
+ response['message'] || response['card']['message']
+ else
+ response['error']['type']
+ end
+ end
+
+ def authorization_from(response)
+ response['transaction'] && response['transaction']['id']
+ end
+
+ def card_authorization_from(response)
+ response['card'] && response['card']['token']
+ end
+
+ def extract_previous_card_token(response)
+ match = /Card already added: (\d+)/.match(response.message)
+ match && match[1]
+ end
+
+ def post_data(parameters = {})
+ JSON.dump(parameters)
+ end
+
+ def error_code_from(response)
+ return if success_from(response)
+ if response['transaction']
+ detail = response['transaction']['status_detail']
+ if STANDARD_ERROR_CODE_MAPPING.include?(detail)
+ return STANDARD_ERROR_CODE[STANDARD_ERROR_CODE_MAPPING[detail]]
+ end
+ elsif response['error']
+ return STANDARD_ERROR_CODE[:config_error]
+ end
+ STANDARD_ERROR_CODE[:processing_error]
+ end
+
+ def card_error_code_from(response)
+ STANDARD_ERROR_CODE[:processing_error] unless card_success_from(response)
+ end
+
+ def authentication_code
+ timestamp = Time.new.to_i
+ unique_token = Digest::SHA256.hexdigest("#{@options[:app_key]}#{timestamp}")
+ authentication_string = "#{@options[:application_code]};#{timestamp};#{unique_token}"
+ Base64.encode64(authentication_string).delete("\n")
+ end
+ end
+ end
+end
diff --git a/lib/active_merchant/billing/gateways/paymill.rb b/lib/active_merchant/billing/gateways/paymill.rb
index 6e0dbdb7f0c..63bb466bb71 100644
--- a/lib/active_merchant/billing/gateways/paymill.rb
+++ b/lib/active_merchant/billing/gateways/paymill.rb
@@ -17,7 +17,7 @@ def initialize(options = {})
super
end
- def purchase(money, payment_method, options = {})
+ def purchase(money, payment_method, options={})
action_with_token(:purchase, money, payment_method, options)
end
@@ -48,7 +48,7 @@ def void(authorization, options={})
end
def store(credit_card, options={})
- save_card(credit_card)
+ save_card(credit_card, options)
end
def supports_scrubbing
@@ -74,11 +74,15 @@ def verify_credentials
private
- def add_credit_card(post, credit_card)
+ def add_credit_card(post, credit_card, options)
+ post['account.holder'] = (credit_card.try(:name) || "")
post['account.number'] = credit_card.number
post['account.expiry.month'] = sprintf("%.2i", credit_card.month)
post['account.expiry.year'] = sprintf("%.4i", credit_card.year)
post['account.verification'] = credit_card.verification_value
+ post['account.email'] = (options[:email] || nil)
+ post['presentation.amount3D'] = (options[:money] || nil)
+ post['presentation.currency3D'] = (options[:currency] || currency( options[:money]))
end
def headers
@@ -122,12 +126,13 @@ def authorization_from(parsed_response)
end
def action_with_token(action, money, payment_method, options)
+ options[:money] = money
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) }
+ when String
+ self.send("#{action}_with_token", money, payment_method, options)
+ else
+ MultiResponse.run do |r|
+ r.process { save_card(payment_method, options) }
r.process { self.send("#{action}_with_token", money, r.authorization, options) }
end
end
@@ -153,10 +158,10 @@ def authorize_with_token(money, card_token, options)
commit(:post, 'preauthorizations', post)
end
- def save_card(credit_card)
+ def save_card(credit_card, options)
post = {}
- add_credit_card(post, credit_card)
+ add_credit_card(post, credit_card, options)
post['channel.id'] = @options[:public_key]
post['jsonPFunction'] = 'jsonPFunction'
post['transaction.mode'] = (test? ? 'CONNECTOR_TEST' : 'LIVE')
@@ -219,6 +224,7 @@ def transaction_id(authorization)
20203 => "Reversed due to complaint by buyer",
20204 => "Payment has been refunded",
20300 => "Reversal has been canceled",
+ 22000 => "Initiation of transaction successful",
30000 => "Transaction still in progress",
30100 => "Transaction has been accepted",
@@ -258,6 +264,8 @@ def transaction_id(authorization)
40420 => "Problem with address data",
40500 => "Permission error with acquirer API",
40510 => "Rate limit reached for acquirer API",
+ 42000 => "Initiation of transaction failed",
+ 42410 => "Initiation of transaction expired",
50000 => "Problem with back end",
50001 => "Country blacklisted",
diff --git a/lib/active_merchant/billing/gateways/paystation.rb b/lib/active_merchant/billing/gateways/paystation.rb
index 332899934da..781952b0ac3 100644
--- a/lib/active_merchant/billing/gateways/paystation.rb
+++ b/lib/active_merchant/billing/gateways/paystation.rb
@@ -75,7 +75,6 @@ def store(credit_card, options = {})
commit(post)
end
-
def refund(money, authorization, options={})
post = new_request
add_amount(post, money, options)
@@ -85,6 +84,10 @@ def refund(money, authorization, options={})
commit(post)
end
+ def verify(credit_card, options={})
+ authorize(0, credit_card, options)
+ end
+
private
def new_request
diff --git a/lib/active_merchant/billing/gateways/payu_latam.rb b/lib/active_merchant/billing/gateways/payu_latam.rb
index 23b1b135d7d..3d5904348d0 100644
--- a/lib/active_merchant/billing/gateways/payu_latam.rb
+++ b/lib/active_merchant/billing/gateways/payu_latam.rb
@@ -9,7 +9,7 @@ class PayuLatamGateway < Gateway
self.test_url = "https://sandbox.api.payulatam.com/payments-api/4.0/service.cgi"
self.live_url = "https://api.payulatam.com/payments-api/4.0/service.cgi"
- self.supported_countries = ["AR", "BR", "CO", "MX", "PA", "PE"]
+ self.supported_countries = ["AR", "BR", "CL", "CO", "MX", "PA", "PE"]
self.default_currency = "USD"
self.money_format = :dollars
self.supported_cardtypes = [:visa, :master, :american_express, :diners_club]
@@ -29,7 +29,7 @@ class PayuLatamGateway < Gateway
}
def initialize(options={})
- requires!(options, :merchant_id, :account_id, :api_login, :api_key)
+ requires!(options, :merchant_id, :account_id, :api_login, :api_key, :payment_country)
super
end
@@ -45,11 +45,11 @@ def authorize(amount, payment_method, options={})
commit('auth', post)
end
- def capture(authorization, options={})
+ def capture(amount, authorization, options={})
post = {}
- add_credentials(post, 'SUBMIT_TRANSACTION')
- add_transaction_type(post, 'CAPTURE')
+ add_credentials(post, 'SUBMIT_TRANSACTION', options)
+ add_transaction_elements(post, 'CAPTURE', options)
add_reference(post, authorization)
commit('capture', post)
@@ -58,18 +58,18 @@ def capture(authorization, options={})
def void(authorization, options={})
post = {}
- add_credentials(post, 'SUBMIT_TRANSACTION')
- add_transaction_type(post, 'VOID')
+ add_credentials(post, 'SUBMIT_TRANSACTION', options)
+ add_transaction_elements(post, 'VOID', options)
add_reference(post, authorization)
commit('void', post)
end
- def refund(authorization, options={})
+ def refund(amount, authorization, options={})
post = {}
- add_credentials(post, 'SUBMIT_TRANSACTION')
- add_transaction_type(post, 'REFUND')
+ add_credentials(post, 'SUBMIT_TRANSACTION', options)
+ add_transaction_elements(post, 'REFUND', options)
add_reference(post, authorization)
commit('refund', post)
@@ -77,7 +77,7 @@ def refund(authorization, options={})
def verify(credit_card, options={})
minimum = MINIMUMS[options[:currency].upcase] if options[:currency]
- amount = minimum || 100
+ amount = options[:verify_amount] || minimum || 100
MultiResponse.run(:use_first_response) do |r|
r.process { authorize(amount, credit_card, options) }
@@ -115,20 +115,20 @@ def scrub(transcript)
private
def auth_or_sale(post, transaction_type, amount, payment_method, options)
- add_credentials(post, 'SUBMIT_TRANSACTION')
- add_transaction_type(post, transaction_type)
+ add_credentials(post, 'SUBMIT_TRANSACTION', options)
+ add_transaction_elements(post, transaction_type, options)
add_order(post, options)
- add_buyer(post, options)
+ add_buyer(post, payment_method, options)
add_invoice(post, amount, options)
add_signature(post)
add_payment_method(post, payment_method, options)
- add_payer(post, options)
+ add_payer(post, payment_method, options)
add_extra_parameters(post, options)
end
- def add_credentials(post, command)
+ def add_credentials(post, command, options={})
post[:test] = test? unless command == 'CREATE_TOKEN'
- post[:language] = 'en'
+ post[:language] = options[:language] || 'en'
post[:command] = command
merchant = {}
merchant[:apiLogin] = @options[:api_login]
@@ -136,36 +136,87 @@ def add_credentials(post, command)
post[:merchant] = merchant
end
- def add_transaction_type(post, type)
+ def add_transaction_elements(post, type, options)
transaction = {}
+ transaction[:paymentCountry] = @options[:payment_country]
transaction[:type] = type
+ transaction[:ipAddress] = options[:ip] || ''
+ transaction[:userAgent] = options[:user_agent] if options[:user_agent]
+ transaction[:cookie] = options[:cookie] if options[:cookie]
+ transaction[:deviceSessionId] = options[:device_session_id] if options[:device_session_id]
post[:transaction] = transaction
end
def add_order(post, options)
order = {}
order[:accountId] = @options[:account_id]
+ order[:partnerId] = options[:partner_id] if options[:partner_id]
order[:referenceCode] = options[:order_id] || generate_unique_id
- order[:description] = options[:description] || 'unspecified'
- order[:language] = 'en'
+ order[:description] = options[:description] || 'Compra en ' + @options[:merchant_id]
+ order[:language] = options[:language] || 'en'
+ order[:shippingAddress] = shipping_address_fields(options) if options[:shipping_address]
post[:transaction][:order] = order
end
- def add_buyer(post, options)
- if address = options[:shipping_address]
- buyer = {}
- buyer[:fullName] = address[:name]
- shipping_address = {}
- shipping_address[:street1] = address[:address1]
- shipping_address[:street2] = address[:address2]
- shipping_address[:city] = address[:city]
- shipping_address[:state] = address[:state]
- shipping_address[:country] = address[:country]
- shipping_address[:postalCode] = address[:zip]
- shipping_address[:phone] = address[:phone]
- buyer[:shippingAddress] = shipping_address
- post[:transaction][:order][:buyer] = buyer
+ def add_payer(post, payment_method, options)
+ address = options[:billing_address]
+ payer = {}
+ payer[:fullName] = payment_method.name.strip
+ payer[:contactPhone] = address[:phone] if (address && address[:phone])
+ payer[:dniNumber] = options[:dni_number] if options[:dni_number]
+ payer[:dniType] = options[:dni_type] if options[:dni_type]
+ payer[:emailAddress] = options[:email] if options[:email]
+ payer[:birthdate] = options[:birth_date] if options[:birth_date] && @options[:payment_country] == 'MX'
+ payer[:billingAddress] = billing_address_fields(options)
+ post[:transaction][:payer] = payer
+ end
+
+ def billing_address_fields(options)
+ return unless address = options[:billing_address]
+ billing_address = {}
+ billing_address[:street1] = address[:address1]
+ billing_address[:street2] = address[:address2]
+ billing_address[:city] = address[:city]
+ billing_address[:state] = address[:state]
+ billing_address[:country] = address[:country]
+ billing_address[:postalCode] = address[:zip] if @options[:payment_country] == 'MX'
+ billing_address[:phone] = address[:phone]
+ billing_address
+ end
+
+ def add_buyer(post, payment_method, options)
+ buyer = {}
+ if buyer_hash = options[:buyer]
+ buyer[:fullName] = buyer_hash[:name]
+ buyer[:dniNumber] = buyer_hash[:dni_number]
+ buyer[:dniType] = buyer_hash[:dni_type]
+ buyer[:cnpj] = buyer_hash[:cnpj] if @options[:payment_country] == 'BR'
+ buyer[:emailAddress] = buyer_hash[:email]
+ buyer[:contactPhone] = (options[:billing_address][:phone] if options[:billing_address]) || (options[:shipping_address][:phone] if options[:shipping_address]) || ''
+ buyer[:shippingAddress] = shipping_address_fields(options) if options[:shipping_address]
+ else
+ buyer[:fullName] = payment_method.name.strip
+ buyer[:dniNumber] = options[:dni_number]
+ buyer[:dniType] = options[:dni_type]
+ buyer[:cnpj] = options[:cnpj] if @options[:payment_country] == 'BR'
+ buyer[:emailAddress] = options[:email]
+ buyer[:contactPhone] = (options[:billing_address][:phone] if options[:billing_address]) || (options[:shipping_address][:phone] if options[:shipping_address]) || ''
+ buyer[:shippingAddress] = shipping_address_fields(options) if options[:shipping_address]
end
+ post[:transaction][:order][:buyer] = buyer
+ end
+
+ def shipping_address_fields(options)
+ return unless address = options[:shipping_address]
+ shipping_address = {}
+ shipping_address[:street1] = address[:address1]
+ shipping_address[:street2] = address[:address2]
+ shipping_address[:city] = address[:city]
+ shipping_address[:state] = address[:state]
+ shipping_address[:country] = address[:country]
+ shipping_address[:postalCode] = address[:zip]
+ shipping_address[:phone] = address[:phone]
+ shipping_address
end
def add_invoice(post, money, options)
@@ -173,8 +224,18 @@ def add_invoice(post, money, options)
tx_value[:value] = amount(money)
tx_value[:currency] = options[:currency] || currency(money)
+ tx_tax = {}
+ tx_tax[:value] = options[:tax] || '0'
+ tx_tax[:currency] = options[:currency] || currency(money)
+
+ tx_tax_return_base = {}
+ tx_tax_return_base[:value] = options[:tax_return_base] || '0'
+ tx_tax_return_base[:currency] = options[:currency] || currency(money)
+
additional_values = {}
additional_values[:TX_VALUE] = tx_value
+ additional_values[:TX_TAX] = tx_tax if @options[:payment_country] == 'CO'
+ additional_values[:TX_TAX_RETURN_BASE] = tx_tax_return_base if @options[:payment_country] == 'CO'
post[:transaction][:order][:additionalValues] = additional_values
end
@@ -200,37 +261,25 @@ def add_payment_method(post, payment_method, options)
brand, token = split_authorization(payment_method)
credit_card = {}
credit_card[:securityCode] = options[:cvv] if options[:cvv]
+ credit_card[:processWithoutCvv2] = true if options[:cvv].blank?
post[:transaction][:creditCard] = credit_card
post[:transaction][:creditCardTokenId] = token
post[:transaction][:paymentMethod] = brand.upcase
else
credit_card = {}
credit_card[:number] = payment_method.number
- credit_card[:securityCode] = payment_method.verification_value
+ credit_card[:securityCode] = payment_method.verification_value || options[:cvv]
credit_card[:expirationDate] = format(payment_method.year, :four_digits).to_s + '/' + format(payment_method.month, :two_digits).to_s
credit_card[:name] = payment_method.name.strip
+ credit_card[:processWithoutCvv2] = true if add_process_without_cvv2(payment_method, options)
post[:transaction][:creditCard] = credit_card
post[:transaction][:paymentMethod] = BRAND_MAP[payment_method.brand.to_s]
end
end
- def add_payer(post, options)
- if address = options[:billing_address]
- payer = {}
- post[:transaction][:paymentCountry] = address[:country]
- payer[:fullName] = address[:name]
- payer[:contactPhone] = address[:phone]
- billing_address = {}
- billing_address[:street1] = address[:address1]
- billing_address[:street2] = address[:address2]
- billing_address[:city] = address[:city]
- billing_address[:state] = address[:state]
- billing_address[:country] = address[:country]
- billing_address[:postalCode] = address[:zip]
- billing_address[:phone] = address[:phone]
- payer[:billingAddress] = billing_address
- post[:transaction][:payer] = payer
- end
+ def add_process_without_cvv2(payment_method, options)
+ return true if payment_method.verification_value.blank? && options[:cvv].blank?
+ false
end
def add_extra_parameters(post, options)
@@ -290,6 +339,7 @@ def headers
end
def post_data(params)
+ params.merge(test: test?)
params.to_json
end
@@ -307,6 +357,8 @@ def success_from(action, response)
response["code"] == "SUCCESS" && response["creditCardToken"] && response["creditCardToken"]["creditCardTokenId"].present?
when 'verify_credentials'
response["code"] == "SUCCESS"
+ when 'refund', 'void'
+ response["code"] == "SUCCESS" && response["transactionResponse"] && (response["transactionResponse"]["state"] == "PENDING" || response["transactionResponse"]["state"] == "APPROVED")
else
response["code"] == "SUCCESS" && response["transactionResponse"] && (response["transactionResponse"]["state"] == "APPROVED")
end
@@ -322,8 +374,10 @@ def message_from(action, success, response)
return "VERIFIED" if success
"FAILED"
else
- response_message = response["transactionResponse"]["responseMessage"] if response["transactionResponse"]
- response_code = response["transactionResponse"]["responseCode"] if response["transactionResponse"]
+ if response["transactionResponse"]
+ response_message = response["transactionResponse"]["responseMessage"]
+ response_code = response["transactionResponse"]["responseCode"] || response["transactionResponse"]["pendingReason"]
+ end
return response_code if success
response["error"] || response_message || response_code || "FAILED"
end
diff --git a/lib/active_merchant/billing/gateways/pin.rb b/lib/active_merchant/billing/gateways/pin.rb
index 5c9b5b86e27..a7e04216977 100644
--- a/lib/active_merchant/billing/gateways/pin.rb
+++ b/lib/active_merchant/billing/gateways/pin.rb
@@ -29,6 +29,7 @@ def purchase(money, creditcard, options = {})
add_creditcard(post, creditcard)
add_address(post, creditcard, options)
add_capture(post, options)
+ add_metadata(post, options)
commit(:post, 'charges', post, options)
end
@@ -141,6 +142,10 @@ def add_creditcard(post, creditcard)
end
end
+ def add_metadata(post, options)
+ post[:metadata] = options[:metadata] if options[:metadata]
+ end
+
def headers(params = {})
result = {
"Content-Type" => "application/json",
diff --git a/lib/active_merchant/billing/gateways/pro_pay.rb b/lib/active_merchant/billing/gateways/pro_pay.rb
new file mode 100644
index 00000000000..73bc14ed749
--- /dev/null
+++ b/lib/active_merchant/billing/gateways/pro_pay.rb
@@ -0,0 +1,326 @@
+require 'nokogiri'
+
+module ActiveMerchant #:nodoc:
+ module Billing #:nodoc:
+ class ProPayGateway < Gateway
+ self.test_url = 'https://xmltest.propay.com/API/PropayAPI.aspx'
+ self.live_url = 'https://epay.propay.com/api/propayapi.aspx'
+
+ self.supported_countries = ['US', 'CA']
+ self.default_currency = 'USD'
+ self.money_format = :cents
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
+
+ self.homepage_url = 'https://www.propay.com/'
+ self.display_name = 'ProPay'
+
+ STATUS_RESPONSE_CODES = {
+ "00" => "Success",
+ "20" => "Invalid username",
+ "21" => "Invalid transType",
+ "22" => "Invalid Currency Code",
+ "23" => "Invalid accountType",
+ "24" => "Invalid sourceEmail",
+ "25" => "Invalid firstName",
+ "26" => "Invalid mInitial",
+ "27" => "Invalid lastName",
+ "28" => "Invalid billAddr",
+ "29" => "Invalid aptNum",
+ "30" => "Invalid city",
+ "31" => "Invalid state",
+ "32" => "Invalid billZip",
+ "33" => "Invalid mailAddr",
+ "34" => "Invalid mailApt",
+ "35" => "Invalid mailCity",
+ "36" => "Invalid mailState",
+ "37" => "Invalid mailZip",
+ "38" => "Invalid dayPhone",
+ "39" => "Invalid evenPhone",
+ "40" => "Invalid ssn",
+ "41" => "Invalid dob",
+ "42" => "Invalid recEmail",
+ "43" => "Invalid knownAccount",
+ "44" => "Invalid amount",
+ "45" => "Invalid invNum",
+ "46" => "Invalid rtNum",
+ "47" => "Invalid accntNum",
+ "48" => "Invalid ccNum",
+ "49" => "Invalid expDate",
+ "50" => "Invalid cvv2",
+ "51" => "Invalid transNum and/or Unable to act perform actions on transNum due to funding",
+ "52" => "Invalid splitNum",
+ "53" => "A ProPay account with this email address already exists AND/OR User has no account number",
+ "54" => "A ProPay account with this social security number already exists",
+ "55" => "The email address provided does not correspond to a ProPay account.",
+ "56" => "Recipient’s email address shouldn’t have a ProPay account and does",
+ "57" => "Cannot settle transaction because it already expired",
+ "58" => "Credit card declined",
+ "59" => "Invalid Credential or IP address not allowed",
+ "60" => "Credit card authorization timed out; retry at a later time",
+ "61" => "Amount exceeds single transaction limit",
+ "62" => "Amount exceeds monthly volume limit",
+ "63" => "Insufficient funds in account",
+ "64" => "Over credit card use limit",
+ "65" => "Miscellaneous error",
+ "66" => "Denied a ProPay account",
+ "67" => "Unauthorized service requested",
+ "68" => "Account not affiliated",
+ "69" => "Duplicate invoice number (The same card was charged for the same amount with the same invoice number (including blank invoices) in a 1 minute period. Details about the original transaction are included whenever a 69 response is returned. These details include a repeat of the auth code, the original AVS response, and the original CVV response.)",
+ "70" => "Duplicate external ID",
+ "71" => "Account previously set up, but problem affiliating it with partner",
+ "72" => "The ProPay Account has already been upgraded to a Premium Account",
+ "73" => "Invalid Destination Account",
+ "74" => "Account or Trans Error",
+ "75" => "Money already pulled",
+ "76" => "Not Premium (used only for push/pull transactions)",
+ "77" => "Empty results",
+ "78" => "Invalid Authentication",
+ "79" => "Generic account status error",
+ "80" => "Invalid Password",
+ "81" => "Account Expired",
+ "82" => "InvalidUserID",
+ "83" => "BatchTransCountError",
+ "84" => "InvalidBeginDate",
+ "85" => "InvalidEndDate",
+ "86" => "InvalidExternalID",
+ "87" => "DuplicateUserID",
+ "88" => "Invalid track 1",
+ "89" => "Invalid track 2",
+ "90" => "Transaction already refunded",
+ "91" => "Duplicate Batch ID"
+ }
+
+ TRANSACTION_RESPONSE_CODES = {
+ "00" => "Success",
+ "1" => "Transaction blocked by issuer",
+ "4" => "Pick up card and deny transaction",
+ "5" => "Problem with the account",
+ "6" => "Customer requested stop to recurring payment",
+ "7" => "Customer requested stop to all recurring payments",
+ "8" => "Honor with ID only",
+ "9" => "Unpaid items on customer account",
+ "12" => "Invalid transaction",
+ "13" => "Amount Error",
+ "14" => "Invalid card number",
+ "15" => "No such issuer. Could not route transaction",
+ "16" => "Refund error",
+ "17" => "Over limit",
+ "19" => "Reenter transaction or the merchant account may be boarded incorrectly",
+ "25" => "Invalid terminal 41 Lost card",
+ "43" => "Stolen card",
+ "51" => "Insufficient funds",
+ "52" => "No such account",
+ "54" => "Expired card",
+ "55" => "Incorrect PIN",
+ "57" => "Bank does not allow this type of purchase",
+ "58" => "Credit card network does not allow this type of purchase for your merchant account.",
+ "61" => "Exceeds issuer withdrawal limit",
+ "62" => "Issuer does not allow this card to be charged for your business.",
+ "63" => "Security Violation",
+ "65" => "Activity limit exceeded",
+ "75" => "PIN tries exceeded",
+ "76" => "Unable to locate account",
+ "78" => "Account not recognized",
+ "80" => "Invalid Date",
+ "82" => "Invalid CVV2",
+ "83" => "Cannot verify the PIN",
+ "85" => "Service not supported for this card",
+ "93" => "Cannot complete transaction. Customer should call 800 number.",
+ "95" => "Misc Error Transaction failure",
+ "96" => "Issuer system malfunction or timeout.",
+ "97" => "Approved for a lesser amount. ProPay will not settle and consider this a decline.",
+ "98" => "Failure HV",
+ "99" => "Generic decline or unable to parse issuer response code"
+ }
+
+ def initialize(options={})
+ requires!(options, :cert_str)
+ super
+ end
+
+ def purchase(money, payment, options={})
+ request = build_xml_request do |xml|
+ add_invoice(xml, money, options)
+ add_payment(xml, payment, options)
+ add_address(xml, options)
+ add_account(xml, options)
+ add_recurring(xml, options)
+ xml.transType "04"
+ end
+
+ commit(request)
+ end
+
+ def authorize(money, payment, options={})
+ request = build_xml_request do |xml|
+ add_invoice(xml, money, options)
+ add_payment(xml, payment, options)
+ add_address(xml, options)
+ add_account(xml, options)
+ add_recurring(xml, options)
+ xml.transType "05"
+ end
+
+ commit(request)
+ end
+
+ def capture(money, authorization, options={})
+ request = build_xml_request do |xml|
+ add_invoice(xml, money, options)
+ add_account(xml, options)
+ xml.transNum authorization
+ xml.transType "06"
+ end
+
+ commit(request)
+ end
+
+ def refund(money, authorization, options={})
+ request = build_xml_request do |xml|
+ add_invoice(xml, money, options)
+ add_account(xml, options)
+ xml.transNum authorization
+ xml.transType "07"
+ end
+
+ commit(request)
+ end
+
+ def void(authorization, options={})
+ refund(nil, authorization, options)
+ end
+
+ def credit(money, payment, options={})
+ request = build_xml_request do |xml|
+ add_invoice(xml, money, options)
+ add_payment(xml, payment, options)
+ add_account(xml, options)
+ xml.transType "35"
+ end
+
+ commit(request)
+ end
+
+ def verify(credit_card, options={})
+ MultiResponse.run(:use_first_response) do |r|
+ r.process { authorize(100, credit_card, options) }
+ r.process(:ignore_result) { void(r.authorization, options) }
+ end
+ end
+
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r(().+()), '\1[FILTERED]\2').
+ gsub(%r(().+()), '\1[FILTERED]\2').
+ gsub(%r(().+()), '\1[FILTERED]\2')
+ end
+
+ private
+
+ def add_payment(xml, payment, options)
+ xml.ccNum payment.number
+ xml.expDate "#{format(payment.month, :two_digits)}#{format(payment.year, :two_digits)}"
+ xml.CVV2 payment.verification_value
+ xml.cardholderName payment.name
+ end
+
+ def add_address(xml, options)
+ if address = options[:billing_address] || options[:address]
+ xml.addr address[:address1]
+ xml.aptNum address[:address2]
+ xml.city address[:city]
+ xml.state address[:state]
+ xml.zip address[:zip]
+ end
+ end
+
+ def add_account(xml, options)
+ xml.accountNum options[:account_num]
+ end
+
+ def add_invoice(xml, money, options)
+ xml.amount amount(money)
+ xml.currencyCode options[:currency] || currency(money)
+ xml.invNum options[:order_id] || SecureRandom.hex(25)
+ end
+
+ def add_recurring(xml, options)
+ xml.recurringPayment options[:recurring_payment]
+ end
+
+ def parse(body)
+ results = {}
+ xml = Nokogiri::XML(body)
+ resp = xml.xpath("//XMLResponse/XMLTrans")
+ resp.children.each do |element|
+ results[element.name.underscore.downcase.to_sym] = element.text
+ end
+ results
+ end
+
+ def commit(parameters)
+ url = (test? ? test_url : live_url)
+ response = parse(ssl_post(url, parameters))
+
+ Response.new(
+ success_from(response),
+ message_from(response),
+ response,
+ authorization: authorization_from(response),
+ avs_result: AVSResult.new(code: response[:avs]),
+ cvv_result: CVVResult.new(response[:cvv2_resp]),
+ test: test?,
+ error_code: error_code_from(response)
+ )
+ end
+
+ def success_from(response)
+ response[:status] == "00"
+ end
+
+ def message_from(response)
+ return "Success" if success_from(response)
+ message = STATUS_RESPONSE_CODES[response[:status]]
+ message += " - #{TRANSACTION_RESPONSE_CODES[response[:response_code]]}" if response[:response_code]
+
+ message
+ end
+
+ def authorization_from(response)
+ response[:trans_num]
+ end
+
+ def error_code_from(response)
+ unless success_from(response)
+ response[:status]
+ end
+ end
+
+ def build_xml_request
+ builder = Nokogiri::XML::Builder.new do |xml|
+ xml.XMLRequest do
+ xml.certStr @options[:cert_str]
+ xml.class_ "partner"
+ xml.XMLTrans do
+ yield(xml)
+ end
+ end
+ end
+
+ builder.to_xml
+ end
+ end
+
+ def underscore(camel_cased_word)
+ camel_cased_word.to_s.gsub(/::/, '/').
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
+ tr("-", "_").
+ downcase
+ end
+ end
+end
diff --git a/lib/active_merchant/billing/gateways/psigate.rb b/lib/active_merchant/billing/gateways/psigate.rb
index f6e017359e2..aa9a5fa53f1 100644
--- a/lib/active_merchant/billing/gateways/psigate.rb
+++ b/lib/active_merchant/billing/gateways/psigate.rb
@@ -35,7 +35,7 @@ module Billing #:nodoc:
# :email => 'jack@yahoo.com'
# )
class PsigateGateway < Gateway
- self.test_url = 'https://dev.psigate.com:7989/Messenger/XMLMessenger'
+ self.test_url = 'https://staging.psigate.com:17989/Messenger/XMLMessenger'
self.live_url = 'https://secure.psigate.com:17934/Messenger/XMLMessenger'
self.supported_cardtypes = [:visa, :master, :american_express]
diff --git a/lib/active_merchant/billing/gateways/quickbooks.rb b/lib/active_merchant/billing/gateways/quickbooks.rb
index 15ae55d54ce..a4f52206ec9 100644
--- a/lib/active_merchant/billing/gateways/quickbooks.rb
+++ b/lib/active_merchant/billing/gateways/quickbooks.rb
@@ -78,6 +78,7 @@ def capture(money, authorization, options = {})
post = {}
capture_uri = "#{ENDPOINT}/#{CGI.escape(authorization)}/capture"
post[:amount] = localized_amount(money, currency(money))
+ add_context(post, options)
commit(capture_uri, post)
end
@@ -85,6 +86,7 @@ def capture(money, authorization, options = {})
def refund(money, authorization, options = {})
post = {}
post[:amount] = localized_amount(money, currency(money))
+ add_context(post, options)
commit(refund_uri(authorization), post)
end
@@ -137,6 +139,7 @@ def add_amount(post, money, options = {})
def add_payment(post, payment, options = {})
add_creditcard(post, payment, options)
+ add_context(post, options)
end
def add_creditcard(post, creditcard, options = {})
@@ -151,6 +154,13 @@ def add_creditcard(post, creditcard, options = {})
post[:card] = card
end
+ def add_context(post, options = {})
+ post[:context] = {
+ mobile: options.fetch(:mobile, false),
+ isEcommerce: options.fetch(:ecommerce, true)
+ }
+ end
+
def parse(body)
JSON.parse(body)
end
diff --git a/lib/active_merchant/billing/gateways/quickpay.rb b/lib/active_merchant/billing/gateways/quickpay.rb
index 6fe34125cbc..3cc61e5656a 100644
--- a/lib/active_merchant/billing/gateways/quickpay.rb
+++ b/lib/active_merchant/billing/gateways/quickpay.rb
@@ -10,16 +10,16 @@ class QuickpayGateway < Gateway
self.abstract_class = true
def self.new(options = {})
- options.fetch(:login) rescue raise ArgumentError.new("Missing required parameter: login")
+ options.fetch(:login) { raise ArgumentError.new("Missing required parameter: login") }
version = options[:login].to_i < 10000000 ? 10 : 7
if version <= 7
QuickpayV4to7Gateway.new(options)
else
- QuickpayV10Gateway.new(options)
+ QuickpayV10Gateway.new(options)
end
end
-
+
end
end
end
diff --git a/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb b/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb
index 586eec8711c..ca185b8ccdf 100644
--- a/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb
+++ b/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb
@@ -15,26 +15,34 @@ def initialize(options = {})
end
def purchase(money, credit_card_or_reference, options = {})
- MultiResponse.run(true) do |r|
+ MultiResponse.run do |r|
+ if credit_card_or_reference.is_a?(String)
+ r.process { create_token(credit_card_or_reference, options) }
+ credit_card_or_reference = r.authorization
+ end
r.process { create_payment(money, options) }
r.process {
post = authorization_params(money, credit_card_or_reference, options)
add_autocapture(post, false)
- commit(synchronized_path("/payments/#{r.authorization}/authorize"), post)
+ commit(synchronized_path("/payments/#{r.responses.last.params["id"]}/authorize"), post)
}
r.process {
post = capture_params(money, credit_card_or_reference, options)
- commit(synchronized_path("/payments/#{r.authorization}/capture"), post)
+ commit(synchronized_path("/payments/#{r.responses.last.params["id"]}/capture"), post)
}
end
end
def authorize(money, credit_card_or_reference, options = {})
- MultiResponse.run(true) do |r|
+ MultiResponse.run do |r|
+ if credit_card_or_reference.is_a?(String)
+ r.process { create_token(credit_card_or_reference, options) }
+ credit_card_or_reference = r.authorization
+ end
r.process { create_payment(money, options) }
r.process {
post = authorization_params(money, credit_card_or_reference, options)
- commit(synchronized_path("/payments/#{r.authorization}/authorize"), post)
+ commit(synchronized_path("/payments/#{r.responses.last.params["id"]}/authorize"), post)
}
end
end
@@ -71,12 +79,10 @@ def store(credit_card, options = {})
MultiResponse.run do |r|
r.process { create_store(options) }
r.process { authorize_store(r.authorization, credit_card, options)}
- r.process { create_token(r.authorization, options.merge({id: r.authorization}))}
end
end
def unstore(identification)
- identification = identification.split(";").last
commit(synchronized_path "/cards/#{identification}/cancel")
end
@@ -93,11 +99,11 @@ def scrub(transcript)
private
- def authorization_params(money, credit_card, options = {})
+ def authorization_params(money, credit_card_or_reference, options = {})
post = {}
add_amount(post, money, options)
- add_credit_card_or_reference(post, credit_card)
+ add_credit_card_or_reference(post, credit_card_or_reference)
add_additional_params(:authorize, post, options)
post
@@ -126,7 +132,6 @@ def authorize_store(identification, credit_card, options = {})
def create_token(identification, options)
post = {}
- post[:id] = options[:id]
commit(synchronized_path("/cards/#{identification}/tokens"), post)
end
@@ -150,15 +155,15 @@ def commit(action, params = {})
Response.new(success, message_from(success, response), response,
:test => test?,
- :authorization => authorization_from(response, params[:id])
+ :authorization => authorization_from(response)
)
end
- def authorization_from(response, auth_id)
+ def authorization_from(response)
if response["token"]
- "#{response["token"]};#{auth_id}"
+ response["token"].to_s
else
- response["id"]
+ response["id"].to_s
end
end
@@ -190,7 +195,7 @@ def add_invoice(post, options)
post[:shipping_address] = map_address(options[:shipping_address])
end
- [:metadata, :brading_id, :variables].each do |field|
+ [:metadata, :branding_id, :variables].each do |field|
post[field] = options[field] if options[field]
end
end
@@ -205,8 +210,7 @@ def add_additional_params(action, post, options = {})
def add_credit_card_or_reference(post, credit_card_or_reference, options = {})
post[:card] ||= {}
if credit_card_or_reference.is_a?(String)
- reference = credit_card_or_reference.split(";").first
- post[:card][:token] = reference
+ post[:card][:token] = credit_card_or_reference
else
post[:card][:number] = credit_card_or_reference.number
post[:card][:cvd] = credit_card_or_reference.verification_value
diff --git a/lib/active_merchant/billing/gateways/qvalent.rb b/lib/active_merchant/billing/gateways/qvalent.rb
index 12ba65e879a..43e3ec9c09f 100644
--- a/lib/active_merchant/billing/gateways/qvalent.rb
+++ b/lib/active_merchant/billing/gateways/qvalent.rb
@@ -13,7 +13,7 @@ class QvalentGateway < Gateway
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners]
def initialize(options={})
- requires!(options, :username, :password, :merchant)
+ requires!(options, :username, :password, :merchant, :pem, :pem_password)
super
end
@@ -24,19 +24,64 @@ def purchase(amount, payment_method, options={})
add_payment_method(post, payment_method)
add_verification_value(post, payment_method)
add_customer_data(post, options)
+ add_soft_descriptors(post, options)
commit("capture", post)
end
+ def authorize(amount, payment_method, options={})
+ post = {}
+ add_invoice(post, amount, options)
+ add_order_number(post, options)
+ add_payment_method(post, payment_method)
+ add_verification_value(post, payment_method)
+ add_customer_data(post, options)
+ add_soft_descriptors(post, options)
+
+ commit("preauth", post)
+ end
+
+ def capture(amount, authorization, options={})
+ post = {}
+ add_invoice(post, amount, options)
+ add_reference(post, authorization, options)
+ add_customer_data(post, options)
+ add_soft_descriptors(post, options)
+
+ commit("captureWithoutAuth", post)
+ end
+
def refund(amount, authorization, options={})
post = {}
add_invoice(post, amount, options)
add_reference(post, authorization, options)
add_customer_data(post, options)
+ add_soft_descriptors(post, options)
commit("refund", post)
end
+ # Credit requires the merchant account to be enabled for "Adhoc Refunds"
+ def credit(amount, payment_method, options={})
+ post = {}
+ add_invoice(post, amount, options)
+ add_order_number(post, options)
+ add_payment_method(post, payment_method)
+ add_customer_data(post, options)
+ add_soft_descriptors(post, options)
+
+ commit("refund", post)
+ end
+
+ def void(authorization, options={})
+ post = {}
+ add_reference(post, authorization, options)
+ add_customer_data(post, options)
+ add_soft_descriptors(post, options)
+
+ commit("reversal", post)
+ end
+
def store(payment_method, options = {})
post = {}
add_payment_method(post, payment_method)
@@ -62,10 +107,20 @@ def scrub(transcript)
CURRENCY_CODES["AUD"] = "AUD"
CURRENCY_CODES["INR"] = "INR"
+ def add_soft_descriptors(post, options)
+ post["customer.merchantName"] = options[:customer_merchant_name] if options[:customer_merchant_name]
+ post["customer.merchantStreetAddress"] = options[:customer_merchant_street_address] if options[:customer_merchant_street_address]
+ post["customer.merchantLocation"] = options[:customer_merchant_location] if options[:customer_merchant_location]
+ post["customer.merchantState"] = options[:customer_merchant_state] if options[:customer_merchant_state]
+ post["customer.merchantCountry"] = options[:customer_merchant_country] if options[:customer_merchant_country]
+ post["customer.merchantPostCode"] = options[:customer_merchant_post_code] if options[:customer_merchant_post_code]
+ post["customer.subMerchantId"] = options[:customer_sub_merchant_id] if options[:customer_sub_merchant_id]
+ end
+
def add_invoice(post, money, options)
post["order.amount"] = amount(money)
post["card.currency"] = CURRENCY_CODES[options[:currency] || currency(money)]
- post["order.ECI"] = "SSL"
+ post["order.ECI"] = options[:eci] ? options[:eci] : "SSL"
end
def add_payment_method(post, payment_method)
@@ -93,7 +148,9 @@ def add_order_number(post, options)
end
def add_customer_data(post, options)
- post["order.ipAddress"] = options[:ip]
+ post["order.ipAddress"] = options[:ip] || "127.0.0.1"
+ post["order.xid"] = options[:xid] if options[:xid]
+ post["order.cavv"] = options[:cavv] if options[:cavv]
end
def commit(action, post)
diff --git a/lib/active_merchant/billing/gateways/realex.rb b/lib/active_merchant/billing/gateways/realex.rb
index 7411f87e087..ecc3150c992 100644
--- a/lib/active_merchant/billing/gateways/realex.rb
+++ b/lib/active_merchant/billing/gateways/realex.rb
@@ -35,7 +35,7 @@ class RealexGateway < Gateway
self.money_format = :cents
self.default_currency = 'EUR'
self.supported_cardtypes = [ :visa, :master, :american_express, :diners_club, :switch, :solo, :laser ]
- self.supported_countries = %w(IE GB FR BE NL LU IT)
+ self.supported_countries = %w(IE GB FR BE NL LU IT US CA)
self.homepage_url = 'http://www.realexpayments.com/'
self.display_name = 'Realex'
@@ -103,11 +103,8 @@ def commit(request)
response,
:test => (response[:message] =~ %r{\[ test system \]}),
:authorization => authorization_from(response),
- :cvv_result => response[:cvnresult],
- :avs_result => {
- :street_match => response[:avspostcoderesponse],
- :postal_match => response[:avspostcoderesponse]
- }
+ avs_result: AVSResult.new(code: response[:avspostcoderesponse]),
+ cvv_result: CVVResult.new(response[:cvnresult])
)
end
@@ -143,6 +140,7 @@ def build_purchase_or_authorization_request(action, money, credit_card, options)
add_card(xml, credit_card)
xml.tag! 'autosettle', 'flag' => auto_settle_flag(action)
add_signed_digest(xml, timestamp, @options[:login], sanitize_order_id(options[:order_id]), amount(money), (options[:currency] || currency(money)), credit_card.number)
+ add_network_tokenization_card(xml, credit_card) if credit_card.is_a?(NetworkTokenizationCreditCard)
add_comments(xml, options)
add_address_and_customer_info(xml, options)
end
@@ -254,6 +252,18 @@ def add_card(xml, credit_card)
end
end
+ def add_network_tokenization_card(xml, payment)
+ xml.tag! 'mpi' do
+ xml.tag! 'cavv', payment.payment_cryptogram
+ xml.tag! 'eci', payment.eci
+ end
+ xml.tag! 'supplementarydata' do
+ xml.tag! 'item', 'type' => 'mobile' do
+ xml.tag! 'field01', payment.source.to_s.gsub('_','-')
+ end
+ end
+ end
+
def format_address_code(address)
code = [address[:zip].to_s, address[:address1].to_s + address[:address2].to_s]
code.collect{|e| e.gsub(/\D/, "")}.reject{|e| e.empty?}.join("|")
diff --git a/lib/active_merchant/billing/gateways/redsys.rb b/lib/active_merchant/billing/gateways/redsys.rb
index f10f13736c8..8611832637d 100644
--- a/lib/active_merchant/billing/gateways/redsys.rb
+++ b/lib/active_merchant/billing/gateways/redsys.rb
@@ -56,24 +56,32 @@ class RedsysGateway < Gateway
"CAD" => '124',
"CHF" => '756',
"CLP" => '152',
+ "CNY" => '156',
"COP" => '170',
+ "CRC" => '188',
"CZK" => '203',
+ "DKK" => '208',
+ "DOP" => '214',
"EUR" => '978',
"GBP" => '826',
"GTQ" => '320',
"HUF" => '348',
+ "IDR" => '360',
+ "INR" => '356',
"JPY" => '392',
+ "KRW" => '410',
"MYR" => '458',
"MXN" => '484',
"NOK" => '578',
"NZD" => '554',
"PEN" => '604',
- "PLN" => '616',
+ "PLN" => '985',
"RUB" => '643',
"SAR" => '682',
"SEK" => '752',
"SGD" => '702',
"THB" => '764',
+ "TWD" => '901',
"USD" => '840',
"UYU" => '858'
}
@@ -488,7 +496,7 @@ def sign_request(xml_request_string, order_id)
def encrypt(key, order_id)
block_length = 8
- cipher = OpenSSL::Cipher::Cipher.new('DES3')
+ cipher = OpenSSL::Cipher.new('DES3')
cipher.encrypt
cipher.key = Base64.strict_decode64(key)
diff --git a/lib/active_merchant/billing/gateways/safe_charge.rb b/lib/active_merchant/billing/gateways/safe_charge.rb
new file mode 100644
index 00000000000..dbbac0788e7
--- /dev/null
+++ b/lib/active_merchant/billing/gateways/safe_charge.rb
@@ -0,0 +1,262 @@
+require 'nokogiri'
+
+module ActiveMerchant #:nodoc:
+ module Billing #:nodoc:
+ class SafeChargeGateway < Gateway
+ self.test_url = 'https://process.sandbox.safecharge.com/service.asmx/Process'
+ self.live_url = 'https://process.safecharge.com/service.asmx/Process'
+
+ self.supported_countries = ['AT', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'GR', 'ES', 'FI', 'FR', 'HR', 'HU', 'IE', 'IS', 'IT', 'LI', 'LT', 'LU', 'LV', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SE', 'SE', 'SI', 'SK', 'GB', 'US']
+ self.default_currency = 'USD'
+ self.supported_cardtypes = [:visa, :master]
+
+ self.homepage_url = 'https://www.safecharge.com'
+ self.display_name = 'SafeCharge'
+
+ VERSION = '4.1.0'
+
+ def initialize(options={})
+ requires!(options, :client_login_id, :client_password)
+ super
+ end
+
+ def purchase(money, payment, options={})
+ post = {}
+ post[:sg_APIType] = 1 if options[:three_d_secure]
+ trans_type = options[:three_d_secure] ? "Sale3D" : "Sale"
+ add_transaction_data(trans_type, post, money, options)
+ add_payment(post, payment, options)
+ add_customer_details(post, payment, options)
+
+ commit(post)
+ end
+
+ def authorize(money, payment, options={})
+ post = {}
+ add_transaction_data("Auth", post, money, options)
+ add_payment(post, payment, options)
+ add_customer_details(post, payment, options)
+
+ commit(post)
+ end
+
+ def capture(money, authorization, options={})
+ post = {}
+ auth, transaction_id, token, exp_month, exp_year, _, original_currency = authorization.split("|")
+ add_transaction_data("Settle", post, money, (options.merge!({currency: original_currency})))
+ post[:sg_AuthCode] = auth
+ post[:sg_TransactionID] = transaction_id
+ post[:sg_CCToken] = token
+ post[:sg_ExpMonth] = exp_month
+ post[:sg_ExpYear] = exp_year
+
+ commit(post)
+ end
+
+ def refund(money, authorization, options={})
+ post = {}
+ auth, transaction_id, token, exp_month, exp_year, _, original_currency = authorization.split("|")
+ add_transaction_data("Credit", post, money, (options.merge!({currency: original_currency})))
+ post[:sg_CreditType] = 2
+ post[:sg_AuthCode] = auth
+ post[:sg_TransactionID] = transaction_id
+ post[:sg_CCToken] = token
+ post[:sg_ExpMonth] = exp_month
+ post[:sg_ExpYear] = exp_year
+
+ commit(post)
+ end
+
+ def credit(money, payment, options={})
+ post = {}
+ add_payment(post, payment, options)
+ add_transaction_data("Credit", post, money, options)
+ post[:sg_CreditType] = 1
+
+ commit(post)
+ end
+
+ def void(authorization, options={})
+ post = {}
+ auth, transaction_id, token, exp_month, exp_year, original_amount, original_currency = authorization.split("|")
+ add_transaction_data("Void", post, (original_amount.to_f * 100), (options.merge!({currency: original_currency})))
+ post[:sg_CreditType] = 2
+ post[:sg_AuthCode] = auth
+ post[:sg_TransactionID] = transaction_id
+ post[:sg_CCToken] = token
+ post[:sg_ExpMonth] = exp_month
+ post[:sg_ExpYear] = exp_year
+
+ commit(post)
+ end
+
+ def verify(credit_card, options={})
+ MultiResponse.run(:use_first_response) do |r|
+ r.process { authorize(100, credit_card, options) }
+ r.process(:ignore_result) { void(r.authorization, options) }
+ end
+ end
+
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r((sg_ClientPassword=)[^&]+(&?)), '\1[FILTERED]\2').
+ gsub(%r((sg_CardNumber=)[^&]+(&?)), '\1[FILTERED]\2').
+ gsub(%r((sg_CVV2=)\d+), '\1[FILTERED]')
+ end
+
+ private
+
+ def add_transaction_data(trans_type, post, money, options)
+ post[:sg_TransType] = trans_type
+ post[:sg_Currency] = (options[:currency] || currency(money))
+ post[:sg_Amount] = amount(money)
+ post[:sg_ClientLoginID] = @options[:client_login_id]
+ post[:sg_ClientPassword] = @options[:client_password]
+ post[:sg_ResponseFormat] = "4"
+ post[:sg_Version] = VERSION
+ post[:sg_ClientUniqueID] = options[:order_id] if options[:order_id]
+ post[:sg_UserID] = options[:user_id] if options[:user_id]
+ post[:sg_AuthType] = options[:auth_type] if options[:auth_type]
+ post[:sg_ExpectedFulfillmentCount] = options[:expected_fulfillment_count] if options[:expected_fulfillment_count]
+ post[:sg_WebsiteID] = options[:website_id] if options[:website_id]
+ post[:sg_IPAddress] = options[:ip] if options[:ip]
+ post[:sg_VendorID] = options[:vendor_id] if options[:vendor_id]
+ post[:sg_Descriptor] = options[:merchant_descriptor] if options[:merchant_descriptor]
+ post[:sg_MerchantPhoneNumber] = options[:merchant_phone_number] if options[:merchant_phone_number]
+ post[:sg_MerchantName] = options[:merchant_name] if options[:merchant_name]
+ end
+
+ def add_payment(post, payment, options={})
+ post[:sg_NameOnCard] = payment.name
+ post[:sg_CardNumber] = payment.number
+ post[:sg_ExpMonth] = format(payment.month, :two_digits)
+ post[:sg_ExpYear] = format(payment.year, :two_digits)
+ post[:sg_CVV2] = payment.verification_value
+ post[:sg_StoredCredentialMode] = (options[:stored_credential_mode] == true ? 1 : 0)
+ end
+
+ def add_customer_details(post, payment, options)
+ if address = options[:billing_address] || options[:address]
+ post[:sg_FirstName] = payment.first_name
+ post[:sg_LastName] = payment.last_name
+ post[:sg_Address] = address[:address1] if address[:address1]
+ post[:sg_City] = address[:city] if address[:city]
+ post[:sg_State] = address[:state] if address[:state]
+ post[:sg_Zip] = address[:zip] if address[:zip]
+ post[:sg_Country] = address[:country] if address[:country]
+ post[:sg_Phone] = address[:phone] if address[:phone]
+ end
+
+ post[:sg_Email] = options[:email]
+ end
+
+ def parse(xml)
+ response = {}
+
+ doc = Nokogiri::XML(xml)
+ doc.root.xpath('*').each do |node|
+ if node.elements.size == 0
+ response[node.name.underscore.downcase.to_sym] = node.text
+ else
+ node.traverse do |childnode|
+ childnode_to_response(response, childnode)
+ end
+ end
+ end
+ response
+ end
+
+ def childnode_to_response(response, childnode)
+ if childnode.elements.size == 0
+ element_name_to_symbol(response, childnode)
+ else
+ childnode.traverse do |node|
+ element_name_to_symbol(response, node)
+ end
+ end
+ end
+
+ def element_name_to_symbol(response, childnode)
+ name = "#{childnode.name.downcase}"
+ response[name.to_sym] = childnode.text
+ end
+
+ def commit(parameters)
+ url = (test? ? test_url : live_url)
+ response = parse(ssl_post(url, post_data(parameters)))
+
+ Response.new(
+ success_from(response),
+ message_from(response),
+ response,
+ authorization: authorization_from(response, parameters),
+ avs_result: AVSResult.new(code: response[:avs_code]),
+ cvv_result: CVVResult.new(response[:cvv2_reply]),
+ test: test?,
+ error_code: error_code_from(response)
+ )
+ end
+
+ def success_from(response)
+ response[:status] == "APPROVED"
+ end
+
+ def message_from(response)
+ return "Success" if success_from(response)
+ response[:reason_codes] || response[:reason]
+ end
+
+ def authorization_from(response, parameters)
+ [
+ response[:auth_code],
+ response[:transaction_id],
+ response[:token],
+ parameters[:sg_ExpMonth],
+ parameters[:sg_ExpYear],
+ parameters[:sg_Amount],
+ parameters[:sg_Currency]
+ ].join("|")
+ end
+
+ def split_authorization(authorization)
+ auth_code, transaction_id, token, month, year, original_amount = authorization.split("|")
+
+ {
+ auth_code: auth_code,
+ transaction_id: transaction_id,
+ token: token,
+ exp_month: month,
+ exp_year: year,
+ original_amount: amount(original_amount.to_f * 100)
+ }
+ end
+
+ def post_data(params)
+ return nil unless params
+
+ params.map do |key, value|
+ next if value != false && value.blank?
+ "#{key}=#{CGI.escape(value.to_s)}"
+ end.compact.join("&")
+ end
+
+ def error_code_from(response)
+ unless success_from(response)
+ response[:ex_err_code] || response[:err_code]
+ end
+ end
+
+ def underscore(camel_cased_word)
+ camel_cased_word.to_s.gsub(/::/, '/').
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
+ tr("-", "_").
+ downcase
+ end
+ end
+ end
+end
diff --git a/lib/active_merchant/billing/gateways/sage.rb b/lib/active_merchant/billing/gateways/sage.rb
index 257c4300c85..3841615c7a2 100644
--- a/lib/active_merchant/billing/gateways/sage.rb
+++ b/lib/active_merchant/billing/gateways/sage.rb
@@ -1,6 +1,8 @@
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class SageGateway < Gateway
+ include Empty
+
self.display_name = 'http://www.sagepayments.com'
self.homepage_url = 'Sage Payment Solutions'
self.live_url = 'https://www.sagepayments.net/cgi-bin'
@@ -100,6 +102,9 @@ def scrub(transcript)
gsub(%r((M_key=)[^&]*), '\1[FILTERED]').
gsub(%r((C_cardnumber=)[^&]*), '\1[FILTERED]').
gsub(%r((C_cvv=)[^&]*), '\1[FILTERED]').
+ gsub(%r((C_rte=)[^&]*), '\1[FILTERED]').
+ gsub(%r((C_acct=)[^&]*), '\1[FILTERED]').
+ gsub(%r((C_ssn=)[^&]*), '\1[FILTERED]').
gsub(%r(().+()), '\1[FILTERED]\2').
gsub(%r(().+()), '\1[FILTERED]\2').
gsub(%r(().+()), '\1[FILTERED]\2')
@@ -204,8 +209,8 @@ def parse_credit_card(data)
def add_invoice(post, options)
post[:T_ordernum] = (options[:order_id] || generate_unique_id).slice(0, 20)
- post[:T_tax] = amount(options[:tax]) unless options[:tax].blank?
- post[:T_shipping] = amount(options[:shipping]) unless options[:shipping].blank?
+ post[:T_tax] = amount(options[:tax]) unless empty?(options[:tax])
+ post[:T_shipping] = amount(options[:shipping]) unless empty?(options[:shipping])
end
def add_reference(post, reference)
@@ -226,7 +231,7 @@ def add_addresses(post, options)
post[:C_address] = billing_address[:address1]
post[:C_city] = billing_address[:city]
- post[:C_state] = billing_address[:state]
+ post[:C_state] = empty?(billing_address[:state]) ? "Outside of US" : billing_address[:state]
post[:C_zip] = billing_address[:zip]
post[:C_country] = billing_address[:country]
post[:C_telephone] = billing_address[:phone]
diff --git a/lib/active_merchant/billing/gateways/sage_pay.rb b/lib/active_merchant/billing/gateways/sage_pay.rb
index 788c2ada605..bcff3473ac2 100644
--- a/lib/active_merchant/billing/gateways/sage_pay.rb
+++ b/lib/active_merchant/billing/gateways/sage_pay.rb
@@ -20,7 +20,8 @@ class SagePayGateway < Gateway
:void => 'VOID',
:abort => 'ABORT',
:store => 'TOKEN',
- :unstore => 'REMOVETOKEN'
+ :unstore => 'REMOVETOKEN',
+ :repeat => 'REPEAT'
}
CREDIT_CARDS = {
@@ -36,13 +37,20 @@ class SagePayGateway < Gateway
:jcb => "JCB"
}
- AVS_CVV_CODE = {
+ AVS_CODE = {
"NOTPROVIDED" => nil,
"NOTCHECKED" => 'X',
"MATCHED" => 'Y',
"NOTMATCHED" => 'N'
}
+ CVV_CODE = {
+ "NOTPROVIDED" => 'S',
+ "NOTCHECKED" => 'X',
+ "MATCHED" => 'M',
+ "NOTMATCHED" => 'N'
+ }
+
OPTIONAL_REQUEST_FIELDS = {
paypal_callback_url: :PayPalCallbackURL,
basket: :Basket,
@@ -87,7 +95,7 @@ def purchase(money, payment_method, options = {})
add_customer_data(post, options)
add_optional_data(post, options)
- commit(:purchase, post)
+ commit((past_purchase_reference?(payment_method) ? :repeat : :purchase), post)
end
def authorize(money, payment_method, options = {})
@@ -130,7 +138,7 @@ def refund(money, identification, options = {})
post = {}
- add_credit_reference(post, identification)
+ add_related_reference(post, identification)
add_amount(post, money, options)
add_invoice(post, options)
@@ -195,7 +203,7 @@ def add_reference(post, identification)
add_pair(post, :SecurityKey, security_key)
end
- def add_credit_reference(post, identification)
+ def add_related_reference(post, identification)
order_id, transaction_id, authorization, security_key = identification.split(';')
add_pair(post, :RelatedVendorTxCode, order_id)
@@ -267,10 +275,14 @@ def add_invoice(post, options)
end
def add_payment_method(post, payment_method, options)
- if payment_method.respond_to?(:number)
- add_credit_card(post, payment_method)
+ if payment_method.is_a?(String)
+ if past_purchase_reference?(payment_method)
+ add_related_reference(post, payment_method)
+ else
+ add_token_details(post, payment_method, options)
+ end
else
- add_token_details(post, payment_method, options)
+ add_credit_card(post, payment_method)
end
end
@@ -343,10 +355,10 @@ def commit(action, parameters)
:test => test?,
:authorization => authorization_from(response, parameters, action),
:avs_result => {
- :street_match => AVS_CVV_CODE[ response["AddressResult"] ],
- :postal_match => AVS_CVV_CODE[ response["PostCodeResult"] ],
+ :street_match => AVS_CODE[ response["AddressResult"] ],
+ :postal_match => AVS_CODE[ response["PostCodeResult"] ],
},
- :cvv_result => AVS_CVV_CODE[ response["CV2Result"] ]
+ :cvv_result => CVV_CODE[ response["CV2Result"] ]
)
end
@@ -356,9 +368,9 @@ def authorization_from(response, params, action)
response['Token']
else
[ params[:VendorTxCode],
- response["VPSTxId"],
+ response["VPSTxId"] || params[:VPSTxId],
response["TxAuthNo"],
- response["SecurityKey"],
+ response["SecurityKey"] || params[:SecurityKey],
action ].join(";")
end
end
@@ -419,6 +431,10 @@ def add_pair(post, key, value, options = {})
post[key] = value if !value.blank? || options[:required]
end
+ def past_purchase_reference?(payment_method)
+ return false unless payment_method.is_a?(String)
+ payment_method.split(';').last == 'purchase'
+ end
end
end
diff --git a/lib/active_merchant/billing/gateways/secure_net.rb b/lib/active_merchant/billing/gateways/secure_net.rb
index abf9d4ebccd..4c18c3b54a4 100644
--- a/lib/active_merchant/billing/gateways/secure_net.rb
+++ b/lib/active_merchant/billing/gateways/secure_net.rb
@@ -61,8 +61,19 @@ def credit(money, authorization, options = {})
refund(money, authorization, options)
end
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r(()\d+())i, '\1[FILTERED]\2').
+ gsub(%r(()\d+())i, '\1[FILTERED]\2').
+ gsub(%r(().+())i, '\1[FILTERED]\2')
+ end
private
+
def commit(request)
xml = build_request(request)
url = test? ? self.test_url : self.live_url
@@ -255,4 +266,3 @@ def build_authorization(response)
end
end
end
-
diff --git a/lib/active_merchant/billing/gateways/secure_pay_au.rb b/lib/active_merchant/billing/gateways/secure_pay_au.rb
index b2e3f20befe..0bf27eb522d 100644
--- a/lib/active_merchant/billing/gateways/secure_pay_au.rb
+++ b/lib/active_merchant/billing/gateways/secure_pay_au.rb
@@ -103,6 +103,18 @@ def unstore(identification, options = {})
commit_periodic(build_periodic_item(:remove_triggered, options[:amount], nil, options))
end
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r(().+()), '\1[FILTERED]\2').
+ gsub(%r(().+()), '\1[FILTERED]\2').
+ gsub(%r(().+()), '\1[FILTERED]\2').
+ gsub(%r(().+()), '\1[FILTERED]\2')
+ end
+
private
def build_purchase_request(money, credit_card, options)
diff --git a/lib/active_merchant/billing/gateways/securion_pay.rb b/lib/active_merchant/billing/gateways/securion_pay.rb
index bb977d54439..5a1048d95ed 100644
--- a/lib/active_merchant/billing/gateways/securion_pay.rb
+++ b/lib/active_merchant/billing/gateways/securion_pay.rb
@@ -5,8 +5,8 @@ class SecurionPayGateway < Gateway
self.live_url = 'https://api.securionpay.com/'
- self.supported_countries = %w(AL AD AT BY BE BG HR CY RE DK EE IS FI FR DE GI GR HU IS IE IT LV LI LT LU
- MK MT MD MC NL NO PL PT RO RU MA RS SK SI ES SE CH UA KI CI RS RS ME)
+ self.supported_countries = %w(AL AD AT BY BE BG HR CY CZ RE DK EE IS FI FR DE GI GR HU IS IE IT IL LV LI LT LU
+ MK MT MD MC NL NO PL PT RO RU MA RS SK SI ES SE CH UA GB KI CI ME)
self.default_currency = 'USD'
self.money_format = :cents
diff --git a/lib/active_merchant/billing/gateways/smart_ps.rb b/lib/active_merchant/billing/gateways/smart_ps.rb
index 5c03505e725..64b59c3f6fe 100644
--- a/lib/active_merchant/billing/gateways/smart_ps.rb
+++ b/lib/active_merchant/billing/gateways/smart_ps.rb
@@ -229,7 +229,7 @@ def parse(body)
end
def commit(action, money, parameters)
- parameters[:amount] = amount(money) if money
+ parameters[:amount] = localized_amount(money, parameters[:currency] || default_currency) if money
response = parse( ssl_post(self.live_url, post_data(action,parameters)) )
Response.new(response["response"] == "1", message_from(response), response,
:authorization => (response["transactionid"] || response["customer_vault_id"]),
diff --git a/lib/active_merchant/billing/gateways/so_easy_pay.rb b/lib/active_merchant/billing/gateways/so_easy_pay.rb
deleted file mode 100644
index 2227a74ebe4..00000000000
--- a/lib/active_merchant/billing/gateways/so_easy_pay.rb
+++ /dev/null
@@ -1,194 +0,0 @@
-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 e4c43db48c0..1aab6d811ce 100644
--- a/lib/active_merchant/billing/gateways/spreedly_core.rb
+++ b/lib/active_merchant/billing/gateways/spreedly_core.rb
@@ -44,7 +44,7 @@ def purchase(money, payment_method, options = {})
purchase_with_token(money, payment_method, options)
else
MultiResponse.run do |r|
- r.process { save_card(false, payment_method, options) }
+ r.process { save_card(options[:store], payment_method, options) }
r.process { purchase_with_token(money, r.authorization, options) }
end
end
@@ -62,7 +62,7 @@ def authorize(money, payment_method, options = {})
authorize_with_token(money, payment_method, options)
else
MultiResponse.run do |r|
- r.process { save_card(false, payment_method, options) }
+ r.process { save_card(options[:store], payment_method, options) }
r.process { authorize_with_token(money, r.authorization, options) }
end
end
@@ -88,6 +88,24 @@ def void(authorization, options={})
commit("transactions/#{authorization}/void.xml", '')
end
+
+ # Public: Determine whether a credit card is chargeable card and available for purchases.
+ #
+ # payment_method - The CreditCard or the Spreedly payment method token.
+ # options - A hash of options:
+ # :store - Retain the payment method if the verify
+ # succeeds. Defaults to false. (optional)
+ def verify(payment_method, options = {})
+ if payment_method.is_a?(String)
+ verify_with_token(payment_method, options)
+ else
+ MultiResponse.run do |r|
+ r.process { save_card(options[:store], payment_method, options) }
+ r.process { verify_with_token(r.authorization, options) }
+ end
+ end
+ end
+
# Public: Store a credit card in the Spreedly vault and retain it.
#
# credit_card - The CreditCard to store
@@ -106,6 +124,25 @@ def unstore(authorization, options={})
commit("payment_methods/#{authorization}/redact.xml", '', :put)
end
+ # Public: Get the transaction with the given token.
+ def find(transaction_token)
+ commit("transactions/#{transaction_token}.xml", nil, :get)
+ end
+
+ alias_method :status, :find
+
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
+ gsub(%r(().+()), '\1[FILTERED]\2').
+ gsub(%r(().+()), '\1[FILTERED]\2').
+ gsub(%r(().+()), '\1[FILTERED]\2')
+ end
+
private
def save_card(retain, credit_card, options)
@@ -128,10 +165,20 @@ def authorize_with_token(money, payment_method_token, options)
commit("gateways/#{@options[:gateway_token]}/authorize.xml", request)
end
+ def verify_with_token(payment_method_token, options)
+ request = build_xml_request('transaction') do |doc|
+ add_invoice(doc, nil, options)
+ doc.payment_method_token(payment_method_token)
+ doc.retain_on_success(true) if options[:store]
+ add_extra_options(:gateway_specific_fields, doc, options)
+ end
+
+ commit("gateways/#{@options[:gateway_token]}/verify.xml", request)
+ end
+
def auth_purchase_request(money, payment_method_token, options)
build_xml_request('transaction') do |doc|
add_invoice(doc, money, options)
- doc.ip(options[:ip])
add_extra_options(:gateway_specific_fields, doc, options)
doc.payment_method_token(payment_method_token)
doc.retain_on_success(true) if options[:store]
@@ -139,11 +186,11 @@ def auth_purchase_request(money, payment_method_token, options)
end
def add_invoice(doc, money, options)
- doc.amount amount(money)
+ doc.amount amount(money) unless money.nil?
doc.currency_code(options[:currency] || currency(money) || default_currency)
doc.order_id(options[:order_id])
- doc.ip(options[:ip])
- doc.description(options[:description])
+ doc.ip(options[:ip]) if options[:ip]
+ doc.description(options[:description]) if options[:description]
end
def add_credit_card(doc, credit_card, options)
@@ -244,4 +291,3 @@ def headers
end
end
end
-
diff --git a/lib/active_merchant/billing/gateways/stripe.rb b/lib/active_merchant/billing/gateways/stripe.rb
index 58d6d844d62..bb76ff26527 100644
--- a/lib/active_merchant/billing/gateways/stripe.rb
+++ b/lib/active_merchant/billing/gateways/stripe.rb
@@ -21,7 +21,7 @@ class StripeGateway < Gateway
'unchecked' => 'P'
}
- self.supported_countries = %w(AT AU BE CA CH DE DK ES FI FR GB IE IT LU NL NO SE SG US)
+ self.supported_countries = %w(AT AU BE BR CA CH DE DK ES FI FR GB HK IE IT JP LU MX NL NO NZ PT SE SG US)
self.default_currency = 'USD'
self.money_format = :cents
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club, :maestro]
@@ -43,7 +43,8 @@ class StripeGateway < Gateway
'call_issuer' => STANDARD_ERROR_CODE[:call_issuer],
'processing_error' => STANDARD_ERROR_CODE[:processing_error],
'incorrect_pin' => STANDARD_ERROR_CODE[:incorrect_pin],
- 'test_mode_live_card' => STANDARD_ERROR_CODE[:test_mode_live_card]
+ 'test_mode_live_card' => STANDARD_ERROR_CODE[:test_mode_live_card],
+ 'pickup_card' => STANDARD_ERROR_CODE[:pickup_card]
}
BANK_ACCOUNT_HOLDER_TYPE_MAPPING = {
@@ -51,11 +52,27 @@ class StripeGateway < Gateway
"business" => "company",
}
+ MINIMUM_AUTHORIZE_AMOUNTS = {
+ "USD" => 100,
+ "CAD" => 100,
+ "GBP" => 60,
+ "EUR" => 100,
+ "DKK" => 500,
+ "NOK" => 600,
+ "SEK" => 600,
+ "CHF" => 100,
+ "AUD" => 100,
+ "JPY" => 100,
+ "MXN" => 2000,
+ "SGD" => 100,
+ "HKD" => 800,
+ "PLN" => 200
+ }
+
def initialize(options = {})
requires!(options, :login)
@api_key = options[:login]
@fee_refund_api_key = options[:fee_refund_login]
-
super
end
@@ -97,6 +114,7 @@ def purchase(money, payment, options = {})
end
r.process do
post = create_post_for_auth_or_purchase(money, payment, options)
+ post[:card][:processing_method] = 'quick_chip' if quickchip_payment?(payment)
commit(:post, 'charges', post, options)
end
end.responses.last
@@ -111,6 +129,7 @@ def capture(money, authorization, options = {})
else
add_application_fee(post, options)
add_amount(post, money, options)
+ add_exchange_rate(post, options)
commit(:post, "charges/#{CGI.escape(authorization)}/capture", post, options)
end
end
@@ -133,25 +152,24 @@ def refund(money, identification, options = {})
MultiResponse.run(:first) do |r|
r.process { commit(:post, "charges/#{CGI.escape(identification)}/refunds", post, options) }
- return r unless options[:refund_fee_amount]
-
- r.process { fetch_application_fees(identification, options) }
- r.process { refund_application_fee(options[:refund_fee_amount], application_fee_from_response(r.responses.last), options) }
+ if options[:refund_fee_amount] && options[:refund_fee_amount].to_s != '0'
+ r.process { fetch_application_fee(identification, options) }
+ r.process { refund_application_fee(options[:refund_fee_amount].to_i, application_fee_from_response(r.responses.last), options) }
+ end
end
end
def verify(payment, options = {})
MultiResponse.run(:use_first_response) do |r|
- r.process { authorize(50, payment, options) }
+ r.process { authorize(auth_minimum_amount(options), payment, options) }
+ options[:idempotency_key] = nil
r.process(:ignore_result) { void(r.authorization, options) }
end
end
def application_fee_from_response(response)
return unless response.success?
-
- application_fees = response.params["data"].select { |fee| fee["object"] == "application_fee" }
- application_fees.first["id"] unless application_fees.empty?
+ response.params["application_fee"] unless response.params["application_fee"].empty?
end
def refund_application_fee(money, identification, options = {})
@@ -159,9 +177,11 @@ def refund_application_fee(money, identification, options = {})
post = {}
add_amount(post, money, options)
- options.merge!(:key => @fee_refund_api_key)
+ options.merge!(:key => @fee_refund_api_key) if @fee_refund_api_key
+ options.delete(:stripe_account)
- commit(:post, "application_fees/#{CGI.escape(identification)}/refund", post, options)
+ refund_fee = commit(:post, "application_fees/#{CGI.escape(identification)}/refunds", post, options)
+ application_fee_response!(refund_fee, "Application fee could not be refunded: #{refund_fee.message}")
end
# Note: creating a new credit card will not change the customer's existing default credit card (use :set_default => true)
@@ -255,13 +275,15 @@ def supports_scrubbing?
def scrub(transcript)
transcript.
gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
- gsub(%r((card\[number\]=)\d+), '\1[FILTERED]').
- gsub(%r((card\[cvc\]=)\d+), '\1[FILTERED]').
gsub(%r((&?three_d_secure\[cryptogram\]=)[\w=]*(&?)), '\1[FILTERED]\2').
- gsub(%r((card\[swipe_data\]=)[^&]+(&?)), '\1[FILTERED]\2').
+ gsub(%r((card\[cryptogram\]=)[^&]+(&?)), '\1[FILTERED]\2').
+ gsub(%r((card\[cvc\]=)\d+), '\1[FILTERED]').
+ gsub(%r((card\[emv_approval_data\]=)[^&]+(&?)), '\1[FILTERED]\2').
+ gsub(%r((card\[emv_auth_data\]=)[^&]+(&?)), '\1[FILTERED]\2').
gsub(%r((card\[encrypted_pin\]=)[^&]+(&?)), '\1[FILTERED]\2').
gsub(%r((card\[encrypted_pin_key_id\]=)[\w=]+(&?)), '\1[FILTERED]\2').
- gsub(%r((card\[emv_auth_data\]=)[^&]+(&?)), '\1[FILTERED]\2')
+ gsub(%r((card\[number\]=)\d+), '\1[FILTERED]').
+ gsub(%r((card\[swipe_data\]=)[^&]+(&?)), '\1[FILTERED]\2')
end
def supports_network_tokenization?
@@ -285,7 +307,10 @@ def create_post_for_auth_or_purchase(money, payment, options)
add_creditcard(post, payment, options)
end
- unless emv_payment?(payment)
+ if emv_payment?(payment)
+ add_statement_address(post, options)
+ add_emv_metadata(post, payment)
+ else
add_amount(post, money, options, true)
add_customer_data(post, options)
post[:description] = options[:description]
@@ -297,6 +322,7 @@ def create_post_for_auth_or_purchase(money, payment, options)
add_metadata(post, options)
add_application_fee(post, options)
+ add_exchange_rate(post, options)
add_destination(post, options)
post
end
@@ -311,8 +337,16 @@ def add_application_fee(post, options)
post[:application_fee] = options[:application_fee] if options[:application_fee]
end
+ def add_exchange_rate(post, options)
+ post[:exchange_rate] = options[:exchange_rate] if options[:exchange_rate]
+ end
+
def add_destination(post, options)
- post[:destination] = options[:destination] if options[:destination]
+ if options[:destination]
+ post[:destination] = {}
+ post[:destination][:account] = options[:destination]
+ post[:destination][:amount] = options[:destination_amount] if options[:destination_amount]
+ end
end
def add_expand_parameters(post, options)
@@ -347,12 +381,23 @@ def add_address(post, options)
end
end
+ def add_statement_address(post, options)
+ return unless statement_address = options[:statement_address]
+ return unless [:address1, :city, :zip, :state].all? { |key| statement_address[key].present? }
+
+ post[:statement_address] = {}
+ post[:statement_address][:line1] = statement_address[:address1]
+ post[:statement_address][:line2] = statement_address[:address2] if statement_address[:address2].present?
+ post[:statement_address][:city] = statement_address[:city]
+ post[:statement_address][:postal_code] = statement_address[:zip]
+ post[:statement_address][:state] = statement_address[:state]
+ end
+
def add_creditcard(post, creditcard, options)
card = {}
if emv_payment?(creditcard)
add_emv_creditcard(post, creditcard.icc_data)
- post[:card][:read_method] = "contactless" if creditcard.contactless_emv
- post[:card][:read_method] = "contactless_magstripe_mode" if creditcard.contactless_magstripe
+ post[:card][:read_method] = "contactless" if creditcard.read_method == 'contactless'
if creditcard.encrypted_pin_cryptogram.present? && creditcard.encrypted_pin_ksn.present?
post[:card][:encrypted_pin] = creditcard.encrypted_pin_cryptogram
post[:card][:encrypted_pin_key_id] = creditcard.encrypted_pin_ksn
@@ -360,9 +405,11 @@ def add_creditcard(post, creditcard, options)
elsif creditcard.respond_to?(:number)
if creditcard.respond_to?(:track_data) && creditcard.track_data.present?
card[:swipe_data] = creditcard.track_data
- card[:fallback_reason] = creditcard.fallback_reason if creditcard.fallback_reason
- card[:read_method] = "contactless" if creditcard.contactless_emv
- post[:read_method] = "contactless_magstripe_mode" if creditcard.contactless_magstripe
+ if creditcard.respond_to?(:read_method)
+ card[:fallback_reason] = 'no_chip' if creditcard.read_method == 'fallback_no_chip'
+ card[:fallback_reason] = 'chip_error' if creditcard.read_method == 'fallback_chip_error'
+ card[:read_method] = "contactless_magstripe_mode" if creditcard.read_method == 'contactless_magstripe'
+ end
else
card[:number] = creditcard.number
card[:exp_month] = creditcard.month
@@ -373,7 +420,7 @@ def add_creditcard(post, creditcard, options)
if creditcard.is_a?(NetworkTokenizationCreditCard)
card[:cryptogram] = creditcard.payment_cryptogram
- card[:eci] = creditcard.eci
+ card[:eci] = creditcard.eci.rjust(2, '0') if creditcard.eci =~ /^[0-9]+$/
card[:tokenization_method] = creditcard.source.to_s
end
post[:card] = card
@@ -414,16 +461,27 @@ def add_flags(post, options)
end
def add_metadata(post, options = {})
- post[:metadata] = options[:metadata] || {}
+ post[:metadata] ||= {}
+ post[:metadata].merge!(options[:metadata]) if options[:metadata]
post[:metadata][:email] = options[:email] if options[:email]
post[:metadata][:order_id] = options[:order_id] if options[:order_id]
post.delete(:metadata) if post[:metadata].empty?
end
- def fetch_application_fees(identification, options = {})
+ def add_emv_metadata(post, creditcard)
+ post[:metadata] ||= {}
+ post[:metadata][:card_read_method] = creditcard.read_method if creditcard.respond_to?(:read_method)
+ end
+
+ def fetch_application_fee(identification, options = {})
options.merge!(:key => @fee_refund_api_key)
- commit(:get, "application_fees?charge=#{identification}", nil, options)
+ fetch_charge = commit(:get, "charges/#{CGI.escape(identification)}", nil, options)
+ application_fee_response!(fetch_charge, "Application fee id could not be retrieved: #{fetch_charge.message}")
+ end
+
+ def application_fee_response!(response, message)
+ response.success? ? response : Response.new(false, message)
end
def parse(body)
@@ -454,10 +512,10 @@ def headers(options = {})
idempotency_key = options[:idempotency_key]
headers = {
- "Authorization" => "Basic " + Base64.encode64(key.to_s + ":").strip,
+ "Authorization" => "Basic " + Base64.strict_encode64(key.to_s + ":").strip,
"User-Agent" => "Stripe/v1 ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
"Stripe-Version" => api_version(options),
- "X-Stripe-Client-User-Agent" => user_agent,
+ "X-Stripe-Client-User-Agent" => stripe_client_user_agent(options),
"X-Stripe-Client-User-Metadata" => {:ip => options[:ip]}.to_json
}
headers.merge!("Idempotency-Key" => idempotency_key) if idempotency_key
@@ -465,6 +523,11 @@ def headers(options = {})
headers
end
+ def stripe_client_user_agent(options)
+ return user_agent unless options[:application]
+ JSON.dump(JSON.parse(user_agent).merge!({application: options[:application]}))
+ end
+
def api_version(options)
options[:version] || @options[:version] || "2015-04-07"
end
@@ -549,6 +612,10 @@ def emv_payment?(payment)
payment.respond_to?(:emv?) && payment.emv?
end
+ def quickchip_payment?(payment)
+ payment.respond_to?(:read_method) && payment.read_method == 'contact_quickchip'
+ end
+
def card_from_response(response)
response["card"] || response["active_card"] || response["source"] || {}
end
@@ -600,6 +667,11 @@ def ach?(payment_method)
card_brand(payment_method) == "check"
end
end
+
+ def auth_minimum_amount(options)
+ return 100 unless options[:currency]
+ return MINIMUM_AUTHORIZE_AMOUNTS[options[:currency].upcase] || 100
+ end
end
end
end
diff --git a/lib/active_merchant/billing/gateways/stripe_payment_intents.rb b/lib/active_merchant/billing/gateways/stripe_payment_intents.rb
new file mode 100644
index 00000000000..63b9440689a
--- /dev/null
+++ b/lib/active_merchant/billing/gateways/stripe_payment_intents.rb
@@ -0,0 +1,198 @@
+require 'active_support/core_ext/hash/slice'
+
+module ActiveMerchant #:nodoc:
+ module Billing #:nodoc:
+ class StripePaymentIntentsGateway < StripeGateway
+ ALLOWED_METHOD_STATES = %w[automatic manual].freeze
+ ALLOWED_CANCELLATION_REASONS = %w[duplicate fraudulent requested_by_customer abandoned].freeze
+ CREATE_INTENT_ATTRIBUTES = %i[description statement_descriptor receipt_email save_payment_method]
+ CONFIRM_INTENT_ATTRIBUTES = %i[receipt_email return_url save_payment_method setup_future_usage off_session]
+ UPDATE_INTENT_ATTRIBUTES = %i[description statement_descriptor receipt_email setup_future_usage]
+ DEFAULT_API_VERSION = '2019-05-16'
+
+ def create_intent(money, payment_method, options = {})
+ post = {}
+ add_amount(post, money, options, true)
+ add_capture_method(post, options)
+ add_confirmation_method(post, options)
+ add_customer(post, options)
+ add_payment_method_token(post, payment_method, options)
+ add_payment_method_options(post, options)
+ add_metadata(post, options)
+ add_return_url(post, options)
+ add_connected_account(post, options)
+ add_shipping_address(post, options)
+ setup_future_usage(post, options)
+
+ CREATE_INTENT_ATTRIBUTES.each do |attribute|
+ add_whitelisted_attribute(post, options, attribute)
+ end
+
+ commit(:post, 'payment_intents', post, options)
+ end
+
+ def authorize(money, payment_method, options = {})
+ create_intent(money, payment_method, options)
+ end
+
+ def show_intent(intent_id, options)
+ commit(:get, "payment_intents/#{intent_id}", nil, options)
+ end
+
+ def confirm_intent(intent_id, payment_method, options = {})
+ post = {}
+ add_payment_method_token(post, payment_method, options)
+ CONFIRM_INTENT_ATTRIBUTES.each do |attribute|
+ add_whitelisted_attribute(post, options, attribute)
+ end
+
+ commit(:post, "payment_intents/#{intent_id}/confirm", post, options)
+ end
+
+ def create_payment_method(payment_method, options = {})
+ post = {}
+ post[:type] = 'card'
+ post[:card] = {}
+ post[:card][:number] = payment_method.number
+ post[:card][:exp_month] = payment_method.month
+ post[:card][:exp_year] = payment_method.year
+ post[:card][:cvc] = payment_method.verification_value if payment_method.verification_value
+
+ commit(:post, 'payment_methods', post, options)
+ end
+
+ def capture(money, intent_id, options = {})
+ post = {}
+ post[:amount_to_capture] = money
+ add_connected_account(post, options)
+ commit(:post, "payment_intents/#{intent_id}/capture", post, options)
+ end
+
+ def update_intent(money, intent_id, payment_method, options = {})
+ post = {}
+ post[:amount] = money if money
+
+ add_payment_method_token(post, payment_method, options)
+ add_payment_method_types(post, options)
+ add_customer(post, options)
+ add_metadata(post, options)
+ add_shipping_address(post, options)
+ add_connected_account(post, options)
+
+ UPDATE_INTENT_ATTRIBUTES.each do |attribute|
+ add_whitelisted_attribute(post, options, attribute)
+ end
+
+ commit(:post, "payment_intents/#{intent_id}", post, options)
+ end
+
+ def void(intent_id, options = {})
+ post = {}
+ post[:cancellation_reason] = options[:cancellation_reason] if ALLOWED_CANCELLATION_REASONS.include?(options[:cancellation_reason])
+ commit(:post, "payment_intents/#{intent_id}/cancel", post, options)
+ end
+
+ def refund(money, intent_id, options = {})
+ intent = commit(:get, "payment_intents/#{intent_id}", nil, options)
+ charge_id = intent.params.dig('charges', 'data')[0].dig('id')
+ super(money, charge_id, options)
+ end
+
+ private
+
+ def add_whitelisted_attribute(post, options, attribute)
+ post[attribute] = options[attribute] if options[attribute]
+ post
+ end
+
+ def add_capture_method(post, options)
+ capture_method = options[:capture_method].to_s
+ post[:capture_method] = capture_method if ALLOWED_METHOD_STATES.include?(capture_method)
+ post
+ end
+
+ def add_confirmation_method(post, options)
+ confirmation_method = options[:confirmation_method].to_s
+ post[:confirmation_method] = confirmation_method if ALLOWED_METHOD_STATES.include?(confirmation_method)
+ post
+ end
+
+ def add_customer(post, options)
+ customer = options[:customer].to_s
+ post[:customer] = customer if customer.start_with?('cus_')
+ post
+ end
+
+ def add_return_url(post, options)
+ return unless options[:confirm]
+ post[:confirm] = options[:confirm]
+ post[:return_url] = options[:return_url] if options[:return_url]
+ post
+ end
+
+ def add_payment_method_token(post, payment_method, options)
+ return if payment_method.nil?
+
+ if payment_method.is_a?(ActiveMerchant::Billing::CreditCard)
+ p = create_payment_method(payment_method, options)
+ payment_method = p.params['id']
+ end
+
+ if payment_method.is_a?(StripePaymentToken)
+ post[:payment_method] = payment_method.payment_data['id']
+ elsif payment_method.is_a?(String)
+ post[:payment_method] = payment_method
+ end
+ end
+
+ def add_payment_method_options(post, options)
+ return if options[:payment_method_options].to_h.empty?
+
+ post[:payment_method_options] = options.fetch(:payment_method_options)
+ post
+ end
+
+ def add_payment_method_types(post, options)
+ payment_method_types = options[:payment_method_types] if options[:payment_method_types]
+ return if payment_method_types.nil?
+
+ post[:payment_method_types] = Array(payment_method_types)
+ post
+ end
+
+ def setup_future_usage(post, options = {})
+ post[:setup_future_usage] = options[:setup_future_usage] if %w( on_session off_session ).include?(options[:setup_future_usage])
+ post[:off_session] = options[:off_session] if options[:off_session] && options[:confirm] == true
+ end
+
+ def add_connected_account(post, options = {})
+ return unless transfer_data = options[:transfer_data]
+ post[:transfer_data] = {}
+ post[:transfer_data][:destination] = transfer_data[:destination] if transfer_data[:destination]
+ post[:transfer_data][:amount] = transfer_data[:amount] if transfer_data[:amount]
+ post[:on_behalf_of] = options[:on_behalf_of] if options[:on_behalf_of]
+ post[:transfer_group] = options[:transfer_group] if options[:transfer_group]
+ post[:application_fee_amount] = options[:application_fee] if options[:application_fee]
+ post
+ end
+
+ def add_shipping_address(post, options = {})
+ return unless shipping = options[:shipping]
+ post[:shipping] = {}
+ post[:shipping][:address] = {}
+ post[:shipping][:address][:line1] = shipping[:address][:line1]
+ post[:shipping][:address][:city] = shipping[:address][:city] if shipping[:address][:city]
+ post[:shipping][:address][:country] = shipping[:address][:country] if shipping[:address][:country]
+ post[:shipping][:address][:line2] = shipping[:address][:line2] if shipping[:address][:line2]
+ post[:shipping][:address][:postal_code] = shipping[:address][:postal_code] if shipping[:address][:postal_code]
+ post[:shipping][:address][:state] = shipping[:address][:state] if shipping[:address][:state]
+
+ post[:shipping][:name] = shipping[:name]
+ post[:shipping][:carrier] = shipping[:carrier] if shipping[:carrier]
+ post[:shipping][:phone] = shipping[:phone] if shipping[:phone]
+ post[:shipping][:tracking_number] = shipping[:tracking_number] if shipping[:tracking_number]
+ post
+ end
+ end
+ end
+end
diff --git a/lib/active_merchant/billing/gateways/tns.rb b/lib/active_merchant/billing/gateways/tns.rb
index e1568130a9e..0aa15904050 100644
--- a/lib/active_merchant/billing/gateways/tns.rb
+++ b/lib/active_merchant/billing/gateways/tns.rb
@@ -16,7 +16,6 @@ class TnsGateway < Gateway
self.supported_countries = %w(AR AU BR FR DE HK MX NZ SG GB US)
self.default_currency = 'USD'
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb, :maestro, :laser]
- self.ssl_version = :TLSv1
end
end
diff --git a/lib/active_merchant/billing/gateways/trans_first.rb b/lib/active_merchant/billing/gateways/trans_first.rb
index cc2c8c084bf..c79d5fab0b2 100644
--- a/lib/active_merchant/billing/gateways/trans_first.rb
+++ b/lib/active_merchant/billing/gateways/trans_first.rb
@@ -1,4 +1,5 @@
module ActiveMerchant #:nodoc:
+
module Billing #:nodoc:
class TransFirstGateway < Gateway
self.test_url = 'https://ws.cert.transfirst.com'
@@ -16,7 +17,7 @@ class TransFirstGateway < Gateway
ACTIONS = {
purchase: "CCSale",
purchase_echeck: "ACHDebit",
- refund: "CreditCardAutoRefundorVoid",
+ refund: "CreditCardCredit",
refund_echeck: "ACHVoidTransaction",
void: "CreditCardAutoRefundorVoid",
}
@@ -52,6 +53,7 @@ def refund(money, authorization, options={})
transaction_id, payment_type = split_authorization(authorization)
add_amount(post, money)
add_pair(post, :TransID, transaction_id)
+ add_pair(post, :RefID, options[:order_id], required: true)
commit((payment_type == "check" ? :refund_echeck : :refund), post)
end
@@ -169,7 +171,6 @@ def parse(data)
def commit(action, params)
response = parse(ssl_post(url(action), post_data(action, params)))
-
Response.new(
success_from(response),
message_from(response),
diff --git a/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb b/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb
index d31ecfb0c75..388ff8b575c 100644
--- a/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb
+++ b/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb
@@ -175,6 +175,10 @@ class TransFirstTransactionExpressGateway < Gateway
verify: 9,
+ purchase_echeck: 11,
+ refund_echeck: 16,
+ void_echeck: 16,
+
wallet_sale: 14,
}
@@ -187,7 +191,15 @@ def purchase(amount, payment_method, options={})
if credit_card?(payment_method)
action = :purchase
request = build_xml_transaction_request do |doc|
- add_payment_method(doc, payment_method)
+ add_credit_card(doc, payment_method)
+ add_contact(doc, payment_method.name, options)
+ add_amount(doc, amount)
+ add_order_number(doc, options)
+ end
+ elsif echeck?(payment_method)
+ action = :purchase_echeck
+ request = build_xml_transaction_request do |doc|
+ add_echeck(doc, payment_method)
add_contact(doc, payment_method.name, options)
add_amount(doc, amount)
add_order_number(doc, options)
@@ -207,7 +219,7 @@ def purchase(amount, payment_method, options={})
def authorize(amount, payment_method, options={})
if credit_card?(payment_method)
request = build_xml_transaction_request do |doc|
- add_payment_method(doc, payment_method)
+ add_credit_card(doc, payment_method)
add_contact(doc, payment_method.name, options)
add_amount(doc, amount)
end
@@ -243,15 +255,14 @@ def void(authorization, options={})
end
def refund(amount, authorization, options={})
- transaction_id = split_authorization(authorization)[1]
+ action, transaction_id = split_authorization(authorization)
request = build_xml_transaction_request do |doc|
- add_amount(doc, amount)
+ add_amount(doc, amount) unless action == 'purchase_echeck'
add_original_transaction_data(doc, transaction_id)
- add_order_number(doc, options)
end
- commit(:refund, request)
+ commit(refund_type(action), request)
end
def credit(amount, payment_method, options={})
@@ -265,7 +276,7 @@ def credit(amount, payment_method, options={})
def verify(credit_card, options={})
request = build_xml_transaction_request do |doc|
- add_payment_method(doc, credit_card)
+ add_credit_card(doc, credit_card)
add_contact(doc, credit_card.name, options)
end
@@ -287,7 +298,7 @@ def store(payment_method, options={})
add_customer_id(doc, customer_id)
doc["v1"].pmt do
doc["v1"].type 0 # add
- add_payment_method(doc, payment_method)
+ add_credit_card(doc, payment_method)
end
end
end
@@ -338,8 +349,8 @@ def commit(action, request)
response,
error_code: error_code_from(succeeded, response),
authorization: authorization_from(action, response),
- avs_result: AVSResult.new(code: response["AVSCode"]),
- cvv_result: CVVResult.new(response["CVV2Response"]),
+ avs_result: AVSResult.new(code: response["avsRslt"]),
+ cvv_result: CVVResult.new(response["secRslt"]),
test: test?
)
end
@@ -384,8 +395,9 @@ def message_from(succeeded, response)
message = RESPONSE_MESSAGES[code]
extended = EXTENDED_RESPONSE_MESSAGES[extended_code]
+ ach_response = response["achResponse"]
- [message, extended].compact.join('. ')
+ [message, extended, ach_response].compact.join('. ')
else
response["faultstring"]
end
@@ -402,7 +414,11 @@ def authorization_from(action, response)
# -- helper methods ----------------------------------------------------
def credit_card?(payment_method)
- payment_method.respond_to?(:number)
+ payment_method.respond_to?(:verification_value)
+ end
+
+ def echeck?(payment_method)
+ payment_method.respond_to?(:routing_number)
end
def split_authorization(authorization)
@@ -410,7 +426,11 @@ def split_authorization(authorization)
end
def void_type(action)
- :"void_#{action}"
+ action == 'purchase_echeck' ? :void_echeck : :"void_#{action}"
+ end
+
+ def refund_type(action)
+ action == 'purchase_echeck' ? :refund_echeck : :refund
end
# -- request methods ---------------------------------------------------
@@ -483,14 +503,21 @@ def add_order_number(doc, options)
}
end
- def add_payment_method(doc, payment_method)
+ def add_credit_card(doc, payment_method)
doc["v1"].card {
doc["v1"].pan payment_method.number
- doc["v1"].sec payment_method.verification_value
+ doc["v1"].sec payment_method.verification_value if payment_method.verification_value?
doc["v1"].xprDt expiration_date(payment_method)
}
end
+ def add_echeck(doc, payment_method)
+ doc["v1"].achEcheck {
+ doc["v1"].bankRtNr payment_method.routing_number
+ doc["v1"].acctNr payment_method.account_number
+ }
+ end
+
def expiration_date(payment_method)
yy = format(payment_method.year, :two_digits)
mm = format(payment_method.month, :two_digits)
@@ -510,15 +537,17 @@ def add_contact(doc, fullname, options)
doc["v1"].title options[:title] if options[:title]
if (billing_address = options[:billing_address])
- doc["v1"].phone do
- doc["v1"].type (options[:phone_number_type] || "4")
- doc["v1"].nr billing_address[:phone].gsub(/\D/, '') if billing_address[:phone]
+ if billing_address[:phone]
+ doc["v1"].phone do
+ doc["v1"].type (options[:phone_number_type] || "4")
+ doc["v1"].nr billing_address[:phone].gsub(/\D/, '')
+ end
end
- doc["v1"].addrLn1 billing_address[:address1]
- doc["v1"].addrLn2 billing_address[:address2]
- doc["v1"].city billing_address[:city]
- doc["v1"].state billing_address[:state]
- doc["v1"].zipCode billing_address[:zip]
+ doc["v1"].addrLn1 billing_address[:address1] if billing_address[:address1]
+ doc["v1"].addrLn2 billing_address[:address2] if billing_address[:address2]
+ doc["v1"].city billing_address[:city] if billing_address[:city]
+ doc["v1"].state billing_address[:state] if billing_address[:state]
+ doc["v1"].zipCode billing_address[:zip] if billing_address[:zip]
doc["v1"].ctry "US"
end
@@ -529,11 +558,11 @@ def add_contact(doc, fullname, options)
if (shipping_address = options[:shipping_address])
doc["v1"].ship do
doc["v1"].fullName fullname
- doc["v1"].addrLn1 shipping_address[:address1]
+ doc["v1"].addrLn1 shipping_address[:address1] if shipping_address[:address1]
doc["v1"].addrLn2 shipping_address[:address2] if shipping_address[:address2]
- doc["v1"].city shipping_address[:city]
- doc["v1"].state shipping_address[:state]
- doc["v1"].zipCode shipping_address[:zip]
+ doc["v1"].city shipping_address[:city] if shipping_address[:city]
+ doc["v1"].state shipping_address[:state] if shipping_address[:state]
+ doc["v1"].zipCode shipping_address[:zip] if shipping_address[:zip]
doc["v1"].phone shipping_address[:phone].gsub(/\D/, '') if shipping_address[:phone]
doc["v1"].email shipping_address[:email] if shipping_address[:email]
end
@@ -541,6 +570,12 @@ def add_contact(doc, fullname, options)
end
end
+ def add_name(doc, payment_method)
+ doc["v1"].contact do
+ doc["v1"].fullName payment_method.name
+ end
+ end
+
def add_original_transaction_data(doc, authorization)
doc["v1"].origTranData do
doc["v1"].tranNr authorization
diff --git a/lib/active_merchant/billing/gateways/trexle.rb b/lib/active_merchant/billing/gateways/trexle.rb
new file mode 100644
index 00000000000..e50c72d481c
--- /dev/null
+++ b/lib/active_merchant/billing/gateways/trexle.rb
@@ -0,0 +1,217 @@
+module ActiveMerchant #:nodoc:
+ module Billing #:nodoc:
+ class TrexleGateway < Gateway
+ self.test_url = 'https://core.trexle.com/api/v1'
+ self.live_url = 'https://core.trexle.com/api/v1'
+
+ self.default_currency = 'USD'
+ self.money_format = :cents
+ self.supported_countries = %w(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)
+ self.supported_cardtypes = [:visa, :master, :american_express]
+ self.homepage_url = 'https://trexle.com'
+ self.display_name = 'Trexle'
+
+ def initialize(options = {})
+ requires!(options, :api_key)
+ super
+ end
+
+ # Create a charge using a credit card, card token or customer token
+ #
+ # To charge a credit card: purchase([money], [creditcard hash], ...)
+ # To charge a customer: purchase([money], [token], ...)
+ def purchase(money, creditcard, options = {})
+ post = {}
+
+ add_amount(post, money, options)
+ add_customer_data(post, options)
+ add_invoice(post, options)
+ add_creditcard(post, creditcard)
+ add_address(post, creditcard, options)
+ commit(:post, 'charges', post, options)
+ end
+
+ # Create a customer and associated credit card. The token that is returned
+ # can be used instead of a credit card parameter in the purchase method
+ def store(creditcard, options = {})
+ post = {}
+
+ add_creditcard(post, creditcard)
+ add_customer_data(post, options)
+ add_address(post, creditcard, options)
+ commit(:post, 'customers', post, options)
+ end
+
+ # Refund a transaction
+ def refund(money, token, options = {})
+ commit(:post, "charges/#{CGI.escape(token)}/refunds", { amount: amount(money) }, options)
+ end
+
+ # Authorize an amount on a credit card. Once authorized, you can later
+ # capture this charge using the charge token that is returned.
+ def authorize(money, creditcard, options = {})
+ post = {}
+
+ add_amount(post, money, options)
+ add_customer_data(post, options)
+ add_invoice(post, options)
+ add_creditcard(post, creditcard)
+ add_address(post, creditcard, options)
+ post[:capture] = false
+ commit(:post, 'charges', post, options)
+ end
+
+ # Captures a previously authorized charge. Capturing only part of the original
+ # authorization is currently not supported.
+ def capture(money, token, options = {})
+ commit(:put, "charges/#{CGI.escape(token)}/capture", { amount: amount(money) }, options)
+ end
+
+ # Updates the credit card for the customer.
+ def update(token, creditcard, options = {})
+ post = {}
+
+ add_creditcard(post, creditcard)
+ add_customer_data(post, options)
+ add_address(post, creditcard, options)
+ commit(:put, "customers/#{CGI.escape(token)}", post, options)
+ end
+
+ def supports_scrubbing
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
+ gsub(/(number\\?":\\?")(\d*)/, '\1[FILTERED]').
+ gsub(/(cvc\\?":\\?")(\d*)/, '\1[FILTERED]')
+ end
+ private
+
+ def add_amount(post, money, options)
+ post[:amount] = amount(money)
+ post[:currency] = (options[:currency] || currency(money))
+ post[:currency] = post[:currency].upcase if post[:currency]
+ end
+
+ def add_customer_data(post, options)
+ post[:email] = options[:email] if options[:email]
+ post[:ip_address] = options[:ip] if options[:ip]
+ end
+
+ def add_address(post, creditcard, options)
+ return if creditcard.kind_of?(String)
+ address = (options[:billing_address] || options[:address])
+ return unless address
+
+ post[:card] ||= {}
+ post[:card].merge!(
+ address_line1: address[:address1],
+ address_line2: address[:address_line2],
+ address_city: address[:city],
+ address_postcode: address[:zip],
+ address_state: address[:state],
+ address_country: address[:country]
+ )
+ end
+
+ def add_invoice(post, options)
+ post[:description] = options[:description] || "Active Merchant Purchase"
+ end
+
+ def add_creditcard(post, creditcard)
+ if creditcard.respond_to?(:number)
+ post[:card] ||= {}
+
+ post[:card].merge!(
+ number: creditcard.number,
+ expiry_month: creditcard.month,
+ expiry_year: creditcard.year,
+ cvc: creditcard.verification_value,
+ name: creditcard.name
+ )
+ elsif creditcard.kind_of?(String)
+ if creditcard =~ /^token_/
+ post[:card_token] = creditcard
+ else
+ post[:customer_token] = creditcard
+ end
+ end
+ end
+
+ def headers(params = {})
+ result = {
+ "Content-Type" => "application/json",
+ "Authorization" => "Basic #{Base64.strict_encode64(options[:api_key] + ':').strip}"
+ }
+
+ result['X-Partner-Key'] = params[:partner_key] if params[:partner_key]
+ result['X-Safe-Card'] = params[:safe_card] if params[:safe_card]
+ result
+ end
+
+ def commit(method, action, params, options)
+ url = "#{test? ? test_url : live_url}/#{action}"
+ raw_response = ssl_request(method, url, post_data(params), headers(options))
+ parsed_response = parse(raw_response)
+ success_response(parsed_response)
+ rescue ResponseError => e
+ error_response(parse(e.response.body))
+ rescue JSON::ParserError
+ unparsable_response(raw_response)
+ end
+
+ def success_response(body)
+ return invalid_response unless body['response']
+
+ response = body['response']
+ Response.new(
+ true,
+ response['status_message'],
+ body,
+ authorization: token(response),
+ test: test?
+ )
+ end
+
+ def error_response(body)
+ return invalid_response unless body['error']
+ Response.new(
+ false,
+ body['error'],
+ body,
+ authorization: nil,
+ test: test?
+ )
+ end
+
+ def unparsable_response(raw_response)
+ message = "Invalid JSON response received from Trexle. Please contact support@trexle.com if you continue to receive this message."
+ message += " (The raw response returned by the API was #{raw_response.inspect})"
+ return Response.new(false, message)
+ end
+
+ def invalid_response
+ message = "Invalid response."
+ return Response.new(false, message)
+ end
+
+ def token(response)
+ response['token']
+ end
+
+ def parse(body)
+ return {} if body.blank?
+ JSON.parse(body)
+ end
+
+ def post_data(parameters = {})
+ parameters.to_json
+ 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 497f4b54e5d..627d2de95d2 100644
--- a/lib/active_merchant/billing/gateways/usa_epay_advanced.rb
+++ b/lib/active_merchant/billing/gateways/usa_epay_advanced.rb
@@ -77,12 +77,14 @@ class UsaEpayAdvancedGateway < Gateway
self.homepage_url = 'http://www.usaepay.com/'
self.display_name = 'USA ePay Advanced SOAP Interface'
- CUSTOMER_OPTIONS = {
+ CUSTOMER_PROFILE_OPTIONS = {
:id => [:string, 'CustomerID'], # merchant assigned number
:notes => [:string, 'Notes'],
:data => [:string, 'CustomData'],
- :url => [:string, 'URL'],
- # Recurring Billing
+ :url => [:string, 'URL']
+ } #:nodoc:
+
+ CUSTOMER_RECURRING_BILLING_OPTIONS = {
:enabled => [:boolean, 'Enabled'],
:schedule => [:string, 'Schedule'],
:number_left => [:integer, 'NumLeft'],
@@ -92,18 +94,24 @@ class UsaEpayAdvancedGateway < Gateway
:user => [:string, 'User'],
:source => [:string, 'Source'],
:send_receipt => [:boolean, 'SendReceipt'],
- :receipt_note => [:string, 'ReceiptNote'],
- # Point of Sale
+ :receipt_note => [:string, 'ReceiptNote']
+ } #:nodoc:
+
+ CUSTOMER_POINT_OF_SALE_OPTIONS = {
:price_tier => [:string, 'PriceTier'],
:tax_class => [:string, 'TaxClass'],
:lookup_code => [:string, 'LookupCode']
} #:nodoc:
- ADDRESS_OPTIONS = {
+ CUSTOMER_OPTIONS = [
+ CUSTOMER_PROFILE_OPTIONS,
+ CUSTOMER_RECURRING_BILLING_OPTIONS,
+ CUSTOMER_POINT_OF_SALE_OPTIONS
+ ].inject(:merge) #:nodoc:
+
+ COMMON_ADDRESS_OPTIONS = {
:first_name => [:string, 'FirstName'],
:last_name => [:string, 'LastName'],
- :address1 => [:string, 'Street'],
- :address2 => [:string, 'Street2'],
:city => [:string, 'City'],
:state => [:string, 'State'],
:zip => [:string, 'Zip'],
@@ -114,6 +122,32 @@ class UsaEpayAdvancedGateway < Gateway
:company => [:string, 'Company']
} #:nodoc:
+ ADDRESS_OPTIONS = [
+ COMMON_ADDRESS_OPTIONS,
+ {
+ :address1 => [:string, 'Street'],
+ :address2 => [:string, 'Street2'],
+ }
+ ].inject(:merge) #:nodoc
+
+ CUSTOMER_UPDATE_DATA_FIELDS = [
+ CUSTOMER_PROFILE_OPTIONS,
+ CUSTOMER_RECURRING_BILLING_OPTIONS,
+ COMMON_ADDRESS_OPTIONS,
+ {
+ :address1 => [:string, 'Address'],
+ :address2 => [:string, 'Address2'],
+ },
+ {
+ :card_number => [:string, 'CardNumber'],
+ :card_exp => [:string, 'CardExp'],
+ :account => [:string, 'Account'],
+ :routing => [:string, 'Routing'],
+ :check_format => [:string, 'CheckFormat'],
+ :record_type => [:string, 'RecordType'],
+ }
+ ].inject(:merge) #:nodoc
+
CUSTOMER_TRANSACTION_REQUEST_OPTIONS = {
:command => [:string, 'Command'],
:ignore_duplicate => [:boolean, 'IgnoreDuplicate'],
@@ -354,6 +388,55 @@ def update_customer(options={})
commit(__method__, request)
end
+ # Update a customer by replacing only the provided fields.
+ #
+ # ==== Required
+ # * :customer_number -- customer to update
+ # * :update_data -- FieldValue array of fields to retrieve
+ # * :first_name
+ # * :last_name
+ # * :id
+ # * :company
+ # * :address
+ # * :address2
+ # * :city
+ # * :state
+ # * :zip
+ # * :country
+ # * :phone
+ # * :fax
+ # * :email
+ # * :url
+ # * :receipt_note
+ # * :send_receipt
+ # * :notes
+ # * :description
+ # * :order_id
+ # * :enabled
+ # * :schedule
+ # * :next
+ # * :num_left
+ # * :amount
+ # * :custom_data
+ # * :source
+ # * :user
+ # * :card_number
+ # * :card_exp
+ # * :account
+ # * :routing
+ # * :check_format or :record_type
+ #
+ # ==== Response
+ # * #message -- boolean; Returns true if successful. Exception thrown all failures.
+ #
+ def quick_update_customer(options={})
+ requires! options, :customer_number
+ requires! options, :update_data
+
+ request = build_request(__method__, options)
+ commit(__method__, request)
+ end
+
# Enable a customer for recurring billing.
#
# Note: Customer does not need to have all recurring parameters to succeed.
@@ -1019,6 +1102,14 @@ def build_delete_customer(soap, options)
build_customer(soap, options, 'deleteCustomer')
end
+ def build_quick_update_customer(soap, options)
+ soap.tag! "ns1:quickUpdateCustomer" do
+ build_token soap, options
+ build_tag soap, :integer, 'CustNum', options[:customer_number]
+ build_field_value_array soap, "UpdateData", "FieldValue", options[:update_data], CUSTOMER_UPDATE_DATA_FIELDS
+ end
+ end
+
def build_add_customer_payment_method(soap, options)
soap.tag! "ns1:addCustomerPaymentMethod" do
build_token soap, options
@@ -1408,6 +1499,21 @@ def build_shipping_address(soap, options)
end
end
+ def build_field_value_array(soap, tag_name, type, custom_data, fields)
+ soap.tag! tag_name, 'SOAP-ENC:arryType' => "xsd:#{type}[#{options.length}]", 'xsi:type' => "ns1:#{type}Array" do
+ custom_data.each do |k, v|
+ build_field_value soap, fields[k][1], v, fields[k][0] if fields.keys.include? k
+ end
+ end
+ end
+
+ def build_field_value(soap, field, value, value_type)
+ soap.FieldValue 'xsi:type' => 'ns1:FieldValue' do
+ build_tag soap, :string, 'Field', field
+ build_tag soap, value_type, 'Value', value
+ end
+ end
+
def build_line_items(soap, options) # TODO
end
@@ -1511,4 +1617,3 @@ def parse_element(node)
end
end
end
-
diff --git a/lib/active_merchant/billing/gateways/usa_epay_transaction.rb b/lib/active_merchant/billing/gateways/usa_epay_transaction.rb
index 65cde2fe40d..27acbf6c0c5 100644
--- a/lib/active_merchant/billing/gateways/usa_epay_transaction.rb
+++ b/lib/active_merchant/billing/gateways/usa_epay_transaction.rb
@@ -16,7 +16,8 @@ class UsaEpayTransactionGateway < Gateway
:capture => 'cc:capture',
:refund => 'cc:refund',
:void => 'cc:void',
- :void_release => 'cc:void:release'
+ :void_release => 'cc:void:release',
+ :check_purchase => 'check:sale'
}
STANDARD_ERROR_CODE_MAPPING = {
@@ -48,7 +49,7 @@ def authorize(money, credit_card, options = {})
add_amount(post, money)
add_invoice(post, options)
- add_credit_card(post, credit_card)
+ add_payment(post, credit_card)
unless credit_card.track_data.present?
add_address(post, credit_card, options)
add_customer_data(post, options)
@@ -59,20 +60,20 @@ def authorize(money, credit_card, options = {})
commit(:authorization, post)
end
- def purchase(money, credit_card, options = {})
+ def purchase(money, payment, options = {})
post = {}
add_amount(post, money)
add_invoice(post, options)
- add_credit_card(post, credit_card)
- unless credit_card.track_data.present?
- add_address(post, credit_card, options)
+ add_payment(post, payment)
+ unless payment.respond_to?(:track_data) && payment.track_data.present?
+ add_address(post, payment, options)
add_customer_data(post, options)
end
add_split_payments(post, options)
add_test_mode(post, options)
- commit(:purchase, post)
+ payment.respond_to?(:routing_number) ? commit(:check_purchase, post) : commit(:purchase, post)
end
def capture(money, authorization, options = {})
@@ -106,6 +107,19 @@ def void(authorization, options = {})
commit(command, post)
end
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r((&?UMcard=)\d*(&?))i, '\1[FILTERED]\2').
+ gsub(%r((&?UMcvv2=)\d*(&?))i, '\1[FILTERED]\2').
+ gsub(%r((&?UMmagstripe=)[^&]*)i, '\1[FILTERED]\2').
+ gsub(%r((&?UMaccount=)[^&]*)i, '\1[FILTERED]').
+ gsub(%r((&?UMkey=)[^&]*)i, '\1[FILTERED]')
+ end
+
private
def add_amount(post, money)
@@ -138,19 +152,19 @@ def add_customer_data(post, options)
end
end
- def add_address(post, credit_card, options)
+ def add_address(post, payment, options)
billing_address = options[:billing_address] || options[:address]
- add_address_for_type(:billing, post, credit_card, billing_address) if billing_address
- add_address_for_type(:shipping, post, credit_card, options[:shipping_address]) if options[:shipping_address]
+ add_address_for_type(:billing, post, payment, billing_address) if billing_address
+ add_address_for_type(:shipping, post, payment, options[:shipping_address]) if options[:shipping_address]
end
- def add_address_for_type(type, post, credit_card, address)
+ def add_address_for_type(type, post, payment, address)
prefix = address_key_prefix(type)
first_name, last_name = split_names(address[:name])
- post[address_key(prefix, 'fname')] = first_name.blank? && last_name.blank? ? credit_card.first_name : first_name
- post[address_key(prefix, 'lname')] = first_name.blank? && last_name.blank? ? credit_card.last_name : last_name
+ post[address_key(prefix, 'fname')] = first_name.blank? && last_name.blank? ? payment.first_name : first_name
+ post[address_key(prefix, 'lname')] = first_name.blank? && last_name.blank? ? payment.last_name : 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?
@@ -177,16 +191,20 @@ def add_invoice(post, options)
post[:description] = options[:description]
end
- def add_credit_card(post, credit_card)
- if credit_card.track_data.present?
- post[:magstripe] = credit_card.track_data
+ def add_payment(post, payment)
+ if payment.respond_to?(:routing_number)
+ post[:account] = payment.account_number
+ post[:routing] = payment.routing_number
+ post[:name] = payment.name unless payment.name.blank?
+ elsif payment.respond_to?(:track_data) && payment.track_data.present?
+ post[:magstripe] = payment.track_data
post[:cardpresent] = true
else
- 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 unless credit_card.name.blank?
- post[:cardpresent] = true if credit_card.manual_entry
+ post[:card] = payment.number
+ post[:cvv2] = payment.verification_value if payment.verification_value?
+ post[:expir] = expdate(payment)
+ post[:name] = payment.name unless payment.name.blank?
+ post[:cardpresent] = true if payment.manual_entry
end
end
diff --git a/lib/active_merchant/billing/gateways/vacaypay.rb b/lib/active_merchant/billing/gateways/vacaypay.rb
new file mode 100644
index 00000000000..15c6608118e
--- /dev/null
+++ b/lib/active_merchant/billing/gateways/vacaypay.rb
@@ -0,0 +1,343 @@
+module ActiveMerchant #:nodoc:
+ module Billing #:nodoc:
+ class VacaypayGateway < Gateway
+ class_attribute :stripe_live_url
+
+ self.stripe_live_url = 'https://api.stripe.com/v1/'
+ self.live_url = 'https://www.procuro.io/api/v1/vacay-pay/'
+
+ # The homepage URL of the gateway
+ self.homepage_url = 'https://www.procuro.io/vacay-pay'
+
+ # The name of the gateway
+ self.display_name = 'VacayPay'
+
+ # Money is referenced in dollars
+ self.money_format = :dollars
+ self.default_currency = 'USD'
+
+ # The countries the gateway supports merchants from as 2 digit ISO country codes
+ self.supported_countries = %w(AU CA GB US BE DK FI FR DE NL NO ES IT IE)
+
+ # The card types supported by the payment gateway
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club, :maestro]
+
+ STANDARD_ERROR_CODE_MAPPING = {
+ 'incorrect_number' => STANDARD_ERROR_CODE[:incorrect_number],
+ 'invalid_number' => STANDARD_ERROR_CODE[:invalid_number],
+ 'invalid_expiry_month' => STANDARD_ERROR_CODE[:invalid_expiry_date],
+ 'invalid_expiry_year' => STANDARD_ERROR_CODE[:invalid_expiry_date],
+ 'invalid_cvc' => STANDARD_ERROR_CODE[:invalid_cvc],
+ 'expired_card' => STANDARD_ERROR_CODE[:expired_card],
+ 'incorrect_cvc' => STANDARD_ERROR_CODE[:incorrect_cvc],
+ 'incorrect_zip' => STANDARD_ERROR_CODE[:incorrect_zip],
+ 'card_declined' => STANDARD_ERROR_CODE[:card_declined],
+ 'call_issuer' => STANDARD_ERROR_CODE[:call_issuer],
+ 'processing_error' => STANDARD_ERROR_CODE[:processing_error],
+ 'incorrect_pin' => STANDARD_ERROR_CODE[:incorrect_pin],
+ 'test_mode_live_card' => STANDARD_ERROR_CODE[:test_mode_live_card]
+ }
+
+ RESPONSE_API_UNAUTHORIZED = 4
+
+ def initialize(options={})
+ requires!(options, :api_key, :account_uuid)
+
+ @api_key = options[:api_key]
+ @account_uuid = options[:account_uuid]
+ @publishable_key = options[:publishable_key]
+
+ super
+ end
+
+ def purchase(money, payment_method, options={})
+ post = {}
+
+ store_response = store(payment_method, options)
+ return store_response unless store_response.success?
+
+ add_payment_method(post, store_response)
+ add_invoice(post, money, options)
+ add_address(post, payment_method, options)
+ add_customer_data(post, options)
+ add_settings(post, options)
+
+ commit('charge', post)
+ end
+
+ def authorize(money, payment_method, options={})
+ post = { :authorize => true }
+
+ store_response = store(payment_method, options)
+ return store_response unless store_response.success?
+
+ add_payment_method(post, store_response)
+ add_invoice(post, money, options)
+ add_address(post, payment_method, options)
+ add_customer_data(post, options)
+ add_settings(post, options)
+
+ commit('authorize', post)
+ end
+
+ def capture(money, authorization, options={})
+ options[:payment_uuid] = authorization
+ options[:amount] = amount(money)
+
+ commit('capture', options)
+ end
+
+ def refund(money, authorization, options={})
+ options[:payment_uuid] = authorization
+ options[:amount] = amount(money)
+
+ commit('refund', options)
+ end
+
+ def void(authorization, options={})
+ options[:payment_uuid] = authorization
+
+ commit('void', options)
+ end
+
+ def store(payment_method, options={})
+ card = {
+ :number => payment_method.number,
+ :cvc => payment_method.verification_value,
+ :exp_month => format(payment_method.month, :two_digits),
+ :exp_year => format(payment_method.year, :two_digits)
+ }
+
+ stripe_commit('tokens', { :card => card })
+ end
+
+ def verify(credit_card, options={})
+ MultiResponse.run(:use_first_response) do |r|
+ r.process { authorize(100, credit_card, options) }
+ r.process(:ignore_result) { void(r.authorization, options) }
+ end
+ end
+
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r(((?:\r\n)?X-Auth-Token: )[^\\]*), '\1[FILTERED]').
+ gsub(%r("number\\?":\\?"[0-9]*\\?"), '\1[FILTERED]').
+ gsub(%r("cvv\\?":\\?"[0-9]*\\?"), '\1[FILTERED]').
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
+ gsub(%r((Authorization: Bearer )\w+), '\1[FILTERED]').
+ gsub(%r((card\[number\]=)\d+), '\1[FILTERED]').
+ gsub(%r((card\[cvc\]=)\d+), '\1[FILTERED]')
+ end
+
+ private
+
+ def add_customer_data(post, options)
+ post[:email] = options[:email].to_s
+ post[:firstName] = options[:first_name].to_s
+ post[:lastName] = options[:last_name].to_s
+ post[:description] = options[:description].to_s
+ post[:externalPaymentReference] = options[:order_id].to_s
+ post[:externalBookingReference] = options[:external_booking_reference].to_s
+ post[:accessingIp] = options[:ip]
+ post[:notes] = options[:notes].to_s
+ post[:metadata] = options[:metadata].to_h
+ end
+
+ def add_address(post, creditcard, options)
+ address = options[:billing_address] || options[:address]
+ if address
+ post[:billingLine1] = address[:address1] if address[:address1]
+ post[:billingLine2] = address[:address2] if address[:address2]
+ post[:billingPostcode] = address[:zip] if address[:zip]
+ post[:billingRegion] = address[:state] if address[:state]
+ post[:billingTown] = address[:city] if address[:city]
+ post[:billingCountry] = address[:country] if address[:country]
+ end
+ end
+
+ def add_invoice(post, money, options)
+ post[:amount] = amount(money)
+ post[:currency] = (options[:currency] || currency(money))
+ end
+
+ def add_payment_method(post, token_response)
+ post[:cardToken] = token_response.authorization
+ end
+
+ def add_settings(post, options)
+ post[:sendEmailConfirmation] = options[:send_email_confirmation] # Defaults to false
+ end
+
+ def parse(body)
+ if body.nil?
+ {}
+ else
+ JSON.parse(body)
+ end
+ end
+
+ def stripe_commit(endpoint, parameters)
+ url = "#{self.stripe_live_url}#{endpoint}"
+
+ begin
+ response = parse(ssl_post(url, stripe_post_data(parameters), stripe_headers))
+ rescue ActiveMerchant::ResponseError => e
+ response = parse(e.response.body)
+ end
+
+ Response.new(
+ stripe_success_from(response),
+ stripe_message_from(response),
+ response,
+ authorization: stripe_authorization_from(response),
+ test: test?,
+ error_code: stripe_error_code_from(response)
+ )
+ end
+
+ def stripe_post_data(params = {})
+ return nil unless params
+
+ params.map do |key, value|
+ next if value != false && value.blank?
+ if value.is_a?(Hash)
+ h = {}
+ value.each do |k, v|
+ h["#{key}[#{k}]"] = v unless v.blank?
+ end
+ stripe_post_data(h)
+ elsif value.is_a?(Array)
+ value.map { |v| "#{key}[]=#{CGI.escape(v.to_s)}" }.join("&")
+ else
+ "#{key}=#{CGI.escape(value.to_s)}"
+ end
+ end.compact.join("&")
+ end
+
+ def stripe_headers
+ {
+ 'Authorization' => 'Bearer ' + publishable_key,
+ 'Content-Type' => 'application/x-www-form-urlencoded'
+ }
+ end
+
+ def stripe_success_from(response)
+ !response.key?('error')
+ end
+
+ def stripe_message_from(response)
+ stripe_success_from(response) ? 'Succeeded' : response['error']['message']
+ end
+
+ def stripe_authorization_from(response)
+ response['id']
+ end
+
+ def stripe_error_code_from(response)
+ return nil if stripe_success_from(response)
+
+ code = response['error']['code']
+ decline_code = response['error']['decline_code'] if code == 'card_declined'
+
+ error_code = STANDARD_ERROR_CODE_MAPPING[decline_code]
+ error_code ||= STANDARD_ERROR_CODE_MAPPING[code]
+ error_code
+ end
+
+ def commit(endpoint, parameters)
+ url = get_url(endpoint, parameters)
+
+ begin
+ response = parse(ssl_post(url, post_data(parameters), headers))
+ rescue ActiveMerchant::ResponseError => e
+ response = parse(e.response.body)
+ end
+
+ Response.new(
+ success_from(response),
+ message_from(response),
+ response,
+ authorization: authorization_from(response),
+ test: test?,
+ error_code: error_code_from(response)
+ )
+ end
+
+ def get_url(action, parameters={})
+ # Set uuid to 0 as we get a route not found (404) when account_uuid empty - this will return the expected 401
+ account_uuid = @account_uuid.to_s.empty? ? '0' : @account_uuid.to_s
+
+ if action == 'charge' || action == 'authorize'
+ return "#{self.live_url}accounts/#{account_uuid}/payments"
+ elsif action == 'capture'
+ return "#{self.live_url}accounts/#{account_uuid}/payments/#{parameters[:payment_uuid]}/capture"
+ elsif action == 'refund' || action == 'void'
+ return "#{self.live_url}accounts/#{account_uuid}/payments/#{parameters[:payment_uuid]}/refund"
+ elsif action =='account_details'
+ return "#{self.live_url}accounts/#{account_uuid}"
+ else
+ raise ActiveMerchantError.new('Cannot commit without a valid endpoint')
+ end
+ end
+
+ def post_data(params = {})
+ params.to_json
+ end
+
+ def headers
+ {
+ 'X-Auth-Token' => @api_key.to_s,
+ 'Content-Type' => 'application/json'
+ }
+ end
+
+ def success_from(response)
+ response['appCode'] == 0
+ end
+
+ def message_from(response)
+ if success_from(response)
+ return 'Succeeded'
+ else
+ if response.key?('data') && response['data'].key?('message')
+ return response['data']['message'].to_s
+ elsif response['appCode'] == RESPONSE_API_UNAUTHORIZED
+ return response['appMessage']
+ elsif response.key?('meta') && response['meta'].key?('errors') && response['meta']['errors'].kind_of?(Array)
+ return response['meta']['errors'].compact.join(', ')
+ end
+ end
+ end
+
+ def authorization_from(response)
+ response['data']['paymentUuid']
+ end
+
+ def error_code_from(response)
+ return nil if success_from(response)
+
+ if response['data'].key?('code')
+ return STANDARD_ERROR_CODE_MAPPING[response['data']['code']] || 'unknown'
+ else
+ return response['appCode'].to_s
+ end
+ end
+
+ def publishable_key
+ return @publishable_key if @publishable_key.present? && @publishable_key != 'nil'
+
+ begin
+ response = parse(ssl_get(get_url('account_details'), headers))
+ @publishable_key = response['data']['publishableKey'].to_s
+ rescue ResponseError
+ # Not authentication part just fetching extra details - wait till we get the 401 if credentials invalid
+ ''
+ end
+ end
+ end
+ end
+end
diff --git a/lib/active_merchant/billing/gateways/vanco.rb b/lib/active_merchant/billing/gateways/vanco.rb
index 78cd819782a..e8a3617de91 100644
--- a/lib/active_merchant/billing/gateways/vanco.rb
+++ b/lib/active_merchant/billing/gateways/vanco.rb
@@ -5,7 +5,7 @@ module Billing
class VancoGateway < Gateway
include Empty
- self.test_url = 'https://www.vancodev.com/cgi-bin/wstest2.vps'
+ self.test_url = 'https://uat.vancopayments.com/cgi-bin/ws2.vps'
self.live_url = 'https://myvanco.vancopayments.com/cgi-bin/ws2.vps'
self.supported_countries = ['US']
diff --git a/lib/active_merchant/billing/gateways/wepay.rb b/lib/active_merchant/billing/gateways/wepay.rb
index d315be6d3b1..b9cd338213a 100644
--- a/lib/active_merchant/billing/gateways/wepay.rb
+++ b/lib/active_merchant/billing/gateways/wepay.rb
@@ -4,7 +4,7 @@ class WepayGateway < Gateway
self.test_url = 'https://stage.wepayapi.com/v2'
self.live_url = 'https://wepayapi.com/v2'
- self.supported_countries = ['US']
+ self.supported_countries = ['US', 'CA']
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
self.homepage_url = 'https://www.wepay.com/'
self.default_currency = 'USD'
@@ -18,38 +18,47 @@ def initialize(options = {})
def purchase(money, payment_method, options = {})
post = {}
if payment_method.is_a?(String)
- purchase_with_token(post, money, payment_method, options)
+ MultiResponse.run do |r|
+ r.process { authorize_with_token(post, money, payment_method, options) }
+ r.process { capture(money, r.authorization, options) }
+ end
else
MultiResponse.run do |r|
r.process { store(payment_method, options) }
- r.process { purchase_with_token(post, money, split_authorization(r.authorization).first, options) }
+ r.process { authorize_with_token(post, money, r.authorization, options) }
+ r.process { capture(money, r.authorization, options) }
end
end
end
def authorize(money, payment_method, options = {})
- post = {auto_capture: 0}
+ post = {}
if payment_method.is_a?(String)
- purchase_with_token(post, money, payment_method, options)
+ authorize_with_token(post, money, payment_method, options)
else
MultiResponse.run do |r|
r.process { store(payment_method, options) }
- r.process { purchase_with_token(post, money, split_authorization(r.authorization).first, options) }
+ r.process { authorize_with_token(post, money, r.authorization, options) }
end
end
end
def capture(money, identifier, options = {})
+ checkout_id, original_amount = split_authorization(identifier)
+
post = {}
- post[:checkout_id] = split_authorization(identifier).first
- commit('/checkout/capture', post)
+ post[:checkout_id] = checkout_id
+ if(money && (original_amount != amount(money)))
+ post[:amount] = amount(money)
+ end
+ commit('/checkout/capture', post, options)
end
def void(identifier, options = {})
post = {}
post[:checkout_id] = split_authorization(identifier).first
post[:cancel_reason] = (options[:description] || "Void")
- commit('/checkout/cancel', post)
+ commit('/checkout/cancel', post, options)
end
def refund(money, identifier, options = {})
@@ -61,68 +70,70 @@ def refund(money, identifier, options = {})
post[:amount] = amount(money)
end
post[:refund_reason] = (options[:description] || "Refund")
- post[:app_fee] = options[:application_fee] if options[:application_fee]
post[:payer_email_message] = options[:payer_email_message] if options[:payer_email_message]
post[:payee_email_message] = options[:payee_email_message] if options[:payee_email_message]
- commit("/checkout/refund", post)
+ commit("/checkout/refund", post, options)
end
def store(creditcard, options = {})
- requires!(options, :email)
-
post = {}
post[:client_id] = @options[:client_id]
post[:user_name] = "#{creditcard.first_name} #{creditcard.last_name}"
post[:email] = options[:email] || "unspecified@example.com"
post[:cc_number] = creditcard.number
post[:cvv] = creditcard.verification_value unless options[:recurring]
- post[:expiration_month] = "#{creditcard.month}"
- post[:expiration_year] = "#{creditcard.year}"
- post[:original_ip] = options[:ip] if options[:ip]
- post[:original_device] = options[:device_fingerprint] if options[:device_fingerprint]
+ post[:expiration_month] = creditcard.month
+ post[:expiration_year] = creditcard.year
+
if(billing_address = (options[:billing_address] || options[:address]))
- post[:address] = {
- "address1" => billing_address[:address1],
- "city" => billing_address[:city],
- "country" => billing_address[:country]
- }
- if(post[:country] == "US")
- post[:address]["zip"] = billing_address[:zip]
- post[:address]["state"] = billing_address[:state]
- else
- post[:address]["region"] = billing_address[:state]
- post[:address]["postcode"] = billing_address[:zip]
- end
+ post[:address] = {}
+ post[:address]["address1"] = billing_address[:address1] if billing_address[:address1]
+ post[:address]["city"] = billing_address[:city] if billing_address[:city]
+ post[:address]["country"] = billing_address[:country] if billing_address[:country]
+ post[:address]["region"] = billing_address[:state] if billing_address[:state]
+ post[:address]["postal_code"] = billing_address[:zip]
end
if options[:recurring] == true
post[:client_secret] = @options[:client_secret]
- commit('/credit_card/transfer', post)
+ commit('/credit_card/transfer', post, options)
else
- commit('/credit_card/create', post)
+ post[:original_device] = options[:device_fingerprint] if options[:device_fingerprint]
+ post[:original_ip] = options[:ip] if options[:ip]
+ commit('/credit_card/create', post, options)
end
end
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r((\\?"cc_number\\?":\\?")[^\\"]+(\\?"))i, '\1[FILTERED]\2').
+ gsub(%r((\\?"cvv\\?":\\?")[^\\"]+(\\?"))i, '\1[FILTERED]\2').
+ gsub(%r((Authorization: Bearer )\w+)i, '\1[FILTERED]\2')
+ end
+
private
- def purchase_with_token(post, money, token, options)
+ def authorize_with_token(post, money, token, options)
add_token(post, token)
add_product_data(post, money, options)
- commit('/checkout/create', post)
+ commit('/checkout/create', post, options)
end
def add_product_data(post, money, options)
post[:account_id] = @options[:account_id]
post[:amount] = amount(money)
post[:short_description] = (options[:description] || "Purchase")
- post[:type] = (options[:type] || "GOODS")
+ post[:type] = (options[:type] || "goods")
post[:currency] = (options[:currency] || currency(money))
post[:long_description] = options[:long_description] if options[:long_description]
post[:payer_email_message] = options[:payer_email_message] if options[:payer_email_message]
post[:payee_email_message] = options[:payee_email_message] if options[:payee_email_message]
post[:reference_id] = options[:order_id] if options[:order_id]
- post[:app_fee] = options[:application_fee] if options[:application_fee]
- post[:fee_payer] = options[:fee_payer] if options[:fee_payer]
+ post[:unique_id] = options[:unique_id] if options[:unique_id]
post[:redirect_uri] = options[:redirect_uri] if options[:redirect_uri]
post[:callback_uri] = options[:callback_uri] if options[:callback_uri]
post[:fallback_uri] = options[:fallback_uri] if options[:fallback_uri]
@@ -133,11 +144,28 @@ def add_product_data(post, money, options)
post[:preapproval_id] = options[:preapproval_id] if options[:preapproval_id]
post[:prefill_info] = options[:prefill_info] if options[:prefill_info]
post[:funding_sources] = options[:funding_sources] if options[:funding_sources]
+ post[:payer_rbits] = options[:payer_rbits] if options[:payer_rbits]
+ post[:transaction_rbits] = options[:transaction_rbits] if options[:transaction_rbits]
+ add_fee(post, options)
end
def add_token(post, token)
- post[:payment_method_id] = token
- post[:payment_method_type] = "credit_card"
+ payment_method = {}
+ payment_method[:type] = "credit_card"
+ payment_method[:credit_card] = {
+ id: token,
+ auto_capture: false
+ }
+
+ post[:payment_method] = payment_method
+ end
+
+ def add_fee(post, options)
+ if options[:application_fee] || options[:fee_payer]
+ post[:fee] = {}
+ post[:fee][:app_fee] = options[:application_fee] if options[:application_fee]
+ post[:fee][:fee_payer] = options[:fee_payer] if options[:fee_payer]
+ end
end
def parse(response)
@@ -149,7 +177,7 @@ def commit(action, params, options={})
response = parse(ssl_post(
((test? ? test_url : live_url) + action),
params.to_json,
- headers
+ headers(options)
))
rescue ResponseError => e
response = parse(e.response.body)
@@ -178,7 +206,8 @@ def message_from(response)
def authorization_from(response, params)
return response["credit_card_id"].to_s if response["credit_card_id"]
- [response["checkout_id"], params[:amount]].join('|')
+ original_amount = response["amount"].nil? ? nil : sprintf("%0.02f", response["amount"])
+ [response["checkout_id"], original_amount].join('|')
end
def split_authorization(authorization)
@@ -192,14 +221,18 @@ def unparsable_response(raw_response)
return Response.new(false, message)
end
- def headers
- {
- "Content-Type" => "application/json",
- "User-Agent" => "ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
- "Authorization" => "Bearer #{@options[:access_token]}"
+ def headers(options)
+ headers = {
+ "Content-Type" => "application/json",
+ "User-Agent" => "ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
+ "Authorization" => "Bearer #{@options[:access_token]}"
}
+ headers["Api-Version"] = options[:version] if options[:version]
+ headers["Client-IP"] = options[:ip] if options[:ip]
+ headers["WePay-Risk-Token"] = options[:risk_token] if options[:risk_token]
+
+ headers
end
end
end
end
-
diff --git a/lib/active_merchant/billing/gateways/wirecard.rb b/lib/active_merchant/billing/gateways/wirecard.rb
index bdc00b46017..46a94f942d3 100644
--- a/lib/active_merchant/billing/gateways/wirecard.rb
+++ b/lib/active_merchant/billing/gateways/wirecard.rb
@@ -235,17 +235,18 @@ def add_transaction_data(xml, money, options)
add_address(xml, options[:billing_address])
when :capture, :bookback
xml.tag! 'GuWID', options[:preauthorization]
- add_amount(xml, money)
+ add_amount(xml, money, options)
when :reversal
xml.tag! 'GuWID', options[:preauthorization]
end
+ add_customer_data(xml, options)
end
end
end
# Includes the payment (amount, currency, country) to the transaction-xml
def add_invoice(xml, money, options)
- add_amount(xml, money)
+ add_amount(xml, money, options)
xml.tag! 'Currency', options[:currency] || currency(money)
xml.tag! 'CountryCode', options[:billing_address][:country]
xml.tag! 'RECURRING_TRANSACTION' do
@@ -254,8 +255,8 @@ def add_invoice(xml, money, options)
end
# Include the amount in the transaction-xml
- def add_amount(xml, money)
- xml.tag! 'Amount', amount(money)
+ def add_amount(xml, money, options)
+ xml.tag! 'Amount', localized_amount(money, options[:currency] || currency(money))
end
# Includes the credit-card data to the transaction-xml
diff --git a/lib/active_merchant/billing/gateways/worldpay.rb b/lib/active_merchant/billing/gateways/worldpay.rb
index 43806646e9c..4f49f1b2915 100644
--- a/lib/active_merchant/billing/gateways/worldpay.rb
+++ b/lib/active_merchant/billing/gateways/worldpay.rb
@@ -6,9 +6,10 @@ class WorldpayGateway < Gateway
self.default_currency = 'GBP'
self.money_format = :cents
- self.supported_countries = %w(HK GB AU AD BE CH CY CZ DE DK ES FI FR GI GR HU IE IL IT LI LU MC MT NL NO NZ PL PT SE SG SI SM TR UM VA)
+ self.supported_countries = %w(HK GB AU AD BE CH CY CZ DE DK ES FI FR GI GR HU IE IT LI LU MC MT NL NO NZ PL PT SE SG SI SM TR UM VA)
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :maestro, :laser, :switch]
self.currencies_without_fractions = %w(HUF IDR ISK JPY KRW)
+ self.currencies_with_three_decimal_places = %w(BHD KWD OMR RSD TND)
self.homepage_url = 'http://www.worldpay.com/'
self.display_name = 'Worldpay Global'
@@ -54,22 +55,35 @@ def capture(money, authorization, options = {})
def void(authorization, options = {})
MultiResponse.run do |r|
- r.process{inquire_request(authorization, options, "AUTHORISED")}
+ r.process{inquire_request(authorization, options, "AUTHORISED")} unless options[:authorization_validated]
r.process{cancel_request(authorization, options)}
end
end
def refund(money, authorization, options = {})
- MultiResponse.run do |r|
- r.process{inquire_request(authorization, options, "CAPTURED", "SETTLED", "SETTLED_BY_MERCHANT")}
- r.process{refund_request(money, authorization, options)}
+ response = MultiResponse.run do |r|
+ r.process { inquire_request(authorization, options, "CAPTURED", "SETTLED", "SETTLED_BY_MERCHANT") }
+ r.process { refund_request(money, authorization, options) }
end
+
+ return response if response.success?
+ return response unless options[:force_full_refund_if_unsettled]
+
+ void(authorization, options ) if response.params["last_event"] == "AUTHORISED"
+ end
+
+ # Credits only function on a Merchant ID/login/profile flagged for Payouts
+ # aka Credit Fund Transfers (CFT), whereas normal purchases, refunds,
+ # and other transactions should be performed on a normal eCom-flagged
+ # merchant ID.
+ def credit(money, payment_method, options = {})
+ credit_request(money, payment_method, options.merge(:credit => true))
end
def verify(credit_card, options={})
MultiResponse.run(:use_first_response) do |r|
r.process { authorize(100, credit_card, options) }
- r.process(:ignore_result) { void(r.authorization, options) }
+ r.process(:ignore_result) { void(r.authorization, options.merge(:authorization_validated => true)) }
end
end
@@ -87,23 +101,27 @@ def scrub(transcript)
private
def authorize_request(money, payment_method, options)
- commit('authorize', build_authorization_request(money, payment_method, options), "AUTHORISED")
+ commit('authorize', build_authorization_request(money, payment_method, options), "AUTHORISED", options)
end
def capture_request(money, authorization, options)
- commit('capture', build_capture_request(money, authorization, options), :ok)
+ commit('capture', build_capture_request(money, authorization, options), :ok, options)
end
def cancel_request(authorization, options)
- commit('cancel', build_void_request(authorization, options), :ok)
+ commit('cancel', build_void_request(authorization, options), :ok, options)
end
def inquire_request(authorization, options, *success_criteria)
- commit('inquiry', build_order_inquiry_request(authorization, options), *success_criteria)
+ commit('inquiry', build_order_inquiry_request(authorization, options), *success_criteria, options)
end
def refund_request(money, authorization, options)
- commit('refund', build_refund_request(money, authorization, options), :ok)
+ commit('refund', build_refund_request(money, authorization, options), :ok, options)
+ end
+
+ def credit_request(money, payment_method, options)
+ commit('credit', build_authorization_request(money, payment_method, options), :ok, options)
end
def build_request
@@ -147,6 +165,9 @@ def build_authorization_request(money, payment_method, options)
end
add_payment_method(xml, money, payment_method, options)
add_email(xml, options)
+ if options[:hcg_additional_data]
+ add_hcg_additional_data(xml, options)
+ end
end
end
end
@@ -184,9 +205,9 @@ def add_amount(xml, money, options)
currency = options[:currency] || currency(money)
amount_hash = {
- :value => amount(money),
+ :value => localized_amount(money, currency),
'currencyCode' => currency,
- 'exponent' => non_fractional_currency?(currency) ? 0 : 2
+ 'exponent' => currency_exponent(currency)
}
if options[:debit_credit_indicator]
@@ -208,7 +229,7 @@ def add_payment_method(xml, amount, payment_method, options)
end
end
else
- xml.tag! 'paymentDetails' do
+ xml.tag! 'paymentDetails', credit_fund_transfer_attribute(options) do
xml.tag! CARD_CODES[card_brand(payment_method)] do
xml.tag! 'cardNumber', payment_method.number
xml.tag! 'expiryDate' do
@@ -220,21 +241,30 @@ def add_payment_method(xml, amount, payment_method, options)
add_address(xml, (options[:billing_address] || options[:address]))
end
- if options[:ip]
- xml.tag! 'session', 'shopperIPAddress' => options[:ip]
+ if options[:ip] && options[:session_id]
+ xml.tag! 'session', 'shopperIPAddress' => options[:ip], 'id' => options[:session_id]
+ else
+ xml.tag! 'session', 'shopperIPAddress' => options[:ip] if options[:ip]
+ xml.tag! 'session', 'id' => options[:session_id] if options[:session_id]
end
end
end
end
def add_email(xml, options)
- return unless options[:email]
+ return unless options[:execute_threed] || options[:email]
xml.tag! 'shopper' do
- xml.tag! 'shopperEmailAddress', options[:email]
+ xml.tag! 'shopperEmailAddress', options[:email] if options[:email]
+ xml.tag! 'browser' do
+ xml.tag! 'acceptHeader', options[:accept_header]
+ xml.tag! 'userAgentHeader', options[:user_agent]
+ end
end
end
def add_address(xml, address)
+ return unless address
+
address = address_with_defaults(address)
xml.tag! 'cardAddress' do
@@ -254,6 +284,14 @@ def add_address(xml, address)
end
end
+ def add_hcg_additional_data(xml, options)
+ xml.tag! 'hcgAdditionalData' do
+ options[:hcg_additional_data].each do |k, v|
+ xml.tag! "param", {name: k.to_s}, v
+ end
+ end
+ end
+
def address_with_defaults(address)
address ||= {}
address.delete_if { |_, v| v.blank? }
@@ -287,9 +325,24 @@ def parse_element(raw, node)
raw
end
- def commit(action, request, *success_criteria)
- xmr = ssl_post(url, request, 'Content-Type' => 'text/xml', 'Authorization' => encoded_credentials)
- raw = parse(action, xmr)
+ def headers(options)
+ headers = {
+ 'Content-Type' => 'text/xml',
+ 'Authorization' => encoded_credentials
+ }
+ if options[:cookie]
+ headers.merge!('Set-Cookie' => options[:cookie]) if options[:cookie]
+ end
+ headers
+ end
+
+ def commit(action, request, *success_criteria, options)
+ xml = ssl_post(url, request, headers(options))
+ raw = parse(action, xml)
+ if options[:execute_threed]
+ raw[:cookie] = @cookie
+ raw[:session_id] = options[:session_id]
+ end
success, message = success_and_message_from(raw, success_criteria)
Response.new(
@@ -297,6 +350,7 @@ def commit(action, request, *success_criteria)
message,
raw,
:authorization => authorization_from(raw),
+ :error_code => error_code_from(success, raw),
:test => test?)
rescue ActiveMerchant::ResponseError => e
@@ -311,6 +365,18 @@ def url
test? ? self.test_url : self.live_url
end
+ # Override the regular handle response so we can access the headers
+ # Set-Cookie value is needed for 3DS transactions
+ def handle_response(response)
+ case response.code.to_i
+ when 200...300
+ @cookie = response.response['Set-Cookie']
+ response.body
+ else
+ raise ResponseError.new(response)
+ end
+ end
+
# success_criteria can be:
# - a string or an array of strings (if one of many responses)
# - An array of strings if one of many responses could be considered a
@@ -326,6 +392,12 @@ def success_and_message_from(raw, success_criteria)
[ success, message ]
end
+ def error_code_from(success, raw)
+ unless success == "SUCCESS"
+ raw[:iso8583_return_code_code] || raw[:error_code] || nil
+ end
+ end
+
def required_status_message(raw, success_criteria)
if(!success_criteria.include?(raw[:last_event]))
"A transaction status of #{success_criteria.collect{|c| "'#{c}'"}.join(" or ")} is required."
@@ -337,10 +409,21 @@ def authorization_from(raw)
(pair ? pair.last : nil)
end
+ def credit_fund_transfer_attribute(options)
+ return unless options[:credit]
+ {'action' => "REFUND"}
+ end
+
def encoded_credentials
credentials = "#{@options[:login]}:#{@options[:password]}"
"Basic #{[credentials].pack('m').strip}"
end
+
+ def currency_exponent(currency)
+ return 0 if non_fractional_currency?(currency)
+ return 3 if three_decimal_currency?(currency)
+ return 2
+ end
end
end
end
diff --git a/lib/active_merchant/billing/gateways/worldpay_us.rb b/lib/active_merchant/billing/gateways/worldpay_us.rb
index e66901e376e..084169424a5 100644
--- a/lib/active_merchant/billing/gateways/worldpay_us.rb
+++ b/lib/active_merchant/billing/gateways/worldpay_us.rb
@@ -3,11 +3,14 @@
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class WorldpayUsGateway < Gateway
+ class_attribute :backup_url
+
self.display_name = "Worldpay US"
self.homepage_url = "http://www.worldpay.com/us"
# No sandbox, just use test cards.
- self.live_url = 'https://trans.worldpay.us/cgi-bin/process.cgi'
+ self.live_url = 'https://trans.worldpay.us/cgi-bin/process.cgi'
+ self.backup_url = 'https://trans.gwtx01.com/cgi-bin/process.cgi'
self.supported_countries = ['US']
self.default_currency = 'USD'
@@ -25,7 +28,7 @@ def purchase(money, payment_method, options={})
add_payment_method(post, payment_method)
add_customer_data(post, options)
- commit('purchase', post)
+ commit('purchase', options, post)
end
def authorize(money, payment, options={})
@@ -34,7 +37,7 @@ def authorize(money, payment, options={})
add_credit_card(post, payment)
add_customer_data(post, options)
- commit('authorize', post)
+ commit('authorize', options, post)
end
def capture(amount, authorization, options={})
@@ -43,7 +46,7 @@ def capture(amount, authorization, options={})
add_reference(post, authorization)
add_customer_data(post, options)
- commit('capture', post)
+ commit('capture', options, post)
end
def refund(amount, authorization, options={})
@@ -52,14 +55,14 @@ def refund(amount, authorization, options={})
add_reference(post, authorization)
add_customer_data(post, options)
- commit("refund", post)
+ commit("refund", options, post)
end
def void(authorization, options={})
post = {}
add_reference(post, authorization)
- commit('void', post)
+ commit('void', options, post)
end
def verify(credit_card, options={})
@@ -69,8 +72,24 @@ def verify(credit_card, options={})
end
end
+ def supports_scrubbing?
+ true
+ end
+
+ def scrub(transcript)
+ transcript.
+ gsub(%r((&?merchantpin=)[^&]*)i, '\1[FILTERED]').
+ gsub(%r((&?ccnum=)[^&]*)i, '\1[FILTERED]').
+ gsub(%r((&?ckacct=)[^&]*)i, '\1[FILTERED]').
+ gsub(%r((&?cvv2=)[^&]*)i, '\1[FILTERED]')
+ end
+
private
+ def url(options)
+ options[:use_backup_url].to_s == "true" ? self.backup_url : self.live_url
+ end
+
def add_customer_data(post, options)
if(billing_address = (options[:billing_address] || options[:address]))
post[:ci_companyname] = billing_address[:company]
@@ -162,7 +181,7 @@ def parse(xml)
"void" => "ns_void",
}
- def commit(action, post)
+ def commit(action, options, post)
post[:action] = ACTIONS[action] unless post[:action]
post[:acctid] = @options[:acctid]
post[:subid] = @options[:subid]
@@ -170,7 +189,7 @@ def commit(action, post)
post[:authonly] = '1' if action == 'authorize'
- raw = parse(ssl_post(live_url, post.to_query))
+ raw = parse(ssl_post(url(options), post.to_query))
succeeded = success_from(raw['result'])
Response.new(
diff --git a/lib/active_merchant/billing/network_tokenization_credit_card.rb b/lib/active_merchant/billing/network_tokenization_credit_card.rb
index cab84f2b748..955557a67d0 100644
--- a/lib/active_merchant/billing/network_tokenization_credit_card.rb
+++ b/lib/active_merchant/billing/network_tokenization_credit_card.rb
@@ -17,7 +17,7 @@ class NetworkTokenizationCreditCard < CreditCard
attr_accessor :payment_cryptogram, :eci, :transaction_id
attr_writer :source
- SOURCES = [:apple_pay, :android_pay]
+ SOURCES = %i(apple_pay android_pay google_pay)
def source
if defined?(@source) && SOURCES.include?(@source)
diff --git a/lib/active_merchant/connection.rb b/lib/active_merchant/connection.rb
index 7bd0328bee8..6db1df3b29f 100644
--- a/lib/active_merchant/connection.rb
+++ b/lib/active_merchant/connection.rb
@@ -21,11 +21,15 @@ class Connection
attr_accessor :read_timeout
attr_accessor :verify_peer
attr_accessor :ssl_version
+ if Net::HTTP.instance_methods.include?(:min_version=)
+ attr_accessor :min_version
+ attr_accessor :max_version
+ end
attr_accessor :ca_file
attr_accessor :ca_path
attr_accessor :pem
attr_accessor :pem_password
- attr_accessor :wiredump_device
+ attr_reader :wiredump_device
attr_accessor :logger
attr_accessor :tag
attr_accessor :ignore_http_status
@@ -44,12 +48,21 @@ def initialize(endpoint)
@max_retries = MAX_RETRIES
@ignore_http_status = false
@ssl_version = nil
- @proxy_address = nil
+ if Net::HTTP.instance_methods.include?(:min_version=)
+ @min_version = nil
+ @max_version = nil
+ end
+ @proxy_address = :ENV
@proxy_port = nil
end
+ def wiredump_device=(device)
+ raise ArgumentError, "can't wiredump to frozen #{device.class}" if device && device.frozen?
+ @wiredump_device = device
+ end
+
def request(method, body, headers = {})
- request_start = Time.now.to_f
+ request_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
retry_exceptions(:max_retries => max_retries, :logger => logger, :tag => tag) do
begin
@@ -75,8 +88,14 @@ def request(method, body, headers = {})
# It's kind of ambiguous whether the RFC allows bodies
# for DELETE requests. But Net::HTTP's delete method
# very unambiguously does not.
- raise ArgumentError, "DELETE requests do not support a request body" if body
- http.delete(endpoint.request_uri, headers)
+ if body
+ debug body
+ req = Net::HTTP::Delete.new(endpoint.request_uri, headers)
+ req.body = body
+ http.request(req)
+ else
+ http.delete(endpoint.request_uri, headers)
+ end
else
raise ArgumentError, "Unsupported request method #{method.to_s.upcase}"
end
@@ -89,7 +108,7 @@ def request(method, body, headers = {})
end
ensure
- info "connection_request_total_time=%.4fs" % [Time.now.to_f - request_start], tag
+ info "connection_request_total_time=%.4fs" % [Process.clock_gettime(Process::CLOCK_MONOTONIC) - request_start], tag
end
private
@@ -116,6 +135,10 @@ def configure_ssl(http)
http.use_ssl = true
http.ssl_version = ssl_version if ssl_version
+ if http.respond_to?(:min_version=)
+ http.min_version = min_version if min_version
+ http.max_version = max_version if max_version
+ end
if verify_peer
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
diff --git a/lib/active_merchant/network_connection_retries.rb b/lib/active_merchant/network_connection_retries.rb
index 4793dcf83b6..fe2c0e29c61 100644
--- a/lib/active_merchant/network_connection_retries.rb
+++ b/lib/active_merchant/network_connection_retries.rb
@@ -1,3 +1,5 @@
+require 'openssl'
+
module ActiveMerchant
module NetworkConnectionRetries
DEFAULT_RETRIES = 3
@@ -42,19 +44,19 @@ def retry_network_exceptions(options = {})
request_start = nil
begin
- request_start = Time.now.to_f
+ request_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
result = yield
- log_with_retry_details(options[:logger], initial_retries-retries + 1, Time.now.to_f - request_start, "success", options[:tag])
+ log_with_retry_details(options[:logger], initial_retries-retries + 1, Process.clock_gettime(Process::CLOCK_MONOTONIC) - request_start, "success", options[:tag])
result
rescue ActiveMerchant::RetriableConnectionError => e
retries -= 1
- log_with_retry_details(options[:logger], initial_retries-retries, Time.now.to_f - request_start, e.message, options[:tag])
+ log_with_retry_details(options[:logger], initial_retries-retries, Process.clock_gettime(Process::CLOCK_MONOTONIC) - request_start, e.message, options[:tag])
retry unless retries.zero?
raise ActiveMerchant::ConnectionError.new(e.message, e)
rescue ActiveMerchant::ConnectionError, ActiveMerchant::InvalidResponseError => e
retries -= 1
- log_with_retry_details(options[:logger], initial_retries-retries, Time.now.to_f - request_start, e.message, options[:tag])
+ log_with_retry_details(options[:logger], initial_retries-retries, Process.clock_gettime(Process::CLOCK_MONOTONIC) - request_start, e.message, options[:tag])
retry if (options[:retry_safe] || retry_safe) && !retries.zero?
raise
end
diff --git a/lib/active_merchant/posts_data.rb b/lib/active_merchant/posts_data.rb
index ad5e03ebd15..db81049df24 100644
--- a/lib/active_merchant/posts_data.rb
+++ b/lib/active_merchant/posts_data.rb
@@ -8,6 +8,12 @@ def self.included(base)
base.class_attribute :ssl_version
base.ssl_version = nil
+ base.class_attribute :min_version
+ base.min_version = nil
+
+ base.class_attribute :max_version
+ base.min_version = nil
+
base.class_attribute :retry_safe
base.retry_safe = false
@@ -41,7 +47,7 @@ def ssl_request(method, endpoint, data, headers)
def raw_ssl_request(method, endpoint, data, headers = {})
logger.warn "#{self.class} using ssl_strict=false, which is insecure" if logger unless ssl_strict
- logger.warn "#{self.class} posting to plaintext endpoint, which is insecure" if logger unless endpoint =~ /^https:/
+ logger.warn "#{self.class} posting to plaintext endpoint, which is insecure" if logger unless endpoint.to_s =~ /^https:/
connection = new_connection(endpoint)
connection.open_timeout = open_timeout
@@ -53,6 +59,10 @@ def raw_ssl_request(method, endpoint, data, headers = {})
connection.max_retries = max_retries
connection.tag = self.class.name
connection.wiredump_device = wiredump_device
+ if connection.respond_to?(:min_version=)
+ connection.min_version = min_version
+ connection.max_version = max_version
+ end
connection.pem = @options[:pem] if @options
connection.pem_password = @options[:pem_password] if @options
diff --git a/lib/active_merchant/version.rb b/lib/active_merchant/version.rb
index f86220f7d41..ce50e19a8a7 100644
--- a/lib/active_merchant/version.rb
+++ b/lib/active_merchant/version.rb
@@ -1,3 +1,3 @@
module ActiveMerchant
- VERSION = "1.60.0"
+ VERSION = "1.78.0"
end
diff --git a/lib/certs/cacert.pem b/lib/certs/cacert.pem
index 9794dfb70f4..e353e84e1c0 100644
--- a/lib/certs/cacert.pem
+++ b/lib/certs/cacert.pem
@@ -501,6 +501,66 @@ W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0
tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8
-----END CERTIFICATE-----
+Entrust G2 Root Certificate Authority
+=====================================
+-----BEGIN CERTIFICATE-----
+MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC
+VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50
+cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs
+IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz
+dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy
+NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu
+dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt
+dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0
+aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T
+RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN
+cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW
+wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1
+U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0
+jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP
+BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN
+BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/
+jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ
+Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v
+1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R
+nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH
+VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g==
+-----END CERTIFICATE-----
+
+Entrust L1M Chain Root Certificate
+==================================
+-----BEGIN CERTIFICATE-----
+MIIE/zCCA+egAwIBAgIEUdNARDANBgkqhkiG9w0BAQsFADCBsDELMAkGA1UEBhMC
+VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0
+Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW
+KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl
+cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE0MDkyMjE3MTQ1N1oXDTI0MDkyMzAx
+MzE1M1owgb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgw
+JgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQL
+EzAoYykgMjAwOSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9u
+bHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
+eSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuoS2ctueDGvi
+mekwAad26jK4lUEaydphTlhyz/72gnm/c2EGCqUn2LNf00VOHHLWTjLycooP94MZ
+0GqAgABFHrDH55q/ElcnHKNoLwqHvWprDl5l8xx31dSFjXAhtLMy54ui1YY5ArG4
+0kfO5MlJxDun3vtUfVe+8OhuwnmyOgtV4lCYFjITXC94VsHClLPyWuQnmp8k18bs
+0JslguPMwsRFxYyXegZrKhGfqQpuSDtv29QRGUL3jwe/9VNfnD70FyzmaaxOMkxi
+d+q36OW7NLwZi66cUee3frVTsTMi5W3PcDwa+uKbZ7aD9I2lr2JMTeBYrGQ0EgP4
+to2UYySkcQIDAQABo4IBDzCCAQswDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI
+MAYBAf8CAQEwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdodHRwOi8vb2Nz
+cC5lbnRydXN0Lm5ldDAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLmVudHJ1
+c3QubmV0L3Jvb3RjYTEuY3JsMDsGA1UdIAQ0MDIwMAYEVR0gADAoMCYGCCsGAQUF
+BwIBFhpodHRwOi8vd3d3LmVudHJ1c3QubmV0L0NQUzAdBgNVHQ4EFgQUanImetAe
+733nO2lR1GyNn5ASZqswHwYDVR0jBBgwFoAUaJDkZ6SmU4DHhmak8fdLQ/uEvW0w
+DQYJKoZIhvcNAQELBQADggEBAGkzg/woem99751V68U+ep11s8zDODbZNKIoaBjq
+HmnTvefQd9q4AINOSs9v0fHBIj905PeYSZ6btp7h25h3LVY0sag82f3Azce/BQPU
+AsXx5cbaCKUTx2IjEdFhMB1ghEXveajGJpOkt800uGnFE/aRs8lFc3a2kvZ2Clvh
+A0e36SlMkTIjN0qcNdh4/R0f5IOJJICtt/nP5F2l1HHEhVtwH9s/HAHrGkUmMRTM
+Zb9n3srMM2XlQZHXN75BGpad5oqXnafOrE6aPb0BoGrZTyIAi0TVaWJ7LuvMuueS
+fWlnPfy4fN5Bh9Bp6roKGHoalUOzeXEodm2h+1dK7E3IDhA=
+-----END CERTIFICATE-----
+
RSA Security 2048 v3
====================
-----BEGIN CERTIFICATE-----
@@ -1394,6 +1454,31 @@ UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
+DigiCert Global Root G2
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
+MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
+b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI
+2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx
+1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ
+q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz
+tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ
+vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP
+BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV
+5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY
+1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4
+NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG
+Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
+8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe
+pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
+MrY=
+-----END CERTIFICATE-----
+
DigiCert High Assurance EV Root CA
==================================
-----BEGIN CERTIFICATE-----
diff --git a/lib/support/ssl_version.rb b/lib/support/ssl_version.rb
new file mode 100644
index 00000000000..e45738d7757
--- /dev/null
+++ b/lib/support/ssl_version.rb
@@ -0,0 +1,87 @@
+require "active_merchant"
+require "support/gateway_support"
+
+class SSLVersion
+ attr_accessor :success, :failed, :missing, :errored
+
+ def initialize
+ @gateways = GatewaySupport.new.gateways
+ @success, @failed, @missing, @errored = [], [], [], []
+ end
+
+ def test_gateways(min_version = :TLS1_1)
+ raise "Requires Ruby 2.5 or better" unless Net::HTTP.instance_methods.include?(:min_version=)
+
+ puts "Verifying #{@gateways.count} gateways for SSL min_version=#{min_version}\n\n"
+
+ @gateways.each do |g|
+ unless g.live_url
+ missing << g unless g.abstract_class
+ next
+ end
+
+ uri = URI.parse(g.live_url)
+ result, message = test_min_version(uri, min_version)
+
+ case result
+ when :success
+ print "."
+ success << g
+ when :fail
+ print "F"
+ failed << {:gateway => g, :message => message}
+ when :error
+ print "E"
+ errored << {:gateway => g, :message => message}
+ end
+ end
+
+ print_summary
+ end
+
+ def print_summary
+ puts "\n\nSucceeded gateways (#{success.length})"
+
+ puts "\n\nFailed Gateways (#{failed.length}):"
+ failed.each do |f|
+ puts "#{f[:gateway].name} - #{f[:message]}"
+ end
+
+ puts "\n\nError Gateways (#{errored.length}):"
+ errored.each do |e|
+ puts "#{e[:gateway].name} - #{e[:message]}"
+ end
+
+ if missing.length > 0
+ puts "\n\nGateways missing live_url (#{missing.length}):"
+ missing.each do |m|
+ puts m.name
+ end
+ end
+ end
+
+ private
+
+ def test_min_version(uri, min_version)
+ http = Net::HTTP.new(uri.host, uri.port)
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE # don't care about certificate validity, just protocol version
+ http.min_version = min_version
+ http.open_timeout = 10
+ http.read_timeout = 10
+
+ http.post("/", "")
+
+ return :success
+ rescue Net::HTTPBadResponse
+ return :success # version negotiation succeeded
+ rescue OpenSSL::SSL::SSLError => ex
+ return :fail, ex.inspect
+ rescue Interrupt => ex
+ print_summary
+ raise ex
+ rescue StandardError => ex
+ return :error, ex.inspect
+ end
+
+end
diff --git a/test/fixtures.yml b/test/fixtures.yml
index c02c60e6d7c..02930d53e15 100644
--- a/test/fixtures.yml
+++ b/test/fixtures.yml
@@ -11,6 +11,11 @@
# Paste any required PEM certificates after the pem key.
#
+adyen:
+ username: ''
+ password: ''
+ merchant_account: ''
+
allied_wallet:
site_id: site_id
merchant_id: merchant_id
@@ -18,8 +23,8 @@ allied_wallet:
# Working credentials, no need to replace as of Oct 28 2014
authorize_net:
- login: 5KP3u95bQpv
- password: 4Ktq966gC55GAX7S
+ login: 7Tt72zseSzH
+ password: 7gTh55rdy92ZkP4z
axcessms:
channel: channel
@@ -47,11 +52,6 @@ barclaycard_smartpay:
merchant: merchant
password: password
-barclays_epdq:
- login: test
- password: test
- client_id: 1234
-
barclays_epdq_extra_plus:
login: merchant number
user: username
@@ -122,6 +122,12 @@ cams:
username: testintegrationc
password: password9
+# Working credentials, no need to replace
+card_connect:
+ merchant_id: "496160873888"
+ username: testing
+ password: testing123
+
#Username/Password given in sign up email. Not MMS credentials
card_save:
login: merchant_id
@@ -135,6 +141,12 @@ card_stream:
cardknox:
api_key: ActiveMerchant_Test
+# Working credentials, no need to replace
+cardprocess:
+ user_id: 8a8294174e735d0c014e78beb6c5154f
+ password: cTZjAm9c87
+ entity_id: 8a8294174e735d0c014e78beb6b9154b
+
# Cashnet doesn't provide public testing data
cashnet:
merchant: 'X'
@@ -196,13 +208,24 @@ commercegate:
card_number: "XXXXXXXXXXXXXXXX"
conekta:
- key: key_eYvWV7gSDkNYXsmr
+ key: key_6FTbuwqhYs6zvyyeL3PySg
# Working credentials, no need to replace
creditcall:
terminal_id: '99961426'
transaction_key: '9drdRU9wJ65SNRw3'
+credorax:
+ merchant_id: 'merchant_id'
+ cipher_key: 'cipher_key'
+
+# Culqi does not provide public testing data
+culqi:
+ merchant_id: MERCHANT
+ terminal_id: TERMINAL
+ partner_id: PARTNER
+ secret_key: SECRET
+
# To get 100% passing Cybersource remote tests, you must ask
# Cybersource support to enable the recurring and pinless debit
# services on your test account.
@@ -219,17 +242,27 @@ dibs:
merchant_id: SOMECREDENTIAL
secret_key: NOPUBLICCREDENTIAL
+# Working credentials, no need to replace
+digitzs:
+ app_key: tcwtTux8SPZYO44Gf0UHZH74Z1HSutqCxmIV2PFj2jRc9Poroh3Z3R1BBQNRQ98Q
+ api_key: 0HhRdOU2AsWVEu3gRIKi2UpMMmj8Fj48qggBYTo4
+
direc_pay:
login: 200904281000001
+# Working credentials, no need to replace
+ebanx:
+ integration_key: 1231000
+
efsnet:
login: X
password: Y
+# Provided for url update test
elavon:
- login: LOGIN
- password: PASSWORD
- user: USER (often the same as LOGIN)
+ login: "000127"
+ user: ssltest
+ password: "IERAOBEE5V0D6Q3Q6R51TG89XAIVGEQ3LGLKMKCKCVQBGGGAU7FN627GPA54P5HR"
element:
account_id: "1013963"
@@ -295,8 +328,8 @@ first_pay:
gateway_id: "a91c38c3-7d7f-4d29-acc7-927b4dca0dbe"
firstdata_e4:
- login:
- password:
+ login: SD8821-67
+ password: T6bxSywbcccbJ19eDXNIGaCDOBg1W7T8
flo2cash:
username: SOMECREDENTIAL
@@ -321,9 +354,9 @@ garanti:
password: "123qweASD"
global_collect:
- merchant_id: 1428
- api_key_id: 96f16a41890565d0
- secret_api_key: g/VQ432G02bFpwg/6EY7uiPRSZxKMbQ87Kal716XORA=
+ merchant_id: 2196
+ api_key_id: c91d6752cbbf9cf1
+ secret_api_key: xHjQr5gL9Wcihkqoj4w/UQugdSCNXM2oUQHG5C82jy4=
global_transport:
global_user_name: "USERNAME"
@@ -374,12 +407,24 @@ itransact:
password: API_ACCESS_KEY
gateway_id: GATEWAY_ID
+# Working credentials, no need to replace
+iveri:
+ cert_id: CB69E68D-C7E7-46B9-9B7A-025DCABAD6EF
+ app_id: d10a603d-4ade-405b-93f1-826dfc0181e8
+
jetpay:
login: TESTTERMINAL
+jetpay_v2:
+ login: TESTMCC3136X
+
komoju:
login: sk_f1dd75ce3d5cad477eac0c827c1cac8eaa51ede3
+kushki:
+ public_merchant_id: "Your Public Merchant Id"
+ private_merchant_id: "Your Private Merchant Id"
+
latitude19:
account_number: "03022016"
configuration_id: "380835424362"
@@ -400,6 +445,10 @@ maxipago:
login: "100"
password: "21g8u6gh6szw1gywfs165vui"
+# Working credentials, no need to replace
+mercado_pago:
+ access_token: "TEST-8527269031909288-071213-0fc96cb7cd3633189bfbe29f63722700__LB_LA__-263489584"
+
# Working test credentials, no need to replace
merchant_esolutions:
login: "94100008043900000004"
@@ -482,6 +531,10 @@ money_movers:
login: demo
password: password
+mundipagg:
+ api_key: api_key
+ gateway_id: gateway_id
+
# Working credentials, no need to replace
nab_transact:
login: ABC0001
@@ -601,15 +654,20 @@ pay_secure:
login: LOGIN
password: PASSWORD
+# Working credentials, no need to replace
paybox_direct:
- login: 199988863
+ login: 199988885
+ password: 1999888I
+
+# Working credentials, no need to replace
+paybox_direct_plus:
+ login: 199988832
password: 1999888I
- rang: 85
# Working credentials, no need to replace
payeezy:
- apikey: oKB61AAxbN3xwC6gVAH3dp58FmioHSAT
- apisecret: 36ef1fc3b908b7a79ad2c29de931d2ccff26fcec35602c709a9e34318e936683
+ apikey: UyDMTXx6TD9WErF6ynw7xeEfCAn8fcGs
+ apisecret: 2a4974e242c91cab7f38910938f2a5db79e89756b084bbf7cab7849b50a9930f
token: fdoa-a480ce8951daa73262734cf102641994c1e55e7cdf4c02b6
payex:
@@ -619,9 +677,9 @@ payex:
# Working credentials, no need to replace
payflow:
- login: spreedly
- password: 'ibB4Z8=d;G'
- partner: PayPal
+ login: 'spreedlyIntegrations'
+ password: 'L9DjqEKjXCkU'
+ partner: 'PayPal'
payflow_uk:
login: LOGIN
@@ -632,6 +690,10 @@ payment_express:
login: LOGIN
password: PASSWORD
+paymentez:
+ application_code: APPCODE
+ app_key: APPKEY
+
paymill:
private_key: a9580be4a7b9d0151a3da88c6c935ce0
public_key: 57313835619696ac361dc591bc973626
@@ -710,6 +772,10 @@ plugnpay:
login: LOGIN
password: PASSWORD
+# Working credentials, no need to replace
+pro_pay:
+ cert_str: "5ab9cddef2e4911b77e0c4ffb70f03"
+
# Working credentials, no need to replace
psigate:
login: teststore
@@ -810,9 +876,102 @@ quickpay_with_api_key:
version: 7
qvalent:
- username: TEST
- password: TEST
- merchant: TEST
+ username: "QRSL"
+ password: "QRSLTEST"
+ merchant: 24436057
+ pem_password: "Ceic4s4ig"
+ pem: |
+ Bag Attributes
+ localKeyID: 01 00 00 00
+ friendlyName: CCAPI Client Certificate
+ Key Attributes
+ X509v3 Key Usage: 10
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIEpAIBAAKCAQEAtgPNEzLEFAm2CXCiaJeEFQxzkxXHn+Lyf65FjTDIOMknXnmw
+ gtoPTrFHtDVLJqWHaccLg9/lMKHivW1iGPTeZj4Uke07EcmIaDQD3Gq33gukDbgx
+ d5QDN+mEPRs0TF6FHlKKELKGIZdw4BlM+Jg7hebwZnBC/g9cetmB3ny99g0e5ANk
+ pPESguo+HTFk5vwHkLjMtDB+0zM5y6AyU6z+PaCDr3Q7+vcR50kQLNxpSWH1Il1g
+ BewIHAroN4lJjPvKLnDWvK5SsxW9XB14gLAyQg6qCHDYOdTsdiG6BFG1eijT+3W7
+ iXm8uTDiXSj+PiADEeKvnbEsWqXu+DvUsR/m8wIDAQABAoIBADtC/ZBUpRbJGqX0
+ MEzRmEWqKi8nljlukPoVabvQuEAU7maKRHg2O2mpuujnuTI6Dt7X2d30FhFBhCuc
+ 46WwhIDRkaz5ipP+BBW5adBoRrlbHO0CnciLPokD1PR4WQzMcZcv1JgfKCDjx/KP
+ CkqedjLgwED6KDXEFp5BF1GzV742dfux0Bgq+5kzLp0uAe0+ADxGll1Fx1wWbP/e
+ 4XSTWz4P1q3fmGAJB7fgXcfdp+Yri1x0RNDjBUa6YsR5fFVZBXcFEmPcRGbcsO3J
+ ApJTrskt4H0rQafcx/LmUj3pBVsl/tcyMBHHGHUb0N5pKypf2X+79IYhV+b9K9Ll
+ 5EHVV+ECgYEA8TTci5PsysRvzN5HIgpfsd3ZK/JrTkJ2z3Xx2HFL9XU8EAx9gO5h
+ Qa7CnJY+myoes+x90I7uKqfJdB1JDq2Bek6Jfj8stVd/YTtrOMt7WhHc6OW2S+qY
+ TpvtdeoVcJ2rKGFyo5/mj0aO+Hg1i6wxsIhDPKvSEqSqUrbjmLi5AfkCgYEAwS2V
+ IVzmMZIaRg0FxRccWl/XgpK9JNVjNr4PRiSkjsbFktVNLkbBcFOvrbmPPkBuHW8t
+ XDK6goRcNraxkkzB81mx0glW/zHfEQS4GWuQQEqIWwBfC9QEvC+AlGLaNhQYMleC
+ omLzTDTkBPtQSc72H0ov8TpowgeyM1+U2uozq0sCgYEAm+Szag64KzEcpQdAaDLW
+ OIoO04WBbvor+dfb8C0Bj+ouYJ0B/HOVLjN6GmRMoFJvt4/wnPvT2IPLAx3uWusu
+ 1NKvsIW6KpYbgMc7fGCfH86NvYTB9nzv5VaH+f7JzphIx/d7dV9iT1WmD9b5nIU1
+ NEhNVIgkZOJCJuWHYex5vlkCgYAF4uqxapBFIGuWiN0NJWgixNrfSrNixPHSADac
+ 747oHtx0XfWNHHDWiGZJB+d6gSIZ2YJrVcxjH79jl2uPxrD+RlRpzwkMm6ttbFRj
+ yehKXTsMctVymdJPHa9wVhbKIRCfsBT198fsIYx1LmdC6ICNcYhGdH4us2dVs2ro
+ xMwwQwKBgQDgNA9bGhiajsOI//M9ndH8KZCGdOoYM+6JBurHKX2fF8INRX+sxWix
+ DbUr9rQXkDrX1CY5RNs9kZpNxLJpOSHOpYlIytnGghYTrU1nzXoKLVopkO9Oz1A6
+ WzrOd297XdEoyEbAeCgcLr2WtjMiFHonesjeWIgbqXSKZ+W1oek0vQ==
+ -----END RSA PRIVATE KEY-----
+ Bag Attributes
+ localKeyID: 01 00 00 00
+ friendlyName: CCAPI Client Certificate
+ subject=/CN=Returned & Services League of Australia (Queensland Branch) - Support/OU=IT/O=Returned & Services League of Australia (Queensland Branch)/ST=NSW/C=AU/EMAILADDRESS=pp_support@qvalent.com
+ issuer=/CN=eQvalent/OU=Operations/O=QValent Pty Ltd/L=Wallsend/ST=NSW/C=AU
+ -----BEGIN CERTIFICATE-----
+ MIIE0DCCA7igAwIBAgIEWLX2pzANBgkqhkiG9w0BAQUFADBwMQswCQYDVQQGEwJB
+ VTEMMAoGA1UECBMDTlNXMREwDwYDVQQHEwhXYWxsc2VuZDEYMBYGA1UEChMPUVZh
+ bGVudCBQdHkgTHRkMRMwEQYDVQQLEwpPcGVyYXRpb25zMREwDwYDVQQDEwhlUXZh
+ bGVudDAeFw0xNzAyMjgyMjE2MDdaFw0xOTAyMjgyMjE2MDdaMIHtMSUwIwYJKoZI
+ hvcNAQkBFhZwcF9zdXBwb3J0QHF2YWxlbnQuY29tMQswCQYDVQQGEwJBVTEMMAoG
+ A1UECAwDTlNXMUgwRgYDVQQKDD9SZXR1cm5lZCAmIzM4OyBTZXJ2aWNlcyBMZWFn
+ dWUgb2YgQXVzdHJhbGlhIChRdWVlbnNsYW5kIEJyYW5jaCkxCzAJBgNVBAsMAklU
+ MVIwUAYDVQQDDElSZXR1cm5lZCAmIzM4OyBTZXJ2aWNlcyBMZWFndWUgb2YgQXVz
+ dHJhbGlhIChRdWVlbnNsYW5kIEJyYW5jaCkgLSBTdXBwb3J0MIIBIjANBgkqhkiG
+ 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtgPNEzLEFAm2CXCiaJeEFQxzkxXHn+Lyf65F
+ jTDIOMknXnmwgtoPTrFHtDVLJqWHaccLg9/lMKHivW1iGPTeZj4Uke07EcmIaDQD
+ 3Gq33gukDbgxd5QDN+mEPRs0TF6FHlKKELKGIZdw4BlM+Jg7hebwZnBC/g9cetmB
+ 3ny99g0e5ANkpPESguo+HTFk5vwHkLjMtDB+0zM5y6AyU6z+PaCDr3Q7+vcR50kQ
+ LNxpSWH1Il1gBewIHAroN4lJjPvKLnDWvK5SsxW9XB14gLAyQg6qCHDYOdTsdiG6
+ BFG1eijT+3W7iXm8uTDiXSj+PiADEeKvnbEsWqXu+DvUsR/m8wIDAQABo4HzMIHw
+ MBMGA1UdJQQMMAoGCCsGAQUFBwMCMB0GA1UdDgQWBBTujPN/magozRJ8XJxAVYPw
+ v5/HsDCBqQYDVR0jBIGhMIGegBQ2QKUf9O24xKZrWTyUqRmZeq04IqF0pHIwcDEL
+ MAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzERMA8GA1UEBxMIV2FsbHNlbmQxGDAW
+ BgNVBAoTD1FWYWxlbnQgUHR5IEx0ZDETMBEGA1UECxMKT3BlcmF0aW9uczERMA8G
+ A1UEAxMIZVF2YWxlbnSCEG49SZXSVViNQE7XcOvVjwYwDgYDVR0PAQH/BAQDAgTw
+ MA0GCSqGSIb3DQEBBQUAA4IBAQCQwdv8D4B5sRnA9/ppDfwlix6omzh/SdA3SI4m
+ BNcImoqzBEq1OokdWKDkRQXlP6822aJn8fzDMV1/YsmomC4fT0wdag0DvBEvRlhy
+ roQRFjQai6CKRUgoH/p0q4EwxKLOa/H0kix+cn6Nszl07wu6YtHzvISXf4MC72au
+ /xTf/qwXI1uXnQghb4sFM9/ubYlsNEFiNHt7CHBK7ivhFGcT7eI9rmSOTMKsZfmV
+ pALJ58Ynz08xLYRMq54FxzwhN3CZ7AWRA+9JpzJacJ6lzHX9Y4FAjzRzoqn5h/IN
+ dgMIW+HoTgCe4+M1aDx2A0SKJCSK9tYZCYSsPMi9JXdLDU+k
+ -----END CERTIFICATE-----
+ Bag Attributes
+ localKeyID: 02 00 00 00
+ subject=/CN=eQvalent/OU=Operations/O=QValent Pty Ltd/L=Wallsend/ST=NSW/C=AU
+ issuer=/CN=eQvalent/OU=Operations/O=QValent Pty Ltd/L=Wallsend/ST=NSW/C=AU
+ -----BEGIN CERTIFICATE-----
+ MIIDuzCCAqOgAwIBAgIQbj1JldJVWI1ATtdw69WPBjANBgkqhkiG9w0BAQUFADBw
+ MQswCQYDVQQGEwJBVTEMMAoGA1UECBMDTlNXMREwDwYDVQQHEwhXYWxsc2VuZDEY
+ MBYGA1UEChMPUVZhbGVudCBQdHkgTHRkMRMwEQYDVQQLEwpPcGVyYXRpb25zMREw
+ DwYDVQQDEwhlUXZhbGVudDAeFw0xMDA0MDcwNTUyMThaFw0zMDA0MDcwNjAyMTda
+ MHAxCzAJBgNVBAYTAkFVMQwwCgYDVQQIEwNOU1cxETAPBgNVBAcTCFdhbGxzZW5k
+ MRgwFgYDVQQKEw9RVmFsZW50IFB0eSBMdGQxEzARBgNVBAsTCk9wZXJhdGlvbnMx
+ ETAPBgNVBAMTCGVRdmFsZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+ AQEAxKuwonFlg53dyb0zFyS0JPUbxKmHwBDZcTT78eV4hs9TveOR34yT9xv+y7R1
+ GbUdtXxidGaXW6lfkWvVnS1mkDbj84OY2FzRTHeKuNmkcPddQDUgE+gaOGV6GKW7
+ v/s5jMt6pz075teLKvNO2Gs2alpPl4NYU5zJfa+AGFzqE1z7Zxbl9N/Sc6Y8ZbWI
+ IGWKJFeaTic8tE05hIGuWw1KfxbhH0QMKnTSzW5fJi4Pce3iIftkCTjIXdTg+El8
+ 9b+Jn5VMk+bQZQiusPbPbHZTvAADX/WCUuzAlFqyyNtutJmUlD/TiEB12YYp9R6U
+ 1MGRvC9+c+sz2bBccW3c+Zm7cQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0T
+ AQH/BAUwAwEB/zAdBgNVHQ4EFgQUNkClH/TtuMSma1k8lKkZmXqtOCIwEAYJKwYB
+ BAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBAAJRMGZzxO3lcTXoFdmOpo8P
+ Ptf/agwrlGT8krwKcZt42YZ98VoIaCTI83kkACYlTlkTTNkDdHtAwF7ROteZmbfQ
+ 5ky0nqZTpdQ2IPfGXpxcMIrGqrSJ6GbLTxrm0kXDH6TgYeY1dVTHtJD9wEgWwdI9
+ coRkKQVjK/QucPuVX0uoRppCz0rCiJfCQf2825BrcbaY4HyDHvNxRXKV9ECuYwrk
+ HYDy3bnYWVB2ekk8xOEiocjQI3T//V36ZfiGubKlUZ4xsSed410hkLtB6ttCyZB1
+ RFjxWKtn9pXbM1PLmUXCkKQnSJSeD1K0NjV+g8KFChTEgmhnLogyF/7YYw/amfc=
+ -----END CERTIFICATE-----
raven_pac_net:
user: ernest
@@ -824,64 +983,69 @@ realex:
password: Y
realex_mastercard:
- number:
+ number: '5425230000004415'
month: '6'
year: '2020'
verification_value: '123'
+ brand: 'master'
realex_mastercard_coms_error:
- number:
+ number: '5135020000005871'
month: '6'
year: '2020'
verification_value: '123'
+ brand: 'master'
realex_mastercard_declined:
- number:
+ number: '5114610000004778'
month: '6'
year: '2020'
verification_value: '123'
+ brand: 'master'
realex_mastercard_referral_a:
- number:
+ number: '5121220000006921'
month: '6'
year: '2020'
verification_value: '123'
+ brand: 'master'
realex_mastercard_referral_b:
- number:
+ number: '5114630000009791'
month: '6'
year: '2020'
verification_value: '123'
+ brand: 'master'
# Realex doesn't provide public testing data
# Fill in the card numbers with the Realex test
# data.
realex_visa:
- number:
+ number: '4263970000005262'
month: '6'
year: '2020'
verification_value: '123'
realex_visa_coms_error:
- number:
+ number: '4009830000001985'
month: '6'
year: '2020'
verification_value: '123'
realex_visa_declined:
- number:
+ number: '4000120000001154'
month: '6'
year: '2020'
verification_value: '123'
realex_visa_referral_a:
- number:
+ number: '4000160000004147'
month: '6'
year: '2020'
verification_value: '123'
realex_visa_referral_b:
- number:
+ number: '4000130000001724'
month: '6'
year: '2020'
verification_value: '123'
@@ -908,12 +1072,21 @@ s5:
login: 8a82941847c4d0780147cea1d1730dcc
password: n3yNMBGK
+# Working credentials, no need to replace
+safe_charge:
+ client_login_id: 'SpreedlyTestTRX'
+ client_password: '5Jp5xKmgqY'
+
+safe_charge_three_ds:
+ client_login_id: 'SpreedlyManTestTRX'
+ client_password: 'iGx9DQQHQG'
+
sage:
login: 214282982451
password: 'Z5W2S8J7X8T5'
sage_pay:
- login: LOGIN
+ login: spreedly
sallie_mae:
login: TEST0
@@ -960,16 +1133,6 @@ stripe:
stripe_destination:
stripe_user_id: "acct_17FRNfIPBJTitsen"
-# Used only for remote tests that use EMV credit card mocks
-stripe_emv_uk:
- login:
- fee_refund_login:
-
-# Used only for remote tests that use EMV credit card mocks
-stripe_emv_us:
- login:
- fee_refund_login:
-
# Externally verified bank account for testing
stripe_verified_bank_account:
customer_id: "cus_7s22nNueP2Hjj6"
@@ -1017,6 +1180,10 @@ transax:
login: transaxdemo
password: nelix123
+# Working credentials, no need to replace
+trexle:
+ api_key: "J5RGMpDlFlTfv9mEFvNWYoqHufyukPP4"
+
# Working credentials, no need to replace
trust_commerce:
login: 'TestMerchant'
@@ -1032,6 +1199,12 @@ usa_epay_advanced:
password: Y
software_id: Z
+# Working credentials, no need to replace
+vacaypay:
+ api_key: 'SGD0qydBXp58i0n5QHnTG38D-OOzvDu0KlVliOhZpyw'
+ account_uuid: '0b72d273-5caf-4a4d-aaf3-3c18267e213e'
+ publishable_key: nil # Optional, if left blank will be retrieved automatically
+
valitor:
login: WebsiteID
password: SecurityNumber
@@ -1065,7 +1238,7 @@ webpay:
wepay:
client_id: "44716"
account_id: "2080478981"
- access_token: "STAGE_67d2e41067af064af698e9cdc185c7570e4cb3191de04d8d092357c2a9120b6c"
+ access_token: "STAGE_c91882b0bed3584b8aed0f7f515f2f05a1d40924ee6f394ce82d91018cb0f2d3"
client_secret: "d48fefe743"
# Working test credentials with AVS/CVV support, no need to replace
@@ -1085,8 +1258,12 @@ world_net:
secret: 'sandboxEUR'
world_pay_gateway:
- login: LOGIN
- password: PASSWORD
+ login: 'SPREEDLY'
+ password: 'KZ#P2aR+'
+
+world_pay_gateway_cft:
+ login: 'SPREEDLYCFT'
+ password: 'Xbf+6#pD'
worldpay_online_payments:
client_key: "T_C_b9f629e7-cea7-4edb-8206-24bbe351d699"
diff --git a/test/remote/gateways/remote_adyen_test.rb b/test/remote/gateways/remote_adyen_test.rb
new file mode 100644
index 00000000000..21232d33bed
--- /dev/null
+++ b/test/remote/gateways/remote_adyen_test.rb
@@ -0,0 +1,260 @@
+require 'test_helper'
+
+class RemoteAdyenTest < Test::Unit::TestCase
+ def setup
+ @gateway = AdyenGateway.new(fixtures(:adyen))
+
+ @amount = 100
+
+ @credit_card = credit_card('4111111111111111',
+ :month => 8,
+ :year => 2018,
+ :first_name => 'John',
+ :last_name => 'Smith',
+ :verification_value => '737',
+ :brand => 'visa'
+ )
+
+ @declined_card = credit_card('4000300011112220')
+
+ @options = {
+ reference: '345123',
+ shopper_email: "john.smith@test.com",
+ shopper_ip: "77.110.174.153",
+ shopper_reference: "John Smith",
+ billing_address: address(),
+ order_id: "123"
+ }
+ end
+
+ def test_successful_authorize
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'Authorised', response.message
+ end
+
+ def test_failed_authorize
+ response = @gateway.authorize(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal 'Refused', response.message
+ end
+
+ def test_successful_purchase
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal '[capture-received]', response.message
+ end
+
+ def test_successful_purchase_no_cvv
+ credit_card = @credit_card
+ credit_card.verification_value = nil
+ response = @gateway.purchase(@amount, credit_card, @options)
+ assert_success response
+ assert_equal '[capture-received]', response.message
+ end
+
+ def test_successful_purchase_with_more_options
+ options = @options.merge!(fraudOffset: '1')
+ response = @gateway.purchase(@amount, @credit_card, options)
+ assert_success response
+ assert_equal '[capture-received]', response.message
+ end
+
+ def test_failed_purchase
+ response = @gateway.purchase(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal 'Refused', response.message
+ end
+
+ def test_successful_authorize_and_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount, auth.authorization)
+ assert_success capture
+ assert_equal '[capture-received]', capture.message
+ end
+
+ def test_partial_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount-1, auth.authorization)
+ assert_success capture
+ end
+
+ def test_failed_capture
+ response = @gateway.capture(@amount, '')
+ assert_failure response
+ assert_equal 'Original pspReference required for this operation', response.message
+ end
+
+ def test_successful_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount, purchase.authorization)
+ assert_success refund
+ assert_equal '[refund-received]', refund.message
+ end
+
+ def test_partial_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount-1, purchase.authorization)
+ assert_success refund
+ end
+
+ def test_failed_refund
+ response = @gateway.refund(@amount, '')
+ assert_failure response
+ assert_equal 'Original pspReference required for this operation', response.message
+ end
+
+ def test_successful_void
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert void = @gateway.void(auth.authorization)
+ assert_success void
+ assert_equal '[cancel-received]', void.message
+ end
+
+ def test_failed_void
+ response = @gateway.void('')
+ assert_failure response
+ assert_equal 'Original pspReference required for this operation', response.message
+ end
+
+ def test_successful_store
+ assert response = @gateway.store(@credit_card, @options)
+
+ assert_success response
+ assert !response.authorization.split("#")[2].nil?
+ assert_equal 'Authorised', response.message
+ end
+
+ def test_failed_store
+ assert response = @gateway.store(@declined_card, @options)
+
+ assert_failure response
+ assert_equal 'Refused', response.message
+ end
+
+ def test_successful_purchase_using_stored_card
+ assert store_response = @gateway.store(@credit_card, @options)
+ assert_success store_response
+
+ response = @gateway.purchase(@amount, store_response.authorization, @options)
+ assert_success response
+ assert_equal '[capture-received]', response.message
+ end
+
+ def test_successful_authorize_using_stored_card
+ assert store_response = @gateway.store(@credit_card, @options)
+ assert_success store_response
+
+ response = @gateway.authorize(@amount, store_response.authorization, @options)
+ assert_success response
+ assert_equal 'Authorised', response.message
+ end
+
+ def test_successful_verify
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ assert_match 'Authorised', response.message
+ end
+
+ def test_failed_verify
+ response = @gateway.verify(@declined_card, @options)
+ assert_failure response
+ assert_match 'Refused', response.message
+ end
+
+ def test_invalid_login
+ gateway = AdyenGateway.new(username: '', password: '', merchant_account: '')
+
+ response = gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
+
+ def test_incorrect_number_for_purchase
+ card = credit_card('4242424242424241')
+ assert response = @gateway.purchase(@amount, card, @options)
+ assert_failure response
+ assert_match Gateway::STANDARD_ERROR_CODE[:incorrect_number], response.error_code
+ end
+
+ def test_invalid_number_for_purchase
+ card = credit_card('-1')
+ assert response = @gateway.purchase(@amount, card, @options)
+ assert_failure response
+ assert_match Gateway::STANDARD_ERROR_CODE[:incorrect_number], response.error_code
+ end
+
+ def test_invalid_expiry_month_for_purchase
+ card = credit_card('4242424242424242', month: 16)
+ assert response = @gateway.purchase(@amount, card, @options)
+ assert_failure response
+ assert_equal 'Expiry month should be between 1 and 12 inclusive', response.message
+ end
+
+ def test_invalid_expiry_year_for_purchase
+ card = credit_card('4242424242424242', year: 'xx')
+ assert response = @gateway.purchase(@amount, card, @options)
+ assert_failure response
+ assert response.message.include?('Expiry year should be a 4 digit number greater than')
+ end
+
+ def test_invalid_cvc_for_purchase
+ card = credit_card('4242424242424242', verification_value: -1)
+ assert response = @gateway.purchase(@amount, card, @options)
+ assert_failure response
+ assert_match Gateway::STANDARD_ERROR_CODE[:invalid_cvc], response.error_code
+ end
+
+ def test_missing_address_for_purchase
+ @options[:billing_address].delete(:address1)
+ @options[:billing_address].delete(:address2)
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+ end
+
+ def test_missing_city_for_purchase
+ @options[:billing_address].delete(:city)
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+ end
+
+ def test_missing_house_number_or_name_for_purchase
+ @options[:billing_address].delete(:address2)
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+ end
+
+ def test_invalid_country_for_purchase
+ @options[:billing_address][:country] = ''
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_failure response
+ assert_match Gateway::STANDARD_ERROR_CODE[:incorrect_address], response.error_code
+ end
+
+ def test_invalid_state_for_purchase
+ @options[:billing_address][:state] = ''
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_failure response
+ assert_match Gateway::STANDARD_ERROR_CODE[:incorrect_address], response.error_code
+ end
+end
diff --git a/test/remote/gateways/remote_authorize_net_test.rb b/test/remote/gateways/remote_authorize_net_test.rb
index 0b6add8854e..eb396551373 100644
--- a/test/remote/gateways/remote_authorize_net_test.rb
+++ b/test/remote/gateways/remote_authorize_net_test.rb
@@ -11,10 +11,31 @@ def setup
@options = {
order_id: '1',
+ email: 'anet@example.com',
duplicate_window: 0,
billing_address: address,
description: 'Store Purchase'
}
+
+ @level_2_options = {
+ tax: {
+ amount: "100",
+ name: "tax name",
+ description: "tax description"
+ },
+ duty: {
+ amount: "200",
+ name: "duty name",
+ description: "duty description"
+ },
+ shipping: {
+ amount: "300",
+ name: "shipping name",
+ description: "shipping description",
+ },
+ tax_exempt: "false",
+ po_number: "123"
+ }
end
def test_successful_purchase
@@ -26,13 +47,68 @@ def test_successful_purchase
end
def test_successful_purchase_with_minimal_options
- response = @gateway.purchase(@amount, @credit_card, duplicate_window: 0)
+ response = @gateway.purchase(@amount, @credit_card, duplicate_window: 0, email: 'anet@example.com', billing_address: address)
+ assert_success response
+ assert response.test?
+ assert_equal 'This transaction has been approved', response.message
+ assert response.authorization
+ end
+
+ def test_successful_purchase_with_email_customer
+ response = @gateway.purchase(@amount, @credit_card, duplicate_window: 0, email_customer: true, email: 'anet@example.com', billing_address: address)
assert_success response
assert response.test?
assert_equal 'This transaction has been approved', response.message
assert response.authorization
end
+ def test_successful_purchase_with_header_email_receipt
+ response = @gateway.purchase(@amount, @credit_card, duplicate_window: 0, header_email_receipt: "subject line", email: 'anet@example.com', billing_address: address)
+ assert_success response
+ assert response.test?
+ assert_equal 'This transaction has been approved', response.message
+ assert response.authorization
+ end
+
+ def test_successful_purchase_with_line_items
+ additional_options = {
+ email: "anet@example.com",
+ line_items: [
+ {
+ item_id: "1",
+ name: "mug",
+ description: "coffee",
+ quantity: "100",
+ unit_price: "10"
+ },
+ {
+ item_id: "2",
+ name: "vase",
+ description: "floral",
+ quantity: "200",
+ unit_price: "20"
+ }
+ ]
+ }
+ response = @gateway.purchase(@amount, @credit_card, @options.merge(additional_options))
+ assert_success response
+ assert response.test?
+ assert_equal 'This transaction has been approved', response.message
+ assert response.authorization
+ end
+
+ def test_successful_purchase_with_level_2_data
+ response = @gateway.purchase(@amount, @credit_card, @options.merge(@level_2_options))
+ assert_success response
+ assert_equal 'This transaction has been approved', response.message
+ end
+
+ def test_successful_purchase_with_customer
+ response = @gateway.purchase(@amount, @credit_card, @options.merge(customer: "abcd_123"))
+ assert_success response
+ assert_equal 'This transaction has been approved', response.message
+ end
+
def test_failed_purchase
response = @gateway.purchase(@amount, @declined_card, @options)
assert_failure response
@@ -230,7 +306,7 @@ def test_failed_store
end
def test_successful_purchase_using_stored_card
- response = @gateway.store(@credit_card)
+ response = @gateway.store(@credit_card, @options)
assert_success response
response = @gateway.purchase(@amount, response.authorization, @options)
@@ -248,18 +324,18 @@ def test_failed_purchase_using_stored_card
assert_equal "incorrect_number", response.error_code
assert_equal "27", response.params["message_code"]
assert_equal "6", response.params["response_reason_code"]
- assert_match /but street address not verified/, response.avs_result["message"]
+ assert_match %r{Address not verified}, response.avs_result["message"]
end
def test_successful_purchase_using_stored_card_new_payment_profile
- assert store = @gateway.store(@credit_card)
+ assert store = @gateway.store(@credit_card, @options)
assert_success store
assert store.authorization
new_card = credit_card('4007000000027')
customer_profile_id, _, _ = store.authorization.split("#")
- assert response = @gateway.store(new_card, customer_profile_id: customer_profile_id)
+ assert response = @gateway.store(new_card, customer_profile_id: customer_profile_id, email: 'anet@example.com', billing_address: address)
assert_success response
response = @gateway.purchase(@amount, response.authorization, @options)
@@ -267,15 +343,37 @@ def test_successful_purchase_using_stored_card_new_payment_profile
assert_equal "This transaction has been approved.", response.message
end
+ def test_successful_purchase_with_stored_card_and_level_2_data
+ store_response = @gateway.store(@credit_card, @options)
+ assert_success store_response
+
+ response = @gateway.purchase(@amount, store_response.authorization, @options.merge(@level_2_options))
+ assert_success response
+ assert_equal 'This transaction has been approved.', response.message
+ end
+
def test_successful_authorize_and_capture_using_stored_card
- store = @gateway.store(@credit_card)
+ store = @gateway.store(@credit_card, @options)
assert_success store
auth = @gateway.authorize(@amount, store.authorization, @options)
assert_success auth
assert_equal "This transaction has been approved.", auth.message
- capture = @gateway.capture(@amount, auth.authorization)
+ capture = @gateway.capture(@amount, auth.authorization, @options)
+ assert_success capture
+ assert_equal "This transaction has been approved.", capture.message
+ end
+
+ def test_successful_authorize_and_capture_using_stored_card_with_level_2_data
+ store = @gateway.store(@credit_card, @options)
+ assert_success store
+
+ auth = @gateway.authorize(@amount, store.authorization, @options.merge(@level_2_options))
+ assert_success auth
+ assert_equal "This transaction has been approved.", auth.message
+
+ capture = @gateway.capture(@amount, auth.authorization, @options.merge(@level_2_options))
assert_success capture
assert_equal "This transaction has been approved.", capture.message
end
@@ -291,23 +389,43 @@ def test_failed_authorize_using_stored_card
assert_equal "incorrect_number", response.error_code
assert_equal "27", response.params["message_code"]
assert_equal "6", response.params["response_reason_code"]
- assert_match /but street address not verified/, response.avs_result["message"]
+ assert_match %r{Address not verified}, response.avs_result["message"]
end
def test_failed_capture_using_stored_card
- store = @gateway.store(@credit_card)
+ store = @gateway.store(@credit_card, @options)
assert_success store
auth = @gateway.authorize(@amount, store.authorization, @options)
assert_success auth
- capture = @gateway.capture(@amount + 4000, auth.authorization)
+ capture = @gateway.capture(@amount + 4000, auth.authorization, @options)
assert_failure capture
- assert_match /The amount requested for settlement cannot be greater/, capture.message
+ assert_match %r{The amount requested for settlement cannot be greater}, capture.message
+ end
+
+ def test_faux_successful_refund_with_billing_address
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ refund = @gateway.refund(@amount, purchase.authorization, @options.merge(first_name: 'Jim', last_name: 'Smith'))
+ assert_failure refund
+ assert_match %r{does not meet the criteria for issuing a credit}, refund.message, "Only allowed to refund transactions that have settled. This is the best we can do for now testing wise."
+ end
+
+ def test_faux_successful_refund_without_billing_address
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ @options[:billing_address] = nil
+
+ refund = @gateway.refund(@amount, purchase.authorization, @options.merge(first_name: 'Jim', last_name: 'Smith'))
+ assert_failure refund
+ assert_match %r{does not meet the criteria for issuing a credit}, refund.message, "Only allowed to refund transactions that have settled. This is the best we can do for now testing wise."
end
def test_faux_successful_refund_using_stored_card
- store = @gateway.store(@credit_card)
+ store = @gateway.store(@credit_card, @options)
assert_success store
purchase = @gateway.purchase(@amount, store.authorization, @options)
@@ -315,11 +433,23 @@ def test_faux_successful_refund_using_stored_card
refund = @gateway.refund(@amount, purchase.authorization, @options)
assert_failure refund
- assert_match /does not meet the criteria for issuing a credit/, refund.message, "Only allowed to refund transactions that have settled. This is the best we can do for now testing wise."
+ assert_match %r{does not meet the criteria for issuing a credit}, refund.message, "Only allowed to refund transactions that have settled. This is the best we can do for now testing wise."
+ end
+
+ def test_faux_successful_refund_using_stored_card_and_level_2_data
+ store = @gateway.store(@credit_card, @options)
+ assert_success store
+
+ purchase = @gateway.purchase(@amount, store.authorization, @options.merge(@level_2_options))
+ assert_success purchase
+
+ refund = @gateway.refund(@amount, purchase.authorization, @options.merge(@level_2_options))
+ assert_failure refund
+ assert_match %r{does not meet the criteria for issuing a credit}, refund.message, "Only allowed to refund transactions that have settled. This is the best we can do for now testing wise."
end
def test_failed_refund_using_stored_card
- store = @gateway.store(@credit_card)
+ store = @gateway.store(@credit_card, @options)
assert_success store
purchase = @gateway.purchase(@amount, store.authorization, @options)
@@ -332,28 +462,28 @@ def test_failed_refund_using_stored_card
end
def test_successful_void_using_stored_card
- store = @gateway.store(@credit_card)
+ store = @gateway.store(@credit_card, @options)
assert_success store
auth = @gateway.authorize(@amount, store.authorization, @options)
assert_success auth
- void = @gateway.void(auth.authorization)
+ void = @gateway.void(auth.authorization, @options)
assert_success void
assert_equal "This transaction has been approved.", void.message
end
def test_failed_void_using_stored_card
- store = @gateway.store(@credit_card)
+ store = @gateway.store(@credit_card, @options)
assert_success store
auth = @gateway.authorize(@amount, store.authorization, @options)
assert_success auth
- void = @gateway.void(auth.authorization)
+ void = @gateway.void(auth.authorization, @options)
assert_success void
- another_void = @gateway.void(auth.authorization)
+ another_void = @gateway.void(auth.authorization, @options)
assert_failure another_void
assert_equal "This transaction has already been voided.", another_void.message
end
@@ -439,6 +569,16 @@ def test_successful_echeck_credit
assert response.authorization
end
+ def test_successful_echeck_refund
+ purchase = @gateway.purchase(@amount, @check, @options)
+ assert_success purchase
+
+ @options.update(transaction_id: purchase.params['transaction_id'], test_request: true)
+ refund = @gateway.credit(@amount, @check, @options)
+ assert_failure refund
+ assert_match %r{The transaction cannot be found}, refund.message, "Only allowed to refund transactions that have settled. This is the best we can do for now testing wise."
+ end
+
def test_failed_credit
response = @gateway.credit(@amount, @declined_card, @options)
assert_failure response
@@ -476,6 +616,34 @@ def test_successful_authorize_and_capture_with_network_tokenization
assert_success capture
end
+ def test_successful_refund_with_network_tokenization
+ credit_card = network_tokenization_credit_card('4000100011112224',
+ payment_cryptogram: "EHuWW9PiBkWvqE5juRwDzAUFBAk=",
+ verification_value: nil
+ )
+
+ purchase = @gateway.purchase(@amount, credit_card, @options)
+ assert_success purchase
+
+ @options[:billing_address] = nil
+
+ refund = @gateway.refund(@amount, purchase.authorization, @options.merge(first_name: 'Jim', last_name: 'Smith'))
+ assert_failure refund
+ assert_match %r{does not meet the criteria for issuing a credit}, refund.message, "Only allowed to refund transactions that have settled. This is the best we can do for now testing wise."
+ end
+
+ def test_successful_credit_with_network_tokenization
+ credit_card = network_tokenization_credit_card('4000100011112224',
+ payment_cryptogram: "EHuWW9PiBkWvqE5juRwDzAUFBAk=",
+ verification_value: nil
+ )
+
+ response = @gateway.credit(@amount, credit_card, @options)
+ assert_success response
+ assert_equal 'This transaction has been approved', response.message
+ assert response.authorization
+ end
+
def test_network_tokenization_transcript_scrubbing
credit_card = network_tokenization_credit_card('4111111111111111',
:brand => 'visa',
diff --git a/test/remote/gateways/remote_barclaycard_smartpay_test.rb b/test/remote/gateways/remote_barclaycard_smartpay_test.rb
index 40b565b8cde..96b6127f41f 100644
--- a/test/remote/gateways/remote_barclaycard_smartpay_test.rb
+++ b/test/remote/gateways/remote_barclaycard_smartpay_test.rb
@@ -3,10 +3,12 @@
class RemoteBarclaycardSmartpayTest < Test::Unit::TestCase
def setup
@gateway = BarclaycardSmartpayGateway.new(fixtures(:barclaycard_smartpay))
+ BarclaycardSmartpayGateway.ssl_strict = false
@amount = 100
@credit_card = credit_card('4111111111111111', :month => 8, :year => 2018, :verification_value => 737)
@declined_card = credit_card('4000300011112220', :month => 8, :year => 2018, :verification_value => 737)
+ @three_ds_enrolled_card = credit_card('4212345678901237', brand: :visa)
@options = {
order_id: '1',
@@ -25,12 +27,81 @@ def setup
description: 'Store Purchase'
}
+ @options_with_alternate_address = {
+ order_id: '1',
+ billing_address: {
+ name: 'PU JOI SO',
+ address1: '新北市店溪路3579號139樓',
+ company: 'Widgets Inc',
+ city: '新北市',
+ zip: '231509',
+ country: 'TW',
+ phone: '(555)555-5555',
+ fax: '(555)555-6666'
+ },
+ email: 'pujoi@so.com',
+ customer: 'PU JOI SO',
+ description: 'Store Purchase'
+ }
+
+ @options_with_house_number_and_street = {
+ order_id: '1',
+ house_number: '100',
+ street: 'Top Level Drive',
+ billing_address: {
+ name: 'Jim Smith',
+ address1: '100 Top Level Dr',
+ company: 'Widgets Inc',
+ city: 'Ottawa',
+ state: 'ON',
+ zip: 'K1C2N6',
+ country: 'CA',
+ phone: '(555)555-5555',
+ fax: '(555)555-6666'
+ },
+ email: 'long@deb.com',
+ customer: 'Longdeb Longsen',
+ description: 'Store Purchase'
+ }
+
+ @options_with_no_address = {
+ order_id: '1',
+ email: 'long@bob.com',
+ customer: 'Longbob Longsen',
+ description: 'Store Purchase'
+ }
+
+ @options_with_credit_fields = {
+ order_id: '1',
+ billing_address: {
+ name: 'Jim Smith',
+ address1: '100 Street',
+ company: 'Widgets Inc',
+ city: 'Ottawa',
+ state: 'ON',
+ zip: 'K1C2N6',
+ country: 'CA',
+ phone: '(555)555-5555',
+ fax: '(555)555-6666'},
+ email: 'long@bob.com',
+ customer: 'Longbob Longsen',
+ description: 'Store Purchase',
+ date_of_birth: '1990-10-11',
+ entity_type: 'NaturalPerson',
+ nationality: 'US',
+ shopper_name: {
+ firstName: 'Longbob',
+ lastName: 'Longsen',
+ gender: 'MALE'
+ }
+ }
+
@avs_credit_card = credit_card('4400000000000008',
:month => 8,
:year => 2018,
:verification_value => 737)
- @avs_address = @options
+ @avs_address = @options.clone
@avs_address.update(billing_address: {
name: 'Jim Smith',
street: 'Test AVS result',
@@ -42,6 +113,10 @@ def setup
})
end
+ def teardown
+ BarclaycardSmartpayGateway.ssl_strict = true
+ end
+
def test_successful_purchase
response = @gateway.purchase(@amount, @credit_card, @options)
assert_success response
@@ -54,6 +129,40 @@ def test_failed_purchase
assert_equal 'Refused', response.message
end
+ def test_successful_purchase_with_unusual_address
+ response = @gateway.purchase(@amount,
+ @credit_card,
+ @options_with_alternate_address)
+ assert_success response
+ assert_equal '[capture-received]', response.message
+ end
+
+ def test_successful_purchase_with_house_number_and_street
+ response = @gateway.purchase(@amount,
+ @credit_card,
+ @options.merge(street: 'Top Level Drive', house_number: '100'))
+ assert_success response
+ assert_equal '[capture-received]', response.message
+ end
+
+ def test_successful_purchase_with_no_address
+ response = @gateway.purchase(@amount,
+ @credit_card,
+ @options_with_no_address)
+ assert_success response
+ assert_equal '[capture-received]', response.message
+ end
+
+ def test_successful_authorize_with_3ds
+ assert response = @gateway.authorize(@amount, @three_ds_enrolled_card, @options.merge(execute_threed: true))
+ assert_equal 'RedirectShopper', response.message
+ assert response.test?
+ refute response.authorization.blank?
+ refute response.params['issuerUrl'].blank?
+ refute response.params['md'].blank?
+ refute response.params['paRequest'].blank?
+ end
+
def test_successful_authorize_and_capture
auth = @gateway.authorize(@amount, @credit_card, @options)
assert_success auth
@@ -101,6 +210,22 @@ def test_failed_refund
assert_failure response
end
+ def test_successful_credit
+ response = @gateway.credit(@amount, @credit_card, @options_with_credit_fields)
+ assert_success response
+ end
+
+ def test_failed_credit
+ response = @gateway.credit(nil, @declined_card, @options)
+ assert_failure response
+ end
+
+ def test_failed_credit_insufficient_validation
+ # This test will fail currently (the credit will succeed), but it should succeed after October 29th
+ # response = @gateway.credit(@amount, @credit_card, @options)
+ # assert_failure response
+ end
+
def test_successful_void
auth = @gateway.authorize(@amount, @credit_card, @options)
assert_success auth
@@ -156,6 +281,28 @@ def test_avs_result
assert_equal 'N', response.avs_result['code']
end
+ def test_avs_no_with_house_number
+ avs_nohousenumber = @avs_address
+ avs_nohousenumber[:billing_address].delete(:houseNumberOrName)
+ response = @gateway.authorize(@amount, @avs_credit_card, avs_nohousenumber)
+ assert_equal 'Z', response.avs_result['code']
+ end
+
+ def test_nonfractional_currency
+ response = @gateway.authorize(1234, @credit_card, @options.merge(:currency => 'JPY'))
+ assert_success response
+ response = @gateway.purchase(1234, @credit_card, @options.merge(:currency => 'JPY'))
+ assert_success response
+ end
+
+ def test_three_decimal_currency
+ response = @gateway.authorize(1234, @credit_card, @options.merge(:currency => 'OMR'))
+ assert_success response
+
+ response = @gateway.purchase(1234, @credit_card, @options.merge(:currency => 'OMR'))
+ assert_success response
+ end
+
def test_transcript_scrubbing
transcript = capture_transcript(@gateway) do
@gateway.purchase(@amount, @credit_card, @options)
diff --git a/test/remote/gateways/remote_barclays_epdq_extra_plus_test.rb b/test/remote/gateways/remote_barclays_epdq_extra_plus_test.rb
index b8fb97c8265..01c575ce95d 100644
--- a/test/remote/gateways/remote_barclays_epdq_extra_plus_test.rb
+++ b/test/remote/gateways/remote_barclays_epdq_extra_plus_test.rb
@@ -5,7 +5,7 @@ class RemoteBarclaysEpdqExtraPlusTest < Test::Unit::TestCase
def setup
@gateway = BarclaysEpdqExtraPlusGateway.new(fixtures(:barclays_epdq_extra_plus))
@amount = 100
- @credit_card = credit_card('4000100011112224')
+ @credit_card = credit_card('4000100011112224', :verification_value => '987')
@mastercard = credit_card('5399999999999999', :brand => "mastercard")
@declined_card = credit_card('1111111111111111')
@credit_card_d3d = credit_card('4000000000000002', :verification_value => '111')
@@ -224,4 +224,14 @@ def test_invalid_login
assert_failure response
assert_equal 'Some of the data entered is incorrect. please retry.', response.message
end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ end
end
diff --git a/test/remote/gateways/remote_barclays_epdq_test.rb b/test/remote/gateways/remote_barclays_epdq_test.rb
deleted file mode 100644
index 346390280c8..00000000000
--- a/test/remote/gateways/remote_barclays_epdq_test.rb
+++ /dev/null
@@ -1,212 +0,0 @@
-require 'test_helper'
-
-class RemoteBarclaysEpdqTest < Test::Unit::TestCase
- def setup
- @gateway = BarclaysEpdqGateway.new(fixtures(:barclays_epdq).merge(:test => true))
-
- @approved_amount = 3900
- @declined_amount = 4205
- @approved_card = credit_card('4715320629000001')
- @declined_card = credit_card('4715320629000027')
-
- @options = {
- :order_id => generate_unique_id,
- :billing_address => address,
- :description => 'Store Purchase'
- }
-
- @periodic_options = @options.merge(
- :payment_number => 1,
- :total_payments => 3,
- :group_id => 'MyTestPaymentGroup'
- )
- end
-
- def test_successful_purchase
- assert response = @gateway.purchase(@approved_amount, @approved_card, @options)
- assert_success response
- assert_equal 'Approved.', response.message
- assert_equal @options[:order_id], response.authorization
- assert_no_match(/PaymentNoFraud/, response.params["raw_response"])
- end
-
- def test_successful_purchase_with_mastercard
- assert response = @gateway.purchase(@approved_amount, credit_card('5301250070000050', :brand => :master), @options)
- assert_success response
- end
-
- def test_successful_purchase_with_maestro
- assert response = @gateway.purchase(@approved_amount, credit_card('675938410597000022', :brand => :maestro, :issue_number => '5'), @options)
- assert_success response
- end
-
- def test_successful_purchase_with_switch
- assert response = @gateway.purchase(@approved_amount, credit_card('6759560045005727054', :brand => :switch, :issue_number => '1'), @options)
- assert_success response
- end
-
- def test_successful_purchase_with_minimal_options
- delete_address_details!
-
- assert response = @gateway.purchase(@approved_amount, @approved_card, @options)
- assert_success response
- assert_equal 'Approved.', response.message
- assert_equal @options[:order_id], response.authorization
- assert_no_match(/PaymentNoFraud/, response.params["raw_response"])
- end
-
- def test_successful_purchase_with_no_fraud
- @options[:no_fraud] = true
- assert response = @gateway.purchase(@approved_amount, @approved_card, @options)
- assert_success response
- assert_equal 'Approved.', response.message
- assert_equal @options[:order_id], response.authorization
- assert_match(/PaymentNoFraud/, response.params["raw_response"])
- end
-
- def test_successful_purchase_with_no_fraud_and_minimal_options
- delete_address_details!
-
- @options[:no_fraud] = true
- assert response = @gateway.purchase(@approved_amount, @approved_card, @options)
- assert_success response
- assert_equal 'Approved.', response.message
- assert_equal @options[:order_id], response.authorization
- assert_match(/PaymentNoFraud/, response.params["raw_response"])
- end
-
- def test_successful_purchase_with_no_address_or_order_id_or_description
- assert response = @gateway.purchase(@approved_amount, @approved_card, {})
- assert_success response
- assert_equal 'Approved.', response.message
- end
-
- def test_unsuccessful_purchase
- assert response = @gateway.purchase(@declined_amount, @declined_card, @options)
- assert_failure response
- assert_match(/^Declined/, response.message)
- end
-
- def test_credit_new_order
- assert response = @gateway.credit(@approved_amount, @approved_card, @options)
- assert_success response
- assert_equal 'Approved.', response.message
- end
-
- def test_refund_existing_order
- assert response = @gateway.purchase(@approved_amount, @approved_card, @options)
- assert_success response
-
- assert refund = @gateway.refund(@approved_amount, response.authorization)
- assert_success refund
- assert_equal 'Approved.', refund.message
- end
-
- def test_refund_nonexisting_order_fails
- assert refund = @gateway.refund(@approved_amount, "DOESNOTEXIST", @options)
- assert_failure refund
- assert_match(/^Payment Mechanism CreditCard information not found/, refund.message)
- end
-
- def test_authorize_and_capture
- amount = @approved_amount
- assert auth = @gateway.authorize(amount, @approved_card, @options)
- assert_success auth
- assert_equal 'Approved.', auth.message
- assert auth.authorization
- assert_equal @options[:order_id], auth.authorization
-
- assert capture = @gateway.capture(amount, auth.authorization)
- assert_success capture
- assert_equal 'Approved.', capture.message
- end
-
- def test_authorize_and_capture_without_order_id
- @options.delete(:order_id)
- amount = @approved_amount
- assert auth = @gateway.authorize(amount, @approved_card, @options)
- assert_success auth
- assert_equal 'Approved.', auth.message
- assert auth.authorization
- assert_match(/[0-9a-f\-]{36}/, auth.authorization)
-
- assert capture = @gateway.capture(amount, auth.authorization)
- assert_success capture
- assert_equal 'Approved.', capture.message
- end
-
- def test_authorize_void_and_failed_capture
- amount = @approved_amount
- assert auth = @gateway.authorize(amount, @approved_card, @options)
- assert_success auth
-
- assert void = @gateway.void(auth.authorization)
- assert_success void
- assert_equal 'Approved.', void.message
-
- assert capture = @gateway.capture(amount, auth.authorization)
- assert_failure capture
- assert_match(/^Did not find a unique, qualifying transaction for Order/, capture.message)
- end
-
- def test_failed_authorize
- assert auth = @gateway.authorize(@declined_amount, @approved_card, @options)
- assert_failure auth
- assert_match(/^Declined/, auth.message)
- end
-
- def test_failed_capture
- amount = @approved_amount
- assert auth = @gateway.authorize(amount, @approved_card, @options)
- assert_success auth
-
- @too_much = amount * 10
- assert capture = @gateway.capture(@too_much, auth.authorization)
- assert_success capture
- assert_match(/^The PostAuth is not valid because the amount/, capture.message)
- end
-
- def test_three_successful_periodic_orders
- amount = @approved_amount
- assert auth1 = @gateway.purchase(amount, @approved_card, @periodic_options)
- assert auth1.success?
- assert_equal 'Approved.', auth1.message
-
- @periodic_options[:payment_number] = 2
- @periodic_options[:order_id] = generate_unique_id
- assert auth2 = @gateway.purchase(amount, @approved_card, @periodic_options)
- assert auth2.success?
- assert_equal 'Approved.', auth2.message
-
- @periodic_options[:payment_number] = 3
- @periodic_options[:order_id] = generate_unique_id
- assert auth3 = @gateway.purchase(amount, @approved_card, @periodic_options)
- assert auth3.success?
- assert_equal 'Approved.', auth3.message
- end
-
- def test_invalid_login
- gateway = BarclaysEpdqGateway.new(
- :login => 'NOBODY',
- :password => 'HOME',
- :client_id => '1234'
- )
- assert response = gateway.purchase(@approved_amount, @approved_card, @options)
- assert_failure response
- assert_equal 'Insufficient permissions to perform requested operation.', response.message
- end
-
- protected
- def delete_address_details!
- @options[:billing_address].delete :city
- @options[:billing_address].delete :state
- @options[:billing_address].delete :country
- @options[:billing_address].delete :address1
- @options[:billing_address].delete :phone
- @options[:billing_address].delete :address1
- @options[:billing_address].delete :address2
- @options[:billing_address].delete :name
- @options[:billing_address].delete :fax
- @options[:billing_address].delete :company
- end
-end
diff --git a/test/remote/gateways/remote_beanstream_test.rb b/test/remote/gateways/remote_beanstream_test.rb
index 2362f197f62..9f382ef4d8b 100644
--- a/test/remote/gateways/remote_beanstream_test.rb
+++ b/test/remote/gateways/remote_beanstream_test.rb
@@ -13,12 +13,13 @@ def setup
# Beanstream test cards. Cards require a CVV of 123, which is the default of the credit card helper
@visa = credit_card('4030000010001234')
@declined_visa = credit_card('4003050500040005')
+ @visa_no_cvv = credit_card('4030000010001234', verification_value: nil)
@mastercard = credit_card('5100000010001004')
@declined_mastercard = credit_card('5100000020002000')
@amex = credit_card('371100001000131', {:verification_value => 1234})
- @declined_amex = credit_card('342400001000180')
+ @declined_amex = credit_card('342400001000180', {:verification_value => 1234})
# Canadian EFT
@check = check(
@@ -36,10 +37,20 @@ def setup
:address1 => '4444 Levesque St.',
:address2 => 'Apt B',
:city => 'Montreal',
- :state => 'QC',
+ :state => 'Quebec',
:country => 'CA',
:zip => 'H2C1X8'
},
+ :shipping_address => {
+ :name => 'shippy',
+ :phone => '888-888-8888',
+ :address1 => '777 Foster Street',
+ :address2 => 'Ste #100',
+ :city => 'Durham',
+ :state => 'North Carolina',
+ :country => 'US',
+ :zip => '27701'
+ },
:email => 'xiaobozzz@example.com',
:subtotal => 800,
:shipping => 100,
@@ -60,6 +71,20 @@ def test_successful_visa_purchase
assert_equal "Approved", response.message
end
+ def test_successful_visa_purchase_with_recurring
+ assert response = @gateway.purchase(@amount, @visa, @options.merge(recurring: true))
+ assert_success response
+ assert_false response.authorization.blank?
+ assert_equal "Approved", response.message
+ end
+
+ def test_successful_visa_purchase_no_cvv
+ assert response = @gateway.purchase(@amount, @visa_no_cvv, @options)
+ assert_success response
+ assert_false response.authorization.blank?
+ assert_equal "Approved", response.message
+ end
+
def test_unsuccessful_visa_purchase
assert response = @gateway.purchase(@amount, @declined_visa, @options)
assert_failure response
@@ -73,6 +98,13 @@ def test_successful_mastercard_purchase
assert_equal "Approved", response.message
end
+ def test_successful_mastercard_purchase_with_recurring
+ assert response = @gateway.purchase(@amount, @mastercard, @options.merge(recurring: true))
+ assert_success response
+ assert_false response.authorization.blank?
+ assert_equal "Approved", response.message
+ end
+
def test_unsuccessful_mastercard_purchase
assert response = @gateway.purchase(@amount, @declined_mastercard, @options)
assert_failure response
@@ -86,12 +118,69 @@ def test_successful_amex_purchase
assert_equal "Approved", response.message
end
+ def test_successful_amex_purchase_with_recurring
+ assert response = @gateway.purchase(@amount, @amex, @options.merge(recurring: true))
+ assert_success response
+ assert_false response.authorization.blank?
+ assert_equal "Approved", response.message
+ end
+
def test_unsuccessful_amex_purchase
assert response = @gateway.purchase(@amount, @declined_amex, @options)
assert_failure response
assert_equal 'DECLINE', response.message
end
+ def test_successful_purchase_with_state_in_iso_format
+ assert response = @gateway.purchase(@amount, @visa, @options.merge(billing_address: address, shipping_address: address))
+ assert_success response
+ assert_false response.authorization.blank?
+ assert_equal "Approved", response.message
+ end
+
+ def test_successful_purchase_with_only_email
+ options = {
+ :order_id => generate_unique_id,
+ :email => 'xiaobozzz@example.com',
+ :shipping_email => 'ship@mail.com'
+ }
+
+ assert response = @gateway.purchase(@amount, @visa, options)
+ assert_success response
+ assert_false response.authorization.blank?
+ assert_equal "Approved", response.message
+ end
+
+ def test_successful_purchase_with_no_addresses
+ @options[:billing_address] = {}
+ @options[:shipping_address] = {}
+ assert response = @gateway.purchase(@amount, @visa, @options)
+ assert_success response
+ assert_false response.authorization.blank?
+ assert_equal "Approved", response.message
+ end
+
+ def test_failed_purchase_due_to_invalid_billing_state
+ @options[:billing_address][:state] = "Invalid"
+ assert response = @gateway.purchase(@amount, @visa, @options)
+ assert_failure response
+ assert_match %r{province does not match country}, response.message
+ end
+
+ def test_failed_purchase_due_to_invalid_shipping_state
+ @options[:shipping_address][:state] = "North"
+ assert response = @gateway.purchase(@amount, @visa, @options)
+ assert_failure response
+ assert_match %r{Invalid shipping province}, response.message
+ end
+
+ def test_failed_purchase_due_to_missing_country_with_state
+ @options[:shipping_address][:country] = nil
+ assert response = @gateway.purchase(@amount, @visa, @options)
+ assert_failure response
+ assert_match %r{Invalid shipping country id}, response.message
+ end
+
def test_authorize_and_capture
assert auth = @gateway.authorize(@amount, @visa, @options)
assert_success auth
@@ -103,6 +192,17 @@ def test_authorize_and_capture
assert_false capture.authorization.blank?
end
+ def test_authorize_and_capture_with_recurring
+ assert auth = @gateway.authorize(@amount, @visa, @options.merge(recurring: true))
+ assert_success auth
+ assert_equal "Approved", auth.message
+ assert_false auth.authorization.blank?
+
+ assert capture = @gateway.capture(@amount, auth.authorization, recurring: true)
+ assert_success capture
+ assert_false capture.authorization.blank?
+ end
+
def test_successful_verify
response = @gateway.verify(@visa, @options)
assert_success response
@@ -130,6 +230,14 @@ def test_successful_purchase_and_void
assert_success void
end
+ def test_successful_purchase_and_void_with_recurring
+ assert purchase = @gateway.purchase(@amount, @visa, @options.merge(recurring: true))
+ assert_success purchase
+
+ assert void = @gateway.void(purchase.authorization)
+ assert_success void
+ end
+
def test_successful_purchase_and_refund_and_void_refund
assert purchase = @gateway.purchase(@amount, @visa, @options)
assert_success purchase
@@ -191,7 +299,7 @@ def test_invalid_login
)
assert response = gateway.purchase(@amount, @visa, @options)
assert_failure response
- assert_equal 'Invalid merchant id (merchant_id = 0)', response.message
+ assert_equal 'merchantid=Invalid merchant id (merchant_id = )', response.message
end
def test_successful_add_to_vault_with_store_method
diff --git a/test/remote/gateways/remote_blue_snap_test.rb b/test/remote/gateways/remote_blue_snap_test.rb
index 6f92f30ca6b..81748aac0e3 100644
--- a/test/remote/gateways/remote_blue_snap_test.rb
+++ b/test/remote/gateways/remote_blue_snap_test.rb
@@ -6,7 +6,7 @@ def setup
@amount = 100
@credit_card = credit_card('4263982640269299')
- @declined_card = credit_card('4917484589897107', month: 1, year: 2018)
+ @declined_card = credit_card('4917484589897107', month: 1, year: 2023)
@options = { billing_address: address }
end
@@ -36,6 +36,14 @@ def test_successful_purchase_with_more_options
assert_equal "Success", response.message
end
+ def test_successful_purchase_with_currency
+ response = @gateway.purchase(@amount, @credit_card, @options.merge(currency: 'CAD'))
+ assert_success response
+
+ assert_equal 'Success', response.message
+ assert_equal 'CAD', response.params["currency"]
+ end
+
def test_failed_purchase
response = @gateway.purchase(@amount, @declined_card, @options)
assert_failure response
@@ -100,8 +108,7 @@ def test_partial_refund
assert_success purchase
assert refund = @gateway.refund(@amount-1, purchase.authorization)
- assert_failure refund
- assert_match /failed because the financial transaction was created less than 24 hours ago/, refund.message
+ assert_success refund
end
def test_failed_refund
diff --git a/test/remote/gateways/remote_borgun_test.rb b/test/remote/gateways/remote_borgun_test.rb
index 1d41f1f8b99..4b4ac758865 100644
--- a/test/remote/gateways/remote_borgun_test.rb
+++ b/test/remote/gateways/remote_borgun_test.rb
@@ -8,7 +8,7 @@ def setup
@gateway = BorgunGateway.new(fixtures(:borgun))
@amount = 100
- @credit_card = credit_card('5587402000012011', year: 2014, month: 9, verification_value: 415)
+ @credit_card = credit_card('5587402000012011', year: 2018, month: 9, verification_value: 415)
@declined_card = credit_card('4155520000000002')
@options = {
@@ -28,6 +28,12 @@ def test_successful_purchase
assert_equal 'Succeeded', response.message
end
+ def test_successful_purchase_usd
+ response = @gateway.purchase(@amount, @credit_card, @options.merge(currency: "USD"))
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ end
+
def test_successful_purchase_without_options
response = @gateway.purchase(@amount, @credit_card)
assert_success response
@@ -48,6 +54,14 @@ def test_successful_authorize_and_capture
assert_success capture
end
+ def test_successful_authorize_and_capture_usd
+ auth = @gateway.authorize(@amount, @credit_card, @options.merge(currency: 'USD'))
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount, auth.authorization, currency: 'USD')
+ assert_success capture
+ end
+
def test_failed_authorize
response = @gateway.authorize(@amount, @declined_card, @options)
assert_failure response
@@ -74,6 +88,14 @@ def test_successful_refund
assert_success refund
end
+ def test_successful_refund_usd
+ purchase = @gateway.purchase(@amount, @credit_card, @options.merge(currency: 'USD'))
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount, purchase.authorization, currency: 'USD')
+ assert_success refund
+ end
+
def test_partial_refund
purchase = @gateway.purchase(@amount, @credit_card, @options)
assert_success purchase
@@ -95,11 +117,39 @@ def test_successful_void
assert_success void
end
+ def test_successful_void_with_no_currency_in_authorization
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ *new_auth, _ = auth.authorization.split("|")
+ assert void = @gateway.void(new_auth.join("|"))
+ assert_success void
+ end
+
+ def test_successful_void_usd
+ auth = @gateway.authorize(@amount, @credit_card, @options.merge(currency: 'USD'))
+ assert_success auth
+
+ assert void = @gateway.void(auth.authorization)
+ assert_success void
+ end
+
+ def test_successful_void_usd_with_options
+ auth = @gateway.authorize(@amount, @credit_card, @options.merge(currency: 'USD'))
+ assert_success auth
+
+ assert void = @gateway.void(auth.authorization, @options.merge(currency: 'USD'))
+ assert_success void
+ end
+
def test_failed_void
response = @gateway.void('')
assert_failure response
end
+ # This test does not consistently pass. When run multiple times within 1 minute,
+ # an ActiveMerchant::ConnectionError()
+ # exception is raised.
def test_invalid_login
gateway = BorgunGateway.new(
processor: '0',
@@ -107,10 +157,20 @@ def test_invalid_login
username: 'not',
password: 'right'
)
- authentication_exception = assert_raise ActiveMerchant::ResponseError, 'Failed with 500 Internal Server Error' do
+ authentication_exception = assert_raise ActiveMerchant::ResponseError, 'Failed with 401 [ISS.0084.9001] Invalid credentials' do
gateway.purchase(@amount, @credit_card, @options)
end
assert response = authentication_exception.response
assert_match(/Access Denied/, response.body)
end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ end
end
diff --git a/test/remote/gateways/remote_braintree_blue_test.rb b/test/remote/gateways/remote_braintree_blue_test.rb
index 18d35841d80..cf060f773d2 100644
--- a/test/remote/gateways/remote_braintree_blue_test.rb
+++ b/test/remote/gateways/remote_braintree_blue_test.rb
@@ -27,6 +27,8 @@ def test_credit_card_details_on_store
assert_equal('510510', response.params["braintree_customer"]["credit_cards"].first["bin"])
assert_match %r{^\d+$}, response.params["customer_vault_id"]
assert_equal response.params["customer_vault_id"], response.authorization
+ assert_match %r{^\w+$}, response.params["credit_card_token"]
+ assert_equal response.params["credit_card_token"], response.params["braintree_customer"]["credit_cards"].first["token"]
end
def test_successful_authorize
@@ -86,6 +88,19 @@ def test_successful_purchase_using_vault_id_as_integer
assert_equal customer_vault_id, response.params["braintree_transaction"]["customer_details"]["id"]
end
+ def test_successful_purchase_using_card_token
+ assert response = @gateway.store(@credit_card)
+ assert_success response
+ assert_equal 'OK', response.message
+ credit_card_token = response.params["credit_card_token"]
+ assert_match %r{^\w+$}, credit_card_token
+
+ assert response = @gateway.purchase(@amount, credit_card_token, :payment_method_token => true)
+ assert_success response
+ assert_equal '1000 Approved', response.message
+ assert_equal 'submitted_for_settlement', response.params["braintree_transaction"]["status"]
+ end
+
def test_successful_verify
assert response = @gateway.verify(@credit_card, @options)
assert_success response
@@ -250,6 +265,27 @@ def test_successful_purchase_with_email
assert_equal 'customer@example.com', transaction["customer_details"]["email"]
end
+ def test_successful_purchase_with_phone
+ assert response = @gateway.purchase(@amount, @credit_card, :phone => "123-345-5678")
+ assert_success response
+ transaction = response.params["braintree_transaction"]
+ assert_equal '123-345-5678', transaction["customer_details"]["phone"]
+ end
+
+ def test_successful_purchase_with_phone_from_address
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ transaction = response.params["braintree_transaction"]
+ assert_equal '(555)555-5555', transaction["customer_details"]["phone"]
+ end
+
+ def test_successful_purchase_with_skip_advanced_fraud_checking_option
+ assert response = @gateway.purchase(@amount, @credit_card, @options.merge(skip_advanced_fraud_checking: true))
+ assert_success response
+ assert_equal '1000 Approved', response.message
+ assert_equal 'submitted_for_settlement', response.params["braintree_transaction"]["status"]
+ end
+
def test_purchase_with_store_using_random_customer_id
assert response = @gateway.purchase(
@amount, credit_card('5105105105105100'), @options.merge(:store => true)
@@ -279,7 +315,8 @@ def test_purchase_using_specified_payment_method_token
:first_name => 'Old First', :last_name => 'Old Last',
:month => 9, :year => 2012
),
- :email => "old@example.com"
+ :email => "old@example.com",
+ :phone => "321-654-0987"
)
payment_method_token = response.params["braintree_customer"]["credit_cards"][0]["token"]
assert response = @gateway.purchase(
@@ -331,6 +368,14 @@ def test_successful_purchase_with_addresses
assert_equal 'Mexico', transaction["shipping_details"]["country_name"]
end
+ def test_successful_purchase_with_three_d_secure_pass_thru
+ three_d_secure_params = { eci: "05", cavv: "cavv", xid: "xid" }
+ assert response = @gateway.purchase(@amount, @credit_card,
+ three_d_secure: three_d_secure_params
+ )
+ assert_success response
+ end
+
def test_unsuccessful_purchase_declined
assert response = @gateway.purchase(@declined_amount, @credit_card, @options)
assert_failure response
@@ -369,6 +414,24 @@ def test_authorize_and_capture_with_apple_pay_card
assert_success capture
end
+ def test_authorize_and_capture_with_android_pay_card
+ credit_card = network_tokenization_credit_card('4111111111111111',
+ :payment_cryptogram => "EHuWW9PiBkWvqE5juRwDzAUFBAk=",
+ :month => "01",
+ :year => "2024",
+ :source => :android_pay,
+ :transaction_id => "123456789",
+ :eci => "05"
+ )
+
+ assert auth = @gateway.authorize(@amount, credit_card, @options)
+ assert_success auth
+ assert_equal '1000 Approved', auth.message
+ assert auth.authorization
+ assert capture = @gateway.capture(@amount, auth.authorization)
+ assert_success capture
+ end
+
def test_authorize_and_void
assert auth = @gateway.authorize(@amount, @credit_card, @options)
assert_success auth
@@ -409,7 +472,7 @@ def test_failed_void
assert_equal 'voided', void.params["braintree_transaction"]["status"]
assert failed_void = @gateway.void(auth.authorization)
assert_failure failed_void
- assert_equal 'Transaction can only be voided if status is authorized or submitted_for_settlement. (91504)', failed_void.message
+ assert_match('Transaction can only be voided if status is authorized', failed_void.message)
assert_equal({"processor_response_code"=>"91504"}, failed_void.params["braintree_transaction"])
end
@@ -474,13 +537,15 @@ def test_successful_update
:first_name => 'Old First', :last_name => 'Old Last',
:month => 9, :year => 2012
),
- :email => "old@example.com"
+ :email => "old@example.com",
+ :phone => "321-654-0987"
)
assert_success response
assert_equal 'OK', response.message
customer_vault_id = response.params["customer_vault_id"]
assert_match(/\A\d+\z/, customer_vault_id)
assert_equal "old@example.com", response.params["braintree_customer"]["email"]
+ assert_equal "321-654-0987", response.params["braintree_customer"]["phone"]
assert_equal "Old First", response.params["braintree_customer"]["first_name"]
assert_equal "Old Last", response.params["braintree_customer"]["last_name"]
assert_equal "411111", response.params["braintree_customer"]["credit_cards"][0]["bin"]
@@ -494,10 +559,12 @@ def test_successful_update
:first_name => 'New First', :last_name => 'New Last',
:month => 10, :year => 2014
),
- :email => "new@example.com"
+ :email => "new@example.com",
+ :phone => "987-765-5432"
)
assert_success response
assert_equal "new@example.com", response.params["braintree_customer"]["email"]
+ assert_equal "987-765-5432", response.params["braintree_customer"]["phone"]
assert_equal "New First", response.params["braintree_customer"]["first_name"]
assert_equal "New Last", response.params["braintree_customer"]["last_name"]
assert_equal "510510", response.params["braintree_customer"]["credit_cards"][0]["bin"]
@@ -507,7 +574,7 @@ def test_successful_update
end
def test_failed_customer_update
- assert response = @gateway.store(credit_card('4111111111111111'), :email => "email@example.com")
+ assert response = @gateway.store(credit_card('4111111111111111'), :email => "email@example.com", :phone => "321-654-0987")
assert_success response
assert_equal 'OK', response.message
assert customer_vault_id = response.params["customer_vault_id"]
@@ -588,7 +655,7 @@ 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"]
+ assert_equal 'authorized', response.params["braintree_transaction"]["status"]
end
def test_authorize_with_descriptor
diff --git a/test/remote/gateways/remote_braintree_orange_test.rb b/test/remote/gateways/remote_braintree_orange_test.rb
index 8bde4c2f561..8468633afbf 100644
--- a/test/remote/gateways/remote_braintree_orange_test.rb
+++ b/test/remote/gateways/remote_braintree_orange_test.rb
@@ -142,6 +142,12 @@ def test_failed_capture
assert response.message.match(/Invalid Transaction ID \/ Object ID specified:/)
end
+ def test_authorize_with_three_d_secure_pass_thru
+ assert auth = @gateway.authorize(@amount, @credit_card, @options.merge(eci: "05", xid: "xid", cavv: "cavv"))
+ assert_success auth
+ assert_equal 'This transaction has been approved', auth.message
+ end
+
def test_successful_verify
assert response = @gateway.verify(@credit_card, @options)
assert_success response
@@ -170,7 +176,7 @@ def test_transcript_scrubbing
@gateway.purchase(@declined_amount, @credit_card, @options)
end
clean_transcript = @gateway.scrub(transcript)
-
+
assert_scrubbed(@credit_card.number, clean_transcript)
assert_scrubbed(@credit_card.verification_value.to_s, clean_transcript)
end
diff --git a/test/remote/gateways/remote_card_connect_test.rb b/test/remote/gateways/remote_card_connect_test.rb
new file mode 100644
index 00000000000..87d1ae47ff9
--- /dev/null
+++ b/test/remote/gateways/remote_card_connect_test.rb
@@ -0,0 +1,200 @@
+require 'test_helper'
+
+class RemoteCardConnectTest < Test::Unit::TestCase
+ def setup
+ @gateway = CardConnectGateway.new(fixtures(:card_connect))
+
+ @amount = 100
+ @credit_card = credit_card('4788250000121443')
+ @declined_card = credit_card('4387751111111053')
+ @options = {
+ billing_address: address,
+ description: 'Store Purchase'
+ }
+ @check = check(routing_number: '053000196')
+ @invalid_txn = '23221'
+ end
+
+ def test_successful_purchase
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'Approval', response.message
+ end
+
+ def test_successful_purchase_with_more_options
+ options = {
+ order_id: '1',
+ ip: "127.0.0.1",
+ email: "joe@example.com",
+ po_number: '5FSD4',
+ tax_amount: '50',
+ freight_amount: '29',
+ duty_amount: '67',
+ order_date: '20170507',
+ ship_from_date: '20877',
+ items: [
+ {
+ line_no: '1',
+ material: 'MATERIAL-1',
+ description: 'DESCRIPTION-1',
+ upc: 'UPC-1',
+ quantity: '1000',
+ uom: 'CS',
+ unit_cost: '900',
+ net_amnt: '150',
+ tax_amnt: '117',
+ disc_amnt: '0'
+ },
+ {
+ line_no: '2',
+ material: 'MATERIAL-2',
+ description: 'DESCRIPTION-2',
+ upc: 'UPC-1',
+ quantity: '2000',
+ uom: 'CS',
+ unit_cost: '450',
+ net_amnt: '300',
+ tax_amnt: '117',
+ disc_amnt: '0'
+ }
+ ]
+ }
+
+ response = @gateway.purchase(@amount, @credit_card, options)
+ assert_success response
+ assert_equal 'Approval Queued for Capture', response.message
+ end
+
+ def test_successful_purchase_3DS
+ three_ds_options = @options.merge(
+ secure_flag: 'se3453',
+ secure_value: '233frdf',
+ secure_xid: '334ef34'
+ )
+ response = @gateway.purchase(@amount, @credit_card, three_ds_options)
+ assert_success response
+ assert_equal 'Approval', response.message
+ end
+
+ def test_failed_purchase
+ response = @gateway.purchase(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal 'Insufficient funds', response.message
+ end
+
+ def test_successful_authorize_and_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount, auth.authorization)
+ assert_success capture
+ assert_includes ['Approval Queued for Capture', 'Approval Accepted'], capture.message
+ end
+
+ def test_failed_authorize
+ response = @gateway.authorize(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal 'Insufficient funds', response.message
+ end
+
+ def test_partial_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount - 1, auth.authorization)
+ assert_success capture
+ end
+
+ def test_failed_capture
+ response = @gateway.capture(@amount, @invalid_txn)
+ assert_failure response
+ assert_equal 'Txn not found', response.message
+ end
+
+ def test_successful_echeck_purchase
+ response = @gateway.purchase(@amount, @check, @options)
+ assert_equal 'Success', response.message
+ assert_success response
+ end
+
+ def test_failed_echeck_purchase
+ response = @gateway.purchase(@amount, check(routing_number: '23433'), @options)
+ assert_failure response
+ assert_equal 'Invalid card', response.message
+ end
+
+ def test_successful_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount, purchase.authorization)
+ assert_success refund
+ assert_equal 'Approval', refund.message
+ end
+
+ def test_partial_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount - 1, purchase.authorization)
+ assert_success refund
+ end
+
+ def test_failed_refund
+ response = @gateway.refund(@amount, @invalid_txn)
+ assert_failure response
+ assert_equal 'Txn not found', response.message
+ end
+
+ def test_successful_void
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert void = @gateway.void(auth.authorization)
+ assert_success void
+ assert_equal 'Approval', void.message
+ end
+
+ def test_failed_void
+ response = @gateway.void(@invalid_txn)
+ assert_failure response
+ end
+
+ def test_successful_verify
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ assert_match %r{Approval}, response.message
+ end
+
+ def test_failed_verify
+ response = @gateway.verify(@declined_card, @options)
+ assert_failure response
+ assert_match %r{Insufficient funds}, response.message
+ end
+
+ def test_invalid_login
+ gateway = CardConnectGateway.new(username: '', password: '', merchant_id: '')
+ assert_raises(ActiveMerchant::ResponseError) do
+ gateway.purchase(@amount, @credit_card, @options)
+ end
+ end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @check, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@check.account_number, transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
+end
diff --git a/test/remote/gateways/remote_card_stream_test.rb b/test/remote/gateways/remote_card_stream_test.rb
index ef9c16cd153..7533036d161 100644
--- a/test/remote/gateways/remote_card_stream_test.rb
+++ b/test/remote/gateways/remote_card_stream_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',
@@ -29,10 +21,10 @@ def setup
)
@visacreditcard = credit_card('4929421234600821',
- :month => '12',
- :year => '2014',
- :verification_value => '356',
- :brand => :visa
+ :month => '12',
+ :year => '2014',
+ :verification_value => '356',
+ :brand => :visa
)
@visadebitcard = credit_card('4539791001730106',
@@ -52,10 +44,12 @@ def setup
:address1 => 'The Hunts Way',
:city => "",
:state => "Leicester",
- :zip => 'SO18 1GW'
+ :zip => 'SO18 1GW',
+ :country => 'GB'
},
:order_id => generate_unique_id,
- :description => 'AM test purchase'
+ :description => 'AM test purchase',
+ :ip => '1.1.1.1'
}
@visacredit_options = {
@@ -64,16 +58,33 @@ def setup
:address2 => "347 Lavender Road",
:city => "",
:state => "Northampton",
- :zip => 'NN17 8YG '
+ :zip => 'NN17 8YG',
+ :country => 'GB'
},
:order_id => generate_unique_id,
- :description => 'AM test purchase'
+ :description => 'AM test purchase',
+ :ip => '1.1.1.1'
+ }
+
+ @visacredit_descriptor_options = {
+ :billing_address => {
+ :address1 => "Flat 6, Primrose Rise",
+ :address2 => "347 Lavender Road",
+ :city => "",
+ :state => "Northampton",
+ :zip => 'NN17 8YG',
+ :country => 'GB'
+ },
+ :merchant_name => 'merchant',
+ :dynamic_descriptor => 'product',
+ :ip => '1.1.1.1',
}
@visacredit_reference_options = {
:order_id => generate_unique_id,
- :description => 'AM test purchase'
- }
+ :description => 'AM test purchase',
+ :ip => '1.1.1.1'
+ }
@visadebit_options = {
:billing_address => {
@@ -81,10 +92,12 @@ def setup
:address2 => "120 Uxbridge Road",
:city => "Hatch End",
:state => "Middlesex",
- :zip => "HA6 7HJ"
+ :zip => "HA6 7HJ",
+ :country => 'GB'
},
:order_id => generate_unique_id,
- :description => 'AM test purchase'
+ :description => 'AM test purchase',
+ :ip => '1.1.1.1'
}
@mastercard_options = {
@@ -92,22 +105,12 @@ def setup
:address1 => '25 The Larches',
:city => "Narborough",
:state => "Leicester",
- :zip => 'LE10 2RT'
- },
- :order_id => generate_unique_id,
- :description => 'AM test purchase'
- }
-
- @uk_maestro_options = {
- :billing_address => {
- :address1 => 'The Parkway',
- :address2 => "5258 Larches Approach",
- :city => "Hull",
- :state => "North Humberside",
- :zip => 'HU10 5OP'
+ :zip => 'LE10 2RT',
+ :country => 'GB'
},
:order_id => generate_unique_id,
- :description => 'AM test purchase'
+ :description => 'AM test purchase',
+ :ip => '1.1.1.1'
}
@three_ds_enrolled_card = credit_card('4012001037141112',
@@ -129,18 +132,52 @@ def test_successful_visacreditcard_authorization_and_capture
assert responseCapture.test?
end
- def test_successful_visacreditcard_purchase_and_refund
+ def test_successful_visacreditcard_authorization_and_capture_no_billing_address
+ assert responseAuthorization = @gateway.authorize(142, @visacreditcard, @visacredit_options.delete(:billing_address))
+ assert_equal 'APPROVED', responseAuthorization.message
+ assert_success responseAuthorization
+ assert responseAuthorization.test?
+ assert !responseAuthorization.authorization.blank?
+ assert responseCapture = @gateway.capture(142, responseAuthorization.authorization, @visacredit_options)
+ assert_equal 'APPROVED', responseCapture.message
+ assert_success responseCapture
+ assert responseCapture.test?
+ end
+
+ def test_successful_visacreditcard_purchase_and_refund_with_force_refund
assert responsePurchase = @gateway.purchase(284, @visacreditcard, @visacredit_options)
assert_equal 'APPROVED', responsePurchase.message
assert_success responsePurchase
assert responsePurchase.test?
assert !responsePurchase.authorization.blank?
- assert responseRefund = @gateway.refund(142, responsePurchase.authorization, @visacredit_options)
+
+ assert responseRefund = @gateway.refund(142, responsePurchase.authorization, @visacredit_options.merge(force_full_refund_if_unsettled: true))
assert_equal 'APPROVED', responseRefund.message
assert_success responseRefund
assert responseRefund.test?
end
+ def test_failed_visacreditcard_purchase_and_refund
+ assert responsePurchase = @gateway.purchase(284, @visacreditcard, @visacredit_options)
+ assert_equal 'APPROVED', responsePurchase.message
+ assert_success responsePurchase
+ assert responsePurchase.test?
+ assert !responsePurchase.authorization.blank?
+
+ assert responseRefund = @gateway.refund(142, responsePurchase.authorization, @visacredit_options)
+ assert_failure responseRefund
+ assert_equal 'Can not REFUND this SALE transaction', responseRefund.message
+ assert responseRefund.test?
+ end
+
+ def test_successful_visacreditcard_purchase_with_dynamic_descriptors
+ assert responsePurchase = @gateway.purchase(284, @visacreditcard, @visacredit_descriptor_options)
+ assert_equal 'APPROVED', responsePurchase.message
+ assert_success responsePurchase
+ assert responsePurchase.test?
+ assert !responsePurchase.authorization.blank?
+ end
+
def test_successful_visacreditcard_authorization_and_void
assert responseAuthorization = @gateway.authorize(284, @visacreditcard, @visacredit_options)
assert_equal 'APPROVED', responseAuthorization.message
@@ -165,18 +202,32 @@ def test_successful_visadebitcard_authorization_and_capture
assert responseCapture.test?
end
- def test_successful_visadebitcard_purchase_and_refund
+ def test_successful_visadebitcard_purchase_and_refund_with_force_refund
assert responsePurchase = @gateway.purchase(284, @visadebitcard, @visadebit_options)
assert_equal 'APPROVED', responsePurchase.message
assert_success responsePurchase
assert responsePurchase.test?
assert !responsePurchase.authorization.blank?
- assert responseRefund = @gateway.refund(142, responsePurchase.authorization, @visadebit_options)
+
+ assert responseRefund = @gateway.refund(142, responsePurchase.authorization, @visadebit_options.merge(force_full_refund_if_unsettled: true))
assert_equal 'APPROVED', responseRefund.message
assert_success responseRefund
assert responseRefund.test?
end
+ def test_failed_visadebitcard_purchase_and_refund
+ assert responsePurchase = @gateway.purchase(284, @visadebitcard, @visadebit_options)
+ assert_equal 'APPROVED', responsePurchase.message
+ assert_success responsePurchase
+ assert responsePurchase.test?
+ assert !responsePurchase.authorization.blank?
+
+ assert responseRefund = @gateway.refund(142, responsePurchase.authorization, @visadebit_options)
+ assert_equal 'Can not REFUND this SALE transaction', responseRefund.message
+ assert_failure responseRefund
+ assert responseRefund.test?
+ end
+
def test_successful_amex_authorization_and_capture
assert responseAuthorization = @gateway.authorize(142, @amex, @amex_options)
assert_equal 'APPROVED', responseAuthorization.message
@@ -189,18 +240,32 @@ def test_successful_amex_authorization_and_capture
assert responseCapture.test?
end
- def test_successful_amex_purchase_and_refund
+ def test_successful_amex_purchase_and_refund_with_force_refund
assert responsePurchase = @gateway.purchase(284, @amex, @amex_options)
assert_equal 'APPROVED', responsePurchase.message
assert_success responsePurchase
assert responsePurchase.test?
assert !responsePurchase.authorization.blank?
- assert responseRefund = @gateway.refund(142, responsePurchase.authorization, @amex_options)
+
+ assert responseRefund = @gateway.refund(142, responsePurchase.authorization, @amex_options.merge(force_full_refund_if_unsettled: true))
assert_equal 'APPROVED', responseRefund.message
assert_success responseRefund
assert responseRefund.test?
end
+ def test_failed_amex_purchase_and_refund
+ assert responsePurchase = @gateway.purchase(284, @amex, @amex_options)
+ assert_equal 'APPROVED', responsePurchase.message
+ assert_success responsePurchase
+ assert responsePurchase.test?
+ assert !responsePurchase.authorization.blank?
+
+ assert responseRefund = @gateway.refund(142, responsePurchase.authorization, @amex_options)
+ assert_equal 'Can not REFUND this SALE transaction', responseRefund.message
+ assert_failure responseRefund
+ assert responseRefund.test?
+ end
+
def test_successful_mastercard_authorization_and_capture
assert responseAuthorization = @gateway.authorize(142, @mastercard, @mastercard_options)
assert_equal 'APPROVED', responseAuthorization.message
@@ -213,18 +278,32 @@ def test_successful_mastercard_authorization_and_capture
assert responseCapture.test?
end
- def test_successful_mastercard_purchase_and_refund
+ def test_successful_mastercard_purchase_and_refund_with_force_refund
assert responsePurchase = @gateway.purchase(284, @mastercard, @mastercard_options)
assert_equal 'APPROVED', responsePurchase.message
assert_success responsePurchase
assert responsePurchase.test?
assert !responsePurchase.authorization.blank?
- assert responseRefund = @gateway.refund(142, responsePurchase.authorization, @mastercard_options)
+
+ assert responseRefund = @gateway.refund(142, responsePurchase.authorization, @mastercard_options.merge(force_full_refund_if_unsettled: true))
assert_equal 'APPROVED', responseRefund.message
assert_success responseRefund
assert responseRefund.test?
end
+ def test_failed_mastercard_purchase_and_refund
+ assert responsePurchase = @gateway.purchase(284, @mastercard, @mastercard_options)
+ assert_equal 'APPROVED', responsePurchase.message
+ assert_success responsePurchase
+ assert responsePurchase.test?
+ assert !responsePurchase.authorization.blank?
+
+ assert responseRefund = @gateway.refund(142, responsePurchase.authorization, @mastercard_options)
+ assert_equal 'Can not REFUND this SALE transaction', responseRefund.message
+ assert_failure responseRefund
+ assert responseRefund.test?
+ end
+
def test_successful_visacreditcard_purchase
assert response = @gateway.purchase(142, @visacreditcard, @visacredit_options)
assert_equal 'APPROVED', response.message
@@ -246,7 +325,7 @@ def test_successful_visacreditcard_purchase_via_reference
def test_failed_visacreditcard_purchase_via_reference
assert response = @gateway.purchase(142, 123, @visacredit_reference_options)
- assert_equal 'DB ERROR', response.message
+ assert_match %r{INVALID_XREF}, response.message
assert_failure response
assert response.test?
end
@@ -261,7 +340,7 @@ def test_purchase_no_currency_specified_defaults_to_GBP
def test_failed_purchase_non_existent_currency
assert response = @gateway.purchase(142, @visacreditcard, @visacredit_options.merge(currency: "CEO"))
assert_failure response
- assert_equal 'MISSING CURRENCYCODE', response.message
+ assert_match %r{MISSING_CURRENCYCODE}, response.message
end
def test_successful_visadebitcard_purchase
@@ -287,20 +366,6 @@ def test_declined_mastercard_purchase
assert response.test?
end
- def test_expired_mastercard
- @mastercard.year = 2012
- assert response = @gateway.purchase(142, @mastercard, @mastercard_options)
- 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
@@ -315,17 +380,10 @@ def test_invalid_login
:shared_secret => ''
)
assert response = gateway.purchase(142, @mastercard, @mastercard_options)
- assert_equal 'MISSING MERCHANTID', response.message
+ assert_match %r{MISSING_MERCHANTID}, response.message
assert_failure response
end
- def test_usd_merchant_currency
- assert response = @gateway.purchase(142, @mastercard, @mastercard_options.update(:currency => 'USD'))
- assert_equal 'APPROVED', response.message
- assert_success response
- assert response.test?
- end
-
def test_successful_verify
response = @gateway.verify(@mastercard, @mastercard_options)
assert_success response
@@ -335,7 +393,7 @@ def test_successful_verify
def test_failed_verify
response = @gateway.verify(@declined_card, @mastercard_options)
assert_failure response
- assert_equal 'INVALID CARDNUMBER', response.message
+ assert_match %r{INVALID_CARDNUMBER}, response.message
end
def test_successful_3dsecure_purchase
diff --git a/test/remote/gateways/remote_cardprocess_test.rb b/test/remote/gateways/remote_cardprocess_test.rb
new file mode 100644
index 00000000000..951c07e0ab3
--- /dev/null
+++ b/test/remote/gateways/remote_cardprocess_test.rb
@@ -0,0 +1,149 @@
+require 'test_helper'
+
+class RemoteCardprocessTest < Test::Unit::TestCase
+ def setup
+ @gateway = CardprocessGateway.new(fixtures(:cardprocess))
+
+ @amount = 100
+ @credit_card = credit_card('4200000000000000')
+ @credit_card_3ds = credit_card('4711100000000000')
+
+ @options = {
+ billing_address: address,
+ description: 'Store Purchase'
+ }
+ end
+
+ def test_successful_purchase
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_match %r{^Request successfully processed}, response.message
+ end
+
+ def test_successful_purchase_with_more_options
+ options = {
+ order_id: '1',
+ ip: '127.0.0.1',
+ email: 'joe@example.com'
+ }
+
+ response = @gateway.purchase(@amount, @credit_card, options)
+ assert_success response
+ assert_match %r{^Request successfully processed}, response.message
+ end
+
+ def test_failed_purchase
+ bad_credit_card = credit_card('4200000000000001')
+ response = @gateway.purchase(@amount, bad_credit_card, @options)
+ assert_failure response
+ assert_equal Gateway::STANDARD_ERROR_CODE[:incorrect_number], response.error_code
+ assert_equal 'invalid creditcard, bank account number or bank name', response.message
+ end
+
+ def test_successful_authorize_and_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount, auth.authorization)
+ assert_success capture
+ assert_match %r{^Request successfully processed}, capture.message
+ end
+
+ def test_failed_authorize
+ @gateway.instance_variable_set(:@test_options, {'customParameters[forceResultCode]' => '800.100.151'})
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal 'transaction declined (invalid card)', response.message
+ end
+
+ def test_partial_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount-1, auth.authorization)
+ assert_success capture
+ end
+
+ def test_failed_capture
+ response = @gateway.capture(@amount, '12345678123456781234567812345678')
+ assert_failure response
+ assert_equal 'capture needs at least one successful transaction of type (PA)', response.message
+ end
+
+ def test_successful_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount, purchase.authorization)
+ assert_success refund
+ assert_match %r{^Request successfully processed}, refund.message
+ end
+
+ def test_successful_credit
+ response = @gateway.credit(@amount, @credit_card, @options)
+ assert_success response
+ assert_match %r{^Request successfully processed}, response.message
+ end
+
+ def test_partial_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount-1, purchase.authorization)
+ assert_success refund
+ end
+
+ def test_failed_refund
+ response = @gateway.refund(@amount, '')
+ assert_failure response
+ assert_equal 'invalid or missing parameter', response.message
+ end
+
+ def test_successful_void
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert void = @gateway.void(auth.authorization)
+ assert_success void
+ assert_match %r{^Request successfully processed}, void.message
+ end
+
+ def test_failed_void
+ response = @gateway.void('')
+ assert_failure response
+ assert_equal 'invalid or missing parameter', response.message
+ end
+
+ def test_successful_verify
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ assert_match %r{^Request successfully processed}, response.message
+ end
+
+ def test_failed_verify
+ @gateway.instance_variable_set(:@test_options, {'customParameters[forceResultCode]' => '600.200.100'})
+ response = @gateway.verify(@credit_card, @options)
+ assert_failure response
+ assert_match %r{invalid Payment Method}, response.message
+ end
+
+ def test_invalid_login
+ gateway = CardprocessGateway.new(user_id: '00000000000000000000000000000000', password: 'qwerty', entity_id: '00000000000000000000000000000000')
+
+ response = gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal 'invalid authentication information', response.message
+ end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ assert_scrubbed(@gateway.options[:entity_id], transcript)
+ end
+end
diff --git a/test/remote/gateways/remote_cashnet_test.rb b/test/remote/gateways/remote_cashnet_test.rb
index b30925b2bbe..babeb00ae47 100644
--- a/test/remote/gateways/remote_cashnet_test.rb
+++ b/test/remote/gateways/remote_cashnet_test.rb
@@ -52,4 +52,15 @@ def test_failed_refund
assert_match %r{Amount to refund exceeds}, refund.message
assert_equal "302", refund.params["result"]
end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
end
diff --git a/test/remote/gateways/remote_cenpos_test.rb b/test/remote/gateways/remote_cenpos_test.rb
index 64adf71cf44..29024454559 100644
--- a/test/remote/gateways/remote_cenpos_test.rb
+++ b/test/remote/gateways/remote_cenpos_test.rb
@@ -107,7 +107,7 @@ def test_failed_capture
capture = @gateway.capture(@amount, response.authorization)
capture = @gateway.capture(@amount, response.authorization)
assert_failure capture
- assert_equal "Duplicated transaction", capture.message
+ assert_equal "Duplicated force transaction.", capture.message
end
def test_successful_void
diff --git a/test/remote/gateways/remote_checkout_v2_test.rb b/test/remote/gateways/remote_checkout_v2_test.rb
index e196c849134..c894bd951a1 100644
--- a/test/remote/gateways/remote_checkout_v2_test.rb
+++ b/test/remote/gateways/remote_checkout_v2_test.rb
@@ -6,6 +6,7 @@ def setup
@amount = 200
@credit_card = credit_card('4242424242424242', verification_value: '100', month: '6', year: '2018')
+ @expired_card = credit_card('4242424242424242', verification_value: '100', month: '6', year: '2010')
@declined_card = credit_card('4000300011112220')
@options = {
@@ -14,6 +15,11 @@ def setup
description: 'Purchase',
email: "longbob.longsen@example.com"
}
+ @additional_options = @options.merge(
+ card_on_file: true,
+ transaction_indicator: 2,
+ previous_charge_id: "charge_12312"
+ )
end
def test_transcript_scrubbing
@@ -33,6 +39,42 @@ def test_successful_purchase
assert_equal 'Succeeded', response.message
end
+ def test_successful_purchase_with_additional_options
+ response = @gateway.purchase(@amount, @credit_card, @additional_options)
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ end
+
+ def test_successful_purchase_includes_avs_result
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ assert_equal 'S', response.avs_result["code"]
+ assert_equal 'U.S.-issuing bank does not support AVS.', response.avs_result["message"]
+ end
+
+ def test_successful_authorize_includes_avs_result
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ assert_equal 'S', response.avs_result["code"]
+ assert_equal 'U.S.-issuing bank does not support AVS.', response.avs_result["message"]
+ end
+
+ def test_successful_purchase_includes_cvv_result
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ assert_equal 'Y', response.cvv_result["code"]
+ end
+
+ def test_successful_authorize_includes_cvv_result
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ assert_equal 'Y', response.cvv_result["code"]
+ end
+
def test_successful_purchase_with_descriptors
options = @options.merge(descriptor_name: "shop", descriptor_city: "london")
response = @gateway.purchase(@amount, @credit_card, options)
@@ -52,12 +94,30 @@ def test_successful_purchase_without_phone_number
assert_equal 'Succeeded', response.message
end
+ def test_successful_purchase_with_ip
+ response = @gateway.purchase(@amount, @credit_card, ip: "96.125.185.52")
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ end
+
def test_failed_purchase
response = @gateway.purchase(@amount, @declined_card, @options)
assert_failure response
assert_equal 'Invalid Card Number', response.message
end
+ def test_avs_failed_purchase
+ response = @gateway.purchase(@amount, @credit_card, billing_address: address.update(address1: 'Test_A'))
+ assert_failure response
+ assert_equal '40111 - Street Match Only', response.message
+ end
+
+ def test_avs_failed_authorize
+ response = @gateway.authorize(@amount, @credit_card, billing_address: address.update(address1: 'Test_A'))
+ assert_failure response
+ assert_equal '40111 - Street Match Only', response.message
+ end
+
def test_successful_authorize_and_capture
auth = @gateway.authorize(@amount, @credit_card, @options)
assert_success auth
@@ -66,6 +126,14 @@ def test_successful_authorize_and_capture
assert_success capture
end
+ def test_successful_authorize_and_capture_with_additional_options
+ auth = @gateway.authorize(@amount, @credit_card, @additional_options)
+ assert_success auth
+
+ assert capture = @gateway.capture(nil, auth.authorization)
+ assert_success capture
+ end
+
def test_failed_authorize
response = @gateway.authorize(@amount, @declined_card, @options)
assert_failure response
@@ -130,4 +198,11 @@ def test_failed_verify
assert_match %r{Invalid Card Number}, response.message
assert_equal Gateway::STANDARD_ERROR_CODE[:invalid_number], response.error_code
end
+
+ def test_expired_card_returns_error_code
+ response = @gateway.purchase(@amount, @expired_card, @options)
+ assert_failure response
+ assert_equal 'Validation error: Expired Card', response.message
+ assert_equal '70000: 70077', response.error_code
+ end
end
diff --git a/test/remote/gateways/remote_citrus_pay_test.rb b/test/remote/gateways/remote_citrus_pay_test.rb
index 9ac51145dab..704d11cf8e0 100644
--- a/test/remote/gateways/remote_citrus_pay_test.rb
+++ b/test/remote/gateways/remote_citrus_pay_test.rb
@@ -42,6 +42,17 @@ def test_successful_purchase_with_more_options
assert_equal "Succeeded", response.message
end
+ def test_adds_3dsecure_id_to_authorize
+ more_options = @options.merge({
+ ip: "127.0.0.1",
+ email: "joe@example.com",
+ threed_secure_id: "abc123"
+ })
+
+ assert response = @gateway.purchase(@amount, @credit_card, @options.merge(more_options))
+ assert_match "No check has been performed for this merchant and 3D Secure Id.", response.message
+ end
+
def test_failed_purchase
assert response = @gateway.purchase(@amount, @declined_card, @options)
assert_failure response
diff --git a/test/remote/gateways/remote_clearhaus_test.rb b/test/remote/gateways/remote_clearhaus_test.rb
index a1a15d8ef59..006249c9263 100644
--- a/test/remote/gateways/remote_clearhaus_test.rb
+++ b/test/remote/gateways/remote_clearhaus_test.rb
@@ -118,7 +118,7 @@ def test_partial_capture
end
def test_failed_capture
- response = @gateway.capture(@amount, '')
+ response = @gateway.capture(@amount, 'z')
assert_failure response
assert_equal 'invalid transaction id', response.message
end
@@ -184,7 +184,7 @@ def test_failed_verify
end
def test_successful_authorize_with_nonfractional_currency
- assert response = @gateway.authorize(100, @credit_card, @options.merge(:currency => 'JPY'))
+ assert response = @gateway.authorize(100, @credit_card, @options.merge(:currency => 'KRW'))
assert_equal 1, response.params['amount']
assert_success response
end
@@ -196,4 +196,14 @@ def test_invalid_login
gateway.purchase(@amount, @credit_card, @options)
end
end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ end
end
diff --git a/test/remote/gateways/remote_conekta_test.rb b/test/remote/gateways/remote_conekta_test.rb
index ba6ddd4f26f..5e30e2582f6 100644
--- a/test/remote/gateways/remote_conekta_test.rb
+++ b/test/remote/gateways/remote_conekta_test.rb
@@ -36,7 +36,16 @@ def setup
name: "Mario Reyes",
phone: "12345678",
},
- carrier: "Estafeta"
+ carrier: "Estafeta",
+ email: "bob@something.com",
+ line_items: [{
+ name: "Box of Cohiba S1s",
+ description: "Imported From Mex.",
+ unit_price: 20000,
+ quantity: 1,
+ sku: "7500244909",
+ type: "food"
+ }]
}
end
@@ -62,6 +71,30 @@ def test_successful_refund
assert_equal nil, response.message
end
+ def test_successful_void
+ assert response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal nil, response.message
+
+ identifier = response.params["id"]
+
+ assert response = @gateway.void(identifier)
+ assert_success response
+ assert_equal nil, response.message
+ end
+
+ def test_unsuccessful_void
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal nil, response.message
+
+ identifier = response.params["id"]
+
+ assert response = @gateway.void(identifier)
+ assert_failure response
+ assert_equal "El cargo no existe o no es apto para esta operación.", response.message
+ end
+
def test_unsuccessful_refund
assert response = @gateway.refund(@amount, "1", @options)
assert_failure response
@@ -108,7 +141,7 @@ def test_successful_purchase_passing_more_details
},
line_items: [
{
- rname: "Box of Cohiba S1s",
+ name: "Box of Cohiba S1s",
description: "Imported From Mex.",
unit_price: 20000,
quantity: 1,
@@ -133,6 +166,12 @@ def test_successful_purchase_passing_more_details
assert_equal "Guerrero", response.params['details']['billing_address']['city']
end
+ def test_failed_purchase_with_no_details
+ assert response = @gateway.purchase(@amount, @credit_card, {})
+ assert_failure response
+ assert_equal "Falta el correo del comprador.", response.message
+ end
+
def test_invalid_key
gateway = ConektaGateway.new(key: 'invalid_token')
assert response = gateway.purchase(@amount, @credit_card, @options)
diff --git a/test/remote/gateways/remote_creditcall_test.rb b/test/remote/gateways/remote_creditcall_test.rb
index abf6c18ff49..fc599ff8b7a 100644
--- a/test/remote/gateways/remote_creditcall_test.rb
+++ b/test/remote/gateways/remote_creditcall_test.rb
@@ -22,6 +22,8 @@ def test_successful_purchase
def test_successful_purchase_sans_options
response = @gateway.purchase(@amount, @credit_card)
assert_success response
+ assert_equal response.params['Zip'], 'notchecked'
+ assert_equal response.params['Address'], 'notchecked'
assert_equal 'Succeeded', response.message
end
@@ -29,7 +31,8 @@ def test_successful_purchase_with_more_options
options = {
order_id: '1',
ip: "127.0.0.1",
- email: "joe@example.com"
+ email: "joe@example.com",
+ manual_type: "cnp"
}
response = @gateway.purchase(@amount, @credit_card, options)
@@ -50,6 +53,22 @@ def test_successful_authorize
assert_equal 'Succeeded', auth.message
end
+ def test_successful_authorize_with_zip_verification
+ response = @gateway.authorize(@amount, @credit_card, @options.merge(verify_zip: 'true'))
+ assert_success response
+ assert_equal response.params['Zip'], 'matched'
+ assert_equal response.params['Address'], 'notchecked'
+ assert_equal 'Succeeded', response.message
+ end
+
+ def test_successful_authorize_with_address_verification
+ response = @gateway.authorize(@amount, @credit_card, @options.merge(verify_address: 'true'))
+ assert_success response
+ assert_equal response.params['Zip'], 'notchecked'
+ assert_equal response.params['Address'], 'matched'
+ assert_equal 'Succeeded', response.message
+ end
+
def test_successful_authorize_and_capture
auth = @gateway.authorize(@amount, @credit_card, @options)
assert_success auth
diff --git a/test/remote/gateways/remote_credorax_test.rb b/test/remote/gateways/remote_credorax_test.rb
new file mode 100644
index 00000000000..02937b313ac
--- /dev/null
+++ b/test/remote/gateways/remote_credorax_test.rb
@@ -0,0 +1,481 @@
+require 'test_helper'
+
+class RemoteCredoraxTest < Test::Unit::TestCase
+ def setup
+ @gateway = CredoraxGateway.new(fixtures(:credorax))
+
+ @amount = 100
+ @credit_card = credit_card('4176661000001015', verification_value: "281", month: "12", year: "2022")
+ @declined_card = credit_card('4176661000001111', verification_value: "681", month: "12", year: "2022")
+ @options = {
+ order_id: "1",
+ currency: "EUR",
+ billing_address: address,
+ description: 'Store Purchase'
+ }
+ end
+
+ def test_invalid_login
+ gateway = CredoraxGateway.new(merchant_id: "", cipher_key: "")
+ response = gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ end
+
+ def test_successful_purchase
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal "1", response.params["H9"]
+ assert_equal "Succeeded", response.message
+ end
+
+ def test_successful_purchase_with_extra_options
+ response = @gateway.purchase(@amount, @credit_card, @options.merge(transaction_type: '10'))
+ assert_success response
+ assert_equal "1", response.params["H9"]
+ assert_equal "Succeeded", response.message
+ end
+
+ def test_failed_purchase
+ response = @gateway.purchase(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal "Transaction not allowed for cardholder", response.message
+ end
+
+ def test_successful_authorize_and_capture
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal "Succeeded", response.message
+ assert response.authorization
+
+ capture = @gateway.capture(@amount, response.authorization)
+ assert_success capture
+ assert_equal "Succeeded", capture.message
+ end
+
+ def test_failed_authorize
+ response = @gateway.authorize(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal "Transaction not allowed for cardholder", response.message
+ end
+
+ def test_failed_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ capture = @gateway.capture(0, auth.authorization)
+ assert_failure capture
+ assert_equal "Invalid amount", capture.message
+ end
+
+ def test_successful_purchase_and_void
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+
+ void = @gateway.void(response.authorization)
+ assert_success void
+ assert_equal "Succeeded", void.message
+ end
+
+ def test_successful_authorize_and_void
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+
+ void = @gateway.void(response.authorization)
+ assert_success void
+ assert_equal "Succeeded", void.message
+ end
+
+ def test_successful_capture_and_void
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal "Succeeded", response.message
+ assert response.authorization
+
+ capture = @gateway.capture(@amount, response.authorization)
+ assert_success capture
+ assert_equal "Succeeded", capture.message
+
+ void = @gateway.void(capture.authorization)
+ assert_success void
+ assert_equal "Succeeded", void.message
+ end
+
+ def test_failed_void
+ response = @gateway.void("")
+ assert_failure response
+ assert_equal "Referred to transaction has not been found.", response.message
+ end
+
+ def test_successful_refund
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+
+ refund = @gateway.refund(@amount, response.authorization)
+ assert_success refund
+ assert_equal "Succeeded", refund.message
+ end
+
+ def test_successful_refund_and_void
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+
+ refund = @gateway.refund(@amount, response.authorization)
+ assert_success refund
+ assert_equal "Succeeded", refund.message
+
+ void = @gateway.void(refund.authorization)
+ assert_success void
+ assert_equal "Succeeded", void.message
+ end
+
+ def test_failed_refund
+ response = @gateway.refund(nil, "123;123;123")
+ assert_failure response
+ assert_equal "Referred to transaction has not been found.", response.message
+ end
+
+ def test_successful_credit
+ response = @gateway.credit(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal "Succeeded", response.message
+ end
+
+ def test_failed_credit
+ response = @gateway.credit(0, @declined_card, @options)
+ assert_failure response
+ assert_equal "Invalid amount", response.message
+ end
+
+ def test_successful_verify
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ assert_equal "Succeeded", response.message
+ end
+
+ def test_failed_verify
+ response = @gateway.verify(@declined_card, @options)
+ assert_failure response
+ assert_equal "Transaction not allowed for cardholder", response.message
+ end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ clean_transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, clean_transcript)
+ assert_scrubbed(@credit_card.verification_value.to_s, clean_transcript)
+ end
+
+ # #########################################################################
+ # # CERTIFICATION SPECIFIC REMOTE TESTS
+ # #########################################################################
+ #
+ # # Send [a5] currency code parameter as "AFN"
+ # def test_certification_error_unregistered_currency
+ # @options[:echo] = "33BE888"
+ # @options[:currency] = "AFN"
+ # response = @gateway.purchase(@amount, @credit_card, @options)
+ # assert_failure response
+ # end
+ #
+ # # Send [b2] parameter as "6"
+ # def test_certification_error_unregistered_card
+ # @options[:echo] = "33BE889"
+ # response = @gateway.purchase(@amount, @credit_card, @options)
+ # assert_failure response
+ # end
+ #
+ # # In the future, merchant expected to investigate each such case offline.
+ # def test_certification_error_no_response_from_the_gate
+ # @options[:echo] = "33BE88A"
+ # response = @gateway.purchase(@amount, @credit_card, @options)
+ # assert_failure response
+ # end
+ #
+ # # Merchant is expected to verify if the code is "0" - in this case the
+ # # transaction should be considered approved. In all other cases the
+ # # offline investigation should take place.
+ # def test_certification_error_unknown_result_code
+ # @options[:echo] = "33BE88B"
+ # response = @gateway.purchase(@amount, @credit_card, @options)
+ # assert_failure response
+ # end
+ #
+ # # Merchant is expected to verify if the code is "00" - in this case the
+ # # transaction should be considered approved. In all other cases the
+ # # transaction is declined. The exact reason should be investigated offline.
+ # def test_certification_error_unknown_response_reason_code
+ # @options[:echo] = "33BE88C"
+ # @options[:email] = "brucewayne@dccomics.com"
+ # @options[:billing_address] = {
+ # address1: "5050 Gotham Drive",
+ # city: "Toronto",
+ # zip: "B2M 1Y9",
+ # state: "ON",
+ # country: "CA",
+ # phone: "(0800)228626"
+ # }
+ #
+ # credit_card = credit_card('4176661000001015',
+ # brand: "visa",
+ # verification_value: "281",
+ # month: "12",
+ # year: "17",
+ # first_name: "Bruce",
+ # last_name: "Wayne")
+ #
+ # response = @gateway.purchase(@amount, credit_card, @options)
+ # assert_failure response
+ # end
+ #
+ # # All fields marked as mandatory are expected to be populated with the
+ # # above default values. Mandatory fields with no values on the
+ # # certification template should be populated with your own meaningful
+ # # values and comply with our API specifications. The d2 parameter is
+ # # mandatory during certification only to allow for tracking of tests.
+ # # Expected result of this test: Time out
+ # def test_certification_time_out
+ # @options[:echo] = "33BE88D"
+ # @options[:email] = "brucewayne@dccomics.com"
+ # @options[:billing_address] = {
+ # address1: "5050 Gotham Drive",
+ # city: "Toronto",
+ # zip: "B2M 1Y9",
+ # state: "ON",
+ # country: "CA",
+ # phone: "(0800)228626"
+ # }
+ #
+ # credit_card = credit_card('5473470000000010',
+ # brand: "master",
+ # verification_value: "939",
+ # month: "12",
+ # year: "17",
+ # first_name: "Bruce",
+ # last_name: "Wayne")
+ #
+ # response = @gateway.purchase(@amount, credit_card, @options)
+ # assert_failure response
+ # end
+ #
+ # # All fields marked as mandatory are expected to be populated
+ # # with the above default values. Mandatory fields with no values
+ # # on the certification template should be populated with your
+ # # own meaningful values and comply with our API specifications.
+ # # The d2 parameter is mandatory during certification only to
+ # # allow for tracking of tests.
+ # def test_certification_za_zb_zc
+ # @options[:echo] = "33BE88E"
+ # @options[:email] = "brucewayne@dccomics.com"
+ # @options[:billing_address] = {
+ # address1: "5050 Gotham Drive",
+ # city: "Toronto",
+ # zip: "B2M 1Y9",
+ # state: "ON",
+ # country: "CA",
+ # phone: "(0800)228626"
+ # }
+ #
+ # credit_card = credit_card('5473470000000010',
+ # verification_value: "939",
+ # month: "12",
+ # year: "17",
+ # first_name: "Bruce",
+ # last_name: "Wayne")
+ #
+ # purchase = @gateway.purchase(@amount, credit_card, @options)
+ # assert_success purchase
+ # assert_equal "Succeeded", purchase.message
+ #
+ # refund_options = {echo: "33BE892"}
+ # refund = @gateway.refund(@amount, purchase.authorization, refund_options)
+ # assert_success refund
+ # assert_equal "Succeeded", refund.message
+ #
+ # void_options = {echo: "33BE895"}
+ # void = @gateway.void(refund.authorization, void_options)
+ # assert_success void
+ # assert_equal "Succeeded", refund.message
+ # end
+ #
+ # # All fields marked as mandatory are expected to be populated
+ # # with the above default values. Mandatory fields with no values
+ # # on the certification template should be populated with your
+ # # own meaningful values and comply with our API specifications.
+ # # The d2 parameter is mandatory during certification only to
+ # # allow for tracking of tests.
+ # def test_certification_zg_zh
+ # @options[:echo] = "33BE88F"
+ # @options[:email] = "clark.kent@dccomics.com"
+ # @options[:billing_address] = {
+ # address1: "2020 Krypton Drive",
+ # city: "Toronto",
+ # zip: "S2M 1YR",
+ # state: "ON",
+ # country: "CA",
+ # phone: "(0800) 78737626"
+ # }
+ #
+ # credit_card = credit_card('4176661000001015',
+ # brand: "visa",
+ # verification_value: "281",
+ # month: "12",
+ # year: "17",
+ # first_name: "Clark",
+ # last_name: "Kent")
+ #
+ # response = @gateway.authorize(@amount, credit_card, @options)
+ # assert_success response
+ # assert_equal "Succeeded", response.message
+ #
+ # capture_options = {echo: "33BE890"}
+ # capture = @gateway.capture(@amount, response.authorization, capture_options)
+ # assert_success capture
+ # assert_equal "Succeeded", capture.message
+ # end
+ #
+ # # All fields marked as mandatory are expected to be populated
+ # # with the above default values. Mandatory fields with no values
+ # # on the certification template should be populated with your
+ # # own meaningful values and comply with our API specifications.
+ # # The d2 parameter is mandatory during certification only to
+ # # allow for tracking of tests.
+ # def test_certification_zg_zj
+ # @options[:echo] = "33BE88F"
+ # @options[:email] = "clark.kent@dccomics.com"
+ # @options[:billing_address] = {
+ # address1: "2020 Krypton Drive",
+ # city: "Toronto",
+ # zip: "S2M 1YR",
+ # state: "ON",
+ # country: "CA",
+ # phone: "(0800) 78737626"
+ # }
+ #
+ # credit_card = credit_card('4176661000001015',
+ # brand: "visa",
+ # verification_value: "281",
+ # month: "12",
+ # year: "17",
+ # first_name: "Clark",
+ # last_name: "Kent")
+ #
+ # response = @gateway.authorize(@amount, credit_card, @options)
+ # assert_success response
+ # assert_equal "Succeeded", response.message
+ #
+ # auth_void_options = {echo: "33BE891"}
+ # auth_void = @gateway.void(response.authorization, auth_void_options)
+ # assert_success auth_void
+ # assert_equal "Succeeded", auth_void.message
+ # end
+ #
+ # # All fields marked as mandatory are expected to be populated
+ # # with the above default values. Mandatory fields with no values
+ # # on the certification template should be populated with your
+ # # own meaningful values and comply with our API specifications.
+ # # The d2 parameter is mandatory during certification only to
+ # # allow for tracking of tests.
+ # #
+ # # Certification for independent credit (credit)
+ # def test_certification_zd
+ # @options[:echo] = "33BE893"
+ # @options[:email] = "wadewilson@marvel.com"
+ # @options[:billing_address] = {
+ # address1: "5050 Deadpool Drive",
+ # city: "Toronto",
+ # zip: "D2P 1Y9",
+ # state: "ON",
+ # country: "CA",
+ # phone: "+1(555)123-4567"
+ # }
+ #
+ # credit_card = credit_card('4176661000001015',
+ # brand: "visa",
+ # verification_value: "281",
+ # month: "12",
+ # year: "17",
+ # first_name: "Wade",
+ # last_name: "Wilson")
+ #
+ # response = @gateway.credit(@amount, credit_card, @options)
+ # assert_success response
+ # assert_equal "Succeeded", response.message
+ # end
+ #
+ # # Use the above values to fill the mandatory parameters in your
+ # # certification test transactions. Note:The d2 parameter is only
+ # # mandatory during certification to allow for tracking of tests.
+ # #
+ # # Certification for purchase void
+ # def test_certification_zf
+ # @options[:echo] = "33BE88E"
+ # @options[:email] = "brucewayne@dccomics.com"
+ # @options[:billing_address] = {
+ # address1: "5050 Gotham Drive",
+ # city: "Toronto",
+ # zip: "B2M 1Y9",
+ # state: "ON",
+ # country: "CA",
+ # phone: "(0800)228626"
+ # }
+ #
+ # credit_card = credit_card('5473470000000010',
+ # verification_value: "939",
+ # month: "12",
+ # year: "17",
+ # first_name: "Bruce",
+ # last_name: "Wayne")
+ #
+ # response = @gateway.purchase(@amount, credit_card, @options)
+ # assert_success response
+ # assert_equal "Succeeded", response.message
+ #
+ # void_options = {echo: "33BE894"}
+ # void = @gateway.void(response.authorization, void_options)
+ # assert_success void
+ # assert_equal "Succeeded", void.message
+ # end
+ #
+ # # Use the above values to fill the mandatory parameters in your
+ # # certification test transactions. Note:The d2 parameter is only
+ # # mandatory during certification to allow for tracking of tests.
+ # #
+ # # Certification for capture void
+ # def test_certification_zi
+ # @options[:echo] = "33BE88F"
+ # @options[:email] = "clark.kent@dccomics.com"
+ # @options[:billing_address] = {
+ # address1: "2020 Krypton Drive",
+ # city: "Toronto",
+ # zip: "S2M 1YR",
+ # state: "ON",
+ # country: "CA",
+ # phone: "(0800) 78737626"
+ # }
+ #
+ # credit_card = credit_card('4176661000001015',
+ # brand: "visa",
+ # verification_value: "281",
+ # month: "12",
+ # year: "17",
+ # first_name: "Clark",
+ # last_name: "Kent")
+ #
+ # authorize = @gateway.authorize(@amount, credit_card, @options)
+ # assert_success authorize
+ # assert_equal "Succeeded", authorize.message
+ #
+ # capture_options = {echo: "33BE890"}
+ # capture = @gateway.capture(@amount, authorize.authorization, capture_options)
+ # assert_success capture
+ # assert_equal "Succeeded", capture.message
+ #
+ # void_options = {echo: "33BE896"}
+ # void = @gateway.void(capture.authorization, void_options)
+ # assert_success void
+ # assert_equal "Succeeded", void.message
+ # end
+end
diff --git a/test/remote/gateways/remote_culqi_test.rb b/test/remote/gateways/remote_culqi_test.rb
new file mode 100644
index 00000000000..1913fd76141
--- /dev/null
+++ b/test/remote/gateways/remote_culqi_test.rb
@@ -0,0 +1,176 @@
+require "test_helper"
+
+class RemoteCulqiTest < Test::Unit::TestCase
+ def setup
+ CulqiGateway.ssl_strict = false # Sandbox has an improperly installed cert
+ @gateway = CulqiGateway.new(fixtures(:culqi))
+
+ @amount = 1000
+ @credit_card = credit_card("4111111111111111")
+ @declined_card = credit_card("4000300011112220", month: 06, year: 2016)
+
+ @options = {
+ order_id: generate_unique_id,
+ billing_address: address
+ }
+ end
+
+ def teardown
+ CulqiGateway.ssl_strict = true
+ end
+
+ def test_invalid_login
+ gateway = CulqiGateway.new(merchant_id: "", terminal_id: "", secret_key: "")
+ response = gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ end
+
+ def test_successful_purchase
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+ assert_match %r{Approved}, purchase.message
+ end
+
+ def test_failed_purchase
+ response = @gateway.purchase(@amount, @declined_card, @options)
+ assert_failure response
+ assert_match %r{Failed}, response.message
+ end
+
+ def test_successful_authorize_and_capture
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+ assert_match %r{Approved}, response.message
+ assert_match %r(^\d+$), response.authorization
+
+ capture = @gateway.capture(@amount, response.authorization)
+ assert_success capture
+ assert_match %r{Transaction has been successfully captured}, capture.message
+ end
+
+ def test_failed_authorize
+ response = @gateway.authorize(@amount, @declined_card, @options)
+ assert_failure response
+ assert_match %r{Failed}, response.message
+ end
+
+ def test_partial_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount-1, auth.authorization)
+ assert_success capture
+ assert_match %r{Transaction has been successfully captured}, capture.message
+ end
+
+ def test_failed_capture
+ response = @gateway.capture(@amount, "0")
+ assert_failure response
+ assert_match %r{Transaction not found}, response.message
+ end
+
+ def test_successful_void
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+
+ void = @gateway.void(response.authorization, @options)
+ assert_success void
+ assert_match %r{cancelled}, void.message
+ end
+
+ def test_failed_void
+ response = @gateway.void("0", @options)
+ assert_failure response
+ assert_match %r{Transaction not found}, response.message
+ end
+
+ def test_successful_refund
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ capture = @gateway.capture(@amount, auth.authorization)
+
+ refund = @gateway.refund(@amount, capture.authorization)
+ assert_success refund
+ assert_match %r{reversed}, refund.message
+ end
+
+ def test_partial_refund
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ capture = @gateway.capture(@amount, auth.authorization)
+
+ refund = @gateway.refund(@amount-1, capture.authorization)
+ assert_success refund
+ assert_match %r{reversed}, refund.message
+ end
+
+ def test_failed_refund
+ response = @gateway.refund(@amount, "0")
+ assert_failure response
+ assert_match %r{Transaction not found}, response.message
+ end
+
+ def test_successful_verify
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ assert_match %r{Approved}, response.message
+ end
+
+ def test_failed_verify
+ response = @gateway.verify(@declined_card, @options)
+ assert_failure response
+ assert_match %r{Failed}, response.message
+ end
+
+ def test_verify_credentials
+ assert @gateway.verify_credentials
+
+ gateway = CulqiGateway.new(merchant_id: 'unknown', terminal_id: 'unknown', secret_key: 'unknown')
+ assert !gateway.verify_credentials
+ gateway = CulqiGateway.new(merchant_id: fixtures(:culqi)[:merchant_id], terminal_id: fixtures(:culqi)[:terminal_id], secret_key: 'unknown')
+ assert !gateway.verify_credentials
+ end
+
+ def test_successful_store_and_purchase
+ credit_card = credit_card("4929927409600297")
+
+ response = @gateway.store(credit_card, @options.merge(partner_id: fixtures(:culqi)[:partner_id]))
+ assert_success response
+ assert_match %r{Card tokenized successfully}, response.message
+
+ purchase = @gateway.purchase(@amount, response.authorization, @options.merge(cvv: credit_card.verification_value))
+ assert_success purchase
+ assert_match %r{Successful}, purchase.message
+
+ response = @gateway.invalidate(response.authorization, @options.merge(partner_id: fixtures(:culqi)[:partner_id]))
+ assert_success response
+ assert_match %r{Token invalidated successfully}, response.message
+ end
+
+ def test_failed_store
+ credit_card = credit_card("4929927409600297")
+
+ store = @gateway.store(credit_card, @options.merge(partner_id: fixtures(:culqi)[:partner_id]))
+ assert_success store
+ assert_match %r{Card tokenized successfully}, store.message
+
+ response = @gateway.store(credit_card, @options.merge(partner_id: fixtures(:culqi)[:partner_id]))
+ assert_failure response
+ assert_match %r{Card already tokenized for same merchant}, response.message
+
+ response = @gateway.invalidate(store.authorization, @options.merge(partner_id: fixtures(:culqi)[:partner_id]))
+ assert_success response
+ assert_match %r{Token invalidated successfully}, response.message
+ end
+
+ def test_transcript_scrubbing
+ assert @gateway.supports_scrubbing?
+
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ clean_transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, clean_transcript)
+ assert_scrubbed(@credit_card.verification_value.to_s, clean_transcript)
+ assert_scrubbed(@gateway.options[:secret_key], clean_transcript)
+ end
+end
diff --git a/test/remote/gateways/remote_cyber_source_test.rb b/test/remote/gateways/remote_cyber_source_test.rb
index 8067a888cb6..3535e079938 100644
--- a/test/remote/gateways/remote_cyber_source_test.rb
+++ b/test/remote/gateways/remote_cyber_source_test.rb
@@ -9,6 +9,24 @@ def setup
@credit_card = credit_card('4111111111111111', verification_value: '321')
@declined_card = credit_card('801111111111111')
@pinless_debit_card = credit_card('4002269999999999')
+ @three_ds_unenrolled_card = credit_card('4000000000000051',
+ verification_value: '321',
+ month: "12",
+ year: "#{Time.now.year + 2}",
+ brand: :visa
+ )
+ @three_ds_enrolled_card = credit_card('4000000000000002',
+ verification_value: '321',
+ month: "12",
+ year: "#{Time.now.year + 2}",
+ brand: :visa
+ )
+ @three_ds_invalid_card = credit_card('4000000000000010',
+ verification_value: '321',
+ month: "12",
+ year: "#{Time.now.year + 2}",
+ brand: :visa
+ )
@amount = 100
@@ -313,7 +331,7 @@ def test_successful_create_subscription_with_setup_fee
def test_successful_create_subscription_with_monthly_options
response = @gateway.store(@credit_card, @subscription_options.merge(:setup_fee => 99.0, :subscription => {:amount => 49.0, :automatic_renew => false, frequency: 'monthly'}))
assert_equal 'Successful transaction', response.message
- response = @gateway.retrieve(";#{response.params['subscriptionID']};", :order_id => @subscription_options[:order_id])
+ response = @gateway.retrieve(response.authorization, order_id: @subscription_options[:order_id])
assert_equal "0.49", response.params['recurringAmount']
assert_equal 'monthly', response.params['frequency']
end
@@ -363,6 +381,64 @@ def test_successful_retrieve_subscription
assert response.test?
end
+ def test_3ds_enroll_request_via_purchase
+ assert response = @gateway.purchase(1202, @three_ds_enrolled_card, @options.merge(payer_auth_enroll_service: true))
+ assert_equal "475", response.params["reasonCode"]
+ assert !response.params["acsURL"].blank?
+ assert !response.params["paReq"].blank?
+ assert !response.params["xid"].blank?
+ assert !response.success?
+ end
+
+ def test_3ds_enroll_request_via_authorize
+ assert response = @gateway.authorize(1202, @three_ds_enrolled_card, @options.merge(payer_auth_enroll_service: true))
+ assert_equal "475", response.params["reasonCode"]
+ assert !response.params["acsURL"].blank?
+ assert !response.params["paReq"].blank?
+ assert !response.params["xid"].blank?
+ assert !response.success?
+ end
+
+ def test_successful_3ds_requests_with_unenrolled_card
+ assert response = @gateway.purchase(1202, @three_ds_unenrolled_card, @options.merge(payer_auth_enroll_service: true))
+ assert response.success?
+
+ assert response = @gateway.authorize(1202, @three_ds_unenrolled_card, @options.merge(payer_auth_enroll_service: true))
+ assert response.success?
+ end
+
+ def test_successful_3ds_validate_purchase_request
+ assert response = @gateway.purchase(1202, @three_ds_enrolled_card, @options.merge(payer_auth_validate_service: true, pares: pares))
+ assert_equal "100", response.params["reasonCode"]
+ assert_equal "0", response.params["authenticationResult"]
+ assert response.success?
+ end
+
+ def test_failed_3ds_validate_purchase_request
+ assert response = @gateway.purchase(1202, @three_ds_invalid_card, @options.merge(payer_auth_validate_service: true, pares: pares))
+ assert_equal "476", response.params["reasonCode"]
+ assert !response.success?
+ end
+
+ def test_successful_3ds_validate_authorize_request
+ assert response = @gateway.authorize(1202, @three_ds_enrolled_card, @options.merge(payer_auth_validate_service: true, pares: pares))
+ assert_equal "100", response.params["reasonCode"]
+ assert_equal "0", response.params["authenticationResult"]
+ assert response.success?
+ end
+
+ def test_failed_3ds_validate_authorize_request
+ assert response = @gateway.authorize(1202, @three_ds_invalid_card, @options.merge(payer_auth_validate_service: true, pares: pares))
+ assert_equal "476", response.params["reasonCode"]
+ assert !response.success?
+ end
+
+ def pares
+ <<-PARES
+eNqdmFuTqkgSgN+N8D90zD46M4B3J+yOKO6goNyFN25yEUHkUsiv31K7T/ec6dg9u75YlWRlZVVmflWw1uNrGNJa6DfX8G0thVXlRuFLErz+tgm67sRlbJr3ky4G9LWn8N/e1nughtVD4dFawFAodT8OqbBx4NLdj/o8y3JqKlavSLsNr1VS5G/En/if4zX20UUTXf3Yzeu3teuXpCC/TeerMTFfY+/d9Tm8CvRbEB7dJqvX2LO7xj7H7Zt7q0JOd0nwpo3VacjVvMc4pZcXfcjFpMqLc6UHr2vsrrEO3Dp8G+P4Ap+PZy/E9C+c+AtfrrGHfH25mwPnokG2CRxfY18Fa7Q71zD3b2/LKXr0o7cOu0uRh0gDre1He419+nZx8zf87z+kepeu9cPbuk7OX31a3X0iFmvsIV9XtVs31Zu9xt5ba99t2zcAAAksNjsr4N5MVctyGIaN2H6E1vpQWYd+8obPkFPo/zEKZFFxTer4fHf174I1dncFe4Tzba0lUY4mu4Yv3TnLURDjur78hWEQwj/h5M/iGmHIYRzDVxhSCKok+tdvz1FhIOTH4n8aRrl5kSe+myW9W6PEkMI6LoKXH759Z0ZX75YITGWoP5CpP3ximv9xl+ATYoZsYt8b/bKyX5nlZ2evlftHFbvEfYKfDL2t1fAY3jMifDFU4fW3f/1KZdBJFFb1/+PKhxtfLXzYM92sCd8qN5U5lrrNDZOFzkiecUIszvyCVJjXj3FPzTX2w/f3hT2j+GW3noobXm8xXJ3KK2aZztNbVdsLWbbOASZgzSY45eYqFNiK5ReRNLKbzvZSIDJj+zqBzIEkIx1L9ZTabYeDJa/MV51fF9A0dxDvxzf5CiPmttuVVBLHxmZSNp53lnBcJzh+IS3YpejKebycHjQlvMggwkvHdZjhYBHf8M1R4ikKjHxMGxlCfuCv+IqmxjTRk9GMnO2ynnXsWMvZSYdlk+Vmvpz1pVns4v05ugRWIGZNMhxUGLzoqs+VDe14Jtzli63TT06WBvpJg2+2UVLie+5mgGDlEVjip+7EmZhCvRdndtQHmKm0vaUDejhYTRgglbR5qysx6I1gf+vTyWJ3ahaXNOWBUrXRYnwasbKlbi3XsJLNuA3g6+uXrHqPzCa8PSNxmKElubX7bGmNl4Z+LbuIEJT8SrnXIMnd7IUOz8XLI4DX3192xucDQGlI8NmnijOiqR/+/rJ9lRCvCqSv6a+7OCl+f6FeDW2N/TzPY2IqvNbJEdUVwqUkCLTVo32vtAhAgQSRQAFNgLRii5vCEeLWl4HCsKQCoJMyWwmcOEAYDBlLlGlKHa2DLRnJ5nCAhkoksypca9nxKfDvUhIUEmvIsX9WL96ZrZTxqvYs82aPjQi1bz7NaBIJHhYpCEXplJ2GA8ea4a7lXCRVgUxk06ai0DSoDecg4wIvE3ZC0ooOQhbinUQzNyn1OzkFM5kWXSS7PWVKNxx8SCV+2VE9EJ8+2TrITF1ScEjBh3WBgere5bJWUpb3ld9lPAMd+e6JNxGQJS4F9vuKdObLigRGbj2LyPyznEmqAZmnxS0DO9o+iCfXmsUeRZIKIXW8Djy0Tw8rks4yX62omWctI2Oc5d7ZvKGokEIKZDI6lfEp4VYQJ+9RAGBHAWUJ7s+HAyraoB4DSmYSEIl4LuOMDMYCIZJ71pj7U99OwbapLHXFMLI66s7eKosO9qmWU56LwmJCul2tccin+XTKE4tV7EatfZaSNCQFH9bYXMNCetuoK2kl0SN6An3f3xmIMwGIT8KlZZS5pV/wpTIz8FzIF9fhIK6EhVLuzEDAg4MI+sybxjVzA/TGuEmsEHDZbZFBtjKxdKfgilSRZDLRoGjQmpWlzUEZGeJ+7CK6jCNPPgQe2ZInYsxH5YEWZoId7i5G2RJNax3USyCJo1OXS/jNLKdCtZiMSaCR4jKPaXvXqjl/6Et+OMBDRoth7MfSnLa3o7ItpxyV8CZcmjrVbJtyWykIypti158qotvx1VkJTm48GzeYBAUaKIAsJhUcDkL9mUO8KjEgBUCiIEdZFKcBjhsxAkpL5cjGxN7nzMYgZElgguweT/ugZg5F0s5BfGT2cGCPWdzRQfCwpkzRoa8YasSpRuIhBMUdRVxBGyn1FouIkytA/p5XKp4iAEO2AMZRSKQkIPDhgLC0ZSKTIV5IsXXC55ue+a566chmgKyLBwZfHlr7igWzo4Dn4m63WjXm3kMV3G7GNc3KJz9Ur5pt1AxBnafhdFf03bi2pnQlT8pZhWNWN7Mu+6RtWe/I6AbUz1wcFd6puR7FdrSYDwcYP5lcIsJ0ZNh7zOxcqcSFOjoUhaui645OzZ5qHGeazOnrqlxJ1+2eSJtTNOo7bBrgyvIanQyHuh9xP/PqO4BROI0Alp6/AOzbLYAh/asAo/t78d0L1ZdQ/mVerrZ+yoQSCZ+wiqCpjNmbw2WNbXW0NyZqFNzU0Uh0dHgTEUqqABnwhAENTjfNUu9WLs751LE60N8xINGsmvkTJTLOqzag/g624UDS72hjelmXP9GmKz9kEmf/R7DR4Ak2ZEmdQv7pz4YmzU84fQHYHWZ+DjomBcrTYiVRuig6KJ1R5Z5dhD5kiRQeewAg3Jqc2SOv+8ASIgVnYOQsf9558pl8OIIWJ4KCQ4u+QWKmIqgK7g5MOZ+0XJ4jemPuucVRUPf5rma5LL6U7RxuXQ4ax+NodrIvC4k53wRDanhGdkGrnhJRq2/UajccHM67ebQItvRyk3PEnFrl1y5dFuT0PEFYMqbn0dG2dlx+js/7Yt7HZFuSVXvsV5OYiTYHec4EG7kxo+GgKfvamoPtDhry3CPLjaJN7okBAJeGPTl7z5+AgQolAQC3wBZtwRGA7U2ViJFJcmnxxgo+jjHdwGGkjs0G5UYccOYJ7XDmP7IgS+9QkEj8YY2OFIsk1WUi3MTJQTed7U3A2YUW3Vh3OND14irp4PiAhSYxHA2siFSZKN1jhOVFme2MOa7LKcst80SEKId+OjqM+9GBjoxIIZfNxsBWkyVmbmYUa4iJghm7gzu+8jeiAxMvJwhiR80zcl4FSr2Q01jx442ebHWlimZHrNQymRgOto7dtFMgbPTdxmG4ayKWQJ+Lp3K0OcQ1rU2jtLyw+XKXOqWoLo7ulVFHgTebYaLWXho+Sr1OPy7AcHCGCar/njbEqWk2ib1Z6iWb3cbm1eTZ6PVXIdCmCAJJ+AEBEYh0tx8xmanGGwngHKWVnCZ4E/qRkgaQ+OgfpYOS+5vi+XoroMHnreA/3XIQBP7LPefzlvPj1oBuOd3zlsOKrYegcC+p4YCPfRmFv5NSZiLpNpR1cLPusvQhw3/IUnIqKRWknr5yDBRNo2dkCVSPmdGNAUBGH8cXr2f29z15gBBCTrfuBb66/SokhoP/gglTIqUPSEjvkNC88QpHo0kEguNHRIaDj5igJAWIBjKgKTJRNmSkUNPwevRaVWGow9Vezev9QtlZJaWDcZpjs3SywiKsxD0p8RVKHQ6u49ExWZz6zY28KaVz4ntbnC0nGDi0G9GFeM2id5cJkwbRKezMS2ZrYcnsZzuDlqaRqx0XJS9F5h6VycYt8nF7TfnOCimzY5NpNyWLIBPzY4ZhNZdu8FKm+3pxwqZyqLHWzSsT5f2mQACop8+THcXu42wXhB5bmeepaHFBHFcOzM7lZZr4DPOPs/073eHgQ5sGD22dBAZE4SSx/vtijxSQsEuSy0gWSqEshkxiw9xVEJhqg78mbmrU3nxGzJe1fLxwDDO59rxHzgrpzPiHrvK8WlDJpo33y3MdhU7GZ81W6fFSHfnjYpbBcDjo4CLNjoAvSxRlLaU2W76plphc5At/tEhKra8VXiLN0FuM59Ddt5zgHZitL1vFyttHamkZ44sToxvD5ubwK/BtsWOfr03Yj1epz5esx7ekx8eu+/ePrx/B/g0UAjN8
+ PARES
+ end
+
def test_verify_credentials
assert @gateway.verify_credentials
diff --git a/test/remote/gateways/remote_data_cash_test.rb b/test/remote/gateways/remote_data_cash_test.rb
index f6fffda0131..9ff6635e5f9 100644
--- a/test/remote/gateways/remote_data_cash_test.rb
+++ b/test/remote/gateways/remote_data_cash_test.rb
@@ -44,8 +44,7 @@ def setup
:brand => :solo,
:issue_number => 5,
:start_month => 12,
- :start_year => 2006,
- :verification_value => 444
+ :start_year => 2006
)
@address = {
@@ -303,6 +302,23 @@ def test_fail_to_refund_purchase_which_is_already_refunded
assert_equal '1.98 > remaining funds 0.00', second_refund.message
end
+ def test_successful_refund_of_a_repeat_payment
+ @params[:set_up_continuous_authority] = true
+ response = @gateway.purchase(@amount, @mastercard, @params)
+ assert_success response
+ assert !response.authorization.to_s.split(';')[2].blank?
+ assert response.test?
+
+ # Make second payment on the continuous authorization that was set up in the first purchase
+ second_order_params = { :order_id => generate_unique_id }
+ purchase = @gateway.purchase(201, response.authorization, second_order_params)
+ assert_success purchase
+
+ # Refund payment that was made via the continuous authorization payment above
+ refund = @gateway.refund(201, purchase.authorization)
+ assert_success refund
+ end
+
def test_order_id_that_is_too_short
@params[:order_id] = @params[:order_id].first(5)
response = @gateway.purchase(@amount, @mastercard, @params)
@@ -316,4 +332,14 @@ def test_order_id_that_is_too_long
assert response.test?
end
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @visa_delta, @params)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@visa_delta.number, transcript)
+ assert_scrubbed(@visa_delta.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
end
diff --git a/test/remote/gateways/remote_dibs_test.rb b/test/remote/gateways/remote_dibs_test.rb
index ab7079dfc93..532be65a61a 100644
--- a/test/remote/gateways/remote_dibs_test.rb
+++ b/test/remote/gateways/remote_dibs_test.rb
@@ -172,7 +172,9 @@ def test_transcript_scrubbing
@gateway.purchase(@amount, @credit_card, @options)
end
clean_transcript = @gateway.scrub(transcript)
+
assert_scrubbed(@credit_card.number, clean_transcript)
assert_scrubbed(@credit_card.verification_value.to_s, clean_transcript)
+ assert_scrubbed(@gateway.options[:secret_key], clean_transcript)
end
end
diff --git a/test/remote/gateways/remote_digitzs_test.rb b/test/remote/gateways/remote_digitzs_test.rb
new file mode 100644
index 00000000000..1074d1556f1
--- /dev/null
+++ b/test/remote/gateways/remote_digitzs_test.rb
@@ -0,0 +1,136 @@
+require 'test_helper'
+
+class RemoteDigitzsTest < Test::Unit::TestCase
+ def setup
+ @gateway = DigitzsGateway.new(fixtures(:digitzs))
+
+ @amount = 500
+ @credit_card = credit_card('4747474747474747', verification_value: '999')
+ @declined_card = credit_card('4616161616161616')
+ @options = {
+ merchant_id: 'spreedly-susanswidg-32268973-2091076-148408385',
+ billing_address: address,
+ description: 'Store Purchase'
+ }
+
+ @options_card_split = {
+ merchant_id: 'spreedly-susanswidg-32268973-2091076-148408385',
+ billing_address: address,
+ description: 'Split Purchase',
+ payment_type: 'card_split',
+ split_amount: 100,
+ split_merchant_id: 'spreedly-susanswidg-32270590-2095203-148657924'
+ }
+
+ @options_token_split = {
+ merchant_id: 'spreedly-susanswidg-32268973-2091076-148408385',
+ billing_address: address,
+ description: 'Token Split Purchase',
+ payment_type: 'token_split',
+ split_amount: 100,
+ split_merchant_id: 'spreedly-susanswidg-32270590-2095203-148657924'
+ }
+ end
+
+ def test_successful_purchase
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'Success', response.message
+ end
+
+ def test_successful_token_purchase
+ assert store = @gateway.store(@credit_card, @options)
+ assert_success store
+
+ response = @gateway.purchase(@amount, store.authorization, @options)
+ assert_success response
+ assert_equal 'Success', response.message
+ end
+
+ def test_successful_token_split_purchase
+ assert store = @gateway.store(@credit_card, @options)
+ assert_success store
+
+ response = @gateway.purchase(@amount, store.authorization, @options_token_split)
+ assert_success response
+ assert_equal 'Success', response.message
+ assert response.params["data"]["attributes"]["split"]["splitId"]
+ end
+
+ def test_successful_card_split_purchase
+ response = @gateway.purchase(@amount, @credit_card, @options_card_split)
+ assert_success response
+ assert_equal 'Success', response.message
+ assert response.params["data"]["attributes"]["split"]["splitId"]
+ end
+
+ def test_failed_purchase
+ response = @gateway.purchase(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal 'Partner error: Credit card declined (transaction element shows reason for decline)', response.message
+ end
+
+ def test_successful_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount, purchase.authorization, @options)
+ assert_success refund
+ assert_equal 'Success', refund.message
+ end
+
+ def test_partial_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount-1, purchase.authorization, @options)
+ assert_success refund
+ end
+
+ def test_failed_refund
+ response = @gateway.refund(@amount, '', @options)
+ assert_failure response
+ assert_equal '"id" is not allowed to be empty', response.message
+ end
+
+ def test_successful_store
+ assert response = @gateway.store(@credit_card, @options)
+ assert_success response
+ end
+
+ def test_successful_store_without_billing_address
+ assert response = @gateway.store(@credit_card, {merchant_id: 'spreedly-susanswidg-32268973-2091076-148408385'})
+ assert_success response
+ end
+
+ def test_store_adds_card_to_existing_customer
+ assert response = @gateway.store(@credit_card, @options.merge({customer_id: "spreedly-susanswidg-32268973-2091076-148408385-5980208887457495-148700575"}))
+ assert_success response
+ end
+
+ def test_store_creates_new_customer_and_adds_card
+ assert response = @gateway.store(@credit_card, @options.merge({customer_id: "nonexistant"}))
+ assert_success response
+ end
+
+ def test_invalid_login
+ gateway = DigitzsGateway.new(app_key: '', api_key: '', merchant_id: '')
+
+ response = gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_match %r{Forbidden}, response.message
+ end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:api_key], transcript)
+ assert_scrubbed(@gateway.options[:app_key], transcript)
+ end
+
+end
diff --git a/test/remote/gateways/remote_ebanx_test.rb b/test/remote/gateways/remote_ebanx_test.rb
new file mode 100644
index 00000000000..8c8232d24f5
--- /dev/null
+++ b/test/remote/gateways/remote_ebanx_test.rb
@@ -0,0 +1,218 @@
+require 'test_helper'
+
+class RemoteEbanxTest < Test::Unit::TestCase
+ def setup
+ @gateway = EbanxGateway.new(fixtures(:ebanx))
+
+ @amount = 100
+ @credit_card = credit_card('4111111111111111')
+ @declined_card = credit_card('5102026827345142')
+ @options = {
+ billing_address: address({
+ address1: '1040 Rua E',
+ city: 'Maracanaú',
+ state: 'CE',
+ zip: '61919-230',
+ country: 'BR',
+ phone_number: '8522847035'
+ }),
+ order_id: generate_unique_id,
+ document: "853.513.468-93"
+ }
+ end
+
+ def test_successful_purchase
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'Accepted', response.message
+ end
+
+ def test_successful_purchase_with_more_options
+ options = @options.merge({
+ order_id: generate_unique_id,
+ ip: "127.0.0.1",
+ email: "joe@example.com",
+ birth_date: "10/11/1980",
+ person_type: 'personal'
+ })
+
+ response = @gateway.purchase(@amount, @credit_card, options)
+ assert_success response
+ assert_equal 'Accepted', response.message
+ end
+
+ def test_successful_purchase_as_brazil_business_with_responsible_fields
+ options = @options.update(document: "32593371000110",
+ person_type: 'business',
+ responsible_name: 'Business Person',
+ responsible_document: '32593371000111',
+ responsible_birth_date: "1/11/1975")
+
+ response = @gateway.purchase(@amount, @credit_card, options)
+ assert_success response
+ assert_equal 'Accepted', response.message
+ end
+
+ def test_successful_purchase_as_colombian
+ options = @options.merge({
+ order_id: generate_unique_id,
+ ip: "127.0.0.1",
+ email: "jose@example.com.co",
+ birth_date: "10/11/1980",
+ billing_address: address({
+ address1: '1040 Rua E',
+ city: 'Medellín',
+ state: 'AN',
+ zip: '29269',
+ country: 'CO',
+ phone_number: '8522847035'
+ })
+ })
+
+ response = @gateway.purchase(500, @credit_card, options)
+ assert_success response
+ assert_equal 'Sandbox - Test credit card, transaction captured', response.message
+ end
+
+ def test_failed_purchase
+ response = @gateway.purchase(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal 'Sandbox - Test credit card, transaction declined reason insufficientFunds', response.message
+ assert_equal 'NOK', response.error_code
+ end
+
+ def test_successful_authorize_and_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+ assert_equal 'Accepted', auth.message
+
+ assert capture = @gateway.capture(@amount, auth.authorization)
+ assert_success capture
+ assert_equal 'Accepted', capture.message
+ end
+
+ def test_failed_authorize
+ response = @gateway.authorize(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal 'Sandbox - Test credit card, transaction declined reason insufficientFunds', response.message
+ assert_equal 'NOK', response.error_code
+ end
+
+ def test_partial_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount-1, auth.authorization)
+ assert_success capture
+ end
+
+ def test_failed_capture
+ response = @gateway.capture(@amount, '')
+ assert_failure response
+ assert_equal 'Parameters hash or merchant_payment_code not informed', response.message
+ end
+
+ def test_successful_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ refund_options = @options.merge({description: "full refund"})
+ assert refund = @gateway.refund(@amount, purchase.authorization, refund_options)
+ assert_success refund
+ assert_equal 'Accepted', refund.message
+ end
+
+ def test_partial_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ refund_options = @options.merge(description: "refund due to returned item")
+ assert refund = @gateway.refund(@amount-1, purchase.authorization, refund_options)
+ assert_success refund
+ end
+
+ def test_failed_refund
+ response = @gateway.refund(@amount, '')
+ assert_failure response
+ assert_match('Parameter hash not informed', response.message)
+ end
+
+ def test_successful_void
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert void = @gateway.void(auth.authorization)
+ assert_success void
+ assert_equal 'Accepted', void.message
+ end
+
+ def test_failed_void
+ response = @gateway.void('')
+ assert_failure response
+ assert_equal 'Parameter hash not informed', response.message
+ end
+
+ def test_successful_store_and_purchase
+ store = @gateway.store(@credit_card, @options)
+ assert_success store
+
+ assert purchase = @gateway.purchase(@amount, store.authorization, @options)
+ assert_success purchase
+ assert_equal 'Accepted', purchase.message
+ end
+
+ def test_successful_store_and_purchase_as_brazil_business
+ options = @options.update(document: "32593371000110",
+ person_type: 'business',
+ responsible_name: 'Business Person',
+ responsible_document: '32593371000111',
+ responsible_birth_date: "1/11/1975")
+
+ store = @gateway.store(@credit_card, options)
+ assert_success store
+
+ assert purchase = @gateway.purchase(@amount, store.authorization, options)
+ assert_success purchase
+ assert_equal 'Accepted', purchase.message
+ end
+
+ def test_failed_purchase_with_stored_card
+ store = @gateway.store(@declined_card, @options)
+ assert_success store
+
+ assert purchase = @gateway.purchase(@amount, store.authorization, @options)
+ assert_failure purchase
+ end
+
+ def test_successful_verify
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ assert_match %r{Accepted}, response.message
+ end
+
+ def test_failed_verify
+ response = @gateway.verify(@declined_card, @options)
+ assert_failure response
+ assert_match %r{Accepted}, response.message
+ end
+
+ def test_invalid_login
+ gateway = EbanxGateway.new(integration_key: '')
+
+ response = gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_match %r{Field integration_key is required}, response.message
+ end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:integration_key], transcript)
+ end
+
+end
diff --git a/test/remote/gateways/remote_elavon_test.rb b/test/remote/gateways/remote_elavon_test.rb
index fefb5f3fe1c..866fcd21eb6 100644
--- a/test/remote/gateways/remote_elavon_test.rb
+++ b/test/remote/gateways/remote_elavon_test.rb
@@ -4,7 +4,7 @@ class RemoteElavonTest < Test::Unit::TestCase
def setup
@gateway = ElavonGateway.new(fixtures(:elavon))
- @credit_card = credit_card('4111111111111111')
+ @credit_card = credit_card('4124939999999990')
@bad_credit_card = credit_card('invalid')
@options = {
@@ -60,8 +60,7 @@ def test_unsuccessful_capture
end
def test_unsuccessful_authorization
- @credit_card.number = "1234567890123"
- assert response = @gateway.authorize(@amount, @credit_card, @options)
+ assert response = @gateway.authorize(@amount, @bad_credit_card, @options)
assert_failure response
assert_equal 'The Credit Card Number supplied in the authorization request appears to be invalid.', response.message
end
@@ -168,7 +167,7 @@ def test_unsuccessful_store
def test_successful_update
store_response = @gateway.store(@credit_card, @options)
token = store_response.params["token"]
- credit_card = credit_card('4111111111111111', :month => 10)
+ credit_card = credit_card('4124939999999990', :month => 10)
assert response = @gateway.update(token, credit_card, @options)
assert_success response
assert response.test?
@@ -196,4 +195,25 @@ def test_failed_purchase_with_token
assert response.test?
assert_match %r{invalid}i, response.message
end
+
+ def test_successful_purchase_with_custom_fields
+ assert response = @gateway.purchase(@amount, @credit_card, @options.merge(custom_fields: {a_key: "a value"}))
+
+ assert_success response
+ assert response.test?
+ assert_equal 'APPROVAL', response.message
+ assert response.authorization
+ end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
+
end
diff --git a/test/remote/gateways/remote_element_test.rb b/test/remote/gateways/remote_element_test.rb
index 5a9bc733b3c..b6a1da7a1b6 100644
--- a/test/remote/gateways/remote_element_test.rb
+++ b/test/remote/gateways/remote_element_test.rb
@@ -8,6 +8,7 @@ def setup
@credit_card = credit_card('4000100011112224')
@check = check
@options = {
+ order_id: '1',
billing_address: address,
description: 'Store Purchase'
}
@@ -17,6 +18,8 @@ def test_successful_purchase
response = @gateway.purchase(@amount, @credit_card, @options)
assert_success response
assert_equal 'Approved', response.message
+ assert_match %r{Street address and postal code do not match}, response.avs_result["message"]
+ assert_match %r{CVV matches}, response.cvv_result["message"]
end
def test_failed_purchase
@@ -41,6 +44,12 @@ def test_successful_purchase_with_payment_account_token
assert_equal 'Approved', response.message
end
+ def test_successful_purchase_with_shipping_address
+ response = @gateway.purchase(@amount, @credit_card, @options.merge(shipping_address: address(address1: "Shipping")))
+ assert_success response
+ assert_equal 'Approved', response.message
+ end
+
def test_successful_authorize_and_capture
auth = @gateway.authorize(@amount, @credit_card, @options)
assert_success auth
diff --git a/test/remote/gateways/remote_fat_zebra_test.rb b/test/remote/gateways/remote_fat_zebra_test.rb
index 6b2cd7eba7a..8dbe064d0ec 100644
--- a/test/remote/gateways/remote_fat_zebra_test.rb
+++ b/test/remote/gateways/remote_fat_zebra_test.rb
@@ -10,7 +10,7 @@ def setup
@options = {
:order_id => rand(100000).to_s,
- :ip => "123.1.2.3"
+ :ip => "1.2.3.4"
}
end
@@ -27,12 +27,6 @@ def test_successful_multi_currency_purchase
assert_equal 'USD', response.params['response']['currency']
end
- def test_successful_purchase_with_descriptor
- assert response = @gateway.purchase(@amount, @credit_card, @options.merge(:merchant => 'Merchant', :merchant_location => 'Location'))
- assert_success response
- assert_equal 'Approved', response.message
- end
-
def test_unsuccessful_multi_currency_purchase
assert response = @gateway.purchase(@amount, @credit_card, @options.merge(:currency => 'XYZ'))
assert_failure response
@@ -107,11 +101,11 @@ def test_refund
end
def test_invalid_refund
- purchase = @gateway.purchase(@amount, @credit_card, @options)
+ @gateway.purchase(@amount, @credit_card, @options)
assert response = @gateway.refund(@amount, nil, @options)
assert_failure response
- assert_match %r{Original transaction is required}, response.message
+ assert_match %r{Invalid credit card for unmatched refund}, response.message
end
def test_store
@@ -127,6 +121,24 @@ def test_purchase_with_token
assert_success purchase
end
+ def test_successful_purchase_with_descriptor
+ assert response = @gateway.purchase(@amount, @credit_card, @options.merge(:merchant => 'Merchant', :merchant_location => 'Location'))
+ assert_success response
+ assert_equal 'Approved', response.message
+ end
+
+ def test_successful_purchase_with_3DS_information
+ assert response = @gateway.purchase(@amount, @credit_card, @options.merge(:cavv => 'MDRjN2MxZTAxYjllNTBkNmM2MTA=', :xid => 'MGVmMmNlMzI4NjAyOWU2ZDgwNTQ=', :sli => '05'))
+ assert_success response
+ assert_equal 'Approved', response.message
+ end
+
+ def test_failed_purchase_with_incomplete_3DS_information
+ assert response = @gateway.purchase(@amount, @credit_card, @options.merge(:cavv => 'MDRjN2MxZTAxYjllNTBkNmM2MTA=', :sli => '05'))
+ assert_failure response
+ assert_match %r{Extra/xid is required for SLI 05}, response.message
+ end
+
def test_invalid_login
gateway = FatZebraGateway.new(
:username => 'invalid',
@@ -136,4 +148,14 @@ def test_invalid_login
assert_failure response
assert_equal 'Invalid Login', response.message
end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ end
end
diff --git a/test/remote/gateways/remote_first_pay_test.rb b/test/remote/gateways/remote_first_pay_test.rb
index b64b05e578f..68607203107 100644
--- a/test/remote/gateways/remote_first_pay_test.rb
+++ b/test/remote/gateways/remote_first_pay_test.rb
@@ -27,6 +27,14 @@ def test_failed_purchase
assert_equal 'Declined', response.message
end
+ def test_failed_purchase_with_no_address
+ @options.delete(:billing_address)
+ response = @gateway.purchase(@amount, @credit_card, @options)
+
+ assert_failure response
+ assert_equal 'Address is invalid (street, city, zip, state and or country fields)', response.message
+ end
+
def test_successful_authorize_and_capture
auth = @gateway.authorize(@amount, @credit_card, @options)
assert_success auth
diff --git a/test/remote/gateways/remote_firstdata_e4_test.rb b/test/remote/gateways/remote_firstdata_e4_test.rb
index 4cb4fd41974..51e1bf6af58 100755
--- a/test/remote/gateways/remote_firstdata_e4_test.rb
+++ b/test/remote/gateways/remote_firstdata_e4_test.rb
@@ -25,6 +25,17 @@ def test_successful_purchase
assert_success response
end
+ def test_successful_purchase_with_network_tokenization
+ @credit_card = network_tokenization_credit_card('4242424242424242',
+ payment_cryptogram: "BwABB4JRdgAAAAAAiFF2AAAAAAA=",
+ verification_value: nil
+ )
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'Transaction Normal - Approved', response.message
+ assert_false response.authorization.blank?
+ end
+
def test_successful_purchase_with_specified_currency
options_with_specified_currency = @options.merge({currency: 'GBP'})
assert response = @gateway.purchase(@amount, @credit_card, options_with_specified_currency)
@@ -217,7 +228,25 @@ def test_refund_with_track_data
assert response.authorization
end
- def test_dump_transcript
- # See firstdata_e4_test.rb for an example of a scrubbed transcript
+ def test_verify_credentials
+ assert @gateway.verify_credentials
+
+ gateway = FirstdataE4Gateway.new(login: 'unknown', password: 'unknown')
+ assert !gateway.verify_credentials
+ gateway = FirstdataE4Gateway.new(login: fixtures(:firstdata_e4)[:login], password: 'unknown')
+ assert !gateway.verify_credentials
end
+
+ def test_transcript_scrubbing
+ cc_with_different_cvc = credit_card(verification_value: '999')
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, cc_with_different_cvc, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(cc_with_different_cvc.number, transcript)
+ assert_scrubbed(cc_with_different_cvc.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
+
end
diff --git a/test/remote/gateways/remote_forte_test.rb b/test/remote/gateways/remote_forte_test.rb
index 782a72a42ae..f54bb27f623 100644
--- a/test/remote/gateways/remote_forte_test.rb
+++ b/test/remote/gateways/remote_forte_test.rb
@@ -21,15 +21,17 @@ def setup
@options = {
billing_address: address,
- description: 'Store Purchase'
+ description: 'Store Purchase',
+ order_id: '1'
}
+
end
def test_invalid_login
gateway = ForteGateway.new(api_key: "InvalidKey", secret: "InvalidSecret", location_id: "11", account_id: "323")
assert response = gateway.purchase(@amount, @credit_card, @options)
assert_failure response
- assert_equal "UserName and Password combination not found.", response.message
+ assert_match "combination not found.", response.message
end
def test_successful_purchase
@@ -76,7 +78,7 @@ def test_successful_authorize_and_capture
wait_for_authorization_to_clear
- assert capture = @gateway.capture(@amount, auth.authorization)
+ assert capture = @gateway.capture(@amount, auth.authorization, @options)
assert_success capture
assert_equal 'APPROVED', capture.message
end
@@ -94,14 +96,14 @@ def test_partial_capture
wait_for_authorization_to_clear
- assert capture = @gateway.capture(@amount-1, auth.authorization)
+ assert capture = @gateway.capture(@amount-1, auth.authorization, @options)
assert_success capture
end
def test_failed_capture
- response = @gateway.capture(@amount, '')
+ response = @gateway.capture(@amount, '', @options)
assert_failure response
- assert_equal 'The field transaction_id is required.', response.message
+ assert_match 'field transaction_id', response.message
end
def test_successful_credit
@@ -140,7 +142,7 @@ def test_successful_void
def test_failed_void
response = @gateway.void('')
assert_failure response
- assert_equal 'The field transaction_id is required.', response.message
+ assert_match 'field transaction_id', response.message
end
def test_successful_verify
@@ -170,7 +172,7 @@ def test_transcript_scrubbing
private
def wait_for_authorization_to_clear
- sleep(7)
+ sleep(10)
end
end
diff --git a/test/remote/gateways/remote_global_collect_test.rb b/test/remote/gateways/remote_global_collect_test.rb
index 98617911a09..a167ca7caa0 100644
--- a/test/remote/gateways/remote_global_collect_test.rb
+++ b/test/remote/gateways/remote_global_collect_test.rb
@@ -21,11 +21,32 @@ def test_successful_purchase
assert_equal 'Succeeded', response.message
end
+ def test_successful_purchase_with_fraud_fields
+ options = @options.merge(
+ fraud_fields:
+ {
+ "website" => "www.example.com",
+ "giftMessage" => "Happy Day!"
+ }
+ )
+
+ response = @gateway.purchase(@amount, @credit_card, options)
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ end
+
def test_successful_purchase_with_more_options
options = @options.merge(
order_id: '1',
ip: "127.0.0.1",
- email: "joe@example.com"
+ email: "joe@example.com",
+ sdk_identifier: 'Channel',
+ sdk_creator: 'Bob',
+ integrator: 'Bill',
+ creator: 'Super',
+ name: 'Cala',
+ version: '1.0',
+ extension_ID: '5555555'
)
response = @gateway.purchase(@amount, @credit_card, options)
@@ -33,6 +54,14 @@ def test_successful_purchase_with_more_options
assert_equal 'Succeeded', response.message
end
+ def test_successful_purchase_with_very_long_name
+ credit_card = credit_card('4567350000427977', { first_name: "thisisaverylongfirstname"})
+
+ response = @gateway.purchase(@amount, credit_card, @options)
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ end
+
def test_failed_purchase
response = @gateway.purchase(@rejected_amount, @declined_card, @options)
assert_failure response
@@ -65,7 +94,7 @@ def test_partial_capture
def test_failed_capture
response = @gateway.capture(@amount, '123', @options)
assert_failure response
- assert_equal 'INVALID_PAYMENT_ID', response.message
+ assert_match %r{UNKNOWN_PAYMENT_ID}, response.message
end
# Because payments are not fully authorized immediately, refunds can only be
@@ -89,7 +118,7 @@ def test_failed_capture
def test_failed_refund
response = @gateway.refund(@amount, '123')
assert_failure response
- assert_equal 'INVALID_PAYMENT_ID', response.message
+ assert_match %r{UNKNOWN_PAYMENT_ID}, response.message
end
def test_successful_void
@@ -104,7 +133,7 @@ def test_successful_void
def test_failed_void
response = @gateway.void('123')
assert_failure response
- assert_equal 'INVALID_PAYMENT_ID', response.message
+ assert_match %r{UNKNOWN_PAYMENT_ID}, response.message
end
def test_successful_verify
diff --git a/test/remote/gateways/remote_global_transport_test.rb b/test/remote/gateways/remote_global_transport_test.rb
index 476d89f3a3c..da308566248 100644
--- a/test/remote/gateways/remote_global_transport_test.rb
+++ b/test/remote/gateways/remote_global_transport_test.rb
@@ -19,6 +19,13 @@ def test_successful_purchase
assert_equal 'Approved', response.message
end
+ def test_successful_partial_purchase
+ @credit_card = credit_card('4111111111111111')
+ response = @gateway.purchase(2354, @credit_card, @options)
+ assert_success response
+ assert_equal 'Partial Approval', response.message
+ end
+
def test_failed_purchase
response = @gateway.purchase(2304, @credit_card, @options)
assert_failure response
@@ -40,11 +47,13 @@ def test_failed_authorize
assert_equal "Declined", response.message
end
- def test_partial_capture
- auth = @gateway.authorize(500, @credit_card, @options)
+ def test_successful_partial_authorize_and_capture
+ @credit_card = credit_card('4111111111111111')
+ auth = @gateway.authorize(2354, @credit_card, @options)
assert_success auth
+ assert_equal "Partial Approval", auth.message
- assert capture = @gateway.capture(499, auth.authorization)
+ assert capture = @gateway.capture(2000, auth.authorization)
assert_success capture
end
@@ -117,4 +126,16 @@ def test_invalid_login
assert_failure response
assert_equal("Invalid Login Information", response.message)
end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(500, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:global_password], transcript)
+ end
+
end
diff --git a/test/remote/gateways/remote_hps_test.rb b/test/remote/gateways/remote_hps_test.rb
index 132f3b7be61..4c183b092ef 100644
--- a/test/remote/gateways/remote_hps_test.rb
+++ b/test/remote/gateways/remote_hps_test.rb
@@ -250,4 +250,15 @@ def tests_failed_verify
assert_failure response
assert_equal 'The card number is not a valid credit card number.', response.message
end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:secret_api_key], transcript)
+ end
end
diff --git a/test/remote/gateways/remote_iats_payments_test.rb b/test/remote/gateways/remote_iats_payments_test.rb
index 635eb8dcad7..5f68a03a1d6 100644
--- a/test/remote/gateways/remote_iats_payments_test.rb
+++ b/test/remote/gateways/remote_iats_payments_test.rb
@@ -112,4 +112,29 @@ def test_invalid_login
assert response = gateway.purchase(@amount, @credit_card)
assert_failure response
end
+
+ def test_purchase_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(credit_card.number, transcript)
+ assert_scrubbed(credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:agent_code], transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
+
+ def test_check_purchase_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @check, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@check.routing_number, transcript)
+ assert_scrubbed(@check.account_number, transcript)
+ assert_scrubbed(@gateway.options[:agent_code], transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
+
end
diff --git a/test/remote/gateways/remote_iveri_test.rb b/test/remote/gateways/remote_iveri_test.rb
new file mode 100644
index 00000000000..2a9ef82ff49
--- /dev/null
+++ b/test/remote/gateways/remote_iveri_test.rb
@@ -0,0 +1,165 @@
+require 'test_helper'
+
+class RemoteIveriTest < Test::Unit::TestCase
+ def setup
+ @gateway = IveriGateway.new(fixtures(:iveri))
+
+ @amount = 100
+ @credit_card = credit_card('4242424242424242')
+ @bad_card = credit_card('2121212121212121')
+ @timeout_card = credit_card('5454545454545454')
+ @invalid_card = credit_card('1111222233334444')
+ @options = {
+ order_id: generate_unique_id,
+ billing_address: address,
+ description: 'Store Purchase'
+ }
+ end
+
+ def test_successful_purchase
+ response = @gateway.purchase(@amount, @credit_card, @options)
+
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ assert_equal '100', response.params['amount']
+ end
+
+ def test_successful_purchase_with_more_options
+ options = {
+ ip: "127.0.0.1",
+ email: "joe@example.com",
+ currency: 'ZAR'
+ }
+
+ response = @gateway.purchase(@amount, @credit_card, options)
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ end
+
+ def test_successful_purchase_with_3ds_params
+ options = {
+ eci: "ThreeDSecure",
+ xid: SecureRandom.hex(14),
+ cavv: SecureRandom.hex(14)
+ }
+
+ response = @gateway.purchase(@amount, @credit_card, options)
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ end
+
+
+ def test_failed_purchase
+ response = @gateway.purchase(@amount, @bad_card, @options)
+ assert_failure response
+ assert_includes ['Denied', 'Hot card', 'Please call'], response.message
+ end
+
+ def test_successful_authorize_and_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount, auth.authorization)
+ assert_success capture
+ assert_equal 'Succeeded', capture.message
+ end
+
+ def test_failed_authorize
+ response = @gateway.authorize(@amount, @bad_card, @options)
+ assert_failure response
+ assert_includes ['Denied', 'Hot card', 'Please call'], response.message
+ end
+
+ def test_partial_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount-1, auth.authorization)
+ assert_success capture
+ end
+
+ def test_failed_capture
+ response = @gateway.capture(@amount, '')
+ assert_failure response
+ assert_equal 'Missing PAN', response.message
+ end
+
+ def test_successful_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount, purchase.authorization, @options)
+ assert_success refund
+ assert_equal 'Succeeded', refund.message
+ end
+
+ def test_partial_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount-1, purchase.authorization)
+ assert_success refund
+ end
+
+ def test_failed_refund
+ response = @gateway.refund(@amount, '', @options)
+ assert_failure response
+ assert_match %r{Credit is not supported}, response.message
+ end
+
+ def test_successful_void
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert void = @gateway.void(auth.authorization)
+ assert_success void
+ assert_equal 'Succeeded', void.message
+ end
+
+ def test_failed_void
+ response = @gateway.void('')
+ assert_failure response
+ assert_equal 'Missing OriginalMerchantTrace', response.message
+ end
+
+ def test_successful_verify
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ end
+
+ def test_failed_verify
+ response = @gateway.verify(@bad_card, @options)
+ assert_failure response
+ assert_includes ['Denied', 'Hot card', 'Please call'], response.message
+ end
+
+ def test_successful_verify_credentials
+ assert @gateway.verify_credentials
+ end
+
+ def test_failed_verify_credentials
+ gateway = IveriGateway.new(app_id: '11111111-1111-1111-1111-111111111111', cert_id: '11111111-1111-1111-1111-111111111111')
+ assert !gateway.verify_credentials
+ end
+
+ def test_invalid_login
+ gateway = IveriGateway.new(app_id: '', cert_id: '')
+
+ response = gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal 'No CertificateID specified', response.message
+ end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:cert_id], transcript)
+ end
+
+end
diff --git a/test/remote/gateways/remote_jetpay_test.rb b/test/remote/gateways/remote_jetpay_test.rb
index d528a3694b5..48ef90770f5 100644
--- a/test/remote/gateways/remote_jetpay_test.rb
+++ b/test/remote/gateways/remote_jetpay_test.rb
@@ -89,8 +89,7 @@ def test_void
assert_success void
end
- def test_refund_with_token
-
+ def test_purchase_refund_with_token
assert response = @gateway.purchase(9900, @credit_card, @options)
assert_success response
assert_equal "APPROVED", response.message
@@ -105,6 +104,25 @@ def test_refund_with_token
assert_equal [response.params['transaction_id'], response.params["approval"], 9900, response.params["token"]].join(";"), response.authorization
end
+ def test_capture_refund_with_token
+ assert auth = @gateway.authorize(9900, @credit_card, @options)
+ assert_success auth
+ assert_equal 'APPROVED', auth.message
+ assert_not_nil auth.authorization
+ assert_not_nil auth.params["approval"]
+ assert_equal [auth.params['transaction_id'], auth.params["approval"], 9900, auth.params["token"]].join(";"), auth.authorization
+
+ assert capture = @gateway.capture(9900, auth.authorization)
+ assert_success capture
+ assert_equal [capture.params['transaction_id'], capture.params["approval"], 9900, auth.params["token"]].join(";"), capture.authorization
+
+ # linked to a specific transaction_id
+ assert refund = @gateway.refund(9900, capture.authorization)
+ assert_success refund
+ assert_not_nil(refund.authorization)
+ assert_not_nil(refund.params["approval"])
+ end
+
def test_refund_backwards_compatible
# no need for csv
card = credit_card('4242424242424242', :verification_value => nil)
diff --git a/test/remote/gateways/remote_jetpay_v2_certification_test.rb b/test/remote/gateways/remote_jetpay_v2_certification_test.rb
new file mode 100644
index 00000000000..4aef90a5de4
--- /dev/null
+++ b/test/remote/gateways/remote_jetpay_v2_certification_test.rb
@@ -0,0 +1,348 @@
+require 'test_helper'
+
+class RemoteJetpayV2CertificationTest < Test::Unit::TestCase
+
+ def setup
+ @gateway = JetpayV2Gateway.new(fixtures(:jetpay_v2))
+
+ @unique_id = ''
+
+ @options = {
+ :device => 'spreedly',
+ :application => 'spreedly',
+ :developer_id => 'GenkID',
+ :billing_address => address(:address1 => '1234 Fifth Street', :address2 => '', :city => 'Beaumont', :state => 'TX', :country => 'US', :zip => '77708'),
+ :shipping_address => address(:address1 => '1234 Fifth Street', :address2 => '', :city => 'Beaumont', :state => 'TX', :country => 'US', :zip => '77708'),
+ :email => 'test@test.com',
+ :ip => '127.0.0.1'
+ }
+ end
+
+ def teardown
+ puts "\n#{@options[:order_id]}: #{@unique_id}"
+ end
+
+ def test_certification_cnp1_authorize_mastercard
+ @options[:order_id] = "CNP1"
+ amount = 1000
+ master = credit_card('5111111111111118', :month => 12, :year => 2017, :brand => 'master', :verification_value => '121')
+ assert response = @gateway.authorize(amount, master, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_cnp2_authorize_visa
+ @options[:order_id] = "CNP2"
+ amount = 1105
+ visa = credit_card('4111111111111111', :month => 12, :year => 2017, :brand => 'visa', :verification_value => '121')
+ assert response = @gateway.authorize(amount, visa, @options)
+ assert_failure response
+ assert_equal "Do not honor.", response.message
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_cnp3_cnp4_authorize_and_capture_amex
+ @options[:order_id] = "CNP3"
+ amount = 1200
+ amex = credit_card('378282246310005', :month => 12, :year => 2017, :brand => 'american_express', :verification_value => '1221')
+ assert response = @gateway.authorize(amount, amex, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ @unique_id = response.params['unique_id']
+ puts "\n#{@options[:order_id]}: #{@unique_id}"
+
+ @options[:order_id] = "CNP4"
+ assert response = @gateway.capture(amount, response.authorization, @options)
+ assert_success response
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_cnp5_purchase_discover
+ @options[:order_id] = "CNP5"
+ amount = 1300
+ discover = credit_card('6011111111111117', :month => 12, :year => 2017, :brand => 'discover', :verification_value => '121')
+ assert response = @gateway.purchase(amount, discover, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_cnp6_purchase_visa
+ @options[:order_id] = "CNP6"
+ amount = 1405
+ visa = credit_card('4111111111111111', :month => 12, :year => 2017, :brand => 'visa', :verification_value => '120')
+ assert response = @gateway.purchase(amount, visa, @options)
+ assert_failure response
+ assert_equal "Do not honor.", response.message
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_cnp7_authorize_mastercard
+ @options[:order_id] = "CNP7"
+ amount = 1500
+ master = credit_card('5111111111111118', :month => 12, :year => 2017, :brand => 'master', :verification_value => '120')
+ assert response = @gateway.authorize(amount, master, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_cnp8_authorize_visa
+ @options[:order_id] = "CNP8"
+ amount = 1605
+ visa = credit_card('4111111111111111', :month => 12, :year => 2017, :brand => 'visa', :verification_value => '120')
+ assert response = @gateway.authorize(amount, visa, @options)
+ assert_failure response
+ assert_equal "Do not honor.", response.message
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_cnp9_cnp10_authorize_and_capture_amex
+ @options[:order_id] = "CNP9"
+ amount = 1700
+ amex = credit_card('378282246310005', :month => 12, :year => 2017, :brand => 'american_express', :verification_value => '1220')
+ assert response = @gateway.authorize(amount, amex, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ @unique_id = response.params['unique_id']
+ puts "\n#{@options[:order_id]}: #{@unique_id}"
+
+ @options[:order_id] = "CNP10"
+ assert response = @gateway.capture(amount, response.authorization, @options)
+ assert_success response
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_cnp11_purchase_discover
+ @options[:order_id] = "CNP11"
+ amount = 1800
+ discover = credit_card('6011111111111117', :month => 12, :year => 2017, :brand => 'discover', :verification_value => '120')
+ assert response = @gateway.purchase(amount, discover, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_rec01_recurring_mastercard
+ @options[:order_id] = "REC01"
+ @options[:origin] = "RECURRING"
+ @options[:billing_address] = nil
+ @options[:shipping_address] = nil
+ amount = 2000
+ master = credit_card('5111111111111118', :month => 12, :year => 2017, :brand => 'master', :verification_value => '120')
+ assert response = @gateway.purchase(amount, master, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_rec02_recurring_visa
+ @options[:order_id] = "REC02"
+ @options[:origin] = "RECURRING"
+ amount = 2100
+ visa = credit_card('4111111111111111', :month => 12, :year => 2017, :brand => 'visa', :verification_value => '')
+ assert response = @gateway.purchase(amount, visa, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_rec03_recurring_amex
+ @options[:order_id] = "REC03"
+ @options[:origin] = "RECURRING"
+ amount = 2200
+ amex = credit_card('378282246310005', :month => 12, :year => 2017, :brand => 'american_express', :verification_value => '1221')
+ assert response = @gateway.purchase(amount, amex, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_corp07_corp08_authorize_and_capture_discover
+ @options[:order_id] = "CORP07"
+ amount = 2500
+ discover = credit_card('6011111111111117', :month => 12, :year => 2018, :brand => 'discover', :verification_value => '120')
+ assert response = @gateway.authorize(amount, discover, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ @unique_id = response.params['unique_id']
+ puts "\n#{@options[:order_id]}: #{@unique_id}"
+
+ @options[:order_id] = "CORP08"
+ assert response = @gateway.capture(amount, response.authorization, @options.merge(:tax_amount => "200"))
+ assert_success response
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_corp09_corp10_authorize_and_capture_visa
+ @options[:order_id] = "CORP09"
+ amount = 5000
+ visa = credit_card('4111111111111111', :month => 12, :year => 2018, :brand => 'visa', :verification_value => '120')
+ assert response = @gateway.authorize(amount, visa, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ @unique_id = response.params['unique_id']
+ puts "\n#{@options[:order_id]}: #{@unique_id}"
+
+ @options[:order_id] = "CORP10"
+ assert response = @gateway.capture(amount, response.authorization, @options.merge(:tax_amount => "0", :tax_exempt => "true"))
+ assert_success response
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_corp11_corp12_authorize_and_capture_mastercard
+ @options[:order_id] = "CORP11"
+ amount = 7500
+ master = credit_card('5111111111111118', :month => 12, :year => 2018, :brand => 'master', :verification_value => '120')
+ assert response = @gateway.authorize(amount, master, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ @unique_id = response.params['unique_id']
+ puts "\n#{@options[:order_id]}: #{@unique_id}"
+
+ @options[:order_id] = "CORP12"
+ assert response = @gateway.capture(amount, response.authorization, @options.merge(:tax_amount => "0", :tax_exempt => "false", :purchase_order => '456456'))
+ assert_success response
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_cred02_credit_visa
+ @options[:order_id] = "CRED02"
+ amount = 100
+ visa = credit_card('4111111111111111', :month => 12, :year => 2017, :brand => 'visa', :verification_value => '120')
+ assert response = @gateway.credit(amount, visa, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_cred03_credit_amex
+ @options[:order_id] = "CRED03"
+ amount = 200
+ amex = credit_card('378282246310005', :month => 12, :year => 2017, :brand => 'american_express', :verification_value => '1220')
+ assert response = @gateway.credit(amount, amex, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_void03_void04_purchase_void_visa
+ @options[:order_id] = "VOID03"
+ amount = 300
+ visa = credit_card('4111111111111111', :month => 12, :year => 2017, :brand => 'visa', :verification_value => '120')
+ assert response = @gateway.purchase(amount, visa, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ @unique_id = response.params['unique_id']
+ puts "\n#{@options[:order_id]}: #{@unique_id}"
+
+ @options[:order_id] = "VOID04"
+ transaction_id, approval, amount, token = response.authorization.split(";")
+ amount = 500
+ authorization = [transaction_id, approval, amount, token].join(";")
+ assert response = @gateway.void(authorization, @options)
+ assert_failure response
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_void07_void08_void09_authorize_capture_void_discover
+ @options[:order_id] = "VOID07"
+ amount = 400
+ discover = credit_card('6011111111111117', :month => 12, :year => 2017, :brand => 'discover', :verification_value => '120')
+ assert response = @gateway.authorize(amount, discover, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ @unique_id = response.params['unique_id']
+ puts "\n#{@options[:order_id]}: #{@unique_id}"
+
+ @options[:order_id] = "VOID08"
+ amount = 600
+ assert response = @gateway.capture(amount, response.authorization, @options)
+ assert_success response
+ @unique_id = response.params['unique_id']
+ puts "\n#{@options[:order_id]}: #{@unique_id}"
+
+ @options[:order_id] = "VOID09"
+ assert response = @gateway.void(response.authorization, @options)
+ assert_success response
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_void12_void13_credit_void_visa
+ @options[:order_id] = "VOID12"
+ amount = 800
+ visa = credit_card('4111111111111111', :month => 12, :year => 2017, :brand => 'visa', :verification_value => '120')
+ assert response = @gateway.credit(amount, visa, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ @unique_id = response.params['unique_id']
+ puts "\n#{@options[:order_id]}: #{@unique_id}"
+
+ @options[:order_id] = "VOID13"
+ assert response = @gateway.void(response.authorization, @options)
+ assert_success response
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_tok15_tokenize_mastercard
+ @options[:order_id] = "TOK15"
+ master = credit_card('5111111111111118', :month => 12, :year => 2017, :brand => 'master', :verification_value => '101')
+ assert response = @gateway.store(master, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ assert_equal "TOKENIZED", response.params["response_text"]
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_tok16_authorize_with_token_request_visa
+ @options[:order_id] = "TOK16"
+ amount = 3100
+ visa = credit_card('4111111111111111', :month => 12, :year => 2017, :brand => 'visa', :verification_value => '101')
+ assert response = @gateway.authorize(amount, visa, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ transaction_id, approval, amount, token = response.authorization.split(";")
+ assert_equal token, response.params["token"]
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_tok17_purchase_with_token_request_amex
+ @options[:order_id] = "TOK17"
+ amount = 3200
+ amex = credit_card('378282246310005', :month => 12, :year => 2017, :brand => 'american_express', :verification_value => '1001')
+ assert response = @gateway.purchase(amount, amex, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ transaction_id, approval, amount, token = response.authorization.split(";")
+ assert_equal token, response.params["token"]
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_tok18_authorize_using_token_mastercard
+ master = credit_card('5111111111111118', :month => 12, :year => 2017, :brand => 'master', :verification_value => '101')
+ assert response = @gateway.store(master, @options)
+ assert_success response
+
+ @options[:order_id] = "TOK18"
+ amount = 3300
+ assert response = @gateway.authorize(amount, response.authorization, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ @unique_id = response.params['unique_id']
+ end
+
+ def test_certification_tok19_purchase_using_token_visa
+ visa = credit_card('4111111111111111', :month => 12, :year => 2017, :brand => 'visa', :verification_value => '101')
+ assert response = @gateway.store(visa, @options)
+ assert_success response
+
+ @options[:order_id] = "TOK19"
+ amount = 3400
+ assert response = @gateway.purchase(amount, response.authorization, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ @unique_id = response.params['unique_id']
+ end
+
+end
diff --git a/test/remote/gateways/remote_jetpay_v2_test.rb b/test/remote/gateways/remote_jetpay_v2_test.rb
new file mode 100644
index 00000000000..9e35bd228f6
--- /dev/null
+++ b/test/remote/gateways/remote_jetpay_v2_test.rb
@@ -0,0 +1,206 @@
+require 'test_helper'
+
+class RemoteJetpayV2Test < Test::Unit::TestCase
+
+ def setup
+ @gateway = JetpayV2Gateway.new(fixtures(:jetpay_v2))
+
+ @credit_card = credit_card('4000300020001000')
+
+ @amount_approved = 9900
+ @amount_declined = 5205
+
+ @options = {
+ :device => 'spreedly',
+ :application => 'spreedly',
+ :developer_id => 'GenkID',
+ :billing_address => address(:city => 'Durham', :state => 'NC', :country => 'US', :zip => '27701'),
+ :shipping_address => address(:city => 'Durham', :state => 'NC', :country => 'US', :zip => '27701'),
+ :email => 'test@test.com',
+ :ip => '127.0.0.1',
+ :order_id => '12345'
+ }
+ end
+
+ def test_successful_purchase
+ assert response = @gateway.purchase(@amount_approved, @credit_card, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ assert_not_nil response.authorization
+ assert_not_nil response.params["approval"]
+ end
+
+ def test_failed_purchase
+ assert response = @gateway.purchase(@amount_declined, @credit_card, @options)
+ assert_failure response
+ assert_equal "Do not honor.", response.message
+ end
+
+ def test_successful_purchase_with_minimal_options
+ assert response = @gateway.purchase(@amount_approved, @credit_card, {:device => 'spreedly', :application => 'spreedly'})
+ assert_success response
+ assert_equal "APPROVED", response.message
+ assert_not_nil response.authorization
+ assert_not_nil response.params["approval"]
+ end
+
+ def test_successful_purchase_with_additional_options
+ options = @options.merge(
+ ud_field_1: "Value1",
+ ud_field_2: "Value2",
+ ud_field_3: "Value3"
+ )
+ assert response = @gateway.purchase(@amount_approved, @credit_card, options)
+ assert_success response
+ end
+
+ def test_successful_authorize_and_capture
+ assert auth = @gateway.authorize(@amount_approved, @credit_card, @options)
+ assert_success auth
+ assert_equal 'APPROVED', auth.message
+ assert_not_nil auth.authorization
+ assert_not_nil auth.params["approval"]
+
+ assert capture = @gateway.capture(@amount_approved, auth.authorization, @options)
+ assert_success capture
+ end
+
+ def test_successful_authorize_and_capture_with_tax
+ assert auth = @gateway.authorize(@amount_approved, @credit_card, @options)
+ assert_success auth
+ assert_equal 'APPROVED', auth.message
+ assert_not_nil auth.authorization
+ assert_not_nil auth.params["approval"]
+
+ assert capture = @gateway.capture(@amount_approved, auth.authorization, @options.merge(:tax_amount => '990', :purchase_order => 'ABC12345'))
+ assert_success capture
+ end
+
+ def test_successful_partial_capture
+ assert auth = @gateway.authorize(9900, @credit_card, @options)
+ assert_success auth
+ assert_equal 'APPROVED', auth.message
+ assert_not_nil auth.authorization
+ assert_not_nil auth.params["approval"]
+
+ assert capture = @gateway.capture(4400, auth.authorization, @options)
+ assert_success capture
+ end
+
+ def test_failed_capture
+ assert response = @gateway.capture(@amount_approved, '7605f7c5d6e8f74deb', @options)
+ assert_failure response
+ assert_equal 'Transaction Not Found.', response.message
+ end
+
+ def test_successful_void
+ assert auth = @gateway.authorize(@amount_approved, @credit_card, @options)
+ assert_success auth
+ assert_equal 'APPROVED', auth.message
+ assert_not_nil auth.authorization
+ assert_not_nil auth.params["approval"]
+
+
+ assert void = @gateway.void(auth.authorization, @options)
+ assert_success void
+ end
+
+ def test_failed_void
+ assert void = @gateway.void('bogus', @options)
+ assert_failure void
+ end
+
+ def test_successful_purchase_refund
+ assert response = @gateway.purchase(@amount_approved, @credit_card, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ assert_not_nil response.authorization
+ assert_not_nil response.params["approval"]
+
+ assert refund = @gateway.refund(@amount_approved, response.authorization, @options)
+ assert_success refund
+ assert_not_nil(refund.authorization)
+ assert_not_nil(response.params["approval"])
+ assert_equal [response.params['transaction_id'], response.params["approval"], @amount_approved, response.params["token"]].join(";"), response.authorization
+ end
+
+ def test_successful_capture_refund
+ assert auth = @gateway.authorize(@amount_approved, @credit_card, @options)
+ assert_success auth
+ assert_equal 'APPROVED', auth.message
+ assert_not_nil auth.authorization
+ assert_not_nil auth.params["approval"]
+ assert_equal [auth.params['transaction_id'], auth.params["approval"], @amount_approved, auth.params["token"]].join(";"), auth.authorization
+
+ assert capture = @gateway.capture(@amount_approved, auth.authorization, @options)
+ assert_success capture
+ assert_equal [capture.params['transaction_id'], capture.params["approval"], @amount_approved, auth.params["token"]].join(";"), capture.authorization
+
+ assert refund = @gateway.refund(@amount_approved, capture.authorization, @options)
+ assert_success refund
+ assert_not_nil(refund.authorization)
+ assert_not_nil(refund.params["approval"])
+ end
+
+ def test_failed_refund
+ assert refund = @gateway.refund(@amount_approved, 'bogus', @options)
+ assert_failure refund
+ end
+
+ def test_successful_credit
+ card = credit_card('4242424242424242', :verification_value => nil)
+
+ assert credit = @gateway.credit(@amount_approved, card, @options)
+ assert_success credit
+ assert_not_nil(credit.authorization)
+ assert_not_nil(credit.params["approval"])
+ end
+
+ def test_failed_credit
+ card = credit_card('2424242424242424', :verification_value => nil)
+
+ assert credit = @gateway.credit(@amount_approved, card, @options)
+ assert_failure credit
+ assert_match %r{Invalid card format}, credit.message
+ end
+
+ def test_successful_verify
+ assert verify = @gateway.verify(@credit_card, @options)
+ assert_success verify
+ end
+
+ def test_failed_verify
+ card = credit_card('2424242424242424', :verification_value => nil)
+
+ assert verify = @gateway.verify(card, @options)
+ assert_failure verify
+ assert_match %r{Invalid card format}, verify.message
+ end
+
+ def test_invalid_login
+ gateway = JetpayV2Gateway.new(:login => 'bogus')
+ assert response = gateway.purchase(@amount_approved, @credit_card, @options)
+ assert_failure response
+
+ assert_equal 'Bad Terminal ID.', response.message
+ end
+
+ def test_missing_login
+ gateway = JetpayV2Gateway.new(:login => '')
+ assert response = gateway.purchase(@amount_approved, @credit_card, @options)
+ assert_failure response
+
+ assert_equal 'No response returned (missing credentials?).', response.message
+ end
+
+ def test_transcript_scrubbing
+ @credit_card.verification_value = "421"
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount_approved, @credit_card, @options)
+ end
+ clean_transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, clean_transcript)
+ assert_scrubbed(@credit_card.verification_value.to_s, clean_transcript)
+ end
+end
diff --git a/test/remote/gateways/remote_kushki_test.rb b/test/remote/gateways/remote_kushki_test.rb
new file mode 100644
index 00000000000..1fae5d77b8c
--- /dev/null
+++ b/test/remote/gateways/remote_kushki_test.rb
@@ -0,0 +1,107 @@
+require 'test_helper'
+
+class RemoteKushkiTest < Test::Unit::TestCase
+ def setup
+ @gateway = KushkiGateway.new(fixtures(:kushki))
+ @amount = 100
+ @credit_card = credit_card('4000100011112224', verification_value: "777")
+ @declined_card = credit_card('4000300011112220')
+ end
+
+ def test_successful_purchase
+ response = @gateway.purchase(@amount, @credit_card)
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ assert_match %r(^\d+$), response.authorization
+ end
+
+ def test_successful_purchase_with_options
+ options = {
+ currency: "USD",
+ amount: {
+ subtotal_iva_0: "4.95",
+ subtotal_iva: "10",
+ iva: "1.54",
+ ice: "3.50"
+ }
+ }
+
+ amount = 100 * (
+ options[:amount][:subtotal_iva_0].to_f +
+ options[:amount][:subtotal_iva].to_f +
+ options[:amount][:iva].to_f +
+ options[:amount][:ice].to_f
+ )
+
+ response = @gateway.purchase(amount, @credit_card, options)
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ assert_match %r(^\d+$), response.authorization
+ end
+
+ def test_failed_purchase
+ options = {
+ amount: {
+ subtotal_iva: "200"
+ }
+ }
+
+ response = @gateway.purchase(@amount, @declined_card, options)
+ assert_failure response
+ assert_equal 'Monto de la transacción es diferente al monto de la venta inicial', response.message
+ end
+
+ def test_successful_refund
+ purchase = @gateway.purchase(@amount, @credit_card)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount, purchase.authorization)
+ assert_success refund
+ assert_equal 'Succeeded', refund.message
+ end
+
+ def test_failed_refund
+ purchase = @gateway.purchase(@amount, @credit_card)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount, nil)
+ assert_failure refund
+ assert_equal 'Missing Authentication Token', refund.message
+ end
+
+ def test_successful_void
+ purchase = @gateway.purchase(@amount, @credit_card)
+ assert_success purchase
+
+ assert void = @gateway.void(purchase.authorization)
+ assert_success void
+ assert_equal 'Succeeded', void.message
+ end
+
+ def test_failed_void
+ response = @gateway.void("000")
+ assert_failure response
+ assert_equal 'El monto de la transacción es requerido', response.message
+ end
+
+ def test_invalid_login
+ gateway = KushkiGateway.new(public_merchant_id: '', private_merchant_id: '')
+
+ response = gateway.purchase(@amount, @credit_card)
+ assert_failure response
+ assert_match %r{ID de comercio no válido}, response.message
+ end
+
+ def test_transcript_scrubbing
+ assert @gateway.supports_scrubbing?
+
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:private_merchant_id], transcript)
+ end
+end
diff --git a/test/remote/gateways/remote_linkpoint_test.rb b/test/remote/gateways/remote_linkpoint_test.rb
index 6c3d4d8c51b..8ad9a942e6f 100644
--- a/test/remote/gateways/remote_linkpoint_test.rb
+++ b/test/remote/gateways/remote_linkpoint_test.rb
@@ -119,6 +119,12 @@ def test_declined_purchase_with_invalid_credit_card
assert_equal "DECLINED", response.params["approved"]
end
+ def test_cleans_whitespace_from_pem
+ @gateway = LinkpointGateway.new(fixtures(:linkpoint).merge(pem: ' ' + fixtures(:linkpoint)[:pem]))
+ assert response = @gateway.authorize(1000, @credit_card, @options)
+ assert_success response
+ end
+
def test_transcript_scrubbing
transcript = capture_transcript(@gateway) do
@gateway.purchase(@amount, @credit_card, @options)
diff --git a/test/remote/gateways/remote_litle_certification_test.rb b/test/remote/gateways/remote_litle_certification_test.rb
index 1c55f9b3182..23bd7fafd13 100644
--- a/test/remote/gateways/remote_litle_certification_test.rb
+++ b/test/remote/gateways/remote_litle_certification_test.rb
@@ -3,14 +3,15 @@
class RemoteLitleCertification < Test::Unit::TestCase
def setup
Base.mode = :test
- @gateway = LitleGateway.new(fixtures(:litle).merge(:url => "https://cert.litle.com/vap/communicator/online"))
+ @gateway = LitleGateway.new(fixtures(:litle))
+ @gateway.test_url = "https://payments.vantivprelive.com/vap/communicator/online"
end
def test1
credit_card = CreditCard.new(
:number => '4457010000000009',
:month => '01',
- :year => '2014',
+ :year => '2021',
:verification_value => '349',
:brand => 'visa'
)
@@ -18,7 +19,7 @@ def test1
options = {
:order_id => '1',
:billing_address => {
- :name => 'John Smith',
+ :name => 'John & Mary Smith',
:address1 => '1 Main St.',
:city => 'Burlington',
:state => 'MA',
@@ -27,24 +28,24 @@ def test1
}
}
- auth_assertions(10010, credit_card, options, :avs => "X", :cvv => "M")
+ auth_assertions(10100, credit_card, options, :avs => "X", :cvv => "M")
- # 1: authorize avs
authorize_avs_assertions(credit_card, options, :avs => "X", :cvv => "M")
- sale_assertions(10010, credit_card, options, :avs => "X", :cvv => "M")
+ sale_assertions(10100, credit_card, options, :avs => "X", :cvv => "M")
end
def test2
credit_card = CreditCard.new(:number => '5112010000000003', :month => '02',
- :year => '2014', :brand => 'master',
- :verification_value => '261')
+ :year => '2021', :brand => 'master',
+ :verification_value => '261',
+ :name => 'Mike J. Hammer')
options = {
:order_id => '2',
:billing_address => {
- :name => 'Mike J. Hammer',
:address1 => '2 Main St.',
+ :address2 => 'Apt. 222',
:city => 'Riverside',
:state => 'RI',
:zip => '02915',
@@ -52,19 +53,18 @@ def test2
}
}
- auth_assertions(20020, credit_card, options, :avs => "Z", :cvv => "M")
+ auth_assertions(10100, credit_card, options, :avs => "Z", :cvv => "M")
- # 2: authorize avs
authorize_avs_assertions(credit_card, options, :avs => "Z", :cvv => "M")
- sale_assertions(20020, credit_card, options, :avs => "Z", :cvv => "M")
+ sale_assertions(10100, credit_card, options, :avs => "Z", :cvv => "M")
end
def test3
credit_card = CreditCard.new(
:number => '6011010000000003',
:month => '03',
- :year => '2014',
+ :year => '2021',
:verification_value => '758',
:brand => 'discover'
)
@@ -80,19 +80,18 @@ def test3
:country => 'US'
}
}
- auth_assertions(30030, credit_card, options, :avs => "Z", :cvv => "M")
+ auth_assertions(10100, credit_card, options, :avs => "Z", :cvv => "M")
- # 3: authorize avs
authorize_avs_assertions(credit_card, options, :avs => "Z", :cvv => "M")
- sale_assertions(30030, credit_card, options, :avs => "Z", :cvv => "M")
+ sale_assertions(10100, credit_card, options, :avs => "Z", :cvv => "M")
end
def test4
credit_card = CreditCard.new(
:number => '375001000000005',
:month => '04',
- :year => '2014',
+ :year => '2021',
:brand => 'american_express'
)
@@ -108,17 +107,37 @@ def test4
}
}
- auth_assertions(40040, credit_card, options, :avs => "A", :cvv => nil)
+ auth_assertions(10100, credit_card, options, :avs => "A", :cvv => nil)
- # 4: authorize avs
authorize_avs_assertions(credit_card, options, :avs => "A")
- sale_assertions(40040, credit_card, options, :avs => "A", :cvv => nil)
+ sale_assertions(10100, credit_card, options, :avs => "A", :cvv => nil)
+ end
+
+ def test5
+ credit_card = ActiveMerchant::Billing::NetworkTokenizationCreditCard.new(
+ :number => '4100200300011001',
+ :month => '05',
+ :year => '2021',
+ :verification_value => '463',
+ :brand => 'visa',
+ :payment_cryptogram => 'BwABBJQ1AgAAAAAgJDUCAAAAAAA='
+ )
+
+ options = {
+ :order_id => '5'
+ }
+
+ auth_assertions(10100, credit_card, options, :avs => "U", :cvv => "M")
+
+ authorize_avs_assertions(credit_card, options, :avs => "U", :cvv => "M")
+
+ sale_assertions(10100, credit_card, options, :avs => "U", :cvv => "M")
end
def test6
credit_card = CreditCard.new(:number => '4457010100000008', :month => '06',
- :year => '2014', :brand => 'visa',
+ :year => '2021', :brand => 'visa',
:verification_value => '992')
options = {
@@ -134,30 +153,35 @@ def test6
}
# 6: authorize
- assert response = @gateway.authorize(60060, credit_card, options)
+ assert response = @gateway.authorize(10100, credit_card, options)
assert !response.success?
- assert_equal '110', response.params['litleOnlineResponse']['authorizationResponse']['response']
+ assert_equal '110', response.params['response']
assert_equal 'Insufficient Funds', response.message
assert_equal "I", response.avs_result["code"]
assert_equal "P", response.cvv_result["code"]
+ puts "Test #{options[:order_id]} Authorize: #{txn_id(response)}"
# 6. sale
- assert response = @gateway.purchase(60060, credit_card, options)
+ assert response = @gateway.purchase(10100, credit_card, options)
assert !response.success?
- assert_equal '110', response.params['litleOnlineResponse']['saleResponse']['response']
+ assert_equal '110', response.params['response']
assert_equal 'Insufficient Funds', response.message
assert_equal "I", response.avs_result["code"]
assert_equal "P", response.cvv_result["code"]
+ puts "Test #{options[:order_id]} Sale: #{txn_id(response)}"
+
# 6A. void
assert response = @gateway.void(response.authorization, {:order_id => '6A'})
- assert_equal '360', response.params['litleOnlineResponse']['voidResponse']['response']
- assert_equal 'No transaction found with specified litleTxnId', response.message
+ assert_equal '360', response.params['response']
+ assert_equal 'No transaction found with specified transaction Id', response.message
+ puts "Test #{options[:order_id]}A: #{txn_id(response)}"
+
end
def test7
credit_card = CreditCard.new(:number => '5112010100000002', :month => '07',
- :year => '2014', :brand => 'master',
+ :year => '2021', :brand => 'master',
:verification_value => '251')
options = {
@@ -173,28 +197,30 @@ def test7
}
# 7: authorize
- assert response = @gateway.authorize(70070, credit_card, options)
+ assert response = @gateway.authorize(10100, credit_card, options)
assert !response.success?
- assert_equal '301', response.params['litleOnlineResponse']['authorizationResponse']['response']
+ assert_equal '301', response.params['response']
assert_equal 'Invalid Account Number', response.message
assert_equal "I", response.avs_result["code"]
assert_equal "N", response.cvv_result["code"]
+ puts "Test #{options[:order_id]} Authorize: #{txn_id(response)}"
# 7: authorize avs
authorize_avs_assertions(credit_card, options, :avs => "I", :cvv => "N", :message => "Invalid Account Number", :success => false)
# 7. sale
- assert response = @gateway.purchase(70070, credit_card, options)
+ assert response = @gateway.purchase(10100, credit_card, options)
assert !response.success?
- assert_equal '301', response.params['litleOnlineResponse']['saleResponse']['response']
+ assert_equal '301', response.params['response']
assert_equal 'Invalid Account Number', response.message
assert_equal "I", response.avs_result["code"]
assert_equal "N", response.cvv_result["code"]
+ puts "Test #{options[:order_id]} Sale: #{txn_id(response)}"
end
def test8
credit_card = CreditCard.new(:number => '6011010100000002', :month => '08',
- :year => '2014', :brand => 'discover',
+ :year => '2021', :brand => 'discover',
:verification_value => '184')
options = {
@@ -210,12 +236,13 @@ def test8
}
# 8: authorize
- assert response = @gateway.authorize(80080, credit_card, options)
+ assert response = @gateway.authorize(10100, credit_card, options)
assert !response.success?
- assert_equal '123', response.params['litleOnlineResponse']['authorizationResponse']['response']
+ assert_equal '123', response.params['response']
assert_equal 'Call Discover', response.message
assert_equal "I", response.avs_result["code"]
assert_equal "P", response.cvv_result["code"]
+ puts "Test #{options[:order_id]} Authorize: #{txn_id(response)}"
# 8: authorize avs
authorize_avs_assertions(credit_card, options, :avs => "I", :cvv => "P", :message => "Call Discover", :success => false)
@@ -223,15 +250,16 @@ def test8
# 8: sale
assert response = @gateway.purchase(80080, credit_card, options)
assert !response.success?
- assert_equal '123', response.params['litleOnlineResponse']['saleResponse']['response']
+ assert_equal '123', response.params['response']
assert_equal 'Call Discover', response.message
assert_equal "I", response.avs_result["code"]
assert_equal "P", response.cvv_result["code"]
+ puts "Test #{options[:order_id]} Sale: #{txn_id(response)}"
end
def test9
credit_card = CreditCard.new(:number => '375001010000003', :month => '09',
- :year => '2014', :brand => 'american_express',
+ :year => '2021', :brand => 'american_express',
:verification_value => '0421')
options = {
@@ -247,92 +275,597 @@ def test9
}
# 9: authorize
- assert response = @gateway.authorize(90090, credit_card, options)
-
+ assert response = @gateway.authorize(10100, credit_card, options)
assert !response.success?
- assert_equal '303', response.params['litleOnlineResponse']['authorizationResponse']['response']
+ assert_equal '303', response.params['response']
assert_equal 'Pick Up Card', response.message
assert_equal "I", response.avs_result["code"]
+ puts "Test #{options[:order_id]} Authorize: #{txn_id(response)}"
# 9: authorize avs
authorize_avs_assertions(credit_card, options, :avs => "I", :message => "Pick Up Card", :success => false)
# 9: sale
- assert response = @gateway.purchase(90090, credit_card, options)
+ assert response = @gateway.purchase(10100, credit_card, options)
assert !response.success?
- assert_equal '303', response.params['litleOnlineResponse']['saleResponse']['response']
+ assert_equal '303', response.params['response']
assert_equal 'Pick Up Card', response.message
assert_equal "I", response.avs_result["code"]
+ puts "Test #{options[:order_id]} Sale: #{txn_id(response)}"
end
# Authorization Reversal Tests
+ def test32
+ credit_card = CreditCard.new(:number => '4457010000000009', :month => '01',
+ :year => '2021', :brand => 'visa',
+ :verification_value => '349')
+
+ options = {
+ :order_id => '32',
+ :billing_address => {
+ :name => 'John Smith',
+ :address1 => '1 Main St.',
+ :city => 'Burlington',
+ :state => 'MA',
+ :zip => '01803-3747',
+ :country => 'US'
+ }
+ }
+
+ assert auth_response = @gateway.authorize(10010, credit_card, options)
+ assert_success auth_response
+ assert_equal '11111 ', auth_response.params['authCode']
+ puts "Test #{options[:order_id]}: #{txn_id(auth_response)}"
+
+ assert capture_response = @gateway.capture(5050, auth_response.authorization, options)
+ assert_success capture_response
+ puts "Test #{options[:order_id]}A: #{txn_id(capture_response)}"
+
+ assert reversal_response = @gateway.void(auth_response.authorization, options)
+ assert_failure reversal_response
+ assert 'Authorization amount has already been depleted', reversal_response.message
+ puts "Test #{options[:order_id]}B: #{txn_id(reversal_response)}"
+ end
+
+ def test33
+ credit_card = CreditCard.new(:number => '5112010000000003', :month => '01',
+ :year => '2021', :brand => 'master',
+ :verification_value => '261')
+
+ options = {
+ :order_id => '33',
+ :billing_address => {
+ :name => 'Mike J. Hammer',
+ :address1 => '2 Main St.',
+ :address2 => 'Apt. 222',
+ :city => 'Riverside',
+ :state => 'RI',
+ :zip => '02915',
+ :country => 'US',
+ :payment_cryptogram => 'BwABBJQ1AgAAAAAgJDUCAAAAAAA='
+ }
+ }
+
+ assert auth_response = @gateway.authorize(20020, credit_card, options)
+ assert_success auth_response
+ assert_equal '22222 ', auth_response.params['authCode']
+ puts "Test #{options[:order_id]}: #{txn_id(auth_response)}"
+
+ assert reversal_response = @gateway.void(auth_response.authorization, options)
+ assert_success reversal_response
+ puts "Test #{options[:order_id]}A: #{txn_id(reversal_response)}"
+ end
+
def test34
- credit_card = CreditCard.new(:number => '6011010000000003', :month => '03',
- :year => '2014', :brand => 'discover',
+ credit_card = CreditCard.new(:number => '6011010000000003', :month => '01',
+ :year => '2021', :brand => 'discover',
:verification_value => '758')
options = {
- :order_id => '34',
- :billing_address => {
- :name => 'Eileen Jones',
- :address1 => '3 Main St.',
- :city => 'Bloomfield',
- :state => 'CT',
- :zip => '06002',
- :country => 'US'
- }
+ :order_id => '34',
+ :billing_address => {
+ :name => 'Eileen Jones',
+ :address1 => '3 Main St.',
+ :city => 'Bloomfield',
+ :state => 'CT',
+ :zip => '06002',
+ :country => 'US'
+ }
}
assert auth_response = @gateway.authorize(30030, credit_card, options)
assert_success auth_response
+ assert '33333 ', auth_response.params['authCode']
+ puts "Test #{options[:order_id]}: #{txn_id(auth_response)}"
- credit_card = CreditCard.new(:number => '4024720001231239', :month => '12',
- :year => '2014', :brand => 'visa')
- assert auth_response2 = @gateway.authorize(18699, credit_card, :order_id => '29')
-
- assert reversal_response = @gateway.void(auth_response2.authorization)
+ assert reversal_response = @gateway.void(auth_response.authorization, options)
assert_success reversal_response
+ puts "Test #{options[:order_id]}A: #{txn_id(reversal_response)}"
end
- def test36
+ def test35
+ credit_card = CreditCard.new(:number => '375001000000005', :month => '01',
+ :year => '2021', :brand => 'american_express')
+
options = {
- :order_id => '36'
+ :order_id => '35',
+ :billing_address => {
+ :name => 'Bob Black',
+ :address1 => '4 Main St.',
+ :city => 'Laurel',
+ :state => 'MD',
+ :zip => '20708',
+ :country => 'US'
+ }
}
- credit_card = CreditCard.new(:number => '375000026600004', :month => '05',
- :year => '2014', :brand => 'american_express',
- :verification_value => '261')
+ assert auth_response = @gateway.authorize(10100, credit_card, options)
+ assert_success auth_response
+ assert_equal '44444 ', auth_response.params['authCode']
+ assert_equal 'A', auth_response.avs_result["code"]
+ puts "Test #{options[:order_id]}: #{txn_id(auth_response)}"
+
+ assert capture_response = @gateway.capture(5050, auth_response.authorization, options)
+ assert_success capture_response
+ puts "Test #{options[:order_id]}A: #{txn_id(capture_response)}"
+
+ assert reversal_response = @gateway.void(auth_response.authorization, options)
+ assert_failure reversal_response
+ assert 'Reversal amount does not match Authorization amount', reversal_response.message
+ puts "Test #{options[:order_id]}B: #{txn_id(reversal_response)}"
+ end
+
+ def test36
+ credit_card = CreditCard.new(:number => '375000026600004', :month => '01',
+ :year => '2021', :brand => 'american_express')
+
+ options = {
+ :order_id => '36'
+ }
assert auth_response = @gateway.authorize(20500, credit_card, options)
assert_success auth_response
+ puts "Test #{options[:order_id]}: #{txn_id(auth_response)}"
+
+ assert reversal_response = @gateway.void(auth_response.authorization, options)
+ assert_failure reversal_response
+ assert 'Reversal amount does not match Authorization amount', reversal_response.message
+ puts "Test #{options[:order_id]}A: #{txn_id(reversal_response)}"
+ end
+
+ # Echeck
+ def test37
+ check = check(
+ name: 'Tom Black',
+ routing_number: '053100300',
+ account_number: '10@BC99999',
+ account_type: 'Checking'
+ )
+ options = {
+ :order_id => '37',
+ :billing_address => {
+ :name => 'Tom Black',
+ :address1 => '8 Main St.',
+ :city => 'Manchester',
+ :state => 'NH',
+ :zip => '03101',
+ :country => 'US',
+ :email => 'test@test.com',
+ :phone => '2233334444'
+ }
+ }
+ assert auth_response = @gateway.authorize(3001, check, options)
+ assert_failure auth_response
+ assert_equal 'Invalid Account Number', auth_response.message
+ assert_equal '301', auth_response.params['response']
+ puts "Test #{options[:order_id]}: #{txn_id(auth_response)}"
+ end
+
+ def test38
+ check = check(
+ name: 'John Smith',
+ routing_number: '011075150',
+ account_number: '1099999999',
+ account_type: 'Checking'
+ )
+ options = {
+ :order_id => '38',
+ :billing_address => {
+ :name => 'John Smith',
+ :address1 => '8 Main St.',
+ :city => 'Manchester',
+ :state => 'NH',
+ :zip => '03101',
+ :country => 'US',
+ :email => 'test@test.com',
+ :phone => '2233334444'
+ }
+ }
+ assert auth_response = @gateway.authorize(3002, check, options)
+ assert_success auth_response
+ assert_equal 'Approved', auth_response.message
+ assert_equal '000', auth_response.params['response']
+ puts "Test #{options[:order_id]}: #{txn_id(auth_response)}"
+ end
+
+ def test39
+ check = check(
+ name: 'Robert Jones',
+ routing_number: '053100300',
+ account_number: '3099999999',
+ account_type: 'Corporate'
+ )
+ options = {
+ :order_id => '39',
+ :billing_address => {
+ :name => 'John Smith',
+ :address1 => '8 Main St.',
+ :city => 'Manchester',
+ :state => 'NH',
+ :zip => '03101',
+ :country => 'US',
+ :company => 'Good Goods Inc',
+ :email => 'test@test.com',
+ :phone => '2233334444'
+ }
+ }
+ assert auth_response = @gateway.authorize(3003, check, options)
+ assert_failure auth_response
+ assert_equal 'Decline - Negative Information on File', auth_response.message
+ assert_equal '950', auth_response.params['response']
+ puts "Test #{options[:order_id]}: #{txn_id(auth_response)}"
+ end
+
+ def test40
+ declined_authorize_check = check(
+ name: 'Peter Green',
+ routing_number: '011075150',
+ account_number: '8099999999',
+ account_type: 'Corporate'
+ )
+ options = {
+ :order_id => '40',
+ :billing_address => {
+ :name => 'Peter Green',
+ :address1 => '8 Main St.',
+ :city => 'Manchester',
+ :state => 'NH',
+ :zip => '03101',
+ :country => 'US',
+ :company => 'Green Co',
+ :email => 'test@test.com',
+ :phone => '2233334444'
+ }
+ }
+ assert auth_response = @gateway.authorize(3004, declined_authorize_check, options)
+ assert_failure auth_response
+ assert_equal 'Absolute Decline', auth_response.message
+ assert_equal '951', auth_response.params['response']
+ puts "Test #{options[:order_id]}: #{txn_id(auth_response)}"
+ end
+
+ def test41
+ check = check(
+ name: 'Mike Hammer',
+ routing_number: '053100300',
+ account_number: '10@BC99999',
+ account_type: 'Checking'
+ )
+ options = {
+ :order_id => '41',
+ :billing_address => {
+ :name => 'Mike Hammer',
+ :address1 => '8 Main St.',
+ :city => 'Manchester',
+ :state => 'NH',
+ :zip => '03101',
+ :country => 'US',
+ :email => 'test@test.com',
+ :phone => '2233334444'
+ }
+ }
+ assert purchase_response = @gateway.purchase(2008, check, options)
+ assert_failure purchase_response
+ assert_equal 'Invalid Account Number', purchase_response.message
+ assert_equal '301', purchase_response.params['response']
+ puts "Test #{options[:order_id]}: #{txn_id(purchase_response)}"
+ end
+
+ def test42
+ check = check(
+ name: 'Tom Black',
+ routing_number: '011075150',
+ account_number: '4099999992',
+ account_type: 'Checking'
+ )
+ options = {
+ :order_id => '42',
+ :billing_address => {
+ :name => 'Tom Black',
+ :address1 => '8 Main St.',
+ :city => 'Manchester',
+ :state => 'NH',
+ :zip => '03101',
+ :country => 'US',
+ :email => 'test@test.com',
+ :phone => '2233334444'
+ }
+ }
+ assert purchase_response = @gateway.purchase(2004, check, options)
+ assert_success purchase_response
+ assert_equal 'Approved', purchase_response.message
+ assert_equal '000', purchase_response.params['response']
+ puts "Test #{options[:order_id]}: #{txn_id(purchase_response)}"
+ end
+
+ def test43
+ check = check(
+ name: 'Peter Green',
+ routing_number: '011075150',
+ account_number: '6099999992',
+ account_type: 'Corporate'
+ )
+ options = {
+ :order_id => '43',
+ :billing_address => {
+ :name => 'Peter Green',
+ :address1 => '8 Main St.',
+ :city => 'Manchester',
+ :state => 'NH',
+ :zip => '03101',
+ :country => 'US',
+ :company => 'Green Co',
+ :email => 'test@test.com',
+ :phone => '2233334444'
+ }
+ }
+ assert purchase_response = @gateway.purchase(2007, check, options)
+ assert_success purchase_response
+ assert_equal 'Approved', purchase_response.message
+ assert_equal '000', purchase_response.params['response']
+ puts "Test #{options[:order_id]}: #{txn_id(purchase_response)}"
+ end
+
+ def test44
+ check = check(
+ name: 'Peter Green',
+ routing_number: '053133052',
+ account_number: '9099999992',
+ account_type: 'Corporate'
+ )
+ options = {
+ :order_id => '44',
+ :billing_address => {
+ :name => 'Peter Green',
+ :address1 => '8 Main St.',
+ :city => 'Manchester',
+ :state => 'NH',
+ :zip => '03101',
+ :country => 'US',
+ :company => 'Green Co',
+ :email => 'test@test.com',
+ :phone => '2233334444'
+ }
+ }
+ assert purchase_response = @gateway.purchase(2009, check, options)
+ assert_failure purchase_response
+ assert_equal 'Invalid Bank Routing Number', purchase_response.message
+ assert_equal '900', purchase_response.params['response']
+ puts "Test #{options[:order_id]}: #{txn_id(purchase_response)}"
+ end
+
+ def test45
+ check = check(
+ name: 'John Smith',
+ routing_number: '053100300',
+ account_number: '10@BC99999',
+ account_type: 'Checking'
+ )
+ options = {
+ :order_id => '45',
+ :billing_address => {
+ :name => 'John Smith',
+ :address1 => '8 Main St.',
+ :city => 'Manchester',
+ :state => 'NH',
+ :zip => '03101',
+ :country => 'US',
+ :email => 'test@test.com',
+ :phone => '2233334444'
+ }
+ }
+ assert refund_response = @gateway.refund(1001, check, options)
+ assert_failure refund_response
+ assert_equal 'Invalid Account Number', refund_response.message
+ assert_equal '301', refund_response.params['response']
+ puts "Test #{options[:order_id]}: #{txn_id(refund_response)}"
+ end
+
+ def test46
+ check = check(
+ name: 'Robert Jones',
+ routing_number: '011075150',
+ account_number: '3099999999',
+ account_type: 'Corporate'
+ )
+ options = {
+ :order_id => '46',
+ :order_source => 'telephone',
+ :billing_address => {
+ :name => 'Robert Jones',
+ :address1 => '8 Main St.',
+ :city => 'Manchester',
+ :state => 'NH',
+ :zip => '03101',
+ :country => 'US',
+ :email => 'test@test.com',
+ :phone => '2233334444',
+ :company => 'Widget Inc'
+ }
+ }
+ assert purchase_response = @gateway.purchase(1003, check, options)
+ sleep(10)
+ assert refund_response = @gateway.refund(1003, purchase_response.authorization, options)
+ assert_success refund_response
+ assert_equal 'Approved', refund_response.message
+ assert_equal '000', refund_response.params['response']
+ puts "Test #{options[:order_id]}: #{txn_id(refund_response)}"
+ end
- assert reversal_response = @gateway.void(auth_response.authorization, amount: 10000)
- assert !reversal_response.success?
- assert_equal '336', reversal_response.params['litleOnlineResponse']['authReversalResponse']['response']
+ def test47
+ check = check(
+ name: 'Peter Green',
+ routing_number: '211370545',
+ account_number: '6099999993',
+ account_type: 'Corporate'
+ )
+ options = {
+ :order_id => '47',
+ :billing_address => {
+ :name => 'Peter Green',
+ :address1 => '8 Main St.',
+ :city => 'Manchester',
+ :state => 'NH',
+ :zip => '03101',
+ :country => 'US',
+ :company => 'Green Co',
+ :email => 'test@test.com',
+ :phone => '2233334444'
+ }
+ }
+ assert purchase_response = @gateway.purchase(1007, check, options)
+ assert refund_response = @gateway.refund(1007, purchase_response.authorization, options)
+ assert_success refund_response
+ assert_equal 'Approved', refund_response.message
+ assert_equal '000', refund_response.params['response']
+ puts "Test #{options[:order_id]}: #{txn_id(refund_response)}"
+ end
+
+ def test48
+ check = check(
+ name: 'Peter Green',
+ routing_number: '011075150',
+ account_number: '6099999992',
+ account_type: 'Corporate'
+ )
+ options = {
+ :order_id => '43',
+ :billing_address => {
+ :name => 'Peter Green',
+ :address1 => '8 Main St.',
+ :city => 'Manchester',
+ :state => 'NH',
+ :zip => '03101',
+ :country => 'US',
+ :company => 'Green Co',
+ :email => 'test@test.com',
+ :phone => '2233334444'
+ }
+ }
+ assert purchase_response = @gateway.purchase(2007, check, options)
+ assert_success purchase_response
+ assert refund_response = @gateway.refund(2007, purchase_response.authorization, options)
+ assert_equal '000', refund_response.params['response']
+ puts "Test 48: #{txn_id(refund_response)}"
+ end
+
+ def test49
+ assert refund_response = @gateway.refund(2007, 2)
+ assert_failure refund_response
+ assert_equal '360', refund_response.params['response']
+ assert_equal 'No transaction found with specified transaction Id', refund_response.message
+ puts "Test 49: #{txn_id(refund_response)}"
+ end
+
+ def test_echeck_void1
+ check = check(
+ name: 'Tom Black',
+ routing_number: '011075150',
+ account_number: '4099999992',
+ account_type: 'Checking'
+ )
+ options = {
+ :order_id => '42',
+ :id => '236222',
+ :billing_address => {
+ :name => 'Tom Black',
+ :address1 => '8 Main St.',
+ :city => 'Manchester',
+ :state => 'NH',
+ :zip => '03101',
+ :country => 'US',
+ :email => 'test@test.com',
+ :phone => '2233334444'
+ }
+ }
+ assert purchase_response = @gateway.purchase(2004, check, options)
+ assert_success purchase_response
+ sleep(10)
+ assert void_response = @gateway.void(purchase_response.authorization)
+ assert_equal '000', void_response.params['response']
+ puts "Test void1: #{txn_id(void_response)}"
+ end
+
+ def test_echeck_void2
+ check = check(
+ name: 'Robert Jones',
+ routing_number: '011075150',
+ account_number: '3099999999',
+ account_type: 'Checking'
+ )
+ options = {
+ :order_id => '46',
+ :id => '232222',
+ :billing_address => {
+ :name => 'Robert Jones',
+ :address1 => '8 Main St.',
+ :city => 'Manchester',
+ :state => 'NH',
+ :zip => '03101',
+ :country => 'US',
+ :email => 'test@test.com',
+ :phone => '2233334444'
+ }
+ }
+ assert purchase_response = @gateway.purchase(1003, check, options)
+ assert_success purchase_response
+ sleep(20)
+ assert void_response = @gateway.void(purchase_response.authorization)
+ assert_equal '000', void_response.params['response']
+ puts "Test void2: #{txn_id(void_response)}"
+ end
+
+ def test_echeck_void3
+ assert void_response = @gateway.void(2)
+ assert_failure void_response
+ assert_equal '360', void_response.params['response']
+ assert_equal 'No transaction found with specified transaction Id', void_response.message
+ puts "Test void3: #{txn_id(void_response)}"
end
# Explicit Token Registration Tests
def test50
credit_card = CreditCard.new(:number => '4457119922390123')
options = {
- :order_id => '50'
+ :order_id => '50'
}
# store
store_response = @gateway.store(credit_card, options)
assert_success store_response
+ assert_equal '445711', store_response.params['bin']
+ assert_equal 'VI', store_response.params['type']
+ assert_equal '0123', store_response.params['litleToken'][-4,4]
+ assert_equal '801', store_response.params['response']
assert_equal 'Account number was successfully registered', store_response.message
- assert_equal '445711', store_response.params['litleOnlineResponse']['registerTokenResponse']['bin']
- assert_equal 'VI', store_response.params['litleOnlineResponse']['registerTokenResponse']['type'] #type is on Object in 1.8.7 - later versions can use .registerTokenResponse.type
- assert_equal '801', store_response.params['litleOnlineResponse']['registerTokenResponse']['response']
- assert_equal '0123', store_response.params['litleOnlineResponse']['registerTokenResponse']['litleToken'][-4,4]
+ puts "Test #{options[:order_id]}: #{txn_id(response)}"
end
def test51
credit_card = CreditCard.new(:number => '4457119999999999')
- options = {
- :order_id => '51'
+ options = {
+ :order_id => '51'
}
# store
@@ -340,14 +873,14 @@ def test51
assert_failure store_response
assert_equal 'Credit card number was invalid', store_response.message
- assert_equal '820', store_response.params['litleOnlineResponse']['registerTokenResponse']['response']
- assert_equal nil, store_response.params['litleOnlineResponse']['registerTokenResponse']['litleToken']
+ assert_equal '820', store_response.params['response']
+ assert_equal nil, store_response.params['litleToken']
end
def test52
credit_card = CreditCard.new(:number => '4457119922390123')
- options = {
- :order_id => '52'
+ options = {
+ :order_id => '52'
}
# store
@@ -355,10 +888,47 @@ def test52
assert_success store_response
assert_equal 'Account number was previously registered', store_response.message
- assert_equal '445711', store_response.params['litleOnlineResponse']['registerTokenResponse']['bin']
- assert_equal 'VI', store_response.params['litleOnlineResponse']['registerTokenResponse']['type'] #type is on Object in 1.8.7 - later versions can use .registerTokenResponse.type
- assert_equal '802', store_response.params['litleOnlineResponse']['registerTokenResponse']['response']
- assert_equal '0123', store_response.params['litleOnlineResponse']['registerTokenResponse']['litleToken'][-4,4]
+ assert_equal '445711', store_response.params['bin']
+ assert_equal 'VI', store_response.params['type']
+ assert_equal '802', store_response.params['response']
+ assert_equal '0123', store_response.params['litleToken'][-4,4]
+ puts "Test #{options[:order_id]}: #{txn_id(store_response)}"
+ end
+
+ def test53
+ check = check(
+ routing_number: '011100012',
+ account_number: '1099999998'
+ )
+ options = {
+ :order_id => '53'
+ }
+
+ store_response = @gateway.store(check, options)
+
+ assert_success store_response
+ assert_equal '998', store_response.params['eCheckAccountSuffix']
+ assert_equal 'EC', store_response.params['type']
+ assert_equal '801', store_response.params['response']
+ assert_equal 'Account number was successfully registered', store_response.message
+ puts "Test #{options[:order_id]}: #{txn_id(store_response)}"
+ end
+
+ def test54
+ check = check(
+ routing_number: '1145_7895',
+ account_number: '1022222102'
+ )
+ options = {
+ :order_id => '54'
+ }
+
+ store_response = @gateway.store(check, options)
+
+ assert_failure store_response
+ assert_equal '900', store_response.params['response']
+ assert_equal 'Invalid Bank Routing Number', store_response.message
+ puts "Test #{options[:order_id]}: #{txn_id(store_response)}"
end
# Implicit Token Registration Tests
@@ -368,23 +938,19 @@ def test55
:year => '2014',
:brand => 'master',
:verification_value => '987')
- options = {
- :order_id => '55'
+ options = {
+ :order_id => '55'
}
# authorize
assert response = @gateway.authorize(15000, credit_card, options)
- #"tokenResponse" => { "litleToken" => "1712000118270196",
- # "tokenResponseCode" => "802",
- # "tokenMessage" => "Account number was previously registered",
- # "type" => "MC",
- # "bin" => "543510" }
assert_success response
assert_equal 'Approved', response.message
- assert_equal '0196', response.params['litleOnlineResponse']['authorizationResponse']['tokenResponse']['litleToken'][-4,4]
- assert %w(801 802).include? response.params['litleOnlineResponse']['authorizationResponse']['tokenResponse']['tokenResponseCode']
- assert_equal 'MC', response.params['litleOnlineResponse']['authorizationResponse']['tokenResponse']['type']
- assert_equal '543510', response.params['litleOnlineResponse']['authorizationResponse']['tokenResponse']['bin']
+ assert_equal '0196', response.params['tokenResponse_litleToken'][-4,4]
+ assert %w(801 802).include? response.params['tokenResponse_tokenResponseCode']
+ assert_equal 'MC', response.params['tokenResponse_type']
+ assert_equal '543510', response.params['tokenResponse_bin']
+ puts "Test #{options[:order_id]}: #{txn_id(response)}"
end
def test56
@@ -393,15 +959,16 @@ def test56
:year => '2014',
:brand => 'master',
:verification_value => '987')
- options = {
- :order_id => '56'
+ options = {
+ :order_id => '56'
}
# authorize
assert response = @gateway.authorize(15000, credit_card, options)
assert_failure response
- assert_equal '301', response.params['litleOnlineResponse']['authorizationResponse']['response']
+ assert_equal '301', response.params['response']
+ puts "Test #{options[:order_id]}: #{txn_id(response)}"
end
def test57_58
@@ -410,8 +977,8 @@ def test57_58
:year => '2014',
:brand => 'master',
:verification_value => '987')
- options = {
- :order_id => '57'
+ options = {
+ :order_id => '57'
}
# authorize card
@@ -419,19 +986,20 @@ def test57_58
assert_success response
assert_equal 'Approved', response.message
- assert_equal '0196', response.params['litleOnlineResponse']['authorizationResponse']['tokenResponse']['litleToken'][-4,4]
- assert %w(801 802).include? response.params['litleOnlineResponse']['authorizationResponse']['tokenResponse']['tokenResponseCode']
- assert_equal 'MC', response.params['litleOnlineResponse']['authorizationResponse']['tokenResponse']['type']
- assert_equal '543510', response.params['litleOnlineResponse']['authorizationResponse']['tokenResponse']['bin']
+ assert_equal '0196', response.params['tokenResponse_litleToken'][-4,4]
+ assert %w(801 802).include? response.params['tokenResponse_tokenResponseCode']
+ assert_equal 'MC', response.params['tokenResponse_type']
+ assert_equal '543510', response.params['tokenResponse_bin']
+ puts "Test #{options[:order_id]}: #{txn_id(response)}"
# authorize token
- token = response.params['litleOnlineResponse']['authorizationResponse']['tokenResponse']['litleToken']
+ token = response.params['tokenResponse_litleToken']
options = {
- :order_id => '58',
- :token => {
- :month => credit_card.month,
- :year => credit_card.year
- }
+ :order_id => '58',
+ :token => {
+ :month => credit_card.month,
+ :year => credit_card.year
+ }
}
# authorize
@@ -439,55 +1007,114 @@ def test57_58
assert_success response
assert_equal 'Approved', response.message
+ puts "Test #{options[:order_id]}: #{txn_id(response)}"
end
def test59
- token = '1712990000040196'
+ token = '1111000100092332'
options = {
- :order_id => '59',
- :token => {
- :month => '11',
- :year => '2014'
- }
+ :order_id => '59',
+ :token => {
+ :month => '11',
+ :year => '2021'
+ }
}
# authorize
assert response = @gateway.authorize(15000, token, options)
assert_failure response
- assert_equal '822', response.params['litleOnlineResponse']['authorizationResponse']['response']
+ assert_equal '822', response.params['response']
assert_equal 'Token was not found', response.message
+ puts "Test #{options[:order_id]}: #{txn_id(response)}"
end
def test60
token = '171299999999999'
options = {
- :order_id => '60',
- :token => {
- :month => '11',
- :year => '2014'
- }
+ :order_id => '60',
+ :token => {
+ :month => '11',
+ :year => '2014'
+ }
}
# authorize
assert response = @gateway.authorize(15000, token, options)
assert_failure response
- assert_equal '823', response.params['litleOnlineResponse']['authorizationResponse']['response']
+ assert_equal '823', response.params['response']
assert_equal 'Token was invalid', response.message
+ puts "Test #{options[:order_id]}: #{txn_id(response)}"
+ end
+
+ def test_apple_pay_purchase
+ options = {
+ :order_id => transaction_id,
+ }
+ decrypted_apple_pay = ActiveMerchant::Billing::NetworkTokenizationCreditCard.new(
+ {
+ month: '01',
+ year: '2021',
+ brand: "visa",
+ number: "4457000300000007",
+ payment_cryptogram: "BwABBJQ1AgAAAAAgJDUCAAAAAAA="
+ })
+
+ assert response = @gateway.purchase(10010, decrypted_apple_pay, options)
+ assert_success response
+ assert_equal 'Approved', response.message
+ end
+
+ def test_android_pay_purchase
+ options = {
+ :order_id => transaction_id,
+ }
+ decrypted_android_pay = ActiveMerchant::Billing::NetworkTokenizationCreditCard.new(
+ {
+ source: :android_pay,
+ month: '01',
+ year: '2021',
+ brand: "visa",
+ number: "4457000300000007",
+ payment_cryptogram: "BwABBJQ1AgAAAAAgJDUCAAAAAAA="
+ })
+
+ assert response = @gateway.purchase(10010, decrypted_android_pay, options)
+ assert_success response
+ assert_equal 'Approved', response.message
+ end
+
+ def test_three_d_secure
+ three_d_secure_assertions('3DS1', '4100200300000004', 'visa', '3dsAuthenticated', '0')
+ three_d_secure_assertions('3DS2', '4100200300000012', 'visa', '3dsAuthenticated', '1')
+ three_d_secure_assertions('3DS3', '4100200300000103', 'visa', '3dsAuthenticated', '2')
+ three_d_secure_assertions('3DS4', '4100200300001002', 'visa', '3dsAuthenticated', 'A')
+ three_d_secure_assertions('3DS5', '4100200300000020', 'visa', '3dsAuthenticated', '3')
+ three_d_secure_assertions('3DS6', '4100200300000038', 'visa', '3dsAuthenticated', '4')
+ three_d_secure_assertions('3DS7', '4100200300000046', 'visa', '3dsAuthenticated', '5')
+ three_d_secure_assertions('3DS8', '4100200300000053', 'visa', '3dsAuthenticated', '6')
+ three_d_secure_assertions('3DS9', '4100200300000061', 'visa', '3dsAuthenticated', '7')
+ three_d_secure_assertions('3DS10', '4100200300000079', 'visa', '3dsAuthenticated', '8')
+ three_d_secure_assertions('3DS11', '4100200300000087', 'visa', '3dsAuthenticated', '9')
+ three_d_secure_assertions('3DS12', '4100200300000095', 'visa', '3dsAuthenticated', 'B')
+ three_d_secure_assertions('3DS13', '4100200300000111', 'visa', '3dsAuthenticated', 'C')
+ three_d_secure_assertions('3DS14', '4100200300000129', 'visa', '3dsAuthenticated', 'D')
+ three_d_secure_assertions('3DS15', '5112010200000001', 'master', '3dsAttempted', nil)
+ three_d_secure_assertions('3DS16', '5112010200000001', 'master', '3dsAttempted', nil)
end
def test_authorize_and_purchase_and_credit_with_token
options = {
- :order_id => transaction_id,
- :billing_address => {
- :name => 'John Smith',
- :address1 => '1 Main St.',
- :city => 'Burlington',
- :state => 'MA',
- :zip => '01803-3747',
- :country => 'US'
- }
+ :order_id => transaction_id,
+ :billing_address => {
+ :name => 'John Smith',
+ :address1 => '1 Main St.',
+ :city => 'Burlington',
+ :state => 'MA',
+ :zip => '01803-3747',
+ :country => 'US'
+ }
}
credit_card = CreditCard.new(:number => '5435101234510196',
@@ -536,63 +1163,128 @@ def test_authorize_and_purchase_and_credit_with_token
private
- def auth_assertions(amount, card, options, assertions)
+ def auth_assertions(amount, card, options, assertions={})
+ # 1: authorize
+ assert response = @gateway.authorize(amount, card, options)
+ assert_success response
+ assert_equal 'Approved', response.message
+ assert_equal assertions[:avs], response.avs_result["code"] if assertions[:avs]
+ assert_equal assertions[:cvv], response.cvv_result["code"] if assertions[:cvv]
+ assert_equal auth_code(options[:order_id]), response.params['authCode']
+ puts "Test #{options[:order_id]} Authorize: #{txn_id(response)}"
+
+ # 1A: capture
+ assert response = @gateway.capture(amount, response.authorization, {:id => transaction_id})
+ assert_equal 'Approved', response.message
+ puts "Test #{options[:order_id]}A: #{txn_id(response)}"
+
+ # 1B: credit
+ assert response = @gateway.credit(amount, response.authorization, {:id => transaction_id})
+ assert_equal 'Approved', response.message
+ puts "Test #{options[:order_id]}B: #{txn_id(response)}"
+
+ # 1C: void
+ assert response = @gateway.void(response.authorization, {:id => transaction_id})
+ assert_equal 'Approved', response.message
+ puts "Test #{options[:order_id]}C: #{txn_id(response)}"
+ end
+
+ def authorize_avs_assertions(credit_card, options, assertions={})
+ assert response = @gateway.authorize(000, credit_card, options)
+ assert_equal assertions.key?(:success) ? assertions[:success] : true, response.success?
+ assert_equal assertions[:message] || 'Approved', response.message
+ assert_equal assertions[:avs], response.avs_result["code"], caller.inspect
+ assert_equal assertions[:cvv], response.cvv_result["code"], caller.inspect if assertions[:cvv]
+ puts "Test #{options[:order_id]} AVS Only: #{txn_id(response)}"
+ end
+
+ def sale_assertions(amount, card, options, assertions={})
+ # 1: sale
+ assert response = @gateway.purchase(amount, card, options)
+ assert_success response
+ assert_equal 'Approved', response.message
+ assert_equal assertions[:avs], response.avs_result["code"] if assertions[:avs]
+ assert_equal assertions[:cvv], response.cvv_result["code"] if assertions[:cvv]
+ assert_equal auth_code(options[:order_id]), response.params['authCode']
+ puts "Test #{options[:order_id]} Sale: #{txn_id(response)}"
+
+
+ # 1B: credit
+ assert response = @gateway.credit(amount, response.authorization, {:id => transaction_id})
+ assert_equal 'Approved', response.message
+ puts "Test #{options[:order_id]}B Sale: #{txn_id(response)}"
+
+ # 1C: void
+ assert response = @gateway.void(response.authorization, {:id => transaction_id})
+ assert_equal 'Approved', response.message
+ puts "Test #{options[:order_id]}C Sale: #{txn_id(response)}"
+ end
+
+ def auth_assertions(amount, card, options, assertions={})
# 1: authorize
assert response = @gateway.authorize(amount, card, options)
assert_success response
assert_equal 'Approved', response.message
- assert_equal assertions[:avs], response.avs_result["code"]
+ assert_equal assertions[:avs], response.avs_result["code"] if assertions[:avs]
assert_equal assertions[:cvv], response.cvv_result["code"] if assertions[:cvv]
- assert_equal options[:order_id], response.params['litleOnlineResponse']['authorizationResponse']['id']
+ assert_equal auth_code(options[:order_id]), response.params['authCode']
# 1A: capture
- id = transaction_id
- assert response = @gateway.capture(amount, response.authorization, {:id => id})
+ assert response = @gateway.capture(amount, response.authorization, {:id => transaction_id})
assert_equal 'Approved', response.message
- assert_equal id, response.params['litleOnlineResponse']['captureResponse']['id']
# 1B: credit
- id = transaction_id
- assert response = @gateway.credit(amount, response.authorization, {:id => id})
+ assert response = @gateway.credit(amount, response.authorization, {:id => transaction_id})
assert_equal 'Approved', response.message
- assert_equal id, response.params['litleOnlineResponse']['creditResponse']['id']
# 1C: void
- id = transaction_id
- assert response = @gateway.void(response.authorization, {:id => id})
+ assert response = @gateway.void(response.authorization, {:id => transaction_id})
assert_equal 'Approved', response.message
- assert_equal id, response.params['litleOnlineResponse']['voidResponse']['id']
end
def authorize_avs_assertions(credit_card, options, assertions={})
- assert response = @gateway.authorize(0, credit_card, options)
+ assert response = @gateway.authorize(000, credit_card, options)
assert_equal assertions.key?(:success) ? assertions[:success] : true, response.success?
assert_equal assertions[:message] || 'Approved', response.message
assert_equal assertions[:avs], response.avs_result["code"], caller.inspect
assert_equal assertions[:cvv], response.cvv_result["code"], caller.inspect if assertions[:cvv]
- assert_equal options[:order_id], response.params['litleOnlineResponse']['authorizationResponse']['id']
end
- def sale_assertions(amount, card, options, assertions)
+ def sale_assertions(amount, card, options, assertions={})
# 1: sale
assert response = @gateway.purchase(amount, card, options)
assert_success response
assert_equal 'Approved', response.message
- assert_equal assertions[:avs], response.avs_result["code"]
+ assert_equal assertions[:avs], response.avs_result["code"] if assertions[:avs]
assert_equal assertions[:cvv], response.cvv_result["code"] if assertions[:cvv]
- assert_equal options[:order_id], response.params['litleOnlineResponse']['saleResponse']['id']
+ # assert_equal auth_code(options[:order_id]), response.params['authCode']
# 1B: credit
- id = transaction_id
- assert response = @gateway.credit(amount, response.authorization, {:id => id})
+ assert response = @gateway.credit(amount, response.authorization, {:id => transaction_id})
assert_equal 'Approved', response.message
- assert_equal id, response.params['litleOnlineResponse']['creditResponse']['id']
# 1C: void
- id = transaction_id
- assert response = @gateway.void(response.authorization, {:id => id})
+ assert response = @gateway.void(response.authorization, {:id => transaction_id})
assert_equal 'Approved', response.message
- assert_equal id, response.params['litleOnlineResponse']['voidResponse']['id']
+ end
+
+ def three_d_secure_assertions(test_id, card, type, source, result)
+ credit_card = CreditCard.new(:number => card, :month => '01',
+ :year => '2021', :brand => type,
+ :verification_value => '261',
+ :name => 'Mike J. Hammer')
+
+ options = {
+ order_id: test_id,
+ order_source: source,
+ cavv: 'BwABBJQ1AgAAAAAgJDUCAAAAAAA=',
+ xid: 'BwABBJQ1AgAAAAAgJDUCAAAAAAA='
+ }
+
+ assert response = @gateway.authorize(10100, credit_card, options)
+ assert_success response
+ assert_equal result, response.params['fraudResult_authenticationResult']
+ puts "Test #{test_id}: #{txn_id(response)}"
end
def transaction_id
@@ -604,4 +1296,12 @@ def transaction_id
# minLength = N/A maxLength = 25
generate_unique_id[0, 24]
end
+
+ def auth_code(order_id)
+ order_id * 5 + ' '
+ end
+
+ def txn_id(response)
+ response.authorization.split(";")[0]
+ end
end
diff --git a/test/remote/gateways/remote_litle_test.rb b/test/remote/gateways/remote_litle_test.rb
index aa3c62ac9c3..014cbdd8afe 100644
--- a/test/remote/gateways/remote_litle_test.rb
+++ b/test/remote/gateways/remote_litle_test.rb
@@ -54,6 +54,31 @@ def setup
number: "44444444400009",
payment_cryptogram: "BwABBJQ1AgAAAAAgJDUCAAAAAAA="
})
+ @decrypted_android_pay = ActiveMerchant::Billing::NetworkTokenizationCreditCard.new(
+ {
+ source: :android_pay,
+ month: '01',
+ year: '2021',
+ brand: "visa",
+ number: "4457000300000007",
+ payment_cryptogram: "BwABBJQ1AgAAAAAgJDUCAAAAAAA="
+ })
+ @check = check(
+ name: 'Tom Black',
+ routing_number: '011075150',
+ account_number: '4099999992',
+ account_type: 'Checking'
+ )
+ @authorize_check = check(
+ name: 'John Smith',
+ routing_number: '011075150',
+ account_number: '1099999999',
+ account_type: 'Checking'
+ )
+ @store_check = check(
+ routing_number: '011100012',
+ account_number: '1099999998'
+ )
end
def test_successful_authorization
@@ -62,6 +87,25 @@ def test_successful_authorization
assert_equal 'Approved', response.message
end
+ def test_successful_authorization_with_merchant_data
+ options = @options.merge(
+ affiliate: 'some-affiliate',
+ campaign: 'super-awesome-campaign',
+ merchant_grouping_id: 'brilliant-group'
+ )
+ assert response = @gateway.authorize(10010, @credit_card1, options)
+ end
+
+ def test_successful_authorization_with_echeck
+ options = @options.merge({
+ order_id: '38',
+ order_source: 'telephone'
+ })
+ assert response = @gateway.authorize(3002, @authorize_check, options)
+ assert_success response
+ assert_equal 'Approved', response.message
+ end
+
def test_avs_and_cvv_result
assert response = @gateway.authorize(10010, @credit_card1, @options)
assert_equal "X", response.avs_result["code"]
@@ -109,12 +153,50 @@ def test_successful_purchase_with_debt_repayment_flag
assert_equal 'Approved', response.message
end
+ def test_successful_purchase_with_3ds_fields
+ options = @options.merge({
+ order_source: '3dsAuthenticated',
+ xid: 'BwABBJQ1AgAAAAAgJDUCAAAAAAA=',
+ cavv: 'BwABBJQ1AgAAAAAgJDUCAAAAAAA='
+ })
+ assert response = @gateway.purchase(10010, @credit_card1, options)
+ assert_success response
+ assert_equal 'Approved', response.message
+ end
+
def test_successful_purchase_with_apple_pay
assert response = @gateway.purchase(10010, @decrypted_apple_pay)
assert_success response
assert_equal 'Approved', response.message
end
+ def test_successful_purchase_with_android_pay
+ assert response = @gateway.purchase(10000, @decrypted_android_pay)
+ assert_success response
+ assert_equal 'Approved', response.message
+ end
+
+ def test_successful_purchase_with_merchant_data
+ options = @options.merge(
+ affiliate: 'some-affiliate',
+ campaign: 'super-awesome-campaign',
+ merchant_grouping_id: 'brilliant-group'
+ )
+ assert response = @gateway.purchase(10010, @credit_card1, options)
+ assert_success response
+ assert_equal 'Approved', response.message
+ end
+
+ def test_successful_purchase_with_echeck
+ options = @options.merge({
+ order_id: '42',
+ order_source: 'telephone'
+ })
+ assert response = @gateway.purchase(2004, @check, options)
+ assert_success response
+ assert_equal 'Approved', response.message
+ end
+
def test_unsuccessful_purchase
assert response = @gateway.purchase(60060, @credit_card2, {
:order_id=>'6',
@@ -150,6 +232,18 @@ def test_authorization_capture_refund_void
assert_equal 'Approved', void.message
end
+ def test_void_with_echeck
+ options = @options.merge({
+ order_id: '42',
+ order_source: 'telephone'
+ })
+ assert sale = @gateway.purchase(2004, @check, options)
+
+ assert void = @gateway.void(sale.authorization)
+ assert_success void
+ assert_equal 'Approved', void.message
+ end
+
def test_void_authorization
assert auth = @gateway.authorize(10010, @credit_card1, @options)
@@ -172,6 +266,18 @@ def test_partial_refund
assert_equal 'Approved', refund.message
end
+ def test_partial_refund_with_echeck
+ options = @options.merge({
+ order_id: '82',
+ order_source: 'telephone'
+ })
+ assert purchase = @gateway.purchase(2004, @check, options)
+
+ assert refund = @gateway.refund(444, purchase.authorization)
+ assert_success refund
+ assert_equal 'Approved', refund.message
+ end
+
def test_partial_capture
assert auth = @gateway.authorize(10010, @credit_card1, @options)
assert_success auth
@@ -203,19 +309,19 @@ def test_nil_amount_capture
end
def test_capture_unsuccessful
- assert capture_response = @gateway.capture(10010, 123456789012345360)
+ assert capture_response = @gateway.capture(10010, '123456789012345360')
assert_failure capture_response
assert_equal 'No transaction found with specified litleTxnId', capture_response.message
end
def test_refund_unsuccessful
- assert credit_response = @gateway.refund(10010, 123456789012345360)
+ assert credit_response = @gateway.refund(10010, '123456789012345360')
assert_failure credit_response
assert_equal 'No transaction found with specified litleTxnId', credit_response.message
end
def test_void_unsuccessful
- assert void_response = @gateway.void(123456789012345360)
+ assert void_response = @gateway.void('123456789012345360')
assert_failure void_response
assert_equal 'No transaction found with specified litleTxnId', void_response.message
end
@@ -264,6 +370,19 @@ def test_store_and_purchase_with_token_successful
assert_equal 'Approved', response.message
end
+ def test_echeck_store_and_purchase
+ assert store_response = @gateway.store(@store_check)
+ assert_success store_response
+ assert_equal 'Account number was successfully registered', store_response.message
+
+ token = store_response.authorization
+ assert_equal store_response.params['litleToken'], token
+
+ assert response = @gateway.purchase(10010, token)
+ assert_success response
+ assert_equal 'Approved', response.message
+ end
+
def test_successful_verify
assert response = @gateway.verify(@credit_card1, @options)
assert_success response
@@ -307,4 +426,20 @@ def test_purchase_scrubbing
assert_scrubbed(@gateway.options[:login], transcript)
assert_scrubbed(@gateway.options[:password], transcript)
end
+
+ def test_echeck_scrubbing
+ options = @options.merge({
+ order_id: '42',
+ order_source: 'telephone'
+ })
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(2004, @check, options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@check.account_number, transcript)
+ assert_scrubbed(@check.routing_number, transcript)
+ assert_scrubbed(@gateway.options[:login], transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
end
diff --git a/test/remote/gateways/remote_maxipago_test.rb b/test/remote/gateways/remote_maxipago_test.rb
index 01bf27a0e2d..9ac1d94c2b5 100644
--- a/test/remote/gateways/remote_maxipago_test.rb
+++ b/test/remote/gateways/remote_maxipago_test.rb
@@ -48,6 +48,11 @@ def test_successful_purchase_sans_options
assert_success response
end
+ def test_successful_purchase_with_currency
+ assert response = @gateway.purchase(@amount, @credit_card, @options.merge(currency: "CLP"))
+ assert_success response
+ end
+
def test_failed_purchase
assert response = @gateway.purchase(@invalid_amount, @credit_card, @options)
assert_failure response
diff --git a/test/remote/gateways/remote_mercado_pago_test.rb b/test/remote/gateways/remote_mercado_pago_test.rb
new file mode 100644
index 00000000000..415de94fbb2
--- /dev/null
+++ b/test/remote/gateways/remote_mercado_pago_test.rb
@@ -0,0 +1,146 @@
+require 'test_helper'
+
+class RemoteMercadoPagoTest < Test::Unit::TestCase
+ def setup
+ @gateway = MercadoPagoGateway.new(fixtures(:mercado_pago))
+
+ @amount = 500
+ @credit_card = credit_card('4509953566233704')
+ @declined_card = credit_card('4000300011112220')
+ @options = {
+ billing_address: address,
+ shipping_address: address,
+ email: "user+br@example.com",
+ description: 'Store Purchase'
+ }
+ end
+
+ def test_successful_purchase
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'accredited', response.message
+ end
+
+ def test_successful_purchase_with_binary_false
+ @options.update(binary_mode: false)
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'accredited', response.message
+ end
+
+ def test_successful_purchase_with_american_express
+ amex_card = credit_card('375365153556885', brand: 'american_express', verification_value: '1234')
+
+ response = @gateway.purchase(@amount, amex_card, @options)
+ assert_success response
+ assert_equal 'accredited', response.message
+ end
+
+ def test_failed_purchase
+ response = @gateway.purchase(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal "rejected", response.error_code
+ assert_equal 'cc_rejected_other_reason', response.message
+ end
+
+ def test_successful_authorize_and_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+ assert_equal 'pending_capture', auth.message
+
+ assert capture = @gateway.capture(@amount, auth.authorization)
+ assert_success capture
+ assert_equal 'accredited', capture.message
+ end
+
+ def test_failed_authorize
+ response = @gateway.authorize(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal 'cc_rejected_other_reason', response.message
+ end
+
+ def test_partial_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount-1, auth.authorization)
+ assert_success capture
+ assert_equal 'accredited', capture.message
+ end
+
+ def test_failed_capture
+ response = @gateway.capture(@amount, '')
+ assert_failure response
+ assert_equal 'Method not allowed', response.message
+ end
+
+ def test_successful_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount, purchase.authorization)
+ assert_success refund
+ assert_equal nil, refund.message
+ end
+
+ def test_partial_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount-1, purchase.authorization)
+ assert_success refund
+ end
+
+ def test_failed_refund
+ response = @gateway.refund(@amount, '')
+ assert_failure response
+ assert_equal 'Resource /payments/refunds not found.', response.message
+ end
+
+ def test_successful_void
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert void = @gateway.void(auth.authorization)
+ assert_success void
+ assert_equal 'by_collector', void.message
+ end
+
+ def test_failed_void
+ response = @gateway.void('')
+ assert_failure response
+ assert_equal 'Method not allowed', response.message
+ end
+
+ def test_successful_verify
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ assert_match %r{pending_capture}, response.message
+ end
+
+ def test_failed_verify
+ response = @gateway.verify(@declined_card, @options)
+ assert_failure response
+ assert_match %r{cc_rejected_other_reason}, response.message
+ end
+
+ def test_invalid_login
+ gateway = MercadoPagoGateway.new(access_token: '')
+
+ response = gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_match %r{Invalid access parameters}, response.message
+ end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:access_token], transcript)
+ end
+
+end
diff --git a/test/remote/gateways/remote_merchant_partners_test.rb b/test/remote/gateways/remote_merchant_partners_test.rb
index b350166ca54..f398ae39112 100644
--- a/test/remote/gateways/remote_merchant_partners_test.rb
+++ b/test/remote/gateways/remote_merchant_partners_test.rb
@@ -33,7 +33,7 @@ def test_successful_purchase
def test_failed_purchase
response = @gateway.purchase(@amount, @declined_card, @options)
assert_failure response
- assert_equal "Invalid account number", response.message
+ assert_match(/Invalid account/, response.message)
assert response.params["result"].start_with?("DECLINED")
end
@@ -51,7 +51,7 @@ def test_successful_authorize_and_capture
def test_failed_authorize
response = @gateway.authorize(@amount, @declined_card, @options)
assert_failure response
- assert_equal "Invalid account number", response.message
+ assert_match(/Invalid account/, response.message)
assert response.params["result"].start_with?("DECLINED")
end
@@ -103,7 +103,7 @@ def test_successful_credit
def test_failed_credit
response = @gateway.credit(@amount, @declined_card, @options)
assert_failure response
- assert_equal "Invalid account number", response.message
+ assert_match(/Invalid account/, response.message)
assert response.params["result"].start_with?("DECLINED")
end
@@ -116,7 +116,7 @@ def test_successful_verify
def test_failed_verify
response = @gateway.verify(@declined_card, @options)
assert_failure response
- assert_equal "Invalid account number", response.message
+ assert_match(/Invalid account/, response.message)
assert response.params["result"].start_with?("DECLINED")
end
diff --git a/test/remote/gateways/remote_mercury_test.rb b/test/remote/gateways/remote_mercury_test.rb
index 4930ddc2417..b2fb7157cd3 100644
--- a/test/remote/gateways/remote_mercury_test.rb
+++ b/test/remote/gateways/remote_mercury_test.rb
@@ -9,9 +9,9 @@ def setup
@amount = 100
- @credit_card = credit_card("4003000123456781", :brand => "visa", :month => "12", :year => "15")
+ @credit_card = credit_card("4003000123456781", :brand => "visa", :month => "12", :year => "18")
- @track_1_data = "%B4003000123456781^LONGSEN/L. ^15121200000000000000**123******?*"
+ @track_1_data = "%B4003000123456781^LONGSEN/L. ^18121200000000000000**123******?*"
@track_2_data = ";5413330089010608=2512101097750213?"
@options = {
@@ -210,7 +210,7 @@ def test_authorize_and_capture_without_tokenization
assert_success capture
assert_equal '1.00', capture.params['authorize']
end
-
+
def test_successful_authorize_and_capture_with_track_1_data
@credit_card.track_data = @track_1_data
response = @gateway.authorize(100, @credit_card, @options)
@@ -241,4 +241,15 @@ def test_authorize_and_void
void = @gateway.void(response.authorization)
assert_success void
end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
end
diff --git a/test/remote/gateways/remote_migs_test.rb b/test/remote/gateways/remote_migs_test.rb
index 2214f8626c7..c6b83c56195 100644
--- a/test/remote/gateways/remote_migs_test.rb
+++ b/test/remote/gateways/remote_migs_test.rb
@@ -10,10 +10,10 @@ def setup
@amount = 100
@declined_amount = 105
- @visa = credit_card('4005550000000001', :month => 5, :year => 2017, :brand => 'visa')
- @master = credit_card('5123456789012346', :month => 5, :year => 2017, :brand => 'master')
- @amex = credit_card('371449635311004', :month => 5, :year => 2017, :brand => 'american_express')
- @diners = credit_card('30123456789019', :month => 5, :year => 2017, :brand => 'diners_club')
+ @visa = credit_card('4987654321098769', :month => 5, :year => 2021, :brand => 'visa')
+ @master = credit_card('5123456789012346', :month => 5, :year => 2021, :brand => 'master')
+ @amex = credit_card('371449635311004', :month => 5, :year => 2021, :brand => 'american_express')
+ @diners = credit_card('30123456789019', :month => 5, :year => 2021, :brand => 'diners_club')
@credit_card = @visa
@options = {
@@ -37,8 +37,6 @@ def test_server_purchase_url
responses = {
'visa' => /You have chosen .*VISA.*/,
'master' => /You have chosen .*MasterCard.*/,
- 'diners_club' => /You have chosen .*Diners Club.*/,
- 'american_express' => /You have chosen .*American Express.*/
}
responses.each_pair do |card_type, response_text|
@@ -76,6 +74,12 @@ def test_authorize_and_void
assert_equal 'Approved', void.message
end
+ def test_verify
+ assert verify = @gateway.verify(@credit_card, @options)
+ assert_success verify
+ assert_equal 'Approved', verify.message
+ end
+
def test_failed_authorize
assert response = @gateway.authorize(@declined_amount, @credit_card, @options)
assert_failure response
@@ -83,11 +87,12 @@ def test_failed_authorize
end
def test_refund
- assert payment_response = @gateway.purchase(@amount, @credit_card, @options)
- assert_success payment_response
- assert response = @gateway.refund(@amount, payment_response.authorization, @options)
- assert_success response
- assert_equal 'Approved', response.message
+ # skip "Refunds are not working in the testing envirnment"
+ # assert payment_response = @gateway.purchase(@amount, @credit_card, @options)
+ # assert_success payment_response
+ # assert response = @gateway.refund(@amount, payment_response.authorization, @options)
+ # refute_success response
+ # assert_equal 'Approved', response.message
end
def test_status
@@ -104,6 +109,31 @@ def test_invalid_login
assert_equal 'Required field vpc_Merchant was not present in the request', response.message
end
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
+
+ def test_transcript_scrubbing_of_advanced_password
+ gateway = MigsGateway.new(fixtures(:migs).merge(advanced_login: 'advlogin', advanced_password: 'advpass'))
+ purchase = gateway.purchase(@amount, @credit_card, @options)
+
+ transcript = capture_transcript(@gateway) do
+ gateway.refund(@amount, purchase.authorization, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:advanced_password], transcript)
+ end
+
private
def assert_response_match(regexp, url)
diff --git a/test/remote/gateways/remote_moneris_test.rb b/test/remote/gateways/remote_moneris_test.rb
index 8d0bc4cc103..59be37db9eb 100644
--- a/test/remote/gateways/remote_moneris_test.rb
+++ b/test/remote/gateways/remote_moneris_test.rb
@@ -32,6 +32,18 @@ def test_successful_purchase_with_network_tokenization
assert_false response.authorization.blank?
end
+ def test_successful_purchase_with_network_tokenization_apple_pay_source
+ @credit_card = network_tokenization_credit_card('4242424242424242',
+ payment_cryptogram: "BwABB4JRdgAAAAAAiFF2AAAAAAA=",
+ verification_value: nil,
+ source: :apple_pay
+ )
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'Approved', response.message
+ assert_false response.authorization.blank?
+ end
+
def test_successful_authorization
response = @gateway.authorize(@amount, @credit_card, @options)
assert_success response
diff --git a/test/remote/gateways/remote_moneris_us_test.rb b/test/remote/gateways/remote_moneris_us_test.rb
index 51b75e843e9..602d56f5d5e 100644
--- a/test/remote/gateways/remote_moneris_us_test.rb
+++ b/test/remote/gateways/remote_moneris_us_test.rb
@@ -203,4 +203,15 @@ def test_avs_result_nil_when_efraud_disabled
})
end
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
+
end
diff --git a/test/remote/gateways/remote_mundipagg_test.rb b/test/remote/gateways/remote_mundipagg_test.rb
new file mode 100644
index 00000000000..52391c42c63
--- /dev/null
+++ b/test/remote/gateways/remote_mundipagg_test.rb
@@ -0,0 +1,164 @@
+require 'test_helper'
+
+class RemoteMundipaggTest < Test::Unit::TestCase
+ def setup
+ @gateway = MundipaggGateway.new(fixtures(:mundipagg))
+
+ @amount = 100
+ @credit_card = credit_card('4000100011112224')
+ @declined_card = credit_card('4000300011112220')
+ @voucher = credit_card('60607044957644', brand: 'sodexo')
+ @options = {
+ billing_address: address(options = { neighborhood: 'Sesame Street' }),
+ description: 'Store Purchase'
+ }
+ end
+
+ def test_successful_purchase
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'Simulator|Transação de simulação autorizada com sucesso', response.message
+ end
+
+ def test_successful_purchase_with_more_options
+ options = @options.update({
+ order_id: '1',
+ ip: '127.0.0.1',
+ email: 'joe@example.com',
+ shipping_address: address
+ })
+
+ response = @gateway.purchase(@amount, @credit_card, options)
+ assert_success response
+ end
+
+ def test_successful_purchase_with_voucher
+ @options.update(holder_document: '93095135270')
+ response = @gateway.purchase(@amount, @voucher, @options)
+ assert_success response
+ assert_equal 'Simulator|Transação de simulação autorizada com sucesso', response.message
+ end
+
+ def test_failed_purchase
+ response = @gateway.purchase(105200, @declined_card, @options)
+ assert_failure response
+ assert_equal 'Simulator|Transação de simulada negada por falta de crédito, utilizado para realizar simulação de autorização parcial.', response.message
+ end
+
+ def test_successful_authorize_and_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount, auth.authorization)
+ assert_success capture
+ assert_equal 'Simulator|Transação de simulação capturada com sucesso', capture.message
+ end
+
+ def test_failed_authorize
+ response = @gateway.authorize(105200, @declined_card, @options)
+ assert_failure response
+ assert_equal 'Simulator|Transação de simulada negada por falta de crédito, utilizado para realizar simulação de autorização parcial.', response.message
+ end
+
+ def test_partial_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount-1, auth.authorization)
+ assert_success capture
+ end
+
+ def test_failed_capture
+ response = @gateway.capture(@amount, 'abc')
+ assert_failure response
+ assert_equal 'The requested resource does not exist; Charge not found.', response.message
+ end
+
+ def test_successful_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount, purchase.authorization)
+ assert_success refund
+ end
+
+ def test_partial_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount - 1, purchase.authorization)
+ assert_success refund
+ end
+
+ def test_failed_refund
+ response = @gateway.refund(@amount, 'abc')
+ assert_failure response
+ assert_equal 'The requested resource does not exist; Charge not found.', response.message
+ end
+
+ def test_successful_void
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert void = @gateway.void(auth.authorization)
+ assert_success void
+ end
+
+ def test_successful_void_with_voucher
+ @options.update(holder_document: '93095135270')
+ auth = @gateway.purchase(@amount, @voucher, @options)
+ assert_success auth
+
+ assert void = @gateway.void(auth.authorization)
+ assert_success void
+ end
+
+ def test_successful_refund_with_voucher
+ @options.update(holder_document: '93095135270')
+ auth = @gateway.purchase(@amount, @voucher, @options)
+ assert_success auth
+
+ assert void = @gateway.refund(1, auth.authorization)
+ assert_success void
+ end
+
+ def test_failed_void
+ response = @gateway.void('abc')
+ assert_failure response
+ assert_equal 'The requested resource does not exist; Charge not found.', response.message
+ end
+
+ def test_successful_verify
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ assert_match %r{Simulator|Transação de simulação autorizada com sucesso}, response.message
+ end
+
+ def test_successful_store_and_purchase
+ store = @gateway.store(@credit_card, @options)
+ assert_success store
+
+ assert purchase = @gateway.purchase(@amount, store.authorization, @options)
+ assert_success purchase
+ assert_equal 'Simulator|Transação de simulação autorizada com sucesso', purchase.message
+ end
+
+ def test_invalid_login
+ gateway = MundipaggGateway.new(api_key: '')
+
+ response = gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_match %r{Invalid API key; Authorization has been denied for this request.}, response.message
+ end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:api_key], transcript)
+ end
+end
diff --git a/test/remote/gateways/remote_nab_transact_test.rb b/test/remote/gateways/remote_nab_transact_test.rb
index 51dc9edd22f..5eb8eac4b33 100644
--- a/test/remote/gateways/remote_nab_transact_test.rb
+++ b/test/remote/gateways/remote_nab_transact_test.rb
@@ -251,4 +251,14 @@ def test_failure_trigger_purchase
assert_equal 'Invalid Amount', purchase_response.message
end
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ clean_transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, clean_transcript)
+ assert_scrubbed(@credit_card.verification_value.to_s, clean_transcript)
+ end
+
end
diff --git a/test/remote/gateways/remote_netbanx_test.rb b/test/remote/gateways/remote_netbanx_test.rb
index 5d2731659c1..5d28ce15ce5 100644
--- a/test/remote/gateways/remote_netbanx_test.rb
+++ b/test/remote/gateways/remote_netbanx_test.rb
@@ -3,13 +3,13 @@
class RemoteNetbanxTest < Test::Unit::TestCase
def setup
@gateway = NetbanxGateway.new(fixtures(:netbanx))
-
@amount = 100
@credit_card = credit_card('4530910000012345')
@declined_amount = 11
@options = {
billing_address: address,
- description: 'Store Purchase'
+ description: 'Store Purchase',
+ currency: 'CAD'
}
end
@@ -54,7 +54,7 @@ def test_successful_authorize_and_capture
auth = @gateway.authorize(@amount, @credit_card, @options)
assert_success auth
- assert capture = @gateway.capture(@amount, auth.authorization)
+ assert capture = @gateway.capture(@amount, auth.authorization, @options)
assert_success capture
assert_equal 'OK', capture.message
end
@@ -63,7 +63,7 @@ def test_partial_capture
auth = @gateway.authorize(@amount, @credit_card, @options)
assert_success auth
- assert capture = @gateway.capture(@amount-1, auth.authorization)
+ assert capture = @gateway.capture(@amount-1, auth.authorization, @options)
assert_success capture
end
@@ -127,7 +127,7 @@ def test_failed_refund
auth = @gateway.authorize(@amount, @credit_card, @options)
assert_success auth
- assert capture = @gateway.capture(@amount, auth.authorization)
+ assert capture = @gateway.capture(@amount, auth.authorization, @options)
assert_success capture
# the following shall fail if you run it immediately after the capture
@@ -141,7 +141,7 @@ def test_successful_void
auth = @gateway.authorize(@amount, @credit_card, @options)
assert_success auth
- assert void = @gateway.void(auth.authorization)
+ assert void = @gateway.void(auth.authorization, @options)
assert_success void
assert_equal 'OK', void.message
end
diff --git a/test/remote/gateways/remote_nmi_test.rb b/test/remote/gateways/remote_nmi_test.rb
index df24174ac18..acf13461d44 100644
--- a/test/remote/gateways/remote_nmi_test.rb
+++ b/test/remote/gateways/remote_nmi_test.rb
@@ -9,6 +9,14 @@ def setup
:routing_number => '123123123',
:account_number => '123123123'
)
+ @apple_pay_card = network_tokenization_credit_card('4111111111111111',
+ :payment_cryptogram => "EHuWW9PiBkWvqE5juRwDzAUFBAk=",
+ :month => "01",
+ :year => "2024",
+ :source => :apple_pay,
+ :eci => "5",
+ :transaction_id => "123456789"
+ )
@options = {
:order_id => generate_unique_id,
:billing_address => address,
@@ -62,6 +70,22 @@ def test_failed_purchase_with_echeck
assert_equal 'FAILED', response.message
end
+ def test_successful_purchase_with_apple_pay_card
+ assert @gateway.supports_network_tokenization?
+ assert response = @gateway.purchase(@amount, @apple_pay_card, @options)
+ assert_success response
+ assert response.test?
+ assert_equal 'Succeeded', response.message
+ assert response.authorization
+ end
+
+ def test_failed_purchase_with_apple_pay_card
+ assert response = @gateway.purchase(99, @apple_pay_card, @options)
+ assert_failure response
+ assert response.test?
+ assert_equal 'DECLINE', response.message
+ end
+
def test_successful_authorization
assert response = @gateway.authorize(@amount, @credit_card, @options)
assert_success response
@@ -209,6 +233,15 @@ def test_merchant_defined_fields
assert_success @gateway.purchase(@amount, @credit_card, @options)
end
+ def test_verify_credentials
+ assert @gateway.verify_credentials
+
+ gateway = NmiGateway.new(login: 'unknown', password: 'unknown')
+ assert !gateway.verify_credentials
+ gateway = NmiGateway.new(login: fixtures(:nmi)[:login], password: 'unknown')
+ assert !gateway.verify_credentials
+ end
+
def test_card_transcript_scrubbing
transcript = capture_transcript(@gateway) do
@gateway.purchase(@amount, @credit_card, @options)
@@ -234,4 +267,17 @@ def test_check_transcript_scrubbing
# "password=password is filtered, but can't be tested b/c of key match"
# assert_scrubbed(@gateway.options[:password], clean_transcript)
end
+
+ def test_network_tokenization_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @apple_pay_card, @options)
+ end
+ clean_transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@apple_pay_card.number, clean_transcript)
+ assert_scrubbed(@apple_pay_card.payment_cryptogram, clean_transcript)
+
+ # "password=password is filtered, but can't be tested b/c of key match"
+ # assert_scrubbed(@gateway.options[:password], clean_transcript)
+ end
end
diff --git a/test/remote/gateways/remote_ogone_test.rb b/test/remote/gateways/remote_ogone_test.rb
index d5a369ebab6..8dd7ae9264e 100644
--- a/test/remote/gateways/remote_ogone_test.rb
+++ b/test/remote/gateways/remote_ogone_test.rb
@@ -14,7 +14,8 @@ def setup
:order_id => generate_unique_id[0...30],
:billing_address => address,
:description => 'Store Purchase',
- :currency => fixtures(:ogone)[:currency] || 'EUR'
+ :currency => fixtures(:ogone)[:currency] || 'EUR',
+ :origin => 'STORE'
}
end
@@ -239,4 +240,15 @@ def test_invalid_login
assert response = gateway.purchase(@amount, @credit_card, @options)
assert_failure response
end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
end
diff --git a/test/remote/gateways/remote_omise_test.rb b/test/remote/gateways/remote_omise_test.rb
index 527e71463ed..4b5be3064b0 100644
--- a/test/remote/gateways/remote_omise_test.rb
+++ b/test/remote/gateways/remote_omise_test.rb
@@ -6,10 +6,11 @@ def setup
@amount = 8888
@credit_card = credit_card('4242424242424242')
@declined_card = credit_card('4255555555555555')
- @invalid_cvc = credit_card('4024007148673576', {verification_value: ''})
+ @invalid_cvc = credit_card('4111111111160001', {verification_value: ''})
@options = {
description: 'Active Merchant',
- email: 'active.merchant@testing.test'
+ email: 'active.merchant@testing.test',
+ currency: 'thb'
}
end
@@ -31,7 +32,7 @@ def test_missing_secret_key
end
def test_successful_purchase
- response = @gateway.purchase(@amount, @credit_card)
+ response = @gateway.purchase(@amount, @credit_card, @options)
assert_success response
assert_equal 'Success', response.message
assert_equal response.params['amount'], @amount
diff --git a/test/remote/gateways/remote_openpay_test.rb b/test/remote/gateways/remote_openpay_test.rb
index bd44baa7b49..01cc9fab432 100644
--- a/test/remote/gateways/remote_openpay_test.rb
+++ b/test/remote/gateways/remote_openpay_test.rb
@@ -21,6 +21,13 @@ def test_successful_purchase
assert_nil response.message
end
+ def test_successful_purchase_with_email
+ @options[:email] = '%d@example.org' % Time.now
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_nil response.message
+ end
+
def test_unsuccessful_purchase
assert response = @gateway.purchase(@amount, @declined_card, @options)
assert_failure response
@@ -52,6 +59,13 @@ def test_successful_authorize
assert_nil response.message
end
+ def test_successful_authorize_with_email
+ @options[:email] = '%d@example.org' % Time.now
+ assert response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+ assert_nil response.message
+ end
+
def test_unsuccessful_authorize
assert response = @gateway.authorize(@amount, @declined_card, @options)
assert_failure response
@@ -106,6 +120,17 @@ def test_successful_purchase_with_device_session_id
assert_success response
end
+ def test_successful_purchase_with_card_points
+ assert response = @gateway.purchase(@amount, @credit_card, @options.merge(use_card_points: 'NONE'))
+ assert_success response
+ end
+
+ def test_failed_purchase_with_card_points
+ assert response = @gateway.purchase(@amount, @credit_card, @options.merge(use_card_points: 'MIXED'))
+ assert_failure response
+ assert_match %r{cardNumber not allowed for Card points}, response.message
+ end
+
def test_successful_store
new_email_address = '%d@example.org' % Time.now
assert response = @gateway.store(@credit_card, name: 'Test User', email: new_email_address)
diff --git a/test/remote/gateways/remote_opp_test.rb b/test/remote/gateways/remote_opp_test.rb
index e5f00172bcd..5b96b7a5c4f 100644
--- a/test/remote/gateways/remote_opp_test.rb
+++ b/test/remote/gateways/remote_opp_test.rb
@@ -10,7 +10,7 @@ def setup
@invalid_card = credit_card("4444444444444444", month: 05, year: 2018)
@amex_card = credit_card("377777777777770 ", month: 05, year: 2018, brand: 'amex', verification_value: '1234')
- request_type = 'complete' # 'minimal' || 'complete'
+ request_type = 'complete' # 'minimal' || 'complete'
time = Time.now.to_i
ip = '101.102.103.104'
@complete_request_options = {
@@ -50,29 +50,38 @@ def setup
ip: ip,
},
}
-
+
@minimal_request_options = {
order_id: "Order #{time}",
description: 'Store Purchase - Books',
}
- @complete_request_options['customParameters[SHOPPER_test124TestName009]'] = 'customParameters_test'
- @complete_request_options['customParameters[SHOPPER_otherCustomerParameter]'] = 'otherCustomerParameter_test'
+ @complete_request_options['customParameters[SHOPPER_test124TestName009]'] = 'customParameters_test'
+ @complete_request_options['customParameters[SHOPPER_otherCustomerParameter]'] = 'otherCustomerParameter_test'
@test_success_id = "8a82944a4e008ca9014e1273e0696122"
@test_failure_id = "8a8294494e0078a6014e12b371fb6a8e"
@test_wrong_reference_id = "8a8444494a0033a6014e12b371fb6a1e"
-
+
@options = @minimal_request_options if request_type == 'minimal'
@options = @complete_request_options if request_type == 'complete'
end
-
+
# ****************************************** SUCCESSFUL TESTS ******************************************
def test_successful_purchase
@options[:description] = __method__
-
+
response = @gateway.purchase(@amount, @valid_card, @options)
assert_success response, "Failed purchase"
+ assert_match %r{Request successfully processed}, response.message
+
+ assert response.test?
+ end
+
+ def test_successful_purchase_sans_options
+ response = @gateway.purchase(@amount, @valid_card)
+ assert_success response
+ assert_match %r{Request successfully processed}, response.message
assert response.test?
end
@@ -81,7 +90,9 @@ def test_successful_authorize
@options[:description] = __method__
response = @gateway.authorize(@amount, @valid_card, @options)
- assert_success response, "Authorization Failed"
+ assert_success response, "Authorization Failed"
+ assert_match %r{Request successfully processed}, response.message
+
assert response.test?
end
@@ -90,31 +101,37 @@ def test_successful_capture
auth = @gateway.authorize(@amount, @valid_card, @options)
assert_success auth, "Authorization Failed"
assert auth.test?
-
+
capt = @gateway.capture(@amount, auth.authorization, @options)
assert_success capt, "Capture failed"
+ assert_match %r{Request successfully processed}, capt.message
+
assert capt.test?
end
def test_successful_refund
@options[:description] = __method__
purchase = @gateway.purchase(@amount, @valid_card, @options)
- assert_success purchase, "Purchase failed"
+ assert_success purchase, "Purchase failed"
assert purchase.test?
refund = @gateway.refund(@amount, purchase.authorization, @options)
assert_success refund, "Refund failed"
+ assert_match %r{Request successfully processed}, refund.message
+
assert refund.test?
end
def test_successful_void
@options[:description] = __method__
purchase = @gateway.purchase(@amount, @valid_card, @options)
- assert_success purchase, "Purchase failed"
+ assert_success purchase, "Purchase failed"
assert purchase.test?
void = @gateway.void(purchase.authorization, @options)
assert_success void, "Void failed"
+ assert_match %r{Request successfully processed}, void.message
+
assert void.test?
end
@@ -125,6 +142,7 @@ def test_successful_partial_capture
assert capture = @gateway.capture(@amount-1, auth.authorization)
assert_success capture
+ assert_match %r{Request successfully processed}, capture.message
end
def test_successful_partial_refund
@@ -134,49 +152,65 @@ def test_successful_partial_refund
assert refund = @gateway.refund(@amount-1, purchase.authorization)
assert_success refund
+ assert_match %r{Request successfully processed}, refund.message
end
def test_successful_verify
@options[:description] = __method__
response = @gateway.verify(@valid_card, @options)
assert_success response
+ assert_match %r{Request successfully processed}, response.message
end
-# ****************************************** FAILURE TESTS ******************************************
+# ****************************************** FAILURE TESTS ******************************************
def test_failed_purchase
@options[:description] = __method__
response = @gateway.purchase(@amount, @invalid_card, @options)
assert_failure response
- assert_equal Gateway::STANDARD_ERROR_CODE[:incorrect_number], response.error_code
+ assert_match %r{invalid creditcard}, response.message
end
def test_failed_authorize
@options[:description] = __method__
response = @gateway.authorize(@amount, @invalid_card, @options)
assert_failure response
- assert_equal Gateway::STANDARD_ERROR_CODE[:incorrect_number], response.error_code
+ assert_match %r{invalid creditcard}, response.message
end
def test_failed_capture
@options[:description] = __method__
response = @gateway.capture(@amount, @test_wrong_reference_id)
assert_failure response
- assert_equal Gateway::STANDARD_ERROR_CODE[:processing_error], response.error_code
+ assert_match %r{capture needs at least one successful transaction}, response.message
end
def test_failed_refund
@options[:description] = __method__
response = @gateway.refund(@amount, @test_wrong_reference_id)
assert_failure response
- assert_equal Gateway::STANDARD_ERROR_CODE[:processing_error], response.error_code
+ assert_match %r{Invalid payment data}, response.message
end
def test_failed_void
@options[:description] = __method__
response = @gateway.void(@test_wrong_reference_id, @options)
assert_failure response
- assert_equal Gateway::STANDARD_ERROR_CODE[:processing_error], response.error_code
+ assert_match %r{reversal needs at least one successful transaction}, response.message
end
+# ************************************** TRANSCRIPT SCRUB ******************************************
+
+ def test_transcript_scrubbing
+ assert @gateway.supports_scrubbing?
+
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @valid_card)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@valid_card.number, transcript)
+ assert_scrubbed(@valid_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
end
diff --git a/test/remote/gateways/remote_optimal_payment_test.rb b/test/remote/gateways/remote_optimal_payment_test.rb
index fa034da690d..20f56fbd196 100644
--- a/test/remote/gateways/remote_optimal_payment_test.rb
+++ b/test/remote/gateways/remote_optimal_payment_test.rb
@@ -44,6 +44,13 @@ def test_unsuccessful_purchase
assert_equal 'auth declined', response.message
end
+ def test_purchase_with_no_cvv
+ @credit_card.verification_value = ''
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'no_error', response.message
+ end
+
def test_authorize_and_capture
assert auth = @gateway.authorize(@amount, @credit_card, @options)
assert_success auth
@@ -134,6 +141,18 @@ def test_invalid_login
)
assert response = gateway.purchase(@amount, @credit_card, @options)
assert_failure response
- assert_equal 'invalid credentials', response.message
+ assert_equal 'invalid merchant account', response.message
+ end
+
+ # Password assertion hard-coded due to the value being the same as the login, which would cause a false-positive
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed('%3CstorePwd%3Etest%3C/storePwd%3E', transcript)
end
end
diff --git a/test/remote/gateways/remote_orbital_test.rb b/test/remote/gateways/remote_orbital_test.rb
index 67f9cd4c628..c608bdd823f 100644
--- a/test/remote/gateways/remote_orbital_test.rb
+++ b/test/remote/gateways/remote_orbital_test.rb
@@ -6,12 +6,13 @@ def setup
@gateway = ActiveMerchant::Billing::OrbitalGateway.new(fixtures(:orbital_gateway))
@amount = 100
- @credit_card = credit_card('4111111111111111')
+ @credit_card = credit_card('4112344112344113')
@declined_card = credit_card('4000300011112220')
@options = {
:order_id => generate_unique_id,
:address => address,
+ :merchant_id => 'merchant1234'
}
@cards = {
@@ -22,6 +23,22 @@ def setup
:diners => "36438999960016",
:jcb => "3566002020140006"}
+ @level_2_options = {
+ tax_indicator: "1",
+ tax: "75",
+ advice_addendum_1: 'taa1 - test',
+ advice_addendum_2: 'taa2 - test',
+ advice_addendum_3: 'taa3 - test',
+ advice_addendum_4: 'taa4 - test',
+ purchase_order: '123abc',
+ name: address[:name],
+ address1: address[:address1],
+ address2: address[:address2],
+ city: address[:city],
+ state: address[:state],
+ zip: address[:zip],
+ }
+
@test_suite = [
{:card => :visa, :AVSzip => 11111, :CVD => 111, :amount => 3000},
{:card => :visa, :AVSzip => 33333, :CVD => nil, :amount => 3801},
@@ -40,11 +57,85 @@ def test_successful_purchase
assert_equal 'Approved', response.message
end
+ def test_successful_purchase_with_soft_descriptor_hash
+ assert response = @gateway.purchase(
+ @amount, @credit_card, @options.merge(
+ soft_descriptors: {
+ merchant_name: 'Merch',
+ product_description: 'Description',
+ merchant_email: 'email@example',
+ }
+ )
+ )
+ assert_success response
+ assert_equal 'Approved', response.message
+ end
+
+ def test_successful_purchase_with_level_2_data
+ response = @gateway.purchase(@amount, @credit_card, @options.merge(level_2_data: @level_2_options))
+
+ assert_success response
+ assert_equal 'Approved', response.message
+ end
+
+ def test_successful_purchase_with_visa_network_tokenization_credit_card_with_eci
+ network_card = network_tokenization_credit_card('4788250000028291',
+ payment_cryptogram: "BwABB4JRdgAAAAAAiFF2AAAAAAA=",
+ transaction_id: "BwABB4JRdgAAAAAAiFF2AAAAAAA=",
+ verification_value: '111',
+ brand: 'visa',
+ eci: '5'
+ )
+ assert response = @gateway.purchase(3000, network_card, @options)
+ assert_success response
+ assert_equal 'Approved', response.message
+ assert_false response.authorization.blank?
+ end
+
+ def test_successful_purchase_with_master_card_network_tokenization_credit_card
+ network_card = network_tokenization_credit_card('4788250000028291',
+ payment_cryptogram: "BwABB4JRdgAAAAAAiFF2AAAAAAA=",
+ transaction_id: "BwABB4JRdgAAAAAAiFF2AAAAAAA=",
+ verification_value: '111',
+ brand: 'master'
+ )
+ assert response = @gateway.purchase(3000, network_card, @options)
+ assert_success response
+ assert_equal 'Approved', response.message
+ assert_false response.authorization.blank?
+ end
+
+ def test_successful_purchase_with_american_express_network_tokenization_credit_card
+ network_card = network_tokenization_credit_card('4788250000028291',
+ payment_cryptogram: "BwABB4JRdgAAAAAAiFF2AAAAAAA=",
+ transaction_id: "BwABB4JRdgAAAAAAiFF2AAAAAAA=",
+ verification_value: '111',
+ brand: 'american_express'
+ )
+ assert response = @gateway.purchase(3000, network_card, @options)
+ assert_success response
+ assert_equal 'Approved', response.message
+ assert_false response.authorization.blank?
+ end
+
+ def test_successful_purchase_with_discover_network_tokenization_credit_card
+ network_card = network_tokenization_credit_card('4788250000028291',
+ payment_cryptogram: "BwABB4JRdgAAAAAAiFF2AAAAAAA=",
+ transaction_id: "BwABB4JRdgAAAAAAiFF2AAAAAAA=",
+ verification_value: '111',
+ brand: 'discover'
+ )
+ assert response = @gateway.purchase(3000, network_card, @options)
+ assert_success response
+ assert_equal 'Approved', response.message
+ assert_false response.authorization.blank?
+ end
+
# Amounts of x.01 will fail
def test_unsuccessful_purchase
assert response = @gateway.purchase(101, @declined_card, @options)
assert_failure response
- assert_equal 'AUTH DECLINED 12001', response.message
+ assert_equal 'Invalid CC Number', response.message
end
def test_authorize_and_capture
@@ -57,6 +148,15 @@ def test_authorize_and_capture
assert_success capture
end
+ def test_successful_authorize_and_capture_with_level_2_data
+ auth = @gateway.authorize(@amount, @credit_card, @options.merge(level_2_data: @level_2_options))
+ assert_success auth
+ assert_equal "Approved", auth.message
+
+ capture = @gateway.capture(@amount, auth.authorization, @options.merge(level_2_data: @level_2_options))
+ assert_success capture
+ end
+
def test_authorize_and_void
assert auth = @gateway.authorize(@amount, @credit_card, @options.merge(:order_id => '2'))
assert_success auth
@@ -75,6 +175,15 @@ def test_refund
assert_success refund
end
+ def test_successful_refund_with_level_2_data
+ amount = @amount
+ assert response = @gateway.purchase(amount, @credit_card, @options.merge(level_2_data: @level_2_options))
+ assert_success response
+ assert response.authorization
+ assert refund = @gateway.refund(amount, response.authorization, @options.merge(level_2_data: @level_2_options))
+ assert_success refund
+ end
+
def test_failed_capture
assert response = @gateway.capture(@amount, '')
assert_failure response
@@ -173,7 +282,7 @@ def test_successful_verify
def test_failed_verify
response = @gateway.verify(@declined_card, @options)
assert_failure response
- assert_equal 'AUTH DECLINED 12001', response.message
+ assert_equal 'Invalid CC Number', response.message
end
def test_transcript_scrubbing
@@ -185,5 +294,20 @@ def test_transcript_scrubbing
assert_scrubbed(@credit_card.number, transcript)
assert_scrubbed(@credit_card.verification_value, transcript)
assert_scrubbed(@gateway.options[:password], transcript)
+ assert_scrubbed(@gateway.options[:login], transcript)
+ assert_scrubbed(@gateway.options[:merchant_id], transcript)
+ end
+
+ def test_transcript_scrubbing_profile
+ transcript = capture_transcript(@gateway) do
+ @gateway.add_customer_profile(@credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ assert_scrubbed(@gateway.options[:login], transcript)
+ assert_scrubbed(@gateway.options[:merchant_id], transcript)
end
end
diff --git a/test/remote/gateways/remote_paybox_direct_plus_test.rb b/test/remote/gateways/remote_paybox_direct_plus_test.rb
new file mode 100644
index 00000000000..e375d5eaeb3
--- /dev/null
+++ b/test/remote/gateways/remote_paybox_direct_plus_test.rb
@@ -0,0 +1,112 @@
+# encoding: utf-8
+
+require 'test_helper'
+
+class RemotePayboxDirectPlusTest < Test::Unit::TestCase
+
+ def setup
+ @gateway = PayboxDirectPlusGateway.new(fixtures(:paybox_direct_plus))
+
+ @amount = 100
+ @credit_card = credit_card('1111222233334444')
+ @declined_card = credit_card('1111222233334445')
+
+ @options = {
+ :order_id => '1',
+ :billing_address => address,
+ :description => 'Store Purchase'
+ }
+ end
+
+ def test_successful_purchase
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'The transaction was approved', response.message
+ end
+
+ def test_unsuccessful_purchase
+ assert response = @gateway.purchase(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal "PAYBOX : Num\xE9ro de porteur invalide".force_encoding('ASCII-8BIT'), response.message
+ end
+
+ def test_authorize_and_capture
+ amount = @amount
+ assert auth = @gateway.authorize(amount, @credit_card, @options)
+ assert_success auth
+ assert_equal 'The transaction was approved', auth.message
+ assert auth.authorization
+ assert capture = @gateway.capture(amount, auth.authorization, :order_id => '1')
+ assert_success capture
+ end
+
+ def test_purchase_and_void
+ assert purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+ assert_equal 'The transaction was approved', purchase.message
+ assert purchase.authorization
+ # Paybox requires you to remember the expiration date
+ assert void = @gateway.void(purchase.authorization, :order_id => '1', :amount => @amount)
+ assert_equal 'The transaction was approved', void.message
+ assert_success void
+ end
+
+ def test_failed_capture
+ assert response = @gateway.capture(@amount, '', :order_id => '1')
+ assert_failure response
+ assert_equal "Invalid data", response.message
+ end
+
+ def test_purchase_and_partial_credit
+ assert purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+ assert_equal 'The transaction was approved', purchase.message
+ assert purchase.authorization
+ assert credit = @gateway.credit(@amount / 2, purchase.authorization, :order_id => '1')
+ assert_equal 'The transaction was approved', credit.message
+ assert_success credit
+ end
+
+ def test_successful_refund
+ assert purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount, purchase.authorization, order_id: '1')
+ assert_success refund
+ end
+
+ def test_partial_refund
+ assert purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount/2, purchase.authorization, order_id: '1')
+ assert_success refund
+ end
+
+ def test_failed_refund
+ refund = @gateway.refund(@amount, '', order_id: '2')
+ assert_failure refund
+ assert_equal 'Invalid data', refund.message
+ end
+
+ def test_invalid_login
+ gateway = PayboxDirectPlusGateway.new(
+ login: '199988899',
+ password: '1999888F',
+ rang: 100
+ )
+ assert response = gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal "Non autorise", response.message
+ end
+
+ def test_invalid_login_without_rang
+ gateway = PayboxDirectPlusGateway.new(
+ login: '199988899',
+ password: '1999888F',
+ )
+ assert response = gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal "Non autorise", response.message
+ end
+end
diff --git a/test/remote/gateways/remote_paybox_direct_test.rb b/test/remote/gateways/remote_paybox_direct_test.rb
index a76d61ab23d..8eefde1a2a7 100644
--- a/test/remote/gateways/remote_paybox_direct_test.rb
+++ b/test/remote/gateways/remote_paybox_direct_test.rb
@@ -6,18 +6,18 @@ class RemotePayboxDirectTest < Test::Unit::TestCase
def setup
@gateway = PayboxDirectGateway.new(fixtures(:paybox_direct))
-
+
@amount = 100
@credit_card = credit_card('1111222233334444')
@declined_card = credit_card('1111222233334445')
-
- @options = {
+
+ @options = {
:order_id => '1',
:billing_address => address,
:description => 'Store Purchase'
}
end
-
+
def test_successful_purchase
assert response = @gateway.purchase(@amount, @credit_card, @options)
assert_success response
@@ -39,7 +39,7 @@ def test_authorize_and_capture
assert capture = @gateway.capture(amount, auth.authorization, :order_id => '1')
assert_success capture
end
-
+
def test_purchase_and_void
assert purchase = @gateway.purchase(@amount, @credit_card, @options)
assert_success purchase
@@ -56,7 +56,7 @@ def test_failed_capture
assert_failure response
assert_equal "Invalid data", response.message
end
-
+
def test_purchase_and_partial_credit
assert purchase = @gateway.purchase(@amount, @credit_card, @options)
assert_success purchase
@@ -66,7 +66,7 @@ def test_purchase_and_partial_credit
assert_equal 'The transaction was approved', credit.message
assert_success credit
end
-
+
def test_successful_refund
assert purchase = @gateway.purchase(@amount, @credit_card, @options)
assert_success purchase
@@ -84,7 +84,7 @@ def test_partial_refund
end
def test_failed_refund
- refund = @gateway.refund(@amount, '', order_id: '2')
+ refund = @gateway.refund(@amount, '', order_id: '2')
assert_failure refund
assert_equal 'Invalid data', refund.message
end
diff --git a/test/remote/gateways/remote_payeezy_test.rb b/test/remote/gateways/remote_payeezy_test.rb
index a94283207bd..155991fc847 100644
--- a/test/remote/gateways/remote_payeezy_test.rb
+++ b/test/remote/gateways/remote_payeezy_test.rb
@@ -9,8 +9,43 @@ def setup
@amount = 100
@options = {
:billing_address => address,
- :merchant_ref => 'Store Purchase'
+ :merchant_ref => 'Store Purchase',
+ :ta_token => 'NOIW'
}
+ @options_mdd = {
+ soft_descriptors: {
+ dba_name: "Caddyshack",
+ street: "1234 Any Street",
+ city: "Durham",
+ region: "North Carolina",
+ mid: "mid_1234",
+ mcc: "mcc_5678",
+ postal_code: "27701",
+ country_code: "US",
+ merchant_contact_info: "8885551212"
+ }
+ }
+ end
+
+ def test_successful_store
+ assert response = @gateway.store(@credit_card, @options)
+ assert_success response
+ assert_equal 'Token successfully created.', response.message
+ assert response.authorization
+ end
+
+ def test_successful_store_and_purchase
+ assert response = @gateway.store(@credit_card, @options)
+ assert_success response
+ assert !response.authorization.blank?
+ assert purchase = @gateway.purchase(@amount, response.authorization, @options)
+ assert_success purchase
+ end
+
+ def test_unsuccessful_store
+ assert response = @gateway.store(@bad_credit_card, @options)
+ assert_failure response
+ assert_equal 'The credit card number check failed', response.message
end
def test_successful_purchase
@@ -20,7 +55,14 @@ def test_successful_purchase
end
def test_successful_purchase_with_echeck
- assert response = @gateway.purchase(@amount, @check, @options)
+ options = @options.merge({customer_id_type: "1", customer_id_number: "1", client_email: "test@example.com"})
+ assert response = @gateway.purchase(@amount, @check, options)
+ assert_match(/Transaction Normal/, response.message)
+ assert_success response
+ end
+
+ def test_successful_purchase_with_soft_descriptors
+ assert response = @gateway.purchase(@amount, @credit_card, @options.merge(@options_mdd))
assert_match(/Transaction Normal/, response.message)
assert_success response
end
@@ -40,6 +82,18 @@ def test_authorize_and_capture
assert_success capture
end
+ def test_successful_store_and_auth_and_capture
+ assert response = @gateway.store(@credit_card, @options)
+ assert_success response
+
+ assert auth = @gateway.authorize(@amount, response.authorization, @options)
+ assert_success auth
+ assert auth.authorization
+
+ assert capture = @gateway.capture(@amount, auth.authorization)
+ assert_success capture
+ end
+
def test_failed_authorize
@amount = 501300
assert auth = @gateway.authorize(@amount, @credit_card, @options)
@@ -82,6 +136,20 @@ def test_successful_refund_with_echeck
assert response.authorization
end
+ def test_successful_refund_with_stored_card
+ response = @gateway.store(@credit_card, @options)
+ assert_success response
+
+ assert purchase = @gateway.purchase(@amount, response.authorization, @options)
+ assert_match(/Transaction Normal/, purchase.message)
+ assert_success purchase
+
+ assert response = @gateway.refund(50, purchase.authorization)
+ assert_success response
+ assert_match(/Transaction Normal/, response.message)
+ assert response.authorization
+ end
+
def test_partial_refund
purchase = @gateway.purchase(@amount, @credit_card, @options)
assert_success purchase
@@ -110,6 +178,18 @@ def test_successful_void
assert_equal 'Transaction Normal - Approved', void.message
end
+ def test_successful_void_with_stored_card
+ response = @gateway.store(@credit_card, @options)
+ assert_success response
+
+ auth = @gateway.authorize(@amount, response.authorization, @options)
+ assert_success auth
+
+ assert void = @gateway.void(auth.authorization)
+ assert_success void
+ assert_equal 'Transaction Normal - Approved', void.message
+ end
+
def test_failed_void
response = @gateway.void('')
assert_failure response
@@ -125,7 +205,7 @@ def test_successful_verify
def test_failed_verify
response = @gateway.verify(@bad_credit_card, @options)
assert_failure response
- assert_match %r{The card number must be numeric}, response.message
+ assert_match %r{The credit card number check failed}, response.message
end
def test_bad_creditcard_number
@@ -152,9 +232,34 @@ def test_trans_error
# ask for error 42 (unable to send trans) as the cents bit...
@amount = 500042
assert response = @gateway.purchase(@amount, @credit_card, @options )
- assert_match(/Internal Server Error/, response.message) # 42 is 'unable to send trans'
+ assert_match(/Server Error/, response.message) # 42 is 'unable to send trans'
assert_failure response
- assert_equal response.error_code, "internal_server_error"
+ assert_equal "500", response.error_code
+ end
+
+ def test_transcript_scrubbing_store
+ transcript = capture_transcript(@gateway) do
+ @gateway.store(@credit_card, @options)
+ end
+
+ transcript = @gateway.scrub(transcript)
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:token], transcript)
+ assert_scrubbed(@gateway.options[:apikey], transcript)
+ end
+
+ def test_transcript_scrubbing_store_with_missing_ta_token
+ transcript = capture_transcript(@gateway) do
+ @options.delete(:ta_token)
+ @gateway.store(@credit_card, @options)
+ end
+
+ transcript = @gateway.scrub(transcript)
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:token], transcript)
+ assert_scrubbed(@gateway.options[:apikey], transcript)
end
def test_transcript_scrubbing
diff --git a/test/remote/gateways/remote_payflow_test.rb b/test/remote/gateways/remote_payflow_test.rb
index b0068e50c96..d9384f08854 100644
--- a/test/remote/gateways/remote_payflow_test.rb
+++ b/test/remote/gateways/remote_payflow_test.rb
@@ -17,6 +17,14 @@ def setup
:customer => 'codyexample'
}
+ @extra_options = {
+ :order_id => "123",
+ :description => "Description string",
+ :order_desc => "OrderDesc string",
+ :comment => "Comment string",
+ :comment2 => "Comment2 string"
+ }
+
@check = check(
:routing_number => '111111118',
:account_number => '1234567801'
@@ -32,6 +40,15 @@ def test_successful_purchase
assert !response.fraud_review?
end
+ def test_successful_purchase_with_extra_options
+ assert response = @gateway.purchase(100000, @credit_card, @options.merge(@extra_options))
+ assert_equal "Approved", response.message
+ assert_success response
+ assert response.test?
+ assert_not_nil response.authorization
+ assert !response.fraud_review?
+ end
+
# In order for this remote test to pass, you must go into your Payflow test
# backend and enable the correct filter. Once logged in:
# "Service Settings" ->
@@ -129,6 +146,16 @@ def test_successful_verify
assert_equal "Verified", response.message
end
+ def test_successful_verify_amex
+ @amex_credit_card = credit_card(
+ '378282246310005',
+ :brand => 'american_express'
+ )
+ assert response = @gateway.verify(@amex_credit_card, @options)
+ assert_success response
+ assert_equal "Approved", response.message
+ end
+
def test_failed_verify
assert response = @gateway.verify(credit_card("4000056655665556"), @options)
assert_failure response
@@ -350,4 +377,23 @@ def three_d_secure_option
}
}
end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(50, @check)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@check.account_number, transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
end
diff --git a/test/remote/gateways/remote_payment_express_test.rb b/test/remote/gateways/remote_payment_express_test.rb
index ce2dda8a764..cc18a63de57 100644
--- a/test/remote/gateways/remote_payment_express_test.rb
+++ b/test/remote/gateways/remote_payment_express_test.rb
@@ -75,7 +75,7 @@ def test_invalid_login
:password => ''
)
assert response = gateway.purchase(@amount, @credit_card, @options)
- assert_match %r{error}i, response.message
+ assert_match %r{Invalid Credentials}i, response.message
assert_failure response
end
@@ -138,6 +138,7 @@ def test_transcript_scrubbing
assert_scrubbed(@credit_card.number, clean_transcript)
assert_scrubbed(@credit_card.verification_value.to_s, clean_transcript)
+ assert_scrubbed(@gateway.options[:password], clean_transcript)
end
end
diff --git a/test/remote/gateways/remote_paymentez_test.rb b/test/remote/gateways/remote_paymentez_test.rb
new file mode 100644
index 00000000000..63ba19b25b1
--- /dev/null
+++ b/test/remote/gateways/remote_paymentez_test.rb
@@ -0,0 +1,135 @@
+require 'test_helper'
+
+class RemotePaymentezTest < Test::Unit::TestCase
+ def setup
+ @gateway = PaymentezGateway.new(fixtures(:paymentez))
+
+ @amount = 100
+ @credit_card = credit_card('4111111111111111', verification_value: '555')
+ @declined_card = credit_card('4242424242424242', verification_value: '555')
+ @options = {
+ billing_address: address,
+ description: 'Store Purchase',
+ user_id: '998',
+ email: 'joe@example.com',
+ vat: 0,
+ dev_reference: 'Testing'
+ }
+ end
+
+ def test_successful_purchase
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ end
+
+ def test_successful_purchase_with_more_options
+ options = {
+ order_id: '1',
+ ip: '127.0.0.1',
+ tax_percentage: 0.07
+ }
+
+ response = @gateway.purchase(@amount, @credit_card, @options.merge(options))
+ assert_success response
+ end
+
+ def test_successful_purchase_with_token
+ store_response = @gateway.store(@credit_card, @options)
+ assert_success store_response
+ token = store_response.authorization
+ purchase_response = @gateway.purchase(@amount, token, @options)
+ assert_success purchase_response
+ end
+
+ def test_failed_purchase
+ response = @gateway.purchase(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal Gateway::STANDARD_ERROR_CODE[:card_declined], response.error_code
+ end
+
+ def test_successful_void
+ auth = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert void = @gateway.void(auth.authorization)
+ assert_success void
+ end
+
+ def test_failed_void
+ response = @gateway.void('')
+ assert_failure response
+ assert_equal 'Carrier not supported', response.message
+ assert_equal Gateway::STANDARD_ERROR_CODE[:config_error], response.error_code
+ end
+
+ def test_successful_authorize_and_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+ assert capture = @gateway.capture(@amount, auth.authorization)
+ assert_success capture
+ assert_equal 'Response by mock', capture.message
+ end
+
+ def test_successful_authorize_and_capture_with_token
+ store_response = @gateway.store(@credit_card, @options)
+ assert_success store_response
+ token = store_response.authorization
+ auth = @gateway.authorize(@amount, token, @options)
+ assert_success auth
+ assert capture = @gateway.capture(@amount, auth.authorization)
+ assert_success capture
+ assert_equal 'Response by mock', capture.message
+ end
+
+ def test_failed_authorize
+ response = @gateway.authorize(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal nil, response.message
+ end
+
+ def test_partial_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+ assert capture = @gateway.capture(@amount - 1, auth.authorization)
+ assert_success capture
+ end
+
+ def test_failed_capture
+ response = @gateway.capture(@amount, '')
+ assert_failure response
+ assert_equal 'The capture method is not supported by carrier', response.message
+ end
+
+ def test_store
+ response = @gateway.store(@credit_card, @options)
+ assert_success response
+ end
+
+ def test_unstore
+ response = @gateway.store(@credit_card, @options)
+ assert_success response
+ auth = response.authorization
+ response = @gateway.unstore(auth, @options)
+ assert_success response
+ end
+
+ def test_invalid_login
+ gateway = PaymentezGateway.new(application_code: '9z8y7w6x', app_key: '1a2b3c4d')
+
+ response = gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal 'BackendResponseException', response.message
+ assert_equal Gateway::STANDARD_ERROR_CODE[:config_error], response.error_code
+ end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:app_key], transcript)
+ end
+end
diff --git a/test/remote/gateways/remote_paymill_test.rb b/test/remote/gateways/remote_paymill_test.rb
index 0a6c35c6f9e..66750bf524d 100644
--- a/test/remote/gateways/remote_paymill_test.rb
+++ b/test/remote/gateways/remote_paymill_test.rb
@@ -4,9 +4,11 @@ class RemotePaymillTest < Test::Unit::TestCase
def setup
params = fixtures(:paymill)
@gateway = PaymillGateway.new(public_key: params[:public_key], private_key: params[:private_key])
-
@amount = 100
@credit_card = credit_card('5500000000000004')
+ @options = {
+ :email => 'Longbob.Longse@example.com'
+ }
@declined_card = credit_card('5105105105105100', month: 5, year: 2020)
uri = URI.parse("https://test-token.paymill.com?transaction.mode=CONNECTOR_TEST&channel.id=#{params[:public_key]}&jsonPFunction=paymilljstests&account.number=4111111111111111&account.expiry.month=12&account.expiry.year=2018&account.verification=123&account.holder=John%20Rambo&presentation.amount3D=#{@amount}&presentation.currency3D=EUR")
@@ -19,7 +21,7 @@ def setup
end
def test_successful_purchase
- assert response = @gateway.purchase(@amount, @credit_card)
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
assert_success response
assert_equal 'Operation successful', response.message
end
@@ -32,19 +34,19 @@ def test_successful_purchase_with_token
def test_failed_store_card_attempting_purchase
@credit_card.number = ''
- assert response = @gateway.purchase(@amount, @credit_card)
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
assert_failure response
assert_equal '[account.number] This field is missing.', response.message
end
def test_failed_purchase
- assert response = @gateway.purchase(@amount, @declined_card)
+ assert response = @gateway.purchase(@amount, @declined_card, @options)
assert_failure response
assert_equal 'Card declined', response.message
end
def test_successful_authorize_and_capture
- assert response = @gateway.authorize(@amount, @credit_card)
+ assert response = @gateway.authorize(@amount, @credit_card, @options)
assert_success response
assert_equal 'Operation successful', response.message
assert response.authorization
@@ -55,7 +57,7 @@ def test_successful_authorize_and_capture
end
def test_successful_authorize_and_capture_with_token
- assert response = @gateway.authorize(@amount, @token)
+ assert response = @gateway.authorize(@amount, @token, @options)
assert_success response
assert_equal 'Operation successful', response.message
assert response.authorization
@@ -66,20 +68,20 @@ def test_successful_authorize_and_capture_with_token
end
def test_successful_authorize_with_token
- assert response = @gateway.authorize(@amount, @token)
+ assert response = @gateway.authorize(@amount, @token, @options)
assert_success response
assert_equal 'Operation successful', response.message
assert response.authorization
end
def test_failed_authorize
- assert response = @gateway.authorize(@amount, @declined_card)
+ assert response = @gateway.authorize(@amount, @declined_card, @options)
assert_failure response
assert_equal 'Preauthorisation failed', response.message
end
def test_failed_capture
- assert response = @gateway.authorize(@amount, @credit_card)
+ assert response = @gateway.authorize(@amount, @credit_card, @options)
assert_success response
assert capture_response = @gateway.capture(@amount, response.authorization)
@@ -91,7 +93,7 @@ def test_failed_capture
end
def test_successful_authorize_and_void
- assert response = @gateway.authorize(@amount, @credit_card)
+ assert response = @gateway.authorize(@amount, @credit_card, @options)
assert_success response
assert_equal 'Operation successful', response.message
assert response.authorization
@@ -102,7 +104,7 @@ def test_successful_authorize_and_void
end
def test_successful_refund
- assert response = @gateway.purchase(@amount, @credit_card)
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
assert_success response
assert response.authorization
@@ -112,7 +114,7 @@ def test_successful_refund
end
def test_failed_refund
- assert response = @gateway.purchase(@amount, @credit_card)
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
assert_success response
assert response.authorization
diff --git a/test/remote/gateways/remote_paystation_test.rb b/test/remote/gateways/remote_paystation_test.rb
index ea773793dcc..691cf66ccd1 100644
--- a/test/remote/gateways/remote_paystation_test.rb
+++ b/test/remote/gateways/remote_paystation_test.rb
@@ -110,4 +110,9 @@ def test_failed_refund
assert_equal "Error 11:", response.params["strong"]
end
+ def test_successful_verify
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ assert_match %r{Transaction successful}, response.message
+ end
end
diff --git a/test/remote/gateways/remote_payu_latam_test.rb b/test/remote/gateways/remote_payu_latam_test.rb
index ccea7163211..e6edb50f82f 100644
--- a/test/remote/gateways/remote_payu_latam_test.rb
+++ b/test/remote/gateways/remote_payu_latam_test.rb
@@ -2,18 +2,25 @@
class RemotePayuLatamTest < Test::Unit::TestCase
def setup
- @gateway = PayuLatamGateway.new(fixtures(:payu_latam))
+ @gateway = PayuLatamGateway.new(fixtures(:payu_latam).update(payment_country: 'AR'))
@amount = 4000
@credit_card = credit_card("4097440000000004", verification_value: "444", first_name: "APPROVED", last_name: "")
- @declined_card = credit_card("4097440000000004", verification_value: "333", first_name: "REJECTED", last_name: "")
- @pending_card = credit_card("4097440000000004", verification_value: "222", first_name: "PENDING", last_name: "")
+ @declined_card = credit_card("4097440000000004", verification_value: "444", first_name: "REJECTED", last_name: "")
+ @pending_card = credit_card("4097440000000004", verification_value: "444", first_name: "PENDING", last_name: "")
@options = {
+ dni_number: '5415668464654',
currency: "ARS",
order_id: generate_unique_id,
description: "Active Merchant Transaction",
installments_number: 1,
+ tax: 0,
+ email: "username@domain.com",
+ ip: "127.0.0.1",
+ device_session_id: 'vghs6tvkcle931686k1900o6e1',
+ cookie: 'pt1t38347bs6jc9ruv2ecpv7o2',
+ user_agent: 'Mozilla/5.0 (Windows NT 5.1; rv:18.0) Gecko/20100101 Firefox/18.0',
billing_address: address(
address1: "Viamonte",
address2: "1366",
@@ -30,7 +37,7 @@ def setup
# supports auth and purchase transactions only
def test_invalid_login
- gateway = PayuLatamGateway.new(merchant_id: "", account_id: "", api_login: "U", api_key: "U")
+ gateway = PayuLatamGateway.new(merchant_id: "", account_id: "", api_login: "U", api_key: "U", payment_country: "AR")
response = gateway.purchase(@amount, @credit_card, @options)
assert_failure response
end
@@ -42,10 +49,56 @@ def test_successful_purchase
assert response.test?
end
+ def test_successful_purchase_with_specified_language
+ response = @gateway.purchase(@amount, @credit_card, @options.merge(language: 'es'))
+ assert_success response
+ assert_equal "APPROVED", response.message
+ assert response.test?
+ end
+
+ def test_successul_purchase_with_buyer
+ gateway = PayuLatamGateway.new(fixtures(:payu_latam).update(:account_id => "512327", payment_country: "BR"))
+
+ options_buyer = {
+ currency: "BRL",
+ billing_address: address(
+ address1: "Calle 100",
+ address2: "BL4",
+ city: "Sao Paulo",
+ state: "SP",
+ country: "BR",
+ zip: "09210710",
+ phone: "(11)756312633"
+ ),
+ shipping_address: address(
+ address1: "Calle 200",
+ address2: "N107",
+ city: "Sao Paulo",
+ state: "SP",
+ country: "BR",
+ zip: "01019-030",
+ phone: "(11)756312633"
+ ),
+ buyer: {
+ name: 'Jorge Borges',
+ dni_number: '5415668464123',
+ dni_type: 'TI',
+ cnpj: '32593371000110',
+ email: 'axaxaxas@mlo.org'
+ }
+ }
+
+ response = gateway.purchase(@amount, @credit_card, @options.update(options_buyer))
+ assert_success response
+ assert_equal "APPROVED", response.message
+ assert response.test?
+ end
+
def test_successful_purchase_brazil
- gateway = PayuLatamGateway.new(fixtures(:payu_latam).update(:account_id => "512327"))
+ gateway = PayuLatamGateway.new(fixtures(:payu_latam).update(:account_id => "512327", payment_country: "BR"))
options_brazil = {
+ payment_country: "BR",
currency: "BRL",
billing_address: address(
address1: "Calle 100",
@@ -64,7 +117,10 @@ def test_successful_purchase_brazil
country: "BR",
zip: "01019-030",
phone: "(11)756312633"
- )
+ ),
+ buyer:{
+ cnpj: "32593371000110"
+ }
}
response = gateway.purchase(@amount, @credit_card, @options.update(options_brazil))
@@ -73,8 +129,77 @@ def test_successful_purchase_brazil
assert response.test?
end
- def test_successful_purchase_sans_options
- response = @gateway.purchase(@amount, @credit_card)
+ def test_successful_purchase_colombia
+ gateway = PayuLatamGateway.new(fixtures(:payu_latam).update(:account_id => "512321", payment_country: "CO"))
+
+ options_colombia = {
+ payment_country: "CO",
+ currency: "COP",
+ billing_address: address(
+ address1: "Calle 100",
+ address2: "BL4",
+ city: "Bogota",
+ state: "Bogota DC",
+ country: "CO",
+ zip: "09210710",
+ phone: "(11)756312633"
+ ),
+ shipping_address: address(
+ address1: "Calle 200",
+ address2: "N107",
+ city: "Bogota",
+ state: "Bogota DC",
+ country: "CO",
+ zip: "01019-030",
+ phone: "(11)756312633"
+ ),
+ tax: "3193",
+ tax_return_base: "16806"
+ }
+
+ response = gateway.purchase(2000000, @credit_card, @options.update(options_colombia))
+ assert_success response
+ assert_equal "APPROVED", response.message
+ assert response.test?
+ end
+
+ def test_successful_purchase_mexico
+ gateway = PayuLatamGateway.new(fixtures(:payu_latam).update(:account_id => "512324", payment_country: "MX"))
+
+ options_mexico = {
+ payment_country: "MX",
+ currency: "MXN",
+ billing_address: address(
+ address1: "Calle 100",
+ address2: "BL4",
+ city: "Guadalajara",
+ state: "Jalisco",
+ country: "MX",
+ zip: "09210710",
+ phone: "(11)756312633"
+ ),
+ shipping_address: address(
+ address1: "Calle 200",
+ address2: "N107",
+ city: "Guadalajara",
+ state: "Jalisco",
+ country: "MX",
+ zip: "01019-030",
+ phone: "(11)756312633"
+ ),
+ birth_date: '1985-05-25'
+ }
+
+ response = gateway.purchase(@amount, @credit_card, @options.update(options_mexico))
+ assert_success response
+ assert_equal "APPROVED", response.message
+ assert response.test?
+ end
+
+ def test_successful_purchase_no_description
+ options = @options
+ options.delete(:description)
+ response = @gateway.purchase(@amount, @credit_card, options)
assert_success response
assert_equal "APPROVED", response.message
assert response.test?
@@ -83,10 +208,22 @@ def test_successful_purchase_sans_options
def test_failed_purchase
response = @gateway.purchase(@amount, @declined_card, @options)
assert_failure response
- assert_equal "ANTIFRAUD_REJECTED", response.message
assert_equal "DECLINED", response.params["transactionResponse"]["state"]
end
+ def test_failed_purchase_with_no_options
+ response = @gateway.purchase(@amount, @declined_card, {})
+ assert_failure response
+ assert_equal "DECLINED", response.params["transactionResponse"]["state"]
+ end
+
+ def test_failed_purchase_with_specified_language
+ gateway = PayuLatamGateway.new(merchant_id: "", account_id: "", api_login: "U", api_key: "U", payment_country: "AR")
+ response = gateway.purchase(@amount, @declined_card, @options.merge(language: 'es'))
+ assert_failure response
+ assert_equal "Credenciales inválidas", response.message
+ end
+
def test_successful_authorize
response = @gateway.authorize(@amount, @credit_card, @options)
assert_success response
@@ -94,20 +231,126 @@ def test_successful_authorize
assert_match %r(^\d+\|(\w|-)+$), response.authorization
end
+ def test_successful_authorize_with_specified_language
+ response = @gateway.authorize(@amount, @credit_card, @options.merge(language: 'es'))
+ assert_success response
+ assert_equal "APPROVED", response.message
+ assert_match %r(^\d+\|(\w|-)+$), response.authorization
+ end
+
def test_failed_authorize
response = @gateway.authorize(@amount, @pending_card, @options)
assert_failure response
- assert_equal "PENDING_TRANSACTION_REVIEW", response.message
- assert_equal "PENDING", response.params["transactionResponse"]["state"]
+ assert_equal "DECLINED", response.params["transactionResponse"]["state"]
+ end
+
+ def test_failed_authorize_with_specified_language
+ gateway = PayuLatamGateway.new(merchant_id: "", account_id: "", api_login: "U", api_key: "U", payment_country: "AR")
+ response = gateway.authorize(@amount, @pending_card, @options.merge(language: 'es'))
+ assert_failure response
+ assert_equal "Credenciales inválidas", response.message
+ end
+
+ def test_well_formed_refund_fails_as_expected
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount, purchase.authorization, @options)
+ assert_equal "The payment plan id cannot be empty", refund.message
+ end
+
+ def test_failed_refund
+ response = @gateway.refund(@amount, '')
+ assert_failure response
+ assert_match (/property: parentTransactionId, message: must not be null/), response.message
+ end
+
+ def test_failed_refund_with_specified_language
+ response = @gateway.refund(@amount, '', language: 'es')
+ assert_failure response
+ assert_match (/property: parentTransactionId, message: No puede ser vacio/), response.message
+ end
+
+ # If this test fails, support for void may have been added to the sandbox
+ def test_unsupported_test_void_fails_as_expected
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert void = @gateway.void(auth.authorization)
+ assert_failure void
+ assert_equal "Internal payment provider error. ", void.message
+ end
+
+ def test_failed_void
+ response = @gateway.void('')
+ assert_failure response
+ assert_match (/property: parentTransactionId, message: must not be null/), response.message
+ end
+
+ def test_failed_void_with_specified_language
+ response = @gateway.void('', language: 'es')
+ assert_failure response
+ assert_match (/property: parentTransactionId, message: No puede ser vacio/), response.message
+ end
+
+ # If this test fails, support for captures may have been added to the sandbox
+ def test_unsupported_test_capture_fails_as_expected
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount, auth.authorization)
+ assert_failure capture
+ assert_equal 'Internal payment provider error. ', capture.message
+ end
+
+ def test_failed_capture
+ response = @gateway.capture(@amount, '')
+ assert_failure response
+ assert_match (/must not be null/), response.message
end
def test_verify_credentials
assert @gateway.verify_credentials
- gateway = PayuLatamGateway.new(merchant_id: "X", account_id: "512322", api_login: "X", api_key: "X")
+ gateway = PayuLatamGateway.new(merchant_id: "X", account_id: "512322", api_login: "X", api_key: "X", payment_country: "AR")
assert !gateway.verify_credentials
end
+ def test_successful_verify
+ verify = @gateway.verify(@credit_card, @options)
+
+ assert_success verify
+ assert_equal "APPROVED", verify.message
+ end
+
+ def test_successful_verify_with_specified_amount
+ verify = @gateway.verify(@credit_card, @options.merge(verify_amount: 5100))
+
+ assert_success verify
+ assert_equal "APPROVED", verify.message
+ end
+
+ def test_successful_verify_with_specified_language
+ verify = @gateway.verify(@credit_card, @options.merge(language: 'es'))
+
+ assert_success verify
+ assert_equal "APPROVED", verify.message
+ end
+
+ def test_failed_verify_with_specified_amount
+ verify = @gateway.verify(@credit_card, @options.merge(verify_amount: 1699))
+
+ assert_failure verify
+ assert_equal "The order value is less than minimum allowed. Minimum value allowed 17 ARS", verify.message
+ end
+
+ def test_failed_verify_with_specified_language
+ verify = @gateway.verify(@credit_card, @options.merge(verify_amount: 1699, language: 'es'))
+
+ assert_failure verify
+ assert_equal "The order value is less than minimum allowed. Minimum value allowed 17 ARS", verify.message
+ end
+
def test_transcript_scrubbing
transcript = capture_transcript(@gateway) do
@gateway.purchase(@amount, @credit_card, @options)
diff --git a/test/remote/gateways/remote_pin_test.rb b/test/remote/gateways/remote_pin_test.rb
index 8ad2f9ce593..88d41346671 100644
--- a/test/remote/gateways/remote_pin_test.rb
+++ b/test/remote/gateways/remote_pin_test.rb
@@ -24,6 +24,20 @@ def test_successful_purchase
assert_equal true, response.params['response']['captured']
end
+ def test_successful_purchase_with_metadata
+ options_with_metadata = {
+ metadata: {
+ order_id: generate_unique_id,
+ purchase_number: generate_unique_id
+ }
+ }
+ response = @gateway.purchase(@amount, @credit_card, @options.merge(options_with_metadata))
+ assert_success response
+ assert_equal true, response.params['response']['captured']
+ assert_equal options_with_metadata[:metadata][:order_id], response.params['response']['metadata']['order_id']
+ assert_equal options_with_metadata[:metadata][:purchase_number], response.params['response']['metadata']['purchase_number']
+ end
+
def test_successful_authorize_and_capture
authorization = @gateway.authorize(@amount, @credit_card, @options)
assert_success authorization
@@ -162,7 +176,7 @@ def test_transcript_scrubbing
@gateway.purchase(@amount, @credit_card, @options)
end
clean_transcript = @gateway.scrub(transcript)
-
+
assert_scrubbed(@credit_card.number, clean_transcript)
assert_scrubbed(@credit_card.verification_value.to_s, clean_transcript)
end
diff --git a/test/remote/gateways/remote_pro_pay_test.rb b/test/remote/gateways/remote_pro_pay_test.rb
new file mode 100644
index 00000000000..f08bd6e9176
--- /dev/null
+++ b/test/remote/gateways/remote_pro_pay_test.rb
@@ -0,0 +1,160 @@
+require 'test_helper'
+
+class RemoteProPayTest < Test::Unit::TestCase
+ def setup
+ @gateway = ProPayGateway.new(fixtures(:pro_pay))
+
+ @amount = 100
+ @credit_card = credit_card('4747474747474747', verification_value: 999)
+ @declined_card = credit_card('4616161616161616')
+ @credit_card_without_cvv = credit_card('4747474747474747', verification_value: nil)
+ @options = {
+ billing_address: address,
+ account_num: "32287391"
+ }
+ end
+
+ def test_successful_purchase
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'Success', response.message
+ end
+
+ def test_successful_purchase_with_more_options
+ options = {
+ order_id: '1',
+ ip: "127.0.0.1",
+ email: "joe@example.com",
+ account_num: "32287391"
+ }
+
+ response = @gateway.purchase(@amount, @credit_card, options)
+ assert_success response
+ assert_equal 'Success', response.message
+ end
+
+ def test_successful_recurring_purchase_without_cvv
+ @options.merge!({recurring_payment: 'Y'})
+ response = @gateway.purchase(@amount, @credit_card_without_cvv, @options)
+ assert_success response
+ assert_equal 'Success', response.message
+ end
+
+ def test_failed_purchase
+ response = @gateway.purchase(@amount, @declined_card, @options)
+ assert_failure response
+ assert_match(/declined/, response.message)
+ assert_match(/Insufficient funds/, response.message)
+ end
+
+ def test_successful_authorize_and_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount, auth.authorization, @options)
+ assert_success capture
+ assert_equal 'Success', capture.message
+ end
+
+ def test_failed_authorize
+ response = @gateway.authorize(@amount, @declined_card, @options)
+ assert_failure response
+ assert_match(/declined/, response.message)
+ assert_match(/Insufficient funds/, response.message)
+ end
+
+ def test_partial_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount-1, auth.authorization, @options)
+ assert_success capture
+ end
+
+ def test_failed_capture
+ response = @gateway.capture(@amount, '', @options)
+ assert_failure response
+ assert_match(/Invalid/, response.message)
+ end
+
+ def test_successful_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount, purchase.authorization, @options)
+ assert_success refund
+ assert_equal 'Success', refund.message
+ end
+
+ def test_partial_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount-1, purchase.authorization, @options)
+ assert_success refund
+ end
+
+ def test_failed_refund
+ response = @gateway.refund(@amount, '', @options)
+ assert_failure response
+ assert_match(/Invalid/, response.message)
+ end
+
+ def test_successful_void
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert void = @gateway.void(auth.authorization, @options)
+ assert_success void
+ assert_equal 'Success', void.message
+ end
+
+ def test_failed_void
+ response = @gateway.void('', @options)
+ assert_failure response
+ assert_match(/Invalid/, response.message)
+ end
+
+ def test_successful_credit
+ response = @gateway.credit(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'Success', response.message
+ end
+
+ def test_failed_credit
+ response = @gateway.credit(@amount, credit_card(''), @options)
+ assert_failure response
+ assert_equal 'Invalid ccNum', response.message
+ end
+
+ def test_successful_verify
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ assert_match "Success", response.message
+ end
+
+ def test_failed_verify
+ response = @gateway.verify(@declined_card, @options)
+ assert_failure response
+ assert_match(/declined/, response.message)
+ assert_match(/Insufficient funds/, response.message)
+ end
+
+ def test_invalid_login
+ gateway = ProPayGateway.new(cert_str: 'bad_cert_str')
+
+ response = gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:cert_str], transcript)
+ end
+end
diff --git a/test/remote/gateways/remote_psigate_test.rb b/test/remote/gateways/remote_psigate_test.rb
index ec740a3ed37..ea414c797da 100644
--- a/test/remote/gateways/remote_psigate_test.rb
+++ b/test/remote/gateways/remote_psigate_test.rb
@@ -5,9 +5,10 @@ class PsigateRemoteTest < Test::Unit::TestCase
def setup
Base.mode = :test
@gateway = PsigateGateway.new(fixtures(:psigate))
+ PsigateGateway.ssl_strict = false
@amount = 2400
- @creditcard = credit_card('4242424242424242')
+ @creditcard = credit_card('4111111111111111')
@options = {
:order_id => generate_unique_id,
:billing_address => address,
diff --git a/test/remote/gateways/remote_quickpay_v10_test.rb b/test/remote/gateways/remote_quickpay_v10_test.rb
index fe87601ddd7..0d887c8ae93 100644
--- a/test/remote/gateways/remote_quickpay_v10_test.rb
+++ b/test/remote/gateways/remote_quickpay_v10_test.rb
@@ -108,7 +108,7 @@ def test_unsuccessful_authorize_and_capture
def test_failed_capture
assert response = @gateway.capture(@amount, '1111')
assert_failure response
- assert_equal 'Unknown error - please contact QuickPay', response.message
+ assert_equal 'Not found: No Payment with id 1111', response.message
end
def test_successful_purchase_and_void
@@ -121,6 +121,12 @@ def test_successful_purchase_and_void
assert_equal 'OK', void.message
end
+ def test_unsuccessful_void
+ assert void = @gateway.void('123')
+ assert_failure void
+ assert_equal 'Not found: No Payment with id 123', void.message
+ end
+
def test_successful_authorization_capture_and_credit
assert auth = @gateway.authorize(@amount, @valid_card, @options)
assert_success auth
@@ -174,6 +180,52 @@ def test_successful_store_and_reference_purchase
assert_success purchase
end
+ def test_successful_store_and_reference_recurring_purchase
+ assert store = @gateway.store(@valid_card, @options)
+ assert_success store
+ assert signup = @gateway.purchase(@amount, store.authorization, @options)
+ assert_success signup
+ @options[:order_id] = generate_unique_id[0...10]
+ assert renewal = @gateway.purchase(@amount, store.authorization, @options)
+ assert_success renewal
+ end
+
+ def test_successful_store_and_reference_authorize
+ assert store = @gateway.store(@valid_card, @options)
+ assert_success store
+ assert authorization = @gateway.authorize(@amount, store.authorization, @options)
+ assert_success authorization
+ end
+
+ def test_successful_store_and_credit
+ assert store = @gateway.store(@valid_card, @options)
+ assert_success store
+ assert purchase = @gateway.purchase(@amount, store.authorization, @options)
+ assert_success purchase
+ assert credit = @gateway.refund(@amount, purchase.authorization)
+ assert_success credit
+ end
+
+ def test_unsuccessful_store_and_credit
+ assert store = @gateway.store(@refund_rejected_card, @options)
+ assert_success store
+ assert purchase = @gateway.purchase(@amount, store.authorization, @options)
+ assert_success purchase
+ assert credit = @gateway.refund(@amount, purchase.authorization)
+ assert_failure credit
+ assert_match(/Rejected test operation/, credit.message)
+ end
+
+ def test_successful_store_and_void_authorize
+ assert store = @gateway.store(@valid_card, @options)
+ assert_success store
+ assert authorize = @gateway.authorize(@amount, store.authorization, @options)
+ assert_success authorize
+ assert void = @gateway.void(authorize.authorization)
+ assert_success void
+ assert_equal 'OK', void.message
+ end
+
def test_successful_unstore
assert response = @gateway.store(@valid_card, @options)
assert_success response
diff --git a/test/remote/gateways/remote_qvalent_test.rb b/test/remote/gateways/remote_qvalent_test.rb
index e18f376c613..875bc081ee8 100644
--- a/test/remote/gateways/remote_qvalent_test.rb
+++ b/test/remote/gateways/remote_qvalent_test.rb
@@ -7,6 +7,7 @@ def setup
@amount = 100
@credit_card = credit_card("4000100011112224")
@declined_card = credit_card("4000000000000000")
+ @expired_card = credit_card("4111111113444494")
@options = {
order_id: generate_unique_id,
@@ -19,14 +20,14 @@ def test_invalid_login
gateway = QvalentGateway.new(
username: "bad",
password: "bad",
- merchant: "101"
+ merchant: "101",
+ pem: "bad",
+ pem_password: "bad"
)
- authentication_exception = assert_raise ActiveMerchant::ResponseError do
+ assert_raise ActiveMerchant::ClientCertificateError do
gateway.purchase(@amount, @credit_card, @options)
end
- response = authentication_exception.response
- assert_match(%r{Error 403: Missing authentication}, response.body)
end
def test_successful_purchase
@@ -35,6 +36,39 @@ def test_successful_purchase
assert_equal "Succeeded", response.message
end
+ def test_successful_purchase_with_soft_descriptors
+ options = {
+ order_id: generate_unique_id,
+ billing_address: address,
+ description: "Store Purchase",
+ customer_merchant_name: "Some Merchant",
+ customer_merchant_street_address: "42 Wallaby Way",
+ customer_merchant_location: "Sydney",
+ customer_merchant_country: "AU",
+ customer_merchant_post_code: "2060",
+ customer_merchant_state: "NSW"
+ }
+
+ response = @gateway.purchase(@amount, @credit_card, options)
+ assert_success response
+ assert_equal "Succeeded", response.message
+ end
+
+ def test_successful_purchase_with_3d_secure
+ options = {
+ order_id: generate_unique_id,
+ billing_address: address,
+ description: "Store Purchase",
+ xid: "123",
+ cavv: "456",
+ eci: "5"
+ }
+
+ response = @gateway.purchase(@amount, @credit_card, options)
+ assert_success response
+ assert_equal "Succeeded", response.message
+ end
+
def test_failed_purchase
response = @gateway.purchase(@amount, @declined_card, @options)
assert_failure response
@@ -42,6 +76,68 @@ def test_failed_purchase
assert_equal Gateway::STANDARD_ERROR_CODE[:invalid_number], response.error_code
end
+ def test_successful_authorize
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal "Succeeded", response.message
+ end
+
+ def test_failed_authorize
+ response = @gateway.authorize(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal "Invalid card number (no such number)", response.message
+ end
+
+ def test_successful_capture
+ assert auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+ assert_equal "Succeeded", auth.message
+ assert_not_nil auth.authorization
+
+ assert capture = @gateway.capture(@amount, auth.authorization, @options.merge({ order_id: generate_unique_id }))
+ assert_success capture
+ end
+
+ def test_failed_capture
+ assert auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+ assert_equal "Succeeded", auth.message
+ assert_not_nil auth.authorization
+
+ assert capture = @gateway.capture(@amount, '', @options.merge({ order_id: generate_unique_id }))
+ assert_failure capture
+ end
+
+ def test_successful_partial_capture
+ assert auth = @gateway.authorize(200, @credit_card, @options)
+ assert_success auth
+ assert_equal "Succeeded", auth.message
+ assert_not_nil auth.authorization
+
+ assert capture = @gateway.capture(100, auth.authorization, @options.merge({ order_id: generate_unique_id }))
+ assert_success capture
+ end
+
+ def test_successful_void
+ assert auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+ assert_equal "Succeeded", auth.message
+ assert_not_nil auth.authorization
+
+ assert void = @gateway.void(auth.authorization, @options.merge({ order_id: generate_unique_id }))
+ assert_success void
+ end
+
+ def test_failed_void
+ assert auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+ assert_equal "Succeeded", auth.message
+ assert_not_nil auth.authorization
+
+ assert void = @gateway.void('', @options.merge({ order_id: generate_unique_id }))
+ assert_failure void
+ end
+
def test_successful_refund
response = @gateway.purchase(@amount, @credit_card, @options)
assert_success response
@@ -58,6 +154,18 @@ def test_failed_refund
assert_equal Gateway::STANDARD_ERROR_CODE[:invalid_number], response.error_code
end
+ def test_successful_credit
+ response = @gateway.credit(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal "Succeeded", response.message
+ end
+
+ def test_failed_credit
+ response = @gateway.credit(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal "Invalid card number (no such number)", response.message
+ end
+
def test_successful_store
response = @gateway.store(@credit_card, @options)
assert_success response
diff --git a/test/remote/gateways/remote_realex_test.rb b/test/remote/gateways/remote_realex_test.rb
index 2cd9c6ff568..f8b233cce7e 100644
--- a/test/remote/gateways/remote_realex_test.rb
+++ b/test/remote/gateways/remote_realex_test.rb
@@ -18,6 +18,19 @@ def setup
@mastercard_referral_a = card_fixtures(:realex_mastercard_referral_a)
@mastercard_coms_error = card_fixtures(:realex_mastercard_coms_error)
+ @apple_pay = credit_card = network_tokenization_credit_card('4242424242424242',
+ payment_cryptogram: "EHuWW9PiBkWvqE5juRwDzAUFBAk=",
+ verification_value: nil,
+ eci: '05',
+ source: :apple_pay
+ )
+
+ @declined_apple_pay = credit_card = network_tokenization_credit_card('4000120000001154',
+ payment_cryptogram: "EHuWW9PiBkWvqE5juRwDzAUFBAk=",
+ verification_value: nil,
+ eci: '05',
+ source: :apple_pay
+ )
@amount = 10000
end
@@ -57,7 +70,7 @@ def test_realex_purchase_with_invalid_login
assert_not_nil response
assert_failure response
- assert_equal '506', response.params['result']
+ assert_equal '504', response.params['result']
assert_match %r{no such}i, response.message
end
@@ -70,12 +83,18 @@ def test_realex_purchase_with_invalid_account
assert_not_nil response
assert_failure response
- assert_equal '506', response.params['result']
+ assert_equal '504', response.params['result']
assert_match %r{no such}i, response.message
end
- def test_realex_purchase_declined
+ def test_realex_purchase_with_apple_pay
+ response = @gateway.purchase(1000, @apple_pay, :order_id => generate_unique_id, :description => 'Test Realex with ApplePay')
+ assert_success response
+ assert response.test?
+ assert_equal 'Successful', response.message
+ end
+ def test_realex_purchase_declined
[ @visa_declined, @mastercard_declined ].each do |card|
response = @gateway.purchase(@amount, card,
@@ -91,6 +110,14 @@ def test_realex_purchase_declined
end
+ def test_realex_purchase_with_apple_pay_declined
+ response = @gateway.purchase(1101, @declined_apple_pay, :order_id => generate_unique_id, :description => 'Test Realex with ApplePay')
+ assert_failure response
+ assert response.test?
+ assert_equal '101', response.params['result']
+ assert_match %r{DECLINED}i, response.message
+ end
+
def test_realex_purchase_referral_b
[ @visa_referral_b, @mastercard_referral_b ].each do |card|
@@ -122,7 +149,6 @@ def test_realex_purchase_referral_a
end
def test_realex_purchase_coms_error
-
[ @visa_coms_error, @mastercard_coms_error ].each do |card|
response = @gateway.purchase(@amount, card,
@@ -178,8 +204,8 @@ def test_invalid_credit_card_name
assert_not_nil response
assert_failure response
- assert_equal '502', response.params['result']
- assert_match(/missing/i, response.message)
+ assert_equal '506', response.params['result']
+ assert_match(/does not conform/i, response.message)
end
def test_cvn
@@ -284,11 +310,28 @@ def test_realex_purchase_then_refund
assert_not_nil rebate_response
assert_success rebate_response
- assert rebate_response.test?
assert rebate_response.authorization.length > 0
assert_equal 'Successful', rebate_response.message
end
+ def test_maps_avs_and_cvv_response_codes
+ [ @visa, @mastercard ].each do |card|
+
+ response = @gateway.purchase(@amount, card,
+ :order_id => generate_unique_id,
+ :description => 'Test Realex Purchase',
+ :billing_address => {
+ :zip => '90210',
+ :country => 'US'
+ }
+ )
+ assert_not_nil response
+ assert_success response
+ assert_equal "M", response.avs_result["code"]
+ assert_equal "M", response.cvv_result["code"]
+ end
+ end
+
def test_transcript_scrubbing
transcript = capture_transcript(@gateway) do
@gateway.purchase(@amount, @visa_declined,
diff --git a/test/remote/gateways/remote_redsys_test.rb b/test/remote/gateways/remote_redsys_test.rb
index 7ecb83a1024..d05e234ae6a 100644
--- a/test/remote/gateways/remote_redsys_test.rb
+++ b/test/remote/gateways/remote_redsys_test.rb
@@ -9,6 +9,7 @@ def setup
order_id: generate_order_id,
description: 'Test Description'
}
+ @amount = 100
end
def test_successful_purchase
diff --git a/test/remote/gateways/remote_safe_charge_test.rb b/test/remote/gateways/remote_safe_charge_test.rb
new file mode 100644
index 00000000000..221f8ab13b7
--- /dev/null
+++ b/test/remote/gateways/remote_safe_charge_test.rb
@@ -0,0 +1,234 @@
+require 'test_helper'
+
+class RemoteSafeChargeTest < Test::Unit::TestCase
+ def setup
+ @gateway = SafeChargeGateway.new(fixtures(:safe_charge))
+
+ @amount = 100
+ @credit_card = credit_card('4000100011112224', verification_value: '912')
+ @declined_card = credit_card('4000300011112220')
+ @options = {
+ order_id: generate_unique_id,
+ billing_address: address,
+ description: 'Store Purchase',
+ currency: "EUR"
+ }
+
+ @three_ds_options = @options.merge(three_d_secure: true)
+ @three_ds_gateway = SafeChargeGateway.new(fixtures(:safe_charge_three_ds))
+ @three_ds_enrolled_card = credit_card('4012 0010 3749 0014')
+ @three_ds_non_enrolled_card = credit_card('5333 3062 3122 6927')
+ @three_ds_invalid_pa_res_card = credit_card('4012 0010 3749 0006')
+ end
+
+ def test_successful_3ds_purchase
+ response = @three_ds_gateway.purchase(@amount, @three_ds_enrolled_card, @three_ds_options)
+ assert_success response
+ assert !response.params["acsurl"].blank?
+ assert !response.params["pareq"].blank?
+ assert !response.params["xid"].blank?
+ assert_equal 'Success', response.message
+ end
+
+ def test_successful_regular_purchase_through_3ds_flow_with_non_enrolled_card
+ response = @three_ds_gateway.purchase(@amount, @three_ds_non_enrolled_card, @three_ds_options)
+ assert_success response
+ assert response.params["acsurl"].blank?
+ assert response.params["pareq"].blank?
+ assert response.params["xid"].blank?
+ assert response.params["threedflow"] = 1
+ assert_equal 'Success', response.message
+ end
+
+ def test_successful_regular_purchase_through_3ds_flow_with_invalid_pa_res
+ response = @three_ds_gateway.purchase(@amount, @three_ds_invalid_pa_res_card, @three_ds_options)
+ assert_success response
+ assert !response.params["acsurl"].blank?
+ assert !response.params["pareq"].blank?
+ assert !response.params["xid"].blank?
+ assert response.params["threedflow"] = 1
+ assert_equal 'Success', response.message
+ end
+
+ def test_successful_purchase
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'Success', response.message
+ end
+
+ def test_successful_purchase_with_more_options
+ options = {
+ order_id: '1',
+ ip: "127.0.0.1",
+ email: "joe@example.com",
+ user_id: '123',
+ auth_type: '2',
+ expected_fulfillment_count: '3',
+ merchant_descriptor: 'Test Descriptor',
+ merchant_phone_number: '(555)555-5555',
+ merchant_name: 'Test Merchant',
+ stored_credential_mode: true
+ }
+
+ response = @gateway.purchase(@amount, @credit_card, options)
+ assert_success response
+ assert_equal 'Success', response.message
+ end
+
+ def test_failed_purchase
+ response = @gateway.purchase(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal 'Decline', response.message
+ end
+
+ def test_successful_authorize_and_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount, auth.authorization)
+ assert_success capture
+ assert_equal 'Success', capture.message
+ end
+
+ def test_successful_authorize_and_capture_with_more_options
+ extra = {
+ order_id: '1',
+ ip: "127.0.0.1",
+ email: "joe@example.com",
+ user_id: '123',
+ auth_type: '2',
+ expected_fulfillment_count: '3',
+ merchant_descriptor: 'Test Descriptor',
+ merchant_phone_number: '(555)555-5555',
+ merchant_name: 'Test Merchant',
+ stored_credential_mode: true
+ }
+ auth = @gateway.authorize(@amount, @credit_card, extra)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount, auth.authorization)
+ assert_success capture
+ assert_equal 'Success', capture.message
+ end
+
+ def test_failed_authorize
+ response = @gateway.authorize(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal 'Decline', response.message
+ end
+
+ def test_partial_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount-1, auth.authorization)
+ assert_success capture
+ end
+
+ def test_failed_capture
+ response = @gateway.capture(@amount, '')
+ assert_failure response
+ assert_equal 'Transaction must contain a Card/Token/Account', response.message
+ end
+
+ def test_successful_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount, purchase.authorization)
+ assert_success refund
+ assert_equal 'Success', refund.message
+ end
+
+ def test_partial_refund
+ purchase = @gateway.purchase(200, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(100, purchase.authorization)
+ assert_success refund
+ end
+
+ def test_failed_refund
+ response = @gateway.refund(@amount, '')
+ assert_failure response
+ assert_equal 'Transaction must contain a Card/Token/Account', response.message
+ end
+
+ def test_successful_credit
+ response = @gateway.credit(@amount, credit_card('4444436501403986'), @options)
+ assert_success response
+ assert_equal 'Success', response.message
+ end
+
+ def test_successful_credit_with_extra_options
+ extra = {
+ order_id: '1',
+ ip: "127.0.0.1",
+ email: "joe@example.com",
+ user_id: '123',
+ auth_type: '2',
+ expected_fulfillment_count: '3',
+ merchant_descriptor: 'Test Descriptor',
+ merchant_phone_number: '(555)555-5555',
+ merchant_name: 'Test Merchant',
+ stored_credential_mode: true
+ }
+
+ response = @gateway.credit(@amount, credit_card('4444436501403986'), extra)
+ assert_success response
+ assert_equal 'Success', response.message
+ end
+
+ def test_failed_credit
+ response = @gateway.credit(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal 'Decline', response.message
+ end
+
+ def test_successful_void
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert void = @gateway.void(auth.authorization)
+ assert_success void
+ assert_equal 'Success', void.message
+ end
+
+ def test_failed_void
+ response = @gateway.void('')
+ assert_failure response
+ assert_equal 'Invalid Amount', response.message
+ end
+
+ def test_successful_verify
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ assert_match 'Success', response.message
+ end
+
+ def test_failed_verify
+ response = @gateway.verify(@declined_card, @options)
+ assert_failure response
+ assert_match 'Decline', response.message
+ end
+
+ def test_invalid_login
+ gateway = SafeChargeGateway.new(client_login_id: '', client_password: '')
+
+ response = gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_match 'Invalid login', response.message
+ end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:client_password], transcript)
+ end
+
+end
diff --git a/test/remote/gateways/remote_sage_pay_test.rb b/test/remote/gateways/remote_sage_pay_test.rb
index fb3e17c3cdd..7826d0074e4 100644
--- a/test/remote/gateways/remote_sage_pay_test.rb
+++ b/test/remote/gateways/remote_sage_pay_test.rb
@@ -124,6 +124,20 @@ def test_successful_authorization_and_capture
assert_success capture
end
+ def test_successful_authorization_and_capture_and_refund
+ assert auth = @gateway.authorize(@amount, @mastercard, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount, auth.authorization)
+ assert_success capture
+
+ assert refund = @gateway.refund(@amount, capture.authorization,
+ :description => 'Crediting trx',
+ :order_id => generate_unique_id
+ )
+ assert_success refund
+ end
+
def test_successful_authorization_and_void
assert auth = @gateway.authorize(@amount, @mastercard, @options)
assert_success auth
@@ -159,13 +173,6 @@ def test_successful_visa_purchase
assert !response.authorization.blank?
end
- def test_successful_maestro_purchase
- assert response = @gateway.purchase(@amount, @maestro, @options)
- assert_success response
- assert response.test?
- assert !response.authorization.blank?
- end
-
def test_successful_amex_purchase
assert response = @gateway.purchase(@amount, @amex, @options)
assert_success response
@@ -313,6 +320,13 @@ def test_successful_purchase_with_website
assert_success response
end
+ def test_successful_repeat_purchase
+ response = @gateway.purchase(@amount, @visa, @options)
+ assert_success response
+ repeat = @gateway.purchase(@amount, response.authorization, @options.merge(order_id: generate_unique_id))
+ assert_success repeat
+ end
+
def test_invalid_login
message = SagePayGateway.simulate ? 'VSP Simulator cannot find your vendor name. Ensure you have have supplied a Vendor field with your VSP Vendor name assigned to it.' : '3034 : The Vendor or VendorName value is required.'
@@ -337,7 +351,7 @@ def test_successful_store_and_repurchase_with_resupplied_verification_value
assert_success response
assert !response.authorization.blank?
assert purchase = @gateway.purchase(@amount, response.authorization, @options.merge(customer: 1))
- assert purchase = @gateway.purchase(@amount, response.authorization, @options.merge(verification_value: '123', order_id: 'foobar123'))
+ assert purchase = @gateway.purchase(@amount, response.authorization, @options.merge(verification_value: '123', order_id: generate_unique_id))
assert_success purchase
end
@@ -417,13 +431,13 @@ def basket_xml
# Example from http://www.sagepay.co.uk/support/customer-xml
def customer_xml
<<-XML
-
- W
- 1983-01-01
- 020 1234567
- 0799 1234567
- 0
- 10
+
+ W
+ 1983-01-01
+ 020 1234567
+ 0799 1234567
+ 0
+ 10
CUST123
XML
diff --git a/test/remote/gateways/remote_sage_test.rb b/test/remote/gateways/remote_sage_test.rb
index d196d4ada97..f68f0767829 100644
--- a/test/remote/gateways/remote_sage_test.rb
+++ b/test/remote/gateways/remote_sage_test.rb
@@ -80,6 +80,13 @@ def test_successful_with_minimal_options
assert_false response.authorization.blank?
end
+ def test_successful_purchase_with_blank_state
+ assert response = @gateway.purchase(@amount, @visa, billing_address: address(state: ""))
+ assert_success response
+ assert response.test?
+ assert_false response.authorization.blank?
+ end
+
def test_authorization_and_capture
assert auth = @gateway.authorize(@amount, @visa, @options)
assert_success auth
@@ -214,4 +221,15 @@ def test_transcript_scrubbing_store
assert_scrubbed(@gateway.options[:password], transcript)
end
+ def test_echeck_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @check, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@check.account_number, transcript)
+ assert_scrubbed(@check.routing_number, transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
+
end
diff --git a/test/remote/gateways/remote_secure_net_test.rb b/test/remote/gateways/remote_secure_net_test.rb
index 424f16f44e6..784c7d42d4d 100644
--- a/test/remote/gateways/remote_secure_net_test.rb
+++ b/test/remote/gateways/remote_secure_net_test.rb
@@ -125,4 +125,15 @@ def test_invoice_description_and_number
assert_equal 'Approved', capture.message
end
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
+
end
diff --git a/test/remote/gateways/remote_secure_pay_au_test.rb b/test/remote/gateways/remote_secure_pay_au_test.rb
index cccab539e40..5f340c19f7c 100644
--- a/test/remote/gateways/remote_secure_pay_au_test.rb
+++ b/test/remote/gateways/remote_secure_pay_au_test.rb
@@ -183,4 +183,15 @@ def test_invalid_login
assert_failure response
assert_equal "Invalid merchant ID", response.message
end
+
+ def test_purchase_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(credit_card.number, transcript)
+ assert_scrubbed(@gateway.options[:login], transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
end
diff --git a/test/remote/gateways/remote_spreedly_core_test.rb b/test/remote/gateways/remote_spreedly_core_test.rb
index 3a3bfb8e9a1..c9ea36a7d2c 100644
--- a/test/remote/gateways/remote_spreedly_core_test.rb
+++ b/test/remote/gateways/remote_spreedly_core_test.rb
@@ -8,8 +8,10 @@ def setup
@amount = 100
@credit_card = credit_card('5555555555554444')
@declined_card = credit_card('4012888888881881')
- @existing_payment_method = '9AjLflWs7SOKuqJLveOZya9bixa'
- @declined_payment_method = 'n3JElNt9joT1mJ3CxvWjyEN39N'
+ @existing_payment_method = '3rEkRlZur2hXKbwwRBidHJAIUTO'
+ @declined_payment_method = 'UPfh3J3JbekLeYC88BP741JWnS5'
+ @existing_transaction = 'PJ5ICgM6h7v9pBNxDCJjRHDDxBC'
+ @not_found_transaction = 'AdyQXaG0SVpSoMPdmFlvd3aA3uz'
end
def test_successful_purchase_with_token
@@ -21,7 +23,7 @@ def test_successful_purchase_with_token
def test_failed_purchase_with_token
assert response = @gateway.purchase(@amount, @declined_payment_method)
assert_failure response
- assert_match %r(Unable to process the transaction), response.message
+ assert_match %r(Unable to process the purchase transaction), response.message
end
def test_successful_authorize_with_token_and_capture
@@ -38,7 +40,7 @@ def test_successful_authorize_with_token_and_capture
def test_failed_authorize_with_token
assert response = @gateway.authorize(@amount, @declined_payment_method)
assert_failure response
- assert_match %r(Unable to process the transaction), response.message
+ assert_match %r(Unable to process the authorize transaction), response.message
end
def test_failed_capture
@@ -55,7 +57,7 @@ def test_successful_purchase_with_credit_card
assert_success response
assert_equal 'Succeeded!', response.message
assert_equal 'Purchase', response.params['transaction_type']
- assert_equal 'used', response.params['payment_method_storage_state']
+ assert_equal 'cached', response.params['payment_method_storage_state']
end
def test_successful_purchase_with_card_and_address
@@ -69,7 +71,7 @@ def test_successful_purchase_with_card_and_address
assert_equal 'Succeeded!', response.message
assert_equal 'joebob@example.com', response.params['payment_method_email']
- assert_equal '1234 My Street', response.params['payment_method_address1']
+ assert_equal '456 My Street', response.params['payment_method_address1']
assert_equal 'Apt 1', response.params['payment_method_address2']
assert_equal 'Ottawa', response.params['payment_method_city']
assert_equal 'ON', response.params['payment_method_state']
@@ -122,7 +124,7 @@ def test_successful_authorize_with_card_and_address
assert_equal 'Authorization', response.params['transaction_type']
assert_equal 'joebob@example.com', response.params['payment_method_email']
- assert_equal '1234 My Street', response.params['payment_method_address1']
+ assert_equal '456 My Street', response.params['payment_method_address1']
assert_equal 'Apt 1', response.params['payment_method_address2']
assert_equal 'Ottawa', response.params['payment_method_city']
assert_equal 'ON', response.params['payment_method_state']
@@ -188,7 +190,7 @@ def test_successful_store_with_address
assert response = @gateway.store(@credit_card, options)
assert_success response
assert_equal 'joebob@example.com', response.params['payment_method_email']
- assert_equal '1234 My Street', response.params['payment_method_address1']
+ assert_equal '456 My Street', response.params['payment_method_address1']
assert_equal 'Apt 1', response.params['payment_method_address2']
assert_equal 'Ottawa', response.params['payment_method_city']
assert_equal 'ON', response.params['payment_method_state']
@@ -238,6 +240,46 @@ def test_successful_void
assert_equal 'Succeeded!', response.message
end
+ def test_successful_verify_with_token
+ assert response = @gateway.verify(@existing_payment_method)
+ assert_success response
+ assert_equal 'Succeeded!', response.message
+ assert_equal 'Verification', response.params['transaction_type']
+ assert_includes %w(cached retained), response.params['payment_method_storage_state']
+ end
+
+ def test_failed_verify_with_token
+ assert response = @gateway.verify(@declined_payment_method)
+ assert_failure response
+ end
+
+ def test_successful_verify_with_credit_card
+ assert response = @gateway.verify(@credit_card)
+ assert_success response
+ assert_equal 'Succeeded!', response.message
+ assert_equal 'Verification', response.params['transaction_type']
+ assert_includes %w(cached retained), response.params['payment_method_storage_state']
+ end
+
+ def test_failed_verify_with_declined_credit_card
+ assert response = @gateway.verify(@declined_card)
+ assert_failure response
+ assert_match %r(Unable to process the verify transaction), response.message
+ end
+
+ def test_successful_find_transaction
+ assert response = @gateway.find(@existing_transaction)
+ assert_success response
+ assert_equal 'Succeeded!', response.message
+ assert_equal 'Purchase', response.params['transaction_type']
+ end
+
+ def test_failed_find_transaction
+ assert response = @gateway.find(@not_found_transaction)
+ assert_failure response
+ assert_match %r(Unable to find the transaction), response.message
+ end
+
def test_invalid_login
gateway = SpreedlyCoreGateway.new(:login => 'Bogus', :password => 'MoreBogus', :gateway_token => 'EvenMoreBogus')
@@ -245,4 +287,27 @@ def test_invalid_login
assert_failure response
assert_match %r{Unable to authenticate}, response.message
end
+
+ def test_scrubbing_purchase
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:login], transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
+
+ def test_scrubbing_purchase_with_token
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @existing_payment_method)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@existing_payment_method, transcript)
+ assert_scrubbed(@gateway.options[:login], transcript)
+ assert_scrubbed(@gateway.options[:password], transcript)
+ end
end
diff --git a/test/remote/gateways/remote_stripe_connect_test.rb b/test/remote/gateways/remote_stripe_connect_test.rb
index c5bbd77d339..2117eb43b92 100644
--- a/test/remote/gateways/remote_stripe_connect_test.rb
+++ b/test/remote/gateways/remote_stripe_connect_test.rb
@@ -12,31 +12,49 @@ def setup
@options = {
:currency => "USD",
:description => 'ActiveMerchant Test Purchase',
- :email => 'wow@example.com'
+ :email => 'wow@example.com',
+ :stripe_account => fixtures(:stripe_destination)[:stripe_user_id]
}
end
def test_application_fee_for_stripe_connect
assert response = @gateway.purchase(@amount, @credit_card, @options.merge(:application_fee => 12 ))
- assert response.params['fee_details'], 'This test will only work if your gateway login is a Stripe Connect access_token.'
- assert response.params['fee_details'].any? do |fee|
- (fee['type'] == 'application_fee') && (fee['amount'] == 12)
- end
+ assert_success response
end
def test_successful_refund_with_application_fee
assert response = @gateway.purchase(@amount, @credit_card, @options.merge(:application_fee => 12))
- assert response.params['fee_details'], 'This test will only work if your gateway login is a Stripe Connect access_token.'
- assert refund = @gateway.refund(@amount, response.authorization, :refund_application_fee => true)
+ assert refund = @gateway.refund(@amount, response.authorization, @options.merge(:refund_application_fee => true))
assert_success refund
- assert_equal 0, refund.params["fee"]
+
+ # Verify the application fee is refunded
+ fetch_fee_id = @gateway.send(:fetch_application_fee, response.authorization, @options)
+ fee_id = @gateway.send(:application_fee_from_response, fetch_fee_id)
+ refund_check = @gateway.send(:refund_application_fee, 10, fee_id, @options)
+ assert_equal "Application fee could not be refunded: Refund amount ($0.10) is greater than unrefunded amount on fee ($0.00)", refund_check.message
end
def test_refund_partial_application_fee
assert response = @gateway.purchase(@amount, @credit_card, @options.merge(:application_fee => 12))
- assert response.params['fee_details'], 'This test will only work if your gateway login is a Stripe Connect access_token.'
- assert refund = @gateway.refund(@amount - 20, response.authorization, { :refund_fee_amount => 10 })
+ assert refund = @gateway.refund(@amount-20, response.authorization, @options.merge(:refund_fee_amount => "10"))
assert_success refund
+
+ # Verify the application fee is partially refunded
+ fetch_fee_id = @gateway.send(:fetch_application_fee, response.authorization, @options)
+ fee_id = @gateway.send(:application_fee_from_response, fetch_fee_id)
+ refund_check = @gateway.send(:refund_application_fee, 10, fee_id, @options)
+ assert_equal "Application fee could not be refunded: Refund amount ($0.10) is greater than unrefunded amount on fee ($0.02)", refund_check.message
end
+ def test_refund_application_fee_amount_zero
+ assert response = @gateway.purchase(@amount, @credit_card, @options.merge(:application_fee => 12))
+ assert refund = @gateway.refund(@amount-20, response.authorization, @options.merge(:refund_fee_amount => "0"))
+ assert_success refund
+
+ # Verify the application fee is not refunded
+ fetch_fee_id = @gateway.send(:fetch_application_fee, response.authorization, @options)
+ fee_id = @gateway.send(:application_fee_from_response, fetch_fee_id)
+ refund_check = @gateway.send(:refund_application_fee, 14, fee_id, @options)
+ assert_equal "Application fee could not be refunded: Refund amount ($0.14) is greater than fee amount ($0.12)", refund_check.message
+ end
end
diff --git a/test/remote/gateways/remote_stripe_emv_test.rb b/test/remote/gateways/remote_stripe_emv_test.rb
index 076d504ff1c..1db4b4f53af 100644
--- a/test/remote/gateways/remote_stripe_emv_test.rb
+++ b/test/remote/gateways/remote_stripe_emv_test.rb
@@ -1,14 +1,16 @@
require 'test_helper'
class RemoteStripeEmvTest < Test::Unit::TestCase
+ CHARGE_ID_REGEX = /ch_[a-zA-Z\d]{24}/
+
def setup
@gateway = StripeGateway.new(fixtures(:stripe))
@amount = 100
@emv_credit_cards = {
- uk: ActiveMerchant::Billing::CreditCard.new(icc_data: '500B56495341204352454449545F201A56495341204143515549524552205445535420434152442030315F24031512315F280208405F2A0208265F300202015F34010182025C008407A0000000031010950502000080009A031408259B02E8009C01009F02060000000734499F03060000000000009F0607A00000000310109F0902008C9F100706010A03A080009F120F4352454449544F20444520564953419F1A0208269F1C0831373030303437309F1E0831373030303437309F2608EB2EC0F472BEA0A49F2701809F3303E0B8C89F34031E03009F3501229F360200C39F37040A27296F9F4104000001319F4502DAC5DFAE5711476173FFFFFF0119D15122011758989389DFAE5A08476173FFFFFF011957114761739001010119D151220117589893895A084761739001010119'),
- us: ActiveMerchant::Billing::CreditCard.new(icc_data: '50074D41455354524F571167999989000018123D25122200835506065A0967999989000018123F5F20134D54495032362D204D41455354524F203132415F24032512315F280200565F2A0208405F300202205F340101820278008407A0000000043060950500000080009A031504219B02E8009C01009F02060000000010009F03060000000000009F0607A00000000430609F090200029F10120210A7800F040000000000000000000000FF9F12074D61657374726F9F1A0208409F1C0831303030333331369F1E0831303030333331369F2608460245B808BCA1369F2701809F3303E0B8C89F34034403029F3501229F360200279F3704EA2C3A7A9F410400000094DF280104DFAE5711679999FFFFFFF8123D2512220083550606DFAE5A09679999FFFFFFF8123F'),
- contactless: ActiveMerchant::Billing::CreditCard.new(icc_data: '500D5649534120454C454354524F4E5F20175649534120434445542032312F434152443035202020205F2A0208405F340111820200008407A00000000320109A031505119C01009F02060000000006959F0607A00000000320109F090200019F100706011103A000009F1A0200569F1C0831323334353637389F1E0831303030333236389F260852A5A96394EDA96D9F2701809F3303E0B8C89F3501229F360200069F3704A4428D7A9F410400000289DF280100DF30020301DFAE021885D6E511F8844CEA0DC72883180AC081AF4593A8A3C5FDD8DFAE030AFFFF0102628D1100005EDFAE5712476173FFFFFFFFF2234D151220114524040FDFAE021892FC2C940487F43AC64AB3DFD54C7B72F445FE409D80FDF5DFAE030AFFFF0102628D1100005F')
+ uk: ActiveMerchant::Billing::CreditCard.new(icc_data: '5A08476173900101011957114761739001010119D221220117589288899F131F3137353839383930303238383030303030304F07A0000000031010500B564953412043524544495482025C008407A00000000310108A025A318E0E00000000000000001E0302031F00950542000080009A031707259B02E8009C01005F24032212315F25030907015F2A0208265F3401019F01060000000000019F02060000000001009F03060000000000009F0607A00000000310109F0702FF009F0902008C9F0D05F0400088009F0E0500100000009F0F05F0400098009F100706010A03A000009F120F4352454449544F20444520564953419F160F3132333435363738393031323334359F1A0208269F1C0831313232333334349F1E0831323334353637389F21031137269F26084A3000C111F061539F2701809F33036028C89F34031E03009F3501219F360200029F370467D5DD109F3901059F40057E0000A0019F4104000001979F4E0D54657374204D65726368616E749F110101DF834F0F434842313136373235303030343439DF83620100'),
+ us: ActiveMerchant::Billing::CreditCard.new(icc_data: '5A08476173900101011957114761739001010119D221220117589288899F131F3137353839383930303238383030303030304F07A0000000031010500B564953412043524544495482025C008407A00000000310108A025A318E0E00000000000000001E0302031F00950542000080009A031707259B02E8009C01005F24032212315F25030907015F2A0208405F3401019F01060000000000019F02060000000001009F03060000000000009F0607A00000000310109F0702FF009F0902008C9F0D05F0400088009F0E0500100000009F0F05F0400098009F100706010A03A000009F120F4352454449544F20444520564953419F160F3132333435363738393031323334359F1A0208409F1C0831313232333334349F1E0831323334353637389F21031137269F26084A3000C111F061539F2701809F33036028C89F34031E03009F3501219F360200029F370467D5DD109F3901059F40057E0000A0019F4104000001979F4E0D54657374204D65726368616E749F110101DF834F0F434842313136373235303030343439DF83620100'),
+ contactless: ActiveMerchant::Billing::CreditCard.new(icc_data: '5A08476173900101011957114761739001010119D22122011758928889500D5649534120454C454354524F4E5F20175649534120434445542032312F434152443035202020205F2A0208405F340111820200008407A00000000320109A031505119C01009F02060000000006009F0607A00000000320109F090200019F100706011103A000009F1A0200569F1C0831323334353637389F1E0831303030333236389F260852A5A96394EDA96D9F2701809F3303E0B8C89F3501229F360200069F3704A4428D7A9F410400000289')
}
@options = {
@@ -16,13 +18,17 @@ def setup
:description => 'ActiveMerchant Test Purchase',
:email => 'wow@example.com'
}
+
+ # This capture hex says that the payload is a transaction cryptogram (TC) but does not
+ # provide the actual cryptogram. This will only work in test mode and would cause real
+ # cards to be declined.
+ @capture_options = { icc_data: "9F270140" }
end
# for EMV contact transactions, it's advised to do a separate auth + capture
# to satisfy the EMV chip's transaction flow, but this works as a legal
# API call. You shouldn't use it in a real EMV implementation, though.
def test_successful_purchase_with_emv_credit_card_in_uk
- @gateway = StripeGateway.new(fixtures(:stripe_emv_uk))
assert response = @gateway.purchase(@amount, @emv_credit_cards[:uk], @options)
assert_success response
assert_equal "charge", response.params["object"]
@@ -33,7 +39,6 @@ def test_successful_purchase_with_emv_credit_card_in_uk
# perform separate auth & capture rather than a purchase in practice for the
# reasons mentioned above.
def test_successful_purchase_with_emv_credit_card_in_us
- @gateway = StripeGateway.new(fixtures(:stripe_emv_us))
assert response = @gateway.purchase(@amount, @emv_credit_cards[:us], @options)
assert_success response
assert_equal "charge", response.params["object"]
@@ -41,12 +46,22 @@ def test_successful_purchase_with_emv_credit_card_in_us
assert_match CHARGE_ID_REGEX, response.authorization
end
+ def test_successful_purchase_with_quickchip_credit_card_in_us
+ credit_card = @emv_credit_cards[:us]
+ credit_card.read_method = 'contact_quickchip'
+ assert response = @gateway.purchase(@amount, credit_card, @options)
+ assert_success response
+ assert_equal "charge", response.params["object"]
+ assert response.params["captured"]
+ assert response.params["paid"]
+ assert_match CHARGE_ID_REGEX, response.authorization
+ end
+
# For EMV contactless transactions, generally a purchase is preferred since
# a TC is typically generated at the point of sale.
def test_successful_purchase_with_emv_contactless_credit_card
- @gateway = StripeGateway.new(fixtures(:stripe_emv_us))
emv_credit_card = @emv_credit_cards[:contactless]
- emv_credit_card.contactless = true
+ emv_credit_card.read_method = 'contactless'
assert response = @gateway.purchase(@amount, emv_credit_card, @options)
assert_success response
assert_equal "charge", response.params["object"]
@@ -55,31 +70,28 @@ def test_successful_purchase_with_emv_contactless_credit_card
end
def test_authorization_and_capture_with_emv_credit_card_in_uk
- @gateway = StripeGateway.new(fixtures(:stripe_emv_uk))
assert authorization = @gateway.authorize(@amount, @emv_credit_cards[:uk], @options)
assert_success authorization
assert authorization.emv_authorization, "Authorization should contain emv_authorization containing the EMV ARPC"
refute authorization.params["captured"]
- assert capture = @gateway.capture(@amount, authorization.authorization)
+ assert capture = @gateway.capture(@amount, authorization.authorization, @capture_options)
assert_success capture
assert capture.emv_authorization, "Capture should contain emv_authorization containing the EMV TC"
end
def test_authorization_and_capture_with_emv_credit_card_in_us
- @gateway = StripeGateway.new(fixtures(:stripe_emv_us))
assert authorization = @gateway.authorize(@amount, @emv_credit_cards[:us], @options)
assert_success authorization
assert authorization.emv_authorization, "Authorization should contain emv_authorization containing the EMV ARPC"
refute authorization.params["captured"]
- assert capture = @gateway.capture(@amount, authorization.authorization)
+ assert capture = @gateway.capture(@amount, authorization.authorization, @capture_options)
assert_success capture
assert capture.emv_authorization, "Capture should contain emv_authorization containing the EMV TC"
end
def test_authorization_and_capture_of_online_pin_with_emv_credit_card_in_us
- @gateway = StripeGateway.new(fixtures(:stripe_emv_us))
emv_credit_card = @emv_credit_cards[:us]
emv_credit_card.encrypted_pin_cryptogram = "8b68af72199529b8"
emv_credit_card.encrypted_pin_ksn = "ffff0102628d12000001"
@@ -89,27 +101,12 @@ def test_authorization_and_capture_of_online_pin_with_emv_credit_card_in_us
assert authorization.emv_authorization, "Authorization should contain emv_authorization containing the EMV ARPC"
refute authorization.params["captured"]
- assert capture = @gateway.capture(@amount, authorization.authorization)
- assert_success capture
- assert capture.emv_authorization, "Capture should contain emv_authorization containing the EMV TC"
- end
-
- def test_authorization_and_capture_with_emv_contactless_credit_card
- @gateway = StripeGateway.new(fixtures(:stripe_emv_us))
- emv_credit_card = @emv_credit_cards[:contactless]
- emv_credit_card.contactless = true
- assert authorization = @gateway.authorize(@amount, emv_credit_card, @options)
- assert_success authorization
- assert authorization.emv_authorization, "Authorization should contain emv_authorization containing the EMV ARPC"
- refute authorization.params["captured"]
-
- assert capture = @gateway.capture(@amount, authorization.authorization)
+ assert capture = @gateway.capture(@amount, authorization.authorization, @capture_options)
assert_success capture
assert capture.emv_authorization, "Capture should contain emv_authorization containing the EMV TC"
end
def test_authorization_and_void_with_emv_credit_card_in_us
- @gateway = StripeGateway.new(fixtures(:stripe_emv_us))
assert authorization = @gateway.authorize(@amount, @emv_credit_cards[:us], @options)
assert_success authorization
assert authorization.emv_authorization, "Authorization should contain emv_authorization containing the EMV ARPC"
@@ -120,7 +117,6 @@ def test_authorization_and_void_with_emv_credit_card_in_us
end
def test_authorization_and_void_with_emv_credit_card_in_uk
- @gateway = StripeGateway.new(fixtures(:stripe_emv_uk))
assert authorization = @gateway.authorize(@amount, @emv_credit_cards[:uk], @options)
assert_success authorization
assert authorization.emv_authorization, "Authorization should contain emv_authorization containing the EMV ARPC"
@@ -130,21 +126,20 @@ def test_authorization_and_void_with_emv_credit_card_in_uk
assert_success void
end
- def test_authorization_and_void_with_emv_contactless_credit_card
- @gateway = StripeGateway.new(fixtures(:stripe_emv_us))
+ def test_purchase_and_void_with_emv_contactless_credit_card
emv_credit_card = @emv_credit_cards[:contactless]
- emv_credit_card.contactless = true
- assert authorization = @gateway.authorize(@amount, emv_credit_card, @options)
- assert_success authorization
- assert authorization.emv_authorization, "Authorization should contain emv_authorization containing the EMV ARPC"
- refute authorization.params["captured"]
-
- assert void = @gateway.void(authorization.authorization)
+ emv_credit_card.read_method = 'contactless'
+ assert purchase = @gateway.purchase(@amount, emv_credit_card, @options)
+ assert_success purchase
+ assert purchase.emv_authorization, "Authorization should contain emv_authorization containing the EMV ARPC"
+ assert purchase.params["captured"]
+ assert purchase.params["paid"]
+
+ assert void = @gateway.void(purchase.authorization)
assert_success void
end
def test_authorization_emv_credit_card_in_us_with_metadata
- @gateway = StripeGateway.new(fixtures(:stripe_emv_us))
assert authorization = @gateway.authorize(@amount, @emv_credit_cards[:us], @options.merge({:metadata => {:this_is_a_random_key_name => 'with a random value', :i_made_up_this_key_too => 'canyoutell'}, :order_id => "42", :email => "foo@wonderfullyfakedomain.com"}))
assert_success authorization
end
diff --git a/test/remote/gateways/remote_stripe_payment_intents_test.rb b/test/remote/gateways/remote_stripe_payment_intents_test.rb
new file mode 100644
index 00000000000..5b578b05a77
--- /dev/null
+++ b/test/remote/gateways/remote_stripe_payment_intents_test.rb
@@ -0,0 +1,351 @@
+require 'test_helper'
+
+class RemoteStripeIntentsTest < Test::Unit::TestCase
+ def setup
+ @gateway = StripePaymentIntentsGateway.new(fixtures(:stripe))
+ @customer = fixtures(:stripe)[:customer_id]
+ @payment_method_token = 'pm_card_threeDSecure2Required'
+ @visa_token = 'pm_card_visa'
+ @three_ds_credit_card = credit_card('4000000000003220',
+ verification_value: '737',
+ month: 10,
+ year: 2020
+ )
+ @destination_account = fixtures(:stripe_destination)[:stripe_user_id]
+ end
+
+ def test_create_payment_intent_manual_capture_method
+ amount = 100
+ options = {
+ currency: 'USD',
+ capture_method: 'manual'
+ }
+
+ assert response = @gateway.create_intent(amount, nil, options)
+
+ assert_success response
+ assert_equal 'payment_intent', response.params['object']
+ assert_equal 'manual', response.params['capture_method']
+ end
+
+ def test_create_payment_intent_manual_confimation_method
+ amount = 100
+ options = {
+ currency: 'USD',
+ description: 'ActiveMerchant Test Purchase',
+ confirmation_method: 'manual'
+ }
+
+ assert response = @gateway.create_intent(amount, nil, options)
+
+ assert_success response
+ assert_equal 'payment_intent', response.params['object']
+ assert_equal 'manual', response.params['confirmation_method']
+ end
+
+ def test_create_payment_intent_with_customer
+ amount = 100
+ options = {
+ currency: 'USD',
+ customer: @customer || 'set customer in fixtures'
+ }
+
+ assert response = @gateway.create_intent(amount, nil, options)
+
+ assert_success response
+ assert_equal 'payment_intent', response.params['object']
+ assert_equal @customer, response.params['customer']
+ end
+
+ def test_create_payment_intent_with_credit_card
+ amount = 100
+ options = {
+ currency: 'USD',
+ customer: @customer,
+ }
+
+ assert response = @gateway.create_intent(amount, @three_ds_credit_card, options)
+
+ assert_success response
+ assert_equal 'payment_intent', response.params['object']
+ end
+
+ def test_create_payment_intent_with_return_url
+ amount = 100
+ options = {
+ currency: 'USD',
+ customer: @customer,
+ confirm: true,
+ return_url: 'https://www.example.com'
+ }
+
+ assert response = @gateway.create_intent(amount, @three_ds_credit_card, options)
+
+ assert_success response
+ assert_equal 'https://www.example.com', response.params['next_action']['redirect_to_url']['return_url']
+ end
+
+ def test_create_payment_intent_with_metadata
+ amount = 100
+ options = {
+ currency: 'USD',
+ customer: @customer,
+ description: 'ActiveMerchant Test Purchase',
+ receipt_email: 'test@example.com',
+ statement_descriptor: 'Statement Descriptor',
+ metadata: { key_1: 'value_1', key_2: 'value_2' }
+ }
+
+ assert response = @gateway.create_intent(amount, nil, options)
+
+ assert_success response
+ assert_equal 'value_1', response.params['metadata']['key_1']
+ assert_equal 'ActiveMerchant Test Purchase', response.params['description']
+ assert_equal 'test@example.com', response.params['receipt_email']
+ assert_equal 'Statement Descriptor', response.params['statement_descriptor']
+ end
+
+ def test_create_payment_intent_that_saves_payment_method
+ amount = 100
+ options = {
+ currency: 'USD',
+ customer: @customer,
+ save_payment_method: true
+ }
+
+ assert response = @gateway.create_intent(amount, @three_ds_credit_card, options)
+ assert_success response
+
+ assert response = @gateway.create_intent(amount, nil, options)
+ assert_failure response
+ assert_equal 'A payment method must be provided or already '\
+ 'attached to the PaymentIntent when `save_payment_method=true`.', response.message
+
+ options.delete(:customer)
+ assert response = @gateway.create_intent(amount, @three_ds_credit_card, options)
+ assert_failure response
+ assert_equal 'A valid `customer` must be provided when `save_payment_method=true`.', response.message
+ end
+
+ def test_create_payment_intent_with_setup_future_usage
+ amount = 100
+ options = {
+ currency: 'USD',
+ customer: @customer,
+ setup_future_usage: 'on_session'
+ }
+
+ assert response = @gateway.create_intent(amount, @three_ds_credit_card, options)
+ assert_success response
+ assert_equal 'on_session', response.params['setup_future_usage']
+ end
+
+ def test_3ds_unauthenticated_authorize_with_off_session
+ options = {
+ currency: 'USD',
+ customer: @customer,
+ off_session: true,
+ }
+
+ assert response = @gateway.authorize(@amount, @three_ds_credit_card, options)
+ assert_failure response
+ end
+
+ def test_create_payment_intent_with_shipping_address
+ amount = 100
+ options = {
+ currency: 'USD',
+ customer: @customer,
+ shipping: {
+ address: {
+ line1: '1 Test Ln',
+ city: 'Durham'
+ },
+ name: 'John Doe',
+ tracking_number: '123456789'
+ }
+ }
+
+ assert response = @gateway.create_intent(amount, nil, options)
+ assert_success response
+ assert response.params['shipping']['address']
+ assert_equal 'John Doe', response.params['shipping']['name']
+ end
+
+ def test_create_payment_intent_with_connected_account
+ amount = 2020
+ options = {
+ currency: 'USD',
+ customer: @customer,
+ application_fee: 100,
+ transfer_data: {destination: @destination_account}
+ }
+
+ assert response = @gateway.create_intent(amount, nil, options)
+
+ assert_success response
+ assert_equal 100, response.params['application_fee_amount']
+ assert_equal @destination_account, response.params.dig('transfer_data', 'destination')
+ end
+
+ def test_create_a_payment_intent_and_confirm
+ amount = 2020
+ options = {
+ currency: 'GBP',
+ customer: @customer,
+ return_url: 'https://www.example.com',
+ confirmation_method: 'manual',
+ capture_method: 'manual',
+ }
+ assert create_response = @gateway.create_intent(amount, @payment_method_token, options)
+ assert_equal 'requires_confirmation', create_response.params['status']
+ intent_id = create_response.params['id']
+
+ assert get_response = @gateway.show_intent(intent_id, options)
+ assert_equal 'requires_confirmation', get_response.params['status']
+
+ assert confirm_response = @gateway.confirm_intent(intent_id, nil, return_url: 'https://example.com/return-to-me')
+ assert_equal 'redirect_to_url', confirm_response.params.dig('next_action', 'type')
+ end
+
+ def test_create_a_payment_intent_and_manually_capture
+ amount = 2020
+ options = {
+ currency: 'GBP',
+ customer: @customer,
+ confirmation_method: 'manual',
+ capture_method: 'manual',
+ confirm: true
+ }
+ assert create_response = @gateway.create_intent(amount, @visa_token, options)
+ intent_id = create_response.params['id']
+ assert_equal 'requires_capture', create_response.params['status']
+
+ assert capture_response = @gateway.capture(amount, intent_id, options)
+ assert_equal 'succeeded', capture_response.params['status']
+ assert_equal 'Payment complete.', capture_response.params.dig('charges', 'data')[0].dig('outcome', 'seller_message')
+ end
+
+ def test_create_a_payment_intent_and_automatically_capture
+ amount = 2020
+ options = {
+ currency: 'GBP',
+ customer: @customer,
+ confirmation_method: 'manual',
+ confirm: true
+ }
+ assert create_response = @gateway.create_intent(amount, @visa_token, options)
+ assert_nil create_response.params['next_action']
+ assert_equal 'succeeded', create_response.params['status']
+ assert_equal 'Payment complete.', create_response.params.dig('charges', 'data')[0].dig('outcome', 'seller_message')
+ end
+
+ def test_failed_capture_after_creation
+ amount = 2020
+ options = {
+ currency: 'GBP',
+ customer: @customer,
+ confirmation_method: 'manual',
+ confirm: true
+ }
+ assert create_response = @gateway.create_intent(amount, 'pm_card_chargeDeclined', options)
+ assert_equal 'requires_payment_method', create_response.params.dig('error', 'payment_intent', 'status')
+ assert_equal false, create_response.params.dig('error', 'payment_intent', 'charges', 'data')[0].dig('captured')
+ end
+
+ def test_create_a_payment_intent_and_update
+ amount = 2020
+ update_amount = 2050
+ options = {
+ currency: 'GBP',
+ customer: @customer,
+ confirmation_method: 'manual',
+ capture_method: 'manual',
+ }
+ assert create_response = @gateway.create_intent(amount, @visa_token, options)
+ intent_id = create_response.params['id']
+ assert_equal amount, create_response.params['amount']
+
+ assert update_response = @gateway.update_intent(update_amount, intent_id, nil, options.merge(payment_method_types: 'card'))
+ assert_equal update_amount, update_response.params['amount']
+ assert_equal 'requires_confirmation', update_response.params['status']
+ end
+
+ def test_create_a_payment_intent_and_void
+ amount = 2020
+ options = {
+ currency: 'GBP',
+ customer: @customer,
+ confirmation_method: 'manual',
+ capture_method: 'manual',
+ confirm: true
+ }
+ assert create_response = @gateway.create_intent(amount, @visa_token, options)
+ intent_id = create_response.params['id']
+
+ assert cancel_response = @gateway.void(intent_id, cancellation_reason: 'requested_by_customer')
+ assert_equal amount, cancel_response.params.dig('charges', 'data')[0].dig('amount_refunded')
+ assert_equal 'canceled', cancel_response.params['status']
+ assert_equal 'requested_by_customer', cancel_response.params['cancellation_reason']
+ end
+
+ def test_failed_void_after_capture
+ amount = 2020
+ options = {
+ currency: 'GBP',
+ customer: @customer,
+ confirmation_method: 'manual',
+ confirm: true
+ }
+ assert create_response = @gateway.create_intent(amount, @visa_token, options)
+ assert_equal 'succeeded', create_response.params['status']
+ intent_id = create_response.params['id']
+
+ assert cancel_response = @gateway.void(intent_id, cancellation_reason: 'requested_by_customer')
+ assert_equal 'You cannot cancel this PaymentIntent because ' \
+ 'it has a status of succeeded. Only a PaymentIntent with ' \
+ 'one of the following statuses may be canceled: ' \
+ 'requires_payment_method, requires_capture, requires_confirmation, requires_action.', cancel_response.message
+ end
+
+ def test_refund_a_payment_intent
+ amount = 2020
+ options = {
+ currency: 'GBP',
+ customer: @customer,
+ confirmation_method: 'manual',
+ capture_method: 'manual',
+ confirm: true
+ }
+ assert create_response = @gateway.create_intent(amount, @visa_token, options)
+ intent_id = create_response.params['id']
+
+ assert capture_response = @gateway.capture(amount, intent_id, options)
+
+ assert refund = @gateway.refund(amount - 20, intent_id)
+ assert_equal amount - 20, refund.params['charge']['amount_refunded']
+ assert_equal true, refund.params['charge']['captured']
+ refund_id = refund.params['id']
+ assert_equal refund.authorization, refund_id
+ end
+
+ def test_transcript_scrubbing
+ amount = 2020
+ options = {
+ currency: 'GBP',
+ customer: @customer,
+ confirmation_method: 'manual',
+ capture_method: 'manual',
+ return_url: 'https://www.example.com/return',
+ confirm: true
+ }
+ transcript = capture_transcript(@gateway) do
+ @gateway.create_intent(amount, @three_ds_credit_card, options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@three_ds_credit_card.number, transcript)
+ assert_scrubbed(@three_ds_credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:login], transcript)
+ end
+end
diff --git a/test/remote/gateways/remote_stripe_test.rb b/test/remote/gateways/remote_stripe_test.rb
index 045f7ef8223..b93cc05b795 100644
--- a/test/remote/gateways/remote_stripe_test.rb
+++ b/test/remote/gateways/remote_stripe_test.rb
@@ -66,6 +66,30 @@ def test_successful_purchase_with_recurring_flag
assert_equal "wow@example.com", response.params["metadata"]["email"]
end
+ def test_successful_purchase_with_destination
+ destination = fixtures(:stripe_destination)[:stripe_user_id]
+ custom_options = @options.merge(:destination => destination)
+ assert response = @gateway.purchase(@amount, @credit_card, custom_options)
+ assert_success response
+ assert_equal "charge", response.params["object"]
+ assert_equal destination, response.params["destination"]
+ assert response.params["paid"]
+ assert_equal "ActiveMerchant Test Purchase", response.params["description"]
+ assert_equal "wow@example.com", response.params["metadata"]["email"]
+ end
+
+ def test_successful_purchase_with_destination_and_amount
+ destination = fixtures(:stripe_destination)[:stripe_user_id]
+ custom_options = @options.merge(:destination => destination, :destination_amount => @amount - 20)
+ assert response = @gateway.purchase(@amount, @credit_card, custom_options)
+ assert_success response
+ assert_equal "charge", response.params["object"]
+ assert_equal destination, response.params["destination"]
+ assert response.params["paid"]
+ assert_equal "ActiveMerchant Test Purchase", response.params["description"]
+ assert_equal "wow@example.com", response.params["metadata"]["email"]
+ end
+
def test_unsuccessful_purchase
assert response = @gateway.purchase(@amount, @declined_card, @options)
assert_failure response
@@ -74,6 +98,14 @@ def test_unsuccessful_purchase
assert_match /ch_[a-zA-Z\d]+/, response.authorization
end
+ def test_unsuccessful_purchase_with_destination_and_amount
+ destination = fixtures(:stripe_destination)[:stripe_user_id]
+ custom_options = @options.merge(:destination => destination, :destination_amount => @amount + 20)
+ assert response = @gateway.purchase(@amount, @credit_card, custom_options)
+ assert_failure response
+ assert_match %r{must be less than or equal to the charge amount}, response.message
+ end
+
def test_successful_echeck_purchase_with_verified_account
customer_id = @verified_bank_account[:customer_id]
bank_account_id = @verified_bank_account[:bank_account_id]
@@ -103,6 +135,36 @@ def test_authorization_and_capture
assert_success capture
end
+ def test_authorization_and_capture_with_destination
+ destination = fixtures(:stripe_destination)[:stripe_user_id]
+ custom_options = @options.merge(:destination => destination)
+
+ assert authorization = @gateway.authorize(@amount, @credit_card, custom_options)
+ assert_success authorization
+ refute authorization.params["captured"]
+ assert_equal destination, authorization.params["destination"]
+ 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
+ end
+
+ def test_authorization_and_capture_with_destination_and_amount
+ destination = fixtures(:stripe_destination)[:stripe_user_id]
+ custom_options = @options.merge(:destination => destination, :destination_amount => @amount - 20)
+
+ assert authorization = @gateway.authorize(@amount, @credit_card, custom_options)
+ assert_success authorization
+ refute authorization.params["captured"]
+ assert_equal destination, authorization.params["destination"]
+ 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
+ end
+
def test_authorization_and_void
assert authorization = @gateway.authorize(@amount, @credit_card, @options)
assert_success authorization
@@ -191,6 +253,90 @@ def test_successful_verify
assert_equal "refund", response.responses.last.params["object"]
end
+ def test_successful_verify_cad
+ assert response = @gateway.verify(@credit_card, @options.merge(currency: "cad"))
+ assert_success response
+ assert_equal 100, response.params["amount"]
+ assert_equal "cad", response.params["currency"]
+ end
+
+ def test_successful_verify_gbp
+ assert response = @gateway.verify(@credit_card, @options.merge(currency: "gbp"))
+ assert_success response
+ assert_equal 60, response.params["amount"]
+ assert_equal "gbp", response.params["currency"]
+ end
+
+ def test_successful_verify_eur
+ assert response = @gateway.verify(@credit_card, @options.merge(currency: "eur"))
+ assert_success response
+ assert_equal 100, response.params["amount"]
+ assert_equal "eur", response.params["currency"]
+ end
+
+ def test_successful_verify_dkk
+ assert response = @gateway.verify(@credit_card, @options.merge(currency: "dkk"))
+ assert_success response
+ assert_equal 500, response.params["amount"]
+ assert_equal "dkk", response.params["currency"]
+ end
+
+ def test_successful_verify_nok
+ assert response = @gateway.verify(@credit_card, @options.merge(currency: "nok"))
+ assert_success response
+ assert_equal 600, response.params["amount"]
+ assert_equal "nok", response.params["currency"]
+ end
+
+ def test_successful_verify_sek
+ assert response = @gateway.verify(@credit_card, @options.merge(currency: "sek"))
+ assert_success response
+ assert_equal 600, response.params["amount"]
+ assert_equal "sek", response.params["currency"]
+ end
+
+ def test_successful_verify_chf
+ assert response = @gateway.verify(@credit_card, @options.merge(currency: "chf"))
+ assert_success response
+ assert_equal 100, response.params["amount"]
+ assert_equal "chf", response.params["currency"]
+ end
+
+ def test_successful_verify_aud
+ assert response = @gateway.verify(@credit_card, @options.merge(currency: "aud"))
+ assert_success response
+ assert_equal 100, response.params["amount"]
+ assert_equal "aud", response.params["currency"]
+ end
+
+ def test_successful_verify_jpy
+ assert response = @gateway.verify(@credit_card, @options.merge(currency: "jpy"))
+ assert_success response
+ assert_equal 100, response.params["amount"]
+ assert_equal "jpy", response.params["currency"]
+ end
+
+ def test_successful_verify_mxn
+ assert response = @gateway.verify(@credit_card, @options.merge(currency: "mxn"))
+ assert_success response
+ assert_equal 2000, response.params["amount"]
+ assert_equal "mxn", response.params["currency"]
+ end
+
+ def test_successful_verify_sgd
+ assert response = @gateway.verify(@credit_card, @options.merge(currency: "sgd"))
+ assert_success response
+ assert_equal 100, response.params["amount"]
+ assert_equal "sgd", response.params["currency"]
+ end
+
+ def test_successful_verify_hkd
+ assert response = @gateway.verify(@credit_card, @options.merge(currency: "hkd"))
+ assert_success response
+ assert_equal 800, response.params["amount"]
+ assert_equal "hkd", response.params["currency"]
+ end
+
def test_unsuccessful_verify
assert response = @gateway.verify(@declined_card, @options)
assert_failure response
@@ -339,8 +485,10 @@ def test_invalid_login
assert_match "Invalid API Key provided", response.message
end
+ # These "track data present" tests fail with invalid expiration dates. The
+ # test track data probably needs to be updated.
def test_card_present_purchase
- @credit_card.track_data = '%B378282246310005^LONGSON/LONGBOB^1705101130504392?'
+ @credit_card.track_data = '%B378282246310005^LONGSON/LONGBOB^2205101130504392?'
assert response = @gateway.purchase(@amount, @credit_card, @options)
assert_success response
assert_equal "charge", response.params["object"]
@@ -348,7 +496,7 @@ def test_card_present_purchase
end
def test_card_present_authorize_and_capture
- @credit_card.track_data = '%B378282246310005^LONGSON/LONGBOB^1705101130504392?'
+ @credit_card.track_data = '%B378282246310005^LONGSON/LONGBOB^2205101130504392?'
assert authorization = @gateway.authorize(@amount, @credit_card, @options)
assert_success authorization
refute authorization.params["captured"]
diff --git a/test/remote/gateways/remote_trans_first_test.rb b/test/remote/gateways/remote_trans_first_test.rb
index 46e3946ef89..a6036eacf10 100644
--- a/test/remote/gateways/remote_trans_first_test.rb
+++ b/test/remote/gateways/remote_trans_first_test.rb
@@ -70,14 +70,33 @@ def test_failed_purchase
assert_equal 'Insufficient funds', response.message
end
- def test_successful_refund
+ def test_successful_void
assert purchase = @gateway.purchase(@amount, @credit_card, @options)
assert_success purchase
- assert refund = @gateway.refund(@amount, purchase.authorization)
- assert_success refund
+ assert void = @gateway.void(purchase.authorization)
+ assert_success void
end
+ # Refunds can only be successfully run on settled transactions which take 24 hours
+ # def test_successful_refund
+ # assert purchase = @gateway.purchase(@amount, @credit_card, @options)
+ # assert_success purchase
+
+ # assert refund = @gateway.refund(@amount, purchase.authorization)
+ # assert_equal @amount, refund.params["amount"].to_i*100
+ # assert_success refund
+ # end
+
+ # def test_successful_partial_refund
+ # assert purchase = @gateway.purchase(@amount, @credit_card, @options)
+ # assert_success purchase
+
+ # assert refund = @gateway.refund(@amount-1, purchase.authorization)
+ # assert_equal @amount-1, refund.params["amount"].to_i*100
+ # assert_success refund
+ # end
+
def test_successful_refund_with_echeck
assert purchase = @gateway.purchase(@amount, @check, @options)
assert_success purchase
diff --git a/test/remote/gateways/remote_trans_first_transaction_express_test.rb b/test/remote/gateways/remote_trans_first_transaction_express_test.rb
index 3f4f2d0fb12..2f4488d9823 100644
--- a/test/remote/gateways/remote_trans_first_transaction_express_test.rb
+++ b/test/remote/gateways/remote_trans_first_transaction_express_test.rb
@@ -1,13 +1,14 @@
require "test_helper"
class RemoteTransFirstTransactionExpressTest < Test::Unit::TestCase
+
def setup
@gateway = TransFirstTransactionExpressGateway.new(fixtures(:trans_first_transaction_express))
@amount = 100
@declined_amount = 21
- @partial_amount = 1110
@credit_card = credit_card("4485896261017708")
+ @check = check
billing_address = address({
address1: "450 Main",
@@ -39,13 +40,80 @@ def test_successful_purchase
response = @gateway.purchase(@amount, @credit_card, @options)
assert_success response
assert_equal "Succeeded", response.message
+ assert_not_nil response.avs_result
+ assert_not_nil response.cvv_result
+ assert_equal "Street address does not match, but 5-digit postal code matches.", response.avs_result["message"]
+ assert_equal "CVV matches", response.cvv_result["message"]
+ end
+
+ def test_successful_purchase_no_avs
+ options = @options.dup
+ options[:shipping_address] = nil
+ options[:billing_address] = nil
+ response = @gateway.purchase(@amount, @credit_card, options)
+ assert_success response
+ end
+
+ def test_successful_purchase_with_only_required
+ # Test the purchase with only the required billing and shipping information
+ options = @options.dup
+ options[:shipping_address] = {
+ address1: "450 Main",
+ zip: "85284",
+ }
+
+ options[:billing_address] = {
+ address1: "450 Main",
+ zip: "85284",
+ }
+
+ response = @gateway.purchase(@amount, @credit_card, options)
+ assert_success response
+ assert_equal "Succeeded", response.message
+ assert_not_nil response.avs_result
+ assert_not_nil response.cvv_result
+ assert_equal "Street address does not match, but 5-digit postal code matches.", response.avs_result["message"]
+ assert_equal "CVV matches", response.cvv_result["message"]
end
- def test_partial_purchase
- response = @gateway.purchase(@partial_amount, @credit_card, @options)
+
+ def test_successful_purchase_without_cvv
+ credit_card_opts = {
+ :number => 4485896261017708,
+ :month => Date.new((Time.now.year + 1), 9, 30).month,
+ :year => Date.new((Time.now.year + 1), 9, 30).year,
+ :first_name => 'Longbob',
+ :last_name => 'Longsen',
+ :brand => 'visa'
+ }
+
+ credit_card = CreditCard.new(credit_card_opts)
+ response = @gateway.purchase(@amount, credit_card, @options)
assert_success response
assert_equal "Succeeded", response.message
- assert_match /0*555$/, response.params["amt"]
+ end
+
+ def test_successful_purchase_with_empty_string_cvv
+ credit_card_opts = {
+ :number => 4485896261017708,
+ :month => Date.new((Time.now.year + 1), 9, 30).month,
+ :year => Date.new((Time.now.year + 1), 9, 30).year,
+ :first_name => 'Longbob',
+ :last_name => 'Longsen',
+ :verification_value => '',
+ :brand => 'visa'
+ }
+
+ credit_card = CreditCard.new(credit_card_opts)
+ response = @gateway.purchase(@amount, credit_card, @options)
+ assert_success response
+ assert_equal "Succeeded", response.message
+ end
+
+ def test_successful_purchase_with_echeck
+ assert response = @gateway.purchase(@amount, @check, @options)
+ assert_success response
+ assert_equal 'Succeeded', response.message
end
def test_failed_purchase
@@ -55,6 +123,12 @@ def test_failed_purchase
assert_equal "51", response.params["rspCode"]
end
+ def test_failed_purchase_with_echeck
+ assert response = @gateway.purchase(@amount, check(routing_number: "121042883"), @options)
+ assert_failure response
+ assert_equal 'Error. Bank routing number validation negative (ABA).', response.message
+ end
+
def test_successful_authorize_and_capture
response = @gateway.authorize(@amount, @credit_card, @options)
assert_success response
@@ -114,10 +188,19 @@ def test_successful_capture_void
end
def test_failed_void
- response = @gateway.void("")
+ response = @gateway.void("purchase|000015212561")
assert_failure response
- assert_equal "Validation Failure", response.message
- assert_equal "50011", response.error_code
+ assert_equal "Invalid transaction", response.message
+ assert_equal "12", response.error_code
+ end
+
+ def test_successful_echeck_purchase_void
+ response = @gateway.purchase(@amount, @check, @options)
+ assert_success response
+
+ void = @gateway.void(response.authorization)
+ assert_success void
+ assert_equal "Succeeded", void.message
end
# gateway does not settle fast enough to test refunds
@@ -147,6 +230,21 @@ def test_failed_refund
assert_equal "50011", response.error_code
end
+ def test_successful_refund_with_echeck
+ purchase = @gateway.purchase(@amount, @check, @options)
+ assert_success purchase
+ assert_match /purchase_echeck/, purchase.authorization
+
+ refund = @gateway.refund(@amount, purchase.authorization)
+ assert_success refund
+ end
+
+ def test_failed_refund_with_echeck
+ refund = @gateway.refund(@amount, 'purchase_echeck|000028706091')
+ assert_failure refund
+ assert_equal "Invalid transaction", refund.message
+ end
+
# Credit is only supported with specific approval from Transaction Express
# def test_successful_credit
# response = @gateway.credit(@amount, @credit_card, @options)
@@ -178,7 +276,7 @@ def test_failed_verify
response = @gateway.verify(credit_card(""), @options)
assert_failure response
assert_equal "Validation Failure", response.message
- assert_equal "50011", response.error_code
+ assert_equal "51308", response.error_code
end
def test_successful_store
@@ -228,7 +326,7 @@ def test_failed_store
response = @gateway.store(credit_card("123"), @options)
assert_failure response
assert_equal "Validation Failure", response.message
- assert_equal "50011", response.error_code
+ assert_equal "51308", response.error_code
end
# def test_dump_transcript
diff --git a/test/remote/gateways/remote_trexle_test.rb b/test/remote/gateways/remote_trexle_test.rb
new file mode 100644
index 00000000000..c0d3fcf09cf
--- /dev/null
+++ b/test/remote/gateways/remote_trexle_test.rb
@@ -0,0 +1,170 @@
+require 'test_helper'
+
+class RemoteTrexleTest < Test::Unit::TestCase
+ def setup
+ @gateway = TrexleGateway.new(fixtures(:trexle))
+
+ @amount = 100
+ @credit_card = credit_card('5555555555554444', year: Time.now.year + 2)
+ @visa_credit_card = credit_card('4242424242424242', year: Time.now.year + 3)
+ @declined_card = credit_card('4000000000000119')
+
+ @options = {
+ email: 'john@trexle.com',
+ ip: '66.249.79.118',
+ order_id: '1',
+ billing_address: address,
+ description: "ActiveMerchant Demo Purchase #{DateTime.now.to_i}"
+ }
+ end
+
+ def test_successful_purchase
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal true, response.params['response']['captured']
+ end
+
+ def test_successful_authorize_and_capture
+ authorization = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success authorization
+ assert_equal false, authorization.params['response']['captured']
+
+ response = @gateway.capture(@amount, authorization.authorization, @options)
+ assert_success response
+ assert_equal true, response.params['response']['captured']
+ end
+
+ def test_failed_authorize
+ response = @gateway.authorize(@amount, @declined_card, @options)
+ assert_failure response
+ end
+
+ def test_failed_capture_due_to_invalid_token
+ response = @gateway.capture(@amount, "bogus", @options)
+ assert_failure response
+ end
+
+ def test_failed_capture_due_to_invalid_amount
+ authorization = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success authorization
+ assert_equal authorization.params['response']['captured'], false
+
+ response = @gateway.capture(@amount + 1, authorization.authorization, @options)
+ assert_failure response
+ assert_equal 'Payment failed', response.params['error']
+ end
+
+ def test_successful_purchase_without_description
+ @options.delete(:description)
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ end
+
+ def test_unsuccessful_purchase
+ response = @gateway.purchase(@amount, @declined_card, @options)
+ assert_failure response
+ end
+
+ # This is a bit manual as we have to create a working card token as
+ # would be returned from trexle.js / the card tokens API which
+ # falls outside of active merchant
+ def test_store_and_charge_with_trexle_js_card_token
+ headers = {
+ "Content-Type" => "application/json",
+ "Authorization" => "Basic #{Base64.strict_encode64(@gateway.options[:api_key] + ':').strip}"
+ }
+ # Get a token equivalent to what is returned by trexle.js
+ card_attrs = {
+ number: @credit_card.number,
+ expiry_month: @credit_card.month,
+ expiry_year: @credit_card.year,
+ cvc: @credit_card.verification_value,
+ name: "#{@credit_card.first_name} #{@credit_card.last_name}",
+ address_line1: "321 Shoreline Park",
+ address_line2: "suite #7",
+ address_city: "Mountain View",
+ address_postcode: "94043",
+ address_state: "CA",
+ address_country: "United States"
+ }
+ url = @gateway.test_url + "/tokens"
+
+ body = JSON.parse(@gateway.ssl_post(url, card_attrs.to_json, headers))
+
+ card_token = body["response"]["token"]
+
+ store = @gateway.store(card_token, @options)
+ assert_success store
+ assert_not_nil store.authorization
+
+ purchase = @gateway.purchase(@amount, card_token, @options)
+ assert_success purchase
+ assert_not_nil purchase.authorization
+ end
+
+ def test_store_and_customer_token_charge
+ response = @gateway.store(@credit_card, @options)
+ assert_success response
+ assert_not_nil response.authorization
+
+ token = response.params['response']['card']['token']
+
+ assert response1 = @gateway.purchase(@amount, token, @options)
+ assert_success response1
+
+ assert response2 = @gateway.purchase(@amount, token, @options)
+ assert_success response2
+ assert_not_equal response1.authorization, response2.authorization
+ end
+
+ def test_store_and_update
+ response = @gateway.store(@credit_card, @options)
+ assert_success response
+ assert_not_nil response.authorization
+ assert_equal @credit_card.year, response.params['response']['card']['expiry_year']
+
+ response = @gateway.update(response.authorization, @credit_card, address: address)
+ assert_success response
+ assert_not_nil response.authorization
+ assert_equal @credit_card.year, response.params['response']['card']['expiry_year']
+ end
+
+ def test_refund
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_not_nil response.authorization
+
+ token = response.authorization
+
+ response = @gateway.refund(@amount, token, @options)
+ assert_success response
+ assert_not_nil response.authorization
+ end
+
+ def test_failed_refund
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_not_nil response.authorization
+
+ token = response.authorization
+
+ response = @gateway.refund(@amount, token.reverse, @options)
+ assert_failure response
+ end
+
+ def test_invalid_login
+ gateway = TrexleGateway.new(api_key: '')
+ response = gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ clean_transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, clean_transcript)
+ assert_scrubbed(@credit_card.verification_value.to_s, clean_transcript)
+ 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 6bac49c18e1..de314da965b 100644
--- a/test/remote/gateways/remote_usa_epay_advanced_test.rb
+++ b/test/remote/gateways/remote_usa_epay_advanced_test.rb
@@ -175,6 +175,14 @@ def test_update_customer
assert response.params['update_customer_return']
end
+ def test_quick_update_customer
+ response = @gateway.add_customer(@options.merge(@customer_options))
+ customer_number = response.params['add_customer_return']
+
+ response = @gateway.quick_update_customer({customer_number: customer_number, update_data: @update_customer_options})
+ assert response.params['quick_update_customer_return']
+ end
+
def test_enable_disable_customer
response = @gateway.add_customer(@options.merge(@customer_options))
customer_number = response.params['add_customer_return']
diff --git a/test/remote/gateways/remote_usa_epay_transaction_test.rb b/test/remote/gateways/remote_usa_epay_transaction_test.rb
index 9aafa893b3f..c1e9a6469bc 100644
--- a/test/remote/gateways/remote_usa_epay_transaction_test.rb
+++ b/test/remote/gateways/remote_usa_epay_transaction_test.rb
@@ -6,6 +6,7 @@ def setup
@credit_card = credit_card('4000100011112224')
@declined_card = credit_card('4000300011112220')
@credit_card_with_track_data = credit_card_with_track_data('4000100011112224')
+ @check = check
@options = { :billing_address => address(:zip => "27614", :state => "NC"), :shipping_address => address }
@amount = 100
end
@@ -22,6 +23,12 @@ def test_successful_purchase_with_track_data
assert_success response
end
+ def test_successful_purchase_with_echeck
+ assert response = @gateway.purchase(@amount, @check, @options)
+ assert_equal 'Success', response.message
+ assert_success response
+ end
+
def test_successful_authorization_with_manual_entry
@credit_card.manual_entry = true
assert response = @gateway.authorize(@amount, @credit_card, @options)
@@ -89,6 +96,14 @@ def test_successful_refund_with_track_data
assert_success refund
end
+ def test_successful_refund_of_echeck
+ assert response = @gateway.purchase(@amount, @check, @options)
+ assert_success response
+ assert response.authorization
+ assert refund = @gateway.refund(@amount - 20, response.authorization)
+ assert_success refund
+ end
+
def test_unsuccessful_refund
assert refund = @gateway.refund(@amount - 20, "unknown_authorization")
assert_failure refund
@@ -103,6 +118,14 @@ def test_successful_void
assert_success void
end
+ def test_successful_void_with_echeck
+ assert response = @gateway.purchase(@amount, @check, @options)
+ assert_success response
+ assert response.authorization
+ assert void = @gateway.void(response.authorization)
+ assert_success void
+ end
+
def test_unsuccessful_void
assert void = @gateway.void("unknown_authorization")
assert_failure void
@@ -117,6 +140,14 @@ def test_successful_void_release
assert_success void
end
+ def test_successful_void_release_with_echeck
+ assert response = @gateway.purchase(@amount, @check, @options)
+ assert_success response
+ assert response.authorization
+ assert void = @gateway.void(response.authorization, void_mode: :void_release)
+ assert_success void
+ end
+
def test_unsuccessful_void_release
assert void = @gateway.void("unknown_authorization", void_mode: :void_release)
assert_failure void
@@ -143,4 +174,30 @@ def test_failed_verify
assert_match "Card Declined (00)", response.message
end
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:login], transcript)
+
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card_with_track_data, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card_with_track_data.track_data, transcript)
+ assert_scrubbed(@gateway.options[:login], transcript)
+
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @check, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@check.account_number, transcript)
+ assert_scrubbed(@gateway.options[:login], transcript)
+ end
end
diff --git a/test/remote/gateways/remote_vacaypay_test.rb b/test/remote/gateways/remote_vacaypay_test.rb
new file mode 100644
index 00000000000..e37d19348df
--- /dev/null
+++ b/test/remote/gateways/remote_vacaypay_test.rb
@@ -0,0 +1,154 @@
+require 'test_helper'
+
+class RemoteVacaypayTest < Test::Unit::TestCase
+ def setup
+ @gateway = VacaypayGateway.new(fixtures(:vacaypay))
+
+ @credit_card = credit_card('4242424242424242')
+ @declined_card = credit_card('4000000000000002')
+ @store_declined_card = credit_card('4000000000000002', {
+ :verification_value => nil
+ })
+ @amount = 10000
+
+ @options = {
+ :currency => "USD",
+ :description => 'ActiveMerchant Test Purchase',
+ :email => 'wow@example.com',
+ :ip => '127.0.0.1',
+ :first_name => 'Longbob',
+ :last_name => 'Longsen'
+ }
+ end
+
+ def test_successful_purchase
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ end
+
+ def test_successful_purchase_with_more_options
+
+ options = @options.merge({
+ :externalPaymentReference => 'payment-test-1234',
+ :externalBookingReference => 'booking-test-1234'
+ })
+
+ response = @gateway.purchase(@amount, @credit_card, options)
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ end
+
+ def test_failed_purchase
+ response = @gateway.purchase(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal 'Your card was declined.', response.message
+ end
+
+ def test_successful_authorize_and_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+ refute auth.params["data"]["captured"]
+ assert_equal "ActiveMerchant Test Purchase", auth.params["data"]["description"]
+ assert_equal "wow@example.com", auth.params["data"]["email"]
+
+ assert capture = @gateway.capture(@amount, auth.authorization)
+ assert_success capture
+ assert_equal 'Succeeded', capture.message
+ end
+
+ def test_failed_authorize
+ response = @gateway.authorize(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal 'card_declined', response.error_code
+ end
+
+ def test_partial_capture
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert capture = @gateway.capture(@amount - 100, auth.authorization)
+ assert_success capture
+ end
+
+ def test_failed_capture
+ response = @gateway.capture(@amount, '0')
+ assert_failure response
+ end
+
+ def test_successful_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount, purchase.authorization)
+ assert_success refund
+ assert_equal 'Succeeded', refund.message
+ end
+
+ def test_partial_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount - 100, purchase.authorization)
+ assert_success refund
+ end
+
+ def test_failed_refund
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ response = @gateway.refund(@amount + 100, purchase.authorization)
+ assert_failure response
+ assert_equal 'Cannot refund a value less than 0, or higher than the amount refundable (100).', response.message
+ end
+
+ def test_successful_void
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success auth
+
+ assert void = @gateway.void(auth.authorization)
+ assert_success void
+ assert_equal 'Succeeded', void.message
+ end
+
+ def test_failed_void
+ purchase = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success purchase
+
+ assert refund = @gateway.refund(@amount, purchase.authorization)
+ assert_success refund
+ assert_equal 'Succeeded', refund.message
+
+ response = @gateway.void(purchase.authorization)
+ assert_failure response
+ assert_equal 'Cannot refund a value less than 0, or higher than the amount refundable (0).', response.message
+ end
+
+ def test_successful_store
+ response = @gateway.store(@credit_card, @options)
+ assert_success response
+ end
+
+ def test_failed_store
+ response = @gateway.store(@store_declined_card, @options)
+ assert_failure response
+ end
+
+ def test_invalid_login
+ gateway = VacaypayGateway.new(api_key: '0', account_uuid: '0')
+
+ response = gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@gateway.options[:api_key], transcript)
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ end
+end
diff --git a/test/remote/gateways/remote_vanco_test.rb b/test/remote/gateways/remote_vanco_test.rb
index 47e58e0e6e0..af1bd190e3a 100644
--- a/test/remote/gateways/remote_vanco_test.rb
+++ b/test/remote/gateways/remote_vanco_test.rb
@@ -6,6 +6,7 @@ def setup
@amount = 10005
@credit_card = credit_card('4111111111111111')
+ @declined_card = credit_card('4111111111111111', year: 2011)
@check = check
@options = {
@@ -40,10 +41,10 @@ def test_successful_purchase_sans_minimal_options
end
def test_failed_purchase
- response = @gateway.purchase(@amount, @credit_card, billing_address: address(country: "CA"))
+ response = @gateway.purchase(@amount, @declined_card)
assert_failure response
- assert_equal("Client not set up for International Credit Card Processing", response.message)
- assert_equal("286", response.params["error_codes"])
+ assert_equal("Invalid Expiration Date", response.message)
+ assert_equal("183", response.params["error_codes"])
end
def test_successful_echeck_purchase
diff --git a/test/remote/gateways/remote_wepay_test.rb b/test/remote/gateways/remote_wepay_test.rb
index b58f2251272..ee7fe151b30 100644
--- a/test/remote/gateways/remote_wepay_test.rb
+++ b/test/remote/gateways/remote_wepay_test.rb
@@ -5,7 +5,9 @@ def setup
@gateway = WepayGateway.new(fixtures(:wepay))
@amount = 2000
- @credit_card = credit_card('5496198584584769')
+ @credit_card = credit_card('5496198584584769', verification_value: '321')
+ @credit_card_without_cvv = credit_card('5496198584584769', verification_value: nil)
+
@declined_card = credit_card('')
@options = {
@@ -33,6 +35,14 @@ def test_successful_purchase_with_token
assert_success response
end
+ def test_successful_purchase_with_recurring_and_ip
+ store = @gateway.store(@credit_card, @options.merge(recurring: true, ip: '127.0.0.1'))
+ assert_success store
+
+ response = @gateway.purchase(@amount, store.authorization, @options)
+ assert_success response
+ end
+
def test_successful_purchase_sans_cvv
@options[:recurring] = true
store = @gateway.store(@credit_card, @options)
@@ -42,6 +52,13 @@ def test_successful_purchase_sans_cvv
assert_success response
end
+ def test_successful_purchase_with_few_options
+ options = { address: { zip: "27701" }, email: "test@example.com" }
+ response = @gateway.purchase(@amount, @credit_card, options)
+ assert_success response
+ assert_equal 'Success', response.message
+ end
+
def test_failed_purchase_sans_ccv
@credit_card.verification_value = nil
response = @gateway.purchase(@amount, @credit_card, @options)
@@ -53,11 +70,68 @@ def test_failed_purchase_with_token
assert_failure response
end
- def test_successful_store
+ def test_successful_purchase_with_fee
+ response = @gateway.purchase(@amount, @credit_card, @options.merge(application_fee: 3, fee_payer: "payee"))
+ assert_success response
+ assert_equal 'Success', response.message
+ end
+
+ def test_successful_purchase_with_unique_id
+ response = @gateway.purchase(@amount, @credit_card, @options.merge(unique_id: generate_unique_id))
+ assert_success response
+ assert_equal 'Success', response.message
+ end
+
+ def test_successful_purchase_with_ip_and_risk_token
+ response = @gateway.purchase(@amount, @credit_card, @options.merge(ip: "100.166.99.123", risk_token: "123e4567-e89b-12d3-a456-426655440000"))
+ assert_success response
+ assert_equal 'Success', response.message
+ end
+
+ def test_successful_authorize
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'Success', response.message
+ end
+
+ def test_failed_authorize
+ response = @gateway.authorize(@amount, @declined_card, @options)
+ assert_failure response
+ end
+
+ def test_successful_store_via_create_with_cvv
+ # POST /credit_card/create
response = @gateway.store(@credit_card, @options)
assert_success response
end
+ def test_successful_store_via_transfer_without_cvv
+ # special permission required
+ # POST /credit_card/transfer
+ response = @gateway.store(@credit_card_without_cvv, @options.merge(recurring: true))
+ assert_success response
+ end
+
+ def test_unsuccessful_store_via_create_with_cvv
+ response = @gateway.store(@credit_card_without_cvv, @options)
+
+ assert_failure response
+ assert_equal('This app does not have permissions to create credit cards without a cvv', response.message)
+ end
+
+ # # Requires commenting out `unless options[:recurring]` when building post hash in `store` method.
+ # def test_unsuccessful_store_via_transfer_with_cvv
+ # response = @gateway.store(@credit_card, @options.merge(recurring: true))
+ #
+ # assert_failure response
+ # assert_equal('cvv parameter is unexpected', response.message)
+ # end
+
+ def test_successful_store_with_defaulted_email
+ response = @gateway.store(@credit_card, {billing_address: address})
+ assert_success response
+ end
+
def test_failed_store
response = @gateway.store(@declined_card, @options)
assert_failure response
@@ -108,6 +182,13 @@ def test_authorize_and_void
assert_success void
end
+ # Version sent here will need to match or be one ahead of the version set in the test account's dashboard
+ def test_successful_purchase_with_version
+ response = @gateway.purchase(@amount, @credit_card, @options.merge(version: '2017-05-31'))
+ assert_success response
+ assert_equal 'Success', response.message
+ end
+
def test_invalid_login
gateway = WepayGateway.new(
client_id: 12515,
@@ -117,4 +198,15 @@ def test_invalid_login
response = gateway.purchase(@amount, @credit_card, @options)
assert_failure response
end
+
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:access_token], transcript)
+ end
end
diff --git a/test/remote/gateways/remote_wirecard_test.rb b/test/remote/gateways/remote_wirecard_test.rb
index c397209b305..a345caa255f 100644
--- a/test/remote/gateways/remote_wirecard_test.rb
+++ b/test/remote/gateways/remote_wirecard_test.rb
@@ -15,7 +15,8 @@ def setup
order_id: 1,
billing_address: address,
description: 'Wirecard remote test purchase',
- email: 'soleone@example.com'
+ email: 'soleone@example.com',
+ ip: '127.0.0.1'
}
@german_address = {
diff --git a/test/remote/gateways/remote_worldpay_test.rb b/test/remote/gateways/remote_worldpay_test.rb
index 81138753038..5180f0b7fc6 100644
--- a/test/remote/gateways/remote_worldpay_test.rb
+++ b/test/remote/gateways/remote_worldpay_test.rb
@@ -4,10 +4,12 @@ class RemoteWorldpayTest < Test::Unit::TestCase
def setup
@gateway = WorldpayGateway.new(fixtures(:world_pay_gateway))
+ @cftgateway = WorldpayGateway.new(fixtures(:world_pay_gateway_cft))
@amount = 100
@credit_card = credit_card('4111111111111111')
@declined_card = credit_card('4111111111111111', :first_name => nil, :last_name => 'REFUSED')
+ @threeDS_card = credit_card('4111111111111111', :first_name => nil, :last_name => '3D')
@options = {order_id: generate_unique_id, email: "wow@example.com"}
end
@@ -18,9 +20,22 @@ def test_successful_purchase
assert_equal 'SUCCESS', response.message
end
+ def test_successful_purchase_with_hcg_additional_data
+ @options.merge!(hcg_additional_data: {
+ key1: "value1",
+ key2: "value2",
+ key3: "value3"
+ })
+
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'SUCCESS', response.message
+ end
+
def test_failed_purchase
assert response = @gateway.purchase(@amount, @declined_card, @options)
assert_failure response
+ assert_equal '5', response.error_code
assert_equal 'REFUSED', response.message
end
@@ -29,6 +44,7 @@ def test_authorize_and_capture
assert_success auth
assert_equal 'SUCCESS', auth.message
assert auth.authorization
+ sleep(40)
assert capture = @gateway.capture(@amount, auth.authorization)
assert_success capture
end
@@ -37,12 +53,14 @@ def test_authorize_and_capture_by_reference
assert auth = @gateway.authorize(@amount, @credit_card, @options)
assert_success auth
assert_equal 'SUCCESS', auth.message
+ sleep(40)
assert capture = @gateway.capture(@amount, auth.authorization)
assert_success capture
assert reference = auth.authorization
@options[:order_id] = generate_unique_id
assert auth = @gateway.authorize(@amount, reference, @options)
+ sleep(40)
assert capture = @gateway.capture(@amount, auth.authorization)
assert_success capture
end
@@ -51,6 +69,7 @@ def test_authorize_and_purchase_by_reference
assert auth = @gateway.authorize(@amount, @credit_card, @options)
assert_success auth
assert_equal 'SUCCESS', auth.message
+ sleep(40)
assert capture = @gateway.capture(@amount, auth.authorization)
assert_success capture
@@ -58,10 +77,51 @@ def test_authorize_and_purchase_by_reference
@options[:order_id] = generate_unique_id
assert auth = @gateway.authorize(@amount, reference, @options)
@options[:order_id] = generate_unique_id
+ sleep(40)
assert capture = @gateway.purchase(@amount, auth.authorization, @options)
assert_success capture
end
+ def test_successful_authorize_with_3ds
+ session_id = generate_unique_id
+ order_id = @options[:order_id]
+ options = @options.merge(
+ {
+ execute_threed: true,
+ accept_header: 'text/html',
+ user_agent: 'Mozilla/5.0',
+ session_id: session_id,
+ ip: '127.0.0.1',
+ cookie: 'machine=32423423'
+ })
+ assert first_message = @gateway.authorize(@amount, @threeDS_card, options)
+ assert_equal "A transaction status of 'AUTHORISED' is required.", first_message.message
+ assert first_message.test?
+ refute first_message.authorization.blank?
+ refute first_message.params['issuer_url'].blank?
+ refute first_message.params['pa_request'].blank?
+ refute first_message.params['cookie'].blank?
+ refute first_message.params['session_id'].blank?
+ end
+
+ def test_failed_authorize_with_3ds
+ session_id = generate_unique_id
+ order_id = @options[:order_id]
+ options = @options.merge(
+ {
+ execute_threed: true,
+ accept_header: 'text/html',
+ session_id: session_id,
+ ip: '127.0.0.1',
+ cookie: 'machine=32423423'
+ })
+ assert first_message = @gateway.authorize(@amount, @threeDS_card, options)
+ assert_match %r{missing info for 3D-secure transaction}i, first_message.message
+ assert first_message.test?
+ assert first_message.params['issuer_url'].blank?
+ assert first_message.params['pa_request'].blank?
+ end
+
def test_failed_capture
assert response = @gateway.capture(@amount, 'bogus')
assert_failure response
@@ -72,12 +132,21 @@ def test_billing_address
assert_success @gateway.authorize(@amount, @credit_card, @options.merge(:billing_address => address))
end
+ def test_partial_address
+ billing_address = address
+ billing_address.delete(:address1)
+ billing_address.delete(:zip)
+ billing_address.delete(:country)
+ assert_success @gateway.authorize(@amount, @credit_card, @options.merge(:billing_address => billing_address))
+ end
+
def test_ip_address
assert_success @gateway.authorize(@amount, @credit_card, @options.merge(ip: "192.18.123.12"))
end
def test_void
assert_success(response = @gateway.authorize(@amount, @credit_card, @options))
+ sleep(40)
assert_success (void = @gateway.void(response.authorization))
assert_equal "SUCCESS", void.message
assert void.params["cancel_received_order_code"]
@@ -98,10 +167,17 @@ def test_authorize_fractional_currency
def test_authorize_nonfractional_currency
assert_success(result = @gateway.authorize(1234, @credit_card, @options.merge(:currency => 'IDR')))
assert_equal "IDR", result.params['amount_currency_code']
- assert_equal "1234", result.params['amount_value']
+ assert_equal "12", result.params['amount_value']
assert_equal "0", result.params['amount_exponent']
end
+ def test_authorize_three_decimal_currency
+ assert_success(result = @gateway.authorize(1234, @credit_card, @options.merge(:currency => 'OMR')))
+ assert_equal "OMR", result.params['amount_currency_code']
+ assert_equal "1234", result.params['amount_value']
+ assert_equal "3", result.params['amount_exponent']
+ end
+
def test_reference_transaction
assert_success(original = @gateway.authorize(100, @credit_card, @options))
assert_success(@gateway.authorize(200, original.authorization, :order_id => generate_unique_id))
@@ -140,6 +216,12 @@ def test_failed_verify
assert_match %r{REFUSED}, response.message
end
+ def test_successful_credit_on_cft_gateway
+ credit = @cftgateway.credit(@amount, @credit_card, @options)
+ assert_success credit
+ assert_equal "SUCCESS", credit.message
+ end
+
def test_transcript_scrubbing
transcript = capture_transcript(@gateway) do
@gateway.purchase(@amount, @credit_card, @options)
@@ -152,18 +234,22 @@ def test_transcript_scrubbing
# Worldpay has a delay between asking for a transaction to be captured and actually marking it as captured
- # These 2 tests work if you take the auth code, wait some time and then perform the next operation.
+ # These 2 tests work if you get authorizations from a purchase, wait some time and then perform the refund/void operation.
- # def test_refund
+ # def get_authorization
# assert_success(response = @gateway.purchase(@amount, @credit_card, @options))
# assert response.authorization
- # refund = @gateway.refund(@amount, capture.authorization)
+ # puts "auth: " + response.authorization
+ # end
+
+ # def test_refund
+ # refund = @gateway.refund(@amount, 'replace_with_authorization')
# assert_success refund
# assert_equal "SUCCESS", refund.message
# end
# def test_void_fails_unless_status_is_authorised
- # response = @gateway.void("33d6dfa9726198d44a743488cf611d3b") # existing transaction in CAPTURED state
+ # response = @gateway.void('replace_with_authorization') # existing transaction in CAPTURED state
# assert_failure response
# assert_equal "A transaction status of 'AUTHORISED' is required.", response.message
# end
diff --git a/test/remote/gateways/remote_worldpay_us_test.rb b/test/remote/gateways/remote_worldpay_us_test.rb
index d9e786ff3d4..db2adc37c01 100644
--- a/test/remote/gateways/remote_worldpay_us_test.rb
+++ b/test/remote/gateways/remote_worldpay_us_test.rb
@@ -5,9 +5,9 @@ def setup
@gateway = WorldpayUsGateway.new(fixtures(:worldpay_us))
@amount = 100
- @credit_card = credit_card('4446661234567892')
+ @credit_card = credit_card('4446661234567892', :verification_value => '987')
@declined_card = credit_card('4000300011112220')
- @check = check
+ @check = check(:number => '12345654321')
@options = {
order_id: generate_unique_id,
@@ -22,6 +22,13 @@ def test_successful_purchase
assert_equal 'Succeeded', response.message
end
+ def test_successful_purchase_on_backup_url
+ gateway = WorldpayUsGateway.new(fixtures(:worldpay_us).merge({ use_backup_url: true}))
+ response = gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ end
+
def test_failed_purchase
response = @gateway.purchase(@amount, @declined_card, @options)
assert_failure response
@@ -109,4 +116,22 @@ def test_invalid_login
assert response.message =~ /DECLINED/
end
+ def test_transcript_scrubbing
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@credit_card.number, transcript)
+ assert_scrubbed(@credit_card.verification_value, transcript)
+ assert_scrubbed(@gateway.options[:merchantpin], transcript)
+
+ transcript = capture_transcript(@gateway) do
+ @gateway.purchase(@amount, @check, @options)
+ end
+ transcript = @gateway.scrub(transcript)
+
+ assert_scrubbed(@check.account_number, transcript)
+ assert_scrubbed(@gateway.options[:merchantpin], transcript)
+ end
end
diff --git a/test/schema/cyber_source/CyberSourceTransaction_1.121.xsd b/test/schema/cyber_source/CyberSourceTransaction_1.121.xsd
new file mode 100644
index 00000000000..dbf57b71011
--- /dev/null
+++ b/test/schema/cyber_source/CyberSourceTransaction_1.121.xsd
@@ -0,0 +1,3627 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/schema/firstdata_e4/v11.xsd b/test/schema/firstdata_e4/v11.xsd
new file mode 100644
index 00000000000..b2bbc2996f6
--- /dev/null
+++ b/test/schema/firstdata_e4/v11.xsd
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/test_helper.rb b/test/test_helper.rb
index c80e72c3f2d..929e5d8df59 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -164,8 +164,10 @@ def credit_card(number = '4242424242424242', options = {})
end
def credit_card_with_track_data(number = '4242424242424242', options = {})
+ exp_date = default_expiration_date.strftime("%y%m")
+
defaults = {
- :track_data => '%B' + number + '^LONGSEN/L. ^15121200000000000000**123******?',
+ :track_data => "%B#{number}^LONGSEN/L. ^#{exp_date}1200000000000000**123******?",
}.update(options)
Billing::CreditCard.new(defaults)
@@ -232,6 +234,16 @@ def address(options = {})
}.update(options)
end
+ def statement_address(options = {})
+ {
+ address1: '456 My Street',
+ address2: 'Apt 1',
+ city: 'Ottawa',
+ state: 'ON',
+ zip: 'K1C2N6'
+ }.update(options)
+ end
+
def generate_unique_id
SecureRandom.hex(16)
end
diff --git a/test/unit/connection_test.rb b/test/unit/connection_test.rb
index 8486ef987d9..4e08eae8be5 100644
--- a/test/unit/connection_test.rb
+++ b/test/unit/connection_test.rb
@@ -26,6 +26,22 @@ def test_connection_endpoint_raises_uri_error
end
end
+ def test_connection_passes_env_proxy_by_default
+ spy = Net::HTTP.new('example.com', 443)
+ Net::HTTP.expects(:new).with('example.com', 443, :ENV, nil).returns(spy)
+ spy.expects(:get).with('/tx.php', {}).returns(@ok)
+ @connection.request(:get, nil, {})
+ end
+
+ def test_connection_does_pass_requested_proxy
+ @connection.proxy_address = "proxy.example.com"
+ @connection.proxy_port = 8080
+ spy = Net::HTTP.new('example.com', 443)
+ Net::HTTP.expects(:new).with('example.com', 443, "proxy.example.com", 8080).returns(spy)
+ spy.expects(:get).with('/tx.php', {}).returns(@ok)
+ @connection.request(:get, nil, {})
+ end
+
def test_successful_get_request
@connection.logger.expects(:info).twice
Net::HTTP.any_instance.expects(:get).with('/tx.php', {}).returns(@ok)
@@ -51,6 +67,12 @@ def test_successful_delete_request
assert_equal 'success', response.body
end
+ def test_successful_delete_with_body_request
+ Net::HTTP.any_instance.expects(:request).at_most(3).returns(@ok)
+ response = @connection.request(:delete, 'data', {})
+ assert_equal 'success', response.body
+ end
+
def test_get_raises_argument_error_if_passed_data
assert_raises(ArgumentError) do
@connection.request(:get, 'data', {})
@@ -75,6 +97,22 @@ def test_override_ssl_version
assert_equal :SSLv3, @connection.ssl_version
end
+ def test_override_min_version
+ omit_if Net::HTTP.instance_methods.exclude?(:min_version=)
+
+ refute_equal :TLS1_1, @connection.min_version
+ @connection.min_version = :TLS1_1
+ assert_equal :TLS1_1, @connection.min_version
+ end
+
+ def test_override_max_version
+ omit_if Net::HTTP.instance_methods.exclude?(:min_version=)
+
+ refute_equal :TLS1_2, @connection.max_version
+ @connection.max_version = :TLS1_2
+ assert_equal :TLS1_2, @connection.max_version
+ end
+
def test_default_read_timeout
assert_equal ActiveMerchant::Connection::READ_TIMEOUT, @connection.read_timeout
end
@@ -178,4 +216,11 @@ def test_failure_with_ssl_certificate
end
end
+ def test_wiredump_service_raises_on_frozen_object
+ transcript = ''.freeze
+ assert_raise ArgumentError, "can't wiredump to frozen object" do
+ @connection.wiredump_device = transcript
+ end
+ end
+
end
diff --git a/test/unit/credit_card_methods_test.rb b/test/unit/credit_card_methods_test.rb
index e910ddfecdd..2e99504a258 100644
--- a/test/unit/credit_card_methods_test.rb
+++ b/test/unit/credit_card_methods_test.rb
@@ -154,6 +154,14 @@ def test_should_detect_laser_card
assert_equal 'laser', CreditCard.brand?('677117111234')
end
+ def test_should_detect_sodexo_card
+ assert_equal 'sodexo', CreditCard.brand?('60606944957644')
+ end
+
+ def test_should_detect_vr_card
+ assert_equal 'vr', CreditCard.brand?('63703644957644')
+ end
+
def test_should_detect_when_an_argument_brand_does_not_match_calculated_brand
assert CreditCard.matching_brand?('4175001000000000', 'visa')
assert_false CreditCard.matching_brand?('4175001000000000', 'master')
diff --git a/test/unit/credit_card_test.rb b/test/unit/credit_card_test.rb
index bc18e0918c7..154e1c72ee6 100644
--- a/test/unit/credit_card_test.rb
+++ b/test/unit/credit_card_test.rb
@@ -346,6 +346,23 @@ def test_should_assign_a_full_name
c = CreditCard.new :name => "Twiggy"
assert_equal "", c.first_name
assert_equal "Twiggy", c.last_name
+ assert_equal "Twiggy", c.name
+ end
+
+ def test_should_remove_trailing_whitespace_on_name
+ c = CreditCard.new(:last_name => 'Herdman')
+ assert_equal "Herdman", c.name
+
+ c = CreditCard.new(:last_name => 'Herdman', first_name: '')
+ assert_equal "Herdman", c.name
+ end
+
+ def test_should_remove_leading_whitespace_on_name
+ c = CreditCard.new(:first_name => 'James')
+ assert_equal "James", c.name
+
+ c = CreditCard.new(:last_name => '', first_name: 'James')
+ assert_equal "James", c.name
end
# The following is a regression for a bug that raised an exception when
diff --git a/test/unit/gateways/adyen_test.rb b/test/unit/gateways/adyen_test.rb
new file mode 100644
index 00000000000..3fcf38c3fbe
--- /dev/null
+++ b/test/unit/gateways/adyen_test.rb
@@ -0,0 +1,396 @@
+require 'test_helper'
+
+class AdyenTest < Test::Unit::TestCase
+ include CommStub
+
+ def setup
+ @gateway = AdyenGateway.new(
+ username: 'ws@adyenmerchant.com',
+ password: 'password',
+ merchant_account: 'merchantAccount'
+ )
+
+ @credit_card = credit_card('4111111111111111',
+ :month => 8,
+ :year => 2018,
+ :first_name => 'Test',
+ :last_name => 'Card',
+ :verification_value => '737',
+ :brand => 'visa'
+ )
+
+ @amount = 100
+
+ @options = {
+ billing_address: address(),
+ shopper_reference: "John Smith",
+ order_id: '345123'
+ }
+ end
+
+ # Subdomains are only valid for production gateways, so the test_url check must be manually bypassed for this test to pass.
+ # def test_subdomain_specification
+ # gateway = AdyenGateway.new(
+ # username: 'ws@adyenmerchant.com',
+ # password: 'password',
+ # merchant_account: 'merchantAccount',
+ # subdomain: '123-subdomain'
+ # )
+ #
+ # response = stub_comms(gateway) do
+ # gateway.authorize(@amount, @credit_card, @options)
+ # end.check_request do |endpoint, data, headers|
+ # assert_match("https://123-subdomain-pal-live.adyenpayments.com/pal/servlet/Payment/v18/authorise", endpoint)
+ # end.respond_with(successful_authorize_response)
+ #
+ # assert response
+ # assert_success response
+ # end
+
+ def test_successful_authorize
+ @gateway.expects(:ssl_post).returns(successful_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal '#7914775043909934#', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_authorize
+ @gateway.expects(:ssl_post).returns(failed_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_equal 'Expired Card', response.message
+ assert_failure response
+ end
+
+ def test_successful_capture
+ @gateway.expects(:ssl_post).returns(successful_capture_response)
+ response = @gateway.capture(@amount, '7914775043909934')
+ assert_equal '7914775043909934#8814775564188305#', response.authorization
+ assert_success response
+ assert response.test?
+ end
+
+ def test_failed_capture
+ @gateway.expects(:ssl_post).returns(failed_capture_response)
+ response = @gateway.capture(nil, '')
+ assert_nil response.authorization
+ assert_equal 'Original pspReference required for this operation', response.message
+ assert_failure response
+ end
+
+ def test_successful_purchase
+ response = stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end.respond_with(successful_authorize_response, successful_capture_response)
+ assert_success response
+ assert_equal '7914775043909934#8814775564188305#', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_purchase
+ @gateway.expects(:ssl_post).returns(failed_purchase_response)
+
+ response = @gateway.purchase(@amount, credit_card('400111'), @options)
+ assert_failure response
+
+ assert_equal AdyenGateway::STANDARD_ERROR_CODE[:incorrect_number], response.error_code
+ end
+
+ def test_successful_refund
+ @gateway.expects(:ssl_post).returns(successful_refund_response)
+ response = @gateway.refund(@amount, '7914775043909934')
+ assert_equal '7914775043909934#8514775559925128#', response.authorization
+ assert_equal '[refund-received]', response.message
+ assert response.test?
+ end
+
+ def test_successful_refund_with_compound_psp_reference
+ @gateway.expects(:ssl_post).returns(successful_refund_response)
+ response = @gateway.refund(@amount, '7914775043909934#8514775559000000')
+ assert_equal '7914775043909934#8514775559925128#', response.authorization
+ assert_equal '[refund-received]', response.message
+ assert response.test?
+ end
+
+ def test_failed_refund
+ @gateway.expects(:ssl_post).returns(failed_refund_response)
+ response = @gateway.refund(@amount, '')
+ assert_nil response.authorization
+ assert_equal 'Original pspReference required for this operation', response.message
+ assert_failure response
+ end
+
+ def test_successful_void
+ @gateway.expects(:ssl_post).returns(successful_void_response)
+ response = @gateway.void('7914775043909934')
+ assert_equal '7914775043909934#8614775821628806#', response.authorization
+ assert_equal '[cancel-received]', response.message
+ assert response.test?
+ end
+
+ def test_failed_void
+ @gateway.expects(:ssl_post).returns(failed_void_response)
+ response = @gateway.void('')
+ assert_equal 'Original pspReference required for this operation', response.message
+ assert_failure response
+ end
+
+ def test_successful_store
+ @gateway.expects(:ssl_post).returns(successful_store_response)
+ response = @gateway.store(@credit_card, @options)
+ assert_success response
+ assert_equal "#8835205392522157#8315202663743702", response.authorization
+ end
+
+ def test_failed_store
+ @gateway.expects(:ssl_post).returns(failed_store_response)
+ response = @gateway.store(@credit_card, @options)
+ assert_failure response
+ assert_equal 'Refused', response.message
+ end
+
+ def test_successful_verify
+ response = stub_comms do
+ @gateway.verify(@credit_card, @options)
+ end.respond_with(successful_verify_response)
+ assert_success response
+ assert_equal '#7914776426645103#', response.authorization
+ assert_equal 'Authorised', response.message
+ assert response.test?
+ end
+
+ def test_failed_verify
+ response = stub_comms do
+ @gateway.verify(@credit_card, @options)
+ end.respond_with(failed_verify_response)
+ assert_failure response
+ assert_equal '#7914776433387947#', response.authorization
+ assert_equal 'Refused', response.message
+ assert response.test?
+ end
+
+ def test_failed_avs_check_returns_refusal_reason_raw
+ @gateway.expects(:ssl_post).returns(failed_authorize_avs_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal 'Refused | 05 : Do not honor', response.message
+ end
+
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
+ def test_add_address
+ post = {:card => {:billingAddress => {}}}
+ @options[:billing_address].delete(:address1)
+ @options[:billing_address].delete(:address2)
+ @gateway.send(:add_address, post, @options)
+ assert_equal 'N/A', post[:card][:billingAddress][:street]
+ assert_equal 'N/A', post[:card][:billingAddress][:houseNumberOrName]
+ assert_equal @options[:billing_address][:zip], post[:card][:billingAddress][:postalCode]
+ assert_equal @options[:billing_address][:city], post[:card][:billingAddress][:city]
+ assert_equal @options[:billing_address][:state], post[:card][:billingAddress][:stateOrProvince]
+ assert_equal @options[:billing_address][:country], post[:card][:billingAddress][:country]
+ end
+
+ private
+
+ def pre_scrubbed
+ <<-PRE_SCRUBBED
+ opening connection to pal-test.adyen.com:443...
+ opened
+ starting SSL for pal-test.adyen.com:443...
+ SSL established
+ <- "POST /pal/servlet/Payment/v18/authorise HTTP/1.1\r\nContent-Type: application/json\r\nAuthorization: Basic d3NfMTYzMjQ1QENvbXBhbnkuRGFuaWVsYmFra2Vybmw6eXU0aD50ZlxIVEdydSU1PDhxYTVMTkxVUw==\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: pal-test.adyen.com\r\nContent-Length: 308\r\n\r\n"
+ <- "{\"merchantAccount\":\"DanielbakkernlNL\",\"reference\":\"345123\",\"amount\":{\"value\":\"100\",\"currency\":\"USD\"},\"card\":{\"expiryMonth\":8,\"expiryYear\":2018,\"holderName\":\"John Smith\",\"number\":\"4111111111111111\",\"cvc\":\"737\"},\"shopperEmail\":\"john.smith@test.com\",\"shopperIP\":\"77.110.174.153\",\"shopperReference\":\"John Smith\"}"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "Date: Thu, 27 Oct 2016 11:37:13 GMT\r\n"
+ -> "Server: Apache\r\n"
+ -> "Set-Cookie: JSESSIONID=C0D66C19173B3491D862B8FDBFD72FD7.test3e; Path=/pal/; Secure; HttpOnly\r\n"
+ -> "pspReference: 8514775682339577\r\n"
+ -> "Connection: close\r\n"
+ -> "Transfer-Encoding: chunked\r\n"
+ -> "Content-Type: application/json;charset=utf-8\r\n"
+ -> "\r\n"
+ -> "50\r\n"
+ reading 80 bytes...
+ -> ""
+ -> "{\"pspReference\":\"8514775682339577\",\"resultCode\":\"Authorised\",\"authCode\":\"31845\"}"
+ read 80 bytes
+ reading 2 bytes...
+ -> ""
+ -> "\r\n"
+ read 2 bytes
+ -> "0\r\n"
+ -> "\r\n"
+ Conn close
+ PRE_SCRUBBED
+ end
+
+ def post_scrubbed
+ <<-POST_SCRUBBED
+ opening connection to pal-test.adyen.com:443...
+ opened
+ starting SSL for pal-test.adyen.com:443...
+ SSL established
+ <- "POST /pal/servlet/Payment/v18/authorise HTTP/1.1\r\nContent-Type: application/json\r\nAuthorization: Basic [FILTERED]==\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: pal-test.adyen.com\r\nContent-Length: 308\r\n\r\n"
+ <- "{\"merchantAccount\":\"DanielbakkernlNL\",\"reference\":\"345123\",\"amount\":{\"value\":\"100\",\"currency\":\"USD\"},\"card\":{\"expiryMonth\":8,\"expiryYear\":2018,\"holderName\":\"John Smith\",\"number\":\"[FILTERED]\",\"cvc\":\"[FILTERED]\"},\"shopperEmail\":\"john.smith@test.com\",\"shopperIP\":\"77.110.174.153\",\"shopperReference\":\"John Smith\"}"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "Date: Thu, 27 Oct 2016 11:37:13 GMT\r\n"
+ -> "Server: Apache\r\n"
+ -> "Set-Cookie: JSESSIONID=C0D66C19173B3491D862B8FDBFD72FD7.test3e; Path=/pal/; Secure; HttpOnly\r\n"
+ -> "pspReference: 8514775682339577\r\n"
+ -> "Connection: close\r\n"
+ -> "Transfer-Encoding: chunked\r\n"
+ -> "Content-Type: application/json;charset=utf-8\r\n"
+ -> "\r\n"
+ -> "50\r\n"
+ reading 80 bytes...
+ -> ""
+ -> "{\"pspReference\":\"8514775682339577\",\"resultCode\":\"Authorised\",\"authCode\":\"31845\"}"
+ read 80 bytes
+ reading 2 bytes...
+ -> ""
+ -> "\r\n"
+ read 2 bytes
+ -> "0\r\n"
+ -> "\r\n"
+ Conn close
+ POST_SCRUBBED
+ end
+
+ def failed_purchase_response
+ <<-RESPONSE
+ {
+ "status": 422,
+ "errorCode": "101",
+ "message": "Invalid card number",
+ "errorType": "validation",
+ "pspReference": "8514775645144049"
+ }
+ RESPONSE
+ end
+
+ def successful_authorize_response
+ <<-RESPONSE
+ {
+ "pspReference":"7914775043909934",
+ "resultCode":"Authorised",
+ "authCode":"50055"
+ }
+ RESPONSE
+ end
+
+ def failed_authorize_response
+ <<-RESPONSE
+ {
+ "pspReference": "8514775559925128",
+ "refusalReason": "Expired Card",
+ "resultCode": "Refused"
+ }
+ RESPONSE
+ end
+
+ def successful_capture_response
+ <<-RESPONSE
+ {
+ "pspReference": "8814775564188305",
+ "response": "[capture-received]"
+ }
+ RESPONSE
+ end
+
+ def failed_capture_response
+ <<-RESPONSE
+ {
+ "status": 422,
+ "errorCode": "167",
+ "message": "Original pspReference required for this operation",
+ "errorType": "validation"
+ }
+ RESPONSE
+ end
+
+ def successful_refund_response
+ <<-RESPONSE
+ {
+ "pspReference": "8514775559925128",
+ "response": "[refund-received]"
+ }
+ RESPONSE
+ end
+
+ def failed_refund_response
+ <<-RESPONSE
+ {
+ "status":422,
+ "errorCode":"167",
+ "message":"Original pspReference required for this operation",
+ "errorType":"validation"
+ }
+ RESPONSE
+ end
+
+ def successful_void_response
+ <<-RESPONSE
+ {
+ "pspReference":"8614775821628806",
+ "response":"[cancel-received]"
+ }
+ RESPONSE
+ end
+
+ def failed_void_response
+ <<-RESPONSE
+ {
+ "status":422,
+ "errorCode":"167",
+ "message":"Original pspReference required for this operation",
+ "errorType":"validation"
+ }
+ RESPONSE
+ end
+
+ def successful_verify_response
+ <<-RESPONSE
+ {
+ "pspReference":"7914776426645103",
+ "resultCode":"Authorised",
+ "authCode":"31265"
+ }
+ RESPONSE
+ end
+
+ def failed_verify_response
+ <<-RESPONSE
+ {
+ "pspReference":"7914776433387947",
+ "refusalReason":"Refused",
+ "resultCode":"Refused"
+ }
+ RESPONSE
+ end
+
+ def failed_authorize_avs_response
+ <<-RESPONSE
+ {\"additionalData\":{\"cvcResult\":\"0 Unknown\",\"fraudResultType\":\"GREEN\",\"avsResult\":\"3 AVS unavailable\",\"fraudManualReview\":\"false\",\"avsResultRaw\":\"U\",\"refusalReasonRaw\":\"05 : Do not honor\",\"authorisationMid\":\"494619000001174\",\"acquirerCode\":\"AdyenVisa_BR_494619\",\"acquirerReference\":\"802320302458\",\"acquirerAccountCode\":\"AdyenVisa_BR_Cabify\"},\"fraudResult\":{\"accountScore\":0,\"results\":[{\"FraudCheckResult\":{\"accountScore\":0,\"checkId\":46,\"name\":\"DistinctCountryUsageByShopper\"}}]},\"pspReference\":\"1715167376763498\",\"refusalReason\":\"Refused\",\"resultCode\":\"Refused\"}
+ RESPONSE
+ end
+
+ def successful_store_response
+ <<-RESPONSE
+ {"additionalData":{"recurring.recurringDetailReference":"8315202663743702","recurring.shopperReference":"John Smith"},"pspReference":"8835205392522157","resultCode":"Authorised","authCode":"94571"}
+ RESPONSE
+ end
+
+ def failed_store_response
+ <<-RESPONSE
+ {"pspReference":"8835205393394754","refusalReason":"Refused","resultCode":"Refused"}
+ RESPONSE
+ end
+end
diff --git a/test/unit/gateways/authorize_net_cim_test.rb b/test/unit/gateways/authorize_net_cim_test.rb
index 75e41ea4a05..472a5878d2a 100644
--- a/test/unit/gateways/authorize_net_cim_test.rb
+++ b/test/unit/gateways/authorize_net_cim_test.rb
@@ -509,6 +509,8 @@ def test_should_create_customer_profile_transaction_auth_capture_and_then_refund
# http://www.modernbill.com/support/manual/old/v4/adminhelp/english/Configuration/Payment_Settings/Gateway_API/AuthorizeNet/Module_Authorize.net.htm
assert_failure response
assert_equal 'The referenced transaction does not meet the criteria for issuing a credit.', response.params['direct_response']['message']
+ assert_equal 'The transaction was unsuccessful.', response.message
+ assert_equal "E00027", response.error_code
return response
end
@@ -583,7 +585,7 @@ def test_should_create_customer_profile_transaction_for_refund_request
assert_equal 'This transaction has been approved.', response.params['direct_response']['message']
end
- def test_should_create_customer_profile_trasnaction_passing_recurring_flag
+ def test_should_create_customer_profile_transaction_passing_recurring_flag
response = stub_comms do
@gateway.create_customer_profile_transaction(
:transaction => {
@@ -618,6 +620,22 @@ def test_full_or_masked_card_number
assert_equal 'XXXX1234', @gateway.send(:full_or_masked_card_number, '1234')
end
+ def test_multiple_errors_when_creating_customer_profile
+ @gateway.expects(:ssl_post).returns(unsuccessful_create_customer_profile_transaction_response_with_multiple_errors(:refund))
+ assert response = @gateway.create_customer_profile_transaction(
+ :transaction => {
+ :type => :refund,
+ :amount => 1,
+
+ :customer_profile_id => @customer_profile_id,
+ :customer_payment_profile_id => @customer_payment_profile_id,
+ :trans_id => 1
+ }
+ )
+ assert_equal 'The transaction was unsuccessful.', response.message
+ assert_equal 'E00027', response.error_code
+ end
+
private
def get_auth_only_response
@@ -1056,4 +1074,26 @@ def unsuccessful_create_customer_profile_transaction_response(transaction_type)
XML
end
+ def unsuccessful_create_customer_profile_transaction_response_with_multiple_errors(transaction_type)
+ <<-XML
+
+
+
+ Error
+
+ E00027
+ The transaction was unsuccessful.
+
+
+ E00001
+ An error occurred during processing. Please try again.
+
+
+ #{UNSUCCESSUL_DIRECT_RESPONSE[transaction_type]}
+
+ XML
+ end
end
diff --git a/test/unit/gateways/authorize_net_test.rb b/test/unit/gateways/authorize_net_test.rb
index 97c9ec62f1c..938ca3e7697 100644
--- a/test/unit/gateways/authorize_net_test.rb
+++ b/test/unit/gateways/authorize_net_test.rb
@@ -28,6 +28,25 @@ def setup
billing_address: address,
description: 'Store Purchase'
}
+
+ @additional_options = {
+ line_items: [
+ {
+ item_id: "1",
+ name: "mug",
+ description: "coffee",
+ quantity: "100",
+ unit_price: "10"
+ },
+ {
+ item_id: "2",
+ name: "vase",
+ description: "floral",
+ quantity: "200",
+ unit_price: "20"
+ }
+ ]
+ }
end
def test_add_swipe_data_with_bad_data
@@ -274,13 +293,52 @@ def test_successful_purchase_with_utf_character
def test_passes_partial_auth
stub_comms do
- @gateway.purchase(100, credit_card, disable_partial_auth: true)
+ @gateway.purchase(@amount, credit_card, disable_partial_auth: true)
end.check_request do |endpoint, data, headers|
assert_match(/allowPartialAuth<\/settingName>/, data)
assert_match(/false<\/settingValue>/, data)
end.respond_with(successful_purchase_response)
end
+ def test_passes_email_customer
+ stub_comms do
+ @gateway.purchase(@amount, credit_card, email_customer: true)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/emailCustomer<\/settingName>/, data)
+ assert_match(/true<\/settingValue>/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
+ def test_passes_header_email_receipt
+ stub_comms do
+ @gateway.purchase(@amount, credit_card, header_email_receipt: "yet another field")
+ end.check_request do |endpoint, data, headers|
+ assert_match(/headerEmailReceipt<\/settingName>/, data)
+ assert_match(/yet another field<\/settingValue>/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
+ def test_passes_line_items
+ stub_comms do
+ @gateway.purchase(@amount, credit_card, @options.merge(@additional_options))
+ end.check_request do |endpoint, data, headers|
+ assert_match(//, data)
+ assert_match(//, data)
+ assert_match(/#{@additional_options[:line_items][0][:item_id]}<\/itemId>/, data)
+ assert_match(/#{@additional_options[:line_items][0][:name]}<\/name>/, data)
+ assert_match(/#{@additional_options[:line_items][0][:description]}<\/description>/, data)
+ assert_match(/#{@additional_options[:line_items][0][:quantity]}<\/quantity>/, data)
+ assert_match(/#{@additional_options[:line_items][0][:unit_price]}<\/unitPrice>/, data)
+ assert_match(/<\/lineItem>/, data)
+ assert_match(/#{@additional_options[:line_items][1][:item_id]}<\/itemId>/, data)
+ assert_match(/#{@additional_options[:line_items][1][:name]}<\/name>/, data)
+ assert_match(/#{@additional_options[:line_items][1][:description]}<\/description>/, data)
+ assert_match(/#{@additional_options[:line_items][1][:quantity]}<\/quantity>/, data)
+ assert_match(/#{@additional_options[:line_items][1][:unit_price]}<\/unitPrice>/, data)
+ assert_match(/<\/lineItems>/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
def test_failed_purchase
@gateway.expects(:ssl_post).returns(failed_purchase_response)
@@ -491,6 +549,29 @@ def test_failed_refund_using_stored_card
assert_equal "The record cannot be found", refund.message
end
+ def test_failed_refund_due_to_unsettled_payment
+ @gateway.expects(:ssl_post).returns(failed_refund_for_unsettled_payment_response)
+ @gateway.expects(:void).never
+
+ @gateway.refund(36.40, '2214269051#XXXX1234')
+ end
+
+ def test_failed_full_refund_due_to_unsettled_payment_forces_void
+ @gateway.expects(:ssl_post).returns(failed_refund_for_unsettled_payment_response)
+ @gateway.expects(:void).once
+
+ @gateway.refund(36.40, '2214269051#XXXX1234', force_full_refund_if_unsettled: true)
+ end
+
+ def test_failed_full_refund_returns_failed_response_if_reason_code_is_not_unsettled_error
+ @gateway.expects(:ssl_post).returns(failed_refund_response)
+ @gateway.expects(:void).never
+
+ response = @gateway.refund(36.40, '2214269051#XXXX1234', force_full_refund_if_unsettled: true)
+ assert response.present?
+ assert_failure response
+ end
+
def test_successful_store
@gateway.expects(:ssl_post).returns(successful_store_response)
@@ -510,6 +591,27 @@ def test_failed_store
assert_equal("15", store.params["message_code"])
end
+ def test_successful_unstore
+ response = stub_comms do
+ @gateway.unstore('35959426#32506918#cim_store')
+ end.check_request do |endpoint, data, headers|
+ doc = parse(data)
+ assert_equal "35959426", doc.at_xpath("//deleteCustomerProfileRequest/customerProfileId").content
+ end.respond_with(successful_unstore_response)
+
+ assert_success response
+ assert_equal "Successful", response.message
+ end
+
+ def test_failed_unstore
+ @gateway.expects(:ssl_post).returns(failed_unstore_response)
+
+ unstore = @gateway.unstore('35959426#32506918#cim_store')
+ assert_failure unstore
+ assert_match(/The record cannot be found/, unstore.message)
+ assert_equal("40", unstore.params["message_code"])
+ end
+
def test_successful_store_new_payment_profile
@gateway.expects(:ssl_post).returns(successful_store_new_payment_profile_response)
@@ -544,9 +646,49 @@ def test_address
end.respond_with(successful_authorize_response)
end
+ def test_address_with_empty_billing_address
+ stub_comms do
+ @gateway.authorize(@amount, @credit_card)
+ end.check_request do |endpoint, data, headers|
+ parse(data) do |doc|
+ assert_equal "", doc.at_xpath("//billTo/address").content, data
+ assert_equal "", doc.at_xpath("//billTo/city").content, data
+ assert_equal "n/a", doc.at_xpath("//billTo/state").content, data
+ assert_equal "", doc.at_xpath("//billTo/zip").content, data
+ assert_equal "", doc.at_xpath("//billTo/country").content, data
+ end
+ end.respond_with(successful_authorize_response)
+ end
+
+ def test_address_with_address2_present
+ stub_comms do
+ @gateway.authorize(@amount, @credit_card, billing_address: {address1: '164 Waverley Street', address2: 'Apt 1234', country: 'US', state: 'CO', phone: '(555)555-5555', fax: '(555)555-4444'})
+ end.check_request do |endpoint, data, headers|
+ parse(data) do |doc|
+ assert_equal "CO", doc.at_xpath("//billTo/state").content, data
+ assert_equal "164 Waverley Street Apt 1234", doc.at_xpath("//billTo/address").content, data
+ assert_equal "US", doc.at_xpath("//billTo/country").content, data
+ assert_equal "(555)555-5555", doc.at_xpath("//billTo/phoneNumber").content
+ assert_equal "(555)555-4444", doc.at_xpath("//billTo/faxNumber").content
+ end
+ end.respond_with(successful_authorize_response)
+ end
+
+ def test_address_north_america_with_defaults
+ stub_comms do
+ @gateway.authorize(@amount, @credit_card, billing_address: {address1: '164 Waverley Street', country: 'US'})
+ end.check_request do |endpoint, data, headers|
+ parse(data) do |doc|
+ assert_equal "NC", doc.at_xpath("//billTo/state").content, data
+ assert_equal "164 Waverley Street", doc.at_xpath("//billTo/address").content, data
+ assert_equal "US", doc.at_xpath("//billTo/country").content, data
+ end
+ end.respond_with(successful_authorize_response)
+ end
+
def test_address_outsite_north_america
stub_comms do
- @gateway.authorize(@amount, @credit_card, billing_address: {address1: '164 Waverley Street', country: 'DE', state: ''})
+ @gateway.authorize(@amount, @credit_card, billing_address: {address1: '164 Waverley Street', country: 'DE'})
end.check_request do |endpoint, data, headers|
parse(data) do |doc|
assert_equal "n/a", doc.at_xpath("//billTo/state").content, data
@@ -556,6 +698,18 @@ def test_address_outsite_north_america
end.respond_with(successful_authorize_response)
end
+ def test_address_outsite_north_america_with_address2_present
+ stub_comms do
+ @gateway.authorize(@amount, @credit_card, billing_address: {address1: '164 Waverley Street', address2: 'Apt 1234', country: 'DE'})
+ end.check_request do |endpoint, data, headers|
+ parse(data) do |doc|
+ assert_equal "n/a", doc.at_xpath("//billTo/state").content, data
+ assert_equal "164 Waverley Street Apt 1234", doc.at_xpath("//billTo/address").content, data
+ assert_equal "DE", doc.at_xpath("//billTo/country").content, data
+ end
+ end.respond_with(successful_authorize_response)
+ end
+
def test_duplicate_window
stub_comms do
@gateway.purchase(@amount, @credit_card, duplicate_window: 0)
@@ -567,7 +721,9 @@ def test_duplicate_window
def test_duplicate_window_class_attribute_deprecated
@gateway.class.duplicate_window = 0
assert_deprecation_warning("Using the duplicate_window class_attribute is deprecated. Use the transaction options hash instead.") do
- @gateway.purchase(@amount, @credit_card)
+ stub_comms do
+ @gateway.purchase(@amount, @credit_card)
+ end.respond_with(successful_purchase_response)
end
ensure
@gateway.class.duplicate_window = nil
@@ -670,7 +826,7 @@ def test_response_under_review_by_fraud_service
@gateway.expects(:ssl_post).returns(fraud_review_response)
response = @gateway.purchase(@amount, @credit_card)
- assert_failure response
+ assert_success response
assert response.fraud_review?
assert_equal "Thank you! For security reasons your order is currently being reviewed", response.message
end
@@ -680,6 +836,16 @@ def test_avs_result
response = @gateway.purchase(@amount, @credit_card)
assert_equal 'X', response.avs_result['code']
+ assert_equal 'Y', response.avs_result['street_match']
+ assert_equal 'Y', response.avs_result['postal_match']
+
+
+ @gateway.expects(:ssl_post).returns(address_not_provided_avs_response)
+
+ response = @gateway.purchase(@amount, @credit_card)
+ assert_equal 'I', response.avs_result['code']
+ assert_equal nil, response.avs_result['street_match']
+ assert_equal nil, response.avs_result['postal_match']
end
def test_cvv_result
@@ -742,7 +908,19 @@ def test_include_cust_id_for_numeric_values
end.respond_with(successful_authorize_response)
end
- def test_dont_include_cust_id_for_non_numeric_values
+ def test_include_cust_id_for_word_character_values
+ stub_comms do
+ @gateway.purchase(@amount, @credit_card, customer: "4840_TT")
+ end.check_request do |endpoint, data, headers|
+ parse(data) do |doc|
+ assert_not_nil doc.at_xpath("//customer/id"), data
+ assert_equal "4840_TT", doc.at_xpath("//customer/id").content, data
+ assert_equal "1.00", doc.at_xpath("//transactionRequest/amount").content
+ end
+ end.respond_with(successful_authorize_response)
+ end
+
+ def test_dont_include_cust_id_for_email_addresses
stub_comms do
@gateway.purchase(@amount, @credit_card, customer: "bob@test.com")
end.check_request do |endpoint, data, headers|
@@ -752,6 +930,16 @@ def test_dont_include_cust_id_for_non_numeric_values
end.respond_with(successful_authorize_response)
end
+ def test_dont_include_cust_id_for_phone_numbers
+ stub_comms do
+ @gateway.purchase(@amount, @credit_card, customer: "111-123-1231")
+ end.check_request do |endpoint, data, headers|
+ doc = parse(data)
+ assert !doc.at_xpath("//customer/id"), data
+ assert_equal "1.00", doc.at_xpath("//transactionRequest/amount").content
+ end.respond_with(successful_authorize_response)
+ end
+
def test_includes_shipping_name_when_different_from_billing_name
card = credit_card('4242424242424242',
first_name: "billing",
@@ -895,6 +1083,23 @@ def test_successful_apple_pay_authorization_with_network_tokenization
assert_equal '508141794', response.authorization.split('#')[0]
end
+ def test_failed_apple_pay_authorization_with_network_tokenization_not_supported
+ credit_card = network_tokenization_credit_card('4242424242424242',
+ :payment_cryptogram => "111111111100cryptogram"
+ )
+
+ response = stub_comms do
+ @gateway.authorize(@amount, credit_card)
+ end.check_request do |endpoint, data, headers|
+ parse(data) do |doc|
+ assert_equal credit_card.payment_cryptogram, doc.at_xpath("//creditCard/cryptogram").content
+ assert_equal credit_card.number, doc.at_xpath("//creditCard/cardNumber").content
+ end
+ end.respond_with(network_tokenization_not_supported_response)
+
+ assert_equal Gateway::STANDARD_ERROR_CODE[:unsupported_feature], response.error_code
+ end
+
def test_supports_network_tokenization_true
response = stub_comms do
@gateway.supports_network_tokenization?
@@ -1099,6 +1304,43 @@ def fraud_review_response
eos
end
+ def address_not_provided_avs_response
+ <<-eos
+
+
+ 1
+
+ Ok
+
+ I00001
+ Successful.
+
+
+
+ 4
+ GSOFTZ
+ B
+ M
+ 2
+ 508141795
+
+ 655D049EE60E1766C9C28EB47CFAA389
+ 0
+ XXXX2224
+ Visa
+
+
+ 1
+ Thank you! For security reasons your order is currently being reviewed
+
+
+
+
+ eos
+ end
+
def no_match_cvv_response
<<-eos
@@ -1747,6 +1989,36 @@ def failed_store_response
eos
end
+ def successful_unstore_response
+ <<-eos
+
+
+
+ Ok
+
+ I00001
+ Successful.
+
+
+
+ eos
+ end
+
+ def failed_unstore_response
+ <<-eos
+
+
+
+ Error
+
+ E00040
+ The record cannot be found.
+
+
+
+ eos
+ end
+
def successful_store_new_payment_profile_response
<<-eos
@@ -2010,4 +2282,39 @@ def credentials_are_bogus_response
eos
end
+ def failed_refund_for_unsettled_payment_response
+ <<-eos
+
+
+
+ Error
+
+ E00027
+ The transaction was unsuccessful.
+
+
+
+ 3
+
+ P
+
+
+ 0
+
+
+ 0
+ XXXX0001
+ Visa
+
+
+ 54
+ The referenced transaction does not meet the criteria for issuing a credit.
+
+
+
+
+
+
+ eos
+ end
end
diff --git a/test/unit/gateways/barclaycard_smartpay_test.rb b/test/unit/gateways/barclaycard_smartpay_test.rb
index 747babdd8ac..23fc6d9238c 100644
--- a/test/unit/gateways/barclaycard_smartpay_test.rb
+++ b/test/unit/gateways/barclaycard_smartpay_test.rb
@@ -1,6 +1,8 @@
require 'test_helper'
class BarclaycardSmartpayTest < Test::Unit::TestCase
+ include CommStub
+
def setup
@gateway = BarclaycardSmartpayGateway.new(
company: 'company',
@@ -9,6 +11,7 @@ def setup
)
@credit_card = credit_card
+ @three_ds_enrolled_card = credit_card('4212345678901237', brand: :visa)
@amount = 100
@options = {
@@ -17,7 +20,77 @@ def setup
description: 'Store Purchase'
}
- @avs_address = @options
+ @options_with_alternate_address = {
+ order_id: '1',
+ billing_address: {
+ name: 'PU JOI SO',
+ address1: '新北市店溪路3579號139樓',
+ company: 'Widgets Inc',
+ city: '新北市',
+ zip: '231509',
+ country: 'TW',
+ phone: '(555)555-5555',
+ fax: '(555)555-6666'
+ },
+ email: 'pujoi@so.com',
+ customer: 'PU JOI SO',
+ description: 'Store Purchase'
+ }
+
+ @options_with_house_number_and_street = {
+ order_id: '1',
+ street: 'Top Level Drive',
+ house_number: '1000',
+ billing_address: address,
+ description: 'Store Purchase'
+ }
+
+ @options_with_shipping_house_number_and_shipping_street = {
+ order_id: '1',
+ street: 'Top Level Drive',
+ house_number: '1000',
+ billing_address: address,
+ shipping_house_number: '999',
+ shipping_street: 'Downtown Loop',
+ shipping_address: {
+ name: 'PU JOI SO',
+ address1: '新北市店溪路3579號139樓',
+ company: 'Widgets Inc',
+ city: '新北市',
+ zip: '231509',
+ country: 'TW',
+ phone: '(555)555-5555',
+ fax: '(555)555-6666'
+ },
+ description: 'Store Purchase'
+ }
+
+ @options_with_credit_fields = {
+ order_id: '1',
+ billing_address: {
+ name: 'Jim Smith',
+ address1: '100 Street',
+ company: 'Widgets Inc',
+ city: 'Ottawa',
+ state: 'ON',
+ zip: 'K1C2N6',
+ country: 'CA',
+ phone: '(555)555-5555',
+ fax: '(555)555-6666'},
+ email: 'long@bob.com',
+ customer: 'Longbob Longsen',
+ description: 'Store Purchase',
+ date_of_birth: '1990-10-11',
+ entity_type: 'NaturalPerson',
+ nationality: 'US',
+ shopper_name: {
+ firstName: 'Longbob',
+ lastName: 'Longsen',
+ gender: 'MALE'
+ }
+ }
+
+ @avs_address = @options.clone
@avs_address.update(billing_address: {
name: 'Jim Smith',
street: 'Test AVS result',
@@ -29,6 +102,61 @@ def setup
})
end
+ def test_successful_purchase
+ response = stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end.respond_with(successful_authorize_response, successful_capture_response)
+
+ assert_success response
+ assert_equal '7914002629995504#8814002632606717', response.authorization
+ assert response.test?
+ end
+
+ def test_successful_authorize_with_alternate_address
+ response = stub_comms do
+ @gateway.authorize(@amount, @credit_card, @options_with_alternate_address)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/billingAddress.houseNumberOrName=%E6%96%B0%E5%8C%97%E5%B8%82%E5%BA%97%E6%BA%AA%E8%B7%AF3579%E8%99%9F139%E6%A8%93/, data)
+ assert_match(/billingAddress.street=Not\+Provided/, data)
+ end.respond_with(successful_authorize_response)
+
+ assert_success response
+ assert_equal '7914002629995504', response.authorization
+ assert response.test?
+ end
+
+ def test_successful_authorize_with_house_number_and_street
+ response = stub_comms do
+ @gateway.authorize(@amount,
+ @credit_card,
+ @options_with_house_number_and_street)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/billingAddress.street=Top\+Level\+Drive/, data)
+ assert_match(/billingAddress.houseNumberOrName=1000/, data)
+ end.respond_with(successful_authorize_response)
+
+ assert response
+ assert_success response
+ assert_equal '7914002629995504', response.authorization
+ end
+
+ def test_successful_authorize_with_shipping_house_number_and_street
+ response = stub_comms do
+ @gateway.authorize(@amount,
+ @credit_card,
+ @options_with_shipping_house_number_and_shipping_street)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/billingAddress.street=Top\+Level\+Drive/, data)
+ assert_match(/billingAddress.houseNumberOrName=1000/, data)
+ assert_match(/deliveryAddress.street=Downtown\+Loop/, data)
+ assert_match(/deliveryAddress.houseNumberOrName=999/, data)
+ end.respond_with(successful_authorize_response)
+
+ assert response
+ assert_success response
+ assert_equal '7914002629995504', response.authorization
+ end
+
def test_successful_authorize
@gateway.stubs(:ssl_post).returns(successful_authorize_response)
@@ -38,6 +166,18 @@ def test_successful_authorize
assert response.test?
end
+ def test_successful_authorize_with_3ds
+ @gateway.stubs(:ssl_post).returns(successful_authorize_with_3ds_response)
+
+ response = @gateway.authorize(@amount, @three_ds_enrolled_card, @options)
+
+ assert_equal '8815161318854998', response.authorization
+ refute response.params['issuerUrl'].blank?
+ refute response.params['md'].blank?
+ refute response.params['paRequest'].blank?
+ assert response.test?
+ end
+
def test_failed_authorize
@gateway.stubs(:ssl_post).returns(failed_authorize_response)
@@ -51,6 +191,7 @@ def test_successful_capture
response = @gateway.capture(@amount, '7914002629995504', @options)
assert_success response
+ assert_equal '7914002629995504#8814002632606717', response.authorization
assert response.test?
end
@@ -62,13 +203,27 @@ def test_failed_capture
assert response.test?
end
- def test_successful_refund
- @gateway.expects(:ssl_post).returns(successful_refund_response)
+ def test_legacy_capture_psp_reference_passed_for_refund
+ response = stub_comms do
+ @gateway.refund(@amount, '8814002632606717', @options)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/originalReference=8814002632606717/, data)
+ end.respond_with(successful_refund_response)
- response = @gateway.refund(@amount, '7914002629995504', @options)
assert_success response
assert response.test?
+ end
+
+ def test_successful_refund
+ response = stub_comms do
+ @gateway.refund(@amount, '7914002629995504#8814002632606717', @options)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/originalReference=7914002629995504&/, data)
+ assert_no_match(/8814002632606717/, data)
+ end.respond_with(successful_refund_response)
+ assert_success response
+ assert response.test?
end
def test_failed_refund
@@ -79,6 +234,34 @@ def test_failed_refund
assert response.test?
end
+ def test_successful_credit
+ @gateway.expects(:ssl_post).returns(successful_credit_response)
+
+ response = @gateway.credit(@amount, @credit_card, @options)
+ assert_success response
+ end
+
+ def test_failed_credit
+ @gateway.expects(:ssl_post).returns(failed_credit_response)
+
+ response = @gateway.credit(nil, @credit_card, @options)
+ assert_failure response
+ end
+
+ def test_credit_contains_all_fields
+ response = stub_comms do
+ @gateway.credit(@amount, @credit_card, @options_with_credit_fields)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/dateOfBirth=1990-10-11&/, data)
+ assert_match(/entityType=NaturalPerson&/, data)
+ assert_match(/nationality=US&/, data)
+ assert_match(/shopperName.firstName=Longbob&/, data)
+ end.respond_with(successful_credit_response)
+
+ assert_success response
+ assert response.test?
+ end
+
def test_successful_void
@gateway.expects(:ssl_post).returns(successful_void_response)
@@ -102,15 +285,26 @@ def test_unsuccessful_verify
assert_equal "Refused", response.message
end
- def test_fractional_currency
- @gateway.expects(:ssl_post).returns(successful_authorize_response)
- @gateway.expects(:post_data).with do |params|
- '100' == params['amount.value'] && 'JPY' == params['amount.currency']
- end
+ def test_authorize_nonfractional_currency
+ response = stub_comms do
+ @gateway.authorize(@amount, @credit_card, @options.merge(currency: 'JPY'))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/amount.value=1/, data)
+ assert_match(/amount.currency=JPY/, data)
+ end.respond_with(successful_authorize_response)
+
+ assert_success response
+ end
- @options[:currency] = 'JPY'
+ def test_authorize_three_decimal_currency
+ response = stub_comms do
+ @gateway.authorize(@amount, @credit_card, @options.merge(currency: 'OMR'))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/amount.value=100/, data)
+ assert_match(/amount.currency=OMR/, data)
+ end.respond_with(successful_authorize_response)
- @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
end
def test_successful_store
@@ -132,6 +326,7 @@ def test_avs_result
@gateway.expects(:ssl_post).returns(failed_avs_response)
response = @gateway.authorize(@amount, @credit_card, @avs_address)
+ assert_failure response
assert_equal "N", response.avs_result['code']
assert response.test?
end
@@ -146,6 +341,10 @@ def successful_authorize_response
'pspReference=7914002629995504&authCode=56469&resultCode=Authorised'
end
+ def successful_authorize_with_3ds_response
+ "pspReference=8815161318854998&resultCode=RedirectShopper&issuerUrl=https%3A%2F%2Ftest.adyen.com%2Fhpp%2F3d%2Fvalidate.shtml&md=WIFa2sF3CuPyN53Txjt3U%2F%2BDuCsddzywiY5NLgEAdUAXPksHUzXL5E%2BsfvdpolkGWR8b1oh%2FNA3jNaUP9UCgfjhXqRslGFy9OGqcZ1ITMz54HHm%2FlsCKN9bTftKnYA4F7GqvOgcIIrinUZjbMvW9doGifwzSqYLo6ASOm6bARL5n7cIFV8IWtA2yPlO%2FztKSTRJt1glN4s8sMcpE57z4soWKMuycbdXdpp6d4ZRSa%2F1TPF0MnJF0zNaSAAkw9JpXqGMOz5sFF2Smpc38HXJzM%2FV%2B1mmoDhhWmXXOb5YQ0QSCS7DXKIcr8ZtuGuGmFp0QOfZiO41%2B2I2N7VhONVx8xSn%2BLu4m6vaDIg5qsnd9saxaWwbJpl9okKm6pB2MJap9ScuBCcvI496BPCrjQ2LHxvDWhk6M3Exemtv942NQIGlsiPaW0KXoC2dQvBsxWh0K&paRequest=eNpVUtuOgjAQ%2FRXj%2B1KKoIWMTVgxWR%2B8RNkPaMpEycrFUlb8%2B20B190%2BnXPm0pnTQnpRiMkJZauQwxabRpxxkmfLacQYDeiczihjgR%2BGbMrhEB%2FxxuEbVZNXJaeO63hAntSUK3kRpeYg5O19s%2BPUm%2FnBHMhIoUC1SXiKjT4URSxvba5QARlkKEWB%2FFSbgbLr41QIpXFVFUB6HWTVllo9OPNMwyeBVl35Reu6iQi53%2B9OM5Y7sipMVqmF1G9tA8QmAnlNeGgtakzjLs%2F4Pjl3u3TtbdNtZzDdJV%2FBPu7PEojNgExo5J5LmUvpfELDyPcjPwDS6yAKOxFffx4nxhXXrDwIUNt74oFQG%2FgrgLFdYSkfPFwws9WTAXZ1VaLJMPb%2BYiCvoVcf1mSpjW%2B%2BN9i8YKFr0MLa3Qdsl9yYREM37NtYAsSWkvElyfjiBv37CT9ySbE1"
+ end
+
def failed_authorize_response
'pspReference=7914002630895750&refusalReason=Refused&resultCode=Refused'
end
@@ -166,6 +365,14 @@ def failed_refund_response
'validation 100 No amount specified'
end
+ def successful_credit_response
+ 'fraudResult.accountScore=70&fraudResult.results.0.accountScore=20&fraudResult.results.0.checkId=2&fraudResult.results.0.name=CardChunkUsage&fraudResult.results.1.accountScore=25&fraudResult.results.1.checkId=4&fraudResult.results.1.name=HolderNameUsage&fraudResult.results.2.accountScore=25&fraudResult.results.2.checkId=8&fraudResult.results.2.name=ShopperEmailUsage&fraudResult.results.3.accountScore=0&fraudResult.results.3.checkId=1&fraudResult.results.3.name=PaymentDetailRefCheck&fraudResult.results.4.accountScore=0&fraudResult.results.4.checkId=13&fraudResult.results.4.name=IssuerRefCheck&fraudResult.results.5.accountScore=0&fraudResult.results.5.checkId=15&fraudResult.results.5.name=IssuingCountryReferral&fraudResult.results.6.accountScore=0&fraudResult.results.6.checkId=26&fraudResult.results.6.name=ShopperEmailRefCheck&fraudResult.results.7.accountScore=0&fraudResult.results.7.checkId=27&fraudResult.results.7.name=PmOwnerRefCheck&fraudResult.results.8.accountScore=0&fraudResult.results.8.checkId=56&fraudResult.results.8.name=ShopperReferenceTrustCheck&fraudResult.results.9.accountScore=0&fraudResult.results.9.checkId=10&fraudResult.results.9.name=HolderNameContainsNumber&fraudResult.results.10.accountScore=0&fraudResult.results.10.checkId=11&fraudResult.results.10.name=HolderNameIsOneWord&fraudResult.results.11.accountScore=0&fraudResult.results.11.checkId=21&fraudResult.results.11.name=EmailDomainValidation&pspReference=8514743049239955&resultCode=Received'
+ end
+
+ def failed_credit_response
+ 'errorType=validation&errorCode=137&message=Invalid+amount+specified&status=422'
+ end
+
def successful_void_response
'pspReference=7914002636728161&response=%5Bcancel-received%5D'
end
diff --git a/test/unit/gateways/barclays_epdq_extra_plus_test.rb b/test/unit/gateways/barclays_epdq_extra_plus_test.rb
index b1f2cb61c61..5e9ff34b2af 100644
--- a/test/unit/gateways/barclays_epdq_extra_plus_test.rb
+++ b/test/unit/gateways/barclays_epdq_extra_plus_test.rb
@@ -103,11 +103,11 @@ def test_successful_authorize
def test_successful_authorize_with_mastercard
@gateway.expects(:add_pair).at_least(1)
- @gateway.expects(:add_pair).with(anything, 'Operation', 'PAU')
+ @gateway.expects(:add_pair).with(anything, 'Operation', 'RES')
@gateway.expects(:ssl_post).returns(successful_purchase_response)
assert response = @gateway.authorize(@amount, @mastercard, @options)
assert_success response
- assert_equal '3014726;PAU', response.authorization
+ assert_equal '3014726;RES', response.authorization
assert response.test?
end
diff --git a/test/unit/gateways/barclays_epdq_test.rb b/test/unit/gateways/barclays_epdq_test.rb
deleted file mode 100644
index 499d877a188..00000000000
--- a/test/unit/gateways/barclays_epdq_test.rb
+++ /dev/null
@@ -1,450 +0,0 @@
-require 'test_helper'
-
-class BarclaysEpdqTest < Test::Unit::TestCase
- def setup
- @gateway = BarclaysEpdqGateway.new(
- :login => 'login',
- :password => 'password',
- :client_id => 'client_id'
- )
-
- @credit_card = credit_card
- @amount = 100
-
- @options = {
- :billing_address => address
- }
- end
-
- def test_successful_purchase
- @gateway.expects(:ssl_post).returns(successful_purchase_response)
-
- assert response = @gateway.purchase(@amount, @credit_card, @options)
- assert_success response
-
- # Replace with authorization number from the successful response
- assert_equal "150127237", response.authorization
- assert response.test?
- end
-
- def test_failed_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_deprecated_credit
- @gateway.expects(:ssl_post).with(anything, regexp_matches(/>asdfasdf)).returns(successful_credit_response)
- assert_deprecation_warning(Gateway::CREDIT_DEPRECATION_MESSAGE) do
- assert_success @gateway.credit(@amount, "asdfasdf:jklljkll")
- end
- end
-
- def test_credit
- @gateway.expects(:ssl_post).with(anything, regexp_matches(/#{@credit_card.number}/)).returns(successful_credit_response)
- assert response = @gateway.credit(@amount, @credit_card)
- assert_success response
- end
-
- def test_refund
- @gateway.expects(:ssl_post).with(anything, regexp_matches(/>asdfasdf)).returns(successful_credit_response)
- assert response = @gateway.refund(@amount, "asdfasdf:jklljkll")
- assert_success response
- end
-
- def test_handling_incorrectly_encoded_message
- @gateway.expects(:ssl_post).returns(incorrectly_encoded_response)
-
- assert_nothing_raised { @gateway.purchase(@amount, @credit_card, @options) }
- end
-
- private
-
- def successful_purchase_response
- %(
-
- 1.0
-
- OrderFormDoc
- 4d45da6a-5e10-3000-002b-00144ff2e45c
-
- Payment
-
-
-
- 3
-
- 32
- Merchant
- CcxBarclaysGbpAuth
- PaymentNormErrors
- 3
- 121
- CcxBarclaysAuthResponseRedirector.cpp
- 10:41:43May 26 2009
- 1
- 3
- Approved.
-
-
-
-
-
-
-
-
-
- Ottawa
-
- K1C2N6
- ON
- 1234 My Street
- Apt 1
-
-
- 4d45da6a-5e12-3000-002b-00144ff2e45c
-
-
-
-
-
-
- 1
- 123
- 09/12
- 4715320629000001
- 1
-
-
- CreditCard
-
-
-
-
- 1296599280954
-
- None
- 0
- 0
-
-
- None
- 1
- My Rules
- 2974
- 0
-
-
-
-
- 0
-
-
- 150127237
- 150127237
- P
-
- 442130
-
- 90003750
-
-
-
- YY
- EX
- 1
- Approved.
- 2
- 22
- 00
- AUTH CODE:442130
- 1
-
-
- 7
-
-
- 3900
-
-
-
-
- 4d45da6a-5e11-3000-002b-00144ff2e45c
- 4
- 7
- 1
- Auth
-
-
-
-
-
- 2974
- 2974
- 2974
- 2974
- spreedlytesting
- XXXXXXX
-
-
-
-
- 1296599280948
- 1296599283885
-
-
-)
- end
-
- def failed_purchase_response
- %(
-
- 1.0
-
- OrderFormDoc
- 4d45da6a-5d6b-3000-002b-00144ff2e45c
-
- Payment
-
-
-
- 3
-
- 32
- Merchant
- CcxBarclaysGbpAuth
- PaymentNormErrors
- 3
- 121
- CcxBarclaysAuthResponseRedirector.cpp
- 10:41:43May 26 2009
- 50
- 3
- Declined (General).
-
-
-
-
-
-
-
-
-
- Ottawa
-
- K1C2N6
- ON
- 1234 My Street
- Apt 1
-
-
- 4d45da6a-5d6d-3000-002b-00144ff2e45c
-
-
-
-
-
-
- 1
- 123
- 09/12
- 4715320629000027
- 1
-
-
- CreditCard
-
-
-
-
- 1296598178436
-
- None
- 0
- 0
-
-
- None
- 1
- My Rules
- 2974
- 0
-
-
-
-
- 0
-
-
- 22394792
- 22394792
- P
-
-
- 90003745
-
-
-
- NY
- B5
- 50
- Declined (General).
- 2
- 24
- 05
- NOT AUTHORISED
- 1
-
-
- 7
-
-
- 4205
-
-
-
-
- 4d45da6a-5d6c-3000-002b-00144ff2e45c
- 4
- 7
- 1
- Auth
-
-
-
-
-
- 2974
- 2974
- 2974
- 2974
- login
- XXXXXXX
-
-
-
-
- 1296598178430
- 1296598179756
-
-
-)
- end
-
- def successful_credit_response
- %(
-
- 1.0
-
- OrderFormDoc
- 4d45da6a-8bcd-3000-002b-00144ff2e45c
-
- Payment
-
-
-
-
-
-
-
-
-
-
- Ottawa
- K1C2N6
- ON
- 1234 My Street
- Apt 1
-
-
- 4d45da6a-8bcc-3000-002b-00144ff2e45c
-
-
-
-
-
-
- 1
- 09/12
- 4715320629000001
-
-
- CreditCard
-
-
-
-
- 1296679499967
-
- None
- 0
- 0
-
-
- None
- 1
- My Rules
- 2974
- 0
-
-
-
-
- 0
-
-
- b92b5bff09d05d771c17e6b6b30531ed
- b92b5bff09d05d771c17e6b6b30531ed
- P
-
-
- 1
- Approved.
- 1
- Approved
- 1
-
-
- 7
- S
-
-
- 3900
-
-
-
-
- 4d45da6a-8bce-3000-002b-00144ff2e45c
- 4
- 7
- 1
- Credit
-
-
-
-
-
- 2974
- 2974
- 2974
- 2974
- spreedlytesting
- XXXXXXX
-
-
-
-
- 1296679499961
- 1296679500312
-
-
-
-)
- end
-
- def incorrectly_encoded_response
- successful_purchase_response.gsub("Ottawa", "\xD6ttawa")
- end
-end
diff --git a/test/unit/gateways/beanstream_test.rb b/test/unit/gateways/beanstream_test.rb
index d57e52afb98..a97990ba636 100644
--- a/test/unit/gateways/beanstream_test.rb
+++ b/test/unit/gateways/beanstream_test.rb
@@ -57,13 +57,36 @@ def setup
end
def test_successful_purchase
- @gateway.expects(:ssl_post).returns(successful_purchase_response)
+ response = stub_comms(@gateway, :ssl_request) do
+ @gateway.purchase(@amount, @decrypted_credit_card, @options)
+ end.check_request do |method, endpoint, data, headers|
+ refute_match(/recurringPayment=true/, data)
+ end.respond_with(successful_purchase_response)
- assert response = @gateway.purchase(@amount, @credit_card, @options)
assert_success response
assert_equal '10000028;15.00;P', response.authorization
end
+ def test_successful_purchase_with_recurring
+ response = stub_comms(@gateway, :ssl_request) do
+ @gateway.purchase(@amount, @decrypted_credit_card, @options.merge(recurring: true))
+ end.check_request do |method, endpoint, data, headers|
+ assert_match(/recurringPayment=true/, data)
+ end.respond_with(successful_purchase_response)
+
+ assert_success response
+ end
+
+ def test_successful_authorize_with_recurring
+ response = stub_comms(@gateway, :ssl_request) do
+ @gateway.authorize(@amount, @decrypted_credit_card, @options.merge(recurring: true))
+ end.check_request do |method, endpoint, data, headers|
+ assert_match(/recurringPayment=true/, data)
+ end.respond_with(successful_purchase_response)
+
+ assert_success response
+ end
+
def test_successful_test_request_in_production_environment
Base.mode = :production
@gateway.expects(:ssl_post).returns(successful_test_purchase_response)
@@ -213,7 +236,7 @@ def test_successful_cancel_recurring
def test_ip_is_being_sent
@gateway.expects(:ssl_post).with do |url, data|
- data =~ /customerIP=123\.123\.123\.123/
+ data =~ /customerIp=123\.123\.123\.123/
end.returns(successful_purchase_response)
@options[:ip] = "123.123.123.123"
@@ -232,12 +255,56 @@ def test_includes_network_tokenization_fields
assert_success response
end
+ def test_defaults_state_and_zip_with_country
+ address = { country: "AF" }
+ @options[:billing_address] = address
+ @options[:shipping_address] = address
+ response = stub_comms(@gateway, :ssl_request) do
+ @gateway.purchase(@amount, @decrypted_credit_card, @options)
+ end.check_request do |method, endpoint, data, headers|
+ assert_match(/ordProvince=--/, data)
+ assert_match(/ordPostalCode=000000/, data)
+ assert_match(/shipProvince=--/, data)
+ assert_match(/shipPostalCode=000000/, data)
+ end.respond_with(successful_purchase_response)
+
+ assert_success response
+ end
+
+ def test_no_state_and_zip_default_with_missing_country
+ address = { }
+ @options[:billing_address] = address
+ @options[:shipping_address] = address
+ response = stub_comms(@gateway, :ssl_request) do
+ @gateway.purchase(@amount, @decrypted_credit_card, @options)
+ end.check_request do |method, endpoint, data, headers|
+ assert_no_match(/ordProvince=--/, data)
+ assert_no_match(/ordPostalCode=000000/, data)
+ assert_no_match(/shipProvince=--/, data)
+ assert_no_match(/shipPostalCode=000000/, data)
+ end.respond_with(successful_purchase_response)
+
+ assert_success response
+ end
+
+ def test_sends_email_without_addresses
+ @options[:billing_address] = nil
+ @options[:shipping_address] = nil
+ @options[:shipping_email] = "ship@mail.com"
+ response = stub_comms(@gateway, :ssl_request) do
+ @gateway.purchase(@amount, @decrypted_credit_card, @options)
+ end.check_request do |method, endpoint, data, headers|
+ assert_match(/ordEmailAddress=xiaobozzz%40example.com/, data)
+ assert_match(/shipEmailAddress=ship%40mail.com/, data)
+ end.respond_with(successful_purchase_response)
+
+ assert_success response
+ end
def test_transcript_scrubbing
assert_equal scrubbed_transcript, @gateway.scrub(transcript)
end
-
private
def successful_purchase_response
@@ -266,7 +333,7 @@ def unsuccessful_authorize_response
def successful_void_response
"trnApproved=1&trnId=10100563&messageId=1&messageText=Approved&trnOrderNumber=6ca476d1a29da81a5f2d5d2c92ddeb&authCode=TEST&errorType=N&errorFields=&responseType=T&trnAmount=15%2E00&trnDate=9%2F9%2F2015+10%3A13%3A12+AM&avsProcessed=0&avsId=U&avsResult=0&avsAddrMatch=0&avsPostalMatch=0&avsMessage=Address+information+is+unavailable%2E&cvdId=2&cardType=VI&trnType=VP&paymentMethod=CC&ref1=reference+one&ref2=&ref3=&ref4=&ref5="
- end
+ end
def unsuccessful_void_response
"trnApproved=0&trnId=0&messageId=0&messageText=%3CLI%3EAdjustment+id+must+be+less+than+8+characters%3Cbr%3E&trnOrderNumber=&authCode=&errorType=U&errorFields=adjId&responseType=T&trnAmount=&trnDate=9%2F9%2F2015+10%3A15%3A20+AM&avsProcessed=0&avsId=0&avsResult=0&avsAddrMatch=0&avsPostalMatch=0&avsMessage=Address+Verification+not+performed+for+this+transaction%2E&cardType=&trnType=VP&paymentMethod=CC&ref1=&ref2=&ref3=&ref4=&ref5="
diff --git a/test/unit/gateways/blue_pay_test.rb b/test/unit/gateways/blue_pay_test.rb
index a783d08be1c..3a13b0fe71b 100644
--- a/test/unit/gateways/blue_pay_test.rb
+++ b/test/unit/gateways/blue_pay_test.rb
@@ -141,7 +141,7 @@ def test_deprecated_credit
end
def test_supported_countries
- assert_equal ['US'], BluePayGateway.supported_countries
+ assert_equal ['US', 'CA'], BluePayGateway.supported_countries
end
def test_supported_card_types
diff --git a/test/unit/gateways/blue_snap_test.rb b/test/unit/gateways/blue_snap_test.rb
index cc84a920f9b..9a901df3f7a 100644
--- a/test/unit/gateways/blue_snap_test.rb
+++ b/test/unit/gateways/blue_snap_test.rb
@@ -1,6 +1,8 @@
require 'test_helper'
class BlueSnapTest < Test::Unit::TestCase
+ include CommStub
+
def setup
@gateway = BlueSnapGateway.new(api_username: 'login', api_password: 'password')
@credit_card = credit_card
@@ -120,6 +122,15 @@ def test_failed_store
assert_equal "14002", response.error_code
end
+ def test_currency_added_correctly
+ omit "flaky spec skipped as we don't support this gateway"
+ stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options.merge(currency: 'CAD'))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/CAD<\/currency>/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
def test_verify_good_credentials
@gateway.expects(:raw_ssl_request).returns(credentials_are_legit_response)
assert @gateway.verify_credentials
diff --git a/test/unit/gateways/bogus_test.rb b/test/unit/gateways/bogus_test.rb
index 25323865538..e75090abde1 100644
--- a/test/unit/gateways/bogus_test.rb
+++ b/test/unit/gateways/bogus_test.rb
@@ -28,6 +28,11 @@ def test_authorize
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_authorize_using_credit_card_token
+ token = @gateway.store(credit_card(CC_SUCCESS_PLACEHOLDER)).authorization
+ assert @gateway.authorize(1000, token).success?
+ end
+
def test_purchase
assert @gateway.purchase(1000, credit_card(CC_SUCCESS_PLACEHOLDER)).success?
response = @gateway.purchase(1000, credit_card(CC_FAILURE_PLACEHOLDER))
diff --git a/test/unit/gateways/borgun_test.rb b/test/unit/gateways/borgun_test.rb
index 79d7c635a4d..6925d0c5c68 100644
--- a/test/unit/gateways/borgun_test.rb
+++ b/test/unit/gateways/borgun_test.rb
@@ -17,7 +17,8 @@ def setup
@options = {
order_id: '1',
billing_address: address,
- description: 'Store Purchase'
+ description: 'Store Purchase',
+ terminal_id: '3'
}
end
@@ -27,7 +28,7 @@ def test_successful_purchase
response = @gateway.purchase(@amount, @credit_card, @options)
assert_success response
- assert_equal "140216103700|11|15|WC0000000001|123456|1|000000012300", response.authorization
+ assert_equal "140216103700|11|15|WC0000000001|123456|1|000000012300|978", response.authorization
assert response.test?
end
@@ -44,7 +45,7 @@ def test_authorize_and_capture
end.respond_with(successful_authorize_response)
assert_success response
- assert_equal "140601083732|11|18|WC0000000001|123456|5|000000012300", response.authorization
+ assert_equal "140601083732|11|18|WC0000000001|123456|5|000000012300|978", response.authorization
capture = stub_comms do
@gateway.capture(@amount, response.authorization)
@@ -61,7 +62,7 @@ def test_refund
end.respond_with(successful_purchase_response)
assert_success response
- assert_equal "140216103700|11|15|WC0000000001|123456|1|000000012300", response.authorization
+ assert_equal "140216103700|11|15|WC0000000001|123456|1|000000012300|978", response.authorization
refund = stub_comms do
@gateway.refund(@amount, response.authorization)
@@ -78,7 +79,7 @@ def test_void
end.respond_with(successful_purchase_response)
assert_success response
- assert_equal "140216103700|11|15|WC0000000001|123456|1|000000012300", response.authorization
+ assert_equal "140216103700|11|15|WC0000000001|123456|1|000000012300|978", response.authorization
refund = stub_comms do
@gateway.void(response.authorization)
@@ -97,6 +98,14 @@ def test_passing_cvv
end.respond_with(successful_purchase_response)
end
+ def test_passing_terminal_id
+ stub_comms do
+ @gateway.purchase(@amount, @credit_card, { terminal_id: '3' })
+ end.check_request do |endpoint, data, headers|
+ assert_match(/TerminalID>3/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
def test_transcript_scrubbing
assert_equal scrubbed_transcript, @gateway.scrub(transcript)
end
diff --git a/test/unit/gateways/braintree_blue_test.rb b/test/unit/gateways/braintree_blue_test.rb
index 8467381cf62..c022cfcec74 100644
--- a/test/unit/gateways/braintree_blue_test.rb
+++ b/test/unit/gateways/braintree_blue_test.rb
@@ -162,6 +162,7 @@ def test_verification_merchant_account_id_exists_when_verify_card_and_merchant_a
customer = stub(
:credit_cards => [stub_everything],
:email => 'email',
+ :phone => '321-654-0987',
:first_name => 'John',
:last_name => 'Smith'
)
@@ -185,6 +186,7 @@ def test_merchant_account_id_can_be_set_by_options
customer = stub(
:credit_cards => [stub_everything],
:email => 'email',
+ :phone => '321-654-0987',
:first_name => 'John',
:last_name => 'Smith'
)
@@ -201,6 +203,7 @@ def test_store_with_verify_card_true
customer = stub(
:credit_cards => [stub_everything],
:email => 'email',
+ :phone => '321-654-0987',
:first_name => 'John',
:last_name => 'Smith'
)
@@ -222,6 +225,7 @@ def test_passes_email
customer = stub(
:credit_cards => [stub_everything],
:email => "bob@example.com",
+ :phone => '321-654-0987',
:first_name => 'John',
:last_name => 'Smith',
id: "123"
@@ -240,6 +244,7 @@ def test_scrubs_invalid_email
customer = stub(
:credit_cards => [stub_everything],
:email => nil,
+ :phone => '321-654-0987',
:first_name => 'John',
:last_name => 'Smith',
:id => "123"
@@ -258,6 +263,7 @@ def test_store_with_verify_card_false
customer = stub(
:credit_cards => [stub_everything],
:email => 'email',
+ :phone => '321-654-0987',
:first_name => 'John',
:last_name => 'Smith'
)
@@ -278,6 +284,7 @@ def test_store_with_billing_address_options
customer_attributes = {
:credit_cards => [stub_everything],
:email => 'email',
+ :phone => '321-654-0987',
:first_name => 'John',
:last_name => 'Smith'
}
@@ -306,6 +313,7 @@ def test_store_with_billing_address_options
def test_store_with_credit_card_token
customer = stub(
:email => 'email',
+ :phone => '321-654-0987',
:first_name => 'John',
:last_name => 'Smith'
)
@@ -329,6 +337,7 @@ def test_store_with_credit_card_token
def test_store_with_customer_id
customer = stub(
:email => 'email',
+ :phone => '321-654-0987',
:first_name => 'John',
:last_name => 'Smith',
:credit_cards => [stub_everything]
@@ -497,6 +506,27 @@ def test_address_zip_handling
@gateway.purchase(100, credit_card("41111111111111111111"), :billing_address => {:zip => "1234567890"})
end
+ def test_cardholder_name_passing_with_card
+ Braintree::TransactionGateway.any_instance.expects(:sale).with do |params|
+ (params[:credit_card][:cardholder_name] == "Longbob Longsen")
+ end.returns(braintree_result)
+ @gateway.purchase(100, credit_card("41111111111111111111"), :customer => {:first_name => "Longbob", :last_name => "Longsen"})
+ end
+
+ def test_three_d_secure_pass_thru_handling
+ Braintree::TransactionGateway.
+ any_instance.
+ expects(:sale).
+ with(has_entries(three_d_secure_pass_thru: {
+ cavv: "cavv",
+ eci_flag: "eci",
+ xid: "xid",
+ })).
+ returns(braintree_result)
+
+ @gateway.purchase(100, credit_card("41111111111111111111"), three_d_secure: {cavv: "cavv", eci: "eci", xid: "xid"})
+ end
+
def test_passes_recurring_flag
@gateway = BraintreeBlueGateway.new(
:merchant_id => 'test',
@@ -591,7 +621,8 @@ def test_apple_pay_card
with(
:amount => '1.00',
:order_id => '1',
- :customer => {:id => nil, :email => nil, :first_name => 'Longbob', :last_name => 'Longsen'},
+ :customer => {:id => nil, :email => nil, :phone => nil,
+ :first_name => 'Longbob', :last_name => 'Longsen'},
:options => {:store_in_vault => false, :submit_for_settlement => nil, :hold_in_escrow => nil},
:custom_fields => nil,
:apple_pay_card => {
@@ -599,7 +630,8 @@ def test_apple_pay_card
:expiration_month => '09',
:expiration_year => (Time.now.year + 1).to_s,
:cardholder_name => 'Longbob Longsen',
- :cryptogram => '111111111100cryptogram'
+ :cryptogram => '111111111100cryptogram',
+ :eci_indicator => '05'
}
).
returns(braintree_result(:id => "transaction_id"))
@@ -615,6 +647,40 @@ def test_apple_pay_card
assert_equal "transaction_id", response.authorization
end
+ def test_android_pay_card
+ Braintree::TransactionGateway.any_instance.expects(:sale).
+ with(
+ :amount => '1.00',
+ :order_id => '1',
+ :customer => {:id => nil, :email => nil, :phone => nil,
+ :first_name => 'Longbob', :last_name => 'Longsen'},
+ :options => {:store_in_vault => false, :submit_for_settlement => nil, :hold_in_escrow => nil},
+ :custom_fields => nil,
+ :android_pay_card => {
+ :number => '4111111111111111',
+ :expiration_month => '09',
+ :expiration_year => (Time.now.year + 1).to_s,
+ :cryptogram => '111111111100cryptogram',
+ :google_transaction_id => '1234567890',
+ :source_card_type => "visa",
+ :source_card_last_four => "1111",
+ :eci_indicator => '05'
+ }
+ ).
+ returns(braintree_result(:id => "transaction_id"))
+
+ credit_card = network_tokenization_credit_card('4111111111111111',
+ :brand => 'visa',
+ :eci => "05",
+ :payment_cryptogram => "111111111100cryptogram",
+ :source => :android_pay,
+ :transaction_id => '1234567890'
+ )
+
+ response = @gateway.authorize(100, credit_card, :test => true, :order_id => '1')
+ assert_equal "transaction_id", response.authorization
+ end
+
def test_supports_network_tokenization
assert_instance_of TrueClass, @gateway.supports_network_tokenization?
end
@@ -635,6 +701,32 @@ def test_unsuccessful_transaction_returns_message_when_available
assert_equal response.message, 'Some error message'
end
+ def test_refund_unsettled_payment
+ Braintree::TransactionGateway.any_instance.
+ expects(:refund).
+ returns(braintree_error_result(message: "Cannot refund a transaction unless it is settled. (91506)"))
+
+ Braintree::TransactionGateway.any_instance.
+ expects(:void).
+ never
+
+ response = @gateway.refund(1.00, 'transaction_id')
+ refute response.success?
+ end
+
+ def test_refund_unsettled_payment_forces_void_on_full_refund
+ Braintree::TransactionGateway.any_instance.
+ expects(:refund).
+ returns(braintree_error_result(message: "Cannot refund a transaction unless it is settled. (91506)"))
+
+ Braintree::TransactionGateway.any_instance.
+ expects(:void).
+ returns(braintree_result)
+
+ response = @gateway.refund(1.00, 'transaction_id', force_full_refund_if_unsettled: true)
+ assert response.success?
+ end
+
private
def braintree_result(options = {})
diff --git a/test/unit/gateways/braintree_orange_test.rb b/test/unit/gateways/braintree_orange_test.rb
index 6607ad45d0a..6ed0d3481e5 100644
--- a/test/unit/gateways/braintree_orange_test.rb
+++ b/test/unit/gateways/braintree_orange_test.rb
@@ -24,6 +24,16 @@ def test_successful_purchase
assert_equal '510695343', response.authorization
end
+ def test_fractional_amounts
+ response = stub_comms do
+ @gateway.purchase(100, @credit_card, @options.merge(currency: 'JPY'))
+ end.check_request do |endpoint, data, headers|
+ refute_match(/amount=1.00/, data)
+ end.respond_with(successful_purchase_response)
+
+ assert_success response
+ end
+
def test_successful_store
@gateway.expects(:ssl_post).returns(successful_store_response)
diff --git a/test/unit/gateways/card_connect_test.rb b/test/unit/gateways/card_connect_test.rb
new file mode 100644
index 00000000000..078f6f9256b
--- /dev/null
+++ b/test/unit/gateways/card_connect_test.rb
@@ -0,0 +1,279 @@
+require 'test_helper'
+
+class CardConnectTest < Test::Unit::TestCase
+ include CommStub
+ def setup
+ @gateway = CardConnectGateway.new(username: 'username', password: 'password', merchant_id: 'merchand_id')
+ @credit_card = credit_card('4788250000121443')
+ @declined_card = credit_card('4387751111111053')
+ @amount = 100
+ @check = check(routing_number: '053000196')
+
+ @options = {
+ order_id: '1',
+ billing_address: address,
+ description: 'Store Purchase'
+ }
+ end
+
+ def test_incorrect_domain
+ assert_raise(ArgumentError) {
+ CardConnectGateway.new(username: 'username', password: 'password', merchant_id: 'merchand_id', domain: 'www.google.com')
+ }
+ end
+
+ def test_successful_purchase
+ @gateway.expects(:ssl_request).returns(successful_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal '363652261392', response.authorization
+ assert response.test?
+ end
+
+ def test_successful_purchase_with_echeck
+ @gateway.expects(:ssl_request).returns(successful_echeck_purchase_response)
+
+ response = @gateway.purchase(@amount, @check, @options)
+ assert_success response
+
+ assert_equal '010136262668', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_purchase
+ @gateway.expects(:ssl_request).returns(failed_purchase_response)
+
+ response = @gateway.purchase(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal Gateway::STANDARD_ERROR_CODE[:card_declined], response.error_code
+ end
+
+ def test_failed_purchase_with_echeck
+ @gateway.expects(:ssl_request).returns(failed_echeck_purchase_response)
+
+ response = @gateway.purchase(@amount, check(routing_number: '23433'), @options)
+ assert_failure response
+ assert_equal Gateway::STANDARD_ERROR_CODE[:card_declined], response.error_code
+ end
+
+ def test_successful_authorize
+ @gateway.expects(:ssl_request).returns(successful_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal '363168161558', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_authorize
+ @gateway.expects(:ssl_request).returns(failed_authorize_response)
+
+ response = @gateway.authorize(@amount, @declined_card, @options)
+ assert_failure response
+ assert_equal Gateway::STANDARD_ERROR_CODE[:card_declined], response.error_code
+ end
+
+ def test_successful_capture
+ @gateway.expects(:ssl_request).returns(successful_capture_response)
+
+ response = @gateway.capture(@amount,'363168161558', @options)
+ assert_success response
+
+ assert_equal '363168161558', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_capture
+ @gateway.expects(:ssl_request).returns(failed_capture_response)
+
+ response = @gateway.capture(@amount, '23221', @options)
+ assert_failure response
+
+ assert_equal '23221', response.authorization
+ assert response.test?
+
+ assert_equal Gateway::STANDARD_ERROR_CODE[:processing_error], response.error_code
+ end
+
+ def test_successful_refund
+ @gateway.expects(:ssl_request).returns(successful_refund_response)
+
+ response = @gateway.refund(@amount, '36366126178', @options)
+ assert_success response
+
+ assert_equal '363661261786', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_refund
+ @gateway.expects(:ssl_request).returns(failed_refund_response)
+
+ response = @gateway.refund(@amount, '23221', @options)
+ assert_failure response
+
+ assert_equal '23221', response.authorization
+ assert response.test?
+
+ assert_equal Gateway::STANDARD_ERROR_CODE[:processing_error], response.error_code
+ end
+
+ def test_successful_void
+ @gateway.expects(:ssl_request).returns(successful_void_response)
+
+ response = @gateway.void('363750268295')
+ assert_success response
+
+ assert_equal '363664261982', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_void
+ @gateway.expects(:ssl_request).returns(failed_void_response)
+
+ response = @gateway.void('23221')
+ assert_failure response
+
+ assert response.test?
+
+ assert_equal Gateway::STANDARD_ERROR_CODE[:processing_error], response.error_code
+ end
+
+ def test_successful_verify
+ @gateway.expects(:ssl_request).returns(successful_verify_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+
+ assert_equal '363272166977', response.authorization
+ assert response.test?
+ end
+
+ def test_successful_verify_with_failed_void
+ response = stub_comms(@gateway, :ssl_request) do
+ @gateway.verify(@credit_card, @options)
+ end.respond_with(successful_authorize_response, failed_void_response)
+ assert_success response
+ end
+
+ def test_failed_verify
+ @gateway.expects(:ssl_request).returns(failed_verify_response)
+
+ response = @gateway.verify(@declined_card, @options)
+ assert_failure response
+ assert_equal "Insufficient funds", response.message
+ end
+
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
+ private
+
+ def pre_scrubbed
+ %q(
+ opening connection to fts.cardconnect.com:6443...
+ opened
+ starting SSL for fts.cardconnect.com:6443...
+ SSL established
+ <- "PUT /cardconnect/rest/auth HTTP/1.1\r\nAuthorization: Basic dGVzdGluZzp0ZXN0aW5nMTIz\r\nContent-Type: application/json\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: fts.cardconnect.com:6443\r\nContent-Length: 298\r\n\r\n"
+ <- "{\"orderid\":null,\"ecomind\":\"E\",\"amount\":\"1.00\",\"name\":\"Longbob Longsen\",\"account\":\"4000100011112224\",\"expiry\":\"0918\",\"cvv2\":\"123\",\"currency\":\"USD\",\"address\":\"456 My Street\",\"city\":\"Ottawa\",\"region\":\"ON\",\"country\":\"CA\",\"postal\":\"K1C2N6\",\"phone\":\"(555)555-5555\",\"capture\":\"Y\",\"merchid\":\"496160873888\"}"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "X-FRAME-OPTIONS: DENY\r\n"
+ -> "Content-Type: application/json\r\n"
+ -> "Content-Length: 281\r\n"
+ -> "Date: Fri, 29 Dec 2017 23:51:22 GMT\r\n"
+ -> "Server: CardConnect\r\n"
+ -> "Connection: close\r\n"
+ -> "Set-Cookie: BIGipServerphu-smb-vip_8080=!3EyEfCvmvK/UDgCOaMq7McVUJtfXHaj0/1BWyxbacLNntp1E0Upt2onAMTKRSSu6r6mZaKuZm7N9ais=; path=/; Httponly; Secure\r\n"
+ -> "\r\n"
+ reading 281 bytes...
+ -> "{\"amount\":\"1.00\",\"resptext\":\"Approval\",\"commcard\":\" C \",\"cvvresp\":\"M\",\"batchid\":\"1900941444\",\"avsresp\":\" \",\"respcode\":\"00\",\"merchid\":\"496160873888\",\"token\":\"9405701444882224\",\"authcode\":\"PPS568\",\"respproc\":\"FNOR\",\"retref\":\"363743267882\",\"respstat\":\"A\",\"account\":\"9405701444882224\"}"
+ read 281 bytes
+ Conn close
+ )
+ end
+
+ def post_scrubbed
+ %q(
+ opening connection to fts.cardconnect.com:6443...
+ opened
+ starting SSL for fts.cardconnect.com:6443...
+ SSL established
+ <- "PUT /cardconnect/rest/auth HTTP/1.1\r\nAuthorization: Basic [FILTERED]\r\nContent-Type: application/json\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: fts.cardconnect.com:6443\r\nContent-Length: 298\r\n\r\n"
+ <- "{\"orderid\":null,\"ecomind\":\"E\",\"amount\":\"1.00\",\"name\":\"Longbob Longsen\",\"account\":\"[FILTERED]\",\"expiry\":\"0918\",\"cvv2\":\"[FILTERED]\",\"currency\":\"USD\",\"address\":\"456 My Street\",\"city\":\"Ottawa\",\"region\":\"ON\",\"country\":\"CA\",\"postal\":\"K1C2N6\",\"phone\":\"(555)555-5555\",\"capture\":\"Y\",\"merchid\":\"[FILTERED]\"}"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "X-FRAME-OPTIONS: DENY\r\n"
+ -> "Content-Type: application/json\r\n"
+ -> "Content-Length: 281\r\n"
+ -> "Date: Fri, 29 Dec 2017 23:51:22 GMT\r\n"
+ -> "Server: CardConnect\r\n"
+ -> "Connection: close\r\n"
+ -> "Set-Cookie: BIGipServerphu-smb-vip_8080=!3EyEfCvmvK/UDgCOaMq7McVUJtfXHaj0/1BWyxbacLNntp1E0Upt2onAMTKRSSu6r6mZaKuZm7N9ais=; path=/; Httponly; Secure\r\n"
+ -> "\r\n"
+ reading 281 bytes...
+ -> "{\"amount\":\"1.00\",\"resptext\":\"Approval\",\"commcard\":\" C \",\"cvvresp\":\"M\",\"batchid\":\"1900941444\",\"avsresp\":\" \",\"respcode\":\"00\",\"merchid\":\"[FILTERED]\",\"token\":\"[FILTERED]\",\"authcode\":\"PPS568\",\"respproc\":\"FNOR\",\"retref\":\"363743267882\",\"respstat\":\"A\",\"account\":\"[FILTERED]\"}"
+ read 281 bytes
+ Conn close
+ )
+ end
+
+ def successful_purchase_response
+ "{\"amount\":\"1.00\",\"resptext\":\"Approval\",\"commcard\":\" C \",\"cvvresp\":\"M\",\"batchid\":\"1900941444\",\"avsresp\":\" \",\"respcode\":\"00\",\"merchid\":\"496160873888\",\"token\":\"9405701444882224\",\"authcode\":\"PPS500\",\"respproc\":\"FNOR\",\"retref\":\"363652261392\",\"respstat\":\"A\",\"account\":\"9405701444882224\"}"
+ end
+
+ def successful_echeck_purchase_response
+ "{\"amount\":\"1.00\",\"resptext\":\"Success\",\"cvvresp\":\"U\",\"batchid\":\"1900940633\",\"avsresp\":\"U\",\"respcode\":\"00\",\"merchid\":\"542041\",\"token\":\"9051769384108535\",\"authcode\":\"GF7PBR\",\"respproc\":\"PSTR\",\"retref\":\"010136262668\",\"respstat\":\"A\",\"account\":\"9051769384108535\"}"
+ end
+
+ def failed_echeck_purchase_response
+ "{\"respproc\":\"PPS\",\"amount\":\"0.00\",\"resptext\":\"Invalid card\",\"cardproc\":\"PSTR\",\"retref\":\"010108164081\",\"respstat\":\"C\",\"respcode\":\"11\",\"account\":\"9235405400368535\",\"merchid\":\"542041\",\"token\":\"9235405400368535\"}"
+ end
+
+ def failed_purchase_response
+ "{\"respproc\":\"FNOR\",\"amount\":\"0.00\",\"resptext\":\"Insufficient funds\",\"cardproc\":\"FNOR\",\"commcard\":\" C \",\"retref\":\"005533134378\",\"respstat\":\"C\",\"respcode\":\"NU\",\"account\":\"9435885049491053\",\"merchid\":\"496160873888\",\"token\":\"9435885049491053\"}"
+ end
+
+ def successful_authorize_response
+ "{\"amount\":\"1.00\",\"resptext\":\"Approval\",\"commcard\":\" C \",\"cvvresp\":\"M\",\"avsresp\":\" \",\"respcode\":\"00\",\"merchid\":\"496160873888\",\"token\":\"9405701444882224\",\"authcode\":\"PPS454\",\"respproc\":\"FNOR\",\"retref\":\"363168161558\",\"respstat\":\"A\",\"account\":\"9405701444882224\"}"
+ end
+
+ def failed_authorize_response
+ "{\"respproc\":\"FNOR\",\"amount\":\"0.00\",\"resptext\":\"Insufficient funds\",\"cardproc\":\"FNOR\",\"commcard\":\" C \",\"retref\":\"005737235263\",\"respstat\":\"C\",\"respcode\":\"NU\",\"account\":\"9435885049491053\",\"merchid\":\"496160873888\",\"token\":\"9435885049491053\"}"
+ end
+
+ def successful_capture_response
+ "{\"respproc\":\"FNOR\",\"amount\":\"1.00\",\"resptext\":\"Approval\",\"setlstat\":\"Queued for Capture\",\"commcard\":\" C \",\"retref\":\"363168161558\",\"respstat\":\"A\",\"respcode\":\"00\",\"batchid\":\"1900941444\",\"account\":\"9405701444882224\",\"merchid\":\"496160873888\",\"token\":\"9405701444882224\"}"
+ end
+
+ def failed_capture_response
+ "{\"respproc\":\"PPS\",\"resptext\":\"Txn not found\",\"retref\":\"23221\",\"respstat\":\"C\",\"respcode\":\"29\",\"batchid\":\"-1\",\"account\":\"\"}"
+ end
+
+ def successful_refund_response
+ "{\"respproc\":\"PPS\",\"amount\":\"1.00\",\"resptext\":\"Approval\",\"retref\":\"363661261786\",\"respstat\":\"A\",\"respcode\":\"00\",\"merchid\":\"496160873888\"}"
+ end
+
+ def failed_refund_response
+ "{\"respproc\":\"PPS\",\"resptext\":\"Txn not found\",\"retref\":\"23221\",\"respcode\":\"29\",\"respstat\":\"C\"}"
+ end
+
+ def successful_void_response
+ "{\"authcode\":\"REVERS\",\"respproc\":\"FNOR\",\"amount\":\"0.00\",\"resptext\":\"Approval\",\"currency\":\"USD\",\"retref\":\"363664261982\",\"respstat\":\"A\",\"respcode\":\"00\",\"merchid\":\"496160873888\"}"
+ end
+
+ def failed_void_response
+ "{\"respproc\":\"PPS\",\"resptext\":\"Txn not found\",\"retref\":\"23221\",\"respcode\":\"29\",\"respstat\":\"C\"}"
+ end
+
+ def successful_verify_response
+ "{\"amount\":\"0.00\",\"resptext\":\"Approval\",\"commcard\":\" C \",\"cvvresp\":\"M\",\"avsresp\":\" \",\"respcode\":\"00\",\"merchid\":\"496160873888\",\"token\":\"9405701444882224\",\"authcode\":\"PPS585\",\"respproc\":\"FNOR\",\"retref\":\"363272166977\",\"respstat\":\"A\",\"account\":\"9405701444882224\"}"
+ end
+
+ def failed_verify_response
+ "{\"respproc\":\"FNOR\",\"amount\":\"0.00\",\"resptext\":\"Insufficient funds\",\"cardproc\":\"FNOR\",\"commcard\":\" C \",\"retref\":\"005101240599\",\"respstat\":\"C\",\"respcode\":\"NU\",\"account\":\"9435885049491053\",\"merchid\":\"496160873888\",\"token\":\"9435885049491053\"}"
+ end
+end
diff --git a/test/unit/gateways/card_stream_test.rb b/test/unit/gateways/card_stream_test.rb
index 12e49a9135c..c92b6459075 100644
--- a/test/unit/gateways/card_stream_test.rb
+++ b/test/unit/gateways/card_stream_test.rb
@@ -28,6 +28,18 @@ def setup
:description => 'AM test purchase'
}
+ @visacredit_descriptor_options = {
+ :billing_address => {
+ :address1 => "Flat 6, Primrose Rise",
+ :address2 => "347 Lavender Road",
+ :city => "",
+ :state => "Northampton",
+ :zip => 'NN17 8YG '
+ },
+ :merchant_name => 'merchant',
+ :dynamic_descriptor => 'product'
+ }
+
@declined_card = credit_card('4000300011112220',
:month => '9',
:year => '2014'
@@ -68,7 +80,7 @@ def test_successful_visacreditcard_capture
assert responseCapture.test?
end
- def test_successful_visacreditcard_refund
+ def test_successful_refund
@gateway.expects(:ssl_post).returns(successful_refund_response)
assert responseRefund = @gateway.refund(142, "authorization", @visacredit_options)
@@ -77,6 +89,26 @@ def test_successful_visacreditcard_refund
assert responseRefund.test?
end
+ def test_successful_refund_due_to_unsettled_payment_forces_void
+ refund = stub_comms do
+ @gateway.refund(142, 'authorization', @visacredit_options.merge(force_full_refund_if_unsettled: true))
+ end.respond_with(failed_refund_for_unsettled_payment_response, successful_void_response)
+
+ assert refund
+ assert_success refund
+ assert_equal 'APPROVED', refund.message
+ assert refund.test?
+ end
+
+ def test_failed_refund_due_to_unsettled_payment
+ @gateway.expects(:ssl_post).returns(failed_refund_for_unsettled_payment_response)
+
+ assert refund = @gateway.refund(142, "authorization", @visacredit_options)
+ assert_equal 'Can not REFUND this SALE transaction', refund.message
+ assert_failure refund
+ assert refund.test?
+ end
+
def test_successful_visacreditcard_void
@gateway.expects(:ssl_post).returns(successful_void_response)
@@ -95,6 +127,31 @@ def test_successful_visacreditcard_purchase
assert responseRefund.test?
end
+ def test_successful_visacreditcard_purchase_with_descriptors
+ stub_comms do
+ @gateway.purchase(284, @visacreditcard, @visacredit_descriptor_options)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/statementNarrative1=merchant/, data)
+ assert_match(/statementNarrative2=product/, data)
+ end.respond_with(successful_purchase_response_with_descriptors)
+ end
+
+ def test_successful_visacreditcard_purchase_with_default_ip
+ stub_comms do
+ @gateway.purchase(284, @visacreditcard, @visacredit_options)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/remoteAddress=1\.1\.1\.1/, data)
+ end.respond_with(successful_purchase_response_with_descriptors)
+ end
+
+ def test_successful_visacreditcard_purchase_with_default_country
+ stub_comms do
+ @gateway.purchase(284, @visacreditcard, @visacredit_options.delete(:billing_address))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/customerCountryCode=GB/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
def test_successful_visacreditcard_purchase_via_reference
@gateway.expects(:ssl_post).returns(successful_reference_purchase_response)
@@ -156,6 +213,14 @@ def test_purchase_options
end.respond_with(successful_purchase_response)
assert_success purchase
+
+ purchase = stub_comms do
+ @gateway.purchase(142, @visacreditcard, @visacredit_options.merge(currency: "PEN"))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/currencyCode=604/, data)
+ end.respond_with(successful_purchase_response)
+
+ assert_success purchase
end
def test_successful_purchase_without_street_address
@@ -171,7 +236,7 @@ def test_successful_purchase_without_any_address
end
def test_hmac_signature_added_to_post
- post_params = "action=SALE&amount=10000&cardCVV=356&cardExpiryMonth=12&cardExpiryYear=14&cardNumber=4929421234600821&countryCode=GB¤cyCode=826&customerAddress=Flat+6%2C+Primrose+Rise+347+Lavender+Road&customerName=Longbob+Longsen&customerPostCode=NN17+8YG+&merchantID=login&orderRef=AM+test+purchase&threeDSRequired=N&transactionUnique=#{@visacredit_options[:order_id]}&type=1"
+ post_params = "action=SALE&amount=10000&captureDelay=0&cardCVV=356&cardExpiryMonth=12&cardExpiryYear=14&cardNumber=4929421234600821&countryCode=GB¤cyCode=826&customerAddress=Flat+6%2C+Primrose+Rise+347+Lavender+Road&customerCountryCode=GB&customerName=Longbob+Longsen&customerPostCode=NN17+8YG+&merchantID=login&orderRef=AM+test+purchase&remoteAddress=1.1.1.1&threeDSRequired=N&transactionUnique=#{@visacredit_options[:order_id]}&type=1"
expected_signature = Digest::SHA512.hexdigest("#{post_params}#{@gateway.options[:shared_secret]}")
@gateway.expects(:ssl_post).with do |url, data|
@@ -235,6 +300,10 @@ def successful_refund_response
"merchantID=0000000&threeDSEnabled=Y&merchantDescription=General+test+account+with+AVS%2FCV2+checking&xref=13021914NT06BM21GJ15VJH&amount=142¤cyCode=826&action=REFUND&type=1&countryCode=826&merchantAlias=0000992&remoteAddress=80.229.33.63&responseCode=0&responseMessage=REFUNDACCEPTED&customerName=Longbob+Longsen&customerAddress=Flat+6%2C+Primrose+Rise+347+Lavender+Road&customerPostCode=NN17+8YG+&transactionUnique=c7981d78d217cf3cfda6559921e31c4a&orderRef=AM+test+purchase&amountReceived=142&avscv2ResponseCode=222100&avscv2ResponseMessage=ALL+MATCH&avscv2AuthEntity=merchant+host&cv2Check=matched&addressCheck=matched&postcodeCheck=matched&threeDSXID=00000000000004717488&threeDSEnrolled=U&threeDSErrorCode=-1&threeDSErrorDescription=Error+while+attempting+to+send+the+request+to%3A+https%3A%2F%2F3dstest.universalpaymentgateway.com%3A4343%2FAPI%0A%0DPlease+make+sure+that+ActiveMerchant+server+is+running+and+the+URL+is+valid.+ERROR_INTERNET_CANNOT_CONNECT%3A+The+attempt+to+connect+to+the+server+failed.&threeDSMerchantPref=PROCEED&threeDSVETimestamp=2013-02-19+14%3A05%3A58&cardTypeCode=VC&cardNumberMask=%2A%2A%2A%2A%2A%2A%2A%2A%2A%2A%2A%2A0821&threeDSRequired=N&transactionID=4717490&transactionPreviousID=4717488×tamp=2013-02-19+14%3A06%3A21&cardType=Visa+Credit¤cyExponent=2&responseStatus=0&merchantName=CARDSTREAM+TEST&merchantID2=100001"
end
+ def failed_refund_for_unsettled_payment_response
+ "responseCode=65541&responseMessage=Can+not+REFUND+this+SALE+transaction&responseStatus=2&xref=18032714RP14KM21FX11YHT&amount=142¤cyCode=826&remoteAddress=1.1.1.1&countryCode=GB&merchantID=103191&action=REFUND_SALE&requestID=5aba43ad1481e&state=finished&requestMerchantID=103191&processMerchantID=103191&transactionID=25814232×tamp=2018-03-27+14%3A14%3A21&vcsResponseCode=0&vcsResponseMessage=Success+-+no+velocity+check+rules+applied&signature=b56640b215510a04ebfaa095b63705cda08cca318a7ccb2b2b48caec75adc187d9cae5082eb1dc71d258813ee9d879721e48af04966a489171f435bfa67b6d92"
+ end
+
def successful_void_response
"merchantID=103191&threeDSEnabled=Y&avscv2CheckEnabled=N&eReceiptsEnabled=N&transactionID=11132605&xref=16050316NZ51LT53FP70BMZ&state=canceled&captureDelay=-1&remoteAddress=107.15.253.186&action=CANCEL%3ASALE&type=1¤cyCode=826&countryCode=826&amount=284&orderRef=AM+test+purchase&transactionUnique=2240526ccaa7d41af63a94aab843e683&cardTypeCode=VC&cardNumberMask=492942%2A%2A%2A%2A%2A%2A0821&cardExpiryDate=1214&cardExpiryMonth=12&cardExpiryYear=14&customerName=Longbob+Longsen&customerAddress=Flat+6%2C+Primrose+Rise+347+Lavender+Road&customerPostcode=NN17+8YG&eReceiptsStoreID=1&customerReceiptsRequired=N&cv2Check=matched&addressCheck=matched&postcodeCheck=matched&avscv2ResponseCode=222100&avscv2ResponseMessage=ALL+MATCH&avscv2AuthEntity=merchant+host&addressCheckPref=not+known%2Cnot+checked%2Cmatched%2Cnot+matched%2Cpartially+matched&postcodeCheckPref=not+known%2Cnot+checked%2Cmatched%2Cnot+matched%2Cpartially+matched&threeDSRequired=N&threeDSCheckPref=authenticated&authorisationCode=084609&amountReceived=0&responseCode=0&responseMessage=Transaction+cancelled.&cardCVVMandatory=N&responseStatus=0×tamp=2016-05-03+16%3A51%3A54&cardType=Visa+Credit&cardScheme=Visa+&cardSchemeCode=VC&cardIssuer=BARCLAYS+BANK+PLC&cardIssuerCountry=United+Kingdom&cardIssuerCountryCode=GBR&signature=73cf5ec5470f6a1b3abce4e2a4b64adc2da0cb7103e8d362b40ab101d2ff339961a593869f289f7660a286e8c92c22fd5dec4330cf7e7e0ca8651cd8c0a8d966"
end
@@ -247,6 +316,10 @@ def successful_purchase_response_with_3dsecure
"responseCode=65802&responseMessage=3DS+AUTHENTICATION+REQUIRED&responseStatus=2&merchantID=103191&threeDSEnabled=Y&threeDSCheckPref=not+known%2Cnot+checked%2Cauthenticated%2Cnot+authenticated%2Cattempted+authentication&avscv2CheckEnabled=N&cv2CheckPref=not+known%2Cnot+checked%2Cmatched%2Cnot+matched%2Cpartially+matched&addressCheckPref=not+known%2Cnot+checked%2Cmatched%2Cnot+matched%2Cpartially+matched&postcodeCheckPref=not+known%2Cnot+checked%2Cmatched%2Cnot+matched%2Cpartially+matched&cardCVVMandatory=Y&customerID=1749&eReceiptsEnabled=N&eReceiptsStoreID=1&amount=1202¤cyCode=826&transactionUnique=42e13d06ce4d5f5e3eb4868d29baa8bb&orderRef=AM+test+purchase&threeDSRequired=Y&customerName=Longbob+Longsen&customerAddress=25+The+Larches&customerPostCode=LE10+2RT&action=SALE&type=1&countryCode=826&customerPostcode=LE10+2RT&customerReceiptsRequired=N&state=finished&remoteAddress=45.37.180.92&requestMerchantID=103191&processMerchantID=103191&xref=15080615RZ18RJ15RJ64YVZ&cardExpiryDate=1220&threeDSXID=MDAwMDAwMDAwMDAwMDg5NjY0OTc%3D&threeDSEnrolled=Y&transactionID=8966497&cardNumberMask=%2A%2A%2A%2A%2A%2A%2A%2A%2A%2A%2A%2A1112&cardType=Visa+Credit&cardTypeCode=VC&cardScheme=Visa+&cardSchemeCode=VC&cardIssuer=Unknown&cardIssuerCountry=Unknown&cardIssuerCountryCode=XXX&threeDSPaReq=eJxVUttuwjAM%2FZWKD2iaQrnJjVQGA7TBGFSTeMxSC8rohTRd2d8vKe0YD5F8jh37%0D%0A%2BCQQHiXidIeilMhghUXBD2jFkd%2FxPNHtCz747EaCdhhsgi1eGHyjLOIsZdR2bBdI%0D%0AC%2FVVKY48VQy4uEyWa%2BYN6LDbA9JASFAup2zk9Tx3pOkbhJQnyHa5FhGdf6wQC2UF%0D%0AQmRlqizdvc5CDeUPG7p9IC2AUp7ZUam8GBNSVZUtuIwKJZEntsgSAsQUALnr2pQm%0D%0AKnTDaxyx1TSoHs%2FBW5%2F2zlsofCCmAiKukLkO9Zyh07dob0yHYzoAUvPAE6OEzScb%0D%0Ai7q2o9U2DORmUHAD1DWZ%2FwxoqyWmot2nRYDXPEtRV%2BgLfzFEWAgWrCxlrMmbFbQG%0D%0AQwO57%2FS0MM4LpU1dxM%2FhrJx9zU8f6%2F3WuZxGL6%2Fvle%2Bbt6gLzKhYe6h3u80yAIhp%0D%0AQZpnJs1X0NHDF%2FkFvj%2B6mg%3D%3D&threeDSACSURL=https%3A%2F%2Fdropit.3dsecure.net%3A9443%2FPIT%2FACS&threeDSVETimestamp=2015-08-06+15%3A18%3A15&threeDSCheck=not+checked&vcsResponseCode=0&vcsResponseMessage=Success+-+no+velocity+check+rules+applied¤cyExponent=2&threeDSMD=UDNLRVk6eHJlZj0xNTA4MDYxNVJaMThSSjE1Uko2NFlWWg%3D%3D×tamp=2015-08-06+15%3A18%3A17&threeDSResponseCode=65802&threeDSResponseMessage=3DS+AUTHENTICATION+REQUIRED&signature=8551e3f1c77b6cfa78e154d99ffb05fdeabbae48a7ce723a3464047731ad98a1c4bfe0b7dfdf46de7ff3dab66b3e2e365025fc9ff3a74d86ae4378c8cc985d88"
end
+ def successful_purchase_response_with_descriptors
+ "merchantID=103191&threeDSEnabled=Y&threeDSCheckPref=authenticated&avscv2CheckEnabled=N&cv2CheckPref=not+known%2Cnot+checked%2Cmatched%2Cnot+matched%2Cpartially+matched&addressCheckPref=not+known%2Cnot+checked%2Cmatched%2Cnot+matched%2Cpartially+matched&postcodeCheckPref=not+known%2Cnot+checked%2Cmatched%2Cnot+matched%2Cpartially+matched&cardCVVMandatory=Y&customerReceiptsRequired=N&eReceiptsEnabled=N&eReceiptsStoreID=1&captureDelay=0&amount=284¤cyCode=826&statementNarrative1=merchant&statementNarrative2=product&type=1&threeDSRequired=N&customerName=Longbob+Longsen&cardExpiryMonth=12&cardExpiryYear=14&customerAddress=Flat+6%2C+Primrose+Rise+347+Lavender+Road&customerPostCode=NN17+8YG&countryCode=826&action=SALE&customerPostcode=NN17+8YG&requestID=586aae8406720&responseCode=0&responseMessage=AUTHCODE%3A684787&state=captured&requestMerchantID=103191&processMerchantID=103191&xref=17010219YV48VW21QH25TKS&paymentMethod=card&cardExpiryDate=1214&authorisationCode=684787&transactionID=13843073&responseStatus=0×tamp=2017-01-02+19%3A48%3A21&amountReceived=284&avscv2ResponseCode=222100&avscv2ResponseMessage=ALL+MATCH&avscv2AuthEntity=merchant+host&cv2Check=matched&addressCheck=matched&postcodeCheck=matched&cardNumberMask=492942%2A%2A%2A%2A%2A%2A0821&cardType=Visa+Credit&cardTypeCode=VC&cardScheme=Visa+&cardSchemeCode=VC&cardIssuer=BARCLAYS+BANK+PLC&cardIssuerCountry=United+Kingdom&cardIssuerCountryCode=GBR&vcsResponseCode=0&vcsResponseMessage=Success+-+no+velocity+check+rules+applied¤cyExponent=2&signature=d47c9253d2d9ff6e9464a782b798b3fa699f6ed085eb03d5f87c4cf1f0994efab0c3145236df2163ea2b48fc0232bed25626b7ac331f0c98473ef4b551099eef"
+ end
+
def failed_purchase_card_declined_response
"merchantID=0000000&threeDSEnabled=Y&merchantDescription=General+test+account+with+AVS%2FCV2+checking&amount=10000¤cyCode=826&transactionUnique=7385df1d9c5484142bb6be1e932cd2df&orderRef=AM+test+purchase&customerName=Longbob+Longsen&customerAddress=25+The+Larches+&customerPostCode=LE10+2RT&action=SALE&type=1&countryCode=826&merchantAlias=0000992&remoteAddress=80.229.33.63&responseCode=5&responseMessage=CARD+DECLINED&xref=13021914RQ07HK55HG29KPH&threeDSEnrolled=U&threeDSXID=00000000000004717495&transactionID=4717495&transactionPreviousID=0×tamp=2013-02-19+14%3A08%3A18&amountReceived=0&cardNumberMask=%2A%2A%2A%2A%2A%2A%2A%2A%2A%2A%2A%2A0191&cardTypeCode=MC&cardType=Mastercard&threeDSErrorCode=-1&threeDSErrorDescription=Error+while+attempting+to+send+the+request+to%3A+https%3A%2F%2F3dstest.universalpaymentgateway.com%3A4343%2FAPI%0A%0DPlease+make+sure+that+ActiveMerchant+server+is+running+and+the+URL+is+valid.+ERROR_INTERNET_CANNOT_CONNECT%3A+The+attempt+to+connect+to+the+server+failed.&threeDSMerchantPref=PROCEED&threeDSVETimestamp=2013-02-19+14%3A07%3A55¤cyExponent=2&responseStatus=1&merchantName=CARDSTREAM+TEST&merchantID2=100001"
end
diff --git a/test/unit/gateways/cardprocess_test.rb b/test/unit/gateways/cardprocess_test.rb
new file mode 100644
index 00000000000..29abcdef429
--- /dev/null
+++ b/test/unit/gateways/cardprocess_test.rb
@@ -0,0 +1,280 @@
+require 'test_helper'
+
+class CardprocessTest < Test::Unit::TestCase
+ def setup
+ @gateway = CardprocessGateway.new(user_id: 'login', password: 'password', entity_id: '123')
+ @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)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal '8a8294495fe8084a016002dd17c163fd', response.authorization
+ assert_equal 'DB', response.params['paymentType']
+ assert response.test?
+ end
+
+ def test_failed_purchase
+ @gateway.expects(:ssl_post).returns(failed_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal Gateway::STANDARD_ERROR_CODE[:incorrect_number], response.error_code
+ end
+
+ def test_successful_authorize
+ @gateway.expects(:ssl_post).returns(successful_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal '8a82944a5fe82704016002caa42c14f8', response.authorization
+ assert_equal 'PA', response.params['paymentType']
+ assert response.test?
+ end
+
+ def test_failed_authorize
+ @gateway.expects(:ssl_post).returns(failed_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal 'PA', response.params['paymentType']
+ assert response.test?
+ end
+
+ def test_successful_capture
+ @gateway.expects(:ssl_post).returns(successful_capture_response)
+
+ response = @gateway.capture(@amount, '123', @options)
+ assert_success response
+
+ assert_equal '8a82944a5fe82704016002caa7cd1513', response.authorization
+ assert_equal 'CP', response.params['paymentType']
+ assert response.test?
+ end
+
+ def test_failed_capture
+ @gateway.expects(:ssl_post).returns(failed_capture_response)
+
+ response = @gateway.capture(@amount, '123', @options)
+ assert_failure response
+ assert response.test?
+ end
+
+ def test_successful_refund
+ @gateway.expects(:ssl_post).returns(successful_refund_response)
+
+ response = @gateway.refund(@amount, '123', @options)
+ assert_success response
+
+ assert_equal '8a82944a5fe82704016002cc88f61731', response.authorization
+ assert_equal 'RF', response.params['paymentType']
+ assert response.test?
+ end
+
+ def test_failed_refund
+ @gateway.expects(:ssl_post).returns(failed_refund_response)
+
+ response = @gateway.refund(@amount, '123', @options)
+ assert_failure response
+ end
+
+ def test_successful_void
+ @gateway.expects(:ssl_post).returns(successful_void_response)
+
+ response = @gateway.void('123')
+ assert_success response
+
+ assert_equal '8a8294495fe8084a016002cc489446d6', response.authorization
+ assert_equal 'RV', response.params['paymentType']
+ assert response.test?
+ end
+
+ def test_failed_void
+ @gateway.expects(:ssl_post).returns(failed_void_response)
+
+ response = @gateway.void('123')
+ assert_failure response
+ end
+
+ def test_successful_verify
+ @gateway.stubs(:ssl_post).returns(successful_authorize_response, successful_capture_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ assert response.test?
+ end
+
+ def test_successful_verify_with_failed_void
+ @gateway.stubs(:ssl_post).returns(successful_authorize_response, failed_void_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_failure response
+ assert response.test?
+ end
+
+ def test_failed_verify
+ @gateway.expects(:ssl_post).returns(failed_authorize_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_failure response
+ assert response.test?
+ end
+
+ def test_general_credit
+ @gateway.expects(:ssl_post).returns(successful_credit_response)
+
+ response = @gateway.credit(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal '8a8294495fe8084a01600332a83d4899', response.authorization
+ assert_equal 'CD', response.params['paymentType']
+ assert response.test?
+ end
+
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
+ def test_error_code_parsing
+ codes = {
+ '000.000.000' => nil,
+ '100.100.101' => :incorrect_number,
+ '100.100.303' => :expired_card,
+ '100.100.305' => :invalid_expiry_date,
+ '100.100.600' => :invalid_cvc,
+ '200.222.222' => :config_error,
+ '700.777.777' => :config_error,
+ '800.800.101' => :card_declined,
+ '800.800.102' => :incorrect_address,
+ '800.800.302' => :incorrect_address,
+ '800.800.150' => :card_declined,
+ '800.100.151' => :invalid_number,
+ '800.100.152' => :card_declined,
+ '800.100.153' => :incorrect_cvc,
+ '800.100.154' => :card_declined,
+ '800.800.202' => :invalid_zip
+ }
+ codes.each_pair do |code, key|
+ response = {'result' => {'code' => code}}
+ assert_equal Gateway::STANDARD_ERROR_CODE[key], @gateway.send(:error_code_from, response), "expecting #{code} => #{key}"
+ end
+ end
+
+ private
+
+ def pre_scrubbed
+ %q(
+opening connection to test.vr-pay-ecommerce.de:443...
+opened
+starting SSL for test.vr-pay-ecommerce.de:443...
+SSL established
+<- "POST /v1/payments HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: test.vr-pay-ecommerce.de\r\nContent-Length: 447\r\n\r\n"
+<- "amount=1.00¤cy=EUR&paymentBrand=VISA&card.number=4200000000000000&card.holder=Longbob+Longsen&card.expiryMonth=09&card.expiryYear=2018&card.cvv=123&billing.street1=456+My+Street&billing.street2=Apt+1&billing.city=Ottawa&billing.state=ON&billing.postcode=K1C2N6&billing.country=CA&authentication.userId=8a8294174e735d0c014e78beb6c5154f&authentication.password=cTZjAm9c87&authentication.entityId=8a8294174e735d0c014e78beb6b9154b&paymentType=DB"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Mon, 27 Nov 2017 21:40:52 GMT\r\n"
+-> "Server: Apache-Coyote/1.1\r\n"
+-> "Strict-Transport-Security: max-age=63072000; includeSubdomains; preload\r\n"
+-> "X-Content-Type-Options: nosniff\r\n"
+-> "X-XSS-Protection: 1; mode=block\r\n"
+-> "Access-Control-Allow-Origin: *\r\n"
+-> "Access-Control-Allow-Credentials: true\r\n"
+-> "X-Application-WAF-Action: allow\r\n"
+-> "Content-Type: application/json;charset=UTF-8\r\n"
+-> "Content-Length: 725\r\n"
+-> "X-Content-Type-Options: nosniff\r\n"
+-> "Connection: close\r\n"
+-> "\r\n"
+reading 725 bytes...
+-> ""
+-> "{\"id\":\"8a82944a5fe82704015fff6cf5e572b4\",\"paymentType\":\"DB\",\"paymentBrand\":\"VISA\",\"amount\":\"1.00\",\"currency\":\"EUR\",\"descriptor\":\"5901.3583.3250 OPP_Channel \",\"result\":{\"code\":\"000.100.110\",\"description\":\"Request successfully processed in 'Merchant in Integrator Test Mode'\"},\"card\":{\"bin\":\"420000\",\"last4Digits\":\"0000\",\"holder\":\"Longbob Longsen\",\"expiryMonth\":\"09\",\"expiryYear\":\"2018\"},\"billing\":{\"street1\":\"456 My Street\",\"street2\":\"Apt 1\",\"city\":\"Ottawa\",\"state\":\"ON\",\"postcode\":\"K1C2N6\",\"country\":\"CA\"},\"risk\":{\"score\":\"100\"},\"buildNumber\":\"a89317e58e01406de09ff75de6c962f2365f66e9@2017-11-27 15:38:09 +0000\",\"timestamp\":\"2017-11-27 21:40:51+0000\",\"ndc\":\"8a8294174e735d0c014e78beb6b9154b_24979bbf8c3a424cbd25e59860bb5417\"}"
+read 725 bytes
+Conn close
+ )
+ end
+
+ def post_scrubbed
+ %q(
+opening connection to test.vr-pay-ecommerce.de:443...
+opened
+starting SSL for test.vr-pay-ecommerce.de:443...
+SSL established
+<- "POST /v1/payments HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: test.vr-pay-ecommerce.de\r\nContent-Length: 447\r\n\r\n"
+<- "amount=1.00¤cy=EUR&paymentBrand=VISA&card.number=[FILTERED]&card.holder=Longbob+Longsen&card.expiryMonth=09&card.expiryYear=2018&card.cvv=[FILTERED]&billing.street1=456+My+Street&billing.street2=Apt+1&billing.city=Ottawa&billing.state=ON&billing.postcode=K1C2N6&billing.country=CA&authentication.userId=[FILTERED]&authentication.password=[FILTERED]&authentication.entityId=[FILTERED]&paymentType=DB"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Mon, 27 Nov 2017 21:40:52 GMT\r\n"
+-> "Server: Apache-Coyote/1.1\r\n"
+-> "Strict-Transport-Security: max-age=63072000; includeSubdomains; preload\r\n"
+-> "X-Content-Type-Options: nosniff\r\n"
+-> "X-XSS-Protection: 1; mode=block\r\n"
+-> "Access-Control-Allow-Origin: *\r\n"
+-> "Access-Control-Allow-Credentials: true\r\n"
+-> "X-Application-WAF-Action: allow\r\n"
+-> "Content-Type: application/json;charset=UTF-8\r\n"
+-> "Content-Length: 725\r\n"
+-> "X-Content-Type-Options: nosniff\r\n"
+-> "Connection: close\r\n"
+-> "\r\n"
+reading 725 bytes...
+-> ""
+-> "{\"id\":\"8a82944a5fe82704015fff6cf5e572b4\",\"paymentType\":\"DB\",\"paymentBrand\":\"VISA\",\"amount\":\"1.00\",\"currency\":\"EUR\",\"descriptor\":\"5901.3583.3250 OPP_Channel \",\"result\":{\"code\":\"000.100.110\",\"description\":\"Request successfully processed in 'Merchant in Integrator Test Mode'\"},\"card\":{\"bin\":\"420000\",\"last4Digits\":\"0000\",\"holder\":\"Longbob Longsen\",\"expiryMonth\":\"09\",\"expiryYear\":\"2018\"},\"billing\":{\"street1\":\"456 My Street\",\"street2\":\"Apt 1\",\"city\":\"Ottawa\",\"state\":\"ON\",\"postcode\":\"K1C2N6\",\"country\":\"CA\"},\"risk\":{\"score\":\"100\"},\"buildNumber\":\"a89317e58e01406de09ff75de6c962f2365f66e9@2017-11-27 15:38:09 +0000\",\"timestamp\":\"2017-11-27 21:40:51+0000\",\"ndc\":\"8a8294174e735d0c014e78beb6b9154b_24979bbf8c3a424cbd25e59860bb5417\"}"
+read 725 bytes
+Conn close
+ )
+ end
+
+ def successful_purchase_response
+ "{\"id\":\"8a8294495fe8084a016002dd17c163fd\",\"paymentType\":\"DB\",\"paymentBrand\":\"VISA\",\"amount\":\"1.00\",\"currency\":\"EUR\",\"descriptor\":\"0177.7272.0802 OPP_Channel \",\"result\":{\"code\":\"000.100.110\",\"description\":\"Request successfully processed in 'Merchant in Integrator Test Mode'\"},\"card\":{\"bin\":\"420000\",\"last4Digits\":\"0000\",\"holder\":\"Longbob Longsen\",\"expiryMonth\":\"09\",\"expiryYear\":\"2018\"},\"billing\":{\"street1\":\"456 My Street\",\"street2\":\"Apt 1\",\"city\":\"Ottawa\",\"state\":\"ON\",\"postcode\":\"K1C2N6\",\"country\":\"CA\"},\"risk\":{\"score\":\"100\"},\"buildNumber\":\"a89317e58e01406de09ff75de6c962f2365f66e9@2017-11-27 15:38:09 +0000\",\"timestamp\":\"2017-11-28 13:42:12+0000\",\"ndc\":\"8a8294174e735d0c014e78beb6b9154b_ed9f9af4170942719cf6e3446f823f45\"}"
+ end
+
+ def failed_purchase_response
+ "{\"id\":\"8a82944a5fe827040160030a3308412d\",\"paymentType\":\"DB\",\"paymentBrand\":\"VISA\",\"result\":{\"code\":\"100.100.101\",\"description\":\"invalid creditcard, bank account number or bank name\"},\"card\":{\"bin\":\"420000\",\"last4Digits\":\"0001\",\"holder\":\"Longbob Longsen\",\"expiryMonth\":\"09\",\"expiryYear\":\"2018\"},\"billing\":{\"street1\":\"456 My Street\",\"street2\":\"Apt 1\",\"city\":\"Ottawa\",\"state\":\"ON\",\"postcode\":\"K1C2N6\",\"country\":\"CA\"},\"buildNumber\":\"a89317e58e01406de09ff75de6c962f2365f66e9@2017-11-27 15:38:09 +0000\",\"timestamp\":\"2017-11-28 14:31:28+0000\",\"ndc\":\"8a8294174e735d0c014e78beb6b9154b_43d463600f8d429ea6ac09cf25fd9f24\"}"
+ end
+
+ def successful_authorize_response
+ "{\"id\":\"8a82944a5fe82704016002caa42c14f8\",\"paymentType\":\"PA\",\"paymentBrand\":\"VISA\",\"amount\":\"1.00\",\"currency\":\"EUR\",\"descriptor\":\"8374.4038.5698 OPP_Channel \",\"result\":{\"code\":\"000.100.110\",\"description\":\"Request successfully processed in 'Merchant in Integrator Test Mode'\"},\"card\":{\"bin\":\"420000\",\"last4Digits\":\"0000\",\"holder\":\"Longbob Longsen\",\"expiryMonth\":\"09\",\"expiryYear\":\"2018\"},\"billing\":{\"street1\":\"456 My Street\",\"street2\":\"Apt 1\",\"city\":\"Ottawa\",\"state\":\"ON\",\"postcode\":\"K1C2N6\",\"country\":\"CA\"},\"risk\":{\"score\":\"100\"},\"buildNumber\":\"a89317e58e01406de09ff75de6c962f2365f66e9@2017-11-27 15:38:09 +0000\",\"timestamp\":\"2017-11-28 13:22:03+0000\",\"ndc\":\"8a8294174e735d0c014e78beb6b9154b_fc59650eafc84da29ce10e7171e71a34\"}"
+ end
+
+ def failed_authorize_response
+ "{\"paymentType\":\"PA\",\"paymentBrand\":\"VISA\",\"result\":{\"code\":\"800.100.151\",\"description\":\"transaction declined (invalid card)\"},\"card\":{\"bin\":\"420000\",\"last4Digits\":\"0000\",\"holder\":\"Longbob Longsen\",\"expiryMonth\":\"09\",\"expiryYear\":\"2018\"},\"billing\":{\"street1\":\"456 My Street\",\"street2\":\"Apt 1\",\"city\":\"Ottawa\",\"state\":\"ON\",\"postcode\":\"K1C2N6\",\"country\":\"CA\"},\"customParameters\":{\"forceResultCode\":\"800.100.151\"},\"buildNumber\":\"a89317e58e01406de09ff75de6c962f2365f66e9@2017-11-27 15:38:09 +0000\",\"timestamp\":\"2017-11-28 13:50:31+0000\",\"ndc\":\"8a8294174e735d0c014e78beb6b9154b_1077c67bc41048ff887da9ab9ee8b89d\"}"
+ end
+
+ def successful_capture_response
+ "{\"id\":\"8a82944a5fe82704016002caa7cd1513\",\"paymentType\":\"CP\",\"amount\":\"1.00\",\"currency\":\"EUR\",\"descriptor\":\"4938.4300.2018 OPP_Channel\",\"result\":{\"code\":\"000.100.110\",\"description\":\"Request successfully processed in 'Merchant in Integrator Test Mode'\"},\"risk\":{\"score\":\"0\"},\"buildNumber\":\"a89317e58e01406de09ff75de6c962f2365f66e9@2017-11-27 15:38:09 +0000\",\"timestamp\":\"2017-11-28 13:22:03+0000\",\"ndc\":\"8a8294174e735d0c014e78beb6b9154b_fb956c7668a04e18af61519693a1d114\"}"
+ end
+
+ def failed_capture_response
+ "{\"id\":\"8a82944a5fe8270401600313d3965bca\",\"paymentType\":\"CP\",\"result\":{\"code\":\"700.400.510\",\"description\":\"capture needs at least one successful transaction of type (PA)\"},\"risk\":{\"score\":\"0\"},\"buildNumber\":\"a89317e58e01406de09ff75de6c962f2365f66e9@2017-11-27 15:38:09 +0000\",\"timestamp\":\"2017-11-28 14:41:59+0000\",\"ndc\":\"8a8294174e735d0c014e78beb6b9154b_8abfd898a6cd406f94181d9096607aea\"}"
+ end
+
+ def successful_refund_response
+ "{\"id\":\"8a82944a5fe82704016002cc88f61731\",\"paymentType\":\"RF\",\"amount\":\"1.00\",\"currency\":\"EUR\",\"descriptor\":\"3478.1411.3954 OPP_Channel\",\"result\":{\"code\":\"000.100.110\",\"description\":\"Request successfully processed in 'Merchant in Integrator Test Mode'\"},\"buildNumber\":\"a89317e58e01406de09ff75de6c962f2365f66e9@2017-11-27 15:38:09 +0000\",\"timestamp\":\"2017-11-28 13:24:07+0000\",\"ndc\":\"8a8294174e735d0c014e78beb6b9154b_99293f4fffe64105870e77b8e18c0c02\"}"
+ end
+
+ def failed_refund_response
+ "{\"result\":{\"code\":\"200.300.404\",\"description\":\"invalid or missing parameter\",\"parameterErrors\":[{\"name\":\"paymentType\",\"value\":\"RF\",\"message\":\"must be one of [PA, DB, CD, PA.CP]\"},{\"name\":\"paymentBrand\",\"value\":null,\"message\":\"card properties must be set\"}]},\"buildNumber\":\"a89317e58e01406de09ff75de6c962f2365f66e9@2017-11-27 15:38:09 +0000\",\"timestamp\":\"2017-11-28 14:33:31+0000\",\"ndc\":\"8a8294174e735d0c014e78beb6b9154b_febee8f6863b4392b064b23602f3f382\"}"
+ end
+
+ def successful_void_response
+ "{\"id\":\"8a8294495fe8084a016002cc489446d6\",\"paymentType\":\"RV\",\"amount\":\"1.00\",\"currency\":\"EUR\",\"descriptor\":\"9673.6314.6402 OPP_Channel\",\"result\":{\"code\":\"000.100.110\",\"description\":\"Request successfully processed in 'Merchant in Integrator Test Mode'\"},\"risk\":{\"score\":\"0\"},\"buildNumber\":\"a89317e58e01406de09ff75de6c962f2365f66e9@2017-11-27 15:38:09 +0000\",\"timestamp\":\"2017-11-28 13:23:50+0000\",\"ndc\":\"8a8294174e735d0c014e78beb6b9154b_744a5416960a420da01edc3b3daf6c6f\"}"
+ end
+
+ def failed_void_response
+ "{\"result\":{\"code\":\"200.300.404\",\"description\":\"invalid or missing parameter\",\"parameterErrors\":[{\"name\":\"paymentBrand\",\"value\":null,\"message\":\"card properties must be set\"},{\"name\":\"paymentType\",\"value\":\"RV\",\"message\":\"must be one of [PA, DB, CD, PA.CP]\"},{\"name\":\"amount\",\"value\":null,\"message\":\"may not be empty\"},{\"name\":\"currency\",\"value\":null,\"message\":\"may not be empty\"}]},\"buildNumber\":\"a89317e58e01406de09ff75de6c962f2365f66e9@2017-11-27 15:38:09 +0000\",\"timestamp\":\"2017-11-28 14:34:50+0000\",\"ndc\":\"8a8294174e735d0c014e78beb6b9154b_4a909e0b99214eb9b155b46a2c67df30\"}"
+ end
+
+ def successful_credit_response
+ "{\"id\":\"8a8294495fe8084a01600332a83d4899\",\"paymentType\":\"CD\",\"paymentBrand\":\"VISA\",\"amount\":\"1.00\",\"currency\":\"EUR\",\"descriptor\":\"2299.3739.4338 OPP_Channel \",\"result\":{\"code\":\"000.100.110\",\"description\":\"Request successfully processed in 'Merchant in Integrator Test Mode'\"},\"card\":{\"bin\":\"420000\",\"last4Digits\":\"0000\",\"holder\":\"Longbob Longsen\",\"expiryMonth\":\"09\",\"expiryYear\":\"2018\"},\"billing\":{\"street1\":\"456 My Street\",\"street2\":\"Apt 1\",\"city\":\"Ottawa\",\"state\":\"ON\",\"postcode\":\"K1C2N6\",\"country\":\"CA\"},\"risk\":{\"score\":\"100\"},\"buildNumber\":\"a89317e58e01406de09ff75de6c962f2365f66e9@2017-11-27 15:38:09 +0000\",\"timestamp\":\"2017-11-28 15:15:39+0000\",\"ndc\":\"8a8294174e735d0c014e78beb6b9154b_691783d2e7834e6eb8ca011f4fee1b74\"}"
+ end
+end
diff --git a/test/unit/gateways/cashnet_test.rb b/test/unit/gateways/cashnet_test.rb
index 52ba6f8255c..a5f1cbc7efb 100644
--- a/test/unit/gateways/cashnet_test.rb
+++ b/test/unit/gateways/cashnet_test.rb
@@ -141,6 +141,11 @@ def test_allows_custcode_override
end.respond_with(successful_purchase_response)
end
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
private
def expected_expiration_date
'%02d%02d' % [@credit_card.month, @credit_card.year.to_s[2..4]]
@@ -169,4 +174,130 @@ def failed_purchase_response
def invalid_response
"A String without a cngateway tag"
end
+
+ def pre_scrubbed
+ <<-TRANSCRIPT
+opening connection to train.cashnet.com:443...
+opened
+starting SSL for train.cashnet.com:443...
+SSL established
+<- "POST /givecorpsgateway HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: train.cashnet.com\r\nContent-Length: 364\r\n\r\n"
+<- "command=SALE&merchant=GiveCorpGW&operator=givecorp&password=14givecorps&station=WEB&custcode=ActiveMerchant%2F1.76.0&cardno=5454545454545454&cid=123&expdate=1215&card_name_g=Longbob+Longsen&fname=Longbob&lname=Longsen&order_number=c440ec8493f215d21c8a993ceae30129&itemcode=FEE&addr_g=456+My+Street%2CApt+1&city_g=Ottawa&state_g=ON&zip_g=K1C2N6&email_g=&amount=1.00"
+-> "HTTP/1.1 302 Found\r\n"
+-> "Date: Wed, 03 Jan 2018 17:03:35 GMT\r\n"
+-> "Content-Type: text/html; charset=utf-8\r\n"
+-> "Transfer-Encoding: chunked\r\n"
+-> "Connection: close\r\n"
+-> "Set-Cookie: AWSALB=5ISjTg8Mez7jS1kEnzY4j5NkQ5bdlwDDNmfzTyEMBmILpb0Tn3k58pUQTGHBj3NUpciP0uqQs7FaAb42YZvt35ndLERGJA0dPQ03iCfrqbneQ+Wm5BhDzMGo5GUT; Expires=Wed, 10 Jan 2018 17:03:35 GMT; Path=/\r\n"
+-> "Set-Cookie: AWSALB=bVhwwfJ2D6cI5zB3eapqNStEzF5yX1pXhaJGUBUCa+DZhEgn/TZGxznxIOYB9qKqzkPF4lq/zxWg/tuMBTiY4JGLRjayyhizvHnj2smrnNvr2DLQN7ZjLSh51BzM; Expires=Wed, 10 Jan 2018 17:03:35 GMT; Path=/\r\n"
+-> "Cache-Control: private\r\n"
+-> "Location: https://train.cashnet.com/cashneti/Gateway/htmlgw.aspx?client=EMARKETVENDOR_DEMO&command=SALE&merchant=GiveCorpGW&operator=givecorp&password=14givecorps&station=WEB&custcode=ActiveMerchant%2f1.76.0&cardno=5454545454545454&cid=123&expdate=1215&card_name_g=Longbob+Longsen&fname=Longbob&lname=Longsen&order_number=c440ec8493f215d21c8a993ceae30129&itemcode=FEE&addr_g=456+My+Street%2cApt+1&city_g=Ottawa&state_g=ON&zip_g=K1C2N6&email_g=&amount=1.00\r\n"
+-> "Set-Cookie: ASP.NET_SessionId=; path=/; HttpOnly\r\n"
+-> "P3P: CP=\"NOI DSP COR NID NOR\"\r\n"
+-> "Set-Cookie: BNI_persistence=0000000000000000000000004d79da0a00005000; Path=/\r\n"
+-> "Strict-Transport-Security: max-age=31536000\r\n"
+-> "\r\n"
+-> "282\r\n"
+reading 642 bytes...
+-> "Object moved\r\nObject moved to here.
\r\n\r\n"
+read 642 bytes
+reading 2 bytes...
+-> "\r\n"
+read 2 bytes
+-> "0\r\n"
+-> "\r\n"
+Conn close
+opening connection to train.cashnet.com:443...
+opened
+starting SSL for train.cashnet.com:443...
+SSL established
+<- "GET /cashneti/Gateway/htmlgw.aspx?client=EMARKETVENDOR_DEMO&command=SALE&merchant=GiveCorpGW&operator=givecorp&password=14givecorps&station=WEB&custcode=ActiveMerchant%2f1.76.0&cardno=5454545454545454&cid=123&expdate=1215&card_name_g=Longbob+Longsen&fname=Longbob&lname=Longsen&order_number=c440ec8493f215d21c8a993ceae30129&itemcode=FEE&addr_g=456+My+Street%2cApt+1&city_g=Ottawa&state_g=ON&zip_g=K1C2N6&email_g=&amount=1.00 HTTP/1.1\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: train.cashnet.com\r\n\r\n"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Wed, 03 Jan 2018 17:03:35 GMT\r\n"
+-> "Content-Type: text/html; charset=utf-8\r\n"
+-> "Transfer-Encoding: chunked\r\n"
+-> "Connection: close\r\n"
+-> "Set-Cookie: AWSALB=lFPwFYRnXJHRNmE6NCRAIfHtQadwx4bYJoT5xeAL5AuAXPcm1vYWx5F/s5FBr3GcungifktpWlwIgAmWS29K7YRXTCjk4xmcAnhXS86fpVUVQt4ECwPH2xdv8tf2; Expires=Wed, 10 Jan 2018 17:03:35 GMT; Path=/\r\n"
+-> "Set-Cookie: AWSALB=mEfysFNBclo1/9+tTuI/XtHrmVkD89Fh6tAJ3Gl0u2EuLCYTW5VwEq+fVqYG1fEkN02dbhKSkIdM22QvyT6cRccDaUBsYAnOKjg2JlVShJlf+li5tfbrsUDk14jG; Expires=Wed, 10 Jan 2018 17:03:35 GMT; Path=/\r\n"
+-> "Cache-Control: private\r\n"
+-> "Set-Cookie: ASP.NET_SessionId=3ocslggtk4cdz54unbdnm25o; path=/; HttpOnly\r\n"
+-> "P3P: CP=\"NOI DSP COR NID NOR\"\r\n"
+-> "Set-Cookie: BNI_persistence=0000000000000000000000004d79da0a00005000; Path=/\r\n"
+-> "Strict-Transport-Security: max-age=31536000\r\n"
+-> "\r\n"
+-> "3a\r\n"
+reading 58 bytes...
+-> "result=0&tx=77972&busdate=7/25/2017"
+read 58 bytes
+reading 2 bytes...
+-> "\r\n"
+read 2 bytes
+-> "0\r\n"
+-> "\r\n"
+Conn close
+TRANSCRIPT
+ end
+
+ def post_scrubbed
+ <<-SCRUBBED
+opening connection to train.cashnet.com:443...
+opened
+starting SSL for train.cashnet.com:443...
+SSL established
+<- "POST /givecorpsgateway HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: train.cashnet.com\r\nContent-Length: 364\r\n\r\n"
+<- "command=SALE&merchant=GiveCorpGW&operator=givecorp&password=[FILTERED]&station=WEB&custcode=ActiveMerchant%2F1.76.0&cardno=[FILTERED]&cid=[FILTERED]&expdate=1215&card_name_g=Longbob+Longsen&fname=Longbob&lname=Longsen&order_number=c440ec8493f215d21c8a993ceae30129&itemcode=FEE&addr_g=456+My+Street%2CApt+1&city_g=Ottawa&state_g=ON&zip_g=K1C2N6&email_g=&amount=1.00"
+-> "HTTP/1.1 302 Found\r\n"
+-> "Date: Wed, 03 Jan 2018 17:03:35 GMT\r\n"
+-> "Content-Type: text/html; charset=utf-8\r\n"
+-> "Transfer-Encoding: chunked\r\n"
+-> "Connection: close\r\n"
+-> "Set-Cookie: AWSALB=5ISjTg8Mez7jS1kEnzY4j5NkQ5bdlwDDNmfzTyEMBmILpb0Tn3k58pUQTGHBj3NUpciP0uqQs7FaAb42YZvt35ndLERGJA0dPQ03iCfrqbneQ+Wm5BhDzMGo5GUT; Expires=Wed, 10 Jan 2018 17:03:35 GMT; Path=/\r\n"
+-> "Set-Cookie: AWSALB=bVhwwfJ2D6cI5zB3eapqNStEzF5yX1pXhaJGUBUCa+DZhEgn/TZGxznxIOYB9qKqzkPF4lq/zxWg/tuMBTiY4JGLRjayyhizvHnj2smrnNvr2DLQN7ZjLSh51BzM; Expires=Wed, 10 Jan 2018 17:03:35 GMT; Path=/\r\n"
+-> "Cache-Control: private\r\n"
+-> "Location: https://train.cashnet.com/cashneti/Gateway/htmlgw.aspx?client=EMARKETVENDOR_DEMO&command=SALE&merchant=GiveCorpGW&operator=givecorp&password=[FILTERED]&station=WEB&custcode=ActiveMerchant%2f1.76.0&cardno=[FILTERED]&cid=[FILTERED]&expdate=1215&card_name_g=Longbob+Longsen&fname=Longbob&lname=Longsen&order_number=c440ec8493f215d21c8a993ceae30129&itemcode=FEE&addr_g=456+My+Street%2cApt+1&city_g=Ottawa&state_g=ON&zip_g=K1C2N6&email_g=&amount=1.00\r\n"
+-> "Set-Cookie: ASP.NET_SessionId=; path=/; HttpOnly\r\n"
+-> "P3P: CP=\"NOI DSP COR NID NOR\"\r\n"
+-> "Set-Cookie: BNI_persistence=0000000000000000000000004d79da0a00005000; Path=/\r\n"
+-> "Strict-Transport-Security: max-age=31536000\r\n"
+-> "\r\n"
+-> "282\r\n"
+reading 642 bytes...
+-> "Object moved\r\nObject moved to here.
\r\n\r\n"
+read 642 bytes
+reading 2 bytes...
+-> "\r\n"
+read 2 bytes
+-> "0\r\n"
+-> "\r\n"
+Conn close
+opening connection to train.cashnet.com:443...
+opened
+starting SSL for train.cashnet.com:443...
+SSL established
+<- "GET /cashneti/Gateway/htmlgw.aspx?client=EMARKETVENDOR_DEMO&command=SALE&merchant=GiveCorpGW&operator=givecorp&password=[FILTERED]&station=WEB&custcode=ActiveMerchant%2f1.76.0&cardno=[FILTERED]&cid=[FILTERED]&expdate=1215&card_name_g=Longbob+Longsen&fname=Longbob&lname=Longsen&order_number=c440ec8493f215d21c8a993ceae30129&itemcode=FEE&addr_g=456+My+Street%2cApt+1&city_g=Ottawa&state_g=ON&zip_g=K1C2N6&email_g=&amount=1.00 HTTP/1.1\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: train.cashnet.com\r\n\r\n"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Wed, 03 Jan 2018 17:03:35 GMT\r\n"
+-> "Content-Type: text/html; charset=utf-8\r\n"
+-> "Transfer-Encoding: chunked\r\n"
+-> "Connection: close\r\n"
+-> "Set-Cookie: AWSALB=lFPwFYRnXJHRNmE6NCRAIfHtQadwx4bYJoT5xeAL5AuAXPcm1vYWx5F/s5FBr3GcungifktpWlwIgAmWS29K7YRXTCjk4xmcAnhXS86fpVUVQt4ECwPH2xdv8tf2; Expires=Wed, 10 Jan 2018 17:03:35 GMT; Path=/\r\n"
+-> "Set-Cookie: AWSALB=mEfysFNBclo1/9+tTuI/XtHrmVkD89Fh6tAJ3Gl0u2EuLCYTW5VwEq+fVqYG1fEkN02dbhKSkIdM22QvyT6cRccDaUBsYAnOKjg2JlVShJlf+li5tfbrsUDk14jG; Expires=Wed, 10 Jan 2018 17:03:35 GMT; Path=/\r\n"
+-> "Cache-Control: private\r\n"
+-> "Set-Cookie: ASP.NET_SessionId=3ocslggtk4cdz54unbdnm25o; path=/; HttpOnly\r\n"
+-> "P3P: CP=\"NOI DSP COR NID NOR\"\r\n"
+-> "Set-Cookie: BNI_persistence=0000000000000000000000004d79da0a00005000; Path=/\r\n"
+-> "Strict-Transport-Security: max-age=31536000\r\n"
+-> "\r\n"
+-> "3a\r\n"
+reading 58 bytes...
+-> "result=0&tx=77972&busdate=7/25/2017"
+read 58 bytes
+reading 2 bytes...
+-> "\r\n"
+read 2 bytes
+-> "0\r\n"
+-> "\r\n"
+Conn close
+SCRUBBED
+ end
end
diff --git a/test/unit/gateways/checkout_v2_test.rb b/test/unit/gateways/checkout_v2_test.rb
index 2ee60065908..1724f172730 100644
--- a/test/unit/gateways/checkout_v2_test.rb
+++ b/test/unit/gateways/checkout_v2_test.rb
@@ -18,11 +18,48 @@ def test_successful_purchase
end.respond_with(successful_purchase_response)
assert_success response
-
assert_equal 'charge_test_941CA9CE174U76BD29C8', response.authorization
assert response.test?
end
+ def test_successful_purchase_includes_avs_result
+ response = stub_comms do
+ @gateway.purchase(@amount, @credit_card)
+ end.respond_with(successful_purchase_response)
+
+ assert_equal 'S', response.avs_result["code"]
+ assert_equal 'U.S.-issuing bank does not support AVS.', response.avs_result["message"]
+ assert_equal 'X', response.avs_result["postal_match"]
+ assert_equal 'X', response.avs_result["street_match"]
+ end
+
+ def test_successful_purchase_includes_cvv_result
+ response = stub_comms do
+ @gateway.purchase(@amount, @credit_card)
+ end.respond_with(successful_purchase_response)
+
+ assert_equal 'Y', response.cvv_result["code"]
+ end
+
+ def test_successful_authorize_includes_avs_result
+ response = stub_comms do
+ @gateway.authorize(@amount, @credit_card)
+ end.respond_with(successful_authorize_response)
+
+ assert_equal 'S', response.avs_result["code"]
+ assert_equal 'U.S.-issuing bank does not support AVS.', response.avs_result["message"]
+ assert_equal 'X', response.avs_result["postal_match"]
+ assert_equal 'X', response.avs_result["street_match"]
+ end
+
+ def test_successful_authorize_includes_cvv_result
+ response = stub_comms do
+ @gateway.authorize(@amount, @credit_card)
+ end.respond_with(successful_authorize_response)
+
+ assert_equal 'Y', response.cvv_result["code"]
+ end
+
def test_purchase_with_additional_fields
response = stub_comms do
@gateway.purchase(@amount, @credit_card, {descriptor_city: "london", descriptor_name: "sherlock"})
@@ -47,7 +84,31 @@ def test_successful_authorize_and_capture
end.respond_with(successful_authorize_response)
assert_success response
- assert_equal "charge_test_941CA9CE174U76BD29C8", response.authorization
+ assert_equal "charge_test_AF1A29AD350Q748C7EA8", response.authorization
+
+ capture = stub_comms do
+ @gateway.capture(@amount, response.authorization)
+ end.respond_with(successful_capture_response)
+
+ assert_success capture
+ end
+
+ def test_successful_authorize_and_capture_with_additional_options
+ response = stub_comms do
+ options = {
+ card_on_file: true,
+ transaction_indicator: 2,
+ previous_charge_id: "charge_123"
+ }
+ @gateway.authorize(@amount, @credit_card, options)
+ end.check_request do |endpoint, data, headers|
+ assert_match(%r{"cardOnFile":true}, data)
+ assert_match(%r{"transactionIndicator":2}, data)
+ assert_match(%r{"previousChargeId":"charge_123"}, data)
+ end.respond_with(successful_authorize_response)
+
+ assert_success response
+ assert_equal "charge_test_AF1A29AD350Q748C7EA8", response.authorization
capture = stub_comms do
@gateway.capture(@amount, response.authorization)
@@ -80,7 +141,7 @@ def test_successful_void
end.respond_with(successful_authorize_response)
assert_success response
- assert_equal "charge_test_941CA9CE174U76BD29C8", response.authorization
+ assert_equal "charge_test_AF1A29AD350Q748C7EA8", response.authorization
void = stub_comms do
@gateway.void(response.authorization)
@@ -149,6 +210,18 @@ def test_invalid_json
assert_match %r{Invalid JSON response}, response.message
end
+ def test_error_code_returned
+ response = stub_comms do
+ @gateway.purchase(@amount, @credit_card)
+ end.respond_with(error_code_response)
+
+ assert_failure response
+ assert_match /70000: 70077/, response.error_code
+ end
+
+ def test_supported_countries
+ assert_equal ['AD', 'AE', 'AT', 'BE', 'BG', 'CH', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FO', 'FI', 'FR', 'GB', 'GI', 'GL', 'GR', 'HR', 'HU', 'IE', 'IS', 'IL', 'IT', 'LI', 'LT', 'LU', 'LV', 'MC', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SE', 'SI', 'SM', 'SK', 'SJ', 'TR', 'VA'], @gateway.supported_countries
+ end
private
@@ -218,23 +291,68 @@ def failed_purchase_response
def successful_authorize_response
%(
- {
- "id":"charge_test_941CA9CE174U76BD29C8",
- "liveMode":false,
- "created":"2015-05-27T20:45:58Z",
- "value":200.0,
- "currency":"USD",
- "trackId":"1",
- "description":null,
- "email":"longbob.longsen@gmail.com",
- "chargeMode":1,
- "transactionIndicator":1,
- "customerIp":null,
- "responseMessage":"Authorised",
- "responseAdvancedInfo":"Authorised",
- "responseCode":"10000"
- }
- )
+ {
+ "id":"charge_test_AF1A29AD350Q748C7EA8",
+ "liveMode":false,
+ "created":"2017-11-13T14:05:27Z",
+ "value":200,
+ "currency":"USD",
+ "trackId":"1",
+ "description":null,
+ "email":"longbob.longsen@example.com",
+ "chargeMode":1,
+ "transactionIndicator":1,
+ "customerIp":null,
+ "responseMessage":"Approved",
+ "responseAdvancedInfo":"Approved",
+ "responseCode":"10000",
+ "status":"Authorised",
+ "authCode":"923189",
+ "isCascaded":false,
+ "autoCapture":"N",
+ "autoCapTime":0.0,
+ "card":{"customerId":
+ "cust_12DCEB24-ACEA-48AB-BEF2-35A3C09BE581",
+ "expiryMonth":"06",
+ "expiryYear":"2018",
+ "billingDetails":{
+ "addressLine1":"456 My Street",
+ "addressLine2":"Apt 1",
+ "postcode":"K1C2N6",
+ "country":"CA",
+ "city":"Ottawa",
+ "state":"ON",
+ "phone":{"number":"(555)555-5555"}
+ },
+ "id":"card_CFA314F4-388D-4CF4-BE6F-940D894C9E64",
+ "last4":"4242",
+ "bin":"424242",
+ "paymentMethod":"Visa",
+ "fingerprint":"F639CAB2745BEE4140BF86DF6B6D6E255C5945AAC3788D923FA047EA4C208622",
+ "name":"Longbob Longsen",
+ "cvvCheck":"Y",
+ "avsCheck":"S"
+ },
+ "riskCheck":true,
+ "customerPaymentPlans":null,
+ "metadata":{},
+ "shippingDetails":{
+ "addressLine1":null,
+ "addressLine2":null,
+ "postcode":null,
+ "country":null,
+ "city":null,
+ "state":null,
+ "phone":{}
+ },
+ "products":[],
+ "udf1":null,
+ "udf2":null,
+ "udf3":null,
+ "udf4":null,
+ "udf5":null
+ }
+ )
end
def failed_authorize_response
@@ -355,5 +473,11 @@ def invalid_json_response
)
end
-
+ def error_code_response
+ %(
+ {
+ "eventId":"1b206f69-b4db-4259-9713-b72dfe0f19da","errorCode":"70000","message":"Validation error","errorMessageCodes":["70077"],"errors":["Expired Card"]
+ }
+ )
+ end
end
diff --git a/test/unit/gateways/conekta_test.rb b/test/unit/gateways/conekta_test.rb
index 198d6e3590f..c959c214722 100644
--- a/test/unit/gateways/conekta_test.rb
+++ b/test/unit/gateways/conekta_test.rb
@@ -34,7 +34,7 @@ def setup
:city => "Guerrero",
:country => "Mexico",
:zip => "5555",
- :name => "Mario Reyes",
+ :customer => "Mario Reyes",
:phone => "12345678",
:carrier => "Estafeta"
}
@@ -81,6 +81,36 @@ def test_successful_authorize
assert response.test?
end
+ def test_successful_void
+ @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?
+
+ @gateway.expects(:ssl_request).returns(successful_void_response)
+ assert response = @gateway.void(successful_authorize_response['id'])
+ assert_success response
+ assert_instance_of Response, response
+ assert_equal nil, response.message
+ assert response.test?
+ end
+
+ def test_unsuccessful_void
+ @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?
+
+ @gateway.expects(:ssl_request).returns(failed_void_response)
+ assert response = @gateway.void(successful_authorize_response['id'])
+ assert_failure response
+ assert response.test?
+ end
+
def test_unsuccessful_authorize
@gateway.expects(:ssl_request).returns(failed_authorize_response)
assert response = @gateway.authorize(@amount, @declined_card, @options)
@@ -259,6 +289,65 @@ def successful_authorize_response
}.to_json
end
+ def successful_void_response
+ {
+ 'id' => '521b859fcfc26c0f180002d9',
+ 'livemode' => false,
+ 'created_at' => 1377535391,
+ 'status' => 'voided',
+ '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',
diff --git a/test/unit/gateways/creditcall_test.rb b/test/unit/gateways/creditcall_test.rb
index 855fda0056d..e0bf6105673 100644
--- a/test/unit/gateways/creditcall_test.rb
+++ b/test/unit/gateways/creditcall_test.rb
@@ -126,6 +126,32 @@ def test_verification_value_not_sent
end.respond_with(successful_authorize_response)
end
+ def test_options_add_avs_additional_verification_fields
+ stub_comms do
+ @gateway.authorize(@amount, @credit_card, @options)
+ end.check_request do |endpoint, data, headers|
+ assert_no_match(/AdditionalVerification/, data)
+ end.respond_with(successful_authorize_response)
+
+ stub_comms do
+ @gateway.authorize(@amount, @credit_card, @options.merge(verify_zip: 'false', verify_address: 'false'))
+ end.check_request do |endpoint, data, headers|
+ assert_no_match(/AdditionalVerification/, data)
+ end.respond_with(successful_authorize_response)
+
+ stub_comms do
+ @gateway.authorize(@amount, @credit_card, @options.merge(verify_zip: 'true', verify_address: 'true'))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/\n K1C2N6<\/Zip>\n /, data)
+ end.respond_with(successful_authorize_response)
+
+ stub_comms do
+ @gateway.authorize(@amount, @credit_card, @options.merge(verify_zip: 'true', verify_address: 'false'))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/ \n K1C2N6<\/Zip>\n <\/AdditionalVerification>\n/, data)
+ end.respond_with(successful_authorize_response)
+ end
+
def test_scrub
assert @gateway.supports_scrubbing?
assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
@@ -147,7 +173,7 @@ def post_scrubbed
def successful_purchase_response
%(
- 0999da90-b342-e511-b302-00505692354f20150814143753201508141837530Unknown
+ 0999da90-b342-e511-b302-00505692354f20150814143753201508141837530Unknownc2c5fa63-3dd1-da11-8531-01422187e378CtuNPQnryhFt6amPWtp6PLZYXI=341111xxxxx10022012AMEX
)
end
diff --git a/test/unit/gateways/credorax_test.rb b/test/unit/gateways/credorax_test.rb
new file mode 100644
index 00000000000..6a404ded066
--- /dev/null
+++ b/test/unit/gateways/credorax_test.rb
@@ -0,0 +1,313 @@
+require 'test_helper'
+
+class CredoraxTest < Test::Unit::TestCase
+ include CommStub
+
+ def setup
+ @gateway = CredoraxGateway.new(merchant_id: 'login', cipher_key: 'password')
+ @credit_card = credit_card
+ @amount = 100
+
+ @options = {
+ billing_address: address,
+ description: 'Store Purchase'
+ }
+ end
+
+ def test_successful_purchase
+ response = stub_comms do
+ @gateway.purchase(@amount, @credit_card)
+ end.check_request do |endpoint, data, headers|
+ assert_no_match(/i8=sample-eci%3Asample-cavv%3Asample-xid/, data)
+ end.respond_with(successful_purchase_response)
+
+ assert_success response
+
+ assert_equal "8a82944a5351570601535955efeb513c;006596;02617cf5f02ccaed239b6521748298c5;purchase", response.authorization
+ assert response.test?
+ end
+
+ def test_failed_purchase
+ response = stub_comms do
+ @gateway.purchase(@amount, @credit_card)
+ end.respond_with(failed_purchase_response)
+
+ assert_failure response
+ assert_equal "Transaction not allowed for cardholder", response.message
+ assert response.test?
+ end
+
+ def test_successful_authorize_and_capture
+ response = stub_comms do
+ @gateway.authorize(@amount, @credit_card)
+ end.respond_with(successful_authorize_response)
+
+ assert_success response
+ assert_equal "8a829449535154bc0153595952a2517a;006597;90f7449d555f7bed0a2c5d780475f0bf;authorize", response.authorization
+
+ capture = stub_comms do
+ @gateway.capture(@amount, response.authorization)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/8a829449535154bc0153595952a2517a/, data)
+ end.respond_with(successful_capture_response)
+
+ assert_success capture
+ end
+
+ def test_failed_authorize
+ response = stub_comms do
+ @gateway.authorize(@amount, @credit_card)
+ end.respond_with(failed_authorize_response)
+
+ assert_failure response
+ assert_equal "Transaction not allowed for cardholder", response.message
+ assert response.test?
+ end
+
+ def test_failed_capture
+ response = stub_comms do
+ @gateway.capture(100, "")
+ end.respond_with(failed_capture_response)
+
+ assert_failure response
+ end
+
+ def test_successful_void
+ response = stub_comms do
+ @gateway.purchase(@amount, @credit_card)
+ end.respond_with(successful_authorize_response)
+
+ assert_success response
+ assert_equal "8a829449535154bc0153595952a2517a;006597;90f7449d555f7bed0a2c5d780475f0bf;purchase", response.authorization
+
+ void = stub_comms do
+ @gateway.void(response.authorization)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/8a829449535154bc0153595952a2517a/, data)
+ end.respond_with(successful_void_response)
+
+ assert_success void
+ end
+
+ def test_failed_void
+ response = stub_comms do
+ @gateway.void("5d53a33d960c46d00f5dc061947d998c")
+ end.check_request do |endpoint, data, headers|
+ assert_match(/5d53a33d960c46d00f5dc061947d998c/, data)
+ end.respond_with(failed_void_response)
+
+ assert_failure response
+ end
+
+ def test_successful_refund
+ response = stub_comms do
+ @gateway.purchase(@amount, @credit_card)
+ end.respond_with(successful_purchase_response)
+
+ assert_success response
+ assert_equal "8a82944a5351570601535955efeb513c;006596;02617cf5f02ccaed239b6521748298c5;purchase", response.authorization
+
+ refund = stub_comms do
+ @gateway.refund(@amount, response.authorization)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/8a82944a5351570601535955efeb513c/, data)
+ end.respond_with(successful_refund_response)
+
+ assert_success refund
+ end
+
+ def test_failed_refund
+ response = stub_comms do
+ @gateway.refund(nil, "")
+ end.respond_with(failed_refund_response)
+
+ assert_failure response
+ end
+
+ def test_successful_credit
+ response = stub_comms do
+ @gateway.credit(@amount, @credit_card)
+ end.respond_with(successful_credit_response)
+
+ assert_success response
+
+ assert_equal "8a82944a53515706015359604c135301;;868f8b942fae639d28e27e8933d575d4;credit", response.authorization
+ assert response.test?
+ end
+
+ def test_failed_credit
+ response = stub_comms do
+ @gateway.credit(@amount, @credit_card)
+ end.respond_with(failed_credit_response)
+
+ assert_failure response
+ assert_equal "Transaction not allowed for cardholder", response.message
+ assert response.test?
+ end
+
+ def test_successful_verify
+ response = stub_comms do
+ @gateway.verify(@credit_card)
+ end.respond_with(successful_authorize_response, failed_void_response)
+ assert_success response
+ assert_equal "Succeeded", response.message
+ end
+
+ def test_failed_verify
+ response = stub_comms do
+ @gateway.verify(@credit_card)
+ end.respond_with(failed_authorize_response, successful_void_response)
+ assert_failure response
+ assert_equal "Transaction not allowed for cardholder", response.message
+ end
+
+ def test_empty_response_fails
+ response = stub_comms do
+ @gateway.purchase(@amount, @credit_card)
+ end.respond_with(empty_purchase_response)
+
+ assert_failure response
+ assert_equal "Unable to read error message", response.message
+ end
+
+ def test_transcript_scrubbing
+ assert_equal scrubbed_transcript, @gateway.scrub(transcript)
+ end
+
+ def test_adds_3d_secure_fields
+ options_with_3ds = @options.merge({eci: "sample-eci", cavv: "sample-cavv", xid: "sample-xid"})
+
+ response = stub_comms do
+ @gateway.purchase(@amount, @credit_card, options_with_3ds)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/i8=sample-eci%3Asample-cavv%3Asample-xid/, data)
+ end.respond_with(successful_purchase_response)
+
+ assert_success response
+
+ assert_equal "8a82944a5351570601535955efeb513c;006596;02617cf5f02ccaed239b6521748298c5;purchase", response.authorization
+ assert response.test?
+ end
+
+ def test_defaults_3d_secure_cavv_field_to_none_if_not_present
+ options_with_3ds = @options.merge({eci: "sample-eci", xid: "sample-xid"})
+
+ response = stub_comms do
+ @gateway.purchase(@amount, @credit_card, options_with_3ds)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/i8=sample-eci%3Anone%3Asample-xid/, data)
+ end.respond_with(successful_purchase_response)
+
+ assert_success response
+
+ assert_equal "8a82944a5351570601535955efeb513c;006596;02617cf5f02ccaed239b6521748298c5;purchase", response.authorization
+ assert response.test?
+ end
+
+ def test_adds_a9_field
+ options_with_3ds = @options.merge({transaction_type: '8'})
+
+ stub_comms do
+ @gateway.purchase(@amount, @credit_card, options_with_3ds)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/a9=8/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
+ private
+
+ def successful_purchase_response
+ "M=SPREE978&O=1&T=03%2F09%2F2016+03%3A05%3A16&V=413&a1=02617cf5f02ccaed239b6521748298c5&a2=2&a4=100&a9=6&z1=8a82944a5351570601535955efeb513c&z13=606944188282&z14=U&z15=100&z2=0&z3=Transaction+has+been+executed+successfully.&z4=006596&z5=0&z6=00&z9=X&K=057e123af2fba5a37b4df76a7cb5cfb6"
+ end
+
+ def failed_purchase_response
+ "M=SPREE978&O=1&T=03%2F09%2F2016+03%3A05%3A47&V=413&a1=92176aca194ceafdb4a679389b77f207&a2=2&a4=100&a9=6&z1=8a82944a535157060153595668fd5162&z13=606944188283&z15=100&z2=05&z3=Transaction+has+been+declined.&z5=0&z6=57&K=2d44820a5a907ff820f928696e460ce1"
+ end
+
+ def successful_authorize_response
+ "M=SPREE978&O=2&T=03%2F09%2F2016+03%3A08%3A58&V=413&a1=90f7449d555f7bed0a2c5d780475f0bf&a2=2&a4=100&a9=6&z1=8a829449535154bc0153595952a2517a&z13=606944188284&z14=U&z15=100&z2=0&z3=Transaction+has+been+executed+successfully.&z4=006597&z5=0&z6=00&z9=X&K=00effd2c80ab7ecd45b499c0bbea3d20"
+ end
+
+ def failed_authorize_response
+ "M=SPREE978&O=2&T=03%2F09%2F2016+03%3A10%3A02&V=413&a1=9bd85e23639ffcd5206f8e7fe4e3d365&a2=2&a4=100&a9=6&z1=8a829449535154bc0153595a4bb051ac&z13=606944188285&z15=100&z2=05&z3=Transaction+has+been+declined.&z5=0&z6=57&K=2fe3ee6b975d1e4ba542c1e7549056f6"
+ end
+
+ def successful_capture_response
+ "M=SPREE978&O=3&T=03%2F09%2F2016+03%3A09%3A03&V=413&a1=2a349969e0ed61fb0db59fc9f32d2fb3&a2=2&a4=100&g2=8a829449535154bc0153595952a2517a&g3=006597&g4=90f7449d555f7bed0a2c5d780475f0bf&z1=8a82944a535157060153595966ba51f9&z13=606944188284&z15=100&z2=0&z3=Transaction+has+been+executed+successfully.&z4=006597&z5=0&z6=00&K=4ad979199490a8d000302735220edfa6"
+ end
+
+ def failed_capture_response
+ "M=SPREE978&O=3&T=03%2F09%2F2016+03%3A10%3A33&V=413&a1=eed7c896e1355dc4007c0c8df44d5852&a2=2&a4=100&a5=EUR&b1=-&z1=1A-1&z2=-9&z3=2.+At+least+one+of+input+parameters+is+malformed.%3A+Parameter+%5Bg4%5D+cannot+be+empty.&K=8d1d8f2f9feeb7909aa3e6c428903d57"
+ end
+
+ def successful_void_response
+ "M=SPREE978&O=4&T=03%2F09%2F2016+03%3A11%3A11&V=413&a1=&a2=2&a4=100&g2=8a82944a535157060153595b484a524d&g3=006598&g4=0d600bf50198059dbe61979f8c28aab2&z1=8a829449535154bc0153595b57c351d2&z13=606944188287&z15=100&z2=0&z3=Transaction+has+been+executed+successfully.&z4=006598&z5=0&z6=00&K=e643b9e88b35fd69d5421b59c611a6c9"
+ end
+
+ def failed_void_response
+ "M=SPREE978&O=4&T=03%2F09%2F2016+03%3A11%3A37&V=413&a1=-&a2=2&a4=-&a5=-&b1=-&z1=1A-1&z2=-9&z3=2.+At+least+one+of+input+parameters+is+malformed.%3A+Parameter+%5Bg4%5D+cannot+be+empty.&K=1e6683cd7b1d01712f12ce7bfc9a5ad2"
+ end
+
+ def successful_refund_response
+ "M=SPREE978&O=5&T=03%2F09%2F2016+03%3A15%3A32&V=413&a1=b449bb41af3eb09fd483e7629eb2266f&a2=2&a4=100&g2=8a82944a535157060153595f3ea352c2&g3=006600&g4=78141b277cfadba072a0bcb90745faef&z1=8a82944a535157060153595f553a52de&z13=606944188288&z15=100&z2=0&z3=Transaction+has+been+executed+successfully.&z4=006600&z5=0&z6=00&K=bfdfd8b0dcee974c07c3c85cfea753fe"
+ end
+
+ def failed_refund_response
+ "M=SPREE978&O=5&T=03%2F09%2F2016+03%3A16%3A06&V=413&a1=c2b481deffe0e27bdef1439655260092&a2=2&a4=-&a5=EUR&b1=-&z1=1A-1&z2=-9&z3=2.+At+least+one+of+input+parameters+is+malformed.%3A+Parameter+%5Bg4%5D+cannot+be+empty.&K=c2f6112b40c61859d03684ac8e422766"
+ end
+
+ def successful_credit_response
+ "M=SPREE978&O=6&T=03%2F09%2F2016+03%3A16%3A35&V=413&a1=868f8b942fae639d28e27e8933d575d4&a2=2&a4=100&z1=8a82944a53515706015359604c135301&z13=606944188289&z15=100&z2=0&z3=Transaction+has+been+executed+successfully.&z5=0&z6=00&K=51ba24f6ef3aa161f86e53c34c9616ac"
+ end
+
+ def failed_credit_response
+ "M=SPREE978&O=6&T=03%2F09%2F2016+03%3A16%3A59&V=413&a1=ff28246cfc811b1c686a52d08d075d9c&a2=2&a4=100&z1=8a829449535154bc01535960a962524f&z13=606944188290&z15=100&z2=05&z3=Transaction+has+been+declined.&z5=0&z6=57&K=cf34816d5c25dc007ef3525505c4c610"
+ end
+
+ def empty_purchase_response
+ %(
+ )
+ end
+
+ def transcript
+ %(
+ opening connection to intconsole.credorax.com:443...
+ opened
+ starting SSL for intconsole.credorax.com:443...
+ SSL established
+ <- "POST /intenv/service/gateway HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: intconsole.credorax.com\r\nContent-Length: 264\r\n\r\n"
+ <- "a4=100&a1=335ebb08c489e6d361108a7eb7d8b92a&a5=EUR&c1=Longbob+Longsen&b2=1&b1=5223450000000007&b5=090&b4=25&b3=12&d1=127.0.0.1&c3=unspecified%40example.com&c5=456+My+StreetApt+1&c7=Ottawa&c10=K1C2N6&c2=+555+555-5555&M=SPREE978&O=1&K=ef26476215cee15664e75d979d33935b"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "Date: Wed, 09 Mar 2016 03:03:00 GMT\r\n"
+ -> "Content-Type: application/x-www-form-urlencoded\r\n"
+ -> "Content-Length: 283\r\n"
+ -> "Connection: close\r\n"
+ -> "\r\n"
+ reading 283 bytes...
+ -> "M=SPREE978&O=1&T=03%2F09%2F2016+03%3A03%3A01&V=413&a1=335ebb08c489e6d361108a7eb7d8b92a&a2=2&a4=100&a9=6&z1=8a829449535154bc01535953dd235043&z13=606944188276&z14=U&z15=100&z2=0&z3=Transaction+has+been+executed+successfully.&z4=006592&z5=0&z6=00&z9=X&K=4061e16f39915297827af1586635015a"
+ read 283 bytes
+ Conn close
+ )
+ end
+
+ def scrubbed_transcript
+ %(
+ opening connection to intconsole.credorax.com:443...
+ opened
+ starting SSL for intconsole.credorax.com:443...
+ SSL established
+ <- "POST /intenv/service/gateway HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: intconsole.credorax.com\r\nContent-Length: 264\r\n\r\n"
+ <- "a4=100&a1=335ebb08c489e6d361108a7eb7d8b92a&a5=EUR&c1=Longbob+Longsen&b2=1&b1=[FILTERED]&b5=[FILTERED]&b4=25&b3=12&d1=127.0.0.1&c3=unspecified%40example.com&c5=456+My+StreetApt+1&c7=Ottawa&c10=K1C2N6&c2=+555+555-5555&M=SPREE978&O=1&K=ef26476215cee15664e75d979d33935b"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "Date: Wed, 09 Mar 2016 03:03:00 GMT\r\n"
+ -> "Content-Type: application/x-www-form-urlencoded\r\n"
+ -> "Content-Length: 283\r\n"
+ -> "Connection: close\r\n"
+ -> "\r\n"
+ reading 283 bytes...
+ -> "M=SPREE978&O=1&T=03%2F09%2F2016+03%3A03%3A01&V=413&a1=335ebb08c489e6d361108a7eb7d8b92a&a2=2&a4=100&a9=6&z1=8a829449535154bc01535953dd235043&z13=606944188276&z14=U&z15=100&z2=0&z3=Transaction+has+been+executed+successfully.&z4=006592&z5=0&z6=00&z9=X&K=4061e16f39915297827af1586635015a"
+ read 283 bytes
+ Conn close
+ )
+ end
+end
diff --git a/test/unit/gateways/culqi_test.rb b/test/unit/gateways/culqi_test.rb
new file mode 100644
index 00000000000..be13543b0f4
--- /dev/null
+++ b/test/unit/gateways/culqi_test.rb
@@ -0,0 +1,441 @@
+require 'test_helper'
+
+class CulqiTest < Test::Unit::TestCase
+ def setup
+ @gateway = CulqiGateway.new(merchant_id: 'merchant', terminal_id: 'terminal', secret_key: 'password')
+
+ @amount = 1000
+ @credit_card = credit_card("4111111111111111")
+
+ @options = {
+ order_id: generate_unique_id,
+ billing_address: address
+ }
+ end
+
+ def test_successful_purchase
+ @gateway.expects(:ssl_post).returns(successful_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_match %r{Approved}, response.message
+ assert response.test?
+ end
+
+ def test_failed_purchase
+ @gateway.expects(:ssl_post).returns(failed_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_match %r{Failed}, response.message
+ end
+
+ def test_successful_authorize_and_capture
+ @gateway.expects(:ssl_post).returns(successful_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+ assert_match %r{Approved}, response.message
+ assert_match %r(^\d+$), response.authorization
+
+ @gateway.expects(:ssl_post).returns(successful_capture_response)
+
+ capture = @gateway.capture(@amount, response.authorization)
+ assert_success capture
+ assert_match %r{Transaction has been successfully captured}, capture.message
+ end
+
+ def test_failed_authorize
+ @gateway.expects(:ssl_post).returns(failed_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_failure response
+ assert_match %r{Failed}, response.message
+ end
+
+ def test_failed_capture
+ @gateway.expects(:ssl_post).returns(failed_capture_response)
+
+ response = @gateway.capture(@amount, "0")
+ assert_failure response
+ assert_match %r{Transaction not found}, response.message
+ end
+
+ def test_successful_refund
+ @gateway.expects(:ssl_post).returns(successful_authorize_response)
+ auth = @gateway.authorize(@amount, @credit_card, @options)
+
+ @gateway.expects(:ssl_post).returns(successful_capture_response)
+ capture = @gateway.capture(@amount, auth.authorization)
+
+ @gateway.expects(:ssl_post).returns(successful_refund_response)
+ refund = @gateway.refund(@amount, capture.authorization)
+ assert_success refund
+ assert_match %r{reversed}, refund.message
+ end
+
+ def test_failed_refund
+ @gateway.expects(:ssl_post).returns(failed_refund_response)
+
+ response = @gateway.refund(@amount, "0")
+ assert_failure response
+ assert_match %r{Transaction not found}, response.message
+ end
+
+ def test_successful_void
+ @gateway.expects(:ssl_post).returns(successful_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+
+ @gateway.expects(:ssl_post).returns(successful_void_response)
+
+ void = @gateway.void(response.authorization, @options)
+ assert_success void
+ assert_match %r{cancelled}, void.message
+ end
+
+ def test_failed_void
+ @gateway.expects(:ssl_post).returns(failed_void_response)
+
+ response = @gateway.void("0", @options)
+ assert_failure response
+ assert_match %r{Transaction not found}, response.message
+ end
+
+ def test_successful_verify
+ @gateway.expects(:ssl_post).returns(successful_void_response)
+ @gateway.expects(:ssl_post).returns(successful_authorize_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ assert_match %r{Approved}, response.message
+ end
+
+ def test_successful_verify_with_failed_void
+ @gateway.expects(:ssl_post).returns(failed_void_response)
+ @gateway.expects(:ssl_post).returns(successful_authorize_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ assert_match %r{Approved}, response.message
+ end
+
+ def test_failed_verify
+ @gateway.expects(:ssl_post).returns(failed_authorize_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_failure response
+ assert_match %r{Failed}, response.message
+ end
+
+ def test_successful_store_and_purchase
+ @gateway.expects(:ssl_post).returns(successful_store_response)
+
+ response = @gateway.store(@credit_card, @options.merge(partner_id: fixtures(:culqi)[:partner_id]))
+ assert_success response
+ assert_match %r{Card tokenized successfully}, response.message
+
+ @gateway.expects(:ssl_post).returns(successful_purchase_response)
+
+ purchase = @gateway.purchase(@amount, response.authorization, @options.merge(cvv: @credit_card.verification_value))
+ assert_success purchase
+ end
+
+ def test_failed_store
+ @gateway.expects(:ssl_post).returns(failed_store_response)
+
+ response = @gateway.store(@credit_card, @options.merge(partner_id: fixtures(:culqi)[:partner_id]))
+ assert_failure response
+ assert_match %r{Card already tokenized for same merchant}, response.message
+ end
+
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
+ private
+
+ def pre_scrubbed
+ %q(
+ opening connection to staging.paymentz.com:443...
+ opened
+ starting SSL for staging.paymentz.com:443...
+ SSL established
+ <- "POST /transaction/SingleCallGenericServlet HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded;charset=UTF-8\r\nAccept: application/json\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: staging.paymentz.com\r\nContent-Length: 442\r\n\r\n"
+ <- "toid=10838&totype=Culqi&terminalid=470&amount=10.00&description=f595d9cc63612484a9314f1141e9de75&redirecturl=http%3A%2F%2Fwww.example.com&language=ENG&cardnumber=4000100011112224&cvv=123&expiry_month=09&expiry_year=2017&firstname=Longbob&lastname=Longsen&emailaddr=unspecified%40example.com&street=456+My+Street+Apt+1&city=Ottawa&state=ON&countrycode=CA&zip=K1C2N6&telno=%28555%29555-5555&telnocc=051&checksum=741d5843d64b750ccd749eb8b17be33c"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "Date: Tue, 15 Nov 2016 17:43:01 GMT\r\n"
+ -> "Server: staging\r\n"
+ -> "X-Frame-Options: SAMEORIGIN\r\n"
+ -> "Cache-Control: no-store, no-cache, must-revalidate\r\n"
+ -> "Pragma: no-cache\r\n"
+ -> "Expires: Wed, 31 Dec 1969 23:59:59 GMT\r\n"
+ -> "Content-Type: text/html;charset=UTF-8\r\n"
+ -> "Content-Length: 510\r\n"
+ -> "Set-Cookie: JSESSIONID=98659561D241CA110A65798B34A3150E; Path=/transaction/; HttpOnly;Secure;\r\n"
+ -> "Connection: close\r\n"
+ -> "\r\n"
+ reading 510 bytes...
+ -> ""
+ -> "{\"fraudscore\":\"\",\"statusdescription\":\"Approved---Your Transaction is successful\",\"billingdiscriptor\":\"Test_Transaction\",\"rulestriggered\":\"\",\"orderid\":\"f595d9cc63612484a9314f1141e9de75\",\"authamount\":\"10.00\",\"resultdescription\":\"\",\"cardissuer\":\"\",\"eci\":\"\",\"validationdescription\":\"\",\"banktransid\":\"\",\"cvvresult\":\"\",\"ecidescription\":\"\",\"token\":\"\",\"authcode\":\"\",\"cardsource\":\"\",\"cardcountrycode\":\"\",\"banktransdate\":\"\",\"checksum\":\"7d65440154044eaa7185d5492c2edd3f\",\"resultcode\":\"\",\"trackingid\":\"37859\",\"status\":\"Y\"}"
+ read 510 bytes
+ Conn close
+ )
+ end
+
+ def post_scrubbed
+ %q(
+ opening connection to staging.paymentz.com:443...
+ opened
+ starting SSL for staging.paymentz.com:443...
+ SSL established
+ <- "POST /transaction/SingleCallGenericServlet HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded;charset=UTF-8\r\nAccept: application/json\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: staging.paymentz.com\r\nContent-Length: 442\r\n\r\n"
+ <- "toid=10838&totype=Culqi&terminalid=470&amount=10.00&description=f595d9cc63612484a9314f1141e9de75&redirecturl=http%3A%2F%2Fwww.example.com&language=ENG&cardnumber=[FILTERED]&cvv=[FILTERED]&expiry_month=09&expiry_year=2017&firstname=Longbob&lastname=Longsen&emailaddr=unspecified%40example.com&street=456+My+Street+Apt+1&city=Ottawa&state=ON&countrycode=CA&zip=K1C2N6&telno=%28555%29555-5555&telnocc=051&checksum=741d5843d64b750ccd749eb8b17be33c"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "Date: Tue, 15 Nov 2016 17:43:01 GMT\r\n"
+ -> "Server: staging\r\n"
+ -> "X-Frame-Options: SAMEORIGIN\r\n"
+ -> "Cache-Control: no-store, no-cache, must-revalidate\r\n"
+ -> "Pragma: no-cache\r\n"
+ -> "Expires: Wed, 31 Dec 1969 23:59:59 GMT\r\n"
+ -> "Content-Type: text/html;charset=UTF-8\r\n"
+ -> "Content-Length: 510\r\n"
+ -> "Set-Cookie: JSESSIONID=98659561D241CA110A65798B34A3150E; Path=/transaction/; HttpOnly;Secure;\r\n"
+ -> "Connection: close\r\n"
+ -> "\r\n"
+ reading 510 bytes...
+ -> ""
+ -> "{\"fraudscore\":\"\",\"statusdescription\":\"Approved---Your Transaction is successful\",\"billingdiscriptor\":\"Test_Transaction\",\"rulestriggered\":\"\",\"orderid\":\"f595d9cc63612484a9314f1141e9de75\",\"authamount\":\"10.00\",\"resultdescription\":\"\",\"cardissuer\":\"\",\"eci\":\"\",\"validationdescription\":\"\",\"banktransid\":\"\",\"cvvresult\":\"\",\"ecidescription\":\"\",\"token\":\"\",\"authcode\":\"\",\"cardsource\":\"\",\"cardcountrycode\":\"\",\"banktransdate\":\"\",\"checksum\":\"7d65440154044eaa7185d5492c2edd3f\",\"resultcode\":\"\",\"trackingid\":\"37859\",\"status\":\"Y\"}"
+ read 510 bytes
+ Conn close
+ )
+ end
+
+ def successful_purchase_response
+ %(
+ {
+ "fraudscore": "",
+ "statusdescription": "Approved---Your Transaction is successful",
+ "billingdiscriptor": "Test_Transaction",
+ "rulestriggered": "",
+ "orderid": "3ee62ef1b32401e9e9ae1db3c3bbdd2c",
+ "authamount": "10.00",
+ "resultdescription": "",
+ "cardissuer": "",
+ "eci": "",
+ "validationdescription": "",
+ "banktransid": "",
+ "cvvresult": "",
+ "ecidescription": "",
+ "token": "ccIJiW3R6eiiWIQSDsCOPuA47MZEfWNS",
+ "authcode": "",
+ "cardsource": "",
+ "cardcountrycode": "",
+ "banktransdate": "",
+ "checksum": "3fc8f97d8918cf533006dc89c71e7bd2",
+ "resultcode": "",
+ "trackingid": "39539",
+ "status": "Y"
+ }
+ )
+ end
+
+ def failed_purchase_response
+ %(
+ {
+ "fraudscore": "",
+ "statusdescription": "Failed--(Card expired )",
+ "billingdiscriptor": " ",
+ "rulestriggered": "",
+ "orderid": "3a8dab9a1082008519c96cb4170e170e",
+ "authamount": "10.00",
+ "resultdescription": "",
+ "cardissuer": "",
+ "eci": "",
+ "validationdescription": "",
+ "banktransid": "",
+ "cvvresult": "",
+ "ecidescription": "",
+ "token": "",
+ "authcode": "",
+ "cardsource": "",
+ "cardcountrycode": "",
+ "banktransdate": "",
+ "checksum": "880492e540f4128680f614b9488fe702",
+ "resultcode": "",
+ "trackingid": "39540",
+ "status": "N"
+ }
+ )
+ end
+
+ def successful_authorize_response
+ %(
+ {
+ "fraudscore": "",
+ "statusdescription": "Approved---Your Transaction is successful",
+ "billingdiscriptor": "Test_Transaction",
+ "rulestriggered": "",
+ "orderid": "f2868a3fcd2f656ff2e57758df218d4e",
+ "authamount": "10.00",
+ "resultdescription": "",
+ "cardissuer": "",
+ "eci": "",
+ "validationdescription": "",
+ "banktransid": "",
+ "cvvresult": "",
+ "ecidescription": "",
+ "token": "ccIJiW3R6eiiWIQSDsCOPuA47MZEfWNS",
+ "authcode": "",
+ "cardsource": "",
+ "cardcountrycode": "",
+ "banktransdate": "",
+ "checksum": "2ac8857b8821b1b07f268ec60d6ddb4d",
+ "resultcode": "",
+ "trackingid": "39541",
+ "status": "Y"
+ }
+ )
+ end
+
+ def failed_authorize_response
+ %(
+ {
+ "fraudscore": "",
+ "statusdescription": "Failed--(Card expired )",
+ "billingdiscriptor": " ",
+ "rulestriggered": "",
+ "orderid": "cffc3df7cd594844d8f3659336fc7ed9",
+ "authamount": "10.00",
+ "resultdescription": "",
+ "cardissuer": "",
+ "eci": "",
+ "validationdescription": "",
+ "banktransid": "",
+ "cvvresult": "",
+ "ecidescription": "",
+ "token": "",
+ "authcode": "",
+ "cardsource": "",
+ "cardcountrycode": "",
+ "banktransdate": "",
+ "checksum": "2c62a41893a8a6aed54ba19994c26455",
+ "resultcode": "",
+ "trackingid": "39542",
+ "status": "N"
+ }
+ )
+ end
+
+ def successful_capture_response
+ %(
+ {
+ "amount": "10.00",
+ "statusdescription": "Transaction has been successfully captured",
+ "resultdescription": "",
+ "lote": "",
+ "newchecksum": "e64824e56e6fbf6cfc569241cb613e03",
+ "bankstatus": "",
+ "resultcode": "",
+ "trackingid": "39541",
+ "status": "Y"
+ }
+ )
+ end
+
+ def failed_capture_response
+ %(
+ {
+ "amount": "10.00",
+ "statusdescription": "Transaction not found",
+ "resultdescription": "",
+ "lote": "",
+ "newchecksum": "fa03a809a37f6f3453b2bba25776b280",
+ "bankstatus": "",
+ "resultcode": "",
+ "trackingid": "0",
+ "status": "N"
+ }
+ )
+ end
+
+ def successful_refund_response
+ %(
+ {
+ "statusDescription": "Transaction has been successfully reversed",
+ "checksum": "6c9a9383627f4527e2476939df445af6",
+ "refundamount": "10.00",
+ "trackingid": "39543",
+ "status": "Y"
+ }
+ )
+ end
+
+ def failed_refund_response
+ %(
+ {
+ "statusDescription": "Transaction not found",
+ "checksum": "fa03a809a37f6f3453b2bba25776b280",
+ "refundamount": "10.00",
+ "trackingid": "0",
+ "status": "N"
+ }
+ )
+ end
+
+ def successful_void_response
+ %(
+ {
+ "statusdescription": "Transaction has been successfully cancelled",
+ "orderid": "31adcdb52b8e20197e98b2b0ed91725a",
+ "resultdescription": "",
+ "newchecksum": "e68065b4cb141382aca9ff56fa057f95",
+ "bankstatus": "",
+ "resultcode": "",
+ "status": "Y",
+ "trackingid": "39544"
+ }
+ )
+ end
+
+ def failed_void_response
+ %(
+ {
+ "statusdescription": "10080_Transaction not found from provided tracking ID.",
+ "orderid": "8501c8205a1b133c41270771a8404334",
+ "resultdescription": "",
+ "newchecksum": "f599cfcc42950ae4be90414560a723de",
+ "bankstatus": "",
+ "resultcode": "",
+ "status": "N",
+ "trackingid": "0"
+ }
+ )
+ end
+
+ def successful_store_response
+ %(
+ {
+ "statusdescription": "Card tokenized successfully",
+ "checksum": "232df0e95a26a0efd2321cedbd1b6113",
+ "days": "90",
+ "status": "Y",
+ "token": "3hYBliyBBTL23joK1m1uul7GDT0Mph75"
+ }
+ )
+ end
+
+ def failed_store_response
+ %(
+ {
+ "statusdescription": "Card already tokenized for same merchant",
+ "checksum": "d8ae2ac457a0df3d1970dda78a260d5a",
+ "days": "",
+ "status": "N",
+ "token": ""
+ }
+ )
+ end
+end
diff --git a/test/unit/gateways/cyber_source_test.rb b/test/unit/gateways/cyber_source_test.rb
index 7806b52e9ee..e0a4351e4d2 100644
--- a/test/unit/gateways/cyber_source_test.rb
+++ b/test/unit/gateways/cyber_source_test.rb
@@ -1,4 +1,5 @@
require 'test_helper'
+require 'nokogiri'
class CyberSourceTest < Test::Unit::TestCase
include CommStub
@@ -58,7 +59,7 @@ def test_successful_credit_card_purchase
assert response = @gateway.purchase(@amount, @credit_card, @options)
assert_equal 'Successful transaction', response.message
assert_success response
- assert_equal "#{@options[:order_id]};#{response.params['requestID']};#{response.params['requestToken']};purchase;100;USD", response.authorization
+ assert_equal "#{@options[:order_id]};#{response.params['requestID']};#{response.params['requestToken']};purchase;100;USD;", response.authorization
assert response.test?
end
@@ -94,7 +95,7 @@ def test_successful_check_purchase
assert response = @gateway.purchase(@amount, @check, @options)
assert_equal 'Successful transaction', response.message
assert_success response
- assert_equal "#{@options[:order_id]};#{response.params['requestID']};#{response.params['requestToken']};purchase;100;USD", response.authorization
+ assert_equal "#{@options[:order_id]};#{response.params['requestID']};#{response.params['requestToken']};purchase;100;USD;", response.authorization
assert response.test?
end
@@ -104,7 +105,7 @@ def test_successful_pinless_debit_card_purchase
assert response = @gateway.purchase(@amount, @credit_card, @options.merge(:pinless_debit_card => true))
assert_equal 'Successful transaction', response.message
assert_success response
- assert_equal "#{@options[:order_id]};#{response.params['requestID']};#{response.params['requestToken']};purchase;100;USD", response.authorization
+ assert_equal "#{@options[:order_id]};#{response.params['requestID']};#{response.params['requestToken']};purchase;100;USD;", response.authorization
assert response.test?
end
@@ -175,6 +176,17 @@ def test_unsuccessful_authorization
@gateway.expects(:ssl_post).returns(unsuccessful_authorization_response)
assert response = @gateway.purchase(@amount, @credit_card, @options)
+ refute_equal 'Successful transaction', response.message
+ assert_instance_of Response, response
+ assert_failure response
+ end
+
+ def test_unsuccessful_authorization_with_reply
+ @gateway.expects(:ssl_post).returns(unsuccessful_authorization_response_with_reply)
+
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ refute_equal 'Successful transaction', response.message
+ assert_equal '481', response.params['reasonCode']
assert_instance_of Response, response
assert_failure response
end
@@ -343,11 +355,24 @@ def test_unsuccessful_verify
end
def test_successful_auth_with_network_tokenization_for_visa
- @gateway.expects(:ssl_post).with do |host, request_body|
- assert_match %r'\n 111111111100cryptogram\n vbv\n 111111111100cryptogram\n\n\n 1\n', request_body
- true
- end.returns(successful_purchase_response)
+ credit_card = network_tokenization_credit_card('4111111111111111',
+ :brand => 'visa',
+ :transaction_id => "123",
+ :eci => "05",
+ :payment_cryptogram => "111111111100cryptogram"
+ )
+ response = stub_comms do
+ @gateway.authorize(@amount, credit_card, @options)
+ end.check_request do |_endpoint, body, _headers|
+ assert_xml_valid_to_xsd(body)
+ assert_match %r'\n 111111111100cryptogram\n vbv\n 111111111100cryptogram\n\n\n 1\n', body
+ end.respond_with(successful_purchase_response)
+
+ assert_success response
+ end
+
+ def test_successful_purchase_with_network_tokenization_for_visa
credit_card = network_tokenization_credit_card('4111111111111111',
:brand => 'visa',
:transaction_id => "123",
@@ -355,12 +380,19 @@ def test_successful_auth_with_network_tokenization_for_visa
:payment_cryptogram => "111111111100cryptogram"
)
- assert response = @gateway.authorize(@amount, credit_card, @options)
+ response = stub_comms do
+ @gateway.purchase(@amount, credit_card, @options)
+ end.check_request do |_endpoint, body, _headers|
+ assert_xml_valid_to_xsd(body)
+ assert_match %r'.+?'m, body
+ end.respond_with(successful_purchase_response)
+
assert_success response
end
def test_successful_auth_with_network_tokenization_for_mastercard
@gateway.expects(:ssl_post).with do |host, request_body|
+ assert_xml_valid_to_xsd(request_body)
assert_match %r'\n 111111111100cryptogram\n 2\n\n\n spa\n\n\n 1\n', request_body
true
end.returns(successful_purchase_response)
@@ -378,6 +410,7 @@ def test_successful_auth_with_network_tokenization_for_mastercard
def test_successful_auth_with_network_tokenization_for_amex
@gateway.expects(:ssl_post).with do |host, request_body|
+ assert_xml_valid_to_xsd(request_body)
assert_match %r'\n MTExMTExMTExMTAwY3J5cHRvZ3I=\n\n aesk\n YW0=\n\n\n\n 1\n', request_body
true
end.returns(successful_purchase_response)
@@ -404,6 +437,39 @@ def test_nonfractional_currency_handling
assert_success response
end
+ def test_malformed_xml_handling
+ @gateway.expects(:ssl_post).returns(malformed_xml_response)
+
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_match %r(Missing end tag for), response.message
+ assert response.test?
+ end
+
+ def test_3ds_enroll_response
+ purchase = stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options.merge(payer_auth_enroll_service: true))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/\/, data)
+ end.respond_with(threedeesecure_purchase_response)
+
+ assert_failure purchase
+ assert_equal "YTJycDdLR3RIVnpmMXNFejJyazA=", purchase.params["xid"]
+ assert_equal "eNpVUe9PwjAQ/d6/ghA/r2tBYMvRBEUFFEKQEP1Yu1Om7gfdJoy/3nZsgk2a3Lveu757B+utRhw/oyo0CphjlskPbIXBsC25TvuPD/lkc3xn2d2R6y+3LWA5WuFOwA/qLExiwRzX4UAbSEwLrbYyzgVItbuZLkS353HWA1pDAhHq6Vgw3ule9/pAT5BALCMUqnwznZJCKwRaZQiopIhzXYpB1wXaAAKF/hbbPE8zn9L9fu9cUB2VREBtAQF6FrQsbJSZOQ9hIF7Xs1KNg6dVZzXdxGk0f1nc4+eslMfREKitIBDIHAV3WZ+Z2+Ku3/F8bjRXeQIysmrEFeOOa0yoIYHUfjQ6Icbt02XGTFRojbFqRmoQATykSYymxlD+YjPDWfntxBqrcusg8wbmWGcrXNFD4w3z2IkfVkZRy6H13mi9YhP9W/0vhyyqPw==", purchase.params["paReq"]
+ assert_equal "https://0eafstag.cardinalcommerce.com/EAFService/jsp/v1/redirect", purchase.params["acsURL"]
+ end
+
+ def test_3ds_validate_response
+ validation = stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options.merge(payer_auth_validate_service: true, pares: 'ABC123'))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/\/, data)
+ assert_match(/\ABC123\<\/signedPARes\>/, data)
+ end.respond_with(successful_threedeesecure_validate_response)
+
+ assert_success validation
+ end
+
def test_scrub
assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
end
@@ -490,6 +556,64 @@ def unsuccessful_authorization_response
XML
end
+ def unsuccessful_authorization_response_with_reply
+ <<-XML
+
+
+
+
+
+ 2017-05-10T01:15:14.835Z
+
+
+
+
+ TEST11111111111
+ 1841784762620176127166
+ REJECT
+ 481
+ AMYJY9fl62i+vx2OEQYAx9zv/9UBZAAA5h5D
+
+ USD
+
+
+ 100
+ 1186.43
+ 123456
+ N
+ N
+ M
+ M
+ 2017-05-10T01:15:14Z
+ 00
+ 013445773WW7EWMB0RYI9
+
+
+ 100
+ 96
+ 1
+ 20:15:14
+ C^H
+ MM-IPBST
+ MUL-EM
+ VEL-ADDR^VEL-CC^VEL-NAME
+ us
+ nvlas vegas
+ fixed
+ default
+ 540510
+ US
+ PURCHASING
+ MASTERCARD CREDIT
+ werewrewrew.
+
+ 3
+
+
+
+ XML
+ end
+
def successful_tax_response
<<-XML
@@ -583,4 +707,37 @@ def successful_nonfractional_authorization_response
2007-07-12T18:31:53.838ZTEST111111111111842651133440156177166ACCEPT100AP4JY+Or4xRonEAOERAyMzQzOTEzMEM0MFZaNUZCBgDH3fgJ8AEGAMfd+AnwAwzRpAAA7RT/JPY1001004542AI72007-07-12T18:31:53Z10023439130C40VZ2FB
XML
end
+
+ def malformed_xml_response
+ <<-XML
+
+
+2008-01-15T21:42:03.343Zb0a6cf9aa07f1a8495f89c364bbd6a9a2004333231260008401927ACCEPT100Afvvj7Ke2Fmsbq0wHFE2sM6R4GAptYZ0jwPSA+R9PhkyhFTb0KRjoE4+ynthZrG6tMBwjAtTUSD1001.00123456YYMM2008-01-15T21:42:03Z00U
+ XML
+ end
+
+ def threedeesecure_purchase_response
+ <<-XML
+
+
+2017-10-17T20:39:27.392Z1a5ba4804da54b384c6e8a2d8057ea995082727663166909004012REJECT475AhjzbwSTE4kEGDR65zjsGwFLjtwzsJ0gXLJx6Xb0ky3SA7ek8AYA/A17475https://0eafstag.cardinalcommerce.com/EAFService/jsp/v1/redirecteNpVUe9PwjAQ/d6/ghA/r2tBYMvRBEUFFEKQEP1Yu1Om7gfdJoy/3nZsgk2a3Lveu757B+utRhw/oyo0CphjlskPbIXBsC25TvuPD/lkc3xn2d2R6y+3LWA5WuFOwA/qLExiwRzX4UAbSEwLrbYyzgVItbuZLkS353HWA1pDAhHq6Vgw3ule9/pAT5BALCMUqnwznZJCKwRaZQiopIhzXYpB1wXaAAKF/hbbPE8zn9L9fu9cUB2VREBtAQF6FrQsbJSZOQ9hIF7Xs1KNg6dVZzXdxGk0f1nc4+eslMfREKitIBDIHAV3WZ+Z2+Ku3/F8bjRXeQIysmrEFeOOa0yoIYHUfjQ6Icbt02XGTFRojbFqRmoQATykSYymxlD+YjPDWfntxBqrcusg8wbmWGcrXNFD4w3z2IkfVkZRy6H13mi9YhP9W/0vhyyqPw==1198888YTJycDdLR3RIVnpmMXNFejJyazA=<AuthProof><Time>2017 Oct 17 20:39:27</Time><DSUrl>https://csrtestcustomer34.cardinalcommerce.com/merchantacsfrontend/vereq.jsp?acqid=CYBS</DSUrl><VEReqProof><Message id="a2rp7KGtHVzf1sEz2rk0"><VEReq><version>1.0.2</version><pan>XXXXXXXXXXXX0002</pan><Merchant><acqBIN>469216</acqBIN><merID>1234567</merID></Merchant><Browser><deviceCategory>0</deviceCategory></Browser></VEReq></Message></VEReqProof><VEResProof><Message id="a2rp7KGtHVzf1sEz2rk0"><VERes><version>1.0.2</version><CH><enrolled>Y</enrolled><acctID>1198888</acctID></CH><url>https://testcustomer34.cardinalcommerce.com/merchantacsfrontend/pareq.jsp?vaa=b&gold=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</url><protocol>ThreeDSecure</protocol></VERes></Message></VEResProof></AuthProof>YENROLLED
+ XML
+ end
+
+ def successful_threedeesecure_validate_response
+ <<-XML
+
+
+2018-05-01T14:28:36.773Z23751b5aeb076ea5940c5b656284bf6a5251849164756591904009ACCEPT100Ahj//wSTHLQMXdtQnQUJGxDds0bNnDRoo0+VcdXMBUafKuOrnpAuWT9zDJpJlukB29J4YBpMctAxd21CdBQkwQ3gUSD10012.02831000YY2018-05-01T14:28:36Z00ZLIU5GM27GBP0110322000000E10000200000000000000120205011428360272225A4C495535474D32374742503833313030303030000159004400103232415050524F56414C00223134573031363135303730333830323039344730363400065649435241201002018-05-01T14:28:36Z12.02764668441000SuccessAAABAWFlmQAAAABjRWWZEEFgFz+=2vbv0505S2R4eGtHbEZqbnozeGhBRHJ6QzA=Y
+ XML
+ end
+
+ def assert_xml_valid_to_xsd(data, root_element = '//s:Body/*')
+ schema_file = File.open("#{File.dirname(__FILE__)}/../../schema/cyber_source/CyberSourceTransaction_#{CyberSourceGateway::XSD_VERSION}.xsd")
+ doc = Nokogiri::XML(data)
+ root = Nokogiri::XML(doc.xpath(root_element).to_s)
+ xsd = Nokogiri::XML::Schema(schema_file)
+ errors = xsd.validate(root)
+ assert_empty errors, "XSD validation errors in the following XML:\n#{root}"
+ end
end
diff --git a/test/unit/gateways/data_cash_test.rb b/test/unit/gateways/data_cash_test.rb
index 066b567ed0c..ad933feb72a 100644
--- a/test/unit/gateways/data_cash_test.rb
+++ b/test/unit/gateways/data_cash_test.rb
@@ -188,4 +188,69 @@ def successful_purchase_using_continuous_authority_response
XML
end
+
+ def test_transcript_scrubbing
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrub), post_scrub
+ end
+
+ def pre_scrub
+ <<-RAW
+opening connection to testserver.datacash.com:443...
+opened
+starting SSL for testserver.datacash.com:443...
+SSL established
+<- "POST /Transaction HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: testserver.datacash.com\r\nContent-Length: 1067\r\n\r\n"
+<- "\n\n \n 99626800\n 9YM3DjUa6\n \n \n \n auth\n \n 4539792100000003\n 03/20\n \n 444\n \n \n \n \n \n \n \n \n \n d36e05ce3604313963854fca858d11\n 1.98\n ecomm\n \n \n\n"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Wed, 03 Jan 2018 21:24:38 GMT\r\n"
+-> "Server: Apache\r\n"
+-> "Connection: close\r\n"
+-> "Transfer-Encoding: chunked\r\n"
+-> "Content-Type: text/plain; charset=iso-8859-1\r\n"
+-> "\r\n"
+-> "559\r\n"
+reading 1369 bytes...
+-> ""
+-> "\n\n \n \n \n notchecked\n \n matched\n ACCEPTED\n \n notchecked\n \n 698899\n VISA Debit\n United Kingdom\n Barclays Bank PLC\n 00\n Approved or completed successfully\n D4B1B0558173CAE56E87293F9E9E899C8002F7B6\n \n RBS\n 4700204504678897\n d36e05ce3604313963854fca858d11\n 99626800\n TEST\n ACCEPTED\n 1\n \n\n\n"
+read 1369 bytes
+reading 2 bytes...
+-> ""
+-> "\r\n"
+read 2 bytes
+-> "0\r\n"
+-> "\r\n"
+Conn close
+ RAW
+ end
+
+ def post_scrub
+ <<-SCRUBBED
+opening connection to testserver.datacash.com:443...
+opened
+starting SSL for testserver.datacash.com:443...
+SSL established
+<- "POST /Transaction HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: testserver.datacash.com\r\nContent-Length: 1067\r\n\r\n"
+<- "\n\n \n 99626800\n [FILTERED]\n \n \n \n auth\n \n [FILTERED]\n 03/20\n \n [FILTERED]\n \n \n \n \n \n \n \n \n \n d36e05ce3604313963854fca858d11\n 1.98\n ecomm\n \n \n\n"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Wed, 03 Jan 2018 21:24:38 GMT\r\n"
+-> "Server: Apache\r\n"
+-> "Connection: close\r\n"
+-> "Transfer-Encoding: chunked\r\n"
+-> "Content-Type: text/plain; charset=iso-8859-1\r\n"
+-> "\r\n"
+-> "559\r\n"
+reading 1369 bytes...
+-> ""
+-> "\n\n \n \n \n notchecked\n \n matched\n ACCEPTED\n \n notchecked\n \n 698899\n VISA Debit\n United Kingdom\n Barclays Bank PLC\n 00\n Approved or completed successfully\n D4B1B0558173CAE56E87293F9E9E899C8002F7B6\n \n RBS\n 4700204504678897\n d36e05ce3604313963854fca858d11\n 99626800\n TEST\n ACCEPTED\n 1\n \n\n\n"
+read 1369 bytes
+reading 2 bytes...
+-> ""
+-> "\r\n"
+read 2 bytes
+-> "0\r\n"
+-> "\r\n"
+Conn close
+SCRUBBED
+ end
end
diff --git a/test/unit/gateways/digitzs_test.rb b/test/unit/gateways/digitzs_test.rb
new file mode 100644
index 00000000000..2218d909dae
--- /dev/null
+++ b/test/unit/gateways/digitzs_test.rb
@@ -0,0 +1,270 @@
+require 'test_helper'
+
+class DigitzsTest < Test::Unit::TestCase
+ include CommStub
+
+ def setup
+ @gateway = DigitzsGateway.new(api_key: 'api_key', app_key: 'app_key')
+ @credit_card = credit_card
+ @amount = 100
+
+ @options = {
+ merchant_id: 'spreedly-susanswidg-32268973-2091076-148408385',
+ order_id: '1',
+ billing_address: address,
+ description: 'Store Purchase'
+ }
+
+ @options_with_split = {
+ merchant_id: 'spreedly-susanswidg-32268973-2091076-148408385',
+ billing_address: address,
+ description: 'Split Purchase',
+ payment_type: 'card_split',
+ split_amount: 100,
+ split_merchant_id: 'spreedly-susanswidg-32270590-2095203-148657924'
+ }
+ end
+
+ def test_successful_purchase
+ @gateway.expects(:ssl_post).times(2).returns(successful_app_token_response, successful_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal 'spreedly-susanswidg-32268973-2091076-148408385-124-148606421', response.authorization
+ assert response.test?
+ end
+
+ def test_successful_card_split_purchase
+ response = stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options_with_split)
+ end.check_request do |endpoint, data, headers|
+ if data =~ /"cardSplit"/
+ assert_match(%r(split), data)
+ assert_match(%r("merchantId":"spreedly-susanswidg-32270590-2095203-148657924"), data)
+ end
+ end.respond_with(successful_app_token_response, successful_purchase_response)
+ assert_success response
+
+ assert_equal 'spreedly-susanswidg-32268973-2091076-148408385-124-148606421', response.authorization
+ end
+
+ def test_successful_token_split_purchase
+ response = stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options_with_split)
+ end.check_request do |endpoint, data, headers|
+ if data =~ /"tokenSplit"/
+ assert_match(%r(split), data)
+ assert_match(%r("merchantId":"spreedly-susanswidg-32270590-2095203-148657924"), data)
+ end
+ end.respond_with(successful_app_token_response, successful_purchase_response)
+ assert_success response
+
+ assert_equal 'spreedly-susanswidg-32268973-2091076-148408385-124-148606421', response.authorization
+ end
+
+ def test_failed_purchase
+ @gateway.expects(:ssl_post).times(2).returns(successful_app_token_response, failed_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal "58", response.error_code
+ end
+
+ def test_successful_refund
+ @gateway.expects(:ssl_post).times(2).returns(successful_app_token_response, successful_refund_response)
+
+ response = @gateway.refund(@amount, "authorization", @options)
+ assert_success response
+
+ assert_equal 'spreedly-susanswidg-32268973-2091076-148408385-127-148606617', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_refund
+ @gateway.expects(:ssl_post).times(2).returns(successful_app_token_response, failed_refund_response)
+
+ response = @gateway.refund(@amount, "", @options)
+ assert_failure response
+
+ assert_equal nil, response.authorization
+ assert response.test?
+ end
+
+ def test_successful_store
+ @gateway.expects(:ssl_post).times(3).returns(successful_app_token_response, successful_create_customer_response, successful_token_response)
+
+ response = @gateway.store(@credit_card, @options)
+ assert_success response
+ assert_equal "spreedly-susanswidg-32268973-2091076-148408385-2894006614343495-148710226|c0302d83-a694-4bec-9086-d1886b9eefd9-148710226", response.authorization
+ end
+
+ def test_successful_store_creates_new_customer
+ @gateway.expects(:ssl_get).returns(customer_id_exists_response)
+ @gateway.expects(:ssl_post).times(3).returns(successful_app_token_response, successful_create_customer_response, successful_token_response)
+
+ assert response = @gateway.store(@credit_card, @options.merge({customer_id: "pre_existing_customer_id"}))
+ assert_success response
+ assert_equal "spreedly-susanswidg-32268973-2091076-148408385-2894006614343495-148710226|c0302d83-a694-4bec-9086-d1886b9eefd9-148710226", response.authorization
+ end
+
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
+ private
+
+ def pre_scrubbed
+ %q(
+opening connection to beta.digitzsapi.com:443...
+opened
+starting SSL for beta.digitzsapi.com:443...
+SSL established
+<- "POST /sandbox/auth/token HTTP/1.1\r\nContent-Type: application/json\r\nX-Api-Key: 0HhRdOU2AsWVEu3gRIKi2UpMMmj8Fj48qggBYTo4\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: beta.digitzsapi.com\r\nContent-Length: 115\r\n\r\n"
+<- "{\"data\":{\"attributes\":{\"appKey\":\"tcwtTux8SPZYO44Gf0UHZH74Z1HSutqCxmIV2PFj2jRc9Poroh3Z3R1BBQNRQ98Q\"},\"type\":\"auth\"}}"
+-> "HTTP/1.1 201 Created\r\n"
+-> "Content-Type: application/json\r\n"
+-> "Content-Length: 434\r\n"
+-> "Connection: close\r\n"
+-> "Date: Fri, 27 Jan 2017 20:47:32 GMT\r\n"
+-> "Content-Location: https://beta.digitzsapi.com/sandbox/auth/token\r\n"
+-> "x-amzn-RequestId: d3637ff0-e4d1-11e6-a393-3dbd03385fb7\r\n"
+-> "X-Amzn-Trace-Id: Root=1-588bb1e4-49acd61c62e319bc67e443d8\r\n"
+-> "Via: 1.1 344c0192a2becdfa5c3c6b927653ff8b.cloudfront.net (CloudFront), 1.1 986a2cb4ab6fb48c9a4379a4e9d691c4.cloudfront.net (CloudFront)\r\n"
+-> "X-Cache: Miss from cloudfront\r\n"
+-> "X-Amz-Cf-Id: NfmaknL15LfaGNXlXtc2mhwFwpzNHMbNExCfsMxORdRF7t3bbc77vA==\r\n"
+-> "\r\n"
+reading 434 bytes...
+-> "{\"links\":{\"self\":\"https://beta.digitzsapi.com/sandbox/auth/token\"},\"data\":{\"type\":\"auth\",\"id\":\"0HhRdOU2AsWVEu3gRIKi2UpMMmj8Fj48qggBYTo4\",\"attributes\":{\"appToken\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXJ0bmVySWQiOiJzcHJlZWRseS0xNDgyMzAxOTEiLCJwYXJ0bmVyUHJlZml4Ijoic3ByZWVkbHkiLCJwcm9wYXlUaWVyIjoiU2V0TGlzdGVyIiwicHJvcGF5TWNjIjoiNTk5OSIsImlhdCI6MTQ4NTU1MDA1MiwiZXhwIjoxNDg1NTUzNjUyfQ.P2gunlNF56IKbAKpnRci7vLgUK0Yd7K1PGPzTtYP3Nc\"}}}"
+read 434 bytes
+Conn close
+opening connection to beta.digitzsapi.com:443...
+opened
+starting SSL for beta.digitzsapi.com:443...
+SSL established
+<- "POST /sandbox/payments HTTP/1.1\r\nContent-Type: application/json\r\nX-Api-Key: 0HhRdOU2AsWVEu3gRIKi2UpMMmj8Fj48qggBYTo4\r\nAuthorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXJ0bmVySWQiOiJzcHJlZWRseS0xNDgyMzAxOTEiLCJwYXJ0bmVyUHJlZml4Ijoic3ByZWVkbHkiLCJwcm9wYXlUaWVyIjoiU2V0TGlzdGVyIiwicHJvcGF5TWNjIjoiNTk5OSIsImlhdCI6MTQ4NTU1MDA1MiwiZXhwIjoxNDg1NTUzNjUyfQ.P2gunlNF56IKbAKpnRci7vLgUK0Yd7K1PGPzTtYP3Nc\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: beta.digitzsapi.com\r\nContent-Length: 430\r\n\r\n"
+<- "{\"data\":{\"attributes\":{\"paymentType\":\"card\",\"merchantId\":\"spreedly-susanswidg-32268973-2091076-148408385\",\"card\":{\"holder\":\"Longbob Longsen\",\"number\":\"4747474747474747\",\"expiry\":\"0918\",\"code\":\"999\"},\"transaction\":{\"amount\":\"200\",\"currency\":\"USD\",\"invoice\":\"91bbccdd926ab8effc53bc7be094bd2b\"},\"billingAddress\":{\"line1\":\"456 My Street\",\"line2\":\"Apt 1\",\"city\":\"Ottawa\",\"state\":\"ON\",\"zip\":\"K1C2N6\",\"country\":\"US\"}},\"type\":\"payments\"}}"
+-> "HTTP/1.1 201 Created\r\n"
+-> "Content-Type: application/json\r\n"
+-> "Content-Length: 504\r\n"
+-> "Connection: close\r\n"
+-> "Date: Fri, 27 Jan 2017 20:47:35 GMT\r\n"
+-> "Content-Location: https://beta.digitzsapi.com/sandbox/payments/spreedly-susanswidg-32268973-2091076-148408385-88-148555005\r\n"
+-> "x-amzn-RequestId: d3dcf5b4-e4d1-11e6-9d9a-59db6e3f8bc6\r\n"
+-> "X-Amzn-Trace-Id: Root=1-588bb1e5-5c6481e9f44a8bd604900914\r\n"
+-> "Via: 1.1 b06057d522f80c65400aebb1c06a2d72.cloudfront.net (CloudFront), 1.1 e6cb8f0dccd39d6bf4fcef2d892671bf.cloudfront.net (CloudFront)\r\n"
+-> "X-Cache: Miss from cloudfront\r\n"
+-> "X-Amz-Cf-Id: Q62cc8eH9XbSUl9No6Mp_xPS10ld0GQ8XN_S5uT4RdxkvUUA97a2kg==\r\n"
+-> "\r\n"
+reading 504 bytes...
+-> "{\"links\":{\"self\":\"https://beta.digitzsapi.com/sandbox/payments/spreedly-susanswidg-32268973-2091076-148408385-88-148555005\"},\"data\":{\"type\":\"payments\",\"id\":\"spreedly-susanswidg-32268973-2091076-148408385-88-148555005\",\"attributes\":{\"paymentType\":\"card\",\"transaction\":{\"code\":\"0\",\"message\":\"Success\",\"amount\":\"200\",\"invoice\":\"91bbccdd926ab8effc53bc7be094bd2b\",\"currency\":\"USD\",\"authCode\":\"A11111\",\"avsResult\":\"T\",\"codeResult\":\"M\",\"gross\":\"200\",\"net\":\"169\",\"grossMinusNet\":\"31\",\"fee\":\"25\",\"rate\":\"2.90\"}}}}"
+read 504 bytes
+Conn close
+ )
+ end
+
+ def post_scrubbed
+ %q(
+opening connection to beta.digitzsapi.com:443...
+opened
+starting SSL for beta.digitzsapi.com:443...
+SSL established
+<- "POST /sandbox/auth/token HTTP/1.1\r\nContent-Type: application/json\r\nX-Api-Key: [FILTERED]\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: beta.digitzsapi.com\r\nContent-Length: 115\r\n\r\n"
+<- "{\"data\":{\"attributes\":{\"appKey\":\"[FILTERED]
+-> "HTTP/1.1 201 Created\r\n"
+-> "Content-Type: application/json\r\n"
+-> "Content-Length: 434\r\n"
+-> "Connection: close\r\n"
+-> "Date: Fri, 27 Jan 2017 20:47:32 GMT\r\n"
+-> "Content-Location: https://beta.digitzsapi.com/sandbox/auth/token\r\n"
+-> "x-amzn-RequestId: d3637ff0-e4d1-11e6-a393-3dbd03385fb7\r\n"
+-> "X-Amzn-Trace-Id: Root=1-588bb1e4-49acd61c62e319bc67e443d8\r\n"
+-> "Via: 1.1 344c0192a2becdfa5c3c6b927653ff8b.cloudfront.net (CloudFront), 1.1 986a2cb4ab6fb48c9a4379a4e9d691c4.cloudfront.net (CloudFront)\r\n"
+-> "X-Cache: Miss from cloudfront\r\n"
+-> "X-Amz-Cf-Id: NfmaknL15LfaGNXlXtc2mhwFwpzNHMbNExCfsMxORdRF7t3bbc77vA==\r\n"
+-> "\r\n"
+reading 434 bytes...
+-> "{\"links\":{\"self\":\"https://beta.digitzsapi.com/sandbox/auth/token\"},\"data\":{\"type\":\"auth\",\"id\":\"[FILTERED]
+read 434 bytes
+Conn close
+opening connection to beta.digitzsapi.com:443...
+opened
+starting SSL for beta.digitzsapi.com:443...
+SSL established
+<- "POST /sandbox/payments HTTP/1.1\r\nContent-Type: application/json\r\nX-Api-Key: [FILTERED]\r\nAuthorization: Bearer [FILTERED]
+<- "{\"data\":{\"attributes\":{\"paymentType\":\"card\",\"merchantId\":\"spreedly-susanswidg-32268973-2091076-148408385\",\"card\":{\"holder\":\"Longbob Longsen\",\"number\":\"[FILTERED]\",\"expiry\":\"0918\",\"code\":\"[FILTERED]\"},\"transaction\":{\"amount\":\"200\",\"currency\":\"USD\",\"invoice\":\"91bbccdd926ab8effc53bc7be094bd2b\"},\"billingAddress\":{\"line1\":\"456 My Street\",\"line2\":\"Apt 1\",\"city\":\"Ottawa\",\"state\":\"ON\",\"zip\":\"K1C2N6\",\"country\":\"US\"}},\"type\":\"payments\"}}"
+-> "HTTP/1.1 201 Created\r\n"
+-> "Content-Type: application/json\r\n"
+-> "Content-Length: 504\r\n"
+-> "Connection: close\r\n"
+-> "Date: Fri, 27 Jan 2017 20:47:35 GMT\r\n"
+-> "Content-Location: https://beta.digitzsapi.com/sandbox/payments/spreedly-susanswidg-32268973-2091076-148408385-88-148555005\r\n"
+-> "x-amzn-RequestId: d3dcf5b4-e4d1-11e6-9d9a-59db6e3f8bc6\r\n"
+-> "X-Amzn-Trace-Id: Root=1-588bb1e5-5c6481e9f44a8bd604900914\r\n"
+-> "Via: 1.1 b06057d522f80c65400aebb1c06a2d72.cloudfront.net (CloudFront), 1.1 e6cb8f0dccd39d6bf4fcef2d892671bf.cloudfront.net (CloudFront)\r\n"
+-> "X-Cache: Miss from cloudfront\r\n"
+-> "X-Amz-Cf-Id: Q62cc8eH9XbSUl9No6Mp_xPS10ld0GQ8XN_S5uT4RdxkvUUA97a2kg==\r\n"
+-> "\r\n"
+reading 504 bytes...
+-> "{\"links\":{\"self\":\"https://beta.digitzsapi.com/sandbox/payments/spreedly-susanswidg-32268973-2091076-148408385-88-148555005\"},\"data\":{\"type\":\"payments\",\"id\":\"[FILTERED]
+read 504 bytes
+Conn close
+ )
+ end
+
+ def successful_app_token_response
+ %(
+ {\"links\":{\"self\":\"https://beta.digitzsapi.com/sandbox/auth/token\"},\"data\":{\"type\":\"auth\",\"id\":\"0HhRdOU2AsWVEu3gRIKi2UpMMmj8Fj48qggBYTo4\",\"attributes\":{\"appToken\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXJ0bmVySWQiOiJzcHJlZWRseS0xNDgyMzAxOTEiLCJwYXJ0bmVyUHJlZml4Ijoic3ByZWVkbHkiLCJwcm9wYXlUaWVyIjoiU2V0TGlzdGVyIiwicHJvcGF5TWNjIjoiNTk5OSIsImlhdCI6MTQ4NjA2NDIxNiwiZXhwIjoxNDg2MDY3ODE2fQ.MaLR3ijeMuIGSHnNYINVILwa9hpxahd4U4Q44HW4jFQ\"}}}
+ )
+ end
+
+ def successful_purchase_response
+ %(
+ {\"links\":{\"self\":\"https://beta.digitzsapi.com/sandbox/payments/spreedly-susanswidg-32268973-2091076-148408385-124-148606421\"},\"data\":{\"type\":\"payments\",\"id\":\"spreedly-susanswidg-32268973-2091076-148408385-124-148606421\",\"attributes\":{\"paymentType\":\"card\",\"transaction\":{\"code\":\"0\",\"message\":\"Success\",\"amount\":\"200\",\"invoice\":\"42d7b1d026becf29e2005bae84e8935b\",\"currency\":\"USD\",\"authCode\":\"A11111\",\"avsResult\":\"T\",\"codeResult\":\"M\",\"gross\":\"200\",\"net\":\"169\",\"grossMinusNet\":\"31\",\"fee\":\"25\",\"rate\":\"2.90\"}}}}
+ )
+ end
+
+ def successful_split_purchase_response
+ %(
+ {\"links\":{\"self\":\"https://beta.digitzsapi.com/sandbox/payments/spreedly-susanswidg-32268973-2091076-148408385-153-148658575\"},\"data\":{\"type\":\"payments\",\"id\":\"spreedly-susanswidg-32268973-2091076-148408385-153-148658575\",\"attributes\":{\"paymentType\":\"cardSplit\",\"transaction\":{\"code\":\"0\",\"message\":\"Success\",\"amount\":\"500\",\"invoice\":\"88ec8adf6c86762684ae54820423acc8\",\"currency\":\"USD\",\"authCode\":\"A11111\",\"avsResult\":\"T\",\"codeResult\":\"M\"},\"split\":{\"merchantId\":\"spreedly-susanswidg-32270590-2095203-148657924\",\"amount\":\"100\",\"splitId\":\"spreedly-susanswidg-32270590-2095203-148657924-2-148658575\"}}}}
+ )
+ end
+
+
+ def failed_purchase_response
+ %(
+ {\"meta\":{},\"errors\":[{\"status\":\"400\",\"source\":{\"pointer\":\"/payments\"},\"title\":\"Bad Request\",\"detail\":\"Partner error: Credit card declined (transaction element shows reason for decline)\",\"code\":\"58\",\"meta\":{\"debug\":{\"message\":\"Include debug info with support request.\",\"resource\":\"/payments POST\",\"log\":\"2017/02/02/[23]eb325f3ca78b4f7eb2178a0d1e635a0e\",\"request\":\"73c22dc3-e980-11e6-9390-69c24d5ed1f4\"},\"transaction\":{\"code\":\"51\",\"message\":\"Insufficient funds\",\"invoice\":\"3d1f247d9112349e3db252f9f3327047\",\"authCode\":\"A11111\",\"avsResult\":\"T\"}}}]}
+ )
+ end
+
+ def successful_refund_response
+ %(
+ {\"links\":{\"self\":\"https://beta.digitzsapi.com/sandbox/payments/spreedly-susanswidg-32268973-2091076-148408385-127-148606617\"},\"data\":{\"type\":\"payments\",\"id\":\"spreedly-susanswidg-32268973-2091076-148408385-127-148606617\",\"attributes\":{\"paymentType\":\"cardRefund\",\"transaction\":{\"code\":\"0\",\"message\":\"Success\",\"amount\":\"200\",\"invoice\":\"f87139e53b5273c12bc32d4be6fff9a8\",\"currency\":\"USD\"}}}}
+ )
+ end
+
+ def failed_refund_response
+ %(
+ {\"meta\":{},\"errors\":[{\"status\":\"400\",\"source\":{\"pointer\":\"/data/attributes/originalTransaction/id\"},\"title\":\"Bad Request\",\"detail\":\"\\\"id\\\" is not allowed to be empty\"}]}
+ )
+ end
+
+ def successful_create_customer_response
+ %(
+ {\"links\":{\"self\":\"https://beta.digitzsapi.com/sandbox/customers/spreedly-susanswidg-32268973-2091076-148408385-2894006614343495-148710226\"},\"data\":{\"type\":\"customers\",\"id\":\"spreedly-susanswidg-32268973-2091076-148408385-2894006614343495-148710226\",\"attributes\":{\"name\":\"Longbob Longsen\",\"externalId\":\"2b942bae49e9297f60428ee841f30724\"}}}
+ )
+ end
+
+ def successful_token_response
+ %(
+ {\"links\":{\"self\":\"https://beta.digitzsapi.com/sandbox/tokens/c0302d83-a694-4bec-9086-d1886b9eefd9-148710226\"},\"data\":{\"type\":\"tokens\",\"id\":\"c0302d83-a694-4bec-9086-d1886b9eefd9-148710226\",\"attributes\":{\"label\":\"Credit Card\",\"customerId\":\"spreedly-susanswidg-32268973-2091076-148408385-2894006614343495-148710226\"}}}
+ )
+ end
+
+ def customer_id_exists_response
+ %(
+ {\"links\":{\"self\":\"https://beta.digitzsapi.com/sandbox/customers/spreedly-susanswidg-32268973-2091076-148408385-5980208887457495-148700575\"},\"data\":{\"id\":\"spreedly-susanswidg-32268973-2091076-148408385-5980208887457495-148700575\",\"attributes\":{\"merchantId\":\"spreedly-susanswidg-32268973-2091076-148408385\",\"created\":\"2017-02-13T17:09:12.724Z\",\"name\":\"Jon Doe\",\"externalId\":\"123456\"},\"type\":\"customers\"}}
+ )
+ end
+end
diff --git a/test/unit/gateways/ebanx_test.rb b/test/unit/gateways/ebanx_test.rb
new file mode 100644
index 00000000000..7504e9c8138
--- /dev/null
+++ b/test/unit/gateways/ebanx_test.rb
@@ -0,0 +1,247 @@
+require 'test_helper'
+
+class EbanxTest < Test::Unit::TestCase
+ def setup
+ @gateway = EbanxGateway.new(integration_key: 'key')
+ @credit_card = credit_card
+ @amount = 100
+
+ @options = {
+ order_id: '1',
+ billing_address: address,
+ description: 'Store Purchase'
+ }
+ end
+
+ def test_successful_purchase
+ @gateway.expects(:ssl_request).returns(successful_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal '592db57ad6933455efbb62a48d1dfa091dd7cd092109db99', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_purchase
+ @gateway.expects(:ssl_request).returns(failed_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal "NOK", response.error_code
+ end
+
+ def test_successful_authorize
+ @gateway.expects(:ssl_request).returns(successful_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal '592dc02dbe421478a132bf5c2ecfe52c86ac01b454ae799b', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_authorize
+ @gateway.expects(:ssl_request).returns(failed_authorize_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal "NOK", response.error_code
+ end
+
+ def test_successful_capture
+ @gateway.expects(:ssl_request).returns(successful_capture_response)
+
+ response = @gateway.capture(@amount, "authorization", @options)
+ assert_success response
+
+ assert_equal 'Sandbox - Test credit card, transaction captured', response.message
+ assert response.test?
+ end
+
+ def test_failed_capture
+ @gateway.expects(:ssl_request).returns(failed_capture_response)
+
+ response = @gateway.capture(@amount, "", @options)
+ assert_failure response
+ assert_equal "BP-CAP-1", response.error_code
+ end
+
+ def test_successful_refund
+ @gateway.expects(:ssl_request).returns(successful_refund_response)
+
+ response = @gateway.refund(@amount, "authorization", @options)
+ assert_success response
+
+ assert_equal '59306246f2a0c5f327a15dd6492687e197aca7eda179da08', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_refund
+ @gateway.expects(:ssl_request).returns(failed_refund_response)
+
+ response = @gateway.refund(@amount, "", @options)
+ assert_failure response
+ assert_equal "BP-REF-CAN-2", response.error_code
+ end
+
+ def test_successful_void
+ @gateway.expects(:ssl_request).returns(successful_void_response)
+
+ response = @gateway.void("authorization", @options)
+ assert_success response
+
+ assert_equal '5930629dde0899dc53b3557ea9887aa8f3d264a91d115d40', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_void
+ @gateway.expects(:ssl_request).returns(failed_void_response)
+
+ response = @gateway.void("", @options)
+ assert_failure response
+ assert_equal "BP-CAN-1", response.error_code
+ end
+
+ def test_successful_verify
+ @gateway.expects(:ssl_request).times(2).returns(successful_authorize_response, successful_void_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ assert_equal nil, response.error_code
+ end
+
+ def test_successful_verify_with_failed_void
+ @gateway.expects(:ssl_request).times(2).returns(successful_authorize_response, failed_void_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ assert_equal nil, response.error_code
+ end
+
+ def test_failed_verify
+ @gateway.expects(:ssl_request).returns(failed_authorize_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_failure response
+ assert_equal "NOK", response.error_code
+ end
+
+ def test_successful_store_and_purchase
+ @gateway.expects(:ssl_request).returns(successful_store_response)
+
+ store = @gateway.store(@credit_card, @options)
+ assert_success store
+ assert_equal 'a61a7c98535718801395991b5112f888d359c2d632e2c3bb8afe75aa23f3334d7fd8dc57d7721f8162503773063de59ee85901b5714a92338c6d9c0352aee78c|visa', store.authorization
+
+ @gateway.expects(:ssl_request).returns(successful_purchase_with_stored_card_response)
+
+ response = @gateway.purchase(@amount, store.authorization, @options)
+ assert_success response
+ end
+
+ def test_error_response_with_invalid_creds
+ @gateway.expects(:ssl_request).returns(invalid_cred_response)
+
+ response = @gateway.store(@credit_card, @options)
+ assert_failure response
+ assert_equal "Invalid integration key", response.message
+ end
+
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
+ private
+
+ def pre_scrubbed
+ %q(
+ request_body={\"integration_key\":\"1231000\",\"operation\":\"request\",\"payment\":{\"amount_total\":\"1.00\",\"currency_code\":\"USD\",\"merchant_payment_code\":\"2bed75b060e936834e354d944aeaa892\",\"name\":\"Longbob Longsen\",\"email\":\"unspecified@example.com\",\"document\":\"853.513.468-93\",\"payment_type_code\":\"visa\",\"creditcard\":{\"card_number\":\"4111111111111111\",\"card_name\":\"Longbob Longsen\",\"card_due_date\":\"9/2018\",\"card_cvv\":\"123\"},\"address\":\"Rua E\",\"street_number\":\"1040\",\"city\":\"Maracana\u{fa}\",\"state\":\"CE\",\"zipcode\":\"61919-230\",\"country\":\"BR\",\"phone_number\":\"(555)555-5555\"}}
+ )
+ end
+
+ def post_scrubbed
+ %q(
+ request_body={\"integration_key\":\"[FILTERED]\",\"operation\":\"request\",\"payment\":{\"amount_total\":\"1.00\",\"currency_code\":\"USD\",\"merchant_payment_code\":\"2bed75b060e936834e354d944aeaa892\",\"name\":\"Longbob Longsen\",\"email\":\"unspecified@example.com\",\"document\":\"853.513.468-93\",\"payment_type_code\":\"visa\",\"creditcard\":{\"card_number\":\"[FILTERED]\",\"card_name\":\"Longbob Longsen\",\"card_due_date\":\"9/2018\",\"card_cvv\":\"[FILTERED]\"},\"address\":\"Rua E\",\"street_number\":\"1040\",\"city\":\"Maracana\u{fa}\",\"state\":\"CE\",\"zipcode\":\"61919-230\",\"country\":\"BR\",\"phone_number\":\"(555)555-5555\"}}
+ )
+ end
+
+ def successful_purchase_response
+ %(
+ {"payment":{"hash":"592db57ad6933455efbb62a48d1dfa091dd7cd092109db99","pin":"081043552","merchant_payment_code":"ca2251ed6ac582162b17d77dfd7fb98a","order_number":null,"status":"CO","status_date":"2017-05-30 15:10:01","open_date":"2017-05-30 15:10:01","confirm_date":"2017-05-30 15:10:01","transfer_date":null,"amount_br":"3.31","amount_ext":"1.00","amount_iof":"0.01","currency_rate":"3.3000","currency_ext":"USD","due_date":"2017-06-02","instalments":"1","payment_type_code":"visa","transaction_status":{"acquirer":"EBANX","code":"OK","description":"Sandbox - Test credit card, transaction captured"},"pre_approved":true,"capture_available":false,"customer":{"document":"85351346893","email":"unspecified@example.com","name":"LONGBOB LONGSEN","birth_date":null}},"status":"SUCCESS"}
+ )
+ end
+
+ def failed_purchase_response
+ %(
+ {"payment":{"hash":"592dd2f17965cd3d7a17e71a3fe943b8363c72d60caffacc","pin":"655998606","merchant_payment_code":"e71e467805aef9064599bc5a76e98e23","order_number":null,"status":"CA","status_date":"2017-05-30 17:15:45","open_date":"2017-05-30 17:15:45","confirm_date":null,"transfer_date":null,"amount_br":"3.31","amount_ext":"1.00","amount_iof":"0.01","currency_rate":"3.3000","currency_ext":"USD","due_date":"2017-06-02","instalments":"1","payment_type_code":"visa","transaction_status":{"acquirer":"EBANX","code":"NOK","description":"Sandbox - Test credit card, transaction declined reason insufficientFunds"},"pre_approved":false,"capture_available":false,"customer":{"document":"85351346893","email":"unspecified@example.com","name":"LONGBOB LONGSEN","birth_date":null}},"status":"SUCCESS"}
+ )
+ end
+
+ def successful_authorize_response
+ %(
+ {"payment":{"hash":"592dc02dbe421478a132bf5c2ecfe52c86ac01b454ae799b","pin":"296389224","merchant_payment_code":"8e5c943c3c93adbed8d8a7347ca333fe","order_number":null,"status":"PE","status_date":null,"open_date":"2017-05-30 15:55:40","confirm_date":null,"transfer_date":null,"amount_br":"3.31","amount_ext":"1.00","amount_iof":"0.01","currency_rate":"3.3000","currency_ext":"USD","due_date":"2017-06-02","instalments":"1","payment_type_code":"visa","transaction_status":{"acquirer":"EBANX","code":"OK","description":"Sandbox - Test credit card, transaction will be approved"},"pre_approved":true,"capture_available":true,"customer":{"document":"85351346893","email":"unspecified@example.com","name":"LONGBOB LONGSEN","birth_date":null}},"status":"SUCCESS"}
+ )
+ end
+
+ def failed_authorize_response
+ %(
+ {"payment":{"hash":"592dd2146d5b8a27924daaa0f0248d8c582cb2ce6b67495e","pin":"467618452","merchant_payment_code":"7883bdbbdfa961ce753247fbeb4ff99d","order_number":null,"status":"CA","status_date":"2017-05-30 17:12:03","open_date":"2017-05-30 17:12:03","confirm_date":null,"transfer_date":null,"amount_br":"3.31","amount_ext":"1.00","amount_iof":"0.01","currency_rate":"3.3000","currency_ext":"USD","due_date":"2017-06-02","instalments":"1","payment_type_code":"visa","transaction_status":{"acquirer":"EBANX","code":"NOK","description":"Sandbox - Test credit card, transaction declined reason insufficientFunds"},"pre_approved":false,"capture_available":false,"customer":{"document":"85351346893","email":"unspecified@example.com","name":"LONGBOB LONGSEN","birth_date":null}},"status":"SUCCESS"}
+ )
+ end
+
+ def successful_capture_response
+ %(
+ {"payment":{"hash":"592dd65824427e4f5f50564c118f399869637bfb30d54f5b","pin":"081043654","merchant_payment_code":"8424e3000d64d056fbd58639957dc1c4","order_number":null,"status":"CO","status_date":"2017-05-30 17:30:16","open_date":"2017-05-30 17:30:15","confirm_date":"2017-05-30 17:30:16","transfer_date":null,"amount_br":"3.31","amount_ext":"1.00","amount_iof":"0.01","currency_rate":"3.3000","currency_ext":"USD","due_date":"2017-06-02","instalments":"1","payment_type_code":"visa","transaction_status":{"acquirer":"EBANX","code":"OK","description":"Sandbox - Test credit card, transaction captured"},"pre_approved":true,"capture_available":false,"customer":{"document":"85351346893","email":"unspecified@example.com","name":"LONGBOB LONGSEN","birth_date":null}},"status":"SUCCESS"}
+ )
+ end
+
+ def failed_capture_response
+ %(
+ {"status":"ERROR","status_code":"BP-CAP-1","status_message":"Parameters hash or merchant_payment_code not informed"}
+ )
+ end
+
+ def successful_refund_response
+ %(
+ {"payment":{"hash":"59306246f2a0c5f327a15dd6492687e197aca7eda179da08","pin":"446189033","merchant_payment_code":"b5e1f7298f8fa645e8a903fbdc5ce44a","order_number":null,"status":"CO","status_date":"2017-06-01 15:51:49","open_date":"2017-06-01 15:51:49","confirm_date":"2017-06-01 15:51:49","transfer_date":null,"amount_br":"3.31","amount_ext":"1.00","amount_iof":"0.01","currency_rate":"3.3000","currency_ext":"USD","due_date":"2017-06-04","instalments":"1","payment_type_code":"visa","transaction_status":{"acquirer":"EBANX","code":"OK","description":"Sandbox - Test credit card, transaction captured"},"pre_approved":true,"capture_available":false,"refunds":[{"id":"20739","merchant_refund_code":null,"status":"RE","request_date":"2017-06-01 15:51:50","pending_date":null,"confirm_date":null,"cancel_date":null,"amount_ext":"1.00","description":"full refund"}],"customer":{"document":"85351346893","email":"unspecified@example.com","name":"LONGBOB LONGSEN","birth_date":null}},"refund":{"id":"20739","merchant_refund_code":null,"status":"RE","request_date":"2017-06-01 15:51:50","pending_date":null,"confirm_date":null,"cancel_date":null,"amount_ext":"1.00","description":"full refund"},"operation":"refund","status":"SUCCESS"}
+ )
+ end
+
+ def failed_refund_response
+ %(
+ {"status":"ERROR","status_code":"BP-REF-CAN-2","status_message":"Payment not found with this hash: "}
+ )
+ end
+
+ def successful_void_response
+ %(
+ {"payment":{"hash":"5930629dde0899dc53b3557ea9887aa8f3d264a91d115d40","pin":"465556618","merchant_payment_code":"8b97c49aecffbb309dadd08c87ccbdd0","order_number":null,"status":"CA","status_date":"2017-06-01 15:53:18","open_date":"2017-06-01 15:53:17","confirm_date":null,"transfer_date":null,"amount_br":"3.31","amount_ext":"1.00","amount_iof":"0.01","currency_rate":"3.3000","currency_ext":"USD","due_date":"2017-06-04","instalments":"1","payment_type_code":"visa","transaction_status":{"acquirer":"EBANX","code":"NOK","description":"Sandbox - Test credit card, transaction cancelled"},"pre_approved":false,"capture_available":false,"customer":{"document":"85351346893","email":"unspecified@example.com","name":"LONGBOB LONGSEN","birth_date":null}},"operation":"cancel","status":"SUCCESS"}
+ )
+ end
+
+ def failed_void_response
+ %(
+ {"status":"ERROR","status_code":"BP-CAN-1","status_message":"Parameter hash not informed"}
+ )
+ end
+
+ def successful_store_response
+ %(
+ {"status":"SUCCESS","payment_type_code":"visa","token":"a61a7c98535718801395991b5112f888d359c2d632e2c3bb8afe75aa23f3334d7fd8dc57d7721f8162503773063de59ee85901b5714a92338c6d9c0352aee78c","masked_card_number":"411111xxxxxx1111"}
+ )
+ end
+
+ def successful_purchase_with_stored_card_response
+ %(
+ {"payment":{"hash":"59d3e2955021c5e2b180e1ea9670e2d9675c15453a2ab346","pin":"252076123","merchant_payment_code":"a942f8a68836e888fa8e8af1e8ca4bf2","order_number":null,"status":"CO","status_date":"2017-10-03 19:18:45","open_date":"2017-10-03 19:18:44","confirm_date":"2017-10-03 19:18:45","transfer_date":null,"amount_br":"3.31","amount_ext":"1.00","amount_iof":"0.01","currency_rate":"3.3000","currency_ext":"USD","due_date":"2017-10-06","instalments":"1","payment_type_code":"visa","details":{"billing_descriptor":""},"transaction_status":{"acquirer":"EBANX","code":"OK","description":"Accepted"},"pre_approved":true,"capture_available":false,"customer":{"document":"85351346893","email":"unspecified@example.com","name":"NOT PROVIDED","birth_date":null}},"status":"SUCCESS"}
+ )
+ end
+
+ def invalid_cred_response
+ %(
+ {"status":"ERROR","status_code":"DA-1","status_message":"Invalid integration key"}
+ )
+ end
+end
diff --git a/test/unit/gateways/elavon_test.rb b/test/unit/gateways/elavon_test.rb
index 26f384dbdf7..48667573b57 100644
--- a/test/unit/gateways/elavon_test.rb
+++ b/test/unit/gateways/elavon_test.rb
@@ -252,7 +252,7 @@ def test_stripping_non_word_characters_from_zip
@options[:billing_address][:zip] = bad_zip
- @gateway.expects(:commit).with(anything, anything, has_entries(:avs_zip => stripped_zip))
+ @gateway.expects(:commit).with(anything, anything, has_entries(:avs_zip => stripped_zip), anything)
@gateway.purchase(@amount, @credit_card, @options)
end
@@ -260,12 +260,28 @@ def test_stripping_non_word_characters_from_zip
def test_zip_codes_with_letters_are_left_intact
@options[:billing_address][:zip] = '.K1%Z_5E3-'
- @gateway.expects(:commit).with(anything, anything, has_entries(:avs_zip => 'K1Z5E3'))
+ @gateway.expects(:commit).with(anything, anything, has_entries(:avs_zip => 'K1Z5E3'), anything)
@gateway.purchase(@amount, @credit_card, @options)
end
+ def test_custom_fields_in_request
+ stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options.merge(:customer_number => '123', :custom_fields => {:a_key => "a value"}))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/customer_number=123/, data)
+ assert_match(/a_key/, data)
+ refute_match(/ssl_a_key/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
+ def test_transcript_scrubbing
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrub), post_scrub
+ end
+
private
+
def successful_purchase_response
"ssl_card_number=42********4242
ssl_exp_date=0910
@@ -383,7 +399,7 @@ def failed_authorization_response
errorName=Credit Card Number Invalid
errorMessage=The Credit Card Number supplied in the authorization request appears to be invalid."
end
-
+
def successful_capture_response
"ssl_card_number=42********4242
ssl_exp_date=0910
@@ -451,4 +467,78 @@ def failed_update_response
errorName=Credit Card Number Invalid
errorMessage=The Credit Card Number supplied in the authorization request appears to be invalid."
end
+
+ def pre_scrub
+ %q{
+opening connection to api.demo.convergepay.com:443...
+opened
+starting SSL for api.demo.convergepay.com:443...
+SSL established
+<- "POST /VirtualMerchantDemo/process.do HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api.demo.convergepay.com\r\nContent-Length: 616\r\n\r\n"
+<- "ssl_merchant_id=000127&ssl_pin=IERAOBEE5V0D6Q3Q6R51TG89XAIVGEQ3LGLKMKCKCVQBGGGAU7FN627GPA54P5HR&ssl_show_form=false&ssl_result_format=ASCII&ssl_user_id=ssltest&ssl_invoice_number=&ssl_description=Test+Transaction&ssl_card_number=4124939999999990&ssl_exp_date=0919&ssl_cvv2cvc2=123&ssl_cvv2cvc2_indicator=1&ssl_first_name=Longbob&ssl_last_name=Longsen&ssl_avs_address=456+My+Street&ssl_address2=Apt+1&ssl_avs_zip=K1C2N6&ssl_city=Ottawa&ssl_state=ON&ssl_company=Widgets+Inc&ssl_phone=%28555%29555-5555&ssl_country=CA&ssl_email=paul%40domain.com&ssl_cardholder_ip=203.0.113.0&ssl_amount=1.00&ssl_transaction_type=CCSALE"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Wed, 03 Jan 2018 21:40:26 GMT\r\n"
+-> "Pragma: no-cache\r\n"
+-> "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\n"
+-> "Expires: 0\r\n"
+-> "Content-Disposition: inline; filename=response.txt\r\n"
+-> "AuthApproved: true\r\n"
+-> "AuthResponse: AA\r\n"
+-> "Set-Cookie: JSESSIONID=00007wKfJV3-JFME8QiC_RCDjuI:14j4qkv92; HTTPOnly; Path=/; Secure\r\n"
+-> "Set-Cookie: JSESSIONID=0000uW6woWZ84eAJunhFLfJz8hS:14j4qkv92; HTTPOnly; Path=/; Secure\r\n"
+-> "Connection: close\r\n"
+-> "Content-Type: text/plain\r\n"
+-> "Content-Language: en-US\r\n"
+-> "Content-Encoding: gzip\r\n"
+-> "Transfer-Encoding: chunked\r\n"
+-> "\r\n"
+-> "1A5 \r\n"
+reading 421 bytes...
+-> "\x1F\x8B\b\x00\x00\x00\x00\x00\x00\x03MR\xEFk\xDB0\x10\xFD\xDE\xBF\xC2\x1F\xB7\x81[\xC9\xB1\x1D\xBB \x98\x7FtP\xD66!+\xDBGs\xB1o\x99\xC0\x96\x84%{q\xFF\xFA\xC9R\x12f\x10\xDC\xBDw\xBEw\xEF8\xAD\xFB\xA6\x85\xB1k\xC44\x1Cqd1\xFDr\xFB\xF2<'w\xDA\x16\xE0Y5\x1D\x18d$\xA7\xB9C`\x90\x930\x8C\xDE\x13_\xA1\xA1Gm\xE0\xCC\\\xC6\xC5,y\x8B\xD7\x9E\x0E\x130\xA0\x8FV9\x1Fu\xA8`4\xD3\x88\xBE\xFB\xDD;WM\xE1;\xFBJ9\xA8\x1E\r\x97\xE2Rp\x05A,\xEC\x17\xEFNht\xF0,Z\x87\xFF\xE6\xA36^\xE6E\x8A\xD3Q\x1E\x1D\xDC\xC3\xFF\xA8F\xE1P\x98u\x03]7\xA2\xD6,N\xD2\xE0u\t~\x98\x11\xD1x\xD63\x11+\x94\t\xA8W\xE5fa;c\xE0/\xB8\xDC\x9A\xB5\x03\xED\xDEn\xDD>\xB8b\xDFi\x15\xBD\xA5\xBE~u1.\xAC*\\\xAA\xFEH\x81\xECS\x92$\x9F\xED\v\xEDK\x1C\x8E\x03\xF0\x9E)\x98\xFA\xAF\x9D\xB4\xB1\xB8\xB7\xF6\x1Cc\a\x98z\xC3\xFCz}\xD2\fv(8!+\xF6\xFB\xC3\xEEg\xF1\xE28s\x16\r\xEF\x18\xD9\x10J\xB3\x82&!\xA9\xD3:O\xF3*|\x8A\xEA2\x8C\xB34\t\xB3o\xDB$,\xD3\xA2,\xB3tC\xB7E\xE9\xFE\x04\xA5F9\xC3:l\x87,\xDEnI\x1C9\xA2\x9D\xE7h\xD5TR\xE8\xCB\xD6W\x8B7\xE4\xE2\xBAu&\x9B#\xF4 Z{\x1C\xD7cX'2\xDCn\x9C\xD0\a\xB2y\x88\b\xCD\x02\x12?\xC6\xE41\xDA\x06\xFBW/\xB1\xDE\x9CY\x14\xB2\xEA\xF0T?\xBFW\xC5\xA1\xFE\aC\x85\x1DS\x8C\x02\x00\x00"
+read 421 bytes
+reading 2 bytes...
+-> "\r\n"
+read 2 bytes
+-> "0\r\n"
+-> "\r\n"
+Conn close
+}}
+ end
+
+ def post_scrub
+ %q{
+opening connection to api.demo.convergepay.com:443...
+opened
+starting SSL for api.demo.convergepay.com:443...
+SSL established
+<- "POST /VirtualMerchantDemo/process.do HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api.demo.convergepay.com\r\nContent-Length: 616\r\n\r\n"
+<- "ssl_merchant_id=000127&ssl_pin=[FILTERED]&ssl_show_form=false&ssl_result_format=ASCII&ssl_user_id=ssltest&ssl_invoice_number=&ssl_description=Test+Transaction&ssl_card_number=[FILTERED]&ssl_exp_date=0919&ssl_cvv2cvc2=[FILTERED]&ssl_cvv2cvc2_indicator=1&ssl_first_name=Longbob&ssl_last_name=Longsen&ssl_avs_address=456+My+Street&ssl_address2=Apt+1&ssl_avs_zip=K1C2N6&ssl_city=Ottawa&ssl_state=ON&ssl_company=Widgets+Inc&ssl_phone=%28555%29555-5555&ssl_country=CA&ssl_email=paul%40domain.com&ssl_cardholder_ip=203.0.113.0&ssl_amount=1.00&ssl_transaction_type=CCSALE"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Wed, 03 Jan 2018 21:40:26 GMT\r\n"
+-> "Pragma: no-cache\r\n"
+-> "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\n"
+-> "Expires: 0\r\n"
+-> "Content-Disposition: inline; filename=response.txt\r\n"
+-> "AuthApproved: true\r\n"
+-> "AuthResponse: AA\r\n"
+-> "Set-Cookie: JSESSIONID=00007wKfJV3-JFME8QiC_RCDjuI:14j4qkv92; HTTPOnly; Path=/; Secure\r\n"
+-> "Set-Cookie: JSESSIONID=0000uW6woWZ84eAJunhFLfJz8hS:14j4qkv92; HTTPOnly; Path=/; Secure\r\n"
+-> "Connection: close\r\n"
+-> "Content-Type: text/plain\r\n"
+-> "Content-Language: en-US\r\n"
+-> "Content-Encoding: gzip\r\n"
+-> "Transfer-Encoding: chunked\r\n"
+-> "\r\n"
+-> "1A5 \r\n"
+reading 421 bytes...
+-> "\x1F\x8B\b\x00\x00\x00\x00\x00\x00\x03MR\xEFk\xDB0\x10\xFD\xDE\xBF\xC2\x1F\xB7\x81[\xC9\xB1\x1D\xBB \x98\x7FtP\xD66!+\xDBGs\xB1o\x99\xC0\x96\x84%{q\xFF\xFA\xC9R\x12f\x10\xDC\xBDw\xBEw\xEF8\xAD\xFB\xA6\x85\xB1k\xC44\x1Cqd1\xFDr\xFB\xF2<'w\xDA\x16\xE0Y5\x1D\x18d$\xA7\xB9C`\x90\x930\x8C\xDE\x13_\xA1\xA1Gm\xE0\xCC\\\xC6\xC5,y\x8B\xD7\x9E\x0E\x130\xA0\x8FV9\x1Fu\xA8`4\xD3\x88\xBE\xFB\xDD;WM\xE1;\xFBJ9\xA8\x1E\r\x97\xE2Rp\x05A,\xEC\x17\xEFNht\xF0,Z\x87\xFF\xE6\xA36^\xE6E\x8A\xD3Q\x1E\x1D\xDC\xC3\xFF\xA8F\xE1P\x98u\x03]7\xA2\xD6,N\xD2\xE0u\t~\x98\x11\xD1x\xD63\x11+\x94\t\xA8W\xE5fa;c\xE0/\xB8\xDC\x9A\xB5\x03\xED\xDEn\xDD>\xB8b\xDFi\x15\xBD\xA5\xBE~u1.\xAC*\\\xAA\xFEH\x81\xECS\x92$\x9F\xED\v\xEDK\x1C\x8E\x03\xF0\x9E)\x98\xFA\xAF\x9D\xB4\xB1\xB8\xB7\xF6\x1Cc\a\x98z\xC3\xFCz}\xD2\fv(8!+\xF6\xFB\xC3\xEEg\xF1\xE28s\x16\r\xEF\x18\xD9\x10J\xB3\x82&!\xA9\xD3:O\xF3*|\x8A\xEA2\x8C\xB34\t\xB3o\xDB$,\xD3\xA2,\xB3tC\xB7E\xE9\xFE\x04\xA5F9\xC3:l\x87,\xDEnI\x1C9\xA2\x9D\xE7h\xD5TR\xE8\xCB\xD6W\x8B7\xE4\xE2\xBAu&\x9B#\xF4 Z{\x1C\xD7cX'2\xDCn\x9C\xD0\a\xB2y\x88\b\xCD\x02\x12?\xC6\xE41\xDA\x06\xFBW/\xB1\xDE\x9CY\x14\xB2\xEA\xF0T?\xBFW\xC5\xA1\xFE\aC\x85\x1DS\x8C\x02\x00\x00"
+read 421 bytes
+reading 2 bytes...
+-> "\r\n"
+read 2 bytes
+-> "0\r\n"
+-> "\r\n"
+Conn close
+}}
+ end
end
diff --git a/test/unit/gateways/fat_zebra_test.rb b/test/unit/gateways/fat_zebra_test.rb
index fc004e0a947..3c62fe2edb9 100644
--- a/test/unit/gateways/fat_zebra_test.rb
+++ b/test/unit/gateways/fat_zebra_test.rb
@@ -84,7 +84,6 @@ def test_successful_purchase_with_descriptor
assert_equal '001-P-12345AA', response.authorization
assert response.test?
-
end
def test_successful_authorization
@@ -175,8 +174,68 @@ def test_unsuccessful_refund
assert response.test?
end
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
private
+ def pre_scrubbed
+ <<-'PRE_SCRUBBED'
+opening connection to gateway.sandbox.fatzebra.com.au:443...
+opened
+starting SSL for gateway.sandbox.fatzebra.com.au:443...
+SSL established
+<- "POST /v1.0/credit_cards HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAuthorization: Basic VEVTVDpURVNU\r\nUser-Agent: Fat Zebra v1.0/ActiveMerchant 1.56.0\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nConnection: close\r\nHost: gateway.sandbox.fatzebra.com.au\r\nContent-Length: 93\r\n\r\n"
+<- "{\"card_number\":\"5123456789012346\",\"card_expiry\":\"5/2017\",\"cvv\":\"111\",\"card_holder\":\"Foo Bar\"}"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Content-Type: application/json; charset=utf-8\r\n"
+-> "Connection: close\r\n"
+-> "Status: 200 OK\r\n"
+-> "Cache-control: no-store\r\n"
+-> "Pragma: no-cache\r\n"
+-> "X-Request-Id: 3BA78272_F214_AC10001D_01BB_566A58EC_222F1D_49F4\r\n"
+-> "X-Runtime: 0.142463\r\n"
+-> "Date: Fri, 11 Dec 2015 05:02:36 GMT\r\n"
+-> "X-Rack-Cache: invalidate, pass\r\n"
+-> "X-Sandbox: true\r\n"
+-> "X-Backend-Server: app-3\r\n"
+-> "\r\n"
+reading all...
+-> "{\"successful\":true,\"response\":{\"token\":\"nkk9rhwu\",\"card_holder\":\"Foo Bar\",\"card_number\":\"512345XXXXXX2346\",\"card_expiry\":\"2017-05-31T23:59:59+10:00\",\"authorized\":true,\"transaction_count\":0},\"errors\":[],\"test\":true}"
+read 214 bytes
+Conn close
+ PRE_SCRUBBED
+ end
+
+ def post_scrubbed
+ <<-'POST_SCRUBBED'
+opening connection to gateway.sandbox.fatzebra.com.au:443...
+opened
+starting SSL for gateway.sandbox.fatzebra.com.au:443...
+SSL established
+<- "POST /v1.0/credit_cards HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAuthorization: Basic [FILTERED]\r\nUser-Agent: Fat Zebra v1.0/ActiveMerchant 1.56.0\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nConnection: close\r\nHost: gateway.sandbox.fatzebra.com.au\r\nContent-Length: 93\r\n\r\n"
+<- "{\"card_number\":\"[FILTERED]\",\"card_expiry\":\"5/2017\",\"cvv\":\"[FILTERED]\",\"card_holder\":\"Foo Bar\"}"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Content-Type: application/json; charset=utf-8\r\n"
+-> "Connection: close\r\n"
+-> "Status: 200 OK\r\n"
+-> "Cache-control: no-store\r\n"
+-> "Pragma: no-cache\r\n"
+-> "X-Request-Id: 3BA78272_F214_AC10001D_01BB_566A58EC_222F1D_49F4\r\n"
+-> "X-Runtime: 0.142463\r\n"
+-> "Date: Fri, 11 Dec 2015 05:02:36 GMT\r\n"
+-> "X-Rack-Cache: invalidate, pass\r\n"
+-> "X-Sandbox: true\r\n"
+-> "X-Backend-Server: app-3\r\n"
+-> "\r\n"
+reading all...
+-> "{\"successful\":true,\"response\":{\"token\":\"nkk9rhwu\",\"card_holder\":\"Foo Bar\",\"card_number\":\"[FILTERED]\",\"card_expiry\":\"2017-05-31T23:59:59+10:00\",\"authorized\":true,\"transaction_count\":0},\"errors\":[],\"test\":true}"
+read 214 bytes
+Conn close
+ POST_SCRUBBED
+ end
# Place raw successful response from gateway here
def successful_purchase_response
{
diff --git a/test/unit/gateways/firstdata_e4_test.rb b/test/unit/gateways/firstdata_e4_test.rb
index 9b625043449..284e2e952a8 100755
--- a/test/unit/gateways/firstdata_e4_test.rb
+++ b/test/unit/gateways/firstdata_e4_test.rb
@@ -1,4 +1,5 @@
require 'test_helper'
+require 'nokogiri'
require 'yaml'
class FirstdataE4Test < Test::Unit::TestCase
@@ -181,57 +182,101 @@ def test_customer_ref_is_sent
end.respond_with(successful_purchase_response)
end
- def test_network_tokenization_requests_with_visa
+ def test_eci_default_value
stub_comms do
- credit_card = network_tokenization_credit_card('4111111111111111',
- :brand => 'visa',
- :transaction_id => "123",
- :eci => "05",
- :payment_cryptogram => "111111111100cryptogram"
- )
+ @gateway.purchase(@amount, @credit_card, @options)
+ end.check_request do |endpoint, data, headers|
+ assert_match "07", data
+ end.respond_with(successful_purchase_response)
+ end
- @gateway.purchase(@amount, credit_card, @options)
+ def test_eci_numeric_padding
+ @credit_card = network_tokenization_credit_card
+ @credit_card.eci = "5"
+
+ stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options)
end.check_request do |endpoint, data, headers|
assert_match "05", data
- assert_match "123", data
- assert_match "111111111100cryptogram", data
end.respond_with(successful_purchase_response)
- end
- def test_network_tokenization_requests_with_mastercard
+ @credit_card = network_tokenization_credit_card
+ @credit_card.eci = 5
+
stub_comms do
- credit_card = network_tokenization_credit_card('5555555555554444',
- :brand => 'mastercard',
- :transaction_id => "123",
- :eci => "05",
- :payment_cryptogram => "111111111100cryptogram"
- )
+ @gateway.purchase(@amount, @credit_card, @options)
+ end.check_request do |endpoint, data, headers|
+ assert_match "05", data
+ end.respond_with(successful_purchase_response)
+ end
- @gateway.purchase(@amount, credit_card, @options)
+ def test_eci_option_value
+ stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options.merge(eci: "05"))
end.check_request do |endpoint, data, headers|
assert_match "05", data
- assert_match "123", data
- assert_match "111111111100cryptogram", data
end.respond_with(successful_purchase_response)
end
def test_network_tokenization_requests_with_amex
stub_comms do
- credit_card = network_tokenization_credit_card('378282246310005',
- :brand => 'american_express',
- :transaction_id => "123",
- :eci => "05",
- :payment_cryptogram => Base64.encode64("111111111100cryptogram")
+ credit_card = network_tokenization_credit_card(
+ "378282246310005",
+ brand: "american_express",
+ transaction_id: "123",
+ eci: "05",
+ payment_cryptogram: "whatever_the_cryptogram_of_at_least_20_characters_is",
)
@gateway.purchase(@amount, credit_card, @options)
- end.check_request do |endpoint, data, headers|
+ end.check_request do |_, data, _|
assert_match "05", data
- assert_match "YW0=\n", data
- assert_match "MTExMTExMTExMTAwY3J5cHRvZ3I=\n", data
+ assert_match "mrLdtHIWq2nLXq7IrA==\n", data
+ assert_match "whateverthecryptogramofatlc=\n", data
+ assert_xml_valid_to_wsdl(data)
+ end.respond_with(successful_purchase_response)
+ end
+
+ def test_network_tokenization_requests_with_discover
+ stub_comms do
+ credit_card = network_tokenization_credit_card(
+ "6011111111111117",
+ brand: "discover",
+ transaction_id: "123",
+ eci: "05",
+ payment_cryptogram: "whatever_the_cryptogram_is",
+ )
+
+ @gateway.purchase(@amount, credit_card, @options)
+ end.check_request do |_, data, _|
+ assert_match "04", data
+ assert_match "123", data
+ assert_match "whatever_the_cryptogram_is", data
+ assert_xml_valid_to_wsdl(data)
end.respond_with(successful_purchase_response)
end
+ def test_network_tokenization_requests_with_other_brands
+ %w(visa mastercard other).each do |brand|
+ stub_comms do
+ credit_card = network_tokenization_credit_card(
+ "378282246310005",
+ brand: brand,
+ transaction_id: "123",
+ eci: "05",
+ payment_cryptogram: "whatever_the_cryptogram_is",
+ )
+
+ @gateway.purchase(@amount, credit_card, @options)
+ end.check_request do |_, data, _|
+ assert_match "05", data
+ assert_match "123", data
+ assert_match "whatever_the_cryptogram_is", data
+ assert_xml_valid_to_wsdl(data)
+ end.respond_with(successful_purchase_response)
+ end
+ end
+
def test_requests_include_card_authentication_data
authentication_hash = {
eci: "06",
@@ -246,6 +291,7 @@ def test_requests_include_card_authentication_data
assert_match "06", data
assert_match "SAMPLECAVV", data
assert_match "SAMPLEXID", data
+ assert_xml_valid_to_wsdl(data)
end.respond_with(successful_purchase_response)
end
@@ -265,15 +311,13 @@ def test_add_swipe_data_with_creditcard
@gateway.purchase(@amount, @credit_card)
end.check_request do |endpoint, data, headers|
assert_match "Track Data", data
+ assert_match "R", data
end.respond_with(successful_purchase_response)
end
- def test_supports_scrubbing?
+ def test_transcript_scrubbing
assert @gateway.supports_scrubbing?
- end
-
- def test_scrub
- assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ assert_equal @gateway.scrub(pre_scrub), post_scrub
end
def test_supports_network_tokenization
@@ -282,14 +326,21 @@ def test_supports_network_tokenization
private
- def pre_scrubbed
+ def assert_xml_valid_to_wsdl(data)
+ xsd = Nokogiri::XML::Schema(File.open("#{File.dirname(__FILE__)}/../../schema/firstdata_e4/v11.xsd"))
+ doc = Nokogiri::XML(data)
+ errors = xsd.validate(doc)
+ assert_empty errors, "XSD validation errors in the following XML:\n#{doc}"
+ end
+
+ def pre_scrub
<<-PRE_SCRUBBED
opening connection to api.demo.globalgatewaye4.firstdata.com:443...
opened
starting SSL for api.demo.globalgatewaye4.firstdata.com:443...
SSL established
<- "POST /transaction/v11 HTTP/1.1\r\nContent-Type: application/xml\r\nAccepts: application/xml\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api.demo.globalgatewaye4.firstdata.com\r\nContent-Length: 593\r\n\r\n"
- <- "REDACTEDREDACTED001.0042424242424242420916Longbob LongsenVisa1234 My Street|K1C2N6|Ottawa|ON|CA11231Store Purchase"
+ <- "REDACTEDREDACTED001.0042424242424242420916Longbob LongsenVisa1234 My Street|K1C2N6|Ottawa|ON|CA11231Store Purchaselol"
-> "HTTP/1.1 201 Created\r\n"
-> "Cache-Control: max-age=0, private, must-revalidate\r\n"
-> "Content-Type: application/xml; charset=utf-8\r\n"
@@ -305,21 +356,21 @@ def pre_scrubbed
-> "Connection: Close\r\n"
-> "\r\n"
reading 2872 bytes...
- -> "\n\n AD2327-05\n \n 00\n 1.0\n \n ############4242\n 42930941\n \n \n \n ET151682\n 0916\n Longbob Longsen\n 1234 My Street|K1C2N6|Ottawa|ON|CA\n 123\n 0\n \n \n \n \n \n \n \n \n \n \n \n 1\n \n Store Purchase\n \n 216.191.105.146\n \n false\n true\n 00\n Transaction Normal\n 100\n Approved\n \n 106826\n 1\n M\n 0025564\n \n USD\n \n false\n Shopify DEMO0678\n 126 York Street\n Ottawa\n Alabama\n Canada\n K1N 5T5\n www.shopify.com\n \n Visa\n \n \n \n \n false\n =========== TRANSACTION RECORD ==========\nShopify DEMO0678\n126 York Street\nOttawa, AL K1N 5T5\nCanada\nwww.shopify.com\n\nTY"
+ -> "\n\n AD2327-05\n \n 00\n 1.0\n \n ############4242\n 42930941\n \n \n \n ET151682\n 0916\n Longbob Longsen\n 1234 My Street|K1C2N6|Ottawa|ON|CA\n 123\n 0\n \n \n \n \n \n \n \n \n \n lol\n \n 1\n \n Store Purchase\n \n 216.191.105.146\n \n false\n true\n 00\n Transaction Normal\n 100\n Approved\n \n 106826\n 1\n M\n 0025564\n \n USD\n \n false\n Shopify DEMO0678\n 126 York Street\n Ottawa\n Alabama\n Canada\n K1N 5T5\n www.shopify.com\n \n Visa\n \n \n \n \n false\n =========== TRANSACTION RECORD ==========\nShopify DEMO0678\n126 York Street\nOttawa, AL K1N 5T5\nCanada\nwww.shopify.com\n\nTY"
-> "PE: Purchase\n\nACCT: Visa $ 1.00 USD\n\nCARDHOLDER NAME : Longbob Longsen\nCARD NUMBER : ############4242\nDATE/TIME : 26 Jan 15 12:11:44\nREFERENCE # : 106826 M\nAUTHOR. # : ET151682\nTRANS. REF. : 1\n\n Approved - Thank You 100\n\n\nPlease retain this copy for your records.\n\nCardholder will pay above amount to card\nissuer pursuant to cardholder agreement.\n=========================================\n\n"
read 2872 bytes
Conn close
PRE_SCRUBBED
end
- def post_scrubbed
+ def post_scrub
<<-POST_SCRUBBED
opening connection to api.demo.globalgatewaye4.firstdata.com:443...
opened
starting SSL for api.demo.globalgatewaye4.firstdata.com:443...
SSL established
<- "POST /transaction/v11 HTTP/1.1\r\nContent-Type: application/xml\r\nAccepts: application/xml\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api.demo.globalgatewaye4.firstdata.com\r\nContent-Length: 593\r\n\r\n"
- <- "REDACTEDREDACTED001.00[FILTERED]0916Longbob LongsenVisa1234 My Street|K1C2N6|Ottawa|ON|CA1[FILTERED]1Store Purchase"
+ <- "REDACTED[FILTERED]001.00[FILTERED]0916Longbob LongsenVisa1234 My Street|K1C2N6|Ottawa|ON|CA1[FILTERED]1Store Purchase[FILTERED]"
-> "HTTP/1.1 201 Created\r\n"
-> "Cache-Control: max-age=0, private, must-revalidate\r\n"
-> "Content-Type: application/xml; charset=utf-8\r\n"
@@ -335,7 +386,7 @@ def post_scrubbed
-> "Connection: Close\r\n"
-> "\r\n"
reading 2872 bytes...
- -> "\n\n AD2327-05\n \n 00\n 1.0\n \n [FILTERED]\n 42930941\n \n \n \n ET151682\n 0916\n Longbob Longsen\n 1234 My Street|K1C2N6|Ottawa|ON|CA\n [FILTERED]\n 0\n \n \n \n \n \n \n \n \n \n \n \n 1\n \n Store Purchase\n \n 216.191.105.146\n \n false\n true\n 00\n Transaction Normal\n 100\n Approved\n \n 106826\n 1\n M\n 0025564\n \n USD\n \n false\n Shopify DEMO0678\n 126 York Street\n Ottawa\n Alabama\n Canada\n K1N 5T5\n www.shopify.com\n \n Visa\n \n \n \n \n false\n =========== TRANSACTION RECORD ==========\nShopify DEMO0678\n126 York Street\nOttawa, AL K1N 5T5\nCanada\nwww.shopify.com\n\nTY"
+ -> "\n\n AD2327-05\n \n 00\n 1.0\n \n [FILTERED]\n 42930941\n \n \n \n ET151682\n 0916\n Longbob Longsen\n 1234 My Street|K1C2N6|Ottawa|ON|CA\n [FILTERED]\n 0\n \n \n \n \n \n \n \n \n \n [FILTERED]\n \n 1\n \n Store Purchase\n \n 216.191.105.146\n \n false\n true\n 00\n Transaction Normal\n 100\n Approved\n \n 106826\n 1\n M\n 0025564\n \n USD\n \n false\n Shopify DEMO0678\n 126 York Street\n Ottawa\n Alabama\n Canada\n K1N 5T5\n www.shopify.com\n \n Visa\n \n \n \n \n false\n =========== TRANSACTION RECORD ==========\nShopify DEMO0678\n126 York Street\nOttawa, AL K1N 5T5\nCanada\nwww.shopify.com\n\nTY"
-> "PE: Purchase\n\nACCT: Visa $ 1.00 USD\n\nCARDHOLDER NAME : Longbob Longsen\nCARD NUMBER : ############4242\nDATE/TIME : 26 Jan 15 12:11:44\nREFERENCE # : 106826 M\nAUTHOR. # : ET151682\nTRANS. REF. : 1\n\n Approved - Thank You 100\n\n\nPlease retain this copy for your records.\n\nCardholder will pay above amount to card\nissuer pursuant to cardholder agreement.\n=========================================\n\n"
read 2872 bytes
Conn close
diff --git a/test/unit/gateways/forte_test.rb b/test/unit/gateways/forte_test.rb
index d3990377e13..de88de9ab31 100644
--- a/test/unit/gateways/forte_test.rb
+++ b/test/unit/gateways/forte_test.rb
@@ -1,6 +1,8 @@
require 'test_helper'
class ForteTest < Test::Unit::TestCase
+ include CommStub
+
def setup
@gateway = ForteGateway.new(location_id: 'location_id', account_id: 'account_id', api_key: 'api_key', secret: 'secret')
@credit_card = credit_card
@@ -15,9 +17,9 @@ def setup
end
def test_successful_purchase
- @gateway.expects(:handle_resp).returns(successful_purchase_response)
-
- response = @gateway.purchase(@amount, @credit_card, @options)
+ response = stub_comms(@gateway, :raw_ssl_request) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end.respond_with(MockedResponse.new(successful_purchase_response))
assert_success response
assert_equal 'trn_bb7687a7-3d3a-40c2-8fa9-90727a814249#123456', response.authorization
@@ -26,24 +28,25 @@ def test_successful_purchase
def test_purchase_passes_options
options = { order_id: '1' }
-
@gateway.expects(:commit).with(anything, has_entries(:order_number => '1'))
- @gateway.purchase(@amount, @credit_card, options)
+ stub_comms(@gateway, :raw_ssl_request) do
+ @gateway.purchase(@amount, @credit_card, options)
+ end.respond_with(MockedResponse.new(successful_purchase_response))
end
def test_failed_purchase
- @gateway.expects(:handle_resp).returns(failed_purchase_response)
-
- response = @gateway.purchase(@amount, @credit_card, @options)
+ response = stub_comms(@gateway, :raw_ssl_request) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end.respond_with(MockedResponse.new(failed_purchase_response))
assert_failure response
assert_equal "INVALID TRN", response.message
end
def test_successful_purchase_with_echeck
- @gateway.expects(:handle_resp).returns(successful_echeck_purchase_response)
-
- response = @gateway.purchase(@amount, @check, @options)
+ response = stub_comms(@gateway, :raw_ssl_request) do
+ @gateway.purchase(@amount, @check, @options)
+ end.respond_with(MockedResponse.new(successful_echeck_purchase_response))
assert_success response
assert_equal 'trn_bb7687a7-3d3a-40c2-8fa9-90727a814249#123456', response.authorization
@@ -51,88 +54,88 @@ def test_successful_purchase_with_echeck
end
def test_failed_purchase_with_echeck
- @gateway.expects(:handle_resp).returns(failed_echeck_purchase_response)
-
- response = @gateway.purchase(@amount, @credit_card, @options)
+ response = stub_comms(@gateway, :raw_ssl_request) do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end.respond_with(MockedResponse.new(failed_echeck_purchase_response))
assert_failure response
assert_equal "INVALID CREDIT CARD NUMBER", response.message
end
def test_successful_authorize
- @gateway.expects(:handle_resp).returns(successful_authorize_response)
-
- response = @gateway.authorize(@amount, @credit_card, @options)
+ response = stub_comms(@gateway, :raw_ssl_request) do
+ @gateway.authorize(@amount, @credit_card, @options)
+ end.respond_with(MockedResponse.new(successful_authorize_response))
assert_success response
end
def test_failed_authorize
- @gateway.expects(:handle_resp).returns(failed_authorize_response)
-
- response = @gateway.authorize(@amount, @credit_card, @options)
+ response = stub_comms(@gateway, :raw_ssl_request) do
+ @gateway.authorize(@amount, @credit_card, @options)
+ end.respond_with(MockedResponse.new(failed_authorize_response))
assert_failure response
assert_equal "INVALID CREDIT CARD NUMBER", response.message
end
def test_successful_capture
- @gateway.expects(:handle_resp).returns(successful_capture_response)
-
- response = @gateway.capture(@amount, "authcode")
+ response = stub_comms(@gateway, :raw_ssl_request) do
+ @gateway.capture(@amount, "authcode")
+ end.respond_with(MockedResponse.new(successful_capture_response))
assert_success response
end
def test_failed_capture
- @gateway.expects(:handle_resp).returns(failed_capture_response)
-
- response = @gateway.capture(@amount, "authcode")
+ response = stub_comms(@gateway, :raw_ssl_request) do
+ @gateway.capture(@amount, "authcode")
+ end.respond_with(MockedResponse.new(failed_capture_response))
assert_failure response
end
def test_successful_credit
- @gateway.expects(:handle_resp).returns(successful_credit_response)
-
- response = @gateway.credit(@amount, @credit_card, @options)
+ response = stub_comms(@gateway, :raw_ssl_request) do
+ @gateway.credit(@amount, @credit_card, @options)
+ end.respond_with(MockedResponse.new(successful_credit_response))
assert_success response
end
def test_failed_credit
- @gateway.expects(:handle_resp).returns(failed_credit_response)
-
- response = @gateway.credit(@amount, @credit_card, @options)
+ response = stub_comms(@gateway, :raw_ssl_request) do
+ @gateway.credit(@amount, @credit_card, @options)
+ end.respond_with(MockedResponse.new(failed_credit_response))
assert_failure response
end
def test_successful_void
- @gateway.expects(:handle_resp).returns(successful_credit_response)
-
- response = @gateway.void("authcode")
+ response = stub_comms(@gateway, :raw_ssl_request) do
+ @gateway.void("authcode")
+ end.respond_with(MockedResponse.new(successful_credit_response))
assert_success response
end
def test_failed_void
- @gateway.expects(:handle_resp).returns(failed_credit_response)
-
- response = @gateway.void("authcode")
+ response = stub_comms(@gateway, :raw_ssl_request) do
+ @gateway.void("authcode")
+ end.respond_with(MockedResponse.new(failed_credit_response))
assert_failure response
end
def test_successful_verify
- @gateway.expects(:handle_resp).times(2).returns(successful_authorize_response, successful_void_response)
-
- response = @gateway.verify(@credit_card, @options)
+ response = stub_comms(@gateway, :raw_ssl_request) do
+ @gateway.verify(@credit_card, @options)
+ end.respond_with(MockedResponse.new(successful_authorize_response), MockedResponse.new(successful_void_response))
assert_success response
end
def test_successful_verify_with_failed_void
- @gateway.expects(:handle_resp).times(2).returns(successful_authorize_response, failed_void_response)
-
- response = @gateway.verify(@credit_card, @options)
+ response = stub_comms(@gateway, :raw_ssl_request) do
+ @gateway.verify(@credit_card, @options)
+ end.respond_with(MockedResponse.new(successful_authorize_response), MockedResponse.new(failed_void_response))
assert_success response
end
def test_failed_verify
- @gateway.expects(:handle_resp).returns(failed_authorize_response)
-
- response = @gateway.verify(@credit_card, @options)
+ response = stub_comms(@gateway, :raw_ssl_request) do
+ @gateway.verify(@credit_card, @options)
+ end.respond_with(MockedResponse.new(failed_authorize_response))
assert_failure response
end
@@ -143,6 +146,14 @@ def test_scrub
private
+ class MockedResponse
+ attr :code, :body
+ def initialize(body, code = 200)
+ @code = code
+ @body = body
+ end
+ end
+
def pre_scrubbed
%q(
<- "POST /api/v2/accounts/act_300111/locations/loc_176008/transactions/ HTTP/1.1\r\nContent-Type: application/json\r\nAuthorization: Basic ZjA4N2E5MGYwMGYwYWU1NzA1MGM5MzdlZDM4MTVjOWY6ZDc5M2Q2NDA2NGUzMTEzYTc0ZmE3MjAzNWNmYzNhMWQ=\r\nX-Forte-Auth-Account-Id: act_300111\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: sandbox.forte.net\r\nContent-Length: 471\r\n\r\n"
diff --git a/test/unit/gateways/gateway_test.rb b/test/unit/gateways/gateway_test.rb
index e2554bf98c5..a96c3c506c7 100644
--- a/test/unit/gateways/gateway_test.rb
+++ b/test/unit/gateways/gateway_test.rb
@@ -83,11 +83,23 @@ def test_localized_amount_should_not_modify_for_fractional_currencies
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')
+ assert_equal '12', @gateway.send(:localized_amount, 1234, 'ISK')
Gateway.money_format = :cents
assert_equal '1', @gateway.send(:localized_amount, 100, 'JPY')
- assert_equal '12', @gateway.send(:localized_amount, 1234, 'HUF')
+ assert_equal '12', @gateway.send(:localized_amount, 1234, 'ISK')
+ end
+
+ def test_localized_amount_returns_three_decimal_places_for_three_decimal_currencies
+ @gateway.currencies_with_three_decimal_places = %w(BHD KWD OMR RSD TND)
+
+ Gateway.money_format = :dollars
+ assert_equal '0.100', @gateway.send(:localized_amount, 100, 'OMR')
+ assert_equal '1.234', @gateway.send(:localized_amount, 1234, 'BHD')
+
+ Gateway.money_format = :cents
+ assert_equal '100', @gateway.send(:localized_amount, 100, 'OMR')
+ assert_equal '1234', @gateway.send(:localized_amount, 1234, 'BHD')
end
def test_split_names
diff --git a/test/unit/gateways/global_collect_test.rb b/test/unit/gateways/global_collect_test.rb
index 0497be74dfc..6ed57284bd5 100644
--- a/test/unit/gateways/global_collect_test.rb
+++ b/test/unit/gateways/global_collect_test.rb
@@ -35,6 +35,72 @@ def test_successful_authorize_and_capture
assert_success capture
end
+ def test_purchase_does_not_run_capture_if_authorize_auto_captured
+ response = stub_comms do
+ @gateway.purchase(@accepted_amount, @credit_card, @options)
+ end.respond_with(successful_capture_response)
+
+ assert_success response
+ assert_equal "CAPTURE_REQUESTED", response.params["payment"]["status"]
+ assert_equal 1, response.responses.size
+ end
+
+ def test_authorize_with_pre_authorization_flag
+ response = stub_comms do
+ @gateway.authorize(@accepted_amount, @credit_card, @options.merge(pre_authorization: true))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/PRE_AUTHORIZATION/, data)
+ end.respond_with(successful_authorize_response)
+
+ assert_success response
+ end
+
+ def test_authorize_without_pre_authorization_flag
+ response = stub_comms do
+ @gateway.authorize(@accepted_amount, @credit_card, @options)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/FINAL_AUTHORIZATION/, data)
+ end.respond_with(successful_authorize_response)
+
+ assert_success response
+ end
+
+ def test_successful_authorization_with_extra_options
+ options = @options.merge(
+ {
+ order_id: "123",
+ ip: "127.0.0.1",
+ fraud_fields:
+ {
+ "website" => "www.example.com",
+ "giftMessage" => "Happy Day!"
+ }
+ }
+ )
+
+ response = stub_comms do
+ @gateway.authorize(@accepted_amount, @credit_card, options)
+ end.check_request do |endpoint, data, headers|
+ assert_match %r("fraudFields":{"website":"www.example.com","giftMessage":"Happy Day!","customerIpAddress":"127.0.0.1"}), data
+ assert_match %r("merchantReference":"123"), data
+ end.respond_with(successful_authorize_response)
+
+ assert_success response
+ end
+
+ def test_trucates_first_name_to_15_chars
+ credit_card = credit_card('4567350000427977', { first_name: "thisisaverylongfirstname" })
+
+ response = stub_comms do
+ @gateway.authorize(@accepted_amount, credit_card, @options)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/thisisaverylong/, data)
+ end.respond_with(successful_authorize_response)
+
+ assert_success response
+ assert_equal "000000142800000000920000100001", response.authorization
+ end
+
def test_failed_authorize
response = stub_comms do
@gateway.authorize(@rejected_amount, @declined_card, @options)
@@ -117,6 +183,14 @@ def test_successful_refund
assert_success refund
end
+ def test_refund_passes_currency_code
+ stub_comms do
+ @gateway.refund(@accepted_amount, '000000142800000000920000100001', {currency: 'COP'})
+ end.check_request do |endpoint, data, headers|
+ assert_match(/"currencyCode\":\"COP\"/, data)
+ end.respond_with(failed_refund_response)
+ end
+
def test_failed_refund
response = stub_comms do
@gateway.refund(nil, "")
@@ -125,6 +199,16 @@ def test_failed_refund
assert_failure response
end
+ def test_rejected_refund
+ response = stub_comms do
+ @gateway.refund(@accepted_amount, '000000142800000000920000100001')
+ end.respond_with(rejected_refund_response)
+
+ assert_failure response
+ assert_equal "1850", response.error_code
+ assert_equal "Status: REJECTED", response.message
+ end
+
def test_scrub
assert @gateway.supports_scrubbing?
assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
@@ -268,6 +352,10 @@ def failed_refund_response
%({\n \"errorId\" : \"1bd31e6a-39dd-4214-941a-088a320e0286\",\n \"errors\" : [ {\n \"code\" : \"1002\",\n \"propertyName\" : \"paymentId\",\n \"message\" : \"INVALID_PAYMENT_ID\"\n } ]\n})
end
+ def rejected_refund_response
+ %({\n \"id\" : \"00000022184000047564000-100001\",\n \"refundOutput\" : {\n \"amountOfMoney\" : {\n \"amount\" : 627000,\n \"currencyCode\" : \"COP\"\n },\n \"references\" : {\n \"merchantReference\" : \"17091GTgZmcC\",\n \"paymentReference\" : \"0\"\n },\n \"paymentMethod\" : \"card\",\n \"cardRefundMethodSpecificOutput\" : {\n }\n },\n \"status\" : \"REJECTED\",\n \"statusOutput\" : {\n \"isCancellable\" : false,\n \"statusCategory\" : \"UNSUCCESSFUL\",\n \"statusCode\" : 1850,\n \"statusCodeChangeDateTime\" : \"20170313230631\"\n }\n})
+ end
+
def successful_void_response
%({\n \"payment\" : {\n \"id\" : \"000000142800000000920000100001\",\n \"paymentOutput\" : {\n \"amountOfMoney\" : {\n \"amount\" : 100,\n \"currencyCode\" : \"USD\"\n },\n \"references\" : {\n \"paymentReference\" : \"0\"\n },\n \"paymentMethod\" : \"card\",\n \"cardPaymentMethodSpecificOutput\" : {\n \"paymentProductId\" : 1,\n \"authorisationCode\" : \"OK1131\",\n \"card\" : {\n \"cardNumber\" : \"************7977\",\n \"expiryDate\" : \"0917\"\n },\n \"fraudResults\" : {\n \"fraudServiceResult\" : \"no-advice\",\n \"avsResult\" : \"0\",\n \"cvvResult\" : \"0\"\n }\n }\n },\n \"status\" : \"CANCELLED\",\n \"statusOutput\" : {\n \"isCancellable\" : false,\n \"statusCode\" : 99999,\n \"statusCodeChangeDateTime\" : \"20160317191526\"\n }\n }\n})
end
diff --git a/test/unit/gateways/global_transport_test.rb b/test/unit/gateways/global_transport_test.rb
index a5956436d26..bce3e7e4d28 100644
--- a/test/unit/gateways/global_transport_test.rb
+++ b/test/unit/gateways/global_transport_test.rb
@@ -31,6 +31,17 @@ def test_failed_purchase
assert_failure response
end
+ def test_successful_partial_purchase
+ @gateway.expects(:ssl_post).returns(successful_partial_purchase_response)
+
+ response = @gateway.purchase(200, credit_card, @options)
+ assert_success response
+ assert_equal '8869188', response.authorization
+ assert_equal 'Partial Approval', response.message
+ assert_equal '3.54', response.params["balance_due"]
+ assert_equal '20.00', response.params["approved_amount"]
+ end
+
def test_successful_authorize_and_capture
response = stub_comms do
@gateway.authorize(100, credit_card)
@@ -48,6 +59,24 @@ def test_successful_authorize_and_capture
assert_success capture
end
+ def test_successful_partial_authorize_and_capture
+ response = stub_comms do
+ @gateway.authorize(200, credit_card, @options)
+ end.respond_with(successful_partial_authorize_response)
+
+ assert_success response
+ assert_equal "8869269", response.authorization
+ assert_equal "Partial Approval", response.message
+
+ capture = stub_comms do
+ @gateway.capture(150, response.authorization)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/PNRef=8869269/, data)
+ end.respond_with(successful_partial_capture_response)
+
+ assert_success capture
+ end
+
def test_failed_authorize
@gateway.expects(:ssl_post).returns(failed_authorize_response)
@@ -138,8 +167,13 @@ def test_truncation
end.respond_with(successful_purchase_response)
end
+ def test_transcript_scrubbing
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrub), post_scrub
+ end
private
+
def successful_purchase_response
%(
@@ -180,6 +214,66 @@ def failed_purchase_response
)
end
+ def successful_partial_purchase_response
+ %(
+
+ 200
+ Partial Approval
+ PARTIAL AP
+ VI2000
+ 8869188
+ 0004
+ N
+ No Match
+ No Match
+ No Match
+ M
+ Match
+ False
+ InvNum=1,CardType=Visa,BatchNum=0005<BatchNum>0005</BatchNum><ReceiptData><MID>332518545311149</MID><Trans_Id>017198190587855</Trans_Id><Val_Code>AABC</Val_Code></ReceiptData><ApprovedAmount>20.00</ApprovedAmount><BalanceDue>3.54</BalanceDue>
+ aWb017198190587855cAABCd5e10fJj470993170717112415k0057840C000000002354lA m000005
+
+ )
+ end
+
+ def successful_partial_authorize_response
+ %(
+
+ 200
+ Partial Approval
+ PARTIAL AP
+ VI2000
+ 8869269
+ N
+ No Match
+ No Match
+ No Match
+ M
+ Match
+ False
+ InvNum=1,CardType=Visa<ReceiptData><MID>332518545311149</MID><Trans_Id>017198190582649</Trans_Id><Val_Code>AABC</Val_Code></ReceiptData><ApprovedAmount>20.00</ApprovedAmount><BalanceDue>3.54</BalanceDue>
+ aWb017198190582649cAABCd5e10fJj471048170717124409k0057840C000000002354lA m000005
+
+ )
+ end
+
+ def successful_partial_capture_response
+ %(
+
+ 0
+ Approved
+ AP
+ VI2000
+ 8869275
+ 0034
+ Service Not Requested
+ False
+ InvNum=1,CardType=Visa,BatchNum=0005<ExtReceiptData><AccountNumber>************1111</AccountNumber><Issuer>Visa</Issuer><Amount>20.00</Amount><AuthAmount>20.00</AuthAmount><TicketNumber>1</TicketNumber><EntryMode>Manual CNP</EntryMode></ExtReceiptData><BatchNum>0005</BatchNum><ReceiptData><MID>332518545311149</MID><Trans_Id>017198190583609</Trans_Id><Val_Code>AABC</Val_Code></ReceiptData>
+ aWb017198190583609cAABCd5e10fJj471054170717130009lA m000005
+
+ )
+ end
+
def successful_authorize_response
%(
@@ -321,4 +415,62 @@ def failed_verify_response
)
end
+
+ def pre_scrub
+ %q{
+opening connection to certapia.globalpay.com:443...
+opened
+starting SSL for certapia.globalpay.com:443...
+SSL established
+<- "POST /GlobalPay/transact.asmx/ProcessCreditCard HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: certapia.globalpay.com\r\nContent-Length: 253\r\n\r\n"
+<- "CardNum=4003002345678903&ExpDate=0919&NameOnCard=Longbob+Longsen&Amount=&PNRef=&Zip=K1C2N6&Street=456+My+Street&CVNum=123&MagData=&InvNum=1&ExtData=%3CTermType%3E1BJ%3C%2FTermType%3E&GlobalUserName=spre930948&GlobalPassword=AoaeYX2n3Y7wfr&TransType=Sale"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Mon, 08 Jan 2018 16:00:33 GMT\r\n"
+-> "Content-Type: text/xml; charset=utf-8\r\n"
+-> "Content-Length: 559\r\n"
+-> "Connection: close\r\n"
+-> "Cache-Control: private, no-store, max-age=0\r\n"
+-> "Pragma: no-cache\r\n"
+-> "X-Frame-Options: SAMEORIGIN\r\n"
+-> "Set-Cookie: ASP.NET_SessionId=tawdjune2xixlighniqxcvkm; path=/; HttpOnly\r\n"
+-> "X-AspNet-Version: 4.0.30319\r\n"
+-> "X-Powered-By: ASP.NET\r\n"
+-> "Set-Cookie: TS012462a7=01cf83bd2f409aa8ee6c4cacb355788019f1a8ff3010306e6b8fe1c42e01745058ecc78aeff78d5071c2b7c56c186a470efd7c78f1; Path=/; Secure\r\n"
+-> "Set-Cookie: TS012462a7_28=013b80ac89ca00c8b688533fc64e6f7b3fa3424b483ef82651a9f9a1c184ec131cc099732b39bf84f703f9f0754d2a12a53fe3d537; Path=/; Secure\r\n"
+-> "\r\n"
+reading 559 bytes...
+-> "\r\n\r\n 12\r\n Declined\r\n INVLD AMOUNT\r\n 9169188\r\n Service Not Requested\r\n False\r\n InvNum=1,CardType=Visa<ReceiptData><MID>332518545311149</MID></ReceiptData>\r\n aY\r\n"
+read 559 bytes
+Conn close
+ }
+ end
+
+ def post_scrub
+ %q{
+opening connection to certapia.globalpay.com:443...
+opened
+starting SSL for certapia.globalpay.com:443...
+SSL established
+<- "POST /GlobalPay/transact.asmx/ProcessCreditCard HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: certapia.globalpay.com\r\nContent-Length: 253\r\n\r\n"
+<- "CardNum=[FILTERED]&ExpDate=0919&NameOnCard=Longbob+Longsen&Amount=&PNRef=&Zip=K1C2N6&Street=456+My+Street&CVNum=[FILTERED]&MagData=&InvNum=1&ExtData=%3CTermType%3E1BJ%3C%2FTermType%3E&GlobalUserName=spre930948&GlobalPassword=[FILTERED]&TransType=Sale"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Mon, 08 Jan 2018 16:00:33 GMT\r\n"
+-> "Content-Type: text/xml; charset=utf-8\r\n"
+-> "Content-Length: 559\r\n"
+-> "Connection: close\r\n"
+-> "Cache-Control: private, no-store, max-age=0\r\n"
+-> "Pragma: no-cache\r\n"
+-> "X-Frame-Options: SAMEORIGIN\r\n"
+-> "Set-Cookie: ASP.NET_SessionId=tawdjune2xixlighniqxcvkm; path=/; HttpOnly\r\n"
+-> "X-AspNet-Version: 4.0.30319\r\n"
+-> "X-Powered-By: ASP.NET\r\n"
+-> "Set-Cookie: TS012462a7=01cf83bd2f409aa8ee6c4cacb355788019f1a8ff3010306e6b8fe1c42e01745058ecc78aeff78d5071c2b7c56c186a470efd7c78f1; Path=/; Secure\r\n"
+-> "Set-Cookie: TS012462a7_28=013b80ac89ca00c8b688533fc64e6f7b3fa3424b483ef82651a9f9a1c184ec131cc099732b39bf84f703f9f0754d2a12a53fe3d537; Path=/; Secure\r\n"
+-> "\r\n"
+reading 559 bytes...
+-> "\r\n\r\n 12\r\n Declined\r\n INVLD AMOUNT\r\n 9169188\r\n Service Not Requested\r\n False\r\n InvNum=1,CardType=Visa<ReceiptData><MID>332518545311149</MID></ReceiptData>\r\n aY\r\n"
+read 559 bytes
+Conn close
+ }
+ end
end
diff --git a/test/unit/gateways/hps_test.rb b/test/unit/gateways/hps_test.rb
index 3f964d51ec7..02112f260f3 100644
--- a/test/unit/gateways/hps_test.rb
+++ b/test/unit/gateways/hps_test.rb
@@ -190,6 +190,11 @@ def test_test_returns_false
assert_false @gateway.send(:test?)
end
+ def test_transcript_scrubbing
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrub), post_scrub
+ end
+
private
def successful_charge_response
@@ -604,4 +609,64 @@ def failed_verify_response
RESPONSE
end
+ def pre_scrub
+ %q{
+opening connection to posgateway.cert.secureexchange.net:443...
+opened
+starting SSL for posgateway.cert.secureexchange.net:443...
+SSL established
+<- "POST /Hps.Exchange.PosGateway/PosGatewayService.asmx?wsdl HTTP/1.1\r\nContent-Type: text/xml\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: posgateway.cert.secureexchange.net\r\nContent-Length: 1295\r\n\r\n"
+<- "skapi_cert_MYl2AQAowiQAbLp5JesGKh7QFkcizOP2jcX9BrEMqQ1.00YLongbobLongsen456 My StreetOttawaONK1C2N6Store Purchase1400010001111222492019123NNN"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Cache-Control: private, max-age=0\r\n"
+-> "Content-Type: text/xml; charset=utf-8\r\n"
+-> "Server: Microsoft-IIS/7.5\r\n"
+-> "X-dynaTrace: PT=266421;PA=-1324159421;SP=Gateway Cert;PS=1926692524\r\n"
+-> "dynaTrace: PT=266421;PA=-1324159421;SP=Gateway Cert;PS=1926692524\r\n"
+-> "X-AspNet-Version: 4.0.30319\r\n"
+-> "X-Powered-By: ASP.NET\r\n"
+-> "X-Frame-Options: DENY\r\n"
+-> "X-Content-Type-Options: nosniff\r\n"
+-> "Date: Mon, 08 Jan 2018 16:28:18 GMT\r\n"
+-> "Connection: close\r\n"
+-> "Content-Length: 1067\r\n"
+-> "\r\n"
+reading 1067 bytes...
+-> "9587895881240900010359677660Success2018-01-08T10:28:18.555593600APPROVAL64349A0M800818231451 "on>ACCEPTACCEPTVisaAVS Not Requested.Match."
+read 1067 bytes
+Conn close
+ }
+ end
+
+ def post_scrub
+ %q{
+opening connection to posgateway.cert.secureexchange.net:443...
+opened
+starting SSL for posgateway.cert.secureexchange.net:443...
+SSL established
+<- "POST /Hps.Exchange.PosGateway/PosGatewayService.asmx?wsdl HTTP/1.1\r\nContent-Type: text/xml\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: posgateway.cert.secureexchange.net\r\nContent-Length: 1295\r\n\r\n"
+<- "[FILTERED]1.00YLongbobLongsen456 My StreetOttawaONK1C2N6Store Purchase1[FILTERED]92019[FILTERED]NNN"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Cache-Control: private, max-age=0\r\n"
+-> "Content-Type: text/xml; charset=utf-8\r\n"
+-> "Server: Microsoft-IIS/7.5\r\n"
+-> "X-dynaTrace: PT=266421;PA=-1324159421;SP=Gateway Cert;PS=1926692524\r\n"
+-> "dynaTrace: PT=266421;PA=-1324159421;SP=Gateway Cert;PS=1926692524\r\n"
+-> "X-AspNet-Version: 4.0.30319\r\n"
+-> "X-Powered-By: ASP.NET\r\n"
+-> "X-Frame-Options: DENY\r\n"
+-> "X-Content-Type-Options: nosniff\r\n"
+-> "Date: Mon, 08 Jan 2018 16:28:18 GMT\r\n"
+-> "Connection: close\r\n"
+-> "Content-Length: 1067\r\n"
+-> "\r\n"
+reading 1067 bytes...
+-> "9587895881240900010359677660Success2018-01-08T10:28:18.555593600APPROVAL64349A0M800818231451 "on>ACCEPTACCEPTVisaAVS Not Requested.Match."
+read 1067 bytes
+Conn close
+ }
+ end
+
end
diff --git a/test/unit/gateways/iats_payments_test.rb b/test/unit/gateways/iats_payments_test.rb
index 26f0812f2a1..e3aa2aab5a1 100644
--- a/test/unit/gateways/iats_payments_test.rb
+++ b/test/unit/gateways/iats_payments_test.rb
@@ -243,6 +243,24 @@ def test_supported_countries
end
end
+ def test_failed_connection
+ response = stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end.respond_with(failed_connection_response)
+
+ assert response
+ assert_failure response
+ assert_match(/Server Error/, response.message)
+ end
+
+ def test_scrub
+ assert_equal @gateway.scrub(pre_scrub), post_scrub
+ end
+
+ def test_supports_scrubbing?
+ assert @gateway.supports_scrubbing?
+ end
+
private
def successful_purchase_response
@@ -504,4 +522,74 @@ def successful_unstore_response
XML
end
+
+ def failed_connection_response
+ <<-XML
+
+
+
+
+
+
+ Failure
+ Server Error
+
+
+
+
+
+
+
+ XML
+ end
+
+ def pre_scrub
+ <<-XML
+ opening connection to www.iatspayments.com:443...
+ opened
+ starting SSL for www.iatspayments.com:443...
+ SSL established
+ <- "POST /NetGate/ProcessLink.asmx?op=ProcessCreditCardV1 HTTP/1.1\r\nContent-Type: application/soap+xml; charset=utf-8\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: www.iatspayments.com\r\nContent-Length: 779\r\n\r\n"
+ <- "TEST88TEST8863b5dd7098e8e3a9ff9a6f0992fdb6d51.00LongbobLongsen422222222222222009/17123VISA456 My StreetOttawaONK1C2N6Store purchase"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "Cache-Control: private, max-age=0\r\n"
+ -> "Content-Type: application/soap+xml; charset=utf-8\r\n"
+ -> "X-AspNet-Version: 4.0.30319\r\n"
+ -> "X-Powered-By: ASP.NET\r\n"
+ -> "Date: Thu, 29 Sep 2016 05:41:04 GMT\r\n"
+ -> "Content-Length: 719\r\n"
+ -> "Connection: close\r\n"
+ -> "Via: 1.1 sjc1-10\r\n"
+ -> "\r\n"
+ reading 719 bytes...
+ -> "Success OK: 678594:\n 09/28/2016\n 09/29/2016\nA92E3B72\n"
+ read 719 bytes
+ Conn close
+ XML
+ end
+
+ def post_scrub
+ <<-XML
+ opening connection to www.iatspayments.com:443...
+ opened
+ starting SSL for www.iatspayments.com:443...
+ SSL established
+ <- "POST /NetGate/ProcessLink.asmx?op=ProcessCreditCardV1 HTTP/1.1\r\nContent-Type: application/soap+xml; charset=utf-8\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: www.iatspayments.com\r\nContent-Length: 779\r\n\r\n"
+ <- "[FILTERED][FILTERED]63b5dd7098e8e3a9ff9a6f0992fdb6d51.00LongbobLongsen[FILTERED]09/17[FILTERED]VISA456 My StreetOttawaONK1C2N6Store purchase"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "Cache-Control: private, max-age=0\r\n"
+ -> "Content-Type: application/soap+xml; charset=utf-8\r\n"
+ -> "X-AspNet-Version: 4.0.30319\r\n"
+ -> "X-Powered-By: ASP.NET\r\n"
+ -> "Date: Thu, 29 Sep 2016 05:41:04 GMT\r\n"
+ -> "Content-Length: 719\r\n"
+ -> "Connection: close\r\n"
+ -> "Via: 1.1 sjc1-10\r\n"
+ -> "\r\n"
+ reading 719 bytes...
+ -> "Success OK: 678594:\n 09/28/2016\n 09/29/2016\nA92E3B72\n"
+ read 719 bytes
+ Conn close
+ XML
+ end
end
diff --git a/test/unit/gateways/iveri_test.rb b/test/unit/gateways/iveri_test.rb
new file mode 100644
index 00000000000..c48b6d60591
--- /dev/null
+++ b/test/unit/gateways/iveri_test.rb
@@ -0,0 +1,553 @@
+require 'test_helper'
+
+class IveriTest < Test::Unit::TestCase
+ def setup
+ @gateway = IveriGateway.new(app_id: '123', cert_id: '321')
+ @credit_card = credit_card('4242424242424242')
+ @amount = 100
+
+ @options = {
+ order_id: generate_unique_id,
+ billing_address: address,
+ description: 'Store Purchase',
+ currency: 'ZAR'
+ }
+ end
+
+ def test_successful_purchase
+ @gateway.expects(:ssl_post).returns(successful_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal "{F0568958-D10B-4093-A3BF-663168B06140}|{5CEF96FD-960E-4EA5-811F-D02CE6E36A96}|48b63446223ce91451fc3c1641a9ec03", response.authorization
+ assert response.test?
+ end
+
+ def test_failed_purchase
+ @gateway.expects(:ssl_post).returns(failed_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal '4', response.error_code
+ end
+
+ def test_successful_authorize
+ @gateway.expects(:ssl_post).returns(successful_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal "{B90D7CDB-C8E8-4477-BDF2-695F28137874}|{EF0DC64E-2D00-4B6C-BDA0-2AD265391317}|23b4125c3b8e2777bffee52e196a863b", response.authorization
+ assert response.test?
+ end
+
+ def test_failed_authorize
+ @gateway.expects(:ssl_post).returns(failed_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal '4', response.error_code
+ end
+
+ def test_successful_capture
+ @gateway.expects(:ssl_post).returns(successful_capture_response)
+
+ response = @gateway.capture(@amount, '{B90D7CDB-C8E8-4477-BDF2-695F28137874}|{EF0DC64E-2D00-4B6C-BDA0-2AD265391317}|23b4125c3b8e2777bffee52e196a863b')
+ assert_success response
+ assert_equal "{7C91245F-607D-44AE-8958-C26E447BAEB7}|{EF0DC64E-2D00-4B6C-BDA0-2AD265391317}|23b4125c3b8e2777bffee52e196a863b", response.authorization
+ assert response.test?
+ end
+
+ def test_failed_capture
+ @gateway.expects(:ssl_post).returns(failed_capture_response)
+
+ response = @gateway.capture(@amount, '', @options)
+ assert_failure response
+ assert_equal '14', response.error_code
+ end
+
+ def test_successful_refund
+ @gateway.expects(:ssl_post).returns(successful_refund_response)
+
+ response = @gateway.refund(@amount, '{33C8274D-6811-409A-BF86-661F24084A2F}|{D50DB1B4-B6EC-4AF1-AFF7-71C2AA4A957B}|5be2c040bd46b7eebc70274659779acf')
+ assert_success response
+ assert_equal "{097C55B5-D020-40AD-8949-F9F5E4102F1D}|{D50DB1B4-B6EC-4AF1-AFF7-71C2AA4A957B}|5be2c040bd46b7eebc70274659779acf", response.authorization
+ assert response.test?
+ end
+
+ def test_failed_refund
+ @gateway.expects(:ssl_post).returns(failed_refund_response)
+
+ response = @gateway.refund(@amount, '', @options)
+ assert_failure response
+ assert_equal '255', response.error_code
+ end
+
+ def test_successful_void
+ @gateway.expects(:ssl_post).returns(successful_void_response)
+
+ response = @gateway.void('{230390C8-4A9E-4426-BDD3-15D072F135FE}|{3CC6E6A8-13E0-41A6-AB1E-71BE1AEEAE58}|1435f1a008137cd8508bf43751e07495')
+ assert_success response
+ assert_equal "{0A1A3FFF-C2A3-4B91-85FD-10D1C25B765B}||", response.authorization
+ assert response.test?
+ end
+
+ def test_failed_void
+ @gateway.expects(:ssl_post).returns(failed_void_response)
+
+ response = @gateway.void('', @options)
+ assert_failure response
+ assert_equal '255', response.error_code
+ end
+
+ def test_successful_verify
+ @gateway.expects(:ssl_post).returns(successful_verify_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ assert_equal "{F4337D04-B526-4A7E-A400-2A6DEADDCF57}|{5D5F8BF7-2D9D-42C3-AF32-08C5E62CD45E}|c0006d1d739905afc9e70beaf4194ea3", response.authorization
+ assert response.test?
+ end
+
+ def test_failed_verify
+ @gateway.expects(:ssl_post).returns(failed_verify_response)
+
+ response = @gateway.verify(credit_card('2121212121212121'), @options)
+ assert_failure response
+ assert_equal '4', response.error_code
+ end
+
+ def test_successful_verify_credentials
+ @gateway.expects(:ssl_post).returns(successful_verify_credentials_response)
+ assert @gateway.verify_credentials
+ end
+
+ def test_failed_verify_credentials
+ @gateway.expects(:ssl_post).returns(failed_verify_credentials_response)
+ assert !@gateway.verify_credentials
+ end
+
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
+ private
+
+ def pre_scrubbed
+ %q(
+opening connection to portal.nedsecure.co.za:443...
+opened
+starting SSL for portal.nedsecure.co.za:443...
+SSL established
+<- "POST /iVeriWebService/Service.asmx HTTP/1.1\r\nContent-Type: text/xml; charset=utf-8\r\nContent-Length: 1016\r\nSoapaction: http://iveri.com/Execute\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: portal.nedsecure.co.za\r\n\r\n"
+<- "\n\n \n \n true\n V_XML\n 2.0\n <V_XML Version=\"2.0\" CertificateID=\"CB69E68D-C7E7-46B9-9B7A-025DCABAD6EF\" Direction=\"Request\">\n <Transaction ApplicationID=\"D10A603D-4ADE-405B-93F1-826DFC0181E8\" Command=\"Debit\" Mode=\"Test\">\n <Amount>100</Amount>\n <Currency>ZAR</Currency>\n <ExpiryDate>092018</ExpiryDate>\n <MerchantReference>b3ceea8b93d5611cbde7d162baef1245</MerchantReference>\n <CardSecurityCode>123</CardSecurityCode>\n <PAN>4242424242424242</PAN>\n </Transaction>\n</V_XML>\n \n \n\n"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Cache-Control: private, max-age=0\r\n"
+-> "Content-Type: text/xml; charset=utf-8\r\n"
+-> "Server: Microsoft-IIS/8.0\r\n"
+-> "X-AspNet-Version: 4.0.30319\r\n"
+-> "X-Powered-By: ASP.NET\r\n"
+-> "Date: Wed, 12 Apr 2017 19:46:44 GMT\r\n"
+-> "Connection: close\r\n"
+-> "Content-Length: 2377\r\n"
+-> "\r\n"
+reading 2377 bytes...
+-> "<V_XML Version=\"2.0\" Direction=\"Response\">\r\n <Transaction ApplicationID=\"{D10A603D-4ADE-405B-93F1-826DFC0181E8}\" Command=\"Debit\" Mode=\"Test\" RequestID=\"{5485B5EA-2661-4436-BAA9-CD6DD546FA0D}\">\r\n <Result Status=\"0\" AppServer=\"105IVERIAPPPR02\" DBServer=\"105iveridbpr01\" Gateway=\"Nedbank\" AcquirerCode=\"00\" />\r\n <Amount>100</Amount>\r\n <AuthorisationCode>115205</AuthorisationCode>\r\n <Currency>ZAR</Currency>\r\n <ExpiryDate>092018</ExpiryDate>\r\n <MerchantReference>b3ceea8b93d5611cbde7d162baef1245</MerchantReference>\r\n <Terminal>Default</Terminal>\r\n <TransactionIndex>{10418186-FE90-44F9-AB7A-FEC11C9027F8}</TransactionIndex>\r\n <MerchantName>iVeri Payment Technology</MerchantName>\r\n <MerchantUSN>7771777</MerchantUSN>\r\n <Acquirer>NBPostilionBICISONBSouthAfrica</Acquirer>\r\n <AcquirerReference>70412:04077382</AcquirerReference>\r\n <AcquirerDate>20170412</AcquirerDate>\r\n <AcquirerTime>214645</AcquirerTime>\r\n <DisplayAmount>R 1.00</DisplayAmount>\r\n <BIN>4</BIN>\r\n <Association>VISA</Association>\r\n <CardType>Unknown CardType</CardType>\r\n <Issuer>Unknown</Issuer>\r\n <Jurisdiction>International</Jurisdiction>\r\n <PANMode>Keyed,CVV</PANMode>\r\n <ReconReference>04077382</ReconReference>\r\n <CardHolderPresence>CardNotPresent</CardHolderPresence>\r\n <MerchantAddress>MERCHANT ADDRESS</MerchantAddress>\r\n <MerchantCity>Sandton</MerchantCity>\r\n <MerchantCountryCode>ZA</MerchantCountryCode>\r\n <MerchantCountry>South Africa</MerchantCountry>\r\n <DistributorName>Nedbank</DistributorName>\r\n <CCNumber>4242........4242</CCNumber>\r\n <PAN>[4242........4242]</PAN>\r\n </Transaction>\r\n</V_XML>"
+read 2377 bytes
+Conn close
+)
+ end
+
+ def post_scrubbed
+ %q(
+opening connection to portal.nedsecure.co.za:443...
+opened
+starting SSL for portal.nedsecure.co.za:443...
+SSL established
+<- "POST /iVeriWebService/Service.asmx HTTP/1.1\r\nContent-Type: text/xml; charset=utf-8\r\nContent-Length: 1016\r\nSoapaction: http://iveri.com/Execute\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: portal.nedsecure.co.za\r\n\r\n"
+<- "\n\n \n \n true\n V_XML\n 2.0\n <V_XML Version=\"2.0\" CertificateID=\"[FILTERED]\" Direction=\"Request\">\n <Transaction ApplicationID=\"D10A603D-4ADE-405B-93F1-826DFC0181E8\" Command=\"Debit\" Mode=\"Test\">\n <Amount>100</Amount>\n <Currency>ZAR</Currency>\n <ExpiryDate>092018</ExpiryDate>\n <MerchantReference>b3ceea8b93d5611cbde7d162baef1245</MerchantReference>\n <CardSecurityCode>[FILTERED]</CardSecurityCode>\n <PAN>[FILTERED]</PAN>\n </Transaction>\n</V_XML>\n \n \n\n"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Cache-Control: private, max-age=0\r\n"
+-> "Content-Type: text/xml; charset=utf-8\r\n"
+-> "Server: Microsoft-IIS/8.0\r\n"
+-> "X-AspNet-Version: 4.0.30319\r\n"
+-> "X-Powered-By: ASP.NET\r\n"
+-> "Date: Wed, 12 Apr 2017 19:46:44 GMT\r\n"
+-> "Connection: close\r\n"
+-> "Content-Length: 2377\r\n"
+-> "\r\n"
+reading 2377 bytes...
+-> "<V_XML Version=\"2.0\" Direction=\"Response\">\r\n <Transaction ApplicationID=\"{D10A603D-4ADE-405B-93F1-826DFC0181E8}\" Command=\"Debit\" Mode=\"Test\" RequestID=\"{5485B5EA-2661-4436-BAA9-CD6DD546FA0D}\">\r\n <Result Status=\"0\" AppServer=\"105IVERIAPPPR02\" DBServer=\"105iveridbpr01\" Gateway=\"Nedbank\" AcquirerCode=\"00\" />\r\n <Amount>100</Amount>\r\n <AuthorisationCode>115205</AuthorisationCode>\r\n <Currency>ZAR</Currency>\r\n <ExpiryDate>092018</ExpiryDate>\r\n <MerchantReference>b3ceea8b93d5611cbde7d162baef1245</MerchantReference>\r\n <Terminal>Default</Terminal>\r\n <TransactionIndex>{10418186-FE90-44F9-AB7A-FEC11C9027F8}</TransactionIndex>\r\n <MerchantName>iVeri Payment Technology</MerchantName>\r\n <MerchantUSN>7771777</MerchantUSN>\r\n <Acquirer>NBPostilionBICISONBSouthAfrica</Acquirer>\r\n <AcquirerReference>70412:04077382</AcquirerReference>\r\n <AcquirerDate>20170412</AcquirerDate>\r\n <AcquirerTime>214645</AcquirerTime>\r\n <DisplayAmount>R 1.00</DisplayAmount>\r\n <BIN>4</BIN>\r\n <Association>VISA</Association>\r\n <CardType>Unknown CardType</CardType>\r\n <Issuer>Unknown</Issuer>\r\n <Jurisdiction>International</Jurisdiction>\r\n <PANMode>Keyed,CVV</PANMode>\r\n <ReconReference>04077382</ReconReference>\r\n <CardHolderPresence>CardNotPresent</CardHolderPresence>\r\n <MerchantAddress>MERCHANT ADDRESS</MerchantAddress>\r\n <MerchantCity>Sandton</MerchantCity>\r\n <MerchantCountryCode>ZA</MerchantCountryCode>\r\n <MerchantCountry>South Africa</MerchantCountry>\r\n <DistributorName>Nedbank</DistributorName>\r\n <CCNumber>4242........4242</CCNumber>\r\n <PAN>[FILTERED]</PAN>\r\n </Transaction>\r\n</V_XML>"
+read 2377 bytes
+Conn close
+)
+ end
+
+ def successful_purchase_response
+ <<-XML
+<V_XML Version="2.0" Direction="Response">
+<Transaction ApplicationID="{D10A603D-4ADE-405B-93F1-826DFC0181E8}" Command="Debit" Mode="Test" RequestID="{F0568958-D10B-4093-A3BF-663168B06140}">
+ <Result Status="0" AppServer="105IVERIAPPPR01" DBServer="105IVERIDBPR01" Gateway="Nedbank" AcquirerCode="00" />
+ <Amount>100</Amount>
+ <AuthorisationCode>537473</AuthorisationCode>
+ <Currency>ZAR</Currency>
+ <ExpiryDate>092018</ExpiryDate>
+ <MerchantReference>48b63446223ce91451fc3c1641a9ec03</MerchantReference>
+ <Terminal>Default</Terminal>
+ <TransactionIndex>{5CEF96FD-960E-4EA5-811F-D02CE6E36A96}</TransactionIndex>
+ <MerchantName>iVeri Payment Technology</MerchantName>
+ <MerchantUSN>7771777</MerchantUSN>
+ <Acquirer>NBPostilionBICISONBSouthAfrica</Acquirer>
+ <AcquirerReference>70417:04077982</AcquirerReference>
+ <AcquirerDate>20170417</AcquirerDate>
+ <AcquirerTime>190433</AcquirerTime>
+ <DisplayAmount>R 1.00</DisplayAmount>
+ <BIN>4</BIN>
+ <Association>VISA</Association>
+ <CardType>Unknown CardType</CardType>
+ <Issuer>Unknown</Issuer>
+ <Jurisdiction>International</Jurisdiction>
+ <PANMode>Keyed,CVV</PANMode>
+ <ReconReference>04077982</ReconReference>
+ <CardHolderPresence>CardNotPresent</CardHolderPresence>
+ <MerchantAddress>MERCHANT ADDRESS</MerchantAddress>
+ <MerchantCity>Sandton</MerchantCity>
+ <MerchantCountryCode>ZA</MerchantCountryCode>
+ <MerchantCountry>South Africa</MerchantCountry>
+ <DistributorName>Nedbank</DistributorName>
+ <CCNumber>4242........4242</CCNumber>
+ <PAN>4242........4242</PAN>
+</Transaction>
+</V_XML>
+ XML
+ end
+
+ def failed_purchase_response
+ <<-XML
+<V_XML Version="2.0" Direction="Response">
+ <Transaction ApplicationID="{D10A603D-4ADE-405B-93F1-826DFC0181E8}" Command="Debit" Mode="Test" RequestID="{B14C3834-72B9-4ACA-B362-B3C9EC96E8C0}">
+ <Result Status="-1" Code="4" Description="Denied" Source="NBPostilionBICISONBSouthAfrica" AppServer="105IVERIAPPPR01" DBServer="105IVERIDBPR01" Gateway="Nedbank" AcquirerCode="05" AcquirerDescription="Do not Honour" />
+ <Amount>100</Amount>
+ <Currency>ZAR</Currency>
+ <ExpiryDate>092018</ExpiryDate>
+ <MerchantReference>435a5d60b5fe874840c34e2e0504626b</MerchantReference>
+ <Terminal>Default</Terminal>
+ <TransactionIndex>{B35872A9-39C7-4DB8-9774-A5E34FFA519E}</TransactionIndex>
+ <MerchantName>iVeri Payment Technology</MerchantName>
+ <MerchantUSN>7771777</MerchantUSN>
+ <Acquirer>NBPostilionBICISONBSouthAfrica</Acquirer>
+ <AcquirerReference>70417:04077988</AcquirerReference>
+ <AcquirerDate>20170417</AcquirerDate>
+ <AcquirerTime>192038</AcquirerTime>
+ <DisplayAmount>R 1.00</DisplayAmount>
+ <BIN>2</BIN>
+ <Association>Unknown Association</Association>
+ <CardType>Unknown CardType</CardType>
+ <Issuer>Unknown</Issuer>
+ <Jurisdiction>Local</Jurisdiction>
+ <PANMode>Keyed,CVV</PANMode>
+ <ReconReference>04077988</ReconReference>
+ <CardHolderPresence>CardNotPresent</CardHolderPresence>
+ <MerchantAddress>MERCHANT ADDRESS</MerchantAddress>
+ <MerchantCity>Sandton</MerchantCity>
+ <MerchantCountryCode>ZA</MerchantCountryCode>
+ <MerchantCountry>South Africa</MerchantCountry>
+ <DistributorName>Nedbank</DistributorName>
+ <CCNumber>2121........2121</CCNumber>
+ <PAN>2121........2121</PAN>
+ </Transaction>
+</V_XML>
+ XML
+ end
+
+ def successful_authorize_response
+ <<-XML
+<V_XML Version="2.0" Direction="Response">
+ <Transaction ApplicationID="{D10A603D-4ADE-405B-93F1-826DFC0181E8}" Command="Authorisation" Mode="Test" RequestID="{B90D7CDB-C8E8-4477-BDF2-695F28137874}">
+ <Result Status="0" AppServer="105IVERIAPPPR01" DBServer="105IVERIDBPR01" Gateway="Nedbank" AcquirerCode="00" />
+ <Amount>100</Amount>
+ <AuthorisationCode>541267</AuthorisationCode>
+ <Currency>ZAR</Currency>
+ <ExpiryDate>092018</ExpiryDate>
+ <MerchantReference>23b4125c3b8e2777bffee52e196a863b</MerchantReference>
+ <Terminal>Default</Terminal>
+ <TransactionIndex>{EF0DC64E-2D00-4B6C-BDA0-2AD265391317}</TransactionIndex>
+ <MerchantName>iVeri Payment Technology</MerchantName>
+ <MerchantUSN>7771777</MerchantUSN>
+ <Acquirer>NBPostilionBICISONBSouthAfrica</Acquirer>
+ <AcquirerReference>70417:04078057</AcquirerReference>
+ <AcquirerDate>20170417</AcquirerDate>
+ <AcquirerTime>200747</AcquirerTime>
+ <DisplayAmount>R 1.00</DisplayAmount>
+ <BIN>4</BIN>
+ <Association>VISA</Association>
+ <CardType>Unknown CardType</CardType>
+ <Issuer>Unknown</Issuer>
+ <Jurisdiction>International</Jurisdiction>
+ <PANMode>Keyed,CVV</PANMode>
+ <ReconReference>04078057</ReconReference>
+ <CardHolderPresence>CardNotPresent</CardHolderPresence>
+ <MerchantAddress>MERCHANT ADDRESS</MerchantAddress>
+ <MerchantCity>Sandton</MerchantCity>
+ <MerchantCountryCode>ZA</MerchantCountryCode>
+ <MerchantCountry>South Africa</MerchantCountry>
+ <DistributorName>Nedbank</DistributorName>
+ <CCNumber>4242........4242</CCNumber>
+ <PAN>4242........4242</PAN>
+ </Transaction>
+</V_XML>
+ XML
+ end
+
+ def failed_authorize_response
+ <<-XML
+<V_XML Version="2.0" Direction="Response">
+ <Transaction ApplicationID="{D10A603D-4ADE-405B-93F1-826DFC0181E8}" Command="Authorisation" Mode="Test" RequestID="{3A1A29BE-288F-4FEE-8C15-B3BB8A207544}">
+ <Result Status="-1" Code="4" Description="Denied" Source="NBPostilionBICISONBSouthAfrica" AppServer="105IVERIAPPPR01" DBServer="105IVERIDBPR01" Gateway="Nedbank" AcquirerCode="05" AcquirerDescription="Do not Honour" />
+ <Amount>100</Amount>
+ <Currency>ZAR</Currency>
+ <ExpiryDate>092018</ExpiryDate>
+ <MerchantReference>3d12442ea042e78fd33057b7b50c76f7</MerchantReference>
+ <Terminal>Default</Terminal>
+ <TransactionIndex>{8AC33FB1-0D2E-42C7-A0DB-CF8B20279825}</TransactionIndex>
+ <MerchantName>iVeri Payment Technology</MerchantName>
+ <MerchantUSN>7771777</MerchantUSN>
+ <Acquirer>NBPostilionBICISONBSouthAfrica</Acquirer>
+ <AcquirerReference>70417:04078062</AcquirerReference>
+ <AcquirerDate>20170417</AcquirerDate>
+ <AcquirerTime>202648</AcquirerTime>
+ <DisplayAmount>R 1.00</DisplayAmount>
+ <BIN>2</BIN>
+ <Association>Unknown Association</Association>
+ <CardType>Unknown CardType</CardType>
+ <Issuer>Unknown</Issuer>
+ <Jurisdiction>Local</Jurisdiction>
+ <PANMode>Keyed,CVV</PANMode>
+ <ReconReference>04078062</ReconReference>
+ <CardHolderPresence>CardNotPresent</CardHolderPresence>
+ <MerchantAddress>MERCHANT ADDRESS</MerchantAddress>
+ <MerchantCity>Sandton</MerchantCity>
+ <MerchantCountryCode>ZA</MerchantCountryCode>
+ <MerchantCountry>South Africa</MerchantCountry>
+ <DistributorName>Nedbank</DistributorName>
+ <CCNumber>2121........2121</CCNumber>
+ <PAN>2121........2121</PAN>
+ </Transaction>
+</V_XML>
+ XML
+ end
+
+ def successful_capture_response
+ <<-XML
+<V_XML Version="2.0" Direction="Response">
+ <Transaction ApplicationID="{D10A603D-4ADE-405B-93F1-826DFC0181E8}" Command="Debit" Mode="Test" RequestID="{7C91245F-607D-44AE-8958-C26E447BAEB7}">
+ <Result Status="0" AppServer="105IVERIAPPPR02" DBServer="105iveridbpr01" Gateway="Nedbank" AcquirerCode="00" />
+ <Amount>100</Amount>
+ <AuthorisationCode>541268</AuthorisationCode>
+ <Currency>ZAR</Currency>
+ <ExpiryDate>092018</ExpiryDate>
+ <MerchantReference>23b4125c3b8e2777bffee52e196a863b</MerchantReference>
+ <Terminal>Default</Terminal>
+ <TransactionIndex>{EF0DC64E-2D00-4B6C-BDA0-2AD265391317}</TransactionIndex>
+ <MerchantName>iVeri Payment Technology</MerchantName>
+ <MerchantUSN>7771777</MerchantUSN>
+ <Acquirer>NBPostilionBICISONBSouthAfrica</Acquirer>
+ <AcquirerReference>70417:04078057</AcquirerReference>
+ <AcquirerDate>20170417</AcquirerDate>
+ <AcquirerTime>200748</AcquirerTime>
+ <DisplayAmount>R 1.00</DisplayAmount>
+ <BIN>4</BIN>
+ <Association>VISA</Association>
+ <CardType>Unknown CardType</CardType>
+ <Issuer>Unknown</Issuer>
+ <Jurisdiction>International</Jurisdiction>
+ <PANMode>Keyed,CVV</PANMode>
+ <ReconReference>04078057</ReconReference>
+ <CardHolderPresence>CardNotPresent</CardHolderPresence>
+ <MerchantAddress>MERCHANT ADDRESS</MerchantAddress>
+ <MerchantCity>Sandton</MerchantCity>
+ <MerchantCountryCode>ZA</MerchantCountryCode>
+ <MerchantCountry>South Africa</MerchantCountry>
+ <DistributorName>Nedbank</DistributorName>
+ <CCNumber>4242........4242</CCNumber>
+ <PAN>4242........4242</PAN>
+ </Transaction>
+</V_XML>
+ XML
+ end
+
+ def failed_capture_response
+ <<-XML
+<V_XML Version="2.0" Direction="Response">
+ <Transaction ApplicationID="{D10A603D-4ADE-405B-93F1-826DFC0181E8}" Command="Debit" Mode="Test" RequestID="{9DAAA002-0EF9-46DC-A440-8DCD9E78B36F}">
+ <Result Status="-1" Code="14" Description="Missing PAN" Source="NBPostilionBICISONBSouthAfricaTestProvider" AppServer="105IVERIAPPPR02" DBServer="105iveridbpr01" Gateway="Nedbank" AcquirerCode="" AcquirerDescription="" />
+ </Transaction>
+</V_XML>
+ XML
+ end
+
+ def successful_refund_response
+ <<-XML
+<V_XML Version="2.0" Direction="Response">
+ <Transaction ApplicationID="{D10A603D-4ADE-405B-93F1-826DFC0181E8}" Command="Credit" Mode="Test" RequestID="{097C55B5-D020-40AD-8949-F9F5E4102F1D}">
+ <Result Status="0" AppServer="105IVERIAPPPR02" DBServer="105iveridbpr01" Gateway="Nedbank" AcquirerCode="00" />
+ <Amount>100</Amount>
+ <AuthorisationCode>541996</AuthorisationCode>
+ <Currency>ZAR</Currency>
+ <ExpiryDate>092018</ExpiryDate>
+ <MerchantReference>5be2c040bd46b7eebc70274659779acf</MerchantReference>
+ <Terminal>Default</Terminal>
+ <TransactionIndex>{D50DB1B4-B6EC-4AF1-AFF7-71C2AA4A957B}</TransactionIndex>
+ <MerchantName>iVeri Payment Technology</MerchantName>
+ <MerchantUSN>7771777</MerchantUSN>
+ <Acquirer>NBPostilionBICISONBSouthAfrica</Acquirer>
+ <AcquirerReference>70417:04078059</AcquirerReference>
+ <AcquirerDate>20170417</AcquirerDate>
+ <AcquirerTime>201956</AcquirerTime>
+ <DisplayAmount>R 1.00</DisplayAmount>
+ <BIN>4</BIN>
+ <Association>VISA</Association>
+ <CardType>Unknown CardType</CardType>
+ <Issuer>Unknown</Issuer>
+ <Jurisdiction>International</Jurisdiction>
+ <PANMode />
+ <ReconReference>04078059</ReconReference>
+ <CardHolderPresence>CardNotPresent</CardHolderPresence>
+ <MerchantAddress>MERCHANT ADDRESS</MerchantAddress>
+ <MerchantCity>Sandton</MerchantCity>
+ <MerchantCountryCode>ZA</MerchantCountryCode>
+ <MerchantCountry>South Africa</MerchantCountry>
+ <DistributorName>Nedbank</DistributorName>
+ <CCNumber>4242........4242</CCNumber>
+ <PAN>4242........4242</PAN>
+ </Transaction>
+</V_XML>
+ XML
+ end
+
+ def failed_refund_response
+ <<-XML
+<V_XML Version="2.0" Direction="Response">
+ <Transaction ApplicationID="{D10A603D-4ADE-405B-93F1-826DFC0181E8}" Command="Credit" Mode="Test" RequestID="{5097A60A-A112-42F1-9490-FA17A859E7A3}">
+ <Result Status="-1" Code="255" Description="Credit is not supported for ApplicationID (D10A603D-4ADE-405B-93F1-826DFC0181E8)" Source="PortalService" AppServer="105IVERIAPPPR02" DBServer="105iveridbpr01" Gateway="Nedbank" AcquirerCode="" AcquirerDescription="" />
+ </Transaction>
+</V_XML>
+ XML
+ end
+
+ def successful_void_response
+ <<-XML
+<V_XML Version="2.0" Direction="Response">
+ <Transaction ApplicationID="{D10A603D-4ADE-405B-93F1-826DFC0181E8}" Command="Void" Mode="Test" RequestID="{0A1A3FFF-C2A3-4B91-85FD-10D1C25B765B}">
+ <Result Status="0" AppServer="105IVERIAPPPR02" DBServer="105iveridbpr01" Gateway="Nedbank" />
+ <OriginalRequestID>{230390C8-4A9E-4426-BDD3-15D072F135FE}</OriginalRequestID>
+ </Transaction>
+</V_XML>
+ XML
+ end
+
+ def failed_void_response
+ <<-XML
+<V_XML Version="2.0" Direction="Response">
+ <Transaction ApplicationID="{D10A603D-4ADE-405B-93F1-826DFC0181E8}" Command="Void" Mode="Test" RequestID="{AE97CCE4-0631-4F08-AB47-9C2698ABEC75}">
+ <Result Status="-1" Code="255" Description="Missing OriginalMerchantTrace" Source="NBPostilionBICISONBSouthAfricaTestProvider" AppServer="105IVERIAPPPR01" DBServer="105IVERIDBPR01" Gateway="Nedbank" AcquirerCode="" AcquirerDescription="" />
+ </Transaction>
+</V_XML>
+ XML
+ end
+
+ def successful_verify_response
+ <<-XML
+<V_XML Version="2.0" Direction="Response">
+ <Transaction ApplicationID="{D10A603D-4ADE-405B-93F1-826DFC0181E8}" Command="Authorisation" Mode="Test" RequestID="{F4337D04-B526-4A7E-A400-2A6DEADDCF57}">
+ <Result Status="0" AppServer="105IVERIAPPPR01" DBServer="105IVERIDBPR01" Gateway="Nedbank" AcquirerCode="00" />
+ <Amount>0</Amount>
+ <AuthorisationCode>613755</AuthorisationCode>
+ <Currency>ZAR</Currency>
+ <ExpiryDate>092018</ExpiryDate>
+ <MerchantReference>c0006d1d739905afc9e70beaf4194ea3</MerchantReference>
+ <Terminal>Default</Terminal>
+ <TransactionIndex>{5D5F8BF7-2D9D-42C3-AF32-08C5E62CD45E}</TransactionIndex>
+ <MerchantName>iVeri Payment Technology</MerchantName>
+ <MerchantUSN>7771777</MerchantUSN>
+ <Acquirer>NBPostilionBICISONBSouthAfrica</Acquirer>
+ <AcquirerReference>70418:04078335</AcquirerReference>
+ <AcquirerDate>20170418</AcquirerDate>
+ <AcquirerTime>161555</AcquirerTime>
+ <DisplayAmount>R 0.00</DisplayAmount>
+ <BIN>4</BIN>
+ <Association>VISA</Association>
+ <CardType>Unknown CardType</CardType>
+ <Issuer>Unknown</Issuer>
+ <Jurisdiction>International</Jurisdiction>
+ <PANMode>Keyed,CVV</PANMode>
+ <ReconReference>04078335</ReconReference>
+ <CardHolderPresence>CardNotPresent</CardHolderPresence>
+ <MerchantAddress>MERCHANT ADDRESS</MerchantAddress>
+ <MerchantCity>Sandton</MerchantCity>
+ <MerchantCountryCode>ZA</MerchantCountryCode>
+ <MerchantCountry>South Africa</MerchantCountry>
+ <DistributorName>Nedbank</DistributorName>
+ <CCNumber>4242........4242</CCNumber>
+ <PAN>4242........4242</PAN>
+ </Transaction>
+</V_XML>
+ XML
+ end
+
+ def failed_verify_response
+ <<-XML
+<V_XML Version="2.0" Direction="Response">
+ <Transaction ApplicationID="{D10A603D-4ADE-405B-93F1-826DFC0181E8}" Command="Authorisation" Mode="Test" RequestID="{A700FAE2-2A76-407D-A540-B41668E2B703}">
+ <Result Status="-1" Code="4" Description="Denied" Source="NBPostilionBICISONBSouthAfrica" AppServer="105IVERIAPPPR02" DBServer="105iveridbpr01" Gateway="Nedbank" AcquirerCode="05" AcquirerDescription="Do not Honour" />
+ <Amount>0</Amount>
+ <Currency>ZAR</Currency>
+ <ExpiryDate>092018</ExpiryDate>
+ <MerchantReference>e955afb03f224284b09ad6ae7e9b4683</MerchantReference>
+ <Terminal>Default</Terminal>
+ <TransactionIndex>{2A378547-AEA4-48E1-8A3E-29F9BBEA954D}</TransactionIndex>
+ <MerchantName>iVeri Payment Technology</MerchantName>
+ <MerchantUSN>7771777</MerchantUSN>
+ <Acquirer>NBPostilionBICISONBSouthAfrica</Acquirer>
+ <AcquirerReference>70418:04078337</AcquirerReference>
+ <AcquirerDate>20170418</AcquirerDate>
+ <AcquirerTime>161716</AcquirerTime>
+ <DisplayAmount>R 0.00</DisplayAmount>
+ <BIN>2</BIN>
+ <Association>Unknown Association</Association>
+ <CardType>Unknown CardType</CardType>
+ <Issuer>Unknown</Issuer>
+ <Jurisdiction>Local</Jurisdiction>
+ <PANMode>Keyed,CVV</PANMode>
+ <ReconReference>04078337</ReconReference>
+ <CardHolderPresence>CardNotPresent</CardHolderPresence>
+ <MerchantAddress>MERCHANT ADDRESS</MerchantAddress>
+ <MerchantCity>Sandton</MerchantCity>
+ <MerchantCountryCode>ZA</MerchantCountryCode>
+ <MerchantCountry>South Africa</MerchantCountry>
+ <DistributorName>Nedbank</DistributorName>
+ <CCNumber>2121........2121</CCNumber>
+ <PAN>2121........2121</PAN>
+ </Transaction>
+</V_XML>
+ XML
+ end
+
+ def successful_verify_credentials_response
+ <<-XML
+<V_XML Version="2.0" Direction="Response">
+ <Transaction ApplicationID="{D10A603D-4ADE-405B-93F1-826DFC0181E8}" Command="Void" Mode="Test" RequestID="{5ED922D0-92AD-40DF-9019-320591A4BA59}">
+ <Result Status="-1" Code="255" Description="Missing OriginalMerchantTrace" Source="NBPostilionBICISONBSouthAfricaTestProvider" AppServer="105IVERIAPPPR01" DBServer="105IVERIDBPR01" Gateway="Nedbank" AcquirerCode="" AcquirerDescription="" />
+ </Transaction>
+</V_XML>
+ XML
+ end
+
+ def failed_verify_credentials_response
+ <<-XML
+<V_XML Version="2.0" Direction="Response">
+ <Result Status="-1" Code="255" Description="The ApplicationID {11111111-1111-1111-1111-111111111111} is not valid for the current CertificateID {11111111-1111-1111-1111-111111111111}" Source="RequestHandler" RequestID="{EE6E5B39-63AD-402C-8331-F25082AD8564}" AppServer="105IVERIAPPPR01" DBServer="105IVERIDBPR01" Gateway="Nedbank" AcquirerCode="" AcquirerDescription="" />
+</V_XML>
+ XML
+ end
+end
diff --git a/test/unit/gateways/jetpay_v2_test.rb b/test/unit/gateways/jetpay_v2_test.rb
new file mode 100644
index 00000000000..b1250dbdc55
--- /dev/null
+++ b/test/unit/gateways/jetpay_v2_test.rb
@@ -0,0 +1,308 @@
+require 'test_helper'
+
+class JetpayV2Test < Test::Unit::TestCase
+
+ def setup
+ @gateway = JetpayV2Gateway.new(:login => 'login')
+
+ @credit_card = credit_card
+ @amount = 100
+
+ @options = {
+ :device => 'spreedly',
+ :application => 'spreedly',
+ :developer_id => 'GenkID',
+ :billing_address => address(:country => 'US'),
+ :shipping_address => address(:country => 'US'),
+ :email => 'test@test.com',
+ :ip => '127.0.0.1',
+ :order_id => '12345'
+ }
+ end
+
+ def test_successful_purchase
+ @gateway.expects(:ssl_post).returns(successful_purchase_response)
+
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal '8afa688fd002821362;TEST97;100;KKLIHOJKKNKKHJKONJHOLHOL', response.authorization
+ assert_equal('TEST97', response.params["approval"])
+ assert response.test?
+ end
+
+ def test_failed_purchase
+ @gateway.expects(:ssl_post).returns(failed_purchase_response)
+
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal('7605f7c5d6e8f74deb;;100;', 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_success response
+
+ assert_equal('cbf902091334a0b1aa;TEST01;100;KKLIHOJKKNKKHJKONOHCLOIO', response.authorization)
+ assert_equal('TEST01', response.params["approval"])
+ assert response.test?
+ end
+
+ def test_successful_capture
+ @gateway.expects(:ssl_post).returns(successful_capture_response)
+
+ assert response = @gateway.capture(1111, "010327153017T10018;502F7B;1111", @options)
+ assert_success response
+
+ assert_equal('010327153017T10018;502F6B;1111;', response.authorization)
+ assert_equal('502F6B', response.params["approval"])
+ assert response.test?
+ end
+
+ def test_failed_capture
+ @gateway.expects(:ssl_post).returns(failed_capture_response)
+
+ assert response = @gateway.capture(@amount, '7605f7c5d6e8f74deb', @options)
+ assert_failure response
+ assert_equal 'Transaction Not Found.', response.message
+ end
+
+ def test_successful_void
+ @gateway.expects(:ssl_post).returns(successful_void_response)
+
+ assert response = @gateway.void('010327153x17T10418;502F7B;500', @options)
+ assert_success response
+
+ assert_equal('010327153x17T10418;502F7B;500;', response.authorization)
+ assert_equal('502F7B', response.params["approval"])
+ assert response.test?
+ end
+
+ def test_failed_void
+ @gateway.expects(:ssl_post).returns(failed_void_response)
+
+ assert void = @gateway.void('bogus', @options)
+ assert_failure void
+ end
+
+ def test_successful_credit
+ card = credit_card('4242424242424242', :verification_value => nil)
+
+ @gateway.expects(:ssl_post).returns(successful_credit_response)
+
+ assert response = @gateway.credit(@amount, card, @options)
+ assert_success response
+ end
+
+ def test_failed_credit
+ card = credit_card('2424242424242424', :verification_value => nil)
+
+ @gateway.expects(:ssl_post).returns(failed_credit_response)
+
+ assert credit = @gateway.credit(@amount, card, @options)
+ assert_failure credit
+ assert_match %r{Invalid card format}, credit.message
+ end
+
+ def test_successful_refund
+ @gateway.expects(:ssl_post).returns(successful_credit_response)
+
+ assert response = @gateway.refund(9900, '010327153017T10017', @options)
+ assert_success response
+
+ assert_equal('010327153017T10017;002F6B;9900;', response.authorization)
+ assert_equal('002F6B', response.params['approval'])
+ assert response.test?
+ end
+
+ def test_failed_refund
+ @gateway.expects(:ssl_post).returns(failed_void_response)
+
+ assert refund = @gateway.refund(@amount, 'bogus', @options)
+ assert_failure refund
+ end
+
+ def test_successful_verify
+ @gateway.expects(:ssl_post).returns(successful_authorize_response)
+
+ assert verify = @gateway.verify(@credit_card, @options)
+ assert_success verify
+ end
+
+ def test_failed_verify
+ card = credit_card('2424242424242424', :verification_value => nil)
+
+ @gateway.expects(:ssl_post).returns(failed_credit_response)
+
+ assert verify = @gateway.verify(card, @options)
+ assert_failure verify
+ assert_match %r{Invalid card format}, verify.message
+ end
+
+ def test_avs_result
+ @gateway.expects(:ssl_post).returns(successful_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_equal 'D', response.avs_result['code']
+ end
+
+ def test_cvv_result
+ @gateway.expects(:ssl_post).returns(successful_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_equal 'P', response.cvv_result['code']
+ end
+
+ def test_transcript_scrubbing
+ assert @gateway.supports_scrubbing
+ assert_equal scrubbed_transcript, @gateway.scrub(transcript)
+ end
+
+ def test_purchase_sends_additional_options
+ @gateway.expects(:ssl_post).
+ with(anything, regexp_matches(/777<\/TaxAmount>/)).
+ with(anything, regexp_matches(/Value1<\/UDField1>/)).
+ with(anything, regexp_matches(/Value2<\/UDField2>/)).
+ with(anything, regexp_matches(/Value3<\/UDField3>/)).
+ returns(successful_purchase_response)
+
+ @gateway.purchase(@amount, @credit_card, {:tax => '777', :ud_field_1 => 'Value1', :ud_field_2 => 'Value2', :ud_field_3 => 'Value3'})
+ end
+
+ private
+
+ def successful_purchase_response
+ <<-EOF
+
+ 8afa688fd002821362
+ 000
+ TEST97
+ P
+ APPROVED
+ KKLIHOJKKNKKHJKONJHOLHOL
+ Y
+ Y
+ D
+
+ EOF
+ end
+
+ def failed_purchase_response
+ <<-EOF
+
+ 7605f7c5d6e8f74deb
+ 005
+ DECLINED
+
+ EOF
+ end
+
+ def successful_authorize_response
+ <<-EOF
+
+ cbf902091334a0b1aa
+ 000
+ TEST01
+ P
+ APPROVED
+ KKLIHOJKKNKKHJKONOHCLOIO
+ Y
+ Y
+ D
+
+ EOF
+ end
+
+ def successful_capture_response
+ <<-EOF
+
+ 010327153017T10018
+ 000
+ 502F6B
+ APPROVED
+
+ EOF
+ end
+
+ def failed_capture_response
+ <<-EOF
+
+ 010327153017T10018
+ 025
+ REJECT
+ ED
+
+ EOF
+ end
+
+ def successful_void_response
+ <<-EOF
+
+ 010327153x17T10418
+ 000
+ 502F7B
+ VOID PROCESSED
+
+ EOF
+ end
+
+ def failed_void_response
+ <<-EOF
+
+ 010327153x17T10418
+ 900
+ INVALID MESSAGE TYPE
+
+ EOF
+ end
+
+ def successful_credit_response
+ <<-EOF
+
+ 010327153017T10017
+ 000
+ 002F6B
+ APPROVED
+
+ EOF
+ end
+
+ def failed_credit_response
+ <<-EOF
+
+ 010327153017T10017
+ 912
+ INVALID CARD NUMBER
+
+ EOF
+ end
+
+ def transcript
+ <<-EOF
+ TESTMCC3136X
+ SALE
+ e23c963a1247fd7aad
+ 4000300020001000
+ 09
+ 16
+ Longbob Longsen
+ 123
+ EOF
+ end
+
+ def scrubbed_transcript
+ <<-EOF
+ TESTMCC3136X
+ SALE
+ e23c963a1247fd7aad
+ [FILTERED]
+ 09
+ 16
+ Longbob Longsen
+ [FILTERED]
+ EOF
+ end
+end
diff --git a/test/unit/gateways/kushki_test.rb b/test/unit/gateways/kushki_test.rb
new file mode 100644
index 00000000000..5aeb1a8af9c
--- /dev/null
+++ b/test/unit/gateways/kushki_test.rb
@@ -0,0 +1,281 @@
+require 'test_helper'
+
+class KushkiTest < Test::Unit::TestCase
+ def setup
+ @gateway = KushkiGateway.new(public_merchant_id: '_', private_merchant_id: '_')
+ @amount = 100
+ @credit_card = credit_card
+ end
+
+ def test_successful_purchase
+ @gateway.expects(:ssl_post).returns(successful_charge_response)
+ @gateway.expects(:ssl_post).returns(successful_token_response)
+
+ response = @gateway.purchase(@amount, @credit_card)
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ assert_match %r(^\d+$), response.authorization
+ assert response.test?
+ end
+
+ def test_successful_purchase_with_options
+ options = {
+ currency: "USD",
+ amount: {
+ subtotal_iva_0: "4.95",
+ subtotal_iva: "10",
+ iva: "1.54",
+ ice: "3.50"
+ }
+ }
+
+ amount = 100 * (
+ options[:amount][:subtotal_iva_0].to_f +
+ options[:amount][:subtotal_iva].to_f +
+ options[:amount][:iva].to_f +
+ options[:amount][:ice].to_f
+ )
+
+ @gateway.expects(:ssl_post).returns(successful_charge_response)
+ @gateway.expects(:ssl_post).returns(successful_token_response)
+
+ response = @gateway.purchase(amount, @credit_card, options)
+ assert_success response
+ assert_equal 'Succeeded', response.message
+ assert_match %r(^\d+$), response.authorization
+ assert response.test?
+ end
+
+ def test_failed_purchase
+ options = {
+ amount: {
+ subtotal_iva: "200"
+ }
+ }
+
+ @gateway.expects(:ssl_post).returns(failed_charge_response)
+ @gateway.expects(:ssl_post).returns(successful_token_response)
+
+ response = @gateway.purchase(@amount, @credit_card, options)
+ assert_failure response
+ assert_equal 'Monto de la transacción es diferente al monto de la venta inicial', response.message
+ assert_equal '220', response.error_code
+ end
+
+ def test_successful_refund
+ @gateway.expects(:ssl_post).returns(successful_charge_response)
+ @gateway.expects(:ssl_post).returns(successful_token_response)
+
+ purchase = @gateway.purchase(@amount, @credit_card)
+ assert_success purchase
+
+ @gateway.expects(:ssl_request).returns(successful_refund_response)
+
+ assert refund = @gateway.refund(@amount, purchase.authorization)
+ assert_success refund
+ assert_equal 'Succeeded', refund.message
+ end
+
+ def test_failed_refund
+ @gateway.expects(:ssl_post).returns(successful_charge_response)
+ @gateway.expects(:ssl_post).returns(successful_token_response)
+
+ purchase = @gateway.purchase(@amount, @credit_card)
+ assert_success purchase
+
+ @gateway.expects(:ssl_request).returns(failed_refund_response)
+
+ assert refund = @gateway.refund(@amount, purchase.authorization)
+ assert_failure refund
+ assert_equal 'Ticket number inválido', refund.message
+ assert_equal 'K010', refund.error_code
+ end
+
+ def test_successful_void
+ @gateway.expects(:ssl_post).returns(successful_charge_response)
+ @gateway.expects(:ssl_post).returns(successful_token_response)
+
+ purchase = @gateway.purchase(@amount, @credit_card)
+ assert_success purchase
+
+ @gateway.expects(:ssl_request).returns(successful_void_response)
+
+ assert void = @gateway.void(purchase.authorization)
+ assert_success void
+ assert_equal 'Succeeded', void.message
+ end
+
+ def test_failed_void
+ @gateway.expects(:ssl_request).returns(failed_void_response)
+
+ response = @gateway.void("000")
+ assert_failure response
+ assert_equal 'Tipo de moneda no válida', response.message
+ assert_equal '205', response.error_code
+ end
+
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
+ private
+
+ def pre_scrubbed
+ %q(
+ opening connection to api-uat.kushkipagos.com:443...
+ opened
+ starting SSL for api-uat.kushkipagos.com:443...
+ SSL established
+ <- "POST /v1/tokens HTTP/1.1\r\nContent-Type: application/json\r\nPublic-Merchant-Id: 10000001837148605646147925549896\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api-uat.kushkipagos.com\r\nContent-Length: 166\r\n\r\n"
+ <- "{\"totalAmount\":1.0,\"currency\":\"USD\",\"isDeferred\":false,\"card\":{\"number\":\"4000100011112224\",\"name\":\"Longbob Longsen\",\"cvv\":\"777\",\"expiryMonth\":\"09\",\"expiryYear\":\"18\"}}"
+ -> "HTTP/1.1 201 Created\r\n"
+ -> "Date: Tue, 07 Feb 2017 14:53:00 GMT\r\n"
+ -> "Server: Apache/2.4.18 (Unix) OpenSSL/1.0.1s Resin/4.0.40\r\n"
+ -> "Access-Control-Allow-Origin: *\r\n"
+ -> "Access-Control-Allow-Methods: OPTIONS\r\n"
+ -> "Access-Control-Max-Age: 1000\r\n"
+ -> "Access-Control-Allow-Headers: x-requested-with, Content-Type, origin, authorization, accept, client-security-token\r\n"
+ -> "Content-Length: 44\r\n"
+ -> "Content-Type: application/json\r\n"
+ -> "Connection: close\r\n"
+ -> "\r\n"
+ reading 44 bytes...
+ -> ""
+ -> "{\"token\":\"BeWb3z100000UXANj0018371b8iPZHYq\"}"
+ read 44 bytes
+ Conn close
+ opening connection to api-uat.kushkipagos.com:443...
+ opened
+ starting SSL for api-uat.kushkipagos.com:443...
+ SSL established
+ <- "POST /v1/charges HTTP/1.1\r\nContent-Type: application/json\r\nPrivate-Merchant-Id: 10000001837138390991147925549896\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api-uat.kushkipagos.com\r\nContent-Length: 123\r\n\r\n"
+ <- "{\"token\":\"BeWb3z100000UXANj0018371b8iPZHYq\",\"amount\":{\"currency\":\"USD\",\"subtotalIva\":1.0,\"iva\":0,\"subtotalIva0\":0,\"ice\":0}}"
+ -> "HTTP/1.1 201 Created\r\n"
+ -> "Date: Tue, 07 Feb 2017 14:53:02 GMT\r\n"
+ -> "Server: Apache/2.4.18 (Unix) OpenSSL/1.0.1s Resin/4.0.40\r\n"
+ -> "Access-Control-Allow-Origin: *\r\n"
+ -> "Access-Control-Allow-Methods: OPTIONS\r\n"
+ -> "Access-Control-Max-Age: 1000\r\n"
+ -> "Access-Control-Allow-Headers: x-requested-with, Content-Type, origin, authorization, accept, client-security-token\r\n"
+ -> "Content-Length: 37\r\n"
+ -> "Content-Type: application/json\r\n"
+ -> "Connection: close\r\n"
+ -> "\r\n"
+ reading 37 bytes...
+ -> ""
+ -> "{\"ticketNumber\":\"170383559069100036\"}"
+ read 37 bytes
+ Conn close
+ )
+ end
+
+ def post_scrubbed
+ %q(
+ opening connection to api-uat.kushkipagos.com:443...
+ opened
+ starting SSL for api-uat.kushkipagos.com:443...
+ SSL established
+ <- "POST /v1/tokens HTTP/1.1\r\nContent-Type: application/json\r\nPublic-Merchant-Id: 10000001837148605646147925549896\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api-uat.kushkipagos.com\r\nContent-Length: 166\r\n\r\n"
+ <- "{\"totalAmount\":1.0,\"currency\":\"USD\",\"isDeferred\":false,\"card\":{\"number\":\"[FILTERED]\",\"name\":\"Longbob Longsen\",\"cvv\":\"[FILTERED]\",\"expiryMonth\":\"09\",\"expiryYear\":\"18\"}}"
+ -> "HTTP/1.1 201 Created\r\n"
+ -> "Date: Tue, 07 Feb 2017 14:53:00 GMT\r\n"
+ -> "Server: Apache/2.4.18 (Unix) OpenSSL/1.0.1s Resin/4.0.40\r\n"
+ -> "Access-Control-Allow-Origin: *\r\n"
+ -> "Access-Control-Allow-Methods: OPTIONS\r\n"
+ -> "Access-Control-Max-Age: 1000\r\n"
+ -> "Access-Control-Allow-Headers: x-requested-with, Content-Type, origin, authorization, accept, client-security-token\r\n"
+ -> "Content-Length: 44\r\n"
+ -> "Content-Type: application/json\r\n"
+ -> "Connection: close\r\n"
+ -> "\r\n"
+ reading 44 bytes...
+ -> ""
+ -> "{\"token\":\"BeWb3z100000UXANj0018371b8iPZHYq\"}"
+ read 44 bytes
+ Conn close
+ opening connection to api-uat.kushkipagos.com:443...
+ opened
+ starting SSL for api-uat.kushkipagos.com:443...
+ SSL established
+ <- "POST /v1/charges HTTP/1.1\r\nContent-Type: application/json\r\nPrivate-Merchant-Id: [FILTERED]\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api-uat.kushkipagos.com\r\nContent-Length: 123\r\n\r\n"
+ <- "{\"token\":\"BeWb3z100000UXANj0018371b8iPZHYq\",\"amount\":{\"currency\":\"USD\",\"subtotalIva\":1.0,\"iva\":0,\"subtotalIva0\":0,\"ice\":0}}"
+ -> "HTTP/1.1 201 Created\r\n"
+ -> "Date: Tue, 07 Feb 2017 14:53:02 GMT\r\n"
+ -> "Server: Apache/2.4.18 (Unix) OpenSSL/1.0.1s Resin/4.0.40\r\n"
+ -> "Access-Control-Allow-Origin: *\r\n"
+ -> "Access-Control-Allow-Methods: OPTIONS\r\n"
+ -> "Access-Control-Max-Age: 1000\r\n"
+ -> "Access-Control-Allow-Headers: x-requested-with, Content-Type, origin, authorization, accept, client-security-token\r\n"
+ -> "Content-Length: 37\r\n"
+ -> "Content-Type: application/json\r\n"
+ -> "Connection: close\r\n"
+ -> "\r\n"
+ reading 37 bytes...
+ -> ""
+ -> "{\"ticketNumber\":\"170383559069100036\"}"
+ read 37 bytes
+ Conn close
+ )
+ end
+
+ def successful_token_response
+ %(
+ {
+ "token":"Rcp7Un10000070Jwa5018371WVtD0ECx"
+ }
+ )
+ end
+
+ def successful_charge_response
+ %(
+ {
+ "ticketNumber":"170384522771700083"
+ }
+ )
+ end
+
+ def failed_charge_response
+ %(
+ {
+ "code":"220",
+ "message":"Monto de la transacción es diferente al monto de la venta inicial"
+ }
+ )
+ end
+
+ def successful_refund_response
+ %(
+ {
+ "code": "K000",
+ "message": "El reembolso solicitado se realizó con éxito."
+ }
+ )
+ end
+
+ def failed_refund_response
+ %(
+ {
+ "code": "K010",
+ "message": "Ticket number inválido"
+ }
+ )
+ end
+
+ def successful_void_response
+ %(
+ {
+ "ticketNumber":"170384634023500095"
+ }
+ )
+ end
+
+ def failed_void_response
+ %(
+ {
+ "code":"205",
+ "message":"Tipo de moneda no válida"
+ }
+ )
+ end
+end
diff --git a/test/unit/gateways/linkpoint_test.rb b/test/unit/gateways/linkpoint_test.rb
index 07ab0971ca5..48312215e0a 100644
--- a/test/unit/gateways/linkpoint_test.rb
+++ b/test/unit/gateways/linkpoint_test.rb
@@ -14,6 +14,12 @@ def setup
@options = { :order_id => 1000, :billing_address => address }
end
+ def test_instantiating_without_credential_raises
+ assert_raise ArgumentError do
+ LinkpointGateway.new(login: 123123)
+ end
+ end
+
def test_credit_card_formatting
assert_equal '04', @gateway.send(:format_creditcard_expiry_year, 2004)
assert_equal '04', @gateway.send(:format_creditcard_expiry_year, '2004')
diff --git a/test/unit/gateways/litle_test.rb b/test/unit/gateways/litle_test.rb
index b3c64744ac1..0687e48ea35 100644
--- a/test/unit/gateways/litle_test.rb
+++ b/test/unit/gateways/litle_test.rb
@@ -21,8 +21,29 @@ def setup
number: "44444444400009",
payment_cryptogram: "BwABBJQ1AgAAAAAgJDUCAAAAAAA="
})
+ @decrypted_android_pay = ActiveMerchant::Billing::NetworkTokenizationCreditCard.new(
+ {
+ source: :android_pay,
+ month: '01',
+ year: '2021',
+ brand: "visa",
+ number: "4457000300000007",
+ payment_cryptogram: "BwABBJQ1AgAAAAAgJDUCAAAAAAA="
+ })
@amount = 100
@options = {}
+ @check = check(
+ name: 'Tom Black',
+ routing_number: '011075150',
+ account_number: '4099999992',
+ account_type: 'Checking'
+ )
+ @authorize_check = check(
+ name: 'John Smith',
+ routing_number: '011075150',
+ account_number: '1099999999',
+ account_type: 'Checking'
+ )
end
def test_successful_purchase
@@ -36,6 +57,17 @@ def test_successful_purchase
assert response.test?
end
+ def test_successful_purchase_with_echeck
+ response = stub_comms do
+ @gateway.purchase(2004, @check)
+ end.respond_with(successful_purchase_with_echeck_response)
+
+ assert_success response
+
+ assert_equal "621100411297330000;echeckSales;2004", response.authorization
+ assert response.test?
+ end
+
def test_failed_purchase
response = stub_comms do
@gateway.purchase(@amount, @credit_card)
@@ -47,6 +79,21 @@ def test_failed_purchase
assert response.test?
end
+ def test_passing_merchant_data
+ options = @options.merge(
+ affiliate: 'some-affiliate',
+ campaign: 'super-awesome-campaign',
+ merchant_grouping_id: 'brilliant-group'
+ )
+ stub_comms do
+ @gateway.purchase(@amount, @credit_card, options)
+ end.check_request do |endpoint, data, headers|
+ assert_match(%r(some-affiliate), data)
+ assert_match(%r(super-awesome-campaign), data)
+ assert_match(%r(brilliant-group), data)
+ end.respond_with(successful_purchase_response)
+ end
+
def test_passing_name_on_card
stub_comms do
@gateway.purchase(@amount, @credit_card)
@@ -114,6 +161,13 @@ def test_add_applepay_order_source
end.respond_with(successful_purchase_response)
end
+ def test_add_android_pay_order_source
+ stub_comms do
+ @gateway.purchase(@amount, @decrypted_android_pay)
+ end.check_request do |endpoint, data, headers|
+ assert_match "androidpay", data
+ end.respond_with(successful_purchase_response)
+ end
def test_successful_authorize_and_capture
response = stub_comms do
@@ -233,6 +287,15 @@ def test_failed_void_of_other_things
assert_equal "360", response.params["response"]
end
+ def test_successful_void_of_echeck
+ response = stub_comms do
+ @gateway.void("945032206979933000;echeckSales;2004")
+ end.respond_with(successful_void_of_echeck_response)
+
+ assert_success response
+ assert_equal "986272331806746000;echeckVoid;", response.authorization
+ end
+
def test_successful_store
response = stub_comms do
@gateway.store(@credit_card)
@@ -355,6 +418,20 @@ def successful_purchase_response
)
end
+ def successful_purchase_with_echeck_response
+ %(
+
+
+ 621100411297330000
+ 42
+ 000
+ 2018-01-09T14:02:20
+ Approved
+
+
+ )
+ end
+
def failed_purchase_response
%(
@@ -489,6 +566,20 @@ def successful_void_of_other_things_response
)
end
+ def successful_void_of_echeck_response
+ %(
+
+
+ 986272331806746000
+ 000
+ 2018-01-09T14:20:00
+ Approved
+ 2018-01-09
+
+
+ )
+ end
+
def failed_void_of_authorization_response
%(
diff --git a/test/unit/gateways/mercado_pago_test.rb b/test/unit/gateways/mercado_pago_test.rb
new file mode 100644
index 00000000000..aaf93e99361
--- /dev/null
+++ b/test/unit/gateways/mercado_pago_test.rb
@@ -0,0 +1,391 @@
+require 'test_helper'
+
+class MercadoPagoTest < Test::Unit::TestCase
+ include CommStub
+
+ def setup
+ @gateway = MercadoPagoGateway.new(access_token: 'access_token')
+ @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).at_most(2).returns(successful_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal "4141491|1.0", response.authorization
+ assert_equal "accredited", response.message
+ assert response.test?
+ end
+
+ def test_failed_purchase
+ @gateway.expects(:ssl_post).at_most(2).returns(failed_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal "rejected", response.error_code
+ assert_equal "cc_rejected_other_reason", response.message
+ end
+
+ def test_successful_authorize
+ @gateway.expects(:ssl_post).at_most(2).returns(successful_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal "4261941|1.0", response.authorization
+ assert_equal "pending_capture", response.message
+ assert response.test?
+ end
+
+ def test_failed_authorize
+ @gateway.expects(:ssl_post).at_most(2).returns(failed_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal "rejected", response.error_code
+ assert_equal "cc_rejected_other_reason", response.message
+ end
+
+ def test_successful_capture
+ @gateway.expects(:ssl_request).returns(successful_capture_response)
+
+ response = @gateway.capture(@amount, "authorization|amount")
+ assert_success response
+
+ assert_equal "4261941|1.0", response.authorization
+ assert_equal "accredited", response.message
+ assert response.test?
+ end
+
+ def test_failed_capture
+ @gateway.expects(:ssl_request).returns(failed_capture_response)
+
+ response = @gateway.capture(@amount, "")
+ assert_failure response
+
+ assert_equal "|1.0", response.authorization
+ assert_equal "Method not allowed", response.message
+ assert response.test?
+ end
+
+ def test_successful_refund
+ @gateway.expects(:ssl_post).returns(successful_refund_response)
+
+ response = @gateway.refund(@amount, 'authorization|1.0', @options)
+ assert_success response
+ end
+
+ def test_failed_refund
+ @gateway.expects(:ssl_post).returns(failed_refund_response)
+
+ response = @gateway.refund(@amount, '', @options)
+ assert_failure response
+ assert_equal nil, response.error_code
+ end
+
+ def test_successful_void
+ @gateway.expects(:ssl_request).returns(successful_void_response)
+
+ response = @gateway.void("authorization|amount")
+ assert_success response
+
+ assert_equal "4261966|", response.authorization
+ assert_equal "by_collector", response.message
+ assert response.test?
+ end
+
+ def test_failed_void
+ @gateway.expects(:ssl_request).returns(failed_void_response)
+
+ response = @gateway.void("")
+ assert_failure response
+
+ assert_equal "|", response.authorization
+ assert_equal "Method not allowed", response.message
+ assert response.test?
+ end
+
+ def test_successful_verify
+ @gateway.expects(:ssl_request).at_most(3).returns(successful_void_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+
+ assert_equal "by_collector", response.message
+ assert response.test?
+ end
+
+ def test_successful_verify_with_failed_void
+ @gateway.expects(:ssl_request).at_most(3).returns(failed_void_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_failure response
+
+ assert_equal "Method not allowed", response.message
+ assert response.test?
+ end
+
+ def test_failed_verify
+ @gateway.expects(:ssl_request).at_most(2).returns(failed_authorize_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_failure response
+
+ assert_equal "cc_rejected_other_reason", response.message
+ assert response.test?
+ end
+
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
+ def test_sends_american_express_as_amex
+ credit_card = credit_card('378282246310005', brand: 'american_express')
+
+ response = stub_comms do
+ @gateway.purchase(@amount, credit_card, @options)
+ end.check_request do |endpoint, data, headers|
+ if data =~ /"payment_method_id"/
+ assert_match(%r(amex), data)
+ end
+ end.respond_with(successful_purchase_response)
+
+ assert_success response
+ assert_equal '4141491|1.0', response.authorization
+ end
+
+ def test_sends_diners_club_as_diners
+ credit_card = credit_card('30569309025904', brand: 'diners_club')
+
+ response = stub_comms do
+ @gateway.purchase(@amount, credit_card, @options)
+ end.check_request do |endpoint, data, headers|
+ if data =~ /"payment_method_id"/
+ assert_match(%r(diners), data)
+ end
+ end.respond_with(successful_purchase_response)
+
+ assert_success response
+ assert_equal '4141491|1.0', response.authorization
+ end
+
+ def test_sends_mastercard_as_master
+ credit_card = credit_card('5555555555554444', brand: 'master')
+
+ response = stub_comms do
+ @gateway.purchase(@amount, credit_card, @options)
+ end.check_request do |endpoint, data, headers|
+ if data =~ /"payment_method_id"/
+ assert_match(%r(master), data)
+ end
+ end.respond_with(successful_purchase_response)
+
+ assert_success response
+ assert_equal '4141491|1.0', response.authorization
+ end
+
+ def test_includes_deviceid_header
+ @options[:device_id] = '1a2b3c'
+ @gateway.expects(:ssl_post).with(anything, anything, headers = {'Content-Type' => 'application/json'}).returns(successful_purchase_response)
+ @gateway.expects(:ssl_post).with(anything, anything, headers = {'Content-Type' => 'application/json', 'X-Device-Session-ID' => '1a2b3c'}).returns(successful_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ end
+
+ def test_includes_additional_data
+ @options[:additional_info] = {'foo' => 'bar', 'baz' => 'quux'}
+ response = stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end.check_request do |endpoint, data, headers|
+ if data =~ /payment_method_id/
+ assert_match(/"foo":"bar"/, data)
+ assert_match(/"baz":"quux"/, data)
+ end
+ end.respond_with(successful_purchase_response)
+
+ assert_success response
+ end
+
+ private
+
+ def pre_scrubbed
+ %q(
+ opening connection to api.mercadopago.com:443...
+ opened
+ starting SSL for api.mercadopago.com:443...
+ SSL established
+ <- "POST /v1/card_tokens?access_token=TEST-8527269031909288-071213-0fc96cb7cd3633189bfbe29f63722700__LB_LA__-263489584 HTTP/1.1\r\nContent-Type: application/json\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api.mercadopago.com\r\nContent-Length: 140\r\n\r\n"
+ <- "{\"card_number\":\"4509953566233704\",\"security_code\":\"123\",\"expiration_month\":9,\"expiration_year\":2018,\"cardholder\":{\"name\":\"Longbob Longsen\"}}"
+ -> "HTTP/1.1 201 Created\r\n"
+ -> "Access-Control-Allow-Origin: *\r\n"
+ -> "X-Request-Id: eb7a95a0-dccb-4580-9a69-534f6faf0bd6\r\n"
+ -> "Content-Type: application/json;charset=utf-8\r\n"
+ -> "Date: Thu, 13 Jul 2017 17:37:58 GMT\r\n"
+ -> "Connection: close\r\n"
+ -> "X-XSS-Protection: 1; mode=block\r\n"
+ -> "X-Content-Type-Options: nosniff\r\n"
+ -> "Strict-Transport-Security: max-age=16070400\r\n"
+ -> "Set-Cookie: TS016da221=0119b547a2244bba3789910575ac019d7d44d644026217ca433918a8c8fd9ff83de9d4b3c095adc76ee58870b56cd33041797db9e2; Path=/; Secure; HTTPOnly\r\n"
+ -> "Vary: Accept-Encoding, User-Agent\r\n"
+ -> "Content-Encoding: gzip\r\n"
+ -> "Transfer-Encoding: chunked\r\n"
+ -> "\r\n"
+ Conn close
+ opening connection to api.mercadopago.com:443...
+ opened
+ starting SSL for api.mercadopago.com:443...
+ SSL established
+ <- "POST /v1/payments?access_token=TEST-8527269031909288-071213-0fc96cb7cd3633189bfbe29f63722700__LB_LA__-263489584 HTTP/1.1\r\nContent-Type: application/json\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api.mercadopago.com\r\nContent-Length: 395\r\n\r\n"
+ <- "{\"transaction_amount\":5.0,\"description\":\"Store Purchase\",\"installments\":1,\"order\":{\"type\":\"mercadopago\",\"id\":2554731505684667137},\"token\":\"02ed9760103508d54361da8741a22a9e\",\"payment_method_id\":\"visa\",\"additional_info\":{\"payer\":{\"address\":{\"zip_code\":\"K1C2N6\",\"street_number\":\"456\",\"street_name\":\"My Street\"}}},\"payer\":{\"email\":\"user+br@example.com\",\"first_name\":\"Longbob\",\"last_name\":\"Longsen\"}}"
+ -> "HTTP/1.1 201 Created\r\n"
+ -> "Date: Thu, 13 Jul 2017 17:37:59 GMT\r\n"
+ -> "Content-Type: application/json;charset=UTF-8\r\n"
+ -> "Connection: close\r\n"
+ -> "X-Response-Status: approved/accredited\r\n"
+ -> "X-Caller-Id: 263489584\r\n"
+ -> "Vary: Accept,Accept-Encoding, User-Agent\r\n"
+ -> "Cache-Control: max-age=0\r\n"
+ -> "ETag: 1deee4b03ae344416c5863ac0d92c13e\r\n"
+ -> "X-Content-Type-Options: nosniff\r\n"
+ -> "X-Frame-Options: DENY\r\n"
+ -> "X-Request-Id: ccb324d1-8365-42dd-8e9a-734488220777\r\n"
+ -> "X-XSS-Protection: 1; mode=block\r\n"
+ -> "Strict-Transport-Security: max-age=15724800\r\n"
+ -> "Access-Control-Allow-Origin: *\r\n"
+ -> "Access-Control-Allow-Headers: Content-Type\r\n"
+ -> "Access-Control-Allow-Methods: PUT, GET, POST, DELETE, OPTIONS\r\n"
+ -> "Access-Control-Max-Age: 86400\r\n"
+ -> "Set-Cookie: TS016da221=0119b547a287375accd901052e4871cecbc881599be32e9bcb508701e62cabee4424801a25969778d1c93e2c57c2fd0a8a934c9817; Path=/; Secure; HTTPOnly\r\n"
+ -> "Content-Encoding: gzip\r\n"
+ -> "Transfer-Encoding: chunked\r\n"
+ -> "\r\n"
+ Conn close
+ )
+ end
+
+ def post_scrubbed
+ %q(
+ opening connection to api.mercadopago.com:443...
+ opened
+ starting SSL for api.mercadopago.com:443...
+ SSL established
+ <- "POST /v1/card_tokens?access_token=[FILTERED] HTTP/1.1\r\nContent-Type: application/json\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api.mercadopago.com\r\nContent-Length: 140\r\n\r\n"
+ <- "{\"card_number\":\"[FILTERED]\",\"security_code\":\"[FILTERED]\",\"expiration_month\":9,\"expiration_year\":2018,\"cardholder\":{\"name\":\"Longbob Longsen\"}}"
+ -> "HTTP/1.1 201 Created\r\n"
+ -> "Access-Control-Allow-Origin: *\r\n"
+ -> "X-Request-Id: eb7a95a0-dccb-4580-9a69-534f6faf0bd6\r\n"
+ -> "Content-Type: application/json;charset=utf-8\r\n"
+ -> "Date: Thu, 13 Jul 2017 17:37:58 GMT\r\n"
+ -> "Connection: close\r\n"
+ -> "X-XSS-Protection: 1; mode=block\r\n"
+ -> "X-Content-Type-Options: nosniff\r\n"
+ -> "Strict-Transport-Security: max-age=16070400\r\n"
+ -> "Set-Cookie: TS016da221=0119b547a2244bba3789910575ac019d7d44d644026217ca433918a8c8fd9ff83de9d4b3c095adc76ee58870b56cd33041797db9e2; Path=/; Secure; HTTPOnly\r\n"
+ -> "Vary: Accept-Encoding, User-Agent\r\n"
+ -> "Content-Encoding: gzip\r\n"
+ -> "Transfer-Encoding: chunked\r\n"
+ -> "\r\n"
+ Conn close
+ opening connection to api.mercadopago.com:443...
+ opened
+ starting SSL for api.mercadopago.com:443...
+ SSL established
+ <- "POST /v1/payments?access_token=[FILTERED] HTTP/1.1\r\nContent-Type: application/json\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api.mercadopago.com\r\nContent-Length: 395\r\n\r\n"
+ <- "{\"transaction_amount\":5.0,\"description\":\"Store Purchase\",\"installments\":1,\"order\":{\"type\":\"mercadopago\",\"id\":2554731505684667137},\"token\":\"02ed9760103508d54361da8741a22a9e\",\"payment_method_id\":\"visa\",\"additional_info\":{\"payer\":{\"address\":{\"zip_code\":\"K1C2N6\",\"street_number\":\"456\",\"street_name\":\"My Street\"}}},\"payer\":{\"email\":\"user+br@example.com\",\"first_name\":\"Longbob\",\"last_name\":\"Longsen\"}}"
+ -> "HTTP/1.1 201 Created\r\n"
+ -> "Date: Thu, 13 Jul 2017 17:37:59 GMT\r\n"
+ -> "Content-Type: application/json;charset=UTF-8\r\n"
+ -> "Connection: close\r\n"
+ -> "X-Response-Status: approved/accredited\r\n"
+ -> "X-Caller-Id: 263489584\r\n"
+ -> "Vary: Accept,Accept-Encoding, User-Agent\r\n"
+ -> "Cache-Control: max-age=0\r\n"
+ -> "ETag: 1deee4b03ae344416c5863ac0d92c13e\r\n"
+ -> "X-Content-Type-Options: nosniff\r\n"
+ -> "X-Frame-Options: DENY\r\n"
+ -> "X-Request-Id: ccb324d1-8365-42dd-8e9a-734488220777\r\n"
+ -> "X-XSS-Protection: 1; mode=block\r\n"
+ -> "Strict-Transport-Security: max-age=15724800\r\n"
+ -> "Access-Control-Allow-Origin: *\r\n"
+ -> "Access-Control-Allow-Headers: Content-Type\r\n"
+ -> "Access-Control-Allow-Methods: PUT, GET, POST, DELETE, OPTIONS\r\n"
+ -> "Access-Control-Max-Age: 86400\r\n"
+ -> "Set-Cookie: TS016da221=0119b547a287375accd901052e4871cecbc881599be32e9bcb508701e62cabee4424801a25969778d1c93e2c57c2fd0a8a934c9817; Path=/; Secure; HTTPOnly\r\n"
+ -> "Content-Encoding: gzip\r\n"
+ -> "Transfer-Encoding: chunked\r\n"
+ -> "\r\n"
+ Conn close
+ )
+ end
+
+ def successful_purchase_response
+ %(
+ {"id":4141491,"date_created":"2017-07-06T09:49:35.000-04:00","date_approved":"2017-07-06T09:49:35.000-04:00","date_last_updated":"2017-07-06T09:49:35.000-04:00","date_of_expiration":null,"money_release_date":"2017-07-18T09:49:35.000-04:00","operation_type":"regular_payment","issuer_id":"166","payment_method_id":"visa","payment_type_id":"credit_card","status":"approved","status_detail":"accredited","currency_id":"MXN","description":"Store Purchase","live_mode":false,"sponsor_id":null,"authorization_code":null,"related_exchange_rate":null,"collector_id":261735089,"payer":{"type":"guest","id":null,"email":"user@example.com","identification":{"type":null,"number":null},"phone":{"area_code":null,"number":null,"extension":""},"first_name":"First User","last_name":"User","entity_type":null},"metadata":{},"additional_info":{"payer":{"address":{"zip_code":"K1C2N6","street_name":"My Street","street_number":"456"}}},"order":{"type":"mercadopago","id":"2326513804447055222"},"external_reference":null,"transaction_amount":5,"transaction_amount_refunded":0,"coupon_amount":0,"differential_pricing_id":null,"deduction_schema":null,"transaction_details":{"net_received_amount":0.14,"total_paid_amount":5,"overpaid_amount":0,"external_resource_url":null,"installment_amount":5,"financial_institution":null,"payment_method_reference_id":null,"payable_deferral_period":null,"acquirer_reference":null},"fee_details":[{"type":"mercadopago_fee","amount":4.86,"fee_payer":"collector"}],"captured":true,"binary_mode":false,"call_for_authorize_id":null,"statement_descriptor":"WWW.MERCADOPAGO.COM","installments":1,"card":{"id":null,"first_six_digits":"450995","last_four_digits":"3704","expiration_month":9,"expiration_year":2018,"date_created":"2017-07-06T09:49:35.000-04:00","date_last_updated":"2017-07-06T09:49:35.000-04:00","cardholder":{"name":"Longbob Longsen","identification":{"number":null,"type":null}}},"notification_url":null,"refunds":[],"processing_mode":null,"merchant_account_id":null,"acquirer":null,"merchant_number":null}
+ )
+ end
+
+ def failed_purchase_response
+ %(
+ {"id":4142297,"date_created":"2017-07-06T10:13:32.000-04:00","date_approved":null,"date_last_updated":"2017-07-06T10:13:32.000-04:00","date_of_expiration":null,"money_release_date":null,"operation_type":"regular_payment","issuer_id":"166","payment_method_id":"visa","payment_type_id":"credit_card","status":"rejected","status_detail":"cc_rejected_other_reason","currency_id":"MXN","description":"Store Purchase","live_mode":false,"sponsor_id":null,"authorization_code":null,"related_exchange_rate":null,"collector_id":261735089,"payer":{"type":"guest","id":null,"email":"user@example.com","identification":{"type":null,"number":null},"phone":{"area_code":null,"number":null,"extension":""},"first_name":"First User","last_name":"User","entity_type":null},"metadata":{},"additional_info":{"payer":{"address":{"zip_code":"K1C2N6","street_name":"My Street","street_number":"456"}}},"order":{"type":"mercadopago","id":"830943860538524456"},"external_reference":null,"transaction_amount":5,"transaction_amount_refunded":0,"coupon_amount":0,"differential_pricing_id":null,"deduction_schema":null,"transaction_details":{"net_received_amount":0,"total_paid_amount":5,"overpaid_amount":0,"external_resource_url":null,"installment_amount":5,"financial_institution":null,"payment_method_reference_id":null,"payable_deferral_period":null,"acquirer_reference":null},"fee_details":[],"captured":true,"binary_mode":false,"call_for_authorize_id":null,"statement_descriptor":"WWW.MERCADOPAGO.COM","installments":1,"card":{"id":null,"first_six_digits":"400030","last_four_digits":"2220","expiration_month":9,"expiration_year":2018,"date_created":"2017-07-06T10:13:32.000-04:00","date_last_updated":"2017-07-06T10:13:32.000-04:00","cardholder":{"name":"Longbob Longsen","identification":{"number":null,"type":null}}},"notification_url":null,"refunds":[],"processing_mode":null,"merchant_account_id":null,"acquirer":null,"merchant_number":null}
+ )
+ end
+
+ def successful_authorize_response
+ %(
+ {"id":4261941,"date_created":"2017-07-13T14:24:46.000-04:00","date_approved":null,"date_last_updated":"2017-07-13T14:24:46.000-04:00","date_of_expiration":null,"money_release_date":null,"operation_type":"regular_payment","issuer_id":"25","payment_method_id":"visa","payment_type_id":"credit_card","status":"authorized","status_detail":"pending_capture","currency_id":"BRL","description":"Store Purchase","live_mode":false,"sponsor_id":null,"authorization_code":null,"related_exchange_rate":null,"collector_id":263489584,"payer":{"type":"guest","id":null,"email":"user+br@example.com","identification":{"type":null,"number":null},"phone":{"area_code":null,"number":null,"extension":null},"first_name":null,"last_name":null,"entity_type":null},"metadata":{},"additional_info":{"payer":{"address":{"zip_code":"K1C2N6","street_name":"My Street","street_number":"456"}}},"order":{"type":"mercadopago","id":"2294029672081601730"},"external_reference":null,"transaction_amount":5,"transaction_amount_refunded":0,"coupon_amount":0,"differential_pricing_id":null,"deduction_schema":null,"transaction_details":{"net_received_amount":0,"total_paid_amount":5,"overpaid_amount":0,"external_resource_url":null,"installment_amount":5,"financial_institution":null,"payment_method_reference_id":null,"payable_deferral_period":null,"acquirer_reference":null},"fee_details":[],"captured":false,"binary_mode":false,"call_for_authorize_id":null,"statement_descriptor":"WWW.MERCADOPAGO.COM","installments":1,"card":{"id":null,"first_six_digits":"450995","last_four_digits":"3704","expiration_month":9,"expiration_year":2018,"date_created":"2017-07-13T14:24:46.000-04:00","date_last_updated":"2017-07-13T14:24:46.000-04:00","cardholder":{"name":"Longbob Longsen","identification":{"number":null,"type":null}}},"notification_url":null,"refunds":[],"processing_mode":"aggregator","merchant_account_id":null,"acquirer":null,"merchant_number":null}
+ )
+ end
+
+ def failed_authorize_response
+ %(
+ {"id":4261953,"date_created":"2017-07-13T14:25:33.000-04:00","date_approved":null,"date_last_updated":"2017-07-13T14:25:33.000-04:00","date_of_expiration":null,"money_release_date":null,"operation_type":"regular_payment","issuer_id":"25","payment_method_id":"visa","payment_type_id":"credit_card","status":"rejected","status_detail":"cc_rejected_other_reason","currency_id":"BRL","description":"Store Purchase","live_mode":false,"sponsor_id":null,"authorization_code":null,"related_exchange_rate":null,"collector_id":263489584,"payer":{"type":"guest","id":null,"email":"user+br@example.com","identification":{"type":null,"number":null},"phone":{"area_code":null,"number":null,"extension":null},"first_name":null,"last_name":null,"entity_type":null},"metadata":{},"additional_info":{"payer":{"address":{"zip_code":"K1C2N6","street_name":"My Street","street_number":"456"}}},"order":{"type":"mercadopago","id":"7528376941458928221"},"external_reference":null,"transaction_amount":5,"transaction_amount_refunded":0,"coupon_amount":0,"differential_pricing_id":null,"deduction_schema":null,"transaction_details":{"net_received_amount":0,"total_paid_amount":5,"overpaid_amount":0,"external_resource_url":null,"installment_amount":5,"financial_institution":null,"payment_method_reference_id":null,"payable_deferral_period":null,"acquirer_reference":null},"fee_details":[],"captured":false,"binary_mode":false,"call_for_authorize_id":null,"statement_descriptor":"WWW.MERCADOPAGO.COM","installments":1,"card":{"id":null,"first_six_digits":"400030","last_four_digits":"2220","expiration_month":9,"expiration_year":2018,"date_created":"2017-07-13T14:25:33.000-04:00","date_last_updated":"2017-07-13T14:25:33.000-04:00","cardholder":{"name":"Longbob Longsen","identification":{"number":null,"type":null}}},"notification_url":null,"refunds":[],"processing_mode":"aggregator","merchant_account_id":null,"acquirer":null,"merchant_number":null}
+ )
+ end
+
+ def successful_capture_response
+ %(
+ {"id":4261941,"date_created":"2017-07-13T14:24:46.000-04:00","date_approved":"2017-07-13T14:24:47.000-04:00","date_last_updated":"2017-07-13T14:24:47.000-04:00","date_of_expiration":null,"money_release_date":"2017-07-27T14:24:47.000-04:00","operation_type":"regular_payment","issuer_id":"25","payment_method_id":"visa","payment_type_id":"credit_card","status":"approved","status_detail":"accredited","currency_id":"BRL","description":"Store Purchase","live_mode":false,"sponsor_id":null,"authorization_code":null,"related_exchange_rate":null,"collector_id":263489584,"payer":{"type":"guest","id":null,"email":"user+br@example.com","identification":{"type":null,"number":null},"phone":{"area_code":null,"number":null,"extension":null},"first_name":null,"last_name":null,"entity_type":null},"metadata":{},"additional_info":{"payer":{"address":{"zip_code":"K1C2N6","street_name":"My Street","street_number":"456"}}},"order":{"type":"mercadopago","id":"2294029672081601730"},"external_reference":null,"transaction_amount":5,"transaction_amount_refunded":0,"coupon_amount":0,"differential_pricing_id":null,"deduction_schema":null,"transaction_details":{"net_received_amount":4.75,"total_paid_amount":5,"overpaid_amount":0,"external_resource_url":null,"installment_amount":5,"financial_institution":null,"payment_method_reference_id":null,"payable_deferral_period":null,"acquirer_reference":null},"fee_details":[{"type":"mercadopago_fee","amount":0.25,"fee_payer":"collector"}],"captured":true,"binary_mode":false,"call_for_authorize_id":null,"statement_descriptor":"WWW.MERCADOPAGO.COM","installments":1,"card":{"id":null,"first_six_digits":"450995","last_four_digits":"3704","expiration_month":9,"expiration_year":2018,"date_created":"2017-07-13T14:24:46.000-04:00","date_last_updated":"2017-07-13T14:24:46.000-04:00","cardholder":{"name":"Longbob Longsen","identification":{"number":null,"type":null}}},"notification_url":null,"refunds":[],"processing_mode":"aggregator","merchant_account_id":null,"acquirer":null,"merchant_number":null}
+ )
+ end
+
+ def failed_capture_response
+ %(
+ {"message":"Method not allowed","error":"method_not_allowed","status":405,"cause":[{"code":"Method not allowed","description":"Method not allowed","data":null}]}
+ )
+ end
+
+ def successful_refund_response
+ %(
+ {"id":4247757,"payment_id":4247751,"amount":5,"metadata":{},"source":{"id":"261735089","name":"Spreedly Integrations","type":"collector"},"date_created":"2017-07-12T14:45:08.752-04:00","unique_sequence_number":null}
+ )
+ end
+
+ def failed_refund_response
+ %(
+ {"message":"Resource /payments/refunds/ not found.","error":"not_found","status":404,"cause":[]}
+ )
+ end
+
+ def successful_void_response
+ %(
+ {"id":4261966,"date_created":"2017-07-13T14:26:56.000-04:00","date_approved":null,"date_last_updated":"2017-07-13T14:26:57.000-04:00","date_of_expiration":null,"money_release_date":null,"operation_type":"regular_payment","issuer_id":"25","payment_method_id":"visa","payment_type_id":"credit_card","status":"cancelled","status_detail":"by_collector","currency_id":"BRL","description":"Store Purchase","live_mode":false,"sponsor_id":null,"authorization_code":null,"related_exchange_rate":null,"collector_id":263489584,"payer":{"type":"guest","id":null,"email":"user+br@example.com","identification":{"type":null,"number":null},"phone":{"area_code":null,"number":null,"extension":null},"first_name":null,"last_name":null,"entity_type":null},"metadata":{},"additional_info":{"payer":{"address":{"zip_code":"K1C2N6","street_name":"My Street","street_number":"456"}}},"order":{"type":"mercadopago","id":"6688620487994029432"},"external_reference":null,"transaction_amount":5,"transaction_amount_refunded":0,"coupon_amount":0,"differential_pricing_id":null,"deduction_schema":null,"transaction_details":{"net_received_amount":0,"total_paid_amount":5,"overpaid_amount":0,"external_resource_url":null,"installment_amount":5,"financial_institution":null,"payment_method_reference_id":null,"payable_deferral_period":null,"acquirer_reference":null},"fee_details":[],"captured":false,"binary_mode":false,"call_for_authorize_id":null,"statement_descriptor":"WWW.MERCADOPAGO.COM","installments":1,"card":{"id":null,"first_six_digits":"450995","last_four_digits":"3704","expiration_month":9,"expiration_year":2018,"date_created":"2017-07-13T14:26:56.000-04:00","date_last_updated":"2017-07-13T14:26:56.000-04:00","cardholder":{"name":"Longbob Longsen","identification":{"number":null,"type":null}}},"notification_url":null,"refunds":[],"processing_mode":"aggregator","merchant_account_id":null,"acquirer":null,"merchant_number":null}
+ )
+ end
+
+ def failed_void_response
+ %(
+ {"message":"Method not allowed","error":"method_not_allowed","status":405,"cause":[{"code":"Method not allowed","description":"Method not allowed","data":null}]}
+ )
+ end
+end
diff --git a/test/unit/gateways/merchant_warrior_test.rb b/test/unit/gateways/merchant_warrior_test.rb
index 17e098d5849..8b9da664447 100644
--- a/test/unit/gateways/merchant_warrior_test.rb
+++ b/test/unit/gateways/merchant_warrior_test.rb
@@ -90,6 +90,47 @@ def test_scrub_name
end.respond_with(successful_purchase_response)
end
+ def test_address
+ @options[:address] = {
+ name: 'Bat Man',
+ address1: '123 Main',
+ city: 'Brooklyn',
+ state: 'NY',
+ country: 'US',
+ zip: '11111',
+ phone: '555-1212',
+ email: 'user@aol.com',
+ ip: '1.2.3.4'
+ }
+
+ stub_comms do
+ @gateway.purchase(@success_amount, @credit_card, @options)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/customerName=Bat\+Man/, data)
+ assert_match(/customerCountry=US/, data)
+ assert_match(/customerState=NY/, data)
+ assert_match(/customerCity=Brooklyn/, data)
+ assert_match(/customerAddress=123\+Main/, data)
+ assert_match(/customerPostCode=11111/, data)
+ assert_match(/customerIP=1.2.3.4/, data)
+ assert_match(/customerPhone=555-1212/, data)
+ assert_match(/customerEmail=user%40aol.com/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
+ def test_address_without_state
+ @options[:address] = {
+ name: 'Bat Man',
+ state: nil
+ }
+
+ stub_comms do
+ @gateway.purchase(@success_amount, @credit_card, @options)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/customerState=N%2FA/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
def test_orderid_truncated
stub_comms do
@gateway.purchase(@success_amount, @credit_card, order_id: "ThisIsQuiteALongDescriptionWithLotsOfChars")
diff --git a/test/unit/gateways/mercury_test.rb b/test/unit/gateways/mercury_test.rb
index 5b756f07ee7..fae8efc7143 100644
--- a/test/unit/gateways/mercury_test.rb
+++ b/test/unit/gateways/mercury_test.rb
@@ -117,91 +117,144 @@ def test_card_present_with_invalid_data
assert_success response
end
+ def test_transcript_scrubbing
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrub), post_scrub
+ end
+
private
def successful_purchase_response
<<-RESPONSE
-<?xml version="1.0"?>
-<RStream>
- <CmdResponse>
- <ResponseOrigin>Processor</ResponseOrigin>
- <DSIXReturnCode>000000</DSIXReturnCode>
- <CmdStatus>Approved</CmdStatus>
- <TextResponse>AP*</TextResponse>
- <UserTraceData></UserTraceData>
- </CmdResponse>
- <TranResponse>
- <MerchantID>595901</MerchantID>
- <AcctNo>5499990123456781</AcctNo>
- <ExpDate>0813</ExpDate>
- <CardType>M/C</CardType>
- <TranCode>Sale</TranCode>
- <AuthCode>000011</AuthCode>
- <CaptureStatus>Captured</CaptureStatus>
- <RefNo>0194</RefNo>
- <InvoiceNo>1</InvoiceNo>
- <AVSResult>Y</AVSResult>
- <CVVResult>M</CVVResult>
- <OperatorID>999</OperatorID>
- <Memo>LM Integration (Ruby)</Memo>
- <Amount>
- <Purchase>1.00</Purchase>
- <Authorize>1.00</Authorize>
- </Amount>
- <AcqRefData>KbMCC0742510421 </AcqRefData>
- <ProcessData>|17|410100700000</ProcessData>
- </TranResponse>
-</RStream>
+
+
+
+ Processor
+ 000000
+ Approved
+ AP*
+
+
+
+ 595901
+ 5499990123456781
+ 0813
+ M/C
+ Sale
+ 000011
+ Captured
+ 0194
+ 1
+ Y
+ M
+ 999
+ LM Integration (Ruby)
+
+ 1.00
+ 1.00
+
+ KbMCC0742510421
+ |17|410100700000
+
+
RESPONSE
end
def failed_purchase_response
<<-RESPONSE
-<?xml version="1.0"?>
-<RStream>
- <CmdResponse>
- <ResponseOrigin>Server</ResponseOrigin>
- <DSIXReturnCode>000000</DSIXReturnCode>
- <CmdStatus>Error</CmdStatus>
- <TextResponse>No Live Cards on Test Merchant ID Allowed.</TextResponse>
- <UserTraceData></UserTraceData>
- </CmdResponse>
-</RStream>
+
+
+
+ Server
+ 000000
+ Error
+ No Live Cards on Test Merchant ID Allowed.
+
+
+
RESPONSE
end
def successful_refund_response
<<-RESPONSE
-<?xml version="1.0"?>
-<RStream>
- <CmdResponse>
- <ResponseOrigin>Processor</ResponseOrigin>
- <DSIXReturnCode>000000</DSIXReturnCode>
- <CmdStatus>Approved</CmdStatus>
- <TextResponse>AP</TextResponse>
- <UserTraceData></UserTraceData>
- </CmdResponse>
- <TranResponse>
- <MerchantID>595901</MerchantID>
- <AcctNo>5499990123456781</AcctNo>
- <ExpDate>0813</ExpDate>
- <CardType>M/C</CardType>
- <TranCode>VoidSale</TranCode>
- <AuthCode>VOIDED</AuthCode>
- <CaptureStatus>Captured</CaptureStatus>
- <RefNo>0568</RefNo>
- <InvoiceNo>123</InvoiceNo>
- <OperatorID>999</OperatorID>
- <Amount>
- <Purchase>1.00</Purchase>
- <Authorize>1.00</Authorize>
- </Amount>
- <AcqRefData>K</AcqRefData>
- </TranResponse>
-</RStream>
+
+
+
+ Processor
+ 000000
+ Approved
+ AP
+
+
+
+ 595901
+ 5499990123456781
+ 0813
+ M/C
+ VoidSale
+ VOIDED
+ Captured
+ 0568
+ 123
+ 999
+
+ 1.00
+ 1.00
+
+ K
+
+
RESPONSE
end
+
+ def pre_scrub
+ %q{
+opening connection to w1.mercurycert.net:443...
+opened
+starting SSL for w1.mercurycert.net:443...
+SSL established
+<- "POST /ws/ws.asmx HTTP/1.1\r\nContent-Type: text/xml; charset=utf-8\r\nSoapaction: http://www.mercurypay.com/CreditTransaction\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: w1.mercurycert.net\r\nContent-Length: 823\r\n\r\n"
+<- "\nCreditSalec111111111.1c111111111.1ActiveMerchantOneTimeRecordNumberRequested0897167417014451.0040030001234567811218VISA123\n]]>\nxyz"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Cache-Control: private, max-age=0\r\n"
+-> "Content-Type: text/xml; charset=utf-8\r\n"
+-> "X-AspNet-Version: 4.0.30319\r\n"
+-> "X-Powered-By: ASP.NET\r\n"
+-> "Date: Mon, 08 Jan 2018 19:49:31 GMT\r\n"
+-> "Connection: close\r\n"
+-> "Content-Length: 1648\r\n"
+-> "\r\n"
+reading 1648 bytes...
+-> "\r\n\r\n\t\r\n\t\tProcessor\r\n\t\t000000\r\n\t\tApproved\r\n\t\tAP*\r\n\t\t\r\n\t\r\n\t\r\n\t\t089716741701445\r\n\t\t400300XXXXXX6781\r\n\t\tXXXX\r\n\t\tVISA\r\n\t\tSale\r\n\t\tVI0100\r\n\t\tCaptured\r\n\t\t0001\r\n\t\tC111111111.1\r\n\t\tU\r\n\t\tActiveMerchant\r\n\t\t\r\n\t\t\t1.00\r\n\t\t\t1.00\r\n\t\t\r\n\t\tKaNb018008177003332cABCAd5e00fJlA m000005\r\n\t\twin4rRFHp8+AV/vstAfKvsUvZ5IH+bHblTktfumnY/EiEgUQFyIQGjMM\r\n\t\t|00|600550672000\r\n\t\r\n\r\n"
+read 1648 bytes
+Conn close
+ }
+ end
+
+ def post_scrub
+ %q{
+opening connection to w1.mercurycert.net:443...
+opened
+starting SSL for w1.mercurycert.net:443...
+SSL established
+<- "POST /ws/ws.asmx HTTP/1.1\r\nContent-Type: text/xml; charset=utf-8\r\nSoapaction: http://www.mercurypay.com/CreditTransaction\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: w1.mercurycert.net\r\nContent-Length: 823\r\n\r\n"
+<- "\nCreditSalec111111111.1c111111111.1ActiveMerchantOneTimeRecordNumberRequested0897167417014451.00[FILTERED]1218VISA[FILTERED]\n]]>\n[FILTERED]"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Cache-Control: private, max-age=0\r\n"
+-> "Content-Type: text/xml; charset=utf-8\r\n"
+-> "X-AspNet-Version: 4.0.30319\r\n"
+-> "X-Powered-By: ASP.NET\r\n"
+-> "Date: Mon, 08 Jan 2018 19:49:31 GMT\r\n"
+-> "Connection: close\r\n"
+-> "Content-Length: 1648\r\n"
+-> "\r\n"
+reading 1648 bytes...
+-> "\r\n\r\n\t\r\n\t\tProcessor\r\n\t\t000000\r\n\t\tApproved\r\n\t\tAP*\r\n\t\t\r\n\t\r\n\t\r\n\t\t089716741701445\r\n\t\t[FILTERED]\r\n\t\tXXXX\r\n\t\tVISA\r\n\t\tSale\r\n\t\tVI0100\r\n\t\tCaptured\r\n\t\t0001\r\n\t\tC111111111.1\r\n\t\tU\r\n\t\tActiveMerchant\r\n\t\t\r\n\t\t\t1.00\r\n\t\t\t1.00\r\n\t\t\r\n\t\tKaNb018008177003332cABCAd5e00fJlA m000005\r\n\t\twin4rRFHp8+AV/vstAfKvsUvZ5IH+bHblTktfumnY/EiEgUQFyIQGjMM\r\n\t\t|00|600550672000\r\n\t\r\n\r\n"
+read 1648 bytes
+Conn close
+ }
+ end
end
diff --git a/test/unit/gateways/metrics_global_test.rb b/test/unit/gateways/metrics_global_test.rb
index 04e8d7ed20c..610834a1766 100644
--- a/test/unit/gateways/metrics_global_test.rb
+++ b/test/unit/gateways/metrics_global_test.rb
@@ -185,6 +185,7 @@ def test_cvv_result
end
def test_message_from
+ omit "flaky spec skipped as we don't support this gateway"
@gateway.class_eval {
public :message_from
}
diff --git a/test/unit/gateways/migs_test.rb b/test/unit/gateways/migs_test.rb
index 644e1bd214a..f34b624c01c 100644
--- a/test/unit/gateways/migs_test.rb
+++ b/test/unit/gateways/migs_test.rb
@@ -10,32 +10,32 @@ def setup
@credit_card = credit_card
@amount = 100
-
- @options = {
+
+ @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 '123456', response.authorization
end
def test_unsuccessful_request
@gateway.expects(:ssl_post).returns(failed_purchase_response)
-
+
assert response = @gateway.purchase(@amount, @credit_card, @options)
assert_instance_of Response, response
assert_failure response
-
+
assert_equal '654321', response.authorization
end
@@ -45,33 +45,37 @@ def test_secure_hash
:OrderInfo => 'A48cvE28',
:Amount => 2995
}
- ordered_values = "#{@gateway.options[:secure_hash]}2995MER123A48cvE28"
-
+ ordered_values = "vpc_Amount=2995&vpc_MerchantId=MER123&vpc_OrderInfo=A48cvE28"
@gateway.send(:add_secure_hash, params)
- assert_equal Digest::MD5.hexdigest(ordered_values).upcase, params[:SecureHash]
+ assert_equal OpenSSL::HMAC.hexdigest('SHA256', [@gateway.options[:secure_hash]].pack('H*'), ordered_values).upcase, params[:SecureHash]
end
def test_purchase_offsite_response
# Below response from instance running remote test
- response_params = "vpc_3DSXID=a1B8UcW%2BKYqkSinLQohGmqQd9uY%3D&vpc_3DSenrolled=U&vpc_AVSResultCode=Unsupported&vpc_AcqAVSRespCode=Unsupported&vpc_AcqCSCRespCode=Unsupported&vpc_AcqResponseCode=00&vpc_Amount=100&vpc_AuthorizeId=367739&vpc_BatchNo=20120421&vpc_CSCResultCode=Unsupported&vpc_Card=MC&vpc_Command=pay&vpc_Locale=en&vpc_MerchTxnRef=9&vpc_Merchant=TESTANZTEST3&vpc_Message=Approved&vpc_OrderInfo=1&vpc_ReceiptNo=120421367739&vpc_SecureHash=8794D9478D030B65F3092282E76283F8&vpc_TransactionNo=2000025183&vpc_TxnResponseCode=0&vpc_VerSecurityLevel=06&vpc_VerStatus=U&vpc_VerType=3DS&vpc_Version=1"
+ response_params = "vpc_3DSXID=a1B8UcW%2BKYqkSinLQohGmqQd9uY%3D&vpc_3DSenrolled=U&vpc_AVSResultCode=Unsupported&vpc_AcqAVSRespCode=Unsupported&vpc_AcqCSCRespCode=Unsupported&vpc_AcqResponseCode=00&vpc_Amount=100&vpc_AuthorizeId=367739&vpc_BatchNo=20120421&vpc_CSCResultCode=Unsupported&vpc_Card=MC&vpc_Command=pay&vpc_Locale=en&vpc_MerchTxnRef=9&vpc_Merchant=TESTANZTEST3&vpc_Message=Approved&vpc_OrderInfo=1&vpc_ReceiptNo=120421367739&vpc_SecureHash=20DE2CDEBE40D6F24E3ABC5D74081CB5B341CD447530121AD51A9504A923BBD0&vpc_TransactionNo=2000025183&vpc_TxnResponseCode=0&vpc_VerSecurityLevel=06&vpc_VerStatus=U&vpc_VerType=3DS&vpc_Version=1"
response_hash = @gateway.send(:parse, response_params)
- response_hash.delete(:SecureHash)
calculated_hash = @gateway.send(:calculate_secure_hash, response_hash, @gateway.options[:secure_hash])
- assert_equal '8794D9478D030B65F3092282E76283F8', calculated_hash
+ expected_hash_input = "vpc_3DSXID=a1B8UcW+KYqkSinLQohGmqQd9uY=&vpc_3DSenrolled=U&vpc_AVSResultCode=Unsupported&vpc_AcqAVSRespCode=Unsupported&vpc_AcqCSCRespCode=Unsupported&vpc_AcqResponseCode=00&vpc_Amount=100&vpc_AuthorizeId=367739&vpc_BatchNo=20120421&vpc_CSCResultCode=Unsupported&vpc_Card=MC&vpc_Command=pay&vpc_Locale=en&vpc_MerchTxnRef=9&vpc_Merchant=TESTANZTEST3&vpc_Message=Approved&vpc_OrderInfo=1&vpc_ReceiptNo=120421367739&vpc_TransactionNo=2000025183&vpc_TxnResponseCode=0&vpc_VerSecurityLevel=06&vpc_VerStatus=U&vpc_VerType=3DS&vpc_Version=1"
+ assert_equal OpenSSL::HMAC.hexdigest('SHA256', [@gateway.options[:secure_hash]].pack('H*'), expected_hash_input).upcase, calculated_hash
response = @gateway.purchase_offsite_response(response_params)
assert_success response
- tampered_response1 = response_params.gsub('83F8', '93F8')
+ tampered_response1 = response_params.gsub('20DE', '20DF')
assert_raise(SecurityError){@gateway.purchase_offsite_response(tampered_response1)}
tampered_response2 = response_params.gsub('Locale=en', 'Locale=es')
assert_raise(SecurityError){@gateway.purchase_offsite_response(tampered_response2)}
end
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
private
-
+
# Place raw successful response from gateway here
def successful_purchase_response
build_response(
@@ -79,7 +83,7 @@ def successful_purchase_response
:TransactionNo => '123456'
)
end
-
+
# Place raw failed response from gateway here
def failed_purchase_response
build_response(
@@ -87,8 +91,60 @@ def failed_purchase_response
:TransactionNo => '654321'
)
end
-
+
def build_response(options)
options.collect { |key, value| "vpc_#{key}=#{CGI.escape(value.to_s)}"}.join('&')
end
+
+ def pre_scrubbed
+ <<-EOS
+opening connection to migs.mastercard.com.au:443...
+opened
+starting SSL for migs.mastercard.com.au:443...
+SSL established
+<- "POST /vpcdps HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: migs.mastercard.com.au\r\nContent-Length: 354\r\n\r\n"
+<- "vpc_Amount=100&vpc_Currency=SAR&vpc_OrderInfo=1&vpc_CardNum=4987654321098769&vpc_CardSecurityCode=123&vpc_CardExp=2105&vpc_Version=1&vpc_Merchant=TESTH-STATION&vpc_AccessCode=F1CE6F32&vpc_Command=pay&vpc_MerchTxnRef=84c1f31ded35dea26ac297fd7ba092da&vpc_SecureHash=CD1B2B8BC325C6C8FC1A041AD6AC90821984277113DF708B16B37809E7B0EC33&vpc_SecureHashType=SHA256"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Tue, 13 Feb 2018 19:02:18 GMT\r\n"
+-> "Expires: Sun, 15 Jul 1990 00:00:00 GMT\r\n"
+-> "Pragma: no-cache\r\n"
+-> "Cache-Control: no-cache\r\n"
+-> "Content-Length: 595\r\n"
+-> "P3P: CP=\"NOI DSP COR CURa ADMa TA1a OUR BUS IND UNI COM NAV INT\"\r\n"
+-> "Content-Type: text/plain;charset=iso-8859-1\r\n"
+-> "Connection: close\r\n"
+-> "Set-Cookie: TS01c4b9ca=01fb8d8de2ba6ffaf7439497dd78d9b3348c82bcf24d4619e65a406161e57276b6b293e77732a293be63bf750213e588797bc86f05; Path=/; Secure; HTTPOnly\r\n"
+-> "\r\n"
+reading 595 bytes...
+-> "vpc_AVSResultCode=Unsupported&vpc_AcqAVSRespCode=Unsupported&vpc_AcqCSCRespCode=Unsupported&vpc_AcqResponseCode=00&vpc_Amount=100&vpc_AuthorizeId=239491&vpc_BatchNo=20180214&vpc_CSCResultCode=Unsupported&vpc_Card=VC&vpc_Command=pay&vpc_Currency=SAR&vpc_Locale=en_SA&vpc_MerchTxnRef=84c1f31ded35dea26ac297fd7ba092da&vpc_Merchant=TESTH-STATION&vpc_Message=Approved&vpc_OrderInfo=1&vpc_ReceiptNo=804506239491&vpc_RiskOverallResult=ACC&vpc_SecureHash=99993E000461810D9F71B1A4FC5EA2D68DF6BA1F7EBA6A9FC544DA035627C03C&vpc_SecureHashType=SHA256&vpc_TransactionNo=372&vpc_TxnResponseCode=0&vpc_Version=1"
+read 595 bytes
+Conn close
+ EOS
+ end
+
+ def post_scrubbed
+ <<-EOS
+opening connection to migs.mastercard.com.au:443...
+opened
+starting SSL for migs.mastercard.com.au:443...
+SSL established
+<- "POST /vpcdps HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: migs.mastercard.com.au\r\nContent-Length: 354\r\n\r\n"
+<- "vpc_Amount=100&vpc_Currency=SAR&vpc_OrderInfo=1&vpc_CardNum=[FILTERED]&vpc_CardSecurityCode=[FILTERED]&vpc_CardExp=2105&vpc_Version=1&vpc_Merchant=TESTH-STATION&vpc_AccessCode=[FILTERED]&vpc_Command=pay&vpc_MerchTxnRef=84c1f31ded35dea26ac297fd7ba092da&vpc_SecureHash=CD1B2B8BC325C6C8FC1A041AD6AC90821984277113DF708B16B37809E7B0EC33&vpc_SecureHashType=SHA256"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Tue, 13 Feb 2018 19:02:18 GMT\r\n"
+-> "Expires: Sun, 15 Jul 1990 00:00:00 GMT\r\n"
+-> "Pragma: no-cache\r\n"
+-> "Cache-Control: no-cache\r\n"
+-> "Content-Length: 595\r\n"
+-> "P3P: CP=\"NOI DSP COR CURa ADMa TA1a OUR BUS IND UNI COM NAV INT\"\r\n"
+-> "Content-Type: text/plain;charset=iso-8859-1\r\n"
+-> "Connection: close\r\n"
+-> "Set-Cookie: TS01c4b9ca=01fb8d8de2ba6ffaf7439497dd78d9b3348c82bcf24d4619e65a406161e57276b6b293e77732a293be63bf750213e588797bc86f05; Path=/; Secure; HTTPOnly\r\n"
+-> "\r\n"
+reading 595 bytes...
+-> "vpc_AVSResultCode=Unsupported&vpc_AcqAVSRespCode=Unsupported&vpc_AcqCSCRespCode=Unsupported&vpc_AcqResponseCode=00&vpc_Amount=100&vpc_AuthorizeId=239491&vpc_BatchNo=20180214&vpc_CSCResultCode=Unsupported&vpc_Card=VC&vpc_Command=pay&vpc_Currency=SAR&vpc_Locale=en_SA&vpc_MerchTxnRef=84c1f31ded35dea26ac297fd7ba092da&vpc_Merchant=TESTH-STATION&vpc_Message=Approved&vpc_OrderInfo=1&vpc_ReceiptNo=804506239491&vpc_RiskOverallResult=ACC&vpc_SecureHash=99993E000461810D9F71B1A4FC5EA2D68DF6BA1F7EBA6A9FC544DA035627C03C&vpc_SecureHashType=SHA256&vpc_TransactionNo=372&vpc_TxnResponseCode=0&vpc_Version=1"
+read 595 bytes
+Conn close
+ EOS
+ end
end
diff --git a/test/unit/gateways/moneris_us_test.rb b/test/unit/gateways/moneris_us_test.rb
index 8bc63a9c289..daac3648e0c 100644
--- a/test/unit/gateways/moneris_us_test.rb
+++ b/test/unit/gateways/moneris_us_test.rb
@@ -243,7 +243,7 @@ def test_avs_enabled_and_provided
gateway = MonerisGateway.new(login: 'store1', password: 'yesguy', avs_enabled: true)
billing_address = address(address1: "1234 Anystreet", address2: "")
- stub_comms do
+ stub_comms(gateway) do
gateway.purchase(@amount, @credit_card, billing_address: billing_address, order_id: "1")
end.check_request do |endpoint, data, headers|
assert_match(%r{avs_street_number>1234<}, data)
@@ -255,7 +255,7 @@ def test_avs_enabled_and_provided
def test_avs_enabled_but_not_provided
gateway = MonerisGateway.new(login: 'store1', password: 'yesguy', avs_enabled: true)
- stub_comms do
+ stub_comms(gateway) do
gateway.purchase(@amount, @credit_card, @options.tap { |x| x.delete(:billing_address) })
end.check_request do |endpoint, data, headers|
assert_no_match(%r{avs_street_number>}, data)
@@ -312,6 +312,11 @@ def test_customer_not_specified_card_name_used
end.respond_with(successful_purchase_response)
end
+ def test_transcript_scrubbing
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrub), post_scrub
+ end
+
private
def successful_purchase_response
@@ -554,4 +559,52 @@ def xml_capture_fixture
'monusqa002qatoken1.01424242424242424203037order1'
end
+ def pre_scrub
+ %q{
+opening connection to esplusqa.moneris.com:443...
+opened
+starting SSL for esplusqa.moneris.com:443...
+SSL established
+<- "POST /gateway_us/servlet/MpgRequest HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: esplusqa.moneris.com\r\nContent-Length: 291\r\n\r\n"
+<- "monusqa002qatokencec9ca34132f0945446589e36fff9cceLongbob Longsen1.00424242424242424219097"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Mon, 08 Jan 2018 19:20:26 GMT\r\n"
+-> "Content-Length: 659\r\n"
+-> "X-UA-Compatible: IE=Edge\r\n"
+-> "Connection: close\r\n"
+-> "Content-Type: text/html; charset=UTF-8\r\n"
+-> "Set-Cookie: TS01d02998=01649737b16d4ca54c296a0a369f4e549e4191e85d8d022d01468e559975e945b419002a42; Path=/\r\n"
+-> "Set-Cookie: TS01d02998_28=01e24a44e55591744bc115f421ddccd549b1655d75bda586c8ea625670efaa4432f67c8b7e06e7af82c70ef3ac4f46d7435664f2ac; Path=/\r\n"
+-> "\r\n"
+reading 659 bytes...
+-> "cec9ca34132f0945446589e36fff9cce6400000300136301900010082712513:20:242018-01-0800trueAPPROVED*1.00V113295-0_25falsenullnullfalseA "
+read 659 bytes
+Conn close
+ }
+ end
+
+ def post_scrub
+ %q{
+opening connection to esplusqa.moneris.com:443...
+opened
+starting SSL for esplusqa.moneris.com:443...
+SSL established
+<- "POST /gateway_us/servlet/MpgRequest HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: esplusqa.moneris.com\r\nContent-Length: 291\r\n\r\n"
+<- "monusqa002[FILTERED]cec9ca34132f0945446589e36fff9cceLongbob Longsen1.00[FILTERED]19097"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Mon, 08 Jan 2018 19:20:26 GMT\r\n"
+-> "Content-Length: 659\r\n"
+-> "X-UA-Compatible: IE=Edge\r\n"
+-> "Connection: close\r\n"
+-> "Content-Type: text/html; charset=UTF-8\r\n"
+-> "Set-Cookie: TS01d02998=01649737b16d4ca54c296a0a369f4e549e4191e85d8d022d01468e559975e945b419002a42; Path=/\r\n"
+-> "Set-Cookie: TS01d02998_28=01e24a44e55591744bc115f421ddccd549b1655d75bda586c8ea625670efaa4432f67c8b7e06e7af82c70ef3ac4f46d7435664f2ac; Path=/\r\n"
+-> "\r\n"
+reading 659 bytes...
+-> "cec9ca34132f0945446589e36fff9cce6400000300136301900010082712513:20:242018-01-0800trueAPPROVED*1.00V113295-0_25falsenullnullfalseA "
+read 659 bytes
+Conn close
+ }
+ end
+
end
diff --git a/test/unit/gateways/mundipagg_test.rb b/test/unit/gateways/mundipagg_test.rb
new file mode 100644
index 00000000000..d610875abc4
--- /dev/null
+++ b/test/unit/gateways/mundipagg_test.rb
@@ -0,0 +1,814 @@
+require 'test_helper'
+
+class MundipaggTest < Test::Unit::TestCase
+ include CommStub
+ def setup
+ @gateway = MundipaggGateway.new(api_key: 'my_api_key')
+ @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)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal 'ch_90Vjq8TrwfP74XJO', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_purchase
+ @gateway.expects(:ssl_post).returns(failed_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal Gateway::STANDARD_ERROR_CODE[:processing_error], response.error_code
+ end
+
+ def test_successful_authorize
+ @gateway.expects(:ssl_post).returns(successful_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal 'ch_gm5wrlGMI2Fb0x6K', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_authorize
+ @gateway.expects(:ssl_post).returns(failed_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal Gateway::STANDARD_ERROR_CODE[:processing_error], response.error_code
+ end
+
+ def test_successful_capture
+ @gateway.expects(:ssl_post).returns(successful_capture_response)
+
+ response = @gateway.capture(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal 'ch_gm5wrlGMI2Fb0x6K', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_capture
+ @gateway.expects(:ssl_post).returns(failed_capture_response)
+
+ response = @gateway.capture(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal Gateway::STANDARD_ERROR_CODE[:processing_error], response.error_code
+ end
+
+ def test_successful_refund
+ @gateway.expects(:ssl_request).returns(successful_refund_response)
+
+ response = @gateway.refund(@amount, 'K1J5B1YFLE')
+ assert_success response
+
+ assert_equal 'ch_RbPVPWMH2bcGA50z', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_refund
+ @gateway.expects(:ssl_request).returns(failed_refund_response)
+
+ response = @gateway.refund(@amount, 'abc')
+ assert_failure response
+ assert_equal Gateway::STANDARD_ERROR_CODE[:processing_error], response.error_code
+ end
+
+ def test_successful_void
+ @gateway.expects(:ssl_request).returns(successful_void_response)
+
+ response = @gateway.void('ch_RbPVPWMH2bcGA50z')
+ assert_success response
+
+ assert_equal 'ch_RbPVPWMH2bcGA50z', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_void
+ @gateway.expects(:ssl_request).returns(failed_void_response)
+
+ response = @gateway.void('abc')
+ assert_failure response
+ assert_equal Gateway::STANDARD_ERROR_CODE[:processing_error], response.error_code
+ end
+
+ def test_successful_verify
+ @gateway.expects(:ssl_post).returns(successful_verify_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+
+ assert_equal 'ch_G9rB74PI3uoDMxAo', response.authorization
+ assert response.test?
+ end
+
+ def test_successful_verify_with_failed_void
+ response = stub_comms(@gateway, :ssl_request) do
+ @gateway.verify(@credit_card, @options)
+ end.respond_with(successful_authorize_response, failed_void_response)
+ assert_success response
+ assert_equal 'Simulator|Transação de simulação autorizada com sucesso', response.message
+ end
+
+ def test_sucessful_store
+ @gateway.expects(:ssl_post).times(2).returns(successful_create_customer_response, successful_store_response)
+
+ response = @gateway.store(@credit_card, @options)
+ assert_success response
+
+ assert_equal 'cus_N70xAX6S65cMnokB|card_51ElNwYSVJFpRe0g', response.authorization
+ assert response.test?
+ end
+
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
+ private
+
+ def pre_scrubbed
+ %q(
+ opening connection to api.mundipagg.com:443...
+ opened
+ starting SSL for api.mundipagg.com:443...
+ SSL established
+ <- "POST /core/v1/charges/ HTTP/1.1\r\nContent-Type: application/json\r\nAuthorization: Basic c2tfdGVzdF9keE1WOE51QnZpajZKNVhuOg==\r\nAccept: application/json\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api.mundipagg.com\r\nContent-Length: 424\r\n\r\n"
+ <- "{\"amount\":100,\"currency\":\"USD\",\"customer\":{\"email\":null,\"name\":\"Longbob Longsen\"},\"payment\":{\"payment_method\":\"credit_card\",\"credit_card\":{\"card\":{\"number\":\"4000100011112224\",\"holder_name\":\"Longbob Longsen\",\"exp_month\":9,\"exp_year\":2019,\"cvv\":\"123\",\"billing_address\":{\"street\":\"My Street\",\"number\":\"456\",\"compliment\":\"Apt 1\",\"city\":\"Ottawa\",\"state\":\"ON\",\"country\":\"CA\",\"zip_code\":\"K1C2N6\",\"neighborhood\":\"Sesame Street\"}}}}}"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "Date: Thu, 01 Feb 2018 20:23:19 GMT\r\n"
+ -> "Content-Type: application/json; charset=utf-8\r\n"
+ -> "Content-Length: 801\r\n"
+ -> "Connection: close\r\n"
+ -> "Cache-Control: no-cache\r\n"
+ -> "Pragma: no-cache\r\n"
+ -> "Content-Encoding: gzip\r\n"
+ -> "Expires: -1\r\n"
+ -> "Set-Cookie: TS01e8e2cd=0118d560cc62281517c87bb3b52c62fba3f9d13acb485adc69cac121833699beb2a66ca4bfe3e2af65dfe2f67542ec36ff8e41db56; Path=/; Domain=.api.mundipagg.com\r\n"
+ -> "\r\n"
+ reading 801 bytes...
+ -> "\x1F\x8B\b\x00\x00\x00\x00\x00\x04\x00\x95T\xCDn\x9C0\x10\xBEW\xEA; \xCEA2,,lnmRU\xD5\xB6I\x9AM\xAA\xAA\x174k\x9B]\xB7`\x13c\xB2\xD9\xA4\xFB4=\xF4A\xF2b\xB5\xCD\xC2\x82H\x95\xE4\x84\x98\xEF\x9B\xF1\xCC7?\x0Fo\xDF8\x8E\xCB\x88{\xEC\xB8x\x9D\xBE\x17\xBF\xD0\x1C>_G??\xDD\x7F\xBF\xF9\x9A\xBBG\x16\xC7\x82P\xC3\xF8\x18$\x97'\xA7\xC1b6\xDF\x03+Pt\x03\xDB\xB4\t\x004\xC1\x81\x8F#\x8FNH\xE6\x85\x10\x11o\x19\xFB\xB1\x87&\xD3I\x12M!\x0E\t\xEC\x1D\xA1\x105W\xDA\xC9G\xA8\xB1\x94\xC0H:6W\nT]\x99\xE8\x86\xD0\xE6SKI9\xDE\x1A\xF3\xF5\xE2\xD4m#l\v\xCAUZP\xB5\x16ME\x92\x12\xA6R\f\x92\xB8\xFDW\xCC\vn\x80\xFC\xC4C\x81\x87\xFC\xAB\x00\x1D\a\x93c\x7F\xF6\xA3\x8D/\xA9.\xEC\xFF\xC4\xA4%\xD6%y\x19\x11\xD7\x95\x12\x05\x95\x9A\xF6`\f\a\xD1\xEB*\xF5O\xCF/6\xE8j\xB3Y\xD0\xF8\xC3\\$\x8D\x8F\xA6p(\xAC\xEE\x9F\x05_-\xC5\xD21\xDF\x8A\xF2\x0E\xA7\x05\xB0\xDC\x10:\v\xA19\xE375\xB5\"f\x90W\xB4E^Z\xD3+\xAA2z\xAE\x05\xA7\xA6=\x0F;c\xD95\xD5\xE6P\xA9TI\xE0\x15`\xC5\x04\x1FUm\xB0\xF4\xAE\xD8\xC2\xE6v\xC3\xC2\xB3\xEB \x96\xF3\xB2\v\xDA\xF3L\xD5\xB6\xA4O\xB6r4}q\xE0\x87\xD3 \x9Eya\x92\x10/DQ\xE6\xC1\x94\xF8\x1E\xC28\xCE\xA2)\xCEhD;\xD7\xD1\xA0\rF\rC\xA9j\xFD`G\xAFj\x8Cie0%\xEBNR\xC6\xB5K\x9E\x9B\xA13\x90\xDF\x05\xC775\x93T\xA6m\xFF*V\xD49(!\xDD\x11\x05\xB2\x8C\xE5\fl\xAD\xED\x9A\x8DY\xAA)1\fga\x18\x8Da^\xD5\x06\x9E\xA0d\x12=\x01C\xAD\xD6]\xF0Y\x10\xC5\xF1lL*t}\xB0\xB2\x94E\x9B\xEE\xEF+\xDB\x89\xC7\xBF\x8F\x7F\x84C\xA8\xD3\xD4\xD1\xFC\xEA\xA0B\xB2{ \xE0`Q8Z!\x1D@\x8C\xE3J\xAA\xA5<\xD4\x86:\x86(\xA9\x84A\x8Fm\x9E\xC0I\xBA\xD7\xBF\xA3\xDA\xAEw3t\xD8\x1DmN\xEF\xE4\xFC'\x8F.\xD4\xEA\x12\xF3\xFB`\x19\xB7N\x9A\x951\xA9\xE7\xB0bw)a+f{\xE4\x86\b!\x1F\xF5HvV3Q\xCB\x1E\b\x82\xB0GYj\x15\xEC\x83\xDFX\x05=\xFBZ\xE4\xA4\xD7\xE5\xFFl\xA9\xD9\xD3\xBB2-\x04WkM\x9B\r\xCD[\n\xE6\xE8%\xEB\x01\x87I4[pK{\xA1\x9E[\xE3\xD9\x8F\x1E\xF9\xB9E\x1E\x90\x97,\xD7\xB7c\x95\x02!\xB2\x99\xF5No\x9B\x92\xA4\xD4\x86\xF9\xB2u\x16\xCD\xCFQ\x0F\xE7u\xB1\xB4\xE7\xCD\r\xA3\xE9\x00\xB9ge\xD7\xFD\xB9\x7F\x12\x9C\raN\xD9j\xBD\x14r-\x9A\x9B\xBD\xA0\x95\xD6\xF3\xA9'0S\xF6\xE2\x9F+\x05\e\x18@F0\xFB\xC0\xF9\xD9\xD0\xC5l\xB9\xB4^'\xEF\x06\x88.\x95\xA6\xFE>\xDF#\xA7+\xEA\xC8\x19&\xD0\xBA\xEC\x0EB\rO\xD2\x9E\xB1\xEBf\xF5\xC5\rzE{\xBAS\xA7;S\n^\xD1\xC16\xB4\xEA\xEA\bm6\xF6\x18\xBF}\xB3\xFB\aD\t\x1C\xBA\xE0\a\x00\x00"
+ read 801 bytes
+ Conn close
+ )
+ end
+
+ def post_scrubbed
+ %q(
+ opening connection to api.mundipagg.com:443...
+ opened
+ starting SSL for api.mundipagg.com:443...
+ SSL established
+ <- "POST /core/v1/charges/ HTTP/1.1\r\nContent-Type: application/json\r\nAuthorization: Basic [FILTERED]==\r\nAccept: application/json\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api.mundipagg.com\r\nContent-Length: 424\r\n\r\n"
+ <- "{\"amount\":100,\"currency\":\"USD\",\"customer\":{\"email\":null,\"name\":\"Longbob Longsen\"},\"payment\":{\"payment_method\":\"credit_card\",\"credit_card\":{\"card\":{\"number\":\"[FILTERED]\",\"holder_name\":\"Longbob Longsen\",\"exp_month\":9,\"exp_year\":2019,\"cvv\":\"[FILTERED]\",\"billing_address\":{\"street\":\"My Street\",\"number\":\"456\",\"compliment\":\"Apt 1\",\"city\":\"Ottawa\",\"state\":\"ON\",\"country\":\"CA\",\"zip_code\":\"K1C2N6\",\"neighborhood\":\"Sesame Street\"}}}}}"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "Date: Thu, 01 Feb 2018 20:23:19 GMT\r\n"
+ -> "Content-Type: application/json; charset=utf-8\r\n"
+ -> "Content-Length: 801\r\n"
+ -> "Connection: close\r\n"
+ -> "Cache-Control: no-cache\r\n"
+ -> "Pragma: no-cache\r\n"
+ -> "Content-Encoding: gzip\r\n"
+ -> "Expires: -1\r\n"
+ -> "Set-Cookie: TS01e8e2cd=0118d560cc62281517c87bb3b52c62fba3f9d13acb485adc69cac121833699beb2a66ca4bfe3e2af65dfe2f67542ec36ff8e41db56; Path=/; Domain=.api.mundipagg.com\r\n"
+ -> "\r\n"
+ reading 801 bytes...
+ -> "\x1F\x8B\b\x00\x00\x00\x00\x00\x04\x00\x95T\xCDn\x9C0\x10\xBEW\xEA; \xCEA2,,lnmRU\xD5\xB6I\x9AM\xAA\xAA\x174k\x9B]\xB7`\x13c\xB2\xD9\xA4\xFB4=\xF4A\xF2b\xB5\xCD\xC2\x82H\x95\xE4\x84\x98\xEF\x9B\xF1\xCC7?\x0Fo\xDF8\x8E\xCB\x88{\xEC\xB8x\x9D\xBE\x17\xBF\xD0\x1C>_G??\xDD\x7F\xBF\xF9\x9A\xBBG\x16\xC7\x82P\xC3\xF8\x18$\x97'\xA7\xC1b6\xDF\x03+Pt\x03\xDB\xB4\t\x004\xC1\x81\x8F#\x8FNH\xE6\x85\x10\x11o\x19\xFB\xB1\x87&\xD3I\x12M!\x0E\t\xEC\x1D\xA1\x105W\xDA\xC9G\xA8\xB1\x94\xC0H:6W\nT]\x99\xE8\x86\xD0\xE6SKI9\xDE\x1A\xF3\xF5\xE2\xD4m#l\v\xCAUZP\xB5\x16ME\x92\x12\xA6R\f\x92\xB8\xFDW\xCC\vn\x80\xFC\xC4C\x81\x87\xFC\xAB\x00\x1D\a\x93c\x7F\xF6\xA3\x8D/\xA9.\xEC\xFF\xC4\xA4%\xD6%y\x19\x11\xD7\x95\x12\x05\x95\x9A\xF6`\f\a\xD1\xEB*\xF5O\xCF/6\xE8j\xB3Y\xD0\xF8\xC3\\$\x8D\x8F\xA6p(\xAC\xEE\x9F\x05_-\xC5\xD21\xDF\x8A\xF2\x0E\xA7\x05\xB0\xDC\x10:\v\xA19\xE375\xB5\"f\x90W\xB4E^Z\xD3+\xAA2z\xAE\x05\xA7\xA6=\x0F;c\xD95\xD5\xE6P\xA9TI\xE0\x15`\xC5\x04\x1FUm\xB0\xF4\xAE\xD8\xC2\xE6v\xC3\xC2\xB3\xEB \x96\xF3\xB2\v\xDA\xF3L\xD5\xB6\xA4O\xB6r4}q\xE0\x87\xD3 \x9Eya\x92\x10/DQ\xE6\xC1\x94\xF8\x1E\xC28\xCE\xA2)\xCEhD;\xD7\xD1\xA0\rF\rC\xA9j\xFD`G\xAFj\x8Cie0%\xEBNR\xC6\xB5K\x9E\x9B\xA13\x90\xDF\x05\xC775\x93T\xA6m\xFF*V\xD49(!\xDD\x11\x05\xB2\x8C\xE5\fl\xAD\xED\x9A\x8DY\xAA)1\fga\x18\x8Da^\xD5\x06\x9E\xA0d\x12=\x01C\xAD\xD6]\xF0Y\x10\xC5\xF1lL*t}\xB0\xB2\x94E\x9B\xEE\xEF+\xDB\x89\xC7\xBF\x8F\x7F\x84C\xA8\xD3\xD4\xD1\xFC\xEA\xA0B\xB2{ \xE0`Q8Z!\x1D@\x8C\xE3J\xAA\xA5<\xD4\x86:\x86(\xA9\x84A\x8Fm\x9E\xC0I\xBA\xD7\xBF\xA3\xDA\xAEw3t\xD8\x1DmN\xEF\xE4\xFC'\x8F.\xD4\xEA\x12\xF3\xFB`\x19\xB7N\x9A\x951\xA9\xE7\xB0bw)a+f{\xE4\x86\b!\x1F\xF5HvV3Q\xCB\x1E\b\x82\xB0GYj\x15\xEC\x83\xDFX\x05=\xFBZ\xE4\xA4\xD7\xE5\xFFl\xA9\xD9\xD3\xBB2-\x04WkM\x9B\r\xCD[\n\xE6\xE8%\xEB\x01\x87I4[pK{\xA1\x9E[\xE3\xD9\x8F\x1E\xF9\xB9E\x1E\x90\x97,\xD7\xB7c\x95\x02!\xB2\x99\xF5No\x9B\x92\xA4\xD4\x86\xF9\xB2u\x16\xCD\xCFQ\x0F\xE7u\xB1\xB4\xE7\xCD\r\xA3\xE9\x00\xB9ge\xD7\xFD\xB9\x7F\x12\x9C\raN\xD9j\xBD\x14r-\x9A\x9B\xBD\xA0\x95\xD6\xF3\xA9'0S\xF6\xE2\x9F+\x05\e\x18@F0\xFB\xC0\xF9\xD9\xD0\xC5l\xB9\xB4^'\xEF\x06\x88.\x95\xA6\xFE>\xDF#\xA7+\xEA\xC8\x19&\xD0\xBA\xEC\x0EB\rO\xD2\x9E\xB1\xEBf\xF5\xC5\rzE{\xBAS\xA7;S\n^\xD1\xC16\xB4\xEA\xEA\bm6\xF6\x18\xBF}\xB3\xFB\aD\t\x1C\xBA\xE0\a\x00\x00"
+ read 801 bytes
+ Conn close
+ )
+ end
+
+ def successful_purchase_response
+ %(
+ {
+ "id": "ch_90Vjq8TrwfP74XJO",
+ "code": "ME0KIN4A0O",
+ "gateway_id": "162bead8-23a0-4708-b687-078a69a1aa7c",
+ "amount": 100,
+ "paid_amount": 100,
+ "status": "paid",
+ "currency": "USD",
+ "payment_method": "credit_card",
+ "paid_at": "2018-02-01T18:41:05Z",
+ "created_at": "2018-02-01T18:41:04Z",
+ "updated_at": "2018-02-01T18:41:04Z",
+ "customer": {
+ "id": "cus_VxJX2NmTqyUnXgL9",
+ "name": "Longbob Longsen",
+ "email": "",
+ "delinquent": false,
+ "created_at": "2018-02-01T18:41:04Z",
+ "updated_at": "2018-02-01T18:41:04Z",
+ "phones": {}
+ },
+ "last_transaction": {
+ "id": "tran_JNzjzadcVZHlG8K2",
+ "transaction_type": "credit_card",
+ "gateway_id": "c579c8fa-53d7-41a8-b4cc-a03c712ebbb7",
+ "amount": 100,
+ "status": "captured",
+ "success": true,
+ "installments": 1,
+ "acquirer_name": "simulator",
+ "acquirer_affiliation_code": "",
+ "acquirer_tid": "198548",
+ "acquirer_nsu": "866277",
+ "acquirer_auth_code": "713736",
+ "acquirer_message": "Simulator|Transação de simulação autorizada com sucesso",
+ "acquirer_return_code": "0",
+ "operation_type": "auth_and_capture",
+ "card": {
+ "id": "card_pD02Q6WtOTB7a3kE",
+ "first_six_digits": "400010",
+ "last_four_digits": "2224",
+ "brand": "Visa",
+ "holder_name": "Longbob Longsen",
+ "exp_month": 9,
+ "exp_year": 2019,
+ "status": "active",
+ "created_at": "2018-02-01T18:41:04Z",
+ "updated_at": "2018-02-01T18:41:04Z",
+ "billing_address": {
+ "street": "My Street",
+ "number": "456",
+ "zip_code": "K1C2N6",
+ "neighborhood": "Sesame Street",
+ "city": "Ottawa",
+ "state": "ON",
+ "country": "CA",
+ "line_1": "456, My Street, Sesame Street"
+ },
+ "type": "credit"
+ },
+ "created_at": "2018-02-01T18:41:04Z",
+ "updated_at": "2018-02-01T18:41:04Z",
+ "gateway_response": {
+ "code": "201"
+ }
+ }
+ }
+ )
+ end
+
+ def failed_purchase_response
+ %(
+ {
+ "id": "ch_ykXLG3RfVfNE4dZe",
+ "code": "3W80HGVS0R",
+ "gateway_id": "79ae6732-1b60-4008-80f5-0d1be8ec41a7",
+ "amount": 105200,
+ "status": "failed",
+ "currency": "USD",
+ "payment_method": "credit_card",
+ "created_at": "2018-02-01T18:42:44Z",
+ "updated_at": "2018-02-01T18:42:45Z",
+ "customer": {
+ "id": "cus_0JnywlzI3hV6ZNe2",
+ "name": "Longbob Longsen",
+ "email": "",
+ "delinquent": false,
+ "created_at": "2018-02-01T18:42:44Z",
+ "updated_at": "2018-02-01T18:42:44Z",
+ "phones": {}
+ },
+ "last_transaction": {
+ "id": "tran_nVx8730IjhOR8PD2",
+ "transaction_type": "credit_card",
+ "gateway_id": "f3993413-73a0-4e8d-a7bc-eb3ed198c770",
+ "amount": 105200,
+ "status": "not_authorized",
+ "success": false,
+ "installments": 1,
+ "acquirer_name": "simulator",
+ "acquirer_affiliation_code": "",
+ "acquirer_message": "Simulator|Transação de simulada negada por falta de crédito, utilizado para realizar simulação de autorização parcial.",
+ "acquirer_return_code": "92",
+ "operation_type": "auth_and_capture",
+ "card": {
+ "id": "card_VrxnWlrsOHOpzMj5",
+ "first_six_digits": "400030",
+ "last_four_digits": "2220",
+ "brand": "Visa",
+ "holder_name": "Longbob Longsen",
+ "exp_month": 9,
+ "exp_year": 2019,
+ "status": "active",
+ "created_at": "2018-02-01T18:42:44Z",
+ "updated_at": "2018-02-01T18:42:44Z",
+ "billing_address": {
+ "street": "My Street",
+ "number": "456",
+ "zip_code": "K1C2N6",
+ "neighborhood": "Sesame Street",
+ "city": "Ottawa",
+ "state": "ON",
+ "country": "CA",
+ "line_1": "456, My Street, Sesame Street"
+ },
+ "type": "credit"
+ },
+ "created_at": "2018-02-01T18:42:44Z",
+ "updated_at": "2018-02-01T18:42:44Z",
+ "gateway_response": {
+ "code": "201"
+ }
+ }
+ }
+ )
+ end
+
+ def successful_authorize_response
+ %(
+ {
+ "id": "ch_gm5wrlGMI2Fb0x6K",
+ "code": "K1J5B1YFLE",
+ "gateway_id": "3b6c0f72-c4b3-48b2-8eb7-2424321a6c93",
+ "amount": 100,
+ "status": "pending",
+ "currency": "USD",
+ "payment_method": "credit_card",
+ "created_at": "2018-02-01T16:43:30Z",
+ "updated_at": "2018-02-01T16:43:30Z",
+ "customer": {
+ "id": "cus_bVWYqeTmpu9VYLd9",
+ "name": "Longbob Longsen",
+ "email": "",
+ "delinquent": false,
+ "created_at": "2018-02-01T16:43:30Z",
+ "updated_at": "2018-02-01T16:43:30Z",
+ "phones": {}
+ },
+ "last_transaction": {
+ "id": "tran_DWJlEApZI9UL2qR9",
+ "transaction_type": "credit_card",
+ "gateway_id": "6dae95a7-6b7f-4431-be33-cb3ecf21287a",
+ "amount": 100,
+ "status": "authorized_pending_capture",
+ "success": true,
+ "installments": 1,
+ "acquirer_name": "simulator",
+ "acquirer_affiliation_code": "",
+ "acquirer_tid": "25970",
+ "acquirer_nsu": "506128",
+ "acquirer_auth_code": "523448",
+ "acquirer_message": "Simulator|Transação de simulação autorizada com sucesso",
+ "acquirer_return_code": "0",
+ "operation_type": "auth_only",
+ "card": {
+ "id": "card_J26O3K2hvPc2vOQG",
+ "first_six_digits": "400010",
+ "last_four_digits": "2224",
+ "brand": "Visa",
+ "holder_name": "Longbob Longsen",
+ "exp_month": 9,
+ "exp_year": 2019,
+ "status": "active",
+ "created_at": "2018-02-01T16:43:30Z",
+ "updated_at": "2018-02-01T16:43:30Z",
+ "billing_address": {
+ "street": "My Street",
+ "number": "456",
+ "zip_code": "K1C2N6",
+ "neighborhood": "Sesame Street",
+ "city": "Ottawa",
+ "state": "ON",
+ "country": "CA",
+ "line_1": "456, My Street, Sesame Street"
+ },
+ "type": "credit"
+ },
+ "created_at": "2018-02-01T16:43:31Z",
+ "updated_at": "2018-02-01T16:43:31Z",
+ "gateway_response": {
+ "code": "201"
+ }
+ }
+ }
+ )
+ end
+
+ def failed_authorize_response
+ %(
+ {
+ "id": "ch_O4bW13ukwF5XpmLg",
+ "code": "2KW1C5VSZO",
+ "gateway_id": "9bf24ea7-e913-44bc-92ca-50491ffcd7a1",
+ "amount": 105200,
+ "status": "failed",
+ "currency": "USD",
+ "payment_method": "credit_card",
+ "created_at": "2018-02-01T18:46:06Z",
+ "updated_at": "2018-02-01T18:46:06Z",
+ "customer": {
+ "id": "cus_7VGAGxqI4OUwZ392",
+ "name": "Longbob Longsen",
+ "email": "",
+ "delinquent": false,
+ "created_at": "2018-02-01T18:46:06Z",
+ "updated_at": "2018-02-01T18:46:06Z",
+ "phones": {}
+ },
+ "last_transaction": {
+ "id": "tran_g0JYdDXcDesqd36E",
+ "transaction_type": "credit_card",
+ "gateway_id": "c0896dd6-0d5c-4e8b-9d92-df5a70e3fb76",
+ "amount": 105200,
+ "status": "not_authorized",
+ "success": false,
+ "installments": 1,
+ "acquirer_name": "simulator",
+ "acquirer_affiliation_code": "",
+ "acquirer_message": "Simulator|Transação de simulada negada por falta de crédito, utilizado para realizar simulação de autorização parcial.",
+ "acquirer_return_code": "92",
+ "operation_type": "auth_only",
+ "card": {
+ "id": "card_LR0A1vcVbsmY3wzY",
+ "first_six_digits": "400030",
+ "last_four_digits": "2220",
+ "brand": "Visa",
+ "holder_name": "Longbob Longsen",
+ "exp_month": 9,
+ "exp_year": 2019,
+ "status": "active",
+ "created_at": "2018-02-01T18:46:06Z",
+ "updated_at": "2018-02-01T18:46:06Z",
+ "billing_address": {
+ "street": "My Street",
+ "number": "456",
+ "zip_code": "K1C2N6",
+ "neighborhood": "Sesame Street",
+ "city": "Ottawa",
+ "state": "ON",
+ "country": "CA",
+ "line_1": "456, My Street, Sesame Street"
+ },
+ "type": "credit"
+ },
+ "created_at": "2018-02-01T18:46:06Z",
+ "updated_at": "2018-02-01T18:46:06Z",
+ "gateway_response": {
+ "code": "201"
+ }
+ }
+ }
+ )
+ end
+
+ def successful_capture_response
+ %(
+ {
+ "id": "ch_gm5wrlGMI2Fb0x6K",
+ "code": "ch_gm5wrlGMI2Fb0x6K",
+ "gateway_id": "3b6c0f72-c4b3-48b2-8eb7-2424321a6c93",
+ "amount": 100,
+ "paid_amount": 100,
+ "status": "paid",
+ "currency": "USD",
+ "payment_method": "credit_card",
+ "paid_at": "2018-02-01T16:43:33Z",
+ "created_at": "2018-02-01T16:43:30Z",
+ "updated_at": "2018-02-01T16:43:33Z",
+ "customer": {
+ "id": "cus_bVWYqeTmpu9VYLd9",
+ "name": "Longbob Longsen",
+ "email": "",
+ "delinquent": false,
+ "created_at": "2018-02-01T16:43:30Z",
+ "updated_at": "2018-02-01T16:43:30Z",
+ "phones": {}
+ },
+ "last_transaction": {
+ "id": "tran_wL9APd6cws19WNJ7",
+ "transaction_type": "credit_card",
+ "gateway_id": "6dae95a7-6b7f-4431-be33-cb3ecf21287a",
+ "amount": 100,
+ "status": "captured",
+ "success": true,
+ "installments": 1,
+ "acquirer_name": "simulator",
+ "acquirer_affiliation_code": "",
+ "acquirer_tid": "299257",
+ "acquirer_nsu": "894685",
+ "acquirer_auth_code": "523448",
+ "acquirer_message": "Simulator|Transação de simulação capturada com sucesso",
+ "acquirer_return_code": "0",
+ "operation_type": "capture",
+ "card": {
+ "id": "card_J26O3K2hvPc2vOQG",
+ "first_six_digits": "400010",
+ "last_four_digits": "2224",
+ "brand": "Visa",
+ "holder_name": "Longbob Longsen",
+ "exp_month": 9,
+ "exp_year": 2019,
+ "status": "active",
+ "created_at": "2018-02-01T16:43:30Z",
+ "updated_at": "2018-02-01T16:43:30Z",
+ "billing_address": {
+ "street": "My Street",
+ "number": "456",
+ "zip_code": "K1C2N6",
+ "neighborhood": "Sesame Street",
+ "city": "Ottawa",
+ "state": "ON",
+ "country": "CA",
+ "line_1": "456, My Street, Sesame Street"
+ },
+ "type": "credit"
+ },
+ "created_at": "2018-02-01T16:43:33Z",
+ "updated_at": "2018-02-01T16:43:33Z",
+ "gateway_response": {
+ "code": "200"
+ }
+ }
+ }
+ )
+ end
+
+ def failed_capture_response
+ '{"message": "Charge not found."}'
+ end
+
+ def successful_refund_response
+ %(
+ {
+ "id": "ch_RbPVPWMH2bcGA50z",
+ "code": "O5L5A4VCRK",
+ "gateway_id": "d77c6a32-e1c8-42d4-bd1b-e92b36f054f9",
+ "amount": 100,
+ "canceled_amount": 100,
+ "status": "canceled",
+ "currency": "USD",
+ "payment_method": "credit_card",
+ "canceled_at": "2018-02-01T16:34:07Z",
+ "created_at": "2018-02-01T16:34:07Z",
+ "updated_at": "2018-02-01T16:34:07Z",
+ "customer": {
+ "id": "cus_odYDGxQirlcp693a",
+ "name": "Longbob Longsen",
+ "email": "",
+ "delinquent": false,
+ "created_at": "2018-02-01T16:34:07Z",
+ "updated_at": "2018-02-01T16:34:07Z",
+ "phones": {}
+ },
+ "last_transaction": {
+ "id": "tran_m1prZBNTgUmZrGzZ",
+ "transaction_type": "credit_card",
+ "gateway_id": "23648dca-07dc-4f31-9b24-26aa702dc7e8",
+ "amount": 100,
+ "status": "voided",
+ "success": true,
+ "acquirer_name": "simulator",
+ "acquirer_affiliation_code": "",
+ "acquirer_tid": "489627",
+ "acquirer_nsu": "174061",
+ "acquirer_auth_code": "433589",
+ "acquirer_return_code": "0",
+ "operation_type": "cancel",
+ "card": {
+ "id": "card_8PaGBMOhXwi9Q24z",
+ "first_six_digits": "400010",
+ "last_four_digits": "2224",
+ "brand": "Visa",
+ "holder_name": "Longbob Longsen",
+ "exp_month": 9,
+ "exp_year": 2019,
+ "status": "active",
+ "created_at": "2018-02-01T16:34:07Z",
+ "updated_at": "2018-02-01T16:34:07Z",
+ "billing_address": {
+ "street": "My Street",
+ "number": "456",
+ "zip_code": "K1C2N6",
+ "neighborhood": "Sesame Street",
+ "city": "Ottawa",
+ "state": "ON",
+ "country": "CA",
+ "line_1": "456, My Street, Sesame Street"
+ },
+ "type": "credit"
+ },
+ "created_at": "2018-02-01T16:34:07Z",
+ "updated_at": "2018-02-01T16:34:07Z",
+ "gateway_response": {
+ "code": "200"
+ }
+ }
+ }
+ )
+ end
+
+ def failed_refund_response
+ '{"message": "Charge not found."}'
+ end
+
+ def successful_void_response
+ %(
+ {
+ "id": "ch_RbPVPWMH2bcGA50z",
+ "code": "O5L5A4VCRK",
+ "gateway_id": "d77c6a32-e1c8-42d4-bd1b-e92b36f054f9",
+ "amount": 100,
+ "canceled_amount": 100,
+ "status": "canceled",
+ "currency": "USD",
+ "payment_method": "credit_card",
+ "canceled_at": "2018-02-01T16:34:07Z",
+ "created_at": "2018-02-01T16:34:07Z",
+ "updated_at": "2018-02-01T16:34:07Z",
+ "customer": {
+ "id": "cus_odYDGxQirlcp693a",
+ "name": "Longbob Longsen",
+ "email": "",
+ "delinquent": false,
+ "created_at": "2018-02-01T16:34:07Z",
+ "updated_at": "2018-02-01T16:34:07Z",
+ "phones": {}
+ },
+ "last_transaction": {
+ "id": "tran_m1prZBNTgUmZrGzZ",
+ "transaction_type": "credit_card",
+ "gateway_id": "23648dca-07dc-4f31-9b24-26aa702dc7e8",
+ "amount": 100,
+ "status": "voided",
+ "success": true,
+ "acquirer_name": "simulator",
+ "acquirer_affiliation_code": "",
+ "acquirer_tid": "489627",
+ "acquirer_nsu": "174061",
+ "acquirer_auth_code": "433589",
+ "acquirer_return_code": "0",
+ "operation_type": "cancel",
+ "card": {
+ "id": "card_8PaGBMOhXwi9Q24z",
+ "first_six_digits": "400010",
+ "last_four_digits": "2224",
+ "brand": "Visa",
+ "holder_name": "Longbob Longsen",
+ "exp_month": 9,
+ "exp_year": 2019,
+ "status": "active",
+ "created_at": "2018-02-01T16:34:07Z",
+ "updated_at": "2018-02-01T16:34:07Z",
+ "billing_address": {
+ "street": "My Street",
+ "number": "456",
+ "zip_code": "K1C2N6",
+ "neighborhood": "Sesame Street",
+ "city": "Ottawa",
+ "state": "ON",
+ "country": "CA",
+ "line_1": "456, My Street, Sesame Street"
+ },
+ "type": "credit"
+ },
+ "created_at": "2018-02-01T16:34:07Z",
+ "updated_at": "2018-02-01T16:34:07Z",
+ "gateway_response": {
+ "code": "200"
+ }
+ }
+ }
+ )
+ end
+
+ def failed_void_response
+ '{"message": "Charge not found."}'
+ end
+
+ def successful_verify_response
+ %(
+ {
+ "id": "ch_G9rB74PI3uoDMxAo",
+ "code": "8EXXFEBQDK",
+ "gateway_id": "4b228be6-6795-416a-9238-204020e7fdd1",
+ "amount": 100,
+ "canceled_amount": 100,
+ "status": "canceled",
+ "currency": "USD",
+ "payment_method": "credit_card",
+ "canceled_at": "2018-02-02T14:25:25Z",
+ "created_at": "2018-02-02T14:25:24Z",
+ "updated_at": "2018-02-02T14:25:25Z",
+ "customer": {
+ "id": "cus_V2GXxeOunSYpEvOD",
+ "name": "Longbob Longsen",
+ "email": "",
+ "delinquent": false,
+ "created_at": "2018-02-02T14:25:24Z",
+ "updated_at": "2018-02-02T14:25:24Z",
+ "phones": {}
+ },
+ "last_transaction": {
+ "id": "tran_r50yVePSJbuA3dKb",
+ "transaction_type": "credit_card",
+ "gateway_id": "2d1c155a-e89d-4972-9ee1-a3b3f56d6ff8",
+ "amount": 100,
+ "status": "voided",
+ "success": true,
+ "acquirer_name": "simulator",
+ "acquirer_affiliation_code": "",
+ "acquirer_tid": "711106",
+ "acquirer_nsu": "553868",
+ "acquirer_auth_code": "271719",
+ "acquirer_return_code": "0",
+ "operation_type": "cancel",
+ "card": {
+ "id": "card_7WraOEps6FpRLQob",
+ "first_six_digits": "400010",
+ "last_four_digits": "2224",
+ "brand": "Visa",
+ "holder_name": "Longbob Longsen",
+ "exp_month": 9,
+ "exp_year": 2019,
+ "status": "active",
+ "created_at": "2018-02-02T14:25:24Z",
+ "updated_at": "2018-02-02T14:25:24Z",
+ "billing_address": {
+ "street": "My Street",
+ "number": "456",
+ "zip_code": "K1C2N6",
+ "neighborhood": "Sesame Street",
+ "city": "Ottawa",
+ "state": "ON",
+ "country": "CA",
+ "line_1": "456, My Street, Sesame Street"
+ },
+ "type": "credit"
+ },
+ "created_at": "2018-02-02T14:25:25Z",
+ "updated_at": "2018-02-02T14:25:25Z",
+ "gateway_response": {
+ "code": "200"
+ }
+ }
+ }
+ )
+ end
+
+ def successful_create_customer_response
+ %(
+ {
+ "id": "cus_NRL1bw3HpAHLbWPQ",
+ "name": "Sideshow Bob",
+ "email": "",
+ "delinquent": false,
+ "created_at": "2018-02-05T15:03:39Z",
+ "updated_at": "2018-02-05T15:03:39Z",
+ "phones": {}
+ }
+ )
+ end
+
+ def successful_store_response
+ %(
+ {
+ "id": "card_51ElNwYSVJFpRe0g",
+ "first_six_digits": "400010",
+ "last_four_digits": "2224",
+ "brand": "Visa",
+ "holder_name": "Longbob Longsen",
+ "exp_month": 9,
+ "exp_year": 2019,
+ "status": "active",
+ "created_at": "2018-02-05T15:45:01Z",
+ "updated_at": "2018-02-05T15:45:01Z",
+ "billing_address": {
+ "street": "My Street",
+ "number": "456",
+ "zip_code": "K1C2N6",
+ "neighborhood": "Sesame Street",
+ "city": "Ottawa",
+ "state": "ON",
+ "country": "CA",
+ "line_1": "456, My Street, Sesame Street"
+ },
+ "customer": {
+ "id": "cus_N70xAX6S65cMnokB",
+ "name": "Bob Belcher",
+ "email": "",
+ "delinquent": false,
+ "created_at": "2018-02-05T15:45:01Z",
+ "updated_at": "2018-02-05T15:45:01Z",
+ "phones": {}
+ },
+ "type": "credit"
+ }
+ )
+ end
+end
diff --git a/test/unit/gateways/nab_transact_test.rb b/test/unit/gateways/nab_transact_test.rb
index f4fde96cfff..a10e2a44ee6 100644
--- a/test/unit/gateways/nab_transact_test.rb
+++ b/test/unit/gateways/nab_transact_test.rb
@@ -153,8 +153,59 @@ def test_override_request_timeout
end.respond_with(successful_purchase_response)
end
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
private
+ def pre_scrubbed
+ <<-'PRE_SCRUBBED'
+opening connection to transact.nab.com.au:443...
+opened
+starting SSL for transact.nab.com.au:443...
+SSL established
+<- "POST /test/xmlapi/payment HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: transact.nab.com.au\r\nContent-Length: 715\r\n\r\n"
+<- "6673348a21d79657983ab247b2483e20151212075932886818+00060xml-4.2XYZ0010abcd1234Payment023200AUD444433332222111105/17111"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Sat, 12 Dec 2015 07:59:34 GMT\r\n"
+-> "Server: Apache-Coyote/1.1\r\n"
+-> "Content-Type: text/xml;charset=ISO-8859-1\r\n"
+-> "Content-Length: 920\r\n"
+-> "Connection: close\r\n"
+-> "\r\n"
+reading 920 bytes...
+-> ""
+-> "6673348a21d79657983ab247b2483e20151212185934964000+660xml-4.2PaymentXYZ0010000Normal023200AUDNo103Invalid Purchase Order Number444433...11105/176Visa"
+read 920 bytes
+Conn close
+ PRE_SCRUBBED
+ end
+
+ def post_scrubbed
+ <<-'POST_SCRUBBED'
+opening connection to transact.nab.com.au:443...
+opened
+starting SSL for transact.nab.com.au:443...
+SSL established
+<- "POST /test/xmlapi/payment HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: transact.nab.com.au\r\nContent-Length: 715\r\n\r\n"
+<- "6673348a21d79657983ab247b2483e20151212075932886818+00060xml-4.2XYZ0010[FILTERED]Payment023200AUD[FILTERED]05/17[FILTERED]"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Sat, 12 Dec 2015 07:59:34 GMT\r\n"
+-> "Server: Apache-Coyote/1.1\r\n"
+-> "Content-Type: text/xml;charset=ISO-8859-1\r\n"
+-> "Content-Length: 920\r\n"
+-> "Connection: close\r\n"
+-> "\r\n"
+reading 920 bytes...
+-> ""
+-> "6673348a21d79657983ab247b2483e20151212185934964000+660xml-4.2PaymentXYZ0010000Normal023200AUDNo103Invalid Purchase Order Number444433...11105/176Visa"
+read 920 bytes
+Conn close
+ POST_SCRUBBED
+ end
+
def check_transaction_type(type)
Proc.new do |endpoint, data, headers|
request_hash = Hash.from_xml(data)
diff --git a/test/unit/gateways/netbanx_test.rb b/test/unit/gateways/netbanx_test.rb
index e1130d469b7..56a74c156aa 100644
--- a/test/unit/gateways/netbanx_test.rb
+++ b/test/unit/gateways/netbanx_test.rb
@@ -9,7 +9,8 @@ def setup
@options = {
order_id: '1',
billing_address: address,
- description: 'Store Purchase'
+ description: 'Store Purchase',
+ currency: 'CAD'
}
end
@@ -139,17 +140,18 @@ def test_successful_purchase_with_token
end
def test_successful_unstore
- @gateway.expects(:ssl_request).twice.returns(successful_unstore_response)
+ @gateway.expects(:ssl_request).twice.returns(successful_unstore_response)
- response = @gateway.unstore('2f840ab3-0e71-4387-bad3-4705e6f4b015|e4a3cd5a-56db-4d9b-97d3-fdd9ab3bd0f4')
- assert_success response
- assert response.test?
+ response = @gateway.unstore('2f840ab3-0e71-4387-bad3-4705e6f4b015|e4a3cd5a-56db-4d9b-97d3-fdd9ab3bd0f4')
+ assert_success response
+ assert response.test?
response = @gateway.unstore('2f840ab3-0e71-4387-bad3-4705e6f4b015')
assert_success response
assert response.test?
end
+
def test_scrub
assert @gateway.supports_scrubbing?
assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
@@ -604,7 +606,32 @@ def successful_store_response
RESPONSE
end
- # just returns a 200 when successful
def successful_unstore_response
+ <<-RESPONSE
+ {
+ "id": "2f840ab3-0e71-4387-bad3-4705e6f4b015",
+ "status": "ACTIVE",
+ "merchantCustomerId": "5e9d1ab0f847d147ffe872a9faf76d98",
+ "locale": "en_GB",
+ "paymentToken": "PJzuA8s6c6pSIs4",
+ "addresses": [],
+ "cards": [
+ {
+ "status": "ACTIVE",
+ "id": "e4a3cd5a-56db-4d9b-97d3-fdd9ab3bd0f4",
+ "cardBin": "453091",
+ "lastDigits": "2345",
+ "cardExpiry": {
+ "year": 2017,
+ "month": 9
+ },
+ "holderName": "Longbob Longsen",
+ "cardType": "VI",
+ "paymentToken": "C6gmdUA1xWT8RsC",
+ "defaultCardIndicator": true
+ }
+ ]
+ }
+ RESPONSE
end
end
diff --git a/test/unit/gateways/nmi_test.rb b/test/unit/gateways/nmi_test.rb
index c2d01a04cef..e5ad32ae22c 100644
--- a/test/unit/gateways/nmi_test.rb
+++ b/test/unit/gateways/nmi_test.rb
@@ -70,6 +70,8 @@ def test_successful_purchase_with_echeck
assert_match(/type=sale/, data)
assert_match(/amount=1.00/, data)
assert_match(/payment=check/, data)
+ assert_match(/firstname=#{@check.first_name}/, data)
+ assert_match(/lastname=#{@check.last_name}/, data)
assert_match(/checkname=#{@check.name}/, CGI.unescape(data))
assert_match(/checkaba=#{@check.routing_number}/, data)
assert_match(/checkaccount=#{@check.account_number}/, data)
diff --git a/test/unit/gateways/ogone_test.rb b/test/unit/gateways/ogone_test.rb
index 6bae8080557..c6487bf5664 100644
--- a/test/unit/gateways/ogone_test.rb
+++ b/test/unit/gateways/ogone_test.rb
@@ -115,11 +115,11 @@ def test_successful_authorize
def test_successful_authorize_with_mastercard
@gateway.expects(:add_pair).at_least(1)
- @gateway.expects(:add_pair).with(anything, 'Operation', 'PAU')
+ @gateway.expects(:add_pair).with(anything, 'Operation', 'RES')
@gateway.expects(:ssl_post).returns(successful_purchase_response)
assert response = @gateway.authorize(@amount, @mastercard, @options)
assert_success response
- assert_equal '3014726;PAU', response.authorization
+ assert_equal '3014726;RES', response.authorization
assert response.test?
end
@@ -445,6 +445,11 @@ def test_response_params_is_hash
assert_instance_of Hash, response.params
end
+ def test_transcript_scrubbing
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrub), post_scrub
+ end
+
private
def string_to_digest
@@ -754,4 +759,52 @@ def failed_authorization_response
END
end
+ def pre_scrub
+ %q{
+opening connection to secure.ogone.com:443...
+opened
+starting SSL for secure.ogone.com:443...
+SSL established
+<- "POST /ncol/test/orderdirect.asp HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: secure.ogone.com\r\nContent-Length: 455\r\n\r\n"
+<- "CARDNO=4000100011112224&CN=Longbob+Longsen&COM=Store+Purchase&CVC=123&ECI=7&ED=0919&Operation=SAL&OwnerZip=K1C2N6&Owneraddress=456+My+Street&PSPID=spreedlyinc&PSWD=spreedly1test&SHASign=A67038AB141C6E54C51315F993DC83F5C28A9E585C6C8A79346F802E6557C0C8EE233A5FF1352AAD3C6AA5D476CF49F2B0DF512C63BA624F0583B72C1DCABCEF&USERID=spreedlytest&amount=100¤cy=EUR&orderID=7de271d36c1c36999a6039d99179b2&ownercty=CA&ownertelno=%28555%29555-5555&ownertown=Ottawa"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Cache-Control: private, max-age=0\r\n"
+-> "Content-Length: 152\r\n"
+-> "Content-Type: text/XML; Charset=iso-8859-1\r\n"
+-> "Expires: Mon, 08 Jan 2018 18:13:04 GMT\r\n"
+-> "Strict-Transport-Security: max-age=31536000;includeSubdomains\r\n"
+-> "Date: Mon, 08 Jan 2018 18:14:05 GMT\r\n"
+-> "Connection: close\r\n"
+-> "\r\n"
+reading 152 bytes...
+-> "\r\n"
+read 152 bytes
+Conn close
+ }
+ end
+
+ def post_scrub
+ %q{
+opening connection to secure.ogone.com:443...
+opened
+starting SSL for secure.ogone.com:443...
+SSL established
+<- "POST /ncol/test/orderdirect.asp HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: secure.ogone.com\r\nContent-Length: 455\r\n\r\n"
+<- "CARDNO=[FILTERED]&CN=Longbob+Longsen&COM=Store+Purchase&CVC=[FILTERED]&ECI=7&ED=0919&Operation=SAL&OwnerZip=K1C2N6&Owneraddress=456+My+Street&PSPID=spreedlyinc&PSWD=[FILTERED]&SHASign=A67038AB141C6E54C51315F993DC83F5C28A9E585C6C8A79346F802E6557C0C8EE233A5FF1352AAD3C6AA5D476CF49F2B0DF512C63BA624F0583B72C1DCABCEF&USERID=spreedlytest&amount=100¤cy=EUR&orderID=7de271d36c1c36999a6039d99179b2&ownercty=CA&ownertelno=%28555%29555-5555&ownertown=Ottawa"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Cache-Control: private, max-age=0\r\n"
+-> "Content-Length: 152\r\n"
+-> "Content-Type: text/XML; Charset=iso-8859-1\r\n"
+-> "Expires: Mon, 08 Jan 2018 18:13:04 GMT\r\n"
+-> "Strict-Transport-Security: max-age=31536000;includeSubdomains\r\n"
+-> "Date: Mon, 08 Jan 2018 18:14:05 GMT\r\n"
+-> "Connection: close\r\n"
+-> "\r\n"
+reading 152 bytes...
+-> "\r\n"
+read 152 bytes
+Conn close
+ }
+ end
+
end
diff --git a/test/unit/gateways/omise_test.rb b/test/unit/gateways/omise_test.rb
index 204592e67c2..fbf75054d7a 100644
--- a/test/unit/gateways/omise_test.rb
+++ b/test/unit/gateways/omise_test.rb
@@ -23,7 +23,11 @@ def setup
end
def test_supported_countries
- assert_equal @gateway.supported_countries, %w( TH )
+ assert_equal @gateway.supported_countries, %w( TH JP )
+ end
+
+ def test_supported_cardtypes
+ assert_equal @gateway.supported_cardtypes, [:visa, :master, :jcb]
end
def test_supports_scrubbing
@@ -153,6 +157,13 @@ def test_add_amount
assert_equal desc, result[:description]
end
+ def test_add_amount_with_correct_currency
+ result = {}
+ jpy_currency = 'JPY'
+ @gateway.send(:add_amount, result, @amount, {currency: jpy_currency})
+ assert_equal jpy_currency, result[:currency]
+ end
+
def test_commit_transaction
@gateway.expects(:ssl_request).returns(successful_purchase_response)
response = @gateway.send(:commit, :post, 'charges', {})
diff --git a/test/unit/gateways/opp_test.rb b/test/unit/gateways/opp_test.rb
index 8225e3c7a91..783d34e887c 100644
--- a/test/unit/gateways/opp_test.rb
+++ b/test/unit/gateways/opp_test.rb
@@ -1,6 +1,8 @@
require 'test_helper'
class OppTest < Test::Unit::TestCase
+ include CommStub
+
def setup
@gateway = OppGateway.new(fixtures(:opp))
@amount = 100
@@ -8,7 +10,7 @@ def setup
@valid_card = credit_card("4200000000000000", month: 05, year: 2018, verification_value: '123')
@invalid_card = credit_card("4444444444444444", month: 05, year: 2018, verification_value: '123')
- request_type = 'complete' # 'minimal' || 'complete'
+ request_type = 'complete' # 'minimal' || 'complete'
time = Time.now.to_i
ip = '101.102.103.104'
@complete_request_options = {
@@ -49,23 +51,23 @@ def setup
ip: ip,
},
}
-
+
@minimal_request_options = {
order_id: "Order #{time}",
description: 'Store Purchase - Books',
}
- @complete_request_options['customParameters[SHOPPER_test124TestName009]'] = 'customParameters_test'
- @complete_request_options['customParameters[SHOPPER_otherCustomerParameter]'] = 'otherCustomerParameter_test'
+ @complete_request_options['customParameters[SHOPPER_test124TestName009]'] = 'customParameters_test'
+ @complete_request_options['customParameters[SHOPPER_otherCustomerParameter]'] = 'otherCustomerParameter_test'
@test_success_id = "8a82944a4e008ca9014e1273e0696122"
@test_failure_id = "8a8294494e0078a6014e12b371fb6a8e"
-
+
@options = @minimal_request_options if request_type == 'minimal'
@options = @complete_request_options if request_type == 'complete'
end
-
-# ****************************************** SUCCESSFUL TESTS ******************************************
+
+# ****************************************** SUCCESSFUL TESTS ******************************************
def test_successful_purchase
@gateway.expects(:raw_ssl_request).returns(successful_response('DB', @test_success_id))
response = @gateway.purchase(@amount, @valid_card, @options)
@@ -77,7 +79,7 @@ def test_successful_purchase
def test_successful_authorize
@gateway.expects(:raw_ssl_request).returns(successful_response('PA', @test_success_id))
response = @gateway.authorize(@amount, @valid_card, @options)
- assert_success response, "Authorization Failed"
+ assert_success response, "Authorization Failed"
assert_equal @test_success_id, response.authorization
assert response.test?
end
@@ -98,7 +100,7 @@ def test_successful_capture
def test_successful_refund
@gateway.expects(:raw_ssl_request).returns(successful_response('DB', @test_success_id))
purchase = @gateway.purchase(@amount, @valid_card, @options)
- assert_success purchase, "Purchase failed"
+ assert_success purchase, "Purchase failed"
assert purchase.test?
@gateway.expects(:raw_ssl_request).returns(successful_response('RF', @test_success_id))
refund = @gateway.refund(@amount, purchase.authorization, @options)
@@ -110,7 +112,7 @@ def test_successful_refund
def test_successful_void
@gateway.expects(:raw_ssl_request).returns(successful_response('DB', @test_success_id))
purchase = @gateway.purchase(@amount, @valid_card, @options)
- assert_success purchase, "Purchase failed"
+ assert_success purchase, "Purchase failed"
assert purchase.test?
@gateway.expects(:raw_ssl_request).returns(successful_response('RV', @test_success_id))
void = @gateway.void(purchase.authorization, @options)
@@ -119,40 +121,54 @@ def test_successful_void
assert void.test?
end
-# ****************************************** FAILURE TESTS ******************************************
+# ****************************************** FAILURE TESTS ******************************************
def test_failed_purchase
@gateway.expects(:raw_ssl_request).returns(failed_response('DB', @test_failure_id))
response = @gateway.purchase(@amount, @invalid_card, @options)
assert_failure response
- assert_equal Gateway::STANDARD_ERROR_CODE[:incorrect_number], response.error_code
+ assert_equal '100.100.101', response.error_code
end
def test_failed_authorize
@gateway.expects(:raw_ssl_request).returns(failed_response('PA', @test_failure_id))
response = @gateway.authorize(@amount, @invalid_card, @options)
assert_failure response
- assert_equal Gateway::STANDARD_ERROR_CODE[:incorrect_number], response.error_code
+ assert_equal '100.100.101', response.error_code
end
def test_failed_capture
@gateway.expects(:raw_ssl_request).returns(failed_response('CP', @test_failure_id))
response = @gateway.capture(@amount, @invalid_card)
assert_failure response
- assert_equal Gateway::STANDARD_ERROR_CODE[:incorrect_number], response.error_code
+ assert_equal '100.100.101', response.error_code
end
def test_failed_refund
@gateway.expects(:raw_ssl_request).returns(failed_response('PF', @test_failure_id))
response = @gateway.refund(@amount, @test_success_id)
assert_failure response
- assert_equal Gateway::STANDARD_ERROR_CODE[:incorrect_number], response.error_code
+ assert_equal '100.100.101', response.error_code
end
def test_failed_void
@gateway.expects(:raw_ssl_request).returns(failed_response('RV', @test_failure_id))
response = @gateway.void(@test_success_id, @options)
assert_failure response
- assert_equal Gateway::STANDARD_ERROR_CODE[:incorrect_number], response.error_code
+ assert_equal '100.100.101', response.error_code
+ end
+
+ def test_passes_3d_secure_fields
+ options = @complete_request_options.merge({eci: "eci", cavv: "cavv", xid: "xid"})
+
+ response = stub_comms(@gateway, :raw_ssl_request) do
+ @gateway.purchase(@amount, @valid_card, options)
+ end.check_request do |method, endpoint, data, headers|
+ assert_match(/threeDSecure.eci=eci/, data)
+ assert_match(/threeDSecure.verificationId=cavv/, data)
+ assert_match(/threeDSecure.xid=xid/, data)
+ end.respond_with(successful_response('DB', @test_success_id))
+
+ assert_success response
end
def test_scrub
@@ -167,11 +183,11 @@ def pre_scrubbed
end
def post_scrubbed
- "paymentType=DB&amount=1.00¤cy=EUR&paymentBrand=VISA&card.holder=Longbob+Longsen&card.number=[FILTERED]&card.expiryMonth=05&card.expiryYear=2018& card.cvv=[FILTERED]&billing.street1=456+My+Street&billing.street2=Apt+1&billing.city=Ottawa&billing.state=ON&billing.postcode=K1C2N6&billing.country=CA&authentication.entityId=[FILTERED]&authentication.password=[FILTERED]&authentication.userId=[FILTERED]"
+ "paymentType=DB&amount=1.00¤cy=EUR&paymentBrand=VISA&card.holder=Longbob+Longsen&card.number=[FILTERED]&card.expiryMonth=05&card.expiryYear=2018& card.cvv=[FILTERED]&billing.street1=456+My+Street&billing.street2=Apt+1&billing.city=Ottawa&billing.state=ON&billing.postcode=K1C2N6&billing.country=CA&authentication.entityId=8a8294174b7ecb28014b9699220015ca&authentication.password=[FILTERED]&authentication.userId=8a8294174b7ecb28014b9699220015cc"
end
def successful_response(type, id)
- OppMockResponse.new(200,
+ OppMockResponse.new(200,
JSON.generate({"id" => id,"paymentType" => type,"paymentBrand" => "VISA","amount" => "1.00","currency" => "EUR","des
criptor" => "5410.9959.0306 OPP_Channel ","result" => {"code" => "000.100.110","description" => "Request successfully processed in 'Merchant in Integrator Test Mode'"},"card" => {"bin
" => "420000","last4Digits" => "0000","holder" => "Longbob Longsen","expiryMonth" => "05","expiryYear" => "2018"},"buildNumber" => "20150618-111601.r185004.opp-tags-20150618_stage","time
@@ -180,7 +196,7 @@ def successful_response(type, id)
end
def failed_response(type, id, code='100.100.101')
- OppMockResponse.new(400,
+ OppMockResponse.new(400,
JSON.generate({"id" => id,"paymentType" => type,"paymentBrand" => "VISA","result" => {"code" => code,"des
cription" => "invalid creditcard, bank account number or bank name"},"card" => {"bin" => "444444","last4Digits" => "4444","holder" => "Longbob Longsen","expiryMonth" => "05","expiryYear" => "2018"},
"buildNumber" => "20150618-111601.r185004.opp-tags-20150618_stage","timestamp" => "2015-06-20 20:40:26+0000","ndc" => "8a8294174b7ecb28014b9699220015ca_5200332e7d664412a84ed5f4777b3c7d"})
@@ -192,14 +208,14 @@ def initialize(code, body)
@code = code
@body = body
end
-
- def code
+
+ def code
@code
- end
-
- def body
+ end
+
+ def body
@body
- end
+ end
end
end
diff --git a/test/unit/gateways/optimal_payment_test.rb b/test/unit/gateways/optimal_payment_test.rb
index 2beade96c86..d37bc29d476 100644
--- a/test/unit/gateways/optimal_payment_test.rb
+++ b/test/unit/gateways/optimal_payment_test.rb
@@ -128,6 +128,29 @@ def test_purchase_without_billing_address
assert_success response
end
+ def test_cvd_fields_pass_correctly
+ stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end.check_request do |endpoint, data, headers|
+ assert_match (/cvdIndicator%3E1%3C\/cvdIndicator%3E%0A%20%20%20%20%3Ccvd%3E123%3C\/cvd/), data
+ end.respond_with(successful_purchase_response)
+
+ credit_card = CreditCard.new(
+ :number => '4242424242424242',
+ :month => 9,
+ :year => Time.now.year + 1,
+ :first_name => 'Longbob',
+ :last_name => 'Longsen',
+ :brand => 'visa'
+ )
+
+ stub_comms do
+ @gateway.purchase(@amount, credit_card, @options)
+ end.check_request do |endpoint, data, headers|
+ assert_match (/cvdIndicator%3E0%3C\/cvdIndicator%3E%0A%20%20%3C\/card/), data
+ end.respond_with(failed_purchase_response)
+ end
+
def test_successful_void
@gateway.expects(:ssl_post).returns(successful_purchase_response)
@@ -209,6 +232,11 @@ def test_deprecated_options
end
end
+ def test_scrub
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ assert_equal @gateway.scrub(pre_scrubbed_double_escaped), post_scrubbed_double_escaped
+ end
+
private
def full_request
@@ -266,6 +294,7 @@ def minimal_request
#{Time.now.year + 1}
VI
+ 0
WEB
@@ -360,4 +389,64 @@ def failed_purchase_response
XML
end
+
+ def pre_scrubbed
+ <<-EOS
+opening connection to webservices.test.optimalpayments.com:443...
+opened
+starting SSL for webservices.test.optimalpayments.com:443...
+SSL established
+<- "POST /creditcardWS/CreditCardServlet/v1 HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: webservices.test.optimalpayments.com\r\nContent-Length: 1616\r\n\r\n"
+<- "txnMode=ccPurchase&txnRequest=%3CccAuthRequestV1%20xmlns=%22http://www.optimalpayments.com/creditcard/xmlschema/v1%22%20xmlns:xsi=%22http://www.w3.org/2001/XMLSchema-instance%22%20xsi:schemaLocation=%22http://www.optimalpayments.com/creditcard/xmlschema/v1%22%3E%0A%20%20%3CmerchantAccount%3E%0A%20%20%20%20%3CaccountNum%3E1001134550%3C/accountNum%3E%0A%20%20%20%20%3CstoreID%3Etest%3C/storeID%3E%0A%20%20%20%20%3CstorePwd%3Etest%3C/storePwd%3E%0A%20%20%3C/merchantAccount%3E%0A%20%20%3CmerchantRefNum%3E1%3C/merchantRefNum%3E%0A%20%20%3Camount%3E1.0%3C/amount%3E%0A%20%20%3Ccard%3E%0A%20%20%20%20%3CcardNum%3E4387751111011%3C/cardNum%3E%0A%20%20%20%20%3CcardExpiry%3E%0A%20%20%20%20%20%20%3Cmonth%3E9%3C/month%3E%0A%20%20%20%20%20%20%3Cyear%3E2019%3C/year%3E%0A%20%20%20%20%3C/cardExpiry%3E%0A%20%20%20%20%3CcardType%3EVI%3C/cardType%3E%0A%20%20%20%20%3CcvdIndicator%3E1%3C/cvdIndicator%3E%0A%20%20%20%20%3Ccvd%3E123%3C/cvd%3E%0A%20%20%3C/card%3E%0A%20%20%3CbillingDetails%3E%0A%20%20%20%20%3CcardPayMethod%3EWEB%3C/cardPayMethod%3E%0A%20%20%20%20%3CfirstName%3EJim%3C/firstName%3E%0A%20%20%20%20%3ClastName%3ESmith%3C/lastName%3E%0A%20%20%20%20%3Cstreet%3E456%20My%20Street%3C/street%3E%0A%20%20%20%20%3Cstreet2%3EApt%201%3C/street2%3E%0A%20%20%20%20%3Ccity%3EOttawa%3C/city%3E%0A%20%20%20%20%3Cstate%3EON%3C/state%3E%0A%20%20%20%20%3Ccountry%3ECA%3C/country%3E%0A%20%20%20%20%3Czip%3EK1C2N6%3C/zip%3E%0A%20%20%20%20%3Cphone%3E(555)555-5555%3C/phone%3E%0A%20%20%20%20%3Cemail%3Eemail@example.com%3C/email%3E%0A%20%20%3C/billingDetails%3E%0A%20%20%3CcustomerIP%3E1.2.3.4%3C/customerIP%3E%0A%3C/ccAuthRequestV1%3E%0A"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Server: WebServer32xS10i3\r\n"
+-> "Content-Length: 632\r\n"
+-> "X-ApplicationUid: GUID=610a301289c34e8254330b7edc724f5b\r\n"
+-> "Content-Type: application/xml\r\n"
+-> "Date: Mon, 12 Feb 2018 21:57:42 GMT\r\n"
+-> "Connection: close\r\n"
+-> "\r\n"
+reading 632 bytes...
+-> "<"
+-> "?xml version=\"1.0\" encoding=\"UTF-8\"?>\n498871860ACCEPTED0
No Error369231XMInternalResponseCode0SubErrorCode0InternalResponseDescriptionno_error2018-02-12T16:57:42.289-05:00false"
+read 632 bytes
+Conn close
+ EOS
+ end
+
+ def post_scrubbed
+ <<-EOS
+opening connection to webservices.test.optimalpayments.com:443...
+opened
+starting SSL for webservices.test.optimalpayments.com:443...
+SSL established
+<- "POST /creditcardWS/CreditCardServlet/v1 HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: webservices.test.optimalpayments.com\r\nContent-Length: 1616\r\n\r\n"
+<- "txnMode=ccPurchase&txnRequest=%3CccAuthRequestV1%20xmlns=%22http://www.optimalpayments.com/creditcard/xmlschema/v1%22%20xmlns:xsi=%22http://www.w3.org/2001/XMLSchema-instance%22%20xsi:schemaLocation=%22http://www.optimalpayments.com/creditcard/xmlschema/v1%22%3E%0A%20%20%3CmerchantAccount%3E%0A%20%20%20%20%3CaccountNum%3E1001134550%3C/accountNum%3E%0A%20%20%20%20%3CstoreID%3Etest%3C/storeID%3E%0A%20%20%20%20%3CstorePwd%3E[FILTERED]%3C/storePwd%3E%0A%20%20%3C/merchantAccount%3E%0A%20%20%3CmerchantRefNum%3E1%3C/merchantRefNum%3E%0A%20%20%3Camount%3E1.0%3C/amount%3E%0A%20%20%3Ccard%3E%0A%20%20%20%20%3CcardNum%3E[FILTERED]%3C/cardNum%3E%0A%20%20%20%20%3CcardExpiry%3E%0A%20%20%20%20%20%20%3Cmonth%3E9%3C/month%3E%0A%20%20%20%20%20%20%3Cyear%3E2019%3C/year%3E%0A%20%20%20%20%3C/cardExpiry%3E%0A%20%20%20%20%3CcardType%3EVI%3C/cardType%3E%0A%20%20%20%20%3CcvdIndicator%3E1%3C/cvdIndicator%3E%0A%20%20%20%20%3Ccvd%3E[FILTERED]%3C/cvd%3E%0A%20%20%3C/card%3E%0A%20%20%3CbillingDetails%3E%0A%20%20%20%20%3CcardPayMethod%3EWEB%3C/cardPayMethod%3E%0A%20%20%20%20%3CfirstName%3EJim%3C/firstName%3E%0A%20%20%20%20%3ClastName%3ESmith%3C/lastName%3E%0A%20%20%20%20%3Cstreet%3E456%20My%20Street%3C/street%3E%0A%20%20%20%20%3Cstreet2%3EApt%201%3C/street2%3E%0A%20%20%20%20%3Ccity%3EOttawa%3C/city%3E%0A%20%20%20%20%3Cstate%3EON%3C/state%3E%0A%20%20%20%20%3Ccountry%3ECA%3C/country%3E%0A%20%20%20%20%3Czip%3EK1C2N6%3C/zip%3E%0A%20%20%20%20%3Cphone%3E(555)555-5555%3C/phone%3E%0A%20%20%20%20%3Cemail%3Eemail@example.com%3C/email%3E%0A%20%20%3C/billingDetails%3E%0A%20%20%3CcustomerIP%3E1.2.3.4%3C/customerIP%3E%0A%3C/ccAuthRequestV1%3E%0A"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Server: WebServer32xS10i3\r\n"
+-> "Content-Length: 632\r\n"
+-> "X-ApplicationUid: GUID=610a301289c34e8254330b7edc724f5b\r\n"
+-> "Content-Type: application/xml\r\n"
+-> "Date: Mon, 12 Feb 2018 21:57:42 GMT\r\n"
+-> "Connection: close\r\n"
+-> "\r\n"
+reading 632 bytes...
+-> "<"
+-> "?xml version=\"1.0\" encoding=\"UTF-8\"?>\n498871860ACCEPTED0
No Error369231XMInternalResponseCode0SubErrorCode0InternalResponseDescriptionno_error2018-02-12T16:57:42.289-05:00false"
+read 632 bytes
+Conn close
+ EOS
+ end
+
+ def pre_scrubbed_double_escaped
+ <<-PRE_SCRUBBED
+txnMode=ccPurchase&txnRequest=%3CccAuthRequestV1+xmlns%3D%22http%3A%2F%2Fwww.optimalpayments.com%2Fcreditcard%2Fxmlschema%2Fv1%22+xmlns%3Axsi%3D%22http%3A%2F%2Fwww.w3.org%2F2001%2FXMLSchema-instance%22+xsi%3AschemaLocation%3D%22http%3A%2F%2Fwww.optimalpayments.com%2Fcreditcard%2Fxmlschema%2Fv1%22%3E%0A++%3CmerchantAccount%3E%0A++++%3CaccountNum%3E89986098%3C%2FaccountNum%3E%0A++++%3CstoreID%3Etest%3C%2FstoreID%3E%0A++++%3CstorePwd%3Etest%3C%2FstorePwd%3E%0A++%3C%2FmerchantAccount%3E%0A++%3CmerchantRefNum%3E1%3C%2FmerchantRefNum%3E%0A++%3Camount%3E1.0%3C%2Famount%3E%0A++%3Ccard%3E%0A++++%3CcardNum%3E4387751111011%3C%2FcardNum%3E%0A++++%3CcardExpiry%3E%0A++++++%3Cmonth%3E9%3C%2Fmonth%3E%0A++++++%3Cyear%3E2015%3C%2Fyear%3E%0A++++%3C%2FcardExpiry%3E%0A++++%3CcardType%3EVI%3C%2FcardType%3E%0A++++%3CcvdIndicator%3E1%3C%2FcvdIndicator%3E%0A++++%3Ccvd%3E123%3C%2Fcvd%3E%0A++%3C%2Fcard%3E%0A++%3CbillingDetails%3E%0A++++%3CcardPayMethod%3EWEB%3C%2FcardPayMethod%3E%0A++++%3CfirstName%3EJim%3C%2FfirstName%3E%0A++++%3ClastName%3ESmith%3C%2FlastName%3E%0A++++%3Cstreet%3E1234+My+Street%3C%2Fstreet%3E%0A++++%3Cstreet2%3EApt+1%3C%2Fstreet2%3E%0A++++%3Ccity%3EOttawa%3C%2Fcity%3E%0A++++%3Cstate%3EON%3C%2Fstate%3E%0A++++%3Ccountry%3ECA%3C%2Fcountry%3E%0A++++%3Czip%3EK1C2N6%3C%2Fzip%3E%0A++++%3Cphone%3E%28555%29555-5555%3C%2Fphone%3E%0A++++%3Cemail%3Eemail%40example.com%3C%2Femail%3E%0A++%3C%2FbillingDetails%3E%0A++%3CcustomerIP%3E1.2.3.4%3C%2FcustomerIP%3E%0A%3C%2FccAuthRequestV1%3E%0A
+ PRE_SCRUBBED
+end
+
+def post_scrubbed_double_escaped
+ <<-POST_SCRUBBED
+txnMode=ccPurchase&txnRequest=%3CccAuthRequestV1+xmlns%3D%22http%3A%2F%2Fwww.optimalpayments.com%2Fcreditcard%2Fxmlschema%2Fv1%22+xmlns%3Axsi%3D%22http%3A%2F%2Fwww.w3.org%2F2001%2FXMLSchema-instance%22+xsi%3AschemaLocation%3D%22http%3A%2F%2Fwww.optimalpayments.com%2Fcreditcard%2Fxmlschema%2Fv1%22%3E%0A++%3CmerchantAccount%3E%0A++++%3CaccountNum%3E89986098%3C%2FaccountNum%3E%0A++++%3CstoreID%3Etest%3C%2FstoreID%3E%0A++++%3CstorePwd%3E[FILTERED]%3C%2FstorePwd%3E%0A++%3C%2FmerchantAccount%3E%0A++%3CmerchantRefNum%3E1%3C%2FmerchantRefNum%3E%0A++%3Camount%3E1.0%3C%2Famount%3E%0A++%3Ccard%3E%0A++++%3CcardNum%3E[FILTERED]%3C%2FcardNum%3E%0A++++%3CcardExpiry%3E%0A++++++%3Cmonth%3E9%3C%2Fmonth%3E%0A++++++%3Cyear%3E2015%3C%2Fyear%3E%0A++++%3C%2FcardExpiry%3E%0A++++%3CcardType%3EVI%3C%2FcardType%3E%0A++++%3CcvdIndicator%3E1%3C%2FcvdIndicator%3E%0A++++%3Ccvd%3E[FILTERED]%3C%2Fcvd%3E%0A++%3C%2Fcard%3E%0A++%3CbillingDetails%3E%0A++++%3CcardPayMethod%3EWEB%3C%2FcardPayMethod%3E%0A++++%3CfirstName%3EJim%3C%2FfirstName%3E%0A++++%3ClastName%3ESmith%3C%2FlastName%3E%0A++++%3Cstreet%3E1234+My+Street%3C%2Fstreet%3E%0A++++%3Cstreet2%3EApt+1%3C%2Fstreet2%3E%0A++++%3Ccity%3EOttawa%3C%2Fcity%3E%0A++++%3Cstate%3EON%3C%2Fstate%3E%0A++++%3Ccountry%3ECA%3C%2Fcountry%3E%0A++++%3Czip%3EK1C2N6%3C%2Fzip%3E%0A++++%3Cphone%3E%28555%29555-5555%3C%2Fphone%3E%0A++++%3Cemail%3Eemail%40example.com%3C%2Femail%3E%0A++%3C%2FbillingDetails%3E%0A++%3CcustomerIP%3E1.2.3.4%3C%2FcustomerIP%3E%0A%3C%2FccAuthRequestV1%3E%0A
+ POST_SCRUBBED
+end
end
diff --git a/test/unit/gateways/orbital_test.rb b/test/unit/gateways/orbital_test.rb
index 310a84e2b75..69ac058dab5 100644
--- a/test/unit/gateways/orbital_test.rb
+++ b/test/unit/gateways/orbital_test.rb
@@ -14,6 +14,22 @@ def setup
)
@customer_ref_num = "ABC"
+ @level_2 = {
+ tax_indicator: "1",
+ tax: "10",
+ advice_addendum_1: 'taa1 - test',
+ advice_addendum_2: 'taa2 - test',
+ advice_addendum_3: 'taa3 - test',
+ advice_addendum_4: 'taa4 - test',
+ purchase_order: '123abc',
+ name: address[:name],
+ address1: address[:address1],
+ address2: address[:address2],
+ city: address[:city],
+ state: address[:state],
+ zip: address[:zip],
+ }
+
@options = { :order_id => '1'}
end
@@ -26,6 +42,37 @@ def test_successful_purchase
assert_equal '4A5398CF9B87744GG84A1D30F2F2321C66249416;1', response.authorization
end
+ def test_level_2_data
+ stub_comms do
+ @gateway.purchase(50, credit_card, @options.merge(level_2_data: @level_2))
+ end.check_request do |endpoint, data, headers|
+ assert_match %{#{@level_2[:tax_indicator].to_i}}, data
+ assert_match %{#{@level_2[:tax].to_i}}, data
+ assert_match %{#{@level_2[:advice_addendum_1]}}, data
+ assert_match %{#{@level_2[:advice_addendum_2]}}, data
+ assert_match %{#{@level_2[:advice_addendum_3]}}, data
+ assert_match %{#{@level_2[:advice_addendum_4]}}, data
+ assert_match %{#{@level_2[:purchase_order]}}, data
+ assert_match %{#{@level_2[:zip]}}, data
+ assert_match %{#{@level_2[:name]}}, data
+ assert_match %{#{@level_2[:address1]}}, data
+ assert_match %{#{@level_2[:address2]}}, data
+ assert_match %{#{@level_2[:city]}}, data
+ assert_match %{#{@level_2[:state]}}, data
+ end.respond_with(successful_purchase_response)
+ end
+
+ def test_network_tokenization_credit_card_data
+ stub_comms do
+ @gateway.purchase(50, network_tokenization_credit_card(nil, eci: '5', transaction_id: 'BwABB4JRdgAAAAAAiFF2AAAAAAA='), @options)
+ end.check_request do |endpoint, data, headers|
+ assert_match %{5}, data
+ assert_match %{Y}, data
+ assert_match %{DigitalTokenCryptogram}, data
+ assert_match %{XID}, data
+ end.respond_with(successful_purchase_response)
+ end
+
def test_currency_exponents
stub_comms do
@gateway.purchase(50, credit_card, :order_id => '1')
@@ -676,6 +723,24 @@ def test_failed_verify
assert_equal "AUTH DECLINED 12001", response.message
end
+ def test_cvv_indicator_present_for_visas_with_cvvs
+ stub_comms do
+ @gateway.purchase(50, credit_card, @options)
+ end.check_request do |_endpoint, data, _headers|
+ assert_match %r{1<\/CardSecValInd>}, data
+ assert_match %r{123<\/CardSecVal>}, data
+ end.respond_with(successful_purchase_response)
+ end
+
+ def test_cvv_indicator_absent_for_recurring
+ stub_comms do
+ @gateway.purchase(50, credit_card(nil, {verification_value: nil}), @options)
+ end.check_request do |_endpoint, data, _headers|
+ assert_no_match %r{}, data
+ assert_no_match %r{}, data
+ end.respond_with(successful_purchase_response)
+ end
+
def test_scrub
assert @gateway.supports_scrubbing?
assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
@@ -705,7 +770,7 @@ def pre_scrubbed
opened
starting SSL for orbitalvar1.paymentech.net:443...
SSL established
-<- "POST /authorize HTTP/1.1\r\nContent-Type: application/PTI56\r\nMime-Version: 1.1\r\nContent-Transfer-Encoding: text\r\nRequest-Number: 1\r\nDocument-Type: Request\r\nInterface-Version: Ruby|ActiveMerchant|Proprietary Gateway\r\nContent-Length: 964\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: orbitalvar1.paymentech.net\r\n\r\n"
+<- "POST /authorize HTTP/1.1\r\nContent-Type: application/PTI71\r\nMime-Version: 1.1\r\nContent-Transfer-Encoding: text\r\nRequest-Number: 1\r\nDocument-Type: Request\r\nInterface-Version: Ruby|ActiveMerchant|Proprietary Gateway\r\nContent-Length: 964\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: orbitalvar1.paymentech.net\r\n\r\n"
<- "\n\n \n T16WAYSACT\n zbp8X1ykGZ\n EC\n AC\n 000001\n 041756\n 001\n 4112344112344113\n 0917\n 840\n 2\n 1\n 123\n K1C2N6\n 456 My Street\n Apt 1\n Ottawa\n ON\n 5555555555\n Longbob Longsen\n CA\n b141cf3ce2a442732e1906\n 100\n \n\n"
-> "HTTP/1.1 200 OK\r\n"
-> "Date: Thu, 02 Jun 2016 07:04:44 GMT\r\n"
@@ -729,7 +794,7 @@ def post_scrubbed
opened
starting SSL for orbitalvar1.paymentech.net:443...
SSL established
-<- "POST /authorize HTTP/1.1\r\nContent-Type: application/PTI56\r\nMime-Version: 1.1\r\nContent-Transfer-Encoding: text\r\nRequest-Number: 1\r\nDocument-Type: Request\r\nInterface-Version: Ruby|ActiveMerchant|Proprietary Gateway\r\nContent-Length: 964\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: orbitalvar1.paymentech.net\r\n\r\n"
+<- "POST /authorize HTTP/1.1\r\nContent-Type: application/PTI71\r\nMime-Version: 1.1\r\nContent-Transfer-Encoding: text\r\nRequest-Number: 1\r\nDocument-Type: Request\r\nInterface-Version: Ruby|ActiveMerchant|Proprietary Gateway\r\nContent-Length: 964\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: orbitalvar1.paymentech.net\r\n\r\n"
<- "\n\n \n [FILTERED]\n [FILTERED]\n EC\n AC\n 000001\n [FILTERED]\n 001\n [FILTERED]\n 0917\n 840\n 2\n 1\n [FILTERED]\n K1C2N6\n 456 My Street\n Apt 1\n Ottawa\n ON\n 5555555555\n Longbob Longsen\n CA\n b141cf3ce2a442732e1906\n 100\n \n\n"
-> "HTTP/1.1 200 OK\r\n"
-> "Date: Thu, 02 Jun 2016 07:04:44 GMT\r\n"
@@ -746,4 +811,16 @@ def post_scrubbed
Conn close
EOS
end
+
+ def pre_scrubbed_profile
+ <<-EOS
+000001253997LONGBOB LONGSEN109273631CREATE0Profile Request Processed456 MY STREETAPT 1OTTAWAONK1C2N65555555555CANOCCA41123441123441130919
+ EOS
+ end
+
+ def post_scrubbed_profile
+ <<-EOS
+000001[FILTERED]LONGBOB LONGSEN109273631CREATE0Profile Request Processed456 MY STREETAPT 1OTTAWAONK1C2N65555555555CANOCCA[FILTERED]0919
+ EOS
+ end
end
diff --git a/test/unit/gateways/paybox_direct_plus_test.rb b/test/unit/gateways/paybox_direct_plus_test.rb
new file mode 100644
index 00000000000..34256678273
--- /dev/null
+++ b/test/unit/gateways/paybox_direct_plus_test.rb
@@ -0,0 +1,124 @@
+# encoding: utf-8
+
+require 'test_helper'
+
+class PayboxDirectPlusTest < Test::Unit::TestCase
+ def setup
+ @gateway = PayboxDirectPlusGateway.new(
+ :login => 'l',
+ :password => 'p'
+ )
+
+ @credit_card = credit_card('1111222233334444',
+ :brand => 'visa'
+ )
+ @amount = 100
+
+ @options = {
+ :order_id => '1',
+ :billing_address => address,
+ :description => 'Store Purchase'
+ }
+ end
+
+ def test_successful_purchase
+ @gateway.expects(:ssl_post).returns(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 response.params['numappel'].to_s + response.params['numtrans'], response.authorization
+ assert_equal 'XXXXXX', response.params['autorisation']
+ assert_equal "The transaction was approved", response.message
+ assert response.test?
+ end
+
+ def test_purchase_with_default_currency
+ @gateway.expects(:ssl_post).with do |_, body|
+ body.include?('DEVISE=978')
+ end.returns(purchase_response)
+
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+
+ def test_purchase_with_set_currency
+ @options.update(currency: 'GBP')
+
+ @gateway.expects(:ssl_post).with do |_, body|
+ body.include?('DEVISE=826')
+ end.returns(purchase_response)
+
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+
+ def test_deprecated_credit
+ @gateway.expects(:ssl_post).with(anything, regexp_matches(/NUMAPPEL=transid/), anything).returns("")
+ @gateway.expects(:parse).returns({})
+ assert_deprecation_warning(Gateway::CREDIT_DEPRECATION_MESSAGE) do
+ @gateway.credit(@amount, "transid", @options)
+ end
+ end
+
+ def test_refund
+ @gateway.expects(:ssl_post).with(anything) do |_, body|
+ body.include?('NUMAPPEL=transid')
+ body.include?('MONTANT=0000000100&DEVISE=97')
+ end.returns("")
+
+ @gateway.expects(:parse).returns({})
+ @gateway.refund(@amount, "transid", @options)
+ 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 "Demande trait?e avec succ?s ✔漢", response.message
+ assert response.test?
+ end
+
+ def test_keep_the_card_code_not_considered_fraudulent
+ @gateway.expects(:ssl_post).returns(purchase_response("00104"))
+
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert !response.fraud_review?
+ end
+
+ def test_do_not_honour_code_not_considered_fraudulent
+ @gateway.expects(:ssl_post).returns(purchase_response("00105"))
+
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert !response.fraud_review?
+ end
+
+ def test_card_absent_from_file_code_not_considered_fraudulent
+ @gateway.expects(:ssl_post).returns(purchase_response("00156"))
+
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert !response.fraud_review?
+ end
+
+ def test_version
+ @gateway.expects(:ssl_post).with(anything, regexp_matches(/VERSION=00104/)).returns(purchase_response)
+ @gateway.purchase(@amount, @credit_card, @options)
+ end
+
+ private
+
+ # Place raw successful response from gateway here
+ def purchase_response(code="00000")
+ "NUMTRANS=0720248861&NUMAPPEL=0713790302&NUMQUESTION=0000790217&SITE=1999888&RANG=99&AUTORISATION=XXXXXX&CODEREPONSE=#{code}&COMMENTAIRE=Demande trait?e avec succ?s ✔漢"
+ end
+
+ # Place raw failed response from gateway here
+ def failed_purchase_response
+ 'NUMTRANS=0000000000&NUMAPPEL=0000000000&NUMQUESTION=0000000000&SITE=1999888&RANG=99&AUTORISATION=&CODEREPONSE=00014&COMMENTAIRE=Demande trait?e avec succ?s ✔漢'
+ end
+end
diff --git a/test/unit/gateways/payeezy_test.rb b/test/unit/gateways/payeezy_test.rb
index 131f3b4d33d..b6b73fad994 100644
--- a/test/unit/gateways/payeezy_test.rb
+++ b/test/unit/gateways/payeezy_test.rb
@@ -5,17 +5,15 @@ class PayeezyGateway < Test::Unit::TestCase
include CommStub
def setup
- @gateway = PayeezyGateway.new(
- apikey: "45234543524353",
- apisecret: "4235423325",
- token: "rewrt-23543543542353542"
- )
+ @gateway = PayeezyGateway.new(fixtures(:payeezy))
@credit_card = credit_card
+ @bad_credit_card = credit_card('4111111111111113')
@check = check
@amount = 100
@options = {
- :billing_address => address
+ :billing_address => address,
+ :ta_token => '123'
}
@authorization = "ET1700|106625152|credit_card|4738"
end
@@ -26,7 +24,7 @@ def test_invalid_credentials
assert response = @gateway.authorize(100, @credit_card, {})
assert_failure response
assert response.test?
- assert_equal '||credit_card|', response.authorization
+ assert response.authorization
assert_equal 'HMAC validation Failure', response.message
end
@@ -36,7 +34,7 @@ def test_invalid_token
assert response = @gateway.authorize(100, @credit_card, {})
assert_failure response
assert response.test?
- assert_equal '||credit_card|', response.authorization
+ assert response.authorization
assert_equal 'Access denied', response.message
end
@@ -46,7 +44,7 @@ def test_invalid_token_on_integration
assert response = @gateway.authorize(100, @credit_card, {})
assert_failure response
assert response.test?
- assert_equal '||credit_card|', response.authorization
+ assert response.authorization
assert_equal 'Invalid ApiKey for given resource', response.message
end
@@ -59,6 +57,40 @@ def test_successful_purchase
assert_equal 'Transaction Normal - Approved', response.message
end
+ def test_successful_store
+ response = stub_comms(@gateway, :ssl_request) do
+ @gateway.store(@credit_card, @options.merge(js_security_key: 'js-f4c4b54f08d6c44c8cad3ea80bbf92c4f4c4b54f08d6c44c'))
+ end.respond_with(successful_store_response)
+
+ assert_success response
+ assert_equal 'Token successfully created.', response.message
+ assert response.test?
+ end
+
+ def test_successful_store_and_purchase
+ response = stub_comms(@gateway, :ssl_request) do
+ @gateway.store(@credit_card, @options.merge(js_security_key: 'js-f4c4b54f08d6c44c8cad3ea80bbf92c4f4c4b54f08d6c44c'))
+ end.respond_with(successful_store_response)
+
+ assert_success response
+ assert_match %r{Token successfully created}, response.message
+
+ @gateway.expects(:ssl_post).returns(successful_purchase_response)
+
+ purchase = @gateway.purchase(@amount, response.authorization, @options)
+ assert_success purchase
+ end
+
+ def test_failed_store
+ response = stub_comms(@gateway, :ssl_request) do
+ @gateway.store(@bad_credit_card, @options.merge(js_security_key: 'js-f4c4b54f08d6c44c8cad3ea80bbf92c4f4c4b54f08d6c44c'))
+ end.respond_with(failed_store_response)
+
+ assert_failure response
+ assert_equal 'The credit card number check failed', response.message
+ assert response.test?
+ end
+
def test_successful_purchase_with_echeck
@gateway.expects(:ssl_post).returns(successful_purchase_echeck_response)
assert response = @gateway.purchase(@amount, @check, @options)
@@ -68,6 +100,21 @@ def test_successful_purchase_with_echeck
assert_equal 'Transaction Normal - Approved', response.message
end
+ def test_successful_purchase_defaulting_check_number
+ check_without_number = check(number: nil)
+
+ response = stub_comms do
+ @gateway.purchase(@amount, check_without_number, @options)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/001/, data)
+ end.respond_with(successful_purchase_echeck_response)
+
+ assert_success response
+ assert_equal 'ET133078|69864362|tele_check|100', response.authorization
+ assert response.test?
+ assert_equal 'Transaction Normal - Approved', response.message
+ end
+
def test_failed_purchase
@gateway.expects(:ssl_post).raises(failed_purchase_response)
assert response = @gateway.purchase(@amount, @credit_card, @options)
@@ -78,7 +125,7 @@ def test_failed_purchase
def test_successful_authorize
@gateway.expects(:ssl_post).returns(successful_authorize_response)
- assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert response = @gateway.authorize(@amount, @credit_card, @options)
assert_success response
assert_equal 'ET156862|69601979|credit_card|100', response.authorization
assert response.test?
@@ -193,6 +240,13 @@ def test_requests_include_verification_string
end.respond_with(successful_purchase_response)
end
+ def test_gateway_message_surfaces
+ @gateway.expects(:ssl_post).returns(below_minimum_response)
+ assert response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal 'Below Minimum Sale', response.message
+ end
+
def test_card_type
assert_equal 'Visa', PayeezyGateway::CREDIT_CARD_BRAND['visa']
assert_equal 'Mastercard', PayeezyGateway::CREDIT_CARD_BRAND['master']
@@ -206,6 +260,10 @@ def test_scrub
assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
end
+ def test_scrub_store
+ assert_equal @gateway.scrub(pre_scrubbed_store), post_scrubbed_store
+ end
+
def test_scrub_echeck
assert @gateway.supports_scrubbing?
assert_equal @gateway.scrub(pre_scrubbed_echeck), post_scrubbed_echeck
@@ -253,7 +311,7 @@ def post_scrubbed
opened
starting SSL for api-cert.payeezy.com:443...
SSL established
- <- "POST /v1/transactions HTTP/1.1\r\nContent-Type: application/json\r\nApikey: oKB61AAxbN3xwC6gVAH3dp58FmioHSAT\r\nToken: [FILTERED]\r\nNonce: 5803993876.636232\r\nTimestamp: 1449523748359\r\nAuthorization: NGRlZjJkMWNlMDc5NGI5OTVlYTQxZDRkOGQ4NjRhNmZhNDgwZmIyNTZkMWJhN2M3MDdkNDI0ZWI1OGUwMGExMA==\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api-cert.payeezy.com\r\nContent-Length: 365\r\n\r\n"
+ <- "POST /v1/transactions HTTP/1.1\r\nContent-Type: application/json\r\nApikey: [FILTERED]\r\nToken: [FILTERED]\r\nNonce: 5803993876.636232\r\nTimestamp: 1449523748359\r\nAuthorization: NGRlZjJkMWNlMDc5NGI5OTVlYTQxZDRkOGQ4NjRhNmZhNDgwZmIyNTZkMWJhN2M3MDdkNDI0ZWI1OGUwMGExMA==\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api-cert.payeezy.com\r\nContent-Length: 365\r\n\r\n"
<- "{\"transaction_type\":\"purchase\",\"merchant_ref\":null,\"method\":\"credit_card\",\"credit_card\":{\"type\":\"Visa\",\"cardholder_name\":\"Longbob Longsen\",\"card_number\":\"[FILTERED]\",\"exp_date\":\"0916\",\"cvv\":\"[FILTERED]\"},\"billing_address\":{\"street\":\"456 My Street\",\"city\":\"Ottawa\",\"state_province\":\"ON\",\"zip_postal_code\":\"K1C2N6\",\"country\":\"CA\"},\"currency_code\":\"USD\",\"amount\":\"100\"}"
-> "HTTP/1.1 201 Created\r\n"
-> "Access-Control-Allow-Headers: Content-Type, apikey, token\r\n"
@@ -335,6 +393,62 @@ def post_scrubbed_echeck
TRANSCRIPT
end
+ def pre_scrubbed_store
+ <<-TRANSCRIPT
+ opening connection to api-cert.payeezy.com:443...
+ opened
+ starting SSL for api-cert.payeezy.com:443...
+ SSL established
+ <- "GET /v1/securitytokens?apikey=UyDMTXx6TD9WErF6ynw7xeEfCAn8fcGs&js_security_key=js-f4c4b54f08d6c44c8cad3ea80bbf92c4f4c4b54f08d6c44c&ta_token=120&callback=Payeezy.callback&type=FDToken&credit_card.type=Visa&credit_card.cardholder_name=Longbob+Longsen&credit_card.card_number=4242424242424242&credit_card.exp_date=0919&credit_card.cvv=123 HTTP/1.1\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api-cert.payeezy.com\r\n\r\n"
+ -> "HTTP/1.1 200 Success\r\n"
+ -> "Content-Language: en-US\r\n"
+ -> "Content-Type: application/json\r\n"
+ -> "correlation_id: 228.1574930196886\r\n"
+ -> "Date: Fri, 12 Jan 2018 09:28:22 GMT\r\n"
+ -> "statuscode: 201\r\n"
+ -> "X-Archived-Client-IP: 10.180.205.250\r\n"
+ -> "X-Backside-Transport: OK OK,OK OK\r\n"
+ -> "X-Client-IP: 10.180.205.250,54.218.45.37\r\n"
+ -> "X-Global-Transaction-ID: 463881989\r\n"
+ -> "X-Powered-By: Servlet/3.0\r\n"
+ -> "Content-Length: 266\r\n"
+ -> "Connection: Close\r\n"
+ -> "\r\n"
+ reading 266 bytes...
+ -> "\n Payeezy.callback({\n \t\"status\":201,\n \t\"results\":{\"correlation_id\":\"228.1574930196886\",\"status\":\"success\",\"type\":\"FDToken\",\"token\":{\"type\":\"Visa\",\"cardholder_name\":\"Longbob Longsen\",\"exp_date\":\"0919\",\"value\":\"2158545373614242\"}}\n })\n "
+ read 266 bytes
+ Conn close
+ TRANSCRIPT
+ end
+
+ def post_scrubbed_store
+ <<-TRANSCRIPT
+ opening connection to api-cert.payeezy.com:443...
+ opened
+ starting SSL for api-cert.payeezy.com:443...
+ SSL established
+ <- "GET /v1/securitytokens?apikey=[FILTERED]js_security_key=js-f4c4b54f08d6c44c8cad3ea80bbf92c4f4c4b54f08d6c44c&ta_token=120&callback=Payeezy.callback&type=FDToken&credit_card.type=Visa&credit_card.cardholder_name=Longbob+Longsen&credit_card.card_number=[FILTERED]credit_card.exp_date=0919&credit_card.cvv=[FILTERED] HTTP/1.1\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api-cert.payeezy.com\r\n\r\n"
+ -> "HTTP/1.1 200 Success\r\n"
+ -> "Content-Language: en-US\r\n"
+ -> "Content-Type: application/json\r\n"
+ -> "correlation_id: 228.1574930196886\r\n"
+ -> "Date: Fri, 12 Jan 2018 09:28:22 GMT\r\n"
+ -> "statuscode: 201\r\n"
+ -> "X-Archived-Client-IP: 10.180.205.250\r\n"
+ -> "X-Backside-Transport: OK OK,OK OK\r\n"
+ -> "X-Client-IP: 10.180.205.250,54.218.45.37\r\n"
+ -> "X-Global-Transaction-ID: 463881989\r\n"
+ -> "X-Powered-By: Servlet/3.0\r\n"
+ -> "Content-Length: 266\r\n"
+ -> "Connection: Close\r\n"
+ -> "\r\n"
+ reading 266 bytes...
+ -> "\n Payeezy.callback({\n \t\"status\":201,\n \t\"results\":{\"correlation_id\":\"228.1574930196886\",\"status\":\"success\",\"type\":\"FDToken\",\"token\":{\"type\":\"Visa\",\"cardholder_name\":\"Longbob Longsen\",\"exp_date\":\"0919\",\"value\":\"2158545373614242\"}}\n })\n "
+ read 266 bytes
+ Conn close
+ TRANSCRIPT
+ end
+
def successful_purchase_response
<<-RESPONSE
{\"method\":\"credit_card\",\"amount\":\"1\",\"currency\":\"USD\",\"avs\":\"4\",\"card\":{\"type\":\"Visa\",\"cardholder_name\":\"Bobsen 995\",\"card_number\":\"4242\",\"exp_date\":\"0816\"},\"token\":{\"token_type\":\"transarmor\",\"token_data\":{\"value\":\"0152552999534242\"}},\"transaction_status\":\"approved\",\"validation_status\":\"success\",\"transaction_type\":\"purchase\",\"transaction_id\":\"ET114541\",\"transaction_tag\":\"55083431\",\"bank_resp_code\":\"100\",\"bank_message\":\"Approved\",\"gateway_resp_code\":\"00\",\"gateway_message\":\"Transaction Normal\",\"correlation_id\":\"124.1433862672836\"}
@@ -347,6 +461,18 @@ def successful_purchase_echeck_response
RESPONSE
end
+ def successful_store_response
+ <<-RESPONSE
+ {\"correlation_id\":\"124.1792879391754\",\"status\":\"success\",\"type\":\"FDToken\",\"token\":{\"type\":\"Visa\",\"cardholder_name\":\"Longbob Longsen\",\"exp_date\":\"0919\",\"value\":\"9045348309244242\"}}
+ RESPONSE
+ end
+
+ def failed_store_response
+ <<-RESPONSE
+ {\"correlation_id\":\"124.1792940806770\",\"status\":\"failed\",\"Error\":{\"messages\":[{\"code\":\"invalid_card_number\",\"description\":\"The credit card number check failed\"}]},\"type\":\"FDToken\"}
+ RESPONSE
+ end
+
def failed_purchase_response
yamlexcep = <<-RESPONSE
--- !ruby/exception:ActiveMerchant::ResponseError
@@ -418,6 +544,12 @@ def successful_refund_echeck_response
RESPONSE
end
+ def below_minimum_response
+ <<-RESPONSE
+ {\"correlation_id\":\"123.1234678982\",\"transaction_status\":\"declined\",\"validation_status\":\"success\",\"transaction_type\":\"authorize\",\"transaction_tag\":\"92384753\",\"method\":\"credit_card\",\"amount\":\"250\",\"currency\":\"USD\",\"card\":{\"type\":\"Mastercard\",\"cardholder_name\":\"Omri Test\",\"card_number\":\"[FILTERED]\",\"exp_date\":\"0123\"},\"gateway_resp_code\":\"36\",\"gateway_message\":\"Below Minimum Sale\"}
+ RESPONSE
+ end
+
def failed_refund_response
yamlexcep = <<-RESPONSE
--- !ruby/exception:ActiveMerchant::ResponseError
diff --git a/test/unit/gateways/payflow_test.rb b/test/unit/gateways/payflow_test.rb
index 037d6e61f91..119f2d4cc7d 100644
--- a/test/unit/gateways/payflow_test.rb
+++ b/test/unit/gateways/payflow_test.rb
@@ -50,6 +50,33 @@ def test_authorization_with_three_d_secure_option
refute response.fraud_review?
end
+ def test_successful_authorization_with_more_options
+ options = @options.merge(
+ {
+ order_id: "123",
+ description: "Description string",
+ order_desc: "OrderDesc string",
+ comment: "Comment string",
+ comment2: "Comment2 string"
+ }
+ )
+
+ response = stub_comms do
+ @gateway.authorize(@amount, @credit_card, options)
+ end.check_request do |endpoint, data, headers|
+ assert_match %r(123), data
+ assert_match %r(Description string), data
+ assert_match %r(OrderDesc string), data
+ assert_match %r(Comment string), data
+ assert_match %r(), data
+ end.respond_with(successful_authorization_response)
+ assert_equal "Approved", response.message
+ assert_success response
+ assert response.test?
+ assert_equal "VUJN1A6E11D9", response.authorization
+ refute response.fraud_review?
+ end
+
def test_successful_purchase_with_fraud_review
@gateway.stubs(:ssl_post).returns(successful_purchase_with_fraud_review_response)
@@ -189,7 +216,7 @@ def test_default_currency
end
def test_supported_countries
- assert_equal ['US', 'CA', 'SG', 'AU'], PayflowGateway.supported_countries
+ assert_equal ['US', 'CA', 'NZ', 'AU'], PayflowGateway.supported_countries
end
def test_supported_card_types
@@ -401,7 +428,117 @@ def test_passed_in_verbosity
assert_equal '2014-06-25 09:33:41', response.params['transaction_time']
end
+ def test_paypal_nvp_option_sends_header
+ headers = @gateway.send(:build_headers, 1)
+ assert_not_include headers, 'PAYPAL-NVP'
+
+ old_use_paypal_nvp = PayflowGateway.use_paypal_nvp
+ PayflowGateway.use_paypal_nvp = true
+ headers = @gateway.send(:build_headers, 1)
+ assert_equal 'Y', headers['PAYPAL-NVP']
+ PayflowGateway.use_paypal_nvp = old_use_paypal_nvp
+ end
+
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ assert_equal @gateway.scrub(pre_scrubbed_check), post_scrubbed_check
+ end
+
private
+
+ def pre_scrubbed
+ <<-EOS
+opening connection to pilot-payflowpro.paypal.com:443...
+opened
+starting SSL for pilot-payflowpro.paypal.com:443...
+SSL established
+<- "POST / HTTP/1.1\r\nContent-Type: text/xml\r\nContent-Length: 1017\r\nX-Vps-Client-Timeout: 60\r\nX-Vps-Vit-Integration-Product: ActiveMerchant\r\nX-Vps-Vit-Runtime-Version: 2.1.7\r\nX-Vps-Request-Id: 3b2f9831949b48b4b0b89a33a60f9b0c\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: pilot-payflowpro.paypal.com\r\n\r\n"
+<- "spreedlyIntegrationsPayPalMEDIUMcody@example.comJim Smithcody@example.com(555)555-5555codyexample456 My StreetOttawaONCAK1C2N6MasterCard5105105105105100201909Longbob123spreedlyIntegrationsL9DjqEKjXCkU"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Connection: close\r\n"
+-> "Server: VPS-3.033.00\r\n"
+-> "X-VPS-Request-ID: 3b2f9831949b48b4b0b89a33a60f9b0c\r\n"
+-> "Date: Thu, 01 Mar 2018 15:42:15 GMT\r\n"
+-> "Content-type: text/xml\r\n"
+-> "Content-length: 267\r\n"
+-> "\r\n"
+reading 267 bytes...
+-> "4Invalid amount"
+read 267 bytes
+Conn close
+ EOS
+ end
+
+ def post_scrubbed
+ <<-EOS
+opening connection to pilot-payflowpro.paypal.com:443...
+opened
+starting SSL for pilot-payflowpro.paypal.com:443...
+SSL established
+<- "POST / HTTP/1.1\r\nContent-Type: text/xml\r\nContent-Length: 1017\r\nX-Vps-Client-Timeout: 60\r\nX-Vps-Vit-Integration-Product: ActiveMerchant\r\nX-Vps-Vit-Runtime-Version: 2.1.7\r\nX-Vps-Request-Id: 3b2f9831949b48b4b0b89a33a60f9b0c\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: pilot-payflowpro.paypal.com\r\n\r\n"
+<- "spreedlyIntegrationsPayPalMEDIUMcody@example.comJim Smithcody@example.com(555)555-5555codyexample456 My StreetOttawaONCAK1C2N6MasterCard[FILTERED]201909Longbob[FILTERED]spreedlyIntegrations[FILTERED]"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Connection: close\r\n"
+-> "Server: VPS-3.033.00\r\n"
+-> "X-VPS-Request-ID: 3b2f9831949b48b4b0b89a33a60f9b0c\r\n"
+-> "Date: Thu, 01 Mar 2018 15:42:15 GMT\r\n"
+-> "Content-type: text/xml\r\n"
+-> "Content-length: 267\r\n"
+-> "\r\n"
+reading 267 bytes...
+-> "4Invalid amount"
+read 267 bytes
+Conn close
+ EOS
+ end
+
+ def pre_scrubbed_check
+ <<-EOS
+opening connection to pilot-payflowpro.paypal.com:443...
+opened
+starting SSL for pilot-payflowpro.paypal.com:443...
+SSL established
+<- "POST / HTTP/1.1\r\nContent-Type: text/xml\r\nContent-Length: 658\r\nX-Vps-Client-Timeout: 60\r\nX-Vps-Vit-Integration-Product: ActiveMerchant\r\nX-Vps-Vit-Runtime-Version: 2.1.7\r\nX-Vps-Request-Id: 863021e6890a0660238ef22d0a21c5f2\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: pilot-payflowpro.paypal.com\r\n\r\n"
+<- "spreedlyIntegrationsPayPalMEDIUMJim SmithC1234567801111111118spreedlyIntegrationsL9DjqEKjXCkU"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Connection: close\r\n"
+-> "Server: VPS-3.033.00\r\n"
+-> "X-VPS-Request-ID: 863021e6890a0660238ef22d0a21c5f2\r\n"
+-> "Date: Thu, 01 Mar 2018 15:45:59 GMT\r\n"
+-> "Content-type: text/xml\r\n"
+-> "Content-length: 267\r\n"
+-> "\r\n"
+reading 267 bytes...
+-> "4Invalid amount"
+read 267 bytes
+Conn close
+ EOS
+ end
+
+ def post_scrubbed_check
+ <<-EOS
+opening connection to pilot-payflowpro.paypal.com:443...
+opened
+starting SSL for pilot-payflowpro.paypal.com:443...
+SSL established
+<- "POST / HTTP/1.1\r\nContent-Type: text/xml\r\nContent-Length: 658\r\nX-Vps-Client-Timeout: 60\r\nX-Vps-Vit-Integration-Product: ActiveMerchant\r\nX-Vps-Vit-Runtime-Version: 2.1.7\r\nX-Vps-Request-Id: 863021e6890a0660238ef22d0a21c5f2\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: pilot-payflowpro.paypal.com\r\n\r\n"
+<- "spreedlyIntegrationsPayPalMEDIUMJim SmithC[FILTERED]111111118spreedlyIntegrations[FILTERED]"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Connection: close\r\n"
+-> "Server: VPS-3.033.00\r\n"
+-> "X-VPS-Request-ID: 863021e6890a0660238ef22d0a21c5f2\r\n"
+-> "Date: Thu, 01 Mar 2018 15:45:59 GMT\r\n"
+-> "Content-type: text/xml\r\n"
+-> "Content-length: 267\r\n"
+-> "\r\n"
+reading 267 bytes...
+-> "4Invalid amount"
+read 267 bytes
+Conn close
+ EOS
+ end
+
def successful_recurring_response
<<-XML
@@ -621,7 +758,7 @@ def verbose_transaction_response
XML
end
-
+
def assert_three_d_secure(xml_doc, buyer_auth_result_path)
assert_equal 'Y', REXML::XPath.first(xml_doc, "#{buyer_auth_result_path}/Status").text
assert_equal 'QvDbSAxSiaQs241899E0', REXML::XPath.first(xml_doc, "#{buyer_auth_result_path}/AuthenticationId").text
diff --git a/test/unit/gateways/payment_express_test.rb b/test/unit/gateways/payment_express_test.rb
index 07fd82cb04f..f892ab3f715 100644
--- a/test/unit/gateways/payment_express_test.rb
+++ b/test/unit/gateways/payment_express_test.rb
@@ -126,7 +126,7 @@ def test_purchase_using_merchant_specified_billing_id_token
end
def test_supported_countries
- assert_equal %w(AU CA DE ES FR GB HK IE MY NL NZ SG US ZA), PaymentExpressGateway.supported_countries
+ assert_equal %w(AU FJ GB HK IE MY NZ PG SG US), PaymentExpressGateway.supported_countries
end
def test_supported_card_types
@@ -433,6 +433,6 @@ def transcript
end
def scrubbed_transcript
- %(Longbob Longsen[FILTERED]0916[FILTERED]11.00NZD59956b468905bde7Store purchase11456 My StreetK1C2N6WaysactDevkvr52dw9Purchase)
+ %(Longbob Longsen[FILTERED]0916[FILTERED]11.00NZD59956b468905bde7Store purchase11456 My StreetK1C2N6WaysactDev[FILTERED]Purchase)
end
end
diff --git a/test/unit/gateways/paymentez_test.rb b/test/unit/gateways/paymentez_test.rb
new file mode 100644
index 00000000000..6c9d3f88c05
--- /dev/null
+++ b/test/unit/gateways/paymentez_test.rb
@@ -0,0 +1,376 @@
+require 'test_helper'
+
+class PaymentezTest < Test::Unit::TestCase
+ def setup
+ @gateway = PaymentezGateway.new(application_code: 'foo', app_key: 'bar')
+ @credit_card = credit_card
+ @amount = 100
+
+ @options = {
+ order_id: '1',
+ user_id: '123',
+ billing_address: address,
+ description: 'Store Purchase',
+ email: 'a@b.com'
+ }
+ end
+
+ def test_successful_purchase
+ @gateway.expects(:ssl_post).returns(successful_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal 'PR-926', response.authorization
+ assert response.test?
+ end
+
+ def test_successful_purchase_with_token
+ @gateway.expects(:ssl_post).returns(successful_purchase_response)
+
+ response = @gateway.purchase(@amount, '123456789012345678901234567890', @options)
+ assert_success response
+
+ assert_equal 'PR-926', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_purchase
+ @gateway.expects(:ssl_post).returns(failed_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal Gateway::STANDARD_ERROR_CODE[:card_declined], response.error_code
+ end
+
+ def test_successful_authorize
+ @gateway.stubs(:ssl_post).returns(successful_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'CI-635', response.authorization
+ assert response.test?
+ end
+
+ def test_successful_authorize_with_token
+ @gateway.stubs(:ssl_post).returns(successful_authorize_response)
+
+ response = @gateway.authorize(@amount, '123456789012345678901234567890', @options)
+ assert_success response
+ assert_equal 'CI-635', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_authorize
+ @gateway.expects(:ssl_post).returns(failed_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_failure response
+ assert response.test?
+ end
+
+ def test_successful_capture
+ @gateway.expects(:ssl_post).returns(successful_capture_response)
+
+ response = @gateway.capture(@amount, '1234', @options)
+ assert_success response
+ assert_equal 'CI-635', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_capture
+ @gateway.expects(:ssl_post).returns(failed_capture_response)
+
+ response = @gateway.capture(@amount, '1234', @options)
+ assert_failure response
+ assert response.test?
+ end
+
+ def test_successful_refund
+ @gateway.expects(:ssl_post).returns(successful_refund_response)
+
+ response = @gateway.refund(@amount, '1234', @options)
+ assert_success response
+ assert response.test?
+ end
+
+ def test_failed_refund
+ @gateway.expects(:ssl_post).returns(failed_refund_response)
+
+ response = @gateway.refund(@amount, '1234', @options)
+ assert_failure response
+ assert response.test?
+ end
+
+ def test_successful_void
+ @gateway.expects(:ssl_post).returns(successful_void_response)
+
+ response = @gateway.void('1234', @options)
+ assert_success response
+ assert response.test?
+ end
+
+ def test_failed_void
+ @gateway.expects(:ssl_post).returns(failed_void_response)
+
+ response = @gateway.void('1234', @options)
+ assert_failure response
+ end
+
+ def test_simple_store
+ @gateway.expects(:ssl_post).returns(successful_store_response)
+
+ response = @gateway.store(@credit_card, @options)
+ assert_success response
+ assert_equal '14436664108567261211', response.authorization
+ end
+
+ def test_complex_store
+ @gateway.stubs(:ssl_post).returns(already_stored_response, successful_unstore_response, successful_store_response)
+
+ response = @gateway.store(@credit_card, @options)
+ assert_success response
+ end
+
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
+ private
+
+ def pre_scrubbed
+ %q(
+opening connection to ccapi-stg.paymentez.com:443...
+opened
+starting SSL for ccapi-stg.paymentez.com:443...
+SSL established
+<- "POST /v2/transaction/debit_cc HTTP/1.1\r\nContent-Type: application/json\r\nAuth-Token: U1BETFktTVgtU0VSVkVSOzE1MTM3MDU5OTc7M8I1MjQ1NT5yMWNlZWU0ZjFlYTdiZDBlOGE1MWIxZjBkYzBjZTMyYjZmN2RmNjE4ZGQ5MmNiODhjMTM5MWIyNg==\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: ccapi-stg.paymentez.com\r\nContent-Length: 264\r\n\r\n"
+<- "{\"order\":{\"amount\":1.0,\"vat\":0,\"dev_reference\":\"Testing\",\"description\":\"Store Purchase\"},\"card\":{\"number\":\"4111111111111111\",\"holder_name\":\"Longbob Longsen\",\"expiry_month\":9,\"expiry_year\":2018,\"cvc\":\"123\",\"type\":\"vi\"},\"user\":{\"id\":\"123\",\"email\":\"joe@example.com\"}}"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Server: nginx/1.12.1\r\n"
+-> "Date: Tue, 19 Dec 2017 17:51:42 GMT\r\n"
+-> "Content-Type: application/json\r\n"
+-> "Content-Length: 402\r\n"
+-> "Connection: close\r\n"
+-> "Vary: Accept-Language, Cookie\r\n"
+-> "Content-Language: es\r\n"
+-> "\r\n"
+reading 402 bytes...
+-> "{\"transaction\": {\"status\": \"success\", \"payment_date\": \"2017-12-19T17:51:39.985\", \"amount\": 1.0, \"authorization_code\": \"123456\", \"installments\": 1, \"dev_reference\": \"Testing\", \"message\": \"Response by mock\", \"carrier_code\": \"00\", \"id\": \"PR-871\", \"status_detail\": 3}, \"card\": {\"bin\": \"411111\", \"expiry_year\": \"2018\", \"expiry_month\": \"9\", \"transaction_reference\": \"PR-871\", \"type\": \"vi\", \"number\": \"1111\"}}"
+read 402 bytes
+Conn close
+ )
+ end
+
+ def post_scrubbed
+ %q(
+opening connection to ccapi-stg.paymentez.com:443...
+opened
+starting SSL for ccapi-stg.paymentez.com:443...
+SSL established
+<- "POST /v2/transaction/debit_cc HTTP/1.1\r\nContent-Type: application/json\r\nAuth-Token: [FILTERED]\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: ccapi-stg.paymentez.com\r\nContent-Length: 264\r\n\r\n"
+<- "{\"order\":{\"amount\":1.0,\"vat\":0,\"dev_reference\":\"Testing\",\"description\":\"Store Purchase\"},\"card\":{\"number\":[FILTERED],\"holder_name\":\"Longbob Longsen\",\"expiry_month\":9,\"expiry_year\":2018,\"cvc\":[FILTERED],\"type\":\"vi\"},\"user\":{\"id\":\"123\",\"email\":\"joe@example.com\"}}"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Server: nginx/1.12.1\r\n"
+-> "Date: Tue, 19 Dec 2017 17:51:42 GMT\r\n"
+-> "Content-Type: application/json\r\n"
+-> "Content-Length: 402\r\n"
+-> "Connection: close\r\n"
+-> "Vary: Accept-Language, Cookie\r\n"
+-> "Content-Language: es\r\n"
+-> "\r\n"
+reading 402 bytes...
+-> "{\"transaction\": {\"status\": \"success\", \"payment_date\": \"2017-12-19T17:51:39.985\", \"amount\": 1.0, \"authorization_code\": \"123456\", \"installments\": 1, \"dev_reference\": \"Testing\", \"message\": \"Response by mock\", \"carrier_code\": \"00\", \"id\": \"PR-871\", \"status_detail\": 3}, \"card\": {\"bin\": \"411111\", \"expiry_year\": \"2018\", \"expiry_month\": \"9\", \"transaction_reference\": \"PR-871\", \"type\": \"vi\", \"number\": \"1111\"}}"
+read 402 bytes
+Conn close
+ )
+ end
+
+ def successful_purchase_response
+ %q(
+ {
+ "transaction": {
+ "status": "success",
+ "payment_date": "2017-12-19T20:29:12.715",
+ "amount": 1,
+ "authorization_code": "123456",
+ "installments": 1,
+ "dev_reference": "Testing",
+ "message": "Response by mock",
+ "carrier_code": "00",
+ "id": "PR-926",
+ "status_detail": 3
+ },
+ "card": {
+ "bin": "411111",
+ "expiry_year": "2018",
+ "expiry_month": "9",
+ "transaction_reference": "PR-926",
+ "type": "vi",
+ "number": "1111"
+ }
+ }
+ )
+ end
+
+ def failed_purchase_response
+ %q(
+ {
+ "transaction": {
+ "status": "failure",
+ "payment_date": null,
+ "amount": 1,
+ "authorization_code": null,
+ "installments": 1,
+ "dev_reference": "Testing",
+ "message": "Response by mock",
+ "carrier_code": "3",
+ "id": "PR-945",
+ "status_detail": 9
+ },
+ "card": {
+ "bin": "424242",
+ "expiry_year": "2018",
+ "expiry_month": "9",
+ "transaction_reference": "PR-945",
+ "type": "vi",
+ "number": "4242"
+ }
+ }
+ )
+ end
+
+ def successful_authorize_response
+ %q(
+ {
+ "transaction": {
+ "status": "success",
+ "payment_date": "2017-12-21T18:04:42",
+ "amount": 1,
+ "authorization_code": "487897",
+ "installments": 1,
+ "dev_reference": "Testing",
+ "message": "Operation Successful",
+ "carrier_code": "4",
+ "id": "CI-635",
+ "status_detail": 0
+ },
+ "card": {
+ "bin": "411111",
+ "status": "valid",
+ "token": "12032069702317830187",
+ "expiry_year": "2018",
+ "expiry_month": "9",
+ "transaction_reference": "CI-635",
+ "type": "vi",
+ "number": "1111"
+ }
+ }
+ )
+ end
+
+ def failed_authorize_response
+ %q(
+ {
+ "transaction": {
+ "status": "failure",
+ "payment_date": null,
+ "amount": 1.0,
+ "authorization_code": null,
+ "installments": 1,
+ "dev_reference": "Testing",
+ "message": null,
+ "carrier_code": "3",
+ "id": "CI-1223",
+ "status_detail": 9
+ },
+ "card": {
+ "bin": "424242",
+ "status": null,
+ "token": "6461587429110733892",
+ "expiry_year": "2019",
+ "expiry_month": "9",
+ "transaction_reference": "CI-1223",
+ "type": "vi",
+ "number": "4242",
+ "origin": "Paymentez"
+ }
+ }
+ )
+ end
+
+ def successful_capture_response
+ %q(
+ {
+ "transaction": {
+ "status": "success",
+ "payment_date": "2017-12-21T18:04:42",
+ "amount": 1,
+ "authorization_code": "487897",
+ "installments": 1,
+ "dev_reference": "Testing",
+ "message": "Operation Successful",
+ "carrier_code": "6",
+ "id": "CI-635",
+ "status_detail": 3
+ },
+ "card": {
+ "bin": "411111",
+ "status": "valid",
+ "token": "12032069702317830187",
+ "expiry_year": "2018",
+ "expiry_month": "9",
+ "transaction_reference": "CI-635",
+ "type": "vi",
+ "number": "1111"
+ }
+ }
+ )
+ end
+
+ def failed_capture_response
+ "{\"error\": {\"type\": \"Carrier not supported\", \"help\": \"\", \"description\": \"{}\"}}"
+ end
+
+ def successful_void_response
+ '{"status": "success", "detail": "Completed"}'
+ end
+
+ def failed_void_response
+ '{"error": {"type": "Carrier not supported", "help": "", "description": "{}"}}'
+ end
+
+ alias_method :successful_refund_response, :successful_void_response
+ alias_method :failed_refund_response, :failed_void_response
+
+ def already_stored_response
+ '{"error": {"type": "Card already added: 14436664108567261211", "help": "If you want to update the card, first delete it", "description": "{}"}}'
+ end
+
+ def successful_unstore_response
+ '{"message": "card deleted"}'
+ end
+
+ def successful_store_response
+ '{"card": {"bin": "411111", "status": "valid", "token": "14436664108567261211", "message": "", "expiry_year": "2018", "expiry_month": "9", "transaction_reference": "PR-959", "type": "vi", "number": "1111"}}'
+ end
+
+ def failed_store_response
+ %q(
+ {
+ "card": {
+ "bin": "424242",
+ "status": "rejected",
+ "token": "2026849624512750545",
+ "message": "Not Authorized",
+ "expiry_year": "2018",
+ "expiry_month": "9",
+ "transaction_reference": "CI-606",
+ "type": "vi",
+ "number": "4242"
+ }
+ }
+ )
+ end
+end
diff --git a/test/unit/gateways/paystation_test.rb b/test/unit/gateways/paystation_test.rb
index 4e4337b14d9..6fa03c4715b 100644
--- a/test/unit/gateways/paystation_test.rb
+++ b/test/unit/gateways/paystation_test.rb
@@ -106,6 +106,13 @@ def test_failed_refund
assert_failure response
end
+ def test_successful_verify
+ @gateway.expects(:ssl_post).returns(successful_authorization_response)
+
+ assert response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ end
+
private
def successful_purchase_response
diff --git a/test/unit/gateways/payu_latam_test.rb b/test/unit/gateways/payu_latam_test.rb
index f06792366dc..7ce307432c9 100644
--- a/test/unit/gateways/payu_latam_test.rb
+++ b/test/unit/gateways/payu_latam_test.rb
@@ -1,19 +1,32 @@
require 'test_helper'
class PayuLatamTest < Test::Unit::TestCase
+ include CommStub
+
def setup
- @gateway = PayuLatamGateway.new(merchant_id: 'merchant_id', account_id: 'account_id', api_login: 'api_login', api_key: 'api_key')
+ @gateway = PayuLatamGateway.new(merchant_id: 'merchant_id', account_id: 'account_id', api_login: 'api_login', api_key: 'api_key', payment_country: 'AR')
@amount = 4000
@credit_card = credit_card("4097440000000004", verification_value: "444", first_name: "APPROVED", last_name: "")
@declined_card = credit_card("4097440000000004", verification_value: "333", first_name: "REJECTED", last_name: "")
@pending_card = credit_card("4097440000000004", verification_value: "222", first_name: "PENDING", last_name: "")
+ @no_cvv_visa_card = credit_card("4097440000000004", verification_value: " ")
+ @no_cvv_amex_card = credit_card("4097440000000004", verification_value: " ", brand: "american_express")
@options = {
+ dni_number: '5415668464654',
+ dni_type: 'TI',
currency: "ARS",
order_id: generate_unique_id,
description: "Active Merchant Transaction",
installments_number: 1,
+ tax: 0,
+ tax_return_base: 0,
+ email: "username@domain.com",
+ ip: "127.0.0.1",
+ device_session_id: 'vghs6tvkcle931686k1900o6e1',
+ cookie: 'pt1t38347bs6jc9ruv2ecpv7o2',
+ user_agent: 'Mozilla/5.0 (Windows NT 5.1; rv:18.0) Gecko/20100101 Firefox/18.0',
billing_address: address(
address1: "Viamonte",
address2: "1366",
@@ -35,6 +48,14 @@ def test_successful_purchase
assert response.test?
end
+ def test_successful_purchase_with_specified_language
+ stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options.merge(language: 'es'))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/"language":"es"/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
def test_failed_purchase
@gateway.expects(:ssl_post).returns(failed_purchase_response)
@@ -53,6 +74,14 @@ def test_successful_authorize
assert_match %r(^\d+\|(\w|-)+$), response.authorization
end
+ def test_successful_authorize_with_specified_language
+ stub_comms do
+ @gateway.authorize(@amount, @credit_card, @options.merge(language: 'es'))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/"language":"es"/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
def test_failed_authorize
@gateway.expects(:ssl_post).returns(pending_authorize_response)
@@ -62,6 +91,62 @@ def test_failed_authorize
assert_equal "PENDING", response.params["transactionResponse"]["state"]
end
+ def test_pending_refund
+ @gateway.expects(:ssl_post).returns(pending_refund_response)
+
+ response = @gateway.refund(@amount, "7edbaf68-8f3a-4ae7-b9c7-d1e27e314999")
+ assert_success response
+ assert_equal "PENDING", response.params["transactionResponse"]["state"]
+ end
+
+ def test_pending_refund_with_specified_language
+ stub_comms do
+ @gateway.refund(@amount, "7edbaf68-8f3a-4ae7-b9c7-d1e27e314999", @options.merge(language: 'es'))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/"language":"es"/, data)
+ end.respond_with(pending_refund_response)
+ end
+
+ def test_failed_refund
+ @gateway.expects(:ssl_post).returns(failed_refund_response)
+
+ response = @gateway.refund(@amount, "")
+ assert_failure response
+ assert_equal "property: order.id, message: must not be null property: parentTransactionId, message: must not be null", response.message
+ end
+
+ def test_successful_void
+ @gateway.expects(:ssl_post).returns(successful_void_response)
+
+ response = @gateway.void("7edbaf68-8f3a-4ae7-b9c7-d1e27e314999", @options)
+ assert_success response
+ assert_equal "PENDING_REVIEW", response.message
+ end
+
+ def test_successful_void_with_specified_language
+ stub_comms do
+ @gateway.void("7edbaf68-8f3a-4ae7-b9c7-d1e27e314999", @options.merge(language: 'es'))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/"language":"es"/, data)
+ end.respond_with(successful_void_response)
+ end
+
+ def test_failed_void
+ @gateway.expects(:ssl_post).returns(failed_void_response)
+
+ response = @gateway.void("")
+ assert_failure response
+ assert_equal "property: order.id, message: must not be null property: parentTransactionId, message: must not be null", response.message
+ end
+
+ def test_successful_purchase_with_dni_number
+ stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/"dniNumber":"5415668464654"/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
def test_verify_good_credentials
@gateway.expects(:ssl_post).returns(credentials_are_legit_response)
assert @gateway.verify_credentials
@@ -72,6 +157,199 @@ def test_verify_bad_credentials
assert !@gateway.verify_credentials
end
+ def test_request_using_visa_card_with_no_cvv
+ @gateway.expects(:ssl_post).with { |url, body, headers|
+ body.match '"securityCode":"000"'
+ body.match '"processWithoutCvv2":true'
+ }.returns(successful_purchase_response)
+ response = @gateway.purchase(@amount, @no_cvv_visa_card, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ assert response.test?
+ end
+
+ def test_request_using_amex_card_with_no_cvv
+ @gateway.expects(:ssl_post).with { |url, body, headers|
+ body.match '"securityCode":"0000"'
+ body.match '"processWithoutCvv2":true'
+ }.returns(successful_purchase_response)
+ response = @gateway.purchase(@amount, @no_cvv_amex_card, @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ assert response.test?
+ end
+
+ def test_request_passes_cvv_option
+ @gateway.expects(:ssl_post).with { |url, body, headers|
+ body.match '"securityCode":"777"'
+ !body.match '"processWithoutCvv2"'
+ }.returns(successful_purchase_response)
+ options = @options.merge(cvv: "777")
+ response = @gateway.purchase(@amount, @no_cvv_visa_card, options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ assert response.test?
+ end
+
+ def test_successful_capture
+ @gateway.expects(:ssl_post).returns(successful_capture_response)
+
+ response = @gateway.capture(@amount, "4000|authorization", @options)
+ assert_success response
+ assert_equal "APPROVED", response.message
+ end
+
+ def test_successful_capture_with_specified_language
+ stub_comms do
+ @gateway.capture(@amount, "4000|authorization", @options.merge(language: 'es'))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/"language":"es"/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
+ def test_failed_capture
+ @gateway.expects(:ssl_post).returns(failed_void_response)
+
+ response = @gateway.capture(@amount, "")
+ assert_failure response
+ assert_equal "property: order.id, message: must not be null property: parentTransactionId, message: must not be null", response.message
+ end
+
+ def test_partial_buyer_hash_info
+ options_buyer = {
+ shipping_address: address(
+ address1: "Calle 200",
+ address2: "N107",
+ city: "Sao Paulo",
+ state: "SP",
+ country: "BR",
+ zip: "01019-030",
+ phone: "(11)756312345"
+ ),
+ buyer: {
+ name: 'Jorge Borges',
+ dni_number: '5415668464456',
+ email: 'axaxaxas@mlo.org'
+ }
+ }
+
+ stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options.update(options_buyer))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/\"buyer\":{\"fullName\":\"Jorge Borges\",\"dniNumber\":\"5415668464456\",\"dniType\":null,\"emailAddress\":\"axaxaxas@mlo.org\",\"contactPhone\":\"7563126\",\"shippingAddress\":{\"street1\":\"Calle 200\",\"street2\":\"N107\",\"city\":\"Sao Paulo\",\"state\":\"SP\",\"country\":\"BR\",\"postalCode\":\"01019-030\",\"phone\":\"\(11\)756312345\"}}/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
+ def test_buyer_fields_default_to_payer
+ stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/\"buyer\":{\"fullName\":\"APPROVED\",\"dniNumber\":\"5415668464654\",\"dniType\":\"TI\",\"emailAddress\":\"username@domain.com\",\"contactPhone\":\"7563126\"/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
+ def test_brazil_required_fields
+ gateway = PayuLatamGateway.new(merchant_id: 'merchant_id', account_id: 'account_id', api_login: 'api_login', api_key: 'api_key', payment_country: 'BR')
+
+ options_brazil = {
+ currency: "BRL",
+ billing_address: address(
+ address1: "Calle 100",
+ address2: "BL4",
+ city: "Sao Paulo",
+ state: "SP",
+ country: "BR",
+ zip: "09210710",
+ phone: "(11)756312633"
+ ),
+ shipping_address: address(
+ address1: "Calle 200",
+ address2: "N107",
+ city: "Sao Paulo",
+ state: "SP",
+ country: "BR",
+ zip: "01019-030",
+ phone: "(11)756312633"
+ ),
+ buyer: {
+ cnpj: "32593371000110"
+ }
+ }
+
+ stub_comms(gateway) do
+ gateway.purchase(@amount, @credit_card, @options.update(options_brazil))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/\"cnpj\":\"32593371000110\"/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
+ def test_colombia_required_fields
+ gateway = PayuLatamGateway.new(merchant_id: 'merchant_id', account_id: 'account_id', api_login: 'api_login', api_key: 'api_key', payment_country: 'CO')
+
+ options_colombia = {
+ currency: "COP",
+ billing_address: address(
+ address1: "Calle 100",
+ address2: "BL4",
+ city: "Bogota",
+ state: "Bogota DC",
+ country: "CO",
+ zip: "09210710",
+ phone: "(11)756312633"
+ ),
+ shipping_address: address(
+ address1: "Calle 200",
+ address2: "N107",
+ city: "Bogota",
+ state: "Bogota DC",
+ country: "CO",
+ zip: "01019-030",
+ phone: "(11)756312633"
+ ),
+ tx_tax: '3193',
+ tx_tax_return_base: '16806'
+ }
+
+ stub_comms(gateway) do
+ gateway.purchase(@amount, @credit_card, @options.update(options_colombia))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/\"additionalValues\":{\"TX_VALUE\":{\"value\":\"40.00\",\"currency\":\"COP\"},\"TX_TAX\":{\"value\":0,\"currency\":\"COP\"},\"TX_TAX_RETURN_BASE\":{\"value\":0,\"currency\":\"COP\"}}/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
+ def test_mexico_required_fields
+ gateway = PayuLatamGateway.new(merchant_id: 'merchant_id', account_id: 'account_id', api_login: 'api_login', api_key: 'api_key', payment_country: 'MX')
+
+ options_mexico = {
+ currency: "MXN",
+ billing_address: address(
+ address1: "Calle 100",
+ address2: "BL4",
+ city: "Guadalajara",
+ state: "Jalisco",
+ country: "MX",
+ zip: "09210710",
+ phone: "(11)756312633"
+ ),
+ shipping_address: address(
+ address1: "Calle 200",
+ address2: "N107",
+ city: "Guadalajara",
+ state: "Jalisco",
+ country: "MX",
+ zip: "01019-030",
+ phone: "(11)756312633"
+ ),
+ birth_date: '1985-05-25'
+ }
+
+ stub_comms(gateway) do
+ gateway.purchase(@amount, @credit_card, @options.update(options_mexico))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/\"birthdate\":\"1985-05-25\"/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
def test_scrub
assert @gateway.supports_scrubbing?
assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
@@ -245,6 +523,119 @@ def pending_authorize_response
RESPONSE
end
+ def pending_refund_response
+ <<-RESPONSE
+ {
+ "code": "SUCCESS",
+ "error": null,
+ "transactionResponse":
+ {
+ "orderId": 924877963,
+ "transactionId": null,
+ "state": "PENDING",
+ "paymentNetworkResponseCode": null,
+ "paymentNetworkResponseErrorMessage": null,
+ "trazabilityCode": null,
+ "authorizationCode": null,
+ "pendingReason": "PENDING_REVIEW",
+ "responseCode": null,
+ "errorCode": null,
+ "responseMessage": "924877963",
+ "transactionDate": null,
+ "transactionTime": null,
+ "operationDate": null,
+ "referenceQuestionnaire": null,
+ "extraParameters": null,
+ "additionalInfo": null
+ }
+ }
+ RESPONSE
+ end
+
+ def failed_refund_response
+ <<-RESPONSE
+ {
+ "code":"ERROR",
+ "error":"property: order.id, message: must not be null property: parentTransactionId, message: must not be null",
+ "transactionResponse": null
+ }
+ RESPONSE
+ end
+
+ def successful_void_response
+ <<-RESPONSE
+ {
+ "code": "SUCCESS",
+ "error": null,
+ "transactionResponse": {
+ "orderId": 840434914,
+ "transactionId": "e66fd9aa-f485-4f10-b1d6-be8e9e354b63",
+ "state": "PENDING",
+ "paymentNetworkResponseCode": "0",
+ "paymentNetworkResponseErrorMessage": null,
+ "trazabilityCode": "49263990",
+ "authorizationCode": "NPS-011111",
+ "pendingReason": "PENDING_REVIEW",
+ "responseCode": null,
+ "errorCode": null,
+ "responseMessage": null,
+ "transactionDate": null,
+ "transactionTime": null,
+ "operationDate": 1486655230074,
+ "referenceQuestionnaire": null,
+ "extraParameters": null,
+ "additionalInfo": null
+ }
+ }
+ RESPONSE
+ end
+
+ def failed_void_response
+ <<-RESPONSE
+ {
+ "code":"ERROR",
+ "error":"property: order.id, message: must not be null property: parentTransactionId, message: must not be null",
+ "transactionResponse": null
+ }
+ RESPONSE
+ end
+
+ def successful_capture_response
+ <<-RESPONSE
+ {
+ "code": "SUCCESS",
+ "error": null,
+ "transactionResponse": {
+ "orderId": 272601,
+ "transactionId": "66c7bff2-c423-42ed-800a-8be11531e7a1",
+ "state": "APPROVED",
+ "paymentNetworkResponseCode": null,
+ "paymentNetworkResponseErrorMessage": null,
+ "trazabilityCode": "00000000",
+ "authorizationCode": "00000000",
+ "pendingReason": null,
+ "responseCode": "APPROVED",
+ "errorCode": null,
+ "responseMessage": null,
+ "transactionDate": null,
+ "transactionTime": null,
+ "operationDate": 1314012754,
+ "extraParameters": null
+ }
+ }
+ RESPONSE
+ end
+
+ def failed_capture_response
+ <<-RESPONSE
+ {
+ "code":"ERROR",
+ "error":"property: order.id, message: must not be null property: parentTransactionId, message: must not be null",
+ "transactionResponse": null
+ }
+ RESPONSE
+ end
+
def credentials_are_legit_response
<<-RESPONSE
{
diff --git a/test/unit/gateways/pro_pay_test.rb b/test/unit/gateways/pro_pay_test.rb
new file mode 100644
index 00000000000..ae462de87f3
--- /dev/null
+++ b/test/unit/gateways/pro_pay_test.rb
@@ -0,0 +1,291 @@
+require 'test_helper'
+
+class ProPayTest < Test::Unit::TestCase
+ include CommStub
+
+ def setup
+ @gateway = ProPayGateway.new(cert_str: 'certStr')
+ @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)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal '16', response.authorization
+ assert_equal 'Success', response.message
+ end
+
+ def test_failed_purchase
+ @gateway.expects(:ssl_post).returns(failed_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal '58', response.error_code
+ assert_equal 'Credit card declined - Insufficient funds', response.message
+ end
+
+ def test_successful_authorize
+ @gateway.expects(:ssl_post).returns(successful_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal '24', response.authorization
+ assert_equal 'Success', response.message
+ end
+
+ def test_failed_authorize
+ @gateway.expects(:ssl_post).returns(failed_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal '58', response.error_code
+ assert_equal 'Credit card declined - Insufficient funds', response.message
+ end
+
+ def test_successful_capture
+ @gateway.expects(:ssl_post).returns(successful_capture_response)
+
+ response = @gateway.capture(@amount, "auth", @options)
+ assert_success response
+
+ assert_equal '24', response.authorization
+ assert_equal 'Success', response.message
+ end
+
+ def test_failed_capture
+ @gateway.expects(:ssl_post).returns(failed_capture_response)
+
+ response = @gateway.capture(@amount, "invalid-auth", @options)
+ assert_failure response
+ assert_equal '51', response.error_code
+ assert_equal 'Invalid transNum and/or Unable to act perform actions on transNum due to funding', response.message
+ end
+
+ def test_successful_refund
+ @gateway.expects(:ssl_post).returns(successful_refund_response)
+
+ response = @gateway.refund(@amount, 'auth', @options)
+ assert_success response
+ end
+
+ def test_failed_refund
+ @gateway.expects(:ssl_post).returns(failed_refund_response)
+
+ response = @gateway.refund(@amount, 'invalid-auth', @options)
+ assert_failure response
+ assert_equal 'Invalid transNum and/or Unable to act perform actions on transNum due to funding', response.message
+ end
+
+ def test_successful_void
+ response = stub_comms do
+ @gateway.void('auth', @options)
+ end.check_request do |endpoint, data, headers|
+ assert_match(%r(07), data)
+ end.respond_with(successful_void_response)
+
+ assert_success response
+ end
+
+ def test_failed_void
+ response = stub_comms do
+ @gateway.void('invalid-auth', @options)
+ end.check_request do |endpoint, data, headers|
+ assert_match(%r(07), data)
+ end.respond_with(failed_void_response)
+
+ assert_failure response
+ end
+
+ def test_successful_verify
+ @gateway.expects(:ssl_post).times(2).returns(successful_authorize_response, successful_void_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ assert_equal 'Success', response.message
+ end
+
+ def test_successful_verify_with_failed_void
+ @gateway.expects(:ssl_post).times(2).returns(successful_authorize_response, failed_void_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ end
+
+ def test_successful_credit
+ @gateway.expects(:ssl_post).returns(successful_credit_response)
+
+ response = @gateway.credit(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal '103', response.authorization
+ assert_equal 'Success', response.message
+ end
+
+ def test_failed_credit
+ @gateway.expects(:ssl_post).returns(failed_credit_response)
+
+ response = @gateway.credit(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal 'Invalid ccNum', response.message
+ end
+
+ def test_failed_verify
+ @gateway.expects(:ssl_post).returns(failed_authorize_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_failure response
+ assert_equal "58", response.error_code
+ end
+
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
+ private
+
+ def pre_scrubbed
+ <<-RESPONSE
+opening connection to xmltest.propay.com:443...
+opened
+starting SSL for xmltest.propay.com:443...
+SSL established
+<- "POST /API/PropayAPI.aspx HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: xmltest.propay.com\r\nContent-Length: 547\r\n\r\n"
+<- "\n\n 5ab9cddef2e4911b77e0c4ffb70f03\n partner\n \n 100\n USD\n 4747474747474747\n 0918\n 999\n Longbob Longsen\n 456 My Street\n Apt 1\n Ottawa\n ON\n K1C2N6\n 32287391\n 04\n \n\n"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Cache-Control: max-age=0,no-cache,no-store,must-revalidate\r\n"
+-> "Pragma: no-cache\r\n"
+-> "Content-Type: text/xml; charset=utf-8\r\n"
+-> "Content-Encoding: gzip\r\n"
+-> "Expires: Thu, 01 Jan 1970 00:00:00 GMT\r\n"
+-> "Vary: Accept-Encoding\r\n"
+-> "Server: Microsoft-IIS/7.5\r\n"
+-> "Set-Cookie: ASP.NET_SessionId=hn1orxwu31yeoym5fkdhac4o; path=/; secure; HttpOnly\r\n"
+-> "Set-Cookie: sessionValidation=1a1d69b6-6e53-408b-b054-602593da00e7; path=/; secure; HttpOnly\r\n"
+-> "X-Powered-By: ASP.NET\r\n"
+-> "X-Frame-Options: SAMEORIGIN\r\n"
+-> "Date: Tue, 25 Apr 2017 19:44:03 GMT\r\n"
+-> "Connection: close\r\n"
+-> "Content-Length: 343\r\n"
+-> "\r\n"
+reading 343 bytes...
+-> ""
+read 343 bytes
+Conn close
+ RESPONSE
+ end
+
+ def post_scrubbed
+ <<-POST_SCRUBBED
+opening connection to xmltest.propay.com:443...
+opened
+starting SSL for xmltest.propay.com:443...
+SSL established
+<- "POST /API/PropayAPI.aspx HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: xmltest.propay.com\r\nContent-Length: 547\r\n\r\n"
+<- "\n\n [FILTERED]\n partner\n \n 100\n USD\n [FILTERED]\n 0918\n [FILTERED]\n Longbob Longsen\n 456 My Street\n Apt 1\n Ottawa\n ON\n K1C2N6\n 32287391\n 04\n \n\n"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Cache-Control: max-age=0,no-cache,no-store,must-revalidate\r\n"
+-> "Pragma: no-cache\r\n"
+-> "Content-Type: text/xml; charset=utf-8\r\n"
+-> "Content-Encoding: gzip\r\n"
+-> "Expires: Thu, 01 Jan 1970 00:00:00 GMT\r\n"
+-> "Vary: Accept-Encoding\r\n"
+-> "Server: Microsoft-IIS/7.5\r\n"
+-> "Set-Cookie: ASP.NET_SessionId=hn1orxwu31yeoym5fkdhac4o; path=/; secure; HttpOnly\r\n"
+-> "Set-Cookie: sessionValidation=1a1d69b6-6e53-408b-b054-602593da00e7; path=/; secure; HttpOnly\r\n"
+-> "X-Powered-By: ASP.NET\r\n"
+-> "X-Frame-Options: SAMEORIGIN\r\n"
+-> "Date: Tue, 25 Apr 2017 19:44:03 GMT\r\n"
+-> "Connection: close\r\n"
+-> "Content-Length: 343\r\n"
+-> "\r\n"
+reading 343 bytes...
+-> ""
+read 343 bytes
+Conn close
+ POST_SCRUBBED
+ end
+
+ def successful_purchase_response
+ %(
+ 04003228739116A11111TM06710033303.25
+ )
+ end
+
+ def failed_purchase_response
+ %(
+ 04583228739122A11111T51
+ )
+ end
+
+ def successful_authorize_response
+ %(
+ 05003228739124A11111TM0010010000.00
+ )
+ end
+
+ def failed_authorize_response
+ %(
+ 05583228739126A11111T51
+ )
+ end
+
+ def successful_capture_response
+ %(
+ 060032287391246710033303.25
+ )
+ end
+
+ def failed_capture_response
+ %(
+ 065132287391
+ )
+ end
+
+ def successful_refund_response
+ %(
+ 0700322873915
+ )
+ end
+
+ def failed_refund_response
+ %(
+ 075132287391
+ )
+ end
+
+ def successful_void_response
+ %(
+ 07003228739144
+ )
+ end
+
+ def failed_void_response
+ %(
+ 075132287391
+ )
+ end
+
+ def successful_credit_response
+ %(
+ 350032287391103
+ )
+ end
+
+ def failed_credit_response
+ %(
+ 354832287391
+ )
+ end
+end
diff --git a/test/unit/gateways/quickbooks_test.rb b/test/unit/gateways/quickbooks_test.rb
index c264c0354b1..c8f5641d1ce 100644
--- a/test/unit/gateways/quickbooks_test.rb
+++ b/test/unit/gateways/quickbooks_test.rb
@@ -112,6 +112,16 @@ def test_scrub_with_small_json
assert_equal @gateway.scrub(pre_scrubbed_small_json), post_scrubbed_small_json
end
+ def test_default_context
+ stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end.check_request do |_endpoint, data, _headers|
+ json = JSON.parse(data)
+ refute json.fetch('context').fetch('mobile')
+ assert json.fetch('context').fetch('isEcommerce')
+ end.respond_with(successful_purchase_response)
+ end
+
private
def pre_scrubbed_small_json
diff --git a/test/unit/gateways/quickpay_v10_test.rb b/test/unit/gateways/quickpay_v10_test.rb
index e87ba7f3b0e..cdd88a1938f 100644
--- a/test/unit/gateways/quickpay_v10_test.rb
+++ b/test/unit/gateways/quickpay_v10_test.rb
@@ -26,7 +26,7 @@ def test_successful_purchase
response = @gateway.purchase(@amount, @credit_card, @options)
assert response
assert_success response
- assert_equal 1145, response.authorization
+ assert_equal "1145", response.authorization
assert response.test?
end.check_request do |endpoint, data, headers|
parsed = parse(data)
@@ -45,7 +45,7 @@ def test_successful_authorization
stub_comms do
assert response = @gateway.authorize(@amount, @credit_card, @options)
assert_success response
- assert_equal 1145, response.authorization
+ assert_equal "1145", response.authorization
assert response.test?
end.check_request do |endpoint, data, headers|
parsed_data = parse(data)
diff --git a/test/unit/gateways/qvalent_test.rb b/test/unit/gateways/qvalent_test.rb
index 5c3ff6d6155..d16d01c7bff 100644
--- a/test/unit/gateways/qvalent_test.rb
+++ b/test/unit/gateways/qvalent_test.rb
@@ -7,7 +7,9 @@ def setup
@gateway = QvalentGateway.new(
username: "username",
password: "password",
- merchant: "merchant"
+ merchant: "merchant",
+ pem: "pem",
+ pem_password: "pempassword"
)
@credit_card = credit_card
@@ -36,6 +38,48 @@ def test_failed_purchase
assert response.test?
end
+ def test_successful_authorize
+ response = stub_comms do
+ @gateway.authorize(@amount, @credit_card)
+ end.respond_with(successful_authorize_response)
+
+ assert_success response
+
+ assert_equal "21c74c8f08bca415b5373022e6194f74", response.authorization
+ assert response.test?
+ end
+
+ def test_failed_authorize
+ response = stub_comms do
+ @gateway.authorize(@amount, @credit_card)
+ end.respond_with(failed_authorize_response)
+
+ assert_failure response
+ assert_equal "Expired card",response.message
+ assert response.test?
+ end
+
+ def test_successful_capture
+ response = stub_comms do
+ @gateway.capture(@amount, "auth")
+ end.respond_with(successful_capture_response)
+
+ assert_success response
+
+ assert_equal "fedf9ea13afa46872592d62e8cdcb0a3", response.authorization
+ assert response.test?
+ end
+
+ def test_failed_capture
+ response = stub_comms do
+ @gateway.capture(@amount, "")
+ end.respond_with(failed_capture_response)
+
+ assert_failure response
+ assert_equal "Invalid Parameters - order.authId: Required field", response.message
+ assert response.test?
+ end
+
def test_successful_refund
response = stub_comms do
@gateway.purchase(@amount, @credit_card)
@@ -61,6 +105,43 @@ def test_failed_refund
assert_failure response
end
+ def test_successful_credit
+ response = stub_comms do
+ @gateway.credit(@amount, @credit_card)
+ end.respond_with(successful_credit_response)
+
+ assert_success response
+ end
+
+ def test_failed_credit
+ response = stub_comms do
+ @gateway.credit(@amount, @credit_card)
+ end.respond_with(failed_credit_response)
+
+ assert_failure response
+ end
+
+ def test_successful_void
+ response = stub_comms do
+ @gateway.void("auth")
+ end.respond_with(successful_void_response)
+
+ assert_success response
+
+ assert_equal "67686b64b544335815002fd85704c8a1", response.authorization
+ assert response.test?
+ end
+
+ def test_failed_void
+ response = stub_comms do
+ @gateway.void("")
+ end.respond_with(failed_void_response)
+
+ assert_failure response
+ assert_equal "Invalid Parameters - customer.originalOrderNumber: Required field", response.message
+ assert response.test?
+ end
+
def test_successful_store
response = stub_comms do
@gateway.store(@credit_card)
@@ -93,6 +174,18 @@ def test_empty_response_fails
assert_equal "Unable to read error message", response.message
end
+ def test_3d_secure_fields
+ response = stub_comms(@gateway, :ssl_request) do
+ @gateway.purchase(@amount, @credit_card, {xid: '123', cavv: '456', eci: '5'})
+ end.check_request do |method, endpoint, data, headers|
+ assert_match(/xid=123/, data)
+ assert_match(/cavv=456/, data)
+ assert_match(/ECI=5/, data)
+ end.respond_with(successful_purchase_response)
+
+ assert_success response
+ end
+
def test_transcript_scrubbing
assert_equal scrubbed_transcript, @gateway.scrub(transcript)
end
@@ -111,6 +204,42 @@ def failed_purchase_response
)
end
+ def successful_authorize_response
+ %(
+ response.summaryCode=0\r\nresponse.responseCode=08\r\nresponse.text=Honour with identification\r\nresponse.referenceNo=731560096\r\nresponse.orderNumber=21c74c8f08bca415b5373022e6194f74\r\nresponse.RRN=731560096 \r\nresponse.settlementDate=20170314\r\nresponse.transactionDate=14-MAR-2017 05:41:44\r\nresponse.cardSchemeName=VISA\r\nresponse.creditGroup=VI/BC/MC\r\nresponse.previousTxn=0\r\nresponse.authId=C3JVDS\r\nresponse.end\r\n
+ )
+ end
+
+ def failed_authorize_response
+ %(
+ response.summaryCode=1\r\nresponse.responseCode=54\r\nresponse.text=Expired card\r\nresponse.referenceNo=731560142\r\nresponse.orderNumber=d48cb6104266ed1a51647576d8948c57\r\nresponse.RRN=731560142 \r\nresponse.settlementDate=20170314\r\nresponse.transactionDate=14-MAR-2017 05:45:18\r\nresponse.cardSchemeName=VISA\r\nresponse.creditGroup=VI/BC/MC\r\nresponse.previousTxn=0\r\nresponse.end\r\n
+ )
+ end
+
+ def successful_capture_response
+ %(
+ response.summaryCode=0\r\nresponse.responseCode=00\r\nresponse.text=Approved or completed successfully\r\nresponse.referenceNo=731560097\r\nresponse.orderNumber=fedf9ea13afa46872592d62e8cdcb0a3\r\nresponse.RRN=731560097\r\nresponse.settlementDate=20170314\r\nresponse.transactionDate=14-MAR-2017 05:45:52\r\nresponse.cardSchemeName=VISA\r\nresponse.creditGroup=VI/BC/MC\r\nresponse.previousTxn=0\r\nresponse.end\r\n
+ )
+ end
+
+ def failed_capture_response
+ %(
+ response.summaryCode=3\r\nresponse.responseCode=QA\r\nresponse.text=Invalid Parameters - order.authId: Required field\r\nresponse.previousTxn=0\r\nresponse.end\r\n
+ )
+ end
+
+ def successful_void_response
+ %(
+ response.summaryCode=0\r\nresponse.responseCode=00\r\nresponse.text=Approved or completed successfully\r\nresponse.referenceNo=731560098\r\nresponse.orderNumber=67686b64b544335815002fd85704c8a1\r\nresponse.settlementDate=20170314\r\nresponse.cardSchemeName=VISA\r\nresponse.creditGroup=VI/BC/MC\r\nresponse.previousTxn=0\r\nresponse.end\r\n
+ )
+ end
+
+ def failed_void_response
+ %(
+ response.summaryCode=3\r\nresponse.responseCode=QA\r\nresponse.text=Invalid Parameters - customer.originalOrderNumber: Required field\r\nresponse.previousTxn=0\r\nresponse.end\r\n
+ )
+ end
+
def successful_refund_response
%(
response.summaryCode=0\r\nresponse.responseCode=08\r\nresponse.text=Honour with identification\r\nresponse.referenceNo=723907127\r\nresponse.orderNumber=f1a65bfe-f95b-4e06-b800-6d3b3a771238\r\nresponse.RRN=723907127 \r\nresponse.settlementDate=20150228\r\nresponse.transactionDate=28-FEB-2015 09:37:20\r\nresponse.cardSchemeName=VISA\r\nresponse.creditGroup=VI/BC/MC\r\nresponse.previousTxn=0\r\nresponse.end\r\n
@@ -123,6 +252,18 @@ def failed_refund_response
)
end
+ def successful_credit_response
+ %(
+ response.summaryCode=0\r\nresponse.responseCode=08\r\nresponse.text=Honour with identification\r\nresponse.referenceNo=732344591\r\nresponse.orderNumber=f365d21f7f5a1a5fe0eb994f144858e2\r\nresponse.RRN=732344591 \r\nresponse.settlementDate=20170817\r\nresponse.transactionDate=17-AUG-2017 01:19:34\r\nresponse.cardSchemeName=VISA\r\nresponse.creditGroup=VI/BC/MC\r\nresponse.previousTxn=0\r\nresponse.traceCode=799500\r\nresponse.end\r\n
+ )
+ end
+
+ def failed_credit_response
+ %(
+ response.summaryCode=1\r\nresponse.responseCode=14\r\nresponse.text=Invalid card number (no such number)\r\nresponse.referenceNo=732344705\r\nresponse.orderNumber=3baab91d5642a34292375a8932cde85f\r\nresponse.settlementDate=20170817\r\nresponse.cardSchemeName=VISA\r\nresponse.creditGroup=VI/BC/MC\r\nresponse.previousTxn=0\r\nresponse.end\r\n
+ )
+ end
+
def successful_store_response
%(
response.summaryCode=0\r\nresponse.responseCode=00\r\nresponse.text=Approved or completed successfully\r\nresponse.cardSchemeName=VISA\r\nresponse.creditGroup=VI/BC/MC\r\nresponse.accountAlias=400010...224\r\nresponse.preregistrationCode=RSL-20887450\r\nresponse.customerReferenceNumber=RSL-20887450\r\nresponse.previousTxn=0\r\nresponse.end\r\n
diff --git a/test/unit/gateways/realex_test.rb b/test/unit/gateways/realex_test.rb
index 69ced65ae8d..67747a11313 100644
--- a/test/unit/gateways/realex_test.rb
+++ b/test/unit/gateways/realex_test.rb
@@ -95,7 +95,7 @@ def test_deprecated_credit
end
def test_supported_countries
- assert_equal ['IE', 'GB', "FR", "BE", "NL", "LU", "IT"], RealexGateway.supported_countries
+ assert_equal ['IE', 'GB', "FR", "BE", "NL", "LU", "IT", "US", "CA"], RealexGateway.supported_countries
end
def test_supported_card_types
diff --git a/test/unit/gateways/redsys_sha256_test.rb b/test/unit/gateways/redsys_sha256_test.rb
index ef1b8fe74c1..67e093762ba 100644
--- a/test/unit/gateways/redsys_sha256_test.rb
+++ b/test/unit/gateways/redsys_sha256_test.rb
@@ -19,6 +19,8 @@ def setup
end
def test_purchase_payload
+ @credit_card.month = 9
+ @credit_card.year = 2017
@gateway.expects(:ssl_post).with(RedsysGateway.test_url, purchase_request, @headers).returns(successful_purchase_response)
@gateway.purchase(100, @credit_card, :order_id => '144742736014')
end
@@ -260,7 +262,7 @@ def generate_order_id
# one with card and another without.
def purchase_request
- "entrada=%3C%3Fxml+version%3D%221.0%22+encoding%3D%22UTF-8%22%3F%3E%3CREQUEST%3E%3CDATOSENTRADA%3E%3CDS_Version%3E0.1%3C%2FDS_Version%3E%3CDS_MERCHANT_CURRENCY%3E978%3C%2FDS_MERCHANT_CURRENCY%3E%3CDS_MERCHANT_AMOUNT%3E100%3C%2FDS_MERCHANT_AMOUNT%3E%3CDS_MERCHANT_ORDER%3E144742736014%3C%2FDS_MERCHANT_ORDER%3E%3CDS_MERCHANT_TRANSACTIONTYPE%3EA%3C%2FDS_MERCHANT_TRANSACTIONTYPE%3E%3CDS_MERCHANT_PRODUCTDESCRIPTION%2F%3E%3CDS_MERCHANT_TERMINAL%3E1%3C%2FDS_MERCHANT_TERMINAL%3E%3CDS_MERCHANT_MERCHANTCODE%3E091952713%3C%2FDS_MERCHANT_MERCHANTCODE%3E%3CDS_MERCHANT_TITULAR%3ELongbob+Longsen%3C%2FDS_MERCHANT_TITULAR%3E%3CDS_MERCHANT_PAN%3E4548812049400004%3C%2FDS_MERCHANT_PAN%3E%3CDS_MERCHANT_EXPIRYDATE%3E#{(Time.now.year + 1).to_s.slice(2,2)}09%3C%2FDS_MERCHANT_EXPIRYDATE%3E%3CDS_MERCHANT_CVV2%3E123%3C%2FDS_MERCHANT_CVV2%3E%3C%2FDATOSENTRADA%3E%3CDS_SIGNATUREVERSION%3EHMAC_SHA256_V1%3C%2FDS_SIGNATUREVERSION%3E%3CDS_SIGNATURE%3Eq9QH2P%2B4qm8w%2FS85KRPVaepWOrOT2RXlEmyPUce5XRM%3D%3C%2FDS_SIGNATURE%3E%3C%2FREQUEST%3E"
+ "entrada=%3C%3Fxml+version%3D%221.0%22+encoding%3D%22UTF-8%22%3F%3E%3CREQUEST%3E%3CDATOSENTRADA%3E%3CDS_Version%3E0.1%3C%2FDS_Version%3E%3CDS_MERCHANT_CURRENCY%3E978%3C%2FDS_MERCHANT_CURRENCY%3E%3CDS_MERCHANT_AMOUNT%3E100%3C%2FDS_MERCHANT_AMOUNT%3E%3CDS_MERCHANT_ORDER%3E144742736014%3C%2FDS_MERCHANT_ORDER%3E%3CDS_MERCHANT_TRANSACTIONTYPE%3EA%3C%2FDS_MERCHANT_TRANSACTIONTYPE%3E%3CDS_MERCHANT_PRODUCTDESCRIPTION%2F%3E%3CDS_MERCHANT_TERMINAL%3E1%3C%2FDS_MERCHANT_TERMINAL%3E%3CDS_MERCHANT_MERCHANTCODE%3E091952713%3C%2FDS_MERCHANT_MERCHANTCODE%3E%3CDS_MERCHANT_TITULAR%3ELongbob+Longsen%3C%2FDS_MERCHANT_TITULAR%3E%3CDS_MERCHANT_PAN%3E4548812049400004%3C%2FDS_MERCHANT_PAN%3E%3CDS_MERCHANT_EXPIRYDATE%3E1709%3C%2FDS_MERCHANT_EXPIRYDATE%3E%3CDS_MERCHANT_CVV2%3E123%3C%2FDS_MERCHANT_CVV2%3E%3C%2FDATOSENTRADA%3E%3CDS_SIGNATUREVERSION%3EHMAC_SHA256_V1%3C%2FDS_SIGNATUREVERSION%3E%3CDS_SIGNATURE%3Eq9QH2P%2B4qm8w%2FS85KRPVaepWOrOT2RXlEmyPUce5XRM%3D%3C%2FDS_SIGNATURE%3E%3C%2FREQUEST%3E"
end
def purchase_request_with_credit_card_token
diff --git a/test/unit/gateways/safe_charge_test.rb b/test/unit/gateways/safe_charge_test.rb
new file mode 100644
index 00000000000..3cddc23e4e2
--- /dev/null
+++ b/test/unit/gateways/safe_charge_test.rb
@@ -0,0 +1,363 @@
+require 'test_helper'
+
+class SafeChargeTest < Test::Unit::TestCase
+ include CommStub
+
+ def setup
+ @gateway = SafeChargeGateway.new(client_login_id: 'login', client_password: 'password')
+ @credit_card = credit_card
+ @three_ds_enrolled_card = credit_card('4012 0010 3749 0014')
+ @amount = 100
+
+ @options = {
+ order_id: '1',
+ billing_address: address,
+ description: 'Store Purchase'
+ }
+ @merchant_options = @options.merge(
+ merchant_descriptor: 'Test Descriptor',
+ merchant_phone_number: '(555)555-5555',
+ merchant_name: 'Test Merchant'
+ )
+ @three_ds_options = @options.merge(three_d_secure: true)
+ end
+
+ def test_successful_purchase
+ @gateway.expects(:ssl_post).returns(successful_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal '111951|101508189567|ZQBpAFAASABGAHAAVgBPAFUAMABiADMAewBtAGsAd' \
+ 'AAvAFIAQQBrAGoAYwBxACoAXABHAEEAOgA3ACsAMgA4AD0AOABDAG4AbQAzAF' \
+ "UAbQBYAFIAMwA=|%02d|%d|1.00|USD" % [@credit_card.month, @credit_card.year.to_s[-2..-1]], response.authorization
+ assert response.test?
+ end
+
+ def test_successful_purchase_with_merchant_options
+ purchase = stub_comms do
+ @gateway.purchase(@amount, @credit_card, @merchant_options)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/sg_Descriptor/, data)
+ assert_match(/sg_MerchantPhoneNumber/, data)
+ assert_match(/sg_MerchantName/, data)
+ end.respond_with(successful_purchase_response)
+
+ assert_success purchase
+ assert_equal '111951|101508189567|ZQBpAFAASABGAHAAVgBPAFUAMABiADMAewBtAGsAd' \
+ 'AAvAFIAQQBrAGoAYwBxACoAXABHAEEAOgA3ACsAMgA4AD0AOABDAG4AbQAzAF' \
+ "UAbQBYAFIAMwA=|%02d|%d|1.00|USD" % [@credit_card.month, @credit_card.year.to_s[-2..-1]], purchase.authorization
+ assert purchase.test?
+ end
+
+ def test_successful_purchase_with_truthy_stored_credential_mode
+ purchase = stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options.merge(stored_credential_mode: true))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/sg_StoredCredentialMode=1/, data)
+ end.respond_with(successful_purchase_response)
+
+ assert_success purchase
+ assert_equal '111951|101508189567|ZQBpAFAASABGAHAAVgBPAFUAMABiADMAewBtAGsAd' \
+ 'AAvAFIAQQBrAGoAYwBxACoAXABHAEEAOgA3ACsAMgA4AD0AOABDAG4AbQAzAF' \
+ "UAbQBYAFIAMwA=|%02d|%d|1.00|USD" % [@credit_card.month, @credit_card.year.to_s[-2..-1]], purchase.authorization
+ assert purchase.test?
+ end
+
+ def test_successful_purchase_with_falsey_stored_credential_mode
+ purchase = stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options.merge(stored_credential_mode: false))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/sg_StoredCredentialMode=0/, data)
+ end.respond_with(successful_purchase_response)
+
+ assert_success purchase
+ assert_equal '111951|101508189567|ZQBpAFAASABGAHAAVgBPAFUAMABiADMAewBtAGsAd' \
+ 'AAvAFIAQQBrAGoAYwBxACoAXABHAEEAOgA3ACsAMgA4AD0AOABDAG4AbQAzAF' \
+ "UAbQBYAFIAMwA=|%02d|%d|1.00|USD" % [@credit_card.month, @credit_card.year.to_s[-2..-1]], purchase.authorization
+ assert purchase.test?
+ end
+
+ def test_failed_purchase
+ @gateway.expects(:ssl_post).returns(failed_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal "0", response.error_code
+ end
+
+ def test_successful_authorize
+ @gateway.expects(:ssl_post).returns(successful_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+
+ assert_equal '111534|101508189855|MQBVAG4ASABkAEgAagB3AEsAbgAtACoAWgAzAFwAW' \
+ 'wBNAF8ATQBUAD0AegBQAGwAQAAtAD0AXAB5AFkALwBtAFAALABaAHoAOgBFAE' \
+ "wAUAA1AFUAMwA=|%02d|%d|1.00|USD" % [@credit_card.month, @credit_card.year.to_s[-2..-1]], response.authorization
+ assert response.test?
+ end
+
+ def test_failed_authorize
+ @gateway.expects(:ssl_post).returns(failed_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal "0", response.error_code
+ end
+
+ def test_successful_capture
+ @gateway.expects(:ssl_post).returns(successful_capture_response)
+
+ response = @gateway.capture(@amount, "auth|transaction_id|token|month|year|amount|currency")
+ assert_success response
+
+ assert_equal '111301|101508190200|RwA1AGQAMgAwAEkAWABKADkAcABjAHYAQQA4AC8AZ' \
+ 'AAlAHMAfABoADEALAA8ADQAewB8ADsAewBiADsANQBoACwAeAA/AGQAXQAjAF' \
+ 'EAYgBVAHIAMwA=|month|year|1.00|currency', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_capture
+ @gateway.expects(:ssl_post).returns(failed_capture_response)
+
+ response = @gateway.capture(@amount, '', @options)
+ assert_failure response
+ assert_equal "1163", response.error_code
+ end
+
+ def test_successful_refund
+ @gateway.expects(:ssl_post).returns(successful_refund_response)
+
+ response = @gateway.refund(@amount, 'authorization', @options)
+ assert_success response
+ assert_equal 'Success', response.message
+ end
+
+ def test_failed_refund
+ @gateway.expects(:ssl_post).returns(failed_refund_response)
+
+ response = @gateway.refund(@amount, '', @options)
+ assert_failure response
+ assert_equal "1163", response.error_code
+ end
+
+ def test_successful_credit
+ @gateway.expects(:ssl_post).returns(successful_credit_response)
+
+ response = @gateway.credit(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'Success', response.message
+ end
+
+ def test_failed_credit
+ @gateway.expects(:ssl_post).returns(failed_credit_response)
+
+ response = @gateway.credit(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal 'Decline', response.message
+ end
+
+ def test_successful_void
+ @gateway.expects(:ssl_post).returns(successful_void_response)
+
+ response = @gateway.void("auth|transaction_id|token|month|year|amount|currency")
+ assert_success response
+
+ assert_equal '111171|101508208625|ZQBpAFAAZgBuAHEATgBUAHcASAAwADUAcwBHAHQAV' \
+ 'QBLAHAAbgB6AGwAJAA1AEMAfAB2AGYASwBrAHEAeQBOAEwAOwBZAGIAewB4AG' \
+ 'wAYwBUAE0AMwA=|month|year|0.00|currency', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_void
+ @gateway.expects(:ssl_post).returns(failed_void_response)
+
+ response = @gateway.void('')
+ assert_failure response
+ assert_equal "Invalid Amount", response.message
+ assert response.test?
+ end
+
+ def test_successful_verify
+ @gateway.expects(:ssl_post).times(2).returns(successful_authorize_response, successful_void_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+
+ assert_equal '111534|101508189855|MQBVAG4ASABkAEgAagB3AEsAbgAtACoAWgAzAFwAW' \
+ 'wBNAF8ATQBUAD0AegBQAGwAQAAtAD0AXAB5AFkALwBtAFAALABaAHoAOgBFAE' \
+ "wAUAA1AFUAMwA=|%02d|%d|1.00|USD" % [@credit_card.month, @credit_card.year.to_s[-2..-1]], response.authorization
+ assert response.test?
+ end
+
+ def test_successful_verify_with_failed_void
+ @gateway.expects(:ssl_post).times(2).returns(successful_authorize_response, failed_void_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_success response
+ end
+
+ def test_failed_verify
+ @gateway.expects(:ssl_post).returns(failed_authorize_response)
+
+ response = @gateway.verify(@credit_card, @options)
+ assert_failure response
+ assert_equal "0", response.error_code
+ end
+
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
+ def test_3ds_response
+ purchase = stub_comms do
+ @gateway.purchase(@amount, @three_ds_enrolled_card, @three_ds_options)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/Sale3D/, data)
+ assert_match(/sg_APIType/, data)
+ end.respond_with(successful_3ds_purchase_response)
+
+ assert_success purchase
+ assert_equal "MDAwMDAwMDAwMDE1MTAxMDgzMTA=", purchase.params["xid"]
+ assert_equal "eJxVUdtuwjAM/ZWK95GYgijIjVTWaUNTGdqQ4DUKFq2gF9J0A75+SVcuixTF59g+sY5xlWqi+ItUo0lgQnUtd+Rl27BXyScYAQce+MB7ApfRJx0FfpOus7IQ0Of9AbIrtK1apbIwAqU6zuYLMQSY8ABZBzEnPY8FfzhjGCH7o7GQOYlIq9J4K6qNd5VD1mZQlU1h9FkEQ47sCrDRB5EaU00ZO5RKHtKyth2ORXYfaNm4qLYqp2wrkjj6ud8XSFbRKYl3F/uGyFwFbqUhMeAwBvC5B6Opz6c+IGt5lLn73hlgR+kAVu6PqMu4xCOB1l1NhTqLydg6ckNIp6osyFZYJ28xsvvAz2/OT2WsRa+bdf2+X6cXtd9oHxZNPks+ojB0DrcFTi2zrkDAJ62cA8icBOuWx7oF2+jf4n8B", purchase.params["pareq"]
+ assert_equal "https://pit.3dsecure.net/VbVTestSuiteService/pit1/acsService/paReq?summary=MjRlZGYwY2EtZTk5Zi00NDJjLTljOTAtNWUxZmRhMjEwODg3", purchase.params["acsurl"]
+ end
+
+ private
+
+ def pre_scrubbed
+ %q(
+opening connection to process.sandbox.safecharge.com:443...
+opened
+starting SSL for process.sandbox.safecharge.com:443...
+SSL established
+<- "POST /service.asmx/Process HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: process.sandbox.safecharge.com\r\nContent-Length: 249\r\n\r\n"
+<- "sg_TransType=Sale&sg_Currency=USD&sg_Amount=1.00&sg_ClientLoginID=SpreedlyTestTRX&sg_ClientPassword=5Jp5xKmgqY&sg_ResponseFormat=4&sg_Version=4.1.0&sg_NameOnCard=Longbob+Longsen&sg_CardNumber=4000100011112224&sg_ExpMonth=09&sg_ExpYear=18&sg_CVV2=123"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Cache-Control: private, max-age=0\r\n"
+-> "Content-Type: text/xml; charset=utf-8\r\n"
+-> "Content-Encoding: gzip\r\n"
+-> "Vary: Accept-Encoding\r\n"
+-> "Server: Microsoft-IIS/8.5\r\n"
+-> "X-AspNet-Version: 4.0.30319\r\n"
+-> "X-Powered-By: ASP.NET\r\n"
+-> "Date: Wed, 29 Mar 2017 18:28:17 GMT\r\n"
+-> "Connection: close\r\n"
+-> "Content-Length: 727\r\n"
+-> "Set-Cookie: visid_incap_847807=oQqFyASiS0y3sQoZ55M7TsH821gAAAAAQUIPAAAAAAA/rRn9PSjQ7LsSqhb2S1AZ; expires=Thu, 29 Mar 2018 13:12:58 GMT; path=/; Domain=.sandbox.safecharge.com\r\n"
+-> "Set-Cookie: incap_ses_225_847807=H1/pC1tNgzhTmiAXOl0fA8H821gAAAAAFE9hBYJtG83f0yrtcxrGsg==; path=/; Domain=.sandbox.safecharge.com\r\n"
+-> "X-Iinfo: 9-132035054-132035081 NNNN CT(207 413 0) RT(1490812095742 212) q(0 0 6 -1) r(14 14) U5\r\n"
+-> "X-CDN: Incapsula\r\n"
+-> "\r\n"
+reading 727 bytes...
+ )
+ end
+
+ def post_scrubbed
+ %q(
+opening connection to process.sandbox.safecharge.com:443...
+opened
+starting SSL for process.sandbox.safecharge.com:443...
+SSL established
+<- "POST /service.asmx/Process HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: process.sandbox.safecharge.com\r\nContent-Length: 249\r\n\r\n"
+<- "sg_TransType=Sale&sg_Currency=USD&sg_Amount=1.00&sg_ClientLoginID=SpreedlyTestTRX&sg_ClientPassword=[FILTERED]&sg_ResponseFormat=4&sg_Version=4.1.0&sg_NameOnCard=Longbob+Longsen&sg_CardNumber=[FILTERED]&sg_ExpMonth=09&sg_ExpYear=18&sg_CVV2=[FILTERED]"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Cache-Control: private, max-age=0\r\n"
+-> "Content-Type: text/xml; charset=utf-8\r\n"
+-> "Content-Encoding: gzip\r\n"
+-> "Vary: Accept-Encoding\r\n"
+-> "Server: Microsoft-IIS/8.5\r\n"
+-> "X-AspNet-Version: 4.0.30319\r\n"
+-> "X-Powered-By: ASP.NET\r\n"
+-> "Date: Wed, 29 Mar 2017 18:28:17 GMT\r\n"
+-> "Connection: close\r\n"
+-> "Content-Length: 727\r\n"
+-> "Set-Cookie: visid_incap_847807=oQqFyASiS0y3sQoZ55M7TsH821gAAAAAQUIPAAAAAAA/rRn9PSjQ7LsSqhb2S1AZ; expires=Thu, 29 Mar 2018 13:12:58 GMT; path=/; Domain=.sandbox.safecharge.com\r\n"
+-> "Set-Cookie: incap_ses_225_847807=H1/pC1tNgzhTmiAXOl0fA8H821gAAAAAFE9hBYJtG83f0yrtcxrGsg==; path=/; Domain=.sandbox.safecharge.com\r\n"
+-> "X-Iinfo: 9-132035054-132035081 NNNN CT(207 413 0) RT(1490812095742 212) q(0 0 6 -1) r(14 14) U5\r\n"
+-> "X-CDN: Incapsula\r\n"
+-> "\r\n"
+reading 727 bytes...
+ )
+ end
+
+ def successful_purchase_response
+ %(
+ 4.1.0SpreedlyTestTRX101508189567APPROVED11195100ZQBpAFAASABGAHAAVgBPAFUAMABiADMAewBtAGsAdAAvAFIAQQBrAGoAYwBxACoAXABHAEEAOgA3ACsAMgA4AD0AOABDAG4AbQAzAFUAbQBYAFIAMwA=19University First Federal Credit UnionusSuiMHP60FrDKfyaJs47hqqrR/JU=0CreditAccept
+ )
+ end
+
+ def failed_purchase_response
+ %(
+ 4.1.0SpreedlyTestTRX101508189637DECLINEDDecline-10bwBVAEYAUgBuAGcAbABSAFYASgB5AEAAMgA/ACsAUQBIAC4AbgB1AHgAdABAAE8ARgBRAGoAbwApACQAWwBKAFwATwAxAEcAMwBZAG4AdwBmACgAMwA=19GyueFkuQqW+UL38d57fuA5/RqfQ=0Accept
+ )
+ end
+
+ def successful_authorize_response
+ %(
+ 4.1.0SpreedlyTestTRX101508189855APPROVED11153400MQBVAG4ASABkAEgAagB3AEsAbgAtACoAWgAzAFwAWwBNAF8ATQBUAD0AegBQAGwAQAAtAD0AXAB5AFkALwBtAFAALABaAHoAOgBFAEwAUAA1AFUAMwA=19University First Federal Credit UnionusSuiMHP60FrDKfyaJs47hqqrR/JU=0CreditAccept
+ )
+ end
+
+ def failed_authorize_response
+ %(
+ 4.1.0SpreedlyTestTRX101508190604DECLINEDDecline-10MQBLAG4AMgAwADMAOABmAFYANABbAGYAcwA+ACMAVgBXAD0AUQBQAEoANQBrAHQAWABsAFEAeABQAF8ARwA6ACsALgBHADUALwBTAEAARwBIACgAMwA=19GyueFkuQqW+UL38d57fuA5/RqfQ=0Accept
+ )
+ end
+
+ def successful_capture_response
+ %(
+ 4.1.0SpreedlyTestTRX101508190200APPROVED11130100RwA1AGQAMgAwAEkAWABKADkAcABjAHYAQQA4AC8AZAAlAHMAfABoADEALAA8ADQAewB8ADsAewBiADsANQBoACwAeAA/AGQAXQAjAFEAYgBVAHIAMwA=19University First Federal Credit UnionusSuiMHP60FrDKfyaJs47hqqrR/JU=0Credit
+ )
+ end
+
+ def failed_capture_response
+ %(
+ 4.1.0SpreedlyTestTRX101508190627ERRORTransaction must contain a Card/Token/Account-11001163-12jmj7l5rSw0yVb/vlWAYkK/YBwk=0
+ )
+ end
+
+ def successful_refund_response
+ %(
+ 4.1.0SpreedlyTestTRX101508440432APPROVED11120700MQBVAG4AUgAwAFcAaABxAGoASABdAE4ALABvAGYANAAmAE8AcQA/AEgAawAkAHYASQBKAFMAegBiACoAcQBBAC8AVABlAD4AKwBkAC0AKwA8ACcAMwA=19SuiMHP60FrDKfyaJs47hqqrR/JU=0
+ )
+ end
+
+ def failed_refund_response
+ %(
+ 4.1.0SpreedlyTestTRX101508208595ERRORTransaction must contain a Card/Token/Account-11001163-12jmj7l5rSw0yVb/vlWAYkK/YBwk=0
+ )
+ end
+
+ def successful_credit_response
+ %(
+ 4.1.0SpreedlyTestTRX101508440421APPROVED11164400bwA1ADAAcAAwAHUAVABJAFYAUQAlAGcAfAB8AFQAbwBkAHAAbwAjAG4AaABDAHsAUABdACoAYwBaAEsAMQBHAEUAMQBuAHQAdwBXAFUAVABZACMAMwA=19SuiMHP60FrDKfyaJs47hqqrR/JU=0
+ )
+ end
+
+ def failed_credit_response
+ %(
+ 4.1.0SpreedlyTestTRX101508440424DECLINEDDecline-10RwBVAGQAZgAwAFMAbABwAEwASgBNAFMAXABJAGAAeAAsAHsALAA7ADUAOgBUAEMAZwBNAG4AbABQAC4AQAAvAC0APwBpAEAAWQBoACMAdwBvAGEAMwA=19GyueFkuQqW+UL38d57fuA5/RqfQ=0
+ )
+ end
+
+ def successful_void_response
+ %(
+ 4.1.0SpreedlyTestTRX101508208625APPROVED11117100ZQBpAFAAZgBuAHEATgBUAHcASAAwADUAcwBHAHQAVQBLAHAAbgB6AGwAJAA1AEMAfAB2AGYASwBrAHEAeQBOAEwAOwBZAGIAewB4AGwAYwBUAE0AMwA=19University First Federal Credit UnionusSuiMHP60FrDKfyaJs47hqqrR/JU=0Credit
+ )
+ end
+
+ def failed_void_response
+ %(
+ 4.1.0SpreedlyTestTRX101508208633ERRORInvalid Amount-11001201-12jmj7l5rSw0yVb/vlWAYkK/YBwk=0
+ )
+ end
+
+ def successful_3ds_purchase_response
+ %(
+ 4.1.0SpreedlyManTestTRX98bd80c8c9534088311153ad6a67d108101510108310APPROVED00ZQBpAFAAMwBTAEcAMQBZAHcASQA4ADoAPQBlACQAZAB3ACMAWwAyAFoAWQBLAFUAPwBTAHYAKQAnAHQAUAA2AHYAYwAoAG0ARgBNAEEAcAAlAGEAMwA=YeJxVUdtuwjAM/ZWK95GYgijIjVTWaUNTGdqQ4DUKFq2gF9J0A75+SVcuixTF59g+sY5xlWqi+ItUo0lgQnUtd+Rl27BXyScYAQce+MB7ApfRJx0FfpOus7IQ0Of9AbIrtK1apbIwAqU6zuYLMQSY8ABZBzEnPY8FfzhjGCH7o7GQOYlIq9J4K6qNd5VD1mZQlU1h9FkEQ47sCrDRB5EaU00ZO5RKHtKyth2ORXYfaNm4qLYqp2wrkjj6ud8XSFbRKYl3F/uGyFwFbqUhMeAwBvC5B6Opz6c+IGt5lLn73hlgR+kAVu6PqMu4xCOB1l1NhTqLydg6ckNIp6osyFZYJ28xsvvAz2/OT2WsRa+bdf2+X6cXtd9oHxZNPks+ojB0DrcFTi2zrkDAJ62cA8icBOuWx7oF2+jf4n8B000000000000715https://pit.3dsecure.net/VbVTestSuiteService/pit1/acsService/paReq?summary=MjRlZGYwY2EtZTk5Zi00NDJjLTljOTAtNWUxZmRhMjEwODg3MDAwMDAwMDAwMDE1MTAxMDgzMTA=19Visa Production Support Client Bid 1usrDNDlh6XR8R6CVdGQyqDkZzdqE0=10Debit01EUR1EUR
+ )
+ end
+end
diff --git a/test/unit/gateways/sage_pay_test.rb b/test/unit/gateways/sage_pay_test.rb
index 439b1483577..fda0187a170 100644
--- a/test/unit/gateways/sage_pay_test.rb
+++ b/test/unit/gateways/sage_pay_test.rb
@@ -57,20 +57,39 @@ def test_capture_url
assert_equal 'https://test.sagepay.com/gateway/service/release.vsp', @gateway.send(:url_for, :capture)
end
- def test_avs_result
+ def test_matched_avs_result
+ @gateway.expects(:ssl_post).returns(unsuccessful_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+
+ assert_equal 'Y', response.avs_result['postal_match']
+ assert_equal 'Y', response.avs_result['street_match']
+ end
+
+ def test_partially_matched_avs_result
@gateway.expects(:ssl_post).returns(successful_purchase_response)
response = @gateway.purchase(@amount, @credit_card, @options)
+
assert_equal 'Y', response.avs_result['postal_match']
assert_equal 'N', response.avs_result['street_match']
end
- def test_cvv_result
- @gateway.expects(:ssl_post).returns(successful_purchase_response)
+ def test_matched_cvv_result
+ @gateway.expects(:ssl_post).returns(unsuccessful_purchase_response)
- response = @gateway.purchase(@amount, @credit_card, @options)
- assert_equal 'N', response.cvv_result['code']
- end
+ response = @gateway.purchase(@amount, @credit_card, @options)
+
+ assert_equal 'M', response.cvv_result['code']
+ end
+
+ def test_not_matched_cvv_result
+ @gateway.expects(:ssl_post).returns(successful_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+
+ assert_equal 'N', response.cvv_result['code']
+ end
def test_dont_send_fractional_amount_for_chinese_yen
@amount = 100_00 # 100 YEN
@@ -285,6 +304,35 @@ def test_truncate_accounts_for_url_encoding
assert_equal "Joikam Lomström", @gateway.send(:truncate, "Joikam Lomström Rate", 20)
end
+ def test_successful_authorization_and_capture_and_refund
+ auth = stub_comms do
+ @gateway.authorize(@amount, @credit_card, @options)
+ end.respond_with(successful_authorize_response)
+ assert_success auth
+
+ capture = stub_comms do
+ @gateway.capture(@amount, auth.authorization)
+ end.respond_with(successful_capture_response)
+ assert_success capture
+
+ refund = stub_comms do
+ @gateway.refund(@amount, capture.authorization,
+ order_id: generate_unique_id,
+ description: "Refund txn"
+ )
+ end.respond_with(successful_refund_response)
+ assert_success refund
+ end
+
+ def test_repeat_purchase_with_reference_token
+ stub_comms(@gateway, :ssl_request) do
+ @gateway.purchase(@amount, "1455548a8d178beecd88fe6a285f50ff;{0D2ACAF0-FA64-6DFF-3869-7ADDDC1E0474};15353766;BS231FNE14;purchase", @options)
+ end.check_request do |method, endpoint, data, headers|
+ assert_match(/RelatedVPSTxId=%7B0D2ACAF0-FA64-6DFF-3869-7ADDDC1E0474%/, data)
+ assert_match(/TxType=REPEAT/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
private
def purchase_with_options(optional)
@@ -339,6 +387,25 @@ def successful_authorize_response
RESP
end
+ def successful_refund_response
+ <<-RESP
+VPSProtocol=3.00
+Status=OK
+StatusDetail=0000 : The Authorisation was Successful.
+SecurityKey=KUMJBP02HM
+TxAuthNo=15282432
+VPSTxId={08C870A9-1E53-3852-BA44-CBC91612CBCA}
+ RESP
+ end
+
+ def successful_capture_response
+ <<-RESP
+VPSProtocol=3.00
+Status=OK
+StatusDetail=2004 : The Release was Successful.
+ RESP
+ end
+
def unsuccessful_authorize_response
<<-RESP
VPSProtocol=2.23
diff --git a/test/unit/gateways/sage_test.rb b/test/unit/gateways/sage_test.rb
index 0c805fb4b2f..314490ed87a 100644
--- a/test/unit/gateways/sage_test.rb
+++ b/test/unit/gateways/sage_test.rb
@@ -185,7 +185,7 @@ def test_cvv_result
assert_equal 'M', response.cvv_result['code']
end
- def test_us_address_with_state
+ def test_address_with_state
post = {}
options = {
:billing_address => { :country => "US", :state => "CA"}
@@ -196,15 +196,15 @@ def test_us_address_with_state
assert_equal "CA", post[:C_state]
end
- def test_us_address_without_state
+ def test_address_without_state
post = {}
options = {
- :billing_address => { :country => "US", :state => ""}
+ :billing_address => { :country => "NZ", :state => ""}
}
@gateway.send(:add_addresses, post, options)
- assert_equal "US", post[:C_country]
- assert_equal "", post[:C_state]
+ assert_equal "NZ", post[:C_country]
+ assert_equal "Outside of US", post[:C_state]
end
def test_successful_check_purchase
@@ -316,6 +316,7 @@ def test_failed_unstore
def test_scrub
assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ assert_equal @gateway.scrub(pre_scrubbed_echeck), post_scrubbed_echeck
end
def test_supports_scrubbing?
@@ -488,4 +489,51 @@ def post_scrubbed
Conn close
POST_SCRUBBED
end
+
+ def pre_scrubbed_echeck
+ <<-PRE_SCRUBBED
+opening connection to www.sagepayments.net:443...
+opened
+starting SSL for www.sagepayments.net:443...
+SSL established
+<- "POST /cgi-bin/eftVirtualCheck.dll?transaction HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: www.sagepayments.net\r\nContent-Length: 562\r\n\r\n"
+<- "C_first_name=Jim&C_last_name=Smith&C_rte=244183602&C_acct=15378535&C_check_number=1&C_acct_type=DDA&C_customer_type=WEB&C_originator_id=&T_addenda=&C_ssn=&C_dl_state_code=&C_dl_number=&C_dob=&T_amt=1.00&T_ordernum=0ac6fd1f74a98de94bf9&C_address=456+My+Street&C_city=Ottawa&C_state=ON&C_zip=K1C2N6&C_country=CA&C_telephone=%28555%29555-5555&C_fax=%28555%29555-6666&C_email=longbob%40example.com&C_ship_name=Jim+Smith&C_ship_address=456+My+Street&C_ship_city=Ottawa&C_ship_state=ON&C_ship_zip=K1C2N6&C_ship_country=CA&M_id=562313162894&M_key=J6U9B3G2F6L3&T_code=01"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Cache-Control: no-cache\r\n"
+-> "Pragma: no-cache\r\n"
+-> "Transfer-Encoding: chunked\r\n"
+-> "Content-Type: text/html; charset=us-ascii\r\n"
+-> "Content-Encoding: gzip\r\n"
+-> "Expires: -1\r\n"
+-> "Vary: Accept-Encoding\r\n"
+-> "Server: Microsoft-IIS/7.5\r\n"
+-> "X-Powered-By: ASP.NET\r\n"
+-> "Date: Thu, 02 Nov 2017 13:26:30 GMT\r\n"
+-> "Connection: close\r\n"
+-> "\r\n"
+-> "ac\r\n"
+reading 172 bytes...
+-> "\x1F\x8B\b\x00\x00\x00\x00\x00\x04\x00\xED\xBD\a`\x1CI\x96%&/m\xCA{\x7FJ\xF5J\xD7\xE0t\xA1\b\x80`\x13$\xD8\x90@\x10\xEC\xC1\x88\xCD\xE6\x92\xEC\x1DiG#)\xAB*\x81\xCAeVe]f\x16@\xCC\xED\x9D\xBC\xF7\xDE{\xEF\xBD\xF7\xDE{\xEF\xBD\xF7\xBA;\x9DN'\xF7\xDF\xFF?\\fd\x01l\xF6\xCEJ\xDA\xC9\x9E!\x80\xAA\xC8\x1F?~|\x1F?\"~\xAD\xE3\x94\x9F\xE3\x93\x93\xD3\x97oN\x9F\xD2\xAF\xD1gg\xE7\xD9\x93\xBDo?\xFC\x89\xAF\x16\xF7v~\xA7\x9Dl\xFA\xE9\xF9l\xF7\xFC\xC1~\xF6\xF0`\x96?\xDC\x9F\x9C?\xFC\x9Dv~\xA7\x17_\xBE8\xA5\x1F\xBF"
+read 172 bytes
+reading 2 bytes...
+-> "\r\n"
+read 2 bytes
+-> "b\r\n"
+reading 11 bytes...
+-> "\xF6\xFF\x03\x90\xEB\x1E T\x00\x00\x00"
+read 11 bytes
+reading 2 bytes...
+-> "\r\n"
+read 2 bytes
+-> "0\r\n"
+-> "\r\n"
+Conn close
+ PRE_SCRUBBED
+ end
+
+ def post_scrubbed_echeck
+ <<-POST_SCRUBBED
+opening connection to www.sagepayments.net:443...\nopened\nstarting SSL for www.sagepayments.net:443...\nSSL established\n<- \"POST /cgi-bin/eftVirtualCheck.dll?transaction HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: www.sagepayments.net\r\nContent-Length: 562\r\n\r\n\"\n<- \"C_first_name=Jim&C_last_name=Smith&C_rte=[FILTERED]&C_acct=[FILTERED]&C_check_number=1&C_acct_type=DDA&C_customer_type=WEB&C_originator_id=&T_addenda=&C_ssn=[FILTERED]&C_dl_state_code=&C_dl_number=&C_dob=&T_amt=1.00&T_ordernum=0ac6fd1f74a98de94bf9&C_address=456+My+Street&C_city=Ottawa&C_state=ON&C_zip=K1C2N6&C_country=CA&C_telephone=%28555%29555-5555&C_fax=%28555%29555-6666&C_email=longbob%40example.com&C_ship_name=Jim+Smith&C_ship_address=456+My+Street&C_ship_city=Ottawa&C_ship_state=ON&C_ship_zip=K1C2N6&C_ship_country=CA&M_id=[FILTERED]&M_key=[FILTERED]&T_code=01\"\n-> \"HTTP/1.1 200 OK\r\n\"\n-> \"Cache-Control: no-cache\r\n\"\n-> \"Pragma: no-cache\r\n\"\n-> \"Transfer-Encoding: chunked\r\n\"\n-> \"Content-Type: text/html; charset=us-ascii\r\n\"\n-> \"Content-Encoding: gzip\r\n\"\n-> \"Expires: -1\r\n\"\n-> \"Vary: Accept-Encoding\r\n\"\n-> \"Server: Microsoft-IIS/7.5\r\n\"\n-> \"X-Powered-By: ASP.NET\r\n\"\n-> \"Date: Thu, 02 Nov 2017 13:26:30 GMT\r\n\"\n-> \"Connection: close\r\n\"\n-> \"\r\n\"\n-> \"ac\r\n\"\nreading 172 bytes...\n-> \"\u001F?\b\u0000\u0000\u0000\u0000\u0000\u0004\u0000??\a`\u001CI?%&/m?{\u007FJ?J??t?\b?`\u0013$?@\u0010??????\u001DiG#)?*??eVe]f\u0016@????{???{???;?N'????\\fd\u0001l??J??!???\u001F?~|\u001F?\"~????oN???gg???o????\u0016?v~??l???l???~??`???????v~?\u0017_?8?\u001F?\"\nread 172 bytes\nreading 2 bytes...\n-> \"\r\n\"\nread 2 bytes\n-> \"b\r\n\"\nreading 11 bytes...\n-> \"??\u0003??\u001E T\u0000\u0000\u0000\"\nread 11 bytes\nreading 2 bytes...\n-> \"\r\n\"\nread 2 bytes\n-> \"0\r\n\"\n-> \"\r\n\"\nConn close
+ POST_SCRUBBED
+ end
end
diff --git a/test/unit/gateways/secure_net_test.rb b/test/unit/gateways/secure_net_test.rb
index 495a733ea27..f1c83e93698 100644
--- a/test/unit/gateways/secure_net_test.rb
+++ b/test/unit/gateways/secure_net_test.rb
@@ -182,6 +182,11 @@ def test_passes_without_test_mode
end.respond_with(successful_purchase_response)
end
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
private
# Place raw successful response from gateway here
@@ -225,4 +230,49 @@ def failed_refund_response
'301R3CREDIT CANNOT BE COMPLETED ON AN UNSETTLED TRANSACTION0000500
FALSE0FALSEFALSECC1285171984419000100255001.000'
end
+ def pre_scrubbed
+ <<-EOS
+opening connection to certify.securenet.com:443...
+opened
+starting SSL for certify.securenet.com:443...
+SSL established
+<- "POST /API/gateway.svc/webHttp/ProcessTransaction HTTP/1.1\r\nContent-Type: text/xml\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: certify.securenet.com\r\nContent-Length: 1044\r\n\r\n"
+<- "1.00123400010001111222409190100
456 My StreetOttawaWidgets IncCALongbobLongsen(555)555-5555ONK1C2N6010BI8gL8HO1dKP7001218CCStore Purchase151992186896260900TRUE00"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Content-Length: 2547\r\n"
+-> "Content-Type: application/xml; charset=utf-8\r\n"
+-> "X-Powered-By: ASP.NET\r\n"
+-> "Date: Thu, 01 Mar 2018 16:31:01 GMT\r\n"
+-> "Connection: close\r\n"
+-> "Set-Cookie: TS01e56b0e=010bfb2c76b6671aabf6f176a4e5aefd8e7a6ce7f697d82dfcfd424edede4ae7d4dba7557a4a7a13a539cfc1c5c061e08d5040811a; Path=/\r\n"
+-> "\r\n"
+reading 2547 bytes...
+-> "10000Approved0JUJQLQ1.00Y0LongbobLongsenVIM000100
456 My StreetOttawaWidgets IncCAFALSELongbobLongsen(555)555-5555ONK1C2N609190P2224FALSEFALSECCStore Purchase151992186896260970012181.000301201811310101.0003012018113101116186071"
+read 2547 bytes
+Conn close
+ EOS
+ end
+
+ def post_scrubbed
+ <<-EOS
+opening connection to certify.securenet.com:443...
+opened
+starting SSL for certify.securenet.com:443...
+SSL established
+<- "POST /API/gateway.svc/webHttp/ProcessTransaction HTTP/1.1\r\nContent-Type: text/xml\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: certify.securenet.com\r\nContent-Length: 1044\r\n\r\n"
+<- "1.00[FILTERED][FILTERED]09190100
456 My StreetOttawaWidgets IncCALongbobLongsen(555)555-5555ONK1C2N6010[FILTERED]7001218CCStore Purchase151992186896260900TRUE00"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Content-Length: 2547\r\n"
+-> "Content-Type: application/xml; charset=utf-8\r\n"
+-> "X-Powered-By: ASP.NET\r\n"
+-> "Date: Thu, 01 Mar 2018 16:31:01 GMT\r\n"
+-> "Connection: close\r\n"
+-> "Set-Cookie: TS01e56b0e=010bfb2c76b6671aabf6f176a4e5aefd8e7a6ce7f697d82dfcfd424edede4ae7d4dba7557a4a7a13a539cfc1c5c061e08d5040811a; Path=/\r\n"
+-> "\r\n"
+reading 2547 bytes...
+-> "10000Approved0JUJQLQ1.00Y0LongbobLongsenVIM000100
456 My StreetOttawaWidgets IncCAFALSELongbobLongsen(555)555-5555ONK1C2N609190P2224FALSEFALSECCStore Purchase151992186896260970012181.000301201811310101.0003012018113101116186071"
+read 2547 bytes
+Conn close
+ EOS
+ end
end
diff --git a/test/unit/gateways/secure_pay_au_test.rb b/test/unit/gateways/secure_pay_au_test.rb
index e3060eb70e0..af937c7910a 100644
--- a/test/unit/gateways/secure_pay_au_test.rb
+++ b/test/unit/gateways/secure_pay_au_test.rb
@@ -191,6 +191,14 @@ def test_successful_triggered_payment
assert_equal 'test3', response.params['client_id']
end
+ def test_scrub
+ assert_equal @gateway.scrub(pre_scrub), post_scrub
+ end
+
+ def test_supports_scrubbing?
+ assert @gateway.supports_scrubbing?
+ end
+
private
def successful_store_response
@@ -482,4 +490,50 @@ def successful_refund_response
def failed_refund_response
%(6bacab2b7ae1200d8099e0873e25bc20102807071248484000+600xml-4.2PaymentCAX0001000Normal423101AUD269061No134Only $1.00 available for refund300000999Error - Transaction Already Fully Refunded/Only $x.xx Available for Refund444433...11109/116Visa)
end
+
+ def pre_scrub
+ <<-XML
+ opening connection to api.securepay.com.au:443...
+ opened
+ starting SSL for api.securepay.com.au:443...
+ SSL established
+ <- "POST /test/payment HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api.securepay.com.au\r\nContent-Length: 710\r\n\r\n"
+ <- "0223a57aff3e71b22fc526a0b9f69220160811005228628453+00060xml-4.2ABC0030abc123Payment023100AUD2424242424242424209/15123"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "Content-Type: text/xml;charset=ISO-8859-1\r\n"
+ -> "Content-Length: 1148\r\n"
+ -> "Date: Tue, 08 Nov 2016 00:52:30 GMT\r\n"
+ -> "Connection: close\r\n"
+ -> "Server: Apache\r\n"
+ -> "Set-Cookie: ltm_api.securepay.com.au=1073719488.36895.0000; path=/\r\n"
+ -> "\r\n"
+ reading 1148 bytes...
+ -> "0223a57aff3e71b22fc526a0b9f69220160811115230823000+660xml-4.2PaymentABC0030000Normal023100AUD2Yes00Approved100000000Normal20161108123822424242...24209/156Visa"
+ read 1148 bytes
+ Conn close
+ XML
+ end
+
+ def post_scrub
+ <<-XML
+ opening connection to api.securepay.com.au:443...
+ opened
+ starting SSL for api.securepay.com.au:443...
+ SSL established
+ <- "POST /test/payment HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: api.securepay.com.au\r\nContent-Length: 710\r\n\r\n"
+ <- "0223a57aff3e71b22fc526a0b9f69220160811005228628453+00060xml-4.2[FILTERED][FILTERED]Payment023100AUD2[FILTERED]09/15[FILTERED]"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "Content-Type: text/xml;charset=ISO-8859-1\r\n"
+ -> "Content-Length: 1148\r\n"
+ -> "Date: Tue, 08 Nov 2016 00:52:30 GMT\r\n"
+ -> "Connection: close\r\n"
+ -> "Server: Apache\r\n"
+ -> "Set-Cookie: ltm_api.securepay.com.au=1073719488.36895.0000; path=/\r\n"
+ -> "\r\n"
+ reading 1148 bytes...
+ -> "0223a57aff3e71b22fc526a0b9f69220160811115230823000+660xml-4.2Payment[FILTERED]000Normal023100AUD2Yes00Approved100000000Normal20161108123822424242...24209/156Visa"
+ read 1148 bytes
+ Conn close
+ XML
+ end
end
diff --git a/test/unit/gateways/skip_jack_test.rb b/test/unit/gateways/skip_jack_test.rb
index 273508220ee..a79cf6eda5d 100644
--- a/test/unit/gateways/skip_jack_test.rb
+++ b/test/unit/gateways/skip_jack_test.rb
@@ -195,7 +195,9 @@ def test_paymentech_authorization_failure
def test_serial_number_is_added_before_developer_serial_number_for_authorization
- @gateway.expects(:ssl_post).with('https://developer.skipjackic.com/scripts/evolvcc.dll?AuthorizeAPI', "Year=#{Time.now.year + 1}&TransactionAmount=1.00&ShipToPhone=&SerialNumber=X&SJName=Longbob+Longsen&OrderString=1%7ENone%7E0.00%7E0%7EN%7E%7C%7C&OrderNumber=1&OrderDescription=&Month=9&InvoiceNumber=&Email=cody%40example.com&DeveloperSerialNumber=Y&CustomerCode=&CVV2=123&AccountNumber=4242424242424242").returns(successful_authorization_response)
+ expected ="Year=#{Time.now.year + 1}&TransactionAmount=1.00&ShipToPhone=&SerialNumber=X&SJName=Longbob+Longsen&OrderString=1~None~0.00~0~N~%7C%7C&OrderNumber=1&OrderDescription=&Month=9&InvoiceNumber=&Email=cody%40example.com&DeveloperSerialNumber=Y&CustomerCode=&CVV2=123&AccountNumber=4242424242424242"
+ expected = expected.gsub('~', '%7E') if RUBY_VERSION < '2.5.0'
+ @gateway.expects(:ssl_post).with('https://developer.skipjackic.com/scripts/evolvcc.dll?AuthorizeAPI', expected).returns(successful_authorization_response)
@gateway.authorize(@amount, @credit_card, @options)
end
diff --git a/test/unit/gateways/so_easy_pay_test.rb b/test/unit/gateways/so_easy_pay_test.rb
deleted file mode 100644
index daa8a9f9d35..00000000000
--- a/test/unit/gateways/so_easy_pay_test.rb
+++ /dev/null
@@ -1,225 +0,0 @@
-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 0ae7a5725d8..251b1c4a119 100644
--- a/test/unit/gateways/spreedly_core_test.rb
+++ b/test/unit/gateways/spreedly_core_test.rb
@@ -8,6 +8,8 @@ def setup
@credit_card = credit_card
@amount = 103
+ @existing_transaction = 'LKA3RchoqYO0njAfhHVw60ohjrC'
+ @not_found_transaction = 'AdyQXaG0SVpSoMPdmFlvd3aA3uz'
end
def test_successful_purchase_with_payment_method_token
@@ -214,6 +216,24 @@ def test_failed_void
assert_equal '10600', response.params['response_error_code']
end
+ def test_successful_verify
+ @gateway.expects(:raw_ssl_request).returns(successful_verify_response)
+ response = @gateway.verify(@payment_method_token)
+ assert_success response
+
+ assert_equal "Succeeded!", response.message
+ assert_equal "Verification", response.params["transaction_type"]
+ end
+
+ def test_failed_verify
+ @gateway.expects(:raw_ssl_request).returns(failed_verify_response)
+ response = @gateway.verify(@payment_method_token)
+ assert_failure response
+
+ assert_equal "Unable to process the verify transaction.", response.message
+ assert_empty response.params['response_error_code']
+ end
+
def test_successful_store
@gateway.expects(:raw_ssl_request).returns(successful_store_response)
response = @gateway.store(@credit_card)
@@ -241,8 +261,31 @@ def test_successful_unstore
assert_equal "Succeeded!", response.message
end
+ def test_successful_find
+ @gateway.expects(:raw_ssl_request).returns(successful_find_response)
+ response = @gateway.find(@existing_transaction)
+ assert_success response
+
+ assert_equal "Succeeded!", response.message
+ assert_equal "LKA3RchoqYO0njAfhHVw60ohjrC", response.authorization
+ end
+
+ def test_failed_find
+ @gateway.expects(:raw_ssl_request).returns(failed_find_response)
+ response = @gateway.find(@not_found_transaction)
+ assert_failure response
+
+ assert_match %r(Unable to find the transaction), response.message
+ assert_match %r(#{@not_found_transaction}), response.message
+ end
+
+ def test_scrubbing
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
private
+
def successful_purchase_response
MockResponse.succeeded <<-XML
@@ -799,4 +842,319 @@ def successful_unstore_response
XML
end
+ def pre_scrubbed
+ <<-EOS
+ opening connection to core.spreedly.com:443...
+ opened
+ starting SSL for core.spreedly.com:443...
+ SSL established
+ <- "POST /v1/payment_methods.xml HTTP/1.1\r\nContent-Type: text/xml\r\nAuthorization: Basic NFk5YlZrT0NwWWVzUFFPZkRpN1RYUXlVdzUwOlkyaTdBamdVMDNTVWp3WTR4bk9QcXpkc3Y0ZE1iUERDUXpvckFrOEJjb3kwVThFSVZFNGlubkdqdW9NUXY3TU4=\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: core.spreedly.com\r\nContent-Length: 404\r\n\r\n"
+ <- "\n\n \n 5555555555554444\n 123\n Longbob\n Longsen\n 9\n 2019\n \n \n \n \n \n \n \n \n \n\n"
+ -> "HTTP/1.1 201 Created\r\n"
+ -> "Date: Sat, 10 Mar 2018 22:04:06 GMT\r\n"
+ -> "Content-Type: application/xml; charset=utf-8\r\n"
+ -> "Content-Length: 1875\r\n"
+ -> "Connection: close\r\n"
+ -> "X-Frame-Options: SAMEORIGIN\r\n"
+ -> "X-XSS-Protection: 1; mode=block\r\n"
+ -> "X-Content-Type-Options: nosniff\r\n"
+ -> "ETag: W/\"c4ef6dfc389a5514d6b6ffd8bac8786c\"\r\n"
+ -> "Cache-Control: max-age=0, private, must-revalidate\r\n"
+ -> "X-Request-Id: b227ok4du2hrj7mrtt10.core_dcaa82760687b3ef\r\n"
+ -> "Server: nginx\r\n"
+ -> "Strict-Transport-Security: max-age=31536000; includeSubdomains;\r\n"
+ -> "\r\n"
+ reading 1875 bytes...
+ -> "\n NRBpydUCWn658GHV8h2kVlUzB0i\n 2018-03-10T22:04:06Z\n 2018-03-10T22:04:06Z\n true\n AddPaymentMethod\n false\n succeeded\n Succeeded!\n \n Wd25UIrH1uopTkZZ4UDdb5XmSDd\n 2018-03-10T22:04:06Z\n 2018-03-10T22:04:06Z\n \n \n cached\n true\n 4444\n 555555\n master\n Longbob\n Longsen\n 9\n 2019\n \n \n \n \n \n \n \n \n Longbob Longsen\n true\n \n \n \n \n \n \n \n credit_card\n \n \n XXX\n XXXX-XXXX-XXXX-4444\n 125370bb396dff6fed4f581f85a91a9e5317\n \n\n"
+ read 1875 bytes
+ Conn close
+ EOS
+ end
+
+ def post_scrubbed
+ <<-EOS
+ opening connection to core.spreedly.com:443...
+ opened
+ starting SSL for core.spreedly.com:443...
+ SSL established
+ <- "POST /v1/payment_methods.xml HTTP/1.1\r\nContent-Type: text/xml\r\nAuthorization: Basic [FILTERED]=\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: core.spreedly.com\r\nContent-Length: 404\r\n\r\n"
+ <- "\n\n \n [FILTERED]\n [FILTERED]\n Longbob\n Longsen\n 9\n 2019\n \n \n \n \n \n \n \n \n \n\n"
+ -> "HTTP/1.1 201 Created\r\n"
+ -> "Date: Sat, 10 Mar 2018 22:04:06 GMT\r\n"
+ -> "Content-Type: application/xml; charset=utf-8\r\n"
+ -> "Content-Length: 1875\r\n"
+ -> "Connection: close\r\n"
+ -> "X-Frame-Options: SAMEORIGIN\r\n"
+ -> "X-XSS-Protection: 1; mode=block\r\n"
+ -> "X-Content-Type-Options: nosniff\r\n"
+ -> "ETag: W/\"c4ef6dfc389a5514d6b6ffd8bac8786c\"\r\n"
+ -> "Cache-Control: max-age=0, private, must-revalidate\r\n"
+ -> "X-Request-Id: b227ok4du2hrj7mrtt10.core_dcaa82760687b3ef\r\n"
+ -> "Server: nginx\r\n"
+ -> "Strict-Transport-Security: max-age=31536000; includeSubdomains;\r\n"
+ -> "\r\n"
+ reading 1875 bytes...
+ -> "\n NRBpydUCWn658GHV8h2kVlUzB0i\n 2018-03-10T22:04:06Z\n 2018-03-10T22:04:06Z\n true\n AddPaymentMethod\n false\n succeeded\n Succeeded!\n \n Wd25UIrH1uopTkZZ4UDdb5XmSDd\n 2018-03-10T22:04:06Z\n 2018-03-10T22:04:06Z\n \n \n cached\n true\n 4444\n 555555\n master\n Longbob\n Longsen\n 9\n 2019\n \n \n \n \n \n \n \n \n Longbob Longsen\n true\n \n \n \n \n \n \n \n credit_card\n \n \n [FILTERED]\n [FILTERED]\n 125370bb396dff6fed4f581f85a91a9e5317\n \n\n"
+ read 1875 bytes
+ Conn close
+ EOS
+ end
+
+ def successful_verify_response
+ MockResponse.succeeded <<-XML
+
+ true
+ 2018-02-24T00:47:56Z
+ 2018-02-24T00:47:56Z
+ true
+ succeeded
+ 891hWyHKmfCggQQ7Q35sGVcEC01
+ Verification
+
+
+
+
+
+
+
+
+
+ 67
+ 27
+ USD
+ false
+ false
+ Succeeded!
+ 3gLeg4726V5P0HK7cq7QzHsL0a6
+ test
+
+ Jim TesterDude
+
+
+
+
+
+
+
+
+
+ true
+ Successful verify
+
+
+
+
+ false
+ false
+
+
+ false
+
+ 2018-02-24T00:47:56Z
+ 2018-02-24T00:47:56Z
+
+
+ 9AjLflWs7SOKuqJLveOZya9bixa
+ 2012-12-07T19:08:15Z
+ 2018-02-24T00:35:45Z
+
+
+ 2
+
+ retained
+ true
+ 4444
+ 555555
+ master
+ Jim
+ TesterDude
+ 9
+ 2022
+
+
+
+
+
+
+
+
+ Jim TesterDude
+
+
+
+
+
+
+
+
+ credit_card
+
+
+
+ XXXX-XXXX-XXXX-4444
+ 125370bb396dff6fed4f581f85a91a9e5317
+
+
+ XML
+ end
+
+ def failed_verify_response
+ MockResponse.failed <<-XML
+
+ true
+ 2018-02-24T00:53:58Z
+ 2018-02-24T00:53:58Z
+ false
+ gateway_processing_failed
+ RwmpyTCRmCpji1YtSD5f5fQDpkS
+ Verification
+
+
+
+
+
+
+
+
+
+
+ 24
+ USD
+ false
+ false
+ Unable to process the verify transaction.
+ 3gLeg4726V5P0HK7cq7QzHsL0a6
+ test
+
+ Longbob Longsen
+
+
+
+
+
+
+
+
+
+ false
+ Unable to process the verify transaction.
+
+
+
+
+ false
+ false
+
+
+ false
+
+ 2018-02-24T00:53:58Z
+ 2018-02-24T00:53:58Z
+
+
+ UzUKWHwI7GtZe3gz1UU5FiZ6DxH
+ 2018-02-24T00:53:56Z
+ 2018-02-24T00:53:56Z
+
+
+ cached
+ true
+ 1881
+ 401288
+ visa
+ Longbob
+ Longsen
+ 9
+ 2019
+
+
+
+
+
+
+
+
+ Longbob Longsen
+
+
+
+
+
+
+
+
+ credit_card
+
+
+ XXX
+ XXXX-XXXX-XXXX-1881
+ db33a42fcf2908a3795bd4ea881de2e0f015
+
+
+ XML
+ end
+
+ def successful_find_response
+ MockResponse.succeeded <<-XML
+
+ LKA3RchoqYO0njAfhHVw60ohjrC
+ 2012-12-07T19:03:50Z
+ 2012-12-07T19:03:50Z
+ true
+ AddPaymentMethod
+ false
+ succeeded
+ Succeeded!
+
+ 67KlSyyvBAt9VUMJg3lUeWbBaWX
+ 2012-12-07T19:03:50Z
+ 2017-07-29T23:25:21Z
+
+
+ 2
+
+ redacted
+ false
+ 4444
+
+ master
+ Jim
+ TesterDude
+ 9
+ 2022
+
+
+
+
+
+
+
+
+ Jim TesterDude
+ true
+
+
+
+
+
+
+
+ credit_card
+
+
+
+
+
+
+
+ XML
+ end
+
+ def failed_find_response
+ MockResponse.failed <<-XML
+
+ Unable to find the transaction AdyQXaG0SVpSoMPdmFlvd3aA3uz.
+
+ XML
+ end
end
diff --git a/test/unit/gateways/stripe_payment_intents_test.rb b/test/unit/gateways/stripe_payment_intents_test.rb
new file mode 100644
index 00000000000..6245e7b494a
--- /dev/null
+++ b/test/unit/gateways/stripe_payment_intents_test.rb
@@ -0,0 +1,266 @@
+require 'test_helper'
+
+class StripePaymentIntentsTest < Test::Unit::TestCase
+ include CommStub
+
+ def setup
+ @gateway = StripePaymentIntentsGateway.new(:login => 'login')
+
+ @credit_card = credit_card()
+ @threeds_2_card = credit_card('4000000000003220')
+ @visa_token = 'pm_card_visa'
+ @amount = 2020
+ @update_amount = 2050
+
+ @options = {
+ currency: 'GBP',
+ confirmation_method: 'manual',
+ }
+ end
+
+ def test_successful_create_and_confirm_intent
+ @gateway.expects(:ssl_request).times(3).returns(successful_create_3ds2_payment_method, successful_create_3ds2_intent_response, successful_confirm_3ds2_intent_response)
+
+ assert create = @gateway.create_intent(@amount, @threeds_2_card, @options.merge(return_url: 'https://www.example.com', capture_method: 'manual'))
+ assert_instance_of Response, create
+ assert_success create
+
+ assert_equal 'pi_1F1wpFAWOtgoysog8nTulYGk', create.authorization
+ assert_equal 'requires_confirmation', create.params['status']
+ assert create.test?
+
+ assert confirm = @gateway.confirm_intent(create.params['id'], nil, return_url: 'https://example.com/return-to-me')
+ assert_equal 'redirect_to_url', confirm.params.dig('next_action', 'type')
+ end
+
+ def test_successful_create_and_capture_intent
+ options = @options.merge(capture_method: 'manual', confirm: true)
+ @gateway.expects(:ssl_request).twice.returns(successful_create_intent_response, successful_capture_response)
+ assert create = @gateway.create_intent(@amount, @visa_token, options)
+ assert_success create
+ assert_equal 'requires_capture', create.params['status']
+
+ assert capture = @gateway.capture(@amount, create.params['id'], options)
+ assert_success capture
+ assert_equal 'succeeded', capture.params['status']
+ assert_equal 'Payment complete.', capture.params.dig('charges', 'data')[0].dig('outcome', 'seller_message')
+ end
+
+ def test_successful_create_and_update_intent
+ @gateway.expects(:ssl_request).twice.returns(successful_create_intent_response, successful_update_intent_response)
+ assert create = @gateway.create_intent(@amount, @visa_token, @options.merge(capture_method: 'manual'))
+
+ assert update = @gateway.update_intent(@update_amount, create.params['id'], nil, @options.merge(capture_method: 'manual'))
+ assert_equal @update_amount, update.params['amount']
+ assert_equal 'requires_confirmation', update.params['status']
+ end
+
+ def test_successful_create_and_void_intent
+ @gateway.expects(:ssl_request).twice.returns(successful_create_intent_response, successful_void_response)
+ assert create = @gateway.create_intent(@amount, @visa_token, @options.merge(capture_method: 'manual', confirm: true))
+
+ assert cancel = @gateway.void(create.params['id'])
+ assert_equal @amount, cancel.params.dig('charges', 'data')[0].dig('amount_refunded')
+ assert_equal 'canceled', cancel.params['status']
+ end
+
+ def test_failed_capture_after_creation
+ @gateway.expects(:ssl_request).returns(failed_capture_response)
+
+ assert create = @gateway.create_intent(@amount, 'pm_card_chargeDeclined', @options.merge(confirm: true))
+ assert_equal 'requires_payment_method', create.params.dig('error', 'payment_intent', 'status')
+ assert_equal false, create.params.dig('error', 'payment_intent', 'charges', 'data')[0].dig('captured')
+ end
+
+ def test_failed_void_after_capture
+ @gateway.expects(:ssl_request).twice.returns(successful_capture_response, failed_cancel_response)
+ assert create = @gateway.create_intent(@amount, @visa_token, @options.merge(confirm: true))
+ assert_equal 'succeeded', create.params['status']
+ intent_id = create.params['id']
+
+ assert cancel = @gateway.void(intent_id, cancellation_reason: 'requested_by_customer')
+ assert_equal 'You cannot cancel this PaymentIntent because ' \
+ 'it has a status of succeeded. Only a PaymentIntent with ' \
+ 'one of the following statuses may be canceled: ' \
+ 'requires_payment_method, requires_capture, requires_confirmation, requires_action.', cancel.message
+ end
+
+ private
+
+ def successful_create_intent_response
+ <<-RESPONSE
+ {"id":"pi_1F1xauAWOtgoysogIfHO8jGi","object":"payment_intent","amount":2020,"amount_capturable":2020,"amount_received":0,"application":null,"application_fee_amount":null,"canceled_at":null,"cancellation_reason":null,"capture_method":"manual","charges":{"object":"list","data":[{"id":"ch_1F1xavAWOtgoysogxrtSiCu4","object":"charge","amount":2020,"amount_refunded":0,"application":null,"application_fee":null,"application_fee_amount":null,"balance_transaction":null,"billing_details":{"address":{"city":null,"country":null,"line1":null,"line2":null,"postal_code":null,"state":null},"email":null,"name":null,"phone":null},"captured":false,"created":1564501833,"currency":"gbp","customer":"cus_7s22nNueP2Hjj6","description":null,"destination":null,"dispute":null,"failure_code":null,"failure_message":null,"fraud_details":{},"invoice":null,"livemode":false,"metadata":{},"on_behalf_of":null,"order":null,"outcome":{"network_status":"approved_by_network","reason":null,"risk_level":"normal","risk_score":58,"seller_message":"Payment complete.","type":"authorized"},"paid":true,"payment_intent":"pi_1F1xauAWOtgoysogIfHO8jGi","payment_method":"pm_1F1xauAWOtgoysog00COoKIU","payment_method_details":{"card":{"brand":"visa","checks":{"address_line1_check":null,"address_postal_code_check":null,"cvc_check":null},"country":"US","exp_month":7,"exp_year":2020,"fingerprint":"hfaVNMiXc0dYSiC5","funding":"credit","last4":"4242","three_d_secure":null,"wallet":null},"type":"card"},"receipt_email":null,"receipt_number":null,"receipt_url":"https://pay.stripe.com/receipts/acct_160DX6AWOtgoysog/ch_1F1xavAWOtgoysogxrtSiCu4/rcpt_FX1eGdFRi8ssOY8Fqk4X6nEjNeGV5PG","refunded":false,"refunds":{"object":"list","data":[],"has_more":false,"total_count":0,"url":"/v1/charges/ch_1F1xavAWOtgoysogxrtSiCu4/refunds"},"review":null,"shipping":null,"source":null,"source_transfer":null,"statement_descriptor":null,"status":"succeeded","transfer_data":null,"transfer_group":null}],"has_more":false,"total_count":1,"url":"/v1/charges?payment_intent=pi_1F1xauAWOtgoysogIfHO8jGi"},"client_secret":"pi_1F1xauAWOtgoysogIfHO8jGi_secret_ZrXvfydFv0BelaMQJgHxjts5b","confirmation_method":"manual","created":1564501832,"currency":"gbp","customer":"cus_7s22nNueP2Hjj6","description":null,"invoice":null,"last_payment_error":null,"livemode":false,"metadata":{},"next_action":null,"on_behalf_of":null,"payment_method":"pm_1F1xauAWOtgoysog00COoKIU","payment_method_options":{"card":{"request_three_d_secure":"automatic"}},"payment_method_types":["card"],"receipt_email":null,"review":null,"setup_future_usage":null,"shipping":null,"source":null,"statement_descriptor":null,"status":"requires_capture","transfer_data":null,"transfer_group":null}
+ RESPONSE
+ end
+
+ def successful_capture_response
+ <<-RESPONSE
+ {"id":"pi_1F1xauAWOtgoysogIfHO8jGi","object":"payment_intent","amount":2020,"amount_capturable":0,"amount_received":2020,"application":null,"application_fee_amount":null,"canceled_at":null,"cancellation_reason":null,"capture_method":"manual","charges":{"object":"list","data":[{"id":"ch_1F1xavAWOtgoysogxrtSiCu4","object":"charge","amount":2020,"amount_refunded":0,"application":null,"application_fee":null,"application_fee_amount":null,"balance_transaction":"txn_1F1xawAWOtgoysog27xGBjM6","billing_details":{"address":{"city":null,"country":null,"line1":null,"line2":null,"postal_code":null,"state":null},"email":null,"name":null,"phone":null},"captured":true,"created":1564501833,"currency":"gbp","customer":"cus_7s22nNueP2Hjj6","description":null,"destination":null,"dispute":null,"failure_code":null,"failure_message":null,"fraud_details":{},"invoice":null,"livemode":false,"metadata":{},"on_behalf_of":null,"order":null,"outcome":{"network_status":"approved_by_network","reason":null,"risk_level":"normal","risk_score":58,"seller_message":"Payment complete.","type":"authorized"},"paid":true,"payment_intent":"pi_1F1xauAWOtgoysogIfHO8jGi","payment_method":"pm_1F1xauAWOtgoysog00COoKIU","payment_method_details":{"card":{"brand":"visa","checks":{"address_line1_check":null,"address_postal_code_check":null,"cvc_check":null},"country":"US","exp_month":7,"exp_year":2020,"fingerprint":"hfaVNMiXc0dYSiC5","funding":"credit","last4":"4242","three_d_secure":null,"wallet":null},"type":"card"},"receipt_email":null,"receipt_number":null,"receipt_url":"https://pay.stripe.com/receipts/acct_160DX6AWOtgoysog/ch_1F1xavAWOtgoysogxrtSiCu4/rcpt_FX1eGdFRi8ssOY8Fqk4X6nEjNeGV5PG","refunded":false,"refunds":{"object":"list","data":[],"has_more":false,"total_count":0,"url":"/v1/charges/ch_1F1xavAWOtgoysogxrtSiCu4/refunds"},"review":null,"shipping":null,"source":null,"source_transfer":null,"statement_descriptor":null,"status":"succeeded","transfer_data":null,"transfer_group":null}],"has_more":false,"total_count":1,"url":"/v1/charges?payment_intent=pi_1F1xauAWOtgoysogIfHO8jGi"},"client_secret":"pi_1F1xauAWOtgoysogIfHO8jGi_secret_ZrXvfydFv0BelaMQJgHxjts5b","confirmation_method":"manual","created":1564501832,"currency":"gbp","customer":"cus_7s22nNueP2Hjj6","description":null,"invoice":null,"last_payment_error":null,"livemode":false,"metadata":{},"next_action":null,"on_behalf_of":null,"payment_method":"pm_1F1xauAWOtgoysog00COoKIU","payment_method_options":{"card":{"request_three_d_secure":"automatic"}},"payment_method_types":["card"],"receipt_email":null,"review":null,"setup_future_usage":null,"shipping":null,"source":null,"statement_descriptor":null,"status":"succeeded","transfer_data":null,"transfer_group":null}
+ RESPONSE
+ end
+
+ def successful_void_response
+ <<-RESPONSE
+ {"id":"pi_1F1yBVAWOtgoysogearamRvl","object":"payment_intent","amount":2020,"amount_capturable":0,"amount_received":0,"application":null,"application_fee_amount":null,"canceled_at":1564504103,"cancellation_reason":"requested_by_customer","capture_method":"manual","charges":{"object":"list","data":[{"id":"ch_1F1yBWAWOtgoysog1MQfDpJH","object":"charge","amount":2020,"amount_refunded":2020,"application":null,"application_fee":null,"application_fee_amount":null,"balance_transaction":null,"billing_details":{"address":{"city":null,"country":null,"line1":null,"line2":null,"postal_code":null,"state":null},"email":null,"name":null,"phone":null},"captured":false,"created":1564504102,"currency":"gbp","customer":"cus_7s22nNueP2Hjj6","description":null,"destination":null,"dispute":null,"failure_code":null,"failure_message":null,"fraud_details":{},"invoice":null,"livemode":false,"metadata":{},"on_behalf_of":null,"order":null,"outcome":{"network_status":"approved_by_network","reason":null,"risk_level":"normal","risk_score":46,"seller_message":"Payment complete.","type":"authorized"},"paid":true,"payment_intent":"pi_1F1yBVAWOtgoysogearamRvl","payment_method":"pm_1F1yBVAWOtgoysogddy4E3hL","payment_method_details":{"card":{"brand":"visa","checks":{"address_line1_check":null,"address_postal_code_check":null,"cvc_check":null},"country":"US","exp_month":7,"exp_year":2020,"fingerprint":"hfaVNMiXc0dYSiC5","funding":"credit","last4":"4242","three_d_secure":null,"wallet":null},"type":"card"},"receipt_email":null,"receipt_number":null,"receipt_url":"https://pay.stripe.com/receipts/acct_160DX6AWOtgoysog/ch_1F1yBWAWOtgoysog1MQfDpJH/rcpt_FX2Go3YHBqAYQPJuKGMeab3nyCU0Kks","refunded":true,"refunds":{"object":"list","data":[{"id":"re_1F1yBXAWOtgoysog0PU371Yz","object":"refund","amount":2020,"balance_transaction":null,"charge":"ch_1F1yBWAWOtgoysog1MQfDpJH","created":1564504103,"currency":"gbp","metadata":{},"reason":"requested_by_customer","receipt_number":null,"source_transfer_reversal":null,"status":"succeeded","transfer_reversal":null}],"has_more":false,"total_count":1,"url":"/v1/charges/ch_1F1yBWAWOtgoysog1MQfDpJH/refunds"},"review":null,"shipping":null,"source":null,"source_transfer":null,"statement_descriptor":null,"status":"succeeded","transfer_data":null,"transfer_group":null}],"has_more":false,"total_count":1,"url":"/v1/charges?payment_intent=pi_1F1yBVAWOtgoysogearamRvl"},"client_secret":"pi_1F1yBVAWOtgoysogearamRvl_secret_oCnlR2t0GPclqACgHt2rst4gM","confirmation_method":"manual","created":1564504101,"currency":"gbp","customer":"cus_7s22nNueP2Hjj6","description":null,"invoice":null,"last_payment_error":null,"livemode":false,"metadata":{},"next_action":null,"on_behalf_of":null,"payment_method":"pm_1F1yBVAWOtgoysogddy4E3hL","payment_method_options":{"card":{"request_three_d_secure":"automatic"}},"payment_method_types":["card"],"receipt_email":null,"review":null,"setup_future_usage":null,"shipping":null,"source":null,"statement_descriptor":null,"status":"canceled","transfer_data":null,"transfer_group":null}
+ RESPONSE
+ end
+
+ def successful_update_intent_response
+ <<-RESPONSE
+ {"id":"pi_1F1yBbAWOtgoysog52J88BuO","object":"payment_intent","amount":2050,"amount_capturable":0,"amount_received":0,"application":null,"application_fee_amount":null,"canceled_at":null,"cancellation_reason":null,"capture_method":"manual","charges":{"object":"list","data":[],"has_more":false,"total_count":0,"url":"/v1/charges?payment_intent=pi_1F1yBbAWOtgoysog52J88BuO"},"client_secret":"pi_1F1yBbAWOtgoysog52J88BuO_secret_olw5rmbtm7cd72S9JfbKjTJJv","confirmation_method":"manual","created":1564504107,"currency":"gbp","customer":"cus_7s22nNueP2Hjj6","description":null,"invoice":null,"last_payment_error":null,"livemode":false,"metadata":{},"next_action":null,"on_behalf_of":null,"payment_method":"pm_1F1yBbAWOtgoysoguJQsDdYj","payment_method_options":{"card":{"request_three_d_secure":"automatic"}},"payment_method_types":["card"],"receipt_email":null,"review":null,"setup_future_usage":null,"shipping":null,"source":null,"statement_descriptor":null,"status":"requires_confirmation","transfer_data":null,"transfer_group":null}
+ RESPONSE
+ end
+
+ def successful_create_3ds2_payment_method
+ <<-RESPONSE
+ {
+ "id": "pm_1F1xK0AWOtgoysogfPuRKN1d",
+ "object": "payment_method",
+ "billing_details": {
+ "address": {"city": null,
+ "country": null,
+ "line1": null,
+ "line2": null,
+ "postal_code": null,
+ "state": null},
+ "email": null,
+ "name": null,
+ "phone": null},
+ "card": {
+ "brand": "visa",
+ "checks": {"address_line1_check": null,
+ "address_postal_code_check": null,
+ "cvc_check": "unchecked"},
+ "country": null,
+ "exp_month": 10,
+ "exp_year": 2020,
+ "fingerprint": "l3J0NJaGgv0jAGLV",
+ "funding": "credit",
+ "generated_from": null,
+ "last4": "3220",
+ "three_d_secure_usage": {"supported": true},
+ "wallet": null},
+ "created": 1564500784,
+ "customer": null,
+ "livemode": false,
+ "metadata": {},
+ "type": "card"
+ }
+ RESPONSE
+ end
+
+ def successful_create_3ds2_intent_response
+ <<-RESPONSE
+ {
+ "id": "pi_1F1wpFAWOtgoysog8nTulYGk",
+ "object": "payment_intent",
+ "amount": 2020,
+ "amount_capturable": 0,
+ "amount_received": 0,
+ "application": null,
+ "application_fee_amount": null,
+ "canceled_at": null,
+ "cancellation_reason": null,
+ "capture_method": "manual",
+ "charges": {
+ "object": "list",
+ "data": [],
+ "has_more": false,
+ "total_count": 0,
+ "url": "/v1/charges?payment_intent=pi_1F1wpFAWOtgoysog8nTulYGk"
+ },
+ "client_secret": "pi_1F1wpFAWOtgoysog8nTulYGk_secret_75qf7rjBDsTTz279LfS1feXUj",
+ "confirmation_method": "manual",
+ "created": 1564498877,
+ "currency": "gbp",
+ "customer": "cus_7s22nNueP2Hjj6",
+ "description": null,
+ "invoice": null,
+ "last_payment_error": null,
+ "livemode": false,
+ "metadata": {},
+ "next_action": null,
+ "on_behalf_of": null,
+ "payment_method": "pm_1F1wpFAWOtgoysogJ8zQ8K07",
+ "payment_method_options": {
+ "card": {"request_three_d_secure": "automatic"}
+ },
+ "payment_method_types": ["card"],
+ "receipt_email": null,
+ "review": null,
+ "setup_future_usage": null,
+ "shipping": null,
+ "source": null,
+ "statement_descriptor": null,
+ "status": "requires_confirmation",
+ "transfer_data": null,
+ "transfer_group": null
+ }
+ RESPONSE
+ end
+
+ def successful_confirm_3ds2_intent_response
+ <<-RESPONSE
+ {
+ "id": "pi_1F1wpFAWOtgoysog8nTulYGk",
+ "object": "payment_intent",
+ "amount": 2020,
+ "amount_capturable": 0,
+ "amount_received": 0,
+ "application": null,
+ "application_fee_amount": null,
+ "canceled_at": null,
+ "cancellation_reason": null,
+ "capture_method": "manual",
+ "charges": {
+ "object": "list",
+ "data": [],
+ "has_more": false,
+ "total_count": 0,
+ "url": "/v1/charges?payment_intent=pi_1F1wpFAWOtgoysog8nTulYGk"},
+ "client_secret": "pi_1F1wpFAWOtgoysog8nTulYGk_secret_75qf7rjBDsTTz279LfS1feXUj",
+ "confirmation_method": "manual",
+ "created": 1564498877,
+ "currency": "gbp",
+ "customer": "cus_7s22nNueP2Hjj6",
+ "description": null,
+ "invoice": null,
+ "last_payment_error": null,
+ "livemode": false,
+ "metadata": {},
+ "next_action": {
+ "redirect_to_url": {
+ "return_url": "https://example.com/return-to-me",
+ "url": "https://hooks.stripe.com/3d_secure_2_eap/begin_test/src_1F1wpGAWOtgoysog4f00umCp/src_client_secret_FX0qk3uQ04woFWgdJbN3pnHD"},
+ "type": "redirect_to_url"},
+ "on_behalf_of": null,
+ "payment_method": "pm_1F1wpFAWOtgoysogJ8zQ8K07",
+ "payment_method_options": {
+ "card": {"request_three_d_secure": "automatic"}
+ },
+ "payment_method_types": ["card"],
+ "receipt_email": null,
+ "review": null,
+ "setup_future_usage": null,
+ "shipping": null,
+ "source": null,
+ "statement_descriptor": null,
+ "status": "requires_action",
+ "transfer_data": null,
+ "transfer_group": null
+ }
+ RESPONSE
+ end
+
+ def failed_capture_response
+ <<-RESPONSE
+ {"error":{"charge":"ch_1F2MB6AWOtgoysogAIvNV32Z","code":"card_declined","decline_code":"generic_decline","doc_url":"https://stripe.com/docs/error-codes/card-declined","message":"Your card was declined.","payment_intent":{"id":"pi_1F2MB5AWOtgoysogCMt8BaxR","object":"payment_intent","amount":2020,"amount_capturable":0,"amount_received":0,"application":null,"application_fee_amount":null,"canceled_at":null,"cancellation_reason":null,"capture_method":"automatic","charges":{"object":"list","data":[{"id":"ch_1F2MB6AWOtgoysogAIvNV32Z","object":"charge","amount":2020,"amount_refunded":0,"application":null,"application_fee":null,"application_fee_amount":null,"balance_transaction":null,"billing_details":{"address":{"city":null,"country":null,"line1":null,"line2":null,"postal_code":null,"state":null},"email":null,"name":null,"phone":null},"captured":false,"created":1564596332,"currency":"gbp","customer":"cus_7s22nNueP2Hjj6","description":null,"destination":null,"dispute":null,"failure_code":"card_declined","failure_message":"Your card was declined.","fraud_details":{},"invoice":null,"livemode":false,"metadata":{},"on_behalf_of":null,"order":null,"outcome":{"network_status":"declined_by_network","reason":"generic_decline","risk_level":"normal","risk_score":41,"seller_message":"The bank did not return any further details with this decline.","type":"issuer_declined"},"paid":false,"payment_intent":"pi_1F2MB5AWOtgoysogCMt8BaxR","payment_method":"pm_1F2MB5AWOtgoysogq3yXZ98h","payment_method_details":{"card":{"brand":"visa","checks":{"address_line1_check":null,"address_postal_code_check":null,"cvc_check":null},"country":"US","exp_month":7,"exp_year":2020,"fingerprint":"1VUoWMvHnqtngyrD","funding":"credit","last4":"0002","three_d_secure":null,"wallet":null},"type":"card"},"receipt_email":null,"receipt_number":null,"receipt_url":"https://pay.stripe.com/receipts/acct_160DX6AWOtgoysog/ch_1F2MB6AWOtgoysogAIvNV32Z/rcpt_FXR3PjBGluHmHsnLmp0S2KQiHl3yg6W","refunded":false,"refunds":{"object":"list","data":[],"has_more":false,"total_count":0,"url":"/v1/charges/ch_1F2MB6AWOtgoysogAIvNV32Z/refunds"},"review":null,"shipping":null,"source":null,"source_transfer":null,"statement_descriptor":null,"status":"failed","transfer_data":null,"transfer_group":null}],"has_more":false,"total_count":1,"url":"/v1/charges?payment_intent=pi_1F2MB5AWOtgoysogCMt8BaxR"},"client_secret":"pi_1F2MB5AWOtgoysogCMt8BaxR_secret_fOHryjtjBE4gACiHTcREraXSQ","confirmation_method":"manual","created":1564596331,"currency":"gbp","customer":"cus_7s22nNueP2Hjj6","description":null,"invoice":null,"last_payment_error":{"charge":"ch_1F2MB6AWOtgoysogAIvNV32Z","code":"card_declined","decline_code":"generic_decline","doc_url":"https://stripe.com/docs/error-codes/card-declined","message":"Your card was declined.","payment_method":{"id":"pm_1F2MB5AWOtgoysogq3yXZ98h","object":"payment_method","billing_details":{"address":{"city":null,"country":null,"line1":null,"line2":null,"postal_code":null,"state":null},"email":null,"name":null,"phone":null},"card":{"brand":"visa","checks":{"address_line1_check":null,"address_postal_code_check":null,"cvc_check":null},"country":"US","exp_month":7,"exp_year":2020,"fingerprint":"1VUoWMvHnqtngyrD","funding":"credit","generated_from":null,"last4":"0002","three_d_secure_usage":{"supported":true},"wallet":null},"created":1564596331,"customer":null,"livemode":false,"metadata":{},"type":"card"},"type":"card_error"},"livemode":false,"metadata":{},"next_action":null,"on_behalf_of":null,"payment_method":null,"payment_method_options":{"card":{"request_three_d_secure":"automatic"}},"payment_method_types":["card"],"receipt_email":null,"review":null,"setup_future_usage":null,"shipping":null,"source":null,"statement_descriptor":null,"status":"requires_payment_method","transfer_data":null,"transfer_group":null},"payment_method":{"id":"pm_1F2MB5AWOtgoysogq3yXZ98h","object":"payment_method","billing_details":{"address":{"city":null,"country":null,"line1":null,"line2":null,"postal_code":null,"state":null},"email":null,"name":null,"phone":null},"card":{"brand":"visa","checks":{"address_line1_check":null,"address_postal_code_check":null,"cvc_check":null},"country":"US","exp_month":7,"exp_year":2020,"fingerprint":"1VUoWMvHnqtngyrD","funding":"credit","generated_from":null,"last4":"0002","three_d_secure_usage":{"supported":true},"wallet":null},"created":1564596331,"customer":null,"livemode":false,"metadata":{},"type":"card"},"type":"card_error"}}
+ RESPONSE
+ end
+
+ def failed_cancel_response
+ <<-RESPONSE
+ {"error":{"code":"payment_intent_unexpected_state","doc_url":"https://stripe.com/docs/error-codes/payment-intent-unexpected-state","message":"You cannot cancel this PaymentIntent because it has a status of succeeded. Only a PaymentIntent with one of the following statuses may be canceled: requires_payment_method, requires_capture, requires_confirmation, requires_action.","payment_intent":{"id":"pi_1F2McmAWOtgoysoglFLDRWab","object":"payment_intent","amount":2020,"amount_capturable":0,"amount_received":2020,"application":null,"application_fee_amount":null,"canceled_at":null,"cancellation_reason":null,"capture_method":"automatic","charges":{"object":"list","data":[{"id":"ch_1F2McmAWOtgoysogQgUS1YtH","object":"charge","amount":2020,"amount_refunded":0,"application":null,"application_fee":null,"application_fee_amount":null,"balance_transaction":"txn_1F2McmAWOtgoysog8uxBEJ30","billing_details":{"address":{"city":null,"country":null,"line1":null,"line2":null,"postal_code":null,"state":null},"email":null,"name":null,"phone":null},"captured":true,"created":1564598048,"currency":"gbp","customer":"cus_7s22nNueP2Hjj6","description":null,"destination":null,"dispute":null,"failure_code":null,"failure_message":null,"fraud_details":{},"invoice":null,"livemode":false,"metadata":{},"on_behalf_of":null,"order":null,"outcome":{"network_status":"approved_by_network","reason":null,"risk_level":"normal","risk_score":53,"seller_message":"Payment complete.","type":"authorized"},"paid":true,"payment_intent":"pi_1F2McmAWOtgoysoglFLDRWab","payment_method":"pm_1F2MclAWOtgoysogq80GBBMO","payment_method_details":{"card":{"brand":"visa","checks":{"address_line1_check":null,"address_postal_code_check":null,"cvc_check":null},"country":"US","exp_month":7,"exp_year":2020,"fingerprint":"hfaVNMiXc0dYSiC5","funding":"credit","last4":"4242","three_d_secure":null,"wallet":null},"type":"card"},"receipt_email":null,"receipt_number":null,"receipt_url":"https://pay.stripe.com/receipts/acct_160DX6AWOtgoysog/ch_1F2McmAWOtgoysogQgUS1YtH/rcpt_FXRVzyFnf7aCS1r13N3uym1u8AaboOJ","refunded":false,"refunds":{"object":"list","data":[],"has_more":false,"total_count":0,"url":"/v1/charges/ch_1F2McmAWOtgoysogQgUS1YtH/refunds"},"review":null,"shipping":null,"source":null,"source_transfer":null,"statement_descriptor":null,"status":"succeeded","transfer_data":null,"transfer_group":null}],"has_more":false,"total_count":1,"url":"/v1/charges?payment_intent=pi_1F2McmAWOtgoysoglFLDRWab"},"client_secret":"pi_1F2McmAWOtgoysoglFLDRWab_secret_z4faDF0Cv0JZJ6pxK3bdIodkD","confirmation_method":"manual","created":1564598048,"currency":"gbp","customer":"cus_7s22nNueP2Hjj6","description":null,"invoice":null,"last_payment_error":null,"livemode":false,"metadata":{},"next_action":null,"on_behalf_of":null,"payment_method":"pm_1F2MclAWOtgoysogq80GBBMO","payment_method_options":{"card":{"request_three_d_secure":"automatic"}},"payment_method_types":["card"],"receipt_email":null,"review":null,"setup_future_usage":null,"shipping":null,"source":null,"statement_descriptor":null,"status":"succeeded","transfer_data":null,"transfer_group":null},"type":"invalid_request_error"}}
+ RESPONSE
+ end
+end
diff --git a/test/unit/gateways/stripe_test.rb b/test/unit/gateways/stripe_test.rb
index 9d8373bdaec..6a8f4f62f87 100644
--- a/test/unit/gateways/stripe_test.rb
+++ b/test/unit/gateways/stripe_test.rb
@@ -12,6 +12,7 @@ def setup
@options = {
:billing_address => address(),
+ :statement_address => statement_address(),
:description => 'Test Purchase'
}
@@ -439,6 +440,25 @@ def test_amount_localization
@gateway.purchase(@amount, @credit_card, @options)
end
+ def test_adds_application_to_x_stripe_client_user_agent_header
+ application = {
+ name: "app",
+ version: "1.0",
+ url: "https://example.com"
+ }
+
+ response = stub_comms(@gateway, :ssl_request) do
+ @gateway.purchase(@amount, "cus_xxx|card_xxx", @options.merge({application: application}))
+ end.check_request do |method, endpoint, data, headers|
+ assert_match(/\"application\"/, headers["X-Stripe-Client-User-Agent"])
+ assert_match(/\"name\":\"app\"/, headers["X-Stripe-Client-User-Agent"])
+ assert_match(/\"version\":\"1.0\"/, headers["X-Stripe-Client-User-Agent"])
+ assert_match(/\"url\":\"https:\/\/example.com\"/, headers["X-Stripe-Client-User-Agent"])
+ end.respond_with(successful_purchase_response)
+
+ assert_success response
+ end
+
def test_successful_purchase_with_token_including_customer
response = stub_comms(@gateway, :ssl_request) do
@gateway.purchase(@amount, "cus_xxx|card_xxx")
@@ -591,18 +611,19 @@ def test_successful_refund_with_reverse_transfer
def test_successful_refund_with_refund_fee_amount
s = sequence("request")
@gateway.expects(:ssl_request).returns(successful_partially_refunded_response).in_sequence(s)
- @gateway.expects(:ssl_request).returns(successful_application_fee_list_response).in_sequence(s)
- @gateway.expects(:ssl_request).returns(successful_refunded_application_fee_response).in_sequence(s)
+ @gateway.expects(:ssl_request).returns(successful_fetch_application_fee_response).in_sequence(s)
+ @gateway.expects(:ssl_request).returns(successful_partially_refunded_application_fee_response).in_sequence(s)
assert response = @gateway.refund(@refund_amount, 'ch_test_charge', :refund_fee_amount => 100)
assert_success response
end
+ # What is the significance of this??? it's to test that the first response is used as primary. so identical to above with an extra assertion
def test_refund_with_fee_response_gives_a_charge_authorization
s = sequence("request")
@gateway.expects(:ssl_request).returns(successful_partially_refunded_response).in_sequence(s)
- @gateway.expects(:ssl_request).returns(successful_application_fee_list_response).in_sequence(s)
- @gateway.expects(:ssl_request).returns(successful_refunded_application_fee_response).in_sequence(s)
+ @gateway.expects(:ssl_request).returns(successful_fetch_application_fee_response).in_sequence(s)
+ @gateway.expects(:ssl_request).returns(successful_partially_refunded_application_fee_response).in_sequence(s)
assert response = @gateway.refund(@refund_amount, 'ch_test_charge', :refund_fee_amount => 100)
assert_success response
@@ -612,17 +633,17 @@ def test_refund_with_fee_response_gives_a_charge_authorization
def test_unsuccessful_refund_with_refund_fee_amount_when_application_fee_id_not_found
s = sequence("request")
@gateway.expects(:ssl_request).returns(successful_partially_refunded_response).in_sequence(s)
- @gateway.expects(:ssl_request).returns(unsuccessful_application_fee_list_response).in_sequence(s)
+ @gateway.expects(:ssl_request).returns(unsuccessful_fetch_application_fee_response).in_sequence(s)
assert response = @gateway.refund(@refund_amount, 'ch_test_charge', :refund_fee_amount => 100)
assert_failure response
- assert_match(/^Application fee id could not be found/, response.message)
+ assert_match(/^Application fee id could not be retrieved/, response.message)
end
def test_unsuccessful_refund_with_refund_fee_amount_when_refunding_application_fee
s = sequence("request")
@gateway.expects(:ssl_request).returns(successful_partially_refunded_response).in_sequence(s)
- @gateway.expects(:ssl_request).returns(successful_application_fee_list_response).in_sequence(s)
+ @gateway.expects(:ssl_request).returns(successful_fetch_application_fee_response).in_sequence(s)
@gateway.expects(:ssl_request).returns(generic_error_response).in_sequence(s)
assert response = @gateway.refund(@refund_amount, 'ch_test_charge', :refund_fee_amount => 100)
@@ -692,6 +713,17 @@ def test_declined_request_advanced_decline_codes
assert_equal 'ch_test_charge', response.authorization
end
+ def test_declined_request_advanced_pickup_card_code
+ @gateway.expects(:ssl_request).returns(declined_pickup_card_purchase_response)
+
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+
+ assert_equal Gateway::STANDARD_ERROR_CODE[:pickup_card], response.error_code
+ refute response.test? # unsuccessful request defaults to live
+ assert_equal 'ch_test_charge', response.authorization
+ end
+
def test_declined_request_advanced_decline_code_not_in_standard_mapping
@gateway.expects(:ssl_request).returns(declined_generic_decline_purchase_response)
@@ -723,9 +755,39 @@ def test_add_creditcard_with_credit_card
def test_add_creditcard_with_track_data
post = {}
- @credit_card.stubs(:track_data).returns("Tracking data")
+ @credit_card.stubs(:track_data).returns("Swipe data")
+ @credit_card.stubs(:read_method).returns('contactless_magstripe')
@gateway.send(:add_creditcard, post, @credit_card, {})
assert_equal @credit_card.track_data, post[:card][:swipe_data]
+ assert_equal "contactless_magstripe_mode", post[:card][:read_method]
+ assert_nil post[:card][:number]
+ assert_nil post[:card][:exp_year]
+ assert_nil post[:card][:exp_month]
+ assert_nil post[:card][:cvc]
+ assert_nil post[:card][:name]
+ end
+
+ def test_add_creditcard_with_fallback_no_chip
+ post = {}
+ @credit_card.stubs(:track_data).returns("Swipe data")
+ @credit_card.stubs(:read_method).returns('fallback_no_chip')
+ @gateway.send(:add_creditcard, post, @credit_card, {})
+ assert_equal @credit_card.track_data, post[:card][:swipe_data]
+ assert_equal "no_chip", post[:card][:fallback_reason]
+ assert_nil post[:card][:number]
+ assert_nil post[:card][:exp_year]
+ assert_nil post[:card][:exp_month]
+ assert_nil post[:card][:cvc]
+ assert_nil post[:card][:name]
+ end
+
+ def test_add_creditcard_with_fallback_chip_error
+ post = {}
+ @credit_card.stubs(:track_data).returns("Swipe data")
+ @credit_card.stubs(:read_method).returns('fallback_chip_error')
+ @gateway.send(:add_creditcard, post, @credit_card, {})
+ assert_equal @credit_card.track_data, post[:card][:swipe_data]
+ assert_equal "chip_error", post[:card][:fallback_reason]
assert_nil post[:card][:number]
assert_nil post[:card][:exp_year]
assert_nil post[:card][:exp_month]
@@ -762,6 +824,19 @@ def test_add_creditcard_with_emv_credit_card
assert_equal @emv_credit_card.icc_data, post[:card][:emv_auth_data]
end
+ def test_add_creditcard_pads_eci_value
+ post = {}
+ credit_card = network_tokenization_credit_card('4242424242424242',
+ payment_cryptogram: "111111111100cryptogram",
+ verification_value: nil,
+ eci: "7"
+ )
+
+ @gateway.send(:add_creditcard, post, credit_card, {})
+
+ assert_equal "07", post[:card][:eci]
+ end
+
def test_application_fee_is_submitted_for_purchase
stub_comms(@gateway, :ssl_request) do
@gateway.purchase(@amount, @credit_card, @options.merge({:application_fee => 144}))
@@ -778,11 +853,35 @@ def test_application_fee_is_submitted_for_capture
end.respond_with(successful_capture_response)
end
+ def test_exchange_rate_is_submitted_for_purchase
+ stub_comms(@gateway, :ssl_request) do
+ @gateway.purchase(@amount, @credit_card, @options.merge({:exchange_rate => 0.96251}))
+ end.check_request do |method, endpoint, data, headers|
+ assert_match(/exchange_rate=0.96251/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
+ def test_exchange_rate_is_submitted_for_capture
+ stub_comms(@gateway, :ssl_request) do
+ @gateway.capture(@amount, "ch_test_charge", @options.merge({:exchange_rate => 0.96251}))
+ end.check_request do |method, endpoint, data, headers|
+ assert_match(/exchange_rate=0.96251/, data)
+ end.respond_with(successful_capture_response)
+ end
+
def test_destination_is_submitted_for_purchase
stub_comms(@gateway, :ssl_request) do
@gateway.purchase(@amount, @credit_card, @options.merge({:destination => 'subaccountid'}))
end.check_request do |method, endpoint, data, headers|
- assert_match(/destination=subaccountid/, data)
+ assert_match(/destination\[account\]=subaccountid/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
+ def test_destination_amount_is_submitted_for_purchase
+ stub_comms(@gateway, :ssl_request) do
+ @gateway.purchase(@amount, @credit_card, @options.merge({:destination => 'subaccountid', :destination_amount => @amount - 20}))
+ end.check_request do |method, endpoint, data, headers|
+ assert_match(/destination\[amount\]=#{@amount - 20}/, data)
end.respond_with(successful_purchase_response)
end
@@ -838,6 +937,7 @@ def test_client_data_submitted_with_metadata_in_options_with_emv_credit_card_pur
assert_match(/metadata\[i_made_up_this_key_too\]=canyoutell/, data)
assert_match(/metadata\[email\]=foo\%40wonderfullyfakedomain\.com/, data)
assert_match(/metadata\[order_id\]=42/, data)
+ assert_match(/metadata\[card_read_method\]=contact/, data)
end.respond_with(successful_purchase_response)
end
@@ -850,6 +950,25 @@ def test_client_data_submitted_with_metadata_in_options_with_emv_credit_card_aut
assert_match(/metadata\[i_made_up_this_key_too\]=canyoutell/, data)
assert_match(/metadata\[email\]=foo\%40wonderfullyfakedomain\.com/, data)
assert_match(/metadata\[order_id\]=42/, data)
+ assert_match(/metadata\[card_read_method\]=contact/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
+ def test_quickchip_is_set_on_purchase
+ stub_comms(@gateway, :ssl_request) do
+ @emv_credit_card.read_method = 'contact_quickchip'
+ @gateway.purchase(@amount, @emv_credit_card, @options)
+ end.check_request do |method, endpoint, data, headers|
+ assert_match(/card\[processing_method\]=quick_chip/, data)
+ end.respond_with(successful_purchase_response)
+ end
+
+ def test_quickchip_is_not_set_on_authorize
+ stub_comms(@gateway, :ssl_request) do
+ @emv_credit_card.read_method = 'contact_quickchip'
+ @gateway.authorize(@amount, @emv_credit_card, @options)
+ end.check_request do |method, endpoint, data, headers|
+ refute_match(/card\[processing_method\]=quick_chip/, data)
end.respond_with(successful_purchase_response)
end
@@ -864,6 +983,32 @@ def test_add_address
assert_equal @options[:billing_address][:city], post[:card][:address_city]
end
+ def test_add_statement_address
+ post = {}
+
+ @gateway.send(:add_statement_address, post, @options)
+
+ assert_equal @options[:statement_address][:zip], post[:statement_address][:postal_code]
+ assert_equal @options[:statement_address][:state], post[:statement_address][:state]
+ assert_equal @options[:statement_address][:address1], post[:statement_address][:line1]
+ assert_equal @options[:statement_address][:address2], post[:statement_address][:line2]
+ assert_equal @options[:statement_address][:country], post[:statement_address][:country]
+ assert_equal @options[:statement_address][:city], post[:statement_address][:city]
+ end
+
+ def test_add_statement_address_returns_nil_if_required_fields_missing
+ post = {}
+ [:address1, :city, :zip, :state].each do |required_key|
+ missing_required = @options.tap do |options|
+ options[:statement_address].delete_if { |k| k == required_key }
+ end
+
+ @gateway.send(:add_statement_address, post, missing_required)
+
+ assert_equal nil, post[:statement_address]
+ end
+ end
+
def test_ensure_does_not_respond_to_credit
assert !@gateway.respond_to?(:credit)
end
@@ -895,9 +1040,31 @@ def test_optional_idempotency_key_header
headers && headers['Idempotency-Key'] == 'test123'
}.returns(successful_purchase_response)
- @gateway.purchase(@amount, @credit_card, @options.merge(:idempotency_key => 'test123'))
+ response = @gateway.purchase(@amount, @credit_card, @options.merge(:idempotency_key => 'test123'))
+ assert_success response
+ end
+
+ def test_optional_idempotency_on_void
+ @gateway.expects(:ssl_request).once.with {|method, url, post, headers|
+ headers && headers['Idempotency-Key'] == 'test123'
+ }.returns(successful_purchase_response(true))
+
+ response = @gateway.void('ch_test_charge', @options.merge(:idempotency_key => 'test123'))
+ assert_success response
end
+ def test_optional_idempotency_on_verify
+ @gateway.expects(:ssl_request).with do |method, url, post, headers|
+ headers && headers['Idempotency-Key'] == nil
+ end.returns(successful_void_response)
+
+ @gateway.expects(:ssl_request).with do |method, url, post, headers|
+ headers && headers['Idempotency-Key'] == 'test123'
+ end.returns(successful_authorization_response)
+
+ response = @gateway.verify(@credit_card, @options.merge(:idempotency_key => 'test123'))
+ assert_success response
+ end
def test_initialize_gateway_with_version
@gateway = StripeGateway.new(:login => 'login', :version => '2013-12-03')
@@ -933,9 +1100,9 @@ def test_address_is_included_with_card_data
end.respond_with(successful_purchase_response)
end
- def test_contactless_emv_flag_is_included_with_emv_card_data
+ def test_contactless_flag_is_included_with_emv_card_data
stub_comms(@gateway, :ssl_request) do
- @emv_credit_card.contactless_emv = true
+ @emv_credit_card.read_method = 'contactless'
@gateway.purchase(@amount, @emv_credit_card, @options)
end.check_request do |method, endpoint, data, headers|
data =~ /card\[read_method\]=contactless/
@@ -944,7 +1111,7 @@ def test_contactless_emv_flag_is_included_with_emv_card_data
def test_contactless_magstripe_flag_is_included_with_emv_card_data
stub_comms(@gateway, :ssl_request) do
- @emv_credit_card.contactless_magstripe = true
+ @emv_credit_card.read_method = 'contactless_magstripe'
@gateway.purchase(@amount, @emv_credit_card, @options)
end.check_request do |method, endpoint, data, headers|
data =~ /card\[read_method\]=contactless_magstripe_mode/
@@ -1203,7 +1370,7 @@ def test_verify_bad_credentials
# this mock is only useful with unit tests, as cryptograms generated by an EMV terminal
# are specific to the target acquirer, so remote tests using this mock will fail elsewhere.
def credit_card_with_icc_data
- ActiveMerchant::Billing::CreditCard.new(icc_data: '500B56495341204352454449545F201A56495341204143515549524552205445535420434152442030315F24031512315F280208405F2A0208265F300202015F34010182025C008407A0000000031010950502000080009A031408259B02E8009C01009F02060000000734499F03060000000000009F0607A00000000310109F0902008C9F100706010A03A080009F120F4352454449544F20444520564953419F1A0208269F1C0831373030303437309F1E0831373030303437309F2608EB2EC0F472BEA0A49F2701809F3303E0B8C89F34031E03009F3501229F360200C39F37040A27296F9F4104000001319F4502DAC5DFAE5711476173FFFFFF0119D15122011758989389DFAE5A08476173FFFFFF011957114761739001010119D151220117589893895A084761739001010119')
+ ActiveMerchant::Billing::CreditCard.new(read_method: 'contact', icc_data: '500B56495341204352454449545F201A56495341204143515549524552205445535420434152442030315F24031512315F280208405F2A0208265F300202015F34010182025C008407A0000000031010950502000080009A031408259B02E8009C01009F02060000000734499F03060000000000009F0607A00000000310109F0902008C9F100706010A03A080009F120F4352454449544F20444520564953419F1A0208269F1C0831373030303437309F1E0831373030303437309F2608EB2EC0F472BEA0A49F2701809F3303E0B8C89F34031E03009F3501229F360200C39F37040A27296F9F4104000001319F4502DAC5DFAE5711476173FFFFFF0119D15122011758989389DFAE5A08476173FFFFFF011957114761739001010119D151220117589893895A084761739001010119')
end
def pre_scrubbed
@@ -1213,7 +1380,7 @@ def pre_scrubbed
starting SSL for api.stripe.com:443...
SSL established
<- "POST /v1/charges HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAuthorization: Basic c2tfdGVzdF9oQkwwTXF6ZGZ6Rnk3OXU0cFloUmVhQlo6\r\nUser-Agent: Stripe/v1 ActiveMerchantBindings/1.45.0\r\nX-Stripe-Client-User-Agent: {\"bindings_version\":\"1.45.0\",\"lang\":\"ruby\",\"lang_version\":\"2.1.3 p242 (2014-09-19)\",\"platform\":\"x86_64-linux\",\"publisher\":\"active_merchant\"}\r\nX-Stripe-Client-User-Metadata: {\"ip\":null}\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nConnection: close\r\nHost: api.stripe.com\r\nContent-Length: 270\r\n\r\n"
- <- "amount=100¤cy=usd&card[number]=4242424242424242&card[exp_month]=9&card[exp_year]=2015&card[cvc]=123&card[name]=Longbob+Longsen&description=ActiveMerchant+Test+Purchase&payment_user_agent=Stripe%2Fv1+ActiveMerchantBindings%2F1.45.0&metadata[email]=wow%40example.com&three_d_secure[cryptogram]=123456789abcdefghijklmnop&three_d_secure[apple_pay]=true"
+ <- "amount=100¤cy=usd&card[number]=4242424242424242&card[exp_month]=9&card[exp_year]=2015&card[cvc]=123&card[name]=Longbob+Longsen&description=ActiveMerchant+Test+Purchase&payment_user_agent=Stripe%2Fv1+ActiveMerchantBindings%2F1.45.0&metadata[email]=wow%40example.com&card[cryptogram]=sensitive_data&three_d_secure[cryptogram]=123456789abcdefghijklmnop&three_d_secure[apple_pay]=true"
-> "HTTP/1.1 200 OK\r\n"
-> "Server: nginx\r\n"
-> "Date: Tue, 02 Dec 2014 19:44:17 GMT\r\n"
@@ -1272,7 +1439,7 @@ def pre_scrubbed_with_emv_data
starting SSL for api.stripe.com:443...
SSL established
<- "POST /v1/charges HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAuthorization: Basic c2tfdGVzdF8zT0Q0VGRLU0lPaERPTDIxNDZKSmNDNzk6\r\nUser-Agent: Stripe/v1 ActiveMerchantBindings/1.54.0\r\nStripe-Version: 2015-04-07\r\nX-Stripe-Client-User-Agent: {\"bindings_version\":\"1.54.0\",\"lang\":\"ruby\",\"lang_version\":\"2.1.1 p76 (2014-02-24)\",\"platform\":\"x86_64-darwin12.0\",\"publisher\":\"active_merchant\"}\r\nX-Stripe-Client-User-Metadata: {\"ip\":null}\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nConnection: close\r\nHost: api.stripe.com\r\nContent-Length: 713\r\n\r\n"
- <- "card[emv_auth_data]=500B56495341204352454449545F201A56495341204143515549524552205445535420434152442030315F24031512315F280208405F2A0208265F300202015F34010182025C008407A0000000031010950502000080009A031408259B02E8009C01009F02060000000734499F03060000000000009F0607A00000000310109F0902008C9F100706010A03A080009F120F4352454449544F20444520564953419F1A0208269F1C0831373030303437309F1E0831373030303437309F2608EB2EC0F472BEA0A49F2701809F3303E0B8C89F34031E03009F3501229F360200C39F37040A27296F9F4104000001319F4502DAC5DFAE5711476173FFFFFF0119D15122011758989389DFAE5A08476173FFFFFF011957114761739001010119D151220117589893895A084761739001010119&card[encrypted_pin]=8b68af72199529b8&card[encrypted_pin_key_id]=ffff0102628d12000001"
+ <- "card[emv_auth_data]=500B56495341204352454449545F201A56495341204143515549524552205445535420434152442030315F24031512315F280208405F2A0208265F300202015F34010182025C008407A0000000031010950502000080009A031408259B02E8009C01009F02060000000734499F03060000000000009F0607A00000000310109F0902008C9F100706010A03A080009F120F4352454449544F20444520564953419F1A0208269F1C0831373030303437309F1E0831373030303437309F2608EB2EC0F472BEA0A49F2701809F3303E0B8C89F34031E03009F3501229F360200C39F37040A27296F9F4104000001319F4502DAC5DFAE5711476173FFFFFF0119D15122011758989389DFAE5A08476173FFFFFF011957114761739001010119D151220117589893895A084761739001010119&card[emv_approval_data]=garbage&card[encrypted_pin]=8b68af72199529b8&card[encrypted_pin_key_id]=ffff0102628d12000001"
-> "HTTP/1.1 402 Payment Required\r\n"
-> "Server: nginx\r\n"
-> "Date: Wed, 21 Oct 2015 17:39:02 GMT\r\n"
@@ -1301,7 +1468,7 @@ def post_scrubbed_with_emv_data
starting SSL for api.stripe.com:443...
SSL established
<- "POST /v1/charges HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAuthorization: Basic [FILTERED]\r\nUser-Agent: Stripe/v1 ActiveMerchantBindings/1.54.0\r\nStripe-Version: 2015-04-07\r\nX-Stripe-Client-User-Agent: {\"bindings_version\":\"1.54.0\",\"lang\":\"ruby\",\"lang_version\":\"2.1.1 p76 (2014-02-24)\",\"platform\":\"x86_64-darwin12.0\",\"publisher\":\"active_merchant\"}\r\nX-Stripe-Client-User-Metadata: {\"ip\":null}\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nConnection: close\r\nHost: api.stripe.com\r\nContent-Length: 713\r\n\r\n"
- <- "card[emv_auth_data]=[FILTERED]&card[encrypted_pin]=[FILTERED]&card[encrypted_pin_key_id]=[FILTERED]"
+ <- "card[emv_auth_data]=[FILTERED]&card[emv_approval_data]=[FILTERED]&card[encrypted_pin]=[FILTERED]&card[encrypted_pin_key_id]=[FILTERED]"
-> "HTTP/1.1 402 Payment Required\r\n"
-> "Server: nginx\r\n"
-> "Date: Wed, 21 Oct 2015 17:39:02 GMT\r\n"
@@ -1360,7 +1527,7 @@ def post_scrubbed
starting SSL for api.stripe.com:443...
SSL established
<- "POST /v1/charges HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAuthorization: Basic [FILTERED]\r\nUser-Agent: Stripe/v1 ActiveMerchantBindings/1.45.0\r\nX-Stripe-Client-User-Agent: {\"bindings_version\":\"1.45.0\",\"lang\":\"ruby\",\"lang_version\":\"2.1.3 p242 (2014-09-19)\",\"platform\":\"x86_64-linux\",\"publisher\":\"active_merchant\"}\r\nX-Stripe-Client-User-Metadata: {\"ip\":null}\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nConnection: close\r\nHost: api.stripe.com\r\nContent-Length: 270\r\n\r\n"
- <- "amount=100¤cy=usd&card[number]=[FILTERED]&card[exp_month]=9&card[exp_year]=2015&card[cvc]=[FILTERED]&card[name]=Longbob+Longsen&description=ActiveMerchant+Test+Purchase&payment_user_agent=Stripe%2Fv1+ActiveMerchantBindings%2F1.45.0&metadata[email]=wow%40example.com&three_d_secure[cryptogram]=[FILTERED]&three_d_secure[apple_pay]=true"
+ <- "amount=100¤cy=usd&card[number]=[FILTERED]&card[exp_month]=9&card[exp_year]=2015&card[cvc]=[FILTERED]&card[name]=Longbob+Longsen&description=ActiveMerchant+Test+Purchase&payment_user_agent=Stripe%2Fv1+ActiveMerchantBindings%2F1.45.0&metadata[email]=wow%40example.com&card[cryptogram]=[FILTERED]&three_d_secure[cryptogram]=[FILTERED]&three_d_secure[apple_pay]=true"
-> "HTTP/1.1 200 OK\r\n"
-> "Server: nginx\r\n"
-> "Date: Tue, 02 Dec 2014 19:44:17 GMT\r\n"
@@ -1808,52 +1975,55 @@ def failed_void_response
RESPONSE
end
- def successful_refunded_application_fee_response
+ def successful_partially_refunded_application_fee_response
<<-RESPONSE
{
- "id": "fee_id",
- "object": "application_fee",
- "created": 1375375417,
- "livemode": false,
+ "id": "fr_C8qmJKrZVMTjjF",
+ "object": "fee_refund",
"amount": 10,
+ "balance_transaction": "txn_1BkZ4uAWOtgoysognvusG5N5",
+ "created": 1516027008,
"currency": "usd",
- "user": "acct_id",
- "user_email": "acct_id",
- "application": "ca_application",
- "charge": "ch_test_charge",
- "refunded": false,
- "amount_refunded": 10
+ "fee": "fee_1BkZ4rIPBJTitsenGWcxYWCZ",
+ "metadata": {}
}
RESPONSE
end
- def successful_application_fee_list_response
+ def successful_fetch_application_fee_response
<<-RESPONSE
{
- "object": "list",
- "count": 2,
- "url": "/v1/application_fees",
- "data": [
- {
- "object": "application_fee",
- "id": "application_fee_id"
- },
- {
- "object": "another_fee",
- "id": "another_fee_id"
- }
- ]
+ "id": "ch_1Bja3MIPBJTitsenv28Gy6iN",
+ "object": "charge",
+ "amount": 100,
+ "amount_refunded": 100,
+ "application": "ca_6E9gvTfZGEMknxpoHhC8xoeyMit55FAV",
+ "application_fee": "fee_1Bja3MIPBJTitsenKqV8Hc6R",
+ "balance_transaction": "txn_1Bja3OIPBJTitsenJ5amtW58",
+ "captured": true,
+ "created": 1515792428,
+ "currency": "usd",
+ "customer": null,
+ "description": "ActiveMerchant Test Purchase",
+ "destination": null,
+ "dispute": null,
+ "failure_code": null,
+ "failure_message": null,
+ "fraud_details": {},
+ "invoice": null,
+ "livemode": false
}
RESPONSE
end
- def unsuccessful_application_fee_list_response
+ def unsuccessful_fetch_application_fee_response
<<-RESPONSE
{
- "object": "list",
- "count": 0,
- "url": "/v1/application_fees",
- "data": []
+ "error": {
+ "type": "invalid_request_error",
+ "message": "No such charge: bad_auth",
+ "param": "id"
+ }
}
RESPONSE
end
@@ -1898,6 +2068,20 @@ def declined_call_issuer_purchase_response
RESPONSE
end
+ def declined_pickup_card_purchase_response
+ <<-RESPONSE
+ {
+ "error": {
+ "message": "Your card was declined.",
+ "type": "card_error",
+ "code": "card_declined",
+ "decline_code": "pickup_card",
+ "charge": "ch_test_charge"
+ }
+ }
+ RESPONSE
+ end
+
def declined_generic_decline_purchase_response
<<-RESPONSE
{
diff --git a/test/unit/gateways/trans_first_test.rb b/test/unit/gateways/trans_first_test.rb
index c9f10c3f810..25c1ef9545d 100644
--- a/test/unit/gateways/trans_first_test.rb
+++ b/test/unit/gateways/trans_first_test.rb
@@ -68,6 +68,7 @@ def test_successful_refund
response = @gateway.refund(@amount, "TransID")
assert_success response
assert_equal '207686608|creditcard', response.authorization
+ assert_equal @amount, response.params["amount"].to_i*100
end
def test_failed_refund
@@ -77,6 +78,20 @@ def test_failed_refund
assert_failure response
end
+ def test_successful_void
+ @gateway.stubs(:ssl_post).returns(successful_void_response)
+
+ response = @gateway.void("TransID")
+ assert_success response
+ end
+
+ def test_failed_void
+ @gateway.stubs(:ssl_post).returns(failed_void_response)
+
+ response = @gateway.void("TransID")
+ assert_failure response
+ end
+
def test_avs_result
@gateway.expects(:ssl_post).returns(successful_purchase_response)
@@ -320,4 +335,36 @@ def failed_refund_response
XML
end
+
+ def successful_void_response
+ <<-XML
+
+
+ 0
+ 207616632
+ 0
+ 123
+ 2010-08-09T12:25:00 0001-01-01T00:00:00
+ 1.3100
+ 012921
+ Voided
+ N
+
+
+ XML
+ end
+
+ def failed_void_response
+ <<-XML
+
+
+ 0
+ 0
+ 0001-01-01T00:00:00 0001-01-01T00:00:00
+ 0
+ Canceled
+ Transaction Is Not Allowed To Void or Refund
+
+ XML
+ end
end
diff --git a/test/unit/gateways/trans_first_transaction_express_test.rb b/test/unit/gateways/trans_first_transaction_express_test.rb
index 938c94a71a5..86db994262b 100644
--- a/test/unit/gateways/trans_first_transaction_express_test.rb
+++ b/test/unit/gateways/trans_first_transaction_express_test.rb
@@ -10,9 +10,9 @@ def setup
)
@credit_card = credit_card
+ @check = check
@amount = 100
@declined_amount = 21
- @partial_amount = 1110
end
def test_successful_purchase
@@ -26,16 +26,6 @@ def test_successful_purchase
assert response.test?
end
- def test_partial_purchase
- response = stub_comms do
- @gateway.purchase(@partial_amount, @credit_card)
- end.respond_with(partial_purchase_response)
-
- assert_success response
- assert_equal "000000000555", response.params["amt"]
- assert response.test?
- end
-
def test_failed_purchase
response = stub_comms do
@gateway.purchase(@declined_amount, @credit_card)
@@ -47,6 +37,22 @@ def test_failed_purchase
assert response.test?
end
+ def test_successful_purchase_with_echeck
+ @gateway.stubs(:ssl_post).returns(successful_purchase_echeck_response)
+ response = @gateway.purchase(@amount, @check)
+
+ assert_success response
+ assert_equal "purchase_echeck|000028705491", response.authorization
+ end
+
+ def test_failed_purchase_with_echeck
+ @gateway.stubs(:ssl_post).returns(failed_purchase_echeck_response)
+ response = @gateway.purchase(@amount, @check)
+
+ assert_failure response
+ assert_equal "Error. Bank routing number validation negative (ABA).", response.message
+ end
+
def test_successful_authorize_and_capture
response = stub_comms do
@gateway.authorize(@amount, @credit_card)
@@ -137,6 +143,32 @@ def test_failed_refund
assert_equal "50011", response.error_code
end
+ def test_successful_refund_with_echeck
+ response = stub_comms do
+ @gateway.purchase(@amount, @check)
+ end.respond_with(successful_purchase_echeck_response)
+
+ assert_success response
+ assert_equal "purchase_echeck|000028705491", response.authorization
+
+ refund = stub_comms do
+ @gateway.refund(@amount, response.authorization)
+ end.check_request do |endpoint, data, headers|
+ assert_match(/000028705491/, data)
+ end.respond_with(successful_refund_echeck_response)
+
+ assert_success refund
+ end
+
+ def test_failed_refund_with_echeck
+ response = stub_comms do
+ @gateway.refund(@amount, 'purchase_echeck|000028706091')
+ end.respond_with(failed_refund_response)
+
+ assert_failure response
+ assert_equal "50011", response.error_code
+ end
+
def test_successful_credit
response = stub_comms do
@gateway.credit(@amount, @credit_card)
@@ -216,10 +248,6 @@ def successful_purchase_response
%(00Y0A1009331525B2A2DBFAF771E2E62B0000152125612016-01-19T10:33:57.000-08:00000000000100305156Lexc050300979940268000)
end
- def partial_purchase_response
- %(10MZY0A10092D15279AD062097039A74A150000155261612016-01-25T08:45:28.000-08:00000000000555332604Lexc0503009799402680000057840C000000001110)
- end
-
def failed_purchase_response
%(51Y0A1009331525BA8F333FC15F59AB320000152206712016-01-19T12:52:25.000-08:00000000000021305918Lexc050300979940268000)
end
@@ -272,6 +300,22 @@ def failed_store_response
%(S:ServerValidation FailureValidation Faultcvc-type.3.1.3: The value '123' of element 'v1:pan' is not valid.50011)
end
+ def successful_purchase_echeck_response
+ %(00435508710A09071615AD2403F804EFDA26EA760000287054912017-03-15T06:55:10-07:00000000000100386950Transaction processed.PrevPay: nil +0Score: 100/100)
+ end
+
+ def failed_purchase_echeck_response
+ %(060A09071715AD2654A6814EE9ADC0EF0000287057112017-03-15T07:35:38-07:00000000000100386972Bank routing number validation negative (ABA).)
+ end
+
+ def successful_refund_echeck_response
+ %( 00435508890A09071715AD2786821E2F357D7E520000287060912017-03-15T07:56:31-07:00000000000100387010Transaction Cancelled.PrevPay: nil +0Score: 100/100Cancellation Notes: RefNumber:28706091)
+ end
+
+ def failed_refund_echeck_response
+ %(12B40F435508890A09071615AD285C3E4E0AE3A42CF30000287060912017-03-15T08:11:06-07:00000000000100)
+ end
+
def empty_purchase_response
%()
end
diff --git a/test/unit/gateways/trexle_test.rb b/test/unit/gateways/trexle_test.rb
new file mode 100644
index 00000000000..b07bbc55393
--- /dev/null
+++ b/test/unit/gateways/trexle_test.rb
@@ -0,0 +1,444 @@
+require 'test_helper'
+
+class TrexleTest < Test::Unit::TestCase
+ def setup
+ @gateway = TrexleGateway.new(api_key: 'THIS_IS_NOT_A_REAL_API_KEY')
+
+ @credit_card = credit_card
+ @amount = 100
+
+ @options = {
+ email: 'john@trexle.com',
+ billing_address: address,
+ description: 'Store Purchase',
+ ip: '127.0.0.1'
+ }
+ end
+
+ def test_required_api_key_on_initialization
+ assert_raises ArgumentError do
+ TrexleGateway.new
+ end
+ end
+
+ def test_default_currency
+ assert_equal 'USD', TrexleGateway.default_currency
+ end
+
+ def test_money_format
+ assert_equal :cents, TrexleGateway.money_format
+ end
+
+ def test_url
+ assert_equal 'https://core.trexle.com/api/v1', TrexleGateway.test_url
+ end
+
+ def test_live_url
+ assert_equal 'https://core.trexle.com/api/v1', TrexleGateway.live_url
+ end
+
+ def test_supported_countries
+ expected_supported_countries = %w(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)
+ assert_equal expected_supported_countries, TrexleGateway.supported_countries
+ end
+
+ def test_supported_cardtypes
+ assert_equal [:visa, :master, :american_express], TrexleGateway.supported_cardtypes
+ end
+
+ def test_display_name
+ assert_equal 'Trexle', TrexleGateway.display_name
+ end
+
+ def test_setup_purchase_parameters
+ @gateway.expects(:add_amount).with(instance_of(Hash), @amount, @options)
+ @gateway.expects(:add_customer_data).with(instance_of(Hash), @options)
+ @gateway.expects(:add_invoice).with(instance_of(Hash), @options)
+ @gateway.expects(:add_creditcard).with(instance_of(Hash), @credit_card)
+ @gateway.expects(:add_address).with(instance_of(Hash), @credit_card, @options)
+
+ @gateway.stubs(:ssl_request).returns(successful_purchase_response)
+ assert_success @gateway.purchase(@amount, @credit_card, @options)
+ end
+
+ def test_successful_purchase
+ post_data = {}
+ headers = {}
+ @gateway.stubs(:headers).returns(headers)
+ @gateway.stubs(:post_data).returns(post_data)
+ @gateway.expects(:ssl_request).with(:post, 'https://core.trexle.com/api/v1/charges', post_data, headers).returns(successful_purchase_response)
+
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'charge_0cfad7ee5ffe75f58222bff214bfa5cc7ad7c367', response.authorization
+ assert_equal JSON.parse(successful_purchase_response), response.params
+ assert response.test?
+ end
+
+ def test_unsuccessful_request
+ @gateway.expects(:ssl_request).returns(failed_purchase_response)
+
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal "Invalid response.", response.message
+ end
+
+ def test_unparsable_body_of_successful_response
+ @gateway.stubs(:raw_ssl_request).returns(MockResponse.succeeded("not-json"))
+
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_match(/Invalid JSON response received/, response.message)
+ end
+
+ def test_successful_store
+ @gateway.expects(:ssl_request).returns(successful_store_response)
+ assert response = @gateway.store(@credit_card, @options)
+ assert_success response
+ assert_equal 'token_2cb443cf26b6ecdadd8144d1fac8240710aa41f1', response.authorization
+ assert_equal JSON.parse(successful_store_response), response.params
+ assert response.test?
+ end
+
+ def test_unsuccessful_store
+ @gateway.expects(:ssl_request).returns(failed_store_response)
+
+ assert response = @gateway.store(@credit_card, @options)
+ assert_failure response
+ assert_equal "Invalid response.", response.message
+ end
+
+ def test_successful_update
+ token = 'token_940ade441a23d53e04017f53af6c3a1eae9978ae'
+ @gateway.expects(:ssl_request).with(:put, "https://core.trexle.com/api/v1/customers/#{token}", instance_of(String), instance_of(Hash)).returns(successful_customer_store_response)
+ assert response = @gateway.update('token_940ade441a23d53e04017f53af6c3a1eae9978ae', @credit_card, @options)
+ assert_success response
+ assert_equal 'token_940ade441a23d53e04017f53af6c3a1eae9978ae', response.authorization
+ assert_equal JSON.parse(successful_customer_store_response), response.params
+ assert response.test?
+ end
+
+ def test_successful_refund
+ token = 'charge_0cfad7ee5ffe75f58222bff214bfa5cc7ad7c367'
+ @gateway.expects(:ssl_request).with(:post, "https://core.trexle.com/api/v1/charges/#{token}/refunds", {amount: '100'}.to_json, instance_of(Hash)).returns(successful_refund_response)
+
+ assert response = @gateway.refund(100, token)
+ assert_equal 'refund_7f696a86f9cb136520c51ea90c17f687b8df40b0', response.authorization
+ assert_success response
+ assert response.test?
+ end
+
+ def test_unsuccessful_refund
+ token = 'charge_0cfad7ee5ffe75f58222bff214bfa5cc7ad7c367'
+ @gateway.expects(:ssl_request).with(:post, "https://core.trexle.com/api/v1/charges/#{token}/refunds", {amount: '100'}.to_json, instance_of(Hash)).returns(failed_refund_response)
+
+ assert response = @gateway.refund(100, token)
+ assert_failure response
+ assert_equal "Invalid response.", response.message
+ end
+
+ def test_successful_authorize
+ post_data = {}
+ headers = {}
+ @gateway.stubs(:headers).returns(headers)
+ @gateway.stubs(:post_data).returns(post_data)
+ @gateway.expects(:ssl_request).with(:post, 'https://core.trexle.com/api/v1/charges', post_data, headers).returns(successful_purchase_response)
+
+ assert response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_success response
+ assert_equal 'charge_0cfad7ee5ffe75f58222bff214bfa5cc7ad7c367', response.authorization
+ assert_equal JSON.parse(successful_purchase_response), response.params
+ assert response.test?
+ end
+
+ def test_successful_capture
+ post_data = {}
+ headers = {}
+ token = 'charge_6e47a330dca67ec7f696e8b650db22fe69bb8499'
+ @gateway.stubs(:headers).returns(headers)
+ @gateway.stubs(:post_data).returns(post_data)
+ @gateway.expects(:ssl_request).with(:put, "https://core.trexle.com/api/v1/charges/#{token}/capture", post_data, headers).returns(successful_capture_response)
+
+ assert response = @gateway.capture(100, token)
+ assert_success response
+ assert_equal token, response.authorization
+ assert response.test?
+ end
+
+ def test_store_parameters
+ @gateway.expects(:add_creditcard).with(instance_of(Hash), @credit_card)
+ @gateway.expects(:add_address).with(instance_of(Hash), @credit_card, @options)
+ @gateway.expects(:ssl_request).returns(successful_store_response)
+ assert_success @gateway.store(@credit_card, @options)
+ end
+
+ def test_update_parameters
+ @gateway.expects(:add_creditcard).with(instance_of(Hash), @credit_card)
+ @gateway.expects(:add_address).with(instance_of(Hash), @credit_card, @options)
+ @gateway.expects(:ssl_request).returns(successful_store_response)
+ assert_success @gateway.update('token_6b5d89f723d1aeee8ff0c588fd4ccbaae223b9aa', @credit_card, @options)
+ end
+
+ def test_add_amount
+ @gateway.expects(:amount).with(100).returns('100')
+ post = {}
+ @gateway.send(:add_amount, post, 100, @options)
+ assert_equal '100', post[:amount]
+ end
+
+ def test_set_default_currency
+ @gateway.expects(:currency).with(100).returns('USD')
+ post = {}
+ @gateway.send(:add_amount, post, 100, @options)
+ assert_equal 'USD', post[:currency]
+ end
+
+ def test_set_currency
+ @gateway.expects(:currency).never
+ post = {}
+ @options[:currency] = 'USD'
+ @gateway.send(:add_amount, post, 100, @options)
+ assert_equal 'USD', post[:currency]
+ end
+
+ def test_set_currency_case
+ @gateway.expects(:currency).never
+ post = {}
+ @options[:currency] = 'usd'
+ @gateway.send(:add_amount, post, 100, @options)
+ assert_equal 'USD', post[:currency]
+ end
+
+ def test_add_customer_data
+ post = {}
+
+ @gateway.send(:add_customer_data, post, @options)
+
+ assert_equal 'john@trexle.com', post[:email]
+ assert_equal '127.0.0.1', post[:ip_address]
+ end
+
+ def test_add_address
+ post = {}
+
+ @gateway.send(:add_address, post, @credit_card, @options)
+
+ assert_equal @options[:billing_address][:address1], post[:card][:address_line1]
+ assert_equal @options[:billing_address][:city], post[:card][:address_city]
+ assert_equal @options[:billing_address][:zip], post[:card][:address_postcode]
+ assert_equal @options[:billing_address][:state], post[:card][:address_state]
+ assert_equal @options[:billing_address][:country], post[:card][:address_country]
+ end
+
+ def test_add_address_with_card_token
+ post = {}
+
+ @gateway.send(:add_address, post, 'somecreditcardtoken', @options)
+
+ assert_equal false, post.has_key?(:card)
+ end
+
+ def test_add_invoice
+ post = {}
+ @gateway.send(:add_invoice, post, @options)
+
+ assert_equal @options[:description], post[:description]
+ end
+
+ def test_add_creditcard
+ post = {}
+ @gateway.send(:add_creditcard, post, @credit_card)
+
+ assert_equal @credit_card.number, post[:card][:number]
+ 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.name, post[:card][:name]
+ end
+
+ def test_add_creditcard_with_card_token
+ post = {}
+ @gateway.send(:add_creditcard, post, 'token_f974687e4e866d6cca534e1cd42236817d315b3a')
+ assert_equal 'token_f974687e4e866d6cca534e1cd42236817d315b3a', post[:card_token]
+ assert_false post.has_key?(:card)
+ end
+
+ def test_add_creditcard_with_customer_token
+ post = {}
+ @gateway.send(:add_creditcard, post, 'token_2cb443cf26b6ecdadd8144d1fac8240710aa41f1')
+ assert_equal 'token_2cb443cf26b6ecdadd8144d1fac8240710aa41f1', post[:card_token]
+ assert_false post.has_key?(:card)
+ end
+
+ def test_post_data
+ post = {}
+ @gateway.send(:add_creditcard, post, @credit_card)
+ assert_equal post.to_json, @gateway.send(:post_data, post)
+ end
+
+ def test_headers
+ expected_headers = {
+ "Content-Type" => "application/json",
+ "Authorization" => "Basic #{Base64.strict_encode64('THIS_IS_NOT_A_REAL_API_KEY:').strip}"
+ }
+
+ @gateway.expects(:ssl_request).with(:post, anything, anything, expected_headers).returns(successful_purchase_response)
+ assert response = @gateway.purchase(@amount, @credit_card, {})
+
+ expected_headers['X-Partner-Key'] = 'MyPartnerKey'
+ expected_headers['X-Safe-Card'] = '1'
+
+ @gateway.expects(:ssl_request).with(:post, anything, anything, expected_headers).returns(successful_purchase_response)
+ assert response = @gateway.purchase(@amount, @credit_card, partner_key: 'MyPartnerKey', safe_card: '1')
+ end
+
+ def test_transcript_scrubbing
+ assert_equal scrubbed_transcript, @gateway.scrub(transcript)
+ end
+
+ private
+
+ def successful_purchase_response
+ '{
+ "response":{
+ "token":"charge_0cfad7ee5ffe75f58222bff214bfa5cc7ad7c367",
+ "success":true,
+ "captured":true
+ }
+ }'
+ end
+
+ def failed_purchase_response
+ '{
+ "error":"Payment failed",
+ "detail":"An error occurred while processing your card. Try again in a little bit."
+ }'
+ end
+
+ def successful_store_response
+ '{
+ "response":{
+ "token":"token_2cb443cf26b6ecdadd8144d1fac8240710aa41f1",
+ "card":{
+ "token":"token_f974687e4e866d6cca534e1cd42236817d315b3a",
+ "primary":true
+ }
+ }
+ }'
+ end
+
+ def failed_store_response
+ '{
+ "error":"an error has occured",
+ "detail":"invalid token"
+ }'
+ end
+
+ def successful_customer_store_response
+ '{
+ "response":{
+ "token":"token_940ade441a23d53e04017f53af6c3a1eae9978ae",
+ "card":{
+ "token":"token_9a3f559962cbf6828e2cc38a02023565b0294548",
+ "scheme":"master",
+ "display_number":"XXXX-XXXX-XXXX-4444",
+ "expiry_year":2019,
+ "expiry_month":9,
+ "cvc":123,
+ "name":"Longbob Longsen",
+ "address_line1":"456 My Street",
+ "address_line2":null,
+ "address_city":"Ottawa",
+ "address_state":"ON",
+ "address_postcode":"K1C2N6",
+ "address_country":"CA",
+ "primary":true
+ }
+ }
+ }'
+ end
+
+ def failed_customer_store_response
+ '{
+ "error":"an error has occured",
+ "detail":"invalid token"
+ }'
+ end
+
+ def successful_refund_response
+ '{
+ "response":{
+ "token":"refund_7f696a86f9cb136520c51ea90c17f687b8df40b0",
+ "success":true,
+ "amount":100,
+ "charge":"charge_ee4542e9f4d2c50f7fea55b694423a53991a323a",
+ "status_message":"Transaction approved"
+ }
+ }'
+ end
+
+ def failed_refund_response
+ '{
+ "error":"Refund failed",
+ "detail":"invalid token"
+ }'
+ end
+
+ def successful_capture_response
+ '{
+ "response":{
+ "token":"charge_6e47a330dca67ec7f696e8b650db22fe69bb8499",
+ "success":true,
+ "captured":true
+ }
+ }'
+ end
+
+ def transcript
+ '{
+ "amount":"100",
+ "currency":"USD",
+ "email":"john@trexle.com",
+ "ip_address":"66.249.79.118",
+ "description":"Store Purchase 1437598192",
+ "card":{
+ "number":"5555555555554444",
+ "expiry_month":9,
+ "expiry_year":2017,
+ "cvc":"123",
+ "name":"Longbob Longsen",
+ "address_line1":"456 My Street",
+ "address_city":"Ottawa",
+ "address_postcode":"K1C2N6",
+ "address_state":"ON",
+ "address_country":"CA"
+ }
+ }'
+ end
+
+ def scrubbed_transcript
+ '{
+ "amount":"100",
+ "currency":"USD",
+ "email":"john@trexle.com",
+ "ip_address":"66.249.79.118",
+ "description":"Store Purchase 1437598192",
+ "card":{
+ "number":"[FILTERED]",
+ "expiry_month":9,
+ "expiry_year":2017,
+ "cvc":"[FILTERED]",
+ "name":"Longbob Longsen",
+ "address_line1":"456 My Street",
+ "address_city":"Ottawa",
+ "address_postcode":"K1C2N6",
+ "address_state":"ON",
+ "address_country":"CA"
+ }
+ }'
+ end
+
+end
diff --git a/test/unit/gateways/usa_epay_advanced_test.rb b/test/unit/gateways/usa_epay_advanced_test.rb
index 91c6d13e2b4..10c8967b96a 100644
--- a/test/unit/gateways/usa_epay_advanced_test.rb
+++ b/test/unit/gateways/usa_epay_advanced_test.rb
@@ -199,6 +199,18 @@ def test_successful_update_customer
assert_nil response.authorization
end
+ def test_successful_quick_update_customer
+ @gateway.expects(:ssl_post).returns(successful_customer_response('quickUpdateCustomer'))
+
+ assert response = @gateway.quick_update_customer({customer_number: @options[:customer_number], update_data: @customer_options})
+ assert_instance_of Response, response
+ assert response.test?
+ assert_success response
+ assert_equal 'true', response.params['quick_update_customer_return']
+ assert_equal 'true', response.message
+ assert_nil response.authorization
+ end
+
def test_successful_enable_customer
@options.merge!(@standard_transaction_options)
@gateway.expects(:ssl_post).returns(successful_customer_response('enableCustomer'))
diff --git a/test/unit/gateways/usa_epay_transaction_test.rb b/test/unit/gateways/usa_epay_transaction_test.rb
index fda1226a1c8..b427560ad1a 100644
--- a/test/unit/gateways/usa_epay_transaction_test.rb
+++ b/test/unit/gateways/usa_epay_transaction_test.rb
@@ -7,6 +7,7 @@ def setup
@gateway = UsaEpayTransactionGateway.new(:login => 'LOGIN')
@credit_card = credit_card('4242424242424242')
+ @check = check
@options = {
:billing_address => address,
:shipping_address => address
@@ -43,6 +44,15 @@ def test_successful_request
assert response.test?
end
+ def test_successful_request_with_echeck
+ @gateway.expects(:ssl_post).returns(successful_purchase_response_echeck)
+
+ response = @gateway.purchase(@amount, @check, @options)
+ assert_success response
+ assert_equal '133134803', response.authorization
+ assert response.test?
+ end
+
def test_unsuccessful_request
@gateway.expects(:ssl_post).returns(unsuccessful_purchase_response)
@@ -174,6 +184,15 @@ def test_successful_refund_request
assert response.test?
end
+ def test_successful_refund_request_with_echeck
+ @gateway.expects(:ssl_post).returns(successful_refund_response_echeck)
+
+ response = @gateway.refund(@amount, '65074409', @options)
+ assert_success response
+ assert_equal '133134926', response.authorization
+ assert response.test?
+ end
+
def test_successful_refund_passing_extra_info
response = stub_comms do
@gateway.refund(@amount, '65074409', @options)
@@ -202,6 +221,15 @@ def test_successful_void_request
assert response.test?
end
+ def test_successful_void_request_with_echeck
+ @gateway.expects(:ssl_post).returns(successful_void_response_echeck)
+
+ response = @gateway.void('65074409', @options)
+ assert_success response
+ assert_equal '133134971', response.authorization
+ assert response.test?
+ end
+
def test_successful_void_passing_extra_info
response = stub_comms do
@gateway.void('65074409', @options.merge(:no_release => true))
@@ -374,6 +402,13 @@ def test_does_not_raise_error_on_missing_values
end
end
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ assert_equal @gateway.scrub(pre_scrubbed_track_data), post_scrubbed_track_data
+ assert_equal @gateway.scrub(pre_scrubbed_echeck), post_scrubbed_echeck
+ end
+
private
def assert_address(type, post, expected_first_name = nil, expected_last_name = nil)
@@ -436,4 +471,161 @@ def successful_refund_response
def successful_void_response
"UMversion=2.9&UMstatus=Approved&UMauthCode=&UMrefNum=63812270&UMavsResult=No%20AVS%20response%20%28Typically%20no%20AVS%20data%20sent%20or%20swiped%20transaction%29&UMavsResultCode=&UMcvv2Result=No%20CVV2%2FCVC%20data%20available%20for%20transaction.&UMcvv2ResultCode=&UMresult=A&UMvpasResultCode=&UMerror=Transaction%20Voided%20Successfully&UMerrorcode=00000&UMcustnum=&UMbatch=&UMbatchRefNum=&UMisDuplicate=N&UMconvertedAmount=&UMconvertedAmountCurrency=840&UMconversionRate=&UMcustReceiptResult=No%20Receipt%20Sent&UMprocRefNum=&UMcardLevelResult=&UMauthAmount=&UMfiller=filled"
end
+
+ def successful_purchase_response_echeck
+ "UMversion=2.9&UMstatus=Approved&UMauthCode=TMEC4D&UMrefNum=133134803&UMavsResult=No%20AVS%20response%20%28Typically%20no%20AVS%20data%20sent%20or%20swiped%20transaction%29&UMavsResultCode=&UMcvv2Result=No%20CVV2%2FCVC%20data%20available%20for%20transaction.&UMcvv2ResultCode=&UMresult=A&UMvpasResultCode=&UMerror=&UMerrorcode=00000&UMcustnum=&UMbatch=180316&UMbatchRefNum=&UMisDuplicate=N&UMconvertedAmount=&UMconvertedAmountCurrency=840&UMconversionRate=&UMcustReceiptResult=No%20Receipt%20Sent&UMprocRefNum=18031621233065&UMcardLevelResult=&UMauthAmount=&UMfiller=filled"
+ end
+
+ def successful_refund_response_echeck
+ "UMversion=2.9&UMstatus=Approved&UMauthCode=TM1E74&UMrefNum=133134926&UMavsResult=No%20AVS%20response%20%28Typically%20no%20AVS%20data%20sent%20or%20swiped%20transaction%29&UMavsResultCode=&UMcvv2Result=No%20CVV2%2FCVC%20data%20available%20for%20transaction.&UMcvv2ResultCode=&UMresult=A&UMvpasResultCode=&UMerror=&UMerrorcode=00000&UMcustnum=&UMbatch=&UMbatchRefNum=&UMisDuplicate=N&UMconvertedAmount=&UMconvertedAmountCurrency=840&UMconversionRate=&UMcustReceiptResult=No%20Receipt%20Sent&UMprocRefNum=&UMcardLevelResult=&UMauthAmount=&UMfiller=filled"
+ end
+
+ def successful_void_response_echeck
+ "UMversion=2.9&UMstatus=Approved&UMauthCode=TM80A5&UMrefNum=133134971&UMavsResult=No%20AVS%20response%20%28Typically%20no%20AVS%20data%20sent%20or%20swiped%20transaction%29&UMavsResultCode=&UMcvv2Result=No%20CVV2%2FCVC%20data%20available%20for%20transaction.&UMcvv2ResultCode=&UMresult=A&UMvpasResultCode=&UMerror=&UMerrorcode=00000&UMcustnum=&UMbatch=&UMbatchRefNum=&UMisDuplicate=N&UMconvertedAmount=&UMconvertedAmountCurrency=840&UMconversionRate=&UMcustReceiptResult=No%20Receipt%20Sent&UMprocRefNum=&UMcardLevelResult=&UMauthAmount=&UMfiller=filled"
+ end
+
+
+ def pre_scrubbed
+ <<-EOS
+opening connection to sandbox.usaepay.com:443...
+opened
+starting SSL for sandbox.usaepay.com:443...
+SSL established
+<- "POST /gate HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: sandbox.usaepay.com\r\nContent-Length: 774\r\n\r\n"
+<- "UMamount=1.00&UMinvoice=&UMdescription=&UMcard=4000100011112224&UMcvv2=123&UMexpir=0919&UMname=Longbob+Longsen&UMbillfname=Jim&UMbilllname=Smith&UMbillcompany=Widgets+Inc&UMbillstreet=456+My+Street&UMbillstreet2=Apt+1&UMbillcity=Ottawa&UMbillstate=NC&UMbillzip=27614&UMbillcountry=CA&UMbillphone=%28555%29555-5555&UMshipfname=Jim&UMshiplname=Smith&UMshipcompany=Widgets+Inc&UMshipstreet=456+My+Street&UMshipstreet2=Apt+1&UMshipcity=Ottawa&UMshipstate=ON&UMshipzip=K1C2N6&UMshipcountry=CA&UMshipphone=%28555%29555-5555&UMstreet=456+My+Street&UMzip=27614&UMcommand=cc%3Asale&UMkey=4EoZ5U2Q55j976W7eplC71i6b7kn4pcV&UMsoftware=Active+Merchant&UMtestmode=0&UMhash=s%2F5268F91058BC9F9FA944693D799F324B2497B7247850A51E53226309FB2540F0%2F7b4c4f6a4e775141cc0e4e10c0388d9adeb47fd1%2Fn"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Server: http\r\n"
+-> "Date: Tue, 13 Feb 2018 18:17:20 GMT\r\n"
+-> "Content-Type: text/html\r\n"
+-> "Content-Length: 485\r\n"
+-> "Connection: close\r\n"
+-> "P3P: policyref=\"http://www.usaepay.com/w3c/p3p.xml\", CP=\"NON TAIa IVAa IVDa OUR NOR PHY ONL UNI FIN INT DEM\"\r\n"
+-> "Strict-Transport-Security: max-age=15768000\r\n"
+-> "\r\n"
+reading 485 bytes...
+-> "UMversion=2.9&UMstatus=Approved&UMauthCode=042366&UMrefNum=132020588&UMavsResult=Address%3A%20Match%20%26%205%20Digit%20Zip%3A%20Match&UMavsResultCode=YYY&UMcvv2Result=Match&UMcvv2ResultCode=M&UMresult=A&UMvpasResultCode=&UMerror=Approved&UMerrorcode=00000&UMcustnum=&UMbatch=120&UMbatchRefNum=848&UMisDuplicate=N&UMconvertedAmount=&UMconvertedAmountCurrency=840&UMconversionRate=&UMcustReceiptResult=No%20Receipt%20Sent&UMprocRefNum=&UMcardLevelResult=A&UMauthAmount=1&UMfiller=filled"
+read 485 bytes
+Conn close
+ EOS
+ end
+
+ def post_scrubbed
+ <<-EOS
+opening connection to sandbox.usaepay.com:443...
+opened
+starting SSL for sandbox.usaepay.com:443...
+SSL established
+<- "POST /gate HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: sandbox.usaepay.com\r\nContent-Length: 774\r\n\r\n"
+<- "UMamount=1.00&UMinvoice=&UMdescription=&UMcard=[FILTERED]&UMcvv2=[FILTERED]&UMexpir=0919&UMname=Longbob+Longsen&UMbillfname=Jim&UMbilllname=Smith&UMbillcompany=Widgets+Inc&UMbillstreet=456+My+Street&UMbillstreet2=Apt+1&UMbillcity=Ottawa&UMbillstate=NC&UMbillzip=27614&UMbillcountry=CA&UMbillphone=%28555%29555-5555&UMshipfname=Jim&UMshiplname=Smith&UMshipcompany=Widgets+Inc&UMshipstreet=456+My+Street&UMshipstreet2=Apt+1&UMshipcity=Ottawa&UMshipstate=ON&UMshipzip=K1C2N6&UMshipcountry=CA&UMshipphone=%28555%29555-5555&UMstreet=456+My+Street&UMzip=27614&UMcommand=cc%3Asale&UMkey=[FILTERED]&UMsoftware=Active+Merchant&UMtestmode=0&UMhash=s%2F5268F91058BC9F9FA944693D799F324B2497B7247850A51E53226309FB2540F0%2F7b4c4f6a4e775141cc0e4e10c0388d9adeb47fd1%2Fn"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Server: http\r\n"
+-> "Date: Tue, 13 Feb 2018 18:17:20 GMT\r\n"
+-> "Content-Type: text/html\r\n"
+-> "Content-Length: 485\r\n"
+-> "Connection: close\r\n"
+-> "P3P: policyref=\"http://www.usaepay.com/w3c/p3p.xml\", CP=\"NON TAIa IVAa IVDa OUR NOR PHY ONL UNI FIN INT DEM\"\r\n"
+-> "Strict-Transport-Security: max-age=15768000\r\n"
+-> "\r\n"
+reading 485 bytes...
+-> "UMversion=2.9&UMstatus=Approved&UMauthCode=042366&UMrefNum=132020588&UMavsResult=Address%3A%20Match%20%26%205%20Digit%20Zip%3A%20Match&UMavsResultCode=YYY&UMcvv2Result=Match&UMcvv2ResultCode=M&UMresult=A&UMvpasResultCode=&UMerror=Approved&UMerrorcode=00000&UMcustnum=&UMbatch=120&UMbatchRefNum=848&UMisDuplicate=N&UMconvertedAmount=&UMconvertedAmountCurrency=840&UMconversionRate=&UMcustReceiptResult=No%20Receipt%20Sent&UMprocRefNum=&UMcardLevelResult=A&UMauthAmount=1&UMfiller=filled"
+read 485 bytes
+Conn close
+ EOS
+ end
+
+ def pre_scrubbed_track_data
+ <<-EOS
+opening connection to sandbox.usaepay.com:443...
+opened
+starting SSL for sandbox.usaepay.com:443...
+SSL established
+<- "POST /gate HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: sandbox.usaepay.com\r\nContent-Length: 382\r\n\r\n"
+<- "UMamount=1.00&UMinvoice=&UMdescription=&UMmagstripe=%25B4000100011112224%5ELONGSEN%2FL.+%5E19091200000000000000%2A%2A123%2A%2A%2A%2A%2A%2A%3F&UMcardpresent=true&UMcommand=cc%3Asale&UMkey=4EoZ5U2Q55j976W7eplC71i6b7kn4pcV&UMsoftware=Active+Merchant&UMtestmode=0&UMhash=s%2FE27734F076643B23131E5432C1E225EFF982A73D350179EFC2F191CA499B59A4%2F13391bd14ab6e61058cc9a1b78f259a4c26aa8e1%2Fn"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Server: http\r\n"
+-> "Date: Tue, 13 Feb 2018 18:13:11 GMT\r\n"
+-> "Content-Type: text/html\r\n"
+-> "Content-Length: 485\r\n"
+-> "Connection: close\r\n"
+-> "P3P: policyref=\"http://www.usaepay.com/w3c/p3p.xml\", CP=\"NON TAIa IVAa IVDa OUR NOR PHY ONL UNI FIN INT DEM\"\r\n"
+-> "Strict-Transport-Security: max-age=15768000\r\n"
+-> "\r\n"
+reading 485 bytes...
+-> "UMversion=2.9&UMstatus=Approved&UMauthCode=042087&UMrefNum=132020522&UMavsResult=Address%3A%20Match%20%26%205%20Digit%20Zip%3A%20Match&UMavsResultCode=YYY&UMcvv2Result=Match&UMcvv2ResultCode=M&UMresult=A&UMvpasResultCode=&UMerror=Approved&UMerrorcode=00000&UMcustnum=&UMbatch=120&UMbatchRefNum=848&UMisDuplicate=N&UMconvertedAmount=&UMconvertedAmountCurrency=840&UMconversionRate=&UMcustReceiptResult=No%20Receipt%20Sent&UMprocRefNum=&UMcardLevelResult=A&UMauthAmount=1&UMfiller=filled"
+read 485 bytes
+Conn close
+ EOS
+ end
+
+ def post_scrubbed_track_data
+ <<-EOS
+opening connection to sandbox.usaepay.com:443...
+opened
+starting SSL for sandbox.usaepay.com:443...
+SSL established
+<- "POST /gate HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: sandbox.usaepay.com\r\nContent-Length: 382\r\n\r\n"
+<- "UMamount=1.00&UMinvoice=&UMdescription=&UMmagstripe=[FILTERED]&UMcardpresent=true&UMcommand=cc%3Asale&UMkey=[FILTERED]&UMsoftware=Active+Merchant&UMtestmode=0&UMhash=s%2FE27734F076643B23131E5432C1E225EFF982A73D350179EFC2F191CA499B59A4%2F13391bd14ab6e61058cc9a1b78f259a4c26aa8e1%2Fn"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Server: http\r\n"
+-> "Date: Tue, 13 Feb 2018 18:13:11 GMT\r\n"
+-> "Content-Type: text/html\r\n"
+-> "Content-Length: 485\r\n"
+-> "Connection: close\r\n"
+-> "P3P: policyref=\"http://www.usaepay.com/w3c/p3p.xml\", CP=\"NON TAIa IVAa IVDa OUR NOR PHY ONL UNI FIN INT DEM\"\r\n"
+-> "Strict-Transport-Security: max-age=15768000\r\n"
+-> "\r\n"
+reading 485 bytes...
+-> "UMversion=2.9&UMstatus=Approved&UMauthCode=042087&UMrefNum=132020522&UMavsResult=Address%3A%20Match%20%26%205%20Digit%20Zip%3A%20Match&UMavsResultCode=YYY&UMcvv2Result=Match&UMcvv2ResultCode=M&UMresult=A&UMvpasResultCode=&UMerror=Approved&UMerrorcode=00000&UMcustnum=&UMbatch=120&UMbatchRefNum=848&UMisDuplicate=N&UMconvertedAmount=&UMconvertedAmountCurrency=840&UMconversionRate=&UMcustReceiptResult=No%20Receipt%20Sent&UMprocRefNum=&UMcardLevelResult=A&UMauthAmount=1&UMfiller=filled"
+read 485 bytes
+Conn close
+ EOS
+ end
+
+ def pre_scrubbed_echeck
+ <<-EOS
+opening connection to sandbox.usaepay.com:443...
+opened
+starting SSL for sandbox.usaepay.com:443...
+SSL established
+<- "POST /gate HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: sandbox.usaepay.com\r\nContent-Length: 762\r\n\r\n"
+<- "UMamount=1.00&UMinvoice=&UMdescription=&UMaccount=15378535&UMrouting=244183602&UMname=Jim+Smith&UMbillfname=Jim&UMbilllname=Smith&UMbillcompany=Widgets+Inc&UMbillstreet=456+My+Street&UMbillstreet2=Apt+1&UMbillcity=Ottawa&UMbillstate=NC&UMbillzip=27614&UMbillcountry=CA&UMbillphone=%28555%29555-5555&UMshipfname=Jim&UMshiplname=Smith&UMshipcompany=Widgets+Inc&UMshipstreet=456+My+Street&UMshipstreet2=Apt+1&UMshipcity=Ottawa&UMshipstate=ON&UMshipzip=K1C2N6&UMshipcountry=CA&UMshipphone=%28555%29555-5555&UMstreet=456+My+Street&UMzip=27614&UMcommand=check%3Asale&UMkey=4EoZ5U2Q55j976W7eplC71i6b7kn4pcV&UMsoftware=Active+Merchant&UMtestmode=0&UMhash=s%2F7F71E7DCB851901EA1D4E2CA1C60D2A7E8BAB99FA10F6220E821BD8B8331114B%2F85f1a7ab01b725c4eed80a12c78ef65d3fa367e6%2Fn"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Server: http\r\n"
+-> "Date: Fri, 16 Mar 2018 20:54:49 GMT\r\n"
+-> "Content-Type: text/html\r\n"
+-> "Content-Length: 572\r\n"
+-> "Connection: close\r\n"
+-> "P3P: policyref=\"http://www.usaepay.com/w3c/p3p.xml\", CP=\"NON TAIa IVAa IVDa OUR NOR PHY ONL UNI FIN INT DEM\"\r\n"
+-> "Strict-Transport-Security: max-age=15768000\r\n"
+-> "\r\n"
+reading 572 bytes...
+-> "UMversion=2.9&UMstatus=Approved&UMauthCode=TMEAAF&UMrefNum=133135121&UMavsResult=No%20AVS%20response%20%28Typically%20no%20AVS%20data%20sent%20or%20swiped%20transaction%29&UMavsResultCode=&UMcvv2Result=No%20CVV2%2FCVC%20data%20available%20for%20transaction.&UMcvv2ResultCode=&UMresult=A&UMvpasResultCode=&UMerror=&UMerrorcode=00000&UMcustnum=&UMbatch=180316&UMbatchRefNum=&UMisDuplicate=N&UMconvertedAmount=&UMconvertedAmountCurrency=840&UMconversionRate=&UMcustReceiptResult=No%20Receipt%20Sent&UMprocRefNum=18031621233689&UMcardLevelResult=&UMauthAmount=&UMfiller=filled"
+read 572 bytes
+Conn close
+ EOS
+ end
+
+ def post_scrubbed_echeck
+ <<-EOS
+opening connection to sandbox.usaepay.com:443...
+opened
+starting SSL for sandbox.usaepay.com:443...
+SSL established
+<- "POST /gate HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: sandbox.usaepay.com\r\nContent-Length: 762\r\n\r\n"
+<- "UMamount=1.00&UMinvoice=&UMdescription=&UMaccount=[FILTERED]&UMrouting=244183602&UMname=Jim+Smith&UMbillfname=Jim&UMbilllname=Smith&UMbillcompany=Widgets+Inc&UMbillstreet=456+My+Street&UMbillstreet2=Apt+1&UMbillcity=Ottawa&UMbillstate=NC&UMbillzip=27614&UMbillcountry=CA&UMbillphone=%28555%29555-5555&UMshipfname=Jim&UMshiplname=Smith&UMshipcompany=Widgets+Inc&UMshipstreet=456+My+Street&UMshipstreet2=Apt+1&UMshipcity=Ottawa&UMshipstate=ON&UMshipzip=K1C2N6&UMshipcountry=CA&UMshipphone=%28555%29555-5555&UMstreet=456+My+Street&UMzip=27614&UMcommand=check%3Asale&UMkey=[FILTERED]&UMsoftware=Active+Merchant&UMtestmode=0&UMhash=s%2F7F71E7DCB851901EA1D4E2CA1C60D2A7E8BAB99FA10F6220E821BD8B8331114B%2F85f1a7ab01b725c4eed80a12c78ef65d3fa367e6%2Fn"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Server: http\r\n"
+-> "Date: Fri, 16 Mar 2018 20:54:49 GMT\r\n"
+-> "Content-Type: text/html\r\n"
+-> "Content-Length: 572\r\n"
+-> "Connection: close\r\n"
+-> "P3P: policyref=\"http://www.usaepay.com/w3c/p3p.xml\", CP=\"NON TAIa IVAa IVDa OUR NOR PHY ONL UNI FIN INT DEM\"\r\n"
+-> "Strict-Transport-Security: max-age=15768000\r\n"
+-> "\r\n"
+reading 572 bytes...
+-> "UMversion=2.9&UMstatus=Approved&UMauthCode=TMEAAF&UMrefNum=133135121&UMavsResult=No%20AVS%20response%20%28Typically%20no%20AVS%20data%20sent%20or%20swiped%20transaction%29&UMavsResultCode=&UMcvv2Result=No%20CVV2%2FCVC%20data%20available%20for%20transaction.&UMcvv2ResultCode=&UMresult=A&UMvpasResultCode=&UMerror=&UMerrorcode=00000&UMcustnum=&UMbatch=180316&UMbatchRefNum=&UMisDuplicate=N&UMconvertedAmount=&UMconvertedAmountCurrency=840&UMconversionRate=&UMcustReceiptResult=No%20Receipt%20Sent&UMprocRefNum=18031621233689&UMcardLevelResult=&UMauthAmount=&UMfiller=filled"
+read 572 bytes
+Conn close
+ EOS
+ end
end
diff --git a/test/unit/gateways/vacaypay_test.rb b/test/unit/gateways/vacaypay_test.rb
new file mode 100644
index 00000000000..34543629908
--- /dev/null
+++ b/test/unit/gateways/vacaypay_test.rb
@@ -0,0 +1,577 @@
+require 'test_helper'
+
+class VacaypayTest < Test::Unit::TestCase
+ include CommStub
+
+ def setup
+ @gateway = VacaypayGateway.new(
+ :api_key => 'SGD0qydBXp58i0n5QHnTG38D-OOzvDu0KlVliOhZpyw',
+ :account_uuid => '0b72d273-5caf-4a4d-aaf3-3c18267e213e',
+ :publishable_key => 'pk_test_rIQe8LhRJGCutDnRGtJADcm2'
+ )
+ @gateway_without_publishable_key = VacaypayGateway.new(
+ :api_key => 'SGD0qydBXp58i0n5QHnTG38D-OOzvDu0KlVliOhZpyw',
+ :account_uuid => '0b72d273-5caf-4a4d-aaf3-3c18267e213e'
+ )
+ @credit_card = credit_card
+ @amount = 10000
+
+ @options = {
+ :currency => "USD",
+ :description => 'ActiveMerchant Test Purchase',
+ :email => 'wow@example.com',
+ :ip => '127.0.0.1',
+ :first_name => 'Longbob',
+ :last_name => 'Longsen'
+ }
+ end
+
+ def test_successful_purchase
+ @gateway.expects(:ssl_post).twice.returns(successful_purchase_response)
+
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_instance_of Response, response
+ assert_success response
+
+ assert_equal '7f8228fe-090e-477f-a365-4dc5c5204ba2', response.authorization
+ assert response.test?
+ end
+
+ def test_successful_purchase_without_publishable_key
+ @gateway_without_publishable_key.expects(:ssl_get).returns(successful_account_details_response)
+ @gateway_without_publishable_key.expects(:ssl_post).twice.returns(successful_purchase_response)
+
+ assert response = @gateway_without_publishable_key.purchase(@amount, @credit_card, @options)
+ assert_instance_of Response, response
+ assert_success response
+
+ assert_equal '7f8228fe-090e-477f-a365-4dc5c5204ba2', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_purchase
+ @gateway.expects(:ssl_post).twice.returns(failed_purchase_response)
+
+ response = @gateway.purchase(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal Gateway::STANDARD_ERROR_CODE[:card_declined], response.error_code
+ end
+
+ def test_successful_authorize
+ @gateway.expects(:ssl_post).twice.returns(successful_authorize_response)
+
+ assert response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_instance_of Response, response
+ assert_success response
+
+ assert_equal '7f8228fe-090e-477f-a365-4dc5c5204ba2', response.authorization
+ assert_equal false, response.params['data']['captured']
+ assert response.test?
+ end
+
+ def test_successful_authorize_with_publishable_key
+ @gateway_without_publishable_key.expects(:ssl_get).returns(successful_account_details_response)
+ @gateway_without_publishable_key.expects(:ssl_post).twice.returns(successful_authorize_response)
+
+ assert response = @gateway_without_publishable_key.authorize(@amount, @credit_card, @options)
+ assert_instance_of Response, response
+ assert_success response
+
+ assert_equal '7f8228fe-090e-477f-a365-4dc5c5204ba2', response.authorization
+ assert_equal false, response.params['data']['captured']
+ assert response.test?
+ end
+
+ def test_failed_authorize
+ @gateway.expects(:ssl_post).twice.returns(failed_authorize_response)
+
+ response = @gateway.authorize(@amount, @credit_card, @options)
+ assert_failure response
+ assert_equal Gateway::STANDARD_ERROR_CODE[:card_declined], response.error_code
+ end
+
+ def test_successful_capture
+ @gateway.expects(:ssl_post).returns(successful_capture_response)
+
+ response = @gateway.capture(@amount, '7f8228fe-090e-477f-a365-4dc5c5204ba2', @options)
+
+ assert_equal '7f8228fe-090e-477f-a365-4dc5c5204ba2', response.authorization
+ assert_equal true, response.params['data']['captured']
+ assert response.test?
+ end
+
+ def test_failed_capture
+ @gateway.expects(:ssl_post).returns(failed_capture_response)
+
+ response = @gateway.capture(@amount, '7f8228fe-090e-477f-a365-4dc5c5204ba2', @options)
+ assert_failure response
+ end
+
+ def test_successful_refund
+ @gateway.expects(:ssl_post).returns(successful_refund_response)
+
+ response = @gateway.refund(@amount, 'fbdff46c-893e-4498-8fe7-734903f40de2', @options)
+
+ assert_equal 'fbdff46c-893e-4498-8fe7-734903f40de2', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_refund
+ @gateway.expects(:ssl_post).returns(failed_refund_response)
+
+ response = @gateway.refund(@amount, '7f8228fe-090e-477f-a365-4dc5c5204ba2', @options)
+ assert_failure response
+ end
+
+ def test_successful_void
+ @gateway.expects(:ssl_post).returns(successful_void_response)
+
+ response = @gateway.void('6cc5a2ab-41ab-47cb-b68f-b038188b4bab', @options)
+
+ assert_equal '6cc5a2ab-41ab-47cb-b68f-b038188b4bab', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_void
+ @gateway.expects(:ssl_post).returns(failed_void_response)
+
+ response = @gateway.void('bad-auth', @options)
+ assert_failure response
+ end
+
+ def test_successful_verify
+ response = stub_comms(@gateway, :ssl_request) do
+ @gateway.verify(@credit_card, @options)
+ end.respond_with(successful_authorize_response, successful_void_response)
+ assert_success response
+ end
+
+ def test_successful_store
+ @gateway.expects(:ssl_post).returns(successful_store_response)
+
+ response = @gateway.store(@credit_card, @options)
+
+ assert_success response
+ assert_equal 'tok_18oAlrE7T9LsrPBuR4e7UkVz', response.authorization
+ assert response.test?
+ end
+
+ def test_failed_store
+ @gateway.expects(:ssl_post).returns(failed_store_response)
+
+ response = @gateway.store(@credit_card, @options)
+ assert_failure response
+ end
+
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
+ private
+
+ def pre_scrubbed
+ %q(
+opening connection to www.procuro.io:443...
+opened
+starting SSL for www.procuro.io:443...
+SSL established
+<- "POST /api/v1/vacay-pay/accounts/0b72d273-5caf-4a4d-aaf3-3c18267e213e/payments HTTP/1.1\r\nContent-Type: application/json\r\nX-Auth-Token: SGD0qydBXp58i0n5QHnTG38D-OOzvDu0KlVliOhZpyw\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: www.procuro.io\r\nContent-Length: 404\r\n\r\n"
+<- "{\"amount\":\"100.00\",\"currency\":\"USD\",\"card\":{\"name\":\"Longbob Longsen\",\"number\":\"4242424242424242\",\"cvv\":\"123\",\"expiryYear\":\"2017\",\"expiryMonth\":\"09\"},\"email\":\"wow@example.com\",\"firstName\":\"Longbob\",\"lastName\":\"Longsen\",\"description\":\"ActiveMerchant Test Purchase\",\"externalPaymentReference\":\"\",\"externalBookingReference\":\"\",\"accessingIp\":\"127.0.0.1\",\"notes\":\"\",\"metadata\":{},\"sendEmailConfirmation\":false}"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Thu, 18 Aug 2016 12:04:20 GMT\r\n"
+-> "Content-Type: application/json\r\n"
+-> "Transfer-Encoding: chunked\r\n"
+-> "Connection: close\r\n"
+-> "Set-Cookie: __cfduid=d25328a3daf701746033da24edffb67ea1471521856; expires=Fri, 18-Aug-17 12:04:16 GMT; path=/; domain=.procuro.io; HttpOnly\r\n"
+-> "Access-Control-Allow-Headers: origin, content-type, accept, x-auth-token, x-provider-token\r\n"
+-> "Access-Control-Allow-Methods: POST, GET, PUT, DELETE, PATCH, OPTIONS\r\n"
+-> "Access-Control-Allow-Origin: *\r\n"
+-> "Cache-Control: no-cache\r\n"
+-> "Strict-Transport-Security: max-age=0; preload\r\n"
+-> "X-Content-Type-Options: nosniff\r\n"
+-> "Server: cloudflare-nginx\r\n"
+-> "CF-RAY: 2d453a35a6b7350c-LHR\r\n"
+-> "Content-Encoding: gzip\r\n"
+)
+ end
+
+ def post_scrubbed
+ %q(
+opening connection to www.procuro.io:443...
+opened
+starting SSL for www.procuro.io:443...
+SSL established
+<- "POST /api/v1/vacay-pay/accounts/0b72d273-5caf-4a4d-aaf3-3c18267e213e/payments HTTP/1.1\r\nContent-Type: application/json\r\nX-Auth-Token: [FILTERED]\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: www.procuro.io\r\nContent-Length: 404\r\n\r\n"
+<- "{\"amount\":\"100.00\",\"currency\":\"USD\",\"card\":{\"name\":\"Longbob Longsen\",\[FILTERED],\[FILTERED],\"expiryYear\":\"2017\",\"expiryMonth\":\"09\"},\"email\":\"wow@example.com\",\"firstName\":\"Longbob\",\"lastName\":\"Longsen\",\"description\":\"ActiveMerchant Test Purchase\",\"externalPaymentReference\":\"\",\"externalBookingReference\":\"\",\"accessingIp\":\"127.0.0.1\",\"notes\":\"\",\"metadata\":{},\"sendEmailConfirmation\":false}"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Thu, 18 Aug 2016 12:04:20 GMT\r\n"
+-> "Content-Type: application/json\r\n"
+-> "Transfer-Encoding: chunked\r\n"
+-> "Connection: close\r\n"
+-> "Set-Cookie: __cfduid=d25328a3daf701746033da24edffb67ea1471521856; expires=Fri, 18-Aug-17 12:04:16 GMT; path=/; domain=.procuro.io; HttpOnly\r\n"
+-> "Access-Control-Allow-Headers: origin, content-type, accept, x-auth-token, x-provider-token\r\n"
+-> "Access-Control-Allow-Methods: POST, GET, PUT, DELETE, PATCH, OPTIONS\r\n"
+-> "Access-Control-Allow-Origin: *\r\n"
+-> "Cache-Control: no-cache\r\n"
+-> "Strict-Transport-Security: max-age=0; preload\r\n"
+-> "X-Content-Type-Options: nosniff\r\n"
+-> "Server: cloudflare-nginx\r\n"
+-> "CF-RAY: 2d453a35a6b7350c-LHR\r\n"
+-> "Content-Encoding: gzip\r\n"
+)
+ end
+
+ def successful_account_details_response
+ %q(
+ {
+ "appCode": 0,
+ "appMessage": "",
+ "meta": [],
+ "data": {
+ "uuid": "2760dd7a-34f4-4278-9e3c-d86f46041fbc",
+ "accountName": "Example Account",
+ "accountType": "vacaypay",
+ "publishableKey": "pk_test_rIQe8LhRJGCutDnRGtJADcm2",
+ "statementDescriptor": "JANE DOE RENTALS",
+ "tradingName": "Jane Doe Rentals",
+ "defaultCurrency": "GBP",
+ "allowedCurrencies": ["GBP", "EUR"],
+ "country": "GB",
+ "email": "janedoe@gmail.com",
+ "firstName": "Jane",
+ "lastName": "Doe",
+ "bookingTermsUrl": "https://www.procuro.io/terms-and-conditions",
+ "enabled": true,
+ "verified": true,
+ "vacayPayApproved": true,
+ "canTakePayments": true,
+ "createdAt": "2015-05-20 09:30:10 UTC",
+ "updatedAt": "2015-05-21 09:35:24 UTC"
+ }
+ }
+ )
+ end
+
+ def successful_purchase_response
+ %q(
+ {
+ "appCode":0,
+ "appMessage":"",
+ "meta":{
+ },
+ "data":{
+ "paymentUuid":"7f8228fe-090e-477f-a365-4dc5c5204ba2",
+ "accountUuid":"0b72d273-5caf-4a4d-aaf3-3c18267e213e",
+ "amount":100,
+ "currency":"USD",
+ "financial":{
+ "currency":"GBP",
+ "total":74.66,
+ "net":72.29,
+ "fees":2.37
+ },
+ "refundedAmount":0,
+ "status":"succeeded",
+ "refunded":false,
+ "captured":true,
+ "paymentReference":"F364DB96",
+ "externalPaymentReference":"",
+ "externalBookingReference":"",
+ "description":"ActiveMerchant Test Purchase",
+ "email":"wow@example.com",
+ "firstName":"Longbob",
+ "lastName":"Longsen",
+ "createdAt":"2016-08-19 09:34:13 UTC",
+ "updatedAt":"2016-08-19 09:34:13 UTC",
+ "meta":[
+ ]
+ }
+ }
+ )
+ end
+
+ def failed_purchase_response
+ %q(
+ {
+ "appCode":6,
+ "appMessage":"The request failed for some reason, check the errors in meta",
+ "meta":{
+ "errors":[
+ "Your card was declined."
+ ]
+ },
+ "data":{
+ "code":"card_declined",
+ "message":"Your card was declined."
+ }
+ }
+ )
+ end
+
+ def successful_authorize_response
+ %q(
+ {
+ "appCode":0,
+ "appMessage":"",
+ "meta":{
+ },
+ "data":{
+ "paymentUuid":"7f8228fe-090e-477f-a365-4dc5c5204ba2",
+ "accountUuid":"0b72d273-5caf-4a4d-aaf3-3c18267e213e",
+ "amount":100,
+ "currency":"USD",
+ "financial":{
+ "currency":"GBP",
+ "total":74.66,
+ "net":72.29,
+ "fees":2.37
+ },
+ "refundedAmount":0,
+ "status":"succeeded",
+ "refunded":false,
+ "captured":false,
+ "paymentReference":"F364DB96",
+ "externalPaymentReference":"",
+ "externalBookingReference":"",
+ "description":"ActiveMerchant Test Purchase",
+ "email":"wow@example.com",
+ "firstName":"Longbob",
+ "lastName":"Longsen",
+ "createdAt":"2016-08-19 09:34:13 UTC",
+ "updatedAt":"2016-08-19 09:34:13 UTC",
+ "meta":[
+ ]
+ }
+ }
+ )
+ end
+
+ def failed_authorize_response
+ %q(
+ {
+ "appCode":6,
+ "appMessage":"The request failed for some reason, check the errors in meta",
+ "meta":{
+ "errors":[
+ "Your card was declined."
+ ]
+ },
+ "data":{
+ "code":"card_declined",
+ "message":"Your card was declined."
+ }
+ }
+ )
+ end
+
+ def successful_capture_response
+ %q(
+ {
+ "appCode":0,
+ "appMessage":"",
+ "meta":{
+ },
+ "data":{
+ "paymentUuid":"7f8228fe-090e-477f-a365-4dc5c5204ba2",
+ "accountUuid":"0b72d273-5caf-4a4d-aaf3-3c18267e213e",
+ "amount":100,
+ "currency":"USD",
+ "financial":{
+ "currency":"GBP",
+ "total":74.66,
+ "net":72.29,
+ "fees":2.37
+ },
+ "refundedAmount":0,
+ "status":"succeeded",
+ "refunded":false,
+ "captured":true,
+ "paymentReference":"F364DB96",
+ "externalPaymentReference":"",
+ "externalBookingReference":"",
+ "description":"ActiveMerchant Test Purchase",
+ "email":"wow@example.com",
+ "firstName":"Longbob",
+ "lastName":"Longsen",
+ "createdAt":"2016-08-19 09:34:13 UTC",
+ "updatedAt":"2016-08-19 09:34:13 UTC",
+ "meta":[
+ ]
+ }
+ }
+ )
+ end
+
+ def failed_capture_response
+ %q(
+ {
+ "appCode":2,
+ "appMessage":"Specified resource not found",
+ "meta":{
+ "resource":"vacay-pay\/account\/payment"
+ },
+ "data":{
+ }
+ }
+ )
+ end
+
+ def successful_refund_response
+ %q(
+ {
+ "appCode":0,
+ "appMessage":"",
+ "meta":{
+ },
+ "data":{
+ "paymentUuid":"fbdff46c-893e-4498-8fe7-734903f40de2",
+ "accountUuid":"0b72d273-5caf-4a4d-aaf3-3c18267e213e",
+ "amount":100,
+ "currency":"USD",
+ "financial":{
+ "currency":"GBP",
+ "total":74.7,
+ "net":72.33,
+ "fees":2.37
+ },
+ "refundedAmount":100,
+ "status":"succeeded",
+ "refunded":true,
+ "captured":true,
+ "paymentReference":"WYMQ1R2Q",
+ "externalPaymentReference":"",
+ "externalBookingReference":"",
+ "description":"ActiveMerchant Test Purchase",
+ "email":"wow@example.com",
+ "firstName":"Longbob",
+ "lastName":"Longsen",
+ "createdAt":"2016-08-19 10:22:17 UTC",
+ "updatedAt":"2016-08-19 10:18:01 UTC",
+ "meta":[
+ ]
+ }
+ }
+ )
+ end
+
+ def failed_refund_response
+ %q(
+ {
+ "appCode":6,
+ "appMessage":"The request failed for some reason, check the errors in meta",
+ "meta":{
+ "errors":[
+ "Cannot refund a value less than 0, or higher than the amount refundable (100)."
+ ]
+ },
+ "data":{
+ }
+ }
+ )
+ end
+
+ def successful_void_response
+ %q(
+ {
+ "appCode":0,
+ "appMessage":"",
+ "meta":{
+ },
+ "data":{
+ "paymentUuid":"6cc5a2ab-41ab-47cb-b68f-b038188b4bab",
+ "accountUuid":"0b72d273-5caf-4a4d-aaf3-3c18267e213e",
+ "amount":100,
+ "currency":"USD",
+ "financial":{
+ "currency":"USD",
+ "total":100,
+ "net":0,
+ "fees":0
+ },
+ "refundedAmount":100,
+ "status":"succeeded",
+ "refunded":true,
+ "captured":false,
+ "paymentReference":"PGD3M97R",
+ "externalPaymentReference":"",
+ "externalBookingReference":"",
+ "description":"ActiveMerchant Test Purchase",
+ "email":"wow@example.com",
+ "firstName":"Longbob",
+ "lastName":"Longsen",
+ "createdAt":"2016-08-19 10:19:11 UTC",
+ "updatedAt":"2016-08-19 10:23:33 UTC",
+ "meta":[
+ ]
+ }
+ }
+ )
+ end
+
+ def failed_void_response
+ %q(
+ {
+ "appCode":6,
+ "appMessage":"The request failed for some reason, check the errors in meta",
+ "meta":{
+ "errors":[
+ "Cannot refund a value less than 0, or higher than the amount refundable (100)."
+ ]
+ },
+ "data":{
+ }
+ }
+ )
+ end
+
+ def successful_store_response
+ %q(
+ {
+ "id": "tok_18oAlrE7T9LsrPBuR4e7UkVz",
+ "object": "token",
+ "card": {
+ "id": "card_18oAlrE7T9LsrPBuJZ6UJXNE",
+ "object": "card",
+ "address_city": null,
+ "address_country": null,
+ "address_line1": null,
+ "address_line1_check": null,
+ "address_line2": null,
+ "address_state": null,
+ "address_zip": null,
+ "address_zip_check": null,
+ "brand": "Visa",
+ "country": "US",
+ "cvc_check": "unchecked",
+ "dynamic_last4": null,
+ "exp_month": 9,
+ "exp_year": 2017,
+ "funding": "credit",
+ "last4": "4242",
+ "metadata": {},
+ "name": null,
+ "tokenization_method": null
+ },
+ "client_ip": "31.217.176.123",
+ "created": 1472557875,
+ "livemode": false,
+ "type": "card",
+ "used": false
+ }
+ )
+ end
+
+ def failed_store_response
+ %q(
+ {
+ "error": {
+ "message": "You must provide a value for 'cvc'.",
+ "type": "card_error",
+ "param": "cvc",
+ "code": "invalid_cvc"
+ }
+ }
+ )
+ end
+end
diff --git a/test/unit/gateways/wepay_test.rb b/test/unit/gateways/wepay_test.rb
index a4915d5d912..8f64d00b984 100644
--- a/test/unit/gateways/wepay_test.rb
+++ b/test/unit/gateways/wepay_test.rb
@@ -1,6 +1,8 @@
require 'test_helper'
class WepayTest < Test::Unit::TestCase
+ include CommStub
+
def setup
@gateway = WepayGateway.new(
client_id: 'client_id',
@@ -17,32 +19,32 @@ def setup
end
def test_successful_purchase
- @gateway.expects(:ssl_post).at_most(2).returns(successful_purchase_response)
+ @gateway.expects(:ssl_post).at_most(3).returns(successful_capture_response)
response = @gateway.purchase(@amount, @credit_card, @options)
assert_success response
- assert_equal "1117213582|200.00", response.authorization
+ assert_equal "1181910285|20.00", response.authorization
end
def test_failed_purchase
- @gateway.expects(:ssl_post).at_most(2).returns(failed_purchase_response)
+ @gateway.expects(:ssl_post).at_most(2).returns(failed_capture_response)
response = @gateway.purchase(@amount, @credit_card, @options)
assert_failure response
end
def test_successful_purchase_with_token
- @gateway.expects(:ssl_post).at_most(2).returns(successful_purchase_response)
+ @gateway.expects(:ssl_post).at_most(2).returns(successful_capture_response)
response = @gateway.purchase(@amount, "1422891921", @options)
assert_success response
- assert_equal "1117213582|200.00", response.authorization
+ assert_equal "1181910285|20.00", response.authorization
end
def test_failed_purchase_with_token
- @gateway.expects(:ssl_post).at_most(2).returns(failed_purchase_response)
+ @gateway.expects(:ssl_post).at_most(2).returns(failed_capture_response)
response = @gateway.purchase(@amount, "1422891921", @options)
assert_failure response
@@ -75,7 +77,7 @@ def test_failed_authorize
response = @gateway.authorize(@amount, @credit_card, @options)
assert_failure response
- assert_equal "checkout type parameter must be either GOODS, SERVICE, DONATION, or PERSONAL", response.message
+ assert_equal "Invalid credit card number", response.message
end
def test_successful_authorize_with_token
@@ -90,7 +92,7 @@ def test_failed_authorize_with_token
response = @gateway.authorize(@amount, "1422891921", @options)
assert_failure response
- assert_equal "checkout type parameter must be either GOODS, SERVICE, DONATION, or PERSONAL", response.message
+ assert_equal "Invalid credit card number", response.message
end
def test_successful_capture
@@ -101,9 +103,9 @@ def test_successful_capture
end
def test_failed_capture
- @gateway.expects(:ssl_post).at_most(2).returns(failed_capture_response)
+ @gateway.expects(:ssl_post).at_most(3).returns(failed_capture_response)
- response = @gateway.capture("auth|amount", @options)
+ response = @gateway.capture(@amount, "auth|200.00", @options)
assert_failure response
assert_equal "Checkout object must be in state 'Reserved' to capture. Currently it is in state captured", response.message
end
@@ -123,7 +125,7 @@ def test_failed_void
assert_equal "this checkout has already been cancelled", response.message
end
- def test_successful_store
+ def test_successful_store_via_create
@gateway.expects(:ssl_post).returns(successful_store_response)
response = @gateway.store(@credit_card, @options)
@@ -131,6 +133,14 @@ def test_successful_store
assert_equal "3322208138", response.authorization
end
+ def test_successful_store_via_transfer
+ @gateway.expects(:ssl_post).returns(successful_store_response)
+
+ response = @gateway.store(@credit_card, @options.merge(recurring: true))
+ assert_success response
+ assert_equal "3322208138", response.authorization
+ end
+
def test_failed_store
@gateway.expects(:ssl_post).returns(failed_store_response)
@@ -147,22 +157,237 @@ def test_invalid_json_response
assert_match(/Invalid JSON response received from WePay/, response.message)
end
+ def test_no_version_by_default
+ stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options)
+ end.check_request do |endpoint, data, headers|
+ assert_no_match(/Api-Version/, headers.to_s)
+ end.respond_with(successful_authorize_response)
+ end
+
+ def test_version_override
+ stub_comms do
+ @gateway.purchase(@amount, @credit_card, @options.merge(version: '2017-05-31'))
+ end.check_request do |endpoint, data, headers|
+ assert_match(/"Api-Version\"=>\"2017-05-31\"/, headers.to_s)
+ end.respond_with(successful_authorize_response)
+ end
+
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ end
+
private
- def successful_store_response
- %({"credit_card_id": 3322208138,"state": "new"})
+ def pre_scrubbed
+ %q(
+ opening connection to stage.wepayapi.com:443...
+ opened
+ starting SSL for stage.wepayapi.com:443...
+ SSL established
+ <- "POST /v2/credit_card/create HTTP/1.1\r\nContent-Type: application/json\r\nUser-Agent: ActiveMerchantBindings/1.65.0\r\nAuthorization: Bearer STAGE_c91882b0bed3584b8aed0f7f515f2f05a1d40924ee6f394ce82d91018cb0f2d3\r\nApi-Version: 2017-02-01\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nConnection: close\r\nHost: stage.wepayapi.com\r\nContent-Length: 272\r\n\r\n"
+ <- "{\"client_id\":\"44716\",\"user_name\":\"Longbob Longsen\",\"email\":\"test@example.com\",\"cc_number\":\"5496198584584769\",\"cvv\":\"123\",\"expiration_month\":9,\"expiration_year\":2018,\"address\":{\"address1\":\"456 My Street\",\"city\":\"Ottawa\",\"country\":\"CA\",\"region\":\"ON\",\"postal_code\":\"K1C2N6\"}}"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "Server: nginx\r\n"
+ -> "Content-Type: application/json\r\n"
+ -> "X-Content-Type-Options: nosniff\r\n"
+ -> "Strict-Transport-Security: max-age=31536000; preload\r\n"
+ -> "Transfer-Encoding: chunked\r\n"
+ -> "Accept-Ranges: bytes\r\n"
+ -> "Date: Wed, 26 Apr 2017 18:27:33 GMT\r\n"
+ -> "Via: 1.1 varnish\r\n"
+ -> "Connection: close\r\n"
+ -> "X-Served-By: cache-fra1231-FRA\r\n"
+ -> "X-Cache: MISS\r\n"
+ -> "X-Cache-Hits: 0\r\n"
+ -> "X-Timer: S1493231252.436069,VS0,VE1258\r\n"
+ -> "Vary: Authorization\r\n"
+ -> "\r\n"
+ -> "2b\r\n"
+ reading 43 bytes...
+ -> "{\"credit_card_id\":2559797807,\"state\":\"new\"}"
+ read 43 bytes
+ reading 2 bytes...
+ -> "\r\n"
+ read 2 bytes
+ -> "0\r\n"
+ -> "\r\n"
+ Conn close
+ opening connection to stage.wepayapi.com:443...
+ opened
+ starting SSL for stage.wepayapi.com:443...
+ SSL established
+ <- "POST /v2/checkout/create HTTP/1.1\r\nContent-Type: application/json\r\nUser-Agent: ActiveMerchantBindings/1.65.0\r\nAuthorization: Bearer STAGE_c91882b0bed3584b8aed0f7f515f2f05a1d40924ee6f394ce82d91018cb0f2d3\r\nApi-Version: 2017-02-01\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nConnection: close\r\nHost: stage.wepayapi.com\r\nContent-Length: 202\r\n\r\n"
+ <- "{\"payment_method\":{\"type\":\"credit_card\",\"credit_card\":{\"id\":\"2559797807\",\"auto_capture\":false}},\"account_id\":\"2080478981\",\"amount\":\"20.00\",\"short_description\":\"Purchase\",\"type\":\"goods\",\"currency\":\"USD\"}"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "Server: nginx\r\n"
+ -> "Content-Type: application/json\r\n"
+ -> "X-Content-Type-Options: nosniff\r\n"
+ -> "Strict-Transport-Security: max-age=31536000; preload\r\n"
+ -> "Transfer-Encoding: chunked\r\n"
+ -> "Accept-Ranges: bytes\r\n"
+ -> "Date: Wed, 26 Apr 2017 18:27:36 GMT\r\n"
+ -> "Via: 1.1 varnish\r\n"
+ -> "Connection: close\r\n"
+ -> "X-Served-By: cache-fra1247-FRA\r\n"
+ -> "X-Cache: MISS\r\n"
+ -> "X-Cache-Hits: 0\r\n"
+ -> "X-Timer: S1493231255.546126,VS0,VE1713\r\n"
+ -> "Vary: Authorization\r\n"
+ -> "\r\n"
+ -> "324\r\n"
+ reading 804 bytes...
+ -> "{\"checkout_id\":1709862829,\"account_id\":2080478981,\"type\":\"goods\",\"short_description\":\"Purchase\",\"currency\":\"USD\",\"amount\":20,\"state\":\"authorized\",\"soft_descriptor\":\"WPY*Spreedly\",\"create_time\":1493231254,\"gross\":20.88,\"reference_id\":null,\"callback_uri\":null,\"long_description\":null,\"delivery_type\":null,\"fee\":{\"app_fee\":0,\"processing_fee\":0.88,\"fee_payer\":\"payer\"},\"chargeback\":{\"amount_charged_back\":0,\"dispute_uri\":null},\"refund\":{\"amount_refunded\":0,\"refund_reason\":null},\"payment_method\":{\"type\":\"credit_card\",\"credit_card\":{\"id\":2559797807,\"data\":{\"emv_receipt\":null,\"signature_url\":null},\"auto_capture\":false}},\"hosted_checkout\":null,\"payer\":{\"email\":\"test@example.com\",\"name\":\"Longbob Longsen\",\"home_address\":null},\"npo_information\":null,\"payment_error\":null,\"in_review\":false,\"auto_release\":true}"
+ read 804 bytes
+ reading 2 bytes...
+ -> "\r\n"
+ read 2 bytes
+ -> "0\r\n"
+ -> "\r\n"
+ Conn close
+ opening connection to stage.wepayapi.com:443...
+ opened
+ starting SSL for stage.wepayapi.com:443...
+ SSL established
+ <- "POST /v2/checkout/capture HTTP/1.1\r\nContent-Type: application/json\r\nUser-Agent: ActiveMerchantBindings/1.65.0\r\nAuthorization: Bearer STAGE_c91882b0bed3584b8aed0f7f515f2f05a1d40924ee6f394ce82d91018cb0f2d3\r\nApi-Version: 2017-02-01\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nConnection: close\r\nHost: stage.wepayapi.com\r\nContent-Length: 28\r\n\r\n"
+ <- "{\"checkout_id\":\"1709862829\"}"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "Server: nginx\r\n"
+ -> "Content-Type: application/json\r\n"
+ -> "X-Content-Type-Options: nosniff\r\n"
+ -> "Strict-Transport-Security: max-age=31536000; preload\r\n"
+ -> "Transfer-Encoding: chunked\r\n"
+ -> "Accept-Ranges: bytes\r\n"
+ -> "Date: Wed, 26 Apr 2017 18:27:38 GMT\r\n"
+ -> "Via: 1.1 varnish\r\n"
+ -> "Connection: close\r\n"
+ -> "X-Served-By: cache-fra1239-FRA\r\n"
+ -> "X-Cache: MISS\r\n"
+ -> "X-Cache-Hits: 0\r\n"
+ -> "X-Timer: S1493231257.113609,VS0,VE1136\r\n"
+ -> "Vary: Authorization\r\n"
+ -> "\r\n"
+ -> "324\r\n"
+ reading 804 bytes...
+ -> "{\"checkout_id\":1709862829,\"account_id\":2080478981,\"type\":\"goods\",\"short_description\":\"Purchase\",\"currency\":\"USD\",\"amount\":20,\"state\":\"authorized\",\"soft_descriptor\":\"WPY*Spreedly\",\"create_time\":1493231254,\"gross\":20.88,\"reference_id\":null,\"callback_uri\":null,\"long_description\":null,\"delivery_type\":null,\"fee\":{\"app_fee\":0,\"processing_fee\":0.88,\"fee_payer\":\"payer\"},\"chargeback\":{\"amount_charged_back\":0,\"dispute_uri\":null},\"refund\":{\"amount_refunded\":0,\"refund_reason\":null},\"payment_method\":{\"type\":\"credit_card\",\"credit_card\":{\"id\":2559797807,\"data\":{\"emv_receipt\":null,\"signature_url\":null},\"auto_capture\":false}},\"hosted_checkout\":null,\"payer\":{\"email\":\"test@example.com\",\"name\":\"Longbob Longsen\",\"home_address\":null},\"npo_information\":null,\"payment_error\":null,\"in_review\":false,\"auto_release\":true}"
+ read 804 bytes
+ reading 2 bytes...
+ -> "\r\n"
+ read 2 bytes
+ -> "0\r\n"
+ -> "\r\n"
+ Conn close
+ )
end
- def failed_store_response
- %({"error": "invalid_request","error_description": "Invalid credit card number","error_code": 1003})
+ def post_scrubbed
+ %q(
+ opening connection to stage.wepayapi.com:443...
+ opened
+ starting SSL for stage.wepayapi.com:443...
+ SSL established
+ <- "POST /v2/credit_card/create HTTP/1.1\r\nContent-Type: application/json\r\nUser-Agent: ActiveMerchantBindings/1.65.0\r\nAuthorization: Bearer [FILTERED]\r\nApi-Version: 2017-02-01\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nConnection: close\r\nHost: stage.wepayapi.com\r\nContent-Length: 272\r\n\r\n"
+ <- "{\"client_id\":\"44716\",\"user_name\":\"Longbob Longsen\",\"email\":\"test@example.com\",\"cc_number\":\"[FILTERED]\",\"cvv\":\"[FILTERED]\",\"expiration_month\":9,\"expiration_year\":2018,\"address\":{\"address1\":\"456 My Street\",\"city\":\"Ottawa\",\"country\":\"CA\",\"region\":\"ON\",\"postal_code\":\"K1C2N6\"}}"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "Server: nginx\r\n"
+ -> "Content-Type: application/json\r\n"
+ -> "X-Content-Type-Options: nosniff\r\n"
+ -> "Strict-Transport-Security: max-age=31536000; preload\r\n"
+ -> "Transfer-Encoding: chunked\r\n"
+ -> "Accept-Ranges: bytes\r\n"
+ -> "Date: Wed, 26 Apr 2017 18:27:33 GMT\r\n"
+ -> "Via: 1.1 varnish\r\n"
+ -> "Connection: close\r\n"
+ -> "X-Served-By: cache-fra1231-FRA\r\n"
+ -> "X-Cache: MISS\r\n"
+ -> "X-Cache-Hits: 0\r\n"
+ -> "X-Timer: S1493231252.436069,VS0,VE1258\r\n"
+ -> "Vary: Authorization\r\n"
+ -> "\r\n"
+ -> "2b\r\n"
+ reading 43 bytes...
+ -> "{\"credit_card_id\":2559797807,\"state\":\"new\"}"
+ read 43 bytes
+ reading 2 bytes...
+ -> "\r\n"
+ read 2 bytes
+ -> "0\r\n"
+ -> "\r\n"
+ Conn close
+ opening connection to stage.wepayapi.com:443...
+ opened
+ starting SSL for stage.wepayapi.com:443...
+ SSL established
+ <- "POST /v2/checkout/create HTTP/1.1\r\nContent-Type: application/json\r\nUser-Agent: ActiveMerchantBindings/1.65.0\r\nAuthorization: Bearer [FILTERED]\r\nApi-Version: 2017-02-01\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nConnection: close\r\nHost: stage.wepayapi.com\r\nContent-Length: 202\r\n\r\n"
+ <- "{\"payment_method\":{\"type\":\"credit_card\",\"credit_card\":{\"id\":\"2559797807\",\"auto_capture\":false}},\"account_id\":\"2080478981\",\"amount\":\"20.00\",\"short_description\":\"Purchase\",\"type\":\"goods\",\"currency\":\"USD\"}"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "Server: nginx\r\n"
+ -> "Content-Type: application/json\r\n"
+ -> "X-Content-Type-Options: nosniff\r\n"
+ -> "Strict-Transport-Security: max-age=31536000; preload\r\n"
+ -> "Transfer-Encoding: chunked\r\n"
+ -> "Accept-Ranges: bytes\r\n"
+ -> "Date: Wed, 26 Apr 2017 18:27:36 GMT\r\n"
+ -> "Via: 1.1 varnish\r\n"
+ -> "Connection: close\r\n"
+ -> "X-Served-By: cache-fra1247-FRA\r\n"
+ -> "X-Cache: MISS\r\n"
+ -> "X-Cache-Hits: 0\r\n"
+ -> "X-Timer: S1493231255.546126,VS0,VE1713\r\n"
+ -> "Vary: Authorization\r\n"
+ -> "\r\n"
+ -> "324\r\n"
+ reading 804 bytes...
+ -> "{\"checkout_id\":1709862829,\"account_id\":2080478981,\"type\":\"goods\",\"short_description\":\"Purchase\",\"currency\":\"USD\",\"amount\":20,\"state\":\"authorized\",\"soft_descriptor\":\"WPY*Spreedly\",\"create_time\":1493231254,\"gross\":20.88,\"reference_id\":null,\"callback_uri\":null,\"long_description\":null,\"delivery_type\":null,\"fee\":{\"app_fee\":0,\"processing_fee\":0.88,\"fee_payer\":\"payer\"},\"chargeback\":{\"amount_charged_back\":0,\"dispute_uri\":null},\"refund\":{\"amount_refunded\":0,\"refund_reason\":null},\"payment_method\":{\"type\":\"credit_card\",\"credit_card\":{\"id\":2559797807,\"data\":{\"emv_receipt\":null,\"signature_url\":null},\"auto_capture\":false}},\"hosted_checkout\":null,\"payer\":{\"email\":\"test@example.com\",\"name\":\"Longbob Longsen\",\"home_address\":null},\"npo_information\":null,\"payment_error\":null,\"in_review\":false,\"auto_release\":true}"
+ read 804 bytes
+ reading 2 bytes...
+ -> "\r\n"
+ read 2 bytes
+ -> "0\r\n"
+ -> "\r\n"
+ Conn close
+ opening connection to stage.wepayapi.com:443...
+ opened
+ starting SSL for stage.wepayapi.com:443...
+ SSL established
+ <- "POST /v2/checkout/capture HTTP/1.1\r\nContent-Type: application/json\r\nUser-Agent: ActiveMerchantBindings/1.65.0\r\nAuthorization: Bearer [FILTERED]\r\nApi-Version: 2017-02-01\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nConnection: close\r\nHost: stage.wepayapi.com\r\nContent-Length: 28\r\n\r\n"
+ <- "{\"checkout_id\":\"1709862829\"}"
+ -> "HTTP/1.1 200 OK\r\n"
+ -> "Server: nginx\r\n"
+ -> "Content-Type: application/json\r\n"
+ -> "X-Content-Type-Options: nosniff\r\n"
+ -> "Strict-Transport-Security: max-age=31536000; preload\r\n"
+ -> "Transfer-Encoding: chunked\r\n"
+ -> "Accept-Ranges: bytes\r\n"
+ -> "Date: Wed, 26 Apr 2017 18:27:38 GMT\r\n"
+ -> "Via: 1.1 varnish\r\n"
+ -> "Connection: close\r\n"
+ -> "X-Served-By: cache-fra1239-FRA\r\n"
+ -> "X-Cache: MISS\r\n"
+ -> "X-Cache-Hits: 0\r\n"
+ -> "X-Timer: S1493231257.113609,VS0,VE1136\r\n"
+ -> "Vary: Authorization\r\n"
+ -> "\r\n"
+ -> "324\r\n"
+ reading 804 bytes...
+ -> "{\"checkout_id\":1709862829,\"account_id\":2080478981,\"type\":\"goods\",\"short_description\":\"Purchase\",\"currency\":\"USD\",\"amount\":20,\"state\":\"authorized\",\"soft_descriptor\":\"WPY*Spreedly\",\"create_time\":1493231254,\"gross\":20.88,\"reference_id\":null,\"callback_uri\":null,\"long_description\":null,\"delivery_type\":null,\"fee\":{\"app_fee\":0,\"processing_fee\":0.88,\"fee_payer\":\"payer\"},\"chargeback\":{\"amount_charged_back\":0,\"dispute_uri\":null},\"refund\":{\"amount_refunded\":0,\"refund_reason\":null},\"payment_method\":{\"type\":\"credit_card\",\"credit_card\":{\"id\":2559797807,\"data\":{\"emv_receipt\":null,\"signature_url\":null},\"auto_capture\":false}},\"hosted_checkout\":null,\"payer\":{\"email\":\"test@example.com\",\"name\":\"Longbob Longsen\",\"home_address\":null},\"npo_information\":null,\"payment_error\":null,\"in_review\":false,\"auto_release\":true}"
+ read 804 bytes
+ reading 2 bytes...
+ -> "\r\n"
+ read 2 bytes
+ -> "0\r\n"
+ -> "\r\n"
+ Conn close
+ )
end
- def successful_purchase_response
- %({"checkout_id":1117213582,"checkout_uri":"https://stage.wepay.com/api/checkout/1117213582/974ff0c0"})
+ def successful_store_response
+ %({"credit_card_id": 3322208138,"state": "new"})
end
- def failed_purchase_response
- %({"error":"access_denied","error_description":"invalid account_id, account does not exist or does not belong to user","error_code":3002})
+ def failed_store_response
+ %({"error": "invalid_request","error_description": "Invalid credit card number","error_code": 1003})
end
def successful_refund_response
@@ -182,15 +407,15 @@ def failed_void_response
end
def successful_authorize_response
- %({"checkout_id":640816095,"checkout_uri":"https://stage.wepay.com/api/checkout/640816095/974ff0c0"})
+ %({\"checkout_id\":1181910285,\"account_id\":2080478981,\"type\":\"goods\",\"short_description\":\"Purchase\",\"currency\":\"USD\",\"amount\":20,\"state\":\"authorized\",\"soft_descriptor\":\"WPY*Spreedly\",\"create_time\":1481836590,\"gross\":20.88,\"reference_id\":null,\"callback_uri\":null,\"long_description\":null,\"delivery_type\":null,\"fee\":{\"app_fee\":0,\"processing_fee\":0.88,\"fee_payer\":\"payer\"},\"chargeback\":{\"amount_charged_back\":0,\"dispute_uri\":null},\"refund\":{\"amount_refunded\":0,\"refund_reason\":null},\"payment_method\":{\"type\":\"credit_card\",\"credit_card\":{\"id\":1929540809,\"data\":{\"emv_receipt\":null,\"signature_url\":null},\"auto_capture\":false}},\"hosted_checkout\":null,\"payer\":{\"email\":\"test@example.com\",\"name\":\"Longbob Longsen\",\"home_address\":null},\"npo_information\":null,\"payment_error\":null,\"in_review\":false,\"auto_release\":true})
end
def failed_authorize_response
- %({"error":"invalid_request","error_description":"checkout type parameter must be either GOODS, SERVICE, DONATION, or PERSONAL","error_code":1003})
+ %({\"error\":\"invalid_request\",\"error_description\":\"Invalid credit card number\",\"error_code\":1003})
end
def successful_capture_response
- %({"checkout_id":1852898602,"state":"captured"})
+ %({\"checkout_id\":1181910285,\"account_id\":2080478981,\"type\":\"goods\",\"short_description\":\"Purchase\",\"currency\":\"USD\",\"amount\":20,\"state\":\"authorized\",\"soft_descriptor\":\"WPY*Spreedly\",\"create_time\":1481836590,\"gross\":20.88,\"reference_id\":null,\"callback_uri\":null,\"long_description\":null,\"delivery_type\":null,\"fee\":{\"app_fee\":0,\"processing_fee\":0.88,\"fee_payer\":\"payer\"},\"chargeback\":{\"amount_charged_back\":0,\"dispute_uri\":null},\"refund\":{\"amount_refunded\":0,\"refund_reason\":null},\"payment_method\":{\"type\":\"credit_card\",\"credit_card\":{\"id\":1929540809,\"data\":{\"emv_receipt\":null,\"signature_url\":null},\"auto_capture\":false}},\"hosted_checkout\":null,\"payer\":{\"email\":\"test@example.com\",\"name\":\"Longbob Longsen\",\"home_address\":null},\"npo_information\":null,\"payment_error\":null,\"in_review\":false,\"auto_release\":true})
end
def failed_capture_response
diff --git a/test/unit/gateways/worldpay_test.rb b/test/unit/gateways/worldpay_test.rb
index d1e990c5a66..35d18b69a5e 100644
--- a/test/unit/gateways/worldpay_test.rb
+++ b/test/unit/gateways/worldpay_test.rb
@@ -44,10 +44,20 @@ def test_successful_reference_transaction_authorize_with_merchant_code
assert_equal 'R50704213207145707', response.authorization
end
+ def test_authorize_passes_ip_and_session_id
+ response = stub_comms do
+ @gateway.authorize(@amount, @credit_card, @options.merge(ip: '127.0.0.1', session_id: '0215ui8ib1'))
+ end.check_request do |endpoint, data, headers|
+ assert_match(//, data)
+ end.respond_with(successful_authorize_response)
+ assert_success response
+ end
+
def test_failed_authorize
response = stub_comms do
@gateway.authorize(@amount, @credit_card, @options)
end.respond_with(failed_authorize_response)
+ assert_equal '7', response.error_code
assert_failure response
end
@@ -122,6 +132,14 @@ def test_successful_refund_for_settled_payment
assert_equal "05d9f8c622553b1df1fe3a145ce91ccf", response.params['refund_received_order_code']
end
+ def test_successful_refund_for_settled_by_merchant_payment
+ response = stub_comms do
+ @gateway.refund(@amount, @options[:order_id], @options)
+ end.respond_with(successful_refund_inquiry_response('SETTLED_BY_MERCHANT'), successful_refund_response)
+ assert_success response
+ assert_equal "05d9f8c622553b1df1fe3a145ce91ccf", response.params['refund_received_order_code']
+ end
+
def test_refund_fails_unless_status_is_captured
response = stub_comms do
@gateway.refund(@amount, @options[:order_id], @options)
@@ -130,6 +148,14 @@ def test_refund_fails_unless_status_is_captured
assert_equal "A transaction status of 'CAPTURED' or 'SETTLED' or 'SETTLED_BY_MERCHANT' is required.", response.message
end
+ def test_full_refund_for_unsettled_payment_forces_void
+ response = stub_comms do
+ @gateway.refund(@amount, @options[:order_id], @options.merge(force_full_refund_if_unsettled: true))
+ end.respond_with(failed_refund_inquiry_response, failed_refund_inquiry_response, successful_void_response)
+ assert_success response
+ assert "cancel", response.responses.last.params["action"]
+ end
+
def test_capture
response = stub_comms do
response = @gateway.authorize(@amount, @credit_card, @options)
@@ -138,6 +164,16 @@ def test_capture
assert_success response
end
+ def test_successful_credit
+ response = stub_comms do
+ @gateway.credit(@amount, @credit_card, @options)
+ end.check_request do |endpoint, data, headers|
+ assert_match(//, data)
+ end.respond_with(successful_credit_response)
+ assert_success response
+ assert_equal '3d4187536044bd39ad6a289c4339c41c', response.authorization
+ end
+
def test_description
stub_comms do
@gateway.authorize(@amount, @credit_card, @options)
@@ -191,12 +227,20 @@ def test_amount_handling
def test_currency_exponent_handling
stub_comms do
- @gateway.authorize(100, @credit_card, @options.merge(currency: :JPY))
+ @gateway.authorize(10000, @credit_card, @options.merge(currency: :JPY))
end.check_request do |endpoint, data, headers|
assert_tag_with_attributes 'amount',
{'value' => '100', 'exponent' => '0', 'currencyCode' => 'JPY'},
data
end.respond_with(successful_authorize_response)
+
+ stub_comms do
+ @gateway.authorize(10000, @credit_card, @options.merge(currency: :OMR))
+ end.check_request do |endpoint, data, headers|
+ assert_tag_with_attributes 'amount',
+ {'value' => '10000', 'exponent' => '3', 'currencyCode' => 'OMR'},
+ data
+ end.respond_with(successful_authorize_response)
end
def test_address_handling
@@ -261,15 +305,17 @@ def test_no_address_specified
stub_comms do
@gateway.authorize(100, @credit_card, @options)
end.check_request do |endpoint, data, headers|
+ assert_no_match %r(cardAddress), data
+ assert_no_match %r(address), data
assert_no_match %r(firstName), data
assert_no_match %r(lastName), data
+ assert_no_match %r(address1), data
assert_no_match %r(address2), data
+ assert_no_match %r(postalCode), data
+ assert_no_match %r(city), data
+ assert_no_match %r(state), data
+ assert_no_match %r(countryCode), data
assert_no_match %r(telephoneNumber), data
- assert_match %r(N/A), data
- assert_match %r(N/A), data
- assert_match %r(0000), data
- assert_match %r(N/A), data
- assert_match %r(US), data
end.respond_with(successful_authorize_response)
end
@@ -396,7 +442,7 @@ def assert_tag_with_attributes(tag, attributes, string)
end
def test_successful_verify
- @gateway.expects(:ssl_post).times(3).returns(successful_authorize_response, successful_void_response)
+ @gateway.expects(:ssl_post).times(2).returns(successful_authorize_response, successful_void_response)
response = @gateway.verify(@credit_card, @options)
assert_success response
@@ -668,6 +714,23 @@ def failed_void_response
REQUEST
end
+ def successful_credit_response
+ <<-RESPONSE
+
+
+
+
+
+
+
+
+
+
+
+ RESPONSE
+ end
+
def sample_authorization_request
<<-REQUEST
diff --git a/test/unit/gateways/worldpay_us_test.rb b/test/unit/gateways/worldpay_us_test.rb
index 63b0ea6ab8c..55797ebbe2b 100644
--- a/test/unit/gateways/worldpay_us_test.rb
+++ b/test/unit/gateways/worldpay_us_test.rb
@@ -175,6 +175,21 @@ def test_empty_response_fails
assert_equal "Unable to read error message", response.message
end
+ def test_backup_url
+ response = stub_comms(@gateway) do
+ @gateway.purchase(@amount, @credit_card, use_backup_url: true)
+ end.check_request do |endpoint, data, headers|
+ assert_equal WorldpayUsGateway.backup_url, endpoint
+ end.respond_with(successful_purchase_response)
+ assert_success response
+ end
+
+ def test_scrub
+ assert @gateway.supports_scrubbing?
+ assert_equal @gateway.scrub(pre_scrubbed), post_scrubbed
+ assert_equal @gateway.scrub(pre_scrubbed_check), post_scrubbed_check
+ end
+
private
def successful_purchase_response
@@ -419,4 +434,96 @@ def failed_void_response
transid=0
)
end
+
+ def pre_scrubbed
+ <<-EOS
+opening connection to trans.worldpay.us:443...
+opened
+starting SSL for trans.worldpay.us:443...
+SSL established
+<- "POST /cgi-bin/process.cgi HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: trans.worldpay.us\r\nContent-Length: 425\r\n\r\n"
+<- "acctid=MPNAB&action=ns_quicksale_cc&amount=1.00&ccname=Longbob+Longsen&ccnum=4446661234567892&ci_billaddr1=456+My+Street&ci_billaddr2=Apt+1&ci_billcity=Ottawa&ci_billcountry=CA&ci_billstate=ON&ci_billzip=K1C2N6&ci_companyname=Widgets+Inc&ci_email=&ci_ipaddress=&ci_phone=%28555%29555-5555¤cycode=USD&cvv2=987&expmon=09&expyear=2019&merchantordernumber=67f4f20082e79684f036f25dafe96304&merchantpin=1234567890&subid=SPREE"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Tue, 13 Feb 2018 19:28:27 GMT\r\n"
+-> "Server: Apache\r\n"
+-> "X-Frame-Options: SAMEORIGIN\r\n"
+-> "Content-Type: text/html;charset=ISO-8859-1\r\n"
+-> "Content-Length: 962\r\n"
+-> "Connection: close\r\n"
+-> "\r\n"
+reading 962 bytes...
+-> "\r\nAccepted=SALE:036586:477::919067116:N::N\r\nhistoryid=919067116\r\norderid=722189706\r\nAccepted=SALE:036586:477::919067116:N::N\r\nACCOUNTNUMBER=************7892\r\nACCTID=MPNAB\r\nauthcode=036586\r\nAuthNo=SALE:036586:477::919067116:N::N\r\nAVS_RESULT=N\r\nBATCHNUMBER=\r\nCVV2_RESULT=N\r\nDEBIT_TRACE_NUMBER=\r\nENTRYMETHOD=M\r\nhistoryid=919067116\r\nMERCHANT_DBA_ADDR=11121 Willows Road NE\r\nMERCHANT_DBA_CITY=Redmond\r\nMERCHANT_DBA_NAME=Merchant Partners\r\nMERCHANT_DBA_PHONE=4254979909\r\nMERCHANT_DBA_STATE=WA\r\nMERCHANTID=542929804946788\r\nMERCHANTORDERNUMBER=67f4f20082e79684f036f25dafe96304\r\norderid=722189706\r\nPAYTYPE=Visa\r\nPRODUCT_DESCRIPTION=\r\nReason=\r\nRECEIPT_FOOTER=Thank You\r\nrecurid=0\r\nrefcode=919067116-036586\r\nresult=1\r\nSEQUENCE_NUMBER=370609730\r\nStatus=Accepted\r\nSUBID=SPREE\r\nSYSTEMAUDITTRACENUMBER=477\r\nTERMINALID=160551\r\nTRANSGUID=d5701d57-9147-4ded-b596-6805581f081c:266\r\ntransid=370609730\r\ntransresult=APPROVED\r\nVISATRANSACTIONID=088044000036586\r\n"
+read 962 bytes
+Conn close
+ EOS
+ end
+
+ def post_scrubbed
+ <<-EOS
+opening connection to trans.worldpay.us:443...
+opened
+starting SSL for trans.worldpay.us:443...
+SSL established
+<- "POST /cgi-bin/process.cgi HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: trans.worldpay.us\r\nContent-Length: 425\r\n\r\n"
+<- "acctid=MPNAB&action=ns_quicksale_cc&amount=1.00&ccname=Longbob+Longsen&ccnum=[FILTERED]&ci_billaddr1=456+My+Street&ci_billaddr2=Apt+1&ci_billcity=Ottawa&ci_billcountry=CA&ci_billstate=ON&ci_billzip=K1C2N6&ci_companyname=Widgets+Inc&ci_email=&ci_ipaddress=&ci_phone=%28555%29555-5555¤cycode=USD&cvv2=[FILTERED]&expmon=09&expyear=2019&merchantordernumber=67f4f20082e79684f036f25dafe96304&merchantpin=[FILTERED]&subid=SPREE"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Tue, 13 Feb 2018 19:28:27 GMT\r\n"
+-> "Server: Apache\r\n"
+-> "X-Frame-Options: SAMEORIGIN\r\n"
+-> "Content-Type: text/html;charset=ISO-8859-1\r\n"
+-> "Content-Length: 962\r\n"
+-> "Connection: close\r\n"
+-> "\r\n"
+reading 962 bytes...
+-> "\r\nAccepted=SALE:036586:477::919067116:N::N\r\nhistoryid=919067116\r\norderid=722189706\r\nAccepted=SALE:036586:477::919067116:N::N\r\nACCOUNTNUMBER=************7892\r\nACCTID=MPNAB\r\nauthcode=036586\r\nAuthNo=SALE:036586:477::919067116:N::N\r\nAVS_RESULT=N\r\nBATCHNUMBER=\r\nCVV2_RESULT=N\r\nDEBIT_TRACE_NUMBER=\r\nENTRYMETHOD=M\r\nhistoryid=919067116\r\nMERCHANT_DBA_ADDR=11121 Willows Road NE\r\nMERCHANT_DBA_CITY=Redmond\r\nMERCHANT_DBA_NAME=Merchant Partners\r\nMERCHANT_DBA_PHONE=4254979909\r\nMERCHANT_DBA_STATE=WA\r\nMERCHANTID=542929804946788\r\nMERCHANTORDERNUMBER=67f4f20082e79684f036f25dafe96304\r\norderid=722189706\r\nPAYTYPE=Visa\r\nPRODUCT_DESCRIPTION=\r\nReason=\r\nRECEIPT_FOOTER=Thank You\r\nrecurid=0\r\nrefcode=919067116-036586\r\nresult=1\r\nSEQUENCE_NUMBER=370609730\r\nStatus=Accepted\r\nSUBID=SPREE\r\nSYSTEMAUDITTRACENUMBER=477\r\nTERMINALID=160551\r\nTRANSGUID=d5701d57-9147-4ded-b596-6805581f081c:266\r\ntransid=370609730\r\ntransresult=APPROVED\r\nVISATRANSACTIONID=088044000036586\r\n"
+read 962 bytes
+Conn close
+ EOS
+ end
+
+ def pre_scrubbed_check
+ <<-EOS
+opening connection to trans.worldpay.us:443...
+opened
+starting SSL for trans.worldpay.us:443...
+SSL established
+<- "POST /cgi-bin/process.cgi HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: trans.worldpay.us\r\nContent-Length: 412\r\n\r\n"
+<- "acctid=MPNAB&action=ns_quicksale_check&amount=1.00&ci_billaddr1=456+My+Street&ci_billaddr2=Apt+1&ci_billcity=Ottawa&ci_billcountry=CA&ci_billstate=ON&ci_billzip=K1C2N6&ci_companyname=Widgets+Inc&ci_email=&ci_ipaddress=&ci_phone=%28555%29555-5555&ckaba=244183602&ckacct=15378535&ckaccttype=1&ckno=12345654321¤cycode=USD&merchantordernumber=5ec80ff8210dc9d24248ac2777d6b4f3&merchantpin=1234567890&subid=SPREE"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Tue, 13 Feb 2018 19:29:38 GMT\r\n"
+-> "Server: Apache\r\n"
+-> "X-Frame-Options: SAMEORIGIN\r\n"
+-> "Content-Type: text/html;charset=ISO-8859-1\r\n"
+-> "Content-Length: 414\r\n"
+-> "Connection: close\r\n"
+-> "\r\n"
+reading 414 bytes...
+-> "\r\nDeclined=DECLINED:1103180001:Invalid Bank:\r\nhistoryid=919060608\r\norderid=722196666\r\nACCOUNTNUMBER=****8535\r\nDeclined=DECLINED:1103180001:Invalid Bank:\r\nENTRYMETHOD=KEYED\r\nhistoryid=919060608\r\nMERCHANTORDERNUMBER=5ec80ff8210dc9d24248ac2777d6b4f3\r\norderid=722196666\r\nPAYTYPE=Check\r\nrcode=1103180001\r\nReason=DECLINED:1103180001:Invalid Bank:\r\nrecurid=0\r\nresult=0\r\nStatus=Declined\r\ntransid=0\r\n"
+read 414 bytes
+Conn close
+ EOS
+ end
+
+ def post_scrubbed_check
+ <<-EOS
+opening connection to trans.worldpay.us:443...
+opened
+starting SSL for trans.worldpay.us:443...
+SSL established
+<- "POST /cgi-bin/process.cgi HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: trans.worldpay.us\r\nContent-Length: 412\r\n\r\n"
+<- "acctid=MPNAB&action=ns_quicksale_check&amount=1.00&ci_billaddr1=456+My+Street&ci_billaddr2=Apt+1&ci_billcity=Ottawa&ci_billcountry=CA&ci_billstate=ON&ci_billzip=K1C2N6&ci_companyname=Widgets+Inc&ci_email=&ci_ipaddress=&ci_phone=%28555%29555-5555&ckaba=244183602&ckacct=[FILTERED]&ckaccttype=1&ckno=12345654321¤cycode=USD&merchantordernumber=5ec80ff8210dc9d24248ac2777d6b4f3&merchantpin=[FILTERED]&subid=SPREE"
+-> "HTTP/1.1 200 OK\r\n"
+-> "Date: Tue, 13 Feb 2018 19:29:38 GMT\r\n"
+-> "Server: Apache\r\n"
+-> "X-Frame-Options: SAMEORIGIN\r\n"
+-> "Content-Type: text/html;charset=ISO-8859-1\r\n"
+-> "Content-Length: 414\r\n"
+-> "Connection: close\r\n"
+-> "\r\n"
+reading 414 bytes...
+-> "\r\nDeclined=DECLINED:1103180001:Invalid Bank:\r\nhistoryid=919060608\r\norderid=722196666\r\nACCOUNTNUMBER=****8535\r\nDeclined=DECLINED:1103180001:Invalid Bank:\r\nENTRYMETHOD=KEYED\r\nhistoryid=919060608\r\nMERCHANTORDERNUMBER=5ec80ff8210dc9d24248ac2777d6b4f3\r\norderid=722196666\r\nPAYTYPE=Check\r\nrcode=1103180001\r\nReason=DECLINED:1103180001:Invalid Bank:\r\nrecurid=0\r\nresult=0\r\nStatus=Declined\r\ntransid=0\r\n"
+read 414 bytes
+Conn close
+ EOS
+ end
end
diff --git a/test/unit/network_tokenization_credit_card_test.rb b/test/unit/network_tokenization_credit_card_test.rb
index 30a6ec95557..c864240d91e 100644
--- a/test/unit/network_tokenization_credit_card_test.rb
+++ b/test/unit/network_tokenization_credit_card_test.rb
@@ -14,6 +14,9 @@ def setup
@tokenized_android_pay_card = ActiveMerchant::Billing::NetworkTokenizationCreditCard.new({
source: :android_pay
})
+ @tokenized_google_pay_card = ActiveMerchant::Billing::NetworkTokenizationCreditCard.new({
+ source: :google_pay
+ })
@tokenized_bogus_pay_card = ActiveMerchant::Billing::NetworkTokenizationCreditCard.new({
source: :bogus_pay
})
@@ -27,6 +30,7 @@ def test_credit_card?
assert @tokenized_card.credit_card?
assert @tokenized_apple_pay_card.credit_card?
assert @tokenized_android_pay_card.credit_card?
+ assert @tokenized_google_pay_card.credit_card?
assert @tokenized_bogus_pay_card.credit_card?
end
@@ -38,6 +42,7 @@ def test_source
assert_equal @tokenized_card.source, :apple_pay
assert_equal @tokenized_apple_pay_card.source, :apple_pay
assert_equal @tokenized_android_pay_card.source, :android_pay
+ assert_equal @tokenized_google_pay_card.source, :google_pay
assert_equal @tokenized_bogus_pay_card.source, :apple_pay
end
end