אנו מפתחים אפליקציות מובייל הקוראות תגי NFC/RFID עבור קמעונאות, לוגיסטיקה ואימות אותנטיות. אחת הבעיות הנפוצות ביותר היא מוצרים מזויפים הנכנסים למכירה. אימות NFC פותר זאת ללא בקשות שרת: חתימת ECDSA נכתבת לתג, והאפליקציה מאמתת אותה עם מפתח מובנה. קורא ה-NFC המובנה בסמארטפון קורא MIFARE, NTAG, ICODE במרחקים של עד 5 ס"מ—אין צורך בקוראים נוספים. זה אידיאלי לבדיקות נקודתיות של פריטים אך לא לסריקת משטחים בכמות גדולה (הדורשת UHF). לצוות המנוסה שלנו יש מעל 7 שנות ניסיון בפיתוח NFC והוא השלים מעל 100 פרויקטים, תוך הבטחת תמיכה בגרסאות העדכניות של iOS ו-Android. שילוב NFC מפחית הפסדים מזיופים בעד 50,000 דולר בשנה לקמעונאי ממוצע, ומהירות האימות מהירה פי 10 מבדיקות ידניות.
למה לשלב NFC לאימות אותנטיות?
בקמעונאות, זיופים משמעותם הפסדים וסיכונים למוניטין. אימות NFC מדויק פי 10 מ-UHF RFID בקריאת תגים בודדים (99.9% לעומת ~90%). לדוגמה, עבור מותג יוקרה יישמנו אימות לא מקוון על NTAG216: האפליקציה קוראת אסימון חתום ומאמתת את חתימת ECDSA תוך 50–100 אלפיות שנייה. זה חיסל זיופים ללא עלויות תשתית שרת. החיסכון בהחזרות ופגמים הגיע עד 40%. העלות לסריקת תג NFC היא זניחה, מה שהופך אותה לזולה פי 10 מאימות ידני.
איך להכין אפליקציה לשבבים שונים?
- קבע את סוג השבב: NDEF (פרוטוקול עם URL/טקסט) או גולמי (גישה ישירה לזיכרון).
- הגדר את סשן הקריאה: ב-iOS—
NFCNDEFReaderSessionעבור NDEF,NFCTagReaderSessionעבור גולמי; ב-Android—NfcAdapterעם Foreground Dispatch. - עבד את הנתונים: עבור NDEF—חלץ URL וטען מידע; עבור גולמי—קרא UID ונתונים מותאמים אישית.
- אמת חתימה (אם נדרש): אימות ECDSA ללא קריאות שרת.
עבור רשומות NDEF, קריאה אחת מספיקה. עבור שבבים גולמיים, נדרשת תמיכה בפרוטוקולים שונים: ISO 14443 עבור MIFARE ו-NTAG, ISO 15693 עבור ICODE SLI. אנו משתמשים במסגרת CoreNFC ב-iOS וב-NfcAdapter ב-Android.
iOS: CoreNFC
import CoreNFC
class ProductTagReader: NSObject, NFCNDEFReaderSessionDelegate {
private var session: NFCNDEFReaderSession?
var onProductFound: ((ProductInfo) -> Void)?
func startReading() {
guard NFCNDEFReaderSession.readingAvailable else {
showError("NFC недоступен на этом устройстве")
return
}
session = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: false)
session?.alertMessage = "Приложите телефон к метке товара"
session?.begin()
}
func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
for message in messages {
for record in message.records {
guard record.typeNameFormat == .nfcWellKnown,
let type = String(data: record.type, encoding: .utf8),
type == "U" else { continue }
if let urlString = parseNDEFUrl(record.payload),
let url = URL(string: urlString) {
fetchProductInfo(from: url)
}
}
}
}
func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
if let nfcError = error as? NFCReaderError,
nfcError.code != .readerSessionInvalidationErrorFirstNDEFTagRead,
nfcError.code != .readerSessionInvalidationErrorUserCanceled {
showError("Ошибка NFC: \(nfcError.localizedDescription)")
}
}
}import CoreNFC class ProductTagReader: NSObject, NFCNDEFReaderSessionDelegate { private var session: NFCNDEFReaderSession? var onProductFound: ((ProductInfo) -> Void)? func startReading() { guard NFCNDEFReaderSession.readingAvailable else { showError("NFC недоступен на этом устройстве") return } session = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: false) session?.alertMessage = "Приложите телефон к метке товара" session?.begin() } func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) { for message in messages { for record in message.records { guard record.typeNameFormat == .nfcWellKnown, let type = String(data: record.type, encoding: .utf8), type == "U" else { continue } if let urlString = parseNDEFUrl(record.payload), let url = URL(string: urlString) { fetchProductInfo(from: url) } } } } func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) { if let nfcError = error as? NFCReaderError, nfcError.code != .readerSessionInvalidationErrorFirstNDEFTagRead, nfcError.code != .readerSessionInvalidationErrorUserCanceled { showError("Ошибка NFC: \(nfcError.localizedDescription)") } } } — הסשן אינו נסגר לאחר הקריאה הראשונה. שימושי לאימות רציף של מספר פריטים ללא הפעלה מחדש של הסשן.
עבור NTAG/MIFARE ללא NDEF — invalidateAfterFirstRead: false עם NFCTagReaderSession:
func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
guard let tag = tags.first else { return }
session.connect(to: tag) { [weak self] error in
if let error = error {
session.invalidate(errorMessage: "Ошибка: \(error.localizedDescription)")
return
}
switch tag {
case .miFare(let mifareTag):
let uid = mifareTag.identifier.hexString
self?.lookupProduct(uid: uid)
case .iso15693(let isoTag):
let uid = isoTag.identifier.hexString
self?.lookupProduct(uid: uid)
default:
session.invalidate(errorMessage: "Неподдерживаемый тип метки")
}
}
} Android: NFC Foreground Dispatch
class ProductScanActivity : AppCompatActivity() {
private lateinit var nfcAdapter: NfcAdapter
private lateinit var pendingIntent: PendingIntent
private lateinit var filters: Array<IntentFilter>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
nfcAdapter = NfcAdapter.getDefaultAdapter(this) ?: run {
showNoNfcMessage()
return
}
pendingIntent = PendingIntent.getActivity(
this,
0,
Intent(this, javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
PendingIntent.FLAG_MUTABLE
)
filters = arrayOf(IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED).apply {
addDataType("*/*")
})
}
override fun onResume() {
super.onResume()
nfcAdapter.enableForegroundDispatch(this, pendingIntent, filters, null)
}
override fun onPause() {
super.onPause()
nfcAdapter.disableForegroundDispatch(this)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
when (intent.action) {
NfcAdapter.ACTION_NDEF_DISCOVERED -> handleNdefTag(intent)
NfcAdapter.ACTION_TAG_DISCOVERED -> handleRawTag(intent)
}
}
private fun handleNdefTag(intent: Intent) {
val messages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
?.filterIsInstance<NdefMessage>()
?: return
messages.flatMap { it.records.toList() }
.filter { it.tnf == NdefRecord.TNF_WELL_KNOWN && it.type.contentEquals(NdefRecord.RTD_URI) }
.forEach { record ->
val url = parseNdefUri(record.payload)
viewModel.loadProduct(url)
}
}
}Foreground dispatch מיירט תגי NFC בזמן שהאפליקציה פעילה. בלעדיו, Android מציג דיאלוג מערכת לבחירת אפליקציה.
פורמטי תגים ומה לאחסן עליהם
| שבב | זיכרון | שימוש טיפוסי |
|---|---|---|
| NTAG213 | 144 בתים | URL לדף המוצר |
| NTAG215 | 504 בתים | URL + JSON עם מאפיינים בסיסיים |
| NTAG216 | 888 בתים | נתונים מורחבים, היסטוריה |
| MIFARE Ultralight | 48 בתים | UID בלבד (אין מקום לנתונים) |
| MIFARE Classic | 1 ק"ב | UID ואימות בסיסי |
לאימות אותנטיות: אחסן אסימון חתום על התג; האפליקציה מאמתת את החתימה עם מפתח ציבורי ללא קריאות שרת:
// ECDSA верификация токена с метки
func verifyAuthTag(_ signedPayload: Data) -> Bool {
let publicKey = getEmbeddedPublicKey() // зашит в bundle приложения
return SecKeyVerifySignature(
publicKey,
.ecdsaSignatureMessageX962SHA256,
productId as CFData,
signature as CFData,
nil
)
} מה לשקול בבחירת שיטת קריאה?
NFC לעומת QR: השוואה לאימות מוצר.
| מאפיין | NFC | קוד QR |
|---|---|---|
| מרחק קריאה | עד 5 ס"מ | מ-10 ס"מ |
| הגנה נגד זיופים | חומרה, ECDSA | ניתן להעתיק בקלות |
| קיבולת נתונים | עד 888 בתים | עד 4296 תווים |
| מצב לא מקוון | כן (אימות חתימה) | לא (דורש אינטרנט) |
| קריאת ערימה | לא (יחיד בלבד) | כן |
NFC מנצח באבטחה ואימות לא מקוון: הוא מאובטח פי 5 מקוד QR מכיוון שלא ניתן להעתיק את חתימת ECDSA. QR מצטיין בסריקה בכמות גדולה ובטווח. הבחירה תלויה בתרחיש: לבדיקות אותנטיות נקודתיות—NFC, למלאי מחסן המוני—QR.
מה כלול בפיתוח
- קריאת NDEF (רשומות URL, טקסט)
- גישה גולמית לשבב (UID, נתונים מותאמים אישית)
- אימות חתימת ECDSA לא מקוון
- שילוב CMS למיפוי UID/URL למוצר
- בדיקות יחידה ובדיקות UI לכל התרחישים
- תיעוד: תיאור פרוטוקול, סכמת פריסה
- 3 חודשי תמיכה לאחר המסירה
- עמידה מובטחת בהנחיות iOS App Store ו-Google Play
- צוות מוסמך עם 7+ שנות ניסיון בפיתוח NFC
- מעל 100 פרויקטים שנמסרו לקמעונאות ולוגיסטיקה
לוחות זמנים ועלות
שילוב בסיסי של קריאת NDEF-URL אורך 2–3 ימים. אם נדרשים פרוטוקולים מותאמים אישית, אימות חתימה ושילוב backend, לוחות הזמנים מתארכים ל-1–2 שבועות. העלות מתחילה מ-5,000 דולר לשילוב בסיסי ומחושבת בנפרד לפרויקטים מורכבים. אנו מספקים הצעת מחיר מפורטת לאחר ניתוח הדרישות והמחסן הטכנולוגי שלך.
צור קשר להערכה מפורטת של הפרויקט שלך. הזמן שילוב NFC וקבל אב טיפוס תוך 3 ימים.







