After activating commerce_stripe in a production environment, I did a test order, and after that of course refunded the payment (via Drupal admin UI). After realizing that I've configured the wrong notification endpoint in Stripe - I took the README too literally and entered /payment/notify/stripe_payment_element, although my payment gateway config is just named "stripe" - I adjusted the configuration and tried to resend the failed API callbacks in Stripe admin UI.
First, I resent the previously failed payment_intent.succeeded notification - it now succeeded. Next, I tried to resend the "charge.refunded" as well, but ended in a 500 HTTP error in Drupal:
TypeError: reset(): Argument #1 ($array) must be of type array, null given in reset() (Zeile 445 in /web/modules/contrib/commerce_stripe/src/Plugin/Commerce/PaymentGateway/StripePaymentElement.php)
I took a look at the code, line 445 is:
$latest_refund = reset($object['refunds']['data']);
Whereas $object is set to this:
$object = $request_body['data']['object'];
Thank god, Stripe has a very detailed logging exposed in the admin interface, so we can look at the requests they are sending. According to this, the $object does not contain a key "refunds", nor "data". instead, it looks like this (starting from the outer "data" key, replaced some sensitive infos with "XXXXX"):
"data": {
"object": {
"id": "XXXXX",
"object": "charge",
"amount": 21000,
"amount_captured": 21000,
"amount_refunded": 21000,
"application": null,
"application_fee": null,
"application_fee_amount": null,
"balance_transaction": "XXXXX",
"billing_details": {
"address": {
"city": null,
"country": "XXXXX",
"line1": null,
"line2": null,
"postal_code": "XXXXX",
"state": null
},
"email": null,
"name": null,
"phone": null
},
"calculated_statement_descriptor": "XXXXX",
"captured": true,
"created": 1702284253,
"currency": "eur",
"customer": null,
"description": null,
"destination": null,
"dispute": null,
"disputed": false,
"failure_balance_transaction": null,
"failure_code": null,
"failure_message": null,
"fraud_details": {
},
"invoice": null,
"livemode": true,
"metadata": {
"order_id": "1",
"store_id": "1"
},
"on_behalf_of": null,
"order": null,
"outcome": {
"network_status": "approved_by_network",
"reason": null,
"risk_level": "normal",
"seller_message": "Payment complete.",
"type": "authorized"
},
"paid": true,
"payment_intent": "XXXXX",
"payment_method": "XXXXX",
"payment_method_details": {
"card": {
"amount_authorized": 21000,
"brand": "mastercard",
"checks": {
"address_line1_check": null,
"address_postal_code_check": "unavailable",
"cvc_check": "pass"
},
"country": "AT",
"exp_month": 5,
"exp_year": 2024,
"extended_authorization": {
"status": "disabled"
},
"fingerprint": "XXXXX",
"funding": "credit",
"incremental_authorization": {
"status": "unavailable"
},
"installments": null,
"last4": "XXXXX",
"mandate": null,
"multicapture": {
"status": "unavailable"
},
"network": "mastercard",
"network_token": {
"used": false
},
"overcapture": {
"maximum_amount_capturable": 21000,
"status": "unavailable"
},
"three_d_secure": {
"authentication_flow": "frictionless",
"electronic_commerce_indicator": "02",
"exemption_indicator": null,
"result": "authenticated",
"result_reason": null,
"transaction_id": "XXXXX",
"version": "2.2.0"
},
"wallet": null
},
"type": "card"
},
"receipt_email": null,
"receipt_number": null,
"receipt_url": "https://pay.stripe.com/receipts/payment/XXXXX",
"refunded": true,
"review": null,
"shipping": {
"address": {
"city": "XXXXX",
"country": "XXXXX",
"line1": "XXXXX",
"line2": "",
"postal_code": "XXXXX",
"state": null
},
"carrier": null,
"name": "XXXXX",
"phone": null,
"tracking_number": 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,
"receipt_url": "https://pay.stripe.com/receipts/payment/XXXXX",
"refunded": false
}
},
I don't know, whether this happens because it's a subsequent trial of a failed notification, but this wouldn't make any sense imho