Skip to content

Commit

Permalink
Add Enhanced DocV support (#27)
Browse files Browse the repository at this point in the history
* Add Enhanced DocV support.

* Revert changes in file.

* Add example.

* Update version and ChangeLog.
  • Loading branch information
nii-mants3 authored Sep 27, 2023
1 parent 665af71 commit dbee3fe
Show file tree
Hide file tree
Showing 7 changed files with 282 additions and 18 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [4.0.3] - 2023-09-27
### Changed
- ID type is now optional for docV

### Added
- Enhanced Document Verification to the SmileIdentityCore class.
- Business verification to the SmileIdentityCore class.
- JobType, ImageType, and BusinessVerificationType enum classes
- [examples](/examples) folder
Expand Down
86 changes: 86 additions & 0 deletions examples/enhanced_document_verification.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

use SmileIdentity\JobType;
use SmileIdentity\ImageType;
use SmileIdentity\SmileIdentityCore;

// Autoload the dependencies
require 'vendor/autoload.php';

// See https://docs.smileidentity.com/server-to-server/php/products/document-verification for
// how to setup and retrieve configuation values for the SmileIdentityCore class.

$partner_id = '<Put your partner ID here>';
$default_callback = '<Put your default callback url here>';
// You can download your API key from the Smile Identity portal.
// NOTE: The sandbox and production servers use different API keys.
$api_key = '<Put your base64 encoded API key here>';
// Use '0' for the sandbox (test) server, use '1' for production server
$sid_server = '0';

$sid_core = new SmileIdentityCore(
$partner_id,
$default_callback,
$api_key,
$sid_server
);

// Create required tracking parameters
// Every communication between your server and the Smile Identity servers contain these parameters.
// Use them to match up the job results with the job and user you submitted.
$partner_params = array(
'job_id' => '<put your unique ID for the user here>',
'user_id' => '<put unique job ID here',
'job_type' => JobType::ENHANCED_DOCUMENT_VERIFICATION,
);

// The ID Document Information
$id_info = array(
'country' => '<2-letter country code>',
'id_type' => '<id type>'
);

// Create image list
// image_type_id Integer
// 0 - Selfie image jpg or png (if you have the full path of the selfie)
// 2 - Selfie image jpg or png base64 encoded (if you have the base64image string of the selfie)
// 4 - Liveness image jpg or png (if you have the full path of the liveness image)
// 6 - Liveness image jpg or png base64 encoded (if you have the base64image string of the liveness image)
// 1 - Front of ID document image jpg or png (if you have the full path of the selfie)
// 3 - Front of ID document image jpg or png base64 encoded (if you have the base64image string of the selfie)
// 5 - Back of ID document image jpg or png (if you have the full path of the selfie)
// 7 - Back of ID document image jpg or png base64 encoded (if you have the base64image string of the selfie)
$selfie_filename = 'tmp/selife.jpg'; // Path to selfie image file
$id_card_filename = 'tmp/idcard.jpg'; // Path to idcard image file
$selfie_image_detail = array(
'image_type_id' => ImageType::SELFIE_FILE, // Selfie image jpg or png
'image' => $selfie_filename
);
// ID card image can be omitted if selfie comparison to issuer image is desired
$id_card_image_detail = array(
'image_type_id' => ImageType::ID_CARD_FILE, // ID card image jpg or png
'image' => $id_card_filename
);
$image_details = array(
$selfie_image_detail,
$id_card_image_detail
);

// Create options
$options = array(
// Per job callback. If blank the default_callback is used
'optional_callback' => '',
// Set to true if you want to get the job result in sync (in addition to the result been sent
// to your callback). If set to false, result is sent to callback url only.
'return_job_status' => '<true | false>',
// Set to true to receive all of the updates you would otherwise have received in your callback
// as opposed to only the final result. You must set return_job_status to true to use this flag.
'return_history' => '<true | false>',
// Set to true to receive links to the selfie and the photo
// it was compared to. You must set return_job_status to true to use this flag.
'return_image_links' => '<true | false>'
);

// submit_job returns an array with at least a Boolean using the key "success" and the Smile Identity job number.
// If options.return_job_status is true it will add to the array the returned job_status information.
$result = $sid_core->submit_job($partner_params, $image_details, $id_info, $options);
2 changes: 1 addition & 1 deletion lib/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
class Config
{
const SDK_CLIENT = 'PHP';
const VERSION = '4.0.2';
const VERSION = '4.0.3';
const DEFAULT_JOB_STATUS_TIMEOUT = 20;
const DEFAULT_JOB_STATUS_SLEEP = 2;
const SID_SERVERS = [
Expand Down
3 changes: 3 additions & 0 deletions lib/JobType.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,7 @@ class JobType

// Compares document verification to an id check
const COMPARE_USER_INFO = 9;

// Verifies user selfie with info retrieved from the ID issuing authority.
const ENHANCED_DOCUMENT_VERIFICATION = 11;
}
18 changes: 8 additions & 10 deletions lib/SmileIdentityCore.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,16 +94,14 @@ public function submit_job($partner_params, $image_details, $id_info, $_options)
return $id_api->submit_job($partner_params, $id_info, $options);
}

validateJobTypes(
[
JobType::BIOMETRIC_KYC,
JobType::DOCUMENT_VERIFICATION,
JobType::SMART_SELFIE_AUTHENTICATION,
JobType::SMART_SELFIE_REGISTRATION,
JobType::BUSINESS_VERIFICATION
],
$job_type
);
validateJobTypes(array(
JobType::BIOMETRIC_KYC,
JobType::BUSINESS_VERIFICATION,
JobType::DOCUMENT_VERIFICATION,
JobType::ENHANCED_DOCUMENT_VERIFICATION,
JobType::SMART_SELFIE_AUTHENTICATION,
JobType::SMART_SELFIE_REGISTRATION,
), $job_type);

if ($job_type === JobType::BUSINESS_VERIFICATION) {
return $this->submit_kyb_job($partner_params, $id_info);
Expand Down
12 changes: 8 additions & 4 deletions lib/utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,15 @@ function validateIdParams($id_params, $job_type)
throw new Exception("Please ensure that you send through partner params");
}

$not_doc_or_biz_verification = !in_array($job_type, array(JobType::DOCUMENT_VERIFICATION, JobType::BUSINESS_VERIFICATION));
$not_doc_or_biz_verification = !in_array($job_type, array(JobType::DOCUMENT_VERIFICATION, JobType::ENHANCED_DOCUMENT_VERIFICATION, JobType::BUSINESS_VERIFICATION));
if ($not_doc_or_biz_verification && (!key_exists("entered", $id_params) || strtolower("{$id_params['entered']}") !== "true")) {
return;
}

if ($job_type == JobType::DOCUMENT_VERIFICATION) {
$required_fields = ["country"];
} else if ($job_type == JobType::ENHANCED_DOCUMENT_VERIFICATION) {
$required_fields = ["country", "id_type"];
} else {
$required_fields = ["country", "id_number", "id_type"];
}
Expand Down Expand Up @@ -98,10 +100,12 @@ function validateImageParams($image_details, $job_type, $use_enrolled_image)
$has_selfie = true;
}
}
if ($job_type == JobType::DOCUMENT_VERIFICATION && !$has_id_image){
throw new Exception('You are attempting to complete a job type 6 without providing an id card image');

$is_docv_job = $job_type == JobType::DOCUMENT_VERIFICATION || $job_type == JobType::ENHANCED_DOCUMENT_VERIFICATION;
if ($is_docv_job && !$has_id_image){
throw new Exception("You are attempting to complete a job type $job_type without providing an id card image");
}
if (!($has_selfie || ($job_type == JobType::DOCUMENT_VERIFICATION && $use_enrolled_image))) {
if (!($has_selfie || ($is_docv_job && $use_enrolled_image))) {
throw new Exception('You need to send through at least one selfie image');
}
}
Expand Down
176 changes: 173 additions & 3 deletions tests/SmileIdentityCoreTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,49 @@ public function testSubmitJobShouldRequireIdCardImageForJT6(): void
$this->sic->submit_job($partnerParams, $imageDetails, $idParams, $this->options);
}

