如何避免Stripe Webhook触发后cURL重复调用RESTful API?
Stripe Webhook触发后cURL重复调用API的问题排查与解决
我在Stripe支付完成后,由Stripe Webhook触发一段cURL代码调用RESTful API,但出现了cURL两次调用API的异常情况——支付仅扣费一次,但API被执行了两次。相关PHP代码如下:
$url = "RESTFUL_API_URL"; // Init cURL session $curl = curl_init(); // Create array of options for the cURL session curl_setopt_array($curl, array( CURLOPT_HTTPHEADER => array('Accept: application/json'), CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, //CURLOPT_MAXREDIRS => 10, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_TIMEOUT => 60, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0, CURLOPT_CUSTOMREQUEST => "GET" )); // Execute cURL session and store the response in $response $response = curl_exec($curl); // If the operation failed, store the error message in $error $error = curl_error($curl); // Close cURL session curl_close($curl); if ($error) { echo "cURL Error: " . $error; } else { $response = Json::decode($response, true); }
可能的原因
- Stripe Webhook重试机制:Stripe要求Webhook在10秒内返回2xx状态码,若脚本处理过慢或未及时响应,Stripe会自动重复发送事件,导致cURL被执行多次。
- 缺少幂等性校验:没有对Stripe事件ID做去重处理,同一条支付事件可能被重复接收并处理,进而触发多次cURL调用。
- cURL重定向(可能性低):虽然默认关闭了重定向跟随,但如果API地址存在隐性重定向,也可能导致看似两次调用的情况。
解决措施
1. 优先返回响应给Stripe
在Webhook脚本最开头就返回200状态码,避免Stripe触发重试:
// 先给Stripe返回200响应,防止重试 http_response_code(200); // 强制输出响应,避免缓冲延迟 flush(); ob_flush(); // 再执行后续的cURL逻辑 $url = "RESTFUL_API_URL"; // ... 剩余cURL代码
2. 添加事件幂等性校验
通过数据库记录已处理的Stripe事件ID,确保同一条事件只处理一次:
// 从Stripe Webhook请求中解析事件ID $event = json_decode(file_get_contents('php://input'), true); $eventId = $event['id']; // 用数据库存储已处理事件 $pdo = new PDO('mysql:host=localhost;dbname=your_database', 'username', 'password'); $stmt = $pdo->prepare("SELECT COUNT(*) FROM processed_stripe_events WHERE event_id = ?"); $stmt->execute([$eventId]); $processed = $stmt->fetchColumn(); if ($processed > 0) { http_response_code(200); exit; // 已处理过,直接退出 } // 记录事件ID到数据库 $stmt = $pdo->prepare("INSERT INTO processed_stripe_events (event_id, created_at) VALUES (?, NOW())"); $stmt->execute([$eventId]); // 继续执行cURL逻辑 // ... 剩余代码
3. 排查cURL重定向问题
开启cURL的详细日志,检查是否存在重定向导致的多次请求:
curl_setopt_array($curl, array( CURLOPT_HTTPHEADER => array('Accept: application/json'), CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_MAXREDIRS => 3, // 限制最大重定向次数 CURLOPT_FOLLOWLOCATION => true, // 明确是否允许跟随重定向 CURLOPT_SSL_VERIFYPEER => false, CURLOPT_TIMEOUT => 60, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0, CURLOPT_CUSTOMREQUEST => "GET", CURLOPT_VERBOSE => true, CURLOPT_STDERR => fopen('curl_debug.log', 'w') // 写入调试日志 ));
查看生成的curl_debug.log,确认是否有多次请求的记录。
4. 优化脚本处理效率
如果cURL调用的API处理较慢,可考虑异步执行(比如用消息队列),避免脚本超时导致Stripe重试。
内容的提问来源于stack exchange,提问作者Shaho
相关产品推荐
相关产品推荐

