I am able to get the card to charge successfully, and I confirm it in my square app but nothing shows up on the DOM. My view (using Laravel and Blade)
in the HEAD:
<!-- Initialize Web Payments SDK -->
<script type="text/javascript" src="https://web.squarecdn.com/v1/square.js"></script>
<script>
{{-- Initialize Web Payments SDK variables --}}
const appId = 'MY_APP_ID';
const locationId = 'MY_LOCATION_ID';
// Initializes the Web Payments SDK by calling the app and location id
// also initializes card payment method by calling card.attach
async function initializeCard(payments) {
const card = await payments.card();
await card.attach('#card-container');
return card;
}
// Call this function to send a payment token, buyer name, and other details
// to the project server code so that a payment can be created with
// Payments API
async function createPayment(token) {
const body = JSON.stringify({
locationId,
sourceId: token,
idempotency_key: {{ $payment->id }},
patient_id: {{ $payment->patient_id }},
amount: @isset($payment->due_amt){{ str_replace('.', '',$payment->due_amt) ?? 0.00 }}@endisset
});
const paymentResponse = await fetch('{{$payment->id}}', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body,
});
if (paymentResponse.ok) {
console.log(paymentResponse.then(result => result.data));
return paymentResponse.json();
}
const errorBody = await paymentResponse.text();
// console.log(errorBody);
throw new Error(errorBody);
}
function buildPaymentRequest(payments) {
const req = payments.paymentRequest({
countryCode: 'US',
currencyCode: 'USD',
total: {
amount: '@isset($payment->due_amt){{ str_replace('.', '',$payment->due_amt) ?? 0.00 }}@endisset',
label: 'Total',
},
requestShippingContact: false,
});
req.addEventListener('afterpay_shippingaddresschanged', function () {
return {
shippingOptions: [
{
amount: '0.00',
id: 'store_pickup',
label: 'Free',
taxLineItems: [
{
amount: '@isset($total){{ round(($total * .05),2) ?? 0.00 }}@endisset',
label: 'Tax',
},
],
total: {
amount: '@isset($total){{ $total ?? 0.00 }}@endisset',
label: 'total',
},
},
],
};
});
req.addEventListener(
'afterpay_shippingoptionchanged',
function (_option) {
// This event listener is for information purposes only.
// Changes here (or values returned) will not affect the Afterpay/Clearpay PaymentRequest.
}
);
return req;
}
// async function initializeAfterpay(payments) {
// const paymentRequest = buildPaymentRequest(payments);
// const afterpay = await payments.afterpayClearpay(paymentRequest);
// await afterpay.attach('#afterpay-button');
//
// return afterpay;
// }
// async function initializeApplePay(payments) {
// const paymentRequest = buildPaymentRequest(payments);
// const applePay = await payments.applePay(paymentRequest);
// // Note: You do not need to `attach` applePay.
// return applePay;
// }
// async function initializeGooglePay(payments) {
// const paymentRequest = buildPaymentRequest(payments);
// const googlePay = await payments.googlePay(paymentRequest);
// await googlePay.attach('#google-pay-button');
//
// return googlePay;
// }
// async function initializeGiftCard(payments) {
// const giftCard = await payments.giftCard();
// await giftCard.attach('#gift-card-container');
//
// return giftCard;
// }
// This function tokenizes a payment method.
// The ‘error’ thrown from this async function denotes a failed tokenization,
// which is due to buyer error (such as an expired card). It is up to the
// developer to handle the error and provide the buyer the chance to fix
// their mistakes.
async function tokenize(paymentMethod) {
// console.log('tokenize', paymentMethod);
const tokenResult = await paymentMethod.tokenize();
// console.log('tokenResult', tokenResult);
if (tokenResult.status === 'OK') {
// console.log('tokenResult.token', tokenResult.token);
return tokenResult.token;
} else {
let errorMessage = `Tokenization failed with status: ${tokenResult.status}`;
if (tokenResult.errors) {
errorMessage += ` and errors: ${JSON.stringify(
tokenResult.errors
)}`;
}
// console.log('errorMessage', errorMessage);
throw new Error(errorMessage);
}
}
// Helper method for displaying the Payment Status on the screen.
// status is either SUCCESS or FAILURE;
function displayPaymentResults(status) {
// console.log('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';
}
// DOMContentLoaded Event Listener OK
document.addEventListener('DOMContentLoaded', async function () {
if (!window.Square) {
throw new Error('Square.js failed to load properly');
}
const payments = window.Square.payments(appId, locationId);
let card;
try {
card = await initializeCard(payments);
// console.log('card', card);
} catch (e) {
console.error('Initializing Card failed', e);
return;
}
// Checkpoint 2
async function handlePaymentMethodSubmission(event, paymentMethod) {
// console.log('handlePaymentMethodSubmission', event, paymentMethod);
event.preventDefault();
try {
// disable the submit button as we await tokenization and make a
// payment request.
cardButton.disabled = true;
const token = await tokenize(paymentMethod);
// console.log('token', token);
const paymentResults = await createPayment(token);
// console.log('paymentResults', paymentResults);
displayPaymentResults('SUCCESS');
console.debug('Payment Success', paymentResults);
} catch (e) {
cardButton.disabled = false;
displayPaymentResults('FAILURE');
// console.log(e);
console.error(e.message);
}
}
const cardButton = document.getElementById(
'card-button'
);
cardButton.addEventListener('click', async function (event) {
await handlePaymentMethodSubmission(event, card);
});
});
</script>
In the BODY:
<!-- Begin Payment Form -->
<form id="payment-form">
<div id="card-container"></div>
<button id="card-button" type="button" class="btn bg-blue-600 text-white">Pay ${{ number_format($payment->due_amt, 2) }}</button>
</form>
<div id="payment-status-container"></div>
<!-- End Payment Form -->
My code on the backend (I understood it that I could use the same Payments API create payment method I used for the old payment form but I changed the nonce to sourceId):
public function checkmeout2(Request $request, $id) {
$payment = Payment::whereId($id)->first();
$patient = Patient::whereId($payment->patient_id)->first();
$family = Family::whereId($patient->family_id)->first();
$plan = Plan::whereId($family->plan_id)->first();
$amount_money = new Money();
$amount_money->setAmount($payment->due_amt * 100);
$amount_money->setCurrency('USD');
$body = new CreatePaymentRequest(
$request->sourceId,
$payment->id,
$amount_money
);
$body->setOrderId($plan->square_catalog_id);
$body->setReferenceId($payment->id);
$body->setNote($plan->plan . " ". $patient->nickname);
$body->setStatementDescriptionIdentifier('Inner Healer Chiro');
try {
$result = $this->client->getPaymentsApi()->createPayment($body);
$res = json_decode($result->getBody());
$body->setAutocomplete(true);
$cardDetails = $res->payment->source_type == 'CARD' ? $res->payment->card_details->card->card_brand . ' '. $res->payment->card_details->card->last_4 . ' Auth Code: '. $res->payment->card_details->auth_result_code : null;
$bankInfo = $res->payment->source_type == 'BANK_ACCOUNT' ? $res->payment->bank_account_details->bank_name : null;
$achDetails = $res->payment->source_type == 'BANK_ACCOUNT' && $res->payment->bank_account_details->transfer_type == 'ACH' ? $res->payment->bank_account_details->ach_details->account_type . ' ' . $res->payment->bank_account_details->ach_details->account_number_suffix : null;
$sourceDetails = $res->payment->source_type == 'CARD' ? $cardDetails : ($res->payment->source_type == 'BANK_ACCOUNT' ? $bankInfo . ' '. $achDetails : null);
$pmt = Payment::whereId($payment->id)->first();
$pmt->transactionId = $res->payment->id;
$pmt->receiptUrl = $res->payment->receipt_url;
$pmt->referenceId = $payment->id;
$pmt->orderId = $plan->square_catalog_id;
$pmt->pmt_note = $sourceDetails;
$pmt->pmt_amt = $res->payment->amount_money->amount / 100;
$pmt->pmt_paid = true;
$pmt->pmt_type = $res->payment->source_type;
$pmt->save();
// return receipt -> square receipt url
// return auth code -> display in SUCCESS alert
// return url -> redirect to url for IHC receipt pdf
return response()->json(['receipt'=> $res->payment->receipt_url, 'auth_code'=>$res->payment->card_details->status . ': '. $res->payment->card_details->auth_result_code, 'url'=> route('payment.show', ['id' => $pmt->id ])]);
} catch (ApiException $e) {
echo "Exception when calling PaymentsApi->createPayment:";
var_dump($e->getResponseBody());
}
}
Can someone please help with this part? I have 1 other issue migrating over from SqPaymentForm (my old one redirected to the url I was sending back in the response - which was a custom receipt on my letterhead that also emailed them a PDF copy of it), but this issue would be more important.