Monitorowanie przepływu otwartej bankowości za pomocą PlayWright i Checkly

Otwarta bankowość oferuje użytkownikom sposób na łatwiejszy dostęp do informacji o własnym koncie bankowym, na przykład za pośrednictwem aplikacji innych firm. Osiąga się to poprzez umożliwienie zewnętrznym dostawcom usług finansowych dostępu do danych finansowych klientów banku za pomocą interfejsów API, które umożliwiają bezpieczną komunikację między różnymi zaangażowanymi stronami.

Kilka godnych uwagi przykładów banków i instytucji finansowych, które wykorzystują otwartą bankowość do oferowania ulepszonych usług, większej przejrzystości i bardziej spersonalizowanego doświadczenia bankowego dla swoich klientów:

  • Barclays uruchomił otwartą bankową platformę API, zapewniająca dostęp do szeregu interfejsów API dla informacji o koncie, płatnościach i transakcjach.
  • Klarna wprowadziła na rynek innowacyjny otwartą platformę bankową o nazwie Kosma, rewolucjonizując dostęp do ponad 15 000 banków w 27 krajach.
  • Wells Fargo posiada Portal API który zapewnia zestaw narzędzi i przykładowy kod dla programistów, wraz ze środowiskiem testowym.
  • Monzowykorzystuje otwartą bankowość do łatwego wykonywania przelewów bankowych, przeglądania wszystkich rachunków jednocześnie, a nawet udowadniania swoich dochodów w celu uzyskania kredytu w rachunku bieżącym.

Dodatkowe przykłady obejmują Truelayer, Trading 212, Plaid, Revolut i Mint. Każda z tych organizacji wykorzystuje otwartą bankowość na różne sposoby, od agregacji kont po przetwarzanie płatności i zarządzanie finansami osobistymi. Proszę spojrzeć na Open Banking Tracker aby uzyskać więcej informacji.

Monitorowanie interfejsów API otwartej bankowości

Jeśli chodzi o podróże API, skomplikowane, sekwencyjne interakcje otwartej bankowości mogą być dość wymagające, jeśli chodzi o monitorowanie. Warstwy uwierzytelnianie, autoryzacja, szyfrowanie, i transfer danych oznacza, że pojedyncza transakcja będzie obejmować wiele kroków i kilka punktów końcowych API współpracujących ze sobą, z których każdy będzie powiązany z poprzednim krokiem. Oznacza to, że monitorowanie odizolowanego punktu końcowego nie jest wystarczające i że musimy raczej spojrzeć na przepływ jako całość, jednocześnie wyświetlając odpowiednie informacje w przypadku awarii, aby szybko zrozumieć, co się zepsuło w tej złożonej wymianie.

Aby lepiej zrozumieć wyzwania związane z monitorowaniem otwartych przepływów bankowych, przyjrzyjmy się przykładowemu przepływowi z witryny Klarna XS2A App.

Aplikacja Open Banking XS2A obsługuje wszystkie interakcje z konsumentami. Jest używana za każdym razem, gdy przepływ API wymaga danych wejściowych od konsumenta, takich jak wybór banku konsumenckiego lub silne uwierzytelnianie klienta w banku. Ponadto, w zależności od wybranego przepływu, aplikacja XS2A może być używana do wyboru określonego konta bankowego lub do autoryzacji płatności z konta na konto.

Teraz podzielmy przepływ na poszczególne kroki:

