PayPal Redirection Issue After Successful Payment in Photobooth Integration

leoshelp
New Community Member

In my Raspberry Pi Photo booth application using Pi booth, PayPal payment is initiated through a QR code. Upon successful payment, however, I am redirected to a non-existent Webpage. This prevents the system from confirming the payment and showing the next step in the process — a Wi-Fi connection QR code. The same PayPal payment flow works correctly in a standalone GUI program where the payment completion closes the window without errors. Could this be related to the integration with Pi booth or the PayPal redirection settings?

This ist my stand alone code:

# -*- coding: utf-8 -*-
import qrcode
from PIL import Image
from io import BytesIO
import PySimpleGUI as sg
import requests
import threading
from flask import Flask, request, redirect, jsonify
 
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
 
# Flask app für PayPal und GUI-Benachrichtigung
app = Flask(__name__)
 
# PayPal-Konfiguration
PAYPAL_CLIENT_ID = 'covered-for-paypal-community'
PAYPAL_SECRET = 'covered-for-paypal-community'
 
gui_notification_url = "http://127.0.0.1:5000/payment-success"
payment_successful = threading.Event()
 
def generate_qr(data):
    qr = qrcode.QRCode(version=1, box_size=10, border=5)
    qr.add_data(data)
    qr.make(fit=True)
    img = qr.make_image(fill='black', back_color='white')
    return img
 
def create_qr_code():
    response = requests.get('http://127.0.0.1:5000/create-payment', verify=False)
    if response.status_code == 200:
        return generate_qr(response.url)
 
('/payment-success', methods=['POST'])
def payment_success():
    payment_successful.set()
    return jsonify({"status": "received"}), 200
 
def get_access_token():
    response = requests.post(
        PAYPAL_OAUTH_URL,
        headers={'Accept': 'application/json', 'Accept-Language': 'de_DE'},
        data={'grant_type': 'client_credentials'},
        auth=(PAYPAL_CLIENT_ID, PAYPAL_SECRET)
    )
    return response.json()['access_token']
 
def create_payment():
    access_token = get_access_token()
    headers = {'Content-Type': 'application/json', 'Authorization': f'Bearer {access_token}'}
    payment_data = {
        "intent": "sale",
        "redirect_urls": {
            "return_url": "https://covered-for-paypal-community(its my IP4 Adress/Public IP)/success",
            "cancel_url": "https://covered-for-paypal-community(its my IP4 Adress/Public IP)/cancel"
        },
        "payer": {"payment_method": "paypal"},
        "transactions": [{
            "amount": {"total": "0.50", "currency": "EUR"},
            "description": "This is the payment transaction description."
        }]
    }
    response = requests.post(PAYPAL_PAYMENT_URL, json=payment_data, headers=headers)
    return response.json()
 
('/create-payment')
def create_payment_route():
    payment = create_payment()
    for link in payment['links']:
        if link['rel'] == 'approval_url':
            approval_url = link['href']
            return redirect(approval_url)
    return 'Error creating payment'
 
('/success')
def success():
    payment_id = request.args.get('paymentId')
    payer_id = request.args.get('PayerID')
    execute_payment(payment_id, payer_id)
    token = request.args.get('token')
    notify_gui_payment_success()
 
def execute_payment(payment_id, payer_id):
    access_token = get_access_token()
    headers = {'Content-Type': 'application/json', 'Authorization': f'Bearer {access_token}'}
    data = {"payer_id": payer_id}
    response = requests.post(url, json=data, headers=headers)
    return response.json()
 
def notify_gui_payment_success():
    try:
        response = requests.post(gui_notification_url, json={"status": "success"}, verify=False)
        response.raise_for_status()
    except requests.exceptions.RequestException as e:
        print(f"Error notifying GUI: {e}")
 
('/cancel')
def cancel():
    return "Zahlung abgebrochen"
 
# GUI
def start_gui():
    layout = [[sg.Text("PayPal Payment")], [sg.Image(key='-QR-')]]
    window = sg.Window("PayPal QR Payment", layout, finalize=True)
 
    img = create_qr_code()
    bio = BytesIO()
    img.save(bio, format='PNG')
    window['-QR-'].update(data=bio.getvalue())
 
    while True:
        event, values = window.read(timeout=100)
        if event == sg.WIN_CLOSED:
            break
        if payment_successful.is_set():
            window.close()
            break
 
    window.close()
 
# Starte Flask-Apps in separaten Threads
if __name__ == "__main__":
    threading.Thread(target=lambda: app.run(host='0.0.0.0', port=443, use_reloader=False, ssl_context=('/home/Desktop/ssl/cert.crt', '/home/Desktop/ssl/key.pem'))).start()
    threading.Thread(target=lambda: app.run(host='127.0.0.1', port=5000, use_reloader=False)).start()
    start_gui()



The above code does work!!
But this code, involving pibooth with the same logik as the above code does not work:

