Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add API key validation #1036

Merged
merged 1 commit into from
Jun 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions stripe/src/main/java/com/stripe/android/ApiKeyValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.stripe.android;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

final class ApiKeyValidator {
private static final ApiKeyValidator DEFAULT = new ApiKeyValidator();

@NonNull
static ApiKeyValidator get() {
return DEFAULT;
}

@NonNull
String requireValid(@Nullable String apiKey) {
if (apiKey == null || apiKey.trim().length() == 0) {
throw new IllegalArgumentException("Invalid Publishable Key: " +
"You must use a valid publishable key to create a token. " +
"For more info, see https://stripe.com/docs/keys");
}

if (apiKey.startsWith("sk_")) {
throw new IllegalArgumentException("Invalid Publishable Key: " +
"You are using a secret key, instead of the publishable one. " +
"For more info, see https://stripe.com/docs/keys");
}

return apiKey;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class PaymentAuthenticationController {
@NonNull private final MessageVersionRegistry mMessageVersionRegistry;
@NonNull private final String mDirectoryServerId;
@NonNull private final PaymentAuthConfig mConfig;
@NonNull private final ApiKeyValidator mApiKeyValidator;

PaymentAuthenticationController(@NonNull Context context,
@NonNull StripeApiHandler apiHandler) {
Expand All @@ -67,6 +68,7 @@ class PaymentAuthenticationController {
mApiHandler = apiHandler;
mMessageVersionRegistry = messageVersionRegistry;
mDirectoryServerId = directoryServerId;
mApiKeyValidator = new ApiKeyValidator();
}

/**
Expand All @@ -76,6 +78,7 @@ void startConfirmAndAuth(@NonNull Stripe stripe,
@NonNull Activity activity,
@NonNull PaymentIntentParams paymentIntentParams,
@NonNull String publishableKey) {
mApiKeyValidator.requireValid(publishableKey);
new ConfirmPaymentIntentTask(stripe, paymentIntentParams, publishableKey,
new ConfirmPaymentIntentCallback(activity, publishableKey, this))
.execute();
Expand All @@ -84,7 +87,8 @@ void startConfirmAndAuth(@NonNull Stripe stripe,
void startAuth(@NonNull Activity activity,
@NonNull PaymentIntent paymentIntent,
@NonNull String publishableKey) {
handleNextAction(activity, paymentIntent, publishableKey);
handleNextAction(activity, paymentIntent,
mApiKeyValidator.requireValid(publishableKey));
}

/**
Expand Down
41 changes: 26 additions & 15 deletions stripe/src/main/java/com/stripe/android/RequestOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,36 +19,47 @@ final class RequestOptions {
}

@Nullable private final String mGuid;
@Nullable private final String mPublishableApiKey;
@Nullable private final String mApiKey;
@RequestType private final int mRequestType;
@Nullable private final String mStripeAccount;

@NonNull
static RequestOptions createForFingerprinting(@NonNull String guid) {
return new RequestOptions(RequestType.FINGERPRINTING, null, null, guid);
return new RequestOptions(guid);
}

@NonNull
static RequestOptions createForApi(@NonNull String publishableApiKey) {
return new RequestOptions(RequestType.API, publishableApiKey, null, null);
static RequestOptions createForApi(@NonNull String apiKey) {
return new RequestOptions(apiKey, null);
}

@NonNull
static RequestOptions createForApi(
@NonNull String publishableApiKey,
@NonNull String apiKey,
@Nullable String stripeAccount) {
return new RequestOptions(RequestType.API, publishableApiKey, stripeAccount, null);
return new RequestOptions(apiKey, stripeAccount);
}

/**
* Constructor for {@link RequestType#API}
*/
private RequestOptions(
@RequestType int requestType,
@Nullable String publishableApiKey,
@Nullable String stripeAccount,
@Nullable String guid) {
mGuid = guid;
mPublishableApiKey = publishableApiKey;
mRequestType = requestType;
@NonNull String apiKey,
@Nullable String stripeAccount) {
mRequestType = RequestType.API;
mApiKey = new ApiKeyValidator().requireValid(apiKey);
mStripeAccount = stripeAccount;
mGuid = null;
}

/**
* Constructor for {@link RequestType#FINGERPRINTING}
*/
private RequestOptions(@NonNull String guid) {
mRequestType = RequestType.FINGERPRINTING;
mGuid = guid;
mApiKey = null;
mStripeAccount = null;
}

/**
Expand All @@ -63,8 +74,8 @@ String getGuid() {
* @return the publishable API key for this request
*/
@Nullable
String getPublishableApiKey() {
return mPublishableApiKey;
String getApiKey() {
return mApiKey;
}

@RequestType
Expand Down
34 changes: 6 additions & 28 deletions stripe/src/main/java/com/stripe/android/Stripe.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public class Stripe {
@NonNull private final StripeNetworkUtils mStripeNetworkUtils;
@NonNull private final PaymentAuthenticationController mPaymentAuthenticationController;
@NonNull private final TokenCreator mTokenCreator;
@NonNull private final ApiKeyValidator mApiKeyValidator;
private String mDefaultPublishableKey;
@Nullable private String mStripeAccount;

Expand All @@ -66,7 +67,7 @@ public Stripe(@NonNull Context context) {
*/
public Stripe(@NonNull Context context, @NonNull String publishableKey) {
this(context, new StripeApiHandler(context), new StripeNetworkUtils(context),
validatedKey(publishableKey));
ApiKeyValidator.get().requireValid(publishableKey));
}

Stripe(@NonNull Context context, @NonNull final StripeApiHandler apiHandler,
Expand Down Expand Up @@ -102,11 +103,13 @@ public void create(
@NonNull PaymentAuthenticationController paymentAuthenticationController,
@Nullable String publishableKey,
@NonNull TokenCreator tokenCreator) {
mApiKeyValidator = new ApiKeyValidator();
mApiHandler = apiHandler;
mStripeNetworkUtils = stripeNetworkUtils;
mPaymentAuthenticationController = paymentAuthenticationController;
mTokenCreator = tokenCreator;
mDefaultPublishableKey = publishableKey != null ? validatedKey(publishableKey) : null;
mDefaultPublishableKey = publishableKey != null ?
mApiKeyValidator.requireValid(publishableKey) : null;
}

/**
Expand Down Expand Up @@ -293,7 +296,6 @@ public Token createBankAccountTokenSynchronous(@NonNull final BankAccount bankAc
APIConnectionException,
CardException,
APIException {
validatedKey(publishableKey);
return mApiHandler.createToken(
mStripeNetworkUtils.hashMapFromBankAccount(bankAccount),
RequestOptions.createForApi(publishableKey, mStripeAccount),
Expand Down Expand Up @@ -626,8 +628,6 @@ public Token createTokenSynchronous(@NonNull final Card card, @NonNull String pu
APIConnectionException,
CardException,
APIException {
validatedKey(publishableKey);

return mApiHandler.createToken(
mStripeNetworkUtils.hashMapFromCard(card),
RequestOptions.createForApi(publishableKey, mStripeAccount),
Expand Down Expand Up @@ -678,7 +678,6 @@ public Token createPiiTokenSynchronous(@NonNull String personalId,
APIConnectionException,
CardException,
APIException {
validatedKey(publishableKey);
return mApiHandler.createToken(
hashMapFromPersonalId(personalId),
RequestOptions.createForApi(publishableKey, mStripeAccount),
Expand Down Expand Up @@ -729,7 +728,6 @@ public Token createCvcUpdateTokenSynchronous(@NonNull String cvc,
APIConnectionException,
CardException,
APIException {
validatedKey(publishableKey);
return mApiHandler.createToken(
mapFromCvc(cvc),
RequestOptions.createForApi(publishableKey, mStripeAccount),
Expand Down Expand Up @@ -781,7 +779,6 @@ public Token createAccountTokenSynchronous(
InvalidRequestException,
APIConnectionException,
APIException {
validatedKey(publishableKey);
try {
return mApiHandler.createToken(
accountParams.toParamMap(),
Expand Down Expand Up @@ -857,7 +854,7 @@ public Source retrieveSourceSynchronous(
*/
@Deprecated
public void setDefaultPublishableKey(@NonNull @Size(min = 1) String publishableKey) {
mDefaultPublishableKey = validatedKey(publishableKey);
mDefaultPublishableKey = mApiKeyValidator.requireValid(publishableKey);
}

/**
Expand All @@ -880,32 +877,13 @@ private void createTokenFromParams(
Objects.requireNonNull(callback,
"Required Parameter: 'callback' is required to use the created " +
"token and handle errors");

validatedKey(publishableKey);
mTokenCreator.create(
tokenParams,
RequestOptions.createForApi(publishableKey, mStripeAccount),
tokenType,
executor, callback);
}

@NonNull
private static String validatedKey(@Nullable @Size(min = 1) String publishableKey) {
if (publishableKey == null || publishableKey.length() == 0) {
throw new IllegalArgumentException("Invalid Publishable Key: " +
"You must use a valid publishable key to create a token. " +
"For more info, see https://stripe.com/docs/keys");
}

if (publishableKey.startsWith("sk_")) {
throw new IllegalArgumentException("Invalid Publishable Key: " +
"You are using a secret key, instead of the publishable one. " +
"For more info, see https://stripe.com/docs/keys");
}

return publishableKey;
}

private static void executeTask(@Nullable Executor executor,
@NonNull AsyncTask<Void, Void, ?> task) {
if (executor != null) {
Expand Down
49 changes: 15 additions & 34 deletions stripe/src/main/java/com/stripe/android/StripeApiHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;

/**
* Handler for calls to the Stripe API.
Expand Down Expand Up @@ -73,7 +74,7 @@ boolean logApiCall(
return false;
}

final String apiKey = options.getPublishableApiKey();
final String apiKey = options.getApiKey();
if (apiKey == null || apiKey.trim().isEmpty()) {
// if there is no apiKey associated with the request, we don't need to react here.
return false;
Expand Down Expand Up @@ -106,20 +107,16 @@ PaymentIntent confirmPaymentIntent(
final RequestOptions options = RequestOptions.createForApi(publishableKey, stripeAccount);

try {
final String apiKey = options.getPublishableApiKey();
if (StripeTextUtils.isBlank(apiKey)) {
return null;
}

logTelemetryData();
final SourceParams sourceParams = paymentIntentParams.getSourceParams();
final String sourceType = sourceParams != null ? sourceParams.getType() : null;
logApiCall(
mLoggingUtils.getPaymentIntentConfirmationParams(null, apiKey, sourceType),
mLoggingUtils.getPaymentIntentConfirmationParams(null,
Objects.requireNonNull(options.getApiKey()), sourceType),
RequestOptions.createForApi(publishableKey)
);
final String paymentIntentId = PaymentIntent.parseIdFromClientSecret(
paymentIntentParams.getClientSecret());
Objects.requireNonNull(paymentIntentParams.getClientSecret()));
final StripeResponse response = requestData(StripeRequest.createPost(
getConfirmPaymentIntentUrl(paymentIntentId), paramMap, options));
return PaymentIntent.fromString(response.getResponseBody());
Expand Down Expand Up @@ -149,18 +146,14 @@ PaymentIntent retrievePaymentIntent(
final RequestOptions options = RequestOptions.createForApi(publishableKey, stripeAccount);

try {
final String apiKey = options.getPublishableApiKey();
if (StripeTextUtils.isBlank(apiKey)) {
return null;
}

logTelemetryData();
logApiCall(
mLoggingUtils.getPaymentIntentRetrieveParams(null, apiKey),
mLoggingUtils.getPaymentIntentRetrieveParams(null,
Objects.requireNonNull(options.getApiKey())),
RequestOptions.createForApi(publishableKey)
);
final String paymentIntentId = PaymentIntent.parseIdFromClientSecret(
paymentIntentParams.getClientSecret());
Objects.requireNonNull(paymentIntentParams.getClientSecret()));
final StripeResponse response = requestData(StripeRequest.createGet(
getRetrievePaymentIntentUrl(paymentIntentId), paramMap, options));
return PaymentIntent.fromString(response.getResponseBody());
Expand Down Expand Up @@ -198,14 +191,10 @@ Source createSource(
final RequestOptions options = RequestOptions.createForApi(publishableKey, stripeAccount);

try {
final String apiKey = options.getPublishableApiKey();
if (StripeTextUtils.isBlank(apiKey)) {
return null;
}

logTelemetryData();
logApiCall(
mLoggingUtils.getSourceCreationParams(null, apiKey, sourceParams.getType()),
mLoggingUtils.getSourceCreationParams(null,
Objects.requireNonNull(options.getApiKey()), sourceParams.getType()),
RequestOptions.createForApi(publishableKey)
);
final StripeResponse response = requestData(
Expand Down Expand Up @@ -275,14 +264,10 @@ PaymentMethod createPaymentMethod(

mNetworkUtils.addUidParams(params);
final RequestOptions options = RequestOptions.createForApi(publishableKey, stripeAccount);
final String apiKey = options.getPublishableApiKey();
if (StripeTextUtils.isBlank(apiKey)) {
return null;
}

logTelemetryData();
logApiCall(
mLoggingUtils.getPaymentMethodCreationParams(null, apiKey),
mLoggingUtils.getPaymentMethodCreationParams(null,
Objects.requireNonNull(options.getApiKey())),
RequestOptions.createForApi(publishableKey)
);

Expand Down Expand Up @@ -324,19 +309,15 @@ Token createToken(
APIException {

try {
final String apiKey = options.getPublishableApiKey();
if (StripeTextUtils.isBlank(apiKey)) {
return null;
}

final List<String> loggingTokens =
(List<String>) tokenParams.get(LoggingUtils.FIELD_PRODUCT_USAGE);
tokenParams.remove(LoggingUtils.FIELD_PRODUCT_USAGE);

logTelemetryData();

logApiCall(
mLoggingUtils.getTokenCreationParams(loggingTokens, apiKey, tokenType),
mLoggingUtils.getTokenCreationParams(loggingTokens,
Objects.requireNonNull(options.getApiKey()), tokenType),
options
);
} catch (ClassCastException classCastEx) {
Expand Down Expand Up @@ -1014,7 +995,7 @@ StripeResponse requestData(@NonNull StripeRequest request)
allowedToSetTTL = false;
}

final String apiKey = request.options.getPublishableApiKey();
final String apiKey = request.options.getApiKey();
if (StripeTextUtils.isBlank(apiKey)) {
throw new AuthenticationException("No API key provided. (HINT: set your API key using" +
" 'Stripe.apiKey = <API-KEY>'. You can generate API keys from the Stripe" +
Expand Down
4 changes: 2 additions & 2 deletions stripe/src/main/java/com/stripe/android/StripeRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import java.util.Locale;
import java.util.Map;

class StripeRequest {
final class StripeRequest {
private static final String CHARSET = "UTF-8";

@NonNull final Method method;
Expand Down Expand Up @@ -100,7 +100,7 @@ Map<String, String> getHeaders(@NonNull ApiVersion apiVersion) {
BuildConfig.VERSION_NAME));

headers.put("Authorization", String.format(Locale.ENGLISH,
"Bearer %s", options.getPublishableApiKey()));
"Bearer %s", options.getApiKey()));

// debug headers
final AbstractMap<String, String> propertyMap = new HashMap<>();
Expand Down
Loading