Ad

How To Send Stripe Response To Client Side(Swift) Using Firebase Cloud Function

I'm making an iOS app like Airbnb using Stripe and Firebase as backend. I'm following the this document. https://medium.com/firebase-developers/go-serverless-manage-payments-in-your-apps-with-cloud-functions-for-firebase-3528cfad770.
As the document says, here's the workflow I made so far.(let's say a user wants to purchase something)
1. A user sends payment information to Firebase realtime database such as amount currency and card token)
2. Firebase triggers a function which sends charge request(stripe.charge.create) to Stripe.
3. After getting response, write it back to Firebase database. if the response failed, write the error message to database (see userFacingMessage functions in index.js)
4. In client side(Swift), observe the Firebase database to check the response.
5. If the response is successful, display successful message to the user. If there're are any errors such as ( payment failed because a credit card expires), display failed message to the user( also display 'please try again" message)

I guess this is not right approach because I think the user should know response (if succeeded or failed) as soon as firebase gets the response from Stripe. In another word, the client side(Swift) should get the response right after getting response before writing back to the Firebase database? Does anyone know how to send response to the client side?
Any help would be appreciated

ChargeViewController.swift (client-side)

  func didTapPurchase(for amountCharge: String, for cardId: String) {
    print("coming from purchas button", amountCharge, cardId)

    guard let uid = Auth.auth().currentUser?.uid else {return}

    guard let cardId = defaultCardId else {return}
    let amount = amountCharge
    let currency = "usd"

    let value = ["source": cardId, "amount": amount, "currency": currency] as [String: Any]

    let ref = Database.database().reference().child("users").child(uid).child("charges")
    ref.childByAutoId().updateChildValues(value) { (err, ref) in
        if let err = err {
            print("failed to inserted charge into db", err)
        }

        print("successfully inserted charge into db")

       //Here, I want to get the response and display messages to user whether the response was successful or not.

    }

}

index.js(Cloud Functions) Language: node.js

exports.createStripeCharge = functions.database
.ref(‘users/{userId}/charges/{id}’)
.onCreate(async (snap, context) => {
const val = snap.data();
try {
// Look up the Stripe customer id written in createStripeCustomer
const snapshot = await admin.database()
.ref(`users/stripe/${context.params.userId}/stripe_customer_id`)
.once('value');

const snapval = snapshot.data();
const customer = snapval.stripe_customer_id;
// Create a charge using the pushId as the idempotency key
// protecting against double charges
const amount = val.amount;
const idempotencyKey = context.params.id;
const charge = {amount, currency, customer};
if (val.source !== null) {
   charge.source = val.source;
}
const response = await stripe.charges
    .create(charge, {idempotency_key: idempotencyKey});
// If the result is successful, write it back to the database
//*I want to send this response to the client side but not sure how if I can do it nor not*
return snap.ref.set(response);
} catch(error) {
    await snap.ref.set(error: userFacingMessage(error));
}
});
    // Sanitize the error message for the user
function userFacingMessage(error) {
  return error.type ? error.message : 'An error occurred, developers have been alerted';
}
Ad

Answer

Based on Franks's post here, I've decided to wait a change from the Firebase database. Below is the workflow and the code (There's no change in index.js file):

1. A user sends payment information to Firebase realtime database such as amount currency and card token) in path of /users/{userId}/charges
2. Firebase triggers a function which sends charge request(stripe.charge.create) to Stripe.
3. After getting response, write it back to Firebase database. if the response failed, write the error message to database (see userFacingMessage functions in index.js)
4. In client side(Swift), wait for the change in the Firebase database to check the whether the response is successful or not by using Observe(.childChanged) (See the Swift code)
5. If the response is successful, display successful message to the user. If there're are any errors such as ( payment failed because a credit card expires), display failed message to the user( also display 'please try again" message)

ChargeViewController.swift

func didTapPurchase(for amountCharge: String, for cardId: String) {
print("coming from purchas button", amountCharge, cardId)

guard let uid = Auth.auth().currentUser?.uid else {return}

guard let cardId = defaultCardId else {return}
let amount = amountCharge
let currency = "usd"

let value = ["source": cardId, "amount": amount, "currency": currency] as [String: Any]

let ref = Database.database().reference().child("users").child(uid).child("charges")
ref.childByAutoId().updateChildValues(value) { (err, ref) in
    if let err = err {
        print("failed to inserted charge into db", err)
    }

    print("successfully inserted charge into db")

   //Here, Wait for the response that has been changed
   waitForResponseBackFromStripe(uid: uid)

  }

 }

func waitForResponseBackFromStripe(uid: String) {

    let ref = Database.database().reference().child("users").child(uid).child("charges")
    ref.observe(.childChanged, with: { (snapshot) in

        guard let dictionary = snapshot.value as? [String: Any] else {return}

        if let errorMessage = dictionary["error"] {
            print("there's an error happening so display error message")
            let alertController = UIAlertController(title: "Sorry:(\n \(errorMessage)", message: "Please try again", preferredStyle: .alert)
            alertController.addAction(UIAlertAction(title: "Ok", style: .cancel, handler: nil))
            //alertController.addAction(UIAlertAction(title: "Ok", style: .cancel, handler: nil))
            self.present(alertController, animated: true, completion: nil)
            return

        } else {
            let alertController = UIAlertController(title: "Success!", message: "The charge was Successful", preferredStyle: .alert)
            alertController.addAction(UIAlertAction(title: "Ok", style: .cancel, handler: nil))
            self.present(alertController, animated: true, completion: nil)
        }
    }) { (err) in
        print("failed to fetch charge data", err.localizedDescription)
        return
    }
}

If I did something logically wrong, please let me know. but it's so far working for me
Hope this would help someone who is integrating Firebase and Stripe payment

Ad
source: stackoverflow.com
Ad