143 lines
6.2 KiB
PHP
143 lines
6.2 KiB
PHP
<?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)
|
|
. '¤cy=' . $_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>
|