ThinkPHP接入PayPal支付

ThinkPHP 5接入PayPal 支付,PayPal的流程是服务器请求Paypal的接口下单(需要传订单id/支付成功的重定向地址/支付失败的重定向地址),接会返回一个支付地址,项目服务器把地址返给用户,用户打开链接登录Paypal完成付款,然后Paypal给重定向到指定地址。

在paypal官网开通商户号,设置通知地址。

开通沙箱模式用于测试,后台会给沙箱模式生成商户账号和用户账号,请注意区分。

申请和开通网上有教程不在赘述。

具体实现步骤如下

1.安装包

复制代码
composer require paypal/rest-api-sdk-php:*

2.具体实现代码

php 复制代码
<?php

namespace app\api\controller;

use app\common\controller\Api;
use app\common\model\ShopOrder;
use PayPal\Api\Amount;
use PayPal\Api\Payer;
use PayPal\Api\Payment;
use PayPal\Api\Transaction;
use PayPal\Api\PaymentExecution;
use PayPal\Auth\OAuthTokenCredential;
use PayPal\Rest\ApiContext;
use PayPal\Api\RedirectUrls;
use think\Log;
class Paypal extends Api
{
    protected $noNeedLogin = ['*'];
    protected $noNeedRight = ['*'];

    private $apiContext;

    public function __construct()
    {
        parent::__construct();

        // 初始化PayPal API上下文
        $this->apiContext = new ApiContext(
            new OAuthTokenCredential(
                'AV8d**************************N-jbpRvV-K0_dLuEA5d8uodUowab6jdWtM',     // 客户端ID
                'EByrRAncAi*****************************RSqIRA'         // 客户端密钥
            )
        );
        $this->apiContext->setConfig([
            'mode' => 'sandbox',      // 或者 'live'
            // 其他配置...
        ]);
    }

    /**
     * 下单接口
     * @return \think\response\Json|void
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function createPaypalPaymentUrl()
    {
        // 获取前端传递的order_Id
        $orderId = input('post.order_id');

        // 查询订单信息(这里你需要根据自己的数据库结构进行查询)
        // 假设我们得到了一个包含订单详情的数组$orderInfo
        $orderInfo = ShopOrder::where('id', $orderId)->where('status', '1')->find();

        if (empty($orderInfo)) {
            $this->error('订单不存在或不是待支付状态');
        }

        // 设置Payer信息,表明付款人是通过PayPal付款
        $payer = new Payer();
        $payer->setPaymentMethod("paypal");

        // 设置交易金额
        $amount = new Amount();
        $amount->setCurrency("AUD")
            ->setTotal(number_format($orderInfo['actual_money'], 2, '.', ''));

        // 创建Transaction对象,并使用订单ID作为发票号
        $transaction = new Transaction();
        $transaction->setAmount($amount)
            ->setDescription("Slem order pay")  // 描述可选
            ->setInvoiceNumber($orderInfo->order_num);  // 使用订单order_num作为发票号

        // 创建RedirectUrls对象
        $redirectUrls = new RedirectUrls();
        $redirectUrls->setReturnUrl("https://*******.cn/api/paypal/paymentSuccess")  // 支付成功后的回调地址
        ->setCancelUrl("https://********/api/paypal/paymentCancel");  // 用户取消支付后的回调地址

        // 创建Payment对象
        $payment = new Payment();
        $payment->setIntent("sale")
            ->setPayer($payer)
            ->setRedirectUrls($redirectUrls)
            ->setTransactions([$transaction]);

