在Django中通过Plaid获取银行账户号码的技术问题咨询
解决Plaid对接后获取测试账户及展示账户信息的问题
一、获取测试用虚拟银行账户
Plaid Sandbox环境提供现成的测试账户,无需手动创建:
- 通用测试登录凭据:用户名为
user_good,密码为pass_good,部分机构需要验证码时输入1234即可完成验证。 - 完成Plaid Link流程后,Sandbox会返回虚拟账户数据,包括掩码形式的账户号码、余额等信息,可直接用于开发测试。
二、获取并展示账户号码与余额
你已拿到access token和账户ID,接下来需调用Plaid的accounts/get接口获取账户详情,再传递到前端展示:
1. 修正现有create_link_token视图的错误
原代码处理Plaid响应的逻辑有误,调整后如下:
@ensure_csrf_cookie def create_link_token(request): user = request.user if user.is_authenticated: data = { 'user': { 'client_user_id': str(user.id) }, 'products': ["transactions"], 'client_name': "Budget Bounty", 'country_codes': ['US'], 'language': 'en' } # 直接获取Plaid响应,无需额外包装 response = client.post('link/token/create', data) link_token = response['link_token'] return JsonResponse({'link_token': link_token}) # 返回键值对格式,而非单纯字符串 else: return HttpResponseRedirect('/')
2. 新增获取账户信息的视图
添加视图函数调用Plaid接口获取账户数据(假设你已将用户的access token存储在数据库中):
from django.http import JsonResponse, HttpResponseRedirect from django.views.decorators.csrf import csrf_protect from django.contrib.auth.decorators import login_required @login_required @csrf_protect def get_accounts(request): # 从用户模型或关联表中取出存储的access_token,根据实际逻辑调整 access_token = request.user.plaid_access_token try: # 调用Plaid accounts/get接口 plaid_response = client.post('accounts/get', { 'access_token': access_token }) accounts = plaid_response['accounts'] # 整理前端需要的字段 formatted_accounts = [] for account in accounts: formatted_accounts.append({ 'account_name': account['name'], 'masked_number': f"****{account['mask']}", # 掩码后的账户号码 'available_balance': account['balances']['available'] or account['balances']['current'], 'account_type': account['type'] }) return JsonResponse({'accounts': formatted_accounts}) except Exception as e: return JsonResponse({'error': str(e)}, status=400)
3. 前端展示账户信息
在link-account.html中,完成Plaid Link认证后调用get_accounts接口渲染账户数据:
<button id="link-button">关联银行账户</button> <div id="account-list"></div> <script src="https://cdn.plaid.com/link/v2/stable/link-initialize.js"></script> <script> // 请求获取link_token fetch('/create-link-token/', { method: 'GET', headers: { 'X-CSRFToken': getCookie('csrftoken') } }) .then(response => response.json()) .then(data => { // 初始化Plaid Link const handler = Plaid.create({ token: data.link_token, onSuccess: function(public_token, metadata) { // 先将public_token传给后端交换为长期有效的access_token并存储 fetch('/exchange-public-token/', { // 需自行实现该交换接口 method: 'POST', headers: { 'X-CSRFToken': getCookie('csrftoken'), 'Content-Type': 'application/json' }, body: JSON.stringify({public_token: public_token}) }) .then(() => { // 交换完成后请求账户信息 return fetch('/get-accounts/', { method: 'POST', headers: { 'X-CSRFToken': getCookie('csrftoken') } }) }) .then(response => response.json()) .then(accountData => { // 渲染账户信息到UI const accountList = document.getElementById('account-list'); accountData.accounts.forEach(account => { const card = document.createElement('div'); card.style.border = '1px solid #eee'; card.style.padding = '1rem'; card.style.margin = '1rem 0'; card.innerHTML = ` <h3>${account.account_name}</h3> <p>账户号码:${account.masked_number}</p> <p>可用余额:$${account.available_balance.toFixed(2)}</p> <p>账户类型:${account.account_type}</p> `; accountList.appendChild(card); }); }); }, onExit: function(err, metadata) { // 处理用户退出Link流程的逻辑 } }); // 绑定按钮点击事件触发Plaid Link document.getElementById('link-button').addEventListener('click', () => { handler.open(); }); }); // 获取CSRF Cookie的辅助函数 function getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== '') { const cookies = document.cookie.split(';'); for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].trim(); if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } </script>
4. 补充说明
- 需自行实现
exchange-public-token接口,将Plaid返回的public_token交换为长期有效的access_token,并关联到当前用户存储到数据库中。 - 如果需要获取完整的账户号码(非掩码),需确保你的Plaid产品权限包含
auth,并在create_link_token的products数组中添加"auth"。
内容的提问来源于stack exchange,提问作者Coder
相关产品推荐
相关产品推荐

