WooCommerce自定义支付网关IPN回调返回-1无法处理求助
解决WooCommerce支付网关回调未触发及URL配置问题
问题诊断
- 回调URL格式错误:你收到的回调URL里出现了两个
?(http://localhost:8000/?wc-api=WC_Plugin_Gateway?x_account_id=...),这会导致WooCommerce无法正确识别API端点——第二个?应替换为&,否则参数无法被解析,钩子woocommerce_api_wc_plugin_gateway不会触发。 - 三个URL混淆使用:
x_url_callback(服务器端IPN回调)、x_url_complete(用户成功跳转页)、x_url_cancel(用户取消跳转页)不应全部指向同一个notify_url,需按用途区分配置。 - 回调逻辑缺失验证与业务处理:当前代码仅输出日志,未验证网关签名,也未处理订单状态更新,既不安全也无法完成业务闭环。
修复步骤
1. 修正回调URL的生成与配置
确保notify_url是正确的WooCommerce API端点,同时区分三个URL的用途:
class WC_Plugin_Gateway extends \WC_Payment_Gateway{ public function __construct(){ // 生成服务器端IPN用的回调地址 $this->notify_url = WC()->api_request_url('WC_Plugin_Gateway'); // 注册回调钩子,类名转小写后拼接钩子前缀 add_action('woocommerce_api_' . strtolower(__CLASS__), array($this, 'handle_callback')); // 配置用户跳转的成功/取消页面(用WooCommerce默认页或自定义页) $this->return_url = $this->get_return_url(null); $this->cancel_url = wc_get_page_permalink('cart'); } // 构建支付请求数据时区分三个URL public function process_payment($order_id){ $order = wc_get_order($order_id); $email = $order->get_billing_email(); $nombre_customer = $order->get_billing_first_name(); $apellido = $order->get_billing_last_name(); $telefono = $order->get_billing_phone(); $monto = $order->get_total(); $cadenaProductos = '订单 #' . $order_id; $nombreSitio = get_bloginfo('name'); $shop_country = WC()->countries->get_base_country(); $new_data = array( "platform" => "woocommerce", "paymentMethod" => "webpay", "x_account_id" => $this->token_service, "x_amount" => round($monto), "x_currency" => get_woocommerce_currency(), "x_customer_email" => $email, "x_customer_first_name" => $nombre_customer, "x_customer_last_name" => $apellido, "x_customer_phone" => $telefono, "x_description" => $cadenaProductos, "x_reference" => $order_id, "x_shop_country" => !empty($shop_country) ? $shop_country : 'CL', "x_shop_name" => $nombreSitio, // 服务器端IPN回调,用于后台更新订单状态 "x_url_callback" => $this->notify_url, // 用户取消支付后跳转回购物车 "x_url_cancel" => $this->cancel_url, // 用户支付成功后跳转回订单完成页 "x_url_complete" => $this->return_url, "secret" => $_ENV['SECRET'] ); // 此处添加生成支付链接并跳转的逻辑 // ... return array( 'result' => 'success', 'redirect' => '生成的支付链接' ); } }
2. 完善回调处理逻辑(含签名验证)
修复URL参数解析问题,添加签名验证,实现订单状态更新:
public function handle_callback(){ // 修复URL参数:将错误的第二个?替换为& $request_uri = $_SERVER['REQUEST_URI']; $request_uri = str_replace('?wc-api=WC_Plugin_Gateway?', '?wc-api=WC_Plugin_Gateway&', $request_uri); parse_str(parse_url($request_uri, PHP_URL_QUERY), $params); // 验证请求签名(必须实现,防止伪造回调) $received_signature = $params['x_signature'] ?? ''; $expected_signature = $this->generate_signature($params); if($received_signature !== $expected_signature){ error_log('Invalid callback signature'); wp_die('Invalid signature', 'Error', array('response' => 403)); } // 处理回调参数 $order_id = $params['x_reference'] ?? ''; $result = strtolower($params['x_result'] ?? ''); $order = wc_get_order($order_id); if(!$order){ error_log('Order not found for callback: ' . $order_id); wp_die('Order not found', 'Error', array('response' => 404)); } // 根据支付结果更新订单状态 switch($result){ case 'success': $order->payment_complete(); $order->add_order_note('支付成功,网关回调确认'); break; case 'failed': $error_msg = $params['x_message'] ?? '未知原因'; $order->update_status('failed', "支付失败:{$error_msg}"); break; case 'cancelled': $order->update_status('cancelled', '用户取消支付'); break; default: $order->add_order_note('收到未知支付状态:' . $result); break; } // 返回200状态码告知网关回调已接收 header('HTTP/1.1 200 OK'); wp_die(); } // 参考网关文档实现签名生成逻辑 private function generate_signature($params){ // 示例:按网关要求排序参数、拼接字符串后生成签名 ksort($params); $signature_string = ''; foreach($params as $key => $value){ if($key !== 'x_signature'){ $signature_string .= "{$key}={$value}&"; } } $signature_string = rtrim($signature_string, '&') . $_ENV['SECRET']; return hash('sha256', $signature_string); // 替换为网关指定的哈希算法 }
3. 排查网关端参数拼接问题
如果修复后仍收到带有两个?的URL,说明网关在拼接回调参数时存在错误,需检查网关文档确认参数拼接规则,或联系网关技术支持修正——正确的回调URL格式应为:http://localhost:8000/?wc-api=WC_Plugin_Gateway&x_account_id=secret&x_amount=1230.0&...
测试方法
- 开启WordPress调试日志(
wp-config.php中设置WP_DEBUG_LOG = true),查看wp-content/debug.log是否有回调相关日志输出。 - 用Postman直接向
notify_url发送模拟请求,验证钩子是否触发。 - 完整测试支付流程,检查订单状态是否正确更新。
内容的提问来源于stack exchange,提问作者Fabian Andres
相关产品推荐
相关产品推荐

