Sandbox Payment fails on default values

Hi - I’m building my first php card-payment for a customer and have the web-payments-quickstart working but the default values for the VISA card (4111 1111 1111 1111, 07/25, 111, 07924 (my zip code)) result in a “Payment Failed” method.
I’m using http://localhost:3000/examples/card-payment as my address.
My code is as follows:

<!doctype html>
<html>
  <head>
    <link href="/app.css" rel="stylesheet" />
    <script
      type="text/javascript"
      src="https://sandbox.web.squarecdn.com/v1/square.js"
    ></script>
    <script>
      const appId = 'sandbox-sq0idb-aw1FtqwNeZro48yfTz39OA';
      const locationId = 'LFW5JTF75XRGJ';

      async function initializeCard(payments) {
        const card = await payments.card();
        await card.attach('#card-container');

        return card;
      }

      async function createPayment(token, verificationToken) {
        const body = JSON.stringify({
          locationId,
          sourceId: token,
          verificationToken,
          idempotencyKey: window.crypto.randomUUID(),
        });

        const paymentResponse = await fetch('/payment', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body,
        });

        if (paymentResponse.ok) {
          return paymentResponse.json();
        }

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

      async function tokenize(paymentMethod) {
        const tokenResult = await paymentMethod.tokenize();
        if (tokenResult.status === 'OK') {
          return tokenResult.token;
        } else {
          let errorMessage = `Tokenization failed with status: ${tokenResult.status}`;
          if (tokenResult.errors) {
            errorMessage += ` and errors: ${JSON.stringify(
              tokenResult.errors,
            )}`;
          }

          throw new Error(errorMessage);
        }
      }

      // Required in SCA Mandated Regions: Learn more at https://developer.squareup.com/docs/sca-overview
      async function verifyBuyer(payments, token) {
        const verificationDetails = {
          amount: '1',
          billingContact: {
            givenName: 'John',
            familyName: 'Doe',
            email: '[email protected]',
            phone: '3214563987',
            addressLines: ['123 Main Street', 'Apartment 1'],
            city: 'London',
            state: 'LND',
            countryCode: 'GB',
          },
          currencyCode: 'GBP',
          intent: 'CHARGE',
        };

        const verificationResults = await payments.verifyBuyer(
          token,
          verificationDetails,
        );
        return verificationResults.token;
      }

      // status is either SUCCESS or FAILURE;
      function displayPaymentResults(status) {
        const statusContainer = document.getElementById(
          'payment-status-container',
        );
        if (status === 'SUCCESS') {
          statusContainer.classList.remove('is-failure');
          statusContainer.classList.add('is-success');
        } else {
          statusContainer.classList.remove('is-success');
          statusContainer.classList.add('is-failure');
        }

        statusContainer.style.visibility = 'visible';
      }

      document.addEventListener('DOMContentLoaded', async function () {
        if (!window.Square) {
          throw new Error('Square.js failed to load properly');
        }

        let payments;
        try {
          payments = window.Square.payments(appId, locationId);
        } catch {
          const statusContainer = document.getElementById(
            'payment-status-container',
          );
          statusContainer.className = 'missing-credentials';
          statusContainer.style.visibility = 'visible';
          return;
        }

        let card;
        try {
          card = await initializeCard(payments);
        } catch (e) {
          console.error('Initializing Card failed', e);
          return;
        }

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

        const cardButton = document.getElementById('card-button');
        cardButton.addEventListener('click', async function (event) {
          await handlePaymentMethodSubmission(event, card);
        });
      });
    </script>
  </head>
  <body>
    <form id="payment-form">
      <div id="card-container"></div>
      <button id="card-button" type="button">Pay $1.00</button>
    </form>
    <div id="payment-status-container"></div>
  </body>
</html>

How do I troubleshoot this?
Thanks for the help!

Where did you get the location_id: LFW5JTF75XRGJ. According to the error your not authorized to take payments with that location. If that’s a separate Square account you’ll need to make sure your using the access token from the OAuth process to charge the customer.

Hi Bryan - Thanks for the response.
I’m using the Location value from:

Should I be using something else?

Hey @rretzko! It looks like your application ID and location ID are fine, but your access token is not matching. Please don’t share the actual value here publicly, but can you verify if you’re using the Sandbox Access Token?

Hi Josh - Thanks for the reply. I have the Access Token, but didn’t see anywhere in the Card payments scripts where that value was requested.
Where is that included?

