几个月来,我一直在致力于一个集成电子签名 API 的项目,名为 Viafirma,该项目尚未被证明可以集成到 Appsheet 中。应该注意的是,我的应用程序生成的 PDF 文档将通过此集成发送进行签名,但由于此错误仍然存在,因此尚未实现:
“ https://sandbox.viafirma.com 请求失败,返回代码 404。截断的服务器响应:{“code”:404,“type”:“ERROR”,“message”:“HTTP 404 Not Found”, "trace":"1210a16cd6ad417ca238bb493428ed29"} (使用 muteHttpExceptions 选项检查完整响应)"
应该注意的是,开发中使用的协议是 oauth2 并且集成它请求来自 AppSheet 的回调,但我不知道如何在我的应用程序中搜索它以及在任何情况下 AppSheet 是否能够支持这种类型请求。
代码:
// Se Definen las Constantes de Autenticación y la URL Base
const OAUTH_CONSUMER_KEY = 'XXXX'; // 'tu_consumer_key' clave de consumidor de OAuth de Viafirma.
const OAUTH_CONSUMER_SECRET = 'XXXXXX'; // 'tu_consumer_secret' clave de consumidor de OAuth de Viafirma.
const VIAFIRMA_BASE_URL = 'https://sandbox.viafirma.com/documents/api/v3'; // URL base del entorno sandbox de Viafirma.
const FOLDER_ID = 'XXXXXXX'; // ID de la carpeta en Google Drive donde se almacenan los archivos
// Función para Obtener el Token OAuth
function obtenerTokenOAuth() {
const OAUTH_TOKEN_URL = `${VIAFIRMA_BASE_URL}/oauth2/token`;
const options = {
method: 'post', // Método HTTP POST.
contentType: 'application/x-www-form-urlencoded',
headers: {
'Authorization': 'Basic ' + Utilities.base64Encode(OAUTH_CONSUMER_KEY + ':' + OAUTH_CONSUMER_SECRET) // Base64 encode the client id and secret
},
payload: {
'grant_type': 'client_credentials' // Tipo de concesión de OAuth.
},
muteHttpExceptions: true // Para obtener la respuesta completa en caso de error.
};
const response = UrlFetchApp.fetch(OAUTH_TOKEN_URL, options); // Se Realiza la solicitud para obtener el token.
if (response.getResponseCode() !== 200) {
Logger.log(response.getContentText()); // Registra el contenido de la respuesta
throw new Error('Error al obtener el token OAuth: ' + response.getContentText());
}
const jsonResponse = JSON.parse(response.getContentText()); // Se Analiza la respuesta JSON.
return jsonResponse.access_token; // Se Devuelve el token de acceso.
}
// Función para leer datos desde la hoja de cálculo y firmar documentos
function leerDatosYFirmarDocumentos() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('DATOS FORM'); // Nombre de tu hoja en Google Sheets.
const data = sheet.getDataRange().getValues(); // Obtiene todos los datos de la hoja.
for (let i = 1; i < data.length; i++) {
const IDATS = data[i][0]; // Asume que la primera columna tiene el IDATS
const DOCUMENTO = data[i][14]; // Asume que la segunda columna tiene la URL del documento
if (IDATS && DOCUMENTO) {
try {
firmarDocumento(IDATS, DOCUMENTO);
} catch (error) {
Logger.log(`Error al firmar el documento con IDATS ${IDATS}: ${error.message}`);
}
} else {
Logger.log(`IDATS o DOCUMENTO no definido para la fila ${i + 1}`);
}
}
}
// Crear un nuevo conjunto de documentos
function crearNuevoSet(token) {
const payload = {
"name": "Conjunto de Documentos a Firmar"
};
const options = {
method: 'post',
contentType: 'application/json',
headers: {
'Authorization': `Bearer ${token}`
},
payload: JSON.stringify(payload)
};
const response = UrlFetchApp.fetch(`${VIAFIRMA_BASE_URL}/sets`, options);
const jsonResponse = JSON.parse(response.getContentText());
return jsonResponse.id; // ID del nuevo conjunto de documentos
}
// Función para Firmar el Documento
function firmarDocumento(IDATS, DOCUMENTO) {
if (!DOCUMENTO) {
throw new Error('La ruta del archivo no está definida');
}
const token = obtenerTokenOAuth(); // Obtén el token de acceso OAuth.
// Paso 1: Se crea un nuevo conjunto de documentos
const setId = crearNuevoSet(token);
// Paso 2: Se Agrega el documento al conjunto
agregarDocumentoAlSet(token, setId, IDATS, DOCUMENTO);
// Paso 3: Se Envia el conjunto para firma
enviarSetParaFirma(token, setId);
// Se guarda la URL del documento firmado en Google Sheets (puede necesitar ajustes)
guardarSignedUrlEnSheet(IDATS, `https://sandbox.viafirma.com/documents/api/v3/sets/${setId}`);
}
// Agregar un documento al conjunto
function agregarDocumentoAlSet(token, setId, IDATS, DOCUMENTO) {
const filePath = DOCUMENTO.split('/').pop(); // Extrae solo el nombre del archivo
const fileId = obtenerFileId(filePath);
const payload = {
"name": IDATS, // Nombre del documento (ID o nombre identificativo).
"contentType": "application/pdf", // Tipo de contenido del documento.
"content": obtenerContenidoBase64(fileId) // Contenido del documento en base64.
};
const options = {
method: 'post',
contentType: 'application/json',
headers: {
'Authorization': `Bearer ${token}`
},
payload: JSON.stringify(payload)
};
const response = UrlFetchApp.fetch(`${VIAFIRMA_BASE_URL}/sets/${setId}/documents`, options);
const jsonResponse = JSON.parse(response.getContentText());
return jsonResponse.id; // ID del documento agregado al conjunto
}
// Enviar el conjunto para firma
function enviarSetParaFirma(token, setId) {
const payload = {
"recipients": [
{
"type": "SIGNER",
"identifier": {
"type": "EMAIL",
"value": "[email protected]" // Reemplaza con el email del firmante
},
"name": "Firmante Ejemplo"
}
],
"callback": {
"url": "https://tudominio.com/callback" // Reemplaza con tu URL de callback
}
};
const options = {
method: 'post',
contentType: 'application/json',
headers: {
'Authorization': `Bearer ${token}`
},
payload: JSON.stringify(payload)
};
const response = UrlFetchApp.fetch(`${VIAFIRMA_BASE_URL}/sets/${setId}/send`, options);
const jsonResponse = JSON.parse(response.getContentText());
return jsonResponse;
}
function obtenerFileId(filePath) {
if (!filePath) {
throw new Error('La ruta del archivo no está definida');
}
// Extraer el ID del archivo de la ruta
const folderId = FOLDER_ID; // Se Reemplaza con el ID del DRIVE
const folder = DriveApp.getFolderById(folderId); // Se Reemplaza 'FOLDER_ID' con el ID de la carpeta donde se almacenan los archivos
const fileName = filePath.split('/').pop(); // Se obtiene el archivo por nombre
const files = folder.getFilesByName(fileName);
if (files.hasNext()) {
return files.next().getId();
} else {
throw new Error('Archivo no encontrado: ' + filePath);
}
}
// Función para Obtener el Contenido en Base64
function obtenerContenidoBase64(fileId) {
const file = DriveApp.getFileById(fileId); // Se Realiza la solicitud para obtener el contenido del archivo.
const blob = file.getBlob(); // Se Obtiene el blob del archivo.
return Utilities.base64Encode(blob.getBytes()); // Se Codifica el blob en base64 y lo devuelve.
}
// Función para Guardar la URL Firmada en Google Sheets
function guardarSignedUrlEnSheet(IDATS, signedUrl) {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('DATOS FORM'); // Reemplaza 'NombreDeTuHoja' con el nombre de tu hoja en Google Sheets.
const data = sheet.getDataRange().getValues(); // Obtiene todos los datos de la hoja.
for (let i = 1; i < data.length; i++) {
if (data[i][0] === IDATS) { // Busca el ID del documento en la primera columna.
sheet.getRange(i + 1, 16).setValue(signedUrl); // Guarda la URL firmada en la columna 16.
break;
}
}
}
// Llama a la función para leer datos y firmar documentos
leerDatosYFirmarDocumentos();
如果您能告诉我这种类型的集成是否可行,或者在任何情况下如果我的代码中存在某种类型的错误,请告诉我,我将非常感激。我愿意继续学习并听取您的意见。
在 Google Apps 脚本项目中,包含称为顶级/全局作用域的函数可能会导致问题。
尽管可能存在其他问题,但应删除以下代码行
// Llama a la función para leer datos y firmar documentos
leerDatosYFirmarDocumentos();