Commit 7183e228 authored by mattwire's avatar mattwire
Browse files

Implement new processWebhookEvent for processing events from the webhook queue...

Implement new processWebhookEvent for processing events from the webhook queue and store error message in 'message' field
parent bf46addb
......@@ -1161,7 +1161,7 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
http_response_code(200);
$rawData = file_get_contents("php://input");
$event = json_decode($rawData);
$ipnClass = new CRM_Core_Payment_StripeIPN();
$ipnClass = new CRM_Core_Payment_StripeIPN($this);
$ipnClass->setEventID($event->id);
if (!$ipnClass->setEventType($event->type)) {
// We don't handle this event
......@@ -1207,7 +1207,36 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
if (isset($emailReceipt)) {
$ipnClass->setSendEmailReceipt($emailReceipt);
}
return $ipnClass->processWebhook();
return $ipnClass->processWebhookEvent()->ok;
}
/**
* Called by mjwshared extension's queue processor api3 Job.process_paymentprocessor_webhooks
*
* The array parameter contains a row of PaymentprocessorWebhook data, which represents a single GC event
*
* Return TRUE for success, FALSE if there's a problem
*/
public function processWebhookEvent(array $webhookEvent) :bool {
// If there is another copy of this event in the table with a lower ID, then
// this is a duplicate that should be ignored. We do not worry if there is one with a higher ID
// because that means that while there are duplicates, we'll only process the one with the lowest ID.
$duplicates = PaymentprocessorWebhook::get(FALSE)
->selectRowCount()
->addWhere('event_id', '=', $webhookEvent['event_id'])
->addWhere('id', '<', $webhookEvent['id'])
->execute()->count();
if ($duplicates) {
PaymentprocessorWebhook::update(FALSE)
->addWhere('id', '=', $webhookEvent['id'])
->addValue('status', 'error')
->addValue('message', 'Refusing to process this event as it is a duplicate.')
->execute();
return FALSE;
}
$handler = new CRM_Core_Payment_StripeIPN($this);
return $handler->processQueuedWebhookEvent($webhookEvent);
}
/**
......
......@@ -75,6 +75,14 @@ class CRM_Core_Payment_StripeIPN {
*/
public $exceptionOnFailure = FALSE;
public function __construct(?CRM_Core_Payment_Stripe $paymentObject = NULL) {
if ($paymentObject !== NULL && !($paymentObject instanceof CRM_Core_Payment_Stripe)) {
// This would be a coding error.
throw new Exception(__CLASS__ . " constructor requires CRM_Core_Payment_Stripe object (or NULL for legacy use).");
}
$this->_paymentProcessor = $paymentObject;
}
/**
* Returns TRUE if we handle this event type, FALSE otherwise
* @param string $eventType
......@@ -268,7 +276,31 @@ class CRM_Core_Payment_StripeIPN {
return TRUE;
}
return $this->processWebhook();
return $this->processWebhookEvent()->ok;
}
/**
* Process a single queued event and update it.
*
* @param array $webhookEvent
*
* @return bool TRUE on success.
* @throws \API_Exception
* @throws \Civi\API\Exception\UnauthorizedException
*/
public function processQueuedWebhookEvent(array $webhookEvent) :bool {
$this->setEventID($webhookEvent['event_id']);
if (!$this->setEventType($webhookEvent['trigger'])) {
// We don't handle this event
return FALSE;
};
// @todo consider storing webhook data when received.
$this->setVerifyData(TRUE);
$this->setExceptionMode(FALSE);
if (isset($emailReceipt)) {
$this->setSendEmailReceipt($emailReceipt);
}
return $this->processWebhookEvent()->ok;
}
/**
......@@ -278,21 +310,24 @@ class CRM_Core_Payment_StripeIPN {
* @throws \API_Exception
* @throws \Civi\API\Exception\UnauthorizedException
*/
public function processWebhook() {
public function processWebhookEvent() :StdClass {
$this->setInputParameters();
$return = (object) ['message' => NULL, 'ok' => FALSE, 'exception' => NULL];
try {
$success = $this->processEventType();
$return->ok = $this->processEventType();
}
catch (Exception $e) {
if ($this->exceptionOnFailure) {
// Re-throw a modified exception. (Special case for phpunit testing).
$message = get_class($e) . ": " . $e->getMessage();
throw new RuntimeException($message, $e->getCode(), $e);
$return->message = get_class($e) . ": " . $e->getMessage();
throw new RuntimeException($return->message, $e->getCode(), $e);
}
else {
// Normal use.
$success = FALSE;
\Civi::log()->error("StripeIPN: processEventType failed. EventID: {$this->eventID} : " . $e->getMessage() . "\n" . $e->getTraceAsString());
$return->ok = FALSE;
$return->message = $e->getMessage(). "\n" . $e->getTraceAsString();
$return->exception = $e;
\Civi::log()->error("StripeIPN: processEventType failed. EventID: {$this->eventID} : " . $return->message);
}
}
......@@ -305,11 +340,12 @@ class CRM_Core_Payment_StripeIPN {
PaymentprocessorWebhook::update(FALSE)
->addWhere('identifier', '=', $uniqueIdentifier)
->addWhere('trigger', '=', $this->eventType)
->addValue('status', $success ? 'success' : 'error')
->addValue('status', $return->ok ? 'success' : 'error')
->addValue('message', preg_replace('/^(.{250}).*/su', '$1 ...', $return->message))
->addValue('processed_date', 'now')
->execute();
return $success;
return $return;
}
/**
......
......@@ -45,7 +45,7 @@ function civicrm_api3_stripe_invoice_process($params) {
$ipnClass->setSendEmailReceipt($params['is_email_receipt']);
}
if ($ipnClass->processWebhook()) {
if ($ipnClass->processWebhookEvent()->ok) {
return civicrm_api3_create_success([TRUE], $params);
}
else {
......
......@@ -650,7 +650,7 @@ class CRM_Stripe_IpnTest extends CRM_Stripe_BaseTest {
// $ipnClass->setSendEmailReceipt($emailReceipt);
// }
return $ipnClass->processWebhook();
return $ipnClass->processWebhookEvent()->ok;
}
/**
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment