Compare commits
4
Commits
b67b11c181
...
a81b39aef0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a81b39aef0 | ||
|
|
301af6d91c | ||
|
|
99e4265e01 | ||
|
|
91fa85f4ef |
+334
-8
@@ -13,7 +13,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</u-navbar>
|
</u-navbar>
|
||||||
<view class="content">
|
<view class="content">
|
||||||
<template v-if="userInfo.pay_channel == 'BC'">
|
<template v-if="userInfo.pay_channel == 'BC' && userInfo.area_id != 3">
|
||||||
<view class="picker-view" @click="show = true">
|
<view class="picker-view" @click="show = true">
|
||||||
<u-input
|
<u-input
|
||||||
readonly
|
readonly
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="userInfo.pay_channel == 'YBF'">
|
<template v-if="userInfo.pay_channel == 'YBF' && userInfo.area_id != 3">
|
||||||
<view
|
<view
|
||||||
class="picker-view"
|
class="picker-view"
|
||||||
@click="fnPicker('currency')"
|
@click="fnPicker('currency')"
|
||||||
@@ -184,18 +184,30 @@
|
|||||||
@confirm="confirmPicker"
|
@confirm="confirmPicker"
|
||||||
></u-picker>
|
></u-picker>
|
||||||
</template>
|
</template>
|
||||||
<!-- 越南 AlinPay 在线充值 -->
|
<!-- 越南地区:银行卡 (AlinPay) / USDT (Epay) 双通道(和 PC 同步按 area_id 判断) -->
|
||||||
<template v-if="userInfo.pay_channel == 'ALIN_VN'">
|
<template v-if="userInfo.area_id == 3">
|
||||||
|
<view class="pay-tabs">
|
||||||
|
<view
|
||||||
|
class="pay-tab"
|
||||||
|
:class="{ active: payChannel === 'alin' }"
|
||||||
|
@click="payChannel = 'alin'"
|
||||||
|
>Thẻ ngân hàng</view>
|
||||||
|
<view
|
||||||
|
class="pay-tab"
|
||||||
|
:class="{ active: payChannel === 'epay' }"
|
||||||
|
@click="payChannel = 'epay'"
|
||||||
|
>USDT (TRC20)</view>
|
||||||
|
</view>
|
||||||
<view class="picker-view">
|
<view class="picker-view">
|
||||||
<u-input
|
<u-input
|
||||||
placeholder="Tối thiểu 1,000,000"
|
:placeholder="payChannel === 'epay' ? 'Nhập số tiền VND (vd: 1,000,000)' : 'Tối thiểu 1,000,000'"
|
||||||
color="#93979b"
|
color="#93979b"
|
||||||
v-model="amount"
|
v-model="amount"
|
||||||
type="number"
|
type="number"
|
||||||
border="none"
|
border="none"
|
||||||
>
|
>
|
||||||
<u--text
|
<u--text
|
||||||
:text="$lang.rechargeAmount || '充值金额'"
|
:text="payChannel === 'epay' ? ($lang.rechargeAmount || '充值金额') + ' (VND)' : ($lang.rechargeAmount || '充值金额')"
|
||||||
slot="prefix"
|
slot="prefix"
|
||||||
margin="0 3px 0 0"
|
margin="0 3px 0 0"
|
||||||
type="tips"
|
type="tips"
|
||||||
@@ -207,17 +219,59 @@
|
|||||||
class="button"
|
class="button"
|
||||||
color="#e5b932"
|
color="#e5b932"
|
||||||
:text="$lang.submit"
|
:text="$lang.submit"
|
||||||
@click="submitAlin"
|
@click="payChannel === 'epay' ? submitEpay() : submitAlin()"
|
||||||
></u-button>
|
></u-button>
|
||||||
</view>
|
</view>
|
||||||
<view class="list">
|
<view class="list">
|
||||||
<view class="tip">
|
<view class="tip">
|
||||||
{{ $lang.rechargeTip }}
|
{{ payChannel === 'epay' ? 'Nhập số tiền theo VND — hệ thống tự động quy đổi sang USDT theo tỷ giá 1 USDT ≈ 27,100 VND. Thanh toán qua mạng TRC20.' : $lang.rechargeTip }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
</view>
|
</view>
|
||||||
<view class="mb60"></view>
|
<view class="mb60"></view>
|
||||||
|
|
||||||
|
<!-- USDT 内嵌二维码弹窗 -->
|
||||||
|
<u-popup :show="epayQr.visible" mode="center" round="10" :closeOnClickOverlay="false" @close="closeEpayQr">
|
||||||
|
<view class="epay-qr-box">
|
||||||
|
<view class="epay-qr-close" @click="closeEpayQr">×</view>
|
||||||
|
<view class="epay-qr-title">USDT (TRC20)</view>
|
||||||
|
<view class="epay-qr-status" :class="epayQr.statusClass">{{ epayQr.statusText }}</view>
|
||||||
|
|
||||||
|
<view class="epay-qr-img-wrap">
|
||||||
|
<u--image v-if="epayQr.qrDataUrl" :src="epayQr.qrDataUrl" width="340rpx" height="340rpx" mode="scaleToFill"></u--image>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="epay-qr-field">
|
||||||
|
<view class="epay-qr-label">Số tiền USDT (chính xác)</view>
|
||||||
|
<view class="epay-qr-value amount">
|
||||||
|
<text>{{ epayQr.data.usdt_amount }} USDT</text>
|
||||||
|
<view class="epay-qr-btn" @click="copyEpayText(epayQr.data.usdt_amount, 'amount')">
|
||||||
|
{{ epayQr.copied === 'amount' ? '✓' : 'Copy' }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="epay-qr-field">
|
||||||
|
<view class="epay-qr-label">Địa chỉ ví (TRC20)</view>
|
||||||
|
<view class="epay-qr-value addr">
|
||||||
|
<text>{{ epayQr.data.usdt_address }}</text>
|
||||||
|
<view class="epay-qr-btn" @click="copyEpayText(epayQr.data.usdt_address, 'addr')">
|
||||||
|
{{ epayQr.copied === 'addr' ? '✓' : 'Copy' }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="epay-qr-meta">
|
||||||
|
<text>≈ {{ epayQr.data.vnd_amount }} VND</text>
|
||||||
|
<text class="epay-qr-countdown">⏱ {{ epayQr.countdownText }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="epay-qr-tip">
|
||||||
|
⚠ Vui lòng chuyển chính xác số tiền. Sai số sẽ không thể xác định đơn hàng!
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</u-popup>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -240,6 +294,20 @@ export default {
|
|||||||
amount: "",
|
amount: "",
|
||||||
currency: { name: "", key: "" },
|
currency: { name: "", key: "" },
|
||||||
coin_type: { name: "", key: "" },
|
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: {
|
computed: {
|
||||||
@@ -409,11 +477,162 @@ export default {
|
|||||||
this.$toastHide();
|
this.$toastHide();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
submitEpay() {
|
||||||
|
if (!this.amount || this.amount <= 0) {
|
||||||
|
this.$toast({ message: 'Vui lòng nhập số tiền', duration: 2000 });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.amount < 1000000) {
|
||||||
|
this.$toast({ message: 'Số tiền tối thiểu là 1,000,000', duration: 2000 });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const params = {
|
||||||
|
user_id: this.userInfo.id,
|
||||||
|
price: this.amount,
|
||||||
|
client: 3,
|
||||||
|
username: this.userInfo.username,
|
||||||
|
pay_type: 'usdt',
|
||||||
|
};
|
||||||
|
this.$toast({
|
||||||
|
type: "loading",
|
||||||
|
message: `${this.$lang.submit}...`,
|
||||||
|
duration: 200000,
|
||||||
|
});
|
||||||
|
this.$api
|
||||||
|
.epayDeposit(params)
|
||||||
|
.then((res) => {
|
||||||
|
const d = res.Data || {};
|
||||||
|
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 });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
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();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.pay-tabs {
|
||||||
|
display: flex;
|
||||||
|
padding: 20rpx 30rpx 0;
|
||||||
|
gap: 15rpx;
|
||||||
|
}
|
||||||
|
.pay-tab {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
padding: 18rpx 0;
|
||||||
|
color: #93979b;
|
||||||
|
background: rgba(255, 255, 255, 0.04);
|
||||||
|
border: 1rpx solid #2a3448;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
transition: all 0.2s;
|
||||||
|
&.active {
|
||||||
|
background: linear-gradient(to right, #e5b932, #c5971e);
|
||||||
|
color: #0b1520;
|
||||||
|
font-weight: bold;
|
||||||
|
border-color: #e5b932;
|
||||||
|
}
|
||||||
|
}
|
||||||
.recharge {
|
.recharge {
|
||||||
background: #0b1520;
|
background: #0b1520;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -483,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;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -57,6 +57,14 @@ let api = {
|
|||||||
alinDeposit: async (params) => {
|
alinDeposit: async (params) => {
|
||||||
return await ajax.post("/alin_online_order", params);
|
return await ajax.post("/alin_online_order", params);
|
||||||
},
|
},
|
||||||
|
// 彩虹易支付充值(USDT TRC20 / 支付宝 / 微信 等,默认 usdt)
|
||||||
|
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) => {
|
applyWithdrawBank: async (params) => {
|
||||||
return await ajax.post("/apply_withdraw_bank", params);
|
return await ajax.post("/apply_withdraw_bank", params);
|
||||||
|
|||||||
Reference in New Issue
Block a user