İçeriğe geç

Hızlı başlangıç

Bu sayfa en yaygın akışı, Türk kimlik kartı (ön ve arka yüz) ve ardından canlılık adımını baştan sona kurar. Her adım, ayrıntılı anlatıldığı sayfaya bağlantı verir.

Bir entegrasyon üç parçadan oluşur:

  1. Sunucunuz Prove ID’ye giriş yapar, oturum açmış kullanıcınız için işlemi başlatır ve erişim token’ı ile işlem kimliğini uygulamaya döner.
  2. Uygulamanız çekim adımlarını Prove ID iOS SDK ile çalıştırır ve her birini bu token ile Prove ID’ye gönderir.
  3. Sunucunuz işlemin o kullanıcıya ait olduğunu kontrol eder, sonucu Prove ID’den okur ve kararı verir.

Başlamadan önce pod’u, ProveIDApiClient paketini ve kamera iznini Kurulum sayfasında anlatıldığı gibi ekleyin.

Yalnızca uygulamanızda oturum açmış kullanıcıların çağırabileceği kendi uç noktanızı ekleyin; örneğin POST /api/kyc/mobile-session. Bu uç nokta:

  1. POST /oauth/access_token ile erişim token’ı alır (form-encoded, client_credentials grant) ve expires_in dolmadan kısa süre öncesine kadar önbellekte tutar.
  2. Belge türünü ve adımları belirterek POST /id/start ile işlemi başlatır.
  3. transactionId değerini kullanıcınızla ilişkilendirerek kaydeder.
  4. Uygulamaya accessToken, transactionId ve gestureType döner.

Erişim token’ı cihaza ulaştığı için mobil akışta, yalnızca doğrulama adımlarının gerektirdiği yetkilere sahip ayrı bir Prove ID API kullanıcısı kullanın. Bu kullanıcının tanımlanması için Prove ID ekibine başvurun.

// Node.js 18+ (built-in fetch), Express
const API = process.env.PROVEID_API ?? 'https://test.api.proveid.io'; // canlı: https://api.proveid.io
let cached; // { token, expiresAt }
async function accessToken() {
if (cached && cached.expiresAt > Date.now() + 60_000) return cached.token;
const res = await fetch(`${API}/oauth/access_token`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.PROVEID_CLIENT_ID,
client_secret: process.env.PROVEID_CLIENT_SECRET,
}),
});
if (!res.ok) throw new Error(`Prove ID sign-in failed: ${res.status}`);
const data = await res.json();
cached = { token: data.access_token, expiresAt: Date.now() + data.expires_in * 1000 };
return cached.token;
}
// requireUser kendi kimlik doğrulama middleware'iniz; db kendi veri katmanınız.
app.post('/api/kyc/mobile-session', requireUser, async (req, res) => {
const token = await accessToken();
const start = await fetch(`${API}/id/start`, {
method: 'POST',
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
optionalData: { idType: 'IDCARD', controls: ['FRONT', 'BACK', 'LIVENESS'] },
}),
});
if (!start.ok) return res.status(502).end();
const { transactionId, gestureType } = await start.json();
await db.kycSessions.insert({ transactionId, userId: req.user.id });
res.set('Cache-Control', 'no-store').json({ accessToken: token, transactionId, gestureType });
});

Hologram veya NFC adımını eklemek için controls listesine HOLOGRAM veya NFC ekleyin; bkz. İşlem başlatma.

Uygulama açılırken SDK’yı ve servis istemcisini bir kez Prove ID’ye yönlendirin:

AppDelegate.swift
import RKYC_iOS
import ProveIDApiClient
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let serverUrl = "https://test.api.proveid.io" // canlı: https://api.proveid.io
EndpointManager.SERVER_URL = serverUrl
ProveIDApiClientAPI.basePath = serverUrl
return true
}

Kullanıcı doğrulamayı başlattığında kendi oturumunuzla uç noktanızı çağırın ve token’ı yalnızca bellekte tutun:

KYCSession.swift
import Foundation
import ProveIDApiClient
struct KYCSessionInfo {
let transactionId: String
let gestureType: String
}
enum KYCSession {
static let backend = URL(string: "https://your-backend.example.com")!
/// Doğrulamayı sunucunuzda başlatır ve SDK çağrıları için Prove ID token'ını ayarlar.
static func begin(completion: @escaping (Result<KYCSessionInfo, Error>) -> Void) {
var request = URLRequest(url: backend.appendingPathComponent("api/kyc/mobile-session"))
request.httpMethod = "POST"
request.setValue("Bearer \(YourAuth.sessionToken)", forHTTPHeaderField: "Authorization") // uygulamanızın kendi oturumu
URLSession.shared.dataTask(with: request) { data, response, error in
guard error == nil,
(response as? HTTPURLResponse)?.statusCode == 200,
let data,
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let token = json["accessToken"] as? String,
let transactionId = json["transactionId"] as? String else {
completion(.failure(error ?? URLError(.badServerResponse)))
return
}
ProveIDApiClientAPI.customHeaders["Authorization"] = "Bearer \(token)"
let gestureType = json["gestureType"] as? String ?? "EYE"
completion(.success(KYCSessionInfo(transactionId: transactionId, gestureType: gestureType)))
}.resume()
}
/// Akış bittiğinde çağırın (tamamlandı, iptal edildi veya hata oluştu).
static func end() {
ProveIDApiClientAPI.customHeaders.removeValue(forKey: "Authorization")
}
}

Token’ı UserDefaults, dosya veya Keychain’de saklamayın ve log’a yazdırmayın. 401 durumunda ne yapılacağı için bkz. Kimlik doğrulama.

Tek bir view controller iki yüzü de yönetebilir: ön yüz için .NEW_ID, arka yüz için .NEW_ID_BACK algılar.

DocumentCaptureViewController.swift
import UIKit
import ProveIDApiClient
import id_card_detection_ios_wrapper
import id_card_detection_ios_cnn
enum DocumentSide { case front, back }
final class DocumentCaptureViewController: UIViewController {
@IBOutlet weak var containerView: UIView!
var side: DocumentSide = .front
var transactionId = ""
var onFinished: ((Result<Void, Error>) -> Void)?
private var reader: IDCardDetectionWrapper?
private var isSending = false
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let reader = IDCardDetectionWrapper(frame: containerView.bounds)
containerView.addSubview(reader.getView()!)
let conf = IDCardDetectionWrapperConfiguration()
conf.cardTypeList = [side == .front ? .NEW_ID : .NEW_ID_BACK]
conf.messageToEncrypt = transactionId
conf.encryptDeviceId = true
conf.detectBothOrientation = true
reader.setConfiguration(conf: conf)
reader.setDelegate(delegate: self)
reader.setViewDelegate(delegate: self)
reader.setFrameDelegate(delegate: self)
reader.startCapture()
self.reader = reader
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
reader?.stopCapture()
}
private func send(image: UIImage, frame: UIImage?) {
guard !isSending, let png = image.pngData() else { return }
isSending = true
reader?.stopCapture()
let imageBase64 = png.base64EncodedString()
let frameBase64 = frame?.pngData()?.base64EncodedString()
switch side {
case .front:
let body = CheckIDModel(transactionId: transactionId, img: imageBase64, frame: frameBase64)
IDServiceAPI.checkNewIDCard(body: body) { [weak self] result in
self?.finish(result.map { _ in () })
}
case .back:
let body = SecondaryIDInformationModel(image: imageBase64, frame: frameBase64, transactionID: transactionId)
IDServiceAPI.addSecondary(body: body) { [weak self] result in
// NFC adımını sonradan eklerseniz capturedData sno, bdat ve edat değerlerini taşır
self?.finish(result.map { _ in () })
}
}
}
private func finish(_ result: Result<Void, ErrorResponse>) {
DispatchQueue.main.async {
self.isSending = false
self.onFinished?(result.mapError { $0 as Error })
}
}
}
extension DocumentCaptureViewController: IDCardDetectionWrapperDelegate,
IDCardDetectionWrapperViewDelegate,
IDCardDetectionWrapperFrameDelegate {
func cardDetected(cardImage: UIImage, cardType: WrapperCardType, frame: UIImage) {
send(image: cardImage, frame: frame)
}
func cardDetected(image: UIImage, cardType: WrapperCardType) {
// Kullanılmıyor: frame delegate ayarlandığında bunun yerine yukarıdaki metot çağrılır.
}
func cardDetectionFailed(wrapperErrorsModel: WrapperErrorsModel) {
// Kullanıcıyı yönlendirin: yaklaşın, yansımayı önleyin vb. Bkz. Hatalar.
}
func onViewDrawn(mainRect: CGRect, photoRect: CGRect?) { }
}

Önce side = .front ile gösterin; onFinished başarılı dönünce side = .back ile tekrar gösterin. Bir çağrı başarısız olursa kullanıcının yeniden denemesine izin verin (bkz. Hatalar). Diğer seçenekler: Belge çekimi.

  1. adımdan gelen gestureType değerini SDK karşılığına çevirin ve videoyu kaydedin:
LivenessViewController.swift
import UIKit
import RKYC_iOS
import ProveIDApiClient
final class LivenessViewController: UIViewController {
@IBOutlet weak var containerView: UIView!
var transactionId = ""
var gestureType = "EYE"
var onFinished: ((Result<Void, Error>) -> Void)?
private let livenessController = LivenessController()
override func viewDidLoad() {
super.viewDidLoad()
LivenessController.FACE_DETECTION_LAYER_SHAPE = .OVAL
LivenessController.CONTROL_FACE_CAPTURE = true
LivenessController.STOP_VIDEO_WHILE_RECORDING = true
LivenessController.IS_GESTURE_CONTROL_ENABLED = true
LivenessController.VIDEO_PREPARATION_TIME_IN_SECONDS = 3
LivenessController.VIDEO_TIME_IN_SECONDS = 5
switch gestureType {
case "MOUTH": LivenessController.GESTURE_TYPE = .MOUTH
case "HEAD-VERTICAL": LivenessController.GESTURE_TYPE = .HEAD_VERTICAL
case "HEAD-HORIZONTAL": LivenessController.GESTURE_TYPE = .HEAD_HORIZONTAL
case let value where value.hasPrefix("MULTIDIR_"):
let directions = value.dropFirst("MULTIDIR_".count).split(separator: "-")
LivenessController.GESTURE_TYPE = .RANDOMIZED_MULTI_GESTURE
LivenessController.RANDOMIZED_GESTURES = directions.compactMap { HeadPosition(string: String($0)) }
LivenessController.VIDEO_PREPARATION_TIME_IN_SECONDS = 0
default: LivenessController.GESTURE_TYPE = .EYE
}
livenessController.setDelegate(self)
livenessController.setGestureDelegate(self)
containerView.addSubview(livenessController.view)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
livenessController.startCapture()
}
private func send(videoURL: URL, rotate: Int) {
defer { try? FileManager.default.removeItem(at: videoURL) } // videoyu cihazda asla bırakmayın
guard let video = try? Data(contentsOf: videoURL) else { return }
let body = FaceVerificationModel(transactionId: transactionId,
video: video.base64EncodedString(),
rotate: rotate)
FaceServiceAPI.verifyFace(body: body) { [weak self] result in
DispatchQueue.main.async {
self?.onFinished?(result.map { _ in () }.mapError { $0 as Error })
}
}
}
}
extension LivenessViewController: LivenessDelegate, GestureDelegate {
func gestureControlled(gestureModel: GestureModel) {
livenessController.stopCapture()
send(videoURL: gestureModel.videoURL, rotate: gestureModel.rotate)
}
func onVideoCaptured(videoURL: URL, rotate: Int) { }
func onVideoStarting() { /* geri sayım gösterin */ }
func onVideoStarted() { }
func onVideoCanceled() { /* yüz kadrajdan çıktı; kullanıcıdan tekrar denemesini isteyin */ }
func onFaceDetectionFailure(isAway: Bool) { }
func onFaceDetectionSuccess() { }
func onFaceDetectionIdle() { }
func onMultiFaceDetected() { /* kadrajda yalnızca bir kişi olmalı */ }
func positionFailed(model: PositionFailedModel) { }
func positionSucceed(model: PositionSuccedModel) { }
func positionCorrect() { }
func preparationPositionControlled(model: PreparationPositionModel) { }
}

Pasif canlılık, fotoğraftan canlılık ve kimlik kartıyla canlılık Canlılık sayfasında anlatılır.

Canlılık başarılı olunca KYCSession.end() çağırın ve transactionId değerini uygulamanızın oturumuyla kendi uç noktanıza gönderin; örneğin POST /api/kyc/mobile-session/complete. Kullanıcıyı uygulamada onaylamayın: uygulama yalnızca adımların bittiğini bilir.

Sunucunuz önce bu transactionId değerinin 1. adımda bu kullanıcı için kaydedildiğini kontrol eder, ardından sonucu GET /id/id-info-with-tid/{transactionId} ile Prove ID’den okur ve kendi kuralınızı uygular. Aşağıdaki örnek doğrulamayı yalnızca tüm kontroller geçtiyse kabul eder; kendi risk politikanıza göre uyarlayın.

app.post('/api/kyc/mobile-session/complete', requireUser, async (req, res) => {
const { transactionId } = req.body;
const session = await db.kycSessions.findOne({ transactionId, userId: req.user.id });
if (!session) return res.status(404).end(); // bu kullanıcının işlemi değil
const result = await fetch(`${API}/id/id-info-with-tid/${encodeURIComponent(transactionId)}`, {
headers: { Authorization: `Bearer ${await accessToken()}` },
});
if (!result.ok) return res.status(502).end();
const info = await result.json();
const controls = info.controlResults ?? [];
const passed = controls.length > 0 && controls.every((c) => c.entryInterpretation === true);
await db.kycSessions.update({ transactionId }, { passed });
res.json({ passed });
});