pag_seguro 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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