CVE-2022-29254: Interpretation Conflict (CWE-436)
value: silverstripe-omnipay is a SilverStripe integration with Omnipay PHP payments library. For a subset of Omnipay gateways (those that use intermediary states like `isNotification()` or `isRedirect()`), if the payment identifier or success URL is exposed it is possible for payments to be prematurely marked as completed without payment being taken. This is mitigated by the fact that most payment gateways hide this information from users, however some issuing banks offer flawed 3DSecure implementations that may inadvertently expose this data. The following versions have been patched to fix this issue: `2.5.2`, `3.0.2`, `3.1.4`, and `3.2.1`. There are no known workarounds for this vulnerability.
CWE description: Product A handles inputs or steps differently than Product B, which causes A to perform incorrect actions based on its perception of B's state.
CVSS v3 base score: 6.5
Published: 2022-06-09T07:15Z
Repository: https://github.com/silverstripe/silverstripe-omnipay
Language: PHP
Files changed: src/Service/AuthorizeService.php, src/Service/CaptureService.php, src/Service/CreateCardService.php, src/Service/NotificationCompleteService.php, src/Service/PurchaseService.php, src/Service/RefundService.php, src/Service/ServiceResponse.php, src/Service/VoidService.php
Commit date: 2022-05-25 12:08:49 +0100
Commit message: [CVE-2022-29254] Add extra validation on payment completion

Diff: diff --git a/src/Service/AuthorizeService.php b/src/Service/AuthorizeService.php
index 0ec7d55..7819ae6 100644
--- a/src/Service/AuthorizeService.php
+++ b/src/Service/AuthorizeService.php
@@ -1,18 +1,18 @@
 <?php
 
 namespace SilverStripe\Omnipay\Service;
 
-use SilverStripe\Omnipay\Exception\InvalidStateException;
 use SilverStripe\Omnipay\Exception\InvalidConfigurationException;
+use SilverStripe\Omnipay\Exception\InvalidStateException;
 use SilverStripe\Omnipay\Helper\ErrorHandling;
-use SilverStripe\Omnipay\Model\Message\AuthorizeRequest;
 use SilverStripe\Omnipay\Model\Message\AuthorizedResponse;
-use SilverStripe\Omnipay\Model\Message\CompleteAuthorizeError;
 use SilverStripe\Omnipay\Model\Message\AuthorizeError;
 use SilverStripe\Omnipay\Model\Message\AuthorizeRedirectResponse;
+use SilverStripe\Omnipay\Model\Message\AuthorizeRequest;
 use SilverStripe\Omnipay\Model\Message\AwaitingAuthorizeResponse;
