Skip to content

ClassicSwedes partners

Installation

Apply supabase/migrations/20260911_partners.sql using the Supabase SQL editor or your migration pipeline. No sample companies are inserted. This migration creates private application/payment tables, a public projection, and service-role-only transaction functions. Public readers cannot access email, billing details or orders. The migration must run before applications can be accepted.

Required server configuration:

  • NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY (existing).
  • MOLLIE_API_KEY: use a test key before enabling real payments.
  • PARTNER_SITE_URL: canonical public HTTPS origin, e.g. https://www.classicswedes.com. Mollie must be able to reach /api/partners/webhook directly.
  • RESEND_API_KEY and CRON_SECRET for renewal reminders.

vercel.json schedules GET /api/partners/renewal-reminders daily at 09:00 UTC. Set CRON_SECRET in Vercel, which supplies Authorization: Bearer <CRON_SECRET>. Without that secret or the Resend key the endpoint fails closed. The job sends a reminder within 30 days of expiry. Replies go to support for a reviewed renewal quote. It records the reminded expiry and uses Resend idempotency for retries.

Operations

  1. Businesses submit /partners/join. Applications and service/model relations are stored atomically with pending/unpaid status. A honeypot, per-email pending uniqueness and a database-backed limit of five submissions per hour per hashed client address limit automated submissions; raw IP addresses are not stored. Keep this endpoint behind a proxy that overwrites forwarding headers (as Vercel does).
  2. Sign in as an existing profiles.role = admin user and visit /admin/partners. Review company relevance, supplied website, profile copy, models and billing details.
  3. Confirm the applicable VAT percentage and invoice note, then approve. VAT is deliberately a manual decision; confirm Dutch VAT and EU B2B treatment with your accountant before live invoicing. No automatic tax validation or invoice generation is claimed.
  4. Send the generated payment-page link to the applicant. Opening the page does not itself create a Mollie payment; the payment button does. Payment buys one year and does not start a subscription.
  5. The webhook retrieves the payment from Mollie and checks currency, amount, order metadata and stored payment ID. A database transaction publishes the approved partner and adds 12 calendar months. Repeated callbacks cannot extend twice. The return page reconciles a paid transaction too.
  6. Renewals open 31 days before expiry. Create a new reviewed order in the same admin screen. A renewal extends from the later of existing expiry or payment time. Founding discounts only apply to the first paid year.

An expired paid_until removes visibility even if the record still has active status. Model pages may cache for their existing 300-second revalidation window. Featured partners alone appear on matching model pages (maximum three). Exact model names reuse MODEL_OPTGROUPS_VOLVO; select all applicable models for broad services such as insurance.

Founding allocation is limited transactionally to 20 open/paid first-year orders. Configure partner_settings.founding_enabled and founding_limit in Supabase. Places are reserved when approved; cancel abandoned orders to release places. No fabricated availability counter is displayed. If Mollie payment expired/canceled/failed, cancel the order in admin and generate a new link. Cancel a still-open Mollie payment in Mollie first.

Seed companies can be granted a reviewed complimentary year by an administrator in Supabase: set approved_at, status = active, payment_status = waived, and an explicit future paid_until. Do not fabricate partners or import company logos without permission. Complimentary years do not qualify for a founding first-year discount. For editorial corrections and package changes, use the private tables in Supabase; existing orders retain their agreed package/amount snapshot.

Refunds and chargebacks require manual review in Mollie and deactivation in admin. Automatic refund accounting and invoice generation are outside this first version. Deactivation blocks future publication and payments; resolve any outstanding paid transaction manually before reapproving a rejected company.

Verification before launch

Use Mollie test mode on an HTTPS preview: approve an application, pay successfully, replay the webhook, cancel a payment and generate a replacement, renew before expiry, and confirm expired/inactive/unapproved records are invisible. Test that anon/authenticated roles cannot read private tables or execute payment functions. The public view intentionally executes as its owner and exposes an explicit allowlist of fields.

Official payment flow references: https://docs.mollie.com/reference/payments-api-webhooks and https://docs.mollie.com/reference/api-idempotency.

Rollback

Export partner data first. Remove the new routes/components, scheduled reminder job and footer/model/admin links before reverting the schema. In a transaction, drop the public_partners view, the three functions (submit_partner_application, approve_partner_order, apply_partner_payment), then partner_orders, partner_models, partner_services, partner_settings, partner_application_limits, and partners. Dropping the tables destroys application and payment records; never run a rollback against paid production data without a backup.

NEDERLANDS

ClassicSwedes partners

Installatie

Voer supabase/migrations/20260911_partners.sql uit via de Supabase SQL-editor of je migratiepipeline. Er worden geen voorbeeldbedrijven toegevoegd. Deze migratie maakt privé-tabellen voor aanvragen en betalingen, een openbare projectie en transactiefuncties die alleen toegankelijk zijn voor de service role. Openbare gebruikers hebben geen toegang tot e-mailadressen, factuurgegevens of bestellingen. De migratie moet zijn uitgevoerd voordat aanvragen kunnen worden geaccepteerd.

