besepa 0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +46 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +132 -0
  6. data/Rakefile +1 -0
  7. data/besepa-ruby.gemspec +31 -0
  8. data/lib/besepa.rb +14 -0
  9. data/lib/besepa/activity.rb +11 -0
  10. data/lib/besepa/api_calls/create.rb +26 -0
  11. data/lib/besepa/api_calls/destroy.rb +15 -0
  12. data/lib/besepa/api_calls/list.rb +36 -0
  13. data/lib/besepa/api_calls/update.rb +23 -0
  14. data/lib/besepa/bank_account.rb +63 -0
  15. data/lib/besepa/business_account.rb +37 -0
  16. data/lib/besepa/customer.rb +134 -0
  17. data/lib/besepa/debit.rb +64 -0
  18. data/lib/besepa/errors/besepa_error.rb +28 -0
  19. data/lib/besepa/errors/invalid_resource_error.rb +11 -0
  20. data/lib/besepa/errors/resource_not_found_error.rb +11 -0
  21. data/lib/besepa/group.rb +27 -0
  22. data/lib/besepa/mandate.rb +18 -0
  23. data/lib/besepa/product.rb +19 -0
  24. data/lib/besepa/remittance.rb +32 -0
  25. data/lib/besepa/resource.rb +96 -0
  26. data/lib/besepa/subscription.rb +50 -0
  27. data/lib/besepa/utils/config.rb +51 -0
  28. data/lib/besepa/utils/connection.rb +27 -0
  29. data/lib/besepa/utils/request.rb +60 -0
  30. data/lib/besepa/utils/version.rb +6 -0
  31. data/lib/besepa/webhook.rb +16 -0
  32. data/spec/besepa/customer_spec.rb +119 -0
  33. data/spec/fixtures/customer.json +17 -0
  34. data/spec/fixtures/customer_add_debit.json +54 -0
  35. data/spec/fixtures/customer_bank_accounts.json +22 -0
  36. data/spec/fixtures/customer_debits.json +71 -0
  37. data/spec/fixtures/customer_removed.json +17 -0
  38. data/spec/fixtures/customers.json +20 -0
  39. data/spec/helper.rb +46 -0
  40. metadata +174 -0
