locomotiva-braspag 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +4 -0
  5. data/Guardfile +10 -0
  6. data/README.md +59 -0
  7. data/RELEASES.md +20 -0
  8. data/Rakefile +6 -0
  9. data/config/braspag.yml +17 -0
  10. data/lib/generators/braspag/install/install_generator.rb +15 -0
  11. data/lib/generators/braspag/install/templates/config/braspag.yml +17 -0
  12. data/lib/locomotiva-braspag.rb +45 -0
  13. data/lib/locomotiva-braspag/bill.rb +164 -0
  14. data/lib/locomotiva-braspag/connection.rb +50 -0
  15. data/lib/locomotiva-braspag/credit_card.rb +210 -0
  16. data/lib/locomotiva-braspag/crypto/jar_webservice.rb +91 -0
  17. data/lib/locomotiva-braspag/crypto/webservice.rb +100 -0
  18. data/lib/locomotiva-braspag/eft.rb +90 -0
  19. data/lib/locomotiva-braspag/errors.rb +40 -0
  20. data/lib/locomotiva-braspag/order.rb +42 -0
  21. data/lib/locomotiva-braspag/payment_method.rb +73 -0
  22. data/lib/locomotiva-braspag/poster.rb +36 -0
  23. data/lib/locomotiva-braspag/protected_credit_card.rb +160 -0
  24. data/lib/locomotiva-braspag/utils.rb +32 -0
  25. data/lib/locomotiva-braspag/version.rb +3 -0
  26. data/locomotiva-braspag.gemspec +33 -0
  27. data/spec/bill_spec.rb +427 -0
  28. data/spec/connection_spec.rb +188 -0
  29. data/spec/credit_card_spec.rb +517 -0
  30. data/spec/crypto/jar_webservice_spec.rb +187 -0
  31. data/spec/crypto/webservice_spec.rb +143 -0
  32. data/spec/eft_spec.rb +209 -0
  33. data/spec/order_spec.rb +118 -0
  34. data/spec/poster_spec.rb +53 -0
  35. data/spec/protected_credit_card_spec.rb +296 -0
  36. data/spec/spec_helper.rb +21 -0
  37. data/spec/utils_spec.rb +47 -0
  38. metadata +244 -0
