class ActiveMerchant::Billing::FirstdataE4V27Gateway
Constants
- BRANDS
- DEFAULT_ECI
- SENSITIVE_FIELDS
- STANDARD_ERROR_CODE_MAPPING
- SUCCESS
- TRANSACTIONS
Public Class Methods
new(options = {})
click to toggle source
Calls superclass method
ActiveMerchant::Billing::Gateway::new
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 58 def initialize(options = {}) requires!(options, :login, :password, :key_id, :hmac_key) @options = options super end
Public Instance Methods
capture(money, authorization, options = {})
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 73 def capture(money, authorization, options = {}) commit(:capture, build_capture_or_credit_request(money, authorization, options)) end
purchase(money, credit_card_or_store_authorization, options = {})
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 69 def purchase(money, credit_card_or_store_authorization, options = {}) commit(:sale, build_sale_or_authorization_request(money, credit_card_or_store_authorization, options)) end
refund(money, authorization, options = {})
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 81 def refund(money, authorization, options = {}) commit(:credit, build_capture_or_credit_request(money, authorization, options)) end
scrub(transcript)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 114 def scrub(transcript) transcript. gsub(%r((<Card_Number>).+(</Card_Number>)), '\1[FILTERED]\2'). gsub(%r((<CVDCode>).+(</CVDCode>)), '\1[FILTERED]\2'). gsub(%r((<Password>).+(</Password>))i, '\1[FILTERED]\2'). gsub(%r((<CAVV>).+(</CAVV>)), '\1[FILTERED]\2'). gsub(%r((CARD NUMBER\s+: )#+\d+), '\1[FILTERED]') end
store(credit_card, options = {})
click to toggle source
Tokenize a credit card with TransArmor
The TransArmor token and other card data necessary for subsequent transactions is stored in the response’s authorization
attribute. The authorization string may be passed to authorize
and purchase
instead of a ActiveMerchant::Billing::CreditCard
instance.
TransArmor support must be explicitly activated on your gateway account by FirstData. If your authorization string is empty, contact FirstData support for account setup assistance.
support.payeezy.com/hc/en-us/articles/203731189-TransArmor-Tokenization
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 101 def store(credit_card, options = {}) commit(:store, build_store_request(credit_card, options), credit_card) end
supports_network_tokenization?()
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 123 def supports_network_tokenization? true end
supports_scrubbing?()
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 110 def supports_scrubbing? true end
verify(credit_card, options = {})
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 85 def verify(credit_card, options = {}) commit(:verify, build_sale_or_authorization_request(0, credit_card, options)) end
verify_credentials()
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 105 def verify_credentials response = void('0') response.message != 'Unauthorized Request. Bad or missing credentials.' end
void(authorization, options = {})
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 77 def void(authorization, options = {}) commit(:void, build_capture_or_credit_request(money_from_authorization(authorization), authorization, options)) end
Private Instance Methods
add_address(xml, options)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 300 def add_address(xml, options) if (address = options[:billing_address] || options[:address]) address = strip_line_breaks(address) xml.tag! 'Address' do xml.tag! 'Address1', address[:address1] xml.tag! 'Address2', address[:address2] if address[:address2] xml.tag! 'City', address[:city] xml.tag! 'State', address[:state] xml.tag! 'Zip', address[:zip] xml.tag! 'CountryCode', address[:country] end xml.tag! 'ZipCode', address[:zip] end end
add_amount(xml, money, options)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 200 def add_amount(xml, money, options) currency_code = options[:currency] || default_currency xml.tag! 'DollarAmount', localized_amount(money, currency_code) xml.tag! 'Currency', currency_code end
add_card_authentication_data(xml, options)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 259 def add_card_authentication_data(xml, options) xml.tag! 'CAVV', options[:cavv] xml.tag! 'XID', options[:xid] end
add_credentials(xml)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 184 def add_credentials(xml) xml.tag! 'ExactID', @options[:login] xml.tag! 'Password', @options[:password] end
add_credit_card(xml, credit_card, options)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 206 def add_credit_card(xml, credit_card, options) if credit_card.respond_to?(:track_data) && credit_card.track_data.present? xml.tag! 'Track1', credit_card.track_data xml.tag! 'Ecommerce_Flag', 'R' else xml.tag! 'Card_Number', credit_card.number xml.tag! 'Expiry_Date', expdate(credit_card) xml.tag! 'CardHoldersName', credit_card.name xml.tag! 'CardType', card_type(credit_card.brand) add_wallet_provider_id(xml, credit_card, options) add_credit_card_eci(xml, credit_card, options) add_credit_card_verification_strings(xml, credit_card, options) end end
add_credit_card_eci(xml, credit_card, options)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 222 def add_credit_card_eci(xml, credit_card, options) eci = if credit_card.is_a?(NetworkTokenizationCreditCard) && credit_card.source == :apple_pay && card_brand(credit_card) == 'discover' # Payeezy requires an ECI of 5 for apple pay transactions # See: https://support.payeezy.com/hc/en-us/articles/203730589-Ecommerce-Flag-Values '05' else (credit_card.respond_to?(:eci) ? credit_card.eci : nil) || options[:eci] || DEFAULT_ECI end xml.tag! 'Ecommerce_Flag', /^[0-9]+$/.match?(eci.to_s) ? eci.to_s.rjust(2, '0') : eci end
add_credit_card_token(xml, store_authorization, options)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 264 def add_credit_card_token(xml, store_authorization, options) params = store_authorization.split(';') credit_card = CreditCard.new( brand: params[1], first_name: params[2], last_name: params[3], month: params[4], year: params[5] ) xml.tag! 'TransarmorToken', params[0] xml.tag! 'Expiry_Date', expdate(credit_card) xml.tag! 'CardHoldersName', credit_card.name xml.tag! 'CardType', card_type(credit_card.brand) add_wallet_provider_id(xml, credit_card, options) add_card_authentication_data(xml, options) end
add_credit_card_verification_strings(xml, credit_card, options)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 234 def add_credit_card_verification_strings(xml, credit_card, options) if credit_card.is_a?(NetworkTokenizationCreditCard) add_network_tokenization_credit_card(xml, credit_card) else if credit_card.verification_value? xml.tag! 'CVD_Presence_Ind', '1' xml.tag! 'CVDCode', credit_card.verification_value end add_card_authentication_data(xml, options) end end
add_customer_data(xml, options)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 294 def add_customer_data(xml, options) xml.tag! 'Customer_Ref', options[:customer] if options[:customer] xml.tag! 'Client_IP', options[:ip] if options[:ip] xml.tag! 'Client_Email', options[:email] if options[:email] end
add_identification(xml, identification)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 193 def add_identification(xml, identification) authorization_num, transaction_tag, = identification.split(';') xml.tag! 'Authorization_Num', authorization_num xml.tag! 'Transaction_Tag', transaction_tag end
add_invoice(xml, options)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 322 def add_invoice(xml, options) xml.tag! 'Reference_No', options[:order_id] xml.tag! 'Reference_3', options[:description] if options[:description] end
add_level_3(xml, options)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 332 def add_level_3(xml, options) xml.tag!('Level3') { |x| x << options[:level_3] } if options[:level_3] end
add_network_tokenization_credit_card(xml, credit_card)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 247 def add_network_tokenization_credit_card(xml, credit_card) case card_brand(credit_card).to_sym when :american_express cryptogram = Base64.decode64(credit_card.payment_cryptogram) xml.tag!('XID', Base64.encode64(cryptogram[20...40])) xml.tag!('CAVV', Base64.encode64(cryptogram[0...20])) else xml.tag!('XID', credit_card.transaction_id) if credit_card.transaction_id xml.tag!('CAVV', credit_card.payment_cryptogram) end end
add_stored_credentials(xml, card, options)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 336 def add_stored_credentials(xml, card, options) return unless options[:stored_credential] xml.tag! 'StoredCredentials' do xml.tag! 'Indicator', stored_credential_indicator(xml, card, options) if initiator = options.dig(:stored_credential, :initiator) xml.tag! 'Initiation', initiator == 'merchant' ? 'M' : 'C' end if reason_type = options.dig(:stored_credential, :reason_type) xml.tag! 'Schedule', reason_type == 'unscheduled' ? 'U' : 'S' end xml.tag! 'AuthorizationTypeOverride', options[:authorization_type_override] if options[:authorization_type_override] if network_transaction_id = options[:stored_credential][:network_transaction_id] xml.tag! 'TransactionId', network_transaction_id else xml.tag! 'TransactionId', 'new' end xml.tag! 'OriginalAmount', options[:original_amount] if options[:original_amount] xml.tag! 'ProtectbuyIndicator', options[:protectbuy_indicator] if options[:protectbuy_indicator] end end
add_tax_fields(xml, options)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 327 def add_tax_fields(xml, options) xml.tag! 'Tax1Amount', options[:tax1_amount] if options[:tax1_amount] xml.tag! 'Tax1Number', options[:tax1_number] if options[:tax1_number] end
add_transaction_type(xml, action)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 189 def add_transaction_type(xml, action) xml.tag! 'Transaction_Type', TRANSACTIONS[action] end
add_wallet_provider_id(xml, credit_card, options)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 283 def add_wallet_provider_id(xml, credit_card, options) provider_id = if options[:wallet_provider_id] options[:wallet_provider_id] elsif credit_card.is_a?(NetworkTokenizationCreditCard) && credit_card.source == :apple_pay # See: https://support.payeezy.com/hc/en-us/articles/206601408-First-Data-Payeezy-Gateway-Web-Service-API-Reference-Guide#3.9 4 end xml.tag! 'WalletProviderID', provider_id if provider_id end
build_capture_or_credit_request(money, identification, options)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 163 def build_capture_or_credit_request(money, identification, options) xml = Builder::XmlMarkup.new add_identification(xml, identification) add_amount(xml, money, options) add_customer_data(xml, options) add_card_authentication_data(xml, options) xml.target! end
build_request(action, body)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 129 def build_request(action, body) xml = Builder::XmlMarkup.new xml.instruct! xml.tag! 'Transaction', xmlns: 'http://secure2.e-xact.com/vplug-in/transaction/rpc-enc/encodedTypes' do add_credentials(xml) add_transaction_type(xml, action) xml << body end xml.target! end
build_store_request(credit_card, options)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 174 def build_store_request(credit_card, options) xml = Builder::XmlMarkup.new add_credit_card(xml, credit_card, options) add_address(xml, options) add_customer_data(xml, options) xml.target! end
card_type(credit_card_brand)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 370 def card_type(credit_card_brand) BRANDS[credit_card_brand.to_sym] if credit_card_brand end
commit(action, data, credit_card = nil)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 374 def commit(action, data, credit_card = nil) url = (test? ? self.test_url : self.live_url) request = build_request(action, data) begin response = parse(ssl_post(url, request, headers('POST', url, request))) rescue ResponseError => e response = parse_error(e.response) end Response.new( successful?(response), message_from(response), response, test: test?, authorization: successful?(response) ? response_authorization(action, response, credit_card) : '', avs_result: { code: response[:avs] }, cvv_result: response[:cvv2], error_code: standard_error_code(response) ) end
expdate(credit_card)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 366 def expdate(credit_card) "#{format(credit_card.month, :two_digits)}#{format(credit_card.year, :two_digits)}" end
headers(method, url, request)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 395 def headers(method, url, request) content_type = 'application/xml' content_digest = Digest::SHA1.hexdigest(request) sending_time = Time.now.utc.iso8601 payload = [method, content_type, content_digest, sending_time, url.split('.com')[1]].join("\n") hmac = OpenSSL::HMAC.digest('sha1', @options[:hmac_key], payload) encoded = Base64.strict_encode64(hmac) { 'x-gge4-date' => sending_time, 'x-gge4-content-sha1' => content_digest, 'Authorization' => 'GGE4_API ' + @options[:key_id].to_s + ':' + encoded, 'Accepts' => content_type, 'Content-Type' => content_type } end
message_from(response)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 456 def message_from(response) if response[:faultcode] && response[:faultstring] response[:faultstring] elsif response[:error_number] && response[:error_number] != '0' response[:error_description] else result = (response[:exact_message] || '') result << " - #{response[:bank_message]}" if response[:bank_message].present? result end end
name_node(root, node)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 503 def name_node(root, node) parent = root.name unless root.name == 'TransactionResult' "#{parent}#{node.name}".gsub(/EXact/, 'Exact').underscore.to_sym end
parse(xml)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 481 def parse(xml) response = {} xml = REXML::Document.new(xml) if (root = REXML::XPath.first(xml, '//TransactionResult')) parse_elements(response, root) end SENSITIVE_FIELDS.each { |key| response.delete(key) } response end
parse_elements(response, root)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 493 def parse_elements(response, root) root.elements.to_a.each do |node| if node.has_elements? parse_elements(response, node) else response[name_node(root, node)] = (node.text || '').strip end end end
parse_error(error)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 468 def parse_error(error) { transaction_approved: 'false', error_number: error.code, error_description: error.body, ecommerce_error_code: error.body.gsub(/[^\d]/, '') } end
standard_error_code(response)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 477 def standard_error_code(response) STANDARD_ERROR_CODE_MAPPING[response[:bank_resp_code] || response[:ecommerce_error_code]] end
stored_credential_indicator(xml, card, options)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 358 def stored_credential_indicator(xml, card, options) if card.brand == 'master' || options.dig(:stored_credential, :initial_transaction) == false 'S' else '1' end end
strip_line_breaks(address)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 316 def strip_line_breaks(address) return unless address.is_a?(Hash) address.map { |k, s| [k, s&.tr("\r\n", ' ')&.strip] }.to_h end
successful?(response)
click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4_v27.rb, line 412 def successful?(response) response[:transaction_approved] == SUCCESS end