#MarcusStripe - tracking credit notes and refunds

1 messages ยท Page 1 of 1 (latest)

hallow wadiBOT
tall jay
#

That link is working for me, can you summarize the state and any new question you have here please?

worn hound
#

So I have read about transfer groups.
It seems it could help, but only under very special conditions:

  • The transfer group must be build at the same time, so the transfer is made immediately.
  • That means, the transfer is made IMMEDIATELY after the subscription charge was made.
  • That again means the customer would get multiple small transfers, and for each transfer (that could be 300x 10 Euros/month!), I would have to create a commission credit invoice on my end. So it doesn't seem practical.

I think it is still the better approach to collect the refunds / list of partial refunds and to substract them on my end,
before I pay the commission, if any remains.

So I could have a status PENDING, PAID, REFUND and PARTIAL_REFUND, and for PARTIAL_REFUND I would show a list of all partial refunds below
the commission amount, and then pay out the remaining amount, if there is any.

So for this again, I am asking - what is the best approach for this:
A) Listening on the credit_note.created event, and adding the amount to a list of refunds to the related commission.
B) Retrieving, once per month or maybe once per day, for each of my affiliated-generated customers,
PaymentIntent.list - the charges and their related refunds -
this would give me the charge.getRefunded() boolean flag to know if the entire charge was refunded -
otherwise I would just add a negative partial amount, and later another one, if there are more over time.

tall jay
#

Taking a step back, I agree with this:

I think it is still the better approach to collect the refunds / list of partial refunds and to substract them on my end, before I pay the commission, if any remains.

Generally it is better to aggregate in your own systems and take fewer actions

#

I would say that it really depends on your requirements and the complexity of the integration for your systems

#

I would err toward using webhooks to get notifications pushed to me and track those, but if both achieve your goals, then its really up to you.

worn hound
#

ok, let's try it more concrete from my end:

  1. credit_note.created event - is that event the right one to use for my needs?
  2. iterating the payment intents for me has the super nice feature of seeing - at once - if the refund is a total refund - I could just directly switch the commission from PENDING to CANCELED / REFUNDED.
    But it has the disadvantage of being batched and not just in time.
    But I fear I could also loose some kind of refunds for the redit_note.created -
    e.g. if several refunds are done for the same charge.. will this still appear as a new redit_note.created event?
#

there is "charge.refund.updated" after all.. and currently I am not listening to it.. so maybe it could happen that ther is some kind of update I am (currently) missing?

#

--
And.. it would be nice to link refunds to their related charges also

#

is there a way to get this info from the credit_note.created event?

tall jay
#

If your needs are mapped to tracking creation of credit notes, then that sounds like the right event (I can't know all of your business requirements, really)

worn hound
#

well, as far as I know.. refunds can be created in different ways in the stripe UI.

#

I do create then with a credit note, as this will trigger all the info needed for the credit invoice also.. if I remember correctly

#

can refunds also be executed by stripe or the bank of the customer or so???

#

--

#

And.. it would be nice to link refunds to their related charges also
is there a way to get this info from the credit_note.created event?

tall jay
#

So you could do eg expand=['refund.charge'] when examining a credit note

#

Note that this is not in the webhook event itself (no expansion in webhooks), you'd need to retrieve it

#

MarcusStripe - tracking credit notes and refunds

worn hound
#

expansion.. but an expansion is hardly available on an event

#

you mean by calling the refund api then?

#

"you'd need to retrieve it".. so you are basically already confirming just that.
So I could -
for every charge, store the related charge ID on my system, if related to a commission.
And then, for every credit_note, retreive the refund id:
stripeCreditNote.getRefund()
and then retrieve the refund.. and I assume I would not even need to expand it.. as the charge id would be present already

#

and then I could link the refund / the list of refunds to each related comission, yes?

tall jay
#

If that's driven by the credit note, then yes

#

You can use expansion to get both at once (or yes, retrieve that refund object and expand the charge)

#

Many different variations, you'll have ot see what fits best

#

But yes, you cannot get that directly in the webhook data, there's no expansion there (its not supported)

worn hound
#

well the charge I would have gotten earlier anyway.. is my point ๐Ÿ™‚

#

I would just need the ID for reference and then linking the two together

tall jay
#

Sure, then just the refund object will do ๐Ÿ‘

#

If you also get refund objects, you may alreayd have this, with clever event processing

#

ie, pair together the credit note event data wiht the refund event data

#

just be aware they may not always arrive in the same order

worn hound
#

Wait.. thinking more..
So actually.. since I need the REFUND for the charge id...
Why not instead directly listening to the charge.refund event? Woulnt it most probably give me the charge ID directly?

#

Here I would not need any invoice related infos.. just the info of the amount of the refund and the id of the related charge

tall jay
#

that would be in the refund info then, yes

worn hound
#

Hm... I am rethinking now my entire process.
To know about commissions to be paid out.... I am currently listening on invoice_paid.

Wouldnt it be best to listen for both - a paid charge and a refunded charge to listen directly on the charge events?

I checked, here is the entire list:
charge.captured
charge.expired
charge.failed
charge.pending
charge.refunded
charge.succeeded
charge.updated
charge.dispute.closed
charge.dispute.created
charge.dispute.funds_reinstated
charge.dispute.funds_withdrawn
charge.dispute.updated
charge.refund.updated

From these, the relevant ones seem to be:
charge.succeeded
Occurs whenever a charge is successful.

charge.refunded
Occurs whenever a charge is refunded, including partial refunds.

charge.dispute.funds_reinstated
Occurs when funds are reinstated to your account after a dispute is closed.

charge.dispute.funds_withdrawn
Occurs when funds are removed from your account due to a dispute.

  1. So for every monthly subscription fee paid, I would get a charge.succeeded event, right?
  2. Will the charge.succeeded show me the amount charged? Including tax od excluding?

And for every refund, a charge.refunded event.
3) For the charge.refunded event, would there be the id of the original charge event, I assume?
4) Will it also show the (partial) amount charged? Including tax or excluding?
5)charge.dispute - Disputes are always full, never partial, right?
6) So I could potentially put on hold a commission when there is a "charge.dispute.created" I guess.
7) charge.dispute.funds_withdrawn shows me a "full refund" based on a dispute of a customer, right?
8) So I would then put the "ON_HOLD" charge on CANCELED in this case.
9) I would then put the "ON_HOLD" charge back on PENDINING in this case.

