integrated supabase in payment process
Some checks failed
Build and deploy the backend to staging / Build and push image (pull_request) Successful in 1m13s
Run linting on the backend code / Build (pull_request) Successful in 3m9s
Run testing on the backend code / Build (pull_request) Failing after 2m32s
Build and deploy the backend to staging / Deploy to staging (pull_request) Failing after 35s

This commit is contained in:
2025-10-09 14:31:56 +02:00
parent 29ac462725
commit 54f541382e
6 changed files with 182 additions and 41 deletions

View File

@@ -1,20 +1,22 @@
import json
import logging
from typing import Literal
from datetime import datetime, timedelta
import logging
import json
from pydantic import BaseModel, Field, field_validator
import requests
from pydantic import BaseModel, Field, field_validator
from ..configuration.environment import Environment
from ..cache import CreditCache, make_credit_cache_key
# Define the base URL, might move that to toml file
BASE_URL = 'https://api-m.sandbox.paypal.com'
# Intialize the logger
logger = logging.getLogger(__name__)
# Define the base URL, might move that to toml file
BASE_URL_PROD = 'https://api-m.paypal.com'
BASE_URL_SANDBOX = 'https://api-m.sandbox.paypal.com'
class BasketItem(BaseModel):
"""
@@ -42,7 +44,7 @@ class Item(BaseModel):
name: str
description: str
unit_price: float
anyway_credits: int
unit_credits: int
def item_from_sql(item_id: str):
@@ -60,8 +62,8 @@ def item_from_sql(item_id: str):
id = '12345678',
name = 'test_item',
description = 'lorem ipsum',
unit_price = 420,
anyway_credits = 99
unit_price = 0.1,
unit_credits = 5
)
@@ -84,7 +86,8 @@ class OrderRequest(BaseModel):
created_at: datetime = Field(default_factory=datetime.now)
updated_at: datetime = Field(default_factory=datetime.now)
items: list[Item] = Field(default_factory=list)
total_price: float = 0
total_price: float = None
total_credits: int = None
@field_validator('basket')
def validate_basket(cls, v):
@@ -104,15 +107,18 @@ class OrderRequest(BaseModel):
return
def load_items_and_price(self):
# This should be automatic upon initialization of the class
"""
Loads item details from database and calculates the total price.
Loads item details from database and calculates the total price as well as the total credits to be granted.
"""
self.items = []
self.total_price = 0
self.total_credits = 0
for basket_item in self.basket:
item = item_from_sql(basket_item.id)
self.items.append(item)
self.total_price += item.unit_price * basket_item.quantity
self.total_price += item.unit_price * basket_item.quantity # increment price
self.total_credits += item.unit_credits * basket_item.quantity # increment credit balance
def to_paypal_items(self):
@@ -138,11 +144,8 @@ class OrderRequest(BaseModel):
return item_list
# Payment handler class for managing PayPal payments
class PaypalHandler:
class PaypalClient:
"""
Handles PayPal payment operations.
@@ -159,7 +162,6 @@ class PaypalHandler:
"expires_at": 0
}
order_request = None
def __init__(
self,
@@ -178,11 +180,11 @@ class PaypalHandler:
if sandbox_mode :
self.id = Environment.paypal_id_sandbox
self.key = Environment.paypal_key_sandbox
self.base_url = BASE_URL
self.base_url = BASE_URL_SANDBOX
else :
self.id = Environment.paypal_id_prod
self.key = Environment.paypal_key_prod
self.base_url = 'https://api-m.paypal.com'
self.base_url = BASE_URL_PROD
@@ -239,6 +241,11 @@ class PaypalHandler:
Returns:
dict | None: PayPal order response JSON, or None if failed.
"""
# Fetch details of order from mart database and compute total credits and price
order_request.load_items_and_price()
# Prepare payload for post request to paypal API
order_data = {
'intent': 'CAPTURE',
'purchase_units': [
@@ -286,20 +293,24 @@ class PaypalHandler:
# order_id (key): json.loads(order_response.text)["id"]
# user_id : order_request.user_id
# created_at : order_request.created_at
# status : order_request.status
# status : PENDING
# basket (json) : OrderDetails.jsonify()
# total_price : order_request.total_price
# currency : order_request.currency
# updated_at : order_request.created_at
# Now we can increment the supabase balance by so many credits as in the balance.
#TODO still
# Create a cache item for credits to be granted to user
CreditCache.set_credits(
user_id = order_request.user_id,
order_id = json.loads(order_response.text)["id"],
credits_to_grant = order_request.total_credits)
return order_response.json()
# Standalone function to capture a payment
def capture(self, order_id: str):
def capture(self, user_id: str, order_id: str):
"""
Captures payment for a PayPal order.
@@ -314,7 +325,7 @@ class PaypalHandler:
try:
capture_response = requests.post(
url = f'{BASE_URL}/v2/checkout/orders/{order_id}/capture',
url = f'{self.base_url}/v2/checkout/orders/{order_id}/capture',
headers = {'Authorization': f'Bearer {access_token}'},
json = {},
)
@@ -322,11 +333,12 @@ class PaypalHandler:
logger.error(f'Error while requesting access token: {exc}')
return None
# Raise exception if API call failed
capture_response.raise_for_status()
# todo check status code + try except
print(capture_response.text)
# order_id = json.loads(response.text)["id"]
# print(capture_response.text)
# TODO: update status to PAID in sql database