@spritz-finance/api-client 0.6.0 → 0.8.0

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 CHANGED
@@ -817,17 +817,23 @@ const accounts = await client.virtualAccounts.list()
817
817
 
818
818
  ## ACH Onramp (Direct Debit)
819
819
 
820
- ACH onramp lets users convert USD from their bank account into USDC delivered to a Solana wallet. The flow is:
820
+ ACH onramp lets users convert USD from their bank account into USDC delivered to a Solana wallet. The integration is a short server-side flow with one client-side Plaid step:
821
821
 
822
- 1. **Link bank account** via Plaid funding source created automatically
823
- 2. **Prepare deposit** quote and ACH authorization message for the user to review
824
- 3. **Create deposit** confirm to debit the bank and release USDC to the wallet
822
+ 1. **Server:** create a Plaid link token with `client.bankAccount.createLinkToken()`
823
+ 2. **Client:** run Plaid Link and send the public token/account IDs back to your server
824
+ 3. **Server:** complete linking with `client.bankAccount.completeLinking(...)`
825
+ 4. **Server:** find an active funding source and fetch limits with `client.fundingSource.getDepositLimits(id)`
826
+ 5. **Server:** prepare a quote with `client.deposit.prepare(...)`
827
+ 6. **Client:** show the quote and ACH authorization message to the user
828
+ 7. **Server:** create the deposit with `client.deposit.create(...)`; Spritz runs risk checks before initiating the ACH pull
825
829
 
826
830
  Authorization is derived from the verified ACH funding source — no wallet signature is required.
827
831
 
832
+ If risk checks block the create step, the API returns 409 before any ACH debit is pulled. Prepare a new quote before retrying; blocked create attempts consume the original `preparationId`.
833
+
828
834
  For a complete walkthrough with code examples, request/response schemas, and deposit lifecycle documentation, see the **[ACH Onramp Integration Guide](docs/ach-onramp-guide.md)**.
829
835
 
830
- A standalone sandbox demo is available at `scripts/sandbox/ach-onramp.html` open it in a browser to walk through the full flow interactively.
836
+ A standalone sandbox demo is available at `scripts/sandbox/ach-onramp.html`. Run `yarn build && node scripts/sandbox/evidence-server.mjs`, then open `http://localhost:3001/ach-onramp.html` to test the SDK-backed flow and save redacted QC evidence.
831
837
 
832
838
  ## Sandbox
833
839
 
@@ -875,12 +881,31 @@ This endpoint returns 403 in production.
875
881
 
876
882
  - `capabilities.updated` — user capabilities changed
877
883
 
884
+ #### On-Ramp Events
885
+
886
+ - `onramp.created` — on-ramp record created after a deposit is authorized
887
+ - `onramp.updated` — on-ramp status, delivery, or reversal details updated
888
+ - `onramp.completed` — on-ramp delivery completed
889
+
890
+ #### ACH Debit Return Events
891
+
892
+ - `achDebitReturn.created` — ACH debit return recorded
893
+ - `achDebitReturn.updated` — ACH debit return details updated
894
+
895
+ Use `'*'` to subscribe a webhook endpoint to all current and future webhook events.
896
+
878
897
  ### Setup
879
898
 
880
899
  ```typescript
881
900
  const webhook = await client.webhook.create({
882
901
  url: 'https://my.webhook.url/spritz',
883
- events: ['account.created', 'account.updated', 'payment.completed'],
902
+ events: ['onramp.created', 'onramp.updated', 'achDebitReturn.created'],
903
+ })
904
+
905
+ // Subscribe to all events
906
+ await client.webhook.create({
907
+ url: 'https://my.webhook.url/spritz/all',
908
+ events: ['*'],
884
909
  })
885
910
  ```
886
911
 
@@ -900,13 +925,18 @@ Webhook payloads have the following shape:
900
925
  // List all webhooks
901
926
  const webhooks = await client.webhook.list()
902
927
 
928
+ // Update a webhook's event subscriptions
929
+ await client.webhook.update('webhook-id', {
930
+ events: ['onramp.updated', 'achDebitReturn.created', 'achDebitReturn.updated'],
931
+ })
932
+
903
933
  // Delete a webhook
904
934
  await client.webhook.delete('webhook-id')
905
935
  ```
906
936
 
907
937
  ### Security and Signing
908
938
 
909
- Webhook requests are signed with HMAC SHA256 using your webhook secret. The signature is sent in the `Signature` HTTP header.
939
+ Webhook requests are signed with HMAC SHA256 using your webhook secret. The signature is sent in the `Signature` HTTP header. Verify the signature against the raw request body before parsing JSON.
910
940
 
911
941
  #### Setting a Webhook Secret
912
942
 
@@ -917,11 +947,19 @@ await client.webhook.updateWebhookSecret('your-secret')
917
947
  #### Verifying Signatures
918
948
 
919
949
  ```typescript
920
- import { createHmac } from 'crypto'
950
+ import { createHmac, timingSafeEqual } from 'node:crypto'
951
+
952
+ function verifySpritzWebhook(rawBody: string, signature: string, secret: string) {
953
+ const expected = createHmac('sha256', secret).update(rawBody).digest('hex')
954
+ const expectedBuffer = Buffer.from(expected, 'utf8')
955
+ const signatureBuffer = Buffer.from(signature, 'utf8')
921
956
 
922
- const expected = createHmac('sha256', WEBHOOK_SECRET).update(JSON.stringify(payload)).digest('hex')
957
+ if (expectedBuffer.length !== signatureBuffer.length) return false
958
+ return timingSafeEqual(expectedBuffer, signatureBuffer)
959
+ }
923
960
 
924
- if (expected !== request.headers['signature']) {
961
+ const signature = request.headers['signature']
962
+ if (!signature || !verifySpritzWebhook(rawBody, signature, WEBHOOK_SECRET)) {
925
963
  throw new Error('Invalid webhook signature')
926
964
  }
927
965
  ```