@rotateprotocol/sdk 1.0.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.
Files changed (74) hide show
  1. package/README.md +453 -0
  2. package/dist/catalog.d.ts +112 -0
  3. package/dist/catalog.d.ts.map +1 -0
  4. package/dist/catalog.js +210 -0
  5. package/dist/catalog.js.map +1 -0
  6. package/dist/components/CheckoutForm.d.ts +86 -0
  7. package/dist/components/CheckoutForm.d.ts.map +1 -0
  8. package/dist/components/CheckoutForm.js +332 -0
  9. package/dist/components/CheckoutForm.js.map +1 -0
  10. package/dist/components/HostedCheckout.d.ts +57 -0
  11. package/dist/components/HostedCheckout.d.ts.map +1 -0
  12. package/dist/components/HostedCheckout.js +414 -0
  13. package/dist/components/HostedCheckout.js.map +1 -0
  14. package/dist/components/PaymentButton.d.ts +80 -0
  15. package/dist/components/PaymentButton.d.ts.map +1 -0
  16. package/dist/components/PaymentButton.js +210 -0
  17. package/dist/components/PaymentButton.js.map +1 -0
  18. package/dist/components/RotateProvider.d.ts +115 -0
  19. package/dist/components/RotateProvider.d.ts.map +1 -0
  20. package/dist/components/RotateProvider.js +264 -0
  21. package/dist/components/RotateProvider.js.map +1 -0
  22. package/dist/components/index.d.ts +17 -0
  23. package/dist/components/index.d.ts.map +1 -0
  24. package/dist/components/index.js +27 -0
  25. package/dist/components/index.js.map +1 -0
  26. package/dist/embed.d.ts +85 -0
  27. package/dist/embed.d.ts.map +1 -0
  28. package/dist/embed.js +313 -0
  29. package/dist/embed.js.map +1 -0
  30. package/dist/hooks.d.ts +156 -0
  31. package/dist/hooks.d.ts.map +1 -0
  32. package/dist/hooks.js +280 -0
  33. package/dist/hooks.js.map +1 -0
  34. package/dist/idl/rotate_connect.json +2572 -0
  35. package/dist/index.d.ts +505 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +1197 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/marketplace.d.ts +257 -0
  40. package/dist/marketplace.d.ts.map +1 -0
  41. package/dist/marketplace.js +433 -0
  42. package/dist/marketplace.js.map +1 -0
  43. package/dist/platform.d.ts +234 -0
  44. package/dist/platform.d.ts.map +1 -0
  45. package/dist/platform.js +268 -0
  46. package/dist/platform.js.map +1 -0
  47. package/dist/react.d.ts +140 -0
  48. package/dist/react.d.ts.map +1 -0
  49. package/dist/react.js +429 -0
  50. package/dist/react.js.map +1 -0
  51. package/dist/store.d.ts +213 -0
  52. package/dist/store.d.ts.map +1 -0
  53. package/dist/store.js +404 -0
  54. package/dist/store.js.map +1 -0
  55. package/dist/webhooks.d.ts +149 -0
  56. package/dist/webhooks.d.ts.map +1 -0
  57. package/dist/webhooks.js +371 -0
  58. package/dist/webhooks.js.map +1 -0
  59. package/package.json +114 -0
  60. package/src/catalog.ts +299 -0
  61. package/src/components/CheckoutForm.tsx +608 -0
  62. package/src/components/HostedCheckout.tsx +675 -0
  63. package/src/components/PaymentButton.tsx +348 -0
  64. package/src/components/RotateProvider.tsx +370 -0
  65. package/src/components/index.ts +26 -0
  66. package/src/embed.ts +408 -0
  67. package/src/hooks.ts +518 -0
  68. package/src/idl/rotate_connect.json +2572 -0
  69. package/src/index.ts +1538 -0
  70. package/src/marketplace.ts +642 -0
  71. package/src/platform.ts +403 -0
  72. package/src/react.ts +459 -0
  73. package/src/store.ts +577 -0
  74. package/src/webhooks.ts +506 -0
