require 'rexml/document' require 'digest/md5' require 'active_merchant/billing/gateways/quickpay/quickpay_common' module ActiveMerchant #:nodoc: module Billing #:nodoc: class QuickpayV4to7Gateway < Gateway include QuickpayCommon self.live_url = self.test_url = 'https://secure.quickpay.dk/api' APPROVED = '000' # The login is the QuickpayId # The password is the md5checkword from the Quickpay manager # To use the API-key from the Quickpay manager, specify :api-key # Using the API-key, requires that you use version 4+. Specify :version => 4/5/6/7 in options. def initialize(options = {}) requires!(options, :login, :password) @protocol = options.delete(:version) || 7 # default to protocol version 7 super end def authorize(money, credit_card_or_reference, options = {}) post = {} action = recurring_or_authorize(credit_card_or_reference) add_amount(post, money, options) add_invoice(post, options) add_creditcard_or_reference(post, credit_card_or_reference, options) add_autocapture(post, false) add_fraud_parameters(post, options) if action.eql?(:authorize) add_testmode(post) commit(action, post) end def purchase(money, credit_card_or_reference, options = {}) post = {} action = recurring_or_authorize(credit_card_or_reference) add_amount(post, money, options) add_creditcard_or_reference(post, credit_card_or_reference, options) add_invoice(post, options) add_fraud_parameters(post, options) if action.eql?(:authorize) add_autocapture(post, true) commit(action, post) end def capture(money, authorization, options = {}) post = {} add_finalize(post, options) add_reference(post, authorization) add_amount_without_currency(post, money) commit(:capture, post) end def void(identification, options = {}) post = {} add_reference(post, identification) commit(:cancel, post) end def refund(money, identification, options = {}) post = {} add_amount_without_currency(post, money) add_reference(post, identification) commit(:refund, post) end def credit(money, identification, options = {}) ActiveMerchant.deprecated CREDIT_DEPRECATION_MESSAGE refund(money, identification, options) end def store(creditcard, options = {}) post = {} add_creditcard(post, creditcard, options) add_amount(post, 0, options) if @protocol >= 7 add_invoice(post, options) add_description(post, options) add_fraud_parameters(post, options) add_testmode(post) commit(:subscribe, post) end private def add_amount(post, money, options = {}) post[:amount] = amount(money) post[:currency] = options[:currency] || currency(money) end def add_amount_without_currency(post, money, options = {}) post[:amount] = amount(money) end def add_invoice(post, options) post[:ordernumber] = format_order_number(options[:order_id]) end def add_creditcard(post, credit_card, options) post[:cardnumber] = credit_card.number post[:cvd] = credit_card.verification_value post[:expirationdate] = expdate(credit_card) post[:cardtypelock] = options[:cardtypelock] unless options[:cardtypelock].blank? post[:acquirers] = options[:acquirers] unless options[:acquirers].blank? end def add_reference(post, identification) post[:transaction] = identification end def add_creditcard_or_reference(post, credit_card_or_reference, options) if credit_card_or_reference.is_a?(String) add_reference(post, credit_card_or_reference) else add_creditcard(post, credit_card_or_reference, options) end end def add_autocapture(post, autocapture) post[:autocapture] = autocapture ? 1 : 0 end def recurring_or_authorize(credit_card_or_reference) credit_card_or_reference.is_a?(String) ? :recurring : :authorize end def add_description(post, options) post[:description] = options[:description] || 'Description' end def add_testmode(post) return if post[:transaction].present? post[:testmode] = test? ? '1' : '0' end def add_fraud_parameters(post, options) if @protocol >= 4 post[:fraud_remote_addr] = options[:ip] if options[:ip] post[:fraud_http_accept] = options[:fraud_http_accept] if options[:fraud_http_accept] post[:fraud_http_accept_language] = options[:fraud_http_accept_language] if options[:fraud_http_accept_language] post[:fraud_http_accept_encoding] = options[:fraud_http_accept_encoding] if options[:fraud_http_accept_encoding] post[:fraud_http_accept_charset] = options[:fraud_http_accept_charset] if options[:fraud_http_accept_charset] post[:fraud_http_referer] = options[:fraud_http_referer] if options[:fraud_http_referer] post[:fraud_http_user_agent] = options[:fraud_http_user_agent] if options[:fraud_http_user_agent] end end def add_finalize(post, options) post[:finalize] = options[:finalize] ? '1' : '0' end def commit(action, params) response = parse(ssl_post(self.live_url, post_data(action, params))) Response.new(successful?(response), message_from(response), response, test: test?, authorization: response[:transaction] ) end def successful?(response) response[:qpstat] == APPROVED end def parse(data) response = {} doc = REXML::Document.new(data) doc.root.elements.each do |element| response[element.name.to_sym] = element.text end response end def message_from(response) response[:qpstatmsg].to_s end def post_data(action, params = {}) params[:protocol] = @protocol params[:msgtype] = action.to_s params[:merchant] = @options[:login] params[:apikey] = @options[:apikey] if @options[:apikey] params[:md5check] = generate_check_hash(action, params) params.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join('&') end def generate_check_hash(action, params) string = MD5_CHECK_FIELDS[@protocol][action].collect do |key| params[key.to_sym] end.join('') # Add the md5checkword string << @options[:password].to_s Digest::MD5.hexdigest(string) end def expdate(credit_card) year = format(credit_card.year, :two_digits) month = format(credit_card.month, :two_digits) "#{year}#{month}" end # Limited to 20 digits max def format_order_number(number) number.to_s.gsub(/[^\w]/, '').rjust(4, '0')[0...20] end end end end