module ActiveMerchant #:nodoc: module Billing #:nodoc: class PagarmeGateway < Gateway self.live_url = 'https://api.pagar.me/1/' self.supported_countries = ['BR'] self.default_currency = 'BRL' self.money_format = :cents self.supported_cardtypes = %i[visa master american_express discover diners_club] self.homepage_url = 'https://pagar.me/' self.display_name = 'Pagar.me' STANDARD_ERROR_CODE_MAPPING = { 'refused' => STANDARD_ERROR_CODE[:card_declined], 'processing_error' => STANDARD_ERROR_CODE[:processing_error], } def initialize(options={}) requires!(options, :api_key) @api_key = options[:api_key] super end def purchase(money, payment_method, options={}) post = {} add_amount(post, money) add_payment_method(post, payment_method) add_metadata(post, options) commit(:post, 'transactions', post) end def authorize(money, payment_method, options={}) post = {} add_amount(post, money) add_payment_method(post, payment_method) add_metadata(post, options) post[:capture] = false commit(:post, 'transactions', post) end def capture(money, authorization, options={}) return Response.new(false, 'Não é possível capturar uma transação sem uma prévia autorização.') if authorization.nil? post = {} commit(:post, "transactions/#{authorization}/capture", post) end def refund(money, authorization, options={}) return Response.new(false, 'Não é possível estornar uma transação sem uma prévia captura.') if authorization.nil? void(authorization, options) end def void(authorization, options={}) return Response.new(false, 'Não é possível estornar uma transação autorizada sem uma prévia autorização.') if authorization.nil? post = {} commit(:post, "transactions/#{authorization}/refund", post) end def verify(payment_method, options={}) MultiResponse.run(:use_first_response) do |r| r.process { authorize(127, payment_method, options) } r.process(:ignore_result) { void(r.authorization, options) } end end def supports_scrubbing? true end def scrub(transcript) transcript. gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]'). gsub(%r((card_number=)\d+), '\1[FILTERED]'). gsub(%r((card_cvv=)\d+), '\1[FILTERED]') end private def add_amount(post, money) post[:amount] = amount(money) end def add_payment_method(post, payment_method) post[:payment_method] = 'credit_card' add_credit_card(post, payment_method) end def add_credit_card(post, credit_card) post[:card_number] = credit_card.number post[:card_holder_name] = credit_card.name post[:card_expiration_date] = "#{credit_card.month}/#{credit_card.year}" post[:card_cvv] = credit_card.verification_value end def add_metadata(post, options={}) post[:metadata] = {} post[:metadata][:order_id] = options[:order_id] post[:metadata][:ip] = options[:ip] post[:metadata][:customer] = options[:customer] post[:metadata][:invoice] = options[:invoice] post[:metadata][:merchant] = options[:merchant] post[:metadata][:description] = options[:description] post[:metadata][:email] = options[:email] end def parse(body) JSON.parse(body) end def post_data(params) return nil unless params params.map do |key, value| next if value != false && value.blank? if value.is_a?(Hash) h = {} value.each do |k, v| h["#{key}[#{k}]"] = v unless v.blank? end post_data(h) elsif value.is_a?(Array) value.map { |v| "#{key}[]=#{CGI.escape(v.to_s)}" }.join('&') else "#{key}=#{CGI.escape(value.to_s)}" end end.compact.join('&') end def headers(options = {}) { 'Authorization' => 'Basic ' + Base64.encode64(@api_key.to_s + ':x').strip, 'User-Agent' => "Pagar.me/1 ActiveMerchant/#{ActiveMerchant::VERSION}", 'Accept-Encoding' => 'deflate' } end def api_request(method, endpoint, parameters = nil, options = {}) raw_response = response = nil begin raw_response = ssl_request(method, self.live_url + endpoint, post_data(parameters), headers(options)) response = parse(raw_response) rescue ResponseError => e raw_response = e.response.body response = response_error(raw_response) rescue JSON::ParserError response = json_error(raw_response) end response end def commit(method, url, parameters, options = {}) response = api_request(method, url, parameters, options) Response.new( success_from(response), message_from(response), response, authorization: authorization_from(response), test: test?, error_code: error_code_from(response) ) end def response_error(raw_response) parse(raw_response) rescue JSON::ParserError json_error(raw_response) end def json_error(raw_response) msg = 'Resposta inválida retornada pela API do Pagar.me. Por favor entre em contato com suporte@pagar.me se você continuar recebendo essa mensagem.' msg += " (A resposta retornada pela API foi #{raw_response.inspect})" { 'errors' => [{ 'message' => msg }] } end def success_from(response) success_purchase = response.key?('status') && response['status'] == 'paid' success_authorize = response.key?('status') && response['status'] == 'authorized' success_refund = response.key?('status') && response['status'] == 'refunded' success_purchase || success_authorize || success_refund end def failure_from(response) response.key?('status') && response['status'] == 'refused' end def message_from(response) if success_from(response) case response['status'] when 'paid' 'Transação aprovada' when 'authorized' 'Transação autorizada' when 'refunded' 'Transação estornada' else "Transação com status '#{response['status']}'" end elsif failure_from(response) 'Transação recusada' elsif response.key?('errors') response['errors'][0]['message'] else msg = json_error(response) msg['errors'][0]['message'] end end def authorization_from(response) response['id'] if success_from(response) end def test? @api_key.start_with?('ak_test') end def error_code_from(response) if failure_from(response) STANDARD_ERROR_CODE_MAPPING['refused'] elsif response.key?('errors') STANDARD_ERROR_CODE_MAPPING['processing_error'] end end end end end