com.paypal.base.rest.OAuthTokenCredential Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rest-api-sdk Show documentation
Show all versions of rest-api-sdk Show documentation
PayPal SDK for integrating with the REST APIs
package com.paypal.base.rest;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.paypal.base.*;
import com.paypal.base.codec.binary.Base64;
import com.paypal.base.exception.ClientActionRequiredException;
import com.paypal.base.exception.HttpErrorException;
import com.paypal.base.sdk.info.SDKVersionImpl;
import com.paypal.base.util.UserAgentHeader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* OAuthTokenCredential is used for generation of OAuth Token used by PayPal
* REST API service. ClientID and ClientSecret are required by the class to
* generate OAuth Token, the resulting token is of the form "Bearer xxxxxx". The
* class has two constructors, one of it taking an additional configuration map
* used for dynamic configuration. When using the constructor with out
* configuration map the endpoint is fetched from the configuration that is used
* during initialization. See {@link PayPalResource} for configuring the system.
* When using a configuration map the class expects an entry by the name
* "oauth.EndPoint" or "service.EndPoint" to retrieve the value of the endpoint
* for the OAuth Service. If either are not present the configuration should
* have a entry by the name "mode" with values sandbox or live wherein the
* corresponding endpoints are default to PayPal endpoints.
*/
public final class OAuthTokenCredential {
private static final Logger log = LoggerFactory.getLogger(OAuthTokenCredential.class);
private static Map ACCESS_TOKENS = new ConcurrentHashMap();
/**
* OAuth URI path parameter
*/
private static String OAUTH_TOKEN_PATH = "/v1/oauth2/token";
/**
* Client ID for OAuth
*/
private String clientID;
/**
* Client Secret for OAuth
*/
private String clientSecret;
/**
* Headers
*/
private Map headers = new HashMap();
private String refreshToken;
/**
* @deprecated This field is only used when OAuthTokenCredential is initialized with access token only.
* If the access token is directly passed in as a part of constructor, we will always pass this back.
*/
@Deprecated
private final AccessToken __accessToken;
/**
* Map used for dynamic configuration
*/
private Map configurationMap;
/**
* {@link SDKVersion} instance
*/
private SDKVersion sdkVersion;
/**
* Sets the URI path for the OAuth Token service. If not set it defaults to
* "/v1/oauth2/token"
*
* @param oauthTokenPath
* the URI part to set
*/
public static void setOAUTH_TOKEN_PATH(String oauthTokenPath) {
OAUTH_TOKEN_PATH = oauthTokenPath;
}
/**
* Constructor that takes in Access Token. Only used internally. Please do not use for external integrations.
*
* @param accessToken
*/
@Deprecated
OAuthTokenCredential(String accessToken) {
__accessToken = new AccessToken(accessToken, 1);
}
/**
* Pass clientId and secret to OAuthTokenCredential.
*
* @param clientID
* Client ID for the OAuth
* @param clientSecret
* Client Secret for OAuth
*/
public OAuthTokenCredential(String clientID, String clientSecret) {
super();
this.clientID = clientID;
this.clientSecret = clientSecret;
this.configurationMap = SDKUtil.combineDefaultMap(ConfigManager.getInstance().getConfigurationMap());
this.sdkVersion = new SDKVersionImpl();
this.__accessToken = null;
}
/**
* Configuration Constructor for dynamic configuration
*
* @param clientID
* Client ID for the OAuth
* @param clientSecret
* Client Secret for OAuth
* @param configurationMap
* Dynamic configuration map which should have an entry for
* 'oauth.EndPoint' or 'service.EndPoint'. If either are not
* present then there should be entry for 'mode' with values
* sandbox/live, wherein PayPals endpoints are used.
*/
public OAuthTokenCredential(String clientID, String clientSecret, Map configurationMap) {
super();
this.clientID = clientID;
this.clientSecret = clientSecret;
this.configurationMap = SDKUtil.combineDefaultMap(configurationMap);
this.sdkVersion = new SDKVersionImpl();
this.__accessToken = null;
}
/**
* Sets refresh token to be used for third party OAuth operations. This is commonly used for
* third party invoicing and future payments.
* This method is for internal use only. Please use {@link APIContext#setRefreshToken(String)} for your integration needs.
*
* @param refreshToken
* @return {@link OAuthTokenCredential}
*/
synchronized OAuthTokenCredential setRefreshToken(String refreshToken) {
if (!this.hasCredentials()) {
throw new IllegalArgumentException("ClientID and Secret are required. Please use OAuthTokenCredential(String clientID, String clientSecret)");
}
this.refreshToken = refreshToken;
return this;
}
/**
* Checks if clientID and secret are set.
*
* @return {@link Boolean}
*/
public boolean hasCredentials() {
return (this.clientID != null) && (this.clientSecret != null);
}
/**
* Sets Headers for every calls.
*
* @param headers
* @return {@link OAuthTokenCredential}
*/
public OAuthTokenCredential setHeaders(Map headers) {
this.headers = headers;
return this;
}
/**
* Adds headers.
*
* @param headers
* @return {@link OAuthTokenCredential}
*/
public OAuthTokenCredential addHeaders(Map headers) {
this.headers.putAll(headers);
return this;
}
/**
* Adds header to existing list of headers.
*
* @param key
* @param value
* @return {@link OAuthTokenCredential}
*/
public OAuthTokenCredential addHeader(String key, String value) {
this.headers.put(key, value);
return this;
}
/**
* Returns the list of headers
*
* @return {@link Map} of headers
*/
public Map getHeaders() {
return this.headers;
}
/**
* Returns the header value
*
* @return {@link String} value of header
*/
public String getHeader(String key) {
return this.headers.get(key);
}
/**
* Computes Access Token by placing a call to OAuth server using ClientID
* and ClientSecret. The token is appended to the token type (Bearer).
*
* @return the accessToken
* @throws PayPalRESTException
*/
public String getAccessToken() throws PayPalRESTException {
if (__accessToken != null) {
return __accessToken.getAccessToken();
}
synchronized (ACCESS_TOKENS) {
if (isRegenerationRequired()) {
generateAccessToken();
}
return ACCESS_TOKENS.get(getCacheTokenKey()).getAccessToken();
}
}
/**
* Checks if the access token is expired or null.
*
* @return true if expired or null, and can be regenerated.
* false otherwise
*/
private boolean isRegenerationRequired() {
AccessToken token = ACCESS_TOKENS.get(getCacheTokenKey());
if (token == null) {
return hasCredentials();
} else {
return (token.getAccessToken() == null || (hasCredentials() && token.expiresIn() <= 0));
}
}
/**
* Computes Access Token by doing a Base64 encoding on the ClientID and
* ClientSecret. The token is appended to the String "Basic ".
*
* @return the accessToken
* @throws PayPalRESTException
*/
public String getAuthorizationHeader() throws PayPalRESTException {
String base64EncodedString = generateBase64String(clientID + ":" + clientSecret);
return "Basic " + base64EncodedString;
}
/**
* Returns clientID
*
* @return {@link String} containing clientID
*/
public String getClientID() {
return this.clientID;
}
/**
* Returns clientSecret
*
* @return {@link String} containing clientSecret
*/
public String getClientSecret() {
return this.clientSecret;
}
/**
* Specifies how long this token can be used for placing API calls. The
* remaining lifetime is given in seconds.
*
* @return remaining lifetime of this access token in seconds
*/
public long expiresIn() {
AccessToken token = ACCESS_TOKENS.get(getCacheTokenKey());
return token != null ? token.getExpires() : -1;
}
/**
* Adds configuration to list of configurations.
*
* @param key
* @param value
* @return {@link OAuthTokenCredential}
*/
public OAuthTokenCredential addConfiguration(String key, String value) {
if (this.configurationMap == null) {
this.configurationMap = new HashMap();
}
this.configurationMap.put(key, value);
return this;
}
/**
* Adds configurations to list of configurations.
* @param configurations
* @return {@link OAuthTokenCredential}
*/
public OAuthTokenCredential addConfigurations(Map configurations) {
if (this.configurationMap == null) {
this.configurationMap = new HashMap();
}
this.configurationMap.putAll(configurations);
return this;
}
/**
* Replaces existing configurations with provided map of configurations.
*
* @param configurations
* @return {@link OAuthTokenCredential}
*/
public OAuthTokenCredential setConfigurations(Map configurations) {
this.configurationMap = configurations;
return this;
}
/**
* Returns list of configurations.
*
* @return {@link Map} of configurations
*/
public Map getConfigurations() {
return this.configurationMap;
}
/**
* Returns specific configuration.
*
* @param key
* @return {@link String} value of configuration
*/
public String getConfiguration(String key) {
if (this.configurationMap != null) {
return this.configurationMap.get(key);
}
return null;
}
private synchronized void generateAccessToken() throws PayPalRESTException {
HttpConnection connection;
HttpConfiguration httpConfiguration;
String base64ClientID = generateBase64String(clientID + ":" + clientSecret);
try {
connection = ConnectionManager.getInstance().getConnection();
httpConfiguration = getOAuthHttpConfiguration();
connection.createAndconfigureHttpConnection(httpConfiguration);
this.headers.put(Constants.AUTHORIZATION_HEADER, "Basic " + base64ClientID);
// Accept only json output
this.headers.put(Constants.HTTP_ACCEPT_HEADER, Constants.HTTP_CONTENT_TYPE_JSON);
this.headers.put(Constants.HTTP_CONTENT_TYPE_HEADER, Constants.HTTP_CONFIG_DEFAULT_CONTENT_TYPE);
UserAgentHeader userAgentHeader = new UserAgentHeader(sdkVersion != null ? sdkVersion.getSDKId() : null,
sdkVersion != null ? sdkVersion.getSDKVersion() : null);
this.headers.putAll(userAgentHeader.getHeader());
String postRequest = getRequestPayload();
// log request
String mode = configurationMap.get(Constants.MODE);
if (Constants.LIVE.equalsIgnoreCase(mode) && log.isDebugEnabled()) {
log.warn("Log level cannot be set to DEBUG in " + Constants.LIVE
+ " mode. Skipping request/response logging...");
}
if (!Constants.LIVE.equalsIgnoreCase(mode)) {
log.debug("request header: " + this.headers.toString());
log.debug("request body: " + postRequest);
}
// send request and get & log response
String jsonResponse = connection.execute("", postRequest, this.headers);
if (!Constants.LIVE.equalsIgnoreCase(mode)) {
log.debug("response header: " + connection.getResponseHeaderMap().toString());
log.debug("response: " + jsonResponse);
}
// parse response as JSON object
JsonParser parser = new JsonParser();
JsonElement jsonElement = parser.parse(jsonResponse);
String accessToken = jsonElement.getAsJsonObject().get("token_type").getAsString() + " "
+ jsonElement.getAsJsonObject().get("access_token").getAsString();
// Save expiry date
long tokenLifeTime = jsonElement.getAsJsonObject().get("expires_in").getAsLong();
long expires = new Date().getTime() / 1000 + tokenLifeTime;
ACCESS_TOKENS.put(getCacheTokenKey(), new AccessToken(accessToken, expires));
} catch (ClientActionRequiredException e) {
throw PayPalRESTException.createFromHttpErrorException(e);
} catch (HttpErrorException e) {
throw PayPalRESTException.createFromHttpErrorException(e);
} catch (Exception e) {
throw new PayPalRESTException(e.getMessage(), e);
} finally {
// Replace the headers back to JSON for any future use.
this.headers.put(Constants.HTTP_CONTENT_TYPE_HEADER, Constants.HTTP_CONTENT_TYPE_JSON);
}
}
private String getCacheTokenKey() {
return clientID + ":" + clientSecret + ":" + refreshToken;
}
/*
* Generate a Base64 encoded String from clientID & clientSecret
*/
private String generateBase64String(String clientCredentials) throws PayPalRESTException {
String base64ClientID = null;
byte[] encoded = null;
try {
encoded = Base64.encodeBase64(clientCredentials.getBytes("UTF-8"));
base64ClientID = new String(encoded, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new PayPalRESTException(e.getMessage(), e);
}
return base64ClientID;
}
/**
* Returns the request payload for OAuth Service. Override this method to
* alter the payload
*
* @return Payload as String
*/
protected String getRequestPayload() {
if (this.refreshToken != null) {
return String.format("grant_type=refresh_token&refresh_token=%s", this.refreshToken);
} else {
return "grant_type=client_credentials";
}
}
/*
* Get HttpConfiguration object for OAuth server
*/
private HttpConfiguration getOAuthHttpConfiguration() throws MalformedURLException {
HttpConfiguration httpConfiguration = new HttpConfiguration();
httpConfiguration
.setHttpMethod(Constants.HTTP_CONFIG_DEFAULT_HTTP_METHOD);
/*
* Check for property 'mode' property in the configuration, if not
* found, check for 'oauth.EndPoint' property in the configuration and default
* endpoint to PayPal sandbox or live endpoints. Throw exception if the
* above rules fail
*/
String mode = this.configurationMap.get(Constants.MODE);
// Default to Endpoint param.
String endPointUrl = this.configurationMap.get(Constants.OAUTH_ENDPOINT);
if (Constants.SANDBOX.equalsIgnoreCase(mode)) {
endPointUrl = Constants.REST_SANDBOX_ENDPOINT;
} else if (Constants.LIVE.equalsIgnoreCase(mode)) {
endPointUrl = Constants.REST_LIVE_ENDPOINT;
} else if (endPointUrl == null || endPointUrl.length() <= 0) {
// Default to Normal endpoint
endPointUrl = this.configurationMap.get(Constants.ENDPOINT);
}
// If none of the option works, throw exception.
if (endPointUrl == null || endPointUrl.length() <= 0) {
throw new MalformedURLException(
"service.EndPoint not set (OR) mode not configured to sandbox/live ");
}
if (Boolean
.parseBoolean(configurationMap.get(Constants.USE_HTTP_PROXY))) {
httpConfiguration.setProxySet(true);
httpConfiguration.setProxyHost(configurationMap
.get(Constants.HTTP_PROXY_HOST));
httpConfiguration.setProxyPort(Integer.parseInt(configurationMap
.get(Constants.HTTP_PROXY_PORT)));
String proxyUserName = configurationMap
.get(Constants.HTTP_PROXY_USERNAME);
String proxyPassword = configurationMap
.get(Constants.HTTP_PROXY_PASSWORD);
if (proxyUserName != null && proxyPassword != null) {
httpConfiguration.setProxyUserName(proxyUserName);
httpConfiguration.setProxyPassword(proxyPassword);
}
}
endPointUrl = (endPointUrl.endsWith("/")) ? endPointUrl.substring(0,
endPointUrl.length() - 1) : endPointUrl;
endPointUrl += OAUTH_TOKEN_PATH;
httpConfiguration.setEndPointUrl(endPointUrl);
httpConfiguration
.setGoogleAppEngine(Boolean.parseBoolean(configurationMap
.get(Constants.GOOGLE_APP_ENGINE)));
return httpConfiguration;
}
}