# Vital Tides CF7 REST Checkout Implementation
## What this gives you
* fully custom frontend UI * no reliance on CF7-rendered field markup * direct submit to the Contact Form 7 REST endpoint * live order summary and totals * cleaner validation and easier styling
---
## 1) HTML
Paste this into a **Custom HTML** block on the page.
```html
Complete the form below and submit your order request securely.
```
## 2) CSS
Paste into **Appearance → Customize → Additional CSS**.
```css :root { --vt-green: #3f8f3f; --vt-green-2: #4fa64f; --vt-green-soft: #f6fbf6; --vt-green-border: #d9ead9; --vt-text: #0f172a; --vt-muted: #64748b; --vt-border: #d1d5db; }
#vt-order-app { max-width: 1360px; margin: 0 auto; display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 24px; align-items: start; font-family: Arial, sans-serif; }
#vt-order-app * { box-sizing: border-box; }
#vt-order-app .vt-order-card, #vt-order-app .vt-summary-card { background: #fff; border: 2px solid var(--vt-green); border-radius: 20px; box-shadow: 0 8px 20px rgba(63, 143, 63, 0.12), 0 2px 6px rgba(63, 143, 63, 0.08); }
#vt-order-app .vt-order-card { padding: 30px 34px; }
#vt-order-app .vt-summary-card { position: sticky; top: 24px; padding: 20px; }
#vt-order-app .vt-header h2 { margin: 0 0 6px; font-size: 28px; color: var(--vt-text); }
#vt-order-app .vt-header p { margin: 0 0 24px; color: var(--vt-muted); }
#vt-order-app .vt-field-row { display: grid; grid-template-columns: 150px minmax(0, 1fr); gap: 12px; align-items: center; margin-bottom: 12px; }
#vt-order-app .vt-note-row { align-items: start; }
#vt-order-app label, #vt-order-app .vt-product-label { color: var(--vt-green); font-size: 14px; font-weight: 700; line-height: 1.25; }
#vt-order-app label span { color: #b91c1c; }
#vt-order-app input:not([type="submit"]), #vt-order-app input:not([type="button"]), #vt-order-app select, #vt-order-app textarea, #vt-order-app button { font: inherit; }
#vt-order-app input[type="text"], #vt-order-app input[type="email"], #vt-order-app input[type="tel"], #vt-order-app input[type="number"], #vt-order-app select, #vt-order-app textarea { width: 100%; margin: 0; padding: 11px 13px; border: 1px solid var(--vt-border); border-radius: 10px; background: #fff; color: var(--vt-text); }
#vt-order-app input:focus, #vt-order-app select:focus, #vt-order-app textarea:focus { outline: none; border-color: var(--vt-green); box-shadow: 0 0 0 3px rgba(63, 143, 63, 0.08); }
#vt-order-app .vt-payment-note, #vt-order-app .vt-summary-payment, #vt-order-app .vt-summary-badge { background: var(--vt-green-soft); border: 1px solid var(--vt-green-border); }
#vt-order-app .vt-payment-note, #vt-order-app .vt-summary-payment { padding: 12px 14px; border-radius: 12px; color: #355e35; font-size: 13px; line-height: 1.6; }
#vt-order-app .vt-products { margin-top: 10px; }
#vt-order-app .vt-products-head, #vt-order-app .vt-product-row { display: grid; grid-template-columns: 150px minmax(0, 1fr) 90px 70px; gap: 12px; align-items: center; margin-bottom: 12px; }
#vt-order-app .vt-products-head { color: #2f7a2f; font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }
#vt-order-app .vt-price, #vt-order-app .vt-qty { text-align: center; }
#vt-order-app .vt-price { background: #f8fbf8; color: #355e35; font-weight: 700; pointer-events: none; }
#vt-order-app .vt-qty { padding-left: 8px; padding-right: 8px; }
#vt-order-app textarea { min-height: 120px; resize: vertical; }
#vt-order-app .vt-submit-wrap { margin-top: 22px; }
#vt-order-app #vt-submit { width: 100%; padding: 14px 18px; border: none; border-radius: 999px; background: linear-gradient(135deg, var(--vt-green), var(--vt-green-2)); color: #fff; font-weight: 700; cursor: pointer; }
#vt-order-app #vt-submit:disabled { opacity: .7; cursor: not-allowed; }
#vt-order-app .vt-form-status { margin-top: 10px; font-size: 13px; }
#vt-order-app .vt-form-status.is-error { color: #b91c1c; }
#vt-order-app .vt-form-status.is-success { color: #2f7a2f; }
#vt-order-app .vt-error { min-height: 16px; margin-top: 4px; color: #b91c1c; font-size: 12px; }
#vt-order-app .is-invalid { border-color: #b91c1c !important; box-shadow: 0 0 0 3px rgba(185, 28, 28, 0.08) !important; }
#vt-order-app .vt-summary-card h3 { margin: 0 0 14px; font-size: 22px; color: var(--vt-text); }
#vt-order-app .vt-summary-badge { display: inline-block; margin-bottom: 16px; padding: 7px 10px; border-radius: 999px; color: #355e35; font-size: 12px; font-weight: 700; }
#vt-order-app .vt-summary-empty { color: var(--vt-muted); font-size: 14px; line-height: 1.5; }
#vt-order-app .vt-summary-item { display: grid; grid-template-columns: 1fr auto; gap: 10px; padding: 10px 0; border-bottom: 1px solid #eef5ee; }
#vt-order-app .vt-summary-item-name { color: var(--vt-text); font-size: 14px; line-height: 1.4; }
#vt-order-app .vt-summary-item-meta { color: var(--vt-muted); font-size: 12px; margin-top: 2px; }
#vt-order-app .vt-summary-item-price { color: var(--vt-text); font-size: 14px; font-weight: 700; white-space: nowrap; }
#vt-order-app .vt-summary-totals { border-top: 1px solid var(--vt-green-border); padding-top: 14px; margin-top: 8px; }
#vt-order-app .vt-summary-line { display: flex; justify-content: space-between; gap: 12px; margin-bottom: 10px; color: var(--vt-text); font-size: 14px; }
#vt-order-app .vt-total-line { font-size: 18px; font-weight: 700; color: #2f7a2f; }
@media (max-width: 991px) { #vt-order-app { grid-template-columns: 1fr; }
#vt-order-app .vt-summary-card { position: static; } }
@media (max-width: 767px) { #vt-order-app .vt-order-card { padding: 22px 16px; }
#vt-order-app .vt-field-row, #vt-order-app .vt-products-head, #vt-order-app .vt-product-row { grid-template-columns: 1fr; gap: 8px; } } ```
## 3) JavaScript
Paste into a JS snippets plugin or enqueue as a file.
```javascript (function () { const PRODUCTS = [ '5-Amino-1MQ 50 mg', 'AOD-9604 10 mg', 'Bacteriostatic Water 10 mL vials', 'BPC-157 20 mg', 'BPC-157 + TB-500 20 mg', 'BPC-157 + TB-500 20 mg / 20 mg', 'Cerebrolysin 60 mg', 'CJC-1295 with DAC 5 mg', 'CJC-1295 without DAC + Ipamorelin 10 mg', 'DSIP 15 mg', 'Epithalon 50 mg', 'FOXO4-DRI 10 mg', 'GHK-Cu 100 mg', 'Glutathione 1500 mg', 'Glow 75 mg', 'Hexarelin 5 mg', 'HGH (Somatropin) 36 IU', 'Ipamorelin 10 mg', 'Kisspeptin 10 mg', 'KLOW 80 mg', 'KPV 10 mg', 'LL-37 5 mg', 'Melanotan-1 10 mg', 'Melanotan-2 10 mg', 'MOTS-C 40 mg', 'NAD+ 1000 mg', 'Oxytocin 10 mg', 'Pinealon 20 mg', 'PT-141 10 mg', 'Retatrutide 20 mg', 'Retatrutide 5 mg + Cagrilintide 5 mg', 'Selank 10 mg', 'Selank 30 mg', 'Semax 10 mg', 'Semax 30 mg', 'Semax 10 mg + Selank 10 mg', 'Sermorelin 5 mg', 'Sermorelin 10 mg', 'Sermorelin 15 mg', 'Sildenafil 50 mg / Sildenafil 20 mg', 'SNAP-8 10 mg', 'SS-31 50 mg', 'TB-500 10 mg', 'TB-500 20 mg', 'Tesamorelin 15 mg', 'Thymosin Alpha-1 10 mg', 'Tirzepatide 20 mg', 'Tirzepatide 30 mg', 'VIP 10 mg' ];
const UNIT_PRICE = 10; const SLOT_COUNT = 5;
function money(n) { return '$' + Number(n).toFixed(2); }
function app() { return document.getElementById('vt-order-app'); }
function endpoint() { return app()?.dataset.cf7Endpoint || ''; }
function selectedText(select) { if (!select || select.selectedIndex < 0) return ''; return (select.options[select.selectedIndex].text || '').trim(); } function isPlaceholder(text) { const t = (text || '').toLowerCase().trim(); return !t || t.includes('select product') || t.includes('optional product'); } function qs(sel, root) { return (root || document).querySelector(sel); } function qsa(sel, root) { return Array.from((root || document).querySelectorAll(sel)); } function setError(inputId, message) { const field = qs('#' + inputId); const error = qs('[data-error-for="' + inputId + '"]'); if (field) field.classList.toggle('is-invalid', !!message); if (error) error.textContent = message || ''; } function fillProductSelects(root) { qsa('.vt-product-select', root).forEach((select) => { PRODUCTS.forEach((product) => { const opt = document.createElement('option'); opt.value = product; opt.textContent = product; select.appendChild(opt); }); }); }
function forceZeroQuantities(root) { qsa('.vt-qty', root).forEach((input) => { if (input.value === '' || input.value == null) input.value = '0'; }); }
function updateSummary(root) { forceZeroQuantities(root);
const itemsWrap = qs('#vt-summary-items', root); const subtotalEl = qs('#vt-subtotal', root); const totalEl = qs('#vt-total', root);
let subtotal = 0; let html = '';
qsa('.vt-product-row', root).forEach((row) => { const select = qs('.vt-product-select', row); const qtyInput = qs('.vt-qty', row); const priceInput = qs('.vt-price', row);
const product = selectedText(select); let qty = parseInt(qtyInput?.value || '0', 10); if (Number.isNaN(qty) || qty < 0) qty = 0; const hasProduct = !isPlaceholder(product); const unitPrice = hasProduct ? UNIT_PRICE : 0; const lineTotal = unitPrice * qty; if (priceInput) priceInput.value = unitPrice ? money(unitPrice) : '$0.00'; if (hasProduct && qty > 0) { subtotal += lineTotal; html += `
`; } });
if (itemsWrap) { itemsWrap.innerHTML = html || '
'; } if (subtotalEl) subtotalEl.textContent = money(subtotal); if (totalEl) totalEl.textContent = money(subtotal); }
function validate(root) { let ok = true;
const name = qs('#vt-name', root); const email = qs('#vt-email', root); const phone = qs('#vt-phone', root);
setError('vt-name', ''); setError('vt-email', ''); setError('vt-phone', '');
if (!name?.value.trim()) { setError('vt-name', 'Full name is required.'); ok = false; }
const emailVal = email?.value.trim() || ''; const emailOk = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailVal); if (!emailOk) { setError('vt-email', emailVal ? 'Please enter a valid email address.' : 'Email is required.'); ok = false; }
if (!phone?.value.trim()) { setError('vt-phone', 'Phone number is required.'); ok = false; }
const hasOrder = qsa('.vt-product-row', root).some((row) => { const product = selectedText(qs('.vt-product-select', row)); const qty = parseInt(qs('.vt-qty', row)?.value || '0', 10) || 0; return !isPlaceholder(product) && qty > 0; });
const status = qs('#vt-form-status', root); if (!hasOrder) { if (status) { status.textContent = 'Please select at least one product with quantity greater than 0.'; status.className = 'vt-form-status is-error'; } ok = false; } else if (status && !status.classList.contains('is-success')) { status.textContent = ''; status.className = 'vt-form-status'; }
return ok; }
async function submitOrder(root) { const submitBtn = qs('#vt-submit', root); const status = qs('#vt-form-status', root);
if (!validate(root)) return; if (!endpoint()) { if (status) { status.textContent = 'Missing CF7 endpoint. Add your form endpoint to data-cf7-endpoint.'; status.className = 'vt-form-status is-error'; } return; }
submitBtn.disabled = true; submitBtn.textContent = 'Submitting...'; status.textContent = ''; status.className = 'vt-form-status';
const fd = new FormData(); fd.append('your-name', qs('#vt-name', root)?.value.trim() || ''); fd.append('your-email', qs('#vt-email', root)?.value.trim() || ''); fd.append('your-phone', qs('#vt-phone', root)?.value.trim() || ''); fd.append('payment-method', qs('#vt-payment', root)?.value || 'Zelle'); fd.append('your-message', qs('#vt-notes', root)?.value.trim() || '');
let total = 0; for (let i = 1; i <= SLOT_COUNT; i++) { const product = qs('[name="product-' + i + '"]', root)?.value || ''; const qty = parseInt(qs('[name="quantity-' + i + '"]', root)?.value || '0', 10) || 0; fd.append('product-' + i, product); fd.append('quantity-' + i, String(qty)); if (product && qty > 0) total += UNIT_PRICE * qty; } fd.append('order-total', money(total));
try { const res = await fetch(endpoint(), { method: 'POST', body: fd }); const data = await res.json();
if (data.status === 'mail_sent') { status.textContent = 'Order submitted successfully.'; status.className = 'vt-form-status is-success'; qs('#vt-order-form', root)?.reset(); forceZeroQuantities(root); updateSummary(root); } else { status.textContent = data.message || 'There was a problem submitting your order.'; status.className = 'vt-form-status is-error'; } } catch (err) { status.textContent = 'Network error. Please try again.'; status.className = 'vt-form-status is-error'; } finally { submitBtn.disabled = false; submitBtn.textContent = 'Submit Order Request'; } }
function bind(root) { if (!root || root.dataset.vtBound === '1') return; root.dataset.vtBound = '1';
fillProductSelects(root); forceZeroQuantities(root); updateSummary(root);
root.addEventListener('change', () => updateSummary(root)); root.addEventListener('input', () => updateSummary(root));
const form = qs('#vt-order-form', root); form?.addEventListener('submit', (e) => { e.preventDefault(); submitOrder(root); }); }
function boot() { const root = app(); if (root) bind(root); }
if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', boot); } else { boot(); } })(); ```
## 4) Your CF7 form only needs matching backend tags
You do **not** need to render the CF7 shortcode UI on the page for this frontend.
In your Contact Form 7 form, use backend fields like:
```text ```
Then map those into your mail template.
## 5) Final setup note
Replace:
```html /wp-json/contact-form-7/v1/contact-forms/YOUR_FORM_ID/feedback ```
with your real CF7 form endpoint.
If you want, I can next turn this into:
* a WordPress-ready enqueue bundle * a single shortcode/plugin version * or a Stripe-style animated checkout version