import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function createCheckoutSession(userId: string, email: string) {
return await stripe.checkout.sessions.create({
payment_method_types: ['card'],
mode: 'subscription',
customer_email: email,
client_reference_id: userId,
// Colectează automat CUI/CIF pentru firme din RO/UE
tax_id_collection: {
enabled: true,
},
billing_address_collection: 'required',
line_items: [
{
price: 'price_1Nxxxxxxxxxxxxxx',
quantity: 1,
},
],
success_url: 'https://app.domeniu.ro/billing/success?session_id={CHECKOUT_SESSION_ID}',
cancel_url: 'https://app.domeniu.ro/billing/cancel',
});
}Am rescris fluxul de billing de trei ori la un SaaS B2B cu vreo 1.400 de clienți activi. Dacă ești la început sau vrei să schimbi procesatorul, alegerea dintre Stripe Checkout și Stripe Elements se reduce la două lucruri: cât timp ai de pierdut și cât de mult te doare integrarea cu datele fiscale din România.
Nu e o decizie pe viață, dar poate fi diferența între a lansa în weekend sau peste o lună.
Stripe Checkout: lansare rapidă, compromis minim
Stripe Checkout este o pagină găzduită de ei. Generezi o sesiune pe backend, redirecționezi userul pe checkout.stripe.com, iar el plătește. Ai Apple Pay, Google Pay, salvare automată de carduri și 3D Secure gestionate impecabil, fără să scrii tu CSS pentru formulare.
Cel mai mare avantaj pentru piața din RO este validarea automată de date fiscale. Activezi tax_id_collection și Stripe colectează CIF/CUI-ul, ba chiar îl validează prin VIES dacă clientul e din UE. În plus, dacă ai activat Stripe Tax, calculează cota corectă de TVA fără să ții tu tabele în baza de date.
Dezavantajul? Redirectul. Utilizatorul iese din platforma ta. Pentru unii fondatori asta e o crimă de UX, deși în realitate rata de conversie rareori suferă din cauza asta la început de drum.
Stripe Elements: libertate totală cu cost de mentenanță
Elements îți oferă componente UI (iframe-uri securizate) pe care le montezi direct în layout-ul tău. Userul nu părăsește nicio secundă domeniul tău, stilizezi câmpurile exact ca restul aplicației și poți crea fluxuri hibride — de exemplu, colectezi date de onboarding și cardul în același ecran de înregistrare.
Sună ideal, dar trade-off-ul e brutal la mentenanță. Când folosești Elements, tu ești responsabil de stările de eroare, de retry-uri și de gestionarea pașilor suplimentari de autentificare (3DS2 cerut de băncile din România). Am pățit la o bancă locală ca fereastra modală de autorizare din aplicație să facă timeout ciudat pentru că nu prinsesem corect un eveniment de eroare în client.
În plus, colectarea de date fiscale pentru persoane juridice trebuie s-o implementezi manual: faci input pentru CUI, îl validezi tu (eventual prin API-ul ANAF sau VIES), iar abia apoi transmiți datele către Stripe prin setupIntent sau paymentIntent.
Cifre și costuri ascunse
Comisioanele Stripe sunt identice în ambele variante: 1.5% + 1 RON pentru cardurile emise în Spațiul Economic European (sau 1.4% + 0.25 EUR dacă facturezi în euro). Stripe nu taxează suplimentar pentru Elements.
Costul real este timpul de inginerie:
- Checkout: L-am implementat cap-coadă, cu webhook-uri de sincronizare în baza de date, în vreo 6 ore de lucru.
- Elements: Un flux complet, testat pe mobile, cu gestionare de erori bancare și câmpuri de facturare B2B, mănâncă lejer 35-50 de ore de dev.
Verdict pragmatic
Recomandarea mea e simplă: începe cu Stripe Checkout. Dacă vinzi un SaaS B2B sau chiar B2C la început, nimeni nu va refuza să plătească doar pentru că a fost redirecționat 10 secunde pe un domeniu Stripe cu SSL și brand recunoscut.
Treci la Stripe Elements abia când treci de 10.000 EUR MRR și ai date clare din analytics că acel redirect îți scade conversia cu măcar 2-3%. Până atunci, codul scris pentru un checkout custom e doar timp furat de la funcționalitățile de bază ale produsului.
Voi cum ați abordat colectarea de CUI/CIF în fluxul de plată — nativ în Stripe sau prin apeluri paralele la API-ul ANAF?