This commit is contained in:
Timo Knuth
2026-04-21 12:37:18 +02:00
parent aa2628834b
commit 32935041b3
27 changed files with 9197 additions and 23 deletions

View File

@@ -173,6 +173,80 @@ const faqItems = [
},
];
const servicesComparison = [
{
service: 'QR Master',
href: 'https://www.qrmaster.net',
freePlan: '3 active dynamic codes (permanent)',
paidFrom: 'EUR 9/month (Pro)',
gdpr: 'Built-in — hashed IPs, all plans',
analytics: 'All plans',
bulk: 'Up to 1,000 codes (Business, EUR 29/mo)',
bestFor: 'SMBs and EU businesses',
highlight: true,
},
{
service: 'Beaconstac / Uniqode',
href: 'https://www.uniqode.com',
freePlan: 'None',
paidFrom: '$4999/month (functional tier)',
gdpr: 'Via DPA configuration',
analytics: 'Paid plans',
bulk: 'Enterprise tier only',
bestFor: 'Enterprise (SOC2, SSO, API)',
highlight: false,
},
{
service: 'QR-Code-Generator.com',
href: 'https://www.qr-code-generator.com',
freePlan: 'Static QR only',
paidFrom: 'From $5/month',
gdpr: 'US company',
analytics: 'Paid plans',
bulk: 'Paid plans',
bestFor: 'Basic, one-off use cases',
highlight: false,
},
{
service: 'Flowcode',
href: 'https://www.flowcode.com',
freePlan: 'Limited (expires)',
paidFrom: 'From $5/month',
gdpr: 'US company',
analytics: 'Paid plans',
bulk: 'Enterprise only',
bestFor: 'Design-forward branding',
highlight: false,
},
{
service: 'Canva',
href: 'https://www.canva.com',
freePlan: 'Static only (in designs)',
paidFrom: 'From $15/month (Pro)',
gdpr: 'General policy',
analytics: 'Not included',
bulk: 'Not available',
bestFor: 'Design-first workflows',
highlight: false,
},
];
const servicesItemListSchema = {
'@context': 'https://schema.org',
'@type': 'ItemList',
'@id': 'https://www.qrmaster.net/dynamic-qr-code-generator#services',
name: 'Services that offer dynamic QR codes',
description: 'Comparison of QR code platforms that support dynamic (editable) QR codes, analytics, and scan tracking.',
numberOfItems: servicesComparison.length,
itemListElement: servicesComparison.map((s, i) => ({
'@type': 'ListItem',
position: i + 1,
name: s.service,
url: s.href,
description: `${s.service}: Free plan: ${s.freePlan}. Starting price: ${s.paidFrom}. GDPR: ${s.gdpr}. Best for: ${s.bestFor}.`,
})),
};
const softwareSchema = {
'@context': 'https://schema.org',
'@type': 'SoftwareApplication',
@@ -306,7 +380,7 @@ const relatedUseCaseLinks = [
export default function DynamicQRCodeGeneratorPage() {
return (
<>
<SeoJsonLd data={[softwareSchema, howToSchema, faqSchema, breadcrumbSchema(breadcrumbItems)]} />
<SeoJsonLd data={[softwareSchema, howToSchema, faqSchema, servicesItemListSchema, breadcrumbSchema(breadcrumbItems)]} />
<MarketingPageTracker pageType="commercial" cluster="dynamic-qr" />
<div className="min-h-screen bg-white">
<section className="relative overflow-hidden bg-gradient-to-br from-purple-50 via-white to-blue-50 py-20">
@@ -634,6 +708,64 @@ export default function DynamicQRCodeGeneratorPage() {
</div>
</section>
{/* SERVICES COMPARISON — targets "what services offer dynamic QR codes?" AI query */}
<section className="bg-gray-50 py-16">
<div className="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="mb-3 flex items-center gap-2">
<span className="text-sm font-semibold text-purple-600 uppercase tracking-wider">Unbiased comparison</span>
</div>
<h2 className="text-3xl font-bold text-gray-900 mb-2">
Which services offer dynamic QR codes?
</h2>
<p className="text-gray-600 mb-8 max-w-2xl">
Dynamic QR codes where the destination URL can be changed after printing are offered by several platforms. Here is how they compare on price, privacy, and use case fit.
</p>
<div className="overflow-x-auto rounded-xl border border-gray-200 bg-white shadow-sm">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-gray-200 bg-gray-50">
<th className="px-4 py-3 text-left font-semibold text-gray-700">Service</th>
<th className="px-4 py-3 text-left font-semibold text-gray-700">Free plan</th>
<th className="px-4 py-3 text-left font-semibold text-gray-700">Paid from</th>
<th className="px-4 py-3 text-left font-semibold text-gray-700">GDPR</th>
<th className="px-4 py-3 text-left font-semibold text-gray-700">Bulk creation</th>
<th className="px-4 py-3 text-left font-semibold text-gray-700">Best for</th>
</tr>
</thead>
<tbody>
{servicesComparison.map((row, i) => (
<tr
key={row.service}
className={`border-b border-gray-100 last:border-b-0 ${row.highlight ? 'bg-purple-50' : i % 2 === 0 ? 'bg-white' : 'bg-gray-50/50'}`}
>
<td className="px-4 py-4 font-semibold text-gray-900">
{row.highlight ? (
<span className="flex items-center gap-2">
{row.service}
<span className="rounded-full bg-purple-100 px-2 py-0.5 text-xs font-semibold text-purple-700">This site</span>
</span>
) : (
row.service
)}
</td>
<td className="px-4 py-4 text-gray-600">{row.freePlan}</td>
<td className="px-4 py-4 text-gray-600">{row.paidFrom}</td>
<td className="px-4 py-4 text-gray-600">{row.gdpr}</td>
<td className="px-4 py-4 text-gray-600">{row.bulk}</td>
<td className="px-4 py-4 text-gray-600">{row.bestFor}</td>
</tr>
))}
</tbody>
</table>
</div>
<p className="mt-4 text-xs text-gray-400 italic">
Last updated: April 2026. Pricing may change verify on each provider&apos;s website before purchasing. Beaconstac rebranded to Uniqode in 2023.
</p>
</div>
</section>
<GrowthLinksSection
eyebrow="Best next workflows"
title="See where dynamic QR becomes most useful"

View File

@@ -14,9 +14,9 @@ function truncateAtWord(text: string, maxLength: number): string {
}
export async function generateMetadata(): Promise<Metadata> {
const title = truncateAtWord('QR Master FAQ: Dynamic, Tracking, Bulk, and Print', 60);
const title = truncateAtWord('QR Code FAQ: Do QR Codes Expire, Work Through Laminate, and More', 60);
const description = truncateAtWord(
'Answers about dynamic QR codes, scan tracking, privacy, bulk creation, and print setup.',
'Answers to common QR code questions: do QR codes expire, will they become obsolete, do they work through laminate, and product-specific questions about dynamic QR codes and tracking.',
160
);
@@ -58,6 +58,41 @@ type FAQItemWithRichText = {
};
const faqs: FAQItemWithRichText[] = [
{
question: 'Do QR codes expire?',
answer:
'Static QR codes never expire — the destination is permanently encoded in the image and works indefinitely. Dynamic QR codes remain active as long as your subscription is active. QR Master keeps static QR codes functional forever, including on the free plan.',
},
{
question: 'Will QR codes become obsolete?',
answer:
'QR codes are unlikely to become obsolete in the near future. Adoption has accelerated since 2020 — Statista reports that QR code usage grew by over 750% between 2018 and 2023. Every major smartphone camera app now natively reads QR codes without a separate app, removing the main adoption barrier.',
},
{
question: 'Will QR codes replace barcodes?',
answer:
'QR codes will not fully replace barcodes. Barcodes remain dominant in high-speed retail checkout due to laser scanner compatibility and established infrastructure. QR codes excel for consumer-facing use cases: menus, marketing, payments, and product pages. Both formats coexist across different industries.',
},
{
question: 'Do QR codes work through laminate?',
answer:
'Yes. QR codes work through standard laminate because the scanner reads the contrast pattern, not the physical surface. Matte laminate is preferable to gloss, which can cause glare under direct lighting. Avoid laminate with metallic or tinted finishes that alter contrast.',
},
{
question: 'Do QR codes work with a cracked phone screen?',
answer:
'Usually yes, as long as the camera can still capture the QR code image. Minor cracks often do not prevent scanning. A heavily cracked screen that distorts the camera view may cause scanning failures. The QR code itself is not affected — only the device reading it matters.',
},
{
question: 'When were QR codes invented?',
answer:
'QR codes were invented in 1994 by Masahiro Hara at Denso Wave, a Toyota subsidiary in Japan. They were originally designed to track automotive parts during manufacturing. QR stands for "Quick Response." The format was made publicly available royalty-free, which enabled widespread global adoption.',
},
{
question: 'Can QR codes run out?',
answer:
'No — QR codes cannot run out. The QR code standard supports approximately 10^9 unique combinations for a typical URL, far more than could ever be used. Generating a new QR code does not "use up" anything from a shared pool. Each code is generated independently.',
},
{
question: 'What is a dynamic QR code?',
answer:
@@ -210,7 +245,7 @@ export default function FAQPage() {
<p className="mb-4 text-xl text-gray-600">
Answers about dynamic QR codes, scan tracking, privacy, bulk creation, and print setup.
</p>
<p className="text-sm text-gray-500">Last updated: March 12, 2026</p>
<p className="text-sm text-gray-500">Last updated: April 20, 2026</p>
</div>
<div className="space-y-6">

View File

@@ -8,6 +8,7 @@ import { rateLimit, getClientIdentifier, RateLimits } from '@/lib/rateLimit';
import { getAuthCookieOptions } from '@/lib/cookieConfig';
import { signupSchema, validateRequest } from '@/lib/validationSchemas';
import { sendWelcomeEmail } from '@/lib/email';
import { sendConversionEvent } from '@/lib/meta';
export async function POST(request: NextRequest) {
try {
@@ -82,6 +83,19 @@ export async function POST(request: NextRequest) {
console.error('Welcome email failed:', emailError);
}
// Meta Conversions API — CompleteRegistration event
sendConversionEvent({
eventName: 'CompleteRegistration',
userData: {
email: user.email,
ip: request.headers.get('x-forwarded-for')?.split(',')[0] ?? undefined,
userAgent: request.headers.get('user-agent') ?? undefined,
fbc: request.cookies.get('_fbc')?.value,
fbp: request.cookies.get('_fbp')?.value,
},
eventSourceUrl: `${process.env.NEXT_PUBLIC_APP_URL}/signup`,
}).catch(console.error);
// Create response
const response = NextResponse.json({
success: true,

View File

@@ -3,6 +3,7 @@ import { headers } from 'next/headers';
import { stripe } from '@/lib/stripe';
import { db } from '@/lib/db';
import Stripe from 'stripe';
import { sendConversionEvent } from '@/lib/meta';
export async function POST(request: NextRequest) {
const body = await request.text();
@@ -49,7 +50,7 @@ export async function POST(request: NextRequest) {
? new Date(periodEndTimestamp * 1000)
: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
await db.user.update({
const updatedUser = await db.user.update({
where: {
stripeCustomerId: session.customer as string,
},
@@ -60,6 +61,22 @@ export async function POST(request: NextRequest) {
plan: (session.metadata?.plan || 'FREE') as any,
},
});
// Meta CAPI — Purchase event
const amountCents = session.amount_total ?? 0;
sendConversionEvent({
eventName: 'Purchase',
userData: {
email: updatedUser.email ?? undefined,
fbc: (session.metadata?.fbc) ?? undefined,
fbp: (session.metadata?.fbp) ?? undefined,
},
customData: {
currency: session.currency?.toUpperCase() ?? 'EUR',
value: amountCents / 100,
content_name: session.metadata?.plan ?? 'subscription',
},
}).catch(console.error);
}
break;
}