@rozoai/intent-common 0.0.32-beta.1 → 0.0.32-beta.3

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.
Files changed (52) hide show
  1. package/README.md +134 -0
  2. package/dist/api/base.d.ts +1 -1
  3. package/dist/api/base.js +2 -1
  4. package/dist/api/base.js.map +1 -1
  5. package/dist/api/fee.d.ts +2 -1
  6. package/dist/api/fee.js +5 -1
  7. package/dist/api/fee.js.map +1 -1
  8. package/dist/api/new-payment.d.ts +319 -0
  9. package/dist/api/new-payment.js +140 -0
  10. package/dist/api/new-payment.js.map +1 -0
  11. package/dist/api/payment.d.ts +106 -5
  12. package/dist/api/payment.js +117 -10
  13. package/dist/api/payment.js.map +1 -1
  14. package/dist/bridge.d.ts +84 -46
  15. package/dist/bridge.js +183 -169
  16. package/dist/bridge.js.map +1 -1
  17. package/dist/chain.d.ts +6 -0
  18. package/dist/chain.js +38 -14
  19. package/dist/chain.js.map +1 -1
  20. package/dist/daimoPay.d.ts +9 -9
  21. package/dist/daimoPay.js +5 -0
  22. package/dist/daimoPay.js.map +1 -1
  23. package/dist/index.d.ts +2 -0
  24. package/dist/index.js +2 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/token.d.ts +9 -0
  27. package/dist/token.js +111 -17
  28. package/dist/token.js.map +1 -1
  29. package/dist/validation.d.ts +9 -0
  30. package/dist/validation.js +47 -0
  31. package/dist/validation.js.map +1 -0
  32. package/package.json +4 -2
  33. package/src/api/base.ts +3 -1
  34. package/src/api/fee.ts +8 -2
  35. package/src/api/new-payment.ts +433 -0
  36. package/src/api/payment.ts +172 -13
  37. package/src/bridge.ts +225 -201
  38. package/src/chain.ts +40 -13
  39. package/src/daimoPay.ts +17 -9
  40. package/src/index.ts +2 -0
  41. package/src/token.ts +124 -18
  42. package/src/validation.ts +54 -0
  43. package/test/bridge.test.ts +393 -0
  44. package/dist/chainAddress.d.ts +0 -27
  45. package/dist/chainAddress.js +0 -87
  46. package/dist/chainAddress.js.map +0 -1
  47. package/dist/supportedChain.d.ts +0 -27
  48. package/dist/supportedChain.js +0 -87
  49. package/dist/supportedChain.js.map +0 -1
  50. package/dist/supportedChains.d.ts +0 -27
  51. package/dist/supportedChains.js +0 -87
  52. package/dist/supportedChains.js.map +0 -1
