Netlify, Lambdas, Stripe, and Googlebots - reactjs

Ive been working on an SPA with React that i have deployed on Netlify. The application uses stripe.js as a form of payment. While all of the functionality of stripe appears to be working fine on the user side, we are running into a problem with Google Search Console. It seems that the Googlebot Crawler is being blocked by the Stripe robots.txt file. basically the ultimate goal is to be approved for google adsense and after numerous rejections (even with prerendering and a lot more content added) we are still getting rejected. when we tried the google search console to see what google bot crawlers see we have absolutely no errors, the site is mobile friendly BUT we are getting this error that is shown below. While I obviously don't have any control over their use of Stripes robot.txt file, the Search console is also telling me this:
Message:
Access to XMLHttpRequest at 'https://m.stripe.com/6' from origin 'https://m.stripe.network' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Source:
https://m.stripe.network/inner.html#url=https%3A%2F%2Fwww.stateiqtest.org%2F&title=&referrer=&muid=NA&sid=NA&version=6&preview=false:0
The last thing i want to mention is all of the stripe functionality is accessed and called from a serverless Lambda function through netlify... I am confused why the CORS issue is a policy for the bot but not for users of the site? I am attaching my stripe lambda function call which I just enabled the cors policy for... but once again ... why do i have to even do this? if im not getting errors in the console from the user side how come the bot can't access it? i have tried everything from changing my netlify.toml file to adding a robots.txt file which disallows the provided Stripe URL. any leads? let me know! your help is already appreciated ! :)
//client sides
import {loadStripe} from "#stripe/stripe-js"
export async function handleFormSubmission(event) {
event.preventDefault();
const form = new FormData(event.target);
const data = {
sku: form.get('sku'),
quantity: Number(form.get('quantity')),
};
const response = await fetch('/.netlify/functions/create-checkout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "Content-Type",
"Access-Control-Allow-Methods": "GET, POST",
},
body: JSON.stringify(data),
}).then((res) => res.json())
const stripe=await loadStripe(response.publishableKey);
const {err}=await stripe.redirectToCheckout({
sessionId:response.sessionId
})
if(err){
console.log(err)
}
}
im honestly going to admit i haven't used stripe in a while and haven't had issues until now thus am revisiting code. here is another function that i believe makes the request and creates the stripe checkout...
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const inventory = require('./data/products.json');
exports.handler = async (event) => {
const { sku, quantity } = JSON.parse(event.body);
const product = inventory.find((p) => p.sku === sku);
const validatedQuantity = quantity > 0 && quantity < 2 ? quantity : 1;
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
billing_address_collection: 'required',
success_url: `${process.env.URL}/success`,
cancel_url: process.env.URL,
line_items: [
{
name: 'Cognitive Analysis',
currency:'USD',
amount: 299,
quantity: 1
},
],
});
return {
statusCode: 200,
body: JSON.stringify({
sessionId: session.id,
publishableKey: process.env.STRIPE_PUBLISHABLE_KEY,
}),
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "Content-Type",
"Access-Control-Allow-Methods": "GET, POST",
}
};
};

Related

CORS errors on Axios Graphql query

I'm trying to use react-axios to query a graphql endpoint but I'm encountering a problem with CORS.
Access to XMLHttpRequest at 'https://rickandmortyapi.com/graphql' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Here it is my setup:
const characterQuery = `{
characters(page: 2, filter: { name: "rick" }) {
info {
count
}
results {
name
status
species
gender
image
}
}
}`
const axiosInstance = axios.create({
data: characterQuery,
headers: {"Access-Control-Allow-Origin": "*"}
})
<AxiosProvider instance={axiosInstance}>
<Post url="https://rickandmortyapi.com/graphql">
{(response: any) => {
console.log(response);
}}
</Post>
</AxiosProvider>
Can someone help me? Thanks
CORS is a pain, always, the problem is that the header you pass is the one the server should give you.
You can't force the server to pass the header if they don't already, that the whole point of this protection, avoiding hacker pretending to be other ppl websites.
So the API you are trying to reach must have the CORS header or it will not work
You can play around with fetch see if you have better luck than axios.
Fetch provide some amount of control over your CORS settings, https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
fetch('https://rickandmortyapi.com/graphql', {
method: 'POST',
mode: 'cors',
headers: { "content-type": "application/json" },
body: JSON.stringify({
query: `{
character(id: 1) {
name
}
}`
})
})
This request work with fetch for me, uppon testing, if the content-type was not set to application/json the server failed with error 500 instead of showing a 400 bad request
Edit again, it works with axios too, are you sure you get a CORS error ?

