Merge modifications for more separate backend functions #69
@@ -13,6 +13,11 @@ load_dotenv(override=True)
|
|||||||
@dataclass
|
@dataclass
|
||||||
class Environment :
|
class Environment :
|
||||||
|
kscheidecker marked this conversation as resolved
|
|||||||
|
|
||||||
|
# Load supabase secrets
|
||||||
supabase_url = os.environ['SUPABASE_URL']
|
supabase_url = os.environ['SUPABASE_URL']
|
||||||
supabase_admin_key = os.environ['SUPABASE_ADMIN_KEY']
|
supabase_admin_key = os.environ['SUPABASE_ADMIN_KEY']
|
||||||
supabase_test_user_id = os.environ['SUPABASE_TEST_USER_ID']
|
supabase_test_user_id = os.environ['SUPABASE_TEST_USER_ID']
|
||||||
|
|
||||||
|
# Load paypal secrets
|
||||||
|
paypal_id_sandbox = os.environ['PAYPAL_ID_SANDBOX']
|
||||||
|
paypal_key_sandbox = os.environ['PAYPAL_KEY_SANDBOX']
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ from pydantic import BaseModel
|
|||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from ..configuration.environment import Environment
|
||||||
|
|
||||||
|
|
||||||
# Model for payment request body
|
# Model for payment request body
|
||||||
class PaymentRequest(BaseModel):
|
class PaymentRequest(BaseModel):
|
||||||
@@ -14,13 +17,22 @@ class PaymentRequest(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
# Payment handler class for managing PayPal payments
|
# Payment handler class for managing PayPal payments
|
||||||
class PaymentHandler:
|
class PaypalHandler:
|
||||||
|
|
||||||
payment_id: str
|
# PayPal secrets
|
||||||
|
username = Environment.paypal_id_sandbox
|
||||||
|
password = Environment.paypal_key_sandbox
|
||||||
|
|
||||||
def __init__(self, transaction_details: PaymentRequest):
|
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
transaction_details: PaymentRequest,
|
||||||
|
sandbox_mode: bool = False
|
||||||
|
):
|
||||||
self.details = transaction_details
|
self.details = transaction_details
|
||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
|
self.sandbox = sandbox_mode
|
||||||
|
|
||||||
# Only support purchase of credit 'bundles': 10, 50 or 100 credits worth of trip generation
|
# Only support purchase of credit 'bundles': 10, 50 or 100 credits worth of trip generation
|
||||||
def fetch_price(self) -> float:
|
def fetch_price(self) -> float:
|
||||||
@@ -34,37 +46,40 @@ class PaymentHandler:
|
|||||||
self.logger.error(f"Unsupported currency: {self.details.currency}")
|
self.logger.error(f"Unsupported currency: {self.details.currency}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def create_paypal_payment(self) -> str:
|
|
||||||
"""
|
|
||||||
Creates a PayPal payment and returns the approval URL.
|
|
||||||
"""
|
|
||||||
price = self.fetch_price()
|
|
||||||
payment = paypalrestsdk.Payment({
|
|
||||||
"intent": "sale",
|
|
||||||
"payer": {
|
|
||||||
"payment_method": "paypal"
|
|
||||||
},
|
|
||||||
"transactions": [{
|
|
||||||
"amount": {
|
|
||||||
"total": f"{price:.2f}",
|
|
||||||
"currency": self.details.currency
|
|
||||||
},
|
|
||||||
"description": self.details.description
|
|
||||||
}],
|
|
||||||
"redirect_urls": {
|
|
||||||
"return_url": "http://localhost:8000/payment/success",
|
|
||||||
"cancel_url": "http://localhost:8000/payment/cancel"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if payment.create():
|
def validate(self):
|
||||||
self.logger.info("Payment created successfully")
|
|
||||||
self.payment_id = payment.id
|
|
||||||
|
|
||||||
# Get the approval URL and return it for the user to approve
|
if self.sandbox :
|
||||||
for link in payment.links:
|
validation_url = "https://api-m.sandbox.paypal.com/v1/oauth2/token"
|
||||||
if link.rel == "approval_url":
|
else :
|
||||||
return link.href
|
validation_url = "https://api-m.paypal.com/v1/oauth2/token"
|
||||||
|
remoll marked this conversation as resolved
Outdated
remoll
commented
Pretty sure that won't work. It should definitely not be hardcoded since the callback will change depending on prod or staging. Pretty sure that won't work. It should definitely not be hardcoded since the callback will change depending on prod or staging.
|
|||||||
else:
|
|
||||||
self.logger.error(f"Failed to create payment: {payment.error}")
|
# payload for the validation request
|
||||||
raise HTTPException(status_code=500, detail="Payment creation failed")
|
validation_data = {'grant_type': 'client_credentials'}
|
||||||
|
|
||||||
|
# pass the request
|
||||||
|
validation_response = requests.post(
|
||||||
|
url=validation_url,
|
||||||
|
data=validation_data,
|
||||||
|
auth=(self.username, self.password)
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO: continue here
|
||||||
|
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
def order(self):
|
||||||
|
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
def capture(self):
|
||||||
|
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def cancel(self):
|
||||||
|
|
||||||
|
pass
|
||||||
|
|||||||
100
backend/src/payments/test.py
Normal file
100
backend/src/payments/test.py
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
from ..configuration.environment import Environment
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# DOCUMENTATION AT : https://developer.paypal.com/api/rest/requests/
|
||||||
|
|
||||||
|
# username and password
|
||||||
|
username = Environment.paypal_id_sandbox
|
||||||
|
password = Environment.paypal_key_sandbox
|
||||||
|
|
||||||
|
|
||||||
|
######## STEP 1: Validation ########
|
||||||
|
# url for validation post request
|
||||||
|
validation_url = "https://api-m.sandbox.paypal.com/v1/oauth2/token"
|
||||||
|
validation_url_prod = "https://api-m.paypal.com/v1/oauth2/token"
|
||||||
|
|
||||||
|
# payload for the post request
|
||||||
|
validation_data = {'grant_type': 'client_credentials'}
|
||||||
|
|
||||||
|
# pass the request
|
||||||
|
validation_response = requests.post(
|
||||||
|
url=validation_url,
|
||||||
|
data=validation_data,
|
||||||
|
auth=(username, password)
|
||||||
|
)
|
||||||
|
|
||||||
|
# todo check status code + try except. Status code 201 ?
|
||||||
|
print(validation_response.text)
|
||||||
|
access_token = json.loads(validation_response.text)["access_token"]
|
||||||
|
|
||||||
|
|
||||||
|
######## STEP 2: Create Order ########
|
||||||
|
# url for post request
|
||||||
|
order_url = "https://api-m.sandbox.paypal.com/v2/checkout/orders"
|
||||||
|
order_url_prod = "https://api-m.paypal.com/v2/checkout/orders"
|
||||||
|
|
||||||
|
# payload for the request
|
||||||
|
order_data = {
|
||||||
|
"intent": "CAPTURE",
|
||||||
|
"purchase_units": [
|
||||||
|
{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"name": "AnyWay Credits",
|
||||||
|
"description": "50 pack of credits",
|
||||||
|
"quantity": 1,
|
||||||
|
"unit_amount": {
|
||||||
|
"currency_code": "CHF",
|
||||||
|
"value": "5.00"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"amount": {
|
||||||
|
"currency_code": "CHF",
|
||||||
|
"value": "5.00",
|
||||||
|
"breakdown": {
|
||||||
|
"item_total": {
|
||||||
|
"currency_code": "CHF",
|
||||||
|
"value": "5.00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"application_context": {
|
||||||
|
"return_url": "https://anydev.info",
|
||||||
|
"cancel_url": "https://anydev.info"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
order_response = requests.post(
|
||||||
|
url=order_url,
|
||||||
|
json=order_data,
|
||||||
|
auth=(username, password)
|
||||||
|
)
|
||||||
|
|
||||||
|
# todo check status code + try except
|
||||||
|
print(order_response.text)
|
||||||
|
order_id = json.loads(order_response.text)["id"]
|
||||||
|
|
||||||
|
|
||||||
|
######## STEP 3: capture payment
|
||||||
|
# url for post request
|
||||||
|
capture_url = f"https://api-m.sandbox.paypal.com/v2/checkout/orders/{order_id}/capture"
|
||||||
|
capture_url_prod = f"https://api-m.paypal.com/v2/checkout/orders/{order_id}/capture"
|
||||||
|
|
||||||
|
capture_response = requests.post(
|
||||||
|
url=capture_url,
|
||||||
|
json={},
|
||||||
|
auth=(username, password)
|
||||||
|
)
|
||||||
|
|
||||||
|
# todo check status code + try except
|
||||||
|
print(capture_response.text)
|
||||||
|
# order_id = json.loads(response.text)["id"]
|
||||||
|
|
||||||
Reference in New Issue
Block a user
I like this.