Skip to content

Xác thực 3DS — iOS ​

Cake 3DS SDK cho iOS (Cake3DS2) triển khai giao thức EMV 3-D Secure 2.x cho việc xác thực chủ thẻ. Phần lõi của SDK được viết bằng Objective-C, với các public header mà code Swift có thể gọi trực tiếp. SDK thu thập và mã hóa dữ liệu thiết bị. Nó trao đổi các thông điệp thử thách CReq và CRes. Nó hiển thị giao diện thử thách. Nó xử lý các lỗi giao thức. Đặc tả EMVCo yêu cầu SDK giao tiếp trực tiếp với Directory Server (DS) và Access Control Server (ACS).

Chi tiết SDK

Tên packageCake3DS2
Phân phốiSwift Package Manager, hoặc một file XCFramework trực tiếp
NamespaceModule Cake3DS2, các lớp công khai tiền tố CKDS
Giao thứcEMV 3DS 2.1.0, 2.2.0, 2.3.0, và 2.3.1
iOS tối thiểu13.0
Ngôn ngữObjective-C (phần lõi SDK), có thể gọi từ Swift

Cài đặt ​

SDK hỗ trợ hai phương thức cài đặt: Swift Package Manager, hoặc một file XCFramework trực tiếp.

Phương án 1: Swift Package Manager (khuyến nghị) ​

Bước 1. Thêm package Cake3DS2 vào dự án của bạn. Sử dụng đường dẫn package hoặc URL mà Cake cung cấp.

swift
// Package.swift
dependencies: [
    .package(path: "/path/to/cake-3ds-swift-package") // hoặc một git URL mà Cake cung cấp
]

Bước 2. Thêm Cake3DS2 làm dependency cho target app của bạn.

swift
.target(
    name: "YourApp",
    dependencies: ["Cake3DS2"]
)

Nếu bạn dùng Xcode thay vì file Package.swift, hãy thêm package qua File > Add Package Dependencies.

Phương án 2: File XCFramework trực tiếp ​

Bước 1. Sao chép file Cake3DS2.xcframework từ Cake vào dự án của bạn.

Bước 2. Thêm file vào target app của bạn. Trong phần cài đặt General của target, dưới "Frameworks, Libraries, and Embedded Content", đặt thành "Embed & Sign".

File XCFramework cũng cần ba system framework. Thêm các mục sau vào cùng danh sách trong target app của bạn:

UIKit.framework
Security.framework
WebKit.framework

Khởi tạo SDK ​

Tạo một instance CKDSThreeDS2Service cho mỗi phiên app. Tạo nó khi app khởi chạy, hoặc trong module dependency injection của bạn. SDK cung cấp hai phương thức khởi tạo.

Phương thức đầu tiên chỉ sử dụng các cài đặt mặc định:

swift
import Cake3DS2

let service = CKDSThreeDS2Service()
let config = CKDSConfigParameters(standardParameters: ())

service.initialize(withConfig: config, locale: nil, uiSettings: nil)

Sử dụng phương thức thứ hai khi bạn cần một analytics delegate:

swift
service.initialize(
    withConfig: config,
    locale: nil,
    uiSettings: uiCustomization,
    analyticsDelegate: myAnalyticsDelegate
)
Tham sốKiểuBắt buộcMô tả
configCKDSConfigParametersCóĐối tượng cấu hình. Dùng CKDSConfigParameters(standardParameters: ()) trừ khi Cake cung cấp thêm tham số để bạn thêm vào.
localeLocale?KhôngGhi đè locale cho giao diện thử thách. Truyền nil để dùng locale hệ thống.
uiSettingsCKDSUICustomization?KhôngTùy chỉnh giao diện cho các màn hình thử thách (xem Tùy chỉnh giao diện thử thách). Truyền nil để dùng CKDSUICustomization.defaultSettings().
analyticsDelegateCKDSAnalyticsDelegateChỉ ở phương thức thứ haiDelegate nhận các sự kiện analytics của thử thách, ví dụ một lần nhấn submit OTP
Thành viênKiểuMô tả
warnings[CKDSWarning]?Các cảnh báo phát hiện được trong quá trình khởi tạo, ví dụ thiết bị đã jailbreak hoặc có debugger đính kèm

Cake3DS2 không có phương thức cleanup ở cấp độ service. Thay vào đó, mỗi CKDSTransaction tự dọn dẹp tài nguyên riêng (xem Tạo một transaction).

