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
 
| 
         @@ -0,0 +1,30 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rubygems'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module GoCardless
         
     | 
| 
      
 5 
     | 
    
         
            +
              class Error < StandardError
         
     | 
| 
      
 6 
     | 
    
         
            +
              end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              class ApiError < Error
         
     | 
| 
      
 9 
     | 
    
         
            +
                attr_reader :response, :code, :description
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def initialize(response)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @response = response
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @code = response.status
         
     | 
| 
      
 14 
     | 
    
         
            +
                  body = JSON.parse(response.body) rescue nil
         
     | 
| 
      
 15 
     | 
    
         
            +
                  if body.is_a? Hash
         
     | 
| 
      
 16 
     | 
    
         
            +
                    @description = body["error"] || response.body.strip || "Unknown error"
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def to_s
         
     | 
| 
      
 21 
     | 
    
         
            +
                  "#{super} [#{self.code}] #{self.description}"
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              class ClientError < Error
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              class SignatureError < Error
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,47 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module GoCardless
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Merchant < Resource
         
     | 
| 
      
 3 
     | 
    
         
            +
                self.endpoint = '/merchants/:id'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                attr_accessor :name
         
     | 
| 
      
 6 
     | 
    
         
            +
                attr_accessor :description
         
     | 
| 
      
 7 
     | 
    
         
            +
                attr_accessor :email
         
     | 
| 
      
 8 
     | 
    
         
            +
                attr_accessor :first_name
         
     | 
| 
      
 9 
     | 
    
         
            +
                attr_accessor :last_name
         
     | 
| 
      
 10 
     | 
    
         
            +
                date_accessor :created_at
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def subscriptions(params = {})
         
     | 
| 
      
 13 
     | 
    
         
            +
                  path = "/merchants/#{self.id}/subscriptions"
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @client.api_get(path, params).map do |attrs|
         
     | 
| 
      
 15 
     | 
    
         
            +
                    GoCardless::Subscription.new_with_client(@client, attrs)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def pre_authorizations(params = {})
         
     | 
| 
      
 20 
     | 
    
         
            +
                  path = "/merchants/#{self.id}/pre_authorizations"
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @client.api_get(path, params).map do |attrs|
         
     | 
| 
      
 22 
     | 
    
         
            +
                    GoCardless::PreAuthorization.new_with_client(@client, attrs)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def users(params = {})
         
     | 
| 
      
 27 
     | 
    
         
            +
                  path = "/merchants/#{self.id}/users"
         
     | 
| 
      
 28 
     | 
    
         
            +
                  @client.api_get(path, params).map do |attrs|
         
     | 
| 
      
 29 
     | 
    
         
            +
                    GoCardless::User.new_with_client(@client, attrs)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def bills(params = {})
         
     | 
| 
      
 34 
     | 
    
         
            +
                  path = "/merchants/#{self.id}/bills"
         
     | 
| 
      
 35 
     | 
    
         
            +
                  @client.api_get(path, params).map do |attrs|
         
     | 
| 
      
 36 
     | 
    
         
            +
                    GoCardless::Bill.new_with_client(@client, attrs)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def payments(params = {})
         
     | 
| 
      
 41 
     | 
    
         
            +
                  path = "/merchants/#{self.id}/payments"
         
     | 
| 
      
 42 
     | 
    
         
            +
                  @client.api_get(path, params).map do |attrs|
         
     | 
| 
      
 43 
     | 
    
         
            +
                    GoCardless::Payment.new_with_client(@client, attrs)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module GoCardless
         
     | 
| 
      
 2 
     | 
    
         
            +
              class PreAuthorization < Resource
         
     | 
| 
      
 3 
     | 
    
         
            +
                self.endpoint = '/pre_authorizations/:id'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                attr_accessor :max_amount, :currency, :amount, :interval_length,
         
     | 
| 
      
 6 
     | 
    
         
            +
                              :interval_unit, :description
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                reference_accessor :merchant_id, :user_id
         
     | 
| 
      
 9 
     | 
    
         
            +
                date_accessor :expires_at, :created_at
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                # Create a new bill under this pre-authorization. Similar to
         
     | 
| 
      
 12 
     | 
    
         
            +
                # {Client#create_bill}, but only requires the amount to be specified.
         
     | 
| 
      
 13 
     | 
    
         
            +
                #
         
     | 
| 
      
 14 
     | 
    
         
            +
                # @option attrs [amount] amount the bill amount in pence
         
     | 
| 
      
 15 
     | 
    
         
            +
                # @return [Bill] the created bill object
         
     | 
| 
      
 16 
     | 
    
         
            +
                def create_bill(attrs)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  Bill.new_with_client(client, attrs.merge(:source => self)).save
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,191 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'date'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module GoCardless
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Resource
         
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize(hash = {})
         
     | 
| 
      
 6 
     | 
    
         
            +
                  # Handle sub resources
         
     | 
| 
      
 7 
     | 
    
         
            +
                  sub_resource_uris = hash.delete('sub_resource_uris')
         
     | 
| 
      
 8 
     | 
    
         
            +
                  unless sub_resource_uris.nil?
         
     | 
| 
      
 9 
     | 
    
         
            +
                    # Need to define a method for each sub resource
         
     | 
| 
      
 10 
     | 
    
         
            +
                    sub_resource_uris.each do |name,uri|
         
     | 
| 
      
 11 
     | 
    
         
            +
                      uri = URI.parse(uri)
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                      # Convert the query string to a hash
         
     | 
| 
      
 14 
     | 
    
         
            +
                      default_query = if uri.query.nil? || uri.query == ''
         
     | 
| 
      
 15 
     | 
    
         
            +
                        nil
         
     | 
| 
      
 16 
     | 
    
         
            +
                      else
         
     | 
| 
      
 17 
     | 
    
         
            +
                        Hash[CGI.parse(uri.query).map { |k,v| [k,v.first] }]
         
     | 
| 
      
 18 
     | 
    
         
            +
                      end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                      # Strip api prefix from path
         
     | 
| 
      
 21 
     | 
    
         
            +
                      path = uri.path.sub(%r{^/api/v\d+}, '')
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                      # Modify the instance's metaclass to add the method
         
     | 
| 
      
 24 
     | 
    
         
            +
                      metaclass = class << self; self; end
         
     | 
| 
      
 25 
     | 
    
         
            +
                      metaclass.send(:define_method, name) do |*args|
         
     | 
| 
      
 26 
     | 
    
         
            +
                        # 'name' will be something like 'bills', convert it to Bill and
         
     | 