podział krok po kroku
  1. Proszę rozpocząć sesję: Proces rozpoczyna się od PUT do punktu końcowego inicjacji sesji Klarna, gdzie ustawiamy niezbędne parametry dla otwartej sesji bankowej. Preferencje językowe, dane bankowe, informacje o użytkowniku i zakres zgody na dostęp do różnych usług konta są tutaj zdefiniowane, więc zakres sesji jest zdefiniowany od samego początku i nie można go przejąć w żadnym innym celu.
  2. Proszę uruchomić przepływ szczegółów konta: Po uzyskaniu identyfikatora sesji, przechodzimy do kolejnego kroku PUT tym razem w celu zainicjowania przepływu danych konta. Poprzez dalsze określanie identyfikatorów kont i kluczy kryptograficznych zapewniamy, że cała komunikacja jest bezpieczna.
  3. Proszę wybrać bank testowy: A POST następuje żądanie mające na celu wybranie banku dla użytkownika. Ten krok symuluje wybór banku przez klienta na placu zabaw Klarna.
    a. [Optional] Proszę pobrać konfigurację przepływu: Następnie przeprowadzamy analizę GET aby pobrać aktualny stan przepływu. Ten krok zapewnia, że sekwencja transakcji przebiega zgodnie z oczekiwaniami.
  4. Szyfrowanie odpowiedzi: Ta faza obejmuje wysyłanie odpowiedzi zaszyfrowanych przy użyciu algorytmów szyfrowania AES i RSA. Zaszyfrowany ładunek jest wysyłany z powrotem do punktu końcowego API przy użyciu algorytmu POST żądania, w tym zaszyfrowany klucz AES RSA i zaszyfrowane dane AES.
    To wielowarstwowe podejście do szyfrowania zapewnia, że zaszyfrowane dane mogą być odszyfrowane tylko przez punkt końcowy API za pomocą odpowiedniego klucza prywatnego RSA, a klucz AES pozostaje chroniony przez cały czas transmisji.
  5. Pełny przepływ: Ten krok kończy przepływ transakcji, publikując uzyskane szczegóły przekierowania z powrotem do interfejsu API Klarna, potwierdzając działania API i przesuwając proces do przodu.
  6. Wybór konta bankowego użytkownika: Wartość konta pobrana w kroku piątym jest przesyłana dalej. Dla bezpieczeństwa musimy zaszyfrować numery kont.
  7. Proszę uzyskać zgodę: A POST żądanie jest składane w celu uzyskania zgody na dostęp do danych, co jest wymogiem regulacyjnym dla otwartych usług bankowych. Odpowiedź zawiera adresy URL dla szczegółów konta i sald, które zostaną wykorzystane w kolejnych żądaniach.
  8. Proszę pobrać szczegóły konta i saldo: Finał POST obejmuje pobranie szczegółów konta użytkownika i salda, co oznacza zakończenie przepływu transakcji. Żądania te weryfikują token zgody i zwracają określone informacje o koncie.

Testowanie pojedynczego punktu końcowego API za pomocą PlayWright

Przepływ, który właśnie przeanalizowaliśmy, składa się z 8 kroków i 11 żądań.

Gdybyśmy mieli napisać skrypt Playwright, aby przetestować pierwsze żądanie w izolacji, mogłoby to wyglądać mniej więcej tak:

{
test(‘XS2A API Flow’, async ({ request }) => {
const startSession = await request.put(`https://authapi.openbanking.playground.klarna.com/xs2a/v1/sessions`, {
data: {
język: ‘en’,
_selected_bank: {
bank_code: ‘000000’,
country_code: ‘GB’,
},
psu: {
ip_address: psu_ip,
user_agent: psu_ua,
},
consent_scope: {
accounts: {},
account_details: {},
balances: {},
transakcje: {},
transfer: {},
_insights_refresh: {},
lifetime: 30,
},
_aspsp_access: ‘prefer_psd2’,
_redirect_return_url: ‘http://test/auth’,
keys: {
hsm: ‘— xxx —‘,
aspsp_data: ‘— xxx —‘
},
},
headers: {
‘Content-Type’: ‘application/json’,
Authorization: ‘Token ‘ + auth_token,
},
});
const startSessionResponse = await startSession.json();
const session_id = startSessionResponse.data.session_id;
});
});” data-lang=”text/javascript”>

const { test } = require('@playwright/test');

