1

I've been searching for this for hours, I hope someone here can help. I built a subscription based site on Laravel and PayPal subscriptions using the PayPal PHP SDK. Everything works perfectly except on thing: I created a webhook for when a user cancels the payment on his end. I'm getting this error:

Got Http response code 400 when accessing https://api.sandbox.paypal.com/v1/notifications/verify-webhook-signature.{"name":"VALIDATION_ERROR","message":"Invalid data provided","debug_id":"7225cebfec35a","information_link":"https://developer.paypal.com/docs/api/webhooks/#errors","details":[{"field":"webhook_id","location":"body","issue":"Required field cannot be blank"}],"links":[]}  

Here is my code:

public function webhook()
{
    

    /**
    * Receive the entire body that you received from PayPal webhook.
    */
    $bodyReceived = file_get_contents('php://input'); 

    // Receive HTTP headers that you received from PayPal webhook.
    $headers = getallheaders(); 

    /**
    * Uppercase all the headers for consistency
    */
    $headers = array_change_key_case($headers, CASE_UPPER); 

    $signatureVerification = new \PayPal\Api\VerifyWebhookSignature(); 
    $signatureVerification->setWebhookId(env('PAYPAL_WEBHOOK_ID')); 
    $signatureVerification->setAuthAlgo($headers['PAYPAL-AUTH-ALGO']); 
    $signatureVerification->setTransmissionId($headers['PAYPAL-TRANSMISSION-ID']); 
    $signatureVerification->setCertUrl($headers['PAYPAL-CERT-URL']); 
    $signatureVerification->setTransmissionSig($headers['PAYPAL-TRANSMISSION-SIG']); 
    $signatureVerification->setTransmissionTime($headers['PAYPAL-TRANSMISSION-TIME']); 

    $webhookEvent = new \PayPal\Api\WebhookEvent(); 
    $webhookEvent->fromJson($bodyReceived); 
    $signatureVerification->setWebhookEvent($webhookEvent); 
    $request = clone $signatureVerification; 

    try {
        $output = $signatureVerification->post($this->apiContext);
        
    } catch(\Exception $ex) {
        //This is where it fails
        print_r($ex->getMessage());
        exit(1);
    }

    $verificationStatus = $output->getVerificationStatus();
    $responseArray = json_decode($request->toJSON(), true);

    $event = $responseArray['webhook_event']['event_type'];

    if ($verificationStatus == 'SUCCESS')
    { 

        switch($event)
        {
            case 'BILLING.SUBSCRIPTION.CANCELLED':
            case 'BILLING.SUBSCRIPTION.SUSPENDED':
            case 'BILLING.SUBSCRIPTION.EXPIRED':
            case 'BILLING_AGREEMENTS.AGREEMENT.CANCELLED':

            // $user = User::where('payer_id',$responseArray['webhook_event']['resource']['payer']['payer_info']['payer_id'])->first();
            $this->updateStatus($responseArray['webhook_event']['resource']['payer']['payer_info']['payer_id'], 0,1);
            
            break;
        }
    }
    echo $verificationStatus;
    
    exit(0);
}

And here is the $this->apiContext:

trait PayPalApiCredentialsTrait {

private $apiContext;

public function setCredentials()
{
    $this->apiContext = new \PayPal\Rest\ApiContext(
        new \PayPal\Auth\OAuthTokenCredential(
            env('PAYPAL_CLIENT_ID'),     // ClientID
            env('PAYPAL_CLIENT_SECRET')      // ClientSecret
        )
    );

    $this->apiContext->setConfig(
        array(
            'mode' => env("PAYPAL_MODE"),
            'log.LogEnabled' => true,
            'log.FileName' => '../PayPal.log',
            'log.LogLevel' => 'INFO', // PLEASE USE `INFO` LEVEL FOR LOGGING IN LIVE ENVIRONMENTS
        )
    );
}

}

This is the error I get from the paypal.log:

        [01-09-2020 15:54:18] PayPal\Core\PayPalHttpConnection : INFO: POST https://api.sandbox.paypal.com/v1/oauth2/token
    [01-09-2020 15:54:18] PayPal\Core\PayPalHttpConnection : INFO: Response Status  : 200
    [01-09-2020 15:54:18] PayPal\Core\PayPalHttpConnection : INFO: POST https://api.sandbox.paypal.com/v1/notifications/verify-webhook-signature
    [01-09-2020 15:54:19] PayPal\Core\PayPalHttpConnection : INFO: Response Status  : 400
[01-09-2020 15:54:19] PayPal\Core\PayPalHttpConnection : ERROR: Got Http response code 400 when accessing https://api.sandbox.paypal.com/v1/notifications/verify-webhook-signature. {"name":"VALIDATION_ERROR","message":"Invalid data provided","debug_id":"26b12ee43cddd","information_link":"https://developer.paypal.com/docs/api/webhooks/#errors","details":[{"field":"webhook_id","location":"body","issue":"Required field cannot be blank"}],"links":[]}

I must mention that everything else works fine. Creating plans, agreements, cancelling the both, showing active plans, and more... Everything works smoothly. This is the only thing that I can't seem to fix. If anyone could figure this out for me, I'd really appreciate it. Thank you!

Avi
  • 728
  • 3
  • 10
  • 20
  • i was using paypal-sdk-api, sometime it is paypal service unavailable. kinda check paypal.log for the response – Alzafan Christian Sep 01 '20 at 15:52
  • Thanks for the response. I edited and added the exact paypal.log output. This doesn't seem to be the case, or else it would say so, no? Since it says "VALIDATION_ERROR" it seems as if the login details are wrong, but they can't be since they work everywhere else. – Avi Sep 01 '20 at 16:03
  • i didnt do webhooks before, but i found this function setRequestBody() is missing, maybe this is the problem – Alzafan Christian Sep 01 '20 at 16:09
  • Didn't work. PayPal sandbox notifications is under maintenance: https://www.paypal-status.com/product/sandbox Could THIS actually be the issue and not my code? – Avi Sep 02 '20 at 07:26
  • maybe yes maybe no idk for sure, i've told u sometime the service is unavailable xD – Alzafan Christian Sep 02 '20 at 07:42

1 Answers1

-1

The PayPal-PHP-SDK is deprecated and no longer maintained; it should not be used for new integrations. Its implementation of billing plans and subscriptions is old, and not compatible with the current version of the Subscriptions API.

In its place, a direct HTTPS integration (no SDK) should be used for Subscriptions, and for verifying webhooks.

(For the one time payment use case, there is a new v2 Checkout-PHP-SDK https://developer.paypal.com/docs/api/rest-sdks/ )

Preston PHX
  • 27,642
  • 4
  • 24
  • 44
  • I understand. However the entire code is already written and it can't be changed for the time being. There is no reason this shouldn't work if the rest works... PayPal sandbox notifications is under maintenance: paypal-status.com/product/sandbox Could THIS actually be the issue and not my code? Any idea? – Avi Sep 02 '20 at 07:28