Skip to content
This repository has been archived by the owner on Sep 29, 2023. It is now read-only.

Failed to validate Webhook event BILLING.PLAN.CREATED #798

Closed
knifesk opened this issue Mar 10, 2017 · 7 comments
Closed

Failed to validate Webhook event BILLING.PLAN.CREATED #798

knifesk opened this issue Mar 10, 2017 · 7 comments
Labels

Comments

@knifesk
Copy link

knifesk commented Mar 10, 2017

General information

  • SDK/Library version: 1.11.0
  • Environment: Sandbox
  • PayPal-Debug-ID values:
  • Language, language version, and OS: PHP 7.1, CentOS 6.5

Issue description

I am unable to validate a webhook request BILLING.PLAN.CREATED. The same implementation validates another event type (PAYMENT.SALE.COMPLETED) just perfect.. I've tracked the problem down to the bit that I think is causing the problem:

BILLING.PLAN.CREATED event contains a property named "plans" that contains a property named "links", this propery is an array of objects... as it comes empty, when I set the request body (as string) to the $webhookEvent->fromJson() method, it parses it as an object.. I found this out because I diffed both strings and came up with this:

martin@devbox:~/Desktop$ diff a.json b.json 
61c61
<             "links": [],
---
>             "links": {},

This code is where the error is coming

$webhookEvent = new WebhookEvent();
$webhookEvent->fromJson($body);
$setWebhookEvent($webhookEvent);

Full received body