Vereiste serverconfiguratie:

  • NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY (bestaand).
  • MOLLIE_API_KEY: gebruik een testsleutel voordat je echte betalingen inschakelt.
  • PARTNER_SITE_URL: de canonieke openbare HTTPS-origin, bijvoorbeeld https://www.classicswedes.com. Mollie moet /api/partners/webhook rechtstreeks kunnen bereiken.
  • RESEND_API_KEY en CRON_SECRET voor herinneringen voor verlengingen.

vercel.json plant dagelijks om 09:00 UTC een GET /api/partners/renewal-reminders in. Stel CRON_SECRET in Vercel in; Vercel stuurt deze mee als Authorization: Bearer <CRON_SECRET>. Zonder deze secret of de Resend-key wordt het endpoint uit veiligheidsoverwegingen niet uitgevoerd. De taak verstuurt een herinnering binnen 30 dagen vóór het verlopen van het partnerschap. Reacties gaan naar support, zodat een verlengingsvoorstel kan worden beoordeeld. Het systeem registreert voor welke vervaldatum een herinnering is verstuurd en gebruikt Resend-idempotentie bij nieuwe pogingen.

Beheer

  1. Bedrijven dienen hun aanvraag in via /partners/join. Aanvragen en relaties met diensten/modellen worden atomair opgeslagen met de status pending/unpaid. Een honeypot, maximaal één openstaande aanvraag per e-mailadres en een databasegestuurde limiet van vijf inzendingen per uur per gehasht clientadres beperken geautomatiseerde inzendingen. Ruwe IP-adressen worden niet opgeslagen. Houd dit endpoint achter een proxy die forwarding headers overschrijft, zoals Vercel dat doet.

  2. Log in als een bestaande gebruiker met profiles.role = admin en ga naar /admin/partners. Controleer de relevantie van het bedrijf, de opgegeven website, profieltekst, modellen en factuurgegevens.

  3. Bevestig het toepasselijke btw-percentage en de factuuropmerking en keur de aanvraag vervolgens goed. De btw-bepaling is bewust een handmatige beslissing. Controleer de Nederlandse btw-regels en de behandeling van EU B2B-transacties met je accountant voordat je live gaat factureren. Er wordt geen automatische belastingvalidatie of factuurgeneratie aangeboden.

  4. Stuur de gegenereerde link naar de betaalpagina naar de aanvrager. Alleen het openen van de pagina maakt nog geen Mollie-betaling aan; dat gebeurt pas via de betaalknop. De betaling geldt voor één jaar en start geen abonnement.

  5. De webhook haalt de betaling op bij Mollie en controleert de valuta, het bedrag, de ordermetadata en de opgeslagen payment-ID. Een databasetransactie publiceert de goedgekeurde partner en voegt 12 kalendermaanden toe. Herhaalde callbacks kunnen de looptijd niet meerdere keren verlengen. De retourpagina verwerkt ook een betaalde transactie correct.

  6. Verlengingen worden 31 dagen vóór de vervaldatum beschikbaar. Maak in hetzelfde adminscherm een nieuwe, gecontroleerde bestelling aan. Een verlenging loopt vanaf het laatste moment van de bestaande vervaldatum of het betalingstijdstip. Founding-kortingen gelden alleen voor het eerste betaalde jaar.

Een verlopen paid_until zorgt ervoor dat de partner niet meer zichtbaar is, zelfs wanneer de record nog de status active heeft. Modelpagina's kunnen vanwege hun bestaande revalidatievenster maximaal 300 seconden gecachet blijven. Alleen featured partners verschijnen op overeenkomende modelpagina's, met een maximum van drie. Exacte modelnamen maken opnieuw gebruik van MODEL_OPTGROUPS_VOLVO; selecteer alle toepasselijke modellen voor brede diensten, zoals verzekeringen.

De founding-toewijzing is transactioneel beperkt tot 20 open/betaalde bestellingen voor het eerste jaar. Configureer partner_settings.founding_enabled en founding_limit in Supabase. Plaatsen worden gereserveerd zodra een aanvraag wordt goedgekeurd; annuleer verlaten bestellingen om plaatsen weer vrij te geven. Er wordt geen kunstmatige teller voor beschikbare plaatsen getoond. Als een Mollie-betaling is verlopen, geannuleerd of mislukt, annuleer dan de bestelling in admin en genereer een nieuwe link. Annuleer een nog openstaande Mollie-betaling eerst in Mollie.

Startbedrijven kunnen door een beheerder in Supabase een gecontroleerd gratis jaar krijgen: stel approved_at, status = active, payment_status = waived en een expliciete toekomstige paid_until in. Verzin geen partners en importeer geen bedrijfslogo's zonder toestemming. Gratis jaren komen niet in aanmerking voor een founding-korting voor het eerste jaar. Gebruik voor redactionele correcties en pakketwijzigingen de privé-tabellen in Supabase; bestaande bestellingen behouden de afgesproken momentopname van pakket en bedrag.

