Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Currencies - part 2 #768

Merged
merged 7 commits into from
Oct 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 24 additions & 19 deletions liberapay/billing/fees.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@
"""
from __future__ import division, print_function, unicode_literals

from decimal import Decimal, ROUND_UP

from pando.utils import typecheck
from mangopay.utils import Money

from liberapay.constants import (
D_CENT,
PAYIN_CARD_MIN, FEE_PAYIN_CARD,
FEE_PAYIN_BANK_WIRE, PAYIN_BANK_WIRE_MIN,
FEE_PAYIN_DIRECT_DEBIT, PAYIN_DIRECT_DEBIT_MIN,
FEE_PAYOUT, FEE_PAYOUT_OUTSIDE_SEPA, SEPA,
FEE_VAT,
FEE_PAYOUT,
Fees,
)


def upcharge(amount, fees, min_amount):
def upcharge(amount, fees, min_amounts):
"""Given an amount, return a higher amount and the difference.
"""
typecheck(amount, Decimal)
assert isinstance(amount, Money), type(amount)

fees = fees if isinstance(fees, Fees) else fees[amount.currency]

min_amount = min_amounts[amount.currency]
if amount < min_amount:
amount = min_amount

Expand All @@ -30,14 +30,14 @@ def upcharge(amount, fees, min_amount):
fee = charge_amount - amount

# + VAT
vat = fee * FEE_VAT
vat = fee * Fees.VAT
charge_amount += vat
fee += vat

# Round
charge_amount = charge_amount.quantize(D_CENT, rounding=ROUND_UP)
fee = fee.quantize(D_CENT, rounding=ROUND_UP)
vat = vat.quantize(D_CENT, rounding=ROUND_UP)
charge_amount = charge_amount.round_up()
fee = fee.round_up()
vat = vat.round_up()

return charge_amount, fee, vat

Expand All @@ -51,10 +51,10 @@ def skim_amount(amount, fees):
"""Given a nominal amount, compute the fees, taxes, and the actual amount.
"""
fee = amount * fees.var + fees.fix
vat = fee * FEE_VAT
vat = fee * Fees.VAT
fee += vat
fee = fee.quantize(D_CENT, rounding=ROUND_UP)
vat = vat.quantize(D_CENT, rounding=ROUND_UP)
fee = fee.round_up()
vat = vat.round_up()
return amount - fee, fee, vat


Expand All @@ -76,10 +76,15 @@ def skim_credit(amount, ba):

The returned amount can be negative, look out for that.
"""
typecheck(amount, Decimal)
assert isinstance(amount, Money), type(amount)
fees = FEE_PAYOUT[amount.currency]
country = get_bank_account_country(ba)
if country in SEPA:
fee = FEE_PAYOUT
if 'domestic' in fees:
countries, domestic_fee = fees['domestic']
if country in countries:
fee = domestic_fee
else:
fee = fees['foreign']
else:
fee = FEE_PAYOUT_OUTSIDE_SEPA
fee = fees['*']
return skim_amount(amount, fee)
41 changes: 25 additions & 16 deletions liberapay/billing/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
SettlementTransfer, Transfer, User, Wallet,
)
from mangopay.utils import Money
from pando.utils import typecheck

