invoice-system/qbo_helper.js

118 lines
4.0 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// qbo_helper.js - FIX FÜR ERROR 3200
require('dotenv').config();
const OAuthClient = require('intuit-oauth');
const fs = require('fs');
const path = require('path');
let oauthClient = null;
const tokenFile = path.join(__dirname, 'qbo_token.json');
const getOAuthClient = () => {
if (!oauthClient) {
oauthClient = new OAuthClient({
clientId: process.env.QBO_CLIENT_ID,
clientSecret: process.env.QBO_CLIENT_SECRET,
environment: process.env.QBO_ENVIRONMENT || 'sandbox',
redirectUri: process.env.QBO_REDIRECT_URI
});
let savedToken = null;
try {
if (fs.existsSync(tokenFile)) {
const stat = fs.statSync(tokenFile);
if (stat.isFile()) {
const content = fs.readFileSync(tokenFile, 'utf8');
if (content.trim() !== "{}") {
savedToken = JSON.parse(content);
}
}
}
} catch (e) {
console.error("❌ Fehler beim Laden des gespeicherten Tokens:", e.message);
}
if (savedToken) {
oauthClient.setToken(savedToken);
console.log("✅ Gespeicherter Token aus qbo_token.json geladen.");
} else {
const envToken = {
access_token: process.env.QBO_ACCESS_TOKEN || '',
refresh_token: process.env.QBO_REFRESH_TOKEN || '',
realmId: process.env.QBO_REALM_ID
};
oauthClient.setToken(envToken);
console.log(" Token aus .env geladen (Fallback).");
}
}
return oauthClient;
};
function saveTokens() {
try {
const token = getOAuthClient().getToken();
fs.writeFileSync(tokenFile, JSON.stringify(token, null, 2));
console.log("💾 Tokens erfolgreich in qbo_token.json gespeichert.");
} catch (e) {
console.error("❌ Fehler beim Speichern der Tokens:", e.message);
}
}
async function makeQboApiCall(requestOptions) {
const client = getOAuthClient();
const doRefresh = async () => {
console.log("🔄 QBO Token Refresh wird ausgeführt...");
try {
const authResponse = await client.refresh();
console.log("✅ Token erfolgreich erneuert.");
saveTokens();
return authResponse;
} catch (e) {
console.error("❌ Refresh fehlgeschlagen:", e.originalMessage || e);
throw e;
}
};
try {
const response = await client.makeApiCall(requestOptions);
// Prüfen, ob die Antwort JSON ist (manche Auth-Fehler sind HTML/Text)
const data = response.getJson ? response.getJson() : response.json;
if (data.fault && data.fault.error) {
const errorCode = data.fault.error[0].code;
// --- FIX: 3200 (Auth Failed) HINZUGEFÜGT ---
if (errorCode === '3200' || errorCode === '3202' || errorCode === '3100') {
console.log(`⚠️ QBO meldet Token-Fehler (${errorCode}). Versuche Refresh und Retry...`);
await doRefresh();
// Retry mit neuem Token
return await client.makeApiCall(requestOptions);
}
throw new Error(`QBO API Error ${errorCode}: ${data.fault.error[0].message}`);
}
saveTokens();
return response;
} catch (e) {
// HTTP 401 Unauthorized fangen (falls die Lib wirft, statt data.fault zurückzugeben)
const isAuthError =
e.response?.status === 401 ||
(e.authResponse && e.authResponse.response && e.authResponse.response.status === 401) ||
e.message?.includes('AuthenticationFailed');
if (isAuthError) {
console.log("⚠️ 401 Unauthorized / AuthFailed erhalten. Versuche Refresh und Retry...");
await doRefresh();
return await client.makeApiCall(requestOptions);
}
throw e;
}
}
module.exports = {
getOAuthClient,
makeQboApiCall
};