class GoCardless::Client
Constants
- API_PATH
- BASE_URLS
Attributes
Public Class Methods
# File lib/gocardless/client.rb, line 22 def base_url @base_url || BASE_URLS[GoCardless.environment || :production] end
# File lib/gocardless/client.rb, line 18 def base_url=(url) @base_url = url.sub(%r|/$|, '') end
# File lib/gocardless/client.rb, line 27 def initialize(args = {}) Utils.symbolize_keys! args @app_id = args.fetch(:app_id) { ENV['GOCARDLESS_APP_ID'] } @app_secret = args.fetch(:app_secret) { ENV['GOCARDLESS_APP_SECRET'] } raise ClientError.new("You must provide an app_id") unless @app_id raise ClientError.new("You must provide an app_secret") unless @app_secret @oauth_client = OAuth2::Client.new(@app_id, @app_secret, :site => self.base_url, :token_url => '/oauth/access_token') self.access_token = args[:token] if args[:token] @merchant_id = args[:merchant_id] if args[:merchant_id] end
Public Instance Methods
# File lib/gocardless/client.rb, line 81 def access_token warn "[DEPRECATION] (gocardless-ruby) the behaviour of " + "Client#access_token is deprecated. In future releases it will " + "return the unscoped access token. If you want the scoped access " + "token, use 'scoped_access_token'" self.scoped_access_token end
Set the client’s access token
@param [String] token a string with format "#{token}"
(as returned by {#access_token})
# File lib/gocardless/client.rb, line 105 def access_token=(token) token, scope = token.sub(/^bearer\s+/i, '').split(' ', 2) if scope warn "[DEPRECATION] (gocardless-ruby) merchant_id is now a separate " + "attribute, the manage_merchant scope should no longer be " + "included in the 'token' attribute. See http://git.io/G9y37Q " + "for more info." else scope = '' end @access_token = OAuth2::AccessToken.new(@oauth_client, token) @access_token.params['scope'] = scope set_merchant_id_from_scope(scope) unless @merchant_id end
Issue a DELETE request to the API server
@note this method is for internal use @param [String] path the path that will be added to the API prefix @param [Hash] data a hash of data that will be sent as the request body @return [Hash] hash the parsed response data
# File lib/gocardless/client.rb, line 166 def api_delete(path, data = {}) api_request(:delete, path, :data => data).parsed end
Issue an GET request to the API server
@note this method is for internal use @param [String] path the path that will be added to the API prefix @param [Hash] params query string parameters @return [Hash] hash the parsed response data
# File lib/gocardless/client.rb, line 136 def api_get(path, params = {}) api_request(:get, path, :params => params).parsed end
Issue a POST request to the API server
@note this method is for internal use @param [String] path the path that will be added to the API prefix @param [Hash] data a hash of data that will be sent as the request body @return [Hash] hash the parsed response data
# File lib/gocardless/client.rb, line 146 def api_post(path, data = {}) api_request(:post, path, :data => data).parsed end
Issue a PUT request to the API server
@note this method is for internal use @param [String] path the path that will be added to the API prefix @param [Hash] data a hash of data that will be sent as the request body @return [Hash] hash the parsed response data
# File lib/gocardless/client.rb, line 156 def api_put(path, data = {}) api_request(:put, path, :data => data).parsed end
Issue a request to the API server, returning the full response
@note this method is for internal use @param [Symbol] method the HTTP method to use (e.g. :get
, :post
) @param [String] path the path that will be added to the API prefix @option [Hash] opts additional request options (e.g. form data, params)
# File lib/gocardless/client.rb, line 177 def api_request(method, path, opts = {}) request(method, "#{API_PATH}#{path}", opts) end
# File lib/gocardless/client.rb, line 329 def api_url "#{base_url}#{API_PATH}" end
Get the base URL for the client. If set manually for the instance, that URL will be returned. Otherwise, it will be deferred to Client.base_url
.
# File lib/gocardless/client.rb, line 325 def base_url @base_url || self.class.base_url end
Set the base URL for this client instance. Overrides all other settings (setting the environment globally, setting the Client
class’s base URL).
@param [String] url the base URL to use
# File lib/gocardless/client.rb, line 318 def base_url=(url) @base_url = url end
@method bill(id) @param [String] id of the bill @return [Bill] the Bill
matching the id requested
# File lib/gocardless/client.rb, line 212 def bill(id) Bill.find_with_client(self, id) end
Confirm a newly-created subscription, pre-authorzation or one-off bill. This method also checks that the resource response data includes a valid signature and will raise a {SignatureError} if the signature is invalid.
@param [Hash] params the response parameters returned by the API server @return [Resource] the confirmed resource object
# File lib/gocardless/client.rb, line 269 def confirm_resource(params) params = prepare_params(params) if signature_valid?(params) data = { :resource_id => params[:resource_id], :resource_type => params[:resource_type], } credentials = Base64.encode64("#{@app_id}:#{@app_secret}") credentials = credentials.gsub(/\s/, '') headers = { 'Authorization' => "Basic #{credentials}" } request(:post, "#{api_url}/confirm", :data => data, :headers => headers) # Initialize the correct class according to the resource's type klass = GoCardless.const_get(Utils.camelize(params[:resource_type])) klass.find_with_client(self, params[:resource_id]) else raise SignatureError, 'An invalid signature was detected' end end
Create a new bill under a given pre-authorization @see PreAuthorization#create_bill
@param [Hash] attrs must include :source_id
(the id of the pre_authorization
you want to bill from) and :amount
@return [Bill] the created bill object
# File lib/gocardless/client.rb, line 222 def create_bill(attrs) Bill.new_with_client(self, attrs).save end
Exchange the authorization code for an access token
@param [String] auth_code to exchange for the access_token
@return [String] the access_token
required to make API calls to resources
# File lib/gocardless/client.rb, line 64 def fetch_access_token(auth_code, options) raise ArgumentError, ':redirect_uri required' unless options[:redirect_uri] # Exchange the auth code for an access token @access_token = @oauth_client.auth_code.get_token(auth_code, options) # Use the scope to figure out which merchant we're managing scope = @access_token.params[:scope] || @access_token.params['scope'] set_merchant_id_from_scope(scope) self.scoped_access_token end
@method merchant @return [Merchant] the merchant associated with the client’s access token
# File lib/gocardless/client.rb, line 183 def merchant raise ClientError, 'Access token missing' unless @access_token Merchant.new_with_client(self, api_get("/merchants/#{merchant_id}")) end
Return the merchant id, throwing a proper error if it’s missing.
# File lib/gocardless/client.rb, line 123 def merchant_id raise ClientError, 'No merchant id set' unless @merchant_id @merchant_id end
Generate the URL for creating a new bill. The parameters passed in define various attributes of the bill. Redirecting a user to the resulting URL will show them a page where they can approve or reject the bill described by the parameters. Note that this method automatically includes the nonce, timestamp and signature.
@param [Hash] params the bill parameters @return [String] the generated URL
# File lib/gocardless/client.rb, line 258 def new_bill_url(params) new_limit_url(:bill, params) end
Generate the URL for creating a new subscription. The parameters passed in define various attributes of the subscription. Redirecting a user to the resulting URL will show them a page where they can approve or reject the subscription described by the parameters. Note that this method automatically includes the nonce, timestamp and signature.
@param [Hash] params the subscription parameters @return [String] the generated URL
# File lib/gocardless/client.rb, line 234 def new_subscription_url(params) new_limit_url(:subscription, params) end
Check that resource response data includes a valid signature.
@param [Hash] params the response parameters returned by the API server @return [Boolean] true when valid, false otherwise
# File lib/gocardless/client.rb, line 299 def response_params_valid?(params) params = prepare_params(params) signature_valid?(params) end
@return [String] the scope of the current access token
# File lib/gocardless/client.rb, line 95 def scope if @access_token @access_token.params[:scope] || @access_token.params['scope'] end end
@return [String] a serialized form of the access token with its scope
# File lib/gocardless/client.rb, line 77 def scoped_access_token "#{self.unscoped_access_token} #{self.scope}".strip if @access_token end
@method subscripton(id) @param [String] id of the subscription @return [Subscription] the subscription matching the id requested
# File lib/gocardless/client.rb, line 191 def subscription(id) Subscription.find_with_client(self, id) end
@return [String] a serialized form of the access token without its scope
# File lib/gocardless/client.rb, line 90 def unscoped_access_token @access_token.token if @access_token end
@method user(id) @param [String] id of the user @return [User] the User
matching the id requested
# File lib/gocardless/client.rb, line 205 def user(id) User.find_with_client(self, id) end
# File lib/gocardless/client.rb, line 333 def user_agent @user_agent ||= begin gem_info = "gocardless-ruby/v#{GoCardless::VERSION}" ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby' ruby_version = RUBY_VERSION ruby_version += " p#{RUBY_PATCHLEVEL}" if defined?(RUBY_PATCHLEVEL) comment = ["#{ruby_engine} #{ruby_version}"] comment << RUBY_PLATFORM if defined?(RUBY_PLATFORM) "#{gem_info} (#{comment.join("; ")})" end end
Validates the payload contents of a webhook request.
@param [Hash] params the contents of payload of the webhook @return [Boolean] true when valid, false otherwise
# File lib/gocardless/client.rb, line 310 def webhook_valid?(params) signature_valid?(params) end
Private Instance Methods
Generate a random base64-encoded string
@return [String] a randomly generated string
# File lib/gocardless/client.rb, line 428 def generate_nonce Base64.encode64((0...45).map { rand(256).chr }.join).strip end
Generate the URL for creating a limit of type type
, including the provided params, nonce, timestamp and signature
@param [Symbol] type the limit type (:subscription
, etc) @param [Hash] params the bill parameters @return [String] the generated URL
# File lib/gocardless/client.rb, line 438 def new_limit_url(type, limit_params) url = URI.parse("#{base_url}/connect/#{type}s/new") limit_params[:merchant_id] = merchant_id redirect_uri = limit_params.delete(:redirect_uri) cancel_uri = limit_params.delete(:cancel_uri) state = limit_params.delete(:state) params = { :nonce => generate_nonce, :timestamp => Time.now.getutc.strftime('%Y-%m-%dT%H:%M:%SZ'), :client_id => @app_id, type => limit_params, } params[:redirect_uri] = redirect_uri unless redirect_uri.nil? params[:cancel_uri] = cancel_uri unless cancel_uri.nil? params[:state] = state unless state.nil? sign_params(params) url.query = Utils.normalize_params(params) url.to_s end
Prepare a Hash of parameters for signing. Presence of required parameters is checked and the others are discarded.
@param [Hash] params the parameters to be prepared for signing @return [Hash] the prepared parameters
# File lib/gocardless/client.rb, line 400 def prepare_params(params) # Create a new hash in case is a HashWithIndifferentAccess (keys are # always a String) params = Utils.symbolize_keys(Hash[params]) # Only pull out the relevant parameters, other won't be included in the # signature so will cause false negatives keys = [:resource_id, :resource_type, :resource_uri, :state, :signature] params = Hash[params.select { |k,v| keys.include? k }] (keys - [:state]).each do |key| raise ArgumentError, "Parameters missing #{key}" if !params.key?(key) end params end
Send a request to the GoCardless
API servers
@param [Symbol] method the HTTP method to use (e.g. :get
, :post
) @param [String] path the path fragment of the URL @option [Hash] opts query string parameters
# File lib/gocardless/client.rb, line 359 def request(method, path, opts = {}) raise ClientError, 'Access token missing' unless @access_token opts[:headers] = {} if opts[:headers].nil? opts[:headers]['Accept'] = 'application/json' opts[:headers]['Content-Type'] = 'application/json' unless method == :get opts[:headers]['User-Agent'] = user_agent opts[:body] = MultiJson.encode(opts[:data]) if !opts[:data].nil? path = URI.encode(path) # Reset the URL in case the environment / base URL has been changed. @oauth_client.site = base_url header_keys = opts[:headers].keys.map(&:to_s) if header_keys.map(&:downcase).include?('authorization') @oauth_client.request(method, path, opts) else @access_token.send(method, path, opts) end rescue OAuth2::Error => err raise GoCardless::ApiError.new(err.response) end
Pull the merchant id out of the access scope
# File lib/gocardless/client.rb, line 349 def set_merchant_id_from_scope(scope) perm = scope.split.select {|p| p.start_with?('manage_merchant:') }.first @merchant_id = perm.split(':')[1] if perm end
Add a signature to a Hash of parameters. The signature will be generated from the app secret and the provided parameters, and should be used whenever signed data needs to be sent to GoCardless
(e.g. when creating a new subscription). The signature will be added to the hash under the key :signature
.
@param [Hash] params the parameters to sign @return [Hash] the parameters with the new :signature
key
# File lib/gocardless/client.rb, line 390 def sign_params(params) params[:signature] = Utils.sign_params(params, @app_secret) params end
Check if a hash’s :signature is valid
@param [Hash] params the parameters to check @return [Boolean] whether or not the signature is valid
# File lib/gocardless/client.rb, line 418 def signature_valid?(params) params = params.clone signature = params.delete(:signature) return false unless signature Utils.secure_compare(sign_params(params)[:signature], signature) end