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:

  1. Instruct your users to initiate a transaction of R0.01 from their account to our designated pix token [email protected].
  2. 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:

  1. Call instantiateMFAFlow to create the pre-built UX flow.
  2. Set the hostingController property to the current view controller if using a navigation controller.
  3. Provide a closure to be called when the 2FA process is successful.
  4. 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 address
  • documentImage :(Data) raw png data from the users proof of address picture
  • address: (String) The user's cmorq wallet address.
  • cpf: (String) The user's CPF number.
  • completion : ((Result<KyCAddressResponseModel, CMKYCSDKError>) -> Void) completion result that returns either a KyCAddressResponseModel or a CMKYCSDKError 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 status
  • accepted(Bool): if the proof of address was accepted
  • report (String): message describing what the obtained result was, both for approval and rejection
  • response_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:

  1. Assign values to the wallet and cpf variables.
  2. Define closure functions for success (onSuccess), failure (onFailure), and selfie image capture (selfieImageClosure).
  3. Call instantiateIDandSelfieFlow to create the KYC flow, providing the wallet address, CPF, and the defined closures.
  4. 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 a HawkInboundModel 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.