/**
* @throws GuzzleException
*/
public function testSubmitJobShouldRequireIdCardImageForJT11(): void
{
$this->expectException(Exception::class);
$this->expectExceptionMessage("You are attempting to complete a job type 11 without providing an id card image");
$resp_body = [
"upload_url" => "https://upload-url.com",
"ref_id" => "212-0000058873-dwhsn9nsax3onnbf8mc1rifirl44z",
"smile_job_id" => "0000058873",
"camera_config" => null,
"code" => 2202
];
$partnerParams = [
"user_id" => "user-id",
"job_id" => "job-id",
"job_type" => JobType::ENHANCED_DOCUMENT_VERIFICATION,
];
$idParams = [
"country" => "NG",
"id_type" => "PASSPORT",
"id_number" => "A00000000",
];
$imageDetails = [["image_type_id" => ImageType::SELFIE_FILE, "image" => "base6image"]];
$signatureKey = $this->sic->generate_signature();
$timestamp = $signatureKey['timestamp'];
$signature = $signatureKey['signature'];
$getStatusResult = '{"timestamp": "' . $timestamp . '", "signature": "' . $signature . '", "job_complete": true, "job_success": false, "code": "2302", "result": {}, "history": [], "image_links": {"selfie_image": "https://selfie-image.com"}}';

$mock = new MockHandler([
new Response(200, [], json_encode($resp_body)),
new Response(200, []),
new Response(200, [], $getStatusResult)
]);

$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$this->sic->setClient($client);

$this->sic->submit_job($partnerParams, $imageDetails, $idParams, $this->options);
}

