{"html":"\n\u003Cheader\u003E\n \u003Ch1\u003ESaving Customer Card Information\u003C/h1\u003E\n \u003Cp\u003EAllow customers to securely store and update payment information.\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 will take you through building your app's customer payment management flow using \u003Ccode\u003ECustomerSession\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 — 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 will specify 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 will talk to your backend to retrieve an ephemeral key (via its key provider), and use 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 will automatically prefetch 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\u003EendCustomerSession\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\u003ELet your user select their payment method\u003C/h3\u003E\n\n \u003Cp\u003EUse the PaymentMethodsActivity to display a list of payment methods to the user, so they can select their preferred payment method. To launch the activity, use the static \u003Ccode\u003EnewIntent\u003C/code\u003E method of the PaymentMethodsActivity class to create an appropriate \u003Ccode\u003EIntent\u003C/code\u003E, then start the activity for result.\u003C/p\u003E\n\n\u003Cdiv class=\"code\" data-language=\"java\"\u003E\n \u003Cnav class=\"nav-filename icon-file\"\u003E\n CustomerSessionActivity.java\n\u003C/nav\u003E\n\u003Cpre class=\"language-java numbered\" \u003E\u003Ccode\u003EIntent payIntent = PaymentMethodsActivity.newIntent(this // your activity);\nstartActivityForResult(payIntent, REQUEST_CODE_SELECT_SOURCE);\n\u003C/code\u003E\u003C/pre\u003E\n\n\u003C/div\u003E\n\u003Cp\u003E\nThen, in the \u003Ccode\u003EonActivityResult\u003C/code\u003E method of your host activity, handle the feedback.\n\u003C/p\u003E\n\n\u003Cdiv class=\"code\" data-language=\"java\"\u003E\n \u003Cnav class=\"nav-filename icon-file\"\u003E\n CustomerSessionActivity.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 if (requestCode == REQUEST_CODE_SELECT_SOURCE \u0026amp;\u0026amp; resultCode == RESULT_OK) {\n String selectedSource = data.getStringExtra(PaymentMethodsActivity.EXTRA_SELECTED_PAYMENT);\n Source source = Source.fromString(selectedSource);\n // This is the customer-selected source.\n // Note: it isn\u0026#39;t possible for a null or non-card source to be returned at this time.\n }\n}\n\u003C/code\u003E\u003C/pre\u003E\n\n\u003C/div\u003E\n\n \u003Cdiv class=\"image full\"\u003E\n \u003Cimg src=\"/img/docs/mobile/android/android-source-selection-framed.png\" width=\"282\" height=\"489\" alt=\"PaymentMethodsActivity\" /\u003E\n \u003Cp\u003EChoosing from stored payment methods in the \u003Ccode\u003EPaymentMethodsActivity\u003C/code\u003E\u003C/p\u003E\n \u003C/div\u003E\n\n\u003Cp\u003E\n Note that this activity can launch the \u003Ca href=\"https://github.com/stripe/stripe-android/blob/master/stripe/src/main/java/com/stripe/android/view/AddSourceActivity.java\" class=\"external\"\u003E\u003Ccode\u003EAddSourceActivity\u003C/code\u003E\u003C/a\u003E if a customer wants to add a new payment method. No changes to your code are necessary to accommodate this.\n\u003C/p\u003E\n\n \u003Cdiv class=\"image full\"\u003E\n \u003Cimg src=\"/img/docs/mobile/android/android-card-multiline-framed.png\" width=\"282\" height=\"489\" alt=\"AddSourceActivity\" /\u003E\n \u003Cp\u003EAdding a new card in the \u003Ccode\u003EAddSourceActivity\u003C/code\u003E\u003C/p\u003E\n \u003C/div\u003E\n\n \u003C/section\u003E\n\n\u003Csection\u003E\n \u003Ch2 id=\"nonui\"\u003ECustomerSession Without UI\u003C/h2\u003E\n\n \u003Cp\u003E\n If you need more flexibility than our UI will allow, you can access all of the functionality of \u003Ccode\u003ECustomerSession\u003C/code\u003E without invoking any UI at all. You'll still need to initialize your \u003Ccode\u003ECustomerSession\u003C/code\u003E as before. After it has been initialized, you can call the following methods.\n \u003C/p\u003E\n\n\u003Ch4\u003E\u003Ccode\u003Evoid retrieveCurrentCustomer\u003C/code\u003E\u003C/h4\u003E\n\u003Cp\u003E\n Arguments:\n\u003C/p\u003E\n\u003Cul\u003E\n \u003Cli\u003E\u003Ccode\u003ECustomerRetrievalListener\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EAs you might expect, this function retrieves the current customer object. It is asynchronous, and first checks whether or not the current customer object is expired. If it is, that object is returned immediately. If not, it makes a call to update the ephemeral key and get a new customer object. This is a good function to call the first time you need a customer object. The retrieved \u003Ccode\u003ECustomer\u003C/code\u003E object is returned via the input \u003Ccode\u003ECustomerRetrievalListener\u003C/code\u003E object.\u003C/p\u003E\n\n\u003Cdiv class=\"code\" data-language=\"java\"\u003E\n \u003Cpre class=\"language-java numbered\" \u003E\u003Ccode\u003Epublic interface CustomerRetrievalListener {\n void onCustomerRetrieved(@NonNull Customer customer);\n void onError(int errorCode, @Nullable String errorMessage);\n}\n\u003C/code\u003E\u003C/pre\u003E\n\n\u003C/div\u003E\n\n\u003Ch4\u003E\u003Ccode\u003Evoid updateCurrentCustomer\u003C/code\u003E\u003C/h4\u003E\n\u003Cp\u003E\n Arguments:\n\u003C/p\u003E\n\u003Cul\u003E\n \u003Cli\u003E\u003Ccode\u003ECustomerRetrievalListener\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis function forces a call to the server to update the Customer object, regardless of whether or not the current customer is expired. This is good to call when you have just added a source to a customer, as the cached Customer object will not yet have the update.\u003C/p\u003E\n\n\u003Ch4\u003E\u003Ccode\u003Epublic void addCustomerSource\u003C/code\u003E\u003C/h4\u003E\n\u003Cp\u003E\n Arguments:\n\u003C/p\u003E\n\u003Cul\u003E\n \u003Cli\u003E\u003Ccode\u003EContext\u003C/code\u003E\u003C/li\u003E\n \u003Cli\u003E\u003Ccode\u003EString sourceId\u003C/code\u003E\u003C/li\u003E\n \u003Cli\u003E\u003Ccode\u003E@Source.SourceType String sourceType\u003C/code\u003E\u003C/li\u003E\n \u003Cli\u003E\u003Ccode\u003ESourceRetrievalListener\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis function adds a payment method to a customer based on the ID and type of that source. Note that at the moment, only sources of type CARD are supported. The \u003Ccode\u003ESource\u003C/code\u003E object added to the \u003Ccode\u003ECustomer\u003C/code\u003E will be returned via the input \u003Ccode\u003ESourceRetrievalListener\u003C/code\u003E.\u003C/p\u003E\n\n\u003Cdiv class=\"code\" data-language=\"java\"\u003E\n \u003Cpre class=\"language-java numbered\" \u003E\u003Ccode\u003Epublic interface SourceRetrievalListener {\n void onSourceRetrieved(@NonNull Source source);\n void onError(int errorCode, @Nullable String errorMessage);\n}\n\u003C/code\u003E\u003C/pre\u003E\n\n\u003C/div\u003E\n\n\u003Ch4\u003E\u003Ccode\u003Epublic void setCustomerDefaultSource\u003C/code\u003E\u003C/h4\u003E\n\u003Cp\u003E\n Arguments:\n\u003C/p\u003E\n\u003Cul\u003E\n \u003Cli\u003E\u003Ccode\u003EContext\u003C/code\u003E\u003C/li\u003E\n \u003Cli\u003E\u003Ccode\u003EString sourceId\u003C/code\u003E\u003C/li\u003E\n \u003Cli\u003E\u003Ccode\u003E@Source.SourceType String sourceType\u003C/code\u003E\u003C/li\u003E\n \u003Cli\u003E\u003Ccode\u003ECustomerRetrievalListener\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis function sets the default payment method of the current \u003Ccode\u003ECustomer\u003C/code\u003E. Note that at the moment, only sources of type CARD are supported. The updated \u003Ccode\u003ECustomer\u003C/code\u003E object will be returned via that input \u003Ccode\u003ECustomerRetrievalListener\u003C/code\u003E.\u003C/p\u003E\n\n\u003C/section\u003E\n\n\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=\"qGtmbmolgTKaluOcDdGfFA==\"\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-4c19b17543f21fdd1ef7.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":"Managing Customer Information in the Android SDK | Stripe Payments","translated":null}