0

0

PayPal PHP安全结账流程:从GET到POST的现代API集成实践

花韻仙語

花韻仙語

发布时间:2025-10-15 08:47:26

|

330人浏览过

|

来源于php中文网

原创

PayPal PHP安全结账流程:从GET到POST的现代API集成实践

本文详细阐述了如何将不安全的get请求paypal结账方式迁移至安全、可靠的post方法。通过集成paypal的现代服务器端api和php sdk,教程涵盖了订单创建、订单捕获以及前端交互的核心流程,旨在防止数据篡改,确保支付过程的完整性和安全性。

在构建在线支付系统时,安全性是首要考虑的因素。传统的PayPal结账方式,特别是通过构建GET请求参数重定向用户到PayPal页面的做法,存在显著的安全隐患。这种方式允许恶意用户通过代理篡改URL参数,例如修改商品金额、数量甚至收款人邮箱,从而导致严重的经济损失和业务风险。为了规避这些问题,PayPal官方推荐使用其现代的服务器端API进行结账流程管理,实现一个基于POST请求且更安全的集成方案。

1. 弃用不安全的GET请求方式

原始的GET请求方式通过http_build_query构建参数,然后重定向用户。虽然简单,但其本质是将所有交易细节暴露在URL中,极易被拦截和篡改。

<?php
// 不推荐的GET请求示例(仅为说明问题)
public function checkoutLegacyGet()
{
    $query = [];
    $query['cmd'] = '_cart';
    $query['upload'] = 1;
    $query['business'] = $this->getCredential(); // 收款人邮箱

    // ... 其他商品及订单信息 ...

    $query_string = http_build_query($query);
    // 这种方式生成的URL可以直接被用户篡改
    return "https://www.paypal.com/cgi-bin/webscr?" . $query_string;
}
?>

这种方法将敏感信息(如商家邮箱、商品价格)作为URL参数传递,极易被中间人攻击或恶意用户修改。

2. 采用PayPal现代服务器端API的POST方式

PayPal推荐的现代结账流程采用服务器端API调用,结合前端JavaScript SDK实现用户友好的支付体验。核心思想是将敏感的订单创建和捕获逻辑放在服务器端处理,前端只负责触发支付流程和接收PayPal的响应。

立即学习PHP免费学习笔记(深入)”;

此流程主要分为两个独立的服务器端路由:

  1. 创建订单 (Create Order):当用户在您的网站上点击“立即支付”时,前端会向您的服务器发送请求,您的服务器调用PayPal API创建一笔订单,并返回订单ID给前端。
  2. 捕获订单 (Capture Order):用户在PayPal界面完成授权后,前端会将订单ID和授权信息传回您的服务器,您的服务器再调用PayPal API捕获这笔订单,完成实际支付。

2.1 准备工作:安装PayPal PHP SDK

首先,通过Composer安装PayPal的PHP Checkout SDK。这是进行服务器端API调用的官方推荐方式。

composer require paypal/paypal-checkout-sdk

2.2 配置PayPal API客户端

在使用SDK之前,需要配置API客户端,包括您的Client ID和Client Secret,以及指定运行环境(沙盒或生产)。

<?php
use PayPalCheckoutSdk\Core\PayPalHttpClient;
use PayPalCheckoutSdk\Core\SandboxEnvironment;
use PayPalCheckoutSdk\Core\ProductionEnvironment;

class PayPalClient
{
    /**
     * Returns PayPal HTTP client instance with environment that has access
     * credentials context. Use this to invoke PayPal APIs.
     */
    public static function client()
    {
        return new PayPalHttpClient(self::environment());
    }

    /**
     * Set up and return PayPal PHP SDK environment with PayPal access credentials.
     * This is where you would use your client ID and client secret.
     */
    public static function environment()
    {
        $clientId = getenv("PAYPAL_CLIENT_ID") ?: "YOUR_PAYPAL_CLIENT_ID";
        $clientSecret = getenv("PAYPAL_CLIENT_SECRET") ?: "YOUR_PAYPAL_CLIENT_SECRET";

        // 根据您的需求选择沙盒环境或生产环境
        if (getenv("APP_ENV") === "production") {
            return new ProductionEnvironment($clientId, $clientSecret);
        } else {
            return new SandboxEnvironment($clientId, $clientSecret);
        }
    }
}
?>

2.3 服务器端路由一:创建订单 (Create Order)

这个路由负责向PayPal发起订单创建请求。它接收来自前端的商品信息,并在服务器端构建订单详情,然后调用PayPal API。

听脑AI
听脑AI

听脑AI语音,一款专注于音视频内容的工作学习助手,为用户提供便捷的音视频内容记录、整理与分析功能。

下载
<?php
use PayPalCheckoutSdk\Orders\OrdersCreateRequest;

