Payments
The bundle exposes the Stripe building blocks needed for one-shot payments, refunds and payment intents.
Note: All amounts are expressed in cents (1999 = $19.99).
Direct charge
use CashierBundle\Contract\BillableEntityInterface;
use CashierBundle\Service\PaymentService;
final readonly class PaymentController
{
public function __construct(
private PaymentService $paymentService,
) {
}
public function charge(BillableEntityInterface $user): void
{
$payment = $this->paymentService->charge(
billable: $user,
amount: 1999, // 19.99 € in cents
paymentMethod: 'pm_card_visa',
options: [
'description' => 'Order #1234',
'metadata' => ['app_order_id' => '1234'],
],
);
}
}Pay with the default method
$payment = $paymentService->pay($user, 2999); // 29.99 € in centsSCA / 3DS handling
If the payment requires an action (3DS authentication), an IncompletePaymentException is thrown:
use CashierBundle\Exception\IncompletePaymentException;
use Symfony\Component\HttpFoundation\RedirectResponse;
try {
$payment = $paymentService->charge($user, 1999, $pmId);
} catch (IncompletePaymentException $e) {
// Redirect to Stripe payment page to complete authentication
return new RedirectResponse('/cashier/payment/' . $e->payment()->id);
}The exception contains the Payment with clientSecret() for the frontend.
Stripe error handling
use Stripe\Exception\CardException;
try {
$payment = $paymentService->charge($user, 1999, $pmId);
} catch (IncompletePaymentException $e) {
return new RedirectResponse('/checkout/confirm/' . $e->payment()->id());
} catch (CardException $e) {
// Card declined
// $e->getMessage() contains the Stripe error message
}Refund
$paymentService->refund($paymentIntentId);
$paymentService->refundPartial(paymentIntent: $paymentIntentId, amount: 1000);PaymentIntentService (advanced)
For low-level PaymentIntent creation (two-step payment):
use CashierBundle\Service\PaymentIntentService;
$intent = $this->paymentIntentService->create([
'amount' => 4999, // 49.99 € in cents
'currency' => 'eur',
'payment_method_types' => ['card'],
'capture_method' => 'manual', // two-step payment
'metadata' => ['order_id' => '123'],
]);
// $intent contains: id, client_secret, status, amount, currencyAvailable methods
authorize(array $options)— returns['id', 'client_secret', 'status', 'amount', 'currency']capture(string $paymentIntentId, ?int $amount = null)— captures the paymentcancel(string $paymentIntentId)— cancels the authorized paymentconfirm(string $paymentIntentId, array $options)— confirms withIncompletePaymentExceptionhandling
Useful states
$payment->isSucceeded();
$payment->isProcessing();
$payment->requiresAction();
$payment->requiresCapture();Best practice
For a modern front-end tunnel, prefer CheckoutService if you want:
- a payment page hosted by Stripe
invoice_creationautomatically enabled- a simpler webhook chain to exploit server-side
Last updated on