238 lines
5.7 KiB
Vue
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> |