| 
      
 27 
     | 
    
         
            +
                        # look up the resource class with that name
         
     | 
| 
      
 28 
     | 
    
         
            +
                        class_name = Utils.camelize(Utils.singularize(name.to_s))
         
     | 
| 
      
 29 
     | 
    
         
            +
                        klass = GoCardless.const_get(class_name)
         
     | 
| 
      
 30 
     | 
    
         
            +
                        # Convert the results to instances of the looked-up class
         
     | 
| 
      
 31 
     | 
    
         
            +
                        params = args.first || {}
         
     | 
| 
      
 32 
     | 
    
         
            +
                        query = default_query.nil? ? nil : default_query.merge(params)
         
     | 
| 
      
 33 
     | 
    
         
            +
                        client.api_get(path, query).map do |attrs|
         
     | 
| 
      
 34 
     | 
    
         
            +
                          klass.new(attrs).tap { |m| m.client = client }
         
     | 
| 
      
 35 
     | 
    
         
            +
                        end
         
     | 
| 
      
 36 
     | 
    
         
            +
                      end
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  # Set resource attribute values
         
     | 
| 
      
 41 
     | 
    
         
            +
                  hash.each { |key,val| send("#{key}=", val) if respond_to?("#{key}=") }
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                attr_writer :client
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 47 
     | 
    
         
            +
                  attr_accessor :endpoint
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  def new_with_client(client, attrs = {})
         
     | 
| 
      
 50 
     | 
    
         
            +
                    self.new(attrs).tap { |obj| obj.client = client }
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  def find_with_client(client_obj, id)
         
     | 
| 
      
 54 
     | 
    
         
            +
                    path = endpoint.gsub(':id', id.to_s)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    data = client_obj.api_get(path)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    obj = self.new(data)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    obj.client = client_obj
         
     | 
| 
      
 58 
     | 
    
         
            +
                    obj
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  def find(id)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    message = "Merchant details not found, set GoCardless.account_details"
         
     | 
| 
      
 63 
     | 
    
         
            +
                    raise Error, message unless GoCardless.client
         
     | 
| 
      
 64 
     | 
    
         
            +
                    self.find_with_client(GoCardless.client, id)
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                  def date_writer(*args)
         
     | 
| 
      
 68 
     | 
    
         
            +
                    args.each do |attr|
         
     | 
| 
      
 69 
     | 
    
         
            +
                      define_method("#{attr.to_s}=".to_sym) do |date|
         
     | 
| 
      
 70 
     | 
    
         
            +
                        date = date.is_a?(String) ? DateTime.parse(date) : date
         
     | 
| 
      
 71 
     | 
    
         
            +
                        instance_variable_set("@#{attr}", date)
         
     | 
| 
      
 72 
     | 
    
         
            +
                      end
         
     | 
| 
      
 73 
     | 
    
         
            +
                    end
         
     | 
| 
      
 74 
     | 
    
         
            +
                  end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  def date_accessor(*args)
         
     | 
| 
      
 77 
     | 
    
         
            +
                    attr_reader *args
         
     | 
| 
      
 78 
     | 
    
         
            +
                    date_writer *args
         
     | 
| 
      
 79 
     | 
    
         
            +
                  end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  def reference_reader(*args)
         
     | 
| 
      
 82 
     | 
    
         
            +
                    attr_reader *args
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                    args.each do |attr|
         
     | 
| 
      
 85 
     | 
    
         
            +
                      if !attr.to_s.end_with?('_id')
         
     | 
| 
      
 86 
     | 
    
         
            +
                        raise ArgumentError, 'reference_reader args must end with _id'
         
     | 
| 
      
 87 
     | 
    
         
            +
                      end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                      name = attr.to_s.sub(/_id$/, '')
         
     | 
| 
      
 90 
     | 
    
         
            +
                      define_method(name.to_sym) do
         
     | 
| 
      
 91 
     | 
    
         
            +
                        obj_id = instance_variable_get("@#{attr}")
         
     | 
| 
      
 92 
     | 
    
         
            +
                        klass = GoCardless.const_get(Utils.camelize(name))
         
     | 
| 
      
 93 
     | 
    
         
            +
                        klass.find_with_client(client, obj_id)
         
     | 
| 
      
 94 
     | 
    
         
            +
                      end
         
     | 
| 
      
 95 
     | 
    
         
            +
                    end
         
     | 
| 
      
 96 
     | 
    
         
            +
                  end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                  def reference_writer(*args)
         
     | 
| 
      
 99 
     | 
    
         
            +
                    attr_writer *args
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                    args.each do |attr|
         
     | 
| 
      
 102 
     | 
    
         
            +
                      if !attr.to_s.end_with?('_id')
         
     | 
| 
      
 103 
     | 
    
         
            +
                        raise ArgumentError, 'reference_writer args must end with _id'
         
     | 
| 
      
 104 
     | 
    
         
            +
                      end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                      name = attr.to_s.sub(/_id$/, '')
         
     | 
| 
      
 107 
     | 
    
         
            +
                      define_method("#{name}=".to_sym) do |obj|
         
     | 
| 
      
 108 
     | 
    
         
            +
                        klass = GoCardless.const_get(Utils.camelize(name))
         
     | 
| 
      
 109 
     | 
    
         
            +
                        if !obj.is_a?(klass)
         
     | 
| 
      
 110 
     | 
    
         
            +
                          raise ArgumentError, "Object must be an instance of #{klass}"
         
     | 
| 
      
 111 
     | 
    
         
            +
                        end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                        instance_variable_set("@#{attr}", obj.id)
         
     | 
| 
      
 114 
     | 
    
         
            +
                      end
         
     | 
| 
      
 115 
     | 
    
         
            +
                    end
         
     | 
| 
      
 116 
     | 
    
         
            +
                  end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                  def reference_accessor(*args)
         
     | 
| 
      
 119 
     | 
    
         
            +
                    reference_reader *args
         
     | 
| 
      
 120 
     | 
    
         
            +
                    reference_writer *args
         
     | 
| 
      
 121 
     | 
    
         
            +
                  end
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                  def creatable(val = true)
         
     | 
| 
      
 124 
     | 
    
         
            +
                    @creatable = val
         
     | 
| 
      
 125 
     | 
    
         
            +
                  end
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                  def updatable(val = true)
         
     | 
| 
      
 128 
     | 
    
         
            +
                    @updatable = val
         
     | 
| 
      
 129 
     | 
    
         
            +
                  end
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                  def creatable?
         
     | 