from liberapay.billing.fees import (
skim_bank_wire, skim_credit, upcharge_card, upcharge_direct_debit
Expand Down Expand Up @@ -84,6 +83,7 @@ def payout(db, route, amount, ignore_high_fee=False):
ba = BankAccount.get(route.address, user_id=participant.mangopay_user_id)

# Do final calculations
amount = Money(amount, 'EUR') if isinstance(amount, Decimal) else amount
credit_amount, fee, vat = skim_credit(amount, ba)
if credit_amount <= 0 and fee > 0:
raise FeeExceedsAmount
Expand All @@ -95,9 +95,9 @@ def payout(db, route, amount, ignore_high_fee=False):
e_id = record_exchange(db, route, -credit_amount, fee, vat, participant, 'pre').id
payout = BankWirePayOut()
payout.AuthorId = participant.mangopay_user_id
payout.DebitedFunds = Money(int(amount * 100), 'EUR')
payout.DebitedFunds = amount.int()
payout.DebitedWalletId = participant.mangopay_wallet_id
payout.Fees = Money(int(fee * 100), 'EUR')
payout.Fees = fee.int()
payout.BankAccountId = route.address
payout.BankWireRef = str(e_id)
payout.Tag = str(e_id)
Expand All @@ -119,12 +119,13 @@ def charge(db, route, amount, return_url):
and add it to amount to end up with charge_amount.

"""
typecheck(amount, Decimal)
assert isinstance(amount, (Decimal, Money)), type(amount)
assert route
assert route.network == 'mango-cc'

participant = route.participant

amount = Money(amount, 'EUR') if isinstance(amount, Decimal) else amount
charge_amount, fee, vat = upcharge_card(amount)
amount = charge_amount - fee

Expand All @@ -135,10 +136,10 @@ def charge(db, route, amount, return_url):
payin = DirectPayIn()
payin.AuthorId = participant.mangopay_user_id
payin.CreditedWalletId = participant.mangopay_wallet_id
payin.DebitedFunds = Money(int(charge_amount * 100), 'EUR')
payin.DebitedFunds = charge_amount.int()
payin.CardId = route.address
payin.SecureModeReturnURL = return_url
payin.Fees = Money(int(fee * 100), 'EUR')
payin.Fees = fee.int()
payin.Tag = str(e_id)
try:
test_hook()
Expand All @@ -158,12 +159,13 @@ def charge(db, route, amount, return_url):
def prepare_direct_debit(db, route, amount):
"""Prepare to debit a bank account.
"""
typecheck(amount, Decimal)
assert isinstance(amount, (Decimal, Money)), type(amount)

assert route.network == 'mango-ba'

participant = route.participant

amount = Money(amount, 'EUR') if isinstance(amount, Decimal) else amount
debit_amount, fee, vat = upcharge_direct_debit(amount)
amount = debit_amount - fee

Expand Down Expand Up @@ -197,16 +199,16 @@ def execute_direct_debit(db, exchange, route):

assert exchange.status == 'pre'

amount, fee = exchange.amount, exchange.fee
amount, fee = Money(exchange.amount, 'EUR'), Money(exchange.fee, 'EUR')
debit_amount = amount + fee

e_id = exchange.id
payin = DirectDebitDirectPayIn()
payin.AuthorId = participant.mangopay_user_id
payin.CreditedWalletId = participant.mangopay_wallet_id
payin.DebitedFunds = Money(int(debit_amount * 100), 'EUR')
payin.DebitedFunds = debit_amount.int()
payin.MandateId = route.mandate
payin.Fees = Money(int(fee * 100), 'EUR')
payin.Fees = fee.int()
payin.Tag = str(e_id)
try:
test_hook()
Expand All @@ -229,6 +231,8 @@ def payin_bank_wire(db, participant, debit_amount):

route = ExchangeRoute.upsert_bankwire_route(participant)

if not isinstance(debit_amount, Money):
debit_amount = Money(debit_amount, 'EUR')
amount, fee, vat = skim_bank_wire(debit_amount)

if not participant.mangopay_wallet_id:
Expand All @@ -238,8 +242,8 @@ def payin_bank_wire(db, participant, debit_amount):
payin = BankWirePayIn()
payin.AuthorId = participant.mangopay_user_id
payin.CreditedWalletId = participant.mangopay_wallet_id
payin.DeclaredDebitedFunds = Money(int(debit_amount * 100), 'EUR')
payin.DeclaredFees = Money(int(fee * 100), 'EUR')
payin.DeclaredDebitedFunds = debit_amount.int()
payin.DeclaredFees = fee.int()
payin.Tag = str(e_id)
try:
test_hook()
Expand All @@ -258,19 +262,20 @@ def record_unexpected_payin(db, payin):
"""Record an unexpected bank wire payin.
"""
assert payin.PaymentType == 'BANK_WIRE'
amount = Decimal(payin.DebitedFunds.Amount) / Decimal(100)
paid_fee = Decimal(payin.Fees.Amount) / Decimal(100)
vat = skim_bank_wire(amount)[2]
debited_amount = payin.DebitedFunds / Decimal(100)
paid_fee = payin.Fees / Decimal(100)
vat = skim_bank_wire(debited_amount)[2].amount
wallet_id = payin.CreditedWalletId
participant = Participant.from_mangopay_user_id(payin.AuthorId)
assert participant.mangopay_wallet_id == wallet_id
route = ExchangeRoute.upsert_bankwire_route(participant)
amount = (debited_amount - paid_fee).amount
return db.one("""
INSERT INTO exchanges
(amount, fee, vat, participant, status, route, note, remote_id, wallet_id)
VALUES (%s, %s, %s, %s, 'created', %s, NULL, %s, %s)
RETURNING id
""", (amount, paid_fee, vat, participant.id, route.id, payin.Id, wallet_id))
""", (amount, paid_fee.amount, vat, participant.id, route.id, payin.Id, wallet_id))


def record_payout_refund(db, payout_refund):
Expand Down Expand Up @@ -314,6 +319,10 @@ def record_exchange(db, route, amount, fee, vat, participant, status, error=None
if participant.is_suspended:
raise AccountSuspended()

amount = getattr(amount, 'amount', amount)
fee = getattr(fee, 'amount', fee)
vat = getattr(vat, 'amount', vat)

with db.get_cursor() as cursor:

wallet_id = participant.mangopay_wallet_id
Expand Down
Loading