public function buildSignature()
    {
        // Re-create the VPSSignature.
        // Note that an ABORT notification for a TOKEN request type flips back to
        // the transaction format. We'll try merging these.

        $VPSTxId = $this->getVPSTxId();

        if ($this->getTxType() === Response::TXTYPE_TOKEN
            && $this->getStatus() === Response::SAGEPAY_STATUS_OK
        ) {
            // For some bizarre reason, the VPSTxId is hashed at the Sage Pay gateway
            // without its curly brackets, so we must do the same to validate the hash.
            // This only happens for a valid TOKEN request, and not for an aborted
            // TOKEN request.
            // The successful TOKEN request also does not include the card details, even
            // though they are present. The ABORTed token request does include the address
            // result details in the signature, even though they are no relevant.

            $VPSTxId = str_replace(['{', '}'], '', $VPSTxId);
        }

        // Transaction types PAYMENT, DEFERRED and AUTHENTICATE (when suppoted)
        // and non-transaction TOKEN request.

        $signatureData = array(
            $VPSTxId,
            // VendorTxCode
            $this->getTransactionId(),
            $this->getStatus(),
            $this->getTxAuthNo(),
            strtolower($this->getVendor()),
            $this->getAVSCV2(),
            ($this->getTxType() === Response::TXTYPE_TOKEN ? $this->getToken() : ''),
            // As saved in the merchant application.
            $this->getSecurityKey(),
        );

        if ($this->getTxType() !== Response::TXTYPE_TOKEN
            || $this->getStatus() !== Response::SAGEPAY_STATUS_OK
        ) {
            // Do not use any of these fields for a successful TOKEN transaction,
            // even though some of them may be present.

            $signatureData = array_merge(
                $signatureData,
                array(
                    // Details for AVSCV2:
                    $this->getAddressResult(),
                    $this->getPostCodeResult(),
                    $this->getCV2Result(),
                    //
                    $this->getGiftAid(),
                    $this->get3DSecureStatus(),
                    $this->getCAVV(),
                    $this->getAddressStatus(),
                    $this->getPayerStatus(),
                    $this->getCardType(),
                    $this->getLast4Digits(),
                    // New for protocol v3.00
                    $this->getDeclineCode(),
                    $this->getExpiryDate(),
                    $this->getFraudResponse(),
                    $this->getBankAuthCode(),
                )
            );
        }

        return md5(implode('', $signatureData));
    }