@@ -0,0 +1,433 @@
1
+ import { createPaymentBridgeConfig } from "../bridge";
2
+ import { getChainById } from "../chain";
3
+ import { getKnownToken } from "../token";
4
+ import { apiClient, ApiResponse } from "./base";
5
+
6
+ /**
7
+ * FeeType, Fee calculation type:
8
+ * - exactIn (default): Fee deducted from input, recipient receives amount - fee
9
+ * - exactOut: Fee added to input, recipient receives exact amount
10
+ */
11
+ export enum FeeType {
12
+ ExactIn = "exactIn",
13
+ ExactOut = "exactOut",
14
+ }
15
+
16
+ /**
17
+ * PaymentStatus, Payment status
18
+ */
19
+ export enum PaymentStatus {
20
+ PaymentBounced = "payment_bounced",
21
+ PaymentCompleted = "payment_completed",
22
+ PaymentExpired = "payment_expired",
23
+ PaymentPayinCompleted = "payment_payin_completed",
24
+ PaymentPayoutCompleted = "payment_payout_completed",
25
+ PaymentRefunded = "payment_refunded",
26
+ PaymentStarted = "payment_started",
27
+ PaymentUnpaid = "payment_unpaid",
28
+ }
29
+
30
+ /**
31
+ * PaymentErrorCode, Error code (only present when status is payment_bounced)
32
+ */
33
+ export enum PaymentErrorCode {
34
+ AmountTooHigh = "amountTooHigh",
35
+ AmountTooLow = "amountTooLow",
36
+ ChainUnavailable = "chainUnavailable",
37
+ InsufficientLiquidity = "insufficientLiquidity",
38
+ InvalidRecipient = "invalidRecipient",
39
+ MissingTrustline = "missingTrustline",
40
+ NetworkError = "networkError",
41
+ ProviderError = "providerError",
42
+ ServiceMaintenance = "serviceMaintenance",
43
+ }
44
+
45
+ /**
46
+ * DestinationRequest
47
+ */
48
+ export interface DestinationRequest {
49
+ /**
50
+ * Receive amount (required for type=exactOut).
51
+ * For exactIn, this field is omitted in request and calculated in response.
52
+ */
53
+ amount?: string;
54
+ chainId: number;
55
+ /**
56
+ * Final recipient's wallet address
57
+ */
58
+ receiverAddress: string;
59
+ /**
60
+ * Memo for Stellar/Solana destinations
61
+ */
62
+ receiverMemo?: string;
63
+ /**
64
+ * Override default token address
65
+ */
66
+ tokenAddress?: string;
67
+ tokenSymbol: string;
68
+ [property: string]: any;
69
+ }
70
+
71
+ /**
72
+ * DisplayInfo
73
+ */
74
+ export interface DisplayInfo {
75
+ /**
76
+ * Display currency
77
+ */
78
+ currency: string;
79
+ /**
80
+ * Detailed description
81
+ */
82
+ description?: string;
83
+ /**
84
+ * Short title
85
+ */
86
+ title: string;
87
+ [property: string]: any;
88
+ }
89
+
90
+ /**
91
+ * SourceRequest
92
+ */
93
+ export interface SourceRequest {
94
+ /**
95
+ * Pay-in amount (required for type=exactIn).
96
+ * For exactOut, this field is omitted in request and calculated in response.
97
+ */
98
+ amount?: string;
99
+ chainId: number;
100
+ /**
101
+ * Override default token address
102
+ */
103
+ tokenAddress?: string;
104
+ tokenSymbol: string;
105
+ [property: string]: any;
106
+ }
107
+
108
+ /**
109
+ * PaymentRequest
110
+ */
111
+ export interface CreatePaymentRequest {
112
+ /**
113
+ * Your application ID
114
+ */
115
+ appId: string;
116
+ destination: DestinationRequest;
117
+ display: DisplayInfo;
118
+ /**
119
+ * Custom metadata (max 4 KB recommended)
120
+ */
121
+ metadata?: { [key: string]: any };
122
+ /**
123
+ * Your order reference ID (for idempotency)
124
+ */
125
+ orderId?: string;
126
+ source: SourceRequest;
127
+ type?: FeeType;
128
+ /**
129
+ * Secret for HMAC-SHA256 signature verification.
130
+ * If not provided, a unique secret is auto-generated.
131
+ * The secret is returned in the response for you to store and use for verification.
132
+ */
133
+ webhookSecret?: string;
134
+ /**
135
+ * URL to receive payment status updates
136
+ */
137
+ webhookUrl?: string;
138
+ [property: string]: any;
139
+ }
140
+
141
+ /**
142
+ * DestinationResponse
143
+ */
144
+ export interface DestinationResponse {
145
+ /**
146
+ * Amount to be sent to recipient
147
+ */
148
+ amount?: string;
149
+ chainId?: number;
150
+ /**
151
+ * Withdrawal confirmation time
152
+ */
153
+ confirmedAt?: Date;
154
+ /**
155
+ * Final recipient's wallet
156
+ */
157
+ receiverAddress?: string;
158
+ /**
159
+ * Memo for Stellar/Solana
160
+ */
161
+ receiverMemo?: string;
162
+ /**
163
+ * Token contract address
164
+ */
165
+ tokenAddress?: string;
166
+ tokenSymbol?: string;
167
+ /**
168
+ * Withdrawal transaction hash
169
+ */
170
+ txHash?: string;
171
+ [property: string]: any;
172
+ }
173
+
174
+ /**
175
+ * SourceResponse
176
+ */
177
+ export interface SourceResponse {
178
+ /**
179
+ * Amount payer must send
180
+ */
181
+ amount?: string;
182
+ /**
183
+ * Actual amount received
184
+ */
185
+ amountReceived?: string;
186
+ chainId?: number;
187
+ /**
188
+ * Deposit confirmation time
189
+ */
190
+ confirmedAt?: Date;
191
+ /**
192
+ * Fee amount
193
+ */
194
+ fee?: string;
195
+ /**
196
+ * Deposit address (where payer sends funds)
197
+ */
198
+ receiverAddress?: string;
199
+ /**
200
+ * Memo for Stellar/Solana deposits
201
+ */
202
+ receiverMemo?: string;
203
+ /**
204
+ * Payer's wallet address (populated after deposit)
205
+ */
206
+ senderAddress?: string;
207
+ /**
208
+ * Token contract address
209
+ */
210
+ tokenAddress?: string;
211
+ tokenSymbol?: string;
212
+ /**
213
+ * Deposit transaction hash
214
+ */
215
+ txHash?: string;
216
+ [property: string]: any;
217
+ }
218
+
219
+ /**
220
+ * PaymentResponse
221
+ */
222
+ export interface PaymentResponse {
223
+ /**
224
+ * Your application ID
225
+ */
226
+ appId: string;
227
+ /**
228
+ * ISO 8601 timestamp
229
+ */
230
+ createdAt: Date;
231
+ destination: DestinationResponse;
232
+ display: DisplayInfo;
233
+ errorCode: PaymentErrorCode | null;
234
+ /**
235
+ * ISO 8601 timestamp (when payment expires)
236
+ */
237
+ expiresAt: Date;
238
+ /**
239
+ * Payment ID
240
+ */
241
+ id: string;
242
+ metadata: { [key: string]: any } | null;
243
+ /**
244
+ * Your order reference ID
245
+ */
246
+ orderId: string | null;
247
+ source: SourceResponse;
248
+ status: PaymentStatus;
249
+ type: FeeType;
250
+ /**
251
+ * ISO 8601 timestamp
252
+ */
253
+ updatedAt: Date;
254
+ /**
255
+ * Secret for webhook signature verification.
256
+ * Only present when webhookUrl was provided in the request.
257
+ * Store this securely to verify incoming webhook signatures.
258
+ */
259
+ webhookSecret: string | null;
260
+ [property: string]: any;
261
+ }
262
+
263
+ /**
264
+ * Parameters for creating a new payment using the new backend interface
265
+ */
266
+ export interface CreateNewPaymentParams {
267
+ /** App ID for authentication */
268
+ appId: string;
269
+ // Destination (where funds will be received)
270
+ /** Destination chain ID (e.g., 8453 for Base, 900 for Solana, 1500 for Stellar) */
271
+ toChain: number;
272
+ /** Destination token address */
273
+ toToken: string;
274
+ /** Destination address - Can be EVM, Solana, or Stellar address */
275
+ toAddress: string;
276
+
277
+ // Preferred payment method (what user will pay with)
278
+ /** Chain ID where user will pay from (e.g., 137 for Polygon, 8453 for Base) */
279
+ preferredChain: number;
280
+ /** Token address user will pay with */
281
+ preferredTokenAddress: string;
282
+
283
+ // Payment details
284
+ /** Amount in human-readable units (e.g., "1" for 1 USDC, "0.5" for half a USDC) */
285
+ toUnits?: string;
286
+
287
+ // Optional fields
288
+ /** Additional metadata to include */
289
+ metadata?: Record<string, unknown>;
290
+ /** Display title for the payment */
291
+ title?: string;
292
+ /** Display description for the payment */
293
+ description?: string;
294
+ /** Order reference ID (for idempotency) */
295
+ orderId?: string;
296
+ /** Fee calculation type (exactIn or exactOut) */
297
+ type?: FeeType;
298
+ /** Webhook URL to receive payment status updates */
299
+ webhookUrl?: string;
300
+ /** Secret for HMAC-SHA256 signature verification */
301
+ webhookSecret?: string;
302
+ /** Memo for Stellar/Solana destinations */
303
+ receiverMemo?: string;
304
+ }
305
+
306
+ /**
307
+ * Creates a payment using the new backend interface
308
+ *
309
+ * This function creates a payment using the new backend API structure with
310
+ * separate source and destination objects, enum-based chain IDs and token symbols.
311
+ *
312
+ * @param params - Payment creation parameters
313
+ * @returns Promise resolving to the payment response data
314
+ * @throws Error if payment creation fails or required parameters are missing
315
+ *
316
+ * @example
317
+ * ```typescript
318
+ * // Simple same-chain payment
319
+ * const payment = await createNewPayment({
320
+ * toChain: 8453, // Base
321
+ * toToken: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base USDC
322
+ * toAddress: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
323
+ * preferredChain: 8453, // User pays from Base
324
+ * preferredTokenAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base USDC
325
+ * toUnits: "1", // 1 USDC
326
+ * appId: "my-app-id",
327
+ * title: "Payment",
328
+ * });
329
+ * ```
330
+ */
331
+ export async function createNewPayment(
332
+ params: CreateNewPaymentParams
333
+ ): Promise<PaymentResponse> {
334
+ const {
335
+ toChain,
336
+ toToken,
337
+ toAddress,
338
+ preferredChain,
339
+ preferredTokenAddress,
340
+ toUnits,
341
+ appId,
342
+ metadata,
343
+ title,
344
+ description,
345
+ orderId,
346
+ type,
347
+ webhookUrl,
348
+ webhookSecret,
349
+ receiverMemo,
350
+ } = params;
351
+
352
+ // Create payment bridge configuration
353
+ const { preferred, destination } = createPaymentBridgeConfig({
354
+ toChain,
355
+ toToken,
356
+ toAddress,
357
+ toUnits: toUnits ?? "0",
358
+ // Preferred payment method (what user will pay with)
359
+ preferredChain,
360
+ preferredTokenAddress,
361
+ });
362
+
363
+ const sourceChain = getChainById(Number(preferred.preferredChain));
364
+ const sourceToken = getKnownToken(
365
+ Number(preferred.preferredChain),
366
+ preferred.preferredTokenAddress
367
+ );
368
+ const destinationChain = getChainById(Number(destination.chainId));
369
+ const destinationToken = getKnownToken(
370
+ Number(destination.chainId),
371
+ destination.tokenAddress
372
+ );
373
+
374
+ if (!sourceToken || !destinationToken) {
375
+ throw new Error("Source or destination token not found");
376
+ }
377
+
378
+ // Build payment request data matching new backend interface
379
+ const paymentData: CreatePaymentRequest = {
380
+ appId,
381
+ destination: {
382
+ chainId: destinationChain.chainId,
383
+ receiverAddress: destination.destinationAddress ?? toAddress,
384
+ tokenSymbol: destinationToken.symbol,
385
+ amount: destination.amountUnits,
386
+ ...(destination.tokenAddress
387
+ ? { tokenAddress: destination.tokenAddress }
388
+ : {}),
389
+ ...(receiverMemo ? { receiverMemo } : {}),
390
+ },
391
+ source: {
392
+ chainId: sourceChain.chainId,
393
+ tokenSymbol: sourceToken.symbol,
394
+ amount: destination.amountUnits, // Use same amount for source
395
+ ...(preferred.preferredTokenAddress
396
+ ? { tokenAddress: preferred.preferredTokenAddress }
397
+ : {}),
398
+ },
399
+ display: {
400
+ currency: "USD",
401
+ title: title ?? "Payment",
402
+ ...(description ? { description } : {}),
403
+ },
404
+ ...(metadata ? { metadata } : {}),
405
+ ...(orderId ? { orderId } : {}),
406
+ ...(type ? { type } : {}),
407
+ ...(webhookUrl ? { webhookUrl } : {}),
408
+ ...(webhookSecret ? { webhookSecret } : {}),
409
+ };
410
+
411
+ // Create payment via API
412
+ const response = await apiClient.post<PaymentResponse>(
413
+ "/payment-api",
414
+ paymentData
415
+ );
416
+
417
+ if (!response?.data?.id) {
418
+ throw new Error(response?.error?.message ?? "Payment creation failed");
419
+ }
420
+
421
+ return response.data;
422
+ }
423
+
424
+ /**
425
+ * Gets payment details by ID using the new backend API
426
+ * @param paymentId - Payment ID
427
+ * @returns Promise with payment response
428
+ */
429
+ export const getNewPayment = (
430
+ paymentId: string
431
+ ): Promise<ApiResponse<PaymentResponse>> => {
432
+ return apiClient.get<PaymentResponse>(`/payment-api/${paymentId}`);
433
+ };
@@ -1,4 +1,6 @@
1
+ import { createPaymentBridgeConfig } from "../bridge";
1
2
  import { apiClient, ApiResponse } from "./base";
