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

如何将PHP实现的PayPal结账从GET方式改为安全POST方式

问题解答

一、旧版PayPal结账GET改POST方案(解决参数篡改问题)

你当前使用的是PayPal HTML Payments Standard模式,GET方式所有参数都暴露在URL中,确实极易被篡改。改造为POST提交的逻辑很简单,不再直接返回拼接好的跳转URL,而是输出自动提交的POST表单,所有参数放在隐藏域中传递,避免用户端随意修改。
改造后的checkout方法代码如下:

public function checkout()
{
    $query = [];
    $query['cmd'] = '_cart';
    $query['upload'] = 1;
    $query['business'] = $this->getCredential();

    foreach ($this->getItems() as $id => $item)
    {
        $id = $id + 1;
        $query['item_name_' . $id] = $item['name'];
        $query['amount_' . $id] = $item['amount'];
        $query['quantity_' . $id] = $item['quantity'];
    }

    $query['custom'] = $this->getReference();
    $query['first_name'] = $this->first_name;
    $query['last_name'] = $this->last_name;
    $query['email'] = $this->customer_email;

    $query['notify_url'] = $this->getNotificationURL();
    $query['return'] = $this->getReturnURL();
    $query['cancel_return'] = $this->getCancelURL();

    $query['rm'] = '2';
    $query['cbt'] = 'Retornar para o site';
    $query['lc'] = $this->getLocation();
    $query['currency_code'] = $this->getCurrency();

    $paypalUrl = "https://". ($this->isSandbox() ? 'sandbox' : 'www' ) .".paypal.com/cgi-bin/webscr";
    // 生成自动提交的POST表单
    $form = '<form id="paypal_form" action="'.$paypalUrl.'" method="post">';
    foreach ($query as $key => $value) {
        $form .= '<input type="hidden" name="'.htmlspecialchars($key).'" value="'.htmlspecialchars($value).'">';
    }
    $form .= '</form><script>document.getElementById("paypal_form").submit();</script>';
    return $form;
}

安全补充提示:就算改为POST提交,也必须在IPN回调中校验返回的business字段和你的官方收款邮箱一致、校验mc_gross金额和订单应付金额一致、校验payment_status为Completed,才能判定支付有效,彻底避免参数篡改风险。

二、PayPal v2 Checkout API跳转后未完成支付的解决方案

这个问题的核心原因是你只完成了「创建订单」的步骤,用户在PayPal页面同意支付后跳回你的return_url,仅代表用户完成了支付授权,你还没有调用资金捕获接口完成扣款,资金还在用户账户中,自然不算支付完成。
解决步骤如下:

  1. 新增return_url对应的后端处理逻辑,从跳转参数中获取token字段(即订单ID)
  2. 调用PayPal v2 订单捕获接口完成扣款,校验订单状态为COMPLETED后再更新你的订单状态、发放用户权益
  3. 同时在notify_url对应的webhook逻辑中做相同的校验,避免用户关闭页面未跳转回return_url导致的订单不同步问题

捕获订单的参考代码如下:

public function handlePaypalReturn()
{
    $orderId = $_GET['token'];
    $accessToken = $this->getAccessToken();

    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, "https://api-m.paypal.com/v2/checkout/orders/{$orderId}/capture");
    curl_setopt($curl, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'Authorization: ' . $accessToken,
        'Prefer: return=representation'
    ]);
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode((object)[]));
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    $response = json_decode(curl_exec($curl));
    $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
    curl_close($curl);

    if ($httpCode == 201 && $response->status == 'COMPLETED') {
        // 支付成功,比对订单金额、商品信息,更新你的订单状态、发放权益
    } else {
        // 支付失败,跳转至错误提示页
    }
}

补充优化提示:原来创建订单的逻辑建议增加错误判断,先校验curl返回的HTTP状态码为201再取跳转链接,不要硬编码取links[1],避免接口结构变化导致报错。

内容的提问来源于stack exchange,提问作者Luís Henrique

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 16:36:07