@w3payments/react 0.0.1

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 ADDED
@@ -0,0 +1,488 @@
1
+ # W3 Payments React
2
+
3
+ React components for crypto onramping and payments. Currently focused on fiat-to-crypto and crypto-to-crypto onramp flows with MeshPay integration.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @w3payments/react @w3payments/core @w3payments/common
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ### 1. Backend Setup (Required)
14
+
15
+ The widget requires a secure backend to create payment sessions. Create an Express server:
16
+
17
+ ```typescript
18
+ // server.ts
19
+ import express from 'express';
20
+ import cors from 'cors';
21
+ import { W3Payments } from '@w3payments/core';
22
+ import type { CreatePaymentOptions } from '@w3payments/common';
23
+
24
+ const app = express();
25
+ app.use(cors());
26
+ app.use(express.json());
27
+
28
+ // Initialize W3Payments with your credentials
29
+ const w3payments = new W3Payments({
30
+ config: {
31
+ environment: 'sandbox', // or 'production'
32
+ meshpay: {
33
+ clientId: process.env.MESH_CLIENT_ID!,
34
+ clientSecret: process.env.MESH_CLIENT_SECRET!,
35
+ },
36
+ },
37
+ });
38
+
39
+ await w3payments.initialize();
40
+
41
+ // Create payment endpoint
42
+ app.post('/api/payments/create', async (req, res) => {
43
+ try {
44
+ // Validate request body matches expected CreatePaymentOptions type
45
+ const paymentOptions: CreatePaymentOptions = req.body;
46
+ const session = await w3payments.paymentIntents.create(paymentOptions);
47
+ res.json(session);
48
+ } catch (error) {
49
+ res.status(500).json({
50
+ error: error instanceof Error ? error.message : 'Payment creation failed'
51
+ });
52
+ }
53
+ });
54
+
55
+ // Payment status endpoint
56
+ app.get('/api/payments/:sessionId', async (req, res) => {
57
+ try {
58
+ const { sessionId } = req.params;
59
+ const { vendorId } = req.query;
60
+
61
+ if (!vendorId || typeof vendorId !== 'string') {
62
+ return res.status(400).json({ error: 'vendorId is required' });
63
+ }
64
+
65
+ const status = await w3payments.paymentIntents.retrieve(sessionId, vendorId);
66
+ res.json(status);
67
+ } catch (error) {
68
+ res.status(500).json({
69
+ error: error instanceof Error ? error.message : 'Status retrieval failed'
70
+ });
71
+ }
72
+ });
73
+
74
+ app.listen(3001);
75
+ ```
76
+
77
+ **Credentials**: MeshPay credentials are provided by the W3 Payments team during onboarding.
78
+
79
+ ### 2. React Integration
80
+
81
+ Add the payment widget to your React app:
82
+
83
+ ```tsx
84
+ import { W3PaymentsProvider, W3PaymentWidget } from '@w3payments/react';
85
+
86
+ export function PaymentPage() {
87
+ return (
88
+ <div style={{ width: '400px', height: '600px' }}>
89
+ <W3PaymentsProvider
90
+ endpoint="http://localhost:3001/api/payments"
91
+ environment="sandbox"
92
+ >
93
+ <W3PaymentWidget
94
+ title="Fund Wallet"
95
+ targetCurrency="USDC"
96
+ targetNetwork="solana"
97
+ destinations={[{
98
+ address: "your_wallet_or_contract_address",
99
+ networkId: "solana",
100
+ symbol: "USDC"
101
+ }]}
102
+ onComplete={(result) => {
103
+ console.log('Payment completed:', result.transactionId);
104
+ // Update UI, redirect, etc.
105
+ }}
106
+ onError={(error) => {
107
+ console.error('Payment failed:', error.message);
108
+ // Show error to user
109
+ }}
110
+ />
111
+ </W3PaymentsProvider>
112
+ </div>
113
+ );
114
+ }
115
+ ```
116
+
117
+ ## Configuration
118
+
119
+ ### Required Props
120
+
121
+ **destinations** - Where payments will be sent:
122
+ ```tsx
123
+ destinations={[{
124
+ address: "contract_or_wallet_address",
125
+ networkId: "solana", // bitcoin, ethereum, solana, polygon, etc.
126
+ symbol: "USDC" // Currency symbol
127
+ }]}
128
+ ```
129
+
130
+ ### Payment Method Filtering
131
+
132
+ Control which payment options are available:
133
+
134
+ ```tsx
135
+ // Show specific wallet currencies
136
+ walletFilter={['BTC', 'ETH', 'USDC']}
137
+
138
+ // Show specific exchanges
139
+ exchangeFilter={['coinbase', 'kraken', 'binance']}
140
+
141
+ // Show fiat payment methods
142
+ fiatFilter={['ACH']}
143
+
144
+ // Target specific currency (auto-converts if needed)
145
+ targetCurrency="USDC"
146
+ targetNetwork="solana"
147
+ ```
148
+
149
+ ### Event Handling
150
+
151
+ ```tsx
152
+ <W3PaymentWidget
153
+ onInitiate={(data) => {
154
+ // User clicked payment button - log for analytics
155
+ console.log(`Starting ${data.currency} payment for ${data.amount}`);
156
+ }}
157
+
158
+ onComplete={(data) => {
159
+ // Payment successful - update your app state
160
+ updateUserBalance(data.amount);
161
+ redirectToSuccess();
162
+ }}
163
+
164
+ onError={(error) => {
165
+ // Payment failed - show user-friendly error
166
+ showErrorMessage(error.message);
167
+ }}
168
+ />
169
+ ```
170
+
171
+ ## Use Cases
172
+
173
+ ### Crypto Onramping (Coming Soon with IronPay)
174
+ ```tsx
175
+ // Buy crypto with fiat (ACH, bank transfer) - IronPay integration in progress
176
+ <W3PaymentWidget
177
+ title="Buy Crypto"
178
+ targetCurrency="USDC"
179
+ targetNetwork="solana"
180
+ fiatFilter={['ACH']} // Will be available with IronPay
181
+ destinations={[{ address: userWallet, networkId: "solana", symbol: "USDC" }]}
182
+ />
183
+ ```
184
+
185
+ ### Crypto-to-Crypto Transfers (Available Now)
186
+ ```tsx
187
+ // Transfer between different cryptocurrencies
188
+ <W3PaymentWidget
189
+ title="Fund Wallet"
190
+ targetCurrency="USDC"
191
+ targetNetwork="solana"
192
+ walletFilter={['BTC', 'ETH']} // Pay with BTC/ETH, receive USDC
193
+ destinations={[{ address: userWallet, networkId: "solana", symbol: "USDC" }]}
194
+ />
195
+ ```
196
+
197
+ ### Coming Soon
198
+
199
+ ```tsx
200
+ // Auto-Converting Payments - COMING SOON
201
+ <W3PaymentWidget
202
+ title="Pay with Any Currency"
203
+ targetCurrency="ETH"
204
+ autoConvert={true} // Not yet available
205
+ destinations={[{ address: receiverWallet, networkId: "ethereum", symbol: "ETH" }]}
206
+ />
207
+
208
+ // Staking with Auto-Yield - COMING SOON
209
+ <W3PaymentWidget
210
+ title="Stake & Earn"
211
+ autoYield={true} // Not yet available
212
+ destinations={[{ address: stakingContract, networkId: "solana", symbol: "USDC" }]}
213
+ />
214
+
215
+ // Crypto Offramping - COMING SOON
216
+ <W3PaymentWidget
217
+ title="Cash Out"
218
+ targetCurrency="USD" // Fiat offramp not yet available
219
+ destinations={[{ address: bankAccount, networkId: "fiat", symbol: "USD" }]}
220
+ />
221
+ ```
222
+
223
+ ## Current Features
224
+
225
+ ### MeshPay Integration
226
+ Secure crypto-to-crypto transfers and exchange integrations through MeshPay's infrastructure.
227
+
228
+ ### Multi-Currency Support
229
+ Support for major cryptocurrencies (BTC, ETH, USDC, SOL) across multiple networks (Bitcoin, Ethereum, Solana).
230
+
231
+ ### Smart Payment Routing
232
+ Automatically routes payments through the most efficient available methods based on your target currency and network.
233
+
234
+ ## Planned Features (Coming Soon)
235
+
236
+ ### IronPay Integration
237
+ Fiat-to-crypto onramping with ACH bank transfers and direct USD purchasing (integration in progress).
238
+
239
+ ### Auto Conversion
240
+ Automatic currency conversions between any supported payment methods and target currencies at optimal rates.
241
+
242
+ ### Auto Yield
243
+ Automatic routing through yield-generating opportunities (staking, lending pools) while maintaining the same user experience.
244
+
245
+ ### Cross-Chain Support
246
+ Seamless payments across Bitcoin, Ethereum, Solana, Polygon with automatic bridging.
247
+
248
+ ### Crypto Offramping
249
+ Direct crypto-to-fiat conversions with bank account deposits.
250
+
251
+ ## Customization
252
+
253
+ ### Widget Branding
254
+
255
+ Customize the widget appearance with props:
256
+
257
+ ```tsx
258
+ <W3PaymentWidget
259
+ title="Your Brand Name"
260
+ subtitle="CHOOSE PAYMENT METHOD"
261
+ logo="/your-logo.png"
262
+ showAmount={false} // Hide amount section
263
+ showHeader={false} // Hide header with title/logo
264
+ // ... other props
265
+ />
266
+ ```
267
+
268
+ ### Theme Customization
269
+
270
+ Use the **W3Appearance API** for consistent theming across all components:
271
+
272
+ #### Built-in Themes
273
+
274
+ ```tsx
275
+ import { W3PaymentWidget } from '@w3payments/react';
276
+
277
+ // Default light theme
278
+ <W3PaymentWidget theme="default" />
279
+
280
+ // Dark theme
281
+ <W3PaymentWidget theme="night" />
282
+ ```
283
+
284
+ #### Custom Themes
285
+
286
+ ```tsx
287
+ import type { W3Appearance } from '@w3payments/common';
288
+
289
+ const customTheme: W3Appearance = {
290
+ variables: {
291
+ // Brand colors
292
+ primary: '#ff6b35', // Orange brand color
293
+ background: '#f8f9fa', // Light gray background
294
+ text: '#2d3748', // Dark text
295
+ textSecondary: '#718096', // Muted text
296
+
297
+ // Status colors
298
+ success: '#38a169', // Green
299
+ danger: '#e53e3e', // Red
300
+ warning: '#dd6b20', // Orange
301
+ border: '#e2e8f0', // Light border
302
+
303
+ // Typography
304
+ fontFamily: 'Inter, system-ui, sans-serif',
305
+ fontSizeBase: '14px',
306
+ fontSizeSmall: '12px',
307
+ fontSizeLarge: '28px',
308
+ fontWeightNormal: '500',
309
+
310
+ // Layout
311
+ spacingUnit: '6px', // Increased spacing
312
+ borderRadius: '12px', // More rounded
313
+ buttonRadius: '8px' // Button radius
314
+ }
315
+ };
316
+
317
+ <W3PaymentWidget appearance={customTheme} />
318
+ ```
319
+
320
+ #### Dark Theme Example
321
+
322
+ ```tsx
323
+ const darkTheme: W3Appearance = {
324
+ variables: {
325
+ primary: '#0085FF', // Blue accent
326
+ background: '#1a202c', // Dark background
327
+ text: '#f7fafc', // Light text
328
+ textSecondary: '#a0aec0', // Muted light text
329
+ success: '#48bb78', // Bright green
330
+ danger: '#f56565', // Bright red
331
+ warning: '#ed8936', // Bright orange
332
+ border: '#4a5568', // Dark border
333
+ fontFamily: 'SF Pro Display, system-ui, sans-serif'
334
+ }
335
+ };
336
+
337
+ <W3PaymentWidget appearance={darkTheme} />
338
+ ```
339
+
340
+ #### Theming with Context
341
+
342
+ Apply themes globally using the provider:
343
+
344
+ ```tsx
345
+ <W3PaymentsProvider
346
+ endpoint="http://localhost:3001/api/payments"
347
+ environment="sandbox"
348
+ appearance={customTheme} // Applied to all widgets
349
+ >
350
+ <W3PaymentWidget amount="100.00" />
351
+ <W3PaymentWidget amount="50.00" /> {/* Both use customTheme */}
352
+ </W3PaymentsProvider>
353
+ ```
354
+
355
+ ### Advanced Theming
356
+
357
+ #### Responsive Themes
358
+
359
+ ```tsx
360
+ const useResponsiveTheme = () => {
361
+ const [theme, setTheme] = useState<W3Appearance>({
362
+ variables: {
363
+ primary: '#2563eb',
364
+ spacingUnit: window.innerWidth < 768 ? '3px' : '4px',
365
+ fontSizeBase: window.innerWidth < 768 ? '13px' : '14px',
366
+ borderRadius: window.innerWidth < 768 ? '6px' : '8px'
367
+ }
368
+ });
369
+
370
+ useEffect(() => {
371
+ const handleResize = () => {
372
+ setTheme(prev => ({
373
+ variables: {
374
+ ...prev.variables,
375
+ spacingUnit: window.innerWidth < 768 ? '3px' : '4px',
376
+ fontSizeBase: window.innerWidth < 768 ? '13px' : '14px',
377
+ borderRadius: window.innerWidth < 768 ? '6px' : '8px'
378
+ }
379
+ }));
380
+ };
381
+
382
+ window.addEventListener('resize', handleResize);
383
+ return () => window.removeEventListener('resize', handleResize);
384
+ }, []);
385
+
386
+ return theme;
387
+ };
388
+
389
+ // Usage
390
+ function PaymentPage() {
391
+ const responsiveTheme = useResponsiveTheme();
392
+
393
+ return (
394
+ <W3PaymentWidget appearance={responsiveTheme} />
395
+ );
396
+ }
397
+ ```
398
+
399
+ #### Brand Integration
400
+
401
+ ```tsx
402
+ // Extract colors from your design system
403
+ const brandTheme: W3Appearance = {
404
+ variables: {
405
+ // Use your brand colors
406
+ primary: 'var(--brand-primary, #2563eb)',
407
+ background: 'var(--surface-primary, #ffffff)',
408
+ text: 'var(--text-primary, #1a1a1a)',
409
+ textSecondary: 'var(--text-secondary, #6b7280)',
410
+
411
+ // Match your typography
412
+ fontFamily: 'var(--font-family, Inter, sans-serif)',
413
+ fontSizeBase: 'var(--text-sm, 14px)',
414
+
415
+ // Match your spacing scale
416
+ spacingUnit: 'var(--spacing-1, 4px)',
417
+ borderRadius: 'var(--radius-md, 8px)'
418
+ }
419
+ };
420
+
421
+ <W3PaymentWidget appearance={brandTheme} />
422
+ ```
423
+
424
+ ### Advanced CSS Customization
425
+
426
+ For specific styling needs beyond the W3Appearance API:
427
+
428
+ ```css
429
+ /* Target widget container */
430
+ [data-w3-payment-widget] {
431
+ /* Widget-level customization */
432
+ min-height: 500px;
433
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
434
+ }
435
+
436
+ /* Customize payment buttons */
437
+ [data-w3-payment-widget] button {
438
+ transition: all 0.2s ease;
439
+ }
440
+
441
+ [data-w3-payment-widget] button:hover {
442
+ transform: translateY(-1px);
443
+ }
444
+
445
+ /* Amount input styling */
446
+ [data-w3-payment-widget] input[type="number"] {
447
+ letter-spacing: 0.05em;
448
+ }
449
+ ```
450
+
451
+ **Note:** The W3Appearance API handles all standard theming needs. CSS overrides should only be used for specific animations or layout adjustments not covered by the design system.
452
+
453
+ ## How It Works
454
+
455
+ 1. **Payment Initiation**: User selects amount and payment method
456
+ 2. **Secure Session**: Widget calls your backend to create payment session
457
+ 3. **Smart Routing**: W3Payments optimizes vendor selection, conversions, and yield
458
+ 4. **User Flow**: User completes payment in vendor's secure interface
459
+ 5. **Auto Processing**: Auto conversions, bridging, and yield optimization happen automatically
460
+ 6. **Completion**: Widget receives result and calls your success/error handlers
461
+
462
+ The widget abstracts all complexity - multi-vendor routing, currency conversions, cross-chain bridging, and yield optimization happen seamlessly behind the scenes.
463
+
464
+ ## Roadmap
465
+
466
+ **Current**: Onramping, basic payments, MeshPay integration
467
+ **Coming Soon**: Offramping, auto-yield optimization, additional vendor integrations
468
+ **Future**: Advanced DeFi integrations, institutional-grade APIs, white-label solutions
469
+
470
+ ## Framework Support
471
+
472
+ This is the React implementation. Other frameworks coming soon:
473
+ - `@w3payments/vue` - Vue 3 components
474
+ - `@w3payments/angular` - Angular components
475
+ - `@w3payments/nextjs` - Next.js integration
476
+
477
+ Full SDK and REST API also in development for non-JavaScript environments.
478
+
479
+ ## Development
480
+
481
+ Test the widget with Storybook:
482
+
483
+ ```bash
484
+ cd packages/react
485
+ npm run storybook
486
+ ```
487
+
488
+ Make sure your backend server is running for end-to-end payment testing.
@@ -0,0 +1,30 @@
1
+ /**
2
+ * W3 Payment Widget Appearance Configuration
3
+ *
4
+ * Following Stripe's appearance API pattern for simple but complete theming.
5
+ * This file belongs in the React package as it's UI-specific.
6
+ */
7
+ export interface W3Appearance {
8
+ variables?: {
9
+ primary?: string;
10
+ background?: string;
11
+ text?: string;
12
+ textSecondary?: string;
13
+ danger?: string;
14
+ success?: string;
15
+ warning?: string;
16
+ border?: string;
17
+ fontFamily?: string;
18
+ fontSizeBase?: string;
19
+ fontSizeSmall?: string;
20
+ fontSizeLarge?: string;
21
+ fontWeightNormal?: string;
22
+ spacingUnit?: string;
23
+ borderRadius?: string;
24
+ buttonRadius?: string;
25
+ };
26
+ }
27
+ declare const BUILT_IN_THEMES: Record<string, Required<W3Appearance['variables']>>;
28
+ export declare const getThemeVariables: (themeName?: string, appearance?: W3Appearance) => Record<string, string>;
29
+ export { BUILT_IN_THEMES };
30
+ //# sourceMappingURL=appearance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appearance.d.ts","sourceRoot":"","sources":["../src/appearance.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE;QAEV,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAGhB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAG1B,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAqBD,QAAA,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,CAWxE,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,YAAW,MAAkB,EAC7B,aAAa,YAAY,KACxB,MAAM,CAAC,MAAM,EAAE,MAAM,CAUvB,CAAC;AAEF,OAAO,EAAE,eAAe,EAAE,CAAC"}
@@ -0,0 +1 @@
1
+ <svg width="24" height="23.5" viewBox="0 0 48 47" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M43.9654 0H3.55466C1.75037 0 0.28833 1.46203 0.28833 3.26633V43.677C0.28833 45.4813 1.75037 46.9434 3.55466 46.9434H43.9654C45.7697 46.9434 47.2317 45.4813 47.2317 43.677V3.26633C47.2317 1.46203 45.7697 0 43.9654 0ZM39.785 30.1883C39.785 35.0097 35.8638 38.9309 31.0425 38.9309H10.8906C9.77276 38.9309 8.86468 38.0248 8.86468 36.905C8.86468 35.7852 9.77078 34.8791 10.8906 34.8791H31.0425C33.6302 34.8791 35.7352 32.7741 35.7352 30.1864C35.7352 27.5986 33.6302 25.4936 31.0425 25.4936H23.0419C21.9241 25.4936 21.016 24.5875 21.016 23.4677C21.016 22.348 21.9221 21.4419 23.0419 21.4419H31.0425C33.6302 21.4419 35.7352 19.3368 35.7352 16.7491C35.7352 14.1614 33.6302 12.0563 31.0425 12.0563H10.8906C9.77276 12.0563 8.86468 11.1502 8.86468 10.0305C8.86468 8.9107 9.77078 8.00459 10.8906 8.00459H31.0425C35.8638 8.00459 39.785 11.9258 39.785 16.7471C39.785 19.4437 38.5564 21.8593 36.6315 23.4638C38.5564 25.0683 39.785 27.4839 39.785 30.1804V30.1883Z" fill="white" class="group-hover:fill-[#FF3223]"></path></svg>
@@ -0,0 +1 @@
1
+ <svg width="14.5" height="11" viewBox="0 0 29 22" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M15.4597 5.22243C16.6803 3.79799 16.2708 4.07496 18.1384 2.86616C18.6825 2.60897 18.8744 2.06096 18.7141 1.47535C18.5539 0.889747 17.9129 0.632558 17.4658 0.816549C15.6714 1.691 14.8523 2.698 14.2608 3.44781C13.9066 3.87515 13.7939 4.52802 14.1144 5.00283C14.4349 5.47963 15.0422 5.58844 15.4597 5.22243Z" fill="white"></path><path d="M28.2084 10.9678C28.1055 10.3169 27.4586 10.123 27.0412 10.2694C26.6257 10.4158 26.3685 10.8926 26.3685 11.4782C26.3685 12.687 26.1449 13.969 25.7275 15.1759C25.0232 17.0811 23.5176 19.0575 22.4295 19.3127C22.3979 19.2395 22.3346 18.9467 22.3346 18.5807C22.3346 16.8239 23.3594 13.4547 25.9194 9.97664C26.2716 9.53744 26.2083 8.84302 25.7908 8.43943C25.407 8.03584 24.7996 8.10904 24.4455 8.58583C23.8401 9.41082 23.306 10.2398 22.8351 11.0588C22.8054 11.0747 22.7777 11.0925 22.748 11.1122C15.7702 15.9831 12.1241 19.2791 9.72427 19.2791C9.72427 19.1683 9.75592 18.9863 9.97948 18.3631L13.3091 9.72143C13.5327 9.17144 13.3091 8.51263 12.8284 8.29303C12.3476 8.03584 11.7719 8.29303 11.58 8.84302L8.09012 17.8863C7.44912 19.5343 7.99318 21.5107 9.72229 21.5107C12.1221 21.5107 15.3706 18.9506 21.0862 14.8969C20.6391 16.2442 20.4096 17.4985 20.4096 18.5827C20.4096 19.9003 20.9536 21.5127 22.3623 21.5127C24.1863 21.5127 26.5228 18.8042 27.516 15.9831C27.7395 15.3242 28.0126 14.1708 28.0759 13.6782C28.1392 13.1856 28.3054 11.6207 28.2025 10.9698L28.2084 10.9678Z" fill="white"></path><path d="M1.16597 19.376C0.576406 19.376 0.0996094 19.8528 0.0996094 20.4423C0.0996094 21.0319 0.576406 21.5087 1.16597 21.5087C1.75553 21.5087 2.23232 21.0319 2.23232 20.4423C2.23232 19.8528 1.75553 19.376 1.16597 19.376Z" fill="white"></path></svg>