PayPal-PHP-SDK icon indicating copy to clipboard operation
PayPal-PHP-SDK copied to clipboard

resource is undefined and verificationStatus is always returning FAILURE

Open viraladmin opened this issue 5 years ago • 7 comments

I have two issues. I am using sandbox mode. My $verificationStatus is always returning FAILURE and $responseArray['webhook_event']['resource']['event_type']; always warns that resource is undefined.

include '../../includes/db.class.php';
require 'vendor/autoload.php';

$stmt= $db->prepare('SELECT pp_public, pp_secret, pp_hook FROM api_config');
$stmt->execute();
$row = $stmt->fetch();
$pp_api = $row['pp_public'];
$pp_secret = $row['pp_secret'];
$pp_webhooks = $row['pp_hook'];

use \PayPal\Api\VerifyWebhookSignature;
use \PayPal\Api\WebhookEvent;

$apiContext = new \PayPal\Rest\ApiContext(
  new \PayPal\Auth\OAuthTokenCredential(
    $pp_api,
    $pp_secret
  )
);

$bodyReceived = file_get_contents('php://input');

$headers = getallheaders();
$headers = array_change_key_case($headers, CASE_UPPER);

$signatureVerification = new VerifyWebhookSignature();
$signatureVerification->setWebhookId($pp_webhooks);
$signatureVerification->setAuthAlgo($headers['PAYPAL-AUTH-ALGO']);
$signatureVerification->setTransmissionId($headers['PAYPAL-TRANSMISSION-ID']);
$signatureVerification->setCertUrl($headers['PAYPAL-CERT-URL']);
$signatureVerification->setTransmissionSig($headers['PAYPAL-TRANSMISSION-SIG']);
$webhookEvent = new WebhookEvent();
$webhookEvent->fromJson($requestBody);

$signatureVerification->setWebhookEvent($webhookEvent);
$request = clone $signatureVerification;

try {
    $output = $signatureVerification->post($apiContext);
} catch (Exception $ex) {
    print_r($ex->getMessage());
    exit(1);
}

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

// this line returns the resource not defined warning and rages on my log files. $event = $responseArray['webhook_event']['resource']['event_type'];

$outputArray = json_decode($output->toJSON(), true);