{"id":"WH-98369035J8790723J-86X201489X416192T","event_version":"1.0","create_time":"2017-03-09T22:53:49Z","resource_type":"Agreement","event_type":"BILLING.SUBSCRIPTION.CREATED","summary":"A billing subscription was created","resource":{"agreement_details":{"outstanding_balance":{"value":"0.00"},"num_cycles_remaining":"0","num_cycles_completed":"0","next_billing_date":"2017-03-09T10:00:00Z","last_payment_date":"2017-03-09T22:53:48Z","last_payment_amount":{"value":"10.00"},"final_payment_due_date":"1970-01-01T00:00:00Z","failed_payment_count":"0"},"description":"Obfuscated Prueba 1","links":[{"href":"api.sandbox.paypal.com/v1/payments/billing-agreements/I-KTC2A0BLXVGD","rel":"self","method":"GET"}],"shipping_address":{"recipient_name":"test buyer","line1":"1 Main St","city":"San Jose","state":"CA","postal_code":"95131","country_code":"US"},"id":"I-KTC2A0BLXVGD","state":"Active","payer":{"payment_method":"paypal","status":"verified","payer_info":{"email":"[email protected]","first_name":"","last_name":"","payer_id":"U6GSXHMT5SBG8","shipping_address":{"recipient_name":"test buyer","line1":"1 Main St","city":"San Jose","state":"CA","postal_code":"95131","country_code":"US"}}},"plan":{"curr_code":"EUR",

"links":[],

"payment_definitions":[{"type":"REGULAR","frequency":"Month","frequency_interval":"1","amount":{"value":"100.00"},"cycles":"0","charge_models":[{"type":"TAX","amount":{"value":"0.00"}},{"type":"SHIPPING","amount":{"value":"0.00"}}]}],"merchant_preferences":{"setup_fee":{"value":"10.00"},"auto_bill_amount":"YES","max_fail_attempts":"2"}},"start_date":"2017-03-09T08:00:00Z"},"links":[{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-98369035J8790723J-86X201489X416192T","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-98369035J8790723J-86X201489X416192T/resend","rel":"resend","method":"POST"}]}

Full posted body

{"webhook_id":"40291045TG185602Y","auth_algo":"SHA256withRSA","transmission_id":"37e451f0-051d-11e7-9432-6b62a8a99ac4","cert_url":"https://api.sandbox.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-a5cafa77","transmission_sig":"Rgc6Vbg/zP78FItMowi5rPCxMJ8SltXzCPtJRw0ZPDQytafQAWBpRR8iovtbuhp92TDEl8AhVTqt/Y1uNJ+Bjhoxe/+eMppQ7KT3VAUkNidPL2tdglknqWU5xZM2BBoISl1UgV667Spmq0KXenoIP4fYVuHcVyCC2dpWM1k8qNB/hkTk6N7nzML+bm0E+/j7LbanQNDMA2mtbMOE55264Ovmt80Rnv4iTbmhTTiR7M8amS+y7xvpEo5jYkyG0Ip4Y59UP51mYMB+E5mv0Ga6MHOE31LjeLNj86VhFXhzvDcoxIdjCDVnBc4z0Y52uDCN4nWi9Rv5P+h5PcctXgwmwQ==","transmission_time":"2017-03-09T23:07:50Z","webhook_event":{"id":"WH-98369035J8790723J-86X201489X416192T","event_version":"1.0","create_time":"2017-03-09T22:53:49Z","resource_type":"Agreement","event_type":"BILLING.SUBSCRIPTION.CREATED","summary":"A billing subscription was created","resource":{"agreement_details":{"outstanding_balance":{"value":"0.00"},"num_cycles_remaining":"0","num_cycles_completed":"0","next_billing_date":"2017-03-09T10:00:00Z","last_payment_date":"2017-03-09T22:53:48Z","last_payment_amount":{"value":"10.00"},"final_payment_due_date":"1970-01-01T00:00:00Z","failed_payment_count":"0"},"description":"Obfuscated Prueba 1","links":[{"href":"api.sandbox.paypal.com/v1/payments/billing-agreements/I-KTC2A0BLXVGD","rel":"self","method":"GET"}],"shipping_address":{"recipient_name":"test buyer","line1":"1 Main St","city":"San Jose","state":"CA","postal_code":"95131","country_code":"US"},"id":"I-KTC2A0BLXVGD","state":"Active","payer":{"payment_method":"paypal","status":"verified","payer_info":{"email":"[email protected]","first_name":"","last_name":"","payer_id":"U6GSXHMT5SBG8","shipping_address":{"recipient_name":"test buyer","line1":"1 Main St","city":"San Jose","state":"CA","postal_code":"95131","country_code":"US"}}},"plan":{"curr_code":"EUR",

"links":{},

"payment_definitions":[{"type":"REGULAR","frequency":"Month","frequency_interval":"1","amount":{"value":"100.00"},"cycles":"0","charge_models":[{"type":"TAX","amount":{"value":"0.00"}},{"type":"SHIPPING","amount":{"value":"0.00"}}]}],"merchant_preferences":{"setup_fee":{"value":"10.00"},"auto_bill_amount":"YES","max_fail_attempts":"2"}},"start_date":"2017-03-09T08:00:00Z"},"links":[{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-98369035J8790723J-86X201489X416192T","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-98369035J8790723J-86X201489X416192T/resend","rel":"resend","method":"POST"}]}}
@knifesk
Copy link
Author

knifesk commented Mar 10, 2017

This is my verification code

    public function validateWebhook($webhookid)
    {
        $headers = array_change_key_case(getallheaders(), CASE_UPPER);
        $body = file_get_contents('php://input');

        $sigVer = new VerifyWebhookSignature();

        $sigVer->setWebhookId($webhookid);

        $sigVer->setAuthAlgo($headers['PAYPAL-AUTH-ALGO']);
        $sigVer->setTransmissionId($headers['PAYPAL-TRANSMISSION-ID']);
        $sigVer->setCertUrl($headers['PAYPAL-CERT-URL']);
        $sigVer->setTransmissionSig($headers['PAYPAL-TRANSMISSION-SIG']);
        $sigVer->setTransmissionTime($headers['PAYPAL-TRANSMISSION-TIME']);

        $webhookEvent = new WebhookEvent();
        $webhookEvent->fromJson($body);
        $sigVer->setWebhookEvent($webhookEvent);

        $request = clone $sigVer;

        try {
            /** @var \PayPal\Api\VerifyWebhookSignatureResponse $output */
            $this->log->debug('Posting VerifyWebhookSignature');
            $this->log->debug($request->toJSON());
            $output = $sigVer->post($this->apiContext);
            $this->log->debug('Output from VerifyWebhookSignature', [$output]);
        } catch (Exception $ex) {
            $this->log->error($ex->getMessage());
            $this->log->error('Exception Data: ' . $ex->getData());
            throw $ex;
        }

        return $output->getVerificationStatus() === 'SUCCESS';
    }

and this is the log:

[2017-03-10 12:44:14] PAYPAL.DEBUG: Posting VerifyWebhookSignature [] {"uid":"845c366"}
[2017-03-10 12:44:17] PAYPAL.DEBUG: Output from VerifyWebhookSignature ["[object] (PayPal\\Api\\VerifyWebhookSignatureResponse: {\n    \"verification_status\": \"FAILURE\"\n})"] {"uid":"845c366"}

@TrevorAtITS
Copy link

TrevorAtITS commented Mar 22, 2017

+1 - I can verify that I am seeing this same behaviour as well, caused by the same diff in links, and I'm on PHP 5.3 ( don't laugh, just pity me )

@randstraw
Copy link

I have escalated this for further review, the validation won't work if the request body doesn't match. Thanks for the helpful difference showing the links being changed from empty array to empty object.

@knifesk
Copy link
Author

knifesk commented Mar 22, 2017

Clearly there's a bug with the (un)serialization, but for this case only we could avoid the error just by avoiding Deserializing and serializing the body.. Of course, if the error its the deserialization there could be other undetected issues related to this

@randstraw
Copy link

I have seen similar issues with parsing the JSON request body then serializing back to JSON. The validation checksum is calculated from the JSON body as a string, not by validating the values in the fields. I learned this the hard way with my webhook listener changing 100.0 to 100.

@TrevorAtITS
Copy link

TrevorAtITS commented Mar 23, 2017

In PayPalModel::_convertToArray() method in the PHP SDK, there is this code:

        // we need to convert array to StdClass object to properly
        // represent JSON String
        if (sizeof($ret) <= 0) {
            $ret = new PayPalModel();
        }

That's the problematic piece, IMHO. If you comment out this line, the event verifies properly. Of course, removing that might well break other things.

@jaypatel512
Copy link
Contributor

Hey @TrevorAtITS !

This can cause issue for us in future. If any object in the APIs are introduced, it would come out as a StdClass, which is fine, but if the developer updates their dependencies once we add those fields to our SDKs, it would become PayPalModel or its child class. This would be a breaking change, and won't let us add new model objects in this SDK in future.

We are overriding and making webhookVerification toJSON to handle this special case for requestBody.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

4 participants