{"html":"\n\u003Cheader\u003E\n \u003Ch1\u003EUsing Android Standard UI Components\u003C/h1\u003E\n \u003Cp\u003ELearn how to build an integration that uses the built-in user interface components in Stripe's Android SDK.\u003C/p\u003E\n\u003C/header\u003E\n\n\u003Csection\u003E\n \u003Caside class=\"important check\"\u003E\n \u003Cp\u003EThis guide assumes you've already followed the \u003Ca href=\"/docs/mobile/android\"\u003EGetting Started\u003C/a\u003E section of our main tutorial to install and configure our SDK.\u003C/p\u003E\n \u003C/aside\u003E\n \u003Cp\u003EThis guide demonstrates how to build your app's customer payment management flow using \u003Ccode\u003EPaymentSession\u003C/code\u003E, a class designed to make building your app's checkout flow as easy as possible. It handles collecting, saving, and reusing your user's payment details.\u003C/p\u003E\n\u003C/section\u003E\n\n\u003Csection\u003E\n \u003Ch2 id=\"prepare-your-api\"\u003EPrepare your API\u003C/h2\u003E\n\n \u003Cp\u003EOur prebuilt UI elements operate on the assumption that you have a single \u003Ccode\u003ECustomer\u003C/code\u003E object for each of your users. We strongly recommend you create this \u003Ccode\u003ECustomer\u003C/code\u003E at the same time you create your user on your own backend, so that every user is guaranteed to have an associated \u003Ccode\u003ECustomer\u003C/code\u003E. (This is fine even if you don't collect your user's payment information when they sign up\u0026mdash;it's totally OK to have a Customer without any attached cards.)\u003C/p\u003E\n\n \u003Cp\u003EOur prebuilt UI elements require an ephemeral key, a short-lived API key with restricted API access. You can think of an ephemeral key as a session, authorizing the SDK to retrieve and update a specific Customer object for the duration of the session. To provide an ephemeral key to the SDK, you'll need to expose a new API endpoint on your backend. This endpoint should create an ephemeral key for the current Stripe customer, and return the key's unmodified response as JSON.\u003C/p\u003E\n\n \u003Cp\u003EWhen the SDK requests an ephemeral key, it specifies the version of the Stripe API that it expects the response to come from. Your endpoint should accept an \u003Ccode\u003Eapi_version\u003C/code\u003E parameter, and use the specified API version when creating the ephemeral key. This ensures that the SDK always receives the correct ephemeral key response from your backend. You can consult our \u003Ca href=\"https://github.com/stripe/example-ios-backend/blob/master/web.rb\" class=\"external\" target=\"_blank\"\u003EExample Backend\u003C/a\u003E to see this in practice.\n\n \u003Cdiv class=\"tabs tabs-code animated\"\u003E\n \u003Cnav class=\"nav-languages\"\u003E\n \u003Ca data-language=\"ruby\"\n \u003ERuby\u003C/a\u003E\n \u003Ca data-language=\"python\"\n \u003EPython\u003C/a\u003E\n \u003Ca data-language=\"php\"\n \u003EPHP\u003C/a\u003E\n \u003Ca data-language=\"node\"\n \u003ENode\u003C/a\u003E\n \u003Ca data-language=\"go\"\n \u003EGo\u003C/a\u003E\n \u003C/nav\u003E\n \u003Cdiv class=\"tabs-content\"\u003E\n \u003Cdiv class=\"code\" data-language=\"ruby\"\u003E\n \u003Cpre class=\"language-ruby numbered\" \u003E\u003Ccode\u003E# Sinatra\npost path do\n stripe_version = params[\u0026#39;api_version\u0026#39;]\n customer_id = session[\u0026#39;customer_id\u0026#39;]\n key = Stripe::EphemeralKey.create(\n {customer: customer_id},\n {stripe_version: stripe_version}\n )\n key.to_json\nend\u003C/code\u003E\u003C/pre\u003E\n\n\u003C/div\u003E\n\u003Cdiv class=\"code\" data-language=\"python\"\u003E\n \u003Cpre class=\"language-python numbered\" \u003E\u003Ccode\u003E# Flask\nfrom flask import Flask, session, jsonify, request\n# This function assumes that the session handling has stored the customerId\n@app.route(path, methods=[\u0026#39;POST\u0026#39;])\ndef issue_key():\n stripe_api_version = request.args[\u0026#39;api_version\u0026#39;]\n customerId = session[\u0026#39;customerId\u0026#39;]\n key = stripe.EphemeralKey.create(customer=customerId, api_version=stripe_api_version)\n return jsonify(key)\u003C/code\u003E\u003C/pre\u003E\n\n\u003C/div\u003E\n\u003Cdiv class=\"code\" data-language=\"php\"\u003E\n \u003Cpre class=\"language-php numbered\" \u003E\u003Ccode\u003E// This assumes that $customerId has been set appropriately from session data\nparse_str($_SERVER[\u0026#39;QUERY_STRING\u0026#39;], $params);\nif (!isset($params[\u0026#39;api_version\u0026#39;]))\n{\n exit(http_response_code(400));\n}\ntry {\n $key = \\Stripe\\EphemeralKey::create(\n [\u0026quot;customer\u0026quot; =\u0026gt; $customerId],\n [\u0026quot;stripe_version\u0026quot; =\u0026gt; $params[\u0026#39;api_version\u0026#39;]]\n );\n header(\u0026#39;Content-Type: application/json\u0026#39;);\n exit(json_encode($key));\n} catch (Exception $e) {\n exit(http_response_code(500));\n}\u003C/code\u003E\u003C/pre\u003E\n\n\u003C/div\u003E\n\u003Cdiv class=\"code\" data-language=\"node\"\u003E\n \u003Cpre class=\"language-javascript numbered\" \u003E\u003Ccode\u003E// Express\napp.post(path, (req, res) =\u0026gt; {\n const stripe_version = req.query.api_version;\n if (!stripe_version) {\n res.status(400).end();\n return;\n }\n // This function assumes that some previous middleware has determined the\n // correct customerId for the session and saved it on the request object.\n stripe.ephemeralKeys.create(\n {customer: req.customerId},\n {stripe_version: stripe_version}\n ).then((key) =\u0026gt; {\n res.status(200).json(key);\n }).catch((err) =\u0026gt; {\n res.status(500).end();\n });\n});\u003C/code\u003E\u003C/pre\u003E\n\n\u003C/div\u003E\n\u003Cdiv class=\"code\" data-language=\"go\"\u003E\n \u003Cpre class=\"language-go numbered\" \u003E\u003Ccode\u003E// net/http\n// The customerId parameter should be the ID of the Customer object associated\n// with the session the request was made on.\nfunc issueKeyHandler(w http.ResponseWriter, r *http.Request, customerId string) {\n r.ParseForm()\n stripeVersion := r.Form.Get(\u0026quot;api_version\u0026quot;)\n if stripeVersion == \u0026quot;\u0026quot; {\n log.Printf(\u0026quot;Stripe-Version not found\\n\u0026quot;)\n w.WriteHeader(400)\n return\n }\n params := \u0026amp;stripe.EphemeralKeyParams{\n Customer: stripe.String(customerId),\n StripeVersion: stripe.String(stripeVersion),\n }\n key, err := ephemeralkey.New(params)\n if err != nil {\n log.Printf(\u0026quot;Stripe bindings call failed, %v\\n\u0026quot;, err)\n w.WriteHeader(500)\n return\n }\n w.Write(key.RawJSON)\n}\u003C/code\u003E\u003C/pre\u003E\n\n\u003C/div\u003E\n\n \u003C/div\u003E\n \u003C/div\u003E\n\n \u003Cp\u003EAfter you've added an ephemeral key endpoint to your backend, you'll need a way for your Android app to communicate with this endpoint. In your app, you should make your API client class implement the \u003Ca href=\"https://github.com/stripe/stripe-android/blob/master/stripe/src/main/java/com/stripe/android/EphemeralKeyProvider.java\" class=\"external\"\u003E\u003Ccode\u003EEphemeralKeyProvider\u003C/code\u003E\u003C/a\u003E interface, which defines a single method, \u003Ccode\u003EcreateEphemeralKey\u003C/code\u003E. When implementing this method, be sure to pass the \u003Ccode\u003EapiVersion\u003C/code\u003E parameter along to your ephemeral keys endpoint. You can consult our \u003Ca href=\"https://github.com/stripe/stripe-android/blob/master/example/src/main/java/com/stripe/example/service/ExampleEphemeralKeyProvider.java\" class=\"external\"\u003EExample App\u003C/a\u003E to see this in practice.\u003C/p\u003E\n\n\u003Cdiv class=\"code\" data-language=\"java\"\u003E\n \u003Cnav class=\"nav-filename icon-file\"\u003E\n ExampleEphemeralKeyProvider.java\n\u003C/nav\u003E\n\u003Cpre class=\"language-java numbered\" \u003E\u003Ccode\u003E// Using RxJava for handling asynchronous responses\n@Override\npublic void createEphemeralKey(@NonNull @Size(min = 4) String apiVersion,\n @NonNull final EphemeralKeyUpdateListener keyUpdateListener) {\n Map\u0026lt;String, String\u0026gt; apiParamMap = new HashMap\u0026lt;\u0026gt;();\n apiParamMap.put(\u0026quot;api_version\u0026quot;, apiVersion);\n\n mCompositeSubscription.add(\n mStripeService.createEphemeralKey(apiParamMap)\n .subscribeOn(Schedulers.io())\n .observeOn(AndroidSchedulers.mainThread())\n .subscribe(new Action1\u0026lt;ResponseBody\u0026gt;() {\n @Override\n public void call(ResponseBody response) {\n try {\n String rawKey = response.string();\n keyUpdateListener.onKeyUpdate(rawKey);\n mProgressListener.onStringResponse(rawKey);\n } catch (IOException iox) {\n\n }\n }\n }, new Action1\u0026lt;Throwable\u0026gt;() {\n @Override\n public void call(Throwable throwable) {\n mProgressListener.onStringResponse(throwable.getMessage());\n }\n }));\n}\n\n\u003C/code\u003E\u003C/pre\u003E\n\n\u003C/div\u003E\n\n \u003Cp\u003E\n After creating an EphemeralKeyProvider, initialize a \u003Ca href=\"https://github.com/stripe/stripe-android/blob/master/stripe/src/main/java/com/stripe/android/CustomerSession.java\" class=\"external\"\u003E\u003Ccode\u003ECustomerSession\u003C/code\u003E\u003C/a\u003E. Think of \u003Ccode\u003ECustomerSession\u003C/code\u003E as a Stripe communication manager for a single logged-in session of a customer. A \u003Ccode\u003ECustomerSession\u003C/code\u003E talks to your backend to retrieve an ephemeral key (via its key provider), and uses that key to manage retrieving and updating its Stripe customer on your behalf.\u003C/p\u003E\n\n\u003Cdiv class=\"code\" data-language=\"java\"\u003E\n \u003Cnav class=\"nav-filename icon-file\"\u003E\n ExampleEphemeralKeyProvider.java\n\u003C/nav\u003E\n\u003Cpre class=\"language-java numbered\" \u003E\u003Ccode\u003ECustomerSession.initCustomerSession(\n new ExampleEphemeralKeyProvider(\n new ExampleEphemeralKeyProvider.ProgressListener() {\n @Override\n public void onStringResponse(String string) {\n if (string.startsWith(\u0026quot;Error: \u0026quot;)) {\n // Show the error to the user.\n }\n }\n }));\n\n\u003C/code\u003E\u003C/pre\u003E\n\n\u003C/div\u003E\n\n \u003Cp\u003E\u003Ccode\u003ECustomerSession\u003C/code\u003E automatically prefetches its customer once its key provider has been set. If you'd like to take advantage of preloading your customer's information, initialize your \u003Ccode\u003ECustomerSession\u003C/code\u003E instance earlier, before your user enters your payment flow. If your current user logs out of the app, you should clear the current \u003Ccode\u003ECustomerSession\u003C/code\u003E singleton using the \u003Ccode\u003EclearInstance\u003C/code\u003E method. When a new user logs in, you can re-initialize the instance. On your backend, be sure to create and return a new ephemeral key for the customer object associated with the new user.\u003C/p\u003E\n\n\u003Ch3\u003EImplement your app's payment flow using \u003Ccode\u003EPaymentSession\u003C/code\u003E\u003C/h3\u003E\n\n \u003Cp\u003EOur SDK provides a class called \u003Ca href=\"https://github.com/stripe/stripe-android/blob/master/stripe/src/main/java/com/stripe/android/PaymentSession.java\" class=\"external\"\u003E\u003Ccode\u003EPaymentSession\u003C/code\u003E\u003C/a\u003E, which is designed to make building your app's checkout flow as easy as possible. It handles collecting, saving, and reusing your user's payment details, and can also be used to collect shipping info. Think of it as the data source for your checkout activity\u0026mdash;it handles asynchronously retrieving the data you need, and notifies its listener when its UI should change.\u003C/p\u003E\n\n \u003Ch4\u003ESetting the listener, configuration, and host activity\u003C/h4\u003E\n\n \u003Cp\u003ETo work with \u003Ccode\u003EPaymentSession\u003C/code\u003E, you'll need to write a class that conforms to \u003Ccode\u003EPaymentSessionListener\u003C/code\u003E, and pass a \u003Ccode\u003EPaymentSessionConfig\u003C/code\u003E object to the \u003Ccode\u003Einit\u003C/code\u003E method. (Note, the code samples in this section are simply examples – your own implementation may differ depending on the structure of your app).\u003C/p\u003E\n\n \u003Cp\u003EThe \u003Ccode\u003EPaymentSessionConfig\u003C/code\u003E object is created using a Builder pattern, and has the following fields, all of which are optional.\u003C/p\u003E\n\n \u003Ch4\u003E\u003Ccode\u003EsetShippingInfoRequired(boolean required)\u003C/code\u003E\u003C/h4\u003E\n \u003Cp\u003EThis method sets whether or not the PaymentSession requires the user\n to have a valid shipping address. The default value is \u003Ccode\u003Etrue\u003C/code\u003E.\n \u003C/p\u003E\n\n \u003Ch4\u003E\u003Ccode\u003EsetShippingMethodsRequired(boolean required)\u003C/code\u003E\u003C/h4\u003E\n \u003Cp\u003EThis method sets whether or not the PaymentSession requires the user\n to select a shipping method (Overnight, Standard Shipping, etc). The default value is \u003Ccode\u003Etrue\u003C/code\u003E. You must set these values later.\n \u003C/p\u003E\n\n \u003Ch4\u003E\u003Ccode\u003EsetPrepopulatedShippingInfo(ShippingInformation shippingInfo)\u003C/code\u003E\u003C/h4\u003E\n \u003Cp\u003ESets a default shipping address for your customer, which you can get from your CustomerSession or your own records. The Shipping information screen will start with this information already filled out.\n \u003C/p\u003E\n\n \u003Ch4\u003E\u003Ccode\u003EsetHiddenShippingInfoFields(List\u003CString\u003E hiddenFields)\u003C/code\u003E\u003C/h4\u003E\n \u003Cp\u003ESets which address fields should be hidden in the shipping information screen. Values must be from among the labels listed in \u003Ca href=\"https://github.com/stripe/stripe-android/blob/master/stripe/src/main/java/com/stripe/android/view/ShippingInfoWidget.java\" class=\"external\"\u003E\u003Ccode\u003EShippingInfoWidget.\u003C/code\u003E\u003C/a\u003E. All fields will be shown if this list is empty. Note that not all fields can be hidden, such as country or name.\n \u003C/p\u003E\n\n \u003Ch4\u003E\u003Ccode\u003EsetOptionalShippingInfoFields(List\u003CString\u003E hiddenFields)\u003C/code\u003E\u003C/h4\u003E\n \u003Cp\u003ESets which address fields should be listed as optional in the shipping information screen. Values must be from among the labels listed in \u003Ca href=\"https://github.com/stripe/stripe-android/blob/master/stripe/src/main/java/com/stripe/android/view/ShippingInfoWidget.java\" class=\"external\"\u003E\u003Ccode\u003EShippingInfoWidget.\u003C/code\u003E\u003C/a\u003E. All fields will be required if this list is empty. Note that not all fields can be optional, such as country or name.\n \u003C/p\u003E\n\n \u003Cp\u003E\n Having created the \u003Ccode\u003EPaymentSessionConfig\u003C/code\u003E, you'll need to create your \u003Ccode\u003EPaymentSessionListener\u003C/code\u003E, which has 3 required methods:\u003C/p\u003E\n\n \u003Ch4\u003E\u003Ccode\u003Evoid onPaymentSessionDataChanged(@NonNull PaymentSessionData data)\u003C/code\u003E\u003C/h4\u003E\n\n \u003Cp\u003EThis method is called whenever the PaymentSession's data changes, e.g., when the user selects a new payment method or enters shipping info. This is a good place to update your UI:\u003C/p\u003E\n\n\u003Cdiv class=\"code\" data-language=\"java\"\u003E\n \u003Cnav class=\"nav-filename icon-file\"\u003E\n MyPaymentSessionListener.java\n\u003C/nav\u003E\n\u003Cpre class=\"language-java numbered\" \u003E\u003Ccode\u003E@Override\npublic void onPaymentSessionDataChanged(@NonNull PaymentSessionData data) {\n final String selectedPaymentMethodId = data.getSelectedPaymentMethodId();\n CustomerSession.getInstance().retrieveCurrentCustomer(\n new CustomerRetrievalListener() {\n @Override\n public void onCustomerRetrieved(@NonNull Customer customer) {\n Source displaySource = customer.getSourceById(selectedPaymentMethodId);\n // Display the card information on your screen.\n }\n\n @Override\n public void onError(int errorCode, @Nullable String errorMessage){\n Toast.makeText(HostActivity.this, errorMesage, Toast.LENGTH_SHORT).show();\n }\n }\n );\n\n // Update your UI here with other data\n if (data.isPaymentReadyToCharge() \u0026amp;\u0026amp; data.getPaymentResult == PaymentResultListener.INCOMPLETE) {\n // Use the data to complete your charge - see below.\n }\n}\u003C/code\u003E\u003C/pre\u003E\n\n\u003C/div\u003E\n\n \u003Cp\u003EThis method should also check for whether or not the payment data is complete, according to the \u003Ccode\u003EPaymentSessionConfig\u003C/code\u003E specified. If you receive an update for which \u003Ccode\u003EPaymentSessionData#isPaymentReadyToCharge()\u003C/code\u003E returns true, you can immediately send a message to your server to complete the charge.\u003C/p\u003E\n\n \u003Ch4\u003E\u003Ccode\u003Evoid onCommunicatingStateChanged(boolean isCommunicating)\u003C/code\u003E\u003C/h4\u003E\n\n \u003Cp\u003EThis method is called whenever the network communication state has changed. We recommend showing a spinner or infinite progress bar when it is set to \u003Ccode\u003Etrue\u003C/code\u003E\u003C/p\u003E\n\n\u003Cdiv class=\"code\" data-language=\"java\"\u003E\n \u003Cnav class=\"nav-filename icon-file\"\u003E\n MyPaymentSessionListener.java\n\u003C/nav\u003E\n\u003Cpre class=\"language-java numbered\" \u003E\u003Ccode\u003Evoid onCommunicatingStateChanged(boolean isCommunicating) {\n if (isCommunicating) {\n mHostActivity.getProgressBar().setVisibility(View.VISIBLE);\n } else {\n mHostActivity.getProgressBar().setVisibility(View.GONE);\n }\n}\u003C/code\u003E\u003C/pre\u003E\n\n\u003C/div\u003E\n\n \u003Ch4\u003E\u003Ccode\u003Evoid onError(int errorCode, @Nullable String errorMessage)\u003C/code\u003E\u003C/h4\u003E\n\n \u003Cp\u003EThis method is called whenever an error occurs when connecting to the Stripe API. The error messages should be user-surfaceable, so displaying them in an alert dialog is recommended.\u003C/p\u003E\n\n \u003Ch4\u003EUsing the \u003Ccode\u003EPaymentSession\u003C/code\u003E to collect data\u003C/h4\u003E\n\n \u003Cp\u003EHaving created your \u003Ccode\u003EPaymentSessionConfig\u003C/code\u003E and \u003Ccode\u003EPaymentSessionListener\u003C/code\u003E, you can now initialize the \u003Ccode\u003EPaymentSession\u003C/code\u003E. In the below example, we use anonymous classes to create our listener and config for simplicity.\u003C/p\u003E\n\n\u003Cdiv class=\"code\" data-language=\"java\"\u003E\n \u003Cnav class=\"nav-filename icon-file\"\u003E\n HostActivity.java\n\u003C/nav\u003E\n\u003Cpre class=\"language-java numbered\" \u003E\u003Ccode\u003E private void setupPaymentSession() {\n mPaymentSession = new PaymentSession(this);\n boolean paymentSessionInitialized = mPaymentSession.init(new PaymentSession.PaymentSessionListener() {\n @Override\n public void onCommunicatingStateChanged(boolean isCommunicating) {\n if (isCommunicating) {\n mProgressBar.setVisibility(View.VISIBLE);\n } else {\n mProgressBar.setVisibility(View.INVISIBLE);\n }\n }\n\n @Override\n public void onError(int errorCode, @Nullable String errorMessage) {\n mErrorDialogHandler.showError(errorMessage);\n }\n\n @Override\n public void onPaymentSessionDataChanged(@NonNull PaymentSessionData data) {\n mResultTitleTextView.setVisibility(View.VISIBLE);\n mResultTextView.setText(formatStringResults(mPaymentSession.getPaymentSessionData()));\n }\n }, new PaymentSessionConfig.Builder()\n .setPrepopulatedShippingInfo(getExampleShippingInfo())\n .build());\n if (paymentSessionInitialized) {\n mStartPaymentFlowButton.setEnabled(true);\n }\n}\u003C/code\u003E\u003C/pre\u003E\n\n\u003C/div\u003E\n\n \u003Cp\u003ENote that the \u003Ccode\u003Einit\u003C/code\u003E function returns a \u003Ccode\u003Eboolean\u003C/code\u003E value to let you know whether or not the initialization was successful. The only reason for failure is if you have not already set up a \u003Ccode\u003ECustomerSession\u003C/code\u003E, which must come first.\u003C/p\u003E\n\n \u003Cp\u003EOnce the \u003Ccode\u003EPaymentSession\u003C/code\u003E has been initialized, you can use it to make the following calls.\u003C/p\u003E\n\n \u003Ch4\u003E\u003Ccode\u003Evoid presentPaymentMethodSelection()\u003C/code\u003E\u003C/h4\u003E\n\n \u003Cp\u003EThis method presents the PaymentMethodsActivity to allow the user to choose a stored form of payment or to add more forms of payment and select from among those. It will automatically be pre-populated with values from your \u003Ccode\u003ECustomerSession\u003C/code\u003E, so previously existing payment methods will appear without any extra work.\u003C/p\u003E\n\n \u003Ch4\u003E\u003Ccode\u003Evoid presentShippingFlow()\u003C/code\u003E\u003C/h4\u003E\n\n \u003Cp\u003EThis method presents the PaymentFlowActivity to allow the user to enter shipping information, if such information is required according to your \u003Ccode\u003EPaymentSessionConfig\u003C/code\u003E. Note that in order to validate the input shipping address, you'll need to register a local \u003Ccode\u003EBroadcastReceiver\u003C/code\u003E.\u003C/p\u003E\n\n\u003Cdiv class=\"code\" data-language=\"java\"\u003E\n \u003Cnav class=\"nav-filename icon-file\"\u003E\n HostActivity.java\n\u003C/nav\u003E\n\u003Cpre class=\"language-java numbered\" \u003E\u003Ccode\u003E\n@Override\nprotected void onCreate(Bundle savedInstance) {\n // Create the BroadcastReceiver in onCreate\n mBroadcastReceiver = new BroadcastReceiver() {\n @Override\n public void onReceive(Context context, Intent intent) {\n ShippingInformation shippingInformation = intent.getParcelableExtra(EXTRA_SHIPPING_INFO_DATA);\n Intent shippingInfoProcessedIntent = new Intent(EVENT_SHIPPING_INFO_PROCESSED);\n if (shippingInformation.getAddress() == null || !shippingInformation.getAddress().getCountry().equals(Locale.US.getCountry())) {\n // In this example, we check to see that the country is US. Your logic will probably be more complicated.\n shippingInfoProcessedIntent.putExtra(EXTRA_IS_SHIPPING_INFO_VALID, false);\n } else {\n ArrayList\u0026lt;ShippingMethod\u0026gt; shippingMethods = createSampleShippingMethods();\n shippingInfoProcessedIntent.putExtra(EXTRA_IS_SHIPPING_INFO_VALID, true);\n shippingInfoProcessedIntent.putParcelableArrayListExtra(EXTRA_VALID_SHIPPING_METHODS, shippingMethods);\n shippingInfoProcessedIntent.putExtra(EXTRA_DEFAULT_SHIPPING_METHOD, shippingMethods.get(1));\n }\n LocalBroadcastManager.getInstance(PaymentSessionActivity.this).sendBroadcast(shippingInfoProcessedIntent);\n }\n };\n // Register in onCreate\n LocalBroadcastManager.getInstance(this).registerReceiver(mBroadcastReceiver,new IntentFilter(EVENT_SHIPPING_INFO_SUBMITTED));\n }\n\n @Override\n public void onDestroy() {\n // Don\u0026#39;t forget to unregister in onDestroy!\n LocalBroadcastManager.getInstance(this).unregisterReceiver(mBroadcastReceiver);\n }\u003C/code\u003E\u003C/pre\u003E\n\n\u003C/div\u003E\n\n \u003Cp\u003E\n When you broadcast your response to the shipping validation request, you can then send down your (potentially) dynamically created shipping methods. This allows you to validate whether or not a customer is eligible for expedited shipping, for instance, based on the input shipping address. This is also the place to filter out locations that you cannot ship to, for instance if you are restricted to domestic customers.\n \u003C/p\u003E\n\n \u003Ch4\u003ECompleting the purchase\u003C/h4\u003E\n\n \u003Cp\u003E\n When you have detected that the \u003Ccode\u003EPaymentSession#isPaymentReadyToCharge()\u003C/code\u003E has come back \u003Ccode\u003Etrue\u003C/code\u003E, you are free to charge the payment method immediately with the data in the \u003Ccode\u003EPaymentSessionData\u003C/code\u003E object using a call to your server. To make full use of the \u003Ccode\u003EPaymentSession\u003C/code\u003E and guard against duplicate payment requests, we recommend making use of the \u003Ccode\u003EcompletePayment\u003C/code\u003E method.\n \u003C/p\u003E\n\n \u003Ch4\u003E\u003Ccode\u003Evoid completePayment(@NonNull PaymentCompletionProvider provider)\u003C/code\u003E\u003C/h4\u003E\n\n \u003Cp\u003EThis method will call the complete payment method on the provider, talking to your server to make the purchase, and recording the result data in the \u003Ccode\u003EPaymentSessionData\u003C/code\u003E To use this method, you must provide a \u003Ccode\u003EPaymentCompletionProvider\u003C/code\u003E, which requires the implementation of just one method:\u003C/p\u003E\n\n \u003Ch4\u003E\u003Ccode\u003Evoid completePayment(@NonNull PaymentSessionData data, @NonNull PaymentResultListener listener)\u003C/code\u003E\u003C/h4\u003E\n\n \u003Cp\u003EThis method will be invoked with the current \u003Ccode\u003EPaymentSessionData\u003C/code\u003E object, and the results will be returned to the \u003Ccode\u003EPaymentResultListener\u003C/code\u003E (which you do not need to create). Any errors will be returned to your original \u003Ccode\u003EPaymentSessionListener\u003C/code\u003E, and a successful update will result in a call to the same \u003Ccode\u003EonPaymentSessionDataChanged\u003C/code\u003E method used to update the listener about any other change in the \u003Ccode\u003EPaymentSessionData\u003C/code\u003E. To verify results, call \u003Ccode\u003EPaymentSessionData#getPaymentResult()\u003C/code\u003E, which returns a \u003Ca href=\"https://github.com/stripe/stripe-android/blob/master/stripe/src/main/java/com/stripe/android/PaymentResultListener.java\" class=\"external\"\u003EPaymentResult\u003C/a\u003E.\u003C/p\u003E\n\n \u003Ch4\u003EManaging PaymentSession in a host Activity\u003C/h4\u003E\n\n \u003Cp\u003EIn order to get updates for the \u003Ccode\u003EPaymentSessionData\u003C/code\u003E object and to handle state during Activity lifecycle, you'll need to hook up your \u003Ccode\u003EPaymentSession\u003C/code\u003E instance to a few key parts of your host Activity lifecycle. The first is in \u003Ccode\u003EonActivityResult\u003C/code\u003E\u003C/p\u003E\n\n\u003Cdiv class=\"code\" data-language=\"java\"\u003E\n \u003Cnav class=\"nav-filename icon-file\"\u003E\n HostActivity.java\n\u003C/nav\u003E\n\u003Cpre class=\"language-java numbered\" \u003E\u003Ccode\u003E@Override\nprotected void onActivityResult(int requestCode, int resultCode, Intent data) {\n super.onActivityResult(requestCode, resultCode, data);\n mPaymentSession.handlePaymentData(requestCode, resultCode, data);\n}\u003C/code\u003E\u003C/pre\u003E\n\n\u003C/div\u003E\n\n \u003Cp\u003EThis is all you need to do to get updates from the various activities launched by \u003Ccode\u003EPaymentSession\u003C/code\u003E. Any updates to the data will be reported to the listener attached during the initialization of the \u003Ccode\u003EPaymentSession\u003C/code\u003E. To handle lifecycle events in your activity that require the saving and restoring of state, the session can be attached and re-initialized as follows:\u003C/p\u003E\n\n\u003Cdiv class=\"code\" data-language=\"java\"\u003E\n \u003Cnav class=\"nav-filename icon-file\"\u003E\n HostActivity.java\n\u003C/nav\u003E\n\u003Cpre class=\"language-java numbered\" \u003E\u003Ccode\u003E@Override\nprotected void onActivityResult(int requestCode, int resultCode, Intent data) {\n super.onActivityResult(requestCode, resultCode, data);\n mPaymentSession.handlePaymentData(requestCode, resultCode, data);\n}\u003C/code\u003E\u003C/pre\u003E\n\n\u003C/div\u003E\n\n \u003Cp\u003EIf saving state during activity lifecycle events is important, be sure to pass the \u003Ccode\u003EsavedInstanceState\u003C/code\u003E into the \u003Ccode\u003Einit\u003C/code\u003E method of the PaymentSession.\u003C/p\u003E\n\n\u003Cdiv class=\"code\" data-language=\"java\"\u003E\n \u003Cnav class=\"nav-filename icon-file\"\u003E\n HostActivity.java\n\u003C/nav\u003E\n\u003Cpre class=\"language-java numbered\" \u003E\u003Ccode\u003E@Override\npublic void onSaveInstanceState(Bundle outState) {\n super.onSaveInstanceState(outState);\n mPaymentSession.savePaymentSessionInstanceState(outState);\n}\n\n// Can also be re-initialized in onRestoreInstanceState\n@Override\npublic void onCreate(@Nullable Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n\n // other onCreate logic\n mPaymentSession = new PaymentSession(this);\n // Create your listener and your configuration\n // ...\n // It\u0026#39;s fine if the savedInstanceState is null.\n mPaymentSession.init(mPaymentSessionListener, mPaymentSessionConfig, savedInstanceState);\n}\u003C/code\u003E\u003C/pre\u003E\n\n\u003C/div\u003E\n\n \u003Cp\u003EFinally, don't forget to destroy the \u003Ccode\u003EPaymentSession\u003C/code\u003E when your activity is destroyed. This clears out any listeners and references that you may have given the PaymentSession object that could have been put in different threads.\u003C/p\u003E\n\n\u003Cdiv class=\"code\" data-language=\"java\"\u003E\n \u003Cnav class=\"nav-filename icon-file\"\u003E\n HostActivity.java\n\u003C/nav\u003E\n\u003Cpre class=\"language-java numbered\" \u003E\u003Ccode\u003E@Override\nprotected void onDestroy() {\n mPaymentSession.onDestroy();\n super.onDestroy();\n}\u003C/code\u003E\u003C/pre\u003E\n\n\u003C/div\u003E\n\u003C/section\u003E\n\u003Cfooter\u003E\n\n\u003Ch2 data-no-anchor\u003EQuestions?\u003C/h2\u003E\n\n\u003Cp\u003E\n We're always happy to help with code or other questions you might have. \u003Ca href='#' id='search-link' class='search-link'\u003ESearch our documentation\u003C/a\u003E, \u003Ca href='https://support.stripe.com/contact'\u003Econtact support\u003C/a\u003E, or \u003Ca href='https://stripe.com/contact/sales'\u003Econnect with our sales team\u003C/a\u003E. You can also chat live with other developers in \u003Ca href=\"irc://irc.freenode.net/stripe\"\u003E#stripe\u003C/a\u003E on freenode.\n\u003C/p\u003E\n\u003Cscript nonce=\"SP8Jr5ieNEXVWogHsHvWcg==\"\u003E document.getElementById(\"search-link\").addEventListener(\"click\", focusSearch);\n function focusSearch() {\n document.querySelector(\".search input\").focus();\n window.scrollTo(0,0);\n }\n\u003C/script\u003E\n\u003Cp data-csat-step=\"poll\" class=\"csat-widget\"\u003E\n \u003Cspan class=\"csat-widget-text\"\u003EWas this page helpful?\u003C/span\u003E\n \u003Cspan data-csat-poll=\"yes\" class=\"csat-button csat-button-yes common-Button\"\u003EYes\u003C/span\u003E\n \u003Cspan data-csat-poll=\"no\" class=\"csat-button csat-button-no common-Button\"\u003ENo\u003C/span\u003E\n\u003C/p\u003E\n\n\u003Cdiv data-csat-step=\"input\" class=\"csat-widget\"\u003E\n \u003Cdiv class=\"csat-input\"\u003E\n \u003Cinput id=\"csat-input-text\" placeholder=\"Feedback about this page?\" class=\"common-Input\"\u003E\n \u003Cspan id=\"csat-input-submit\" class=\"csat-button common-Link\"\u003ESend\u003C/span\u003E\n \u003C/div\u003E\n \u003Cp class=\"csat-contact\"\u003E\n \u003Clabel class=\"csat-contact-option\"\u003E\n \u003Cinput id=\"csat-contact-optin-checkbox\" type=\"checkbox\"\u003E\n Yes, it’s okay to follow up by email.\n \u003C/label\u003E\n \u003C/p\u003E\n\u003C/div\u003E\n\n\u003Cp data-csat-step=\"reply\" class=\"csat-widget\"\u003E\n \u003Cspan class=\"csat-reply-main\"\u003EThank you for helping improve Stripe's documentation.\u003C/span\u003E\n \u003Cspan class=\"csat-reply-extended\"\u003EIf you need help or have any questions, please consider \u003Ca href=\"https://support.stripe.com/email\"\u003Econtacting support\u003C/a\u003E.\u003C/span\u003E\n\u003C/p\u003E\n\n\n\u003C/footer\u003E\n","head_css":" \u003Clink rel=\"stylesheet\" href=\"/assets/compiled/js/../css/sprockets-css-v3/documentation/mobile-c5432e80cc7c0ccf95f7.min.css\"\u003E\n \u003Cstyle\u003E\n section.intro ul {\n column-count: 3;\n list-style: none;\n margin: 15px 0;\n }\n \u003C/style\u003E\n","documentation_footer_js":"","title":"Android Standard UI Components | Stripe Payments","translated":null}