package com.braintreegateway.integrationtest; import com.braintreegateway.*; import com.braintreegateway.BraintreeGateway; import com.braintreegateway.Environment; import com.braintreegateway.MerchantAccount; import com.braintreegateway.WebhookNotification; import com.braintreegateway.exceptions.InvalidSignatureException; import com.braintreegateway.exceptions.InvalidChallengeException; import com.braintreegateway.testhelpers.TestHelper; import com.braintreegateway.util.NodeWrapper; import com.braintreegateway.util.NodeWrapperFactory; import com.braintreegateway.ValidationErrorCode; import org.junit.Test; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import static org.junit.Assert.*; public class WebhookNotificationIT extends IntegrationTest { @Test public void createNotificationWithUnrecognizedKind() { String xml = "<notification><kind>" + "bad_kind" + "</kind> <subject> </subject> </notification>"; NodeWrapper node = NodeWrapperFactory.instance.create(xml); WebhookNotification notification = new WebhookNotification(node); assertEquals(WebhookNotification.Kind.UNRECOGNIZED, notification.getKind()); } @Test public void verifyCreatesAVerificationString() { String verification = this.gateway.webhookNotification().verify("20f9f8ed05f77439fe955c977e4c8a53"); assertEquals("integration_public_key|d9b899556c966b3f06945ec21311865d35df3ce4", verification); } @Test public void invalidChallengeRaisesException() { try { this.gateway.webhookNotification().verify("bad challenge"); fail("Should have throw exception, but did not."); } catch(final InvalidChallengeException e) { final String expectedMessage = "challenge contains non-hex characters"; assertEquals(expectedMessage, e.getMessage()); } } @Test public void createsSampleSubscriptionNotification() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting().sampleNotification(WebhookNotification.Kind.SUBSCRIPTION_WENT_PAST_DUE, "my_id"); WebhookNotification notification = this.gateway.webhookNotification().parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.SUBSCRIPTION_WENT_PAST_DUE, notification.getKind()); assertEquals("my_id", notification.getSubscription().getId()); TestHelper.assertDatesEqual(Calendar.getInstance(), notification.getTimestamp()); } @Test public void createsSampleSubscriptionChargedSuccessfullyNotification() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting().sampleNotification(WebhookNotification.Kind.SUBSCRIPTION_CHARGED_SUCCESSFULLY, "my_id"); WebhookNotification notification = this.gateway.webhookNotification().parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.SUBSCRIPTION_CHARGED_SUCCESSFULLY, notification.getKind()); assertEquals("my_id", notification.getSubscription().getId()); assertEquals(1, notification.getSubscription().getTransactions().size()); Transaction transaction = notification.getSubscription().getTransactions().get(0); assertEquals(Transaction.Status.SUBMITTED_FOR_SETTLEMENT, transaction.getStatus()); assertEquals("49.99", transaction.getAmount().toString()); } @Test public void createsSampleMerchantAccountApprovedNotification() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting().sampleNotification(WebhookNotification.Kind.SUB_MERCHANT_ACCOUNT_APPROVED, "my_id"); WebhookNotification notification = this.gateway.webhookNotification().parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.SUB_MERCHANT_ACCOUNT_APPROVED, notification.getKind()); assertEquals("my_id", notification.getMerchantAccount().getId()); assertEquals(MerchantAccount.Status.ACTIVE, notification.getMerchantAccount().getStatus()); TestHelper.assertDatesEqual(Calendar.getInstance(), notification.getTimestamp()); } @Test public void createsSampleMerchantAccountDeclinedNotification() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting().sampleNotification(WebhookNotification.Kind.SUB_MERCHANT_ACCOUNT_DECLINED, "my_id"); WebhookNotification notification = this.gateway.webhookNotification().parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.SUB_MERCHANT_ACCOUNT_DECLINED, notification.getKind()); assertEquals("my_id", notification.getMerchantAccount().getId()); assertEquals(MerchantAccount.Status.SUSPENDED, notification.getMerchantAccount().getStatus()); TestHelper.assertDatesEqual(Calendar.getInstance(), notification.getTimestamp()); } @Test public void createsSampleMerchantAccountDeclinedNotificationWithErrorCodes() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting().sampleNotification(WebhookNotification.Kind.SUB_MERCHANT_ACCOUNT_DECLINED, "my_id"); WebhookNotification notification = this.gateway.webhookNotification().parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.SUB_MERCHANT_ACCOUNT_DECLINED, notification.getKind()); assertEquals("my_id", notification.getMerchantAccount().getId()); TestHelper.assertDatesEqual(Calendar.getInstance(), notification.getTimestamp()); assertEquals(ValidationErrorCode.MERCHANT_ACCOUNT_DECLINED_OFAC, notification.getErrors().forObject("merchantAccount").onField("base").get(0).getCode()); } @Test public void createsSampleDisputeOpenedNotification() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting().sampleNotification(WebhookNotification.Kind.DISPUTE_OPENED, "my_id"); WebhookNotification notification = this.gateway.webhookNotification().parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.DISPUTE_OPENED, notification.getKind()); assertEquals("my_id", notification.getDispute().getId()); assertEquals(Dispute.Status.OPEN, notification.getDispute().getStatus()); assertEquals(Dispute.Kind.CHARGEBACK, notification.getDispute().getKind()); assertNotNull(notification.getDispute().getOpenedDate()); } public void createsSampleDisputeWonNotification() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting().sampleNotification(WebhookNotification.Kind.DISPUTE_WON, "my_id"); WebhookNotification notification = this.gateway.webhookNotification().parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.DISPUTE_WON, notification.getKind()); assertEquals("my_id", notification.getDispute().getId()); assertEquals(Dispute.Status.WON, notification.getDispute().getStatus()); assertEquals(Dispute.Kind.CHARGEBACK, notification.getDispute().getKind()); assertNotNull(notification.getDispute().getOpenedDate()); } public void createsSampleDisputeLostNotification() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting().sampleNotification(WebhookNotification.Kind.DISPUTE_LOST, "my_id"); WebhookNotification notification = this.gateway.webhookNotification().parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.DISPUTE_LOST, notification.getKind()); assertEquals("my_id", notification.getDispute().getId()); assertEquals(Dispute.Status.LOST, notification.getDispute().getStatus()); assertEquals(Dispute.Kind.CHARGEBACK, notification.getDispute().getKind()); assertNotNull(notification.getDispute().getOpenedDate()); assertNotNull(notification.getDispute().getWonDate()); } @Test(expected = InvalidSignatureException.class) public void invalidSignatureRaisesException() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting().sampleNotification(WebhookNotification.Kind.SUBSCRIPTION_WENT_PAST_DUE, "my_id"); this.gateway.webhookNotification().parse(sampleNotification.get("bt_signature") + "bad_stuff", sampleNotification.get("bt_payload")); } @Test public void signatureWithoutMatchingPublicKeyRaisesException() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting().sampleNotification(WebhookNotification.Kind.SUBSCRIPTION_WENT_PAST_DUE, "my_id"); try { this.gateway.webhookNotification().parse("unknown_public_key|signature", sampleNotification.get("bt_payload")); fail("Should have throw exception, but did not."); } catch(final InvalidSignatureException e) { final String expectedMessage = "no matching public key"; assertEquals(expectedMessage, e.getMessage()); } } @Test public void retriesPayloadWithNewLine() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting().sampleNotification(WebhookNotification.Kind.SUBSCRIPTION_WENT_PAST_DUE, "my_id"); try { this.gateway.webhookNotification().parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload").trim()); } catch(final InvalidSignatureException e) { fail("Threw an exception, but it should not have: " + e.getMessage()); } } @Test public void signatureWithChangedPayloadRaisesException() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting().sampleNotification(WebhookNotification.Kind.SUBSCRIPTION_WENT_PAST_DUE, "my_id"); try { this.gateway.webhookNotification().parse(sampleNotification.get("bt_signature"), "badstuff" + sampleNotification.get("bt_payload")); fail("Should have throw exception, but did not."); } catch(final InvalidSignatureException e) { final String expectedMessage = "signature does not match payload - one has been modified"; assertEquals(expectedMessage, e.getMessage()); } } @Test public void payloadWithInvalidCharactersRaisesException() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting().sampleNotification(WebhookNotification.Kind.SUBSCRIPTION_WENT_PAST_DUE, "my_id"); try { this.gateway.webhookNotification().parse(sampleNotification.get("bt_signature"), "~*~* Invalid! *~*~"); fail("Should have throw exception, but did not."); } catch(final InvalidSignatureException e) { final String expectedMessage = "payload contains illegal characters"; assertEquals(expectedMessage, e.getMessage()); } } @Test public void parseAllowsAllValidCharacters() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting().sampleNotification(WebhookNotification.Kind.SUBSCRIPTION_WENT_PAST_DUE, "my_id"); try { this.gateway.webhookNotification().parse(sampleNotification.get("bt_signature"), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+=/\n"); fail("Should have throw exception, but did not."); } catch(final InvalidSignatureException e) { final String unexpectedMessage = "payload contains illegal characters"; assertFalse(unexpectedMessage.equals(e.getMessage())); } } @Test public void createsSampleTransactionDisbursedNotification() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting().sampleNotification(WebhookNotification.Kind.TRANSACTION_DISBURSED, "my_id"); WebhookNotification notification = this.gateway.webhookNotification().parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.TRANSACTION_DISBURSED, notification.getKind()); assertEquals("my_id", notification.getTransaction().getId()); assertEquals(2013, notification.getTransaction().getDisbursementDetails().getDisbursementDate().get(Calendar.YEAR)); assertEquals(Calendar.JULY, notification.getTransaction().getDisbursementDetails().getDisbursementDate().get(Calendar.MONTH)); assertEquals(9, notification.getTransaction().getDisbursementDetails().getDisbursementDate().get(Calendar.DAY_OF_MONTH)); } @Test public void createsSampleTransactionSettledNotification() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting().sampleNotification(WebhookNotification.Kind.TRANSACTION_SETTLED, "my_id"); WebhookNotification notification = this.gateway.webhookNotification().parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.TRANSACTION_SETTLED, notification.getKind()); Transaction transaction = notification.getTransaction(); assertEquals("100", transaction.getAmount().toString()); assertEquals(Transaction.Status.SETTLED, transaction.getStatus()); UsBankAccountDetails usBankAccountDetails = transaction.getUsBankAccountDetails(); assertEquals("123456789", usBankAccountDetails.getRoutingNumber()); assertEquals("1234", usBankAccountDetails.getLast4()); assertEquals("checking", usBankAccountDetails.getAccountType()); assertEquals("Dan Schulman", usBankAccountDetails.getAccountHolderName()); } @Test public void createsSampleTransactionSettlementDeclinedNotification() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting().sampleNotification(WebhookNotification.Kind.TRANSACTION_SETTLEMENT_DECLINED, "my_id"); WebhookNotification notification = this.gateway.webhookNotification().parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.TRANSACTION_SETTLEMENT_DECLINED, notification.getKind()); Transaction transaction = notification.getTransaction(); assertEquals("100", transaction.getAmount().toString()); assertEquals(Transaction.Status.SETTLEMENT_DECLINED, transaction.getStatus()); UsBankAccountDetails usBankAccountDetails = transaction.getUsBankAccountDetails(); assertEquals("123456789", usBankAccountDetails.getRoutingNumber()); assertEquals("1234", usBankAccountDetails.getLast4()); assertEquals("checking", usBankAccountDetails.getAccountType()); assertEquals("Dan Schulman", usBankAccountDetails.getAccountHolderName()); } @Test public void createsSampleDisbursementNotification() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting().sampleNotification(WebhookNotification.Kind.DISBURSEMENT, "my_id"); WebhookNotification notification = this.gateway.webhookNotification().parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.DISBURSEMENT, notification.getKind()); assertEquals("my_id", notification.getDisbursement().getId()); assertEquals(null, notification.getDisbursement().getExceptionMessage()); assertEquals(2014, notification.getDisbursement().getDisbursementDate().get(Calendar.YEAR)); assertEquals(Calendar.FEBRUARY, notification.getDisbursement().getDisbursementDate().get(Calendar.MONTH)); assertEquals(10, notification.getDisbursement().getDisbursementDate().get(Calendar.DAY_OF_MONTH)); assertEquals(null, notification.getDisbursement().getFollowUpAction()); } @Test public void createsSampleDisbursementExceptionNotification() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting().sampleNotification(WebhookNotification.Kind.DISBURSEMENT_EXCEPTION, "my_id"); WebhookNotification notification = this.gateway.webhookNotification().parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.DISBURSEMENT_EXCEPTION, notification.getKind()); assertEquals("my_id", notification.getDisbursement().getId()); assertEquals("bank_rejected", notification.getDisbursement().getExceptionMessage()); assertEquals(2014, notification.getDisbursement().getDisbursementDate().get(Calendar.YEAR)); assertEquals(Calendar.FEBRUARY, notification.getDisbursement().getDisbursementDate().get(Calendar.MONTH)); assertEquals(10, notification.getDisbursement().getDisbursementDate().get(Calendar.DAY_OF_MONTH)); assertEquals("update_account_information", notification.getDisbursement().getFollowUpAction()); } @Test public void buildsSampleNotificationForPartnerMerchantConnectedWebhook() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting() .sampleNotification(WebhookNotification.Kind.PARTNER_MERCHANT_CONNECTED, "my_id"); WebhookNotification notification = this.gateway.webhookNotification() .parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.PARTNER_MERCHANT_CONNECTED, notification.getKind()); assertEquals("public_id", notification.getPartnerMerchant().getMerchantPublicId()); assertEquals("public_key", notification.getPartnerMerchant().getPublicKey()); assertEquals("private_key", notification.getPartnerMerchant().getPrivateKey()); assertEquals("abc123", notification.getPartnerMerchant().getPartnerMerchantId()); assertEquals("cse_key", notification.getPartnerMerchant().getClientSideEncryptionKey()); long now = new Date().getTime(); long age = now - notification.getTimestamp().getTime().getTime(); assertTrue(age < 5000); } @Test public void buildsSampleNotificationForPartnerMerchantDeclinedWebhook() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting() .sampleNotification(WebhookNotification.Kind.PARTNER_MERCHANT_DECLINED, "my_id"); WebhookNotification notification = this.gateway.webhookNotification() .parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.PARTNER_MERCHANT_DECLINED, notification.getKind()); assertEquals("abc123", notification.getPartnerMerchant().getPartnerMerchantId()); long now = new Date().getTime(); long age = now - notification.getTimestamp().getTime().getTime(); assertTrue(age < 5000); } @Test public void buildsSampleNotificationForPartnerMerchantDisconnectedWebhook() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting() .sampleNotification(WebhookNotification.Kind.PARTNER_MERCHANT_DISCONNECTED, "my_id"); WebhookNotification notification = this.gateway.webhookNotification() .parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.PARTNER_MERCHANT_DISCONNECTED, notification.getKind()); assertEquals("abc123", notification.getPartnerMerchant().getPartnerMerchantId()); assertEquals(null, notification.getPartnerMerchant().getMerchantPublicId()); assertEquals(null, notification.getPartnerMerchant().getPublicKey()); assertEquals(null, notification.getPartnerMerchant().getPrivateKey()); long now = new Date().getTime(); long age = now - notification.getTimestamp().getTime().getTime(); assertTrue(age < 5000); } @Test public void buildsSampleNotificationForConnectedMerchantStatusTransitionedWebhook() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting() .sampleNotification(WebhookNotification.Kind.CONNECTED_MERCHANT_STATUS_TRANSITIONED, "my_id"); WebhookNotification notification = this.gateway.webhookNotification() .parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.CONNECTED_MERCHANT_STATUS_TRANSITIONED, notification.getKind()); assertEquals("my_id", notification.getConnectedMerchantStatusTransitioned().getMerchantPublicId()); assertEquals("new_status", notification.getConnectedMerchantStatusTransitioned().getStatus()); assertEquals("oauth_application_client_id", notification.getConnectedMerchantStatusTransitioned().getOAuthApplicationClientId()); } @Test public void buildsSampleNotificationForConnectedMerchantPayPalStatusChangedWebhook() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting() .sampleNotification(WebhookNotification.Kind.CONNECTED_MERCHANT_PAYPAL_STATUS_CHANGED, "my_id"); WebhookNotification notification = this.gateway.webhookNotification() .parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.CONNECTED_MERCHANT_PAYPAL_STATUS_CHANGED, notification.getKind()); assertEquals("my_id", notification.getConnectedMerchantPayPalStatusChanged().getMerchantPublicId()); assertEquals("link", notification.getConnectedMerchantPayPalStatusChanged().getAction()); assertEquals("oauth_application_client_id", notification.getConnectedMerchantPayPalStatusChanged().getOAuthApplicationClientId()); } @Test public void buildsSampleNotificationForCheck() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting() .sampleNotification(WebhookNotification.Kind.CHECK, ""); WebhookNotification notification = this.gateway.webhookNotification() .parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.CHECK, notification.getKind()); long now = new Date().getTime(); long age = now - notification.getTimestamp().getTime().getTime(); assertTrue(age < 5000); } @Test public void buildsSampleNotificationForAccountUpdaterDailyReportWebhook() { HashMap<String, String> sampleNotification = this.gateway.webhookTesting() .sampleNotification(WebhookNotification.Kind.ACCOUNT_UPDATER_DAILY_REPORT, "my_id"); WebhookNotification notification = this.gateway.webhookNotification() .parse(sampleNotification.get("bt_signature"), sampleNotification.get("bt_payload")); assertEquals(WebhookNotification.Kind.ACCOUNT_UPDATER_DAILY_REPORT, notification.getKind()); assertEquals("link-to-csv-report", notification.getAccountUpdaterDailyReport().getReportUrl()); assertEquals(2016, notification.getAccountUpdaterDailyReport().getReportDate().get(Calendar.YEAR)); assertEquals(Calendar.JANUARY, notification.getAccountUpdaterDailyReport().getReportDate().get(Calendar.MONTH)); assertEquals(14, notification.getAccountUpdaterDailyReport().getReportDate().get(Calendar.DAY_OF_MONTH)); } }