Ik heb Stripe-integraties waarschijnlijk een tiental keer in de afgelopen jaren opgezet, en dit is wat ik heb geleerd: de getting-started tutorial is het gemakkelijke gedeelte. Het moeilijke gedeelte is vier volledig verschillende prijsmodellen in dezelfde organisatie uitvoeren, 30+ landvaluta's zonder afrondingsfouten verwerken, en ervoor zorgen dat uw webhook handlers niet stilzwijgend falen om 3 uur 's ochtends op zaterdag.

Dit is niet nog een "maak een checkout session in 5 minuten"-artikel. We gaan vier productierijsmodellen doorlopen die we hebben gebouwd en beheerd -- gelaagde abonnementen met regionale prijsstelling, marketplace-commissies via Stripe Connect, terugkerende donaties gekoppeld aan specifieke entiteiten, en eenmalige servicebetalingen. Elk heeft zijn eigen verzameling valkuilen, en ik zal de specifieke codepatronen en configuratiebeslissingen delen die ons hebben beschermd tegen pijnlijke bugs.

Inhoudsopgave

Best Stripe Setup for Subscription Business: 4 Models We Run

Waarom één Stripe-setup niet voor iedereen geschikt is

De documentatie van Stripe is uitstekend voor bedrijven met één model. U kiest voor abonnementen of eenmalige betalingen, volgt de handleiding en bent live. Maar de meeste echte bedrijven blijven niet zo lang eenvoudig.

We werken met meerdere producten: een SaaS-platform met gelaagde abonnementen, een marketplace die commissies van aanbieders in rekening brengt, een liefdadigheidsinitiatie met terugkerende sponsorships, en een consultingboekingssysteem met eenmalige betalingen. Elk van deze bevindt zich onder dezelfde Stripe-account, maar vereist fundamenteel verschillende configuraties voor producten, prijsstelling, webhooks en klantbeheer.

De grootste fout die ik teams zie maken, is het proberen om alle facturering in één model te forceren. Een op abonnementen gerichte architectuur breekt als u eenmalige betalingen nodig hebt. Een checkout-session-only-benadering valt uiteen als u terugkerende facturering met proration nodig hebt. U moet uw Stripe-setup zien als een portfolio van factureingspatronen.

Als u iets soortgelijks bouwt -- vooral op een headless-architectuur met Next.js of Astro op de frontend -- zullen de patronen hier u weken debuggen besparen.

Model 1: Gelaagde abonnementen met regionale prijsstelling

Dit is het meest complexe model dat we uitvoeren, en het is degene die ons de meest pijnlijke lessen heeft geleerd. De setup: vier tiers (Free, Basic, Pro, Premium) met prijzen die in 30+ landen variëren.

De productstructuur in Stripe

In Stripe is elke tier een afzonderlijk product. Elk product heeft meerdere prijzen -- één per valuta-/regio-combinatie. Dit is belangrijk: probeer niet om één prijs te gebruiken en zelf valutaomzetting uit te voeren. De multi-currency prijsstelling van Stripe is specifiek voor dit doel gebouwd.

// Regionale prijsconfiguratie
const REGIONAL_PRICING = {
  pro: {
    USD: { monthly: 2900, yearly: 29000 },  // $29/ma, $290/jr
    EUR: { monthly: 2700, yearly: 27000 },  // €27/ma, €270/jr
    GBP: { monthly: 2300, yearly: 23000 },  // £23/ma, £230/jr
    JPY: { monthly: 4200, yearly: 42000 },  // ¥4.200/ma -- NIET ¥42,00!
    KRW: { monthly: 38000, yearly: 380000 }, // ₩38.000/ma
    INR: { monthly: 190000, yearly: 1900000 }, // ₹1.900/ma
    BRL: { monthly: 14900, yearly: 149000 }, // R$149/ma
  },
  // ... herhaal voor basic, premium
};

