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
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:
@@ -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)
|
||||
|
||||
|
@@ -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],
|
||||
|
49
backend/src/payments/payment_routes.py
Normal file
49
backend/src/payments/payment_routes.py
Normal 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))
|
87
backend/src/payments/paypal.py
Normal file
87
backend/src/payments/paypal.py
Normal 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()
|
53
backend/src/payments/region_access.py
Normal file
53
backend/src/payments/region_access.py
Normal 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."
|
||||
)
|
||||
|
Reference in New Issue
Block a user