+use SilverStripe\Omnipay\Model\Message\CompleteAuthorizeError;
 use SilverStripe\Omnipay\Model\Message\CompleteAuthorizeRequest;
 
 class AuthorizeService extends PaymentService
 {
     /**
@@ -65,11 +65,11 @@ class AuthorizeService extends PaymentService
                 $serviceResponse->isRedirect() ? AuthorizeRedirectResponse::class : AwaitingAuthorizeResponse::class,
                 $response
             );
         } elseif ($serviceResponse->isError()) {
             $this->createMessage(AuthorizeError::class, $response);
-        } else {
+        } elseif ($serviceResponse->isSuccessful()) {
             $this->markCompleted('Authorized', $serviceResponse, $response);
         }
 
         return $serviceResponse;
     }
@@ -116,19 +116,16 @@ class AuthorizeService extends PaymentService
             return $this->generateServiceResponse($flags | ServiceResponse::SERVICE_ERROR);
         }
 
         $serviceResponse = $this->wrapOmnipayResponse($response, $isNotification);
 
-        if ($serviceResponse->isError()) {
+        if ($serviceResponse->isAwaitingNotification()) {
+            ErrorHandling::safeExtend($this->payment, 'onAwaitingAuthorized', $serviceResponse);
+        } elseif ($serviceResponse->isError()) {
             $this->createMessage(CompleteAuthorizeError::class, $response);
-            return $serviceResponse;
-        }
-
-        if (!$serviceResponse->isAwaitingNotification()) {
+        } elseif ($serviceResponse->isSuccessful()) {
             $this->markCompleted('Authorized', $serviceResponse, $response);
-        } else {
-            ErrorHandling::safeExtend($this->payment, 'onAwaitingAuthorized', $serviceResponse);
         }
 
         return $serviceResponse;
     }
 
diff --git a/src/Service/CaptureService.php b/src/Service/CaptureService.php
index 89d07a6..a2df097 100644
--- a/src/Service/CaptureService.php
+++ b/src/Service/CaptureService.php
@@ -136,29 +136,27 @@ class CaptureService extends NotificationCompleteService
 
         ErrorHandling::safeExtend($this, 'onAfterSendCapture', $request, $response);
 
         $serviceResponse = $this->wrapOmnipayResponse($response);
 
-        if ($serviceResponse->isAwaitingNotification()) {
+        if ($serviceResponse->isError()) {
+            $this->createMessage($this->errorMessageType, $response);
+        } elseif ($serviceResponse->isRedirect() || $serviceResponse->isAwaitingNotification()) {
             if ($diff < 0) {
                 $this->createPartialPayment(PaymentMath::multiply($amount, '-1'), $this->pendingState);
             } elseif ($diff > 0) {
                 $this->createPartialPayment($diff, $this->pendingState);
             }
             $this->payment->Status = $this->pendingState;
             $this->payment->write();
-        } else {
-            if ($serviceResponse->isError()) {
-                $this->createMessage($this->errorMessageType, $response);
-            } else {
-                if ($diff < 0) {
-                    $this->createPartialPayment(PaymentMath::multiply($amount, '-1'), $this->pendingState);
-                } elseif ($diff > 0) {
-                    $this->createPartialPayment($diff, $this->pendingState);
-                }
-                $this->markCompleted($this->endState, $serviceResponse, $response);
+        } elseif ($serviceResponse->isSuccessful()) {
+            if ($diff < 0) {
+                $this->createPartialPayment(PaymentMath::multiply($amount, '-1'), $this->pendingState);
+            } elseif ($diff > 0) {
+                $this->createPartialPayment($diff, $this->pendingState);
             }
+            $this->markCompleted($this->endState, $serviceResponse, $response);
         }
 
         return $serviceResponse;
     }
 
diff --git a/src/Service/CreateCardService.php b/src/Service/CreateCardService.php
index cceebdc..31e8d01 100644
--- a/src/Service/CreateCardService.php
+++ b/src/Service/CreateCardService.php
@@ -1,18 +1,23 @@
 <?php
 
 namespace SilverStripe\Omnipay\Service;
 
 use Omnipay\Common\Message\RequestInterface;
-use SilverStripe\Omnipay\Exception\InvalidStateException;
 use SilverStripe\Omnipay\Exception\InvalidConfigurationException;
+use SilverStripe\Omnipay\Exception\InvalidStateException;
 use SilverStripe\Omnipay\Helper\ErrorHandling;
-use SilverStripe\Omnipay\Model\Message;
+use SilverStripe\Omnipay\Model\Message\AwaitingCreateCardResponse;
+use SilverStripe\Omnipay\Model\Message\CompleteCreateCardError;
+use SilverStripe\Omnipay\Model\Message\CompleteCreateCardRequest;
+use SilverStripe\Omnipay\Model\Message\CreateCardError;
+use SilverStripe\Omnipay\Model\Message\CreateCardRedirectResponse;
+use SilverStripe\Omnipay\Model\Message\CreateCardRequest;
+use SilverStripe\Omnipay\Model\Message\CreateCardResponse;
 
 class CreateCardService extends PaymentService
 {
-
     /**
      * Start a createcard request
      *
      * @inheritdoc
      */
@@ -37,16 +42,16 @@ class CreateCardService extends PaymentService
 
         $this->extend('onBeforeCreateCard', $gatewayData);
         $request = $this->oGateway()->createCard($gatewayData);
         $this->extend('onAfterCreateCard', $request);
 
-        $this->createMessage(Message\CreateCardRequest::class, $request);
+        $this->createMessage(CreateCardRequest::class, $request);
 
         try {
             $response = $this->response = $request->send();
         } catch (\Omnipay\Common\Exception\OmnipayException $e) {
-            $this->createMessage(Message\CreateCardError::class, $e);
+            $this->createMessage(CreateCardError::class, $e);
             // create an error response
             return $this->generateServiceResponse(ServiceResponse::SERVICE_ERROR);
         }
 
         ErrorHandling::safeExtend($this, 'onAfterSendCreateCard', $request, $response);
@@ -56,16 +61,16 @@ class CreateCardService extends PaymentService
         if ($serviceResponse->isRedirect() || $serviceResponse->isAwaitingNotification()) {
             $this->payment->Status = 'PendingCreateCard';
             $this->payment->write();
 
             $this->createMessage(
-                $serviceResponse->isRedirect() ? Message\CreateCardRedirectResponse::class : Message\AwaitingCreateCardResponse::class,
+                $serviceResponse->isRedirect() ? CreateCardRedirectResponse::class : AwaitingCreateCardResponse::class,
                 $response
             );
         } elseif ($serviceResponse->isError()) {
-            $this->createMessage(Message\CreateCardError::class, $response);
-        } else {
+            $this->createMessage(CreateCardError::class, $response);
+        } elseif ($serviceResponse->isSuccessful()) {
             $this->markCompleted('CardCreated', $serviceResponse, $response);
         }
 
         return $serviceResponse;
     }
