package com.braintreegateway.testhelpers; import com.braintreegateway.*; import com.braintreegateway.Transaction.Status; import com.braintreegateway.exceptions.UnexpectedException; import com.braintreegateway.util.Sha1Hasher; import com.braintreegateway.util.Http; import com.braintreegateway.util.NodeWrapper; import com.braintreegateway.util.QueryString; import com.braintreegateway.util.StringUtils; import com.braintreegateway.EuropeBankAccount.MandateType; import com.braintreegateway.testhelpers.MerchantAccountTestConstants; import com.braintreegateway.org.apache.commons.codec.binary.Base64; import org.junit.Ignore; import org.json.*; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.net.URLDecoder; import javax.net.ssl.SSLContext; import javax.net.ssl.HttpsURLConnection; import java.nio.charset.Charset; import java.util.Calendar; import java.util.Comparator; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.*; import java.io.UnsupportedEncodingException; import java.io.BufferedReader; import java.io.InputStreamReader; import static org.junit.Assert.*; @Ignore("Testing utility class") public abstract class TestHelper { public static final class CompareModificationsById implements Comparator<Modification> { public int compare(Modification left, Modification right) { return left.getId().compareTo(right.getId()); } } public static void assertDatesEqual(Calendar first, Calendar second) { if (first == null && second != null) { throw new AssertionError("dates are not equal. first is null, second is not"); } else if (first != null && second == null) { throw new AssertionError("dates are not equal. second is null, first is not"); } boolean yearsNotEqual = first.get(Calendar.YEAR) != second.get(Calendar.YEAR); boolean monthsNotEqual = first.get(Calendar.MONTH) != second.get(Calendar.MONTH); boolean daysNotEqual = first.get(Calendar.DAY_OF_MONTH) != second.get(Calendar.DAY_OF_MONTH); if (yearsNotEqual || monthsNotEqual || daysNotEqual) { StringBuffer buffer = new StringBuffer("dates are not equal. "); if (yearsNotEqual) { buffer.append("years (" + first.get(Calendar.YEAR) + ", " + second.get(Calendar.YEAR) + ") not equal."); } if (monthsNotEqual) { buffer.append("months (" + first.get(Calendar.MONTH) + ", " + second.get(Calendar.MONTH) + ") not equal."); } if (daysNotEqual) { buffer.append("days (" + first.get(Calendar.DAY_OF_MONTH) + ", " + second.get(Calendar.DAY_OF_MONTH) + ") not equal."); } throw new AssertionError(buffer.toString()); } } public static void assertIncludes(String expected, String all) { assertTrue("Expected:\n" + all + "\nto include:\n" + expected, all.indexOf(expected) >= 0); } public static void assertValidTrData(Configuration configuration, String trData) { String[] dataSections = trData.split("\\|"); String trHash = dataSections[0]; String trContent = dataSections[1]; assertEquals(trHash, new Sha1Hasher().hmacHash(configuration.getPrivateKey(), trContent)); } public static boolean listIncludes(List<? extends Object> list, Object expectedItem) { for (Object item : list) { if (item.equals(expectedItem)) { return true; } } return false; } public static boolean includesSubscription(ResourceCollection<Subscription> collection, Subscription item) { for (Subscription subscription : collection) { if (subscription.getId().equals(item.getId())) { return true; } } return false; } public static boolean includesStatus(ResourceCollection<Transaction> collection, Status status) { for (Transaction transaction : collection) { if (transaction.getStatus().equals(status)) { return true; } } return false; } public static Result<Transaction> settle(BraintreeGateway gateway, String transactionId) { return gateway.testing().settle(transactionId); } public static Result<Transaction> settlement_confirm(BraintreeGateway gateway, String transactionId) { return gateway.testing().settlementConfirm(transactionId); } public static Result<Transaction> settlement_decline(BraintreeGateway gateway, String transactionId) { return gateway.testing().settlementDecline(transactionId); } public static void escrow(BraintreeGateway gateway, String transactionId) { NodeWrapper response = new Http(gateway.getConfiguration()).put(gateway.getConfiguration().getMerchantPath() + "/transactions/" + transactionId + "/escrow"); assertTrue(response.isSuccess()); } public static String createTest3DS(BraintreeGateway gateway, String merchantAccountId, ThreeDSecureRequestForTests request) { String url = gateway.getConfiguration().getMerchantPath() + "/three_d_secure/create_verification/" + merchantAccountId; NodeWrapper response = new Http(gateway.getConfiguration()).post(url, request); assertTrue(response.isSuccess()); String token = response.findString("three-d-secure-token"); assertNotNull(token); return token; } public static String simulateFormPostForTR(BraintreeGateway gateway, Request trParams, Request request, String postUrl) { String response = ""; try { String trData = gateway.transparentRedirect().trData(trParams, "http://example.com"); StringBuilder postData = new StringBuilder("tr_data=") .append(URLEncoder.encode(trData, "UTF-8")) .append("&") .append(request.toQueryString()); URL url = new URL(postUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setInstanceFollowRedirects(false); connection.setDoOutput(true); connection.setRequestMethod("POST"); connection.addRequestProperty("Accept", "application/xml"); connection.addRequestProperty("Content-Type", "application/x-www-form-urlencoded"); connection.getOutputStream().write(postData.toString().getBytes("UTF-8")); connection.getOutputStream().close(); if (connection.getResponseCode() == 422) { connection.getErrorStream(); } else { connection.getInputStream(); } response = new URL(connection.getHeaderField("Location")).getQuery(); } catch (IOException e) { throw new UnexpectedException(e.getMessage()); } return response; } public static String generateUnlockedNonce(BraintreeGateway gateway, String customerId, String creditCardNumber) { ClientTokenRequest request = new ClientTokenRequest(); if (customerId != null) { request = request.customerId(customerId); } String encodedClientToken = gateway.clientToken().generate(request); String clientToken = TestHelper.decodeClientToken(encodedClientToken); String authorizationFingerprint = extractParamFromJson("authorizationFingerprint", clientToken); Configuration configuration = gateway.getConfiguration(); String url = configuration.getBaseURL() + configuration.getMerchantPath() + "/client_api/nonces.json"; QueryString payload = new QueryString(); payload.append("authorization_fingerprint", authorizationFingerprint). append("shared_customer_identifier_type", "testing"). append("shared_customer_identifier", "test-identifier"). append("credit_card[number]", creditCardNumber). append("credit_card[expiration_month]", "11"). append("share", "true"). append("credit_card[expiration_year]", "2099"); String responseBody; String nonce = ""; try { responseBody = HttpHelper.post(url, payload.toString()); nonce = extractParamFromJson("nonce", responseBody); } catch (Exception e) { throw new RuntimeException(e); } return nonce; } public static String generateThreeDSecureNonce(BraintreeGateway gateway, CreditCardRequest creditCardRequest) { String merchantAccountId = MerchantAccountTestConstants.THREE_D_SECURE_MERCHANT_ACCOUNT_ID; String url = gateway.getConfiguration().getMerchantPath() + "/three_d_secure/create_nonce/" + merchantAccountId; NodeWrapper response = new Http(gateway.getConfiguration()).post(url, creditCardRequest); assertTrue(response.isSuccess()); String nonce = response.findString("nonce"); assertNotNull(nonce); return nonce; } public static String decodeClientToken(String rawClientToken) { String decodedClientToken = new String(Base64.decodeBase64(rawClientToken), Charset.forName("UTF-8")); return decodedClientToken.replace("\\u0026", "&"); } public static String generateOneTimePayPalNonce(BraintreeGateway gateway) { String encodedClientToken = gateway.clientToken().generate(); String clientToken = TestHelper.decodeClientToken(encodedClientToken); String authorizationFingerprint = extractParamFromJson("authorizationFingerprint", clientToken); Configuration configuration = gateway.getConfiguration(); String url = configuration.getBaseURL() + configuration.getMerchantPath() + "/client_api/v1/payment_methods/paypal_accounts"; QueryString payload = new QueryString(); payload.append("authorization_fingerprint", authorizationFingerprint). append("shared_customer_identifier_type", "testing"). append("shared_customer_identifier", "test-identifier"). append("paypal_account[access_token]", "access"). append("paypal_account[options][validate]", "false"); String responseBody; String nonce = ""; try { responseBody = HttpHelper.post(url, payload.toString()); nonce = extractParamFromJson("nonce", responseBody); } catch (Exception e) { throw new RuntimeException(e); } return nonce; } public static String generateOrderPaymentPayPalNonce(BraintreeGateway gateway) { QueryString payload = new QueryString(); payload.append("paypal_account[intent]", "order"); payload.append("paypal_account[payment_token]", "fake_payment_token"); payload.append("paypal_account[payer_id]", "fake_payer_id"); return generatePayPalNonce(gateway, payload); } public static String generateNonceForCreditCard(BraintreeGateway gateway, CreditCardRequest creditCardRequest, String customerId, boolean validate) { ClientTokenRequest clientTokenRequest = new ClientTokenRequest(). customerId(customerId); String encodedClientToken = gateway.clientToken().generate(clientTokenRequest); String clientToken = TestHelper.decodeClientToken(encodedClientToken); String authorizationFingerprint = extractParamFromJson("authorizationFingerprint", clientToken); Configuration configuration = gateway.getConfiguration(); String url = configuration.getBaseURL() + configuration.getMerchantPath() + "/client_api/v1/payment_methods/credit_cards"; QueryString payload = new QueryString(); payload.append("authorization_fingerprint", authorizationFingerprint). append("shared_customer_identifier_type", "testing"). append("shared_customer_identifier", "fake_identifier"). append("credit_card[options][validate]", new Boolean(validate).toString()); String responseBody; String nonce = ""; try { String payloadString = payload.toString(); payloadString += "&" + creditCardRequest.toQueryString(); responseBody = HttpHelper.post(url, payloadString); nonce = extractParamFromJson("nonce", responseBody); } catch (Exception e) { throw new RuntimeException(e); } return nonce; } public static String generateEuropeBankAccountNonce(BraintreeGateway gateway, Customer customer) { SEPAClientTokenRequest request = new SEPAClientTokenRequest(); request.customerId(customer.getId()); request.mandateType(EuropeBankAccount.MandateType.BUSINESS); request.mandateAcceptanceLocation("Rostock, Germany"); String encodedClientToken = gateway.clientToken().generate(request); String clientToken = TestHelper.decodeClientToken(encodedClientToken); String authorizationFingerprint = extractParamFromJson("authorizationFingerprint", clientToken); Configuration configuration = gateway.getConfiguration(); String url = configuration.getBaseURL() + configuration.getMerchantPath() + "/client_api/v1/sepa_mandates"; QueryString payload = new QueryString(); payload.append("authorization_fingerprint", authorizationFingerprint) .append("sepa_mandate[locale]", "de-DE") .append("sepa_mandate[bic]", "DEUTDEFF") .append("sepa_mandate[iban]", "DE89370400440532013000") .append("sepa_mandate[accountHolderName]", "Bob Holder") .append("sepa_mandate[billingAddress][streetAddress]", "123 Currywurst Way") .append("sepa_mandate[billingAddress][extendedAddress]", "Lager Suite") .append("sepa_mandate[billingAddress][firstName]", "Wilhelm") .append("sepa_mandate[billingAddress][lastName]", "Dix") .append("sepa_mandate[billingAddress][locality]", "Frankfurt") .append("sepa_mandate[billingAddress][postalCode]", "60001") .append("sepa_mandate[billingAddress][countryCodeAlpha2]", "DE") .append("sepa_mandate[billingAddress][region]", "Hesse"); String responseBody; String nonce = ""; try { responseBody = HttpHelper.post(url, payload.toString()); nonce = extractParamFromJson("nonce", responseBody); } catch (Exception e) { throw new RuntimeException(e); } return nonce; } public static String getNonceForPayPalAccount(BraintreeGateway gateway, String consentCode) { return getNonceForPayPalAccount(gateway, consentCode, null); } public static String getNonceForPayPalAccount(BraintreeGateway gateway, String consentCode, String token) { String encodedClientToken = gateway.clientToken().generate(); String clientToken = TestHelper.decodeClientToken(encodedClientToken); String authorizationFingerprint = extractParamFromJson("authorizationFingerprint", clientToken); Configuration configuration = gateway.getConfiguration(); String url = configuration.getBaseURL() + configuration.getMerchantPath() + "/client_api/v1/payment_methods/paypal_accounts"; QueryString payload = new QueryString(); payload.append("authorization_fingerprint", authorizationFingerprint). append("shared_customer_identifier_type", "testing"). append("shared_customer_identifier", "test-identifier"). append("paypal_account[consent_code]", consentCode). append("paypal_account[token]", token). append("paypal_account[options][validate]", "false"); String responseBody; String nonce = ""; try { responseBody = HttpHelper.post(url, payload.toString()); nonce = extractParamFromJson("nonce", responseBody); } catch (Exception e) { throw new RuntimeException(e); } return nonce; } public static String generateFuturePaymentPayPalNonce(BraintreeGateway gateway) { QueryString payload = new QueryString(); payload.append("paypal_account[consent_code]", "consent"); return generatePayPalNonce(gateway, payload); } public static String generateBillingAgreementPayPalNonce(BraintreeGateway gateway) { QueryString payload = new QueryString(); payload.append("paypal_account[billing_agreement_token]", "fake_ba_token"); return generatePayPalNonce(gateway, payload); } private static String generatePayPalNonce(BraintreeGateway gateway, QueryString payload) { String encodedClientToken = gateway.clientToken().generate(); String clientToken = TestHelper.decodeClientToken(encodedClientToken); String authorizationFingerprint = extractParamFromJson("authorizationFingerprint", clientToken); Configuration configuration = gateway.getConfiguration(); String url = configuration.getBaseURL() + configuration.getMerchantPath() + "/client_api/v1/payment_methods/paypal_accounts"; payload.append("authorization_fingerprint", authorizationFingerprint). append("shared_customer_identifier_type", "testing"). append("shared_customer_identifier", "test-identifier"). append("paypal_account[options][validate]", "false"); String responseBody; String nonce = ""; try { responseBody = HttpHelper.post(url, payload.toString()); nonce = extractParamFromJson("nonce", responseBody); } catch (Exception e) { throw new RuntimeException(e); } return nonce; } public static String generateUnlockedNonce(BraintreeGateway gateway) { return generateUnlockedNonce(gateway, null, "4111111111111111"); } public static String extractParamFromJson(String keyName, String json) { String regex = "\"" + keyName + "\":\\s*\"([^\"]+)\""; Pattern keyPattern = Pattern.compile(regex); Matcher m = keyPattern.matcher(json); String value = ""; if (m.find()) { value = m.group(1); } return value; } public static int extractIntParamFromJson(String keyName, String json) { String regex = "\"" + keyName + "\":\\s*(\\d+)"; Pattern keyPattern = Pattern.compile(regex); Matcher m = keyPattern.matcher(json); int value = 0; if (m.find()) { value = Integer.parseInt(m.group(1)); } return value; } public static final class OAuthGrantRequest extends Request { private String scope; private String merchantId; public OAuthGrantRequest scope(String scope) { this.scope = scope; return this; } public OAuthGrantRequest merchantId(String merchantId) { this.merchantId = merchantId; return this; } @Override public String toXML() { return new RequestBuilder("grant"). addElement("scope", scope). addElement("merchant_public_id", merchantId). toXML(); } } public static String createOAuthGrant(BraintreeGateway gateway, String merchantId, String scope) { Http http = new Http(gateway.getConfiguration()); OAuthGrantRequest request = new OAuthGrantRequest(). scope(scope). merchantId(merchantId); NodeWrapper node = http.post("/oauth_testing/grants", request); return node.findString("code"); } /* http://stackoverflow.com/questions/13592236/parse-the-uri-string-into-name-value-collection-in-java */ public static Map<String, String> splitQuery(URL url) throws UnsupportedEncodingException { Map<String, String> queryPairs = new LinkedHashMap<String, String>(); String query = url.getQuery(); String[] pairs = query.split("&"); for (String pair : pairs) { int idx = pair.indexOf("="); String key = URLDecoder.decode(pair.substring(0, idx), "UTF-8"); if (queryPairs.get(key) == null) { queryPairs.put(key, URLDecoder.decode(pair.substring(idx + 1), "UTF-8")); } else { queryPairs.put(key, queryPairs.get(key) + ", " + URLDecoder.decode(pair.substring(idx + 1), "UTF-8")); } } return queryPairs; } public static String generateValidUsBankAccountNonce(BraintreeGateway gateway) { String encodedClientToken = gateway.clientToken().generate(); String clientToken = TestHelper.decodeClientToken(encodedClientToken); String payload = new StringBuilder() .append("{\n") .append("\"type\": \"us_bank_account\",\n") .append("\"billing_address\": {\n") .append("\"street_address\": \"123 Ave\",\n") .append("\"region\": \"CA\",\n") .append("\"locality\": \"San Francisco\",\n") .append("\"postal_code\": \"94112\"\n") .append("},\n") .append("\"account_type\": \"checking\",\n") .append("\"routing_number\": \"021000021\",\n") .append("\"account_number\": \"567891234\",\n") .append("\"account_holder_name\": \"Dan Schulman\",\n") .append("\"ach_mandate\": {\n") .append("\"text\": \"cl mandate text\"\n") .append("}\n") .append("}") .toString(); String nonce = ""; try { JSONObject json = new JSONObject(clientToken); URL url = new URL(json.getJSONObject("braintree_api").getString("url") + "/tokens"); String token = json.getJSONObject("braintree_api").getString("access_token"); SSLContext sc = SSLContext.getInstance("TLSv1.1"); sc.init(null, null, null); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setSSLSocketFactory(sc.getSocketFactory()); connection.setRequestMethod("POST"); connection.addRequestProperty("Content-Type", "application/json"); connection.addRequestProperty("Braintree-Version", "2015-11-01"); connection.addRequestProperty("Authorization", "Bearer " + token); connection.setDoOutput(true); connection.getOutputStream().write(payload.getBytes("UTF-8")); connection.getOutputStream().close(); InputStream responseStream = connection.getInputStream(); String body = StringUtils.inputStreamToString(responseStream); responseStream.close(); JSONObject responseJson = new JSONObject(body); nonce = responseJson.getJSONObject("data").getString("id"); } catch (Exception e) { throw new RuntimeException(e); } return nonce; } public static String generateInvalidUsBankAccountNonce() { String valid_characters = "bcdfghjkmnpqrstvwxyz23456789"; String token = "tokenusbankacct"; for(int i=0; i < 4; i++) { token += '_'; for(int j=0; j<6; j++) { Integer pick = new Random().nextInt(valid_characters.length()); token += valid_characters.charAt(pick); } } return token + "_xxx"; } public static String generateValidIdealPaymentId(BraintreeGateway gateway, BigDecimal amount) { String encodedClientToken = gateway.clientToken().generate( new ClientTokenRequest() .merchantAccountId("ideal_merchant_account")); String clientToken = TestHelper.decodeClientToken(encodedClientToken); String authorizationFingerprint = extractParamFromJson("authorizationFingerprint", clientToken); Configuration configuration = gateway.getConfiguration(); String configurationUrl = new StringBuilder() .append(configuration.getBaseURL()) .append(configuration.getMerchantPath()) .append("/client_api/v1/configuration") .append("?") .append(new QueryString() .append("authorizationFingerprint", authorizationFingerprint) .append("configVersion", "3") .toString()) .toString(); String routeId = ""; try { String responseBody = HttpHelper.get(configurationUrl); routeId = extractParamFromJson("routeId", responseBody); } catch (Exception e) { throw new RuntimeException(e); } String payload = new StringBuilder() .append("{\n") .append("\"issuer\": \"RABON2LU\",\n") .append("\"order_id\": \"ABC123\",\n") .append("\"currency\": \"EUR\",\n") .append("\"redirect_url\": \"https://braintree-api.com\",\n") .append("\"route_id\": \"" + routeId + "\",\n") .append("\"amount\": \"") .append(amount.toString()) .append("\"") .append("}") .toString(); String idealPaymentId = ""; try { JSONObject json = new JSONObject(clientToken); URL url = new URL(json.getJSONObject("braintree_api").getString("url") + "/ideal-payments"); String token = json.getJSONObject("braintree_api").getString("access_token"); SSLContext sc = SSLContext.getInstance("TLSv1.1"); sc.init(null, null, null); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setSSLSocketFactory(sc.getSocketFactory()); connection.setRequestMethod("POST"); connection.addRequestProperty("Content-Type", "application/json"); connection.addRequestProperty("Braintree-Version", "2015-11-01"); connection.addRequestProperty("Authorization", "Bearer " + token); connection.setDoOutput(true); connection.getOutputStream().write(payload.getBytes("UTF-8")); connection.getOutputStream().close(); InputStream responseStream = connection.getInputStream(); String body = StringUtils.inputStreamToString(responseStream); responseStream.close(); JSONObject responseJson = new JSONObject(body); idealPaymentId = responseJson.getJSONObject("data").getString("id"); } catch (Exception e) { throw new RuntimeException(e); } return idealPaymentId; } public static String generateValidIdealPaymentId(BraintreeGateway gateway) { return generateValidIdealPaymentId(gateway, SandboxValues.TransactionAmount.AUTHORIZE.amount); } public static String generateInvalidIdealPaymentId() { String valid_characters = "bcdfghjkmnpqrstvwxyz23456789"; String token = "idealpayment"; for(int i=0; i < 4; i++) { token += '_'; for(int j=0; j<6; j++) { Integer pick = new Random().nextInt(valid_characters.length()); token += valid_characters.charAt(pick); } } return token + "_xxx"; } }