jcloud/dashboard/src2/components/billing/mpesa/BuyPrepaidCreditsMpesa.vue
2025-06-27 18:50:23 +08:00

238 lines
5.7 KiB
Vue

<template>
<div>
<label class="block mt-4">
<span class="text-sm leading-4 text-gray-700"> </span>
<ErrorMessage class="mt-1" :message="paymentErrorMessage" />
</label>
<ErrorMessage class="mt-2" :message="errorMessage" />
<FormControl
type="autocomplete"
:options="teams"
size="sm"
variant="subtle"
placeholder="支付合作伙伴"
:disabled="false"
label="选择支付合作伙伴"
v-model="partnerInput"
class="mb-5"
/>
<div class="flex gap-5 col-2">
<FormControl
label="M-Pesa 手机号码"
v-model="this.phoneNumberInput"
name="phone_number"
autocomplete="off"
class="mb-5"
type="number"
placeholder="例如 123456789"
/>
<FormControl
label="税号"
v-model="this.taxIdInput"
name="tax_id"
autocomplete="off"
class="mb-5"
type="string"
placeholder="例如 78346"
/>
</div>
<!-- 显示税后金额 -->
<div v-if="showTaxInfo" class="flex col-2 gap-5">
<FormControl
label="税率(%)"
disabled
:modelValue="taxPercentage"
type="number"
/>
<FormControl
label="含税总金额"
disabled
:modelValue="amountWithTax.toFixed(2)"
type="number"
/>
</div>
<div class="mt-4 flex w-full justify-end">
<Button variant="solid" @click="onPayClick" :loading="paymentInProgress">
通过 M-Pesa 支付
</Button>
</div>
<ErrorMessage v-if="errorMessage" :message="errorMessage" />
</div>
</template>
<script>
import { toast } from 'vue-sonner';
import { DashboardError } from '../../../utils/error';
import { jingrowRequest, ErrorMessage } from 'jingrow-ui';
export default {
name: 'BuyPrepaidCreditsMpesa',
props: {
amount: {
type: Number,
required: true,
},
amountKES: {
type: Number,
required: true,
default: 1,
},
minimumAmount: {
type: Number,
required: true,
default: 10,
},
exchangeRate: {
type: Number,
required: true,
default: 129,
},
},
data() {
return {
paymentErrorMessage: null,
errorMessage: null,
paymentInProgress: false,
partnerInput: '',
phoneNumberInput: this.$team.pg.mpesa_phone_number || '',
taxIdInput: this.$team.pg.mpesa_tax_id || '',
teams: [],
taxPercentage: 1,
amountWithTax: 0,
showTaxInfo: false,
maximumAmount: 150000,
};
},
resources: {
requestForPayment() {
return {
url: 'jcloud.api.billing.request_for_payment',
params: {
request_amount: this.amountKES,
sender: this.phoneNumberInput,
partner: this.partnerInput.value,
tax_id: this.taxIdInput,
amount_with_tax: this.amountWithTax,
phone_number: this.phoneNumberInput,
amount_usd: this.amount,
exchange_rate: this.exchangeRate,
},
validate() {
if (this.amount < this.minimumAmount) {
throw new DashboardError(
`金额低于允许的最小值: ${this.minimumAmount}`,
);
}
if (this.amount > this.maximumAmount) {
throw new DashboardError(
`金额超过允许的最大值: ${this.maximumAmount}`,
);
}
if (!this.partnerInput.value || !this.phoneNumberInput) {
throw new DashboardError(
'支付需要同时提供合作伙伴和手机号码。',
);
}
if (!this.taxIdInput) {
throw new DashboardError(
'支付需要提供税号。',
);
}
},
async onSuccess(data) {
if (data?.ResponseCode === '0') {
toast.success(
data.ResponseDescription ||
'请按照手机上的指示操作',
);
} else {
toast.error(
data.ResponseDescription ||
'出了点问题。请重试。',
);
}
},
};
},
},
methods: {
async onPayClick() {
this.paymentInProgress = true;
try {
const response = await this.$resources.requestForPayment.submit();
if (response.ResponseCode === '0') {
toast.success(
response.Success ||
'支付已成功发起,请查看手机上的指示',
);
this.$emit('success');
} else {
throw new Error(response.ResponseDescription || '支付失败');
}
} catch (error) {
this.paymentErrorMessage =
error.message || '支付失败。请重试。';
toast.error(this.paymentErrorMessage);
} finally {
this.paymentInProgress = false;
}
},
async fetchTeams() {
try {
const response = await jingrowRequest({
url: '/api/action/jcloud.api.regional_payments.mpesa.utils.display_mpesa_payment_partners',
method: 'GET',
});
if (Array.isArray(response)) {
this.teams = response;
} else {
console.log('无数据');
}
} catch (error) {
this.errorMessage = `获取团队失败 ${error.message}`;
}
},
totalAmountWithTax() {
const amountWithTax =
this.amountKES + (this.amountKES * this.taxPercentage) / 100;
this.amountWithTax = Math.round(amountWithTax);
},
async fetchTaxPercentage() {
try {
const taxPercentage = await jingrowRequest({
url: '/api/action/jcloud.api.regional_payments.mpesa.utils.get_tax_percentage',
method: 'GET',
params: {
payment_partner: this.partnerInput.value,
},
});
this.taxPercentage = taxPercentage;
} catch (error) {
this.errorMessage = `获取税率失败 ${error.message}`;
}
},
},
watch: {
partnerInput() {
this.fetchTaxPercentage();
this.totalAmountWithTax();
this.showTaxInfo = true;
},
amountKES() {
this.totalAmountWithTax();
},
taxPercentage() {
this.totalAmountWithTax();
},
},
mounted() {
this.fetchTeams();
},
};
</script>