SwiftID KYC SDK Integration Guide
This guide details the integration process for the SwiftID KYC SDK for developers.
Supported KYC Flows
The SwiftID KYC SDK supports the following flows:
Authentication token
Create Wallet
Assign ID (CPF)
Name & ID Number Verification
Identification Document & Selfie Verification
Two-Factor Authentication
Proof of Address
Salary verification (Coming soon)
The following sections detail the usage of each implemented flow.
SDK Initialization
- Locate the
GoogleService-Info.plist
file at the root of the SDK. - This file contains a
BUNDLE_ID
entry corresponding to the app identifier using the SDK which will grant you access to our database upload system so that users can upload their documentation. This is necessary for uploading files such as ID images and the Selfie identification image as well as the proof of physical address. - Replace the value of this entry with your project's bundle identifier found in the Signing and Capabilities tab of your app target configuration.
- This identifier is required by SwiftID to grant access to the database upload system for user documentation (ID images, selfie, proof of address). So please provide us with this bundle identifier. This is necessary for uploading files such as ID images and the Selfie identification image aswell as the proof of physical address.
Once the BUNDLE_ID
is configured, first import the SDK Module into all the files you intend to use it in, by doing:
import CMKyCSDK
instantiate the SDK in the AppDelegate.swift
file, inside the method application(_ application: didFinishLaunchingWithOptions:)
using the provided user and user token:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
KyCModule.initiateSDK(user: <userID>, apiToken: <apiToken>)
return true
}
Create Wallet
This flow generates a non-custodial cmorq EVM address. The network SDK call returns a KyCCreateWalletModel
object through a completion closure:
public struct KyCCreateWalletModel: Codable {
public var message: String = ""
public var status: Int = 0
public var address: String = ""
public var mnemonic: String = ""
}
To create a new wallet:
KyCModule.instanciateCreateWalletCall { result in
switch result {
case let .success(walletModel):
// Access the wallet address from walletModel.address
print(walletModel.address)
case let .failure(error):
print(error)
}
}
We also have the option to use a pre done UX flow, which will allow the user to create a wallet by simply calling the following method.
KyCModule.instantiateWalletUXFlow(completion: ((Result<KyCCreateWalletModel, CMKYCSDKError>) -> Void)? = nil) -> UIViewController
Which contains a completion closure similar to the previous manual flow, but at the same time, it returns a UIViewController where the developer has a pre-made UX that includes a button for creating the wallet, and then for copying it to clipboard.
Usage is as follows:
let vc = KyCModule.instantiateWalletUXFlow { result in
switch result {
case let .success(walletModel):
// Wallet is walletModel.address
print("#### wallet is: \(walletModel.address)")
case let .failure(error):
print(error)
}
}
self.navigationController?.pushViewController(vc, animated: true)
Assign CPF
This flow binds an existing cmorq wallet with a provided CPF (Cadastro de Pessoas Físicas - Brazilian tax ID).
- Ensure the user enters a valid CPF and the wallet is a valid cmorq wallet. Currently, the SDK performs no validation for either.
Binding, we make use of the method addAndBindCPF
which takes in a cmorq address and a CPF as parameters to bind them. It returns a CMKYCSDKModel
object in the completion closure.
public struct CMKYCSDKModel: Codable {
public let message: String
public let status: Int
public let does_cpf_exist: Bool
enum CodingKeys: String, CodingKey {
case message
case status
case does_cpf_exist
}
}
The does_cpf_exist
flag indicates if the CPF is already bound to another cmorq wallet.
KyCModule.instantiateAddBindCPF(address: address, cpf: cpf) { result in
switch result {
case let .success(model):
// Check 'model.does_cpf_exist' for successful binding.
print(model)
case let .failure(error):
// Handle error
print(error)
}
}
Get Entry by ID
This library method allows the user to provide a wallet address and retrieve all the available information about that user. The way to use this method is by calling the following:
let address: String = <Some address>
let completion: ((Result<KYCEntryModelsResponse, Error>) -> Void)? = { result in
switch result {
case let .success(model):
for entry in model.entries {
print(entry.fullName)
print(entry.walletAddress)
print(entry.taxID)
.......
}
case let .failure(error):
print(error)
}
CMKYCSDKService.shared.getEntryByID(address: address, completion: completion)
Where the model
is KYCEntryModelsResponse
which contains all the existing entries of KYCEntryModel
for the given address.
public struct KYCEntryModelsResponse: Codable {
public let entries: [KYCEntryModel]
public let message: String
public let status: Int
}
Then KYCEntryModel
is:
public struct KYCEntryModel: Codable {
public let cpfHash: String?
public let createdAt: String?
public let dob: String?
public let fullName: String?
public let imageBack: String?
public let imageBackDirect: String?
public let imageFront: String?
public let imageFrontDirect: String?
public let imageSelfie: String?
public let kycVerified: Bool?
public let kycVerifiedAI: Bool?
public let taxId: String?
public let walletAddress: String?
....
}
While this library API does not have a pre-made UX flow (since this library method can return an entry with many different attributes per address), there is an implementation example printing an entry that can be found in the Demo App shipping alongside this SDK.
Verify Name and ID
To ensure seamless identity verification for our platform, we kindly request that you follow these simple steps with your users:
- Instruct your users to initiate a transaction of R0.01 from their account to our designated pix token [email protected].
- Emphasize the importance of using an account linked to their CPF for this transaction.
This straightforward process triggers the verification mechanism for their Full Name and CPF within our system. It securely locks and confirms their details, enabling us to validate their identity accurately. By cross-referencing the CPF provided during registration with the one used for the transaction, we ensure a robust verification process that enhances security and trust in our platform. Thank you for your cooperation in maintaining the integrity of our services.
Two-Factor Authentication
This flow allows for two different instantiations, one which is our pre-made UX flow that allows the user to simply instantiate the flow, input their phone number, and verify their phone number through 2FA. This flow requires a completion closure to be passed as a parameter, this closure should look like:
let onMFASuccessDo: ((String, String) -> Void)
Where the return type is a tuple containing two strings, the first of them being the phone number that was used, and the second string being the OTP number that was successfully verified.
1. Pre-built User Experience (UX) Flow
The SDK provides a pre-built UX flow for 2FA that simplifies the process for developers. Here's how to use it:
let vc = KyCModule.instanciateMFAFlow(walletAddress: "",
hostingController: self,
onMFASuccessDo: onMFASuccessDo)
self.navigationController?.pushViewController(vc, animated: true)
walletAddress
: (String) The user's cmorq wallet address.hostingController
: (UIViewController) The view controller initiating the flow (usually the current view controller). (This field is nullable, and not required if not instantiating from a navigation controller)onMFASuccessDo
: (phoneNumber: String, otpCode: String) A closure to be executed upon successful 2FA completion returning phone number and otp code.
Steps:
- Call
instantiateMFAFlow
to create the pre-built UX flow. - Set the
hostingController
property to the current view controller if using a navigation controller. - Provide a closure to be called when the 2FA process is successful.
- Present the created view controller using
pushViewController
.
2. Manual Flow with sendMFA
and verifyMFA
Another way of working with this API involves utilizing two methods: one to send the MFA and another to verify it. These methods must be executed sequentially. First, we confirm that the MFA message has been sent, indicating backend confirmation of the OTP message transmission. Subsequently, we initiate the verification process by calling the 'verify' method, passing both the OTP received by the user and the user's phone number
These methods are called sendMFA
and verifyMFA
.
The sendMFA
method returns the KyCSend2FAModel
. In this response, we analyze the message
and status
fields. If the status= 200
and the message= "Sent MFA"
this means that the MFA sms was sent successfully. If the sending of the SMS failed, the message
will not be "Sent MFA"
. This may be due to the number being invalid or not existing.
Once the sendMFA
has concluded with a successful result, we then call the verifyMFA
method, which will return a KyCVerify2FAModel
. This model will return a property is_valid
which if true, concludes our verification flow.
a. Sending the MFA Code
- Use
CMKYCSDKService.shared.sendMFA
to initiate the 2FA process.
CMKYCSDKService.shared.sendMFA(phone: phoneNumber) { result in
switch result {
case let .success(smsResponseModel):
// Check for successful message sending (status code 200 and message "Sent MFA")
if smsResponseModel.response_info.message == "Sent MFA" && smsResponseModel.response_info.status == 200 {
// MFA sent, proceed to verification
// ...
}
}
}
phone
: (String) The user's phone number.
b. Verifying the MFA Code
- Once the MFA code is received by the user, use
CMKYCSDKService.shared.verifyMFA
to validate it.
CMKYCSDKService.shared.verifyMFA(phone: phoneNumber, token: otpToken, address: self.walletAddress) { result in
switch result {
case let .success(verifyMFAResponseModel):
if verifyMFAResponseModel.response_info.is_valid {
// 2FA verification successful
// ...
}
}
}
phone
: (String) The user's phone number.token
: (String) The one-time password (OTP) received by the user.address
: (String) The user's cmorq wallet address.
Response Models:
KyCSend2FAModel
: This model indicates successful sending of the MFA code.
public struct KyCSend2FAModel: Codable {
public let message: String
public let status: Int
public let sid: String
}
KyCVerify2FAModel
: This model indicates the result of the MFA code verification.
public struct KyCVerify2FAModel: Codable {
public let message: String
public let status: Int
public let is_valid: Bool
}
Proof of Address
This section explains the proof of address
method.
The method has the signature:
public func submitProofOfAddress(physicalAddress: String, documentImage: Data, address: String, cpf: String, completion: ((Result<KyCAddressResponseModel, CMKYCSDKError>) -> Void)? = nil)
this method's arguments are:
physicalAddress
:(String) the users physical addressdocumentImage
:(Data) raw png data from the users proof of address pictureaddress
: (String) The user's cmorq wallet address.cpf
: (String) The user's CPF number.completion
: ((Result<KyCAddressResponseModel, CMKYCSDKError>) -> Void) completion result that returns either aKyCAddressResponseModel
or aCMKYCSDKError
where the model is:public struct KyCAddressResponseModel: Codable { public let report: String public let status: Int public let accepted: Bool public let response_message: LanguageTextModel public struct LanguageTextModel: Codable { var en: String = "" var pr: String = "" var sp: String = "" func localized(_ language: KyCSDKLanguage = getLanguage()) -> String { switch language { case .english: return en case .spanish: return sp default: return pr } } private static func getLanguage() -> KyCSDKLanguage { // print("#### device languages: \(Locale.preferredLanguages)") guard let deviceLanguage = Locale.preferredLanguages.first else { return .portuguese } switch deviceLanguage { case let str where str.contains("en"): return .english case let str where str.contains("es"): return .spanish case let str where str.contains("pt"): return .portuguese default : return .english } } } }
The parameters from this model response are:
status
(Int): network response statusaccepted
(Bool): if the proof of address was acceptedreport
(String): message describing what the obtained result was, both for approval and rejectionresponse_message
(String) : message in multiple languages showing a user-friendly message regarding the status of the response.
ID & Selfie Verification
This section explains the two methods for implementing ID and Selfie verification with the SwiftID KYC SDK.
1. Pre-built User Experience (UX) Flow
The SDK provides a pre-built UX flow that guides the user through the ID and Selfie verification process.
Using this flow, a new ViewController is created where you must provide the address, cpf, and two callbacks: one for success and one for failure. Both callbacks will return a message stating the status of the Hawk Verification Process, for both success and failure cases. The selfieSuccess callback will provide the saved selfie image back to the user for them to use it.
Here's how to use it:
let wallet = "<some wallet>"
let cpf = "<someCPFNumber>"
// Define callbacks for success, failure, and selfie image capture
let onSuccess: (String) -> Void = { message in
// Code to execute upon successful verification
}
let onFailure: (String) -> Void = { message in
// Code to execute upon verification failure
}
let selfieImageClosure: (Data?) -> Void = { selfieData in
// Code to handle captured selfie image data
}
// Create the KYC flow using the provided information and callbacks
let kycFlow = KyCModule.instantiateIDandSelfieFlow(walletAddress: wallet,
cpf: cpf,
onSuccess: onSuccess,
onFailure: onFailure,
selfieImageClosure: selfieImageClosure,
hostingViewController: self))
// Present the KYC flow to the user
self.navigationController?.pushViewController(kycFlow, animated: true)
Steps:
- Assign values to the
wallet
andcpf
variables. - Define closure functions for success (
onSuccess
), failure (onFailure
), and selfie image capture (selfieImageClosure
). - Call
instantiateIDandSelfieFlow
to create the KYC flow, providing the wallet address, CPF, and the defined closures. - If presenting the KYC flow using navigation controller, please assign the
hostingViewController
field to the ViewController pushing it.
This approach simplifies the integration process by handling the UI flow within the SDK.
Another way to use this SDK is by manually using the submitID
method which takes in an address, cpf, and then the raw data for the three images, the front id image, the back id image and the selfie id image, lastly it also takes in a completion handler that should look like:
let completion: ((Result<HawkInboundModel, CMKYCSDKError>) -> Void)
Where the result type is the HawkInboundModel
.
2. Manual Flow with submitID
For more control, you can use the submitID
method to handle image data and verification logic manually.
let address: String = "<Some user wallet>"
let cpf: String = "<Some user CPF>"
let frontImageData: Data = <front image data>
let backImageData: Data = <back image data>
let selfieImageData: Data = <selfie image data>
let completion = { (response: HawkInboundModel) in
// Check response.message for backend message and response.accepted for verification status
}
CMKYCSDKService.shared.submitID(address: address, cpf: cpf, frontImageData: frontImageData, backImageData: backImageData, selfieImageData: selfieImageData, completion: completion)
address
: (String) The user's cmorq wallet address.cpf
: (String) The user's CPF number.frontImageData
: (Data) The data of the user's ID front side image.backImageData
: (Data) The data of the user's ID back side image.selfieImageData
: (Data) The data of the user's selfie image.completion
: (Closure) A closure that receives aHawkInboundModel
object containing the verification response.
Hawk Response Model:
HawkInboundModel
: This model contains the response from the HAWK verification service.
public struct HawkInboundModel: Codable {
let message: String
let status: Int
let accepted: Bool
// ... (other response properties)
}
message
: (String) A message from the backend regarding the verification.status
: (Int) The HTTP status code of the response.accepted
: (Bool) A boolean indicating whether the verification was successful.
Once the Hawk Process has ended, the HawkInboundModel
will be returned inside the completion closure. If the accepted
boolean is true
, the Id process has concluded, meaning that the user has been successfully KYC'd. In case of a false
result, it means that compliance personnel must manually verify the sent ID, since the Hawk System Verification has failed to validate the user.
Choose the method that best suits your development needs. The pre-built UX flow offers a user-friendly experience, while the manual flow provides more control over the verification process.