/**
* @throws GuzzleException
*/
Expand Down Expand Up @@ -256,7 +299,91 @@ public function testSubmitJobShouldRequireCountryInIdInfoForJT6(): void
/**
* @throws GuzzleException
*/
public function testSubmitJobShouldRequireIdTypeInIdInfoForJT6(): void
public function testSubmitJobShouldRequireCountryInIdInfoForJT11(): void
{
$this->expectException(Exception::class);
$this->expectExceptionMessage("Please make sure that country is included in the id_info and has a value");
$resp_body = [
"upload_url" => "https://upload-url.com",
"ref_id" => "212-0000058873-dwhsn9nsax3onnbf8mc1rifirl44z",
"smile_job_id" => "0000058873",
"camera_config" => null,
"code" => 2202
];
$partnerParams = [
"user_id" => "user-id",
"job_id" => "job-id",
"job_type" => JobType::ENHANCED_DOCUMENT_VERIFICATION,
];
$idParams = [
"id_type" => "PASSPORT",
"id_number" => "A00000000",
];
$imageDetails = [["image_type_id" => ImageType::SELFIE_FILE, "image" => "base6image"]];
$signatureKey = $this->sic->generate_signature();
$timestamp = $signatureKey['timestamp'];
$signature = $signatureKey['signature'];
$getStatusResult = '{"timestamp": "' . $timestamp . '", "signature": "' . $signature . '", "job_complete": true, "job_success": false, "code": "2302", "result": {}, "history": [], "image_links": {"selfie_image": "https://selfie-image.com"}}';

$mock = new MockHandler([
new Response(200, [], json_encode($resp_body)),
new Response(200, []),
new Response(200, [], $getStatusResult)
]);

$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$this->sic->setClient($client);

$this->sic->submit_job($partnerParams, $imageDetails, $idParams, $this->options);
}