const auth_token = process.env.KOSMA_AUTH_TOKEN;
const psu_ua="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36";
const psu_ip = '10.20.30.40';

test.describe('Klarna Open Banking', () => {
	test('XS2A API Flow', async ({ request }) => {
		const startSession = await request.put(`https://authapi.openbanking.playground.klarna.com/xs2a/v1/sessions`, {
			data: {
				language: 'en',
				_selected_bank: {
					bank_code: '000000',
					country_code: 'GB',
				},
				psu: {
					ip_address: psu_ip,
					user_agent: psu_ua,
				},
				consent_scope: {
					accounts: {},
					account_details: {},
					balances: {},
					transactions: {},
					transfer: {},
					_insights_refresh: {},
					lifetime: 30,
				},
				_aspsp_access: 'prefer_psd2',
				_redirect_return_url: 'http://test/auth',
				keys: {
					hsm: '--- xxx ---',
					aspsp_data: '--- xxx ---',
				},
			},
			headers: {
				'Content-Type': 'application/json',
				Authorization: 'Token ' + auth_token,
			},
		});
		const startSessionResponse = await startSession.json();
		const session_id = startSessionResponse.data.session_id;
	});
});

Uruchomienie tego skryptu zgodnie z harmonogramem może już dostarczyć nam cennych informacji, ale biorąc pod uwagę, jak połączone będą te punkty końcowe w kontekście testowanego przez nas otwartego przepływu bankowego, będziemy chcieli połączyć każde żądanie w łańcuch, aby przetestować przepływ od końca do końca.

Testowanie całego przepływu end-to-end

PlayWright może nam tutaj bardzo pomóc, pozwalając nam na zawijanie każdego żądania i późniejszej manipulacji odpowiedzią we własne test.step() aby nasz kod był czystszy, a raportowanie bardziej zrozumiałe.

Oto jak wyglądałby cały przepływ od końca do końca (ponumerowaliśmy kroki w kodzie, aby dopasować je do naszego podziału przepływu z góry):

const { test, expect } = require('@playwright/test');
const { sendEncryptedResponse } = require('./snippets/functions.js')

const auth_token = process.env.KOSMA_AUTH_TOKEN
const psu_ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36";
const psu_ip = "10.20.30.40"