| 
      
 132 
     | 
    
         
            +
                    !!@creatable
         
     | 
| 
      
 133 
     | 
    
         
            +
                  end
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                  def updatable?
         
     | 
| 
      
 136 
     | 
    
         
            +
                    !!@updatable
         
     | 
| 
      
 137 
     | 
    
         
            +
                  end
         
     | 
| 
      
 138 
     | 
    
         
            +
                end
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                # @macro [attach] resource.property
         
     | 
| 
      
 142 
     | 
    
         
            +
                # @return [String] the $1 property of the object
         
     | 
| 
      
 143 
     | 
    
         
            +
                attr_accessor :id
         
     | 
| 
      
 144 
     | 
    
         
            +
                attr_accessor :uri
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
                def to_hash
         
     | 
| 
      
 147 
     | 
    
         
            +
                  attrs = instance_variables.map { |v| v.to_s.sub(/^@/, '') }
         
     | 
| 
      
 148 
     | 
    
         
            +
                  attrs.delete 'client'
         
     | 
| 
      
 149 
     | 
    
         
            +
                  Hash[attrs.select { |v| respond_to? v }.map { |v| [v.to_sym, send(v)] }]
         
     | 
| 
      
 150 
     | 
    
         
            +
                end
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
                def to_json
         
     | 
| 
      
 153 
     | 
    
         
            +
                  to_hash.to_json
         
     | 
| 
      
 154 
     | 
    
         
            +
                end
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
                def inspect
         
     | 
| 
      
 157 
     | 
    
         
            +
                  "#<#{self.class} #{to_hash.map { |k,v| "#{k}=#{v.inspect}" }.join(', ')}>"
         
     | 
| 
      
 158 
     | 
    
         
            +
                end
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
                def persisted?
         
     | 
| 
      
 161 
     | 
    
         
            +
                  !id.nil?
         
     | 
| 
      
 162 
     | 
    
         
            +
                end
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
                # Save a resource on the API server. If the resource already exists (has a
         
     | 
| 
      
 165 
     | 
    
         
            +
                # non-null id), it will be updated with a PUT, otherwise it will be created
         
     | 
| 
      
 166 
     | 
    
         
            +
                # with a POST.
         
     | 
| 
      
 167 
     | 
    
         
            +
                def save
         
     | 
| 
      
 168 
     | 
    
         
            +
                  save_data self.to_hash
         
     | 
| 
      
 169 
     | 
    
         
            +
                  self
         
     | 
| 
      
 170 
     | 
    
         
            +
                end
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
              protected
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
                def client
         
     | 
| 
      
 175 
     | 
    
         
            +
                  @client || GoCardless.client
         
     | 
| 
      
 176 
     | 
    
         
            +
                end
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                def save_data(data)
         
     | 
| 
      
 179 
     | 
    
         
            +
                  method = if self.persisted?
         
     | 
| 
      
 180 
     | 
    
         
            +
                    raise "#{self.class} cannot be updated" unless self.class.updatable?
         
     | 
| 
      
 181 
     | 
    
         
            +
                    'put'
         
     | 
| 
      
 182 
     | 
    
         
            +
                  else
         
     | 
| 
      
 183 
     | 
    
         
            +
                    raise "#{self.class} cannot be created" unless self.class.creatable?
         
     | 
| 
      
 184 
     | 
    
         
            +
                    'post'
         
     | 
| 
      
 185 
     | 
    
         
            +
                  end
         
     | 
| 
      
 186 
     | 
    
         
            +
                  path = self.class.endpoint.gsub(':id', id.to_s)
         
     | 
| 
      
 187 
     | 
    
         
            +
                  response = client.send("api_#{method}", path, data)
         
     | 
| 
      
 188 
     | 
    
         
            +
                  response.each { |key,val| send("#{key}=", val) if respond_to?("#{key}=") } if response.is_a? Hash
         
     | 
| 
      
 189 
     | 
    
         
            +
                end
         
     | 
| 
      
 190 
     | 
    
         
            +
              end
         
     | 
| 
      
 191 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module GoCardless
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Subscription < Resource
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
                self.endpoint = '/subscriptions/:id'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                attr_accessor  :amount, :currency, :interval_length, :interval_unit,
         
     | 
| 
      
 7 
     | 
    
         
            +
                               :description, :setup_fee, :trial_length, :trial_unit
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                reference_accessor :merchant_id, :user_id
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                date_accessor :expires_at, :created_at
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
            end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,36 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
             
     | 
| 
      
 2 
     | 
    
         
            +
            module GoCardless
         
     | 
| 
      
 3 
     | 
    
         
            +
              module Utils
         
     | 
| 
      
 4 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                  # String Helpers
         
     | 
| 
      
 7 
     | 
    
         
            +
                  def camelize(str)
         
     | 
| 
      
 8 
     | 
    
         
            +
                    str.split('_').map(&:capitalize).join
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  def underscore(str)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    str.gsub(/(.)([A-Z])/) { "#{$1}_#{$2.downcase}" }.downcase
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  def singularize(str)
         
     | 
| 
      
 16 
     | 
    
         
            +
                    # This should probably be a bit more robust
         
     | 
| 
      
 17 
     | 
    
         
            +
                    str.sub(/s$/, '').sub(/i$/, 'us')
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  # Hash Helpers
         
     | 
| 
      
 21 
     | 
    
         
            +
                  def symbolize_keys(hash)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    symbolize_keys! hash.dup
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  def symbolize_keys!(hash)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    hash.keys.each do |key|
         
     | 
| 
      
 27 
     | 
    
         
            +
                      sym_key = key.to_s.to_sym rescue key
         
     | 
| 
      
 28 
     | 
    
         
            +
                      hash[sym_key] = hash.delete(key) unless hash.key?(sym_key)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    end
         
     | 
| 
      
 30 
     | 
    
         
            +
                    hash
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
            end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
    
        data/spec/bill_spec.rb
    ADDED
    
    | 
         @@ -0,0 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe GoCardless::Bill do
         
     | 
| 
      
 4 
     | 
    
         
            +
              before :each do
         
     | 
| 
      
 5 
     | 
    
         
            +
                @app_id = 'abc'
         
     | 
| 
      
 6 
     | 
    
         
            +
                @app_secret = 'xyz'
         
     | 
| 
      
 7 
     | 
    
         
            +
                GoCardless.account_details = {:app_id => @app_id, :app_secret => @app_secret,
         
     | 
| 
      
 8 
     | 
    
         
            +
                                              :token  => 'xxx manage_merchant:1'}
         
     | 
| 
      
 9 
     | 
    
         
            +
                @client = GoCardless.client
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              it "source getter works" do
         
     | 
| 
      
 13 
     | 
    
         
            +
                b = GoCardless::Bill.new(:source_type => :subscription, :source_id => 123)
         
     | 
| 
      
 14 
     | 
    
         
            +
                @client.access_token = 'TOKEN manage_merchant:123'
         
     | 
| 
      
 15 
     | 
    
         
            +
                stub_get(@client, :id => 123)
         
     | 
| 
      
 16 
     | 
    
         
            +
                source = b.source
         
     | 
| 
      
 17 
     | 
    
         
            +
                source.should be_a GoCardless::Subscription
         
     | 
| 
      
 18 
     | 
    
         
            +
                source.id.should == 123
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              it "source setter works" do
         
     | 
| 
      
 22 
     | 
    
         
            +
                b = GoCardless::Bill.new
         
     | 
| 
      
 23 
     | 
    
         
            +
                b.source = GoCardless::Subscription.new(:id => 123)
         
     | 
| 
      
 24 
     | 
    
         
            +
                b.source_id.should == 123
         
     | 
| 
      
 25 
     | 
    
         
            +
                b.source_type.should.to_s == 'subscription'
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
            end
         
     | 
    
        data/spec/client_spec.rb
    ADDED
    
    | 
         @@ -0,0 +1,376 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe GoCardless::Client do
         
     | 
| 
      
 4 
     | 
    
         
            +
              before :each do
         
     | 
| 
      
 5 
     | 
    
         
            +
                @app_id = 'abc'
         
     | 
| 
      
 6 
     | 
    
         
            +
                @app_secret = 'xyz'
         
     | 
| 
      
 7 
     | 
    
         
            +
                @redirect_uri = 'http://test.com/cb'
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              describe ".base_url" do
         
     | 
| 
      
 11 
     | 
    
         
            +
                it "returns the correct url for the production environment" do
         
     | 
| 
      
 12 
     | 
    
         
            +
                  GoCardless.environment = :production
         
     | 
| 
      
 13 
     | 
    
         
            +
                  GoCardless::Client.base_url.should == 'https://gocardless.com'
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                it "returns the correct url for the sandbox environment" do
         
     | 
| 
      
 17 
     | 
    
         
            +
                  GoCardless.environment = :sandbox
         
     | 
| 
      
 18 
     | 
    
         
            +
                  GoCardless::Client.base_url.should == 'https://sandbox.gocardless.com'
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                it "returns the correct url when it's set manually" do
         
     | 
| 
      
 22 
     | 
    
         
            +
                  GoCardless::Client.base_url = 'https://abc.gocardless.com'
         
     | 
| 
      
 23 
     | 
    
         
            +
                  GoCardless::Client.base_url.should == 'https://abc.gocardless.com'
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              describe "#new" do
         
     | 
| 
      
 28 
     | 
    
         
            +
                it "without an app id should raise an error" do
         
     | 
| 
      
 29 
     | 
    
         
            +
                  lambda do
         
     | 
| 
      
 30 
     | 
    
         
            +
                    GoCardless::Client.new({:app_secret => @app_secret})
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end.should raise_exception(GoCardless::ClientError)
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
                it "without an app_secret should raise an error" do
         
     | 
| 
      
 34 
     | 
    
         
            +
                  lambda do
         
     | 
| 
      
 35 
     | 
    
         
            +
                    GoCardless::Client.new({:app_id => @app_id})
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end.should raise_exception(GoCardless::ClientError)
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
              before do
         
     | 
| 
      
 41 
     | 
    
         
            +
                @client = GoCardless::Client.new({:app_id => @app_id, :app_secret => @app_secret})
         
     | 
| 
      
 42 
     | 
    
         
            +
              end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              describe "#authorize_url" do
         
     | 
| 
      
 45 
     | 
    
         
            +
                it "fails without a redirect uri" do
         
     | 
| 
      
 46 
     | 
    
         
            +
                  lambda { @client.authorize_url }.should raise_exception(ArgumentError)
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                it "generates the authorize url correctly" do
         
     | 
| 
      
 50 
     | 
    
         
            +
                  url = URI.parse(@client.authorize_url(:redirect_uri => @redirect_uri))
         
     | 
| 
      
 51 
     | 
    
         
            +
                  query = CGI.parse(url.query)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  query['response_type'].first.should == 'code'
         
     | 
| 
      
 53 
     | 
    
         
            +
                  query['redirect_uri'].first.should == @redirect_uri
         
     | 
| 
      
 54 
     | 
    
         
            +
                  query['client_id'].first.should == @app_id
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
              end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
              describe "#fetch_access_token" do
         
     | 
| 
      
 59 
     | 
    
         
            +
                access_token_url = "#{GoCardless::Client.base_url}/oauth/access_token"
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                it "fails without a redirect uri" do
         
     | 
| 
      
 62 
     | 
    
         
            +
                  lambda do
         
     | 
| 
      
 63 
     | 
    
         
            +
                    @client.fetch_access_token('code', {})
         
     | 
| 
      
 64 
     | 
    
         
            +
                  end.should raise_exception(ArgumentError)
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                describe "with valid params" do
         
     | 
| 
      
 68 
     | 
    
         
            +
                  it "calls correct method with correct args" do
         
     | 
| 
      
 69 
     | 
    
         
            +
                    auth_code = 'fakecode'
         
     | 
| 
      
 70 
     | 
    
         
            +
                    access_token = mock
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                    @client.instance_variable_get(:@access_token).should be_nil
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                    oauth_client = @client.instance_variable_get(:@oauth_client)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    oauth_client.auth_code.expects(:get_token).with(
         
     | 
| 
      
 76 
     | 
    
         
            +
                      auth_code, has_entry(:redirect_uri => @redirect_uri)
         
     | 
| 
      
 77 
     | 
    
         
            +
                    )
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                    @client.fetch_access_token(auth_code, {:redirect_uri => @redirect_uri})
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  it "sets @access_token" do
         
     | 
| 
      
 83 
     | 
    
         
            +
                    access_token = mock
         
     | 
| 
      
 84 
     | 
    
         
            +
                    access_token.stubs(:params).returns('scope' => '')
         
     | 
| 
      
 85 
     | 
    
         
            +
                    access_token.stubs(:token).returns('')
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                    oauth_client = @client.instance_variable_get(:@oauth_client)
         
     | 
| 
      
 88 
     | 
    
         
            +
                    oauth_client.auth_code.expects(:get_token).returns(access_token)
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                    @client.instance_variable_get(:@access_token).should be_nil
         
     | 
| 
      
 91 
     | 
    
         
            +
                    @client.fetch_access_token('code', {:redirect_uri => @redirect_uri})
         
     | 
| 
      
 92 
     | 
    
         
            +
                    @client.instance_variable_get(:@access_token).should == access_token
         
     | 
| 
      
 93 
     | 
    
         
            +
                  end
         
     | 
| 
      
 94 
     | 
    
         
            +
                end
         
     | 
| 
      
 95 
     | 
    
         
            +
              end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
              describe "#access_token" do
         
     | 
| 
      
 98 
     | 
    
         
            +
                it "serializes access token correctly" do
         
     | 
| 
      
 99 
     | 
    
         
            +
                  oauth_client = @client.instance_variable_get(:@oauth_client)
         
     | 
| 
      
 100 
     | 
    
         
            +
                  token = OAuth2::AccessToken.new(oauth_client, 'TOKEN123')
         
     | 
| 
      
 101 
     | 
    
         
            +
                  token.params['scope'] = 'a:1 b:2'
         
     | 
| 
      
 102 
     | 
    
         
            +
                  @client.instance_variable_set(:@access_token, token)
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                  @client.access_token.should == 'TOKEN123 a:1 b:2'
         
     | 
| 
      
 105 
     | 
    
         
            +
                end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                it "returns nil when there's no token" do
         
     | 
| 
      
 108 
     | 
    
         
            +
                  @client.access_token.should be_nil
         
     | 
| 
      
 109 
     | 
    
         
            +
                end
         
     | 
| 
      
 110 
     | 
    
         
            +
              end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
              describe "#access_token=" do
         
     | 
| 
      
 113 
     | 
    
         
            +
                it "deserializes access token correctly" do
         
     | 
| 
      
 114 
     | 
    
         
            +
                  @client.access_token = 'TOKEN123 a:1 b:2'
         
     | 
| 
      
 115 
     | 
    
         
            +
                  token = @client.instance_variable_get(:@access_token)
         
     | 
| 
      
 116 
     | 
    
         
            +
                  token.token.should == 'TOKEN123'
         
     | 
| 
      
 117 
     | 
    
         
            +
                  token.params['scope'].should == 'a:1 b:2'
         
     | 
| 
      
 118 
     | 
    
         
            +
                end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                it "handles invalid values correctly" do
         
     | 
| 
      
 121 
     | 
    
         
            +
                  token = 'TOKEN123'  # missing scope
         
     | 
| 
      
 122 
     | 
    
         
            +
                  expect { @client.access_token = token }.to raise_exception ArgumentError
         
     | 
| 
      
 123 
     | 
    
         
            +
                end
         
     | 
| 
      
 124 
     | 
    
         
            +
              end
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
              describe "#api_get" do
         
     | 
| 
      
 127 
     | 
    
         
            +
                it "uses the correct path prefix" do
         
     | 
| 
      
 128 
     | 
    
         
            +
                  @client.access_token = 'TOKEN123 a:1 b:2'
         
     | 
| 
      
 129 
     | 
    
         
            +
                  token = @client.instance_variable_get(:@access_token)
         
     | 
| 
      
 130 
     | 
    
         
            +
                  r = mock
         
     | 
| 
      
 131 
     | 
    
         
            +
                  r.stubs(:parsed)
         
     | 
| 
      
 132 
     | 
    
         
            +
                  token.expects(:get).with { |p,o| p =~ %r|/api/v1/test| }.returns(r)
         
     | 
| 
      
 133 
     | 
    
         
            +
                  @client.api_get('/test')
         
     | 
| 
      
 134 
     | 
    
         
            +
                end
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                it "fails without an access_token" do
         
     | 
| 
      
 137 
     | 
    
         
            +
                  expect { @client.api_get '/' }.to raise_exception GoCardless::ClientError
         
     | 
| 
      
 138 
     | 
    
         
            +
                end
         
     | 
| 
      
 139 
     | 
    
         
            +
              end
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
              describe "#api_post" do
         
     | 
| 
      
 142 
     | 
    
         
            +
                it "encodes data to json" do
         
     | 
| 
      
 143 
     | 
    
         
            +
                  @client.access_token = 'TOKEN123 a:1 b:2'
         
     | 
| 
      
 144 
     | 
    
         
            +
                  token = @client.instance_variable_get(:@access_token)
         
     | 
| 
      
 145 
     | 
    
         
            +
                  r = mock
         
     | 
| 
      
 146 
     | 
    
         
            +
                  r.stubs(:parsed)
         
     | 
| 
      
 147 
     | 
    
         
            +
                  token.expects(:post).with { |p,opts| opts[:body] == '{"a":1}' }.returns(r)
         
     | 
| 
      
 148 
     | 
    
         
            +
                  @client.api_post('/test', {:a => 1})
         
     | 
| 
      
 149 
     | 
    
         
            +
                end
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
                it "fails without an access_token" do
         
     | 
| 
      
 152 
     | 
    
         
            +
                  expect { @client.api_get '/' }.to raise_exception GoCardless::ClientError
         
     | 
| 
      
 153 
     | 
    
         
            +
                end
         
     | 
| 
      
 154 
     | 
    
         
            +
              end
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
              describe "#merchant" do
         
     | 
| 
      
 157 
     | 
    
         
            +
                it "looks up the correct merchant" do
         
     | 
| 
      
 158 
     | 
    
         
            +
                  @client.access_token = 'TOKEN a manage_merchant:123 b'
         
     | 
| 
      
 159 
     | 
    
         
            +
                  response = mock
         
     | 
| 
      
 160 
     | 
    
         
            +
                  response.expects(:parsed)
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                  token = @client.instance_variable_get(:@access_token)
         
     | 
| 
      
 163 
     | 
    
         
            +
                  merchant_url = '/api/v1/merchants/123'
         
     | 
| 
      
 164 
     | 
    
         
            +
                  token.expects(:get).with { |p,o| p == merchant_url }.returns response
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
                  GoCardless::Merchant.stubs(:new_with_client)
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                  @client.merchant
         
     | 
| 
      
 169 
     | 
    
         
            +
                end
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                it "creates a Merchant object" do
         
     | 
| 
      
 172 
     | 
    
         
            +
                  @client.access_token = 'TOKEN manage_merchant:123'
         
     | 
| 
      
 173 
     | 
    
         
            +
                  response = mock
         
     | 
