diff --git a/assets/css/frontend/cms-download.css b/assets/css/frontend/cms-download.css
new file mode 100644
index 00000000..e22ca51a
--- /dev/null
+++ b/assets/css/frontend/cms-download.css
@@ -0,0 +1,702 @@
+.aksrt-dl-modal {
+ position: fixed;
+ inset: 0;
+ z-index: 10000;
+ display: none;
+}
+
+.aksrt-dl-modal.is-open {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.aksrt-dl-modal__backdrop {
+ position: absolute;
+ inset: 0;
+ background: rgb(0 0 0 / 55%);
+}
+
+.aksrt-dl-modal__dialog {
+ position: relative;
+ z-index: 1;
+ width: 400px;
+ max-width: calc(100% - 20px);
+ margin: 0 auto;
+}
+
+.aksrt-dl-modal__panel {
+ position: relative;
+ width: 100%;
+ max-height: calc(100vh - 20px);
+ overflow: hidden auto;
+ color: var(--main-color, #111827);
+ background: var(--main-bg-color, #fff);
+ border: 0;
+ border-radius: var(--main-radius, 8px);
+ box-shadow: 0 24px 56px rgb(15 23 42 / 24%);
+}
+
+.aksrt-dl-modal:not(.is-cashier) .aksrt-dl-modal__panel {
+ padding: 26px 28px 24px;
+}
+
+.aksrt-dl-modal__close,
+.aksrt-cashier__close {
+ appearance: none;
+ position: absolute;
+ z-index: 9;
+ top: 10px;
+ right: 12px;
+ padding: 0;
+ color: var(--muted-color, #6b7280);
+ font: 400 24px/1 sans-serif;
+ background: transparent;
+ border: 0;
+ box-shadow: none;
+ cursor: pointer;
+}
+
+.aksrt-dl-modal.is-cashier > .aksrt-dl-modal__dialog > .aksrt-dl-modal__panel > .aksrt-dl-modal__close {
+ display: none;
+}
+
+.aksrt-dl-modal__title {
+ margin: 0 28px 18px 0;
+ color: var(--main-color, #111827);
+ font: 700 20px/1.3 inherit;
+}
+
+.aksrt-dl-modal__msg {
+ margin: 0 0 18px;
+ color: var(--muted-color, #4b5563);
+ font-size: 14px;
+ line-height: 1.65;
+}
+
+.aksrt-dl-modal__msg--warn {
+ padding: 12px 0;
+ color: #92400e;
+ background: transparent;
+ border: 0;
+ border-top: 1px solid #fde68a;
+ border-bottom: 1px solid #fde68a;
+}
+
+.aksrt-dl-modal__row {
+ display: grid;
+ grid-template-columns: 64px minmax(0, 1fr) 72px;
+ gap: 12px;
+ align-items: center;
+ padding: 12px 0;
+ margin: 0 0 18px;
+ border-top: 1px solid var(--main-border-color, #e5e7eb);
+ border-bottom: 1px solid var(--main-border-color, #e5e7eb);
+}
+
+.aksrt-dl-modal__label {
+ color: var(--muted-color, #6b7280);
+ font-size: 13px;
+ font-weight: 600;
+ white-space: nowrap;
+}
+
+.aksrt-dl-modal__code {
+ display: block;
+ min-width: 0;
+ overflow: hidden;
+ color: var(--main-color, #111827);
+ font: 800 18px/1.25 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
+ letter-spacing: 0.06em;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.aksrt-dl-modal__copy {
+ appearance: none !important;
+ display: inline-flex !important;
+ align-items: center !important;
+ justify-content: center !important;
+ width: 72px !important;
+ height: 32px !important;
+ padding: 0 !important;
+ overflow: visible !important;
+ color: #2563eb !important;
+ font-family: inherit !important;
+ font-size: 0 !important;
+ font-weight: 700 !important;
+ line-height: 1 !important;
+ text-indent: 0 !important;
+ white-space: nowrap !important;
+ cursor: pointer !important;
+ visibility: visible !important;
+ background: #eff6ff !important;
+ border: 1px solid #bfdbfe !important;
+ border-radius: 8px !important;
+ opacity: 1 !important;
+ -webkit-text-fill-color: #2563eb !important;
+}
+
+.aksrt-dl-modal__copy::before,
+.aksrt-dl-modal__copy::after {
+ display: none !important;
+ content: none !important;
+}
+
+.aksrt-dl-modal__copy:hover {
+ color: #1d4ed8 !important;
+ background: #dbeafe !important;
+ border-color: #93c5fd !important;
+ -webkit-text-fill-color: #1d4ed8 !important;
+}
+
+.aksrt-dl-modal__copy-text {
+ position: relative !important;
+ z-index: 2 !important;
+ display: inline-flex !important;
+ align-items: center !important;
+ justify-content: center !important;
+ color: #2563eb !important;
+ font-family: inherit !important;
+ font-size: 13px !important;
+ font-weight: 700 !important;
+ line-height: 1 !important;
+ text-indent: 0 !important;
+ white-space: nowrap !important;
+ visibility: visible !important;
+ opacity: 1 !important;
+ -webkit-text-fill-color: #2563eb !important;
+}
+
+.aksrt-dl-modal__copy:hover .aksrt-dl-modal__copy-text {
+ color: #1d4ed8 !important;
+ -webkit-text-fill-color: #1d4ed8 !important;
+}
+
+.aksrt-dl-modal__actions {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+ align-items: center;
+ padding-top: 4px;
+}
+
+.aksrt-dl-modal__download {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 40px;
+ padding: 0 18px;
+ text-decoration: none;
+ cursor: pointer;
+ border: 0;
+ border-radius: 9px;
+}
+
+.aksrt-dl-modal__balance {
+ margin-left: auto;
+ color: var(--muted-color, #6b7280);
+ font-size: 13px;
+ white-space: nowrap;
+}
+
+.aksrt-dl-modal.is-cashier .aksrt-dl-modal__body {
+ position: relative;
+ min-height: 120px;
+}
+
+.aksrt-dl-modal .modal-colorful-header,
+.aksrt-dl-modal .colorful-bg {
+ position: relative;
+ overflow: hidden;
+ color: var(--this-color);
+ background: var(--this-bg);
+ background-size: 120%;
+}
+
+.aksrt-dl-modal .modal-colorful-header {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 120px;
+ border-radius: var(--main-radius, 8px) var(--main-radius, 8px) 0 0;
+}
+
+.aksrt-dl-modal .colorful-bg > .colorful-make,
+.aksrt-dl-modal .colorful-bg > .colorful-make::before,
+.aksrt-dl-modal .colorful-bg > .colorful-make::after {
+ position: absolute;
+ color: var(--this-color);
+ background: var(--this-bg);
+ border-radius: 100%;
+ content: '';
+}
+
+.aksrt-dl-modal .colorful-bg > .colorful-make {
+ top: 60%;
+ bottom: 0;
+ width: 323px;
+ height: 290px;
+ animation: aksrt-cashier-colorful 120s linear infinite;
+}
+
+.aksrt-dl-modal .colorful-bg > .colorful-make::before {
+ top: -325px;
+ right: -558px;
+ bottom: 0;
+ width: 677px;
+ height: 300px;
+ opacity: 0.6;
+ transform: rotate(341deg);
+}
+
+.aksrt-dl-modal .colorful-bg > .colorful-make::after {
+ top: -170px;
+ bottom: 0;
+ left: -262px;
+ width: 402px;
+ height: 214px;
+ opacity: 0.4;
+ transform: rotate(326deg);
+}
+
+.aksrt-dl-modal .colorful-bg > .colorful-make + * {
+ position: relative;
+}
+
+.aksrt-dl-modal .aksrt-cashier__close {
+ color: var(--this-color, #fff);
+ font-size: 18px;
+ opacity: 0.6;
+}
+
+.aksrt-dl-modal .modal-pay-body {
+ padding: 15px;
+ margin-top: 120px;
+}
+
+.aksrt-cashier__loading {
+ min-height: 70px;
+ margin: 0;
+ line-height: 70px;
+ text-align: center;
+}
+
+.aksrt-dl-modal .text-center {
+ text-align: center;
+}
+
+.aksrt-dl-modal .em2x {
+ font-size: 2em;
+}
+
+.aksrt-dl-modal .em09 {
+ font-size: 0.9em;
+}
+
+.aksrt-dl-modal .em12 {
+ font-size: 1.2em;
+}
+
+.aksrt-dl-modal .em14 {
+ font-size: 1.4em;
+}
+
+.aksrt-dl-modal .mt6 {
+ margin-top: 6px;
+}
+
+.aksrt-dl-modal .mt10 {
+ margin-top: 10px;
+}
+
+.aksrt-dl-modal .mb6 {
+ margin-bottom: 6px;
+}
+
+.aksrt-dl-modal .mb10 {
+ margin-bottom: 10px;
+}
+
+.aksrt-dl-modal .mb20 {
+ margin-bottom: 20px;
+}
+
+.aksrt-dl-modal .mr6 {
+ margin-right: 6px;
+}
+
+.aksrt-dl-modal .ml6 {
+ margin-left: 6px;
+}
+
+.aksrt-dl-modal .ml10 {
+ margin-left: 10px;
+}
+
+.aksrt-dl-modal .px12 {
+ font-size: 12px;
+}
+
+.aksrt-dl-modal .padding-h6 {
+ padding-top: 6px;
+ padding-bottom: 6px;
+}
+
+.aksrt-dl-modal .padding-h10 {
+ padding-top: 10px;
+ padding-bottom: 10px;
+}
+
+.aksrt-dl-modal .padding-w10 {
+ padding-right: 10px;
+ padding-left: 10px;
+}
+
+.aksrt-dl-modal .line-16 {
+ line-height: 1.6;
+}
+
+.aksrt-dl-modal .flex {
+ display: flex;
+}
+
+.aksrt-dl-modal .flex.jsb {
+ justify-content: space-between;
+}
+
+.aksrt-dl-modal .flex.jc {
+ justify-content: center;
+}
+
+.aksrt-dl-modal .flex.ac {
+ align-items: center;
+}
+
+.aksrt-dl-modal .flex.ab {
+ align-items: baseline;
+}
+
+.aksrt-dl-modal .flex.hh {
+ flex-direction: column;
+}
+
+.aksrt-dl-modal .flex-auto {
+ flex: auto;
+}
+
+.aksrt-dl-modal .pointer {
+ cursor: pointer;
+}
+
+.aksrt-dl-modal .font-bold {
+ font-weight: 700;
+}
+
+.aksrt-dl-modal .muted-2-color {
+ color: var(--muted-2-color, #8b93a7);
+}
+
+.aksrt-dl-modal .muted-3-color {
+ color: var(--muted-3-color, #a9afbe);
+}
+
+.aksrt-dl-modal .c-red {
+ color: #ff5473;
+}
+
+.aksrt-dl-modal .c-green {
+ color: #20b759;
+}
+
+.aksrt-dl-modal .c-blue-2 {
+ color: #5c7cff;
+}
+
+.aksrt-dl-modal .muted-box {
+ padding: 10px;
+ background: var(--muted-border-color, rgb(132 145 165 / 10%));
+ border-radius: 4px;
+}
+
+.aksrt-dl-modal .pay-tag {
+ display: inline-block;
+ padding: 2px 7px;
+ color: #2997f7;
+ font-size: 12px;
+ line-height: 1.4;
+ background: rgb(41 151 247 / 10%);
+ border-radius: 4px;
+}
+
+.aksrt-cashier__price-row {
+ gap: 12px;
+ min-height: 27px;
+}
+
+.aksrt-cashier__regular-price {
+ display: flex;
+ gap: 10px;
+ align-items: baseline;
+}
+
+.aksrt-dl-modal .original-price {
+ position: relative;
+ display: inline-flex;
+ align-items: baseline;
+ padding-right: 2px;
+ padding-left: 1px;
+ color: var(--muted-color, #7d8496);
+ font-size: 14px;
+}
+
+.aksrt-dl-modal .original-price::before {
+ position: absolute;
+ top: 50%;
+ left: 0;
+ width: 100%;
+ height: 1px;
+ background: #ff343b;
+ transform: rotate(8deg);
+ transform-origin: center;
+ content: '';
+}
+
+.aksrt-cashier__vip-mark {
+ display: inline-block;
+ padding: 0 4px;
+ margin-left: 3px;
+ color: #866127;
+ font-size: 10px;
+ font-weight: 700;
+ line-height: 16px;
+ vertical-align: 1px;
+ background: linear-gradient(25deg, #eabe7b 10%, #f5e3c7 70%, #edc788 100%);
+ border-radius: 4px;
+}
+
+.aksrt-cashier__asset-icon {
+ width: 1.2em;
+ margin-right: 6px;
+ color: #5c7cff;
+ text-align: center;
+}
+
+.aksrt-dl-modal .payment-method-radio {
+ min-width: 0;
+ padding: 5px;
+ text-align: center;
+ border: 1px solid var(--main-border-color, rgb(132 145 165 / 20%));
+ border-radius: 4px;
+}
+
+.aksrt-dl-modal .payment-method-radio > * {
+ margin: 3px;
+}
+
+.aksrt-dl-modal .payment-method-radio img {
+ width: auto;
+ max-width: 68px;
+ height: 35px;
+ object-fit: contain;
+}
+
+.aksrt-dl-modal .payment-method-radio + .payment-method-radio {
+ margin-left: 10px;
+}
+
+.aksrt-dl-modal .hollow-radio.active {
+ color: var(--focus-color, #2997f7);
+ background: var(--focus-color-opacity1, rgb(41 151 247 / 10%));
+ border-color: var(--focus-color, #2997f7);
+}
+
+.aksrt-dl-modal .payment-method-radio:focus {
+ outline: 0;
+ box-shadow: none;
+}
+
+.aksrt-cashier__balance[hidden] {
+ display: none;
+}
+
+.aksrt-dl-modal .form-control {
+ display: block;
+ width: 100%;
+ min-height: 36px;
+ padding: 6px 92px 6px 10px;
+ color: var(--main-color, #111827);
+ font: inherit;
+ background: var(--main-bg-color, #fff);
+ border: 1px solid var(--main-border-color, #e5e7eb);
+ border-radius: 4px;
+}
+
+.aksrt-dl-modal .relative {
+ position: relative;
+}
+
+.aksrt-dl-modal .abs-right {
+ position: absolute;
+ top: 50%;
+ right: 3px;
+ transform: translateY(-50%);
+}
+
+.aksrt-dl-modal .coupon-submit {
+ padding: 0.3em 0.8em;
+}
+
+.aksrt-cashier__coupon-desc,
+.aksrt-cashier__coupon-ok {
+ margin-top: 6px;
+ font-size: 12px;
+ line-height: 1.5;
+}
+
+.aksrt-cashier__coupon-ok {
+ color: #20b759;
+}
+
+.aksrt-cashier__warn {
+ padding: 8px 10px;
+ color: #ff5473;
+ font-size: 13px;
+ line-height: 1.5;
+ text-align: center;
+ background: rgb(255 84 115 / 10%);
+ border-radius: 4px;
+}
+
+.aksrt-dl-modal .but.initiate-pay {
+ padding: 0.6em 1em;
+}
+
+.aksrt-dl-modal .modal-buts.but-average {
+ overflow: hidden;
+ border-top: 1px solid var(--main-border-color, #e5e7eb);
+ border-radius: 0 0 var(--main-radius, 8px) var(--main-radius, 8px);
+}
+
+.aksrt-dl-modal .modal-buts.but-average > .but {
+ flex: auto;
+ padding: 0.8em;
+ margin: 0 !important;
+ background: transparent;
+ border-radius: 0;
+}
+
+.aksrt-dl-modal .modal-buts.but-average > .but + .but {
+ border-left: 1px solid var(--main-border-color, #e5e7eb);
+}
+
+.aksrt-cashier__recharge {
+ display: block;
+ text-decoration: none;
+}
+
+.resource-download-box__attrs {
+ display: grid;
+ gap: 6px;
+ padding: 0;
+ margin: 10px 0 0;
+ list-style: none;
+}
+
+.resource-download-box__attrs-item {
+ display: flex;
+ justify-content: space-between;
+ gap: 12px;
+ color: #6b7280;
+ font-size: 12px;
+}
+
+.resource-download-box__attrs-name {
+ flex: 0 0 auto;
+}
+
+.resource-download-box__attrs-value {
+ color: #111827;
+ text-align: right;
+}
+
+.resource-download-box__paid-badge {
+ color: #047857 !important;
+ background: #ecfdf5 !important;
+}
+
+.resource-download-box__paid-meta {
+ display: grid;
+ gap: 4px;
+ padding: 10px 12px;
+ margin: 8px 0 12px;
+ color: #166534;
+ font-size: 12px;
+ background: #f0fdf4;
+ border: 1px solid #bbf7d0;
+ border-radius: 10px;
+}
+
+body.aksrt-dl-modal-open {
+ overflow: hidden;
+}
+
+@keyframes aksrt-cashier-colorful {
+ from {
+ transform: translate3d(0, 0, 0) rotate(0deg) scale(1);
+ }
+
+ 20% {
+ transform: translate3d(-30px, -20px, 50px) rotate(87deg) scale(0.9);
+ }
+
+ 65% {
+ transform: translate3d(-80px, -40px, 0) rotate(156deg) scale(0.6);
+ }
+
+ to {
+ transform: translate3d(0, 0, 0) rotate(360deg) scale(1);
+ }
+}
+
+@media (max-width: 480px) {
+ .aksrt-dl-modal:not(.is-cashier) .aksrt-dl-modal__panel {
+ padding: 24px 20px 22px;
+ }
+
+ .aksrt-dl-modal__row {
+ grid-template-columns: 1fr 72px;
+ gap: 8px 12px;
+ }
+
+ .aksrt-dl-modal__label {
+ grid-column: 1 / -1;
+ }
+
+ .aksrt-dl-modal__code {
+ font-size: 17px;
+ }
+
+ .aksrt-dl-modal__actions {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .aksrt-dl-modal__download {
+ width: 100%;
+ }
+
+ .aksrt-dl-modal__balance {
+ margin-left: 0;
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .aksrt-dl-modal .colorful-bg > .colorful-make {
+ animation: none;
+ }
+}
diff --git a/assets/img/pay-alipay-logo.svg b/assets/img/pay-alipay-logo.svg
new file mode 100644
index 00000000..ca409961
--- /dev/null
+++ b/assets/img/pay-alipay-logo.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/img/pay-balance-logo.svg b/assets/img/pay-balance-logo.svg
new file mode 100644
index 00000000..403f949a
--- /dev/null
+++ b/assets/img/pay-balance-logo.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/img/pay-paypal-logo.svg b/assets/img/pay-paypal-logo.svg
new file mode 100644
index 00000000..72f0fcb8
--- /dev/null
+++ b/assets/img/pay-paypal-logo.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/img/pay-stripe-logo.svg b/assets/img/pay-stripe-logo.svg
new file mode 100644
index 00000000..fe5086be
--- /dev/null
+++ b/assets/img/pay-stripe-logo.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/img/pay-wechat-logo.svg b/assets/img/pay-wechat-logo.svg
new file mode 100644
index 00000000..c9c5fdd9
--- /dev/null
+++ b/assets/img/pay-wechat-logo.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/js/cms-download.js b/assets/js/cms-download.js
index 38da0f35..b95fb8d4 100644
--- a/assets/js/cms-download.js
+++ b/assets/js/cms-download.js
@@ -15,6 +15,7 @@
if (!window.aksrtData) return;
const REST_BASE = window.aksrtData.siteUrl ? window.aksrtData.siteUrl.replace(/\/$/, '') + '/wp-json/aksrt/v1' : '/wp-json/aksrt/v1';
+ const PAY_CONTENT_PATH = '/pay/content/';
function tt(key, zh) { return zh; }
@@ -75,14 +76,16 @@
modal.setAttribute('aria-labelledby', 'aksrt-dl-modal-title');
modal.innerHTML =
'
' +
- '
' +
- '
' +
+ '
';
document.body.appendChild(modal);
modal.addEventListener('click', function (e) {
- if (e.target.matches('[data-close]')) close();
+ if (e.target.closest('[data-close]')) close();
});
modal.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && modal.classList.contains('is-open')) {
@@ -95,14 +98,16 @@
return modal;
}
- function open(html, returnFocusEl) {
+ function open(html, returnFocusEl, variant) {
// 记录调用方按钮以便关闭后回焦
lastFocusedElement = returnFocusEl || document.activeElement;
const modal = buildModal();
const body = modal.querySelector('[data-body]');
- // 给标题加 id,与 aria-labelledby 联动
- body.innerHTML = html.replace(/aksrt-dl-modal__title/, 'aksrt-dl-modal__title" id="aksrt-dl-modal-title');
+ modal.classList.toggle('is-cashier', variant === 'cashier');
+ body.innerHTML = html;
+ const title = body.querySelector('.aksrt-dl-modal__title, .aksrt-cashier__title');
+ if (title) title.id = 'aksrt-dl-modal-title';
modal.classList.add('is-open');
modal.setAttribute('aria-hidden', 'false');
document.body.classList.add('aksrt-dl-modal-open');
@@ -252,6 +257,25 @@
return card.getAttribute('data-captcha') === '1';
}
+ function cashierHeader() {
+ return '';
+ }
+
+ function openCashierContent(content, returnFocusEl) {
+ open(
+ cashierHeader() + '
' + content + '
',
+ returnFocusEl,
+ 'cashier'
+ );
+ }
+
function ensureCaptchaThen(sourceEl, callback) {
if (!isCaptchaActive(sourceEl)) { callback(); return; }
const state = window.AKSRTCaptchaState;
@@ -362,13 +386,8 @@
return headers;
}
- function payApiBase(apiKind) {
- return '/pay/content/';
- }
-
- function buildPreviewUrl(postId, priceType, couponCode, apiKind) {
- apiKind = apiKind || 'content';
- var previewUrl = REST_BASE + payApiBase(apiKind) + postId;
+ function buildPreviewUrl(postId, priceType, couponCode) {
+ var previewUrl = REST_BASE + PAY_CONTENT_PATH + postId;
var qs = [];
if (priceType) qs.push('price_type=' + encodeURIComponent(priceType));
if (couponCode) qs.push('coupon_code=' + encodeURIComponent(couponCode));
@@ -376,8 +395,8 @@
return previewUrl;
}
- function fetchPreview(postId, priceType, couponCode, apiKind) {
- return fetch(buildPreviewUrl(postId, priceType, couponCode, apiKind), {
+ function fetchPreview(postId, priceType, couponCode) {
+ return fetch(buildPreviewUrl(postId, priceType, couponCode), {
method: 'GET',
credentials: 'same-origin',
headers: restHeaders(),
@@ -386,18 +405,16 @@
});
}
- function openCashier(postId, returnFocusEl, priceType, couponCode, apiKind) {
+ function openCashier(postId, returnFocusEl, priceType, couponCode) {
if (!postId) return;
priceType = priceType || '';
couponCode = couponCode || '';
- apiKind = apiKind || 'content';
- open(
- '
' + tt('cashier_loading', '确认购买') + '
' +
- '
' + tt('loading', '加载中…') + '
',
+ openCashierContent(
+ '
' + tt('loading', '加载中…') + '
',
returnFocusEl
);
- fetchPreview(postId, priceType, couponCode, apiKind)
+ fetchPreview(postId, priceType, couponCode)
.then(function (r) {
if (!r.ok || (r.json && r.json.code)) {
const code = r.json && r.json.code ? r.json.code : '';
@@ -420,7 +437,6 @@
var prev = r.json || {};
if (priceType) prev._price_type = priceType;
if (couponCode) prev._coupon_code = couponCode;
- prev._api_kind = apiKind;
renderCashier(prev, returnFocusEl);
})
.catch(function () {
@@ -428,6 +444,35 @@
});
}
+ function formatAssetLabel(preview, value) {
+ const currency = escapeHtml(preview.currency || (preview.pay_modo === 'points' ? '积分' : '¥'));
+ const amount = escapeHtml(value == null ? '' : value);
+ return preview.pay_modo === 'points' ? amount + currency : currency + amount;
+ }
+
+ function paymentMethodLogo(methodId) {
+ const logoNames = {
+ balance: 'pay-balance-logo.svg',
+ alipay: 'pay-alipay-logo.svg',
+ epay_alipay: 'pay-alipay-logo.svg',
+ wechatpay: 'pay-wechat-logo.svg',
+ epay_wxpay: 'pay-wechat-logo.svg',
+ paypal: 'pay-paypal-logo.svg',
+ stripe: 'pay-stripe-logo.svg',
+ };
+ const logo = logoNames[methodId];
+ if (!logo) return '';
+ const themeUrl = String(window.aksrtData.themeUrl || '').replace(/\/$/, '');
+ return '
 + ')
';
+ }
+
+ function cashierPriceRow(label, value, className) {
+ return '
' +
+ '
' + label + '' +
+ '
' + value + '
' +
+ '
';
+ }
+
function renderCashier(preview, returnFocusEl) {
if (!preview) return;
if (preview.need_login && !preview.allow_guest) {
@@ -450,7 +495,6 @@
const isPoints = preview.pay_modo === 'points';
const priceType = (preview._price_type || preview.price_type || '');
- const apiKind = preview._api_kind || 'content';
let amountLabel = escapeHtml(preview.amount_label || '');
let baseLabel = escapeHtml(preview.base_label || '');
@@ -470,51 +514,78 @@
let priceRows = '';
if (!preview.pay_limit) {
- priceRows += '
' + tt('price', '价格') + '' + baseLabel + '
';
+ let regularPrice = '
' + baseLabel + '';
+ if (!isPoints && Number(preview.original) > Number(preview.base)) {
+ regularPrice = '
' + formatAssetLabel(preview, preview.original) + '' + regularPrice;
+ }
+ priceRows += cashierPriceRow(tt('price', '价格'), regularPrice, 'aksrt-cashier__regular-price');
}
- // VIP list price row when VIP reduced list amount.
if (preview.list_amount !== preview.base) {
- priceRows += '
' + tt('vip_price', '会员价') + '' + listLabel + '
';
+ priceRows += cashierPriceRow(
+ tt('vip_price', '会员价') + '
VIP' + escapeHtml(preview.vip_level || '') + '',
+ '
' + listLabel + '',
+ 'c-red'
+ );
} else if (preview.amount !== preview.base && !preview.rebate_discount && !preview.coupon_discount) {
- priceRows += '
' + tt('vip_price', '会员价') + '' + amountLabel + '
';
+ priceRows += cashierPriceRow(
+ tt('vip_price', '会员价') + '
VIP' + escapeHtml(preview.vip_level || '') + '',
+ '
' + amountLabel + '',
+ 'c-red'
+ );
}
if (preview.rebate_discount > 0) {
- priceRows += '
' + tt('rebate_discount', '推广折扣') + '' + escapeHtml(preview.rebate_discount_label || ('-' + preview.rebate_discount)) + '
';
+ priceRows += cashierPriceRow(
+ tt('rebate_discount', '推广折扣'),
+ '
' + escapeHtml(preview.rebate_discount_label || ('-' + preview.rebate_discount)) + ''
+ );
}
if (preview.coupon_discount > 0) {
- priceRows += '
' + tt('coupon_discount', '优惠码') + '' + escapeHtml(preview.coupon_discount_label || ('-' + preview.coupon_discount)) + '
';
+ priceRows += cashierPriceRow(
+ tt('coupon_discount', '优惠码'),
+ '
' + escapeHtml(preview.coupon_discount_label || ('-' + preview.coupon_discount)) + ''
+ );
+ }
+ if (preview.amount !== preview.base || preview.rebate_discount > 0 || preview.coupon_discount > 0) {
+ priceRows += cashierPriceRow(
+ tt('pay_amount', '支付金额'),
+ '
' + amountLabel + '',
+ 'font-bold c-red'
+ );
}
- priceRows += '
' + tt('pay_amount', '支付金额') + '' + amountLabel + '
';
let body = '';
- body += '
' + typeLabel + '' + title + '
';
- body += '
' + priceRows + '
';
+ body += '
' +
+ '' + typeLabel + '' + title + '' +
+ '
';
+ body += '
' + priceRows + '
';
if (isPoints) {
- body += '
' + tt('my_points', '我的积分') + '' + balance + '
';
+ body += '
' +
+ '
' +
+ '' + tt('my_points', '我的积分') +
+ '' + balance + '
';
if (!preview.balance_enough) {
- body += '
' + tt('points_short', '抱歉,您的积分不足,暂时无法支付') + '
';
- body += '
';
+ body += '
' + tt('points_short', '抱歉,您的积分不足,暂时无法支付') + '
';
+ body += '
';
- open('
' + tt('confirm_buy', '确认购买') + '
' + body, returnFocusEl);
+ openCashierContent(body, returnFocusEl);
return;
}
- body += '
';
- body += '';
- body += '
';
+ body += '
';
} else {
if (couponAllowed) {
- body += '
';
- body += '
' + tt('use_coupon', '使用优惠码') + '
';
- body += '
';
- body += '
';
- body += '
';
+ body += '
';
}
const walletBal = typeof preview.wallet_balance === 'string'
- ? preview.wallet_balance
- : (typeof preview.balance === 'string' ? preview.balance : '0.00');
- const hasBalanceMethod = methods.some(function (m) { return (m && m.id) === 'balance'; });
- if (hasBalanceMethod) {
- body += '
' + tt('my_wallet', '我的余额') + '¥' + escapeHtml(walletBal) + '
';
- if (preview.balance_enough === false) {
- body += '
' + tt('wallet_short', '余额不足,请先充值或选择其他支付方式') + '
';
- if (preview.credits_url) {
- body += '
';
- }
+ ? preview.wallet_balance
+ : (typeof preview.balance === 'string' ? preview.balance : '0.00');
+ const hasBalanceMethod = methods.some(function (m) { return (m && m.id) === 'balance'; });
+ let methodHtml = '';
+ methods.forEach(function (m, idx) {
+ const rawId = String((m && m.id) || '');
+ const id = escapeAttr(rawId);
+ const name = escapeHtml((m && m.name) || rawId);
+ methodHtml += '
' +
+ paymentMethodLogo(rawId) + '
' + name + '
';
+ });
+ if (methods.length > 1) {
+ body += '
' + tt('pay_method', '请选择支付方式') + '
' +
+ '
' + methodHtml + '
';
+ }
+ if (methods.length) {
+ body += '
';
+ }
+ if (hasBalanceMethod) {
+ const balanceSelected = methods[0] && methods[0].id === 'balance';
+ body += '
' +
+ '
' +
+ '' + tt('my_wallet', '我的余额') +
+ '¥' + escapeHtml(walletBal) + '
';
+ if (preview.balance_enough === false) {
+ if (preview.credits_url) {
+ body += '
' +
+ tt('wallet_short', '抱歉!余额不足,请先充值') + ' ';
+ } else {
+ body += '
' + tt('wallet_short', '抱歉!余额不足,请先充值') + '
';
}
}
- let methodHtml = '';
- methods.forEach(function (m, idx) {
- const id = escapeAttr(m.id || '');
- const name = escapeHtml(m.name || id);
- methodHtml += '
';
- });
- if (methodHtml) {
- body += '
' + tt('pay_method', '支付方式') + '
' + methodHtml + '
';
- } else {
- body += '
' + tt('no_pay_method', '暂无可用支付方式,请联系站长配置收款接口') + '
';
- }
- body += '
';
- if (methodHtml) {
- body += '';
- }
body += '
';
+ }
+ if (methods.length) {
+ body += '
';
+ } else {
+ body += '
' + tt('no_pay_method', '暂无可用支付方式,请联系站长配置收款接口') + '
';
+ }
}
- open('
' + tt('confirm_buy', '确认购买') + '
' + body, returnFocusEl);
+ openCashierContent(body, returnFocusEl);
+ const modal = document.getElementById('aksrt-dl-modal');
- const checkBtn = document.querySelector('[data-aksrt-check-coupon]');
+ const checkBtn = modal && modal.querySelector('[data-aksrt-check-coupon]');
if (checkBtn) {
checkBtn.addEventListener('click', function () {
- const input = document.querySelector('input[name="aksrt_coupon_code"]');
+ const input = modal.querySelector('input[name="aksrt_coupon_code"]');
const code = input ? String(input.value || '').trim() : '';
if (!code) {
if (input) input.focus();
@@ -568,7 +653,7 @@
checkBtn.disabled = true;
const old = checkBtn.innerHTML;
checkBtn.innerHTML = tt('checking', '检查中…');
- fetchPreview(preview.post_id, priceType, code, apiKind)
+ fetchPreview(preview.post_id, priceType, code)
.then(function (r) {
checkBtn.disabled = false;
checkBtn.innerHTML = old;
@@ -579,7 +664,6 @@
var prev = r.json || {};
if (priceType) prev._price_type = priceType;
prev._coupon_code = code;
- prev._api_kind = apiKind;
renderCashier(prev, returnFocusEl);
})
.catch(function () {
@@ -590,23 +674,47 @@
});
}
- const submitBtn = document.querySelector('[data-aksrt-pay-submit]');
+ if (modal) {
+ const methodCards = modal.querySelectorAll('[data-aksrt-payment-method]');
+ const methodInput = modal.querySelector('input[name="aksrt_pay_method"]');
+ const balanceBox = modal.querySelector('[data-aksrt-balance-box]');
+ const selectMethod = function (card) {
+ const method = card.getAttribute('data-aksrt-payment-method') || '';
+ methodCards.forEach(function (item) {
+ const active = item === card;
+ item.classList.toggle('active', active);
+ item.setAttribute('aria-checked', active ? 'true' : 'false');
+ });
+ if (methodInput) methodInput.value = method;
+ if (balanceBox) balanceBox.hidden = method !== 'balance';
+ };
+ methodCards.forEach(function (card) {
+ card.addEventListener('click', function () { selectMethod(card); });
+ card.addEventListener('keydown', function (event) {
+ if (event.key === 'Enter' || event.key === ' ') {
+ event.preventDefault();
+ selectMethod(card);
+ }
+ });
+ });
+ }
+
+ const submitBtn = modal && modal.querySelector('[data-aksrt-pay-submit]');
if (submitBtn) {
submitBtn.addEventListener('click', function () {
const mode = submitBtn.getAttribute('data-mode') || (isPoints ? 'points' : 'cash');
let paymentMethod = 'balance';
- const checked = document.querySelector('input[name="aksrt_pay_method"]:checked');
- if (checked) paymentMethod = checked.value;
- const input = document.querySelector('input[name="aksrt_coupon_code"]');
+ const selected = modal.querySelector('input[name="aksrt_pay_method"]');
+ if (selected) paymentMethod = selected.value;
+ const input = modal.querySelector('input[name="aksrt_coupon_code"]');
const code = input ? String(input.value || '').trim() : (preview.coupon_code || preview._coupon_code || '');
- submitPurchase(preview.post_id, mode, paymentMethod, submitBtn, returnFocusEl, code, apiKind);
+ submitPurchase(preview.post_id, mode, paymentMethod, submitBtn, returnFocusEl, code);
});
}
}
- function submitPurchase(postId, mode, paymentMethod, button, returnFocusEl, couponCode, apiKind) {
+ function submitPurchase(postId, mode, paymentMethod, button, returnFocusEl, couponCode) {
if (!postId) return;
- apiKind = apiKind || 'content';
const original = button.innerHTML;
button.disabled = true;
button.innerHTML = tt('processing', '处理中…');
@@ -620,7 +728,7 @@
if (!button.dataset.aksrtOperationKey) {
button.dataset.aksrtOperationKey = window.AksrtRequest.createIdempotencyKey();
}
- fetch(REST_BASE + payApiBase(apiKind) + postId, {
+ fetch(REST_BASE + PAY_CONTENT_PATH + postId, {
method: 'POST',
credentials: 'same-origin',
headers: restHeaders(button.dataset.aksrtOperationKey),
@@ -687,7 +795,7 @@
10
);
const priceType = buyBtn.getAttribute('data-price-type') || '';
- if (postId) openCashier(postId, buyBtn, priceType, '', 'content');
+ if (postId) openCashier(postId, buyBtn, priceType);
return;
}
@@ -695,7 +803,7 @@
if (cashierLink && cashierLink.hasAttribute('data-aksrt-pay-content')) {
e.preventDefault();
const postId = parseInt(cashierLink.getAttribute('data-aksrt-pay-content') || '0', 10);
- if (postId) openCashier(postId, cashierLink, '', '', 'content');
+ if (postId) openCashier(postId, cashierLink);
return;
}
@@ -707,54 +815,8 @@
});
// Public cashier bridge (content pay).
- window.aksrtOpenCashier = function (postId, returnFocusEl, priceType, couponCode, apiKind) {
- openCashier(postId, returnFocusEl || null, priceType || '', couponCode || '', apiKind || 'content');
+ window.aksrtOpenCashier = function (postId, returnFocusEl, priceType, couponCode) {
+ openCashier(postId, returnFocusEl || null, priceType || '', couponCode || '');
};
- // ===== 注入 modal 基础样式(避免必须额外加载 CSS)=====
- const style = document.createElement('style');
- style.textContent =
- '.aksrt-dl-modal{position:fixed;inset:0;z-index:10000;display:none;}' +
- '.aksrt-dl-modal.is-open{display:flex;align-items:center;justify-content:center;}' +
- '.aksrt-dl-modal__backdrop{position:absolute;inset:0;background:rgba(0,0,0,.55);}' +
- '.aksrt-dl-modal__panel{position:relative;background:#fff;border-radius:14px;padding:26px 28px 24px;width:min(420px,calc(100vw - 32px));box-shadow:0 24px 56px rgba(15,23,42,.24);color:#111827;}' +
- '.aksrt-dl-modal__close{position:absolute;top:10px;right:12px;background:transparent;border:0;font-size:24px;line-height:1;cursor:pointer;color:#6b7280;}' +
- '.aksrt-dl-modal__title{font:700 20px/1.3 inherit;margin:0 28px 18px 0;color:#111827;}' +
- '.aksrt-dl-modal__msg{margin:0 0 18px;color:#4b5563;font-size:14px;line-height:1.65;}' +
- '.aksrt-dl-modal__msg--warn{color:#92400e;background:transparent;border:0;border-top:1px solid #fde68a;border-bottom:1px solid #fde68a;padding:12px 0;}' +
- '.aksrt-dl-modal__row{display:grid;grid-template-columns:64px minmax(0,1fr) 72px;align-items:center;gap:12px;margin:0 0 18px;padding:12px 0;border-top:1px solid #e5e7eb;border-bottom:1px solid #e5e7eb;}' +
- '.aksrt-dl-modal__label{font-size:13px;font-weight:600;color:#6b7280;white-space:nowrap;}' +
- '.aksrt-dl-modal__code{display:block;min-width:0;color:#111827;font:800 18px/1.25 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;letter-spacing:.06em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}' +
- '.aksrt-dl-modal__copy{appearance:none!important;display:inline-flex!important;align-items:center!important;justify-content:center!important;width:72px!important;height:32px!important;background:#eff6ff!important;border:1px solid #bfdbfe!important;border-radius:8px!important;padding:0!important;cursor:pointer!important;color:#2563eb!important;-webkit-text-fill-color:#2563eb!important;font-size:0!important;font-weight:700!important;line-height:1!important;font-family:inherit!important;text-indent:0!important;opacity:1!important;visibility:visible!important;white-space:nowrap!important;overflow:visible!important;}' +
- '.aksrt-dl-modal__copy::before,.aksrt-dl-modal__copy::after{content:none!important;display:none!important;}' +
- '.aksrt-dl-modal__copy:hover{background:#dbeafe!important;border-color:#93c5fd!important;color:#1d4ed8!important;-webkit-text-fill-color:#1d4ed8!important;}' +
- '.aksrt-dl-modal__copy-text{display:inline-flex!important;align-items:center!important;justify-content:center!important;position:relative!important;z-index:2!important;color:#2563eb!important;-webkit-text-fill-color:#2563eb!important;font-size:13px!important;font-weight:700!important;line-height:1!important;font-family:inherit!important;text-indent:0!important;opacity:1!important;visibility:visible!important;white-space:nowrap!important;}' +
- '.aksrt-dl-modal__copy:hover .aksrt-dl-modal__copy-text{color:#1d4ed8!important;-webkit-text-fill-color:#1d4ed8!important;}' +
- '.aksrt-dl-modal__actions{display:flex;align-items:center;gap:12px;padding-top:4px;flex-wrap:wrap;}' +
- '.aksrt-dl-modal__download{min-height:40px;padding:0 18px;border-radius:9px;display:inline-flex;align-items:center;justify-content:center;text-decoration:none;border:0;cursor:pointer;}' +
- '.aksrt-dl-modal__balance{font-size:13px;color:#6b7280;margin-left:auto;white-space:nowrap;}' +
- '.aksrt-cashier__product{margin:0 0 12px;font-size:14px;line-height:1.5;}' +
- '.aksrt-cashier__tag{display:inline-block;margin-right:8px;padding:2px 8px;border-radius:6px;background:#eff6ff;color:#1d4ed8;font-size:12px;font-weight:700;}' +
- '.aksrt-cashier__box{background:#f8fafc;border:1px solid #e5e7eb;border-radius:10px;padding:10px 12px;margin:0 0 12px;}' +
- '.aksrt-cashier__row{display:flex;justify-content:space-between;align-items:center;gap:12px;font-size:13px;color:#6b7280;padding:4px 0;}' +
- '.aksrt-cashier__row b{color:#111827;font-size:15px;}' +
- '.aksrt-cashier__row b.is-sale{color:#dc2626;}' +
- '.aksrt-cashier__row b.is-ok{color:#059669;}' +
- '.aksrt-cashier__warn{background:#fef2f2;color:#b91c1c;border-radius:8px;padding:10px 12px;margin:0 0 12px;font-size:13px;}' +
- '.aksrt-cashier__methods{margin:0 0 14px;}' +
- '.aksrt-cashier__methods-title{font-size:13px;font-weight:600;color:#374151;margin:0 0 8px;}' +
- '.aksrt-cashier__method{display:flex;align-items:center;gap:8px;padding:8px 10px;border:1px solid #e5e7eb;border-radius:8px;margin:0 0 6px;cursor:pointer;font-size:13px;}' +
- '.aksrt-cashier__coupon{margin:0 0 14px;}' +
- '.aksrt-cashier__coupon-row{display:flex;gap:8px;align-items:center;}' +
- '.aksrt-cashier__coupon-input{flex:1;min-width:0;padding:8px 10px;border:1px solid #e5e7eb;border-radius:8px;font-size:13px;}' +
- '.aksrt-cashier__coupon-desc{margin-top:6px;font-size:12px;color:#9ca3af;line-height:1.5;}' +
- '.aksrt-cashier__coupon-ok{margin-top:6px;font-size:12px;color:#059669;}' +
- '.resource-download-box__attrs{list-style:none;margin:10px 0 0;padding:0;display:grid;gap:6px;}' +
- '.resource-download-box__attrs-item{display:flex;justify-content:space-between;gap:12px;font-size:12px;color:#6b7280;}' +
- '.resource-download-box__attrs-name{flex:0 0 auto;}' +
- '.resource-download-box__attrs-value{color:#111827;text-align:right;}' +
- '.resource-download-box__paid-badge{background:#ecfdf5!important;color:#047857!important;}.resource-download-box__paid-meta{display:grid;gap:4px;margin:8px 0 12px;padding:10px 12px;background:#f0fdf4;border:1px solid #bbf7d0;border-radius:10px;font-size:12px;color:#166534;}' +
- '@media(max-width:480px){.aksrt-dl-modal__panel{padding:24px 20px 22px;}.aksrt-dl-modal__row{grid-template-columns:1fr 72px;gap:8px 12px;}.aksrt-dl-modal__label{grid-column:1 / -1;}.aksrt-dl-modal__code{font-size:17px;}.aksrt-dl-modal__actions{align-items:flex-start;flex-direction:column;}.aksrt-dl-modal__download{width:100%;}.aksrt-dl-modal__balance{margin-left:0;}}' +
- 'body.aksrt-dl-modal-open{overflow:hidden;}';
- document.head.appendChild(style);
})();
diff --git a/src/Core/ScriptsPart1b.php b/src/Core/ScriptsPart1b.php
index 4883c2b7..d5d7934a 100644
--- a/src/Core/ScriptsPart1b.php
+++ b/src/Core/ScriptsPart1b.php
@@ -611,6 +611,14 @@ trait ScriptsPart1b {
// 单篇资源 / 付费内容 / 独立下载页:下载 + 收银台脚本
if ($is_single_resource || $is_paid_content_single || $is_download_file_page) {
+ $cms_download_css_path = get_template_directory() . '/assets/css/frontend/cms-download.css';
+ wp_enqueue_style(
+ 'aksrt-cms-download',
+ $base_uri . '/assets/css/frontend/cms-download.css',
+ [$core_dep],
+ file_exists($cms_download_css_path) ? (string) filemtime($cms_download_css_path) : $version
+ );
+
$cms_download_js_path = get_template_directory() . '/assets/js/cms-download.js';
wp_enqueue_script(
'aksrt-cms-download',
diff --git a/tests/widget-system/pay-card-contract.test.cjs b/tests/widget-system/pay-card-contract.test.cjs
index 7767da5d..c26271ec 100644
--- a/tests/widget-system/pay-card-contract.test.cjs
+++ b/tests/widget-system/pay-card-contract.test.cjs
@@ -79,3 +79,31 @@ test('the purchase card owns one direct stylesheet and no retired paywall path',
assert.doesNotMatch(pay + download, /安全检测|支持 VIP 权益|支持积分结算/);
assert.doesNotMatch(download, /function_exists\('aksrt_get_pay_service_html'\)/);
});
+
+test('the article cashier uses the reference modal contract and one direct asset path', () => {
+ const cashier = read('assets/js/cms-download.js');
+ const css = read('assets/css/frontend/cms-download.css');
+ const assets = read('src/Core/ScriptsPart1b.php');
+
+ assert.match(cashier, /modal-colorful-header colorful-bg jb-blue/);
+ assert.match(cashier, /modal-pay-body box-body/);
+ assert.match(cashier, /pay-tag badg badg-sm mr6/);
+ assert.match(cashier, /muted-box padding-h6 line-16/);
+ assert.match(cashier, /payment-method-radio hollow-radio flex-auto pointer/);
+ assert.match(cashier, /mt6 but jb-red initiate-pay btn-block radius/);
+ assert.match(cashier, /data-aksrt-payment-method/);
+ assert.match(cashier, /pay-(?:balance|alipay|wechat|paypal|stripe)-logo\.svg/);
+ assert.doesNotMatch(cashier, /document\.createElement\(['"]style['"]\)/);
+ assert.doesNotMatch(cashier, /apiKind|_api_kind|payApiBase/);
+ assert.doesNotMatch(cashier, /\/(?:shop|forum|bbs)\//i);
+
+ assert.match(css, /\.aksrt-dl-modal \.modal-colorful-header/);
+ assert.match(css, /\.aksrt-dl-modal \.payment-method-radio/);
+ assert.match(css, /\.aksrt-dl-modal \.but\.initiate-pay/);
+ assert.match(assets, /assets\/css\/frontend\/cms-download\.css/);
+ assert.match(assets, /filemtime\(\$cms_download_css_path\)/);
+
+ for (const logo of ['balance', 'alipay', 'wechat', 'paypal', 'stripe']) {
+ assert.equal(fs.existsSync(path.join(root, `assets/img/pay-${logo}-logo.svg`)), true);
+ }
+});