Unexpected End of Data at line 1 column 1 of the JSON Data

I am a new square developer and working on integrating payments within a wordpress plugin. Currently I am receiving an error when attempting to process the payment after returning from an ajax function: “Unexpected End of Data at line 1 column 1 of the JSON Data” on Firefox Console.

I am using composer to install the Square SDK inside /<plugin_root>/<path_to_composer_root_dir>/. Running php composer.phar show verifies that I am using version 37.1.0.20240604 of the api, which is the same version that my application sandbox api is using.

Upon DOMContentLoaded I attach a handlePaymentMethodSubmission(event, card) handler to the click event on the make payment button on my order form page. The handlePaymentMethodSubmission is as follows:

	async function handlePaymentMethodSubmission(event, card) {
		event.preventDefault();

		try {
			// disable the submit button as we await tokenization and make a payment request.
			cardButton.disabled = true;
			const token = await tokenize(card);
			const verificationToken = await verifyBuyer(payments, token);
			const paymentResults = await createPayment(
				token,
				verificationToken,
			);
			displayPaymentResults('SUCCESS');

			console.debug('Payment Success', paymentResults);
		} catch (e) {
			cardButton.disabled = false;
			displayPaymentResults('FAILURE');
			console.error(e.message);
		}
	}

Inside this method, the createPayment(token, verificationToken) method is the point at which I interface with the wordpress ajax, and is implemented like so:

async function createPayment(token, verificationToken){
	console.log("Inside Ajax method.");
	
	jQuery().post(ajax_obj.ajax_url, {
		_ajax_nonce: ajax_obj.nonce,
		action: "process_payment",
		payload: JSON.stringify({
			locationId,
			sourceId: token,
			verificationToken,
			idempotencyKey: window.crypto.randomUUID(),
			amount: 100.69,
		}, function(data){
			console.log("Returned from Ajax.");
			const paymentResponse = JSON.parse(data);
			if(paymentResponse.ok){
				alert("Payment Reponse was ok");
				return paymentResponse;
			}

			const errorBody = paymentResponse.text();
			throw new Error(errorBody);
		})
	});
}

Wordpress requires that ajax calls are sent to an ‘wp_admin_ajax’ backend endpoint, with the ‘action’ parameter being used to refer the call to the proper php backend handler method. In this case, the “process_payment” value set on the action parameter is essentially equivalent to the “/payment” endpoint using the fetch api in the example code on the web-payments quickstart example on github.

Once the request is sent to the backend, the following php method ends up servicing the ajax request:

        function process_payment(){
            $payload = json_decode(file_get_contents('php://input'));
            
            $client = new SquareClient([
                'accessToken' => getEnv('SQUARE_ACCESS_TOKEN'),
                'environment' => Environment::SANDBOX,
                // 'environment' => Environment::CUSTOM,
                // 'customUrl' => 'https://store.kohlodt.com',
                'numberOfRetries' => 3,
                'timeout' => 45,
            ]);
            
            $payment = new Money();
            $payment->setAmount($payload.amount);
            $payment->setCurrency('USD');

            $fee = new Money();

            if($payload.amount > 100){
                $fee->setAmount($payload.amount * 0.035);
            } else {
                $fee->setAmount(0.35);
            } $fee->setCurrency('USD');

            $request = new CreatePaymentRequest($payload.sourceId, $payload.idempotencyKey);
            $request.setAmountMoney($payment);
            $request.setAppFeeMoney($fee);
            $request.setAutoComplete(true);

            try {                
                // $apiResponse = $client->getLocationsApi()->listLocations();
                $apiResponse = $client->getPaymentsApi()->createPayment($request);

                if($apiResponse->isSuccess()){
                    $result = $apiResponse->getResult();
                    echo $result;
                } else {
                    $errors = $apiResponse->getErrors();
                    echo $errors;
                }
            } catch (ApiException $e){
                echo "Api Exception ocurred: <b/>";
                echo $e->getMessage() . "<p/>";
            }

            die();
        }

The die(); at the end is a wordpress requirement, as all ajax handlers need to kill themselves after finishing processing. But the apiResponses/Errors still get sent back to the front end for further processing.

I have tried to file_put_contents($file_path, json_encode($apiResponse->getResult())) to multiple filesystem locations with maximum permissive settings across the board, and am not using SELinux. In all cases, no file has been created at all, and nothing has shown up in the server logs regarding this. I am going to change the loglevel to debug mode and currently investigate this further.

In firefox the console stack trace after returning from the backend php handler shows:

JSON.parse: unexpected end of data at line 1 column 1 of the JSON data square.js:3:94490
    lt https://sandbox.web.squarecdn.com/v1/square.js:3
    handlePaymentMethodSubmission http://localhost:1113/wp-content/plugins/ecommerce-product-catalog/modules/cart/js/squareup-integration.js:224
    <anonymous> http://localhost:1113/wp-content/plugins/ecommerce-product-catalog/modules/cart/js/squareup-integration.js:230
    n https://sandbox.web.squarecdn.com/v1/square.js:3
    (Async: EventListener.handleEvent)
    Tr https://sandbox.web.squarecdn.com/v1/square.js:3
    mt https://sandbox.web.squarecdn.com/v1/square.js:3
    <anonymous> http://localhost:1113/wp-content/plugins/ecommerce-product-catalog/modules/cart/js/squareup-integration.js:229
    <anonymous> http://localhost:1113/wp-content/plugins/ecommerce-product-catalog/modules/cart/js/squareup-integration.js:178

It seems that the issue occurs inside square.js. I suspect that for whatever reason, my code isn’t properly sending back a proper json response from the backend or elsewhere. I also can’t seem to find anything resembling json in a response header in any of the network requests in the network panel of the Firefox developer console.

Not sure what else to say. Any help would be greatly appreciated.

It looks like you’re encountering a common issue where the backend PHP script is not returning a valid JSON response, which is causing the JSON.parse error in your frontend JavaScript.

PHP Backend Debugging

First, ensure that your PHP function is correctly processing the payment and returning a JSON response.

Correct JSON Response

Modify your PHP process_payment function to ensure it always returns a valid JSON response:

function process_payment(){
    $payload = json_decode(file_get_contents('php://input'));

    $client = new SquareClient([
        'accessToken' => getenv('SQUARE_ACCESS_TOKEN'),
        'environment' => Environment::SANDBOX,
        'numberOfRetries' => 3,
        'timeout' => 45,
    ]);

    $payment = new Money();
    $payment->setAmount($payload->amount);
    $payment->setCurrency('USD');

    $fee = new Money();
    if($payload->amount > 100){
        $fee->setAmount($payload->amount * 0.035);
    } else {
        $fee->setAmount(0.35);
    }
    $fee->setCurrency('USD');

    $request = new CreatePaymentRequest($payload->sourceId, $payload->idempotencyKey);
    $request->setAmountMoney($payment);
    $request->setAppFeeMoney($fee);
    $request->setAutoComplete(true);

    try {
        $apiResponse = $client->getPaymentsApi()->createPayment($request);

        if($apiResponse->isSuccess()){
            $result = $apiResponse->getResult();
            echo json_encode(['ok' => true, 'result' => $result]);
        } else {
            $errors = $apiResponse->getErrors();
            echo json_encode(['ok' => false, 'errors' => $errors]);
        }
    } catch (ApiException $e){
        echo json_encode(['ok' => false, 'error' => $e->getMessage()]);
    }

    die();
}

JavaScript AJAX Handling

Ensure your JavaScript AJAX call is correctly handling the response:

Correct AJAX Call

Update your createPayment function to properly handle the AJAX response and ensure that the data variable is correctly parsed:

async function createPayment(token, verificationToken) {
    console.log("Inside Ajax method.");

    return new Promise((resolve, reject) => {
        jQuery.post(ajax_obj.ajax_url, {
            _ajax_nonce: ajax_obj.nonce,
            action: "process_payment",
            payload: JSON.stringify({
                locationId,
                sourceId: token,
                verificationToken,
                idempotencyKey: window.crypto.randomUUID(),
                amount: 100.69,
            })
        }, function (data) {
            console.log("Returned from Ajax.");
            try {
                const paymentResponse = JSON.parse(data);
                if (paymentResponse.ok) {
                    alert("Payment Response was ok");
                    resolve(paymentResponse);
                } else {
                    const errorBody = paymentResponse.errors ? paymentResponse.errors : paymentResponse.error;
                    reject(new Error(errorBody));
                }
            } catch (e) {
                reject(new Error("Invalid JSON response"));
            }
        }).fail(function (jqXHR, textStatus, errorThrown) {
            reject(new Error("AJAX call failed: " + textStatus + ", " + errorThrown));
        });
    });
}

Ensure that your JSON payload structure matches what the Square API expects. Double-check the keys and values you’re sending. :slightly_smiling_face:

I face the same issue, as you say in your question. thanks for sharing this post.

So, it turns out the issue was that I forgot to properly enclose the payload data object that I was creating to send to the backend. Without the final brace, the javascript was essentially running the methods for handling the ajax response and attempting to JSON.stringify the results, which was causing all sorts of strange issues. Fixing the braces and adding the additional testing code that was suggested really helped. Thank you very much, Bryan!