test.describe('Klarna Open Banking', () => {

    test('XS2A API Flow', async ({ request }) => {

			/* --------------------------------------- 1 ----------------------------------------------- */

      const startSession = await test.step('Start Session', async () => {
        return request.put(`https://authapi.openbanking.playground.klarna.com/xs2a/v1/sessions`, {
        data: {
            language: "en",
            _selected_bank: {
              bank_code: "000000",
              country_code: "GB",
            },
            psu: {
              ip_address: psu_ip,
              user_agent: psu_ua,
            },
            consent_scope: {
              accounts: {},
              account_details: {},
              balances: {},
              transactions: {},
              transfer: {},
              _insights_refresh: {},
              lifetime: 30,
            },
            _aspsp_access: "prefer_psd2",
            _redirect_return_url: "http://test/auth",
            keys: {
              hsm: "--- xxx ---",
              aspsp_data: "--- xxx ---"
            }
        },
        headers: {
          "Content-Type": "application/json",
          "Authorization": "Token " + auth_token,
        }
      })
      });
      const startSessionResponse = await startSession.json();
      const session_id = startSessionResponse.data.session_id;
    
      /* --------------------------------------- 2 ----------------------------------------------- */

      const accountDetailsFlow = await test.step('Start Account Details Flow', async () => {
        return request.put(`https://authapi.openbanking.playground.klarna.com/xs2a/v1/sessions/` + session_id + `/flows/account-details`, {
        data: {
          "account_id": "",
          "iban": "",
          "keys": {
            "hsm": "",
            "aspsp_data": ""
          }
        },
        headers: {
          "Content-Type": "application/json",
          "Authorization": "Token " + auth_token,
        }
      })

        expect(client_token).toBeEmpty();
      });
      const accountDetailsFlowResponse = await accountDetailsFlow.json();
      const flow_id = accountDetailsFlowResponse.data.flow_id;
      var client_token = accountDetailsFlowResponse.data.client_token;

      /* --------------------------------------- 3 ----------------------------------------------- */

      const selectTestBank = await test.step('Select Test Bank (Germany)', async () => {
        return request.post(`https://authapi.openbanking.playground.klarna.com/branded/xs2a/v1/wizard/` + flow_id, {
        data: {
            "bank_code": "00000",
            "country_code": "DE",
            "keys": {
              "hsm": "",
              "aspsp_data": ""
            }
          },
        headers: {
          "Content-Type": "application/json",
          "Authorization": "Token " + auth_token,
        }
      })
      });
      const selectTestBankResponse = await selectTestBank.json();
      
      const getFlowConfig = await test.step('Get Flow Configuration', async () => {
        return request.get(`https://authapi.openbanking.playground.klarna.com/branded/xs2a/v1/wizard/` + flow_id, {
        headers: {
          "Content-Type": "application/json",
          "Authorization": "Token " + auth_token,
        }
      })
      });

      const getFlowConfigResponseJSON = await getFlowConfig.json();
      const getFlowConfigResponse = getFlowConfigResponseJSON.data;

      /* --------------------------------------- 4 ----------------------------------------------- */

      // Encrypt Responses Here

      const selectTransportForm = JSON.stringify(
      {
        "form_identifier": getFlowConfigResponse.result.form.form_identifier,
        "data": [
          { "key": "interface", "value": "de_testbank_bias" }
        ]
      });
    
      const selectTransportFormResponse = await sendEncryptedResponse(getFlowConfigResponse, selectTransportForm, auth_token);

      const userAndPasswordForm = JSON.stringify(
      {
        "form_identifier": selectTransportFormResponse.result.form.form_identifier,
        "data": [
          { "key": "bias.apis.forms.elements.UsernameElement", "value": "redirect" },
          { "key": "bias.apis.forms.elements.PasswordElement", "value": "123456" }
        ]
      });
    
      const userAndPasswordFormResponse = await sendEncryptedResponse(selectTransportFormResponse, userAndPasswordForm, auth_token);
      if (userAndPasswordFormResponse.result.context === "authentication"){
        var redirect_url = userAndPasswordFormResponse.result.redirect.url + "&result=success"
        var redirect_id = userAndPasswordFormResponse.result.redirect.id
      }

      /* --------------------------------------- 5 ----------------------------------------------- */

      const completeFlow = await test.step('Complete Flow', async () => {
        return request.post(`https://authapi.openbanking.playground.klarna.com/branded/xs2a/v1/wizard/` + flow_id, {
        data: {
            "redirect_id": redirect_id,
            "return_url": redirect_url,
        
            "keys": {
              "hsm": "",
              "aspsp_data": ""
            }
          },
        headers: {
          "Content-Type": "application/json",
          "Authorization": "Token " + auth_token,
        }
      })
      });
      const completeFlowResponseJSON = await completeFlow.json();
      const completeFlowResponse = completeFlowResponseJSON.data;

      if (completeFlowResponse.result.form.elements.options === null) {
        console.error("Log: " + "No accounts found for this user?");
      }

      const accounts = completeFlowResponse.result.form.elements[0].options;
      const first_account = accounts[0];

      /* --------------------------------------- 6 ----------------------------------------------- */

      const selectFirstAccountForm = JSON.stringify({
        "form_identifier": completeFlowResponse.result.form.form_identifier,
        "data": [
          { "key": "account_id", "value": first_account.value }
        ]
      });

      const accountSelectionForm = await sendEncryptedResponse(completeFlowResponse, selectFirstAccountForm);

      if (accountSelectionForm.state === "FINISHED"){
        console.log("Log: " + first_account.label + " selected." );
      }

      /* --------------------------------------- 7 ----------------------------------------------- */

      const getConsent = await test.step('Get Consent', async () => {
        return request.post(`https://api.openbanking.playground.klarna.com/xs2a/v1/sessions/${session_id}/consent/get`, {
        data: {
          "keys": {
            "hsm": "--- xxx ---",
            "aspsp_data": "--- xxx ---"
          }
          },
        headers: {
          "Content-Type": "application/json",
          "Authorization": "Token " + auth_token,
        }
      })
      });
      const getConsentResponseJSON = await getConsent.json();
      const getConsentResponse = getConsentResponseJSON.data;
      const balances_url = getConsentResponse.consents.balances;
      const account_details_url = getConsentResponse.consents.account_details;

      /* --------------------------------------- 8 ----------------------------------------------- */

      const getAccountDetails = await test.step('Get Account Details', async () => {
        return request.post(account_details_url, {
        data: {
          "consent_token": getConsentResponse.consent_token,
          "account_id": first_account.value,
      
          "psu": {
            "ip_address": psu_ip,
            "user_agent": psu_ua
          },
      
          "keys": {
            "hsm": "",
            "aspsp_data": ""
          }
          },
        headers: {
          "Content-Type": "application/json",
          "Authorization": "Token " + auth_token,
        }
      })
      });
      const getAccountDetailsResponseJSON = await getAccountDetails.json();
      const getAccountDetailsResponse = getAccountDetailsResponseJSON.data;

      const getAccountBalance = await test.step('Get Account Balance', async () => {
        return request.post(balances_url, {
        data: {
          "consent_token": getAccountDetailsResponse.consent_token,
          "account_id": getAccountDetailsResponse.result.account.id,
      
          "psu": {
            "ip_address": psu_ip,
            "user_agent": psu_ua
          },
      
          "keys": {
            "hsm": "",
            "aspsp_data": ""
          }
          },
        headers: {
          "Content-Type": "application/json",
          "Authorization": "Token " + auth_token,
        }
      })
      });

      const getAccountBalanceResponse = await getAccountBalance.json();
      const accountBalance = getAccountBalanceResponse.data.result.available.amount

      console.log("Log: Account Balance = €" + accountBalance );
      expect(accountBalance).toBeGreaterThanOrEqual(0);
    
    });
  });

