Шаг 3. Интеграция в качестве проверяющей стороны

В этом руководстве рассказывается, как прямые полагающиеся стороны (отдельные продавцы, компании и разработчики приложений) могут интегрировать Digital Credentials API, чтобы запрашивать и проверять цифровые удостоверения личности в приложениях Android и на сайтах.

Как работает интеграция с проверяющей стороной

Интеграция каждой полагающейся стороны состоит из четырех шагов. Шаги А и В выполняются на вашем сервере, а шаг Б – в приложении Android или браузере. Выберите шаг, чтобы перейти к подробным инструкциям:

Создайте и протестируйте интеграцию в тестовой среде, прежде чем запрашивать учетные данные для рабочей среды.

При выполнении шагов A–D используйте предварительно доверенные объекты изолированной среды Google, чтобы сразу же приступить к созданию и тестированию.

Подписание запроса

Подписывайте запросы OpenID4VP, используя тестовые ключи и метаданные тестовой среды Google.

Тестовые учетные данные

Протестируйте на устройстве Android в режиме изолированной среды с помощью тестового цифрового удостоверения Utopia.

Проверка ответов

Проверьте issuerAuth с помощью сертификата Utopia Sandbox.

Сквозная последовательность между вашим бэкенд-сервером, клиентским приложением или браузером и Google Кошельком для онлайн-проверки

Поддерживаемые форматы, учетные данные и функции

Google Кошелек поддерживает цифровые удостоверения личности на основе ISO mdoc, которые передаются по протоколу OpenID4VP (версия 1.0). Они доступны на устройствах с Android 9 (уровень API 28) и более поздними версиями, а также в поддерживаемых браузерах.

Поддерживаемые учетные данные и атрибуты

Посмотрите, какие идентификаторы doctype_value (цифровое водительское удостоверение, цифровое удостоверение личности, удостоверение личности с фотографией) и пути к атрибутам можно запрашивать у Google Кошелька.

Поддерживаемые эмитенты и сертификаты IACA

Скачайте официальные корневые сертификаты IACA для штатов США, международных эмитентов и эмитента Sandbox Utopia, чтобы подтвердить issuerAuth.


Шаг А. Форматирование и подпись запроса (на стороне сервера)

Чтобы запросить учетные данные из любого кошелька, необходимо отформатировать запрос с помощью OpenID4VP на сервере. Вы можете запросить определенные учетные данные или несколько учетных данных в одном объекте dcql_query.

Пример запроса JSON

Ниже приведен пример запроса mdoc requestJson для получения удостоверения личности из любого кошелька на устройстве Android или в интернете.

{
      "requests" : [
        {
          "protocol": "openid4vp-v1-signed",
          "data": {<signed_credential_request>} // This is an object, shouldn't be a string.
        }
      ]
}

Запрашивать шифрование

client_metadata содержит открытый ключ шифрования для каждого запроса. Вам нужно будет хранить закрытые ключи для каждого запроса и использовать их для аутентификации и авторизации токена, полученного из приложения кошелька.

Интегрированные метаданные OpenID4VP

При форматировании запроса учетных данных необходимо включить поле gw_rp_metadata_bytes в объект client_metadata (как показано в примере кода запроса ниже). Это поле содержит метаданные доверяющей стороны, закодированные в Base64URL и необходимые Google Кошельку для подтверждения вашей личности и показа вашего брендинга пользователю.

Полезная нагрузка запроса учетных данных

Параметр credential_request в requestJson содержит следующие поля.

Определенные учетные данные

