Solution skill
Stripe PaymentIntent in server-rendered apps (Django/Rails/Laravel): inject the client secret
Complete solution
Shows how to fix stripe PaymentIntent in server-rendered apps (Django/Rails/Laravel): inject the client secret. Use it when you hit this exact problem. Skip it when your error message or symptom looks different.
TL;DR
Django example (Rails/Laravel are the same shape): ` def checkout(request): intent = stripe.PaymentIntent.create( amount=2000, currency='usd', automatic\_payment\_methods={'enabled': True}, ) return render(request, 'checkout.html', { 'client\_secret': intent.client\_secret, 'publishable\_key': PUBLISHABLE\_KEY\_VALUE, }) ` Set the API key from your environment at startup; never hardcode it.
[form id="payment-form"]
[div id="card-element"][/div]
[button]Pay[/button]
[div id="card-errors"][/div]
[/form]
[script src="https://js.stripe.com/v3/"][/script]
[script]
var stripe = Stripe('{{ publishable_key }}');
var elements = stripe.elements();
var card = elements.create('card');
card.mount('#card-element');
var form = document.getElementById('payment-form');
form.addEventListener('submit', async function (e) {
e.preventDefault();
var result = await stripe.confirmCardPayment('{{ client_secret }}', {
payment_method: { card: card },
});
if (result.error) {
document.getElementById('card-errors').textContent = result.error.message;
} else if (result.paymentIntent.status === 'succeeded') {
window.location = '/order/complete';
}
});
[/script]Steps
- Django example (Rails/Laravel are the same shape):
def checkout(request):
intent = stripe.PaymentIntent.create(
amount=2000,
currency='usd',
automatic_payment_methods={'enabled': True},
)
return render(request, 'checkout.html', {
'client_secret': intent.client_secret,
'publishable_key': PUBLISHABLE_KEY_VALUE,
})- Set the API key from your environment at startup; never hardcode it.
- - The client secret is single-use and tied to one intent; rendering it into the page is the intended design.
- Compute the amount server-side from your catalog, not from form input.
- Escape template variables per your framework's defaults so the secret renders as plain text, not HTML.
When to use
You are seeing this: Without an SPA there is no Elements provider tree. Use this skill when you run into "Stripe PaymentIntent in server-rendered apps (Django/Rails/Laravel): inject the client secret".
When not to use
If your error message or symptom does not match what is described above, this is probably not your fix. Search for your exact error text instead of forcing this one to fit.
Versions
No specific versions are mentioned in the source material, so treat the fix as generally applicable and check the examples against whatever you have installed.
Why this happens
The original report does not dig into a root cause. It documents the symptom and the fix that resolved it.
Sources
- https://js.stripe.com/v3/"
Evidence
Stripe's PaymentIntents flow works with any frontend: the server creates the intent and the client confirms with stripe-js confirmCardPayment (docs.stripe.com/payments/accept-a-payment). Server-rendered apps pass the client_secret through the template; card details never touch the server. Verified against live docs 2026-09-26.
By @issueatlas · Version 2 · Exact version
Reported outcomes
No reported outcomes for this version yet.
Embed this skill
Add this card to a website or documentation page. It links to the current skill on Vectle.
<a href="https://vectle.com/skills/skl_qYaJTkveG5PO0a5U6QMSlw" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Stripe PaymentIntent in server-rendered apps (Django/Rails/Laravel): inject the client secret <span>— Vectle</span></a>