PHP 验证 Paypal webhook 签名
PHP verify Paypal webhook signature
我在尝试使用 PHP 验证 paypal webhook 签名时遇到问题。使用新的 V2 paypals API,我在我的页面上收到了 paypal webhook。
但是我似乎无法成功验证签名。
从 link HERE 我从 paypal 获得了一些示例 webhook 验证 PHP 代码。
我无法让它工作,我不知道我应该从 paypal 代码的哪里得到 bootstrap.php。贝宝信息似乎不完整或半生不熟。与 Stripe 相比,Paypal 的设置似乎很糟糕。
有没有人在使用 V2 的 paypal API 时使用 PHP 验证 paypal webhook 签名的经验?
好吧,我得出的结论是 Paypal 开发人员信息相当少,它散落在各个地方,在多个不同的页面和站点上。他们在 paypal 开发者网站 HERE 上提供的示例并未完整描述验证 webhook 签名所需的内容。 Stripe 开发人员文档的格式和简洁性要好得多。
安装 Paypal Checkout V2 SDK 不会为您提供必要的开发工具来验证 paypal webhook 签名,即您可以处理付款和接收 webhook,但您无法验证 webhook 签名....我知道愚蠢。提示不要直接下载 SDK,因为您不会包含所需的 autoload.php 文件。使用composer安装Paypal Checkout V2 SDK,得到autoload.php文件。
一旦您可以处理付款并从 paypal 接收 webhook,您需要安装另一个名为 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 端点页面编写代码,即 paypal 将 webhook 发送到的页面。我在下面包含了我的 PHP 代码,用于验证和处理 paypal webhook。在下面的代码中提示您需要指定 webhook ID,您在 paypal 中创建的每个 webhook 都有一个唯一的 ID。此外,当您正在测试时,您不能使用 webhook 模拟器,因为这将导致验证失败,您可以使用您的沙盒帐户详细信息手动付款,这将触发 webhook 付款事件。
Paypal 确实不容易,与 Stripe 相比,他们的文档随处可见。付款后 Paypal webhook 有时可能需要几分钟才能到达,这在尝试调试时非常令人沮丧。他们在 paypal 开发者网站上有一个不能用于验证签名的 webhook 模拟器也有点荒谬......如果 stripe 可以做到为什么 paypal 不能。
<?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
}
我在尝试使用 PHP 验证 paypal webhook 签名时遇到问题。使用新的 V2 paypals API,我在我的页面上收到了 paypal webhook。 但是我似乎无法成功验证签名。
从 link HERE 我从 paypal 获得了一些示例 webhook 验证 PHP 代码。
我无法让它工作,我不知道我应该从 paypal 代码的哪里得到 bootstrap.php。贝宝信息似乎不完整或半生不熟。与 Stripe 相比,Paypal 的设置似乎很糟糕。
有没有人在使用 V2 的 paypal API 时使用 PHP 验证 paypal webhook 签名的经验?
好吧,我得出的结论是 Paypal 开发人员信息相当少,它散落在各个地方,在多个不同的页面和站点上。他们在 paypal 开发者网站 HERE 上提供的示例并未完整描述验证 webhook 签名所需的内容。 Stripe 开发人员文档的格式和简洁性要好得多。
安装 Paypal Checkout V2 SDK 不会为您提供必要的开发工具来验证 paypal webhook 签名,即您可以处理付款和接收 webhook,但您无法验证 webhook 签名....我知道愚蠢。提示不要直接下载 SDK,因为您不会包含所需的 autoload.php 文件。使用composer安装Paypal Checkout V2 SDK,得到autoload.php文件。
一旦您可以处理付款并从 paypal 接收 webhook,您需要安装另一个名为 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 端点页面编写代码,即 paypal 将 webhook 发送到的页面。我在下面包含了我的 PHP 代码,用于验证和处理 paypal webhook。在下面的代码中提示您需要指定 webhook ID,您在 paypal 中创建的每个 webhook 都有一个唯一的 ID。此外,当您正在测试时,您不能使用 webhook 模拟器,因为这将导致验证失败,您可以使用您的沙盒帐户详细信息手动付款,这将触发 webhook 付款事件。
Paypal 确实不容易,与 Stripe 相比,他们的文档随处可见。付款后 Paypal webhook 有时可能需要几分钟才能到达,这在尝试调试时非常令人沮丧。他们在 paypal 开发者网站上有一个不能用于验证签名的 webhook 模拟器也有点荒谬......如果 stripe 可以做到为什么 paypal 不能。
<?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
}