GithubHelp home page GithubHelp logo

kontist / kontist.github.io Goto Github PK

View Code? Open in Web Editor NEW
3.0 3.0 0.0 5.93 MB

Developer Portal

Home Page: https://kontist.dev

HTML 35.77% Ruby 1.07% CSS 2.62% JavaScript 4.41% TypeScript 41.67% Shell 0.20% SCSS 14.25%

kontist.github.io's People

Contributors

dependabot[bot] avatar dmytroyarmak avatar hihuz avatar jablonskipj avatar johannespfeiffer avatar p-janik avatar pstockschlaeder avatar sepehrdaddev avatar sgalonska avatar sobafuchs avatar teodorpatras avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

kontist.github.io's Issues

Test Account

Hi, I'd like to play around with the Kontist API but I don't want to mess with my real account. Would it instead be possible to get some kind of testing/dummy account for development purposes?

Missing Asset

I am not able to query the asset from my Kontist Invoice. If I add a second one by my self I will only receive that, but not the origin one.

KontistSDKError: Too Many Requests - Authentication Issues in Local Server

Hello! I hope you're doing well.

I'm currently working on integrating authentication with the api.kontist.com server, following the kontist.com documentation. Everything works fine on my local server until I turn it off. My setup involves connecting to the api.kontist.com server and fetching a transaction list every 5 seconds.
However, after about 10 to 15 minutes, I encounter a problem where I can no longer obtain an accessToken, and I have to reconnect to api.kontist.com. After attempting to reconnect multiple times, I end up receiving a "Too Many Requests" error from api.kontist.com, which causes my server to stop due to the numerous errors.

I'll share my current code with you. Could you please take a careful look and advise me on what adjustments I might need to make?

Error message like that:

subscribeNewTransactionByFiveSeconds error KontistSDKError: request to https://api.kontist.com/api/user/mfa/challenges/501427e9-e92b-4973-b94d-d912f85bf984 failed, reason: getaddrinfo ENOTFOUND api.kontist.com
    at new KontistSDKError (<anonymized>/node_modules/kontist/lib/errors.ts:28:5)
    at HttpRequest.<anonymous> (<anonymized>/node_modules/kontist/lib/request.ts:55:13)
    at step (<anonymized>/node_modules/kontist/dist/lib/request.js:33:23)
    at Object.throw (<anonymized>/node_modules/kontist/dist/lib/request.js:14:53)
    at rejected (<anonymized>/node_modules/kontist/dist/lib/request.js:6:65)
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  status: undefined,
  type: undefined
}

KontistSDKError: Too Many Requests
    at new KontistSDKError (<anonymized>/node_modules/kontist/lib/errors.ts:28:5)
    at HttpRequest.<anonymous> (<anonymized>/node_modules/kontist/lib/request.ts:55:13)
    at step (<anonymized>/node_modules/kontist/dist/lib/request.js:33:23)
    at Object.next (<anonymized>/node_modules/kontist/dist/lib/request.js:14:53)
    at fulfilled (<anonymized>/node_modules/kontist/dist/lib/request.js:5:58)
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  status: 429,
  type: undefined
}

transaction.service.ts

....
constructor(private httpService: HttpService, private transactionsGateway: TransactionsGateway) {
        const keepAliveAgent = new https.Agent({ keepAlive: true });
        this.client = new Client({
            clientId: process.env.CLIENT_ID,
            clientSecret: process.env.CLIENT_SECRET,
            scopes: ["transactions", "subscriptions"],
            agent: keepAliveAgent,
        });
        this.accessToken = null;
        this.accessTokenExpiresAt = null; // Track the expiry of the token
    }
    username = process.env.USERNAME;
    password = process.env.PASSWORD;

    retryDelay = 180000; // Initial delay in milliseconds for exponential backoff
    maxRetries = 3; // Maximum number of retries


    async waitFor(delay) {
        return new Promise(resolve => setTimeout(resolve, delay));
    }

    async initiateMFA(retryCount = 0) {
        try {
            const tokenData: any = await this.client.auth.tokenManager.fetchTokenFromCredentials({
                username: this.username,
                password: this.password
            });
            this.accessToken = tokenData.accessToken;
            this.accessTokenExpiresAt = new Date(new Date().getTime() + tokenData.expires * 1000);

            const confirmedToken: any = await this.client.auth.push.getConfirmedToken();
            this.accessToken = confirmedToken.accessToken;
            this.accessTokenExpiresAt = new Date(new Date().getTime() + confirmedToken.expires * 1000);

            console.log("MFA confirmed. Access token is now confirmed.");
            this.retryDelay = 180000; // Reset retry delay upon success
        } catch (err) {
            if (err instanceof ChallengeExpiredError) {
                console.log("Challenge expired, trying again...");
                await this.initiateMFA(retryCount + 1); // Retry initiating MFA if the challenge expired
            } else if (err.status === 429 && retryCount < this.maxRetries) { // Check for rate limit error and retry count
                console.log(`Rate limit hit, retrying in ${this.retryDelay}ms...`);
                await this.waitFor(this.retryDelay);
                this.retryDelay *= 2; // Exponentially increase the delay
                await this.initiateMFA(retryCount + 1);
            } else {
                throw err; // Re-throw other errors that we're not handling here
            }
        }
    }

    async getAccessToken() {
        if (!this.accessToken || new Date() >= this.accessTokenExpiresAt) {
            await this.initiateMFA();
        }
        return this.accessToken;
    }

