moiper 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,58 @@
1
+ require "nokogiri"
2
+
3
+ module Moiper
4
+ class Payment
5
+ # The unique ID you can set for this payment. This is usefull to keep
6
+ # trak of which payment we will receive on the Moip's notification process.
7
+ attr_accessor :id
8
+
9
+ # The description of the purchase.
10
+ attr_accessor :description
11
+
12
+ # The amount to be billed from the user.
13
+ attr_accessor :price
14
+
15
+ # The URL where the user will be redirected after he finishes the
16
+ # payment process.
17
+ attr_accessor :return_url
18
+
19
+ # The URL where Moip will send notifications about your purchase updates.
20
+ attr_accessor :notification_url
21
+
22
+ # Create a new Moip Payment request
23
+ def initialize(options = {})
24
+ raise ArgumentError if options[:description].nil? || options[:description].empty?
25
+ raise ArgumentError if options[:price].nil? || options[:price].to_f <= 0
26
+
27
+ options.each do |attribute, value|
28
+ send "#{attribute}=", value
29
+ end
30
+ end
31
+
32
+ # Create the payment XML representation
33
+ def to_xml
34
+ builder = Nokogiri::XML::Builder.new(:encoding => "UTF-8") do |xml|
35
+ xml.EnviarInstrucao {
36
+ xml.InstrucaoUnica {
37
+ xml.Razao description
38
+ xml.IdProprio id
39
+
40
+ xml.Valores {
41
+ xml.Valor price, :moeda => "BRL"
42
+ }
43
+
44
+ xml.URLNotificacao notification_url
45
+ xml.URLRetorno return_url
46
+ }
47
+ }
48
+ end
49
+
50
+ builder.to_xml
51
+ end
52
+
53
+ def checkout
54
+ request = Request.new
55
+ request.process(to_xml)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,46 @@
1
+ require "net/http"
2
+ require "net/https"
3
+
4
+ module Moiper
5
+ class Request
6
+ CA_FILE = File.expand_path(File.dirname(__FILE__)) + "/cacert.pem"
7
+
8
+ attr_reader :response
9
+
10
+ def process(payload)
11
+ @response = post(payload)
12
+ Response.new(@response.body)
13
+ end
14
+
15
+ def client
16
+ @client ||= Net::HTTP.new(uri.host, uri.port).tap do |http|
17
+ http.use_ssl = true
18
+ http.ca_file = CA_FILE
19
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
20
+ end
21
+ end
22
+
23
+ def request
24
+ @request ||= Net::HTTP::Post.new(uri.path).tap do |request|
25
+ request.basic_auth Moiper.token, Moiper.key
26
+ request.content_type = "text/xml"
27
+ request["User-Agent"] = "Moiper/#{Moiper::VERSION}"
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def post(payload)
34
+ request.body = payload
35
+ client.request(request)
36
+ end
37
+
38
+ def uri
39
+ @uri ||= URI(Moiper.api_entrypoint + path)
40
+ end
41
+
42
+ def path
43
+ "ws/alpha/EnviarInstrucao/Unica"
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,29 @@
1
+ module Moiper
2
+ class Response
3
+ def initialize(body)
4
+ @body = body
5
+ end
6
+
7
+ def success?
8
+ parsed_body.css("Status").text == "Sucesso"
9
+ end
10
+
11
+ def checkout_url
12
+ Moiper.api_entrypoint + "Instrucao.do?token=" + token if success?
13
+ end
14
+
15
+ def token
16
+ parsed_body.css("Token").text
17
+ end
18
+
19
+ def errors
20
+ parsed_body.css("Erro").map(&:text)
21
+ end
22
+
23
+ private
24
+
25
+ def parsed_body
26
+ @parsed_body ||= Nokogiri::XML(@body)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,4 @@
1
+ module Moiper
2
+ # Specify the current gem version
3
+ VERSION = "0.0.1"
4
+ end
data/lib/moiper.rb ADDED
@@ -0,0 +1,47 @@
1
+ require "moiper/version"
2
+ require "moiper/payment"
3
+ require "moiper/request"
4
+ require "moiper/response"
5
+
6
+ module Moiper
7
+ API_ENTRYPOINTS = {
8
+ :sandbox => "https://desenvolvedor.moip.com.br/sandbox/",
9
+ :production => "https://www.moip.com.br/"
10
+ }
11
+
12
+ class << self
13
+ # Set Moip's API token
14
+ attr_accessor :token
15
+
16
+ # Set Moip's API key
17
+ attr_accessor :key
18
+
19
+ # Define if requests should be made against Moip's sandbox
20
+ # environment. This is specially usefull when running
21
+ # on development or test mode. Default is false.
22
+ #
23
+ # Moiper.sandbox = true
24
+ #
25
+ attr_accessor :sandbox
26
+
27
+ # Configure Moiper options.
28
+ #
29
+ # Moiper.configure do |config|
30
+ # config.sandbox = true
31
+ # end
32
+ #
33
+ def configure(&block)
34
+ yield self
35
+ end
36
+
37
+ # Inform if in sandbox mode
38
+ def sandbox?
39
+ sandbox == true
40
+ end
41
+
42
+ # Returns the Moip API entrypoint based on the current environment
43
+ def api_entrypoint
44
+ sandbox? ? API_ENTRYPOINTS[:sandbox] : API_ENTRYPOINTS[:production]
45
+ end
46
+ end
47
+ end
data/moiper.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'moiper/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "moiper"
8
+ gem.version = Moiper::VERSION
9
+ gem.authors = ["Rodrigo Navarro"]
10
+ gem.email = ["rnavarro1@gmail.com"]
11
+ gem.description = %q{Moip payment service integration library.}
12
+ gem.summary = gem.description
13
+ gem.homepage = "http://rubygems.org/gems/moiper"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency "nokogiri"
21
+ gem.add_development_dependency "rspec"
22
+ end
@@ -0,0 +1,108 @@
1
+ require "spec_helper"
2
+
3
+ describe Moiper::Payment do
4
+ describe "#initialize" do
5
+ subject {
6
+ Moiper::Payment.new(
7
+ :description => "A chair",
8
+ :price => 1.99,
9
+ :id => "some unique id",
10
+ :return_url => "http://example.org/thank_you",
11
+ :notification_url => "http://example.org/moip/notification"
12
+ )
13
+ }
14
+
15
+ its(:description) { should eq "A chair" }
16
+ its(:price) { should == 1.99 }
17
+ its(:id) { should eq "some unique id" }
18
+ its(:return_url) { should eq "http://example.org/thank_you"}
19
+ its(:notification_url) { should eq "http://example.org/moip/notification" }
20
+
21
+ it "raises an error when no description is given" do
22
+ expect {
23
+ Moiper::Payment.new(
24
+ :price => 1.99,
25
+ :id => "some unique id",
26
+ :return_url => "http://example.org/thank_you",
27
+ :notification_url => "http://example.org/moip/notification"
28
+ )
29
+ }.to raise_error ArgumentError
30
+ end
31
+
32
+ it "raises an error when a blank description is given" do
33
+ expect {
34
+ Moiper::Payment.new(
35
+ :description => "",
36
+ :price => 1.99,
37
+ :id => "some unique id",
38
+ :return_url => "http://example.org/thank_you",
39
+ :notification_url => "http://example.org/moip/notification"
40
+ )
41
+ }.to raise_error ArgumentError
42
+ end
43
+
44
+ it "raises an error when no price is given" do
45
+ expect {
46
+ Moiper::Payment.new(
47
+ :description => "Some description",
48
+ :id => "some unique id",
49
+ :return_url => "http://example.org/thank_you",
50
+ :notification_url => "http://example.org/moip/notification"
51
+ )
52
+ }.to raise_error ArgumentError
53
+ end
54
+
55
+ it "raises an error when the price is zero" do
56
+ expect {
57
+ Moiper::Payment.new(
58
+ :description => "Some description",
59
+ :price => 0,
60
+ :id => "some unique id",
61
+ :return_url => "http://example.org/thank_you",
62
+ :notification_url => "http://example.org/moip/notification"
63
+ )
64
+ }.to raise_error ArgumentError
65
+ end
66
+
67
+ it "raises an error when the price is lower than zero" do
68
+ expect {
69
+ Moiper::Payment.new(
70
+ :description => "Some description",
71
+ :price => -10,
72
+ :id => "some unique id",
73
+ :return_url => "http://example.org/thank_you",
74
+ :notification_url => "http://example.org/moip/notification"
75
+ )
76
+ }.to raise_error ArgumentError
77
+ end
78
+ end
79
+
80
+ describe "#to_xml" do
81
+ let(:payment) {
82
+ Moiper::Payment.new(
83
+ :description => "A chair",
84
+ :price => 1.99,
85
+ :id => "some unique id",
86
+ :return_url => "http://example.org/thank_you",
87
+ :notification_url => "http://example.org/moip/notification"
88
+ )
89
+ }
90
+
91
+ let(:doc) { Nokogiri::XML(payment.to_xml) }
92
+
93
+ it { doc.at_css("> EnviarInstrucao").should_not be_nil }
94
+ it { doc.at_css("EnviarInstrucao > InstrucaoUnica").should_not be_nil }
95
+ it { doc.at_css("InstrucaoUnica > IdProprio").should_not be_nil }
96
+ it { doc.at_css("InstrucaoUnica > Valores").should_not be_nil }
97
+ it { doc.at_css("Valores > Valor").should_not be_nil }
98
+ it { doc.at_css("InstrucaoUnica > URLRetorno").should_not be_nil }
99
+ it { doc.at_css("InstrucaoUnica > URLNotificacao").should_not be_nil }
100
+
101
+ it { doc.at_css("InstrucaoUnica > IdProprio").text.should eq "some unique id"}
102
+ it { doc.at_css("Valores > Valor").text.should eq "1.99"}
103
+ it { doc.at_css("Valores > Valor").attributes["moeda"].value.should eq "BRL"}
104
+
105
+ it { doc.at_css("InstrucaoUnica > URLRetorno").text.should eq "http://example.org/thank_you" }
106
+ it { doc.at_css("InstrucaoUnica > URLNotificacao").text.should eq "http://example.org/moip/notification" }
107
+ end
108
+ end
@@ -0,0 +1,41 @@
1
+ require "spec_helper"
2
+
3
+ describe Moiper::Request do
4
+ describe "#process" do
5
+ it "returns a Response object" do
6
+ response = Moiper::Request.new.process("<someXml></someXml>")
7
+ response.should be_kind_of Moiper::Response
8
+ end
9
+ end
10
+
11
+ describe "#client" do
12
+ let(:client) { Moiper::Request.new.client }
13
+
14
+ it "uses SSL" do
15
+ client.use_ssl?.should be_true
16
+ end
17
+
18
+ it "verifies the certificate" do
19
+ client.verify_mode.should == OpenSSL::SSL::VERIFY_PEER
20
+ end
21
+
22
+ it "uses the right ca file" do
23
+ client.ca_file.should == Moiper::Request::CA_FILE
24
+ end
25
+ end
26
+
27
+ describe "#request" do
28
+ subject { Moiper::Request.new.request }
29
+ its(:content_type) { should eq "text/xml" }
30
+
31
+ it "sets the correct token and key parameters" do
32
+ Net::HTTP::Post.any_instance.should_receive(:basic_auth).with(Moiper.token, Moiper.key)
33
+ Moiper::Request.new.request
34
+ end
35
+
36
+ it "has the correct user agent" do
37
+ headers = Hash[subject.each_capitalized.to_a]
38
+ headers["User-Agent"].should eq "Moiper/#{Moiper::VERSION}"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,78 @@
1
+ # encoding: utf-8
2
+
3
+ require "spec_helper"
4
+
5
+ describe Moiper::Response do
6
+ let(:success_response_fixture) {
7
+ %Q{
8
+ <?xml version="1.0"?>
9
+ <ns1:EnviarInstrucaoUnicaResponse xmlns:ns1="http://www.moip.com.br/ws/alpha/">
10
+ <Resposta>
11
+ <ID>201301221539573040000001453849</ID>
12
+ <Status>Sucesso</Status>
13
+ <Token>M2M0J1D3M0U1L2L2S1W5V3X9T5V7Y3X0W4U0M0Z0L0G02001S4F513N8N4J9</Token>
14
+ </Resposta>
15
+ </ns1:EnviarInstrucaoUnicaResponse>
16
+ }
17
+ }
18
+
19
+ let(:error_response_fixture) {
20
+ %Q{
21
+ <?xml version="1.0"?>
22
+ <ns1:EnviarInstrucaoUnicaResponse xmlns:ns1="http://www.moip.com.br/ws/alpha/">
23
+ <Resposta>
24
+ <ID>201301221546325890000001453860</ID>
25
+ <Status>Falha</Status>
26
+ <Erro Codigo="101">A razão do pagamento deve ser enviada obrigatoriamente</Erro>
27
+ <Erro Codigo="102">Id Próprio já foi utilizado em outra Instrução</Erro>
28
+ </Resposta>
29
+ </ns1:EnviarInstrucaoUnicaResponse>
30
+ }
31
+ }
32
+
33
+ describe "#checkout_url" do
34
+ context "when the response is success" do
35
+ it "returns the url the user should be redirected" do
36
+ response = Moiper::Response.new(success_response_fixture)
37
+ response.checkout_url.should eq Moiper.api_entrypoint + "Instrucao.do?token=" + response.token
38
+ end
39
+ end
40
+
41
+ context "when the response returns an error" do
42
+ it "returns nil" do
43
+ response = Moiper::Response.new(error_response_fixture)
44
+ response.checkout_url.should be_nil
45
+ end
46
+ end
47
+ end
48
+
49
+ describe "#success?" do
50
+ subject { Moiper::Response.new(body).success? }
51
+
52
+ context "when the response is success" do
53
+ let(:body) { success_response_fixture }
54
+ it { should be_true }
55
+ end
56
+
57
+ context "when the response returns an error" do
58
+ let(:body) { error_response_fixture }
59
+ it { should be_false }
60
+ end
61
+ end
62
+
63
+ describe "#errors" do
64
+ context "when the response is success" do
65
+ it "is an empty array" do
66
+ Moiper::Response.new(success_response_fixture).errors.should be_empty
67
+ end
68
+ end
69
+
70
+ context "when the response returns an error" do
71
+ it "returns an array with errors" do
72
+ errors = Moiper::Response.new(error_response_fixture).errors
73
+ errors.should include "A razão do pagamento deve ser enviada obrigatoriamente"
74
+ errors.should include "Id Próprio já foi utilizado em outra Instrução"
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,59 @@
1
+ require "spec_helper"
2
+
3
+ describe Moiper do
4
+ describe ".configure" do
5
+ it "yields Moiper" do
6
+ Moiper.configure do |config|
7
+ config.should be Moiper
8
+ end
9
+ end
10
+
11
+ it "sets a token configuration" do
12
+ Moiper.configure do |config|
13
+ config.token = "Some Token"
14
+ end
15
+
16
+ Moiper.token.should eq "Some Token"
17
+ end
18
+
19
+ it "sets a key configuration" do
20
+ Moiper.configure do |config|
21
+ config.key = "Some Key"
22
+ end
23
+
24
+ Moiper.key.should eq "Some Key"
25
+ end
26
+ end
27
+
28
+ describe ".sandbox?" do
29
+ it "is true when you set sandbox configuration true" do
30
+ Moiper.configure do |config|
31
+ config.sandbox = true
32
+ end
33
+
34
+ Moiper.sandbox?.should be_true
35
+ end
36
+
37
+ it "is false when you set sandbox configuration to false" do
38
+ Moiper.configure do |config|
39
+ config.sandbox = false
40
+ end
41
+
42
+ Moiper.sandbox?.should be_false
43
+ end
44
+ end
45
+
46
+ describe ".api_entrypoint" do
47
+ subject { Moiper.api_entrypoint }
48
+
49
+ it "is the sandbox url when sandbox is enabled" do
50
+ Moiper.sandbox = true
51
+ should == Moiper::API_ENTRYPOINTS[:sandbox]
52
+ end
53
+
54
+ it "is the production url when sandbox is disabled" do
55
+ Moiper.sandbox = false
56
+ should == Moiper::API_ENTRYPOINTS[:production]
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,11 @@
1
+ require "moiper"
2
+
3
+ RSpec.configure do |config|
4
+ config.before do
5
+ Moiper.configure do |config|
6
+ config.sandbox = true
7
+ config.token = "01010101010101010101010101010101"
8
+ config.key = "ABABABABABABABABABABABABABABABABABABABAB"
9
+ end
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: moiper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Rodrigo Navarro
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: nokogiri
16
+ requirement: &2160726500 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2160726500
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &2160725540 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *2160725540
36
+ description: Moip payment service integration library.
37
+ email:
38
+ - rnavarro1@gmail.com
39
+ executables: []
40
+ extensions: []
41
+ extra_rdoc_files: []
42
+ files:
43
+ - .gitignore
44
+ - Gemfile
45
+ - LICENSE.txt
46
+ - README.md
47
+ - Rakefile
48
+ - lib/moiper.rb
49
+ - lib/moiper/cacert.pem
50
+ - lib/moiper/payment.rb
51
+ - lib/moiper/request.rb
52
+ - lib/moiper/response.rb
53
+ - lib/moiper/version.rb
54
+ - moiper.gemspec
55
+ - spec/moiper/payment_spec.rb
56
+ - spec/moiper/request_spec.rb
57
+ - spec/moiper/response_spec.rb
58
+ - spec/moiper_spec.rb
59
+ - spec/spec_helper.rb
60
+ homepage: http://rubygems.org/gems/moiper
61
+ licenses: []
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ! '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ segments:
73
+ - 0
74
+ hash: 657788424020654005
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ segments:
82
+ - 0
83
+ hash: 657788424020654005
84
+ requirements: []
85
+ rubyforge_project:
86
+ rubygems_version: 1.8.11
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: Moip payment service integration library.
90
+ test_files:
91
+ - spec/moiper/payment_spec.rb
92
+ - spec/moiper/request_spec.rb
93
+ - spec/moiper/response_spec.rb
94
+ - spec/moiper_spec.rb
95
+ - spec/spec_helper.rb