[//cdck-file-uploads-global.s3.dualstack.us-west-2.amazonaws.com/square/original/2X/b/b3c13c87bbc3edb0812f111e5e03bc6dcbb3b0cd.png]

The Access Token is only used on your backend to authenticate your API request, so it won’t be in the payments script with the Application ID and Location ID. If you’re building off of the Web Payments SDK quickstart, see this step for where it’s set.

Otherwise, you’ll need to trace back through your logic from where you’re making the API request to find where the access token is set.

Let me know if there’s anything I can clarify!

Hi Josh - Thanks for the additional information. To reduce the injection of my own code, I’m working with the vanilla web-payments-quickstart repository.

  • I’ve hard-coded the appId and locationId (see attached file)

  • I’ve created a .env.sandbox file in the root folder with the SQUARE_ACCESS_TOKEN value.

  • I’m still getting the payment failed message.

  • Square is really successful, so I have no doubt that the problem is on my end, but I don’t know what I could be missing in the vanilla impementation.

Your help is appreciated!

card-payment.html

<!doctype html>
<html>
  <head>
    <link href="/app.css" rel="stylesheet" />
    <script
      type="text/javascript"
      src="https://sandbox.web.squarecdn.com/v1/square.js"
    ></script>
    <script>
      const appId = 'sandbox-sq0idb-aw1FtqwNeZro48yfTz39OA';
      const locationId = 'LFW5JTF75XRGJ';

      async function initializeCard(payments) {
        const card = await payments.card();
        await card.attach('#card-container');

        return card;
      }

      async function createPayment(token, verificationToken) {
        const body = JSON.stringify({
          locationId,
          sourceId: token,
          verificationToken,
          idempotencyKey: window.crypto.randomUUID(),
        });

        const paymentResponse = await fetch('/payment', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body,
        });

        if (paymentResponse.ok) {
          return paymentResponse.json();
        }

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

      async function tokenize(paymentMethod) {
        const tokenResult = await paymentMethod.tokenize();
        if (tokenResult.status === 'OK') {
          return tokenResult.token;
        } else {
          let errorMessage = `Tokenization failed with status: ${tokenResult.status}`;
          if (tokenResult.errors) {
            errorMessage += ` and errors: ${JSON.stringify(
              tokenResult.errors,
            )}`;
          }

          throw new Error(errorMessage);
        }
      }

      // Required in SCA Mandated Regions: Learn more at https://developer.squareup.com/docs/sca-overview
      async function verifyBuyer(payments, token) {
        const verificationDetails = {
          amount: '30',
          billingContact: {
            givenName: 'John',
            familyName: 'Doe',
            email: '[email protected]',
            phone: '3214563987',
            addressLines: ['123 Main Street', 'Apartment 1'],
            city: 'London',
            state: 'LND',
            countryCode: 'GB',
          },
          currencyCode: 'GBP',
          intent: 'CHARGE',
        };

        const verificationResults = await payments.verifyBuyer(
          token,
          verificationDetails,
        );
        return verificationResults.token;
      }

      // status is either SUCCESS or FAILURE;
      function displayPaymentResults(status) {
        const statusContainer = document.getElementById(
          'payment-status-container',
        );
        if (status === 'SUCCESS') {
          statusContainer.classList.remove('is-failure');
          statusContainer.classList.add('is-success');
        } else {
          statusContainer.classList.remove('is-success');
          statusContainer.classList.add('is-failure');
        }

        statusContainer.style.visibility = 'visible';
      }

      document.addEventListener('DOMContentLoaded', async function () {
        if (!window.Square) {
          throw new Error('Square.js failed to load properly');
        }

        let payments;
        try {
          payments = window.Square.payments(appId, locationId);
        } catch {
          const statusContainer = document.getElementById(
            'payment-status-container',
          );
          statusContainer.className = 'missing-credentials';
          statusContainer.style.visibility = 'visible';
          return;
        }

        let card;
        try {
          card = await initializeCard(payments);
        } catch (e) {
          console.error('Initializing Card failed', e);
          return;
        }

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

        const cardButton = document.getElementById('card-button');
        cardButton.addEventListener('click', async function (event) {
          await handlePaymentMethodSubmission(event, card);
        });
      });
    </script>
  </head>
  <body>
    <form id="payment-form">
      <div id="card-container"></div>
      <button id="card-button" type="button">Pay $1.00</button>
    </form>
    <div id="payment-status-container"></div>
  </body>
</html>

Great, working with the vanilla quickstart repository will definitely help narrow this down!

A few things to check first:

  • Did you install the dependencies for the project? (npm install)
  • Are you running the development server with npm run dev when testing this?

If you’re doing both of the above and still running into issues, can you look into your square.js file? This is where the quickstart’s credentials are set, fetched from your .env variables.

It might be a good idea to set some breakpoints here to determine what values are being set for your SQUARE_ACCESS_TOKEN. You could also just try hardcoding your access token here temporarily for troubleshooting purposes.

@rretzko As a follow up here, can you also check whether you are using your Personal Access Token (e.g. the one found on the Credentials section of your Developer Dashboard), or the OAuth Access Token for your test account? It looks like the location_id you’re using (LFW5JTF75XRGJ) is for a new test account you created, not the default test account. If you’re using this account, you’ll need to get that account’s OAuth Access token:

  1. Go to your Developer Dashboard
  2. Click “Sandbox Test Accounts”
  3. Click on the name of the test account you’re using
  4. Under “Authorized applications”, click the name of your app to open the dropdown and view the Access Token

Hi Josh - OK, thanks. I’ve made some progress in that I have the success message on the vanilla package now.
I’m still getting the “Payment Failed” on my application (png attached). Here’s what I’ve done:

  • Confirmed that SQUARE_SANDBOX_APPLICATION_ID is on my .env file, that it matches the value in my vanilla test, and is properly filling the appId var

  • Confirmed that SQUARE_SANDBOX_LOCATION_ID is on my .env file, that it matches the value in my vanilla test, and is properly filling the locationId var

  • Confirmed that SQUARE_ACCESS_TOKEN is on my .env file, that it matches the value in my vanilla test, and is properly filling the accessToken var which I added to the script.

  • I also created a .env.sandbox file with one entry: SQUARE_ACCESS_TOKEN=******************************************* ensuring that it matches the OATH value for the CJMEAtest project.

  • I also ran the npm install for good measure on my application.

  • I could not find a square.js file on my application’s file system, so, guessing, I’ve copied the /server file from the vanilla folder into the root service of my application (excluding the *.test.js files).

I am getting a 404 (Not found) response, but that’s not terribly helpful.

Clearly I’m missing a link somewhere and hopefully the info above will help you guide me on a next-step to take.

Hopefully this is helpful in your diagnosis. Perhaps I missed a step that should have installed the /server files?
Thanks for your guidance.

(attachments)

paymentFailedWithConsoleLog.png

Further update: I’ve tried the vanilla again with my sandbox and the customer’s sandbox credentials. Both display the “Payment Failed” message, but also return

{
  success: true,
  payment: {
    id: "5g4DKXmwlDnBQd4MtxibWIPQQu8YY",
    status: "COMPLETED",
    receiptUrl: "https://squareupsandbox.com/receipt/preview/5g4DKXmwlDnBQd4MtxibWIPQQu8YY",
    orderId: "kb74RvaHaZN1Kmk4wZylYqXT5sBZY"
  }
}

to the terminal.

Hi again - I’ve loaded the customer’s sandbox credentials into my local machine as follows:
.env

SQUARE_ACCESS_TOKEN=*****************
SQUARE_SANDBOX_APPLICATION_ID=sandbox-sq0idb-3C3Zh_0fPPyGXSgY******
SQUARE_SANDBOX_LOCATION=LXF6YB6******

.env.square

SQUARE_ACCESS_TOKEN=*****************

I’ve reverted the square.js file (note: which i copied onto my app as noted above) to

const client = new Client({
  environment: isProduction ? Environment.Production : Environment.Sandbox,
  accessToken: SQUARE_ACCESS_TOKEN,
});

Note: Hardcoding the SQUARE_ACCESS_TOKEN had no impact.

I’m getting four:

Uncaught (in promise) NotSupportedError: Failed to read the 'ready' property from 'ServiceWorkerContainer': 'ready' is only supported in pages.
    at downloader.js:1:25

errors in my console.log and then the 404 (Not Found) error.

Last entry for today: Retested vanilla card-payment. “Payment Failed” returned to the screen, but the following payload returned to the terminal:

#3 POST /payment                                                                                                                                                                               1:00:48 PM

{
  locationId: "LXF6YB6B5ZVNS",
  sourceId: "cnon:CBASENu97cf2rqZPRy9gMuoC4cUoAg",
  verificationToken: "verf:CA4SEG9Vvbc27OZgrBD6wf5xoZQgACgB",
  idempotencyKey: "a4c15050-2d9b-4a07-a44e-fe52f2219550"
}

Payment succeeded! {
  result: {
    payment: {
      id: 'bD7e7MYU20tIrMSkYoGeq6lV4uLZY',
      createdAt: '2024-10-12T17:00:50.443Z',
      updatedAt: '2024-10-12T17:00:50.728Z',
      amountMoney: [Object],
      totalMoney: [Object],
      approvedMoney: [Object],
      status: 'COMPLETED',
      delayDuration: 'PT168H',
      delayAction: 'CANCEL',
      delayedUntil: '2024-10-19T17:00:50.443Z',
      sourceType: 'CARD',
      cardDetails: [Object],
      locationId: 'LXF6YB6B5ZVNS',
      orderId: '2ropTpUo9998Q2waApgAMFRUSOHZY',
      riskEvaluation: [Object],
      buyerEmailAddress: '[email protected]',
      billingAddress: [Object],
      receiptNumber: 'bD7e',
      receiptUrl: 'https://squareupsandbox.com/receipt/preview/bD7e7MYU20tIrMSkYoGeq6lV4uLZY',
      applicationDetails: [Object],
      versionToken: '7IxY4ccUGE3uMS2IXAKCgpNYJpYVYfqIip8CXypwDGW6o'
    }
  },
  statusCode: 200
}
< #3 200 [+2052ms]                                                                                                                                                                               1:00:50 PM

{
  success: true,
  payment: {
    id: "bD7e7MYU20tIrMSkYoGeq6lV4uLZY",
    status: "COMPLETED",
    receiptUrl: "https://squareupsandbox.com/receipt/preview/bD7e7MYU20tIrMSkYoGeq6lV4uLZY",
    orderId: "2ropTpUo9998Q2waApgAMFRUSOHZY"
  }
}