// 假设这是一个处理创建订单请求的控制器方法或路由处理函数
public function createOrderHandler(Request $request)
{
    // 1. 从请求中获取商品信息 (通常是商品ID和数量,然后在服务器端查询价格,防止篡改)
    $itemsData = $request->input('items'); // 示例:[['id' => 'prod1', 'quantity' => 2]]

    $purchaseUnits = [];
    $totalAmount = 0;

    foreach ($itemsData as $item) {
        // 在服务器端查询商品详情和价格,确保数据准确性
        $product = $this->getProductDetails($item['id']); // 假设存在此方法
        if (!$product) {
            return response()->json(['error' => 'Product not found'], 400);
        }

        $itemAmount = $product->price * $item['quantity'];
        $totalAmount += $itemAmount;

        $purchaseUnits[] = [
            'reference_id' => uniqid(), // 唯一引用ID
            'amount' => [
                'currency_code' => 'USD', // 货币代码
                'value' => number_format($itemAmount, 2, '.', ''),
                'breakdown' => [
                    'item_total' => [
                        'currency_code' => 'USD',
                        'value' => number_format($itemAmount, 2, '.', '')
                    ]
                ]
            ],
            'items' => [
                [
                    'name' => $product->name,
                    'unit_amount' => [
                        'currency_code' => 'USD',
                        'value' => number_format($product->price, 2, '.', '')
                    ],
                    'quantity' => $item['quantity']
                ]
            ]
        ];
    }

    $request = new OrdersCreateRequest();
    $request->prefer('return=representation');
    $request->body = [
        "intent" => "CAPTURE", // 意图:捕获
        "application_context" => [
            "return_url" => "https://yourdomain.com/paypal-success", // 支付成功后重定向URL
            "cancel_url" => "https://yourdomain.com/paypal-cancel", // 支付取消后重定向URL
            "brand_name" => "Your Store Name",
            "locale" => "en-US",
            "landing_page" => "BILLING",
            "shipping_preference" => "NO_SHIPPING" // 根据需求设置
        ],
        "purchase_units" => $purchaseUnits,
        "payer" => [
            // 可选:如果已知用户邮箱等信息,可在此处预填
            // 'email_address' => 'customer@example.com'
        ]
    ];

    try {
        $client = PayPalClient::client();
        $response = $client->execute($request);

        // 返回订单ID和批准链接给前端
        return response()->json([
            'id' => $response->result->id,
            'status' => $response->result->status,
            'links' => $response->result->links
        ]);
    } catch (Exception $ex) {
        // 错误处理
        return response()->json(['error' => $ex->getMessage()], 500);
    }
}
?>

注意事项:

  • 商品价格和总金额必须在服务器端计算和验证,绝不能依赖前端传递的数据。
  • return_url 和 cancel_url 是用户在PayPal页面完成操作后重定向回您网站的URL。
  • 此路由的输出应仅为JSON格式。

2.4 服务器端路由二:捕获订单 (Capture Order)

用户在PayPal界面完成授权后,前端会将PayPal返回的订单ID发送到此路由。此路由负责调用PayPal API执行实际的支付捕获操作。

<?php
use PayPalCheckoutSdk\Orders\OrdersCaptureRequest;

// 假设这是一个处理捕获订单请求的控制器方法或路由处理函数
public function captureOrderHandler(Request $request)
{
    $orderId = $request->input('orderID'); // 从前端获取的订单ID

    $request = new OrdersCaptureRequest($orderId);
    $request->prefer('return=representation');

    try {
        $client = PayPalClient::client();
        $response = $client->execute($request);

        // 2. 处理捕获结果
        if ($response->result->status === 'COMPLETED') {
            // 支付成功
            $transactionId = $response->result->purchase_units[0]->payments->captures[0]->id;

            // 3. 存储支付详情到数据库
            // 例如:$this->orderService->updateOrderStatus($orderId, 'paid', $transactionId);
            // 务必存储 PayPal 交易ID (transactionId),用于后续对账和查询。
            // 示例:$order->paypal_transaction_id = $transactionId; $order->save();

            // 4. 执行业务逻辑 (例如:发送订单确认邮件、减少库存、生成发货单等)
            // $this->sendOrderConfirmationEmail($orderId);
            // $this->updateProductInventory($orderId);

            return response()->json([
                'status' => 'success',
                'order_id' => $orderId,
                'transaction_id' => $transactionId,
                'details' => $response->result
            ]);
        } else {
            // 支付状态不是COMPLETED,可能需要进一步处理(例如:PENDING, DENIED等)
            return response()->json([
                'status' => 'failed',
                'message' => 'Payment not completed',
                'details' => $response->result
            ], 400);
        }
    } catch (Exception $ex) {
        // 错误处理
        return response()->json(['error' => $ex->getMessage()], 500);
    }
}
?>

注意事项:

  • 关键数据存储: 务必将PayPal返回的交易ID (purchase_units[0].payments.captures[0].id) 存储到您的数据库中,这是PayPal的唯一交易标识符。
  • 业务逻辑: 在捕获成功后立即执行所有必要的业务逻辑,如更新订单状态、发送确认邮件、更新库存等。
  • 幂等性: 捕获订单操作应设计为幂等。如果由于网络问题重复收到捕获请求,确保不会重复处理订单。

