// ── 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 );