async subscribeNewTransactionByFiveSeconds(retryCount = 0) {
        const maxRetries = 3;
        const baseDelay = 180000; // 3 minutes
        const maxDelay = 600000; // 10 minutes

        try {
            const accessToken = await this.getAccessToken();
            const transactions = await this.client.models.transaction.fetch();
            // Filter transactions
            const fiveSecondsAgo = new Date(new Date().getTime() - 5 * 1000).toISOString();
            const transactionList = transactions.items;
            const searchResult = transactionList.filter(transaction => new Date(transaction.createdAt) > new Date(fiveSecondsAgo));
            if (searchResult.length > 0) {
                this.transactionsGateway.sendNewTransactionToClients(searchResult);
            }
            return searchResult;
        } catch (error) {
            console.error("subscribeNewTransactionByFiveSeconds error", error);
            if (error.status === 429) { // Rate limiting error
                console.log("Rate limiting error, retrying...");
                const delay = Math.min(baseDelay * Math.pow(2, retryCount), maxDelay);
                console.log(`Retrying after ${delay}ms...`);
                return new Promise((resolve, reject) => {
                    setTimeout(() => {
                        this.subscribeNewTransactionByFiveSeconds(retryCount + 1).then(resolve).catch(reject);
                    }, delay);
                });
            } else if (retryCount < maxRetries) {
                // Retry for other errors
                const delay = baseDelay;
                console.log(`Retrying for error after ${delay}ms...`);
                return new Promise((resolve, reject) => {
                    setTimeout(() => {
                        this.subscribeNewTransactionByFiveSeconds(retryCount + 1).then(resolve).catch(reject);
                    }, delay);
                });
            } else {
                throw error; // Rethrow after max retries
            }
        }
    }
...

Seeking Assistance with Kontist API Integration for Transaction Notifications

Hello! I hope you're doing well.

I'm currently integrating the Kontist API into my local server to manage transactions more efficiently. My goal is to set up a feature where my application can subscribe to notifications of new transactions as they occur within the Kontist system.
Here’s what I’ve done so far:

  • I've created a new client account on Kontist.
  • I've managed to authenticate successfully and can retrieve a list of transactions without issues.
  • I've written the necessary code in a file named kontist.service.ts within my project.
  • To make my local server accessible, I'm running it on port 3000 and have set up ngrok to expose it externally.
  • Despite these steps, I haven't been able to receive any notifications from the Kontist App about new transactions. I suspect the issue might be related to how I'm using ngrok or perhaps something within my kontist.service.ts file.

Could you please review my setup to see if there's anything I might have missed or need to adjust? Any guidance or recommendations would be greatly appreciated.

Thank you so much for your assistance. Here is the code:

kontist.service.ts

@Injectable()
export class KontistService implements OnModuleInit {
    private client: ApolloClient<any>;
    private kontistClient: Client;

    constructor() {
        this.kontistClient = new Client({
            clientId: process.env.CLIENT_ID,
            clientSecret: process.env.CLIENT_SECRET,
            scopes: ["transactions", "subscriptions"]
        });
    }

    onModuleInit() {
        this.setupSubscription();
    }

    setupSubscription() {
        const username = process.env.USERNAME;
        const password = process.env.PASSWORD;
        this.kontistClient.auth.tokenManager.fetchTokenFromCredentials({ username: username, password: password }).then((tokenData) => {
            const accessToken = tokenData.accessToken;
            const wsClient = new SubscriptionClient(
                'wss://api.kontist.com/api/graphql',
                {
                    reconnect: true,
                    connectionParams: {
                        headers: {
                            Authorization: `Bearer ${accessToken}`,
                        },
                    },
                },
                WebSocket,
            );
            const link = new WebSocketLink(wsClient);
            this.client = new ApolloClient({
                link,
                cache: new InMemoryCache(),
            });
            this.subscribeToNewTransactions();
        });
    }