Tạo một transaction ​

Với mỗi thanh toán cần xác thực 3DS, tạo một CKDSTransaction. Cake3DS2 có chứng chỉ tích hợp sẵn cho bảy mạng thẻ. Chỉ cần truyền directoryServerID cho các mạng này:

swift
let transaction = service.createTransaction(
    forDirectoryServer: directoryServerId,   // ví dụ "A000000003" cho Visa
    withProtocolVersion: nil                 // nil = SDK tự chọn giá trị mặc định (2.2.0)
)
Tham sốKiểuMô tả
directoryServerIDStringDirectory Server ID của mạng thẻ (xem bảng bên dưới)
protocolVersionString?Phiên bản giao thức cần dùng. Truyền nil để dùng giá trị mặc định của SDK.

Đối với một mạng ngoài danh sách tích hợp sẵn, hãy tự cung cấp chứng chỉ của riêng bạn thay thế:

swift
let transaction = service.createTransaction(
    forDirectoryServer: directoryServerId,
    serverKeyID: serverKeyId,                // key tùy chọn từ DS
    certificateString: certificateString,    // PEM hoặc DER mã hóa base64, public key của DS
    rootCertificateStrings: rootCertStrings, // PEM hoặc DER mã hóa base64, chứng chỉ gốc của DS-CA
    withProtocolVersion: nil
)
Tham sốKiểuMô tả
directoryServerIDStringDirectory Server ID được trả về trong response xác thực
serverKeyIDString?Key xác thực bổ sung mà một số Directory Server yêu cầu
certificateStringStringChứng chỉ PEM hoặc DER mã hóa base64 chứa public key của DS
rootCertificateStrings[String]Các chứng chỉ gốc PEM hoặc DER của DS-CA mã hóa base64, dùng để kiểm tra nội dung đã ký bởi ACS
protocolVersionString?Phiên bản giao thức cần dùng. Truyền nil để dùng giá trị mặc định của SDK.

Tùy chỉnh giao diện

Truyền uiSettings một lần, cho service.initialize(withConfig:locale:uiSettings:). createTransaction không nhận tham số tùy chỉnh giao diện. Mọi transaction từ cùng một service instance chia sẻ chung một tùy chỉnh. Xem Tùy chỉnh giao diện thử thách.

Directory Server ​

Cake3DS2 có chứng chỉ tích hợp sẵn cho các mạng sau:

MạngID
VisaA000000003
MastercardA000000004
American ExpressA000000025
DiscoverA000000324
Cartes BancairesA000000042
Test (RSA)F055545342
Test (EC)F155545342

Đối với một mạng ngoài danh sách này, hãy tự cung cấp certificateString và rootCertificateStrings của riêng bạn cho createTransaction(forDirectoryServer:serverKeyID:certificateString:rootCertificateStrings:withProtocolVersion:) thay thế.


Lấy tham số authentication request ​

Trước khi gửi Authentication Request (AReq) đến 3DS Server của bạn, gọi createAuthenticationRequestParameters() cho mỗi transaction:

swift
let params = transaction.createAuthenticationRequestParameters()
// Gửi `params` đến backend của bạn để xây dựng và gửi AReq

Phương thức này đồng bộ. Nó trả về ngay lập tức. Cake3DS2 thu thập dữ liệu thiết bị một lần, trong quá trình initialize(withConfig:locale:uiSettings:). Phương thức này chỉ mã hóa dữ liệu đó, và đóng gói nó, cùng với chi tiết SDK, vào một đối tượng CKDSAuthenticationRequestParameters.

TrườngMô tả
deviceDataThông tin thiết bị, được mã hóa JWE. nil nếu SDK không có dữ liệu thiết bị để báo cáo.
sdkTransactionIdentifierID transaction mà SDK gán cho CKDSTransaction này
sdkAppIdentifierUUID định danh cài đặt app này, được tạo bởi SDK
sdkReferenceNumberĐịnh danh tham chiếu SDK
sdkEphemeralPublicKeyPublic key EC tạm thời của SDK, dưới dạng chuỗi JWK
messageVersionPhiên bản giao thức được chọn cho transaction này

Backend của bạn đưa các trường này vào AReq. Response từ 3DS Server, ARes, cho biết kết quả. Một kết quả frictionless không cần thêm hành động nào từ SDK. Ngược lại, ARes yêu cầu luồng thử thách.


