Best way to integrate payment?

We currently use the terminal app to check people out (for monthly care or for a service as they checkout).

I would love to create a payment form page where I can pass the amount due and let them pay for that online OR send a link to pay for their care along with their text reminder for their visit so we don’t have to ask for payment in person.

I am using Laravel (with blade) and integrating the square payment form has not worked for me.

Is there a simple way to do this? I have connected my site to square to import payments taken using the card reader, but I really want to take it a step further.

There are so many API options I don’t know what way to go.

1 Like

Hi @drcarr welcome to the forums!

You’re totally right, there are a lot of options to choose from. I’ll try to break it down at a high-level, and can gladly answer additional questions that may arise.

  1. Square Payment Form - it sounds like you started here; basically these are credit card fields you can embed on your own website. However, you’ll also need to implement a backend to call the Payments API to actually handle the payment (the form only creates a secure token called a nonce, but doesn’t charge the customer). This is the most flexible option, but it does require the most work since you need to be familiar with coding and backend development.
  2. Square Checkout Links - these are much less flexible, but much easier to use. You can basically create a “checkout link” (which is a Square-hosted checkout page) from an item in your Square catalog, or simply an amount. You can then embed this link on your website via a simple link or even embedded button that a customer can click. The entire transaction takes place off of your website, though.
  3. Square Online - a free way to quickly start selling all of your items through Square, although since you already have a website this may not be ideal for you. However, it is quick and easy, and looks good!
  4. Square App Marketplace - this is a marketplace of Square partners (only applications that get approved by our internal teams are allowed on here at this time). There are integrations from full ecom solutions to tax or inventory management. Some of these are free and some of these are paid services, but the beauty is they do not require any coding/development on your part and may completely provide a solution for you.

If you prefer sticking to just our APIs, I’m happy to help dive into those questions (like why your payment form isn’t working), just let me know.

I am getting further, but I don’t know how to add my csrf token to a form like this one. I’m using Laravel and Blade. My code on the html side is:

<?php

use Square\Environment;

?>

My Payment Form /* Copyright 2019 Square Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */
    * {
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
    }

    body, html {
        background-color: #F7F8F9;
        color: #373F4A;
        font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
        font-weight: normal;
        height: 100%;
    }

    button {
        border: 0;
        font-weight: 500;
    }

    fieldset {
        margin: 0;
        padding: 0;
        border: 0;
    }

    #form-container {
        position: relative;
        width: 380px;
        margin: 0 auto;
        top: 50%;
        transform: translateY(-50%);
    }

    .third {
        float: left;
        width: calc((100% - 32px) / 3);
        padding: 0;
        margin: 0 16px 16px 0;
    }

    .third:last-of-type {
        margin-right: 0;
    }

    /* Define how SqPaymentForm iframes should look */
    .sq-input {
        height: 56px;
        box-sizing: border-box;
        border: 1px solid #E0E2E3;
        background-color: white;
        border-radius: 6px;
        display: inline-block;
        -webkit-transition: border-color .2s ease-in-out;
        -moz-transition: border-color .2s ease-in-out;
        -ms-transition: border-color .2s ease-in-out;
        transition: border-color .2s ease-in-out;
    }

    /* Define how SqPaymentForm iframes should look when they have focus */
    .sq-input--focus {
        border: 1px solid #4A90E2;
    }

    /* Define how SqPaymentForm iframes should look when they contain invalid values */
    .sq-input--error {
        border: 1px solid #E02F2F;
    }

    #sq-card-number {
        margin-bottom: 16px;
    }

    /* Customize the "Pay with Credit Card" button */
    .button-credit-card {
        width: 100%;
        height: 56px;
        margin-top: 10px;
        background: #4A90E2;
        border-radius: 6px;
        cursor: pointer;
        display: block;
        color: #FFFFFF;
        font-size: 16px;
        line-height: 24px;
        font-weight: 700;
        letter-spacing: 0;
        text-align: center;
        -webkit-transition: background .2s ease-in-out;
        -moz-transition: background .2s ease-in-out;
        -ms-transition: background .2s ease-in-out;
        transition: background .2s ease-in-out;
    }

    .button-credit-card:hover {
        background-color: #4281CB;
    }

