balanced 0.3.11 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -21,6 +21,7 @@ doc/
21
21
  # env
22
22
  .idea
23
23
  werk
24
+ *.iml
24
25
 
25
26
  # lib
26
27
  Gemfile.lock
data/README.md CHANGED
@@ -55,6 +55,12 @@ spec/cassettes. To clear them and regenerate:
55
55
  $ rm -rf spec/cassettes
56
56
 
57
57
 
58
+ ### Issues
59
+
60
+ All issues should be documented at
61
+ [balanced-ruby/issues](https://github.com/balanced/balanced-ruby/issues)
62
+
63
+
58
64
  ### Building Documentation
59
65
 
60
66
  Documentation is built using YARD - http://rubydoc.info/docs/yard
data/examples/examples.rb CHANGED
@@ -1,4 +1,5 @@
1
- $:.unshift("/Users/mahmoud/code/poundpay/ruby/balanced-ruby/lib")
1
+ cwd = File.dirname(File.dirname(File.absolute_path(__FILE__)))
2
+ $:.unshift(cwd + "/lib")
2
3
  require 'balanced'
3
4
 
4
5
  begin
@@ -7,9 +8,9 @@ rescue NameError
7
8
  raise "wtf"
8
9
  end
9
10
 
10
- host = ENV['BALANCED_HOST'] or nil
11
+ host = ENV.fetch('BALANCED_HOST') { nil }
11
12
  options = {}
12
- if host then
13
+ if host
13
14
  options[:scheme] = 'http'
14
15
  options[:host] = host
15
16
  options[:port] = 5000
@@ -79,11 +80,11 @@ refund = debit.refund() # the full amount!
79
80
  puts "ok, we have a merchant that's signing up, let's create an account for them " \
80
81
  "first, lets create their bank account."
81
82
 
82
- bank_account = Balanced::BankAccount.new(
83
+ bank_account = marketplace.create_bank_account(
83
84
  :account_number => "1234567890",
84
85
  :bank_code => "12",
85
86
  :name => "Jack Q Merchant",
86
- ).save
87
+ )
87
88
 
88
89
  merchant = marketplace.create_merchant(
89
90
  :email_address => "merchant@example.org",
@@ -100,9 +101,9 @@ merchant = marketplace.create_merchant(
100
101
  :name => "Jack Q Merchant",
101
102
  )
102
103
 
103
- puts "oh our buyer is interested in buying something for 130.00$"
104
+ puts "oh our buyer is interested in buying something for 530.00$"
104
105
  another_debit = buyer.debit(
105
- :amount => 13000,
106
+ :amount => 53000,
106
107
  :appears_on_statement_as => "MARKETPLACE.COM"
107
108
  )
108
109
 
@@ -127,3 +128,32 @@ puts "invalidating a bank account"
127
128
  bank_account.invalidate
128
129
 
129
130
  raise "This card is INCORRECTLY VALID" if bank_account.is_valid
131
+
132
+ puts "let's create a bank account not associated to an account"
133
+ bank_account = Balanced::BankAccount.new(
134
+ :account_number => "9876543210",
135
+ :routing_number => "021000021",
136
+ :name => "Jake Skellington",
137
+ :type => "checking"
138
+ ).save
139
+
140
+ puts "now let's credit it, the super-simple way"
141
+ credit = bank_account.credit(
142
+ :amount => 500
143
+ )
144
+
145
+ raise "Incorrect value for credit" if credit.amount != 500
146
+
147
+ puts "That was still too hard -- let's credit without creating a bank account first"
148
+ credit = Balanced::Credit.new(
149
+ :amount => 700,
150
+ :description => "Amazing",
151
+ :bank_account => {
152
+ :account_number => "55555555",
153
+ :bank_code => "021000021",
154
+ :name => "Wanda Wandy",
155
+ :type => "checking"
156
+ }
157
+ ).save
158
+ raise "OOPS -- that was hard after all" if (credit.amount != 700 or
159
+ credit.created_at.nil?)
data/lib/balanced.rb CHANGED
@@ -25,7 +25,8 @@ module Balanced
25
25
  attr_accessor :config
26
26
 
27
27
  def configure(api_key=nil, options={})
28
- @client = Balanced::Client.new(api_key, @config.merge(options))
28
+ @config = @config.merge(options)
29
+ @client = Balanced::Client.new(api_key, @config)
29
30
  end
30
31
 
31
32
  def is_configured_with_api_key?
@@ -16,7 +16,8 @@ module Balanced
16
16
  :logging_level => 'WARN',
17
17
  :connection_timeout => 2,
18
18
  :read_timeout => 5,
19
- :logger => nil
19
+ :logger => nil,
20
+ :ssl_verify => true
20
21
  }
21
22
 
22
23
  attr_reader :conn
@@ -43,6 +44,9 @@ module Balanced
43
44
  :request => {
44
45
  :open_timeout => config[:connection_timeout],
45
46
  :timeout => config[:read_timeout]
47
+ },
48
+ :ssl => {
49
+ :verify => @config[:ssl_verify] # Only set this to false for testing
46
50
  }
47
51
  }
