How to Show Firebase Auth User UID and also my Firestore doc uid as Stripe customer id

Viewed 285

I am trying to Show Firestore doc uid as Stripe custommer id.and to link Stripe to Firestore cloud but it show error - "error loading page: failed to decode response from the server" as shown in the image -

My cloud function i.e. index.js -

 const admin = require('firebase-admin');
 const express = require('express')
 admin.initializeApp();

 const functions = require('firebase-functions');
 const stripe = require('stripe')     ("sk_________");


// When a user is created, register them with Stripe
exports.createStripeCustomer = functions.auth.user().onCreate(async (user) => {
 const customer = await stripe.customers.create({email: user.email});
 return   admin.firestore().collection('stripe_customers').doc(user.uid).set({customer_id: customer.id});
 });

exports.createPaymentIntent = functions.https.onCall(async (req, res) => {
const amount = req.amount;
const customer = req.customer;
console.log(customer)

const paymentIntent = await stripe.paymentIntents.create({
  amount: 1099,
  currency: 'usd',
 });
const clientSecret = paymentIntent.clientSecret
console.log(clientSecret)
return clientSecret
 });

 exports.getPaymentMethods = functions.https.onCall(async (req, res) => {
 const customer = req.customer;
 const type = "card"

 stripe.paymentMethods.list({customer : customer, type: type}, function(err,  paymentMethods) {
 if (err !== null) {
  console.log("ERROR")
 } else {
  return paymentMethods
 }
 return
})
});

exports.createEphemeralKey = functions.https.onCall(async(data, context) => {
const stripeVersion = data.stripe_version;
const customerId = data.customer_id;

 return stripe.ephemeralKeys.create(
 {customer: customerId},
 {stripe_version: stripeVersion}
 ).then((key) => {
 return key
 }).catch((err) => {
 console.log(err)
})
})

My CheckoutViewController -

import UIKit
import Stripe
import Firebase

let backendUrl = "https://fireupgoods-a0036.firebaseio.com/"

class CheckoutViewController: UIViewController {

 var paymentIntentClientSecret: String?

lazy var cardTextField: STPPaymentCardTextField = {

let cardTextField = STPPaymentCardTextField()

return cardTextField

}()

lazy var payButton: UIButton = {

let button = UIButton(type: .custom)

button.layer.cornerRadius = 5

button.backgroundColor = .systemBlue

button.titleLabel?.font = UIFont.systemFont(ofSize: 22)

button.setTitle("Pay now", for: .normal)

button.addTarget(self, action: #selector(pay), for: .touchUpInside)

return button

}()

override func viewDidLoad() {

super.viewDidLoad()

StripeAPI.defaultPublishableKey = "pk_test_TYooMQauvdEDq54NiTphI7jxpk_test_51HmAsMFzRM1fSBZ2tTT1SxPxRBzParFfjm6s0aXD0F5dYLeOSVCKtiZ4lq0TGwJxPhHpsq6Hga7I0QzRWisPYMdj00bDxAMTKf"

view.backgroundColor = .white

let stackView = UIStackView(arrangedSubviews: [cardTextField, payButton])

stackView.axis = .vertical

stackView.spacing = 20

stackView.translatesAutoresizingMaskIntoConstraints = false

view.addSubview(stackView)

NSLayoutConstraint.activate([

  stackView.leftAnchor.constraint(equalToSystemSpacingAfter: view.leftAnchor, multiplier: 2),

  view.rightAnchor.constraint(equalToSystemSpacingAfter: stackView.rightAnchor, multiplier: 2),

  stackView.topAnchor.constraint(equalToSystemSpacingBelow: view.safeAreaLayoutGuide.topAnchor, multiplier: 2),

])

startCheckout()

 }

  func displayAlert(title: String, message: String, restartDemo: Bool = false) {

   DispatchQueue.main.async {

  let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)

  alert.addAction(UIAlertAction(title: "OK", style: .cancel))

  self.present(alert, animated: true, completion: nil)

  }

}

 func startCheckout() {

// Create a PaymentIntent as soon as the view loads

let url = URL(string: backendUrl + "create-payment-intent")!

let json: [String: Any] = [

  "items": [

      ["id": "xl-shirt"]

  ]

]

var request = URLRequest(url: url)

request.httpMethod = "POST"

request.setValue("application/json", forHTTPHeaderField: "Content-Type")

request.httpBody = try? JSONSerialization.data(withJSONObject: json)

let task = URLSession.shared.dataTask(with: request, completionHandler: { [weak self] (data, response, error) in

  guard let response = response as? HTTPURLResponse,

    response.statusCode == 200,

    let data = data,

    let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String : Any],

    let clientSecret = json["clientSecret"] as? String else {

        let message = error?.localizedDescription ?? "Failed to decode response from server."

        self?.displayAlert(title: "Error loading page", message: message)

        return

  }

  print("Created PaymentIntent")

  self?.paymentIntentClientSecret = clientSecret

})

