Got Http response code 400 When executing payment on PayPal Sandbox, using PayPal PHP SDK

I am trying to implement PayPal Checkout with PHP and "paypal/paypal-checkout-sdk": "^1.0"

I manage to create a payment, it redirects correctly on PayPal Sandbox Login, login ok with test users, Amount ok... When I validate, I am returned to my return URL with paymentId, token and PayerId.

I want to execute the payment but I this error :

PayPal\Exception\PayPalConnectionException
Got Http response code 400 when accessing https://api.sandbox.paypal.com/v1/payments/payment/PAYID-L7KLVNA5VA13973Y0247405F/execute.

I was inspired by : https://github.com/EvolutedNewMedia/paypal-rest-api-example/blob/master/src/response.php

Also tried that : https://paypal.github.io/PayPal-PHP-SDK/sample/doc/payments/ExecutePayment.html

Here is my code of my return URL :

public function actionResponse($paymentId, $PayerID)
    {
        if( $paypalPayment = PaypalPayment::find()->where(['transaction_id' => $paymentId])->one() ) {
            $order = $paypalPayment->getOrder()->one();
        }else{
            die('Can not find PayPal reference in DB');
        }

        $apiContext = $this->getApiContext(self::OMT_PAYPAL_CLIENT_ID, self::OMT_PAYPAL_CLIENT_SECRET, self::OMT_PAYPAL_SANDBOX);

        $amount = new Amount();
        $amount->setCurrency($order->currency);
        $amount->setTotal($order->amount/100);

        $transaction = new Transaction();
        $transaction->setAmount($amount);


        $execution = new PaymentExecution();
        $execution->setPayerId($PayerID);
        $execution->addTransaction($transaction);

        //die($execution);

        $payment = Payment::get($paymentId, $apiContext);

        try {
            $payment->execute($execution, $apiContext);

            try {

                $paypalPayment = PaypalPayment::find()->where(['transaction_id' => $payment->getId()])->one();
                $paypalPayment->payment_amount =  $payment->transactions[0]->amount->total;
                $paypalPayment->payment_status =  $payment->getState();
                $paypalPayment->invoice_id =  $payment->transactions[0]->invoice_number;

                if ($paypalPayment->save() && $paypalPayment->payment_status === 'approved') {
                    // Payment successfully added, redirect to the payment complete page.
                    echo 'success';
                } else {
                    // Payment failed
                    echo 'Payment failed';

                }

            } catch (Exception $ex) {
                // Failed to retrieve payment from PayPal
                die('Failed to retrieve payment from PayPal');
                echo $ex->getCode();
                echo $ex->getData();
                die($ex);

            }

        } catch (Exception $ex) {
            // Failed to take payment
            die('Failed to take payment from PayPal');
            echo $ex->getCode();
            echo $ex->getData();
            die($ex);

        }
    }

Here is the $payment object before execute ( $payment->execute($execution, $apiContext); )

I also dont understand why state = failed, I just got back grom PayPal approval


{
    "id": "PAYID-L7KNALA80B388166U211654T",
    "intent": "sale",
    "state": "failed",
    "cart": "3AY17068NG554090V",
    "payer": {
        "payment_method": "paypal",
        "status": "VERIFIED",
        "payer_info": {
            "email": "sb-mbri64023415@business.example.com",
            "first_name": "John",
            "last_name": "Doe",
            "payer_id": "MWYZL3GMGV86S",
            "shipping_address": {
                "recipient_name": "John Doe",
                "line1": "Rue du Cornet 6",
                "city": "Verviers",
                "state": "BE_zip = 4800",
                "postal_code": "4800",
                "country_code": "BE"
            },
            "country_code": "BE",
            "business_name": "John Doe's Test Store"
        }
    },
    "transactions": [
        {
            "amount": {
                "total": "5880.60",
                "currency": "EUR",
                "details": {
                    "subtotal": "5880.60",
                    "tax": "0.00",
                    "shipping": "0.00"
                }
            },
            "payee": {
                "merchant_id": "AEVEQ26UKMLZL",
                "email": "XXXX"
            },
            "description": "XXXX",
            "custom": "b1900ec71f8694995925602422215428",
            "invoice_number": "b1900ec71f8694995925602422215428",
            "item_list": {
                "shipping_address": {
                    "recipient_name": "John Doe",
                    "line1": "Rue du Cornet 6",
                    "city": "Verviers",
                    "state": "BE_zip = 4800",
                    "postal_code": "4800",
                    "country_code": "BE"
                }
            },
            "related_resources": []
        }
    ],
    "create_time": "2020-12-12T14:14:04Z",
    "update_time": "2020-12-12T14:17:17Z",
    "links": [
        {
            "href": "https://api.sandbox.paypal.com/v1/payments/payment/PAYID-L7KNALA80B388166U211654T",
            "rel": "self",
            "method": "GET"
        },
        {
            "href": "https://api.sandbox.paypal.com/v1/payments/payment/PAYID-L7KNALA80B388166U211654T/execute",
            "rel": "execute",
            "method": "POST"
        },
        {
            "href": "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-3AY17068NG554090V",
            "rel": "approval_url",
            "method": "REDIRECT"
        }
    ]
}

Any Idea ?

Thanks :) Clem

Answer

Solution:

The v1/payments PayPal-PHP-SDK is deprecated.

Use the v2 Checkout-PHP-SDK, documented here: https://developer.paypal.com/docs/business/checkout/server-side-api-calls/

You'll need two routes, one for 'Create an Order' and one for 'Capture Order'.

The best approval flow to pair with the above two routes is https://developer.paypal.com/demo/checkout/#/pattern/server

Source