PHP SDK process-card.php erroring out

AppID sandbox-sq0idb-fPhWF8SVrMcRSyoVuTHMmQ
The process-card.php script fails and it seems the first error is here:
$currency = $client->getLocationsApi()->retrieveLocation(env(‘SQUARE_LOCATION_ID’))->getResult()->getLocation()->getCurrency();

(locationID is being successfully retrieved from the .env file).
I have no idea what the following errors in plesk logs mean and would appreciate your assistance, please.

The server error logs show 5 warnings.

  1. mod_fcgid: stderr: PHP Fatal error: Uncaught TypeError: Argument 1 passed to Square\ApiHelper::appendUrlWithTemplateParameters() must be of the type string, null given, called in /var/www/vhosts/example.co.uk/demo.example.co.uk/square-payment-gateway/phpsdk-integration/vendor/square/square/src/SquareClient.php on line 163 and defined in /var/www/vhosts/example.co.uk/demo.example.co.uk/square-payment-gateway/phpsdk-integration/vendor/square/square/src/ApiHelper.php:24, referer: https://demo.example.co.uk/square-payment-gateway/phpsdk-integration/index.php

  2. mod_fcgid: stderr: Stack trace:, referer: https://demo.example.co.uk/square-payment-gateway/phpsdk-integration/index.php

  3. mod_fcgid: stderr: #0 /var/www/vhosts/example.co.uk/demo.example.co.uk/square-payment-gateway/phpsdk-integration/vendor/square/square/src/SquareClient.php(163): Square\ApiHelper::appendUrlWithTemplateParameters(NULL, Array, false), referer: https://demo.example.co.uk/square-payment-gateway/phpsdk-integration/index.php

  4. mod_fcgid: stderr: #1 /var/www/vhosts/example.co.uk/demo.example.co.uk/square-payment-gateway/phpsdk-integration/vendor/square/square/src/Apis/LocationsApi.php(177): Square\SquareClient->getBaseUri(), referer: https://demo.example.co.uk/square-payment-gateway/phpsdk-integration/index.php

  5. mod_fcgid: stderr: #2 /var/www/vhosts/example.co.uk/demo.example.co.uk/square-payment-gateway/phpsdk-integratio in /var/www/vhosts/example.co.uk/demo.example.co.uk/square-payment-gateway/phpsdk-integration/vendor/square/square/src/ApiHelper.php on line 24, referer: https://demo.example.co.uk/square-payment-gateway/phpsdk-integration/index.php

This is my index.php file.

<?php


require 'vendor/autoload.php';


use Square\SquareClient;
use Square\Environment;
use function Prinx\Dotenv\env;

// use the loadEnv function
use function Prinx\Dotenv\loadEnv;

// Load the specific env file 
$env_path = __DIR__ . '/.env';
loadEnv($env_path);



$db_host = env('DB_HOST');
$db_user = env('DB_USER');
$db_name = env('DB_NAME');
$db_password = env('DB_PASS');
$square_application_id = env('SQUARE_APPLICATION_ID');
$square_access_token = env('SQUARE_ACCESS_TOKEN');
$square_location_id = env('SQUARE_LOCATION_ID');
$environment = env('ENVIRONMENT');



// Pulled from the .env file and upper cased e.g. SANDBOX, PRODUCTION.
$upper_case_environment = strtoupper($environment);
$full_environment_string = "Environment::$upper_case_environment";

$client = new SquareClient([
    'accessToken' => '$square_access_token',
    'environment' => '$upper_case_environment'
]);



?>
<html>
<head>
  <title>My Payment Form</title>
  <!-- link to the SqPaymentForm library -->
<script type="text/javascript" src=
<?php
    echo "\"";
   
    echo ( $environment === 'PRODUCTION' )  ?  "https://js.squareup.com/v2/paymentform"
                                        :  "https://js.squareupsandbox.com/v2/paymentform";
    echo "\"";
 ?>
  ></script>
 <script type="text/javascript">
window.applicationId =
  <?php
    echo "\"";
    echo env('SQUARE_APPLICATION_ID');
    echo "\"";
  ?>;
window.locationId =
<?php
  echo "\"";
  echo env('SQUARE_LOCATION_ID');
  echo "\"";
?>;
  </script>

<!-- link to the local SqPaymentForm initialization -->
 <script type="text/javascript" src="https://cdn.jsdelivr.net/gh/square/connect-api-examples/templates/web-ui/payment-form/custom/sq-payment-form.js"></script>