{
  "response_type": "vp_token",
  "response_mode": "dc_api.jwt", // change this to dc_api if you want to demo with a non encrypted response.
  "nonce": "1234",
  "dcql_query": {
    "credentials": [
      {
        "id": "cred1",
        "format": "mso_mdoc",
        "meta": {
          // Use org.iso.18013.5.1.mDL for mDL,
          // com.google.wallet.idcard.1 for ID pass, or
          // org.iso.23220.photoid.1 for ID pass (using photo ID document type)
          "doctype_value": "org.iso.18013.5.1.mDL"
        },
        "claims": [
          {
            "path": [
              "org.iso.18013.5.1",
              "family_name"
            ],
            "intent_to_retain": false // set this to true if you are saving the value of the field
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "given_name"
            ],
            "intent_to_retain": false
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "age_over_18"
            ],
            "intent_to_retain": false
          }
        ]
      }
    ]
  },
  "client_metadata": {
    "jwks": {
      "keys": [ // sample request encryption key
        {
          "kty": "EC",
          "crv": "P-256",
          "x": "pDe667JupOe9pXc8xQyf_H03jsQu24r5qXI25x_n1Zs",
          "y": "w-g0OrRBN7WFLX3zsngfCWD3zfor5-NLHxJPmzsSvqQ",
          "use": "enc",
          "kid" : "1",  // This is required
          "alg" : "ECDH-ES",  // This is required
        }
      ]
    },
    "vp_formats_supported": {
      "mso_mdoc": {
        "deviceauth_alg_values": [
          -7
        ],
        "issuerauth_alg_values": [
          -7
        ]
      }
    },
    "gw_rp_metadata_bytes": "<base64url encoded metadata string>"
  }
}

Любые подходящие учетные данные

Ниже приведен пример запроса для цифрового водительского удостоверения, цифрового удостоверения личности и цифрового удостоверения личности (с использованием типа документа "Удостоверение личности с фотографией"). Пользователь может продолжить с любым из них.

{
  "response_type": "vp_token",
  "response_mode": "dc_api.jwt", // change this to dc_api if you want to demo with a non encrypted response.
  "nonce": "1234",
  "dcql_query": {
    "credentials": [
      {
        "id": "mdl-request",
        "format": "mso_mdoc",
        "meta": {
          "doctype_value": "org.iso.18013.5.1.mDL"
        },
        "claims": [
          {
            "path": [
              "org.iso.18013.5.1",
              "family_name"
            ],
            "intent_to_retain": false // set this to true if you are saving the value of the field
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "given_name"
            ],
            "intent_to_retain": false
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "age_over_18"
            ],
            "intent_to_retain": false
          }
        ]
      },
      {  // Credential type 2: ID pass
        "id": "id_pass-request",
        "format": "mso_mdoc",
        "meta": {
          "doctype_value": "com.google.wallet.idcard.1"
        },
        "claims": [
          {
            "path": [
              "org.iso.18013.5.1",
              "family_name"
            ],
            "intent_to_retain": false // set this to true if you are saving the value of the field
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "given_name"
            ],
            "intent_to_retain": false
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "age_over_18"
            ],
            "intent_to_retain": false
          }
        ]
      },
      {  // Credential type 3: ID pass (using photo ID document type)
        "id": "photo_id-request",
        "format": "mso_mdoc",
        "meta": {
          "doctype_value": "org.iso.23220.photoid.1"
        },
        "claims": [
          {
            "path": [
              "org.iso.23220.1",
              "family_name"
            ],
            "intent_to_retain": false // set this to true if you are saving the value of the field
          },
          {
            "path": [
              "org.iso.23220.1",
              "given_name"
            ],
            "intent_to_retain": false
          },
          {
            "path": [
              "org.iso.23220.1",
              "age_over_18"
            ],
            "intent_to_retain": false
          }
        ]
      }
    ]
    credential_sets : [
      {
        "options": [
          [ "mdl-request" ],
          [ "id_pass-request" ],
          [ "photo_id-request" ]
        ]
      }
    ]
  },
  "client_metadata": {
    "jwks": {
      "keys": [ // sample request encryption key
        {
          "kty": "EC",
          "crv": "P-256",
          "x": "pDe667JupOe9pXc8xQyf_H03jsQu24r5qXI25x_n1Zs",
          "y": "w-g0OrRBN7WFLX3zsngfCWD3zfor5-NLHxJPmzsSvqQ",
          "use": "enc",
          "kid" : "1",  // This is required
          "alg" : "ECDH-ES",  // This is required
        }
      ]
    },
    "vp_formats_supported": {
      "mso_mdoc": {
        "deviceauth_alg_values": [
          -7
        ],
        "issuerauth_alg_values": [
          -7
        ]
      }
    },
    "gw_rp_metadata_bytes": "<base64url encoded metadata string>"
  }
}

Вы можете запросить любое количество поддерживаемых атрибутов из любого удостоверения личности, сохраненного в Google Кошельке.

Подписанные запросы

