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 bothChangePlanDrawerand the public/pricingpage — 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_ADDONat €18. - Multi-org cockpit is now bundled free with the FULL add-on (
multiOrgis derived fromaccountingFull) — consistent with the 2026-06-25 multi-org pricing decision and the Fiken-inspired model. Verified live via the TestcontainersMeCapabilitiesRoutesTest(multiOrg=trueon 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
planTiervalues.
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
| Item | Location |
|---|---|
| Live UAT screenshots + reports (both MCs) | ~/system/evidence/deploy-497-uat/ |
| Contact-click before/after | 05-before-kontakt-click.png, 06-after-kontakt-click.png, 06-modal-text.txt, 07-modal-closeup.png |
| Public pricing page | 02-public-pricing-page.png/.txt |
| Subscription (/pretplata) page | 03-pretplata-page.png/.txt, 08-pretplata-addons-scrolled.png, 09-addon-add-button.png |
| Change-plan drawer | 04-change-plan-drawer.png/.txt |
| UAT structured report | uat-report.json |
Related MC tasks
- #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
No comments to display
No comments to display