Skip to content

Commit

Permalink
Merge 181b8f3 into 9ab09ce
Browse files Browse the repository at this point in the history
  • Loading branch information
kendallcorner committed May 11, 2018
2 parents 9ab09ce + 181b8f3 commit bb66b31
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 72 deletions.
4 changes: 3 additions & 1 deletion payments/management/commands/calculate.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pprint
from decimal import Decimal

from django.core.management.base import BaseCommand

Expand All @@ -10,9 +11,10 @@
class Command(BaseCommand):

def add_arguments(self, parser):
parser.add_argument('amount', nargs='+', type=int)
parser.add_argument('amount', nargs='+', type=float)

def handle(self, *args, **options):
amount = options['amount'][0]
amount = Decimal(amount)
details = utils.transaction_amounts(amount)
pp.pprint(details)
Empty file added payments/test.txt
Empty file.
94 changes: 73 additions & 21 deletions payments/tests/util_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,91 @@ class PaymentAmountTest(TestCase):

def test_calculate_amounts(self):
amounts = range(1, 1000)
nonconvergances = 0
nonconvergance_rate = 0
amount_count = 0

for amount in amounts:
print "---"
offer_values = utils.transaction_amounts(amount)

for comp in offer_values:
print u'%s : %s' % (comp, offer_values[comp])

