activemerchant/active_merchant

View on GitHub
lib/active_merchant/billing/gateways/shift4_v2.rb

Summary

Maintainability
A
0 mins
Test Coverage
module ActiveMerchant #:nodoc:
  module Billing #:nodoc:
    class Shift4V2Gateway < SecurionPayGateway
      # same endpont for testing
      self.live_url = 'https://api.shift4.com/'
      self.display_name = 'Shift4'
      self.homepage_url = 'https://dev.shift4.com/us/'

      def credit(money, payment, options = {})
        post = create_post_for_auth_or_purchase(money, payment, options)
        commit('credits', post, options)
      end

      def store(payment_method, options = {})
        post = case payment_method
               when CreditCard
                 cc = {}.tap { |card| add_creditcard(card, payment_method, options) }[:card]
                 options[:customer_id].blank? ? { email: options[:email], card: cc } : cc
               when Check
                 bank_account_object(payment_method, options)
               else
                 raise ArgumentError.new("Unhandled payment method #{payment_method.class}.")
               end

        commit url_for_store(payment_method, options), post, options
      end

      def url_for_store(payment_method, options = {})
        case payment_method
        when CreditCard
          options[:customer_id].blank? ? 'customers' : "customers/#{options[:customer_id]}/cards"
        when Check then 'payment-methods'
        end
      end

      def unstore(reference, options = {})
        commit("customers/#{options[:customer_id]}/cards/#{reference}", nil, options, :delete)
      end

      def create_post_for_auth_or_purchase(money, payment, options)
        super.tap do |post|
          add_stored_credentials(post, options)
        end
      end

      def add_stored_credentials(post, options)
        return unless options[:stored_credential].present?

        initiator = options.dig(:stored_credential, :initiator)
        reason_type = options.dig(:stored_credential, :reason_type)

        post_type = {
          %w[cardholder recurring] => 'first_recurring',
          %w[merchant recurring] => 'subsequent_recurring',
          %w[cardholder unscheduled] => 'customer_initiated',
          %w[merchant installment] => 'merchant_initiated'
        }[[initiator, reason_type]]
        post[:type] = post_type if post_type
      end

      def headers(options = {})
        super.tap do |headers|
          headers['User-Agent'] = "Shift4/v2 ActiveMerchantBindings/#{ActiveMerchant::VERSION}"
        end
      end

      def scrub(transcript)
        super.
          gsub(%r((card\[expMonth\]=)\d+), '\1[FILTERED]').
          gsub(%r((card\[expYear\]=)\d+), '\1[FILTERED]').
          gsub(%r((card\[cardholderName\]=)\w+[^ ]\w+), '\1[FILTERED]')
      end

      def json_error(raw_response)
        super(raw_response, 'Shift4 V2')
      end

      def add_amount(post, money, options, include_currency = false)
        super
        post[:currency]&.upcase!
      end

      def add_creditcard(post, payment_method, options)
        return super unless payment_method.is_a?(Check)

        post[:paymentMethod] = bank_account_object(payment_method, options)
      end

      def bank_account_object(payment_method, options)
        {
          type: :ach,
          fraudCheckData: {
            ipAddress: options[:ip],
            email: options[:email]
          }.compact,
          billing: {
            name: payment_method.name,
            address: { country: options.dig(:billing_address, :country) }
          }.compact,
          ach: {
            account: {
              routingNumber: payment_method.routing_number,
              accountNumber: payment_method.account_number,
              accountType: get_account_type(payment_method)
            },
            verificationProvider: :external
          }
        }
      end

      def get_account_type(check)
        holder = (check.account_holder_type || '').match(/business/i) ? :corporate : :personal
        "#{holder}_#{check.account_type}"
      end
    end
  end
end