<commit_before>require 'nokogiri'

module ActiveMerchant
  module Billing
    class BlueSnapGateway < Gateway
      self.test_url = "https://sandbox.bluesnap.com/services/2"
      self.live_url = "https://ws.bluesnap.com/services/2"
      self.supported_countries = %w(US GB)

      self.default_currency = 'USD'
      self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club, :maestro]

      self.homepage_url = 'https://home.bluesnap.com/'
      self.display_name = 'BlueSnap'

      TRANSACTIONS = {
        purchase: "AUTH_CAPTURE",
        authorize: "AUTH_ONLY",
        capture: "CAPTURE",
        void: "AUTH_REVERSAL",
        refund: "REFUND"
      }

      CVC_CODE_TRANSLATOR = {
        'MA' => 'M',
        'NC' => 'U',
        'ND' => 'P',
        'NM' => 'N',
        'NP' => 'S'
      }

      AVS_CODE_TRANSLATOR = {
        'line1: U, zip: U, name: U' => 'I',
        'line1: U, zip: U, name: M' => 'I',
        'line1: U, zip: U, name: N' => 'I',
        'line1: U, zip: M, name: U' => 'P',
        'line1: U, zip: M, name: M' => 'P',
        'line1: U, zip: M, name: N' => 'F',
        'line1: U, zip: N, name: U' => 'O',
        'line1: U, zip: N, name: M' => 'O',
        'line1: U, zip: N, name: N' => 'O',
        'line1: M, zip: U, name: U' => 'B',
        'line1: M, zip: U, name: M' => 'B',
        'line1: M, zip: U, name: N' => 'T',
        'line1: M, zip: M, name: U' => 'M',
        'line1: M, zip: M, name: M' => 'V',
        'line1: M, zip: M, name: N' => 'H',
        'line1: M, zip: N, name: U' => 'A',
        'line1: M, zip: N, name: M' => 'O',
        'line1: M, zip: N, name: N' => 'A',
        'line1: N, zip: U, name: U' => 'C',
        'line1: N, zip: U, name: M' => 'C',
        'line1: N, zip: U, name: N' => 'C',
        'line1: N, zip: M, name: U' => 'W',
        'line1: N, zip: M, name: M' => 'L',
        'line1: N, zip: M, name: N' => 'W',
        'line1: N, zip: N, name: U' => 'N',
        'line1: N, zip: N, name: M' => 'K',
        'line1: N, zip: N, name: N' => 'N',
      }

      def initialize(options={})
        requires!(options, :api_username, :api_password)
        super
      end

      def purchase(money, payment_method, options={})
        commit(:purchase) do |doc|
          add_auth_purchase(doc, money, payment_method, options)
        end
      end

      def authorize(money, payment_method, options={})
        commit(:authorize) do |doc|
          add_auth_purchase(doc, money, payment_method, options)
        end
      end

      def capture(money, authorization, options={})
        commit(:capture, :put) do |doc|
          add_authorization(doc, authorization)
          add_order(doc, options)
        end
      end

      def refund(money, authorization, options={})
        commit(:refund, :put) do |doc|
          add_authorization(doc, authorization)
          add_amount(doc, money)
          add_order(doc, options)
        end
      end

      def void(authorization, options={})
        commit(:void, :put) do |doc|
          add_authorization(doc, authorization)
          add_order(doc, options)
        end
      end

      def verify(payment_method, options={})
        authorize(0, payment_method, options)
      end

      def store(credit_card, options = {})
        commit(:store) do |doc|
          add_personal_info(doc, credit_card, options)
          doc.send("payment-sources") do
            doc.send("credit-card-info") do
              add_credit_card(doc, credit_card)
            end
          end
          add_order(doc, options)
        end
      end

      def verify_credentials
        begin
          ssl_get("#{url}/nonexistent", headers)
        rescue ResponseError => e
          return false if e.response.code.to_i == 401
        end

        true
      end

      def supports_scrubbing?
        true
      end

      def scrub(transcript)
        transcript.
          gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
          gsub(%r((<card-number>).+(</card-number>)), '\1[FILTERED]\2').
          gsub(%r((<security-code>).+(</security-code>)), '\1[FILTERED]\2')
      end

      private

      def add_auth_purchase(doc, money, payment_method, options)
        doc.send("recurring-transaction", options[:recurring] ? "RECURRING" : "ECOMMERCE")
        add_order(doc, options)
        add_amount(doc, money)
        doc.send("transaction-fraud-info") do
          doc.send("shopper-ip-address", options[:ip]) if options[:ip]
        end

        if payment_method.is_a?(String)
          doc.send("vaulted-shopper-id", payment_method)
        else
          doc.send("card-holder-info") do
            add_personal_info(doc, payment_method, options)
          end
          add_credit_card(doc, payment_method)
        end
      end

      def add_amount(doc, money)
        doc.amount(amount(money))
        doc.currency(options[:currency] || currency(money))
      end

      def add_personal_info(doc, credit_card, options)
        doc.send("first-name", credit_card.first_name)
        doc.send("last-name", credit_card.last_name)
        doc.email(options[:email]) if options[:email]
        add_address(doc, options)
      end

      def add_credit_card(doc, card)
        doc.send("credit-card") do
          doc.send("card-number", card.number)
          doc.send("security-code", card.verification_value)
          doc.send("expiration-month", card.month)
          doc.send("expiration-year", card.year)
        end
      end

      def add_description(doc, description)
        doc.send("transaction-meta-data") do
          doc.send("meta-data") do
            doc.send("meta-key", "description")
            doc.send("meta-value", truncate(description, 500))
            doc.send("meta-description", "Description")
          end
        end
      end

      def add_order(doc, options)
        doc.send("merchant-transaction-id", truncate(options[:order_id], 50)) if options[:order_id]
        doc.send("soft-descriptor", options[:soft_descriptor]) if options[:soft_descriptor]
        add_description(doc, options[:description]) if options[:description]
      end

      def add_address(doc, options)
        address = options[:billing_address]
        return unless address
