You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Laravel集成PayPal支付按钮报错求助

PayPal支付集成问题排查(Laravel+React+Omnipay)

初次接触Laravel后端和React前端,尝试用Omnipay实现PayPal支付按钮功能,点击支付按钮后出现大量错误(错误截图显示请求解析及响应处理类错误)。

相关代码

前端React代码

export default function OnePost({ postP }) {
    const { data, setData, post, processing, errors, reset } = useForm({
        amount: "",
    });

    const pay = (e, value) => {
        e.preventDefault();
        setData({ amount: value });
    };

    useEffect(() => {
        if (data.amount !== "") {
            post(route("payment", [data.amount]));
        }
    }, [data.amount]);
    return (
            ... 
                    <div className="">
                        {" "}
                        <form
                            onSubmit={(e) => pay(e, postP.price)}
                            encType="multipart/form-data"
                        >
                            <input
                                type="hidden"
                                name="amount"
                                value={postP.price}
                            />
                            {postP.price !== 0 && (
                                <PrimaryButton
                                    type="submit"
                                    disabled={processing}
                                >
                                    Buy for {postP.price}
                                </PrimaryButton>
                            )}
                        </form>
                    </div>
                </div>
    );
}

Laravel路由配置(web.php)

Route::post('pay', [PaymentController::class, 'pay'])->name('payment');
Route::get('success', [PaymentController::class, 'success']);
Route::get('error', [PaymentController::class, 'error']);

PaymentController代码

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Omnipay\Omnipay;
use App\Models\Payment;

class PaymentController extends Controller
{
    private $gateway;


    public function __construct() {
        $this->gateway = Omnipay::create('PayPal_Rest');
        $this->gateway->setClientId(env('PAYPAL_CLIENT_ID'));
        $this->gateway->setSecret(env('PAYPAL_CLIENT_SECRET'));
        $this->gateway->setTestMode(true);
    }

    public function pay(Request $request)
    {
        try {

            $response = $this->gateway->purchase(array(
                'amount' => $request->amount,
                'currency' => env('PAYPAL_CURRENCY'),
                'returnUrl' => url('success'),
                'cancelUrl' => url('error')
            ))
            ->send();

            if ($response->isRedirect()) {
                // dd($response);
                $response->redirect();
                //send json response back
                
            }
            else{
                return $response->getMessage();
            }

        } catch (\Throwable $th) {
            return $th->getMessage();
        }
    }

    public function success(Request $request)
    {
        if ($request->input('paymentId') && $request->input('PayerID')) {
            $transaction = $this->gateway->completePurchase(array(
                'payer_id' => $request->input('PayerID'),
                'transactionReference' => $request->input('paymentId')
            ));

            $response = $transaction->send();

            if ($response->isSuccessful()) {

                $arr = $response->getData();

                $payment = new Payment();
                $payment->payment_id = $arr['id'];
                $payment->payer_id = $arr['payer']['payer_info']['payer_id'];
                $payment->payer_email = $arr['payer']['payer_info']['email'];
                $payment->amount = $arr['transactions'][0]['amount']['total'];
                $payment->currency = env('PAYPAL_CURRENCY');
                $payment->payment_status = $arr['state'];

                $payment->save();

                return "Payment is Successfull. Your Transaction Id is : " . $arr['id'];

            }
            else{
                return $response->getMessage();
            }
        }
        else{
            return 'Payment declined!!';
        }
    }

    public function error()
    {
        return 'User declined the payment!';   
    }
}

错误原因分析

  1. 前后端响应不匹配
    后端pay方法中,当Omnipay返回跳转响应时,调用了$response->redirect()——这是服务端重定向逻辑,仅适用于传统表单提交。而前端通过useForm的post方法发起AJAX请求,无法处理服务端返回的重定向响应,会收到HTML内容而非预期的JSON,导致前端解析错误。

  2. 不必要的表单编码类型
    表单设置了encType="multipart/form-data",但没有上传文件需求,该编码会导致请求格式异常,后端可能无法正确解析amount参数。

  3. 前端请求触发逻辑不合理
    通过useEffect监听data.amount变化发起请求,可能导致重复触发请求;且未处理请求失败的情况,错误发生时无捕获逻辑。

  4. 后端参数验证缺失
    未对$request->amount做数值合法性验证,若传入非数值格式,会直接导致Omnipay请求失败。

  5. 环境变量可能未配置
    env('PAYPAL_CURRENCY')若未在.env文件中配置,会导致PayPal请求缺少必填的货币参数。

修复建议

前端修复

  • 移除表单的encType="multipart/form-data"属性
  • 调整请求逻辑,直接在pay方法中发起请求并处理跳转,示例:
const pay = async (e, value) => {
    e.preventDefault();
    try {
        const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
        const response = await fetch(route("payment"), {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRF-TOKEN': csrfToken
            },
            body: JSON.stringify({ amount: value })
        });
        const resData = await response.json();
        if (resData.redirectUrl) {
            window.location.href = resData.redirectUrl;
        }
    } catch (err) {
        console.error('请求失败:', err);
    }
};
  • 移除useEffect中监听data.amount发起请求的逻辑,避免重复请求

后端修复

  • 修改pay方法,返回JSON格式的跳转URL给前端:
public function pay(Request $request)
{
    try {
        // 添加参数验证
        $request->validate([
            'amount' => 'required|numeric|min:0.01'
        ]);

        $response = $this->gateway->purchase([
            'amount' => $request->amount,
            'currency' => env('PAYPAL_CURRENCY', 'USD'), // 设置默认值
            'returnUrl' => url('success'),
            'cancelUrl' => url('error')
        ])->send();

        if ($response->isRedirect()) {
            return response()->json([
                'redirectUrl' => $response->getRedirectUrl()
            ]);
        } else {
            return response()->json(['error' => $response->getMessage()], 400);
        }

    } catch (\Throwable $th) {
        return response()->json(['error' => $th->getMessage()], 500);
    }
}
  • 确保.env文件中配置PAYPAL_CURRENCY(如PAYPAL_CURRENCY=USD)

内容的提问来源于stack exchange,提问作者Evan Washington

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 22:25:02