  task.resume()

 }

  @objc

  func pay() {

  guard let paymentIntentClientSecret = paymentIntentClientSecret else {

    return;

  }

// Collect card details

let cardParams = cardTextField.cardParams

let paymentMethodParams = STPPaymentMethodParams(card: cardParams, billingDetails: nil, metadata: nil)

let paymentIntentParams = STPPaymentIntentParams(clientSecret: paymentIntentClientSecret)

paymentIntentParams.paymentMethodParams = paymentMethodParams

// Submit the payment

let paymentHandler = STPPaymentHandler.shared()

paymentHandler.confirmPayment(paymentIntentParams, with: self) { (status, paymentIntent, error) in

  switch (status) {

  case .failed:

      self.displayAlert(title: "Payment failed", message: error?.localizedDescription ?? "")

      break

  case .canceled:

      self.displayAlert(title: "Payment canceled", message: error?.localizedDescription ?? "")

      break

     case .succeeded:

      self.displayAlert(title: "Payment succeeded", message:   paymentIntent?.description ?? "")
  
   break
   @unknown default:
   fatalError()
   break
     }
    }
    }
   }
   extension CheckoutViewController: STPAuthenticationContext {
   func authenticationPresentingViewController() -> UIViewController {
    return self
    }
   }

screenshot of the error -

enter image description here

Firebase code which writes my Firebase Auth User UID to Firestore Document ID is -

 import UIKit
 import Firebase
 import FirebaseFirestore
 
class UserIdtoFirestoreViewController: UIViewController {
let db = Firestore.firestore()
var ref: DocumentReference? = nil
let email = Auth.auth().currentUser!.email
let id = Auth.auth().currentUser!.uid
let fullName = Auth.auth().currentUser!.displayName

@IBAction func GetUserId(_ sender: UIButton) {
    db
        .collection("users")
        .document(id)
        .setData([
                    "email":email,"fullName":fullName])

      }
 }

The above code writes my Auth User UIDfrom here (see image below):-

enter image description here

to here (see image below):-

enter image description here

As you can see in the 2 images above, the Auth User UID (which begins with 2k3k) gets written to Firestore Document ID. I want the same ID to be shown in Stripe customer ID and logs and events in Stripe, but as you can see below, there is nothing on the Stripe Dashboard: - enter image description here

How to sort it out ?

I know I am missing out on many things, but my immediate issue is to make the error go away and the Firestore Document ID to be added as Stripe customer id. programmatically.

Any help will be appreciated.

Edit: I am also adding AppDelegate code and MyAPIClient code below:-

AppDelegate code -

  import UIKit
  import GoogleSignIn
  import Firebase
  import CoreData
  import Stripe
  @UIApplicationMain
  
   class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate {

    var window: UIWindow?
    
     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
   
    Stripe.setDefaultPublishableKey( "pk_test_51HmAsMFzRM1fSBZ2tTT1SxPxRBzParFfjm6s0aXD0F5dYLeOSVCKtiZ4lq0TGwJxPhHpsq6Hga7I0QzRWisPYMdj00bDxAMTKf")
      FirebaseApp.configure()
    
        // Override point for customization after application launch.
        GIDSignIn.sharedInstance().clientID =    FirebaseApp.app()?.options.clientID
         GIDSignIn.sharedInstance().delegate = self
    
        return true
   }

     func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
     return GIDSignIn.sharedInstance().handle(url as URL?)
     }
       func sign(_ signIn: GIDSignIn!,
          didSignInFor user: GIDGoogleUser!,
          withError error: Error!) {
    
        // Check for sign in error
        if let error = error {
        if (error as NSError).code ==       GIDSignInErrorCode.hasNoAuthInKeychain.rawValue {
            print("The user has not signed in before or they have  since signed out.")
        } else {
            print("\(error.localizedDescription)")
        }
        return
      }
    
       // Get credential object using Google ID token and Google  access token
      guard let authentication = user.authentication else {
        return
     }
      let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
                                                   accessToken:   authentication.accessToken)
    
       // Authenticate with Firebase using the credential object
       Auth.auth().signIn(with: credential) { (authResult, error) in
           if let error = error {
            print("Error occurs when authenticate with Firebase: \(error.localizedDescription)")
        }
            
          // Post notification after user successfully sign in
         // NotificationCenter.default.post(name: .signInGoogleCompleted, object: nil)
       }
     }

      func sign(_ signIn: GIDSignIn!, didDisconnectWith user:  GIDGoogleUser!, withError error: Error!) {
        // Perform any operations when the user disconnects from app here.
         print("User has disconnected")
    }

      func applicationWillResignActive(_ application: UIApplication) {
        // Sent when the application is about to move from active    to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
         // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
    }

       func applicationDidEnterBackground(_ application: UIApplication) {
         // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
      }

       func applicationWillEnterForeground(_ application: UIApplication)   {
       // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
     }

      func applicationDidBecomeActive(_ application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
       }

      func applicationWillTerminate(_ application: UIApplication) {
         // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
     }
  }

MyAPIClient code:-

   import Stripe
   import UIKit
   class MyAPIClient: NSObject, STPCustomerEphemeralKeyProvider          {
   let baseURL = URL(string: "https://api.stripe.com")!

    func createCustomerKey(withAPIVersion apiVersion: String,   completion: @escaping STPJSONResponseCompletionBlock) {
        let url = self.baseURL.appendingPathComponent("ephemeral_keys")
        var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false)!
        urlComponents.queryItems = [URLQueryItem(name: "api_version", value: apiVersion)]
        var request = URLRequest(url: urlComponents.url!)
        request.httpMethod = "POST"
        let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
            guard let response = response as? HTTPURLResponse,
                response.statusCode == 200,
                let data = data,
                let json = ((try? JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]) as [String : Any]??) else {
                completion(nil, error)
                return
            }
            completion(json, nil)
        })
        task.resume()
        }
    }

Thanks.

0 Answers
Related