Proszę zauważyć, że będziemy musieli wyeksportować plik KOSMA_AUTH_TOKEN zmienną środowiskową ustawioną na wartość Kosma’s demo auth token.

Nasz skrypt ma dodatkową zależność oprócz samego Playwright, functions.js, która wygląda następująco (a także zawiera jsbn.js.):

const { RSA } = require('./jsbn.js');
const axios = require('axios');
const crypto = require('crypto');
const CryptoJS = require("crypto-js");

function findModAndExp(xs2a_form_key) {

  // Base64 decoding function
  function b64Decode(str) {
    str = str.replace(/-/g, '+').replace(/_/g, "https://dzone.com/");
    while (str.length % 4) {
      str += '=';
    }
    return Buffer.from(str, 'base64').toString('utf8');;
  }

  // Split JWT into its three parts
  const parts = xs2a_form_key.split('.');
  const header = JSON.parse(b64Decode(parts[0]));
  const payload = JSON.parse(b64Decode(parts[1]));
  const signature = parts[2];

  // Extract the modulus value from the JWK object
  const modulus = payload.modulus;
  const exponent = payload.exponent;

  return {
    'modulus': modulus,
    'exponent': exponent
  };
}

function generateRandomHexString(byteLength) {
  // Create a buffer with random bytes
  const buf = crypto.randomBytes(byteLength);

  // Convert buffer to hex string
  let res="";
  for (let i = 0; i < buf.length; i++) {
    res += ('0' + (buf[i] & 0xff).toString(16)).slice(-2);
  }
  return res;
}