if ($verificationStatus == 'SUCCESS') {
    switch($event) {
         ........

viraladmin avatar Nov 26 '19 06:11 viraladmin

Hi @viraladmin, can you help us with PayPal-Debug-ID for this issue? Refer Exception Handling to understand your error.

prakash-gangadharan avatar Nov 26 '19 06:11 prakash-gangadharan

Well I'd love to add the PayPal-Debug-ID but as is the case with all hings relating to the paypal API figuring it out is a nightmare. So I add the exception handling to my webhook and it ends up tossing even more errors in my error logs.

Uncaught Error: Call to a member function create() on null Undefined variable: payment

I would guess thats because the webhook doesn't have any $payment so it can't issue a $payment->create from the webhook. Should I be adding this code to the initial payment request? The return url? Or the webhook?

viraladmin avatar Nov 26 '19 07:11 viraladmin

@viraladmin, did you get a chance to refer this sample code? https://github.com/paypal/PayPal-PHP-SDK/blob/master/sample/notifications/ValidateWebhookEvent.php

prakash-gangadharan avatar Nov 26 '19 09:11 prakash-gangadharan

I simply have no idea where this code should go. I have tried all the following:

try {
  $apiContext = new \PayPal\Rest\ApiContext(
    new \PayPal\Auth\OAuthTokenCredential(
      $pp_api,
      $pp_secret
    )
  );
}  catch (PayPal\Exception\PayPalConnectionException $ex) {
  $myfile = fopen("newfile2.txt", "w") or die("Unable to open file!");
  $txt = $ex->getCode();
  fwrite($myfile, $txt);
  $txt = $ex->getData();
  fwrite($myfile, $txt);
  fclose($myfile);
  die($ex);
}

try {
    $signatureVerification->setWebhookEvent($webhookEvent);
    $request = clone $signatureVerification;
}  catch (PayPal\Exception\PayPalConnectionException $ex) {
    $myfile = fopen("newfile2.txt", "w") or die("Unable to open file!");
    $txt = $ex->getCode();
    fwrite($myfile, $txt);
    $txt = $ex->getData();
    fwrite($myfile, $txt);
    fclose($myfile);
    die($ex);
}


try {
    $output = $signatureVerification->post($apiContext);
}  catch (PayPal\Exception\PayPalConnectionException $ex) {
    $myfile = fopen("log.txt", "w") or die("Unable to open file!");
    $txt = $ex->getCode();
    fwrite($myfile, $txt);
    $txt = $ex->getData();
    fwrite($myfile, $txt);
    fclose($myfile);
    die($ex);
}

try {
    $verificationStatus = $output->getVerificationStatus();
} catch (PayPal\Exception\PayPalConnectionException $ex) {
    $myfile = fopen("log.txt", "w") or die("Unable to open file!");
    $txt = $ex->getCode();
    fwrite($myfile, $txt);
    $txt = $ex->getData();
    fwrite($myfile, $txt);
    fclose($myfile);
    die($ex);
}

try {
    $responseArray = json_decode($request->toJSON(), true);
} catch (PayPal\Exception\PayPalConnectionException $ex) {
    $myfile = fopen("log.txt", "w") or die("Unable to open file!");
    $txt = $ex->getCode();
    fwrite($myfile, $txt);
    $txt = $ex->getData();
    fwrite($myfile, $txt);
    fclose($myfile);
    die($ex);
}

try {
    $event = $responseArray['webhook_event']['resource']['event_type'];
} catch (PayPal\Exception\PayPalConnectionException $ex) {
    $myfile = fopen("log.txt", "w") or die("Unable to open file!");
    $txt = $ex->getCode();
    fwrite($myfile, $txt);
    $txt = $ex->getData();
    fwrite($myfile, $txt);
    fclose($myfile);
    die($ex);
}

try {
    $outputArray = json_decode($output->toJSON(), true);
} catch (PayPal\Exception\PayPalConnectionException $ex) {
    $myfile = fopen("log.txt", "w") or die("Unable to open file!");
    $txt = $ex->getCode();
    fwrite($myfile, $txt);
    $txt = $ex->getData();
    fwrite($myfile, $txt);
    fclose($myfile);
    die($ex);
}

Nothing is being saved to log.txt and as its a webhook i cant echo responses. So clearly I have no clue where to implement this sample code.

viraladmin avatar Nov 26 '19 15:11 viraladmin

My guess is its not returning any PayPal-Debug-ID because there is no error. $verificationStatus of "failure" isn't really an error its a proper response that it failed so the "try" is succeeding is just always a failure response. As for why the resource is undefined my guess it that would be because there is no ['resource'] defined in $responseArray['webhook_event']['resource']['event_type']; as it returned failure.

viraladmin avatar Nov 26 '19 16:11 viraladmin

When I'm not blind, you simply forgot this line:

$signatureVerification->setTransmissionTime($headers['PAYPAL-TRANSMISSION-TIME']);

See sample/notifications/ValidateWebhookEvent.php

Please also make sure, that you're accessing those $headers correct, because I had sub arrays for example:

$signature_verification->setCertUrl($headers['PAYPAL-CERT-URL'][0]);
$signature_verification->setTransmissionTime($headers['PAYPAL-TRANSMISSION-TIME'][0]);

Try the following code and logic, which I've updated from mine to your code example:

// Provide login information
$apiContext = new \PayPal\Rest\ApiContext(
    new \PayPal\Auth\OAuthTokenCredential(
        $pp_api, // Client ID
        $pp_secret, // Secret
    )
);

// Define API configuration
// Sandbox: Log and show more than in production (live)
if (config('app.use_sandbox')) {
    $apiContext->setConfig(
        array(
            'mode' => 'sandbox',
            'log.LogEnabled' => true,
            'log.FileName' => __DIR__.'/paypal.log',
            'log.LogLevel' => 'DEBUG'
        )
    );
} else {
    $apiContext->setConfig(
        array(
            'mode' => 'live',
            'log.LogEnabled' => true,
            'log.FileName' => __DIR__.'/paypal.log',
            'log.LogLevel' => 'WARN'
        )
    );
}

// Get request details
$request_body = file_get_contents('php://input');
$headers = array_change_key_case(getallheaders(), CASE_UPPER);

// Verify webhook signature
$signature_verification = new \PayPal\Api\VerifyWebhookSignature();
$signature_verification->setAuthAlgo($headers['PAYPAL-AUTH-ALGO'][0]);
$signature_verification->setTransmissionId($headers['PAYPAL-TRANSMISSION-ID'][0]);
$signature_verification->setCertUrl($headers['PAYPAL-CERT-URL'][0]);
$signature_verification->setWebhookId($pp_webhooks);
$signature_verification->setTransmissionSig($headers['PAYPAL-TRANSMISSION-SIG'][0]);
$signature_verification->setTransmissionTime($headers['PAYPAL-TRANSMISSION-TIME'][0]);
$signature_verification->setRequestBody($request_body);

try {
    $output = $signature_verification->post($apiContext);
} catch (\Exception $ex) {
    return response('Error: Could not verify signature.', 500)->header('Content-Type', 'text/plain');
}

$status = $output->getVerificationStatus(); // 'SUCCESS' or 'FAILURE'

switch(strtoupper($status)) {
    case "SUCCESS":
        $json = json_decode($request_body, 1);
    default:
        return response('Forbidden: Invalid signature.', 403)->header('Content-Type', 'text/plain');
}

switch(strtoupper($json['event_type'])) {
    case "BILLING.PLAN.CREATED":
        // DO SOMETHING with $json (contains the webhook data)
        return response($status, 200)->header('Content-Type', 'text/plain');
}

return response('Error: Invalid webhook.', 500)->header('Content-Type', 'text/plain');

This should work. You may only need to replace all return lines with an echo or something else since return statements will only work in a function.

Sebbo94BY avatar Nov 28 '19 21:11 Sebbo94BY

HI @viraladmin , I hope @Sebi94nbg's above comment helps your query and do you still facing any challenges?

prakash-gangadharan avatar Dec 18 '19 05:12 prakash-gangadharan