diff --git a/pages/recharge.vue b/pages/recharge.vue
index 175ad0a..08e8472 100644
--- a/pages/recharge.vue
+++ b/pages/recharge.vue
@@ -230,6 +230,48 @@
+
+
+
+
+ ×
+ USDT (TRC20)
+ {{ epayQr.statusText }}
+
+
+
+
+
+
+ Số tiền USDT (chính xác)
+
+ {{ epayQr.data.usdt_amount }} USDT
+
+ {{ epayQr.copied === 'amount' ? '✓' : 'Copy' }}
+
+
+
+
+
+ Địa chỉ ví (TRC20)
+
+ {{ epayQr.data.usdt_address }}
+
+ {{ epayQr.copied === 'addr' ? '✓' : 'Copy' }}
+
+
+
+
+
+ ≈ {{ epayQr.data.vnd_amount }} VND
+ ⏱ {{ epayQr.countdownText }}
+
+
+
+ ⚠ Vui lòng chuyển chính xác số tiền. Sai số sẽ không thể xác định đơn hàng!
+
+
+
@@ -254,6 +296,18 @@ export default {
coin_type: { name: "", key: "" },
// 越南地区在线支付通道:'alin'(银行卡) | 'epay'(USDT)
payChannel: "alin",
+ // 内嵌 USDT 二维码弹窗状态
+ epayQr: {
+ visible: false,
+ data: { usdt_address: "", usdt_amount: "", vnd_amount: "", expires_at: 0, order_sn: "" },
+ qrDataUrl: "",
+ countdownText: "",
+ statusText: "",
+ statusClass: "waiting",
+ copied: "",
+ },
+ _epayPollTimer: null,
+ _epayCountdownTimer: null,
};
},
computed: {
@@ -448,14 +502,12 @@ export default {
.epayDeposit(params)
.then((res) => {
const d = res.Data || {};
- if (d.status == 1 && d.url) {
- const o = navigator.userAgent;
- const isiOS = !!o.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
- if (isiOS) {
- window.location.href = d.url;
- } else {
- window.open(d.url, "_blank");
- }
+ if (d.status == 1 && d.qrcode) {
+ this.openEpayQr(d.qrcode);
+ } else if (d.status == 1 && d.url) {
+ // 后端未升级时兼容跳转
+ const isiOS = !!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
+ if (isiOS) window.location.href = d.url; else window.open(d.url, "_blank");
} else {
this.$toast({ message: d.message || 'Nạp thất bại', duration: 2000 });
}
@@ -467,6 +519,93 @@ export default {
this.$toastHide();
});
},
+ openEpayQr(qrcode) {
+ this.epayQr.data = qrcode;
+ this.epayQr.statusText = "Đang chờ thanh toán...";
+ this.epayQr.statusClass = "waiting";
+ this.epayQr.copied = "";
+ // 生成二维码图片(复用现有 qr-image 库)
+ try {
+ this.epayQr.qrDataUrl =
+ "data:image/png;base64," +
+ uni.arrayBufferToBase64(qr.imageSync(qrcode.usdt_address, { margin: 2 }));
+ } catch (e) {
+ this.epayQr.qrDataUrl = "";
+ }
+ this.epayQr.visible = true;
+ this.startEpayCountdown();
+ this.startEpayPolling();
+ },
+ startEpayCountdown() {
+ const tick = () => {
+ const left = Math.max(0, this.epayQr.data.expires_at - Math.floor(Date.now() / 1000));
+ const m = Math.floor(left / 60);
+ const s = left % 60;
+ this.epayQr.countdownText = (m < 10 ? "0" : "") + m + ":" + (s < 10 ? "0" : "") + s;
+ if (left <= 0) {
+ this.epayQr.statusText = "Đơn hàng đã hết hạn";
+ this.epayQr.statusClass = "expired";
+ this.stopEpayTimers();
+ }
+ };
+ tick();
+ this._epayCountdownTimer = setInterval(tick, 1000);
+ },
+ startEpayPolling() {
+ const poll = () => {
+ if (!this.epayQr.visible || !this.epayQr.data.order_sn) return;
+ this.$api
+ .epayOrderStatus({
+ user_id: this.userInfo.id,
+ order_sn: this.epayQr.data.order_sn,
+ })
+ .then((res) => {
+ const d = res.Data || {};
+ if (d.status == 1) {
+ this.epayQr.statusText = "Thanh toán thành công!";
+ this.epayQr.statusClass = "success";
+ this.stopEpayTimers();
+ this.$store.dispatch("app/getUserInfo");
+ setTimeout(() => this.closeEpayQr(), 2500);
+ } else if (d.status == 2) {
+ this.epayQr.statusText = "Đơn hàng đã hết hạn";
+ this.epayQr.statusClass = "expired";
+ this.stopEpayTimers();
+ }
+ })
+ .catch(() => {});
+ };
+ this._epayPollTimer = setInterval(poll, 3000);
+ setTimeout(poll, 1000);
+ },
+ copyEpayText(text, tag) {
+ uni.setClipboardData({
+ data: String(text),
+ success: () => {
+ this.epayQr.copied = tag;
+ setTimeout(() => {
+ if (this.epayQr.copied === tag) this.epayQr.copied = "";
+ }, 1500);
+ },
+ });
+ },
+ closeEpayQr() {
+ this.epayQr.visible = false;
+ this.stopEpayTimers();
+ },
+ stopEpayTimers() {
+ if (this._epayPollTimer) {
+ clearInterval(this._epayPollTimer);
+ this._epayPollTimer = null;
+ }
+ if (this._epayCountdownTimer) {
+ clearInterval(this._epayCountdownTimer);
+ this._epayCountdownTimer = null;
+ }
+ },
+ },
+ beforeDestroy() {
+ this.stopEpayTimers();
},
};
@@ -563,4 +702,111 @@ export default {
}
}
}
+
+/* ============ 内嵌 USDT 二维码弹窗 ============ */
+.epay-qr-box {
+ width: 600rpx;
+ padding: 30rpx 34rpx 24rpx;
+ background: #1a1206;
+ border: 1rpx solid #907545;
+ border-radius: 10px;
+ color: #e5b932;
+ box-sizing: border-box;
+ position: relative;
+}
+.epay-qr-close {
+ position: absolute;
+ top: 8rpx;
+ right: 18rpx;
+ color: #aaa;
+ font-size: 40rpx;
+ line-height: 1;
+ padding: 10rpx 16rpx;
+}
+.epay-qr-title {
+ text-align: center;
+ font-size: 30rpx;
+ font-weight: bold;
+ margin-bottom: 8rpx;
+}
+.epay-qr-status {
+ text-align: center;
+ font-size: 24rpx;
+ margin-bottom: 16rpx;
+ &.waiting { color: #e5b932; }
+ &.success { color: #7cd992; }
+ &.expired { color: #e74c3c; }
+}
+.epay-qr-img-wrap {
+ width: 340rpx;
+ height: 340rpx;
+ margin: 0 auto 24rpx;
+ background: #fff;
+ padding: 14rpx;
+ border-radius: 8rpx;
+ box-sizing: content-box;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+.epay-qr-field {
+ margin-bottom: 16rpx;
+}
+.epay-qr-label {
+ color: #bbb;
+ font-size: 22rpx;
+ margin-bottom: 6rpx;
+}
+.epay-qr-value {
+ display: flex;
+ align-items: center;
+ gap: 12rpx;
+ background: rgba(0, 0, 0, 0.35);
+ border: 1rpx solid #5a4323;
+ border-radius: 6rpx;
+ padding: 12rpx 14rpx;
+ font-size: 24rpx;
+ word-break: break-all;
+ &.amount {
+ color: #fff;
+ font-weight: bold;
+ font-size: 28rpx;
+ }
+ &.addr {
+ color: #e5b932;
+ font-family: 'Courier New', monospace;
+ font-size: 20rpx;
+ }
+ text {
+ flex: 1;
+ }
+}
+.epay-qr-btn {
+ flex-shrink: 0;
+ background: #c5971e;
+ color: #161107;
+ font-size: 22rpx;
+ padding: 6rpx 18rpx;
+ border-radius: 4rpx;
+ font-weight: bold;
+}
+.epay-qr-meta {
+ display: flex;
+ justify-content: space-between;
+ color: #bbb;
+ font-size: 24rpx;
+ margin: 20rpx 0 12rpx;
+}
+.epay-qr-countdown {
+ color: #e5b932;
+ font-weight: bold;
+}
+.epay-qr-tip {
+ color: #e74c3c;
+ font-size: 22rpx;
+ text-align: center;
+ line-height: 1.5;
+ margin-top: 14rpx;
+ opacity: 0.85;
+}
diff --git a/request/api.js b/request/api.js
index b9ae2d4..12757f2 100644
--- a/request/api.js
+++ b/request/api.js
@@ -61,6 +61,10 @@ let api = {
epayDeposit: async (params) => {
return await ajax.post("/epay_online_order", params);
},
+ // 易支付订单状态轮询(内嵌二维码支付用)
+ epayOrderStatus: async (params) => {
+ return await ajax.post("/epay_order_status", params);
+ },
// 越南银行卡提现
applyWithdrawBank: async (params) => {
return await ajax.post("/apply_withdraw_bank", params);