-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathknowledge
More file actions
114 lines (91 loc) Β· 5.99 KB
/
Copy pathknowledge
File metadata and controls
114 lines (91 loc) Β· 5.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
GigGuard knowledge file
========================
Field notes from building this. Mostly the Investec specific things that
cost me time, plus a couple of money handling rules I do not want anyone
to quietly break later. Read this before you touch the card code.
1. beforeTransaction has roughly a 2 second budget
The Investec card hook gives you about 2 seconds to make a decision.
If your code takes longer, or throws, the platform stops waiting and
approves the transaction itself. So a slow backend does not block the
card, it silently lets spends through. Keep /check fast and dumb. All
the heavy lifting (polling, token refresh) happens elsewhere.
2. Sandbox and live use different token endpoints, and this one bit us
In the sandbox the token AND the data both come from
openapisandbox.investec.com, and the token path is
/identity/v2/oauth2/token. In production the token comes from a different
host entirely, identity.secure.investec.com/connect/token, with data on
openapi.investec.com. We learned this the direct way: an early version
pointed the sandbox at the live token host, every poll failed auth, and the
buffer never filled. If a token call fails or every data call 404s, the
first thing to check is which host and path you are hitting. The two
constants live at the top of server.js and can be overridden by env.
3. The API talks rands, the card talks cents
This is the one that will bite you. The transactions API returns the
amount as a plain number in rands, for example 8000 or 152.75. The
card hook gives you centsAmount, already in cents. Internally GigGuard
is cents everywhere. So when a credit comes off the poll we multiply by
100 (Math.round to be safe about float dust), and when the card hands
us centsAmount we use it as is. Get this wrong and you are off by 100x.
4. In the card IDE, environment variables are env.NAME not process.env
The card code does not run in Node. There is no process, no require,
no npm. Environment variables are read off a global called env, so it
is env.GIGGUARD_WEBHOOK_URL and env.GIGGUARD_API_KEY. Paste those two
into the IDE settings panel, not into a .env file. Only fetch and
console.log are reliably available in there.
5. afterTransaction can fire for things that did not actually spend
Depending on card settings you may get an after hook for declines and
reversals as well as clean approvals. If you blindly add every after
hook to the weekly total, a declined R900 would eat R900 of someone's
release that they never spent. GigGuard only records an approved debit,
and the backend double checks status and type before it touches the
ledger. The card code keeps declines on a separate afterDecline path
that does nothing but log.
6. Income detection is polling, not a webhook
Investec will push you card transaction hooks, but plain incoming
credits (a gig payout landing in the account) do not come with a
webhook in the sandbox. So we poll the transactions API on a schedule.
That means income is detected a little late, whenever the next poll
runs, not the instant it lands. Fine for smoothing. Just do not promise
anyone real time income alerts.
7. Money is integer cents, full stop
No floats for money, ever. toBuffer is Math.floor(amount * pct / 100).
weeklyRelease is Math.floor(buffer / weeks). Flooring means a cent or
two stays parked in the buffer rather than being released, which is the
safe direction to round. test.js locks all of this in. If you ever see
a value like 599.9999 you have let a float in somewhere, go find it.
8. When in doubt, approve
Both the card code and the backend fail open. Backend unreachable,
timeout, bad JSON, wrong api key, all of it returns approved true or
lets the platform default through. The reasoning: GigGuard is a
budgeting helper, not a fraud system. Wrongly declining someone's only
card at a till is a real world harm. Wrongly approving one spend just
means the weekly cap was soft that one time. Pick the smaller harm.
9. Lazy week reset is on purpose
There is no cron resetting the weekly spend at midnight on Monday. We
check the elapsed time whenever the card or the dashboard touches the
user, and if a full seven days have passed we zero spentThisWeek and
start a fresh week from that moment. Simpler, no scheduler to babysit,
and the only cost is the reset happens on first use of the new week
rather than exactly at the boundary. Nobody cares about that gap.
10. The buffer is a ledger, not an account
Important and not just a technicality. GigGuard never moves money. The
buffer is a number we keep that says how much of your own income we
are suggesting you not spend yet. The cash stays in your own Investec
account the whole time. We are not holding funds, not running escrow,
not a third party custodian. That keeps this firmly a budgeting tool
rather than a deposit taking or payment business, which is a very
different regulatory conversation in South Africa. If you ever change
this to actually sweep money somewhere, stop and get advice first.
11. check and record are two calls, so watch the gap between them
beforeTransaction asks /check, which reads remaining and approves, but
the weekly total only moves later when afterTransaction calls /record.
Two taps in the same blink both read the same remaining and can both
pass, quietly busting the cap. The fix is a short reservation: /check
holds the approved amount and counts it against remaining until /record
settles it or the hold ages out (we use roughly the 2 second hook budget
plus a margin). The holds have to expire on their own, otherwise an
approve that never gets a matching record would wedge the cap shut. This
narrows the race, it does not make enforcement atomic. Do not advertise
the cap as unbreakable: fail open and a missing record still let the
total drift. There is a test that fires two checks before any record and
asserts the second is declined.