Reputation: 11
The following are the two errors I get in my browser when attempting to create a customer using the Square API:
Timeout (504 Gateway Timeout): A POST request to https://backend-437525971388.us-central1.run.app/customer is failing with a 504 Gateway Timeout error.
CORS Error: Access to fetch at 'https://backend-437525971388.us-central1.run.app/customer' from origin 'https://buzzysweets.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I verified with console logs that the correct production application ID, location ID, access token, and cdn are being used.
Note that: In my development environment, I successfully created a customer, order, and payment - using the sandbox credentials and cdn. I also my successfully created a customer and order - using the production credentials and cdn.
In summary, I failed to make a POST request from my frontend hosted at https://buzzysweets.com to my backend API hosted on Google Cloud Run at https://backend-437525971388.us-central1.run.app/customer. Here are the snippets of code:
import React, { useEffect, useState } from 'react';
text`import { selectCart } from '../../features/cart/cartSlice';
import { useSelector } from 'react-redux';
const PaymentForm = () => {
const appId = process.env.REACT_APP_YOUR_SQUARE_APPLICATION_ID;
const locationId = process.env.REACT_APP_YOUR_SQUARE_LOCATION_ID;
const { customerInfo } = useSelector(selectCart);
// Load Square Web Payments SDK
useEffect(() => {
const loadSquare = async () => {
if (!window.Square) {
return;
}
const payments = window.Square.payments(appId, locationId);
try {
const card = await payments.card();
await card.attach('#card-container');
setCard(card);
} catch (err) {
console.error('Error loading Square SDK', err);
}
};
loadSquare();
}, []);
const handlePayment = async (e) => {
e.preventDefault();
if (!card) return;
try {
const tokenResult = await card.tokenize();
if (tokenResult.status === 'OK') {
const customerResults = await createCustomer(tokenResult.token, customerInfo);
}
} catch (err) {
console.error('Payment error:', err);
}
};
const createCustomer = async (token, customerInfo) => {
const bodyParameters = {
address: {
address: customerInfo.address,
country: customerInfo.country,
firstName: customerInfo.firstName,
lastName: customerInfo.lastName,
zipCode: customerInfo.zipcode,
},
givenName: customerInfo.firstName,
familyName: customerInfo.lastName,
emailAddress: customerInfo.emailAddress,
idempotency_key: window.crypto.randomUUID(),
};
const body = JSON.stringify(bodyParameters);
try {
const response = await fetch(`${process.env.REACT_APP_API_URL_BACK}/customer`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body,
});
const result = await response.json();
if (!response.ok) {
throw new Error(`Failed to create customer: ${result.errors[0].detail}`);
}
return result;
} catch (err) {
console.error('Error creating customer:', err);
}
};
};
//ON MY BACKEND SIDE: USING NODE.JS
// server.js
const { ApiError, client: square } = require('./server/square');
const corsMiddleware = require('micro-cors')({
origin: process.env.REACT_APP_API_URL_FRONT,
allowMethods: ['POST', 'GET'],
allowedHeaders: ['Content-Type', 'Authorization'],
});
async function createCustomer(req, res) {
const payload = await json(req);
await retry(async (bail, attempt) => {
try {
const customerReq = {
address: {
addressLine1: payload.address.address,
firstName: payload.address.firstName,
lastName: payload.address.lastName,
country: payload.address.country,
postalCode: payload.address.zipCode,
},
emailAddress: payload.emailAddress,
idempotencyKey: payload.idempotency_key,
familyName: payload.familyName,
};
console.log('customerReq:', customerReq) //This output shows correctly in Google Cloud Run's logs
const { result, statusCode } = await square.customersApi.createCustomer(customerReq);
console.log('customer returned result:', result); //Google Cloud Run does not show any output from this console.log
send(res, statusCode, {
success: true,
customer: {
id: result.customer.id,
status: result.customer.status,
},
});
} catch (ex) {
if (ex instanceof ApiError) {
logger.error("API Error:", ex.errors);
bail(ex);
} else {
logger.error(`Unexpected error: ${ex}`);
throw ex;
}
}
});
}
const appRouter = router(post('/customer', createCustomer));
const corsWrappedApp = corsMiddleware(appRouter);
if (require.main === module) {
const PORT = process.env.PORT || 8080;
require('http')
.createServer(corsWrappedApp)
.listen(PORT);
}
module.exports = corsWrappedApp;
//config.js file: create Square API client
require('dotenv').config({
path: process.env.NODE_ENV === 'production' ? '.env.production' : '.env.sandbox', // Dynamically choose .env file based on NODE_ENV
});
const { Client, Environment } = require('square');
const client = new Client({
environment: process.env.NODE_ENV === 'production' ? Environment.Production : Environment.Sandbox,
accessToken: process.env.SQUARE_ACCESS_TOKEN,
});
module.exports = {
client,
isProduction: process.env.NODE_ENV === 'production',
};
//square.js file:
// Import the client from the config file
const { client } = require('./config');
const { ApiError } = require('square');
(async () => {
try {
const { result, statusCode } = await client.customersApi.listCustomers();
console.log('Customers retrieved successfully:', result.customers);
} catch (ex) {
// Handle errors
if (ex instanceof ApiError) {
console.error('API Error:', ex.errors);
} else {
console.error('Unexpected Error:', ex);
}
}
})();
module.exports = { ApiError: client.ApiError, client };
Upvotes: 1
Views: 107
Reputation: 413
It seems like CORS (Cross-Origin Resource Sharing) is not properly setup or your Cloud Run can’t handle the POST method.
Found this answer by John Hanley from this post that might be helpful for you:
Notice the HTTP 302 response on your POST. That means the client is being instructed to go to a new Location. Your client is then converting the POST into a GET and making another request at the new location.
Cloud Run only supports HTTPS. Your POST request is being sent to an HTTP endpoint. The Cloud Run frontend will automatically redirect the client to the HTTPS endpoint.
Change your request from using HTTP to HTTPS.
You can also check this documentation about Handling CORs (a way to let applications running on one domain access another domain) and CORS limitations since you are encountering a CORS Error.
Upvotes: 0