Files
HotLunches/square/embed.js
T
2026-10-19 15:22:52 -07:00

89 lines
3.7 KiB
JavaScript

/*
* Square "Pay" button embed loader.
* ─────────────────────────────────
* A school drops ONE script tag onto their own website:
*
* <script src="https://app.hotlunches.net/square/embed.js"
* data-square-key="sqpk_xxxxxxxxxxxxxxxx"
* data-amount="25.00"
* data-currency="CAD"
* data-email="" (optional, prefill receipt email)
* data-success="" (optional, redirect URL after payment)
* async></script>
*
* It injects a responsive <iframe> (pay-frame.php) right where the script tag
* sits, so the Square card form renders inline on their page. The card data is
* tokenized by Square inside the iframe and never touches their site or ours.
*
* To place the button somewhere specific, give a target:
* data-target="#pay-here" → renders into that element instead of inline.
*/
(function () {
'use strict';
// The currently executing script tag (so we can read its data-* and the base
// URL it was served from). document.currentScript is reliable for sync loads;
// for async we fall back to the last script that points at embed.js.
var me = document.currentScript || (function () {
var all = document.getElementsByTagName('script');
for (var i = all.length - 1; i >= 0; i--) {
if ((all[i].src || '').indexOf('embed.js') !== -1) return all[i];
}
return null;
})();
if (!me) return;
var key = me.getAttribute('data-square-key') || '';
if (!key) {
console.error('[square-embed] missing data-square-key on the embed script tag.');
return;
}
// Derive the integration base URL (…/square/) from this script's own src so
// the school never has to configure a second URL.
var base = me.src.replace(/embed\.js(\?.*)?$/, '');
var amount = me.getAttribute('data-amount') || '';
var currency = me.getAttribute('data-currency') || '';
var email = me.getAttribute('data-email') || '';
var success = me.getAttribute('data-success') || '';
var qs = 'key=' + encodeURIComponent(key) + '&amount=' + encodeURIComponent(amount);
if (currency) qs += '&currency=' + encodeURIComponent(currency);
if (email) qs += '&email=' + encodeURIComponent(email);
if (success) qs += '&success=' + encodeURIComponent(success);
var iframe = document.createElement('iframe');
iframe.src = base + 'pay-frame.php?' + qs;
iframe.setAttribute('title', 'Pay with Square');
iframe.setAttribute('scrolling', 'no');
iframe.setAttribute('allow', 'payment');
iframe.style.cssText =
'width:100%;max-width:520px;border:0;overflow:hidden;display:block;height:280px;' +
'transition:height .15s ease;';
// A stable name lets us match height messages to THIS iframe if a page has
// several embedded buttons.
var frameName = 'sqpayframe_' + Math.random().toString(36).slice(2);
iframe.name = frameName;
// Resize the iframe to its content height as reported by pay-frame.php.
window.addEventListener('message', function (ev) {
var d = ev.data;
if (!d || d.sqPayFrame !== true || d.type !== 'height') return;
// Only react to messages from our own frame's content window.
if (ev.source !== iframe.contentWindow) return;
var h = parseInt(d.height, 10);
if (h > 0) iframe.style.height = (h + 4) + 'px';
});
// Insert: into data-target if given, else right where the script tag is.
var targetSel = me.getAttribute('data-target');
if (targetSel) {
var target = document.querySelector(targetSel);
if (target) { target.appendChild(iframe); return; }
console.warn('[square-embed] data-target "' + targetSel + '" not found; inlining instead.');
}
(me.parentNode || document.body).insertBefore(iframe, me);
})();