/**
* @throws GuzzleException
*/
public function testSubmitJobShouldRequireIdTypeInIdInfoForJT11(): void
{
$this->expectException(Exception::class);
$this->expectExceptionMessage("Please make sure that id_type is included in the id_info and has a value");
$resp_body = [
"upload_url" => "https://upload-url.com",
"ref_id" => "212-0000058873-dwhsn9nsax3onnbf8mc1rifirl44z",
"smile_job_id" => "0000058873",
"camera_config" => null,
"code" => 2202
];
$partnerParams = [
"user_id" => "user-id",
"job_id" => "job-id",
"job_type" => JobType::ENHANCED_DOCUMENT_VERIFICATION,
];
$idParams = [
"country" => "NG",
"id_number" => "A00000000",
];
$imageDetails = [["image_type_id" => ImageType::SELFIE_FILE, "image" => "base6image"]];
$signatureKey = $this->sic->generate_signature();
$timestamp = $signatureKey['timestamp'];
$signature = $signatureKey['signature'];
$getStatusResult = '{"timestamp": "' . $timestamp . '", "signature": "' . $signature . '", "job_complete": true, "job_success": false, "code": "2302", "result": {}, "history": [], "image_links": {"selfie_image": "https://selfie-image.com"}}';

$mock = new MockHandler([
new Response(200, [], json_encode($resp_body)),
new Response(200, []),
new Response(200, [], $getStatusResult)
]);

$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$this->sic->setClient($client);

$this->sic->submit_job($partnerParams, $imageDetails, $idParams, $this->options);
}

/**
* @throws GuzzleException
*/
public function testSubmitJobSuccessForJT6(): void
{
$resp_body = [
"upload_url" => "https://upload-url.com",
Expand Down Expand Up @@ -295,6 +422,48 @@ public function testSubmitJobShouldRequireIdTypeInIdInfoForJT6(): void
$this->assertTrue($result["success"]);
}

/**
* @throws GuzzleException
*/
public function testSubmitJobSuccessForJT11(): void
{
$resp_body = [
"upload_url" => "https://upload-url.com",
"ref_id" => "212-0000058873-dwhsn9nsax3onnbf8mc1rifirl44z",
"smile_job_id" => "0000058873",
"camera_config" => null,
"code" => 2202
];
$partnerParams = [
"user_id" => "user-id",
"job_id" => "job-id",
"job_type" => JobType::ENHANCED_DOCUMENT_VERIFICATION,
];
$idParams = [
"country" => "NG",
"id_type" => "PASSPORT"
];
$imageDetails = [["image_type_id" => ImageType::SELFIE_FILE, "image" => "base6image"], ["image_type_id" => ImageType::ID_CARD_FILE, "image" => "base6image"]];
$signatureKey = $this->sic->generate_signature();
$timestamp = $signatureKey['timestamp'];
$signature = $signatureKey['signature'];
$getStatusResult = '{"timestamp": "' . $timestamp . '", "signature": "' . $signature . '", "job_complete": true, "job_success": false, "code": "2302", "result": {}, "history": [], "image_links": {"selfie_image": "https://selfie-image.com"}}';

$mock = new MockHandler([
new Response(200, [], json_encode($resp_body)),
new Response(200, []),
new Response(200, [], $getStatusResult)
]);

$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$this->sic->setClient($client);

$result = $this->sic->submit_job($partnerParams, $imageDetails, $idParams, $this->options);

$this->assertTrue($result["success"]);
}

/**
* @throws GuzzleException
*/
Expand Down Expand Up @@ -334,7 +503,7 @@ public function testSubmitJobShouldRequiresAtLeastOneSelfieForJTOtherThanJT6():
$client = new Client(['handler' => $handler]);
$this->sic->setClient($client);

$this->sic->submit_job($this->partnerParams, $imageDetails, $this->idParams, $this->options);
$this->sic->submit_job($partnerParams, $imageDetails, $idParams, $this->options);
}

public function testGenerateSignature(): void
Expand Down Expand Up @@ -465,10 +634,11 @@ public function testExceptionWhenJobTypeIsInvalid()
{
$expected_values = implode(", ", array(
JobType::BIOMETRIC_KYC,
JobType::BUSINESS_VERIFICATION,
JobType::DOCUMENT_VERIFICATION,
JobType::ENHANCED_DOCUMENT_VERIFICATION,
JobType::SMART_SELFIE_AUTHENTICATION,
JobType::SMART_SELFIE_REGISTRATION,
JobType::BUSINESS_VERIFICATION
));
$this->expectException(Exception::class);
$this->expectExceptionMessage("job_type must be one of $expected_values");
Expand Down

0 comments on commit dbee3fe

Please sign in to comment.