ws4934/hyperf-paypal

Hyperf PayPal SDK component for processing payments through PayPal API. Compatible with Hyperf 3.1 framework with coroutine support.

v1.0.4 2025-06-03 14:10 UTC

This package is auto-updated.

Last update: 2025-06-03 14:14:54 UTC


README

Latest Version on Packagist Total Downloads License

本组件从 srmklive/paypal Laravel 包移植而来,适配 Hyperf 框架的协程环境。

支持的 PayPal API

  • ✅ 订单管理 (Orders)
  • ✅ 支付授权/捕获 (Payment Authorizations/Captures)
  • ✅ 退款管理 (Refunds)
  • ✅ 订阅管理 (Subscriptions)
  • ✅ 账单计划 (Billing Plans)
  • ✅ 发票管理 (Invoices)
  • ✅ 支付令牌 (Payment Method Tokens)
  • ✅ Webhook 管理
  • ✅ 争议处理 (Disputes)
  • ✅ 身份验证 (Identity)
  • ✅ 报告 (Reporting)
  • ✅ 产品目录 (Catalog Products)
  • ✅ 合作伙伴推荐 (Partner Referrals)
  • ✅ 批量支付 (Payouts)
  • ✅ 跟踪器 (Trackers)

环境要求

  • PHP >= 8.1
  • Hyperf >= 3.1

安装

使用 Composer 安装:

composer require ws4934/hyperf-paypal

在 Hyperf 框架中使用

发布配置文件:

php bin/hyperf.php vendor:publish ws4934/hyperf-paypal

配置

环境变量配置

在您的 .env 文件中添加以下配置:

# PayPal 配置
PAYPAL_MODE=sandbox
PAYPAL_CURRENCY=USD
PAYPAL_PAYMENT_ACTION=Sale

# 沙盒环境配置
PAYPAL_SANDBOX_CLIENT_ID=your-sandbox-client-id
PAYPAL_SANDBOX_CLIENT_SECRET=your-sandbox-client-secret

# 生产环境配置
PAYPAL_LIVE_CLIENT_ID=your-live-client-id
PAYPAL_LIVE_CLIENT_SECRET=your-live-client-secret

# Webhook 配置
PAYPAL_NOTIFY_URL=https://your-domain.com/webhooks/paypal
PAYPAL_RETURN_URL=https://your-domain.com/payment/success
PAYPAL_CANCEL_URL=https://your-domain.com/payment/cancel

# HTTP 配置
PAYPAL_VALIDATE_SSL=true
PAYPAL_HTTP_TIMEOUT=30
PAYPAL_HTTP_CONNECT_TIMEOUT=10

基本使用

Token 缓存(推荐)

组件集成了 Redis 缓存来提高性能,避免频繁获取 PayPal 访问令牌。缓存功能是可选的,如果不配置Redis则自动禁用:

配置缓存

config/autoload/paypal.php 中配置:

return [
    'default' => [
        // ... 其他配置 ...
        
        // Token缓存配置
        'cache' => [
            // Redis工厂类,如果为null则不使用缓存
            'redis_factory' => \Hyperf\Redis\RedisFactory::class,
            
            // Redis连接池名称
            'redis_pool' => 'default',
            
            // 缓存前缀
            'prefix' => 'paypal:token',
            
            // Token过期安全缓冲时间(秒)
            'safety_buffer' => 300, // 5分钟
        ],
    ],
];

使用缓存

<?php

use Ws4934\HyperfPayPal\Services\PayPalInterface;

class PaymentService
{
    public function __construct(
        private PayPalInterface $paypal
    ) {}

    public function processPayment()
    {
    
        $client = $this->paypal->getAuthenticatedClient();
    
        $order = $client->createOrder([...]);
        
        // 查看缓存状态
        if ($this->paypal->hasCachedToken()) {
            $cacheInfo = $this->paypal->getTokenCacheInfo();
            echo "Token缓存剩余TTL: " . $cacheInfo['ttl'] . "\n";
        }
        
        return $order;
    }
    
    public function refreshToken()
    {
        // 清除缓存并强制获取新token
        $this->paypal->clearTokenCache();
        $response = $this->paypal->refreshAccessToken();
        
        return $response;
    }
}

缓存特性:

  • ✅ 自动缓存 PayPal 访问令牌
  • ✅ 支持多账户独立缓存(基于 client_id)
  • ✅ 自动处理 token 过期(可配置安全缓冲时间)
  • ✅ Redis 不配置或不可用时自动禁用缓存
  • ✅ 支持自定义 Redis 连接池和工厂类
  • ✅ 显著提高 API 调用性能

缓存配置选项:

  • redis_factory: Redis工厂类名,设置为 null 禁用缓存
  • redis_pool: Redis连接池名称,对应 config/autoload/redis.php 配置
  • prefix: 缓存键前缀,用于区分不同应用
  • safety_buffer: 安全缓冲时间,提前多少秒认为token过期

在 Hyperf 控制器中使用

<?php

declare(strict_types=1);

namespace App\Controller;

use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\RequestMapping;
use Ws4934\HyperfPayPal\Services\PayPalInterface;

#[Controller]
class PaymentController
{
    public function __construct(
        private PayPalInterface $paypal
    ) {}

    #[RequestMapping(path: '/payment/create', methods: 'POST')]
    public function createPayment()
    {
        // 获取访问令牌并认证
        $client = $this->paypal->getAuthenticatedClient();
        
        // 创建订单
        $order = $client->createOrder([
            'intent' => 'CAPTURE',
            'purchase_units' => [
                [
                    'amount' => [
                        'currency_code' => 'USD',
                        'value' => '100.00'
                    ],
                    'description' => 'Test Payment'
                ]
            ],
            'application_context' => [
                'return_url' => 'https://your-domain.com/payment/success',
                'cancel_url' => 'https://your-domain.com/payment/cancel'
            ]
        ]);

        return $order;
    }

