Managing Customer Information in the Android SDK

    Allow customers to securely store and update payment information.

    This guide will take you through building your app's customer payment management flow using CustomerSession, 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.

    Prepare your API

    Our prebuilt UI elements operate on the assumption that you have a single Customer object for each of your users. We strongly recommend you create this Customer at the same time you create your user on your own backend, so that every user is guaranteed to have an associated Customer. (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.)

    Our 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.

    When 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 api_version 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 Example Backend to see this in practice.

    # Sinatra
    post path do
      stripe_version = params['api_version']
      customer_id = session['customer_id']
      key = Stripe::EphemeralKey.create(
        {customer: customer_id},
        {stripe_version: stripe_version}
      )
      key.to_json
    end
    # Flask
    from flask import Flask, session, jsonify, request
    # This function assumes that the session handling has stored the customerId
    @app.route(path, methods=['POST'])
    def issue_key():
        stripe_api_version = request.args['api_version']
        customerId = session['customerId']
        key = stripe.EphemeralKey.create(customer=customerId, api_version=stripe_api_version)
        return jsonify(key)
    // This assumes that $customerId has been set appropriately from session data
    parse_str($_SERVER['QUERY_STRING'], $params);
    if (!isset($params['api_version']))
    {
        exit(http_response_code(400));
    }
    try {
        $key = \Stripe\EphemeralKey::create(
          array("customer" => $customerId),
          array("stripe_version" => $params['api_version'])
        );
        header('Content-Type: application/json');
        exit(json_encode($key));
    } catch (Exception $e) {
        exit(http_response_code(500));
    }
    // Express
    app.post(path, (req, res) => {
      const stripe_version = req.query.api_version;
      if (!stripe_version) {
        res.status(400).end();
        return;
      }
      // This function assumes that some previous middleware has determined the
      // correct customerId for the session and saved it on the request object.
      stripe.ephemeralKeys.create(
        {customer: req.customerId},
        {stripe_version: stripe_version}
      ).then((key) => {
        res.status(200).json(key);
      }).catch((err) => {
        res.status(500).end();
      });
    });
    // net/http
    // The customerId parameter should be the ID of the Customer object associated
    // with the session the request was made on.
    func issueKeyHandler(w http.ResponseWriter, r *http.Request, customerId string) {
        r.ParseForm()
        stripeVersion := r.Form.Get("api_version")
        if stripeVersion == "" {
            log.Printf("Stripe-Version not found\n")
            w.WriteHeader(400)
            return
        }
        key, err := ephemeralkey.New(&stripe.EphemeralKeyParams{
            Customer:      customerId,
            StripeVersion: stripeVersion,
        })
        if err != nil {
            log.Printf("Stripe bindings call failed, %v\n", err)
            w.WriteHeader(500)
            return
        }
        w.Write(key.RawJSON)
    }

    After 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 EphemeralKeyProvider interface, which defines a single method, createEphemeralKey. When implementing this method, be sure to pass the apiVersion parameter along to your ephemeral keys endpoint. You can consult our Example App to see this in practice.

    // Using RxJava for handling asynchronous responses
    @Override
    public void createEphemeralKey(@NonNull @Size(min = 4) String apiVersion,
                                 @NonNull final EphemeralKeyUpdateListener keyUpdateListener) {
      Map<String, String> apiParamMap = new HashMap<>();
      apiParamMap.put("api_version", apiVersion);
    
      mCompositeSubscription.add(
              mStripeService.createEphemeralKey(apiParamMap)
                      .subscribeOn(Schedulers.io())
                      .observeOn(AndroidSchedulers.mainThread())
                      .subscribe(new Action1<ResponseBody>() {
                          @Override
                          public void call(ResponseBody response) {
                              try {
                                  String rawKey = response.string();
                                  keyUpdateListener.onKeyUpdate(rawKey);
                                  mProgressListener.onStringResponse(rawKey);
                              } catch (IOException iox) {
    
                              }
                          }
                      }, new Action1<Throwable>() {
                          @Override
                          public void call(Throwable throwable) {
                              mProgressListener.onStringResponse(throwable.getMessage());
                          }
                      }));
    }
    
    

    After creating an EphemeralKeyProvider, initialize a CustomerSession. Think of CustomerSession as a Stripe communication manager for a single logged-in session of a customer. A CustomerSession 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.

    CustomerSession.initCustomerSession(
            new ExampleEphemeralKeyProvider(
                new ExampleEphemeralKeyProvider.ProgressListener() {
                    @Override
                    public void onStringResponse(String string) {
                        if (string.startsWith("Error: ")) {
                            // Show the error to the user.
                        }
                    }
                }));
    
    

    CustomerSession 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 CustomerSession instance earlier, before your user enters your payment flow. If your current user logs out of the app, you should clear the current CustomerSession singleton using the clearInstance 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.

    Let your user select their payment method

    Use the PaymentMethodsActivity to display a list of payment methods to the user, so they can select their preferred payment source. To launch the activity, use the static newIntent method of the PaymentMethodsActivity class to create an appropriate Intent, then start the activity for result.

    Intent payIntent = PaymentMethodsActivity.newIntent(this // your activity);
    startActivityForResult(payIntent, REQUEST_CODE_SELECT_SOURCE);
    

    Then, in the onActivityResult method of your host activity, handle the feedback.

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_SELECT_SOURCE && resultCode == RESULT_OK) {
            String selectedSource = data.getStringExtra(PaymentMethodsActivity.EXTRA_SELECTED_PAYMENT);
            Source source = Source.fromString(selectedSource);
            // This is the customer-selected source.
            // Note: it isn't possible for a null or non-card source to be returned at this time.
        }
    }
    
    PaymentMethodsActivity

    Choosing from stored payment sources in the PaymentMethodsActivity

    Note that this activity can launch the AddSourceActivity if a customer wants to add a new payment source. No changes to your code are necessary to accommodate this.

    AddSourceActivity

    Adding a new card in the AddSourceActivity

    CustomerSession Without UI

    If you need more flexibility than our UI will allow, you can access all of the functionality of CustomerSession without invoking any UI at all. You'll still need to initialize your CustomerSession as before. After it has been initialized, you can call the following methods.

    void retrieveCurrentCustomer

    Arguments:

    • CustomerRetrievalListener

    As 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 Customer object is returned via the input CustomerRetrievalListener object.

    public interface CustomerRetrievalListener {
        void onCustomerRetrieved(@NonNull Customer customer);
        void onError(int errorCode, @Nullable String errorMessage);
    }
    

    void updateCurrentCustomer

    Arguments:

    • CustomerRetrievalListener

    This 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.

    public void addCustomerSource

    Arguments:

    • Context
    • String sourceId
    • @Source.SourceType String sourceType
    • SourceRetrievalListener

    This function adds a payment source 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 Source object added to the Customer will be returned via the input SourceRetrievalListener.

    public interface SourceRetrievalListener {
        void onSourceRetrieved(@NonNull Source source);
        void onError(int errorCode, @Nullable String errorMessage);
    }
    

    public void setCustomerDefaultSource

    Arguments:

    • Context
    • String sourceId
    • @Source.SourceType String sourceType
    • CustomerRetrievalListener

    This function sets the default payment source of the current Customer. Note that at the moment, only sources of type CARD are supported. The updated Customer object will be returned via that input CustomerRetrievalListener.