1

From my iOS app, I'm calling Firebase to 1) create a Stripe customer (and this works, I get a valid looking customer ID back) and 2) get the ephemeral key.

The second part is where things are failing. The error I get back looks like this:

message = "No such customer: \"cus_CFRA95y1cKuNH7\"";
param = customer;
requestId = "req_TKAARUJqcDqecK";
statusCode = 400;
type = "invalid_request_error";

Because I'm successfully creating and able to view customer IDs in the dashboard, I'm pretty convinced I have the correct test publishable key in my iOS app and the correct secret key in my Firebase .js file. I do have "viewing test data" set to "ON" in my Stripe dashboard.

If you eyeball the below code, do you see anything wrong here?

index.js

const functions = require('firebase-functions');
const stripe_key = "sk_test_y1otMY_SECRET_KEY"
var stripeFire = require("stripe-fire")(stripe_key);

// The Firebase Admin SDK to access the Firebase Realtime Database. 
const admin = require('firebase-admin');
var stripe = require('stripe')(stripe_key);
admin.initializeApp(functions.config().firebase);

exports.newCustomer = functions.https.onRequest((req, res) => {
    console.log("Creating new customer account...")
    var body = req.body

    stripe.customers.create(
        { email: body.email }
    ).then((customer) => {
          console.log(customer)
          // Send customerId -> Save this for later use
          res.status(200).json(customer.id)
    }).catch((err) => {
           console.log('error while creating new customer account' + err)
           res.status(400).send(JSON.stringify({ success: false, error: err }))
    });
});

// Express
exports.StripeEphemeralKeys = functions.https.onRequest((req, res) => {
  const stripe_version = req.body.api_version;
  const customerId = req.body.customerId
  if (!stripe_version) {
    console.log('I did not see any api version')
    res.status(400).end()
    return;
  }

  stripe.ephemeralKeys.create(
    {customer: customerId},
    {stripe_version: stripe_version}
  ).then((key) => {
     console.log("Ephemeral key: " + key)
     res.status(200).json(key)
  }).catch((err) => {
    console.log('stripe version is ' + stripe_version + " and customer id is " + customerId + " for key: " + stripe_key + " and err is " + err.message )
    res.status(500).json(err)
  });
});

on the Swift side, in MyAPIClient.swift:

func createNewCustomer(withAPIVersion apiVersion : String, completion: @escaping STPJSONResponseCompletionBlock)
{
    // using our Facebook account's e-mail for now...
    let facebookDictionary = FacebookInfo.sharedInstance.graphDictionary
    if let facebookemail = facebookDictionary["email"] as? String {

        guard let key = Stripe.defaultPublishableKey() , !key.contains("#") else {
            let error = NSError(domain: StripeDomain, code: 50, userInfo: [
                NSLocalizedDescriptionKey: "Please set stripePublishableKey to your account's test publishable key in CheckoutViewController.swift"
                ])

            completion(nil, error)

            return
        }
        guard let baseURLString = self.baseURLString, let baseURL = URL(string: baseURLString) else {

            print("something broken... what should I do?")
            // how about some kind of error in the second parameter??
            completion(nil, nil)

            return
        }

        let path = "newCustomer"
        let url = baseURL.appendingPathComponent(path)
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("application/json", forHTTPHeaderField: "Accept")
        let parameters = ["email" : facebookemail]
        do {
            request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
        } catch let error {
            print("error while serialization parameters is \(error.localizedDescription)")
        }

        let task = self.session.dataTask(with: request) { (data, urlResponse, error) in

            if let actualError = error {
                print("error from createNewCustomer API is \(actualError)")
            }

            if let httpResponse = urlResponse as? HTTPURLResponse {
                print("httpResponse is \(httpResponse.statusCode)")

                if (httpResponse.statusCode == 200)
                {
                    // eventually we'll want to get this into an actual complex JSON response / structure
                    if let actualData = data
                    {
                        if let customerIDString = String(data: actualData, encoding: .utf8) {
                            print("customer id string is \(customerIDString)")

                            let defaults = UserDefaults.standard

                            let originalcustomerid = defaults.string(forKey: "CustomerID")
                            if customerIDString != originalcustomerid
                            {
                                defaults.set(customerIDString, forKey: "CustomerID")
                                defaults.set(facebookemail, forKey: "CustomerEmail")
                            }
                        }
                    }
                    self.createCustomerKey(withAPIVersion: apiVersion, completion: completion)
                }
            } else {
                assertionFailure("unexpected response")
            }
        }
        task.resume()
    }
}

