pag_seguro 0.4.1 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +5 -2
- data/README.md +71 -6
- data/lib/pag_seguro/checkout.xml.haml +24 -5
- data/lib/pag_seguro/convert_field_to_digit.rb +15 -0
- data/lib/pag_seguro/day_of_year.rb +35 -0
- data/lib/pag_seguro/item.rb +9 -11
- data/lib/pag_seguro/notification.rb +1 -1
- data/lib/pag_seguro/payment.rb +26 -19
- data/lib/pag_seguro/pre_approval.rb +84 -0
- data/lib/pag_seguro/query.rb +27 -1
- data/lib/pag_seguro/sender.rb +2 -2
- data/lib/pag_seguro/shipping.rb +2 -2
- data/lib/pag_seguro/transaction.rb +1 -1
- data/lib/pag_seguro/version.rb +1 -1
- data/lib/pag_seguro.rb +9 -1
- data/lib/pagseguro_decimal_validator.rb +9 -0
- data/pag_seguro.gemspec +1 -0
- data/spec/factories.rb +137 -0
- data/spec/fixtures/transaction_history.xml +40 -0
- data/spec/pag_seguro/checkout_xml_spec.rb +142 -159
- data/spec/pag_seguro/convert_field_to_digit_spec.rb +68 -0
- data/spec/pag_seguro/day_of_year_spec.rb +49 -0
- data/spec/pag_seguro/integration/checkout_spec.rb +34 -67
- data/spec/pag_seguro/integration/config.yml +4 -4
- data/spec/pag_seguro/integration/query_spec.rb +56 -34
- data/spec/pag_seguro/item_spec.rb +46 -72
- data/spec/pag_seguro/payment_method_spec.rb +58 -63
- data/spec/pag_seguro/payment_spec.rb +150 -123
- data/spec/pag_seguro/pre_approval_spec.rb +112 -0
- data/spec/pag_seguro/query_spec.rb +111 -4
- data/spec/pag_seguro/sender_spec.rb +50 -62
- data/spec/pag_seguro/shipping_spec.rb +36 -51
- data/spec/spec_helper.rb +11 -20
- data/spec/support/transaction_shared_examples.rb +7 -7
- metadata +32 -3
data/.gitignore
CHANGED
data/Gemfile
CHANGED
@@ -3,8 +3,11 @@ source "http://rubygems.org"
|
|
3
3
|
# Specify your gem's dependencies in PagSeguro.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
-
# test gems
|
6
|
+
# test or development gems
|
7
7
|
gem 'rspec'
|
8
8
|
gem 'simplecov', require: false
|
9
9
|
gem 'guard-rspec'
|
10
|
-
gem '
|
10
|
+
gem 'shoulda-matchers'
|
11
|
+
gem "factory_girl", "~> 4.0"
|
12
|
+
gem 'growl'
|
13
|
+
gem 'pry'
|
data/README.md
CHANGED
@@ -73,7 +73,9 @@ Com exceção do atributo response (que é utilizado para armazenar a resposta e
|
|
73
73
|
|
74
74
|
### API de Notificação
|
75
75
|
|
76
|
-
As notificações de alteração no status da compra no PagSeguro serão enviadas para a URL que tiver configurado na [Notificação de transações](https://pagseguro.uol.com.br/v2/guia-de-integracao/consulta-de-transacoes-por-codigo.html).
|
76
|
+
As notificações de alteração no status da compra no PagSeguro serão enviadas para a URL que tiver configurado na [Notificação de transações](https://pagseguro.uol.com.br/v2/guia-de-integracao/consulta-de-transacoes-por-codigo.html). Se quiser configurar uma url dinâmica para envio das notificação é necessário ativar a página de redirecionamento dinâmico em [Integrações > Página de redirecionamento](https://pagseguro.uol.com.br/integracao/pagina-de-redirecionamento.jhtml), e passar o argumento `redirect_url` para o objeto PagSeguro::Payment:
|
77
|
+
|
78
|
+
PagSeguro::Payment.new(email, token, id: invoice.id, redirect_url: "http://lojamodelo.com.br/checkout")
|
77
79
|
|
78
80
|
O código da notificação é enviado pelo PagSeguro através do parâmentro `notificationCode` em uma requisição do tipo POST. Segue um exemplo de uso da notificação em uma aplicação rails (este exemplo supõe a existência de um `resources :notifications` em suas rotas, e um modelo `Invoice` responsável pelos pagamentos):
|
79
81
|
|
@@ -108,15 +110,28 @@ Para este exemplo, o url configurada na [Notificação de transações](https://
|
|
108
110
|
|
109
111
|
### Consulta de Transações
|
110
112
|
|
111
|
-
|
113
|
+
Há duas maneiras de se realizar a consulta das transações, a primeira delas buscando o código de uma transação, e outra buscando por todas as transações em um determinado período
|
112
114
|
|
113
|
-
|
115
|
+
#### Consulta de Transações por Período
|
114
116
|
|
115
|
-
|
117
|
+
Você pode consultar as transações por uma data através do método `PagSeguro::Query::find`, informando seu email, token, e algumas opções adicionais:
|
116
118
|
|
117
|
-
PagSeguro::
|
119
|
+
transactions = PagSeguro::Query.find(email, token, initial_date: 30.days.ago, final_date: Time.now)
|
120
|
+
|
121
|
+
transactions.each do |transaction|
|
122
|
+
puts "id: #{transaction.id}, transaction_id: #{transaction.transaction_id}"
|
123
|
+
...
|
124
|
+
end
|
125
|
+
|
126
|
+
Além das opções acima, você pode enviar também as opções `:page` (que pelo padrão é a primeira) e `:max_page_results` (que por padrão é 50). Obviamente a data final precisa ser maior que a inicial, não podem haver mais de 30 dias de diferença entre as duas, e a data inicial precisa estar dentro dos últimos 6 meses.
|
127
|
+
|
128
|
+
#### Consulta de Transação por código
|
118
129
|
|
119
|
-
|
130
|
+
O código das transações são enviadas nas Notificações de Transações do PagSeguro (de forma assíncrona), e podem ser obtidas através do método `notification.transaction_id`, e também podem ser obtidas de forma síncrona assim que o usuário retorna à loja após ter concluído a compra. Este código também pode ser encontrado através da busca de transações por período.
|
131
|
+
|
132
|
+
Para buscar informações da transação de forma síncrona, é necessário que acesse sua conta no PagSeguro, e clique em [Integrações > Página de redirecionamento](https://pagseguro.uol.com.br/integracao/pagina-de-redirecionamento.jhtml) e ative o redirecionamento com o código da transação, definindo o nome do parâmetro que será enviado para sua aplicação (e.g.: http://lojamodelo.com.br/checkout?transaction_id=E884542-81B3-4419-9A75-BCC6FB495EF1 ). O redirecionamento para esta página é executado através de uma requisição GET.
|
133
|
+
|
134
|
+
Você pode consultar as informações da transação instanciando a classe `PagSeguro::Query`, que possui os mesmos attributos e métodos que uma notificação:
|
120
135
|
|
121
136
|
query = PagSeguro::Query.new(email, token, "E884542-81B3-4419-9A75-BCC6FB495EF1")
|
122
137
|
|
@@ -124,6 +139,54 @@ Você pode consultar as informações da transação através do `PagSeguro::Que
|
|
124
139
|
# ...
|
125
140
|
end
|
126
141
|
|
142
|
+
### Pagamento Recorrente
|
143
|
+
|
144
|
+
**Primeiro de tudo vale ressaltar que a API de pagamento recorrente não está documentada oficialmente pelo pagseguro, apesar de ter sido lançada em dezembro de 2012. Esta funcionalidade foi criada com base em um post em um [blog](http://sounoob.com.br/requisicao-de-pagamento-do-pagseguro-com-assinatura-associada-usando-php/) e em tentativa e erro. Use por sua conta e risco.**
|
145
|
+
|
146
|
+
É possível enviar a requisição de uma pagamento recorrente juntamente com o pedido de compra (e por enquanto não é possível enviar um pedido de assinatura sem enviar adicionar nenhum ítem ao pagamento). Para usá-la, basta adicionar um `pre_approval` a um pagamento:
|
147
|
+
|
148
|
+
# suponho que uma variavel payment (do tipo PagSeguro::Payment) já foi instanciada, e que o payment.items não está vazio
|
149
|
+
payment.pre_approval = PagSeguro::PreApproval.new
|
150
|
+
|
151
|
+
# obrigatório. Recebe uma string (de até 100 caracteres) e representa o nome da sua assinatura
|
152
|
+
payment.pre_approval.name = "nome da minha assinatura"
|
153
|
+
|
154
|
+
# obrigatório. Recebe uma data e representa a data em que sua assinatura termina. Não pode ser maior do que a data de início (ou hoje) em mais de 744 dias (pouco menos de 3 anos)
|
155
|
+
payment.pre_approval.pre_approval.final_date = Date.new(2014, 6, 12)
|
156
|
+
|
157
|
+
# obrigatório. Valor máximo da assinatura por período/cobrança. Recebe uma string (formatada como "%.2f"), um float ou um BigDecimal
|
158
|
+
payment.pre_approval.max_amount_per_period = '200.00'
|
159
|
+
|
160
|
+
# obrigatório. Valor máximo total da assinatura. Recebe uma string (formatada como "%.2f"), um float ou um BigDecimal
|
161
|
+
payment.pre_approval.max_total_amount = '1000.00'
|
162
|
+
|
163
|
+
# obrigatório. Representa a periodicidade da cobraça. Recebe uma string ou símbolo e pode ser: weekly, monthly, bimonthly, trimonthly, semiannually, ou yearly
|
164
|
+
payment.pre_approval.period = :monthly
|
165
|
+
|
166
|
+
# obrigatório no caso de pagamentos de periodicidade monthly, bimonthly ou trimonthly. Recebe um número (dia do mês) de 1 à 28
|
167
|
+
payment.pre_approval.day_of_month = 10
|
168
|
+
|
169
|
+
# obrigatório no caso de pagamentos de periodicidade weekly. Recebe uma string ou símbolo representando o dia na semana, e pode ser monday, tuesday, wednesday, thursday, friday, saturday ou sunday
|
170
|
+
payment.pre_approval.day_of_week = :friday
|
171
|
+
|
172
|
+
# obrigatório no caso de pagamentos de periodicidade yearly. Recebe uma string representando o dia do mês e o mês do ano no formato 'MM-dd'. Para facilitar use a classe DayOfYear que gera a string no formato correto.
|
173
|
+
payment.pre_approval.day_of_year = PagSeguro::DayOfYear.new(day: 10, month: 4)
|
174
|
+
|
175
|
+
# estranhamente é opcional! Valor de cada cobrança. Recebe uma string (no formato "%.2f"), um float ou um BigDecimal
|
176
|
+
payment.pre_approval.amount_per_payment = '200.00'
|
177
|
+
|
178
|
+
# opcional. Recebe uma string (de até 255 caracteres) e representa os detalhes da assinatura
|
179
|
+
payment.pre_approval.details = "detalhes da assinatura"
|
180
|
+
|
181
|
+
# opcional. Recebe uma data de quando a assinatura passa a valer. Não pode ser maior do que 2 anos da data atual, e precisa ser inferior a data de final_date (que pode ser maior em até 744 dias da data de início)
|
182
|
+
payment.pre_approval.initial_date = Date.new(2014, 3, 12)
|
183
|
+
|
184
|
+
# opcional. Recebe uma string que supostamente deveria levar às condições da sua assinatura
|
185
|
+
payment.pre_approval.reviewURL = "http://seuproduto.com/assinatura"
|
186
|
+
|
187
|
+
# Por fim gere a URL do pagseguro da mesma forma como nas compras/pagamentos normais.
|
188
|
+
redirect_to_url = payment.checkout_payment_url
|
189
|
+
|
127
190
|
## Validações
|
128
191
|
|
129
192
|
Os modelos utilizados nesta gem utilizam as validações do ActiveModel (semelhantes às presentes em ActiveRecord/Rails) e incluem diversas validações, permitindo que se verifique a validade (utilizando object.valid?) dos dados antes de enviá-los ao PagSeguro. A gem não bloqueia o envio das informações caso os dados estejam inválidos, deixando este passo a cargo da sua aplicação, mas levanta erros caso o pag seguro retorne algum erro relativo às informações enviadas.
|
@@ -152,3 +215,5 @@ Desenvolvida por [Stefano Diem Benatti](mailto:stefano@heavenstudio.com.br)
|
|
152
215
|
Rafael Castilho (<http://github.com/castilhor>)
|
153
216
|
|
154
217
|
Rafael Ivan Garcia (https://github.com/rafaelivan)
|
218
|
+
|
219
|
+
efmiglioranza (https://github.com/efmiglioranza)
|
@@ -8,7 +8,7 @@
|
|
8
8
|
%redirectURL= payment.redirect_url
|
9
9
|
- if payment.max_uses.present?
|
10
10
|
%maxUses= payment.max_uses
|
11
|
-
- if payment.
|
11
|
+
- if payment.max_age.present?
|
12
12
|
%maxAge= payment.max_age
|
13
13
|
|
14
14
|
%currency BRL
|
@@ -39,10 +39,7 @@
|
|
39
39
|
|
40
40
|
- if shipping.present?
|
41
41
|
%shipping
|
42
|
-
|
43
|
-
%type= shipping.type
|
44
|
-
- else
|
45
|
-
%type 3
|
42
|
+
%type= shipping.type
|
46
43
|
- if shipping.cost.present?
|
47
44
|
%cost= shipping.cost
|
48
45
|
%address
|
@@ -61,3 +58,25 @@
|
|
61
58
|
%number= shipping.number
|
62
59
|
- if shipping.complement.present?
|
63
60
|
%complement= shipping.complement
|
61
|
+
|
62
|
+
- if pre_approval.present?
|
63
|
+
%preApproval
|
64
|
+
%name= pre_approval.name
|
65
|
+
%finalDate= pre_approval.final_date.iso8601
|
66
|
+
%maxAmountPerPeriod= pre_approval.max_amount_per_period
|
67
|
+
%maxTotalAmount= pre_approval.max_total_amount
|
68
|
+
%period= pre_approval.period
|
69
|
+
- if pre_approval.weekly?
|
70
|
+
%dayOfWeek= pre_approval.day_of_week
|
71
|
+
- if pre_approval.monthly?
|
72
|
+
%dayOfMonth= pre_approval.day_of_month
|
73
|
+
- if pre_approval.yearly?
|
74
|
+
%dayOfYear= pre_approval.day_of_year
|
75
|
+
- if pre_approval.details.present?
|
76
|
+
%details= pre_approval.details
|
77
|
+
- if pre_approval.amount_per_payment.present?
|
78
|
+
%amountPerPayment= pre_approval.amount_per_payment
|
79
|
+
- if pre_approval.initial_date.present?
|
80
|
+
%initialDate= pre_approval.initial_date.iso8601
|
81
|
+
- if pre_approval.review_URL.present?
|
82
|
+
%reviewURL= pre_approval.review_URL
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module PagSeguro
|
2
|
+
module ConvertFieldToDigit
|
3
|
+
def attr_reader_as_digit(*fields)
|
4
|
+
fields.each do |field|
|
5
|
+
define_method(field) do
|
6
|
+
begin
|
7
|
+
"%.2f" % instance_variable_get("@#{field}")
|
8
|
+
rescue ArgumentError, TypeError
|
9
|
+
instance_variable_get("@#{field}")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module PagSeguro
|
2
|
+
module Error
|
3
|
+
class InvalidDayOfYear < Exception
|
4
|
+
def initialize(date_of_year)
|
5
|
+
super("DateOfYear should be a valid date: (month: #{date_of_year.month}, day: #{date_of_year.day})")
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class DayOfYear
|
11
|
+
include Comparable
|
12
|
+
attr_accessor :day, :month
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
@day = options[:day]
|
16
|
+
@month = options[:month]
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
raise Error::InvalidDayOfYear.new(self) unless valid?
|
21
|
+
"#{"%02d" % @month}-#{"%02d" % @day}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def valid?
|
25
|
+
# very simple date validation, just to smoke test possible errors of switching day with month
|
26
|
+
@day < 31 && @month < 12
|
27
|
+
end
|
28
|
+
|
29
|
+
def <=>(other_day_of_the_year)
|
30
|
+
return 1 if @month > other_day_of_the_year.month
|
31
|
+
return -1 if @month < other_day_of_the_year.month
|
32
|
+
@day <=> other_day_of_the_year.day
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/pag_seguro/item.rb
CHANGED
@@ -1,14 +1,17 @@
|
|
1
1
|
module PagSeguro
|
2
2
|
class Item
|
3
3
|
include ActiveModel::Validations
|
4
|
+
extend PagSeguro::ConvertFieldToDigit
|
4
5
|
|
5
6
|
attr_accessor :id, :description, :amount, :quantity, :shipping_cost, :weight
|
7
|
+
attr_reader_as_digit :amount, :shipping_cost
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
validates :id, presence: true
|
10
|
+
validates :description, presence: true
|
11
|
+
validates :amount, pagseguro_decimal: true, presence: true
|
12
|
+
validates :shipping_cost, pagseguro_decimal: true
|
13
|
+
validates :weight, numericality: { only_integer: true, greater_than: 0, allow_blank: true }
|
14
|
+
validates :quantity, presence: true, numericality: { only_integer: true, greater_than: 0, less_than: 1000 }
|
12
15
|
|
13
16
|
def initialize(attributes = {})
|
14
17
|
@id = attributes[:id]
|
@@ -22,10 +25,5 @@ module PagSeguro
|
|
22
25
|
def description
|
23
26
|
@description.present? && @description.size > 100 ? @description[0..99] : @description
|
24
27
|
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
|
28
|
+
end
|
31
29
|
end
|
@@ -10,7 +10,7 @@ module PagSeguro
|
|
10
10
|
|
11
11
|
private
|
12
12
|
def transaction_data(email, token, notification_code)
|
13
|
-
super
|
13
|
+
super RestClient.get "#{PAGSEGURO_TRANSACTIONS_URL}/notifications/#{notification_code}", params: {email: email, token: token}
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
data/lib/pag_seguro/payment.rb
CHANGED
@@ -1,13 +1,16 @@
|
|
1
1
|
module PagSeguro
|
2
2
|
class Payment
|
3
3
|
include ActiveModel::Validations
|
4
|
-
|
5
|
-
|
4
|
+
extend PagSeguro::ConvertFieldToDigit
|
5
|
+
|
6
|
+
attr_accessor :id, :email, :token, :items, :sender, :shipping, :extra_amount,
|
7
|
+
:redirect_url, :max_uses, :max_age, :response, :pre_approval
|
8
|
+
attr_reader_as_digit :extra_amount
|
6
9
|
|
7
10
|
validates_presence_of :email, :token
|
8
|
-
|
11
|
+
validates :extra_amount, pagseguro_decimal: true
|
9
12
|
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
|
13
|
+
validate :max_uses_number, :max_age_number, :valid_pre_approval, :valid_items
|
11
14
|
|
12
15
|
def initialize(email = nil, token = nil, options = {})
|
13
16
|
@email = email unless email.nil?
|
@@ -20,6 +23,7 @@ module PagSeguro
|
|
20
23
|
@redirect_url = options[:redirect_url]
|
21
24
|
@max_uses = options[:max_uses]
|
22
25
|
@max_age = options[:max_age]
|
26
|
+
@pre_approval = options[:pre_approval]
|
23
27
|
end
|
24
28
|
|
25
29
|
def self.checkout_payment_url(code)
|
@@ -28,7 +32,7 @@ module PagSeguro
|
|
28
32
|
|
29
33
|
def checkout_xml
|
30
34
|
xml_content = File.open( File.dirname(__FILE__) + "/checkout.xml.haml" ).read
|
31
|
-
Haml::Engine.new(xml_content).render(nil, items: @items, payment: self, sender: @sender, shipping: @shipping)
|
35
|
+
Haml::Engine.new(xml_content).render(nil, items: @items, payment: self, sender: @sender, shipping: @shipping, pre_approval: @pre_approval)
|
32
36
|
end
|
33
37
|
|
34
38
|
def checkout_url_with_params
|
@@ -40,12 +44,12 @@ module PagSeguro
|
|
40
44
|
end
|
41
45
|
|
42
46
|
def code
|
43
|
-
|
47
|
+
response || parse_checkout_response
|
44
48
|
parse_code
|
45
49
|
end
|
46
50
|
|
47
51
|
def date
|
48
|
-
|
52
|
+
response || parse_checkout_response
|
49
53
|
parse_date
|
50
54
|
end
|
51
55
|
|
@@ -61,30 +65,33 @@ module PagSeguro
|
|
61
65
|
def max_age_number
|
62
66
|
errors.add(:max_age, " must be an integer grater or equal to 30") if @max_age.present? && @max_age.to_i < 30
|
63
67
|
end
|
68
|
+
|
69
|
+
def valid_pre_approval
|
70
|
+
errors.add(:pre_approval, " must be valid") if pre_approval && !pre_approval.valid?
|
71
|
+
end
|
72
|
+
|
73
|
+
def valid_items
|
74
|
+
errors.add(:items, " must be all valid") if items.blank? || !items.all?(&:valid?)
|
75
|
+
end
|
64
76
|
|
65
77
|
def send_checkout
|
66
78
|
RestClient.post(checkout_url_with_params, checkout_xml, content_type: "application/xml"){|resp, request, result| resp }
|
67
79
|
end
|
68
80
|
|
69
81
|
def parse_checkout_response
|
70
|
-
|
71
|
-
if
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
elsif response.code == 400
|
76
|
-
raise Errors::InvalidData.new(response.body)
|
77
|
-
else
|
78
|
-
raise Errors::UnknownError.new(response)
|
79
|
-
end
|
82
|
+
res = send_checkout
|
83
|
+
raise Errors::Unauthorized if res.code == 401
|
84
|
+
raise Errors::InvalidData.new(res.body) if res.code == 400
|
85
|
+
raise Errors::UnknownError.new(res) if res.code != 200
|
86
|
+
@response = res.body
|
80
87
|
end
|
81
88
|
|
82
89
|
def parse_date
|
83
|
-
DateTime.iso8601( Nokogiri::XML(
|
90
|
+
DateTime.iso8601( Nokogiri::XML(response.body).css("checkout date").first.content )
|
84
91
|
end
|
85
92
|
|
86
93
|
def parse_code
|
87
|
-
Nokogiri::XML(
|
94
|
+
Nokogiri::XML(response.body).css("checkout code").first.content
|
88
95
|
end
|
89
96
|
end
|
90
97
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module PagSeguro
|
2
|
+
class PreApproval
|
3
|
+
include ActiveModel::Validations
|
4
|
+
extend PagSeguro::ConvertFieldToDigit
|
5
|
+
|
6
|
+
PERIOD_TYPES = %w(weekly monthly bimonthly trimonthly semiannually yearly)
|
7
|
+
DAYS_OF_WEEK = %w(monday tuesday wednesday thursday friday saturday sunday)
|
8
|
+
DATE_RANGE = 17856.hours
|
9
|
+
|
10
|
+
attr_accessor :name, :details, :amount_per_payment, :period, :day_of_week, :day_of_month,
|
11
|
+
:day_of_year, :initial_date, :final_date, :max_amount_per_period, :max_total_amount, :review_URL
|
12
|
+
attr_reader_as_digit :amount_per_payment, :max_amount_per_period, :max_total_amount
|
13
|
+
|
14
|
+
validates_presence_of :name, :period, :final_date, :max_total_amount, :max_amount_per_period
|
15
|
+
validates_inclusion_of :period, in: PERIOD_TYPES
|
16
|
+
validates_inclusion_of :day_of_week, in: DAYS_OF_WEEK, if: :weekly?
|
17
|
+
validates_inclusion_of :day_of_month, in: (1..28), if: :monthly?
|
18
|
+
validates_presence_of :day_of_year, if: :yearly?
|
19
|
+
validates_format_of :day_of_year, with: /^\d{2}-\d{2}$/, if: :yearly?
|
20
|
+
validate :initial_date_range, :final_date_range
|
21
|
+
validates :max_amount_per_period, pagseguro_decimal: true
|
22
|
+
validates :max_total_amount, pagseguro_decimal: true
|
23
|
+
|
24
|
+
def initialize(options = {})
|
25
|
+
@name = options[:name]
|
26
|
+
@details = options[:details]
|
27
|
+
@amount_per_payment = options[:amount_per_payment]
|
28
|
+
@period = options[:period]
|
29
|
+
@day_of_week = options[:day_of_week]
|
30
|
+
@day_of_month = options[:day_of_month]
|
31
|
+
@day_of_year = options[:day_of_year]
|
32
|
+
@initial_date = options[:initial_date]
|
33
|
+
@final_date = options[:final_date]
|
34
|
+
@max_amount_per_period = options[:max_amount_per_period]
|
35
|
+
@max_total_amount = options[:max_total_amount]
|
36
|
+
@review_URL = options[:review_URL]
|
37
|
+
end
|
38
|
+
|
39
|
+
def period
|
40
|
+
@period.to_s.downcase
|
41
|
+
end
|
42
|
+
|
43
|
+
def day_of_week
|
44
|
+
@day_of_week.to_s.downcase
|
45
|
+
end
|
46
|
+
|
47
|
+
def day_of_year
|
48
|
+
@day_of_year.to_s
|
49
|
+
end
|
50
|
+
|
51
|
+
def initial_date
|
52
|
+
@initial_date.to_datetime if @initial_date.present?
|
53
|
+
end
|
54
|
+
|
55
|
+
def final_date
|
56
|
+
@final_date.to_datetime if @final_date.present?
|
57
|
+
end
|
58
|
+
|
59
|
+
def weekly?
|
60
|
+
period == 'weekly'
|
61
|
+
end
|
62
|
+
|
63
|
+
def monthly?
|
64
|
+
%w(monthly bimonthly trimonthly).include? period
|
65
|
+
end
|
66
|
+
|
67
|
+
def yearly?
|
68
|
+
period == 'yearly'
|
69
|
+
end
|
70
|
+
|
71
|
+
protected
|
72
|
+
def initial_date_range
|
73
|
+
return unless initial_date
|
74
|
+
errors.add(:initial_date) if initial_date < Time.now - 5.minutes
|
75
|
+
errors.add(:initial_date) if initial_date > DATE_RANGE.from_now
|
76
|
+
end
|
77
|
+
|
78
|
+
def final_date_range
|
79
|
+
return unless final_date
|
80
|
+
errors.add(:final_date) if final_date < (initial_date || Time.now) - 5.minutes
|
81
|
+
errors.add(:final_date) if final_date > (initial_date || Time.now) + DATE_RANGE
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/pag_seguro/query.rb
CHANGED
@@ -8,9 +8,35 @@ module PagSeguro
|
|
8
8
|
@data = transaction_data(email, token, transaction_code)
|
9
9
|
end
|
10
10
|
|
11
|
+
def self.find(email, token, options={})
|
12
|
+
url = Transaction::PAGSEGURO_TRANSACTIONS_URL
|
13
|
+
transactions_data = Nokogiri::XML(RestClient.get url, params: search_params(email, token, options))
|
14
|
+
transactions_data.css("transaction").map{|transaction_xml| Transaction.new(transaction_xml) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.search_params(email, token, options={})
|
18
|
+
params = {email: email, token: token}
|
19
|
+
params[:initialDate], params[:finalDate] = parse_dates(options)
|
20
|
+
params[:page] = options[:page] if options[:page]
|
21
|
+
params[:maxPageResults] = options[:max_page_results] if options[:max_page_results]
|
22
|
+
params
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.parse_dates(options={})
|
26
|
+
initial_date = (options[:initial_date] || Time.now - 1.day).to_time
|
27
|
+
final_date = (options[:final_date] || initial_date + 1.day).to_time
|
28
|
+
|
29
|
+
raise "Invalid initial date. Must be bigger than 6 months ago" if initial_date < 6.months.ago
|
30
|
+
raise "Invalid end date. Must be less than today" if final_date > Date.today.end_of_day
|
31
|
+
raise "Invalid end date. Must be bigger than initial date" if final_date < initial_date
|
32
|
+
raise "Invalid end date. Must not differ from initial date in more than 30 days" if (final_date.to_date - initial_date.to_date) > 30
|
33
|
+
|
34
|
+
return initial_date.to_time.iso8601, final_date.to_time.iso8601
|
35
|
+
end
|
36
|
+
|
11
37
|
private
|
12
38
|
def transaction_data(email, token, transaction_code)
|
13
|
-
super
|
39
|
+
super RestClient.get "#{PAGSEGURO_TRANSACTIONS_URL}/#{transaction_code}", params: {email: email, token: token}
|
14
40
|
end
|
15
41
|
end
|
16
42
|
end
|
data/lib/pag_seguro/sender.rb
CHANGED
@@ -27,11 +27,11 @@ module PagSeguro
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def phone_ddd
|
30
|
-
@phone_ddd if @phone_ddd =~ /^\d{2}$/
|
30
|
+
@phone_ddd if @phone_ddd.to_s =~ /^\d{2}$/
|
31
31
|
end
|
32
32
|
|
33
33
|
def phone_number
|
34
|
-
@phone_number if @phone_number =~/^\d{8,9}$/
|
34
|
+
@phone_number if @phone_number.to_s =~/^\d{8,9}$/
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
data/lib/pag_seguro/shipping.rb
CHANGED
@@ -6,12 +6,12 @@ module PagSeguro
|
|
6
6
|
SEDEX = 2
|
7
7
|
UNIDENTIFIED = 3
|
8
8
|
|
9
|
-
|
9
|
+
validates :postal_code, numericality: true, length: {is: 8}
|
10
10
|
|
11
11
|
attr_accessor :type, :state, :city, :postal_code, :district, :street, :number, :complement, :cost
|
12
12
|
|
13
13
|
def initialize(attributes = {})
|
14
|
-
@type = attributes[:type]
|
14
|
+
@type = attributes[:type] || UNIDENTIFIED
|
15
15
|
@state = attributes[:state]
|
16
16
|
@city = attributes[:city]
|
17
17
|
@postal_code = attributes[:postal_code]
|
@@ -170,7 +170,7 @@ module PagSeguro
|
|
170
170
|
|
171
171
|
protected
|
172
172
|
def transaction_data(transaction_xml)
|
173
|
-
Nokogiri::XML(transaction_xml)
|
173
|
+
transaction_xml.instance_of?(Nokogiri::XML::Element) ? transaction_xml : Nokogiri::XML(transaction_xml)
|
174
174
|
end
|
175
175
|
|
176
176
|
def parse_item(data, attribute)
|
data/lib/pag_seguro/version.rb
CHANGED
data/lib/pag_seguro.rb
CHANGED
@@ -1,12 +1,18 @@
|
|
1
1
|
$: << File.expand_path(File.dirname(__FILE__) + "/../lib/pag_seguro")
|
2
2
|
|
3
|
-
require
|
3
|
+
require "date"
|
4
|
+
require "bigdecimal"
|
4
5
|
|
5
6
|
# Third party gems
|
6
7
|
require "active_model"
|
7
8
|
require "nokogiri"
|
8
9
|
require "haml"
|
9
10
|
require "rest-client"
|
11
|
+
require "active_support"
|
12
|
+
require "active_support/time"
|
13
|
+
|
14
|
+
require "pagseguro_decimal_validator"
|
15
|
+
require "convert_field_to_digit"
|
10
16
|
|
11
17
|
# PagSeguro classes
|
12
18
|
require "item"
|
@@ -14,6 +20,8 @@ require "payment"
|
|
14
20
|
require "payment_method"
|
15
21
|
require "sender"
|
16
22
|
require "shipping"
|
23
|
+
require "day_of_year"
|
24
|
+
require "pre_approval"
|
17
25
|
require "transaction"
|
18
26
|
require "notification"
|
19
27
|
require "query"
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class PagseguroDecimalValidator < ActiveModel::EachValidator
|
2
|
+
def validate_each(object, attribute, value)
|
3
|
+
object.errors.add(attribute, error_message) unless value.nil? || value =~ /^\d+\.\d{2}$/
|
4
|
+
end
|
5
|
+
|
6
|
+
def error_message
|
7
|
+
" must be a decimal and have 2 digits after the dot"
|
8
|
+
end
|
9
|
+
end
|
data/pag_seguro.gemspec
CHANGED