搜索全站
建站 14 分钟阅读

外贸站多币种与报价展示:币种切换、价格换算与缓存冲突处理

多币种做好了能提升询盘,做错了会让客户以为你在乱报价。本文讲清 B2B 与 B2C 两种场景的币种策略,附不依赖插件的切换实现。

U
UFO_PF 2017-07-27 · 阅读 14 分钟
外贸 建站
阅读提示
本文约 5,224 字,预计阅读 14 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:客户看到的价格,首先要”可信”

外贸站展示多币种,动机通常是好的:让美国客户看到美元、欧洲客户看到欧元,降低换算成本。但落地时经常出问题:汇率是手动填的,三个月没更新,客户一算发现比市场价高 8%;或者切换币种后购物车价格对不上,结账时又跳回美元,客户以为被套路。价格是信任的最后一道防线,一旦客户觉得你的报价”不准”,前面的所有努力都白费。所以多币种的核心不是”能切换”,而是”汇率可信、全程一致、边界清晰”。

原理分析:B2B 与 B2C 的币种需求完全不同

B2C 零售必须实时结算:客户用美元下单,就用美元扣款,所以必须接多币种支付通道,价格与结算货币一致。

B2B 询盘则不同:绝大多数订单走电汇,币种只是”报价参考”。这时做实时汇率反而是负担——汇率每天都在动,你今天报 100 美元,客户下周才下单,价格可能已经变了。B2B 更常见的做法是:以美元为基准报价,页面展示一个”参考汇率”换算的本地货币,并明确标注”仅供参考,最终以确认单为准”。

场景 币种策略 是否需要实时汇率
B2C 零售 客户币种结算 必须
B2B 询盘 基准币种报价 + 参考换算 建议,但需标注
批发议价 只展示基准币种 不需要

WooCommerce 提供了两个关键过滤器:woocommerce_currency 用于切换当前显示的货币代码,woocommerce_product_get_price 用于在读取价格时做换算。这两个过滤器配合,就能实现一套不依赖插件的轻量切换,非常适合 B2B 展示型需求。

保姆级实操

步骤 1:确定基准货币。 在”WooCommerce → Settings → General”里把 Currency 设为 USD(或你的实际结算币种)。所有价格以后都按这个币种录入。

步骤 2:存汇率。 汇率不要写死在代码里,存进 option,用每日定时任务更新。

# 每日更新汇率到 option(由系统 cron 触发)
wp cron event list --fields=hook,next_run --format=table
wp option get mmf_fx_rates

步骤 3:拉取并缓存汇率。 用 WordPress 的 HTTP API,失败时保留上一次的值,绝不因为接口挂掉就显示错误价格。

function mimangfei_refresh_fx_rates() {
    $base  = 'USD';
    $symbols = 'EUR,GBP,JPY,AUD,CAD';
    $url   = 'https://api.exchangerate.host/latest?base=' . $base . '&symbols=' . $symbols;

    $response = wp_remote_get( $url, array( 'timeout' => 10 ) );
    if ( is_wp_error( $response ) ) {
        return false;
    }
    $code = wp_remote_retrieve_response_code( $response );
    if ( 200 !== $code ) {
        return false;
    }
    $body = json_decode( wp_remote_retrieve_body( $response ), true );
    if ( empty( $body['rates'] ) || ! is_array( $body['rates'] ) ) {
        return false;
    }

    $rates = array_map( 'floatval', $body['rates'] );
    $rates['USD'] = 1.0;
    update_option( 'mmf_fx_rates', $rates );
    update_option( 'mmf_fx_updated', current_time( 'mysql' ) );
    return true;
}

步骤 4:注册每日更新任务。

if ( ! wp_next_scheduled( 'mmf_daily_fx_update' ) ) {
    wp_schedule_event( strtotime( '02:30:00' ), 'daily', 'mmf_daily_fx_update' );
}
add_action( 'mmf_daily_fx_update', 'mimangfei_refresh_fx_rates' );

步骤 5:实现币种切换。 用 cookie 记住用户选择,再用 woocommerce_currency 过滤器切换显示币种。

add_filter( 'woocommerce_currency', 'mimangfei_switch_currency' );
function mimangfei_switch_currency( $currency ) {
    $allowed = array( 'USD', 'EUR', 'GBP', 'JPY', 'AUD', 'CAD' );
    $picked  = isset( $_COOKIE['mmf_currency'] ) ? strtoupper( sanitize_key( $_COOKIE['mmf_currency'] ) ) : '';
    if ( in_array( $picked, $allowed, true ) ) {
        return $picked;
    }
    return $currency;
}