Terugbetalingen en chargebacks vereisen handmatige controle in Mollie en deactivering via admin. Automatische verwerking van terugbetalingen en factuurgeneratie vallen buiten deze eerste versie. Deactivering blokkeert toekomstige publicatie en betalingen; handel eventuele openstaande betaalde transacties handmatig af voordat je een afgewezen bedrijf opnieuw goedkeurt.

Controle vóór livegang

Gebruik de testmodus van Mollie op een HTTPS-preview: keur een aanvraag goed, voer een succesvolle betaling uit, speel de webhook opnieuw af, annuleer een betaling en genereer een vervangende betaling, verleng vóór de vervaldatum en controleer of verlopen/inactieve/niet-goedgekeurde records niet zichtbaar zijn. Test ook of anon/authenticated roles geen privé-tabellen kunnen lezen of betalingsfuncties kunnen uitvoeren. De openbare view wordt bewust uitgevoerd als eigenaar en stelt alleen een expliciete allowlist van velden beschikbaar.

Officiële referenties voor de betaalflow: https://docs.mollie.com/reference/payments-api-webhooks en https://docs.mollie.com/reference/api-idempotency.

Rollback

Exporteer eerst de partnergegevens. Verwijder de nieuwe routes/componenten, de geplande taak voor verlengingsherinneringen en de links in de footer, modelpagina's en admin voordat je het schema terugdraait.

Verwijder binnen een transactie eerst de public_partners-view en de drie functies (submit_partner_application, approve_partner_order, apply_partner_payment) en vervolgens partner_orders, partner_models, partner_services, partner_settings, partner_application_limits en partners.

Het verwijderen van deze tabellen vernietigt de aanvraag- en betalingsgegevens. Voer daarom nooit een rollback uit op betaalde productiegegevens zonder eerst een back-up te maken.

Mollie instellen voor ClassicSwedes

Voor ClassicSwedes is het verstandig om Mollie eerst volledig in testmodus op te zetten. De huidige implementatie is hier al op ingericht via MOLLIE_API_KEY.

Pas wanneer de volledige partnerflow correct werkt, vervang je de testkey door de live key.


1. Mollie-account aanmaken en verifiëren

Maak een zakelijk Mollie-account aan voor de onderneming waaronder ClassicSwedes valt.

Mollie kan onder andere vragen om:

  • Bedrijfsgegevens
  • Gegevens van wettelijke vertegenwoordigers/belanghebbenden
  • Identificatie
  • Bankrekening
  • Website
  • Omschrijving van je bedrijfsactiviteiten

Voor ClassicSwedes kun je de activiteit bijvoorbeeld omschrijven als:

ClassicSwedes is an online community and archive for classic and youngtimer Volvos. Businesses can purchase an annual partner listing to promote relevant products and services to Volvo enthusiasts.

Hiermee maak je duidelijk dat ClassicSwedes zelf de partnervermelding verkoopt en geen marktplaats voor auto's is.


2. ClassicSwedes als website instellen

Gebruik als website:

https://www.classicswedes.com

Voor de omschrijving kun je bijvoorbeeld gebruiken:

ClassicSwedes is een online community en archief voor klassieke Volvo’s en youngtimers. Bedrijven kunnen een jaarlijkse partnervermelding aanschaffen om relevante producten en diensten onder de aandacht te brengen bij Volvo-liefhebbers.

De term annual partner listing is hier handig, omdat duidelijk wordt dat bedrijven betalen voor een jaarlijkse partnervermelding.

Zorg voordat je livebetalingen activeert dat op ClassicSwedes duidelijk zichtbaar is:

  • Wat een partnerschap inhoudt
  • Wat een partner krijgt
  • Wat het kost
  • Hoe lang het partnerschap duurt
  • Wie achter ClassicSwedes zit
  • Hoe een bedrijf contact kan opnemen

3. Betaalmethoden activeren

Activeer in Mollie de betaalmethoden die je voor partners wilt aanbieden.

Voor ClassicSwedes zou ik beginnen met:

iDEAL

Belangrijk voor Nederlandse bedrijven.

Creditcard

Bijvoorbeeld Visa en Mastercard. Vooral handig wanneer ClassicSwedes meer Europese partners krijgt.

Bancontact

Interessant voor Belgische partners.

Eventueel kun je later SEPA-overboeking toevoegen.

Voor de eerste versie zou ik het eenvoudig houden:

iDEAL + creditcard + Bancontact


4. Test API-key aanmaken

Ga in Mollie naar het gedeelte voor developers/API-toegang.

Maak een Standard API key aan voor ClassicSwedes en kies:

Test mode

Je krijgt vervolgens een sleutel die ongeveer als volgt begint:

text
test_...

Bewaar deze key veilig.

De key mag nooit in:

  • GitHub
  • Client-side JavaScript
  • Een publieke .env
  • Een NEXT_PUBLIC_... variabele

terechtkomen.


5. Mollie Test API-key in Vercel zetten

Ga naar het ClassicSwedes-project in Vercel.

Ga naar:

Settings → Environment Variables

