@spritz-finance/api-client 0.4.27 → 0.5.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
@@ -1,178 +1,128 @@
1
1
  # @spritz-finance/api-client
2
2
 
3
- A Typescript library for interacting with the Spritz Finance API
3
+ TypeScript client for the Spritz Finance API — convert crypto to fiat payments.
4
4
 
5
5
  [![NPM](https://img.shields.io/npm/v/@spritz-finance/api-client.svg)](https://www.npmjs.com/package/@spritz-finance/api-client)
6
6
 
7
7
  ## Installation
8
8
 
9
- ### Using npm
10
-
11
9
  ```bash
12
- npm install --save @spritz-finance/api-client
13
- ```
14
-
15
- ### Using yarn
16
-
17
- ```bash
18
- yarn add @spritz-finance/api-client
10
+ npm install @spritz-finance/api-client
11
+ # or
12
+ yarn add @spritz-finance/api-client
19
13
  ```
20
14
 
21
15
  ## Quick Start
22
16
 
23
- Get started with Spritz in minutes:
24
-
25
17
  ```typescript
26
18
  import {
27
- SpritzApiClient,
28
- Environment,
29
- PaymentNetwork,
30
- BankAccountType,
31
- BankAccountSubType,
32
- DebitCardNetwork,
33
- AmountMode,
19
+ SpritzApiClient,
20
+ Environment,
21
+ PaymentNetwork,
22
+ BankAccountType,
23
+ BankAccountSubType,
34
24
  } from '@spritz-finance/api-client'
35
25
 
36
- // Initialize the client with your integration key
26
+ // Initialize with your integration key
37
27
  const client = SpritzApiClient.initialize({
38
- environment: Environment.Sandbox,
39
- integrationKey: 'YOUR_INTEGRATION_KEY_HERE',
28
+ environment: Environment.Sandbox,
29
+ integrationKey: 'YOUR_INTEGRATION_KEY_HERE',
40
30
  })
41
31
 
42
- // Create a user
43
- const user = await client.user.create({
44
- email: 'user@example.com',
45
- })
46
-
47
- // Set the user's API key
32
+ // Create a user and set their API key
33
+ const user = await client.user.create({ email: 'user@example.com' })
48
34
  client.setApiKey(user.apiKey)
49
35
 
50
36
  // Add a bank account
51
37
  const bankAccount = await client.bankAccount.create(BankAccountType.USBankAccount, {
52
- accountNumber: '123456789',
53
- routingNumber: '987654321',
54
- name: 'My Checking Account',
55
- ownedByUser: true,
56
- subType: BankAccountSubType.Checking,
38
+ accountNumber: '123456789',
39
+ routingNumber: '987654321',
40
+ name: 'My Checking Account',
41
+ ownedByUser: true,
42
+ subType: BankAccountSubType.Checking,
57
43
  })
58
44
 
59
45
  // Create a payment request
60
46
  const paymentRequest = await client.paymentRequest.create({
61
- amount: 100,
62
- accountId: bankAccount.id,
63
- network: PaymentNetwork.Ethereum,
47
+ amount: 100,
48
+ accountId: bankAccount.id,
49
+ network: PaymentNetwork.Ethereum,
64
50
  })
65
51
 
66
- // Get transaction data for blockchain payment
52
+ // Get transaction data for the blockchain payment
67
53
  const transactionData = await client.paymentRequest.getWeb3PaymentParams({
68
- paymentRequest,
69
- paymentTokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC on mainnet
54
+ paymentRequest,
55
+ paymentTokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC
70
56
  })
71
57
 
72
- // Use transactionData to execute blockchain transaction in your app
58
+ // Execute the blockchain transaction from the user's wallet
73
59
  ```
74
60
 
75
- ## Table of contents
76
-
77
- - [Quick Start](#quick-start)
78
- - [Installation](#installation)
79
- - [API Overview](#api-overview)
80
- - [Creating a user](#creating-a-user)
81
- - [Capabilities of the API Key](#capabilities-of-the-api-key)
82
- - [Usage](#usage)
83
- - [User Management](#user-management)
84
- - [Creating a user](#creating-a-user-1)
85
- - [Setting the User API Key](#setting-the-user-api-key)
86
- - [Reauthorizing a user](#reauthorizing-a-user)
87
- - [Basic User Data](#basic-user-data)
88
- - [User Verification](#user-verification)
89
- - [Payment Flow](#payment-flow)
90
- - [Basic payment flow](#basic-payment-flow)
91
- - [Note on Issuing the Blockchain Transaction](#note-on-issuing-the-blockchain-transaction)
92
- - [Example](#example)
61
+ ## Table of Contents
62
+
63
+ - [Authentication](#authentication)
64
+ - [Users](#users)
65
+ - [Creating a User](#creating-a-user)
66
+ - [Reauthorization](#reauthorization)
67
+ - [User Data](#user-data)
68
+ - [Identity Verification](#identity-verification)
93
69
  - [Accounts](#accounts)
94
- - [Account Types](#account-types)
95
- - [Commonalities & Differences](#commonalities---differences)
96
- - [Bank Accounts](#bank-accounts)
97
- - [Debit Cards](#debit-cards)
98
- - [Bills](#bills)
99
- - [Virtual Card](#virtual-card)
100
- - [Address Book](#address-book)
101
- - [Account Management](#account-management)
102
- - [Renaming accounts](#renaming-accounts)
103
- - [Deleting accounts](#deleting-accounts)
104
- - [Bill Institutions](#bill-institutions)
105
- - [Fetching popular bill institutions](#fetching-popular-bill-institutions)
106
- - [Searching for bill institutions by name](#searching-for-bill-institutions-by-name)
107
- - [Payment Requests](#payment-requests)
108
- - [Create a payment request](#create-a-payment-request)
109
- - [Fulfil a payment request (EVM transactions)](#fulfil-a-payment-request--evm-transactions-)
110
- - [Fulfil a payment request (Solana transactions)](#fulfil-a-payment-request--solana-transactions-)
111
- - [Transaction fees](#transaction-fees)
112
- - [Payments](#payments)
113
- - [Retrieve the payment for a payment request](#retrieve-the-payment-for-a-payment-request)
114
- - [Retrieve all payments for an account](#retrieve-all-payments-for-an-account)
115
- - [Get payment limits for an account](#get-payment-limits-for-an-account)
116
- - [Onramp Payments](#onramp-payments)
117
- - [Create an onramp payment](#create-onramp-payment)
118
- - [Retrieve all onramp payments for an account](#retrieve-all-onramp-payments-for-an-account)
70
+ - [Bank Accounts](#bank-accounts)
71
+ - [Debit Cards](#debit-cards)
72
+ - [Bills](#bills)
73
+ - [Virtual Cards](#virtual-cards)
74
+ - [Address Book](#address-book)
75
+ - [Renaming Accounts](#renaming-accounts)
76
+ - [Deleting Accounts](#deleting-accounts)
77
+ - [Payments (Off-ramp)](#payments-off-ramp)
78
+ - [Payment Flow](#payment-flow)
79
+ - [Creating a Payment Request](#creating-a-payment-request)
80
+ - [Fulfilling a Payment — EVM](#fulfilling-a-payment--evm)
81
+ - [Fulfilling a Payment — Solana](#fulfilling-a-payment--solana)
82
+ - [Transaction Fees](#transaction-fees)
83
+ - [Retrieving Payments](#retrieving-payments)
84
+ - [Payment Limits](#payment-limits)
85
+ - [On-ramp](#on-ramp)
86
+ - [Prerequisites](#prerequisites)
87
+ - [Checking User Access](#checking-user-access)
88
+ - [Activation Steps](#activation-steps)
89
+ - [Virtual Accounts](#virtual-accounts)
90
+ - [Supported Tokens](#supported-tokens)
91
+ - [ACH Onramp (Direct Debit)](#ach-onramp-direct-debit)
92
+ - [Sandbox](#sandbox)
93
+ - [Bypassing KYC](#bypassing-kyc)
119
94
  - [Webhooks](#webhooks)
120
- - [Supported webhook events](#supported-webhook-events)
121
- - [Setting up webhooks](#setting-up-webhooks)
122
- - [On-ramp Setup Guide](#on-ramp-setup-guide)
123
- - [Prerequisites](#prerequisites)
124
- - [Checking User Access Capabilities](#checking-user-access-capabilities)
125
- - [Step-by-Step On-ramp Activation](#step-by-step-on-ramp-activation)
126
- - [Creating Virtual Accounts](#creating-virtual-accounts)
127
- - [Listing Virtual Accounts](#listing-virtual-accounts)
128
- - [Supported Token Matrix](#supported-token-matrix)
129
- - [Complete Example](#complete-example)
95
+ - [Events](#events)
96
+ - [Setup](#setup)
97
+ - [Management](#management)
98
+ - [Security and Signing](#security-and-signing)
130
99
 
131
- ## API Overview
100
+ ## Authentication
132
101
 
133
- **Purpose**: As an integrator, this guide will assist you in creating users and performing user-specific operations on the Spritz platform using the provided API key.
102
+ Spritz uses two levels of authentication:
134
103
 
135
- ### Creating a user
136
-
137
- When you create a user using your integration key:
138
-
139
- - You will receive an `API key` specific to that user.
140
- - This enables you to interact with the Spritz platform on the user's behalf.
141
-
142
- ### Capabilities of the API Key
143
-
144
- Using the user-specific API key, you can:
145
-
146
- 1. **Identity Verification**: Guide a user through the identity verification process.
147
- 2. **Account Addition**:
148
- - Add Bills for the user.
149
- - Register Bank accounts.
150
- - Issue Virtual cards.
151
- 3. **Payment Requests**: Initiate payment requests to the aforementioned accounts.
152
- 4. **Blockchain Transactions**: Issue blockchain-based transactions to fulfill the payment requests.
153
- 5. **Payment Status**: Query the status of payments directed to the user's accounts.
154
-
155
- ## Usage
156
-
157
- Your integration key is provided by Spritz and must always be provided.
158
- The api key is specific to each user,
159
- and is returned once the user is created. Leave the api key blank if you haven't created the user yet.
104
+ - **Integration key** — identifies your application. Provided by Spritz.
105
+ - **User API key** — scoped to a single user. Returned when you create a user.
160
106
 
161
107
  ```typescript
162
108
  import { SpritzApiClient, Environment } from '@spritz-finance/api-client'
163
109
 
164
110
  const client = SpritzApiClient.initialize({
165
- environment: Environment.Sandbox,
166
- apiKey: 'YOUR_USER_API_KEY_HERE',
167
- integrationKey: 'YOUR_INTEGRATION_KEY_HERE',
111
+ environment: Environment.Sandbox,
112
+ integrationKey: 'YOUR_INTEGRATION_KEY_HERE',
113
+ apiKey: 'YOUR_USER_API_KEY_HERE', // omit if no user exists yet
168
114
  })
169
115
  ```
170
116
 
171
- ## User Management
117
+ After creating a user, set their API key on the client:
172
118
 
173
- ### Creating a user
119
+ ```typescript
120
+ client.setApiKey(user.apiKey)
121
+ ```
174
122
 
175
- To create a new Spritz user, all you need is the user's email address. Note that trying to create a user with an email that already exists in the Spritz platform will throw an error.
123
+ ## Users
124
+
125
+ ### Creating a User
176
126
 
177
127
  ```typescript
178
128
  const user = await client.user.create({
@@ -180,504 +130,339 @@ const user = await client.user.create({
180
130
  })
181
131
 
182
132
  // Response
183
- user = {
184
- email: "bilbo@shiremail.net"
185
- userId: "62d17d3b377dab6c1342136e",
186
- apiKey: "ak_ZTBGDcjfdTg3NmYtZDJlZC00ZjYyLThlMDMtZmYwNDJiZDRlMWZm"
133
+ {
134
+ email: 'bilbo@shiremail.net',
135
+ userId: '62d17d3b377dab6c1342136e',
136
+ apiKey: 'ak_ZTBGDcjfdTg3NmYtZDJlZC00ZjYyLThlMDMtZmYwNDJiZDRlMWZm',
187
137
  }
188
138
  ```
189
139
 
190
- ### Setting the User API Key
191
-
192
- After creating a user, you can easily set the user's API key onto your initialized client using the provided method:
193
-
194
- ```typescript
195
- client.setApiKey(user.apiKey)
196
- ```
197
-
198
- Now you're ready to issue requests on behalf of the user.
140
+ Creating a user with an email that already exists will throw an error.
199
141
 
200
- ### Reauthorizing a user
142
+ ### Reauthorization
201
143
 
202
- There is a scenrio where you may need to get access to a users API key again. This can happen if you are trying to sign in a user that already has a Spritz account, or if you have lost access to their API key. In this case, you can reauthorize the user by providing their email. The process is that we will send the user an OTP code to their email, and then the user must pass that code on to you to confirm that they are allowing you to interact with their account on their behalf.
144
+ If you need to recover a user's API key (e.g., the user already has a Spritz account, or you've lost access), use the OTP reauthorization flow:
203
145
 
204
146
  ```typescript
147
+ // Request an OTP code sent to the user's email
205
148
  const { success } = await client.user.requestApiKey('bilbo@shiremail.net')
206
149
 
150
+ // Confirm with the OTP code the user provides
207
151
  const { apiKey, userId, email } = await client.user.authorizeApiKeyWithOTP({
208
- email: 'bilbo@shiremail.net',
209
- otp: '123456',
152
+ email: 'bilbo@shiremail.net',
153
+ otp: '123456',
210
154
  })
211
155
  ```
212
156
 
213
- ### Basic User Data
214
-
215
- Use this to fetch the user's basic data
157
+ ### User Data
216
158
 
217
159
  ```typescript
218
160
  const userData = await client.user.getCurrentUser()
219
161
  ```
220
162
 
221
- ### User Verification
222
-
223
- **Purpose**: To ensure users are properly identified before interacting with the Spritz platform.
224
-
225
- ### Overview
226
-
227
- All users must undergo basic identity verification before they can engage with the Spritz platform's features.
163
+ ### Identity Verification
228
164
 
229
- ### Process
165
+ All users must complete identity verification before using the platform. New users start with a verification status of `NotStarted`.
230
166
 
231
- 1. **User Creation**: Upon the creation of a new user, their default verification status will be set to `NotStarted`.
167
+ The user's verification data is included in the `getCurrentUser` response, including verification status, verification URL, verified country, and retry capability.
232
168
 
233
- 2. **Checking Verification Status**: The user's verification data is included in the `getCurrentUser` response, including verification status, verification URL, verified country, and retry capability.
234
-
235
- 3. **Verification Transition**: Once a user completes the identity verification process, their status will change from `NotStarted` to `Verified`. Only then can the user fully interact with the platform.
236
-
237
- 4. **Getting Verification URL**: The verification URL is included in the user data response and is essential for the user to proceed with their identity verification.
238
-
239
- ### How to Present Verification Flow to the User
240
-
241
- Spritz offers two methods for integrating KYC verification, both using the `getVerificationParams()` method:
169
+ #### Getting Verification Parameters
242
170
 
243
171
  ```typescript
244
- // Get verification parameters from Spritz
245
172
  const verificationParams = await client.user.getVerificationParams()
246
173
 
247
- // The response includes:
174
+ // Returns:
248
175
  // - inquiryId: Unique identifier for this verification inquiry
249
176
  // - verificationUrl: URL for hosted verification
250
- // - sessionToken: Token for use with Plaid Link SDK
177
+ // - sessionToken: Token for use with Persona's Embedded Flow
251
178
  // - verificationUrlExpiresAt: Expiration timestamp for the verification URL
252
179
  ```
253
180
 
254
- #### Method 1: Verification URL (Simple Integration)
181
+ #### Option 1: Verification URL
255
182
 
256
- Use the `verificationUrl` from `getVerificationParams()` for a straightforward integration where Spritz handles the entire verification flow:
183
+ The simplest integration redirect the user to the hosted verification flow:
257
184
 
258
185
  ```typescript
259
- const verificationParams = await client.user.getVerificationParams()
260
- const verificationUrl = verificationParams.verificationUrl
261
- const expiresAt = verificationParams.verificationUrlExpiresAt
262
-
263
- // Important: The verification URL is short-lived and single-use
264
- // - Check the expiration with verificationUrlExpiresAt before using
265
- // - Once accessed, the URL becomes invalid
266
- // - If verification is not completed, call getVerificationParams() again for a new URL
267
-
268
- // Use the verification URL in your application:
269
- // - Browser: Open the URL in a new browser tab
270
- // - In-App: Embed the URL in an iframe within your application
271
- // - Mobile: Open the URL in a native mobile web view
272
- // - Email: Send users an email containing the link
273
- ```
274
-
275
- **Note**: The verification URL can only be used once. If the user doesn't complete verification after accessing the URL, you'll need to call `getVerificationParams()` again to generate a fresh URL and session.
186
+ const { verificationUrl, verificationUrlExpiresAt } = await client.user.getVerificationParams()
276
187
 
277
- #### Method 2: Embedded Flow (Advanced Integration)
278
-
279
- For more control over the verification experience, use the `inquiryId` and `sessionToken` (if present) from `getVerificationParams()` with the Persona Embedded Flow:
280
-
281
- ```typescript
282
- const verificationParams = await client.user.getVerificationParams()
283
- const inquiryId = verificationParams.inquiryId
284
- const sessionToken = verificationParams.sessionToken // May be null for some flows
285
-
286
- // Use the inquiryId (and sessionToken if present) with Persona's Embedded Flow
287
- // to create a custom verification experience
288
- // This allows you to:
289
- // - Customize the UI/UX of the verification process
290
- // - Handle callbacks and events directly
291
- // - Integrate verification seamlessly into your application flow
292
- // - Build a native mobile experience using Persona's mobile SDKs
188
+ // Open in a browser tab, iframe, or mobile web view.
189
+ // The URL is single-use and short-lived. If it expires or the user
190
+ // doesn't complete verification, call getVerificationParams() again.
293
191
  ```
294
192
 
295
- The embedded flow method is ideal when you want to:
296
-
297
- - Maintain full control over the user experience
298
- - Integrate verification directly into your app without redirects
299
- - Handle verification events and callbacks programmatically
300
- - Track and resume verification sessions with the inquiryId
301
-
302
- See the [Persona Embedded Flow documentation](https://docs.withpersona.com/quickstart-embedded-flow) for detailed integration instructions with the inquiryId and sessionToken.
193
+ #### Option 2: Embedded Flow
303
194
 
304
- ### Verification Metadata (Failed Verifications)
195
+ For full control over the UX, use the `inquiryId` and `sessionToken` with [Persona's Embedded Flow](https://docs.withpersona.com/quickstart-embedded-flow):
305
196
 
306
- When a user's verification fails, Spritz now provides additional metadata to help understand the failure reason and provide better user experience. This metadata is particularly useful for handling duplicate identity scenarios.
307
-
308
- #### Understanding Verification Metadata
309
-
310
- The verification metadata is available when a verification has failed and includes:
311
-
312
- - **`failureReason`**: The specific reason why the verification failed
313
- - **`details.matchedEmail`**: For duplicate identity failures, shows the email of the matched user (only if created through the same integration)
314
-
315
- #### Verification Failure Reasons
316
-
317
- The following verification failure reasons may be returned:
197
+ ```typescript
198
+ const { inquiryId, sessionToken } = await client.user.getVerificationParams()
318
199
 
319
- - **`verify_sms`**: SMS verification failed
320
- - **`documentary_verification`**: Document verification failed
321
- - **`risk_check`**: Risk assessment check failed
322
- - **`kyc_check`**: KYC (Know Your Customer) check failed
323
- - **`watchlist_screening`**: Watchlist screening check failed
324
- - **`selfie_check`**: Selfie verification check failed
325
- - **`address_invalid`**: Provided address is invalid
326
- - **`duplicate_identity`**: Identity already exists in the system
200
+ // Use inquiryId (and sessionToken if present) with Persona's SDK
201
+ // to embed the verification flow directly in your app.
202
+ ```
327
203
 
328
- #### Duplicate Identity Handling
204
+ #### Handling Verification Failures
329
205
 
330
- When a verification fails due to `duplicate_identity`, the system detects that the user has already been verified under a different account. The `matchedEmail` field helps determine:
206
+ When verification fails, the `verificationMetadata` field on the user object provides the failure reason:
331
207
 
332
- - **If `matchedEmail` is populated**: The duplicate user was created through your integration
333
- - **If `matchedEmail` is `null`**: The duplicate user was created through a different integration (e.g., the main Spritz app)
208
+ | Failure Reason | Description |
209
+ | -------------------------- | ---------------------------- |
210
+ | `verify_sms` | SMS verification failed |
211
+ | `documentary_verification` | Document verification failed |
212
+ | `risk_check` | Risk assessment failed |
213
+ | `kyc_check` | KYC check failed |
214
+ | `watchlist_screening` | Watchlist screening failed |
215
+ | `selfie_check` | Selfie verification failed |
216
+ | `address_invalid` | Invalid address |
217
+ | `duplicate_identity` | Identity already exists |
334
218
 
335
- This allows you to provide appropriate guidance to your users:
219
+ For `duplicate_identity` failures, `matchedEmail` indicates whether the duplicate was created through your integration:
336
220
 
337
221
  ```typescript
338
222
  const userData = await client.user.getCurrentUser()
339
223
 
340
224
  if (userData.verificationMetadata?.failureReason === 'duplicate_identity') {
341
- const matchedEmail = userData.verificationMetadata.details.matchedEmail
342
-
343
- if (matchedEmail) {
344
- // User exists within your integration
345
- console.log(`This identity is already verified with account: ${matchedEmail}`)
346
- // Guide user to use their existing account
347
- } else {
348
- // User exists in Spritz but not in your integration
349
- console.log('This identity is already verified with another Spritz account')
350
- // Inform user they need to use their original Spritz account
351
- }
225
+ const matchedEmail = userData.verificationMetadata.details.matchedEmail
226
+
227
+ if (matchedEmail) {
228
+ // Duplicate exists within your integration — guide user to their existing account
229
+ console.log(`Already verified as: ${matchedEmail}`)
230
+ } else {
231
+ // Duplicate exists in a different integration (e.g., the main Spritz app)
232
+ console.log('Identity already verified with another Spritz account')
233
+ }
352
234
  }
353
235
  ```
354
236
 
355
- ## Payment Flow
356
-
357
- ### Basic payment flow
358
-
359
- Execute a payment in a few simple steps:
360
-
361
- 1. **Select an Account**: Choose the account you wish to pay to.
362
- 2. **Initiate Payment Request**: Use the account's ID, your desired payment amount, and the chosen blockchain network to create a payment request.
363
- 3. **Retrieve Transaction Data**: Use the `getWeb3PaymentParams` method to obtain the necessary transaction data for fulfilling the payment request.
364
- 4. **Blockchain Transaction**: Issue the required blockchain transaction from the user's wallet.
365
- 5. **Payment Confirmation**: After blockchain transaction confirmation, check the status of the TradFi payment.
366
-
367
- ### Note on Issuing the Blockchain Transaction
368
-
369
- For Spritz to process a TradFi payment to an account, we need to receive a blockchain transaction on our smart contract, which provides us the crypto funds. As an integrator, it's essential to manage how the blockchain transaction is initiated from the user's wallet to Spritz.
370
-
371
- - **Wallet Apps**: If your application functions as a wallet, prompt the user to sign a transaction using data from `getWeb3PaymentParams`.
372
- - **Web-based Dapps**: Use your existing connection to the user's wallet to prompt a transaction.
373
-
374
- If your application doesn't have a connection to the user's wallet, consider implementing one. Some popular options include:
375
-
376
- - [Web3Modal (Web-based)](https://github.com/WalletConnect/web3modal)
377
- - [Web3Modal (React Native)](https://github.com/WalletConnect/modal-react-native)
378
- - [Web3-Onboard](https://onboard.blocknative.com/docs/overview/introduction#features)
379
-
380
- ### Example
381
-
382
- ```typescript
383
- // Fetch all bank accounts for the user
384
- const bankAccounts = await client.bankAccount.list()
385
-
386
- // Choose a bank account to use for the payment request
387
- const account = bankAccounts[0]
388
-
389
- // Create a payment request for the selected bank account
390
- const paymentRequest = await client.paymentRequest.create({
391
- amount: 100,
392
- accountId: account.id,
393
- network: PaymentNetwork.Ethereum,
394
- })
395
-
396
- // Retrieve the transaction data required to issue a blockchain transaction
397
- const transactionData = await client.paymentRequest.getWeb3PaymentParams({
398
- paymentRequest,
399
- paymentTokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC on mainnet
400
- })
401
-
402
- /**
403
- * Issue blockchain transaction with the transaction data
404
- * and wait for confirmation
405
- **/
406
-
407
- // Retrieve the payment issued for the payment request to check the payment status and confirmation
408
- const payment = await client.payment.getForPaymentRequest(paymentRequest.id)
409
- ```
410
-
411
237
  ## Accounts
412
238
 
413
- Spritz emphasizes its capabilities in account handling and payment processing.
414
-
415
- ### Account Types
416
-
417
- Spritz supports four distinct types of accounts:
418
-
419
- 1. **Bank Account**
420
- 2. **Debit Card**
421
- 3. **Bill**
422
- 4. **Virtual Card**
423
-
424
- Though each account type possesses its unique creation process and specific properties, it's important to understand that all of them are uniformly termed as an "account" within the Spritz platform.
425
-
426
- ### Commonalities & Differences
427
-
428
- - **Common Properties**: Every type of account shares certain properties consistent across the platform.
429
- - **Unique Properties**: Each account type also has attributes specific to its nature and functionality.
430
-
431
- Recognizing these nuances is crucial for optimal interaction with the Spritz platform's account-related features.
239
+ Spritz supports four account types: **Bank Account**, **Debit Card**, **Bill**, and **Virtual Card**. All are referred to as "accounts" within the platform and share common properties (`id`, `type`, `userId`, `country`, `currency`, `createdAt`), with additional fields specific to each type.
432
240
 
433
241
  ### Bank Accounts
434
242
 
435
- Spritz offers a dedicated interface to manage bank accounts, allowing seamless listing and addition of bank account details for users.
436
-
437
- #### List user bank accounts
438
-
439
- To retrieve all bank accounts linked to a user:
243
+ #### List
440
244
 
441
245
  ```typescript
442
246
  const bankAccounts = await client.bankAccount.list()
443
247
  ```
444
248
 
445
- The `bankAccount.list()` method returns an array of user-linked bank accounts, complete with essential details to display in a UI and facilitate payments:
446
-
447
249
  ```typescript
448
- const bankAccounts = [{
449
- id: "62d17d3b377dab6c1342136e",
450
- name: "Precious Savings",
451
- type: "BankAccount",
452
- bankAccountType: "USBankAccount",
453
- bankAccountSubType: "Checking",
454
- userId: "62d17d3b377dab6c1342136e",
455
- accountNumber: "1234567",
456
- bankAccountDetails: {
457
- routingNumber: "00000123",
458
- }
459
- country: "US",
460
- currency: "USD",
461
- email: "bilbo@shiremail.net",
462
- institution: {
463
- id: "62d27d4b277dab3c1342126e",
464
- name: "Shire Bank",
465
- logo: "https://tinyurl.com/shire-bank-logo",
466
- },
467
- ownedByUser: true,
468
- createdAt: "2023-05-03T11:25:02.401Z",
469
- deliveryMethods: ['STANDARD', 'INSTANT']
470
- }]
250
+ // Example response
251
+ ;[
252
+ {
253
+ id: '62d17d3b377dab6c1342136e',
254
+ name: 'Precious Savings',
255
+ type: 'BankAccount',
256
+ bankAccountType: 'USBankAccount',
257
+ bankAccountSubType: 'Checking',
258
+ userId: '62d17d3b377dab6c1342136e',
259
+ accountNumber: '1234567',
260
+ bankAccountDetails: {
261
+ routingNumber: '00000123',
262
+ },
263
+ country: 'US',
264
+ currency: 'USD',
265
+ email: 'bilbo@shiremail.net',
266
+ institution: {
267
+ id: '62d27d4b277dab3c1342126e',
268
+ name: 'Shire Bank',
269
+ logo: 'https://tinyurl.com/shire-bank-logo',
270
+ },
271
+ ownedByUser: true,
272
+ createdAt: '2023-05-03T11:25:02.401Z',
273
+ deliveryMethods: ['STANDARD', 'INSTANT'],
274
+ },
275
+ ]
471
276
  ```
472
277
 
473
- #### Add US bank account
474
-
475
- Currently, Spritz supports the addition of US bank accounts:
476
-
477
- The input structure for adding a US bank account is defined as:
478
-
479
- ```typescript
480
- // Input arguments for creating a US bank account
481
- export interface USBankAccountInput {
482
- accountNumber: string
483
- email?: string | null
484
- name?: string | null
485
- ownedByUser?: boolean | null
486
- routingNumber: string
487
- subType: BankAccountSubType
488
- }
489
- ```
278
+ #### Create US Bank Account
490
279
 
491
280
  ```typescript
492
281
  import { BankAccountType, BankAccountSubType } from '@spritz-finance/api-client'
493
282
 
494
- const bankAccounts = await client.bankAccount.create(BankAccountType.USBankAccount, {
495
- accountNumber: '123456789',
496
- routingNumber: '987654321',
497
- name: 'Precious Savings',
498
- ownedByUser: true,
499
- subType: BankAccountSubType.Savings,
283
+ const bankAccount = await client.bankAccount.create(BankAccountType.USBankAccount, {
284
+ accountNumber: '123456789',
285
+ routingNumber: '987654321',
286
+ name: 'Precious Savings',
287
+ ownedByUser: true,
288
+ subType: BankAccountSubType.Savings,
500
289
  })
501
290
  ```
502
291
 
503
- #### Add Canadian bank account
504
-
505
- Currently, Spritz supports the addition of Canadian bank accounts:
506
-
507
- The input structure for adding a Canadian bank account is defined as:
292
+ Input fields:
508
293
 
509
294
  ```typescript
510
- // Input arguments for creating a Canadian bank account
511
- export interface CABankAccountInput {
512
- accountNumber: string
513
- email?: string
514
- name: string
515
- ownedByUser?: boolean | null
516
- transitNumber: string
517
- institutionNumber: string
518
- subType: BankAccountSubType
295
+ interface USBankAccountInput {
296
+ accountNumber: string
297
+ routingNumber: string
298
+ subType: BankAccountSubType
299
+ name?: string | null
300
+ email?: string | null
301
+ ownedByUser?: boolean | null
519
302
  }
520
303
  ```
521
304
 
305
+ #### Create Canadian Bank Account
306
+
522
307
  ```typescript
523
308
  import { BankAccountType, BankAccountSubType } from '@spritz-finance/api-client'
524
309
 
525
- const bankAccounts = await client.bankAccount.create(BankAccountType.CABankAccount, {
526
- accountNumber: '123456789',
527
- transitNumber: '12345',
528
- institutionNumber: '123',
529
- name: 'Precious Savings',
530
- ownedByUser: true,
531
- subType: BankAccountSubType.Savings,
310
+ const bankAccount = await client.bankAccount.create(BankAccountType.CABankAccount, {
311
+ accountNumber: '123456789',
312
+ transitNumber: '12345',
313
+ institutionNumber: '123',
314
+ name: 'Precious Savings',
315
+ ownedByUser: true,
316
+ subType: BankAccountSubType.Savings,
532
317
  })
533
318
  ```
534
319
 
535
- ### Debit Cards
320
+ Input fields:
321
+
322
+ ```typescript
323
+ interface CABankAccountInput {
324
+ accountNumber: string
325
+ transitNumber: string
326
+ institutionNumber: string
327
+ name: string
328
+ subType: BankAccountSubType
329
+ email?: string
330
+ ownedByUser?: boolean | null
331
+ }
332
+ ```
536
333
 
537
- Spritz provides support for adding debit cards as payment accounts, allowing users to make payments directly to their debit cards.
334
+ ### Debit Cards
538
335
 
539
- #### List user debit cards
336
+ Supported networks: **Visa** and **Mastercard**.
540
337
 
541
- To retrieve all debit cards linked to a user:
338
+ #### List
542
339
 
543
340
  ```typescript
544
341
  const debitCards = await client.debitCard.list()
545
342
  ```
546
343
 
547
- The `debitCard.list()` method returns an array of user-linked debit cards:
548
-
549
344
  ```typescript
550
- const debitCards = [
551
- {
552
- id: '62d17d3b377dab6c1342136e',
553
- type: 'DebitCard',
554
- name: 'My Visa Debit',
555
- userId: '62d17d3b377dab6c1342136e',
556
- country: 'US',
557
- currency: 'USD',
558
- payable: true,
559
- debitCardNetwork: 'Visa',
560
- expirationDate: '12/25',
561
- cardNumber: '4111111111111111',
562
- mask: '1111',
563
- createdAt: '2023-01-01T00:00:00Z',
564
- paymentCount: 5,
565
- externalId: 'ext-123',
566
- },
345
+ // Example response
346
+ ;[
347
+ {
348
+ id: '62d17d3b377dab6c1342136e',
349
+ type: 'DebitCard',
350
+ name: 'My Visa Debit',
351
+ userId: '62d17d3b377dab6c1342136e',
352
+ country: 'US',
353
+ currency: 'USD',
354
+ payable: true,
355
+ debitCardNetwork: 'Visa',
356
+ expirationDate: '12/25',
357
+ cardNumber: '4111111111111111',
358
+ mask: '1111',
359
+ createdAt: '2023-01-01T00:00:00Z',
360
+ paymentCount: 5,
361
+ externalId: 'ext-123',
362
+ },
567
363
  ]
568
364
  ```
569
365
 
570
- #### Add a debit card
571
-
572
- To add a new debit card for a user:
366
+ #### Create
573
367
 
574
368
  ```typescript
575
- import { DebitCardNetwork } from '@spritz-finance/api-client'
576
-
577
369
  const debitCard = await client.debitCard.create({
578
- name: 'My Visa Debit',
579
- cardNumber: '4111111111111111',
580
- expirationDate: '12/25',
370
+ cardNumber: '4111111111111111', // 13-19 digits
371
+ expirationDate: '12/25', // MM/YY
372
+ name: 'My Visa Debit', // optional
581
373
  })
582
374
  ```
583
375
 
584
- The input structure for adding a debit card is:
585
-
586
- ```typescript
587
- export interface DebitCardInput {
588
- name?: string | null // Optional name for the card
589
- cardNumber: string // Full card number (13-19 digits)
590
- expirationDate: string // Expiration date in MM/YY format
591
- }
592
- ```
593
-
594
- Supported debit card networks:
595
-
596
- - `Visa`
597
- - `Mastercard`
598
-
599
376
  ### Bills
600
377
 
601
- Spritz provides robust support for bills, allowing seamless management and interaction with user billing accounts. Below is a guide to the methods and functionalities specifically designed for handling bills within Spritz.
602
-
603
- #### List user bills
604
-
605
- To retrieve all bill accounts associated with a user:
378
+ #### List
606
379
 
607
380
  ```typescript
608
381
  const bills = await client.bill.list()
609
382
  ```
610
383
 
611
- The `bill.list()` method returns an array of user-associated bills, complete with essential details for display in a UI and for processing payments:
612
-
613
384
  ```typescript
614
- const bills = [
615
- {
616
- id: '62d17d3b377dab6c1342136e',
617
- name: 'Precious Credit Card',
618
- type: 'Bill',
619
- billType: 'CreditCard',
620
- userId: '62d17d3b377dab6c1342136e',
621
- mask: '4567',
622
- originator: 'User',
623
- payable: true,
624
- verifying: false,
625
- billAccountDetails: {
626
- balance: 240.23,
627
- amountDue: 28.34,
628
- openedAt: '2023-05-03T11:25:02.401Z',
629
- lastPaymentAmount: null,
630
- lastPaymentDate: null,
631
- nextPaymentDueDate: '2023-06-03T11:25:02.401Z',
632
- nextPaymentMinimumAmount: 28.34,
633
- lastStatementBalance: 180.23,
634
- remainingStatementBalance: null,
635
- },
636
- country: 'US',
637
- currency: 'USD',
638
- dataSync: {
639
- lastSync: '2023-05-03T11:25:02.401Z',
640
- syncStatus: 'Active',
641
- },
642
- institution: {
643
- id: '62d27d4b277dab3c1342126e',
644
- name: 'Shire Bank Credit Card',
645
- logo: 'https://tinyurl.com/shire-bank-logo',
385
+ // Example response
386
+ ;[
387
+ {
388
+ id: '62d17d3b377dab6c1342136e',
389
+ name: 'Precious Credit Card',
390
+ type: 'Bill',
391
+ billType: 'CreditCard',
392
+ userId: '62d17d3b377dab6c1342136e',
393
+ mask: '4567',
394
+ originator: 'User',
395
+ payable: true,
396
+ verifying: false,
397
+ billAccountDetails: {
398
+ balance: 240.23,
399
+ amountDue: 28.34,
400
+ openedAt: '2023-05-03T11:25:02.401Z',
401
+ lastPaymentAmount: null,
402
+ lastPaymentDate: null,
403
+ nextPaymentDueDate: '2023-06-03T11:25:02.401Z',
404
+ nextPaymentMinimumAmount: 28.34,
405
+ lastStatementBalance: 180.23,
406
+ remainingStatementBalance: null,
407
+ },
408
+ country: 'US',
409
+ currency: 'USD',
410
+ dataSync: {
411
+ lastSync: '2023-05-03T11:25:02.401Z',
412
+ syncStatus: 'Active',
413
+ },
414
+ institution: {
415
+ id: '62d27d4b277dab3c1342126e',
416
+ name: 'Shire Bank Credit Card',
417
+ logo: 'https://tinyurl.com/shire-bank-logo',
418
+ },
419
+ createdAt: '2023-05-03T11:25:02.401Z',
420
+ deliveryMethods: ['STANDARD'],
646
421
  },
647
- createdAt: '2023-05-03T11:25:02.401Z',
648
- deliveryMethods: ['STANDARD'],
649
- },
650
422
  ]
651
423
  ```
652
424
 
653
- #### Add US bill account
425
+ #### Create
654
426
 
655
- Currently, Spritz allows the addition of US bill accounts only. The process involves identifying the institution managing the bill and inputting the bill's account number. Here's a guide on how to add a bill for a user:
427
+ Adding a bill requires the institution ID and the account number:
656
428
 
657
429
  ```typescript
658
430
  import { BillType } from '@spritz-finance/api-client'
659
431
 
660
432
  const institutions = await client.institution.popularUSBillInstitutions(BillType.CreditCard)
661
- const billInstitution = institutions[0]
662
- const accountNumber = '12345678913213'
433
+ const bill = await client.bill.create(institutions[0].id, '12345678913213', BillType.CreditCard)
434
+ ```
663
435
 
664
- const bill = await client.bill.create(billInstitution.id, accountNumber, BillType.CreditCard)
436
+ #### Finding Bill Institutions
437
+
438
+ ```typescript
439
+ // Popular institutions (optionally filtered by bill type)
440
+ const popular = await client.institution.popularUSBillInstitutions()
441
+ const mortgages = await client.institution.popularUSBillInstitutions(BillType.Mortgage)
442
+
443
+ // Search by name
444
+ const results = await client.institution.searchUSBillInstitutions('american express')
445
+ const filtered = await client.institution.searchUSBillInstitutions(
446
+ 'american express',
447
+ BillType.CreditCard
448
+ )
665
449
  ```
666
450
 
667
- ### Virtual Card
451
+ ### Virtual Cards
668
452
 
669
- Spritz offers the ability to create virtual cards that users can fund using cryptocurrency. These virtual cards represent an alternative payment account offered by Spritz. To effectively interact with the Virtual Card feature, use the API endpoints detailed below.
453
+ Virtual cards are crypto-funded payment cards.
670
454
 
671
- #### Fetch a users virtual card
455
+ #### Fetch
672
456
 
673
- The fetch endpoint returns an object containing details associated with the virtual card. Importantly, this object excludes sensitive card information such as the card number and the CVV.
457
+ Returns card details excluding sensitive fields (card number, CVV):
674
458
 
675
459
  ```typescript
676
460
  const virtualCard = await client.virtualCard.fetch()
677
461
  ```
678
462
 
679
463
  ```typescript
680
- const virtualCard = {
464
+ // Example response
465
+ {
681
466
  id: '62d17d3b377dab6c1342136e',
682
467
  type: 'VirtualCard',
683
468
  virtualCardType: 'USVirtualDebitCard',
@@ -703,7 +488,7 @@ const virtualCard = {
703
488
  }
704
489
  ```
705
490
 
706
- #### Create a US virtual debit card
491
+ #### Create
707
492
 
708
493
  ```typescript
709
494
  import { VirtualCardType } from '@spritz-finance/api-client'
@@ -711,189 +496,71 @@ import { VirtualCardType } from '@spritz-finance/api-client'
711
496
  const virtualCard = await client.virtualCard.create(VirtualCardType.USVirtualDebitCard)
712
497
  ```
713
498
 
714
- #### Displaying sensitive card details
715
-
716
- To show the sensitive card details that users require for payment transactions, you must integrate our dedicated drop-in widget. This widget securely renders card details. Use the renderSecret, obtained from the standard fetch card endpoint, in conjunction with the user's API key.
499
+ #### Displaying Sensitive Card Details
717
500
 
718
- We currently support and maintain the following packages for the card rendering process:
501
+ To render the full card number and CVV, use the `renderSecret` from the fetch response with one of the Spritz secure element libraries:
719
502
 
720
- - [React Library](https://www.npmjs.com/package/@spritz-finance/react-secure-elements)
721
- - [React Native Library](https://www.npmjs.com/package/@spritz-finance/react-native-secure-elements)
503
+ - [React](https://www.npmjs.com/package/@spritz-finance/react-secure-elements)
504
+ - [React Native](https://www.npmjs.com/package/@spritz-finance/react-native-secure-elements)
722
505
 
723
- ## Address Book
506
+ ### Address Book
724
507
 
725
- Each account created in Spritz is allocated a unique on-chain payment address for each network. Tokens transferred to this address will be picked up and credited to the account. A list of the addresses, one per network, is available under the `paymentAddresses` property. Refer to the Spritz UI for which tokens are accepted for these addresses -- generally, we accept at least USDC and USDT on all networks which we integrate with.
508
+ Each account is allocated a unique on-chain payment address per network. Tokens sent to these addresses are automatically credited to the account. Accepted tokens vary by network generally USDC and USDT at minimum.
726
509
 
727
510
  ```typescript
728
- [
729
- {
730
- id: '62d17d3b377dab6c1342136e',
731
- name: 'Precious Credit Card',
732
- type: 'Bill',
733
- billType: 'CreditCard',
734
- ...
735
- paymentAddresses: [
736
- {
737
- network: 'ethereum',
738
- address: '0xc0ffee254729296a45a3885639AC7E10F9d54979',
739
- },
740
- {
741
- network: 'polygon',
742
- address: '0xc0ffee254729296a45a3885639AC7E10F9d54979',
743
- },
744
- ],
745
- },
746
- ]
747
- ```
748
-
749
- ## Account Management
750
-
751
- ### Renaming accounts
752
-
753
- #### Rename a bank account
754
-
755
- You can conveniently change the display name of a bank account using the following endpoint. The first argument specifies the ID of the bank account, while the second argument represents the desired new name for the account.
756
-
757
- ```typescript
758
- const updateAccount = await client.bankAccount.rename('62d17d3b377dab6c1342136e', 'My new account')
759
- ```
760
-
761
- #### Rename a debit card
762
-
763
- You can change the display name of a debit card:
764
-
765
- ```typescript
766
- const updatedCard = await client.debitCard.rename(
767
- '62d17d3b377dab6c1342136e',
768
- 'My primary debit card'
769
- )
770
- ```
771
-
772
- #### Rename a bill
773
-
774
- You can conveniently change the display name of a bill using the following endpoint. The first argument specifies the ID of the bill, while the second argument represents the desired new name for the account.
775
-
776
- ```typescript
777
- const updateAccount = await client.bill.rename('62d17d3b377dab6c1342136e', 'My first credit card')
511
+ // Included in account responses
512
+ {
513
+ paymentAddresses: [
514
+ { network: 'ethereum', address: '0xc0ffee254729296a45a3885639AC7E10F9d54979' },
515
+ { network: 'polygon', address: '0xc0ffee254729296a45a3885639AC7E10F9d54979' },
516
+ ],
517
+ }
778
518
  ```
779
519
 
780
- ### Deleting accounts
781
-
782
- #### Delete a bank account
783
-
784
- To remove a bank account from a user's account, you can use the following endpoint. You only need to specify the ID of the bank account that you want to delete as an argument.
520
+ ### Renaming Accounts
785
521
 
786
522
  ```typescript
787
- await client.bankAccount.delete('62d17d3b377dab6c1342136e')
523
+ await client.bankAccount.rename('account-id', 'New Name')
524
+ await client.debitCard.rename('card-id', 'New Name')
525
+ await client.bill.rename('bill-id', 'New Name')
788
526
  ```
789
527
 
790
- #### Delete a debit card
791
-
792
- To remove a debit card from a user's account:
528
+ ### Deleting Accounts
793
529
 
794
530
  ```typescript
795
- await client.debitCard.delete('62d17d3b377dab6c1342136e')
531
+ await client.bankAccount.delete('account-id')
532
+ await client.debitCard.delete('card-id')
533
+ await client.bill.delete('bill-id')
796
534
  ```
797
535
 
798
- #### Delete a bill
536
+ ## Payments (Off-ramp)
799
537
 
800
- To remove a bill from a user's account, you can use the following endpoint. You only need to specify the ID of the bill that you want to delete as an argument.
538
+ ### Payment Flow
801
539
 
802
- ```typescript
803
- await client.bill.delete('62d17d3b377dab6c1342136e')
804
- ```
805
-
806
- ## Bill Institutions
807
-
808
- When adding a new bill for a user, we need to provide a reference to the institution who holds the account for the user. As an example, if a user wanted to add their Chase Visa Credit Card to their Spritz account, the Institution of the account would be `Chase Credit Cards` and then the account number provided would be the 16-digit card number for their credit card.
540
+ 1. **Select an account** — choose the bank account, debit card, or bill to pay.
541
+ 2. **Create a payment request** — specify amount, account ID, and blockchain network.
542
+ 3. **Get transaction data** — call `getWeb3PaymentParams` (EVM) or `getSolanaPaymentParams` (Solana).
543
+ 4. **Execute the blockchain transaction** — sign and submit from the user's wallet.
544
+ 5. **Check payment status** — query the resulting fiat payment.
809
545
 
810
- Spritz exposes several endpoints to help users find the Institutions of their bill accounts.
546
+ > Your application needs a connection to the user's wallet to sign transactions. If you don't have one, consider [Web3Modal](https://github.com/WalletConnect/web3modal) or [Web3-Onboard](https://onboard.blocknative.com/docs/overview/introduction#features).
811
547
 
812
- ### Fetching popular bill institutions
548
+ ### Creating a Payment Request
813
549
 
814
550
  ```typescript
815
- const popularInstitutions = await client.institution.popularUSBillInstitutions()
816
-
817
- // Optionally filter by a specific bill type
818
- const popularInstitutions = await client.institution.popularUSBillInstitutions(BillType.Mortgage)
819
- ```
820
-
821
- ### Searching for bill institutions by name
551
+ import { PaymentNetwork, AmountMode } from '@spritz-finance/api-client'
822
552
 
823
- ```typescript
824
- const institutions = await client.institution.searchUSBillInstitutions('american express')
825
-
826
- // Optionally filter by a specific bill type
827
- const institutions = await client.institution.searchUSBillInstitutions(
828
- 'american express',
829
- BillType.CreditCard
830
- )
831
- ```
832
-
833
- ## Payment Requests
834
-
835
- A payment request refers to the intent to initiate a payment to a specific account. Once a payment request is created, a blockchain transaction is required to fulfill it. After the blockchain transaction settles, the payment request is completed, and a fiat payment is issued to the designated account.
836
-
837
- ### Create a payment request
838
-
839
- To initiate a payment request for a specific account, you can use the following functionality. The required inputs for creating a payment request include the ID of the account to be paid, the amount of the fiat payment in USD, and the network on which the blockchain transaction will settle.
840
-
841
- #### Amount Mode
842
-
843
- When creating a payment request, you can specify how the amount should be calculated using the `amountMode` parameter:
844
-
845
- - **`TOTAL_AMOUNT`**: The payer covers the entire amount including fees. The specified amount is the total that will be deducted from the payer's wallet.
846
- - **`AMOUNT_RECEIVED`** (default): The payment that arrives in the bank account excludes fees. The specified amount is what the recipient will receive, and fees are added on top.
847
-
848
- This allows you to control whether fees are included in or added to the specified amount.
849
-
850
- #### Fee Subsidies (Integrator Feature)
851
-
852
- Integrators can subsidize transaction fees on behalf of their users. When enabled, you can cover part or all of the transaction fees, which Spritz will invoice to you separately.
853
-
854
- > **Note**: Fee subsidies are a gated feature and must be enabled by the Spritz team before use. Contact Spritz to request access.
855
-
856
- **Parameters:**
857
-
858
- - **`feeSubsidyPercentage`** (string): The percentage of the transaction fee you want to subsidize for the user. For example, `"100"` covers the entire fee, `"50"` covers half.
859
- - **`maxFeeSubsidyAmount`** (string, optional): The maximum dollar amount you're willing to subsidize per transaction. If the fee exceeds this cap, the user pays the difference.
860
-
861
- **How it works:**
862
-
863
- 1. Set `feeSubsidyPercentage` to define what portion of fees you'll cover
864
- 2. Optionally set `maxFeeSubsidyAmount` to cap your exposure
865
- 3. Spritz invoices you separately for the subsidized amounts
866
- 4. Users see reduced or zero fees on their transactions
867
-
868
- **Example:**
869
-
870
- ```typescript
871
- // Cover 100% of fees up to $5 per transaction
872
553
  const paymentRequest = await client.paymentRequest.create({
873
- amount: 100,
874
- accountId: account.id,
875
- network: PaymentNetwork.Ethereum,
876
- feeSubsidyPercentage: '100',
877
- maxFeeSubsidyAmount: '5',
554
+ amount: 100,
555
+ accountId: account.id,
556
+ network: PaymentNetwork.Ethereum,
557
+ deliveryMethod: 'INSTANT', // optional
558
+ amountMode: AmountMode.TOTAL_AMOUNT, // optional, defaults to AMOUNT_RECEIVED
878
559
  })
879
-
880
- // If transaction fee is $3: integrator pays $3, user pays $0
881
- // If transaction fee is $8: integrator pays $5, user pays $3
882
560
  ```
883
561
 
884
562
  ```typescript
885
- import {PaymentNetwork, AmountMode} from '@spritz-finance/api-client';
886
-
887
- const paymentRequest = await client.paymentRequest.create({
888
- amount: 100,
889
- accountId: account.id,
890
- network: PaymentNetwork.Ethereum,
891
- deliveryMethod: 'INSTANT',
892
- amountMode: AmountMode.TOTAL_AMOUNT // Optional, defaults to AMOUNT_RECEIVED
893
- });
894
-
895
563
  // Example response
896
-
897
564
  {
898
565
  id: '645399c8c1ac408007b12273',
899
566
  userId: '63d12d3B577fab6c6382136e',
@@ -903,108 +570,98 @@ const paymentRequest = await client.paymentRequest.create({
903
570
  feeAmount: 0,
904
571
  amountDue: 100,
905
572
  network: 'ethereum',
906
- createdAt: '2023-05-04T11:40:56.488Z'
573
+ createdAt: '2023-05-04T11:40:56.488Z',
907
574
  }
908
575
  ```
909
576
 
910
- ### Fulfil a payment request (EVM transactions)
577
+ #### Amount Mode
911
578
 
912
- After creating a payment request, you must issue a blockchain transaction to settle the payment request. For EVM compatible networks, this involves interacting with the SpritzPay smart contract (see: [SpritzPay deployments](https://docs.spritz.finance/docs/deployment-addresses)).
579
+ - **`AMOUNT_RECEIVED`** (default) the recipient receives the specified amount; fees are added on top.
580
+ - **`TOTAL_AMOUNT`** — the specified amount includes fees; the recipient receives less.
913
581
 
914
- To obtain the data needed for the transaction, you can use the following endpoint.
582
+ #### Fee Subsidies
915
583
 
916
- ```typescript
917
- import {PaymentNetwork} from '@spritz-finance/api-client';
584
+ Integrators can subsidize transaction fees on behalf of users. This is a gated feature — contact Spritz to enable it.
918
585
 
586
+ ```typescript
919
587
  const paymentRequest = await client.paymentRequest.create({
920
- amount: 100,
921
- accountId: account.id,
922
- network: PaymentNetwork.Ethereum,
923
- });
588
+ amount: 100,
589
+ accountId: account.id,
590
+ network: PaymentNetwork.Ethereum,
591
+ feeSubsidyPercentage: '100', // percentage of fee to cover
592
+ maxFeeSubsidyAmount: '5', // cap per transaction in USD
593
+ })
594
+
595
+ // Fee = $3 → integrator pays $3, user pays $0
596
+ // Fee = $8 → integrator pays $5, user pays $3
597
+ ```
598
+
599
+ Subsidized amounts are invoiced to the integrator separately.
924
600
 
601
+ ### Fulfilling a Payment — EVM
602
+
603
+ For EVM networks, you interact with the SpritzPay smart contract ([deployment addresses](https://docs.spritz.finance/docs/deployment-addresses)):
604
+
605
+ ```typescript
925
606
  const transactionData = await client.paymentRequest.getWeb3PaymentParams({
926
- paymentRequest,
927
- paymentTokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC on mainnet
607
+ paymentRequest,
608
+ paymentTokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC
928
609
  })
929
610
 
930
611
  // Example response
931
-
932
612
  {
933
613
  contractAddress: '0xbF7Abc15f00a8C2d6b13A952c58d12b7c194A8D0',
934
614
  method: 'payWithToken',
935
- calldata: '0xd71d9632000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000064539a31c1ac408007b12277',
615
+ calldata: '0xd71d9632...',
936
616
  value: null,
937
617
  requiredTokenInput: '100000000',
938
618
  }
939
619
  ```
940
620
 
941
- The contract address (to), calldata (data), and value are the primary components used to execute the blockchain transaction. You can use the `requiredTokenInput` to verify that the user's wallet has sufficient funds to complete the payment before initiating the transaction.
942
-
943
- ### Fulfil a payment request (Solana transactions)
621
+ Use `contractAddress` as `to`, `calldata` as `data`, and `value` to build the transaction. Check `requiredTokenInput` against the user's balance before submitting.
944
622
 
945
- For Solana payments, you need to obtain a versioned transaction that can be signed and submitted to the Solana network.
623
+ ### Fulfilling a Payment Solana
946
624
 
947
625
  ```typescript
948
- import {PaymentNetwork} from '@spritz-finance/api-client';
949
-
950
- const paymentRequest = await client.paymentRequest.create({
951
- amount: 100,
952
- accountId: account.id,
953
- network: PaymentNetwork.Solana,
954
- });
955
-
956
626
  const transactionData = await client.paymentRequest.getSolanaPaymentParams({
957
- paymentRequest,
958
- paymentTokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC on Solana
959
- signer: 'YourSolanaWalletAddress...',
627
+ paymentRequest,
628
+ paymentTokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
629
+ signer: 'YourSolanaWalletAddress',
960
630
  })
961
631
 
962
632
  // Example response
963
-
964
633
  {
965
- versionedTransaction: VersionedTransaction, // Deserialized transaction ready to sign
966
- transactionSerialized: 'base64EncodedTransaction...' // Base64 encoded transaction
634
+ versionedTransaction: VersionedTransaction, // ready to sign
635
+ transactionSerialized: 'base64...', // base64-encoded alternative
967
636
  }
968
637
  ```
969
638
 
970
- The `versionedTransaction` is a deserialized Solana transaction that can be signed with your wallet and submitted to the network. The `transactionSerialized` contains the same transaction in base64 encoded format if needed for your implementation.
971
-
972
- ### Transaction fees
639
+ ### Transaction Fees
973
640
 
974
- Transaction fees are applied once the monthly transaction volume exceeds $100. To determine the fee amount for a specific payment value, you can use the following endpoint.
641
+ Fees apply once monthly volume exceeds $100. To check the fee for a given amount:
975
642
 
976
643
  ```typescript
977
- const fees = await client.paymentRequest.transactionPrice(101)
978
-
979
- // Example response
980
- 0.01
644
+ const fee = await client.paymentRequest.transactionPrice(101)
645
+ // Returns: 0.01
981
646
  ```
982
647
 
983
- ## Payments
984
-
985
- Payments represent a fiat payment that has been issued to the account. Once the status of the Payment Request has moved to `Confirmed` then the Payment will be created.
986
-
987
- ### Transaction Details
988
-
989
- Payments now include transaction details about the blockchain transaction that fulfilled the payment. When a payment is completed, the `transaction` field contains:
648
+ ### Retrieving Payments
990
649
 
991
- - **hash**: The blockchain transaction hash
992
- - **from**: The wallet address that sent the payment
993
- - **asset**: The token contract address used for payment
994
- - **value**: The amount transferred (in the token's smallest unit)
995
- - **network**: The blockchain network used (e.g., 'ethereum', 'polygon', etc.)
650
+ Payments are created once a payment request reaches `Confirmed` status.
996
651
 
997
- This allows you to track the on-chain transaction that corresponds to each fiat payment.
652
+ ```typescript
653
+ // By payment ID
654
+ const payment = await client.payment.fetchById('6368e3a3ec516e9572bbd23b')
998
655
 
999
- ### Retrieve a payment by ID
656
+ // By payment request ID
657
+ const payment = await client.payment.getForPaymentRequest(paymentRequest.id)
1000
658
 
1001
- You can fetch a payment directly by its ID:
659
+ // All payments for an account
660
+ const payments = await client.payment.listForAccount(account.id)
661
+ ```
1002
662
 
1003
663
  ```typescript
1004
- const payment = await client.payment.fetchById('6368e3a3ec516e9572bbd23b');
1005
-
1006
664
  // Example response
1007
-
1008
665
  {
1009
666
  id: '6368e3a3ec516e9572bbd23b',
1010
667
  userId: '63d12d3B577fab6c6382136e',
@@ -1014,513 +671,255 @@ const payment = await client.payment.fetchById('6368e3a3ec516e9572bbd23b');
1014
671
  feeAmount: null,
1015
672
  createdAt: '2022-11-07T10:53:23.998Z',
1016
673
  transaction: {
1017
- hash: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
674
+ hash: '0x1234...abcdef',
1018
675
  from: '0xYourWalletAddress',
1019
676
  asset: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
1020
677
  value: 100000000,
1021
- network: 'ethereum'
1022
- }
678
+ network: 'ethereum',
679
+ },
1023
680
  }
1024
681
  ```
1025
682
 
1026
- ### Retrieve the payment for a payment request
683
+ ### Payment Limits
1027
684
 
1028
685
  ```typescript
1029
- import {PaymentNetwork} from '@spritz-finance/api-client';
1030
-
1031
- const paymentRequest = await client.paymentRequest.create({
1032
- amount: 100,
1033
- accountId: account.id,
1034
- network: PaymentNetwork.Ethereum,
1035
- });
1036
-
1037
- const payment = await client.payment.getForPaymentRequest(paymentRequest.id);
686
+ const limits = await client.payment.getPaymentLimits(account.id)
1038
687
 
1039
688
  // Example response
1040
-
1041
689
  {
1042
- id: '6368e3a3ec516e9572bbd23b',
1043
- userId: '63d12d3B577fab6c6382136e',
1044
- status: 'PENDING',
1045
- accountId: '6322445f10d3f4d19c4d72fe',
1046
- amount: 100,
1047
- feeAmount: null,
1048
- createdAt: '2022-11-07T10:53:23.998Z',
1049
- transaction: null // Will be populated once the payment is fulfilled
690
+ perTransaction: 20000,
691
+ dailyRemainingVolume: 150000,
1050
692
  }
1051
-
1052
693
  ```
1053
694
 
1054
- ### Retrieve all payments for an account
695
+ ## On-ramp
1055
696
 
1056
- ```typescript
1057
- const payments = await client.payment.listForAccount(account.id)
697
+ The on-ramp feature allows users to purchase crypto stablecoins via ACH or wire transfer.
1058
698
 
1059
- // Example response
1060
-
1061
- [
1062
- {
1063
- id: '6368e3a3ec516e9572bbd23b',
1064
- userId: '63d12d3B577fab6c6382136e',
1065
- status: 'COMPLETED',
1066
- accountId: '6322445f10d3f4d19c4d72fe',
1067
- amount: 100,
1068
- feeAmount: null,
1069
- createdAt: '2022-11-07T10:53:23.998Z',
1070
- transaction: {
1071
- __typename: 'BlockchainTransaction',
1072
- hash: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
1073
- from: '0xYourWalletAddress',
1074
- asset: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
1075
- value: 100000000,
1076
- network: 'ethereum'
1077
- }
1078
- },
1079
- ]
1080
- ```
699
+ ### Prerequisites
1081
700
 
1082
- ### Get payment limits for an account
701
+ 1. Complete platform-level KYC (identity verification)
702
+ 2. Accept the third-party on-ramp provider's Terms of Service
703
+ 3. Provider KYC processes automatically after ToS acceptance
1083
704
 
1084
- Retrieve the payment limits for a specific account, including the per-transaction limit and the remaining daily volume.
705
+ ### Checking User Access
1085
706
 
1086
707
  ```typescript
1087
- const limits = await client.payment.getPaymentLimits(account.id)
708
+ const access = await client.user.getUserAccess()
1088
709
 
1089
- // Example response
1090
- {
1091
- perTransaction: 20000,
1092
- dailyRemainingVolume: 150000,
710
+ // Off-ramp capabilities
711
+ if (access.capabilities.offramp.active) {
712
+ console.log('Off-ramp features:', access.capabilities.offramp.features)
713
+ // US: 'us_bank_account', 'us_debit_card'
714
+ // CA: 'ca_bank_account'
1093
715
  }
1094
- ```
1095
-
1096
- ## Onramp Payments
1097
-
1098
- Onramp Payments are orders to buy crypto stablecoins with a bank transfer. Upon creating an onramp payment, you will receive deposit instructions to fulfill that order. When the bank transfer has been received and disbursed, the status of that onramp payment will change.
1099
-
1100
- ### Create onramp payment
1101
716
 
1102
- ```typescript
1103
- const onrampPayment = await client.onrampPayment.create({
1104
- token: 'USDC' // Supported: currently only 'USDC'
1105
- network: 'ethereum' // supported: 'ethereum', 'polygon', 'avalanche'
1106
- amount: 100, // How much token to purchase (100 USDC)
1107
- address: '0xbB76483e33e01315438D8F6CF1Aee9C9b85f433b', // Wallet address to disburse tokens to
1108
- paymentMethod: 'ACH' // 'WIRE' or 'ACH'
1109
- });
1110
-
1111
- // Example response
1112
-
1113
- {
1114
- "id": "653fab35ad263e5ae8b0e605",
1115
- "amount": 100,
1116
- "feeAmount": 1.5,
1117
- "depositInstructions": {
1118
- "amount": 101.5,
1119
- "currency": "USD",
1120
- "bankName": "Bank of Nowhere",
1121
- "bankAddress": "1800 North Pole St., Orlando, FL 32801",
1122
- "bankBeneficiaryName": "Bridge Ventures Inc",
1123
- "bankRoutingNumber": "123456789",
1124
- "bankAccountNumber": "11223344556677",
1125
- "paymentMethod": "WIRE",
1126
- "depositMessage": "BVI72D90851F051F4189",
1127
- },
1128
- "network": "ethereum",
1129
- "token": "USDC",
1130
- "address": "0xbb76483e33e01315438d8f6cf1aee9c9b85f433b",
1131
- "status": "AWAITING_FUNDS",
1132
- "createdAt": "2023-10-30T13:10:13.521Z",
717
+ // On-ramp capabilities
718
+ if (access.capabilities.onramp.active) {
719
+ console.log('On-ramp features:', access.capabilities.onramp.features)
720
+ // May include: 'ach_purchase', 'wire_purchase'
721
+ } else {
722
+ for (const req of access.capabilities.onramp.requirements) {
723
+ console.log(`${req.type}: ${req.description}`)
724
+ }
1133
725
  }
1134
-
1135
-
1136
726
  ```
1137
727
 
1138
- ### Retrieve all onramp payments for an account
728
+ ### Activation Steps
729
+
730
+ #### 1. Complete Platform KYC
1139
731
 
1140
732
  ```typescript
1141
- const payments =
1142
- await client.onrampPayment.list()[
1143
- // Example response
733
+ const access = await client.user.getUserAccess()
1144
734
 
1145
- {
1146
- id: '653fab35ad263e5ae8b0e605',
1147
- amount: 100,
1148
- feeAmount: 1.5,
1149
- depositInstructions: {
1150
- amount: 101.5,
1151
- currency: 'USD',
1152
- bankName: 'Bank of Nowhere',
1153
- bankAddress: '1800 North Pole St., Orlando, FL 32801',
1154
- bankBeneficiaryName: 'Bridge Ventures Inc',
1155
- bankRoutingNumber: '123456789',
1156
- bankAccountNumber: '11223344556677',
1157
- paymentMethod: 'WIRE',
1158
- depositMessage: 'BVI72D90851F051F4189',
1159
- },
1160
- network: 'ethereum',
1161
- token: 'USDC',
1162
- address: '0xbb76483e33e01315438d8f6cf1aee9c9b85f433b',
1163
- status: 'AWAITING_FUNDS',
1164
- createdAt: '2023-10-30T13:10:13.521Z',
735
+ if (!access.kycStatus.verified) {
736
+ if (access.kycRequirement?.actionUrl) {
737
+ console.log('Complete KYC at:', access.kycRequirement.actionUrl)
1165
738
  }
1166
- ]
739
+ if (access.kycRequirement?.status === 'failed' && access.kycRequirement.retryable) {
740
+ await client.user.retryFailedVerification()
741
+ }
742
+ }
1167
743
  ```
1168
744
 
1169
- ## Webhooks
1170
-
1171
- Instead of making a request to get information, webhooks send information to your specified endpoint as soon as an event occurs. Spritz's integration offers a variety of webhook events that can be crucial for maintaining data integrity and responsiveness in applications. Let's dive into how you can best utilize these.
1172
-
1173
- ### Supported webhook events
1174
-
1175
- Spritz currently supports the following webhook events:
1176
-
1177
- #### Account Events
1178
-
1179
- - `account.created`: Triggered when a new account is created.
1180
- - `account.updated`: Triggered when details of an account are updated.
1181
- - `account.deleted`: Triggered when an account is deleted.
1182
-
1183
- #### Payment Events
1184
-
1185
- - `payment.created`: Triggered when a new payment is initiated.
1186
- - `payment.updated`: Triggered when details of a payment are updated.
1187
- - `payment.completed`: Triggered when a payment is successfully completed.
1188
- - `payment.refunded`: Triggered when a payment is refunded.
1189
-
1190
- #### Verification Events
1191
-
1192
- - `verification.status.updated`: Triggered when a user's verification status changes.
1193
-
1194
- These events allow you to respond to changes in the account and payments for a user.
1195
-
1196
- ### Setting up webhooks
1197
-
1198
- To set up a webhook with Spritz, you'll need to provide:
1199
-
1200
- 1. **URL**: The endpoint URL where Spritz will send the webhook data.
1201
- 2. **Events**: The specific events you want to listen for.
745
+ #### 2. Accept Terms of Service
1202
746
 
1203
747
  ```typescript
1204
- const webhook = await client.webhook.create({
1205
- url: 'https://my.webhook.url/spritz',
1206
- events: ['account.created', 'account.updated', 'account.deleted'],
1207
- })
1208
- ```
1209
-
1210
- Upon receiving a webhook, your server will get a payload with the following structure:
748
+ const access = await client.user.getUserAccess()
749
+ const tosRequirement = access.capabilities.onramp.requirements.find(
750
+ (req) => req.type === 'terms_acceptance'
751
+ )
1211
752
 
1212
- ```json
1213
- {
1214
- "userId": "user-id-here",
1215
- "id": "resource-id-here",
1216
- "eventName": "name-of-the-event-here"
753
+ if (tosRequirement?.actionUrl) {
754
+ // Display tosRequirement.actionUrl in a browser tab, iframe, or webview.
755
+ // Listen for the signedAgreementId via postMessage:
756
+ window.addEventListener('message', (event) => {
757
+ if (event.data.signedAgreementId) {
758
+ await client.onramp.acceptTermsOfService(event.data.signedAgreementId)
759
+ }
760
+ })
1217
761
  }
1218
762
  ```
1219
763
 
1220
- ### Managing webhooks
1221
-
1222
- #### List all webhooks
764
+ #### 3. Wait for Provider KYC
1223
765
 
1224
- To retrieve all webhooks configured for your integration:
766
+ Provider KYC runs automatically after ToS acceptance. No action required — monitor the status:
1225
767
 
1226
768
  ```typescript
1227
- const webhooks = await client.webhook.list()
1228
- // Returns an array of webhook configurations
1229
- ```
1230
-
1231
- #### Delete a webhook
1232
-
1233
- To delete a specific webhook by its ID:
769
+ const access = await client.user.getUserAccess()
770
+ const kycReq = access.capabilities.onramp.requirements.find(
771
+ (req) => req.type === 'identity_verification'
772
+ )
1234
773
 
1235
- ```typescript
1236
- const deletedWebhook = await client.webhook.delete('webhook-id-here')
1237
- // Returns the deleted webhook object
774
+ // kycReq is undefined when complete, otherwise check kycReq.status ('pending' | 'failed')
1238
775
  ```
1239
776
 
1240
- ### Webhook security and signing
777
+ Use the `capabilities.updated` webhook event to be notified when the user's capabilities change.
1241
778
 
1242
- Each webhook request is signed using an HMAC SHA256 signature, based on the exact JSON payload sent in the body. This signature is included in the Signature HTTP header of the request.
779
+ ### Virtual Accounts
1243
780
 
1244
- The secret key used to compute the signature is the webhook secret you set when configuring your webhook integration. If you have not set a webhook secret, there will be no Signature header in the webhook request.
1245
-
1246
- You can verify webhook authenticity by computing the HMAC signature and comparing it to the Signature header included in the webhook request.
1247
-
1248
- #### Example: Verifying a webhook signature (Node.js)
781
+ Once on-ramp is active, users can create virtual accounts to receive fiat deposits:
1249
782
 
1250
783
  ```typescript
1251
- import { createHmac } from "crypto";
1252
-
1253
- const signature = createHmac("sha256", <YOUR_WEBHOOK_SECRET>)
1254
- .update(<REQUEST_BODY_AS_JSON_STRING>) // JSON.stringify(payload)
1255
- .digest("hex");
1256
- ```
784
+ import { PaymentNetwork, onrampSupportedTokens } from '@spritz-finance/api-client'
1257
785
 
1258
- Ensure that the computed signature matches the Signature header received in the webhook request before processing the payload.
786
+ // Check supported tokens for a network
787
+ const tokens = onrampSupportedTokens[PaymentNetwork.Ethereum]
788
+ // ['USDC', 'USDT', 'DAI', 'USDP', 'PYUSD']
1259
789
 
1260
- ### Setting webhook secret
790
+ // Create a virtual account
791
+ const virtualAccount = await client.virtualAccounts.create({
792
+ network: PaymentNetwork.Ethereum,
793
+ address: '0xYourEthereumAddress',
794
+ token: 'USDC',
795
+ })
1261
796
 
1262
- To add or update a webhook secret for signing webhook requests:
797
+ // Deposit instructions for funding via ACH/wire
798
+ const { bankName, bankAccountNumber, bankRoutingNumber, bankAddress } =
799
+ virtualAccount.depositInstructions
1263
800
 
1264
- ```typescript
1265
- const result = await client.webhook.updateWebhookSecret('your-webhook-secret-here')
1266
- // Returns: { success: true }
801
+ // List all virtual accounts
802
+ const accounts = await client.virtualAccounts.list()
1267
803
  ```
1268
804
 
1269
- This secret will be used to sign all subsequent webhook requests sent to your endpoint. Always store your webhook secret securely and never expose it in client-side code.
805
+ ### Supported Tokens
1270
806
 
1271
- ## On-ramp Setup Guide
807
+ | Network | Tokens |
808
+ | --------- | ---------------------------- |
809
+ | Ethereum | USDC, USDT, DAI, USDP, PYUSD |
810
+ | Polygon | USDC |
811
+ | Base | USDC |
812
+ | Arbitrum | USDC |
813
+ | Avalanche | USDC |
814
+ | Optimism | USDC |
815
+ | Solana | USDC, PYUSD |
816
+ | Tron | USDT |
1272
817
 
1273
- The on-ramp feature allows users to purchase cryptocurrency using traditional payment methods (ACH, Wire transfers). Before users can on-ramp, they must complete identity verification and accept terms of service.
818
+ ## ACH Onramp (Direct Debit)
1274
819
 
1275
- ### Prerequisites
1276
-
1277
- Before a user can on-ramp, they must:
1278
-
1279
- 1. Complete platform-level KYC (identity verification)
1280
- 2. Accept the third-party on-ramp provider's Terms of Service
1281
- 3. Wait for the provider's KYC to process (happens automatically after accepting ToS)
820
+ ACH onramp lets users convert USD from their bank account into USDC delivered to a Solana wallet. The flow is:
1282
821
 
1283
- ### Checking User Access Capabilities
822
+ 1. **Link bank account** via Plaid → funding source created automatically
823
+ 2. **Bind wallet** — user signs a message proving wallet ownership
824
+ 3. **Create deposit** — user authorizes an ACH debit, USDC released to wallet
1284
825
 
1285
- Use the `getUserAccess()` method to check what features are available to a user and what requirements they need to complete:
826
+ 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)**.
1286
827
 
1287
- ```typescript
1288
- const accessCapabilities = await client.user.getUserAccess()
828
+ A standalone sandbox demo is available at `scripts/sandbox/ach-onramp.html` — open it in a browser to walk through the full flow interactively.
1289
829
 
1290
- // Check if on-ramp is active
1291
- if (accessCapabilities.capabilities.onramp.active) {
1292
- console.log('User can on-ramp!')
1293
- console.log('Available features:', accessCapabilities.capabilities.onramp.features)
1294
- // Features may include: 'ach_purchase', 'wire_purchase'
1295
- } else {
1296
- console.log('User needs to complete requirements:')
1297
- accessCapabilities.capabilities.onramp.requirements.forEach((req) => {
1298
- console.log(`- ${req.type}: ${req.description}`)
1299
- if (req.actionUrl) {
1300
- console.log(` Action URL: ${req.actionUrl}`)
1301
- }
1302
- })
1303
- }
830
+ ## Sandbox
1304
831
 
1305
- // Check if off-ramp is active
1306
- if (accessCapabilities.capabilities.offramp.active) {
1307
- console.log('User can off-ramp!')
1308
- console.log('Available features:', accessCapabilities.capabilities.offramp.features)
1309
- // US users: 'us_bank_account', 'us_debit_card'
1310
- // CA users: 'ca_bank_account'
1311
- }
1312
- ```
832
+ Use `Environment.Sandbox` for development and testing. The sandbox environment is available at `https://sandbox.spritz.finance`.
1313
833
 
1314
- ### Step-by-Step On-ramp Activation
834
+ ### Bypassing KYC
1315
835
 
1316
- #### 1. Check Initial Requirements
836
+ In sandbox, you can skip identity verification to speed up testing:
1317
837
 
1318
838
  ```typescript
1319
- const access = await client.user.getUserAccess()
839
+ // Simulate successful US KYC verification
840
+ await client.sandbox.bypassKyc()
1320
841
 
1321
- // Check platform-level KYC first
1322
- if (!access.kycStatus.verified) {
1323
- // User needs to complete platform KYC
1324
- if (access.kycRequirement) {
1325
- if (access.kycRequirement.actionUrl) {
1326
- // Direct user to complete KYC
1327
- console.log('Complete KYC at:', access.kycRequirement.actionUrl)
1328
- }
1329
- if (access.kycRequirement.status === 'failed' && access.kycRequirement.retryable) {
1330
- // User can retry failed verification
1331
- await client.user.retryFailedVerification()
1332
- }
1333
- }
1334
- }
1335
- ```
1336
-
1337
- #### 2. Accept Terms of Service
842
+ // Simulate KYC for a specific country
843
+ await client.sandbox.bypassKyc({ country: 'CA' })
1338
844
 
1339
- Once platform KYC is complete, the user needs to accept the third-party on-ramp provider's Terms of Service:
845
+ // Simulate a failed KYC check
846
+ await client.sandbox.bypassKyc({ failed: true })
847
+ ```
1340
848
 
1341
- ```typescript
1342
- const access = await client.user.getUserAccess()
849
+ This endpoint returns 403 in production.
1343
850
 
1344
- // Find the Terms of Service requirement
1345
- const tosRequirement = access.capabilities.onramp.requirements.find(
1346
- (req) => req.type === 'terms_acceptance'
1347
- )
851
+ ## Webhooks
1348
852
 
1349
- if (tosRequirement && tosRequirement.actionUrl) {
1350
- // Use this URL to guide the customer towards Terms of Service acceptance
1351
- // You can embed this URL in an iFrame or display in a new browser window
853
+ ### Events
1352
854
 
1353
- // For iFrame and WebView implementations:
1354
- // Listen to the postMessage event for the signedAgreementId
1355
- window.addEventListener('message', (event) => {
1356
- if (event.data.signedAgreementId) {
1357
- // Use the agreement ID to complete acceptance
1358
- await client.onramp.acceptTermsOfService(event.data.signedAgreementId)
1359
- }
1360
- })
855
+ #### Account Events
1361
856
 
1362
- // Direct user to review the terms at tosRequirement.actionUrl
1363
- console.log('Terms of Service URL:', tosRequirement.actionUrl)
1364
- }
1365
- ```
857
+ - `account.created` new account created
858
+ - `account.updated` account details updated
859
+ - `account.deleted` — account deleted
1366
860
 
1367
- #### 3. Provider KYC Processing (Automatic)
861
+ #### Payment Events
1368
862
 
1369
- After accepting the Terms of Service, the third-party provider's KYC process happens automatically in the background. When you accept the ToS, the platform automatically submits the user's existing KYC data to the provider. The integrator doesn't need to take any action - just monitor the status:
863
+ - `payment.created` payment initiated
864
+ - `payment.updated` — payment details updated
865
+ - `payment.completed` — payment completed
866
+ - `payment.refunded` — payment refunded
1370
867
 
1371
- ```typescript
1372
- // After accepting ToS, provider KYC is processed automatically
1373
- // You can monitor the status but no action is required
1374
- const access = await client.user.getUserAccess()
868
+ #### Verification Events
1375
869
 
1376
- // Check provider KYC status (for monitoring only)
1377
- const kycRequirement = access.capabilities.onramp.requirements.find(
1378
- (req) => req.type === 'identity_verification'
1379
- )
870
+ - `verification.status.updated` user verification status changed
1380
871
 
1381
- if (kycRequirement) {
1382
- switch (kycRequirement.status) {
1383
- case 'pending':
1384
- console.log('Provider KYC is being processed automatically')
1385
- break
1386
- case 'failed':
1387
- console.log('Provider KYC failed - user may need to retry')
1388
- break
1389
- default:
1390
- // KYC completed successfully if no requirement exists
1391
- console.log('Provider KYC complete!')
1392
- }
1393
- }
1394
- ```
872
+ #### Capability Events
1395
873
 
1396
- #### 4. Monitor Capability Updates
874
+ - `capabilities.updated` user capabilities changed
1397
875
 
1398
- Use webhooks to be notified when user capabilities change:
876
+ ### Setup
1399
877
 
1400
878
  ```typescript
1401
- // Set up a webhook to monitor capability updates
1402
879
  const webhook = await client.webhook.create({
1403
- url: 'https://your-server.com/webhook',
1404
- events: ['capabilities.updated'],
880
+ url: 'https://my.webhook.url/spritz',
881
+ events: ['account.created', 'account.updated', 'payment.completed'],
1405
882
  })
1406
-
1407
- // In your webhook handler:
1408
- // When you receive a 'capabilities.updated' event, check the user's access again
1409
- const updatedAccess = await client.user.getUserAccess()
1410
- if (updatedAccess.capabilities.onramp.active) {
1411
- console.log('User can now on-ramp!')
1412
- }
1413
883
  ```
1414
884
 
1415
- ### Creating Virtual Accounts
1416
-
1417
- Once on-ramp is active, users can create virtual accounts to receive funds:
885
+ Webhook payloads have the following shape:
1418
886
 
1419
- ```typescript
1420
- import { PaymentNetwork, onrampSupportedTokens } from '@spritz-finance/api-client'
1421
-
1422
- // Check supported tokens for a network
1423
- const supportedTokens = onrampSupportedTokens[PaymentNetwork.Ethereum]
1424
- console.log('Supported tokens on Ethereum:', supportedTokens)
1425
- // Output: ['USDC', 'USDT', 'DAI', 'USDP', 'PYUSD']
1426
-
1427
- // Create a virtual account
1428
- const virtualAccount = await client.virtualAccounts.create({
1429
- network: PaymentNetwork.Ethereum,
1430
- address: '0xYourEthereumAddress',
1431
- token: 'USDC',
1432
- })
1433
-
1434
- // The virtual account includes deposit instructions
1435
- if (virtualAccount.depositInstructions) {
1436
- const instructions = virtualAccount.depositInstructions
1437
- console.log('Bank Name:', instructions.bankName)
1438
- console.log('Account Number:', instructions.bankAccountNumber)
1439
- console.log('Routing Number:', instructions.bankRoutingNumber)
1440
- console.log('Bank Address:', instructions.bankAddress)
1441
- // User sends wire/ACH to these details to fund their account
887
+ ```json
888
+ {
889
+ "userId": "user-id",
890
+ "id": "resource-id",
891
+ "eventName": "event-name"
1442
892
  }
1443
893
  ```
1444
894
 
1445
- ### Listing Virtual Accounts
895
+ ### Management
1446
896
 
1447
897
  ```typescript
1448
- // Get all virtual accounts for the user
1449
- const accounts = await client.virtualAccounts.list()
1450
-
1451
- accounts.forEach((account) => {
1452
- console.log(`Network: ${account.network}`)
1453
- console.log(`Address: ${account.address}`)
1454
- console.log(`Token: ${account.token}`)
1455
- console.log(`Deposited: ${account.deposited}`)
898
+ // List all webhooks
899
+ const webhooks = await client.webhook.list()
1456
900
 
1457
- if (account.microdeposits) {
1458
- console.log('Microdeposits:', account.microdeposits)
1459
- }
1460
- })
901
+ // Delete a webhook
902
+ await client.webhook.delete('webhook-id')
1461
903
  ```
1462
904
 
1463
- ### Supported Token Matrix
1464
-
1465
- The following tokens are supported on each network for virtual accounts:
905
+ ### Security and Signing
1466
906
 
1467
- - **Ethereum**: USDC, USDT, DAI, USDP, PYUSD
1468
- - **Polygon**: USDC
1469
- - **Base**: USDC
1470
- - **Arbitrum**: USDC
1471
- - **Avalanche**: USDC
1472
- - **Optimism**: USDC
1473
- - **Solana**: USDC, PYUSD
1474
- - **Tron**: USDT
907
+ Webhook requests are signed with HMAC SHA256 using your webhook secret. The signature is sent in the `Signature` HTTP header.
1475
908
 
1476
- ### Complete Example
909
+ #### Setting a Webhook Secret
1477
910
 
1478
911
  ```typescript
1479
- import { SpritzApiClient, Environment, PaymentNetwork } from '@spritz-finance/api-client'
1480
-
1481
- const client = SpritzApiClient.initialize({
1482
- environment: Environment.Production,
1483
- integrationKey: 'YOUR_INTEGRATION_KEY',
1484
- })
1485
-
1486
- // Set user API key
1487
- client.setApiKey(userApiKey)
1488
-
1489
- async function setupOnramp() {
1490
- // 1. Check current access
1491
- const access = await client.user.getUserAccess()
1492
-
1493
- if (!access.capabilities.onramp.active) {
1494
- console.log('On-ramp not active. Requirements:')
1495
-
1496
- for (const req of access.capabilities.onramp.requirements) {
1497
- if (req.type === 'terms_acceptance' && req.actionUrl) {
1498
- // Direct user to accept terms
1499
- console.log('Accept terms at:', req.actionUrl)
1500
- // After acceptance, call:
1501
- // await client.onramp.acceptTermsOfService(agreementId)
1502
- }
1503
- }
912
+ await client.webhook.updateWebhookSecret('your-secret')
913
+ ```
1504
914
 
1505
- return false
1506
- }
915
+ #### Verifying Signatures
1507
916
 
1508
- // 2. Create virtual account
1509
- const virtualAccount = await client.virtualAccounts.create({
1510
- network: PaymentNetwork.Ethereum,
1511
- address: '0xUserWalletAddress',
1512
- token: 'USDC',
1513
- })
917
+ ```typescript
918
+ import { createHmac } from 'crypto'
1514
919
 
1515
- console.log('Virtual account created!')
1516
- console.log('Send funds to:', virtualAccount.depositInstructions)
920
+ const expected = createHmac('sha256', WEBHOOK_SECRET).update(JSON.stringify(payload)).digest('hex')
1517
921
 
1518
- return true
922
+ if (expected !== request.headers['signature']) {
923
+ throw new Error('Invalid webhook signature')
1519
924
  }
1520
-
1521
- setupOnramp().then((success) => {
1522
- if (success) {
1523
- console.log('On-ramp setup complete!')
1524
- }
1525
- })
1526
925
  ```