removed authorize stuff

This commit is contained in:
2026-01-18 19:04:21 -05:00
parent 3864937906
commit 2257c084d3
10 changed files with 187 additions and 372 deletions

View File

@@ -1,3 +1,80 @@
# Eamco Playground # EAMCO Playground
This is a FastAPI project for Eamco playground application. **EAMCO Playground** is a non-production, sandboxed microservice used for development, testing, and experimentation. It provides a safe and isolated environment for prototyping new features and refining complex algorithms before they are integrated into the core production services.
Think of this as the R&D department's workshop. It often contains experimental or alternative versions of logic found in other services like `eamco_auto_api`.
[![Language](https://img.shields.io/badge/Language-Python-blue)](https://www.python.org/)
[![Framework](https://img.shields.io/badge/Framework-FastAPI-green)](https://fastapi.tiangolo.com/)
[![Database](https://img.shields.io/badge/Database-PostgreSQL-blue)](https://www.postgresql.org/)
---
## Purpose
The primary purpose of the Playground is to facilitate development and testing without affecting live data. Key use cases include:
- **Algorithm Refinement**: Testing new versions of the "K-Factor" (house consumption factor) calculation, as seen in `app/script/update_auto.py`.
- **New Integrations**: Evaluating new third-party services, such as testing the `pyowm` library as an alternative for weather data.
- **API Simulation**: Mimicking the endpoints of other microservices to test interactions and data flow in a controlled environment.
- **Debugging**: Providing a space to replicate and diagnose complex issues found in the production system without the risk of data corruption.
**This service should NOT be deployed to a production environment.**
---
## Key Components
- **Experimental Scripts**: The `app/script/` directory contains experimental logic. For example, `update_auto.py` holds a specific implementation of the K-Factor refinement algorithm.
- **Weather API Integration**: Uses the `pyowm` library to connect to the OpenWeatherMap API, likely for testing purposes.
- **Simulated API Routers**: Includes routers for `/main`, `/delivery`, `/confirm`, and `/info` that mirror the structure of other services, allowing developers to test frontend interactions or service-to-service communication.
---
## Getting Started
### Prerequisites
- Python 3.10+
- A PostgreSQL database (preferably a dedicated development or test instance).
### Installation
1. **Clone the repository and navigate into it.**
2. **Create a virtual environment and install dependencies:**
```bash
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```
3. **Configure your environment:**
The application's configuration is managed by environment variables set in `settings_local.py`, `settings_dev.py`, or `settings_prod.py`. Ensure your database connection URI and any API keys (like for OpenWeatherMap) are set correctly in your chosen settings file.
### Running the Service
This service is intended for development use.
```bash
export MODE=DEVELOPMENT
uvicorn main:app --reload --host 0.0.0.0 --port <your_port>
```
---
## Project Structure
```
eamco_playground/
├── app/
│ ├── models/ # SQLAlchemy ORM models
│ ├── routers/ # API endpoint definitions for simulating other services
│ ├── schema/ # Pydantic models
│ └── script/ # Experimental scripts and algorithms
├── config.py # Logic for loading environment-specific settings
├── database.py # SQLAlchemy engine and session setup
├── main.py # FastAPI application entry point
├── requirements.txt # Python dependencies
└── README.md # This file
```

View File

@@ -1,7 +1,8 @@
from fastapi import APIRouter, Request from fastapi import APIRouter, Request, Depends
from datetime import date from datetime import date
from database import session from sqlalchemy.orm import Session
from database import get_db
from pyowm import OWM from pyowm import OWM
from decimal import Decimal from decimal import Decimal
from app.models.auto import Auto_Delivery, Tickets_Auto_Delivery from app.models.auto import Auto_Delivery, Tickets_Auto_Delivery
@@ -19,14 +20,14 @@ router = APIRouter(
@router.put("/auto/update/{autoid}") @router.put("/auto/update/{autoid}")
async def update_auto(autoid: int, request: Request): async def update_auto(autoid: int, request: Request, db: Session = Depends(get_db)):
request_body = await request.json() request_body = await request.json()
gallons_delivered = request_body['gallons_delivered'] gallons_delivered = request_body['gallons_delivered']
gallons_delivered = Decimal(gallons_delivered) gallons_delivered = Decimal(gallons_delivered)
get_auto_delivery = (session get_auto_delivery = (db
.query(Auto_Delivery) .query(Auto_Delivery)
.filter(Auto_Delivery.id == autoid) .filter(Auto_Delivery.id == autoid)
.first()) .first())
@@ -50,9 +51,9 @@ async def update_auto(autoid: int, request: Request):
get_auto_delivery.auto_status = 1 get_auto_delivery.auto_status = 1
get_auto_delivery.days_since_last_fill = 0 get_auto_delivery.days_since_last_fill = 0
session.add(get_auto_delivery) db.add(get_auto_delivery)
session.commit() db.commit()
return ({"ok": True}), 200 return ({"ok": True}), 200
@@ -60,7 +61,7 @@ async def update_auto(autoid: int, request: Request):
@router.post("/auto/create/{autoid}") @router.post("/auto/create/{autoid}")
async def create_auto_ticket(autoid: int, request: Request): async def create_auto_ticket(autoid: int, request: Request, db: Session = Depends(get_db)):
request_body = await request.json() request_body = await request.json()
@@ -68,11 +69,11 @@ async def create_auto_ticket(autoid: int, request: Request):
gallons_delivered = Decimal(gallons_delivered) gallons_delivered = Decimal(gallons_delivered)
get_auto_delivery = (session get_auto_delivery = (db
.query(Auto_Delivery) .query(Auto_Delivery)
.filter(Auto_Delivery.id == autoid) .filter(Auto_Delivery.id == autoid)
.first()) .first())
get_todays_price = (session.query(Pricing_Oil_Oil) get_todays_price = (db.query(Pricing_Oil_Oil)
.order_by(Pricing_Oil_Oil.id.desc()) .order_by(Pricing_Oil_Oil.id.desc())
.first()) .first())
gallons_put_in_home = Decimal(gallons_delivered) gallons_put_in_home = Decimal(gallons_delivered)
@@ -95,9 +96,9 @@ async def create_auto_ticket(autoid: int, request: Request):
) )
session.add(create_new_ticket) db.add(create_new_ticket)
session.commit() db.commit()
return ({ return ({
"ok": True, "ok": True,

View File

@@ -1,7 +1,8 @@
from fastapi import APIRouter, Request, Depends from fastapi import APIRouter, Request, Depends
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder from fastapi.encoders import jsonable_encoder
from database import session from sqlalchemy.orm import Session
from database import get_db
from app.models.auto import Auto_Delivery, Tickets_Auto_Delivery from app.models.auto import Auto_Delivery, Tickets_Auto_Delivery
from app.models.delivery import Delivery from app.models.delivery import Delivery
@@ -17,9 +18,9 @@ router = APIRouter(
@router.get("/past/{customer_id}", status_code=200) @router.get("/past/{customer_id}", status_code=200)
def past(customer_id: int): def past(customer_id: int, db: Session = Depends(get_db)):
get_customer_past_delivery = (session get_customer_past_delivery = (db
.query(Delivery) .query(Delivery)
.filter(Delivery.customer_id == customer_id) .filter(Delivery.customer_id == customer_id)
.order_by(Delivery.id.desc()) .order_by(Delivery.id.desc())
@@ -30,11 +31,11 @@ def past(customer_id: int):
@router.get("/all/customers", status_code=200) @router.get("/all/customers", status_code=200)
def get_delivery_customers(): def get_delivery_customers(db: Session = Depends(get_db)):
automatics = ( automatics = (
session.query(Auto_Delivery) db.query(Auto_Delivery)
.filter(Auto_Delivery.auto_status == 1) .filter(Auto_Delivery.auto_status == 1)
.order_by(Auto_Delivery.estimated_gallons_left.asc()) .order_by(Auto_Delivery.estimated_gallons_left.asc())
.all() .all()
@@ -45,9 +46,9 @@ def get_delivery_customers():
@router.get("/driver/{driver_employee_id}", status_code=200) @router.get("/driver/{driver_employee_id}", status_code=200)
def get_delivery_for_specific_driver(driver_employee_id: int): def get_delivery_for_specific_driver(driver_employee_id: int, db: Session = Depends(get_db)):
automatics = ( automatics = (
session.query(Delivery) db.query(Delivery)
.filter(Delivery.driver_employee_id == driver_employee_id) .filter(Delivery.driver_employee_id == driver_employee_id)
.filter(Delivery.automatic == 1) .filter(Delivery.automatic == 1)
.filter(Delivery.delivery_status == 0) .filter(Delivery.delivery_status == 0)
@@ -58,10 +59,10 @@ def get_delivery_for_specific_driver(driver_employee_id: int):
@router.get("/{auto_id}", status_code=200) @router.get("/{auto_id}", status_code=200)
def get_auto(auto_id): def get_auto(auto_id, db: Session = Depends(get_db)):
get_delivery = ( get_delivery = (
session.query(Auto_Delivery) db.query(Auto_Delivery)
.filter(Auto_Delivery.id == auto_id) .filter(Auto_Delivery.id == auto_id)
.first() .first()
) )
@@ -71,10 +72,10 @@ def get_auto(auto_id):
@router.get("/delivery/{delivery_id_order}", status_code=200) @router.get("/delivery/{delivery_id_order}", status_code=200)
def get_delivery(delivery_id_order): def get_delivery(delivery_id_order, db: Session = Depends(get_db)):
get_delivery = ( get_delivery = (
session.query(Tickets_Auto_Delivery) db.query(Tickets_Auto_Delivery)
.filter(Tickets_Auto_Delivery.id == delivery_id_order) .filter(Tickets_Auto_Delivery.id == delivery_id_order)
.first() .first()
) )
@@ -83,10 +84,10 @@ def get_delivery(delivery_id_order):
@router.get("/all/profile/{customer_id}", status_code=200) @router.get("/all/profile/{customer_id}", status_code=200)
def get_autos_customers(customer_id): def get_autos_customers(customer_id, db: Session = Depends(get_db)):
get_delivery = ( get_delivery = (
session.query(Tickets_Auto_Delivery) db.query(Tickets_Auto_Delivery)
.filter(Tickets_Auto_Delivery.customer_id == customer_id) .filter(Tickets_Auto_Delivery.customer_id == customer_id)
.order_by(Tickets_Auto_Delivery.id.asc()) .order_by(Tickets_Auto_Delivery.id.asc())
.all() .all()

68
app/routers/info.py Normal file
View File

@@ -0,0 +1,68 @@
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from datetime import datetime
from app.models.customer import Customer_Customer
from app.models.delivery import Delivery
from database import get_db
from sqlalchemy import func
router = APIRouter(
prefix="/info",
tags=["info"],
)
def get_customers_without_delivery_since(db: Session, date_threshold: datetime):
"""
Queries all customers who haven't received a delivery since the specified date.
Args:
db: The database session.
date_threshold: The date threshold. Customers whose last delivery is
before this date (or have no deliveries) will be returned.
Returns:
A tuple containing the count and a list of Customer_Customer objects.
"""
# Subquery to find the last delivery date for each customer
last_delivery_subquery = (
db.query(
Delivery.customer_id,
func.max(Delivery.when_delivered).label("last_delivery_date")
)
.group_by(Delivery.customer_id)
.subquery()
)
# Main query to join customers with their last delivery date
# and filter based on the date threshold
customers = (
db.query(Customer_Customer)
.outerjoin(
last_delivery_subquery,
Customer_Customer.id == last_delivery_subquery.c.customer_id
)
.filter(
(last_delivery_subquery.c.last_delivery_date < date_threshold) |
(last_delivery_subquery.c.last_delivery_date == None)
)
.all()
)
return len(customers), customers
@router.get("/customers/no_delivery_since/{year}/{month}/{day}")
async def customers_no_delivery_since(
year: int,
month: int,
day: int,
db: Session = Depends(get_db)
):
"""
Get customers who have not had a delivery since the specified date.
"""
try:
threshold_date = datetime(year, month, day).date()
count, customers = get_customers_without_delivery_since(db, threshold_date)
return {"customer_count": count, "customers": customers}
except ValueError as e:
return {"error": str(e)}

View File

@@ -1,31 +1,15 @@
## File: app/routers/main.py (or your equivalent path) ## File: app/routers/main.py (or your equivalent path)
from fastapi import APIRouter, HTTPException from fastapi import APIRouter, HTTPException, Depends
from datetime import datetime from datetime import datetime
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from sqlalchemy import func from sqlalchemy import func
# Your existing imports # Your existing imports
from database import session from database import get_db
from pyowm import OWM from pyowm import OWM
from app.models.customer import Customer_Customer
from app.models.cards import Card_Card
# Imports needed for the migration task.
# YOU MUST ADJUST THESE IMPORTS TO MATCH YOUR PROJECT'S ACTUAL STRUCTURE.
from database import session # Assuming your SessionLocal is here for a clean session
from authorizenet import apicontractsv1
from authorizenet.constants import constants
from authorizenet.apicontrollers import (
createCustomerProfileController,
createCustomerPaymentProfileController
)
from config import load_config # Assuming you have this
# Load Authorize.net credentials
ApplicationConfig = load_config()
# --- ROUTER DEFINITION --- # --- ROUTER DEFINITION ---
router = APIRouter( router = APIRouter(
@@ -33,323 +17,3 @@ router = APIRouter(
tags=["main"], tags=["main"],
responses={404: {"description": "Not found"}}, responses={404: {"description": "Not found"}},
) )
if ApplicationConfig.CURRENT_SETTINGS == 'PRODUCTION':
constants.environment = constants.PRODUCTION
VALIDATION_MODE = "liveMode"
API_LOGIN_ID = ApplicationConfig.API_LOGIN_ID
TRANSACTION_KEY = ApplicationConfig.TRANSACTION_KEY
else:
constants.environment = constants.SANDBOX
constants.show_url_on_request = True
VALIDATION_MODE = "testMode"
API_LOGIN_ID = ApplicationConfig.API_LOGIN_ID
TRANSACTION_KEY = ApplicationConfig.TRANSACTION_KEY
state_abbrevs = ['MA', 'RI', 'NH', 'ME', 'VT', 'CT', 'NY']
@router.post("/maintenance/migrate-cards-now")
def run_card_migration_synchronously():
"""
Triggers a one-time, SYNCHRONOUS task to migrate all existing, insecurely
stored credit cards from the `card_card` table to secure Authorize.Net CIM profiles.
This function is self-contained and does not use `crud` or `schemas`.
WARNING: This is a long-running process and the request may time out.
Monitor server logs for completion. This endpoint should be removed after use.
"""
def _create_authnet_profile(customer_obj: Customer_Customer, card_obj: Card_Card):
"""Helper to create a new Authorize.Net Customer Profile."""
merchantAuth = apicontractsv1.merchantAuthenticationType(name=API_LOGIN_ID, transactionKey=TRANSACTION_KEY)
expiration_date_str = f"{card_obj.expiration_year}-{str(card_obj.expiration_month).zfill(2)}"
# Validate expiration
month = None
year = None
if card_obj.expiration_month:
try:
month = int(card_obj.expiration_month)
except:
pass
if card_obj.expiration_year:
try:
year = int(card_obj.expiration_year)
except:
pass
if not year or not month or month < 1 or month > 12 or year < 2000 or year > 2050:
raise ValueError(f"Invalid expiration year={card_obj.expiration_year}, month={card_obj.expiration_month} for card ID {card_obj.id}")
expiration_date_str = f"{year:04d}-{month:02d}"
# // FIX 1: Sanitize card number by removing all non-digit characters
sanitized_card_number = ''.join(filter(str.isdigit, str(card_obj.card_number)))
if len(sanitized_card_number) < 13 or len(sanitized_card_number) > 19 or not sanitized_card_number.isdigit():
raise ValueError(f"Invalid card number length {len(sanitized_card_number)} or non-numeric for card ID {card_obj.id}")
# Sanitize CVV
cardCode = str(card_obj.security_number).strip()
if len(cardCode) < 3 or len(cardCode) > 4 or not cardCode.isdigit():
raise ValueError(f"Invalid CVV length {len(cardCode)} or non-numeric for card ID {card_obj.id}")
# Get dynamic state
the_state = state_abbrevs[customer_obj.customer_state] if 0 <= customer_obj.customer_state < len(state_abbrevs) else 'MA'
# Debug print
print(f"DEBUG SEND CREATE: cardNumber='{sanitized_card_number}', expirationDate='{expiration_date_str}', cardCode='{cardCode}', customerID={customer_obj.id}, name='{customer_obj.customer_first_name} {customer_obj.customer_last_name}', address='{customer_obj.customer_address}', city='{customer_obj.customer_town}', state='{the_state}', zip='{customer_obj.customer_zip}', country='USA', phone='{customer_obj.customer_phone_number}', email='{customer_obj.customer_email}'")
creditCard = apicontractsv1.creditCardType(
cardNumber=sanitized_card_number,
expirationDate=expiration_date_str,
cardCode=cardCode
)
billTo = apicontractsv1.customerAddressType(
firstName=customer_obj.customer_first_name,
lastName=customer_obj.customer_last_name,
address=customer_obj.customer_address,
city=customer_obj.customer_town,
state=the_state,
zip=customer_obj.customer_zip,
country="USA",
phoneNumber=customer_obj.customer_phone_number
)
paymentProfile = apicontractsv1.customerPaymentProfileType(
billTo=billTo,
payment=apicontractsv1.paymentType(creditCard=creditCard),
defaultPaymentProfile=True
)
customerProfile = apicontractsv1.customerProfileType(
merchantCustomerId=str(customer_obj.id),
email=customer_obj.customer_email,
paymentProfiles=[paymentProfile]
)
request = apicontractsv1.createCustomerProfileRequest(
merchantAuthentication=merchantAuth,
profile=customerProfile,
validationMode="testMode"
)
controller = createCustomerProfileController(request)
if ApplicationConfig.CURRENT_SETTINGS == 'PRODUCTION':
controller.setenvironment(constants.PRODUCTION)
controller.execute()
elif ApplicationConfig.CURRENT_SETTINGS == 'LOCAL':
controller.setenvironment(constants.PRODUCTION)
controller.execute()
else:
controller.execute()
response = controller.getresponse()
if response.messages.resultCode == "Ok":
profile_id = response.customerProfileId if hasattr(response, 'customerProfileId') else None
payment_ids = response.customerPaymentProfileIdList.numericString if hasattr(response, 'customerPaymentProfileIdList') and hasattr(response.customerPaymentProfileIdList, 'numericString') else []
payment_id = payment_ids[0] if payment_ids else None
return str(profile_id), str(payment_id) if payment_id else None
else:
# Handle errors
error_msg = "Unknown Authorize.Net Error"
if response is not None and hasattr(response, 'messages') and response.messages is not None and hasattr(response.messages, 'message') and response.messages.message:
msg = response.messages.message[0] if isinstance(response.messages.message, list) else response.messages.message
if msg and hasattr(msg, 'text') and msg.text is not None:
if isinstance(msg.text, str):
error_msg = msg.text
else:
# Handle case where text might be an object with 'text' attribute
error_msg = msg.text.text if hasattr(msg.text, 'text') else str(msg.text)
print(f" AUTH.NET ERROR: {error_msg}")
return None, None
def _add_authnet_payment_profile(profile_id: str, customer_obj: Customer_Customer, card_obj: Card_Card):
"""Helper to add a new Payment Profile to an existing Customer Profile."""
merchantAuth = apicontractsv1.merchantAuthenticationType(name=API_LOGIN_ID, transactionKey=TRANSACTION_KEY)
expiration_date_str = f"{card_obj.expiration_year}-{str(card_obj.expiration_month).zfill(2)}"
# State abbreviations list
# // FIX 1 (Applied here as well): Sanitize card number
sanitized_card_number = ''.join(filter(str.isdigit, str(card_obj.card_number)))
creditCard = apicontractsv1.creditCardType(
cardNumber=sanitized_card_number,
expirationDate=expiration_date_str,
cardCode=str(card_obj.security_number).strip()
)
# Get dynamic state
the_state = state_abbrevs[customer_obj.customer_state] if 0 <= customer_obj.customer_state < len(state_abbrevs) else 'MA'
billTo = apicontractsv1.customerAddressType(
firstName=customer_obj.customer_first_name,
lastName=customer_obj.customer_last_name,
address=customer_obj.customer_address,
city=customer_obj.customer_town,
state=the_state,
zip=customer_obj.customer_zip,
country="USA",
phoneNumber=customer_obj.customer_phone_number
)
paymentProfile = apicontractsv1.customerPaymentProfileType(
billTo=billTo,
payment=apicontractsv1.paymentType(creditCard=creditCard),
defaultPaymentProfile=False
)
request = apicontractsv1.createCustomerPaymentProfileRequest(
merchantAuthentication=merchantAuth,
customerProfileId=profile_id,
paymentProfile=paymentProfile,
validationMode="testMode"
)
controller = createCustomerPaymentProfileController(request)
if ApplicationConfig.CURRENT_SETTINGS == 'PRODUCTION':
controller.setenvironment(constants.PRODUCTION)
controller.execute()
elif ApplicationConfig.CURRENT_SETTINGS == 'LOCAL':
controller.setenvironment(constants.PRODUCTION)
controller.execute()
else:
controller.execute()
response = controller.getresponse()
if response.messages.resultCode == "Ok":
return str(response.customerPaymentProfileId)
else:
# // FIX 2 (Applied here as well): Robust error message parsing
error_msg = "Unknown Authorize.Net Error"
if response is not None and hasattr(response, 'messages') and response.messages is not None and hasattr(response.messages, 'message') and response.messages.message:
msg = response.messages.message[0]
if hasattr(msg, 'text') and msg.text is not None:
error_msg = msg.text.text
print(f" AUTH.NET ERROR: {error_msg}")
return None
# --- MIGRATION SCRIPT LOGIC ---
print("="*60)
print("MIGRATION STARTED: Migrating all customer cards to Authorize.Net CIM.")
print(f"Start Time: {datetime.now()}")
print("="*60)
customers_processed = 0
cards_migrated = 0
error_count = 0
try:
customers_to_migrate = (session
.query(Customer_Customer)
.filter(Customer_Customer.auth_net_profile_id == None)
.all())
total_customers = len(customers_to_migrate)
print(f"Found {total_customers} customers to migrate.")
for index, customer in enumerate(customers_to_migrate):
customers_processed += 1
print(f"\n--- Processing Customer {index + 1}/{total_customers} (ID: {customer.id}, Name: {customer.customer_first_name}) ---")
insecure_cards = (session
.query(Card_Card)
.filter(Card_Card.user_id == customer.id)
.all())
print(f"DEBUG: Customer {customer.id} has {len(insecure_cards)} cards")
customer_profile_id_for_session = None
# # Check for existing profile
# existing_profile, existing_payment_ids = _get_existing_profile(str(customer.id))
# if existing_profile and existing_payment_ids:
# print(f" -> Found existing Authorize.Net profile ID {existing_profile}")
# customer.auth_net_profile_id = existing_profile
# session.add(customer)
# session.commit()
# for i, card_data in enumerate(insecure_cards):
# if i < len(existing_payment_ids):
# display_card_number = ''.join(filter(str.isdigit, str(card_data.card_number)))
# if not display_card_number:
# print(f" SKIPPING CARD: Card number is empty after sanitization for card ID {card_data.id}")
# error_count += 1
# continue
# card_data.auth_net_payment_profile_id = existing_payment_ids[i]
# session.add(card_data)
# session.commit()
# print(f" SUCCESS: Mapped existing Payment ID {existing_payment_ids[i]} to card ending ...{display_card_number[-4:]}")
# cards_migrated += 1
# else:
# print(f" WARNING: No existing payment profile Kawasaki for card {card_data.id}")
# error_count += 1
# continue # skip creation loop
for i, card_data in enumerate(insecure_cards):
try:
# Basic data validation before sending
if not card_data.card_number or not card_data.expiration_year or not card_data.expiration_month:
print(f" SKIPPING CARD: Incomplete card data for card ID {card_data.id}.")
error_count += 1
continue
print(f"DEBUG RAW: Card ID {card_data.id}: number='{card_data.card_number}', year='{card_data.expiration_year}', month='{card_data.expiration_month}', cvv='{card_data.security_number}', name='{customer.customer_first_name} {customer.customer_last_name}'")
# Sanitize card number for display
display_card_number = ''.join(filter(str.isdigit, str(card_data.card_number)))
if not display_card_number:
print(f" SKIPPING CARD: Card number is empty after sanitization for card ID {card_data.id}.")
error_count += 1
continue
if i == 0:
print(f" -> Creating new CIM profile with card ending in ...{display_card_number[-4:]}")
profile_id, payment_id = _create_authnet_profile(customer, card_data)
if profile_id and payment_id:
customer.auth_net_profile_id = profile_id
session.add(customer)
customer_profile_id_for_session = profile_id
card_data.auth_net_payment_profile_id = payment_id
session.add(card_data)
session.commit()
print(f" SUCCESS: Created Profile ID {profile_id} and Payment ID {payment_id}")
cards_migrated += 1
else:
print(f" ERROR: Failed to create profile for customer {customer.id}.")
error_count += 1
break
else:
if not display_card_number:
print(f" SKIPPING CARD: Card number is empty after sanitization for card ID {card_data.id}.")
error_count += 1
continue
print(f" -> Adding additional card ending in ...{display_card_number[-4:]}")
payment_id = _add_authnet_payment_profile(customer_profile_id_for_session, customer, card_data)
if payment_id:
card_data.auth_net_payment_profile_id = payment_id
session.add(card_data)
session.commit()
print(f" SUCCESS: Added Payment ID {payment_id}")
cards_migrated += 1
else:
print(f" ERROR: Failed to add additional card for customer {customer.id}.")
error_count += 1
except Exception as e:
print(f" CRITICAL ERROR processing a card for customer {customer.id}: {str(e)}")
error_count += 1
session.rollback()
except Exception as e:
print(f"A critical error occurred during the migration process: {e}")
session.rollback()
return {"ok": False, "error": f"Migration failed with a critical error: {e}"}, 500
finally:
print("="*60)
print("MIGRATION FINISHED!")
print(f"End Time: {datetime.now()}")
print(f"Summary: Processed {customers_processed} customers, migrated {cards_migrated} cards, encountered {error_count} errors.")
print("="*60)
return {
"ok": True,
"message": "Migration process completed successfully.",
"customers_processed": customers_processed,
"cards_migrated": cards_migrated,
"errors": error_count,
}, 200

View File

@@ -1,5 +1,5 @@
from decimal import Decimal from decimal import Decimal
from database import session from database import Session

View File

@@ -22,7 +22,13 @@ url = URL.create(
engine = create_engine(url) engine = create_engine(url)
Session = sessionmaker(autocommit=False, autoflush=False, bind=engine) Session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
session = Session()
Base = declarative_base() Base = declarative_base()
Base.metadata.create_all(engine) Base.metadata.create_all(engine)
def get_db():
db = Session()
try:
yield db
finally:
db.close()

View File

@@ -1,5 +1,5 @@
from fastapi import FastAPI from fastapi import FastAPI
from app.routers import main, delivery, confirm from app.routers import main, delivery, confirm, info
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
@@ -9,6 +9,7 @@ app = FastAPI()
app.include_router(main.router) app.include_router(main.router)
app.include_router(delivery.router) app.include_router(delivery.router)
app.include_router(confirm.router) app.include_router(confirm.router)
app.include_router(info.router)
origins = [ origins = [
"http://localhost:9000", "http://localhost:9000",

View File

@@ -4,4 +4,3 @@ psycopg2-binary
sqlalchemy sqlalchemy
pyowm pyowm
setuptools setuptools
authorizenet

View File

@@ -30,5 +30,3 @@ class ApplicationConfig:
"http://localhost:9516", # Authorize service port "http://localhost:9516", # Authorize service port
] ]
API_LOGIN_ID = '9U6w96gZmX'
TRANSACTION_KEY = '94s6Qy458mMNJr7G'