MJ-Prod commited on
Commit
6f02f79
·
1 Parent(s): ccd6802

add plaid link endpoints

Browse files
Files changed (1) hide show
  1. plaid_client.py +122 -0
plaid_client.py CHANGED
@@ -1,8 +1,129 @@
 
 
 
 
 
 
 
 
 
 
1
  from plaid.model.link_token_create_request import LinkTokenCreateRequest
2
  from plaid.model.link_token_create_request_user import LinkTokenCreateRequestUser
3
  from plaid.model.item_public_token_exchange_request import ItemPublicTokenExchangeRequest
4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  def create_link_token(user_id: str) -> str:
 
6
  request = LinkTokenCreateRequest(
7
  user=LinkTokenCreateRequestUser(client_user_id=user_id),
8
  client_name="FISCAL",
@@ -14,6 +135,7 @@ def create_link_token(user_id: str) -> str:
14
  return response["link_token"]
15
 
16
  def exchange_public_token(public_token: str) -> str:
 
17
  request = ItemPublicTokenExchangeRequest(public_token=public_token)
18
  response = plaid_client.item_public_token_exchange(request)
19
  return response["access_token"]
 
1
+ import os
2
+ from datetime import date, timedelta
3
+ from plaid.api import plaid_api
4
+ from plaid.configuration import Configuration
5
+ from plaid.api_client import ApiClient
6
+ from plaid.model.products import Products
7
+ from plaid.model.country_code import CountryCode
8
+ from plaid.model.accounts_balance_get_request import AccountsBalanceGetRequest
9
+ from plaid.model.transactions_get_request import TransactionsGetRequest
10
+ from plaid.model.transactions_get_request_options import TransactionsGetRequestOptions
11
  from plaid.model.link_token_create_request import LinkTokenCreateRequest
12
  from plaid.model.link_token_create_request_user import LinkTokenCreateRequestUser
13
  from plaid.model.item_public_token_exchange_request import ItemPublicTokenExchangeRequest
14
 
15
+ # ---------- Client setup ----------
16
+
17
+ def _make_client():
18
+ env = os.environ.get("PLAID_ENV", "sandbox")
19
+ host = {
20
+ "sandbox": "https://sandbox.plaid.com",
21
+ "development": "https://development.plaid.com",
22
+ "production": "https://production.plaid.com",
23
+ }[env]
24
+
25
+ config = Configuration(
26
+ host=host,
27
+ api_key={
28
+ "clientId": os.environ["PLAID_CLIENT_ID"], # reads from .env
29
+ "secret": os.environ["PLAID_SECRET"]
30
+ }
31
+ )
32
+ return plaid_api.PlaidApi(ApiClient(config))
33
+
34
+ plaid_client = _make_client()
35
+
36
+
37
+ # ---------- Balances ----------
38
+
39
+ def get_balances(access_token: str) -> str:
40
+ """Returns formatted balance snapshot for the LLM prompt."""
41
+ request = AccountsBalanceGetRequest(access_token=access_token)
42
+ response = plaid_client.accounts_balance_get(request)
43
+
44
+ lines = ["USER'S CURRENT ACCOUNT BALANCES:"]
45
+ for account in response['accounts']:
46
+ name = account['name']
47
+ subtype = account['subtype'] # 'checking', 'credit card', etc.
48
+ current = account['balances']['current']
49
+
50
+ if subtype == 'credit card':
51
+ limit = account['balances']['limit'] or 0
52
+ available = account['balances']['available'] or (limit - current)
53
+ lines.append(
54
+ f"- {name} (Visa/Credit): "
55
+ f"${current:.2f} owing, ${available:.2f} available"
56
+ )
57
+ else:
58
+ lines.append(f"- {name} (Chequing): ${current:.2f}")
59
+
60
+ return "\n".join(lines)
61
+
62
+
63
+ # ---------- Transactions ----------
64
+
65
+ def get_transactions(access_token: str, days: int = 30) -> str:
66
+ """Returns formatted transaction summary for the LLM prompt."""
67
+ end_date = date.today()
68
+ start_date = end_date - timedelta(days=days)
69
+
70
+ request = TransactionsGetRequest(
71
+ access_token=access_token,
72
+ start_date=start_date,
73
+ end_date=end_date,
74
+ options=TransactionsGetRequestOptions(
75
+ count=100,
76
+ include_personal_finance_category=True
77
+ )
78
+ )
79
+ response = plaid_client.transactions_get(request)
80
+ transactions = response['transactions']
81
+
82
+ if not transactions:
83
+ return "No transactions found in the last 30 days."
84
+
85
+ # Python does the math — never let the LLM calculate
86
+ category_totals: dict[str, float] = {}
87
+ for txn in transactions:
88
+ if txn['amount'] <= 0:
89
+ continue # skip deposits/refunds
90
+ category = txn.get('personal_finance_category', {}).get('primary', 'OTHER')
91
+ category_totals[category] = category_totals.get(category, 0) + txn['amount']
92
+
93
+ # Format for prompt
94
+ lines = [f"SPENDING SUMMARY (last {days} days):"]
95
+ for category, total in sorted(category_totals.items(), key=lambda x: -x[1]):
96
+ readable = category.replace("_", " ").title()
97
+ lines.append(f"- {readable}: ${total:.2f}")
98
+
99
+ lines.append(f"\nTotal spent: ${sum(category_totals.values()):.2f}")
100
+ lines.append(f"\nRECENT TRANSACTIONS (last 10):")
101
+ for txn in transactions[:10]:
102
+ lines.append(
103
+ f"- {txn['date']} {txn['name']:<30} ${txn['amount']:.2f}"
104
+ )
105
+
106
+ return "\n".join(lines)
107
+
108
+
109
+ # ---------- Combined snapshot ----------
110
+
111
+ def get_financial_snapshot(access_token: str) -> str:
112
+ """
113
+ Returns full financial context string to inject into the LLM prompt.
114
+ Combines balances + transaction summary.
115
+ """
116
+ try:
117
+ balances = get_balances(access_token)
118
+ transactions = get_transactions(access_token)
119
+ return f"{balances}\n\n{transactions}"
120
+ except Exception as e:
121
+ return f"Unable to fetch account data: {str(e)}"
122
+
123
+
124
+
125
  def create_link_token(user_id: str) -> str:
126
+ """Creates a Plaid Link token for the given user."""
127
  request = LinkTokenCreateRequest(
128
  user=LinkTokenCreateRequestUser(client_user_id=user_id),
129
  client_name="FISCAL",
 
135
  return response["link_token"]
136
 
137
  def exchange_public_token(public_token: str) -> str:
138
+ """Exchanges a public token for a permanent access token."""
139
  request = ItemPublicTokenExchangeRequest(public_token=public_token)
140
  response = plaid_client.item_public_token_exchange(request)
141
  return response["access_token"]