PayPal Checkout onApprove阶段数据库写入失败问题求助
PHP购物车PayPal Checkout升级:解决custom_ID和orderID为空问题
前端PayPal按钮代码(关键:传递参数到后端)
核心是在创建订单时注入会话ID作为custom_id,并在onApprove阶段主动将orderID和custom_ID提交到后端接口:
<script src="https://www.paypal.com/sdk/js?client-id=你的客户端ID¤cy=USD"></script> <div id="paypal-button-container"></div> <script> paypal.Buttons({ createOrder: function(data, actions) { // 从PHP获取会话ID const customId = '<?php echo $seid; ?>'; return actions.order.create({ purchase_units: [{ amount: { value: '<?php echo $orderAmount; ?>' }, custom_id: customId // 注入会话ID到PayPal订单 }] }); }, onApprove: function(data, actions) { const orderId = data.orderID; const customId = '<?php echo $seid; ?>'; // 提交参数到completed.php return fetch('completed.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ orderID: orderId, custom_ID: customId }) }).then(res => res.json()) .then(result => { result.success ? window.location.href = 'success.php' : alert(result.message); }).catch(err => { console.error(err); alert('支付验证失败'); }); }, onError: err => console.error('PayPal按钮错误:', err) }).render('#paypal-button-container'); </script>
后端completed.php代码(关键:接收+验证+更新)
接收前端参数后,必须先通过PayPal API验证订单合法性,再执行数据库更新:
<?php // PayPal配置 $clientId = '你的客户端ID'; $clientSecret = '你的客户端密钥'; $paypalApiUrl = 'https://api-m.sandbox.paypal.com'; // 生产环境改为https://api-m.paypal.com // 接收前端JSON数据 $input = file_get_contents('php://input'); $data = json_decode($input, true); // 检查必填参数 if (empty($data['orderID']) || empty($data['custom_ID'])) { echo json_encode(['success' => false, 'message' => '缺少orderID或custom_ID']); exit; } $orderId = $data['orderID']; $customId = $data['custom_ID']; // 获取PayPal访问令牌 function getAccessToken($cid, $csec, $url) { $ch = curl_init($url.'/v1/oauth2/token'); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, 'grant_type=client_credentials'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_USERPWD, "$cid:$csec"); $res = curl_exec($ch); curl_close($ch); return json_decode($res, true)['access_token'] ?? false; } // 查询PayPal订单详情 function getOrderDetails($oid, $token, $url) { $ch = curl_init($url."/v2/checkout/orders/$oid"); curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer $token"]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $res = curl_exec($ch); curl_close($ch); return json_decode($res, true); } // 验证订单合法性 $token = getAccessToken($clientId, $clientSecret, $paypalApiUrl); if (!$token) { echo json_encode(['success' => false, 'message' => '无法获取PayPal令牌']); exit; } $orderDetails = getOrderDetails($orderId, $token, $paypalApiUrl); if ($orderDetails['status'] !== 'COMPLETED' || $orderDetails['purchase_units'][0]['custom_id'] !== $customId) { echo json_encode(['success' => false, 'message' => '订单无效或未完成']); exit; } // 更新数据库(示例,根据你的表结构调整) $conn = mysqli_connect('主机', '用户名', '密码', '数据库名'); if (!$conn) { echo json_encode(['success' => false, 'message' => '数据库连接失败']); exit; } $sql = "UPDATE orders SET status = 'completed', paypal_order_id = ? WHERE session_id = ?"; $stmt = mysqli_prepare($conn, $sql); mysqli_stmt_bind_param($stmt, 'ss', $orderId, $customId); if (mysqli_stmt_execute($stmt)) { echo json_encode(['success' => true, 'message' => '订单更新成功']); } else { echo json_encode(['success' => false, 'message' => '数据库更新失败:'.mysqli_error($conn)]); } mysqli_stmt_close($stmt); mysqli_close($conn); ?>
关键注意点
- 前端必须显式传递
custom_ID,不能仅依赖PayPal返回的custom_id,防止前端篡改 - 后端必须验证PayPal订单的状态、custom_id、金额,确保数据真实可信
- 数据库操作使用预处理语句,避免SQL注入
内容的提问来源于stack exchange,提问作者Steve
相关产品推荐
相关产品推荐

