PaymentCenter/front/templates/payPage.html

666 lines
20 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{{define "payPage.html"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>收银台页面</title>
<style>
/* 基础样式 */
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
/* 段落样式 */
p {
color: #666666;
margin-bottom: 10px;
}
/* 无序列表样式 */
ul {
list-style: none;
padding: 0;
}
/* 列表项样式 */
li {
margin: 10px 0;
display: flex;
align-items: center;
}
/* 按钮样式 */
button {
background-color: #007bff;
color: #fff;
border: none;
padding: 10px 20px;
cursor: pointer;
transition: background-color 0.3s ease;
border-radius: 4px;
font-size: 16px;
margin-top: 20px;
}
/* 按钮悬停样式 */
button:hover {
background-color: #0056b3;
}
/* 弹窗样式 */
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background-color: white;
padding: 30px;
border-radius: 10px;
text-align: center;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
width: 90%;
max-width: 400px;
}
.modal-content h3 {
margin-top: 0;
color: #333;
}
.modal-content p {
color: #666;
margin-bottom: 30px;
}
.modal-buttons {
display: flex;
justify-content: space-around;
gap: 20px;
}
.modal-button {
padding: 12px 24px;
font-size: 16px;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
flex: 1;
max-width: 150px;
}
.paid-button {
background-color: #28a745;
color: white;
}
.paid-button:hover {
background-color: #218838;
}
.unpaid-button {
background-color: #dc3545;
color: white;
}
.unpaid-button:hover {
background-color: #c82333;
}
/* Toast提示样式 */
.toast {
position: fixed;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
padding: 12px 24px;
border-radius: 5px;
color: white;
font-size: 14px;
z-index: 1001;
background-color: rgba(0, 0, 0, 0.7);
transition: opacity 0.3s ease, transform 0.3s ease;
}
.toast.show {
display: block;
opacity: 1;
transform: translateX(-50%) translateY(0);
}
.toast.hide {
opacity: 0;
transform: translateX(-50%) translateY(20px);
}
/* loading样式 */
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
}
.loading-spinner {
border: 5px solid #f3f3f3;
border-top: 5px solid #007bff;
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
margin-bottom: 20px;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.loading-text {
color: #666;
font-size: 18px;
}
/* 支付信息卡片 */
.payment-info {
background: #f9f9f9;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.payment-info h2 {
margin-top: 0;
color: #333;
}
/* 支付方式选项 */
.payment-option {
padding: 15px;
border: 1px solid #ddd;
border-radius: 8px;
margin-bottom: 10px;
cursor: pointer;
transition: all 0.3s ease;
}
.payment-option:hover {
border-color: #007bff;
background-color: #f0f7ff;
}
.payment-option input[type="radio"] {
margin-right: 15px;
}
.payment-option.selected {
border-color: #007bff;
background-color: #f0f7ff;
}
</style>
</head>
{{if eq .code 200 }}
<body>
<!-- 页面内容 -->
<div class="payment-info" id="payment-info" style="display: none">
<h2>订单支付</h2>
<p>{{.desc}}</p>
<p>交易号:{{ .id }}</p>
<p>
交易金额:<strong style="color: #ff5500; font-size: 24px"
>{{ .amount }}</strong
>
</p>
</div>
<!-- 支付方式选择区域 -->
<div id="pay-container" style="display: none">
<div id="pay"></div>
<!-- 支付方式列表 -->
<ul id="payment-list"></ul>
</div>
<!-- Loading状态 -->
<div id="loading" class="loading-container" style="display: flex">
<div class="loading-spinner"></div>
<p class="loading-text">支付处理中,请稍等...</p>
</div>
<!-- 支付状态确认弹窗 -->
<div id="payment-status-modal" class="modal" style="display: none;">
<div class="modal-content">
<h3>支付状态确认</h3>
<p>请确认您的支付状态</p>
<div class="modal-buttons">
<button id="btn-paid" class="modal-button paid-button">已支付</button>
<button id="btn-unpaid" class="modal-button unpaid-button">未支付</button>
</div>
</div>
</div>
<!-- Toast提示 -->
<div id="toast" class="toast" style="display: none;"></div>
</body>
<script>
// 支付页面模块化实现
const API_BASE_URL = 'https://pay.cdlsxd.cn';
// ========== 配置模块 ==========
const CONFIG = {
API: {
LIST: API_BASE_URL + '/pay/front/api/v1/payPage/list',
SUBMIT: API_BASE_URL + '/pay/front/api/v1/payPage/submit',
QUERY: API_BASE_URL + '/pay/front/api/v1/payPage/query'
},
PAYMENT_STATUS: {
PROCESSING: 2,
SUCCESS: 3
},
REDIRECT_DELAY: 3000
};
// ========== 工具函数模块 ==========
const Utils = {
// 从URL中提取参数
getQueryParam(param) {
const queryString = window.location.search.substring(1);
const params = queryString.split("&");
for (let i = 0; i < params.length; i++) {
const [key, value] = params[i].split("=");
if (key === param) {
return decodeURIComponent(value);
}
}
return null;
},
// 验证订单号格式
validateOrderNumber(orderNo) {
// 订单号格式验证假设订单号由字母、数字、下划线组成长度在10-32之间
const orderNoPattern = /^[a-zA-Z0-9_]{10,32}$/;
return orderNoPattern.test(orderNo);
},
// 显示Loading效果
showLoading(text = "支付处理中,请稍等...") {
document.getElementById("payment-info").style.display = "none";
document.getElementById("pay-container").style.display = "none";
const loadingElement = document.getElementById("loading");
const loadingText = loadingElement.querySelector(".loading-text");
if (loadingText) {
loadingText.textContent = text;
}
loadingElement.style.display = "flex";
},
// 关闭Loading效果
closeLoading() {
document.getElementById("loading").style.display = "none";
document.getElementById("payment-info").style.display = "block";
document.getElementById("pay-container").style.display = "block";
},
// 显示支付状态确认弹窗
showPaymentStatusModal() {
const modal = document.getElementById('payment-status-modal');
if (modal) {
modal.style.display = 'flex';
}
},
// 隐藏支付状态确认弹窗
hidePaymentStatusModal() {
const modal = document.getElementById('payment-status-modal');
if (modal) {
modal.style.display = 'none';
}
},
// 显示Toast提示
showToast(message, duration = 3000) {
const toast = document.getElementById('toast');
if (toast) {
toast.textContent = message;
toast.className = 'toast show';
// 自动隐藏
setTimeout(() => {
Utils.hideToast();
}, duration);
}
},
// 隐藏Toast提示
hideToast() {
const toast = document.getElementById('toast');
if (toast) {
toast.className = 'toast hide';
// 动画结束后完全隐藏
setTimeout(() => {
toast.style.display = 'none';
}, 300);
}
}
};
// ========== 支付管理模块 ==========
const PaymentManager = {
// 处理支付回调
handleCallback() {
const id = Utils.getQueryParam("no");
if (!id || !Utils.validateOrderNumber(id)) {
Utils.closeLoading();
console.error("无效的订单号");
return;
}
// 显示查询支付结果的loading
Utils.showLoading("正在查询支付结果...");
// 单次查询订单状态
fetch(`${CONFIG.API.QUERY}?no=${id}`, {
method: "POST",
timeout: 10000
})
.then(async (response) => {
if (!response.ok) throw new Error(`HTTP错误: ${response.status}`);
return await response.json();
})
.then((data) => {
switch (data.status) {
case CONFIG.PAYMENT_STATUS.PROCESSING: // 处理中
Utils.closeLoading();
Utils.showToast("支付处理中,请稍后再试");
Utils.showPaymentStatusModal();
break;
case CONFIG.PAYMENT_STATUS.SUCCESS: // 支付成功
PaymentManager.handlePaymentSuccess(data);
break;
default: // 其他状态(待支付/失败/关闭)
// 显示未查到支付状态的提示
Utils.closeLoading();
Utils.showToast("未查到支付状态");
}
})
.catch((error) => {
// 网络错误或请求失败
console.error("查询订单状态失败:", error);
Utils.closeLoading();
Utils.showToast("查询失败,请稍后重试");
});
},
// 处理支付成功
handlePaymentSuccess(data) {
Utils.closeLoading();
// 显示支付成功提示
const payContainer = document.getElementById("pay");
payContainer.innerHTML = `
<div style="text-align: center; padding: 40px;">
<h3 style="color: #28a745;">支付成功!</h3>
<p style="margin: 20px 0; font-size: 16px;">正在为您跳转...<span id="countdown">${Math.ceil(CONFIG.REDIRECT_DELAY / 1000)}</span></p>
<p style="color: #666; font-size: 14px;">如果没有自动跳转,请点击下方按钮</p>
<button onclick="window.location.href='${data.return_url}'" style="background-color: #007bff; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; margin-top: 10px;">
立即跳转
</button>
</div>
`;
// 添加倒计时跳转
let countdown = Math.ceil(CONFIG.REDIRECT_DELAY / 1000);
const countdownElement = document.getElementById("countdown");
const countdownInterval = setInterval(() => {
countdown--;
if (countdownElement) {
countdownElement.textContent = countdown;
}
if (countdown <= 0) {
clearInterval(countdownInterval);
if (data.return_url) {
window.location.href = data.return_url;
}
}
}, 1000);
// 如果有返回URL设置跳转
if (data.return_url) {
// 设置自动跳转
setTimeout(() => {
window.location.href = data.return_url;
}, CONFIG.REDIRECT_DELAY);
}
},
// 获取支付方式列表
fetchPaymentMethods() {
const id = Utils.getQueryParam("no");
if (id && Utils.validateOrderNumber(id)) {
// 显示获取支付方式的loading
Utils.showLoading("正在加载支付方式...");
fetch(`${CONFIG.API.LIST}?id=${id}`, {
method: "POST"
})
.then(async (response) => {
if (response.ok) {
const data = await response.json();
console.log(data);
if (data.code !== 200) {
throw new Error("无效");
} else {
return data;
}
} else {
throw new Error("无效");
}
})
.then((data) => {
PaymentManager.processPaymentMethods(data, id);
})
.catch((error) => {
Utils.closeLoading();
console.error("获取支付方式失败:", error);
const pay = document.getElementById("pay");
pay.innerHTML = `
<h3 style="color: red;">获取支付方式失败</h3>
<p style="color: #666;">请检查网络连接或刷新页面重试</p>
<button onclick="location.reload()" style="background-color: #007bff; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer;">
刷新重试
</button>
`;
});
} else {
Utils.closeLoading();
console.error("Order no not found in URL");
}
},
// 处理支付方式数据
processPaymentMethods(data, id) {
// 处理返回的数据,例如渲染支付方式列表
if (data === null || data.data.length === 0) {
const pay = document.getElementById("pay");
pay.innerHTML = "<h3>支付环境异常,请检查</h3>";
} else if (data.data.length === 1) {
// 只有一种支付方式,自动跳转
window.location.href = `${CONFIG.API.SUBMIT}?pay_channel_id=${data.data[0].pay_channel_id}&no=${id}`;
} else {
Utils.closeLoading();
// 多种支付方式,展示支付界面
PaymentManager.renderPaymentMethods(data.data);
}
},
// 渲染支付方式列表
renderPaymentMethods(paymentMethods) {
const pay = document.getElementById("pay");
pay.innerHTML = "<h3>请选择支付方式</h3>";
const paymentList = document.getElementById("payment-list");
paymentList.innerHTML = "";
// 标记是否是第一个支付方式
let isFirstMethod = true;
paymentMethods.forEach((method) => {
const listItem = document.createElement("li");
listItem.className = "payment-option";
const radioInput = document.createElement("input");
radioInput.type = "radio";
radioInput.name = "paymentMethod";
radioInput.value = method.pay_channel_id;
radioInput.id = `method-${method.pay_channel_id}`;
// 如果是第一个支付方式,默认选中
if (isFirstMethod) {
radioInput.checked = true;
listItem.classList.add("selected");
isFirstMethod = false;
}
const label = document.createElement("label");
label.htmlFor = `method-${method.pay_channel_id}`;
label.textContent = method.pay_name;
if (method.icon_url) {
const icon = document.createElement("img");
icon.src = method.icon_url;
icon.style.height = "24px";
icon.style.marginRight = "10px";
label.prepend(icon);
}
listItem.appendChild(radioInput);
listItem.appendChild(label);
paymentList.appendChild(listItem);
// 点击整个区域也可以选择
listItem.addEventListener("click", () => {
// 移除所有选中样式
document.querySelectorAll(".payment-option").forEach((item) => {
item.classList.remove("selected");
});
// 添加当前选中样式
listItem.classList.add("selected");
radioInput.checked = true;
});
});
// 添加提交按钮
const submitButton = document.createElement("button");
submitButton.type = "button";
submitButton.textContent = "立即支付";
submitButton.onclick = function () {
PaymentManager.submitPayment();
};
const buttonContainer = document.createElement("div");
buttonContainer.style.textAlign = "center";
buttonContainer.appendChild(submitButton);
paymentList.appendChild(buttonContainer);
},
// 提交支付
submitPayment() {
const no = Utils.getQueryParam("no");
const selectedMethod = document.querySelector(
'input[name="paymentMethod"]:checked'
);
if (selectedMethod) {
// 显示加载状态
Utils.showLoading();
// 尝试导航到支付页面,如果失败则显示错误
try {
window.location.href = `${CONFIG.API.SUBMIT}?pay_channel_id=${selectedMethod.value}&no=${no}`;
// 设置超时检查,如果长时间未跳转则显示错误
setTimeout(() => {
Utils.closeLoading();
const payContainer = document.getElementById("pay");
payContainer.innerHTML = `
<h3 style="color: red;">支付提交超时</h3>
<p style="color: #666;">请检查网络连接并重试</p>
<button onclick="location.reload()" style="background-color: #007bff; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer;">
重新提交
</button>
`;
}, 5000);
} catch (error) {
Utils.closeLoading();
alert("支付提交失败,请重试");
console.error("支付提交失败:", error);
}
} else {
alert("请选择支付方式");
}
}
};
// 页面加载时执行
window.onload = function () {
// 检查是否有return参数
if (Utils.getQueryParam("return")) {
// 用户从支付页面返回,显示支付状态确认弹窗
Utils.closeLoading();
Utils.showPaymentStatusModal();
} else {
// 首次访问,获取支付方式
PaymentManager.fetchPaymentMethods();
}
// 绑定弹窗按钮事件
document.getElementById('btn-paid').addEventListener('click', function() {
// 显示加载状态
Utils.showLoading("正在查询支付结果...");
// 隐藏弹窗
Utils.hidePaymentStatusModal();
// 查询订单状态
PaymentManager.handleCallback();
});
document.getElementById('btn-unpaid').addEventListener('click', function() {
// 隐藏弹窗
Utils.hidePaymentStatusModal();
// 重新获取支付方式
PaymentManager.fetchPaymentMethods();
});
};
</script>
{{ else}}
<body>
<div class="payment-info" style="text-align: center">
<h2 style="color: #ff0000">支付异常</h2>
<p>{{.message}}</p>
</div>
</body>
{{end}}
</html>
{{end}}