# PayPal 支付过程解析：Checkout 收银台与 Subscription 订阅计划实现全过程

By [tangxue](https://paragraph.com/@tangxue) · 2025-01-16

---

PayPal 支付过程解析：Checkout 收银台与 Subscription 订阅计划实现全过程
==================================================

本文详细介绍了如何通过 PayPal 完成 **Checkout 收银台支付** 和 **Subscription 订阅计划**。我们将分步骤分析流程并提供代码实现，帮助开发者顺利集成 PayPal 支付功能。

一. 生命周期
-------

### 1\. Checkout - 收银台支付

以下是支付过程的详细拆解流程，类似于支付宝的收银台：

1.  本地应用组装好参数并请求 **Checkout 接口**，接口同步返回一个支付 URL。
    
2.  本地应用重定向至该 URL，用户登录 PayPal 账户并确认支付。支付后，用户跳转至设置好的本地应用地址。
    
3.  本地请求 PayPal 执行付款接口发起扣款。
    
4.  PayPal 发送异步通知至本地应用，本地应用进行验签操作。
    
5.  验签成功后，执行支付完成后的业务逻辑，如修改订单状态、增加销量、发送邮件等。
    

### 2\. Subscription - 订阅支付

订阅支付的流程如下：

1.  创建一个 **订阅计划**。
    
2.  激活该计划。
    
3.  使用已激活的计划去创建订阅申请。
    
4.  跳转至订阅申请链接获取用户授权并完成首期付款，支付后携带 token 跳转至本地应用地址。
    
5.  回跳后请求执行订阅。
    
6.  接收到订阅授权的异步回调后，验证支付成功并执行支付完成后的业务。
    

二. 具体实现
-------

### Checkout

#### 安装 PayPal SDK

    $ composer require paypal/rest-api-sdk-php:* // 使用最新版本
    

#### 配置 PayPal 文件

    $ touch config/paypal.php
    

配置内容如下：

    <?php
    
    return [
        'sandbox' => [
            'client_id' => env('PAYPAL_SANDBOX_CLIENT_ID', ''),
            'secret' => env('PAYPAL_SANDBOX_SECRET', ''),
            'notify_web_hook_id' => env('PAYPAL_SANDBOX_NOTIFY_WEB_HOOK_ID', ''),
            'checkout_notify_web_hook_id' => env('PAYPAL_SANDBOX_CHECKOUT_NOTIFY_WEB_HOOK_ID', ''),
        ],
    
        'live' => [
            'client_id' => env('PAYPAL_CLIENT_ID', ''),
            'secret' => env('PAYPAL_SECRET', ''),
            'notify_web_hook_id' => env('PAYPAL_NOTIFY_WEB_HOOK_ID', ''),
            'checkout_notify_web_hook_id' => env('PAYPAL_CHECKOUT_NOTIFY_WEB_HOOK_ID', ''),
        ],
    ];
    

#### 创建 PayPal 服务类

    $ mkdir -p app/Services && touch app/Services/PayPalService.php
    

#### 编写 Checkout 支付方法

    <?php
    
    namespace App\Services;
    
    use PayPal\Api\Payment;
    use PayPal\Api\PaymentExecution;
    use PayPal\Api\RedirectUrls;
    use PayPal\Api\Transaction;
    use PayPal\Api\Amount;
    use PayPal\Api\ItemList;
    use PayPal\Api\Item;
    use PayPal\Api\Payer;
    
    class PayPalService
    {
        public function checkout(Order $order)
        {
            try {
                $payer = new Payer();
                $payer->setPaymentMethod('paypal');
    
                $item = new Item();
                $item->setName($order->product->title)
                     ->setDescription($order->no)
                     ->setCurrency($order->product->currency)
                     ->setQuantity(1)
                     ->setPrice($order->total_amount);
    
                $itemList = new ItemList();
                $itemList->setItems([$item]);
    
                $amount = new Amount();
                $amount->setCurrency($order->product->currency)
                       ->setTotal($order->total_amount);
    
                $transaction = new Transaction();
                $transaction->setAmount($amount)
                            ->setItemList($itemList)
                            ->setDescription($order->no);
    
                $redirectUrls = new RedirectUrls();
                $redirectUrls->setReturnUrl(route('payment.paypal.return', ['success' => 'true', 'no' => $order->no]))
                             ->setCancelUrl(route('payment.paypal.return', ['success' => 'false', 'no' => $order->no]));
    
                $payment = new Payment();
                $payment->setIntent('sale')
                        ->setPayer($payer)
                        ->setRedirectUrls($redirectUrls)
                        ->setTransactions([$transaction]);
    
                $payment->create($this->apiContext);
    
                return $payment->getApprovalLink();
            } catch (\Exception $e) {
                return null;
            }
        }
    }
    

#### 配置控制器

    $ php artisan make:controller PaymentsController
    

    <?php
    
    namespace App\Http\Controllers;
    
    use App\Models\Order;
    
    class PaymentController extends Controller
    {
        public function payByPayPalCheckout(Order $order)
        {
            $approvalUrl = app('paypal')->checkout($order);
            if (!$approvalUrl) {
                return json_encode(['code' => 500, 'msg' => 'Interval Error.', 'url' => '']);
            }
            return json_encode(['code' => 201, 'msg' => 'success.', 'url' => $approvalUrl]);
        }
    }
    

### Subscription

#### 创建计划并激活计划

    <?php
    
    namespace App\Services;
    
    use PayPal\Api\Plan;
    use PayPal\Api\PaymentDefinition;
    use PayPal\Api\ChargeModel;
    use PayPal\Api\MerchantPreferences;
    
    class PayPalService
    {
        public function createPlan(Order $order)
        {
            $plan = new Plan();
            $plan->setName($order->no)
                 ->setDescription($order->product->title)
                 ->setType('INFINITE'); // 可选(FIXED | INFINITE)
    
            $paymentDefinition = new PaymentDefinition();
            $paymentDefinition->setAmount(new Currency([
                'value' => $order->product->price,
                'currency' => $order->product->currency
            ]));
    
            $merchantPreferences = new MerchantPreferences();
            $merchantPreferences->setReturnUrl(route('subscriptions.paypal.return', ['success' => 'true', 'no' => $order->no]))
                                ->setCancelUrl(route('subscriptions.paypal.return', ['success' => 'false', 'no' => $order->no]))
                                ->setAutoBillAmount("yes");
    
            $plan->setPaymentDefinitions([$paymentDefinition]);
            $plan->setMerchantPreferences($merchantPreferences);
    
            $output = $plan->create($this->apiContext);
    
            $patch = new Patch();
            $patch->setOp('replace')
                  ->setPath('/')
                  ->setValue(new PayPalModel('{"state":"ACTIVE"}'));
            
            $patchRequest = new PatchRequest();
            $patchRequest->addPatch($patch);
    
            $output->update($patchRequest, $this->apiContext);
            return $output;
        }
    }
    

#### 控制器和路由

    $ php artisan make:controller SubscriptionsController
    

    <?php
    
    namespace App\Http\Controllers;
    
    use App\Models\Order;
    
    class SubscriptionsController extends Controller
    {
        public function createPlan(Order $order)
        {
            $plan = app('paypal')->createPlan($order);
            $approvalUrl = app('paypal')->createAgreement($plan, $order);
            return json_encode(['code' => 201, 'msg' => 'success.', 'url' => $approvalUrl]);
        }
    }
    

常见问题解答
------

1.  **如何处理 PayPal 异常回调？**
    
    *   在接收到 PayPal 的回调时，需验证回调的有效性。使用 `verify` 方法来确认回调是否来自 PayPal。
        
2.  **如何配置 PayPal Webhook？**
    
    *   登录到 [PayPal 开发者中心](https://developer.paypal.com)，添加 `Webhook` 并选择相应的事件类型。
        

* * *

☞ [WildCard | 一分钟注册，轻松订阅海外线上服务](https://bit.ly/bewildcard)

使用门槛极低，微信支付宝均可开通使用。支持开通各类海外平台：ChatGPT、Claude、Google Play、Apple Store、OpenAI、X、Patreon、MidJourney、Amazon、POE、Microsoft、Facebook、GitHub、Telegram、PayPal等各类海淘订阅平台。使用邀请码：ACCPAY，立享消费0手续费，减免开卡费用。

* * *

通过本文，您已经掌握了如何使用 **PayPal** 完成 **Checkout 收银台支付** 和 **Subscription 订阅支付** 的集成。根据您的需求，选择适合的支付方式，顺利实现 PayPal 集成。

---

*Originally published on [tangxue](https://paragraph.com/@tangxue/paypal-checkout-subscription)*
