Encrypt AES-GCM in PHP, decrypt in Javascript

I have a message encrypted in PHP like this:

function encode(string $input): string
{
    return base64EncodeSafe(encodeOpenSsl($input));
}

function encodeOpenSsl(string $string): string
{
    if (!defined('CRYPT_KEY') || !defined('CRYPT_CIPHER')) {
        return $string;
    }

    $key = hex2bin(CRYPT_KEY);
    $ivLength = openssl_cipher_iv_length(CRYPT_CIPHER);
    do {
        $iv = openssl_random_pseudo_bytes($ivLength, $cstrong);
    } while (!$iv && !$cstrong);
    $encryptedString = $iv . openssl_encrypt($string, CRYPT_CIPHER, $key, OPENSSL_RAW_DATA, $iv, $tag);

    if (!empty($tag)) {
        $encryptedString .= '||' . $tag;
    }
    return base64_encode($encryptedString . '||' . $tag);
}

function base64EncodeSafe(string $string): string
{
    return str_replace(['/', '+'], ['_', '-'], base64_encode($string));
}

And I need to decrypt it in Javascript. This is my decryption code:

async function decode(input) {
    return await decodeOpenSsl(base64DecodeSafe(input));
}

function base64DecodeSafe(string) {
    return atob(string.replace(/_/g, '/').replace(/-/g, '+'));
}

async function decodeOpenSsl(data) {
    const key = hexStringToByteArray(CRYPT_KEY);
    const parts = atob(data).split('||');
    const ivAndEncrypted = new Uint8Array(parts[0].split('').map(c => c.charCodeAt(0)));
    const tag = new Uint8Array(parts[1].split('').map(c => c.charCodeAt(0)));
    const iv = ivAndEncrypted.slice(0, 12);
    const encrypted = ivAndEncrypted.slice(12);

    const encryptedWithTag = new Uint8Array([...encrypted, ...tag]);

    const cryptoKey = await crypto.subtle.importKey(
        'raw',
        key,
        { name: 'AES-GCM' },
        false,
        ['decrypt']
    );

    const decrypted = await crypto.subtle.decrypt(
        { name: 'AES-GCM', iv: iv, additionalData: new Uint8Array(), tagLength: 128 },
        cryptoKey,
        encryptedWithTag
    );

    return new TextDecoder().decode(decrypted);
}

function hexStringToByteArray(hexString) {
    var result = [];
    while (hexString.length >= 2) {
        result.push(parseInt(hexString.substring(0, 2), 16));
        hexString = hexString.substring(2, hexString.length);
    }
    return new Uint8Array(result);
}

But I keep getting the following:

DOMException [OperationError]: The operation failed for an operation-specific reason
at AESCipherJob.onDone (node:internal/crypto/util:445:19) {
[cause]: [Error: Cipher job failed]

The error doesn’t give much info probably for security reasons, but maybe someone had a problem like this and could help. Thank you.