Skip to content

Noticing blocked charges in stripe

We recently had our first blocked charge at The Food Corridor. We found out because Stripe kindly sent us a notice. It was blocked because Stripe’s algorithms determined that it was possibly fraudulent.

This is a pretty rare event (hopefully) but we still want to be prepared to deal with it. In the evolution of admin features, this is definitely at level 2, because it involves customers and money.

Luckily stripe_event has our backs and we can easily catch a webhook event and send an email if this happens. We just need to figure out what the event looks like.

I searched the stripe API but found nothing about blocked charges. I sent an email into Stripe’s helpful support system, and shortly thereafter someone had tracked down the event so that we could set up that webhook. Here’s the event (anonymized):

{
  "object": {
    "id": "ch_xxx",
    "object": "charge",
    "amount": 100,
    "amount_refunded": 0,
    "application": null,
    "application_fee": null,
    "balance_transaction": null,
    "captured": false,
    "created": 111,
    "currency": "usd",
    "customer": "cus_xxx",
    "description": "desc",
    "destination": "acct_xxx",
    "dispute": null,
    "failure_code": "card_declined",
    "failure_message": "Your card was declined.",
    "fraud_details": {
      "stripe_report": "fraudulent"
    },
    "invoice": null,
    "livemode": true,
    "on_behalf_of": "acct_xxx",
    "order": null,
    "outcome": {
      "network_status": "not_sent_to_network",
      "reason": "highest_risk_level",
      "risk_level": "highest",
      "rule": {
        "id": "block_if_high_risk__enabled",
        "action": "block",
        "predicate": ":risk_level: = 'highest'"
      },
      "seller_message": "Stripe blocked this charge as too risky.",
      "type": "blocked"
    },
    "paid": false,
    "receipt_email": null,
    "receipt_number": null,
    "refunded": false,
    "refunds": {
      "object": "list",
      "data": [
      ],
      "has_more": false,
      "total_count": 0,
      "url": "/v1/charges/ch_xxx/refunds"
    },
    "review": null,
    "shipping": null,
    "source": {
      ...
    },
    "source_transfer": null,
    "statement_descriptor": "descriptor",
    "status": "failed",
    "transfer_group": "group_ch_xxx"
  },
  "previous_attributes": null
}

The key is to look at the outcome type value, and handle blocked failed charges differently than normal failed charges (perhaps by contacting the customer).