diff --git a/README.md b/README.md index 08436771baa..0ea423fb3c2 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,16 @@ Please take care to set conservative read timeouts. Some API requests can take some time, and a short timeout increases the likelihood of a problem within our servers. +### Writing a plugin + +If you're writing a plugin that uses the library, we'd appreciate it if you +identified using `Stripe.setAppInfo()`: + + Stripe.setAppInfo("MyAwesomePlugin", "1.2.34", "https://myawesomeplugin.info"); + +This information is passed along when the library makes calls to the Stripe +API. + ## Testing You must have Gradle installed. To run the tests: diff --git a/src/main/java/com/stripe/Stripe.java b/src/main/java/com/stripe/Stripe.java index 328e5da4244..7e0794e164a 100644 --- a/src/main/java/com/stripe/Stripe.java +++ b/src/main/java/com/stripe/Stripe.java @@ -3,6 +3,9 @@ import java.net.PasswordAuthentication; import java.net.Proxy; +import java.util.HashMap; +import java.util.Map; + public abstract class Stripe { private final static int DEFAULT_CONNECT_TIMEOUT = 30 * 1000; private final static int DEFAULT_READ_TIMEOUT = 80 * 1000; @@ -27,6 +30,8 @@ public abstract class Stripe { private static volatile Proxy connectionProxy = null; private static volatile PasswordAuthentication proxyCredential = null; + private static volatile Map appInfo = null; + /** * (FOR TESTING ONLY) If you'd like your API requests to hit your own @@ -116,4 +121,26 @@ public static void setProxyCredential(final PasswordAuthentication auth) { public static PasswordAuthentication getProxyCredential() { return proxyCredential; } + + public static void setAppInfo(String name) { + setAppInfo(name, null, null); + } + + public static void setAppInfo(String name, String version) { + setAppInfo(name, version, null); + } + + public static void setAppInfo(String name, String version, String url) { + if (appInfo == null) { + appInfo = new HashMap(); + } + + appInfo.put("name", name); + appInfo.put("version", version); + appInfo.put("url", url); + } + + public static Map getAppInfo() { + return appInfo; + } } diff --git a/src/main/java/com/stripe/net/LiveStripeResponseGetter.java b/src/main/java/com/stripe/net/LiveStripeResponseGetter.java index 240a1e36010..b581dfbd542 100644 --- a/src/main/java/com/stripe/net/LiveStripeResponseGetter.java +++ b/src/main/java/com/stripe/net/LiveStripeResponseGetter.java @@ -89,13 +89,29 @@ private static String urlEncodePair(String k, String v) return String.format("%s=%s", APIResource.urlEncode(k), APIResource.urlEncode(v)); } + static String formatAppInfo(Map info) { + String str = info.get("name"); + if (info.get("version") != null) { + str += String.format("/%s", info.get("version")); + } + if (info.get("url") != null) { + str += String.format(" (%s)", info.get("url")); + } + return str; + } + static Map getHeaders(RequestOptions options) { Map headers = new HashMap(); + + String userAgent = String.format("Stripe/v1 JavaBindings/%s", Stripe.VERSION); + if (Stripe.getAppInfo() != null) { + userAgent += " " + formatAppInfo(Stripe.getAppInfo()); + } + headers.put("User-Agent", userAgent); + String apiVersion = options.getStripeVersion(); headers.put("Accept-Charset", APIResource.CHARSET); headers.put("Accept", "application/json"); - headers.put("User-Agent", - String.format("Stripe/v1 JavaBindings/%s", Stripe.VERSION)); headers.put("Authorization", String.format("Bearer %s", options.getApiKey())); @@ -110,6 +126,9 @@ static Map getHeaders(RequestOptions options) { propertyMap.put("bindings.version", Stripe.VERSION); propertyMap.put("lang", "Java"); propertyMap.put("publisher", "Stripe"); + if (Stripe.getAppInfo() != null) { + propertyMap.put("application", APIResource.GSON.toJson(Stripe.getAppInfo())); + } headers.put("X-Stripe-Client-User-Agent", APIResource.GSON.toJson(propertyMap)); if (apiVersion != null) { headers.put("Stripe-Version", apiVersion); diff --git a/src/test/java/com/stripe/net/LiveStripeResponseGetterTest.java b/src/test/java/com/stripe/net/LiveStripeResponseGetterTest.java index 8117eb7e674..b59992f324a 100644 --- a/src/test/java/com/stripe/net/LiveStripeResponseGetterTest.java +++ b/src/test/java/com/stripe/net/LiveStripeResponseGetterTest.java @@ -1,7 +1,12 @@ package com.stripe.net; +import com.stripe.Stripe; import com.stripe.exception.StripeException; import com.stripe.net.LiveStripeResponseGetter; +import com.stripe.net.RequestOptions; +import com.stripe.net.RequestOptions.RequestOptionsBuilder; + +import com.google.gson.Gson; import java.io.UnsupportedEncodingException; import java.util.HashMap; @@ -14,6 +19,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; public class LiveStripeResponseGetterTest { LiveStripeResponseGetter srg; @@ -126,4 +132,28 @@ public void testCorrectAdditionalOwners() throws StripeException, UnsupportedEnc assertEquals(encode("legal_entity[additional_owners][0][first_name]=Stripe"), LiveStripeResponseGetter.createQuery(params)); } + + @Test + public void testAppInfo() { + RequestOptions options = (new RequestOptionsBuilder()).setApiKey("sk_foobar").build(); + + Stripe.setAppInfo("MyAwesomePlugin", "1.2.34", "https://myawesomeplugin.info"); + + Map headers = srg.getHeaders(options); + + String expectedUserAgent = String.format( + "Stripe/v1 JavaBindings/%s MyAwesomePlugin/1.2.34 (https://myawesomeplugin.info)", + Stripe.VERSION); + assertEquals(expectedUserAgent, headers.get("User-Agent")); + + Gson gson = new Gson(); + + Map uaMap = gson.fromJson(headers.get("X-Stripe-Client-User-Agent"), Map.class); + assertNotNull(uaMap.get("application")); + + Map appMap = gson.fromJson(uaMap.get("application"), Map.class); + assertEquals("MyAwesomePlugin", appMap.get("name")); + assertEquals("1.2.34", appMap.get("version")); + assertEquals("https://myawesomeplugin.info", appMap.get("url")); + } }