balanced 0.3.11 → 0.5.1

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 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