此处引用答案:PHP验证Paypal webhook签名
这仍然是今天(1月22日(的有效解决方案吗?好奇为什么它在github.com/paypal/paypal-PHP-SDK上说这个包不推荐使用?(并存档(?
这就是我如何用PHP:验证从PayPal到我的webhook的调用
$success = (
openssl_verify(
data: implode(separator: '|', array: [
$httpPayPalTransmissionId,
$httpPayPalTransmissionTime,
$webhookID,
crc32(string: $rawRequestBody),
]),
signature: base64_decode(string: $httpPayPalTransmissionSignature),
public_key: openssl_pkey_get_public(public_key: file_get_contents(filename: $httpPayPalCertUrl)),
algorithm: 'sha256WithRSAEncryption'
) === 1
);
该SDK已弃用且未维护。没有人支持它。
有两种受支持的方法来验证webhook,一种是将其发布回API端点,另一种是自己验证加密消息签名。
- 验证消息签名
- 返回PayPal
这两个文档都不是PHP特有的,但都有必要的信息来适应您自己在任何语言中的实现。
我刚刚为wordpress完成了这项工作,可以根据您的需要进行更改。
确保在仪表板中创建包含所需事件的webhook,并从中获取要使用的ID。
创建一个函数为贝宝生成身份验证令牌:
function paypal_access_token() {
$clientID = 'PAYPAL_CLIENT_ID';
$clientSecret = 'PAYPAL_SECRET_key';
$auth = base64_encode( $clientID . ':' . $clientSecret );
$cacheKey = 'PAYPAL_ACCESS_TOKEN_' . $clientID;
$token = get_transient($cacheKey);
if ($token === false){
$body = [
'grant_type' => 'client_credentials',
];
$credential_post = wp_remote_post(
'https://api-m.sandbox.paypal.com/v1/oauth2/token',
array(
'method' => 'POST',
'headers' => array(
'Authorization' => "Basic $auth",
'Content-Type' => 'application/x-www-form-urlencoded',
),
'body' => urlencode_deep( $body ),
)
);
$result = wp_remote_retrieve_body( $credential_post );
$token = json_decode($result, true);
set_transient($cacheKey, $token['access_token'], ($token['expires_in'] - 60));
$token = $token['access_token'];
}
return $token;
}
然后创建一个验证事件函数:
function paypal_verify_webhook($args){
$token = paypal_access_token();
$paypal_post = wp_remote_post(
'https://api-m.sandbox.paypal.com/v1/notifications/verify-webhook-signature',
array(
'method' => 'POST',
'headers' => array(
'Content-Type' => 'application/json',
'Authorization' => "Bearer $token",
),
'body' => wp_json_encode( $args, JSON_UNESCAPED_SLASHES ),
)
);
$paypal_response = wp_remote_retrieve_body( $paypal_post );
if ( !is_wp_error( $paypal_response ) ) {
return $paypal_response;
}
return false;
}
最后是webhook文件本身:
$requestBody = file_get_contents('php://input');
if(!$requestBody) {
exit();
}
$headers = getallheaders();
$headers = array_change_key_case($headers, CASE_UPPER);
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();
}
$webhook_id = 'WEBHOOK_ID_FROM_DASHBOARD';
$args = array(
'auth_algo' => $headers['PAYPAL-AUTH-ALGO'],
'cert_url' => $headers['PAYPAL-CERT-URL'],
'transmission_id' => $headers['PAYPAL-TRANSMISSION-ID'],
'transmission_sig' => $headers['PAYPAL-TRANSMISSION-SIG'],
'transmission_time' => $headers['PAYPAL-TRANSMISSION-TIME'],
'webhook_id' => $webhook_id,
'webhook_event' => json_decode($requestBody)
);
$verify_webhook = paypal_verify_webhook($args);
$verify_status = json_decode($verify_webhook, true);
if( isset($verify_status['verification_status'] ) && $verify_status['verification_status'] === "SUCCESS"){
$data = json_decode($requestBody, true);
if (isset($data['event_type'])) {
$event_type = $data['event_type'];
switch ($event_type) {
case 'CHECKOUT.ORDER.COMPLETED':
// Do Stuff...
break;
case 'PAYMENT.SALE.COMPLETED':
// Do Stuff...
break;
// Add more cases etc...
default:
// Do Stuff...
break;
}
}
http_response_code(200);
exit();
}
else{
exit();
}