Properly Using Firebase Cloud Functions and Stripe

Properly Using Firebase Cloud Functions and Stripe

Alright guys I figured it out myself and I will write a mini tutorial here.

Because this question specifically deals with stripe I will only cover this specific use case. First it is important to read the Stripe documentation. It specifies the parameters it is expecting you to give to it's api. Here is the link for what is expected when you charge a card with Stripe: Stripe Charges Documentation

Second you need to model your Firebase Realtime database after those expected parameters, at least in regards to the purchases a user will make in your app. Most of the time you can use a dictionary with Key:value pairs. Make sure you multiply your amount parameter by 100 as Stripe only takes integers. This means if you you are charging 22.48 for example then you multiply it by 100 to get 2248. When you check your dashboard in stripe it will show up as 22.48.

Third Stripe needs to talk to a back end so that you can charge the card. Firebase Cloud functions are perfect for this. Remember your real time database? Well you can trigger a Cloud function when a write occurs on a node that you specify. Luckily Firebase has provided a sample on GitHub: Firebase Stripe Example

You can modify the line of code where it listens for a write to the data base. You can change it to your structure specifically as long as you return at least the token and the amount to be charged. If you are going to make the user enter their information every time, then you need to delete the customer parameter (in the index.js file) as it will expect a different token with a different prefix. (This is noted in the documentation) The rest of the example is well documented and can be followed. The outcome of the charge will be written back into your database. The following video shows you how Functions triggered on write work and some of the nomenclature used. : Youtube Video for Cloud Functions triggered on write.

I hope this helps a few of you as I know questions about stripe and firebase are fairly common.

Can't deploy firebase cloud functions with Stripe

Turns out I just needed to npm install stripe --save inside the functions folder, and not in the source folder.

Stripe Connect Firebase Functions

You'll want to do something like this:

  1. Open a browser or web view from your App. Let the user work through the form to sign up for a Stripe account.
  2. Use Firebase's ability to call functions via HTTP requests, to define an endpoint that you can set as your Connect redirect_uri. Let's say https://us-central1-example.cloudfunctions.net/connect_redirect
  3. In your Firebase function, you will grab the code parameter that's included in the query string (request.query.code), then to complete the connection process, use a HTTP client to POST the appropriate data to https://connect.stripe.com/oauth/token
  4. In the return of your Firebase function, either supply a message encouraging the user to close their browser, OR create a redirect, directing the user back to your app (using something like a custom url scheme as the redirect destination)

How to send stripe response to client side(Swift) using Firebase cloud function

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

Creating a Stripe Connect account with Firebase Cloud Functions

Funny after days of looking I find an answer that works minutes after posting this!

An HTTP request needs to end properly (with a send(), end(), or redirect()).

I added:

return res.send("Please close this page")

Run Function when a Customer is charged using Stripe's Subscription Firebase Extension

I was able to solve this myself by borrowing some code from the official repo for Stripe's Firebase extension here: https://github.com/stripe/stripe-firebase-extensions/blob/next/firestore-stripe-subscriptions/functions/src/index.ts

I made my own Firebase function and called it handleInvoiceWebhook that validated and constructed a Stripe Invoice object, mapped it to a custom Invoice interface I made with the fields I cared about, then saved that to an invoices collection.

export const handleInvoiceWebhook = functions.https.onRequest(
async (req: functions.https.Request, resp) => {
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
req.rawBody,
req.headers['stripe-signature'] || '',
functions.config().stripe.secret
);
} catch (error) {
resp.status(401).send('Webhook Error: Invalid Secret');
return;
}
const invoice = event.data.object as Stripe.Invoice;
const customerId= invoice.customer as string;

await insertInvoiceRecord(invoice, customerId);
resp.status(200).send(invoice.id);
}
);

/**
* Create an Invoice record in Firestore when a customer's monthly subscription payment succeeds.
*/
const insertInvoiceRecord = async (
invoice: Stripe.Invoice,
customerId: string
): Promise<void> => {
// Invoice is an interface with only fields I care about
const invoiceData: Invoice = {
invoiceId: invoice.id,
...map invoice data here
};
await admin
.firestore()
.collection('invoices')
.doc(invoice.id)
.set(invoiceData);
};

Once deployed, I went to the Stripe developer dashboard (https://dashboard.stripe.com/test/webhooks) and added a new Webhook listening for the Event type invoice.payment_succeeded and with the url being the Firebase function I just made.

NOTE: I had to deploy my Stripe API key and Webhook Secret as enviroment variables for my Firebase function with the following command: firebase functions:config:set stripe.key="sk_test_123" stripe.secret="whsec_456"



Related Topics



Leave a reply



Submit