@@ -0,0 +1,37 @@
1
+ module Besepa
2
+
3
+ class BusinessAccount < Besepa::Resource
4
+
5
+ include Besepa::ApiCalls::List
6
+ include Besepa::ApiCalls::Create
7
+ include Besepa::ApiCalls::Update
8
+ # include Besepa::ApiCalls::Destroy
9
+
10
+ FIELDS = [:id, :iban, :bic, :bank_name, :status, :default, :core_enabled, :core_suffix, :b2b_enabled, :b2b_suffix]
11
+
12
+ FIELDS.each do |f|
13
+ attr_accessor f
14
+ end
15
+
16
+ def self.klass_name
17
+ "bank_account"
18
+ end
19
+
20
+ def set_as_default
21
+ response = put "/#{api_path}/set_as_default"
22
+ process_attributes(response['response'])
23
+ self
24
+ end
25
+
26
+ protected
27
+
28
+ def self.api_path(filters={})
29
+ "/account/bank_accounts"
30
+ end
31
+
32
+ def api_path(filters={})
33
+ "/account/bank_accounts/#{CGI.escape(id)}"
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,134 @@
1
+ module Besepa
2
+
3
+ class Customer < Besepa::Resource
4
+
5
+ include Besepa::ApiCalls::List
6
+ include Besepa::ApiCalls::Create
7
+ include Besepa::ApiCalls::Update
8
+ include Besepa::ApiCalls::Destroy
9
+
10
+ FIELDS = [:id, :name, :taxid, :reference,
11
+ :contact_name, :contact_email, :contact_phone, :contact_language,
12
+ :address_street, :address_city, :address_postalcode, :address_state, :address_country,
13
+ :status]
14
+
15
+ FIELDS.each do |f|
16
+ attr_accessor f
17
+ end
18
+
19
+ # Customer's bank accounts
20
+ #
21
+ # @return collection of Besepa::BankAccount
22
+ def bank_accounts
23
+ BankAccount.all( {:customer_id => id} )
24
+ end
25
+
26
+ # Debits sent to this customer
27
+ #
28
+ # @return collection of Besepa::Debits
29
+ def debits
30
+ Debit.all( {:customer_id => id} )
31
+ end
32
+
33
+ # Subscriptions from this customer
34
+ #
35
+ # @return collection of Besepa::Subscription
36
+ def subscriptions
37
+ Subscription.all( {:customer_id => id} )
38
+ end
39
+
40
+ # List of groups this customers blongs to
41
+ #
42
+ # @return collection of Besepa::Group
43
+ def groups
44
+ Group.all( {:customer_id => id} )
45
+ end
46
+
47
+ # Adds this customer to the given group
48
+ #
49
+ # @param group_id The ID of the group to which this user should be added
50
+ #
51
+ # @return true if user is now a member of the group
52
+ def add_to_group(group_id)
53
+ response = post "/#{self.class.api_path}/#{id}/memberships/#{group_id}"
54
+ response['response'].select{|c| c['id'] == group_id}.any?
55
+ end
56
+
57
+ # Removed this customer from the given group
58
+ #
59
+ # @param group_id The ID of the group from which this user should be removed
60
+ #
61
+ # @return true if user is no longer a member of the group
62
+ def remove_from_group(group_id)
63
+ response = delete "/#{self.class.api_path}/#{id}/memberships/#{group_id}"
64
+ response['response'].select{|c| c['id'] == group_id}.empty?
65
+ end
66
+
67
+ # Creates a new subscription for this customer
68
+ #
69
+ # @param starts_at The date this subscription should start (Format: YYYY-MM-DD)
70
+ # @param product_code The ID of the product the customer is subscribing to
71
+ # @param bank_account_code The ID of the bank account where debits should be sent to
72
+ # @param setup_fee Initial set-up fee. Optional, default: 0
73
+ # @param metadata Optional Hash with metadata related to this subscription, if any.
74
+ #
75
+ # @return new created Besepa::Subscription
76
+ def add_subscription(starts_at, product_code, bank_account_code, setup_fee=0, metadata=nil)
77
+ params = {:starts_at => starts_at, :product_id => product_code, :debtor_bank_account_id => bank_account_code}
78
+ params[:setup_fee] = setup_fee if setup_fee
79
+ params[:metadata] = metadata if metadata
80
+ Subscription.create( params, {:customer_id => id} )
81
+ end
82
+
83
+ # Adds a bank account to this customer.
84
+ # IBAN and BIC are the only mandatory fields. If you already have the mandate signed, you can pass mandate
85
+ # detail's and account will be activated by default. Otherwise BankAccount will be marked as inactive (not usable
86
+ # for creating debits or subscriptions) until mandate is signed. BankAccount includes mandate's info, including
87
+ # signature URL.
88
+ #
89
+ # @param iban
90
+ # @param bic
91
+ # @param bank_name
92
+ # @param scheme CORE|COR1|B2B. Default: CORE
93
+ # @param mandate_signature_date Date in which this mandate was signed if already signed (Format: YYYY-MM-DD)
94
+ # @param mandate_ref Mandate's reference. If none, Besepa will create one.
95
+ # @param used Says if this mandate has already been used or not.
96
+ # @param signature_type Signature to be used: checkbox|sms|biometric
97
+ # @param phone_number Phone number where the signature SMS will be sent in case signature_type==sms is used.
98
+ #
99
+ # @return new created Besepa::BankAccount
100
+ def add_bank_account(iban, bic, bank_name=nil, scheme='CORE', mandate_signature_date=nil, mandate_ref=nil, used=false, signature_type='checkbox', phone_number=nil)
101
+ params = {:iban => iban, :bic => bic }
102
+ params[:mandate] = {scheme: scheme, used: used}
103
+ if mandate_signature_date
104
+ params[:mandate][:signed_at] = mandate_signature_date if mandate_signature_date
105
+ params[:mandate][:reference] = mandate_ref if mandate_ref
106
+ else
107
+ params[:mandate][:signature_type] = signature_type
108
+ params[:mandate][:phone_number] = phone_number if phone_number
109
+ end
110
+ params[:bank_name] = bank_name if bank_name
111
+ BankAccount.create( params, {:customer_id => id} )
112
+ end
113
+
114
+ # Generates a direct debit that will be charged to this customer.
115
+ #
116
+ # @param debtor_bank_account_id Customer's BankAccount code this Debit shouyd be charged to.
117
+ # @param reference Debit's unique reference
118
+ # @param description Debit's description. Customer will see this in his Bank's statements
119
+ # @param amount Amount to be charged. Integer, last two digits represent decimals: 10.25 should be 1025
120
+ # @param collect_at Date in which the charge should be made (Format: YYYY-MM-DD).
121
+ # @param creditor_account_id Business' BankAccount that should receive the money. If none passed, account marked as default in Besepa's dashboard will be used.
122
+ # @param metadata Optional Hash with metadata related to this debit, if any.
123
+ #
124
+ # @return new created Besepa::Debit
125
+ def add_debit(debtor_bank_account_id, reference, description, amount, collect_at, creditor_account_id=nil, metadata=nil)
126
+ params = {:reference => reference, :description => description, :debtor_bank_account_id => debtor_bank_account_id,
127
+ :amount => amount, :collect_at => collect_at}
128
+ params[:creditor_account_id] = creditor_account_id if creditor_account_id
129
+ params[:metadata] = metadata if metadata
130
+ Debit.create( params, {:customer_id => id} )
131
+ end
132
+
133
+ end
134
+ end
@@ -0,0 +1,64 @@
1
+ module Besepa
2
+
3
+ class Debit < Besepa::Resource
4
+
5
+ include Besepa::ApiCalls::List
6
+ include Besepa::ApiCalls::Create
7
+ include Besepa::ApiCalls::Update
8
+ include Besepa::ApiCalls::Destroy
9
+
10
+ FIELDS = [:id, :reference, :amount, :currency, :status,
11
+ :collect_at, :sent_at, :description, :metadata,
12
+ :error_code, :platform_error_code]
13
+
14
+ FIELDS.each do |f|
15
+ attr_accessor f
16
+ end
17
+
18
+ attr_accessor :debtor_bank_account, :creditor_bank_account, :customer, :remittance, :subscription
19
+
20
+ def to_hash
21
+ values = {}
22
+ self.class::FIELDS.each do |key|
23
+ values[key] = self.send("#{key.to_s}")
24
+ end
25
+ values[:debtor_bank_account] = debtor_bank_account.to_hash if debtor_bank_account
26
+ values[:creditor_bank_account] = creditor_bank_account.to_hash if creditor_bank_account
27
+ values[:customer] = customer.to_hash if customer
28
+ values[:remittance] = remittance.to_hash if remittance
29
+ values[:subscription] = subscription.to_hash if subscription
30
+ values
31
+ end
32
+
33
+ protected
34
+
35
+ def self.api_path(filters={})
36
+ if filters[:customer_id]
37
+ "#{Customer.api_path}/#{CGI.escape(filters[:customer_id])}/debits"
38
+ else
39
+ "#{Group.api_path}/#{CGI.escape(filters[:group_id])}/debits"
40
+ end
41
+ end
42
+
43
+ def api_path(filters={})
44
+ if filters[:customer_id]
45
+ "#{Customer.api_path}/#{CGI.escape(filters[:customer_id])}/debits/#{CGI.escape(id)}"
46
+ else
47
+ "#{Group.api_path}/#{CGI.escape(filters[:group_id])}/debits/#{CGI.escape(id)}"
48
+ end
49
+ end
50
+
51
+ def process_attributes(attrs)
52
+ self.class::FIELDS.each do |key|
53
+ self.send("#{key.to_s}=", attrs[key.to_s] || attrs[key.to_sym])
54
+ end
55
+ self.debtor_bank_account = Besepa::BankAccount.new(attrs['debtor_bank_account']) if attrs['debtor_bank_account']
56
+ self.creditor_bank_account = Besepa::BusinessAccount.new(attrs['creditor_bank_account']) if attrs['creditor_bank_account']
57
+ self.customer = Besepa::Customer.new(attrs['customer']) if attrs['customer']
58
+ self.remittance = Besepa::Remittance.new(attrs['remittance']) if attrs['remittance']
59
+ self.subscription = Besepa::Subscription.new(attrs['subscription']) if attrs['subscription']
60
+ process_activities(attrs)
61
+ self
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,28 @@
1
+ module Besepa
2
+
3
+ module Errors
4
+
5
+ class BesepaError < StandardError
6
+
7
+ attr_reader :error
8
+ attr_reader :description
9
+ attr_reader :http_status
10
+ attr_reader :messages
11
+
12
+ def initialize(error=nil, description=nil, http_status=nil, messages=nil)
13
+ @messages = messages
14
+ @http_status = http_status
15
+ @description = description
16
+ @error = error
17
+ end
18
+
19
+ def to_s
20
+ status_string = @http_status.nil? ? "" : "(Status #{@http_status}) "
21
+ "#{status_string}#{@error} (#{@description})"
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,11 @@
1
+ module Besepa
2
+
3
+ module Errors
4
+
5
+ class InvalidResourceError < BesepaError
6
+
7
+ end
8
+
9
+ end
10
+
11
+ end
@@ -0,0 +1,11 @@
1
+ module Besepa
2
+
3
+ module Errors
4
+
5
+ class ResourceNotFoundError < BesepaError
6
+
7
+ end
8
+
9
+ end
10
+
11
+ end
@@ -0,0 +1,27 @@
1
+ module Besepa
2
+
3
+ class Group < Besepa::Resource
4
+
5
+ include Besepa::ApiCalls::List
6
+ include Besepa::ApiCalls::Create
7
+ include Besepa::ApiCalls::Update
8
+ include Besepa::ApiCalls::Destroy
9
+
10
+ FIELDS = [:id, :name, :reference]
11
+
12
+ FIELDS.each do |f|
13
+ attr_accessor f
14
+ end
15
+
16
+ protected
17
+
18
+ def self.api_path(filters={})
19
+ if filters[:customer_id]
20
+ "#{Customer.api_path}/#{CGI.escape(filters[:customer_id])}/groups"
21
+ else
22
+ "/groups"
23
+ end
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,18 @@
1
+ module Besepa
2
+
3
+ class Mandate < Besepa::Resource
4
+
5
+ include Besepa::ApiCalls::List
6
+
7
+ FIELDS = [:id, :signed_at, :status, :description, :signature_type, :mandate_type, :reference, :url, :used, :phone_number, :scheme, :signature_url]
8
+
9
+ FIELDS.each do |f|
10
+ attr_accessor f
11
+ end
12
+
13
+ def api_path
14
+ "/customers/#{self.customer_id}/#{self.class.api_path}"
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ module Besepa
2
+
3
+ class Product < Besepa::Resource
4
+
5
+ include Besepa::ApiCalls::List
6
+ include Besepa::ApiCalls::Create
7
+ include Besepa::ApiCalls::Update
8
+ include Besepa::ApiCalls::Destroy
9
+
10
+ FIELDS = [:id, :name, :amount, :currency, :reference,
11
+ :recurrent, :max_charges, :periodicity,
12
+ :status]
13
+
14
+ FIELDS.each do |f|
15
+ attr_accessor f
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,32 @@
1
+ module Besepa
2
+
3
+ class Remittance < Besepa::Resource
4
+
5
+ include Besepa::ApiCalls::List
6
+
7
+ FIELDS = [:id, :collect_at, :send_at, :sent_at, :status, :scheme]
8
+
9
+ attr_accessor :bank_account
10
+
11
+ FIELDS.each do |f|
12
+ attr_accessor f
13
+ end
14
+
15
+ def self.api_path(filters={})
16
+ "/remittances"
17
+ end
18
+
19
+ protected
20
+
21
+ def process_attributes(attrs)
22
+ self.class::FIELDS.each do |key|
23
+ self.send("#{key.to_s}=", attrs[key.to_s] || attrs[key.to_sym])
24
+ end
25
+ self.bank_account = Besepa::BusinessAccount.new(attrs['bank_account']) if attrs['bank_account']
26
+ process_activities(attrs)
27
+ self
28
+ end
29
+
30
+
31
+ end
32
+ end
@@ -0,0 +1,96 @@
1
+ require 'besepa/utils/request'
2
+ require 'besepa/utils/connection'
3
+ require 'besepa/utils/config'
4
+
5
+ module Besepa
6
+
7
+ class Resource
8
+
9
+ class <<self
10
+
11
+ include Besepa::Utils::Connection
12
+ include Besepa::Utils::Request
13
+
14
+ def api_path(filters={})
15
+ "#{klass_name}s"
16
+ end
17
+
18
+ def klass_name
19
+ name.split('::')[-1].downcase
20
+ end
21
+
22
+ def handle_errors(http_status, response)
23
+ error = response['error']
24
+ desc = response['error_description']
25
+ msgs = response['messages']
26
+ if error == 'invalid_resource'
27
+ raise Besepa::Errors::InvalidResourceError.new(error, desc, http_status, msgs)
28
+ elsif error == 'not_found'
29
+ raise Besepa::Errors::ResourceNotFoundError.new(error, desc, http_status, msgs)
30
+ else
31
+ raise Besepa::Errors::BesepaError.new(error, desc, http_status, msgs)
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ include Besepa::Utils::Connection
38
+ include Besepa::Utils::Request
39
+
40
+ attr_accessor :activities
41
+
42
+ def initialize(attrs={})
43
+ process_attributes(attrs)
44
+ end
45
+
46
+ def to_hash
47
+ values = {}
48
+ self.class::FIELDS.each do |key|
49
+ values[key] = self.send("#{key.to_s}")
50
+ end
51
+ values
52
+ end
53
+
54
+ def serializable_hash
55
+ to_hash
56
+ end
57
+
58
+ def as_json
59
+ to_hash.as_json
60
+ end
61
+
62
+ def klass_name
63
+ self.class.name.split('::')[-1].downcase
64
+ end
65
+
66
+ protected
67
+
68
+ def handle_errors(http_status, response)
69
+ Module.const_get(self.class.name).handle_errors(http_status, response)
70
+ end
71
+
72
+ def process_attributes(attrs)
73
+ self.class::FIELDS.each do |key|
74
+ self.send("#{key.to_s}=", attrs[key.to_s] || attrs[key.to_sym])
75
+ end
76
+ process_activities(attrs)
77
+ end
78
+
79
+ def process_activities(attrs)
80
+ if attrs['activities']
81
+ self.activities = Array.new
82
+ attrs['activities'].each do |a|
83
+ self.activities << Besepa::Activity.new(a)
84
+ end
85
+ end
86
+ end
87
+
88
+ # def api_path(filters={})
89
+ # self.class.api_path
90
+ # end
91
+
92
+
93
+ end
94
+
95
+
96
+ end