3
+ import { DisplayInfo } from "./new-payment";
2
4
 
3
5
  /**
4
6
  * Payment display information
@@ -48,11 +50,15 @@ export interface PaymentResponseData {
48
50
  id: string;
49
51
  status: "payment_unpaid" | string;
50
52
  createdAt: string;
51
- display: {
52
- intent: string;
53
- currency: string;
54
- paymentValue?: string;
55
- };
53
+ updatedAt: string;
54
+ expiresAt: string;
55
+ display:
56
+ | {
57
+ intent: string;
58
+ currency: string;
59
+ paymentValue?: string;
60
+ }
61
+ | DisplayInfo;
56
62
  source: PaymentSource | null;
57
63
  destination: {
58
64
  destinationAddress: string;
@@ -86,15 +92,33 @@ export interface PaymentResponseData {
86
92
  }
87
93
 
88
94
  /**
89
- * Creates a new payment
90
- * @param paymentData - Payment data to send
91
- * @returns Promise with payment response
95
+ * Simplified interface for creating a payment bridge
92
96
  */
93
- export const createRozoPayment = (
94
- paymentData: PaymentRequestData
95
- ): Promise<ApiResponse<PaymentResponseData>> => {
96
- return apiClient.post<PaymentResponseData>("/payment-api", paymentData);
97
- };
97
+ export interface CreatePaymentBridgeParams {
98
+ /** App ID for authentication */
99
+ appId: string;
100
+ // Destination (where funds will be received)
101
+ /** Destination chain ID (e.g., 8453 for Base, 900 for Solana, 10001 for Stellar) */
102
+ toChain: number;
103
+ /** Destination token address */
104
+ toToken: string;
105
+ /** Destination address - Can be EVM, Solana, or Stellar address */
106
+ toAddress: string;
107
+
108
+ // Preferred payment method (what user will pay with)
109
+ /** Chain ID where user will pay from (e.g., 137 for Polygon, 8453 for Base) */
110
+ preferredChain: number;
111
+ /** Token address user will pay with */
112
+ preferredTokenAddress: string;
113
+
114
+ // Payment details
115
+ /** Amount in human-readable units (e.g., "1" for 1 USDC, "0.5" for half a USDC) */
116
+ toUnits?: string;
117
+
118
+ // Optional metadata
119
+ /** Additional metadata to include */
120
+ metadata?: Record<string, unknown>;
121
+ }
98
122
 