        try {
            // 创建支付请求
            $payment->create($this->apiContext);

            // 获取approval_url,这是用户需要访问来完成支付的URL
            foreach ($payment->getLinks() as $link) {
                if ($link->getRel() == 'approval_url') {
                    // 返回支付链接给客户端
                    return json(['code' => 1, 'data' => $link->getHref()]);
//                    $data = ['status' => 'success', 'approval_url' => $link->getHref()];
//                    $this->success(__('SUccess'),$data);
                }
            }
        } catch (\Exception $ex) {
            // 输出详细的错误信息
            return json([
                'status' => 'error',
                'message' => 'Error creating PayPal payment: ' . $ex->getMessage(),
                'details' => $ex->getTraceAsString(),
                'response' => $payment->toArray()
            ]);
        }
    }

    /**
     * 支付成功跳转的页面
     * 建议前端出个html后台做渲染,本方法只为展示流程
     * @return \think\response\Json
     */
    public function paymentSuccess()
    {
        // 获取PayPal传递过来的参数
        $paymentId = input('get.paymentId');
        $payerId = input('get.PayerID');

        if (empty($paymentId) || empty($payerId)) {
            return json(['status' => 'error', 'message' => 'Missing payment ID or payer ID']);
        }

        try {
            // 获取支付信息
            $payment = Payment::get($paymentId, $this->apiContext);

            // 创建PaymentExecution对象并设置payer_id
            $execution = new PaymentExecution();
            $execution->setPayerId($payerId);

            // 执行支付请求
            $result = $payment->execute($execution, $this->apiContext);

            // 检查支付状态
            if ($result->getState() === 'approved') {
                // 使用发票号(即订单ID)来查找订单
                $invoiceNumber = $payment->getTransactions()[0]->getInvoiceNumber();
                $order = ShopOrder::where('order_num', $invoiceNumber)->find();

                if (!empty($order)) {
                    // 更新订单状态为已支付
                    $order->payment = 'paypal';
                    $order->status = '2';
                    $order->save();

                    // 你可以在这里添加更多的业务逻辑,比如发送确认邮件等

                    // 返回成功信息给前端
                    return json(['status' => 'success', 'message' => 'Payment successful']);
                } else {
                    return json(['status' => 'error', 'message' => 'Order not found']);
                }
            } else {
                return json(['status' => 'error', 'message' => 'Payment not approved']);
            }
        } catch (\Exception $ex) {
            // 错误处理
            Log::error('PayPal Error: ' . $ex->getMessage());
            return json([
                'status' => 'error',
                'message' => 'Error executing payment: ' . $ex->getMessage(),
                'details' => $ex->getTraceAsString()
            ]);
        }
    }

    /**
     * 支付取消跳转的页面
     * @return \think\response\Json
     */
    public function paymentCancel()
    {
        // 获取订单ID或其他相关信息(如果需要)
        $orderId = input('get.order_id'); // 如果PayPal回调包含order_id

        if (!empty($orderId)) {
            try {
                // 根据订单ID查找订单信息
                $order = ShopOrder::where('id', $orderId)->find();

                if (!empty($order)) {
                    // 你可以在这里添加更多的业务逻辑,比如记录取消原因、发送通知等

                    // 更新订单状态为已取消或保持不变,视业务需求而定
                    // 这里假设我们不改变订单状态,仅记录取消事件
                    Log::info("Payment cancelled for order ID: " . $orderId);

                    // 返回取消信息给前端
                    return json(['status' => 'info', 'message' => 'Payment cancelled.']);
                } else {
                    return json(['status' => 'error', 'message' => 'Order not found.']);
                }
            } catch (\Exception $ex) {
                // 错误处理
                Log::error('Error handling payment cancellation: ' . $ex->getMessage());
                return json([
                    'status' => 'error',
                    'message' => 'An error occurred while processing your request.',
                    'details' => $ex->getTraceAsString()
                ]);
            }
        } else {
            // 如果没有提供订单ID,则简单地告知用户支付已被取消
            return json(['status' => 'info', 'message' => 'Payment cancelled.']);
        }
    }
}
相关推荐
新知图书4 天前
ThinkPHP 8 操作JSON数据
php·thinkphp
新知图书5 天前
ThinkPHP 8模型与数据的插入、更新、删除
php·thinkphp
新知图书12 天前
ThinkPHP 8的多对多关联
php·thinkphp
新知图书19 天前
ThinkPHP 8的一对一关联
php·thinkphp
新知图书21 天前
PHP与ThinkPHP连接数据库示例
开发语言·数据库·php·thinkphp
胡萝卜的兔1 个月前
thinnkphp5.1和 thinkphp6以及nginx,apache 解决跨域问题
运维·nginx·apache·thinkphp
新知图书1 个月前
ThinkPHP 8开发环境安装
thinkphp·thinkphp8
一一程序1 个月前
Thinkphp 使用workerman消息实现消息推送完整示例
websocket·gateway·thinkphp·workerman
非凡的世界1 个月前
PHP高性能webman管理系统EasyAdmin8
php·thinkphp·高性能·webman