activemerchant 1.66.0 → 1.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +28 -0
- data/lib/active_merchant/billing/gateways/authorize_net.rb +74 -7
- data/lib/active_merchant/billing/gateways/checkout_v2.rb +1 -0
- data/lib/active_merchant/billing/gateways/credorax.rb +8 -1
- data/lib/active_merchant/billing/gateways/cyber_source.rb +14 -7
- data/lib/active_merchant/billing/gateways/ebanx.rb +234 -0
- data/lib/active_merchant/billing/gateways/elavon.rb +23 -92
- data/lib/active_merchant/billing/gateways/fat_zebra.rb +1 -27
- data/lib/active_merchant/billing/gateways/firstdata_e4.rb +5 -3
- data/lib/active_merchant/billing/gateways/jetpay_v2.rb +410 -0
- data/lib/active_merchant/billing/gateways/nmi.rb +10 -1
- data/lib/active_merchant/billing/gateways/opp.rb +124 -114
- data/lib/active_merchant/billing/gateways/orbital.rb +15 -4
- data/lib/active_merchant/billing/gateways/payeezy.rb +9 -6
- data/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb +19 -14
- data/lib/active_merchant/billing/gateways/realex.rb +2 -5
- data/lib/active_merchant/billing/gateways/safe_charge.rb +2 -1
- data/lib/active_merchant/billing/gateways/wepay.rb +11 -6
- data/lib/active_merchant/version.rb +1 -1
- metadata +4 -2
| @@ -2,33 +2,6 @@ require 'active_merchant/billing/gateways/viaklix' | |
| 2 2 |  | 
| 3 3 | 
             
            module ActiveMerchant #:nodoc:
         | 
| 4 4 | 
             
              module Billing #:nodoc:
         | 
| 5 | 
            -
                # = Elavon Virtual Merchant Gateway
         | 
| 6 | 
            -
                #
         | 
| 7 | 
            -
                # == Example use:
         | 
| 8 | 
            -
                #
         | 
| 9 | 
            -
                #   gateway = ActiveMerchant::Billing::ElavonGateway.new(
         | 
| 10 | 
            -
                #               :login     => "my_virtual_merchant_id",
         | 
| 11 | 
            -
                #               :password  => "my_virtual_merchant_pin",
         | 
| 12 | 
            -
                #               :user      => "my_virtual_merchant_user_id" # optional
         | 
| 13 | 
            -
                #            )
         | 
| 14 | 
            -
                #
         | 
| 15 | 
            -
                #   # set up credit card obj as in main ActiveMerchant example
         | 
| 16 | 
            -
                #   creditcard = ActiveMerchant::Billing::CreditCard.new(
         | 
| 17 | 
            -
                #     :type       => 'visa',
         | 
| 18 | 
            -
                #     :number     => '41111111111111111',
         | 
| 19 | 
            -
                #     :month      => 10,
         | 
| 20 | 
            -
                #     :year       => 2011,
         | 
| 21 | 
            -
                #     :first_name => 'Bob',
         | 
| 22 | 
            -
                #     :last_name  => 'Bobsen'
         | 
| 23 | 
            -
                #   )
         | 
| 24 | 
            -
                #
         | 
| 25 | 
            -
                #   # run request
         | 
| 26 | 
            -
                #   response = gateway.purchase(1000, creditcard) # authorize and capture 10 USD
         | 
| 27 | 
            -
                #
         | 
| 28 | 
            -
                #   puts response.success?      # Check whether the transaction was successful
         | 
| 29 | 
            -
                #   puts response.message       # Retrieve the message returned by Elavon
         | 
| 30 | 
            -
                #   puts response.authorization # Retrieve the unique transaction ID returned by Elavon
         | 
| 31 | 
            -
                #
         | 
| 32 5 | 
             
                class ElavonGateway < Gateway
         | 
| 33 6 | 
             
                  include Empty
         | 
| 34 7 |  | 
| @@ -55,23 +28,11 @@ module ActiveMerchant #:nodoc: | |
| 55 28 | 
             
                    :update => 'CCUPDATETOKEN',
         | 
| 56 29 | 
             
                  }
         | 
| 57 30 |  | 
| 58 | 
            -
                  # Initialize the Gateway
         | 
| 59 | 
            -
                  #
         | 
| 60 | 
            -
                  # The gateway requires that a valid login and password be passed
         | 
| 61 | 
            -
                  # in the +options+ hash.
         | 
| 62 | 
            -
                  #
         | 
| 63 | 
            -
                  # ==== Options
         | 
| 64 | 
            -
                  #
         | 
| 65 | 
            -
                  # * <tt>:login</tt> -- Merchant ID
         | 
| 66 | 
            -
                  # * <tt>:password</tt> -- PIN
         | 
| 67 | 
            -
                  # * <tt>:user</tt> -- Specify a subuser of the account (optional)
         | 
| 68 | 
            -
                  # * <tt>:test => +true+ or +false+</tt> -- Force test transactions
         | 
| 69 31 | 
             
                  def initialize(options = {})
         | 
| 70 32 | 
             
                    requires!(options, :login, :password)
         | 
| 71 33 | 
             
                    super
         | 
| 72 34 | 
             
                  end
         | 
| 73 35 |  | 
| 74 | 
            -
                  # Make a purchase
         | 
| 75 36 | 
             
                  def purchase(money, payment_method, options = {})
         | 
| 76 37 | 
             
                    form = {}
         | 
| 77 38 | 
             
                    add_salestax(form, options)
         | 
| @@ -85,16 +46,9 @@ module ActiveMerchant #:nodoc: | |
| 85 46 | 
             
                    add_customer_data(form, options)
         | 
| 86 47 | 
             
                    add_test_mode(form, options)
         | 
| 87 48 | 
             
                    add_ip(form, options)
         | 
| 88 | 
            -
                    commit(:purchase, money, form)
         | 
| 49 | 
            +
                    commit(:purchase, money, form, options)
         | 
| 89 50 | 
             
                  end
         | 
| 90 51 |  | 
| 91 | 
            -
                  # Authorize a credit card for a given amount.
         | 
| 92 | 
            -
                  #
         | 
| 93 | 
            -
                  # ==== Parameters
         | 
| 94 | 
            -
                  # * <tt>money</tt> - The amount to be authorized as an Integer value in cents.
         | 
| 95 | 
            -
                  # * <tt>credit_card</tt> - The CreditCard details for the transaction.
         | 
| 96 | 
            -
                  # * <tt>options</tt>
         | 
| 97 | 
            -
                  #   * <tt>:billing_address</tt> - The billing address for the cardholder.
         | 
| 98 52 | 
             
                  def authorize(money, creditcard, options = {})
         | 
| 99 53 | 
             
                    form = {}
         | 
| 100 54 | 
             
                    add_salestax(form, options)
         | 
| @@ -104,16 +58,9 @@ module ActiveMerchant #:nodoc: | |
| 104 58 | 
             
                    add_customer_data(form, options)
         | 
| 105 59 | 
             
                    add_test_mode(form, options)
         | 
| 106 60 | 
             
                    add_ip(form, options)
         | 