<commit_msg>Update blue_snap.rb<commit_after>require 'nokogiri'

module ActiveMerchant
  module Billing
    class BlueSnapGateway < Gateway
      self.test_url = "https://sandbox.bluesnap.com/services/2"
      self.live_url = "https://ws.bluesnap.com/services/2"
      self.supported_countries = %w(US GB)

      self.default_currency = 'USD'
      self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club, :maestro]

      self.homepage_url = 'https://home.bluesnap.com/'
      self.display_name = 'BlueSnap'

      TRANSACTIONS = {
        purchase: "AUTH_CAPTURE",
        authorize: "AUTH_ONLY",
        capture: "CAPTURE",
        void: "AUTH_REVERSAL",
        refund: "REFUND"
      }

      CVC_CODE_TRANSLATOR = {
        'MA' => 'M',
        'NC' => 'U',
        'ND' => 'P',
        'NM' => 'N',
        'NP' => 'S'
      }

      AVS_CODE_TRANSLATOR = {
        'line1: U, zip: U, name: U' => 'I',
        'line1: U, zip: U, name: M' => 'I',
        'line1: U, zip: U, name: N' => 'I',
        'line1: U, zip: M, name: U' => 'P',
        'line1: U, zip: M, name: M' => 'P',
        'line1: U, zip: M, name: N' => 'F',
        'line1: U, zip: N, name: U' => 'O',
        'line1: U, zip: N, name: M' => 'O',
        'line1: U, zip: N, name: N' => 'O',
        'line1: M, zip: U, name: U' => 'B',
        'line1: M, zip: U, name: M' => 'B',
        'line1: M, zip: U, name: N' => 'T',
        'line1: M, zip: M, name: U' => 'M',
        'line1: M, zip: M, name: M' => 'V',
        'line1: M, zip: M, name: N' => 'H',
        'line1: M, zip: N, name: U' => 'A',
        'line1: M, zip: N, name: M' => 'O',
        'line1: M, zip: N, name: N' => 'A',
        'line1: N, zip: U, name: U' => 'C',
        'line1: N, zip: U, name: M' => 'C',
        'line1: N, zip: U, name: N' => 'C',
        'line1: N, zip: M, name: U' => 'W',
        'line1: N, zip: M, name: M' => 'L',
        'line1: N, zip: M, name: N' => 'W',
        'line1: N, zip: N, name: U' => 'N',
        'line1: N, zip: N, name: M' => 'K',
        'line1: N, zip: N, name: N' => 'N',
      }

      def initialize(options={})
        requires!(options, :api_username, :api_password)
        super
      end

      def purchase(money, payment_method, options={})
        commit(:purchase) do |doc|
          add_auth_purchase(doc, money, payment_method, options)
        end
      end

      def authorize(money, payment_method, options={})
        commit(:authorize) do |doc|
          add_auth_purchase(doc, money, payment_method, options)
        end
      end

      def capture(money, authorization, options={})
        commit(:capture, :put) do |doc|
          add_authorization(doc, authorization)
          add_order(doc, options)
        end
      end

      def refund(money, authorization, options={})
        commit(:refund, :put) do |doc|
          add_authorization(doc, authorization)
          add_amount(doc, money)
          add_order(doc, options)
        end
      end

      def void(authorization, options={})
        commit(:void, :put) do |doc|
          add_authorization(doc, authorization)
          add_order(doc, options)
        end
      end

      def verify(payment_method, options={})
        authorize(0, payment_method, options)
      end

      def store(credit_card, options = {})
        commit(:store) do |doc|
          add_personal_info(doc, credit_card, options)
          doc.send("payment-sources") do
            doc.send("credit-card-info") do
              add_credit_card(doc, credit_card)
            end
          end
          add_order(doc, options)
        end
      end

      def verify_credentials
        begin
          ssl_get("#{url}/nonexistent", headers)
        rescue ResponseError => e
          return false if e.response.code.to_i == 401
        end

        true
      end

      def supports_scrubbing?
        true
      end

      def scrub(transcript)
        transcript.
          gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
          gsub(%r((<card-number>).+(</card-number>)), '\1[FILTERED]\2').
          gsub(%r((<security-code>).+(</security-code>)), '\1[FILTERED]\2')
      end

      private

      def add_auth_purchase(doc, money, payment_method, options)
        doc.send("recurring-transaction", options[:recurring] ? "RECURRING" : "ECOMMERCE")
        add_order(doc, options)
        add_amount(doc, money)
        doc.send("transaction-fraud-info") do
          doc.send("shopper-ip-address", options[:ip]) if options[:ip]
        end

        if payment_method.is_a?(String)
          doc.send("vaulted-shopper-id", payment_method)
        else
          doc.send("card-holder-info") do
            add_personal_info(doc, payment_method, options)
          end
          add_credit_card(doc, payment_method)
        end
      end

      def add_amount(doc, money)
        doc.amount(amount(money))
        doc.currency(options[:currency] || currency(money))
      end

      def add_personal_info(doc, credit_card, options)
        doc.send("first-name", credit_card.first_name)
        doc.send("last-name", credit_card.last_name)
        doc.email(options[:email]) if options[:email]
        add_address(doc, options)
      end

      def add_credit_card(doc, card)
        doc.send("credit-card") do
          doc.send("card-number", card.number)
          doc.send("security-code", card.verification_value)
          doc.send("expiration-month", card.month)
          doc.send("expiration-year", card.year)
        end
      end

      def add_description(doc, description)
        doc.send("transaction-meta-data") do
          doc.send("meta-data") do
            doc.send("meta-key", "description")
            doc.send("meta-value", truncate(description, 500))
            doc.send("meta-description", "Description")
          end
        end
      end

      def add_order(doc, options)
        doc.send("merchant-transaction-id", truncate(options[:order_id], 50)) if options[:order_id]
        doc.send("soft-descriptor", options[:soft_descriptor]) if options[:soft_descriptor]
        add_description(doc, options[:description]) if options[:description]
        
        if options[:fraud_session_id]
          doc.send("fraud-info") do
            doc.send("fraud-session-id", options[:fraud_session_id])
          end
        end
      end

      def add_address(doc, options)
        address = options[:billing_address]
        return unless address

{'fraction_non_alphanumeric': 0.15126512651265125, 'fraction_numerical': 0.006757818639006758, 'mean_word_length': 2.118598382749326, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 77, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5519990', 'n_tokens_mistral': 4304, 'n_tokens_neox': 4183, 'n_words': 993}