Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

valentinmorice's avatar

PHPStan and Stripe Webhooks

I've just started implementing static analysis with PHPStan, and I'm wondering how to type-hint a JSON webhook body received from Stripe and passed on to a Laravel job:

class PaymentWebhook implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected array $stripe_event;

    public function __construct(array $stripe_event)
    {
        $this->stripe_event = $stripe_event;
    }

    public function handle(): void
    {
       // Do something here...
    }
}

Here is an example of the JSON received from Stripe:

{
    "id": "evt_3OEgITAO5frM15J30L5cau2f",
    "object": "event",
    "api_version": "2022-08-01",
    "created": 1700520629,
    "data": {
        "object": {
            "id": "pi_3OEgITAO5frM15J30jfroHwp",
            "object": "payment_intent",
            "amount": 2000,
            "amount_capturable": 0,
            "amount_details": {
                "tip": {
                }
            },
            "amount_received": 0,
            "application": null,
            "application_fee_amount": null,
            "automatic_payment_methods": {
                "allow_redirects": "always",
                "enabled": true
            },
            "canceled_at": null,
            "cancellation_reason": null,
            "capture_method": "automatic",
            "charges": {
                "object": "list",
                "data": [
                ],
                "has_more": false,
                "total_count": 0,
                "url": "some_url"
            },
            "client_secret": "some_secret",
            "confirmation_method": "automatic",
            "created": 1700520629,
            "currency": "eur",
            "customer": "customer123",
            "description": null,
            "invoice": null,
            "last_payment_error": null,
            "latest_charge": null,
            "livemode": false,
            "metadata": {
                "payment_method": "1"
            },
            "next_action": null,
            "on_behalf_of": null,
            "payment_method": null,
            "payment_method_configuration_details": {
                "id": "pmc_1LsR7YAO5frM15J3FZloygAl",
                "parent": null
            },
            "payment_method_options": {
                "bancontact": {
                    "preferred_language": "en"
                },
                "card": {
                    "installments": null,
                    "mandate_options": null,
                    "network": null,
                    "request_three_d_secure": "automatic"
                },
                "eps": {
                },
                "giropay": {
                },
                "ideal": {
                },
                "link": {
                    "persistent_token": null
                }
            },
            "payment_method_types": [
                "card",
                "bancontact",
                "eps",
                "giropay",
                "ideal",
                "link"
            ],
            "processing": null,
            "receipt_email": null,
            "review": null,
            "setup_future_usage": null,
            "shipping": null,
            "source": null,
            "statement_descriptor": null,
            "statement_descriptor_suffix": null,
            "status": "succeeded",
            "transfer_data": null,
            "transfer_group": null
        }
    },
    "livemode": false,
    "pending_webhooks": 2,
    "request": {
        "id": "req_Lcu8hqvAq0s14a",
        "idempotency_key": "a8c586e0-0082-424f-9ed0-54624a469763"
    },
    "type": "payment_intent.created"
}
0 likes
1 reply
LaryAI's avatar
LaryAI
AI
Best Answer
Level 58

To handle the JSON webhook body received from Stripe and ensure that PHPStan understands the structure of the data, you can create a DTO (Data Transfer Object) that represents the structure of the Stripe event. This DTO can then be used to type-hint the $stripe_event property in your PaymentWebhook class.

Here's how you can create a DTO and use it in your Laravel job:

  1. Create a DTO class that represents the structure of the Stripe event:
class StripeEventDTO
{
    public string $id;
    public string $object;
    public string $api_version;
    public int $created;
    public array $data;
    public bool $livemode;
    public int $pending_webhooks;
    public array $request;
    public string $type;

    // Add more properties as needed, based on the Stripe event structure

    public function __construct(array $stripeEventData)
    {
        // Map the array data to the properties of this DTO
        // You can use a library like spatie/data-transfer-object for automatic mapping
        // or manually assign the values as shown below:

        $this->id = $stripeEventData['id'];
        $this->object = $stripeEventData['object'];
        $this->api_version = $stripeEventData['api_version'];
        $this->created = $stripeEventData['created'];
        $this->data = $stripeEventData['data'];
        $this->livemode = $stripeEventData['livemode'];
        $this->pending_webhooks = $stripeEventData['pending_webhooks'];
        $this->request = $stripeEventData['request'];
        $this->type = $stripeEventData['type'];
    }
}
  1. Update your PaymentWebhook class to use the StripeEventDTO:
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\Queueable;

class PaymentWebhook implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected StripeEventDTO $stripe_event;

    public function __construct(StripeEventDTO $stripe_event)
    {
        $this->stripe_event = $stripe_event;
    }

    public function handle(): void
    {
       // Do something here with $this->stripe_event...
    }
}
  1. When dispatching the job, convert the JSON payload to an array and instantiate the StripeEventDTO:
$stripeEventData = json_decode($jsonPayload, true);
$stripeEventDTO = new StripeEventDTO($stripeEventData);

PaymentWebhook::dispatch($stripeEventDTO);

By using a DTO, you provide PHPStan with a clear structure of the expected data, which allows for better static analysis and type checking. Additionally, this approach makes your code more maintainable and easier to understand.

1 like

Please or to participate in this conversation.