@@ -101,37 +106,34 @@ class CreateCardService extends PaymentService
         $this->extend('onBeforeCompleteCreateCard', $gatewayData);
         /** @var RequestInterface $request */
         $request = $gateway->completeCreateCard($gatewayData);
         $this->extend('onAfterCompleteCreateCard', $request);
 
-        $this->createMessage(Message\CompleteCreateCardRequest::class, $request);
+        $this->createMessage(CompleteCreateCardRequest::class, $request);
         $response = null;
         try {
             $response       = $this->response = $request->send();
         } catch (\Omnipay\Common\Exception\OmnipayException $e) {
-            $this->createMessage(Message\CompleteCreateCardError::class, $e);
+            $this->createMessage(CompleteCreateCardError::class, $e);
             return $this->generateServiceResponse($flags | ServiceResponse::SERVICE_ERROR);
         }
 
         $serviceResponse = $this->wrapOmnipayResponse($response, $isNotification);
 
-        if ($serviceResponse->isError()) {
-            $this->createMessage(Message\CompleteCreateCardError::class, $response);
-            return $serviceResponse;
-        }
-
-        if (!$serviceResponse->isAwaitingNotification()) {
-            $this->markCompleted('CardCreated', $serviceResponse, $response);
-        } else {
+        if ($serviceResponse->isAwaitingNotification()) {
             ErrorHandling::safeExtend($this->payment, 'onAwaitingCreateCard', $serviceResponse);
+        } elseif ($serviceResponse->isError()) {
+            $this->createMessage(CompleteCreateCardError::class, $response);
+        } elseif ($serviceResponse->isSuccessful()) {
+            $this->markCompleted('CardCreated', $serviceResponse, $response);
         }
 
         return $serviceResponse;
     }
 
     protected function markCompleted($endStatus, ServiceResponse $serviceResponse, $gatewayMessage)
     {
         parent::markCompleted($endStatus, $serviceResponse, $gatewayMessage);
-        $this->createMessage(Message\CreateCardResponse::class, $gatewayMessage);
+        $this->createMessage(CreateCardResponse::class, $gatewayMessage);
         ErrorHandling::safeExtend($this->payment, 'onCardCreated', $serviceResponse);
     }
 }
diff --git a/src/Service/NotificationCompleteService.php b/src/Service/NotificationCompleteService.php
index 7ab5d1d..e18b561 100644
--- a/src/Service/NotificationCompleteService.php
+++ b/src/Service/NotificationCompleteService.php
@@ -77,11 +77,14 @@ abstract class NotificationCompleteService extends PaymentService
             $serviceResponse->addFlag(ServiceResponse::SERVICE_ERROR);
             $this->createMessage($this->errorMessageType, 'Transaction references do not match!');
         }
 
         // check if we're done
-        if (!$serviceResponse->isError() && !$serviceResponse->isAwaitingNotification()) {
+        if (!$serviceResponse->isError()
+            && !$serviceResponse->isAwaitingNotification()
+            && $serviceResponse->isSuccessful()
+        ) {
             $this->markCompleted($this->endState, $serviceResponse, $serviceResponse->getOmnipayResponse());
         }
 
         return $serviceResponse;
     }
diff --git a/src/Service/PurchaseService.php b/src/Service/PurchaseService.php
index 926d888..027fa02 100644
--- a/src/Service/PurchaseService.php
+++ b/src/Service/PurchaseService.php
@@ -3,11 +3,17 @@
 namespace SilverStripe\Omnipay\Service;
 
 use SilverStripe\Omnipay\Exception\InvalidStateException;
 use SilverStripe\Omnipay\Exception\InvalidConfigurationException;
 use SilverStripe\Omnipay\Helper\ErrorHandling;
