starting to implement paywall logic
Some checks failed
Run testing on the backend code / Build (pull_request) Has been cancelled
Build and deploy the backend to staging / Deploy to staging (pull_request) Has been cancelled
Build and deploy the backend to staging / Build and push image (pull_request) Has been cancelled
Run linting on the backend code / Build (pull_request) Has been cancelled

This commit is contained in:
2025-02-11 17:19:03 +01:00
parent c15e257dea
commit 3a9ef4e7d3
7 changed files with 882 additions and 64 deletions

View File

@@ -12,6 +12,14 @@ LANDMARK_PARAMETERS_PATH = PARAMETERS_DIR / 'landmark_parameters.yaml'
OPTIMIZER_PARAMETERS_PATH = PARAMETERS_DIR / 'optimizer_parameters.yaml'
PAYPAL_CLIENT_ID = os.getenv("your-paypal-client-id")
PAYPAL_SECRET = os.getenv("your-paypal-secret")
PAYPAL_API_URL = "https://api-m.sandbox.paypal.com"
SUPABASE_URL = os.getenv("your-supabase-url")
SUPABASE_KEY = os.getenv("your-supabase-api-key")
cache_dir_string = os.getenv('OSM_CACHE_DIR', './cache')
OSM_CACHE_DIR = Path(cache_dir_string)

View File

@@ -16,6 +16,7 @@ from .optimization.optimizer import Optimizer
from .optimization.refiner import Refiner
from .overpass.overpass import fill_cache
from .cache import client as cache_client
from .payments.payment_routes import router as payment_router
logger = logging.getLogger(__name__)
@@ -37,6 +38,10 @@ async def lifespan(app: FastAPI):
app = FastAPI(lifespan=lifespan)
# Include the payment routes
app.include_router(payment_router, prefix="/payments")
@app.post("/trip/new")
def new_trip(preferences: Preferences,
start: tuple[float, float],

View File

@@ -0,0 +1,49 @@
from os import getenv
from fastapi import APIRouter, HTTPException
from .paypal import create_paypal_order, capture_paypal_order
from supabase import create_client, Client
from ..constants import SUPABASE_URL, SUPABASE_KEY
# Initialize Supabase
supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
# Define router
router = APIRouter()
@router.post("/create-paypal-payment/")
def create_payment(amount: float, currency: str = "USD"):
"""
Create a PayPal payment and return approval URL.
"""
try:
payment = create_paypal_order(amount, currency)
return {"approval_url": payment["approval_url"]}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.post("/capture-paypal-payment/")
def capture_payment(order_id: str, user_id: str, country: str):
"""
Capture the PayPal payment and unlock the country in Supabase.
"""
try:
# Capture payment
capture_response = capture_paypal_order(order_id)
if capture_response.get("status") == "COMPLETED":
# Update Supabase to unlock the country for the user
supabase.table("unlocked_countries").insert({
"user_id": user_id,
"country": country,
"paid": True,
}).execute()
return {"status": "Payment captured and country unlocked successfully"}
else:
raise HTTPException(status_code=400, detail="Payment not completed")
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

View File

@@ -0,0 +1,87 @@
from requests import post
from fastapi import HTTPException
from ..constants import PAYPAL_API_URL, PAYPAL_CLIENT_ID, PAYPAL_SECRET
def get_paypal_access_token():
"""
Get an access token from PayPal to authenticate API requests.
"""
response = post(
f"{PAYPAL_API_URL}/v1/oauth2/token",
headers={
"Accept": "application/json",
"Accept-Language": "en_US",
},
data={"grant_type": "client_credentials"},
auth=(PAYPAL_CLIENT_ID, PAYPAL_SECRET),
)
if response.status_code != 200:
raise HTTPException(status_code=response.status_code, detail="Failed to get PayPal access token")
return response.json()["access_token"]
def create_paypal_order(amount: float, currency: str = "USD"):
"""
Create a PayPal payment order.
"""
access_token = get_paypal_access_token()
order_data = {
"intent": "CAPTURE",
"purchase_units": [
{
"amount": {
"currency_code": currency,
"value": f"{amount:.2f}",
}
}
],
"application_context": {
"return_url": "https://placeholderlink.com/payment-success",
"cancel_url": "https://placeholderlink.com/payment-cancelled",
}
}
response = post(
f"{PAYPAL_API_URL}/v2/checkout/orders",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}",
},
json=order_data,
)
if response.status_code != 201:
raise HTTPException(status_code=response.status_code, detail="Failed to create PayPal order")
order = response.json()
approval_url = next(link['href'] for link in order['links'] if link['rel'] == 'approve')
return {
"approval_url": approval_url,
"order_id": order["id"],
}
def capture_paypal_order(order_id: str):
"""
Capture the PayPal payment order after user approval.
"""
access_token = get_paypal_access_token()
response = post(
f"{PAYPAL_API_URL}/v2/checkout/orders/{order_id}/capture",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}",
},
)
if response.status_code != 201:
raise HTTPException(status_code=response.status_code, detail="Failed to capture PayPal payment")
return response.json()

View File

@@ -0,0 +1,53 @@
from fastapi import HTTPException, status
from supabase import create_client, Client
from ..constants import SUPABASE_URL, SUPABASE_KEY
# Initialize Supabase client
supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
def is_loc_in_country(location: tuple[float, float], country: str) -> bool:
"""
TODO: needs to be implemented.
"""
pass
def check_region_access(user_id: str, start_location: tuple[float, float]) -> None:
"""
Checks if the user has access to the region where `start_location` is located.
Args:
user_id (str): The ID of the current user.
start_location (tuple): The starting location of the trip (lat, lon).
Raises:
HTTPException: If the region is locked and the user has not unlocked it.
"""
# Define the locked countries (regions that require payment)
locked_countries = ["FR", "DE", "ES"] # Example list of locked countries. Needs to be adapted also.
# Iterate over locked countries and check if the start location is in a locked region
for country in locked_countries:
if is_loc_in_country(start_location, country):
# Query Supabase to check if the user has unlocked this country
response = supabase \
.from_("unlocked_countries") \
.select(f"{country}") \
.eq("user_id", user_id) \
.single() \
.execute()
# Check if the country is unlocked (True) or locked (False or missing)
if response.data and response.data.get(country) is True:
# Country is unlocked; continue
continue
else:
# Raise an exception if the country is not unlocked
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=f"Access to the region {country} is locked. Please unlock it to continue."
)