Khởi chạy thử thách ​

Cake3DS2 không dùng một launcher dựa trên request. Thay vào đó, hãy tuân theo protocol CKDSChallengeStatusReceiver. Truyền một đối tượng tuân theo protocol này vào doChallenge(with:challengeParameters:challengeStatusReceiver:timeout:).

swift
import Cake3DS2

extension PaymentViewController: CKDSChallengeStatusReceiver {
    func transaction(_ transaction: CKDSTransaction, didCompleteChallengeWith completionEvent: CKDSCompletionEvent) {
        onAuthenticationSucceeded(completionEvent)
    }

    func transactionDidCancel(_ transaction: CKDSTransaction) {
        onAuthenticationCanceled()
    }

    func transactionDidTimeOut(_ transaction: CKDSTransaction) {
        onAuthenticationTimedOut()
    }

    func transaction(_ transaction: CKDSTransaction, didErrorWith protocolErrorEvent: CKDSProtocolErrorEvent) {
        onProtocolError(protocolErrorEvent.errorMessage)
    }

    func transaction(_ transaction: CKDSTransaction, didErrorWith runtimeErrorEvent: CKDSRuntimeErrorEvent) {
        onRuntimeError(runtimeErrorEvent)
    }
}

Khi ARes yêu cầu một thử thách, xây dựng CKDSChallengeParameters từ nội dung của nó. Sau đó gọi doChallenge(with:challengeParameters:challengeStatusReceiver:timeout:) trên transaction:

swift
import Cake3DS2

let challengeParameters = CKDSChallengeParameters()
challengeParameters.threeDSServerTransactionID = ares.threeDSServerTransID
challengeParameters.acsTransactionID = ares.acsTransID
challengeParameters.acsReferenceNumber = ares.acsReferenceNumber
challengeParameters.acsSignedContent = ares.acsSignedContent

transaction.doChallenge(
    with: presentingViewController,
    challengeParameters: challengeParameters,
    challengeStatusReceiver: self,
    timeout: 5 * 60 // giây. Tối thiểu 5 phút (300 giây).
)
Tham sốKiểuMô tả
presentingViewControllerUIViewControllerView controller mà SDK dùng để hiển thị màn hình thử thách
challengeParametersCKDSChallengeParametersThông tin ACS từ ARes. threeDSServerTransactionID, acsTransactionID, acsReferenceNumber, và acsSignedContent đều là bắt buộc.
challengeStatusReceiverCKDSChallengeStatusReceiverĐối tượng nhận kết quả thử thách (xem bảng bên dưới)
timeoutTimeIntervalThời gian timeout của thử thách, tính bằng giây. Tối thiểu là 5 phút (300 giây).

doChallenge(...) xác thực nội dung đã ký bởi ACS dựa trên các chứng chỉ gốc DS-CA. Nó hoàn tất trao đổi khóa Diffie-Hellman (ECDH P-256). SDK sử dụng trao đổi này để mã hóa và giải mã các thông điệp CReq và CRes tiếp theo. Lệnh gọi duy nhất này chạy toàn bộ luồng thử thách. Nó hiển thị màn hình thử thách, và gọi lại challengeStatusReceiver của bạn khi luồng kết thúc.

Màn hình xử lý

Gọi transaction.createProgressViewController(didCancel:) để lấy một view controller với logo DS và một spinner, không có điều khiển hủy trên màn hình. Hiển thị nó sau khi bạn gửi AReq. Đóng nó khi màn hình thử thách xuất hiện. Đặc tả EMVCo yêu cầu Màn Hình Xử Lý này.


CKDSChallengeStatusReceiver ​

Tuân theo protocol này để nhận trạng thái thử thách khi kết thúc quá trình thử thách:

Phương thứcĐược gọi khi
transaction(_:didCompleteChallengeWith:)Quá trình thử thách đã kết thúc. Kiểm tra completionEvent.transactionStatus để biết kết quả mà ACS gửi trong CRes cuối cùng.
transactionDidCancel(_:)Chủ thẻ chọn hủy transaction trên màn hình thử thách
transactionDidTimeOut(_:)Quá trình thử thách đạt đến khoảng thời gian timeout mà bạn đã truyền cho doChallenge
transaction(_:didErrorWith:) (với một CKDSProtocolErrorEvent)SDK nhận được một thông điệp lỗi giao thức EMV 3-D Secure từ ACS (xem Xử lý lỗi)
transaction(_:didErrorWith:) (với một CKDSRuntimeErrorEvent)SDK phát hiện một lỗi trong quá trình thử thách nằm ngoài các lỗi giao thức ở trên, ví dụ lỗi mạng hoặc lỗi phân tích dữ liệu