Ziet u die JPY- en KRW-waarden? Ik ga later in detail in op die bug, maar het korte versie: dit zijn zero-decimal valuta's. Wanneer u 4200 voor JPY doorgeeft, interpreteert Stripe dit als ¥4.200 -- niet ¥42,00. Als u met 100 vermenigvuldigt zoals u voor USD doet, hebt u iemand net ¥420.000 ($2.800) in plaats van ¥4.200 ($28) in rekening gebracht. Vraag me hoe ik het weet.

Regionale proeflogica

Niet elke regio krijgt een gratis proefperiode. We hebben dit op de moeilijke manier geleerd met bepaalde markten in Zuidoost-Azië waar proefmisbruik aanzienlijk hoger was dan in andere regio's. Onze configuratie ziet er als volgt uit:

const TRIAL_CONFIG = {
  default_trial_days: 14,
  excluded_regions: ['VN', 'PH', 'ID', 'TH', 'MM', 'KH', 'LA'],
  reduced_trial_regions: {
    IN: 7,
    BR: 7,
  },
};

function getTrialDays(countryCode) {
  if (TRIAL_CONFIG.excluded_regions.includes(countryCode)) {
    return 0;
  }
  return TRIAL_CONFIG.reduced_trial_regions[countryCode] 
    ?? TRIAL_CONFIG.default_trial_days;
}

Dit wordt doorgegeven in het abonnementaanmaakproces:

const subscription = await stripe.subscriptions.create({
  customer: customerId,
  items: [{ price: regionalPriceId }],
  trial_period_days: getTrialDays(customer.address.country),
  payment_behavior: 'default_incomplete',
  payment_settings: {
    save_default_payment_method: 'on_subscription',
  },
  expand: ['latest_invoice.payment_intent'],
});

Proratiegedrag

Wanneer iemand halverwege de cyclus van Basic naar Pro upgradet, moet u besluiten: betalen zij het verschil onmiddellijk of bij de volgende factureringscyclus? We gebruiken create_prorations met onmiddellijke betaling:

const updatedSubscription = await stripe.subscriptions.update(subscriptionId, {
  items: [{
    id: existingItemId,
    price: newPriceId,
  }],
  proration_behavior: 'create_prorations',
  payment_behavior: 'pending_if_incomplete',
});

Voor downgrades plannen we de wijziging aan het einde van de factureringscyclus in. Niemand wil een verrassing creditering op zijn factuur.

Klantportaal

Het Customer Portal van Stripe wordt onderschat. In plaats van uw eigen abonnementbeheer-UI te bouwen, configureert u het portaal en leidt u gebruikers ernaar:

const portalSession = await stripe.billingPortal.sessions.create({
  customer: customerId,
  return_url: `${process.env.APP_URL}/settings/billing`,
});

Configureer het in het Stripe Dashboard om planwijzigingen, annulering (met een annuleringsvragenlijst -- de gegevens zijn goud) en betaalmethode-updates toe te staan. Dit alleen heeft ons waarschijnlijk 40 uur frontend-ontwikkeling bespaard.

Model 2: Marketplace-commissies met Stripe Connect

Ons marketplace-model gebruikt Stripe Connect om betalingen tussen klanten en serviceproviders te faciliteren. Het platform neemt een commissie op elke transactie. Dit is de Stripe Connect-setup die de meeste tutorials overslaande.

Provider-onboarding

Elke provider op de marketplace heeft een Stripe Express-account nodig. De onboarding-stroom maakt het account aan en leidt hem om naar de gehoste onboarding van Stripe:

const account = await stripe.accounts.create({
  type: 'express',
  country: provider.country,
  email: provider.email,
  capabilities: {
    card_payments: { requested: true },
    transfers: { requested: true },
  },
  business_type: 'individual',
  metadata: {
    provider_id: provider.id,
    platform: 'fme',
  },
});