React JS CORS-ORIGIN - Instagram Basic Display API

I have problem with CORS: Access-Control-Allow-Origin when try to exchange the code for access token from Instagram API.
Instagram API documentation Step 5 : https://developers.facebook.com/docs/instagram-basic-display-api/getting-started#step-5--exchange-the-code-for-a-token
Body parametars:
const body = {
'client_id': 'xxxxxxxxxxxxxx',
'client_secret': 'xxxxxxxxxxxxxx',
'grant_type': 'authorization_code',
'redirect_uri': 'https://localhost:3000/',
'code': instaCode
};
My request:
axios.post(`https://api.instagram.com/oauth/access_token`, qs.stringify(body), {
headers: {'content-type': 'application/x-www-form-urlencoded'}
});
Đ¢his code worked 2 months ago.
edit: you can try this axios post request to send form data first which seems better, below worked for me though
That won't work, you have to send the body in a form data.
I believe fetch API would work but that's available since node 17, if you're using earlier version this is what worked for me.
SOURCE https://www.section.io/engineering-education/integrating-instagram-basic-display-api/
const { post } = require("request");
const { promisify } = require("util");
const postAsync = promisify(post);
const form = {
client_id: NUMBER,
client_secret: STRING,
grant_type: "authorization_code",
redirect_uri: STRING,
code: req.body.code,
};
let { body, statusCode } = await postAsync({
// let result = await postAsync({
url: "https://api.instagram.com/oauth/access_token",
form,
headers: {
"content-type": "multipart/form-data",
host: "api.instagram.com",
},
});
npm i request
and use that inside a try/catch of course

How can I add a CORS header to this Lambda function

Please bare with me as this is my first stack overflow post, but I have minimal backend experience and am really struggling to meet CORS requirements.
I want to use AWS (SES, API Gateway, Lambda) to send form data to our company email account. My function works currently when testing in AWS, but it doesn't work on the client side of my site. From what I've gathered from research so far, my Lambda function needs a CORS header to work. Here is the code:
var aws = require("aws-sdk");
var ses = new aws.SES({ region: "us-east-1" });
exports.handler = async function(payload) {
var params = {
Destination: {
ToAddresses: ['placeholder#place.com'],
},
Message: {
Body: {
Text: {
Data: `\n
${payload.fullName} has tried to contact you. \n
Message: \n
-------------------- \n
${payload.comments} \n
-------------------- \n
Here is the sender's contact information: \n
Name: ${payload.fullName} \n
Email: ${payload.emailAddress} \n
Phone: ${payload.phone} \n
Company: ${payload.companyName}`
},
},
Subject: { Data: payload.subject },
},
Source: 'placeholder#place.com',
};
return ses.sendEmail(params).promise()
};
I'm looking at this code as an example of how to include a CORS header:
exports.handler = async (event) => {
let data = {};
let res = {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*' // replace with hostname of frontend (CloudFront)
},
body: JSON.stringify(data)
};
return res;
};
Can anyone help me to combine these two approaches? I don't understand how to make the SES function into a more traditional response. I am mostly a frontend dev, so I expect that I'm missing something silly. I appreciate any responses though.
If you can change the API Gateway integration type to Lambda Proxy, then this code can help you.
Move the entire code in the handler method to another function say sendEmail
const sendEmail = async function(payload) {
// Your code to crete the `params` omitted for brevity
return ses.sendEmail(params).promise()
};
The handler can call this function and based on the outcome of this function send an appropriate result with the CORS headers
exports.handler = async function(event) {
const payload = JSON.parse(event.body);
const CORS_HEADERS = {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*' // Your origin name
};
try {
await sendEmail(payload);
return {
statusCode: 200,
headers: CORS_HEADERS,
body: '{}'
}
} catch(err) {
return {
statusCode: 500, // Can be 4XX or 5XX depending on the error
headers: CORS_HEADERS,
body: `{"err": ${err.messge}}`
}
}
}
For this to work for CORS requests, you also need to ensure the OPTIONS request responds with appropriate headers. You can do so using the AWS console following this documentation. For CloudFormation along with api-gateway V2, this documentation should help. For AWS SAM, this documentation should help (If you are not already using any Serverless development tool, take a look at AWS SAM).
If you don't wish to use the Lambda proxy, then ensure the integration response send the appropriate CORS headers for both the OPTIONS request and the POST request. This can help.