@@ -0,0 +1,403 @@
1
+ /**
2
+ * Rotate Platform Manager — Merchant Onboarding & Platform Administration
3
+ *
4
+ * A high-level SDK layer for platform operators to onboard merchants,
5
+ * manage their accounts, and monitor platform-wide activity.
6
+ *
7
+ * The platform admin wallet is the signer — merchants don't need to
8
+ * interact with the SDK directly. The platform creates merchant accounts
9
+ * on their behalf, and merchants just receive payments to their wallet.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import { RotateSDK, RotatePlatformManager } from '@rotateprotocol/sdk';
14
+ *
15
+ * const sdk = new RotateSDK({ connection, wallet });
16
+ *
17
+ * // Create a platform first (one-time setup)
18
+ * const { platformId } = await sdk.createPlatform({ feeBps: 500, wallet: adminWallet });
19
+ *
20
+ * // Then use the manager for ongoing operations
21
+ * const platform = new RotatePlatformManager(sdk, { platformId });
22
+ *
23
+ * // Onboard a single merchant
24
+ * const merchant = await platform.onboardMerchant({
25
+ * wallet: new PublicKey('merchant-wallet-address'),
26
+ * name: 'Coffee Shop',
27
+ * });
28
+ * console.log(`Merchant created: #${merchant.merchantId}`);
29
+ *
30
+ * // Bulk onboard merchants
31
+ * const results = await platform.onboardMerchants([
32
+ * { wallet: walletA, name: 'Store A' },
33
+ * { wallet: walletB, name: 'Store B' },
34
+ * { wallet: walletC, name: 'Store C' },
35
+ * ]);
36
+ *
37
+ * // Get all merchants on the platform
38
+ * const merchants = await platform.getMerchants();
39
+ *
40
+ * // Get platform stats
41
+ * const stats = await platform.getStats();
42
+ * console.log(`Total merchants: ${stats.merchantCount}`);
43
+ * console.log(`Fee revenue: $${stats.feeRevenue.toFixed(2)}`);
44
+ * ```
45
+ *
46
+ * @packageDocumentation
47
+ */
48
+
49
+ import { PublicKey } from '@solana/web3.js';
50
+ import RotateSDK, {
51
+ PaymentLink,
52
+ Platform,
53
+ Merchant,
54
+ PROTOCOL_FEE_BPS,
55
+ } from './index';
56
+
57
+ // ==================== TYPES ====================
58
+
59
+ /** Configuration for the platform manager */
60
+ export interface PlatformManagerConfig {
61
+ /** Your platform ID (from createPlatform) */
62
+ platformId: number;
63
+ }
64
+
65
+ /** Input for onboarding a single merchant */
66
+ export interface OnboardMerchantInput {
67
+ /** The merchant's Solana wallet address (where they receive payments) */
68
+ wallet: PublicKey;
69
+ /** Optional display name for the merchant (stored locally, not on-chain) */
70
+ name?: string;
71
+ /** Optional metadata (e.g. email, store URL, category) */
72
+ metadata?: Record<string, string>;
73
+ }
74
+
75
+ /** Result of onboarding a merchant */
76
+ export interface OnboardMerchantResult {
77
+ /** The assigned on-chain merchant ID */
78
+ merchantId: number;
79
+ /** Transaction signature */
80
+ tx: string;
81
+ /** The merchant's wallet address */
82
+ wallet: PublicKey;
83
+ /** Display name (if provided) */
84
+ name?: string;
85
+ /** Metadata (if provided) */
86
+ metadata?: Record<string, string>;
87
+ }
88
+
89
+ /** Result of a bulk onboarding operation */
90
+ export interface BulkOnboardResult {
91
+ /** Successfully onboarded merchants */
92
+ succeeded: OnboardMerchantResult[];
93
+ /** Failed onboardings */
94
+ failed: Array<{
95
+ input: OnboardMerchantInput;
96
+ error: string;
97
+ }>;
98
+ }
99
+
100
+ /** Merchant info with on-chain and local data */
101
+ export interface MerchantInfo {
102
+ /** On-chain merchant ID */
103
+ merchantId: number;
104
+ /** Platform ID this merchant belongs to */
105
+ platformId: number;
106
+ /** Merchant wallet address */
107
+ wallet: PublicKey;
108
+ /** Whether the merchant is active */
109
+ active: boolean;
110
+ /** Display name (local only) */
111
+ name?: string;
112
+ /** Metadata (local only) */
113
+ metadata?: Record<string, string>;
114
+ }
115
+
116
+ /** Platform-wide statistics */
117
+ export interface PlatformStats {
118
+ /** Platform ID */
119
+ platformId: number;
120
+ /** Platform fee in basis points */
121
+ feeBps: number;
122
+ /** Platform fee as percentage string (e.g. "5.0%") */
123
+ feePercent: string;
124
+ /** Total merchants on the platform */
125
+ merchantCount: number;
126
+ /** Platform admin wallet */
127
+ adminWallet: PublicKey;
128
+ /** Platform payment wallet */
129
+ paymentWallet: PublicKey;
130
+ /** Whether the platform is active */
131
+ active: boolean;
132
+ /** Total payment links created (across all merchants tracked locally) */
133
+ totalLinks: number;
134
+ /** Total volume (USD) across tracked merchants */
135
+ totalVolume: number;
136
+ /** Estimated platform fee revenue */
137
+ feeRevenue: number;
138
+ /** Protocol fee (always 3%) */
139
+ protocolFeeBps: number;
140
+ }
141
+
142
+ // ==================== PLATFORM MANAGER ====================
143
+
144
+ export class RotatePlatformManager {
145
+ private sdk: RotateSDK;
146
+ private platformId: number;
147
+ /** Local merchant directory (name + metadata). Persists only in memory. */
148
+ private merchantDirectory: Map<number, { name?: string; metadata?: Record<string, string> }> = new Map();
149
+
150
+ constructor(sdk: RotateSDK, config: PlatformManagerConfig) {
151
+ this.sdk = sdk;
152
+ this.platformId = config.platformId;
153
+ }
154
+
155
+ // ==================== MERCHANT ONBOARDING ====================
156
+
157
+ /**
158
+ * Onboard a single merchant onto the platform.
159
+ *
160
+ * The platform admin's wallet signs the transaction and pays the account
161
+ * creation fee (~0.001 SOL). The merchant just needs a wallet address.
162
+ *
163
+ * @param input - Merchant wallet and optional name/metadata
164
+ * @returns The created merchant's ID, tx, and details
165
+ */
166
+ async onboardMerchant(input: OnboardMerchantInput): Promise<OnboardMerchantResult> {
167
+ const { tx, merchantId } = await this.sdk.createMerchant({
168
+ platformId: this.platformId,
169
+ wallet: input.wallet,
170
+ });
171
+
172
+ // Store local directory info
173
+ if (input.name || input.metadata) {
174
+ this.merchantDirectory.set(merchantId, {
175
+ name: input.name,
176
+ metadata: input.metadata,
177
+ });
178
+ }
179
+
180
+ return {
181
+ merchantId,
182
+ tx,
183
+ wallet: input.wallet,
184
+ name: input.name,
185
+ metadata: input.metadata,
186
+ };
187
+ }
188
+
189
+ /**
190
+ * Bulk onboard multiple merchants in sequence.
191
+ *
192
+ * Each merchant gets its own transaction. If one fails, the rest continue.
193
+ * Returns both succeeded and failed results so the caller can handle retries.
194
+ *
195
+ * @param inputs - Array of merchant onboarding inputs
196
+ * @param options - Optional configuration
197
+ * @returns Succeeded and failed results
198
+ */
199
+ async onboardMerchants(
200
+ inputs: OnboardMerchantInput[],
201
+ options?: {
202
+ /** Called after each merchant (for progress tracking) */
203
+ onProgress?: (completed: number, total: number, latest: OnboardMerchantResult | null) => void;
204
+ /** Stop on first failure */
205
+ stopOnError?: boolean;
206
+ }
207
+ ): Promise<BulkOnboardResult> {
208
+ const succeeded: OnboardMerchantResult[] = [];
209
+ const failed: BulkOnboardResult['failed'] = [];
210
+
211
+ for (let i = 0; i < inputs.length; i++) {
212
+ try {
213
+ const result = await this.onboardMerchant(inputs[i]);
214
+ succeeded.push(result);
215
+ options?.onProgress?.(i + 1, inputs.length, result);
216
+ } catch (err: any) {
217
+ const errorMsg = err?.message || String(err);
218
+ failed.push({ input: inputs[i], error: errorMsg });
219
+ options?.onProgress?.(i + 1, inputs.length, null);
220
+
221
+ if (options?.stopOnError) break;
222
+ }
223
+ }
224
+
225
+ return { succeeded, failed };
226
+ }
227
+
228
+ // ==================== MERCHANT MANAGEMENT ====================
229
+
230
+ /**
231
+ * Get on-chain info for a specific merchant.
232
+ */
233
+ async getMerchant(merchantId: number): Promise<MerchantInfo | null> {
234
+ const merchant = await this.sdk.getMerchant(merchantId);
235
+ if (!merchant) return null;
236
+
237
+ const local = this.merchantDirectory.get(merchantId);
238
+
239
+ return {
240
+ merchantId: merchant.id,
241
+ platformId: merchant.platformId,
242
+ wallet: merchant.wallet,
243
+ active: merchant.active,
244
+ name: local?.name,
245
+ metadata: local?.metadata,
246
+ };
247
+ }
248
+
249
+ /**
250
+ * Fetch all merchants belonging to this platform.
251
+ *
252
+ * Uses `getProgramAccounts` with a `memcmp` filter on `platform_id`
253
+ * for efficient single-RPC retrieval instead of scanning every merchant
254
+ * PDA sequentially.
255
+ *
256
+ * @param options - Optional filters
257
+ * @returns Array of merchant info
258
+ */
259
+ async getMerchants(options?: {
260
+ /** Only return active merchants */
261
+ activeOnly?: boolean;
262
+ /** Maximum number of merchants to return */
263
+ limit?: number;
264
+ }): Promise<MerchantInfo[]> {
265
+ const onChainMerchants = await this.sdk.getMerchantsByPlatform(
266
+ this.platformId,
267
+ options?.activeOnly ?? false,
268
+ );
269
+
270
+ let results: MerchantInfo[] = onChainMerchants.map((merchant) => {
271
+ const local = this.merchantDirectory.get(merchant.id);
272
+ return {
273
+ merchantId: merchant.id,
274
+ platformId: merchant.platformId,
275
+ wallet: merchant.wallet,
276
+ active: merchant.active,
277
+ name: local?.name,
278
+ metadata: local?.metadata,
279
+ };
280
+ });
281
+
282
+ if (options?.limit) {
283
+ results = results.slice(0, options.limit);
284
+ }
285
+
286
+ return results;
287
+ }
288
+
289
+ /**
290
+ * Update the local directory entry for a merchant (name/metadata).
291
+ * Does NOT modify on-chain data.
292
+ */
293
+ setMerchantInfo(merchantId: number, info: { name?: string; metadata?: Record<string, string> }): void {
294
+ const existing = this.merchantDirectory.get(merchantId) || {};
295
+ this.merchantDirectory.set(merchantId, {
296
+ name: info.name ?? existing.name,
297
+ metadata: info.metadata ?? existing.metadata,
298
+ });
299
+ }
300
+
301
+ // ==================== PLATFORM INFO ====================
302
+
303
+ /**
304
+ * Get the on-chain platform account data.
305
+ */
306
+ async getPlatform(): Promise<Platform | null> {
307
+ return this.sdk.getPlatform(this.platformId);
308
+ }
309
+
310
+ /**
311
+ * Update platform fee rate and/or active status.
312
+ * Only the platform admin wallet can call this.
313
+ *
314
+ * @param feeBps - New fee in basis points (0-600)
315
+ * @param active - Whether the platform is active
316
+ * @param newWallet - Optional new wallet for fee collection (defaults to current)
317
+ */
318
+ async updatePlatform(feeBps: number, active: boolean = true, newWallet?: PublicKey): Promise<string> {
319
+ const platform = await this.getPlatform();
320
+ if (!platform) throw new Error(`Platform #${this.platformId} not found`);
321
+
322
+ return this.sdk.updatePlatform({
323
+ platformId: this.platformId,
324
+ feeBps,
325
+ active,
326
+ newWallet: newWallet || platform.wallet,
327
+ });
328
+ }
329
+
330
+ /**
331
+ * Get platform-wide statistics.
332
+ *
333
+ * Uses `getProgramAccounts` for efficient merchant counting instead of
334
+ * scanning every merchant ID sequentially.
335
+ */
336
+ async getStats(): Promise<PlatformStats> {
337
+ const platform = await this.getPlatform();
338
+ if (!platform) throw new Error(`Platform #${this.platformId} not found`);
339
+
340
+ // Single RPC call to get all merchants on this platform
341
+ const merchants = await this.sdk.getMerchantsByPlatform(this.platformId);
342
+
343
+ return {
344
+ platformId: this.platformId,
345
+ feeBps: platform.feeBps,
346
+ feePercent: (platform.feeBps / 100).toFixed(1) + '%',
347
+ merchantCount: merchants.length,
348
+ adminWallet: platform.admin,
349
+ paymentWallet: platform.wallet,
350
+ active: platform.active,
351
+ totalLinks: 0, // Would require scanning all links; 0 as placeholder
352
+ totalVolume: 0,
353
+ feeRevenue: 0,
354
+ protocolFeeBps: PROTOCOL_FEE_BPS,
355
+ };
356
+ }
357
+
358
+ // ==================== CONVENIENCE METHODS ====================
359
+
360
+ /**
361
+ * Generate a checkout URL for a merchant's payment link.
362
+ */
363
+ getCheckoutUrl(linkId: number): string {
364
+ return this.sdk.getPaymentUrl(linkId);
365
+ }
366
+
367
+ /**
368
+ * Get the platform ID.
369
+ */
370
+ getPlatformId(): number {
371
+ return this.platformId;
372
+ }
373
+
374
+ // ==================== DIRECTORY PERSISTENCE ====================
375
+
376
+ /**
377
+ * Export the local merchant directory as JSON.
378
+ * Use this to persist merchant names/metadata to your database.
379
+ */
380
+ exportDirectory(): Array<{ merchantId: number; name?: string; metadata?: Record<string, string> }> {
381
+ const entries: Array<{ merchantId: number; name?: string; metadata?: Record<string, string> }> = [];
382
+ for (const [merchantId, info] of this.merchantDirectory) {
383
+ entries.push({ merchantId, ...info });
384
+ }
385
+ return entries;
386
+ }
387
+
388
+ /**
389
+ * Import a previously exported merchant directory.
390
+ */
391
+ importDirectory(entries: Array<{ merchantId: number; name?: string; metadata?: Record<string, string> }>): void {
392
+ for (const entry of entries) {
393
+ this.merchantDirectory.set(entry.merchantId, {
394
+ name: entry.name,
395
+ metadata: entry.metadata,
396
+ });
397
+ }
398
+ }
399
+ }
400
+
401
+ // ==================== EXPORTS ====================
402
+
403
+ export default RotatePlatformManager;