- Webhook handlers stored euros in cent columns (charge.succeeded,
charge.refunded, refund.created, payment_intent.succeeded, the success
redirect, dispute/invoice notes). All amounts are cents now; purchases get
their own line amount as amount_paid.
- One Stripe refund arrives as refund.created AND charge.refunded, in either
order. New Order::applyStripeRefund() / applyStripeRefundedTotal() raise the
order's Stripe refund total (meta.stripe_refunded, meta.stripe_refunds) to
what Stripe confirmed, so nothing is counted twice. refund.created now finds
Checkout orders by payment_intent.
- New ShopService::refundOrder($order, $cents, $reason) refunds through the
Stripe API (idempotency key per order/amount/state) and records it, plus
refundableAmount(). Bind \Stripe\StripeClient to swap the client.
- charge.succeeded remembers meta.stripe_charge_id; with
shop.ledger.sync_on_webhook (default off) charge/refund/dispute webhooks
upsert the balance transactions into stripe_transactions.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>