99
123
  /**
100
124
  * Gets payment details by ID
@@ -110,3 +134,138 @@ export const getRozoPayment = (
110
134
  : `payment/id/${paymentId}`;
111
135
  return apiClient.get<PaymentResponseData>(endpoint);
112
136
  };
137
+
138
+ /**
139
+ * Creates a payment bridge configuration and initiates a Rozo payment
140
+ *
141
+ * This function combines the payment bridge configuration logic with payment creation,
142
+ * handling cross-chain payment routing and metadata merging. It provides a simplified
143
+ * API that doesn't require understanding internal types like WalletPaymentOption.
144
+ *
145
+ * @param params - Payment bridge creation parameters
146
+ * @returns Promise resolving to the payment response data
147
+ * @throws Error if payment creation fails or required parameters are missing
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * // Simple same-chain payment
152
+ * const payment = await createPaymentBridge({
153
+ * toChain: 8453, // Base
154
+ * toToken: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base USDC
155
+ * toAddress: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
156
+ * preferredChainId: 8453, // User pays from Base
157
+ * preferredToken: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base USDC
158
+ * toUnits: "1", // 1 USDC
159
+ * appId: "my-app-id",
160
+ * intent: "Pay",
161
+ * });
162
+ * ```
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * // Cross-chain payment: Polygon to Base
167
+ * const payment = await createPaymentBridge({
168
+ * toChain: 8453, // Base (destination)
169
+ * toToken: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base USDC
170
+ * toAddress: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
171
+ * preferredChainId: 137, // Polygon (user pays from)
172
+ * preferredToken: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", // Polygon USDC
173
+ * toUnits: "1",
174
+ * appId: "my-app-id",
175
+ * });
176
+ * ```
177
+ *
178
+ * @example
179
+ * ```typescript
180
+ * // Payment to Solana address (payout → Solana)
181
+ * const payment = await createPaymentBridge({
182
+ * toChain: 900, // Solana
183
+ * toToken: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // Solana USDC
184
+ * toAddress: "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU", // Solana address
185
+ * preferredChainId: 8453, // User pays from Base
186
+ * preferredToken: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base USDC
187
+ * toUnits: "1",
188
+ * appId: "my-app-id",
189
+ * });
190
+ * ```
191
+ *
192
+ * @example
193
+ * ```typescript
194
+ * // Payment paid in from Stellar account
195
+ * const payment = await createPaymentBridge({
196
+ * toChain: 8453, // Payout on Base
197
+ * toToken: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base USDC
198
+ * toAddress: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", // EVM address
199
+ * preferredChainId: 1500, // Pay in from Stellar
200
+ * preferredToken: "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN", // Stellar USDC (token format: code:issuer)
201
+ * toUnits: "10",
202
+ * appId: "my-app-id",
203
+ * });
204
+ * ```
205
+ *
206
+ * @example
207
+ * ```typescript
208
+ * // Payment paying out to Stellar address (payout → Stellar)
209
+ * const payment = await createPaymentBridge({
210
+ * toChain: 1500, // Stellar
211
+ * toToken: "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN", // Stellar USDC (token format: code:issuer)
212
+ * toAddress: "GA5ZSEPRY3STUGUUXZGHV5CDEQ2AJGEAAUUMSZK2QIPICFL2JVP4X6T4", // Stellar address
213
+ * preferredChainId: 8453, // User pays from Base
214
+ * preferredToken: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base USDC
215
+ * toUnits: "1",
216
+ * appId: "my-app-id",
217
+ * });
218
+ * ```
219
+ */
220
+ export async function createRozoPayment(
221
+ params: CreatePaymentBridgeParams
222
+ ): Promise<PaymentResponseData> {
223
+ const {
224
+ toChain,
225
+ toToken,
226
+ toAddress,
227
+ preferredChain,
228
+ preferredTokenAddress,
229
+ toUnits,
230
+ appId,
231
+ metadata,
232
+ } = params;
233
+ // Create payment bridge configuration
234
+ const { preferred, destination, isIntentPayment } = createPaymentBridgeConfig(
235
+ {
236
+ toChain,
237
+ toToken,
238
+ toAddress,
239
+ toUnits: toUnits ?? "0",
240
+ // Preferred payment method (what user will pay with)
241
+ preferredChain,
242
+ preferredTokenAddress,
243
+ }
244
+ );
245
+
246
+ // Build payment request data
247
+ const paymentData: PaymentRequestData = {
248
+ appId,
249
+ display: {
250
+ intent: "",
251
+ paymentValue: String(toUnits ?? ""),
252
+ currency: "USD",
253
+ },
254
+ destination,
255
+ ...preferred,
256
+ ...(metadata ?? {}),
257
+ ...(isIntentPayment ? { intents: true } : {}),
258
+ };
259
+
260
+ // Create payment via API
261
+ const response = await apiClient.post<PaymentResponseData>(
262
+ "/payment-api",
263
+ paymentData
264
+ );
265
+
266
+ if (!response?.data?.id) {
267
+ throw new Error(response?.error?.message ?? "Payment creation failed");
268
+ }
269
+
270
+ return response.data;
271
+ }