<?php

namespace Test\Unit;

require_once dirname(__DIR__) . '/Setup.php';

use DateTime;
use Test\Setup;
use Test\Helper;
use Braintree;

class WebhookNotificationTest extends Setup
{
    public function setUp(): void
    {
        self::integrationMerchantConfig();
    }

    public function testVerify()
    {
        $verificationString = Braintree\WebhookNotification::verify('20f9f8ed05f77439fe955c977e4c8a53');
        $this->assertEquals('integration_public_key|d9b899556c966b3f06945ec21311865d35df3ce4', $verificationString);
    }

    public function testVerifyRaisesErrorWithInvalidChallenge()
    {
        $this->expectException('Braintree\Exception\InvalidChallenge', 'challenge contains non-hex characters');

        Braintree\WebhookNotification::verify('bad challenge');
    }

    public function testVerifyRaisesErrorWhenEnvironmentNotSet()
    {
        Braintree\Configuration::reset();

        $this->expectException('Braintree\Exception\Configuration', 'Braintree\Configuration::merchantId needs to be set (or accessToken needs to be passed to Braintree\Gateway)');

        Braintree\WebhookNotification::verify('20f9f8ed05f77439fe955c977e4c8a53');
    }

    public function testSampleNotificationReturnsAParsableNotification()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::SUBSCRIPTION_WENT_PAST_DUE,
            'my_id'
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::SUBSCRIPTION_WENT_PAST_DUE, $webhookNotification->kind);
        $this->assertNotNull($webhookNotification->timestamp);
        $this->assertEquals("my_id", $webhookNotification->subscription->id);
        $this->assertNull($webhookNotification->sourceMerchantId);
    }

    public function testSampleNotificationContainsSourceMerchantIdIfSpecified()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::SUBSCRIPTION_WENT_PAST_DUE,
            'my_id',
            'my_source_merchant_id'
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals($webhookNotification->sourceMerchantId, 'my_source_merchant_id');
    }

    public function testParsingModifiedSignatureRaisesError()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::SUBSCRIPTION_WENT_PAST_DUE,
            'my_id'
        );

        $this->expectException('Braintree\Exception\InvalidSignature', 'signature does not match payload - one has been modified');

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'] . "bad",
            $sampleNotification['bt_payload']
        );
    }

    public function testParsingWithNoKeysRaisesError()
    {
        Braintree\Configuration::reset();

        $this->expectException('Braintree\Exception\Configuration', 'Braintree\Configuration::merchantId needs to be set (or accessToken needs to be passed to Braintree\Gateway)');

        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::SUBSCRIPTION_WENT_PAST_DUE,
            'my_id'
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );
    }

    public function testParsingWebhookWithWrongKeysRaisesError()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::SUBSCRIPTION_WENT_PAST_DUE,
            'my_id'
        );

        Braintree\Configuration::environment('development');
        Braintree\Configuration::merchantId('integration_merchant_id');
        Braintree\Configuration::publicKey('wrong_public_key');
        Braintree\Configuration::privateKey('wrong_private_key');

        $this->expectException('Braintree\Exception\InvalidSignature', 'no matching public key');

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            "bad" . $sampleNotification['bt_payload']
        );
    }

    public function testParsingModifiedPayloadRaisesError()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::SUBSCRIPTION_WENT_PAST_DUE,
            'my_id'
        );

        $this->expectException('Braintree\Exception\InvalidSignature');

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            "bad" . $sampleNotification['bt_payload']
        );
    }

    public function testParsingUnknownPublicKeyRaisesError()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::SUBSCRIPTION_WENT_PAST_DUE,
            'my_id'
        );

        $this->expectException('Braintree\Exception\InvalidSignature');

        $webhookNotification = Braintree\WebhookNotification::parse(
            "bad" . $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );
    }

    public function testParsingInvalidSignatureRaisesError()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::SUBSCRIPTION_WENT_PAST_DUE,
            'my_id'
        );

        $this->expectException('Braintree\Exception\InvalidSignature');

        $webhookNotification = Braintree\WebhookNotification::parse(
            "bad_signature",
            $sampleNotification['bt_payload']
        );
    }

    public function testParsingNullSignatureRaisesError()
    {
        $this->expectException('Braintree\Exception\InvalidSignature', 'signature cannot be null');

        $webhookNotification = Braintree\WebhookNotification::parse(null, "payload");
    }

    public function testParsingNullPayloadRaisesError()
    {
        $this->expectException('Braintree\Exception\InvalidSignature', 'payload cannot be null');

        $webhookNotification = Braintree\WebhookNotification::parse("signature", null);
    }

    public function testParsingInvalidCharactersRaisesError()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::SUBSCRIPTION_WENT_PAST_DUE,
            'my_id'
        );

        $this->expectException('Braintree\Exception\InvalidSignature', 'payload contains illegal characters');

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            "~*~*invalid*~*~"
        );
    }

    public function testParsingAllowsAllValidCharacters()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::SUBSCRIPTION_WENT_PAST_DUE,
            'my_id'
        );

        $this->expectException('Braintree\Exception\InvalidSignature', 'signature does not match payload - one has been modified');

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+=/\n"
        );
    }

    public function testParsingRetriesPayloadWithANewline()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::SUBSCRIPTION_WENT_PAST_DUE,
            'my_id'
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            rtrim($sampleNotification['bt_payload'])
        );

        $this->assertInstanceOf('Braintree\WebhookNotification', $webhookNotification);
    }

    public function testAllowsParsingUsingGateway()
    {
        $gateway = new Braintree\Gateway([
            'privateKey' => 'integration_private_key',
            'publicKey' => 'integration_public_key',
            'merchantId' => 'integration_merchant_id',
            'environment' => 'development'
        ]);

        $sampleNotification = $gateway->webhookTesting()->sampleNotification(
            Braintree\WebhookNotification::CHECK,
            "my_id"
        );

        $webhookNotification = $gateway->webhookNotification()->parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::CHECK, $webhookNotification->kind);
    }

    public function testAllowsParsingUsingStaticMethods()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::CHECK,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::CHECK, $webhookNotification->kind);
    }

    public function testBuildsASampleNotificationForASubscriptionBillingSkippedWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::SUBSCRIPTION_BILLING_SKIPPED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::SUBSCRIPTION_BILLING_SKIPPED, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->subscription->id);
        $this->assertEquals([], $webhookNotification->subscription->transactions);
        $this->assertEquals([], $webhookNotification->subscription->discounts);
        $this->assertEquals([], $webhookNotification->subscription->addOns);
        $this->assertEquals("Active", $webhookNotification->subscription->status);
    }

    public function testBuildsASampleNotificationForASubscriptionChargedSuccessfullyWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::SUBSCRIPTION_CHARGED_SUCCESSFULLY,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::SUBSCRIPTION_CHARGED_SUCCESSFULLY, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->subscription->id);
        $this->assertEquals("Active", $webhookNotification->subscription->status);
        $this->assertEquals(new DateTime('2016-03-21'), $webhookNotification->subscription->billingPeriodStartDate);
        $this->assertEquals(new DateTime('2017-03-31'), $webhookNotification->subscription->billingPeriodEndDate);
        $this->assertEquals(1, count($webhookNotification->subscription->transactions));

        $transaction = $webhookNotification->subscription->transactions[0];
        $this->assertEquals('submitted_for_settlement', $transaction->status);
        $this->assertEquals('49.99', $transaction->amount);
    }

    public function testBuildsASampleNotificationForASubscriptionChargedUnsuccessfullyWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::SUBSCRIPTION_CHARGED_UNSUCCESSFULLY,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::SUBSCRIPTION_CHARGED_UNSUCCESSFULLY, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->subscription->id);
        $this->assertEquals("Active", $webhookNotification->subscription->status);
        $this->assertEquals(new DateTime('2016-03-21'), $webhookNotification->subscription->billingPeriodStartDate);
        $this->assertEquals(new DateTime('2017-03-31'), $webhookNotification->subscription->billingPeriodEndDate);
        $this->assertEquals(1, count($webhookNotification->subscription->transactions));

        $transaction = $webhookNotification->subscription->transactions[0];
        $this->assertEquals('failed', $transaction->status);
        $this->assertEquals('49.99', $transaction->amount);
    }

    public function testBuildsASampleNotificationForASubscriptionExpiredWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::SUBSCRIPTION_EXPIRED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::SUBSCRIPTION_EXPIRED, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->subscription->id);
        $this->assertEquals("Expired", $webhookNotification->subscription->status);
        $this->assertEquals(0, count($webhookNotification->subscription->transactions));
    }

    public function testBuildsASampleNotificationForASubscriptionCanceledWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::SUBSCRIPTION_CANCELED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::SUBSCRIPTION_CANCELED, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->subscription->id);
        $this->assertEquals("Canceled", $webhookNotification->subscription->status);
        $this->assertEquals(0, count($webhookNotification->subscription->transactions));
    }

    public function testBuildsASampleNotificationForASubscriptionWentPastDueWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::SUBSCRIPTION_WENT_PAST_DUE,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::SUBSCRIPTION_WENT_PAST_DUE, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->subscription->id);
        $this->assertEquals("Past Due", $webhookNotification->subscription->status);
        $this->assertEquals(0, count($webhookNotification->subscription->transactions));
    }

    public function testBuildsASampleNotificationForATransactionDisbursedWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::TRANSACTION_DISBURSED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::TRANSACTION_DISBURSED, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->transaction->id);
        $this->assertEquals(100, $webhookNotification->transaction->amount);
        $this->assertNotNull($webhookNotification->transaction->disbursementDetails->disbursementDate);
    }

    public function testBuildsASampleNotificationForATransactionRetriedWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::TRANSACTION_RETRIED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::TRANSACTION_RETRIED, $webhookNotification->kind);
        $transaction = $webhookNotification->transaction;
        $this->assertEquals("my_id", $transaction->id);
        $this->assertEquals(100, $transaction->amount);
        $this->assertEquals("submitted_for_settlement", $transaction->status);
        $this->assertEquals('original_txn_id', $transaction->retriedTransactionId);
    }

    public function testBuildsASampleNotificationForATransactionReviewedWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::TRANSACTION_REVIEWED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::TRANSACTION_REVIEWED, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->transactionReview->transactionId);
        $this->assertEquals("smart_decision", $webhookNotification->transactionReview->decision);
        $this->assertEquals("hey@girl.com", $webhookNotification->transactionReview->reviewerEmail);
        $this->assertEquals("I reviewed this", $webhookNotification->transactionReview->reviewerNote);
        $this->assertNotNull($webhookNotification->transactionReview->reviewedTime);
    }
    public function testBuildsASampleNotificationForATransactionSettledWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::TRANSACTION_SETTLED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::TRANSACTION_SETTLED, $webhookNotification->kind);
        $transaction = $webhookNotification->transaction;
        $this->assertEquals("my_id", $transaction->id);
        $this->assertEquals("settled", $transaction->status);
        $this->assertEquals(100, $transaction->amount);
        $this->assertEquals('123456789', $transaction->usBankAccount->routingNumber);
        $this->assertEquals('1234', $transaction->usBankAccount->last4);
        $this->assertEquals('checking', $transaction->usBankAccount->accountType);
        $this->assertEquals('Dan Schulman', $transaction->usBankAccount->accountHolderName);
    }

    public function testBuildsASampleNotificationForATransactionSettlementDeclinedWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::TRANSACTION_SETTLEMENT_DECLINED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::TRANSACTION_SETTLEMENT_DECLINED, $webhookNotification->kind);
        $transaction = $webhookNotification->transaction;
        $this->assertEquals("my_id", $transaction->id);
        $this->assertEquals("settlement_declined", $transaction->status);
        $this->assertEquals(100, $transaction->amount);
        $this->assertEquals('123456789', $transaction->usBankAccount->routingNumber);
        $this->assertEquals('1234', $transaction->usBankAccount->last4);
        $this->assertEquals('checking', $transaction->usBankAccount->accountType);
        $this->assertEquals('Dan Schulman', $transaction->usBankAccount->accountHolderName);
    }

    public function testBuildsASampleNotificationForARefundFailedWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::REFUND_FAILED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::REFUND_FAILED, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->transaction->id);
    }

    public function testBuildsASampleNotificationForADisputeUnderReviewWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::DISPUTE_UNDER_REVIEW,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::DISPUTE_UNDER_REVIEW, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->dispute->id);
        $this->assertEquals(Braintree\Dispute::UNDER_REVIEW, $webhookNotification->dispute->status);
        $this->assertEquals(Braintree\Dispute::CHARGEBACK, $webhookNotification->dispute->kind);
    }

    public function testBuildsASampleNotificationForADisputeOpenedWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::DISPUTE_OPENED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::DISPUTE_OPENED, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->dispute->id);
        $this->assertEquals(Braintree\Dispute::OPEN, $webhookNotification->dispute->status);
        $this->assertEquals(Braintree\Dispute::CHARGEBACK, $webhookNotification->dispute->kind);
        $this->assertEquals(new DateTime('2014-03-21'), $webhookNotification->dispute->dateOpened);
    }

    public function testBuildsASampleNotificationForADisputeLostWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::DISPUTE_LOST,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::DISPUTE_LOST, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->dispute->id);
        $this->assertEquals(Braintree\Dispute::LOST, $webhookNotification->dispute->status);
        $this->assertEquals(Braintree\Dispute::CHARGEBACK, $webhookNotification->dispute->kind);
        $this->assertEquals(new DateTime('2014-03-21'), $webhookNotification->dispute->dateOpened);
    }

    public function testBuildsASampleNotificationForADisputeWonWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::DISPUTE_WON,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::DISPUTE_WON, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->dispute->id);
        $this->assertEquals(Braintree\Dispute::WON, $webhookNotification->dispute->status);
        $this->assertEquals(Braintree\Dispute::CHARGEBACK, $webhookNotification->dispute->kind);
        $this->assertEquals(new DateTime('2014-03-21'), $webhookNotification->dispute->dateOpened);
        $this->assertEquals(new DateTime('2014-03-22'), $webhookNotification->dispute->dateWon);
    }

    public function testBuildsASampleNotificationForADisputeAcceptedWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::DISPUTE_ACCEPTED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::DISPUTE_ACCEPTED, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->dispute->id);
        $this->assertEquals(Braintree\Dispute::ACCEPTED, $webhookNotification->dispute->status);
        $this->assertEquals(Braintree\Dispute::CHARGEBACK, $webhookNotification->dispute->kind);
    }

    public function testBuildsASampleNotificationForADisputeAutoAcceptedWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::DISPUTE_AUTO_ACCEPTED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::DISPUTE_AUTO_ACCEPTED, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->dispute->id);
        $this->assertEquals(Braintree\Dispute::AUTO_ACCEPTED, $webhookNotification->dispute->status);
        $this->assertEquals(Braintree\Dispute::CHARGEBACK, $webhookNotification->dispute->kind);
    }

    public function testBuildsASampleNotificationForADisputeDisputedWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::DISPUTE_DISPUTED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::DISPUTE_DISPUTED, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->dispute->id);
        $this->assertEquals(Braintree\Dispute::DISPUTED, $webhookNotification->dispute->status);
        $this->assertEquals(Braintree\Dispute::CHARGEBACK, $webhookNotification->dispute->kind);
    }

    public function testBuildsASampleNotificationForADisputeExpiredWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::DISPUTE_EXPIRED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::DISPUTE_EXPIRED, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->dispute->id);
        $this->assertEquals(Braintree\Dispute::EXPIRED, $webhookNotification->dispute->status);
        $this->assertEquals(Braintree\Dispute::CHARGEBACK, $webhookNotification->dispute->kind);
    }

    public function testBuildsASampleNotificationForADisbursementWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::DISBURSEMENT,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );


        $this->assertEquals(Braintree\WebhookNotification::DISBURSEMENT, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->disbursement->id);
        $this->assertEquals(false, $webhookNotification->disbursement->retry);
        $this->assertEquals(true, $webhookNotification->disbursement->success);
        $this->assertEquals(null, $webhookNotification->disbursement->exceptionMessage);
        $this->assertEquals(100.00, $webhookNotification->disbursement->amount);
        $this->assertEquals(null, $webhookNotification->disbursement->followUpAction);
        $this->assertEquals("merchant_account_token", $webhookNotification->disbursement->merchantAccount->id);
        $this->assertEquals(new DateTime("2014-02-10"), $webhookNotification->disbursement->disbursementDate);
        $this->assertEquals(["asdfg", "qwert"], $webhookNotification->disbursement->transactionIds);
    }
    public function testBuildsASampleNotificationForAPartnerMerchantConnectedWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::PARTNER_MERCHANT_CONNECTED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::PARTNER_MERCHANT_CONNECTED, $webhookNotification->kind);
        $this->assertEquals("public_id", $webhookNotification->partnerMerchant->merchantPublicId);
        $this->assertEquals("public_key", $webhookNotification->partnerMerchant->publicKey);
        $this->assertEquals("private_key", $webhookNotification->partnerMerchant->privateKey);
        $this->assertEquals("abc123", $webhookNotification->partnerMerchant->partnerMerchantId);
        $this->assertEquals("cse_key", $webhookNotification->partnerMerchant->clientSideEncryptionKey);
    }

    public function testBuildsASampleNotificationForAPartnerMerchantDisconnectedWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::PARTNER_MERCHANT_DISCONNECTED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::PARTNER_MERCHANT_DISCONNECTED, $webhookNotification->kind);
        $this->assertEquals("abc123", $webhookNotification->partnerMerchant->partnerMerchantId);
    }

    public function testBuildsASampleNotificationForAPartnerMerchantDeclinedWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::PARTNER_MERCHANT_DECLINED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::PARTNER_MERCHANT_DECLINED, $webhookNotification->kind);
        $this->assertEquals("abc123", $webhookNotification->partnerMerchant->partnerMerchantId);
    }

    public function testBuildsASampleNotificationForOAuthAccessRevokedWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::OAUTH_ACCESS_REVOKED,
            'my_id'
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::OAUTH_ACCESS_REVOKED, $webhookNotification->kind);
        $this->assertEquals('my_id', $webhookNotification->oauthAccessRevocation->merchantId);
        $this->assertEquals("oauth_application_client_id", $webhookNotification->oauthAccessRevocation->oauthApplicationClientId);
    }

    public function testBuildsASampleNotificationForConnectedMerchantStatusTransitionedWebhook()
    {
        $gateway = new Braintree\Gateway([
            'privateKey' => 'integration_private_key',
            'publicKey' => 'integration_public_key',
            'merchantId' => 'integration_merchant_id',
            'environment' => 'development'
        ]);

        $sampleNotification = $gateway->webhookTesting()->sampleNotification(
            Braintree\WebhookNotification::CONNECTED_MERCHANT_STATUS_TRANSITIONED,
            "my_id"
        );

        $webhookNotification = $gateway->webhookNotification()->parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::CONNECTED_MERCHANT_STATUS_TRANSITIONED, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->connectedMerchantStatusTransitioned->merchantPublicId);
        $this->assertEquals("my_id", $webhookNotification->connectedMerchantStatusTransitioned->merchantId);
        $this->assertEquals("new_status", $webhookNotification->connectedMerchantStatusTransitioned->status);
        $this->assertEquals("oauth_application_client_id", $webhookNotification->connectedMerchantStatusTransitioned->oauthApplicationClientId);
    }

    public function testBuildsASampleNotificationForConnectedMerchantPayPalStatusChangedWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::CONNECTED_MERCHANT_PAYPAL_STATUS_CHANGED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::CONNECTED_MERCHANT_PAYPAL_STATUS_CHANGED, $webhookNotification->kind);
        $this->assertEquals("my_id", $webhookNotification->connectedMerchantPayPalStatusChanged->merchantPublicId);
        $this->assertEquals("my_id", $webhookNotification->connectedMerchantPayPalStatusChanged->merchantId);
        $this->assertEquals("link", $webhookNotification->connectedMerchantPayPalStatusChanged->action);
        $this->assertEquals("oauth_application_client_id", $webhookNotification->connectedMerchantPayPalStatusChanged->oauthApplicationClientId);
    }

    public function testBuildsASampleNotificationForACheckWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::CHECK,
            ""
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification["bt_signature"],
            $sampleNotification["bt_payload"]
        );

        $this->assertEquals(Braintree\WebhookNotification::CHECK, $webhookNotification->kind);
    }

    public function testAccountUpdaterDailyReportWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::ACCOUNT_UPDATER_DAILY_REPORT,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::ACCOUNT_UPDATER_DAILY_REPORT, $webhookNotification->kind);
        $this->assertEquals("link-to-csv-report", $webhookNotification->accountUpdaterDailyReport->reportUrl);
        $this->assertEquals(new DateTime("2016-01-14"), $webhookNotification->accountUpdaterDailyReport->reportDate);
    }

    public function testGrantorUpdatedGrantedPaymentMethodWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::GRANTOR_UPDATED_GRANTED_PAYMENT_METHOD,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::GRANTOR_UPDATED_GRANTED_PAYMENT_METHOD, $webhookNotification->kind);
        $update = $webhookNotification->grantedPaymentInstrumentUpdate;

        $this->assertEquals("vczo7jqrpwrsi2px", $update->grantOwnerMerchantId);
        $this->assertEquals("cf0i8wgarszuy6hc", $update->grantRecipientMerchantId);
        $this->assertEquals("ee257d98-de40-47e8-96b3-a6954ea7a9a4", $update->paymentMethodNonce->nonce);
        $this->assertEquals("abc123z", $update->token);
        $this->assertEquals(array("expiration-month", "expiration-year"), $update->updatedFields);
    }

    public function testRecipientUpdatedGrantedPaymentMethodWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::RECIPIENT_UPDATED_GRANTED_PAYMENT_METHOD,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::RECIPIENT_UPDATED_GRANTED_PAYMENT_METHOD, $webhookNotification->kind);
        $update = $webhookNotification->grantedPaymentInstrumentUpdate;

        $this->assertEquals("vczo7jqrpwrsi2px", $update->grantOwnerMerchantId);
        $this->assertEquals("cf0i8wgarszuy6hc", $update->grantRecipientMerchantId);
        $this->assertEquals("ee257d98-de40-47e8-96b3-a6954ea7a9a4", $update->paymentMethodNonce->nonce);
        $this->assertEquals("abc123z", $update->token);
        $this->assertEquals(array("expiration-month", "expiration-year"), $update->updatedFields);
    }

    public function testGrantedPaymentMethodRevokedCreditCardWebhook()
    {
        $xmlPayload = "
            <notification>
                <source-merchant-id>12345</source-merchant-id>
                <timestamp type='datetime'>2018-10-10T22:46:41Z</timestamp>
                <kind>granted_payment_method_revoked</kind>
                <subject>
                    <credit-card>
                        <bin>555555</bin>
                        <business>Unknown</business>
                        <card-type>MasterCard</card-type>
                        <cardholder-name>Amber Ankunding</cardholder-name>
                        <commercial>Unknown</commercial>
                        <consumer>Unknown</consumer>
                        <corporate>Unknown</corporate>
                        <country-of-issuance>Unknown</country-of-issuance>
                        <created-at type='datetime'>2018-10-10T22:46:41Z</created-at>
                        <customer-id>credit_card_customer_id</customer-id>
                        <customer-location>US</customer-location>
                        <debit>Unknown</debit>
                        <default type='boolean'>true</default>
                        <durbin-regulated>Unknown</durbin-regulated>
                        <expiration-month>06</expiration-month>
                        <expiration-year>2020</expiration-year>
                        <expired type='boolean'>false</expired>
                        <global-id>cGF5bWVudG1ldGhvZF8zcHQ2d2hz</global-id>
                        <healthcare>Unknown</healthcare>
                        <image-url>https://assets.braintreegateway.com/payment_method_logo/mastercard.png?environment=test</image-url>
                        <issuing-bank>Unknown</issuing-bank>
                        <last-4>4444</last-4>
                        <payroll>Unknown</payroll>
                        <prepaid>Unknown</prepaid>
                        <prepaid-reloadable>Unknown</prepaid-reloadable>
                        <product-id>Unknown</product-id>
                        <purchase>Unknown</purchase>
                        <subscriptions type='array'/>
                        <token>credit_card_token</token>
                        <unique-number-identifier>08199d188e37460163207f714faf074a</unique-number-identifier>
                        <updated-at type='datetime'>2018-10-10T22:46:41Z</updated-at>
                        <venmo-sdk type='boolean'>false</venmo-sdk>
                        <verifications type='array'/>
                    </credit-card>
                </subject>
            </notification>
        ";

        $sampleNotification = Helper::sampleNotificationFromXml($xmlPayload);

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::GRANTED_PAYMENT_METHOD_REVOKED, $webhookNotification->kind);
        $metadata = $webhookNotification->revokedPaymentMethodMetadata;

        $this->assertEquals("credit_card_customer_id", $metadata->customerId);
        $this->assertEquals("credit_card_token", $metadata->token);
        $this->assertTrue($metadata->revokedPaymentMethod instanceof Braintree\CreditCard);
    }

    public function testGrantedPaymentMethodRevokedPayPalAccountWebhook()
    {
        $xmlPayload = "
			<notification>
				<source-merchant-id>12345</source-merchant-id>
				<timestamp type='datetime'>2018-10-10T22:46:41Z</timestamp>
				<kind>granted_payment_method_revoked</kind>
				<subject>
					<paypal-account>
						<billing-agreement-id>billing_agreement_id</billing-agreement-id>
						<created-at type='dateTime'>2018-10-11T21:10:33Z</created-at>
						<customer-id>paypal_customer_id</customer-id>
						<default type='boolean'>true</default>
						<email>johndoe@example.com</email>
						<global-id>cGF5bWVudG1ldGhvZF9wYXlwYWxfdG9rZW4</global-id>
						<image-url>https://assets.braintreegateway.com/payment_method_logo/mastercard.png?environment=test</image-url>
						<subscriptions type='array'></subscriptions>
						<token>paypal_token</token>
						<updated-at type='dateTime'>2018-10-11T21:10:33Z</updated-at>
						<payer-id>a6a8e1a4</payer-id>
					</paypal-account>
				</subject>
			</notification>";

        $sampleNotification = Helper::sampleNotificationFromXml($xmlPayload);

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::GRANTED_PAYMENT_METHOD_REVOKED, $webhookNotification->kind);
        $metadata = $webhookNotification->revokedPaymentMethodMetadata;

        $this->assertEquals("paypal_customer_id", $metadata->customerId);
        $this->assertEquals("paypal_token", $metadata->token);
        $this->assertTrue($metadata->revokedPaymentMethod instanceof Braintree\PayPalAccount);
    }

    public function testGrantedPaymentMethodRevokedVenmoAccountWebhook()
    {
        $xmlPayload = "
			 <notification>
				<source-merchant-id>12345</source-merchant-id>
				<timestamp type='datetime'>2018-10-10T22:46:41Z</timestamp>
				<kind>granted_payment_method_revoked</kind>
				<subject>
					<venmo-account>
						<created-at type='dateTime'>2018-10-11T21:28:37Z</created-at>
						<updated-at type='dateTime'>2018-10-11T21:28:37Z</updated-at>
						<default type='boolean'>true</default>
						<image-url>https://assets.braintreegateway.com/payment_method_logo/mastercard.png?environment=test</image-url>
						<token>venmo_token</token>
						<source-description>Venmo Account: venmojoe</source-description>
						<username>venmojoe</username>
						<venmo-user-id>456</venmo-user-id>
						<subscriptions type='array'/>
						<customer-id>venmo_customer_id</customer-id>
						<global-id>cGF5bWVudG1ldGhvZF92ZW5tb2FjY291bnQ</global-id>
					</venmo-account>
				</subject>
			 </notification>";

        $sampleNotification = Helper::sampleNotificationFromXml($xmlPayload);

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::GRANTED_PAYMENT_METHOD_REVOKED, $webhookNotification->kind);
        $metadata = $webhookNotification->revokedPaymentMethodMetadata;

        $this->assertEquals("venmo_customer_id", $metadata->customerId);
        $this->assertEquals("venmo_token", $metadata->token);
        $this->assertTrue($metadata->revokedPaymentMethod instanceof Braintree\VenmoAccount);
    }

    public function testGrantedPaymentMethodRevokedWebhookSample()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::GRANTED_PAYMENT_METHOD_REVOKED,
            "my_payment_method_token"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::GRANTED_PAYMENT_METHOD_REVOKED, $webhookNotification->kind);
        $metadata = $webhookNotification->revokedPaymentMethodMetadata;

        $this->assertEquals("my_payment_method_token", $metadata->token);
        $this->assertTrue($metadata->revokedPaymentMethod instanceof Braintree\VenmoAccount);
        $this->assertEquals("venmo_customer_id", $metadata->customerId);
    }

    public function testPaymentMethodRevokedByCustomerWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::PAYMENT_METHOD_REVOKED_BY_CUSTOMER,
            "my_payment_method_token"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::PAYMENT_METHOD_REVOKED_BY_CUSTOMER, $webhookNotification->kind);
        $metadata = $webhookNotification->revokedPaymentMethodMetadata;

        $this->assertEquals("my_payment_method_token", $metadata->token);
        $this->assertTrue($metadata->revokedPaymentMethod instanceof Braintree\PayPalAccount);
        $this->assertNotNull($metadata->revokedPaymentMethod->revokedAt);
    }

    public function testLocalPaymentReversedWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::LOCAL_PAYMENT_REVERSED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::LOCAL_PAYMENT_REVERSED, $webhookNotification->kind);
        $localPaymentReversed = $webhookNotification->localPaymentReversed;

        $this->assertEquals("a-payment-id", $localPaymentReversed->paymentId);
    }

    public function testLocalPaymentCompletedWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::LOCAL_PAYMENT_COMPLETED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::LOCAL_PAYMENT_COMPLETED, $webhookNotification->kind);
        $localPaymentCompleted = $webhookNotification->localPaymentCompleted;

        $this->assertEquals("a-bic", $localPaymentCompleted->bic);
        $this->assertEquals([], $localPaymentCompleted->blikAliases);
        $this->assertEquals("1234", $localPaymentCompleted->ibanLastChars);
        $this->assertEquals("a-payer-id", $localPaymentCompleted->payerId);
        $this->assertEquals("a-payer-name", $localPaymentCompleted->payerName);
        $this->assertEquals("a-payment-id", $localPaymentCompleted->paymentId);
        $this->assertEquals("ee257d98-de40-47e8-96b3-a6954ea7a9a4", $localPaymentCompleted->paymentMethodNonce);
        $this->assertNotNull($localPaymentCompleted->transaction);
        $t = $localPaymentCompleted->transaction;
        $this->assertEquals("1", $t['id']);
        $this->assertEquals("authorizing", $t['status']);
        $this->assertEquals("10.00", $t['amount']);
        $this->assertEquals("order1234", $t['orderId']);
    }

    public function testLocalPaymentCompletedWebhookiforBlikOneClick()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::LOCAL_PAYMENT_COMPLETED,
            "blik_one_click_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::LOCAL_PAYMENT_COMPLETED, $webhookNotification->kind);
        $localPaymentCompleted = $webhookNotification->localPaymentCompleted;

        $this->assertEquals("a-bic", $localPaymentCompleted->bic);
        $this->assertEquals("unique-key-1", $localPaymentCompleted->blikAliases[0]->key);
        $this->assertEquals("unique-label-1", $localPaymentCompleted->blikAliases[0]->label);
        $this->assertEquals("1234", $localPaymentCompleted->ibanLastChars);
        $this->assertEquals("a-payer-id", $localPaymentCompleted->payerId);
        $this->assertEquals("a-payer-name", $localPaymentCompleted->payerName);
        $this->assertEquals("a-payment-id", $localPaymentCompleted->paymentId);
        $this->assertEquals("ee257d98-de40-47e8-96b3-a6954ea7a9a4", $localPaymentCompleted->paymentMethodNonce);
        $this->assertNotNull($localPaymentCompleted->transaction);
        $t = $localPaymentCompleted->transaction;
        $this->assertEquals("1", $t['id']);
        $this->assertEquals("authorizing", $t['status']);
        $this->assertEquals("10.00", $t['amount']);
        $this->assertEquals("order1234", $t['orderId']);
    }

    public function testLocalPaymentFundedWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::LOCAL_PAYMENT_FUNDED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::LOCAL_PAYMENT_FUNDED, $webhookNotification->kind);
        $localPaymentFunded = $webhookNotification->localPaymentFunded;

        $this->assertEquals("a-payment-id", $localPaymentFunded->paymentId);
        $this->assertEquals("a-payment-context-id", $localPaymentFunded->paymentContextId);
        $transaction = $localPaymentFunded->transaction;

        $this->assertNotNull($transaction);
        $this->assertEquals("1", $transaction["id"]);
        $this->assertEquals("settled", $transaction["status"]);
        $this->assertEquals("order1234", $transaction["orderId"]);
    }

    public function testLocalPaymentExpiredWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::LOCAL_PAYMENT_EXPIRED,
            "my_id"
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::LOCAL_PAYMENT_EXPIRED, $webhookNotification->kind);
        $localPaymentExpired = $webhookNotification->localPaymentExpired;

        $this->assertEquals("a-payment-id", $localPaymentExpired->paymentId);
        $this->assertEquals("a-payment-context-id", $localPaymentExpired->paymentContextId);
    }

    public function testPaymentMethodCustomerDataUpdatedWebhook()
    {
        $sampleNotification = Braintree\WebhookTesting::sampleNotification(
            Braintree\WebhookNotification::PAYMENT_METHOD_CUSTOMER_DATA_UPDATED,
            'venmo_account'
        );

        $webhookNotification = Braintree\WebhookNotification::parse(
            $sampleNotification['bt_signature'],
            $sampleNotification['bt_payload']
        );

        $this->assertEquals(Braintree\WebhookNotification::PAYMENT_METHOD_CUSTOMER_DATA_UPDATED, $webhookNotification->kind);

        $paymentMethodCustomerDataUpdated = $webhookNotification->paymentMethodCustomerDataUpdatedMetadata;
        $this->assertEquals('TOKEN-12345', $paymentMethodCustomerDataUpdated->token);
        $this->assertEquals('2022-01-01T21:28:37Z', $paymentMethodCustomerDataUpdated->datetimeUpdated);
        $enrichedCustomerData = $paymentMethodCustomerDataUpdated->enrichedCustomerData;
        $this->assertEquals(array("firstName"), $enrichedCustomerData->fieldsUpdated);

        $profileData = $enrichedCustomerData->profileData;
        $this->assertEquals('venmo_username', $profileData->username);
        $this->assertEquals('John', $profileData->firstName);
        $this->assertEquals('Doe', $profileData->lastName);
        $this->assertEquals('1231231234', $profileData->phoneNumber);
        $this->assertEquals('john.doe@paypal.com', $profileData->email);

        $billingAddress = $profileData->billingAddress;
        $this->assertEquals('billing-street-address', $billingAddress->streetAddress);
        $this->assertEquals('billing-extended-address', $billingAddress->extendedAddress);
        $this->assertEquals('billing-locality', $billingAddress->locality);
        $this->assertEquals('billing-region', $billingAddress->region);
        $this->assertEquals('billing-code', $billingAddress->postalCode);

        $shippingAddress = $profileData->shippingAddress;
        $this->assertEquals('shipping-street-address', $shippingAddress->streetAddress);
        $this->assertEquals('shipping-extended-address', $shippingAddress->extendedAddress);
        $this->assertEquals('shipping-locality', $shippingAddress->locality);
        $this->assertEquals('shipping-region', $shippingAddress->region);
        $this->assertEquals('shipping-code', $shippingAddress->postalCode);
    }
}