-use SilverStripe\Omnipay\Model\Message;
+use SilverStripe\Omnipay\Model\Message\AwaitingPurchaseResponse;
+use SilverStripe\Omnipay\Model\Message\CompletePurchaseError;
+use SilverStripe\Omnipay\Model\Message\CompletePurchaseRequest;
+use SilverStripe\Omnipay\Model\Message\PurchasedResponse;
+use SilverStripe\Omnipay\Model\Message\PurchaseError;
+use SilverStripe\Omnipay\Model\Message\PurchaseRedirectResponse;
+use SilverStripe\Omnipay\Model\Message\PurchaseRequest;
 
 class PurchaseService extends PaymentService
 {
     /**
      * Attempt to make a payment.
@@ -41,16 +47,16 @@ class PurchaseService extends PaymentService
 
         $this->extend('onBeforePurchase', $gatewayData);
         $request = $this->oGateway()->purchase($gatewayData);
         $this->extend('onAfterPurchase', $request);
 
-        $this->createMessage(Message\PurchaseRequest::class, $request);
+        $this->createMessage(PurchaseRequest::class, $request);
 
         try {
             $response = $this->response = $request->send();
         } catch (\Omnipay\Common\Exception\OmnipayException $e) {
-            $this->createMessage(Message\PurchaseError::class, $e);
+            $this->createMessage(PurchaseError::class, $e);
             // create an error response
             return $this->generateServiceResponse(ServiceResponse::SERVICE_ERROR);
         }
 
         ErrorHandling::safeExtend($this, 'onAfterSendPurchase', $request, $response);
@@ -60,16 +66,16 @@ class PurchaseService extends PaymentService
         if ($serviceResponse->isRedirect() || $serviceResponse->isAwaitingNotification()) {
             $this->payment->Status = 'PendingPurchase';
             $this->payment->write();
 
             $this->createMessage(
-                $serviceResponse->isRedirect() ? Message\PurchaseRedirectResponse::class : Message\AwaitingPurchaseResponse::class,
+                $serviceResponse->isRedirect() ? PurchaseRedirectResponse::class : AwaitingPurchaseResponse::class,
                 $response
             );
         } elseif ($serviceResponse->isError()) {
-            $this->createMessage(Message\PurchaseError::class, $response);
-        } else {
+            $this->createMessage(PurchaseError::class, $response);
+        } elseif ($serviceResponse->isSuccessful()) {
             $this->markCompleted('Captured', $serviceResponse, $response);
         }
 
         return $serviceResponse;
     }
@@ -103,38 +109,34 @@ class PurchaseService extends PaymentService
 
         $this->extend('onBeforeCompletePurchase', $gatewayData);
         $request = $gateway->completePurchase($gatewayData);
         $this->extend('onAfterCompletePurchase', $request);
 
-        $this->createMessage(Message\CompletePurchaseRequest::class, $request);
+        $this->createMessage(CompletePurchaseRequest::class, $request);
         $response = null;
         try {
             $response = $this->response = $request->send();
         } catch (\Omnipay\Common\Exception\OmnipayException $e) {
-            $this->createMessage(Message\CompletePurchaseError::class, $e);
+            $this->createMessage(CompletePurchaseError::class, $e);
             return $this->generateServiceResponse($flags | ServiceResponse::SERVICE_ERROR);
         }
 
         $serviceResponse = $this->wrapOmnipayResponse($response, $isNotification);
-        if ($serviceResponse->isError()) {
-            $this->createMessage(Message\CompletePurchaseError::class, $response);
-            return $serviceResponse;
-        }
 
-        // only update payment status if we're not waiting for a notification
-        if (!$serviceResponse->isAwaitingNotification()) {
-            $this->markCompleted('Captured', $serviceResponse, $response);
-        } else {
+        if ($serviceResponse->isAwaitingNotification()) {
             ErrorHandling::safeExtend($this->payment, 'onAwaitingCaptured', $serviceResponse);
+        } elseif ($serviceResponse->isError()) {
+            $this->createMessage(CompletePurchaseError::class, $response);
+        } elseif ($serviceResponse->isSuccessful()) {
+            $this->markCompleted('Captured', $serviceResponse, $response);
         }
 
-
         return $serviceResponse;
     }
 
     protected function markCompleted($endStatus, ServiceResponse $serviceResponse, $gatewayMessage)
     {
         parent::markCompleted($endStatus, $serviceResponse, $gatewayMessage);
-        $this->createMessage(Message\PurchasedResponse::class, $gatewayMessage);
+        $this->createMessage(PurchasedResponse::class, $gatewayMessage);
         ErrorHandling::safeExtend($this->payment, 'onCaptured', $serviceResponse);
     }
 }
diff --git a/src/Service/RefundService.php b/src/Service/RefundService.php
index b04845d..20d8df7 100644
--- a/src/Service/RefundService.php
+++ b/src/Service/RefundService.php
@@ -7,24 +7,27 @@ use SilverStripe\Omnipay\Exception\InvalidConfigurationException;
 use SilverStripe\Omnipay\Exception\InvalidParameterException;
 use SilverStripe\Omnipay\Exception\MissingParameterException;
 use SilverStripe\Omnipay\GatewayInfo;
 use SilverStripe\Omnipay\Helper\ErrorHandling;
 use SilverStripe\Omnipay\Helper\PaymentMath;
-use SilverStripe\Omnipay\Model\Message;
+use SilverStripe\Omnipay\Model\Message\PartiallyRefundedResponse;
+use SilverStripe\Omnipay\Model\Message\RefundedResponse;
+use SilverStripe\Omnipay\Model\Message\RefundError;
+use SilverStripe\Omnipay\Model\Message\RefundRequest;
 use SilverStripe\Omnipay\Model\Payment;
 
 class RefundService extends NotificationCompleteService
 {
     protected $startState = 'Captured';
 
     protected $endState = 'Refunded';
 
     protected $pendingState = 'PendingRefund';
 
-    protected $requestMessageType = Message\RefundRequest::class;
+    protected $requestMessageType = RefundRequest::class;
 
-    protected $errorMessageType = Message\RefundError::class;
+    protected $errorMessageType = RefundError::class;
 
     /**
      * Return money to the previously charged credit card.
      *
      * If the transaction-reference of the payment to refund is known, pass it via $data as
@@ -125,25 +128,24 @@ class RefundService extends NotificationCompleteService
 
         ErrorHandling::safeExtend($this, 'onAfterSendRefund', $request, $response);
 
         $serviceResponse = $this->wrapOmnipayResponse($response);
 
-        if ($serviceResponse->isAwaitingNotification()) {
+        if ($serviceResponse->isError()) {
+            $this->createMessage($this->errorMessageType, $response);
+        } elseif ($serviceResponse->isRedirect() || $serviceResponse->isAwaitingNotification()) {
             if ($isPartial) {
                 $this->createPartialPayment(PaymentMath::multiply($amount, '-1'), $this->pendingState);
             }
             $this->payment->Status = $this->pendingState;
             $this->payment->write();
-        } else {
-            if ($serviceResponse->isError()) {
-                $this->createMessage($this->errorMessageType, $response);
-            } else {
-                if ($isPartial) {
-                    $this->createPartialPayment(PaymentMath::multiply($amount, '-1'), $this->pendingState);
-                }
-                $this->markCompleted($this->endState, $serviceResponse, $response);
+        } elseif ($serviceResponse->isSuccessful()) {
+            if ($isPartial) {
+                $this->createPartialPayment(PaymentMath::multiply($amount, '-1'), $this->pendingState);
             }
+
+            $this->markCompleted($this->endState, $serviceResponse, $response);
         }
 
         return $serviceResponse;
     }
 
@@ -180,13 +182,13 @@ class RefundService extends NotificationCompleteService
             }
         }
 
         parent::markCompleted($endStatus, $serviceResponse, $gatewayMessage);
         if ($endStatus === 'Captured') {
-            $this->createMessage(Message\PartiallyRefundedResponse::class, $gatewayMessage);
+            $this->createMessage(PartiallyRefundedResponse::class, $gatewayMessage);
         } else {
-            $this->createMessage(Message\RefundedResponse::class, $gatewayMessage);
+            $this->createMessage(RefundedResponse::class, $gatewayMessage);
         }
 
         ErrorHandling::safeExtend($this->payment, 'onRefunded', $serviceResponse);
     }
 }
diff --git a/src/Service/ServiceResponse.php b/src/Service/ServiceResponse.php
index e693907..306b71b 100644
--- a/src/Service/ServiceResponse.php
+++ b/src/Service/ServiceResponse.php
@@ -95,10 +95,26 @@ class ServiceResponse
     public function getPayment()
     {
         return $this->payment;
     }
 
+    /**
+     * Whether the response is marked as successful by Omnipay.
+     *
+     * @return bool
+     */
+    public function isSuccessful()
+    {
+        if ($this->omnipayResponse instanceof NotificationInterface) {
+            return $this->omnipayResponse->getTransactionStatus() === NotificationInterface::STATUS_COMPLETED;
+        } elseif ($this->omnipayResponse instanceof AbstractResponse) {
+            return $this->omnipayResponse->isSuccessful();
+        }
+
+        return false;
+    }
+
     /**
      * Whether or not this is an *offsite* redirect.
      * This is only the case when there's an Omnipay response present that *is* a redirect.
      *
      * @return bool
diff --git a/src/Service/VoidService.php b/src/Service/VoidService.php
index c4d5efd..5862258 100644
--- a/src/Service/VoidService.php
+++ b/src/Service/VoidService.php
@@ -1,27 +1,29 @@
 <?php
 
 namespace SilverStripe\Omnipay\Service;
 
+use Omnipay\Common\Exception\OmnipayException;
 use SilverStripe\Omnipay\Exception\InvalidConfigurationException;
 use SilverStripe\Omnipay\Exception\MissingParameterException;
-use Omnipay\Common\Exception\OmnipayException;
 use SilverStripe\Omnipay\GatewayInfo;
 use SilverStripe\Omnipay\Helper\ErrorHandling;
-use SilverStripe\Omnipay\Model\Message;
+use SilverStripe\Omnipay\Model\Message\VoidedResponse;
+use SilverStripe\Omnipay\Model\Message\VoidError;
+use SilverStripe\Omnipay\Model\Message\VoidRequest;
 
 class VoidService extends NotificationCompleteService
 {
     protected $startState = 'Authorized';
 
     protected $endState = 'Void';
 
     protected $pendingState = 'PendingVoid';
 
-    protected $requestMessageType = Message\VoidRequest::class;
+    protected $requestMessageType = VoidRequest::class;
 
-    protected $errorMessageType = Message\VoidError::class;
+    protected $errorMessageType = VoidError::class;
 
     /**
      * Void/cancel a payment
      *
      * If the transaction-reference of the payment to capture is known, pass it via $data as
@@ -95,27 +97,25 @@ class VoidService extends NotificationCompleteService
 
         ErrorHandling::safeExtend($this, 'onAfterSendVoid', $request, $response);
 
         $serviceResponse = $this->wrapOmnipayResponse($response);
 
-        if ($serviceResponse->isAwaitingNotification()) {
+        if ($serviceResponse->isError()) {
+            $this->createMessage($this->errorMessageType, $response);
+        } elseif ($serviceResponse->isRedirect() || $serviceResponse->isAwaitingNotification()) {
             $this->payment->Status = $this->pendingState;
             $this->payment->write();
-        } else {
-            if ($serviceResponse->isError()) {
-                $this->createMessage($this->errorMessageType, $response);
-            } else {
-                $this->markCompleted($this->endState, $serviceResponse, $response);
-            }
+        } elseif ($serviceResponse->isSuccessful()) {
+            $this->markCompleted($this->endState, $serviceResponse, $response);
         }
 
         return $serviceResponse;
     }
 
     protected function markCompleted($endStatus, ServiceResponse $serviceResponse, $gatewayMessage)
     {
         parent::markCompleted($endStatus, $serviceResponse, $gatewayMessage);
-        $this->createMessage(Message\VoidedResponse::class, $gatewayMessage);
+        $this->createMessage(VoidedResponse::class, $gatewayMessage);
 
         ErrorHandling::safeExtend($this->payment, 'onVoid', $serviceResponse);
     }
 }

Vulnerable code: use SilverStripe\Omnipay\Exception\InvalidStateException;
use SilverStripe\Omnipay\Model\Message\AuthorizeRequest;
use SilverStripe\Omnipay\Model\Message\CompleteAuthorizeError;
        } else {
        if ($serviceResponse->isError()) {
            return $serviceResponse;
        }

        if (!$serviceResponse->isAwaitingNotification()) {
        } else {
            ErrorHandling::safeExtend($this->payment, 'onAwaitingAuthorized', $serviceResponse);
        if ($serviceResponse->isAwaitingNotification()) {
        } else {
            if ($serviceResponse->isError()) {
                $this->createMessage($this->errorMessageType, $response);
            } else {
                if ($diff < 0) {
                    $this->createPartialPayment(PaymentMath::multiply($amount, '-1'), $this->pendingState);
                } elseif ($diff > 0) {
                    $this->createPartialPayment($diff, $this->pendingState);
                }
                $this->markCompleted($this->endState, $serviceResponse, $response);
use SilverStripe\Omnipay\Exception\InvalidStateException;
use SilverStripe\Omnipay\Model\Message;

        $this->createMessage(Message\CreateCardRequest::class, $request);
            $this->createMessage(Message\CreateCardError::class, $e);
                $serviceResponse->isRedirect() ? Message\CreateCardRedirectResponse::class : Message\AwaitingCreateCardResponse::class,
            $this->createMessage(Message\CreateCardError::class, $response);
        } else {
        $this->createMessage(Message\CompleteCreateCardRequest::class, $request);
            $this->createMessage(Message\CompleteCreateCardError::class, $e);
        if ($serviceResponse->isError()) {
            $this->createMessage(Message\CompleteCreateCardError::class, $response);
            return $serviceResponse;
        }

        if (!$serviceResponse->isAwaitingNotification()) {
            $this->markCompleted('CardCreated', $serviceResponse, $response);
        } else {
        $this->createMessage(Message\CreateCardResponse::class, $gatewayMessage);
        if (!$serviceResponse->isError() && !$serviceResponse->isAwaitingNotification()) {
use SilverStripe\Omnipay\Model\Message;
        $this->createMessage(Message\PurchaseRequest::class, $request);
            $this->createMessage(Message\PurchaseError::class, $e);
                $serviceResponse->isRedirect() ? Message\PurchaseRedirectResponse::class : Message\AwaitingPurchaseResponse::class,
            $this->createMessage(Message\PurchaseError::class, $response);
        } else {
        $this->createMessage(Message\CompletePurchaseRequest::class, $request);
            $this->createMessage(Message\CompletePurchaseError::class, $e);
        if ($serviceResponse->isError()) {
            $this->createMessage(Message\CompletePurchaseError::class, $response);
            return $serviceResponse;
        }
        // only update payment status if we're not waiting for a notification
        if (!$serviceResponse->isAwaitingNotification()) {
            $this->markCompleted('Captured', $serviceResponse, $response);
        } else {

        $this->createMessage(Message\PurchasedResponse::class, $gatewayMessage);
use SilverStripe\Omnipay\Model\Message;
    protected $requestMessageType = Message\RefundRequest::class;
    protected $errorMessageType = Message\RefundError::class;
        if ($serviceResponse->isAwaitingNotification()) {
        } else {
            if ($serviceResponse->isError()) {
                $this->createMessage($this->errorMessageType, $response);
            } else {
                if ($isPartial) {
                    $this->createPartialPayment(PaymentMath::multiply($amount, '-1'), $this->pendingState);
                }
                $this->markCompleted($this->endState, $serviceResponse, $response);
            $this->createMessage(Message\PartiallyRefundedResponse::class, $gatewayMessage);
            $this->createMessage(Message\RefundedResponse::class, $gatewayMessage);
use Omnipay\Common\Exception\OmnipayException;
use SilverStripe\Omnipay\Model\Message;
    protected $requestMessageType = Message\VoidRequest::class;
    protected $errorMessageType = Message\VoidError::class;
        if ($serviceResponse->isAwaitingNotification()) {
        } else {
            if ($serviceResponse->isError()) {
                $this->createMessage($this->errorMessageType, $response);
            } else {
                $this->markCompleted($this->endState, $serviceResponse, $response);
            }
        $this->createMessage(Message\VoidedResponse::class, $gatewayMessage);
Fixed code: use SilverStripe\Omnipay\Exception\InvalidStateException;
use SilverStripe\Omnipay\Model\Message\AuthorizeRequest;
use SilverStripe\Omnipay\Model\Message\CompleteAuthorizeError;
        } elseif ($serviceResponse->isSuccessful()) {
        if ($serviceResponse->isAwaitingNotification()) {
            ErrorHandling::safeExtend($this->payment, 'onAwaitingAuthorized', $serviceResponse);
        } elseif ($serviceResponse->isError()) {
        } elseif ($serviceResponse->isSuccessful()) {
        if ($serviceResponse->isError()) {
            $this->createMessage($this->errorMessageType, $response);
        } elseif ($serviceResponse->isRedirect() || $serviceResponse->isAwaitingNotification()) {
        } elseif ($serviceResponse->isSuccessful()) {
            if ($diff < 0) {
                $this->createPartialPayment(PaymentMath::multiply($amount, '-1'), $this->pendingState);
            } elseif ($diff > 0) {
                $this->createPartialPayment($diff, $this->pendingState);
            $this->markCompleted($this->endState, $serviceResponse, $response);
use SilverStripe\Omnipay\Exception\InvalidStateException;
use SilverStripe\Omnipay\Model\Message\AwaitingCreateCardResponse;
use SilverStripe\Omnipay\Model\Message\CompleteCreateCardError;
use SilverStripe\Omnipay\Model\Message\CompleteCreateCardRequest;
use SilverStripe\Omnipay\Model\Message\CreateCardError;
use SilverStripe\Omnipay\Model\Message\CreateCardRedirectResponse;
use SilverStripe\Omnipay\Model\Message\CreateCardRequest;
use SilverStripe\Omnipay\Model\Message\CreateCardResponse;
        $this->createMessage(CreateCardRequest::class, $request);
            $this->createMessage(CreateCardError::class, $e);
                $serviceResponse->isRedirect() ? CreateCardRedirectResponse::class : AwaitingCreateCardResponse::class,
            $this->createMessage(CreateCardError::class, $response);
        } elseif ($serviceResponse->isSuccessful()) {
        $this->createMessage(CompleteCreateCardRequest::class, $request);
            $this->createMessage(CompleteCreateCardError::class, $e);
        if ($serviceResponse->isAwaitingNotification()) {
        } elseif ($serviceResponse->isError()) {
            $this->createMessage(CompleteCreateCardError::class, $response);
        } elseif ($serviceResponse->isSuccessful()) {
            $this->markCompleted('CardCreated', $serviceResponse, $response);
        $this->createMessage(CreateCardResponse::class, $gatewayMessage);
        if (!$serviceResponse->isError()
            && !$serviceResponse->isAwaitingNotification()
            && $serviceResponse->isSuccessful()
        ) {
use SilverStripe\Omnipay\Model\Message\AwaitingPurchaseResponse;
use SilverStripe\Omnipay\Model\Message\CompletePurchaseError;
use SilverStripe\Omnipay\Model\Message\CompletePurchaseRequest;
use SilverStripe\Omnipay\Model\Message\PurchasedResponse;
use SilverStripe\Omnipay\Model\Message\PurchaseError;
use SilverStripe\Omnipay\Model\Message\PurchaseRedirectResponse;
use SilverStripe\Omnipay\Model\Message\PurchaseRequest;
        $this->createMessage(PurchaseRequest::class, $request);
            $this->createMessage(PurchaseError::class, $e);
                $serviceResponse->isRedirect() ? PurchaseRedirectResponse::class : AwaitingPurchaseResponse::class,
            $this->createMessage(PurchaseError::class, $response);
        } elseif ($serviceResponse->isSuccessful()) {
        $this->createMessage(CompletePurchaseRequest::class, $request);
            $this->createMessage(CompletePurchaseError::class, $e);
        if ($serviceResponse->isAwaitingNotification()) {
        } elseif ($serviceResponse->isError()) {
            $this->createMessage(CompletePurchaseError::class, $response);
        } elseif ($serviceResponse->isSuccessful()) {
            $this->markCompleted('Captured', $serviceResponse, $response);
        $this->createMessage(PurchasedResponse::class, $gatewayMessage);
use SilverStripe\Omnipay\Model\Message\PartiallyRefundedResponse;
use SilverStripe\Omnipay\Model\Message\RefundedResponse;
use SilverStripe\Omnipay\Model\Message\RefundError;
use SilverStripe\Omnipay\Model\Message\RefundRequest;
    protected $requestMessageType = RefundRequest::class;
    protected $errorMessageType = RefundError::class;
        if ($serviceResponse->isError()) {
            $this->createMessage($this->errorMessageType, $response);
        } elseif ($serviceResponse->isRedirect() || $serviceResponse->isAwaitingNotification()) {
        } elseif ($serviceResponse->isSuccessful()) {
            if ($isPartial) {
                $this->createPartialPayment(PaymentMath::multiply($amount, '-1'), $this->pendingState);

            $this->markCompleted($this->endState, $serviceResponse, $response);
            $this->createMessage(PartiallyRefundedResponse::class, $gatewayMessage);
            $this->createMessage(RefundedResponse::class, $gatewayMessage);
    /**
     * Whether the response is marked as successful by Omnipay.
     *
     * @return bool
     */
    public function isSuccessful()
    {
        if ($this->omnipayResponse instanceof NotificationInterface) {
            return $this->omnipayResponse->getTransactionStatus() === NotificationInterface::STATUS_COMPLETED;
        } elseif ($this->omnipayResponse instanceof AbstractResponse) {
            return $this->omnipayResponse->isSuccessful();
        }

        return false;
    }

use Omnipay\Common\Exception\OmnipayException;
use SilverStripe\Omnipay\Model\Message\VoidedResponse;
use SilverStripe\Omnipay\Model\Message\VoidError;
use SilverStripe\Omnipay\Model\Message\VoidRequest;
    protected $requestMessageType = VoidRequest::class;
    protected $errorMessageType = VoidError::class;
        if ($serviceResponse->isError()) {
            $this->createMessage($this->errorMessageType, $response);
        } elseif ($serviceResponse->isRedirect() || $serviceResponse->isAwaitingNotification()) {
        } elseif ($serviceResponse->isSuccessful()) {
            $this->markCompleted($this->endState, $serviceResponse, $response);
        $this->createMessage(VoidedResponse::class, $gatewayMessage);