48
52
  @conn = Faraday.new(url, options) do |cxn|
@@ -63,20 +67,34 @@ module Balanced
63
67
  #end
64
68
 
65
69
  def url
66
- URI::HTTPS.build :host => config[:host], :port => config[:port]
67
- end
70
+ builder = (config[:scheme] == 'http') ? URI::HTTP : URI::HTTPS
68
71
 
69
- private
72
+ builder.build ({:host => config[:host],
73
+ :port => config[:port],
74
+ :scheme => config[:scheme]})
75
+ end
70
76
 
71
77
  def method_missing(method, *args, &block)
72
- case method
73
- when :get, :post, :put, :delete
78
+ if is_http_method? method
74
79
  conn.basic_auth(api_key, '') unless api_key.nil?
75
80
  conn.send method, *args
76
81
  else
77
- super
82
+ super method, *args, &block
78
83
  end
79
84
  end
80
85
 
86
+ private
87
+
88
+ def is_http_method? method
89
+ [:get, :post, :put, :delete].include? method
90
+ end
91
+
92
+ def respond_to?(method, include_private = false)
93
+ if is_http_method? method
94
+ true
95
+ else
96
+ super method, include_private
97
+ end
98
+ end
81
99
  end
82
100
  end
@@ -1,16 +1,21 @@
1
1
  module Balanced
2
2
 
3
- # Custom error class for rescuing from all Balanced errors
4
- class Error < StandardError
3
+ # Custom error class for rescuing from all API response-related Balanced errors
4
+ class Error < ::StandardError
5
5
  attr_reader :response
6
6
 
7
+ # @param [Hash] response the decoded json response body
7
8
  def initialize(response)
8
9
  @response = response
9
10
  super error_message
10
11
  end
11
12
 
13
+ # @return [Hash]
12
14
  def body
13
- Utils.hash_with_indifferent_read_access response[:body]
15
+ @body ||= begin
16
+ return {} unless response[:body]
17
+ Utils.hash_with_indifferent_read_access(response[:body])
18
+ end
14
19
  end
15
20
 
16
21
  def error_message
@@ -26,12 +31,22 @@ module Balanced
26
31
  body.keys.each do |name|
27
32
  self.class.instance_eval {
28
33
  define_method(name) { body[name] } # Get.
29
- define_method("#{name}?") { !!body[name].nil? } # Present.
34
+ define_method("#{name}?") { !!body[name] } # Present.
30
35
  }
31
36
  end
32
37
  end
33
38
  end
34
39
 
40
+ # General error class for non API response exceptions
41
+ class StandardError < Error
42
+ attr_reader :message
43
+ alias :error_message :message
44
+
45
+ def initialize(message)
46
+ @message = message
47
+ end
48
+ end
49
+
35
50
  class MoreInformationRequired < Error
36
51
  def redirect_uri
37
52
  response.headers['Location']
@@ -29,9 +29,11 @@ module Balanced
29
29
  #
30
30
  # @return [Debit]
31
31
  def debit *args
32
+ warn_on_positional args
33
+
32
34
  options = args.last.is_a?(Hash) ? args.pop : {}
33
35
  amount = args[0] || options.fetch(:amount) { nil }
34
- soft_descriptor = args[1] || options.fetch(:appears_on_statement_as) { nil }
36
+ appears_on_statement_as = args[1] || options.fetch(:appears_on_statement_as) { nil }
35
37
  hold_uri = args[2] || options.fetch(:hold_uri) { nil }
36
38
  meta = args[3] || options.fetch(:meta) { nil }
37
39
  description = args[4] || options.fetch(:description) { nil }