<!-- link to the custom styles for SqPaymentForm -->
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/square/connect-api-examples/templates/web-ui/payment-form/custom/sq-payment-form.css">
</head>
<body>
<!-- Begin Payment Form -->
<div class="sq-payment-form">
<!--
  Square's JS will automatically hide these buttons if they are unsupported
  by the current device.
-->
<div id="sq-walletbox">
  <button id="sq-google-pay" class="button-google-pay"></button>
  <button id="sq-apple-pay" class="sq-apple-pay"></button>
  <button id="sq-masterpass" class="sq-masterpass"></button>
  <div class="sq-wallet-divider">
    <span class="sq-wallet-divider__text">Or</span>
  </div>
</div>
<div id="sq-ccbox">
  <!--
    You should replace the action attribute of the form with the path of
    the URL you want to POST the nonce to (for example, "/process-card").

    You need to then make a "Charge" request to Square's Payments API with
    this nonce to securely charge the customer.

    Learn more about how to setup the server component of the payment form here:
    https://developer.squareup.com/docs/payments-api/overview
  -->
  
  <!-- The 109 on the form path is for testing an orderId 
  We know an orderId that is valid and we want to test with it. 
  Later, we'll add a query to get the orderId or insert this form in a page which knows the orderId. -->
  <form id="nonce-form" novalidate action="/square-payment-gateway/phpsdk-integration/process-card.php/109" method="post">
    <div class="sq-field">
      <label class="sq-label">Card Number</label>
      <div id="sq-card-number"></div>
    </div>
    <div class="sq-field-wrapper">
      <div class="sq-field sq-field--in-wrapper">
        <label class="sq-label">CVV</label>
        <div id="sq-cvv"></div>
      </div>
      <div class="sq-field sq-field--in-wrapper">
        <label class="sq-label">Expiration</label>
        <div id="sq-expiration-date"></div>
      </div>
      <div class="sq-field sq-field--in-wrapper">
        <label class="sq-label">Postal</label>
        <div id="sq-postal-code"></div>
      </div>
    </div>
    <div class="sq-field">
      <button id="sq-creditcard" class="sq-button" onclick="onGetCardNonce(event)">
        Make Payment
      </button>
    </div>
    <!--
      After a nonce is generated it will be assigned to this hidden input field.
    -->
    <div id="error"></div>
    <input type="hidden" id="card-nonce" name="nonce">
  </form>
</div>
</div>
<!-- End Payment Form -->
</body>
</html>

Based on this:

$client = new SquareClient([
    'accessToken' => '$square_access_token',
    'environment' => '$upper_case_environment'
]);

It looks like you aren’t passing the variables, and you’re passing literal strings. The line it references is attempting to pull the server_url for the environment, but given that’s an invalid environment you’re passing, it will fail and return null. I imagine you want to do this instead:

$client = new SquareClient([
    'accessToken' => $square_access_token,
    'environment' => $upper_case_environment
]);

Let me know if that doesn’t work of course.

Unfortunately that has made no difference. Are you seeing anything your side?

It’s actually the propcess-payment.php script that is erroring but that seems to be because the nonce is not being sent from index.php.

Are you seeing a different error, now? I won’t be able to see anything on Square’s end based on the above errors; those are all client-side before it hits Square’s servers. My suggestion should’ve resolved your original error, but it sounds like you’re seeing a different error, now? Can you clarify what you mean by the nonce is not being sent?

Thanks for your reply.
The errors are the same.
The process payment script is shown below.

The var_dump outputs like this:

dumping=array(2) {
  ["nonce"]=>
  string(0) ""
      ["nds-pmd"]=>
  string(1495) "{"jvqtrgQngn":{"oq":"redacted"}"
}

Later line 72, this is outputted
here 1
nonce =

That’s why I think the nonce is not being passed from index.php.

This is the process-payment.php script.

<?php

require 'vendor/autoload.php';

use Square\Models\Money;
use Square\Models\CreatePaymentRequest;
use Square\Exceptions\ApiException; 
use Square\SquareClient;


use function Prinx\Dotenv\env;
// use the loadEnv function
use function Prinx\Dotenv\loadEnv;

// Load the specific env file
$env_path = __DIR__ . '/.env';
loadEnv($env_path);

