如何在Stripe的退款Webhook事件中获取产品信息
Question
I've configured a webhook in Stripe's Developer tab and it's running normally. However, when handling the charge.refunded event, I noticed the response data doesn't include product-related information. Here's an example of the response data:
{ "id": "evt_1IsnzmJROm6awlUaq3rcFwr8", "object": "event", "api_version": "2019-11-05", "created": 1621425325, "data": { "object": { "id": "ch_1IsYZMJROm6awlUaER10QD3d", "object": "charge", "amount": 1750, "amount_captured": 1750, "amount_refunded": 1750, "application": null, "application_fee": null, "application_fee_amount": null, "balance_transaction": "txn_1IsYZMJROm6awlUassUs5W6y", "billing_details": { "address": { "city": null, "country": null, "line1": null, "line2": null, "postal_code": "71854", "state": null }, "email": "test@email-receiver.com", "name": "Test", "phone": null }, "calculated_statement_descriptor": "TEST", "captured": true, "created": 1621366028, "currency": "usd", "customer": "cus_JVZiIhKIpCH9qh", "description": "Bla bla", "destination": null, "dispute": null, "disputed": false, "failure_code": null, "failure_message": null, "fraud_details": { }, "invoice": null, "livemode": false, "metadata": { }, "on_behalf_of": null, "order": null, "outcome": { "network_status": "approved_by_network", "reason": null, "risk_level": "normal", "risk_score": 14, "seller_message": "Payment complete.", "type": "authorized" }, "paid": true, "payment_intent": "pi_1IsYZMJROm6awlUaVsZOefRh", "payment_method": "pm_1IsYZJJROm6awlUasaBRHxzq", "payment_method_details": { "card": { "brand": "visa", "checks": { "address_line1_check": null, "address_postal_code_check": "pass", "cvc_check": "pass" }, "country": "US", "exp_month": 1, "exp_year": 2023, "fingerprint": "QA94mAZK6R1nyB79", "funding": "credit", "installments": null, "last4": "4242", "network": "visa", "three_d_secure": null, "wallet": null }, "type": "card" }, "receipt_email": "test@email-receiver.com", "receipt_number": null, "receipt_url": "https://pay.stripe.com/receipts/acct_1Fg5jCJROm6awlas/ch_1IsYZMJROm6awlUaOI10QD3d/rcpt_JVZijS8cso7ks67hLGI705AvCCDOC8J", "refunded": true, "refunds": { "object": "list", "data": [ { "id": "re_1IsnzlJROm6awlUa5vVlKl1T", "object": "refund", "amount": 1750, "balance_transaction": "txn_1IsnzlJROm6awlUaFZvKoHcE", "charge": "ch_1IsYZMJROm6awlUaER10QD3d", "created": 1621425325, "currency": "usd", "metadata": { }, "payment_intent": "pi_1IsYZMJROm6awlUaVsZOefRh", "reason": "requested_by_customer", "receipt_number": null, "source_transfer_reversal": null, "status": "succeeded", "transfer_reversal": null } ], "has_more": false, "total_count": 1, "url": "/v1/charges/ch_1IsYZMJRlT6awlUaER10QD3d/refunds" }, "review": null, "shipping": null, "source": null, "source_transfer": null, "statement_descriptor": null, "statement_descriptor_suffix": null, "status": "succeeded", "transfer_data": null, "transfer_group": null }, "previous_attributes": { "amount_refunded": 0, "refunded": false, "refunds": { "data": [ ], "total_count": 0 } } }, "livemode": false, "pending_webhooks": 1, "request": { "id": "req_Ga1TLn7TsaGIBg", "idempotency_key": null }, "type": "charge.refunded" }
Answer
Hey there, let's break down why you're not seeing product details in the charge.refunded event and how to fix it.
First, the core reason: Stripe's charge object (which is what you get in this webhook event) doesn't include product data by default. Charges represent the raw payment transaction—product information lives in related objects like invoices, or needs to be explicitly stored when creating the charge.
Here are your solutions based on how you initially created the charge:
1. If your charge was created from an Invoice
If the charge had an associated invoice (your example shows invoice: null, so this might not apply here), you can use the invoice ID from the charge to fetch full product details via Stripe's API. Just expand the line_items when retrieving the invoice:
import stripe stripe.api_key = "your_secret_api_key" # Extract invoice ID from the webhook event's charge object invoice_id = event['data']['object']['invoice'] invoice = stripe.Invoice.retrieve( invoice_id, expand=['line_items'] ) # Loop through line items to get product info for line_item in invoice.line_items.data: product = line_item.price.product print(f"Product ID: {product.id}, Name: {product.name}, Description: {product.description}")
2. If you created the charge directly (no invoice)
Since your example has invoice: null, this is likely your scenario. The simplest fix is to store product details in the charge's metadata when you first create it. This way, the metadata will be included in the charge.refunded event.
When creating the charge:
charge = stripe.Charge.create( amount=1750, currency='usd', customer='cus_JVZiIhKIpCH9qh', payment_method='pm_1IsYZJJROm6awlUasaBRHxzq', metadata={ 'product_id': 'prod_your_product_id', 'product_name': 'Your Product Name', 'product_description': 'Brief product description' } )
Then, in your webhook handler, you can access this metadata directly from the charge object:
# Extract charge from webhook event data charge = event['data']['object'] product_id = charge['metadata']['product_id'] product_name = charge['metadata']['product_name']
3. Alternative: Fetch related objects on webhook receipt
If you didn't store metadata initially, you can trace back from the charge's payment_intent ID to find product details. Retrieve the Payment Intent and expand any related line items or checkout sessions (if applicable). Note this is less straightforward if you didn't structure the payment with these links upfront.
To sum it up: Stripe doesn't embed product data in charge objects automatically, so you either need to store it in metadata at charge creation time or fetch related objects like invoices via the API when handling the refund event.
内容的提问来源于stack exchange,提问作者Ted