| 107 | 
            -
                    commit(:authorize, money, form)
         | 
| 61 | 
            +
                    commit(:authorize, money, form, options)
         | 
| 108 62 | 
             
                  end
         | 
| 109 63 |  | 
| 110 | 
            -
                  # Capture authorized funds from a credit card.
         | 
| 111 | 
            -
                  #
         | 
| 112 | 
            -
                  # ==== Parameters
         | 
| 113 | 
            -
                  # * <tt>money</tt> - The amount to be captured as an Integer value in cents.
         | 
| 114 | 
            -
                  # * <tt>authorization</tt> - The approval code returned from the initial authorization.
         | 
| 115 | 
            -
                  # * <tt>options</tt>
         | 
| 116 | 
            -
                  #   * <tt>:credit_card</tt> - The CreditCard details from the initial transaction (required).
         | 
| 117 64 | 
             
                  def capture(money, authorization, options = {})
         | 
| 118 65 | 
             
                    form = {}
         | 
| 119 66 | 
             
                    if options[:credit_card]
         | 
| @@ -130,45 +77,23 @@ module ActiveMerchant #:nodoc: | |
| 130 77 | 
             
                      add_partial_shipment_flag(form, options)
         | 
| 131 78 | 
             
                      add_test_mode(form, options)
         | 
| 132 79 | 
             
                    end
         | 
| 133 | 
            -
                    commit(action, money, form)
         | 
| 134 | 
            -
                  end
         | 
| 135 | 
            -
             | 
| 136 | 
            -
                  # Refund a transaction.
         | 
| 137 | 
            -
                  #
         | 
| 138 | 
            -
                  # This transaction indicates to the gateway that
         | 
| 139 | 
            -
                  # money should flow from the merchant to the customer.
         | 
| 140 | 
            -
                  #
         | 
| 141 | 
            -
                  # ==== Parameters
         | 
| 142 | 
            -
                  #
         | 
| 143 | 
            -
                  # * <tt>money</tt> -- The amount to be credited to the customer as an Integer value in cents.
         | 
| 144 | 
            -
                  # * <tt>identification</tt> -- The ID of the original transaction against which the refund is being issued.
         | 
| 145 | 
            -
                  # * <tt>options</tt> -- A hash of parameters.
         | 
| 80 | 
            +
                    commit(action, money, form, options)
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
             | 
| 146 83 | 
             
                  def refund(money, identification, options = {})
         | 
| 147 84 | 
             
                    form = {}
         | 
| 148 85 | 
             
                    add_txn_id(form, identification)
         | 
| 149 86 | 
             
                    add_test_mode(form, options)
         | 
| 150 | 
            -
                    commit(:refund, money, form)
         | 
| 87 | 
            +
                    commit(:refund, money, form, options)
         | 
| 151 88 | 
             
                  end
         | 
| 152 89 |  | 
| 153 | 
            -
                  # Void a previous transaction
         | 
| 154 | 
            -
                  #
         | 
| 155 | 
            -
                  # ==== Parameters
         | 
| 156 | 
            -
                  #
         | 
| 157 | 
            -
                  # * <tt>authorization</tt> - The authorization returned from the previous request.
         | 
| 158 90 | 
             
                  def void(identification, options = {})
         | 
| 159 91 | 
             
                    form = {}
         | 
| 160 92 | 
             
                    add_txn_id(form, identification)
         | 
| 161 93 | 
             
                    add_test_mode(form, options)
         | 
| 162 | 
            -
                    commit(:void, nil, form)
         | 
| 94 | 
            +
                    commit(:void, nil, form, options)
         | 
| 163 95 | 
             
                  end
         | 
| 164 96 |  | 
| 165 | 
            -
                  # Make a credit to a card.  Use the refund method if you'd like to credit using
         | 
| 166 | 
            -
                  # previous transaction
         | 
| 167 | 
            -
                  #
         | 
| 168 | 
            -
                  # ==== Parameters
         | 
| 169 | 
            -
                  # * <tt>money</tt> - The amount to be credited as an Integer value in cents.
         | 
| 170 | 
            -
                  # * <tt>creditcard</tt> - The credit card to be credited.
         | 
| 171 | 
            -
                  # * <tt>options</tt>
         | 
| 172 97 | 
             
                  def credit(money, creditcard, options = {})
         | 
| 173 98 | 
             
                    if creditcard.is_a?(String)
         | 
| 174 99 | 
             
                      raise ArgumentError, "Reference credits are not supported. Please supply the original credit card or use the #refund method."
         | 
| @@ -180,7 +105,7 @@ module ActiveMerchant #:nodoc: | |
| 180 105 | 
             
                    add_address(form, options)
         | 
| 181 106 | 
             
                    add_customer_data(form, options)
         | 
| 182 107 | 
             
                    add_test_mode(form, options)
         | 
| 183 | 
            -
                    commit(:credit, money, form)
         | 
| 108 | 
            +
                    commit(:credit, money, form, options)
         | 
| 184 109 | 
             
                  end
         | 
| 185 110 |  | 
| 186 111 | 
             
                  def verify(credit_card, options = {})
         | 
| @@ -198,7 +123,7 @@ module ActiveMerchant #:nodoc: | |
| 198 123 | 
             
                    add_test_mode(form, options)
         | 
| 199 124 | 
             
                    add_verification(form, options)
         | 
| 200 125 | 
             
                    form[:add_token] = 'Y'
         | 
| 201 | 
            -
                    commit(:store, nil, form)
         | 
| 126 | 
            +
                    commit(:store, nil, form, options)
         | 
| 202 127 | 
             
                  end
         | 
| 203 128 |  | 
| 204 129 | 
             
                  def update(token, creditcard, options = {})
         | 
| @@ -208,7 +133,7 @@ module ActiveMerchant #:nodoc: | |
| 208 133 | 
             
                    add_address(form, options)
         | 
| 209 134 | 
             
                    add_customer_data(form, options)
         | 
| 210 135 | 
             
                    add_test_mode(form, options)
         | 
| 211 | 
            -
                    commit(:update, nil, form)
         | 
| 136 | 
            +
                    commit(:update, nil, form, options)
         | 
| 212 137 | 
             
                  end
         | 
| 213 138 |  | 
| 214 139 | 
             
                  private
         | 
| @@ -255,6 +180,11 @@ module ActiveMerchant #:nodoc: | |
| 255 180 | 
             
                    form[:email] = truncate(options[:email], 100) unless empty?(options[:email])
         | 
| 256 181 | 
             
                    form[:customer_code] = truncate(options[:customer], 10) unless empty?(options[:customer])
         | 
| 257 182 | 
             
                    form[:customer_number] = options[:customer_number] unless empty?(options[:customer_number])
         | 
| 183 | 
            +
                    if options[:custom_fields]
         | 
| 184 | 
            +
                      options[:custom_fields].each do |key, value|
         | 
| 185 | 
            +
                        form[key.to_s] = value
         | 
| 186 | 
            +
                      end
         | 
| 187 | 
            +
                    end
         | 
| 258 188 | 
             
                  end
         | 
