fix(pay): 待支付浮窗无刷新同步
- modal-cache 失效时广播 aksrt:cache:invalidated - 新增 GET /orders/wait-pay-float 返回浮窗片段 - wait-pay-float.js 监听 orders 标签失效并替换/移除浮窗、重启倒计时 - VIP/充值/下载待支付分支触发 orders 失效 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d9d1122e9c
commit
c3328e7279
8 files changed
+83
-1
No files matched your search
@@ -678,6 +678,7 @@
|
||||
window.open(data.payment_url, '_blank');
|
||||
}
|
||||
renderScanPay(data, returnFocusEl);
|
||||
if (window.AksrtModalRuntime) window.AksrtModalRuntime.invalidate(['orders']);
|
||||
if (data.order_number) pollOrderPaid(data.order_number, returnFocusEl);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -73,6 +73,8 @@
|
||||
if (typeof flight.cancel === "function") flight.cancel();
|
||||
}
|
||||
});
|
||||
// 广播失效标签,供页面常驻视图(如待支付浮窗)按标签自行刷新。
|
||||
document.dispatchEvent(new CustomEvent("aksrt:cache:invalidated", { detail: { tags: wanted.slice() } }));
|
||||
return removed;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 待支付订单浮窗无刷新同步(对齐参考主题 pay.js 下单后替换 .float-right-wait-pay)。
|
||||
*
|
||||
* 订单相关缓存标签失效(下单待支付 / 支付 / 关闭)时,向服务端拉取最新浮窗片段
|
||||
* 并替换现有节点;无待支付订单时移除浮窗。
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
(function (window, document) {
|
||||
"use strict";
|
||||
|
||||
var SELECTOR = ".float-right-wait-pay";
|
||||
var timer = null;
|
||||
var seq = 0;
|
||||
|
||||
function loggedIn() {
|
||||
var config = window.aksrtData || window.aksrt || {};
|
||||
return !!config.isLoggedIn;
|
||||
}
|
||||
|
||||
function touchesOrders(tags) {
|
||||
if (!Array.isArray(tags) || !tags.length) return true;
|
||||
return tags.some(function (tag) {
|
||||
tag = String(tag || "");
|
||||
return tag === "orders" || tag.indexOf("order:") === 0;
|
||||
});
|
||||
}
|
||||
|
||||
function render(html) {
|
||||
var current = document.querySelector(SELECTOR);
|
||||
if (!html) {
|
||||
if (current) current.remove();
|
||||
return;
|
||||
}
|
||||
var holder = document.createElement("div");
|
||||
holder.innerHTML = html;
|
||||
var next = holder.firstElementChild;
|
||||
if (!next) return;
|
||||
if (current) current.replaceWith(next);
|
||||
else document.body.appendChild(next);
|
||||
if (window.jQuery && window.jQuery.fn.countdown) {
|
||||
window.jQuery(next).find("[data-countdown]").countdown();
|
||||
}
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
if (!loggedIn() || !window.AksrtRequest) return;
|
||||
var id = ++seq;
|
||||
window.AksrtRequest.get("/orders/wait-pay-float").then(function (result) {
|
||||
if (id !== seq) return;
|
||||
var data = result.data || {};
|
||||
render(String(data.html || ""));
|
||||
}).catch(function () { /* 保留现有浮窗 */ });
|
||||
}
|
||||
|
||||
document.addEventListener("aksrt:cache:invalidated", function (event) {
|
||||
if (!touchesOrders(event.detail && event.detail.tags)) return;
|
||||
window.clearTimeout(timer);
|
||||
timer = window.setTimeout(refresh, 300);
|
||||
});
|
||||
})(window, document);
|
||||
@@ -769,6 +769,7 @@
|
||||
resetOperationKey(form);
|
||||
if (data && data.redirect && (data.payment_url || data.qr_content)) {
|
||||
if (data.payment_url) window.open(data.payment_url, '_blank');
|
||||
if (window.AksrtModalRuntime) window.AksrtModalRuntime.invalidate(['orders']);
|
||||
notyf(data.message || '请完成支付,支付成功后将自动刷新。', 'info', 5, 'aksrt-balance-charge-toast');
|
||||
if (data.order_number) activePoll = pollPaidOrder(data.order_number, ['credits', 'orders']);
|
||||
return;
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
(function($,window2,document2){"use strict";function closeNotyf($element){$element.addClass("notyn-out"),window2.setTimeout(function(){$element.remove()},1e3)}function notyf2(message,type,time,id){var $body=jQuery(document2.body);jQuery(".notyn").length||$body.append('<div class="notyn"></div>'),type=type||"success",type==="error"&&(type="danger"),time=time||5e3,time=time<100?time*1e3:time;var idAttribute=id?' id="'+id+'"':"",$notification=jQuery('<div class="noty1"'+idAttribute+'><div class="notyf '+type+'">'+message+"</div></div>"),shouldClose=!id;id&&jQuery("#"+id).length?(jQuery("#"+id).find(".notyf").removeClass().addClass("notyf "+type).html(message),$notification=jQuery("#"+id),shouldClose=!0):jQuery(".notyn").append($notification),shouldClose&&window2.setTimeout(function(){closeNotyf($notification)},time)}jQuery(document2.body).on("click",".noty1",function(){closeNotyf(jQuery(this))}),window2.notyf=notyf2})(jQuery,window,document),(function(window2){"use strict";var allowedKinds={identity:!0,badges:!0,orders:!0,vip:!0},allowedEvents={click:!0,shell:!0,"first-content":!0,ready:!0,"action-start":!0,"action-end":!0,unauthorized:!0,error:!0},sessions=Object.create(null),sequence=0,maxSessions=100;function normalize(value,fallback){var normalized=String(value||fallback||"").toLowerCase().replace(/[^a-z0-9-]/g,"");return normalized||fallback}function now(){return window2.performance&&typeof window2.performance.now=="function"?window2.performance.now():0}function markName(session,event){return"aksrt-modal-"+session.kind+"-"+session.id+"-"+event}function emit(session,event){if(!(!window2.performance||typeof window2.performance.mark!="function")){var name=markName(session,event);if(window2.performance.mark(name),session.marks[event]=name,typeof window2.performance.measure=="function"&&session.marks.click&&event!=="click")try{window2.performance.measure("aksrt-modal-"+session.kind+"-"+session.id+"-click-to-"+event,session.marks.click,name)}catch{}}}function start(kind){var safeKind=normalize(kind,"unknown");allowedKinds[safeKind]||(safeKind="unknown"),sequence+=1;var id=String(sequence),session={id,kind:safeKind,startedAt:now(),marks:Object.create(null)};sessions[id]=session,emit(session,"click");for(var ids=Object.keys(sessions);ids.length>maxSessions;)delete sessions[ids.shift()];return id}function mark(id,event){var session=sessions[String(id||"")],safeEvent=normalize(event,"error");!session||!allowedEvents[safeEvent]||emit(session,safeEvent)}function finish(id){mark(id,"ready")}function fail(id){mark(id,"error")}window2.AksrtModalTelemetry={start,mark,finish,error:fail}})(window),(function(window2){"use strict";function data(){return window2.aksrtData||window2.aksrt||{}}function createIdempotencyKey(){if(window2.crypto&&typeof window2.crypto.randomUUID=="function")return window2.crypto.randomUUID();if(!window2.crypto||typeof window2.crypto.getRandomValues!="function")throw new Error("当前浏览器无法生成安全的操作标识");var bytes=new Uint8Array(16);window2.crypto.getRandomValues(bytes),bytes[6]=bytes[6]&15|64,bytes[8]=bytes[8]&63|128;var hex=Array.prototype.map.call(bytes,function(value){return value.toString(16).padStart(2,"0")}).join("");return hex.slice(0,8)+"-"+hex.slice(8,12)+"-"+hex.slice(12,16)+"-"+hex.slice(16,20)+"-"+hex.slice(20)}function restBase(){var config=data(),raw=config.restUrl||config.apiUrl||config.rest_url||"",siteUrl=config.siteUrl||config.homeUrl||window2.location.origin+"/";return window2.location.protocol==="https:"&&siteUrl.substring(0,5)==="http:"&&(siteUrl="https:"+siteUrl.substring(5)),raw?String(raw).replace(/\/$/,""):String(siteUrl).replace(/\/$/,"")+"/index.php?rest_route=/aksrt/v1"}function appendQuery(url2,query){if(!query)return url2;var parts=[];return typeof query=="string"?parts=query.replace(/^\?/,"").split("&").filter(Boolean):typeof query=="object"&&Object.keys(query).forEach(function(key){query[key]===void 0||query[key]===null||parts.push(encodeURIComponent(key)+"="+encodeURIComponent(String(query[key])))}),parts.length?url2+(url2.indexOf("?")>=0?"&":"?")+parts.join("&"):url2}function url(path,query){path=String(path||"");var inlineQuery="",queryIndex=path.indexOf("?");if(queryIndex>=0&&(inlineQuery=path.slice(queryIndex+1),path=path.slice(0,queryIndex)),/^https?:\/\//i.test(path))return appendQuery(path,query||inlineQuery);path.charAt(0)!=="/"&&(path="/"+path);var base=restBase(),result=(base.indexOf("rest_route=")>=0,base+path);return appendQuery(result,query||inlineQuery)}function headers(options){var config=data(),source=options&&options.headers||{},result={},nonce=config.restNonce||config.nonce||"";return Object.keys(source).forEach(function(key){result[key]=source[key]}),result.Accept||(result.Accept="application/json, text/html;q=0.9, */*;q=0.8"),nonce&&!result["X-WP-Nonce"]&&(result["X-WP-Nonce"]=nonce),options&&options.body!==void 0&&options.body!==null&&!(window2.FormData&&options.body instanceof window2.FormData)&&!result["Content-Type"]&Line truncated
|
||||
(function($,window2,document2){"use strict";function closeNotyf($element){$element.addClass("notyn-out"),window2.setTimeout(function(){$element.remove()},1e3)}function notyf2(message,type,time,id){var $body=jQuery(document2.body);jQuery(".notyn").length||$body.append('<div class="notyn"></div>'),type=type||"success",type==="error"&&(type="danger"),time=time||5e3,time=time<100?time*1e3:time;var idAttribute=id?' id="'+id+'"':"",$notification=jQuery('<div class="noty1"'+idAttribute+'><div class="notyf '+type+'">'+message+"</div></div>"),shouldClose=!id;id&&jQuery("#"+id).length?(jQuery("#"+id).find(".notyf").removeClass().addClass("notyf "+type).html(message),$notification=jQuery("#"+id),shouldClose=!0):jQuery(".notyn").append($notification),shouldClose&&window2.setTimeout(function(){closeNotyf($notification)},time)}jQuery(document2.body).on("click",".noty1",function(){closeNotyf(jQuery(this))}),window2.notyf=notyf2})(jQuery,window,document),(function(window2){"use strict";var allowedKinds={identity:!0,badges:!0,orders:!0,vip:!0},allowedEvents={click:!0,shell:!0,"first-content":!0,ready:!0,"action-start":!0,"action-end":!0,unauthorized:!0,error:!0},sessions=Object.create(null),sequence=0,maxSessions=100;function normalize(value,fallback){var normalized=String(value||fallback||"").toLowerCase().replace(/[^a-z0-9-]/g,"");return normalized||fallback}function now(){return window2.performance&&typeof window2.performance.now=="function"?window2.performance.now():0}function markName(session,event){return"aksrt-modal-"+session.kind+"-"+session.id+"-"+event}function emit(session,event){if(!(!window2.performance||typeof window2.performance.mark!="function")){var name=markName(session,event);if(window2.performance.mark(name),session.marks[event]=name,typeof window2.performance.measure=="function"&&session.marks.click&&event!=="click")try{window2.performance.measure("aksrt-modal-"+session.kind+"-"+session.id+"-click-to-"+event,session.marks.click,name)}catch{}}}function start(kind){var safeKind=normalize(kind,"unknown");allowedKinds[safeKind]||(safeKind="unknown"),sequence+=1;var id=String(sequence),session={id,kind:safeKind,startedAt:now(),marks:Object.create(null)};sessions[id]=session,emit(session,"click");for(var ids=Object.keys(sessions);ids.length>maxSessions;)delete sessions[ids.shift()];return id}function mark(id,event){var session=sessions[String(id||"")],safeEvent=normalize(event,"error");!session||!allowedEvents[safeEvent]||emit(session,safeEvent)}function finish(id){mark(id,"ready")}function fail(id){mark(id,"error")}window2.AksrtModalTelemetry={start,mark,finish,error:fail}})(window),(function(window2){"use strict";function data(){return window2.aksrtData||window2.aksrt||{}}function createIdempotencyKey(){if(window2.crypto&&typeof window2.crypto.randomUUID=="function")return window2.crypto.randomUUID();if(!window2.crypto||typeof window2.crypto.getRandomValues!="function")throw new Error("当前浏览器无法生成安全的操作标识");var bytes=new Uint8Array(16);window2.crypto.getRandomValues(bytes),bytes[6]=bytes[6]&15|64,bytes[8]=bytes[8]&63|128;var hex=Array.prototype.map.call(bytes,function(value){return value.toString(16).padStart(2,"0")}).join("");return hex.slice(0,8)+"-"+hex.slice(8,12)+"-"+hex.slice(12,16)+"-"+hex.slice(16,20)+"-"+hex.slice(20)}function restBase(){var config=data(),raw=config.restUrl||config.apiUrl||config.rest_url||"",siteUrl=config.siteUrl||config.homeUrl||window2.location.origin+"/";return window2.location.protocol==="https:"&&siteUrl.substring(0,5)==="http:"&&(siteUrl="https:"+siteUrl.substring(5)),raw?String(raw).replace(/\/$/,""):String(siteUrl).replace(/\/$/,"")+"/index.php?rest_route=/aksrt/v1"}function appendQuery(url2,query){if(!query)return url2;var parts=[];return typeof query=="string"?parts=query.replace(/^\?/,"").split("&").filter(Boolean):typeof query=="object"&&Object.keys(query).forEach(function(key){query[key]===void 0||query[key]===null||parts.push(encodeURIComponent(key)+"="+encodeURIComponent(String(query[key])))}),parts.length?url2+(url2.indexOf("?")>=0?"&":"?")+parts.join("&"):url2}function url(path,query){path=String(path||"");var inlineQuery="",queryIndex=path.indexOf("?");if(queryIndex>=0&&(inlineQuery=path.slice(queryIndex+1),path=path.slice(0,queryIndex)),/^https?:\/\//i.test(path))return appendQuery(path,query||inlineQuery);path.charAt(0)!=="/"&&(path="/"+path);var base=restBase(),result=(base.indexOf("rest_route=")>=0,base+path);return appendQuery(result,query||inlineQuery)}function headers(options){var config=data(),source=options&&options.headers||{},result={},nonce=config.restNonce||config.nonce||"";return Object.keys(source).forEach(function(key){result[key]=source[key]}),result.Accept||(result.Accept="application/json, text/html;q=0.9, */*;q=0.8"),nonce&&!result["X-WP-Nonce"]&&(result["X-WP-Nonce"]=nonce),options&&options.body!==void 0&&options.body!==null&&!(window2.FormData&&options.body instanceof window2.FormData)&&!result["Content-Type"]&Line truncated
|
||||
@@ -375,6 +375,7 @@
|
||||
return response;
|
||||
}
|
||||
if (data.redirect && (data.payment_url || data.qr_content)) {
|
||||
invalidate(["orders"]);
|
||||
callback(options, "onPending", data);
|
||||
if (!data.order_number) {
|
||||
var missing = { ok: false, status: 500, json: { message: "订单号缺失" } };
|
||||
|
||||
@@ -40,6 +40,12 @@ class OrderCenterRest {
|
||||
'permission_callback' => [$this, 'require_logged_in'],
|
||||
'args' => $query_args,
|
||||
]);
|
||||
// 待支付浮窗片段:须在 /orders/{order_number} 之前注册,避免被单号路由吞掉。
|
||||
register_rest_route($namespace, '/orders/wait-pay-float', [
|
||||
'methods' => \WP_REST_Server::READABLE,
|
||||
'callback' => [$this, 'get_wait_pay_float'],
|
||||
'permission_callback' => [$this, 'require_logged_in'],
|
||||
]);
|
||||
$route_args = [
|
||||
'order_number' => [
|
||||
'required' => true,
|
||||
@@ -97,6 +103,15 @@ class OrderCenterRest {
|
||||
return $this->success(['data' => $view]);
|
||||
}
|
||||
|
||||
public function get_wait_pay_float(): \WP_REST_Response {
|
||||
$enabled = class_exists('\AKSRT\Settings\Settings')
|
||||
&& !empty(\AKSRT\Settings\Settings::get_order_settings()['wait_float']);
|
||||
$html = $enabled && function_exists('aksrt_get_wait_pay_float_html') ? aksrt_get_wait_pay_float_html() : '';
|
||||
$response = $this->success(['data' => ['html' => $html]]);
|
||||
$response->header('Cache-Control', 'no-store, private');
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function pay(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$operation_key = \AKSRT\Ledger\OperationKey::from_request($request, 'order-pay', get_current_user_id());
|
||||
if (is_wp_error($operation_key)) {
|
||||
|
||||
@@ -61,6 +61,7 @@ const runtimePreludeScripts = [
|
||||
'assets/js/core/modal-telemetry.js',
|
||||
'assets/js/core/request.js',
|
||||
'assets/js/core/modal-cache.js',
|
||||
'assets/js/core/wait-pay-float.js',
|
||||
'assets/js/core/modal-state.js',
|
||||
'assets/js/core/modal-runtime.js',
|
||||
'assets/js/core/modal-triggers.js',
|
||||
|
||||
Reference in new issue
Block a user