搜索全站
安装部署 12 分钟阅读

WordPress 优惠券返利站搭建:数据源对接、自动更新与合规红线

券码过期比想象中快,人工更新根本追不上。本文讲如何用联盟开放平台的 API 拉券、用 WP-Cron 定时同步、用 Transients 做缓存与失效兜底,并厘清返利站的几处合规红线。

U
UFO_PF 2025-02-25 · 阅读 12 分钟
Wo 安装部署
阅读提示
本文约 4,726 字,预计阅读 12 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:写着”满 200 减 30″的券已经失效两周了

做优惠券/返利站,最伤害信任的场景不是没券,而是有券但不能用。用户从搜索进来,复制券码去结算,发现提示”优惠券已过期”,跳出率接近满分。这类站点的内容半衰期极短:今天有效的券明天可能就没了,一天两三百条人工更新几乎是极限,规模做不上去。唯一可行的路线是把数据源接到 API 上,由程序定时刷新 + 过期自动下线,把人从复制粘贴里解放出来做选品与页面质量。

原理分析:券站本质是「定时 ETL + 页面生成」

整个系统分三层。第一层是数据源:电商联盟平台通常提供券/商品接口,例如淘宝开放平台的 taobao.tbk.item.get(淘宝客物料搜索接口),京东联盟的 jd.union.open.goods.query,海外则是 Amazon Product Advertising API v5 或各家 Affiliate Network(如 Awin、CJ Affiliate)的商品与促销 feed。第二层是调度与存储:WordPress 的定时任务 WP-Cron + 自定义数据表,负责拉数据、过滤、落库。第三层是缓存与呈现:Transients API 承载热数据,页面按商品库/品牌/券区/活动专区生成。

需要注意的是,WordPress 默认的 cron 由页面访问触发,纯静态缓存站点可能长时间不跑 wp-cron.php。成熟的解法是在系统 crontab 里定时 wget -q -O - https://example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1,并在 wp-config.php 里用 define( 'DISABLE_WP_CRON', true ); 关闭默认机制。

环节 使用方式 备注
调度 WP-Cron + 系统 crontab 前者易用,后者准时
存储 自定义表($wpdb->prefix . 'mf_coupons') 量大时别塞 postmeta
缓存 set_transient() / get_transient() 没装外部对象缓存时写入 options 表
失效 券 end_time 字段 + 查询过滤 不应删除,留作历史

保姆级实操

步骤 1:确认数据源资质与调用配额。 国内平台普遍要求先注册开发者、创建应用拿到 appkey/appsecret,并对调用频次做限流;海外 PA-API v5 还要求站点有实际销售才会放款。拿到凭证后先 Postman 跑通一次。

步骤 2:建自定义表而不是堆 postmeta。 券这种几千上万条的数据写进 postmeta 会让 meta_query 慢到无法接受:

register_activation_hook( __FILE__, function () {
    global $wpdb;
    require_once ABSPATH . 'wp-admin/includes/upgrade.php';
    $charset = $wpdb->get_charset_collate();
    dbDelta( "CREATE TABLE {$wpdb->prefix}mf_coupons (
        id bigint(20) NOT NULL AUTO_INCREMENT,
        source varchar(32) NOT NULL,
        external_id varchar(64) NOT NULL,
        title varchar(255) NOT NULL,
        coupon_amount varchar(64) DEFAULT '',
        start_time datetime DEFAULT NULL,
        end_time datetime DEFAULT NULL,
        click_url text,
        updated_at datetime NOT NULL,
        PRIMARY KEY  (id),
        UNIQUE KEY src_ext (source, external_id),
        KEY end_time (end_time)
    ) $charset;" );
} );

步骤 3:注册定时任务并写好幂等逻辑。 拉券要做到「重复跑不脏库」,用 upsert(ON DUPLICATE KEY UPDATE):

add_action( 'init', function () {
    if ( ! wp_next_scheduled( 'mf_sync_coupons_hourly' ) ) {
        wp_schedule_event( time(), 'hourly', 'mf_sync_coupons_hourly' );
    }
} );

