lib/active_merchant/billing/gateways/wirecard.rb in jelaniharris-activemerchant-1.24.1 vs lib/active_merchant/billing/gateways/wirecard.rb in jelaniharris-activemerchant-1.29.1

- old
+ new

@@ -2,37 +2,36 @@ module ActiveMerchant #:nodoc: module Billing #:nodoc: class WirecardGateway < Gateway # Test server location - TEST_URL = 'https://c3-test.wirecard.com/secure/ssl-gateway' - + self.test_url = 'https://c3-test.wirecard.com/secure/ssl-gateway' + # Live server location - LIVE_URL = 'https://c3.wirecard.com/secure/ssl-gateway' + self.live_url = 'https://c3.wirecard.com/secure/ssl-gateway' # The Namespaces are not really needed, because it just tells the System, that there's actually no namespace used. # It's just specified here for completeness. ENVELOPE_NAMESPACES = { 'xmlns:xsi' => 'http://www.w3.org/1999/XMLSchema-instance', - 'xsi:noNamespaceSchemaLocation' => 'wirecard.xsd' - } + 'xsi:noNamespaceSchemaLocation' => 'wirecard.xsd' + } - PERMITTED_TRANSACTIONS = %w[ AUTHORIZATION CAPTURE_AUTHORIZATION PURCHASE ] + PERMITTED_TRANSACTIONS = %w[ AUTHORIZATION CAPTURE_AUTHORIZATION PURCHASE ] RETURN_CODES = %w[ ACK NOK ] - # Wirecard only allows phone numbers with a format like this: +xxx(yyy)zzz-zzzz-ppp, where: - # xxx = Country code - # yyy = Area or city code - # zzz-zzzz = Local number - # ppp = PBX extension - # For example, a typical U.S. or Canadian number would be "+1(202)555-1234-739" indicating PBX extension 739 at phone + # Wirecard only allows phone numbers with a format like this: +xxx(yyy)zzz-zzzz-ppp, where: + # xxx = Country code + # yyy = Area or city code + # zzz-zzzz = Local number + # ppp = PBX extension + # For example, a typical U.S. or Canadian number would be "+1(202)555-1234-739" indicating PBX extension 739 at phone # number 5551234 within area code 202 (country code 1). VALID_PHONE_FORMAT = /\+\d{1,3}(\(?\d{3}\)?)?\d{3}-\d{4}-\d{3}/ - + # The countries the gateway supports merchants from as 2 digit ISO country codes - # TODO: Check supported countries self.supported_countries = ['DE'] # Wirecard supports all major credit and debit cards: # Visa, Mastercard, American Express, Diners Club, # JCB, Switch, VISA Carte Bancaire, Visa Electron and UATP cards. @@ -56,50 +55,37 @@ def initialize(options = {}) # verify that username and password are supplied requires!(options, :login, :password) # unfortunately Wirecard also requires a BusinessCaseSignature in the XML request requires!(options, :signature) - @options = options super end - # Should run against the test servers or not? - def test? - @options[:test] || super - end - # Authorization def authorize(money, creditcard, options = {}) - prepare_options_hash(options) - @options[:credit_card] = creditcard - request = build_request(:authorization, money, @options) - commit(request) + options[:credit_card] = creditcard + commit(:authorization, money, options) end - # Capture Authorization def capture(money, authorization, options = {}) - prepare_options_hash(options) - @options[:authorization] = authorization - request = build_request(:capture_authorization, money, @options) - commit(request) + options[:authorization] = authorization + commit(:capture_authorization, money, options) end - # Purchase def purchase(money, creditcard, options = {}) - prepare_options_hash(options) - @options[:credit_card] = creditcard - request = build_request(:purchase, money, @options) - commit(request) + options[:credit_card] = creditcard + commit(:purchase, money, options) end - private + private def prepare_options_hash(options) - @options.update(options) - setup_address_hash!(options) + result = @options.merge(options) + setup_address_hash!(result) + result end # Create all address hash key value pairs so that # it still works if only provided with one or two of them def setup_address_hash!(options) @@ -109,125 +95,129 @@ options[:billing_address][:email] = options[:email] if options[:email] end # Contact WireCard, make the XML request, and parse the # reply into a Response object - def commit(request) - headers = { 'Content-Type' => 'text/xml', - 'Authorization' => encoded_credentials } + def commit(action, money, options) + request = build_request(action, money, options) - response = parse(ssl_post(test? ? TEST_URL : LIVE_URL, request, headers)) + headers = { 'Content-Type' => 'text/xml', + 'Authorization' => encoded_credentials } + + response = parse(ssl_post(test? ? self.test_url : self.live_url, request, headers)) # Pending Status also means Acknowledged (as stated in their specification) - success = response[:FunctionResult] == "ACK" || response[:FunctionResult] == "PENDING" - message = response[:Message] - authorization = (success && @options[:action] == :authorization) ? response[:GuWID] : nil + success = response[:FunctionResult] == "ACK" || response[:FunctionResult] == "PENDING" + message = response[:Message] + authorization = response[:GuWID] Response.new(success, message, response, :test => test?, :authorization => authorization, :avs_result => { :code => response[:avsCode] }, :cvv_result => response[:cvCode] ) + rescue ResponseError => e + if e.response.code == "401" + return Response.new(false, "Invalid Login") + else + raise + end end # Generates the complete xml-message, that gets sent to the gateway - def build_request(action, money, options = {}) - xml = Builder::XmlMarkup.new :indent => 2 - xml.instruct! - xml.tag! 'WIRECARD_BXML' do - xml.tag! 'W_REQUEST' do + def build_request(action, money, options) + options = prepare_options_hash(options) + options[:action] = action + xml = Builder::XmlMarkup.new :indent => 2 + xml.instruct! + xml.tag! 'WIRECARD_BXML' do + xml.tag! 'W_REQUEST' do xml.tag! 'W_JOB' do - # TODO: OPTIONAL, check what value needs to be insert here - xml.tag! 'JobID', 'test dummy data' + xml.tag! 'JobID', '' # UserID for this transaction xml.tag! 'BusinessCaseSignature', options[:signature] || options[:login] # Create the whole rest of the message - add_transaction_data(xml, action, money, options) - end - end - end - xml.target! + add_transaction_data(xml, money, options) + end + end + end + xml.target! end # Includes the whole transaction data (payment, creditcard, address) - def add_transaction_data(xml, action, money, options = {}) - options[:action] = action - # TODO: require order_id instead of auto-generating it if not supplied + def add_transaction_data(xml, money, options) options[:order_id] ||= generate_unique_id - transaction_type = action.to_s.upcase - xml.tag! "FNC_CC_#{transaction_type}" do - # TODO: OPTIONAL, check which param should be used here - xml.tag! 'FunctionID', options[:description] || 'Test dummy FunctionID' - + xml.tag! "FNC_CC_#{options[:action].to_s.upcase}" do + xml.tag! 'FunctionID', options[:description] xml.tag! 'CC_TRANSACTION' do xml.tag! 'TransactionID', options[:order_id] - if [:authorization, :purchase].include?(action) + case options[:action] + when :authorization, :purchase add_invoice(xml, money, options) add_creditcard(xml, options[:credit_card]) add_address(xml, options[:billing_address]) - elsif action == :capture_authorization - xml.tag! 'GuWID', options[:authorization] if options[:authorization] + when :capture_authorization + xml.tag! 'GuWID', options[:authorization] end end end end - # Includes the payment (amount, currency, country) to the transaction-xml + # Includes the payment (amount, currency, country) to the transaction-xml def add_invoice(xml, money, options) xml.tag! 'Amount', amount(money) xml.tag! 'Currency', options[:currency] || currency(money) xml.tag! 'CountryCode', options[:billing_address][:country] xml.tag! 'RECURRING_TRANSACTION' do xml.tag! 'Type', options[:recurring] || 'Single' end end - # Includes the credit-card data to the transaction-xml - def add_creditcard(xml, creditcard) + # Includes the credit-card data to the transaction-xml + def add_creditcard(xml, creditcard) raise "Creditcard must be supplied!" if creditcard.nil? xml.tag! 'CREDIT_CARD_DATA' do xml.tag! 'CreditCardNumber', creditcard.number xml.tag! 'CVC2', creditcard.verification_value xml.tag! 'ExpirationYear', creditcard.year xml.tag! 'ExpirationMonth', format(creditcard.month, :two_digits) xml.tag! 'CardHolderName', [creditcard.first_name, creditcard.last_name].join(' ') end end - # Includes the IP address of the customer to the transaction-xml + # Includes the IP address of the customer to the transaction-xml def add_customer_data(xml, options) return unless options[:ip] - xml.tag! 'CONTACT_DATA' do - xml.tag! 'IPAddress', options[:ip] - end - end + xml.tag! 'CONTACT_DATA' do + xml.tag! 'IPAddress', options[:ip] + end + end # Includes the address to the transaction-xml def add_address(xml, address) return if address.nil? xml.tag! 'CORPTRUSTCENTER_DATA' do - xml.tag! 'ADDRESS' do - xml.tag! 'Address1', address[:address1] - xml.tag! 'Address2', address[:address2] if address[:address2] - xml.tag! 'City', address[:city] - xml.tag! 'ZipCode', address[:zip] - - if address[:state] =~ /[A-Za-z]{2}/ && address[:country] =~ /^(us|ca)$/i - xml.tag! 'State', address[:state].upcase - end - - xml.tag! 'Country', address[:country] + xml.tag! 'ADDRESS' do + xml.tag! 'Address1', address[:address1] + xml.tag! 'Address2', address[:address2] if address[:address2] + xml.tag! 'City', address[:city] + xml.tag! 'ZipCode', address[:zip] + + if address[:state] =~ /[A-Za-z]{2}/ && address[:country] =~ /^(us|ca)$/i + xml.tag! 'State', address[:state].upcase + end + + xml.tag! 'Country', address[:country] xml.tag! 'Phone', address[:phone] if address[:phone] =~ VALID_PHONE_FORMAT - xml.tag! 'Email', address[:email] - end - end + xml.tag! 'Email', address[:email] + end + end end - # Read the XML message from the gateway and check if it was successful, - # and also extract required return values from the response. + # and also extract required return values from the response. def parse(xml) basepath = '/WIRECARD_BXML/W_RESPONSE' response = {} xml = REXML::Document.new(xml) @@ -309,10 +299,9 @@ # (for http basic authentication) def encoded_credentials credentials = [@options[:login], @options[:password]].join(':') "Basic " << Base64.encode64(credentials).strip end - end end end