class ActiveMerchant::Billing::BlueSnapGateway
Constants
- AVS_CODE_TRANSLATOR
- BANK_ACCOUNT_TYPE_MAPPING
- CVC_CODE_TRANSLATOR
- SHOPPER_INITIATOR
- STATE_CODE_COUNTRIES
- TRANSACTIONS
Public Class Methods
new(options = {})
click to toggle source
Calls superclass method
ActiveMerchant::Billing::Gateway::new
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 75 def initialize(options = {}) requires!(options, :api_username, :api_password) super end
Public Instance Methods
capture(money, authorization, options = {})
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 98 def capture(money, authorization, options = {}) commit(:capture, options, :put) do |doc| add_authorization(doc, authorization) add_order(doc, options) add_amount(doc, money, options) if options[:include_capture_amount] == true end end
purchase(money, payment_method, options = {})
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 80 def purchase(money, payment_method, options = {}) payment_method_details = PaymentMethodDetails.new(payment_method) commit(:purchase, options, :post, payment_method_details) do |doc| if payment_method_details.alt_transaction? add_alt_transaction_purchase(doc, money, payment_method_details, options) else add_auth_purchase(doc, money, payment_method, options) end end end
refund(money, authorization, options = {})
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 106 def refund(money, authorization, options = {}) options[:endpoint] = options[:merchant_transaction_id] ? "/refund/merchant/#{options[:merchant_transaction_id]}" : "/refund/#{authorization}" commit(:refund, options, :post) do |doc| add_amount(doc, money, options) if money %i[reason cancel_subscription tax_amount].each { |field| send_when_present(doc, field, options) } add_metadata(doc, options) end end
scrub(transcript)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 167 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'). gsub(%r((<(?:public-)?account-number>).+(</(?:public-)?account-number>)), '\1[FILTERED]\2'). gsub(%r((<(?:public-)?routing-number>).+(</(?:public-)?routing-number>)), '\1[FILTERED]\2') end
store(payment_method, options = {})
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 126 def store(payment_method, options = {}) payment_method_details = PaymentMethodDetails.new(payment_method) commit(:store, options, :post, payment_method_details) do |doc| add_personal_info(doc, payment_method, options) add_echeck_company(doc, payment_method) if payment_method_details.check? doc.send('payment-sources') do payment_method_details.check? ? store_echeck(doc, payment_method) : store_credit_card(doc, payment_method) end add_order(doc, options) end end
store_credit_card(doc, payment_method)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 139 def store_credit_card(doc, payment_method) doc.send('credit-card-info') do add_credit_card(doc, payment_method) end end
store_echeck(doc, payment_method)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 145 def store_echeck(doc, payment_method) doc.send('ecp-info') do doc.send('ecp') do add_echeck(doc, payment_method) end end end
supports_scrubbing?()
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 163 def supports_scrubbing? true end
verify(payment_method, options = {})
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 122 def verify(payment_method, options = {}) authorize(0, payment_method, options) end
verify_credentials()
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 153 def verify_credentials begin ssl_get(url.to_s, headers(options)) rescue ResponseError => e return false if e.response.code.to_i == 401 end true end
void(authorization, options = {})
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 115 def void(authorization, options = {}) commit(:void, options, :put) do |doc| add_authorization(doc, authorization) add_order(doc, options) end end
Private Instance Methods
add_3ds(doc, three_d_secure_options)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 281 def add_3ds(doc, three_d_secure_options) eci = three_d_secure_options[:eci] cavv = three_d_secure_options[:cavv] xid = three_d_secure_options[:xid] ds_transaction_id = three_d_secure_options[:ds_transaction_id] version = three_d_secure_options[:version] doc.send('three-d-secure') do doc.eci(eci) if eci doc.cavv(cavv) if cavv doc.xid(xid) if xid doc.send('three-d-secure-version', version) if version doc.send('ds-transaction-id', ds_transaction_id) if ds_transaction_id end end
add_address(doc, options)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 269 def add_address(doc, options) address = options[:billing_address] return unless address doc.country(address[:country]) if address[:country] doc.state(address[:state]) if address[:state] && STATE_CODE_COUNTRIES.include?(address[:country]) doc.address(address[:address1]) if address[:address1] doc.address2(address[:address2]) if address[:address2] doc.city(address[:city]) if address[:city] doc.zip(address[:zip]) if address[:zip] end
add_alt_transaction_purchase(doc, money, payment_method_details, options)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 367 def add_alt_transaction_purchase(doc, money, payment_method_details, 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] doc.send('descriptor-phone-number', options[:descriptor_phone_number]) if options[:descriptor_phone_number] add_amount(doc, money, options) vaulted_shopper_id = payment_method_details.vaulted_shopper_id doc.send('vaulted-shopper-id', vaulted_shopper_id) if vaulted_shopper_id add_echeck_transaction(doc, payment_method_details.payment_method, options, vaulted_shopper_id.present?) if payment_method_details.check? add_fraud_info(doc, payment_method_details.payment_method, options) add_stored_credentials(doc, options) add_metadata(doc, options) end
add_amount(doc, money, options)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 209 def add_amount(doc, money, options) currency = options[:currency] || currency(money) doc.amount(localized_amount(money, currency)) doc.currency(currency) end
add_auth_purchase(doc, money, payment_method, options)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 178 def add_auth_purchase(doc, money, payment_method, options) doc.send('recurring-transaction', options[:recurring] ? 'RECURRING' : 'ECOMMERCE') add_order(doc, options) doc.send('store-card', options[:store_card] || false) add_amount(doc, money, options) add_fraud_info(doc, payment_method, options) add_stored_credentials(doc, options) 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
add_credit_card(doc, card)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 224 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
add_echeck(doc, check)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 402 def add_echeck(doc, check) doc.send('account-number', check.account_number) doc.send('routing-number', check.routing_number) doc.send('account-type', BANK_ACCOUNT_TYPE_MAPPING["#{check.account_holder_type}_#{check.account_type}"]) end
add_echeck_company(doc, check)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 398 def add_echeck_company(doc, check) doc.send('company-name', truncate(check.name, 50)) if check.account_holder_type = 'business' end
add_echeck_transaction(doc, check, options, vaulted_shopper)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 383 def add_echeck_transaction(doc, check, options, vaulted_shopper) unless vaulted_shopper doc.send('payer-info') do add_personal_info(doc, check, options) add_echeck_company(doc, check) end end doc.send('ecp-transaction') do add_echeck(doc, check) unless vaulted_shopper end doc.send('authorized-by-shopper', options[:authorized_by_shopper]) end
add_fraud_info(doc, payment_method, options)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 338 def add_fraud_info(doc, payment_method, options) doc.send('transaction-fraud-info') do doc.send('shopper-ip-address', options[:ip]) if options[:ip] if fraud_info = options[:transaction_fraud_info] doc.send('fraud-session-id', fraud_info[:fraud_session_id]) if fraud_info[:fraud_session_id] end unless payment_method.is_a? String doc.send('shipping-contact-info') do add_shipping_contact_info(doc, payment_method, options) end end end end
add_level_3_data(doc, options)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 297 def add_level_3_data(doc, options) return unless options[:customer_reference_number] doc.send('level-3-data') do send_when_present(doc, :customer_reference_number, options) send_when_present(doc, :sales_tax_amount, options) send_when_present(doc, :freight_amount, options) send_when_present(doc, :duty_amount, options) send_when_present(doc, :destination_zip_code, options) send_when_present(doc, :destination_country_code, options) send_when_present(doc, :ship_from_zip_code, options) send_when_present(doc, :discount_amount, options) send_when_present(doc, :tax_amount, options) send_when_present(doc, :tax_rate, options) add_level_3_data_items(doc, options[:level_3_data_items]) if options[:level_3_data_items] end end
add_level_3_data_items(doc, items)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 323 def add_level_3_data_items(doc, items) items.each do |item| doc.send('level-3-data-item') do item.each do |key, value| key = key.to_s.dasherize doc.send(key, value) end end end end
add_metadata(doc, options)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 233 def add_metadata(doc, options) transaction_meta_data = options[:transaction_meta_data] || [] return if transaction_meta_data.empty? && !options[:description] doc.send('transaction-meta-data') do # ensure backwards compatibility for calls expecting :description # to become meta-data fields. if options[:description] doc.send('meta-data') do doc.send('meta-key', 'description') doc.send('meta-value', truncate(options[:description], 500)) doc.send('meta-description', 'Description') end end # https://developers.bluesnap.com/v8976-XML/docs/meta-data transaction_meta_data.each do |entry| doc.send('meta-data') do doc.send('meta-key', truncate(entry[:meta_key], 40)) doc.send('meta-value', truncate(entry[:meta_value], 500)) doc.send('meta-description', truncate(entry[:meta_description], 40)) doc.send('is-visible', truncate(entry[:meta_is_visible], 5)) end end end end
add_order(doc, options)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 260 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] doc.send('descriptor-phone-number', options[:descriptor_phone_number]) if options[:descriptor_phone_number] add_metadata(doc, options) add_3ds(doc, options[:three_d_secure]) if options[:three_d_secure] add_level_3_data(doc, options) end
add_personal_info(doc, payment_method, options)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 215 def add_personal_info(doc, payment_method, options) doc.send('first-name', payment_method.first_name) doc.send('last-name', payment_method.last_name) doc.send('personal-identification-number', options[:personal_identification_number]) if options[:personal_identification_number] doc.email(options[:email]) if options[:email] doc.phone(options[:phone_number]) if options[:phone_number] add_address(doc, options) end
add_shipping_contact_info(doc, payment_method, options)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 352 def add_shipping_contact_info(doc, payment_method, options) if address = options[:shipping_address] # https://developers.bluesnap.com/v8976-XML/docs/shipping-contact-info doc.send('first-name', payment_method.first_name) doc.send('last-name', payment_method.last_name) doc.country(address[:country]) if address[:country] doc.state(address[:state]) if address[:state] && STATE_CODE_COUNTRIES.include?(address[:country]) doc.address1(address[:address1]) if address[:address1] doc.address2(address[:address2]) if address[:address2] doc.city(address[:city]) if address[:city] doc.zip(address[:zip]) if address[:zip] end end
add_stored_credentials(doc, options)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 196 def add_stored_credentials(doc, options) return unless stored_credential = options[:stored_credential] initiator = stored_credential[:initiator]&.upcase initiator = 'SHOPPER' if SHOPPER_INITIATOR.include?(initiator) doc.send('transaction-initiator', initiator) if stored_credential[:initiator] if stored_credential[:network_transaction_id] doc.send('network-transaction-info') do doc.send('original-network-transaction-id', stored_credential[:network_transaction_id]) end end end
api_request(action, request, verb, payment_method_details, options)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 456 def api_request(action, request, verb, payment_method_details, options) ssl_request(verb, url(action, options, payment_method_details), request, headers(options)) rescue ResponseError => e e.response end
avs_lookup_key(p)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 495 def avs_lookup_key(p) "line1: #{p['avs-response-code-address']}, zip: #{p['avs-response-code-zip']}, name: #{p['avs-response-code-name']}" end
avs_result(parsed)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 491 def avs_result(parsed) AVSResult.new(code: AVS_CODE_TRANSLATOR[avs_lookup_key(parsed)]) end
bad_authentication_response()
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 594 def bad_authentication_response { 'description' => 'Unable to authenticate. Please check your credentials.' } end
build_xml_request(action, payment_method_details) { |doc| ... }
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 576 def build_xml_request(action, payment_method_details) builder = Nokogiri::XML::Builder.new builder.__send__(root_element(action, payment_method_details), root_attributes) do |doc| doc.send('card-transaction-type', TRANSACTIONS[action]) if TRANSACTIONS[action] && !payment_method_details.alt_transaction? && action != :refund yield(doc) end builder.doc.root.to_xml end
commit(action, options, verb = :post, payment_method_details = PaymentMethodDetails.new(), &block)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 462 def commit(action, options, verb = :post, payment_method_details = PaymentMethodDetails.new(), &block) request = build_xml_request(action, payment_method_details, &block) response = api_request(action, request, verb, payment_method_details, options) parsed = parse(response) succeeded = success_from(action, response) Response.new( succeeded, message_from(succeeded, response), parsed, authorization: authorization_from(action, parsed, payment_method_details), avs_result: avs_result(parsed), cvv_result: cvv_result(parsed), error_code: error_code_from(parsed), test: test? ) end
cvv_result(parsed)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 487 def cvv_result(parsed) CVVResult.new(CVC_CODE_TRANSLATOR[parsed['cvv-response-code']]) end
error_code_from(parsed_response)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 547 def error_code_from(parsed_response) parsed_response['code'] end
fraud_codes_from(response)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 514 def fraud_codes_from(response) event_summary = {} doc = Nokogiri::XML(response.body) fraud_events = doc.xpath('//xmlns:fraud-events', 'xmlns' => 'http://ws.plimus.com') fraud_events.children.each do |child| if child.children.children.any? event_summary[child.name] = event_summary[child.name] || [] event = {} child.children.each do |chi| event[chi.name] = chi.text end event_summary[child.name] << event else event_summary[child.name] = child.text end end event_summary.to_json end
generic_error_response(body)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 598 def generic_error_response(body) { 'description' => body } end
handle_response(response)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 585 def handle_response(response) case response.code.to_i when 200...300 response else raise ResponseError.new(response) end end
headers(options)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 564 def headers(options) idempotency_key = options[:idempotency_key] if options[:idempotency_key] headers = { 'Content-Type' => 'application/xml', 'Authorization' => ('Basic ' + Base64.strict_encode64("#{@options[:api_username]}:#{@options[:api_password]}").strip) } headers['Idempotency-Key'] = idempotency_key if idempotency_key headers end
message_from(succeeded, response)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 503 def message_from(succeeded, response) return 'Success' if succeeded parsed = parse(response) if parsed.dig('error-name') == 'FRAUD_DETECTED' fraud_codes_from(response) else parsed['description'] end end
parse(response)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 408 def parse(response) return bad_authentication_response if response.code.to_i == 401 return generic_error_response(response.body) if [403, 405, 429].include?(response.code.to_i) parsed = {} doc = Nokogiri::XML(response.body) doc.root.xpath('*').each do |node| name = node.name.downcase if node.elements.empty? parsed[name] = node.text elsif name == 'transaction-meta-data' metadata = [] node.elements.each { |m| metadata.push parse_metadata_entry(m) } parsed['transaction-meta-data'] = metadata else node.elements.each { |childnode| parse_element(parsed, childnode) } end end parsed['content-location-header'] = response['content-location'] parsed end
parse_element(parsed, node)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 448 def parse_element(parsed, node) if node.elements.empty? parsed[node.name.downcase] = node.text else node.elements.each { |e| parse_element(parsed, e) } end end
parse_metadata_entry(node)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 436 def parse_metadata_entry(node) entry = {} node.elements.each { |e| entry = entry.merge({ e.name => e.text }) } entry end
root_attributes()
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 551 def root_attributes { xmlns: 'http://ws.plimus.com' } end
root_element(action, payment_method_details)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 557 def root_element(action, payment_method_details) return 'refund' if action == :refund return 'vaulted-shopper' if action == :store payment_method_details.root_element end
send_when_present(doc, options_key, options, xml_element_name = nil)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 315 def send_when_present(doc, options_key, options, xml_element_name = nil) return unless options[options_key] xml_element_name ||= options_key.to_s doc.send(xml_element_name.dasherize, options[options_key]) end
success_from(action, response)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 499 def success_from(action, response) (200...300).cover?(response.code.to_i) end
url(action = nil, options = {}, payment_method_details = PaymentMethodDetails.new())
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 480 def url(action = nil, options = {}, payment_method_details = PaymentMethodDetails.new()) base = test? ? test_url : live_url resource = action == :store ? 'vaulted-shoppers' : payment_method_details.resource_url resource += options[:endpoint] if action == :refund "#{base}/#{resource}" end
vaulted_shopper_id(parsed_response, payment_method_details)
click to toggle source
# File lib/active_merchant/billing/gateways/blue_snap.rb, line 539 def vaulted_shopper_id(parsed_response, payment_method_details) return nil unless parsed_response['content-location-header'] vaulted_shopper_id = parsed_response['content-location-header'].split('/').last vaulted_shopper_id += "|#{payment_method_details.payment_method_type}" if payment_method_details.alt_transaction? vaulted_shopper_id end