如果你尝试使用欧洲银行 API 构建任何东西,你可能遇到了和我一样的障碍:证书要求。PSD2 规定你需要 eIDAS QWAC 证书才能直接与银行对话,而这些证书既昂贵又难以快速获取。

不过有一条路径可以完全跳过证书。我将向你展示它是如何工作的——使用你今天就能运行的真实代码。

简述

无需获取自己的 eIDAS 证书并作为第三方提供商(Third Party Provider)在每家银行注册,你可以使用一个已经拥有证书的账户信息服务提供商(Account Information Service Provider,AISP)。你的应用与他们的 API 通信;他们再与银行通信。你永远不会接触到证书。

权衡:你依赖于一个中间商。但对于大多数独立项目、小型企业和个人理财工具来说,这是一个完全合理的权衡。你通过一个干净的 REST API 获取银行数据,而无需处理双向 TLS 和每家银行的注册。

你需要什么

  • Python 3.8+(仅使用标准库,基础部分无需 pip 安装)
  • 来自 AISP 提供商的 API 密钥
  • 一个支持的欧盟国家的银行账户

我是 open-banking.io 的维护者,因此我会以我们的 API 作为示例——但模式对任何 PSD2 AISP(Enable Banking、Salt Edge、Tink 等)都相同。请选择最适合你覆盖范围和预算的提供商。

步骤 1:获取你的 API 密钥

注册并获取 API 密钥。将它存储为环境变量:

export OBI_API_KEY="***"

Enter fullscreen mode Exit fullscreen mode

切勿硬编码。如果提交到 git,立即轮换。

步骤 2:发起银行连接

你的用户需要同意分享他们的数据。这会将他们重定向到银行的登录页面:

import os
import json
import urllib.request

API_BASE = "https://api.open-banking.io/v1"
API_KEY=os.env...EY"]

def create_session(bank_id: str, return_url: str) -> dict:
    """Start a bank connection. Returns a consent URL for the user."""
    payload = json.dumps({
        "bank_id": bank_id,
        "return_url": return_url,
    }).encode()

    req = urllib.request.Request(
        f"{API_BASE}/sessions",
        data=payload,
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json",
        },
        method="POST",
    )

    with urllib.request.urlopen(req) as resp:
        return json.loads(resp.read())

# Example: connect to a Danish bank
result = create_session("nordea-dk", "https://yourapp.com/callback")
print(f"Send the user to: {result['consent_url']}")

Enter fullscreen mode Exit fullscreen mode

用户访问 consent_url,登录他们的银行并批准。他们会被重定向回你的 return_url,并带有一个会话令牌。

步骤 3:获取账户和余额

会话授权后:

def get_accounts(session_id: str) -> list:
    req = urllib.request.Request(
        f"{API_BASE}/sessions/{session_id}/accounts",
        headers={"Authorization": f"Bearer {API_KEY}"},
    )
    with urllib.request.urlopen(req) as resp:
        return json.loads(resp.read())["accounts"]

def get_balance(session_id: str, account_id: str) -> dict:
    req = urllib.request.Request(
        f"{API_BASE}/sessions/{session_id}/accounts/{account_id}/balance",
        headers={"Authorization": f"Bearer {API_KEY}"},
    )
    with urllib.request.urlopen(req) as resp:
        return json.loads(resp.read())

accounts = get_accounts(result["session_id"])
for acct in accounts:
    bal = get_balance(result["session_id"], acct["id"])
    print(f"{acct['name']}: {bal['amount']} {bal['currency']}")

Enter fullscreen mode Exit fullscreen mode

步骤 4:获取交易

def get_transactions(session_id: str, account_id: str) -> list:
    req = urllib.request.Request(
        f"{API_BASE}/sessions/{session_id}/accounts/{account_id}/transactions",
        headers={"Authorization": f"Bearer {API_KEY}"},
    )
    with urllib.request.urlopen(req) as resp:
        return json.loads(resp.read())["transactions"]

for txn in get_transactions(result["session_id"], accounts[0]["id"]):
    print(f"{txn['date']} | {txn['amount']:>8} | {txn['description'][:50]}")

Enter fullscreen mode Exit fullscreen mode

就是这样。你可以在不接触任何证书的情况下读取实时银行数据。

值得注意的陷阱

同意到期。 PSD2 同意会到期(通常为 90 天,视银行而定)。你需要定期重新授权。将此纳入你的应用流程——不要假设会话永远有效。

已入账 vs 待处理。 交易分为两种类型。booked 交易已完成;pending 交易是临时的,可能会更改或消失。如果你正在构建预算工具,请将 booked 作为你的真实数据源,并仔细去重——同一笔交易在结算窗口期间可能同时出现在两个列表中。

银行覆盖缺口。 没有 AISP 覆盖所有欧盟银行。在承诺之前,请检查提供商的覆盖列表,确认你的用户实际需要的银行。较小的区域银行通常是缺口所在。

速率限制。 PSD2 API 对每个同意执行访问频率限制(交易通常为每天 4 次)。积极缓存。不要循环轮询。

隐私模型。 一些提供商在服务器端存储你的交易数据。其他提供商(如我们)使用客户端持有的加密密钥,因此提供商根本无法读取用户数据。如果你面向注重 GDPR 的用户,请检查你使用的是哪种模型。

何时应该获取证书

如果你正在构建大规模的受监管金融产品——处理支付(PIS)、进行信用检查,或服务跨多家银行的数千用户——获取你自己的 eIDAS 证书并直接连接。中间商会增加延迟、每次调用的成本,以及你在规模化时不想要的依赖。

但对于个人项目、小型 SaaS 工具、内部仪表板,以及任何只需要读取账户数据而无需企业开销的场景:AISP 路径是务实的选择。


披露:我是 John,open-banking.io 的维护者——一个免证书的 PSD2 欧盟银行账户信息服务。上述代码适用于我们的 API,但该模式适用于任何合规的 AISP。有问题?请在评论区找到我。