Skip to main content

Bilko — Contact Form + Pricing Restructure (2026-07-14)

Documents two shipped-and-live-verified changes deployed to demo (app.bilko.cloud) on 2026-07-14. Written for the ZAKON DOC gate covering MC #105588 and MC #105589.

MC #105588 — In-app contact form (RESOLVED, live)

Problem

The "Kontaktiraj nas" CTA (shown on contact-only tiers) executed window.location.href='mailto:[email protected]'. This was broken in practice: it requires a configured mail client on the visitor's machine, and it pointed at a .io domain that looks suspicious to a Balkan SMB user rather than the product's own domain.

Fix

  • New backend endpoint: POST /api/v1/leads — public, rate-limited to 5 requests/min/IP via trusted-IP keying, closed tier enum (no arbitrary string injection), no PII echoed back in the response.
  • Lead emails are sent via Resend (note: the repo uses Resend, not SendGrid) to LEADS_NOTIFICATION_EMAIL, which defaults to [email protected].
  • New shared frontend component, LeadContactForm, used as a modal from both ChangePlanDrawer and the public /pricing page — so both contact-only tiers (Računovođa, Enterprise) route through the same in-app form instead of a mailto link.

Security review

Parisa Tabriz (Securion) reviewed the public lead endpoint: PASS WITH FOLLOW-UPS. Hardening follow-ups tracked as MC #105592: HTML-escape the email body, escape the C0 control-character range in JSON output, add a RequestBodyLimit, and add a global request cap.

Commits / PR

  • Backend: f15dee26
  • Frontend: 07ee9a9b
  • PR144 → merged as 2ce713e2

Verification (Angie Jones / Proveo, live UAT)

PASS — clicking "Kontaktiraj nas" opens an in-app modal titled "Kontaktirajte nas" with Ime / Email / Poruka fields. No mailto: navigation occurs. Evidence captured at ~/system/evidence/deploy-497-uat/, notably 06-after-kontakt-click.png (plus 05-before-kontakt-click.png, 07-modal-closeup.png, 06-modal-text.txt, and the run's uat-report.json).


MC #105589 — Pricing restructure: base + add-ons spine (RESOLVED, live)

Decision

CEO decision (2026-07-14), adversarially reviewed by Markos Zachariadis (Finverge) — ENDORSE WITH CHANGES. Bilko had two overlapping pricing models running in parallel: a tier-bundle model and an add-on module model. This created two competing paths to full bookkeeping — the Računovođa tier at €39, versus Biznis (€19) + FULL add-on (€18) = €37 — which cannibalized each other. The decision collapses this into one base + add-ons spine.

Changes shipped

  • Removed the Računovođa tier everywhere in the UI (pricing-matrix.json / TIER_UI / ChangePlanDrawer).
  • Full bookkeeping is now available only via the ACCOUNTING_FULL_ADDON at €18.
  • Multi-org cockpit is now bundled free with the FULL add-on (multiOrg is derived from accountingFull) — consistent with the 2026-06-25 multi-org pricing decision and the Fiken-inspired model. Verified live via the Testcontainers MeCapabilitiesRoutesTest (multiOrg=true on ENTERPRISE).
  • FULL grants an accountant-sized e-invoice quota (~300).
  • Added a "vs Pantheon €150+" competitive anchor line on the FULL add-on card.
  • HR pricing: PDV running total including 25% shown on /pretplata, plus "cijena bez PDV-a (25%)" labels on /pricing.
  • Kept the BIZNIS bundle tier as-is.
  • Enterprise remains the sole contact-only tier.
  • Added graceful fallback handling for unknown/legacy planTier values.

Explicitly out of scope

No Stripe price mutations. Tier prices in Stripe remain placeholders pending explicit CEO confirmation of final numbers before any Stripe price create call.

Blast-radius check

Live enumeration before removal: 0 organizations on ENTERPRISE or RACUNOVODJA tiers (PRO=5, BASIC=2, PREMIUM=1). Removing the Računovođa tier breaks no live customer.

Commits / PR

  • eb3b81ef + fd439b0e (cleanup)
  • PR145 → merged as 21ad3534

Verification (Angie Jones / Proveo, live UAT)

PASS — live tiers are Paušalac €9 / Biznis €19 / Enterprise (contact); no Računovođa tier present; FULL add-on shown at €18 with the Pantheon comparison line; PDV labels present as specified. Evidence at ~/system/evidence/deploy-497-uat/ (02-public-pricing-page.png/.txt, 03-pretplata-page.png/.txt, 08-pretplata-addons-scrolled.png, 09-addon-add-button.png).

Follow-up

MC #105614 — two stale "Računovođa" copy strings remain (Modul B CTA + /pricing sidebar) and need a copy cleanup pass.


Deploy note

Both changes were deployed to demo via the canonical azdo pipeline Promote_Demo stage — self-hosted agent, pipeline service connection, not a manual personal az deploy (per ZAKON PI2). Build 497 (manual trigger, commit 21ad3534), approved via PAT, Promote_Demo succeeded, and app.bilko.cloud returned HTTP 200 post-deploy.

Evidence index

ItemLocation
Live UAT screenshots + reports (both MCs)~/system/evidence/deploy-497-uat/
Contact-click before/after05-before-kontakt-click.png, 06-after-kontakt-click.png, 06-modal-text.txt, 07-modal-closeup.png
Public pricing page02-public-pricing-page.png/.txt
Subscription (/pretplata) page03-pretplata-page.png/.txt, 08-pretplata-addons-scrolled.png, 09-addon-add-button.png
Change-plan drawer04-change-plan-drawer.png/.txt
UAT structured reportuat-report.json
  • #105588 — In-app contact form (this doc)
  • #105589 — Pricing restructure base+add-ons (this doc)
  • #105592 — Lead endpoint hardening follow-up (Parisa)
  • #105614 — Stale "Računovođa" copy cleanup follow-up