Unhandled error { Error: You did not provide an API key - node.js

I am integrating Stripe into Firebase with my iOS project and this is the error I receive when I try to open PaymentOptionsViewController (in my iOS app simulator):
Unhandled error { Error: You did not provide an API key. You need to provide your API key in the Authorization header, using Bearer auth (e.g. 'Authorization: Bearer YOUR_SECRET_KEY'). See https://stripe.com/docs/api#authentication for details, or we can help at https://support.stripe.com/.
Here is my code in my nodejs index.js:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const stripe = require("stripe")(functions.config().stripe.secret_test_key);
exports.createStripeCustomer = functions.firestore.document('users/{userId}').onCreate(async (snap, context) => {
const data = snap.data();
const email = data.email;
const customer = await stripe.customers.create({ email: email })
return admin.firestore().collection('users').doc(data.id).update({ stripeId : customer.id})
});
exports.createCharge = functions.https.onCall(async (data, context) => {
const customerId = data.customerId;
const totalAmount = data.total;
const idempotency = data.idempotency;
const uid = context.auth.uid
if (uid === null) {
console.log('Illegal access attempt due to unauthenticated user');
throw new functions.https.HttpsError('permission-denied', 'Illegal access attempt.')
}
return stripe.charges.create({
amount: totalAmount,
currency: 'usd',
customer: customerId
}, {
idempotency_key: idempotency
}).then( _ => {
return
}).catch( err => {
console.log(err);
throw new functions.https.HttpsError('internal', 'Unable to create charge')
});
})
exports.createEphemeralKey = functions.https.onCall(async (data, context) => {
const customerId = data.customer_id;
const stripeVersion = data.stripe_version;
const uid = context.auth.uid;
if (uid === null) {
console.log('Illegal access attempt due to unauthenticated user');
throw new functions.https.HttpsError('permission-denied', 'Illegal access attempt.')
}
let key = await stripe.ephemeralKeys.create(
{customer: '{{CUSTOMER_ID}}'},
{stripe_version: '{{2019-05-16}}'}
);
return stripe.ephemeralKeys.create(
{customer: customerId},
{stripe_version: stripeVersion}
).then((key) => {
return key
}).catch((err) => {
console.log(err)
throw new functions.https.HttpsError('internal', 'Unable to create ephemeral key.')
})
})
// exports.helloWorld = functions.https.onRequest((request, response) => {
// console.log('This is the console message.')
// response.send("Hello from JonnyB Codes!");
// });s
How do I solve the error above? Let me know if more information is needed to get an answer.

Got this error message in iOS too.
In my case I was not setting correctly the publishable key when the app was launched, before doing the payment. In Swift this did the trick.
Stripe.setDefaultPublishableKey("pk_test_....")
Probably doesn't solve your issue, but maybe give you a clue.

Related

Callable cloud functions - handle error in android

im trying to delete an user from firestore and from auth.
I have this callable cloud function:
export const deleteUser = functions.https.onCall(async (data, context) => {
const userEmail = data.userEmail;
const collection = data.collection;
try {
deleteUserByEmail(userEmail, collection)
return "deleted!"
} catch (error) {
throw new functions.https.HttpsError('invalid-argument', 'there is no user with that email', error);
}
})
async function deleteUserByEmail(userEmail: string, collection: string) {
const auth = admin.auth();
const db = admin.firestore();
const { uid } = await auth.getUserByEmail(userEmail);
await db.collection(collection)
.doc(uid)
.delete();
await auth.deleteUser(uid);
return uid;
}
in android i have this:
fun deleteFromFirebase(){
val data = hashMapOf(
"userEmail" to user.email,
"collection" to "User"
)
functions // Optional region: .getInstance("europe-west1")
.getHttpsCallable("deleteUser")
.call(data)
.addOnCompleteListener() { task ->
if(!task.isSuccessful)
{
Log.d("User", "ERROR")
val e = task.exception
if (e != null) {
Log.d("Admin", e.message.toString())
}
}else{
Log.d("User", "Deleted")
//make something
}
}
}
If the user in auth and the document nin firestore exist, works great.
But i tryed to generate some error.
So I deleted the user from auth and ran the function. The Android log says D/User: User deleted
but in the console from google cloud:
Function execution took 1878 ms, finished with status code: 200
Exception from a finished function: Error: There is no user record corresponding to the provided identifier.
How can I handle the error and get correctly in android? Thanks!
The deleteUserByEmail function is async and returns a Promise. Your return statement runs before the promises is resolved. Try refactoring the code as shown below:
export const deleteUser = functions.https.onCall(async (data, context) => {
const userEmail = data.userEmail;
const collection = data.collection;
try {
// add await, continues after Promise is resolved
await deleteUserByEmail(userEmail, collection)
return "deleted!"
} catch (error) {
console.log(error) // <-- check for any errors
throw new functions.https.HttpsError('invalid-argument', 'there is no user with that email', error);
}
})
async function deleteUserByEmail(userEmail: string, collection: string) {
const auth = admin.auth();
const db = admin.firestore();
const { uid } = await auth.getUserByEmail(userEmail);
return await Promise.all([
db.collection(collection).doc(uid).delete(),
auth.deleteUser(uid)
])
}

stripe CLI webhook not reciving in firebase without firebasefuctions

It is showing no error but it is not reaching Firebase.i am using stripe CLI
console.log(success: order ${session.id} had been added to db); this line never comes in the console.
the terminal shows everything created but it does not reach Firebase database I am thinking there is an error in the code. i think the firebase is not connecting
The stripe dashboard also says connected
I am using the forward to localhost line in git terminal
webhook code
import { buffer } from "micro";
import * as admin from 'firebase-admin'
//secure a connection to Firebase from backend
const serviceAccount = require('../../../permissions.json');
const app = !admin.apps.length ? admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
})
: admin.app();
// establish connection to stripe
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const endpointSecret = process.env.STRIPE_SIGNING_SECRET;
if (typeof endpointSecret !== "string") {
console.error("Unexpected value for STRIPE_SIGNING_SECRET");
// potentially throw an error here
}
const fulfillOrder = async (session) => {
//console.log('Fulfilling order', session)
return app
.firestore()
.collection("user")
.doc(session.metadata.email)
.collection("orders")
.doc(session.id)
.set({
amount: session.amount_total / 100,
amount_shipping: session.amount_total_details.amount_shipping / 100,
images: JSON.parse(session.metadata.images),
timestamp: admin.firestore.FieldValue.serverTimestamp(),
})
.then(() => {
console.log(`success: order ${session.id} had been added to db`);
});
};
export default async (req, res) =>{
if(req.method === 'post'){
const requestBuffer = await buffer(req);
const payload = requestBuffer.toString();
const sig = req.headers["stripe-signature"];
let event;
// verify that the event posted came from stripe
try{
event = stripe.webhooks.constructEvent(
payload,
sig,
endpointSecret);
} catch (err) {
console.log('ERROR', err.message)
return res.status(400).send(`Webhook error: ${err.message}`)
}
//handle the checkout event
if (event.type === 'checkout.session.completed') {
const session = event .data.object;
//fulfill the order...
return fulfillOrder(session)
.then(() => res.status(200))
.catch((err) => res.status(400).send(`Webhook error: ${err.message}`));
}
}
};
export const config = {
api: {
bodyParser: false,
externalResolver: true,
},
};
firebase rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow write: if false;
allow read: if true;
}
}
}

