@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.
- package/README.md +453 -0
- package/dist/catalog.d.ts +112 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +210 -0
- package/dist/catalog.js.map +1 -0
- package/dist/components/CheckoutForm.d.ts +86 -0
- package/dist/components/CheckoutForm.d.ts.map +1 -0
- package/dist/components/CheckoutForm.js +332 -0
- package/dist/components/CheckoutForm.js.map +1 -0
- package/dist/components/HostedCheckout.d.ts +57 -0
- package/dist/components/HostedCheckout.d.ts.map +1 -0
- package/dist/components/HostedCheckout.js +414 -0
- package/dist/components/HostedCheckout.js.map +1 -0
- package/dist/components/PaymentButton.d.ts +80 -0
- package/dist/components/PaymentButton.d.ts.map +1 -0
- package/dist/components/PaymentButton.js +210 -0
- package/dist/components/PaymentButton.js.map +1 -0
- package/dist/components/RotateProvider.d.ts +115 -0
- package/dist/components/RotateProvider.d.ts.map +1 -0
- package/dist/components/RotateProvider.js +264 -0
- package/dist/components/RotateProvider.js.map +1 -0
- package/dist/components/index.d.ts +17 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +27 -0
- package/dist/components/index.js.map +1 -0
- package/dist/embed.d.ts +85 -0
- package/dist/embed.d.ts.map +1 -0
- package/dist/embed.js +313 -0
- package/dist/embed.js.map +1 -0
- package/dist/hooks.d.ts +156 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +280 -0
- package/dist/hooks.js.map +1 -0
- package/dist/idl/rotate_connect.json +2572 -0
- package/dist/index.d.ts +505 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1197 -0
- package/dist/index.js.map +1 -0
- package/dist/marketplace.d.ts +257 -0
- package/dist/marketplace.d.ts.map +1 -0
- package/dist/marketplace.js +433 -0
- package/dist/marketplace.js.map +1 -0
- package/dist/platform.d.ts +234 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +268 -0
- package/dist/platform.js.map +1 -0
- package/dist/react.d.ts +140 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +429 -0
- package/dist/react.js.map +1 -0
- package/dist/store.d.ts +213 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +404 -0
- package/dist/store.js.map +1 -0
- package/dist/webhooks.d.ts +149 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +371 -0
- package/dist/webhooks.js.map +1 -0
- package/package.json +114 -0
- package/src/catalog.ts +299 -0
- package/src/components/CheckoutForm.tsx +608 -0
- package/src/components/HostedCheckout.tsx +675 -0
- package/src/components/PaymentButton.tsx +348 -0
- package/src/components/RotateProvider.tsx +370 -0
- package/src/components/index.ts +26 -0
- package/src/embed.ts +408 -0
- package/src/hooks.ts +518 -0
- package/src/idl/rotate_connect.json +2572 -0
- package/src/index.ts +1538 -0
- package/src/marketplace.ts +642 -0
- package/src/platform.ts +403 -0
- package/src/react.ts +459 -0
- package/src/store.ts +577 -0
- package/src/webhooks.ts +506 -0
package/src/platform.ts
ADDED
|
@@ -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;
|