echo "<pre>dumping=";
var_dump($_POST);
echo "</pre>";

// The access token to use in all Connect API requests.
// Set your environment as *sandbox* if you're just testing things out.
$access_token =  env('SQUARE_ACCESS_TOKEN');
$environment = env('ENVIRONMENT'); 
$square_location_id = env('SQUARE_LOCATION_ID');
$upper_case_environment = strtoupper($environment);


// Initialize the Square client.
$client = new SquareClient([
  'accessToken' => $access_token,  
  'environment' => $upper_case_environment
]);

// Helps ensure this code has been reached via form submission
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
  error_log('Received a non-POST request');
  echo 'Request not allowed';
  http_response_code(405);
  return;
}

// Fail if the card form didn't send a value for `nonce` to the server
$nonce = $_POST['nonce'];
if (is_null($nonce)) {
  echo 'Invalid card data';
  http_response_code(422);
  return;
}

$payments_api = $client->getPaymentsApi();


// To learn more about splitting payments with additional recipients,
// see the Payments API documentation on our [developer site]
// (https://developer.squareup.com/docs/payments-api/overview).

$money = new Money();
  // Monetary amounts are specified in the smallest unit of the applicable currency.
  // This amount is in cents. It's also hard-coded for $1.00, which isn't very useful.

 echo "<pre>
 here 1
 nonce = $nonce</pre>";
$money->setAmount(100);


 // Set currency to the currency for the location
$currency = $client->getLocationsApi()->retrieveLocation(env('SQUARE_LOCATION_ID'))->getResult()->getLocation()->getCurrency();
//$currency = 'GBP';
echo "here2";

$money->setCurrency($currency);



// Every payment you process with the SDK must have a unique idempotency key.
// If you're unsure whether a particular payment succeeded, you can reattempt
// it with the same idempotency key without worrying about double charging
// the buyer.
$create_payment_request = new CreatePaymentRequest($nonce, uniqid(), $money);


// The SDK throws an exception if a Connect endpoint responds with anything besides
// a 200-level HTTP code. This block catches any exceptions that occur from the request.
try {
  $response = $payments_api->createPayment($create_payment_request);
  // If there was an error with the request we will
  // print them to the browser screen here
  if ($response->isError()) {
    echo 'Api response has Errors';
    $errors = $response->getErrors();
    echo '<ul>';
    foreach ($errors as $error) {
        echo '<li>❌ ' . $error->getDetail() . '</li>';
    }
    echo '</ul>';
    exit();
 }
echo '<pre>';
  print_r($response);
  echo '</pre>';
} catch (ApiException $e) {
  echo 'Caught exception!<br/>';
  echo('<strong>Response body:</strong><br/>');
  echo '<pre>'; var_dump($e->getResponseBody()); echo '</pre>';
  echo '<br/><strong>Context:</strong><br/>';
  echo '<pre>'; var_dump($e->getContext()); echo '</pre>';
  exit();
}

I took a look at our logs for your application (sandbox-sq0idb-fPhWF8SVrMcRSyoVuTHMmQ) and haven’t seen a successful call to Square to create a nonce, so it sounds like you’re not even requesting a card nonce at all, or something is breaking before that point, so it shouldn’t even be going to process-payment.php yet. How are you handling the nonce generation in your Javascript currently?

The only place in the docs I can find with a get card nonce request, is in the payment form - in the walkthrough doc. Of course, that only works with 4111 1111 1111 1111 cvv 111 etc and not the card details the SDK requires.

Here is the nonce JS i used.

function onGetCardNonce(event) {

// Don't submit the form until SqPaymentForm returns with a nonce
event.preventDefault();
// Request a nonce from the SqPaymentForm object
paymentForm.requestCardNonce();
}

Please link me to the docs which properly set out what is necessary. I have spent over a week on this because the docs are rubbish. I have integrated successfully for a different client of mine, with Stripe in less than half a day. This client wants to stay with Square but my time costs may affect that decision.

Correct, you can only generate a nonce using either the Square Payment Form (web) or In-App Payments SDK (which is basically a mobile payment form). Since my last comment, I do see that you were able to generate three nonces successfully, so it sounds like progress was made. However, I do not see a follow up CreatePayment call, so something went wrong prior to this call.

Can you confirm what happened after generating the nonces? If anything is preventing the CreatePayment call, it would be happening on your side since I am not seeing any server logs, so I do not know what went wrong.