ThinkPHP接入PayPal支付

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

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

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

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

具体实现步骤如下

1.安装包

composer require paypal/rest-api-sdk-php:*

2.具体实现代码

<?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.']);
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/942647.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[手机Linux] 七,NextCloud优化设置

安装完成后在个人设置里发现很多警告&#xff0c;一一消除。 只能一条一条解决了。 关于您的设置有一些错误。 1&#xff0c;PHP 内存限制低于建议值 512 MB。 设置php配置文件&#xff1a; /usr/local/php/etc/php.ini 把里面的&#xff1a; memory_limit 128M 根据你自…

使用Excel制作通达信自定义“序列数据“

序列数据的视频教程演示 Excel制作通达信自定义序列数据 1.序列数据的制作方法&#xff1a;删掉没有用的数据&#xff08;行与列&#xff09;和股代码格式处理&#xff0c;是和外部数据的制作方法是相同&#xff0c;自己上面看历史博文。只需要判断一下&#xff0c;股代码跟随的…

逆向工程在医疗器械中的应用

关于逆向工程&#xff1a; 逆向设计跟正向设计流程不同&#xff0c;它是对己有产品原型进行分析、改进和再创造的过程。通过先进的数字测量手段反向获取产品的外形数据&#xff0c;然后利用各种造型软件由点云数据重构出该产品的CAD模型。逆向工程的辅助设计建构可以缩短产品的…

Web安全攻防入门教程——hvv行动详解

Web安全攻防入门教程 Web安全攻防是指在Web应用程序的开发、部署和运行过程中&#xff0c;保护Web应用免受攻击和恶意行为的技术与策略。这个领域不仅涉及防御措施的实现&#xff0c;还包括通过渗透测试、漏洞挖掘和模拟攻击来识别潜在的安全问题。 本教程将带你入门Web安全攻防…

rk3588 android12 root

问题说明&#xff1a; 将 andorid12 root 测试情况说明&#xff1a;我在 串口上 实际上 是可以 使用 su root 命令 进入 root 的&#xff0c;但是 使用 root check apk 检测的时候却通不过。 是否解决说明&#xff1a; 已解决 解决问题的逻辑&#xff1a; 就按照 网上的资料…

基于Mysql、JavaScript、PHP、ajax开发的MBTI性格测试网站(前端+后端)

源码地址&#xff1a;https://download.csdn.net/download/2302_79553009/89933699 项目简介 本项目旨在构建一个基于MBTI&#xff08;迈尔斯-布里格斯性格分类指标&#xff09;理论的在线平台——“16Personalities”。该平台利用PHP、MySQL、JavaScript等技术栈开发&#x…

数字IC后端设计实现十大精华主题分享

今天小编给大家分享下吾爱IC社区星球上周十大后端精华主题。 Q1:星主&#xff0c;请教个问题&#xff0c;长tree的时候发现这个scan的tree 的skew差不多400p&#xff0c;我高亮了整个tree的schematic&#xff0c;我在想是不是我在这一系列mux前边打断&#xff0c;设置ignore p…

Docker 快速搭建 GBase 8s数据库服务

1.查看Gbase 8s镜像版本 可以去到docker hub网站搜索&#xff1a;gbase8s liaosnet/gbase8s如果无法访问到该网站&#xff0c;可以通过docker search搜索 docker search gbase8s2.拉取Gbase 8s镜像 以下演示的版本是目前官网最新版本Gbase8sV8.8_3.5.1 docker pull liaosn…

大型语言模型(LLMs)演化树 Large Language Models

大型语言模型&#xff08;LLMs&#xff09;演化树 Large Language Models flyfish 下面的图来自论文地址 Transformer 模型&#xff08;如 BERT 和 GPT-3&#xff09;已经给自然语言处理&#xff08;NLP&#xff09;领域带来了革命性的变化。这得益于它们具备并行化能力&…

让 AMD GPU 在大语言模型推理中崭露头角:机遇与挑战

在当今科技飞速发展的时代&#xff0c;大语言模型&#xff08;LLM&#xff09;的兴起彻底改变了人工智能领域的格局。从智能客服到文本生成&#xff0c;从知识问答到代码编写辅助&#xff0c;大语言模型的应用无处不在&#xff0c;深刻影响着我们的生活和工作。然而&#xff0c…

