使用woocommerce_checkout_create_order_line_item钩子保存自定义字段值遇阻
WooCommerce自定义字段无法在订单详情/邮件显示,结账出现500错误
我通过产品页面自定义字段动态设置WooCommerce产品价格,其他功能正常,但无法在后台订单详情及邮件中显示自定义字段数据,Ajax响应无内容,结账后要么跳转到完成页要么出现500错误。
问题代码(保存订单元数据部分)
// Get cart item custom data and update order item meta and display in orders and emails add_action( 'woocommerce_checkout_create_order_line_item', 'save_order_item_custom_meta_data', 10, 2 ); function save_order_item_custom_meta_data( $item, $values ) { $domain = 'woocommerce'; $owner = $values['custom_data']['share_owner'] ?? ''; $getAmount = $values['custom_data']['dynamic_amount'] ?? ''; // $pmType = ''; // $pmAmount = ''; // if($owner && $getAmount){ // $pmType = __('Payment Breakdown', $domain ); // $pmAmount = '30€ + ( '. number_format((int) $getAmount) .' × 0.05€ )'; // }else{ // $pmType = __('Donation Amount', $domain ); // $pmAmount = number_format((int) $getAmount) . '€'; // } $item->update_meta_data( $owner, $getAmount ); //returns empty string }
其他相关代码
// Display custom input fields in single product page add_action( 'woocommerce_before_add_to_cart_button', 'add_product_custom_fields', 20 ); function add_product_custom_fields(){ $domain = 'woocommerce'; printf( '<div id="owner"><div class="lined-up"><input type="radio" id="yes" name="share_owner" value="1"><label for="yes">%s</label></div><div class="lined-up"><input type="radio" id="no" name="share_owner" value="0"><label for="no">%s</label></div></div>', __( 'I own share', $domain ), __( 'I do not own share', $domain ), ); $value = isset( $_POST['dynamic_amount'] ) ? sanitize_key( $_POST['dynamic_amount'] ) : ''; printf( '<div id="dyn_amount" style="display:none;"><label for="dynamic_amount">%s</label><input name="dynamic_amount" id="dynamic_amount" value="%s" min="30" type="number"/></div>', __( 'Amount', $domain ), esc_attr( $value ) ); } // Add custom fields data to cart items and make price calculation add_filter( 'woocommerce_add_cart_item_data', 'custom_add_cart_item_data', 20, 2 ); function custom_add_cart_item_data( $cart_item, $product_id ){ if( isset( $_POST['share_owner'] ) ){ $cart_item['custom_data']['share_owner'] = (int) sanitize_key( $_POST['share_owner'] ); } if( isset( $_POST['dynamic_amount'] ) ){ $cart_item['custom_data']['dynamic_amount'] = sanitize_key( $_POST['dynamic_amount'] ); } // Make calculation and save calculated price if( isset( $_POST['share_owner'] ) && isset( $_POST['dynamic_amount'] ) ){ $own_share = (int) sanitize_key( $_POST['share_owner'] ); $dynamic_amount = (int) sanitize_key( $_POST['dynamic_amount'] ); //If selected share owner we need to calculate price according to input value if( $own_share ){ // calculating the amount (share amount * 0.05) + 30 $total_price = ( ( $dynamic_amount * 0.05 ) + 30.0 ); $cart_item['custom_data']['price'] = round($total_price, 2); } else{ //Otherwise its a donation, no calculation needed $total_price = $dynamic_amount; $cart_item['custom_data']['price'] = round($total_price, 2); } } return $cart_item; } // Display custom data in cart and checkout pages add_filter( 'woocommerce_get_item_data', 'udaac_display_custom_cart_item_data', 10, 2 ); function udaac_display_custom_cart_item_data( $cart_data, $cart_item ) { $domain = 'woocommerce'; $input_amount = $cart_item['custom_data']['dynamic_amount'] ?? ''; if ( isset( $cart_item['custom_data']['share_owner'] ) ){ $owner = (int) $cart_item['custom_data']['share_owner']; $paymentType = ''; $paymentOrShareAmount = ''; if($owner){ $paymentType = __('Payment Breakdown', $domain ); $paymentOrShareAmount = '30&euro; + ( '. number_format((int) $input_amount) .' &times; 0.05&euro; )'; }else{ $paymentType = __('Donation Amount', $domain ); $paymentOrShareAmount = number_format((int) $input_amount) . '&euro;'; } $cart_data[] = array( 'name' => $paymentType, 'value' => $paymentOrShareAmount ); } return $cart_data; } // Set the new calculated price replacing cart item price add_action( 'woocommerce_before_calculate_totals', 'udaac_set_cart_item_calculated_price', 20, 1 ); function udaac_set_cart_item_calculated_price( $cart ) { if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return; if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 ) return; // Loop through cart items foreach ( $cart->get_cart() as $cart_item ){ if( ! isset( $cart_item['custom_data']['price'] ) ){ continue; } if( $cart_item['custom_data']['price'] > 0 ){ // Set the calculated item price (if there is one) $cart_item['data']->set_price( (float) $cart_item['custom_data']['price'] ); } } }
问题分析与修复方案
核心问题点
update_meta_data参数错误:用$owner(值为0或1)作为元数据键,不符合WooCommerce元数据规范,会导致存储异常甚至触发500错误。- 未处理空值情况:当
$owner或$getAmount为空时,直接调用update_meta_data会产生无效数据。 - HTML实体未转换:注释部分的
&euro;等实体未转换为实际字符,会导致显示异常。
修正后的代码(替换原保存订单元数据的函数)
// Get cart item custom data and update order item meta and display in orders and emails add_action( 'woocommerce_checkout_create_order_line_item', 'save_order_item_custom_meta_data', 10, 3 ); function save_order_item_custom_meta_data( $item, $cart_item_key, $values ) { $domain = 'woocommerce'; // 安全获取自定义数据,避免未定义索引警告 $owner = $values['custom_data']['share_owner'] ?? 0; $getAmount = $values['custom_data']['dynamic_amount'] ?? ''; if ( empty($getAmount) ) return; // 金额为空时不处理 $owner_label = $owner ? __('I own share', $domain) : __('I do not own share', $domain); // 添加所有权状态元数据 $item->update_meta_data( __('Share Owner Status', $domain), $owner_label ); // 根据状态生成金额描述 if( $owner ){ $pmType = __('Payment Breakdown', $domain ); $pmAmount = '30€ + ( '. number_format((int) $getAmount) .' × 0.05€ )'; }else{ $pmType = __('Donation Amount', $domain ); $pmAmount = number_format((int) $getAmount) . '€'; } // 添加金额明细元数据 $item->update_meta_data( $pmType, $pmAmount ); }
额外优化建议
- 启用调试模式排查错误:在
wp-config.php中添加以下代码,查看具体的500错误日志:
define( 'WP_DEBUG', true ); define( 'WP_DEBUG_LOG', true ); define( 'WP_DEBUG_DISPLAY', false );
- 数据 sanitize 优化:
sanitize_key不适合处理数字金额,改用absint()处理整数金额,确保数据安全:
$cart_item['custom_data']['dynamic_amount'] = absint( $_POST['dynamic_amount'] );
- 前端字段验证:添加JavaScript验证,确保用户选择选项并输入有效金额后才能加入购物车,避免后端处理空数据。
内容的提问来源于stack exchange,提问作者mRashid
相关产品推荐
相关产品推荐