@@ -40,7 +42,7 @@ module Balanced
40
42
  debit = Debit.new(
41
43
  :uri => self.debits_uri,
42
44
  :amount => amount,
43
- :appears_on_statement_as => soft_descriptor,
45
+ :appears_on_statement_as => appears_on_statement_as,
44
46
  :hold_uri => hold_uri,
45
47
  :meta => meta,
46
48
  :description => description,
@@ -59,6 +61,8 @@ module Balanced
59
61
  # @return [Hold] A Hold representing the reservation of funds from
60
62
  # this Account to your Marketplace.
61
63
  def hold *args
64
+ warn_on_positional args
65
+
62
66
  options = args.last.is_a?(Hash) ? args.pop : {}
63
67
  amount = args[0] || options.fetch(:amount) { }
64
68
  meta = args[1] || options.fetch(:meta) { nil }
@@ -81,20 +85,14 @@ module Balanced
81
85
  # @return [Credit] A Credit representing the transfer of funds from
82
86
  # your Marketplace to this Account.
83
87
  def credit *args
84
- options = args.last.is_a?(Hash) ? args.pop : {}
85
- amount = args[0] || options.fetch(:amount) { }
86
- description = args[1] || options.fetch(:description) { nil }
87
- meta = args[2] || options.fetch(:meta) { nil }
88
- destination_uri = args[3] || options.fetch(:destination_uri) { nil }
88
+ warn_on_positional args
89
89
 
90
- credit = Credit.new(
91
- :uri => self.credits_uri,
92
- :amount => amount,
93
- :meta => meta,
94
- :description => description,
95
- :destination_uri => destination_uri,
96
- )
97
- credit.save
90
+ if args.last.is_a? Hash
91
+ args.last.merge! uri: self.credits_uri
92
+ else
93
+ args << { uri: self.credits_uri }
94
+ end
95
+ Credit.new(*args).save
98
96
  end
99
97
 
100
98
  # Associates the Card represented by +card_uri+ with this Account.
@@ -7,6 +7,19 @@ module Balanced
7
7
  class BankAccount
8
8
  include Balanced::Resource
9
9
 
10
+ def self.has_account?
11
+ self.respond_to?('account') && !self.account.nil?
12
+ end
13
+
14
+ def self.uri
15
+ # Override the default nesting -- bank accounts can be toplevel now
16
+ if !self.has_account?
17
+ self.collection_path
18
+ else
19
+ self.class.uri
20
+ end
21
+ end
22
+
10
23
  def initialize attributes = {}
11
24
  Balanced::Utils.stringify_keys! attributes
12
25
  unless attributes.has_key? 'uri'
@@ -21,25 +34,39 @@ module Balanced
21
34
  # the +domain_name+ property from your Marketplace.
22
35
  # @return [Debit]
23
36
  def debit *args
37
+ warn_on_positional args
38
+
24
39
  options = args.last.is_a?(Hash) ? args.pop : {}
25
40
  amount = args[0] || options.fetch(:amount) { nil }
26
- soft_descriptor = args[1] || options.fetch(:appears_on_statement_as) { nil }
41
+ appears_on_statement_as = args[1] || options.fetch(:appears_on_statement_as) { nil }
27
42
  meta = args[2] || options.fetch(:meta) { nil }
28
43
  description = args[3] || options.fetch(:description) { nil }
29
44
 
30
- self.account.debit(amount, soft_descriptor, meta, description, self.uri)
45
+ self.account.debit(amount, appears_on_statement_as, meta, description, self.uri)
31
46
  end
32
47
 
33
- # Creates a Credit of funds from your Marketplace's escrow account to this Account.
48
+ # Creates a Credit of funds from your Marketplace's escrow account to this
49
+ # Account.
34
50
  #
35
51
  # @return [Credit]
36
52
  def credit *args
53
+ warn_on_positional args
54
+
37
55
  options = args.last.is_a?(Hash) ? args.pop : {}
38
56
  amount = args[0] || options.fetch(:amount) { nil }
39
57
  description = args[1] || options.fetch(:description) { nil }
40
- meta = args[2] || options.fetch(:meta) { nil }
41
-
42
- self.account.credit(amount, description, meta, self.uri)
58
+ if !self.class.has_account?
59
+ Credit.new(
60
+ :amount => amount,
61
+ :description => description,
62
+ :uri => self.credits_uri,
63
+ ).save
64
+ else
65
+ meta = args[2] || options.fetch(:meta) { nil }
66
+ appears_on_statement_as = args[3] || options.fetch(:appears_on_statement_as) { nil }
67
+ destination_url = args[4] || options.fetch(:destination_uri) { self.uri }
68
+ self.account.credit(amount, meta, description, destination_uri, appears_on_statement_as)
69
+ end
43
70
  end
44
71
 
45
72
  def invalidate
@@ -20,25 +20,38 @@ module Balanced
20
20
  #
21
21
  # @return [Debit]
22
22
  def debit *args
23
+ warn_on_positional args
23
24
  options = args.last.is_a?(Hash) ? args.pop : {}
24
25
  amount = args[0] || options.fetch(:amount) { nil }
25
26
  appears_on_statement_as = args[1] || options.fetch(:appears_on_statement_as) { nil }
26
- holds_uri = args[2] || options.fetch(:holds_uri) { nil }
27
+ hold_uri = args[2] || options.fetch(:hold_uri) { nil }
27
28
  meta = args[3] || options.fetch(:meta) { nil }
28
29
  description = args[3] || options.fetch(:description) { nil }
29
30
 
30
- self.account.debit(amount, appears_on_statement_as, holds_uri, meta, description, self.uri)
31
+ self.account.debit(
32
+ :amount => amount,
33
+ :appears_on_statement_as => appears_on_statement_as,
34
+ :hold_uri => hold_uri,
35
+ :meta => meta,
36
+ :description => description,
37
+ :source_uri => self.uri
38
+ )
31
39
  end
32
40
 
33
41
  # Creates a Hold of funds from this Card to your Marketplace.
34
42
  #
35
43
  # @return [Hold]
36
44
  def hold *args
45
+ warn_on_positional args
37
46
  options = args.last.is_a?(Hash) ? args.pop : {}
38
47
  amount = args[0] || options.fetch(:amount) { nil }
39
- meta = args[3] || options.fetch(:meta) { nil }
48
+ meta = args[1] || options.fetch(:meta) { nil }
40
49
 
41
- self.account.hold(amount, meta, self.uri)
50
+ self.account.hold(
51
+ :amount => amount,
52
+ :meta => meta,
53
+ :source_uri => self.uri
54
+ )
42
55
  end
43
56
 
44
57
  def invalidate
@@ -8,13 +8,40 @@ module Balanced
8
8
  #
9
9
  class Credit
10
10
  include Balanced::Resource
11
- def initialize attributes = {}
12
- Balanced::Utils.stringify_keys! attributes
13
- unless attributes.has_key? 'uri'
14
- attributes['uri'] = self.class.uri
11
+
12
+ def initialize *args
13
+ options = args.last.is_a?(Hash) ? args.pop : {}
14
+ uri = options.fetch(:uri) { self.class.uri }
15
+ bank_account = options.fetch(:bank_account) {}
16
+ amount = args[0] || options.fetch(:amount) { }
17
+ description = args[1] || options.fetch(:description) { nil }
18
+
19
+ unless bank_account.nil?
20
+ # Accountless bank account
21
+ attributes = {
22
+ uri: uri,
23
+ amount: amount,
24
+ description: description,
25
+ bank_account: bank_account,
26
+ meta: nil
27
+ }
28
+ else
29
+ meta = args[2] || options.fetch(:meta) { nil }
30
+ destination_uri = args[3] || options.fetch(:destination_uri) { nil }
31
+ appears_on_statement_as = args[4] || options.fetch(:appears_on_statement_as) { nil }
32
+ attributes = {
33
+ uri: uri,
34
+ amount: amount,
35
+ meta: meta,
36
+ description: description,
37
+ destination_uri: destination_uri,
38
+ appears_on_statement_as: appears_on_statement_as
39
+ }
15
40
  end
41
+
42
+ Balanced::Utils.stringify_keys! attributes
43
+
16
44
  super attributes
17
45
  end
18
46
  end
19
47
  end
20
-
@@ -28,6 +28,8 @@ module Balanced
28
28
  #
29
29
  # @return [Refund]
30
30
  def refund *args
31
+ warn_on_positional args
32
+
31
33
  options = args.last.is_a?(Hash) ? args.pop : {}
32
34
  amount = args[0] || options.fetch(:amount) { nil }
33
35
  description = args[1] || options.fetch(:description) { nil }
@@ -38,6 +38,8 @@ module Balanced
38
38
  #
39
39
  # @return [Debit]
40
40
  def capture *args
41
+ warn_on_positional args
42
+
41
43
  options = args.last.is_a?(Hash) ? args.pop : {}
42
44
  amount = args[0] || options.fetch(:amount) { nil }
43
45
  appears_on_statement_as = args[1] || options.fetch(:appears_on_statement_as) { nil }
@@ -45,7 +47,13 @@ module Balanced
45
47
  description = args[3] || options.fetch(:description) { nil }
46
48
 
47
49
  amount ||= self.amount
48
- self.account.debit(amount, appears_on_statement_as, self.uri, meta, description)
50
+ self.account.debit(
51
+ :amount => amount,
52
+ :appears_on_statement_as => appears_on_statement_as,
53
+ :hold_uri => self.uri,
54
+ :meta => meta,
55
+ :description => description
56
+ )
49
57
  end
50
58
 
51
59
  end