feat: 对接越南AlinPay支付(充值+提现)

- recharge.vue: 新增ALIN_VN充值模板,最低1,000,000 VND
- withdraw.vue: 新增银行卡提现模板,银行列表API加载,最低1,000,000 VND
- api.js: 新增alinDeposit/applyWithdrawBank/getBankListVn接口

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
li
2026-03-22 12:55:52 +08:00
co-authored by Claude Happy
parent be597042c8
commit 18a4d03c3a
3 changed files with 371 additions and 72 deletions
+85
View File
@@ -184,6 +184,38 @@
@confirm="confirmPicker" @confirm="confirmPicker"
></u-picker> ></u-picker>
</template> </template>
<!-- 越南 AlinPay 在线充值 -->
<template v-if="userInfo.pay_channel == 'ALIN_VN'">
<view class="picker-view">
<u-input
placeholder="Tối thiểu 1,000,000"
color="#93979b"
v-model="amount"
type="number"
border="none"
>
<u--text
:text="$lang.rechargeAmount || '充值金额'"
slot="prefix"
margin="0 3px 0 0"
type="tips"
></u--text>
</u-input>
</view>
<view class="list mb60">
<u-button
class="button"
color="#e5b932"
:text="$lang.submit"
@click="submitAlin"
></u-button>
</view>
<view class="list">
<view class="tip">
{{ $lang.rechargeTip }}
</view>
</view>
</template>
</view> </view>
<view class="mb60"></view> <view class="mb60"></view>
</view> </view>
@@ -324,6 +356,59 @@ export default {
}); });
} }
}, },
// 越南 AlinPay 充值
submitAlin() {
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,
};
this.$toast({
type: "loading",
message: `${this.$lang.submit}...`,
duration: 200000,
});
this.$api
.alinDeposit(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");
}
} else {
this.$toast({
message: d.message || '充值失败',
duration: 2000,
});
}
})
.catch((err) => {
console.log(err);
})
.finally(() => {
this.$toastHide();
});
},
}, },
}; };
</script> </script>
+274 -72
View File
@@ -18,77 +18,161 @@
>{{ $lang.balance }}{{ userInfo.money }}</view >{{ $lang.balance }}{{ userInfo.money }}</view
> >
</view> </view>
<view class="picker-view" @click="show = true"> <!-- USDT 提现 -->
<u-input <template v-if="userInfo.pay_channel != 'ALIN_VN'">
readonly <view class="picker-view" @click="show = true">
:placeholder="$lang.pleaseChoose" <u-input
color="#93979b" readonly
v-model="type" :placeholder="$lang.pleaseChoose"
border="none" color="#93979b"
> v-model="type"
<u--text border="none"
:text="$lang.chooseWithdraw" >
slot="prefix" <u--text
margin="0 3px 0 0" :text="$lang.chooseWithdraw"
type="tips" slot="prefix"
></u--text> margin="0 3px 0 0"
<u-icon type="tips"
slot="suffix" ></u--text>
name="arrow-down-fill" <u-icon
color="#757272" slot="suffix"
size="16" name="arrow-down-fill"
></u-icon> color="#757272"
</u-input> size="16"
</view> ></u-icon>
<u-picker </u-input>
:title="$lang.pleaseChooseWithdraw" </view>
closeOnClickOverlay <u-picker
:show="show" :title="$lang.pleaseChooseWithdraw"
:columns="columns" closeOnClickOverlay
:cancelText="$lang.cancel" :show="show"
:confirmText="$lang.confirm" :columns="columns"
@cancel="close" :cancelText="$lang.cancel"
@close="close" :confirmText="$lang.confirm"
@confirm="confirm" @cancel="close"
></u-picker> @close="close"
<view class="list"> @confirm="confirm"
<view class="lable">{{ $lang.walletAddress }}</view> ></u-picker>
<u-input <view class="list">
class="input" <view class="lable">{{ $lang.walletAddress }}</view>
color="#947b2b" <u-input
clearable class="input"
:placeholder="$lang.enterWithdrawalAddress" color="#947b2b"
border="surround" clearable
v-model="address" :placeholder="$lang.enterWithdrawalAddress"
></u-input> border="surround"
</view> v-model="address"
<view class="list"> ></u-input>
<view class="lable">{{ $lang.amount }}</view> </view>
<u-input <view class="list">
class="input" <view class="lable">{{ $lang.amount }}</view>
color="#947b2b" <u-input
clearable class="input"
:placeholder="$lang.enterWithdrawalamount" color="#947b2b"
border="surround" clearable
v-model="money" :placeholder="$lang.enterWithdrawalamount"
></u-input> border="surround"
</view> v-model="money"
<view class="list"> ></u-input>
<view class="lab">{{ $lang.rechargeWithdrawNetwork }}</view> </view>
<view class="type">TRC20</view> <view class="list">
</view> <view class="lab">{{ $lang.rechargeWithdrawNetwork }}</view>
<view class="list"> <view class="type">TRC20</view>
<u-button </view>
@tap="applyWithdraw" <view class="list">
class="button" <u-button
color="#e5b932" @tap="applyWithdraw"
:text="$lang.submit" class="button"
></u-button> color="#e5b932"
</view> :text="$lang.submit"
<view class="list"> ></u-button>
<view class="tip">{{ $lang.withdrawTip }}</view> </view>
<view class="tip">{{ $lang.commission }}:1 USDT</view> <view class="list">
</view> <view class="tip">{{ $lang.withdrawTip }}</view>
<view class="tip">{{ $lang.commission }}:1 USDT</view>
</view>
</template>
<!-- 越南银行卡提现 -->
<template v-if="userInfo.pay_channel == 'ALIN_VN'">
<view class="picker-view" @click="showBankPicker = true">
<u-input
readonly
:placeholder="$lang.pleaseChoose || '请选择'"
color="#93979b"
v-model="bankName"
border="none"
>
<u--text
:text="$lang.bankName || '银行名称'"
slot="prefix"
margin="0 3px 0 0"
type="tips"
></u--text>
<u-icon
slot="suffix"
name="arrow-down-fill"
color="#757272"
size="16"
></u-icon>
</u-input>
</view>
<u-picker
:title="$lang.bankName || '选择银行'"
closeOnClickOverlay
:show="showBankPicker"
:columns="bankColumns"
:cancelText="$lang.cancel"
:confirmText="$lang.confirm"
@cancel="showBankPicker = false"
@close="showBankPicker = false"
@confirm="confirmBank"
></u-picker>
<view class="list">
<view class="lable">{{ $lang.accountName || '账户姓名' }}</view>
<u-input
class="input"
color="#947b2b"
clearable
:placeholder="$lang.enterAccountName || '请输入账户姓名'"
border="surround"
v-model="accountName"
></u-input>
</view>
<view class="list">
<view class="lable">{{ $lang.accountNumber || '银行卡号' }}</view>
<u-input
class="input"
color="#947b2b"
clearable
:placeholder="$lang.enterAccountNumber || '请输入银行卡号'"
border="surround"
v-model="accountNumber"
></u-input>
</view>
<view class="list">
<view class="lable">{{ $lang.amount }}</view>
<u-input
class="input"
color="#947b2b"
clearable
type="number"
:placeholder="'Tối thiểu 1,000,000 VND'"
border="surround"
v-model="money"
></u-input>
</view>
<view class="list">
<u-button
@tap="applyWithdrawBank"
class="button"
color="#e5b932"
:text="$lang.submit"
></u-button>
</view>
<view class="list">
<view class="tip">{{ $lang.withdrawTip }}</view>
</view>
</template>
</view> </view>
<view class="mb60"></view> <view class="mb60"></view>
</view> </view>
@@ -104,6 +188,14 @@ export default {
type: "USDT", type: "USDT",
show: false, show: false,
columns: [["USDT"]], columns: [["USDT"]],
// 越南银行卡提现
bankName: "",
bankCode: "",
accountName: "",
accountNumber: "",
showBankPicker: false,
bankColumns: [[]],
bankList: [],
}; };
}, },
computed: { computed: {
@@ -112,7 +204,12 @@ export default {
$lang: (state) => state.app.lang[state.app.language], $lang: (state) => state.app.lang[state.app.language],
}), }),
}, },
onLoad() {}, onLoad() {
// 越南渠道加载银行列表
if (this.userInfo && this.userInfo.pay_channel === 'ALIN_VN') {
this.loadBankList();
}
},
methods: { methods: {
confirm(chose) { confirm(chose) {
this.type = chose.value[0]; this.type = chose.value[0];
@@ -169,6 +266,111 @@ export default {
}); });
} }
}, },
// 加载越南银行列表
loadBankList() {
this.$api
.getBankListVn({ user_id: this.userInfo.id })
.then((res) => {
if (res.Data && res.Data.data) {
this.bankList = res.Data.data;
this.bankColumns = [res.Data.data.map((item) => item.name)];
}
})
.catch((err) => {
console.log(err);
});
},
// 选择银行
confirmBank(chose) {
this.bankName = chose.value[0];
this.bankCode = chose.value[0];
this.showBankPicker = false;
},
// 越南银行卡提现
applyWithdrawBank() {
if (!this.bankName) {
this.$toast({
message: this.$lang.bankName || '请选择银行',
duration: 2000,
});
return;
}
if (!this.accountName) {
this.$toast({
message: this.$lang.enterAccountName || '请输入账户姓名',
duration: 2000,
});
return;
}
if (!this.accountNumber) {
this.$toast({
message: this.$lang.enterAccountNumber || '请输入银行卡号',
duration: 2000,
});
return;
}
if (!this.money || this.money <= 0) {
this.$toast({
message: this.$lang.amountCannotBe0,
duration: 2000,
});
return;
}
if (Number(this.money) < 1000000) {
this.$toast({
message: 'Số tiền rút tối thiểu là 1,000,000 VND',
duration: 2000,
});
return;
}
if (Number(this.money) > Number(this.userInfo.money)) {
this.$toast({
message: this.$lang.insufficientBalance || 'Số dư không đủ',
duration: 2000,
});
return;
}
const params = {
user_id: this.userInfo.id,
account_name: this.accountName,
account_number: this.accountNumber,
bank_code: this.bankCode,
bank_name: this.bankName,
amount: this.money,
};
this.$toast({
type: "loading",
message: this.$lang.submittingApplication || '提交中...',
duration: 200000,
});
this.$api
.applyWithdrawBank(params)
.then((res) => {
if (res.Success) {
this.$toast({
type: "success",
message: res.Msg,
duration: 2000,
});
this.bankName = "";
this.bankCode = "";
this.accountName = "";
this.accountNumber = "";
this.money = "";
} else {
this.$toast({
message: res.Msg,
duration: 2000,
});
}
})
.catch((err) => {
console.log(err);
})
.finally(() => {
this.$toastHide();
});
},
}, },
}; };
</script> </script>
+12
View File
@@ -53,6 +53,18 @@ let api = {
ybfDeposit: async (params) => { ybfDeposit: async (params) => {
return await ajax.post("/deposit", params); return await ajax.post("/deposit", params);
}, },
// 越南AlinPay充值
alinDeposit: async (params) => {
return await ajax.post("/alin_online_order", params);
},
// 越南银行卡提现
applyWithdrawBank: async (params) => {
return await ajax.post("/apply_withdraw_bank", params);
},
// 获取越南银行列表
getBankListVn: async (params) => {
return await ajax.post("/get_bank_list_vn", params);
},
// 获取用户信息 // 获取用户信息
getUserInfo: async (params) => { getUserInfo: async (params) => {
return await ajax.post("/get_user_info", params); return await ajax.post("/get_user_info", params);