@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/README.md
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
# @rotateprotocol/sdk
|
|
2
|
+
|
|
3
|
+
**Accept Solana payments in minutes. Non-custodial, instant settlement.**
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @rotateprotocol/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Quick Start (React)
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { RotateProvider, PaymentButton } from '@rotateprotocol/sdk/components';
|
|
17
|
+
|
|
18
|
+
function App() {
|
|
19
|
+
return (
|
|
20
|
+
<RotateProvider merchantId={1000000} platformId={1000000}>
|
|
21
|
+
<PaymentButton
|
|
22
|
+
amount={29.99}
|
|
23
|
+
onSuccess={(payment) => console.log('Paid!', payment)}
|
|
24
|
+
/>
|
|
25
|
+
</RotateProvider>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start (HTML)
|
|
31
|
+
|
|
32
|
+
```html
|
|
33
|
+
<script src="https://js.rotate.app/embed.js"></script>
|
|
34
|
+
|
|
35
|
+
<button
|
|
36
|
+
data-rotate-pay
|
|
37
|
+
data-amount="29.99"
|
|
38
|
+
data-merchant="1000000"
|
|
39
|
+
data-platform="1000000"
|
|
40
|
+
>
|
|
41
|
+
Pay $29.99
|
|
42
|
+
</button>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Creating Platforms & Merchants
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import RotateSDK from '@rotateprotocol/sdk';
|
|
51
|
+
|
|
52
|
+
const sdk = new RotateSDK({ network: 'mainnet-beta' });
|
|
53
|
+
sdk.initWithWallet(wallet);
|
|
54
|
+
|
|
55
|
+
// Create platform (one-time) — ID assigned automatically
|
|
56
|
+
const { platformId } = await sdk.createPlatform({
|
|
57
|
+
feeBps: 300, // 3% platform fee
|
|
58
|
+
wallet: wallet.publicKey,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Create merchant (one-time)
|
|
62
|
+
const { merchantId } = await sdk.createMerchant({
|
|
63
|
+
platformId,
|
|
64
|
+
wallet: wallet.publicKey,
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Creating Payment Links
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// USD link — buyer chooses SOL, USDC, or USDT
|
|
74
|
+
const { linkId } = await sdk.createLinkUsd({
|
|
75
|
+
merchantId, platformId,
|
|
76
|
+
amount: 25_000_000n, // $25.00 (micro-USD, 6 decimals)
|
|
77
|
+
allowTips: true,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// SOL link
|
|
81
|
+
const { linkId } = await sdk.createLinkSol({
|
|
82
|
+
merchantId, platformId,
|
|
83
|
+
amount: 500_000_000n, // 0.5 SOL (lamports, 9 decimals)
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// USDC/USDT link
|
|
87
|
+
const { linkId } = await sdk.createLinkToken({
|
|
88
|
+
merchantId, platformId,
|
|
89
|
+
amount: 25_000_000n, // $25.00
|
|
90
|
+
currency: 'USDC', // or 'USDT'
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Link Types
|
|
95
|
+
|
|
96
|
+
| Type | Method | Amount Unit | Buyer Pays With |
|
|
97
|
+
|------|--------|-------------|-----------------|
|
|
98
|
+
| **USD** | `createLinkUsd` | Micro-USD (6 dec) | SOL, USDC, or USDT |
|
|
99
|
+
| **SOL** | `createLinkSol` | Lamports (9 dec) | SOL only |
|
|
100
|
+
| **USDC** | `createLinkToken` | Micro-USDC (6 dec) | USDC only |
|
|
101
|
+
| **USDT** | `createLinkToken` | Micro-USDT (6 dec) | USDT only |
|
|
102
|
+
|
|
103
|
+
### Tips
|
|
104
|
+
|
|
105
|
+
Set `allowTips: true` when creating a link. Tips go **100% to the merchant** — no fees on tips. Capped at 100% of the payment amount.
|
|
106
|
+
|
|
107
|
+
### Expiration
|
|
108
|
+
|
|
109
|
+
E-commerce links auto-expire after 5 minutes. Dashboard links can be manually cancelled.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Checking Payment Status
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// Get link status
|
|
117
|
+
const link = await sdk.getPaymentLink(linkId);
|
|
118
|
+
console.log(link.status); // 'Pending' | 'Paid' | 'Cancelled'
|
|
119
|
+
|
|
120
|
+
// Check if paid
|
|
121
|
+
const isPaid = await sdk.isLinkPaid(linkId);
|
|
122
|
+
|
|
123
|
+
// Wait for payment (with timeout)
|
|
124
|
+
const result = await sdk.waitForPayment(linkId, 300_000); // 5 min
|
|
125
|
+
|
|
126
|
+
// Get checkout URL
|
|
127
|
+
const url = sdk.getPaymentUrl(linkId);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## React Components
|
|
133
|
+
|
|
134
|
+
### PaymentButton
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
import { PaymentButton } from '@rotateprotocol/sdk/components';
|
|
138
|
+
|
|
139
|
+
<PaymentButton
|
|
140
|
+
amount={99.99}
|
|
141
|
+
currency="USD"
|
|
142
|
+
label="Buy Now"
|
|
143
|
+
variant="solid" // 'solid' | 'outline' | 'ghost'
|
|
144
|
+
size="lg" // 'sm' | 'md' | 'lg'
|
|
145
|
+
allowTips={true}
|
|
146
|
+
onSuccess={(payment) => router.push('/success')}
|
|
147
|
+
onError={(error) => alert(error.message)}
|
|
148
|
+
/>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### CheckoutForm
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
import { CheckoutForm } from '@rotateprotocol/sdk/components';
|
|
155
|
+
|
|
156
|
+
<CheckoutForm
|
|
157
|
+
amount={149.99}
|
|
158
|
+
productName="Pro Plan"
|
|
159
|
+
productDescription="Unlimited access for 1 year"
|
|
160
|
+
allowTips={true}
|
|
161
|
+
showCurrencySelector={true}
|
|
162
|
+
onSuccess={(payment) => fulfillOrder(payment.linkId)}
|
|
163
|
+
/>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### HostedCheckout
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
import { HostedCheckout } from '@rotateprotocol/sdk/components';
|
|
170
|
+
|
|
171
|
+
<HostedCheckout
|
|
172
|
+
linkId={12345}
|
|
173
|
+
merchantId={1000000}
|
|
174
|
+
platformId={1000000}
|
|
175
|
+
brandName="My Store"
|
|
176
|
+
brandColor="#8B5CF6"
|
|
177
|
+
onSuccess={(result) => window.location.href = '/success'}
|
|
178
|
+
/>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## React Hooks
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
import { usePaymentLink, useSolPrice, useRotate, useCreateEntities, useTokenBalances } from '@rotateprotocol/sdk/react';
|
|
187
|
+
|
|
188
|
+
// Payment status
|
|
189
|
+
const { link, status, startPolling, stopPolling } = usePaymentLink(linkId);
|
|
190
|
+
|
|
191
|
+
// SOL price
|
|
192
|
+
const { price, usdToSol, solToUsd } = useSolPrice();
|
|
193
|
+
|
|
194
|
+
// SDK instance
|
|
195
|
+
const { sdk, connected, publicKey } = useRotate({ network: 'mainnet-beta' });
|
|
196
|
+
|
|
197
|
+
// Create entities
|
|
198
|
+
const { createPlatform, createMerchant, creating } = useCreateEntities();
|
|
199
|
+
|
|
200
|
+
// Token balances
|
|
201
|
+
const { balances, hasEnough } = useTokenBalances();
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Vanilla JavaScript
|
|
207
|
+
|
|
208
|
+
```html
|
|
209
|
+
<script src="https://js.rotate.app/embed.js"></script>
|
|
210
|
+
|
|
211
|
+
<script>
|
|
212
|
+
Rotate.configure({ network: 'mainnet-beta' });
|
|
213
|
+
|
|
214
|
+
// Open checkout popup
|
|
215
|
+
Rotate.checkout({
|
|
216
|
+
amount: 29.99,
|
|
217
|
+
merchantId: 1000000,
|
|
218
|
+
platformId: 1000000,
|
|
219
|
+
allowTips: true,
|
|
220
|
+
onSuccess: (payment) => console.log('Paid!', payment),
|
|
221
|
+
});
|
|
222
|
+
</script>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Data Attributes
|
|
226
|
+
|
|
227
|
+
| Attribute | Description | Example |
|
|
228
|
+
|-----------|-------------|---------|
|
|
229
|
+
| `data-rotate-pay` | Marks as payment button | (no value) |
|
|
230
|
+
| `data-amount` | Amount | `"29.99"` |
|
|
231
|
+
| `data-merchant` | Merchant ID | `"1000000"` |
|
|
232
|
+
| `data-platform` | Platform ID | `"1000000"` |
|
|
233
|
+
| `data-currency` | Currency | `"USD"`, `"SOL"` |
|
|
234
|
+
| `data-tips` | Allow tips | `"true"` |
|
|
235
|
+
| `data-brand` | Brand name | `"My Store"` |
|
|
236
|
+
| `data-color` | Brand color | `"8B5CF6"` |
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Webhooks
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
import { EventListener, verifyWebhookSignature } from '@rotateprotocol/sdk/webhooks';
|
|
244
|
+
|
|
245
|
+
const listener = new EventListener({
|
|
246
|
+
network: 'mainnet-beta',
|
|
247
|
+
merchantId: 1000000,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
listener.on('payment.completed', async (event) => {
|
|
251
|
+
await fulfillOrder(event.data.orderRef);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
listener.start();
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Server-Side Verification
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
app.post('/api/webhooks/rotate', async (req, res) => {
|
|
261
|
+
const signature = req.headers['x-rotate-signature'];
|
|
262
|
+
const isValid = await verifyWebhookSignature(
|
|
263
|
+
JSON.stringify(req.body), signature, process.env.WEBHOOK_SECRET
|
|
264
|
+
);
|
|
265
|
+
if (!isValid) return res.status(401).json({ error: 'Invalid signature' });
|
|
266
|
+
|
|
267
|
+
if (req.body.type === 'payment.completed') {
|
|
268
|
+
await fulfillOrder(req.body.data.orderRef);
|
|
269
|
+
}
|
|
270
|
+
res.json({ received: true });
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Events
|
|
275
|
+
|
|
276
|
+
| Event | When |
|
|
277
|
+
|-------|------|
|
|
278
|
+
| `payment.completed` | Payment successful |
|
|
279
|
+
| `payment.pending` | Payment started |
|
|
280
|
+
| `link.created` | New link created |
|
|
281
|
+
| `link.cancelled` | Link cancelled |
|
|
282
|
+
| `link.expired` | Link expired |
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Platform Merchant Onboarding
|
|
287
|
+
|
|
288
|
+
For marketplaces that need to onboard sellers:
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
import { RotatePlatformManager } from '@rotateprotocol/sdk/platform';
|
|
292
|
+
|
|
293
|
+
const manager = new RotatePlatformManager(sdk, { platformId: 1000000 });
|
|
294
|
+
|
|
295
|
+
// Onboard a seller
|
|
296
|
+
const { merchantId } = await manager.onboardMerchant({
|
|
297
|
+
wallet: sellerAddress,
|
|
298
|
+
name: 'Coffee Shop',
|
|
299
|
+
metadata: { location: 'NYC' },
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Bulk onboard
|
|
303
|
+
const results = await manager.onboardMerchants(sellers, {
|
|
304
|
+
onProgress: (i, total) => console.log(`${i}/${total}`),
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// Stats
|
|
308
|
+
const stats = await manager.getStats();
|
|
309
|
+
|
|
310
|
+
// Export/import directory for persistence
|
|
311
|
+
const data = manager.exportDirectory();
|
|
312
|
+
manager.importDirectory(data);
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Only the merchant's wallet owner can update their account. Platform admins cannot modify merchant settings after creation.
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## Store & Marketplace
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
import { RotateStore } from '@rotateprotocol/sdk/store';
|
|
323
|
+
import { RotateMarketplace } from '@rotateprotocol/sdk/marketplace';
|
|
324
|
+
|
|
325
|
+
// Single-vendor store
|
|
326
|
+
const store = new RotateStore(sdk, { merchantId: 1000000, platformId: 1000000 });
|
|
327
|
+
store.addProducts([{ id: 'tshirt', name: 'Logo Tee', price: 29.99, inventory: 100 }]);
|
|
328
|
+
store.addDiscount({ code: 'SAVE10', type: 'percent', value: 10 });
|
|
329
|
+
const cart = store.createCart();
|
|
330
|
+
cart.addItem('tshirt', 2);
|
|
331
|
+
cart.applyDiscount('SAVE10');
|
|
332
|
+
const { paymentUrl } = await cart.checkout();
|
|
333
|
+
|
|
334
|
+
// Multi-vendor marketplace
|
|
335
|
+
const marketplace = new RotateMarketplace(sdk, { platformId: 1000000, platformFeeBps: 200 });
|
|
336
|
+
marketplace.addVendor({ id: 'vendor-a', merchantId: 1000001, name: 'Shirt Co.' });
|
|
337
|
+
marketplace.addProduct({ id: 'shirt-1', vendorId: 'vendor-a', name: 'Logo Tee', price: 29.99 });
|
|
338
|
+
const mktCart = marketplace.createCart();
|
|
339
|
+
mktCart.addItem('shirt-1', 2);
|
|
340
|
+
const result = await mktCart.checkout(); // Creates separate links per vendor
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Store React Hooks
|
|
344
|
+
|
|
345
|
+
```tsx
|
|
346
|
+
import { useProducts, useCart } from '@rotateprotocol/sdk/hooks';
|
|
347
|
+
|
|
348
|
+
const { products } = useProducts(store);
|
|
349
|
+
const { addItem, totals, checkout } = useCart(store);
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## Fees
|
|
355
|
+
|
|
356
|
+
| Fee | Rate |
|
|
357
|
+
|-----|------|
|
|
358
|
+
| Protocol | 3% (fixed) |
|
|
359
|
+
| Platform | 0–6% (you set it) |
|
|
360
|
+
| Split | 50/50 buyer/seller |
|
|
361
|
+
| Tips | 100% to merchant |
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## API Reference
|
|
366
|
+
|
|
367
|
+
### RotateSDK Methods
|
|
368
|
+
|
|
369
|
+
| Method | Description |
|
|
370
|
+
|--------|-------------|
|
|
371
|
+
| `getPaymentLink(id)` | Get link status |
|
|
372
|
+
| `isLinkPaid(id)` | Check if paid |
|
|
373
|
+
| `waitForPayment(id, timeout)` | Wait for payment |
|
|
374
|
+
| `createPlatform(params)` | Create platform |
|
|
375
|
+
| `createMerchant(params)` | Create merchant |
|
|
376
|
+
| `createLinkUsd(params)` | Create USD link |
|
|
377
|
+
| `createLinkSol(params)` | Create SOL link |
|
|
378
|
+
| `createLinkToken(params)` | Create USDC/USDT link |
|
|
379
|
+
| `payLinkSol(params)` | Pay SOL link |
|
|
380
|
+
| `payLinkToken(params)` | Pay token link |
|
|
381
|
+
| `payLinkUsdSol(params)` | Pay USD link with SOL |
|
|
382
|
+
| `payLinkUsdToken(params)` | Pay USD link with USDC/USDT |
|
|
383
|
+
| `cancelLink(linkId, merchantId)` | Cancel pending link |
|
|
384
|
+
| `getPaymentUrl(id)` | Get checkout URL |
|
|
385
|
+
| `getSolPrice()` | Get SOL/USD price |
|
|
386
|
+
| `calculateFees(amount, platformFeeBps)` | Calculate fee breakdown |
|
|
387
|
+
|
|
388
|
+
### ID System
|
|
389
|
+
|
|
390
|
+
| Entity | Format | Example |
|
|
391
|
+
|--------|--------|---------|
|
|
392
|
+
| Platform | 7-digit sequential | `#1000000` |
|
|
393
|
+
| Merchant | 7-digit sequential | `#1000000` |
|
|
394
|
+
| Payment Link | Sequential | `#1`, `#2`, `#3` |
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## Development & Testing
|
|
399
|
+
|
|
400
|
+
### Prerequisites
|
|
401
|
+
|
|
402
|
+
| Tool | Version | Install |
|
|
403
|
+
|------|---------|---------|
|
|
404
|
+
| Node.js | 20+ | [nodejs.org](https://nodejs.org) |
|
|
405
|
+
| Solana CLI | 2.1.4 | `sh -c "$(curl -sSfL https://release.anza.xyz/v2.1.4/install)"` |
|
|
406
|
+
| Anchor CLI | 0.32.1 | `cargo install --git https://github.com/coral-xyz/anchor avm && avm install 0.32.1 && avm use 0.32.1` |
|
|
407
|
+
| Rust | stable | `rustup update stable` |
|
|
408
|
+
|
|
409
|
+
### Running SDK Tests
|
|
410
|
+
|
|
411
|
+
```bash
|
|
412
|
+
cd sdk
|
|
413
|
+
npm install
|
|
414
|
+
npm test # Unit tests (no validator needed)
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### Running Integration Tests (Local Validator)
|
|
418
|
+
|
|
419
|
+
Integration tests automatically detect a local validator. To start one:
|
|
420
|
+
|
|
421
|
+
```bash
|
|
422
|
+
# Terminal 1: Start local validator
|
|
423
|
+
solana-test-validator
|
|
424
|
+
|
|
425
|
+
# Terminal 2: Run tests (integration tests will connect to localhost:8899)
|
|
426
|
+
cd sdk
|
|
427
|
+
npm test
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
If no local validator is running, integration tests fall back to devnet.
|
|
431
|
+
|
|
432
|
+
### Running Anchor Tests
|
|
433
|
+
|
|
434
|
+
```bash
|
|
435
|
+
cd solana
|
|
436
|
+
yarn install
|
|
437
|
+
anchor test # Builds, deploys to localnet, and runs tests
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Environment Variables
|
|
441
|
+
|
|
442
|
+
Create a `.env` file in the SDK directory for custom configuration:
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
# Custom RPC endpoint (optional, defaults to localhost:8899 for tests)
|
|
446
|
+
RPC_URL=http://127.0.0.1:8899
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## License
|
|
452
|
+
|
|
453
|
+
MIT
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rotate Catalog — Shared product catalog, inventory, and discount logic.
|
|
3
|
+
*
|
|
4
|
+
* Used internally by `RotateStore` and `RotateMarketplace` to avoid
|
|
5
|
+
* duplicating catalog management code. Not exported to SDK consumers —
|
|
6
|
+
* use `RotateStore` or `RotateMarketplace` instead.
|
|
7
|
+
*
|
|
8
|
+
* @internal
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
/** Base product shape — extend this for domain-specific fields */
|
|
12
|
+
export interface BaseProduct {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
price: number;
|
|
17
|
+
compareAtPrice?: number;
|
|
18
|
+
category?: string;
|
|
19
|
+
images?: string[];
|
|
20
|
+
inventory?: number;
|
|
21
|
+
active: boolean;
|
|
22
|
+
metadata?: Record<string, string>;
|
|
23
|
+
createdAt: number;
|
|
24
|
+
updatedAt: number;
|
|
25
|
+
}
|
|
26
|
+
/** Minimum required input to create a product */
|
|
27
|
+
export interface BaseProductInput {
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
description?: string;
|
|
31
|
+
price: number;
|
|
32
|
+
compareAtPrice?: number;
|
|
33
|
+
category?: string;
|
|
34
|
+
images?: string[];
|
|
35
|
+
inventory?: number;
|
|
36
|
+
active?: boolean;
|
|
37
|
+
metadata?: Record<string, string>;
|
|
38
|
+
}
|
|
39
|
+
/** Discount code */
|
|
40
|
+
export interface Discount {
|
|
41
|
+
code: string;
|
|
42
|
+
type: 'percent' | 'fixed';
|
|
43
|
+
value: number;
|
|
44
|
+
minAmount?: number;
|
|
45
|
+
maxUses?: number;
|
|
46
|
+
usedCount: number;
|
|
47
|
+
expiresAt?: number;
|
|
48
|
+
productIds?: string[];
|
|
49
|
+
active: boolean;
|
|
50
|
+
}
|
|
51
|
+
export interface DiscountInput {
|
|
52
|
+
code: string;
|
|
53
|
+
type: 'percent' | 'fixed';
|
|
54
|
+
value: number;
|
|
55
|
+
minAmount?: number;
|
|
56
|
+
maxUses?: number;
|
|
57
|
+
expiresAt?: number;
|
|
58
|
+
productIds?: string[];
|
|
59
|
+
active?: boolean;
|
|
60
|
+
}
|
|
61
|
+
/** Product filter options */
|
|
62
|
+
export interface ProductFilter {
|
|
63
|
+
category?: string;
|
|
64
|
+
active?: boolean;
|
|
65
|
+
search?: string;
|
|
66
|
+
minPrice?: number;
|
|
67
|
+
maxPrice?: number;
|
|
68
|
+
sortBy?: 'name' | 'price' | 'createdAt' | 'rating';
|
|
69
|
+
sortOrder?: 'asc' | 'desc';
|
|
70
|
+
limit?: number;
|
|
71
|
+
offset?: number;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Generic product catalog with inventory and discount management.
|
|
75
|
+
*
|
|
76
|
+
* @typeParam P - Product type (must extend BaseProduct).
|
|
77
|
+
* @typeParam I - Product input type (must extend BaseProductInput).
|
|
78
|
+
*/
|
|
79
|
+
export declare class Catalog<P extends BaseProduct, I extends BaseProductInput> {
|
|
80
|
+
private products;
|
|
81
|
+
private discounts;
|
|
82
|
+
private categories;
|
|
83
|
+
/** Optional hook to transform an input into a full product. */
|
|
84
|
+
private createProduct;
|
|
85
|
+
constructor(createProduct: (input: I) => P);
|
|
86
|
+
addProduct(input: I): P;
|
|
87
|
+
addProducts(inputs: I[]): P[];
|
|
88
|
+
updateProduct(id: string, updates: Partial<I>): P;
|
|
89
|
+
removeProduct(id: string): boolean;
|
|
90
|
+
getProduct(id: string): P | undefined;
|
|
91
|
+
getProducts(filter?: ProductFilter): P[];
|
|
92
|
+
getCategories(): string[];
|
|
93
|
+
get productCount(): number;
|
|
94
|
+
/** Direct access to the internal product map (for advanced filtering). */
|
|
95
|
+
entries(): IterableIterator<[string, P]>;
|
|
96
|
+
values(): IterableIterator<P>;
|
|
97
|
+
isInStock(productId: string, quantity?: number): boolean;
|
|
98
|
+
reserveInventory(productId: string, quantity: number): void;
|
|
99
|
+
releaseInventory(productId: string, quantity: number): void;
|
|
100
|
+
addDiscount(input: DiscountInput): Discount;
|
|
101
|
+
getDiscount(code: string): Discount | undefined;
|
|
102
|
+
isDiscountValid(code: string, subtotal: number): boolean;
|
|
103
|
+
useDiscount(code: string): void;
|
|
104
|
+
exportProducts(): P[];
|
|
105
|
+
exportDiscounts(): Discount[];
|
|
106
|
+
importProducts(products: P[]): void;
|
|
107
|
+
importDiscounts(discounts: Discount[]): void;
|
|
108
|
+
clear(): void;
|
|
109
|
+
/** Remove all products matching a predicate. Returns count removed. */
|
|
110
|
+
removeProductsWhere(predicate: (product: P) => boolean): number;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"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"}
|