Am trying to add Stripe to my android app with Firebase. I set the publishable key in gradle.properties and builtype in gradle.build then call in application as BuildConfig.PublishableKey
Every time I try to add card a dialog pops up with this warning...
unexpected char 0x0a at 9 header value: bearer pk_test_xxxxkeyxxxx
Any ideas? Am using prebuilt UI from stripe too (presentPaymentMethodSelection() ) following their Firebase mobile payments android
var RC_SIGN_IN = 1
class MainActivityStripe : AppCompatActivity() {
    private var currentUser: FirebaseUser? = null
    private lateinit var paymentSession: PaymentSession
    private lateinit var selectedPaymentMethod: PaymentMethod
    private val stripe: Stripe by lazy { Stripe(applicationContext,
        PaymentConfiguration.getInstance(applicationContext).publishableKey) }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main_stripe)
        currentUser = FirebaseAuth.getInstance().currentUser
        payButton.isEnabled = false
        loginButton.setOnClickListener {
            // login to firebase
            val providers = arrayListOf(
                AuthUI.IdpConfig.EmailBuilder().build())
            startActivityForResult(
                AuthUI.getInstance()
                    .createSignInIntentBuilder()
                    .setAvailableProviders(providers)
                    .build(),
                RC_SIGN_IN
            )
        }
        payButton.setOnClickListener {
            confirmPayment(selectedPaymentMethod.id!!)
        }
        paymentmethod.setOnClickListener {
            Toast.makeText(applicationContext, PaymentConfiguration.getInstance(applicationContext).publishableKey, Toast.LENGTH_LONG).show()
            // Create the customer session and kick start the payment flow
            paymentSession.presentPaymentMethodSelection()
        }
        showUI()
    }
    private fun confirmPayment(paymentMethodId: String) {
        val paymentCollection = Firebase.firestore
            .collection("stripe_customers").document(currentUser?.uid?:"")
            .collection("payments")
        // Add a new document with a generated ID
        paymentCollection.add(hashMapOf(
            "amount" to 8800,
            "currency" to "cad"
        ))
            .addOnSuccessListener { documentReference ->
                Log.d("paymentsss", "DocumentSnapshot added with ID: ${documentReference.id}")
                documentReference.addSnapshotListener { snapshot, e ->
                    if (e != null) {
                        Log.w("paymentsss", "Listen failed.", e)
                        return@addSnapshotListener
                    }
                    if (snapshot != null && snapshot.exists()) {
                        Log.d("paymentsss", "Current data: ${snapshot.data}")
                        val clientSecret = snapshot.data?.get("client_secret")
                        Log.d("paymentsss", "Create paymentIntent returns $clientSecret")
                        clientSecret?.let {
                            stripe.confirmPayment(this, ConfirmPaymentIntentParams.createWithPaymentMethodId(
                                paymentMethodId,
                                (it as String)
                            ))
                            checkoutSummary.text = "Thank you for your payment"
                            Toast.makeText(applicationContext, "Payment Done!!", Toast.LENGTH_LONG).show()
                        }
                    } else {
                        Log.e("paymentsss", "Current payment intent : null")
                        payButton.isEnabled = true
                    }
                }
            }
            .addOnFailureListener { e ->
                Log.w("paymentsss", "Error adding document", e)
                payButton.isEnabled = true
            }
    }
    private fun showUI() {
        currentUser?.let {
            loginButton.visibility = View.INVISIBLE
            greeting.visibility = View.VISIBLE
            checkoutSummary.visibility = View.VISIBLE
            payButton.visibility = View.VISIBLE
            paymentmethod.visibility = View.VISIBLE
            greeting.text = "Hello ${it.displayName}"
            setupPaymentSession()
        }?: run {
            // User does not login
            loginButton.visibility = View.VISIBLE
            greeting.visibility = View.INVISIBLE
            checkoutSummary.visibility = View.INVISIBLE
            paymentmethod.visibility = View.INVISIBLE
            payButton.visibility = View.INVISIBLE
            payButton.isEnabled = false
        }
    }
    private fun setupPaymentSession () {
        // Setup Customer Session
        CustomerSession.initCustomerSession(this, FirebaseEphemeralKeyProvider())
        // Setup a payment session
        paymentSession = PaymentSession(this, PaymentSessionConfig.Builder()
            .setShippingInfoRequired(false)
            .setShippingMethodsRequired(false)
            .setBillingAddressFields(BillingAddressFields.None)
            .setShouldShowGooglePay(true)
            .build())
        paymentSession.init(
            object: PaymentSession.PaymentSessionListener {
                override fun onPaymentSessionDataChanged(data: PaymentSessionData) {
                    Log.d("PaymentSession1", "11PaymentSession has changed: $data")
                    Log.d("PaymentSession11", "1111 ${data.isPaymentReadyToCharge} <> ${data.paymentMethod}")
                    if (data.isPaymentReadyToCharge) {
                        Log.d("PaymentSession2", "222Ready to charge");
                        payButton.isEnabled = true
                        data.paymentMethod?.let {
                            Log.d("PaymentSession3", "333PaymentMethod $it selected")
                            paymentmethod.text = "${it.card?.brand} card ends with ${it.card?.last4}"
                            selectedPaymentMethod = it
                        }
                    }
                }
                override fun onCommunicatingStateChanged(isCommunicating: Boolean) {
                    Log.d("PaymentSession4",  "444isCommunicating $isCommunicating")
                }
                override fun onError(errorCode: Int, errorMessage: String) {
                    Log.e("PaymentSession5",  "555onError: $errorCode, $errorMessage")
                }
            }
        )
    }
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == RC_SIGN_IN) {
            val response = IdpResponse.fromResultIntent(data)
            if (resultCode == Activity.RESULT_OK) {
                currentUser = FirebaseAuth.getInstance().currentUser
                Log.d("Login", "User ${currentUser?.displayName} has signed in.")
                Toast.makeText(applicationContext, "Welcome ${currentUser?.displayName}", Toast.LENGTH_SHORT).show()
                showUI()
            } else {
                Log.d("Login", "Signing in failed!")
                Toast.makeText(applicationContext, response?.error?.message?:"Sign in failed", Toast.LENGTH_LONG).show()
            }
        } else {
            paymentSession.handlePaymentData(requestCode, resultCode, data ?: Intent())
        }
    }
}**
'use strict';
  const functions = require('firebase-functions');
  const admin = require('firebase-admin');
  const { Logging } = require('@google-cloud/logging');
  const logging = new Logging({
    projectId: process.env.GCLOUD_PROJECT,
  });
  admin.initializeApp();
  const stripe = require('stripe')(functions.config().stripe.secret, {
     apiVersion: '2020-03-02',
   });
  exports.createEphemeralKey = functions.https.onCall(async (data, context) => {
      // Checking that the user is authenticated.
      if (!context.auth) {
        // Throwing an HttpsError so that the client gets the error details.
        throw new functions.https.HttpsError(
          'failed-precondition',
          'The function must be called while authenticated!'
        );
      }
      const uid = context.auth.uid;
      try {
            if (!uid) throw new Error('Not authenticated!');
                // Get stripe customer id
            const customer = (
              await admin.firestore().collection('stripe_customers').doc(uid).get()
            ).data().customer_id;
            const key = await stripe.ephemeralKeys.create(
              { customer: customer },
              { apiVersion: data.api_version }
            );
            return key;
      } catch (error) {
            throw new functions.https.HttpsError('internal', error.message);
      }
  });
  exports.createStripeCustomer = functions.auth.user().onCreate(async (user) => {
        const customer = await stripe.customers.create({
          email: user.email,
          metadata: { firebaseUID: user.uid },
        });
        await admin.firestore().collection('stripe_customers').doc(user.uid).set({
          customer_id: customer.id,
        });
        return;
  });
  exports.createStripePayment = functions.firestore
    .document('stripe_customers/{userId}/payments/{pushId}')
    .onCreate(async (snap, context) => {
    const { amount, currency } = snap.data();
    try {
      // Look up the Stripe customer id.
      const customer = (await snap.ref.parent.parent.get()).data().customer_id;
      // Create a charge using the pushId as the idempotency key
      // to protect against double charges.
      const idempotencyKey = context.params.pushId;
      const payment = await stripe.paymentIntents.create(
        {
          amount,
          currency,
          customer,
        },
        { idempotencyKey }
      );
      // If the result is successful, write it back to the database.
      await snap.ref.set(payment);
    } catch (error) {
      // We want to capture errors and render them in a user-friendly way, while
      // still logging an exception with StackDriver
      console.log(error);
      await snap.ref.set({ error: userFacingMessage(error) }, { merge: true });
      await reportError(error, { user: context.params.userId });
    }
  });
    const updatePaymentRecord = async (id) => {
          // Retrieve the payment object to make sure we have an up to date status.
          const payment = await stripe.paymentIntents.retrieve(id);
          const customerId = payment.customer;
          // Get customer's doc in Firestore.
          const customersSnap = await admin
            .firestore()
            .collection('stripe_customers')
            .where('customer_id', '==', customerId)
            .get();
          if (customersSnap.size !== 1) throw new Error('User not found!');
          // Update record in Firestore
          const paymentsSnap = await customersSnap.docs[0].ref
            .collection('payments')
            .where('id', '==', payment.id)
            .get();
          if (paymentsSnap.size !== 1) throw new Error('Payment not found!');
          await paymentsSnap.docs[0].ref.set(payment);
    };
    exports.cleanupUser = functions.auth.user().onDelete(async (user) => {
          const dbRef = admin.firestore().collection('stripe_customers');
          const customer = (await dbRef.doc(user.uid).get()).data();
          await stripe.customers.del(customer.customer_id);
          // Delete the customers payments & payment methods in firestore.
          const snapshot = await dbRef
            .doc(user.uid)
            .collection('payment_methods')
            .get();
          snapshot.forEach((snap) => snap.ref.delete());
          await dbRef.doc(user.uid).delete();
          return;
    });
    exports.handleWebhookEvents = functions.https.onRequest(async (req, resp) => {
      const relevantEvents = new Set([
        'payment_intent.succeeded',
        'payment_intent.processing',
        'payment_intent.payment_failed',
        'payment_intent.canceled',
      ]);
      let event;
      try {
        event = stripe.webhooks.constructEvent(
          req.rawBody,
          req.headers['stripe-signature'],
          functions.config().stripe.webhooksecret
        );
      } catch (error) {
        console.error('❗️ Webhook Error: Invalid Secret');
        resp.status(401).send('Webhook Error: Invalid Secret');
        return;
      }
      if (relevantEvents.has(event.type)) {
        try {
          switch (event.type) {
            case 'payment_intent.succeeded':
            case 'payment_intent.processing':
            case 'payment_intent.payment_failed':
            case 'payment_intent.canceled':{
              const id = event.data.object.id;
              await updatePaymentRecord(id);
              break;
            }
            default:
              throw new Error('Unhandled relevant event!');
          }
        } catch (error) {
          console.error(
            `❗️ Webhook error for [${event.data.object.id}]`,
            error.message
          );
          resp.status(400).send('Webhook handler failed. View Function logs.');
          return;
        }
      }
      // Return a response to Stripe to acknowledge receipt of the event.
      resp.json({ received: true });
    });
function reportError(err, context = {}) {
  // This is the name of the StackDriver log stream that will receive the log
  // entry. This name can be any valid log stream name, but must contain "err"
  // in order for the error to be picked up by StackDriver Error Reporting.
  const logName = 'errors';
  const log = logging.log(logName);
  // https://cloud.google.com/logging/docs/api/ref_v2beta1/rest/v2beta1/MonitoredResource
  const metadata = {
    resource: {
      type: 'cloud_function',
      labels: { function_name: process.env.FUNCTION_NAME },
    },
  };
  // https://cloud.google.com/error-reporting/reference/rest/v1beta1/ErrorEvent
  const errorEvent = {
    message: err.stack,
    serviceContext: {
      service: process.env.FUNCTION_NAME,
      resourceType: 'cloud_function',
    },
    context: context,
  };
  // Write the error log entry
  return new Promise((resolve, reject) => {
    log.write(log.entry(metadata, errorEvent), (error) => {
      if (error) {
        return reject(error);
      }
      return resolve();
    });
  });
}
// [END reporterror]
/**
 * Sanitize the error message for the user.
 */
function userFacingMessage(error) {
  return error.type
    ? error.message
    : 'An error occurred, developers have been alerted';
}
buildTypes.each {
    it.buildConfigField 'String', 'PublishableKey', stripePublishableKey
}
import android.util.Log
import com.google.firebase.functions.FirebaseFunctionsException
import com.google.firebase.functions.ktx.functions
import com.google.firebase.ktx.Firebase
import com.stripe.android.EphemeralKeyProvider
import com.stripe.android.EphemeralKeyUpdateListener
class FirebaseEphemeralKeyProvider: EphemeralKeyProvider {
    override fun createEphemeralKey(
        apiVersion: String,
        keyUpdateListener: EphemeralKeyUpdateListener
    ) {
        val data = hashMapOf(
            "api_version" to apiVersion
        )
        // User firebase to call the functions
        Firebase.functions
            .getHttpsCallable("createEphemeralKey")
            .call(data)
            .continueWith { task ->
                if (!task.isSuccessful) {
                    val e = task.exception
                    if (e is FirebaseFunctionsException) {
                        val code = e.code
                        val message = e.message
                        Log.e("EphemeralKey", "Ephemeral key provider returns error: $e $code $message")
                    }
                }
                val key = task.result?.data.toString()
                Log.d("EphemeralKey", "Ephemeral key provider returns $key")
                keyUpdateListener.onKeyUpdate(key)
            }
    }
}
import android.app.Application
import com.stripe.android.PaymentConfiguration
class MyApp : Application(){
    override fun onCreate() {
        super.onCreate()
        PaymentConfiguration.init(applicationContext, BuildConfig.PublishableKey)
    }
}```
>2020-09-07 09:23:29.753 15967-15967/com.asgd.indigenoussource D/PaymentSession4: 444isCommunicating true
>2020-09-07 09:23:29.754 15967-15967/com.asgd.indigenoussource D/PaymentSession1: 11PaymentSession has changed: PaymentSessionData(isShippingInfoRequired=false, isShippingMethodRequired=false, cartTotal=0, shippingTotal=0, shippingInformation=null, shippingMethod=null, paymentMethod=null, useGooglePay=false)
>2020-09-07 09:23:29.754 15967-15967/com.asgd.indigenoussource D/PaymentSession11: 1111 false <> null
>2020-09-07 09:23:31.636 15967-15967/com.asgd.indigenoussource D/PaymentSession4: 444isCommunicating false