Hai phương thức khác là tùy chọn:

Phương thứcĐược gọi khi
transactionDidPresentChallengeScreen(_:)Transaction hiển thị một màn hình thử thách mới
dismissChallengeViewController(_:forTransaction:)Dành riêng cho việc sử dụng nội bộ bởi Cake SDK

Mọi phương thức đều mang theo instance transaction. Nếu app của bạn chạy nhiều hơn một transaction cùng lúc, hãy dùng giá trị này để khớp callback với transaction tương ứng.


Màn hình thử thách ​

Màn hình thử thách 3DS được hiển thị bởi SDK trên iOS

CKDSChallengeResponseViewController hiển thị màn hình này. CKDSUICustomization mà bạn truyền cho initialize(withConfig:locale:uiSettings:) kiểm soát màu sắc, font chữ, và kiểu nút của nó. Xem Tùy chỉnh giao diện thử thách để biết toàn bộ các tùy chọn.


Luồng frictionless so với luồng thử thách ​

stateDiagram-v2
    [*] --> Created: createTransaction(forDirectoryServer:withProtocolVersion:)
    Created --> AuthParams: createAuthenticationRequestParameters()
    AuthParams --> Frictionless: ARes, no challenge required
    AuthParams --> Challenge: ARes, challenge required
    Challenge --> Completed: didCompleteChallengeWith
    Challenge --> Canceled: transactionDidCancel
    Challenge --> Error: didErrorWith (protocol or runtime event)
    Challenge --> TimedOut: transactionDidTimeOut
    Frictionless --> [*]
    Completed --> [*]
    Canceled --> [*]
    Error --> [*]
    TimedOut --> [*]

Các kiểu giao diện thử thách ​

ACS quyết định giao diện nào màn hình thử thách sẽ hiển thị. Cake3DS2 expose giá trị này qua transaction.presentedChallengeUIType, sau khi thử thách hoàn tất:

Giá trịMãYêu cầu nút SubmitMô tả
text01CóNhập văn bản tự do, ví dụ một mã OTP
single_select02CóChọn một lựa chọn từ danh sách
multi_select03CóChọn nhiều lựa chọn từ danh sách
oob04KhôngĐược giải quyết bên ngoài app, ví dụ trong ứng dụng ngân hàng của chủ thẻ
html05KhôngHTML do ACS cung cấp, hiển thị trong một WKWebView
html_oob06KhôngThử thách HTML kết hợp với một hành động tiếp tục out-of-band
information07KhôngMàn hình thông tin với một nút tiếp tục, không có nhập liệu

Xử lý lỗi ​

CKDSProtocolErrorEvent.errorMessage là một đối tượng CKDSErrorMessage. Đối tượng này mô tả lý do CRes thất bại khi kiểm tra hợp lệ:

TrườngMô tả
errorCodeMã lỗi giao thức (xem bảng bên dưới)
errorComponentComponent 3-D Secure đã phát hiện lỗi
errorDescriptionMô tả có thể đọc được bởi con người
errorDetailsChi tiết lỗi bổ sung, nếu có
messageVersionĐịnh danh phiên bản giao thức
acsTransactionIdentifierĐịnh danh transaction do ACS gán, nếu biết
errorMessageTypeLoại thông điệp mà lỗi được phát hiện

Mã lỗi giao thức ​

MãTênNguyên nhân kích hoạt
101InvalidMessageSDK nhận được một thông điệp không phải ARes, CRes, hoặc ErrorMessage
201RequiredDataElementMissingThiếu một trường bắt buộc trong response từ mạng thẻ
202UnrecognizedCriticalMessageExtensionCó một critical message extension không được hỗ trợ
203InvalidDataElementĐịnh dạng hoặc giá trị của một trường không hợp lệ
301TransactionIDNotRecognizedID transaction trong CRes không khớp với CReq
302DataDecryptionFailureViệc giải mã JWE của CRes thất bại
402TimeoutSDK đạt đến thời gian timeout