Подписанные запросы (запросы авторизации, защищенные JWT) включают запрос на подтверждение в криптографически подписанный веб-токен JSON (JWT). При этом используется ваша инфраструктура PKI, что обеспечивает целостность запроса и подтверждает вашу личность для Google Кошелька.

Требования

Прежде чем вносить изменения в код для подписанных запросов, убедитесь, что у вас есть:

  • Закрытый ключ. Вам нужен закрытый ключ (например, Elliptic Curve ES256), чтобы подписывать запросы, управляемые на вашем сервере. Для тестирования в изолированной среде в качестве полагающейся стороны используйте закрытый ключ для тестирования в изолированной среде.
  • Сертификат. Вам нужен стандартный сертификат X.509, полученный из вашей пары ключей.
  • Регистрация. Убедитесь, что Google Кошелек доверяет вашему общедоступному сертификату (или корневому центру сертификации для регистраторов верификаторов).

Логика создания запросов

При создании и подписании запроса OpenID4VP обратите особое внимание на то, какой формат кодировки ожидается для каждого поля. Если вы перепутаете base64 и base64url или заполнение (=), то проверка подписи или источника не будет выполнена.

Поле Требуемая кодировка / формат Символы и отступы
client_id (x509_hash:<hash>) base64url без заполнения. Используется алфавит, безопасный для URL (-, _); удалите конечные символы =.
expected_origins (android:apk-key-hash:<hash>) Стандартный Base64 без заполнения Используется стандартный алфавит (+, /); удаляется завершающий символ =.
expected_origins (https://...) URL в нижнем регистре без косой черты в конце. Пример: https://example.com (без / в конце).
Заголовок JWS x5c Стандартная кодировка Base64 с заполнением. Используется стандартный алфавит (+, /); сохраняется заполнение = в конце.

Чтобы создать запрос, используйте закрытый ключ и упакуйте полезную нагрузку в JWS:

def construct_openid4vp_request(
    doctypes: list[str],
    requested_fields: list[dict],
    nonce_base64: str,
    jwe_encryption_public_jwk: jwk.JWK,
    is_zkp_request: bool,
    is_signed_request: bool,
    state: dict,
    origin: str
) -> dict:

    # ... [Existing logic to build 'presentation_definition' and basic 'request_payload'] ...

    # ------------------------------------------------------------------
    # SIGNED REQUEST IMPLEMENTATION (JAR)
    # ------------------------------------------------------------------
    if is_signed_request:
        try:
            # 1. Load the Verifier's Certificate
            # We must load the PEM string into a cryptography x509 object
            verifier_cert_obj = x509.load_pem_x509_certificate(
                CERTIFICATE.encode('utf-8'),
                backend=default_backend()
            )

            # 2. Calculate Client ID (x509_hash)
            # We calculate the SHA-256 hash of the DER-encoded certificate.
            cert_der = verifier_cert_obj.public_bytes(serialization.Encoding.DER)
            verifier_fingerprint_bytes = hashlib.sha256(cert_der).digest()

            # Important: Use URL-safe Base64 (base64url: '-', '_') without padding ('=') for client_id
            verifier_fingerprint_b64 = base64.urlsafe_b64encode(verifier_fingerprint_bytes).decode('utf-8').rstrip("=")

            # Format the client_id as required by the spec
            client_id = f'x509_hash:{verifier_fingerprint_b64}'

            # 3. Update Request Payload with JAR specific fields
            request_payload["client_id"] = client_id

            # Explicitly set expected origins to prevent relay attacks
            # Important: Use standard Base64 (not URL-safe) without padding for android:apk-key-hash:
            # origin = f"android:apk-key-hash:{base64.b64encode(apk_cert_hash).decode('utf-8').rstrip('=')}"
            # Format for web origin (lowercase URL without trailing slash): origin = "https://example.com"
            if origin:
                request_payload["expected_origins"] = [origin]

            # 4. Create Signed JWT (JWS)
            # Load the signing private key
            signing_key = jwk.JWK.from_pem(PRIVATE_KEY.encode('utf-8'))

            # Initialize JWS with the JSON payload
            jws_token = jws.JWS(json.dumps(request_payload).encode('utf-8'))

            # Construct the JOSE Header
            # 'x5c' (X.509 Certificate Chain) is critical: it allows the wallet
            # to validate your key against the one registered in the console.
            # Important: Use standard Base64 ('+', '/') WITH padding ('=') for x5c
            x5c_value = base64.b64encode(cert_der).decode('utf-8')

            protected_header = {
                "alg": "ES256",                 # Algorithm (e.g., ES256 or RS256)
                "typ": "oauth-authz-req+jwt",   # Standard type for JAR
                "kid": "1",                     # Key ID
                "x5c": [x5c_value]              # Embed the certificate
            }

            # Sign the token
            jws_token.add_signature(
                key=signing_key,
                alg=None,
                protected=json_encode(protected_header)
            )

            # 5. Return the Request Object
            # Instead of returning the raw JSON, we return the signed JWT string
            # under the 'request' key.
            return {"request": jws_token.serialize(compact=True)}

        except Exception as e:
            print(f"Error signing OpenID4VP request: {e}")
            return None

    # ... [Fallback for unsigned requests] ...
    return request_payload

Шаг Б. Активируйте API (на стороне клиента)

Весь запрос к API должен генерироваться на стороне сервера. В зависимости от платформы передайте сгенерированный JSON в API платформы.

В приложении (Android)

Чтобы запросить удостоверение личности из приложений Android, выполните следующие действия:

Обновить зависимости

В файле build.gradle проекта обновите зависимости, чтобы использовать Менеджер учетных данных (бета-версия):

dependencies {
    implementation("androidx.credentials:credentials:1.5.0-beta01")
    implementation("androidx.credentials:credentials-play-services-auth:1.5.0-beta01")
}

Как настроить Менеджер учетных данных

Чтобы настроить и инициализировать объект CredentialManager, добавьте логику, аналогичную следующей:

// Use your app or activity context to instantiate a client instance of CredentialManager.
val credentialManager = CredentialManager.create(context)

Запросить атрибуты идентификации

Вместо того чтобы указывать отдельные параметры для запросов идентификации, приложение предоставляет их все вместе в виде строки JSON в CredentialOption. Менеджер учетных данных передает эту строку JSON доступным цифровым кошелькам, не проверяя ее содержимое. Каждый кошелек отвечает за:

  • Анализ строки JSON для понимания запроса идентификации.
  • Определяет, какие из сохраненных учетных данных соответствуют запросу.

Мы рекомендуем партнерам создавать запросы на сервере даже для интеграции приложений Android.

В качестве request в вызове функции GetDigitalCredentialOption() используйте requestJson из раздела Форматирование и подпись запроса.

// The request in the JSON format to conform with
// the JSON-ified Digital Credentials API request definition.
val requestJson = generateRequestFromServer()
val digitalCredentialOption =
    GetDigitalCredentialOption(requestJson = requestJson)

// Use the option from the previous step to build the `GetCredentialRequest`.
val getCredRequest = GetCredentialRequest(
    listOf(digitalCredentialOption)
)

coroutineScope.launch {
    try {
        val result = credentialManager.getCredential(
            context = activityContext,
            request = getCredRequest
        )
        verifyResult(result)
    } catch (e : GetCredentialException) {
        handleFailure(e)
    }
}

Обработка ответа с учетными данными

Получив ответ от кошелька, проверьте, является ли он успешным и содержит ли ответ credentialJson:

// Handle the successfully returned credential.
fun verifyResult(result: GetCredentialResponse) {
    val credential = result.credential
    when (credential) {
        is DigitalCredential -> {
            val responseJson = credential.credentialJson
            validateResponseOnServer(responseJson) // make a server call to validate the response
        }
        else -> {
            // Catch any unrecognized credential type here.
            Log.e(TAG, "Unexpected type of credential ${credential.type}")
        }
    }
}

// Handle failure.
fun handleFailure(e: GetCredentialException) {
  when (e) {
        is GetCredentialCancellationException -> {
            // The user intentionally canceled the operation and chose not
            // to share the credential.
        }
        is GetCredentialInterruptedException -> {
            // Retry-able error. Consider retrying the call.
        }
        is NoCredentialException -> {
            // No credential was available.
        }
        else -> Log.w(TAG, "Unexpected exception type ${e::class.java}")
    }
}

Ответ credentialJson содержит зашифрованный токен identityToken (JWT), определенный W3C. За создание этого ответа отвечает приложение "Кошелек".

Пример:

{
  "protocol" : "openid4vp-v1-signed",
  "data" : {
    <encrypted_response>
  }
}

Передайте этот ответ на свой сервер, чтобы проверить его подлинность. Подробнее о том, как расшифровать и проверить ответ…

Веб

Чтобы запросить удостоверение личности с помощью Digital Credentials API в Chrome или другом поддерживаемом браузере, выполните следующий запрос:

const credentialResponse = await navigator.credentials.get({
          digital : {
          requests : [
            {
              protocol: "openid4vp-v1-signed",
              data: {<credential_request>} // This is an object, shouldn't be a string.
            }
          ]
        }
      })

Отправьте ответ от этого API обратно на свой сервер, чтобы проверить ответ учетных данных.


Шаг 3. Расшифруйте и проверьте ответ (на стороне сервера)

Когда кошелек вернет зашифрованный токен identityToken (JWT), вы должны выполнить строгую проверку на стороне сервера, прежде чем доверять данным.

Расшифровка ответа

Расшифруйте JWE с помощью закрытого ключа, соответствующего открытому ключу, который был отправлен в поле client_metadata запроса. Это дает vp_token.

Пример на Python:

from jwcrypto import jwe, jwk

# Retrieve the Private Key from Datastore
reader_private_jwk = jwk.JWK.from_json(jwe_private_key_json_str)
# Save public key thumbprint for session transcript
encryption_public_jwk_thumbprint = reader_private_jwk.thumbprint()


# Decrypt the JWE encrypted response from Google Wallet
jwe_object = jwe.JWE()
jwe_object.deserialize(encrypted_jwe_response_from_wallet)
jwe_object.decrypt(reader_private_jwk)
decrypted_payload_bytes = jwe_object.payload
decrypted_data = json.loads(decrypted_payload_bytes)

decrypted_data возвращает объект vp_token JSON, содержащий учетные данные:

{
  "vp_token":
  {
    "cred1": ["<base64UrlNoPadding_encoded_credential>"] // This applies to OpenID4VP 1.0 spec.
  }
}

Восстановление SessionTranscript и проверка DeviceResponse

  1. Создайте SessionTranscript из ISO/IEC 18013-5:2021 со структурой Handover для Android или веб-страниц:
SessionTranscript = [
  null,                // DeviceEngagementBytes not available
  null,                // EReaderKeyBytes not available
  [
    "OpenID4VPDCAPIHandover",
    AndroidHandoverDataBytes   // BrowserHandoverDataBytes for Web
  ]
]

Для передачи как на Android, так и на веб-странице используйте тот же nonce, который вы использовали для создания credential_request.

Android Handover

AndroidHandoverData = [
  origin,             // "android:apk-key-hash:<base64SHA256_ofAppSigningCert>",
  nonce,           // nonce that was used to generate credential request,
  encryption_public_jwk_thumbprint,  // Encryption public key (JWK) Thumbprint
]

AndroidHandoverDataBytes = hashlib.sha256(cbor2.dumps(AndroidHandoverData)).digest()

Передача браузера

BrowserHandoverData =[
  origin,               // Origin URL
  nonce,               //  nonce that was used to generate credential request
  encryption_public_jwk_thumbprint,  // Encryption public key (JWK) Thumbprint
]

BrowserHandoverDataBytes = hashlib.sha256(cbor2.dumps(BrowserHandoverData)).digest()

При использовании SessionTranscript ответ устройства должен быть проверен в соответствии с пунктом 9 стандарта ISO/IEC 18013-5:2021.

Проверка состоит из четырех этапов:

  1. Проверьте сертификат издателя. Извлеките цепочку сертификатов подписи издателя из issuerAuth и проверьте ее на соответствие доверенным корневым сертификатам IACA. Ознакомьтесь со списком поддерживаемых эмитентов и сертификатов IACA (и добавьте сертификат Utopia Sandbox, если вы тестируете удостоверение в изолированной среде).
  2. Проверка подписи MSO (ISO/IEC 18013-5, раздел 9.1.2).
  3. Рассчитайте и проверьте ValueDigests для элементов данных (ISO/IEC 18013-5, раздел 9.1.2).
  4. Проверьте подпись deviceSignature (ISO/IEC 18013-5, раздел 9.1.3).
{
  "version": "1.0",
  "documents": [
    {
      "docType": "org.iso.18013.5.1.mDL",
      "issuerSigned": {
        "nameSpaces": {...}, // contains data elements
        "issuerAuth": [...]  // COSE_Sign1 w/ issuer PK, mso + sig
      },
      "deviceSigned": {
        "nameSpaces": 24(<< {} >>), // empty
        "deviceAuth": {
          "deviceSignature": [...] // COSE_Sign1 w/ device signature
        }
      }
    }
  ],
  "status": 0
}

Шаг 4 (необязательно). Подтверждение возраста с обеспечением конфиденциальности (ZKP)

Чтобы поддерживать доказательства с нулевым разглашением (например, подтверждать, что пользователю больше 18 лет, не видя его точную дату рождения), измените формат запроса на mso_mdoc_zk и укажите необходимую конфигурацию zk_system_type.

Общие сведения о ZKP и его возможностях приведены в разделе часто задаваемых вопросов.

  ...
  "dcql_query": {
    "credentials": [{
      "id": "cred1",
      "format": "mso_mdoc_zk",
      "meta": {
        "doctype_value": "org.iso.18013.5.1.mDL"
        "zk_system_type": [
        {
          "system": "longfellow-libzk-v1",
          "circuit_hash": "f88a39e561ec0be02bb3dfe38fb609ad154e98decbbe632887d850fc612fea6f", // This will differ if you need more than 1 attribute.
          "num_attributes": 1, // number of attributes (in claims) this has can support
          "version": 5,
          "block_enc_hash": 4096,
          "block_enc_sig": 2945,
        }
        {
          "system": "longfellow-libzk-v1",
          "circuit_hash": "137e5a75ce72735a37c8a72da1a8a0a5df8d13365c2ae3d2c2bd6a0e7197c7c6", // This will differ if you need more than 1 attribute.
          "num_attributes": 1, // number of attributes (in claims) this has can support
          "version": 6,
          "block_enc_hash": 4096,
          "block_enc_sig": 2945,
        }
       ],
       "verifier_message": "challenge"
      },
     "claims": [{
         ...
      "client_metadata": {
        "jwks": {
          "keys": [ // sample request encryption key
            {
              ...

Вы получите от кошелька зашифрованное доказательство с нулевым разглашением. Вы можете проверить это подтверждение по сертификатам IACA эмитента, используя библиотеку Google longfellow-zk.

verifier-service содержит готовый к развертыванию сервер на основе Docker, который позволяет проверять ответ по сертификатам IACA эмитента.

Вы можете изменить файл certs.pem, чтобы управлять сертификатами издателя IACA, которым вы хотите доверять.


SDK, примеры реализации и поддержка

Чтобы ускорить реализацию, используйте наши библиотеки с открытым исходным кодом, эталонный сервер и инструмент проверки веб-страниц.

Ресурс / библиотека Что он дает Ссылка
Identity Verifiers Reference Implementation Пример сервера и клиента, демонстрирующий создание запроса OpenID4VP, подпись JWS, расшифровку JWE и проверку ответа mdoc. Хранилище GitHub
Multipaz SDK Библиотека с открытым исходным кодом (OpenWallet Foundation), включая DeviceResponseParser для декодирования CBOR, реконструкцию SessionTranscript и проверку подписи ISO/IEC 18013-5/7. openwallet-foundation/multipaz
Библиотека Longfellow ZKP и сервис Verifier Библиотека с открытым исходным кодом и сервер на основе Docker для проверки возраста и атрибутов с помощью доказательства с нулевым разглашением (mso_mdoc_zk). google/longfellow-zk
Интерактивный инструмент проверки сайтов Размещенный в интернете верификатор для тестирования запросов W3C Digital Credentials API / OpenID4VP к Google Кошельку. verifier.multipaz.org
Спецификация OpenID4VP 1.0 Официальная спецификация протокола OpenID for Verifiable Presentations 1.0. Спецификация OpenID4VP
Часто задаваемые вопросы и поддержка разработчиков Ответы на часто задаваемые вопросы о технической интеграции. Если вам нужна помощь с отладкой, напишите на адрес wallet-identity-rp-support@google.com. Часто задаваемые вопросы о технической интеграции

Дальнейшие действия