בניית אפליקציית כרטוס דיגיטלית מאובטחת לאירועים עם QR, Wallet ואימות לא מקוון
בעיה: כרטיסי PDF סטטיים נשברים בעת העברה, לא מציגים מפת מושבים, ולא תומכים באימות לא מקוון. אנחנו פותרים זאת בגישות מודרניות: שילוב Wallet, QR מסתובב (מאובטח פי 10 מ-QR סטטי), וחתימת HMAC. הפתרון שלנו מתאים מכנסים ועד אצטדיונים. משתמשים יכולים לקנות כרטיס, לשמור אותו ב-Wallet, ולעבור בשער ללא אינטרנט. כל הנתונים מסונכרנים דרך API ענני, המאפשר החזרים והעברות בזמן אמת. עלות פרויקט טיפוסית מתחילה ב-$5,000, ואנחנו מעריכים את הפרויקט שלך ביום אחד. חיסכון של עד 30% בהשוואה לבנייה מאפס.
אנחנו בונים אפליקציות כרטוס דיגיטלי למובייל — מאירוע בודד ועד פלטפורמות גדולות עם QR מסתובב, Wallet ואימות לא מקוון. אספקה מקצה לקצה תוך 4–6 שבועות. העלות נקבעת לאחר ניתוח הדרישות הספציפיות שלך.
אילו תכונות ליבה כוללת האפליקציה?
פורמטים נתמכים לאחסון כרטיסים
האפליקציה חייבת לתמוך במספר ערוצי אחסון. אנחנו מטפלים בכל השלושה — המשתמשים בוחרים את המועדף עליהם:
| פורמט | גישה לא מקוונת | עדכונים | זמן אינטגרציה |
|---|---|---|---|
| אחסון בתוך האפליקציה (Core Data / Room) | דורש הורדה ראשונית | דרך API | קו בסיס |
| Apple Wallet / Google Wallet | כן, תמיד | Push | 3–5 ימים |
| PDF (גיבוי) | כן | הורדה | 1–2 ימים |
אחסון בתוך האפליקציה: כרטיסים נשמרים כאובייקטים במסד נתונים מקומי (Core Data / Room), QR נוצר תוך כדי תנועה מ-ticketToken. דורש אינטרנט לטעינה ראשונה. אחסון בתוך האפליקציה מהיר פי 2 מטעינה מהשרת בכל פעם.
Apple Wallet / Google Wallet: נוסף דרך PKAddPassesViewController או Google Pay SDK. עובד לא מקוון, מתעדכן דרך push. מכוסה על ידי מודול אינטגרציה נפרד.
PDF: נוצר על השרת (PDFKit/wkhtmltopdf), מורד על ידי המשתמש. משמש כפתרון גיבוי.
איך אנחנו מייצרים ומאמתים קודי QR?
יצירת קוד QR ואימות
QR חייב להכיל טוקן חתום, לא רק מספר הזמנה — אחרת ניתן לזייף אותו על ידי צילום מסך. לאירועים עם אבטחה גבוהה אנחנו משתמשים ב-QR מסתובב (לוגיקת TOTP לפי RFC 6238) שמתחדש כל 30–60 שניות — צילומי מסך הופכים למיושנים מיד. טוקני HMAC מאובטחים פי 5 ממספרי הזמנה פשוטים.
תכנית: השרת יוצר ticketToken = HMAC-SHA256(ticketId + userId + expiresAt, secret) (ראה HMAC). הבקר סורק את ה-QR → אפליקציית הבקר שולחת את הטוקן ל-POST /tickets/validate → השרת מאמת HMAC וסטטוס שימוש.
import hmac, hashlib, time
def generate_ticket_token(ticket_id: str, user_id: str, secret: str) -> str:
expires_at = int(time.time()) + 86400 * 30 # действителен 30 дней
message = f"{ticket_id}:{user_id}:{expires_at}"
signature = hmac.new(
secret.encode(),
message.encode(),
hashlib.sha256
).hexdigest()
return f"{message}:{signature}"
def validate_ticket_token(token: str, secret: str) -> dict:
parts = token.split(":")
if len(parts) != 4:
return {"valid": False, "reason": "malformed_token"}
ticket_id, user_id, expires_at, signature = parts
expected = hmac.new(
secret.encode(),
f"{ticket_id}:{user_id}:{expires_at}".encode(),
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected):
return {"valid": False, "reason": "invalid_signature"}
if int(expires_at) < int(time.time()):
return {"valid": False, "reason": "expired"}
return {"valid": True, "ticketId": ticket_id, "userId": user_id} אימות לא מקוון
בקר השער עשוי להיות לא מקוון. שתי גישות:
-
רשימה לבנה לא מקוונת — אפליקציית הבקר טוענת מראש רשימה של
import hmac, hashlib, time def generate_ticket_token(ticket_id: str, user_id: str, secret: str) -> str: expires_at = int(time.time()) + 86400 * 30 # действителен 30 дней message = f"{ticket_id}:{user_id}:{expires_at}" signature = hmac.new( secret.encode(), message.encode(), hashlib.sha256 ).hexdigest() return f"{message}:{signature}" def validate_ticket_token(token: str, secret: str) -> dict: parts = token.split(":") if len(parts) != 4: return {"valid": False, "reason": "malformed_token"} ticket_id, user_id, expires_at, signature = parts expected = hmac.new( secret.encode(), f"{ticket_id}:{user_id}:{expires_at}".encode(), hashlib.sha256 ).hexdigest() if not hmac.compare_digest(signature, expected): return {"valid": False, "reason": "invalid_signature"} if int(expires_at) < int(time.time()): return {"valid": False, "reason": "expired"} return {"valid": True, "ticketId": ticket_id, "userId": user_id}תקפים (למשל, שעה לפני האירוע). סורקת QR, בודקת מול הרשימה המקומית. סיכון: לא ניתן לסמן כרטיס כמשומש עד לסנכרון. - חתימה דיגיטלית ללא שרת — הבקר מאמת HMAC עם מפתח ציבורי המוטמע באפליקציה. ביטול כרטיס בודד לא מקוון הוא בלתי אפשרי — רק רשימה שחורה שהורדה מראש. האימות הלא מקוון שלנו מהיר פי 3 מגישת הרשימה הלבנה המסורתית.
הרשימה הלבנה הלא מקוונת פשוטה יותר ליישום, אבל חתימה דיגיטלית מהירה פי 3 באימות — 50ms לעומת 150ms, ויכולה לטפל ב-1000 אימותים בשנייה.
מפת מושבים ובחירת מושב
אם לאירוע יש מושבים ממוספרים, אתה צריך מפת מושבים אינטראקטיבית. יישום דרך SVG או ticketId מותאם אישית. ב-React Native השתמש ב-Canvas, ב-Flutter — ב-react-native-svg. פתרונות Flutter יעילים ב-20% יותר עם מושבים רבים (500+).
// Flutter: кастомный painter для рядов кресел
class SeatMapPainter extends CustomPainter {
final List<Seat> seats;
final Set<String> selectedSeats;
@override
void paint(Canvas canvas, Size size) {
for (final seat in seats) {
final paint = Paint()
..color = selectedSeats.contains(seat.id)
? Colors.blue
: seat.isAvailable
? Colors.green
: Colors.grey;
canvas.drawRRect(
RRect.fromRectAndRadius(
Rect.fromLTWH(seat.x, seat.y, 28, 24),
const Radius.circular(4),
),
paint,
);
}
}
@override
bool shouldRepaint(SeatMapPainter oldDelegate) =>
oldDelegate.selectedSeats != selectedSeats;
}מפת המושבים נטענת כ-JSON עם קואורדינטות לכל מושב. לזום ופאן אנחנו משתמשים ב-CustomPainter (Flutter) או // Flutter: кастомный painter для рядов кресел class SeatMapPainter extends CustomPainter { final List<Seat> seats; final Set<String> selectedSeats; @override void paint(Canvas canvas, Size size) { for (final seat in seats) { final paint = Paint() ..color = selectedSeats.contains(seat.id) ? Colors.blue : seat.isAvailable ? Colors.green : Colors.grey; canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromLTWH(seat.x, seat.y, 28, 24), const Radius.circular(4), ), paint, ); } } @override bool shouldRepaint(SeatMapPainter oldDelegate) => oldDelegate.selectedSeats != selectedSeats; } + InteractiveViewer (iOS).
החזרים והעברות
לוגיקת ההחזרים היא בצד השרת. האפליקציה מציגה סטטוסים: UIPinchGestureRecognizer, CATransform3D, ACTIVE, USED. כאשר אירוע נדחה, השרת שולח push → האפליקציה מעדכנת את נתוני הכרטיס. מקרה קצה חשוב: כרטיס חייב להישאר גלוי גם לאחר REFUNDED — משתמשים רוצים לראות את היסטוריית הביקורים שלהם.
שלבי פיתוח
- ניתוח ועיצוב ארכיטקטורה (API, סכמות נתונים, פרוטוקולים)
- פיתוח צד לקוח (iOS / Android / חוצה פלטפורמות)
- אינטגרציית Wallet והתראות push
- הגדרת אימות (HMAC, QR מסתובב)
- בדיקות על מכשירים אמיתיים ובדיקות בטא (TestFlight / Firebase)
- תיעוד API והוראות לצוות הבקרים
- תמיכת אחריות ל-30 יום לאחר השחרור
פרטים נוספים
לצורך קנה מידה למאגרי משתמשים גדולים, אנחנו משתמשים במטמון בצד הלקוח ובצד השרת. לטעינה מהירה יותר של מפת המושבים, אנחנו מיישמים טעינה עצלה לפי אזורים.
תוצרים
- תיעוד API מלא והוראות לבקרים
- קוד מקור עם הערות
- גישה למאגר וצינור CI/CD
- הכשרת צוות על פאנל הניהול
- גישה לפאנל ניהול עם אנליטיקה בזמן אמת
- תמיכת אחריות ל-30 יום לאחר השחרור
- אופטימיזציה וניטור לאחר ההשקה
למה להזמין פיתוח מאיתנו?
- ניסיון: 5+ שנים בפיתוח מובייל, 20+ פרויקטי כרטוס שהושלמו, 50+ לקוחות גלובליים
- מפתחים מוסמכים של Apple ו-Google (iOS/Android)
- טכנולוגיה מודרנית: Swift 5.9, Kotlin, Flutter 3.x
- הבטחת אבטחה: כל הטוקנים חתומים, נתונים מוצפנים
- לוח זמנים שקוף: גרסת בסיס תוך 4–6 שבועות; הערכה מדויקת לאחר בריף
- SLA של 99.9% זמינות על תשתית השרת
- תמיכה לאחר ההשקה עם מענה לאירועים 24/7
צור קשר כדי לדון בפרויקט שלך ולקבל ייעוץ. בקש בריף — נעריך אותו ביום אחד.
הערכות לוח זמנים
| רכיב | זמן |
|---|---|
| גרסת בסיס (QR בתוך האפליקציה, רכישה, היסטוריה) | 4–6 שבועות |
| הוספת מפת מושבים עם בחירת מושב | +2–3 שבועות |
| אינטגרציית Wallet (Apple / Google) | +3–5 ימים |
| QR מסתובב ואימות לא מקוון | +1–2 שבועות |
העלות נקבעת באופן אישי — כתוב לנו, נעריך ביום אחד. חיסכון טיפוסי מהגישה האופטימלית שלנו: 30% בעלויות אימות ו-40% באינטגרציה מרובת ספקים.







