@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,26 @@
1
+ /**
2
+ * Rotate Protocol React Components
3
+ *
4
+ * Drop-in components for seamless payment integration on Solana.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+
9
+ // Provider
10
+ export { RotateProvider, useRotateContext } from './RotateProvider';
11
+ export type { RotateProviderProps, RotateConfig, RotateContextType, CheckoutSession, CreateCheckoutParams } from './RotateProvider';
12
+
13
+ // Payment Button
14
+ export { PaymentButton } from './PaymentButton';
15
+ export type { PaymentButtonProps, PaymentResult } from './PaymentButton';
16
+
17
+ // Checkout Form
18
+ export { CheckoutForm } from './CheckoutForm';
19
+ export type { CheckoutFormProps } from './CheckoutForm';
20
+
21
+ // Hosted Checkout
22
+ export { HostedCheckout } from './HostedCheckout';
23
+ export type { HostedCheckoutProps } from './HostedCheckout';
24
+
25
+ // Default export for convenience
26
+ export { RotateProvider as default } from './RotateProvider';
package/src/embed.ts ADDED
@@ -0,0 +1,408 @@
1
+ /**
2
+ * Rotate Embeddable Widget
3
+ *
4
+ * Add Rotate payments to any website with just a script tag.
5
+ * No React required - works with vanilla HTML/JS.
6
+ *
7
+ * @example
8
+ * ```html
9
+ * <!-- Add this to your HTML -->
10
+ * <script src="https://js.rotate.app/embed.js"></script>
11
+ *
12
+ * <!-- Simple payment button -->
13
+ * <button data-rotate-pay data-amount="29.99" data-merchant="1000000" data-platform="1000000">
14
+ * Pay $29.99
15
+ * </button>
16
+ *
17
+ * <!-- Or use JavaScript -->
18
+ * <script>
19
+ * Rotate.checkout({
20
+ * amount: 29.99,
21
+ * merchantId: 1000000,
22
+ * platformId: 1000000,
23
+ * onSuccess: (payment) => console.log('Paid!', payment)
24
+ * });
25
+ * </script>
26
+ * ```
27
+ */
28
+
29
+ // ==================== TYPES ====================
30
+
31
+ interface RotateCheckoutOptions {
32
+ amount: number;
33
+ merchantId: number;
34
+ platformId: number;
35
+ currency?: 'SOL' | 'USDC' | 'USDT' | 'USD';
36
+ description?: string;
37
+ orderRef?: string;
38
+ allowTips?: boolean;
39
+ brandName?: string;
40
+ brandLogo?: string;
41
+ brandColor?: string;
42
+ successUrl?: string;
43
+ cancelUrl?: string;
44
+ popup?: boolean;
45
+ onSuccess?: (payment: PaymentResult) => void;
46
+ onCancel?: () => void;
47
+ onError?: (error: Error) => void;
48
+ }
49
+
50
+ interface PaymentResult {
51
+ linkId: number;
52
+ amount: number;
53
+ currency: string;
54
+ transactionId: string;
55
+ paidAt: number;
56
+ }
57
+
58
+ interface RotateButtonOptions extends RotateCheckoutOptions {
59
+ element: HTMLElement;
60
+ label?: string;
61
+ size?: 'sm' | 'md' | 'lg';
62
+ variant?: 'solid' | 'outline' | 'ghost';
63
+ }
64
+
65
+ // ==================== STYLES ====================
66
+
67
+ const BUTTON_BASE_STYLES = `
68
+ display: inline-flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ gap: 8px;
72
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
73
+ font-weight: 600;
74
+ border: none;
75
+ cursor: pointer;
76
+ transition: all 0.2s ease;
77
+ position: relative;
78
+ overflow: hidden;
79
+ `;
80
+
81
+ const BUTTON_SIZES: Record<string, string> = {
82
+ sm: 'padding: 8px 16px; font-size: 14px; border-radius: 6px;',
83
+ md: 'padding: 12px 24px; font-size: 16px; border-radius: 8px;',
84
+ lg: 'padding: 16px 32px; font-size: 18px; border-radius: 10px;',
85
+ };
86
+
87
+ const SPINNER_CSS = `
88
+ @keyframes rotate-spin {
89
+ from { transform: rotate(0deg); }
90
+ to { transform: rotate(360deg); }
91
+ }
92
+ `;
93
+
94
+ // ==================== UTILITY FUNCTIONS ====================
95
+
96
+ function adjustColor(hex: string, percent: number): string {
97
+ const num = parseInt(hex.replace('#', ''), 16);
98
+ const amt = Math.round(2.55 * percent);
99
+ const R = (num >> 16) + amt;
100
+ const G = (num >> 8 & 0x00FF) + amt;
101
+ const B = (num & 0x0000FF) + amt;
102
+ return '#' + (
103
+ 0x1000000 +
104
+ (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000 +
105
+ (G < 255 ? G < 1 ? 0 : G : 255) * 0x100 +
106
+ (B < 255 ? B < 1 ? 0 : B : 255)
107
+ ).toString(16).slice(1);
108
+ }
109
+
110
+ function createSpinner(): string {
111
+ return `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="animation: rotate-spin 1s linear infinite;">
112
+ <circle cx="12" cy="12" r="10" stroke-opacity="0.25"/>
113
+ <path d="M12 2a10 10 0 0 1 10 10" stroke-linecap="round"/>
114
+ </svg>`;
115
+ }
116
+
117
+ function createWalletIcon(): string {
118
+ return `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
119
+ <path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/>
120
+ <path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/>
121
+ <path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/>
122
+ </svg>`;
123
+ }
124
+
125
+ // ==================== MAIN ROTATE OBJECT ====================
126
+
127
+ const Rotate = {
128
+ // Configuration
129
+ _config: {
130
+ baseUrl: 'https://rotate.app',
131
+ network: 'mainnet-beta' as 'devnet' | 'mainnet-beta',
132
+ },
133
+
134
+ /**
135
+ * Configure Rotate settings
136
+ */
137
+ configure(options: { baseUrl?: string; network?: 'devnet' | 'mainnet-beta' }) {
138
+ if (options.baseUrl) this._config.baseUrl = options.baseUrl;
139
+ if (options.network) this._config.network = options.network;
140
+ },
141
+
142
+ /**
143
+ * Open checkout popup/redirect
144
+ */
145
+ checkout(options: RotateCheckoutOptions) {
146
+ const {
147
+ amount,
148
+ merchantId,
149
+ platformId,
150
+ currency = 'USD',
151
+ description,
152
+ orderRef,
153
+ allowTips,
154
+ brandName,
155
+ brandLogo,
156
+ brandColor = '#8B5CF6',
157
+ successUrl,
158
+ cancelUrl,
159
+ popup = true,
160
+ onSuccess,
161
+ onCancel,
162
+ onError,
163
+ } = options;
164
+
165
+ // Build checkout URL
166
+ const params = new URLSearchParams({
167
+ amount: amount.toString(),
168
+ merchant: merchantId.toString(),
169
+ platform: platformId.toString(),
170
+ currency,
171
+ network: this._config.network,
172
+ });
173
+
174
+ if (description) params.set('description', description);
175
+ if (orderRef) params.set('ref', orderRef);
176
+ if (allowTips) params.set('tips', '1');
177
+ if (brandName) params.set('brand', brandName);
178
+ if (brandLogo) params.set('logo', brandLogo);
179
+ if (brandColor) params.set('color', brandColor.replace('#', ''));
180
+ if (successUrl) params.set('success', successUrl);
181
+ if (cancelUrl) params.set('cancel', cancelUrl);
182
+
183
+ const checkoutUrl = `${this._config.baseUrl}/checkout?${params.toString()}`;
184
+
185
+ if (popup) {
186
+ // Open popup window
187
+ const width = 450;
188
+ const height = 700;
189
+ const left = window.screenX + (window.outerWidth - width) / 2;
190
+ const top = window.screenY + (window.outerHeight - height) / 2;
191
+
192
+ const popupWindow = window.open(
193
+ checkoutUrl,
194
+ 'rotate_checkout',
195
+ `width=${width},height=${height},left=${left},top=${top},popup=1`
196
+ );
197
+
198
+ if (popupWindow) {
199
+ // Listen for popup messages
200
+ const messageHandler = (event: MessageEvent) => {
201
+ if (event.data?.type === 'rotate_payment_success') {
202
+ onSuccess?.(event.data.payment);
203
+ window.removeEventListener('message', messageHandler);
204
+ } else if (event.data?.type === 'rotate_payment_cancel') {
205
+ onCancel?.();
206
+ window.removeEventListener('message', messageHandler);
207
+ } else if (event.data?.type === 'rotate_payment_error') {
208
+ onError?.(new Error(event.data.message));
209
+ window.removeEventListener('message', messageHandler);
210
+ }
211
+ };
212
+
213
+ window.addEventListener('message', messageHandler);
214
+
215
+ // Check if popup closed without completing
216
+ const checkClosed = setInterval(() => {
217
+ if (popupWindow.closed) {
218
+ clearInterval(checkClosed);
219
+ window.removeEventListener('message', messageHandler);
220
+ }
221
+ }, 500);
222
+ }
223
+ } else {
224
+ // Redirect
225
+ window.location.href = checkoutUrl;
226
+ }
227
+ },
228
+
229
+ /**
230
+ * Create a payment button
231
+ */
232
+ createButton(options: RotateButtonOptions) {
233
+ const {
234
+ element,
235
+ amount,
236
+ merchantId,
237
+ platformId,
238
+ currency = 'USD',
239
+ label,
240
+ size = 'md',
241
+ variant = 'solid',
242
+ brandColor = '#8B5CF6',
243
+ onSuccess,
244
+ onCancel,
245
+ onError,
246
+ ...checkoutOptions
247
+ } = options;
248
+
249
+ // Inject styles
250
+ if (!document.getElementById('rotate-styles')) {
251
+ const style = document.createElement('style');
252
+ style.id = 'rotate-styles';
253
+ style.textContent = SPINNER_CSS;
254
+ document.head.appendChild(style);
255
+ }
256
+
257
+ // Format amount
258
+ const formattedAmount = currency === 'SOL'
259
+ ? `${amount} SOL`
260
+ : `$${amount.toFixed(2)}`;
261
+
262
+ const buttonLabel = label || `Pay ${formattedAmount}`;
263
+
264
+ // Build styles
265
+ let buttonStyles = BUTTON_BASE_STYLES + BUTTON_SIZES[size];
266
+
267
+ if (variant === 'solid') {
268
+ buttonStyles += `
269
+ background: linear-gradient(135deg, ${brandColor} 0%, ${adjustColor(brandColor, -20)} 100%);
270
+ color: #ffffff;
271
+ box-shadow: 0 4px 14px ${brandColor}40;
272
+ `;
273
+ } else if (variant === 'outline') {
274
+ buttonStyles += `
275
+ background: transparent;
276
+ color: ${brandColor};
277
+ border: 2px solid ${brandColor};
278
+ `;
279
+ } else {
280
+ buttonStyles += `
281
+ background: ${brandColor}15;
282
+ color: ${brandColor};
283
+ `;
284
+ }
285
+
286
+ // Create button
287
+ const button = document.createElement('button');
288
+ button.setAttribute('style', buttonStyles);
289
+ button.innerHTML = `${createWalletIcon()}<span>${buttonLabel}</span>`;
290
+ button.className = 'rotate-pay-button';
291
+
292
+ // Add hover effect
293
+ button.addEventListener('mouseenter', () => {
294
+ button.style.transform = 'translateY(-2px)';
295
+ button.style.filter = 'brightness(1.1)';
296
+ });
297
+ button.addEventListener('mouseleave', () => {
298
+ button.style.transform = 'translateY(0)';
299
+ button.style.filter = 'brightness(1)';
300
+ });
301
+
302
+ // Handle click
303
+ let loading = false;
304
+ button.addEventListener('click', () => {
305
+ if (loading) return;
306
+
307
+ loading = true;
308
+ button.innerHTML = `${createSpinner()}<span>Processing...</span>`;
309
+ button.style.cursor = 'wait';
310
+
311
+ this.checkout({
312
+ amount,
313
+ merchantId,
314
+ platformId,
315
+ currency,
316
+ brandColor,
317
+ ...checkoutOptions,
318
+ onSuccess: (payment) => {
319
+ loading = false;
320
+ button.innerHTML = `<span>✓ Paid!</span>`;
321
+ button.style.background = '#10B981';
322
+ button.style.cursor = 'pointer';
323
+ onSuccess?.(payment);
324
+
325
+ // Reset after delay
326
+ setTimeout(() => {
327
+ button.innerHTML = `${createWalletIcon()}<span>${buttonLabel}</span>`;
328
+ button.style.background = variant === 'solid'
329
+ ? `linear-gradient(135deg, ${brandColor} 0%, ${adjustColor(brandColor, -20)} 100%)`
330
+ : variant === 'outline' ? 'transparent' : `${brandColor}15`;
331
+ }, 3000);
332
+ },
333
+ onCancel: () => {
334
+ loading = false;
335
+ button.innerHTML = `${createWalletIcon()}<span>${buttonLabel}</span>`;
336
+ button.style.cursor = 'pointer';
337
+ onCancel?.();
338
+ },
339
+ onError: (error) => {
340
+ loading = false;
341
+ button.innerHTML = `${createWalletIcon()}<span>${buttonLabel}</span>`;
342
+ button.style.cursor = 'pointer';
343
+ onError?.(error);
344
+ },
345
+ });
346
+ });
347
+
348
+ // Replace element content
349
+ element.innerHTML = '';
350
+ element.appendChild(button);
351
+
352
+ return button;
353
+ },
354
+
355
+ /**
356
+ * Auto-initialize buttons from data attributes
357
+ */
358
+ init() {
359
+ // Find all elements with data-rotate-pay
360
+ const buttons = document.querySelectorAll('[data-rotate-pay]');
361
+
362
+ buttons.forEach((element) => {
363
+ const el = element as HTMLElement;
364
+
365
+ const amount = parseFloat(el.dataset.amount || '0');
366
+ const merchantId = parseInt(el.dataset.merchant || '0');
367
+ const platformId = parseInt(el.dataset.platform || '0');
368
+
369
+ if (!amount || !merchantId || !platformId) {
370
+ console.warn('Rotate: Missing required data attributes (amount, merchant, platform)', el);
371
+ return;
372
+ }
373
+
374
+ this.createButton({
375
+ element: el,
376
+ amount,
377
+ merchantId,
378
+ platformId,
379
+ currency: (el.dataset.currency as any) || 'USD',
380
+ label: el.dataset.label || el.textContent?.trim(),
381
+ size: (el.dataset.size as any) || 'md',
382
+ variant: (el.dataset.variant as any) || 'solid',
383
+ brandColor: el.dataset.color || '#8B5CF6',
384
+ brandName: el.dataset.brand,
385
+ description: el.dataset.description,
386
+ orderRef: el.dataset.ref,
387
+ allowTips: el.dataset.tips === 'true',
388
+ successUrl: el.dataset.successUrl,
389
+ cancelUrl: el.dataset.cancelUrl,
390
+ popup: el.dataset.popup !== 'false',
391
+ });
392
+ });
393
+ },
394
+ };
395
+
396
+ // Auto-initialize on DOMContentLoaded
397
+ if (typeof window !== 'undefined') {
398
+ if (document.readyState === 'loading') {
399
+ document.addEventListener('DOMContentLoaded', () => Rotate.init());
400
+ } else {
401
+ Rotate.init();
402
+ }
403
+
404
+ // Expose globally
405
+ (window as any).Rotate = Rotate;
406
+ }
407
+
408
+ export default Rotate;