Skip to content

Commit 3c51183

Browse files
committed
feat(api): add parse/parseUnsafe webhook helpers and event schemas
1 parent 08fd371 commit 3c51183

File tree

120 files changed

+90238
-92
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

120 files changed

+90238
-92
lines changed

README.md

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -354,15 +354,50 @@ Or to `debug` for more verbose logging:
354354
export LITHIC_LOG=debug
355355
```
356356

357-
## Webhook Verification
357+
## Webhooks
358358

359-
We provide helper methods for verifying that a webhook request came from Lithic, and not a malicious third party.
359+
Lithic uses webhooks to notify your application when events happen. The SDK provides methods for verifying webhook signatures and parsing event payloads.
360360

361-
You can use `lithic.webhooks().verifySignature(body, headers, secret?)` or `lithic.webhooks().unwrap(body, headers, secret?)`,
362-
both of which will raise an error if the signature is invalid.
361+
### Parsing and verifying webhooks
363362

364-
Note that the "body" parameter must be the raw JSON string sent from the server (do not parse it first).
365-
The `.unwrap()` method can parse this JSON for you.
363+
Use `parse()` to verify the signature and return a typed event:
364+
365+
```java
366+
import com.lithic.api.core.http.Headers;
367+
import com.lithic.api.models.ParsedWebhookEvent;
368+
369+
// Convert request headers to Headers object
370+
Headers headers = Headers.builder().putAll(request.getHeaders()).build();
371+
372+
// Verify signature and parse payload
373+
// Secret is optional if configured on client or set via LITHIC_WEBHOOK_SECRET env var
374+
ParsedWebhookEvent event = client.webhooks().parse(requestBody, headers, null);
375+
376+
// Handle based on event type using isXxx() checks
377+
if (event.isCardCreated()) {
378+
System.out.println("Card created: " + event.asCardCreated().cardToken());
379+
} else if (event.isCardTransactionUpdated()) {
380+
String transactionToken = event.asCardTransactionUpdated().token();
381+
System.out.println("Transaction updated: " + transactionToken);
382+
}
383+
384+
```
385+
386+
### Parsing without verification
387+
You can parse the webhook body without verifying the signature using `parseUnsafe()`. This is not recommended for production use:
388+
389+
```
390+
// Parse only - skips signature verification (not recommended for production)
391+
ParsedWebhookEvent event = client.webhooks().parseUnsafe(body);
392+
```
393+
394+
### Manual signature verification
395+
To verify the signature without parsing the body, use `verifySignature()`:
396+
397+
```
398+
// Only verify signature, don't parse. Throws LithicException if signature is invalid
399+
client.webhooks().verifySignature(body, headers, null);
400+
```
366401

367402
## ProGuard and R8
368403

lithic-java-core/src/main/kotlin/com/lithic/api/client/LithicClient.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,6 @@ interface LithicClient {
113113

114114
fun responderEndpoints(): ResponderEndpointService
115115

116-
fun webhooks(): WebhookService
117-
118116
fun externalBankAccounts(): ExternalBankAccountService
119117

120118
fun payments(): PaymentService
@@ -145,6 +143,8 @@ interface LithicClient {
145143

146144
fun accountActivity(): AccountActivityService
147145

146+
fun webhooks(): WebhookService
147+
148148
/** Status of api */
149149
fun apiStatus(): ApiStatus = apiStatus(ClientApiStatusParams.none())
150150

@@ -249,6 +249,8 @@ interface LithicClient {
249249

250250
fun accountActivity(): AccountActivityService.WithRawResponse
251251

252+
fun webhooks(): WebhookService.WithRawResponse
253+
252254
/**
253255
* Returns a raw HTTP response for `get /v1/status`, but is otherwise the same as
254256
* [LithicClient.apiStatus].

lithic-java-core/src/main/kotlin/com/lithic/api/client/LithicClientAsync.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,6 @@ interface LithicClientAsync {
113113

114114
fun responderEndpoints(): ResponderEndpointServiceAsync
115115

116-
fun webhooks(): WebhookServiceAsync
117-
118116
fun externalBankAccounts(): ExternalBankAccountServiceAsync
119117

120118
fun payments(): PaymentServiceAsync
@@ -145,6 +143,8 @@ interface LithicClientAsync {
145143

146144
fun accountActivity(): AccountActivityServiceAsync
147145

146+
fun webhooks(): WebhookServiceAsync
147+
148148
/** Status of api */
149149
fun apiStatus(): CompletableFuture<ApiStatus> = apiStatus(ClientApiStatusParams.none())
150150

@@ -252,6 +252,8 @@ interface LithicClientAsync {
252252

253253
fun accountActivity(): AccountActivityServiceAsync.WithRawResponse
254254

255+
fun webhooks(): WebhookServiceAsync.WithRawResponse
256+
255257
/**
256258
* Returns a raw HTTP response for `get /v1/status`, but is otherwise the same as
257259
* [LithicClientAsync.apiStatus].

lithic-java-core/src/main/kotlin/com/lithic/api/client/LithicClientAsyncImpl.kt

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,6 @@ class LithicClientAsyncImpl(private val clientOptions: ClientOptions) : LithicCl
169169
ResponderEndpointServiceAsyncImpl(clientOptionsWithUserAgent)
170170
}
171171

172-
private val webhooks: WebhookServiceAsync by lazy { WebhookServiceAsyncImpl(clientOptions) }
173-
174172
private val externalBankAccounts: ExternalBankAccountServiceAsync by lazy {
175173
ExternalBankAccountServiceAsyncImpl(clientOptionsWithUserAgent)
176174
}
@@ -231,6 +229,10 @@ class LithicClientAsyncImpl(private val clientOptions: ClientOptions) : LithicCl
231229
AccountActivityServiceAsyncImpl(clientOptionsWithUserAgent)
232230
}
233231

232+
private val webhooks: WebhookServiceAsync by lazy {
233+
WebhookServiceAsyncImpl(clientOptionsWithUserAgent)
234+
}
235+
234236
override fun sync(): LithicClient = sync
235237

236238
override fun withRawResponse(): LithicClientAsync.WithRawResponse = withRawResponse
@@ -273,8 +275,6 @@ class LithicClientAsyncImpl(private val clientOptions: ClientOptions) : LithicCl
273275

274276
override fun responderEndpoints(): ResponderEndpointServiceAsync = responderEndpoints
275277

276-
override fun webhooks(): WebhookServiceAsync = webhooks
277-
278278
override fun externalBankAccounts(): ExternalBankAccountServiceAsync = externalBankAccounts
279279

280280
override fun payments(): PaymentServiceAsync = payments
@@ -305,6 +305,8 @@ class LithicClientAsyncImpl(private val clientOptions: ClientOptions) : LithicCl
305305

306306
override fun accountActivity(): AccountActivityServiceAsync = accountActivity
307307

308+
override fun webhooks(): WebhookServiceAsync = webhooks
309+
308310
override fun apiStatus(
309311
params: ClientApiStatusParams,
310312
requestOptions: RequestOptions,
@@ -449,6 +451,10 @@ class LithicClientAsyncImpl(private val clientOptions: ClientOptions) : LithicCl
449451
AccountActivityServiceAsyncImpl.WithRawResponseImpl(clientOptions)
450452
}
451453

454+
private val webhooks: WebhookServiceAsync.WithRawResponse by lazy {
455+
WebhookServiceAsyncImpl.WithRawResponseImpl(clientOptions)
456+
}
457+
452458
override fun withOptions(
453459
modifier: Consumer<ClientOptions.Builder>
454460
): LithicClientAsync.WithRawResponse =
@@ -530,6 +536,8 @@ class LithicClientAsyncImpl(private val clientOptions: ClientOptions) : LithicCl
530536
override fun accountActivity(): AccountActivityServiceAsync.WithRawResponse =
531537
accountActivity
532538

539+
override fun webhooks(): WebhookServiceAsync.WithRawResponse = webhooks
540+
533541
private val apiStatusHandler: Handler<ApiStatus> =
534542
jsonHandler<ApiStatus>(clientOptions.jsonMapper)
535543

lithic-java-core/src/main/kotlin/com/lithic/api/client/LithicClientImpl.kt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,6 @@ class LithicClientImpl(private val clientOptions: ClientOptions) : LithicClient
160160
ResponderEndpointServiceImpl(clientOptionsWithUserAgent)
161161
}
162162

163-
private val webhooks: WebhookService by lazy { WebhookServiceImpl(clientOptions) }
164-
165163
private val externalBankAccounts: ExternalBankAccountService by lazy {
166164
ExternalBankAccountServiceImpl(clientOptionsWithUserAgent)
167165
}
@@ -214,6 +212,8 @@ class LithicClientImpl(private val clientOptions: ClientOptions) : LithicClient
214212
AccountActivityServiceImpl(clientOptionsWithUserAgent)
215213
}
216214

215+
private val webhooks: WebhookService by lazy { WebhookServiceImpl(clientOptionsWithUserAgent) }
216+
217217
override fun async(): LithicClientAsync = async
218218

219219
override fun withRawResponse(): LithicClient.WithRawResponse = withRawResponse
@@ -255,8 +255,6 @@ class LithicClientImpl(private val clientOptions: ClientOptions) : LithicClient
255255

256256
override fun responderEndpoints(): ResponderEndpointService = responderEndpoints
257257

258-
override fun webhooks(): WebhookService = webhooks
259-
260258
override fun externalBankAccounts(): ExternalBankAccountService = externalBankAccounts
261259

262260
override fun payments(): PaymentService = payments
@@ -287,6 +285,8 @@ class LithicClientImpl(private val clientOptions: ClientOptions) : LithicClient
287285

288286
override fun accountActivity(): AccountActivityService = accountActivity
289287

288+
override fun webhooks(): WebhookService = webhooks
289+
290290
override fun apiStatus(
291291
params: ClientApiStatusParams,
292292
requestOptions: RequestOptions,
@@ -431,6 +431,10 @@ class LithicClientImpl(private val clientOptions: ClientOptions) : LithicClient
431431
AccountActivityServiceImpl.WithRawResponseImpl(clientOptions)
432432
}
433433

434+
private val webhooks: WebhookService.WithRawResponse by lazy {
435+
WebhookServiceImpl.WithRawResponseImpl(clientOptions)
436+
}
437+
434438
override fun withOptions(
435439
modifier: Consumer<ClientOptions.Builder>
436440
): LithicClient.WithRawResponse =
@@ -510,6 +514,8 @@ class LithicClientImpl(private val clientOptions: ClientOptions) : LithicClient
510514

511515
override fun accountActivity(): AccountActivityService.WithRawResponse = accountActivity
512516

517+
override fun webhooks(): WebhookService.WithRawResponse = webhooks
518+
513519
private val apiStatusHandler: Handler<ApiStatus> =
514520
jsonHandler<ApiStatus>(clientOptions.jsonMapper)
515521

0 commit comments

Comments
 (0)