diff --git a/server/vbv_lernwelt/payment/README.md b/server/vbv_lernwelt/payment/README.md
new file mode 100644
index 00000000..4c4d4202
--- /dev/null
+++ b/server/vbv_lernwelt/payment/README.md
@@ -0,0 +1,181 @@
+# Datatrans - Proof of Concept
+
+## Links
+
+- https://admin.sandbox.datatrans.com
+- https://api-reference.datatrans.ch/#section/Idempotency
+- https://docs.datatrans.ch/docs/redirect-lightbox#section-initializing-transactions
+
+## Code
+
+Simple example of the payment flow with Datatrans:
+
+```python
+
+from flask import Flask, request, render_template_string, jsonify, abort
+import uuid
+import hmac
+import hashlib
+import requests
+import os
+
+app = Flask(__name__)
+
+if "HMAC_KEY" not in os.environ:
+ exit("Please set the HMAC_KEY environment variable.")
+
+if "BASIC_AUTH" not in os.environ:
+ exit("Please set the BASIC_AUTH environment variable.")
+
+# https://admin.sandbox.datatrans.com/MerchSecurAdmin.jsp
+HMAC_KEY = os.environ["HMAC_KEY"]
+BASIC_AUTH = os.environ["BASIC_AUTH"]
+API_ENDPOINT = "https://api.sandbox.datatrans.com/v1/transactions"
+
+LIGHTBOX_PAGE = """
+
+
+
+
+
+
+
+
+
+"""
+
+SUCCESS_PAGE = "Payment Success
"
+ERROR_PAGE = "Payment Error
"
+CANCEL_PAGE = "Payment Cancelled
"
+
+# TODO: There is now way to test this locally, so we need to use ngrok (?)
+BASE_URL = "https://89d3-2a02-21b4-9679-d800-ac5f-489-e9f6-694e.ngrok-free.app"
+
+
+@app.route("/success", methods=["GET"])
+def success():
+ return render_template_string(SUCCESS_PAGE)
+
+
+@app.route("/error", methods=["GET"])
+def error():
+ return render_template_string(ERROR_PAGE)
+
+
+@app.route("/cancel", methods=["GET"])
+def cancel():
+ return render_template_string(CANCEL_PAGE)
+
+
+@app.route("/init_transaction", methods=["GET"])
+def init_transaction():
+ # TODO
+ # for debugging, it might be handy to know the
+ # user who initiated the transaction
+ refno = uuid.uuid4().hex
+
+ # TODO
+ # The language of user
+ language = "en"
+
+ # Transaction payload
+ payload = {
+ "currency": "CHF",
+ "refno": refno,
+ "amount": 10_00, # 10 CHF
+ "autoSettle": True,
+ "language": language,
+ "redirect": {
+ "successUrl": f"{BASE_URL}/success",
+ "errorUrl": f"{BASE_URL}/error",
+ "cancelUrl": f"{BASE_URL}/cancel",
+ },
+ "webhook": {
+ "url": f"{BASE_URL}/webhook",
+ },
+ }
+
+ # Headers
+ headers = {
+ "Authorization": f"Basic {BASIC_AUTH}",
+ "Content-Type": "application/json",
+ }
+
+ # 1. USING LIGHTBOX
+ response = requests.post(API_ENDPOINT, json=payload, headers=headers)
+
+ if response.ok:
+ transaction_id = response.json().get("transactionId")
+ return render_template_string(LIGHTBOX_PAGE, transaction_id=transaction_id)
+ else:
+ return (
+ jsonify(
+ {"error": "Failed to initiate transaction", "details": response.text}
+ ),
+ response.status_code,
+ )
+
+ # 2. USING REDIRECT
+ # # Send POST request to Datatrans API
+ # response = requests.post(url, json=payload, headers=headers)
+
+ # if response.ok:
+ # transaction_id = response.json().get('transactionId')
+ # payment_url = f'https://pay.sandbox.datatrans.com/v1/start/{transaction_id}'
+ # return redirect(payment_url)
+ # else:
+ # # Return error message
+ # return jsonify({"error": "Failed to initiate transaction", "details": response.text}), response.status_code
+
+
+@app.route("/webhook", methods=["POST"])
+def webhook():
+ """
+ Checks the Datatrans-Signature header of the incoming request and validates the signature:
+ https://api-reference.datatrans.ch/#section/Webhook/Webhook-signing
+ """
+
+ hmac_key = HMAC_KEY
+
+ def calculate_signature(key: str, timestamp: str, payload: str) -> str:
+ key_bytes = bytes.fromhex(key)
+ signing_data = f"{timestamp}{payload}".encode("utf-8")
+ hmac_obj = hmac.new(key_bytes, signing_data, hashlib.sha256)
+ return hmac_obj.hexdigest()
+
+ # Header format:
+ # Datatrans-Signature: t={{timestamp}},s0={{signature}}
+ datatrans_signature = request.headers.get("Datatrans-Signature", "")
+
+ try:
+ parts = datatrans_signature.split(",")
+ timestamp = parts[0].split("=")[1]
+ received_signature = parts[1].split("=")[1]
+
+ calculated_signature = calculate_signature(
+ hmac_key, timestamp, request.data.decode("utf-8")
+ )
+
+ if calculated_signature == received_signature:
+ return "Signature validated.", 200
+ else:
+ abort(400, "Invalid signature.")
+ except (IndexError, ValueError):
+ abort(400, "Invalid Datatrans-Signature header.")
+
+
+if __name__ == "__main__":
+ app.run(debug=True, host="0.0.0.0", port=5500)
+
+```