Maak de volgende environment variable aan:

text
MOLLIE_API_KEY

De waarde is je Mollie Test API-key:

text
MOLLIE_API_KEY=test_xxxxxxxxxxxxxxxxx

Daarnaast gebruikt de partnerimplementatie:

text
PARTNER_SITE_URL=https://www.classicswedes.com

De Mollie API-key moet uitsluitend server-side beschikbaar zijn.

Gebruik daarom nadrukkelijk niet:

text
NEXT_PUBLIC_MOLLIE_API_KEY

6. Webhook

De partnerimplementatie gebruikt:

text
/api/partners/webhook

In productie wordt dit:

text
https://www.classicswedes.com/api/partners/webhook

Deze URL moet vanaf het internet rechtstreeks bereikbaar zijn voor Mollie.

Je hoeft voor deze implementatie niet handmatig één algemene webhook in het Mollie-dashboard in te stellen.

Wanneer ClassicSwedes via de Mollie API een betaling maakt, wordt de webhook-URL bij die betaling opgegeven.

De flow wordt daarmee:

text
ClassicSwedes

Mollie-betaling aanmaken

webhookUrl meegeven

Klant betaalt

Mollie roept webhook aan

/api/partners/webhook

ClassicSwedes vraagt betaling opnieuw op bij Mollie

Betaling controleren

Partner activeren

De webhook controleert onder andere:

  • Currency
  • Amount
  • Order metadata
  • Payment ID

Daarmee vertrouwt ClassicSwedes niet alleen op de binnenkomende webhookmelding, maar controleert het de betaling opnieuw rechtstreeks bij Mollie.


7. Eerste testpartner aanmaken

Maak via ClassicSwedes een fictieve partneraanvraag aan:

text
/partners/join

Bijvoorbeeld:

text
Company: Test Volvo Garage
Website: https://example.com

Vul de aanvraag volledig in.


8. Partner als admin beoordelen

Log vervolgens in met een gebruiker waarvoor geldt:

text
profiles.role = admin

Ga naar:

text
/admin/partners

Controleer:

  • Bedrijfsnaam
  • Website
  • Omschrijving
  • Diensten
  • Volvo-modellen
  • Factuurgegevens
  • Pakket
  • Bedrag
  • BTW

Keur daarna de aanvraag goed.


Na goedkeuring kan voor de aanvraag een bestelling worden aangemaakt.

ClassicSwedes genereert vervolgens een betaalpagina.

Stuur deze pagina normaal gesproken naar de partner.

Bijvoorbeeld conceptueel:

text
https://www.classicswedes.com/partners/payment/ORDER-ID

Alleen het openen van deze pagina maakt nog geen Mollie-betaling aan.

De daadwerkelijke Mollie-betaling wordt pas gemaakt wanneer op de betaalknop wordt geklikt.


10. Testbetaling uitvoeren

Omdat je gebruikt:

text
MOLLIE_API_KEY=test_...

wordt er geen echte betaling uitgevoerd.

Mollie toont een testomgeving waarin verschillende betalingsresultaten kunnen worden gesimuleerd.

Test minimaal:

Succesvolle betaling

Simuleer:

text
Paid

Controleer daarna in Supabase of bijvoorbeeld het volgende correct is verwerkt:

text
payment_status = paid
status = active
paid_until = +12 maanden

Controleer vervolgens of het bedrijf zichtbaar wordt op:

text
/partners

11. Geannuleerde betaling testen

Voer opnieuw een testbetaling uit en simuleer:

text
Canceled

Controleer dat:

  • De partner niet wordt gepubliceerd
  • payment_status niet op paid komt
  • paid_until niet met 12 maanden wordt verlengd

12. Mislukte betaling testen

Test ook:

text
Failed

Ook hierbij mag de partner niet actief worden.

De order kan vervolgens in admin worden geannuleerd.

Daarna kan een nieuwe betaalmogelijkheid worden gegenereerd.


13. Betaling controleren in Mollie

Open het Mollie-dashboard en controleer de testtransactie.

Je zou bijvoorbeeld iets kunnen zien als:

text
Test Volvo Garage
€99,00
Paid

Controleer dat het bedrag exact overeenkomt met het bedrag in:

text
partner_orders

Controleer ook:

  • Currency
  • Payment status
  • Payment ID
  • Ordergegevens

14. Webhook opnieuw uitvoeren

Een belangrijke test is het meerdere keren verwerken van dezelfde webhook.

Stel:

text
paid_until = 11-09-2026

Na de eerste succesvolle betaling:

text
paid_until = 11-09-2027

Als dezelfde Mollie-webhook opnieuw wordt verwerkt, moet het resultaat nog steeds zijn:

text
paid_until = 11-09-2027

en nadrukkelijk niet:

text
paid_until = 11-09-2028

De databasefunctie:

text
apply_partner_payment

hoort dit transactioneel te voorkomen.

Hiermee voorkom je dat één betaling per ongeluk meerdere gratis jaren oplevert.


15. Verlenging testen

Test vervolgens een bestaande partner waarvan het partnerschap bijna verloopt.

