The charge.refunded webhook fails, response structure differs from the expected one

Created on 11 December 2023, 12 months ago
Updated 6 February 2024, 10 months ago

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

πŸ› Bug report
Status

Fixed

Version

1.0

Component

Code

Created by

πŸ‡¦πŸ‡ΉAustria agoradesign

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

Production build 0.71.5 2024