function encrypt(publicKey, plainText) {
  if (!publicKey) {
    throw new Error('No or wrongly formatted Public Key for Encryption given');
  }
  var { modulus, exponent } = findModAndExp(publicKey)
  const iv = generateRandomHexString(16);
  const keyHex = generateRandomHexString(256 / 8);
  const key = CryptoJS.enc.Hex.parse(keyHex);
  const encrypted = CryptoJS.AES.encrypt(plainText, key, { iv: CryptoJS.enc.Hex.parse(iv) });
  const ciphertext = encrypted.toString();

  const rsa = new RSA.key();
  rsa.setPublic(modulus, exponent);
  // Encrypt the data
  const encryptedByRsa = rsa.encrypt(keyHex);
  const encryptedKeyBytes = CryptoJS.enc.Hex.parse(encryptedByRsa);
  // Convert the encrypted key to base64
  const encryptedKey = encryptedKeyBytes.toString(CryptoJS.enc.Base64);
  return { ct: ciphertext, iv: iv, ek: encryptedKey };
}

async function sendEncryptedResponse(lastResponse, responseForm, auth_token) {
  const publicKey = lastResponse.result.key
  const encryptedData = encrypt(publicKey, responseForm);
  let data = JSON.stringify(encryptedData);

  try {
    const response = await axios({
      method: 'post',
      maxBodyLength: Infinity,
      url: lastResponse.next,
      headers: {
        "Content-Type": "application/json",
        Authorization:
          "Token " + auth_token,
      },
      data: data
    });
    return response.data.data
  } catch (err) {
    throw new Error(err);
  }
}

module.exports = { sendEncryptedResponse, generateRandomHexString, encrypt, findModAndExp }

Monitorowanie przepływu za pomocą Checkly

Aby upewnić się, że przepływ działa niezawodnie w całości, musimy uruchamiać nasz test w regularnych odstępach czasu, co czyni go skuteczną kontrolą monitorowania. Nasza kontrola będzie prawdopodobnie musiała trwać od wielu lokalizacji i z pewnością będzie musiał być powiązany z jednym lub kilkoma kanałów ostrzegawczych. The Checkly CLI umożliwia nam szybkie rozpoczęcie pracy, definiując to wszystko bez opuszczania edytora kodu.

Proszę teraz zainicjować projekt Checkly CLI i skopiować do niego nasz powyższy skrypt. Aby zaoszczędzić czas, mamy już to dla Państwa zrobiliśmy.

Proszę zauważyć, w jaki sposób definiujemy wieloetapowe sprawdzanie interfejsu API Checkly przy użyciu funkcji odpowiedniej konstrukcji:

import * as path from 'path';
import { MultiStepCheck } from 'checkly/constructs';
import { emailChannel, callChannel } from './alertChannels';

new MultiStepCheck('xs2a-flow-check', {
	name: 'Klarna Open Banking - XS2A API Flow',
	alertChannels: [emailChannel, callChannel],
	muted: false,
	code: {
		entrypoint: path.join(__dirname, 'xs2a.spec.ts'),
	},
});

To wskazuje na xs2a.spec.ts który zawiera naszą specyfikację Playwright testującą cały przepływ API od końca do końca.

Za każdym razem, gdy kontrola zakończy się niepowodzeniem, Checkly skontaktuje się z nami za pomocą połączonego linku emailChannel i callChannel:

import { EmailAlertChannel } from 'checkly/constructs';
import { PhoneCallAlertChannel } from 'checkly/constructs';

const sendDefaults = {
	sendFailure: true,
	sendRecovery: true,
	sendDegraded: false,
	sslExpiry: true,
	sslExpiryThreshold: 30,
};

export const emailChannel = new EmailAlertChannel('email-channel-1', {
	address: 'user@email.com', // Substitute with your email address
	...sendDefaults,
});