CKDSErrorMessageCode không định nghĩa hằng số có tên cho mã lỗi 102 (UnsupportedMessageVersion) hoặc 207 (ReservedValueRange). Nếu ACS gửi một trong các mã này, SDK vẫn báo cáo nó dưới dạng chuỗi thuần trong errorCode.

SDK tự động thông báo lỗi cho ACS. Đây là một hành động fire-and-forget. Sau đó, Cake3DS2 gọi transaction(_:didErrorWith:) (với một CKDSProtocolErrorEvent) trên app của bạn. Bạn không cần thực hiện thêm hành động nào khác.

CKDSRuntimeErrorEvent mang theo một chuỗi errorCode thuần và một chuỗi errorMessage, cho các lỗi nằm ngoài bảng giao thức ở trên, ví dụ lỗi mạng hoặc lỗi phân tích dữ liệu. Hai giá trị được biết đến là kCKDSRuntimeErrorCodeParsingError và kCKDSRuntimeErrorCodeEncryptionError.


Tùy chỉnh giao diện thử thách ​

Bạn có thể xây dựng một CKDSUICustomization theo hai cách.

Phương án 1: CKDSUICustomization.defaultSettings() ​

CKDSUICustomization.defaultSettings() cung cấp cho bạn theme design-system của Cake, đã được thiết lập sẵn. Mọi màu sắc, font chữ, và bán kính bo góc đều có giá trị mặc định.

swift
let uiCustomization = CKDSUICustomization.defaultSettings()

Sử dụng tùy chọn này để khớp với giao diện checkout mặc định của Cake. Bạn vẫn có thể ghi đè từng phần riêng lẻ sau đó, như ví dụ bên dưới cho thấy.

Phương án 2: Tùy chỉnh thủ công ​

Tự xây dựng từng đối tượng tùy chỉnh, và gán nó cho một instance CKDSUICustomization mới:

swift
let uiCustomization = CKDSUICustomization()
uiCustomization.navigationBarCustomization = CKDSNavigationBarCustomization.defaultSettings()
uiCustomization.labelCustomization = CKDSLabelCustomization.defaultSettings()
uiCustomization.textFieldCustomization = CKDSTextFieldCustomization.defaultSettings()
uiCustomization.footerCustomization = CKDSFooterCustomization.defaultSettings()
uiCustomization.selectionCustomization = CKDSSelectionCustomization.defaultSettings()

Sử dụng tùy chọn này khi bạn cần toàn quyền kiểm soát màn hình thử thách.

Tùy chỉnh từng phần ​

CKDSUICustomization chứa năm đối tượng lồng nhau: một cho navigation bar, một cho label, một cho text field, một cho footer, và một cho radio button và checkbox. Nút sử dụng một tùy chỉnh riêng, mỗi loại cho một CKDSUICustomizationButtonType. Ví dụ bên dưới bắt đầu từ defaultSettings() (Phương án 1) và ghi đè các phần cụ thể:

swift
import Cake3DS2

let uiCustomization = CKDSUICustomization.defaultSettings()
uiCustomization.backgroundColor = .white

let navigationBar = CKDSNavigationBarCustomization.defaultSettings()
navigationBar.barTintColor = UIColor(red: 0.10, green: 0.10, blue: 0.10, alpha: 1)
navigationBar.headerText = "Secure Checkout"
navigationBar.buttonText = "CANCEL"
navigationBar.textColor = .white
uiCustomization.navigationBarCustomization = navigationBar

let labels = CKDSLabelCustomization.defaultSettings()
labels.headingTextColor = .black
labels.textColor = UIColor(white: 0.2, alpha: 1)
uiCustomization.labelCustomization = labels

let textField = CKDSTextFieldCustomization.defaultSettings()
textField.borderColor = UIColor(white: 0, alpha: 0.5)
textField.borderWidth = 2
textField.cornerRadius = 8
uiCustomization.textFieldCustomization = textField

let submitButton = CKDSButtonCustomization.defaultSettings(for: .submit)
submitButton.backgroundColor = UIColor(red: 1.0, green: 0.42, blue: 0.0, alpha: 1)
submitButton.cornerRadius = 8
uiCustomization.setButton(submitButton, for: .submit)
uiCustomization.setButton(submitButton, for: .next)
uiCustomization.setButton(submitButton, for: .continue)

let cancelButton = CKDSButtonCustomization.defaultSettings(for: .cancel)
cancelButton.backgroundColor = UIColor(red: 0.93, green: 0.28, blue: 0.28, alpha: 1)
uiCustomization.setButton(cancelButton, for: .cancel)

Màu sắc và font chữ

Các setter của Cake3DS2 nhận giá trị UIColor và UIFont gốc, không phải chuỗi hex. Hãy tự xây dựng mỗi UIColor từ helper hex riêng của bạn, hoặc từ UIColor(red:green:blue:alpha:).

CKDSNavigationBarCustomization ​

Thuộc tínhĐặt
barTintColorMàu nền của navigation bar
barStyleKiểu thanh của navigation bar
translucentNavigation bar có trong suốt hay không
scrollEdgeAppearanceGiao diện của navigation bar tại mép cuộn
headerTextVăn bản tiêu đề hiển thị trong navigation bar
buttonTextVăn bản của nút hủy trong navigation bar
font / textColorFont và màu của văn bản tiêu đề và văn bản nút

CKDSLabelCustomization ​

Thuộc tínhĐặt
headingTextColorMàu của văn bản tiêu đề
headingFontFont của văn bản tiêu đề
font / textColorFont và màu của văn bản label chính

CKDSTextFieldCustomization ​

Thuộc tínhĐặt
borderWidthĐộ dày viền của ô nhập liệu
borderColorMàu viền của ô nhập liệu
cornerRadiusBán kính bo góc của ô nhập liệu
keyboardAppearanceGiao diện bàn phím cho ô nhập liệu
placeholderTextColorMàu của văn bản gợi ý (hint)
font / textColorFont và màu của văn bản mà chủ thẻ nhập

CKDSFooterCustomization ​

Thuộc tínhĐặt
backgroundColorMàu nền của footer có thể mở rộng
chevronColorMàu của mũi tên mở/thu gọn
headingTextColorMàu của văn bản tiêu đề footer
headingFontFont của văn bản tiêu đề footer

CKDSSelectionCustomization ​

Thuộc tínhĐặt
primarySelectedColorMàu chính của trạng thái đã chọn
secondarySelectedColorMàu phụ của trạng thái đã chọn, ví dụ dấu tick
unselectedBackgroundColorMàu nền ở trạng thái chưa chọn
unselectedBorderColorMàu viền ở trạng thái chưa chọn

CKDSButtonCustomization ​

Thuộc tínhĐặt
backgroundColorMàu nền của nút
cornerRadiusBán kính bo góc của nút
titleStyleBiến đổi chữ hoa/thường cho tiêu đề nút: .default, .uppercase, .lowercase, hoặc .sentenceCapitalized
font / textColorFont và màu của văn bản nút

Đặt một CKDSButtonCustomization cho mỗi loại nút bạn muốn tùy chỉnh. CKDSUICustomizationButtonType định nghĩa bảy giá trị: .submit, .continue, .next, .cancel, .resend, .openOOBApp, và .addChoice.

Truyền uiCustomization kết quả vào service.initialize(withConfig:locale:uiSettings:).


Sơ đồ luồng đầy đủ ​

sequenceDiagram
    participant App as Merchant App
    participant SDK as 3DS SDK
    participant ACS as Issuer ACS

    App->>SDK: CKDSThreeDS2Service().initialize(withConfig:locale:uiSettings:)
    SDK-->>App: warnings, if any

    App->>SDK: createTransaction(forDirectoryServer:withProtocolVersion:)
    SDK-->>App: CKDSTransaction

    App->>SDK: createAuthenticationRequestParameters()
    SDK-->>App: CKDSAuthenticationRequestParameters

    Note over App: Send AReq to your 3DS Server, receive ARes

    alt Frictionless
        Note over App: ARes indicates no challenge is needed
    else Challenge required
        App->>SDK: doChallenge(with:challengeParameters:challengeStatusReceiver:timeout:)
        SDK->>ACS: Validate ACS-signed content, key exchange, initial CReq
        ACS-->>SDK: Initial CRes

        Note over SDK: Presents the challenge screen

        SDK->>ACS: Remaining CReq and CRes exchange
        ACS-->>SDK: Challenge result
        SDK-->>App: didComplete, didCancel, didTimeOut, or didError on CKDSChallengeStatusReceiver
    end

Developers