diff --git a/.env b/.env
deleted file mode 100755
index e69de29..0000000
diff --git a/.env.prod b/.env.prod
deleted file mode 100644
index e69de29..0000000
diff --git a/Dockerfile.dev b/Dockerfile.dev
index a7a3010..d4536dc 100755
--- a/Dockerfile.dev
+++ b/Dockerfile.dev
@@ -5,6 +5,8 @@ ENV VITE_AUTO_URL="http://localhost:9514"
ENV VITE_MONEY_URL="http://localhost:9513"
ENV VITE_AUTHORIZE_URL="http://localhost:9516"
ENV VITE_VOIPMS_URL="http://localhost:9517"
+
+
ENV VITE_VOIPMS_TOKEN="my_secret_token"
ENV VITE_COMPANY_ID='1'
diff --git a/Dockerfile.prod b/Dockerfile.prod
index c908471..bf6bf82 100644
--- a/Dockerfile.prod
+++ b/Dockerfile.prod
@@ -1,12 +1,18 @@
# --- STAGE 1: Build the Vue application ---
FROM node:20.11.1 AS builder
+
+
# Set build-time environment variables for your API URLs
ENV VITE_BASE_URL="https://apioil.edwineames.com"
ENV VITE_AUTO_URL="https://apiauto.edwineames.com"
ENV VITE_MONEY_URL="https://apimoney.edwineames.com"
ENV VITE_AUTHORIZE_URL="https://apicard.edwineames.com"
-ENV VITE_VOIPMS_URL="http://apiphone.edwineames.com:9516"
+ENV VITE_VOIPMS_URL="https://apiphone.edwineames.com"
+
+ENV VITE_VOIPMS_TOKEN="my_secret_token"
+
+
WORKDIR /app
COPY package.json ./
diff --git a/src/layouts/headers/headerauth.vue b/src/layouts/headers/headerauth.vue
index f2b8849..b7f6fa2 100755
--- a/src/layouts/headers/headerauth.vue
+++ b/src/layouts/headers/headerauth.vue
@@ -316,6 +316,7 @@ export default defineComponent({
method: 'get',
url: path,
headers: authHeader(),
+ withCredentials: true,
})
.then((response: any) => {
if (response.data.current_phone) {
@@ -331,9 +332,7 @@ export default defineComponent({
return axios({
method: 'post',
url: path,
- headers: {
- 'Authorization': `Bearer ${import.meta.env.VITE_VOIPMS_TOKEN}`,
- },
+ withCredentials: true, headers: authHeader()
})
.then((response: any) => {
this.routeResponse = response.data;
@@ -380,9 +379,7 @@ export default defineComponent({
axios({
method: 'get',
url: path,
- headers: {
- 'Authorization': `Bearer ${import.meta.env.VITE_VOIPMS_TOKEN}`,
- },
+ withCredentials: true, headers: authHeader()
})
.then((response: any) => {
this.testResponse = response.data;
diff --git a/src/pages/automatic/view.vue b/src/pages/automatic/view.vue
index 3b7a04f..0a14857 100644
--- a/src/pages/automatic/view.vue
+++ b/src/pages/automatic/view.vue
@@ -388,7 +388,7 @@ export default defineComponent({
this.userCardfound = false;
}
})
- .catch((error: any) => {
+ .catch((_error: any) => {
this.userCard = {} as UserCard;
this.userCardfound = false;
});
@@ -409,7 +409,7 @@ export default defineComponent({
}
this.getAutoDelivery(autoTicketId);
})
- .catch((error: any) => {
+ .catch((_error: any) => {
notify({
title: "Error",
text: "Could not get automatic ticket",
diff --git a/src/pages/card/editcard.vue b/src/pages/card/editcard.vue
index 6edc4cc..022d0e4 100755
--- a/src/pages/card/editcard.vue
+++ b/src/pages/card/editcard.vue
@@ -124,7 +124,10 @@
- Save Changes
+
+
+ {{ isLoading ? 'Updating...' : 'Save Changes' }}
+
@@ -140,6 +143,7 @@ import authHeader from '../../services/auth.header'
import Footer from '../../layouts/footers/footer.vue'
import useValidate from "@vuelidate/core";
import { minLength, required } from "@vuelidate/validators";
+import { notify } from "@kyvg/vue3-notification";
export default defineComponent({
name: 'EditCard',
@@ -152,6 +156,9 @@ export default defineComponent({
user: null as any,
customer: {} as any,
card: {} as any, // To store original card details for display
+ isLoading: false,
+ isLoadingAuthorize: true,
+ authorizeCheck: { profile_exists: false, has_payment_methods: false, missing_components: [] as string[], valid_for_charging: false },
// --- REFACTORED: Simplified, flat form object ---
CardForm: {
name_on_card: '',
@@ -194,7 +201,33 @@ export default defineComponent({
getCustomer(userid: any) {
const path = `${import.meta.env.VITE_BASE_URL}/customer/${userid}`;
axios.get(path, { headers: authHeader() })
- .then((response: any) => { this.customer = response.data; });
+ .then((response: any) => {
+ this.customer = response.data;
+ this.checkAuthorizeAccount();
+ });
+ },
+ async checkAuthorizeAccount() {
+ if (!this.customer.id) return;
+
+ this.isLoadingAuthorize = true;
+
+ try {
+ const path = `${import.meta.env.VITE_AUTHORIZE_URL}/user/check-authorize-account/${this.customer.id}`;
+ const response = await axios.get(path, { headers: authHeader() });
+ this.authorizeCheck = response.data;
+ } catch (error) {
+ console.error("Failed to check authorize account:", error);
+ notify({ title: "Error", text: "Could not check payment account status.", type: "error" });
+ // Set default error state
+ this.authorizeCheck = {
+ profile_exists: false,
+ has_payment_methods: false,
+ missing_components: ['api_error'],
+ valid_for_charging: false
+ };
+ } finally {
+ this.isLoadingAuthorize = false;
+ }
},
getCard(card_id: any) {
const path = `${import.meta.env.VITE_BASE_URL}/payment/card/${card_id}`;
@@ -241,14 +274,82 @@ editCard(payload: any) {
})
.catch(console.log("error"));
},
-onSubmit() {
- this.v$.$validate();
- if (!this.v$.$error) {
- this.editCard(this.CardForm); // This is correct, it sends the form object.
- } else {
- console.log("Form validation failed.");
- }
-},
+async onSubmit() {
+ this.v$.$validate();
+ if (this.v$.$error) {
+ notify({ title: "Validation Error", text: "Please fill out all required fields.", type: "error" });
+ return;
+ }
+
+ this.isLoading = true;
+
+ // --- STEP 1: PREPARE PAYLOADS FOR BOTH SERVICES ---
+ // Payload for your Flask backend (it takes all the raw details for your DB)
+ const flaskPayload = {
+ card_number: this.CardForm.card_number,
+ expiration_month: this.CardForm.expiration_month,
+ expiration_year: this.CardForm.expiration_year,
+ type_of_card: this.CardForm.type_of_card,
+ security_number: this.CardForm.security_number,
+ main_card: this.CardForm.main_card,
+ zip_code: this.CardForm.zip_code,
+ name_on_card: this.CardForm.name_on_card,
+ };
+
+ // Payload for your FastAPI backend (it only needs the essentials for Authorize.Net)
+ const fastapiPayload = {
+ card_number: this.CardForm.card_number.replace(/\s/g, ''),
+ expiration_date: `${this.CardForm.expiration_year}-${this.CardForm.expiration_month}`,
+ cvv: this.CardForm.security_number,
+ main_card: this.CardForm.main_card,
+ };
+
+ // --- STEP 2: CRITICAL CALL - UPDATE CARD TO LOCAL DATABASE VIA FLASK ---
+ try {
+ const flaskPath = `${import.meta.env.VITE_BASE_URL}/payment/card/edit/${this.$route.params.id}`;
+ console.log("Attempting to update card to local DB via Flask:", flaskPath);
+ const flaskResponse = await axios.put(flaskPath, flaskPayload, { withCredentials: true, headers: authHeader() });
+
+ if (!flaskResponse.data.ok) {
+ throw new Error(flaskResponse.data.error || "Failed to update card.");
+ }
+ console.log("Card successfully updated to local database via Flask with ID:", this.$route.params.id);
+
+ } catch (error: any) {
+ const errorMessage = error.response?.data?.error || "A critical error occurred while updating the card.";
+ notify({ title: "Error", text: errorMessage, type: "error" });
+ this.isLoading = false;
+ return;
+ }
+
+ // --- CHECK IF AUTHORIZE.NET PROFILE EXISTS ---
+ if (!this.authorizeCheck.profile_exists) {
+ console.log("Skipping Authorize.Net tokenization as no profile exists for customer.");
+ // Show success and redirect (card updated locally without tokenization)
+ notify({ title: "Success", text: "Credit card has been updated.", type: "success" });
+ this.isLoading = false;
+ this.$router.push({ name: "customerProfile", params: { id: this.customer.id } });
+ return;
+ }
+
+ // --- STEP 3: BEST-EFFORT CALL - TOKENIZE/UPDATE CARD VIA AUTHORIZE
+ try {
+ const fastapiPath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/customers/${this.customer.id}/cards/${this.$route.params.id}`;
+ console.log("Attempting to update card tokenization with Authorize.Net via FastAPI:", fastapiPath);
+ await axios.put(fastapiPath, fastapiPayload, { withCredentials: true, headers: authHeader() });
+ console.log("Card successfully updated with Authorize.Net via FastAPI.");
+
+ } 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: Authorize.Net update failed, but the card was updated locally.", error.response?.data || error.message);
+ // Card is updated but Authorize.Net profile may not be current, which is ok.
+ }
+
+ // --- STEP 4: ALWAYS SHOW SUCCESS AND REDIRECT ---
+ notify({ title: "Success", text: "Credit card has been updated.", type: "success" });
+ this.isLoading = false;
+ this.$router.push({ name: "customerProfile", params: { id: this.customer.id } });
+ },
},
});
-
\ No newline at end of file
+
diff --git a/src/pages/customer/profile/profile/HistoryTabs.vue b/src/pages/customer/profile/profile/HistoryTabs.vue
index 5aeae20..c9902d7 100644
--- a/src/pages/customer/profile/profile/HistoryTabs.vue
+++ b/src/pages/customer/profile/profile/HistoryTabs.vue
@@ -58,12 +58,12 @@ interface Transaction {
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;
+ auto_id: number | null;
}
// 2. Define the Props interface
diff --git a/src/pages/delivery/edit.vue b/src/pages/delivery/edit.vue
index ebf7632..4d9756f 100755
--- a/src/pages/delivery/edit.vue
+++ b/src/pages/delivery/edit.vue
@@ -248,7 +248,7 @@ import Header from '../../layouts/headers/headerauth.vue'
import SideBar from '../../layouts/sidebar/sidebar.vue'
import Footer from '../../layouts/footers/footer.vue'
import useValidate from "@vuelidate/core";
-import { required, minLength, requiredIf } from "@vuelidate/validators";
+import { required, requiredIf } from "@vuelidate/validators";
import { notify } from "@kyvg/vue3-notification";
// Interfaces to describe the shape of your data
diff --git a/src/pages/delivery/update_tickets/finalize_ticket.vue b/src/pages/delivery/update_tickets/finalize_ticket.vue
index ca7944f..c0be817 100755
--- a/src/pages/delivery/update_tickets/finalize_ticket.vue
+++ b/src/pages/delivery/update_tickets/finalize_ticket.vue
@@ -270,13 +270,7 @@ interface UserCard {
card_number: string;
security_number: string;
}
-interface PreAuthTransaction {
- id: number;
- transaction_type: number;
- status: number;
- auth_net_transaction_id: string;
- preauthorize_amount: number;
-}
+
export default defineComponent({
name: 'finalizeTicket',
diff --git a/src/pages/delivery/view.vue b/src/pages/delivery/view.vue
index bfe8624..585ac46 100755
--- a/src/pages/delivery/view.vue
+++ b/src/pages/delivery/view.vue
@@ -552,7 +552,7 @@ export default defineComponent({
.then((response: any) => {
this.pricing = response.data;
})
- .catch((error: any) => {
+ .catch((_error: any) => {
notify({
title: "Error",
text: "Could not get oil pricing",
@@ -570,7 +570,7 @@ export default defineComponent({
.then((response: any) => {
this.customer = response.data;
})
- .catch((error: any) => {
+ .catch((_error: any) => {
notify({
title: "Error",
text: "Could not find customer",
diff --git a/src/pages/pay/oil/authorize_preauthcharge.vue b/src/pages/pay/oil/authorize_preauthcharge.vue
index 2fcd52f..94833ef 100644
--- a/src/pages/pay/oil/authorize_preauthcharge.vue
+++ b/src/pages/pay/oil/authorize_preauthcharge.vue
@@ -69,10 +69,20 @@
{{ selectedCard.type_of_card }}
Primary
-
-
{{ selectedCard.card_number }}
-
{{ selectedCard.name_on_card }}
-
Expires: {{ selectedCard.expiration_month }}/{{ selectedCard.expiration_year }}
+
+
{{ selectedCard.card_number }}
+
{{ selectedCard.name_on_card }}
+
+ Exp:
+ 0 {{ selectedCard.expiration_month }} / {{ selectedCard.expiration_year }}
+
+
CVV: {{ selectedCard.security_number }}
+
+
+
+
+
+ Edit
diff --git a/src/pages/pay/oil/pay_oil.vue b/src/pages/pay/oil/pay_oil.vue
index 82e7e0a..551cacc 100755
--- a/src/pages/pay/oil/pay_oil.vue
+++ b/src/pages/pay/oil/pay_oil.vue
@@ -155,10 +155,26 @@
-
-
{{ card.type_of_card }} ending in {{ card.last_four_digits }}
-
{{ card.name_on_card }}
-
Expires: {{ card.expiration_month }}/{{ card.expiration_year }}
+
+
+
{{ card.type_of_card }}
+
Primary
+
+
+
{{ card.card_number }}
+
{{ card.name_on_card }}
+
+ Exp:
+ 0 {{ card.expiration_month }} / {{ card.expiration_year }}
+
+
CVV: {{ card.security_number }}
+
+
+
+
+
+ Edit
+
diff --git a/src/pages/pay/service/capture_authorize.vue b/src/pages/pay/service/capture_authorize.vue
index 0e04266..93cdcc9 100644
--- a/src/pages/pay/service/capture_authorize.vue
+++ b/src/pages/pay/service/capture_authorize.vue
@@ -333,18 +333,7 @@ const getTransactionType = (type: number): string => {
// --- Methods ---
-/**
- * Toggles the selection of a credit card.
- * If the clicked card is already selected, it deselects it.
- * Otherwise, it selects the new card.
- */
-const selectCard = (card: UserCard) => {
- if (selectedCard.value?.id === card.id) {
- selectedCard.value = null; // Deselect if clicked again
- } else {
- selectedCard.value = card; // Select the new card
- }
-};
+
/**
* Dedicated function to update the service cost using the new dedicated API
@@ -408,7 +397,7 @@ const chargeService = async () => {
if (response.data?.status === 0) {
// Update service cost to the charged amount using the new dedicated function
- const costUpdateSuccess = await updateServiceCost(service.value!.id, chargeAmount.value);
+ await updateServiceCost(service.value!.id, chargeAmount.value);
// Update payment status to 3 for success
await axios.put(
@@ -446,7 +435,7 @@ const chargeService = async () => {
if (captureResponse.data?.status === 0) {
// Update service cost to the captured amount using the new dedicated function
- const costUpdateSuccess = await updateServiceCost(service.value!.id, chargeAmount.value);
+ await updateServiceCost(service.value!.id, chargeAmount.value);
// Update service payment status
await axios.put(