module ActiveMerchant #:nodoc: module Billing #:nodoc: # # NETPAY Gateway # # Support for NETPAY's HTTP Connector payment gateway in Mexico. # # The gateway sends requests as HTTP POST and receives the response details # in the HTTP header, making the process really rather simple. # # Support for calls to the authorize and capture methods have been included # as per the Netpay manuals, however, your millage may vary with these # methods. At the time of writing (January 2013) they were not fully # supported by the production or test gateways. This situation is # expected to change within a few weeks/months. # # Purchases can be cancelled (`#void`) only within 24 hours of the # transaction. After this, a refund should be performed instead. # # In addition to the regular ActiveMerchant transaction options, NETPAY # also supports a `:mode` parameter. This allows testing to be performed # in production and force specific results. # # * 'P' - Production # * 'A' - Approved # * 'D' - Declined # * 'R' - Random (Approved or Declined) # * 'T' - Test # # For example: # # response = @gateway.purchase(1000, card, :mode => 'D') # response.success # false # class NetpayGateway < Gateway self.test_url = 'http://200.57.87.243:8855' self.live_url = 'https://suite.netpay.com.mx/acquirerprd' # The countries the gateway supports merchants from as 2 digit ISO country codes self.supported_countries = ['MX'] self.default_currency = 'MXN' # The card types supported by the payment gateway self.supported_cardtypes = [:visa, :master, :american_express, :diners_club] # The homepage URL of the gateway self.homepage_url = 'http://www.netpay.com.mx' # The name of the gateway self.display_name = 'NETPAY Gateway' CURRENCY_CODES = { 'MXN' => '484' } # The header keys that we will provide in the response params hash RESPONSE_KEYS = ['ResponseMsg', 'ResponseText', 'ResponseCode', 'TimeIn', 'TimeOut', 'AuthCode', 'OrderId', 'CardTypeName', 'MerchantId', 'IssuerAuthDate'] def initialize(options = {}) requires!(options, :store_id, :login, :password) super end # Send an authorization request def authorize(money, creditcard, options = {}) post = {} add_invoice(post, options) add_creditcard(post, creditcard) add_customer_data(post, options) add_amount(post, money, options) commit('PreAuth', post, options) end # Capture an authorization def capture(money, authorization, options = {}) post = {} add_order_id(post, order_id_from(authorization)) add_amount(post, money, options) commit('PostAuth', post, options) end # Cancel an auth/purchase within first 24 hours def void(authorization, options = {}) post = {} order_id, amount, currency = split_authorization(authorization) add_order_id(post, order_id) post['Total'] = (options[:amount] || amount) post['CurrencyCode'] = currency commit('Refund', post, options) end # Make a purchase. def purchase(money, creditcard, options = {}) post = {} add_invoice(post, options) add_creditcard(post, creditcard) add_customer_data(post, options) add_amount(post, money, options) commit('Auth', post, options) end # Perform a Credit transaction. def refund(money, authorization, options = {}) post = {} add_order_id(post, order_id_from(authorization)) add_amount(post, money, options) commit('Credit', post, options) end private def add_login_data(post) post['StoreId'] = @options[:store_id] post['UserName'] = @options[:login] post['Password'] = @options[:password] end def add_action(post, action, options) post['ResourceName'] = action post['ContentType'] = 'Transaction' post['Mode'] = options[:mode] || 'P' end def add_order_id(post, order_id) post['OrderId'] = order_id end def add_amount(post, money, options) post['Total'] = amount(money) post['CurrencyCode'] = currency_code(options[:currency] || currency(money)) end def add_customer_data(post, options) post['IPAddress'] = options[:ip] unless options[:ip].blank? end def add_invoice(post, options) post['Comments'] = options[:description] if options[:description] end def add_creditcard(post, creditcard) post['CardNumber'] = creditcard.number post['ExpDate'] = expdate(creditcard) post['CustomerName'] = creditcard.name post['CVV2'] = creditcard.verification_value unless creditcard.verification_value.nil? end def build_authorization(request_params, response_params) [response_params['OrderId'], request_params['Total'], request_params['CurrencyCode']].join('|') end def split_authorization(authorization) order_id, amount, currency = authorization.split('|') [order_id, amount, currency] end def order_id_from(authorization) split_authorization(authorization).first end def expdate(credit_card) year = sprintf('%.4i', credit_card.year) month = sprintf('%.2i', credit_card.month) "#{month}/#{year[-2..-1]}" end def url test? ? test_url : live_url end def parse(response, request_params) response_params = params_from_response(response) success = (response_params['ResponseCode'] == '00') message = response_params['ResponseText'] || response_params['ResponseMsg'] options = @options.merge(test: test?, authorization: build_authorization(request_params, response_params)) Response.new(success, message, response_params, options) end def commit(action, parameters, options) add_login_data(parameters) add_action(parameters, action, options) post = parameters.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join('&') parse(ssl_post(url, post), parameters) end # Override the regular handle response so we can access the headers def handle_response(response) case response.code.to_i when 200...300 response else raise ResponseError.new(response) end end # Return a hash containing all the useful, or informative values from netpay def params_from_response(response) params = {} RESPONSE_KEYS.each do |k| params[k] = response[k] unless response[k].to_s.empty? end params end def currency_code(currency) return currency if currency =~ /^\d+$/ CURRENCY_CODES[currency] end end end end