module ActiveMerchant #:nodoc: module Billing #:nodoc: class ConektaGateway < Gateway self.live_url = 'https://api.conekta.io/' self.supported_countries = ['MX'] self.supported_cardtypes = [:visa, :master] self.homepage_url = 'https://conekta.io/' self.display_name = 'Conekta Gateway' self.money_format = :cents self.default_currency = 'MXN' def initialize(options = {}) requires!(options, :key) options[:version] ||= '0.2.0' super end def purchase(money, payment_source, options = {}) post = {} add_order(post, money, options) add_payment_source(post, payment_source, options) add_details_data(post, options) commit(:post, 'charges', post) end def authorize(money, payment_source, options = {}) post = {} add_order(post, money, options) add_payment_source(post, payment_source, options) add_details_data(post, options) post[:capture] = false commit(:post, "charges", post) end def capture(money, identifier, options = {}) post = {} post[:order_id] = identifier add_order(post, money, options) commit(:post, "charges/#{identifier}/capture", post) end def refund(money, identifier, options) post = {} post[:order_id] = identifier add_order(post, money, options) commit(:post, "charges/#{identifier}/refund", post) end def store(creditcard, options = {}) post = {} add_payment_source(post, creditcard, options) post[:name] = options[:name] post[:email] = options[:email] path = if options[:customer] "customers/#{CGI.escape(options[:customer])}" else 'customers' end commit(:post, path, post) end def unstore(customer_id, options = {}) commit(:delete, "customers/#{CGI.escape(customer_id)}", nil) end private def add_order(post, money, options) post[:description] = options[:description] || "Active Merchant Purchase" post[:reference_id] = options[:order_id] post[:currency] = (options[:currency] || currency(money)).downcase post[:amount] = amount(money) end def add_details_data(post, options) details = {} details[:name] = options[:customer] details[:email] = options[:email] details[:phone] = options[:phone] details[:device_fingerprint] = options[:device_fingerprint] details[:ip] = options[:ip] add_billing_address(details, options) add_line_items(details, options) add_shipment(details, options) post[:details] = details end def add_shipment(post, options) shipment = {} shipment[:carrier] = options[:carrier] shipment[:service] = options[:service] shipment[:tracking_number] = options[:tracking_number] shipment[:price] = options[:price] add_shipment_address(shipment, options) post[:shipment] = shipment end def add_shipment_address(post, options) address = {} address[:street1] = options[:address1] address[:street2] = options[:address2] address[:street3] = options[:address3] address[:city] = options[:city] address[:state] = options[:state] address[:country] = options[:country] address[:zip] = options[:zip] post[:address] = address end def add_line_items(post, options) post[:line_items] = (options[:line_items] || []).collect do |line_item| line_item end end def add_billing_address(post, options) address = {} address[:street1] = options[:address1] address[:street2] = options[:address2] address[:street3] = options[:address3] address[:city] = options[:city] address[:state] = options[:state] address[:country] = options[:country] address[:zip] = options[:zip] address[:company_name] = options[:company_name] address[:tax_id] = options[:tax_id] address[:name] = options[:name] address[:phone] = options[:phone] address[:email] = options[:email] post[:billing_address] = address end def add_address(post, options) address = {} address[:street1] = options[:address1] address[:street2] = options[:address2] address[:street3] = options[:address3] address[:city] = options[:city] address[:state] = options[:state] address[:country] = options[:country] address[:zip] = options[:zip] post[:address] = address end def add_payment_source(post, payment_source, options) if payment_source.kind_of?(String) post[:card] = payment_source elsif payment_source.respond_to?(:number) card = {} card[:name] = payment_source.name card[:cvc] = payment_source.verification_value card[:number] = payment_source.number card[:exp_month] = "#{sprintf("%02d", payment_source.month)}" card[:exp_year] = "#{"#{payment_source.year}"[-2, 2]}" post[:card] = card add_address(post[:card], options) end end def parse(body) return {} unless body JSON.parse(body) end def headers(meta) @@ua ||= JSON.dump({ :bindings_version => ActiveMerchant::VERSION, :lang => 'ruby', :lang_version => "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})", :platform => RUBY_PLATFORM, :publisher => 'active_merchant' }) { "Accept" => "application/vnd.conekta-v#{options[:version]}+json", "Authorization" => "Basic " + Base64.encode64("#{options[:key]}:"), "RaiseHtmlError" => "false", "User-Agent" => "Conekta ActiveMerchantBindings/#{ActiveMerchant::VERSION}", "X-Conekta-Client-User-Agent" => @@ua, "X-Conekta-Client-User-Metadata" => meta.to_json } end def commit(method, url, parameters, options = {}) success = false begin raw_response = parse(ssl_request(method, live_url + url, (parameters ? parameters.to_query : nil), headers(options[:meta]))) success = (raw_response.key?("object") && (raw_response["object"] != "error")) rescue ResponseError => e raw_response = response_error(e.response.body) rescue JSON::ParserError raw_response = json_error(raw_response) end Response.new( success, raw_response["message"], raw_response, :test => test?, :authorization => raw_response["id"] ) end def response_error(raw_response) begin parse(raw_response) rescue JSON::ParserError json_error(raw_response) end end def json_error(raw_response) msg = 'Invalid response received from the Conekta API.' msg += " (The raw response returned by the API was #{raw_response.inspect})" { "message" => msg } end end end end