package com.braintreegateway;
import com.braintreegateway.org.apache.commons.codec.binary.Base64;
import com.braintreegateway.util.Sha1Hasher;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.TimeZone;
public class WebhookTestingGateway {
private Configuration configuration;
public WebhookTestingGateway(Configuration configuration) {
this.configuration = configuration;
}
private String buildPayload(WebhookNotification.Kind kind, String id) {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
String timestamp = dateFormat.format(new Date());
String payload = "<notification><timestamp type=\"datetime\">" + timestamp + "</timestamp><kind>" + kind + "</kind><subject>" + subjectXml(kind, id) + "</subject></notification>";
return Base64.encodeBase64String(payload.getBytes()).replace("\r", "");
}
private String publicKeySignaturePair(String stringToSign) {
return String.format("%s|%s", configuration.getPublicKey(), new Sha1Hasher().hmacHash(configuration.getPrivateKey(), stringToSign));
}
public HashMap<String, String> sampleNotification(WebhookNotification.Kind kind, String id) {
HashMap<String, String> response = new HashMap<String, String>();
String payload = buildPayload(kind, id);
response.put("bt_payload", payload);
response.put("bt_signature", publicKeySignaturePair(payload));
return response;
}
private String subjectXml(WebhookNotification.Kind kind, String id) {
switch(kind) {
case CHECK: return checkXml();
case SUB_MERCHANT_ACCOUNT_APPROVED: return merchantAccountXmlActive(id);
case SUB_MERCHANT_ACCOUNT_DECLINED: return merchantAccountXmlDeclined(id);
case TRANSACTION_DISBURSED: return transactionXml(id);
case TRANSACTION_SETTLED: return transactionSettledXml(id);
case TRANSACTION_SETTLEMENT_DECLINED: return transactionSettlementDeclinedXml(id);
case DISBURSEMENT: return disbursementXml(id);
case DISPUTE_OPENED: return disputeOpenedXml(id);
case DISPUTE_LOST: return disputeLostXml(id);
case DISPUTE_WON: return disputeWonXml(id);
case DISBURSEMENT_EXCEPTION: return disbursementExceptionXml(id);
case PARTNER_MERCHANT_CONNECTED: return partnerMerchantConnectedXml(id);
case PARTNER_MERCHANT_DISCONNECTED: return partnerMerchantDisconnectedXml(id);
case PARTNER_MERCHANT_DECLINED: return partnerMerchantDeclinedXml(id);
case CONNECTED_MERCHANT_STATUS_TRANSITIONED: return connectedMerchantStatusTransitionedXml(id);
case CONNECTED_MERCHANT_PAYPAL_STATUS_CHANGED: return connectedMerchantPayPalStatusChangedXml(id);
case SUBSCRIPTION_CHARGED_SUCCESSFULLY: return subscriptionChargedSuccessfullyXml(id);
case ACCOUNT_UPDATER_DAILY_REPORT: return accountUpdaterDailyReportXml(id);
default: return subscriptionXml(id);
}
}
private String[][] TYPE_DATE = {{"type", "date"}};
private String[][] TYPE_ARRAY = {{"type", "array"}};
private String[][] TYPE_SYMBOL = {{"type", "symbol"}};
private String[][] TYPE_BOOLEAN = {{"type", "boolean"}};
private String[][] NIL_TRUE = {{"nil", "true"}};
private String merchantAccountXmlDeclined(String id) {
return node("api-error-response",
node("message", "Credit score is too low"),
node("errors", TYPE_ARRAY,
node("merchant-account",
node("errors", TYPE_ARRAY,
node("error",
node("code", "82621"),
node("message", "Credit score is too low"),
node("attribute", TYPE_SYMBOL, "base")
)
)
)
),
node("merchant-account",
node("id", id),
node("status", "suspended"),
node("master-merchant-account",
node("id", "master_ma_for_" + id),
node("status", "suspended")
)
)
);
}
private String merchantAccountXmlActive(String id) {
return node("merchant-account",
node("id", id),
node("master-merchant-account",
node("id", id),
node("status", "active")
),
node("status", "active")
);
}
private String subscriptionXml(String id) {
return node("subscription",
node("id", id),
node("transactions", TYPE_ARRAY),
node("add_ons", TYPE_ARRAY),
node("discounts", TYPE_ARRAY)
);
}
private String subscriptionChargedSuccessfullyXml(String id) {
return node("subscription",
node("id", id),
node("add_ons", TYPE_ARRAY),
node("transactions",
node("transaction",
node("id", "1"),
node("status", "submitted_for_settlement"),
node("amount", "49.99"),
node("billing"),
node("credit-card"),
node("customer"),
node("descriptor"),
node("shipping"),
node("disbursement-details", TYPE_ARRAY),
node("subscription")
)
),
node("discounts", TYPE_ARRAY)
);
}
private String transactionXml(String id) {
return node("transaction",
node("id", id),
node("amount", "100"),
node("disbursement-details",
node("disbursement-date", TYPE_DATE, "2013-07-09")
),
node("billing"),
node("credit-card"),
node("customer"),
node("descriptor"),
node("shipping"),
node("subscription")
);
}
private String transactionSettledXml(String id) {
return node("transaction",
node("id", id),
node("status", "settled"),
node("amount", "100"),
node("us-bank-account",
node("routing-number", "123456789"),
node("last-4", "1234"),
node("account-type", "checking"),
node("account-holder-name", "Dan Schulman"),
node("ach-mandate",
node("text", "Sample ACH Mandate Text"),
node("accepted-at", "2017-01-17")
)
),
node("disbursement-details"),
node("billing"),
node("credit-card"),
node("customer"),
node("descriptor"),
node("shipping"),
node("subscription")
);
}
private String transactionSettlementDeclinedXml(String id) {
return node("transaction",
node("id", id),
node("status", "settlement_declined"),
node("amount", "100"),
node("us-bank-account",
node("routing-number", "123456789"),
node("last-4", "1234"),
node("account-type", "checking"),
node("account-holder-name", "Dan Schulman"),
node("ach-mandate",
node("text", "Sample ACH Mandate Text"),
node("accepted-at", "2017-01-17")
)
),
node("disbursement-details"),
node("billing"),
node("credit-card"),
node("customer"),
node("descriptor"),
node("shipping"),
node("subscription")
);
}
private String disputeOpenedXml(String id) {
return node("dispute",
node("id", id),
node("amount", "250.00"),
node("received-date", TYPE_DATE, "2014-03-21"),
node("reply-by-date", TYPE_DATE, "2014-03-21"),
node("date-opened", TYPE_DATE, "2014-03-21"),
node("kind", "chargeback"),
node("currency-iso-code", "USD"),
node("status", "open"),
node("reason", "fraud"),
node("transaction",
node("id", id),
node("amount", "250.00")
)
);
}
private String disputeLostXml(String id) {
return node("dispute",
node("id", id),
node("amount", "250.00"),
node("received-date", TYPE_DATE, "2014-03-21"),
node("reply-by-date", TYPE_DATE, "2014-03-21"),
node("date-opened", TYPE_DATE, "2014-03-21"),
node("kind", "chargeback"),
node("currency-iso-code", "USD"),
node("status", "lost"),
node("reason", "fraud"),
node("transaction",
node("id", id),
node("amount", "250.00")
)
);
}
private String disputeWonXml(String id) {
return node("dispute",
node("id", id),
node("amount", "250.00"),
node("received-date", TYPE_DATE, "2014-03-21"),
node("reply-by-date", TYPE_DATE, "2014-03-21"),
node("date-opened", TYPE_DATE, "2014-03-21"),
node("date-won", TYPE_DATE, "2014-03-22"),
node("kind", "chargeback"),
node("currency-iso-code", "USD"),
node("status", "won"),
node("reason", "fraud"),
node("transaction",
node("id", id),
node("amount", "250.00")
)
);
}
private String disbursementXml(String id) {
return node("disbursement",
node("id", id),
node("transaction-ids", TYPE_ARRAY,
node("item", "asdf"),
node("item", "qwer")
),
node("success", TYPE_BOOLEAN, "true"),
node("retry", TYPE_BOOLEAN, "false"),
node("exception-message", NIL_TRUE),
node("amount", "100.00"),
node("disbursement-date", TYPE_DATE, "2014-02-10"),
node("follow-up-action", NIL_TRUE),
node("merchant-account",
node("id", "merchant_account_token"),
node("currency-iso-code", "USD"),
node("sub-merchant-account", TYPE_BOOLEAN, "false"),
node("status", "active")
)
);
}
private String disbursementExceptionXml(String id) {
return node("disbursement",
node("id", id),
node("transaction-ids", TYPE_ARRAY,
node("item", "asdf"),
node("item", "qwer")
),
node("success", TYPE_BOOLEAN, "false"),
node("retry", TYPE_BOOLEAN, "false"),
node("exception-message", "bank_rejected"),
node("amount", "100.00"),
node("disbursement-date", TYPE_DATE, "2014-02-10"),
node("follow-up-action", "update_account_information"),
node("merchant-account",
node("id", "merchant_account_token"),
node("currency-iso-code", "USD"),
node("sub-merchant-account", TYPE_BOOLEAN, "false"),
node("status", "active")
)
);
}
private String partnerMerchantConnectedXml(String id) {
return node("partner-merchant",
node("partner-merchant-id", "abc123"),
node("merchant-public-id", "public_id"),
node("public-key", "public_key"),
node("private-key", "private_key"),
node("client-side-encryption-key", "cse_key")
);
}
private String partnerMerchantDisconnectedXml(String id) {
return node("partner-merchant",
node("partner-merchant-id", "abc123")
);
}
private String partnerMerchantDeclinedXml(String id) {
return node("partner-merchant",
node("partner-merchant-id", "abc123")
);
}
private String connectedMerchantStatusTransitionedXml(String id) {
return node("connected-merchant-status-transitioned",
node("oauth-application-client-id", "oauth_application_client_id"),
node("merchant-public-id", id),
node("status", "new_status")
);
}
private String connectedMerchantPayPalStatusChangedXml(String id) {
return node("connected-merchant-paypal-status-changed",
node("oauth-application-client-id", "oauth_application_client_id"),
node("merchant-public-id", id),
node("action", "link")
);
}
private String accountUpdaterDailyReportXml(String id) {
return node("account-updater-daily-report",
node("report-url", "link-to-csv-report"),
node("report-date", TYPE_DATE, "2016-01-14")
);
}
private String checkXml() {
return node("check", TYPE_BOOLEAN, "true");
}
private static String node(String name, String... contents) {
return node(name, null, contents);
}
private static String node(String name, String[][] attributes, String... contents) {
StringBuilder buffer = new StringBuilder();
buffer.append('<').append(name);
if (attributes != null) {
for (String[] pair : attributes) {
buffer.append(" ");
buffer.append(pair[0]).append("=\"").append(pair[1]).append("\"");
}
}
buffer.append('>');
for (String content : contents) {
buffer.append(content);
}
buffer.append("</").append(name).append('>');
return buffer.toString();
}
}