#cyral_refund-subscription-attribution
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/1425979767076687911
๐ Have more to share? Add more details, code, screenshots, videos, etc. below.
discord wont let me type that many characters so attaching the rest of my question as a txt file
Better illustration:
Here is the charge in workbench, it has the invoice_id which is exactly what I want.
Even if I run stripe charges retrieve ch_3SCp2mBHmn7qM5jZ0YCO0qqZ in the Stripe Shell in my browser, I get the invoice ID back
Running the exact same command in the Stripe CLI on my local machine or with curl returns a response without the invoice field
discord is processing the image forever for some reason, this is what I see in the workbench: https://i.imgur.com/J7xNMwv.png
Hi there,
the different results where an invoice is present while it isn't in your Srtipe CLI, curl, local machine has most likely to do with API versions. We had beginning of the year an API release with a lot of breaking changes. One of them being that we removed the invoice property from charges, and payment intents.
Here you can find more information about the API verion change: https://docs.stripe.com/changelog/basil/2025-03-31/add-support-for-multiple-partial-payments-on-invoices
Basically we have removed the invoice property from the charge, and introduced a new object called Invoice Payments. So when using API version 2025-03-31.basil and later, you can use the charge ID (or Payment Intent ID) on List all all Invoice Payments https://docs.stripe.com/api/invoice-payment/list. The response will contain the invoice ID.
Ok that makes sense, thanks
I just tried with stripe charges retrieve ch_3SCp2mBHmn7qM5jZ0YCO0qqZ --stripe-version 2024-06-20 | grep "invoice" and sure enough it includes the invoice with the older version
Yeah that makes sense
I'm looking into this still.. not immediately clear how to relate refunds back to the invoice/subscription though
I see there is a invoice_payment.paid event which lets you get the invoice actually, great. easier than making another call to expand the payments of the invoice and try to find the right one
wish there was an invoice_payment.refunded but I guess refunds are a bit disconnected from invoices
When a refund is issued/succeeded you should receive a charge.refund.updated event. With that you receive the refund ID and the charge ID that you can use to List the Invoice Payments that also contain the Invoice ID. Does that make sense?
Hmm so the invoices endpoint (expanding payments) contains something like:
"payments": {
"object": "list",
"data": [
{
"id": "inpay_1SGRweBHmn7qM5jZGhG3u8yL",
"object": "invoice_payment",
"amount_paid": 13860,
"amount_requested": 13860,
"created": 1760047457,
"currency": "usd",
"invoice": "in_1SGRwbBHmn7qM5jZ2sQnfn8J",
"is_default": true,
"livemode": false,
"payment": {
"payment_intent": "pi_3SGRwcBHmn7qM5jZ1cO4l3A5",
"type": "payment_intent"
},
"status": "paid",
"status_transitions": {
"canceled_at": null,
"paid_at": 1760047458
}
}
],
"has_more": false,
"url": "/v1/invoices/payments"
},
Which exposes the inpay_ ID, but not the charge
But all of the refunded events expose the charge, not the inpay_
"object": {
"id":
"re_3SGSMvBHmn7qM5jZ1IZKWEeV"
,
"object":
"refund",
"amount":
2000,
"balance_transaction":
"txn_3SGSMvBHmn7qM5jZ1IERvQzM"
,
"charge":
"ch_3SGSMvBHmn7qM5jZ1PB324YS"
,
"created":
1760052214
,
"currency":
"usd",
"destination_details": {
"card": {
"reference":
"7300502887278129",
"reference_status":
"available",
"reference_type":
"acquirer_reference_number",
"type":
"refund",
},
"type":
"card",
},
"metadata": {},
"payment_intent":
"pi_3SGSMvBHmn7qM5jZ1lI009Zp"
,
"reason":
null,
"receipt_number":
null,
"source_transfer_reversal":
null,
"status":
"succeeded",
"transfer_reversal": null
}
So for the invoice payment we are good, but for the refund we only have a charge and no way to relate that back to an invoice payment or the invoice. The payment_intent is different between the refund and charge so no luck there either unfortunately
Only when you use the charge ID from the event to call the List all invoice payment API
And you can expand the invoice on the Invoice Payment API to have access to the Invoice data.
Hmm can you explain that in more detail?
I am not seeing how to go from the charge ID to getting the invoice payment
Yeah no problem.
Let start with the event. When you receive the charge.refund.updated event, the event body contains the Charge ID.
Example https://dashboard.stripe.com/test/events/evt_3SCp2mBHmn7qM5jZ0PILZLrU
With that Charge ID, you call the List all invoice payments API
stripe invoice_payments list \
--expand=data.invoice
-d "payment[type]='charge'
-d "payment[charge]={here comes the charge ID from the event}
(I hope this works. Havent used the stripe cli for this in a while)
hmm okay I need to figure out the formatting for the [type] [charge] thing, its not liking that
looking at the docs I am not seeing "charge" as an option here though
just payment_intent
I gotta go and will look into this later but let me know if you have any other ideas
Appreciate the help
Oh yeah. I wonder why it showed on my screen differently. But the charge.refund.updated event also gives you the Payment Intent ID. So it should still work that way
The payment intent for refunds appears to be a new one not related to the original charge
If Iโm looking at it right
Wait I had so many events pulled up I got confused sorry
invoice_payment.paid contains the invoice ID for finding the subscription, and the payment intent. And the refund event contains the same payment intent so we can reference both of those to find the original subscription when getting a refund event
And they are actually the same so it works, I should have realized that way earlier
Thanks again
I'm glad we could figure it out ๐
cyral_refund-subscription-attribution
Is there anything I can help with? Otherwise I'll close the thread if that is ok with you.
That is all, have a good evening