675 lines
21 KiB
Vue
675 lines
21 KiB
Vue
<!-- src/pages/pay/oil/authorize_preauthcharge.vue -->
|
||
<template>
|
||
<div class="flex">
|
||
|
||
|
||
<!-- Main Content -->
|
||
<div class="flex-1 px-8 py-6">
|
||
|
||
<!-- Breadcrumbs & Header -->
|
||
<div class="text-sm breadcrumbs mb-6">
|
||
<ul>
|
||
<li><router-link :to="{ name: 'home' }">Home</router-link></li>
|
||
<li><router-link :to="{ name: 'payOil', params: { id: deliveryId } }">Payment Confirmation</router-link></li>
|
||
<li>Payment Authorization</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<!-- 2x2 Grid Layout -->
|
||
<div class="max-w-6xl">
|
||
<h1 class="text-3xl font-bold mb-8">Payment Authorization Authorize.net</h1>
|
||
|
||
<!-- Top Row: Charge Breakdown and Payment Method -->
|
||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
|
||
<!-- Charge Breakdown -->
|
||
<div class="bg-base-100 rounded-lg p-6">
|
||
<h3 class="text-lg font-semibold mb-4">Charge Breakdown</h3>
|
||
<div class="space-y-2">
|
||
<div class="flex justify-between">
|
||
<span>Gallons Ordered:</span>
|
||
<span>{{ delivery.gallons_ordered || 0 }} gallons</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span>Price per Gallon:</span>
|
||
<span>${{ delivery.customer_price || 0 }}</span>
|
||
</div>
|
||
<div class="flex justify-between font-semibold">
|
||
<span>Subtotal:</span>
|
||
<span>${{ calculateSubtotal() }}</span>
|
||
</div>
|
||
<div v-if="delivery.prime == 1" class="flex justify-between text-sm">
|
||
<span>Prime Fee:</span>
|
||
<span>+ ${{ pricing.price_prime || 0 }}</span>
|
||
</div>
|
||
<div v-if="delivery.same_day == 1" class="flex justify-between text-sm">
|
||
<span>Same Day Fee:</span>
|
||
<span>+ ${{ pricing.price_same_day || 0 }}</span>
|
||
</div>
|
||
<div v-if="delivery.emergency == 1" class="flex justify-between text-sm">
|
||
<span>Emergency Fee:</span>
|
||
<span>+ ${{ pricing.price_emergency || 0 }}</span>
|
||
</div>
|
||
<div v-if="promo_active" class="flex justify-between text-success">
|
||
<span>{{ promo.name_of_promotion }}:</span>
|
||
<span>- ${{ discount }}</span>
|
||
</div>
|
||
<hr class="my-3">
|
||
<div class="flex justify-between font-bold text-lg">
|
||
<span>Total:</span>
|
||
<span>${{ calculateTotalAmount() }}</span>
|
||
</div>
|
||
</div> <!-- close space-y-2 -->
|
||
</div> <!-- close bg-base-100 -->
|
||
|
||
<!-- Credit Card Display -->
|
||
<div class="bg-base-100 rounded-lg p-6">
|
||
<h3 class="text-lg font-semibold mb-4">Payment Method</h3>
|
||
<div v-if="selectedCard" class="bg-base-200 p-4 rounded-md">
|
||
<div class="flex justify-between items-center mb-2">
|
||
<div class="font-semibold">{{ selectedCard.type_of_card }}</div>
|
||
<div v-if="selectedCard.main_card" class="badge badge-primary">Primary</div>
|
||
</div>
|
||
<div class="mt-3 text-sm font-mono tracking-wider">
|
||
<p>{{ selectedCard.card_number }}</p>
|
||
<p>{{ selectedCard.name_on_card }}</p>
|
||
<p>
|
||
Exp:
|
||
<span v-if="Number(selectedCard.expiration_month) < 10">0</span>{{ selectedCard.expiration_month }} / {{ selectedCard.expiration_year }}
|
||
</p>
|
||
<p>CVV: {{ selectedCard.security_number }}</p>
|
||
</div>
|
||
|
||
<div class="divider my-2"></div>
|
||
|
||
<div class="flex justify-end gap-2">
|
||
<router-link :to="{ name: 'cardedit', params: { id: selectedCard.id }}" class="link link-hover text-xs">Edit</router-link>
|
||
</div>
|
||
</div>
|
||
<div v-else class="text-gray-500 p-4">
|
||
No payment method selected
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Error/Success Messages -->
|
||
<div class="mb-6">
|
||
<div v-if="error" class="alert alert-error">
|
||
<span>{{ error }}</span>
|
||
</div>
|
||
<div v-if="success" class="alert alert-success">
|
||
<span>{{ success }}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Bottom Section: Charge Amount Input and Action Buttons -->
|
||
<div class="bg-base-100 rounded-lg p-6">
|
||
<div class="flex flex-col lg:flex-row lg:items-center gap-6">
|
||
<!-- Charge Amount Input - Compact -->
|
||
<div class="flex-1">
|
||
<h3 class="text-lg font-semibold mb-2">Charge Amount</h3>
|
||
<div class="flex">
|
||
<span class="inline-flex items-center px-3 text-sm bg-base-200 rounded-l-lg border border-r-0">$</span>
|
||
<input
|
||
v-model="chargeAmount"
|
||
type="number"
|
||
step="0.01"
|
||
class="input input-bordered flex-1 rounded-l-none"
|
||
placeholder="Enter amount"
|
||
:disabled="loading"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Action Buttons -->
|
||
<div class="flex gap-3 align-bottom">
|
||
<router-link :to="{ name: 'payOil', params: { id: deliveryId } }">
|
||
<button class="btn btn-ghost">Cancel</button>
|
||
</router-link>
|
||
<button
|
||
@click="handlePreauthorize"
|
||
class="btn btn-success"
|
||
:disabled="loading || !chargeAmount"
|
||
>
|
||
<span v-if="loading && action === 'preauthorize'" class="loading loading-spinner loading-sm"></span>
|
||
Preauthorize
|
||
</button>
|
||
<button
|
||
@click="handleChargeNow"
|
||
class="btn btn-warning text-black"
|
||
:disabled="loading || !chargeAmount"
|
||
>
|
||
<span v-if="loading && action === 'charge'" class="loading loading-spinner loading-sm"></span>
|
||
Charge Now
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Charge Confirmation Modal -->
|
||
<div class="modal" :class="{ 'modal-open': isChargeConfirmationModalVisible }">
|
||
<div class="modal-box">
|
||
<h3 class="font-bold text-lg text-warning">⚠️ Warning: Charge Now</h3>
|
||
<p class="py-4">
|
||
You are about to <strong>immediately charge</strong> this customer's card
|
||
for <strong>${{ chargeAmount.toFixed(2) }}</strong>.
|
||
<br><br>
|
||
This action is <strong>not reversible</strong> and will debit the customer's account immediately.
|
||
<br><br>
|
||
Are you sure you want to proceed with the charge?
|
||
</p>
|
||
<div class="modal-action">
|
||
<button @click="proceedWithCharge" class="btn btn-warning">
|
||
Yes, Charge Now
|
||
</button>
|
||
<button @click="cancelCharge" class="btn btn-ghost">
|
||
Cancel
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed, onMounted, watch } from 'vue'
|
||
import { useRoute, useRouter } from 'vue-router'
|
||
import axios, { AxiosResponse, AxiosError } from 'axios'
|
||
import authHeader from '../../../services/auth.header'
|
||
import { notify } from "@kyvg/vue3-notification"
|
||
import type {
|
||
DeliveryFormData,
|
||
CustomerFormData,
|
||
CreditCardFormData,
|
||
PricingData,
|
||
PromoData,
|
||
DeliveryOrderResponse,
|
||
DeliveryTotalResponse,
|
||
OilPricingResponse,
|
||
PromoResponse,
|
||
WhoAmIResponse,
|
||
UpdateStatusResponse
|
||
} from '../../../types/models'
|
||
|
||
// Router and route
|
||
const route = useRoute()
|
||
const router = useRouter()
|
||
|
||
// Reactive data
|
||
const deliveryId = ref(route.params.id as string)
|
||
const loaded = ref(false)
|
||
const chargeAmount = ref(0)
|
||
const loading = ref(false)
|
||
const action = ref('') // 'preauthorize' or 'charge'
|
||
const error = ref('')
|
||
const success = ref('')
|
||
const isChargeConfirmationModalVisible = ref(false)
|
||
const user = ref({
|
||
user_id: 0,
|
||
})
|
||
const delivery = ref<DeliveryFormData>({
|
||
id: 0,
|
||
customer_id: 0,
|
||
customer_name: '',
|
||
customer_address: '',
|
||
customer_town: '',
|
||
customer_state: 0,
|
||
customer_zip: '',
|
||
gallons_ordered: 0,
|
||
customer_asked_for_fill: 0,
|
||
gallons_delivered: 0,
|
||
customer_filled: 0,
|
||
delivery_status: 0,
|
||
when_ordered: '',
|
||
when_delivered: '',
|
||
expected_delivery_date: '',
|
||
automatic: 0,
|
||
oil_id: 0,
|
||
supplier_price: 0,
|
||
customer_price: 0,
|
||
customer_temperature: 0,
|
||
dispatcher_notes: '',
|
||
prime: 0,
|
||
promo_id: 0,
|
||
emergency: 0,
|
||
same_day: 0,
|
||
payment_type: 0,
|
||
payment_card_id: 0,
|
||
driver_employee_id: 0,
|
||
driver_first_name: '',
|
||
driver_last_name: '',
|
||
pre_charge_amount: 0,
|
||
total_price: 0,
|
||
service_id: null,
|
||
})
|
||
const credit_cards = ref<CreditCardFormData[]>([
|
||
{
|
||
id: 0,
|
||
name_on_card: '',
|
||
main_card: false,
|
||
card_number: '',
|
||
expiration_month: '',
|
||
type_of_card: '',
|
||
last_four_digits: '',
|
||
expiration_year: '',
|
||
security_number: '',
|
||
}
|
||
])
|
||
const customer = ref<CustomerFormData>({
|
||
id: 0,
|
||
user_id: 0,
|
||
customer_first_name: '',
|
||
customer_last_name: '',
|
||
customer_town: '',
|
||
customer_address: '',
|
||
customer_state: 0,
|
||
customer_zip: '',
|
||
customer_apt: '',
|
||
customer_home_type: 0,
|
||
customer_phone_number: '',
|
||
account_number: '',
|
||
})
|
||
const pricing = ref<PricingData>({
|
||
price_from_supplier: 0,
|
||
price_for_customer: 0,
|
||
price_for_employee: 0,
|
||
price_same_day: 0,
|
||
price_prime: 0,
|
||
price_emergency: 0,
|
||
date: "",
|
||
})
|
||
const promo_active = ref(false)
|
||
const promo = ref<PromoData>({
|
||
name_of_promotion: '',
|
||
description: '',
|
||
money_off_delivery: 0,
|
||
text_on_ticket: ''
|
||
})
|
||
const total_amount = ref(0)
|
||
const discount = ref(0)
|
||
const total_amount_after_discount = ref(0)
|
||
|
||
// Computed properties
|
||
const selectedCard = computed(() => {
|
||
return credit_cards.value.find((card) => card.id === delivery.value.payment_card_id)
|
||
})
|
||
|
||
// Lifecycle
|
||
onMounted(() => {
|
||
loadData(deliveryId.value)
|
||
})
|
||
|
||
// Watchers
|
||
watch(() => route.params.id, (newId) => {
|
||
if (newId !== deliveryId.value) {
|
||
resetState()
|
||
deliveryId.value = newId as string
|
||
loadData(newId as string)
|
||
}
|
||
})
|
||
|
||
// Functions
|
||
const resetState = () => {
|
||
loading.value = false
|
||
action.value = ''
|
||
error.value = ''
|
||
success.value = ''
|
||
chargeAmount.value = 0
|
||
promo_active.value = false
|
||
total_amount.value = 0
|
||
discount.value = 0
|
||
total_amount_after_discount.value = 0
|
||
deliveryId.value = route.params.id as string
|
||
}
|
||
|
||
const loadData = (deliveryId: string) => {
|
||
userStatus()
|
||
getOilOrder(deliveryId)
|
||
sumdelivery(deliveryId)
|
||
getOilPricing()
|
||
updatestatus()
|
||
}
|
||
|
||
const updatestatus = () => {
|
||
let path = import.meta.env.VITE_BASE_URL + '/delivery/updatestatus';
|
||
axios({
|
||
method: 'get',
|
||
url: path,
|
||
headers: authHeader(),
|
||
}).then((response: AxiosResponse<UpdateStatusResponse>) => {
|
||
if (response.data.update)
|
||
console.log("Updated Status of Deliveries")
|
||
})
|
||
}
|
||
|
||
const updateChargeAmount = () => {
|
||
// Only update if we have all necessary data
|
||
if (total_amount_after_discount.value > 0 &&
|
||
pricing.value.price_prime !== undefined &&
|
||
pricing.value.price_same_day !== undefined &&
|
||
pricing.value.price_emergency !== undefined) {
|
||
chargeAmount.value = calculateTotalAsNumber();
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
const sumdelivery = (delivery_id: number | string) => {
|
||
let path = import.meta.env.VITE_BASE_URL + "/delivery/total/" + delivery_id;
|
||
axios({
|
||
method: "get",
|
||
url: path,
|
||
withCredentials: true,
|
||
})
|
||
.then((response: AxiosResponse<DeliveryTotalResponse>) => {
|
||
if (response.data.ok) {
|
||
total_amount.value = parseFloat(String(response.data.total_amount)) || 0;
|
||
discount.value = parseFloat(String(response.data.discount)) || 0;
|
||
total_amount_after_discount.value = parseFloat(String(response.data.total_amount_after_discount)) || 0;
|
||
|
||
// Try to update charge amount with complete pricing
|
||
const updated = updateChargeAmount();
|
||
|
||
// Fallback only if pricing not loaded yet and calculation didn't run
|
||
if (!updated) {
|
||
if (promo_active.value) {
|
||
chargeAmount.value = total_amount_after_discount.value;
|
||
} else {
|
||
chargeAmount.value = total_amount.value;
|
||
}
|
||
}
|
||
}
|
||
})
|
||
.catch(() => {
|
||
notify({
|
||
title: "Error",
|
||
text: "Could not get oil pricing",
|
||
type: "error",
|
||
});
|
||
});
|
||
}
|
||
|
||
const getPromo = (promo_id: number) => {
|
||
let path = import.meta.env.VITE_BASE_URL + "/promo/" + promo_id;
|
||
axios({
|
||
method: "get",
|
||
url: path,
|
||
withCredentials: true,
|
||
headers: authHeader(),
|
||
})
|
||
.then((response: AxiosResponse<PromoResponse>) => {
|
||
if (response.data) {
|
||
promo.value = response.data
|
||
promo_active.value = true
|
||
|
||
// Trigger a charge amount update if all data is available
|
||
updateChargeAmount();
|
||
}
|
||
})
|
||
}
|
||
|
||
const userStatus = () => {
|
||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||
axios({
|
||
method: 'get',
|
||
url: path,
|
||
withCredentials: true,
|
||
headers: authHeader(),
|
||
})
|
||
.then((response: AxiosResponse<WhoAmIResponse>) => {
|
||
if (response.data.ok) {
|
||
user.value = response.data.user;
|
||
}
|
||
})
|
||
}
|
||
|
||
const getOilPricing = () => {
|
||
let path = import.meta.env.VITE_BASE_URL + "/info/price/oil/table";
|
||
axios({
|
||
method: "get",
|
||
url: path,
|
||
withCredentials: true,
|
||
})
|
||
.then((response: AxiosResponse<OilPricingResponse>) => {
|
||
pricing.value = response.data;
|
||
// Try to update charge amount when pricing is loaded
|
||
updateChargeAmount();
|
||
})
|
||
.catch(() => {
|
||
notify({
|
||
title: "Error",
|
||
text: "Could not get oil pricing",
|
||
type: "error",
|
||
});
|
||
});
|
||
}
|
||
|
||
const getOilOrder = (delivery_id: number | string) => {
|
||
let path = import.meta.env.VITE_BASE_URL + "/delivery/order/" + delivery_id;
|
||
axios({
|
||
method: "get",
|
||
url: path,
|
||
withCredentials: true,
|
||
})
|
||
.then((response: AxiosResponse<DeliveryOrderResponse>) => {
|
||
if (response.data && response.data.ok) {
|
||
delivery.value = response.data.delivery as DeliveryFormData;
|
||
getCustomer(delivery.value.customer_id)
|
||
getCreditCards(delivery.value.customer_id)
|
||
if (delivery.value.promo_id != null) {
|
||
getPromo(delivery.value.promo_id);
|
||
promo_active.value = true;
|
||
}
|
||
} else {
|
||
console.error("API Error:", response.data.error || "Failed to fetch delivery data.");
|
||
}
|
||
})
|
||
.catch((error: Error) => {
|
||
console.error("API Error in getOilOrder:", error);
|
||
notify({
|
||
title: "Error",
|
||
text: "Could not get delivery",
|
||
type: "error",
|
||
});
|
||
});
|
||
}
|
||
|
||
const getCreditCards = (user_id: number) => {
|
||
let path = import.meta.env.VITE_BASE_URL + '/payment/cards/' + user_id;
|
||
axios({
|
||
method: 'get',
|
||
url: path,
|
||
headers: authHeader(),
|
||
}).then((response: AxiosResponse<CreditCardFormData[]>) => {
|
||
credit_cards.value = response.data
|
||
})
|
||
}
|
||
|
||
const getCustomer = (userid: number) => {
|
||
let path = import.meta.env.VITE_BASE_URL + '/customer/' + userid;
|
||
axios({
|
||
method: 'get',
|
||
url: path,
|
||
headers: authHeader(),
|
||
}).then((response: AxiosResponse<CustomerFormData>) => {
|
||
customer.value = response.data
|
||
})
|
||
}
|
||
|
||
const calculateSubtotal = () => {
|
||
const gallons = delivery.value.gallons_ordered || 0
|
||
const pricePerGallon = delivery.value.customer_price || 0
|
||
return (gallons * pricePerGallon).toFixed(2)
|
||
}
|
||
|
||
const calculateTotalAmount = () => {
|
||
if (total_amount_after_discount.value == null || total_amount_after_discount.value === undefined) {
|
||
return '0.00';
|
||
}
|
||
|
||
let totalNum = Number(total_amount_after_discount.value);
|
||
if (isNaN(totalNum)) {
|
||
return '0.00';
|
||
}
|
||
|
||
if (delivery.value && delivery.value.prime == 1 && pricing.value && pricing.value.price_prime) {
|
||
totalNum += Number(pricing.value.price_prime) || 0;
|
||
}
|
||
if (delivery.value && delivery.value.same_day == 1 && pricing.value && pricing.value.price_same_day) {
|
||
totalNum += Number(pricing.value.price_same_day) || 0;
|
||
}
|
||
if (delivery.value && delivery.value.emergency == 1 && pricing.value && pricing.value.price_emergency) {
|
||
totalNum += Number(pricing.value.price_emergency) || 0;
|
||
}
|
||
|
||
return totalNum.toFixed(2);
|
||
}
|
||
|
||
const calculateTotalAsNumber = () => {
|
||
if (total_amount_after_discount.value == null || total_amount_after_discount.value === undefined) {
|
||
return 0;
|
||
}
|
||
|
||
let totalNum = Number(total_amount_after_discount.value);
|
||
if (isNaN(totalNum)) {
|
||
return 0;
|
||
}
|
||
|
||
if (delivery.value && delivery.value.prime == 1 && pricing.value && pricing.value.price_prime) {
|
||
totalNum += Number(pricing.value.price_prime) || 0;
|
||
}
|
||
if (delivery.value && delivery.value.same_day == 1 && pricing.value && pricing.value.price_same_day) {
|
||
totalNum += Number(pricing.value.price_same_day) || 0;
|
||
}
|
||
if (delivery.value && delivery.value.emergency == 1 && pricing.value && pricing.value.price_emergency) {
|
||
totalNum += Number(pricing.value.price_emergency) || 0;
|
||
}
|
||
|
||
return totalNum;
|
||
}
|
||
|
||
const handlePreauthorize = async () => {
|
||
await processPayment('preauthorize')
|
||
}
|
||
|
||
const handleChargeNow = async () => {
|
||
if (!selectedCard.value) {
|
||
error.value = 'No credit card found for this customer'
|
||
return
|
||
}
|
||
if (!chargeAmount.value || chargeAmount.value <= 0) {
|
||
error.value = 'Please enter a valid charge amount'
|
||
return
|
||
}
|
||
isChargeConfirmationModalVisible.value = true
|
||
}
|
||
|
||
const proceedWithCharge = async () => {
|
||
isChargeConfirmationModalVisible.value = false
|
||
await processPayment('charge')
|
||
}
|
||
|
||
const cancelCharge = () => {
|
||
isChargeConfirmationModalVisible.value = false
|
||
}
|
||
|
||
const processPayment = async (actionType: string) => {
|
||
if (!selectedCard.value) {
|
||
error.value = 'No credit card found for this customer'
|
||
return
|
||
}
|
||
|
||
loading.value = true
|
||
action.value = actionType
|
||
error.value = ''
|
||
success.value = ''
|
||
|
||
try {
|
||
// Step 2: If payment method is credit, perform the pre-authorization
|
||
if (actionType === 'preauthorize') {
|
||
if (!chargeAmount.value || chargeAmount.value <= 0) {
|
||
throw new Error("Pre-authorization amount must be greater than zero.");
|
||
}
|
||
|
||
const authPayload = {
|
||
card_id: selectedCard.value!.id,
|
||
preauthorize_amount: chargeAmount.value.toFixed(2),
|
||
delivery_id: delivery.value.id,
|
||
};
|
||
|
||
const authPath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/authorize/saved-card/${customer.value.id}`;
|
||
|
||
const response = await axios.post(authPath, authPayload, { withCredentials: true, headers: authHeader() });
|
||
|
||
// Update payment type to 11 after successful preauthorization
|
||
try {
|
||
await axios.put(`${import.meta.env.VITE_BASE_URL}/payment/authorize/${delivery.value.id}`, {}, { headers: authHeader() });
|
||
} catch (updateError) {
|
||
console.error('Failed to update payment type after preauthorization:', updateError);
|
||
}
|
||
|
||
// On successful authorization, show success and redirect
|
||
success.value = `Preauthorization successful! Transaction ID: ${response.data?.auth_net_transaction_id || 'N/A'}`;
|
||
setTimeout(() => {
|
||
router.push({ name: "customerProfile", params: { id: customer.value.id } });
|
||
}, 2000);
|
||
} else { // Handle 'charge' action
|
||
if (!chargeAmount.value || chargeAmount.value <= 0) {
|
||
throw new Error("Charge amount must be greater than zero.");
|
||
}
|
||
|
||
// Create a payload that matches the backend's TransactionCreateByCardID schema
|
||
const chargePayload = {
|
||
card_id: selectedCard.value!.id,
|
||
charge_amount: chargeAmount.value.toFixed(2),
|
||
delivery_id: delivery.value.id,
|
||
service_id: delivery.value.service_id || null,
|
||
// You can add other fields here if your schema requires them
|
||
};
|
||
|
||
// Use the correct endpoint for charging a saved card
|
||
const chargePath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/charge/saved-card/${customer.value.id}`;
|
||
|
||
console.log('=== DEBUG: Charge payload ===');
|
||
console.log('Calling endpoint:', chargePath);
|
||
console.log('Final payload being sent:', chargePayload);
|
||
|
||
const response = await axios.post(chargePath, chargePayload, { withCredentials: true, headers: authHeader() });
|
||
|
||
// Update payment type to 11 after successful charge
|
||
try {
|
||
await axios.put(`${import.meta.env.VITE_BASE_URL}/payment/authorize/${delivery.value.id}`, {}, { headers: authHeader() });
|
||
} catch (updateError) {
|
||
console.error('Failed to update payment type after charge:', updateError);
|
||
}
|
||
|
||
// Status codes: 0 = APPROVED, 1 = DECLINED (based on backend TransactionStatus enum)
|
||
if (response.data && response.data.status === 0) { // 0 = APPROVED
|
||
success.value = `Charge successful! Transaction ID: ${response.data.auth_net_transaction_id || 'N/A'}`;
|
||
setTimeout(() => {
|
||
router.push({ name: "customerProfile", params: { id: customer.value.id } });
|
||
}, 2000);
|
||
} else {
|
||
// The error message from your backend will be more specific now
|
||
throw new Error(`Payment charge failed: ${response.data?.rejection_reason || 'Unknown error'}`);
|
||
}
|
||
}
|
||
} catch (err: unknown) {
|
||
const axiosErr = err as AxiosError<{ detail?: string }>;
|
||
console.log(err)
|
||
error.value = axiosErr.response?.data?.detail || `Failed to ${actionType} payment`
|
||
notify({
|
||
title: "Error",
|
||
text: error.value,
|
||
type: "error",
|
||
})
|
||
} finally {
|
||
loading.value = false
|
||
action.value = ''
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped></style>
|