步骤 6:换算价格。 用 woocommerce_product_get_price 过滤器,按汇率换算并按币种做尾数取整(日元没有小数,美元保留两位)。

add_filter( 'woocommerce_product_get_price', 'mimangfei_convert_price', 10, 2 );
function mimangfei_convert_price( $price, $product ) {
    if ( '' === $price || ! is_numeric( $price ) ) {
        return $price;
    }
    $rates = (array) get_option( 'mmf_fx_rates', array() );
    $target = apply_filters( 'woocommerce_currency', get_option( 'woocommerce_currency' ) );
    $base   = 'USD';

    if ( $target === $base || empty( $rates[ $target ] ) ) {
        return $price;
    }
    $converted = (float) $price * (float) $rates[ $target ];

    // 按币种处理小数位与尾数
    if ( in_array( $target, array( 'JPY' ), true ) ) {
        return ceil( $converted );
    }
    return round( $converted, 2 );
}

步骤 7:处理缓存冲突。 这是最容易出错的一环:同一 URL 对不同币种返回不同价格,页面缓存会把美元版本发给选了欧元的客户。解决方案是按币种做缓存变体——把币种写进 cookie 并在缓存层按该 cookie 区分。

# 按币种 cookie 区分缓存变体
map $http_cookie $mmf_currency_key {
    default                "usd";
    "~*mmf_currency=EUR"   "eur";
    "~*mmf_currency=GBP"   "gbp";
    "~*mmf_currency=JPY"   "jpy";
}

步骤 8:展示汇率更新时间与免责声明。 在价格下方小字标注 “Converted at 1 USD = X EUR, updated 2026-01-01. For reference only.”,这句声明能消除绝大部分争议。

add_action( 'woocommerce_single_product_summary', 'mimangfei_fx_notice', 31 );
function mimangfei_fx_notice() {
    $rates   = (array) get_option( 'mmf_fx_rates', array() );
    $updated = get_option( 'mmf_fx_updated' );
    $target  = apply_filters( 'woocommerce_currency', get_option( 'woocommerce_currency' ) );
    if ( 'USD' === $target || empty( $rates[ $target ] ) || ! $updated ) {
        return;
    }
    printf(
        '<p class="fx-note">Converted at 1 USD = %s %s (updated %s). For reference only.</p>',
        esc_html( number_format( (float) $rates[ $target ], 4 ) ),
        esc_html( $target ),
        esc_html( mysql2date( 'Y-m-d', $updated ) )
    );
}

步骤 9:B2C 场景用现成插件。 如果确实需要多币种结算,用 WooCommerce 官方的 WooCommerce Payments、或 WPML 的 WooCommerce Multilingual & Multicurrency、Aelia Currency Switcher 这类成熟方案,不要自研结算逻辑。

常见踩坑

  1. 汇率长期不更新。 客户发现价格偏离市场价,直接质疑你的专业性。
  2. 缓存导致币种串台。 选了欧元却显示美元价格,必须按币种做缓存变体。
  3. 结算币种与展示币种不一致。 页面显示欧元,扣款按美元,是典型的信任事故。
  4. 案例: 某外贸站手动填了汇率两年未更新,美元对欧元汇率变动超过 10%,欧洲客户按页面价格做预算,实际签约时价格高出一大截,导致两笔订单直接谈崩。改成了每日自动更新加明确标注更新时间。
  5. 接口失败时显示错误价格。 wp_remote_get 失败后不能回落到默认值 1,应保留上一次成功的值。
  6. B2B 站做了实时结算。 反而增加了客户 confusion,报价展示就够了。
  7. 忽略了货币符号与格式。 欧元符号在部分欧洲国家放在数字后面,需要用 woocommerce_currency_symbol 过滤器调整。

总结

多币种的策略先分场景:B2B 只做”基准报价 + 参考换算 + 更新时间标注”,B2C 才做实时结算并用成熟插件。技术上用 option 存汇率、每日定时更新、woocommerce_currency 与 woocommerce_product_get_price 两个过滤器切换,缓存层必须按币种区分变体。判断标准:客户在任何币种下看到的价格,都能追溯到明确的汇率与更新时间。产品页的价格展示方式见外贸产品页结构设计;面向欧盟客户的合规要求见外贸站合规页面清单。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。