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

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?


WildCard | 一分钟注册,轻松订阅海外线上服务

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


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