offer_components = (
amount
+ offer_values['codesy_fee']
+ offer_values['offer_stripe_fee']
# Application fee is made up of 2 codesyfees, the fee from stripe
# for the credit card charnge, and the fee from stripe for the
# transfer
application_fee_components = (
2 * offer_values['codesy_fee'] +
offer_values['charge_stripe_fee'] +
offer_values['actual_transfer_fee']
)

self.assertEqual(
application_fee_components,
offer_values['application_fee']
)

# Application fee is also made up of the total fees the offer is
# covering and the total fees the payout is covering.
application_fee_components = (
offer_values['offer_fee'] +
offer_values['payout_fee']
)

self.assertEqual(
application_fee_components,
offer_values['application_fee']
)

# The amount and offer fees always add up to the charge amount
self.assertEqual(
offer_components,
amount + offer_values['offer_fee'],
offer_values['charge_amount']
)

payout_components = (
amount
- offer_values['codesy_fee']
- offer_values['payout_stripe_fee']
# The payout amount and fees add up to the amount, unless the
# application fee has an odd penny.
payout_test = (abs(offer_values['payout_amount'] +
offer_values['payout_fee'] - amount) <= 0.01)

self.assertEqual(
True,
payout_test
)

# The payout amount and application fee should add up to the charge
# amount unless the solution does not coverge.
application_fee_test = (abs(offer_values['payout_amount'] +
offer_values['application_fee'] -
offer_values['charge_amount']) <= 0.01)

self.assertEqual(
payout_components,
offer_values['payout_amount']
True,
application_fee_test
)

# The non-covergance rate should be low
amount_count += 1.0

payout_test = (offer_values['payout_amount'] +
offer_values['payout_fee'] == amount)

application_fee_test = (offer_values['payout_amount'] +
offer_values['application_fee'] ==
offer_values['charge_amount'])

if not(payout_test and application_fee_test):
nonconvergances += 1.0

nonconvergance_rate = float(nonconvergances / amount_count)
print u'n nonconvergances = %s, amount_count = %s, \
nonconvergance_rate = %s' % (nonconvergances, amount_count,
nonconvergance_rate)

self.assertEqual(
offer_values['payout_amount'],
(offer_values['charge_amount']
- offer_values['application_fee']
)
True,
nonconvergance_rate < (2.0 / 500)
)

def test_fixed_amounts(self):
Expand All @@ -56,11 +108,11 @@ def test_fixed_amounts(self):
self.assertEqual(values['actual_transfer_fee'], Decimal('0.05'))

values = utils.transaction_amounts(50)
self.assertEqual(values['total_stripe_fee'], Decimal('2.08'))
self.assertEqual(values['application_fee'], Decimal('4.58'))
self.assertEqual(values['total_stripe_fee'], Decimal('2.06'))
self.assertEqual(values['application_fee'], Decimal('4.56'))
self.assertEqual(values['codesy_fee'], Decimal('1.25'))
self.assertEqual(values['charge_amount'], Decimal('52.29'))
self.assertEqual(values['offer_stripe_fee'], Decimal('1.04'))
self.assertEqual(values['payout_amount'], Decimal('47.71'))
self.assertEqual(values['payout_stripe_fee'], Decimal('1.04'))
self.assertEqual(values['charge_amount'], Decimal('52.28'))
self.assertEqual(values['offer_stripe_fee'], Decimal('1.03'))
self.assertEqual(values['payout_amount'], Decimal('47.72'))
self.assertEqual(values['payout_stripe_fee'], Decimal('1.03'))
self.assertEqual(values['actual_transfer_fee'], Decimal('0.24'))
143 changes: 93 additions & 50 deletions payments/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,86 +21,129 @@ def round_penny(amount):
return amount.quantize(Decimal('.01'), rounding=ROUND_HALF_UP)


def calculate_codesy_fee(amount):
def calculate_half_codesy_fee(amount):
return round_penny(amount * codesy_pct)


def calculate_stripe_fee(amount):
def calculate_charge_stripe_fee(offer_charge):
return round_penny(
(amount * (stripe_pct + stripe_transfer_pct)) + stripe_transaction
offer_charge * stripe_pct + stripe_transaction
)


def calculate_stripe_transfer(amount):
return round_penny(amount * stripe_transfer_pct)


def calculate_charge_amount(goal):
def calculate_transfer_stripe_fee(payout_amount):
return round_penny(
(goal + stripe_transaction) / (1 - stripe_pct)
)


def calculate_offer_charge(goal):
return round_penny(
(goal + (stripe_transaction / 2))
/ (1 - ((stripe_pct + stripe_transfer_pct) / 2))
payout_amount * stripe_transfer_pct
)


def transaction_amounts(amount):
codesy_fee_amount = calculate_codesy_fee(amount)

charge_amount = calculate_offer_charge(
amount
+ codesy_fee_amount
)
# if amount <= 0:
# raise ValueError('Zeros and negatives are not allowed')
charge_guess = 0
payout_guess = 0
half_codesy_fee_amount = calculate_half_codesy_fee(amount)
# Codesy's total 5% fee.
charge_stripe_fee = 0
# Stripe's actual fee on the charged amount--should equal Stripe's info.
transfer_stripe_fee = 0
# Stripe's actual fee on the payout amount--should equal Stripe's info.
application_fee = 0
# total fees taken out
# Information given to Stripe
iteration = 0
calc_charge = 0
calc_payout = 0

# |--F--|
# -+------------+--+--+----
# 0 T A C
#
# A = agreed amount
# C = charge = A + 1/2*F
# T = payout transfer = A - 1/2*F
# F = Fees = rp(C*r_sc + f_sc) + rp(A*r_c) + rp(T*r_st)
# r_sc = stripe rate for charges
# f_sc = stripe flat fee for charges
# r_st = stripe rate for payout transfers
# r_c = codesy rate
# rp = round_penny function rounds to the nearest penney

for iteration in range(0, 11):
# used a while originally, but sometimes the while gets stuck
charge_guess = calc_charge
payout_guess = calc_payout
charge_stripe_fee = calculate_charge_stripe_fee(charge_guess)
transfer_stripe_fee = calculate_transfer_stripe_fee(payout_guess)
application_fee = (
2 * half_codesy_fee_amount + charge_stripe_fee +
transfer_stripe_fee
)
calc_charge = round_penny(amount + application_fee / 2)
calc_payout = round_penny(calc_charge - application_fee)
# Note: when total fees are uneven, charge gets the extra penny
if (charge_guess == calc_charge) and (payout_guess == calc_payout):
# breaks out when guess and calc are equal
break

offer_stripe_fee = (
charge_amount
- amount
- codesy_fee_amount
)
charge_amount = charge_guess
# Amount charged to the bidder/offerer's card or account
# information given to Stripe

total_stripe_fee = calculate_stripe_fee(charge_amount)
payout_amount = payout_guess
# Amount that ends up in the asker/fixer's bank account

payout_stripe_fee = total_stripe_fee - offer_stripe_fee
offer_fee = round_penny(application_fee / 2)
# Part of the fee-split added on to offer. Calculated for testing purposes.
# Note: when total fees are uneven, offer gets the extra penny

payout_amount = (
amount
- codesy_fee_amount
- payout_stripe_fee
)
offer_stripe_fee = offer_fee - half_codesy_fee_amount

application_fee = (
offer_stripe_fee
+ payout_stripe_fee
+ (codesy_fee_amount * 2)
)
payout_fee = application_fee - offer_fee
# part of the fee-split taken out of payout. Calculated for testing
# purposes. Note: when total fees are uneven, offer gets the extra penny

gross_transfer_fee = calculate_stripe_transfer(amount)
payout_stripe_fee = payout_fee - half_codesy_fee_amount

actual_transfer_fee = (
calculate_stripe_transfer(payout_amount)
payout_alt_calc = (
charge_amount - application_fee
)
# back calculating payout for testing

transfer_overage = (
gross_transfer_fee
- actual_transfer_fee
payout_overage = (
payout_alt_calc
- payout_amount
)

total_stripe_fee = charge_stripe_fee + transfer_stripe_fee

return {
'amount': amount,
'charge_amount': round_penny(charge_amount),
'charge_amount': charge_amount,
'payout_amount': payout_amount,
'codesy_fee': codesy_fee_amount,
'codesy_fee': half_codesy_fee_amount,
'total_stripe_fee': total_stripe_fee,
'offer_stripe_fee': offer_stripe_fee,
# offer's half of total stripe fees
'payout_stripe_fee': payout_stripe_fee,
# payout's half of total stripe fees
'charge_stripe_fee': charge_stripe_fee,
# Stripe's fee on the credit card charge
'gross_transfer_fee': calculate_transfer_stripe_fee(payout_alt_calc),
'actual_transfer_fee': transfer_stripe_fee,
# Stripe's fee on the payout transfer
'offer_fee': offer_fee,
# total fees given to offer
'payout_fee': payout_fee,
# total fees given to payout
'application_fee': application_fee,
'gross_transfer_fee': gross_transfer_fee,
'actual_transfer_fee': actual_transfer_fee,
'transfer_overage': transfer_overage
'payout_alt_calc': payout_alt_calc,
'payout_overage': payout_overage,
'miscalculation_of_total_stripe_fee': (
total_stripe_fee - charge_stripe_fee -
transfer_stripe_fee
),
'iterations': iteration
}


Expand Down

0 comments on commit bb66b31

Please sign in to comment.