#milan_connect-payout-transactions
1 messages ยท Page 1 of 1 (latest)
๐ 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.
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.
To clarify, for the PO given you're finding that charge object ID but now trying to get to the payment intent?
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.
Can you clarify what you mean by "return" here? Maybe thats part of the issue
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.
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
Is a txn a transfer Id?
no thats the balance transaction ID on the connected account
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?
At the platform level the payment just fails because it never settled, but the connected account transfer needs to be reversed
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.
That BT id is from the "payment refund" initiated on the connected account associated with the transfer reversal: pyr_1Rekb0ELWMRmr0fAHllFzzrB
When the payment failed, you could trace down from that charge via the transfer and transfer reversal: https://docs.stripe.com/api/transfers/object#transfer_object-reversals
Complete reference documentation for the Stripe API. Includes code snippets and examples for our Python, Java, PHP, Node.js, Go, Ruby, and .NET libraries.
(you should end up at the same place starting from the BT on the connected account)
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).
Hello ๐
I'm taking over for my colleague. Give me a minute to review the context
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?
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 get a list of all Balance Transactions by the Payout ID po_1Rj90RELWMRmr0fA53QzsMvC using this API: https://docs.stripe.com/api/balance_transactions/list
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
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
okay well filtering the Balance Transactions list by Payout ID is the only way to map different transactions to a specific payout
That's where we always start
Okay. Is there any other aspect in particular that you have more questions about?
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.
Okay what objects are you looking at. A Refund object will have the link to a Charge
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;
}
๐งโ๐ป 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
foocontains the valuebar.
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)
Okay so you get Refund -> Charge -> SourceTransfer -> Refund
What is not working here?
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.
For py_3ReNIZCksBNNG9HC0D4jJwYy, source_transfer is null
So is your actual question "why is there no source_transfer for py_3ReNIZCksBNNG9HC0D4jJwYy?"
My question is - How do I get from po_1Rj90RELWMRmr0fA53QzsMvC to py_3ReNIZCksBNNG9HC0D4jJwYy or txn_3ReNIZCksBNNG9HC0T5ERsmg?
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
Ok. I'll look again at this and get back to you.
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
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
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_1Rekb0ELWMRmr0fABQCDZtFlThis it the BT created by the Payment Refund -pyr_1Rekb0ELWMRmr0fAHllFzzrB
What happens when you attempt to retrieve that ID from the Refund API?
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.
Did the Charge and refund happen at different enough times that they might have occurred across different payouts?
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
Okay but I think something got confused here.
po_1Rj90RELWMRmr0fA53QzsMvCoccurred on accountacct_1RdMCcELWMRmr0fApy_3ReNIZCksBNNG9HC0D4jJwYyoccurred on accountacct_1R5ENQCksBNNG9HC
So you would not expect that payment to be related directly to that payout.
But as for following the trail:
trr_1Rekb0CksBNNG9HCUuqbq1CQgets you to the original transfertr_3ReNIZCksBNNG9HC0acdwME5which points you back to the source transactionpy_3ReNIZCksBNNG9HC0D4jJwYy
I see. So the payment was to the connected account and the py was against the platform?
Correct
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.
Happy to shed what ๐ก I can
You can eventually get there but it's definitely more steps!
Have a great day
You too ๐