Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.stripe.net.LiveStripeResponseGetter Maven / Gradle / Ivy
// Generated by delombok at Fri Dec 20 23:44:58 CET 2019
package com.stripe.net;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import com.stripe.Stripe;
import com.stripe.exception.ApiConnectionException;
import com.stripe.exception.ApiException;
import com.stripe.exception.AuthenticationException;
import com.stripe.exception.CardException;
import com.stripe.exception.IdempotencyException;
import com.stripe.exception.InvalidRequestException;
import com.stripe.exception.PermissionException;
import com.stripe.exception.RateLimitException;
import com.stripe.exception.StripeException;
import com.stripe.exception.oauth.InvalidClientException;
import com.stripe.exception.oauth.InvalidGrantException;
import com.stripe.exception.oauth.InvalidScopeException;
import com.stripe.exception.oauth.OAuthException;
import com.stripe.exception.oauth.UnsupportedGrantTypeException;
import com.stripe.exception.oauth.UnsupportedResponseTypeException;
import com.stripe.model.StripeError;
import com.stripe.model.StripeObject;
import com.stripe.model.oauth.OAuthError;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.Authenticator;
import java.net.HttpURLConnection;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.net.URLStreamHandler;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.ConcurrentLinkedQueue;
public class LiveStripeResponseGetter implements StripeResponseGetter {
private static final String DNS_CACHE_TTL_PROPERTY_NAME = "networkaddress.cache.ttl";
private static final int MAX_REQUEST_METRICS_BUFFER_SIZE = 100;
private static final class Parameter {
public final String key;
public final String value;
public Parameter(String key, String value) {
this.key = key;
this.value = value;
}
}
/*
* Set this property to override your environment's default
* URLStreamHandler; Settings the property should not be needed in most
* environments.
*/
private static final String CUSTOM_URL_STREAM_HANDLER_PROPERTY_NAME = "com.stripe.net.customURLStreamHandler";
@Override
public T request(ApiResource.RequestMethod method, String url, Map params, Class clazz, ApiResource.RequestType type, RequestOptions options) throws StripeException {
return staticRequest(method, url, params, clazz, type, options);
}
@Override
public T oauthRequest(ApiResource.RequestMethod method, String url, Map params, Class clazz, ApiResource.RequestType type, RequestOptions options) throws StripeException {
return staticOAuthRequest(method, url, params, clazz, type, options);
}
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);
headers.put("Accept-Charset", ApiResource.CHARSET);
headers.put("Accept", "application/json");
headers.put("Authorization", String.format("Bearer %s", options.getApiKey()));
// debug headers
String[] propertyNames = {"os.name", "os.version", "os.arch", "java.version", "java.vendor", "java.vm.version", "java.vm.vendor"};
Map propertyMap = new HashMap<>();
for (String propertyName : propertyNames) {
propertyMap.put(propertyName, System.getProperty(propertyName));
}
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));
// for some APIs, the client is making request on behalf of other clients
// (like mobile for ephemeral key), so Stripe-Version is override instead of the pinned value.
if (options.getStripeVersionOverride() != null) {
headers.put("Stripe-Version", options.getStripeVersionOverride());
} else if (options.getStripeVersion() != null) {
headers.put("Stripe-Version", options.getStripeVersion());
} else {
throw new IllegalStateException("Either `stripeVersion`, or `stripeVersionOverride` value must be set.");
}
if (options.getIdempotencyKey() != null) {
headers.put("Idempotency-Key", options.getIdempotencyKey());
}
if (options.getStripeAccount() != null) {
headers.put("Stripe-Account", options.getStripeAccount());
}
RequestMetrics lastRequestMetrics = prevRequestMetrics.poll();
if (Stripe.enableTelemetry && lastRequestMetrics != null) {
headers.put("X-Stripe-Client-Telemetry", ApiResource.GSON.toJson(lastRequestMetrics.payload()));
}
return headers;
}
@SuppressWarnings("unchecked")
private static HttpURLConnection createStripeConnection(String url, RequestOptions options) throws IOException {
URL stripeUrl;
String customUrlStreamHandlerClassName = System.getProperty(CUSTOM_URL_STREAM_HANDLER_PROPERTY_NAME, null);
if (customUrlStreamHandlerClassName != null) {
// instantiate the custom handler provided
try {
Class clazz = (Class) Class.forName(customUrlStreamHandlerClassName);
Constructor constructor = clazz.getConstructor();
URLStreamHandler customHandler = constructor.newInstance();
stripeUrl = new URL(null, url, customHandler);
} catch (ClassNotFoundException e) {
throw new IOException(e);
} catch (SecurityException e) {
throw new IOException(e);
} catch (NoSuchMethodException e) {
throw new IOException(e);
} catch (IllegalArgumentException e) {
throw new IOException(e);
} catch (InstantiationException e) {
throw new IOException(e);
} catch (IllegalAccessException e) {
throw new IOException(e);
} catch (InvocationTargetException e) {
throw new IOException(e);
}
} else {
stripeUrl = new URL(url);
}
HttpURLConnection conn;
if (Stripe.getConnectionProxy() != null) {
conn = (HttpURLConnection) stripeUrl.openConnection(Stripe.getConnectionProxy());
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return Stripe.getProxyCredential();
}
});
} else {
conn = (HttpURLConnection) stripeUrl.openConnection();
}
conn.setConnectTimeout(options.getConnectTimeout());
conn.setReadTimeout(options.getReadTimeout());
conn.setUseCaches(false);
for (Map.Entry header : getHeaders(options).entrySet()) {
conn.setRequestProperty(header.getKey(), header.getValue());
}
return conn;
}
private static String formatUrl(String url, String query) {
if (query == null || query.isEmpty()) {
return url;
} else {
// In some cases, URL can already contain a question mark (eg, upcoming invoice lines)
String separator = url.contains("?") ? "&" : "?";
return String.format("%s%s%s", url, separator, query);
}
}
private static HttpURLConnection createGetConnection(String url, String query, RequestOptions options) throws IOException {
String getUrl = formatUrl(url, query);
HttpURLConnection conn = createStripeConnection(getUrl, options);
conn.setRequestMethod("GET");
return conn;
}
private static HttpURLConnection createPostConnection(String url, String query, RequestOptions options) throws IOException {
HttpURLConnection conn = createStripeConnection(url, options);
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", String.format("application/x-www-form-urlencoded;charset=%s", ApiResource.CHARSET));
OutputStream output = conn.getOutputStream();
try {
output.write(query.getBytes(ApiResource.CHARSET));
return conn;
} finally {
if (java.util.Collections.singletonList(output).get(0) != null) {
output.close();
}
}
}
private static HttpURLConnection createDeleteConnection(String url, String query, RequestOptions options) throws IOException {
String deleteUrl = formatUrl(url, query);
HttpURLConnection conn = createStripeConnection(deleteUrl, options);
conn.setRequestMethod("DELETE");
return conn;
}
static String createQuery(Map params) throws InvalidRequestException {
StringBuilder queryStringBuffer = new StringBuilder();
List flatParams = flattenParams(params);
Iterator it = flatParams.iterator();
while (it.hasNext()) {
if (queryStringBuffer.length() > 0) {
queryStringBuffer.append("&");
}
Parameter param = it.next();
queryStringBuffer.append(urlEncodePair(param.key, param.value));
}
return queryStringBuffer.toString();
}
private static List flattenParams(Map params) throws InvalidRequestException {
return flattenParamsMap(params, null);
}
private static List flattenParamsCollection(Collection params, String keyPrefix) throws InvalidRequestException {
List flatParams = new ArrayList<>();
// Rely on the collection specific implementation for iterator to provide the order and the
// indices of elements in the array encoding
Iterator> it = params.iterator();
// Because application/x-www-form-urlencoded cannot represent an empty
// list, convention is to take the list parameter and just set it to an
// empty string. (e.g. A regular list might look like `a[0]=1&b[1]=2`.
// Emptying it would look like `a=`.)
if (params.isEmpty()) {
flatParams.add(new Parameter(keyPrefix, ""));
} else {
int index = 0;
while (it.hasNext()) {
String newPrefix = String.format("%s[%d]", keyPrefix, index++);
flatParams.addAll(flattenParamsValue(it.next(), newPrefix));
}
}
return flatParams;
}
private static List flattenParamsArray(Object[] params, String keyPrefix) throws InvalidRequestException {
List flatParams = new ArrayList<>();
// Because application/x-www-form-urlencoded cannot represent an empty
// list, convention is to take the list parameter and just set it to an
// empty string. (e.g. A regular list might look like `a[0]=1&b[1]=2`.
// Emptying it would look like `a=`.)
if (params.length == 0) {
flatParams.add(new Parameter(keyPrefix, ""));
} else {
for (int i = 0; i < params.length; i++) {
String newPrefix = String.format("%s[%d]", keyPrefix, i);
flatParams.addAll(flattenParamsValue(params[i], newPrefix));
}
}
return flatParams;
}
private static List flattenParamsMap(Map params, String keyPrefix) throws InvalidRequestException {
List flatParams = new ArrayList<>();
if (params == null) {
return flatParams;
}
for (Map.Entry entry : params.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
String newPrefix = key;
if (keyPrefix != null) {
newPrefix = String.format("%s[%s]", keyPrefix, key);
}
flatParams.addAll(flattenParamsValue(value, newPrefix));
}
return flatParams;
}
@SuppressWarnings("unchecked")
private static List flattenParamsValue(Object value, String keyPrefix) throws InvalidRequestException {
List flatParams;
if (value instanceof Map, ?>) {
flatParams = flattenParamsMap((Map) value, keyPrefix);
} else if (value instanceof Collection>) {
flatParams = flattenParamsCollection((Collection) value, keyPrefix);
} else if (value instanceof Object[]) {
flatParams = flattenParamsArray((Object[]) value, keyPrefix);
} else if ("".equals(value)) {
throw new InvalidRequestException("You cannot set \'" + keyPrefix + "\' to an empty string. We interpret empty strings as null in requests. You may set \'" + keyPrefix + "\' to null to delete the property.", keyPrefix, null, null, 0, null);
} else if (value == null) {
flatParams = new ArrayList<>();
flatParams.add(new Parameter(keyPrefix, ""));
} else if ((value instanceof File) || (value instanceof InputStream)) {
throw new InvalidRequestException(String.format("java.io.File or java.io.InputStream %s is not supported at \'%s\' parameter. Please check our API reference for the parameter type, or use the provided parameter class instead.", value, keyPrefix), keyPrefix, null, null, 0, null);
} else {
flatParams = new ArrayList<>();
flatParams.add(new Parameter(keyPrefix, value.toString()));
}
return flatParams;
}
private static ConcurrentLinkedQueue prevRequestMetrics = new ConcurrentLinkedQueue();
private static String getResponseBody(InputStream responseStream) throws IOException {
try (Scanner scanner = new Scanner(responseStream, ApiResource.CHARSET)) {
// \A is the beginning of the stream boundary
final String responseBody = scanner.useDelimiter("\\A").next();
responseStream.close();
return responseBody;
}
}
private static StripeResponse makeUrlConnectionRequest(ApiResource.RequestMethod method, String url, String query, RequestOptions options) throws ApiConnectionException {
HttpURLConnection conn = null;
try {
switch (method) {
case GET:
conn = createGetConnection(url, query, options);
break;
case POST:
conn = createPostConnection(url, query, options);
break;
case DELETE:
conn = createDeleteConnection(url, query, options);
break;
default:
throw new ApiConnectionException(String.format("Unrecognized HTTP method %s. This indicates a bug in the Stripe bindings. Please contact [email protected] for assistance.", method));
}
// trigger the request
int responseCode = conn.getResponseCode();
String responseBody;
Map> headers;
if (responseCode >= 200 && responseCode < 300) {
responseBody = getResponseBody(conn.getInputStream());
} else {
responseBody = getResponseBody(conn.getErrorStream());
}
headers = conn.getHeaderFields();
return new StripeResponse(responseCode, responseBody, headers);
} catch (IOException e) {
throw new ApiConnectionException(String.format("IOException during API request to Stripe (%s): %s Please check your internet connection and try again. If this problem persists,you should check Stripe\'s service status at https://twitter.com/stripestatus, or let us know at [email protected] .", Stripe.getApiBase(), e.getMessage()), e);
} finally {
if (conn != null) {
conn.disconnect();
}
}
}
private static StripeResponse rawRequest(ApiResource.RequestMethod method, String url, Map params, ApiResource.RequestType type, RequestOptions options) throws AuthenticationException, InvalidRequestException, ApiConnectionException, ApiException {
if (options == null) {
options = RequestOptions.getDefault();
}
String originalDnsCacheTtl = null;
Boolean allowedToSetTtl = true;
try {
originalDnsCacheTtl = java.security.Security.getProperty(DNS_CACHE_TTL_PROPERTY_NAME);
// Disable the DNS cache.
//
// Unfortunately the original author of this change didn't leave a
// comment explaining why it was required, but presumably the worry
// was that cache times were being expanded to a point that was
// problematic for proper resolution. Various JVM's have pretty
// good defaults though, so if the user hasn't touched this value,
// don't touch it either.
if (originalDnsCacheTtl != null) {
java.security.Security.setProperty(DNS_CACHE_TTL_PROPERTY_NAME, "0");
}
} catch (SecurityException se) {
allowedToSetTtl = false;
}
String apiKey = options.getApiKey();
if (apiKey == null || apiKey.trim().isEmpty()) {
throw new AuthenticationException("No API key provided. (HINT: set your API key using \'Stripe.apiKey = \'. You can generate API keys from the Stripe web interface. See https://stripe.com/api for details or email [email protected] if you have questions.", null, null, 0);
}
try {
StripeResponse response;
switch (type) {
case NORMAL:
response = getStripeResponse(method, url, params, options);
break;
case MULTIPART:
response = getMultipartStripeResponse(method, url, params, options);
break;
default:
throw new RuntimeException("Invalid APIResource request type. This indicates a bug in the Stripe bindings. Please contact [email protected] for assistance.");
}
return response;
} finally {
if (allowedToSetTtl && originalDnsCacheTtl != null) {
java.security.Security.setProperty(DNS_CACHE_TTL_PROPERTY_NAME, originalDnsCacheTtl);
}
}
}
private static T staticRequest(ApiResource.RequestMethod method, String url, Map params, Class clazz, ApiResource.RequestType type, RequestOptions options) throws StripeException {
long requestStartMs = System.currentTimeMillis();
StripeResponse response = rawRequest(method, url, params, type, options);
long requestDurationMs = System.currentTimeMillis() - requestStartMs;
int responseCode = response.code();
String responseBody = response.body();
String requestId = response.requestId();
if (responseCode < 200 || responseCode >= 300) {
handleApiError(response);
}
T resource = null;
try {
resource = ApiResource.GSON.fromJson(responseBody, clazz);
} catch (JsonSyntaxException e) {
raiseMalformedJsonError(responseBody, responseCode, requestId);
}
if (resource instanceof StripeObject) {
StripeObject obj = (StripeObject) resource;
obj.setLastResponse(response);
}
if (Stripe.enableTelemetry && prevRequestMetrics.size() < MAX_REQUEST_METRICS_BUFFER_SIZE) {
prevRequestMetrics.add(new RequestMetrics(requestId, requestDurationMs));
}
return resource;
}
private static T staticOAuthRequest(ApiResource.RequestMethod method, String url, Map params, Class clazz, ApiResource.RequestType type, RequestOptions options) throws StripeException {
StripeResponse response = rawRequest(method, url, params, type, options);
int responseCode = response.code();
String responseBody = response.body();
String requestId = response.requestId();
if (responseCode < 200 || responseCode >= 300) {
handleOAuthError(response);
}
T resource = null;
try {
resource = ApiResource.GSON.fromJson(responseBody, clazz);
} catch (JsonSyntaxException e) {
raiseMalformedJsonError(responseBody, responseCode, requestId);
}
return resource;
}
private static StripeResponse getStripeResponse(ApiResource.RequestMethod method, String url, Map params, RequestOptions options) throws InvalidRequestException, ApiConnectionException, ApiException {
String query = createQuery(params);
return makeUrlConnectionRequest(method, url, query, options);
}
private static StripeResponse getMultipartStripeResponse(ApiResource.RequestMethod method, String url, Map params, RequestOptions options) throws InvalidRequestException, ApiConnectionException, ApiException {
if (method != ApiResource.RequestMethod.POST) {
throw new InvalidRequestException("Multipart requests for HTTP methods other than POST are currently not supported.", null, null, null, 0, null);
}
java.net.HttpURLConnection conn = null;
try {
conn = createStripeConnection(url, options);
String boundary = MultipartProcessor.getBoundary();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", String.format("multipart/form-data; boundary=%s", boundary));
MultipartProcessor multipartProcessor = null;
try {
multipartProcessor = new MultipartProcessor(conn, boundary, ApiResource.CHARSET);
encodeMultipartParams(multipartProcessor, params);
} finally {
if (multipartProcessor != null) {
multipartProcessor.finish();
}
}
// trigger the request
int responseCode = conn.getResponseCode();
String responseBody;
Map> headers;
if (responseCode >= 200 && responseCode < 300) {
responseBody = getResponseBody(conn.getInputStream());
} else {
responseBody = getResponseBody(conn.getErrorStream());
}
headers = conn.getHeaderFields();
return new StripeResponse(responseCode, responseBody, headers);
} catch (IOException e) {
throw new ApiConnectionException(String.format("IOException during API request to Stripe (%s): %s Please check your internet connection and try again. If this problem persists,you should check Stripe\'s service status at https://twitter.com/stripestatus, or let us know at [email protected] .", Stripe.getApiBase(), e.getMessage()), e);
} finally {
if (conn != null) {
conn.disconnect();
}
}
}
/**
* Encode multipart params as a counter-part method to {@link this#createQuery(Map)} for encoding
* params for non-multipart request.
*
* @param multipartProcessor multi-part processor handling encoding of input stream and basic
* key-value forms.
* @param params parameter map that can contain file or input stream.
*/
static void encodeMultipartParams(MultipartProcessor multipartProcessor, Map params) throws InvalidRequestException, IOException {
for (Map.Entry entry : params.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (value instanceof File) {
File currentFile = (File) value;
if (!currentFile.exists()) {
throw new InvalidRequestException("File for key " + key + " must exist.", null, null, null, 0, null);
} else if (!currentFile.isFile()) {
throw new InvalidRequestException("File for key " + key + " must be a file and not a directory.", null, null, null, 0, null);
} else if (!currentFile.canRead()) {
throw new InvalidRequestException("Must have read permissions on file for key " + key + ".", null, null, null, 0, null);
}
multipartProcessor.addFileField(key, currentFile.getName(), new FileInputStream(currentFile));
} else if (value instanceof InputStream) {
InputStream inputStream = (InputStream) value;
try {
if (inputStream.available() == 0) {
throw new InvalidRequestException("Must have available bytes to read on InputStream for key " + key + ".", null, null, null, 0, null);
}
multipartProcessor.addFileField(key, "blob", inputStream);
} finally {
if (java.util.Collections.singletonList(inputStream).get(0) != null) {
inputStream.close();
}
}
} else {
List parameters = flattenParamsValue(value, key);
for (Parameter parameter : parameters) {
multipartProcessor.addFormField(parameter.key, parameter.value);
}
}
}
}
private static void raiseMalformedJsonError(String responseBody, int responseCode, String requestId) throws ApiException {
throw new ApiException(String.format("Invalid response object from API: %s. (HTTP response code was %d)", responseBody, responseCode), requestId, null, responseCode, null);
}
private static void handleApiError(StripeResponse response) throws StripeException {
StripeError error = null;
StripeException exception = null;
try {
JsonObject jsonObject = ApiResource.GSON.fromJson(response.body(), JsonObject.class).getAsJsonObject("error");
error = ApiResource.GSON.fromJson(jsonObject, StripeError.class);
} catch (JsonSyntaxException e) {
raiseMalformedJsonError(response.body(), response.code(), response.requestId());
}
if (error == null) {
raiseMalformedJsonError(response.body(), response.code(), response.requestId());
}
error.setLastResponse(response);
switch (response.code()) {
case 400:
case 404:
if ("idempotency_error".equals(error.getType())) {
exception = new IdempotencyException(error.getMessage(), response.requestId(), error.getCode(), response.code());
} else {
exception = new InvalidRequestException(error.getMessage(), error.getParam(), response.requestId(), error.getCode(), response.code(), null);
}
break;
case 401:
exception = new AuthenticationException(error.getMessage(), response.requestId(), error.getCode(), response.code());
break;
case 402:
exception = new CardException(error.getMessage(), response.requestId(), error.getCode(), error.getParam(), error.getDeclineCode(), error.getCharge(), response.code(), null);
break;
case 403:
exception = new PermissionException(error.getMessage(), response.requestId(), error.getCode(), response.code());
break;
case 429:
exception = new RateLimitException(error.getMessage(), error.getParam(), response.requestId(), error.getCode(), response.code(), null);
break;
default:
exception = new ApiException(error.getMessage(), response.requestId(), error.getCode(), response.code(), null);
break;
}
exception.setStripeError(error);
throw exception;
}
private static void handleOAuthError(StripeResponse response) throws StripeException {
OAuthError error = null;
StripeException exception = null;
try {
error = ApiResource.GSON.fromJson(response.body(), OAuthError.class);
} catch (JsonSyntaxException e) {
raiseMalformedJsonError(response.body(), response.code(), response.requestId());
}
if (error == null) {
raiseMalformedJsonError(response.body(), response.code(), response.requestId());
}
error.setLastResponse(response);
String code = error.getError();
String description = (error.getErrorDescription() != null) ? error.getErrorDescription() : code;
switch (code) {
case "invalid_client":
exception = new InvalidClientException(code, description, response.requestId(), response.code(), null);
break;
case "invalid_grant":
exception = new InvalidGrantException(code, description, response.requestId(), response.code(), null);
break;
case "invalid_request":
exception = new com.stripe.exception.oauth.InvalidRequestException(code, description, response.requestId(), response.code(), null);
break;
case "invalid_scope":
exception = new InvalidScopeException(code, description, response.requestId(), response.code(), null);
break;
case "unsupported_grant_type":
exception = new UnsupportedGrantTypeException(code, description, response.requestId(), response.code(), null);
break;
case "unsupported_response_type":
exception = new UnsupportedResponseTypeException(code, description, response.requestId(), response.code(), null);
break;
default:
exception = new ApiException(code, response.requestId(), null, response.code(), null);
break;
}
if (exception instanceof OAuthException) {
((OAuthException) exception).setOauthError(error);
}
throw exception;
}
}