dune-balanced 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.gitmodules +3 -0
- data/.rspec +2 -0
- data/.travis.yml +13 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +125 -0
- data/LICENSE.txt +22 -0
- data/README.md +63 -0
- data/Rakefile +34 -0
- data/app/controllers/dune/balanced/notifications_controller.rb +11 -0
- data/app/decorators/dune/balanced/customer_decorator.rb +14 -0
- data/app/models/dune/balanced/.gitkip +0 -0
- data/app/models/dune/balanced/contributor.rb +11 -0
- data/app/models/dune/balanced/customer.rb +56 -0
- data/app/models/dune/balanced/event.rb +101 -0
- data/app/models/dune/balanced/order.rb +9 -0
- data/app/models/dune/balanced/order_proxy.rb +55 -0
- data/app/models/dune/balanced/payout.rb +77 -0
- data/app/models/dune/balanced/refund.rb +53 -0
- data/app/models/dune/balanced/user.rb +6 -0
- data/app/observers/dune/balanced/events_observer/debit_canceled.rb +13 -0
- data/bin/rails +8 -0
- data/config/initializers/balanced.rb +2 -0
- data/config/initializers/events_observer.rb +2 -0
- data/config/locales/en.yml +9 -0
- data/config/routes.rb +4 -0
- data/db/migrate/20140211203335_create_balanced_contributors.rb +10 -0
- data/db/migrate/20140324175041_add_bank_account_uri_to_balanced_contributors.rb +5 -0
- data/db/migrate/20140817195359_create_dune_balanced_orders.rb +10 -0
- data/dune-balanced.gemspec +32 -0
- data/lib/dune/balanced.rb +9 -0
- data/lib/dune/balanced/engine.rb +13 -0
- data/lib/dune/balanced/version.rb +5 -0
- data/spec/controllers/dune/balanced/notifications_controller_spec.rb +61 -0
- data/spec/decorators/dune/balanced/customer_decorator_spec.rb +19 -0
- data/spec/fixtures/notifications/bank_account_verification.deposited.yml +48 -0
- data/spec/fixtures/notifications/bank_account_verification.verified.yml +48 -0
- data/spec/fixtures/notifications/debit.canceled.yml +74 -0
- data/spec/fixtures/notifications/debit.created.yml +72 -0
- data/spec/fixtures/notifications/debit.succeeded.yml +72 -0
- data/spec/models/dune/balanced/customer_spec.rb +73 -0
- data/spec/models/dune/balanced/event_spec.rb +219 -0
- data/spec/models/dune/balanced/order_proxy_spec.rb +87 -0
- data/spec/models/dune/balanced/order_spec.rb +8 -0
- data/spec/models/dune/balanced/payout_spec.rb +104 -0
- data/spec/models/dune/balanced/refund_spec.rb +157 -0
- data/spec/observers/dune/balanced/events_observer/debit_canceled_spec.rb +32 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/fixtures_test_helper.rb +11 -0
- metadata +223 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
module Dune::Balanced
|
2
|
+
class OrderProxy
|
3
|
+
I18N_SCOPE = 'dune.balanced.order'
|
4
|
+
|
5
|
+
delegate :user, to: :project
|
6
|
+
delegate :amount, :amount_escrowed, :debit_from, :description, :meta,
|
7
|
+
:reload, :save, to: :order
|
8
|
+
|
9
|
+
attr_reader :project
|
10
|
+
|
11
|
+
def initialize(project)
|
12
|
+
@project = project
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def order
|
18
|
+
@order ||= if order_href
|
19
|
+
::Balanced::Order.find(order_href)
|
20
|
+
else
|
21
|
+
create_order
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def order_href
|
26
|
+
@order_href ||= Order.find_by(project_id: project).try(:href)
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_order
|
30
|
+
subject = Customer.new(user, {}).fetch.create_order
|
31
|
+
Order.create!(href: subject.href, project: project)
|
32
|
+
|
33
|
+
subject.description = I18n.t('description',
|
34
|
+
project_id: project.id,
|
35
|
+
project_name: project.name,
|
36
|
+
scope: I18N_SCOPE
|
37
|
+
)
|
38
|
+
|
39
|
+
project_url = Rails.application.routes.url_helpers.project_url(project)
|
40
|
+
subject.meta = {
|
41
|
+
'Project' => project.name,
|
42
|
+
'Goal' => project.goal,
|
43
|
+
'Campaign Type' => project.campaign_type.humanize,
|
44
|
+
'User' => project.user.name,
|
45
|
+
'Category' => project.category.name_en,
|
46
|
+
'URL' => project_url,
|
47
|
+
'Expires At' => I18n.l(project.expires_at),
|
48
|
+
'ID' => project.id,
|
49
|
+
}
|
50
|
+
subject.save
|
51
|
+
|
52
|
+
subject
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Dune::Balanced
|
2
|
+
class Error < StandardError; end
|
3
|
+
class NoBankAccount < Error; end
|
4
|
+
|
5
|
+
class Payout
|
6
|
+
def initialize(project, requestor_user)
|
7
|
+
@project = project
|
8
|
+
@requestor = requestor_user
|
9
|
+
end
|
10
|
+
|
11
|
+
def complete!(bank_account_href = nil)
|
12
|
+
credit_project_owner!(bank_account_href)
|
13
|
+
credit_platform!
|
14
|
+
end
|
15
|
+
|
16
|
+
def customer
|
17
|
+
dune_customer.fetch
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def credit_project_owner!(bank_account_href = nil)
|
23
|
+
to_be_credited = if bank_account_href
|
24
|
+
Balanced::BankAccount.find(bank_account_href)
|
25
|
+
else
|
26
|
+
customer.bank_accounts.first
|
27
|
+
end
|
28
|
+
|
29
|
+
credit!(to_be_credited, financials.net_amount)
|
30
|
+
end
|
31
|
+
|
32
|
+
def credit_platform!
|
33
|
+
to_be_credited =
|
34
|
+
Balanced::Marketplace.mine.owner_customer.bank_accounts.first
|
35
|
+
credit!(to_be_credited, financials.platform_fee)
|
36
|
+
end
|
37
|
+
|
38
|
+
def order
|
39
|
+
@order ||= begin
|
40
|
+
href = Dune::Balanced::Order
|
41
|
+
.find_by(project_id: @project.id).href
|
42
|
+
Balanced::Order.find(href)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def financials
|
47
|
+
@financials ||= ProjectFinancialByService
|
48
|
+
.new(p, %w(balanced-bankaccount balanced-creditcard))
|
49
|
+
end
|
50
|
+
|
51
|
+
def dune_customer
|
52
|
+
@dune_customer ||= Dune::Balanced::Customer.new(@project.user, {})
|
53
|
+
end
|
54
|
+
|
55
|
+
def credit!(bank_account, amount)
|
56
|
+
if bank_account.blank?
|
57
|
+
raise NoBankAccount, 'The customer doesn\'t have a bank account to credit.'
|
58
|
+
end
|
59
|
+
|
60
|
+
order.credit_to(
|
61
|
+
amount: in_cents(amount),
|
62
|
+
destination: bank_account,
|
63
|
+
)
|
64
|
+
::Payout.create(
|
65
|
+
bank_account_href: bank_account.href,
|
66
|
+
payment_service: 'balanced',
|
67
|
+
project_id: @project.id,
|
68
|
+
user_id: @requestor.id,
|
69
|
+
value: amount,
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
def in_cents(amount)
|
74
|
+
(amount * 100).round
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Dune::Balanced
|
2
|
+
class Refund
|
3
|
+
FIXED_OPERATIONAL_FEE = 0.3
|
4
|
+
|
5
|
+
attr_reader :paid_resource
|
6
|
+
|
7
|
+
def initialize(paid_resource)
|
8
|
+
@paid_resource = paid_resource
|
9
|
+
end
|
10
|
+
|
11
|
+
def complete!(reason, amount = paid_resource.value)
|
12
|
+
unless amount.zero?
|
13
|
+
refund_amount = ((amount + refundable_fees(amount)) * 100).round
|
14
|
+
debit.refund(
|
15
|
+
amount: refund_amount,
|
16
|
+
description: I18n.t('dune.balanced.refund.description',
|
17
|
+
resource_id: paid_resource.id,
|
18
|
+
resource_name: paid_resource.class.model_name.human
|
19
|
+
),
|
20
|
+
meta: {
|
21
|
+
'reason' => I18n.t("dune.balanced.refund_reasons.#{reason}")
|
22
|
+
}
|
23
|
+
)
|
24
|
+
end
|
25
|
+
paid_resource.refund!
|
26
|
+
end
|
27
|
+
|
28
|
+
def debit
|
29
|
+
@debit ||= ::Balanced::Debit.find("/debits/#{paid_resource.payment_id}")
|
30
|
+
end
|
31
|
+
|
32
|
+
def refundable_fees(refund_amount)
|
33
|
+
percentual_fee = if paid_resource.payment_service_fee_paid_by_user
|
34
|
+
refund_amount / paid_resource.value * paid_resource.payment_service_fee
|
35
|
+
else
|
36
|
+
0
|
37
|
+
end
|
38
|
+
|
39
|
+
(percentual_fee - FIXED_OPERATIONAL_FEE).round(2)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def resource_amount
|
45
|
+
to_be_refunded = if paid_resource.payment_service_fee_paid_by_user
|
46
|
+
paid_resource.value + paid_resource.payment_service_fee
|
47
|
+
else
|
48
|
+
paid_resource.value
|
49
|
+
end
|
50
|
+
(to_be_refunded * 100 - FIXED_OPERATIONAL_FEE).round
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/bin/rails
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application.
|
3
|
+
|
4
|
+
ENGINE_ROOT = File.expand_path('../..', __FILE__)
|
5
|
+
ENGINE_PATH = File.expand_path('../../lib/dune/balanced/engine', __FILE__)
|
6
|
+
|
7
|
+
require 'rails/all'
|
8
|
+
require 'rails/engine/commands'
|
@@ -0,0 +1,9 @@
|
|
1
|
+
en:
|
2
|
+
dune:
|
3
|
+
balanced:
|
4
|
+
order:
|
5
|
+
description: "Project #%{project_id} %{project_name}"
|
6
|
+
refund:
|
7
|
+
description: 'Refund for %{resource_name} #%{resource_id}'
|
8
|
+
refund_reasons:
|
9
|
+
match_automatic: 'Automatic refund done after match finishing without use entire paid value'
|
data/config/routes.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'dune/balanced/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'dune-balanced'
|
8
|
+
spec.version = Dune::Balanced::VERSION
|
9
|
+
spec.authors = ['Pierro']
|
10
|
+
spec.email = %w(legrand.work@gmail.com)
|
11
|
+
spec.summary = 'dune integration with Balanced Payments From Neighborly'
|
12
|
+
spec.description = 'This is the base to integrate Balanced Payments on dune-investissement'
|
13
|
+
spec.homepage = 'https://github.com/FromUte/dune-balanced'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
# faraday_middleware 0.9.1 is raising
|
22
|
+
# NoMethodError: undefined method `register_middleware' for #<Faraday::Connection:0x00000002ebc178>
|
23
|
+
spec.add_dependency 'faraday', '0.8.9'
|
24
|
+
spec.add_dependency 'faraday_middleware', '0.9.0'
|
25
|
+
|
26
|
+
spec.add_dependency 'balanced', '~> 1.1'
|
27
|
+
spec.add_dependency 'draper', '~> 1.3'
|
28
|
+
spec.add_dependency 'rails', '~> 4.0'
|
29
|
+
spec.add_development_dependency 'rspec-rails', '~> 2.14'
|
30
|
+
spec.add_development_dependency 'shoulda-matchers'
|
31
|
+
spec.add_development_dependency 'sqlite3', '~> 1.3'
|
32
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Dune
|
2
|
+
module Balanced
|
3
|
+
class Engine < ::Rails::Engine
|
4
|
+
isolate_namespace Dune::Balanced
|
5
|
+
|
6
|
+
config.autoload_paths += Dir["#{config.root}/app/observers/**/"]
|
7
|
+
|
8
|
+
config.to_prepare do
|
9
|
+
::User.send(:include, Dune::Balanced::User)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dune::Balanced::NotificationsController do
|
4
|
+
routes { Dune::Balanced::Engine.routes }
|
5
|
+
let(:event) { double('Event') }
|
6
|
+
|
7
|
+
shared_examples 'create action' do
|
8
|
+
it 'saves a new event' do
|
9
|
+
expect_any_instance_of(Dune::Balanced::Event).to receive(:save)
|
10
|
+
post :create, params
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'with valid event' do
|
14
|
+
before do
|
15
|
+
Dune::Balanced::Event.any_instance.stub(:valid?).and_return(true)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'responds with 200 http status' do
|
19
|
+
post :create, params
|
20
|
+
expect(response.status).to eql(200)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'with invalid event' do
|
25
|
+
before do
|
26
|
+
Dune::Balanced::Event.any_instance.stub(:valid?).and_return(false)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'responds with 400 http status' do
|
30
|
+
post :create, params
|
31
|
+
expect(response.status).to eql(400)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'POST \'create\'' do
|
37
|
+
context 'with debit.created notification' do
|
38
|
+
let(:params) do
|
39
|
+
attributes_for_notification('debit.created')
|
40
|
+
end
|
41
|
+
|
42
|
+
it_behaves_like 'create action'
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'with debit.succeeded notification' do
|
46
|
+
let(:params) do
|
47
|
+
attributes_for_notification('debit.succeeded')
|
48
|
+
end
|
49
|
+
|
50
|
+
it_behaves_like 'create action'
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'with bank_account_verification.deposited notification' do
|
54
|
+
let(:params) do
|
55
|
+
attributes_for_notification('bank_account_verification.deposited')
|
56
|
+
end
|
57
|
+
|
58
|
+
it_behaves_like 'create action'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dune::Balanced::CustomerDecorator do
|
4
|
+
let(:remote_source) { double('Balanced::Customer') }
|
5
|
+
let(:source) do
|
6
|
+
double('Dune::Balanced::Customer',fetch: remote_source)
|
7
|
+
end
|
8
|
+
subject { described_class.new(source) }
|
9
|
+
|
10
|
+
describe 'bank account name' do
|
11
|
+
it 'gets this info fetching the bank accout of the customer' do
|
12
|
+
bank_account = double('Balanced::BankAccount',
|
13
|
+
bank_name: 'JPMORGAN CHASE BANK',
|
14
|
+
account_number: 'xxxxxx0002')
|
15
|
+
remote_source.stub(:bank_accounts).and_return([bank_account])
|
16
|
+
expect(subject.bank_account_name).to eql('JPMORGAN CHASE BANK xxxxxx0002')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
---
|
2
|
+
events:
|
3
|
+
- links: {}
|
4
|
+
occurred_at: '2014-08-14T18:58:44.850000Z'
|
5
|
+
uri: "/events/EV048db6b423e511e4b0ef0647853a3607"
|
6
|
+
entity:
|
7
|
+
bank_account_verifications:
|
8
|
+
- verification_status: pending
|
9
|
+
links:
|
10
|
+
bank_account: BA7AA3yiW6upqETZwU8pyqYg
|
11
|
+
created_at: '2014-08-14T18:58:44.399925Z'
|
12
|
+
attempts_remaining: 3
|
13
|
+
updated_at: '2014-08-14T18:58:44.850225Z'
|
14
|
+
deposit_status: succeeded
|
15
|
+
attempts: 0
|
16
|
+
href: "/verifications/BZ6V6wsEkufWOpfszp360nj"
|
17
|
+
meta: {}
|
18
|
+
id: BZ6V6wsEkufWOpfszp360nj
|
19
|
+
links:
|
20
|
+
bank_account_verifications.bank_account: "/bank_accounts/{bank_account_verifications.bank_account}"
|
21
|
+
href: "/events/EV048db6b423e511e4b0ef0647853a3607"
|
22
|
+
type: bank_account_verification.deposited
|
23
|
+
id: EV048db6b423e511e4b0ef0647853a3607
|
24
|
+
links: {}
|
25
|
+
registration:
|
26
|
+
events:
|
27
|
+
- links: {}
|
28
|
+
occurred_at: '2014-08-14T18:58:44.850000Z'
|
29
|
+
uri: "/events/EV048db6b423e511e4b0ef0647853a3607"
|
30
|
+
entity:
|
31
|
+
bank_account_verifications:
|
32
|
+
- verification_status: pending
|
33
|
+
links:
|
34
|
+
bank_account: BA7AA3yiW6upqETZwU8pyqYg
|
35
|
+
created_at: '2014-08-14T18:58:44.399925Z'
|
36
|
+
attempts_remaining: 3
|
37
|
+
updated_at: '2014-08-14T18:58:44.850225Z'
|
38
|
+
deposit_status: succeeded
|
39
|
+
attempts: 0
|
40
|
+
href: "/verifications/BZ6V6wsEkufWOpfszp360nj"
|
41
|
+
meta: {}
|
42
|
+
id: BZ6V6wsEkufWOpfszp360nj
|
43
|
+
links:
|
44
|
+
bank_account_verifications.bank_account: "/bank_accounts/{bank_account_verifications.bank_account}"
|
45
|
+
href: "/events/EV048db6b423e511e4b0ef0647853a3607"
|
46
|
+
type: bank_account_verification.deposited
|
47
|
+
id: EV048db6b423e511e4b0ef0647853a3607
|
48
|
+
links: {}
|