Web sdk: verifybuyer stalls in sandbox

I am having difficulty processing a credit card payment through the web SDK in sandbox mode.

So I added some logging javascript code to see where things are stuck. Upon entering a test credit card number that doesn’t require verification as described in square’s documention and clicking pay $1.00, the button greys out and the page is stuck.
Based on my debugging, the code is stuck at:

const verificationToken = await verifyBuyer(payments, token);

and it doesn’t reach…
const paymentResults = await createPayment(token,verificationToken);

I have used the correct appid and locationid values for testing.

and also how do I change the zip code field to the postal code field? I am in Canada.

The code excluding CSS and excluding the actual appID and locationID values follows.

<!doctype html>
<html>
  <head>
    <link href="/app.css" rel="stylesheet"> <script src="https://sandbox.web.squarecdn.com/v1/square.js"></script>
    <script>
      const appId='my-sandbox-app-id',locationId='my-location-id';
      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.00',
          billingContact: {
            givenName: 'John',familyName: 'Doe',
            email: '[email protected]',phone: '3214563987',
            addressLines: ['123 Main Street', 'Apartment 1'],
            city: 'London',state: 'LND',countryCode: 'GB',
          },
          currencyCode: 'CAD',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) {
	 console.debug("Starting....");
          event.preventDefault();

          try {
            // disable the submit button as we await tokenization and make a payment request.
   	    console.log("Greying button...");
            cardButton.disabled = true;
   	    console.log("Getting token");
	    const token = await tokenize(card);
 	    console.log("Validating buyer...");
            const verificationToken = await verifyBuyer(payments, token);
   	    console.log("Making payment...");
            const paymentResults = await createPayment(token,verificationToken);
   	    console.log("Showing success...");
            displayPaymentResults('SUCCESS');
            console.debug('Payment Success', paymentResults);
   	    console.log("DONE!");
          } catch (e) {
            cardButton.disabled = false;
            displayPaymentResults('FAILURE');
            console.error(e.message);
   	    console.log("Payment failed!");
          }
        }

        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>

What value did you use and are there any console errors? I just tested our quickstart with test values and they all worked as expected.

As for zip code and postal code place holders the Web Payments SDK will automatically infer what value to fill in production. Sandbox will unfortunately always show zip code at this time. :slightly_smiling_face:

I was unable to add photo attachments and text here because it gives me a 422 error, but I notice in my logs that the verifications endpoint that the script was trying to access gives a 400 error.

y

Are you passing in GB for the country even though your charging CAD? :slightly_smiling_face:

I played around with countries. I even matched them up (using ca for canada). I also tried removing some parameters and if I remove the billing contact portion of the json request, the function returns with a failure but as soon as I add any data for billing contact, it locks up.

So now I decided to run tests while bypassing the verification step completely and I ran into a new set of problems.

I used your test credit card with CVV 111 and an expiry date of 11/25 with zip code 11111.

I configured the /payment link in the test server so it eventually directs to your payments endpoint.

On my test server, I create a customer by calling the customers endpoint and I get a customer ID. I then import that into the payment call

My payment call has this JSON code as the parameters to the call:

{"idempotency_key":"49","note":"This test payment has worked well","buyer_email_address":"[email protected]","reference_id":"reference-label","location_id":"(location ID assigned to me for sandbox environment)","customer_id":"K8PEDMP5747S9NTJKDC41414M4","source_id":"(value received from web SDK)","autocomplete":true,"amount_money":{"amount":100,"currency":"CAD"},"app_fee_money":{"amount":0,"currency":"CAD"},"tip_money":{"amount":0,"currency":"CAD"},"customer_details":{"customer_initiated":true,"seller_keyed_in":false}}

After calling the code, I receive an error:

{"errors": [{"code": "CARD_DECLINED_VERIFICATION_REQUIRED","detail": "Authorization error: 'CARD_DECLINED_VERIFICATION_REQUIRED'","category": "PAYMENT_METHOD_ERROR"}],"payment": {"id": "HJ7m7BErkOvo38CJBTMoXrrU9ZDZY","created_at": "2024-03-09T03:20:13.131Z","updated_at": "2024-03-09T03:20:13.250Z","amount_money": {"amount": 100,"currency": "CAD"},"tip_money": {"amount": 0,"currency": "CAD"},"app_fee_money": {"amount": 0,"currency": "CAD"},"status": "FAILED","delay_duration": "PT168H","source_type": "CARD","card_details": {"status": "FAILED","card": {"card_brand": "MASTERCARD","last_4": "0005","exp_month": 11,"exp_year": 2025,"fingerprint": "sq-1-Ua9RPMQ2-0FqjXeJ5GQkARyFXB3G-TYEng1ntGU9en1229_1nmHJXRnAW9_9MU8tfw","card_type": "CREDIT","prepaid_type": "NOT_PREPAID","bin": "522222"},"entry_method": "KEYED","cvv_status": "CVV_NOT_CHECKED","avs_status": "AVS_NOT_CHECKED","errors": [{"code": "CARD_DECLINED_VERIFICATION_REQUIRED","detail": "Authorization error: 'CARD_DECLINED_VERIFICATION_REQUIRED'","category": "PAYMENT_METHOD_ERROR"}],"statement_description": "","card_payment_timeline": {"authorized_at": "2024-03-09T03:20:13.250Z"}},"location_id": "LWQMB4156V036","order_id": "Xk99XiGpqRQpdyJssxHzxCR7SdNZY","reference_id": "reference-label","buyer_email_address": "[email protected]","note": "This test payment has worked well","customer_id": "K8PEDMP5747S9NTJKDC41414M4","total_money": {"amount": 100,"currency": "CAD"},"approved_money": {"amount": 0,"currency": "CAD"},"delay_action": "CANCEL","delayed_until": "2024-03-16T03:20:13.131Z","application_details": {"square_product": "ECOMMERCE_API","application_id": "(my sandbox application id)"},"version_token": "QmSeZGYVaO8rb9A8PCcZT5cR5xIKEzlPXLGka2crRJp6o"}}

Am I missing parameters or am I using the wrong card number for testing?

This is the mastercard credit card I’m using: 5222 2200 0000 0005 with CVV 111

[SOLVED] in an obstacle-course fashion

Ok so it turns out that if I pass in incorrect parameters when calling the verifyBuyer function, the payment form stalls at that point with NO obvious error message anywhere.

So the only way I pulled this off is as follows:

Each time there’s a stall, go into the browser’s developer tools (via tools, browser tools, web developer tools in firefox)

Click on Network and reload the payment form. When it stalls again, go back to the developer tools and look for the request where the status is 400. In my case, its for the request “verifications”.

Then go to “Response” and it will tell you what you need to change.

All that obstacle course just to see an error.

But it really doesn’t help the end customer if they are asked to put in their country and they type in an incorrect code. It would make them sad to see a form stall.

Thank you for sharing your findings. :slightly_smiling_face: