WooCommerce:实现虚拟可下载产品动态差异化文件交付的技术问询
解决WooCommerce分发唯一数字码(如Steam码)的方案
这个问题我之前帮客户解决过——WooCommerce默认的可下载产品机制确实只能绑定固定文件,没法直接给每个订单分发唯一的数字码文件。下面给你几个实用的解决方案,从无代码到定制开发都覆盖:
方案1:用专门的数字码管理插件(无代码入门首选)
如果你不想写代码,直接找一款WooCommerce数字密钥/序列号管理插件就行,这类工具专门解决这个场景:
- 支持批量导入预生成的唯一码(比如Steam码),存储在插件的码库中
- 订单付款完成后,自动从码库中分配一个未使用的码,标记为已用
- 可以自动生成包含该码的PDF下载链接,直接添加到订单页面或发送到客户邮箱
- 还能同步产品库存(码库耗尽时自动下架产品)
这类插件的逻辑都是现成的,不用自己折腾,适合新手或者快速上线的场景。
方案2:自定义代码实现(适合需要高度定制的场景)
如果你有开发能力,完全可以自己写代码实现,核心逻辑是预存码库→订单完成时分配唯一码→生成带码的PDF→添加到订单下载项。
步骤1:给产品添加自定义字段存储可用码库
先在产品编辑页面加一个输入框,让商家批量输入所有可用的数字码(每行一个):
// 添加产品元框用于存储Steam码库 add_action('add_meta_boxes', 'add_steam_keys_meta_box'); function add_steam_keys_meta_box() { add_meta_box( 'steam_keys_box', 'Steam 数字码库', 'render_steam_keys_meta_box', 'product', 'normal', 'high' ); } // 渲染元框内容 function render_steam_keys_meta_box($post) { $available_keys = get_post_meta($post->ID, '_steam_available_keys', true); wp_nonce_field('save_steam_keys_nonce', 'steam_keys_nonce'); echo '<textarea name="steam_available_keys" rows="12" style="width:100%;">' . esc_textarea($available_keys) . '</textarea>'; echo '<p style="margin-top:10px; color:#666;">请每行输入一个唯一的Steam码,系统会自动为订单分配未使用的码</p>'; } // 保存码库到产品元数据 add_action('save_post_product', 'save_steam_keys_meta_data'); function save_steam_keys_meta_data($post_id) { // 验证权限和非ce if (!isset($_POST['steam_keys_nonce']) || !wp_verify_nonce($_POST['steam_keys_nonce'], 'save_steam_keys_nonce')) return; if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return; if (!current_user_can('edit_product', $post_id)) return; // 保存码库 if (isset($_POST['steam_available_keys'])) { update_post_meta($post_id, '_steam_available_keys', sanitize_textarea_field($_POST['steam_available_keys'])); } }
步骤2:订单完成时分配码并生成PDF
当订单状态变为「已完成」时,自动分配码、生成带码的PDF,并把PDF添加为订单的可下载项:
// 订单完成时分配Steam码并生成下载PDF add_action('woocommerce_order_status_completed', 'assign_steam_key_and_generate_pdf', 10, 1); function assign_steam_key_and_generate_pdf($order_id) { $order = wc_get_order($order_id); if (!$order) return; foreach ($order->get_items() as $item) { $product_id = $item->get_product_id(); $available_keys = get_post_meta($product_id, '_steam_available_keys', true); // 检查码库是否为空 if (empty($available_keys)) { wc_add_notice('该产品的数字码已耗尽,请补充库存', 'error'); return; } // 拆分码库为数组,取出第一个可用码 $keys_array = array_filter(explode("\n", trim($available_keys))); $assigned_key = array_shift($keys_array); // 更新剩余码库 update_post_meta($product_id, '_steam_available_keys', implode("\n", $keys_array)); // 把分配的码保存到订单元数据 update_post_meta($order_id, '_assigned_steam_key', $assigned_key); // 生成带码的PDF(这里用TCPDF为例,需先安装TCPDF库到wp-content/plugins/) require_once(ABSPATH . 'wp-content/plugins/tcpdf/tcpdf.php'); $pdf = new TCPDF(); $pdf->AddPage(); // 构造PDF内容 $pdf_content = '<h1>感谢你的购买!</h1>'; $pdf_content .= '<p>你的Steam激活码:</p>'; $pdf_content .= '<p style="font-size:24px; font-weight:bold; color:#2c3e50; margin:20px 0;">' . esc_html($assigned_key) . '</p>'; $pdf_content .= '<p>请在Steam客户端中输入此码激活游戏</p>'; $pdf->writeHTML($pdf_content, true, false, true, false, ''); // 保存PDF到上传目录 $upload_dir = wp_upload_dir(); $pdf_filename = 'steam-key-' . $order_id . '.pdf'; $pdf_path = $upload_dir['path'] . '/' . $pdf_filename; $pdf->Output($pdf_path, 'F'); // 添加PDF到订单的可下载项 $download_url = $upload_dir['url'] . '/' . $pdf_filename; $item->add_download(array( 'name' => '你的Steam激活码PDF', 'file' => $download_url )); $item->save(); } $order->save(); }
额外优化:清理过期PDF
为了避免服务器存储过多旧PDF,可以加个定时任务自动删除30天前的PDF:
// 定时删除过期的Steam码PDF add_action('wp_scheduled_delete_steam_pdfs', 'delete_old_steam_pdfs'); function delete_old_steam_pdfs() { $upload_dir = wp_upload_dir(); $files = glob($upload_dir['path'] . '/steam-key-*.pdf'); if ($files) { foreach ($files as $file) { if (filemtime($file) < strtotime('-30 days')) { unlink($file); } } } } // 激活定时任务 register_activation_hook(__FILE__, 'schedule_steam_pdf_cleanup'); function schedule_steam_pdf_cleanup() { if (!wp_next_scheduled('wp_scheduled_delete_steam_pdfs')) { wp_schedule_event(time(), 'daily', 'wp_scheduled_delete_steam_pdfs'); } }
方案3:直接在订单邮件中发送码(替代PDF下载)
如果不需要生成PDF,也可以直接把唯一码放到订单完成邮件里,这样更简单:
// 在客户完成订单邮件中插入Steam码 add_action('woocommerce_email_order_details', 'add_steam_key_to_completion_email', 20, 4); function add_steam_key_to_completion_email($order, $sent_to_admin, $plain_text, $email) { // 只在客户的完成订单邮件中显示 if ($email->id !== 'customer_completed_order') return; $assigned_key = get_post_meta($order->get_id(), '_assigned_steam_key', true); if (!empty($assigned_key)) { if ($plain_text) { echo "\n\n你的Steam激活码:" . $assigned_key . "\n"; } else { echo '<div style="margin:20px 0; padding:15px; background:#f8f9fa; border-radius:4px;">'; echo '<h3 style="margin-top:0;">你的Steam激活码</h3>'; echo '<p style="font-size:20px; font-weight:bold; color:#dc3545;">' . esc_html($assigned_key) . '</p>'; echo '</div>'; } } }
内容的提问来源于stack exchange,提问作者Khoontay
相关产品推荐
相关产品推荐

