PHP 验证 Paypal 网络钩子签名

Mik*_*Moy 6 php validation paypal paypal-sandbox

我在尝试使用 PHP 验证贝宝 webhook 签名时遇到问题。使用贝宝 API 的新 V2,我在我的页面上收到了贝宝 webhook。但是我似乎无法成功验证签名。

这里的链接我得到了一些来自贝宝的示例 webhook 验证 PHP 代码。

我无法让它工作,我不知道我应该在哪里从 paypal 代码中获取 bootstrap.php。贝宝信息似乎不完整或半成品。与 Stripe 相比,Paypal 的设置似乎很糟糕。

有没有人在使用 V2 的 paypal API 时有使用 PHP 验证 paypal webhook 签名的经验?

Mik*_*Moy 15

好吧,我得出的结论是,Paypal 开发人员信息相当糟糕,它散布在各个不同的页面和站点上。他们的贝宝开发者网站上给出的示例这里是不是需要什么来验证网络挂接签名的完整画面。Stripe 开发人员文档的格式和简洁得多。

安装 Paypal Checkout V2 SDK 并没有为您提供必要的开发工具来验证 Paypal webhook 签名,即您可以处理付款和接收 webhook 但您无法验证 webhook 签名......我知道愚蠢。提示不要直接下载 SDK,因为您不会包含所需的 autoload.php 文件。使用 composer 安装 Paypal Checkout V2 SDK 以便获得 autoload.php 文件。

一旦您可以处理付款并接收来自 paypal 的 webhooks,您需要安装另一个名为 Paypal Rest API SDK 的 SKD。再次使用 composer 安装 SDK,以便您获得一个您需要的 autoload.php 文件。

当您惊人地安装 Paypal Rest API SDK 时,您仍然会丢失验证 payapl webhook 签名所需的文件。我在 paypal 开发者网站上的任何地方都找不到这些内容。

bootstrap.php 和 common.php

感谢@Grumpy,我在 github HERE上获得了一些示例

请注意,您可能需要稍微修改示例,以便让它们与您的网站一起使用。提示将记录器设置为 false,如果您没有写入的必要访问权限,则可以省去一些麻烦。

一旦你创建了 bootstrap.php 和 common.php 文件,你就可以为你的 webhook 端点页面编写代码,即贝宝将 webhook 发送到的页面。我在下面包含了我的 PHP 代码,以了解如何验证和处理 paypal webhook。在下面的代码中提示您需要指定 webhook ID,您在 paypal 中创建的每个 webhook 都有一个唯一的 ID。此外,当您进行测试时,您不能使用 webhook 模拟器,因为这会导致验证失败,您可以使用沙箱帐户详细信息手动付款,这将触发 webhook 付款事件。

Paypal 肯定不会让它变得容易,与 Stripe 相比,他们的文档无处不在。Paypal webhook 有时可能需要几分钟才能在付款后到达,在尝试调试时非常令人沮丧。此外,他们在贝宝开发人员网站上有一个无法用于验证签名的网络钩子模拟器,这有点荒谬……如果条纹可以做到,为什么贝宝不能。

<?php





//get the webhook payload

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

//check if webhook payload has data
if($requestBody) {
//request body is set
} else {
//request body is not set
exit(); 
}




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

$apiContext = require __DIR__ . '/bootstrap.php';




//Receive HTTP headers that you received from PayPal webhook.

$headers = getallheaders();


//need header keys to be UPPERCASE

$headers = array_change_key_case($headers, CASE_UPPER);


/*

example header paypal signature content for webhook, these values are recieved as an array, we then need to use this data to verify the payload


CONTENT-LENGTH : 1376

CORRELATION-ID : 6db85170269e7

USER-AGENT : PayPal/AUHD-214.0-54377828

CONTENT-TYPE: application/json

PAYPAL-AUTH-ALGO : SHA256withRSA

PAYPAL-CERT-URL : https://api.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a784-5edc0ebc

PAYPAL-AUTH-VERSION : v2

PAYPAL-TRANSMISSION-SIG : Hc2lsDedYdSjOM4/t3T/ioAVQqFPNVB/AY/EyPNlavXk5WYUfnAmt9dyEP6neAPOjFHiVkXMK+JlLODbr6dalw6i26aFQdsPXqGl38Mafuu9elPE74qgsqNferUFgHi9QFXL+UZCNYcb4mvlDePXZIIAPbB0gOuFGOdEv2uqNwTCSAa/D8aguv1/51FWb3RkytFuVwXK/XNfIEy2oJCpDs8dgtYAZeojH8qO6IAwchdSpttMods5YfNBzT7oCoxO80hncVorBtjj1zQrkoynEB9WNNN9ytepNCkT8l29fQ4Sx/WRndm/PESCqxqmRoYJoiSosxYU3bZP7QTtILDykQ==

PAYPAL-TRANSMISSION-TIME : 2020-04-05T14:40:43Z

PAYPAL-TRANSMISSION-ID : 6dec99b0-774b-11ea-b306-c3ed128f0c4b


*/


//if any of the relevant paypal signature headers are not set exit()

if(
(!array_key_exists('PAYPAL-AUTH-ALGO', $headers)) ||
(!array_key_exists('PAYPAL-TRANSMISSION-ID', $headers)) ||
(!array_key_exists('PAYPAL-CERT-URL', $headers)) ||
(!array_key_exists('PAYPAL-TRANSMISSION-SIG', $headers)) ||
(!array_key_exists('PAYPAL-TRANSMISSION-TIME', $headers)) 
)
{

exit();     
}

//specify the ID for the webhook that you have set up on the paypal developer website, each web hook that you create has a unique ID


$webhookID = "ENTER_YOUR_WEBHOOK_ID_HERE";




//start paypal webhook signature validation 

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

$signatureVerification->setRequestBody($requestBody);
$request = clone $signatureVerification;

try {

$output = $signatureVerification->post($apiContext);

} catch (Exception $ex) {

//error during signature validation, capture error and exit

ResultPrinter::printError("Validate Received Webhook Event", "WebhookEvent", null, $request->toJSON(), $ex);
exit(1);

}


$sigVerificationResult = $output->getVerificationStatus();

// $sigVerificationResult is a string and will either be "SUCCESS" or "FAILURE"


//if not webhook signature failed validation exit
if($sigVerificationResult != "SUCCESS"){

exit(); 
}
else if($sigVerificationResult == "SUCCESS"){

//paypay webhook signature is valid

//proceed to process webhook payload


//decode raw request body

$requestBodyDecode = json_decode($requestBody);


//pull whatever info required from decoded request body, some examples below


$paymentSystemID = $requestBodyDecode->id;


$eventType = $requestBodyDecode->event_type;


//do something with info captured from the webhook payload


} 
Run Code Online (Sandbox Code Playgroud)

  • 这仍然是今天(22 年 1 月)的有效解决方案吗?好奇为什么 https://github.com/paypal/PayPal-PHP-SDK 上说这个包已被弃用?(并存档)? (3认同)
  • “Paypal 开发者信息相当贫乏,分散在各处”如果我能给你 +100 我会给你 (3认同)
  • 根据 Paypal 开发文档“PHP 目前不支持证书链验证,这是从接收到的数据直接验证 Webhook 所必需的。为了解决这个问题,我们需要使用替代方案,即调用 PayPal 的 verify-webhook-signature API。” https://paypal.github.io/PayPal-PHP-SDK/sample/doc/notifications/ValidateWebhookEvent.html (2认同)