#credit-info button {
text-shadow: none;
}
.pricing-container {
font-family: -apple-system, BlinkMacSystemFont, ‘Segoe UI’, Roboto,
Oxygen-Sans, Ubuntu, Cantarell, ‘Helvetica Neue’, sans-serif;
max-width: 800px;
margin: 0 auto;
}
.pricing-card {
background: #ffffff;
border-radius: 8px;
padding: 30px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
border: 1px solid #e0e7ff;
}
.pricing-title {
font-size: 1.4rem;
margin-bottom: 20px;
color: #2c3e50;
font-weight: 600;
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
}
.pricing-project {
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
border-left: 4px solid;
}
.pricing-paid {
background: rgba(39, 174, 96, 0.1);
border-color: #27ae60;
}
.pricing-unpaid {
background: rgba(231, 76, 60, 0.1);
border-color: #e74c3c;
}
.pricing-info {
margin: 8px 0;
line-height: 1.5;
}
.pricing-highlight {
color: #2980b9;
font-weight: bold;
}
.pricing-input {
padding: 10px;
border: 1px solid #bdc3c7;
border-radius: 4px;
font-size: 1rem;
text-align: center;
width: 100px;
}
.pricing-discount {
padding: 15px;
border-radius: 6px;
margin: 15px 0;
display: inline-block;
}
.pricing-price {
font-size: 1.3rem;
background: #f8f9fa;
padding: 15px;
border-radius: 8px;
margin: 15px 0;
border-left: 4px solid #3498db;
}
.pricing-button {
display: inline-block;
background: #2c3e50;
color: white;
padding: 12px 20px;
border-radius: 6px;
text-decoration: none !important;
margin: 10px 10px 10px 0;
transition: background 0.3s;
font-weight: bold;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
border: none;
}
.pricing-button:hover {
background: #1a252f;
color: white !important;
text-decoration: none !important;
}
.pricing-button:visited {
color: white !important;
}
.pricing-button img {
height: 24px;
vertical-align: middle;
margin-right: 8px;
}
.pricing-buttons-container {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin: 15px 0;
}
.pricing-secondary {
font-size: 0.9rem;
color: #7f8c8d;
}
.pricing-promo {
display: inline-block;
background: #2c3e50;
color: white !important;
font-weight: bold;
padding: 10px 15px;
border-radius: 6px;
margin: 15px 0;
text-decoration: none !important;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.pricing-promo:hover {
background: #1a252f;
color: white !important;
text-decoration: none !important;
}
.pricing-promo:visited {
color: white !important;
}
.addon-link {
display: inline-block;
background: #5e72e4;
color: white !important;
padding: 14px 24px;
border-radius: 8px;
text-decoration: none !important;
margin: 8px;
transition: all 0.3s ease;
font-weight: 600;
box-shadow: 0 4px 15px rgba(94, 114, 228, 0.3);
border: 2px solid transparent;
font-size: 0.95rem;
}
.addon-link:hover {
transform: translateY(-2px);
background: #4c63d2;
box-shadow: 0 6px 20px rgba(94, 114, 228, 0.4);
border-color: rgba(255, 255, 255, 0.3);
color: white !important;
text-decoration: none !important;
}
.addon-link:visited {
color: white !important;
}
.addon-links-container {
display: flex;
flex-wrap: wrap;
gap: 12px;
justify-content: center;
margin: 15px 0;
}
.pricing-range-section {
margin-bottom: 20px;
}
.attendee-input-container {
margin: 20px 0;
padding: 20px;
background: white;
border-radius: 8px;
border: 2px solid #3498db;
}
.attendee-slider {
width: 100%;
height: 8px;
border-radius: 4px;
background: #e0e7ff;
outline: none;
margin: 15px 0;
-webkit-appearance: none;
}
.attendee-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: #3498db;
cursor: pointer;
}
.attendee-slider::-moz-range-thumb {
width: 20px;
height: 20px;
border-radius: 50%;
background: #3498db;
cursor: pointer;
border: none;
}
.attendee-number-input {
width: 120px;
padding: 10px;
border: 2px solid #bdc3c7;
border-radius: 6px;
font-size: 1.1rem;
font-weight: bold;
text-align: center;
color: #2c3e50;
}
const sheetsAddonUrl =
‘https://workspace.google.com/marketplace/app/qr_code_ticket_per_row_for_checkin/9398047938?utm_source=blog&utm_medium=organic&utm_campaign=pricing-page’;
const formsAddonUrl =
‘https://workspace.google.com/marketplace/app/qr_code_ticket_per_response_for_event_at/1028329904752?utm_source=blog&utm_medium=organic&utm_campaign=pricing-page’;
const qs = new URLSearchParams(document.location.search);
function getStr(id) {
return qs.get(id) || ‘?’;
}
function getNum(id) {
return parseInt(qs.get(id), 10) || 0;
}
const defaultProvider = qs.get(‘provider’) || ‘paypal’;
// see Credits.tsx
const email = getStr(’email’);
const title = getStr(‘title’);
const procId = getStr(‘proc’);
const agentId = getStr(‘agent’);
const account = getStr(‘account’);
const procs = getNum(‘processes’);
const currentCases = getNum(‘currentCases’);
const procCreatedTs = getNum(‘procCreatedTs’);
const currentCheckIns = getNum(‘currentCheckIns’);
const currentIsPaid = getStr(‘currentIsPaid’) === ‘true’;
const bundleCheckInsLeft = getNum(‘bundleCheckInsLeft’);
const projectCheckInsLeft = getNum(‘projectCheckInsLeft’);
const projectUsesBundle = getStr(‘projectUsesBundle’) === ‘true’;
const checkInsLeft = projectUsesBundle
? bundleCheckInsLeft
: projectCheckInsLeft;
const discountCode = getStr(‘dc’);
const discountRate = Math.min(100, getNum(‘d’));
const ref = `${procId}_${agentId}`;
function getUrl(nbCases, theProvider) {
const amp = ‘u0026’; // wordpress encodes the litteral version
const eurAmount = getBaseCharge(nbCases);
const amount = (
theProvider === ‘polar’ ? eurToUsd(eurAmount) : eurAmount
).toFixed(2);
const provider = theProvider || defaultProvider;
return `https://backend.trak.codes/api/v0/addons/pay/${provider}/project?ref=${ref}${amp}title=${encodeURIComponent(
title
)}${amp}amount=${amount}${amp}nbCases=${nbCases}${amp}discount=${discountRate}${amp}discountCode=${discountCode}`;
}
function eurToUsd(eur) {
const rate = 1.09;
const paypalConversionFee = 0.045;
return eur * rate * (1 + paypalConversionFee);
}
async function populateCurrencySelect(selectId) {
const select = document.getElementById(selectId);
if (!select) return;
const prev = select.value;
try {
const res = await fetch(
‘https://backend.trak.codes/api/v0/pdf/fx-currencies’
);
if (!res.ok) return;
const data = await res.json();
const currencies = Array.isArray(data.currencies) ? data.currencies : [];
if (!currencies.length) return;
const normalized = currencies
.map((c) => String(c || ”).toUpperCase())
.filter((c) => /^[A-Z]{3}$/.test(c));
normalized.sort((a, b) => {
if (a === ‘EUR’) return -1;
if (b === ‘EUR’) return 1;
return a.localeCompare(b);
});
select.innerHTML = normalized
.map((c) => `${c}`)
.join(”);
if (currencies.includes(prev)) select.value = prev;
else if (currencies.includes(‘USD’)) select.value = ‘USD’;
else select.value = ‘EUR’;
} catch {
// keep the initial static list
}
}
function showProformaForm(ticketCount, finalPriceEur, discountRateVal) {
const existingForm = document.getElementById(‘proformaFormOverlay’);
if (existingForm) existingForm.remove();
const overlay = document.createElement(‘div’);
overlay.id = ‘proformaFormOverlay’;
overlay.style.cssText =
‘position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:1000;’;
overlay.innerHTML = `
๐ Proforma Invoice Details
Customer Name
Customer Email
Customer Address
Currency equivalent
EUR
USD
CAD
GBP
CHF
Shown in the generated PDF using current ECB rates.
Cancel
Get Invoice
`;
document.body.appendChild(overlay);
overlay.addEventListener(‘click’, (e) => {
if (e.target === overlay) overlay.remove();
});
populateCurrencySelect(‘proformaCurrency’);
document.getElementById(‘proformaForm’).onsubmit = function (e) {
e.preventDefault();
const params = new URLSearchParams({
customerName: document.getElementById(‘proformaName’).value,
customerEmail: document.getElementById(‘proformaEmail’).value,
customerAddress: document.getElementById(‘proformaAddress’).value,
ticketCount: String(ticketCount),
finalPriceEur: finalPriceEur.toFixed(2),
discountRate: discountRateVal.toFixed(2),
currency: String(
document.getElementById(‘proformaCurrency’)?.value || ‘EUR’
).toUpperCase(),
});
window.open(
`https://backend.trak.codes/api/v0/pdf/proforma-invoice?${params}`,
‘_blank’
);
overlay.remove();
};
document.getElementById(‘proformaName’).focus();
}
function getBaseCharge(nbAttendees) {
return nbAttendees < 20
? 0
: 0.2 * Math.min(nbAttendees, 300) +
0.06 * Math.max(0, nbAttendees – 300);
}
function getCharge(nbAttendees) {
const base = getBaseCharge(nbAttendees);
const discount = base * (discountRate / 100);
return base – discount;
}
function setTextContents(spec) {
for (const id of Object.keys(spec)) {
const el = document.getElementById(id);
if (!el) continue;
el.textContent = spec[id];
}
}
function updateCharge() {
const chargeEl = document.getElementById('charge');
const attendeesEl = document.getElementById('attendees');
try {
const nbAttendees = parseInt(attendeesEl.value, 10) || 0;
const charge = getCharge(nbAttendees);
setTextContents({
charge: charge.toFixed(2),
usdCharge: eurToUsd(charge).toFixed(2),
baseCharge: getBaseCharge(nbAttendees).toFixed(2),
});
const paypalLink = document.getElementById('paypalLink');
if (paypalLink) paypalLink.href = getUrl(nbAttendees, 'paypal');
const polarLink = document.getElementById('polarLink');
if (polarLink) polarLink.href = getUrl(nbAttendees, 'polar');
} catch (e) {
console.error('updateCharge', e);
setTextContents({
charge: '?',
usdCharge: '?',
baseCharge: '?',
});
}
}
const initialAttendeeCount = currentIsPaid
? 500
: procs
? currentCases || 100
: 1000;
const baseAmount = getBaseCharge(initialAttendeeCount);
const amount = getCharge(initialAttendeeCount);
const amountInUsd = eurToUsd(amount);
const discountHtml =
discountRate === 0
? ''
: `
0 ? ‘rgba(39, 174, 96, 0.1)’ : ‘rgba(231, 76, 60, 0.1)’
}; border-left: 4px solid ${discountRate > 0 ? ‘#27ae60’ : ‘#e74c3c’}”>
Price before discount: ${baseAmount.toFixed(
2
)} โฌ (EUR)
0 ? ‘#27ae60’ : ‘#e74c3c’
}; font-weight: 600;”>
${discountRate > 0 ? ‘Discount’ : ‘Overcharge’}: ${Math.abs(
discountRate
)}%
(${discountCode.toUpperCase()})
`;
const paypalHtml = `
`;
const creditCardHtml = ”;
const payHtml = `
${discountHtml}
Final price: ${amount.toFixed(
2
)} โฌ (EUR)
Approximately $${amountInUsd.toFixed(
2
)} (USD)
${paypalHtml}
${creditCardHtml}
๐ Get Proforma Invoice
`;
const extraHtml =
account === ‘?’
? ”
: `
๐ฐ๐ท๏ธ Buy a ticket bundle! (for multiple events)
`;
const projectHtml = `
Title: ${title}
Creation date: ${new Date(
procCreatedTs
).toLocaleString()}
Paid: ${currentIsPaid ? ‘YES’ : ‘NO’}
${
!currentIsPaid
? ”
: `
Remaining check-ins: ${checkInsLeft} ${
projectUsesBundle
? ‘ (bundle) ‘
: ”
}
`
}
Current check-ins: ${currentCheckIns}
Current number of attendees: ${currentCases}
`;
let userDiscountRate = 0;
let currentAttendeeCount = 300;
const addonLinksHtml = `
Get QR Code Ticket Add-ons:
`;
function updateAttendeeCount(value, source) {
currentAttendeeCount = Math.max(
20,
Math.min(10000, parseInt(value) || 300)
);
if (source !== ‘slider’) {
document.getElementById(‘attendeeSlider’).value = currentAttendeeCount;
}
if (source !== ‘input’) {
document.getElementById(‘attendeeNumberInput’).value =
currentAttendeeCount;
}
updatePricingDisplay();
}
function updatePricingDisplay() {
const finalPrice = calculateExactPrice(currentAttendeeCount);
const finalPriceUsd = eurToUsd(finalPrice);
const perAttendee = (finalPrice / currentAttendeeCount).toFixed(3);
const perAttendeeUsd = (finalPriceUsd / currentAttendeeCount).toFixed(3);
document.getElementById(‘dynamicPrice’).textContent = finalPrice.toFixed(2);
document.getElementById(‘dynamicPriceUsd’).textContent =
finalPriceUsd.toFixed(2);
document.getElementById(
‘dynamicPerAttendee’
).textContent = `โฌ${perAttendee} / $${perAttendeeUsd}`;
}
async function fetchDiscountRate() {
const loadingDiv = document.getElementById(‘pricingLoading’);
const errorDiv = document.getElementById(‘pricingError’);
if (loadingDiv) loadingDiv.style.display = ‘block’;
if (errorDiv) errorDiv.style.display = ‘none’;
try {
const url = `https://backend.trak.codes/api/v0/appscript/addon-discount-rate`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
userDiscountRate = Math.min(100, Math.max(0, data.discountRate || 0));
renderPricingWithDiscount();
} catch (error) {
console.error(‘Error fetching discount rate:’, error);
if (errorDiv) {
errorDiv.textContent =
‘Could not load personalized pricing. Showing general price range.’;
errorDiv.style.display = ‘block’;
}
if (loadingDiv) loadingDiv.style.display = ‘none’;
// Show static price range on error
document.getElementById(‘credit-info’).innerHTML = `
General Price Range
Unable to fetch personalized pricing
Showing general price range instead
General price range:
$0.02 – $0.2 per attendee
(varies by region and volume)
๐ก For exact pricing
Open the add-on in your Google Form/Sheet and use the “Pricing” link in the sidebar.
${addonLinksHtml}
`;
}
}
function calculateExactPrice(nbAttendees) {
return getBaseCharge(nbAttendees) * (1 – userDiscountRate / 100);
}
function renderPricingWithDiscount() {
const finalPrice = calculateExactPrice(currentAttendeeCount);
const perAttendee = (finalPrice / currentAttendeeCount).toFixed(3);
const perAttendeeUsd = (
eurToUsd(finalPrice) / currentAttendeeCount
).toFixed(3);
const discountInfo =
userDiscountRate > 0
? `
โ Your regional discount: ${userDiscountRate}%
`
: ”;
document.getElementById(‘credit-info’).innerHTML = `
Your Exact Pricing
${discountInfo}
Total Price
โฌ${finalPrice.toFixed(2)}
$${eurToUsd(finalPrice).toFixed(
2
)}
โฌ${perAttendee} / $${perAttendeeUsd} per attendee
๐ Get Proforma Invoice
๐ก For project-specific pricing
Open the add-on in your Google Form/Sheet and use the “Pricing” link in the sidebar.
${addonLinksHtml}
`;
}
document.getElementById(‘credit-info’).innerHTML = !procs
? `
Your Pricing
๐ณ Payment Methods
We accept PayPal and Credit/Debit Cards (via Polar).
To proceed with payment:
Go back to the add-on in your Google Form/Sheet
Open the add-on and finish creating your project
Use the “Pricing” link at the top of the add-on sidebar
${addonLinksHtml}
`
: `
Current Project
${projectHtml}
${
currentIsPaid
? ‘Number of additional attendees, if needed’
: ‘Planned number of attendees’
}:
${payHtml}
${extraHtml}
Your Other Projects
Email: ${email}
Total projects (documents): ${procs}
`;
// Auto-fetch pricing when on standalone page (no project context)
if (!procs) {
fetchDiscountRate();
}
โ Your project can be used immediately after payment (reload the add-on window if necessary).
๐ You can send the payment link to a friend . Their payment, with a different email, will be correctly applied to your project.
๐ฐ When using sessions or multiple check-ins, you’re billed only once for each attendee.
๐ฎโโ๏ธ You can add as many check-in agent accounts as needed.
โ
The data in your paid project remains accessible at least 1 year after payment, and 3 months after check-ins. However, you can’t reuse tickets.
๐ก Need a discounted bundle of tickets for use in multiple events? Check our ticket bundles .
๐งพ Need a proforma / itemized invoice ? Please contact us .
Your other projects are all the forms and spreadsheets where you enabled our add-ons, using the current email address. Payment is per project .
What is free
QR code generation and sending tickets by email are free .
This means you can freely activate the add-on and let it send tickets to attendees while you are exploring payment options.
You can check-in up to 10 attendees per event, for free . Paid projects support unlimited attendees.
If your project is in sponsored mode (student-run events), you can run check-in for free . However tickets will include an ad at the top. If you pay , your project will automatically switch back to premium mode.
What we charge for
We currently charge per event , and for larger events (over 20 attendee s). Paid projects can support any number of attendees. We have events with 4000+ attendees .
If you send tickets using WhatsApp , we separately charge for it. If your event is in multiple sessions , but over a few days, it is considered a single event, and you only pay once. If you are using this for regular attendance tracking, where the same attendees are checked in at regular intervals indefinitely, then use the links below to reach out.
We receive payments mainly by Paypal . If you follow the payment link at the top of the add-on, then use the Paypal payment button, the payment is automatically applied to your event (reload the add-on if it doesn’t show it). You can reach-out for help by Email , Telegram or Whatsapp for more payment options and discounts (non-profits, student-run events, etc.)