@@ -0,0 +1,40 @@
1
+ module Braspag
2
+ class Error < Exception; end
3
+ class InvalidConnection < Error ; end
4
+ class InvalidMerchantId < Error ; end
5
+ class InvalidConnection < Error ; end
6
+ class IncompleteParams < Error ; end
7
+ class InvalidOrderId < Error ; end
8
+ class InvalidCustomerName < Error ; end
9
+ class InvalidCustomerId < Error ; end
10
+ class InvalidNumber < Error ; end
11
+ class InvalidInstructions < Error ; end
12
+ class InvalidExpirationDate < Error ; end
13
+ class InvalidStringFormat < Error ; end
14
+ class InvalidPost < Error ; end
15
+ class InvalidPaymentMethod < Error ; end
16
+ class InvalidAmount < Error ; end
17
+ class InvalidInstallments < Error ; end
18
+ class InvalidHasInterest < Error ; end
19
+ class InvalidIP < Error; end
20
+ class InvalidCryptKey < Error; end
21
+ class InvalidEncryptedKey < Error; end
22
+ class InvalidHolder < Error ; end
23
+ class InvalidExpirationDate < Error ; end
24
+ class InvalidSecurityCode < Error ; end
25
+ class InvalidType < Error ; end
26
+ class InvalidNumberPayments < Error ; end
27
+ class InvalidNumberInstallments < Error ; end
28
+ class InvalidJustClickKey < Error ; end
29
+ class UnknownError < Error ; end
30
+
31
+ class Connection
32
+ class InvalidMerchantId < Error ; end
33
+ class InvalidEnv < Error ; end
34
+ class InvalidBraspagUrl < Error ; end
35
+ end
36
+
37
+ class Order
38
+ class InvalidData < Error; end
39
+ end
40
+ end
@@ -0,0 +1,42 @@
1
+ module Braspag
2
+ class Order
3
+ PRODUCTION_INFO_URI = "/webservices/pagador/pedido.asmx/GetDadosPedido"
4
+ HOMOLOGATION_INFO_URI = "/pagador/webservice/pedido.asmx/GetDadosPedido"
5
+
6
+ def self.status(order_id)
7
+ connection = Braspag::Connection.instance
8
+
9
+ raise InvalidOrderId unless Braspag::PaymentMethod.valid_order_id?(order_id)
10
+
11
+ data = {
12
+ :loja => connection.merchant_id, :numeroPedido => order_id.to_s
13
+ }
14
+
15
+ response = Braspag::Poster.new(self.status_url).do_post(:order_status, data)
16
+
17
+ response = Utils::convert_to_map(response.body, {
18
+ :authorization => "CodigoAutorizacao",
19
+ :error_code => "CodigoErro",
20
+ :error_message => "MensagemErro",
21
+ :payment_method => "CodigoPagamento",
22
+ :payment_method_name => "FormaPagamento",
23
+ :installments => "NumeroParcelas",
24
+ :status => "Status",
25
+ :amount => "Valor",
26
+ :cancelled_at => "DataCancelamento",
27
+ :paid_at => "DataPagamento",
28
+ :order_date => "DataPedido",
29
+ :transaction_id => "TransId",
30
+ :tid => "BraspagTid"
31
+ })
32
+
33
+ raise InvalidData if response[:authorization].nil?
34
+ response
35
+ end
36
+
37
+ def self.status_url
38
+ connection = Braspag::Connection.instance
39
+ connection.braspag_url + (connection.production? ? PRODUCTION_INFO_URI : HOMOLOGATION_INFO_URI)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,73 @@
1
+ module Braspag
2
+ class PaymentMethod
3
+ PAYMENT_METHODS = {
4
+ # BRASIL
5
+ :amex_2p => 18, # American Express 2 Party
6
+ :cielo_noauth_visa => 71, # Cielo webservice captura automática sem autenticação - Visa
7
+ :cielo_preauth_visa => 73, # Cielo webservice preauth sem autenticação - Visa
8
+ :cielo_noauth_mastercard => 120, # Cielo webservice captura automática sem autenticação - Mastercard
9
+ :cielo_preauth_mastercard => 122, # Cielo webservice preauth sem autenticação - Mastercard
10
+ :cielo_noauth_elo => 126, # Cielo webservice captura automática sem autenticação - ELO
11
+ :cielo_noauth_diners => 130, # Cielo webservice captura automática sem autenticação - Diners
12
+ :redecard => 20, # Redecard Mastercard/Diners/Visa
13
+ :redecard_preauth => 42, # Redecard preauth Mastercard/Diners/Visa
14
+ :cielo_sitef => 57, # Cielo SITEF
15
+ :hipercard_sitef => 62, # Hipercard SITEF
16
+ :hipercard_moip => 90, # Hipercard MOIP
17
+ :oi_paggo => 55, # OiPaggo
18
+ :amex_sitef => 58, # Amex SITEF
19
+ :aura_dtef => 37, # Aura DTEF
20
+ :redecard_sitef => 44, # Redecard SITEF - Mastercard/Diners
21
+ :amex_cielo => 180,
22
+ # MÉXICO
23
+ :mex_amex_2p => 45, # American Express 2 Party
24
+ :mex_banorte_visa => 50, # Banorte Visa
25
+ :mex_banorte_diners => 52, # Banorte Diners
26
+ :mex_banorte_mastercard => 53, # Banorte Mastercard
27
+ # COLÔMBIA
28
+ :col_visa => 63, # Visa
29
+ :col_amex => 65, # Amex
30
+ :col_diners => 66, # Diners
31
+ # INTERNACIONAL
32
+ :paypal_express => 35, # PayPal Express Checkout
33
+ # HOMOLOGATION
34
+ :braspag => 997
35
+ }
36
+
37
+ def self.payment_method_from_id(code)
38
+ self::PAYMENT_METHODS.invert.values_at(code).first
39
+ end
40
+
41
+ def self.normalize_params(params)
42
+ if params[:amount] && !params[:amount].is_a?(BigDecimal)
43
+ params[:amount] = BigDecimal.new(params[:amount].to_s)
44
+ end
45
+
46
+ params
47
+ end
48
+
49
+ def self.check_params(params)
50
+ [:order_id, :amount, :payment_method].each do |param|
51
+ raise IncompleteParams if params[param].nil?
52
+ end
53
+
54
+ raise InvalidOrderId unless self.valid_order_id?(params[:order_id])
55
+
56
+ if params[:customer_name]
57
+ raise InvalidCustomerName unless (1..255).include?(params[:customer_name].to_s.size)
58
+ end
59
+
60
+ if params[:customer_id]
61
+ raise InvalidCustomerId unless (11..18).include?(params[:customer_id].to_s.size)
62
+ end
63
+
64
+ unless params[:payment_method].is_a?(Symbol) && self::PAYMENT_METHODS[params[:payment_method]]
65
+ raise InvalidPaymentMethod
66
+ end
67
+ end
68
+
69
+ def self.valid_order_id?(order_id)
70
+ (order_id.is_a?(String) || order_id.is_a?(Fixnum)) && (1..50).include?(order_id.to_s.size)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,36 @@
1
+ module Braspag
2
+ class Poster
3
+ def initialize(url)
4
+ @request = ::HTTPI::Request.new(url)
5
+ end
6
+
7
+ def do_post(method, data)
8
+ @request.body = data
9
+ @request.proxy = Braspag.proxy_address if Braspag.proxy_address
10
+
11
+ with_logger(method) do
12
+ ::HTTPI.post @request
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def with_logger(method)
19
+ if Braspag::logger
20
+ Braspag::logger.info("[Braspag] ##{method}: #{@request.url}, data: #{mask_data(@request.body).inspect}")
21
+ response = yield
22
+ Braspag::logger.info("[Braspag] ##{method} returns: #{response.body.inspect}")
23
+ else
24
+ response = yield
25
+ end
26
+ response
27
+ end
28
+
29
+ def mask_data(data)
30
+ copy_data = Rack::Utils.parse_nested_query(data)
31
+ copy_data['cardNumber'] = "************%s" % copy_data['cardNumber'][-4..-1] if copy_data['cardNumber']
32
+ copy_data['securityCode'] = "***" if copy_data['securityCode']
33
+ copy_data
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,160 @@
1
+ module Braspag
2
+ class ProtectedCreditCard < PaymentMethod
3
+
4
+ PROTECTED_CARD_MAPPING = {
5
+ :request_id => "RequestId",
6
+ :merchant_id => "MerchantKey",
7
+ :customer_name => "CustomerName",
8
+ :holder => "CardHolder",
9
+ :card_number => "CardNumber",
10
+ :expiration => "CardExpiration"
11
+ }
12
+
13
+ JUST_CLICK_MAPPING = {
14
+ :request_id => "RequestId",
15
+ :merchant_id => "MerchantKey",
16
+ :customer_name => "CustomerName",
17
+ :order_id => "OrderId",
18
+ :amount => "Amount",
19
+ :payment_method => "PaymentMethod",
20
+ :number_installments => "NumberInstallments",
21
+ :payment_type => "PaymentType",
22
+ :just_click_key => "JustClickKey",
23
+ :security_code => "SecurityCode"
24
+ }
25
+
26
+ SAVE_PROTECTED_CARD_URI = "/CartaoProtegido.asmx?wsdl"
27
+ GET_PROTECTED_CARD_URI = "/CartaoProtegido.asmx/GetCreditCard"
28
+ JUST_CLICK_SHOP_URI = "/CartaoProtegido.asmx?wsdl"
29
+
30
+ # saves credit card in Braspag PCI Compliant
31
+ def self.save(params = {})
32
+ connection = Braspag::Connection.instance
33
+ params[:merchant_id] = connection.merchant_id
34
+
35
+ self.check_protected_card_params(params)
36
+
37
+ data = { 'saveCreditCardRequestWS' => {} }
38
+
39
+ PROTECTED_CARD_MAPPING.each do |k, v|
40
+ data['saveCreditCardRequestWS'][v] = params[k] || ""
41
+ end
42
+
43
+
44
+ client = savon_client(self.save_protected_card_url)
45
+ response = client.request(:web, :save_credit_card) do
46
+ soap.body = data
47
+ end
48
+
49
+ response.to_hash[:save_credit_card_response][:save_credit_card_result]
50
+
51
+ end
52
+
53
+ # request the credit card info in Braspag PCI Compliant
54
+ def self.get(just_click_key)
55
+ connection = Braspag::Connection.instance
56
+
57
+ raise InvalidJustClickKey unless valid_just_click_key?(just_click_key)
58
+
59
+ data = { 'getCreditCardRequestWS' => {:loja => connection.merchant_id, :justClickKey => just_click_key} }
60
+
61
+ response = Braspag::Poster.new(self.get_protected_card_url).do_post(:get_protected_card, data)
62
+
63
+ response = Utils::convert_to_map(response.body, {
64
+ :holder => "CardHolder",
65
+ :card_number => "CardNumber",
66
+ :expiration => "CardExpiration",
67
+ :masked_card_number => "MaskedCardNumber"
68
+ })
69
+
70
+ raise UnknownError if response[:card_number].nil?
71
+ response
72
+ end
73
+
74
+ def self.just_click_shop(params = {})
75
+ connection = Braspag::Connection.instance
76
+ params[:merchant_id] = connection.merchant_id
77
+
78
+ self.check_just_click_shop_params(params)
79
+
80
+ order_id = params[:order_id]
81
+ raise InvalidOrderId unless self.valid_order_id?(order_id)
82
+
83
+ data = { 'justClickShopRequestWS' => {} }
84
+
85
+ JUST_CLICK_MAPPING.each do |k, v|
86
+ case k
87
+ when :payment_method
88
+ data['justClickShopRequestWS'][v] = Braspag::Connection.instance.homologation? ? PAYMENT_METHODS[:braspag] : PAYMENT_METHODS[params[:payment_method]]
89
+ when :amount
90
+ data['justClickShopRequestWS'][v] = ("%.2f" % params[k].to_f).gsub('.', '')
91
+ else
92
+ data['justClickShopRequestWS'][v] = params[k] || ""
93
+ end
94
+ end
95
+
96
+ client = savon_client(self.just_click_shop_url)
97
+ response = client.request(:web, :just_click_shop) do
98
+ soap.body = data
99
+ end
100
+
101
+ response.to_hash[:just_click_shop_response][:just_click_shop_result]
102
+
103
+ end
104
+
105
+ def self.check_protected_card_params(params)
106
+ [:request_id, :customer_name, :holder, :card_number, :expiration].each do |param|
107
+ raise IncompleteParams if params[param].nil?
108
+ end
109
+
110
+ raise InvalidHolder if params[:holder].to_s.size < 1 || params[:holder].to_s.size > 100
111
+
112
+ matches = params[:expiration].to_s.match /^(\d{2})\/(\d{2,4})$/
113
+ raise InvalidExpirationDate unless matches
114
+ begin
115
+ year = matches[2].to_i
116
+ year = "20#{year}" if year.size == 2
117
+
118
+ Date.new(year.to_i, matches[1].to_i)
119
+ rescue ArgumentError
120
+ raise InvalidExpirationDate
121
+ end
122
+ end
123
+
124
+ def self.check_just_click_shop_params(params)
125
+ just_click_shop_attributes = [:request_id, :customer_name, :order_id, :amount, :payment_method,
126
+ :number_installments, :payment_type, :just_click_key, :security_code]
127
+
128
+ just_click_shop_attributes.each do |param|
129
+ raise IncompleteParams if params[param].nil?
130
+ end
131
+
132
+ raise InvalidSecurityCode if params[:security_code].to_s.size < 1 || params[:security_code].to_s.size > 4
133
+
134
+ raise InvalidNumberInstallments if params[:number_installments].to_i < 1 || params[:number_installments].to_i > 99
135
+
136
+ end
137
+
138
+ def self.valid_just_click_key?(just_click_key)
139
+ (just_click_key.is_a?(String) && just_click_key.size == 36)
140
+ end
141
+
142
+ def self.save_protected_card_url
143
+ Braspag::Connection.instance.protected_card_url + SAVE_PROTECTED_CARD_URI
144
+ end
145
+
146
+ def self.get_protected_card_url
147
+ Braspag::Connection.instance.protected_card_url + GET_PROTECTED_CARD_URI
148
+ end
149
+
150
+ def self.just_click_shop_url
151
+ Braspag::Connection.instance.protected_card_url + JUST_CLICK_SHOP_URI
152
+ end
153
+
154
+ def self.savon_client url
155
+ s = Savon::Client.new(url)
156
+ s.http.proxy = Braspag.proxy_address if Braspag.proxy_address
157
+ s
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,32 @@
1
+ module Braspag
2
+ class Utils
3
+ def self.convert_decimal_to_string(value)
4
+ ("%.2f" % value.to_f).gsub('.', ',')
5
+ end
6
+
7
+ def self.convert_to_map(document, map = {})
8
+ document = Nokogiri::XML(document)
9
+
10
+ map.each do |key, value|
11
+ if value.is_a?(String) || value.nil?
12
+ value = key if value.nil?
13
+
14
+ new_value = document.search(value).first
15
+
16
+ if new_value.nil?
17
+ map[key] = nil
18
+ else
19
+ new_value = new_value.content.to_s
20
+ map[key] = new_value unless new_value == ""
21
+ map[key] = nil if new_value == ""
22
+ end
23
+
24
+ elsif value.is_a?(Proc)
25
+ map[key] = value.call(document)
26
+ end
27
+ end
28
+
29
+ map
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module Braspag
2
+ VERSION = "0.1.6"
3
+ end
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "locomotiva-braspag/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "locomotiva-braspag"
7
+ s.version = Braspag::VERSION
8
+ s.authors = ["Locomotiva.pro"]
9
+ s.email = %w["contato@locomotiva.pro"]
10
+ s.homepage = "https://github.com/locomotivapro/braspag"
11
+ s.summary = "Locomotiva.pro braspag gem to use Braspag gateway"
12
+ s.description = "Locomotiva.pro braspag gem to use Braspag gateway"
13
+
14
+ s.rubyforge_project = "locomotiva-braspag"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency 'httpi', '>= 0.9.6'
22
+ s.add_dependency 'json', '>= 1.6.1'
23
+ s.add_dependency 'nokogiri', '>= 1.4.7'
24
+ s.add_dependency 'savon', '>= 0.9.9'
25
+
26
+ s.add_development_dependency "rake"
27
+ s.add_development_dependency "rspec"
28
+ s.add_development_dependency "fakeweb"
29
+ s.add_development_dependency "shoulda-matchers"
30
+ s.add_development_dependency "guard-rspec"
31
+ s.add_development_dependency "guard-bundler"
32
+ s.add_development_dependency "debugger"
33
+ end
data/spec/bill_spec.rb ADDED
@@ -0,0 +1,427 @@
1
+ # encoding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
+
4
+ describe Braspag::Bill do
5
+ let(:braspag_homologation_url) { "https://homologacao.pagador.com.br" }
6
+ let(:braspag_production_url) { "https://transaction.pagador.com.br" }
7
+ let(:merchant_id) { "um id qualquer" }
8
+
9
+ before do
10
+ @connection = mock(:merchant_id => merchant_id)
11
+ Braspag::Connection.stub(:instance => @connection)
12
+ end
13
+
14
+ describe ".generate" do
15
+ let(:params) do
16
+ {
17
+ :order_id => 11,
18
+ :amount => 3,
19
+ :payment_method => :hsbc
20
+ }
21
+ end
22
+
23
+ let(:params_with_merchant_id) do
24
+ params.merge!(:merchant_id => merchant_id)
25
+ end
26
+
27
+ let(:creation_url) { "https://bla.com/foo/bar/baz" }
28
+
29
+ before do
30
+ @connection.should_receive(:merchant_id)
31
+
32
+ Braspag::Bill.should_receive(:creation_url)
33
+ .and_return(creation_url)
34
+
35
+ Braspag::Bill.should_receive(:normalize_params)
36
+ .with(params_with_merchant_id)
37
+ .and_return(params_with_merchant_id)
38
+
39
+ Braspag::Bill.should_receive(:check_params)
40
+ .and_return(true)
41
+ end
42
+
43
+ context "with invalid params" do
44
+ it "should raise an error when Braspag returns 'Invalid merchantId' as response" do
45
+ xml = <<-EOXML
46
+ <?xml version="1.0" encoding="utf-8"?>
47
+ <PagadorBoletoReturn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
48
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
49
+ xmlns="https://www.pagador.com.br/webservice/pagador">
50
+ <amount xsi:nil="true" />
51
+ <expirationDate xsi:nil="true" />
52
+ <returnCode>1</returnCode>
53
+ <message>Invalid merchantId</message>
54
+ <status xsi:nil="true" />
55
+ </PagadorBoletoReturn>
56
+ EOXML
57
+
58
+ FakeWeb.register_uri(:post, creation_url, :body => xml)
59
+
60
+ expect {
61
+ Braspag::Bill.generate(params)
62
+ }.to raise_error(Braspag::InvalidMerchantId)
63
+ end
64
+
65
+ it "should raise an error when Braspag returns 'Input string was not in a correct format' as response" do
66
+ xml = <<-EOXML
67
+ <?xml version="1.0" encoding="utf-8"?>
68
+ <PagadorBoletoReturn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
69
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
70
+ xmlns="https://www.pagador.com.br/webservice/pagador">
71
+ <amount xsi:nil="true" />
72
+ <expirationDate xsi:nil="true" />
73
+ <returnCode>1</returnCode>
74
+ <message>Input string was not in a correct format.</message>
75
+ <status xsi:nil="true" />
76
+ </PagadorBoletoReturn>
77
+ EOXML
78
+
79
+ FakeWeb.register_uri(:post, creation_url, :body => xml)
80
+
81
+ expect {
82
+ Braspag::Bill.generate(params)
83
+ }.to raise_error(Braspag::InvalidStringFormat)
84
+ end
85
+
86
+ it "should raise an error when Braspag returns 'Invalid payment method' as response" do
87
+ xml = <<-EOXML
88
+ <?xml version="1.0" encoding="utf-8"?>
89
+ <PagadorBoletoReturn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
90
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
91
+ xmlns="https://www.pagador.com.br/webservice/pagador">
92
+ <amount xsi:nil="true" />
93
+ <expirationDate xsi:nil="true" />
94
+ <returnCode>3</returnCode>
95
+ <message>Invalid payment method</message>
96
+ <status xsi:nil="true" />
97
+ </PagadorBoletoReturn>
98
+ EOXML
99
+
100
+ FakeWeb.register_uri(:post, creation_url, :body => xml)
101
+
102
+ expect {
103
+ Braspag::Bill.generate(params)
104
+ }.to raise_error(Braspag::InvalidPaymentMethod)
105
+ end
106
+
107
+ it "should raise an error when Braspag returns 'Invalid purchase amount' as response" do
108
+ xml = <<-EOXML
109
+ <?xml version="1.0" encoding="utf-8"?>
110
+ <PagadorBoletoReturn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
111
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
112
+ xmlns="https://www.pagador.com.br/webservice/pagador">
113
+ <amount xsi:nil="true" />
114
+ <expirationDate xsi:nil="true" />
115
+ <returnCode>1</returnCode>
116
+ <message>Invalid purchase amount</message>
117
+ <status xsi:nil="true" />
118
+ </PagadorBoletoReturn>
119
+ EOXML
120
+
121
+ FakeWeb.register_uri(:post, creation_url, :body => xml)
122
+
123
+ expect {
124
+ Braspag::Bill.generate(params)
125
+ }.to raise_error(Braspag::InvalidAmount)
126
+ end
127
+
128
+ it "should raise an error when Braspag returns any other error as response" do
129
+ xml = <<-EOXML
130
+ <?xml version="1.0" encoding="utf-8"?>
131
+ <PagadorBoletoReturn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
132
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
133
+ xmlns="https://www.pagador.com.br/webservice/pagador">
134
+ <amount xsi:nil="true" />
135
+ <expirationDate xsi:nil="true" />
136
+ <returnCode>1</returnCode>
137
+ <message>Invalid server</message>
138
+ <status xsi:nil="true" />
139
+ </PagadorBoletoReturn>
140
+ EOXML
141
+
142
+ FakeWeb.register_uri(:post, creation_url, :body => xml)
143
+
144
+ expect {
145
+ Braspag::Bill.generate(params)
146
+ }.to raise_error(Braspag::UnknownError)
147
+ end
148
+ end
149
+
150
+ context "with valid params" do
151
+ let(:valid_xml) do
152
+ <<-EOXML
153
+ <?xml version="1.0" encoding="utf-8"?>
154
+ <PagadorBoletoReturn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
155
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
156
+ xmlns="https://www.pagador.com.br/webservice/pagador">
157
+ <amount>3.00</amount>
158
+ <boletoNumber>123123</boletoNumber>
159
+ <expirationDate>2012-01-08T00:00:00</expirationDate>
160
+ <url>https://homologacao.pagador.com.br/pagador/reenvia.asp?Id_Transacao=722934be-6756-477a-87ab-42115ee1424d</url>
161
+ <returnCode>0</returnCode>
162
+ <status>0</status>
163
+ </PagadorBoletoReturn>
164
+ EOXML
165
+ end
166
+
167
+ let(:params) do
168
+ {
169
+ :order_id => 2901,
170
+ :amount => 3,
171
+ :payment_method => :real,
172
+ :number => "123123",
173
+ :expiration_date => Date.today.strftime("%d/%m/%y")
174
+ }
175
+ end
176
+
177
+ before do
178
+ FakeWeb.register_uri(:post, creation_url, :body => valid_xml)
179
+ @response = Braspag::Bill.generate(params)
180
+ end
181
+
182
+ it "should return a Hash" do
183
+ @response.should be_kind_of Hash
184
+ @response.should == {
185
+ :url => "https://homologacao.pagador.com.br/pagador/reenvia.asp?Id_Transacao=722934be-6756-477a-87ab-42115ee1424d",
186
+ :amount => BigDecimal.new("3.00"),
187
+ :number => "123123",
188
+ :expiration_date => Date.new(2012, 1, 8),
189
+ :return_code => "0",
190
+ :status => "0",
191
+ :message => nil
192
+ }
193
+ end
194
+ end
195
+ end
196
+
197
+ describe ".normalize_params" do
198
+ it "should format the expiration_date param" do
199
+ params = { :expiration_date => Date.new(2011, 12, 10) }
200
+
201
+ result = Braspag::Bill.normalize_params(params)
202
+ result.should be_kind_of Hash
203
+
204
+ result[:expiration_date].should =~ /10\/12\/11/
205
+ end
206
+
207
+ it "should convert amount to BigDecimal" do
208
+ params = { :amount => "100.2" }
209
+
210
+ result = Braspag::Bill.normalize_params(params)
211
+ result.should be_kind_of Hash
212
+
213
+ result[:amount].should be_kind_of BigDecimal
214
+ end
215
+ end
216
+
217
+ describe ".check_params" do
218
+ let(:params) do
219
+ {
220
+ :order_id => "111",
221
+ :amount => 100.0,
222
+ :payment_method => :bradesco
223
+ }
224
+ end
225
+
226
+ [:order_id, :amount, :payment_method].each do |param|
227
+ it "should raise an error when #{param} is not present" do
228
+ expect {
229
+ params[param] = nil
230
+ Braspag::Bill.check_params(params)
231
+ }.to raise_error Braspag::IncompleteParams
232
+ end
233
+ end
234
+
235
+ it "should raise an error when order_id is not valid" do
236
+ Braspag::Bill.should_receive(:valid_order_id?)
237
+ .with(params[:order_id])
238
+ .and_return(false)
239
+
240
+ expect {
241
+ Braspag::Bill.check_params(params)
242
+ }.to raise_error Braspag::InvalidOrderId
243
+ end
244
+
245
+ it "should raise an error when customer_name is present and is greater than 255 chars" do
246
+ expect {
247
+ params[:customer_name] = "b" * 256
248
+ Braspag::Bill.check_params(params)
249
+ }.to raise_error Braspag::InvalidCustomerName
250
+ end
251
+
252
+ it "should raise an error when customer_id is present and is greater than 18 chars" do
253
+ expect {
254
+ params[:customer_id] = "1" * 19
255
+ Braspag::Bill.check_params(params)
256
+ }.to raise_error Braspag::InvalidCustomerId
257
+ end
258
+
259
+ it "should raise an error when customer_id is present and is less than 11 chars" do
260
+ expect {
261
+ params[:customer_id] = "1" * 10
262
+ Braspag::Bill.check_params(params)
263
+ }.to raise_error Braspag::InvalidCustomerId
264
+ end
265
+
266
+ it "should raise an error when number is present and is greater than 255 chars" do
267
+ expect {
268
+ params[:number] = "1" * 256
269
+ Braspag::Bill.check_params(params)
270
+ }.to raise_error Braspag::InvalidNumber
271
+ end
272
+
273
+ it "should raise an error when instructions is present and is greater than 512 chars" do
274
+ expect {
275
+ params[:instructions] = "A" * 513
276
+ Braspag::Bill.check_params(params)
277
+ }.to raise_error Braspag::InvalidInstructions
278
+ end
279
+
280
+ it "should raise an error when expiration_date is present and is not in a valid format" do
281
+ expect {
282
+ params[:expiration_date] = "2011/19/19"
283
+ Braspag::Bill.check_params(params)
284
+ }.to raise_error Braspag::InvalidExpirationDate
285
+
286
+ expect {
287
+ params[:expiration_date] = "29/10/1991"
288
+ Braspag::Bill.check_params(params)
289
+ }.to_not raise_error Braspag::InvalidExpirationDate
290
+ end
291
+
292
+ it "should raise an error when payment_method is not invalid" do
293
+ expect {
294
+ params[:payment_method] = "non ecziste"
295
+ Braspag::Bill.check_params(params)
296
+ }.to raise_error Braspag::InvalidPaymentMethod
297
+ end
298
+ end
299
+
300
+ describe ".valid_order_id?" do
301
+ it "should return false when order id is greater than 50 chars" do
302
+ Braspag::Bill.valid_order_id?("A"*51).should be_false
303
+ end
304
+
305
+ it "should return false when order id is not a String or Fixnum" do
306
+ Braspag::Bill.valid_order_id?(nil).should be_false
307
+ end
308
+
309
+ it "should return true" do
310
+ Braspag::Bill.valid_order_id?("A"*50).should be_true
311
+ Braspag::Bill.valid_order_id?(100).should be_true
312
+ end
313
+ end
314
+
315
+ describe ".info" do
316
+ let(:info_url) { "http://braspag/bla" }
317
+ let(:invalid_xml) do
318
+ <<-EOXML
319
+ <?xml version="1.0" encoding="utf-8"?>
320
+ <DadosBoleto xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
321
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
322
+ xsi:nil="true"
323
+ xmlns="http://www.pagador.com.br/" />
324
+ EOXML
325
+ end
326
+
327
+ let(:valid_xml) do
328
+ <<-EOXML
329
+ <?xml version="1.0" encoding="utf-8"?>
330
+ <DadosBoleto xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
331
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
332
+ xmlns="http://www.pagador.com.br/">
333
+ <NumeroDocumento>999</NumeroDocumento>
334
+ <Sacado/>
335
+ <NossoNumero>999</NossoNumero>
336
+ <LinhaDigitavel>35690.00361 03962.070003 00000.009993 4 50160000001000</LinhaDigitavel>
337
+ <DataDocumento>22/6/2011</DataDocumento>
338
+ <DataVencimento>2/7/2011</DataVencimento>
339
+ <Cedente>Gonow Tecnologia e Acessoria Empresarial Ltda</Cedente>
340
+ <Banco>356-5</Banco>
341
+ <Agencia>0003</Agencia>
342
+ <Conta>6039620</Conta>
343
+ <Carteira>57</Carteira>
344
+ <ValorDocumento>10,00</ValorDocumento>
345
+ </DadosBoleto>
346
+ EOXML
347
+ end
348
+
349
+ it "should raise an error when order id is not valid" do
350
+ Braspag::Bill.should_receive(:valid_order_id?)
351
+ .with("bla")
352
+ .and_return(false)
353
+
354
+ expect {
355
+ Braspag::Bill.info "bla"
356
+ }.to raise_error(Braspag::InvalidOrderId)
357
+ end
358
+
359
+ it "should raise an error when Braspag returned an invalid xml as response" do
360
+ FakeWeb.register_uri(:post, info_url, :body => invalid_xml)
361
+
362
+ Braspag::Bill.should_receive(:info_url)
363
+ .and_return(info_url)
364
+
365
+ expect {
366
+ Braspag::Bill.info("orderid")
367
+ }.to raise_error(Braspag::UnknownError)
368
+ end
369
+
370
+ it "should return a Hash when Braspag returned a valid xml as response" do
371
+ FakeWeb.register_uri(:post, info_url, :body => valid_xml)
372
+
373
+ Braspag::Bill.should_receive(:info_url)
374
+ .and_return(info_url)
375
+
376
+ response = Braspag::Bill.info("orderid")
377
+ response.should be_kind_of Hash
378
+
379
+ response.should == {
380
+ :document_number => "999",
381
+ :payer => nil,
382
+ :our_number => "999",
383
+ :bill_line => "35690.00361 03962.070003 00000.009993 4 50160000001000",
384
+ :document_date => "22/6/2011",
385
+ :expiration_date => "2/7/2011",
386
+ :receiver => "Gonow Tecnologia e Acessoria Empresarial Ltda",
387
+ :bank => "356-5",
388
+ :agency => "0003",
389
+ :account => "6039620",
390
+ :wallet => "57",
391
+ :amount => "10,00",
392
+ :amount_invoice => nil,
393
+ :invoice_date => nil
394
+ }
395
+ end
396
+ end
397
+
398
+ describe ".creation_url" do
399
+ it "should return the correct bill creation url when connection environment is homologation" do
400
+ @connection.stub(:braspag_url => braspag_homologation_url)
401
+ Braspag::Bill.creation_url.should == "#{braspag_homologation_url}/webservices/pagador/Boleto.asmx/CreateBoleto"
402
+ end
403
+
404
+ it "should return the correct bill creation url when connection environment is production" do
405
+ @connection.stub(:braspag_url => braspag_production_url)
406
+ Braspag::Bill.creation_url.should == "#{braspag_production_url}/webservices/pagador/Boleto.asmx/CreateBoleto"
407
+ end
408
+ end
409
+
410
+ describe ".info_url" do
411
+ it "should return the correct info url when connection environment is homologation" do
412
+ @connection.stub(:braspag_url => braspag_homologation_url)
413
+ @connection.should_receive(:production?)
414
+ .and_return(false)
415
+
416
+ Braspag::Bill.info_url.should == "#{braspag_homologation_url}/pagador/webservice/pedido.asmx/GetDadosBoleto"
417
+ end
418
+
419
+ it "should return the correct info url when connection environment is production" do
420
+ @connection.stub(:braspag_url => braspag_production_url)
421
+ @connection.should_receive(:production?)
422
+ .and_return(true)
423
+
424
+ Braspag::Bill.info_url.should == "#{braspag_production_url}/webservices/pagador/pedido.asmx/GetDadosBoleto"
425
+ end
426
+ end
427
+ end