├── .rspec ├── lib ├── pagseguro-oficial.rb ├── pagseguro │ ├── version.rb │ ├── exceptions.rb │ ├── extensions │ │ ├── ensure_type.rb │ │ └── mass_assignment.rb │ ├── phone.rb │ ├── transaction │ │ ├── response.rb │ │ └── serializer.rb │ ├── sender.rb │ ├── notification.rb │ ├── items.rb │ ├── item.rb │ ├── payment_request │ │ ├── response.rb │ │ └── serializer.rb │ ├── payment_status.rb │ ├── errors.rb │ ├── payment_method.rb │ ├── address.rb │ ├── shipping.rb │ ├── request.rb │ ├── payment_request.rb │ ├── report.rb │ └── transaction.rb └── pagseguro.rb ├── Gemfile ├── Rakefile ├── .travis.yml ├── spec ├── pagseguro │ ├── phone_spec.rb │ ├── sender_spec.rb │ ├── address_spec.rb │ ├── notification_spec.rb │ ├── item_spec.rb │ ├── payment_request │ │ ├── response_spec.rb │ │ └── serializer_spec.rb │ ├── payment_status_spec.rb │ ├── shipping_spec.rb │ ├── items_spec.rb │ ├── payment_method_spec.rb │ ├── pagseguro_spec.rb │ ├── payment_request_spec.rb │ ├── request_spec.rb │ ├── errors_spec.rb │ ├── transaction │ │ └── serializer_spec.rb │ └── transaction_spec.rb ├── fixtures │ ├── payment_request │ │ ├── success.xml │ │ └── failure.xml │ ├── invalid_code.xml │ ├── transactions │ │ ├── additional.xml │ │ └── success.xml │ └── by_date │ │ └── success.xml ├── support │ ├── mass_assignment_macro.rb │ └── ensure_type_macro.rb └── spec_helper.rb ├── examples ├── transaction_by_code.rb ├── invalid_transaction_by_code.rb ├── boot.rb ├── abandoned_transactions.rb ├── transactions_by_date.rb └── payment_request.rb ├── .gitignore ├── pagseguro-oficial.gemspec ├── locales └── pt-BR.yml ├── README.md └── LICENSE-2.0.txt /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format documentation 3 | -------------------------------------------------------------------------------- /lib/pagseguro-oficial.rb: -------------------------------------------------------------------------------- 1 | require "pagseguro" 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gemspec 3 | -------------------------------------------------------------------------------- /lib/pagseguro/version.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | VERSION = "2.0.2" 3 | end 4 | -------------------------------------------------------------------------------- /lib/pagseguro/exceptions.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | InvalidEnvironmentError = Class.new(StandardError) 3 | end 4 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | task default: "spec" 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.0.0 4 | - 1.9.3 5 | - jruby-19mode 6 | 7 | notifications: 8 | email: 9 | - fnando.vieira@gmail.com 10 | -------------------------------------------------------------------------------- /spec/pagseguro/phone_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe PagSeguro::Phone do 4 | it_assigns_attribute :area_code 5 | it_assigns_attribute :number 6 | end 7 | -------------------------------------------------------------------------------- /examples/transaction_by_code.rb: -------------------------------------------------------------------------------- 1 | require_relative "boot" 2 | 3 | transaction = PagSeguro::Transaction.find_by_code("739D69-79C052C05280-55542D9FBB33-4AB2D0") 4 | 5 | puts transaction 6 | -------------------------------------------------------------------------------- /spec/fixtures/payment_request/success.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8CF4BE7DCECEF0F004A6DFA0A8243412 4 | 2010-12-02T10:11:28.000-02:00 5 | 6 | -------------------------------------------------------------------------------- /spec/fixtures/payment_request/failure.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11024 5 | Items invalid quantity. 6 | 7 | -------------------------------------------------------------------------------- /spec/pagseguro/sender_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe PagSeguro::Sender do 4 | it_assigns_attribute :name 5 | it_assigns_attribute :email 6 | it_assigns_attribute :cpf 7 | it_ensures_type PagSeguro::Phone, :phone 8 | end 9 | 10 | -------------------------------------------------------------------------------- /examples/invalid_transaction_by_code.rb: -------------------------------------------------------------------------------- 1 | require_relative "boot" 2 | 3 | transaction = PagSeguro::Transaction.find_by_code("Invalid") 4 | 5 | if transaction.errors.any? 6 | puts transaction.errors.join("\n") 7 | else 8 | puts transaction 9 | end 10 | -------------------------------------------------------------------------------- /spec/fixtures/invalid_code.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13001 5 | invalid notification code value: 999769-79C052C05280-55542D9FBB33-4AB2D0 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/pagseguro/extensions/ensure_type.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | module Extensions 3 | module EnsureType 4 | def ensure_type(klass, options = {}) 5 | options.kind_of?(klass) ? options : klass.new(options) 6 | end 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | InstalledFiles 7 | _yardoc 8 | coverage 9 | doc/ 10 | lib/bundler/man 11 | pkg 12 | rdoc 13 | spec/reports 14 | test/tmp 15 | test/version_tmp 16 | tmp 17 | Gemfile.lock 18 | .env 19 | *~ 20 | .rvmrc 21 | -------------------------------------------------------------------------------- /examples/boot.rb: -------------------------------------------------------------------------------- 1 | $:.unshift File.expand_path("../../lib", __FILE__) 2 | require "pagseguro" 3 | 4 | I18n.locale = "pt-BR" 5 | 6 | PagSeguro.configure do |config| 7 | config.token = ENV.fetch("PAGSEGURO_TOKEN") 8 | config.email = ENV.fetch("PAGSEGURO_EMAIL") 9 | end 10 | -------------------------------------------------------------------------------- /lib/pagseguro/phone.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | class Phone 3 | include Extensions::MassAssignment 4 | 5 | # Set the area code. 6 | attr_accessor :area_code 7 | 8 | # Set the phone number. 9 | # Must have 7-9 numbers. 10 | attr_accessor :number 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/pagseguro/transaction/response.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | class Transaction 3 | class Response 4 | # Set the response errors. 5 | attr_reader :errors 6 | 7 | def initialize(errors = Errors.new) 8 | @errors = errors 9 | end 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/support/mass_assignment_macro.rb: -------------------------------------------------------------------------------- 1 | module MassAssignmentMacro 2 | def it_assigns_attribute(attr) 3 | it "assigns #{attr} on initialize" do 4 | value = attr.to_s.upcase 5 | object = described_class.new(attr => value) 6 | expect(object.public_send(attr)).to eql(value) 7 | end 8 | end 9 | end 10 | 11 | RSpec.configure {|c| c.extend(MassAssignmentMacro) } 12 | -------------------------------------------------------------------------------- /lib/pagseguro/extensions/mass_assignment.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | module Extensions 3 | module MassAssignment 4 | def initialize(options = {}) 5 | before_initialize if respond_to?(:before_initialize, true) 6 | options.each {|name, value| public_send("#{name}=", value) } 7 | after_initialize if respond_to?(:after_initialize, true) 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/pagseguro/address_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe PagSeguro::Address do 4 | it_assigns_attribute :street 5 | it_assigns_attribute :number 6 | it_assigns_attribute :complement 7 | it_assigns_attribute :district 8 | it_assigns_attribute :city 9 | it_assigns_attribute :state 10 | it_assigns_attribute :country 11 | it_assigns_attribute :postal_code 12 | 13 | it "sets default country" do 14 | address = PagSeguro::Address.new 15 | expect(address.country).to eql("BRA") 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/support/ensure_type_macro.rb: -------------------------------------------------------------------------------- 1 | module EnsureTypeMacro 2 | def it_ensures_type(klass, attr) 3 | it "ensures that #{attr.inspect} coerces hash to #{klass}" do 4 | options = double(:options) 5 | 6 | klass 7 | .should_receive(:new) 8 | .with(options) 9 | .and_return("INSTANCE") 10 | 11 | instance = described_class.new(attr => options) 12 | expect(instance.public_send(attr)).to eql("INSTANCE") 13 | end 14 | end 15 | end 16 | 17 | RSpec.configure {|c| c.extend(EnsureTypeMacro) } 18 | -------------------------------------------------------------------------------- /lib/pagseguro/sender.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | class Sender 3 | include Extensions::MassAssignment 4 | include Extensions::EnsureType 5 | 6 | # Get the sender phone. 7 | attr_reader :phone 8 | 9 | # Set the sender name. 10 | attr_accessor :name 11 | 12 | # Set the sender e-mail. 13 | attr_accessor :email 14 | 15 | # Set the CPF document. 16 | attr_accessor :cpf 17 | 18 | # Set the sender phone. 19 | def phone=(phone) 20 | @phone = ensure_type(Phone, phone) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/pagseguro/notification_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe PagSeguro::Notification do 4 | it_assigns_attribute :code 5 | it_assigns_attribute :type 6 | 7 | it "detects notification as transaction" do 8 | expect(PagSeguro::Notification.new(type: "transaction")).to be_transaction 9 | end 10 | 11 | it "fetches transaction by its code" do 12 | PagSeguro::Transaction 13 | .should_receive(:find_by_code) 14 | .with("CODE") 15 | 16 | PagSeguro::Notification.new(code: "CODE").transaction 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/pagseguro/item_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe PagSeguro::Item do 4 | it_assigns_attribute :id 5 | it_assigns_attribute :description 6 | it_assigns_attribute :amount 7 | it_assigns_attribute :quantity 8 | it_assigns_attribute :weight 9 | it_assigns_attribute :shipping_cost 10 | 11 | it "sets default quantity" do 12 | item = PagSeguro::Item.new 13 | expect(item.quantity).to eql(1) 14 | end 15 | 16 | it "sets default weight" do 17 | item = PagSeguro::Item.new 18 | expect(item.weight).to be_zero 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/pagseguro/notification.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | class Notification 3 | include PagSeguro::Extensions::MassAssignment 4 | 5 | # The notification code sent by PagSeguro. 6 | attr_accessor :code 7 | 8 | # The notification type sent by PagSeguro. 9 | attr_accessor :type 10 | 11 | # Detect if the notification is from a transaction. 12 | def transaction? 13 | type == "transaction" 14 | end 15 | 16 | # Fetch the transaction by its code. 17 | def transaction 18 | Transaction.find_by_code(code) 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/pagseguro/items.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | class Items 3 | extend Forwardable 4 | include Enumerable 5 | include Extensions::EnsureType 6 | 7 | def_delegators :@store, :size, :clear, :empty?, :any?, :each 8 | 9 | def initialize 10 | @store = [] 11 | end 12 | 13 | def <<(item) 14 | item = ensure_type(Item, item) 15 | 16 | if include?(item) 17 | item.quantity += 1 18 | else 19 | @store << item 20 | end 21 | end 22 | 23 | def include?(item) 24 | @store.find {|stored_item| stored_item.id == ensure_type(Item, item).id } 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/pagseguro/item.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | class Item 3 | include Extensions::MassAssignment 4 | 5 | # Set the product identifier, such as SKU. 6 | attr_accessor :id 7 | 8 | # Set the product description. 9 | attr_accessor :description 10 | 11 | # Set the quantity. 12 | # Defaults to 1. 13 | attr_accessor :quantity 14 | 15 | # Set the amount per unit. 16 | attr_accessor :amount 17 | 18 | # Set the weight per unit, in grams. 19 | attr_accessor :weight 20 | 21 | # Set the shipping cost per unit. 22 | attr_accessor :shipping_cost 23 | 24 | private 25 | def before_initialize 26 | self.quantity = 1 27 | self.weight = 0 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require "simplecov" 2 | SimpleCov.start 3 | 4 | require "bundler/setup" 5 | Bundler.require(:default, :development) 6 | 7 | require "test_notifier/runner/rspec" 8 | require "fakeweb" 9 | require "pagseguro" 10 | 11 | FakeWeb.allow_net_connect = false 12 | 13 | Dir["./spec/support/**/*.rb"].each {|file| require file } 14 | 15 | I18n.exception_handler = proc do |scope, *args| 16 | message = scope.to_s 17 | raise message unless message.include?(".i18n.plural.rule") 18 | end 19 | 20 | I18n.default_locale = "pt-BR" 21 | I18n.locale = ENV.fetch("LOCALE", I18n.default_locale) 22 | 23 | RSpec.configure do |config| 24 | config.before(:each) do 25 | load "./lib/pagseguro.rb" 26 | FakeWeb.clean_registry 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /examples/abandoned_transactions.rb: -------------------------------------------------------------------------------- 1 | require_relative "boot" 2 | require "active_support/all" 3 | 4 | report = PagSeguro::Transaction.find_abandoned( 5 | starts_at: 30.days.ago, 6 | size: 1 7 | ) 8 | 9 | while report.next_page? 10 | report.next_page! 11 | puts "=> Fetching page #{report.page}" 12 | 13 | abort "=> Errors: #{report.errors.join("\n")}" unless report.valid? 14 | 15 | puts "=> Report was created at: #{report.created_at}" 16 | puts 17 | 18 | report.transactions.each do |transaction| 19 | puts "=> Abandoned transaction" 20 | puts " created at: #{transaction.created_at}" 21 | puts " code: #{transaction.code}" 22 | puts " type_id: #{transaction.type_id}" 23 | puts " gross amount: #{transaction.gross_amount}" 24 | puts 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/pagseguro/payment_request/response.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | class PaymentRequest 3 | class Response 4 | extend Forwardable 5 | 6 | def_delegators :response, :success? 7 | attr_reader :response 8 | 9 | def initialize(response) 10 | @response = response 11 | end 12 | 13 | def errors 14 | @errors ||= Errors.new(response) 15 | end 16 | 17 | def url 18 | PagSeguro.site_url("checkout/payment.html?code=#{code}") if code 19 | end 20 | 21 | def code 22 | @code ||= response.data.css("checkout > code").text if success? 23 | end 24 | 25 | def created_at 26 | @created_at ||= Time.parse(response.data.css("checkout > date").text) if success? 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/pagseguro/payment_status.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | class PaymentStatus 3 | STATUSES = { 4 | "0" => :initiated, 5 | "1" => :waiting_payment, 6 | "2" => :in_analysis, 7 | "3" => :paid, 8 | "4" => :available, 9 | "5" => :in_dispute, 10 | "6" => :refunded, 11 | "7" => :cancelled, 12 | }.freeze 13 | 14 | # The payment status id. 15 | attr_reader :id 16 | 17 | def initialize(id) 18 | @id = id 19 | end 20 | 21 | # Dynamically define helpers. 22 | STATUSES.each do |id, _status| 23 | define_method "#{_status}?" do 24 | _status == status 25 | end 26 | end 27 | 28 | # Return a readable status. 29 | def status 30 | STATUSES.fetch(id.to_s) { raise "PagSeguro::PaymentStatus#id isn't mapped" } 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/pagseguro/errors.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | class Errors 3 | extend Forwardable 4 | include Enumerable 5 | 6 | def_delegators :@messages, :each, :empty?, :any?, :join, :include? 7 | 8 | def initialize(response = nil) 9 | @response = response 10 | @messages = [] 11 | 12 | process_response if response 13 | end 14 | 15 | private 16 | def process_response 17 | @messages << error_message(:unauthorized, "Unauthorized") if @response.unauthorized? 18 | 19 | @response.data.css("errors > error").each do |error| 20 | @messages << error_message(error.css("code").text, error.css("message").text) 21 | end if @response.bad_request? 22 | end 23 | 24 | def error_message(code, message) 25 | I18n.t(code, scope: "pagseguro.errors", default: message) 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /examples/transactions_by_date.rb: -------------------------------------------------------------------------------- 1 | require_relative "boot" 2 | require "active_support/all" 3 | 4 | report = PagSeguro::Transaction.find_by_date(starts_at: 29.days.ago, per_page: 1) 5 | 6 | while report.next_page? 7 | report.next_page! 8 | puts "== Page #{report.page}" 9 | abort "=> Errors: #{report.errors.join("\n")}" unless report.valid? 10 | puts "Report created on #{report.created_at}" 11 | puts 12 | 13 | report.transactions.each do |transaction| 14 | puts "=> Transaction" 15 | puts " created_at: #{transaction.created_at}" 16 | puts " code: #{transaction.code}" 17 | puts " cancellation_source: #{transaction.cancellation_source}" 18 | puts " payment method: #{transaction.payment_method.type}" 19 | puts " gross amount: #{transaction.gross_amount}" 20 | puts " updated at: #{transaction.updated_at}" 21 | puts 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/pagseguro/payment_request/response_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe PagSeguro::PaymentRequest::Response do 4 | context "when payment request is created" do 5 | def xml_response(path) 6 | response = double( 7 | body: File.read("./spec/fixtures/#{path}"), 8 | code: 200, 9 | content_type: "text/xml", 10 | "[]" => nil 11 | ) 12 | 13 | Aitch::Response.new({xml_parser: Aitch::XMLParser}, response) 14 | end 15 | 16 | let(:http_response) { xml_response("payment_request/success.xml") } 17 | subject(:response) { described_class.new(http_response) } 18 | 19 | it { expect(response.code).to eql("8CF4BE7DCECEF0F004A6DFA0A8243412") } 20 | it { expect(response.created_at).to eql(Time.parse("2010-12-02T10:11:28.000-02:00")) } 21 | it { expect(response.url).to eql("https://pagseguro.uol.com.br/v2/checkout/payment.html?code=8CF4BE7DCECEF0F004A6DFA0A8243412") } 22 | end 23 | end 24 | 25 | -------------------------------------------------------------------------------- /spec/pagseguro/payment_status_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | shared_examples_for "payment status mapping" do |id, status| 4 | it "returns #{status} as status when id is #{id}" do 5 | expect(PagSeguro::PaymentStatus.new(id).status).to eql(status) 6 | end 7 | 8 | it "detects as #{status}" do 9 | expect(PagSeguro::PaymentStatus.new(id).public_send("#{status}?")).to be 10 | end 11 | end 12 | 13 | describe PagSeguro::PaymentStatus do 14 | context "status mapping" do 15 | it_behaves_like "payment status mapping", 0, :initiated 16 | it_behaves_like "payment status mapping", 1, :waiting_payment 17 | it_behaves_like "payment status mapping", 2, :in_analysis 18 | it_behaves_like "payment status mapping", 3, :paid 19 | it_behaves_like "payment status mapping", 4, :available 20 | it_behaves_like "payment status mapping", 5, :in_dispute 21 | it_behaves_like "payment status mapping", 6, :refunded 22 | it_behaves_like "payment status mapping", 7, :cancelled 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/pagseguro/payment_method.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | module PagSeguro 3 | class PaymentMethod 4 | include Extensions::MassAssignment 5 | 6 | TYPES = { 7 | "1" => :credit_card, 8 | "2" => :boleto, 9 | "3" => :online_transfer, 10 | "4" => :balance, 11 | "5" => :oi_paggo, 12 | "7" => :direct_deposit 13 | }.freeze 14 | 15 | # The payment method code. 16 | attr_accessor :code 17 | 18 | # The payment method type. 19 | attr_accessor :type_id 20 | 21 | # Define shortcuts dynamically. 22 | TYPES.each do |id, type| 23 | define_method "#{type}?" do 24 | type_id.to_s == id 25 | end 26 | end 27 | 28 | # Return the type in a readable manner. 29 | def type 30 | TYPES.fetch(type_id.to_s) { raise "PagSeguro::PaymentMethod#type_id isn't mapped" } 31 | end 32 | 33 | # Return the payment method's description. 34 | def description 35 | I18n.t(code, scope: "pagseguro.payment_methods") 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/pagseguro/address.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | # Set the shipping address information. 3 | class Address 4 | include Extensions::MassAssignment 5 | 6 | # Set the street name. 7 | attr_accessor :street 8 | 9 | # Set the house/building number. 10 | attr_accessor :number 11 | 12 | # Set the address complement. 13 | # Can be the apartment, suite number or any other qualifier after 14 | # the street/number pair. 15 | attr_accessor :complement 16 | 17 | # Set the district. 18 | # Can be the district, county or neighborhood, if applicable. 19 | attr_accessor :district 20 | 21 | # Set the city name. 22 | attr_accessor :city 23 | 24 | # Set the state or province. 25 | attr_accessor :state 26 | 27 | # Set the postal code. 28 | # Must contain 8 numbers. 29 | attr_accessor :postal_code 30 | 31 | # Set the country code. 32 | # Defaults to +BRA+. 33 | attr_accessor :country 34 | 35 | private 36 | def before_initialize 37 | self.country = "BRA" 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/pagseguro/shipping_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe PagSeguro::Shipping do 4 | it_assigns_attribute :cost 5 | it_ensures_type PagSeguro::Address, :address 6 | 7 | PagSeguro::Shipping::TYPE.each do |name, id| 8 | it "sets id for name (#{id.inspect} => #{name.inspect})" do 9 | shipping = PagSeguro::Shipping.new(type_name: name) 10 | expect(shipping.type_id).to eql(id) 11 | end 12 | 13 | it "sets name for id (#{name.inspect} => #{id.inspect})" do 14 | shipping = PagSeguro::Shipping.new(type_id: id) 15 | expect(shipping.type_name).to eql(name) 16 | end 17 | end 18 | 19 | it "raises when setting an invalid type id" do 20 | shipping = PagSeguro::Shipping.new 21 | 22 | expect { 23 | shipping.type_id = 1234 24 | }.to raise_error( 25 | PagSeguro::Shipping::InvalidShippingTypeError, 26 | "invalid 1234 type id" 27 | ) 28 | end 29 | 30 | it "raises when setting an invalid type name" do 31 | shipping = PagSeguro::Shipping.new 32 | 33 | expect { 34 | shipping.type_name = :invalid 35 | }.to raise_error( 36 | PagSeguro::Shipping::InvalidShippingTypeError, 37 | "invalid :invalid type name" 38 | ) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /examples/payment_request.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding : utf-8 -*- 2 | require_relative "boot" 3 | 4 | payment = PagSeguro::PaymentRequest.new 5 | payment.abandon_url = "http://dev.simplesideias.com.br/?abandoned" 6 | payment.notification_url = "http://dev.simplesideias.com.br/?notification" 7 | payment.redirect_url = "http://dev.simplesideias.com.br/?redirect" 8 | 9 | payment.items << { 10 | id: 1234, 11 | description: %[Televisão 19" Sony], 12 | amount: 459.50, 13 | weight: 0 14 | } 15 | 16 | payment.reference = "REF1234" 17 | payment.sender = { 18 | name: "Nando Vieira", 19 | email: "fnando.vieira@gmail.com", 20 | cpf: "21639716866", 21 | phone: { 22 | area_code: 11, 23 | number: "12345678" 24 | } 25 | } 26 | 27 | payment.shipping = { 28 | type_name: "sedex", 29 | address: { 30 | street: "R. Vergueiro", 31 | number: 1421, 32 | complement: "Sala 213", 33 | city: "São Paulo", 34 | state: "SP", 35 | district: "Vila Mariana" 36 | } 37 | } 38 | 39 | puts "=> REQUEST" 40 | puts PagSeguro::PaymentRequest::Serializer.new(payment).to_params 41 | 42 | response = payment.register 43 | 44 | puts 45 | puts "=> RESPONSE" 46 | puts response.url 47 | puts response.code 48 | puts response.created_at 49 | puts response.errors.to_a 50 | -------------------------------------------------------------------------------- /spec/pagseguro/items_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe PagSeguro::Items do 4 | it "implements #each" do 5 | expect(PagSeguro::Items.new).to respond_to(:each) 6 | end 7 | 8 | it "implements #any" do 9 | expect(PagSeguro::Items.new).to respond_to(:any?) 10 | end 11 | 12 | it "includes Enumerable" do 13 | expect(PagSeguro::Items.included_modules).to include(Enumerable) 14 | end 15 | 16 | it "sets item" do 17 | item = PagSeguro::Item.new 18 | items = PagSeguro::Items.new 19 | items << item 20 | 21 | expect(items.size).to eql(1) 22 | expect(items).to include(item) 23 | end 24 | 25 | it "sets item from Hash" do 26 | item = PagSeguro::Item.new(id: 1234) 27 | 28 | PagSeguro::Item 29 | .should_receive(:new) 30 | .with({id: 1234}) 31 | .and_return(item) 32 | 33 | items = PagSeguro::Items.new 34 | items << {id: 1234} 35 | 36 | expect(items).to include(item) 37 | end 38 | 39 | it "increases quantity when adding duplicated item" do 40 | item = PagSeguro::Item.new(id: 1234) 41 | items = PagSeguro::Items.new 42 | items << item 43 | items << item 44 | 45 | expect(items.size).to eql(1) 46 | expect(item.quantity).to eql(2) 47 | end 48 | 49 | it "empties items" do 50 | items = PagSeguro::Items.new 51 | items << {id: 1234} 52 | items.clear 53 | 54 | expect(items).to be_empty 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /pagseguro-oficial.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | require "./lib/pagseguro/version" 3 | 4 | Gem::Specification.new do |spec| 5 | spec.required_ruby_version = ">= 1.9.3" 6 | spec.name = "pagseguro-oficial" 7 | spec.version = PagSeguro::VERSION 8 | spec.authors = ["Nando Vieira"] 9 | spec.email = ["fnando.vieira@gmail.com"] 10 | spec.description = "Biblioteca oficial de integração PagSeguro em Ruby" 11 | spec.summary = spec.description 12 | spec.homepage = "http://www.pagseguro.com.br" 13 | spec.license = "ASL" 14 | 15 | spec.files = `git ls-files`.split($/) 16 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 17 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 18 | spec.require_paths = ["lib"] 19 | 20 | spec.add_dependency "aitch" 21 | spec.add_dependency "nokogiri" 22 | spec.add_dependency "i18n" 23 | spec.add_dependency "json" 24 | 25 | spec.add_development_dependency "bundler" 26 | spec.add_development_dependency "rake" 27 | spec.add_development_dependency "rspec" 28 | spec.add_development_dependency "pry-meta" 29 | spec.add_development_dependency "autotest-standalone" 30 | spec.add_development_dependency "test_notifier" 31 | spec.add_development_dependency "simplecov" 32 | spec.add_development_dependency "fakeweb" 33 | end 34 | -------------------------------------------------------------------------------- /spec/pagseguro/payment_method_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require "spec_helper" 3 | 4 | shared_examples_for "type mapping" do |id, type| 5 | it "returns #{type.inspect} as type when id is #{id}" do 6 | expect(PagSeguro::PaymentMethod.new(type_id: id).type).to eql(type) 7 | end 8 | end 9 | 10 | describe PagSeguro::PaymentMethod do 11 | context "type mapping" do 12 | it_behaves_like "type mapping", 1, :credit_card 13 | it_behaves_like "type mapping", 2, :boleto 14 | it_behaves_like "type mapping", 3, :online_transfer 15 | it_behaves_like "type mapping", 4, :balance 16 | it_behaves_like "type mapping", 5, :oi_paggo 17 | 18 | it "raises for invalid id" do 19 | expect { 20 | PagSeguro::PaymentMethod.new(type_id: "invalid").type 21 | }.to raise_exception("PagSeguro::PaymentMethod#type_id isn't mapped") 22 | end 23 | end 24 | 25 | context "shortcuts" do 26 | it { expect(PagSeguro::PaymentMethod.new(type_id: 1)).to be_credit_card } 27 | it { expect(PagSeguro::PaymentMethod.new(type_id: 2)).to be_boleto } 28 | it { expect(PagSeguro::PaymentMethod.new(type_id: 3)).to be_online_transfer } 29 | it { expect(PagSeguro::PaymentMethod.new(type_id: 4)).to be_balance } 30 | it { expect(PagSeguro::PaymentMethod.new(type_id: 5)).to be_oi_paggo } 31 | 32 | it { expect(PagSeguro::PaymentMethod.new(type_id: 5)).not_to be_credit_card } 33 | end 34 | 35 | context "description" do 36 | subject(:payment_method) { PagSeguro::PaymentMethod.new(code: 102) } 37 | it { expect(payment_method.description).to eql("Cartão de crédito MasterCard") } 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /spec/pagseguro/pagseguro_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe PagSeguro do 4 | before do 5 | PagSeguro.email = "EMAIL" 6 | PagSeguro.token = "TOKEN" 7 | PagSeguro.receiver_email = "RECEIVER_EMAIL" 8 | end 9 | 10 | it { expect(PagSeguro.email).to eql("EMAIL") } 11 | it { expect(PagSeguro.token).to eql("TOKEN") } 12 | it { expect(PagSeguro.receiver_email).to eql("RECEIVER_EMAIL") } 13 | 14 | context "configuring library" do 15 | it "yields PagSeguro" do 16 | expect {|block| 17 | PagSeguro.configure(&block) 18 | }.to yield_with_args(PagSeguro) 19 | end 20 | end 21 | 22 | context "default settings" do 23 | it { expect(PagSeguro.encoding).to eql("UTF-8") } 24 | it { expect(PagSeguro.environment).to eql(:production) } 25 | end 26 | 27 | describe ".api_url" do 28 | it "raises when environment has no endpoint" do 29 | PagSeguro.environment = :invalid 30 | 31 | expect { 32 | PagSeguro.api_url("/") 33 | }.to raise_exception(PagSeguro::InvalidEnvironmentError) 34 | end 35 | 36 | it "returns api url" do 37 | expect(PagSeguro.api_url("/some/path")).to eql("https://ws.pagseguro.uol.com.br/v2/some/path") 38 | end 39 | end 40 | 41 | describe ".site_url" do 42 | it "raises when environment has no endpoint" do 43 | PagSeguro.environment = :invalid 44 | 45 | expect { 46 | PagSeguro.site_url("/") 47 | }.to raise_exception(PagSeguro::InvalidEnvironmentError) 48 | end 49 | 50 | it "returns site url" do 51 | expect(PagSeguro.site_url("/some/path")).to eql("https://pagseguro.uol.com.br/v2/some/path") 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/pagseguro/shipping.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | class Shipping 3 | # Set the available shipping type. 4 | TYPE = { 5 | pac: 1, 6 | sedex: 2, 7 | not_specified: 3 8 | } 9 | 10 | # Define the error class for invalid type assignment. 11 | InvalidShippingTypeError = Class.new(StandardError) 12 | 13 | include Extensions::MassAssignment 14 | include Extensions::EnsureType 15 | 16 | # Define the shipping type id. 17 | attr_reader :type_id 18 | 19 | # Get the shipping type name. 20 | attr_reader :type_name 21 | 22 | # Get the address object. 23 | attr_reader :address 24 | 25 | # Set the shipping cost. 26 | attr_accessor :cost 27 | 28 | # Set the shipping address info. 29 | def address=(address) 30 | @address = ensure_type(Address, address) 31 | end 32 | 33 | # Set the shipping type. 34 | # It raises the PagSeguro::Shipping::InvalidShippingTypeError exception 35 | # when trying to assign an invalid type name. 36 | def type_name=(type_name) 37 | type_name = type_name.to_sym 38 | @type_id = TYPE.fetch(type_name) { 39 | raise InvalidShippingTypeError, "invalid #{type_name.inspect} type name" 40 | } 41 | @type_name = type_name 42 | end 43 | 44 | # Set the shipping type id. 45 | # It raises the PagSeguro::Shipping::InvalidShippingTypeError exception 46 | # when trying to assign an invalid type id. 47 | def type_id=(id) 48 | type_id = id.to_i 49 | 50 | raise InvalidShippingTypeError, 51 | "invalid #{id.inspect} type id" unless TYPE.value?(type_id) 52 | 53 | @type_id = type_id 54 | @type_name = TYPE.key(type_id) 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /spec/fixtures/transactions/additional.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2013-05-01T01:40:27.000-03:00 4 | 667A3914-4F9F-4705-0EB6-CA6FA0DF8A19 5 | REF1234 6 | 1 7 | 1 8 | 2013-05-01T01:41:20.000-03:00 9 | PagSeguro 10 | 2013-06-01T01:41:20.000-03:00 11 | 12 | 2 13 | 202 14 | 15 | https://pagseguro.uol.com.br/checkout/imprimeBoleto.jhtml?code=667D39144F9F47059FB6CA6FA0DF8A20 16 | 459.50 17 | 0.00 18 | 13.73 19 | 445.77 20 | 0.00 21 | 1 22 | 1 23 | 24 | 25 | 1234 26 | Some product 27 | 1 28 | 459.50 29 | 30 | 31 | 32 | JOHN DOE 33 | john@example.com 34 | 35 | 11 36 | 12345678 37 | 38 | 39 | 40 |
41 | AV. BRIG. FARIA LIMA 42 | 1384 43 | 5 ANDAR 44 | JARDIM PAULISTANO 45 | SAO PAULO 46 | SP 47 | BRA 48 | 01452002 49 |
50 | 2 51 | 0.00 52 |
53 |
54 | -------------------------------------------------------------------------------- /spec/fixtures/transactions/success.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2013-05-01T01:40:27.000-03:00 4 | 667A3914-4F9F-4705-0EB6-CA6FA0DF8A19 5 | REF1234 6 | 1 7 | 1 8 | 2013-05-01T01:41:20.000-03:00 9 | 10 | 11 | 12 | 2 13 | 202 14 | 15 | https://pagseguro.uol.com.br/checkout/imprimeBoleto.jhtml?code=667D39144F9F47059FB6CA6FA0DF8A20 16 | 459.50 17 | 0.00 18 | 13.73 19 | 445.77 20 | 0.00 21 | 1 22 | 1 23 | 24 | 25 | 1234 26 | Some product 27 | 1 28 | 459.50 29 | 30 | 31 | 32 | JOHN DOE 33 | john@example.com 34 | 35 | 11 36 | 12345678 37 | 38 | 39 | 40 |
41 | AV. BRIG. FARIA LIMA 42 | 1384 43 | 5 ANDAR 44 | JARDIM PAULISTANO 45 | SAO PAULO 46 | SP 47 | BRA 48 | 01452002 49 |
50 | 2 51 | 0.00 52 |
53 |
54 | -------------------------------------------------------------------------------- /spec/pagseguro/payment_request_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe PagSeguro::PaymentRequest do 4 | it_assigns_attribute :currency 5 | it_assigns_attribute :redirect_url 6 | it_assigns_attribute :extra_amount 7 | it_assigns_attribute :reference 8 | it_assigns_attribute :max_age 9 | it_assigns_attribute :max_uses 10 | it_assigns_attribute :notification_url 11 | it_assigns_attribute :abandon_url 12 | 13 | it_ensures_type PagSeguro::Sender, :sender 14 | it_ensures_type PagSeguro::Shipping, :shipping 15 | 16 | it "sets the sender" do 17 | sender = PagSeguro::Sender.new 18 | payment = PagSeguro::PaymentRequest.new(sender: sender) 19 | 20 | expect(payment.sender).to eql(sender) 21 | end 22 | 23 | it "sets the items" do 24 | payment = PagSeguro::PaymentRequest.new 25 | expect(payment.items).to be_a(PagSeguro::Items) 26 | end 27 | 28 | it "sets default currency" do 29 | payment = PagSeguro::PaymentRequest.new 30 | expect(payment.currency).to eql("BRL") 31 | end 32 | 33 | describe "#register" do 34 | let(:payment) { PagSeguro::PaymentRequest.new } 35 | before { FakeWeb.register_uri :any, %r[.*?], body: "" } 36 | 37 | it "serializes payment request" do 38 | PagSeguro::PaymentRequest::Serializer 39 | .should_receive(:new) 40 | .with(payment) 41 | .and_return(double.as_null_object) 42 | 43 | payment.register 44 | end 45 | 46 | it "performs request" do 47 | params = double 48 | PagSeguro::PaymentRequest::Serializer.any_instance.stub to_params: params 49 | 50 | PagSeguro::Request 51 | .should_receive(:post) 52 | .with("checkout", params) 53 | 54 | payment.register 55 | end 56 | 57 | it "initializes response" do 58 | response = double 59 | PagSeguro::Request.stub post: response 60 | 61 | PagSeguro::PaymentRequest::Response 62 | .should_receive(:new) 63 | .with(response) 64 | 65 | payment.register 66 | end 67 | 68 | it "returns response" do 69 | response = double 70 | PagSeguro::PaymentRequest::Response.stub new: response 71 | 72 | expect(payment.register).to eql(response) 73 | end 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /spec/pagseguro/request_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe PagSeguro::Request do 4 | context "default headers" do 5 | subject(:headers) { PagSeguro::Request.config.default_headers } 6 | 7 | it { should include("lib-description" => "ruby:#{PagSeguro::VERSION}") } 8 | it { should include("language-engine-description" => "ruby:#{RUBY_VERSION}") } 9 | end 10 | 11 | context "POST request" do 12 | before do 13 | FakeWeb.register_uri :post, %r[.+], body: "BODY" 14 | end 15 | 16 | it "includes credentials" do 17 | PagSeguro.email = "EMAIL" 18 | PagSeguro.token = "TOKEN" 19 | PagSeguro::Request.post("checkout") 20 | 21 | expect(FakeWeb.last_request.body).to include("email=EMAIL&token=TOKEN") 22 | end 23 | 24 | it "includes encoding" do 25 | PagSeguro::Request.post("checkout") 26 | expect(FakeWeb.last_request.body).to include("charset=UTF-8") 27 | end 28 | 29 | it "include request headers" do 30 | PagSeguro::Request.post("checkout") 31 | request = FakeWeb.last_request 32 | 33 | expect(request["Accept-Charset"]).to eql("UTF-8") 34 | expect(request["Content-Type"]).to eql("application/x-www-form-urlencoded; charset=UTF-8") 35 | expect(request["lib-description"]).to be 36 | expect(request["language-engine-description"]).to be 37 | end 38 | end 39 | 40 | context "GET request" do 41 | before do 42 | FakeWeb.register_uri :get, %r[.+], body: "BODY" 43 | end 44 | 45 | it "includes credentials" do 46 | PagSeguro.email = "EMAIL" 47 | PagSeguro.token = "TOKEN" 48 | PagSeguro::Request.get("checkout") 49 | 50 | expect(FakeWeb.last_request.path).to include("email=EMAIL&token=TOKEN") 51 | end 52 | 53 | it "includes encoding" do 54 | PagSeguro::Request.get("checkout") 55 | expect(FakeWeb.last_request.path).to include("charset=UTF-8") 56 | end 57 | 58 | it "include request headers" do 59 | PagSeguro::Request.get("checkout") 60 | request = FakeWeb.last_request 61 | 62 | expect(request["Accept-Charset"]).to eql("UTF-8") 63 | expect(request["lib-description"]).to be 64 | expect(request["language-engine-description"]).to be 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /lib/pagseguro/request.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | module Request 3 | extend self 4 | extend Forwardable 5 | 6 | # Delegates the :config and :configure methods 7 | # to the :request method, which returns a Aitch::Namespace instance. 8 | def_delegators :request, :config, :configure 9 | 10 | # Perform a GET request. 11 | # 12 | # # +path+: the path that will be requested. Must be something like "transactions/code/739D69-79C052C05280-55542D9FBB33-CAB2B1". 13 | # # +data+: the data that will be sent as query string. Must be a Hash. 14 | # # +headers+: any additional header that will be sent through the request. 15 | # 16 | def get(path, data = {}, headers = {}) 17 | execute :get, path, data, headers 18 | end 19 | 20 | # Perform a POST request. 21 | # 22 | # # +path+: the path that will be requested. Must be something like "checkout". 23 | # # +data+: the data that will be sent as body data. Must be a Hash. 24 | # # +headers+: any additional header that will be sent through the request. 25 | # 26 | def post(path, data = {}, headers = {}) 27 | execute :post, path, data, headers 28 | end 29 | 30 | # Perform the specified HTTP request. It will include the API credentials, 31 | # encoding and additional headers. 32 | def execute(request_method, path, data, headers) # :nodoc: 33 | request.public_send( 34 | request_method, 35 | PagSeguro.api_url(path), 36 | extended_data(data), 37 | extended_headers(request_method, headers) 38 | ) 39 | end 40 | 41 | private 42 | def request 43 | @request ||= Aitch::Namespace.new 44 | end 45 | 46 | def extended_data(data) 47 | data.merge( 48 | email: PagSeguro.email, 49 | token: PagSeguro.token, 50 | charset: PagSeguro.encoding 51 | ) 52 | end 53 | 54 | def extended_headers(request_method, headers) 55 | headers.merge __send__("headers_for_#{request_method}") 56 | end 57 | 58 | def headers_for_post 59 | { 60 | "Accept-Charset" => PagSeguro.encoding, 61 | "Content-Type" => "application/x-www-form-urlencoded; charset=#{PagSeguro.encoding}" 62 | } 63 | end 64 | 65 | def headers_for_get 66 | { 67 | "Accept-Charset" => PagSeguro.encoding 68 | } 69 | end 70 | end 71 | 72 | Request.configure do |config| 73 | config.default_headers = { 74 | "lib-description" => "ruby:#{PagSeguro::VERSION}", 75 | "language-engine-description" => "ruby:#{RUBY_VERSION}" 76 | } 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /spec/pagseguro/errors_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require "spec_helper" 3 | 4 | describe PagSeguro::Errors do 5 | let(:response) { double } 6 | 7 | context "when have no response" do 8 | it "returns errors" do 9 | errors = PagSeguro::Errors.new 10 | expect(errors).to be_empty 11 | end 12 | end 13 | 14 | context "when unauthorized" do 15 | subject(:errors) { PagSeguro::Errors.new(response) } 16 | 17 | before do 18 | response.stub unauthorized?: true, bad_request?: false 19 | end 20 | 21 | it { should_not be_empty } 22 | it { should include(I18n.t("pagseguro.errors.unauthorized")) } 23 | end 24 | 25 | context "when message can't be translated" do 26 | let(:error) { 27 | <<-XML 28 | 29 | 30 | 1234 31 | Sample message 32 | 33 | 34 | XML 35 | } 36 | 37 | let(:xml) { Nokogiri::XML(error) } 38 | subject(:errors) { PagSeguro::Errors.new(response) } 39 | 40 | before do 41 | response.stub data: xml, unauthorized?: false, bad_request?: true 42 | end 43 | 44 | it { expect(errors).to include("Sample message") } 45 | end 46 | 47 | context "when message can be translated" do 48 | let(:error) { 49 | <<-XML 50 | 51 | 52 | 10001 53 | Sample message 54 | 55 | 56 | XML 57 | } 58 | 59 | let(:xml) { Nokogiri::XML(error) } 60 | subject(:errors) { PagSeguro::Errors.new(response) } 61 | 62 | before do 63 | response.stub data: xml, unauthorized?: false, bad_request?: true 64 | end 65 | 66 | it { expect(errors).to include("O parâmetro email deve ser informado.") } 67 | end 68 | 69 | context "when returning 404 status" do 70 | let(:error) { 71 | <<-XML 72 | 73 | 74 | 75 | Malformed request XML: {0}. 76 | Malformed request XML: XML document structures must start and end within the same entity.. 77 | 78 | 79 | XML 80 | } 81 | 82 | let(:xml) { Nokogiri::XML(error) } 83 | subject(:errors) { PagSeguro::Errors.new(response) } 84 | 85 | before do 86 | response.stub data: xml, unauthorized?: false, bad_request?: true 87 | end 88 | 89 | it { expect(errors).to include("Malformed request XML: XML document structures must start and end within the same entity..") } 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /lib/pagseguro.rb: -------------------------------------------------------------------------------- 1 | require "bigdecimal" 2 | require "forwardable" 3 | require "time" 4 | 5 | require "nokogiri" 6 | require "aitch" 7 | require "i18n" 8 | 9 | require "pagseguro/version" 10 | require "pagseguro/errors" 11 | require "pagseguro/exceptions" 12 | require "pagseguro/extensions/mass_assignment" 13 | require "pagseguro/extensions/ensure_type" 14 | require "pagseguro/address" 15 | require "pagseguro/shipping" 16 | require "pagseguro/phone" 17 | require "pagseguro/item" 18 | require "pagseguro/items" 19 | require "pagseguro/payment_method" 20 | require "pagseguro/payment_request" 21 | require "pagseguro/payment_request/serializer" 22 | require "pagseguro/payment_request/response" 23 | require "pagseguro/payment_status" 24 | require "pagseguro/request" 25 | require "pagseguro/report" 26 | require "pagseguro/sender" 27 | require "pagseguro/notification" 28 | require "pagseguro/transaction" 29 | require "pagseguro/transaction/response" 30 | require "pagseguro/transaction/serializer" 31 | 32 | I18n.load_path += Dir[File.expand_path("../../locales/*.yml", __FILE__)] 33 | 34 | module PagSeguro 35 | class << self 36 | # Primary e-mail associated with this account. 37 | attr_accessor :email 38 | 39 | # The e-mail that will be displayed when sender is redirected 40 | # to PagSeguro. 41 | attr_accessor :receiver_email 42 | 43 | # The API token associated with this account. 44 | attr_accessor :token 45 | 46 | # The encoding that will be used. 47 | attr_accessor :encoding 48 | 49 | # The PagSeguro environment. 50 | # Only +production+ for now. 51 | attr_accessor :environment 52 | end 53 | 54 | self.encoding = "UTF-8" 55 | self.environment = :production 56 | 57 | # Register endpoints by environment. 58 | def self.uris 59 | @uris ||= { 60 | production: { 61 | api: "https://ws.pagseguro.uol.com.br/v2", 62 | site: "https://pagseguro.uol.com.br/v2" 63 | } 64 | } 65 | end 66 | 67 | # Return the root uri based on its type. 68 | # Current types are :api or :site 69 | def self.root_uri(type) 70 | root = uris.fetch(environment.to_sym) { raise InvalidEnvironmentError } 71 | root[type.to_sym] 72 | end 73 | 74 | # Set the global configuration. 75 | # 76 | # PagSeguro.configure do |config| 77 | # config.email = "john@example.com" 78 | # config.token = "abc" 79 | # end 80 | # 81 | def self.configure(&block) 82 | yield self 83 | end 84 | 85 | # The API endpoint. 86 | def self.api_url(path) 87 | File.join(root_uri(:api), path) 88 | end 89 | 90 | # The site url. 91 | def self.site_url(path) 92 | File.join(root_uri(:site), path) 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /lib/pagseguro/payment_request.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | class PaymentRequest 3 | include Extensions::MassAssignment 4 | include Extensions::EnsureType 5 | 6 | # Set the payment currency. 7 | # Defaults to BRL. 8 | attr_accessor :currency 9 | 10 | # Get the payment sender. 11 | attr_reader :sender 12 | 13 | # Get the shipping info. 14 | attr_reader :shipping 15 | 16 | # Set the redirect url. 17 | # The URL that will be used by PagSeguro to redirect the user after 18 | # the payment information is processed. Typically this is a 19 | # confirmation page on your web site. 20 | attr_accessor :redirect_url 21 | 22 | # Set the extra amount to be applied to the transaction's total. 23 | # This value can be used to add an extra charge to the transaction 24 | # or provide a discount, if negative. 25 | attr_accessor :extra_amount 26 | 27 | # Set the reference code. 28 | # Optional. You can use the reference code to store an identifier so you can 29 | # associate the PagSeguro transaction to a transaction in your system. 30 | # Tipically this is the order id. 31 | attr_accessor :reference 32 | 33 | # Set the payment request duration, in seconds. 34 | attr_accessor :max_age 35 | 36 | # How many times the payment redirect uri returned by the payment 37 | # web service can be accessed. 38 | # Optional. After this payment request is submitted, the payment 39 | # redirect uri returned by the payment web service will remain valid 40 | # for the number of uses specified here. 41 | attr_accessor :max_uses 42 | 43 | # Determines for which url PagSeguro will send the order related 44 | # notifications codes. 45 | # Optional. Any change happens in the transaction status, a new notification 46 | # request will be send to this url. You can use that for update the related 47 | # order. 48 | attr_accessor :notification_url 49 | 50 | # Determines for which url PagSeguro will send the buyer when he doesn't 51 | # complete the payment. 52 | attr_accessor :abandon_url 53 | 54 | # Products/items in this payment request. 55 | def items 56 | @items ||= Items.new 57 | end 58 | 59 | # Set the payment sender. 60 | def sender=(sender) 61 | @sender = ensure_type(Sender, sender) 62 | end 63 | 64 | # Set the shipping info. 65 | def shipping=(shipping) 66 | @shipping = ensure_type(Shipping, shipping) 67 | end 68 | 69 | # Calls the PagSeguro web service and register this request for payment. 70 | def register 71 | params = Serializer.new(self).to_params 72 | Response.new Request.post("checkout", params) 73 | end 74 | 75 | private 76 | def before_initialize 77 | self.currency = "BRL" 78 | end 79 | 80 | def endpoint 81 | PagSeguro.api_url("checkout") 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /spec/fixtures/by_date/success.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2013-05-29T07:55:26.000-03:00 4 | 5 | 6 | 2013-05-24T15:42:39.000-03:00 7 | 6853CD06-562E-4D9C-9427-75BFADBCEA0D 8 | 1 9 | 3 10 | 11 | 1 12 | 13 | 450.00 14 | 0.00 15 | 29.20 16 | 420.80 17 | 0.00 18 | 2013-05-24T15:44:27.000-03:00 19 | 20 | 21 | 2013-05-24T15:10:59.000-03:00 22 | DE9F09F9-BC1C-459A-A3A9-9D4B9D8FA663 23 | 1 24 | 3 25 | 26 | 1 27 | 28 | 550.00 29 | 0.00 30 | 35.60 31 | 514.40 32 | 0.00 33 | 2013-05-24T15:12:06.000-03:00 34 | 35 | 36 | 2013-05-16T09:40:14.000-03:00 37 | AF469F9E-4C87-47B9-ACF5-0678037483E1 38 | 1 39 | 3 40 | 41 | 1 42 | 43 | 520.00 44 | 0.00 45 | 33.68 46 | 486.32 47 | 0.00 48 | 2013-05-16T09:42:53.000-03:00 49 | 50 | 51 | 2013-05-09T01:21:24.000-03:00 52 | 52BBF866-1EE3-486A-AA11-C08C00375BDF 53 | 1 54 | 7 55 | EXTERNAL 56 | 57 | 1 58 | 59 | 520.00 60 | 0.00 61 | 33.68 62 | 486.32 63 | 0.00 64 | 2013-05-09T01:22:20.000-03:00 65 | 66 | 67 | 2013-05-06T19:45:39.000-03:00 68 | 5E68D3A7-AB57-4C0E-B29F-1901AF497BA9 69 | 1 70 | 4 71 | 72 | 1 73 | 74 | 500.00 75 | 0.00 76 | 32.40 77 | 467.60 78 | 0.00 79 | 2013-05-20T19:57:57.000-03:00 80 | 81 | 82 | 5 83 | 1 84 | 1 85 | 86 | -------------------------------------------------------------------------------- /lib/pagseguro/payment_request/serializer.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | class PaymentRequest 3 | class Serializer 4 | # The payment request that will be serialized. 5 | attr_reader :payment_request 6 | 7 | def initialize(payment_request) 8 | @payment_request = payment_request 9 | end 10 | 11 | def to_params 12 | params[:receiverEmail] = PagSeguro.receiver_email 13 | params[:currency] = payment_request.currency 14 | params[:reference] = payment_request.reference 15 | params[:extraAmount] = to_amount(payment_request.extra_amount) 16 | params[:redirectURL] = payment_request.redirect_url 17 | params[:notificationURL] = payment_request.notification_url 18 | params[:abandonURL] = payment_request.abandon_url 19 | params[:maxUses] = payment_request.max_uses 20 | params[:maxAge] = payment_request.max_age 21 | payment_request.items.each.with_index(1) do |item, index| 22 | serialize_item(item, index) 23 | end 24 | 25 | serialize_sender(payment_request.sender) 26 | serialize_shipping(payment_request.shipping) 27 | 28 | params.delete_if {|key, value| value.nil? } 29 | 30 | params 31 | end 32 | 33 | private 34 | def params 35 | @params ||= {} 36 | end 37 | 38 | def serialize_item(item, index) 39 | params["itemId#{index}"] = item.id 40 | params["itemDescription#{index}"] = item.description 41 | params["itemAmount#{index}"] = to_amount(item.amount) 42 | params["itemQuantity#{index}"] = item.quantity 43 | params["itemShippingCost#{index}"] = to_amount(item.shipping_cost) 44 | params["itemWeight#{index}"] = item.weight if item.weight 45 | end 46 | 47 | def serialize_sender(sender) 48 | return unless sender 49 | 50 | params[:senderEmail] = sender.email 51 | params[:senderName] = sender.name 52 | params[:senderCPF] = sender.cpf 53 | 54 | serialize_phone(sender.phone) 55 | end 56 | 57 | def serialize_phone(phone) 58 | return unless phone 59 | 60 | params[:senderAreaCode] = phone.area_code 61 | params[:senderPhone] = phone.number 62 | end 63 | 64 | def serialize_shipping(shipping) 65 | return unless shipping 66 | 67 | params[:shippingType] = shipping.type_id 68 | params[:shippingCost] = to_amount(shipping.cost) 69 | 70 | serialize_address(shipping.address) 71 | end 72 | 73 | def serialize_address(address) 74 | return unless address 75 | 76 | params[:shippingAddressCountry] = address.country 77 | params[:shippingAddressState] = address.state 78 | params[:shippingAddressCity] = address.city 79 | params[:shippingAddressPostalCode] = address.postal_code 80 | params[:shippingAddressDistrict] = address.district 81 | params[:shippingAddressStreet] = address.street 82 | params[:shippingAddressNumber] = address.number 83 | params[:shippingAddressComplement] = address.complement 84 | end 85 | 86 | def to_amount(amount) 87 | "%.2f" % BigDecimal(amount.to_s).round(2).to_s("F") if amount 88 | end 89 | end 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /spec/pagseguro/transaction/serializer_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require "spec_helper" 3 | 4 | describe PagSeguro::Transaction::Serializer do 5 | context "for existing transactions" do 6 | let(:source) { File.read("./spec/fixtures/transactions/success.xml") } 7 | let(:xml) { Nokogiri::XML(source) } 8 | let(:serializer) { described_class.new(xml.css("transaction").first) } 9 | subject(:data) { serializer.serialize } 10 | 11 | it { expect(data).to include(created_at: Time.parse("2013-05-01T01:40:27.000-03:00")) } 12 | it { expect(data).to include(updated_at: Time.parse("2013-05-01T01:41:20.000-03:00")) } 13 | it { expect(data).to include(code: "667A3914-4F9F-4705-0EB6-CA6FA0DF8A19") } 14 | it { expect(data).to include(reference: "REF1234") } 15 | it { expect(data).to include(type_id: "1") } 16 | it { expect(data).to include(status: "1") } 17 | it { expect(data).to include(payment_method: {type_id: "2", code: "202"}) } 18 | it { expect(data).to include(payment_link: "https://pagseguro.uol.com.br/checkout/imprimeBoleto.jhtml?code=667D39144F9F47059FB6CA6FA0DF8A20") } 19 | it { expect(data).to include(gross_amount: BigDecimal("459.50")) } 20 | it { expect(data).to include(discount_amount: BigDecimal("0.00")) } 21 | it { expect(data).to include(fee_amount: BigDecimal("13.73")) } 22 | it { expect(data).to include(net_amount: BigDecimal("445.77")) } 23 | it { expect(data).to include(extra_amount: BigDecimal("0.00")) } 24 | it { expect(data).to include(installments: 1) } 25 | 26 | it { expect(data.keys).not_to include(:cancellation_source) } 27 | it { expect(data.keys).not_to include(:escrow_end_date) } 28 | 29 | it { expect(data[:items]).to have(1).item } 30 | it { expect(data[:items].first).to include(id: "1234") } 31 | it { expect(data[:items].first).to include(description: "Some product") } 32 | it { expect(data[:items].first).to include(quantity: 1) } 33 | it { expect(data[:items].first).to include(amount: BigDecimal("459.50")) } 34 | 35 | it { expect(data[:sender]).to include(name: "JOHN DOE") } 36 | it { expect(data[:sender]).to include(email: "john@example.com") } 37 | it { expect(data[:sender][:phone]).to include(area_code: "11") } 38 | it { expect(data[:sender][:phone]).to include(number: "12345678") } 39 | 40 | it { expect(data[:shipping]).to include(type_id: "2") } 41 | 42 | it { expect(data[:shipping][:address]).to include(street: "AV. BRIG. FARIA LIMA") } 43 | it { expect(data[:shipping][:address]).to include(number: "1384") } 44 | it { expect(data[:shipping][:address]).to include(complement: "5 ANDAR") } 45 | it { expect(data[:shipping][:address]).to include(district: "JARDIM PAULISTANO") } 46 | it { expect(data[:shipping][:address]).to include(city: "SAO PAULO") } 47 | it { expect(data[:shipping][:address]).to include(state: "SP") } 48 | it { expect(data[:shipping][:address]).to include(country: "BRA") } 49 | it { expect(data[:shipping][:address]).to include(postal_code: "01452002") } 50 | end 51 | 52 | context "additional nodes" do 53 | let(:source) { File.read("./spec/fixtures/transactions/additional.xml") } 54 | let(:xml) { Nokogiri::XML(source) } 55 | let(:serializer) { described_class.new(xml) } 56 | subject(:data) { serializer.serialize } 57 | 58 | it { expect(data).to include(cancellation_source: "PagSeguro") } 59 | it { expect(data).to include(escrow_end_date: Time.parse("2013-06-01T01:41:20.000-03:00")) } 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/pagseguro/report.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | class Report 3 | # Set the report options. 4 | # 5 | # # +per_page+: the page size. 6 | # # +starts_at+: the report's starting date. Can't be older than 6 months. 7 | # # +ends_at+: the report's ending date. Can't be greater than 30 days from the starting date. 8 | # 9 | attr_reader :options 10 | 11 | # Set the errors from the report request. 12 | attr_reader :errors 13 | 14 | # Return the current page. 15 | attr_reader :page 16 | 17 | def initialize(item_class, path, options) 18 | @item_class = item_class 19 | @path = path 20 | @options = options 21 | @page = 0 22 | end 23 | 24 | # Return the list of transactions. 25 | # Each item will be wrapped in a PagSeguro::Transaction instance. 26 | # Notice that transactions instantiated by the report won't have all attributes. 27 | # If you need additional attributes, do a PagSeguro::Transaction.find_by_code 28 | # call. Remember that this will perform an additional HTTP request. 29 | def transactions 30 | xml do |xml| 31 | @transactions ||= xml.css("transactionSearchResult transaction").map do |node| 32 | Transaction.load_from_xml(node) 33 | end 34 | end 35 | end 36 | 37 | # The report's creation date. 38 | def created_at 39 | xml do |xml| 40 | @created_at ||= Time.parse xml.css("transactionSearchResult > date").text 41 | end 42 | end 43 | 44 | # How many results the report returned on the given page. 45 | def results 46 | xml do |xml| 47 | @results ||= xml.css("transactionSearchResult > resultsInThisPage").text.to_i 48 | end 49 | end 50 | 51 | # How many pages the report returned. 52 | def total_pages 53 | xml do |xml| 54 | @total_pages ||= xml.css("transactionSearchResult > totalPages").text.to_i 55 | end 56 | end 57 | 58 | # Detect if the report has a next page. 59 | def next_page? 60 | page.zero? || page < total_pages 61 | end 62 | 63 | # Detect if the report has a previous page. 64 | def previous_page? 65 | page > 1 66 | end 67 | 68 | # Move the page pointer to the next page. 69 | def next_page! 70 | return unless next_page? 71 | @page += 1 72 | clear! 73 | end 74 | 75 | # Move the page pointer to the previous page. 76 | def previous_page! 77 | return unless previous_page? 78 | @page -= 1 79 | clear! 80 | end 81 | 82 | # Detect if the report request returned errors. 83 | def valid? 84 | fetch { errors.empty? } 85 | end 86 | 87 | private 88 | def perform_request_and_serialize 89 | @response = Request.get(@path, { 90 | initialDate: options[:starts_at].xmlschema, 91 | finalDate: options[:ends_at].xmlschema, 92 | page: page, 93 | maxPageResults: options.fetch(:per_page, 50) 94 | }) 95 | 96 | @errors = Errors.new(@response) 97 | end 98 | 99 | def fetched? 100 | @fetched 101 | end 102 | 103 | def fetched! 104 | @fetched = true 105 | end 106 | 107 | def clear! 108 | @fetched = false 109 | end 110 | 111 | def fetch(&block) 112 | unless fetched? 113 | perform_request_and_serialize 114 | fetched! 115 | end 116 | 117 | instance_eval(&block) 118 | end 119 | 120 | def xml(&block) 121 | valid? && block.call(@response.data) 122 | end 123 | end 124 | end 125 | -------------------------------------------------------------------------------- /spec/pagseguro/transaction_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe PagSeguro::Transaction do 4 | describe ".find_by_code" do 5 | it "finds transaction by the given code" do 6 | PagSeguro::Transaction.stub :load_from_response 7 | 8 | PagSeguro::Request 9 | .should_receive(:get) 10 | .with("transactions/notifications/CODE") 11 | .and_return(double.as_null_object) 12 | 13 | PagSeguro::Transaction.find_by_code("CODE") 14 | end 15 | 16 | it "returns response with errors when request fails" do 17 | body = %[1234Sample error] 18 | FakeWeb.register_uri :get, %r[.+], status: [400, "Bad Request"], body: body, content_type: "text/xml" 19 | response = PagSeguro::Transaction.find_by_code("invalid") 20 | 21 | expect(response).to be_a(PagSeguro::Transaction::Response) 22 | expect(response.errors).to include("Sample error") 23 | end 24 | end 25 | 26 | describe ".find_by_date" do 27 | it "initializes report with default options" do 28 | now = Time.now 29 | Time.stub now: now 30 | 31 | PagSeguro::Report 32 | .should_receive(:new) 33 | .with( 34 | PagSeguro::Transaction, 35 | "transactions", 36 | hash_including(per_page: 50, starts_at: now - 86400, ends_at: now) 37 | ) 38 | 39 | PagSeguro::Transaction.find_by_date 40 | end 41 | 42 | it "initializes report with given options" do 43 | starts_at = Time.now - 3600 44 | ends_at = starts_at + 180 45 | 46 | PagSeguro::Report 47 | .should_receive(:new) 48 | .with( 49 | PagSeguro::Transaction, 50 | "transactions", 51 | hash_including(per_page: 10, starts_at: starts_at, ends_at: ends_at) 52 | ) 53 | 54 | PagSeguro::Transaction.find_by_date( 55 | per_page: 10, 56 | starts_at: starts_at, 57 | ends_at: ends_at 58 | ) 59 | end 60 | end 61 | 62 | describe ".find_abandoned" do 63 | it "initializes report with default options" do 64 | now = Time.now 65 | Time.stub now: now 66 | 67 | PagSeguro::Report 68 | .should_receive(:new) 69 | .with( 70 | PagSeguro::Transaction, 71 | "transactions/abandoned", 72 | hash_including(per_page: 50, starts_at: now - 86400, ends_at: now - 900) 73 | ) 74 | 75 | PagSeguro::Transaction.find_abandoned 76 | end 77 | 78 | it "initializes report with given options" do 79 | starts_at = Time.now - 3600 80 | ends_at = starts_at + 180 81 | 82 | PagSeguro::Report 83 | .should_receive(:new) 84 | .with( 85 | PagSeguro::Transaction, 86 | "transactions/abandoned", 87 | hash_including(per_page: 10, starts_at: starts_at, ends_at: ends_at) 88 | ) 89 | 90 | PagSeguro::Transaction.find_abandoned( 91 | per_page: 10, 92 | starts_at: starts_at, 93 | ends_at: ends_at 94 | ) 95 | end 96 | end 97 | 98 | describe "attributes" do 99 | before do 100 | body = File.read("./spec/fixtures/transactions/success.xml") 101 | FakeWeb.register_uri :get, %r[.+], body: body, content_type: "text/xml" 102 | end 103 | 104 | subject(:transaction) { PagSeguro::Transaction.find_by_code("CODE") } 105 | 106 | it { expect(transaction.sender).to be_a(PagSeguro::Sender) } 107 | it { expect(transaction.shipping).to be_a(PagSeguro::Shipping) } 108 | it { expect(transaction.items).to be_a(PagSeguro::Items) } 109 | it { expect(transaction.payment_method).to be_a(PagSeguro::PaymentMethod) } 110 | it { expect(transaction.status).to be_a(PagSeguro::PaymentStatus) } 111 | it { expect(transaction.items).to have(1).item } 112 | it { expect(transaction).to respond_to(:escrow_end_date) } 113 | end 114 | end 115 | -------------------------------------------------------------------------------- /lib/pagseguro/transaction/serializer.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | class Transaction 3 | class Serializer 4 | attr_reader :xml 5 | 6 | def initialize(xml) 7 | @xml = xml 8 | end 9 | 10 | def serialize 11 | {}.tap do |data| 12 | serialize_general(data) 13 | serialize_amounts(data) 14 | serialize_dates(data) 15 | serialize_items(data) 16 | serialize_sender(data) 17 | serialize_shipping(data) if xml.css("shipping").any? 18 | end 19 | end 20 | 21 | def serialize_general(data) 22 | data[:code] = xml.css(">code").text 23 | data[:reference] = xml.css("reference").text 24 | data[:type_id] = xml.css(">type").text 25 | data[:payment_link] = xml.css("paymentLink").text 26 | data[:status] = xml.css("status").text 27 | 28 | cancellation_source = xml.css("cancellationSource") 29 | data[:cancellation_source] = cancellation_source.text if cancellation_source.any? 30 | 31 | data[:payment_method] = { 32 | type_id: xml.css("paymentMethod > type").text, 33 | code: xml.css("paymentMethod > code").text 34 | } 35 | end 36 | 37 | def serialize_dates(data) 38 | data[:created_at] = Time.parse(xml.css("date").text) 39 | 40 | updated_at = xml.css("lastEventDate").text 41 | data[:updated_at] = Time.parse(updated_at) unless updated_at.empty? 42 | 43 | escrow_end_date = xml.css("escrowEndDate") 44 | data[:escrow_end_date] = Time.parse(escrow_end_date.text) if escrow_end_date.any? 45 | end 46 | 47 | def serialize_amounts(data) 48 | data[:gross_amount] = BigDecimal(xml.css("grossAmount").text) 49 | data[:discount_amount] = BigDecimal(xml.css("discountAmount").text) 50 | data[:fee_amount] = BigDecimal(xml.css("feeAmount").text) 51 | data[:net_amount] = BigDecimal(xml.css("netAmount").text) 52 | data[:extra_amount] = BigDecimal(xml.css("extraAmount").text) 53 | data[:installments] = xml.css("installmentCount").text.to_i 54 | end 55 | 56 | def serialize_items(data) 57 | data[:items] = [] 58 | 59 | xml.css("items > item").each do |node| 60 | item = {} 61 | item[:id] = node.css("id").text 62 | item[:description] = node.css("description").text 63 | item[:quantity] = node.css("quantity").text.to_i 64 | item[:amount] = BigDecimal(node.css("amount").text) 65 | 66 | data[:items] << item 67 | end 68 | end 69 | 70 | def serialize_sender(data) 71 | sender = { 72 | name: xml.css("sender > name").text, 73 | email: xml.css("sender > email").text 74 | } 75 | 76 | serialize_phone(sender) 77 | data[:sender] = sender 78 | end 79 | 80 | def serialize_phone(data) 81 | data[:phone] = { 82 | area_code: xml.css("sender > phone > areaCode").text, 83 | number: xml.css("sender > phone > number").text 84 | } 85 | end 86 | 87 | def serialize_shipping(data) 88 | shipping = { 89 | type_id: xml.css("shipping > type").text, 90 | cost: BigDecimal(xml.css("shipping > cost").text), 91 | } 92 | 93 | serialize_address(shipping) 94 | data[:shipping] = shipping 95 | end 96 | 97 | def serialize_address(data) 98 | data[:address] = { 99 | street: address_node.css("> street").text, 100 | number: address_node.css("> number").text, 101 | complement: address_node.css("> complement").text, 102 | district: address_node.css("> district").text, 103 | city: address_node.css("> city").text, 104 | state: address_node.css("> state").text, 105 | country: address_node.css("> country").text, 106 | postal_code: address_node.css("> postalCode").text, 107 | } 108 | end 109 | 110 | def address_node 111 | @address_node ||= xml.css("shipping > address") 112 | end 113 | end 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /lib/pagseguro/transaction.rb: -------------------------------------------------------------------------------- 1 | module PagSeguro 2 | class Transaction 3 | include Extensions::MassAssignment 4 | include Extensions::EnsureType 5 | 6 | # When the payment request was created. 7 | attr_accessor :created_at 8 | 9 | # The transaction code. 10 | attr_accessor :code 11 | 12 | # The reference code identifies the order you placed on the payment request. 13 | # It's used by the store and can be something like the order id. 14 | attr_accessor :reference 15 | 16 | # The transaction type. 17 | attr_accessor :type_id 18 | 19 | # The last notification's update. 20 | attr_accessor :updated_at 21 | 22 | # The transaction status. 23 | attr_reader :status 24 | 25 | # The payment method. 26 | attr_reader :payment_method 27 | 28 | # The boleto payment url. 29 | attr_accessor :payment_link 30 | 31 | # The gross amount. 32 | attr_accessor :gross_amount 33 | 34 | # The discount amount. 35 | attr_accessor :discount_amount 36 | 37 | # The PagSeguro fee amount. 38 | attr_accessor :fee_amount 39 | 40 | # The net amount. 41 | attr_accessor :net_amount 42 | 43 | # Set the extra amount applied to the transaction's total. 44 | # It's considered as an extra charge when positive, or a discount if 45 | # negative. 46 | attr_accessor :extra_amount 47 | 48 | # The installment count. 49 | attr_accessor :installments 50 | 51 | # The payer information (who is sending money). 52 | attr_reader :sender 53 | 54 | # The shipping information. 55 | attr_reader :shipping 56 | 57 | # The cancellation source. 58 | attr_accessor :cancellation_source 59 | 60 | # The escrow end date. 61 | attr_accessor :escrow_end_date 62 | 63 | # Set the transaction errors. 64 | attr_reader :errors 65 | 66 | # Find a transaction by its code. 67 | # Return a PagSeguro::Transaction instance. 68 | def self.find_by_code(code) 69 | load_from_response Request.get("transactions/notifications/#{code}") 70 | end 71 | 72 | # Search transactions within a date range. 73 | # Return a PagSeguro::Report instance. 74 | # 75 | # Options: 76 | # 77 | # # +starts_at+: the starting date. Defaults to the last 24-hours. 78 | # # +ends_at+: the ending date. 79 | # # +page+: the current page. 80 | # # +per_page+: the result limit. 81 | # 82 | def self.find_by_date(options = {}) 83 | options = { 84 | starts_at: Time.now - 86400, 85 | ends_at: Time.now, 86 | per_page: 50 87 | }.merge(options) 88 | 89 | Report.new(Transaction, "transactions", options) 90 | end 91 | 92 | # Get abandoned transactions. 93 | # Return a PagSeguro::Report instance. 94 | # 95 | # Options: 96 | # 97 | # # +starts_at+: the starting date. Defaults to the last 24-hours. 98 | # # +ends_at+: the ending date. Defaults to 15 minutes ago. **Attention:** you have to set it this to Time.now - 15 minutes, otherwise the "finalDate must be lower than allowed limit" error will be returned. 99 | # # +page+: the current page. 100 | # # +per_page+: the result limit. 101 | # 102 | def self.find_abandoned(options = {}) 103 | options = { 104 | starts_at: Time.now - 86400, 105 | ends_at: Time.now - 900, 106 | per_page: 50 107 | }.merge(options) 108 | 109 | Report.new(Transaction, "transactions/abandoned", options) 110 | end 111 | 112 | # Serialize the HTTP response into data. 113 | def self.load_from_response(response) # :nodoc: 114 | if response.success? 115 | load_from_xml response.data.css("transaction").first 116 | else 117 | Response.new Errors.new(response) 118 | end 119 | end 120 | 121 | # Serialize the XML object. 122 | def self.load_from_xml(xml) # :nodoc: 123 | new Serializer.new(xml).serialize 124 | end 125 | 126 | # Normalize the sender object. 127 | def sender=(sender) 128 | @sender = ensure_type(Sender, sender) 129 | end 130 | 131 | # Normalize the shipping object. 132 | def shipping=(shipping) 133 | @shipping = ensure_type(Shipping, shipping) 134 | end 135 | 136 | # Hold the transaction's items. 137 | def items 138 | @items ||= Items.new 139 | end 140 | 141 | # Normalize the items list. 142 | def items=(_items) 143 | _items.each {|item| items << item } 144 | end 145 | 146 | # Normalize the payment method. 147 | def payment_method=(payment_method) 148 | @payment_method = ensure_type(PaymentMethod, payment_method) 149 | end 150 | 151 | # Normalize the payment status. 152 | def status=(status) 153 | @status = ensure_type(PaymentStatus, status) 154 | end 155 | 156 | private 157 | def after_initialize 158 | @errors = Errors.new 159 | end 160 | end 161 | end 162 | -------------------------------------------------------------------------------- /spec/pagseguro/payment_request/serializer_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe PagSeguro::PaymentRequest::Serializer do 4 | let(:payment_request) { PagSeguro::PaymentRequest.new } 5 | let(:params) { serializer.to_params } 6 | subject(:serializer) { described_class.new(payment_request) } 7 | 8 | context "global configuration serialization" do 9 | before do 10 | PagSeguro.receiver_email = "RECEIVER" 11 | end 12 | 13 | it { expect(params).to include(receiverEmail: PagSeguro.receiver_email) } 14 | end 15 | 16 | context "generic attributes serialization" do 17 | before do 18 | payment_request.stub({ 19 | currency: "BRL", 20 | reference: "REF123", 21 | extra_amount: 1234.50, 22 | redirect_url: "REDIRECT_URL", 23 | notification_url: "NOTIFICATION_URL", 24 | abandon_url: "ABANDON_URL", 25 | max_uses: 5, 26 | max_age: 3600 27 | }) 28 | end 29 | 30 | it { expect(params).to include(currency: "BRL") } 31 | it { expect(params).to include(reference: "REF123") } 32 | it { expect(params).to include(extraAmount: "1234.50") } 33 | it { expect(params).to include(redirectURL: "REDIRECT_URL") } 34 | it { expect(params).to include(notificationURL: "NOTIFICATION_URL") } 35 | it { expect(params).to include(abandonURL: "ABANDON_URL") } 36 | it { expect(params).to include(maxUses: 5) } 37 | it { expect(params).to include(maxAge: 3600) } 38 | end 39 | 40 | context "shipping serialization" do 41 | before do 42 | payment_request.shipping = PagSeguro::Shipping.new({ 43 | type_id: 1, 44 | cost: 1234.56 45 | }) 46 | end 47 | 48 | it { expect(params).to include(shippingType: 1) } 49 | it { expect(params).to include(shippingCost: "1234.56") } 50 | end 51 | 52 | context "address serialization" do 53 | before do 54 | address = PagSeguro::Address.new({ 55 | street: "STREET", 56 | state: "STATE", 57 | city: "CITY", 58 | postal_code: "POSTAL_CODE", 59 | district: "DISTRICT", 60 | number: "NUMBER", 61 | complement: "COMPLEMENT" 62 | }) 63 | 64 | shipping = double(address: address).as_null_object 65 | 66 | payment_request.stub( 67 | shipping: shipping 68 | ) 69 | end 70 | 71 | it { expect(params).to include(shippingAddressStreet: "STREET") } 72 | it { expect(params).to include(shippingAddressCountry: "BRA") } 73 | it { expect(params).to include(shippingAddressState: "STATE") } 74 | it { expect(params).to include(shippingAddressCity: "CITY") } 75 | it { expect(params).to include(shippingAddressPostalCode: "POSTAL_CODE") } 76 | it { expect(params).to include(shippingAddressDistrict: "DISTRICT") } 77 | it { expect(params).to include(shippingAddressNumber: "NUMBER") } 78 | it { expect(params).to include(shippingAddressComplement: "COMPLEMENT") } 79 | end 80 | 81 | context "sender serialization" do 82 | before do 83 | sender = PagSeguro::Sender.new({ 84 | email: "EMAIL", 85 | name: "NAME", 86 | cpf: "CPF" 87 | }) 88 | 89 | payment_request.stub(sender: sender) 90 | end 91 | 92 | it { expect(params).to include(senderEmail: "EMAIL") } 93 | it { expect(params).to include(senderName: "NAME") } 94 | it { expect(params).to include(senderCPF: "CPF") } 95 | end 96 | 97 | context "phone serialization" do 98 | before do 99 | sender = PagSeguro::Sender.new({ 100 | phone: { 101 | area_code: "AREA_CODE", 102 | number: "NUMBER" 103 | } 104 | }) 105 | 106 | payment_request.stub(sender: sender) 107 | end 108 | 109 | it { expect(params).to include(senderAreaCode: "AREA_CODE") } 110 | it { expect(params).to include(senderPhone: "NUMBER") } 111 | end 112 | 113 | context "items serialization" do 114 | def build_item(index) 115 | PagSeguro::Item.new({ 116 | id: "ID#{index}", 117 | description: "DESC#{index}", 118 | quantity: "QTY#{index}", 119 | amount: index * 100 + 0.12, 120 | weight: "WEIGHT#{index}", 121 | shipping_cost: index * 100 + 0.34 122 | }) 123 | end 124 | 125 | shared_examples_for "item serialization" do |index| 126 | it { expect(params).to include("itemId#{index}" => "ID#{index}") } 127 | it { expect(params).to include("itemDescription#{index}" => "DESC#{index}") } 128 | it { expect(params).to include("itemAmount#{index}" => "#{index}00.12") } 129 | it { expect(params).to include("itemShippingCost#{index}" => "#{index}00.34") } 130 | it { expect(params).to include("itemQuantity#{index}" => "QTY#{index}") } 131 | it { expect(params).to include("itemWeight#{index}" => "WEIGHT#{index}") } 132 | end 133 | 134 | before do 135 | payment_request.items << build_item(1) 136 | payment_request.items << build_item(2) 137 | end 138 | 139 | it_behaves_like "item serialization", 1 140 | it_behaves_like "item serialization", 2 141 | end 142 | end 143 | -------------------------------------------------------------------------------- /locales/pt-BR.yml: -------------------------------------------------------------------------------- 1 | pt-BR: 2 | pagseguro: 3 | errors: 4 | unauthorized: "Não autorizado." 5 | "10001": "O parâmetro email deve ser informado." 6 | "10002": "O parâmetro token deve ser informado." 7 | "10003": "O valor informado no parâmetro email é inválido." 8 | "11001": "O parâmetro receiverEmail deve ser informado." 9 | "11002": "O tamanho do valor informado no parâmetro receiverEmail é inválido." 10 | "11003": "O valor informado no parâmetro receiverEmail é inválido." 11 | "11004": "O parâmetro currency deve ser informado." 12 | "11005": "O valor informado no parâmetro currency é inválido." 13 | "11006": "O tamanho do valor informado no parâmetro redirectURL é inválido." 14 | "11007": "O valor informado no parâmetro redirectURL é inválido." 15 | "11008": "O tamanho do valor informado no parâmetro reference é inválido." 16 | "11009": "O tamanho do valor informado no parâmetro senderEmail é inválido." 17 | "11010": "O valor informado no parâmetro senderEmail é inválido." 18 | "11011": "O tamanho do valor informado no parâmetro senderName é inválido." 19 | "11012": "O valor informado no parâmetro senderName é inválido." 20 | "11013": "O valor informado no parâmetro senderAreaCode é inválido." 21 | "11014": "O valor informado no parâmetro senderPhone é inválido." 22 | "11015": "O parâmetro shippingType deve ser informado." 23 | "11016": "O tipo definido no parâmetro shippingType é inválido." 24 | "11017": "O valor informado no parâmetro shippingPostalCode é inválido." 25 | "11018": "O tamanho do valor informado no parâmetro shippingAddressStreet é inválido." 26 | "11019": "O tamanho do valor informado no parâmetro shippingAddressNumber é inválido." 27 | "11020": "O tamanho do valor informado no parâmetro shippingAddressComplement é inválido." 28 | "11021": "O tamanho do valor informado no parâmetro shippingAddressDistrict é inválido." 29 | "11022": "O tamanho do valor informado no parâmetro shippingAddressCity é inválido." 30 | "11023": "O formato do valor informado no parâmetro shippingAddressState é inválido." 31 | "11024": "O valor informado no parâmetro itemQuantity é inválido." 32 | "11025": "O parâmetro itemId deve ser informado." 33 | "11026": "O parâmetro itemQuantity deve ser informado." 34 | "11027": "O valor informado no parâmetro itemQuantity é inválido." 35 | "11028": "O parâmetro itemAmount deve ser informado." 36 | "11029": "O formato do valor informado no parâmetro itemAmount é inválido." 37 | "11030": "O valor informado no parâmetro itemAmount é inválido." 38 | "11031": "O formato do valor informado no parâmetro itemShippingCost é inválido." 39 | "11032": "O valor informado no parâmetro itemShippingCost é inválido." 40 | "11033": "O parâmetro itemDescription deve ser informado." 41 | "11034": "O tamanho do valor informado no parâmetro itemDescription é inválido." 42 | "11035": "O valor informado no parâmetro itemWeight é inválido." 43 | "11036": "O formato do valor informado no parâmetro extraAmount é inválido." 44 | "11037": "O valor informado no parâmetro extraAmount é inválido." 45 | "11038": "Valor não autorizado para receiverEmail." 46 | "11039": "A estrutura do xml está incorreta." 47 | "11040": "O formato do valor informado no parâmetro maxAge é inválido." 48 | "11041": "O valor informado no parâmetro maxAge é inválido." 49 | "11042": "O formato do valor informado no parâmetro maxUses é inválido." 50 | "11043": "O valor informado no parâmetro maxUses é inválido." 51 | "11044": "O parâmetro initialDate deve ser informado." 52 | "11045": "O valor informado no parâmetro initialDate deve ser inferior ou igual a data atual." 53 | "11046": "O valor informado no parâmetro initialDate não pode ser anterior a 180 dias contados da data atual." 54 | "11047": "O valor informado no parâmetro initialDate deve ser menor ou igual ao valor informado no parâmetro finalDate." 55 | "11048": "O intervalo entre os parâmetros initialDate e finalDate deve ser inferior ou igual a 30 dias." 56 | "11049": "O valor informado no parâmetro finalDate deve ser inferior ou igual a data atual." 57 | "11050": "O valor informado no parâmetro initialDate é inválido. O formato correto é yyyy-MM-ddTHH:mm." 58 | "11051": "O valor informado no parâmetro finalDate é inválido. O formato correto é yyyy-MM-ddTHH:mm." 59 | "11052": "O valor informado no parâmetro page é inválido. Utilize um inteiro maior que 0." 60 | "11053": "O valor informado no parâmetro maxPageResults deve ser entre 1 e 1000." 61 | "11105": "O valor total do checkout é inválido." 62 | "11112": "O formato do valor informado no parâmetro shippingCost é inválido." 63 | "11113": "O valor informado no parâmetro ShippingCost é inválido." 64 | "11157": "O valor informado no parâmetro senderCPF é inválido." 65 | "13001": "O código de notificação informado é inválido." 66 | "13002": "O código da transação deve ser informado." 67 | "13003": "O código de transação informado é inválido." 68 | "13004": "O parâmetro initialDate deve ser informado" 69 | "13005": "O valor informado no parâmetro initialDate deve ser inferior ou igual a data atual." 70 | "13006": "O valor informado no parâmetro initialDate não pode ser anterior a 180 dias contados da data atual." 71 | "13007": "O valor informado no parâmetro initialDate deve ser menor ou igual ao valor informado no parâmetro finalDate." 72 | "13008": "O intervalo entre os parâmetros initialDate e finalDate deve ser inferior ou igual a 30 dias." 73 | "13009": "O valor informado no parâmetro finalDate deve ser inferior ou igual a data atual." 74 | "13010": "O valor informado no parâmetro initialDate é inválido. O formato correto é yyyy-MM-ddTHH:mm." 75 | "13011": "O valor informado no parâmetro finalDate é inválido. O formato correto é yyyy-MM-ddTHH:mm." 76 | "13012": "O valor informado no parâmetro initialDate deve ser inferior a." 77 | "13013": "O valor informado no parâmetro page é inválido. Utilize um inteiro maior que 0." 78 | "13014": "O valor informado no parâmetro maxPageResults deve ser entre 1 e 1000." 79 | "13015": "Transação não encontrada." 80 | "13020": "O valor informado no parâmetro page é superior a quantidade de páginas retornadas." 81 | "13021": "Esta consulta não pode ser realizada pois o status da transação requisitada é inválido." 82 | 83 | payment_methods: 84 | "101": "Cartão de crédito Visa" 85 | "102": "Cartão de crédito MasterCard" 86 | "103": "Cartão de crédito American Express" 87 | "104": "Cartão de crédito Diners" 88 | "105": "Cartão de crédito Hipercard" 89 | "106": "Cartão de crédito Aura" 90 | "107": "Cartão de crédito Elo" 91 | "108": "Cartão de crédito PLENOCard" 92 | "109": "Cartão de crédito PersonalCard" 93 | "110": "Cartão de crédito JCB" 94 | "111": "Cartão de crédito Discover" 95 | "112": "Cartão de crédito BrasilCard" 96 | "113": "Cartão de crédito FORTBRASIL" 97 | "114": "Cartão de crédito CARDBAN" 98 | "115": "Cartão de crédito VALECARD" 99 | "116": "Cartão de crédito Cabal" 100 | "117": "Cartão de crédito Mais!" 101 | "118": "Cartão de crédito Avista" 102 | "119": "Cartão de crédito GRANDCARD" 103 | "201": "Boleto Bradesco" 104 | "202": "Boleto Santander" 105 | "301": "Débito online Bradesco" 106 | "302": "Débito online Itaú" 107 | "303": "Débito online Unibanco" 108 | "304": "Débito online Banco do Brasil" 109 | "305": "Débito online Banco Real" 110 | "306": "Débito online Banrisul" 111 | "307": "Débito online HSBC" 112 | "401": "Saldo PagSeguro" 113 | "501": "Oi Paggo" 114 | "701": "Depósito em conta Banco do Brasil" 115 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Biblioteca de integração PagSeguro em Ruby 2 | 3 | [![Build Status](https://travis-ci.org/pagseguro/ruby.png?branch=master)](https://travis-ci.org/pagseguro/ruby) 4 | 5 | [![Code Climate](https://codeclimate.com/github/pagseguro/ruby.png)](https://codeclimate.com/github/pagseguro/ruby) 6 | 7 | ## Descrição 8 | 9 | A biblioteca PagSeguro em Ruby é um conjunto de classes de domínio que facilitam, para o desenvolvedor Ruby, a utilização das funcionalidades que o PagSeguro oferece na forma de APIs. Com a biblioteca instalada e configurada, você pode facilmente integrar funcionalidades como: 10 | 11 | - Criar [requisições de pagamentos] 12 | - Consultar [transações por código] 13 | - Consultar [transações por intervalo de datas] 14 | - Consultar [transações abandonadas] 15 | - Receber [notificações] 16 | 17 | 18 | ## Requisitos 19 | 20 | - [Ruby] 1.9.3+ 21 | 22 | 23 | ## Instalação 24 | 25 | - Adicione a biblioteca ao seu Gemfile. 26 | 27 | ```ruby 28 | gem "pagseguro-oficial", git: "git://github.com/pagseguro/ruby.git" 29 | ``` 30 | 31 | - Execute o comando `bundle install`. 32 | 33 | ## Configuração 34 | 35 | Para fazer a autenticação, você precisará configurar as credenciais do PagSeguro. Crie o arquivo `config/initializers/pagseguro.rb` com o conteúdo abaixo. 36 | 37 | ```ruby 38 | PagSeguro.configure do |config| 39 | config.token = "seu token" 40 | config.email = "seu e-mail" 41 | end 42 | ``` 43 | 44 | O token de segurança está disponível em sua [conta do PagSeguro](https://pagseguro.uol.com.br/integracao/token-de-seguranca.jhtml). 45 | 46 | ## Pagamentos 47 | 48 | Para iniciar uma requisição de pagamento, você precisa instanciar a classe `PagSeguro::PaymentRequest`. Isso normalmente será feito em seu controller de checkout. 49 | 50 | ```ruby 51 | class CheckoutController < ApplicationController 52 | def create 53 | # O modo como você irá armazenar os produtos que estão sendo comprados 54 | # depende de você. Neste caso, temos um modelo Order que referência os 55 | # produtos que estão sendo comprados. 56 | order = Order.find(params[:id]) 57 | 58 | payment = PagSeguro::PaymentRequest.new 59 | payment.reference = order.id 60 | payment.notification_url = notifications_url 61 | payment.redirect_url = processing_url 62 | 63 | order.products.each do |product| 64 | payment.items << { 65 | id: product.id, 66 | description: product.title, 67 | amount: product.price, 68 | weight: product.weight 69 | } 70 | end 71 | 72 | response = payment.register 73 | 74 | # Caso o processo de checkout tenha dado errado, lança uma exceção. 75 | # Assim, um serviço de rastreamento de exceções ou até mesmo a gem 76 | # exception_notification poderá notificar sobre o ocorrido. 77 | # 78 | # Se estiver tudo certo, redireciona o comprador para o PagSeguro. 79 | if response.errors.any? 80 | raise response.errors.join("\n") 81 | else 82 | redirect_to response.url 83 | end 84 | end 85 | end 86 | ``` 87 | 88 | ## Notificações 89 | 90 | O PagSeguro irá notificar a URL informada no processo de checkout. Isso é feito através do método `PagSeguro::PaymentRequest#notification_url`. Esta URL irá receber o código da notificação e tipo de notificação. Com estas informações, podemos recuperar as informações detalhadas sobre o pagamento. 91 | 92 | ```ruby 93 | class NotificationsController < ApplicationController 94 | skip_before_filter :verify_authenticity_token, only: :create 95 | 96 | def create 97 | transaction = PagSeguro::Transaction.find_by_code(params[:notificationCode]) 98 | 99 | if transaction.errors.empty? 100 | # Processa a notificação. A melhor maneira de se fazer isso é realizar 101 | # o processamento em background. Uma boa alternativa para isso é a 102 | # biblioteca Sidekiq. 103 | end 104 | 105 | render nothing: true, status: 200 106 | end 107 | end 108 | ``` 109 | 110 | ## Consultas 111 | 112 | ### Transações abandonadas 113 | 114 | Para quantificar o número de transações abandonadas, você pode solicitar uma lista com histórico dessas transações. 115 | 116 | ```ruby 117 | report = PagSeguro::Transaction.find_abandoned 118 | 119 | while report.next_page? 120 | report.next_page! 121 | puts "=> Page #{report.page}" 122 | 123 | abort "=> Errors: #{report.errors.join("\n")}" unless report.valid? 124 | 125 | puts "=> Report was created at: #{report.created_at}" 126 | puts 127 | 128 | report.transactions.each do |transaction| 129 | puts "=> Abandoned transaction" 130 | puts " created at: #{transaction.created_at}" 131 | puts " code: #{transaction.code}" 132 | puts " type_id: #{transaction.type_id}" 133 | puts " gross amount: #{transaction.gross_amount}" 134 | puts 135 | end 136 | end 137 | ``` 138 | 139 | ### Histórico de transações 140 | 141 | Para facilitar seu controle financeiro e seu estoque, você pode solicitar uma lista com histórico das transações da sua loja. 142 | 143 | ```ruby 144 | report = PagSeguro::Transaction.find_by_date 145 | 146 | while report.next_page? 147 | report.next_page! 148 | puts "== Page #{report.page}" 149 | abort "=> Errors: #{report.errors.join("\n")}" unless report.valid? 150 | puts "Report created on #{report.created_at}" 151 | puts 152 | 153 | report.transactions.each do |transaction| 154 | puts "=> Transaction" 155 | puts " created_at: #{transaction.created_at}" 156 | puts " code: #{transaction.code}" 157 | puts " cancellation_source: #{transaction.cancellation_source}" 158 | puts " payment method: #{transaction.payment_method.type}" 159 | puts " gross amount: #{transaction.gross_amount}" 160 | puts " updated at: #{transaction.updated_at}" 161 | puts 162 | end 163 | end 164 | ``` 165 | 166 | ## API 167 | 168 | ### PagSeguro::PaymentRequest 169 | 170 | #### Definindo identificador do pedido 171 | 172 | ```ruby 173 | payment.reference = "ref1234" 174 | ``` 175 | 176 | #### Definindo informações de entrega 177 | 178 | ```ruby 179 | payment = PagSeguro::PaymentRequest.new 180 | payment.shipping = { 181 | type_name: "sedex", 182 | cost: 20.00, 183 | address: { 184 | street: "Av. Brig. Faria Lima", 185 | number: 1384, 186 | complement: "5 andar", 187 | district: "Jardim Paulistano", 188 | city: "São Paulo", 189 | state: "SP", 190 | postal_code: "01452002" 191 | } 192 | } 193 | ``` 194 | 195 | #### Alternativamente você pode definir uma instância da classe `PagSeguro::Shipping` 196 | 197 | ```ruby 198 | shipping = { 199 | type_name: "sedex", 200 | cost: 20.00, 201 | address: { 202 | street: "Av. Brig. Faria Lima", 203 | number: 1384, 204 | complement: "5 andar", 205 | district: "Jardim Paulistano", 206 | city: "São Paulo", 207 | state: "SP", 208 | postal_code: "01452002" 209 | } 210 | } 211 | 212 | payment.shipping = shipping 213 | ``` 214 | 215 | #### Definindo informações do comprador 216 | 217 | ```ruby 218 | payment.sender = { 219 | name: "John Doe", 220 | email: "john@example.org", 221 | cpf: "12345678901", 222 | phone: { 223 | area_code: "11", 224 | number: "123456789" 225 | } 226 | } 227 | ``` 228 | 229 | #### Definindo valores de acréscimo/desconto 230 | 231 | ```ruby 232 | payment.extra_amount = 123.45 # acréscimo 233 | payment.extra_amount = -123.45 # desconto 234 | ``` 235 | 236 | #### URLS 237 | 238 | ```ruby 239 | # URL de notificação 240 | payment.notification_url = "http://example.org/notifications" 241 | 242 | # URL de retorno 243 | payment.return_url = "http://example.org/processando" 244 | ``` 245 | 246 | #### Definindo tempo de vida do código de pagamento 247 | 248 | ```ruby 249 | payment.max_uses = 100 250 | payment.max_age = 3600 # em segundos 251 | ``` 252 | 253 | ## Changelog 254 | 255 | 2.0.2 256 | 257 | - Atualização dos tipos e códigos de meio de pagamento. 258 | - Correção do exemplo payment_request. 259 | 260 | 2.0.1 261 | 262 | - Classes de domínios que representam pagamentos, notificações e transações. 263 | - Criação de checkouts via API. 264 | - Tratamento de notificações de pagamento enviadas pelo PagSeguro. 265 | - Consulta de transações. 266 | 267 | ## Licença 268 | 269 | Copyright 2013 PagSeguro Internet LTDA. 270 | 271 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 272 | 273 | http://www.apache.org/licenses/LICENSE-2.0 274 | 275 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 276 | 277 | ## Notas 278 | - O PagSeguro somente aceita pagamento utilizando a moeda Real brasileiro (BRL). 279 | - Certifique-se que o email e o token informados estejam relacionados a uma conta que possua o perfil de vendedor ou empresarial. 280 | - Certifique-se que tenha definido corretamente o charset de acordo com a codificação (ISO-8859-1 ou UTF-8) do seu sistema. Isso irá prevenir que as transações gerem possíveis erros ou quebras ou ainda que caracteres especiais possam ser apresentados de maneira diferente do habitual. 281 | - Para que ocorra normalmente a geração de logs, certifique-se que o diretório e o arquivo de log tenham permissões de leitura e escrita. 282 | 283 | ## [Dúvidas?] 284 | 285 | 286 | ## Contribuições 287 | 288 | Achou e corrigiu um bug ou tem alguma feature em mente e deseja contribuir? 289 | 290 | * Faça um fork. 291 | * Adicione sua feature ou correção de bug. 292 | * Envie um pull request no [GitHub]. 293 | 294 | 295 | [requisições de pagamentos]: https://pagseguro.uol.com.br/v2/guia-de-integracao/api-de-pagamentos.html 296 | [notificações]: https://pagseguro.uol.com.br/v2/guia-de-integracao/api-de-notificacoes.html 297 | [transações por código]: https://pagseguro.uol.com.br/v2/guia-de-integracao/consulta-de-transacoes-por-codigo.html 298 | [transações por intervalo de datas]: https://pagseguro.uol.com.br/v2/guia-de-integracao/consulta-de-transacoes-por-intervalo-de-datas.html 299 | [transações abandonadas]: https://pagseguro.uol.com.br/v2/guia-de-integracao/consulta-de-transacoes-abandonadas.html 300 | [Dúvidas?]: https://pagseguro.uol.com.br/desenvolvedor/comunidade.jhtml 301 | [Ruby]: http://www.ruby-lang.org/pt/ 302 | [GitHub]: https://github.com/pagseguro/ruby/ 303 | -------------------------------------------------------------------------------- /LICENSE-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | --------------------------------------------------------------------------------