Bijvoorbeeld:

text
paid_until = 25-09-2026

Ga naar:

text
/admin/partners

Maak een nieuwe renewal order.

Betaal deze via Mollie Test Mode.

Stel:

text
Bestaande paid_until:
25-09-2026

Nieuwe betaling:
11-09-2026

Dan moet de nieuwe einddatum worden:

text
25-09-2027

en niet:

text
11-09-2027

Een verlenging gaat dus verder vanaf de bestaande einddatum als die datum later ligt dan de betaaldatum.


16. Founding discount testen

Als je bijvoorbeeld de eerste 20 partners korting geeft, test dan ook deze situatie.

De instellingen staan in:

text
partner_settings.founding_enabled
partner_settings.founding_limit

Bijvoorbeeld:

text
founding_enabled = true
founding_limit = 20

Een plek wordt gereserveerd wanneer een aanvraag wordt goedgekeurd.

Een geannuleerde order moet de plek weer vrijgeven.

Controleer ook dat founding-korting alleen geldt voor:

text
eerste betaalde jaar

en niet voor verlengingen.


17. Gratis partner testen

Je kunt een bedrijf ook handmatig een gratis jaar geven.

Daarvoor kunnen in Supabase bijvoorbeeld de volgende waarden worden ingesteld:

text
approved_at = ...
status = active
payment_status = waived
paid_until = toekomstige datum

Zo kun je bijvoorbeeld een relevante Volvo-specialist uitnodigen als lanceringspartner.

Een gratis jaar telt niet als een betaald founding-jaar.


18. Refund testen

Refunds worden in deze eerste versie bewust niet volledig geautomatiseerd.

Als een partner zijn geld terugkrijgt:

  1. Zoek de betaling op in Mollie.
  2. Voer daar de refund uit.
  3. Controleer de partner in ClassicSwedes.
  4. Deactiveer de partner indien nodig handmatig via admin.

Automatische refund-accounting en factuurgeneratie vallen buiten deze eerste versie.


19. Controleren of verlopen partners verdwijnen

Test ook:

text
paid_until = datum in het verleden

De partner mag daarna niet meer publiek zichtbaar zijn.

Dit geldt zelfs wanneer:

text
status = active

nog steeds in de database staat.

De publieke zichtbaarheid moet dus mede afhankelijk zijn van:

text
paid_until

20. Live API-key aanmaken

Pas wanneer alle bovenstaande tests correct werken, ga je over naar livebetalingen.

Maak in Mollie een:

Standard API key → Live

aan.

Deze begint ongeveer met:

text
live_...

Vervang vervolgens in Vercel:

text
MOLLIE_API_KEY=test_xxxxxxxxx

door:

text
MOLLIE_API_KEY=live_xxxxxxxxx

Deploy ClassicSwedes daarna opnieuw.

Vanaf dat moment worden echte betalingen verwerkt.


21. Eén echte end-to-endbetaling uitvoeren

Voer na livegang zelf één volledige echte betaling uit.

Test de complete keten:

text
/partners/join

Partneraanvraag

/admin/partners

Aanvraag controleren

BTW controleren

Goedkeuren

Order aanmaken

Betaalpagina

Mollie-betaling

iDEAL / creditcard / Bancontact

Betaling geslaagd

Mollie webhook

ClassicSwedes controleert betaling

partner_orders → paid

partner → active

paid_until → +12 maanden

Partner zichtbaar op /partners

Checklist voor livegang

Mollie

Vercel

ClassicSwedes

  • Publieke /partners

Belangrijkste volgorde

De praktische volgorde is uiteindelijk:

text
1. Mollie-account afronden
2. ClassicSwedes als website instellen
3. Betaalmethoden activeren
4. Test API-key aanmaken
5. MOLLIE_API_KEY in Vercel zetten
6. Partneraanvraag indienen
7. Aanvraag in admin goedkeuren
8. Testbetaling uitvoeren
9. Webhook controleren
10. Database controleren
11. Partnerpagina controleren
12. Annulering/failure testen
13. Verlenging testen
14. Live API-key aanmaken
15. Live deploy uitvoeren
16. Eén echte end-to-endbetaling testen

TESTING

local test

Start je app met:

npm run dev

Daarna kun je een open partner-payment link lokaal openen/gebruiken. Omdat NODE_ENV === "development" bij next dev, zal de checkout- route geen Mollie-call doen maar direct lokaal simuleren en dezelfde apply_partner_payment finalization gebruiken als productie.

Belangrijk om te checken na betaling:

  • partner_orders.status wordt paid

  • partner_orders.paid_at en applied_at zijn gevuld

  • partners.status wordt active

  • partners.payment_status wordt paid

  • partners.paid_until staat 12 maanden vooruit

  • eventuele founding_partner logica wordt meegenomen

STAGING TESTING

Dus: ja, committen en pushen naar staging is prima. Test daar dan vooral:

  • partneraanvraag indienen
  • logo upload
  • admin ontvangt e-mail
  • partner ontvangt bevestigingsmail
  • aanvraag verschijnt in admin
  • approve maakt betaallink
  • Mollie testbetaling rondt af
  • partner verschijnt publiek na betaling

