pag_seguro 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ coverage/*
6
+ spec/pag_seguro/integration/config.yml
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in PagSeguro.gemspec
4
+ gemspec
5
+
6
+ # test gems
7
+ gem 'rspec'
8
+ gem 'simplecov', require: false
9
+ gem 'guard-rspec'
10
+ gem 'growl'
data/Guardfile ADDED
@@ -0,0 +1,7 @@
1
+ guard 'rspec', version: 2 do
2
+ watch(%r{^spec/.+_spec\.rb})
3
+ watch(%r{^lib/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ watch('lib/pag_seguro.rb') { "spec" }
6
+ watch('lib/checkout.xml.haml') { "spec/pag_seguro/checkout_xml_spec.rb" }
7
+ end
data/README.markdown ADDED
@@ -0,0 +1,9 @@
1
+ Esta gem foi criada com o intuito de facilitar o uso da versão 2 das APIs do PagSeguro, como pagamento e notificações.
2
+
3
+ Para exemplos de uso, olhe spec/pag_seguro/integration/checkout_spec.rb
4
+ Para rodar os testes de integração, é necessário alterar os arquivo spec/pag_seguro/integration/config.yml com seu e-mail e token cadastrados no PagSeguro
5
+
6
+ Desenvolvida por Stefano Diem Benatti e Thiago Colucci (stefano@heavenstudio.com.br, ticolucci@gmail.com)
7
+
8
+ Obrigado à:
9
+ - Nando Vieira pela gem pagseguro que usei por bastante tempo
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,59 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2
+ %checkout
3
+ - if payment.id.present?
4
+ %reference= payment.id
5
+ - if payment.extra_amount.present?
6
+ %extraAmount= payment.extra_amount
7
+ - if payment.redirect_url.present?
8
+ %redirectUrl= payment.redirect_url
9
+ - if payment.max_uses.present?
10
+ %maxUses= payment.max_uses
11
+ - if payment. max_age.present?
12
+ %maxAge= payment.max_age
13
+
14
+ %currency BRL
15
+
16
+ %items
17
+ - items.each do |item|
18
+ %item
19
+ %id= item.id
20
+ %description= item.description
21
+ %amount= item.amount
22
+ %quantity= item.quantity
23
+ - if item.shipping_cost.present?
24
+ %shippingCost= item.shipping_cost
25
+ - if item.weight.present?
26
+ %weight= item.weight
27
+
28
+ - if sender.present?
29
+ %sender
30
+ - if sender.name.present?
31
+ %name= sender.name
32
+ - if sender.email.present?
33
+ %email= sender.email
34
+ %phone
35
+ - if sender.phone_ddd.present?
36
+ %areaCode= sender.phone_ddd
37
+ - if sender.phone_number.present?
38
+ %number= sender.phone_number
39
+
40
+ - if shipping.present?
41
+ %shipping
42
+ - if shipping.type.present?
43
+ %type= shipping.type
44
+ %address
45
+ %country BRA
46
+ - if shipping.state.present?
47
+ %state= shipping.state
48
+ - if shipping.city.present?
49
+ %city= shipping.city
50
+ - if shipping.postal_code.present?
51
+ %postalCode= shipping.postal_code
52
+ - if shipping.district.present?
53
+ %district= shipping.district
54
+ - if shipping.street.present?
55
+ %street= shipping.street
56
+ - if shipping.number.present?
57
+ %number= shipping.number
58
+ - if shipping.complement.present?
59
+ %complement= shipping.complement
@@ -0,0 +1,12 @@
1
+ module PagSeguro
2
+ module Errors
3
+ class InvalidData < Exception
4
+ def initialize(response_xml)
5
+ err_msg = Nokogiri::XML(response_xml).css("errors error").inject("") do |acc, node|
6
+ acc + "#{node.css('code').first.content}: #{node.css('message').first.content}\n"
7
+ end
8
+ super(err_msg)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ module PagSeguro
2
+ module Errors
3
+ class Unauthorized < Exception
4
+ def initialize
5
+ super("Credentials provided (e-mail and token) failed to authenticate")
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module PagSeguro
2
+ module Errors
3
+ class UnknownError
4
+ def initialize(response)
5
+ super("Unknown response code (#{response.code}):\n#{reponse.body}")
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,31 @@
1
+ module PagSeguro
2
+ class Item
3
+ include ActiveModel::Validations
4
+
5
+ attr_accessor :id, :description, :amount, :quantity, :shipping_cost, :weight
6
+
7
+ validates_presence_of :id, :description, :amount, :quantity
8
+ validates_format_of :amount, with: /^\d+\.\d{2}$/, message: " must be a decimal and have 2 digits after the dot"
9
+ validates_format_of :shipping_cost, with: /^\d+\.\d{2}$/, message: " must be a decimal and have 2 digits after the dot"
10
+ validates_format_of :weight, with: /^\d+$/, message: " must be an integer"
11
+ validate :quantity_amount
12
+
13
+ def initialize(attributes = {})
14
+ @id = attributes[:id]
15
+ @description = attributes[:description]
16
+ @amount = attributes[:amount]
17
+ @quantity = attributes[:quantity]
18
+ @shipping_cost = attributes[:shipping_cost]
19
+ @weight = attributes[:weight]
20
+ end
21
+
22
+ def description
23
+ @description.present? && @description.size > 100 ? @description[0..99] : @description
24
+ end
25
+
26
+ protected
27
+ def quantity_amount
28
+ errors.add(:quantity, " must be a number between 1 and 999") if @quantity.present? && (@quantity == "0" || @quantity.to_s !~ /^\d{1,3}$/)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,79 @@
1
+ require 'net/https'
2
+
3
+ module PagSeguro
4
+ class Notification
5
+ attr_accessor :data
6
+ PAGSEGURO_APPROVED = 3
7
+ PAGSEGURO_AVAILABLE = 4
8
+
9
+ def initialize(email = nil, token = nil, notification_code=nil)
10
+ raise "Needs a notification code" if notification_code.blank?
11
+ raise "Needs an email" if email.blank?
12
+ raise "Needs a token" if token.blank?
13
+ @data = Nokogiri::XML(notification_data(email, token, notification_code))
14
+ end
15
+
16
+ def status
17
+ @data.css("status").first.content.to_i
18
+ end
19
+
20
+ def approved?
21
+ PAGSEGURO_APPROVED == status
22
+ end
23
+
24
+ def available?
25
+ PAGSEGURO_AVAILABLE == status
26
+ end
27
+
28
+ def id
29
+ @data.css("reference").first.content
30
+ end
31
+
32
+ def transaction_id
33
+ @data.css("code").first.content
34
+ end
35
+
36
+ def items
37
+ @data.css("items item").map do |i|
38
+ Item.new(id: parse_item(i, "id"), description: parse_item(i, "description"), quantity: parse_item(i, "quantity"), amount: parse_item(i, "amount"))
39
+ end
40
+ end
41
+
42
+ def sender
43
+ sn = Sender.new
44
+ sn.name = parse_css("sender name")
45
+ sn.email = parse_css("sender email")
46
+ sn.phone_ddd = parse_css("sender phone areaCode")
47
+ sn.phone_number = parse_css("sender phone number")
48
+ sn
49
+ end
50
+
51
+ def shipping
52
+ sh = Shipping.new
53
+ sh.type = parse_css("shipping type")
54
+ sh.cost = parse_css("shipping cost")
55
+ sh.state = parse_css("shipping address state")
56
+ sh.city = parse_css("shipping address city")
57
+ sh.postal_code = parse_css("shipping address postalCode")
58
+ sh.district = parse_css("shipping address district")
59
+ sh.street = parse_css("shipping address street")
60
+ sh.number = parse_css("shipping address number")
61
+ sh.complement = parse_css("shipping address complement")
62
+ sh
63
+ end
64
+
65
+ private
66
+ def notification_data(email, token, notification_code)
67
+ RestClient.get("https://ws.pagseguro.uol.com.br/v2/transactions/notifications/#{notification_code}?email=#{email}&token=#{token}")
68
+ end
69
+
70
+ def parse_item(data, attribute)
71
+ data.css(attribute).first.content
72
+ end
73
+
74
+ def parse_css(selector)
75
+ value = @data.css(selector).first
76
+ value.nil? ? nil : value.content
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,64 @@
1
+ module PagSeguro
2
+ class Payment
3
+ include ActiveModel::Validations
4
+
5
+ attr_accessor :id, :email, :token, :items, :sender, :shipping, :extra_amount, :redirect_url, :max_uses, :max_age
6
+
7
+ validates_presence_of :email, :token
8
+ validates_format_of :extra_amount, with: /^\d+\.\d{2}$/, message: " must be a decimal and have 2 digits after the dot", allow_blank: true
9
+ validates_format_of :redirect_url, with: URI::regexp(%w(http https)), message: " must give a correct url for redirection", allow_blank: true
10
+ validate :max_uses_number, :max_age_number
11
+
12
+ def initialize(email = nil, token = nil, options = {})
13
+ @email = email unless email.nil?
14
+ @token = token unless token.nil?
15
+ @id = options[:id]
16
+ @sender = options[:sender] || Sender.new
17
+ @shipping = options[:shipping] || Shipping.new
18
+ @items = options[:items] || []
19
+ @extra_amount = options[:extra_amount]
20
+ @redirect_url = options[:redirect_url]
21
+ @max_uses = options[:max_uses]
22
+ @max_age = options[:max_age]
23
+ end
24
+
25
+ def checkout_xml
26
+ xml_content = File.open( File.dirname(__FILE__) + "/checkout.xml.haml" ).read
27
+ Haml::Engine.new(xml_content).render(nil, items: @items, payment: self, sender: @sender, shipping: @shipping)
28
+ end
29
+
30
+ def checkout_url_with_params
31
+ "https://ws.pagseguro.uol.com.br/v2/checkout?email=#{@email}&token=#{@token}"
32
+ end
33
+
34
+ def checkout_payment_url
35
+ "https://pagseguro.uol.com.br/v2/checkout/payment.html?code=#{code}"
36
+ end
37
+
38
+ def code
39
+ response = send_checkout
40
+ if response.code == 200
41
+ Nokogiri::XML(response.body).css("checkout code").first.content
42
+ elsif response.code == 401
43
+ raise Errors::Unauthorized
44
+ elsif response.code == 400
45
+ raise Errors::InvalidData.new(response.body)
46
+ else
47
+ raise Errors::UnknownError.new(response)
48
+ end
49
+ end
50
+
51
+ protected
52
+ def max_uses_number
53
+ errors.add(:max_uses, " must be an integer greater than 0") if @max_uses.present? && @max_uses.to_i <= 0
54
+ end
55
+
56
+ def max_age_number
57
+ errors.add(:max_age, " must be an integer grater or equal to 30") if @max_age.present? && @max_age.to_i < 30
58
+ end
59
+
60
+ def send_checkout
61
+ RestClient.post(checkout_url_with_params, checkout_xml, content_type: "application/xml"){|response, request, result| response }
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,32 @@
1
+ module PagSeguro
2
+ class Sender
3
+ attr_accessor :name, :email, :phone_ddd, :phone_number
4
+
5
+ def initialize(options = {})
6
+ @name = options[:name]
7
+ @email = options[:email]
8
+ @phone_ddd = options[:phone_ddd]
9
+ @phone_number = options[:phone_number]
10
+ end
11
+
12
+ def email
13
+ valid_email? ? @email : nil
14
+ end
15
+
16
+ def valid_email?
17
+ @email =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i && @email.size <= 60
18
+ end
19
+
20
+ def name
21
+ ( @name.present? && @name.size > 50 ) ? @name[0..49] : @name
22
+ end
23
+
24
+ def phone_ddd
25
+ @phone_ddd if @phone_ddd =~ /^\d{2}$/
26
+ end
27
+
28
+ def phone_number
29
+ @phone_number if @phone_number =~/^\d{8}$/
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,29 @@
1
+ module PagSeguro
2
+ class Shipping
3
+ include ActiveModel::Validations
4
+
5
+ PAC = 1
6
+ SEDEX = 2
7
+ NOT_IDENTIFIED = 3
8
+
9
+ validates_format_of :postal_code, with: /^\d{8}$/, message: " must be an integer with 8 digits", allow_blank: true
10
+
11
+ attr_accessor :type, :state, :city, :postal_code, :district, :street, :number, :complement, :cost
12
+
13
+ def initialize(attributes = {})
14
+ @type = attributes[:type]
15
+ @state = attributes[:state]
16
+ @city = attributes[:city]
17
+ @postal_code = attributes[:postal_code]
18
+ @district = attributes[:district]
19
+ @street = attributes[:street]
20
+ @number = attributes[:number]
21
+ @complement = attributes[:complement]
22
+ @cost = attributes[:cost]
23
+ end
24
+
25
+ def postal_code
26
+ @postal_code if @postal_code.present? && @postal_code.to_s.size == 8
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module PagSeguro
2
+ VERSION = "0.1.0"
3
+ end
data/lib/pag_seguro.rb ADDED
@@ -0,0 +1,25 @@
1
+ $: << "lib/pag_seguro"
2
+
3
+ # Third party gems
4
+ require "active_model"
5
+ require "nokogiri"
6
+ require "haml"
7
+ require "rest-client"
8
+
9
+ # PagSeguro classes
10
+ require "item"
11
+ require "payment"
12
+ require "sender"
13
+ require "shipping"
14
+ require "notification"
15
+
16
+ # Error classes
17
+ require "errors/unauthorized"
18
+ require "errors/invalid_data"
19
+ require "errors/unknown_error"
20
+
21
+ # Version
22
+ require "version"
23
+
24
+ module PagSeguro
25
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "pag_seguro/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.platform = Gem::Platform::RUBY
7
+ s.name = "pag_seguro"
8
+ s.version = PagSeguro::VERSION
9
+ s.authors = ["Stefano Diem Benatti"]
10
+ s.email = ["stefano.diem@gmail.com"]
11
+ s.homepage = "http://github.com/heavenstudio/pag_seguro"
12
+ s.summary = %q{A ruby gem to handle PagSeguro's API version 2}
13
+ s.required_ruby_version = '>= 1.9.2'
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+ s.has_rdoc = false
20
+
21
+ s.add_dependency('activemodel')
22
+ s.add_dependency('haml')
23
+ s.add_dependency('nokogiri')
24
+ s.add_dependency('rest-client', '~> 1.6.7')
25
+ end
@@ -0,0 +1,176 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ items = [
5
+ PagSeguro::Item.new(id: 25, description: "A Bic Pen", amount: "1.50", quantity: "4", shipping_cost: "1.00", weight: 10),
6
+ PagSeguro::Item.new(id: 73, description: "A Book", amount: "38.23", quantity: "1", shipping_cost: "12.00", weight: 300),
7
+ PagSeguro::Item.new(id: 95, description: "A Towel", amount: "69.35", quantity: "2", weight: 400),
8
+ PagSeguro::Item.new(id: 17, description: "A pipe", amount: "3.00", quantity: "89")
9
+ ]
10
+
11
+ sender_info = {name: "Stefano Diem Benatti", email: "stefano@heavenstudio.com.br", phone_ddd: "11", phone_number: "93430994"}
12
+
13
+ shipping_info = {type: PagSeguro::Shipping::SEDEX, state: "SP", city: "São Paulo", postal_code: "05363000",
14
+ district: "Jd. PoliPoli", street: "Av. Otacilio Tomanik", number: "775", complement: "apto. 92"}
15
+
16
+
17
+ describe PagSeguro::Payment do
18
+ context "checkout_xml" do
19
+ before { @payment = PagSeguro::Payment.new }
20
+
21
+ it "should be a valid xml" do
22
+ lambda { Nokogiri::XML(@payment.checkout_xml) { |config| config.options = Nokogiri::XML::ParseOptions::STRICT } }.should_not raise_error
23
+ end
24
+
25
+ it "should have encoding UTF-8" do
26
+ @payment.checkout_xml.should match(/^<\?xml.+encoding="UTF-8".+\?>$/)
27
+ end
28
+
29
+ it "should have currency BRL" do
30
+ Nokogiri::XML(@payment.checkout_xml).css("checkout currency").first.content.should == "BRL"
31
+ end
32
+
33
+ context "items" do
34
+ before do
35
+ @payment.items = items
36
+ @xml = Nokogiri::XML(@payment.checkout_xml)
37
+ end
38
+
39
+ it "should have 4 items" do
40
+ @xml.css("checkout items item").size.should == 4
41
+ end
42
+
43
+ it "should show all ids" do
44
+ @xml.css("checkout items item id").map(&:content).should == ["25","73","95","17"]
45
+ end
46
+
47
+ it "should show all descriptions" do
48
+ @xml.css("checkout items item description").map(&:content).should == ["A Bic Pen","A Book","A Towel","A pipe"]
49
+ end
50
+
51
+ it "should show all amounts" do
52
+ @xml.css("checkout items item amount").map(&:content).should == ["1.50","38.23","69.35","3.00"]
53
+ end
54
+
55
+ it "should show all quantities" do
56
+ @xml.css("checkout items item quantity").map(&:content).should == ["4","1","2","89"]
57
+ end
58
+
59
+ it "should show all shipping_costs" do
60
+ @xml.css("checkout items item shippingCost").map(&:content).should == ["1.00","12.00"]
61
+ end
62
+
63
+ it "should show all weights" do
64
+ @xml.css("checkout items item weight").map(&:content).should == ["10","300","400"]
65
+ end
66
+ end
67
+
68
+ context "sender info" do
69
+ before do
70
+ @xml_without_sender_info = Nokogiri::XML(@payment.checkout_xml)
71
+ @payment.sender = PagSeguro::Sender.new(sender_info)
72
+ @xml = Nokogiri::XML(@payment.checkout_xml)
73
+ end
74
+
75
+ it "should have sender name" do
76
+ @xml_without_sender_info.css("checkout sender name").should be_empty
77
+ @xml.css("checkout sender name").first.content.should == "Stefano Diem Benatti"
78
+ end
79
+
80
+ it "should have sender email" do
81
+ @xml_without_sender_info.css("checkout sender email").should be_empty
82
+ @xml.css("checkout sender email").first.content.should == "stefano@heavenstudio.com.br"
83
+ end
84
+
85
+ it "should have sender phone ddd" do
86
+ @xml_without_sender_info.css("checkout sender phone areaCode").should be_empty
87
+ @xml.css("checkout sender phone areaCode").first.content.should == "11"
88
+ end
89
+
90
+ it "should have sender phone number" do
91
+ @xml_without_sender_info.css("checkout sender phone number").should be_empty
92
+ @xml.css("checkout sender phone number").first.content.should == "93430994"
93
+ end
94
+ end
95
+
96
+ context "shipping" do
97
+ before do
98
+ @xml_without_shipping_info = Nokogiri::XML(@payment.checkout_xml)
99
+ @payment.shipping = PagSeguro::Shipping.new(shipping_info)
100
+ @xml = Nokogiri::XML(@payment.checkout_xml)
101
+ end
102
+
103
+ it "should have shipping type" do
104
+ @xml_without_shipping_info.css("checkout shipping type").should be_empty
105
+ @xml.css("checkout shipping type").first.content.to_i.should == PagSeguro::Shipping::SEDEX
106
+ end
107
+
108
+ it "should have state" do
109
+ @xml_without_shipping_info.css("checkout shipping address state").should be_empty
110
+ @xml.css("checkout shipping address state").first.content.should == "SP"
111
+ end
112
+
113
+ it "should have city" do
114
+ @xml_without_shipping_info.css("checkout shipping address city").should be_empty
115
+ @xml.css("checkout shipping address city").first.content.should == "São Paulo"
116
+ end
117
+
118
+ it "should have posta code" do
119
+ @xml_without_shipping_info.css("checkout shipping address postalCode").should be_empty
120
+ @xml.css("checkout shipping address postalCode").first.content.should == "05363000"
121
+ end
122
+
123
+ it "should have district" do
124
+ @xml_without_shipping_info.css("checkout shipping address district").should be_empty
125
+ @xml.css("checkout shipping address district").first.content.should == "Jd. PoliPoli"
126
+ end
127
+
128
+ it "should have street" do
129
+ @xml_without_shipping_info.css("checkout shipping address street").should be_empty
130
+ @xml.css("checkout shipping address street").first.content.should == "Av. Otacilio Tomanik"
131
+ end
132
+
133
+ it "should have number" do
134
+ @xml_without_shipping_info.css("checkout shipping address number").should be_empty
135
+ @xml.css("checkout shipping address number").first.content.should == "775"
136
+ end
137
+
138
+ it "should have complement" do
139
+ @xml_without_shipping_info.css("checkout shipping address complement").should be_empty
140
+ @xml.css("checkout shipping address complement").first.content.should == "apto. 92"
141
+ end
142
+ end
143
+
144
+ context "payment settings" do
145
+ it "should not show id unless specified" do
146
+ Nokogiri::XML(@payment.checkout_xml).css("checkout reference").should be_empty
147
+ @payment.id = 305
148
+ Nokogiri::XML(@payment.checkout_xml).css("checkout reference").first.content.should == "305"
149
+ end
150
+
151
+ it "should not show extra amount unless specified" do
152
+ Nokogiri::XML(@payment.checkout_xml).css("checkout extraAmount").should be_empty
153
+ @payment.extra_amount = "10.50"
154
+ Nokogiri::XML(@payment.checkout_xml).css("checkout extraAmount").first.content.should == "10.50"
155
+ end
156
+
157
+ it "should not show redirect url unless specified" do
158
+ Nokogiri::XML(@payment.checkout_xml).css("checkout redirectUrl").should be_empty
159
+ @payment.redirect_url = "http://heavenstudio.com.br"
160
+ Nokogiri::XML(@payment.checkout_xml).css("checkout redirectUrl").first.content.should == "http://heavenstudio.com.br"
161
+ end
162
+
163
+ it "should not show max uses unless specified" do
164
+ Nokogiri::XML(@payment.checkout_xml).css("checkout maxUses").should be_empty
165
+ @payment.max_uses = "10"
166
+ Nokogiri::XML(@payment.checkout_xml).css("checkout maxUses").first.content.should == "10"
167
+ end
168
+
169
+ it "should not show max age unless specified" do
170
+ Nokogiri::XML(@payment.checkout_xml).css("checkout maxAge").should be_empty
171
+ @payment.max_age = "5000"
172
+ Nokogiri::XML(@payment.checkout_xml).css("checkout maxAge").first.content.should == "5000"
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ config = YAML.load_file(File.dirname(__FILE__) + "/config.yml")
5
+ EMAIL = config["email"]
6
+ TOKEN = config["token"]
7
+
8
+ def create_valid_payment
9
+ payment = PagSeguro::Payment.new(EMAIL, TOKEN)
10
+ payment.items = [
11
+ PagSeguro::Item.new(id: 25, description: "A Bic Pen", amount: "1.50", quantity: "4", shipping_cost: "1.00", weight: 10),
12
+ PagSeguro::Item.new(id: 73, description: "A Book", amount: "38.23", quantity: "1", shipping_cost: "12.00", weight: 300),
13
+ PagSeguro::Item.new(id: 95, description: "A Towel", amount: "69.35", quantity: "2", weight: 400),
14
+ PagSeguro::Item.new(id: 17, description: "A pipe", amount: "3.00", quantity: "89")
15
+ ]
16
+ payment.sender = PagSeguro::Sender.new(name: "Stefano Diem Benatti", email: "stefano@heavenstudio.com.br", phone_ddd: "11", phone_number: "93430994")
17
+ payment.shipping = PagSeguro::Shipping.new(type: PagSeguro::Shipping::SEDEX, state: "SP", city: "São Paulo", postal_code: "05363000", district: "Jd. PoliPoli", street: "Av. Otacilio Tomanik", number: "775", complement: "apto. 92")
18
+ payment
19
+ end
20
+
21
+ describe "PagSeguro::Payment.code" do
22
+ it "should send a request to pagseguro" do
23
+ payment = create_valid_payment
24
+ payment.code.size.should == 32
25
+ end
26
+
27
+ it "should tell me when the email and token are invalid" do
28
+ payment = PagSeguro::Payment.new("not_a_user@not_an_email.com", "NOTATOKEN7F048A09A8AEFDD1E5A7B91")
29
+ lambda { payment.code }.should raise_error(PagSeguro::Errors::Unauthorized)
30
+ end
31
+
32
+ it "should list errors given by pagseguro" do
33
+ payment = PagSeguro::Payment.new(EMAIL, TOKEN)
34
+ lambda { payment.code }.should raise_error(PagSeguro::Errors::InvalidData)
35
+ end
36
+
37
+ it "should give a response code of 200 for the user pagseguro url" do
38
+ payment = create_valid_payment
39
+ RestClient.get(payment.checkout_payment_url).code.should == 200
40
+ end
41
+ end
@@ -0,0 +1,2 @@
1
+ email: billing@heavenstudio.com.br
2
+ token: 97392F4047F048A09A8AEFDD1E5A7B91
@@ -0,0 +1,79 @@
1
+ require "spec_helper"
2
+
3
+ valid_attributes = {
4
+ id: 1,
5
+ description: "descrevendo um item",
6
+ amount: "100.50",
7
+ quantity: 1,
8
+ shipping_cost: "10.50",
9
+ weight: 300
10
+ }
11
+
12
+ describe PagSeguro::Item do
13
+ context "instance" do
14
+ before { @item = PagSeguro::Item.new }
15
+
16
+ it { @item.should have_attribute_accessor(:id) }
17
+ it { @item.should have_attribute_accessor(:description) }
18
+ it { @item.should have_attribute_accessor(:amount) }
19
+ it { @item.should have_attribute_accessor(:quantity) }
20
+ it { @item.should have_attribute_accessor(:shipping_cost) }
21
+ it { @item.should have_attribute_accessor(:weight) }
22
+
23
+ it "should be valid with valid attributes" do
24
+ PagSeguro::Item.new(valid_attributes).should be_valid
25
+ end
26
+
27
+ it "should not be valid without an id" do
28
+ PagSeguro::Item.new(valid_attributes.except(:id)).should_not be_valid
29
+ end
30
+
31
+ it "should not be valid without a description" do
32
+ PagSeguro::Item.new(valid_attributes.except(:description)).should_not be_valid
33
+ end
34
+
35
+ it "should trim description to 100 characters if it has more than 100 characters" do
36
+ item = PagSeguro::Item.new(valid_attributes)
37
+ item.description = "-" * 100
38
+ item.description.size.should == 100
39
+ item.should be_valid
40
+ item.description = "-" * 101
41
+ item.description.size.should == 100
42
+ item.should be_valid
43
+ end
44
+
45
+ it "should not be valid without an amount" do
46
+ PagSeguro::Item.new(valid_attributes.except(:amount)).should_not be_valid
47
+ end
48
+
49
+ it "should not allow invalid amount formats" do
50
+ PagSeguro::Item.new(valid_attributes.merge(amount: "10")).should_not be_valid
51
+ PagSeguro::Item.new(valid_attributes.merge(amount: "10,50")).should_not be_valid
52
+ PagSeguro::Item.new(valid_attributes.merge(amount: "R$ 10.50")).should_not be_valid
53
+ PagSeguro::Item.new(valid_attributes.merge(amount: "-10.50")).should_not be_valid
54
+ end
55
+
56
+ it "should not be valid without a quantity" do
57
+ PagSeguro::Item.new(valid_attributes.except(:quantity)).should_not be_valid
58
+ end
59
+
60
+ it "should not allow invalid quantities" do
61
+ PagSeguro::Item.new(valid_attributes.merge(quantity: "1000")).should_not be_valid
62
+ PagSeguro::Item.new(valid_attributes.merge(quantity: "0")).should_not be_valid
63
+ PagSeguro::Item.new(valid_attributes.merge(quantity: "-1")).should_not be_valid
64
+ end
65
+
66
+ it "should not allow invalid shipping_cost formats" do
67
+ PagSeguro::Item.new(valid_attributes.merge(shipping_cost: "10")).should_not be_valid
68
+ PagSeguro::Item.new(valid_attributes.merge(shipping_cost: "10,50")).should_not be_valid
69
+ PagSeguro::Item.new(valid_attributes.merge(shipping_cost: "R$ 10.50")).should_not be_valid
70
+ PagSeguro::Item.new(valid_attributes.merge(shipping_cost: "-10.50")).should_not be_valid
71
+ end
72
+
73
+ it "should not allow non integer values for weight" do
74
+ PagSeguro::Item.new(valid_attributes.merge(weight: "-10")).should_not be_valid
75
+ PagSeguro::Item.new(valid_attributes.merge(weight: "10.5")).should_not be_valid
76
+ PagSeguro::Item.new(valid_attributes.merge(weight: "10,5")).should_not be_valid
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,64 @@
1
+ # encoding: utf-8
2
+ module PagSeguro
3
+ class Notification
4
+ def notification_data(email, token, notification_code)
5
+ <<-XML
6
+ <?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
7
+ <transaction>
8
+ <date>2011-02-10T16:13:41.000-03:00</date>
9
+ <code>9E884542-81B3-4419-9A75-BCC6FB495EF1</code>
10
+ <reference>REF1234</reference>
11
+ <type>1</type>
12
+ <status>3</status>
13
+ <paymentMethod>
14
+ <type>1</type>
15
+ <code>101</code>
16
+ </paymentMethod>
17
+ <grossAmount>49900.00</grossAmount>
18
+ <discountAmount>0.00</discountAmount>
19
+ <feeAmount>0.00</feeAmount>
20
+ <netAmount>49900.00</netAmount>
21
+ <extraAmount>0.00</extraAmount>
22
+ <installmentCount>1</installmentCount>
23
+ <itemCount>2</itemCount>
24
+ <items>
25
+ <item>
26
+ <id>0001</id>
27
+ <description>Notebook Prata</description>
28
+ <quantity>1</quantity>
29
+ <amount>24300.00</amount>
30
+ </item>
31
+ <item>
32
+ <id>0002</id>
33
+ <description>Notebook Rosa</description>
34
+ <quantity>1</quantity>
35
+ <amount>25600.00</amount>
36
+ </item>
37
+ </items>
38
+ <sender>
39
+ <name>José Comprador</name>
40
+ <email>comprador@uol.com.br</email>
41
+ <phone>
42
+ <areaCode>11</areaCode>
43
+ <number>56273440</number>
44
+ </phone>
45
+ </sender>
46
+ <shipping>
47
+ <address>
48
+ <street>Av. Brig. Faria Lima</street>
49
+ <number>1384</number>
50
+ <complement>5o andar</complement>
51
+ <district>Jardim Paulistano</district>
52
+ <postalCode>01452002</postalCode>
53
+ <city>Sao Paulo</city>
54
+ <state>SP</state>
55
+ <country>BRA</country>
56
+ </address>
57
+ <type>1</type>
58
+ <cost>21.50</cost>
59
+ </shipping>
60
+ </transaction>
61
+ XML
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require_relative 'notification_data_mock'
5
+
6
+ describe PagSeguro::Notification do
7
+ before do
8
+ @notification = PagSeguro::Notification.new("mail", "token", "not_code")
9
+ end
10
+
11
+ it "should have an id" do
12
+ @notification.id.should == "REF1234"
13
+ end
14
+
15
+ it "should have a transaction id" do
16
+ @notification.transaction_id.should == "9E884542-81B3-4419-9A75-BCC6FB495EF1"
17
+ end
18
+
19
+ it "should be approved in this case" do
20
+ @notification.should be_approved
21
+ end
22
+
23
+ it "should have a sender" do
24
+ @sender = @notification.sender
25
+ @sender.name.should == "José Comprador"
26
+ @sender.email.should == "comprador@uol.com.br"
27
+ @sender.phone_ddd.should == "11"
28
+ @sender.phone_number == "56273440"
29
+ end
30
+
31
+ it "should have a shipping" do
32
+ @shipping = @notification.shipping
33
+ @shipping.type.should == "1"
34
+ @shipping.cost.should == "21.50"
35
+ @shipping.state.should == "SP"
36
+ @shipping.city.should == "Sao Paulo"
37
+ @shipping.postal_code.should == "01452002"
38
+ @shipping.district.should == "Jardim Paulistano"
39
+ @shipping.street.should == "Av. Brig. Faria Lima"
40
+ @shipping.number.should == "1384"
41
+ @shipping.complement.should == "5o andar"
42
+ end
43
+
44
+ it "should have items" do
45
+ @items = @notification.items
46
+ @items.size.should == 2
47
+
48
+ @items[0].id.should == "0001"
49
+ @items[0].description.should == "Notebook Prata"
50
+ @items[0].quantity.should == "1"
51
+ @items[0].amount.should == "24300.00"
52
+
53
+ @items[1].id.should == "0002"
54
+ @items[1].description.should == "Notebook Rosa"
55
+ @items[1].quantity.should == "1"
56
+ @items[1].amount.should == "25600.00"
57
+ end
58
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+
3
+ describe PagSeguro::Payment do
4
+ context "instance" do
5
+ context "accessors" do
6
+ before { @payment = PagSeguro::Payment.new }
7
+
8
+ it { @payment.should have_attribute_accessor(:id) }
9
+ it { @payment.should have_attribute_accessor(:items) }
10
+ it { @payment.should have_attribute_accessor(:sender) }
11
+ it { @payment.should have_attribute_accessor(:shipping) }
12
+ it { @payment.should have_attribute_accessor(:email) }
13
+ it { @payment.should have_attribute_accessor(:token) }
14
+ it { @payment.should have_attribute_accessor(:extra_amount) }
15
+ it { @payment.should have_attribute_accessor(:redirect_url) }
16
+ it { @payment.should have_attribute_accessor(:max_uses) }
17
+ it { @payment.should have_attribute_accessor(:max_age) }
18
+
19
+ it "should have items" do
20
+ @payment.items.should be_instance_of(Array)
21
+ @payment.items.should be_empty
22
+ end
23
+
24
+ it "should have a sender" do
25
+ @payment.sender.should be_instance_of(PagSeguro::Sender)
26
+ end
27
+ end
28
+
29
+ it "should allow to set email and token initialization" do
30
+ payment = PagSeguro::Payment.new("mymail", "mytoken")
31
+ payment.email.should == "mymail"
32
+ payment.token.should == "mytoken"
33
+ end
34
+
35
+ context "validation" do
36
+ before { @payment = PagSeguro::Payment.new("mymail", "mytoken") }
37
+ it "should be valid with valid attributes" do
38
+ @payment.should be_valid
39
+ end
40
+
41
+ it "should not be valid without email" do
42
+ @payment.email = nil
43
+ @payment.should_not be_valid
44
+ end
45
+
46
+ it "should not be valid without token" do
47
+ @payment.token = nil
48
+ @payment.should_not be_valid
49
+ end
50
+
51
+ it "should not be valid with invalid extra amount format" do
52
+ @payment.extra_amount = "10,50"
53
+ @payment.should_not be_valid
54
+ @payment.extra_amount = "R$ 10.50"
55
+ @payment.should_not be_valid
56
+ @payment.extra_amount = "10.50"
57
+ @payment.should be_valid
58
+ end
59
+
60
+ it "should not allow invalid urls" do
61
+ @payment.redirect_url = "httd://something"
62
+ @payment.should_not be_valid
63
+ @payment.redirect_url = "http://heavenstudio.com.br"
64
+ @payment.should be_valid
65
+ end
66
+
67
+ it "should not allow an invalid number of uses" do
68
+ @payment.max_uses = "0"
69
+ @payment.should_not be_valid
70
+ @payment.max_uses = "10"
71
+ @payment.should be_valid
72
+ end
73
+
74
+ it "should not allow an invalid second time" do
75
+ @payment.max_age = "29"
76
+ @payment.should_not be_valid
77
+ @payment.max_age = "30"
78
+ @payment.should be_valid
79
+ end
80
+ end
81
+ end
82
+
83
+ context "checking out" do
84
+ it "should have a checkout_url_with_params" do
85
+ PagSeguro::Payment.new("mymail", "mytoken").checkout_url_with_params.should == 'https://ws.pagseguro.uol.com.br/v2/checkout?email=mymail&token=mytoken'
86
+ end
87
+
88
+ it "should generate a checkout url based on the received response" do
89
+ payment = PagSeguro::Payment.new("mymail", "mytoken")
90
+ payment.stub(:code).and_return("aabbcc")
91
+ payment.checkout_payment_url.should == "https://pagseguro.uol.com.br/v2/checkout/payment.html?code=aabbcc"
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,60 @@
1
+ require "spec_helper"
2
+
3
+ describe PagSeguro::Sender do
4
+ context "instance" do
5
+ before { @sender = PagSeguro::Sender.new }
6
+
7
+ it "should have an email accessor" do
8
+ @sender.should have_attribute_accessor(:email)
9
+ end
10
+
11
+ it "should have a name accessor" do
12
+ @sender.should have_attribute_accessor(:name)
13
+ end
14
+
15
+ it "should have a phone_ddd accessor" do
16
+ @sender.should have_attribute_accessor(:phone_ddd)
17
+ end
18
+
19
+ it "should have a phone_number" do
20
+ @sender.should have_attribute_accessor(:phone_number)
21
+ end
22
+
23
+ it "should be able to initialize with all attributes" do
24
+ sender = PagSeguro::Sender.new(name: "Stefano Diem Benatti", email: "stefano@heavenstudio.com.br", phone_ddd: "11", phone_number: "93430994")
25
+ sender.name.should == "Stefano Diem Benatti"
26
+ sender.email.should == "stefano@heavenstudio.com.br"
27
+ sender.phone_ddd.should == "11"
28
+ sender.phone_number.should == "93430994"
29
+ end
30
+
31
+ it "should tell valid e-mail appart" do
32
+ @sender.email = "nothing"
33
+ @sender.should_not be_a_valid_email
34
+ @sender.email = ("a" * 50) + "waytoolongemail@mail.com"
35
+ @sender.should_not be_a_valid_email
36
+ @sender.email = "stefano@heavenstudio.com.br"
37
+ @sender.should be_a_valid_email
38
+ end
39
+
40
+ it "should not show invalid e-mail" do
41
+ @sender.email = "nothing"
42
+ @sender.email.should be_nil
43
+ end
44
+
45
+ it "should crop the name if it is too big" do
46
+ @sender.name = "a" * 50 + "b" * 10
47
+ @sender.name.should == "a" * 50
48
+ end
49
+
50
+ it "should not show invalid phone ddd's" do
51
+ @sender.phone_ddd = "111"
52
+ @sender.phone_ddd.should be_nil
53
+ end
54
+
55
+ it "should not show invalid phone number" do
56
+ @sender.phone_number = "1234567"
57
+ @sender.phone_number.should be_nil
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+ require "spec_helper"
3
+
4
+ valid_attributes = {
5
+ type: PagSeguro::Shipping::SEDEX,
6
+ state: "SP",
7
+ city: "São Paulo",
8
+ postal_code: "05363000",
9
+ district: "Jd. PoliPoli",
10
+ street: "Av. Otacilio Tomanik",
11
+ number: "775",
12
+ complement: "apto. 92"
13
+ }
14
+
15
+ describe PagSeguro::Shipping do
16
+ context "instance" do
17
+ before { @shipping = PagSeguro::Shipping.new }
18
+ it { @shipping.should have_attribute_accessor(:type) }
19
+ it { @shipping.should have_attribute_accessor(:state) }
20
+ it { @shipping.should have_attribute_accessor(:city) }
21
+ it { @shipping.should have_attribute_accessor(:postal_code) }
22
+ it { @shipping.should have_attribute_accessor(:district) }
23
+ it { @shipping.should have_attribute_accessor(:street) }
24
+ it { @shipping.should have_attribute_accessor(:number) }
25
+ it { @shipping.should have_attribute_accessor(:complement) }
26
+ end
27
+
28
+ it "should be able to initialize all attributes" do
29
+ PagSeguro::Shipping.new(valid_attributes).should be_valid
30
+ end
31
+
32
+ it "should not show postal code unless valid" do
33
+ PagSeguro::Shipping.new(valid_attributes).postal_code.should == "05363000"
34
+ PagSeguro::Shipping.new(valid_attributes.merge(postal_code: 1234567)).postal_code.should be_blank
35
+ end
36
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe PagSeguro do
4
+ it "should have a version" do
5
+ PagSeguro::VERSION.should_not be_empty
6
+ end
7
+ end
@@ -0,0 +1,31 @@
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ add_filter "spec/"
4
+ end
5
+
6
+ require 'yaml'
7
+ require File.dirname(__FILE__) + "/../lib/pag_seguro"
8
+
9
+ class HaveAttributeAccessor
10
+ def initialize(attribute)
11
+ @attribute = attribute
12
+ end
13
+
14
+ def matches?(target)
15
+ @target = target
16
+ @target.respond_to?(:"#{@attribute}").should == true
17
+ @target.respond_to?(:"#{@attribute}=").should == true
18
+ end
19
+
20
+ def failure_message
21
+ "expected #{@target.inspect} to have '#{@expected}' attribute accessor"
22
+ end
23
+
24
+ def negative_failure_message
25
+ "expected #{@target.inspect} not to have '#{@expected}' attribute accessor"
26
+ end
27
+ end
28
+
29
+ def have_attribute_accessor(attribute)
30
+ HaveAttributeAccessor.new(attribute)
31
+ end
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pag_seguro
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Stefano Diem Benatti
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2012-01-24 00:00:00 -02:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: activemodel
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: "0"
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: haml
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: "0"
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: nokogiri
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: rest-client
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ version: 1.6.7
58
+ type: :runtime
59
+ version_requirements: *id004
60
+ description:
61
+ email:
62
+ - stefano.diem@gmail.com
63
+ executables: []
64
+
65
+ extensions: []
66
+
67
+ extra_rdoc_files: []
68
+
69
+ files:
70
+ - .gitignore
71
+ - .rspec
72
+ - Gemfile
73
+ - Guardfile
74
+ - README.markdown
75
+ - Rakefile
76
+ - lib/pag_seguro.rb
77
+ - lib/pag_seguro/checkout.xml.haml
78
+ - lib/pag_seguro/errors/invalid_data.rb
79
+ - lib/pag_seguro/errors/unauthorized.rb
80
+ - lib/pag_seguro/errors/unknown_error.rb
81
+ - lib/pag_seguro/item.rb
82
+ - lib/pag_seguro/notification.rb
83
+ - lib/pag_seguro/payment.rb
84
+ - lib/pag_seguro/sender.rb
85
+ - lib/pag_seguro/shipping.rb
86
+ - lib/pag_seguro/version.rb
87
+ - pag_seguro.gemspec
88
+ - spec/pag_seguro/checkout_xml_spec.rb
89
+ - spec/pag_seguro/integration/checkout_spec.rb
90
+ - spec/pag_seguro/integration/config.yml
91
+ - spec/pag_seguro/item_spec.rb
92
+ - spec/pag_seguro/notification_data_mock.rb
93
+ - spec/pag_seguro/notification_spec.rb
94
+ - spec/pag_seguro/payment_spec.rb
95
+ - spec/pag_seguro/sender_spec.rb
96
+ - spec/pag_seguro/shipping_spec.rb
97
+ - spec/pag_seguro/version_spec.rb
98
+ - spec/spec_helper.rb
99
+ has_rdoc: false
100
+ homepage: http://github.com/heavenstudio/pag_seguro
101
+ licenses: []
102
+
103
+ post_install_message:
104
+ rdoc_options: []
105
+
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: 1.9.2
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: "0"
120
+ requirements: []
121
+
122
+ rubyforge_project:
123
+ rubygems_version: 1.6.2
124
+ signing_key:
125
+ specification_version: 3
126
+ summary: A ruby gem to handle PagSeguro's API version 2
127
+ test_files:
128
+ - spec/pag_seguro/checkout_xml_spec.rb
129
+ - spec/pag_seguro/integration/checkout_spec.rb
130
+ - spec/pag_seguro/integration/config.yml
131
+ - spec/pag_seguro/item_spec.rb
132
+ - spec/pag_seguro/notification_data_mock.rb
133
+ - spec/pag_seguro/notification_spec.rb
134
+ - spec/pag_seguro/payment_spec.rb
135
+ - spec/pag_seguro/sender_spec.rb
136
+ - spec/pag_seguro/shipping_spec.rb
137
+ - spec/pag_seguro/version_spec.rb
138
+ - spec/spec_helper.rb