Google Merchant Center trust signal improvements

- Add contact.php (was 404, linked from footer and returns page)
- Add shippingDetails and hasMerchantReturnPolicy to product schema
- Add priceValidUntil to product Offer schema
- Improve merchant feed descriptions (use DB description when present)
- Add handling/transit times and return_policy_label to feed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-15 18:46:21 +00:00
parent 5637b6d7f5
commit 15bcef262f
3 changed files with 216 additions and 11 deletions
+169
View File
@@ -0,0 +1,169 @@
<?php
$pageTitle = "Contact Us - Tom's Java Jive";
$metaDescription = "Get in touch with Tom's Java Jive. We're here to help with orders, questions, and anything coffee-related.";
$canonicalUrl = 'https://tomsjavajive.com/contact.php';
$breadcrumbs = [
['name' => 'Home', 'url' => 'https://tomsjavajive.com'],
['name' => 'Contact', 'url' => 'https://tomsjavajive.com/contact.php']
];
require_once __DIR__ . '/includes/functions.php';
$sent = false;
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
$subject = trim($_POST['subject'] ?? '');
$message = trim($_POST['message'] ?? '');
if (empty($name)) $errors['name'] = 'Name is required.';
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL))
$errors['email'] = 'A valid email address is required.';
if (empty($subject)) $errors['subject'] = 'Please choose a subject.';
if (empty($message)) $errors['message'] = 'Message cannot be empty.';
if (empty($errors)) {
$body = "Name: {$name}\nEmail: {$email}\nSubject: {$subject}\n\n{$message}";
cybermailSend('hello@tomsjavajive.com', "Contact Form: {$subject}", $body, $name, $email);
$sent = true;
}
}
require_once __DIR__ . '/includes/header.php';
?>
<section class="section" style="padding-top: 2rem; padding-bottom: 4rem;">
<div class="container" style="max-width: 900px;">
<div style="text-align: center; margin-bottom: 3rem;">
<h1 style="font-size: 2.5rem; margin-bottom: 0.75rem;">Contact Us</h1>
<p class="text-muted" style="font-size: 1.1rem;">Questions about your order or just want to talk coffee? We'd love to hear from you.</p>
</div>
<div style="display: grid; grid-template-columns: 1fr 1.6fr; gap: 2.5rem; align-items: start;">
<!-- Contact Info -->
<div>
<div class="card" style="margin-bottom: 1.5rem;">
<div class="card-body">
<h3 style="margin-bottom: 1.25rem; font-size: 1.1rem;">Get in Touch</h3>
<div style="display: flex; align-items: flex-start; gap: 0.85rem; margin-bottom: 1.25rem;">
<div style="width: 38px; height: 38px; background: rgba(255,94,26,0.1); border-radius: 50%; display: flex; align-items: center; justify-content: center; flex-shrink: 0; color: var(--color-primary);">
<i class="fas fa-envelope"></i>
</div>
<div>
<div style="font-weight: 600; margin-bottom: 0.2rem;">Email</div>
<a href="mailto:hello@tomsjavajive.com">hello@tomsjavajive.com</a>
<div class="text-muted" style="font-size: 0.85rem; margin-top: 0.15rem;">We reply within 1 business day</div>
</div>
</div>
<div style="display: flex; align-items: flex-start; gap: 0.85rem; margin-bottom: 1.25rem;">
<div style="width: 38px; height: 38px; background: rgba(255,94,26,0.1); border-radius: 50%; display: flex; align-items: center; justify-content: center; flex-shrink: 0; color: var(--color-primary);">
<i class="fas fa-phone"></i>
</div>
<div>
<div style="font-weight: 600; margin-bottom: 0.2rem;">Phone</div>
<a href="tel:+18175550120">(817) 555-0120</a>
<div class="text-muted" style="font-size: 0.85rem; margin-top: 0.15rem;">MonFri, 9am5pm CT</div>
</div>
</div>
<div style="display: flex; align-items: flex-start; gap: 0.85rem;">
<div style="width: 38px; height: 38px; background: rgba(255,94,26,0.1); border-radius: 50%; display: flex; align-items: center; justify-content: center; flex-shrink: 0; color: var(--color-primary);">
<i class="fas fa-map-marker-alt"></i>
</div>
<div>
<div style="font-weight: 600; margin-bottom: 0.2rem;">Location</div>
<div>Weatherford, TX 76086</div>
<div class="text-muted" style="font-size: 0.85rem; margin-top: 0.15rem;">United States</div>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-body">
<h3 style="margin-bottom: 0.75rem; font-size: 1rem;">Quick Links</h3>
<ul class="footer-links" style="list-style: none; padding: 0; margin: 0;">
<li style="margin-bottom: 0.4rem;"><a href="/track-order.php"><i class="fas fa-search" style="width: 16px;"></i> Track Your Order</a></li>
<li style="margin-bottom: 0.4rem;"><a href="/returns.php"><i class="fas fa-undo" style="width: 16px;"></i> Returns &amp; Refunds</a></li>
<li style="margin-bottom: 0.4rem;"><a href="/shipping.php"><i class="fas fa-truck" style="width: 16px;"></i> Shipping Info</a></li>
<li><a href="/faq.php"><i class="fas fa-question-circle" style="width: 16px;"></i> FAQ</a></li>
</ul>
</div>
</div>
</div>
<!-- Contact Form -->
<div class="card">
<div class="card-body">
<?php if ($sent): ?>
<div style="text-align: center; padding: 2rem 0;">
<div style="font-size: 3rem; margin-bottom: 1rem;">☕</div>
<h3 style="margin-bottom: 0.5rem;">Message Sent!</h3>
<p class="text-muted">Thanks for reaching out. We'll get back to you within one business day.</p>
<a href="/shop.php" class="btn btn-primary mt-2">Browse Our Coffee</a>
</div>
<?php else: ?>
<h3 style="margin-bottom: 1.5rem; font-size: 1.1rem;">Send a Message</h3>
<form method="POST" action="">
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem;">
<div class="form-group">
<label class="form-label">Your Name *</label>
<input type="text" name="name" class="form-input <?= isset($errors['name']) ? 'is-invalid' : '' ?>"
value="<?= htmlspecialchars($_POST['name'] ?? '') ?>" required>
<?php if (isset($errors['name'])): ?>
<span class="form-error"><?= $errors['name'] ?></span>
<?php endif; ?>
</div>
<div class="form-group">
<label class="form-label">Email Address *</label>
<input type="email" name="email" class="form-input <?= isset($errors['email']) ? 'is-invalid' : '' ?>"
value="<?= htmlspecialchars($_POST['email'] ?? '') ?>" required>
<?php if (isset($errors['email'])): ?>
<span class="form-error"><?= $errors['email'] ?></span>
<?php endif; ?>
</div>
</div>
<div class="form-group">
<label class="form-label">Subject *</label>
<select name="subject" class="form-input <?= isset($errors['subject']) ? 'is-invalid' : '' ?>" required>
<option value="">— Select a subject —</option>
<option value="Order Question" <?= ($_POST['subject'] ?? '') === 'Order Question' ? 'selected' : '' ?>>Order Question</option>
<option value="Shipping &amp; Delivery" <?= ($_POST['subject'] ?? '') === 'Shipping &amp; Delivery' ? 'selected' : '' ?>>Shipping &amp; Delivery</option>
<option value="Returns &amp; Refunds" <?= ($_POST['subject'] ?? '') === 'Returns &amp; Refunds' ? 'selected' : '' ?>>Returns &amp; Refunds</option>
<option value="Product Question" <?= ($_POST['subject'] ?? '') === 'Product Question' ? 'selected' : '' ?>>Product Question</option>
<option value="Wholesale Inquiry" <?= ($_POST['subject'] ?? '') === 'Wholesale Inquiry' ? 'selected' : '' ?>>Wholesale Inquiry</option>
<option value="Other" <?= ($_POST['subject'] ?? '') === 'Other' ? 'selected' : '' ?>>Other</option>
</select>
<?php if (isset($errors['subject'])): ?>
<span class="form-error"><?= $errors['subject'] ?></span>
<?php endif; ?>
</div>
<div class="form-group">
<label class="form-label">Message *</label>
<textarea name="message" class="form-textarea <?= isset($errors['message']) ? 'is-invalid' : '' ?>"
rows="5" placeholder="How can we help?" required><?= htmlspecialchars($_POST['message'] ?? '') ?></textarea>
<?php if (isset($errors['message'])): ?>
<span class="form-error"><?= $errors['message'] ?></span>
<?php endif; ?>
</div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-paper-plane"></i> Send Message
</button>
</form>
<?php endif; ?>
</div>
</div>
</div>
</div>
</section>
<?php require_once __DIR__ . '/includes/footer.php'; ?>
+21 -5
View File
@@ -31,12 +31,22 @@ foreach ($products as $p) {
$imageUrl = !empty($images) ? $base . $images[0] : $base . '/assets/images/placeholder-product.svg'; $imageUrl = !empty($images) ? $base . $images[0] : $base . '/assets/images/placeholder-product.svg';
$categoryLabel = $p['category'] === 'Bean' ? 'Whole Bean' : 'Ground'; $categoryLabel = $p['category'] === 'Bean' ? 'Whole Bean' : 'Ground';
$grindNote = $p['category'] === 'Bean'
? 'Available as whole bean so you can grind fresh for maximum flavor.'
: 'Pre-ground to a versatile medium grind, ready to brew right out of the bag.';
$title = htmlspecialchars($p['name'] . ' ' . $categoryLabel . ' Coffee'); $title = htmlspecialchars($p['name'] . ' ' . $categoryLabel . ' Coffee');
$desc = htmlspecialchars( $rawDesc = $p['description'] ?? '';
$p['name'] . ' flavored artisan coffee — ' . strtolower($categoryLabel) . '. ' . if (!empty($rawDesc)) {
'All natural flavoring. ' . $desc = htmlspecialchars(
'12 oz bag.' strip_tags($rawDesc) . ' ' . $grindNote . ' 12 oz bag. Ships from Weatherford, TX.'
); );
} else {
$desc = htmlspecialchars(
"Tom's Java Jive {$p['name']} flavored artisan coffee, freshly roasted in small batches in Weatherford, Texas. " .
"All-natural flavoring with no artificial additives. {$grindNote} " .
"12 oz bag. Free shipping on orders over \$50."
);
}
$link = htmlspecialchars($base . '/product.php?id=' . $p['product_id']); $link = htmlspecialchars($base . '/product.php?id=' . $p['product_id']);
$imageLink = htmlspecialchars($imageUrl); $imageLink = htmlspecialchars($imageUrl);
$price = number_format((float)($p['sale_price'] ?? $p['price']), 2, '.', ''); $price = number_format((float)($p['sale_price'] ?? $p['price']), 2, '.', '');
@@ -66,11 +76,17 @@ foreach ($products as $p) {
echo " <g:google_product_category>{$gCategory}</g:google_product_category>\n"; echo " <g:google_product_category>{$gCategory}</g:google_product_category>\n";
echo " <g:product_type>Coffee &gt; Flavored Coffee &gt; {$categoryLabel}</g:product_type>\n"; echo " <g:product_type>Coffee &gt; Flavored Coffee &gt; {$categoryLabel}</g:product_type>\n";
echo " <g:identifier_exists>false</g:identifier_exists>\n"; echo " <g:identifier_exists>false</g:identifier_exists>\n";
echo " <g:custom_label_0>{$categoryLabel}</g:custom_label_0>\n";
echo " <g:shipping>\n"; echo " <g:shipping>\n";
echo " <g:country>US</g:country>\n"; echo " <g:country>US</g:country>\n";
echo " <g:service>Standard</g:service>\n"; echo " <g:service>Standard</g:service>\n";
echo " <g:price>5.99 USD</g:price>\n"; echo " <g:price>5.99 USD</g:price>\n";
echo " <g:min_handling_time>1</g:min_handling_time>\n";
echo " <g:max_handling_time>2</g:max_handling_time>\n";
echo " <g:min_transit_time>3</g:min_transit_time>\n";
echo " <g:max_transit_time>7</g:max_transit_time>\n";
echo " </g:shipping>\n"; echo " </g:shipping>\n";
echo " <g:return_policy_label>default</g:return_policy_label>\n";
echo " </item>\n"; echo " </item>\n";
} }
+26 -6
View File
@@ -69,12 +69,32 @@ $productSchemaData = [
'description' => $metaDescription, 'description' => $metaDescription,
'brand' => ['@type' => 'Brand', 'name' => "Tom's Java Jive"], 'brand' => ['@type' => 'Brand', 'name' => "Tom's Java Jive"],
'offers' => [ 'offers' => [
'@type' => 'Offer', '@type' => 'Offer',
'priceCurrency' => 'USD', 'priceCurrency' => 'USD',
'price' => number_format((float) $displayPrice, 2, '.', ''), 'price' => number_format((float) $displayPrice, 2, '.', ''),
'availability' => $inStock ? 'https://schema.org/InStock' : 'https://schema.org/OutOfStock', 'priceValidUntil' => date('Y-12-31', strtotime('+1 year')),
'url' => $canonicalUrl, 'availability' => $inStock ? 'https://schema.org/InStock' : 'https://schema.org/OutOfStock',
'seller' => ['@type' => 'Organization', 'name' => "Tom's Java Jive"], 'url' => $canonicalUrl,
'seller' => ['@type' => 'Organization', 'name' => "Tom's Java Jive"],
'shippingDetails' => [
'@type' => 'OfferShippingDetails',
'shippingRate' => ['@type' => 'MonetaryAmount', 'value' => '5.99', 'currency' => 'USD'],
'shippingDestination'=> ['@type' => 'DefinedRegion', 'addressCountry' => 'US'],
'deliveryTime' => [
'@type' => 'ShippingDeliveryTime',
'handlingTime' => ['@type' => 'QuantitativeValue', 'minValue' => 1, 'maxValue' => 2, 'unitCode' => 'DAY'],
'transitTime' => ['@type' => 'QuantitativeValue', 'minValue' => 3, 'maxValue' => 7, 'unitCode' => 'DAY'],
],
],
'hasMerchantReturnPolicy' => [
'@type' => 'MerchantReturnPolicy',
'applicableCountry' => 'US',
'returnPolicyCategory' => 'https://schema.org/MerchantReturnFiniteReturnWindow',
'merchantReturnDays' => 30,
'returnMethod' => 'https://schema.org/ReturnByMail',
'returnFees' => 'https://schema.org/FreeReturn',
'returnPolicyLink' => 'https://tomsjavajive.com/returns.php',
],
], ],
]; ];
if ($reviewCount > 0) { if ($reviewCount > 0) {