Werking nderdeel Partner/join

• “Relevante Volvo-modellen” is een filter/tag-systeem voor partners.

Bij het join-formulier kiest een partner voor welke Volvo-modellen zijn bedrijf relevant is. Bijvoorbeeld: een Amazon-specialist kiest Amazon / 120-serie, een onderdelenbedrijf voor bredere klassiekers kan meerdere modellen aanvinken.

Die selectie wordt op drie plekken gebruikt:

  1. In de partnerdirectory Bezoekers kunnen filteren op Volvo-model. Als iemand filtert op 240, dan verschijnen alleen partners waarbij 240 is aangevinkt.

  2. Op modelpagina’s Featured partners kunnen automatisch verschijnen bij relevante modelpagina’s. Bijvoorbeeld een partner met P1800 / 1800S / 1800ES kan op de P1800-pagina getoond worden.

  3. Op de partnerkaart Bij een partner wordt getoond voor welke Volvo-modellen die relevant is.

De huidige modellenlijst komt uit app/components/model-options.ts:1. Het zijn nu onder andere:

  • 200/300-serie: 240, 260, 262C Bertone, Volvo 66, 340 / 360
  • 400-serie
  • 700-serie
  • 800/900-serie
  • S/V-serie eerste generatie
  • C70
  • Klassiekers zoals PV, Duett, Amazon, P1800, 140-serie, 164

Dus kort gezegd: dit bepaalt “waar mag deze partner gevonden of getoond worden?” Niet zozeer wat hij exact verkoopt, maar voor welke modellen hij relevant is.

Werking factuur pdf

Wat er nu gebeurt bij een goedgekeurde betaling:

  1. De partner krijgt de "je vermelding staat live"-mail (zoals eerder gebouwd).
  2. Er wordt automatisch een PDF-factuur gegenereerd en als bijlage meegestuurd, met een doorlopend, uniek factuurnummer (CS-000001, CS-000002, ...).

Factuurinhoud: Fillingscreens.nl / Talmastraat 75, 1182 EL Amstelveen / KvK 97284556, met de vermelding "Vrijgesteld van btw — kleineondernemersregeling (KOR)" (geen btw-nummer, geen btw-bedrag, zoals je aangaf). Verder: factuurnummer, datum, omschrijving (Partner/Featured Partner, met of zonder introductietarief), bedrag, en de tax_note die de admin per order invult.

Technisch:

  • Nieuwe dependency @react-pdf/renderer toegevoegd (package.json) — genereert de PDF puur in Node, geen headless browser nodig, dus prima geschikt voor Vercel.
  • Nieuw bestand lib/partner-invoice.tsx met het factuursjabloon.
  • partner_orders heeft een nieuwe kolom invoice_number; de bestaande 3 testbetalingen zijn met terugwerkende kracht genummerd (CS-000001 t/m CS-000003).
  • Alle 3 migraties zijn toegepast én geverifieerd op de live database, en staan ook als bestand in supabase/migrations/.

Let op: het admin-paneel staat toe om per order handmatig een btw-percentage in te vullen — onder de KOR hoort dat altijd 0% te zijn.

Werking Gratis periode verstrekken

Voor een platform in jouw fase vind ik het een sterke zet — mits je het als lanceringstactiek inzet en niet als open einde.

Het grootste probleem van een nieuw platform is het kip-ei-verhaal: een lege partnersectie ziet er dood uit, en dan wil niemand €199 betalen om als enige op een verlaten pagina te staan. Gratis vermeldingen lossen dat op. Ze vullen de pagina, geven sociale bewijskracht, en maken je betaalde pitch dáárna véél makkelijker — "sluit je aan bij deze 30 bedrijven" verkoopt zichzelf. De marginale kosten van een extra vermelding zijn bovendien bijna nul.

Maar er zitten twee reële risico's aan:

Anker-effect. Als je het weggeeft, wordt de referentieprijs in het hoofd van de ondernemer €0. Volgend jaar €199 vragen voelt dan als een prijsverhoging in plaats van een normale prijs. Dat vang je op door het te framen als wat het waard is: "founding partner — normaal €199, gratis in het eerste jaar." De waarde staat er dan, je geeft alleen tijdelijk korting.

De verlenging is alles. "Gratis, daarna betalen" valt of staat bij of je hard kunt maken dat het iets oplevert. Meet daarom vanaf dag één de clicks/doorverwijzingen per partner, zodat je bij de verlenging kunt zeggen "je vermelding leverde X bezoekers op" in plaats van te hopen op goodwill.

Een paar dingen die ik zou overwegen:

