#milan_connect-payout-transactions

1 messages ยท Page 1 of 1 (latest)

plucky rampartBOT
#

๐Ÿ‘‹ Welcome to your new thread!

โฒ๏ธ We'll be here soon! Typically we respond in a few minutes, but sometimes we might take a bit longer if the server is busy or if you have a particularly tricky question.

โฑ๏ธ We close idle threads, which makes them read-only. Once a thread is closed it won't be reopened, but you can always start a new thread if you have another question.

๐Ÿ”— This thread will always be available, even after it's closed. You can find it again using Discord's search, or you can save this link: https://discord.com/channels/841573134531821608/1423318853844209664

๐Ÿ“ Have more to share? Add more details, code, screenshots, videos, etc. below.

hot sparrow
#

I'm trying to walk that down to py_3ReNIZCksBNNG9HC0D4jJwYy which is the Id we recorded when we first got notification of the ACH return.

subtle reef
#

To clarify, for the PO given you're finding that charge object ID but now trying to get to the payment intent?

hot sparrow
#

Well, that brings up another question. Maybe we should back up as we probably made a mistake earlier in the process.

When the notification is initially received that a return happened on pi_3ReNIZCksBNNG9HC0VqgrIlu (the original payment), what Id should we store that represents the return that we can also find when traversing transactions later during the payout? Right now we are storing the charge.Id, but maybe that's the wrong value to store.

subtle reef
#

Can you clarify what you mean by "return" here? Maybe thats part of the issue

hot sparrow
#

An ACH payment was made (pi_3ReNIZCksBNNG9HC0VqgrIlu) on 6/26. On 6/27, it was returned since the bank account could not be located. However, Stripe still included the original payment in a payout. Later on, Stripe included the return in a separate payout to back out the credit from the initial payment.

#

Or actually, I phrased that wrong. Stripe included both the payment and the return in a single payout.

subtle reef
#

Gotcha, thanks.
So theres no additional payment intent.
Theres the original payment intent, the charge, transfer, connected account payment

and for the return/failure refund there is a connected account payment refund, a transfer reversal and a payment refund
That refund/return flow starts on the connected account with txn_1Rekb0ELWMRmr0fABQCDZtFl

hot sparrow
#

Is a txn a transfer Id?

subtle reef
#

no thats the balance transaction ID on the connected account

hot sparrow
#

Got it. Ok, so when we first learn of the return (prior to the payout), we should store the balance transaction Id?

I am able to walk my way from the payout Id to the Id you provided.

#

When we first get the ChargeFailed event (notifying us of the ACH return), would I get that from chargeFailed.BalanceTransactionId?

subtle reef
#

At the platform level the payment just fails because it never settled, but the connected account transfer needs to be reversed

hot sparrow
#

I'm not sure I understand. I think Stripe already accounted for that in the payout.

#

But what I'm asking is whether the chargeFailed.BalanceTransactionId in this case would have been txn_1Rekb0ELWMRmr0fABQCDZtFl. Is that right?

We bumped up our logging after this event occurred but I'm unable to get the original webhook event on this one.

subtle reef
#

That BT id is from the "payment refund" initiated on the connected account associated with the transfer reversal: pyr_1Rekb0ELWMRmr0fAHllFzzrB

#

(you should end up at the same place starting from the BT on the connected account)

hot sparrow
#

OK. I found the original event from the return. Is it possible to get the full payload of evt_3ReNIZCksBNNG9HC0GWYVeqI?

#

Oh actually, don't worry about it. I see the txn Id there.

#

That's a different one than the txn_1Rekb0ELWMRmr0fABQCDZtFl

#

I'm still confused then. This event comes in and gives me the original PI which we mark as returned. However, we need to store a value from this event that we can eventually walk to the upcoming payout (po_1Rj90RELWMRmr0fA53QzsMvC).

plucky rampartBOT
elder anchor
#

Hello ๐Ÿ‘‹

I'm taking over for my colleague. Give me a minute to review the context

hot sparrow
#

Here's the payload from the return on 6/26