    #[RequestMapping(path: '/payment/capture/{orderId}', methods: 'POST')]
    public function capturePayment(string $orderId)
    {
        $client = $this->paypal->getAuthenticatedClient();
        
        // 捕获支付
        $result = $client->capturePaymentOrder($orderId, []);
        
        return $result;
    }
}

订阅管理示例

<?php

use Ws4934\HyperfPayPal\Services\PayPalInterface;

class SubscriptionController
{
    public function __construct(
        private PayPalInterface $paypal
    ) {}

    public function createSubscription()
    {
        $client = $this->paypal->getAuthenticatedClient();
        
        // 创建产品
        $product = $client->createProduct([
            'name' => 'Monthly Subscription',
            'type' => 'SERVICE',
            'category' => 'SOFTWARE'
        ]);
        
        // 创建计划
        $plan = $client->createPlan([
            'product_id' => $product['id'],
            'name' => 'Monthly Plan',
            'status' => 'ACTIVE',
            'billing_cycles' => [
                [
                    'frequency' => [
                        'interval_unit' => 'MONTH',
                        'interval_count' => 1
                    ],
                    'tenure_type' => 'REGULAR',
                    'sequence' => 1,
                    'total_cycles' => 0,
                    'pricing_scheme' => [
                        'fixed_price' => [
                            'value' => '29.99',
                            'currency_code' => 'USD'
                        ]
                    ]
                ]
            ],
            'payment_preferences' => [
                'auto_bill_outstanding' => true,
                'setup_fee_failure_action' => 'CONTINUE',
                'payment_failure_threshold' => 3
            ]
        ]);
        
        // 创建订阅
        $subscription = $client->createSubscription([
            'plan_id' => $plan['id'],
            'subscriber' => [
                'email_address' => 'user@example.com',
                'name' => [
                    'given_name' => 'John',
                    'surname' => 'Doe'
                ]
            ],
            'application_context' => [
                'brand_name' => 'Your Company',
                'locale' => 'en-US',
                'shipping_preference' => 'NO_SHIPPING',
                'user_action' => 'SUBSCRIBE_NOW',
                'payment_method' => [
                    'payer_selected' => 'PAYPAL',
                    'payee_preferred' => 'IMMEDIATE_PAYMENT_REQUIRED'
                ],
                'return_url' => 'https://your-domain.com/subscription/success',
                'cancel_url' => 'https://your-domain.com/subscription/cancel'
            ]
        ]);
        
        return $subscription;
    }
}

Webhook 处理

<?php

use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\RequestMapping;
use Hyperf\HttpServer\Contract\RequestInterface;
use Ws4934\HyperfPayPal\Services\PayPalInterface;

#[Controller]
class WebhookController
{
    public function __construct(
        private PayPalInterface $paypal
    ) {}

    #[RequestMapping(path: '/webhooks/paypal', methods: 'POST')]
    public function handleWebhook(RequestInterface $request)
    {
        $client = $this->paypal->getAuthenticatedClient();
        
        // 获取 webhook 数据
        $webhookPayload = $request->getBody()->getContents();
        $headers = $request->getHeaders();
        
        // 验证 webhook 签名
        $isValid = $client->verifyWebhookSignature([
            'webhook_id' => 'your-webhook-id',
            'webhook_event' => json_decode($webhookPayload, true),
            'headers' => $headers
        ]);
        
        if ($isValid) {
            $event = json_decode($webhookPayload, true);
            
            // 处理不同类型的事件
            switch ($event['event_type']) {
                case 'PAYMENT.CAPTURE.COMPLETED':
                    // 处理支付完成
                    break;
                case 'BILLING.SUBSCRIPTION.CREATED':
                    // 处理订阅创建
                    break;
                case 'BILLING.SUBSCRIPTION.CANCELLED':
                    // 处理订阅取消
                    break;
                default:
                    // 处理其他事件
                    break;
            }
        }
        
        return ['status' => 'success'];
    }
}

错误处理

<?php

use Ws4934\HyperfPayPal\Services\PayPalInterface;
use RuntimeException;

class PaymentService
{
    public function __construct(
        private PayPalInterface $paypal
    ) {}

    public function processPayment(array $data)
    {
        try {
            $client = $this->paypal->getAuthenticatedClient();
            
            $order = $client->createOrder($data);
            
            return $order;
        } catch (RuntimeException $e) {
            // 获取详细错误信息
            $lastError = $this->paypal->getLastError();
            $lastRequest = $this->paypal->getLastRequest();
            $lastResponse = $this->paypal->getLastResponse();
            
            // 记录错误日志
            logger()->error('PayPal API Error', [
                'error' => $e->getMessage(),
                'last_error' => $lastError,
                'request' => $lastRequest,
                'response' => $lastResponse
            ]);
            
            throw $e;
        } finally {
            // 清理上下文
            $this->paypal->clearContext();
        }
    }
}

协程上下文

组件使用 Hyperf 的协程上下文来存储请求和响应数据,这样在协程环境中不会出现数据混乱:

// 获取最后一次请求详情
$lastRequest = $paypal->getLastRequest();

// 获取最后一次响应详情  
$lastResponse = $paypal->getLastResponse();

// 获取最后一次错误详情
$lastError = $paypal->getLastError();

// 清理上下文数据
$paypal->clearContext();

许可证

本项目基于 MIT 许可证开源。详情请见 LICENSE 文件。