besepa 0.2

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