{
"object": {
"id": "py_3ReNIZCksBNNG9HC0D4jJwYy",
"object": "charge",
"amount": 3307225,
"amount_captured": 3307225,
"amount_refunded": 0,
"balance_transaction": "txn_3ReNIZCksBNNG9HC0T5ERsmg",
"captured": true,
"currency": "usd",
"customer": null,
"payment_intent": "pi_3ReNIZCksBNNG9HC0VqgrIlu"
},
"previous_attributes": null
}

How do one of these Ids map to po_1Rj90RELWMRmr0fA53QzsMvC which was the payout on 7/9?

elder anchor
#

How do one of these Ids map to po_1Rj90RELWMRmr0fA53QzsMvC which was the payout on 7/9?

This is a little tricky to get to but here is how you would link these payments to a payout.

#

You can also include the type: charge to restrict it just to payments

#

and you can pass expand=['source',] to expand the Charge objects associated with those balance transactions

hot sparrow
#

We start by getting the balance transactions currently. Then we look at the reporting category and find this is a refund.

#

Then we use the refund service followed by the charge service

#

All of the above works great except for this one case

elder anchor
#

okay well filtering the Balance Transactions list by Payout ID is the only way to map different transactions to a specific payout

hot sparrow
#

That's where we always start

elder anchor
#

Okay. Is there any other aspect in particular that you have more questions about?

hot sparrow
#

