diff --git a/src/AfipSigner.php b/src/AfipCmsSigner.php similarity index 76% rename from src/AfipSigner.php rename to src/AfipCmsSigner.php index 050c5c7..87af523 100644 --- a/src/AfipSigner.php +++ b/src/AfipCmsSigner.php @@ -7,7 +7,7 @@ use litvinjuan\LaravelAfip\Exceptions\AfipSigningException; use Spatie\TemporaryDirectory\TemporaryDirectory; -class AfipSigner +class AfipCmsSigner { private string $certificate; @@ -32,13 +32,13 @@ public function sign(string $input): string File::put($input_filename, $input); try { - $result = openssl_pkcs7_sign( + $result = openssl_cms_sign( $input_filename, $output_filename, $this->getCertificate(), [$this->getPrivateKey(), $this->getPrivateKeyPassphrase()], [], - ! PKCS7_DETACHED + ! OPENSSL_CMS_DETACHED ); if (! $result) { @@ -49,21 +49,23 @@ public function sign(string $input): string throw new AfipSigningException('There was an error while signing using the certificate and key'); } - $file = fopen($output_filename, 'r'); - $i = 0; + $cms = $this->getCmsFromCmsOutputFile($output_filename); + $temporaryDirectory->delete(); - $signed = ''; - while (! feof($file)) { - $buffer = fgets($file); - if ($i++ >= 4) { - $signed .= $buffer; - } - } - fclose($file); + return $cms; + } - $temporaryDirectory->delete(); + private function getCmsFromCmsOutputFile(string $output_filename): string + { + $cms = File::get($output_filename); + + $lastHeader = "Content-Transfer-Encoding: base64"; + $lastHeaderPosition = strpos($cms, $lastHeader); + $lastHeaderLength = strlen($lastHeader); - return $signed; + return trim( + substr($cms, $lastHeaderPosition + $lastHeaderLength) + ); } private function getCertificate() diff --git a/src/AfipConfiguration.php b/src/AfipConfiguration.php index a6935a2..78ea230 100644 --- a/src/AfipConfiguration.php +++ b/src/AfipConfiguration.php @@ -14,7 +14,7 @@ class AfipConfiguration private bool $production_mode; - private AfipSigner $signer; + private AfipCmsSigner $signer; public function __construct(string $cuit = null, string $certificate = null, string $private_key = null, string $private_key_passphrase = null, bool $production_mode = true) { @@ -24,14 +24,14 @@ public function __construct(string $cuit = null, string $certificate = null, str $this->private_key_passphrase = $private_key_passphrase ?? config('afip.key-passphrase'); $this->production_mode = $production_mode ?? config('afip.production', false); - $this->signer = new AfipSigner( + $this->signer = new AfipCmsSigner( $this->certificate, $this->private_key, $this->private_key_passphrase ); } - public function getSigner(): AfipSigner + public function getSigner(): AfipCmsSigner { return $this->signer; } @@ -45,4 +45,12 @@ public function getCuit(): string { return $this->cuit; } + + public function getPublicIdentifier(): string + { + $privateKey = openssl_pkey_get_private($this->private_key, $this->private_key_passphrase); + $publicKey = openssl_pkey_get_details($privateKey)['key']; + + return hash('sha256', $publicKey); + } } diff --git a/src/AfipTokenAuthorizationProvider.php b/src/AfipTokenAuthorizationProvider.php index cd1b181..eea6017 100644 --- a/src/AfipTokenAuthorizationProvider.php +++ b/src/AfipTokenAuthorizationProvider.php @@ -37,9 +37,9 @@ public static function createServiceTokenAuthorization(AfipConfiguration $config private static function getCacheKey(AfipConfiguration $configuration, AfipService $service): string { if ($configuration->isProduction()) { - return "AFIP-TA-{$service->name}"; + return "AFIP-TA-{$configuration->getPublicIdentifier()}-{$service->name}"; } - return "AFIP-TA-{$service->name}-dev"; + return "AFIP-TA-{$configuration->getPublicIdentifier()}-{$service->name}-dev"; } } diff --git a/src/WebServices/AfipClient.php b/src/WebServices/AfipClient.php index 092bfbb..659bba0 100644 --- a/src/WebServices/AfipClient.php +++ b/src/WebServices/AfipClient.php @@ -11,6 +11,7 @@ use litvinjuan\LaravelAfip\Exceptions\AfipException; use litvinjuan\LaravelAfip\Exceptions\AfipSoapException; use litvinjuan\LaravelAfip\TokenAuthorization; +use SimpleXMLElement; use SoapClient; class AfipClient @@ -58,7 +59,7 @@ public function call(string $name, ?array $params = []) $client = $this->getClient(); $rawResponse = $client->{$name}($params); - $response = Arr::get(json_decode(json_encode($rawResponse), true), $this->getReturnKey($name)); + $response = $this->convertResponseToJson($rawResponse, $name); if (Arr::has($response, 'Errors')) { $this->throwFirstError($response); @@ -98,6 +99,17 @@ public function getSign(): string return $this->getTokenAuthorization()->getSign(); } + private function convertResponseToJson(mixed $response, string $methodName): array + { + $responseKey = $this->getReturnKey($methodName); + + if ($this->service === AfipService::wsaa) { + return json_decode(json_encode(new SimpleXMLElement($response->{$responseKey})), true); + } + + return Arr::get(json_decode(json_encode($response), true), $responseKey); + } + private function getSoapVersion() { return match ($this->service) { diff --git a/src/WebServices/AuthenticationWebService.php b/src/WebServices/AuthenticationWebService.php index 88a4c5a..d394d70 100644 --- a/src/WebServices/AuthenticationWebService.php +++ b/src/WebServices/AuthenticationWebService.php @@ -19,7 +19,7 @@ public function __construct(AfipConfiguration $configuration = null) $this->client = new AfipClient(AfipService::wsaa, $this->configuration); } - public function login(string $cms): AfipService + public function login(string $cms): array { try { return $this->client->call('loginCms', [