</style>
Pay ${{number_format($payment->due_amt, 2)}}
    //TODO: paste code from step 2.1.1
    const idempotency_key = uuidv4();

    // Create and initialize a payment form object
    const paymentForm = new SqPaymentForm({
        // Initialize the payment form elements

        //TODO: Replace with your sandbox application ID
        applicationId: "MY_APP_ID",
        inputClass: 'sq-input',
        autoBuild: false,
        // Customize the CSS for SqPaymentForm iframe elements
        inputStyles: [{
            fontSize: '16px',
            lineHeight: '24px',
            padding: '16px',
            placeholderColor: '#a0a0a0',
            backgroundColor: 'transparent',
        }],
        // Initialize the credit card placeholders
        cardNumber: {
            elementId: 'sq-card-number',
            placeholder: 'Card Number'
        },
        cvv: {
            elementId: 'sq-cvv',
            placeholder: 'CVV'
        },
        expirationDate: {
            elementId: 'sq-expiration-date',
            placeholder: 'MM/YY'
        },
        postalCode: {
            elementId: 'sq-postal-code',
            placeholder: 'Postal'
        },
        // SqPaymentForm callback functions
        callbacks: {
            /*
            * callback function: cardNonceResponseReceived
            * Triggered when: SqPaymentForm completes a card nonce request
            */
            cardNonceResponseReceived: function (errors, nonce, cardData) {
                if (errors) {
                    // Log errors from nonce generation to the browser developer console.
                    console.error('Encountered errors:');
                    errors.forEach(function (error) {
                        console.error('  ' + error.message);
                    });
                    alert('Encountered errors, check browser developer console for more details');
                    return;
                }
                //TODO: Replace alert with code in step 2.1
                fetch('{{$payment->id}}', {
                    method: 'POST',
                    headers: {
                        'Accept': 'application/json',
                        'Content-Type': 'application/json',
                        'X-CSRF-TOKEN': '{{ csrf_token() }}'

                    },
                    body: JSON.stringify({
                        nonce: nonce,
                        idempotency_key: idempotency_key,
                        location_id: "MY_LOCATION_ID"
                    })
                })
                    .catch(err => {
                        alert('Network error: ' + err);
                    })
                    .then(response => {
                        if (!response.ok) {
                            return response.json().then(
                                errorInfo => Promise.reject(errorInfo));
                        }
                        return response.json();
                    })
                    .then(data => {
                        console.log(data);
                        alert('Payment complete successfully!\nCheck browser developer console for more details');
                    })
                    .catch(err => {
                        console.error(err);
                        alert('Payment failed to complete!\nCheck browser developer console for more details');
                    });

            }
        }
    });
    //TODO: paste code from step 1.1.4
    //TODO: paste code from step 1.1.5
    paymentForm.build();

    //TODO: paste code from step 2.1.2
    //Generate a random UUID as an idempotency key for the payment request
    // length of idempotency_key should be less than 45
    function uuidv4() {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
            var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }



    // onGetCardNonce is triggered when the "Pay $1.00" button is clicked
    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();
    }

</script>

On the backend, I’m using:
public function checkmeout(Request $request, $id) {
dd($request);
$requestParams = $request->getBody();

    $payment = Payment::whereId($id)->first();
    $amount_money = new Money();
    $amount_money->setAmount($payment->due_amt);
    $amount_money->setCurrency('USD');

    $body = new CreatePaymentRequest([
        $requestParams->nonce,
        $payment->id,
        $amount_money

    ]);
    $body->setOrderId($payment->orderId);
    $body->setReferenceId($payment->referenceId);
    $body->setNote($payment->patient_id);
    $body->setStatementDescriptionIdentifier('Inner Healer Chiro');

    $api_response = $this->client->getPaymentsApi()->createPayment($body);
    $body->setAutocomplete(true);

    $api_response = $this->paymentsApi->createPayment($body);

    if ($api_response->isSuccess()) {
        $result = json_decode($api_response->getBody());
    } else {
        $errors = $api_response->getErrors();
    }
    return view('square.success',['result' => $result ?: null, 'errors' => $errors ?: null]);
}

I’m getting errors returned from that method on the backend, so that’s good that I can send the nonce to the backend, but I don’t get how I can manage the flow from there. It also appears I was able to successfully charge the card. So I just don’t know how to finish things off on the back end. Can you help me see where I’m going wrong?

I’m getting a console error of “The string did not match the expected pattern”

Thanks for all the info. So just to confirm, you’re able successfully charge the nonce on the backend? The error that you provided isn’t an error from Square, though, so I’m not 100% sure what the issue is (I’m not familiar with Laravel or Blade to be honest). Do you know which line of code is causing that error to appear?

Yes I am successfully able to charge the card BUT it responds to the front end with Payment Failed. Doesn’t make sense to me. I’ll keep at it but I do not know what exactly the front end is looking for as a response from the backend in a successful transaction or failed.

Glasvezelbehang
It helps me a lot.

it helps me a lot. Thanks sharing your wonder ideas.

2 Likes

thanks for sharing this type of useful information.

1 Like