diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 8ef9f42d..1c8a1e9c 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -31,6 +31,36 @@ jobs: - run: composer test + php8-4: + name: Unit Tests php8.4 (php ${{ matrix.php-version }}) + runs-on: ubuntu-latest + # always run on push events + # only run on pull_request_target event when pull request pulls from fork repository + if: > + github.event_name == 'push' || + github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository + strategy: + fail-fast: false + matrix: + php-version: [ 8.4 ] + + steps: + - uses: actions/checkout@v2 + + - uses: shivammathur/setup-php@2.9.0 + with: + php-version: ${{ matrix.php-version }} + + # Remove php-cs-fixer until compatible with PHP 8 + # This step might be removable if php-cs-fixer is compatible with 8.4 by the time this runs + - run: composer remove --dev --no-update --no-interaction friendsofphp/php-cs-fixer + + - run: composer self-update + + - run: composer install --no-interaction --prefer-source --dev + + - run: composer test + php7-4: name: Unit Tests php7.4 (php ${{ matrix.php-version }}) runs-on: ubuntu-latest @@ -109,4 +139,3 @@ jobs: - run: composer install --no-interaction --prefer-source --dev - run: composer test - diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache new file mode 100644 index 00000000..c0dde319 --- /dev/null +++ b/.php-cs-fixer.cache @@ -0,0 +1 @@ +{"php":"8.1.27","version":"3.22.0:v3.22.0#92b019f6c8d79aa26349d0db7671d37440dc0ff3","indent":" ","lineEnding":"\n","rules":{"array_syntax":{"syntax":"short"},"no_unused_imports":true,"ordered_imports":{"imports_order":["const","class","function"]},"php_unit_fqcn_annotation":true,"phpdoc_return_self_reference":true,"phpdoc_scalar":true},"hashes":{"\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder2419\/examples\/digitalidentity\/app\/Http\/Controllers\/IdentityController.php":"369515522c3efd6cd55a8363d4e97c05","\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder4308\/src\/Identity\/Policy\/Policy.php":"e1bca74eaafe5271dd1a38769fe1c3b2","\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder3692\/src\/Identity\/Policy\/PolicyBuilder.php":"88302b88aba33563661d4b989b5dc429","\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder2054\/tests\/Identity\/Policy\/PolicyBuilderTest.php":"a262a261102744a1acf6d5d0b421dc44","\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder5221\/src\/Identity\/ReceiptBuilder.php":"2e6ef33d3401f7cbd36145ad66b3b2ef","\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder289\/examples\/digitalidentity\/app\/Http\/Controllers\/ReceiptController.php":"e79ec7e1511895c954f77c713d435ad2","\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder2348\/tests\/Identity\/ReceiptTest.php":"b602e6828020fef411df597e17fa7c88","\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder4139\/examples\/digitalidentity\/routes\/web.php":"dcdc77843f3e59dd61467a324edf0c77","\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder5396\/src\/Identity\/Receipt.php":"4744c8887009fd9ffbf084f99021eb1c","\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder5193\/src\/Identity\/WrappedReceipt.php":"3a77a22be093a1da75438ea2bb9fcb20","\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder4416\/examples\/digitalidentity\/app\/Http\/Controllers\/AdvancedIdentityController.php":"6b5c23f2ce8da246bc41d136d87ecdb6","\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder5544\/examples\/digitalidentity\/app\/Http\/Controllers\/AdvancedIdentityController.php":"6b5c23f2ce8da246bc41d136d87ecdb6","\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder4533\/src\/Identity\/Policy\/Policy.php":"bea4b7ebb268fca1ad719f933ec82cbd","\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder3559\/tests\/Identity\/Policy\/PolicyBuilderTest.php":"f6d7380ae2db4eca426bb39ccfb3a900","\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder2922\/examples\/digitalidentity\/routes\/web.php":"fdf260e4dfd18c8ba12078943564875a","\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder3947\/src\/Constants.php":"4bb1127c9665c5d0496b90ea3211951d","\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder4440\/src\/Constants.php":"99a3224f6e3fcae067362798bbab64f0","\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder4745\/src\/Constants.php":"afc40e02bdc3a87ff7a874826447c604","\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder1311\/src\/Constants.php":"77a8a39eac3e973495b7719ebd41509e","\/private\/var\/folders\/b6\/tqq9d7y54ll62fjfysz50ry80000gn\/T\/PHP CS Fixertemp_folder1635\/examples\/digitalidentity\/app\/Http\/Controllers\/ReceiptController.php":"10f70ffe111a0030b29762494cc5de7d"}} \ No newline at end of file diff --git a/README.md b/README.md index baf6ffee..e6dadebc 100755 --- a/README.md +++ b/README.md @@ -42,13 +42,13 @@ Add the Yoti SDK dependency: ```json "require": { - "yoti/yoti-php-sdk" : "^4.1" + "yoti/yoti-php-sdk" : "^4.4.0" } ``` Or run this Composer command ```console -$ composer require yoti/yoti-php-sdk "^4.1" +$ composer require yoti/yoti-php-sdk "^4.4.0" ``` ## Setup diff --git a/composer.json b/composer.json index 46af649a..b08dca14 100755 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "yoti/yoti-php-sdk", "description": "Yoti SDK for quickly integrating your PHP backend with Yoti", - "version": "4.2.2", + "version": "4.4.0", "keywords": [ "yoti", "sdk" @@ -9,13 +9,13 @@ "homepage": "https://yoti.com", "license": "MIT", "require": { - "php": "^7.4 || ^8.0 || ^8.1", + "php": "^7.4 || ^8.0 || ^8.1 || ^8.4", "ext-json": "*", "google/protobuf": "^3.10", "phpseclib/phpseclib": "^3.0", "guzzlehttp/guzzle": "^7.0", "psr/http-client": "^1.0", - "psr/http-message": "^1.0", + "psr/http-message": "^2.0", "guzzlehttp/psr7": "^2.4", "ext-openssl": "*" }, @@ -66,4 +66,4 @@ "phpstan/extension-installer": true } } -} \ No newline at end of file +} diff --git a/examples/digitalidentity/README.md b/examples/digitalidentity/README.md index 8faa9b32..9bf2d1b3 100644 --- a/examples/digitalidentity/README.md +++ b/examples/digitalidentity/README.md @@ -18,7 +18,10 @@ This example requires [Docker](https://docs.docker.com/) * Visit [https://localhost:4002](https://localhost:4002) * Run the `docker-compose stop` command to stop the containers. -> To see how to retrieve activity details using the one time use token, refer to the [digitalidentity controller](app/Http/Controllers/IdentityController.php) - +> To see how to retrieve a profile using share receipt, refer to the [receipt controller](app/Http/Controllers/ReceiptController.php) ## Digital Identity Example * Visit [/generate-share](https://localhost:4002/generate-share) +## Digital Identity(Advanced) Share Example +* Visit [/generate-advanced-identity-share](https://localhost:4002/generate-advanced-identity-share) +* ## Digital Identity DBS Example +* Visit [/generate-dbs-share](https://localhost:4002/generate-dbs-share) diff --git a/examples/digitalidentity/app/Http/Controllers/AdvancedIdentityController.php b/examples/digitalidentity/app/Http/Controllers/AdvancedIdentityController.php index cdfda0f6..c084faf2 100644 --- a/examples/digitalidentity/app/Http/Controllers/AdvancedIdentityController.php +++ b/examples/digitalidentity/app/Http/Controllers/AdvancedIdentityController.php @@ -4,12 +4,10 @@ use Illuminate\Routing\Controller as BaseController; use Illuminate\Support\Facades\Log; -use mysql_xdevapi\Exception; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Yoti\DigitalIdentityClient; use Yoti\Identity\Policy\PolicyBuilder; use Yoti\Identity\ShareSessionRequestBuilder; -use Yoti\YotiClient; class AdvancedIdentityController extends BaseController { diff --git a/examples/digitalidentity/app/Http/Controllers/DbsController.php b/examples/digitalidentity/app/Http/Controllers/DbsController.php new file mode 100644 index 00000000..e8e47e74 --- /dev/null +++ b/examples/digitalidentity/app/Http/Controllers/DbsController.php @@ -0,0 +1,53 @@ +withIdentityProfileRequirements((object)[ + 'trust_framework' => 'UK_TFIDA', + 'scheme' => [ + 'type' => 'DBS', + 'objective' => 'BASIC' + ] + ]) + ->build(); + + $redirectUri = 'https://host/redirect/'; + + $shareSessionRequest = (new ShareSessionRequestBuilder()) + ->withPolicy($policy) + ->withRedirectUri($redirectUri) + ->build(); + $session = $client->createShareSession($shareSessionRequest); + return $session->getId(); + } + catch (\Throwable $e) { + Log::error($e->getTraceAsString()); + throw new BadRequestHttpException($e->getMessage()); + } + } + public function show(DigitalIdentityClient $client) + { + try { + return view('dbs', [ + 'title' => 'Digital Identity DBS Check Example', + 'sdkId' => $client->id + ]); + } catch (\Throwable $e) { + Log::error($e->getTraceAsString()); + throw new BadRequestHttpException($e->getMessage()); + } + } +} diff --git a/examples/digitalidentity/app/Http/Controllers/IdentityController.php b/examples/digitalidentity/app/Http/Controllers/IdentityController.php index 72ba9fc3..ca987617 100644 --- a/examples/digitalidentity/app/Http/Controllers/IdentityController.php +++ b/examples/digitalidentity/app/Http/Controllers/IdentityController.php @@ -4,12 +4,10 @@ use Illuminate\Routing\Controller as BaseController; use Illuminate\Support\Facades\Log; -use mysql_xdevapi\Exception; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Yoti\DigitalIdentityClient; use Yoti\Identity\Policy\PolicyBuilder; use Yoti\Identity\ShareSessionRequestBuilder; -use Yoti\YotiClient; class IdentityController extends BaseController { diff --git a/examples/digitalidentity/app/Http/Controllers/ReceiptController.php b/examples/digitalidentity/app/Http/Controllers/ReceiptController.php index 39fe75a6..40299d42 100644 --- a/examples/digitalidentity/app/Http/Controllers/ReceiptController.php +++ b/examples/digitalidentity/app/Http/Controllers/ReceiptController.php @@ -3,12 +3,12 @@ namespace App\Http\Controllers; use Yoti\DigitalIdentityClient; -use Yoti\YotiClient; use Illuminate\Http\Request; use Illuminate\Routing\Controller as BaseController; use Yoti\Profile\Attribute; use Yoti\Profile\UserProfile; use Yoti\Util\Logger; + class ReceiptController extends BaseController { public function show(Request $request, DigitalIdentityClient $client, ?LoggerInterface $logger = null) @@ -17,14 +17,25 @@ public function show(Request $request, DigitalIdentityClient $client, ?LoggerInt $logger->warning("Unknown Content Type parsing as a String"); $shareReceipt = $client->fetchShareReceipt($request->query('ReceiptID')); - - $profile = $shareReceipt->getProfile(); - - return view('receipt', [ - 'fullName' => $profile->getFullName(), - 'selfie' => $profile->getSelfie(), - 'profileAttributes' => $this->createAttributesDisplayList($profile), - ]); + if ($shareReceipt->getError() != null) + { + error_log($shareReceipt->getErrorReason()->getRequirementNotMetDetails()->getDocumentCountryIsoCode()); + return view('receipt', [ + 'fullName' => null, + 'selfie' => null, + 'profileAttributes' => null, + 'error' => $shareReceipt->getErrorReason() + ]); + } + else { + $profile = $shareReceipt->getProfile(); + return view('receipt', [ + 'fullName' => $profile->getFullName(), + 'selfie' => $profile->getSelfie(), + 'profileAttributes' => $this->createAttributesDisplayList($profile), + 'error' => null + ]); + } } /** diff --git a/examples/digitalidentity/resources/views/dbs.blade.php b/examples/digitalidentity/resources/views/dbs.blade.php new file mode 100644 index 00000000..9f325177 --- /dev/null +++ b/examples/digitalidentity/resources/views/dbs.blade.php @@ -0,0 +1,90 @@ + + + + + + + {{ $title }} + + + + + +
+
+
+ + Yoti + +
+

Digital Identity DBS Ckeck Example

+ +
+
+
+ +
+ +
+

The Yoti app is free to download and use:

+ +
+ + Download on the App Store + + + + get it on Google Play + +
+
+
+ + + + + diff --git a/examples/digitalidentity/resources/views/partial/report.blade.php b/examples/digitalidentity/resources/views/partial/report.blade.php index 28bdd3f0..ec1dc60d 100644 --- a/examples/digitalidentity/resources/views/partial/report.blade.php +++ b/examples/digitalidentity/resources/views/partial/report.blade.php @@ -1,39 +1,54 @@ -@foreach ($report as $key => $value) - - - - - - - - @foreach ($value as $name => $result) - @if (is_array($result)) - @foreach ($result as $data => $view) - @if (is_array($view)) - @foreach ($view as $key2 => $value2) - @if (is_array($value2)) - {{json_encode($value2)}} - @else - - - - @endif - @endforeach - @else - - - - @endif - @endforeach - @else - +@if (isset($key) && is_array($key)) + @foreach ($report as $key => $value) +
-

{{ $key }}

-
{{ $key2 }}
{{ $value2 }}
{{ $data }}
{{ $view }}
+ + + + + + + @foreach ($value as $name => $result) + @if (isset($result) && is_array($result)) + @foreach ($result as $data => $view) + @if (is_array($view)) + @foreach ($view as $key2 => $value2) + @if (is_array($value2)) + {{json_encode($value2)}} + @else + + + + @endif + @endforeach + @else + + + + @endif + @endforeach + @else + - - @endif - @endforeach + + @endif + @endforeach - -
+

{{ $key }}

+
{{ $key2 }}
{{ $value2 }}
{{ $data }}
{{ $view }}
{{ $name }}
{{ $result }}
+ + @endforeach +@else + + @foreach ($report as $key => $value) + + + + @endforeach +
+ {{ $key }}
+
+                        {!! json_encode($value, JSON_PRETTY_PRINT) !!}
+                    
+
+@endif diff --git a/examples/digitalidentity/resources/views/receipt.blade.php b/examples/digitalidentity/resources/views/receipt.blade.php index 1fef5463..b166dbdd 100644 --- a/examples/digitalidentity/resources/views/receipt.blade.php +++ b/examples/digitalidentity/resources/views/receipt.blade.php @@ -36,10 +36,37 @@
+ + @if ($error) +
+
Errors
+
+
+
+
Audit Id
+
{{ $error->getRequirementNotMetDetails()->getAuditId() }}
+
+
+
Details
+
{{ $error->getRequirementNotMetDetails()->getDetails() }}
+
+
+
Failure Type
+
{{ $error->getRequirementNotMetDetails()->getFailureType() }}
+
+
+
Document Type
+
{{ $error->getRequirementNotMetDetails()->getDocumentType() }}
+
+
+
Country
+
{{ $error->getRequirementNotMetDetails()->getDocumentCountryIsoCode() }}
+
+ @endif
Attribute
Value
@@ -55,6 +82,7 @@
+ @if(@$profileAttributes) @foreach($profileAttributes as $item) @if ($item['obj'])
@@ -96,6 +124,7 @@
@endif @endforeach + @endif
diff --git a/examples/digitalidentity/routes/web.php b/examples/digitalidentity/routes/web.php index 44933279..f0e02806 100644 --- a/examples/digitalidentity/routes/web.php +++ b/examples/digitalidentity/routes/web.php @@ -18,3 +18,5 @@ Route::get('/generate-session', 'IdentityController@generateSession'); Route::get('/generate-advanced-identity-share', 'AdvancedIdentityController@show'); Route::get('/generate-advanced-identity-session', 'AdvancedIdentityController@generateSession'); +Route::get('/generate-dbs-share', 'DbsController@show'); +Route::get('/generate-dbs-session', 'DbsController@generateSession'); diff --git a/examples/doc-scan/app/Http/Controllers/HomeController.php b/examples/doc-scan/app/Http/Controllers/HomeController.php index 9bd67d01..f116e776 100644 --- a/examples/doc-scan/app/Http/Controllers/HomeController.php +++ b/examples/doc-scan/app/Http/Controllers/HomeController.php @@ -77,28 +77,41 @@ public function show(Request $request, DocScanClient $client) ->withRemoveDeceased(true) ->build(); + //Identity Profile Requeirements Object + /*$identityProfileRequirements = (object)[ + 'trust_framework' => 'UK_TFIDA', + 'scheme' => [ + 'type' => 'DBS', + 'objective' => 'BASIC' + ] + ];*/ $sessionSpec = (new SessionSpecificationBuilder()) ->withClientSessionTokenTtl(600) - ->withResourcesTtl(90000) + ->withResourcesTtl(604800) ->withUserTrackingId('some-user-tracking-id') + //For Identity Profile Requirements Object + //->withBlockBiometricConsent(false) //User needs to provide consent for the liveness detection + //->withIdentityProfileRequirements($identityProfileRequirements) ->withRequestedCheck( (new RequestedDocumentAuthenticityCheckBuilder()) ->build() ) ->withRequestedCheck( (new RequestedLivenessCheckBuilder()) - ->forZoomLiveness() + ->forStaticLiveness() + ->withMaxRetries(3) ->build() ) + /* ->withRequestedCheck( (new RequestedWatchlistAdvancedCaCheckBuilder()) ->withConfig($customConfig) ->build() - ) + )*/ ->withRequestedCheck( (new RequestedFaceMatchCheckBuilder()) - ->withManualCheckAlways() + ->withManualCheckFallback() ->build() ) ->withRequestedCheck( @@ -116,20 +129,20 @@ public function show(Request $request, DocScanClient $client) ) ->withRequestedTask( (new RequestedTextExtractionTaskBuilder()) - ->withManualCheckAlways() + ->withManualCheckFallback() ->withChipDataDesired() ->withCreateExpandedDocumentFields(true) ->build() ) ->withRequestedTask( (new RequestedSupplementaryDocTextExtractionTaskBuilder()) - ->withManualCheckAlways() + ->withManualCheckFallback() ->build() ) ->withSdkConfig( (new SdkConfigBuilder()) ->withAllowsCameraAndUpload() - ->withPrimaryColour('#2d9fff') + ->withPrimaryColour('#2875BC') ->withSecondaryColour('#FFFFFF') ->withFontColour('#FFFFFF') ->withLocale('en-GB') @@ -138,6 +151,7 @@ public function show(Request $request, DocScanClient $client) ->withErrorUrl(config('app.url') . '/error') ->withPrivacyPolicyUrl(config('app.url') . '/privacy-policy') ->withBiometricConsentFlow('EARLY') + ->withBrandId('brand_id') ->build() ) ->withRequiredDocument( diff --git a/examples/doc-scan/resources/views/success.blade.php b/examples/doc-scan/resources/views/success.blade.php index 0e49c218..63b0b486 100644 --- a/examples/doc-scan/resources/views/success.blade.php +++ b/examples/doc-scan/resources/views/success.blade.php @@ -293,7 +293,61 @@ @endif - + @if (isset($sessionResult)) + @if ($sessionResult->getIdentityProfile() != null) + @if ($sessionResult->getIdentityProfile()->getFailureReason() != null) + @if ($sessionResult->getIdentityProfile()->getFailureReason()->getReasonCode()) +
+
+

Identity Result Error

+
+
+ @if ($sessionResult->getIdentityProfile()->getFailureReason()->getReasonCode()) + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Reason Code + {{$sessionResult->getIdentityProfile()->getFailureReason()->getReasonCode()}} +
Failure Type + {{$sessionResult->getIdentityProfile()->getFailureReason()->getRequirementNotMetDetails()->getFailureType()}} +
Details + {{$sessionResult->getIdentityProfile()->getFailureReason()->getRequirementNotMetDetails()->getDetails()}} +
Audit Id + {{$sessionResult->getIdentityProfile()->getFailureReason()->getRequirementNotMetDetails()->getAuditId()}} +
Country ISO Code + {{$sessionResult->getIdentityProfile()->getFailureReason()->getRequirementNotMetDetails()->getDocumentCountryIsoCode()}} +
Document Type + {{$sessionResult->getIdentityProfile()->getFailureReason()->getRequirementNotMetDetails()->getDocumentType()}} +
+ @endif + @endif + @endif + @endif + @endif @if (count($sessionResult->getResources()->getIdDocuments()) > 0)
diff --git a/examples/profile/resources/views/partial/report.blade.php b/examples/profile/resources/views/partial/report.blade.php index 707abe99..d3466436 100644 --- a/examples/profile/resources/views/partial/report.blade.php +++ b/examples/profile/resources/views/partial/report.blade.php @@ -1,40 +1,54 @@ -@foreach ($report as $key => $value) - - - - - - - - @if (isset($value) && is_array($value)) - @foreach ($value as $name => $result) - @if (is_array($result)) - @foreach ($result as $data => $view) - @if (is_array($view)) - @foreach ($view as $key2 => $value2) - @if (is_array($value2)) - {{json_encode($value2)}} - @else - - - - @endif - @endforeach - @else - - - - @endif - @endforeach - @else - +@if (isset($key) && is_array($key)) + @foreach ($report as $key => $value) +
-

{{ $key }}

-
{{ $key2 }}
{{ $value2 }}
{{ $data }}
{{ $view }}
+ + + + + + + @foreach ($value as $name => $result) + @if (isset($result) && is_array($result)) + @foreach ($result as $data => $view) + @if (is_array($view)) + @foreach ($view as $key2 => $value2) + @if (is_array($value2)) + {{json_encode($value2)}} + @else + + + + @endif + @endforeach + @else + + + + @endif + @endforeach + @else + - - @endif + + @endif + @endforeach + + +
+

{{ $key }}

