Skip to content

Commit cbee598

Browse files
Setup Intents (#1142)
* add setup intent model * add usage enum * add setup future usage to payment intent * add simple setup intent test * add SetupIntentParams * Add API calls for setup intents * add stripe intent params interface * add createRetrieveSetupIntentParams and formatting * update test case with actual redirect JSON * Update Payment Auth Web View to handle setup intent return URL params * Migrate to setup intent fixtures * add setup intent result * Update stripe and stripe payment auth tests * fix import order for checkstyle * add equals and hashCode methods for testing * update payment controller and tests * Add example app implementation of setup intents * Add new SI logging events to StringDef * add tests for create*IntentParams * remove unused import * add null check in tests * fix createConfirmSetupIntentParamsWithPaymentMethodId typo * re-add data != null check * remove generic builder type * move common API PARAMS up to shared interface * fix whitespace typo * convert to builder for SetupIntent * clean up booleans for enabling buttons * analyze code cleanups * add null checks in test case
1 parent a3448b9 commit cbee598

29 files changed

+1823
-87
lines changed

example/AndroidManifest.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,22 @@
3939
android:scheme="stripe" />
4040
</intent-filter>
4141
</activity>
42+
43+
<activity
44+
android:name=".activity.SetupIntentActivity"
45+
android:launchMode="singleInstance"
46+
android:theme="@style/AppTheme">
47+
<intent-filter>
48+
<action android:name="android.intent.action.VIEW" />
49+
<category android:name="android.intent.category.DEFAULT" />
50+
<category android:name="android.intent.category.BROWSABLE" />
51+
52+
<data
53+
android:host="setup_intent_return"
54+
android:scheme="stripe" />
55+
</intent-filter>
56+
</activity>
57+
4258
<service
4359
android:name=".service.TokenIntentService"
4460
android:exported="false"/>

example/res/layout/activity_payment_auth.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,18 @@
1717
android:layout_marginBottom="40dp"
1818
android:text="@string/buy" />
1919

20+
<TextView
21+
android:layout_width="match_parent"
22+
android:layout_height="wrap_content"
23+
android:text="@string/setup_auth_intro" />
24+
25+
<Button
26+
android:id="@+id/setup_button"
27+
android:layout_width="match_parent"
28+
android:layout_height="wrap_content"
29+
android:layout_marginBottom="40dp"
30+
android:text="@string/setup" />
31+
2032
<ProgressBar
2133
android:id="@+id/progress_bar"
2234
android:layout_width="match_parent"
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:stripe="http://schemas.android.com/apk/res-auto"
4+
android:layout_width="match_parent"
5+
android:layout_height="wrap_content"
6+
>
7+
8+
<LinearLayout
9+
android:layout_width="match_parent"
10+
android:layout_height="wrap_content"
11+
android:gravity="center_horizontal"
12+
android:orientation="vertical"
13+
android:layout_margin="10dp"
14+
android:paddingBottom="40dp"
15+
>
16+
17+
<TextView
18+
android:id="@+id/create_setup_intent"
19+
android:layout_width="match_parent"
20+
android:layout_height="wrap_content"
21+
android:text="@string/create_setup_intent_backend"
22+
/>
23+
24+
<Button
25+
android:id="@+id/btn_create_setup_intent"
26+
android:layout_width="200dp"
27+
android:layout_height="wrap_content"
28+
android:layout_gravity="center"
29+
android:text="@string/create_setup_intent"
30+
/>
31+
32+
<TextView
33+
android:id="@+id/confirm_setup_intent"
34+
android:layout_width="match_parent"
35+
android:layout_height="wrap_content"
36+
android:text="@string/create_payment_method_and_confirm_setup_intent"
37+
android:layout_marginTop="20dp"
38+
/>
39+
40+
<com.stripe.android.view.CardInputWidget
41+
android:id="@+id/card_input_widget"
42+
android:layout_width="match_parent"
43+
android:layout_height="wrap_content"
44+
android:layout_margin="10dp"
45+
stripe:cardHintText="@string/sample_card_requiring_auth"
46+
/>
47+
48+
<Button
49+
android:id="@+id/btn_create_payment_method"
50+
android:layout_width="200dp"
51+
android:layout_height="wrap_content"
52+
android:layout_gravity="center"
53+
android:enabled="false"
54+
android:text="@string/create_payment_method"
55+
/>
56+
57+
<Button
58+
android:id="@+id/btn_confirm_setup_intent"
59+
android:layout_width="200dp"
60+
android:layout_height="wrap_content"
61+
android:layout_gravity="center"
62+
android:enabled="false"
63+
android:text="@string/confirm_setup_intent"
64+
/>
65+
66+
<Button
67+
android:id="@+id/btn_retrieve_setup_intent"
68+
android:layout_width="200dp"
69+
android:layout_height="wrap_content"
70+
android:layout_gravity="center"
71+
android:enabled="false"
72+
android:text="@string/retrieve_setup_intent"
73+
/>
74+
75+
<TextView
76+
android:id="@+id/setup_intent_value"
77+
android:layout_width="match_parent"
78+
android:layout_height="wrap_content"
79+
android:typeface="monospace"
80+
/>
81+
82+
</LinearLayout>
83+
</ScrollView>

example/res/values/strings.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
<string name="label_source_id">ID Last 6</string>
5252

5353
<string name="sample_card_requiring_3ds">4000 0000 0000 3063</string>
54+
<string name="sample_card_requiring_auth">4000 0025 0000 3155</string>
5455

5556
<string name="pay_with_google_content_description">Pay with Google</string>
5657

@@ -71,13 +72,29 @@
7172
<string name="retrieve_payment_intent">Retrieve PaymentIntent</string>
7273
<string name="error_while_retrieving_payment_intent">An error occurred while retrieving the PaymentIntent</string>
7374

75+
<string name="launch_setup_intent_example">Launch SetupIntent Example</string>
76+
<string name="creating_setup_intent">Creating SetupIntent&#8230;</string>
77+
<string name="creating_payment_method">Creating Payment Method&#8230;</string>
78+
<string name="confirming_setup_intent">Confirming SetupIntent&#8230;</string>
79+
<string name="retrieving_setup_intent">Retrieving SetupIntent&#8230;</string>
80+
<string name="create_setup_intent_backend">First, create a SetupIntent on your backend.</string>
81+
<string name="create_setup_intent">Create SetupIntent</string>
82+
<string name="create_payment_method_and_confirm_setup_intent">Second, create a payment method and confirm the SetupIntent</string>
83+
<string name="create_payment_method">Create Payment Method</string>
84+
<string name="confirm_setup_intent">Confirm SetupIntent</string>
85+
<string name="retrieve_setup_intent">Retrieve SetupIntent</string>
86+
<string name="error_while_retrieving_setup_intent">An error occurred while retrieving the SetupIntent</string>
87+
7488
<string name="authentication_dialog_title">Authentication required</string>
7589
<string name="authentication_dialog_message">Authenticate %1$s ending in %2$s?</string>
7690
<string name="pay_with_google">Pay with Google</string>
7791

7892
<!-- Payment Auth example -->
7993
<string name="launch_payment_auth_example">Launch Payment Auth Example</string>
8094
<string name="buy">Buy!</string>
95+
<string name="setup">Buy in the Future!</string>
8196
<string name="payment_intent_status">PaymentIntent status: %s</string>
97+
<string name="setup_intent_status">SetupIntent status: %s</string>
8298
<string name="payment_auth_intro">Tapping the button below will create a PaymentIntent then attempt to confirm it.</string>
99+
<string name="setup_auth_intro">Tapping the button below will create a SetupIntent then attempt to confirm it.</string>
83100
</resources>

example/src/main/java/com/stripe/example/activity/LauncherActivity.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ private ExamplesAdapter(@NonNull Activity activity) {
5353
this.mItems = new ArrayList<>(Arrays.asList(
5454
new Item(activity.getString(R.string.launch_payment_intent_example),
5555
PaymentIntentActivity.class),
56+
new Item(activity.getString(R.string.launch_setup_intent_example),
57+
SetupIntentActivity.class),
5658
new Item(activity.getString(R.string.launch_payment_auth_example),
5759
PaymentAuthActivity.class),
5860
new Item(activity.getString(R.string.create_card_tokens),

example/src/main/java/com/stripe/example/activity/PaymentAuthActivity.java

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@
1515
import com.stripe.android.PaymentAuthConfig;
1616
import com.stripe.android.PaymentConfiguration;
1717
import com.stripe.android.PaymentIntentResult;
18+
import com.stripe.android.SetupIntentResult;
1819
import com.stripe.android.Stripe;
1920
import com.stripe.android.model.PaymentIntent;
2021
import com.stripe.android.model.PaymentIntentParams;
22+
import com.stripe.android.model.SetupIntent;
23+
import com.stripe.android.model.SetupIntentParams;
2124
import com.stripe.example.R;
2225
import com.stripe.example.module.RetrofitFactory;
2326
import com.stripe.example.service.StripeService;
@@ -46,6 +49,8 @@ public class PaymentAuthActivity extends AppCompatActivity {
4649
*/
4750
private static final String PAYMENT_METHOD_3DS2_REQUIRED = "pm_card_threeDSecure2Required";
4851
private static final String PAYMENT_METHOD_3DS_REQUIRED = "pm_card_threeDSecureRequired";
52+
private static final String PAYMENT_METHOD_AUTH_REQUIRED_ON_SETUP =
53+
"pm_card_authenticationRequiredOnSetup";
4954

5055
private static final String RETURN_URL = "stripe://payment_auth";
5156

@@ -58,6 +63,7 @@ public class PaymentAuthActivity extends AppCompatActivity {
5863
private StripeService mStripeService;
5964
private TextView mStatusTextView;
6065
private Button mBuyButton;
66+
private Button mSetupButton;
6167
private ProgressBar mProgressBar;
6268

6369
@Override
@@ -85,6 +91,9 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
8591
mBuyButton = findViewById(R.id.buy_button);
8692
mBuyButton.setOnClickListener((v) -> createPaymentIntent());
8793

94+
mSetupButton = findViewById(R.id.setup_button);
95+
mSetupButton.setOnClickListener((v) -> createSetupIntent());
96+
8897
mProgressBar = findViewById(R.id.progress_bar);
8998
}
9099

@@ -97,14 +106,25 @@ private void confirmPaymentIntent(@NonNull String paymentIntentClientSecret) {
97106
RETURN_URL));
98107
}
99108

109+
private void confirmSetupIntent(@NonNull String setupIntentClientSecret) {
110+
mStatusTextView.append("\n\nStarting setup intent authentication");
111+
mStripe.confirmSetupIntent(this,
112+
SetupIntentParams.createConfirmSetupIntentParamsWithPaymentMethodId(
113+
PAYMENT_METHOD_AUTH_REQUIRED_ON_SETUP,
114+
setupIntentClientSecret,
115+
RETURN_URL));
116+
}
117+
100118
@Override
101-
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
119+
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
102120
super.onActivityResult(requestCode, resultCode, data);
103121

104122
mProgressBar.setVisibility(View.VISIBLE);
105123
mStatusTextView.append("\n\nPayment authentication completed, getting result");
106-
mStripe.onPaymentResult(requestCode, resultCode, data,
107-
new AuthResultListener(this));
124+
125+
mStripe.onPaymentResult(requestCode, resultCode, data, new AuthResultListener(this));
126+
127+
mStripe.onSetupResult(requestCode, resultCode, data, new SetupAuthResultListener(this));
108128
}
109129

110130
@Override
@@ -133,11 +153,26 @@ private void createPaymentIntent() {
133153
.doOnSubscribe((d) -> {
134154
mProgressBar.setVisibility(View.VISIBLE);
135155
mBuyButton.setEnabled(false);
156+
mSetupButton.setEnabled(false);
136157
mStatusTextView.setText(R.string.creating_payment_intent);
137158
})
138159
.subscribe(this::handleCreatePaymentIntentResponse));
139160
}
140161

162+
private void createSetupIntent() {
163+
mCompositeSubscription.add(
164+
mStripeService.createSetupIntent(new HashMap<>(0))
165+
.subscribeOn(Schedulers.io())
166+
.observeOn(AndroidSchedulers.mainThread())
167+
.doOnSubscribe((d) -> {
168+
mProgressBar.setVisibility(View.VISIBLE);
169+
mBuyButton.setEnabled(false);
170+
mSetupButton.setEnabled(false);
171+
mStatusTextView.setText(R.string.creating_setup_intent);
172+
})
173+
.subscribe(this::handleCreateSetupIntentResponse));
174+
}
175+
141176
private void handleCreatePaymentIntentResponse(@NonNull ResponseBody responseBody) {
142177
try {
143178
final JSONObject responseData = new JSONObject(responseBody.string());
@@ -151,8 +186,22 @@ private void handleCreatePaymentIntentResponse(@NonNull ResponseBody responseBod
151186
}
152187
}
153188

189+
private void handleCreateSetupIntentResponse(@NonNull ResponseBody responseBody) {
190+
try {
191+
final JSONObject responseData = new JSONObject(responseBody.string());
192+
mStatusTextView.append("\n\n" +
193+
getString(R.string.setup_intent_status,
194+
responseData.getString("status")));
195+
final String secret = responseData.getString("secret");
196+
confirmSetupIntent(secret);
197+
} catch (IOException | JSONException e) {
198+
e.printStackTrace();
199+
}
200+
}
201+
154202
private void onAuthComplete() {
155203
mBuyButton.setEnabled(true);
204+
mSetupButton.setEnabled(true);
156205
mProgressBar.setVisibility(View.INVISIBLE);
157206
}
158207

@@ -196,4 +245,36 @@ public void onError(@NonNull Exception e) {
196245
activity.onAuthComplete();
197246
}
198247
}
248+
249+
private static class SetupAuthResultListener implements ApiResultCallback<SetupIntentResult> {
250+
@NonNull private final WeakReference<PaymentAuthActivity> mActivityRef;
251+
252+
private SetupAuthResultListener(@NonNull PaymentAuthActivity activity) {
253+
this.mActivityRef = new WeakReference<>(activity);
254+
}
255+
256+
@Override
257+
public void onSuccess(@NonNull SetupIntentResult setupIntentResult) {
258+
final PaymentAuthActivity activity = mActivityRef.get();
259+
if (activity == null) {
260+
return;
261+
}
262+
263+
final SetupIntent setupIntent = setupIntentResult.getIntent();
264+
activity.mStatusTextView.append("\n\n" +
265+
activity.getString(R.string.setup_intent_status, setupIntent.getStatus()));
266+
activity.onAuthComplete();
267+
}
268+
269+
@Override
270+
public void onError(@NonNull Exception e) {
271+
final PaymentAuthActivity activity = mActivityRef.get();
272+
if (activity == null) {
273+
return;
274+
}
275+
276+
activity.mStatusTextView.append("\n\nException: " + e.getMessage());
277+
activity.onAuthComplete();
278+
}
279+
}
199280
}

example/src/main/java/com/stripe/example/activity/PaymentIntentActivity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
7171
mErrorDialogHandler = new ErrorDialogHandler(this);
7272
mStripe = new Stripe(getApplicationContext(),
7373
PaymentConfiguration.getInstance().getPublishableKey());
74-
Retrofit retrofit = RetrofitFactory.getInstance();
74+
final Retrofit retrofit = RetrofitFactory.getInstance();
7575
mStripeService = retrofit.create(StripeService.class);
7676

7777
createPaymentIntent.setOnClickListener(v -> createPaymentIntent());

0 commit comments

Comments
 (0)