| 259 189 |  | 
| 260 190 | 
             
                  def add_salestax(form, options)
         | 
| @@ -313,11 +243,11 @@ module ActiveMerchant #:nodoc: | |
| 313 243 | 
             
                    !response.has_key?('errorMessage')
         | 
| 314 244 | 
             
                  end
         | 
| 315 245 |  | 
| 316 | 
            -
                  def commit(action, money, parameters)
         | 
| 246 | 
            +
                  def commit(action, money, parameters, options)
         | 
| 317 247 | 
             
                    parameters[:amount] = amount(money)
         | 
| 318 248 | 
             
                    parameters[:transaction_type] = self.actions[action]
         | 
| 319 249 |  | 
| 320 | 
            -
                    response = parse( ssl_post(test? ? self.test_url : self.live_url, post_data(parameters)) )
         | 
| 250 | 
            +
                    response = parse( ssl_post(test? ? self.test_url : self.live_url, post_data(parameters, options)) )
         | 
| 321 251 |  | 
| 322 252 | 
             
                    Response.new(response['result'] == '0', message_from(response), response,
         | 
| 323 253 | 
             
                      :test => @options[:test] || test?,
         | 
| @@ -327,21 +257,22 @@ module ActiveMerchant #:nodoc: | |
| 327 257 | 
             
                    )
         | 
| 328 258 | 
             
                  end
         | 
| 329 259 |  | 
| 330 | 
            -
                  def post_data(parameters)
         | 
| 260 | 
            +
                  def post_data(parameters, options)
         | 
| 331 261 | 
             
                    result = preamble
         | 
| 332 262 | 
             
                    result.merge!(parameters)
         | 
| 333 | 
            -
                    result.collect { |key, value| post_data_string(key, value) }.join("&")
         | 
| 263 | 
            +
                    result.collect { |key, value| post_data_string(key, value, options) }.join("&")
         | 
| 334 264 | 
             
                  end
         | 
| 335 265 |  | 
| 336 | 
            -
                  def post_data_string(key, value)
         | 
| 337 | 
            -
                    if custom_field?(key)
         | 
| 266 | 
            +
                  def post_data_string(key, value, options)
         | 
| 267 | 
            +
                    if custom_field?(key, options)
         | 
| 338 268 | 
             
                      "#{key}=#{CGI.escape(value.to_s)}"
         | 
| 339 269 | 
             
                    else
         | 
| 340 270 | 
             
                      "ssl_#{key}=#{CGI.escape(value.to_s)}"
         | 
| 341 271 | 
             
                    end
         | 
| 342 272 | 
             
                  end
         | 
| 343 273 |  | 
| 344 | 
            -
                  def custom_field?(field_name)
         | 
| 274 | 
            +
                  def custom_field?(field_name, options)
         | 
| 275 | 
            +
                    return true if options[:custom_fields] && options[:custom_fields].include?(field_name.to_sym)
         | 
| 345 276 | 
             
                    field_name == :customer_number
         | 
| 346 277 | 
             
                  end
         | 
| 347 278 |  | 
| @@ -14,23 +14,11 @@ module ActiveMerchant #:nodoc: | |
| 14 14 | 
             
                  self.homepage_url = 'https://www.fatzebra.com.au/'
         | 
| 15 15 | 
             
                  self.display_name = 'Fat Zebra'
         | 
| 16 16 |  | 
| 17 | 
            -
                  # Setup a new instance of the gateway.
         | 
| 18 | 
            -
                  #
         | 
| 19 | 
            -
                  # The options hash should include :username and :token
         | 
| 20 | 
            -
                  # You can find your username and token at https://dashboard.fatzebra.com.au
         | 
| 21 | 
            -
                  # Under the Your Account section
         | 
| 22 17 | 
             
                  def initialize(options = {})
         | 
| 23 18 | 
             
                    requires!(options, :username, :token)
         | 
| 24 19 | 
             
                    super
         | 
| 25 20 | 
             
                  end
         | 
| 26 21 |  | 
| 27 | 
            -
                  # To create a purchase on a credit card use:
         | 
| 28 | 
            -
                  #
         | 
| 29 | 
            -
                  #   purchase(money, creditcard)
         | 
| 30 | 
            -
                  #
         | 
| 31 | 
            -
                  # To charge a tokenized card
         | 
| 32 | 
            -
                  #
         | 
| 33 | 
            -
                  #   purchase(money, "abzy87u", :cvv => "123")
         | 
| 34 22 | 
             
                  def purchase(money, creditcard, options = {})
         | 
| 35 23 | 
             
                    post = {}
         | 
| 36 24 |  | 
| @@ -65,11 +53,6 @@ module ActiveMerchant #:nodoc: | |
| 65 53 | 
             
                    commit(:post, "purchases/#{CGI.escape(authorization)}/capture", post)
         | 
| 66 54 | 
             
                  end
         | 
| 67 55 |  | 
| 68 | 
            -
                  # Refund a transaction
         | 
| 69 | 
            -
                  #
         | 
| 70 | 
            -
                  # amount - Integer - the amount to refund
         | 
| 71 | 
            -
                  # txn_id - String - the original transaction to be refunded
         | 
| 72 | 
            -
                  # reference - String - your transaction reference
         | 
| 73 56 | 
             
                  def refund(money, txn_id, options={})
         | 
| 74 57 | 
             
                    post = {}
         | 
| 75 58 |  | 
| @@ -81,9 +64,6 @@ module ActiveMerchant #:nodoc: | |
| 81 64 | 
             
                    commit(:post, "refunds", post)
         | 
| 82 65 | 
             
                  end
         | 
| 83 66 |  | 
| 84 | 
            -
                  # Tokenize a credit card
         | 
| 85 | 
            -
                  #
         | 
| 86 | 
            -
                  # The token is returned in the Response#authorization
         | 
| 87 67 | 
             
                  def store(creditcard, options={})
         | 
| 88 68 | 
             
                    post = {}
         | 
| 89 69 | 
             
                    add_creditcard(post, creditcard)
         | 
| @@ -104,14 +84,12 @@ module ActiveMerchant #:nodoc: | |
| 104 84 |  | 
| 105 85 | 
             
                  private
         | 
| 106 86 |  | 
| 107 | 
            -
                  # Add the money details to the request
         | 
| 108 87 | 
             
                  def add_amount(post, money, options)
         | 
| 109 88 | 
             
                    post[:currency] = (options[:currency] || currency(money))
         | 
| 110 89 | 
             
                    post[:currency] = post[:currency].upcase if post[:currency]
         | 
| 111 90 | 
             
                    post[:amount] = money
         | 
| 112 91 | 
             
                  end
         | 
| 113 92 |  | 
| 114 | 
            -
                  # Add the credit card details to the request
         | 
| 115 93 | 
             
                  def add_creditcard(post, creditcard, options = {})
         | 
| 116 94 | 
             
                    if creditcard.respond_to?(:number)
         | 
| 117 95 | 
             
                      post[:card_number] = creditcard.number
         | 
| @@ -134,7 +112,7 @@ module ActiveMerchant #:nodoc: | |
| 134 112 | 
             
                    extra = {}
         | 