import os
import shutil
import PySimpleGUI as sg
import qrcode
import pibooth
import subprocess
import tkinter as tk
import time
import requests
import threading
from flask import Flask, request, redirect, jsonify
 
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
 
 
# Flask app für PayPal und GUI-Benachrichtigung
app = Flask(__name__)
 
PAYPAL_CLIENT_ID = 'covered-for-paypal-community'
PAYPAL_SECRET = 'covered-for-paypal-community'
 
gui_notification_url = "http://127.0.0.1:5000/payment-success"
payment_successful = threading.Event()
 
# Funktionen für die Zahlung
 
# 1. Token von PayPal holen
def get_access_token():
    response = requests.post(
        PAYPAL_OAUTH_URL,
        headers={
            'Accept': 'application/json',
            'Accept-Language': 'de_DE'
        },
        data={
            'grant_type': 'client_credentials'
        },
        auth=(PAYPAL_CLIENT_ID, PAYPAL_SECRET)
    )
    
     
    # Füge diese Zeilen hinzu, um die Antwort zu überprüfen
    #print("Response Status Code:", response.status_code)
    #print("Response Text:", response.text)
    
    #response.raise_for_status()
    return response.json()['access_token']
 
# 2. Zahlung erstellen
def create_payment():
    access_token = get_access_token()
    headers = {
        'Content-Type': 'application/json',
        'Authorization': f'Bearer {access_token}'
    }
    payment_data = {
        "intent": "sale",
        "redirect_urls": {
            "return_url": "https://covered-for-paypal-community/success",
            "cancel_url": "https://covered-for-paypal-community/cancel"
        },
        "payer": {
            "payment_method": "paypal"
        },
        "transactions": [{
            "amount": {
                "total": "0.50",
                "currency": "EUR"
            },
            "description": "Spenden Sie 50 Cent, um Ihr Bild herunterzuladen :)"
        }]
    }
    response = requests.post(PAYPAL_PAYMENT_URL, json=payment_data, headers=headers)
    return response.json()
 
 
('/payment-success', methods=['POST'])
def payment_success():
    payment_successful.set()
    return jsonify({"status": "received"}), 200
 
('/success')
def success():
    payment_id = request.args.get('paymentId')
    payer_id = request.args.get('PayerID')
    execute_payment(payment_id, payer_id)
    #token = request.args.get('token')
    notify_gui_payment_success()
    payment_successful.set()
    return jsonify({"status": "Payment successful"})
 
# 3. Zahlung abschließen
def execute_payment(payment_id, payer_id):
    access_token = get_access_token()
    headers = {'Content-Type': 'application/json', 'Authorization': f'Bearer {access_token}'}
    data = {"payer_id": payer_id}
    response = requests.post(url, json=data, headers=headers)
    return response.json()
 
# 4. Benachrichtige GUI bei erfolgreicher Zahlung
def notify_gui_payment_success():
    try:
        response = requests.post(gui_notification_url, json={"status": "success"}, verify=False)
        response.raise_for_status()
    except requests.exceptions.RequestException as e:
        print(f"Error notifying GUI: {e}")
 
('/cancel')
def cancel():
    return "Zahlung abgebrochen"
        
 
# Pibooth-Funktionen
def get_directory_content(folder_path):
    if os.path.isdir(folder_path):
        contents = os.listdir(folder_path)
        jpg_files = [item for item in contents if item.lower().endswith('.jpg')]
        for jpg_file in jpg_files:
            return jpg_file
    return None
 
def delete_folder_content(folder_path):
    if os.path.exists(folder_path):
        for file_name in os.listdir(folder_path):
            file_path = os.path.join(folder_path, file_name)
            if os.path.isfile(file_path):
                os.unlink(file_path)
            elif os.path.isdir(file_path):
                shutil.rmtree(file_path)
 
class QRCodePopup:
    def __init__(self, link):
        self.link = link
        self.show_payment_popup()
 
    def show_payment_popup(self):
        layout = [
            [sg.Text("Möchten Sie 50 Cent spenden, um Ihr Bild herunterzuladen?", font=("Amatic Bold", 20))],
            [sg.Image(key='-QR-')],
            [sg.Button("NEIN DANKE", key="CLOSE", font=("Helvetica", 20))]
        ]
 
        window = sg.Window("Zahlung", layout, finalize=True)
 
        # Generiere den QR-Code für die Zahlung
        payment_info = create_payment()
        approval_url = next(link['href'] for link in payment_info['links'] if link['rel'] == 'approval_url')
        qr_code_filename = self.generate_qr_code(approval_url)
        window['-QR-'].update(filename=qr_code_filename)
 
        while True:
            event, values = window.read()
 
            if event in (sg.WIN_CLOSED, "CLOSE"):
                break
            if payment_successful.is_set():
                window.close()
                self.show_download_popup()
 
        window.close()
 
    def show_download_popup(self):
        layout = [
            [sg.Text("Zahlung erfolgreich! Hier ist Ihr Download-Link:", font=("Amatic Bold", 20))],
            [sg.Button("Download Bild", key="DOWNLOAD", font=("Helvetica", 20))],
            [sg.Button("Schließen", key="CLOSE", font=("Helvetica", 20))]
        ]
 
        window = sg.Window("Download", layout, finalize=True)
 
        while True:
            event, values = window.read()
            if event in (sg.WIN_CLOSED, "CLOSE"):
                break
            if event == "DOWNLOAD":
                # Logik zum Herunterladen des Bildes
                sg.popup("Bild wird heruntergeladen...")
 
        window.close()
 
    def generate_qr_code(self, link, filename="qrcode.png"):
        qr = qrcode.QRCode(version=1, box_size=10, border=4)
        qr.add_data(link)
        qr.make(fit=True)
        img = qr.make_image(fill_color="black", back_color="white")
        img.save(filename)
        return filename
 
 