func createCustomerKey(withAPIVersion apiVersion: String, completion: @escaping STPJSONResponseCompletionBlock)
{
    // first, let's see if we have a valid customer ID for the facebook e-mail we're using

    if weHaveCustomerIDSaved() == false
    {
        createNewCustomer(withAPIVersion: apiVersion, completion: completion)
        return
    }

    guard let key = Stripe.defaultPublishableKey() , !key.contains("#") else {
        let error = NSError(domain: StripeDomain, code: 50, userInfo: [
            NSLocalizedDescriptionKey: "Please set stripePublishableKey to your account's test publishable key in CheckoutViewController.swift"
            ])

        completion(nil, error)

        return
    }
    guard let baseURLString = baseURLString, let baseURL = URL(string: baseURLString) else {

        print("something broken... what should I do?")
        // how about some kind of error in the second parameter??
        completion(nil, nil)

        return
    }

    let defaults = UserDefaults.standard

    if let customerid = defaults.string(forKey: "CustomerID")
    {
        let path = "StripeEphemeralKeys"
        let url = baseURL.appendingPathComponent(path)
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("application/json", forHTTPHeaderField: "Accept")
        let parameters = ["api_version" : apiVersion, "customerId" : customerid]
        do {
            request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
        } catch let error {
            print("error while serialization parameters is \(error.localizedDescription)")
        }

        let task = self.session.dataTask(with: request) { (data, urlResponse, error) in

            if let httpResponse = urlResponse as? HTTPURLResponse {
                print("httpResponse is \(httpResponse.statusCode)")
            } else {
                assertionFailure("unexpected response")
            }

            if let actualError = error {
                print("error from EphemeralKey API is \(actualError)")
            }

            DispatchQueue.main.async {
                do {
                    if let actualData = data
                    {
                        if let json = try JSONSerialization.jsonObject(with: actualData) as? [AnyHashable : Any]
                        {
                            print("json is \(json)")
                            completion(json, nil)
                        }
                    }
                } catch let error {
                    print("error from json \(error.localizedDescription)")
                }
            }
        }
        task.resume()
    }
}

Here's my Firebase dashboard

Firebase Dashboard

And my Stripe customer tab, showing the customer account I created is really there...

Stripe Customer Tab

Lastly, here's a log of a typical ephemeral key transaction: The error as seen in my Stripe log

Michael Dautermann
  • 88,797
  • 17
  • 166
  • 215
  • The ```index.js``` looks fine...I would setup a simple node.js script that creates a customer and then attempts to generate the ephemeral key just to check the server side is doing what it's supposed to. Your issue seems to be a key mismatch OR the fact that you created a customer and are immediately asking to create the ephemeral key and stripe is having caching/other issues. I'm guessing you can take the request_id and look at it in the dashboard too. – Brian Putt Feb 02 '18 at 05:11
  • @BrianPutt thanks for the fast eyeballs. I can't imagine it's a key mismatch, since new customer id's get created successfully. When I launch the app 60+ seconds later, I've previously saved off the customer id and it bypasses new account creation and still gets the "no such customer" line in the Firebase console, even though the values all look very valid to my eyes. I did edit the question to show the request input & output in the Stripe log. – Michael Dautermann Feb 02 '18 at 10:49
  • @MichaelDautermann How did you create this if statement . if weHaveCustomerIDSaved() == false { createNewCustomer(withAPIVersion: apiVersion, completion: completion) return } . I'm trying to recreate your code but I keep getting an error for this weHaveCustomerIDSaved(). At first I thought it was just a Bool – Markinson Mar 04 '18 at 06:58
  • Hi @Markinson -- That function basically looks to see if I previously saved the customer ID into UserDefaults. – Michael Dautermann Mar 08 '18 at 23:36

1 Answers1

2

Use cus_CFRA95y1cKuNH7, not \" cus_CFRA95y1cKuNH7 \"

You don't need the \" before and after your customerId. The customerId that Stripe needs should looks like cus_CFRA95y1cKuNH7 instead of you sending \"cus_CFRA95y1cKuNH7\". And that should work after all.

The error message should look like No such customer: cus_CFRA95y1cKuNH7; not "No such customer: \"cus_CFRA95y1cKuNH7\"";

Ziac
  • 70
  • 5
  • Yeah, that was the issue (and right in front of me too). I need to figure out how quotes are getting into the customer id that I'm saving off, but for now I'm simply stripping them out of the string I send along to Firebase/Stripe. – Michael Dautermann Feb 22 '18 at 03:46