supreme 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.md +67 -0
- data/lib/supreme.rb +47 -0
- data/lib/supreme/api.rb +148 -0
- data/lib/supreme/response.rb +133 -0
- data/lib/supreme/uri.rb +88 -0
- data/lib/supreme/version.rb +3 -0
- metadata +144 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Fingertips, Manfred Stienstra <manfred@fngtps.com>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
A Ruby client that allows you to do iDEAL transactions through the [Mollie iDEAL API](http://www.mollie.nl/betaaldiensten/ideal).
|
2
|
+
|
3
|
+
## Install
|
4
|
+
|
5
|
+
$ gem install supreme
|
6
|
+
|
7
|
+
## Payment flow
|
8
|
+
|
9
|
+
You start by setting the mode of the library to either :test or :live. The default is :test, so
|
10
|
+
for tests you don't have to change anything.
|
11
|
+
|
12
|
+
Supreme.mode = :live
|
13
|
+
|
14
|
+
You can choose to set the partner (client) id globally so you don't have to include it with every
|
15
|
+
call.
|
16
|
+
|
17
|
+
Supreme.partner_id = '000000'
|
18
|
+
|
19
|
+
Then you get a list of supported banks. Note that this list can change at any time, so be conservative
|
20
|
+
with caching these values.
|
21
|
+
|
22
|
+
Supreme.api.bank_list #=> [["ABN AMRO", "0031"], ["Postbank", "0721"], ["Rabobank", "0021"]]
|
23
|
+
|
24
|
+
When the user has selected a bank, you start a transaction.
|
25
|
+
|
26
|
+
transaction = Supreme.api.fetch(
|
27
|
+
:bank_id => '0031',
|
28
|
+
:amount => 1299,
|
29
|
+
:description => 'A fluffy bunny',
|
30
|
+
:return_url => 'http://example.com/payments/as45re/thanks',
|
31
|
+
:report_url => 'http://example.com/payments/as45re'
|
32
|
+
)
|
33
|
+
transaction.transaction_id #=> '482d599bbcc7795727650330ad65fe9b'
|
34
|
+
|
35
|
+
Keep the transaction_id around for reference and redirect the customer to the indicated URL.
|
36
|
+
|
37
|
+
Location: #{transaction.url}
|
38
|
+
|
39
|
+
Once the transaction is done you will receive a GET on the report_url with a ‘transaction_id’ parameter
|
40
|
+
to indicate that the transaction has changed state. You will need to check the status of the transaction.
|
41
|
+
|
42
|
+
status = Supreme.api.check(
|
43
|
+
:transaction_id => '482d599bbcc7795727650330ad65fe9b'
|
44
|
+
)
|
45
|
+
|
46
|
+
# Note that the status will only be paid? after the first check, for each consecutive call
|
47
|
+
# paid? will be false regardless of the outcome of the transaction.
|
48
|
+
#
|
49
|
+
# We use success? and make sure we don't deliver the product more than once.
|
50
|
+
if status.success?
|
51
|
+
# Update the local status of the payment
|
52
|
+
end
|
53
|
+
|
54
|
+
When the customer returns to your site it returns with its transaction_id attached to your provided URL.
|
55
|
+
You can present a page depending on the status of the payment.
|
56
|
+
|
57
|
+
## Errors
|
58
|
+
|
59
|
+
When an error occurs you get a Supreme::Error object instead of the response object you expected.
|
60
|
+
|
61
|
+
status = Supreme.api.check(
|
62
|
+
:transaction_id => '482d599bbcc7795727650330ad65fe9b'
|
63
|
+
)
|
64
|
+
|
65
|
+
if status.error?
|
66
|
+
log("#{status.message} (#{status.code})")
|
67
|
+
end
|
data/lib/supreme.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# Supreme is a Ruby for doing iDEAL transactions through the Mollie iDEAL API.
|
2
|
+
# http://www.mollie.nl/betaaldiensten/ideal
|
3
|
+
#
|
4
|
+
# To get going read the documentation for Supreme and Supreme::API.
|
5
|
+
|
6
|
+
require 'supreme/uri'
|
7
|
+
require 'supreme/api'
|
8
|
+
require 'supreme/version'
|
9
|
+
require 'supreme/response'
|
10
|
+
|
11
|
+
module Supreme
|
12
|
+
class << self
|
13
|
+
# Holds either :test or :live to signal whether to run in test or live mode. The default
|
14
|
+
# value is :test.
|
15
|
+
attr_accessor :mode
|
16
|
+
|
17
|
+
# Your Mollie Partner ID, you can find it under ‘Accountgegevens’ in the settings for your account on mollie.nl.
|
18
|
+
attr_accessor :partner_id
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns an instance of the API with settings from the Supreme class accessors. If you need to handle
|
22
|
+
# multiple accounts in your application you will need to instantiate multiple API instances yourself.
|
23
|
+
def self.api
|
24
|
+
Supreme::API.new(
|
25
|
+
:mode => mode,
|
26
|
+
:partner_id => partner_id
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Resets the class back to the default settings
|
31
|
+
def self.reset!
|
32
|
+
self.mode = :test
|
33
|
+
self.partner_id = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
reset!
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def self.translate_hash_keys(translation, hash)
|
41
|
+
translated = {}
|
42
|
+
hash.each do |key, value|
|
43
|
+
new_key = translation[key.to_sym] || translation[key.to_s] ||key
|
44
|
+
translated[new_key.to_s] = value
|
45
|
+
end; translated
|
46
|
+
end
|
47
|
+
end
|
data/lib/supreme/api.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'rest'
|
3
|
+
|
4
|
+
module Supreme
|
5
|
+
class API
|
6
|
+
ENDPOINT = ::URI.parse("https://secure.mollie.nl/xml/ideal")
|
7
|
+
|
8
|
+
attr_accessor :mode, :partner_id
|
9
|
+
|
10
|
+
# Creates a new API instance. Normally you would use <tt>Supreme.api</tt> to create an
|
11
|
+
# API instance. If you have to interact with multiple accounts in one process you can
|
12
|
+
# instantiate the API class youself.
|
13
|
+
#
|
14
|
+
# === Options
|
15
|
+
#
|
16
|
+
# * <tt>:mode</tt> – Holds either :test or :live to signal whether to run in test or live mode. The default value is :test.
|
17
|
+
# * <tt>:partner_id</tt> – Your Mollie Partner ID, you can find it under ‘Accountgegevens’ in the settings for your account on mollie.nl.
|
18
|
+
#
|
19
|
+
# === Example
|
20
|
+
#
|
21
|
+
# api = Supreme::API.new(:partner_id => '9834234')
|
22
|
+
# api.test? #=> true
|
23
|
+
# api.banklist
|
24
|
+
def initialize(options={})
|
25
|
+
self.mode = options[:mode] || options['mode'] || :test
|
26
|
+
self.partner_id = options[:partner_id] || options['partner_id']
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns true when we're in test mode and false otherwise
|
30
|
+
def test?
|
31
|
+
self.mode.to_sym == :test
|
32
|
+
end
|
33
|
+
|
34
|
+
# Requests a list of available banks. Turns a Banklist response.
|
35
|
+
# Use Banklist#to_a to get a list of hashes with actual information.
|
36
|
+
#
|
37
|
+
# Supreme.api.banklist.to_a # => [{ :id => '1006', :name => 'ABN AMRO Bank' }, …]
|
38
|
+
#
|
39
|
+
# Returns a Supreme::Error when the call fails.
|
40
|
+
def banklist
|
41
|
+
response = get('banklist')
|
42
|
+
log('Banklist response', response.body)
|
43
|
+
Supreme::Response.for(response.body, Supreme::Banklist)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Starts a new payment by sending payment information. It also configures how the iDEAL
|
47
|
+
# provider handles payment status information. It returns a Supreme::Transaction response
|
48
|
+
# object.
|
49
|
+
#
|
50
|
+
# Returns a Supreme::Error when the call fails.
|
51
|
+
#
|
52
|
+
# === Options
|
53
|
+
#
|
54
|
+
# Note that the <tt>:description</tt> option has a character limit of 29 characters.
|
55
|
+
# Anything after the 29 characters will be silently removed by the API. Note that
|
56
|
+
# this description might be handled by ancient bank systems and anything but ASCII
|
57
|
+
# characters might be mangled or worse.
|
58
|
+
#
|
59
|
+
# ==== Required
|
60
|
+
#
|
61
|
+
# * <tt>:bank_id</tt> – The bank selected by the customer from the <tt>banklist</tt>.
|
62
|
+
# * <tt>:amount</tt> – The amount you want to charge in cents (EURO) (ie. €12,99 is 1299)
|
63
|
+
# * <tt>:description</tt> – Describe what the payment is for (max 29 characters) (ie. ‘Fluffy Bunny (sku 1234)’ )
|
64
|
+
# * <tt>:report_url</tt> – You will receive a GET to this URL with the transaction_id appended in the query (ie. http://example.com/payments?transaction_id=23ad33)
|
65
|
+
# * <tt>:return_url</tt> – The customer is redirected to this URL after the payment is complete. The transaction_id is appended as explained for <tt>:report_url</tt>
|
66
|
+
#
|
67
|
+
# ==== Optional
|
68
|
+
#
|
69
|
+
# * <tt>:partner_id</tt> – Your Mollie Partner ID, you can find it under ‘Accountgegevens’ in the settings for your account on mollie.nl. Note that the Partner ID is only optional if you've set it either on the API instance or using <tt>Supreme.partner_id</tt>.
|
70
|
+
# * <tt>:profile_key</tt> – When your account receives payment from different websites or companies you can set up company profiles. See the Mollie documentation for more information: http://www.mollie.nl/support/documentatie/betaaldiensten/ideal/.
|
71
|
+
#
|
72
|
+
# === Example
|
73
|
+
#
|
74
|
+
# transaction = Supreme.api.fetch({
|
75
|
+
# :bank_id => '0031',
|
76
|
+
# :amount => 1299,
|
77
|
+
# :description => '20 credits for your account',
|
78
|
+
# :report_url => 'http://example.com/payments/ad74hj23',
|
79
|
+
# :return_url => 'http://example.com/payments/ad74hj23/thanks'
|
80
|
+
# })
|
81
|
+
# @purchase.update_attributes!(:transaction_id => transaction.transaction_id)
|
82
|
+
#
|
83
|
+
# See the Supreme::Transaction class for more information.
|
84
|
+
def fetch(options)
|
85
|
+
options = options.dup
|
86
|
+
options[:partner_id] ||= partner_id
|
87
|
+
response = get('fetch', Supreme.translate_hash_keys({
|
88
|
+
:partner_id => :partnerid,
|
89
|
+
:return_url => :returnurl,
|
90
|
+
:report_url => :reporturl
|
91
|
+
}, options))
|
92
|
+
log('Fetch response', response.body)
|
93
|
+
Supreme::Response.for(response.body, Supreme::Transaction)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Requests the status information for a payment. It returns a Supreme::Status
|
97
|
+
# response object.
|
98
|
+
#
|
99
|
+
# Returns a Supreme::Error when the call fails.
|
100
|
+
#
|
101
|
+
# === Options
|
102
|
+
#
|
103
|
+
# * <tt>:transaction_id</tt> – The transaction ID you received earlier when setting up the transaction.
|
104
|
+
#
|
105
|
+
# == Example
|
106
|
+
#
|
107
|
+
# status = Supreme.api.check(:transaction_id => '482d599bbcc7795727650330ad65fe9b')
|
108
|
+
# if status.paid?
|
109
|
+
# @purchase.paid!
|
110
|
+
# end
|
111
|
+
#
|
112
|
+
# See the Supreme::Status class for more information.
|
113
|
+
def check(options)
|
114
|
+
options = options.dup
|
115
|
+
options[:partner_id] ||= partner_id
|
116
|
+
response = get('check', Supreme.translate_hash_keys({
|
117
|
+
:partner_id => :partnerid,
|
118
|
+
}, options))
|
119
|
+
log('Status response', response.body)
|
120
|
+
Supreme::Response.for(response.body, Supreme::Status)
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def endpoint
|
126
|
+
ENDPOINT.dup
|
127
|
+
end
|
128
|
+
|
129
|
+
def query(options={})
|
130
|
+
options = options.dup
|
131
|
+
options[:testmode] = 'true' if test?
|
132
|
+
options == {} ? nil : Supreme::URI.generate_query(options)
|
133
|
+
end
|
134
|
+
|
135
|
+
def get(action, options={})
|
136
|
+
options = options.dup
|
137
|
+
options[:a] = action
|
138
|
+
url = endpoint
|
139
|
+
url.query = query(options)
|
140
|
+
log('GET', url.to_s)
|
141
|
+
REST.get(url.to_s)
|
142
|
+
end
|
143
|
+
|
144
|
+
def log(thing, contents)
|
145
|
+
$stderr.write("\n#{thing}:\n\n#{contents}\n") if $DEBUG
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
module Supreme
|
4
|
+
# The base class for all response classes.
|
5
|
+
class Response
|
6
|
+
attr_accessor :body
|
7
|
+
|
8
|
+
def initialize(body)
|
9
|
+
@body = body
|
10
|
+
end
|
11
|
+
|
12
|
+
def error?
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
# Return an instance of a reponse class based on the contents of the body
|
17
|
+
def self.for(response_body, klass)
|
18
|
+
body = REXML::Document.new(response_body).root
|
19
|
+
if body.elements["/response/item"]
|
20
|
+
::Supreme::Error.new(body)
|
21
|
+
else
|
22
|
+
klass.new(body)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def text(path)
|
29
|
+
@body.get_text(path).to_s
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Response to a banklist request
|
34
|
+
class Banklist < Response
|
35
|
+
def to_a
|
36
|
+
@body.get_elements('//bank').map do |issuer|
|
37
|
+
{ :id => issuer.get_text('bank_id').to_s, :name => issuer.get_text('bank_name').to_s }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Response to a fetch request
|
43
|
+
class Transaction < Response
|
44
|
+
def transaction_id
|
45
|
+
text('//transaction_id')
|
46
|
+
end
|
47
|
+
|
48
|
+
def amount
|
49
|
+
text('//amount')
|
50
|
+
end
|
51
|
+
|
52
|
+
def url
|
53
|
+
text('//URL')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Response to a check request
|
58
|
+
class Status < Response
|
59
|
+
def transaction_id
|
60
|
+
text('//transaction_id')
|
61
|
+
end
|
62
|
+
|
63
|
+
def amount
|
64
|
+
text('//amount')
|
65
|
+
end
|
66
|
+
|
67
|
+
def currency
|
68
|
+
text('//currency')
|
69
|
+
end
|
70
|
+
|
71
|
+
def paid
|
72
|
+
text('//payed')
|
73
|
+
end
|
74
|
+
|
75
|
+
# A payment will return paid? for just one request. If you issue it too early you might never
|
76
|
+
# get a truthy value from this.
|
77
|
+
def paid?
|
78
|
+
paid == 'true'
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns the status of the payment. This is probably the best way to check if the payment has
|
82
|
+
# succeeded. It returns one of the following values:
|
83
|
+
#
|
84
|
+
# * <tt>Open</tt> – Payment is still processing.
|
85
|
+
# * <tt>Success</tt> – The payment was successful.
|
86
|
+
# * <tt>Cancelled</tt> – The payment was explicitly cancelled by the customer.
|
87
|
+
# * <tt>Failure</tt> – The payment failed.
|
88
|
+
# * <tt>Expired</tt> – The customer abandoned the payment, we don't expect them to finish it.
|
89
|
+
# * <tt>CheckedBefore</tt> – You've requested the payment status before.
|
90
|
+
#
|
91
|
+
# You can also check the status of the payment with one of the boolean accessors: open?, success?,
|
92
|
+
# cancelled?, failed?, expired?, and checked_before?.
|
93
|
+
def status
|
94
|
+
text('//status')
|
95
|
+
end
|
96
|
+
|
97
|
+
[
|
98
|
+
['Open', :open?],
|
99
|
+
['Success', :success?],
|
100
|
+
['Cancelled', :cancelled?],
|
101
|
+
['Failure', :failed?],
|
102
|
+
['Expired', :expired?],
|
103
|
+
['CheckedBefore', :checked_before?]
|
104
|
+
].each do |expected, accessor|
|
105
|
+
define_method accessor do
|
106
|
+
status == expected
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def customer
|
111
|
+
{
|
112
|
+
'name' => text('//consumerName'),
|
113
|
+
'account' => text('//consumerAccount'),
|
114
|
+
'city' => text('//consumerCity')
|
115
|
+
}
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Response was an error
|
120
|
+
class Error < Response
|
121
|
+
def error?
|
122
|
+
true
|
123
|
+
end
|
124
|
+
|
125
|
+
def code
|
126
|
+
text('//errorcode')
|
127
|
+
end
|
128
|
+
|
129
|
+
def message
|
130
|
+
text('//message')
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/lib/supreme/uri.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
module Supreme
|
2
|
+
class URI
|
3
|
+
# Borrowed from uri/common.rb, with modifications to support 1.8.x
|
4
|
+
# https://github.com/ruby/ruby/blob/trunk/lib/uri/common.rb
|
5
|
+
|
6
|
+
TBLENCWWWCOMP_ = {} # :nodoc:
|
7
|
+
TBLDECWWWCOMP_ = {} # :nodoc:
|
8
|
+
|
9
|
+
# Encode given +s+ to URL-encoded form data.
|
10
|
+
#
|
11
|
+
# This method doesn't convert *, -, ., 0-9, A-Z, _, a-z, but does convert SP
|
12
|
+
# (ASCII space) to + and converts others to %XX.
|
13
|
+
#
|
14
|
+
# This is an implementation of
|
15
|
+
# http://www.w3.org/TR/html5/forms.html#url-encoded-form-data
|
16
|
+
#
|
17
|
+
# See URI.decode_www_form_component, URI.encode_www_form
|
18
|
+
def self.encode_www_form_component(s)
|
19
|
+
str = s.to_s
|
20
|
+
if RUBY_VERSION < "1.9" && $KCODE =~ /u/i
|
21
|
+
str.gsub(/([^ a-zA-Z0-9_.-]+)/) do
|
22
|
+
'%' + $1.unpack('H2' * Rack::Utils.bytesize($1)).join('%').upcase
|
23
|
+
end.tr(' ', '+')
|
24
|
+
else
|
25
|
+
if TBLENCWWWCOMP_.empty?
|
26
|
+
tbl = {}
|
27
|
+
256.times do |i|
|
28
|
+
tbl[i.chr] = '%%%02X' % i
|
29
|
+
end
|
30
|
+
tbl[' '] = '+'
|
31
|
+
begin
|
32
|
+
TBLENCWWWCOMP_.replace(tbl)
|
33
|
+
TBLENCWWWCOMP_.freeze
|
34
|
+
rescue
|
35
|
+
end
|
36
|
+
end
|
37
|
+
str.gsub(/[^*\-.0-9A-Z_a-z]/) {|m| TBLENCWWWCOMP_[m]}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Decode given +str+ of URL-encoded form data.
|
42
|
+
#
|
43
|
+
# This decods + to SP.
|
44
|
+
#
|
45
|
+
# See URI.encode_www_form_component, URI.decode_www_form
|
46
|
+
def self.decode_www_form_component(str, enc=nil)
|
47
|
+
if TBLDECWWWCOMP_.empty?
|
48
|
+
tbl = {}
|
49
|
+
256.times do |i|
|
50
|
+
h, l = i>>4, i&15
|
51
|
+
tbl['%%%X%X' % [h, l]] = i.chr
|
52
|
+
tbl['%%%x%X' % [h, l]] = i.chr
|
53
|
+
tbl['%%%X%x' % [h, l]] = i.chr
|
54
|
+
tbl['%%%x%x' % [h, l]] = i.chr
|
55
|
+
end
|
56
|
+
tbl['+'] = ' '
|
57
|
+
begin
|
58
|
+
TBLDECWWWCOMP_.replace(tbl)
|
59
|
+
TBLDECWWWCOMP_.freeze
|
60
|
+
rescue
|
61
|
+
end
|
62
|
+
end
|
63
|
+
raise ArgumentError, "invalid %-encoding (#{str})" unless /\A(?:%[0-9a-fA-F]{2}|[^%])*\z/ =~ str
|
64
|
+
str.gsub(/\+|%[0-9a-fA-F]{2}/) {|m| TBLDECWWWCOMP_[m]}
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.decode(value)
|
68
|
+
Supreme::URI.decode_www_form_component(value)
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.encode(value)
|
72
|
+
Supreme::URI.encode_www_form_component(value).gsub("%7E", '~').gsub("+", "%20")
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.parse_query(query_string)
|
76
|
+
return [] if query_string.to_s.strip == ''
|
77
|
+
query_string.strip.split('&').inject([]) do |parsed, pair|
|
78
|
+
parsed << pair.split('=', 2).map { |x| decode(x) }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.generate_query(pairs)
|
83
|
+
pairs.inject([]) do |parts, (key, value)|
|
84
|
+
parts << "#{encode(key)}=#{encode(value)}"
|
85
|
+
end.join('&')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
metadata
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: supreme
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Manfred Stienstra
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-02-11 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: nap
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: rake
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: rdoc
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: mocha
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
type: :development
|
76
|
+
version_requirements: *id004
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: fakeweb
|
79
|
+
prerelease: false
|
80
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 3
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
type: :development
|
90
|
+
version_requirements: *id005
|
91
|
+
description:
|
92
|
+
email:
|
93
|
+
- manfred@fngtps.com
|
94
|
+
executables: []
|
95
|
+
|
96
|
+
extensions: []
|
97
|
+
|
98
|
+
extra_rdoc_files:
|
99
|
+
- LICENSE
|
100
|
+
- README.md
|
101
|
+
files:
|
102
|
+
- lib/supreme/api.rb
|
103
|
+
- lib/supreme/response.rb
|
104
|
+
- lib/supreme/uri.rb
|
105
|
+
- lib/supreme/version.rb
|
106
|
+
- lib/supreme.rb
|
107
|
+
- README.md
|
108
|
+
- LICENSE
|
109
|
+
has_rdoc: true
|
110
|
+
homepage: http://github.com/Fingertips/Supreme
|
111
|
+
licenses: []
|
112
|
+
|
113
|
+
post_install_message:
|
114
|
+
rdoc_options: []
|
115
|
+
|
116
|
+
require_paths:
|
117
|
+
- lib
|
118
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
119
|
+
none: false
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
hash: 3
|
124
|
+
segments:
|
125
|
+
- 0
|
126
|
+
version: "0"
|
127
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
none: false
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
hash: 3
|
133
|
+
segments:
|
134
|
+
- 0
|
135
|
+
version: "0"
|
136
|
+
requirements: []
|
137
|
+
|
138
|
+
rubyforge_project:
|
139
|
+
rubygems_version: 1.6.2
|
140
|
+
signing_key:
|
141
|
+
specification_version: 3
|
142
|
+
summary: Ruby implementation of the Mollie iDEAL API
|
143
|
+
test_files: []
|
144
|
+
|