| 
      
 174 
     | 
    
         
            +
                  response.expects(:parsed).returns({:name => 'test', :id => 123})
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
      
 176 
     | 
    
         
            +
                  token = @client.instance_variable_get(:@access_token)
         
     | 
| 
      
 177 
     | 
    
         
            +
                  token.expects(:get).returns response
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
                  merchant = @client.merchant
         
     | 
| 
      
 180 
     | 
    
         
            +
                  merchant.should be_an_instance_of GoCardless::Merchant
         
     | 
| 
      
 181 
     | 
    
         
            +
                  merchant.id.should == 123
         
     | 
| 
      
 182 
     | 
    
         
            +
                  merchant.name.should == 'test'
         
     | 
| 
      
 183 
     | 
    
         
            +
                end
         
     | 
| 
      
 184 
     | 
    
         
            +
              end
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
              %w{subscription pre_authorization user bill payment}.each do |resource|
         
     | 
| 
      
 187 
     | 
    
         
            +
                describe "##{resource}" do
         
     | 
| 
      
 188 
     | 
    
         
            +
                  it "returns the correct #{GoCardless::Utils.camelize(resource)} object" do
         
     | 
| 
      
 189 
     | 
    
         
            +
                    @client.access_token = 'TOKEN manage_merchant:123'
         
     | 
| 
      
 190 
     | 
    
         
            +
                    stub_get(@client, {:id => 123})
         
     | 
| 
      
 191 
     | 
    
         
            +
                    obj = @client.send(resource, 123)
         
     | 
| 
      
 192 
     | 
    
         
            +
                    obj.should be_a GoCardless.const_get(GoCardless::Utils.camelize(resource))
         
     | 
| 
      
 193 
     | 
    
         
            +
                    obj.id.should == 123
         
     | 
| 
      
 194 
     | 
    
         
            +
                  end
         
     | 
| 
      
 195 
     | 
    
         
            +
                end
         
     | 
| 
      
 196 
     | 
    
         
            +
              end
         
     | 
| 
      
 197 
     | 
    
         
            +
             
     | 
| 
      
 198 
     | 
    
         
            +
              describe "#encode_params" do
         
     | 
| 
      
 199 
     | 
    
         
            +
                it "correctly encodes hashes" do
         
     | 
| 
      
 200 
     | 
    
         
            +
                  params = {:a => {:b => :c}, :x => :y}
         
     | 
| 
      
 201 
     | 
    
         
            +
                  result = 'a%5Bb%5D=c&x=y'
         
     | 
| 
      
 202 
     | 
    
         
            +
                  @client.send(:encode_params, params).should == result
         
     | 
| 
      
 203 
     | 
    
         
            +
                end
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
      
 205 
     | 
    
         
            +
                it "correctly encodes arrays" do
         
     | 
| 
      
 206 
     | 
    
         
            +
                  params = {:a => [1,2]}
         
     | 
| 
      
 207 
     | 
    
         
            +
                  result = 'a%5B%5D=1&a%5B%5D=2'
         
     | 
| 
      
 208 
     | 
    
         
            +
                  @client.send(:encode_params, params).should == result
         
     | 
| 
      
 209 
     | 
    
         
            +
                end
         
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
      
 211 
     | 
    
         
            +
                it "sorts params by key" do
         
     | 
| 
      
 212 
     | 
    
         
            +
                  params = {:b => 1, :a => 2}
         
     | 
| 
      
 213 
     | 
    
         
            +
                  result = 'a=2&b=1'
         
     | 
| 
      
 214 
     | 
    
         
            +
                  @client.send(:encode_params, params).should == result
         
     | 
| 
      
 215 
     | 
    
         
            +
                end
         
     | 
| 
      
 216 
     | 
    
         
            +
              end
         
     | 
| 
      
 217 
     | 
    
         
            +
             
     | 
| 
      
 218 
     | 
    
         
            +
              it "#sign_params signs pararmeter hashes correctly" do
         
     | 
| 
      
 219 
     | 
    
         
            +
                @client.instance_variable_set(:@app_secret, 'testsecret')
         
     | 
| 
      
 220 
     | 
    
         
            +
                params = {:test => true}
         
     | 
| 
      
 221 
     | 
    
         
            +
                sig = '6e4613b729ce15c288f70e72463739feeb05fc0b89b55d248d7f259b5367148b'
         
     | 
| 
      
 222 
     | 
    
         
            +
                @client.send(:sign_params, params)[:signature].should == sig
         
     | 
| 
      
 223 
     | 
    
         
            +
              end
         
     | 
| 
      
 224 
     | 
    
         
            +
             
     | 
| 
      
 225 
     | 
    
         
            +
              describe "#signature_valid?" do
         
     | 
| 
      
 226 
     | 
    
         
            +
                before(:each) { @params = { :x => 'y', :a => 'b' } }
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
      
 228 
     | 
    
         
            +
                it "succeeds with a valid signature" do
         
     | 
| 
      
 229 
     | 
    
         
            +
                  params = @client.send(:sign_params, @params)
         
     | 
| 
      
 230 
     | 
    
         
            +
                  @client.send(:signature_valid?, params).should be_true
         
     | 
| 
      
 231 
     | 
    
         
            +
                end
         
     | 
| 
      
 232 
     | 
    
         
            +
             
     | 
| 
      
 233 
     | 
    
         
            +
                it "fails with an invalid signature" do
         
     | 
| 
      
 234 
     | 
    
         
            +
                  params = {:signature => 'invalid'}.merge(@params)
         
     | 
| 
      
 235 
     | 
    
         
            +
                  @client.send(:signature_valid?, params).should be_false
         
     | 
| 
      
 236 
     | 
    
         
            +
                end
         
     | 
| 
      
 237 
     | 
    
         
            +
              end
         
     | 
| 
      
 238 
     | 
    
         
            +
             
     | 
| 
      
 239 
     | 
    
         
            +
              describe "#confirm_resource" do
         
     | 
| 
      
 240 
     | 
    
         
            +
                before :each do
         
     | 
| 
      
 241 
     | 
    
         
            +
                  @params = {
         
     | 
| 
      
 242 
     | 
    
         
            +
                    :resource_id   => '1',
         
     | 
| 
      
 243 
     | 
    
         
            +
                    :resource_uri  => 'a',
         
     | 
| 
      
 244 
     | 
    
         
            +
                    :resource_type => 'subscription',
         
     | 
| 
      
 245 
     | 
    
         
            +
                  }
         
     | 
