moiper 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +10 -0
- data/README.md +11 -1
- data/lib/moiper.rb +25 -9
- data/lib/moiper/notification.rb +25 -0
- data/lib/moiper/notification_controller_helper.rb +27 -0
- data/lib/moiper/payment.rb +31 -9
- data/lib/moiper/railtie.rb +4 -0
- data/lib/moiper/request.rb +15 -4
- data/lib/moiper/response.rb +8 -0
- data/lib/moiper/version.rb +1 -2
- data/spec/moiper/payment_spec.rb +9 -0
- data/spec/moiper/request_spec.rb +1 -2
- metadata +9 -8
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Moiper
|
2
2
|
|
3
|
+
[![Build Status](https://travis-ci.org/reu/moiper.png?branch=master)](https://travis-ci.org/reu/moiper)
|
4
|
+
|
3
5
|
[Moip payment service](http://moip.com.br/) integration library.
|
4
6
|
|
5
7
|
## Installation
|
@@ -59,7 +61,7 @@ You need to redirect your user to the url returned by `response.checkout_url`. A
|
|
59
61
|
|
60
62
|
### Notifications
|
61
63
|
|
62
|
-
Moip will notify your application about order updates through [NASP](http://labs.moip.com.br/referencia/nasp/). Moiper provides a Rails controller helper
|
64
|
+
Moip will notify your application about order updates through [NASP](http://labs.moip.com.br/referencia/nasp/). Moiper provides a Rails controller helper so you can easily intercept these notifications with the `moip_notification` method.
|
63
65
|
|
64
66
|
```ruby
|
65
67
|
class OrdersController < ApplicationController
|
@@ -81,6 +83,14 @@ class OrdersController < ApplicationController
|
|
81
83
|
end
|
82
84
|
```
|
83
85
|
|
86
|
+
If you are not using Rails, you can manually instantiate a Moiper::Notification class by passing the POST parameters received from Moip's request.
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
post "/moip/callback" do
|
90
|
+
notification = Moiper::Notification.new(params)
|
91
|
+
end
|
92
|
+
```
|
93
|
+
|
84
94
|
#### Payment Status
|
85
95
|
|
86
96
|
Following are the possible payment statuses returned by the `Moip::Notification#payment_status`.
|
data/lib/moiper.rb
CHANGED
@@ -6,31 +6,47 @@ require "moiper/notification"
|
|
6
6
|
require "moiper/railtie" if defined? Rails
|
7
7
|
|
8
8
|
module Moiper
|
9
|
+
# The available Moip entrypoints
|
10
|
+
# @see http://labs.moip.com.br/referencia/autenticacao_api/#ambientes
|
11
|
+
# The Moip's official documentation regarding its entrypoints
|
9
12
|
API_ENTRYPOINTS = {
|
10
13
|
:sandbox => "https://desenvolvedor.moip.com.br/sandbox/",
|
11
14
|
:production => "https://www.moip.com.br/"
|
12
15
|
}
|
13
16
|
|
14
17
|
class << self
|
15
|
-
#
|
18
|
+
# @!group Configurable options
|
19
|
+
|
20
|
+
# @return [String] Moip's API token
|
16
21
|
attr_accessor :token
|
17
22
|
|
18
|
-
#
|
23
|
+
# @return [String] Moip's API key
|
19
24
|
attr_accessor :key
|
20
25
|
|
21
26
|
# Define if requests should be made against Moip's sandbox
|
22
27
|
# environment. This is specially usefull when running
|
23
|
-
# on development or test mode. Default is false
|
28
|
+
# on development or test mode. Default is false
|
29
|
+
#
|
30
|
+
# @see http://labs.moip.com.br/referencia/autenticacao_api/#sandbox
|
31
|
+
# Moip's official documentation regarding sandbox activation
|
24
32
|
#
|
25
|
-
#
|
33
|
+
# @example
|
34
|
+
# Moiper.sandbox = true
|
26
35
|
#
|
36
|
+
# @return [Boolean] current sandbox state
|
27
37
|
attr_accessor :sandbox
|
28
38
|
|
29
|
-
#
|
39
|
+
# @!endgroup
|
40
|
+
|
41
|
+
# Configure Moiper's options
|
42
|
+
#
|
43
|
+
# @yieldparam config [Moiper]
|
44
|
+
# @return [void]
|
30
45
|
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
46
|
+
# @example
|
47
|
+
# Moiper.configure do |config|
|
48
|
+
# config.sandbox = true
|
49
|
+
# end
|
34
50
|
#
|
35
51
|
def configure(&block)
|
36
52
|
yield self
|
@@ -41,7 +57,7 @@ module Moiper
|
|
41
57
|
sandbox == true
|
42
58
|
end
|
43
59
|
|
44
|
-
#
|
60
|
+
# @return [String] the Moip API entrypoint based on the current environment
|
45
61
|
def api_entrypoint
|
46
62
|
sandbox? ? API_ENTRYPOINTS[:sandbox] : API_ENTRYPOINTS[:production]
|
47
63
|
end
|
data/lib/moiper/notification.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Moiper
|
2
2
|
class Notification
|
3
|
+
# @see http://labs.moip.com.br/parametro/statuspagamento/
|
4
|
+
# The list of payment status on the Moip's official documentation
|
3
5
|
PAYMENT_STATUSES = {
|
4
6
|
1 => :authorized,
|
5
7
|
2 => :started,
|
@@ -11,6 +13,8 @@ module Moiper
|
|
11
13
|
9 => :reimbursed
|
12
14
|
}
|
13
15
|
|
16
|
+
# @see http://labs.moip.com.br/parametro/instituicaopagamento/
|
17
|
+
# The list of financial institutions on the Moip's official documentation
|
14
18
|
FINANCIAL_INSTITUTIONS = {
|
15
19
|
1 => "MoIP",
|
16
20
|
3 => "Visa",
|
@@ -25,6 +29,8 @@ module Moiper
|
|
25
29
|
88 => "Banrisul"
|
26
30
|
}
|
27
31
|
|
32
|
+
# @see http://labs.moip.com.br/parametro/formapagamento/
|
33
|
+
# The list of payment methods on the Moip's official documentation
|
28
34
|
PAYMENT_METHODS = {
|
29
35
|
"BoletoBancario" => :payment_form,
|
30
36
|
"CartaoDeCredito" => :credit_card,
|
@@ -41,38 +47,57 @@ module Moiper
|
|
41
47
|
@params = params
|
42
48
|
end
|
43
49
|
|
50
|
+
# @return [String] informed unique identifier
|
44
51
|
def id
|
45
52
|
params["id_transacao"]
|
46
53
|
end
|
47
54
|
|
55
|
+
# @return [Float] amount paid by the user
|
48
56
|
def price
|
49
57
|
params["valor"].to_i / 100.0
|
50
58
|
end
|
51
59
|
|
60
|
+
# @return [Symbol] payment status
|
61
|
+
# @see PAYMENT_STATUSES The full list of possible payment statuses
|
62
|
+
# returned
|
52
63
|
def payment_status
|
53
64
|
PAYMENT_STATUSES[params["status_pagamento"].to_i]
|
54
65
|
end
|
55
66
|
|
67
|
+
# @return [Integer] the internal Moip identifier for this transaction
|
56
68
|
def moip_id
|
57
69
|
params["cod_moip"].to_i
|
58
70
|
end
|
59
71
|
|
72
|
+
# @return [String] financial institution name
|
73
|
+
# @see FINANCIAL_INSTITUTIONS The full list of possible financial
|
74
|
+
# institutions returned
|
60
75
|
def financial_institution
|
61
76
|
FINANCIAL_INSTITUTIONS[params["forma_pagamento"].to_i]
|
62
77
|
end
|
63
78
|
|
79
|
+
# @return [String] payment method used by this transaction
|
80
|
+
# @see PAYMENT_METHODS The full list of possible payment
|
81
|
+
# method returned
|
64
82
|
def payment_method
|
65
83
|
PAYMENT_METHODS[params["tipo_pagamento"]]
|
66
84
|
end
|
67
85
|
|
86
|
+
# @return [Integer] number of quotas that the payment was
|
87
|
+
# divided
|
68
88
|
def quotas
|
69
89
|
params["parcelas"].to_i
|
70
90
|
end
|
71
91
|
|
92
|
+
# @return [String] user email address
|
72
93
|
def user_email
|
73
94
|
params["email_consumidor"]
|
74
95
|
end
|
75
96
|
|
97
|
+
# @return [String] additional information provided by the financial
|
98
|
+
# institution regarding this transaction when it has a canceled status
|
99
|
+
# @see http://labs.moip.com.br/referencia/classificacao-de-cancelamento/
|
100
|
+
# The Moip's official documentation regarting payment cancelling
|
76
101
|
def additional_info
|
77
102
|
params["classificacao"]
|
78
103
|
end
|
@@ -1,5 +1,32 @@
|
|
1
1
|
module Moiper
|
2
2
|
module NotificationControllerHelper
|
3
|
+
# Helper method to abstract how to create a notification during the Moip's
|
4
|
+
# NASP request.
|
5
|
+
#
|
6
|
+
# @see http://labs.moip.com.br/referencia/nasp/ Moip's NASP documentation
|
7
|
+
#
|
8
|
+
# @param data [Hash] controller parameters
|
9
|
+
# @yieldparam [Notification] notification update from Moip
|
10
|
+
# @return [Notification]
|
11
|
+
#
|
12
|
+
# @example Usage inside a Rails controller
|
13
|
+
# class OrdersController < ApplicationController
|
14
|
+
# include Moiper::NotificationControllerHelper
|
15
|
+
#
|
16
|
+
# def confirm
|
17
|
+
# moip_notification do |notification|
|
18
|
+
# # Here you can update your database with updated information.
|
19
|
+
# # Ex:
|
20
|
+
# @order = Order.find_by_unique_identifier(notification.id)
|
21
|
+
# @order.update_attribute :status, notification.payment_status
|
22
|
+
# @order.save
|
23
|
+
#
|
24
|
+
# # Moip will recognize the request as successfully if the statuses
|
25
|
+
# # are 200, 201, 202, 203, 204, 205, 206 or 207.
|
26
|
+
# head 200
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
# end
|
3
30
|
def moip_notification(data = params, &block)
|
4
31
|
notification = Notification.new(data)
|
5
32
|
yield notification if block_given?
|
data/lib/moiper/payment.rb
CHANGED
@@ -2,27 +2,44 @@ require "nokogiri"
|
|
2
2
|
|
3
3
|
module Moiper
|
4
4
|
class Payment
|
5
|
-
#
|
6
|
-
#
|
5
|
+
# @return [String] the unique ID you can set for this payment. This is usefull to keep
|
6
|
+
# track of which payment we will receive on the Moip's notification process
|
7
7
|
attr_accessor :id
|
8
8
|
|
9
|
-
#
|
9
|
+
# @return [String] the description of the purchase
|
10
10
|
attr_accessor :description
|
11
11
|
|
12
|
-
#
|
12
|
+
# @return [Float] the amount to be billed from the user
|
13
13
|
attr_accessor :price
|
14
14
|
|
15
|
-
#
|
16
|
-
|
15
|
+
# @return [Float] the amount to be increased from the final price
|
16
|
+
attr_accessor :accretion
|
17
|
+
|
18
|
+
# @return [Float] the amount to be deducted from the final price
|
19
|
+
attr_accessor :deduction
|
20
|
+
|
21
|
+
# @return [String] the URL where the user will be redirected after he finishes the
|
22
|
+
# payment process.
|
17
23
|
attr_accessor :return_url
|
18
24
|
|
19
|
-
#
|
25
|
+
# @return [String] the URL where Moip will send notifications about your purchase updates
|
20
26
|
attr_accessor :notification_url
|
21
27
|
|
22
28
|
# Create a new Moip Payment request
|
29
|
+
#
|
30
|
+
# @param [Hash] options the options to create the payment with
|
31
|
+
# @option options [String] :description {#description}
|
32
|
+
# @option options [String, #to_s] :id {#id}
|
33
|
+
# @option options [#to_f] :price {#price}
|
34
|
+
# @option options [#to_f] :accretion {#accretion}
|
35
|
+
# @option options [#to_f] :deduction {#deduction}
|
36
|
+
# @option options [String, #to_s] :return_url {#return_url}
|
37
|
+
# @option options [String, #to_s] :notification_url {#notification_url}
|
38
|
+
#
|
39
|
+
# @raise [ArgumentError] if description or price options are blank
|
23
40
|
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
|
41
|
+
raise ArgumentError, "You must inform a description" if options[:description].nil? || options[:description].empty?
|
42
|
+
raise ArgumentError, "You must inform a price" if options[:price].nil? || options[:price].to_f <= 0
|
26
43
|
|
27
44
|
options.each do |attribute, value|
|
28
45
|
send "#{attribute}=", value
|
@@ -30,6 +47,7 @@ module Moiper
|
|
30
47
|
end
|
31
48
|
|
32
49
|
# Create the payment XML representation
|
50
|
+
# @return [String] Moip's formatted XML
|
33
51
|
def to_xml
|
34
52
|
builder = Nokogiri::XML::Builder.new(:encoding => "UTF-8") do |xml|
|
35
53
|
xml.EnviarInstrucao {
|
@@ -39,6 +57,8 @@ module Moiper
|
|
39
57
|
|
40
58
|
xml.Valores {
|
41
59
|
xml.Valor price, :moeda => "BRL"
|
60
|
+
xml.Acrescimo accretion, :moeda => "BRL" if accretion
|
61
|
+
xml.Deducao deduction, :moeda => "BRL" if deduction
|
42
62
|
}
|
43
63
|
|
44
64
|
xml.URLNotificacao notification_url if notification_url
|
@@ -50,6 +70,8 @@ module Moiper
|
|
50
70
|
builder.to_xml
|
51
71
|
end
|
52
72
|
|
73
|
+
# Send a new payment request to Moip
|
74
|
+
# @return [Response] the Moip response
|
53
75
|
def checkout
|
54
76
|
request = Request.new
|
55
77
|
request.process(to_xml)
|
data/lib/moiper/railtie.rb
CHANGED
@@ -2,6 +2,7 @@ module Moiper
|
|
2
2
|
class Railtie < Rails::Railtie
|
3
3
|
# Expose Moiper's configuration to the Rails application configuration.
|
4
4
|
#
|
5
|
+
# @example
|
5
6
|
# module MyApplication
|
6
7
|
# class Application < Rails::Application
|
7
8
|
# config.moiper.sandbox = true
|
@@ -11,10 +12,13 @@ module Moiper
|
|
11
12
|
# end
|
12
13
|
config.moiper = Moiper
|
13
14
|
|
15
|
+
# Load notification controller helper
|
14
16
|
initializer "load notification controller helper" do
|
15
17
|
require "moiper/notification_controller_helper"
|
16
18
|
end
|
17
19
|
|
20
|
+
# Automatically read and configure Moiper with the content
|
21
|
+
# of the config/moiper.yml file if it is present
|
18
22
|
initializer "load moiper.yml file" do
|
19
23
|
config_file = Rails.root.join("config", "moiper.yml")
|
20
24
|
|
data/lib/moiper/request.rb
CHANGED
@@ -5,13 +5,17 @@ module Moiper
|
|
5
5
|
class Request
|
6
6
|
CA_FILE = File.expand_path(File.dirname(__FILE__)) + "/cacert.pem"
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
# Process a given payload
|
9
|
+
# @param payload [String]
|
10
|
+
# @return [Response] the response from Moip
|
10
11
|
def process(payload)
|
11
|
-
|
12
|
-
Response.new(
|
12
|
+
response = post(payload)
|
13
|
+
Response.new(response.body)
|
13
14
|
end
|
14
15
|
|
16
|
+
# @!group HTTP handling
|
17
|
+
|
18
|
+
# @return [Net::HTTP::Session] the http session client
|
15
19
|
def client
|
16
20
|
@client ||= Net::HTTP.new(uri.host, uri.port).tap do |http|
|
17
21
|
http.use_ssl = true
|
@@ -20,6 +24,9 @@ module Moiper
|
|
20
24
|
end
|
21
25
|
end
|
22
26
|
|
27
|
+
# @return [Net::HTTP::Post] the http POST request already
|
28
|
+
# configured with the right agent, content type and
|
29
|
+
# basic authentication headers
|
23
30
|
def request
|
24
31
|
@request ||= Net::HTTP::Post.new(uri.path).tap do |request|
|
25
32
|
request.basic_auth Moiper.token, Moiper.key
|
@@ -28,6 +35,8 @@ module Moiper
|
|
28
35
|
end
|
29
36
|
end
|
30
37
|
|
38
|
+
# @!endgroup
|
39
|
+
|
31
40
|
private
|
32
41
|
|
33
42
|
def post(payload)
|
@@ -39,6 +48,8 @@ module Moiper
|
|
39
48
|
@uri ||= URI(Moiper.api_entrypoint + path)
|
40
49
|
end
|
41
50
|
|
51
|
+
# @api private
|
52
|
+
# @return [String] path where the request should be made
|
42
53
|
def path
|
43
54
|
"ws/alpha/EnviarInstrucao/Unica"
|
44
55
|
end
|
data/lib/moiper/response.rb
CHANGED
@@ -1,21 +1,29 @@
|
|
1
1
|
module Moiper
|
2
2
|
class Response
|
3
|
+
# @param body [String] the response body from Moip
|
3
4
|
def initialize(body)
|
4
5
|
@body = body
|
5
6
|
end
|
6
7
|
|
8
|
+
# Detects if the response was successfully
|
9
|
+
# @return [Boolean]
|
7
10
|
def success?
|
8
11
|
parsed_body.css("Status").text == "Sucesso"
|
9
12
|
end
|
10
13
|
|
14
|
+
# @return [String, nil] the URL which the user should be redirected
|
15
|
+
# to finish payment process or nil if the request was not successfully
|
11
16
|
def checkout_url
|
12
17
|
Moiper.api_entrypoint + "Instrucao.do?token=" + token if success?
|
13
18
|
end
|
14
19
|
|
20
|
+
# @return [String] the response token
|
15
21
|
def token
|
16
22
|
parsed_body.css("Token").text
|
17
23
|
end
|
18
24
|
|
25
|
+
# List the possible errors returned by Moip
|
26
|
+
# @return [Array<String>]
|
19
27
|
def errors
|
20
28
|
parsed_body.css("Erro").map(&:text)
|
21
29
|
end
|
data/lib/moiper/version.rb
CHANGED
data/spec/moiper/payment_spec.rb
CHANGED
@@ -82,6 +82,8 @@ describe Moiper::Payment do
|
|
82
82
|
Moiper::Payment.new(
|
83
83
|
:description => "A chair",
|
84
84
|
:price => 1.99,
|
85
|
+
:accretion => 0.5,
|
86
|
+
:deduction => 0.3,
|
85
87
|
:id => "some unique id",
|
86
88
|
:return_url => "http://example.org/thank_you",
|
87
89
|
:notification_url => "http://example.org/moip/notification"
|
@@ -99,9 +101,16 @@ describe Moiper::Payment do
|
|
99
101
|
it { doc.at_css("InstrucaoUnica > URLNotificacao").should_not be_nil }
|
100
102
|
|
101
103
|
it { doc.at_css("InstrucaoUnica > IdProprio").text.should eq "some unique id"}
|
104
|
+
|
102
105
|
it { doc.at_css("Valores > Valor").text.should eq "1.99"}
|
103
106
|
it { doc.at_css("Valores > Valor").attributes["moeda"].value.should eq "BRL"}
|
104
107
|
|
108
|
+
it { doc.at_css("Valores > Acrescimo").text.should eq "0.5"}
|
109
|
+
it { doc.at_css("Valores > Acrescimo").attributes["moeda"].value.should eq "BRL"}
|
110
|
+
|
111
|
+
it { doc.at_css("Valores > Deducao").text.should eq "0.3"}
|
112
|
+
it { doc.at_css("Valores > Deducao").attributes["moeda"].value.should eq "BRL"}
|
113
|
+
|
105
114
|
it { doc.at_css("InstrucaoUnica > URLRetorno").text.should eq "http://example.org/thank_you" }
|
106
115
|
it { doc.at_css("InstrucaoUnica > URLNotificacao").text.should eq "http://example.org/moip/notification" }
|
107
116
|
|
data/spec/moiper/request_spec.rb
CHANGED
@@ -34,8 +34,7 @@ describe Moiper::Request do
|
|
34
34
|
end
|
35
35
|
|
36
36
|
it "has the correct user agent" do
|
37
|
-
|
38
|
-
headers["User-Agent"].should eq "Moiper/#{Moiper::VERSION}"
|
37
|
+
subject.get_fields("user-agent")[0].should eq "Moiper/#{Moiper::VERSION}"
|
39
38
|
end
|
40
39
|
end
|
41
40
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: moiper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-01-
|
12
|
+
date: 2013-01-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: nokogiri
|
16
|
-
requirement: &
|
16
|
+
requirement: &2160708660 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2160708660
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &2160708220 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *2160708220
|
36
36
|
description: Moip payment service integration library.
|
37
37
|
email:
|
38
38
|
- rnavarro1@gmail.com
|
@@ -41,6 +41,7 @@ extensions: []
|
|
41
41
|
extra_rdoc_files: []
|
42
42
|
files:
|
43
43
|
- .gitignore
|
44
|
+
- .travis.yml
|
44
45
|
- Gemfile
|
45
46
|
- LICENSE.txt
|
46
47
|
- README.md
|
@@ -76,7 +77,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
76
77
|
version: '0'
|
77
78
|
segments:
|
78
79
|
- 0
|
79
|
-
hash:
|
80
|
+
hash: -1487992565957500539
|
80
81
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
82
|
none: false
|
82
83
|
requirements:
|
@@ -85,7 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
85
86
|
version: '0'
|
86
87
|
segments:
|
87
88
|
- 0
|
88
|
-
hash:
|
89
|
+
hash: -1487992565957500539
|
89
90
|
requirements: []
|
90
91
|
rubyforge_project:
|
91
92
|
rubygems_version: 1.8.11
|