// ── Frontend: expose ALL manual prices on cart page (for currency display) ──
add_action('wp_footer', function() {
global $post;
if (!$post || !has_shortcode($post->post_content, 'bc_cart')) return;
// Get/cache exchange rates (daily)
$rate_key = 'bc_fx_rates_' . date('Y-m-d');
$rate_cache = get_transient($rate_key);
if (!$rate_cache) {
$response = wp_remote_get('https://open.er-api.com/v6/latest/EUR', ['timeout' => 5]);
if (!is_wp_error($response)) {
$data = json_decode(wp_remote_retrieve_body($response), true);
$rate_cache = ['gbp' => $data['rates']['GBP'] ?? 0.85, 'usd' => $data['rates']['USD'] ?? 1.08];
set_transient($rate_key, $rate_cache, DAY_IN_SECONDS);
set_transient('bc_fx_rates', $rate_cache, DAY_IN_SECONDS); /* keep undated key in sync for dashboard */
} else {
$rate_cache = ['gbp' => 0.871, 'usd' => 1.168];
}
}
// Get all SureCart variant EUR prices (cached hourly)
$var_cache_key = 'bc_variants_grouped_v1';
$all_variants = get_transient($var_cache_key);
if (!$all_variants && function_exists('bc_get_all_variants_grouped')) {
$all_variants = bc_get_all_variants_grouped();
if (!empty($all_variants)) set_transient($var_cache_key, $all_variants, HOUR_IN_SECONDS);
}
if (!$all_variants) $all_variants = [];
$products = get_posts(['post_type' => 'sc_product', 'posts_per_page' => -1, 'post_status' => 'publish']);
$all_prices = [];
foreach ($products as $product) {
$slug = $product->post_name;
$sc_id = get_post_meta($product->ID, 'sc_id', true);
$variants = ($sc_id && isset($all_variants[$sc_id])) ? $all_variants[$sc_id] : [];
$all_meta = get_post_meta($product->ID);
// Collect manual prices from post meta
$manual = [];
foreach ($all_meta as $key => $values) {
if (preg_match('/^bc_price_(gbp|usd)_(.+)$/', $key, $m) && (float)$values[0] > 0) {
$manual[$m[2]][$m[1]] = (float) $values[0];
}
}
// Build complete price table: manual takes priority, auto-computed for the rest
foreach ($variants as $v) {
$vkey = $v['key'];
$eur = (float) $v['price_eur'];
if ($eur <= 0) continue;
$gbp = isset($manual[$vkey]['gbp']) ? $manual[$vkey]['gbp'] : (ceil(round($eur * $rate_cache['gbp'] * 2000) / 100) / 20);
$usd = isset($manual[$vkey]['usd']) ? $manual[$vkey]['usd'] : (ceil(round($eur * $rate_cache['usd'] * 2000) / 100) / 20);
$all_prices[$slug][$vkey] = ['gbp' => $gbp, 'usd' => $usd, 'eur' => $eur];
}
// Include manual-only entries for variants not in SureCart data
foreach ($manual as $vkey => $prices) {
if (!isset($all_prices[$slug][$vkey])) {
$all_prices[$slug][$vkey] = $prices;
}
}
}
echo '' . "\n";
// Free shipping thresholds
$fs_eur = (float) get_option('bc_free_ship_eur', 70);
$fs_gbp_saved = get_option('bc_free_ship_gbp', '');
$fs_usd_saved = get_option('bc_free_ship_usd', '');
$thresholds = [
'eur' => $fs_eur,
'gbp' => ($fs_gbp_saved !== '' && (float)$fs_gbp_saved > 0) ? (float)$fs_gbp_saved : floor(($fs_eur * $rate_cache['gbp']) / 5) * 5,
'usd' => ($fs_usd_saved !== '' && (float)$fs_usd_saved > 0) ? (float)$fs_usd_saved : floor(($fs_eur * $rate_cache['usd']) / 5) * 5,
];
echo '' . "\n";
}, 1);
// ── Frontend: expose shipping prices on any page using [bc_shipping] shortcode ──
add_action('wp_footer', function() {
global $post;
if (!$post || !has_shortcode($post->post_content, 'bc_shipping')) return;
$opts = get_option('bc_shipping_prices', []);
if (empty($opts)) return;
// Emit as-is — keyed by slug (ireland-standard, uk-express, etc.)
echo '' . "\n";
}, 1);
// ── Frontend: expose prices on product page (EUR + GBP + USD per variant) ──
add_action('wp_footer', function() {
if (!is_singular('sc_product')) return;
$post_id = get_the_ID();
$sc_id = get_post_meta($post_id, 'sc_id', true);
$rate_key = 'bc_fx_rates_' . date('Y-m-d');
$rate_cache = get_transient($rate_key) ?: ['gbp' => 0.871, 'usd' => 1.168];
// Reuse the variant transient already populated by cart/checkout pages
$all_variants = get_transient('bc_variants_grouped_v1');
if (!$all_variants && function_exists('bc_get_all_variants_grouped')) {
$all_variants = bc_get_all_variants_grouped();
if (!empty($all_variants)) set_transient('bc_variants_grouped_v1', $all_variants, HOUR_IN_SECONDS);
}
$product_variants = ($sc_id && !empty($all_variants[$sc_id])) ? $all_variants[$sc_id] : [];
// Manual GBP/USD overrides from post meta
$all_meta = get_post_meta($post_id);
$manual = [];
foreach ($all_meta as $key => $values) {
if (preg_match('/^bc_price_(gbp|usd)_(.+)$/', $key, $m)) {
$manual[$m[2]][$m[1]] = (float) $values[0];
}
}
// Full price table: EUR from SureCart variant data, GBP/USD manual or roundUp5
$product_prices = [];
foreach ($product_variants as $v) {
$vkey = $v['key'];
$eur = (float) $v['price_eur'];
if ($eur <= 0) continue;
$gbp = isset($manual[$vkey]['gbp']) ? $manual[$vkey]['gbp']
: (ceil(round($eur * $rate_cache['gbp'] * 2000) / 100) / 20);
$usd = isset($manual[$vkey]['usd']) ? $manual[$vkey]['usd']
: (ceil(round($eur * $rate_cache['usd'] * 2000) / 100) / 20);
$product_prices[$vkey] = ['eur' => $eur, 'gbp' => $gbp, 'usd' => $usd];
}
echo '' . "\n";
if (!empty($product_prices)) {
echo '' . "\n";
}
}, 1);
// ── Frontend: expose shop prices on shop page (keyed by min EUR price string) ──
add_action('wp_footer', function() {
if (!is_page(9)) return; // shop page ID
$products = get_posts([
'post_type' => 'sc_product',
'posts_per_page' => -1,
'post_status' => 'publish',
]);
$shop_prices = []; // keyed by min_price_amount string -> ['gbp' => x, 'usd' => y]
foreach ($products as $product) {
$min_eur = get_post_meta($product->ID, 'min_price_amount', true);
if (!$min_eur) continue;
// Get all manual prices for this product
$all_meta = get_post_meta($product->ID);
$by_variant = [];
foreach ($all_meta as $key => $values) {
if (preg_match('/^bc_price_(gbp|usd)_(.+)$/', $key, $m)) {
$by_variant[$m[2]][$m[1]] = (float) $values[0];
}
}
if (empty($by_variant)) continue;
// Find cheapest variant's manual price by matching against min_price_amount
// Sort variants — we need the one whose EUR price matches min_price_amount
$sc_id = get_post_meta($product->ID, 'sc_id', true);
if (!$sc_id) continue;
// Fetch variants to find cheapest
$response = wp_remote_get(rest_url('surecart/v1/variants?limit=100'), [
'headers' => [
'X-WP-Nonce' => wp_create_nonce('wp_rest'),
'Cookie' => isset($_SERVER['HTTP_COOKIE']) ? $_SERVER['HTTP_COOKIE'] : '',
],
]);
if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) continue;
$variants = json_decode(wp_remote_retrieve_body($response), true);
if (!is_array($variants)) continue;
// Filter variants for this product and find cheapest
$product_variants = array_filter($variants, function($v) use ($sc_id) {
return ($v['product'] ?? '') === $sc_id;
});
usort($product_variants, function($a, $b) { return $a['amount'] - $b['amount']; });
$cheapest = reset($product_variants);
if (!$cheapest) continue;
$cheapest_name = $cheapest['option_1'] ?? 'Default';
$cheapest_key = sanitize_key(str_replace(['(', ')'], '', preg_replace('/\s+/', '-', strtolower($cheapest_name))));
if (isset($by_variant[$cheapest_key])) {
$shop_prices[(string) $min_eur] = $by_variant[$cheapest_key];
}
}
if (!empty($shop_prices)) {
echo '' . "\n";
}
}, 1);
// ── Frontend: expose free shipping thresholds + method prices on checkout page ──
add_action('wp_footer', function() {
if ( ! is_page( 6 ) ) return;
/* Free shipping thresholds */
$fs_eur = (float) get_option( 'bc_free_ship_eur', 70 );
$fs_gbp_saved = get_option( 'bc_free_ship_gbp', '' );
$fs_usd_saved = get_option( 'bc_free_ship_usd', '' );
$rate_key = 'bc_fx_rates_' . date( 'Y-m-d' );
$rate_cache = get_transient( $rate_key );
if ( ! $rate_cache ) {
$resp = wp_remote_get( 'https://open.er-api.com/v6/latest/EUR', [ 'timeout' => 5 ] );
if ( ! is_wp_error( $resp ) ) {
$d = json_decode( wp_remote_retrieve_body( $resp ), true );
$rate_cache = [ 'gbp' => $d['rates']['GBP'] ?? 0.85, 'usd' => $d['rates']['USD'] ?? 1.08 ];
set_transient( $rate_key, $rate_cache, DAY_IN_SECONDS );
set_transient( 'bc_fx_rates', $rate_cache, DAY_IN_SECONDS ); /* keep undated key in sync for dashboard */
} else {
$rate_cache = [ 'gbp' => 0.871, 'usd' => 1.168 ];
}
}
$thresholds = [
'eur' => $fs_eur,
'gbp' => ( $fs_gbp_saved !== '' && (float) $fs_gbp_saved > 0 ) ? (float) $fs_gbp_saved : round( ( $fs_eur * $rate_cache['gbp'] ) / 5 ) * 5,
'usd' => ( $fs_usd_saved !== '' && (float) $fs_usd_saved > 0 ) ? (float) $fs_usd_saved : round( ( $fs_eur * $rate_cache['usd'] ) / 5 ) * 5,
];
echo '' . "\n";
/* Emit the same daily-cached rates so snippet 235 uses the identical rate as the admin/store credit pages */
echo '' . "\n";
/* Prime membership flag — used by snippet 235 for reward credit rate (5% standard, 10% Prime) */
$bc_prime_is_active = is_user_logged_in() ? (bool) get_user_meta( get_current_user_id(), 'bc_prime_active', true ) : false;
echo '' . "\n";
/* Shipping method prices — always emit full structure.
EU countries get 4.95/9.95 EUR defaults. GBP/USD use roundUp5 when not custom. */
$ship_opts = get_option( 'bc_shipping_prices', [] );
$eu_cc_p6 = [ 'AT','BE','BG','HR','CY','CZ','DK','EE','FI','FR','DE','GR',
'HU','IS','IT','LV','LI','LT','LU','MT','NL','NO','PL','PT',
'RO','SK','SI','ES','SE','CH' ];
$all_cc_p6 = array_merge( [ 'IE','GB','US','CA','AU','NZ' ], $eu_cc_p6 );
$rc = $rate_cache ?: [ 'gbp' => 0.871, 'usd' => 1.168 ];
$emit = [];
foreach ( $all_cc_p6 as $cc ) {
foreach ( [ 'standard', 'express', 'prime_delivery', 'prime_express' ] as $method ) {
$eur = isset( $ship_opts[$cc][$method]['eur'] ) ? (float)$ship_opts[$cc][$method]['eur'] : null;
if ( $eur === null && in_array( $cc, $eu_cc_p6 ) ) {
if ( $method === 'prime_delivery' ) { $emit[$cc][$method] = [ 'eur'=>0.00,'gbp'=>0.00,'usd'=>0.00 ]; continue; }
$eur = ( $method === 'standard' ) ? 4.95 : ( ( $method === 'express' ) ? 9.95 : 4.95 );
}
if ( $eur === null ) {
/* Non-EU: derive EUR from saved USD or GBP (roundUp5 = same formula as admin JS) */
$saved_usd_v = isset( $ship_opts[$cc][$method]['usd'] ) ? (float)$ship_opts[$cc][$method]['usd'] : null;
$saved_gbp_v = isset( $ship_opts[$cc][$method]['gbp'] ) ? (float)$ship_opts[$cc][$method]['gbp'] : null;
if ( $saved_usd_v ) $eur = ceil( round( $saved_usd_v / $rc['usd'] * 2000 ) / 100 ) / 20;
elseif ( $saved_gbp_v ) $eur = ceil( round( $saved_gbp_v / $rc['gbp'] * 2000 ) / 100 ) / 20;
else continue;
}
$emit[$cc][$method]['eur'] = $eur;
$emit[$cc][$method]['gbp'] = isset( $ship_opts[$cc][$method]['gbp'] )
? (float)$ship_opts[$cc][$method]['gbp']
: ceil( round( $eur * $rc['gbp'] * 2000 ) / 100 ) / 20;
$emit[$cc][$method]['usd'] = isset( $ship_opts[$cc][$method]['usd'] )
? (float)$ship_opts[$cc][$method]['usd']
: ceil( round( $eur * $rc['usd'] * 2000 ) / 100 ) / 20;
}
}
echo '' . "\n";
/* Product prices for checkout item displaydisplay — keyed by productSlug.variantKey.
Pure post_meta read: no API call, no rate-limit risk.
Snippet 235 matches via item.price.product.slug + sanitised variant_options[0]. */
$ck_products = get_posts( [ 'post_type' => 'sc_product', 'posts_per_page' => -1, 'post_status' => 'publish' ] );
$checkout_prices = [];
foreach ( $ck_products as $p ) {
$slug = $p->post_name;
$all_meta = get_post_meta( $p->ID );
foreach ( $all_meta as $key => $values ) {
if ( preg_match( '/^bc_price_(gbp|usd)_(.+)$/', $key, $m ) ) {
$checkout_prices[ $slug ][ $m[2] ][ $m[1] ] = (float) $values[0];
}
}
}
if ( ! empty( $checkout_prices ) ) {
echo '' . "\n";
}
}, 1 );
// ── Frontend: emit Prime flag on cart page so 233/632 can show discounts ──────
add_action('wp_footer', function() {
if ( is_admin() ) return;
$uri = isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '';
$is_cart_page = is_page( 193 ) || strpos( $uri, '/cart' ) !== false;
if ( ! $is_cart_page ) return;
$bc_prime_active = is_user_logged_in() ? (bool) get_user_meta( get_current_user_id(), 'bc_prime_active', true ) : false;
echo '' . "\n";
$rate_key = 'bc_fx_rates_' . date('Y-m-d');
$rate_cache = get_transient( $rate_key ) ?: ['gbp' => 0.871, 'usd' => 1.168];
echo '' . "\n";
}, 1 );