| 135 113 | 
             
                    extra[:ecm] = "32" if options[:recurring]
         | 
| 136 114 | 
             
                    extra[:cavv] = options[:cavv] if options[:cavv]
         | 
| 137 | 
            -
                    extra[:xid] = options[: | 
| 115 | 
            +
                    extra[:xid] = options[:xid] if options[:xid]
         | 
| 138 116 | 
             
                    extra[:sli] = options[:sli] if options[:sli]
         | 
| 139 117 | 
             
                    extra[:name] = options[:merchant] if options[:merchant]
         | 
| 140 118 | 
             
                    extra[:location] = options[:merchant_location] if options[:merchant_location]
         | 
| @@ -149,7 +127,6 @@ module ActiveMerchant #:nodoc: | |
| 149 127 | 
             
                    post[:customer_ip] = options[:ip] || "127.0.0.1"
         | 
| 150 128 | 
             
                  end
         | 
| 151 129 |  | 
| 152 | 
            -
                  # Post the data to the gateway
         | 
| 153 130 | 
             
                  def commit(method, uri, parameters=nil)
         | 
| 154 131 | 
             
                    response = begin
         | 
| 155 132 | 
             
                      parse(ssl_request(method, get_url(uri), parameters.to_json, headers))
         | 
| @@ -194,7 +171,6 @@ module ActiveMerchant #:nodoc: | |
| 194 171 | 
             
                    end
         | 
| 195 172 | 
             
                  end
         | 
| 196 173 |  | 
| 197 | 
            -
                  # Parse the returned JSON, if parse errors are raised then return a detailed error.
         | 
| 198 174 | 
             
                  def parse(response)
         | 
| 199 175 | 
             
                    begin
         | 
| 200 176 | 
             
                      JSON.parse(response)
         | 
| @@ -209,13 +185,11 @@ module ActiveMerchant #:nodoc: | |
| 209 185 | 
             
                    end
         | 
| 210 186 | 
             
                  end
         | 
| 211 187 |  | 
| 212 | 
            -
                  # Build the URL based on the AM mode and the URI
         | 
| 213 188 | 
             
                  def get_url(uri)
         | 
| 214 189 | 
             
                    base = test? ? self.test_url : self.live_url
         | 
| 215 190 | 
             
                    base + "/" + uri
         | 
| 216 191 | 
             
                  end
         | 
| 217 192 |  | 
| 218 | 
            -
                  # Builds the auth and U-A headers for the request
         | 
| 219 193 | 
             
                  def headers
         | 
| 220 194 | 
             
                    {
         | 
| 221 195 | 
             
                      "Authorization" => "Basic " + Base64.strict_encode64(@options[:username].to_s + ":" + @options[:token].to_s).strip,
         | 
| @@ -34,6 +34,8 @@ module ActiveMerchant #:nodoc: | |
| 34 34 |  | 
| 35 35 | 
             
                  E4_BRANDS = BRANDS.merge({:mastercard => "Mastercard"})
         | 
| 36 36 |  | 
| 37 | 
            +
                  DEFAULT_ECI = "07"
         | 
| 38 | 
            +
             | 
| 37 39 | 
             
                  self.supported_cardtypes = BRANDS.keys
         | 
| 38 40 | 
             
                  self.supported_countries = ["CA", "US"]
         | 
| 39 41 | 
             
                  self.default_currency = "USD"
         | 
| @@ -235,6 +237,9 @@ module ActiveMerchant #:nodoc: | |
| 235 237 | 
             
                      xml.tag! "CardHoldersName", credit_card.name
         | 
| 236 238 | 
             
                      xml.tag! "CardType", card_type(credit_card.brand)
         | 
| 237 239 |  | 
| 240 | 
            +
                      eci = (credit_card.respond_to?(:eci) ? credit_card.eci : nil) || options[:eci] || DEFAULT_ECI
         | 
| 241 | 
            +
                      xml.tag! "Ecommerce_Flag", eci
         | 
| 242 | 
            +
             | 
| 238 243 | 
             
                      add_credit_card_verification_strings(xml, credit_card, options)
         | 
| 239 244 | 
             
                    end
         | 
| 240 245 | 
             
                  end
         | 
| @@ -256,8 +261,6 @@ module ActiveMerchant #:nodoc: | |
| 256 261 | 
             
                  end
         | 
| 257 262 |  | 
| 258 263 | 
             
                  def add_network_tokenization_credit_card(xml, credit_card)
         | 
| 259 | 
            -
                    xml.tag!("Ecommerce_Flag", credit_card.eci)
         | 
| 260 | 
            -
             | 
| 261 264 | 
             
                    case card_brand(credit_card).to_sym
         | 
| 262 265 | 
             
                    when :visa
         | 
| 263 266 | 
             
                      xml.tag!("XID", credit_card.transaction_id) if credit_card.transaction_id
         | 
| @@ -275,7 +278,6 @@ module ActiveMerchant #:nodoc: | |
| 275 278 | 
             
                  def add_card_authentication_data(xml, options)
         | 
| 276 279 | 
             
                    xml.tag! "CAVV", options[:cavv]
         | 
| 277 280 | 
             
                    xml.tag! "XID", options[:xid]
         | 
| 278 | 
            -
                    xml.tag! "Ecommerce_Flag", options[:eci]
         | 
| 279 281 | 
             
                  end
         | 
| 280 282 |  | 
| 281 283 | 
             
                  def add_credit_card_token(xml, store_authorization)
         | 
| @@ -0,0 +1,410 @@ | |
| 1 | 
            +
            module ActiveMerchant #:nodoc:
         | 
| 2 | 
            +
              module Billing #:nodoc:
         | 
| 3 | 
            +
                class JetpayV2Gateway < Gateway
         | 
| 4 | 
            +
                  self.test_url = 'https://test1.jetpay.com/jetpay'
         | 
| 5 | 
            +
                  self.live_url = 'https://gateway20.jetpay.com/jetpay'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  self.money_format = :cents
         | 
| 8 | 
            +
                  self.default_currency = 'USD'
         | 
| 9 | 
            +
                  self.supported_countries = ['US', 'CA']
         | 
| 10 | 
            +
                  self.supported_cardtypes = [:visa, :master, :american_express, :discover]
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  self.homepage_url = 'http://www.jetpay.com'
         | 
| 13 | 
            +
                  self.display_name = 'JetPay'
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  API_VERSION = '2.2'
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  ACTION_CODE_MESSAGES = {
         | 
| 18 | 
            +
                    "000" =>  "Approved.",
         | 
| 19 | 
            +
                    "001" =>  "Refer to card issuer.",
         | 
| 20 | 
            +
                    "002" =>  "Refer to card issuer, special condition.",
         | 
| 21 | 
            +
                    "003" =>  "Invalid merchant or service provider.",
         | 
| 22 | 
            +
                    "004" =>  "Pick up card.",
         | 
| 23 | 
            +
                    "005" =>  "Do not honor.",
         | 
| 24 | 
            +
                    "006" =>  "Error.",
         | 
| 25 | 
            +
                    "007" =>  "Pick up card, special condition.",
         | 
| 26 | 
            +
                    "008" =>  "Honor with ID (Show ID).",
         | 
| 27 | 
            +
                    "010" =>  "Partial approval.",
         | 
| 28 | 
            +
                    "011" =>  "VIP approval.",
         | 
| 29 | 
            +
                    "012" =>  "Invalid transaction.",
         | 
| 30 | 
            +
                    "013" =>  "Invalid amount or exceeds maximum for card program.",
         | 
| 31 | 
            +
                    "014" =>  "Invalid account number (no such number).",
         | 
| 32 | 
            +
                    "015" =>  "No such issuer.",
         | 
| 33 | 
            +
                    "019" =>  "Re-enter Transaction.",
         | 
| 34 | 
            +
                    "021" =>  "No action taken (unable to back out prior transaction).",
         | 
| 35 | 
            +
                    "025" =>  "Transaction Not Found.",
         | 
| 36 | 
            +
                    "027" =>  "File update field edit error.",
         | 
| 37 | 
            +
                    "028" =>  "File is temporarily unavailable.",
         | 
| 38 | 
            +
                    "030" =>  "Format error.",
         | 
| 39 | 
            +
                    "039" =>  "No credit account.",
         | 
| 40 | 
            +
                    "041" =>  "Pick up card (lost card).",
         | 
| 41 | 
            +
                    "043" =>  "Pick up card (stolen card).",
         | 
| 42 | 
            +
                    "051" =>  "Insufficient funds.",
         | 
| 43 | 
            +
                    "052" =>  "No checking account.",
         | 
| 44 | 
            +
                    "053" =>  "Mp savomgs accpimt.",
         | 
| 45 | 
            +
                    "054" =>  "Expired Card.",
         | 
| 46 | 
            +
                    "055" =>  "Incorrect PIN.",
         | 
| 47 | 
            +
                    "057" =>  "Transaction not permitted to cardholder.",
         | 
| 48 | 
            +
                    "058" =>  "Transaction not allowed at terminal.",
         | 
| 49 | 
            +
                    "061" =>  "Exceeds withdrawal limit.",
         | 
| 50 | 
            +
                    "062" =>  "Restricted card (eg, Country Exclusion).",
         | 
| 51 | 
            +
                    "063" =>  "Security violation.",
         | 
| 52 | 
            +
                    "065" =>  "Activity count limit exceeded.",
         | 
| 53 | 
            +
                    "068" =>  "Response late.",
         | 
| 54 | 
            +
                    "070" =>  "Contact card issuer.",
         | 
| 55 | 
            +
                    "071" =>  "PIN not changed.",
         | 
| 56 | 
            +
                    "075" =>  "Allowable number of PIN-entry tries exceeded.",
         | 
| 57 | 
            +
                    "076" =>  "Unable to locate previous message (no matching retrieval reference number).",
         | 
| 58 | 
            +
                    "077" =>  "Repeat or reversal data are inconsistent with original message.",
         | 
| 59 | 
            +
                    "078" =>  "Blocked (first use), or non-existent account.",
         | 
| 60 | 
            +
                    "079" =>  "Key exchange validation failed.",
         | 
| 61 | 
            +
                    "080" =>  "Credit issuer unavailable or invalid date.",
         | 
| 62 | 
            +
                    "081" =>  "PIN cryptographic error found.",
         | 
| 63 | 
            +
                    "082" =>  "Negative online CVV results.",
         | 
| 64 | 
            +
                    "084" =>  "Invalid auth life cycle.",
         | 
| 65 | 
            +
                    "085" =>  "No reason to decline - CVV or AVS approved.",
         | 
| 66 | 
            +
                    "086" =>  "Cannot verify PIN.",
         | 
| 67 | 
            +
                    "087" =>  "Cashback not allowed.",
         | 
| 68 | 
            +
                    "089" =>  "Issuer Down.",
         | 
| 69 | 
            +
                    "091" =>  "Issuer Down.",
         | 
| 70 | 
            +
                    "092" =>  "Unable to route transaction.",
         | 
| 71 | 
            +
                    "093" =>  "Transaction cannot be completed - violation of law.",
         | 
| 72 | 
            +
                    "094" =>  "Duplicate transmission.",
         | 
| 73 | 
            +
                    "096" =>  "System error.",
         | 
| 74 | 
            +
                    "100" =>  "Deny.",
         | 
| 75 | 
            +
                    "101" =>  "Expired Card.",
         | 
| 76 | 
            +
                    "103" =>  "Deny - Invalid manual Entry 4DBC.",
         | 
| 77 | 
            +
                    "104" =>  "Deny - New card issued.",
         | 
| 78 | 
            +
                    "105" =>  "Deny - Account Cancelled.",
         | 
| 79 | 
            +
                    "106" =>  "Exceeded PIN Attempts.",
         | 
| 80 | 
            +
                    "107" =>  "Please Call Issuer.",
         | 
| 81 | 
            +
                    "109" =>  "Invalid merchant.",
         | 
| 82 | 
            +
                    "110" =>  "Invalid amount.",
         | 
| 83 | 
            +
                    "111" =>  "Invalid account.",
         | 
| 84 | 
            +
                    "115" =>  "Service not permitted.",
         | 
| 85 | 
            +
                    "117" =>  "Invalid PIN.",
         | 
| 86 | 
            +
                    "119" =>  "Card member not enrolled.",
         | 
| 87 | 
            +
                    "122" =>  "Invalid card (CID) security code.",
         | 
| 88 | 
            +
                    "125" =>  "Invalid effective date.",
         | 
| 89 | 
            +
                    "181" =>  "Format error.",
         | 
| 90 | 
            +
                    "182" =>  "Please wait.",
         | 
| 91 | 
            +
                    "183" =>  "Invalid currency code.",
         | 
| 92 | 
            +
                    "187" =>  "Deny - new card issued.",
         | 
| 93 | 
            +
                    "188" =>  "Deny - Expiration date required.",
         | 
| 94 | 
            +
                    "189" =>  "Deny - Cancelled or Closed Merchant/SE.",
         | 
| 95 | 
            +
                    "200" =>  "Deny - Pick up card.",
         | 
| 96 | 
            +
                    "400" =>  "Reversal accepted.",
         | 
| 97 | 
            +
                    "601" =>  "Reject - EMV Chip Declined Transaction.",
         | 
| 98 | 
            +
                    "602" =>  "Reject - Suspected Fraud.",
         | 
| 99 | 
            +
                    "603" =>  "Reject - Communications Error.",
         | 
| 100 | 
            +
                    "604" =>  "Reject - Insufficient Approval.",
         | 
| 101 | 
            +
                    "750" =>  "Velocity Check Fail.",
         | 
| 102 | 
            +
                    "899" =>  "Misc Decline.",
         | 
| 103 | 
            +
                    "900" =>  "Invalid Message Type.",
         | 
| 104 | 
            +
                    "901" =>  "Invalid Merchant ID.",
         | 
| 105 | 
            +
                    "903" =>  "Debit not supported.",
         | 
| 106 | 
            +
                    "904" =>  "Private label not supported.",
         | 
| 107 | 
            +
                    "905" =>  "Invalid card type.",
         | 
| 108 | 
            +
                    "906" =>  "Unit not active.",
         | 
| 109 | 
            +
                    "908" =>  "Manual card entry invalid.",
         | 
| 110 | 
            +
                    "909" =>  "Invalid track information.",
         | 
| 111 | 
            +
                    "911" =>  "Master merchant not found.",
         | 
| 112 | 
            +
                    "912" =>  "Invalid card format.",
         | 
| 113 | 
            +
                    "913" =>  "Invalid card type.",
         | 
| 114 | 
            +
                    "914" =>  "Invalid card length.",
         | 
| 115 | 
            +
                    "917" =>  "Expired card.",
         | 
| 116 | 
            +
                    "919" =>  "Invalid entry type.",
         | 
| 117 | 
            +
                    "920" =>  "Invalid amount.",
         | 
| 118 | 
            +
                    "921" =>  "Invalid messge format.",
         | 
| 119 | 
            +
                    "923" =>  "Invalid ABA.",
         | 
| 120 | 
            +
                    "924" =>  "Invalid DDA.",
         | 
| 121 | 
            +
                    "925" =>  "Invalid TID.",
         | 
| 122 | 
            +
                    "926" =>  "Invalid Password.",
         | 
| 123 | 
            +
                    "930" =>  "Invalid zipcode.",
         | 
| 124 | 
            +
                    "931" =>  "Invalid Address.",
         | 
| 125 | 
            +
                    "932" =>  "Invalid ZIP and Address.",
         | 
| 126 | 
            +
                    "933" =>  "Invalid CVV2.",
         | 
| 127 | 
            +
                    "934" =>  "Program Not Allowed.",
         | 
| 128 | 
            +
                    "935" =>  "Invalid Device/App.",
         | 
| 129 | 
            +
                    "940" =>  "Record Not Found.",
         | 
| 130 | 
            +
                    "941" =>  "Merchant ID error.",
         | 
| 131 | 
            +
                    "942" =>  "Refund Not Allowed.",
         | 
| 132 | 
            +
                    "943" =>  "Refund denied.",
         | 
| 133 | 
            +
                    "955" =>  "Invalid PIN block.",
         | 
| 134 | 
            +
                    "956" =>  "Invalid KSN.",
         | 
| 135 | 
            +
                    "958" =>  "Bad Status.",
         | 
| 136 | 
            +
                    "959" =>  "Seek Record limit exceeded.",
         | 
| 137 | 
            +
                    "960" =>  "Internal Key Database Error.",
         | 
| 138 | 
            +
                    "961" =>  "TRANS not Supported. Cash Disbursement required a specific MCC.",
         | 
| 139 | 
            +
                    "962" =>  "Invalid PIN key (Unknown KSN).",
         | 
| 140 | 
            +
                    "981" =>  "Invalid AVS.",
         | 
| 141 | 
            +
                    "987" =>  "Issuer Unavailable.",
         | 
| 142 | 
            +
                    "988" =>  "System error SD.",
         | 
| 143 | 
            +
                    "989" =>  "Database Error.",
         | 
| 144 | 
            +
                    "992" =>  "Transaction Timeout.",
         | 
| 145 | 
            +
                    "996" =>  "Bad Terminal ID.",
         | 
| 146 | 
            +
                    "997" =>  "Message rejected by association.",
         | 
| 147 | 
            +
                    "999" =>  "Communication failure",
         | 
| 148 | 
            +
                    nil   =>  "No response returned (missing credentials?)."
         | 
| 149 | 
            +
                  }
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                  def initialize(options = {})
         | 
| 152 | 
            +
                    requires!(options, :login)
         | 
| 153 | 
            +
                    super
         | 
| 154 | 
            +
                  end
         | 
| 155 | 
            +
             | 
| 156 | 
            +
                  def purchase(money, credit_card, options = {})
         | 
| 157 | 
            +
                    commit(money, build_sale_request(money, credit_card, options))
         | 
| 158 | 
            +
                  end
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                  def authorize(money, credit_card, options = {})
         | 
| 161 | 
            +
                    commit(money, build_authonly_request(money, credit_card, options))
         | 
| 162 | 
            +
                  end
         | 
| 163 | 
            +
             | 
| 164 | 
            +
                  def capture(money, reference, options = {})
         | 
| 165 | 
            +
                    split_authorization = reference.split(";")
         | 
| 166 | 
            +
                    transaction_id = split_authorization[0]
         | 
| 167 | 
            +
                    token = split_authorization[3]
         | 
| 168 | 
            +
                    commit(money, build_capture_request(transaction_id, money, options), token)
         | 
| 169 | 
            +
                  end
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                  def void(reference, options = {})
         | 
| 172 | 
            +
                    transaction_id, approval, amount, token = reference.split(";")
         | 
| 173 | 
            +
                    commit(amount.to_i, build_void_request(amount.to_i, transaction_id, approval, token, options), token)
         | 
| 174 | 
            +
                  end
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                  def credit(money, credit_card, options = {})
         | 
| 177 | 
            +
                    commit(money, build_credit_request(money, nil, credit_card, nil, options))
         | 
| 178 | 
            +
                  end
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                  def refund(money, reference, options = {})
         | 
| 181 | 
            +
                    split_authorization = reference.split(";")
         | 
| 182 | 
            +
                    transaction_id = split_authorization[0]
         | 
| 183 | 
            +
                    token = split_authorization[3]
         | 
| 184 | 
            +
                    commit(money, build_credit_request(money, transaction_id, nil, token, options))
         | 
| 185 | 
            +
                  end
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                  def verify(credit_card, options={})
         | 
| 188 | 
            +
                    authorize(0, credit_card, options)
         | 
| 189 | 
            +
                  end
         | 
| 190 | 
            +
             | 
| 191 | 
            +
                  def supports_scrubbing
         | 
| 192 | 
            +
                    true
         | 
| 193 | 
            +
                  end
         | 
| 194 | 
            +
             | 
| 195 | 
            +
                  def scrub(transcript)
         | 
| 196 | 
            +
                    transcript.
         | 
| 197 | 
            +
                      gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
         | 
| 198 | 
            +
                      gsub(%r((>)\d+(</CardNum>)), '\1[FILTERED]\2').
         | 
| 199 | 
            +
                      gsub(%r((<CVV2>)\d+(</CVV2>)), '\1[FILTERED]\2')
         | 
| 200 | 
            +
                  end
         | 
| 201 | 
            +
             | 
| 202 | 
            +
                  private
         | 
| 203 | 
            +
             | 
| 204 | 
            +
                  def build_xml_request(transaction_type, options = {}, transaction_id = nil, &block)
         | 
| 205 | 
            +
                    xml = Builder::XmlMarkup.new
         | 
| 206 | 
            +
                    xml.tag! 'JetPay', 'Version' => API_VERSION do
         | 
| 207 | 
            +
                      # Basic values needed for any request
         | 
| 208 | 
            +
                      xml.tag! 'TerminalID', @options[:login]
         | 
| 209 | 
            +
                      xml.tag! 'TransactionType', transaction_type
         | 
| 210 | 
            +
                      xml.tag! 'TransactionID', transaction_id.nil? ? generate_unique_id.slice(0, 18) : transaction_id
         | 
| 211 | 
            +
                      xml.tag! 'Origin', options[:origin] || 'INTERNET'
         | 
| 212 | 
            +
                      xml.tag! 'IndustryInfo', 'Type' => options[:industry_info] || 'ECOMMERCE'
         | 
| 213 | 
            +
                      xml.tag! 'Application', (options[:application] || 'n/a'), {'Version' => options[:application_version] || '1.0'}
         | 
| 214 | 
            +
                      xml.tag! 'Device', (options[:device] || 'n/a'), {'Version' => options[:device_version] || '1.0'}
         | 
| 215 | 
            +
                      xml.tag! 'Library', 'VirtPOS SDK', 'Version' => '1.5'
         | 
| 216 | 
            +
                      xml.tag! 'Gateway', 'JetPay'
         | 
| 217 | 
            +
                      xml.tag! 'DeveloperID', options[:developer_id] || 'n/a'
         | 
| 218 | 
            +
             | 
| 219 | 
            +
                      if block_given?
         | 
| 220 | 
            +
                        yield xml
         | 
| 221 | 
            +
                      else
         | 
| 222 | 
            +
                        xml.target!
         | 
| 223 | 
            +
                      end
         | 
| 224 | 
            +
                    end
         | 
| 225 | 
            +
                  end
         | 
| 226 | 
            +
             | 
| 227 | 
            +
                  def build_sale_request(money, credit_card, options)
         | 
| 228 | 
            +
                    build_xml_request('SALE', options) do |xml|
         | 
| 229 | 
            +
                      add_credit_card(xml, credit_card)
         | 
| 230 | 
            +
                      add_addresses(xml, options)
         | 
| 231 | 
            +
                      add_customer_data(xml, options)
         | 
| 232 | 
            +
                      add_invoice_data(xml, options)
         | 
| 233 | 
            +
                      add_user_defined_fields(xml, options)
         | 
| 234 | 
            +
                      xml.tag! 'TotalAmount', amount(money)
         | 
| 235 | 
            +
             | 
| 236 | 
            +
                      xml.target!
         | 
| 237 | 
            +
                    end
         | 
| 238 | 
            +
                  end
         | 
| 239 | 
            +
             | 
| 240 | 
            +
                  def build_authonly_request(money, credit_card, options)
         | 
| 241 | 
            +
                    build_xml_request('AUTHONLY', options) do |xml|
         | 
| 242 | 
            +
                      add_credit_card(xml, credit_card)
         | 
| 243 | 
            +
                      add_addresses(xml, options)
         | 
| 244 | 
            +
                      add_customer_data(xml, options)
         | 
| 245 | 
            +
                      add_invoice_data(xml, options)
         | 
| 246 | 
            +
                      add_user_defined_fields(xml, options)
         | 
| 247 | 
            +
                      xml.tag! 'TotalAmount', amount(money)
         | 
| 248 | 
            +
             | 
| 249 | 
            +
                      xml.target!
         | 
| 250 | 
            +
                    end
         | 
| 251 | 
            +
                  end
         | 
| 252 | 
            +
             | 
| 253 | 
            +
                  def build_capture_request(transaction_id, money, options)
         | 
| 254 | 
            +
                    build_xml_request('CAPT', options, transaction_id) do |xml|
         | 
| 255 | 
            +
                      add_invoice_data(xml, options)
         | 
| 256 | 
            +
                      add_user_defined_fields(xml, options)
         | 
| 257 | 
            +
                      xml.tag! 'TotalAmount', amount(money)
         | 
| 258 | 
            +
             | 
| 259 | 
            +
                      xml.target!
         | 
| 260 | 
            +
                    end
         | 
| 261 | 
            +
                  end
         | 
| 262 | 
            +
             | 
| 263 | 
            +
                  def build_void_request(money, transaction_id, approval, token, options)
         | 
| 264 | 
            +
                    build_xml_request('VOID', options, transaction_id) do |xml|
         | 
| 265 | 
            +
                      xml.tag! 'Approval', approval
         | 
| 266 | 
            +
                      xml.tag! 'TotalAmount', amount(money)
         | 
| 267 | 
            +
                      xml.tag! 'Token', token if token
         | 
| 268 | 
            +
             | 
| 269 | 
            +
                      xml.target!
         | 
| 270 | 
            +
                    end
         | 
| 271 | 
            +
                  end
         | 
| 272 | 
            +
             | 
| 273 | 
            +
                  def build_credit_request(money, transaction_id, card, token, options)
         | 
| 274 | 
            +
                    build_xml_request('CREDIT', options, transaction_id) do |xml|
         | 
| 275 | 
            +
                      add_credit_card(xml, card) if card
         | 
| 276 | 
            +
                      add_invoice_data(xml, options)
         | 
| 277 | 
            +
                      add_addresses(xml, options)
         | 
| 278 | 
            +
                      add_customer_data(xml, options)
         | 
| 279 | 
            +
                      add_user_defined_fields(xml, options)
         | 
| 280 | 
            +
                      xml.tag! 'TotalAmount', amount(money)
         | 
| 281 | 
            +
                      xml.tag! 'Token', token if token
         | 
| 282 | 
            +
             | 
| 283 | 
            +
                      xml.target!
         | 
| 284 | 
            +
                    end
         | 
| 285 | 
            +
                  end
         | 
| 286 | 
            +
             | 
| 287 | 
            +
                  def commit(money, request, token = nil)
         | 
| 288 | 
            +
                    response = parse(ssl_post(url, request))
         | 
| 289 | 
            +
             | 
| 290 | 
            +
                    success = success?(response)
         | 
| 291 | 
            +
                    Response.new(success,
         | 
| 292 | 
            +
                      success ? 'APPROVED' : message_from(response),
         | 
| 293 | 
            +
                      response,
         | 
| 294 | 
            +
                      :test => test?,
         | 
| 295 | 
            +
                      :authorization => authorization_from(response, money, token),
         | 
| 296 | 
            +
                      :avs_result => AVSResult.new(:code => response[:avs]),
         | 
| 297 | 
            +
                      :cvv_result => CVVResult.new(response[:cvv2]),
         | 
| 298 | 
            +
                      :error_code => success ? nil : error_code_from(response)
         | 
| 299 | 
            +
                    )
         | 
| 300 | 
            +
                  end
         | 
| 301 | 
            +
             | 
| 302 | 
            +
                  def url
         | 
| 303 | 
            +
                    test? ? test_url : live_url
         | 
| 304 | 
            +
                  end
         | 
| 305 | 
            +
             | 
| 306 | 
            +
                  def parse(body)
         | 
| 307 | 
            +
                    return {} if body.blank?
         | 
| 308 | 
            +
             | 
| 309 | 
            +
                    xml = REXML::Document.new(body)
         | 
| 310 | 
            +
             | 
| 311 | 
            +
                    response = {}
         | 
| 312 | 
            +
                    xml.root.elements.to_a.each do |node|
         | 
| 313 | 
            +
                      parse_element(response, node)
         | 
| 314 | 
            +
                    end
         | 
| 315 | 
            +
                    response
         | 
| 316 | 
            +
                  end
         | 
| 317 | 
            +
             | 
| 318 | 
            +
                  def parse_element(response, node)
         | 
| 319 | 
            +
                    if node.has_elements?
         | 
| 320 | 
            +
                      node.elements.each{|element| parse_element(response, element) }
         | 
| 321 | 
            +
                    else
         | 
| 322 | 
            +
                      response[node.name.underscore.to_sym] = node.text
         | 
| 323 | 
            +
                    end
         | 
| 324 | 
            +
                  end
         | 
| 325 | 
            +
             | 
| 326 | 
            +
                  def format_exp(value)
         | 
| 327 | 
            +
                    format(value, :two_digits)
         | 
| 328 | 
            +
                  end
         | 
| 329 | 
            +
             | 
| 330 | 
            +
                  def success?(response)
         | 
| 331 | 
            +
                    response[:action_code] == "000"
         | 
| 332 | 
            +
                  end
         | 
| 333 | 
            +
             | 
| 334 | 
            +
                  def message_from(response)
         | 
| 335 | 
            +
                    ACTION_CODE_MESSAGES[response[:action_code]]
         | 
| 336 | 
            +
                  end
         | 
| 337 | 
            +
             | 
| 338 | 
            +
                  def authorization_from(response, money, previous_token)
         | 
| 339 | 
            +
                    original_amount = amount(money) if money
         | 
| 340 | 
            +
                    [ response[:transaction_id], response[:approval], original_amount, (response[:token] || previous_token)].join(";")
         | 
| 341 | 
            +
                  end
         | 
| 342 | 
            +
             | 
| 343 | 
            +
                  def error_code_from(response)
         | 
| 344 | 
            +
                    response[:action_code]
         | 
| 345 | 
            +
                  end
         | 
| 346 | 
            +
             | 
| 347 | 
            +
                  def add_credit_card(xml, credit_card)
         | 
| 348 | 
            +
                    xml.tag! 'CardNum', credit_card.number, "CardPresent" => false, "Tokenize" => true
         | 
| 349 | 
            +
                    xml.tag! 'CardExpMonth', format_exp(credit_card.month)
         | 
| 350 | 
            +
                    xml.tag! 'CardExpYear', format_exp(credit_card.year)
         | 
| 351 | 
            +
             | 
| 352 | 
            +
                    if credit_card.first_name || credit_card.last_name
         | 
| 353 | 
            +
                      xml.tag! 'CardName', [credit_card.first_name,credit_card.last_name].compact.join(' ')
         | 
| 354 | 
            +
                    end
         | 
| 355 | 
            +
             | 
| 356 | 
            +
                    unless credit_card.verification_value.nil? || (credit_card.verification_value.length == 0)
         | 
| 357 | 
            +
                      xml.tag! 'CVV2', credit_card.verification_value
         | 
| 358 | 
            +
                    end
         | 
| 359 | 
            +
                  end
         | 
| 360 | 
            +
             | 
| 361 | 
            +
                  def add_addresses(xml, options)
         | 
| 362 | 
            +
                    if billing_address = options[:billing_address] || options[:address]
         | 
| 363 | 
            +
                      xml.tag! 'Billing' do
         | 
| 364 | 
            +
                        xml.tag! 'Address', [billing_address[:address1], billing_address[:address2]].compact.join(" ")
         | 
| 365 | 
            +
                        xml.tag! 'City', billing_address[:city]
         | 
| 366 | 
            +
                        xml.tag! 'StateProv', billing_address[:state]
         | 
| 367 | 
            +
                        xml.tag! 'PostalCode', billing_address[:zip]
         | 
| 368 | 
            +
                        xml.tag! 'Country', lookup_country_code(billing_address[:country])
         | 
| 369 | 
            +
                        xml.tag! 'Phone', billing_address[:phone]
         | 
| 370 | 
            +
                        xml.tag! 'Email', options[:email] if options[:email]
         | 
| 371 | 
            +
                      end
         | 
| 372 | 
            +
                    end
         | 
| 373 | 
            +
             | 
| 374 | 
            +
                    if shipping_address = options[:shipping_address]
         | 
| 375 | 
            +
                      xml.tag! 'Shipping' do
         | 
| 376 | 
            +
                        xml.tag! 'Name', shipping_address[:name]
         | 
| 377 | 
            +
                        xml.tag! 'Address', [shipping_address[:address1], shipping_address[:address2]].compact.join(" ")
         | 
| 378 | 
            +
                        xml.tag! 'City', shipping_address[:city]
         | 
| 379 | 
            +
                        xml.tag! 'StateProv', shipping_address[:state]
         | 
| 380 | 
            +
                        xml.tag! 'PostalCode', shipping_address[:zip]
         | 
| 381 | 
            +
                        xml.tag! 'Country', lookup_country_code(shipping_address[:country])
         | 
| 382 | 
            +
                        xml.tag! 'Phone', shipping_address[:phone]
         | 
| 383 | 
            +
                      end
         | 
| 384 | 
            +
                    end
         | 
| 385 | 
            +
                  end
         | 
| 386 | 
            +
             | 
| 387 | 
            +
                  def add_customer_data(xml, options)
         | 
| 388 | 
            +
                    xml.tag! 'UserIPAddress', options[:ip] if options[:ip]
         | 
| 389 | 
            +
                  end
         | 
| 390 | 
            +
             | 
| 391 | 
            +
                  def add_invoice_data(xml, options)
         | 
| 392 | 
            +
                    xml.tag! 'OrderNumber', options[:order_id] if options[:order_id]
         | 
| 393 | 
            +
                    if tax_amount = options[:tax_amount]
         | 
| 394 | 
            +
                      xml.tag! 'TaxAmount', tax_amount, {'ExemptInd' => options[:tax_exemption] || "false"}
         | 
| 395 | 
            +
                    end
         | 
| 396 | 
            +
                  end
         | 
| 397 | 
            +
             | 
| 398 | 
            +
                  def add_user_defined_fields(xml, options)
         | 
| 399 | 
            +
                    xml.tag! 'UDField1', options[:ud_field_1] if options[:ud_field_1]
         | 
| 400 | 
            +
                    xml.tag! 'UDField2', options[:ud_field_2] if options[:ud_field_2]
         | 
| 401 | 
            +
                    xml.tag! 'UDField3', options[:ud_field_3] if options[:ud_field_3]
         | 
| 402 | 
            +
                  end
         | 
| 403 | 
            +
             | 
| 404 | 
            +
                  def lookup_country_code(code)
         | 
| 405 | 
            +
                    country = Country.find(code) rescue nil
         | 
| 406 | 
            +
                    country && country.code(:alpha3)
         | 
| 407 | 
            +
                  end
         | 
| 408 | 
            +
                end
         | 
| 409 | 
            +
              end
         | 
| 410 | 
            +
            end
         |