#ryan-connect-application-fee-refund

1 messages · Page 1 of 1 (latest)

thorny coral
#

hey ryan, you'll need to review the balance transactions in this case to see that level of detail

eternal flint
#

I found that this even tends to be innacurate in certain cases

thorny coral
#

can you provide an example of that?

eternal flint
#

yea give me a second

#

pi_2Ja4DaUmS57H7yUM0anAJZK6

#

so we do a partial refund direct to the buyer and avoid taking money from the Connect account

#

This happens when we have taken too much in taxes and want to do a partial refund direct to the customer because it was part of the application fee that directly went to us

#

however when we look at the last refund re_2Ja4DaUmS57H7yUM0L4J0fU3

#

There is no way to see how much has come out of the primary(my) stripe account

#

As the partial refund was never logged as a application fee refund so when we do the full refund it lists that it is doing a full refund of the application fee when in reality it is only doing part of it because we already refunded a certain amount.

#

I know we could do an application fee refund then do a reverse transfer so it correctly shows up in stripe, but that means I could incur a double exchange fee when that may not be necessary

thorny coral
#

I'm not sure about that last part, but looking at the refund eg, does the balance transaction not show you what you are looking for here with the amount moved from the account?

eternal flint
#

It shows the full amount moved from my account in EUR but I can't find how much was moved from the Connect account

thorny coral
#

Ah i think i see now

#

which in this case reveals the amount withdrawn in eur

thorny coral
#

Did that get you the info you needed?

eternal flint
#

not really unfortunately

#

it doesn't show the hidden exchange rate fee

thorny coral
#

where are you looking, exactly?

eternal flint
#

we looked in transfer_reversal, we looked in charge.refunds we looked in charge.application_fee.refunds

#

hrm it might work though

#

let me try

#

nope doesn't show enough info. We refund 100 EUR, transfer reversal says 100 EUR. We have no idea how much came out of the connect account or our account as it kinda mushes it together

#

so lets say we have 110 EUR charge with 25 EUR application fee.

We do an initial partial refund of 10 EUR from my account to the buyer because of a tax error.

Then we do a 100 EUR refund. I think stripe pulls 100 EUR from the connect account and take 15 EUR from my account

#

But there is no way to see the 15 EUR from the account. I can do math on my side to figure it out, but this isn't always accurate due to exchange rates changing over time. So it might actually pull say 15.50 EUR to account for a fluctuation in the charge amount exchange rate

#

It would be so much simpler though if we could just get the net changes in my account and the seller account after a given transaction

fossil spade
#

@eternal flint What matters here is the BalanceTransaction. This represents the real movement of funds in your own balance. Every object that represents money movement (Charge, Refund, Transfer, TransferReversal, etc.) has an associated balance_transaction property with all the information you're after. What you can do is use the https://stripe.com/docs/expand feature to expand those in full BalanceTransaction objects
Does that make sense?

eternal flint
fossil spade
#

Every single object has a BalanceTransaction like I mentioned, so you find the information on the relevant object
If you have a Destination charge you have a Charge on the platform (ch_123) that creates a Transfer (tr_123) still on the platform and that creates what we call the "destination payment" on the connected account which is a Charge (py_123)
When you refund the charge you get a Refund (re_123) and you also pull funds back from the connected account by reversing the transfer and getting a TransferReversal (trr_123) and that refunds the "destination payment" so you get a Refund on the connected account (pyr_123)

Each of those objects (+ the ApplicationFee and ApplicationFeeRefund if you use that) will have the balance_transaction property with the information you want

#

Can you share some code on how you create a refund today via the API? Then I can show you how to tweak it

eternal flint
#

I honestly believe my code is too far gone at this point

#
          Stripe::Refund.create(
            charge: charge_id,
            amount: amount_cents, # 90000
            refund_application_fee: true,
            reverse_transfer: true,
            expand: ['balance_transaction', 'charge.application_fee.balance_transaction'],
          )

When I look at the transfer_reversal here I only see 90000.

fossil spade
#

yeah you don't expand enougj objects really

#
  charge: charge_id,
  amount: amount_cents, # 90000
  refund_application_fee: true,
  reverse_transfer: true,
  expand: [
    'balance_transaction', # BT for the Refund on your account
    'source_transfer_reversal.balance_transaction', # BT for the transfer reversal
    'source_transfer_reversal.destination_payment_refund.balance_transaction', # BT for the refund on the connected account
  ],
})```
#

try ^, this should show you most of the relevant objects. The only tricky one left is Application Fee because they are harder to debug. But let's start with this and you looking at each relevant object

eternal flint
#

source_transfer_reversal is null for me

fossil spade
#

ah yes my bad, sorry all the names are so close to each other you want transfer_reversal

#

Basically it's Refund re_123 -> transfer_reversal -> TransferReversal trr_123 -> destination_payment_refund -> Refund pyr_123
and the way back it's Refund pyr_123 -> source_transfer_reversal -> TransferReversal trr_123 -> source_refund -> Refund re_123

eternal flint
#

so the original charge is in GBP, the Connect account is in EUR

#

the refund.transfer_reversal.destination_payment_refund.balance_transaction returns the full refund amount in EUR

#

which doesn't tell me the net amount that comes out of the refund

#

I was able to get the application fee but it is a really ugly process

#

but that is the application fee only in the Connect currency and not my account

#

the way I get it now is by going into refund.charge.refunds and refund.charge.application_fee.refunds and filtering them for past refunds and then subtracting that from the refund.charge.application_fee.amount_refunded using the many exchange_rates that are returned in the response. The logic to get the exchange rates is really confusing though because depending on which currencies are the buyers and connect accounts they have different edge cases

#

We noticed that when we did our own math to calculate the account balances, when we compared them to stripe they were quite a bit off due to subtle exchange rate differences over time and the hidden exchange_rate fee that built into the exchange_rate

#

I have a feeling there isn't a simple way of getting that information and should just do the math on our side separately and tell accounting we can't track it and just write it off to revenue control

fossil spade
#

I don't really understand what math you're doing on your own? Like overall, we have an exchange rate and an FX fee too, this is all blended in your pricing and you should trust our numbers

eternal flint
#

One example is that when we look at our charge.application_fee.balance_transaction
If we multiply charge.application_fee.amount with charge.application_fee.balance_transaction.exchange_rate The value that results tends to be higher than charge.application_fee.balance_transaction.amount

fossil spade
#

do you have a concrete/specific example?

eternal flint
#

yea sure

fossil spade
#

there's some rounding involved but it should just be off by a cent, not higher and getting details information (exact object ids, exact values/amounts and your math would help

eternal flint
#

looking at fee fee_1Ja4DbRtqiHiNTNRhFh3v1wQ on charge ch_2Ja4DaUmS57H7yUM0njMu96d

fee.amount = 67592
fee.currency = 'gbp'
fee.balance_transaction.exchange_rate = 1.3828
fee.balance_transaction.amount = 91597
fee.balance_transaction.currency = 'usd'
fee.amount * fee.balance_transaction.exchange_rate = 93466.2176
which ends up being a diff of 1869.2176000000036

fossil spade
#

correct, that's the FX fee in that case

eternal flint
#

We can get that on the initial charge easily

fossil spade
#

Out of curiosity, did you consider moving away from application fees and using transfer_data[amount] instead to transfer less to the connected account? It tends to make reconciliation a lot easier

eternal flint
#

but getting that value on refunds is super hard

#

oh interesting?

#

I have not heard of this

fossil spade
#

Basically there are 2 approaches to taking a fee as a platform. One is Application Fees and the other is to just transfer less
App fee: Charge $100, send the funds to the connected account (all $100) and then get back $10 to yours as an application fee
Transfer: Charge $100, but configure to only send $90 to the connected account and then keep $10 yourself

#

it's quite similar though there are less objects to keep track of and less currency conversion(s)

#

one downside is that with the second flow you can't just keep your fee. So you can't charge $100, take $10 as an app fee, but later if you refund the connected account owes the $100 and you keep your $10

#

but you seem to not keep your fee already

eternal flint
#

yea we refund any fees

#

interesting we will give it a shot?

#

when we do a partial refund, will refund_application_fee still work?

fossil spade
#

there's still currency conversion but less, and AppFee<>FeeRefund are really hard to reconcile so you're solving that too

#

well you don't pass refund_application_fee at all anymore, the partial refund is already done from your share of the charge

#

(as long as you also pass reverse_transfer: true which you already do)

eternal flint
#

neat

#

and then if we want to refund only from us we just use reverse_transfer: false

#

this does make it a lot easier

#

and I am guessing the balance transactions will make a bit more sense

fossil spade
#

exactly

#

I mean there are still a ton of BTs

#

like with one refund, it's 6 separate objects, each with their BT, but it's still easier

eternal flint
#

okay... this seems like the long term option

#

but it means we would have to have two different paths for refunding while we support old transactions

#

okay thanks so much

fossil spade
#

Sure thing! I'm sorry this is so hard, we know that exchange rates and refunds make this extremely complex for platforms like you, it's just not a problem with an easy solution especially with fluctuating exchange rates 😦

eternal flint
#

one thing that would be nice was if the exchange_rate variable fee was transparent

#

or have the exchange_rate match the math in the data

#

I have a feeling what is happening is that there is a hidden fee when we convert from buyer to connect and then that gets sent along to the application fee