@distyra/sdk 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -2
- package/dist/generated.d.ts +4317 -759
- package/dist/index.d.ts +94 -2
- package/dist/index.js +154 -4
- package/dist/webhook.d.ts +56 -0
- package/dist/webhook.js +93 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,8 +36,9 @@ if (error) {
|
|
|
36
36
|
throw new Error(error.detail ?? error.error);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
// `data` is fully typed against the EnrichResponse schema
|
|
40
|
-
|
|
39
|
+
// `data` is fully typed against the EnrichResponse schema. `merchant` is null
|
|
40
|
+
// for non-merchant transactions (bank fees, transfers), so guard it.
|
|
41
|
+
console.log(data.merchant?.name ?? '(no merchant)', data.category.primary);
|
|
41
42
|
```
|
|
42
43
|
|
|
43
44
|
## Options
|
|
@@ -52,6 +53,67 @@ console.log(data.merchant.name, data.category.primary);
|
|
|
52
53
|
|
|
53
54
|
The returned client is a typed [`openapi-fetch`](https://openapi-ts.dev/openapi-fetch/) client — use `.GET`, `.POST`, etc. against any path in the spec, with full request/response typing.
|
|
54
55
|
|
|
56
|
+
## Handling unknown merchants (eventual consistency)
|
|
57
|
+
|
|
58
|
+
The first time anyone enriches a brand-new merchant, the response may be unresolved
|
|
59
|
+
(`resolution.status` other than `resolved`). The system then discovers and confirms that
|
|
60
|
+
merchant in the background, so the same descriptor resolves shortly after, for every
|
|
61
|
+
caller, with no model call.
|
|
62
|
+
|
|
63
|
+
Read `resolution.retry_after`:
|
|
64
|
+
|
|
65
|
+
- A number (seconds) means a confirmed answer is likely coming. Retry the same descriptor
|
|
66
|
+
once after that delay, or just let your next natural re-sync pick it up.
|
|
67
|
+
- `null` means the result is definitive; no retry will help.
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
const { data } = await distyra.POST('/v1/enrich', { body: { descriptor, country_hint } });
|
|
71
|
+
if (data.resolution.status !== 'resolved' && data.resolution.retry_after != null) {
|
|
72
|
+
// retry once after data.resolution.retry_after seconds, or wait for your next sync
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Note: `resolution.retryable` is a different signal. It is `true` only for a transient
|
|
77
|
+
backend error (`timed_out` / `transient_error`), which you retry within seconds. See the
|
|
78
|
+
full resolution-and-retries guide at <https://api.distyra.com/docs>.
|
|
79
|
+
|
|
80
|
+
## Verifying webhooks
|
|
81
|
+
|
|
82
|
+
Distyra signs every webhook delivery with a Stripe-shape header:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
X-Distyra-Signature: t=<unix>,v1=<hmac_sha256_hex>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`verifyWebhook` checks it (HMAC-SHA256 over `"<t>.<rawBody>"`, keyed by the
|
|
89
|
+
endpoint's signing secret, with replay protection). Always verify against the
|
|
90
|
+
**raw request body**, before JSON parsing — re-serializing changes the bytes and
|
|
91
|
+
breaks the signature.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { verifyWebhook } from '@distyra/sdk';
|
|
95
|
+
|
|
96
|
+
// Express — capture the raw body, e.g. app.use(express.raw({ type: 'application/json' }))
|
|
97
|
+
app.post('/webhooks/distyra', (req, res) => {
|
|
98
|
+
const raw = req.body.toString('utf8');
|
|
99
|
+
const result = verifyWebhook(
|
|
100
|
+
process.env.DISTYRA_WEBHOOK_SECRET!,
|
|
101
|
+
raw,
|
|
102
|
+
req.header('X-Distyra-Signature'),
|
|
103
|
+
);
|
|
104
|
+
if (!result.ok) return res.status(400).send(result.reason);
|
|
105
|
+
|
|
106
|
+
const event = JSON.parse(raw);
|
|
107
|
+
// ... handle event ...
|
|
108
|
+
res.sendStatus(200);
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
It returns `{ ok: true, timestamp }` or `{ ok: false, reason }` where `reason` is
|
|
113
|
+
`'malformed'`, `'stale'`, or `'no_match'`. The default replay window is 5 minutes
|
|
114
|
+
(`DEFAULT_TOLERANCE_SECONDS`); override it with `{ toleranceSeconds }`. Uses
|
|
115
|
+
`node:crypto`, so it runs server-side (Node 18+).
|
|
116
|
+
|
|
55
117
|
## Lower-level access
|
|
56
118
|
|
|
57
119
|
The generated `paths` and `operations` types are re-exported if you want to build your own `openapi-fetch` client or reference response shapes directly:
|