    subscribeToNewTransactions() {
        const SUBSCRIBE_TO_NEW_TRANSACTION = gql`
          subscription {
            newTransaction {
              id
              amount
              bookingDate
            }
          }
        `;

        console.log('Subscribing to new transactions')
        this.client.subscribe({
            query: SUBSCRIBE_TO_NEW_TRANSACTION,
        }).subscribe({
            next: (data) => {
                console.log('New transaction:', data);
                // Here, you can integrate with Prisma to save the transaction or perform any other logic
            },
            error: (err) => console.error('Error on new transaction subscription:', err),
        });
    }
 }

transaction.module.ts

@Module({
    imports: [
        HttpModule,
        GraphQLModule.forRoot<ApolloDriverConfig>({
            driver: ApolloDriver,
            playground: false,
            typePaths: ['./**/*.graphql'],
            installSubscriptionHandlers: true,
            subscriptions: {
                'graphql-ws': true,
            }
        }),
    ],
    providers: [
        TransactionsService,
        TransactionsGateway,
        PrismaService,
        TransactionResolver,
        AuthService,
        JwtService,
        UsersService,
        KontistService,
    ],
    exports: [KontistService, TransactionsService, TransactionsGateway],
    controllers: [TransactionsController],
})
export class TransactionsModule {}

transaction.graphql

type Transaction {
    id: ID!
    amount: Int!
    bookingDate: String!
    purpose: String!
    createdAt: String!
}

type Query {
    transactions: [Transaction!]!
}

type Subscription {
    newTransaction: Transaction!
}

transaction.resolver.ts

import { Resolver, Subscription } from '@nestjs/graphql';
import { PubSub } from 'graphql-subscriptions';
import {Transaction} from "kontist/dist/lib/graphql/transaction";

const pubSub = new PubSub();

@Resolver(of => Transaction)
export class TransactionResolver {
    @Subscription(returns => Transaction)
    newTransaction() {
        return pubSub.asyncIterator('newTransaction');
    }
}

transaction.controller.ts

@Controller('transactions')
@ApiTags('Transactions')
export class TransactionsController {
    constructor(private readonly service: TransactionsService, private readonly kontistService: KontistService) {}

    @Get('getAllList')
    async getAllTransactions() {
        const transactions = this.service.getAllTransactions();
        return transactions;
    }
 }

400 - Invalid payload: client

I followed your instructions to the letter:

  1. I created an app with the correct scopes and redirect_uris
  2. I used the exact same authorization code URL as in your instructions: https://api.kontist.com/api/oauth/authorize?scope=offline&response_type=code&client_id={my client ID}&redirect_uri={my redirect URI}&state={a value}
  3. I pasted this URL in my browser.

Resulting URL: https://api.kontist.com/api/oauth/mfa/authorize?allowed=true

Response:

{
  "status": 400,
  "type": "http://www.iana.org/assignments/http-status-codes#400",
  "title": "Invalid payload: client"
}

Could you help me figure out what I've done wrong? The error message gives zero useful information.

400 - Invalid payload: client

Hallo!

Bei dem Versuch mein Kontist-Konto mittels API zu erreichen stellte ich heute bei der Authentifizierung nachfolgenden Fehler fest:

  1. Aufruf von

https://api.kontist.com/api/oauth/authorize?scope=offline&response_type=code&client_id={Client ID meiner zuvor erstellten Anwendung}&redirect_uri=https://meine-domain.tld/callback.php&state=OPAQUE_VALUE

im Browser.

  1. Authentifizierung mittels Anmeldedaten inkl. Bestätigung der Anmeldung in der Kontist-App. Ergebnis:

{
"status": 400,
"type": "http://www.iana.org/assignments/http-status-codes#400",
"title": "Invalid payload: client"
}

Verweigere ich jedoch den Zugriff der Anwendung, so werde ich korrekt zu meiner Application weitergeleitet und kann die GET-Parameter "state" und "error" auslesen und verarbeiten. Die "callback.php" enthält zu Testzwecken nur eine Ausgabe der GET-Parameter.

Mache ich etwas falsch oder habe ich etwas übersehen? Für meinen Test habe ich Mozilla Firefox und Microsoft Edge verwendet.

Vielen Dank für die Unterstützung!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.