diff --git a/lib/pagseguro.rb b/lib/pagseguro.rb index 20a0368..e47870e 100644 --- a/lib/pagseguro.rb +++ b/lib/pagseguro.rb @@ -10,6 +10,8 @@ require "pagseguro/railtie" require "pagseguro/notification" require "pagseguro/order" +require "pagseguro/api_order" +require "pagseguro/api_payment" require "pagseguro/action_controller" require "pagseguro/helper" require "pagseguro/utils" diff --git a/lib/pagseguro/api_order.rb b/lib/pagseguro/api_order.rb new file mode 100644 index 0000000..41c01c7 --- /dev/null +++ b/lib/pagseguro/api_order.rb @@ -0,0 +1,96 @@ +module PagSeguro + class ApiOrder + # Map all billing attributes that will be added as form inputs. + BILLING_MAPPING = { + :name => "senderName", + :email => "senderEmail", + :phone_area_code => "senderAreaCode", + :phone_number => "senderPhone", + :address_zipcode => "shippingAddressPostalCode", + :address_street => "shippingAddressStreet", + :address_number => "shippingAddressNumber", + :address_complement => "shippingAddressComplement", + :address_neighbourhood => "shippingAddressDistrict", + :address_city => "shippingAddressCity", + :address_state => "shippingAddressState", + :address_country => "shippingAddressCountry", + } + + # The list of products added to the order + attr_accessor :products + + # The billing info that will be sent to PagSeguro. + attr_accessor :billing + + # Optional: define the shipping type. Can be 1 (PAC) or 2 (Sedex) + attr_accessor :shipping_type + + # Optional: extra amount on the purchase (negative for discount + attr_accessor :extra_amount + + # Optional: specific redirect URL + attr_accessor :redirect_url + + # Optional: order id in your system + attr_accessor :reference + + # Optional: maximum number of uses of generated code, integer greater than 0 + attr_accessor :max_uses + + # Optional: maximum age of generated code in seconds, integer greater than 30 + attr_accessor :max_age + + def initialize(order_id = nil) + reset! + self.id = order_id + self.billing = {} + end + + # Set the order identifier. Should be a unique + # value to identify this order on your own application + def id=(identifier) + @id = identifier + end + + # Get the order identifier + def id + @id + end + + # Remove all products from this order + def reset! + @products = [] + end + + # Add a new product to the PagSeguro order + # The allowed values are: + # - weight (Optional. If float, will be multiplied by 1000g) + # - quantity (Optional. Defaults to 1) + # - price (Required, can be float) + # - description (Required. Identifies the product) + # - id (Required. Should match the product on your database) + # - shipping_cost(Optional. Will be used if provided instead + # of calculated by Correio) + def <<(options) + options = { + :weight => nil, + :quantity => 1 + }.merge(options) + + # convert weight to grammes + options[:weight] = convert_unit(options[:weight], 1000) + + products.push(options) + end + + def add(options) + self << options + end + + private + def convert_unit(number, unit) + number = (BigDecimal("#{number}") * unit).to_i unless number.nil? || number.kind_of?(Integer) + number + end + end +end \ No newline at end of file diff --git a/lib/pagseguro/api_payment.rb b/lib/pagseguro/api_payment.rb new file mode 100644 index 0000000..d000ee3 --- /dev/null +++ b/lib/pagseguro/api_payment.rb @@ -0,0 +1,113 @@ +# encoding: utf-8 +module PagSeguro + class ApiPayment + + API_URL = "https://ws.pagseguro.uol.com.br/v2/checkout/" + + # the pure response from the HTTP request + attr_accessor :response + + # The complete response hash + attr_accessor :payment + + # Errors hash if any + attr_accessor :errors + + # Payment code attributed by PagSeguro + attr_accessor :code + + # The redirect_url if successful + attr_accessor :redirect_url + + # Normalize the specified hash converting all data to UTF-8. + # + def normalize(hash) + each_value(hash) do |value| + Utils.to_utf8(value) + end + end + + # Denormalize the specified hash converting all data to ISO-8859-1. + # + def denormalize(hash) + each_value(hash) do |value| + Utils.to_iso8859(value) + end + end + + # Send the ApiOrder information and get redirect url + def initialize(api_order) + # include the params to validate our request + request_params = { + :encoding => "UTF-8", + :email => PagSeguro.config["email"], + :token => PagSeguro.config["authenticity_token"], + :currency => "BRL" + } + + api_order.products.each_with_index do |product, i| + i += 1 + request_params.merge!({ + "itemQuantity#{i}".to_sym => product[:quantity], + "itemId#{i}".to_sym => product[:id], + "itemDescription#{i}".to_sym => product[:description], + "itemAmount#{i}".to_sym => product[:price] + }) + request_params.merge!({ + "itemWeight#{i}".to_sym => product[:weight].to_i + }) if product[:weight] + end + + api_order.billing.each do |name, value| + request_params.merge!({ + PagSeguro::ApiOrder::BILLING_MAPPING[name.to_sym].to_sym => value + }) + end + + # add optional values if available + request_params.merge!({:reference => api_order.id}) if api_order.id + request_params.merge!({:shippingType => api_order.shipping_type}) if api_order.shipping_type + request_params.merge!({:extraAmount => api_order.extra_amount}) if api_order.extra_amount + request_params.merge!({:redirectURL => api_order.redirect_url}) if api_order.redirect_url + request_params.merge!({:maxUses => api_order.max_uses}) if api_order.max_uses + request_params.merge!({:maxAge => api_order.max_age}) if api_order.max_age + + # do the request + uri = URI.parse(API_URL) + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + http.verify_mode = OpenSSL::SSL::VERIFY_PEER + http.ca_file = File.dirname(__FILE__) + "/cacert.pem" + + request = Net::HTTP::Post.new(uri.path) + request.form_data = denormalize(request_params) + response = http.start {|r| r.request request } + + # saves the response + @response = response + + # saves the payment in hash format + @payment = Hash.from_xml(@response.body) + + # get errors if any + @errors = @payment.try(:[], 'errors') + + # saves the redirect_url + @code = @payment.try(:[], 'checkout').try(:[], 'code') + @redirect_url = @code ? "https://pagseguro.uol.com.br/v2/checkout/payment.html?code=#{@code}" : nil + end + + private + def each_value(hash, &blk) # :nodoc: + hash.each do |key, value| + if value.kind_of?(Hash) + hash[key] = each_value(value, &blk) + else + hash[key] = blk.call value + end + end + + hash + end + end +end diff --git a/lib/pagseguro/order.rb b/lib/pagseguro/order.rb index d71efb2..dc80d6e 100644 --- a/lib/pagseguro/order.rb +++ b/lib/pagseguro/order.rb @@ -16,6 +16,9 @@ class Order :email => "cliente_email" } + # Optional: extra amount on the purchase (negative for discount + attr_accessor :extra_amount + # The list of products added to the order attr_accessor :products @@ -87,7 +90,7 @@ def add(options) private def convert_unit(number, unit) number = (BigDecimal("#{number}") * unit).to_i unless number.nil? || number.kind_of?(Integer) - number + "%03d" % number.to_i end end end diff --git a/lib/pagseguro/utils.rb b/lib/pagseguro/utils.rb index 6070cb9..1258509 100644 --- a/lib/pagseguro/utils.rb +++ b/lib/pagseguro/utils.rb @@ -9,5 +9,9 @@ def to_utf8(string) def to_iso8859(string) string.to_s.unpack("U*").pack("C*") end + + def to_payment_url(string) + string ? "https://pagseguro.uol.com.br/v2/checkout/payment.html?code=#{string}" : nil + end end end