How to retrieve FCM Token and userId from database then send Push Notification with Node.js and Cloud Functions

I'm very new to Node.js and I'm having trouble figuring out how to send a push notification.
This is my code so far.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.sendPushOrderOnTheWay = functions.https.onRequest((req, res) => {
const userId = req.body.id;
const registrationToken = ''
db.collection('users').doc(userId).get().then(function (doc) {
registrationToken = doc.data().registrationToken;
});
const payload = {
token: registrationToken,
notification: {
title: 'Din levering er på vej!',
body: 'Vi er der indenfor 30 min.'
},
data: {
body: message,
}
};
admin.messaging().send(payload).then((response) => {
console.log('wuhuu:', response);
return { success: true };
}).catch((error) => {
return { error: error.code };
});
});
I know something is missing to make it work, but I can't seem to figure out what it is.
My problem:
I don't know how to properly get the userId and FCM token.
Or if there are any other mistakes in my code.
This is my firestore structure:
Picture here

How to get a stripe payment charge 'success' from a Cloud Function for Firebase?

I'm working on a project with angular 6, Stripe elements and Google's Firebase (with Cloud Functions). All are new to me!
For the life of me, I'm unable to figure out how I can return 'something' which states that the payment has been successful. Stripe API docs, state that it only returns an error call if there is an 'error'...
I can see that the card is being charged successfully from the charge object in Firebase.
What can I use to query this and return the 'status: paid' value to my front-end...so I can use an *ngIf to display confirmation/failure message?
I know i'm missing something dead simple here...! I really appreciate any help with this guys.
index.js (cloud function)
const functions = require('firebase-functions');
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase);
const stripe = require('stripe')
(functions.config().stripe.testkey)
exports.stripeCharge = functions.database
.ref('/payments/{paymentId}')
.onWrite((change, context) => {
const payment = change.after.val();
const paymentId = context.params.paymentId;
// checks if payment exists or if it has already been charged
if (!payment || payment.charge) {
return
}
return admin.database()
.ref('/users/')
.once('value')
.then(snapshot => {
return snapshot.val()
})
.then(customer => {
const amount = payment.amount;
const idempotency_key = paymentId; // prevent duplicate charges
const source = payment.token.id;
const currency = 'gbp';
const charge = { amount, currency, source };
return stripe.charges.create(charge, { idempotency_key });
})
.then(charge => {
admin.database()
.ref(`/payments/${paymentId}/charge`)
.set(charge)
return true;
})
});
Payment.service.ts
import { Injectable } from '#angular/core';
import { AngularFireDatabase } from 'angularfire2/database';
#Injectable()
export class PaymentService {
constructor(private db: AngularFireDatabase) {}
// save the token to firebase, triggering the cloud function
processPayment(token: any, amount) {
const payment = { token, amount }
return this.db.list('/payments/').push(payment)
}
}
payment.component.ts (here's my onSubmit handler for the checkout)
async onSubmit(form: NgForm) {
//this.paymentProcess = true;
const { token, error } = await stripe.createToken(this.card, {
name: this.contactName,
email: this.contactEmail
});
if (error) {
console.log('Something is wrong:', error);
} else {
console.log('Success!', token);
this.paymentSvc.processPayment(token, this.amount);
}
this.card.clear();
}
You should modify your Cloud Function code as follows, in order to return the promise returned by the asynchronous .set(charge) method.
exports.stripeCharge = functions.database
.ref('/payments/{paymentId}')
.onWrite((change, context) => {
const payment = change.after.val();
const paymentId = context.params.paymentId;
// checks if payment exists or if it has already been charged
if (!payment || payment.charge) {
return
}
return admin.database()
.ref('/users/')
.once('value')
.then(snapshot => {
return snapshot.val()
})
.then(customer => {
const amount = payment.amount;
const idempotency_key = paymentId; // prevent duplicate charges
const source = payment.token.id;
const currency = 'gbp';
const charge = { amount, currency, source };
return stripe.charges.create(charge, { idempotency_key });
})
.then(charge => {
return admin.database() <-- Here return !!!!!!
.ref(`/payments/${paymentId}/charge`)
.set(charge);
//return true; <-- Here don't return !!!!!!
})
});
In order to "return the 'status: paid' value to the front-end..", just set a listener to the /payments/${paymentId}/charge path and as soon as the charge has the correct status value, update your front-end.
Finally, note that with:
...
.then(charge => {
admin.database()
.ref(`/payments/${paymentId}/charge`)
.set(charge)
return true;
})
you were returning the value true to the Cloud Function platform, (indicating that the Function can be terminated) before the set() asynchronous operation was completed.

Cloud Functions for Firebase Notification

I am trying to fire a notification using Cloud Functions for Firebase. I can get it to console log stating a message has been fired, but can't actually get the notification to work in the browser. Can anyone see a flaw?
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.newMessageAlert = functions.database.ref('/messages/{message}').onWrite((event) => {
const message = event.data.val();
const getTokens = admin.database().ref('users').once('value').then((snapshot) => {
const tokens = [];
snapshot.forEach((user) => {
const token = user.child('token').val();
if (token) tokens.push(token);
});
return tokens;
});
const getAuthor = admin.auth().getUser(message.uid);
Promise.all([getTokens, getAuthor]).then(([tokens, author]) => {
const payload = {
notification: {
title: `Hot Take from ${author.displayName}`,
body: message.content,
icon: author.photoURL
}
};
admin.messaging().sendToDevice(tokens, payload).then((resp) =>{
console.log("IT WORKED", resp);
});
});
});

Resources