@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,210 @@
1
+ "use strict";
2
+ /**
3
+ * Rotate Catalog — Shared product catalog, inventory, and discount logic.
4
+ *
5
+ * Used internally by `RotateStore` and `RotateMarketplace` to avoid
6
+ * duplicating catalog management code. Not exported to SDK consumers —
7
+ * use `RotateStore` or `RotateMarketplace` instead.
8
+ *
9
+ * @internal
10
+ * @packageDocumentation
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.Catalog = void 0;
14
+ // ==================== CATALOG ====================
15
+ /**
16
+ * Generic product catalog with inventory and discount management.
17
+ *
18
+ * @typeParam P - Product type (must extend BaseProduct).
19
+ * @typeParam I - Product input type (must extend BaseProductInput).
20
+ */
21
+ class Catalog {
22
+ constructor(createProduct) {
23
+ this.products = new Map();
24
+ this.discounts = new Map();
25
+ this.categories = new Set();
26
+ this.createProduct = createProduct;
27
+ }
28
+ // ==================== PRODUCT MANAGEMENT ====================
29
+ addProduct(input) {
30
+ const product = this.createProduct(input);
31
+ this.products.set(product.id, product);
32
+ if (product.category)
33
+ this.categories.add(product.category);
34
+ return product;
35
+ }
36
+ addProducts(inputs) {
37
+ return inputs.map((p) => this.addProduct(p));
38
+ }
39
+ updateProduct(id, updates) {
40
+ const existing = this.products.get(id);
41
+ if (!existing)
42
+ throw new Error(`Product '${id}' not found`);
43
+ const updated = {
44
+ ...existing,
45
+ ...updates,
46
+ id: existing.id, // immutable
47
+ updatedAt: Date.now(),
48
+ };
49
+ this.products.set(id, updated);
50
+ if (updated.category)
51
+ this.categories.add(updated.category);
52
+ return updated;
53
+ }
54
+ removeProduct(id) {
55
+ return this.products.delete(id);
56
+ }
57
+ getProduct(id) {
58
+ return this.products.get(id);
59
+ }
60
+ getProducts(filter) {
61
+ let results = Array.from(this.products.values());
62
+ if (filter) {
63
+ if (filter.active !== undefined) {
64
+ results = results.filter((p) => p.active === filter.active);
65
+ }
66
+ if (filter.category) {
67
+ results = results.filter((p) => p.category === filter.category);
68
+ }
69
+ if (filter.search) {
70
+ const q = filter.search.toLowerCase();
71
+ results = results.filter((p) => p.name.toLowerCase().includes(q) ||
72
+ p.description?.toLowerCase().includes(q) ||
73
+ p.id.toLowerCase().includes(q));
74
+ }
75
+ if (filter.minPrice !== undefined) {
76
+ results = results.filter((p) => p.price >= filter.minPrice);
77
+ }
78
+ if (filter.maxPrice !== undefined) {
79
+ results = results.filter((p) => p.price <= filter.maxPrice);
80
+ }
81
+ const sortBy = filter.sortBy || 'createdAt';
82
+ const order = filter.sortOrder === 'desc' ? -1 : 1;
83
+ results.sort((a, b) => {
84
+ if (sortBy === 'name')
85
+ return a.name.localeCompare(b.name) * order;
86
+ if (sortBy === 'price')
87
+ return (a.price - b.price) * order;
88
+ return (a.createdAt - b.createdAt) * order;
89
+ });
90
+ if (filter.offset)
91
+ results = results.slice(filter.offset);
92
+ if (filter.limit)
93
+ results = results.slice(0, filter.limit);
94
+ }
95
+ return results;
96
+ }
97
+ getCategories() {
98
+ return Array.from(this.categories).sort();
99
+ }
100
+ get productCount() {
101
+ return this.products.size;
102
+ }
103
+ /** Direct access to the internal product map (for advanced filtering). */
104
+ entries() {
105
+ return this.products.entries();
106
+ }
107
+ values() {
108
+ return this.products.values();
109
+ }
110
+ // ==================== INVENTORY ====================
111
+ isInStock(productId, quantity = 1) {
112
+ const product = this.products.get(productId);
113
+ if (!product || !product.active)
114
+ return false;
115
+ if (product.inventory === undefined)
116
+ return true;
117
+ return product.inventory >= quantity;
118
+ }
119
+ reserveInventory(productId, quantity) {
120
+ const product = this.products.get(productId);
121
+ if (!product)
122
+ throw new Error(`Product '${productId}' not found`);
123
+ if (product.inventory !== undefined) {
124
+ if (product.inventory < quantity) {
125
+ throw new Error(`Insufficient inventory for '${productId}': need ${quantity}, have ${product.inventory}`);
126
+ }
127
+ product.inventory -= quantity;
128
+ product.updatedAt = Date.now();
129
+ }
130
+ }
131
+ releaseInventory(productId, quantity) {
132
+ const product = this.products.get(productId);
133
+ if (!product)
134
+ throw new Error(`Product '${productId}' not found`);
135
+ if (product.inventory !== undefined) {
136
+ product.inventory += quantity;
137
+ product.updatedAt = Date.now();
138
+ }
139
+ }
140
+ // ==================== DISCOUNTS ====================
141
+ addDiscount(input) {
142
+ const discount = {
143
+ ...input,
144
+ usedCount: 0,
145
+ active: input.active ?? true,
146
+ };
147
+ this.discounts.set(discount.code.toUpperCase(), discount);
148
+ return discount;
149
+ }
150
+ getDiscount(code) {
151
+ return this.discounts.get(code.toUpperCase());
152
+ }
153
+ isDiscountValid(code, subtotal) {
154
+ const d = this.discounts.get(code.toUpperCase());
155
+ if (!d || !d.active)
156
+ return false;
157
+ if (d.expiresAt && Date.now() > d.expiresAt)
158
+ return false;
159
+ if (d.maxUses !== undefined && d.usedCount >= d.maxUses)
160
+ return false;
161
+ if (d.minAmount !== undefined && subtotal < d.minAmount)
162
+ return false;
163
+ return true;
164
+ }
165
+ useDiscount(code) {
166
+ const d = this.discounts.get(code.toUpperCase());
167
+ if (d)
168
+ d.usedCount++;
169
+ }
170
+ // ==================== SERIALIZATION ====================
171
+ exportProducts() {
172
+ return Array.from(this.products.values());
173
+ }
174
+ exportDiscounts() {
175
+ return Array.from(this.discounts.values());
176
+ }
177
+ importProducts(products) {
178
+ this.products.clear();
179
+ this.categories.clear();
180
+ for (const p of products) {
181
+ this.products.set(p.id, p);
182
+ if (p.category)
183
+ this.categories.add(p.category);
184
+ }
185
+ }
186
+ importDiscounts(discounts) {
187
+ this.discounts.clear();
188
+ for (const d of discounts) {
189
+ this.discounts.set(d.code.toUpperCase(), d);
190
+ }
191
+ }
192
+ clear() {
193
+ this.products.clear();
194
+ this.discounts.clear();
195
+ this.categories.clear();
196
+ }
197
+ /** Remove all products matching a predicate. Returns count removed. */
198
+ removeProductsWhere(predicate) {
199
+ let removed = 0;
200
+ for (const [id, product] of this.products.entries()) {
201
+ if (predicate(product)) {
202
+ this.products.delete(id);
203
+ removed++;
204
+ }
205
+ }
206
+ return removed;
207
+ }
208
+ }
209
+ exports.Catalog = Catalog;
210
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAuEH,oDAAoD;AAEpD;;;;;GAKG;AACH,MAAa,OAAO;IAQlB,YAAY,aAA8B;QAPlC,aAAQ,GAAmB,IAAI,GAAG,EAAE,CAAC;QACrC,cAAS,GAA0B,IAAI,GAAG,EAAE,CAAC;QAC7C,eAAU,GAAgB,IAAI,GAAG,EAAE,CAAC;QAM1C,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IACrC,CAAC;IAED,+DAA+D;IAE/D,UAAU,CAAC,KAAQ;QACjB,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACvC,IAAI,OAAO,CAAC,QAAQ;YAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC5D,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,WAAW,CAAC,MAAW;QACrB,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC;IAED,aAAa,CAAC,EAAU,EAAE,OAAmB;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvC,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;QAC5D,MAAM,OAAO,GAAM;YACjB,GAAG,QAAQ;YACX,GAAG,OAAO;YACV,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,YAAY;YAC7B,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACjB,CAAC;QACP,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAC/B,IAAI,OAAO,CAAC,QAAQ;YAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC5D,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,aAAa,CAAC,EAAU;QACtB,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IAED,UAAU,CAAC,EAAU;QACnB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,WAAW,CAAC,MAAsB;QAChC,IAAI,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAEjD,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAChC,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC;YAC9D,CAAC;YACD,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;gBACpB,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,QAAQ,CAAC,CAAC;YAClE,CAAC;YACD,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClB,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;gBACtC,OAAO,GAAG,OAAO,CAAC,MAAM,CACtB,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;oBAChC,CAAC,CAAC,WAAW,EAAE,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;oBACxC,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CACjC,CAAC;YACJ,CAAC;YACD,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAClC,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC,QAAS,CAAC,CAAC;YAC/D,CAAC;YACD,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAClC,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC,QAAS,CAAC,CAAC;YAC/D,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,WAAW,CAAC;YAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBACpB,IAAI,MAAM,KAAK,MAAM;oBAAE,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;gBACnE,IAAI,MAAM,KAAK,OAAO;oBAAE,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;gBAC3D,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC;YAC7C,CAAC,CAAC,CAAC;YAEH,IAAI,MAAM,CAAC,MAAM;gBAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC1D,IAAI,MAAM,CAAC,KAAK;gBAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7D,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,aAAa;QACX,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5C,CAAC;IAED,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;IAC5B,CAAC;IAED,0EAA0E;IAC1E,OAAO;QACL,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;IACjC,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;IAChC,CAAC;IAED,sDAAsD;IAEtD,SAAS,CAAC,SAAiB,EAAE,WAAmB,CAAC;QAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC9C,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACjD,OAAO,OAAO,CAAC,SAAS,IAAI,QAAQ,CAAC;IACvC,CAAC;IAED,gBAAgB,CAAC,SAAiB,EAAE,QAAgB;QAClD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,YAAY,SAAS,aAAa,CAAC,CAAC;QAClE,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACpC,IAAI,OAAO,CAAC,SAAS,GAAG,QAAQ,EAAE,CAAC;gBACjC,MAAM,IAAI,KAAK,CACb,+BAA+B,SAAS,WAAW,QAAQ,UAAU,OAAO,CAAC,SAAS,EAAE,CACzF,CAAC;YACJ,CAAC;YACD,OAAO,CAAC,SAAS,IAAI,QAAQ,CAAC;YAC9B,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACjC,CAAC;IACH,CAAC;IAED,gBAAgB,CAAC,SAAiB,EAAE,QAAgB;QAClD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,YAAY,SAAS,aAAa,CAAC,CAAC;QAClE,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACpC,OAAO,CAAC,SAAS,IAAI,QAAQ,CAAC;YAC9B,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACjC,CAAC;IACH,CAAC;IAED,sDAAsD;IAEtD,WAAW,CAAC,KAAoB;QAC9B,MAAM,QAAQ,GAAa;YACzB,GAAG,KAAK;YACR,SAAS,EAAE,CAAC;YACZ,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI;SAC7B,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,QAAQ,CAAC,CAAC;QAC1D,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,WAAW,CAAC,IAAY;QACtB,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,eAAe,CAAC,IAAY,EAAE,QAAgB;QAC5C,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QACjD,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAClC,IAAI,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,SAAS;YAAE,OAAO,KAAK,CAAC;QAC1D,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QACtE,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,IAAI,QAAQ,GAAG,CAAC,CAAC,SAAS;YAAE,OAAO,KAAK,CAAC;QACtE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,WAAW,CAAC,IAAY;QACtB,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QACjD,IAAI,CAAC;YAAE,CAAC,CAAC,SAAS,EAAE,CAAC;IACvB,CAAC;IAED,0DAA0D;IAE1D,cAAc;QACZ,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,eAAe;QACb,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,cAAc,CAAC,QAAa;QAC1B,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QACxB,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YAC3B,IAAI,CAAC,CAAC,QAAQ;gBAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IAED,eAAe,CAAC,SAAqB;QACnC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YAC1B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IAED,KAAK;QACH,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QACvB,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC;IAED,uEAAuE;IACvE,mBAAmB,CAAC,SAAkC;QACpD,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;YACpD,IAAI,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;gBACvB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACzB,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;CACF;AAlND,0BAkNC"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Rotate Checkout Form
3
+ *
4
+ * Embeddable checkout form that can be styled to match your site.
5
+ * Drop-in component for seamless crypto payments.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * // Basic usage
10
+ * <CheckoutForm
11
+ * amount={99.99}
12
+ * onSuccess={(payment) => {
13
+ * console.log('Paid!', payment);
14
+ * router.push('/success');
15
+ * }}
16
+ * />
17
+ *
18
+ * // With product details
19
+ * <CheckoutForm
20
+ * amount={149.99}
21
+ * productName="Pro Plan"
22
+ * productDescription="Unlimited access for 1 year"
23
+ * productImage="/images/pro-plan.png"
24
+ * onSuccess={handleSuccess}
25
+ * onError={handleError}
26
+ * />
27
+ *
28
+ * // Embedded in your own form
29
+ * <form onSubmit={handleSubmit}>
30
+ * <input name="email" placeholder="Email" />
31
+ * <CheckoutForm
32
+ * amount={total}
33
+ * embedded
34
+ * onSuccess={onPaymentSuccess}
35
+ * />
36
+ * </form>
37
+ * ```
38
+ */
39
+ import { CSSProperties } from 'react';
40
+ export interface CheckoutFormProps {
41
+ /** Amount to charge */
42
+ amount: number;
43
+ /** Currency (default: USD) */
44
+ currency?: 'SOL' | 'USDC' | 'USDT' | 'USD';
45
+ /** Product name to display */
46
+ productName?: string;
47
+ /** Product description */
48
+ productDescription?: string;
49
+ /** Product image URL */
50
+ productImage?: string;
51
+ /** Your order reference ID */
52
+ orderRef?: string;
53
+ /** Allow customer tips */
54
+ allowTips?: boolean;
55
+ /** Show currency selector */
56
+ showCurrencySelector?: boolean;
57
+ /** Default payment currency */
58
+ defaultPaymentCurrency?: 'SOL' | 'USDC' | 'USDT';
59
+ /** Compact mode (no product display) */
60
+ compact?: boolean;
61
+ /** Embedded mode (no container styling) */
62
+ embedded?: boolean;
63
+ /** Success callback */
64
+ onSuccess?: (payment: PaymentResult) => void;
65
+ /** Error callback */
66
+ onError?: (error: Error) => void;
67
+ /** Custom class name */
68
+ className?: string;
69
+ /** Custom styles */
70
+ style?: CSSProperties;
71
+ /** Submit button text */
72
+ submitText?: string;
73
+ /** Show "Powered by Rotate" badge */
74
+ showBranding?: boolean;
75
+ }
76
+ export interface PaymentResult {
77
+ linkId: number;
78
+ amount: number;
79
+ currency: string;
80
+ transactionId: string;
81
+ paidAt: number;
82
+ tip?: number;
83
+ }
84
+ export declare function CheckoutForm({ amount, currency, productName, productDescription, productImage, orderRef, allowTips, showCurrencySelector, defaultPaymentCurrency, compact, embedded, onSuccess, onError, className, style, submitText, showBranding, }: CheckoutFormProps): import("react/jsx-runtime").JSX.Element;
85
+ export default CheckoutForm;
86
+ //# sourceMappingURL=CheckoutForm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CheckoutForm.d.ts","sourceRoot":"","sources":["../../src/components/CheckoutForm.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAc,EAAoC,aAAa,EAAE,MAAM,OAAO,CAAC;AAK/E,MAAM,WAAW,iBAAiB;IAChC,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;IAC3C,8BAA8B;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0BAA0B;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wBAAwB;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6BAA6B;IAC7B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,+BAA+B;IAC/B,sBAAsB,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IACjD,wCAAwC;IACxC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uBAAuB;IACvB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,qBAAqB;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,wBAAwB;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB;IACpB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,yBAAyB;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAwND,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,QAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,YAAY,EACZ,QAAQ,EACR,SAAiB,EACjB,oBAA2B,EAC3B,sBAA+B,EAC/B,OAAe,EACf,QAAgB,EAChB,SAAS,EACT,OAAO,EACP,SAAc,EACd,KAAU,EACV,UAAU,EACV,YAAmB,GACpB,EAAE,iBAAiB,2CA2RnB;AAED,eAAe,YAAY,CAAC"}
@@ -0,0 +1,332 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CheckoutForm = CheckoutForm;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ /**
6
+ * Rotate Checkout Form
7
+ *
8
+ * Embeddable checkout form that can be styled to match your site.
9
+ * Drop-in component for seamless crypto payments.
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * // Basic usage
14
+ * <CheckoutForm
15
+ * amount={99.99}
16
+ * onSuccess={(payment) => {
17
+ * console.log('Paid!', payment);
18
+ * router.push('/success');
19
+ * }}
20
+ * />
21
+ *
22
+ * // With product details
23
+ * <CheckoutForm
24
+ * amount={149.99}
25
+ * productName="Pro Plan"
26
+ * productDescription="Unlimited access for 1 year"
27
+ * productImage="/images/pro-plan.png"
28
+ * onSuccess={handleSuccess}
29
+ * onError={handleError}
30
+ * />
31
+ *
32
+ * // Embedded in your own form
33
+ * <form onSubmit={handleSubmit}>
34
+ * <input name="email" placeholder="Email" />
35
+ * <CheckoutForm
36
+ * amount={total}
37
+ * embedded
38
+ * onSuccess={onPaymentSuccess}
39
+ * />
40
+ * </form>
41
+ * ```
42
+ */
43
+ const react_1 = require("react");
44
+ const RotateProvider_1 = require("./RotateProvider");
45
+ // ==================== STYLES ====================
46
+ const containerStyles = {
47
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
48
+ maxWidth: '400px',
49
+ width: '100%',
50
+ background: '#ffffff',
51
+ borderRadius: '12px',
52
+ boxShadow: '0 4px 24px rgba(0, 0, 0, 0.08)',
53
+ overflow: 'hidden',
54
+ };
55
+ const headerStyles = {
56
+ padding: '24px',
57
+ background: 'linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%)',
58
+ borderBottom: '1px solid #e2e8f0',
59
+ };
60
+ const productStyles = {
61
+ display: 'flex',
62
+ gap: '16px',
63
+ alignItems: 'center',
64
+ };
65
+ const productImageStyles = {
66
+ width: '60px',
67
+ height: '60px',
68
+ borderRadius: '8px',
69
+ objectFit: 'cover',
70
+ background: '#e2e8f0',
71
+ };
72
+ const bodyStyles = {
73
+ padding: '24px',
74
+ };
75
+ const sectionStyles = {
76
+ marginBottom: '20px',
77
+ };
78
+ const labelStyles = {
79
+ display: 'block',
80
+ fontSize: '13px',
81
+ fontWeight: 600,
82
+ color: '#64748b',
83
+ marginBottom: '8px',
84
+ textTransform: 'uppercase',
85
+ letterSpacing: '0.5px',
86
+ };
87
+ const currencySelectorStyles = {
88
+ display: 'flex',
89
+ gap: '8px',
90
+ };
91
+ const currencyOptionStyles = (selected, color) => ({
92
+ flex: 1,
93
+ padding: '12px 16px',
94
+ border: `2px solid ${selected ? color : '#e2e8f0'}`,
95
+ borderRadius: '8px',
96
+ background: selected ? `${color}10` : '#ffffff',
97
+ cursor: 'pointer',
98
+ transition: 'all 0.2s ease',
99
+ display: 'flex',
100
+ alignItems: 'center',
101
+ justifyContent: 'center',
102
+ gap: '8px',
103
+ fontSize: '14px',
104
+ fontWeight: selected ? 600 : 400,
105
+ color: selected ? color : '#64748b',
106
+ });
107
+ const tipSelectorStyles = {
108
+ display: 'flex',
109
+ gap: '8px',
110
+ };
111
+ const tipOptionStyles = (selected, color) => ({
112
+ flex: 1,
113
+ padding: '10px',
114
+ border: `2px solid ${selected ? color : '#e2e8f0'}`,
115
+ borderRadius: '8px',
116
+ background: selected ? `${color}10` : '#ffffff',
117
+ cursor: 'pointer',
118
+ transition: 'all 0.2s ease',
119
+ textAlign: 'center',
120
+ fontSize: '14px',
121
+ fontWeight: selected ? 600 : 400,
122
+ color: selected ? color : '#64748b',
123
+ });
124
+ const summaryStyles = {
125
+ background: '#f8fafc',
126
+ borderRadius: '8px',
127
+ padding: '16px',
128
+ marginBottom: '20px',
129
+ };
130
+ const summaryRowStyles = {
131
+ display: 'flex',
132
+ justifyContent: 'space-between',
133
+ alignItems: 'center',
134
+ padding: '8px 0',
135
+ fontSize: '14px',
136
+ color: '#64748b',
137
+ };
138
+ const summaryTotalStyles = {
139
+ ...summaryRowStyles,
140
+ borderTop: '1px solid #e2e8f0',
141
+ marginTop: '8px',
142
+ paddingTop: '16px',
143
+ fontSize: '18px',
144
+ fontWeight: 700,
145
+ color: '#1e293b',
146
+ };
147
+ const buttonStyles = (color, loading) => ({
148
+ width: '100%',
149
+ padding: '16px 24px',
150
+ background: `linear-gradient(135deg, ${color} 0%, ${adjustColor(color, -20)} 100%)`,
151
+ border: 'none',
152
+ borderRadius: '8px',
153
+ color: '#ffffff',
154
+ fontSize: '16px',
155
+ fontWeight: 600,
156
+ cursor: loading ? 'wait' : 'pointer',
157
+ transition: 'all 0.2s ease',
158
+ display: 'flex',
159
+ alignItems: 'center',
160
+ justifyContent: 'center',
161
+ gap: '8px',
162
+ boxShadow: `0 4px 14px ${color}40`,
163
+ });
164
+ const footerStyles = {
165
+ textAlign: 'center',
166
+ padding: '16px',
167
+ borderTop: '1px solid #e2e8f0',
168
+ fontSize: '12px',
169
+ color: '#94a3b8',
170
+ };
171
+ const errorStyles = {
172
+ padding: '12px 16px',
173
+ background: '#fef2f2',
174
+ border: '1px solid #fecaca',
175
+ borderRadius: '8px',
176
+ color: '#dc2626',
177
+ fontSize: '14px',
178
+ marginBottom: '16px',
179
+ };
180
+ function adjustColor(hex, percent) {
181
+ const num = parseInt(hex.replace('#', ''), 16);
182
+ const amt = Math.round(2.55 * percent);
183
+ const R = (num >> 16) + amt;
184
+ const G = (num >> 8 & 0x00FF) + amt;
185
+ const B = (num & 0x0000FF) + amt;
186
+ return '#' + (0x1000000 +
187
+ (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000 +
188
+ (G < 255 ? G < 1 ? 0 : G : 255) * 0x100 +
189
+ (B < 255 ? B < 1 ? 0 : B : 255)).toString(16).slice(1);
190
+ }
191
+ // ==================== ICONS ====================
192
+ const CurrencyIcon = {
193
+ SOL: () => ((0, jsx_runtime_1.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: [(0, jsx_runtime_1.jsx)("circle", { cx: "12", cy: "12", r: "10", fillOpacity: "0.2" }), (0, jsx_runtime_1.jsx)("text", { x: "12", y: "16", textAnchor: "middle", fontSize: "10", fontWeight: "bold", children: "\u25CE" })] })),
194
+ USDC: () => ((0, jsx_runtime_1.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: [(0, jsx_runtime_1.jsx)("circle", { cx: "12", cy: "12", r: "10", fillOpacity: "0.2" }), (0, jsx_runtime_1.jsx)("text", { x: "12", y: "16", textAnchor: "middle", fontSize: "10", fontWeight: "bold", children: "$" })] })),
195
+ USDT: () => ((0, jsx_runtime_1.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: [(0, jsx_runtime_1.jsx)("circle", { cx: "12", cy: "12", r: "10", fillOpacity: "0.2" }), (0, jsx_runtime_1.jsx)("text", { x: "12", y: "16", textAnchor: "middle", fontSize: "10", fontWeight: "bold", children: "\u20AE" })] })),
196
+ };
197
+ const WalletIcon = () => ((0, jsx_runtime_1.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [(0, jsx_runtime_1.jsx)("path", { d: "M21 12V7H5a2 2 0 0 1 0-4h14v4" }), (0, jsx_runtime_1.jsx)("path", { d: "M3 5v14a2 2 0 0 0 2 2h16v-5" }), (0, jsx_runtime_1.jsx)("path", { d: "M18 12a2 2 0 0 0 0 4h4v-4h-4z" })] }));
198
+ const SpinnerIcon = () => ((0, jsx_runtime_1.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", style: { animation: 'rotate-spin 1s linear infinite' }, children: [(0, jsx_runtime_1.jsx)("circle", { cx: "12", cy: "12", r: "10", strokeOpacity: "0.25" }), (0, jsx_runtime_1.jsx)("path", { d: "M12 2a10 10 0 0 1 10 10", strokeLinecap: "round" })] }));
199
+ const LockIcon = () => ((0, jsx_runtime_1.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor", children: (0, jsx_runtime_1.jsx)("path", { d: "M19 11H5c-1.1 0-2 .9-2 2v7c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-7c0-1.1-.9-2-2-2zm0 9H5v-7h14v7zm-7-3c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm3-10V5c0-1.7-1.3-3-3-3S9 3.3 9 5v2H7V5c0-2.8 2.2-5 5-5s5 2.2 5 5v2h-2z" }) }));
200
+ // ==================== COMPONENT ====================
201
+ function CheckoutForm({ amount, currency = 'USD', productName, productDescription, productImage, orderRef, allowTips = false, showCurrencySelector = true, defaultPaymentCurrency = 'USDC', compact = false, embedded = false, onSuccess, onError, className = '', style = {}, submitText, showBranding = true, }) {
202
+ const { config, createCheckoutSession, calculateTotal, sdk } = (0, RotateProvider_1.useRotateContext)();
203
+ const [selectedCurrency, setSelectedCurrency] = (0, react_1.useState)(defaultPaymentCurrency);
204
+ const [selectedTip, setSelectedTip] = (0, react_1.useState)(0);
205
+ const [loading, setLoading] = (0, react_1.useState)(false);
206
+ const [error, setError] = (0, react_1.useState)(null);
207
+ const [solPrice, setSolPrice] = (0, react_1.useState)(null);
208
+ const brandColor = config.brandColor || '#8B5CF6';
209
+ const tipOptions = [0, 10, 15, 20]; // Percentage options
210
+ // Fetch SOL price
211
+ (0, react_1.useEffect)(() => {
212
+ async function fetchPrice() {
213
+ try {
214
+ const price = await sdk.getSolPrice();
215
+ setSolPrice(price);
216
+ }
217
+ catch (e) {
218
+ setSolPrice(100); // Fallback
219
+ }
220
+ }
221
+ fetchPrice();
222
+ }, [sdk]);
223
+ // Calculate totals
224
+ const tipAmount = (amount * selectedTip) / 100;
225
+ const subtotalWithTip = amount + tipAmount;
226
+ const { total, fees, merchantReceives } = calculateTotal(subtotalWithTip);
227
+ // Convert to selected currency
228
+ const getAmountInCurrency = (usdAmount) => {
229
+ if (selectedCurrency === 'SOL' && solPrice) {
230
+ return usdAmount / solPrice;
231
+ }
232
+ return usdAmount; // USDC/USDT are 1:1
233
+ };
234
+ const formatAmount = (amt, curr) => {
235
+ if (curr === 'SOL') {
236
+ const solAmt = getAmountInCurrency(amt);
237
+ return `${solAmt.toFixed(solAmt < 1 ? 4 : 3)} SOL`;
238
+ }
239
+ return `$${amt.toFixed(2)}`;
240
+ };
241
+ // Handle payment — creates an on-chain checkout session and opens the
242
+ // hosted checkout page so the buyer can connect their wallet and pay.
243
+ const handleSubmit = (0, react_1.useCallback)(async () => {
244
+ setLoading(true);
245
+ setError(null);
246
+ try {
247
+ const session = await createCheckoutSession({
248
+ amount: subtotalWithTip,
249
+ currency,
250
+ description: productName,
251
+ orderRef,
252
+ allowTips,
253
+ metadata: {
254
+ tip: tipAmount.toString(),
255
+ paymentCurrency: selectedCurrency,
256
+ },
257
+ });
258
+ // Open hosted checkout in a popup so the buyer can connect a wallet and pay
259
+ const checkoutUrl = sdk.getPaymentUrl(session.linkId);
260
+ const width = 450;
261
+ const height = 700;
262
+ const left = window.screenX + (window.outerWidth - width) / 2;
263
+ const top = window.screenY + (window.outerHeight - height) / 2;
264
+ const popup = window.open(checkoutUrl, 'rotate_checkout', `width=${width},height=${height},left=${left},top=${top},popup=1`);
265
+ if (popup) {
266
+ const messageHandler = (event) => {
267
+ if (event.data?.type === 'rotate_payment_success') {
268
+ setLoading(false);
269
+ window.removeEventListener('message', messageHandler);
270
+ onSuccess?.({
271
+ linkId: session.linkId,
272
+ amount: total,
273
+ currency: selectedCurrency,
274
+ transactionId: event.data.payment?.transactionId || session.id,
275
+ paidAt: Date.now(),
276
+ tip: tipAmount,
277
+ });
278
+ }
279
+ else if (event.data?.type === 'rotate_payment_cancel') {
280
+ setLoading(false);
281
+ window.removeEventListener('message', messageHandler);
282
+ }
283
+ else if (event.data?.type === 'rotate_payment_error') {
284
+ setLoading(false);
285
+ setError(event.data.message || 'Payment failed');
286
+ window.removeEventListener('message', messageHandler);
287
+ onError?.(new Error(event.data.message || 'Payment failed'));
288
+ }
289
+ };
290
+ window.addEventListener('message', messageHandler);
291
+ // Detect popup closed without completing
292
+ const checkClosed = setInterval(() => {
293
+ if (popup.closed) {
294
+ clearInterval(checkClosed);
295
+ setLoading(false);
296
+ window.removeEventListener('message', messageHandler);
297
+ }
298
+ }, 500);
299
+ }
300
+ else {
301
+ // Popup blocked — fall back to redirect
302
+ window.location.href = checkoutUrl;
303
+ }
304
+ }
305
+ catch (err) {
306
+ setLoading(false);
307
+ setError(err.message);
308
+ onError?.(err);
309
+ }
310
+ }, [
311
+ subtotalWithTip, currency, productName, orderRef, allowTips,
312
+ tipAmount, selectedCurrency, total, sdk,
313
+ createCheckoutSession, onSuccess, onError,
314
+ ]);
315
+ // Render
316
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("style", { children: `
317
+ @keyframes rotate-spin {
318
+ from { transform: rotate(0deg); }
319
+ to { transform: rotate(360deg); }
320
+ }
321
+ ` }), (0, jsx_runtime_1.jsxs)("div", { className: className, style: embedded ? style : { ...containerStyles, ...style }, children: [!compact && productName && ((0, jsx_runtime_1.jsx)("div", { style: headerStyles, children: (0, jsx_runtime_1.jsxs)("div", { style: productStyles, children: [productImage && ((0, jsx_runtime_1.jsx)("img", { src: productImage, alt: productName, style: productImageStyles })), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { style: { fontSize: '16px', fontWeight: 600, color: '#1e293b' }, children: productName }), productDescription && ((0, jsx_runtime_1.jsx)("div", { style: { fontSize: '14px', color: '#64748b', marginTop: '4px' }, children: productDescription })), (0, jsx_runtime_1.jsx)("div", { style: { fontSize: '18px', fontWeight: 700, color: brandColor, marginTop: '8px' }, children: formatAmount(amount, currency) })] })] }) })), (0, jsx_runtime_1.jsxs)("div", { style: bodyStyles, children: [error && ((0, jsx_runtime_1.jsx)("div", { style: errorStyles, children: error })), showCurrencySelector && ((0, jsx_runtime_1.jsxs)("div", { style: sectionStyles, children: [(0, jsx_runtime_1.jsx)("label", { style: labelStyles, children: "Pay with" }), (0, jsx_runtime_1.jsx)("div", { style: currencySelectorStyles, children: ['SOL', 'USDC', 'USDT'].map((curr) => ((0, jsx_runtime_1.jsxs)("button", { type: "button", style: currencyOptionStyles(selectedCurrency === curr, brandColor), onClick: () => setSelectedCurrency(curr), children: [CurrencyIcon[curr](), (0, jsx_runtime_1.jsx)("span", { children: curr })] }, curr))) })] })), allowTips && ((0, jsx_runtime_1.jsxs)("div", { style: sectionStyles, children: [(0, jsx_runtime_1.jsx)("label", { style: labelStyles, children: "Add a tip" }), (0, jsx_runtime_1.jsx)("div", { style: tipSelectorStyles, children: tipOptions.map((tip) => ((0, jsx_runtime_1.jsx)("button", { type: "button", style: tipOptionStyles(selectedTip === tip, brandColor), onClick: () => setSelectedTip(tip), children: tip === 0 ? 'None' : `${tip}%` }, tip))) })] })), (0, jsx_runtime_1.jsxs)("div", { style: summaryStyles, children: [(0, jsx_runtime_1.jsxs)("div", { style: summaryRowStyles, children: [(0, jsx_runtime_1.jsx)("span", { children: "Subtotal" }), (0, jsx_runtime_1.jsx)("span", { children: formatAmount(amount, currency) })] }), tipAmount > 0 && ((0, jsx_runtime_1.jsxs)("div", { style: summaryRowStyles, children: [(0, jsx_runtime_1.jsxs)("span", { children: ["Tip (", selectedTip, "%)"] }), (0, jsx_runtime_1.jsx)("span", { children: formatAmount(tipAmount, currency) })] })), (0, jsx_runtime_1.jsxs)("div", { style: summaryRowStyles, children: [(0, jsx_runtime_1.jsx)("span", { children: "Processing fee" }), (0, jsx_runtime_1.jsx)("span", { children: formatAmount(fees, currency) })] }), (0, jsx_runtime_1.jsxs)("div", { style: summaryTotalStyles, children: [(0, jsx_runtime_1.jsx)("span", { children: "Total" }), (0, jsx_runtime_1.jsx)("span", { children: formatAmount(total, selectedCurrency) })] })] }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: handleSubmit, disabled: loading, style: buttonStyles(brandColor, loading), children: loading ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(SpinnerIcon, {}), (0, jsx_runtime_1.jsx)("span", { children: "Connecting Wallet..." })] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(WalletIcon, {}), (0, jsx_runtime_1.jsx)("span", { children: submitText || `Pay ${formatAmount(total, selectedCurrency)}` })] })) }), (0, jsx_runtime_1.jsxs)("div", { style: {
322
+ display: 'flex',
323
+ alignItems: 'center',
324
+ justifyContent: 'center',
325
+ gap: '6px',
326
+ marginTop: '16px',
327
+ fontSize: '12px',
328
+ color: '#94a3b8',
329
+ }, children: [(0, jsx_runtime_1.jsx)(LockIcon, {}), (0, jsx_runtime_1.jsx)("span", { children: "Secure payment \u2022 Funds go directly to merchant" })] })] }), showBranding && !embedded && ((0, jsx_runtime_1.jsxs)("div", { style: footerStyles, children: ["Powered by ", (0, jsx_runtime_1.jsx)("strong", { style: { color: brandColor }, children: "Rotate" }), " \u2022 Non-custodial payments"] }))] })] }));
330
+ }
331
+ exports.default = CheckoutForm;
332
+ //# sourceMappingURL=CheckoutForm.js.map