First Commit

This commit is contained in:
mark committed 2026-10-19 15:22:52 -07:00
1 parent 5eb9dd037d
commit 2e62c0e60b
11877 files changed
+2035312

No files matched your search

+142
View File
@@ -0,0 +1,142 @@
<?php
/**
* PayPal Pay Later Messaging — standalone promotional banner.
* Shows text like "Pay in 4 payments of $X" near prices on product / cart pages.
*
* Usage — set $ppMessage before including:
*
* $ppMessage = [
* 'school_name' => 'Springfield Elementary', // required — used to resolve merchant ID
* 'amount' => '95.00', // required — string
* 'currency' => 'USD', // optional, default USD
* 'placement' => 'product', // product | cart | payment | home
* 'page_type' => 'product-detail', // for SDK data-page-type attr
* 'style' => [ // optional — PayPal Messages style object
* 'layout' => 'text',
* 'logo' => ['type' => 'inline'], // inline | primary | alternative | none
* 'text' => ['size' => 13, 'color' => 'black'],
* ],
* ];
* require_once '/path/to/ppp/paypal-paylater-message.php';
*
* Coordinates with paypal-checkout-widget.php via $GLOBALS['_pp_sdk_loaded'] so the
* SDK is never loaded twice on the same page regardless of include order.
*/
if (!isset($ppMessage) || !is_array($ppMessage)) {
throw new RuntimeException('$ppMessage array must be set before including paypal-paylater-message.php');
}
// -- Resolve merchant and check readiness -------------------------------------
$_ppm_config = require __DIR__ . '/config.php';
// Kill-switch shared with the checkout widget: set 'paylater_messaging' => false
// in config.php to disable Pay Later promo messaging everywhere (the SDK's
// messaging component has thrown unhandled PAGE_TYPE exceptions in some envs).
// Defaults to FALSE — see paypal-checkout-widget.php. A config predating this
// key must not re-enable the SDK messaging crash.
if (!(bool)($_ppm_config['paylater_messaging'] ?? false)) return;
$_ppm_merchantId = null;
$_ppm_merchantsFile = __DIR__ . '/data/school_merchants.json';
if (file_exists($_ppm_merchantsFile)) {
$_ppm_merchants = json_decode(file_get_contents($_ppm_merchantsFile), true) ?: [];
$_ppm_record = $_ppm_merchants[$ppMessage['school_name']] ?? null;
if ($_ppm_record) {
if ($ppMessage['school_name'] === '') return;
if ($ppMessage['school_name'] !== '' && ($GLOBALS['_pp_msg_skipped'][$ppMessage['school_name']] ?? false)) return;
if ($_ppm_record['consent_revoked'] ?? false) return;
if (isset($_ppm_record['paypal_ready']) && !$_ppm_record['paypal_ready']) return;
$_ppm_merchantId = $_ppm_record['merchant_id'] ?? null;
}
}
if (!$_ppm_merchantId) return;
// -- Unique container ID (counter prevents collisions when included multiple times) --
$GLOBALS['_pp_msg_count'] = ($GLOBALS['_pp_msg_count'] ?? 0) + 1;
$_ppm_containerId = 'pp-msg-' . $GLOBALS['_pp_msg_count'];
// -- Params --------------------------------------------------------------------
$_ppm_clientId = $_ppm_config['client_id'];
$_ppm_amount = $ppMessage['amount'];
$_ppm_currency = strtoupper($ppMessage['currency'] ?? 'USD');
$_ppm_placement = $ppMessage['placement'] ?? 'product';
$_ppm_pageType = $ppMessage['page_type'] ?? 'product-details';
// PayPal's SDK rejects unknown data-page-type values ("Invalid page type"),
// which aborts the whole SDK load. Map aliases and fall back to a safe value.
$_ppmValidPageTypes = [
'home', 'category', 'product-listing', 'search-results', 'product-details',
'cart', 'mini-cart', 'checkout', 'order-review', 'order-confirmation',
'account', 'profile', 'blog', 'about', 'contact', 'landing', 'marketing',
'help', 'search', 'unknown',
];
$_ppmPageTypeAliases = ['product-detail' => 'product-details', 'product' => 'product-details'];
$_ppm_pageType = $_ppmPageTypeAliases[$_ppm_pageType] ?? $_ppm_pageType;
if (!in_array($_ppm_pageType, $_ppmValidPageTypes, true)) {
$_ppm_pageType = 'product-details';
}
$_ppm_style = $ppMessage['style'] ?? [
'layout' => 'text',
'logo' => ['type' => 'inline'],
'text' => ['size' => 13, 'color' => 'black'],
];
// -- SDK: load once per page ---------------------------------------------------
$_ppmSdkAlready = $GLOBALS['_pp_sdk_loaded'] ?? false;
if (!$_ppmSdkAlready) {
$GLOBALS['_pp_sdk_loaded'] = true;
// Include the full component set (buttons,messages + applepay,googlepay) so the
// checkout widget can share this SDK if it loads after us. The PayPal SDK can only
// be loaded once per page, so if we omit applepay/googlepay here, paypal.Applepay /
// paypal.Googlepay would be undefined in the widget and the wallet buttons vanish.
$_ppm_sdkUrl = 'https://www.paypal.com/sdk/js'
. '?client-id=' . urlencode($_ppm_clientId)
. '&merchant-id=' . urlencode($_ppm_merchantId)
. '&currency=' . $_ppm_currency
. '&components=buttons,applepay,googlepay';
}
?>
<?php /* NOTE: no data-page-type attribute — the SDK throws "Cannot set
properties of undefined (setting 'PAGE_TYPE')" asynchronously when it
is present (internal SDK bug). Optional analytics hint only. */ ?>
<?php if (!$_ppmSdkAlready): ?>
<script
src="<?= htmlspecialchars($_ppm_sdkUrl) ?>"
data-partner-attribution-id="EZSSOFTWAREINC_SP_PPCP"
></script>
<?php endif; ?>
<div id="<?= $_ppm_containerId ?>" class="pp-paylater-banner"></div>
<style>
#<?= $_ppm_containerId ?>.pp-paylater-banner { min-height: 18px; margin: 4px 0 8px; }
</style>
<script>
(function () {
var id = <?= json_encode('#' . $_ppm_containerId) ?>;
// No placement/pageType — the SDK's placement?PAGE_TYPE mapping has crashed
// with "Cannot set properties of undefined"; defaults are fine for text promos.
var config = {
amount: parseFloat(<?= json_encode($_ppm_amount) ?>),
style: <?= json_encode($_ppm_style, JSON_UNESCAPED_UNICODE) ?>,
};
function render() {
if (window.paypal && window.paypal.Messages) {
window.paypal.Messages(config).render(id);
}
}
// If SDK is already present (loaded above or by widget earlier on the page) render now.
// Otherwise DOMContentLoaded is the safe fallback — by then all blocking scripts have run.
if (window.paypal && window.paypal.Messages) {
render();
} else {
document.addEventListener('DOMContentLoaded', render);
}
}());
</script>