All correct? Any important event I forgot?

tall jay
#

1/ assuming there was a payment yes (maybe there isn't due to discount or credit)
2/ yes
3/ yes
4/ it's the final charge amount, what the customer paid
5/ No, they can be partial: https://stripe.com/docs/disputes/how-disputes-work#disputed-amount

Dispute flows are another beast, dpends on your business setup and customer relationship etc. I'd suggest testing extensively (both the dispute and non-dispute bits) and making sure you have everything you need and cover your defined busines sprocesses

Learn about the high level lifecycle of payment card disputes.

#

We can't tell you if you've missed anything, you own your processes

#

Please test extensively, if you find something is not working as you expect or you need help finding a particular bit of info, feel free to pop by again

#

But really the answer now is for you to test a lot with our various test cards for failures, disputes etc

worn hound
#

Ok, so since disputes should be rare (~ 0.3% I am told), given that we have a 100% refund policy in case of any issues for 30 days...

I will for now just keep ignoring them.

As for the charges and refunds.. based on your feedback, I came up with this very simple logic:

public class ChargeHandler implements StripeEventHandler {

public void handle(StripeObject stripeObject) {
    Charge charge = (Charge) stripeObject;
    String customerId = charge.getCustomer();
    if (isNotAquiredByAffiliatePartner(customerId)) {
        return;
    }

    if (affiliateService.isDuplicateCharge(charge.getId())) {
        return;
    }

    Affiliate affiliate = findAffiliatPartner(customerId);
    assignChargeToAffiliatePartner(charge.getAmountCaptured(), affiliate);
}

public StripeEventType type() {
    return CHARGE;
}

}

AND for refunds:

public class RefundHandler implements StripeEventHandler {

public void handle(StripeObject stripeObject) {
    Charge charge = (Charge) stripeObject;

    String customerId = charge.getCustomer();
    if (isNotAquiredByAffiliatePartner(customerId)) {
        return;
    }

    for (Refund refund : charge.getRefunds().getData()) {
        if (isDuplicateRefund(refund.getId())) {
            continue;
        }

        attachdRefundToCharge(refund.getAmount(), charge.getId());
    }
}

public StripeEventType type() {
    return REFUND;
}

}

#

--

#

as much as you can tell from my java code... does this make sense ?

spice elm
#

๐Ÿ‘‹ I'm hopping in since roadrunner has to head out - give me a few mi nutes to catch up

spice elm
#

overall this seems to make sense, but it's hard to tell since I don't know what any of the functions you call (like assignChargeToAffiliatePartner and attachdRefundToCharge ) are doing

worn hound
#

yeah well I named them in a way to tell as good as possible to you what I have in mind ๐Ÿ™‚

#

they are not implemented yet

#

So my thinking is - for the charge - add a commission value (30% of the charge in the average case)

#

and store the charge id with it

#

and IF a refund charge event comes - iterate over all no existing refunds of the event

#

and add any new refunds that were not yet existing

#

and then I would have a parent comission amount with an optional list of refunds on my end

#

and when I pay this out after 30 days, I would substract the refunds from the comissions and pay out the resulting amount

#

that should be good in 99.5% of the cases, i guess

#

--
the charge_updated event.. I would not need to care since I already listen on the charge_refund event, right?