You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在用户离开页面时取消待处理的cURL支付请求?

How to Cancel a Pending cURL Payment Request When User Leaves the Page

Great question! Handling cancelation of pending payment requests when a user leaves the page involves coordinating between your frontend (to detect page exit) and backend (to terminate the in-progress cURL call to the payment terminal). Let's walk through the implementation step by step:

1. Frontend: Detect Page Exit and Send Cancel Signal

First, you need to listen for when the user is leaving the page, then send a reliable cancel request to your backend. Avoid regular fetch/XMLHttpRequest here—browsers often abort these during unload. Instead, use navigator.sendBeacon(): it's designed for asynchronous, non-blocking requests that survive page unload.

// Store the unique ID of the active payment request (received from backend on initiation)
let activePaymentRequestId = null;

// Function to start the payment flow
async function startPayment(amount, currency) {
  const response = await fetch('/api/initiate-payment', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ amount, currency })
  });
  const data = await response.json();
  activePaymentRequestId = data.requestId; // Save the request ID for later cancelation
}

// Listen for page unload/navigate events
window.addEventListener('beforeunload', () => {
  if (activePaymentRequestId) {
    // Send cancel signal via sendBeacon
    const payload = new Blob([JSON.stringify({ requestId: activePaymentRequestId })], {
      type: 'application/json'
    });
    navigator.sendBeacon('/api/cancel-payment', payload);
  }
});

2. Backend: Track and Terminate the cURL Process

Your backend needs to track each in-progress cURL request so it can terminate it when a cancel signal arrives. The approach varies by language—here are examples for two common stacks:

PHP Example

Use exec() to run cURL in the background, store the process ID (PID) in a cache (like Redis) tied to the request ID, then kill the process when cancel is requested.

// /api/initiate-payment endpoint
public function initiatePayment() {
  // Generate a unique request ID
  $requestId = uniqid('payment_', true);
  
  // Build cURL command to send to the payment terminal (run in background)
  $paymentUrl = "https://your-payment-terminal.com/charge";
  $postData = json_encode([/* your payment params */]);
  $curlCmd = "curl -X POST '$paymentUrl' -H 'Content-Type: application/json' -d '$postData' > /dev/null 2>&1 & echo $!";
  
  // Execute command and get the background process PID
  $pid = exec($curlCmd);
  
  // Store PID in Redis with a timeout (match your payment terminal's timeout)
  $redis = new Redis();
  $redis->connect('localhost');
  $redis->setex("payment_pid_$requestId", 300, $pid); // 5-minute timeout
  
  // Return request ID to frontend
  header('Content-Type: application/json');
  echo json_encode(['requestId' => $requestId]);
}

// /api/cancel-payment endpoint
public function cancelPayment() {
  $payload = json_decode(file_get_contents('php://input'), true);
  $requestId = $payload['requestId'] ?? '';
  
  if (empty($requestId)) {
    http_response_code(400);
    exit;
  }
  
  // Retrieve PID from Redis
  $redis = new Redis();
  $redis->connect('localhost');
  $pid = $redis->get("payment_pid_$requestId");
  
  if ($pid) {
    // Terminate the cURL process (Linux uses posix_kill; Windows uses taskkill)
    if (function_exists('posix_kill')) {
      posix_kill((int)$pid, SIGTERM);
    } else {
      exec("taskkill /F /PID $pid");
    }
    // Clean up the Redis entry
    $redis->del("payment_pid_$requestId");
  }
  
  http_response_code(200);
}

Node.js Example

Use child_process.spawn() to launch cURL, track the child process, and kill it when a cancel request comes in. For multi-server setups, use Redis to share process info.

const express = require('express');
const { spawn } = require('child_process');
const redis = require('redis');
const client = redis.createClient();

const app = express();
app.use(express.json());

// Track active payment processes (use Redis for multi-instance setups)
const activeProcesses = new Map();

app.post('/api/initiate-payment', async (req, res) => {
  const requestId = `payment_${Date.now()}_${Math.random().toString(36).slice(2)}`;
  
  // Spawn cURL process to send payment request
  const curlProcess = spawn('curl', [
    '-X', 'POST',
    'https://your-payment-terminal.com/charge',
    '-H', 'Content-Type: application/json',
    '-d', JSON.stringify(req.body)
  ]);
  
  // Track the process
  activeProcesses.set(requestId, curlProcess);
  await client.setEx(`payment_process_${requestId}`, 300, curlProcess.pid);
  
  res.json({ requestId });
});

app.post('/api/cancel-payment', async (req, res) => {
  const { requestId } = req.body;
  if (!requestId) return res.sendStatus(400);
  
  // Get the process from memory or Redis
  const process = activeProcesses.get(requestId);
  if (process) {
    process.kill('SIGTERM');
    activeProcesses.delete(requestId);
    await client.del(`payment_process_${requestId}`);
  }
  
  res.sendStatus(200);
});

app.listen(3000, () => console.log('Server running on port 3000'));

Key Notes to Consider

  • Payment Terminal Support: If your payment terminal has its own cancel API, you should call that in addition to terminating the cURL request—this ensures the terminal stops waiting for PIN input.
  • Multi-Server Setups: Always use a shared cache (like Redis) to track process IDs, not in-memory storage—otherwise, cancel requests sent to a different server won't find the process.
  • Edge Cases: Handle scenarios where the payment already completed before the cancel request arrives (your backend can ignore these, as the process will have exited already).

内容的提问来源于stack exchange,提问作者Stefan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:51:23