Maak het schaars en tijdgebonden. "De eerste 25 partners" of "founding partners tot [datum]" — dat verhoogt de waardebeleving én voorkomt dat het een permanent gratis-loket wordt.
Wees selectief. Gratis trekt ook mensen die het niet echt waarderen en hun vermelding nooit bijwerken. Nodig liever gericht de bedrijven uit die je pagina geloofwaardig maken (bekende specialisten, mooie namen).
Pas op met je twee mailsets tegelijk. Als je gratis én betaald tegelijk uitstuurt naar overlappende doelgroepen, en een betalende prospect hoort dat een collega het gratis kreeg, heb je een probleem. Segmenteer: gratis voor de seed-/ankercohort nu, betaald zodra er bewijs op de pagina staat.

Kort door de bocht: nu wél gratis om het vliegwiel op gang te krijgen, met een duidelijke einddatum en een verlengingsplan. Zodra de pagina vol en levendig is, schakel je over op de betaalde werving als standaard. Het is uiteindelijk jouw inschatting hoe snel je die omslag aandurft, maar de volgorde "eerst vullen, dan verkopen" is bij dit soort platforms bijna altijd de juiste.


**Proefeperiode van een jaar reeel?**
Een jaar is verdedigbaar, maar ik denk dat het aan de lange kant zit — en dat de lengte belangrijker is dan hij lijkt. De vraag achter je vraag is eigenlijk: hoe lang wil je wachten op het moment dat je gaat verdienen én op het bewijs of dit werkt?

Een paar overwegingen:

Een jaar is traag om van te leren. Als je pas na twaalf maanden bij de verlenging hoort of partners willen betalen, heb je een heel jaar aannames gestapeld voordat je weet of het model klopt. Kortere periodes geven je eerder een eerlijk signaal. Zes maanden vertelt je al veel over of een partner z'n vermelding bijwerkt, of ie clicks krijgt, en of ie überhaupt reageert als je terugkomt.

Maar te kort ondermijnt het cadeau. Drie maanden voelt als een proefabonnement, niet als een echt gebaar — en het geeft de partner nauwelijks tijd om resultaat te zien voordat je alweer met een factuur aan de deur staat. Voor een seizoensgebonden hobbywereld als klassieke auto's is dat ook krap: het rij- en klussenseizoen loopt grofweg van lente tot najaar, dus je wilt dat een partner minstens één volledig seizoen meemaakt.

De verlenging valt beter op een logisch moment. Als je nu (najaar-ish) gratis jaren weggeeft, komen al je verlengingen precies een jaar later tegelijk terug — mogelijk in een dood seizoen, en allemaal op één hoop. Dat is druk en ongunstig getimed. Kortere of op het seizoen afgestemde periodes spreiden dat.

Waar ik naartoe zou neigen:

Founding partners: gratis t/m een vaste einddatum, bijvoorbeeld "gratis tot eind [volgend seizoen]" in plaats van "een jaar vanaf je aanmelding". Dan lopen alle gratis periodes op hetzelfde, door jou gekozen moment af — makkelijker te beheren en je kunt het laten samenvallen met een piek in verkeer.
Overweeg 6–9 maanden als je sneller wilt leren, mét de framing dat het één volledig seizoen dekt.
Houd één jaar aan als je bewust kiest voor maximale goodwill en het opbouwen van een zo vol mogelijke pagina belangrijker vindt dan snel valideren — dat is een legitieme keuze, zeker als je nog echt in de vul-fase zit.

Kort gezegd: een jaar is niet fout, maar "gratis tot een vaste datum" is bijna altijd handiger dan "een jaar vanaf aanmelding", en iets korter dan een jaar levert je sneller de informatie op waar je straks je hele betaalde model op baseert. Het hangt er vooral van af of je nu meer waarde hecht aan vullen (dan langer) of aan valideren (dan korter).


**Exclusiviteit**
Exclusiviteit is je grootste troef — gebruik hem expliciet. "Ik benader een klein aantal bedrijven dat volgens ons echt bij de community past, en jullie horen daarbij" is veel overtuigender dan een massamail. Het is een compliment, geen aanbieding. Zorg dat dat gevoel in je mail en telefoongesprek zit. De eerdere mailteksten die ik maakte zijn eigenlijk te veel "campagne" voor dit doel — voor een geselecteerde lijst wil je persoonlijker, korter, en met de naam van hun bedrijf/specialisme erin.

Het ankerprobleem verdwijnt grotendeels. Omdat het een persoonlijk gebaar is ("wij nodigen jóú uit") in plaats van een publieke prijs van €0, kun je bij de verlenging veel makkelijker zeggen "de introductieperiode loopt af, het reguliere tarief is €199". Het was immers nooit "gratis voor iedereen", het was een uitnodiging aan hen.

Framing die ik zou aanhouden bij het bellen/mailen:

Benoem waaróm juist zij — iets specifieks over hun bedrijf, niet generiek. Dat bewijst dat het geen bulkactie is.
Noem de reguliere waarde (€199/jaar) zodat het gebaar telt, en dat je het hen kosteloos aanbiedt.
Wees eerlijk dat het een select gezelschap is en tijdelijk — dat maakt "ja" makkelijker.
Houd de vervolgstap laagdrempelig: bij een telefoontje kun je aanbieden het zelf voor ze klaar te zetten, in plaats van ze naar het aanmeldformulier te sturen.