+
{{ $key2 }}
{{ $value2 }}
{{ $data }}
{{ $view }}
{{ $name }}
{{ $result }}
+ @endforeach +@else + + @foreach ($report as $key => $value) + + + @endforeach - @endif -
+ {{ $key }}
+
+                        {!! json_encode($value, JSON_PRETTY_PRINT) !!}
+                    
+
- @endforeach +@endif \ No newline at end of file diff --git a/src/Aml/Profile.php b/src/Aml/Profile.php index b0ee2a10..d03219c3 100644 --- a/src/Aml/Profile.php +++ b/src/Aml/Profile.php @@ -50,7 +50,7 @@ class Profile implements \JsonSerializable * @param \Yoti\Aml\Address $amlAddress * @param null|string $ssn */ - public function __construct($givenNames, $familyName, Address $amlAddress, string $ssn = null) + public function __construct(string $givenNames, string $familyName, Address $amlAddress, ?string $ssn = null) { $this->givenNames = $givenNames; $this->familyName = $familyName; diff --git a/src/Constants.php b/src/Constants.php index e6dd08d8..67486d3d 100644 --- a/src/Constants.php +++ b/src/Constants.php @@ -31,7 +31,7 @@ class Constants public const SDK_IDENTIFIER = 'PHP'; /** Default SDK version */ - public const SDK_VERSION = '4.2.2'; + public const SDK_VERSION = '4.4.0'; /** Base url for connect page (user will be redirected to this page eg. baseurl/app-id) */ public const CONNECT_BASE_URL = 'https://www.yoti.com/connect'; diff --git a/src/DocScan/Session/Create/Check/RequestedLivenessConfig.php b/src/DocScan/Session/Create/Check/RequestedLivenessConfig.php index 050655db..07db4fea 100644 --- a/src/DocScan/Session/Create/Check/RequestedLivenessConfig.php +++ b/src/DocScan/Session/Create/Check/RequestedLivenessConfig.php @@ -23,7 +23,7 @@ class RequestedLivenessConfig implements RequestedCheckConfigInterface */ private $manualCheck; - public function __construct(string $livenessType, int $maxRetries, string $manualCheck = null) + public function __construct(string $livenessType, int $maxRetries, ?string $manualCheck = null) { $this->livenessType = $livenessType; $this->maxRetries = $maxRetries; diff --git a/src/DocScan/Session/Create/ImportTokenBuilder.php b/src/DocScan/Session/Create/ImportTokenBuilder.php index 84340213..b40dd9bf 100644 --- a/src/DocScan/Session/Create/ImportTokenBuilder.php +++ b/src/DocScan/Session/Create/ImportTokenBuilder.php @@ -10,7 +10,7 @@ class ImportTokenBuilder private int $ttl; - public function withTtl(int $ttl = null): ImportTokenBuilder + public function withTtl(?int $ttl = null): ImportTokenBuilder { $this->ttl = $ttl ?? self::DEFAULT_TTL; diff --git a/src/DocScan/Session/Create/SdkConfig.php b/src/DocScan/Session/Create/SdkConfig.php index a3c8086d..43c0498d 100644 --- a/src/DocScan/Session/Create/SdkConfig.php +++ b/src/DocScan/Session/Create/SdkConfig.php @@ -68,6 +68,21 @@ class SdkConfig implements \JsonSerializable */ private $biometricConsentFlow; + /** + * @var string|null + */ + private $darkMode; + + /** + * @var string|null + */ + private $primaryColourDarkMode; + + /** + * @var string|null + */ + private $brandId; + /** * @param string|null $allowedCaptureMethods * @param string|null $primaryColour @@ -81,6 +96,9 @@ class SdkConfig implements \JsonSerializable * @param bool|null $allowHandoff * @param array|null $idDocumentTextDataExtractionRetriesConfig * @param string|null $biometricConsentFlow + * @param string|null $darkMode + * @param string|null $primaryColourDarkMode + * @param string|null $brandId */ public function __construct( ?string $allowedCaptureMethods, @@ -94,7 +112,10 @@ public function __construct( ?string $privacyPolicyUrl = null, ?bool $allowHandoff = null, ?array $idDocumentTextDataExtractionRetriesConfig = null, - ?string $biometricConsentFlow = null + ?string $biometricConsentFlow = null, + ?string $darkMode = null, + ?string $primaryColourDarkMode = null, + ?string $brandId = null ) { $this->allowedCaptureMethods = $allowedCaptureMethods; $this->primaryColour = $primaryColour; @@ -110,6 +131,9 @@ public function __construct( $this->attemptsConfiguration = new AttemptsConfiguration($idDocumentTextDataExtractionRetriesConfig); } $this->biometricConsentFlow = $biometricConsentFlow; + $this->darkMode = $darkMode; + $this->primaryColourDarkMode = $primaryColourDarkMode; + $this->brandId = $brandId; } /** @@ -129,7 +153,10 @@ public function jsonSerialize(): \stdClass 'privacy_policy_url' => $this->getPrivacyPolicyUrl(), 'allow_handoff' => $this->getAllowHandoff(), 'attempts_configuration' => $this->getAttemptsConfiguration(), - 'biometric_consent_flow' => $this->getBiometricConsentFlow() + 'biometric_consent_flow' => $this->getBiometricConsentFlow(), + 'dark_mode' => $this->getDarkMode(), + 'primary_colour_dark_mode' => $this->getPrimaryColourDarkMode(), + 'brand_id' => $this->getBrandId() ]); } @@ -228,4 +255,28 @@ public function getBiometricConsentFlow(): ?string { return $this->biometricConsentFlow; } + + /** + * @return string|null + */ + public function getDarkMode(): ?string + { + return $this->darkMode; + } + + /** + * @return string|null + */ + public function getPrimaryColourDarkMode(): ?string + { + return $this->primaryColourDarkMode; + } + + /** + * @return string|null + */ + public function getBrandId(): ?string + { + return $this->brandId; + } } diff --git a/src/DocScan/Session/Create/SdkConfigBuilder.php b/src/DocScan/Session/Create/SdkConfigBuilder.php index acf30fcc..5a4661dd 100644 --- a/src/DocScan/Session/Create/SdkConfigBuilder.php +++ b/src/DocScan/Session/Create/SdkConfigBuilder.php @@ -71,6 +71,21 @@ class SdkConfigBuilder */ private $biometricConsentFlow; + /** + * @var string|null + */ + private $darkMode; + + /** + * @var string|null + */ + private $primaryColourDarkMode; + + /** + * @var string|null + */ + private $brandId; + public function withAllowsCamera(): self { return $this->withAllowedCaptureMethod(self::CAMERA); @@ -146,6 +161,7 @@ public function withBiometricConsentFlow(string $biometricConsentFlow): self $this->biometricConsentFlow = $biometricConsentFlow; return $this; } + /** * Allows configuring the number of attempts permitted for text extraction on an ID document * @@ -199,6 +215,41 @@ public function withIdDocumentTextExtractionGenericAttempts(int $genericRetries) return $this; } + public function withDarkMode(string $darkMode): self + { + $this->darkMode = $darkMode; + return $this; + } + + public function withDarkModeOn(): self + { + $this->darkMode = "ON"; + return $this; + } + + public function withDarkModeOff(): self + { + $this->darkMode = "OFF"; + return $this; + } + + public function withDarkModeAuto(): self + { + $this->darkMode = "AUTO"; + return $this; + } + + public function withPrimaryColourDarkMode(string $primaryColourDarkMode): self + { + $this->primaryColourDarkMode = $primaryColourDarkMode; + return $this; + } + + public function withBrandId(string $brandId): self + { + $this->brandId = $brandId; + return $this; + } public function build(): SdkConfig { @@ -214,7 +265,10 @@ public function build(): SdkConfig $this->privacyPolicyUrl, $this->allowHandoff, $this->idDocumentTextDataExtractionRetriesConfig, - $this->biometricConsentFlow + $this->biometricConsentFlow, + $this->darkMode, + $this->primaryColourDarkMode, + $this->brandId ); } } diff --git a/src/DocScan/Session/Retrieve/CustomAccountWatchlistCaSearchConfigResponse.php b/src/DocScan/Session/Retrieve/CustomAccountWatchlistCaSearchConfigResponse.php index efa7819d..6b95fd6c 100644 --- a/src/DocScan/Session/Retrieve/CustomAccountWatchlistCaSearchConfigResponse.php +++ b/src/DocScan/Session/Retrieve/CustomAccountWatchlistCaSearchConfigResponse.php @@ -35,7 +35,9 @@ public function __construct(array $searchConfig) $this->apiKey = $searchConfig['api_key']; $this->monitoring = $searchConfig['monitoring']; $this->clientRef = $searchConfig['client_ref']; - $this->tags = array_key_exists('tags', $searchConfig) ? json_decode($searchConfig['tags'], true) : []; + $this->tags = array_key_exists('tags', $searchConfig) && is_string($searchConfig['tags']) + ? json_decode($searchConfig['tags'], true) + : (array_key_exists('tags', $searchConfig) && is_array($searchConfig['tags']) ? $searchConfig['tags'] : []); } /** diff --git a/src/DocScan/Session/Retrieve/GetSessionResult.php b/src/DocScan/Session/Retrieve/GetSessionResult.php index 919053c3..857e3263 100644 --- a/src/DocScan/Session/Retrieve/GetSessionResult.php +++ b/src/DocScan/Session/Retrieve/GetSessionResult.php @@ -313,7 +313,11 @@ function ($checkResponse) use ($class): bool { public function getIdentityProfile(): ?IdentityProfileResponse { - return $this->identityProfile; + if (isset($this->identityProfile)) { + return $this->identityProfile; + } else { + return null; + } } public function getIdentityProfilePreview(): ?IdentityProfilePreviewResponse diff --git a/src/DocScan/Session/Retrieve/IdentityProfileResponse.php b/src/DocScan/Session/Retrieve/IdentityProfileResponse.php index 17643e89..06bea9c2 100644 --- a/src/DocScan/Session/Retrieve/IdentityProfileResponse.php +++ b/src/DocScan/Session/Retrieve/IdentityProfileResponse.php @@ -31,7 +31,7 @@ class IdentityProfileResponse */ public function __construct(array $sessionData) { - $this->subjectId = $sessionData['subject_id']; + $this->subjectId = $sessionData['subject_id'] ?? ''; $this->result = $sessionData['result']; if (isset($sessionData['failure_reason'])) { @@ -62,7 +62,7 @@ public function getResult(): string /** * @return FailureReasonResponse */ - public function getFailureReason(): FailureReasonResponse + public function getFailureReason(): ?FailureReasonResponse { return $this->failureReason; } diff --git a/src/Exception/ActivityDetailsException.php b/src/Exception/ActivityDetailsException.php index 1913be81..d01f61ac 100644 --- a/src/Exception/ActivityDetailsException.php +++ b/src/Exception/ActivityDetailsException.php @@ -25,7 +25,7 @@ public function __construct( $message = "", ?ResponseInterface $response = null, ?array $responseBody = null, - \Throwable $previous = null + ?\Throwable $previous = null ) { parent::__construct($message, $response, $previous); diff --git a/src/Exception/base/YotiException.php b/src/Exception/base/YotiException.php index 8dde0005..b6a7d2e6 100644 --- a/src/Exception/base/YotiException.php +++ b/src/Exception/base/YotiException.php @@ -20,7 +20,7 @@ class YotiException extends \Exception * @param ResponseInterface|null $response * @param \Throwable|null $previous */ - public function __construct($message = "", ?ResponseInterface $response = null, \Throwable $previous = null) + public function __construct($message = "", ?ResponseInterface $response = null, ?\Throwable $previous = null) { parent::__construct($this->formatMessage($message, $response), 0, $previous); diff --git a/src/Http/Exception/NetworkException.php b/src/Http/Exception/NetworkException.php index 0879d9f9..814871f7 100644 --- a/src/Http/Exception/NetworkException.php +++ b/src/Http/Exception/NetworkException.php @@ -19,7 +19,7 @@ class NetworkException extends ClientException implements NetworkExceptionInterf public function __construct( string $message, RequestInterface $request, - \Throwable $previous = null + ?\Throwable $previous = null ) { $this->setRequest($request); parent::__construct($message, 0, $previous); diff --git a/src/Http/Exception/RequestException.php b/src/Http/Exception/RequestException.php index 2ce34d5d..6c3a946c 100644 --- a/src/Http/Exception/RequestException.php +++ b/src/Http/Exception/RequestException.php @@ -19,7 +19,7 @@ class RequestException extends ClientException implements RequestExceptionInterf public function __construct( string $message, RequestInterface $request, - \Throwable $previous = null + ?\Throwable $previous = null ) { $this->setRequest($request); parent::__construct($message, 0, $previous); diff --git a/src/Http/RequestBuilder.php b/src/Http/RequestBuilder.php index 8afff19e..e2413181 100644 --- a/src/Http/RequestBuilder.php +++ b/src/Http/RequestBuilder.php @@ -73,9 +73,9 @@ class RequestBuilder private $multipartEntity; /** - * @param \Yoti\Util\Config $config + * @param \Yoti\Util\Config|null $config */ - public function __construct(Config $config = null) + public function __construct(?Config $config = null) { $this->config = $config ?? new Config(); } diff --git a/src/Http/RequestSigner.php b/src/Http/RequestSigner.php index 99bdff79..abf09fe0 100644 --- a/src/Http/RequestSigner.php +++ b/src/Http/RequestSigner.php @@ -26,7 +26,7 @@ public static function sign( PemFile $pemFile, string $endpoint, string $httpMethod, - Payload $payload = null + ?Payload $payload = null ): string { $messageToSign = "{$httpMethod}&$endpoint"; if ($payload instanceof Payload) { diff --git a/src/Identity/Content/ApplicationContent.php b/src/Identity/Content/ApplicationContent.php index 8487fc4a..7c0d267f 100644 --- a/src/Identity/Content/ApplicationContent.php +++ b/src/Identity/Content/ApplicationContent.php @@ -10,7 +10,7 @@ class ApplicationContent private ?ApplicationProfile $profile; private ?ExtraData $extraData; - public function __construct(ApplicationProfile $profile = null, ExtraData $extraData = null) + public function __construct(?ApplicationProfile $profile = null, ?ExtraData $extraData = null) { $this->profile = $profile; $this->extraData = $extraData; diff --git a/src/Identity/Content/Content.php b/src/Identity/Content/Content.php index 9cf61c01..cef3ef37 100644 --- a/src/Identity/Content/Content.php +++ b/src/Identity/Content/Content.php @@ -9,7 +9,7 @@ class Content private ?string $profile; private ?string $extraData; - public function __construct(string $profile = null, string $extraData = null) + public function __construct(?string $profile = null, ?string $extraData = null) { $this->profile = $profile; $this->extraData = $extraData; diff --git a/src/Identity/Content/UserContent.php b/src/Identity/Content/UserContent.php index a32c2dfd..a69e24fd 100644 --- a/src/Identity/Content/UserContent.php +++ b/src/Identity/Content/UserContent.php @@ -10,7 +10,7 @@ class UserContent private ?UserProfile $profile; private ?ExtraData $extraData; - public function __construct(UserProfile $profile = null, ExtraData $extraData = null) + public function __construct(?UserProfile $profile = null, ?ExtraData $extraData = null) { $this->profile = $profile; $this->extraData = $extraData; diff --git a/src/Identity/Policy/Policy.php b/src/Identity/Policy/Policy.php index a71e1fdc..5c54f2fa 100644 --- a/src/Identity/Policy/Policy.php +++ b/src/Identity/Policy/Policy.php @@ -37,7 +37,7 @@ class Policy implements \JsonSerializable * @param int[] $wantedAuthTypes * Auth types represents the authentication type to be used. * @param object $identityProfileRequirements - * @param object $advancedidentityProfileRequirements + * @param object $advancedIdentityProfileRequirements */ public function __construct( array $wantedAttributes, diff --git a/src/Identity/Policy/PolicyBuilder.php b/src/Identity/Policy/PolicyBuilder.php index a3b8f479..8ad89aaa 100644 --- a/src/Identity/Policy/PolicyBuilder.php +++ b/src/Identity/Policy/PolicyBuilder.php @@ -51,8 +51,8 @@ public function withWantedAttribute(WantedAttribute $wantedAttribute): self */ public function withWantedAttributeByName( string $name, - array $constraints = null, - bool $acceptSelfAsserted = null + ?array $constraints = null, + ?bool $acceptSelfAsserted = null ): self { $wantedAttributeBuilder = (new WantedAttributeBuilder()) ->withName($name); @@ -71,7 +71,7 @@ public function withWantedAttributeByName( /** * @param Constraint[]|null $constraints */ - public function withFamilyName(array $constraints = null, bool $acceptSelfAsserted = null): self + public function withFamilyName(?array $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_FAMILY_NAME, @@ -83,7 +83,7 @@ public function withFamilyName(array $constraints = null, bool $acceptSelfAssert /** * @param Constraint[]|null $constraints */ - public function withGivenNames(array $constraints = null, bool $acceptSelfAsserted = null): self + public function withGivenNames(?array $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_GIVEN_NAMES, @@ -95,7 +95,7 @@ public function withGivenNames(array $constraints = null, bool $acceptSelfAssert /** * @param Constraint[]|null $constraints */ - public function withFullName(array $constraints = null, bool $acceptSelfAsserted = null): self + public function withFullName(?array $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_FULL_NAME, @@ -107,7 +107,7 @@ public function withFullName(array $constraints = null, bool $acceptSelfAsserted /** * @param Constraint[]|null $constraints */ - public function withDateOfBirth(array $constraints = null, bool $acceptSelfAsserted = null): self + public function withDateOfBirth(?array $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_DATE_OF_BIRTH, @@ -119,7 +119,7 @@ public function withDateOfBirth(array $constraints = null, bool $acceptSelfAsser /** * @param Constraint[]|null $constraints */ - public function withAgeOver(int $age, array $constraints = null, bool $acceptSelfAsserted = null): self + public function withAgeOver(int $age, ?array $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withAgeDerivedAttribute( UserProfile::AGE_OVER . $age, @@ -131,7 +131,7 @@ public function withAgeOver(int $age, array $constraints = null, bool $acceptSel /** * @param Constraint[]|null $constraints */ - public function withAgeUnder(int $age, array $constraints = null, bool $acceptSelfAsserted = null): self + public function withAgeUnder(int $age, ?array $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withAgeDerivedAttribute( UserProfile::AGE_UNDER . $age, @@ -145,8 +145,8 @@ public function withAgeUnder(int $age, array $constraints = null, bool $acceptSe */ public function withAgeDerivedAttribute( string $derivation, - array $constraints = null, - bool $acceptSelfAsserted = null + ?array $constraints = null, + ?bool $acceptSelfAsserted = null ): self { $wantedAttributeBuilder = (new WantedAttributeBuilder()) ->withName(UserProfile::ATTR_DATE_OF_BIRTH) @@ -166,7 +166,7 @@ public function withAgeDerivedAttribute( /** * @param Constraint[]|null $constraints */ - public function withGender(array $constraints = null, bool $acceptSelfAsserted = null): self + public function withGender(?array $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_GENDER, @@ -178,7 +178,7 @@ public function withGender(array $constraints = null, bool $acceptSelfAsserted = /** * @param Constraint[]|null $constraints */ - public function withPostalAddress(array $constraints = null, bool $acceptSelfAsserted = null): self + public function withPostalAddress(?array $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_POSTAL_ADDRESS, @@ -190,7 +190,7 @@ public function withPostalAddress(array $constraints = null, bool $acceptSelfAss /** * @param Constraint[]|null $constraints */ - public function withStructuredPostalAddress(array $constraints = null, bool $acceptSelfAsserted = null): self + public function withStructuredPostalAddress(?array $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_STRUCTURED_POSTAL_ADDRESS, @@ -202,7 +202,7 @@ public function withStructuredPostalAddress(array $constraints = null, bool $acc /** * @param Constraint[]|null $constraints */ - public function withNationality(array $constraints = null, bool $acceptSelfAsserted = null): self + public function withNationality(?array $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_NATIONALITY, @@ -214,7 +214,7 @@ public function withNationality(array $constraints = null, bool $acceptSelfAsser /** * @param Constraint[]|null $constraints */ - public function withPhoneNumber(array $constraints = null, bool $acceptSelfAsserted = null): self + public function withPhoneNumber(?array $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_PHONE_NUMBER, @@ -226,7 +226,7 @@ public function withPhoneNumber(array $constraints = null, bool $acceptSelfAsser /** * @param Constraint[]|null $constraints */ - public function withSelfie(array $constraints = null, bool $acceptSelfAsserted = null): self + public function withSelfie(?array $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_SELFIE, @@ -238,7 +238,7 @@ public function withSelfie(array $constraints = null, bool $acceptSelfAsserted = /** * @param Constraint[]|null $constraints */ - public function withDocumentDetails(array $constraints = null, bool $acceptSelfAsserted = null): self + public function withDocumentDetails(?array $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_DOCUMENT_DETAILS, @@ -250,7 +250,7 @@ public function withDocumentDetails(array $constraints = null, bool $acceptSelfA /** * @param Constraint[]|null $constraints */ - public function withDocumentImages(array $constraints = null, bool $acceptSelfAsserted = null): self + public function withDocumentImages(?array $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_DOCUMENT_IMAGES, @@ -262,7 +262,7 @@ public function withDocumentImages(array $constraints = null, bool $acceptSelfAs /** * @param Constraint[]|null $constraints */ - public function withEmail(array $constraints = null, bool $acceptSelfAsserted = null): self + public function withEmail(?array $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_EMAIL_ADDRESS, diff --git a/src/Identity/Policy/WantedAttribute.php b/src/Identity/Policy/WantedAttribute.php index dc34da79..47005af1 100644 --- a/src/Identity/Policy/WantedAttribute.php +++ b/src/Identity/Policy/WantedAttribute.php @@ -29,10 +29,10 @@ class WantedAttribute implements \JsonSerializable */ public function __construct( string $name, - string $derivation = null, + ?string $derivation = null, bool $optional = false, - bool $acceptSelfAsserted = null, - array $constraints = null + ?bool $acceptSelfAsserted = null, + ?array $constraints = null ) { Validation::notEmptyString($name, 'name'); $this->name = $name; diff --git a/src/Identity/Receipt.php b/src/Identity/Receipt.php index 162761f2..893c5459 100644 --- a/src/Identity/Receipt.php +++ b/src/Identity/Receipt.php @@ -12,8 +12,8 @@ class Receipt private string $id; private string $sessionId; private \DateTime $timestamp; - private ApplicationContent $applicationContent; - private UserContent $userContent; + private ?ApplicationContent $applicationContent; + private ?UserContent $userContent; private ?string $rememberMeId; private ?string $parentRememberMeId; private ?string $error; @@ -23,8 +23,8 @@ public function __construct( string $id, string $sessionId, \DateTime $timestamp, - ApplicationContent $applicationContent, - UserContent $userContent, + ?ApplicationContent $applicationContent, + ?UserContent $userContent, ?string $rememberMeId, ?string $parentRememberMeId, ?string $error, @@ -58,20 +58,28 @@ public function getTimestamp(): \DateTime public function getProfile(): ?UserProfile { - return $this->userContent->getProfile(); + if ($this->userContent !== null) { + return $this->userContent->getProfile(); + } else { + return null; + } } public function getExtraData(): ?ExtraData { - return $this->userContent->getExtraData(); + if ($this->userContent !== null) { + return $this->userContent->getExtraData(); + } else { + return null; + } } - public function getApplicationContent(): ApplicationContent + public function getApplicationContent(): ?ApplicationContent { return $this->applicationContent; } - public function getUserContent(): UserContent + public function getUserContent(): ?UserContent { return $this->userContent; } diff --git a/src/Identity/ReceiptBuilder.php b/src/Identity/ReceiptBuilder.php index 7a091388..1ec378f2 100644 --- a/src/Identity/ReceiptBuilder.php +++ b/src/Identity/ReceiptBuilder.php @@ -16,9 +16,9 @@ class ReceiptBuilder private \DateTime $timestamp; - private ApplicationContent $applicationContent; + private ?ApplicationContent $applicationContent = null; - private UserContent $userContent; + private ?UserContent $userContent = null; private ?string $rememberMeId = null; @@ -42,14 +42,14 @@ public function withSessionId(string $sessionId): self return $this; } - public function withRememberMeId(string $rememberMeId = null): self + public function withRememberMeId(?string $rememberMeId = null): self { $this->rememberMeId = $rememberMeId; return $this; } - public function withParentRememberMeId(string $parentRememberMeId = null): self + public function withParentRememberMeId(?string $parentRememberMeId = null): self { $this->parentRememberMeId = $parentRememberMeId; @@ -63,28 +63,28 @@ public function withTimestamp(\DateTime $timestamp): self return $this; } - public function withApplicationContent(ApplicationProfile $profile, ExtraData $extraData = null): self + public function withApplicationContent(ApplicationProfile $profile, ?ExtraData $extraData = null): self { $this->applicationContent = new ApplicationContent($profile, $extraData); return $this; } - public function withUserContent(UserProfile $profile = null, ExtraData $extraData = null): self + public function withUserContent(?UserProfile $profile = null, ?ExtraData $extraData = null): self { $this->userContent = new UserContent($profile, $extraData); return $this; } - public function withError(string $error = null): self + public function withError(?string $error = null): self { $this->error = $error; return $this; } - public function withErrorReason(ErrorReason $errorReason = null): self + public function withErrorReason(?ErrorReason $errorReason = null): self { $this->errorReason = $errorReason; diff --git a/src/Identity/ReceiptParser.php b/src/Identity/ReceiptParser.php index 375249da..39fae9ee 100644 --- a/src/Identity/ReceiptParser.php +++ b/src/Identity/ReceiptParser.php @@ -21,7 +21,7 @@ class ReceiptParser */ private $logger; - public function __construct(LoggerInterface $logger = null) + public function __construct(?LoggerInterface $logger = null) { $this->logger = $logger ?? new Logger(); } @@ -93,8 +93,11 @@ public function createFailure(WrappedReceipt $wrappedReceipt): Receipt ->build(); } - private function decryptReceiptKey(string $wrappedKey, ReceiptItemKey $wrappedItemKey, PemFile $pemFile): string + private function decryptReceiptKey(?string $wrappedKey, ReceiptItemKey $wrappedItemKey, PemFile $pemFile): string { + if ($wrappedKey == null) { + throw new EncryptedDataException('Wrapped is null'); + } // Convert 'iv' and 'value' from base64 to binary $iv = (string)base64_decode($wrappedItemKey->getIv(), true); $encryptedItemKey = (string)base64_decode($wrappedItemKey->getValue(), true); diff --git a/src/Identity/WrappedReceipt.php b/src/Identity/WrappedReceipt.php index 7d1f4b74..28e93f60 100644 --- a/src/Identity/WrappedReceipt.php +++ b/src/Identity/WrappedReceipt.php @@ -19,9 +19,9 @@ class WrappedReceipt private Content $otherPartyContent; - private string $wrappedItemKeyId; + private ?string $wrappedItemKeyId = null; - private string $wrappedKey; + private ?string $wrappedKey; private ?string $rememberMeId = null; @@ -38,8 +38,8 @@ public function __construct(array $sessionData) $this->id = $sessionData['id']; $this->sessionId = $sessionData['sessionId']; $this->timestamp = DateTime::stringToDateTime($sessionData['timestamp']); - $this->wrappedItemKeyId = $sessionData['wrappedItemKeyId']; - $this->wrappedKey = $sessionData['wrappedKey']; + $this->wrappedItemKeyId = $sessionData['wrappedItemKeyId'] ?? null; + $this->wrappedKey = $sessionData['wrappedKey'] ?? null; if (isset($sessionData['content'])) { $this->content = new Content( @@ -63,10 +63,10 @@ public function __construct(array $sessionData) if (isset($sessionData['error'])) { $this->error = $sessionData['error']; } - if (isset($sessionData['errorDetails'])) { - if (isset($sessionData["error_details"]["error_reason"]["requirements_not_met_details"])) { + if (isset($sessionData['errorReason'])) { + if (isset($sessionData["errorReason"]["requirements_not_met_details"])) { $this->errorReason = new ErrorReason( - $sessionData['errorDetails']['error_reason']['requirements_not_met_details'] + $sessionData["errorReason"]["requirements_not_met_details"] ); } } @@ -115,12 +115,12 @@ public function getOtherPartyExtraData(): ?string return $this->otherPartyContent->getExtraData(); } - public function getWrappedItemKeyId(): string + public function getWrappedItemKeyId(): ?string { return $this->wrappedItemKeyId; } - public function getWrappedKey(): string + public function getWrappedKey(): ?string { return $this->wrappedKey; } diff --git a/src/Profile/Attribute.php b/src/Profile/Attribute.php index 1117f728..39f1d8b2 100644 --- a/src/Profile/Attribute.php +++ b/src/Profile/Attribute.php @@ -36,7 +36,7 @@ class Attribute * @param Anchor[] $anchors * @param string|null $id */ - public function __construct(string $name, $value, array $anchors, string $id = null) + public function __construct(string $name, $value, array $anchors, ?string $id = null) { $this->name = $name; $this->value = $value; diff --git a/src/Profile/ExtraData/AttributeIssuanceDetails.php b/src/Profile/ExtraData/AttributeIssuanceDetails.php index 019350c6..31d6e9ca 100644 --- a/src/Profile/ExtraData/AttributeIssuanceDetails.php +++ b/src/Profile/ExtraData/AttributeIssuanceDetails.php @@ -25,10 +25,10 @@ class AttributeIssuanceDetails /** * @param string $token - * @param \DateTime $expiryDate + * @param \DateTime|null $expiryDate * @param \Yoti\Profile\ExtraData\AttributeDefinition[] $issuingAttributes */ - public function __construct(string $token, \DateTime $expiryDate = null, array $issuingAttributes = []) + public function __construct(string $token, ?\DateTime $expiryDate = null, array $issuingAttributes = []) { $this->token = $token; diff --git a/src/Profile/UserProfile.php b/src/Profile/UserProfile.php index d9ac0210..e4189a56 100644 --- a/src/Profile/UserProfile.php +++ b/src/Profile/UserProfile.php @@ -28,7 +28,6 @@ class UserProfile extends BaseProfile public const ATTR_DOCUMENT_IMAGES = 'document_images'; public const ATTR_STRUCTURED_POSTAL_ADDRESS = 'structured_postal_address'; public const ATTR_IDENTITY_PROFILE_REPORT = 'identity_profile_report'; - /** @var \Yoti\Profile\Attribute\AgeVerification[] */ private $ageVerifications; diff --git a/src/Profile/Util/Attribute/AnchorConverter.php b/src/Profile/Util/Attribute/AnchorConverter.php index a13d6147..93f9b130 100644 --- a/src/Profile/Util/Attribute/AnchorConverter.php +++ b/src/Profile/Util/Attribute/AnchorConverter.php @@ -60,8 +60,31 @@ private static function decodeAnchorValue(string $extEncodedValue): string { $encodedBER = ASN1::extractBER($extEncodedValue); $decodedValArr = ASN1::decodeBER($encodedBER); + if (isset($decodedValArr[0]['content'][0]['content'])) { - return $decodedValArr[0]['content'][0]['content']; + $value = $decodedValArr[0]['content'][0]['content']; + + if (!is_string($value)) { + return ''; + } + + $detectionOrder = mb_detect_order(); + $encoding = mb_detect_encoding($value, is_array($detectionOrder) ? $detectionOrder : null, true); + + if (is_string($encoding)) { + if ($encoding !== 'UTF-8') { + // PHPStan implies $value is string, $encoding is valid string, so result is string. + return mb_convert_encoding($value, 'UTF-8', $encoding); + } + // It is UTF-8 + return $value; + } else { // $encoding is false (detection failed) + if (!mb_check_encoding($value, 'UTF-8')) { + // PHPStan implies $value is string, so result is string. + return mb_convert_encoding($value, 'UTF-8', 'ISO-8859-1'); + } + return $value; // It's valid UTF-8 despite detection failing + } } return ''; } @@ -108,16 +131,28 @@ private static function convertCertToX509(string $certificate): \stdClass $X509 = new X509(); $X509Data = $X509->loadX509($certificate); - /** We need because of new 3.0 version phpseclib @link https://github.com/phpseclib/phpseclib/issues/1738 */ array_walk_recursive($X509Data, function (&$item): void { - if (is_string($item) && mb_detect_encoding($item) != 'ASCII') { - $item = base64_encode($item); + if (is_string($item)) { + $detectionOrder = mb_detect_order(); + $encoding = mb_detect_encoding($item, is_array($detectionOrder) ? $detectionOrder : null, true); + + if (is_string($encoding)) { + if ($encoding !== 'UTF-8' && $encoding !== 'ASCII') { + // PHPStan implies $item is string, $encoding is valid string, so result is string. + // The 'else' branch for base64_encode was deemed unreachable by PHPStan. + $item = mb_convert_encoding($item, 'UTF-8', $encoding); + } + // If $encoding is 'UTF-8' or 'ASCII', $item is left as is. + } else { // $encoding is false (detection failed) + if (!mb_check_encoding($item, 'UTF-8') && !mb_check_encoding($item, 'ASCII')) { + $item = base64_encode($item); + } + // If it's valid UTF-8/ASCII despite detection failing, $item is left as is. + } } }); $decodedX509Data = Json::decode(Json::encode(Json::convertFromLatin1ToUtf8Recursively($X509Data)), false); - // Ensure serial number is cast to string. - // @see \phpseclib\Math\BigInteger::__toString() $decodedX509Data ->tbsCertificate ->serialNumber diff --git a/src/ShareUrl/Policy/DynamicPolicyBuilder.php b/src/ShareUrl/Policy/DynamicPolicyBuilder.php index 4d3ba083..cf395be2 100644 --- a/src/ShareUrl/Policy/DynamicPolicyBuilder.php +++ b/src/ShareUrl/Policy/DynamicPolicyBuilder.php @@ -77,8 +77,8 @@ public function withWantedAttribute(WantedAttribute $wantedAttribute): self */ public function withWantedAttributeByName( string $name, - Constraints $constraints = null, - bool $acceptSelfAsserted = null + ?Constraints $constraints = null, + ?bool $acceptSelfAsserted = null ): self { $wantedAttributeBuilder = (new WantedAttributeBuilder()) ->withName($name); @@ -100,7 +100,7 @@ public function withWantedAttributeByName( * * @return $this */ - public function withFamilyName(Constraints $constraints = null, bool $acceptSelfAsserted = null): self + public function withFamilyName(?Constraints $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_FAMILY_NAME, @@ -115,7 +115,7 @@ public function withFamilyName(Constraints $constraints = null, bool $acceptSelf * * @return self */ - public function withGivenNames(Constraints $constraints = null, bool $acceptSelfAsserted = null): self + public function withGivenNames(?Constraints $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_GIVEN_NAMES, @@ -130,7 +130,7 @@ public function withGivenNames(Constraints $constraints = null, bool $acceptSelf * * @return self */ - public function withFullName(Constraints $constraints = null, bool $acceptSelfAsserted = null): self + public function withFullName(?Constraints $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_FULL_NAME, @@ -145,7 +145,7 @@ public function withFullName(Constraints $constraints = null, bool $acceptSelfAs * * @return $this */ - public function withDateOfBirth(Constraints $constraints = null, bool $acceptSelfAsserted = null): self + public function withDateOfBirth(?Constraints $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_DATE_OF_BIRTH, @@ -161,7 +161,7 @@ public function withDateOfBirth(Constraints $constraints = null, bool $acceptSel * * @return $this */ - public function withAgeOver(int $age, Constraints $constraints = null, bool $acceptSelfAsserted = null): self + public function withAgeOver(int $age, ?Constraints $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withAgeDerivedAttribute( UserProfile::AGE_OVER . (string) $age, @@ -177,7 +177,7 @@ public function withAgeOver(int $age, Constraints $constraints = null, bool $acc * * @return $this */ - public function withAgeUnder(int $age, Constraints $constraints = null, bool $acceptSelfAsserted = null): self + public function withAgeUnder(int $age, ?Constraints $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withAgeDerivedAttribute( UserProfile::AGE_UNDER . (string) $age, @@ -195,8 +195,8 @@ public function withAgeUnder(int $age, Constraints $constraints = null, bool $ac */ public function withAgeDerivedAttribute( string $derivation, - Constraints $constraints = null, - bool $acceptSelfAsserted = null + ?Constraints $constraints = null, + ?bool $acceptSelfAsserted = null ): self { $wantedAttributeBuilder = (new WantedAttributeBuilder()) ->withName(UserProfile::ATTR_DATE_OF_BIRTH) @@ -216,7 +216,7 @@ public function withAgeDerivedAttribute( * * @return $this */ - public function withGender(Constraints $constraints = null, bool $acceptSelfAsserted = null): self + public function withGender(?Constraints $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_GENDER, @@ -231,7 +231,7 @@ public function withGender(Constraints $constraints = null, bool $acceptSelfAsse * * @return $this */ - public function withPostalAddress(Constraints $constraints = null, bool $acceptSelfAsserted = null): self + public function withPostalAddress(?Constraints $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_POSTAL_ADDRESS, @@ -246,8 +246,10 @@ public function withPostalAddress(Constraints $constraints = null, bool $acceptS * * @return $this */ - public function withStructuredPostalAddress(Constraints $constraints = null, bool $acceptSelfAsserted = null): self - { + public function withStructuredPostalAddress( + ?Constraints $constraints = null, + ?bool $acceptSelfAsserted = null + ): self { return $this->withWantedAttributeByName( UserProfile::ATTR_STRUCTURED_POSTAL_ADDRESS, $constraints, @@ -261,7 +263,7 @@ public function withStructuredPostalAddress(Constraints $constraints = null, boo * * @return $this */ - public function withNationality(Constraints $constraints = null, bool $acceptSelfAsserted = null): self + public function withNationality(?Constraints $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_NATIONALITY, @@ -276,7 +278,7 @@ public function withNationality(Constraints $constraints = null, bool $acceptSel * * @return $this */ - public function withPhoneNumber(Constraints $constraints = null, bool $acceptSelfAsserted = null): self + public function withPhoneNumber(?Constraints $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_PHONE_NUMBER, @@ -291,7 +293,7 @@ public function withPhoneNumber(Constraints $constraints = null, bool $acceptSel * * @return $this */ - public function withSelfie(Constraints $constraints = null, bool $acceptSelfAsserted = null): self + public function withSelfie(?Constraints $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_SELFIE, @@ -306,7 +308,7 @@ public function withSelfie(Constraints $constraints = null, bool $acceptSelfAsse * * @return $this */ - public function withDocumentDetails(Constraints $constraints = null, bool $acceptSelfAsserted = null): self + public function withDocumentDetails(?Constraints $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_DOCUMENT_DETAILS, @@ -321,7 +323,7 @@ public function withDocumentDetails(Constraints $constraints = null, bool $accep * * @return $this */ - public function withDocumentImages(Constraints $constraints = null, bool $acceptSelfAsserted = null): self + public function withDocumentImages(?Constraints $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_DOCUMENT_IMAGES, @@ -336,7 +338,7 @@ public function withDocumentImages(Constraints $constraints = null, bool $accept * * @return $this */ - public function withEmail(Constraints $constraints = null, bool $acceptSelfAsserted = null): self + public function withEmail(?Constraints $constraints = null, ?bool $acceptSelfAsserted = null): self { return $this->withWantedAttributeByName( UserProfile::ATTR_EMAIL_ADDRESS, @@ -411,7 +413,7 @@ public function withIdentityProfileRequirements($identityProfileRequirements): s * @param object $advancedIdentityProfileRequirements * @return $this */ - public function withAdvancedIdentityProfileRequirements($advancedIdentityProfileRequirements): self + public function withAdvIdentityProfileReqs($advancedIdentityProfileRequirements): self { $this->advancedIdentityProfileRequirements = $advancedIdentityProfileRequirements; return $this; diff --git a/src/ShareUrl/Policy/WantedAttribute.php b/src/ShareUrl/Policy/WantedAttribute.php index a5b0f2ed..d176070a 100644 --- a/src/ShareUrl/Policy/WantedAttribute.php +++ b/src/ShareUrl/Policy/WantedAttribute.php @@ -32,17 +32,24 @@ class WantedAttribute implements \JsonSerializable */ private $acceptSelfAsserted; + /** + * @var bool|null + */ + private $optional; + /** * @param string $name * @param string $derivation * @param bool $acceptSelfAsserted * @param \Yoti\ShareUrl\Policy\Constraints $constraints + * @param bool $optional */ public function __construct( string $name, - string $derivation = null, - bool $acceptSelfAsserted = null, - Constraints $constraints = null + ?string $derivation = null, + ?bool $acceptSelfAsserted = null, + ?Constraints $constraints = null, + ?bool $optional = null ) { Validation::notEmptyString($name, 'name'); $this->name = $name; @@ -50,6 +57,7 @@ public function __construct( $this->derivation = $derivation; $this->acceptSelfAsserted = $acceptSelfAsserted; $this->constraints = $constraints; + $this->optional = $optional; } /** @@ -97,6 +105,14 @@ public function getAcceptSelfAsserted(): ?bool return $this->acceptSelfAsserted; } + /** + * @return bool|null + */ + public function getOptional(): ?bool + { + return $this->optional; + } + /** * @inheritDoc * @@ -106,7 +122,7 @@ public function jsonSerialize(): array { $json = [ 'name' => $this->getName(), - 'optional' => false, + 'optional' => $this->getOptional(), ]; if ($this->getDerivation() !== null) { @@ -121,6 +137,12 @@ public function jsonSerialize(): array $json['accept_self_asserted'] = $this->getAcceptSelfAsserted(); } + if ($this->getOptional() !== null) { + $json['optional'] = $this->getOptional(); + } + + + return $json; } diff --git a/src/ShareUrl/Policy/WantedAttributeBuilder.php b/src/ShareUrl/Policy/WantedAttributeBuilder.php index 37f381e7..9995cfdb 100644 --- a/src/ShareUrl/Policy/WantedAttributeBuilder.php +++ b/src/ShareUrl/Policy/WantedAttributeBuilder.php @@ -29,6 +29,10 @@ class WantedAttributeBuilder */ private $acceptSelfAsserted; + /** + * @var bool|null + */ + private $optional = false; /** * @param string $name * @@ -73,6 +77,16 @@ public function withAcceptSelfAsserted(?bool $acceptSelfAsserted = true): self return $this; } + /** + * @param bool $optional + * + * @return $this + */ + public function withOptional(?bool $optional = false): self + { + $this->optional = $optional; + return $this; + } /** * @return \Yoti\ShareUrl\Policy\WantedAttribute */ @@ -82,7 +96,8 @@ public function build(): WantedAttribute $this->name, $this->derivation, $this->acceptSelfAsserted, - $this->constraints + $this->constraints, + $this->optional ); } } diff --git a/src/Util/Json.php b/src/Util/Json.php index 8be63f78..81f7bdb7 100644 --- a/src/Util/Json.php +++ b/src/Util/Json.php @@ -65,7 +65,7 @@ private static function validate(): void public static function convertFromLatin1ToUtf8Recursively($dat) { if (is_string($dat)) { - return utf8_encode($dat); + return mb_convert_encoding($dat, 'UTF-8', 'ISO-8859-1'); } elseif (is_array($dat)) { $ret = []; foreach ($dat as $i => $d) { diff --git a/tests/Aml/ResultTest.php b/tests/Aml/ResultTest.php index 76dd42d0..86f83eaf 100644 --- a/tests/Aml/ResultTest.php +++ b/tests/Aml/ResultTest.php @@ -21,6 +21,11 @@ class ResultTest extends TestCase */ public $amlResult; + /** + * @var \PHPUnit\Framework\MockObject\MockObject&\Psr\Http\Message\ResponseInterface + */ + private $responseMock; + public function setup(): void { $this->responseMock = $this->createMock(ResponseInterface::class); diff --git a/tests/DocScan/DocScanClientTest.php b/tests/DocScan/DocScanClientTest.php index 306e1d17..623c0a60 100644 --- a/tests/DocScan/DocScanClientTest.php +++ b/tests/DocScan/DocScanClientTest.php @@ -6,6 +6,7 @@ use Psr\Http\Client\ClientInterface; use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\StreamInterface; use Yoti\DocScan\DocScanClient; use Yoti\DocScan\Session\Create\CreateSessionResult; use Yoti\DocScan\Session\Create\FaceCapture\CreateFaceCaptureResourcePayload; @@ -92,8 +93,12 @@ public function testEmptyApiUrlEnvironmentVariable() */ private function assertApiUrlStartsWith($expectedUrl, $clientApiUrl = null) { + $stream = $this->createMock(\Psr\Http\Message\StreamInterface::class); + $stream->method('getContents')->willReturn(file_get_contents(TestData::DOC_SCAN_SESSION_CREATION_RESPONSE)); + $stream->method('__toString')->willReturn(file_get_contents(TestData::DOC_SCAN_SESSION_CREATION_RESPONSE)); + $response = $this->createMock(ResponseInterface::class); - $response->method('getBody')->willReturn(file_get_contents(TestData::DOC_SCAN_SESSION_CREATION_RESPONSE)); + $response->method('getBody')->willReturn($stream); $response->method('getStatusCode')->willReturn(200); $httpClient = $this->createMock(ClientInterface::class); @@ -126,8 +131,13 @@ private function assertApiUrlStartsWith($expectedUrl, $clientApiUrl = null) */ public function testCreateSession() { + $stream = $this->createMock(\Psr\Http\Message\StreamInterface::class); + $stream->method('getContents')->willReturn(file_get_contents(TestData::DOC_SCAN_SESSION_CREATION_RESPONSE)); + $stream->method('__toString')->willReturn(file_get_contents(TestData::DOC_SCAN_SESSION_CREATION_RESPONSE)); + $response = $this->createMock(ResponseInterface::class); - $response->method('getBody')->willReturn(file_get_contents(TestData::DOC_SCAN_SESSION_CREATION_RESPONSE)); + $response->method('getBody')->willReturn($stream); + $response->method('getStatusCode')->willReturn(200); $httpClient = $this->createMock(ClientInterface::class); @@ -155,8 +165,12 @@ public function testCreateSession() */ public function testGetSession() { + $stream = $this->createMock(\Psr\Http\Message\StreamInterface::class); + $stream->method('getContents')->willReturn(file_get_contents(TestData::DOC_SCAN_SESSION_RESPONSE)); + $stream->method('__toString')->willReturn(file_get_contents(TestData::DOC_SCAN_SESSION_RESPONSE)); + $response = $this->createMock(ResponseInterface::class); - $response->method('getBody')->willReturn(file_get_contents(TestData::DOC_SCAN_SESSION_RESPONSE)); + $response->method('getBody')->willReturn($stream); $response->method('getStatusCode')->willReturn(200); $httpClient = $this->createMock(ClientInterface::class); @@ -203,8 +217,13 @@ public function testDeleteSessionDoesNotThrowException() */ public function testGetMedia() { + $stream = $this->createMock(\Psr\Http\Message\StreamInterface::class); + $stream->method('getContents')->willReturn(file_get_contents(TestData::DOC_SCAN_SESSION_RESPONSE)); + $stream->method('__toString')->willReturn(file_get_contents(TestData::DOC_SCAN_SESSION_RESPONSE)); + $response = $this->createMock(ResponseInterface::class); - $response->method('getBody')->willReturn(file_get_contents(TestData::DOC_SCAN_SESSION_RESPONSE)); + $response->method('getBody')->willReturn($stream); + $response->method('getStatusCode')->willReturn(200); $response->method('getHeader')->willReturn([ 'image/png' ]); @@ -276,8 +295,12 @@ public function testDeleteMediaDoesNotThrowException() */ public function testGetSupportedDocuments() { + $stream = $this->createMock(StreamInterface::class); + $stream->method('getContents')->willReturn(json_encode((object)[])); + $stream->method('__toString')->willReturn(json_encode((object)[])); + $response = $this->createMock(ResponseInterface::class); - $response->method('getBody')->willReturn(json_encode((object)[])); + $response->method('getBody')->willReturn($stream); $response->method('getStatusCode')->willReturn(200); $httpClient = $this->createMock(ClientInterface::class); @@ -301,8 +324,12 @@ public function testGetSupportedDocuments() */ public function testCreateFaceCaptureResource() { + $stream = $this->createMock(StreamInterface::class); + $stream->method('getContents')->willReturn(json_encode((object)[])); + $stream->method('__toString')->willReturn(json_encode((object)[])); + $response = $this->createMock(ResponseInterface::class); - $response->method('getBody')->willReturn(json_encode((object)[])); + $response->method('getBody')->willReturn($stream); $response->method('getStatusCode')->willReturn(201); $createFaceCaptureResourcePayloadMock = $this->createMock(CreateFaceCaptureResourcePayload::class); @@ -331,9 +358,14 @@ public function testCreateFaceCaptureResource() */ public function testUploadFaceCaptureImage() { + + $stream = $this->createMock(StreamInterface::class); + $stream->method('getContents')->willReturn(json_encode((object)[])); + $stream->method('__toString')->willReturn(json_encode((object)[])); + $response = $this->createMock(ResponseInterface::class); + $response->method('getBody')->willReturn($stream); $uploadFaceCaptureImagePayloadMock = $this->createMock(UploadFaceCaptureImagePayload::class); - $response->method('getBody')->willReturn(json_encode((object)[])); $response->method('getStatusCode')->willReturn(200); $httpClient = $this->createMock(ClientInterface::class); @@ -358,8 +390,12 @@ public function testUploadFaceCaptureImage() */ public function testGetSessionConfiguration() { + $stream = $this->createMock(StreamInterface::class); + $stream->method('getContents')->willReturn(json_encode((object)[])); + $stream->method('__toString')->willReturn(json_encode((object)[])); + $response = $this->createMock(ResponseInterface::class); - $response->method('getBody')->willReturn(json_encode((object)[])); + $response->method('getBody')->willReturn($stream); $response->method('getStatusCode')->willReturn(200); $httpClient = $this->createMock(ClientInterface::class); @@ -383,9 +419,13 @@ public function testGetSessionConfiguration() */ public function testPutIbvInstructions() { + $stream = $this->createMock(StreamInterface::class); + $stream->method('getContents')->willReturn(json_encode((object)[])); + $stream->method('__toString')->willReturn(json_encode((object)[])); + $response = $this->createMock(ResponseInterface::class); + $response->method('getBody')->willReturn($stream); $instructionsMock = $this->createMock(Instructions::class); - $response->method('getBody')->willReturn(json_encode((object)[])); $response->method('getStatusCode')->willReturn(200); $httpClient = $this->createMock(ClientInterface::class); @@ -409,8 +449,12 @@ public function testPutIbvInstructions() */ public function testGetIbvInstructions() { + $stream = $this->createMock(StreamInterface::class); + $stream->method('getContents')->willReturn(json_encode((object)[])); + $stream->method('__toString')->willReturn(json_encode((object)[])); + $response = $this->createMock(ResponseInterface::class); - $response->method('getBody')->willReturn(json_encode((object)[])); + $response->method('getBody')->willReturn($stream); $response->method('getStatusCode')->willReturn(200); $httpClient = $this->createMock(ClientInterface::class); @@ -433,8 +477,12 @@ public function testGetIbvInstructions() */ public function testGetIbvInstructionsPdf() { + $stream = $this->createMock(StreamInterface::class); + $stream->method('getContents')->willReturn(json_encode((object)[])); + $stream->method('__toString')->willReturn(json_encode((object)[])); + $response = $this->createMock(ResponseInterface::class); - $response->method('getBody')->willReturn(json_encode((object)[])); + $response->method('getBody')->willReturn($stream); $response->method('getStatusCode')->willReturn(200); $httpClient = $this->createMock(ClientInterface::class); @@ -457,8 +505,12 @@ public function testGetIbvInstructionsPdf() */ public function testFetchInstructionsContactProfile() { + $stream = $this->createMock(StreamInterface::class); + $stream->method('getContents')->willReturn(json_encode((object)[])); + $stream->method('__toString')->willReturn(json_encode((object)[])); + $response = $this->createMock(ResponseInterface::class); - $response->method('getBody')->willReturn(json_encode((object)[])); + $response->method('getBody')->willReturn($stream); $response->method('getStatusCode')->willReturn(200); $httpClient = $this->createMock(ClientInterface::class); @@ -481,8 +533,12 @@ public function testFetchInstructionsContactProfile() */ public function testTriggerIbvEmailNotification() { + $stream = $this->createMock(StreamInterface::class); + $stream->method('getContents')->willReturn(json_encode((object)[])); + $stream->method('__toString')->willReturn(json_encode((object)[])); + $response = $this->createMock(ResponseInterface::class); - $response->method('getBody')->willReturn(json_encode((object)[])); + $response->method('getBody')->willReturn($stream); $response->method('getStatusCode')->willReturn(200); $httpClient = $this->createMock(ClientInterface::class); diff --git a/tests/DocScan/Session/Create/SdkConfigBuilderTest.php b/tests/DocScan/Session/Create/SdkConfigBuilderTest.php index 3c84f5d2..846f2433 100644 --- a/tests/DocScan/Session/Create/SdkConfigBuilderTest.php +++ b/tests/DocScan/Session/Create/SdkConfigBuilderTest.php @@ -23,7 +23,9 @@ class SdkConfigBuilderTest extends TestCase private const SOME_CATEGORY = 'someCategory'; private const SOME_NUMBER_RETRIES = 5; private const SOME_BIOMETRIC_CONSENT_FLOW = 'someBiometricConsentFlow'; - + private const SOME_DARK_MODE = 'someDarkMode'; + private const SOME_PRIMARY_COLOUR_DARK_MODE = 'somePrimaryColourDarkMode'; + private const SOME_BRAND_ID = 'someBrandId'; /** * @test @@ -38,6 +40,7 @@ class SdkConfigBuilderTest extends TestCase * @covers ::withErrorUrl * @covers ::withPrivacyPolicyUrl * @covers ::withAllowHandoff + * @covers ::withBrandId * @covers \Yoti\DocScan\Session\Create\SdkConfig::__construct * @covers \Yoti\DocScan\Session\Create\SdkConfig::getAllowedCaptureMethods * @covers \Yoti\DocScan\Session\Create\SdkConfig::getPrimaryColour @@ -49,6 +52,9 @@ class SdkConfigBuilderTest extends TestCase * @covers \Yoti\DocScan\Session\Create\SdkConfig::getErrorUrl * @covers \Yoti\DocScan\Session\Create\SdkConfig::getPrivacyPolicyUrl * @covers \Yoti\DocScan\Session\Create\SdkConfig::getAllowHandoff + * @covers \Yoti\DocScan\Session\Create\SdkConfig::getDarkMode + * @covers \Yoti\DocScan\Session\Create\SdkConfig::getPrimaryColourDarkMode + * @covers \Yoti\DocScan\Session\Create\SdkConfig::getBrandId */ public function shouldCorrectlyBuildSdkConfig() { @@ -63,6 +69,10 @@ public function shouldCorrectlyBuildSdkConfig() ->withErrorUrl(self::SOME_ERROR_URL) ->withPrivacyPolicyUrl(self::SOME_PRIVACY_POLICY_URL) ->withAllowHandoff(true) + ->withBiometricConsentFlow(self::SOME_BIOMETRIC_CONSENT_FLOW) + ->withDarkMode(self::SOME_DARK_MODE) + ->withPrimaryColourDarkMode(self::SOME_PRIMARY_COLOUR_DARK_MODE) + ->withBrandId(self::SOME_BRAND_ID) ->build(); $this->assertEquals(self::SOME_CAPTURE_METHOD, $result->getAllowedCaptureMethods()); @@ -74,7 +84,11 @@ public function shouldCorrectlyBuildSdkConfig() $this->assertEquals(self::SOME_SUCCESS_URL, $result->getSuccessUrl()); $this->assertEquals(self::SOME_ERROR_URL, $result->getErrorUrl()); $this->assertEquals(self::SOME_PRIVACY_POLICY_URL, $result->getPrivacyPolicyUrl()); + $this->assertEquals(self::SOME_BIOMETRIC_CONSENT_FLOW, $result->getBiometricConsentFlow()); $this->assertTrue($result->getAllowHandoff()); + $this->assertEquals(self::SOME_DARK_MODE, $result->getDarkMode()); + $this->assertEquals(self::SOME_PRIMARY_COLOUR_DARK_MODE, $result->getPrimaryColourDarkMode()); + $this->assertEquals(self::SOME_BRAND_ID, $result->getBrandId()); } /** @@ -121,6 +135,8 @@ public function shouldProduceTheCorrectJsonString() ->withPrivacyPolicyUrl(self::SOME_PRIVACY_POLICY_URL) ->withAllowHandoff(true) ->withBiometricConsentFlow(self::SOME_BIOMETRIC_CONSENT_FLOW) + ->withPrimaryColourDarkMode(self::SOME_PRIMARY_COLOUR_DARK_MODE) + ->withDarkMode(self::SOME_DARK_MODE) ->build(); $expected = [ @@ -134,7 +150,9 @@ public function shouldProduceTheCorrectJsonString() 'error_url' => self::SOME_ERROR_URL, 'privacy_policy_url' => self::SOME_PRIVACY_POLICY_URL, 'allow_handoff' => true, - 'biometric_consent_flow' => self::SOME_BIOMETRIC_CONSENT_FLOW + 'biometric_consent_flow' => self::SOME_BIOMETRIC_CONSENT_FLOW, + 'dark_mode' => self::SOME_DARK_MODE, + 'primary_colour_dark_mode' => self::SOME_PRIMARY_COLOUR_DARK_MODE ]; $this->assertJsonStringEqualsJsonString(json_encode($expected), json_encode($result)); @@ -291,4 +309,43 @@ public function attemptsConfigurationShouldAllowMultipleCategories(): void ->getIdDocumentTextDataExtraction() ); } + + /** + * @test + * @covers ::withDarkModeAuto + */ + public function shouldSetCorrectValueWithDarkModeAuto() + { + $result = (new SdkConfigBuilder()) + ->withDarkModeAuto() + ->build(); + + $this->assertEquals('AUTO', $result->getDarkMode()); + } + + /** + * @test + * @covers ::withDarkModeOn + */ + public function shouldSetCorrectValueWithDarkModeOn() + { + $result = (new SdkConfigBuilder()) + ->withDarkModeOn() + ->build(); + + $this->assertEquals('ON', $result->getDarkMode()); + } + + /** + * @test + * @covers ::withDarkModeOff + */ + public function shouldSetCorrectValueWithDarkModeOff() + { + $result = (new SdkConfigBuilder()) + ->withDarkModeOff() + ->build(); + + $this->assertEquals('OFF', $result->getDarkMode()); + } } diff --git a/tests/Identity/Policy/PolicyBuilderTest.php b/tests/Identity/Policy/PolicyBuilderTest.php index 58b7968e..4865504b 100644 --- a/tests/Identity/Policy/PolicyBuilderTest.php +++ b/tests/Identity/Policy/PolicyBuilderTest.php @@ -680,7 +680,7 @@ public function testWithAdvancedIdentityProfileRequirements() "label" => "identity-AL-L1", "type" => "IDENTITY", - "objective"=> "AL_L1" + "objective" => "AL_L1" ], [ "label" => "identity-AL-M1", diff --git a/tests/Profile/BaseProfileTest.php b/tests/Profile/BaseProfileTest.php index 71fce4bb..20c7b122 100644 --- a/tests/Profile/BaseProfileTest.php +++ b/tests/Profile/BaseProfileTest.php @@ -138,7 +138,7 @@ public function testGetAttributeById() $givenNamesAttribute = new ProtobufAttribute([ 'name' => self::SOME_ATTRIBUTE, - 'value' => utf8_decode('Alan'), + 'value' => mb_convert_encoding('Alan', 'ISO-8859-1', 'UTF-8'), 'content_type' => self::CONTENT_TYPE_STRING, ]); $newAttribute = AttributeConverter::convertToYotiAttribute($givenNamesAttribute); diff --git a/tests/Profile/Util/EncryptedDataTest.php b/tests/Profile/Util/EncryptedDataTest.php index 1680c9a8..4d1e8741 100644 --- a/tests/Profile/Util/EncryptedDataTest.php +++ b/tests/Profile/Util/EncryptedDataTest.php @@ -28,6 +28,11 @@ class EncrypedDataTest extends TestCase */ private $wrappedKey; + /** + * @var \Yoti\Protobuf\Compubapi\EncryptedData + */ + private $encryptedDataProto; + /** * Setup test data. */ diff --git a/tests/ShareUrl/Policy/DynamicPolicyBuilderTest.php b/tests/ShareUrl/Policy/DynamicPolicyBuilderTest.php index 0980b58e..0c1680a6 100644 --- a/tests/ShareUrl/Policy/DynamicPolicyBuilderTest.php +++ b/tests/ShareUrl/Policy/DynamicPolicyBuilderTest.php @@ -719,7 +719,7 @@ public function testWithAdvancedIdentityProfileRequirements() ]; $dynamicPolicy = (new DynamicPolicyBuilder()) - ->withAdvancedIdentityProfileRequirements($advancedIdentityProfileSample) + ->withAdvIdentityProfileReqs($advancedIdentityProfileSample) ->build(); $this->assertEquals(json_encode($expectedWantedAttributeData), json_encode($dynamicPolicy)); diff --git a/tests/ShareUrl/Policy/WantedAttributeBuilderTest.php b/tests/ShareUrl/Policy/WantedAttributeBuilderTest.php index 5abb542e..2b1c20f3 100644 --- a/tests/ShareUrl/Policy/WantedAttributeBuilderTest.php +++ b/tests/ShareUrl/Policy/WantedAttributeBuilderTest.php @@ -32,6 +32,7 @@ public function testBuild() $wantedAttribute = (new WantedAttributeBuilder()) ->withName($someName) ->withDerivation($someDerivation) + ->withOptional(false) ->build(); $expectedJsonData = [ @@ -91,6 +92,7 @@ public function testAcceptSelfAsserted() $wantedAttributeDefault = (new WantedAttributeBuilder()) ->withName($someName) ->withAcceptSelfAsserted() + ->withOptional(false) ->build(); $this->assertEquals(json_encode($expectedJsonData), json_encode($wantedAttributeDefault)); @@ -124,6 +126,7 @@ public function testWithoutAcceptSelfAsserted() $wantedAttribute = (new WantedAttributeBuilder()) ->withName($someName) ->withAcceptSelfAsserted(false) + ->withOptional(false) ->build(); $this->assertEquals(json_encode($expectedJsonData), json_encode($wantedAttribute)); @@ -149,6 +152,7 @@ public function testWithConstraints() $wantedAttribute = (new WantedAttributeBuilder()) ->withName($someName) + ->withOptional(false) ->withConstraints($constraints) ->build(); diff --git a/tests/Util/JsonTest.php b/tests/Util/JsonTest.php index df232c6e..30a9b29b 100644 --- a/tests/Util/JsonTest.php +++ b/tests/Util/JsonTest.php @@ -81,18 +81,27 @@ public function testWithoutNullValues() */ public function testConvertFromLatin1ToUtf8Recursively() { - $latin1String = utf8_decode('éàê'); - $latin1Array = [utf8_decode('éàê'), utf8_decode('çî')]; - $nestedLatin1Array = [utf8_decode('éàê'), [utf8_decode('çî'), utf8_decode('üñ')]]; + $latin1String = mb_convert_encoding('éàê', 'ISO-8859-1', 'UTF-8'); + $latin1Array = [ + mb_convert_encoding('éàê', 'ISO-8859-1', 'UTF-8'), + mb_convert_encoding('çî', 'ISO-8859-1', 'UTF-8') + ]; + $nestedLatin1Array = [ + mb_convert_encoding('éàê', 'ISO-8859-1', 'UTF-8'), + [ + mb_convert_encoding('çî', 'ISO-8859-1', 'UTF-8'), + mb_convert_encoding('üñ', 'ISO-8859-1', 'UTF-8') + ] + ]; $latin1Object = new \stdClass(); - $latin1Object->property1 = utf8_decode('éàê'); - $latin1Object->property2 = utf8_decode('çî'); + $latin1Object->property1 = mb_convert_encoding('éàê', 'ISO-8859-1', 'UTF-8'); + $latin1Object->property2 = mb_convert_encoding('çî', 'ISO-8859-1', 'UTF-8'); $nestedLatin1Object = new \stdClass(); - $nestedLatin1Object->property = utf8_decode('çî'); + $nestedLatin1Object->property = mb_convert_encoding('çî', 'ISO-8859-1', 'UTF-8'); $latin1ObjectWithNestedObject = new \stdClass(); - $latin1ObjectWithNestedObject->property1 = utf8_decode('éàê'); + $latin1ObjectWithNestedObject->property1 = mb_convert_encoding('éàê', 'ISO-8859-1', 'UTF-8'); $latin1ObjectWithNestedObject->property2 = $nestedLatin1Object; $this->assertSame('éàê', Json::convertFromLatin1ToUtf8Recursively($latin1String)); diff --git a/tests/YotiClientTest.php b/tests/YotiClientTest.php index 9f345a5a..d95b635f 100755 --- a/tests/YotiClientTest.php +++ b/tests/YotiClientTest.php @@ -7,6 +7,7 @@ use GuzzleHttp\Psr7; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\StreamInterface; use Psr\Log\LoggerInterface; use Yoti\Aml\Address as AmlAddress; use Yoti\Aml\Country as AmlCountry; @@ -122,8 +123,12 @@ private function assertApiUrlStartsWith($expectedUrl, $clientApiUrl = null) */ public function testGetActivityDetails() { + $stream = $this->createMock(StreamInterface::class); + $stream->method('getContents')->willReturn(file_get_contents(TestData::RECEIPT_JSON)); + $stream->method('__toString')->willReturn(file_get_contents(TestData::RECEIPT_JSON)); + $response = $this->createMock(ResponseInterface::class); - $response->method('getBody')->willReturn(file_get_contents(TestData::RECEIPT_JSON)); + $response->method('getBody')->willReturn($stream); $response->method('getStatusCode')->willReturn(200); $httpClient = $this->createMock(ClientInterface::class); @@ -220,20 +225,21 @@ public function testGetLoginUrl() */ public function testCustomLogger() { - $response = $this->createMock(ResponseInterface::class); + $jsonstr = json_encode([ + 'receipt' => [ + 'timestamp' => 'some invalid timestamp', + 'wrapped_receipt_key' => 'some receipt key', + 'sharing_outcome' => 'SUCCESS', + ] + ]); + $stream = $this->createMock(StreamInterface::class); + $stream->method('getContents')->willReturn($jsonstr); + $stream->method('__toString')->willReturn($jsonstr); + + $response = $this->createMock(ResponseInterface::class); + $response->method('getBody')->willReturn($stream); $response->method('getStatusCode')->willReturn(200); - $response - ->method('getBody') - ->willReturn( - json_encode([ - 'receipt' => [ - 'timestamp' => 'some invalid timestamp', - 'wrapped_receipt_key' => 'some receipt key', - 'sharing_outcome' => 'SUCCESS', - ] - ]) - ); $httpClient = $this->createMock(ClientInterface::class); $httpClient->expects($this->exactly(1))