const accountLink = await stripe.accountLinks.create({
  account: account.id,
  refresh_url: `${process.env.APP_URL}/provider/onboarding/refresh`,
  return_url: `${process.env.APP_URL}/provider/onboarding/complete`,
  type: 'account_onboarding',
});

Het belangrijkste detail: refresh_url is waar Stripe gebruikers naartoe stuurt als de link verloopt. Dit gebeurt vaker dan je zou denken -- als iemand onboarding start op zijn telefoon, wordt afgeleid en later terugkomt. Handel dit altijd gracefully af door een nieuwe link te genereren.

Commissiestructuur

Wanneer een klant een service boekt, maken we een PaymentIntent met een application_fee_amount:

const paymentIntent = await stripe.paymentIntents.create({
  amount: bookingAmountInCents,
  currency: 'usd',
  application_fee_amount: Math.round(bookingAmountInCents * 0.15), // 15% platformtarief
  transfer_data: {
    destination: providerStripeAccountId,
  },
  metadata: {
    booking_id: booking.id,
    provider_id: provider.id,
    customer_id: customer.id,
  },
});

De 15%-commissie gaat naar het platform. Het resterende 85% (minus Stripe's verwerkingskosten) gaat naar het Express-account van de provider.

Uitbetalingsplanning

Standaard betaalt Stripe Express-accounts op een doorlopende basis uit. We overschrijven dit naar wekelijkse uitbetalingen, wat ons een buffer voor restituties en geschillen geeft:

await stripe.accounts.update(providerStripeAccountId, {
  settings: {
    payouts: {
      schedule: {
        interval: 'weekly',
        weekly_anchor: 'friday',
      },
    },
  },
});

Vrijdagse uitbetalingen betekenen dat providers het geld maandag op hun bankrekening zien. Het is een klein detail, maar het is enorm belangrijk voor provider-tevredenheid en retentie.

Connect-specifieke webhooks

Met Stripe Connect ontvangt u webhooks voor zowel uw platform ALS uw verbonden accounts. U hebt een afzonderlijk webhook-eindpunt nodig voor Connect-gebeurtenissen:

// Regelmatig webhook-eindpunt
app.post('/webhooks/stripe', handlePlatformWebhooks);

// Connect webhook-eindpunt
app.post('/webhooks/stripe-connect', handleConnectWebhooks);

De Connect webhook-handler moet de gebeurtenis anders verifiëren en het account-veld controleren:

async function handleConnectWebhooks(req, res) {
  const event = stripe.webhooks.constructEvent(
    req.body,
    req.headers['stripe-signature'],
    process.env.STRIPE_CONNECT_WEBHOOK_SECRET // Ander geheim!
  );
  
  const connectedAccountId = event.account;
  // Verwerk nu de gebeurtenis in de context van het verbonden account
}

Best Stripe Setup for Subscription Business: 4 Models We Run - architectuur

Model 3: Terugkerende donaties gekoppeld aan entiteiten

Deze is voor een dierenliefdeadigheidsinitiatie die we bouwen -- denk aan "een specifiek dier sponsoren" met maandelijkse terugkerende donaties. De donor kiest een dier, stelt een maandbedrag in en ontvangt fotoupdates.

Aan entiteit gekoppelde abonnementen

De truc hier is een Stripe-abonnement aan een specifieke entiteit (dier) in uw database koppelen. We doen dit volledig via metadata:

const subscription = await stripe.subscriptions.create({
  customer: donorCustomerId,
  items: [{
    price_data: {
      currency: 'usd',
      product: sponsorshipProductId,
      unit_amount: donorChosenAmount, // Donor kiest zijn bedrag
      recurring: {
        interval: 'month',
      },
    },
  }],
  metadata: {
    entity_id: animal.id,
    entity_type: 'animal',
    entity_name: animal.name,
    sponsor_email: donor.email,
  },
});

Met price_data in plaats van een vooraf gemaakte prijs kunnen donoren hun eigen maandbedrag kiezen. Dit is schoner dan het aanmaken van honderden prijsobjecten.

Maandelijkse updatee-mails

Wanneer invoice.paid voor een sponsorshipabonnement afvlamt, activeren we de maandelijkse update-stroom:

async function handleSponsorshipInvoicePaid(invoice) {
  const subscription = await stripe.subscriptions.retrieve(invoice.subscription);
  const entityId = subscription.metadata.entity_id;
  
  // Wachtrij maandelijkse updatemail met meest recente foto's
  await emailQueue.add('sponsorship-update', {
    donorEmail: subscription.metadata.sponsor_email,
    entityId,
    invoiceAmount: invoice.amount_paid,
    invoicePdf: invoice.invoice_pdf,
  });
}

De e-mail bevat de invoerpdf (Stripe genereert deze automatisch), recente foto's van het gesponsorde dier en een zorgupdate. Het is een klein detail dat de versnelling bij terugkerende donaties dramatisch vermindert.

Donatie-annuleringen afhandelen

Wanneer iemand zijn sponsorship annuleert, moet u het anders afhandelen dan een SaaS-annulering. Er is geen "downgrade" -- het is annuleren of niets. Maar u wilt het gemakkelijk maken om later opnieuw in te schrijven:

async function handleSponsorshipCancellation(subscription) {
  const entityId = subscription.metadata.entity_id;
  
  // Markeer sponsorship als inactief, niet verwijderd
  await db.sponsorships.update({
    where: { stripeSubscriptionId: subscription.id },
    data: { 
      status: 'inactive',
      cancelledAt: new Date(),
    },
  });
  
  // Stuur "we zullen je missen"-email met eenvoudig opnieuw inschrijven-link
  await sendCancellationEmail(subscription.metadata.sponsor_email, entityId);
}

Model 4: Eenmalige servicebetalingen

Het eenvoudigste model, maar er zijn nog steeds details die ertoe doen. Dit patroon is voor consultingboekingen waarbij iemand eenmaal betaalt en een service krijgt -- geen terugkerende facturering.

Checkout-sessie met boekingsgegevens

const session = await stripe.checkout.sessions.create({
  mode: 'payment',
  line_items: [{
    price: consultationPriceId,
    quantity: 1,
  }],
  customer_email: customer.email,
  metadata: {
    booking_id: booking.id,
    service_type: 'consultation',
    appointment_date: booking.date.toISOString(),
    practitioner_id: booking.practitionerId,
  },
  success_url: `${process.env.APP_URL}/booking/confirmed?session_id={CHECKOUT_SESSION_ID}`,
  cancel_url: `${process.env.APP_URL}/booking/${booking.id}`,
  expires_after: 1800, // 30 minuten
  payment_intent_data: {
    metadata: {
      booking_id: booking.id,
    },
  },
});

Twee dingen om op te merken. Eerst: expires_after voorkomt dat verlaten checkout-sessies blijven hangen. Een boeking slot mag niet voor altijd worden ingehouden. Ten tweede: we dupliceren de booking_id in payment_intent_data.metadata omdat de PaymentIntent-metadata gescheiden is van de Checkout Session-metadata. Wanneer u de payment_intent.succeeded webhook ontvangt, wilt u die boekings-ID daar recht hebben.

Betaling + boekingsbevestiging

Op checkout.session.completed bevestigen we de boeking en sturen alles in één schot:

async function handleCheckoutComplete(session) {
  const bookingId = session.metadata.booking_id;
  
  // Bevestig de boeking
  const booking = await db.bookings.update({
    where: { id: bookingId },
    data: { 
      status: 'confirmed',
      paymentSessionId: session.id,
      paidAt: new Date(),
    },
  });
  
  // Stuur bevestiging naar klant
  await sendBookingConfirmation(session.customer_email, booking);
  
  // Meld praktiserend
  await notifyPractitioner(booking.practitionerId, booking);
}

Webhook-architectuur die werkelijk functioneert

In alle vier de modellen zijn webhooks de ruggengraat. Hier is de architectuur die we hebben vastgesteld na te veel debugsessies:

const WEBHOOK_HANDLERS = {
  'checkout.session.completed': handleCheckoutComplete,
  'invoice.paid': handleInvoicePaid,
  'invoice.payment_failed': handlePaymentFailed,
  'customer.subscription.created': handleSubscriptionCreated,
  'customer.subscription.updated': handleSubscriptionUpdated,
  'customer.subscription.deleted': handleSubscriptionDeleted,
  'account.updated': handleConnectAccountUpdated,
  'payment_intent.succeeded': handlePaymentSucceeded,
};

async function webhookHandler(req, res) {
  let event;
  try {
    event = stripe.webhooks.constructEvent(
      req.rawBody, // U hebt de onbewerkte body nodig, niet geparseede JSON
      req.headers['stripe-signature'],
      process.env.STRIPE_WEBHOOK_SECRET
    );
  } catch (err) {
    console.error('Webhook signature verification failed:', err.message);
    return res.status(400).send();
  }

  // Idempotentie: controleer of we deze gebeurtenis al hebben verwerkt
  const processed = await db.webhookEvents.findUnique({
    where: { stripeEventId: event.id },
  });
  if (processed) {
    return res.status(200).json({ received: true, duplicate: true });
  }

  const handler = WEBHOOK_HANDLERS[event.type];
  if (handler) {
    try {
      await handler(event.data.object, event);
      await db.webhookEvents.create({
        data: { stripeEventId: event.id, type: event.type, processedAt: new Date() },
      });
    } catch (err) {
      console.error(`Error processing ${event.type}:`, err);
      return res.status(500).send(); // Stripe probeert opnieuw
    }
  }

  res.status(200).json({ received: true });
}

De idempotentiecontrole is kritiek. Stripe zal mislukte webhooks opnieuw proberen, en u wilt absoluut niet dezelfde gebeurtenis twee keer verwerken -- vooral niet voor dingen zoals het aanmaken van boekingen of het activeren van uitbetalingen.

Logica voor opnieuw proberen van mislukte betalingen en dunning

Stripe heeft ingebouwde Smart Retries, maar u moet uw eigen dunning-logica erop stapelen:

async function handlePaymentFailed(invoice) {
  const attemptCount = invoice.attempt_count;
  const subscription = await stripe.subscriptions.retrieve(invoice.subscription);
  
  if (attemptCount === 1) {
    // Eerste fout: zachte duw
    await sendEmail(invoice.customer_email, 'payment-failed-soft', {
      updatePaymentUrl: await createPortalLink(invoice.customer),
    });
  } else if (attemptCount === 2) {
    // Tweede fout: dringender
    await sendEmail(invoice.customer_email, 'payment-failed-urgent', {
      updatePaymentUrl: await createPortalLink(invoice.customer),
      daysUntilCancellation: 7,
    });
  } else if (attemptCount >= 3) {
    // Laatste waarschuwing
    await sendEmail(invoice.customer_email, 'payment-failed-final', {
      updatePaymentUrl: await createPortalLink(invoice.customer),
    });
  }
}

Configureer Stripe's retry-schema in Dashboard → Settings → Subscriptions and emails → Manage failed payments. We gebruiken 3 pogingen over 14 dagen voordat we annuleren.

De zero-decimal currency-bug die ons geld kostte

Dit verdient zijn eigen sectie omdat het een bug is die iedereen uiteindelijk tegenkomt. Stripe gebruikt cents (kleinste valuta-eenheid) voor de meeste valuta's. $29,00 wordt 2900. Maar sommige valuta's hebben geen decimale plaatsen.

Hier zijn de zero-decimal valuta's die ertoe doen:

Valuta Code Voorbeeld: "€29 equivalent" Wat u doorgeeft aan Stripe
Japanse Yen JPY ¥4.200 4200 (NIET 420000)
Koreaanse Won KRW ₩38.000 38000 (NIET 3800000)
Vietnamees Dong VND ₫700.000 700000
Chileense Peso CLP $25.000 25000
Paraguayaanse Guarani PYG ₲200.000 200000

Hier is de utiliteitsfunctie die we overal gebruiken:

const ZERO_DECIMAL_CURRENCIES = [
  'BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW',
  'MGA', 'PYG', 'RWF', 'UGX', 'VND', 'VUV', 'XAF',
  'XOF', 'XPF',
];

function toStripeAmount(amount, currency) {
  const curr = currency.toUpperCase();
  if (ZERO_DECIMAL_CURRENCIES.includes(curr)) {
    return Math.round(amount); // Al in kleinste eenheid
  }
  return Math.round(amount * 100);
}

function fromStripeAmount(stripeAmount, currency) {
  const curr = currency.toUpperCase();
  if (ZERO_DECIMAL_CURRENCIES.includes(curr)) {
    return stripeAmount;
  }
  return stripeAmount / 100;
}

Gebruik dit overal. Bij het aanmaken van uw checkout, in uw webhook-handlers, in uw dashboardweergaven. Overal. De ene keer dat u het vergeet, is de keer dat u iemand 100 keer zoveel in rekening brengt als hij verwacht.

De vier modellen vergelijken

Aspect Gelaagde abonnementen Marketplace Terugkerende donatie Eenmalige betaling
Stripe-product Meerdere producten, meerdere prijzen per product Enkel product per servicetype Enkel product, dynamische prijsstelling Enkel product, vaste prijs
Factureingsmodus subscription payment met Connect subscription payment
Webhook-complexiteit Hoog (levenscyclusgebeurtenissen) Hoog (Connect-gebeurtenissen) Gemiddeld Laag
Valutaverwerking Regionale prijsmatrix Valuta van provider Valuta van donor Enkele valuta
Proefondersteuning Ja, afhankelijk van regio N.v.t. N.v.t. N.v.t.
Proration Ja, bij upgrades N.v.t. N.v.t. N.v.t.
Restitutiecomplexiteit Gепrationeerde berekeningen Omkeringsfactuur toepassingstaken Eenvoudige volledige restitutie Eenvoudige volledige restitutie
Klantportaal Essentieel Niet nodig Leuk om te hebben Niet nodig
Stripe-kosten (2025) 2,9% + €0,30 2,9% + €0,30 + 0,5% Connect 2,9% + €0,30 2,9% + €0,30

Veelgestelde vragen

Hoeveel Stripe-producten zou ik moeten maken voor gelaagde prijzen? Eén product per tier. Dus als u Free, Basic, Pro en Premium hebt, zijn dat vier producten. Elk product heeft dan meerdere prijzen -- één per valuta en factureringintervalcombinatie. Een Pro-tier met maandelijkse en jaarlijkse facturering over 10 valuta's betekent 20 prijsobjecten op dat ene product. Het klinkt als veel, maar Stripe verwerkt dit goed en het houdt uw catalogus georganiseerd.

Kan ik Stripe Checkout gebruiken voor abonnementen met regionale prijsstelling? Ja, maar u moet de regio van de klant bepalen voordat u de Checkout-sessie maakt, zodat u de juiste prijs-ID kunt doorgeven. We gebruiken IP-geolocatie (via Cloudflare-headers) om de valuta vooraf in te stellen, en laten de klant deze dan bevestigen of wijzigen. Vertrouw niet op de automatische valuta van Checkout -- u wilt controle over welke prijs zij zien.

Wat is het verschil tussen Stripe Connect Express- en aangepaste accounts? Express-accounts laten Stripe de onboarding, identiteitsverificatie en dashboard voor uw providers afhandelen. Aangepaste accounts geven u volledige controle, maar vereisen dat u dit alles zelf bouwt. Voor de meeste marktplaatsen is Express de juiste keuze. We hebben nooit een geval gehad waarin het verlies van controle de engineeringkosten van Custom rechtvaardigde. Express-accounts verwerken ook belastingrapportage (1099's in de VS) automatisch, wat een enorme nalevingswinst is.

Hoe ga ik om met mislukte abonnementsbetalingen zonder klanten kwijt te raken? Stapel drie dingen: Stripe's Smart Retries (ingeschakeld in Dashboard), aangepaste dunning-e-mails geactiveerd door invoice.payment_failed webhooks, en een tolperiode vóór annulering. We geven 14 dagen over 3 herpogingen. De eerste e-mail is vriendelijk ("hé, uw kaart kan zijn verlopen"), de tweede is dringend, en de derde is een laatste waarschuwing. Voeg een directe link naar het Customer Portal toe waar zij hun betaalmethode kunnen bijwerken. Dit alleen herstelt ongeveer 30-40% van mislukte betalingen.

Heb ik afzonderlijke webhook-eindpunten nodig voor Stripe Connect? Ja. Platform-gebeurtenissen en Connect-account-gebeurtenissen gebruiken verschillende webhook-geheimen en verschillende eventstructuren. Connect-gebeurtenissen bevatten een account-veld dat aangeeft welk verbonden account de gebeurtenis betreft. Registreer twee eindpunten in uw Stripe Dashboard: één voor platformgebeurtenissen, één voor Connect-gebeurtenissen. Deze scheiding maakt debuggen ook veel gemakkelijker.

Wat zijn zero-decimal valuta's en waarom zou ik me er om bekommeren? Zero-decimal valuta's zoals JPY (Japanse Yen) en KRW (Koreaanse Won) gebruiken geen fractionele eenheden. Wanneer Stripe zegt "bedrag in kleinste valuta-eenheid", voor USD zijn dat cents (2900 = $29,00), maar voor JPY is het yen (4200 = ¥4.200). Als u met 100 vermenigvuldigt zoals u voor USD doet, berekent u ¥420.000 in plaats van ¥4.200. Gebruik altijd een helperfunctie die de valuta controleert voordat u converteert. Stripe onderhoudt de officiële lijst met zero-decimal valuta's in hun documentatie.

Zou ik de Customer Portal van Stripe Billing moeten gebruiken of mijn eigen bouwen? Gebruik het Customer Portal voor abonnementbeheer, tenzij u zeer specifieke UI-vereisten hebt. Het verwerkt planwijzigingen, annuleringen, betaalmethode-updates en factuursgeschiedenis uit de doos. U kunt de branding aanpassen en configureren welke acties zijn toegestaan. Het bouwen van uw eigen portaal betekent dat u zelf proration-berekeningen, betaalmethode-tokenisering en SCA/3D Secure-flows afhandelt. Het portaal is gratis -- het is inbegrepen in uw Stripe-abonnementskosten.

Hoe test ik regionale prijsstelling en valutaverwerking lokaal? Stripe's testmodus ondersteunt alle valuta's. Maak testprijzen in elke valuta die u van plan bent te ondersteunen, en gebruik vervolgens Stripe CLI om webhooks naar uw lokale server door te sturen: stripe listen --forward-to localhost:3000/webhooks/stripe. Voor specifiek testen van zero-decimal valuta, maakt u een JPY-prijs en verifieert u de bedragen in uw webhook-handler-logboeken voordat u live gaat. We onderhouden ook een testpakket dat toStripeAmount en fromStripeAmount tegen elke ondersteunde valuta uitvoert -- het heeft meer dan eens problemen opgemerkt.

Als u een op abonnementen gebaseerd product bouwt en hulp nodig hebt met de factureringsarchitectuur, of als u Stripe integreert met een headless CMS-setup, neem contact met ons op. We hebben deze patronen gebouwd in meerdere headless CMS-projecten en kunnen u helpen dure fouten te vermijden. Controleer onze prijspagina voor engagement-modellen -- we doen zowel projectgebaseerde builds als doorlopend advies.