Eén ding om vooraf voor jezelf helder te hebben, want dit gaat je later helpen: waaróm bied je het juist deze bedrijven aan? Grofweg twee doelen, met elk andere consequenties:

Anker/geloofwaardigheid — je wilt bekende, mooie namen op de pagina zodat de rest zich aansluit. Dan selecteer je op reputatie en zichtbaarheid.
Betalende klanten kweken — je wilt dat deze bedrijven na afloop verlengen. Dan selecteer je op wie het budget en de motivatie heeft om te betalen, en meet je vanaf dag één wat het ze oplevert.

Vaak wil je beide, maar het helpt om te weten welke zwaarder weegt, want dat bepaalt wie je belt en wat je straks bij de verlenging als succes ziet.

  Qua redelijkheid: 6 maanden is een prima proefperiode —
  lang genoeg om een heel seizoen/zomerdrukte mee te maken
  en resultaat (leads, contact) te laten zien, maar kort
  genoeg dat je niet een heel jaar misloopt als het weinig
  oplevert. Belangrijkste afweging: hoe selectiever je belt
  (kleine groep gewenste partners), hoe eerder je een jaar
  zou kunnen overwegen om ze echt over de streep te
  trekken — bij een bredere groep is 6 maanden veiliger.

Samenvatting

Voorstel: het schema had al een payment_status = 'waived' (kosteloos) concept voorbereid, maar er was nog geen manier om dit daadwerkelijk toe te kennen. Ik heb dat pad afgemaakt in plaats van iets nieuws te verzinnen.

Hoe je nu een gratis periode geeft:

  1. Ga naar het admin-partnerpaneel (/admin/partners).
  2. Bij de partner die je gratis wilt geven, vul je bij "Free months" het aantal maanden in (standaard 12) en klik je op "Grant free period (no payment)".
  3. De partner wordt meteen actief/live, krijgt een bevestigingsmail (geen factuur, want geen betaling), en de vermelding vervalt vanzelf zodra de periode voorbij is — er is geen aparte "opzeg"-stap nodig.

"Zonder verplichtingen" is technisch geborgd:

  • Geen partner_orders-rij, dus geen factuur, geen betaalverplichting.
    • auto_renew wordt automatisch op false gezet — geen automatische verlenging, geen herinneringsmail van de renewal-cron.
  • Na de einddatum verdwijnt de vermelding gewoon stil uit de bedrijvengids (de publieke view filtert al op paid_until > now()), zonder dat er iets afgeschreven wordt.
  • Kun je later de periode verlengen door de actie nogmaals uit te voeren — die telt op bij de bestaande paid_until.

Alle code getypecheckt/gelint zonder fouten, en de migratie is toegepast en geverifieerd op de live database.

  • Wat er verandert:
  • De cron (draait al dagelijks om 9:00 via vercel.json) haalt nu ook partners op met payment_status = 'waived' (gratis periode), ook al staat hun auto_renew op false. Voor betalende partners blijft auto_renew het bepalende opt-in-vlaggetje, zoals voorheen.
  • Ze krijgen ~30 dagen voor het verlopen van paid_until een aparte mail: "Je gratis ClassicSwedes-periode loopt bijna af", met de vraag of ze willen doorgaan als betalend partner (reply-to voor een betaallink), en de tekst dat er niks gebeurt als ze niet reageren — vermelding vervalt dan gewoon.
  • Betalende partners krijgen de bestaande verlengingsmail (nu ook in het Nederlands, consistent met de rest — was eerst Engels).
  • Dubbele verzending wordt al voorkomen door het bestaande renewal_reminded_until-mechanisme (per partner, per paid_until-waarde maar één keer).

Werking automtsiche mailtjes

admin-notificatie blijft Nederlands (intern), aanvrager-mail is per taal. Alles is getypecheckt en gelint zonder errors. Samenvatting:

lib/partner-email.ts — partnerLang() herkent nu alle 8 sitetalen (nl, en, de, fr, es, it, sv, pl), met partnerDateLocale() voor datumnotatie per taal, en een nieuwe buildPartnerEmail()-helper die de HTML- en tekstversie van een mail opbouwt uit vertaalde paragrafen (voorkomt duplicatie van opmaak/groet/ondertekening per taal).

lib/partner-payments.ts — alle vier partnermails (live, gratis periode, afwijzing, betaallink) hebben nu volledige vertalingen in alle 8 talen.

app/api/partners/apply/route.ts — de bevestigingsmail naar de aanvrager is nu in alle 8 talen; de interne meldingsmail aan het ClassicSwedes-team blijft bewust Nederlands.

app/api/partners/renewal-reminders/route.ts — de automatische verlengingsherinnering (cron, zowel gratis- als betaalde variant) is nu ook in alle 8 talen, inclusief lokale datumnotatie.

De taal wordt overal automatisch bepaald door de locale-kolom die al bij aanmelding wordt opgeslagen — geen verdere actie nodig. Onbekende/oude partners zonder locale vallen terug op Nederlands.