Skip to content

Commit 5112451

Browse files
committed
feat: python example
1 parent 1f05857 commit 5112451

7 files changed

Lines changed: 622 additions & 0 deletions

File tree

.github/workflows/python.yaml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Python CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths: [backend/python/**, .github/workflows/python.yaml]
7+
pull_request:
8+
branches: [main]
9+
paths: [backend/python/**, .github/workflows/python.yaml]
10+
11+
jobs:
12+
backend-python:
13+
name: Backend Python
14+
runs-on: ubuntu-latest
15+
defaults:
16+
run:
17+
working-directory: backend/python
18+
steps:
19+
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
20+
21+
- name: Install uv
22+
uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
23+
24+
- name: Install dependencies
25+
run: uv sync
26+
27+
- name: Install Ruff
28+
run: uv pip install ruff
29+
30+
- name: Lint
31+
run: uv run ruff check app.py
32+
33+
- name: Type check
34+
run: uv run python -m compileall app.py

backend/python/.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
SUMUP_API_KEY=
2+
SUMUP_MERCHANT_CODE=
3+
PORT=3003

backend/python/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.venv/

backend/python/README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Python example
2+
3+
## Setup
4+
5+
```sh
6+
python -m venv .venv
7+
source .venv/bin/activate
8+
pip install -r requirements.txt
9+
```
10+
11+
```sh
12+
export SUMUP_API_KEY="your_api_key"
13+
export SUMUP_MERCHANT_CODE="your_merchant_code"
14+
```
15+
16+
## Run
17+
18+
```sh
19+
python app.py
20+
```
21+
22+
## Test
23+
24+
```sh
25+
curl -X POST http://localhost:8080/readers \
26+
-H "Content-Type: application/json" \
27+
-d '{"pairing_code":"12345678","name":"Front Desk"}'
28+
```
29+
30+
```sh
31+
curl http://localhost:8080/readers
32+
```
33+
34+
```sh
35+
curl -X POST http://localhost:8080/readers/READER_ID/checkout \
36+
-H "Content-Type: application/json" \
37+
-d '{"amount": 10.0}'
38+
```

backend/python/app.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import os
2+
3+
from flask import Flask, jsonify, request
4+
from sumup import Sumup
5+
from sumup.readers import CreateReaderBody, CreateReaderCheckoutBody, CreateReaderCheckoutBodyTotalAmount
6+
7+
api_key = os.environ.get("SUMUP_API_KEY")
8+
merchant_code = os.environ.get("SUMUP_MERCHANT_CODE")
9+
10+
if not api_key:
11+
raise SystemExit("Missing SUMUP_API_KEY env var.")
12+
13+
if not merchant_code:
14+
raise SystemExit("Missing SUMUP_MERCHANT_CODE env var.")
15+
16+
client = Sumup(api_key=api_key)
17+
18+
app = Flask(__name__)
19+
20+
21+
def parse_amount(value):
22+
try:
23+
amount = float(value)
24+
except (TypeError, ValueError):
25+
return None
26+
27+
if amount <= 0:
28+
return None
29+
30+
return CreateReaderCheckoutBodyTotalAmount(currency="EUR", minor_unit=2, value=round(amount * 100))
31+
32+
33+
@app.post("/readers")
34+
def create_reader():
35+
data = request.get_json(silent=True) or {}
36+
pairing_code = str(data.get("pairing_code", "")).strip()
37+
name = str(data.get("name", "")).strip()
38+
39+
if not pairing_code or not name:
40+
return jsonify({"error": "pairing_code and name are required"}), 400
41+
42+
reader = client.readers.create(
43+
merchant_code,
44+
body=CreateReaderBody(pairing_code=pairing_code, name=name),
45+
)
46+
47+
if hasattr(reader, "model_dump"):
48+
payload = reader.model_dump()
49+
else:
50+
payload = reader.dict()
51+
52+
return jsonify(payload), 201
53+
54+
55+
@app.get("/readers")
56+
def list_readers():
57+
readers = client.readers.list(merchant_code)
58+
59+
if hasattr(readers, "model_dump"):
60+
payload = readers.model_dump()
61+
else:
62+
payload = readers.dict()
63+
64+
return jsonify(payload)
65+
66+
67+
@app.post("/readers/<reader_id>/checkout")
68+
def create_reader_checkout(reader_id):
69+
data = request.get_json(silent=True) or {}
70+
total_amount = parse_amount(data.get("amount"))
71+
72+
if not total_amount:
73+
return jsonify({"error": "amount must be a positive number"}), 400
74+
75+
checkout = client.readers.create_checkout(
76+
merchant_code,
77+
reader_id,
78+
body=CreateReaderCheckoutBody(
79+
total_amount=total_amount,
80+
description="Card reader checkout",
81+
),
82+
)
83+
84+
if hasattr(checkout, "model_dump"):
85+
payload = checkout.model_dump()
86+
else:
87+
payload = checkout.dict()
88+
89+
return jsonify(payload), 201
90+
91+
92+
if __name__ == "__main__":
93+
port = int(os.environ.get("PORT", "8080"))
94+
app.run(host="0.0.0.0", port=port)

backend/python/pyproject.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[project]
2+
name = "sumup-examples-python"
3+
version = "0.1.0"
4+
description = "SumUp Python example"
5+
requires-python = ">=3.10"
6+
dependencies = [
7+
"flask",
8+
"sumup",
9+
]
10+
11+
[tool.uv]

0 commit comments

Comments
 (0)