Cancel Subscription Paypal API + Axios + React

I'm stuck with a Paypal Smart buttons error that says 401 (Unauthorized)
The business solution is paid for and everything that should be authorized is.
This is the function I created. Anything in-between [ ] are placement holders of private info:
cancelSubscription = () => {
axios({
url: `https://api.paypal.com/v1/billing/subscriptions/[USER_SUBSCRIPTION_ID]/cancel`,
method: 'post',
headers: { "Content-Type": "application/json", "Authorization": "Bearer [FACILITATOR_ACCESS_TOKEN]" },
data: { "reason": "test -- Not satisfied with the service" }
})
.then(res => {
console.log(`Axios Call completed: ${res}`)
});
}
I don't see a problem with your code, so the clientid, access token, and full response body+response headers (including a PayPal-Debug-Id) will all have to be looked at to troubleshoot a 401. Submit this information to PayPal's support if you aren't going to post it here.

How to send an SMS using Twilio through frontend with react?

I've read the Twilio documentation and I can't find a way to send a simple SMS from the frontend using JavaScript/React.
The Twilio documentation just shows how to do that using Node.js(server side).
Actually, I found the documentation a bit awkward because they don't explain the how to do that using the most common programme language on the web.
I'm using postman and it works fine, but on my react code doesn't.
The code below was exported from Postman:
var settings = {
"async": true,
"crossDomain": true,
"url": "https://api.twilio.com/2010-04-01/Accounts/AC62761f2bae5c5659cc5eb65d42e5d57e/Messages.json",
"method": "POST",
"headers": {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": "Basic hashedAuthToken",
"Cache-Control": "no-cache",
"Postman-Token": "0s41f5ac-2630-40c4-8041-1e5ee513f20d"
},
"data": {
"To": "+353838173123",
"From": "+18634000432",
"MessagingServiceSid": "MG3d622e63a343e11a2032b1414560f227",
"Body": "Test, hi"
}
}
$.ajax(settings).done(function (response) {
console.log(response);
});
PS: The tokens above was modified. It won't work if you are not using your own credential.
Twilio developer evangelist here.
There is a huge problem with what you are trying to attempt here.
Putting your Twilio credentials into the front end (or into a Stack Overflow question/answer) leaves them open to anyone to read your source code and steal them. A malicious attacker can take those credentials and abuse your account with them.
I recommend you refresh your Auth Token in your Twilio console now. You should consider them compromised.
What you should do is build an SMS sending service on your own server side and then call that service from your React front end. There is a blog post on sending SMS with Twilio on React that is worth reading and I will try to put something together to show it too.
Update:
I wrote a blog post explaining how to send an SMS with React and Twilio. The important thing is that you should perform the API call in your server (in the blog post, it's an Node.js/Express server but you can use whatever server-side tech you want). Then you send the message from your React application to the server using fetch (or axios or XMLHttpRequest if you want).
You can use the method below to do that easily.
sendSMSTwilio(message) {
const url = Config.sms.url;
const accountSid = Config.sms.accoundId;
const authToken = Config.sms.authToken;
const auth = 'Basic ' + new Buffer(Config.sms.accountSid + ':' + Config.sms.authToken).toString('base64');
const details = {
To: message.to,
From: message.from,
MessagingServiceSid: Config.sms.serviceSid,
Body: message.text
};
const formBody = [];
for (var property in details) {
const encodedKey = encodeURIComponent(property);
const encodedValue = encodeURIComponent(details[property]);
formBody.push(encodedKey + '=' + encodedValue);
}
const body = formBody.join('&');
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
Authorization: auth
},
body
};
return new Promise((resolve, reject) => {
return fetch(url, options)
.then((response) => {
return resolve(response);
})
.then((responseJson) => {
return resolve(responseJson);
})
.catch((error) => {
return reject(error);
});
});
}
You can call and receive the promise response like that:
this.sendSMSTwilio()
.then((data) => {
console.log(data);
})
.catch((err) => {
console.log('Error SMS sender', err);
});

Resources