CPU条件下Pytorch、jupyter环境配置

一、创建虚拟环境 查看虚拟环境 conda env list 创建python虚拟环境 conda create -n minist python3.11 激活虚拟环境 conda activate minist 查看虚拟环境下有哪些包 pip list 二、安装pytorch 切换清华源 conda config --add channels https://mirrors.tuna.tsing…

【iOS安全】Block开发与逆向

1. OC中的Block 1.1 Block的基本概念 在iOS开发中&#xff0c;Block是一种特殊的数据类型&#xff0c;类似于其他编程语言中的匿名函数。它可以封装一段代码&#xff0c;并且能够像普通变量一样传递、存储和执行。Block可以捕获并访问定义它时所在作用域的变量&#xff0c;这…

C# 中的记录类型简介 【代码之美系列】

&#x1f380;&#x1f380;&#x1f380;代码之美系列目录&#x1f380;&#x1f380;&#x1f380; 一、C# 命名规则规范 二、C# 代码约定规范 三、C# 参数类型约束 四、浅析 B/S 应用程序体系结构原则 五、浅析 C# Async 和 Await 六、浅析 ASP.NET Core SignalR 双工通信 …

查询 MySQL 默认的存储引擎(SELECT @@default_storage_engine;)

要查询 MySQL 默认的存储引擎&#xff0c;可以使用以下 SQL 查询语句&#xff1a; SELECT default_storage_engine;解释&#xff1a; SELECT: 表示你要执行一个查询。default_storage_engine: 这是一个 MySQL 系统变量&#xff0c;它存储着当前 MySQL 服务器的默认存储引擎。…

大数据技术-Hadoop(二)HDFS的介绍与使用

目录 1、HDFS简介 1.1 什么是HDFS 1.2 HDFS的优点 1.3、HDFS的架构 1.3.1、 NameNode 1.3.2、 NameNode的职责 1.3.3、DataNode 1.3.4、 DataNode的职责 1.3.5、Secondary NameNode 1.3.6、Secondary NameNode的职责 2、HDFS的工作原理 2.1、文件存储 2.2 、数据写…

SpringBoot项目的5种搭建方式(以idea2017为例)

目录 1. idea中使用官方API 2. idea中使用阿里云API 3. 在spring官网创建 4. 在阿里云官网创建 5. Maven项目改造成springboot项目 SpringBoot项目的创建细分一共有5种&#xff0c;其实主要分为以下三种&#xff1a; ①使用开发工具idea创建springboot项目&#xff08; Sp…

Android 设置铃声和闹钟

Android设置铃声和闹钟使用的方法是一样的&#xff0c;但是要区别的去获取对应的权限。 统一权限&#xff0c;不管是设置闹钟还是铃声&#xff0c;他们都需要一个系统设置权限如下: //高版本需要WRITE_SETTINGS权限//此权限是敏感权限&#xff0c;无法动态申请&#xff0c;需要…

三维扫描在汽车/航空行业应用

三维扫描技术应用范围广泛&#xff0c;从小型精密零件到大型工业设备&#xff0c;都能实现快速、准确的测量。 通过先进三维扫描技术获取产品和物体的形面三维数据&#xff0c;建立实物的三维图档&#xff0c;满足各种实物3D模型数据获取、三维数字化展示、3D多媒体开发、三维…

optuna和 lightgbm

文章目录 optuna使用1.导入相关包2.定义模型可选参数3.定义训练代码和评估代码4.定义目标函数5.运行程序6.可视化7.超参数的重要性8.查看相关信息9.可视化的一个完整示例10.lightgbm实验 optuna使用 1.导入相关包 import torch import torch.nn as nn import torch.nn.functi…

【Yonghong 企业日常问题 06】上传的文件不在白名单,修改allow.jar.digest属性添加允许上传的文件SH256值?

文章目录 前言问题描述问题分析问题解决1.允许所有用户上传驱动文件2.如果是想只上传白名单的驱动 前言 该方法适合永洪BI系列产品&#xff0c;包括不限于vividime desktop&#xff0c;vividime z-suit&#xff0c;vividime x-suit产品。 问题描述 当我们连接数据源的时候&a…