3. 前端集成 (PayPal JavaScript SDK)

前端负责渲染PayPal支付按钮,并在用户点击并完成PayPal授权后,将结果(特别是订单ID)传递给您的服务器端捕获订单路由。

<!-- 在您的HTML页面中引入PayPal JavaScript SDK -->
<script src="https://www.paypal.com/sdk/js?client-id=YOUR_PAYPAL_CLIENT_ID&currency=USD"></script>

<div id="paypal-button-container"></div>

<script>
    paypal.Buttons({
        createOrder: function(data, actions) {
            // 调用您的服务器端创建订单API
            return fetch('/api/paypal/create-order', {
                method: 'post',
                headers: {
                    'content-type': 'application/json'
                },
                body: JSON.stringify({
                    items: [ // 示例:传递商品信息给后端
                        { id: 'prod1', quantity: 1 },
                        { id: 'prod2', quantity: 2 }
                    ]
                })
            }).then(function(res) {
                return res.json();
            }).then(function(orderData) {
                // 返回PayPal订单ID
                return orderData.id;
            });
        },
        onApprove: function(data, actions) {
            // 用户在PayPal完成授权后,调用您的服务器端捕获订单API
            return fetch('/api/paypal/capture-order', {
                method: 'post',
                headers: {
                    'content-type': 'application/json'
                },
                body: JSON.stringify({
                    orderID: data.orderID // PayPal返回的订单ID
                })
            }).then(function(res) {
                return res.json();
            }).then(function(orderData) {
                // 处理支付结果
                if (orderData.status === 'success') {
                    alert('支付成功!交易ID: ' + orderData.transaction_id);
                    window.location.href = '/order-confirmation/' + orderData.order_id;
                } else {
                    alert('支付失败:' + orderData.message);
                    window.location.href = '/payment-failed';
                }
            });
        },
        onCancel: function (data) {
            // 用户取消支付
            alert('支付已取消!');
            window.location.href = '/payment-cancelled';
        },
        onError: function (err) {
            // 支付过程中发生错误
            console.error('PayPal支付错误:', err);
            alert('支付过程中发生错误,请稍后重试。');
            window.location.href = '/payment-error';
        }
    }).render('#paypal-button-container'); // 渲染PayPal按钮
</script>

注意事项:

  • client-id 应该使用您的PayPal应用客户端ID。
  • createOrder 函数中,前端向您的服务器发送请求,获取PayPal订单ID。
  • onApprove 函数中,前端将PayPal返回的orderID发送给您的服务器,由服务器完成最终的捕获操作。
  • 务必实现 onCancel 和 onError 回调函数,以提供健壮的用户体验和错误处理。

4. 总结与最佳实践

通过将PayPal结账流程从GET请求迁移到服务器端POST API调用,您可以显著提升支付系统的安全性、可靠性和可维护性。

关键要点:

  • 安全性: 所有敏感的交易数据(如金额、商品详情、收款方)都在服务器端处理,防止客户端篡改。
  • 数据完整性: 在服务器端创建订单和捕获订单,确保交易数据的准确性。
  • 官方SDK: 使用PayPal PHP Checkout SDK简化API交互。
  • 两阶段流程: 遵循“创建订单”和“捕获订单”的两阶段服务器端API调用模式。
  • 前端交互: 结合PayPal JavaScript SDK提供流畅的用户体验。
  • 错误处理: 在服务器端和前端都实现完善的错误处理机制。
  • 交易ID: 成功捕获订单后,务必将PayPal返回的交易ID(purchase_units[0].payments.captures[0].id)存储到您的数据库中。

遵循这些指导原则,您将能够构建一个安全、高效且符合PayPal最佳实践的PHP支付集成方案。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
composer是什么插件
composer是什么插件

Composer是一个PHP的依赖管理工具,它可以帮助开发者在PHP项目中管理和安装依赖的库文件。Composer通过一个中央化的存储库来管理所有的依赖库文件,这个存储库包含了各种可用的依赖库的信息和版本信息。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

162

2023.12.25

json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

457

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

549

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

337

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

82

2025.09.10

mysql标识符无效错误怎么解决
mysql标识符无效错误怎么解决

mysql标识符无效错误的解决办法:1、检查标识符是否被其他表或数据库使用;2、检查标识符是否包含特殊字符;3、使用引号包裹标识符;4、使用反引号包裹标识符;5、检查MySQL的配置文件等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

211

2023.12.04

Python标识符有哪些
Python标识符有哪些

Python标识符有变量标识符、函数标识符、类标识符、模块标识符、下划线开头的标识符、双下划线开头、双下划线结尾的标识符、整型标识符、浮点型标识符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

325

2024.02.23

java标识符合集
java标识符合集

本专题整合了java标识符相关内容,想了解更多详细内容,请阅读下面的文章。

293

2025.06.11

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP课程
PHP课程

共137课时 | 13.5万人学习

JavaScript ES5基础线上课程教学
JavaScript ES5基础线上课程教学

共6课时 | 11.3万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号