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

Payment Method Support #952

Merged
merged 56 commits into from
May 20, 2019
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
f936949
Add utility getters for Payment Method Card and Billing Details to Ca…
jemerick-stripe May 7, 2019
0333442
add PaymentMethodCallback interface
jemerick-stripe May 7, 2019
742c0e2
Add ActivityPaymentMethodCallback impl
jemerick-stripe May 7, 2019
29f3450
Add ability to create payment methods with Stripe
jemerick-stripe May 8, 2019
ebc729f
Refactor AddSourceActivity to AddPaymentMethodActivity
jemerick-stripe May 8, 2019
fb48b45
update tests
jemerick-stripe May 8, 2019
37c9678
clean up add payment method activity
jemerick-stripe May 15, 2019
9edf0c9
update masked card view to accept a Payment Method
jemerick-stripe May 15, 2019
2536054
make getPaymentMethods public
jemerick-stripe May 15, 2019
989d000
Add ActivityPaymentMethodsRetrievalListener
jemerick-stripe May 15, 2019
cff9ef3
Update MaskedCardAdapter to work with PaymentMethods
jemerick-stripe May 15, 2019
b99a3bd
Update PaymentMethodsActivity to use payment methods
jemerick-stripe May 15, 2019
c006f7b
Merge branch 'master' into payment-method-standard-integration
jemerick-stripe May 15, 2019
9a275b3
Map payment method brands to card brands
jemerick-stripe May 15, 2019
a2d29f5
Update masked card adapter to preseve selected item on update
jemerick-stripe May 15, 2019
583c3ed
Update masked card adapter tests
jemerick-stripe May 15, 2019
8b3849c
remove proxy wait delay
jemerick-stripe May 15, 2019
8b5b536
auto select newest added payment method
jemerick-stripe May 15, 2019
8a0ca52
select newest payment method as default
jemerick-stripe May 15, 2019
d4c7f0a
remove commented out code
jemerick-stripe May 15, 2019
5e469b4
update test json to match real data
jemerick-stripe May 15, 2019
4ad649b
update payment method tests
jemerick-stripe May 15, 2019
f09124c
Merge branch 'master' into payment-method-standard-integration
jemerick-stripe May 15, 2019
91347b1
Fix payment method test
jemerick-stripe May 16, 2019
639af1b
add test to ensure newest is selected
jemerick-stripe May 16, 2019
26eca44
fix checkstyle errors
jemerick-stripe May 16, 2019
1dfa1af
Make ActivityPaymentMethodCallback an inner class of AddPaymentMethod…
jemerick-stripe May 16, 2019
5222bbe
Move payment method card brand constants into class, add String Def
jemerick-stripe May 16, 2019
9afe846
fix indent
jemerick-stripe May 16, 2019
5416a87
Make product usage tokens constants throughout
jemerick-stripe May 16, 2019
497f3ba
fix keyword order
jemerick-stripe May 16, 2019
c6f6671
make view holder fields private
jemerick-stripe May 16, 2019
9c4c412
clean up imports
jemerick-stripe May 16, 2019
094b3c3
Fix comments
jemerick-stripe May 16, 2019
ebdfef0
Inline building
jemerick-stripe May 16, 2019
5e62ea2
Update CardInputWidget to support getting payment method card create …
jemerick-stripe May 16, 2019
0421d12
Remove source Card from MaskedCardView
jemerick-stripe May 17, 2019
f7fe45f
update masked card view tests to use payment methods
jemerick-stripe May 17, 2019
4778e4e
update brand string display
jemerick-stripe May 17, 2019
122eeb4
add null check before creating json object
jemerick-stripe May 17, 2019
0beb567
Update payment session data with payment method
jemerick-stripe May 17, 2019
8073644
Update payment session to use payment method
jemerick-stripe May 17, 2019
7845705
Move ActivityPaymentMethodRetrievalListener
jemerick-stripe May 17, 2019
4b02853
Merge branch 'master' into payment-method-standard-integration
jemerick-stripe May 17, 2019
6d15f49
use ApiResultCallback
jemerick-stripe May 17, 2019
8e80f99
Update Brand StringDef
jemerick-stripe May 17, 2019
dc65dd4
cleanup imports
jemerick-stripe May 17, 2019
9d0872b
make constructor private
jemerick-stripe May 17, 2019
417422f
add extra error handling
jemerick-stripe May 17, 2019
fff76c2
fix line lengths
jemerick-stripe May 17, 2019
09a160b
clean up imports
jemerick-stripe May 17, 2019
1946aea
update example app usage of payment method
jemerick-stripe May 20, 2019
aa9c167
updates sample store to use payment methods
jemerick-stripe May 20, 2019
6881a8a
update payment method id param name
jemerick-stripe May 20, 2019
5caaad2
fix import order
jemerick-stripe May 20, 2019
daa8867
clean up tests
jemerick-stripe May 20, 2019
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
2 changes: 1 addition & 1 deletion stripe/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<application>
<activity
android:name=".view.AddSourceActivity"
android:name=".view.AddPaymentMethodActivity"
android:theme="@style/StripeDefaultTheme"
android:windowSoftInputMode="stateVisible" />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import android.support.test.runner.AndroidJUnit4;