add_action( 'mf_sync_coupons_hourly', function () {
    global $wpdb;
    $resp = wp_remote_post( 'https://api.example-network.com/v1/promotions/search', [
        'timeout' => 15,
        'headers' => [ 'Authorization' => 'Bearer ' . get_option( 'mf_api_token' ) ],
        'body'    => [ 'page_size' => 100, 'sort_tag' => 'total_sales_des' ],
    ] );
    if ( is_wp_error( $resp ) || 200 !== (int) wp_remote_retrieve_response_code( $resp ) ) {
        error_log( '券同步失败:上游不可达' );
        return;
    }
    $rows = json_decode( wp_remote_retrieve_body( $resp ), true );
    foreach ( (array) ( $rows['result_list'] ?? [] ) as $item ) {
        $wpdb->query( $wpdb->prepare(
            "INSERT INTO {$wpdb->prefix}mf_coupons
             (source, external_id, title, coupon_amount, start_time, end_time, click_url, updated_at)
             VALUES (%s,%s,%s,%s,%s,%s,%s,%s)
             ON DUPLICATE KEY UPDATE
             coupon_amount = VALUES(coupon_amount), end_time = VALUES(end_time),
             click_url = VALUES(click_url), updated_at = VALUES(updated_at)",
            'example', $item['promotion_id'], $item['title'], $item['discount'],
            $item['start_time'], $item['end_time'], $item['click_url'], current_time( 'mysql', 1 )
        ) );
    }
    delete_transient( 'mf_coupon_cache_home' );
} );

注意这里要用 wp_remote_retrieve_response_code() 取值,传的是 wp_remote_post() 返回的整个响应对象,而不是响应体字符串。

步骤 4:页面读取时先查 transient。 热区数据不直接打数据库:

$cache = get_transient( 'mf_coupon_cache_home' );
if ( false === $cache ) {
    global $wpdb;
    $cache = $wpdb->get_results(
        "SELECT * FROM {$wpdb->prefix}mf_coupons
         WHERE end_time > UTC_TIMESTAMP()
         ORDER BY end_time ASC LIMIT 20"
    );
    set_transient( 'mf_coupon_cache_home', $cache, 15 * MINUTE_IN_SECONDS );
}

步骤 5:过期券的处置策略。 不要物理删除,改成查询时过滤 end_time,并对已过期超过 30 天的 URL 返回 410 Gone 或 301 到同类页,避免留下大量死链。WordPress 404 与 301 重定向处理

步骤 6:合规红线。 禁止对券面额度做夸大表述,禁止使用”全网最低””第一”等极限词;返利要与平台规则一致,不得虚构返利比例;页面需说明价格与优惠信息以商家结算页为准。

常见踩坑

  1. 把 wp-cron.php 交给页面访问触发,站点上了静态缓存后任务长期不执行,表现为”券列表一整天没变化”。
  2. 案例: 某站把券数据全写进 postmeta,三个月后 EXPLAIN 显示单次查询扫描行数超过百万,列表页 TTFB 从 200ms 涨到 6 秒以上。
  3. set_transient() 第二个参数传了数组却忘了过期时间,等价于永不过期,缓存击穿。
  4. 上游 API 返回分页 100 条但只处理了第一页,热门券永远进不来。
  5. dbDelta 的 KEY 行写成了逗号结尾,导致索引未创建,慢查询日志暴涨。
  6. 未处理时区,current_time() 与 API 的 UTC 串混淆,券提前或延后失效。
  7. 同一个券被多次入库只靠 title 去重,导致标题相同、额度不同的券互相覆盖。WordPress 定时任务 WP-Cron 详解

总结

券站能否长期存活,取决于三点:数据是否自动更新且幂等、过期券是否不留聚合死链、表达是否不越合规边界。行动清单:确认 API 配额 → 自定义表 + upsert → 系统级 crontab → transient 缓存 → 过期策略 → 文案合规审查。先把”不出错”做到位,规模是自然结果。

U
UFO_PF作者

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

留下你的想法

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