gocardless 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +10 -0
- data/.rspec +1 -0
- data/Gemfile +12 -0
- data/Guardfile +6 -0
- data/LICENSE +22 -0
- data/README.md +15 -0
- data/Rakefile +6 -0
- data/gocardless.gemspec +22 -0
- data/lib/gocardless.rb +32 -0
- data/lib/gocardless/bill.rb +44 -0
- data/lib/gocardless/client.rb +352 -0
- data/lib/gocardless/errors.rb +30 -0
- data/lib/gocardless/merchant.rb +47 -0
- data/lib/gocardless/payment.rb +9 -0
- data/lib/gocardless/pre_authorization.rb +21 -0
- data/lib/gocardless/resource.rb +191 -0
- data/lib/gocardless/subscription.rb +15 -0
- data/lib/gocardless/user.rb +8 -0
- data/lib/gocardless/utils.rb +36 -0
- data/lib/gocardless/version.rb +3 -0
- data/spec/bill_spec.rb +27 -0
- data/spec/client_spec.rb +376 -0
- data/spec/gocardless_spec.rb +41 -0
- data/spec/merchant_spec.rb +31 -0
- data/spec/resource_spec.rb +395 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/utils_spec.rb +67 -0
- metadata +195 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
guard 'rspec', :version => 2, :cli => '--color --format doc' do
|
2
|
+
watch(%r{^spec/.+_spec\.rb$})
|
3
|
+
watch(%r{^lib/gocardless/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
4
|
+
watch('lib/gocardless.rb') { "spec/gocardless_spec.rb" }
|
5
|
+
watch('spec/spec_helper.rb') { "spec" }
|
6
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2011 GoCardless
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+

|
2
|
+
|
3
|
+
## GoCardless Ruby Client Library
|
4
|
+
|
5
|
+
The GoCardless Ruby client provides a simple Ruby interface to the GoCardless
|
6
|
+
API.
|
7
|
+
|
8
|
+
If you want to use the library as an individual merchant, refer to the
|
9
|
+
[merchant guide](https://gocardless.com/docs/ruby/merchant_client_guide). If
|
10
|
+
you want to support multiple merchant accounts, see the
|
11
|
+
[partner guide](https://gocardless.com/docs/ruby/partner_client_guide).
|
12
|
+
|
13
|
+
The full API reference is available at on
|
14
|
+
[rubydoc.info](http://rubydoc.info/github/gocardless/gocardless-ruby/master/frames).
|
15
|
+
|
data/Rakefile
ADDED
data/gocardless.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.expand_path('../lib/gocardless/version', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.add_runtime_dependency "oauth2", "~> 0.5.0.rc1"
|
5
|
+
gem.add_runtime_dependency "json", "~> 1.5.3"
|
6
|
+
|
7
|
+
gem.add_development_dependency 'rspec', '~> 2.6'
|
8
|
+
gem.add_development_dependency 'mocha', '~> 0.9.12'
|
9
|
+
gem.add_development_dependency "yard", "~> 0.7.3"
|
10
|
+
gem.add_development_dependency "redcarpet", "~> 1.17.2"
|
11
|
+
|
12
|
+
gem.authors = ["Harry Marr"]
|
13
|
+
gem.description = %q{A Ruby wrapper for the GoCardless API}
|
14
|
+
gem.email = ['harry@gocardless.com']
|
15
|
+
gem.files = `git ls-files`.split("\n")
|
16
|
+
gem.homepage = 'https://github.com/gocardless/gocardless-ruby'
|
17
|
+
gem.name = 'gocardless'
|
18
|
+
gem.require_paths = ['lib']
|
19
|
+
gem.summary = %q{Ruby wrapper for the GoCardless API}
|
20
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
gem.version = GoCardless::VERSION.dup
|
22
|
+
end
|
data/lib/gocardless.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module GoCardless
|
2
|
+
require 'gocardless/errors'
|
3
|
+
require 'gocardless/utils'
|
4
|
+
require 'gocardless/resource'
|
5
|
+
require 'gocardless/subscription'
|
6
|
+
require 'gocardless/pre_authorization'
|
7
|
+
require 'gocardless/user'
|
8
|
+
require 'gocardless/bill'
|
9
|
+
require 'gocardless/payment'
|
10
|
+
require 'gocardless/merchant'
|
11
|
+
require 'gocardless/client'
|
12
|
+
|
13
|
+
class << self
|
14
|
+
attr_accessor :environment
|
15
|
+
attr_reader :account_details, :client
|
16
|
+
|
17
|
+
def account_details=(details)
|
18
|
+
raise ClientError.new("You must provide a token") unless details[:token]
|
19
|
+
@account_details = details
|
20
|
+
@client = Client.new(details)
|
21
|
+
end
|
22
|
+
|
23
|
+
%w(new_subscription_url new_pre_authorization_url new_bill_url confirm_resource).each do |name|
|
24
|
+
class_eval <<-EOM
|
25
|
+
def #{name}(*args)
|
26
|
+
raise ClientError.new('Need to set account_details first') unless @client
|
27
|
+
@client.send(:#{name}, *args)
|
28
|
+
end
|
29
|
+
EOM
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module GoCardless
|
2
|
+
class Bill < Resource
|
3
|
+
self.endpoint = '/bills/:id'
|
4
|
+
|
5
|
+
creatable
|
6
|
+
|
7
|
+
attr_accessor :amount
|
8
|
+
attr_accessor :source_type
|
9
|
+
attr_accessor :description
|
10
|
+
|
11
|
+
# @attribute source_id
|
12
|
+
# @return [Integer] the ID of the bill's source (eg subscription, pre_authorization)
|
13
|
+
attr_accessor :source_id
|
14
|
+
|
15
|
+
reference_accessor :merchant_id, :user_id, :payment_id
|
16
|
+
date_accessor :created_at
|
17
|
+
|
18
|
+
def source
|
19
|
+
klass = GoCardless.const_get(Utils.camelize(source_type.to_s))
|
20
|
+
klass.find_with_client(client, @source_id)
|
21
|
+
end
|
22
|
+
|
23
|
+
def source=(obj)
|
24
|
+
klass = obj.class.to_s.split(':').last
|
25
|
+
if !%w{Subscription PreAuthorization}.include?(klass)
|
26
|
+
raise ArgumentError, ("Object must be an instance of Subscription or "
|
27
|
+
"PreAuthorization")
|
28
|
+
end
|
29
|
+
@source_id = obj.id
|
30
|
+
@source_type = Utils.underscore(klass)
|
31
|
+
end
|
32
|
+
|
33
|
+
def save
|
34
|
+
save_data({
|
35
|
+
:bill => {
|
36
|
+
:pre_authorization_id => self.source_id,
|
37
|
+
:amount => self.amount,
|
38
|
+
:description => self.description,
|
39
|
+
}
|
40
|
+
})
|
41
|
+
self
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,352 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'json'
|
3
|
+
require 'oauth2'
|
4
|
+
require 'openssl'
|
5
|
+
require 'uri'
|
6
|
+
require 'cgi'
|
7
|
+
require 'time'
|
8
|
+
|
9
|
+
module GoCardless
|
10
|
+
class Client
|
11
|
+
BASE_URLS = {
|
12
|
+
:production => 'https://gocardless.com',
|
13
|
+
:sandbox => 'https://sandbox.gocardless.com',
|
14
|
+
}
|
15
|
+
API_PATH = '/api/v1'
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def base_url=(url)
|
19
|
+
@base_url = url.sub(%r|/$|, '')
|
20
|
+
end
|
21
|
+
|
22
|
+
def base_url
|
23
|
+
@base_url || BASE_URLS[GoCardless.environment || :production]
|
24
|
+
end
|
25
|
+
|
26
|
+
def api_url
|
27
|
+
"#{base_url}#{API_PATH}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(args = {})
|
32
|
+
Utils.symbolize_keys! args
|
33
|
+
@app_id = args[:app_id]
|
34
|
+
@app_secret = args[:app_secret]
|
35
|
+
raise ClientError.new("You must provide an app_id") unless @app_id
|
36
|
+
raise ClientError.new("You must provide an app_secret") unless @app_secret
|
37
|
+
|
38
|
+
@oauth_client = OAuth2::Client.new(@app_id, @app_secret,
|
39
|
+
:site => self.class.base_url,
|
40
|
+
:token_url => '/oauth/access_token')
|
41
|
+
|
42
|
+
self.access_token = args[:token] if args[:token]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Generate the OAuth authorize url
|
46
|
+
# @param [Hash] options parameters to be included in the url.
|
47
|
+
# +:redirect_uri+ is required.
|
48
|
+
# @return [String] the authorize url
|
49
|
+
def authorize_url(options)
|
50
|
+
raise ArgumentError, ':redirect_uri required' unless options[:redirect_uri]
|
51
|
+
params = {
|
52
|
+
:client_id => @app_id,
|
53
|
+
:response_type => 'code',
|
54
|
+
:scope => 'manage_merchant'
|
55
|
+
}
|
56
|
+
@oauth_client.authorize_url(params.merge(options))
|
57
|
+
end
|
58
|
+
alias :new_merchant_url :authorize_url
|
59
|
+
|
60
|
+
# @method fetch_access_token(auth_code, options)
|
61
|
+
# @param [String] auth_code to exchange for the access_token
|
62
|
+
# @return [String] the access_token required to make API calls to resources
|
63
|
+
def fetch_access_token(auth_code, options)
|
64
|
+
raise ArgumentError, ':redirect_uri required' unless options[:redirect_uri]
|
65
|
+
@access_token = @oauth_client.auth_code.get_token(auth_code, options)
|
66
|
+
self.access_token
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [String] a serialized form of the access token with its scope
|
70
|
+
def access_token
|
71
|
+
if @access_token
|
72
|
+
scope = @access_token.params[:scope] || @access_token.params['scope']
|
73
|
+
"#{@access_token.token} #{scope}".strip
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Set the client's access token
|
78
|
+
#
|
79
|
+
# @param [String] token a string with format <code>"#{token} #{scope}"</code>
|
80
|
+
# (as returned by {#access_token})
|
81
|
+
def access_token=(token)
|
82
|
+
token, scope = token.split(' ', 2)
|
83
|
+
if scope.nil?
|
84
|
+
raise ArgumentError, ('Access token missing scope. Use format '
|
85
|
+
'<token> <scope>')
|
86
|
+
end
|
87
|
+
@access_token = OAuth2::AccessToken.new(@oauth_client, token)
|
88
|
+
@access_token.params['scope'] = scope
|
89
|
+
end
|
90
|
+
|
91
|
+
# Issue an GET request to the API server
|
92
|
+
#
|
93
|
+
# @note this method is for internal use
|
94
|
+
# @param [String] path the path that will be added to the API prefix
|
95
|
+
# @param [Hash] params query string parameters
|
96
|
+
# @return [Hash] hash the parsed response data
|
97
|
+
def api_get(path, params = {})
|
98
|
+
request(:get, "#{API_PATH}#{path}", :params => params).parsed
|
99
|
+
end
|
100
|
+
|
101
|
+
# Issue a POST request to the API server
|
102
|
+
#
|
103
|
+
# @note this method is for internal use
|
104
|
+
# @param [String] path the path that will be added to the API prefix
|
105
|
+
# @param [Hash] data a hash of data that will be sent as the request body
|
106
|
+
# @return [Hash] hash the parsed response data
|
107
|
+
def api_post(path, data = {})
|
108
|
+
request(:post, "#{API_PATH}#{path}", :data => data).parsed
|
109
|
+
end
|
110
|
+
|
111
|
+
# Issue a PUT request to the API server
|
112
|
+
#
|
113
|
+
# @note this method is for internal use
|
114
|
+
# @param [String] path the path that will be added to the API prefix
|
115
|
+
# @param [Hash] data a hash of data that will be sent as the request body
|
116
|
+
# @return [Hash] hash the parsed response data
|
117
|
+
def api_put(path, data = {})
|
118
|
+
request(:put, "#{API_PATH}#{path}", :data => data).parsed
|
119
|
+
end
|
120
|
+
|
121
|
+
# @method merchant
|
122
|
+
# @return [Merchant] the merchant associated with the client's access token
|
123
|
+
def merchant
|
124
|
+
raise ClientError, 'Access token missing' unless @access_token
|
125
|
+
Merchant.new_with_client(self, api_get("/merchants/#{merchant_id}"))
|
126
|
+
end
|
127
|
+
|
128
|
+
# @method subscripton(id)
|
129
|
+
# @param [String] id of the subscription
|
130
|
+
# @return [Subscription] the subscription matching the id requested
|
131
|
+
def subscription(id)
|
132
|
+
Subscription.find_with_client(self, id)
|
133
|
+
end
|
134
|
+
|
135
|
+
# @method pre_authorization(id)
|
136
|
+
# @param [String] id of the pre_authorization
|
137
|
+
# @return [PreAuthorization] the pre_authorization matching the id requested
|
138
|
+
def pre_authorization(id)
|
139
|
+
PreAuthorization.find_with_client(self, id)
|
140
|
+
end
|
141
|
+
|
142
|
+
# @method user(id)
|
143
|
+
# @param [String] id of the user
|
144
|
+
# @return [User] the User matching the id requested
|
145
|
+
def user(id)
|
146
|
+
User.find_with_client(self, id)
|
147
|
+
end
|
148
|
+
|
149
|
+
# @method bill(id)
|
150
|
+
# @param [String] id of the bill
|
151
|
+
# @return [Bill] the Bill matching the id requested
|
152
|
+
def bill(id)
|
153
|
+
Bill.find_with_client(self, id)
|
154
|
+
end
|
155
|
+
|
156
|
+
# @method payment(id)
|
157
|
+
# @param [String] id of the payment
|
158
|
+
# @return [Payment] the payment matching the id requested
|
159
|
+
def payment(id)
|
160
|
+
Payment.find_with_client(self, id)
|
161
|
+
end
|
162
|
+
|
163
|
+
# Create a new bill under a given pre-authorization
|
164
|
+
# @see PreAuthorization#create_bill
|
165
|
+
#
|
166
|
+
# @param [Hash] attrs must include +:pre_authorization_id+ and +:amount+
|
167
|
+
# @return [Bill] the created bill object
|
168
|
+
def create_bill(attrs)
|
169
|
+
Bill.new_with_client(self, attrs).save
|
170
|
+
end
|
171
|
+
|
172
|
+
# Generate the URL for creating a new subscription. The parameters passed
|
173
|
+
# in define various attributes of the subscription. Redirecting a user to
|
174
|
+
# the resulting URL will show them a page where they can approve or reject
|
175
|
+
# the subscription described by the parameters. Note that this method
|
176
|
+
# automatically includes the nonce, timestamp and signature.
|
177
|
+
#
|
178
|
+
# @param [Hash] params the subscription parameters
|
179
|
+
# @return [String] the generated URL
|
180
|
+
def new_subscription_url(params)
|
181
|
+
new_limit_url(:subscription, params)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Generate the URL for creating a new pre authorization. The parameters
|
185
|
+
# passed in define various attributes of the pre authorization. Redirecting
|
186
|
+
# a user to the resulting URL will show them a page where they can approve
|
187
|
+
# or reject the pre authorization described by the parameters. Note that
|
188
|
+
# this method automatically includes the nonce, timestamp and signature.
|
189
|
+
#
|
190
|
+
# @param [Hash] params the pre authorization parameters
|
191
|
+
# @return [String] the generated URL
|
192
|
+
def new_pre_authorization_url(params)
|
193
|
+
new_limit_url(:pre_authorization, params)
|
194
|
+
end
|
195
|
+
|
196
|
+
# Generate the URL for creating a new bill. The parameters passed in define
|
197
|
+
# various attributes of the bill. Redirecting a user to the resulting URL
|
198
|
+
# will show them a page where they can approve or reject the bill described
|
199
|
+
# by the parameters. Note that this method automatically includes the
|
200
|
+
# nonce, timestamp and signature.
|
201
|
+
#
|
202
|
+
# @param [Hash] params the bill parameters
|
203
|
+
# @return [String] the generated URL
|
204
|
+
def new_bill_url(params)
|
205
|
+
new_limit_url(:bill, params)
|
206
|
+
end
|
207
|
+
|
208
|
+
# Confirm a newly-created subscription, pre-authorzation or one-off
|
209
|
+
# payment. This method also checks that the resource response data includes
|
210
|
+
# a valid signature and will raise a {SignatureError} if the signature is
|
211
|
+
# invalid.
|
212
|
+
#
|
213
|
+
# @param [Hash] params the response parameters returned by the API server
|
214
|
+
# @return [Resource] the confirmed resource object
|
215
|
+
def confirm_resource(params)
|
216
|
+
params = Utils.symbolize_keys(params)
|
217
|
+
# Only pull out the relevant parameters, other won't be included in the
|
218
|
+
# signature so will cause false negatives
|
219
|
+
keys = [:resource_id, :resource_type, :resource_uri, :state, :signature]
|
220
|
+
params = Hash[params.select { |k,v| keys.include? k }]
|
221
|
+
(keys - [:state]).each do |key|
|
222
|
+
raise ArgumentError, "Parameters missing #{key}" if !params.key?(key)
|
223
|
+
end
|
224
|
+
|
225
|
+
if signature_valid?(params)
|
226
|
+
data = {
|
227
|
+
:resource_id => params[:resource_id],
|
228
|
+
:resource_type => params[:resource_type],
|
229
|
+
}
|
230
|
+
|
231
|
+
credentials = Base64.encode64("#{@app_id}:#{@app_secret}")
|
232
|
+
credentials = credentials.gsub(/\s/, '')
|
233
|
+
headers = {
|
234
|
+
'Authorization' => "Basic #{credentials}"
|
235
|
+
}
|
236
|
+
request(:post, "#{self.class.api_url}/confirm", :data => data,
|
237
|
+
:headers => headers)
|
238
|
+
|
239
|
+
# Initialize the correct class according to the resource's type
|
240
|
+
klass = GoCardless.const_get(Utils.camelize(params[:resource_type]))
|
241
|
+
klass.find_with_client(self, params[:resource_id])
|
242
|
+
else
|
243
|
+
raise SignatureError, 'An invalid signature was detected'
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
private
|
248
|
+
|
249
|
+
# Convert a hash into query-string style parameters
|
250
|
+
def encode_params(params, ns = nil)
|
251
|
+
params.map do |key,val|
|
252
|
+
key = ns ? "#{ns}[#{key.is_a?(Integer) ? '' : key.to_s}]" : key.to_s
|
253
|
+
case val
|
254
|
+
when Hash
|
255
|
+
encode_params(val, key)
|
256
|
+
when Array
|
257
|
+
encode_params(Hash[(1..val.length).zip(val)], key)
|
258
|
+
else
|
259
|
+
"#{CGI.escape(key)}=#{CGI.escape(val.to_s)}"
|
260
|
+
end
|
261
|
+
end.sort * '&'
|
262
|
+
end
|
263
|
+
|
264
|
+
# Send a request to the GoCardless API servers
|
265
|
+
#
|
266
|
+
# @param [Symbol] method the HTTP method to use (e.g. +:get+, +:post+)
|
267
|
+
# @param [String] path the path fragment of the URL
|
268
|
+
# @option [String] body the request body
|
269
|
+
# @option [Hash] params query string parameters
|
270
|
+
def request(method, path, opts = {})
|
271
|
+
raise ClientError, 'Access token missing' unless @access_token
|
272
|
+
opts[:headers] = {} if opts[:headers].nil?
|
273
|
+
opts[:headers]['Accept'] = 'application/json'
|
274
|
+
opts[:headers]['Content-Type'] = 'application/json' unless method == :get
|
275
|
+
opts[:body] = JSON.generate(opts[:data]) if !opts[:data].nil?
|
276
|
+
header_keys = opts[:headers].keys.map(&:to_s)
|
277
|
+
if header_keys.map(&:downcase).include?('authorization')
|
278
|
+
@oauth_client.request(method, path, opts)
|
279
|
+
else
|
280
|
+
@access_token.send(method, path, opts)
|
281
|
+
end
|
282
|
+
rescue OAuth2::Error => err
|
283
|
+
raise GoCardless::ApiError.new(err.response)
|
284
|
+
end
|
285
|
+
|
286
|
+
# Add a signature to a Hash of parameters. The signature will be generated
|
287
|
+
# from the app secret and the provided parameters, and should be used
|
288
|
+
# whenever signed data needs to be sent to GoCardless (e.g. when creating
|
289
|
+
# a new subscription). The signature will be added to the hash under the
|
290
|
+
# key +:signature+.
|
291
|
+
#
|
292
|
+
# @param [Hash] params the parameters to sign
|
293
|
+
# @return [Hash] the parameters with the new +:signature+ key
|
294
|
+
def sign_params(params)
|
295
|
+
msg = encode_params(params)
|
296
|
+
digest = OpenSSL::Digest::Digest.new('sha256')
|
297
|
+
params[:signature] = OpenSSL::HMAC.hexdigest(digest, @app_secret, msg)
|
298
|
+
params
|
299
|
+
end
|
300
|
+
|
301
|
+
# Check if a hash's :signature is valid
|
302
|
+
#
|
303
|
+
# @param [Hash] params the parameters to check
|
304
|
+
# @return [Boolean] whether or not the signature is valid
|
305
|
+
def signature_valid?(params)
|
306
|
+
params = params.clone
|
307
|
+
signature = params.delete(:signature)
|
308
|
+
sign_params(params)[:signature] == signature
|
309
|
+
end
|
310
|
+
|
311
|
+
# Generate a random base64-encoded string
|
312
|
+
#
|
313
|
+
# @return [String] a randomly generated string
|
314
|
+
def generate_nonce
|
315
|
+
Base64.encode64((0...45).map { rand(256).chr }.join).strip
|
316
|
+
end
|
317
|
+
|
318
|
+
# Generate the URL for creating a limit of type +type+, including the
|
319
|
+
# provided params, nonce, timestamp and signature
|
320
|
+
#
|
321
|
+
# @param [Symbol] type the limit type (+:subscription+, etc)
|
322
|
+
# @param [Hash] params the bill parameters
|
323
|
+
# @return [String] the generated URL
|
324
|
+
def new_limit_url(type, limit_params)
|
325
|
+
url = URI.parse("#{self.class.base_url}/connect/#{type}s/new")
|
326
|
+
|
327
|
+
limit_params[:merchant_id] = merchant_id
|
328
|
+
redirect_uri = limit_params.delete(:redirect_uri)
|
329
|
+
|
330
|
+
params = {
|
331
|
+
:nonce => generate_nonce,
|
332
|
+
:timestamp => Time.now.getutc.strftime('%Y-%m-%dT%H:%M:%SZ'),
|
333
|
+
:client_id => @app_id,
|
334
|
+
type => limit_params,
|
335
|
+
}
|
336
|
+
params[:redirect_uri] = redirect_uri unless redirect_uri.nil?
|
337
|
+
|
338
|
+
sign_params(params)
|
339
|
+
|
340
|
+
url.query = encode_params(params)
|
341
|
+
url.to_s
|
342
|
+
end
|
343
|
+
|
344
|
+
def merchant_id
|
345
|
+
raise ClientError, 'Access token missing' unless @access_token
|
346
|
+
scope = @access_token.params['scope'].split
|
347
|
+
perm = scope.select {|p| p.start_with?('manage_merchant:') }.first
|
348
|
+
perm.split(':')[1]
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|