Newer
Older
<?php
use CRM_Stripe_ExtensionUtil as E;
class CRM_Stripe_Webhook {

Rich
committed
* Checks whether the payment processors have a correctly configured webhook
*
* @param array $messages

Rich
committed
* @param bool $attemptFix If TRUE, try to fix the webhook.
*
* @throws \CiviCRM_API3_Exception

Rich
committed
public static function check(&$messages, $attemptFix = FALSE) {
$result = civicrm_api3('PaymentProcessor', 'get', [
'class_name' => 'Payment_Stripe',
'is_active' => 1,
]);
foreach ($result['values'] as $paymentProcessor) {

Rich
committed
$messageTexts = [];
$webhook_path = self::getWebhookPath($paymentProcessor['id']);
\Stripe\Stripe::setApiKey(CRM_Core_Payment_Stripe::getSecretKey($paymentProcessor));
try {
$webhooks = \Stripe\WebhookEndpoint::all(["limit" => 100]);
}
catch (Exception $e) {
$error = $e->getMessage();
$messages[] = new CRM_Utils_Check_Message(
'stripe_webhook',

Rich
committed
$error,
E::ts('Stripe Payment Processor: %1 (%2)', [
1 => $paymentProcessor['name'],
2 => $paymentProcessor['id'],
]),
\Psr\Log\LogLevel::ERROR,
'fa-money'
);
continue;
}
$found_wh = FALSE;
foreach ($webhooks->data as $wh) {
if ($wh->url == $webhook_path) {
$found_wh = TRUE;
// Check and update webhook
try {

Rich
committed
$updates = self::checkWebhook($wh);
if ($updates) {
if ($attemptFix) {
// We should try to update the webhook.
$messageTexts[] = E::ts('Unable to update the webhook %1. To correct this please delete the webhook at Stripe and then revisit this page which will recreate it correctly.',
[1 => urldecode($webhook_path)]
);
\Stripe\WebhookEndpoint::update($wh['id'], $updates);
}
else {
$messageTexts[] = E::ts('Problems detected with Stripe webhook %1. Please visit <a href="%2">Fix Stripe Webhook</a> to fix.', [
1 => urldecode($webhook_path),
2 => CRM_Utils_System::url('civicrm/stripe/fix-webhook'),
]);
}
}
}
catch (Exception $e) {

Rich
committed
$messageTexts[] = E::ts('Could not check/update existing webhooks, got error from stripe <em>%1</em>', [
1 => htmlspecialchars($e->getMessage())
]
);
}

Rich
committed
if ($attemptFix) {
try {
// Try to create one.
self::createWebhook($paymentProcessor['id']);
}
catch (Exception $e) {
$messageTexts[] = E::ts('Could not create webhook, got error from stripe <em>%1</em>', [
1 => htmlspecialchars($e->getMessage())
]);
}

Rich
committed
else {
$messageTexts[] = E::ts('Stripe Webhook missing! Please visit <a href="%1">Fix Stripe Webhook</a> to fix.', [
1 => CRM_Utils_System::url('civicrm/stripe/fix-webhook'),
]);

Rich
committed
foreach ($messageTexts as $messageText) {
$messages[] = new CRM_Utils_Check_Message(
'stripe_webhook',
$messageText,
E::ts('Stripe Payment Processor Webhook: %1 (%2)', [
1 => $paymentProcessor['name'],
2 => $paymentProcessor['id'],
]),
\Psr\Log\LogLevel::WARNING,
'fa-money'
);
}
}
}
/**
* Create a new webhook for payment processor
*
* @param int $paymentProcessorId
*/
public static function createWebhook($paymentProcessorId) {
\Stripe\Stripe::setApiKey(CRM_Core_Payment_Stripe::getSecretKeyById($paymentProcessorId));
$params = [
'enabled_events' => self::getDefaultEnabledEvents(),
'api_version' => CRM_Core_Payment_Stripe::getApiVersion(),
'connect' => FALSE,
];
\Stripe\WebhookEndpoint::create($params);
}

Rich
committed
/**
* Check and update existing webhook
*
* @param array $webhook

Rich
committed
* @return array of correction params. Empty array if it's OK.

Rich
committed
public static function checkWebhook($webhook) {
$params = [];
if (empty($webhook['api_version']) || ($webhook['api_version'] !== CRM_Core_Payment_Stripe::API_VERSION)) {
$params['api_version'] = CRM_Core_Payment_Stripe::API_VERSION;
}

Rich
committed
if (array_diff(self::getDefaultEnabledEvents(), $webhook['enabled_events'])) {
$params['enabled_events'] = self::getDefaultEnabledEvents();
}

Rich
committed
return $params;
}
/**
* List of webhooks we currently handle
* @return array
*/
public static function getDefaultEnabledEvents() {
return [
'invoice.payment_succeeded',
'invoice.payment_failed',
'charge.failed',
'charge.refunded',
'charge.succeeded',
'customer.subscription.updated',
'customer.subscription.deleted',
];
}
}