import com.stripe.android.view.AddSourceActivity;
import com.stripe.android.view.AddPaymentMethodActivity;

import org.junit.Rule;
import org.junit.Test;
Expand All @@ -14,11 +14,11 @@
import static android.support.test.espresso.matcher.ViewMatchers.withText;

@RunWith(AndroidJUnit4.class)
public class AddSourceActivityTest {
public class AddPaymentMethodActivityTest {

@Rule
public ActivityTestRule<AddSourceActivity> mActivityRule =
new ActivityTestRule(AddSourceActivity.class);
public ActivityTestRule<AddPaymentMethodActivity> mActivityRule =
new ActivityTestRule(AddPaymentMethodActivity.class);

@Test
public void titleRenders() {
Expand Down
41 changes: 32 additions & 9 deletions stripe/src/main/java/com/stripe/android/CustomerSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import com.stripe.android.model.PaymentMethod;
import com.stripe.android.model.ShippingInformation;
import com.stripe.android.model.Source;
import com.stripe.android.view.AddPaymentMethodActivity;
import com.stripe.android.view.PaymentFlowActivity;
import com.stripe.android.view.PaymentMethodsActivity;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
Expand Down Expand Up @@ -57,14 +60,16 @@ public class CustomerSession {
private static final String KEY_SOURCE = "source";
private static final String KEY_SOURCE_TYPE = "source_type";
private static final String KEY_SHIPPING_INFO = "shipping_info";
private static final String TOKEN_PAYMENT_SESSION = "PaymentSession";

private static final Set<String> VALID_TOKENS =
new HashSet<>(Arrays.asList("AddSourceActivity",
"PaymentMethodsActivity",
"PaymentFlowActivity",
TOKEN_PAYMENT_SESSION,
"ShippingInfoScreen",
"ShippingMethodScreen"));
new HashSet<>(Arrays.asList(
AddPaymentMethodActivity.TOKEN_ADD_PAYMENT_METHOD_ACTIVITY,
PaymentMethodsActivity.TOKEN_PAYMENT_METHODS_ACTIVITY,
PaymentFlowActivity.TOKEN_PAYMENT_FLOW_ACTIVITY,
PaymentSession.TOKEN_PAYMENT_SESSION,
PaymentFlowActivity.TOKEN_SHIPPING_INFO_SCREEN,
PaymentFlowActivity.TOKEN_SHIPPING_METHOD_SCREEN
));

@IntDef({
MessageCode.ERROR,
Expand Down Expand Up @@ -401,8 +406,8 @@ public void detachPaymentMethod(
/**
* Gets a Customer's PaymentMethods
*/
void getPaymentMethods(@NonNull PaymentMethod.Type paymentMethodType,
@NonNull PaymentMethodsRetrievalListener listener) {
public void getPaymentMethods(@NonNull PaymentMethod.Type paymentMethodType,
@NonNull PaymentMethodsRetrievalListener listener) {
final Map<String, String> arguments = new HashMap<>();
arguments.put(KEY_PAYMENT_METHOD_TYPE, paymentMethodType.code);

Expand Down Expand Up @@ -918,6 +923,24 @@ protected A getActivity() {
}
}

/**
* Abstract implementation of {@link PaymentMethodsRetrievalListener} that holds a
* {@link WeakReference} to an {@link Activity} object.
*/
public abstract static class ActivityPaymentMethodsRetrievalListener<A extends Activity>
jemerick-stripe marked this conversation as resolved.
Show resolved Hide resolved
implements PaymentMethodsRetrievalListener {
@NonNull private final WeakReference<A> mActivityRef;

public ActivityPaymentMethodsRetrievalListener(@NonNull A activity) {
this.mActivityRef = new WeakReference<>(activity);
}

@Nullable
protected A getActivity() {
return mActivityRef.get();
}
}

private abstract static class CustomerSessionRunnable<T> implements Runnable {
@NonNull private final Handler mUiThreadHandler;
@NonNull private final LocalBroadcastManager mLocalBroadcastManager;
Expand Down
25 changes: 25 additions & 0 deletions stripe/src/main/java/com/stripe/android/PaymentMethodCallback.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.stripe.android;

import android.support.annotation.NonNull;

import com.stripe.android.model.PaymentMethod;

/**
* An interface representing a callback to be notified about the results of
* {@link PaymentMethod} creation.
*/
public interface PaymentMethodCallback {
jemerick-stripe marked this conversation as resolved.
Show resolved Hide resolved
/**
* Error callback method.
*
* @param error the error that occurred.
*/
void onError(@NonNull Exception error);

/**
* Success callback method.
*
* @param paymentMethod the {@link PaymentMethod} that was found or created.
*/
void onSuccess(@NonNull PaymentMethod paymentMethod);
}
162 changes: 103 additions & 59 deletions stripe/src/main/java/com/stripe/android/Stripe.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,6 @@
*/
public class Stripe {

@NonNull
private final SourceCreator mSourceCreator = new SourceCreator() {
@Override
public void create(
@NonNull final SourceParams sourceParams,
@NonNull final String publishableKey,
@Nullable final String stripeAccount,
@Nullable Executor executor,
@NonNull final SourceCallback sourceCallback) {
executeTask(executor, new CreateSourceTask(mApiHandler, sourceParams,
publishableKey, stripeAccount, sourceCallback));
}
};

@VisibleForTesting
TokenCreator mTokenCreator = new TokenCreator() {
@Override
Expand Down Expand Up @@ -307,7 +293,43 @@ public void createSource(
if (apiKey == null) {
return;
}
mSourceCreator.create(sourceParams, apiKey, mStripeAccount, executor, callback);
executeTask(executor,
new CreateSourceTask(mApiHandler, sourceParams, publishableKey, mStripeAccount,
callback));
}

/**
* Create a {@link PaymentMethod} using an {@link AsyncTask} on the default {@link Executor}
* with a publishable api key that has already been set on this {@link Stripe} instance.
*
* @param paymentMethodCreateParams the {@link PaymentMethodCreateParams} to be used
* @param callback a {@link PaymentMethodCallback} to receive a result or an error message
*/
public void createPaymentMethod(@NonNull PaymentMethodCreateParams paymentMethodCreateParams,
@NonNull PaymentMethodCallback callback) {
createPaymentMethod(paymentMethodCreateParams, callback, null, null);
}

/**
* Create a {@link PaymentMethod} using an {@link AsyncTask}.
*
* @param paymentMethodCreateParams the {@link PaymentMethodCreateParams} to be used
* @param callback a {@link PaymentMethodCallback} to receive a result or an error message
* @param publishableKey the publishable api key to be used
* @param executor an {@link Executor} on which to execute the task, or {@link null} for default
*/
public void createPaymentMethod(
@NonNull PaymentMethodCreateParams paymentMethodCreateParams,
@NonNull PaymentMethodCallback callback,
@Nullable String publishableKey,
@Nullable Executor executor) {
final String apiKey = publishableKey == null ? mDefaultPublishableKey : publishableKey;
if (apiKey == null) {
return;
}

executeTask(executor, new CreatePaymentMethodTask(mApiHandler, paymentMethodCreateParams,
apiKey, mStripeAccount, callback));
}

/**
Expand Down Expand Up @@ -878,47 +900,29 @@ private void validateKey(@NonNull @Size(min = 1) String publishableKey) {
}

private void executeTask(@Nullable Executor executor,
@NonNull AsyncTask<Void, Void, ResponseWrapper> task) {
@NonNull AsyncTask<Void, Void, ?> task) {
if (executor != null) {
task.executeOnExecutor(executor);
} else {
task.execute();
}
}

private static class ResponseWrapper {
@Nullable final Source source;
@Nullable final Token token;
private static class ResponseWrapper<T> {
@Nullable final T result;
@Nullable final Exception error;

private ResponseWrapper(@Nullable Token token) {
this.token = token;
this.source = null;
this.error = null;
}

private ResponseWrapper(@Nullable Source source) {
this.source = source;
private ResponseWrapper(@Nullable T result) {
this.result = result;
this.error = null;
this.token = null;
}

private ResponseWrapper(@NonNull Exception error) {
this.error = error;
this.source = null;
this.token = null;
this.result = null;
}
}

interface SourceCreator {
void create(
@NonNull SourceParams params,
@NonNull String publishableKey,
@Nullable String stripeAccount,
@Nullable Executor executor,
@NonNull SourceCallback sourceCallback);
}

@VisibleForTesting
interface TokenCreator {
void create(Map<String, Object> params,
Expand All @@ -929,7 +933,7 @@ void create(Map<String, Object> params,
TokenCallback callback);
}

private static class CreateSourceTask extends AsyncTask<Void, Void, ResponseWrapper> {
private static class CreateSourceTask extends AsyncTask<Void, Void, ResponseWrapper<Source>> {
@NonNull private final StripeApiHandler mApiHandler;
@NonNull private final SourceParams mSourceParams;
@NonNull private final String mPublishableKey;
Expand All @@ -949,30 +953,74 @@ private static class CreateSourceTask extends AsyncTask<Void, Void, ResponseWrap
}

@Override
protected ResponseWrapper doInBackground(Void... params) {
protected ResponseWrapper<Source> doInBackground(Void... params) {
try {
final Source source = mApiHandler.createSource(
mSourceParams,
mPublishableKey,
mStripeAccount
);
return new ResponseWrapper(source);
return new ResponseWrapper<>(source);
} catch (StripeException stripeException) {
return new ResponseWrapper(stripeException);
return new ResponseWrapper<>(stripeException);
}
}

@Override
protected void onPostExecute(@NonNull ResponseWrapper responseWrapper) {
if (responseWrapper.source != null) {
mSourceCallback.onSuccess(responseWrapper.source);
protected void onPostExecute(@NonNull ResponseWrapper<Source> responseWrapper) {
if (responseWrapper.result != null) {
mSourceCallback.onSuccess(responseWrapper.result);
jemerick-stripe marked this conversation as resolved.
Show resolved Hide resolved
} else if (responseWrapper.error != null) {
mSourceCallback.onError(responseWrapper.error);
}
}
}

private static class CreateTokenTask extends AsyncTask<Void, Void, ResponseWrapper> {
private static class CreatePaymentMethodTask extends AsyncTask<Void, Void,
ResponseWrapper<PaymentMethod>> {
@NonNull private final StripeApiHandler mApiHandler;
@NonNull private final PaymentMethodCreateParams mPaymentMethodCreateParams;
@NonNull private final String mPublishableKey;
@Nullable private final String mStripeAccount;
@NonNull private final PaymentMethodCallback mPaymentMethodCallback;

CreatePaymentMethodTask(@NonNull StripeApiHandler apiHandler,
@NonNull PaymentMethodCreateParams paymentMethodCreateParams,
@NonNull String publishableKey,
@Nullable String stripeAccount,
@NonNull PaymentMethodCallback paymentMethodCallback) {
mApiHandler = apiHandler;
mPaymentMethodCreateParams = paymentMethodCreateParams;
mPublishableKey = publishableKey;
mStripeAccount = stripeAccount;
mPaymentMethodCallback = paymentMethodCallback;
}

@Override
protected ResponseWrapper<PaymentMethod> doInBackground(Void... params) {
try {
final PaymentMethod paymentMethod = mApiHandler.createPaymentMethod(
mPaymentMethodCreateParams,
mPublishableKey,
mStripeAccount
);
return new ResponseWrapper<>(paymentMethod);
} catch (StripeException stripeException) {
return new ResponseWrapper<>(stripeException);
}
}

@Override
protected void onPostExecute(@NonNull ResponseWrapper<PaymentMethod> responseWrapper) {
if (responseWrapper.result != null) {
mPaymentMethodCallback.onSuccess(responseWrapper.result);
} else if (responseWrapper.error != null) {
mPaymentMethodCallback.onError(responseWrapper.error);
}
jemerick-stripe marked this conversation as resolved.
Show resolved Hide resolved
}
}

private static class CreateTokenTask extends AsyncTask<Void, Void, ResponseWrapper<Token>> {
@NonNull private final StripeApiHandler mApiHandler;
@NonNull private final Map<String, Object> mTokenParams;
@NonNull private final String mPublishableKey;
Expand All @@ -996,7 +1044,7 @@ private static class CreateTokenTask extends AsyncTask<Void, Void, ResponseWrapp
}

@Override
protected ResponseWrapper doInBackground(Void... params) {
protected ResponseWrapper<Token> doInBackground(Void... params) {
try {
final RequestOptions requestOptions = RequestOptions.builder(mPublishableKey,
mStripeAccount, RequestOptions.TYPE_QUERY).build();
Expand All @@ -1005,22 +1053,18 @@ protected ResponseWrapper doInBackground(Void... params) {
requestOptions,
mTokenType
);
return new ResponseWrapper(token);
return new ResponseWrapper<>(token);
} catch (StripeException e) {
return new ResponseWrapper(e);
return new ResponseWrapper<>(e);
}
}

@Override
protected void onPostExecute(@NonNull ResponseWrapper result) {
tokenTaskPostExecution(result);
}

private void tokenTaskPostExecution(@NonNull ResponseWrapper result) {
if (result.token != null) {
mCallback.onSuccess(result.token);
} else if (result.error != null) {
mCallback.onError(result.error);
protected void onPostExecute(@NonNull ResponseWrapper<Token> response) {
if (response.result != null) {
mCallback.onSuccess(response.result);
} else if (response.error != null) {
mCallback.onError(response.error);
} else {
mCallback.onError(new RuntimeException(
"Somehow got neither a token response or an error response"));
Expand Down
Loading