@fystack/sdk 0.1.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/src/api.ts ADDED
@@ -0,0 +1,332 @@
1
+ import fetch from 'cross-fetch'
2
+ import { APIConfig, Environment, createAPI } from './config'
3
+ import { computeHMAC, computeHMACForWebhook } from './utils'
4
+ import {
5
+ APICredentials,
6
+ WebhookEvent,
7
+ SignRequestParams,
8
+ SignResponse,
9
+ SignatureStatusResponse,
10
+ TransactionStatusResponse,
11
+ CreateWalletResponse,
12
+ WalletCreationStatusResponse,
13
+ WalletType,
14
+ WalletAsset,
15
+ AddressType,
16
+ DepositAddressResponse
17
+ } from './types'
18
+ import {
19
+ CreateCheckoutPayload,
20
+ CreateCheckoutPaymentPayload,
21
+ CreateCheckoutPaymentResponse,
22
+ CreateCheckoutResponse,
23
+ GetCheckoutPaymentResponse,
24
+ GetCheckoutResponse
25
+ } from './payment'
26
+
27
+ interface APIResponse {
28
+ data: any
29
+ success: boolean
30
+ message?: string
31
+ }
32
+
33
+ export interface HMACParams {
34
+ method: string
35
+ path: string
36
+ timestamp: string
37
+ body?: string
38
+ }
39
+
40
+ export interface WalletDetail {
41
+ APIKey: string
42
+ WalletID: string
43
+ Name: string
44
+ AddressType: string
45
+ Address: string
46
+ }
47
+
48
+ export interface CreateWalletPayload {
49
+ name: string
50
+ walletType: WalletType
51
+ }
52
+
53
+ export interface PaymentServiceParams {
54
+ apiKey: string
55
+ environment: Environment
56
+ }
57
+
58
+ export enum WalletAddressType {
59
+ Evm = 'evm',
60
+ Sol = 'sol'
61
+ }
62
+
63
+ async function composeAPIHeaders(
64
+ credentials: APICredentials,
65
+ httpMethod: string,
66
+ apiEndpoint: string,
67
+ body: Record<string, any> = {}
68
+ ): Promise<Record<string, string>> {
69
+ if (credentials.apiSecret == '') {
70
+ // If APISecret is not provided, fallback to cookie mode with no headers
71
+ return {}
72
+ }
73
+
74
+ const currentTimestampInSeconds = Math.floor(Date.now() / 1000)
75
+ const url = new URL(apiEndpoint)
76
+ const path = url.pathname // Extract the path
77
+ const params: HMACParams = {
78
+ method: httpMethod,
79
+ path,
80
+ timestamp: String(currentTimestampInSeconds),
81
+ body: Object.keys(body).length ? JSON.stringify(body) : ''
82
+ }
83
+
84
+ const digest = await computeHMAC(credentials.apiSecret, params as Record<string, any>)
85
+
86
+ const headers = {
87
+ 'ACCESS-API-KEY': credentials.apiKey,
88
+ 'ACCESS-TIMESTAMP': String(currentTimestampInSeconds),
89
+ 'ACCESS-SIGN': btoa(digest) // convert to base64
90
+ }
91
+
92
+ return headers
93
+ }
94
+
95
+ export class APIService {
96
+ private credentials!: APICredentials
97
+ public Webhook: WebhookService
98
+ private API: APIConfig
99
+
100
+ constructor(credentials: APICredentials, environment: Environment) {
101
+ this.credentials = credentials
102
+ this.Webhook = new WebhookService(credentials)
103
+ this.API = createAPI(environment)
104
+ }
105
+
106
+ async getWalletDetail(
107
+ addressType: WalletAddressType = WalletAddressType.Evm,
108
+ walletId?: string
109
+ ): Promise<WalletDetail> {
110
+ const endpoint = this.API.endpoints.getWalletDetail(walletId)
111
+ const headers = await composeAPIHeaders(this.credentials, 'GET', endpoint)
112
+ console.info('headers', headers)
113
+ const resp = await get(endpoint + `?address_type=${addressType}`, headers)
114
+ return transformWalletDetail(resp.data)
115
+ }
116
+
117
+ async requestSign(walletId: string, params: SignRequestParams): Promise<SignResponse> {
118
+ const endpoint = this.API.endpoints.requestSign(walletId)
119
+ const headers = await composeAPIHeaders(this.credentials, 'POST', endpoint, params)
120
+ const response = await post(endpoint, params, headers)
121
+ return response.data
122
+ }
123
+
124
+ async getSignStatus(walletId: string, transactionId: string): Promise<SignatureStatusResponse> {
125
+ const endpoint = this.API.endpoints.getSignStatus(walletId, transactionId)
126
+ const headers = await composeAPIHeaders(this.credentials, 'GET', endpoint)
127
+ const response = await get(endpoint, headers)
128
+ return response.data
129
+ }
130
+
131
+ async signTransaction(walletId: string, body: Record<string, any>): Promise<SignResponse> {
132
+ const startTime = Date.now()
133
+ const endpoint = this.API.endpoints.signTransaction(walletId)
134
+ const headers = await composeAPIHeaders(this.credentials, 'POST', endpoint, body)
135
+ const response = await post(endpoint, body, headers)
136
+ const elapsedTime = Date.now() - startTime
137
+
138
+ console.log(`[WalletSDK] Sign transaction completed in ${elapsedTime}ms`)
139
+ console.log('[WalletSDK] Sign transaction response:', response)
140
+
141
+ return response.data
142
+ }
143
+
144
+ async getTransactionStatus(
145
+ walletId: string,
146
+ transactionId: string
147
+ ): Promise<TransactionStatusResponse> {
148
+ const endpoint = this.API.endpoints.getTransactionStatus(walletId, transactionId)
149
+ const headers = await composeAPIHeaders(this.credentials, 'GET', endpoint)
150
+ const response = await get(endpoint, headers)
151
+ return response.data
152
+ }
153
+
154
+ async createWallet(payload: CreateWalletPayload): Promise<CreateWalletResponse> {
155
+ const endpoint = this.API.endpoints.createWallet()
156
+ const transformedPayload = transformCreateWalletPayload(payload)
157
+ const headers = await composeAPIHeaders(this.credentials, 'POST', endpoint, transformedPayload)
158
+ const resp = await post(endpoint, transformedPayload, headers)
159
+ return resp.data
160
+ }
161
+
162
+ async createCheckout(payload: CreateCheckoutPayload): Promise<CreateCheckoutResponse> {
163
+ const endpoint = this.API.endpoints.createCheckout()
164
+ const headers = await composeAPIHeaders(this.credentials, 'POST', endpoint, payload)
165
+ const response = await post(endpoint, payload, headers)
166
+ return response.data
167
+ }
168
+
169
+ async getWalletCreationStatus(walletId: string): Promise<WalletCreationStatusResponse> {
170
+ const endpoint = this.API.endpoints.getWalletCreationStatus(walletId)
171
+ const headers = await composeAPIHeaders(this.credentials, 'GET', endpoint)
172
+ const response = await get(endpoint, headers)
173
+ return response.data
174
+ }
175
+
176
+ async getWalletAssets(walletId: string): Promise<WalletAsset[]> {
177
+ const endpoint = this.API.endpoints.getWalletAssets(walletId)
178
+ const headers = await composeAPIHeaders(this.credentials, 'GET', endpoint)
179
+ const response = await get(endpoint, headers)
180
+ return response.data
181
+ }
182
+
183
+ /**
184
+ * Gets deposit address for a wallet by address type
185
+ * @param walletId The wallet ID
186
+ * @param addressType The type of address (evm, sol)
187
+ * @returns Deposit address response
188
+ */
189
+ async getDepositAddress(
190
+ walletId: string,
191
+ addressType: AddressType
192
+ ): Promise<DepositAddressResponse> {
193
+ const endpoint = this.API.endpoints.getDepositAddress(walletId, addressType)
194
+ const headers = await composeAPIHeaders(this.credentials, 'GET', endpoint)
195
+ const response = await get(endpoint, headers)
196
+ return response.data
197
+ }
198
+ }
199
+
200
+ export class PaymentService {
201
+ private apiKey!: string
202
+ private API: Record<string, any>
203
+
204
+ constructor(params: PaymentServiceParams) {
205
+ this.apiKey = params.apiKey
206
+ this.API = createAPI(params.environment)
207
+ }
208
+
209
+ async createCheckout(payload: CreateCheckoutPayload): Promise<CreateCheckoutResponse> {
210
+ const endpoint = this.API.endpoints.createCheckout()
211
+ const headers = {
212
+ 'ACCESS-API-KEY': this.apiKey
213
+ }
214
+ const response = await post(endpoint, payload, headers)
215
+ return response.data
216
+ }
217
+
218
+ async getCheckout(checkoutId: string): Promise<GetCheckoutResponse> {
219
+ const endpoint = this.API.endpoints.getCheckout(checkoutId)
220
+ const response = await get(endpoint)
221
+ return response.data
222
+ }
223
+
224
+ async createCheckoutPayment(
225
+ checkoutId: string,
226
+ payload: CreateCheckoutPaymentPayload
227
+ ): Promise<CreateCheckoutPaymentResponse> {
228
+ const endpoint = this.API.endpoints.createCheckoutPayment(checkoutId)
229
+ const response = await post(endpoint, payload)
230
+ return response.data
231
+ }
232
+
233
+ async getCheckoutPayment(checkoutId: string): Promise<GetCheckoutPaymentResponse> {
234
+ const endpoint = this.API.endpoints.getCheckoutPayment(checkoutId)
235
+ const response = await get(endpoint)
236
+ return response.data
237
+ }
238
+ }
239
+
240
+ export class WebhookService {
241
+ private credentials!: APICredentials
242
+
243
+ constructor(credentials: APICredentials) {
244
+ this.credentials = credentials
245
+ }
246
+
247
+ // Implement verify webhook here
248
+ async verifyEvent(event: WebhookEvent, signature: string): Promise<boolean> {
249
+ // Recompute HMAC
250
+ const computedHMAC = await computeHMACForWebhook(this.credentials.apiSecret, event)
251
+ const isValid = signature === computedHMAC
252
+ return isValid
253
+ }
254
+ }
255
+
256
+ // Simplified GET request function
257
+ export async function get(
258
+ endpoint: string,
259
+ headers: Record<string, string> = {}
260
+ ): Promise<APIResponse> {
261
+ const request: RequestInit = {
262
+ method: 'GET',
263
+ headers: {
264
+ ...headers
265
+ }
266
+ }
267
+
268
+ // Only include credentials if we're in cookie mode (no API key in headers)
269
+ if (!headers['ACCESS-API-KEY']) {
270
+ request.credentials = 'include'
271
+ }
272
+
273
+ const response = await fetch(`${endpoint}`, request)
274
+ const data = await response.json()
275
+ if (!response.ok || !data.success) {
276
+ console.error('Status Code', response.status)
277
+ throw new Error(data.message || 'Failed to fetch data')
278
+ }
279
+
280
+ return data
281
+ }
282
+
283
+ // Simplified POST request function
284
+ export async function post(
285
+ endpoint: string,
286
+ body: any,
287
+ headers: Record<string, string> = {}
288
+ ): Promise<APIResponse> {
289
+ const request: RequestInit = {
290
+ method: 'POST',
291
+ headers: {
292
+ 'Content-Type': 'application/json',
293
+ ...headers
294
+ },
295
+ body: JSON.stringify(body)
296
+ }
297
+
298
+ // Only include credentials if we're in cookie mode (no API key in headers)
299
+ if (!headers['ACCESS-API-KEY']) {
300
+ request.credentials = 'include'
301
+ }
302
+
303
+ const response = await fetch(`${endpoint}`, request)
304
+ const data = await response.json()
305
+ if (!response.ok || !data.success) {
306
+ console.error('Status Code', response.status)
307
+ throw new Error(data.message || 'Failed to fetch data')
308
+ }
309
+
310
+ return data
311
+ }
312
+
313
+ export function transformWalletDetail(data: Record<string, string>): WalletDetail {
314
+ return {
315
+ APIKey: data?.ap_key,
316
+ WalletID: data?.wallet_id,
317
+ Name: data?.name,
318
+ AddressType: data?.address_type,
319
+ Address: data?.address
320
+ }
321
+ }
322
+
323
+ export function transformCreateWalletPayload(data: CreateWalletPayload) {
324
+ return {
325
+ name: data.name,
326
+ wallet_type: data.walletType
327
+ }
328
+ }
329
+
330
+ ;(BigInt.prototype as any).toJSON = function () {
331
+ return this.toString()
332
+ }
package/src/config.ts ADDED
@@ -0,0 +1,75 @@
1
+ enum Environment {
2
+ Local = 'local',
3
+ Sandbox = 'sandbox',
4
+ Production = 'production'
5
+ }
6
+
7
+ // Define the API routes structure
8
+ export interface APIEndpoints {
9
+ signTransaction: (walletId: string) => string
10
+ requestSign: (walletId: string) => string
11
+ getSignStatus: (walletId: string, transactionId: string) => string
12
+ getTransactionStatus: (walletId: string, transactionId: string) => string
13
+ getWalletDetail: (walletId?: string) => string
14
+ createWallet: () => string
15
+ createCheckout: () => string
16
+ getCheckout: (checkoutId: string) => string
17
+ createCheckoutPayment: (checkoutId: string) => string
18
+ getCheckoutPayment: (checkoutId: string) => string
19
+ getWalletCreationStatus: (walletId: string) => string
20
+ getWalletAssets: (walletId: string) => string
21
+ getDepositAddress: (walletId: string, addressType: string) => string
22
+ }
23
+
24
+ const getBaseURL = (env: Environment): string => {
25
+ switch (env) {
26
+ case Environment.Local:
27
+ return 'http://localhost:8150'
28
+ case Environment.Sandbox:
29
+ return 'https://apex.void.exchange'
30
+ case Environment.Production:
31
+ return 'https://api.fystack.io'
32
+ }
33
+ }
34
+
35
+ export interface APIConfig {
36
+ baseURL: string
37
+ endpoints: APIEndpoints
38
+ }
39
+
40
+ const createAPI = (env: Environment): APIConfig => {
41
+ const baseURL = `${getBaseURL(env)}/api/v1`
42
+
43
+ const withBaseURL = (path: string) => `${baseURL}${path}`
44
+
45
+ return {
46
+ baseURL,
47
+ endpoints: {
48
+ signTransaction: (walletId: string) => withBaseURL(`/web3/transaction/${walletId}/signRaw`),
49
+ getWalletDetail: (walletId?: string) =>
50
+ walletId
51
+ ? withBaseURL(`/web3/wallet-detail/${walletId}`)
52
+ : withBaseURL('/web3/wallet-detail'),
53
+ createWallet: () => withBaseURL('/wallets'),
54
+ createCheckout: () => withBaseURL('/checkouts'),
55
+ getCheckout: (checkoutId: string) => withBaseURL(`/checkouts/${checkoutId}`),
56
+ createCheckoutPayment: (checkoutId: string) =>
57
+ withBaseURL(`/checkouts/${checkoutId}/payment`),
58
+ getCheckoutPayment: (checkoutId: string) => withBaseURL(`/checkouts/payment/${checkoutId}`),
59
+
60
+ // New endpoints for signing and transaction status
61
+ requestSign: (walletId: string) => withBaseURL(`/web3/${walletId}/sign`),
62
+ getSignStatus: (walletId: string, transactionId: string) =>
63
+ withBaseURL(`/web3/${walletId}/sign/${transactionId}`),
64
+ getTransactionStatus: (walletId: string, transactionId: string) =>
65
+ withBaseURL(`/web3/transaction/${walletId}/${transactionId}`),
66
+ getWalletCreationStatus: (walletId: string) =>
67
+ withBaseURL(`/wallets/creation-status/${walletId}`),
68
+ getWalletAssets: (walletId: string) => withBaseURL(`/wallets/${walletId}/assets`),
69
+ getDepositAddress: (walletId: string, addressType: string) =>
70
+ withBaseURL(`/wallets/${walletId}/deposit-address?address_type=${addressType}`)
71
+ }
72
+ }
73
+ }
74
+
75
+ export { Environment, createAPI }
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ export * from './sdk'
2
+ export * from './signer'
3
+ export * from './solanaSigner'
4
+ export * from './api'
5
+ export * from './types'
6
+ export * from './config'
7
+ export * from './utils/statusPoller'
package/src/payment.ts ADDED
@@ -0,0 +1,268 @@
1
+ export interface Network {
2
+ id: string
3
+ created_at: string
4
+ updated_at: string
5
+ name: string
6
+ description: string
7
+ is_evm: boolean
8
+ chain_id: number
9
+ native_currency: string
10
+ is_testnet: boolean
11
+ internal_code: string
12
+ explorer_tx: string
13
+ explorer_address: string
14
+ explorer_token: string
15
+ confirmation_blocks: number
16
+ block_interval_in_seconds: number
17
+ disabled: boolean
18
+ logo_url: string
19
+ }
20
+
21
+ export interface Asset {
22
+ id: string
23
+ created_at: string
24
+ updated_at: string
25
+ name: string
26
+ symbol: string
27
+ decimals: number
28
+ logo_url: string
29
+ is_native: boolean
30
+ address_type: 'evm'
31
+ is_whitelisted: boolean
32
+ address?: string
33
+ network_id: string
34
+ network?: Network
35
+ }
36
+ export interface CreateCheckoutPayload {
37
+ price: string
38
+ currency: string
39
+ supported_assets: string[]
40
+ underpaid_cover_percent?: number
41
+ description?: string
42
+ success_url?: string
43
+ cancel_url?: string
44
+ product_id?: string
45
+ customer_id?: string
46
+ order_id?: string
47
+ enable_localization?: boolean
48
+ destination_wallet_id: string
49
+ from_address?: string
50
+ expiry_duration_seconds: number
51
+ }
52
+
53
+ /**
54
+ * Represents the payload required to create a checkout payment.
55
+ */
56
+ export interface CreateCheckoutPaymentPayload {
57
+ /**
58
+ * Customer's email address.
59
+ *
60
+ * - This field is **optional**.
61
+ * - If provided, it must be a valid email format.
62
+ */
63
+ customer_email?: string
64
+
65
+ /**
66
+ * Unique identifier of the asset used for payment.
67
+ *
68
+ * - This field is **required**.
69
+ * - Must be a **UUIDv4**.
70
+ */
71
+ pay_asset_id: string
72
+ }
73
+
74
+ export interface CreateCheckoutResponse {
75
+ id: string
76
+ created_at: string
77
+ updated_at: string
78
+ currency: string
79
+ product_id: string
80
+ order_id: string
81
+ workspace_id: string
82
+ underpaid_cover_percent: number
83
+ description: string
84
+ price: string
85
+ supported_assets: Array<{
86
+ id: string
87
+ created_at: string
88
+ updated_at: string
89
+ is_native: boolean
90
+ is_whitelisted: boolean
91
+ }>
92
+ customer_id: string
93
+ expiry_duration_seconds: number
94
+ enable_localization: boolean
95
+ success_url: string
96
+ cancel_url: string
97
+ status: string
98
+ outcome: any
99
+ from_address: string
100
+ }
101
+
102
+ export enum PaymentStatus {
103
+ Pending = 'Pending',
104
+ Confirming = 'Confirming',
105
+ Success = 'Success'
106
+ }
107
+
108
+ export interface SupportedAsset {
109
+ id: string
110
+ created_at: string
111
+ updated_at: string
112
+ name: string
113
+ symbol: string
114
+ decimals: number
115
+ logo_url: string
116
+ is_native: boolean
117
+ address_type: 'evm'
118
+ is_whitelisted: boolean
119
+ address?: string
120
+ network_id: string
121
+ network: {
122
+ id: string
123
+ created_at: string
124
+ updated_at: string
125
+ name: string
126
+ description: string
127
+ is_evm: boolean
128
+ chain_id: number
129
+ native_currency: string
130
+ is_testnet: boolean
131
+ internal_code: string
132
+ explorer_tx: string
133
+ explorer_address: string
134
+ explorer_token: string
135
+ confirmation_blocks: number
136
+ block_interval_in_seconds: number
137
+ disabled: boolean
138
+ logo_url: string
139
+ }
140
+ }
141
+
142
+ export interface GetCheckoutResponse {
143
+ id: string
144
+ created_at: string
145
+ updated_at: string
146
+ currency: string
147
+ product_id: string
148
+ order_id: string
149
+ workspace_id: string
150
+ underpaid_cover_percent: number
151
+ description: string
152
+ price: string
153
+ supported_assets: SupportedAsset[]
154
+ customer_id: string
155
+ expiry_duration_seconds: number
156
+ enable_localization: boolean
157
+ success_url: string
158
+ cancel_url: string
159
+ status: PaymentStatus
160
+ outcome?: string | null
161
+ from_address: string
162
+ }
163
+
164
+ /**
165
+ * Represents the response structure for a checkout payment.
166
+ */
167
+ export interface CreateCheckoutPaymentResponse {
168
+ /**
169
+ * Unique identifier of the payment.
170
+ */
171
+ id: string
172
+
173
+ /**
174
+ * Timestamp when the payment was created (ISO 8601 format).
175
+ */
176
+ created_at: string
177
+
178
+ /**
179
+ * Timestamp when the payment was last updated (ISO 8601 format).
180
+ */
181
+ updated_at: string
182
+
183
+ /**
184
+ * Unique identifier of the associated checkout.
185
+ */
186
+ checkout_id: string
187
+
188
+ /**
189
+ * Details of the associated checkout (if available).
190
+ * Can be `null` if no data is linked.
191
+ */
192
+ checkout: any | null // Replace `any` with the actual Checkout type if available
193
+
194
+ /**
195
+ * Customer's email address (if provided).
196
+ * Can be `null` if not available.
197
+ */
198
+ customer_email: string | null
199
+
200
+ /**
201
+ * Unique identifier of the asset used for payment.
202
+ */
203
+ pay_asset_id: string
204
+
205
+ /**
206
+ * Unique identifier of the blockchain network used for the transaction.
207
+ */
208
+ network_id: string
209
+
210
+ /**
211
+ * Details of the associated blockchain network (if available).
212
+ * Can be `null` if no data is linked.
213
+ */
214
+ network: any | null // Replace `any` with the actual Network type if available
215
+
216
+ /**
217
+ * Timestamp when the payment expires (ISO 8601 format).
218
+ */
219
+ expired_at: string
220
+
221
+ /**
222
+ * Blockchain address of the payer (if available).
223
+ * Can be `null` if not yet provided.
224
+ */
225
+ payer_address: string | null
226
+
227
+ /**
228
+ * Hash of the blockchain transaction (if completed).
229
+ * Can be `null` if the transaction has not been broadcasted yet.
230
+ */
231
+ tx_hash: string | null
232
+
233
+ /**
234
+ * The amount of cryptocurrency required for the payment,
235
+ * converted to the selected payment asset.
236
+ */
237
+ converted_crypto_price: string
238
+
239
+ /**
240
+ * The blockchain address where the customer should send the payment.
241
+ */
242
+ deposit_address: string
243
+
244
+ /**
245
+ * QR code (as a string) representing the deposit address for easy payment.
246
+ * Could be an empty string if not generated.
247
+ */
248
+ deposit_qr: string
249
+ }
250
+
251
+ export interface GetCheckoutPaymentResponse {
252
+ id: string
253
+ created_at: string
254
+ updated_at: string
255
+ checkout_id: string
256
+ checkout: GetCheckoutResponse
257
+ customer_email?: string | null
258
+ pay_asset_id: string
259
+ pay_asset: Asset
260
+ network_id: string
261
+ network: Network
262
+ expired_at: string
263
+ payer_address?: string | null
264
+ tx_hash?: string | null
265
+ converted_crypto_price: string
266
+ deposit_address: string
267
+ deposit_qr: string
268
+ }