def state_finish_do():
    folder_path = "covered-for-paypal-community"
    filename = get_directory_content(folder_path)
    if filename:
        link = "http://raspberrypi.local/pictures/" + filename
        QRCodePopup(link)
 
pibooth.hookimpl
def state_wait_enter():
    folder_path = "covered-for-paypal-community"
    delete_folder_content(folder_path)
 
if __name__ == "__main__":
    threading.Thread(target=lambda: app.run(host='0.0.0.0', port=443, use_reloader=False, ssl_context=('/home/Desktop/ssl/cert.crt', '/home/Desktop/ssl/key.pem'))).start()
    threading.Thread(target=lambda: app.run(host='0.0.0.0', port=5000, use_reloader=False)).start()

PLEASE ITS VERY IMPORTANT AND I NEED HELP ASAP!

I am thankfull for every answer and help! 🙂
Have a nice day!
Login to Me Too
1 REPLY 1

MTS_Jennifer
Moderator
Moderator

Hello @leoshelp ,

Thank you for posting to the PayPal Sandbox Community.

v1 payments is deprecated, and was replaced by v2 orders api, which is probably part of the issue you are facing with testing. The replacement was v2 orders api, which has the ability to process both PayPal and Credit Cards. So in the create order api call the payment_source declares paypal or card, and the experience_context includes the return_url.

We do have a Python example for our Create order api available. Since v1 payments is deprecated, it may be impacting the return_url.

Here is the documentation for v2 orders api:

https://developer.paypal.com/docs/api/orders/v2/#orders_create

I copied the Python code from the above documentation: The access_token is an example, you will still need to request an access_token the same way that you have been. You can update the shipping_preference to GET_FROM_FILE to get the shipping address from PayPal or NO_SHIPPING which will remove the shipping address from the transaction. The return_url is part of the payment_source object, so that is where you declare it.

import requests

headers = {
    'Content-Type': 'application/json',
    'PayPal-Request-Id': '7b92603e-77ed-4896-8e78-5dea2050476a',
    'Authorization': 'Bearer 6V7rbVwmlM1gFZKW_8QtzWXqpcwQ6T5vhEGYNJDAAdn3paCgRpdeMdVYmWzgbKSsECednupJ3Zx5Xd-g',
}

data = '{ "intent": "CAPTURE", "purchase_units": [ { "reference_id": "d9f80740-38f0-11e8-b467-0ed5f89f718b", "amount": { "currency_code": "USD", "value": "100.00" } } ], "payment_source": { "paypal": { "experience_context": { "payment_method_preference": "IMMEDIATE_PAYMENT_REQUIRED", "brand_name": "EXAMPLE INC", "locale": "en-US", "landing_page": "LOGIN", "shipping_preference": "SET_PROVIDED_ADDRESS", "user_action": "PAY_NOW", "return_url": "https://example.com", "cancel_url": "https://example.com/cancelUrl" } } } }'

response = requests.post('https://api-m.sandbox.paypal.com/v2/checkout/orders', headers=headers, data=data)

 After you create the order, you should receive the url link for the buyer to approve the payment in the response (for PayPal Transactions).

Once the buyer approves the payment, they will be returned to the return url of your choice. then you send the Capture payment for order api call:

https://developer.paypal.com/docs/api/orders/v2/#orders_capture (below example has an order id in it, you would pass in the order id that is returned in the create order api call)

import requests

headers = {
    'Content-Type': 'application/json',
    'PayPal-Request-Id': '7b92603e-77ed-4896-8e78-5dea2050476a',
    'Authorization': 'Bearer access_token6V7rbVwmlM1gFZKW_8QtzWXqpcwQ6T5vhEGYNJDAAdn3paCgRpdeMdVYmWzgbKSsECednupJ3Zx5Xd-g',
}

response = requests.post('https://api-m.sandbox.paypal.com/v2/checkout/orders/5O190127TN364715T/capture', headers=headers)

 

Thank you,

Jennifer

MTS

PayPal

 

Login to Me Too

Haven't Found your Answer?

It happens. Hit the "Login to Ask the community" button to create a question for the PayPal community.