| 
      
 246 
     | 
    
         
            +
                end
         
     | 
| 
      
 247 
     | 
    
         
            +
             
     | 
| 
      
 248 
     | 
    
         
            +
                [:resource_id, :resource_uri, :resource_type].each do |param|
         
     | 
| 
      
 249 
     | 
    
         
            +
                  it "fails when :#{param} is missing" do
         
     | 
| 
      
 250 
     | 
    
         
            +
                    p = @params.tap { |d| d.delete(param) }
         
     | 
| 
      
 251 
     | 
    
         
            +
                    expect { @client.confirm_resource p }.to raise_exception ArgumentError
         
     | 
| 
      
 252 
     | 
    
         
            +
                  end
         
     | 
| 
      
 253 
     | 
    
         
            +
                end
         
     | 
| 
      
 254 
     | 
    
         
            +
             
     | 
| 
      
 255 
     | 
    
         
            +
                it "doesn't confirm the resource when the signature is invalid" do
         
     | 
| 
      
 256 
     | 
    
         
            +
                  @client.expects(:request).never
         
     | 
| 
      
 257 
     | 
    
         
            +
                  @client.confirm_resource({:signature => 'xxx'}.merge(@params)) rescue nil
         
     | 
| 
      
 258 
     | 
    
         
            +
                end
         
     | 
| 
      
 259 
     | 
    
         
            +
             
     | 
| 
      
 260 
     | 
    
         
            +
                it "fails when the signature is invalid" do
         
     | 
| 
      
 261 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 262 
     | 
    
         
            +
                    @client.confirm_resource({:signature => 'xxx'}.merge(@params))
         
     | 
| 
      
 263 
     | 
    
         
            +
                  end.to raise_exception GoCardless::SignatureError
         
     | 
| 
      
 264 
     | 
    
         
            +
                end
         
     | 
| 
      
 265 
     | 
    
         
            +
             
     | 
| 
      
 266 
     | 
    
         
            +
                it "confirms the resource when the signature is valid" do
         
     | 
| 
      
 267 
     | 
    
         
            +
                  # Once for confirm, once to fetch result
         
     | 
| 
      
 268 
     | 
    
         
            +
                  @client.expects(:request).twice.returns(stub(:parsed => {}))
         
     | 
| 
      
 269 
     | 
    
         
            +
                  @client.confirm_resource(@client.send(:sign_params, @params))
         
     | 
| 
      
 270 
     | 
    
         
            +
                end
         
     | 
| 
      
 271 
     | 
    
         
            +
             
     | 
| 
      
 272 
     | 
    
         
            +
                it "returns the correct object when the signature is valid" do
         
     | 
| 
      
 273 
     | 
    
         
            +
                  @client.stubs(:request).returns(stub(:parsed => {}))
         
     | 
| 
      
 274 
     | 
    
         
            +
                  subscription = GoCardless::Subscription.new_with_client @client
         
     | 
| 
      
 275 
     | 
    
         
            +
                  GoCardless::Subscription.expects(:find_with_client).returns subscription
         
     | 
| 
      
 276 
     | 
    
         
            +
             
     | 
| 
      
 277 
     | 
    
         
            +
                  # confirm_resource should use the Subcription class because
         
     | 
| 
      
 278 
     | 
    
         
            +
                  # the :response_type is set to subscription
         
     | 
| 
      
 279 
     | 
    
         
            +
                  resource = @client.confirm_resource(@client.send(:sign_params, @params))
         
     | 
| 
      
 280 
     | 
    
         
            +
                  resource.should be_a GoCardless::Subscription
         
     | 
| 
      
 281 
     | 
    
         
            +
                end
         
     | 
| 
      
 282 
     | 
    
         
            +
             
     | 
| 
      
 283 
     | 
    
         
            +
                it "includes valid http basic credentials" do
         
     | 
| 
      
 284 
     | 
    
         
            +
                  GoCardless::Subscription.stubs(:find_with_client)
         
     | 
| 
      
 285 
     | 
    
         
            +
                  auth = 'Basic YWJjOnh5eg=='
         
     | 
| 
      
 286 
     | 
    
         
            +
                  @client.expects(:request).once.with do |type, path, opts|
         
     | 
| 
      
 287 
     | 
    
         
            +
                    opts.should include :headers
         
     | 
| 
      
 288 
     | 
    
         
            +
                    opts[:headers].should include 'Authorization'
         
     | 
| 
      
 289 
     | 
    
         
            +
                    opts[:headers]['Authorization'].should == auth
         
     | 
| 
      
 290 
     | 
    
         
            +
                  end
         
     | 
| 
      
 291 
     | 
    
         
            +
                  @client.confirm_resource(@client.send(:sign_params, @params))
         
     | 
| 
      
 292 
     | 
    
         
            +
                end
         
     | 
| 
      
 293 
     | 
    
         
            +
             
     | 
| 
      
 294 
     | 
    
         
            +
                it "works with string params" do
         
     | 
| 
      
 295 
     | 
    
         
            +
                  @client.stubs(:request)
         
     | 
| 
      
 296 
     | 
    
         
            +
                  GoCardless::Subscription.stubs(:find_with_client)
         
     | 
| 
      
 297 
     | 
    
         
            +
                  params = Hash[@params.dup.map { |k,v| [k.to_s, v] }]
         
     | 
| 
      
 298 
     | 
    
         
            +
                  params.keys.each { |p| p.should be_a String }
         
     | 
| 
      
 299 
     | 
    
         
            +
                  # No ArgumentErrors should be raised
         
     | 
| 
      
 300 
     | 
    
         
            +
                  @client.confirm_resource(@client.send(:sign_params, params))
         
     | 
| 
      
 301 
     | 
    
         
            +
                end
         
     | 
| 
      
 302 
     | 
    
         
            +
              end
         
     | 
| 
      
 303 
     | 
    
         
            +
             
     | 
| 
      
 304 
     | 
    
         
            +
              it "#generate_nonce should generate a random string" do
         
     | 
| 
      
 305 
     | 
    
         
            +
                @client.send(:generate_nonce).should_not == @client.send(:generate_nonce)
         
     | 
| 
      
 306 
     | 
    
         
            +
              end
         
     | 
| 
      
 307 
     | 
    
         
            +
             
     | 
| 
      
 308 
     | 
    
         
            +
              describe "#new_limit_url" do
         
     | 
| 
      
 309 
     | 
    
         
            +
                before(:each) do
         
     | 
| 
      
 310 
     | 
    
         
            +
                  @merchant_id = '123'
         
     | 
