├── .github └── workflows │ └── ruby-workflow.yml ├── .gitignore ├── .gitmodules ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE.txt ├── MIGRATING.md ├── README.md ├── Rakefile ├── authorizenet.gemspec ├── lib ├── app │ └── helpers │ │ └── authorize_net_helper.rb ├── authorize_net.rb ├── authorize_net │ ├── api │ │ ├── LogHelper.rb │ │ ├── SensitiveDataFilter.rb │ │ ├── api_transaction.rb │ │ ├── constants.yml │ │ ├── schema.rb │ │ └── transaction.rb │ ├── authorize_net.rb │ ├── response.rb │ ├── transaction.rb │ ├── xml_response.rb │ └── xml_transaction.rb └── authorizenet.rb └── spec ├── api_spec.rb ├── credentials.yml ├── spec.opts ├── spec_helper.rb └── support └── shared_helper.rb /.github/workflows/ruby-workflow.yml: -------------------------------------------------------------------------------- 1 | name: Authorize.net Ruby CI 2 | on: 3 | push: 4 | pull_request: 5 | workflow_dispatch: 6 | env: 7 | sdk_ruby: 'sdk-ruby' 8 | sample_code_ruby: 'sample-code-ruby' 9 | jobs: 10 | workflow-job: 11 | defaults: 12 | run: 13 | shell: bash 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | operating-system: [ubuntu-latest, macos-latest, windows-latest] 18 | ruby-version: ['2.4', '2.5', '2.6', '2.7', '3.0', '3.1', '3.2', '3.3'] 19 | exclude: 20 | - operating-system: macos-latest 21 | ruby-version: '2.4' 22 | - operating-system: macos-latest 23 | ruby-version: '2.5' 24 | include: 25 | - operating-system: macos-13 26 | ruby-version: '2.4' 27 | - operating-system: macos-13 28 | ruby-version: '2.5' 29 | runs-on: ${{matrix.operating-system}} 30 | env: 31 | API_LOGIN_ID: 5KP3u95bQpv 32 | API_TRANSACTION_KEY: 346HZ32z3fP4hTG2 33 | MD5_VALUE: 'MD5_TEST' 34 | steps: 35 | - name: Creating separate folders for SDK and Sample Codes 36 | run: | 37 | rm -rf $sdk_ruby 38 | rm -rf $sample_code_ruby 39 | mkdir $sdk_ruby $sample_code_ruby 40 | 41 | - name: Checkout authorizenet/sdk-ruby 42 | uses: actions/checkout@v4 43 | with: 44 | path: ${{env.sdk_ruby}} 45 | 46 | 47 | - name: Checkout authorizenet/sample-code-ruby 48 | uses: actions/checkout@v4 49 | with: 50 | repository: 'authorizenet/sample-code-ruby' 51 | ref: 'master' 52 | path: ${{env.sample_code_ruby}} 53 | 54 | 55 | - name: Install ruby 56 | uses: ruby/setup-ruby@v1 57 | with: 58 | ruby-version: ${{matrix.ruby-version}} 59 | 60 | 61 | - name: Install and Test 62 | run: | 63 | cd $sdk_ruby 64 | bundle install 65 | rake gem 66 | PACKAGE_VERSION=$(cat authorizenet.gemspec | grep -w s.version | cut -d '"' -f 2) 67 | bundle exec rake spec 68 | 69 | cd ../$sample_code_ruby 70 | gem install ../$sdk_ruby/authorizenet-$PACKAGE_VERSION.gem 71 | bundle install 72 | bundle exec rspec spec/sample_code_spec.rb -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | .ruby-* 3 | .idea 4 | gemfiles/*.lock 5 | /coverage/ 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "sample-code-ruby"] 2 | path = sample-code-ruby 3 | url = https://github.com/AuthorizeNet/sample-code-ruby 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | + Thanks for contributing to the Authorize.Net Ruby SDK. 2 | 3 | + Before you submit a pull request, we ask that you consider the following: 4 | 5 | - Submit an issue to state the problem your pull request solves or the funtionality that it adds. We can then advise on the feasability of the pull request, and let you know if there are other possible solutions. 6 | - Part of the SDK is auto-generated based on the XML schema. Due to this auto-generation, we cannot merge contributions for request or response classes. You are welcome to open an issue to report problems or suggest improvements. Auto-generated classes include all classes in [lib/authorize_net/api/schema.rb](https://github.com/AuthorizeNet/sdk-ruby/tree/master/lib/authorize_net/api) and [lib/authorize_net/api/transaction.rb](https://github.com/AuthorizeNet/sdk-ruby/tree/master/lib/authorize_net/api) folders, except [lib/authorize_net/api/api_transaction.rb](https://github.com/AuthorizeNet/sdk-ruby/tree/master/lib/authorize_net/api). 7 | - Files marked as deprecated are no longer supported. Issues and pull requests for changes to these deprecated files will be closed. 8 | - Recent changes will be in [the future branch](https://github.com/AuthorizeNet/sdk-ruby/tree/future). Before submitting an issue or pull request, check the future branch first to see if a fix has already been merged. 9 | - **Always use the future branch for pull requests.** We will first merge pull requests to the future branch, before pushing to the master branch for the next release. -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | SDK LICENSE AGREEMENT 2 | This Software Development Kit (“SDK”) License Agreement (“Agreement”) is between you (both the individual downloading the SDK and any legal entity on behalf of which such individual is acting) (“You” or “Your”) and Authorize.Net LLC (“Authorize.Net’). 3 | IT IS IMPORTANT THAT YOU READ CAREFULLY AND UNDERSTAND THIS AGREEMENT. BY CLICKING THE “I ACCEPT” BUTTON OR AN EQUIVALENT INDICATOR OR BY DOWNLOADING, INSTALLING OR USING THE SDK OR THE DOCUMENTATION, YOU AGREE TO BE BOUND BY THIS AGREEMENT. 4 | 5 | 1. DEFINITIONS 6 | 1.1 “Application(s)” means software programs that You develop to operate with the Gateway using components of the Software. 7 | 1.2 “Documentation” means the materials made available to You in connection with the Software by or on behalf of Authorize.Net pursuant to this Agreement. 8 | 1.3 “Gateway” means any electronic payment platform maintained and operated by Authorize.Net and any of its affiliates. 9 | 1.4 “Software” means all of the software included in the software development kit made available to You by or on behalf of Authorize.Net pursuant to this Agreement, including but not limited to sample source code, code snippets, software tools, code libraries, sample applications, Documentation and any upgrades, modified versions, updates, and/or additions thereto, if any, made available to You by or on behalf of Authorize.Net pursuant to this Agreement. 10 | 2. GRANT OF LICENSE; RESTRICTIONS 11 | 2.1 Limited License. Subject to and conditioned upon Your compliance with the terms of this Agreement, Authorize.Net hereby grants to You a limited, revocable, non-exclusive, non-transferable, royalty-free license during the term of this Agreement to: (a) in any country worldwide, use, reproduce, modify, and create derivative works of the components of the Software solely for the purpose of developing, testing and manufacturing Applications; (b) distribute, sell or otherwise provide Your Applications that include components of the Software to Your end users; and (c) use the Documentation in connection with the foregoing activities. The license to distribute Applications that include components of the Software as set forth in subsection (b) above includes the right to grant sublicenses to Your end users to use such components of the Software as incorporated into such Applications, subject to the limitations and restrictions set forth in this Agreement. 12 | 2.2 Restrictions. You shall not (and shall have no right to): (a) make or distribute copies of the Software or the Documentation, in whole or in part, except as expressly permitted pursuant to Section 2.1; (b) alter or remove any copyright, trademark, trade name or other proprietary notices, legends, symbols or labels appearing on or in the Software or Documentation; (c) sublicense (or purport to sublicense) the Software or the Documentation, in whole or in part, to any third party except as expressly permitted pursuant to Section 2.1; (d) engage in any activity with the Software, including the development or distribution of an Application, that interferes with, disrupts, damages, or accesses in an unauthorized manner the Gateway or platform, servers, or systems of Authorize.Net, any of its affiliates, or any third party; (e) make any statements that Your Application is “certified” or otherwise endorsed, or that its performance is guaranteed, by Authorize.Net or any of its affiliates; or (f) otherwise use or exploit the Software or the Documentation for any purpose other than to develop and distribute Applications as expressly permitted by this Agreement. 13 | 2.3 Ownership. You shall retain ownership of Your Applications developed in accordance with this Agreement, subject to Authorize.Net’s ownership of the Software and Documentation (including Authorize.Net’s ownership of any portion of the Software or Documentation incorporated in Your Applications). You acknowledge and agree that all right, title and interest in and to the Software and Documentation shall, at all times, be and remain the exclusive property of Authorize.Net and that You do not have or acquire any rights, express or implied, in the Software or Documentation except those rights expressly granted under this Agreement. 14 | 2.4 No Support. Authorize.Net has no obligation to provide support, maintenance, upgrades, modifications or new releases of the Software. 15 | 2.5 Open Source Software. You hereby acknowledge that the Software may contain software that is distributed under “open source” license terms (“Open Source Software”). You shall review the Documentation in order to determine which portions of the Software are Open Source Software and are licensed under such Open Source Software license terms. To the extent any such license requires that Authorize.Net provide You any rights with respect to such Open Source Software that are inconsistent with the limited rights granted to You in this Agreement, then such rights in the applicable Open Source Software license shall take precedence over the rights and restrictions granted in this Agreement, but solely with respect to such Open Source Software. You acknowledge that the Open Source Software license is solely between You and the applicable licensor of the Open Source Software and that Your use, reproduction and distribution of Open Source Software shall be in compliance with applicable Open Source Software license. You understand and agree that Authorize.Net is not liable for any loss or damage that You may experience as a result of Your use of Open Source Software and that You will look solely to the licensor of the Open Source Software in the event of any such loss or damage. 16 | 2.6 License to Authorize.Net. In the event You choose to submit any suggestions, feedback or other information or materials related to the Software or Documentation or Your use thereof (collectively, “Feedback”) to Authorize.Net, You hereby grant to Authorize.Net a worldwide, non-exclusive, royalty-free, transferable, sublicensable, perpetual and irrevocable license to use and otherwise exploit such Feedback in connection with the Software, Documentation, and other products and services. 17 | 2.7 Use. 18 | (a) You represent, warrant and agree to use the Software and write Applications only for purposes permitted by (i) this Agreement; (ii) applicable law and regulation, including, without limitation, the Payment Card Industry Data Security Standard (PCI DSS); and (iii) generally accepted practices or guidelines in the relevant jurisdictions. You represent, warrant and agree that if You use the Software to develop Applications for general public end users, that You will protect the privacy and legal rights of those users. If the Application receives or stores personal or sensitive information provided by end users, it must do so securely and in compliance with all applicable laws and regulations, including card association regulations. If the Application receives Authorize.Net account information, the Application may only use that information to access the end user's Authorize.Net account. You represent, warrant and agree that You are solely responsible for (and that neither Authorize.Net nor its affiliates have any responsibility to You or to any third party for): (i) any data, content, or resources that You obtain, transmit or display through the Application; and (ii) any breach of Your obligations under this Agreement, any applicable third party license, or any applicable law or regulation, and for the consequences of any such breach. 19 | 3. WARRANTY DISCLAIMER; LIMITATION OF LIABILITY 20 | 3.1 Disclaimer. THE SOFTWARE AND THE DOCUMENTATION ARE PROVIDED ON AN “AS IS” AND “AS AVAILABLE” BASIS WITH NO WARRANTY. YOU AGREE THAT YOUR USE OF THE SOFTWARE AND THE DOCUMENTATION IS AT YOUR SOLE RISK AND YOU ARE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR OTHER DEVICE OR LOSS OF DATA THAT RESULTS FROM SUCH USE. TO THE FULLEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW, AUTHORIZE.NET AND ITS AFFILIATES EXPRESSLY DISCLAIM ALL WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, WITH RESPECT TO THE SOFTWARE AND THE DOCUMENTATION, INCLUDING ALL WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, SATISFACTORY QUALITY, ACCURACY, TITLE AND NON-INFRINGEMENT, AND ANY WARRANTIES THAT MAY ARISE OUT OF COURSE OF PERFORMANCE, COURSE OF DEALING OR USAGE OF TRADE. NEITHER AUTHORIZE.NET NOR ITS AFFILIATES WARRANT THAT THE FUNCTIONS OR INFORMATION CONTAINED IN THE SOFTWARE OR THE DOCUMENTATION WILL MEET ANY REQUIREMENTS OR NEEDS YOU MAY HAVE, OR THAT THE SOFTWARE OR DOCUMENTATION WILL OPERATE ERROR FREE, OR THAT THE SOFTWARE OR DOCUMENTATION IS COMPATIBLE WITH ANY PARTICULAR OPERATING SYSTEM.  21 | 3.2 Limitation of Liability. IN NO EVENT SHALL AUTHORIZE.NET AND ITS AFFILIATES BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR PUNITIVE DAMAGES, OR DAMAGES FOR LOSS OF PROFITS, REVENUE, BUSINESS, SAVINGS, DATA, USE OR COST OF SUBSTITUTE PROCUREMENT, INCURRED BY YOU OR ANY THIRD PARTY, WHETHER IN AN ACTION IN CONTRACT OR TORT, EVEN IF AUTHORIZE.NET HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES OR IF SUCH DAMAGES ARE FORESEEABLE. IN NO EVENT SHALL THE ENTIRE LIABILITY OF AUTHORIZE.NET AND AFFILIATES ARISING FROM OR RELATING TO THIS AGREEMENT OR THE SUBJECT MATTER HEREOF EXCEED ONE HUNDRED U.S. DOLLARS ($100). THE PARTIES ACKNOWLEDGE THAT THE LIMITATIONS OF LIABILITY IN THIS SECTION 3.2 AND IN THE OTHER PROVISIONS OF THIS AGREEMENT AND THE ALLOCATION OF RISK HEREIN ARE AN ESSENTIAL ELEMENT OF THE BARGAIN BETWEEN THE PARTIES, WITHOUT WHICH AUTHORIZE.NET WOULD NOT HAVE ENTERED INTO THIS AGREEMENT. 22 | 4. INDEMNIFICATION. You shall indemnify, hold harmless and, at Authorize.Net’s request, defend Authorize.Net and its affiliates and their officers, directors, employees, and agents from and against any claim, suit or proceeding, and any associated liabilities, costs, damages and expenses, including reasonable attorneys’ fees, that arise out of relate to: (i) Your Applications or the use or distribution thereof and Your use or distribution of the Software or the Documentation (or any portion thereof including Open Source Software), including, but not limited to, any allegation that any such Application or any such use or distribution infringes, misappropriates or otherwise violates any intellectual property (including, without limitation, copyright, patent, and trademark), privacy, publicity or other rights of any third party, or has caused the death or injury of any person or damage to any property; (ii) Your alleged or actual breach of this Agreement; (iii) the alleged or actual breach of this Agreement by any party to whom you have provided Your Applications, the Software or the Documentation or (iii) Your alleged or actual violation of or non-compliance with any applicable laws, legislation, policies, rules, regulations or governmental requirements (including, without limitation, any laws, legislation, policies, rules, regulations or governmental requirements related to privacy and data collection). 23 | 5. TERMINATION. This Agreement and the licenses granted to you herein are effective until terminated. Authorize.Net may terminate this Agreement and the licenses granted to You at any time. Upon termination of this Agreement, You shall cease all use of the Software and the Documentation, return to Authorize.Net or destroy all copies of the Software and Documentation and related materials in Your possession, and so certify to Authorize.Net. Except for the license to You granted herein, the terms of this Agreement shall survive termination. 24 | 6. CONFIDENTIAL INFORMATION 25 | a. You hereby agree (i) to hold Authorize.Net’s Confidential Information in strict confidence and to take reasonable precautions to protect such Confidential Information (including, without limitation, all precautions You employ with respect to Your own confidential materials), (ii) not to divulge any such Confidential Information to any third person; (iii) not to make any use whatsoever at any time of such Confidential Information except as strictly licensed hereunder, (iv) not to remove or export from the United States or re-export any such Confidential Information or any direct product thereof, except in compliance with, and with all licenses and approvals required under applicable U.S. and foreign export laws and regulations, including, without limitation, those of the U.S. Department of Commerce. 26 | b. “Confidential Information” shall mean any data or information, oral or written, treated as confidential that relates to Authorize.Net’s past, present, or future research, development or business activities, including without limitation any unannounced products and services, any information relating to services, developments, inventions, processes, plans, financial information, customer data, revenue, transaction volume, forecasts, projections, application programming interfaces, Software and Documentation. 27 | 7. General Terms 28 | 7.1 Law. This Agreement and all matters arising out of or relating to this Agreement shall be governed by the internal laws of the State of California without giving effect to any choice of law rule. This Agreement shall not be governed by the United Nations Convention on Contracts for the International Sales of Goods, the application of which is expressly excluded. In the event of any controversy, claim or dispute between the parties arising out of or relating to this Agreement, such controversy, claim or dispute shall be resolved in the state or federal courts in Santa Clara County, California, and the parties hereby irrevocably consent to the jurisdiction and venue of such courts. 29 | 7.2 Logo License. Authorize.Net hereby grants to You the right to use, reproduce, publish, perform and display Authorize.Net logo solely in accordance with the current Authorize.Net brand guidelines. 30 | 7.3 Severability and Waiver. If any provision of this Agreement is held to be illegal, invalid or otherwise unenforceable, such provision shall be enforced to the extent possible consistent with the stated intention of the parties, or, if incapable of such enforcement, shall be deemed to be severed and deleted from this Agreement, while the remainder of this Agreement shall continue in full force and effect. The waiver by either party of any default or breach of this Agreement shall not constitute a waiver of any other or subsequent default or breach. 31 | 7.4 No Assignment. You may not assign, sell, transfer, delegate or otherwise dispose of, whether voluntarily or involuntarily, by operation of law or otherwise, this Agreement or any rights or obligations under this Agreement without the prior written consent of Authorize.Net, which may be withheld in Authorize.Net’s sole discretion. Any purported assignment, transfer or delegation by You shall be null and void. Subject to the foregoing, this Agreement shall be binding upon and shall inure to the benefit of the parties and their respective successors and assigns. 32 | 7.5 Government Rights. If You (or any person or entity to whom you provide the Software or Documentation) are an agency or instrumentality of the United States Government, the Software and Documentation are “commercial computer software” and “commercial computer software documentation,” and pursuant to FAR 12.212 or DFARS 227.7202, and their successors, as applicable, use, reproduction and disclosure of the Software and Documentation are governed by the terms of this Agreement. 33 | 7.6 Export Administration. You shall comply fully with all relevant export laws and regulations of the United States, including, without limitation, the U.S. Export Administration Regulations (collectively “Export Controls”). Without limiting the generality of the foregoing, You shall not, and You shall require Your representatives not to, export, direct or transfer the Software or the Documentation, or any direct product thereof, to any destination, person or entity restricted or prohibited by the Export Controls. 34 | 7.7 Privacy. In order to continually innovate and improve the Software, Licensee understands and agrees that Authorize.Net may collect certain usage statistics including but not limited to a unique identifier, associated IP address, version number of software, and information on which tools and/or services in the Software are being used and how they are being used. 35 | 7.8 Entire Agreement; Amendments. This Agreement constitutes the entire agreement between the parties and supersedes all prior or contemporaneous agreements or representations, written or oral, concerning the subject matter of this Agreement. Authorize.Net may make changes to this Agreement, Software or Documentation in its sole discretion. When these changes are made, Authorize.Net will make a new version of the Agreement, Software or Documentation available on the website where the Software is available. This Agreement may not be modified or amended by You except in a writing signed by a duly authorized representative of each party. You acknowledge and agree that 36 | Authorize.Net has not made any representations, warranties or agreements of any kind, except as expressly set forth herein. 37 | 38 | 39 | Authorize.Net Software Development Kit (SDK) License Agreement 40 | v. February 1, 2017 41 | 1 42 | -------------------------------------------------------------------------------- /MIGRATING.md: -------------------------------------------------------------------------------- 1 | # Migrating from Legacy Authorize.Net Classes 2 | 3 | Authorize.Net no longer supports several legacy classes, including AIM, ARB and others listed below, as part of sdk-ruby. If you are using any of these, we recommend that you update your code to use the new Authorize.Net API classes under (sdk-ruby/lib/authorize_net/api). 4 | 5 | **For details on the deprecation and replacement of legacy Authorize.Net APIs, visit https://developer.authorize.net/api/upgrade_guide/.** 6 | 7 | ## Full list of classes that are no longer supported 8 | | Class | New Feature | Sample Codes directory/repository | 9 | |-------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------| 10 | | AIM (Authorize.NET/AIM) | [PaymentTransactions](https://developer.authorize.net/api/reference/index.html#payment-transactions) | [sample-code-ruby/PaymentTransactions](https://github.com/AuthorizeNet/sample-code-ruby/tree/master/PaymentTransactions) | 11 | | ARB (Authorize.NET/ARB) | [RecurringBilling](https://developer.authorize.net/api/reference/index.html#recurring-billing) | [sample-code-ruby/Recurring Billing](https://github.com/AuthorizeNet/sample-code-ruby/tree/master/RecurringBilling) | 12 | | CIM (Authorize.NET/CIM) | [CustomerProfiles](https://developer.authorize.net/api/reference/index.html#customer-profiles) | [sample-code-ruby/CustomerProfiles](https://github.com/AuthorizeNet/sample-code-ruby/tree/master/CustomerProfiles) | 13 | | SIM (Authorize.NET/SIM) | [Accept Hosted](https://developer.authorize.net/content/developer/en_us/api/reference/features/accept_hosted.html) | Not available | 14 | | Reporting (Authorize.NET/Reporting) | [TransactionReporting](https://developer.authorize.net/api/reference/index.html#transaction-reporting) | [sample-code-ruby/TransactionReporting](https://github.com/AuthorizeNet/sample-code-ruby/tree/master/TransactionReporting) | 15 | 16 | ## Example 17 | #### New model sample code for (charge-credit-card) 18 | ```Ruby 19 | require 'rubygems' 20 | require 'yaml' 21 | require 'authorizenet' 22 | require 'securerandom' 23 | 24 | include AuthorizeNet::API 25 | 26 | def charge_credit_card() 27 | config = YAML.load_file(File.dirname(__FILE__) + "/../credentials.yml") 28 | 29 | # Set the request to operate in either the sandbox or production environment 30 | transaction = Transaction.new(config["api_login_id"], config["api_transaction_key"], :gateway => :sandbox) 31 | 32 | # Create the payment transaction object 33 | request = CreateTransactionRequest.new 34 | 35 | # Populate the payment data 36 | request.transactionRequest = TransactionRequestType.new() 37 | request.transactionRequest.amount = ((SecureRandom.random_number + 1 ) * 150 ).round(2) 38 | request.transactionRequest.payment = PaymentType.new 39 | request.transactionRequest.payment.creditCard = CreditCardType.new("4242424242424242","0220","123") 40 | request.transactionRequest.customer = CustomerDataType.new(CustomerTypeEnum::Individual,"CUST-1234","bmc@mail.com",DriversLicenseType.new("DrivLicenseNumber123","WA","05/05/1990"),"123456789") 41 | 42 | # call the Transaction and get the response 43 | response = transaction.create_transaction(request) 44 | 45 | return response 46 | end 47 | 48 | if __FILE__ == $0 49 | charge_credit_card() 50 | end 51 | ``` 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Authorize.Net Ruby SDK 2 | 3 | [![Authorize.net Ruby CI](https://github.com/AuthorizeNet/sdk-ruby/actions/workflows/ruby-workflow.yml/badge.svg?branch=master)](https://github.com/AuthorizeNet/sdk-ruby/actions/workflows/ruby-workflow.yml) 4 | [![Version ][rubygems_badge]][rubygems] 5 | 6 | Starting Release 1.8.6 November 2015 the Authorize.Net API has been [reorganized to be more merchant focused](https://developer.authorize.net/api/upgrade_guide/). 7 | AIM, ARB, CIM, Reporting and SIM have all been deprecated in favor of sdk-ruby/lib/authorize_net/api. 8 | 9 | ## Requirements 10 | * Ruby 2.4 or higher 11 | * RubyGem 1.3.7 or higher (to build the gem) 12 | * RDoc 1.0 or higher (to build documentation) 13 | * Rake 0.8.7 or higher (to use the rake tasks) 14 | * Bundle 1.6 or higher 15 | * RSpec 2.1 or higher (to run rspec tests) 16 | * An Authorize.Net account (see _Registration & Configuration_ section below) 17 | 18 | ### Migrating from older versions 19 | Since August 2018, the Authorize.Net API has been reorganized to be more merchant focused. AuthorizeNetAIM, AuthorizeNetARB, AuthorizeNetCIM, Reporting and AuthorizeNetSIM classes have been deprecated in favor of AuthorizeNet::API. To see the full list of mapping of new features corresponding to the deprecated features, see [MIGRATING.md](MIGRATING.md). 20 | 21 | ### Contribution 22 | - If you need information or clarification about Authorize.Net features, create an issue with your question. You can also search the [Authorize.Net developer community](https://community.developer.authorize.net/) for discussions related to your question. 23 | - Before creating pull requests, read [the contributors guide](CONTRIBUTING.md). 24 | 25 | ### TLS 1.2 26 | The Authorize.Net API only support connections using the TLS 1.2 security protocol. Make sure to upgrade all required components to support TLS 1.2. Keep these components up to date to mitigate the risk of new security flaws. 27 | 28 | 29 | ## Installation 30 | 31 | ### Installation from rubygems.org 32 | ``` 33 | > sudo gem install authorizenet 34 | ``` 35 | 36 | ### Installation from project 37 | ``` 38 | > bundle install 39 | > rake gem 40 | > sudo gem install ./authorizenet-.gem 41 | ``` 42 | 43 | 44 | ## Registration & Configuration 45 | Use of this SDK and the Authorize.Net APIs requires having an account on the Authorize.Net system. You can find these details in the Settings section. 46 | If you don't currently have a production Authorize.Net account, [sign up for a sandbox account](https://developer.authorize.net/sandbox/). 47 | 48 | ### Authentication 49 | To authenticate with the Authorize.Net API, use your account's API Login ID and Transaction Key. If you don't have these credentials, obtain them from the Merchant Interface. For production accounts, the Merchant Interface is located at (https://account.authorize.net/), and for sandbox accounts, at (https://sandbox.authorize.net). 50 | 51 | After you have obtained your credentials, load them into the appropriate variables in your code. The below sample code shows how to set the credentials as part of the API request. 52 | 53 | #### To set your API credentials for an API request: 54 | ```ruby 55 | transaction = Transaction.new('YOUR_API_LOGIN_ID', 'YOUR_TRANSACTION_KEY', :gateway => :sandbox) 56 | ``` 57 | 58 | Never include your API Login ID and Transaction Key directly in a file in a publicly accessible portion of your website. As a best practice, define the API Login ID and Transaction Key in a constants file, and reference those constants in your code. 59 | 60 | #### Setting OAuth credentials 61 | Access Tokens can be set up using the transaction instantiation without the constructor. For example, in the method above: 62 | ```ruby 63 | transaction = Transaction.new 64 | transaction.access_token = 'testTokenValue' 65 | transaction.options_OAuth = {:gateway => :sandbox, :verify_ssl => true} 66 | ``` 67 | 68 | ### Switching between the sandbox environment and the production environment 69 | Authorize.Net maintains a complete sandbox environment for testing and development purposes. The sandbox environment is an exact replica of our production environment, with simulated transaction authorization and settlement. By default, this SDK is configured to use the sandbox environment. To switch to the production environment, replace the environment constant in the transaction instantiation. For example: 70 | ```ruby 71 | # For PRODUCTION use 72 | transaction = Transaction.new('YOUR_API_LOGIN_ID', 'YOUR_TRANSACTION_KEY', :gateway => :production) 73 | ``` 74 | API credentials are different for each environment, so be sure to switch to the appropriate credentials when switching environments. 75 | 76 | ## SDK Usage Examples and Sample Code 77 | When using this SDK, downloading the Authorize.Net sample code repository is recommended. 78 | * [Authorize.Net Ruby Sample Code Repository (on GitHub)](https://github.com/AuthorizeNet/sample-code-ruby) 79 | 80 | The repository contains comprehensive sample code for common uses of the Authorize.Net API. 81 | 82 | The API Reference contains details and examples of the structure and formatting of the Authorize.Net API. 83 | * [Developer Center API Reference](http://developer.authorize.net/api/reference/index.html) 84 | 85 | Use the examples in the API Reference to determine which methods and information to include in an API request using this SDK. 86 | 87 | ## Create a Chase Pay Transaction 88 | 89 | Use this method to authorize and capture a payment using a tokenized credit card number issued by Chase Pay. Chase Pay transactions are only available to merchants using the Paymentech processor. 90 | 91 | The following information is required in the request: 92 | - **payment token** 93 | - **expiration date** 94 | - **cryptogram** received from the token provider 95 | - **tokenRequestorName** 96 | - **tokenRequestorId** 97 | - **tokenRequestorEci** 98 | 99 | When using the SDK to submit Chase Pay transactions, consider the following points: 100 | - `tokenRequesterName` must be populated with **`”CHASE_PAY”`** 101 | - `tokenRequestorId` must be populated with the **`Token Requestor ID`** provided by Chase Pay services for each transaction during consumer checkout 102 | - `tokenRequesterEci` must be populated with the **`ECI Indicator`** provided by Chase Pay services for each transaction during consumer checkout 103 | 104 | 105 | ## Building & Testing the SDK 106 | 107 | ### Running the SDK Tests 108 | To run the integration tests in the sandbox: 109 | ``` 110 | rake spec 111 | ``` 112 | 113 | To run rspec tests, create a spec/credentials.yml with the following credentials and the values obtained as described below. 114 | ```ruby 115 | #obtain an API login_id and transaction_id according to instructions at https://developer.authorize.net/faqs/#gettranskey 116 | api_login_id: {login_id_value} 117 | api_transaction_key: {transaction_key_value} 118 | ``` 119 | 120 | ### Testing Guide 121 | For additional help in testing your code, Authorize.Net maintains a [comprehensive testing guide](http://developer.authorize.net/hello_world/testing_guide/) that includes test credit card numbers to use and special triggers to generate certain responses from the sandbox environment. 122 | 123 | ## Logging Sensitive Data 124 | A new sensitive data logger has been introduced with the Authorize.Net Ruby SDK. To use it in your code, create a file called `LogConfig.yml` and place it in the base folder of your application. The logger configuration should contain the following lines: 125 | ``` 126 | loglevel: info 127 | filepath: 128 | maskSensitiveData: true 129 | ``` 130 | The logger code uses the default Ruby `Logger` library. There is no need to install external libraries for the purpose of logging. The above three fields in the LogConfig.yml file are mandatory. The logging levels available are `debug, info, warn` and `error`. 131 | 132 | The value for `maskSensitiveData` can either be `true` or `false`. Setting the `maskSensitiveData` flag to `true` masks the sensitive data in the request XML body while logging to the log file. You can turn off logging by removing the configuration file from your application folder. 133 | 134 | The sensitive fields that are masked during logging are: 135 | * Card Number 136 | * Card Code 137 | * Expiration Date 138 | * Transaction Key 139 | * Account Number 140 | * Name on Account 141 | 142 | There is also a list of regular expressions which the sensitive logger uses to mask credit card numbers while logging. 143 | 144 | More information on the regular expressions used during sensitive data logging [can be found here](https://github.com/AuthorizeNet/sdk-ruby/blob/master/lib/authorize_net/api/SensitiveDataFilter.rb). 145 | 146 | ### Transaction Hash Upgrade 147 | Authorize.Net is phasing out the MD5 based `transHash` element in favor of the SHA-512 based `transHashSHA2`. The setting in the Merchant Interface which controlled the MD5 Hash option is no longer available, and the `transHash` element will stop returning values at a later date to be determined. For information on how to use `transHashSHA2`, see the [Transaction Hash Upgrade Guide](https://developer.authorize.net/support/hash_upgrade/). 148 | 149 | ## License 150 | This repository is distributed under a proprietary license. See the provided [`LICENSE.txt`](/LICENSE.txt) file. 151 | 152 | [rubygems_badge]: https://badge.fury.io/rb/authorizenet.svg 153 | [rubygems]: https://rubygems.org/gems/authorizenet -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "rake" 2 | require "rspec/core/rake_task" 3 | 4 | task default: :spec 5 | desc "Run all specs" 6 | RSpec::Core::RakeTask.new(:spec) do |spec| 7 | spec.pattern = FileList['spec/**/*_spec.rb'] 8 | spec.rspec_opts = ['--options', 'spec/spec.opts'] 9 | end 10 | 11 | namespace "spec" do 12 | desc "Run the API spec" 13 | RSpec::Core::RakeTask.new('api') do |spec| 14 | spec.pattern = FileList['spec/api_spec.rb'] 15 | spec.rspec_opts = ['--options', 'spec/spec.opts'] 16 | end 17 | 18 | desc "Run the Sample code Test Runner" 19 | RSpec::Core::RakeTask.new('testrunner') do |spec| 20 | spec.pattern = FileList['sample-code-ruby/spec/sample_code_spec.rb'] 21 | spec.rspec_opts = ['--options', 'spec/spec.opts'] 22 | end 23 | end 24 | 25 | desc "Builds the gem" 26 | task :gem do 27 | `gem build authorizenet.gemspec` 28 | end 29 | 30 | desc "Builds the documentation" 31 | task :doc do 32 | `rdoc -U -S --main=README.rdoc -A documented_accessor=RW README.rdoc lib/` 33 | end 34 | 35 | namespace "doc" do 36 | desc "Builds the documentation with graphical class hierarchy" 37 | task :graph do 38 | `rdoc -U -d -S --main=README.rdoc -A documented_accessor=RW README.rdoc lib/` 39 | end 40 | end 41 | 42 | desc "Builds the documentation (alias of :doc)" 43 | task :rdoc do 44 | Rake::Task[:doc].execute 45 | end 46 | 47 | desc "Bundles the sample app." 48 | task :samples do 49 | `. sample_app_version && zip -r anet_ruby_samples-$VERSION.zip sample_app -x '*/.*' -x '*/Icon' -x '*/__MACOSX'` 50 | end 51 | 52 | desc "Bundles the sample app and gem." 53 | task :bundle do 54 | Rake::Task[:samples].execute 55 | Rake::Task[:gem].execute 56 | end 57 | -------------------------------------------------------------------------------- /authorizenet.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |s| 2 | s.name = "authorizenet" 3 | s.version = "2.0.2" 4 | s.platform = Gem::Platform::RUBY 5 | s.date = "2021-03-31" 6 | s.summary = "Authorize.Net Payments SDK" 7 | s.description = "Authorize.Net SDK includes standard payments, recurring billing, and customer profiles" 8 | s.authors = ["Authorize.Net"] 9 | s.email = "developer@authorize.net" 10 | s.files = Dir.glob("{lib}/**/*") 11 | s.homepage = "https://github.com/AuthorizeNet/sdk-ruby" 12 | s.license = "https://github.com/AuthorizeNet/sdk-ruby/blob/master/LICENSE.txt" 13 | 14 | s.required_ruby_version = '>= 2.4.0' 15 | s.required_rubygems_version = '>= 1.3.7' 16 | 17 | s.add_runtime_dependency 'activesupport', '>= 5.2.4.5' 18 | s.add_runtime_dependency 'nokogiri', '~> 1.6', '>= 1.6.4' 19 | s.add_runtime_dependency 'roxml', '>= 3.3.1' 20 | s.add_runtime_dependency 'rexml', '>= 3.2.0' 21 | 22 | s.add_development_dependency 'rake', '~> 12.3', '>= 12.3.3' 23 | s.add_development_dependency 'rspec', '~> 3.9.0' 24 | end 25 | -------------------------------------------------------------------------------- /lib/app/helpers/authorize_net_helper.rb: -------------------------------------------------------------------------------- 1 | # The Authorize.Net Rails Helper module. Provides methods to assist with integrating the various APIs. 2 | 3 | module AuthorizeNetHelper 4 | # Generates a collection of hidden form fields (as a raw HTML string) for a AuthorizeNet::SIM::Transaction 5 | # (sim_transaction). You can specify any html_options that hidden_field_tag accepts, and the 6 | # hidden fields will be built with those options. 7 | def sim_fields(sim_transaction, html_options = {}) 8 | fields = sim_transaction.form_fields.collect do |k, v| 9 | if v.is_a? Array 10 | v.collect { |val| hidden_field_tag(k, val, html_options) } 11 | else 12 | hidden_field_tag(k, v, html_options) 13 | end 14 | end 15 | fields.flatten! 16 | field_str = fields.join("\n") 17 | if field_str.respond_to?(:html_safe) 18 | return field_str.html_safe 19 | else 20 | return field_str 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/authorize_net.rb: -------------------------------------------------------------------------------- 1 | # The SDK initialization enters here. Loads all needed libraries and files. Inspects 2 | # the current runtime to see if Rails is present. If it is, we inject our helper into 3 | # ActiveSupport. 4 | 5 | # coverall 6 | # require 'coveralls' 7 | # Coveralls.wear! 8 | 9 | require "cgi" 10 | require "net/https" 11 | require "uri" 12 | require "openssl" 13 | require 'bigdecimal' 14 | require 'nokogiri' 15 | require 'date' 16 | 17 | # TODO: Add local data validation where possible 18 | 19 | $LOAD_PATH.unshift File.dirname(__FILE__) 20 | 21 | require "authorize_net/authorize_net" 22 | # require "authorize_net/customer" 23 | # require "authorize_net/email_receipt" 24 | # require "authorize_net/order" 25 | # require "authorize_net/line_item" 26 | require "authorize_net/response" 27 | # require "authorize_net/key_value_response" 28 | require "authorize_net/xml_response" 29 | require "authorize_net/transaction" 30 | # require "authorize_net/key_value_transaction" 31 | require "authorize_net/xml_transaction" 32 | 33 | # API 34 | require "authorize_net/api/schema" 35 | require "authorize_net/api/api_transaction" 36 | require "authorize_net/api/transaction" 37 | 38 | # Load our Rails plugin 39 | 40 | if defined?(Rails) 41 | if defined?(Rails::Railtie) 42 | module AuthorizeNet 43 | class Railtie < Rails::Railtie 44 | initializer "authorize_net.load_path_initialize" do |_app| 45 | %w[models controllers helpers].each do |dir| 46 | path = File.join(File.dirname(__FILE__), 'app', dir) 47 | $LOAD_PATH << path 48 | ActiveSupport::Dependencies.autoload_paths << path 49 | ActiveSupport::Dependencies.autoload_once_paths.delete(path) 50 | end 51 | end 52 | end 53 | end 54 | else 55 | %w[models controllers helpers].each do |dir| 56 | path = File.join(File.dirname(__FILE__), 'app', dir) 57 | $LOAD_PATH << path 58 | ActiveSupport::Dependencies.load_paths << path 59 | ActiveSupport::Dependencies.load_once_paths.delete(path) 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /lib/authorize_net/api/LogHelper.rb: -------------------------------------------------------------------------------- 1 | require 'logger' 2 | require 'yaml' 3 | require File.join File.dirname(__FILE__), 'SensitiveDataFilter' 4 | 5 | module AuthorizeNet::API 6 | class Log 7 | @@shouldLog = false 8 | @@loglevels = ['debug','info','warn','error'] 9 | def initialize() 10 | begin 11 | filepath = './LogConfig.yml' 12 | if(File.file?(filepath)) 13 | cnf = YAML::load(File.open(filepath)) 14 | if(@@loglevels.include? cnf['loglevel'].downcase) 15 | @@shouldLog = true 16 | @logger = Logger.new(cnf['filepath']) 17 | @logger.level = LogLevelMapper(cnf['loglevel'].downcase) 18 | if(cnf['maskSensitiveData']) 19 | @logger.formatter = SensitiveDataFilter.new 20 | else 21 | constants = YAML.load_file(File.dirname(__FILE__) + "/constants.yml") 22 | @logger.formatter = proc do |severity, datetime, progname, msg| 23 | progname = constants['clientId'] 24 | date_format = datetime.strftime("%Y-%m-%d %H:%M:%S") 25 | if severity == "INFO" or severity == "WARN" 26 | "[#{date_format}] #{severity} (#{progname}): #{msg}\n" 27 | else 28 | "[#{date_format}] #{severity} (#{progname}): #{msg}\n" 29 | end 30 | end 31 | end 32 | else 33 | raise "Invalid log levels" 34 | end 35 | else 36 | @@shouldLog = false 37 | end 38 | rescue 39 | @@shouldLog = false 40 | end 41 | end 42 | def debug(message) 43 | if(@@shouldLog) 44 | begin 45 | @logger.debug message 46 | rescue Exception => ex 47 | ex 48 | end 49 | end 50 | end 51 | def info(message) 52 | if(@@shouldLog) 53 | begin 54 | @logger.info message 55 | rescue Exception => ex 56 | ex 57 | end 58 | end 59 | end 60 | def warn(message) 61 | if(@@shouldLog) 62 | begin 63 | @logger.warn message 64 | rescue Exception => ex 65 | ex 66 | end 67 | end 68 | end 69 | def error(message) 70 | if(@@shouldLog) 71 | begin 72 | @logger.error message 73 | rescue Exception => ex 74 | ex 75 | end 76 | end 77 | end 78 | def LogLevelMapper(loglevel) 79 | case loglevel 80 | when 'debug' 81 | Logger::DEBUG 82 | when 'info' 83 | Logger::INFO 84 | when 'warn' 85 | Logger::WARN 86 | when 'error' 87 | Logger::ERROR 88 | end 89 | end 90 | end 91 | 92 | class LogHelper 93 | def self.log 94 | Log.new 95 | end 96 | end 97 | end 98 | -------------------------------------------------------------------------------- /lib/authorize_net/api/SensitiveDataFilter.rb: -------------------------------------------------------------------------------- 1 | require 'logger' 2 | 3 | module AuthorizeNet::API 4 | class SensitiveTag 5 | attr_accessor :tagName, :pattern, :replacement, :disableMask 6 | def initialize(tagName, pattern, replacement, disableMask) 7 | @tagName = tagName 8 | @pattern = pattern 9 | @replacement = replacement 10 | @disableMask = disableMask 11 | end 12 | end 13 | 14 | class SensitiveDataConfigType 15 | attr_accessor :sensitiveTags, :sensitiveStringRegexes 16 | def initialize 17 | @sensitiveTags = Array.new([SensitiveTag.new("cardCode", "", "XXX", false), 18 | SensitiveTag.new("cardNumber", "(\\p{N}+)(\\p{N}{4})", "XXXX-\\2", false), 19 | SensitiveTag.new("expirationDate", "", "XXX", false), 20 | SensitiveTag.new("accountNumber", "(\\p{N}+)(\\p{N}{4})", "XXXX-\\2", false), 21 | SensitiveTag.new("nameOnAccount", "", "XXX", false), 22 | SensitiveTag.new("transactionKey", "", "XXX", false)]).freeze 23 | @sensitiveStringRegexes = ["4\\p{N}{3}([\\ \\-]?)\\p{N}{4}\\1\\p{N}{4}\\1\\p{N}{4}", 24 | "4\\p{N}{3}([\\ \\-]?)(?:\\p{N}{4}\\1){2}\\p{N}(?:\\p{N}{3})?", 25 | "5[1-5]\\p{N}{2}([\\ \\-]?)\\p{N}{4}\\1\\p{N}{4}\\1\\p{N}{4}", 26 | "6(?:011|22(?:1(?=[\\ \\-]?(?:2[6-9]|[3-9]))|[2-8]|9(?=[\\ \\-]?(?:[01]|2[0-5])))|4[4-9]\\p{N}|5\\p{N}\\p{N})([\\ \\-]?)\\p{N}{4}\\1\\p{N}{4}\\1\\p{N}{4}", 27 | "35(?:2[89]|[3-8]\\p{N})([\\ \\-]?)\\p{N}{4}\\1\\p{N}{4}\\1\\p{N}{4}", 28 | "3[47]\\p{N}\\p{N}([\\ \\-]?)\\p{N}{6}\\1\\p{N}{5}"].freeze 29 | end 30 | end 31 | 32 | class SensitiveDataFilter < Logger::Formatter 33 | @@sensitiveTagConfig = nil 34 | @@tagPatterns = nil 35 | @@tagReplacements = nil 36 | @@cardPatterns = nil 37 | 38 | def initialize 39 | @@sensitiveTagConfig = SensitiveDataConfigType.new 40 | @@cardPatterns = @@sensitiveTagConfig.sensitiveStringRegexes 41 | @@tagPatterns = Array.new(@@sensitiveTagConfig.sensitiveStringRegexes.length) 42 | @@tagReplacements = Array.new(@@sensitiveTagConfig.sensitiveTags.length) 43 | 44 | @@sensitiveTagConfig.sensitiveTags.each_with_index do |sensitiveTag, index| 45 | tagName = sensitiveTag.tagName 46 | replacement = sensitiveTag.replacement 47 | 48 | if sensitiveTag.pattern.nil? || sensitiveTag.pattern.empty? 49 | pattern = "(.*)" 50 | else 51 | pattern = sensitiveTag.pattern 52 | end 53 | 54 | @@tagPatterns[index] = "<"+tagName+">"+pattern+"" 55 | @@tagReplacements[index] = "<"+tagName+">"+replacement+"" 56 | end 57 | end 58 | 59 | def call(severity, time, progname, msg) 60 | xmlMaskedLog = maskSensitiveXmlString(msg) 61 | ccMasked = maskCreditCards(xmlMaskedLog) 62 | return formatLogEntry(severity, time, progname, ccMasked) 63 | end 64 | 65 | def maskCreditCards(input) 66 | input = input.force_encoding("UTF-8") 67 | @@cardPatterns.each {|cardNumberRegex| 68 | input = input.gsub(/#{cardNumberRegex}/, "XXX") 69 | } 70 | return input 71 | end 72 | 73 | def maskSensitiveXmlString(input) 74 | input = input.force_encoding("UTF-8") 75 | @@tagPatterns.each_with_index do |item, index| 76 | input = input.gsub(/#{item}/,@@tagReplacements[index]) 77 | end 78 | return input 79 | end 80 | 81 | def formatLogEntry(severity, time, progname, msg) 82 | constants = YAML.load_file(File.dirname(__FILE__) + "/constants.yml") 83 | progname = constants['clientId'] 84 | date_format = time.strftime("%Y-%m-%d %H:%M:%S") 85 | if severity == "INFO" or severity == "WARN" 86 | "[#{date_format}] #{severity} (#{progname}): #{msg}\n" 87 | else 88 | "[#{date_format}] #{severity} (#{progname}): #{msg}\n" 89 | end 90 | end 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /lib/authorize_net/api/api_transaction.rb: -------------------------------------------------------------------------------- 1 | require File.join File.dirname(__FILE__), 'LogHelper' 2 | 3 | module AuthorizeNet::API 4 | class ApiTransaction < AuthorizeNet::XmlTransaction 5 | module Type 6 | API_CREATE_TRANSACTION = "createTransactionRequest".freeze 7 | API_UPDATE_SPLIT_TENDER_GROUP = "updateSplitTenderGroupRequest".freeze 8 | 9 | API_CREATE_SUBSCRIPTION = "ARBCreateSubscriptionRequest".freeze 10 | API_UPDATE_SUBSCRIPTION = "ARBUpdateSubscriptionRequest".freeze 11 | API_CANCEL_SUBSCRIPTION = "ARBCancelSubscriptionRequest".freeze 12 | API_GET_SUBSCRIPTION_STATUS = "ARBGetSubscriptionStatusRequest".freeze 13 | API_GET_SUBSCRIPTION_LIST = "ARBGetSubscriptionListRequest".freeze 14 | 15 | API_GET_CUSTOMER_PROFILE_IDS = "getCustomerProfileIdsRequest".freeze 16 | 17 | API_CREATE_CUSTOMER_PROFILE = "createCustomerProfileRequest".freeze 18 | API_GET_CUSTOMER_PROFILE = "getCustomerProfileRequest".freeze 19 | API_UPDATE_CUSTOMER_PROFILE = "updateCustomerProfileRequest".freeze 20 | API_DELETE_CUSTOMER_PROFILE = "deleteCustomerProfileRequest".freeze 21 | 22 | API_CREATE_CUSTOMER_PAYMENT_PROFILE = "createCustomerPaymentProfileRequest".freeze 23 | API_GET_CUSTOMER_PAYMENT_PROFILE = "getCustomerPaymentProfileRequest".freeze 24 | API_UPDATE_CUSTOMER_PAYMENT_PROFILE = "updateCustomerPaymentProfileRequest".freeze 25 | API_VALIDATE_CUSTOMER_PAYMENT_PROFILE = "validateCustomerPaymentProfileRequest".freeze 26 | API_DELETE_CUSTOMER_PAYMENT_PROFILE = "deleteCustomerPaymentProfileRequest".freeze 27 | 28 | API_CREATE_CUSTOMER_SHIPPING_PROFILE = "createCustomerShippingAddressRequest".freeze 29 | API_GET_CUSTOMER_SHIPPING_PROFILE = "getCustomerShippingAddressRequest".freeze 30 | API_UPDATE_CUSTOMER_SHIPPING_PROFILE = "updateCustomerShippingAddressRequest".freeze 31 | API_DELETE_CUSTOMER_SHIPPING_PROFILE = "deleteCustomerShippingAddressRequest".freeze 32 | 33 | API_CREATE_CUSTOMER_PROFILE_FROM_TRANSACTION = "createCustomerProfileFromTransactionRequest".freeze 34 | 35 | API_GET_SETTLED_BATCH_LIST = "getSettledBatchListRequest".freeze 36 | API_GET_TRANSACTION_LIST = "getTransactionListRequest".freeze 37 | API_GET_TRANSACTION_DETAILS = "getTransactionDetailsRequest".freeze 38 | API_GET_UNSETTLED_TRANSACTION_LIST = "getUnsettledTransactionListRequest".freeze 39 | API_GET_BATCH_STATISTICS = "getBatchStatisticsRequest".freeze 40 | API_GET_TRANSACTION_LIST_FOR_CUSTOMER = "getTransactionListForCustomerRequest".freeze 41 | 42 | API_GET_HOSTED_PROFILE_PAGE = "getHostedProfilePageRequest".freeze 43 | 44 | API_DECRYPT_PAYMENT_DATA = "decryptPaymentDataRequest".freeze 45 | API_AUTHENTICATE_TEST_REQUEST = "authenticateTestRequest".freeze 46 | 47 | API_GET_CUSTOMER_PAYMENT_PROFILE_LIST = "getCustomerPaymentProfileListRequest".freeze 48 | 49 | API_ARB_GET_SUBSCRIPTION_REQUEST = "ARBGetSubscriptionRequest".freeze 50 | 51 | API_GET_MERCHANT_DETAILS = "getMerchantDetailsRequest".freeze 52 | API_GET_HOSTED_PAYMENT_PAGE = "getHostedPaymentPageRequest".freeze 53 | API_UDPATE_HELD_TRANSACTION = "updateHeldTransactionRequest".freeze 54 | API_UPDATE_MERCHANT_DETAILS = "updateMerchantDetailsRequest".freeze 55 | API_GET_CUSTOMER_PAYMENT_PROFILE_NONCE = "getCustomerPaymentProfileNonceRequest".freeze 56 | 57 | end 58 | 59 | def initialize(api_login_id = nil, api_transaction_key = nil, options = {}) 60 | super 61 | end 62 | 63 | def setOAuthOptions 64 | super 65 | end 66 | 67 | def make_request(request, responseClass, type) 68 | setOAuthOptions 69 | unless responseClass.nil? || request.nil? 70 | begin 71 | @xml = serialize(request, type) 72 | LogHelper.log.debug(@xml) 73 | respXml = send_request(@xml) 74 | @response = deserialize(respXml.body, responseClass) 75 | LogHelper.log.debug(respXml.body) 76 | return @response 77 | rescue Exception => ex 78 | LogHelper.log.error(ex.message) 79 | ex.backtrace.each {|line| LogHelper.log.error(line)} 80 | ex 81 | end 82 | end 83 | end 84 | 85 | def serialize(object, type) 86 | doc = Nokogiri::XML::Document.new 87 | doc.root = object.to_xml 88 | constants = YAML.load_file(File.dirname(__FILE__) + "/constants.yml") 89 | clientId = constants['clientId'] 90 | 91 | builder = Nokogiri::XML::Builder.new(encoding: 'utf-8') do |x| 92 | x.send(type.to_sym, xmlns: XML_NAMESPACE) do 93 | x.merchantAuthentication do 94 | x.accessToken @access_token unless @access_token.blank? 95 | if !@api_login_id.blank? || (@access_token.blank? && @api_login_id.blank?) 96 | x.name @api_login_id 97 | x.transactionKey @api_transaction_key 98 | end 99 | end 100 | x.clientId clientId 101 | x.send :insert, doc.root.element_children 102 | end 103 | end 104 | builder.to_xml 105 | end 106 | 107 | 108 | def send_request(xml) 109 | url = URI.parse(@gateway) 110 | 111 | httpRequest = Net::HTTP::Post.new(url.path) 112 | httpRequest.content_type = 'text/xml' 113 | httpRequest.body = xml 114 | connection = Net::HTTP.new(url.host, url.port) 115 | connection.use_ssl = true 116 | if @verify_ssl 117 | connection.verify_mode = OpenSSL::SSL::VERIFY_PEER 118 | else 119 | connection.verify_mode = OpenSSL::SSL::VERIFY_NONE 120 | end 121 | 122 | response = connection.start { |http| http.request(httpRequest) } 123 | end 124 | 125 | def deserialize(xml, responseClass) 126 | responseClass.from_xml(xml) 127 | end 128 | end 129 | end 130 | -------------------------------------------------------------------------------- /lib/authorize_net/api/constants.yml: -------------------------------------------------------------------------------- 1 | clientId: sdk-ruby-2.0.1 2 | -------------------------------------------------------------------------------- /lib/authorize_net/api/transaction.rb: -------------------------------------------------------------------------------- 1 | module AuthorizeNet::API 2 | class Transaction < ApiTransaction 3 | attr_accessor :access_token 4 | attr_accessor :options_OAuth 5 | 6 | def initialize(api_login_id = nil, api_transaction_key = nil, options = {}) 7 | super 8 | end 9 | 10 | # This request enables you to create a transaction, and optional 11 | # customer profile. 12 | # NOTE: Network tokenized transactions (e.g. Apple Pay), or PayPal should 13 | # not be used to create payment profiles. 14 | # 15 | # See spec/api_spec.rb for usage examples 16 | def create_transaction(request) 17 | make_request(request, CreateTransactionResponse, Type::API_CREATE_TRANSACTION) 18 | end 19 | 20 | # This request enables you to create a recurring billing subscription. 21 | # 22 | # See spec/api_spec.rb for usage examples 23 | def create_subscription(request) 24 | make_request(request, ARBCreateSubscriptionResponse, Type::API_CREATE_SUBSCRIPTION) 25 | end 26 | 27 | # This request enables you to update a recurring billing subscription. 28 | # 29 | # See spec/api_spec.rb for usage examples 30 | def update_subscription(request) 31 | make_request(request, ARBUpdateSubscriptionResponse, Type::API_UPDATE_SUBSCRIPTION) 32 | end 33 | 34 | # This request enables you to cancel a recurring billing subscription. 35 | # 36 | # See spec/api_spec.rb for usage examples 37 | def cancel_subscription(request) 38 | make_request(request, ARBCancelSubscriptionResponse, Type::API_CANCEL_SUBSCRIPTION) 39 | end 40 | 41 | # This request enables you to get a recurring billing subscription status. 42 | # 43 | # See spec/api_spec.rb for usage examples 44 | def get_subscription_status(request) 45 | make_request(request, ARBGetSubscriptionStatusResponse, Type::API_GET_SUBSCRIPTION_STATUS) 46 | end 47 | 48 | # This request enables you to get a list of all recurring billing 49 | # subscriptions. 50 | # 51 | # See spec/api_spec.rb for usage examples 52 | def get_subscription_list(request) 53 | make_request(request, ARBGetSubscriptionListResponse, Type::API_GET_SUBSCRIPTION_LIST) 54 | end 55 | 56 | # This request enables you to create a customer profile. 57 | # 58 | # See spec/api_spec.rb for usage examples 59 | def create_customer_profile(request) 60 | make_request(request, CreateCustomerProfileResponse, Type::API_CREATE_CUSTOMER_PROFILE) 61 | end 62 | 63 | # This request enables you to retrieve a customer profile. 64 | # 65 | # See spec/api_spec.rb for usage examples 66 | def get_customer_profile(request) 67 | make_request(request, GetCustomerProfileResponse, Type::API_GET_CUSTOMER_PROFILE) 68 | end 69 | 70 | # This request enables you to retrieve all customer profile IDs. 71 | # 72 | # See spec/api_spec.rb for usage examples 73 | def get_customer_profile_ids(request) 74 | make_request(request, GetCustomerProfileIdsResponse, Type::API_GET_CUSTOMER_PROFILE_IDS) 75 | end 76 | 77 | # This request enables you to update a customer profile. 78 | # 79 | # See spec/api_spec.rb for usage examples 80 | def update_customer_profile(request) 81 | make_request(request, UpdateCustomerProfileResponse, Type::API_UPDATE_CUSTOMER_PROFILE) 82 | end 83 | 84 | # This request enables you to delete a customer profile. 85 | # 86 | # See spec/api_spec.rb for usage examples 87 | def delete_customer_profile(request) 88 | make_request(request, DeleteCustomerProfileResponse, Type::API_DELETE_CUSTOMER_PROFILE) 89 | end 90 | 91 | # This request enables you to create a customer payment profile. 92 | # 93 | # See spec/api_spec.rb for usage examples 94 | def create_customer_payment_profile(request) 95 | make_request(request, CreateCustomerPaymentProfileResponse, Type::API_CREATE_CUSTOMER_PAYMENT_PROFILE) 96 | end 97 | 98 | # This request enables you to retrieve a customer payment profile. 99 | # 100 | # See spec/api_spec.rb for usage examples 101 | def get_customer_payment_profile(request) 102 | make_request(request, GetCustomerPaymentProfileResponse, Type::API_GET_CUSTOMER_PAYMENT_PROFILE) 103 | end 104 | 105 | # This request enables you to update a customer payment profile. 106 | # 107 | # See spec/api_spec.rb for usage examples 108 | def update_customer_payment_profile(request) 109 | make_request(request, UpdateCustomerPaymentProfileResponse, Type::API_UPDATE_CUSTOMER_PAYMENT_PROFILE) 110 | end 111 | 112 | # This request enables you to delete a customer payment profile. 113 | # 114 | # See spec/api_spec.rb for usage examples 115 | def delete_customer_payment_profile(request) 116 | make_request(request, DeleteCustomerPaymentProfileResponse, Type::API_DELETE_CUSTOMER_PAYMENT_PROFILE) 117 | end 118 | 119 | # This request enables you to create a customer shipping profile. 120 | # 121 | # See spec/api_spec.rb for usage examples 122 | def create_customer_shipping_profile(request) 123 | make_request(request, CreateCustomerShippingAddressResponse, Type::API_CREATE_CUSTOMER_SHIPPING_PROFILE) 124 | end 125 | 126 | # This request enables you to retrieve a customer shipping profile. 127 | # 128 | # See spec/api_spec.rb for usage examples 129 | def get_customer_shipping_profile(request) 130 | make_request(request, GetCustomerShippingAddressResponse, Type::API_GET_CUSTOMER_SHIPPING_PROFILE) 131 | end 132 | 133 | # This request enables you to update a customer shipping profile. 134 | # 135 | # See spec/api_spec.rb for usage examples 136 | def update_customer_shipping_profile(request) 137 | make_request(request, UpdateCustomerShippingAddressResponse, Type::API_UPDATE_CUSTOMER_SHIPPING_PROFILE) 138 | end 139 | 140 | # This request enables you to delete a customer shipping profile. 141 | # 142 | # See spec/api_spec.rb for usage examples 143 | def delete_customer_shipping_profile(request) 144 | make_request(request, DeleteCustomerShippingAddressResponse, Type::API_DELETE_CUSTOMER_SHIPPING_PROFILE) 145 | end 146 | 147 | # This request enables you to create a customer profile, payment 148 | # profile, and shipping profile from an existing successful transaction. 149 | # NOTE: Network tokenized transactions (e.g. Apple Pay), or PayPal 150 | # should not be used to create payment profiles. 151 | # 152 | # See spec/api_spec.rb for usage examples 153 | def create_customer_profile_from_transaction(request) 154 | make_request(request, CreateProfileResponse, Type::API_CREATE_CUSTOMER_PROFILE_FROM_TRANSACTION) 155 | end 156 | 157 | # This request enables you to decrypt a payment data blob. 158 | # 159 | # See spec/api_spec.rb for usage examples 160 | def decrypt_payment_data(request) 161 | make_request(request, DecryptPaymentDataResponse, Type::API_DECRYPT_PAYMENT_DATA) 162 | end 163 | 164 | # This request enables confirm authentication values. 165 | # 166 | # See spec/api_spec.rb for usage examples 167 | def authenticate_test_request(request) 168 | make_request(request, AuthenticateTestResponse, Type::API_AUTHENTICATE_TEST_REQUEST) 169 | end 170 | 171 | # This request enables you to get batch statistics details. 172 | # 173 | # See spec/api_spec.rb for usage examples 174 | def get_batch_statistics(request) 175 | make_request(request, GetBatchStatisticsResponse, Type::API_GET_BATCH_STATISTICS) 176 | end 177 | 178 | # This request enables you to get batch transaction details. 179 | # 180 | # See spec/api_spec.rb for usage examples 181 | def get_transaction_details(request) 182 | make_request(request, GetTransactionDetailsResponse, Type::API_GET_TRANSACTION_DETAILS) 183 | end 184 | 185 | # This request enables you to get batch settled transaction details. 186 | # 187 | # See spec/api_spec.rb for usage examples 188 | def get_settled_batch_list(request) 189 | make_request(request, GetSettledBatchListResponse, Type::API_GET_SETTLED_BATCH_LIST) 190 | end 191 | 192 | # This request enables you to get batch unsettled transaction details. 193 | # 194 | # See spec/api_spec.rb for usage examples 195 | def get_unsettled_transaction_list(request) 196 | make_request(request, GetUnsettledTransactionListResponse, Type::API_GET_UNSETTLED_TRANSACTION_LIST) 197 | end 198 | 199 | # This request enables confirm authentication values. 200 | # 201 | # See spec/api_spec.rb for usage examples 202 | def get_hosted_profile_page(request) 203 | make_request(request, GetHostedProfilePageResponse, Type::API_GET_HOSTED_PROFILE_PAGE) 204 | end 205 | 206 | # This request enables you to update split tender group. 207 | # 208 | # See spec/api_spec.rb for usage examples 209 | def update_split_tender_group(request) 210 | make_request(request, UpdateSplitTenderGroupResponse, Type::API_UPDATE_SPLIT_TENDER_GROUP) 211 | end 212 | 213 | # This request enables you to get customer payment profile list. 214 | # 215 | # See spec/api_spec.rb for usage examples 216 | def get_customer_payment_profile_list(request) 217 | make_request(request, GetCustomerPaymentProfileListResponse, Type::API_GET_CUSTOMER_PAYMENT_PROFILE_LIST) 218 | end 219 | 220 | # This request enables you to get ARB Subscription. 221 | # 222 | # See spec/api_spec.rb for usage examples 223 | def arb_get_subscription_request(request) 224 | make_request(request, ARBGetSubscriptionResponse, Type::API_ARB_GET_SUBSCRIPTION_REQUEST) 225 | end 226 | 227 | # This request enables you to get transaction list. 228 | # 229 | # See spec/api_spec.rb for usage examples 230 | def get_transaction_list(request) 231 | make_request(request, GetTransactionListResponse, Type::API_GET_TRANSACTION_LIST) 232 | end 233 | 234 | # This request enables you to validate customer payment profile. 235 | # 236 | # See spec/api_spec.rb for usage examples 237 | def validate_customer_payment_profile(request) 238 | make_request(request, ValidateCustomerPaymentProfileResponse, Type::API_VALIDATE_CUSTOMER_PAYMENT_PROFILE) 239 | end 240 | 241 | def get_merchant_details(request) 242 | make_request(request, GetMerchantDetailsResponse, Type::API_GET_MERCHANT_DETAILS) 243 | end 244 | 245 | def get_hosted_payment_page(request) 246 | make_request(request, GetHostedPaymentPageResponse, Type::API_GET_HOSTED_PAYMENT_PAGE) 247 | end 248 | 249 | def update_held_transaction(request) 250 | make_request(request, UpdateHeldTransactionResponse, Type::API_UDPATE_HELD_TRANSACTION) 251 | end 252 | 253 | def get_transaction_list_for_customer(request) 254 | make_request(request, GetTransactionListResponse, Type::API_GET_TRANSACTION_LIST_FOR_CUSTOMER) 255 | end 256 | 257 | def update_merchant_details(request) 258 | make_request(request, UpdateMerchantDetailsResponse, Type::API_UPDATE_MERCHANT_DETAILS) 259 | end 260 | 261 | def get_customer_payment_profile_nonce(request) 262 | make_request(request, GetCustomerPaymentProfileNonceResponse, Type::API_GET_CUSTOMER_PAYMENT_PROFILE_NONCE) 263 | end 264 | end 265 | end 266 | -------------------------------------------------------------------------------- /lib/authorize_net/authorize_net.rb: -------------------------------------------------------------------------------- 1 | # :title: Authorize.Net Ruby SDK 2 | # The core AuthoizeNet module. 3 | # The entire SDK is name-spaced inside of this module. 4 | module AuthorizeNet 5 | # Some type conversion routines that will be injected into our 6 | # Transaction/Response classes. 7 | module TypeConversions 8 | API_FIELD_PREFIX = 'x_'.freeze 9 | 10 | # Converts a value received from Authorize.Net into a boolean if 11 | # possible. This is designed to handle the wide range of boolean 12 | # formats that Authorize.Net uses. 13 | def value_to_boolean(value) 14 | case value 15 | when "TRUE", "T", "YES", "Y", "1", "true" 16 | true 17 | when "FALSE", "F", "NO", "N", "0", "false" 18 | false 19 | else 20 | value 21 | end 22 | end 23 | 24 | # Converts a boolean into an Authorize.Net boolean value string. 25 | # This is designed to handle the wide range of boolean formats that 26 | # Authorize.Net uses. If bool isn't a Boolean, its converted to a 27 | # string and passed along. 28 | def boolean_to_value(bool) 29 | case bool 30 | when TrueClass, FalseClass 31 | bool ? 'TRUE' : 'FALSE' 32 | else 33 | bool.to_s 34 | end 35 | end 36 | 37 | # Converts a value received from Authorize.Net into a BigDecimal. 38 | def value_to_decimal(value) 39 | value = 0 if value == '' # Ruby 2.4+ does not accept "" 40 | BigDecimal(value) 41 | end 42 | 43 | # Converts a BigDecimal (or Float) into an Authorize.Net float value 44 | # string. If float isn't a BigDecimal (or Float), its converted to a 45 | # string and passed along. 46 | def decimal_to_value(float) 47 | case float 48 | when Float 49 | format("%0.2f", float) 50 | when BigDecimal 51 | float.truncate(2).to_s('F') 52 | else 53 | float.to_s 54 | end 55 | end 56 | 57 | # Converts a value received from Authorize.Net into a Date. 58 | def value_to_date(value) 59 | Date.strptime(value, '%Y-%m-%d') 60 | end 61 | 62 | # Converts a Date (or DateTime, or Time) into an Authorize.Net date 63 | # value string. If date isn't a Date (or DateTime, or Time), its 64 | # converted to a string and passed along. 65 | def date_to_value(date) 66 | case date 67 | when Date, DateTime, Time 68 | date.strftime('%Y-%m-%d') 69 | else 70 | date.to_s 71 | end 72 | end 73 | 74 | # Converts a value received from Authorize.Net into a DateTime. 75 | def value_to_datetime(value) 76 | DateTime.strptime(value, '%Y-%m-%dT%H:%M:%S') 77 | end 78 | 79 | # Converts a Date (or DateTime, or Time) into an Authorize.Net datetime 80 | # value string. If date isn't a Date (or DateTime, or Time), it's 81 | # converted to a string and passed along. 82 | def datetime_to_value(datetime) 83 | case datetime 84 | when Date, DateTime 85 | datetime.new_offset(0).strftime('%Y-%m-%dT%H:%M:%SZ') 86 | when Time 87 | datetime.utc.strftime('%Y-%m-%dT%H:%M:%SZ') 88 | else 89 | datetime.to_s 90 | end 91 | end 92 | 93 | # Converts a value received from Authorize.Net into an Integer. 94 | def value_to_integer(value) 95 | value.to_s.to_i 96 | end 97 | 98 | # Converts an Integer into an Authorize.Net integer string. 99 | def integer_to_value(int) 100 | int.to_s 101 | end 102 | 103 | # Converts a key value pair into a HTTP POST parameter. The key is 104 | # prefixed with key_prefix when being converted to a parameter name. 105 | def to_param(key, value, key_prefix = API_FIELD_PREFIX) 106 | key_str = "#{key_prefix}#{key}=" 107 | if value.is_a?(Array) 108 | (value.collect do |v| 109 | key_str + CGI.escape(v.to_s) 110 | end).join('&') 111 | else 112 | key_str + CGI.escape(value.to_s) 113 | end 114 | end 115 | 116 | # Converts an internal field name (Symbol) into an external field 117 | # name (Symbol) that can be consumed by the Authorize.Net API. 118 | def to_external_field(key) 119 | (API_FIELD_PREFIX + key.to_s).to_sym 120 | end 121 | 122 | # Converts an external field name (Symbol) into an internal field 123 | # name (Symbol). This is the exact inverse of to_external_field. 124 | # Running to_internal_field(to_external_field(:foo)) would return 125 | # :foo back. 126 | def to_internal_field(key) 127 | k_str = key.to_s 128 | k_str[API_FIELD_PREFIX.length..k_str.length].to_sym 129 | end 130 | end 131 | 132 | # Provides some basic methods used by the various model classes. 133 | module Model 134 | # The constructor for models. Takes any of the supported attributes 135 | # as key/value pairs. 136 | def initialize(fields = {}) 137 | fields.each do |k, v| 138 | method_name = (k.to_s + '=').to_sym 139 | send(method_name, v) if respond_to?(method_name) 140 | end 141 | end 142 | 143 | def to_a 144 | [self] 145 | end 146 | 147 | #:enddoc: 148 | protected 149 | 150 | def handle_multivalue_hashing(obj) 151 | obj.to_a.collect(&:to_hash) 152 | end 153 | end 154 | end 155 | -------------------------------------------------------------------------------- /lib/authorize_net/response.rb: -------------------------------------------------------------------------------- 1 | module AuthorizeNet 2 | # The core, API agnostic response class. 3 | # You shouldn't instantiate this one. 4 | class Response 5 | include AuthorizeNet::TypeConversions 6 | 7 | # Fields to convert to/from booleans. 8 | @@boolean_fields = [] 9 | 10 | # Fields to convert to/from BigDecimal. 11 | @@decimal_fields = [] 12 | 13 | # DO NOT USE. 14 | def initialize 15 | raise "#{self.class} should not be instantiated directly." 16 | end 17 | 18 | # Check to see if the response indicated success. 19 | def success? 20 | false 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/authorize_net/transaction.rb: -------------------------------------------------------------------------------- 1 | module AuthorizeNet 2 | # The core, API agnostic transaction class. 3 | # You shouldn't instantiate this one. 4 | class Transaction 5 | include AuthorizeNet::TypeConversions 6 | 7 | # Fields to convert to/from booleans. 8 | @@boolean_fields = [] 9 | 10 | # Fields to convert to/from BigDecimal. 11 | @@decimal_fields = [] 12 | 13 | # DO NOT USE. 14 | def initialize 15 | @fields ||= {} 16 | end 17 | 18 | # Sets arbitrary API fields, overwriting existing values if they exist. 19 | # Takes a hash of key/value pairs, where the keys are the field names 20 | # without the "x_" prefix. You can set a field to Nil to unset it. If the 21 | # value is an array, each value in the array will be added. For example, 22 | # set_fields({:line_item => ["item1<|>golf balls<|><|>2<|>18.95<|>Y", 23 | # "item2<|>golf bag<|>Wilson golf carry bag, red<|>1<|>39.99<|>"]}) 24 | # would generate two x_line_item fields in the transaction, one for 25 | # each value in the array. 26 | def set_fields(fields = {}) 27 | @fields.merge!(fields) 28 | @fields.reject! { |_k, v| v.nil? } 29 | @fields 30 | end 31 | 32 | # Returns the current hash of API fields. 33 | attr_reader :fields 34 | 35 | # Takes an instance of AuthorizeNet::Address and adds it to the transaction. 36 | def set_address(address) 37 | @fields.merge!(address.to_hash) 38 | end 39 | 40 | # Takes an instance of AuthorizeNet::ShippingAddress and adds it to the 41 | # transaction. 42 | def set_shipping_address(address) 43 | @fields.merge!(address.to_hash) 44 | end 45 | 46 | # Takes an instance of AuthorizeNet::Customer and adds it to the transaction. 47 | def set_customer(customer) 48 | @fields.merge!(customer.to_hash) 49 | end 50 | 51 | #:enddoc: 52 | protected 53 | 54 | # Internal method to handle multiple types of payment arguments. 55 | def handle_payment_argument(payment) 56 | case payment 57 | when AuthorizeNet::CreditCard, AuthorizeNet::ECheck 58 | set_fields(payment.to_hash) 59 | else 60 | set_fields(card_num: payment) 61 | end 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/authorize_net/xml_response.rb: -------------------------------------------------------------------------------- 1 | module AuthorizeNet 2 | # The core, xml response class. You shouldn't instantiate this one. 3 | # Instead you should use AuthorizeNet::ARB::Response. 4 | class XmlResponse < AuthorizeNet::Response 5 | # DO NOT USE. Instantiate AuthorizeNet::ARB::Response or AuthorizeNet::CIM::Response instead. 6 | def initialize(raw_response, transaction) 7 | @raw_response = raw_response 8 | @transaction = transaction 9 | unless connection_failure? 10 | begin 11 | xml = Nokogiri::XML(@raw_response.body) do |config| 12 | # confirm noent is the right flag 13 | config.recover.noent.nonet 14 | end 15 | @root = xml.children[0] 16 | @result_code = node_content_unless_nil(@root.at_css('messages resultCode')) 17 | @message_code = node_content_unless_nil(@root.at_css('messages message code')) 18 | @message_text = node_content_unless_nil(@root.at_css('messages message text')) 19 | @reference_id = node_content_unless_nil(@root.at_css('refId')) 20 | rescue StandardError 21 | @raw_response = $ERROR_INFO 22 | end 23 | end 24 | end 25 | 26 | # Check to see if the response indicated success. Success is defined as a 200 OK response with a resultCode 27 | # of 'Ok'. 28 | def success? 29 | !connection_failure? && @result_code == 'Ok' 30 | end 31 | 32 | # Returns true if we failed to open a connection to the gateway or got back a non-200 OK HTTP response. 33 | def connection_failure? 34 | !@raw_response.is_a?(Net::HTTPOK) 35 | end 36 | 37 | # Returns the underlying Net::HTTPResponse object. This has the original response body along with 38 | # headers and such. Note that if an exception is generated while making the request (which happens 39 | # if there is no internet connection for example), you will get the exception object here instead of 40 | # a Net::HTTPResponse object. 41 | def raw 42 | @raw_response 43 | end 44 | 45 | # Returns a deep-copy of the XML object received from the payment gateway. Or nil if there was no XML payload. 46 | def xml 47 | @root.dup unless @root.nil? 48 | end 49 | 50 | # Returns the resultCode from the XML response. resultCode will be either 'Ok' or 'Error'. 51 | attr_reader :result_code 52 | 53 | # Returns the messageCode from the XML response. This is a code indicating the details of an error 54 | # or success. 55 | attr_reader :message_code 56 | 57 | # Returns the messageText from the XML response. This is a text description of the message_code. 58 | attr_reader :message_text 59 | 60 | # Alias for result_code. 61 | def response_code 62 | result_code 63 | end 64 | 65 | # Alias for message_code. 66 | def response_reason_code 67 | message_code 68 | end 69 | 70 | # Alias for message_text. 71 | def response_reason_text 72 | message_text 73 | end 74 | 75 | # Returns the refId from the response if there is one. Otherwise returns nil. 76 | attr_reader :reference_id 77 | 78 | #:enddoc: 79 | protected 80 | 81 | def node_content_unless_nil(node) 82 | if node.nil? 83 | nil 84 | else 85 | node.content 86 | end 87 | end 88 | 89 | def node_child_content_unless_nil(node) 90 | if node.nil? 91 | nil 92 | else 93 | node.children.collect(&:content) unless node.children.empty? 94 | end 95 | end 96 | 97 | # Transforms a block of XML into a model Object defined by entity_desc. 98 | def build_entity(xml, entity_desc) 99 | args = {} 100 | entity_desc.node_structure.each do |node_desc| 101 | node_name = (node_desc.keys.reject { |k| k.to_s[0..0] == '_' }).first 102 | args.merge!(handle_node_type(xml, node_desc, node_name, args, '')) 103 | end 104 | 105 | return nil if args.empty? 106 | 107 | if entity_desc.arg_mapping.nil? 108 | return entity_desc.entity_class.new(args) 109 | else 110 | args_list = [] 111 | entity_desc.arg_mapping.each do |arg| 112 | args_list <<= args[arg] 113 | args.delete(arg) 114 | end 115 | args_list <<= args 116 | return entity_desc.entity_class.new(*args_list) 117 | end 118 | end 119 | 120 | # Parses an XML fragment into an internal representation. 121 | def handle_node_type(xml, node_desc, node_name, args, base_name) 122 | case node_desc[node_name] 123 | when Symbol 124 | node = xml.at_css(base_name + node_name.to_s) 125 | unless node.nil? 126 | content = node.content 127 | case node_desc[:_converter] 128 | when Method, Proc 129 | content = node_desc[:_converter].call(content) 130 | when Symbol 131 | content = send(node_desc[:_converter], content) 132 | end 133 | args[node_desc[node_name]] = content unless content.nil? 134 | end 135 | when AuthorizeNet::EntityDescription 136 | if node_desc[:_multivalue].nil? 137 | entity = build_entity(xml.css(base_name + node_name.to_s), node_desc[node_name]) 138 | args[node_desc[:_value]] = entity unless entity.nil? 139 | else 140 | xml.css(base_name + node_name.to_s).each do |node| 141 | entity = build_entity(node, node_desc[node_name]) 142 | args[node_desc[:_multivalue]] = args[node_desc[:_multivalue]].to_a + entity.to_a unless entity.nil? 143 | end 144 | end 145 | when Array 146 | node_desc[node_name].each do |inner_node| 147 | inner_node_name = (inner_node.keys.reject { |k| k.to_s[0..0] == '_' }).first 148 | args.merge!(handle_node_type(xml, inner_node, inner_node_name, args, node_name.to_s + ' ')) 149 | end 150 | end 151 | args 152 | end 153 | end 154 | end 155 | -------------------------------------------------------------------------------- /lib/authorize_net/xml_transaction.rb: -------------------------------------------------------------------------------- 1 | module AuthorizeNet 2 | # The ARB transaction class. 3 | class XmlTransaction < AuthorizeNet::Transaction 4 | # The XML namespace used by the ARB API. 5 | XML_NAMESPACE = 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'.freeze 6 | 7 | # Constants for both the various Authorize.Net subscription gateways are defined here. 8 | module Gateway 9 | LIVE = 'https://api2.authorize.net/xml/v1/request.api'.freeze 10 | TEST = 'https://apitest.authorize.net/xml/v1/request.api'.freeze 11 | end 12 | 13 | # Constants for both the various Authorize.Net transaction types are defined here. 14 | module Type 15 | ARB_CREATE = "ARBCreateSubscriptionRequest".freeze 16 | ARB_UPDATE = "ARBUpdateSubscriptionRequest".freeze 17 | ARB_GET_STATUS = "ARBGetSubscriptionStatusRequest".freeze 18 | ARB_CANCEL = "ARBCancelSubscriptionRequest".freeze 19 | ARB_GET_SUBSCRIPTION_LIST = "ARBGetSubscriptionListRequest".freeze 20 | CIM_CREATE_PROFILE = "createCustomerProfileRequest".freeze 21 | CIM_CREATE_PAYMENT = "createCustomerPaymentProfileRequest".freeze 22 | CIM_CREATE_ADDRESS = "createCustomerShippingAddressRequest".freeze 23 | CIM_CREATE_TRANSACTION = "createCustomerProfileTransactionRequest".freeze 24 | CIM_DELETE_PROFILE = "deleteCustomerProfileRequest".freeze 25 | CIM_DELETE_PAYMENT = "deleteCustomerPaymentProfileRequest".freeze 26 | CIM_DELETE_ADDRESS = "deleteCustomerShippingAddressRequest".freeze 27 | CIM_GET_PROFILE_IDS = "getCustomerProfileIdsRequest".freeze 28 | CIM_GET_PROFILE = "getCustomerProfileRequest".freeze 29 | CIM_GET_PAYMENT = "getCustomerPaymentProfileRequest".freeze 30 | CIM_GET_ADDRESS = "getCustomerShippingAddressRequest".freeze 31 | CIM_GET_HOSTED_PROFILE = "getHostedProfilePageRequest".freeze 32 | CIM_UPDATE_PROFILE = "updateCustomerProfileRequest".freeze 33 | CIM_UPDATE_PAYMENT = "updateCustomerPaymentProfileRequest".freeze 34 | CIM_UPDATE_ADDRESS = "updateCustomerShippingAddressRequest".freeze 35 | CIM_UPDATE_SPLIT = "updateSplitTenderGroupRequest".freeze 36 | CIM_VALIDATE_PAYMENT = "validateCustomerPaymentProfileRequest".freeze 37 | REPORT_GET_BATCH_LIST = "getSettledBatchListRequest".freeze 38 | REPORT_GET_TRANSACTION_LIST = "getTransactionListRequest".freeze 39 | REPORT_GET_UNSETTLED_TRANSACTION_LIST = "getUnsettledTransactionListRequest".freeze 40 | REPORT_GET_TRANSACTION_DETAILS = "getTransactionDetailsRequest".freeze 41 | end 42 | 43 | # Fields to convert to/from booleans. 44 | @@boolean_fields = [] 45 | 46 | # Fields to convert to/from BigDecimal. 47 | @@decimal_fields = [] 48 | 49 | # Fields to convert to/from Date. 50 | @@date_fields = [] 51 | 52 | # Fields to convert to/from DateTime. 53 | @@datetime_fields = [] 54 | 55 | # The class to wrap our response in. 56 | @response_class = AuthorizeNet::XmlResponse 57 | 58 | # The default options for the constructor. 59 | @@option_defaults = { 60 | gateway: :production, 61 | verify_ssl: true, 62 | reference_id: nil 63 | } 64 | 65 | # DO NOT USE. Instantiate AuthorizeNet::ARB::Transaction or AuthorizeNet::CIM::Transaction instead. 66 | def initialize(api_login_id, api_transaction_key, options = {}) 67 | super() 68 | @api_login_id = api_login_id 69 | @api_transaction_key = api_transaction_key 70 | 71 | @response ||= nil 72 | @type ||= nil 73 | 74 | options = @@option_defaults.merge(options) 75 | @verify_ssl = options[:verify_ssl] 76 | @reference_id = options[:reference_id] 77 | @gateway = case options[:gateway].to_s 78 | when 'sandbox', 'test' 79 | Gateway::TEST 80 | when 'production', 'live' 81 | Gateway::LIVE 82 | else 83 | @gateway = options[:gateway] 84 | options[:gateway] 85 | end 86 | end 87 | 88 | def setOAuthOptions 89 | unless @options_OAuth.blank? 90 | @options_OAuth = @@option_defaults.merge(@options_OAuth) 91 | @verify_ssl = options_OAuth[:verify_ssl] 92 | @reference_id = options_OAuth[:reference_id] 93 | 94 | @gateway = case options_OAuth[:gateway].to_s 95 | when 'sandbox', 'test' 96 | Gateway::TEST 97 | when 'production', 'live' 98 | Gateway::LIVE 99 | else 100 | @gateway = options_OAuth[:gateway] 101 | options_OAuth[:gateway] 102 | end 103 | end 104 | end 105 | 106 | # Checks if the transaction has been configured for the sandbox or not. Return FALSE if the 107 | # transaction is running against the production, TRUE otherwise. 108 | def test? 109 | @gateway != Gateway::LIVE 110 | end 111 | 112 | # Checks to see if the transaction has a response (meaning it has been submitted to the gateway). 113 | # Returns TRUE if a response is present, FALSE otherwise. 114 | def has_response? 115 | !@response.nil? 116 | end 117 | 118 | # Retrieve the response object (or Nil if transaction hasn't been sent to the gateway). 119 | attr_reader :response 120 | 121 | # Submits the transaction to the gateway for processing. Returns a response object. If the transaction 122 | # has already been run, it will return nil. 123 | def run 124 | make_request 125 | end 126 | 127 | # Returns a deep-copy of the XML object sent to the payment gateway. Or nil if there was no XML payload. 128 | attr_reader :xml 129 | 130 | #:enddoc: 131 | protected 132 | 133 | # Takes a list of nodes (a Hash is a node, and Array is a list) and returns True if any nodes 134 | # would be built by build_nodes. False if no new nodes would be generated. 135 | def has_content(nodeList, data) 136 | nodeList.each do |node| 137 | nodeName = (node.keys.reject { |_k| nodeName.to_s[0..0] == '_' }).first 138 | multivalue = node[:_multivalue] 139 | conditional = node[:_conditional] 140 | value = node[nodeName] 141 | value = send(conditional, nodeName) unless conditional.nil? 142 | case value 143 | when Array 144 | if multivalue.nil? 145 | return true if has_content(value, data) 146 | else 147 | data[multivalue].each do |v| 148 | return true if has_content(value, v) 149 | end 150 | end 151 | when Symbol 152 | converted = convert_field(value, data[value]) 153 | return true unless converted.nil? 154 | else 155 | return true 156 | end 157 | end 158 | false 159 | end 160 | 161 | # Takes a list of nodes (a Hash is a node, and Array is a list) and recursively builds the XML by pulling 162 | # values as needed from data. 163 | def build_nodes(builder, nodeList, data) 164 | nodeList.each do |node| 165 | # TODO: - ADD COMMENTS HERE 166 | nodeName = (node.keys.reject { |k| k.to_s[0..0] == '_' }).first 167 | multivalue = node[:_multivalue] 168 | conditional = node[:_conditional] 169 | value = node[nodeName] 170 | 171 | value = send(conditional, nodeName) unless conditional.nil? 172 | case value 173 | when Array # node containing other nodes 174 | if multivalue.nil? 175 | proc = proc { build_nodes(builder, value, data) } 176 | builder.send(nodeName, &proc) if has_content(value, data) 177 | else 178 | data[multivalue].to_a.each do |v| 179 | proc = proc { build_nodes(builder, value, v) } 180 | builder.send(nodeName, &proc) if has_content(value, v) 181 | end 182 | end 183 | when Symbol # node containing actual data 184 | if data[value].is_a?(Array) 185 | data[value].each do |v| 186 | converted = convert_field(value, v) 187 | builder.send(nodeName, converted) unless converted.nil? 188 | end 189 | else 190 | converted = convert_field(value, data[value]) 191 | builder.send(nodeName, converted) unless converted.nil? 192 | end 193 | else 194 | builder.send(nodeName, value) 195 | end 196 | end 197 | end 198 | 199 | def convert_field(field, value) 200 | if @@boolean_fields.include?(field) && !value.nil? 201 | return boolean_to_value(value) 202 | elsif @@decimal_fields.include?(field) && !value.nil? 203 | return decimal_to_value(value) 204 | elsif @@date_fields.include?(field) && !value.nil? 205 | return date_to_value(value) 206 | elsif @@datetime_fields.include?(field) && !value.nil? 207 | return datetime_to_value(value) 208 | elsif field == :extra_options 209 | # handle converting extra options 210 | options = [] 211 | value.each_pair { |k, v| options <<= to_param(k, v) } unless value.nil? 212 | unless @custom_fields.nil? 213 | # special sort to maintain compatibility with AIM custom field ordering 214 | # FIXME - This should be DRY'd up. 215 | custom_field_keys = @custom_fields.keys.collect(&:to_s).sort.collect(&:to_sym) 216 | for key in custom_field_keys 217 | options <<= to_param(key, @custom_fields[key.to_sym], '') 218 | end 219 | end 220 | 221 | if !options.empty? 222 | return options.join('&') 223 | else 224 | return nil 225 | end 226 | elsif field == :exp_date 227 | # convert MMYY expiration dates into the XML equivalent 228 | unless value.nil? 229 | begin 230 | return value.to_s.casecmp('xxxx').zero? ? 'XXXX' : Date.strptime(value.to_s, '%m%y').strftime('%Y-%m') 231 | rescue StandardError 232 | # If we didn't get the exp_date in MMYY format, try our best to convert it 233 | return Date.parse(value.to_s).strftime('%Y-%m') 234 | end 235 | end 236 | end 237 | 238 | value 239 | end 240 | 241 | # An internal method that builds the POST body, submits it to the gateway, and constructs a Response object with the response. 242 | def make_request 243 | return nil if has_response? 244 | 245 | fields = @fields 246 | 247 | builder = Nokogiri::XML::Builder.new(encoding: 'utf-8') do |x| 248 | x.send(@type.to_sym, xmlns: XML_NAMESPACE) do 249 | x.merchantAuthentication do 250 | x.name @api_login_id 251 | x.transactionKey @api_transaction_key 252 | end 253 | build_nodes(x, self.class.const_get(:FIELDS)[@type], fields) 254 | end 255 | end 256 | @xml = builder.to_xml 257 | 258 | url = URI.parse(@gateway) 259 | 260 | request = Net::HTTP::Post.new(url.path) 261 | request.content_type = 'text/xml' 262 | request.body = @xml 263 | connection = Net::HTTP.new(url.host, url.port) 264 | connection.use_ssl = true 265 | if @verify_ssl 266 | connection.verify_mode = OpenSSL::SSL::VERIFY_PEER 267 | else 268 | connection.verify_mode = OpenSSL::SSL::VERIFY_NONE 269 | end 270 | 271 | # Use our Class's @response_class variable to find the Response class we are supposed to use. 272 | begin 273 | @response = self.class.instance_variable_get(:@response_class).new((connection.start { |http| http.request(request) }), self) 274 | rescue StandardError 275 | @response = self.class.instance_variable_get(:@response_class).new($ERROR_INFO, self) 276 | end 277 | end 278 | end 279 | end 280 | -------------------------------------------------------------------------------- /lib/authorizenet.rb: -------------------------------------------------------------------------------- 1 | # This file is just here to avoid obnoxious gem name/require name issues. All this 2 | # file does is require authorize_net.rb, the real initialization file. 3 | 4 | require 'authorize_net' 5 | -------------------------------------------------------------------------------- /spec/api_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | require "active_support" 3 | include AuthorizeNet::API 4 | 5 | describe Transaction do 6 | before :all do 7 | begin 8 | creds = credentials 9 | @api_key = creds['api_transaction_key'] 10 | @api_login = creds['api_login_id'] 11 | @gateway = :sandbox 12 | rescue Errno::ENOENT => e 13 | @api_key = "TEST" 14 | @api_login = "TEST" 15 | warn "WARNING: Running w/o valid AuthorizeNet sandbox credentials. Create spec/credentials.yml." 16 | end 17 | end 18 | 19 | before do 20 | @transaction = Transaction.new(@api_login, @api_key, gateway: @gateway) 21 | @amount = rand(100..10_099) / 100.0 22 | @expDate = '01' + (Time.now + (3600 * 24 * 365)).strftime('%y') 23 | @validCcNum = '4111111111111111' 24 | create_transaction_request 25 | create_transaction_response 26 | end 27 | 28 | it "should be able to run a test request" do 29 | @testRequest = AuthenticateTestRequest.new 30 | 31 | response = @transaction.authenticate_test_request(@testRequest) 32 | 33 | expect(response).not_to eq(nil) 34 | expect(response.messages).not_to eq(nil) 35 | expect(response.messages.resultCode).not_to eq(nil) 36 | expect(response.messages.resultCode).to eq(MessageTypeEnum::Ok) 37 | 38 | transaction = Transaction.new(@api_login, @api_key, gateway: 'sandbox') 39 | response = transaction.authenticate_test_request(@testRequest) 40 | 41 | expect(response).not_to eq(nil) 42 | expect(response.messages).not_to eq(nil) 43 | expect(response.messages.resultCode).not_to eq(nil) 44 | expect(response.messages.resultCode).to eq(MessageTypeEnum::Ok) 45 | end 46 | 47 | it "should be able to run credit card transaction" do 48 | @createTransactionRequest = CreateTransactionRequest.new 49 | @createTransactionRequest.transactionRequest = TransactionRequestType.new 50 | @createTransactionRequest.transactionRequest.amount = @amount 51 | @createTransactionRequest.transactionRequest.payment = PaymentType.new 52 | @createTransactionRequest.transactionRequest.payment.creditCard = CreditCardType.new(@validCcNum, @expDate, '123') 53 | @createTransactionRequest.transactionRequest.transactionType = TransactionTypeEnum::AuthCaptureTransaction 54 | 55 | response = @transaction.create_transaction(@createTransactionRequest) 56 | 57 | expect(response).not_to eq(nil) 58 | expect(response.messages).not_to eq(nil) 59 | expect(response.messages.resultCode).not_to eq(nil) 60 | expect(response.transactionResponse).not_to eq(nil) 61 | end 62 | 63 | it "should be able to run echeck transaction" do 64 | @createTransactionRequest = CreateTransactionRequest.new 65 | @createTransactionRequest.transactionRequest = TransactionRequestType.new 66 | @createTransactionRequest.transactionRequest.amount = @amount 67 | @createTransactionRequest.transactionRequest.payment = PaymentType.new 68 | @createTransactionRequest.transactionRequest.payment.bankAccount = BankAccountType.new("checking", "125000024", "123456789", "name", EcheckTypeEnum::WEB, "123", "123") 69 | @createTransactionRequest.transactionRequest.transactionType = TransactionTypeEnum::AuthCaptureTransaction 70 | 71 | response = @transaction.create_transaction(@createTransactionRequest) 72 | 73 | expect(response).not_to eq(nil) 74 | expect(response.messages).not_to eq(nil) 75 | expect(response.messages.resultCode).not_to eq(nil) 76 | expect(response.transactionResponse).not_to eq(nil) 77 | end 78 | 79 | it "should be able to run apple pay transaction" do 80 | @createTransactionRequest = CreateTransactionRequest.new 81 | @createTransactionRequest.transactionRequest = TransactionRequestType.new 82 | @createTransactionRequest.transactionRequest.amount = @amount 83 | @createTransactionRequest.transactionRequest.payment = PaymentType.new 84 | @createTransactionRequest.transactionRequest.payment.opaqueData = OpaqueDataType.new("dataDescriptor", "dataValue") 85 | @createTransactionRequest.transactionRequest.transactionType = TransactionTypeEnum::AuthCaptureTransaction 86 | 87 | response = @transaction.create_transaction(@createTransactionRequest) 88 | 89 | expect(response).not_to eq(nil) 90 | expect(response.messages).not_to eq(nil) 91 | expect(response.messages.resultCode).not_to eq(nil) 92 | expect(response.transactionResponse).not_to eq(nil) 93 | end 94 | 95 | it "should be able to run a Visa Checkout transaction" do 96 | @createTransactionRequest = CreateTransactionRequest.new 97 | @createTransactionRequest.transactionRequest = TransactionRequestType.new 98 | @createTransactionRequest.transactionRequest.amount = @amount 99 | @createTransactionRequest.transactionRequest.payment = PaymentType.new 100 | @createTransactionRequest.transactionRequest.payment.opaqueData = OpaqueDataType.new("dataDescriptor", "dataValue", "dataKey") 101 | @createTransactionRequest.transactionRequest.transactionType = TransactionTypeEnum::AuthCaptureTransaction 102 | 103 | response = @transaction.create_transaction(@createTransactionRequest) 104 | 105 | expect(response).not_to eq(nil) 106 | expect(response.messages).not_to eq(nil) 107 | expect(response.messages.resultCode).not_to eq(nil) 108 | expect(response.transactionResponse).not_to eq(nil) 109 | end 110 | 111 | it "should be able to run paypal transaction" do 112 | @createTransactionRequest = CreateTransactionRequest.new 113 | @createTransactionRequest.transactionRequest = TransactionRequestType.new 114 | @createTransactionRequest.transactionRequest.amount = @amount 115 | @createTransactionRequest.transactionRequest.payment = PaymentType.new 116 | @createTransactionRequest.transactionRequest.payment.payPal = PayPalType.new("123", "123", "123", "123", "123", "123") 117 | @createTransactionRequest.transactionRequest.transactionType = TransactionTypeEnum::AuthCaptureTransaction 118 | 119 | response = @transaction.create_transaction(@createTransactionRequest) 120 | 121 | expect(response).not_to eq(nil) 122 | expect(response.messages).not_to eq(nil) 123 | expect(response.messages.resultCode).not_to eq(nil) 124 | end 125 | 126 | it "should return error when connection is not available" do 127 | @transaction = Transaction.new(@api_login, @api_key, gateway: "wrong gateway") 128 | response = @transaction.create_transaction(@createTransactionRequest) 129 | expect(response).not_to eq(nil) 130 | expect(response).is_a? Exception 131 | expect(response.message).not_to eq(nil) 132 | end 133 | 134 | it "should be able to create a customer profile from existing transaction" do 135 | # create a transaction 136 | createTranReq = CreateTransactionRequest.new 137 | createTranReq.transactionRequest = TransactionRequestType.new 138 | createTranReq.transactionRequest.amount = @amount 139 | createTranReq.transactionRequest.transactionType = TransactionTypeEnum::AuthCaptureTransaction 140 | 141 | createTranReq.transactionRequest.payment = PaymentType.new 142 | createTranReq.transactionRequest.payment.creditCard = CreditCardType.new(@validCcNum, @expDate, '123') 143 | 144 | # customer info needed for profile 145 | createTranReq.transactionRequest.customer = CustomerDataType.new 146 | createTranReq.transactionRequest.customer.email = rand(1_000_000).to_s + "joe@somedomain.ddd" 147 | createTranReq.transactionRequest.customer.id = rand(1_000_000).to_s 148 | 149 | # send the request and get response 150 | createTranResp = @transaction.create_transaction(createTranReq) 151 | 152 | # validate the transaction was created 153 | expect(createTranResp.transactionResponse.transId).not_to eq("0") 154 | 155 | # create profile from this transaction 156 | createProfReq = CreateCustomerProfileFromTransactionRequest.new 157 | createProfReq.transId = createTranResp.transactionResponse.transId 158 | createProfResp = @transaction.create_customer_profile_from_transaction(createProfReq) 159 | 160 | expect(createProfResp).not_to eq(nil) 161 | unless createProfResp.messages.resultCode == MessageTypeEnum::Ok 162 | puts createProfResp.messages.messages[0].text, createProfReq.transId 163 | end 164 | expect(createProfResp.messages.resultCode).to eq(MessageTypeEnum::Ok) 165 | expect(createProfResp.customerProfileId).not_to eq(nil) 166 | expect(createProfResp.customerPaymentProfileIdList).not_to eq(nil) 167 | 168 | # delete the profile for the test repeatability 169 | deleteProfReq = DeleteCustomerProfileRequest.new 170 | deleteProfReq.customerProfileId = createProfResp.customerProfileId 171 | @transaction.delete_customer_profile(deleteProfReq) 172 | end 173 | 174 | it "should be able to create a customer profile with a new transaction" do 175 | # create a transaction 176 | createTranReq = CreateTransactionRequest.new 177 | createTranReq.transactionRequest = TransactionRequestType.new 178 | createTranReq.transactionRequest.amount = @amount 179 | createTranReq.transactionRequest.transactionType = TransactionTypeEnum::AuthCaptureTransaction 180 | 181 | createTranReq.transactionRequest.payment = PaymentType.new 182 | createTranReq.transactionRequest.payment.creditCard = CreditCardType.new(@validCcNum, @expDate, '123') 183 | 184 | # customer info needed for profile 185 | createTranReq.transactionRequest.customer = CustomerDataType.new 186 | createTranReq.transactionRequest.customer.email = rand(1_000_000).to_s + "joe@somedomain.ddd" 187 | createTranReq.transactionRequest.customer.id = rand(1_000_000).to_s 188 | 189 | # set the "createProfile" flag to true 190 | createTranReq.transactionRequest.profile = CustomerProfilePaymentType.new 191 | createTranReq.transactionRequest.profile.createProfile = "true" 192 | 193 | # send the request and get response 194 | createTranResp = @transaction.create_transaction(createTranReq) 195 | 196 | # validate transaction was successful 197 | unless createTranResp.messages.resultCode == MessageTypeEnum::Ok 198 | puts createTranResp.messages.messages[0].text 199 | end 200 | expect(createTranResp.messages.resultCode).to eq(MessageTypeEnum::Ok) 201 | expect(createTranResp.transactionResponse.transId).not_to eq("0") 202 | 203 | # validate customer profile was created 204 | expect(createTranResp.profileResponse).not_to eq(nil) 205 | unless createTranResp.profileResponse.messages.resultCode == MessageTypeEnum::Ok 206 | puts createTranResp.profileResponse.messages.messages[0].text 207 | end 208 | expect(createTranResp.profileResponse.messages.resultCode).to eq(MessageTypeEnum::Ok) 209 | expect(createTranResp.profileResponse.customerProfileId).not_to eq("0") 210 | 211 | # delete customer profile for repeatability 212 | deleteProfReq = DeleteCustomerProfileRequest.new 213 | deleteProfReq.customerProfileId = createTranResp.profileResponse.customerProfileId 214 | @transaction.delete_customer_profile(deleteProfReq) 215 | end 216 | 217 | it "should serialize and deserialize CreateTransactionRequest" do 218 | expected = @createTransactionRequest.transactionRequest 219 | 220 | actual = get_actual(@createTransactionRequest, CreateTransactionRequest, Transaction::Type::API_CREATE_TRANSACTION).transactionRequest 221 | 222 | # validate all properties 223 | expected.amount.should == actual.amount 224 | expected.currencyCode.should == actual.currencyCode 225 | expected.authCode.should == actual.authCode 226 | expected.refTransId.should == actual.refTransId 227 | expected.splitTenderId.should == actual.splitTenderId 228 | expect(expected.taxExempt).to eq(actual.taxExempt) 229 | expected.poNumber.should == actual.poNumber 230 | expected.customerIP.should == actual.customerIP 231 | expected.transactionType.should == actual.transactionType 232 | 233 | # payment 234 | expected.payment.creditCard.cardNumber.should == actual.payment.creditCard.cardNumber 235 | expected.payment.creditCard.expirationDate.should == actual.payment.creditCard.expirationDate 236 | expected.payment.creditCard.isPaymentToken.should == actual.payment.creditCard.isPaymentToken 237 | expected.payment.creditCard.cardCode.should == actual.payment.creditCard.cardCode 238 | 239 | # CustomerProfilePaymentType 240 | expect(expected.profile.createProfile).to eq(actual.profile.createProfile) 241 | expected.profile.customerProfileId.should == actual.profile.customerProfileId 242 | expected.profile.shippingProfileId.should == actual.profile.shippingProfileId 243 | expected.profile.paymentProfile.paymentProfileId.should == actual.profile.paymentProfile.paymentProfileId 244 | expected.profile.paymentProfile.cardCode.should == actual.profile.paymentProfile.cardCode 245 | 246 | # Solution Type 247 | expected.solution.id.should == actual.solution.id 248 | expected.solution.name.should == actual.solution.name 249 | 250 | # OrderType 251 | expected.order.invoiceNumber.should == actual.order.invoiceNumber 252 | expected.order.description.should == actual.order.description 253 | 254 | # LineItemType 255 | expected.lineItems.lineItems.each_with_index do |item, index| 256 | item.itemId.should == actual.lineItems.lineItems[index].itemId 257 | item.name.should == actual.lineItems.lineItems[index].name 258 | item.description.should == actual.lineItems.lineItems[index].description 259 | item.quantity.should == actual.lineItems.lineItems[index].quantity 260 | item.unitPrice.should == actual.lineItems.lineItems[index].unitPrice 261 | expect(item.taxable).to eq(actual.lineItems.lineItems[index].taxable) 262 | end 263 | 264 | # Tax 265 | expected.tax.amount.should == actual.tax.amount 266 | expected.tax.name.should == actual.tax.name 267 | expected.tax.description.should == actual.tax.description 268 | 269 | # Duty 270 | expected.duty.amount.should == actual.duty.amount 271 | expected.duty.name.should == actual.duty.name 272 | expected.duty.description.should == actual.duty.description 273 | 274 | # Shipping 275 | expected.shipping.amount.should == actual.shipping.amount 276 | expected.shipping.name.should == actual.shipping.name 277 | expected.shipping.description.should == actual.shipping.description 278 | 279 | # CustomerDataType 280 | expected.customer.type.should == actual.customer.type 281 | expected.customer.id.should == actual.customer.id 282 | expected.customer.email.should == actual.customer.email 283 | expected.customer.taxId.should == actual.customer.taxId 284 | expected.customer.driversLicense.number.should == actual.customer.driversLicense.number 285 | expected.customer.driversLicense.state.should == actual.customer.driversLicense.state 286 | expected.customer.driversLicense.dateOfBirth.should == actual.customer.driversLicense.dateOfBirth 287 | 288 | # BillTo 289 | expected.billTo.firstName.should == actual.billTo.firstName 290 | expected.billTo.lastName.should == actual.billTo.lastName 291 | expected.billTo.company.should == actual.billTo.company 292 | expected.billTo.address.should == actual.billTo.address 293 | expected.billTo.city.should == actual.billTo.city 294 | expected.billTo.state.should == actual.billTo.state 295 | expected.billTo.zip.should == actual.billTo.zip 296 | expected.billTo.country.should == actual.billTo.country 297 | expected.billTo.phoneNumber.should == actual.billTo.phoneNumber 298 | expected.billTo.faxNumber.should == actual.billTo.faxNumber 299 | 300 | # ShipTo 301 | expected.shipTo.firstName.should == actual.shipTo.firstName 302 | expected.shipTo.lastName.should == actual.shipTo.lastName 303 | expected.shipTo.company.should == actual.shipTo.company 304 | expected.shipTo.address.should == actual.shipTo.address 305 | expected.shipTo.city.should == actual.shipTo.city 306 | expected.shipTo.state.should == actual.shipTo.state 307 | expected.shipTo.zip.should == actual.shipTo.zip 308 | expected.shipTo.country.should == actual.shipTo.country 309 | 310 | # CardHolderAuthentication 311 | expected.cardholderAuthentication.authenticationIndicator.should == actual.cardholderAuthentication.authenticationIndicator 312 | expected.cardholderAuthentication.cardholderAuthenticationValue.should == actual.cardholderAuthentication.cardholderAuthenticationValue 313 | 314 | # Retail 315 | expected.retail.marketType.should == actual.retail.marketType 316 | expected.retail.deviceType.should == actual.retail.deviceType 317 | 318 | # TransactionSettings 319 | expected.transactionSettings.settings.each_with_index do |item, index| 320 | item.settingName.should == actual.transactionSettings.settings[index].settingName 321 | item.settingValue.should == actual.transactionSettings.settings[index].settingValue 322 | end 323 | 324 | # UserFields 325 | expected.userFields.userFields.each_with_index do |item, index| 326 | item.name.should == actual.userFields.userFields[index].name 327 | item.value.should == actual.userFields.userFields[index].value 328 | end 329 | end 330 | 331 | it "should serialize and deserialize CreateTransactionResponse" do 332 | expected = @createTransactionResponse.transactionResponse 333 | 334 | actResponse = get_actual(@createTransactionResponse, CreateTransactionResponse, "createTransactionResponse") 335 | 336 | actual = actResponse.transactionResponse 337 | 338 | # validate 339 | @createTransactionResponse.refId.should == actResponse.refId 340 | @createTransactionResponse.sessionToken.should == actResponse.sessionToken 341 | @createTransactionResponse.messages.resultCode.should == actResponse.messages.resultCode 342 | @createTransactionResponse.messages.messages.each_with_index do |item, index| 343 | item.code.should == actResponse.messages.messages[index].code 344 | item.text.should == actResponse.messages.messages[index].text 345 | end 346 | 347 | expected.responseCode.should == actual.responseCode 348 | expected.rawResponseCode.should == actual.rawResponseCode 349 | expected.authCode.should == actual.authCode 350 | expected.avsResultCode.should == actual.avsResultCode 351 | expected.cvvResultCode.should == actual.cvvResultCode 352 | expected.cavvResultCode.should == actual.cavvResultCode 353 | expected.transId.should == actual.transId 354 | expected.refTransID.should == actual.refTransID 355 | expected.transHash.should == actual.transHash 356 | expected.testRequest.should == actual.testRequest 357 | expected.accountNumber.should == actual.accountNumber 358 | expected.accountType.should == actual.accountType 359 | expected.splitTenderId.should == actual.splitTenderId 360 | 361 | expected.prePaidCard.requestedAmount.should == actual.prePaidCard.requestedAmount 362 | expected.prePaidCard.approvedAmount.should == actual.prePaidCard.approvedAmount 363 | expected.prePaidCard.balanceOnCard.should == actual.prePaidCard.balanceOnCard 364 | 365 | expected.messages.messages.each_with_index do |item, index| 366 | item.code.should == actual.messages.messages[index].code 367 | item.description.should == actual.messages.messages[index].description 368 | end 369 | 370 | expected.errors.errors.each_with_index do |item, index| 371 | item.errorCode.should == actual.errors.errors[index].errorCode 372 | item.errorText.should == actual.errors.errors[index].errorText 373 | end 374 | 375 | expected.splitTenderPayments.splitTenderPayments.each_with_index do |item, index| 376 | item.transId.should == actual.splitTenderPayments.splitTenderPayments[index].transId 377 | item.responseCode.should == actual.splitTenderPayments.splitTenderPayments[index].responseCode 378 | item.responseToCustomer.should == actual.splitTenderPayments.splitTenderPayments[index].responseToCustomer 379 | item.authCode.should == actual.splitTenderPayments.splitTenderPayments[index].authCode 380 | item.accountNumber.should == actual.splitTenderPayments.splitTenderPayments[index].accountNumber 381 | item.accountType.should == actual.splitTenderPayments.splitTenderPayments[index].accountType 382 | item.requestedAmount.should == actual.splitTenderPayments.splitTenderPayments[index].requestedAmount 383 | item.approvedAmount.should == actual.splitTenderPayments.splitTenderPayments[index].approvedAmount 384 | item.balanceOnCard.should == actual.splitTenderPayments.splitTenderPayments[index].balanceOnCard 385 | end 386 | 387 | expected.userFields.userFields.each_with_index do |item, index| 388 | item.name.should == actual.userFields.userFields[index].name 389 | item.value.should == actual.userFields.userFields[index].value 390 | end 391 | 392 | expected.shipTo.firstName.should == actual.shipTo.firstName 393 | expected.shipTo.lastName.should == actual.shipTo.lastName 394 | expected.shipTo.company.should == actual.shipTo.company 395 | expected.shipTo.address.should == actual.shipTo.address 396 | expected.shipTo.city.should == actual.shipTo.city 397 | expected.shipTo.state.should == actual.shipTo.state 398 | expected.shipTo.zip.should == actual.shipTo.zip 399 | expected.shipTo.country.should == actual.shipTo.country 400 | 401 | expected.secureAcceptance.SecureAcceptanceUrl.should == actual.secureAcceptance.SecureAcceptanceUrl 402 | expected.secureAcceptance.PayerID.should == actual.secureAcceptance.PayerID 403 | 404 | @createTransactionResponse.profileResponse.messages.resultCode.should == actResponse.profileResponse.messages.resultCode 405 | @createTransactionResponse.profileResponse.messages.messages.each_with_index do |item, index| 406 | item.code.should == actResponse.profileResponse.messages.messages[index].code 407 | item.text.should == actResponse.profileResponse.messages.messages[index].text 408 | end 409 | 410 | @createTransactionResponse.profileResponse.customerProfileId.should == actResponse.profileResponse.customerProfileId 411 | 412 | @createTransactionResponse.profileResponse.customerPaymentProfileIdList.numericString.each_with_index do |item, index| 413 | item.should == actResponse.profileResponse.customerPaymentProfileIdList.numericString[index] 414 | end 415 | 416 | @createTransactionResponse.profileResponse.customerShippingAddressIdList.numericString.each_with_index do |item, index| 417 | item.should == actResponse.profileResponse.customerShippingAddressIdList.numericString[index] 418 | end 419 | end 420 | 421 | it "should serialize and deserialize BankAccountType" do 422 | expected = BankAccountType.new("123", "123", "123", "name", EcheckTypeEnum::ARC, "123", "123") 423 | actual = get_actual(expected, BankAccountType, "bankAccountType") 424 | 425 | expected.accountType.should == actual.accountType 426 | expected.routingNumber.should == actual.routingNumber 427 | expected.accountNumber.should == actual.accountNumber 428 | expected.nameOnAccount.should == actual.nameOnAccount 429 | expected.echeckType.should == actual.echeckType 430 | expected.bankName.should == actual.bankName 431 | expected.checkNumber.should == actual.checkNumber 432 | end 433 | 434 | it "should serialize and deserialize CreditCardTrackType" do 435 | expected = CreditCardTrackType.new("track1", "track2") 436 | actual = get_actual(expected, CreditCardTrackType, "creditCardTrackType") 437 | 438 | expected.track1.should == actual.track1 439 | expected.track2.should == actual.track2 440 | end 441 | 442 | it "should serialize and deserialize PayPalType" do 443 | expected = PayPalType.new("123", "123", "123", "123", "123", "123") 444 | actual = get_actual(expected, PayPalType, "payPalType") 445 | 446 | expected.successUrl.should == actual.successUrl 447 | expected.cancelUrl.should == actual.cancelUrl 448 | expected.paypalLc.should == actual.paypalLc 449 | expected.paypalHdrImg.should == actual.paypalHdrImg 450 | expected.paypalPayflowcolor.should == actual.paypalPayflowcolor 451 | expected.payerID.should == actual.payerID 452 | end 453 | 454 | it "should serialize and deserialize OpaqueDataType" do 455 | expected = OpaqueDataType.new("dataDescriptor", "dataValue") 456 | actual = get_actual(expected, OpaqueDataType, "opaqueDataType") 457 | 458 | expected.dataDescriptor.should == actual.dataDescriptor 459 | expected.dataValue.should == actual.dataValue 460 | end 461 | 462 | it "should serialize and deserialize EncryptedTrackDataType" do 463 | expected = EncryptedTrackDataType.new 464 | expected.FormOfPayment = KeyBlock.new 465 | expected.FormOfPayment.Value = KeyValue.new 466 | expected.FormOfPayment.Value.Encoding = EncodingType::Base64 467 | expected.FormOfPayment.Value.EncryptionAlgorithm = EncryptionAlgorithmType::TDES 468 | expected.FormOfPayment.Value.Scheme = KeyManagementScheme.new 469 | expected.FormOfPayment.Value.Scheme.DUKPT = KeyManagementScheme::DUKPT.new 470 | 471 | expected.FormOfPayment.Value.Scheme.DUKPT.Operation = OperationType::DECRYPT 472 | expected.FormOfPayment.Value.Scheme.DUKPT.Mode = KeyManagementScheme::DUKPT::Mode.new("1234", "1234") 473 | expected.FormOfPayment.Value.Scheme.DUKPT.DeviceInfo = KeyManagementScheme::DUKPT::DeviceInfo.new("description") 474 | expected.FormOfPayment.Value.Scheme.DUKPT.EncryptedData = KeyManagementScheme::DUKPT::EncryptedData.new("bla") 475 | 476 | actual = get_actual(expected, EncryptedTrackDataType, "encryptedTrackDataType") 477 | 478 | expected.FormOfPayment.Value.Encoding.should == actual.FormOfPayment.Value.Encoding 479 | expected.FormOfPayment.Value.EncryptionAlgorithm.should == actual.FormOfPayment.Value.EncryptionAlgorithm 480 | expected.FormOfPayment.Value.Scheme.DUKPT.Operation.should == actual.FormOfPayment.Value.Scheme.DUKPT.Operation 481 | expected.FormOfPayment.Value.Scheme.DUKPT.Mode.PIN.should == actual.FormOfPayment.Value.Scheme.DUKPT.Mode.PIN 482 | expected.FormOfPayment.Value.Scheme.DUKPT.Mode.Data.should == actual.FormOfPayment.Value.Scheme.DUKPT.Mode.Data 483 | expected.FormOfPayment.Value.Scheme.DUKPT.DeviceInfo.Description.should == actual.FormOfPayment.Value.Scheme.DUKPT.DeviceInfo.Description 484 | expected.FormOfPayment.Value.Scheme.DUKPT.EncryptedData.Value.should == actual.FormOfPayment.Value.Scheme.DUKPT.EncryptedData.Value 485 | end 486 | 487 | it "should be able to get subscription" do 488 | @api_login, @api_key = '5KP3u95bQpv', '346HZ32z3fP4hTG2' # FIXME: this spec added in #66 depends on a hard coded test account. 489 | transaction = AuthorizeNet::API::Transaction.new(@api_login, @api_key, gateway: @gateway) 490 | @createTransactionRequest = ARBGetSubscriptionRequest.new 491 | 492 | @createTransactionRequest.refId = 'Sample' 493 | @createTransactionRequest.subscriptionId = '2930242' 494 | 495 | transaction.should respond_to(:arb_get_subscription_request) 496 | response = transaction.arb_get_subscription_request(@createTransactionRequest) 497 | 498 | expect(response).not_to eq(nil) 499 | expect(response.messages).not_to eq(nil) 500 | expect(response.messages.resultCode).not_to eq(nil) 501 | expect(response.messages.resultCode).to eq(MessageTypeEnum::Ok) 502 | expect(response.subscription.name).not_to eq(nil) 503 | expect(response.subscription.paymentSchedule).not_to eq(nil) 504 | expect(response.subscription.profile).not_to eq(nil) 505 | expect(response.subscription.profile.paymentProfile).not_to eq(nil) 506 | expect(response.subscription.profile.paymentProfile.billTo).not_to eq(nil) 507 | end 508 | 509 | it "should be able to get Customer Payment Profile List Request" do 510 | @api_login, @api_key = '5KP3u95bQpv', '346HZ32z3fP4hTG2' # FIXME: this spec added in #66 depends on a hard coded test account. 511 | transaction = AuthorizeNet::API::Transaction.new(@api_login, @api_key, gateway: @gateway) 512 | 513 | searchTypeEnum = CustomerPaymentProfileSearchTypeEnum::CardsExpiringInMonth 514 | sorting = CustomerPaymentProfileSorting.new 515 | orderByEnum = CustomerPaymentProfileOrderFieldEnum::Id 516 | sorting.orderBy = orderByEnum 517 | sorting.orderDescending = false 518 | 519 | paging = Paging.new 520 | paging.limit = 1000 521 | paging.offset = 1 522 | 523 | @createTransactionRequest = GetCustomerPaymentProfileListRequest.new 524 | 525 | @createTransactionRequest.searchType = searchTypeEnum 526 | @createTransactionRequest.month = "2020-12" 527 | @createTransactionRequest.sorting = sorting 528 | @createTransactionRequest.paging = paging 529 | 530 | transaction.should respond_to(:get_customer_payment_profile_list) 531 | response = transaction.get_customer_payment_profile_list(@createTransactionRequest) 532 | 533 | expect(response).not_to eq(nil) 534 | expect(response.messages).not_to eq(nil) 535 | expect(response.messages.resultCode).not_to eq(nil) 536 | expect(response.messages.resultCode).to eq(MessageTypeEnum::Ok) 537 | expect(response.totalNumInResultSet).not_to eq(nil) 538 | expect(response.paymentProfiles.paymentProfile.length).not_to eq(0) 539 | expect(response.paymentProfiles.paymentProfile[0].billTo.firstName).not_to eq(nil) 540 | expect(response.paymentProfiles.paymentProfile[0].payment.creditCard.cardNumber).not_to eq(nil) 541 | end 542 | 543 | it "should be able to get transaction List Request" do 544 | @api_login, @api_key = '5KP3u95bQpv', '346HZ32z3fP4hTG2' # FIXME: this spec added in #72 depends on a hard coded test account. 545 | transaction = AuthorizeNet::API::Transaction.new(@api_login, @api_key, gateway: @gateway) 546 | 547 | batchId = "15181378" 548 | 549 | @getTransactionListRequest = GetTransactionListRequest.new 550 | @getTransactionListRequest.batchId = batchId 551 | 552 | transaction.should respond_to(:get_transaction_list) 553 | response = transaction.get_transaction_list(@getTransactionListRequest) 554 | 555 | expect(response).not_to eq(nil) 556 | expect(response.messages).not_to eq(nil) 557 | expect(response.messages.resultCode).not_to eq(nil) 558 | expect(response.messages.resultCode).to eq(MessageTypeEnum::Ok) 559 | expect(response.transactions).not_to eq nil 560 | end 561 | 562 | def get_actual(expected, className, topElement) 563 | xmlText = @transaction.serialize(expected, topElement) 564 | className.from_xml(xmlText) 565 | end 566 | 567 | def create_transaction_request 568 | # create transaction request 569 | @createTransactionRequest = CreateTransactionRequest.new 570 | 571 | # all simple props 572 | @createTransactionRequest.transactionRequest = TransactionRequestType.new 573 | tReq = @createTransactionRequest.transactionRequest 574 | tReq.amount = 37.55 575 | tReq.currencyCode = "USD" 576 | tReq.authCode = "1546" 577 | tReq.refTransId = "123" 578 | tReq.splitTenderId = "123" 579 | tReq.taxExempt = "true" 580 | tReq.poNumber = "12345" 581 | tReq.customerIP = "12345" 582 | 583 | tReq.payment = PaymentType.new 584 | tReq.payment.creditCard = CreditCardType.new('4111111111111111', '0515', "true", '123') 585 | 586 | tReq.transactionType = TransactionTypeEnum::AuthCaptureTransaction 587 | 588 | # CustomerProfilePaymentType 589 | tReq.profile = CustomerProfilePaymentType.new 590 | tReq.profile.createProfile = "true" 591 | tReq.profile.customerProfileId = 123 592 | tReq.profile.shippingProfileId = 123 593 | tReq.profile.paymentProfile = PaymentProfile.new(1_234_556_565_656_565_656_565_656, "056") 594 | 595 | # Solution Type 596 | tReq.solution = SolutionType.new("A1000005", "somesolution") 597 | 598 | # OrderType 599 | tReq.order = OrderType.new("invoiceNumber123", "description123") 600 | 601 | # LineItemType 602 | tReq.lineItems = LineItems.new([LineItemType.new("itemId123", "name123", "description123", 123, 45.67, "true"), 603 | LineItemType.new("itemId456", "name456", "description456", 456, 35.67, "false")]) 604 | # ExtendedAmountType 605 | tReq.tax = ExtendedAmountType.new(11.32, "taxName", "taxDescription") 606 | tReq.duty = ExtendedAmountType.new(12.35, "dutyName", "dutyDescription") 607 | tReq.shipping = ExtendedAmountType.new(5.32, "shippingName", "shippingDescription") 608 | 609 | # CustomerDataType 610 | driversLicense = DriversLicenseType.new("DrivLicenseNumber123", "WA", "05/05/1990") 611 | tReq.customer = CustomerDataType.new(CustomerTypeEnum::Individual, "custId1", "a@a.com", driversLicense, "123456789") 612 | 613 | # BillTo 614 | tReq.billTo = CustomerAddressType.new("firstNameBT", "lastNameBT", "companyBT", "addressBT", "New York", "NY", 615 | "10010", "USA", "2121111111", "2121111111") 616 | # ShipTo 617 | tReq.shipTo = NameAndAddressType.new("firstNameST", "lastNameST", "companyST", "addressST", "New York", "NY", 618 | "10010", "USA") 619 | 620 | # CardHolderAuthentication 621 | tReq.cardholderAuthentication = CcAuthenticationType.new("authenticationIndicator1", "cardholderAuthenticationValue1") 622 | 623 | # TODO: add enums 624 | # Retail 625 | tReq.retail = TransRetailInfoType.new("2", "1") 626 | 627 | # TODO: add enums 628 | # TransactionSettings 629 | tReq.transactionSettings = Settings.new([SettingType.new("testRequest", "1"), 630 | SettingType.new("duplicateWindow", "0")]) 631 | 632 | # UserFields 633 | tReq.userFields = UserFields.new([UserField.new("field1", "fieldValue1"), UserField.new("field2", "fieldValue2")]) 634 | end 635 | 636 | def create_transaction_response 637 | @createTransactionResponse = CreateTransactionResponse.new 638 | @createTransactionResponse.refId = "someRefId123" 639 | @createTransactionResponse.sessionToken = "some session token" 640 | @createTransactionResponse.messages = MessagesType.new(MessageTypeEnum::Ok, [MessagesType::Message.new("code1", "text1"), MessagesType::Message.new("code2", "text2")]) 641 | 642 | @createTransactionResponse.transactionResponse = TransactionResponse.new 643 | tResp = @createTransactionResponse.transactionResponse 644 | tResp.responseCode = "123" 645 | tResp.rawResponseCode = "123" 646 | tResp.authCode = "123" 647 | tResp.avsResultCode = "123" 648 | tResp.cvvResultCode = "123" 649 | tResp.cavvResultCode = "123" 650 | tResp.transId = "123" 651 | tResp.refTransID = "123" 652 | tResp.transHash = "123" 653 | tResp.testRequest = "123" 654 | tResp.accountNumber = "123" 655 | tResp.accountType = "123" 656 | tResp.splitTenderId = "123" 657 | 658 | tResp.prePaidCard = TransactionResponse::PrePaidCard.new("123.33", "123.33", "123.33") 659 | tResp.messages = TransactionResponse::Messages.new([TransactionResponse::Messages::Message.new("code1", "descr1"), TransactionResponse::Messages::Message.new("code1", "descr1")]) 660 | tResp.errors = TransactionResponse::Errors.new([TransactionResponse::Errors::Error.new("code1", "descr1"), TransactionResponse::Errors::Error.new("errorCode1", "errorDescr1")]) 661 | tResp.splitTenderPayments = TransactionResponse::SplitTenderPayments.new 662 | tResp.splitTenderPayments.splitTenderPayments = 663 | [ 664 | TransactionResponse::SplitTenderPayments::SplitTenderPayment.new("transId1", "responseCode1", "responseToCustomer1", 665 | "authCode1", "accountNumber1", "accountType1", "requestedAmount1", "approvedAmount1", "balanceOnCard1"), 666 | TransactionResponse::SplitTenderPayments::SplitTenderPayment.new("transId12", "responseCode12", "responseToCustomer12", 667 | "authCode12", "accountNumber12", "accountType12", "requestedAmount12", "approvedAmount12", 668 | "balanceOnCard12") 669 | ] 670 | 671 | tResp.userFields = UserFields.new([UserField.new("field1", "fieldValue1"), UserField.new("field2", "fieldValue2")]) 672 | 673 | tResp.shipTo = shipTo = NameAndAddressType.new("firstNameST", "lastNameST", "companyST", "addressST", "New York", "NY", 674 | "10010", "USA") 675 | tResp.secureAcceptance = TransactionResponse::SecureAcceptance.new("secureAcceptanceUrl1", "payerID1") 676 | 677 | @createTransactionResponse.profileResponse = CreateProfileResponse.new 678 | @createTransactionResponse.profileResponse.messages = MessagesType.new(MessageTypeEnum::Ok, [MessagesType::Message.new("pRespCode1", "pRespText1"), MessagesType::Message.new("pRespCode2", "pRespText2")]) 679 | @createTransactionResponse.profileResponse.customerProfileId = "3154654646546" 680 | @createTransactionResponse.profileResponse.customerPaymentProfileIdList = NumericStringsType.new(%w[546654656465 5665656565656565]) 681 | @createTransactionResponse.profileResponse.customerShippingAddressIdList = NumericStringsType.new(%w[66232546654656465 566565656565787876565]) 682 | end 683 | end 684 | -------------------------------------------------------------------------------- /spec/credentials.yml: -------------------------------------------------------------------------------- 1 | #obtain an API login_id and transaction_id according to instructions at https://developer.authorize.net/faqs/#gettranskey 2 | api_login_id: <%= ENV.fetch 'API_LOGIN_ID' %> 3 | api_transaction_key: <%= ENV.fetch 'API_TRANSACTION_KEY' %> 4 | 5 | #obtained md5 hash value by first setting the hash value in https://sandbox.authorize.net/ under the Account tab->MD5 Hash 6 | md5_value: <%= ENV['MD5_VALUE'] || 'MD5_TEST' %> 7 | -------------------------------------------------------------------------------- /spec/spec.opts: -------------------------------------------------------------------------------- 1 | --colour 2 | --format progress 3 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # require 'simplecov' 2 | # SimpleCov.start 3 | 4 | # require 'scrutinizer/ocular' 5 | # Scrutinizer::Ocular.watch! 6 | 7 | require "authorizenet" 8 | require "yaml" 9 | require "active_support" 10 | 11 | # ActiveSupport::Deprecation.behavior = lambda do |msg, stack| 12 | # # when running this test suite ignore our own deprecation warnings 13 | # unless /use AuthorizeNet::API::Transaction/ =~ msg 14 | # ActiveSupport::Deprecation::DEFAULT_BEHAVIORS[:stderr].call(msg, stack, ActiveSupport::Deprecation.new) 15 | # end 16 | # end 17 | 18 | Dir['./spec/support/**/*.rb'].each { |f| require f } 19 | 20 | RSpec.configure do |config| 21 | config.include SharedHelper 22 | end 23 | -------------------------------------------------------------------------------- /spec/support/shared_helper.rb: -------------------------------------------------------------------------------- 1 | require 'erb' 2 | require 'yaml' 3 | 4 | module SharedHelper 5 | def credentials 6 | $credentials ||= YAML.load(ERB.new(File.read "#{__dir__}/../credentials.yml").result) 7 | rescue Errno::ENOENT 8 | warn "WARNING: Running w/o valid AuthorizeNet sandbox credentials. Create spec/credentials.yml." 9 | end 10 | end 11 | --------------------------------------------------------------------------------