diff --git a/src/pages/card/addcard.vue b/src/pages/card/addcard.vue
index 39eeb06..8c34552 100755
--- a/src/pages/card/addcard.vue
+++ b/src/pages/card/addcard.vue
@@ -201,16 +201,18 @@ export default defineComponent({
};
// --- STEP 2: CRITICAL CALL - SAVE CARD TO LOCAL DATABASE VIA FLASK ---
+ let card_id: number;
try {
const flaskPath = `${import.meta.env.VITE_BASE_URL}/payment/card/create/${this.customer.id}`;
console.log("Attempting to save card to local DB via Flask:", flaskPath);
const flaskResponse = await axios.post(flaskPath, flaskPayload, { withCredentials: true, headers: authHeader() });
-
+
if (!flaskResponse.data.ok) {
// If the primary save fails, stop everything and show an error.
throw new Error(flaskResponse.data.error || "Failed to save card.");
}
- console.log("Card successfully saved to local database via Flask.");
+ card_id = flaskResponse.data.card_id;
+ console.log("Card successfully saved to local database via Flask with ID:", card_id);
} catch (error: any) {
const errorMessage = error.response?.data?.error || "A critical error occurred while saving the card.";
@@ -219,15 +221,24 @@ export default defineComponent({
return; // End the function here
}
- // --- STEP 3: BEST-EFFORT CALL - TOKENIZE CARD VIA FASTAPI ---
+ // --- STEP 3: BEST-EFFORT CALL - TOKENIZE CARD VIA FASTAPI AND UPDATE LOCAL CARD ---
try {
const fastapiPath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/customers/${this.customer.id}/cards`;
console.log("Attempting to tokenize card with Authorize.Net via FastAPI:", fastapiPath);
- await axios.post(fastapiPath, fastapiPayload, { withCredentials: true, headers: authHeader() });
+ const fastapiResponse = await axios.post(fastapiPath, fastapiPayload, { withCredentials: true, headers: authHeader() });
console.log("Card successfully tokenized with Authorize.Net via FastAPI.");
+
+ // --- STEP 4: UPDATE LOCAL CARD WITH PAYMENT_PROFILE_ID ---
+ const payment_profile_id = fastapiResponse.data.payment_profile_id;
+ console.log("Updating local card with payment_profile_id:", payment_profile_id);
+ const updatePath = `${import.meta.env.VITE_BASE_URL}/payment/card/update_payment_profile/${card_id}`;
+ await axios.put(updatePath, { auth_net_payment_profile_id: payment_profile_id }, { withCredentials: true, headers: authHeader() });
+ console.log("Card successfully updated with payment_profile_id.");
+
} catch (error: any) {
// If this fails, we just log it for the developers. We DON'T show an error to the user.
console.warn("NON-CRITICAL-ERROR: Tokenization with Authorize.Net failed, but the card was saved locally.", error.response?.data || error.message);
+ // Card is saved but without payment_profile_id, which is ok as nullable.
}
// --- STEP 4: ALWAYS SHOW SUCCESS AND REDIRECT ---
@@ -238,4 +249,4 @@ export default defineComponent({
},
},
});
-
\ No newline at end of file
+
diff --git a/src/pages/customer/profile/profile.vue b/src/pages/customer/profile/profile.vue
index ce20823..0ea7a76 100755
--- a/src/pages/customer/profile/profile.vue
+++ b/src/pages/customer/profile/profile.vue
@@ -59,9 +59,10 @@
-
@@ -78,12 +79,13 @@
-
@@ -248,10 +250,11 @@ export default defineComponent({
deliveries: [] as Delivery[],
autodeliveries: [] as AutomaticDelivery[],
serviceCalls: [] as ServiceCall[],
+ transactions: [] as any[],
// --- END OF UPDATES ---
automatic_response: 0,
credit_cards_count: 0,
- customer: { 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: '', customer_latitude: 0, customer_longitude: 0, correct_address: true, account_number: '' },
+ customer: { 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: '', customer_latitude: 0, customer_longitude: 0, correct_address: true, account_number: '', auth_net_profile_id: null },
customer_description: { id: 0, customer_id: 0, account_number: '', company_id: '', fill_location: 0, description: '' },
customer_tank: { id: 0, last_tank_inspection: null, tank_status: false, outside_or_inside: false, tank_size: 0 },
customer_stats: { id: 0, customer_id: 0, total_calls: 0, service_calls_total: 0, service_calls_total_spent: 0, service_calls_total_profit: 0, oil_deliveries: 0, oil_total_gallons: 0, oil_total_spent: 0, oil_total_profit: 0 },
@@ -323,6 +326,7 @@ export default defineComponent({
this.getServiceCalls(this.customer.id);
this.fetchCustomerParts(this.customer.id);
this.loadServicePlan(this.customer.id);
+ this.getCustomerTransactions(this.customer.id);
}).catch((error: any) => {
console.error("CRITICAL: Failed to fetch main customer data. Aborting other calls.", error);
@@ -576,6 +580,19 @@ onSubmitSocial(commentText: string) {
console.error("Failed to get customer service calls:", error);
});
},
+ getCustomerTransactions(customerId: number) {
+ let path = `${import.meta.env.VITE_BASE_URL}/payment/transactions/customer/${customerId}/1`;
+ axios({
+ method: 'get',
+ url: path,
+ headers: authHeader(),
+ }).then((response: any) => {
+ this.transactions = response.data;
+ }).catch((error: any) => {
+ console.error("Failed to get customer transactions:", error);
+ this.transactions = [];
+ });
+ },
openEditModal(service: ServiceCall) {
this.selectedServiceForEdit = service;
},
diff --git a/src/pages/customer/profile/profile/CreditCards.vue b/src/pages/customer/profile/profile/CreditCards.vue
index afe6e23..406bdbf 100644
--- a/src/pages/customer/profile/profile/CreditCards.vue
+++ b/src/pages/customer/profile/profile/CreditCards.vue
@@ -16,6 +16,9 @@
{{ count }} card(s) on file.
+
+ Authorize.net Profile ID: {{ auth_net_profile_id || 'No Authorize.net account' }}
+
@@ -71,6 +74,7 @@ interface Props {
cards: CreditCard[];
count: number;
user_id: number;
+ auth_net_profile_id: string | null;
}
// 3. Use the generic defineProps to apply the types
diff --git a/src/pages/customer/profile/profile/HistoryTabs.vue b/src/pages/customer/profile/profile/HistoryTabs.vue
index 4eed15c..5aeae20 100644
--- a/src/pages/customer/profile/profile/HistoryTabs.vue
+++ b/src/pages/customer/profile/profile/HistoryTabs.vue
@@ -12,7 +12,14 @@
Service History
- $emit('openServiceModal', service)" />
+ $emit('openServiceModal', service)" />
+
+
+
+ Transactions
+
+
+
@@ -21,6 +28,7 @@
import { ref } from 'vue';
import DeliveriesTable from './DeliveriesTable.vue';
import ServiceCallsTable from './ServiceCallsTable.vue';
+import TransactionsTable from './TransactionsTable.vue';
// 1. Define the interfaces for the data this component receives and passes down.
// These should match the interfaces in the child components.
@@ -37,19 +45,39 @@ interface Delivery {
interface ServiceCall {
id: number;
scheduled_date: string;
+ customer_name: string;
+ customer_address: string;
+ customer_town: string;
type_service_call: number;
description: string;
}
+interface Transaction {
+ id: number;
+ preauthorize_amount: number | null;
+ charge_amount: number | null;
+ transaction_type: number;
+ status: number;
+ customer_name: string;
+ created_at: string;
+ auth_net_transaction_id: string | null;
+ rejection_reason: string | null;
+ delivery_id: number | null;
+ service_id: number | null;
+}
+
// 2. Define the Props interface
interface Props {
deliveries: Delivery[];
serviceCalls: ServiceCall[];
+ transactions: Transaction[];
}
// 3. Use the typed defineProps
defineProps();
-defineEmits(['openServiceModal']);
+defineEmits<{
+ openServiceModal: [service: ServiceCall];
+}>();
const activeTab = ref('deliveries');
-
\ No newline at end of file
+
diff --git a/src/pages/customer/profile/profile/TransactionsTable.vue b/src/pages/customer/profile/profile/TransactionsTable.vue
new file mode 100644
index 0000000..71509d2
--- /dev/null
+++ b/src/pages/customer/profile/profile/TransactionsTable.vue
@@ -0,0 +1,146 @@
+
+
+
+
No transaction history found.
+
+
+
+
+
+
+
+ | Transaction Info |
+ Amounts & Type |
+ Details & Actions |
+
+
+
+
+
+
+
+ #{{ transaction.id }}
+ {{ formatDate(transaction.created_at) }}
+ {{ getStatusText(transaction.status) }}
+
+ |
+
+
+
+ Pre-Auth: ${{ transaction.preauthorize_amount || '0.00' }}
+ Charge: ${{ transaction.charge_amount || '0.00' }}
+
+ Type: {{ transaction.transaction_type === 0 ? 'Charge' : transaction.transaction_type === 1 ? 'Auth' : 'Capture' }}
+
+ |
+
+
+ {{ getSourceText(transaction) }}
+
+
+ View Delivery
+
+
+
+ |
+
+
+
+
+
+
+ {{ transaction.rejection_reason }}
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
Transaction #{{ transaction.id }}
+
{{ formatDate(transaction.created_at) }}
+
+
+ {{ getStatusText(transaction.status) }}
+
+
+
+
+
Transaction ID: {{ transaction.auth_net_transaction_id || 'N/A' }}
+
Pre-Auth: ${{ transaction.preauthorize_amount || '0.00' }}
+
Charge: ${{ transaction.charge_amount || '0.00' }}
+
Type: {{ transaction.transaction_type === 0 ? 'Charge' : transaction.transaction_type === 1 ? 'Auth' : 'Capture' }}
+
Source: {{ getSourceText(transaction) }}
+
+
+
+
+
{{ transaction.rejection_reason }}
+
+
+
+
+
+
+ View Delivery
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/delivery/update_tickets/finalize_ticket.vue b/src/pages/delivery/update_tickets/finalize_ticket.vue
index 8aea0d5..764cc1f 100755
--- a/src/pages/delivery/update_tickets/finalize_ticket.vue
+++ b/src/pages/delivery/update_tickets/finalize_ticket.vue
@@ -90,14 +90,45 @@
When Ordered
{{ deliveryOrder.when_ordered }}
-
-
When Delivered
-
{{ deliveryOrder.when_delivered }}
-
+
+
+
Transaction Summary
+
+
+
+ Transaction ID:
+ {{ transaction.auth_net_transaction_id }}
+
+
+ Pre-Auth Amount:
+ ${{ transaction.preauthorize_amount || '0.00' }}
+
+
+ Charge Amount:
+ ${{ transaction.charge_amount || '0.00' }}
+
+
+ Type:
+ {{ transaction.transaction_type === 0 ? 'Charge' : transaction.transaction_type === 1 ? 'Auth' : 'Capture' }}
+
+
+ Date:
+ {{ transaction.created_at }}
+
+
+ Status:
+
+ {{ transaction.status === 0 ? 'Approved' : 'Declined' }}
+
+
+
+
+
+
Financial Summary
@@ -134,10 +165,12 @@
No Payment Type Added
-
+
{{ userCard.type_of_card }}
-
-
**** **** **** {{ userCard.last_four }}
+
+
Name: {{ userCard.name_on_card }}
+
Card: {{ userCard.card_number }}
+
CVV: {{ userCard.security_number }}
Exp: {{ userCard.expiration_month }} / {{ userCard.expiration_year }}
@@ -217,6 +250,9 @@ interface UserCard {
type_of_card: string;
expiration_month: number;
expiration_year: number;
+ name_on_card: string;
+ card_number: string;
+ security_number: string;
}
interface PreAuthTransaction {
id: number;
@@ -276,6 +312,7 @@ export default defineComponent({
price_prime: 0,
price_same_day: 0,
},
+ transaction: null as any,
}
},
computed: {
@@ -297,6 +334,7 @@ export default defineComponent({
console.log(`[DEBUG] Component Mounted. Fetching data for delivery ID: ${deliveryId}`);
this.getOilOrder(deliveryId);
this.getOilPricing();
+ this.getTransaction(deliveryId);
},
methods: {
async getOilOrder(delivery_id: any) {
@@ -371,6 +409,31 @@ export default defineComponent({
.then((response: any) => { this.pricing = response.data; })
.catch((error: any) => { console.error("[DEBUG] Error fetching oil pricing:", error); });
},
+ getTransaction(delivery_id: any) {
+ const path = `${import.meta.env.VITE_AUTHORIZE_URL}/api/transaction/delivery/${delivery_id}`;
+ axios.get(path, { withCredentials: true, headers: authHeader() })
+ .then((response: any) => {
+ this.transaction = response.data;
+ })
+ .catch((error: any) => {
+ // Handle 404 gracefully - delivery doesn't have transaction data in authorize system
+ if (error.response && error.response.status === 404) {
+ console.log(`No transaction found for delivery ${delivery_id} in authorize system`);
+ this.transaction = null;
+ } else {
+ console.error("Error fetching transaction:", error);
+ this.transaction = null;
+ }
+ });
+ },
+ getTypeColor(transactionType: number) {
+ switch (transactionType) {
+ case 1: return 'text-blue-600';
+ case 0: return 'text-orange-600';
+ case 2: return 'text-green-600';
+ default: return 'text-gray-600';
+ }
+ },
CreateTransaction() {
const path = `${import.meta.env.VITE_MONEY_URL}/delivery/add/${this.deliveryOrder.id}`;
axios.post(path, {}, { withCredentials: true, headers: authHeader() })
@@ -397,18 +460,29 @@ export default defineComponent({
}
if ([1, 2].includes(this.deliveryOrder.payment_type)) {
const transactionUrl = `${import.meta.env.VITE_AUTHORIZE_URL}/api/transaction/delivery/${this.deliveryOrder.id}`;
- const transactionResponse = await axios.get(transactionUrl, { withCredentials: true, headers: authHeader() });
- const preAuthTx = transactionResponse.data as PreAuthTransaction;
- if (preAuthTx && preAuthTx.transaction_type === 1 && preAuthTx.status === 0) {
- const capturePayload = {
- auth_net_transaction_id: preAuthTx.auth_net_transaction_id,
- charge_amount: this.finalChargeAmount.toFixed(2),
- };
- const capturePath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/capture/`;
- await axios.post(capturePath, capturePayload, { withCredentials: true, headers: authHeader() });
- notify({ title: "Payment Captured", text: `Successfully charged $${capturePayload.charge_amount}.`, type: "success" });
- } else {
- notify({ title: "Warning", text: "No valid pre-authorization found to capture. Please charge manually.", type: "warn" });
+ try {
+ const transactionResponse = await axios.get(transactionUrl, { withCredentials: true, headers: authHeader() });
+ const preAuthTx = transactionResponse.data as PreAuthTransaction;
+ if (preAuthTx && preAuthTx.transaction_type === 1 && preAuthTx.status === 0) {
+ const capturePayload = {
+ auth_net_transaction_id: preAuthTx.auth_net_transaction_id,
+ charge_amount: this.finalChargeAmount.toFixed(2),
+ };
+ const capturePath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/capture/`;
+ await axios.post(capturePath, capturePayload, { withCredentials: true, headers: authHeader() });
+ notify({ title: "Payment Captured", text: `Successfully charged $${capturePayload.charge_amount}.`, type: "success" });
+ } else {
+ notify({ title: "Warning", text: "No valid pre-authorization found to capture. Please charge manually.", type: "warn" });
+ }
+ } catch (error: any) {
+ if (error.response?.status === 404 && error.response?.data?.detail?.includes("No pre-authorization transaction found")) {
+ notify({ title: "Redirecting", text: "No pre-authorization found. Redirecting to customer profile to update payment method.", type: "info" });
+ this.$router.push({ name: "customerProfile", params: { id: this.customer.id } });
+ return;
+ } else {
+ notify({ title: "Error", text: "Failed to check transaction status.", type: "error" });
+ throw error;
+ }
}
}
this.CreateTransaction();
@@ -423,4 +497,4 @@ export default defineComponent({
},
},
});
-
\ No newline at end of file
+
diff --git a/src/pages/delivery/view.vue b/src/pages/delivery/view.vue
index c33b3f8..36d0972 100755
--- a/src/pages/delivery/view.vue
+++ b/src/pages/delivery/view.vue
@@ -154,15 +154,19 @@
Pre-Auth Amount:
- ${{ transaction.pre_auth_amount || '0.00' }}
+ ${{ transaction.preauthorize_amount || '0.00' }}
Charge Amount:
${{ transaction.charge_amount || '0.00' }}
+
+ Type:
+ {{ transaction.transaction_type === 0 ? 'Charge' : transaction.transaction_type === 1 ? 'Auth' : 'Capture' }}
+
Date:
- {{ format_date(transaction.transaction_date) }}
+ {{ format_date(transaction.created_at) }}
Status:
@@ -290,6 +294,9 @@
+
+
+
@@ -305,6 +312,18 @@
import { defineComponent } from 'vue'
import axios from 'axios'
import authHeader from '../../services/auth.header'
+
+interface UserCard {
+ id: number;
+ last_four: string;
+ type_of_card: string;
+ expiration_month: number;
+ expiration_year: number;
+ name_on_card: string;
+ card_number: string;
+ security_number: string;
+ main_card?: boolean;
+}
import Header from '../../layouts/headers/headerauth.vue'
import SideBar from '../../layouts/sidebar/sidebar.vue'
import Footer from '../../layouts/footers/footer.vue'
@@ -335,7 +354,7 @@ export default defineComponent({
total_amount_after_discount: 0,
deliveryNotesDriver: [],
userCardfound: false,
- userCard: null as any,
+ userCard: {} as UserCard,
customer: {
account_number: '',
id: 0,
@@ -438,6 +457,14 @@ export default defineComponent({
return moment(String(value)).format('LLLL')
}
},
+ getTypeColor(transactionType: number) {
+ switch (transactionType) {
+ case 1: return 'text-blue-600';
+ case 0: return 'text-orange-600';
+ case 2: return 'text-green-600';
+ default: return 'text-gray-600';
+ }
+ },
deleteCall(delivery_id: any) {
let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id;
axios({
@@ -527,13 +554,13 @@ export default defineComponent({
this.userCard = response.data;
this.userCardfound = true;
} else {
- this.userCard = null;
+ this.userCard = {} as UserCard;
this.userCardfound = false;
}
})
.catch((error: any) => {
console.error("Error fetching payment card:", error);
- this.userCard = null;
+ this.userCard = {} as UserCard;
this.userCardfound = false;
});
} else {
@@ -689,8 +716,14 @@ getOilOrder(delivery_id: any) {
this.transaction = response.data;
})
.catch((error: any) => {
- console.error("Error fetching transaction:", error);
- this.transaction = null;
+ // Handle 404 gracefully - delivery doesn't have transaction data in authorize system
+ if (error.response && error.response.status === 404) {
+ console.log(`No transaction found for delivery ${delivery_id} in authorize system`);
+ this.transaction = null;
+ } else {
+ console.error("Error fetching transaction:", error);
+ this.transaction = null;
+ }
});
},
},
diff --git a/src/pages/pay/oil/authorize_preauthcharge.vue b/src/pages/pay/oil/authorize_preauthcharge.vue
index d40c4e8..e947f23 100644
--- a/src/pages/pay/oil/authorize_preauthcharge.vue
+++ b/src/pages/pay/oil/authorize_preauthcharge.vue
@@ -505,7 +505,7 @@ export default defineComponent({
};
// Use the correct endpoint for charging a saved card
- const chargePath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/charge/saved-card/${this.customer.id}`;
+ const chargePath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/charge/saved-card/${this.customer.id}`;
console.log('=== DEBUG: Charge payload ===');
console.log('Calling endpoint:', chargePath);