Well, this is a reporting category == "Refund". So we are in the balance transactions that belong to a payout. We are in the part where we see this is a "Refund" (but actually it's an ACH return). We still can't walk our way to either of the Ids I included in the 6/26 event payload.

elder anchor
#

Okay what objects are you looking at. A Refund object will have the link to a Charge

hot sparrow
#

We are trying to set the local variable called processorTransactionId which tells us the Id of this return.

if (balanceTransaction.ReportingCategory == "refund")
{
var refundService = new RefundService(stripeClient);
var refund = await refundService.GetAsync(balanceTransaction.SourceId,
requestOptions: new RequestOptions
{
StripeAccount = StripeAccountId
});
var charge = await chargeService.GetAsync(refund.ChargeId,
requestOptions: new RequestOptions
{
StripeAccount = StripeAccountId
});

                    if (charge.SourceTransferId != null)
                    {
                        var transferReversal = await transferReversalService.GetAsync(charge.SourceTransferId,
                                refund.SourceTransferReversalId);

                        processorTransactionId = transferReversal.SourceRefundId;
                    }
                    else
                        processorTransactionId = charge.PaymentIntentId;
                }
plucky rampartBOT
#

๐Ÿง‘โ€๐Ÿ’ป How to format code on Discord

Inline code: wrap in single backticks (`)

This:

The variable `foo` contains the value `bar`.

Will turn into this:

The variable foo contains the value bar.

Code blocks: wrap in three backticks (```)

Also, you can specify the language after the first three backticks to get syntax highlighting.

This:

```javascript
function foo() {
return 'bar';
}
```

Will turn into this:

function foo() {
  return 'bar';
}```

Notes about **code blocks**:
- Specifying the language is optional (e.g., you can omit `javascript` in the example above)
  - If you don't specify the language you won't get syntax highlighting
- When you're inside a code block (after you type \`\`\`) the `Return`/`Enter` key will add a new line instead of sending your message
  - Once you end the code block `Return`/`Enter` works normally again

You can [read more about message formatting on Discord's website.](https://support.discord.com/hc/en-us/articles/210298617)
elder anchor
#

Okay so you get Refund -> Charge -> SourceTransfer -> Refund

What is not working here?

hot sparrow
#

That works great in every other case, but that doesn't get us from po_1Rj90RELWMRmr0fA53QzsMvC to py_3ReNIZCksBNNG9HC0D4jJwYy or txn_3ReNIZCksBNNG9HC0T5ERsmg

#

All other refunds, etc are tracked perfectly in our system. This is an odd case I can't figure out.

elder anchor
#

For py_3ReNIZCksBNNG9HC0D4jJwYy, source_transfer is null

hot sparrow
#

Yep

#

Saw that

elder anchor
#

So is your actual question "why is there no source_transfer for py_3ReNIZCksBNNG9HC0D4jJwYy?"

hot sparrow
#

My question is - How do I get from po_1Rj90RELWMRmr0fA53QzsMvC to py_3ReNIZCksBNNG9HC0D4jJwYy or txn_3ReNIZCksBNNG9HC0T5ERsmg?

elder anchor
#

I just told you

#

Start from po_1Rj90RELWMRmr0fA53QzsMvC to get a list of BTs. Find txn_3ReNIZCksBNNG9HC0T5ERsmg in that list. If it isn't in that list, then you're looking at the wrong payout

#

You won't get a source_transfer value because that is null

hot sparrow
#

Ok. I'll look again at this and get back to you.

elder anchor
#

Also, another confounding point, py_3ReNIZCksBNNG9HC0D4jJwYy is the Charge object on the Platform account. You would only expect a source_transfer for a Payment on a Connected Account (this property identifies the transfer from the Platform that resulted in this payment).

#

Just based on the code you shared, it looks like you are mostly iterating over payments and payouts on the Connected Account

hot sparrow
#

I'll dive back in in 15. Just got pulled into a call.

#

Here are the balance transaction Ids that come back when we ask for balance transactions under that payout Id. Balance transaction Id txn_1Rekb0ELWMRmr0fABQCDZtFl is the one that represents the ACH return.

I still don't see how to walk that to py_3ReNIZCksBNNG9HC0D4jJwYy or txn_3ReNIZCksBNNG9HC0T5ERsmg

elder anchor
#

Sorry there are a lot of assumptions going on here and it's not clear to me why you think you should be able to get there.

Stepping through the objects we have.

  • txn_1Rekb0ELWMRmr0fABQCDZtFl This it the BT created by the Payment Refund -pyr_1Rekb0ELWMRmr0fAHllFzzrB

What happens when you attempt to retrieve that ID from the Refund API?

hot sparrow
#

Starting with the assumption....we received a webhook on 6/26 saying the original PI returned. We log the charge.Id (or let me know if something else should be logged). Then on 7/9 we have the payout. When the payout occurs, our system needs to know which return was in the payout. However, the charge.Id we stored initially from 6/26 isn't found in that payout.

elder anchor
#

Did the Charge and refund happen at different enough times that they might have occurred across different payouts?

hot sparrow
#

Going back to the question about the txn_ you provided...

I can go from "txn_1Rekb0ELWMRmr0fABQCDZtFl" to pyr_1Rekb0ELWMRmr0fAHllFzzrB to py_1ReOxJELWMRmr0fAxWHewD7Z to trr_1Rekb0CksBNNG9HCUuqbq1CQ

#

The original payment was 6/26, the return was a day later, and the payout that included both was on 7/9

elder anchor
#

Okay but I think something got confused here.

  • po_1Rj90RELWMRmr0fA53QzsMvC occurred on account acct_1RdMCcELWMRmr0fA
  • py_3ReNIZCksBNNG9HC0D4jJwYy occurred on account acct_1R5ENQCksBNNG9HC

So you would not expect that payment to be related directly to that payout.

But as for following the trail:

  • trr_1Rekb0CksBNNG9HCUuqbq1CQ gets you to the original transfer tr_3ReNIZCksBNNG9HC0acdwME5 which points you back to the source transaction py_3ReNIZCksBNNG9HC0D4jJwYy
hot sparrow
#

I see. So the payment was to the connected account and the py was against the platform?

elder anchor
#

Correct

hot sparrow
#

That explains why the Id can't be walked. Let me think through how to work this out. I think we'll have to rework (in a very minor way) how we handle the initial webhook that notifies us of the return so we can capture it in the reporting when the payout occurs.

#

I've taken enough of your time today. Thank you for the help.

elder anchor
#

Happy to shed what ๐Ÿ’ก I can

#

You can eventually get there but it's definitely more steps!

hot sparrow
#

Have a great day

elder anchor
#

You too ๐Ÿ‘‹