From 5952d00de445a1612adf732fa84c49f8780ed6a1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Nov 2025 18:04:37 +0000 Subject: [PATCH 1/5] Initial plan From 5b7d17f7789ffeae10c2353be4fa33ec7a0b5c09 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Nov 2025 18:19:19 +0000 Subject: [PATCH 2/5] Add enforce_handoff property with validation logic Co-authored-by: mehmet-yoti <111424390+mehmet-yoti@users.noreply.github.com> --- src/DocScan/Session/Create/SdkConfig.php | 34 +++++ .../Session/Create/SdkConfigBuilder.php | 12 ++ .../Session/Create/SdkConfigBuilderTest.php | 131 ++++++++++++++++++ 3 files changed, 177 insertions(+) diff --git a/src/DocScan/Session/Create/SdkConfig.php b/src/DocScan/Session/Create/SdkConfig.php index 43c0498d..55a81ef6 100644 --- a/src/DocScan/Session/Create/SdkConfig.php +++ b/src/DocScan/Session/Create/SdkConfig.php @@ -58,6 +58,11 @@ class SdkConfig implements \JsonSerializable */ private $allowHandoff; + /** + * @var bool|null + */ + private $enforceHandoff; + /** * @var AttemptsConfiguration|null */ @@ -94,6 +99,7 @@ class SdkConfig implements \JsonSerializable * @param string|null $errorUrl * @param string|null $privacyPolicyUrl * @param bool|null $allowHandoff + * @param bool|null $enforceHandoff * @param array|null $idDocumentTextDataExtractionRetriesConfig * @param string|null $biometricConsentFlow * @param string|null $darkMode @@ -111,6 +117,7 @@ public function __construct( ?string $errorUrl, ?string $privacyPolicyUrl = null, ?bool $allowHandoff = null, + ?bool $enforceHandoff = null, ?array $idDocumentTextDataExtractionRetriesConfig = null, ?string $biometricConsentFlow = null, ?string $darkMode = null, @@ -127,6 +134,8 @@ public function __construct( $this->errorUrl = $errorUrl; $this->privacyPolicyUrl = $privacyPolicyUrl; $this->allowHandoff = $allowHandoff; + $this->validateEnforceHandoff($allowHandoff, $enforceHandoff); + $this->enforceHandoff = $enforceHandoff; if (!is_null($idDocumentTextDataExtractionRetriesConfig)) { $this->attemptsConfiguration = new AttemptsConfiguration($idDocumentTextDataExtractionRetriesConfig); } @@ -152,6 +161,7 @@ public function jsonSerialize(): \stdClass 'error_url' => $this->getErrorUrl(), 'privacy_policy_url' => $this->getPrivacyPolicyUrl(), 'allow_handoff' => $this->getAllowHandoff(), + 'enforce_handoff' => $this->getEnforceHandoff(), 'attempts_configuration' => $this->getAttemptsConfiguration(), 'biometric_consent_flow' => $this->getBiometricConsentFlow(), 'dark_mode' => $this->getDarkMode(), @@ -160,6 +170,22 @@ public function jsonSerialize(): \stdClass ]); } + /** + * Validates that enforce_handoff cannot be true when allow_handoff is false + * + * @param bool|null $allowHandoff + * @param bool|null $enforceHandoff + * @throws \Yoti\DocScan\Exception\DocScanException + */ + private function validateEnforceHandoff(?bool $allowHandoff, ?bool $enforceHandoff): void + { + if ($enforceHandoff === true && $allowHandoff === false) { + throw new \Yoti\DocScan\Exception\DocScanException( + 'enforce_handoff cannot be set to true when allow_handoff is false' + ); + } + } + /** * @return string|null */ @@ -240,6 +266,14 @@ public function getAllowHandoff(): ?bool return $this->allowHandoff; } + /** + * @return bool|null + */ + public function getEnforceHandoff(): ?bool + { + return $this->enforceHandoff; + } + /** * @return AttemptsConfiguration|null */ diff --git a/src/DocScan/Session/Create/SdkConfigBuilder.php b/src/DocScan/Session/Create/SdkConfigBuilder.php index 5a4661dd..9647da81 100644 --- a/src/DocScan/Session/Create/SdkConfigBuilder.php +++ b/src/DocScan/Session/Create/SdkConfigBuilder.php @@ -61,6 +61,11 @@ class SdkConfigBuilder */ private $allowHandoff; + /** + * @var bool|null + */ + private $enforceHandoff; + /** * @var array|null */ @@ -156,6 +161,12 @@ public function withAllowHandoff(bool $allowHandoff): self return $this; } + public function withEnforceHandoff(bool $enforceHandoff): self + { + $this->enforceHandoff = $enforceHandoff; + return $this; + } + public function withBiometricConsentFlow(string $biometricConsentFlow): self { $this->biometricConsentFlow = $biometricConsentFlow; @@ -264,6 +275,7 @@ public function build(): SdkConfig $this->errorUrl, $this->privacyPolicyUrl, $this->allowHandoff, + $this->enforceHandoff, $this->idDocumentTextDataExtractionRetriesConfig, $this->biometricConsentFlow, $this->darkMode, diff --git a/tests/DocScan/Session/Create/SdkConfigBuilderTest.php b/tests/DocScan/Session/Create/SdkConfigBuilderTest.php index 846f2433..762836b2 100644 --- a/tests/DocScan/Session/Create/SdkConfigBuilderTest.php +++ b/tests/DocScan/Session/Create/SdkConfigBuilderTest.php @@ -348,4 +348,135 @@ public function shouldSetCorrectValueWithDarkModeOff() $this->assertEquals('OFF', $result->getDarkMode()); } + + /** + * @test + * @covers ::withEnforceHandoff + * @covers \Yoti\DocScan\Session\Create\SdkConfig::getEnforceHandoff + */ + public function shouldSetEnforceHandoffToTrue() + { + $result = (new SdkConfigBuilder()) + ->withEnforceHandoff(true) + ->build(); + + $this->assertTrue($result->getEnforceHandoff()); + } + + /** + * @test + * @covers ::withEnforceHandoff + * @covers \Yoti\DocScan\Session\Create\SdkConfig::getEnforceHandoff + */ + public function shouldSetEnforceHandoffToFalse() + { + $result = (new SdkConfigBuilder()) + ->withEnforceHandoff(false) + ->build(); + + $this->assertFalse($result->getEnforceHandoff()); + } + + /** + * @test + * @covers \Yoti\DocScan\Session\Create\SdkConfigBuilder::build + * @covers \Yoti\DocScan\Session\Create\SdkConfig::getEnforceHandoff + */ + public function enforceHandoffShouldBeNullWhenItIsNotSet() + { + $result = (new SdkConfigBuilder()) + ->withAllowedCaptureMethod(self::SOME_CAPTURE_METHOD) + ->build(); + + $this->assertNull($result->getEnforceHandoff()); + } + + /** + * @test + * @covers ::withEnforceHandoff + * @covers ::withAllowHandoff + * @covers \Yoti\DocScan\Session\Create\SdkConfig::getEnforceHandoff + * @covers \Yoti\DocScan\Session\Create\SdkConfig::getAllowHandoff + */ + public function shouldSetBothEnforceHandoffAndAllowHandoffToTrue() + { + $result = (new SdkConfigBuilder()) + ->withAllowHandoff(true) + ->withEnforceHandoff(true) + ->build(); + + $this->assertTrue($result->getAllowHandoff()); + $this->assertTrue($result->getEnforceHandoff()); + } + + /** + * @test + * @covers ::withEnforceHandoff + * @covers ::withAllowHandoff + * @covers \Yoti\DocScan\Session\Create\SdkConfig::__construct + * @expectedException \Yoti\DocScan\Exception\DocScanException + * @expectedExceptionMessage enforce_handoff cannot be set to true when allow_handoff is false + */ + public function shouldThrowExceptionWhenEnforceHandoffTrueAndAllowHandoffFalse() + { + $this->expectException(\Yoti\DocScan\Exception\DocScanException::class); + $this->expectExceptionMessage('enforce_handoff cannot be set to true when allow_handoff is false'); + + (new SdkConfigBuilder()) + ->withAllowHandoff(false) + ->withEnforceHandoff(true) + ->build(); + } + + /** + * @test + * @covers ::withEnforceHandoff + * @covers ::withAllowHandoff + * @covers \Yoti\DocScan\Session\Create\SdkConfig::getEnforceHandoff + * @covers \Yoti\DocScan\Session\Create\SdkConfig::getAllowHandoff + */ + public function shouldAllowEnforceHandoffFalseWhenAllowHandoffFalse() + { + $result = (new SdkConfigBuilder()) + ->withAllowHandoff(false) + ->withEnforceHandoff(false) + ->build(); + + $this->assertFalse($result->getAllowHandoff()); + $this->assertFalse($result->getEnforceHandoff()); + } + + /** + * @test + * @covers ::withEnforceHandoff + * @covers \Yoti\DocScan\Session\Create\SdkConfig::getEnforceHandoff + */ + public function shouldAllowEnforceHandoffTrueWhenAllowHandoffIsNull() + { + $result = (new SdkConfigBuilder()) + ->withEnforceHandoff(true) + ->build(); + + $this->assertNull($result->getAllowHandoff()); + $this->assertTrue($result->getEnforceHandoff()); + } + + /** + * @test + * @covers \Yoti\DocScan\Session\Create\SdkConfig::jsonSerialize + */ + public function shouldIncludeEnforceHandoffInJsonSerialization() + { + $result = (new SdkConfigBuilder()) + ->withAllowHandoff(true) + ->withEnforceHandoff(true) + ->build(); + + $expected = [ + 'allow_handoff' => true, + 'enforce_handoff' => true + ]; + + $this->assertJsonStringEqualsJsonString(json_encode($expected), json_encode($result)); + } } From 85a8b368e169060d2fbac10d6c5ebc435a0b01cb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Nov 2025 18:19:46 +0000 Subject: [PATCH 3/5] Remove empty AI_PLAN.md file Co-authored-by: mehmet-yoti <111424390+mehmet-yoti@users.noreply.github.com> --- AI_PLAN.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 AI_PLAN.md diff --git a/AI_PLAN.md b/AI_PLAN.md deleted file mode 100644 index e69de29b..00000000 From 35a3ab4939120cdd3c2d7f78b7ac08b34babdd0f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Nov 2025 18:23:34 +0000 Subject: [PATCH 4/5] Fix code review issues: import DocScanException and remove deprecated annotations Co-authored-by: mehmet-yoti <111424390+mehmet-yoti@users.noreply.github.com> --- src/DocScan/Session/Create/SdkConfig.php | 5 +++-- tests/DocScan/Session/Create/SdkConfigBuilderTest.php | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/DocScan/Session/Create/SdkConfig.php b/src/DocScan/Session/Create/SdkConfig.php index 55a81ef6..4828e909 100644 --- a/src/DocScan/Session/Create/SdkConfig.php +++ b/src/DocScan/Session/Create/SdkConfig.php @@ -4,6 +4,7 @@ namespace Yoti\DocScan\Session\Create; +use Yoti\DocScan\Exception\DocScanException; use Yoti\Util\Json; class SdkConfig implements \JsonSerializable @@ -175,12 +176,12 @@ public function jsonSerialize(): \stdClass * * @param bool|null $allowHandoff * @param bool|null $enforceHandoff - * @throws \Yoti\DocScan\Exception\DocScanException + * @throws DocScanException */ private function validateEnforceHandoff(?bool $allowHandoff, ?bool $enforceHandoff): void { if ($enforceHandoff === true && $allowHandoff === false) { - throw new \Yoti\DocScan\Exception\DocScanException( + throw new DocScanException( 'enforce_handoff cannot be set to true when allow_handoff is false' ); } diff --git a/tests/DocScan/Session/Create/SdkConfigBuilderTest.php b/tests/DocScan/Session/Create/SdkConfigBuilderTest.php index 762836b2..9b78ba98 100644 --- a/tests/DocScan/Session/Create/SdkConfigBuilderTest.php +++ b/tests/DocScan/Session/Create/SdkConfigBuilderTest.php @@ -414,8 +414,6 @@ public function shouldSetBothEnforceHandoffAndAllowHandoffToTrue() * @covers ::withEnforceHandoff * @covers ::withAllowHandoff * @covers \Yoti\DocScan\Session\Create\SdkConfig::__construct - * @expectedException \Yoti\DocScan\Exception\DocScanException - * @expectedExceptionMessage enforce_handoff cannot be set to true when allow_handoff is false */ public function shouldThrowExceptionWhenEnforceHandoffTrueAndAllowHandoffFalse() { From dd371efc0595c458930c21b1f4ce39a0d3018317 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Nov 2025 18:25:07 +0000 Subject: [PATCH 5/5] Add documentation and improve imports in tests Co-authored-by: mehmet-yoti <111424390+mehmet-yoti@users.noreply.github.com> --- src/DocScan/Session/Create/SdkConfigBuilder.php | 8 ++++++++ tests/DocScan/Session/Create/SdkConfigBuilderTest.php | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/DocScan/Session/Create/SdkConfigBuilder.php b/src/DocScan/Session/Create/SdkConfigBuilder.php index 9647da81..946fd3fc 100644 --- a/src/DocScan/Session/Create/SdkConfigBuilder.php +++ b/src/DocScan/Session/Create/SdkConfigBuilder.php @@ -161,6 +161,14 @@ public function withAllowHandoff(bool $allowHandoff): self return $this; } + /** + * Sets whether handoff should be enforced during the session. + * Note: enforce_handoff cannot be set to true if allow_handoff is false. + * The validation will throw a DocScanException during build(). + * + * @param bool $enforceHandoff Whether to enforce handoff + * @return $this + */ public function withEnforceHandoff(bool $enforceHandoff): self { $this->enforceHandoff = $enforceHandoff; diff --git a/tests/DocScan/Session/Create/SdkConfigBuilderTest.php b/tests/DocScan/Session/Create/SdkConfigBuilderTest.php index 9b78ba98..e58d32d1 100644 --- a/tests/DocScan/Session/Create/SdkConfigBuilderTest.php +++ b/tests/DocScan/Session/Create/SdkConfigBuilderTest.php @@ -3,6 +3,7 @@ namespace Yoti\Test\DocScan\Session\Create; use Yoti\DocScan\Constants; +use Yoti\DocScan\Exception\DocScanException; use Yoti\DocScan\Session\Create\SdkConfigBuilder; use Yoti\Test\TestCase; @@ -417,7 +418,7 @@ public function shouldSetBothEnforceHandoffAndAllowHandoffToTrue() */ public function shouldThrowExceptionWhenEnforceHandoffTrueAndAllowHandoffFalse() { - $this->expectException(\Yoti\DocScan\Exception\DocScanException::class); + $this->expectException(DocScanException::class); $this->expectExceptionMessage('enforce_handoff cannot be set to true when allow_handoff is false'); (new SdkConfigBuilder())