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 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 };