I've been working on implementing Stripe processing in a firebase hosted function but can't seem to get it working. Whenever I send the request from the browser to test it I get the following error message:
Access to XMLHttpRequest at 'https://firestore.googleapis.com/google.firestore.v1.Firestore/Write/channel?database=projects%app-name-a4da5%2Fdatabases%2F(default)&VER=8&RID=73206&CVER=22&X-HTTP-Session-Id=gsessionid&%24httpHeaders=Authorization%3ABearer%2tokent=1' from origin 'http://localhost:8100' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
zone-evergreen.js:2952 POST https://firestore.googleapis.com/google.firestore.v1.Firestore/Write/channel?database=projects%app-name-a4da5%2Fdatabases%2F(default)&VER=8&RID=7206&CVER=22&X-HTTP-Session-Id=gsessionid&%24httpHeaders=Authorization%3ABearer%token=1 net::ERR_FAILED
Unchecked runtime.lastError: The message port closed before a response was received.
I know the function works because if I send a request to the firebase function via postman I receive a 200. Also, if I serve firebase locally and I send the post from the browser to my localhost firebase server it's also successful. But when I send the post request from the browser to the firebase function it will not work.
I've tried adding the application/json header per another post.
let header = new HttpHeaders();
header = header.append('Content-Type', 'application/json');
console.log('token', token);
this.http
.post(this.firebaseURL,
{
amount: 100,
currency: 'usd',
token
},
{
headers: header
})
.subscribe(data => {
console.log('data', data);
});
}
And here is the firebase function:
exports.payWithStripe = functions.https.onRequest((request, response) => {
// tslint:disable-next-line:max-line-length
console.log('exports.payWithStripe = functions.https.onRequest', request);
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
// eslint-disable-next-line promise/catch-or-return
stripe.charges.create({
amount: request.body.amount,
currency: request.body.currency,
source: request.body.token,
}).then((charge: any) => {
// asynchronously called
console.log('then after charges', charge);
response.send(charge);
})
.catch((err: any) => {
console.log('error from charges', err);
});
});
This is being used for a PWA.
Again, if I ionic serve and test from a browser it works if I serve firebase locally and send the post to firebase locally but fails if I try to send the post to the firebase function on the backend.
It works both locally and in firebase if I send the request via postman.
Another thing that does work also is requesting a token from Stripe from the front end with the following:
this.stripe.createSource(this.cardDetails).then(result => {
if (result.error) {
const errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
console.log('results', result.source);
this.makePayment(result.source.id);
}
});
Everything works except for the backend requests to the firebase function from the browser with the test cards.
I'm not sure how my postman request differs from the request I'm contructing in the code. My postman request only has the Content-Type = application/json
Again, if I send the request from the browser and serve firebase locally it works just not sending the post to the backend.
I've also tried this:
makePayment(token) {
let header = new HttpHeaders();
header = header.append('Content-Type', 'text/plain');
const body = {
amount: 100,
currency: 'usd',
token
};
const postBody = JSON.stringify(body);
console.log('token', token);
this.http
.post(this.firebaseURL,
postBody,
{
headers: header
})
.subscribe(data => {
console.log('data', data);
});
}
I've been stuck on this for a while...any help would really be appreciated.
As explained in the HTTPS Cloud Functions doc, you need to encapsulate your code in a block like:
return cors(req, res, () => {
// ...
});
Therefore, for your CF, the following should work (untested):
// ...
const cors = require('cors')({ origin: true });
// ...
exports.payWithStripe = functions.https.onRequest((request, response) => {
console.log('exports.payWithStripe = functions.https.onRequest', request);
return cors(request, response, () => {
stripe.charges.create({
amount: request.body.amount,
currency: request.body.currency,
source: request.body.token,
}).then((charge: any) => {
// asynchronously called
console.log('then after charges', charge);
response.send(charge);
})
.catch((err: any) => {
console.log('error from charges', err);
// !!!! Here you need to send back a response, as follows !!!
response.status(500).send(err); // <= see addition here !!!
});
});
});
In addition, see in the catch
block how errors shall be handled.