export const callChannel = new PhoneCallAlertChannel('call-channel-1', {
	phoneNumber: '+31061234567890', // Substitute with your phone number
});

Są to oczywiście przykłady; Checkly jest w stanie alarmować na wielu różnych kanałach, od e-maili i SMS-ów, przez Pagerduty i OpsGenie, po Slack i MSTeams.

Nasza kontrola dziedziczy również domyślne ustawienia kontroli, które są ustawione na poziomie projektu w naszej aplikacji config.checkly.ts:

import { defineConfig } from 'checkly'
import { Frequency } from 'checkly/constructs'

const config = defineConfig({
  projectName: 'OpenBanking CLI Project',
  logicalId: 'openbanking-cli-project',
  repoUrl: 'https://github.com/checkly-solutions/checkly-open-banking',
  checks: {
    locations: ['us-east-1', 'us-east-2', 'us-west-1'],
    runParallel: true,
    frequency: Frequency.EVERY_1M,
    tags: ['open-banking'],
    runtimeId: '2023.09',
    checkMatch: '**/*.check.ts',
    browserChecks: {
      testMatch: '**/__checks__/*.spec.ts',
    },
  },
})

export default config

Proszę zwrócić uwagę na locations kontrola zostanie uruchomiona ze strategii planowania runParallel i frequency param. Upewniamy się, że nasze kontrole są przeprowadzane z dużą częstotliwością i z wielu lokalizacji jednocześnie, aby dokładnie monitorować to, co jest niezbędnym przepływem skierowanym do klienta.

Pod względem kodu wszystko jest gotowe, ale nadal musimy zasilić aplikację KOSMA_AUTH_TOKEN z naszej specyfikacji Playwright do Checkly, jeśli chcemy, aby kontrola faktycznie działała. Możemy to łatwo zrobić za pomocą:

npx checkly env add KOSMA_AUTH_TOKEN <my_token_value> -l

Jesteśmy teraz gotowi do faktycznego wdrożenia naszej kontroli w Checkly:

Logując się na nasze konto Checkly, widzimy, że nasza kontrola działa teraz zgodnie z harmonogramem:

harmonogram

Dla każdego połączenia możemy teraz zobaczyć szczegółowe raporty pokazujące czas, wynik i inne szczegóły żądania dla każdego żądania w naszej kontroli:

raport z testu

Nasze połączone kanały ostrzegawcze również zostały wdrożone:

Checkly

Checkly będzie teraz wysyłać do nas wiadomości e-mail i zadzwonić do nas na nasz telefon, gdy nasz przepływ otwartej bankowości jest uszkodzony, co pozwala nam przejść do szczegółowego raportu pokazującego, który krok zawiódł w tym złożonym przepływie. To bardzo przydatne.

Podsumowanie

Słaba wydajność API i trudności mogą nie tylko wpływać na użytkowników końcowych, ale także budzić obawy organizacji zajmujących się standardami branżowymi i organów regulacyjnych. Ponieważ otwarte bankowe interfejsy API obsługują bardzo wrażliwe dane, ciągłe monitorowanie tych przepływów ma kluczowe znaczenie dla bezpieczeństwa danych i zadowolenia użytkowników.

W tym wpisie na blogu przedstawiliśmy szczegółowy przewodnik dotyczący korzystania z Playwright i Checkly do testowania i monitorowania przepływu API Klarna Open Banking XS2A. Pokazaliśmy również, jak szyfrować odpowiedzi w celu zapewnienia bezpieczeństwa, skonfigurować kontrole monitorowania, utworzyć kanały alertów dla powiadomień o niepowodzeniu i wdrożyć kontrolę do Checkly w celu regularnego monitorowania. Aby rozpocząć, skonfigurowaliśmy to repozytorium GitHub.

Łącząc Checkly i Playwright, mogą Państwo upewnić się, że otwarty przepływ bankowy działa prawidłowo i być natychmiast powiadamiani o pojawiających się problemach.