| 
      
 311 
     | 
    
         
            +
                  @client.access_token = "TOKEN manage_merchant:#{@merchant_id}"
         
     | 
| 
      
 312 
     | 
    
         
            +
                end
         
     | 
| 
      
 313 
     | 
    
         
            +
             
     | 
| 
      
 314 
     | 
    
         
            +
                def get_params(url)
         
     | 
| 
      
 315 
     | 
    
         
            +
                  Hash[CGI.parse(URI.parse(url).query).map{ |k,v| [k, v.first] }]
         
     | 
| 
      
 316 
     | 
    
         
            +
                end
         
     | 
| 
      
 317 
     | 
    
         
            +
             
     | 
| 
      
 318 
     | 
    
         
            +
                it "should use the correct path" do
         
     | 
| 
      
 319 
     | 
    
         
            +
                  url = @client.send(:new_limit_url, :test_limit, {})
         
     | 
| 
      
 320 
     | 
    
         
            +
                  URI.parse(url).path.should == '/connect/test_limits/new'
         
     | 
| 
      
 321 
     | 
    
         
            +
                end
         
     | 
| 
      
 322 
     | 
    
         
            +
             
     | 
| 
      
 323 
     | 
    
         
            +
                it "should include the params in the URL query" do
         
     | 
| 
      
 324 
     | 
    
         
            +
                  params = { 'a' => '1', 'b' => '2' }
         
     | 
| 
      
 325 
     | 
    
         
            +
                  url = @client.send(:new_limit_url, :subscription, params)
         
     | 
| 
      
 326 
     | 
    
         
            +
                  url_params = get_params(url)
         
     | 
| 
      
 327 
     | 
    
         
            +
                  params.each do |key, value|
         
     | 
| 
      
 328 
     | 
    
         
            +
                    url_params["subscription[#{key}]"].should == value
         
     | 
| 
      
 329 
     | 
    
         
            +
                  end
         
     | 
| 
      
 330 
     | 
    
         
            +
                end
         
     | 
| 
      
 331 
     | 
    
         
            +
             
     | 
| 
      
 332 
     | 
    
         
            +
                it "should add merchant_id to the limit" do
         
     | 
| 
      
 333 
     | 
    
         
            +
                  url = @client.send(:new_limit_url, :subscription, {})
         
     | 
| 
      
 334 
     | 
    
         
            +
                  get_params(url)['subscription[merchant_id]'].should == @merchant_id
         
     | 
| 
      
 335 
     | 
    
         
            +
                end
         
     | 
| 
      
 336 
     | 
    
         
            +
             
     | 
| 
      
 337 
     | 
    
         
            +
                it "should include a valid signature" do
         
     | 
| 
      
 338 
     | 
    
         
            +
                  params = get_params(@client.send(:new_limit_url, :subscription, :x => 1))
         
     | 
| 
      
 339 
     | 
    
         
            +
                  params.key?('signature').should be_true
         
     | 
| 
      
 340 
     | 
    
         
            +
                  sig = params.delete('signature')
         
     | 
| 
      
 341 
     | 
    
         
            +
                  sig.should == @client.send(:sign_params, params.clone)[:signature]
         
     | 
| 
      
 342 
     | 
    
         
            +
                end
         
     | 
| 
      
 343 
     | 
    
         
            +
             
     | 
| 
      
 344 
     | 
    
         
            +
                it "should include a nonce" do
         
     | 
| 
      
 345 
     | 
    
         
            +
                  params = get_params(@client.send(:new_limit_url, :subscription, :x => 1))
         
     | 
| 
      
 346 
     | 
    
         
            +
                  params['nonce'].should be_a String
         
     | 
| 
      
 347 
     | 
    
         
            +
                end
         
     | 
| 
      
 348 
     | 
    
         
            +
             
     | 
| 
      
 349 
     | 
    
         
            +
                it "should include a client_id" do
         
     | 
| 
      
 350 
     | 
    
         
            +
                  params = get_params(@client.send(:new_limit_url, :subscription, :x => 1))
         
     | 
| 
      
 351 
     | 
    
         
            +
                  params['client_id'].should == @client.instance_variable_get(:@app_id)
         
     | 
| 
      
 352 
     | 
    
         
            +
                end
         
     | 
| 
      
 353 
     | 
    
         
            +
             
     | 
| 
      
 354 
     | 
    
         
            +
                it "should include a timestamp" do
         
     | 
| 
      
 355 
     | 
    
         
            +
                  # Time.now returning Pacific time
         
     | 
| 
      
 356 
     | 
    
         
            +
                  time = Time.parse('Sat Jan 01 00:00:00 -0800')
         
     | 
| 
      
 357 
     | 
    
         
            +
                  Time.expects(:now).returns time
         
     | 
| 
      
 358 
     | 
    
         
            +
                  params = get_params(@client.send(:new_limit_url, :subscription, :x => 1))
         
     | 
| 
      
 359 
     | 
    
         
            +
                  # Check that timezone is ISO formatted UTC
         
     | 
| 
      
 360 
     | 
    
         
            +
                  params['timestamp'].should == "2011-01-01T08:00:00Z"
         
     | 
| 
      
 361 
     | 
    
         
            +
                end
         
     | 
| 
      
 362 
     | 
    
         
            +
              end
         
     | 
| 
      
 363 
     | 
    
         
            +
             
     | 
| 
      
 364 
     | 
    
         
            +
              describe "#merchant_id" do
         
     | 
| 
      
 365 
     | 
    
         
            +
                it "returns the merchant id when an access token is set" do
         
     | 
| 
      
 366 
     | 
    
         
            +
                  @client.access_token = 'TOKEN manage_merchant:123'
         
     | 
| 
      
 367 
     | 
    
         
            +
                  @client.send(:merchant_id).should == '123'
         
     | 
| 
      
 368 
     | 
    
         
            +
                end
         
     | 
| 
      
 369 
     | 
    
         
            +
             
     | 
| 
      
 370 
     | 
    
         
            +
                it "fails if there's no access token" do
         
     | 
| 
      
 371 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 372 
     | 
    
         
            +
                    @client.send(:merchant_id)
         
     | 
| 
      
 373 
     | 
    
         
            +
                  end.to raise_exception GoCardless::ClientError
         
     | 
| 
      
 374 
     | 
    
         
            +
                end
         
     | 
| 
      
 375 
     | 
    
         
            +
              end
         
     | 
| 
      
 376 
     | 
    
         
            +
            end
         
     |