@superlogic/spree-pay 0.1.42 → 0.2.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.
@@ -0,0 +1,795 @@
1
+ // src/types/payments.ts
2
+ var PaymentType = /* @__PURE__ */ ((PaymentType2) => {
3
+ PaymentType2["CREDIT_CARD"] = "CREDIT_CARD";
4
+ PaymentType2["CRYPTO"] = "CRYPTO";
5
+ PaymentType2["CDC"] = "CDC";
6
+ PaymentType2["CREDIT_CARD_SPLIT"] = "SPLIT";
7
+ PaymentType2["POINTS"] = "POINTS";
8
+ return PaymentType2;
9
+ })(PaymentType || {});
10
+
11
+ // package.json
12
+ var version = "0.2.1";
13
+
14
+ // src/utils/logger.ts
15
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
16
+ LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
17
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
18
+ LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
19
+ LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
20
+ LogLevel2[LogLevel2["NONE"] = 4] = "NONE";
21
+ return LogLevel2;
22
+ })(LogLevel || {});
23
+ var Logger = class _Logger {
24
+ config;
25
+ constructor(config2 = {}) {
26
+ this.config = {
27
+ minLevel: this.getDefaultLogLevel(config2.environment),
28
+ prefix: "[spree-pay]",
29
+ ...config2
30
+ };
31
+ }
32
+ /**
33
+ * Determines default log level based on environment
34
+ * - Production: Only ERROR and version info
35
+ * - Development/Staging: All logs
36
+ */
37
+ getDefaultLogLevel(environment) {
38
+ if (typeof window === "undefined") return 1 /* INFO */;
39
+ if (environment) {
40
+ return environment === "prod" ? 3 /* ERROR */ : 0 /* DEBUG */;
41
+ }
42
+ return 3 /* ERROR */;
43
+ }
44
+ /**
45
+ * Set the environment and update log level accordingly
46
+ */
47
+ setEnvironment(environment) {
48
+ this.config.environment = environment;
49
+ this.config.minLevel = this.getDefaultLogLevel(environment);
50
+ }
51
+ shouldLog(level) {
52
+ return level >= this.config.minLevel;
53
+ }
54
+ formatMessage(level, message, context) {
55
+ const levelName = LogLevel[level];
56
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
57
+ const contextStr = context && Object.keys(context).length > 0 ? ` ${JSON.stringify(context)}` : "";
58
+ return `${this.config.prefix} [${levelName}] ${timestamp} - ${message}${contextStr}`;
59
+ }
60
+ /**
61
+ * Log debug information (only in dev/stg)
62
+ */
63
+ debug(message, context) {
64
+ if (!this.shouldLog(0 /* DEBUG */)) return;
65
+ console.log(this.formatMessage(0 /* DEBUG */, message, context));
66
+ }
67
+ /**
68
+ * Log informational messages (only in dev/stg)
69
+ */
70
+ info(message, context) {
71
+ if (!this.shouldLog(1 /* INFO */)) return;
72
+ console.log(this.formatMessage(1 /* INFO */, message, context));
73
+ }
74
+ /**
75
+ * Log warnings (only in dev/stg)
76
+ */
77
+ warn(message, context) {
78
+ if (!this.shouldLog(2 /* WARN */)) return;
79
+ console.warn(this.formatMessage(2 /* WARN */, message, context));
80
+ }
81
+ /**
82
+ * Log errors (shows in all environments)
83
+ */
84
+ error(message, error, context) {
85
+ if (!this.shouldLog(3 /* ERROR */)) return;
86
+ const errorContext = {
87
+ ...context,
88
+ ...error instanceof Error ? {
89
+ errorName: error.name,
90
+ errorMessage: error.message,
91
+ errorStack: error.stack
92
+ } : { error }
93
+ };
94
+ console.error(this.formatMessage(3 /* ERROR */, message, errorContext));
95
+ }
96
+ /**
97
+ * Always logs the version, regardless of environment
98
+ */
99
+ logVersion() {
100
+ console.log(`${this.config.prefix} v${version} - ${this.config.environment}`);
101
+ }
102
+ /**
103
+ * Creates a child logger with additional context
104
+ * Useful for component-specific logging
105
+ */
106
+ child(prefix) {
107
+ return new _Logger({
108
+ ...this.config,
109
+ prefix: `${this.config.prefix}:${prefix}`
110
+ });
111
+ }
112
+ /**
113
+ * Override log level (useful for testing or debugging)
114
+ */
115
+ setLogLevel(level) {
116
+ this.config.minLevel = level;
117
+ }
118
+ };
119
+ var logger = new Logger();
120
+ var configureLogger = (config2) => {
121
+ if (config2.environment) {
122
+ logger.setEnvironment(config2.environment);
123
+ }
124
+ const { environment, ...restConfig } = config2;
125
+ Object.assign(logger["config"], restConfig);
126
+ };
127
+
128
+ // src/context/SpreePayActionsContext.tsx
129
+ import { createContext, useCallback, useContext, useRef, useState } from "react";
130
+
131
+ // src/types/errors.ts
132
+ var PaymentError = class extends Error {
133
+ status;
134
+ details;
135
+ constructor(message, status, details) {
136
+ super(message);
137
+ this.name = "PaymentError";
138
+ this.status = status;
139
+ this.details = details;
140
+ }
141
+ };
142
+
143
+ // src/context/SpreePayActionsContext.tsx
144
+ import { jsx } from "react/jsx-runtime";
145
+ var processLogger = logger.child("process");
146
+ var SpreePayActionsContext = createContext(void 0);
147
+ var SpreePayProvider = ({ children, env }) => {
148
+ const processRef = useRef(null);
149
+ const [selectedPaymentMethod, setSelectedPaymentMethod] = useState({
150
+ type: "CREDIT_CARD" /* CREDIT_CARD */,
151
+ method: null
152
+ });
153
+ const [isInternalProcessing, setInternalProcessing] = useState(false);
154
+ const register = useCallback((fn) => {
155
+ processRef.current = fn;
156
+ }, []);
157
+ const process = useCallback(
158
+ async (data) => {
159
+ if (!processRef.current) {
160
+ const error = new Error("SpreePay process function not registered");
161
+ processLogger.error("Process function not registered", error);
162
+ throw error;
163
+ }
164
+ processLogger.info("Payment process started", {
165
+ hash: data.hash,
166
+ capture: data.capture,
167
+ hasMetadata: Boolean(data.metadata),
168
+ paymentType: selectedPaymentMethod.type
169
+ });
170
+ setInternalProcessing(true);
171
+ try {
172
+ const result = await processRef.current(data);
173
+ processLogger.info("Payment process completed", {
174
+ status: result.status,
175
+ paymentId: result.paymentId,
176
+ paymentType: result.paymentType,
177
+ txHash: result.txHash
178
+ });
179
+ return result;
180
+ } catch (e) {
181
+ processLogger.error("Payment process failed", e instanceof Error ? e : void 0, {
182
+ hash: data.hash,
183
+ paymentType: selectedPaymentMethod.type,
184
+ errorMessage: e instanceof Error ? e.message : String(e)
185
+ });
186
+ if (e instanceof Error) throw e;
187
+ throw new PaymentError("Payment failed", "FAILED" /* FAILED */, e);
188
+ } finally {
189
+ setInternalProcessing(false);
190
+ }
191
+ },
192
+ [selectedPaymentMethod.type]
193
+ );
194
+ const value = {
195
+ // Set default to true for web3 points to backward compatibility
196
+ env: { ...env, useWeb3Points: env.useWeb3Points ?? true },
197
+ enabled: Boolean(selectedPaymentMethod.method),
198
+ isInternalProcessing,
199
+ process,
200
+ register,
201
+ selectedPaymentMethod,
202
+ setSelectedPaymentMethod
203
+ };
204
+ return /* @__PURE__ */ jsx(SpreePayActionsContext.Provider, { value, children });
205
+ };
206
+ var useSpreePay = () => {
207
+ const ctx = useContext(SpreePayActionsContext);
208
+ if (!ctx) throw new Error("useSpreePay must be used within a SpreePayProvider");
209
+ return {
210
+ process: ctx.process,
211
+ isProcessing: ctx.isInternalProcessing,
212
+ enabled: ctx.enabled,
213
+ selectedPaymentMethod: ctx.selectedPaymentMethod
214
+ };
215
+ };
216
+ var useSpreePaymentMethod = () => {
217
+ const ctx = useContext(SpreePayActionsContext);
218
+ if (!ctx) throw new Error("useSpreePay must be used within a SpreePayProvider");
219
+ return {
220
+ setSelectedPaymentMethod: ctx.setSelectedPaymentMethod,
221
+ selectedPaymentMethod: ctx.selectedPaymentMethod,
222
+ isInternalProcessing: ctx.isInternalProcessing
223
+ };
224
+ };
225
+ var useSpreePayEnv = () => {
226
+ const ctx = useContext(SpreePayActionsContext);
227
+ if (!ctx) throw new Error("useSpreePay must be used within a SpreePayProvider");
228
+ return { env: ctx.env };
229
+ };
230
+ var useSpreePayRegister = () => {
231
+ const ctx = useContext(SpreePayActionsContext);
232
+ if (!ctx) throw new Error("useSpreePayRegister must be used within a SpreePayProvider");
233
+ return { register: ctx.register };
234
+ };
235
+
236
+ // src/context/StaticConfigContext.tsx
237
+ import { createContext as createContext2, useContext as useContext2, useEffect, useMemo, useState as useState2 } from "react";
238
+
239
+ // src/context/config.ts
240
+ var config = {
241
+ dev: {
242
+ bookit: {
243
+ slapiUrl: "https://slapi.dev.superlogic.com",
244
+ keycloakUrl: "https://auth.dev.join.bookit.com",
245
+ keycloakClientId: "oneof-next"
246
+ },
247
+ moca: {
248
+ slapiUrl: "https://slapi.dev.air.shop",
249
+ keycloakUrl: "https://login.dev.air.shop",
250
+ keycloakClientId: "oneof-next"
251
+ },
252
+ qiibee: {
253
+ slapiUrl: "https://slapi.dev.superlogic.com",
254
+ keycloakUrl: "https://sso.dev.tickets.qiibeefoundation.org",
255
+ keycloakClientId: "oneof-next"
256
+ },
257
+ umhp: {
258
+ slapiUrl: "https://slapi.dev.umusicpassport.com",
259
+ keycloakUrl: "https://auth.dev.umusicpassport.com",
260
+ keycloakClientId: "oneof-next"
261
+ },
262
+ cdc: {
263
+ slapiUrl: "https://slapi.dev.superlogic.com",
264
+ keycloakUrl: "https://auth.dev02.superlogic.com",
265
+ keycloakClientId: "oneof-next"
266
+ }
267
+ },
268
+ stg: {
269
+ bookit: {
270
+ slapiUrl: "https://slapi.stg.superlogic.com",
271
+ keycloakUrl: "https://auth.stg.join.bookit.com",
272
+ keycloakClientId: "oneof-next"
273
+ },
274
+ moca: {
275
+ slapiUrl: "https://slapi.stg.air.shop",
276
+ keycloakUrl: "https://login.stg.air.shop",
277
+ keycloakClientId: "oneof-next"
278
+ },
279
+ qiibee: {
280
+ slapiUrl: "https://slapi.stg.tickets.qiibeefoundation.org",
281
+ keycloakUrl: "https://sso.stg.tickets.qiibeefoundation.org",
282
+ keycloakClientId: "oneof-next"
283
+ },
284
+ umhp: {
285
+ slapiUrl: "https://slapi.stg.umusicpassport.com",
286
+ keycloakUrl: "https://auth.stg.umusicpassport.com",
287
+ keycloakClientId: "oneof-next"
288
+ },
289
+ cdc: {
290
+ slapiUrl: "https://slapi.stg.superlogic.com",
291
+ keycloakUrl: "https://auth.stg02.superlogic.com",
292
+ keycloakClientId: "oneof-next"
293
+ }
294
+ },
295
+ prod: {
296
+ bookit: {
297
+ slapiUrl: "https://slapi.superlogic.com",
298
+ keycloakUrl: "https://auth.join.bookit.com",
299
+ keycloakClientId: "oneof-next"
300
+ },
301
+ moca: {
302
+ slapiUrl: "https://slapi.air.shop",
303
+ keycloakUrl: "https://login.air.shop",
304
+ keycloakClientId: "oneof-next"
305
+ },
306
+ qiibee: {
307
+ slapiUrl: "https://slapi.tickets.qiibeefoundation.org",
308
+ keycloakUrl: "https://sso.tickets.qiibeefoundation.org",
309
+ keycloakClientId: "oneof-next"
310
+ },
311
+ umhp: {
312
+ slapiUrl: "https://slapi.umusicpassport.com",
313
+ keycloakUrl: "https://auth.umusicpassport.com",
314
+ keycloakClientId: "oneof-next"
315
+ },
316
+ cdc: {
317
+ slapiUrl: "https://slapi.superlogic.com",
318
+ keycloakUrl: "https://auth.superlogic.com",
319
+ keycloakClientId: "oneof-next"
320
+ }
321
+ }
322
+ };
323
+
324
+ // src/context/StaticConfigContext.tsx
325
+ import { jsx as jsx2 } from "react/jsx-runtime";
326
+ var StaticConfigContext = createContext2(null);
327
+ var StaticConfigProvider = ({ children, props }) => {
328
+ const { env } = useSpreePayEnv();
329
+ const [appProps, setAppProps] = useState2(props);
330
+ useEffect(() => {
331
+ setAppProps(props);
332
+ }, [props]);
333
+ const staticConfig = useMemo(() => {
334
+ const envConfig = config[env.environment];
335
+ const isKnownTenant = env.tenantId in envConfig;
336
+ if (!isKnownTenant) {
337
+ logger.warn(`Unknown tenantId "${env.tenantId}", falling back to "moca"`, {
338
+ tenantId: env.tenantId,
339
+ environment: env.environment
340
+ });
341
+ }
342
+ const appKey = isKnownTenant ? env.tenantId : "moca";
343
+ return envConfig[appKey];
344
+ }, [env.environment, env.tenantId]);
345
+ return /* @__PURE__ */ jsx2(StaticConfigContext.Provider, { value: { staticConfig, appProps }, children });
346
+ };
347
+ var useStaticConfig = () => {
348
+ const ctx = useContext2(StaticConfigContext);
349
+ if (!ctx) throw new Error("useStaticConfig must be used within StaticConfigProvider");
350
+ return ctx;
351
+ };
352
+
353
+ // src/lib/utils.ts
354
+ import { clsx } from "clsx";
355
+ import { twMerge } from "tailwind-merge";
356
+ function cn(...inputs) {
357
+ return twMerge(clsx(inputs));
358
+ }
359
+
360
+ // src/ui/portal.ts
361
+ import React from "react";
362
+ var PortalContainerContext = React.createContext({ container: null });
363
+ function PortalContainerProvider({
364
+ container,
365
+ children
366
+ }) {
367
+ return React.createElement(PortalContainerContext.Provider, { value: { container } }, children);
368
+ }
369
+ function usePortalContainer() {
370
+ return React.useContext(PortalContainerContext).container;
371
+ }
372
+
373
+ // src/fetcher/client.ts
374
+ var cfg = { baseUrl: void 0, accessToken: void 0, tenantId: void 0 };
375
+ var buildUrl = (key) => {
376
+ if (!cfg.baseUrl) throw new Error("Missing SLAPI baseUrl. Call registerApi(...) first.");
377
+ if (typeof key === "string") return cfg.baseUrl + key;
378
+ const { url, params = {} } = key;
379
+ const usp = new URLSearchParams();
380
+ Object.entries(params).forEach(([k, v]) => {
381
+ if (v === void 0 || v === null) return;
382
+ usp.append(k, String(v));
383
+ });
384
+ const qs = usp.toString();
385
+ return cfg.baseUrl + url + (qs ? `?${qs}` : "");
386
+ };
387
+ var request = async (method, url, body) => {
388
+ if (!cfg.accessToken) throw new Error("Missing SLAPI accessToken. Call registerApi(...) first.");
389
+ if (!cfg.tenantId) throw new Error("Missing SLAPI tenantId. Call registerApi(...) first.");
390
+ const headers = {
391
+ Accept: "application/json",
392
+ Authorization: `Bearer ${cfg.accessToken}`,
393
+ ["X-Tenant-ID"]: cfg.tenantId
394
+ };
395
+ let payload;
396
+ if (method !== "GET" && method !== "DELETE" && body !== void 0) {
397
+ if (typeof FormData !== "undefined" && body instanceof FormData) {
398
+ payload = body;
399
+ } else if (body instanceof Blob) {
400
+ payload = body;
401
+ } else {
402
+ headers["Content-Type"] = "application/json";
403
+ payload = JSON.stringify(body);
404
+ }
405
+ }
406
+ const res = await fetch(url, {
407
+ method,
408
+ headers,
409
+ body: payload,
410
+ cache: "no-store"
411
+ });
412
+ if (!res.ok) {
413
+ const text = await res.text().catch(() => "");
414
+ throw new Error(text || `Request failed: ${res.status}`);
415
+ }
416
+ const contentType = res.headers.get("content-type") || "";
417
+ if (!contentType.includes("application/json")) {
418
+ return await res.text();
419
+ }
420
+ return await res.json();
421
+ };
422
+ var slapiApi = {
423
+ get: async () => {
424
+ throw new Error("slapiApi is not configured. Call registerApi(...) first.");
425
+ },
426
+ post: async () => {
427
+ throw new Error("slapiApi is not configured. Call registerApi(...) first.");
428
+ },
429
+ put: async () => {
430
+ throw new Error("slapiApi is not configured. Call registerApi(...) first.");
431
+ },
432
+ patch: async () => {
433
+ throw new Error("slapiApi is not configured. Call registerApi(...) first.");
434
+ },
435
+ delete: async () => {
436
+ throw new Error("slapiApi is not configured. Call registerApi(...) first.");
437
+ }
438
+ };
439
+ var registerApi = (config2) => {
440
+ cfg.baseUrl = config2.baseUrl;
441
+ cfg.accessToken = config2.accessToken;
442
+ cfg.tenantId = config2.tenantId;
443
+ slapiApi = {
444
+ get: async (key) => {
445
+ const url = buildUrl(key);
446
+ return request("GET", url);
447
+ },
448
+ post: async (key, body) => {
449
+ const url = buildUrl(key);
450
+ return request("POST", url, body);
451
+ },
452
+ put: async (key, body) => {
453
+ const url = buildUrl(key);
454
+ return request("PUT", url, body);
455
+ },
456
+ patch: async (key, body) => {
457
+ const url = buildUrl(key);
458
+ return request("PATCH", url, body);
459
+ },
460
+ delete: async (key) => {
461
+ const url = buildUrl(key);
462
+ return request("DELETE", url);
463
+ }
464
+ };
465
+ return (key) => slapiApi.get(key);
466
+ };
467
+
468
+ // src/hooks/useSpreePayConfig.ts
469
+ import useSWR from "swr";
470
+ var useSpreePayConfig = () => {
471
+ const { data, isLoading } = useSWR("/v1/tenants/configs/spree-pay");
472
+ return {
473
+ spreePayConfig: data ? {
474
+ ...data,
475
+ rainbowProjectId: data.rainbowProjectId ?? "3fdcd5ff50cb84917cd05e40146975d8",
476
+ rainbowAppName: data.rainbowAppName ?? "AIR Shop",
477
+ pointsTitle: data.pointsTitle ?? "AIR SP",
478
+ pointsConversionRatio: data.pointsConversionRatio ?? 0.01,
479
+ crypto: {
480
+ ...data.crypto,
481
+ oneInchAggregationRouter: data.crypto?.oneInchAggregationRouter || "0x111111125421ca6dc452d289314280a0f8842a65"
482
+ }
483
+ } : null,
484
+ configIsLoading: isLoading
485
+ };
486
+ };
487
+
488
+ // src/components/common/InfoBanner.tsx
489
+ import xss from "xss";
490
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
491
+ var InfoBanner = (props) => {
492
+ const { type = "info", message } = props;
493
+ if (!message) return null;
494
+ return /* @__PURE__ */ jsxs(
495
+ "div",
496
+ {
497
+ className: cn("flex w-full gap-1.5 rounded-md p-2", {
498
+ "border border-(--b-secondary)": type === "info",
499
+ "bg-(--s-warning)/10": type === "warning"
500
+ }),
501
+ children: [
502
+ type === "info" && /* @__PURE__ */ jsx3(
503
+ "svg",
504
+ {
505
+ xmlns: "http://www.w3.org/2000/svg",
506
+ fill: "none",
507
+ viewBox: "0 0 20 20",
508
+ className: "size-5 shrink-0 text-(--positive)",
509
+ children: /* @__PURE__ */ jsx3(
510
+ "path",
511
+ {
512
+ fill: "currentColor",
513
+ d: "M10 17.92a2.2 2.2 0 0 1-1.59-.66 4 4 0 0 0-1.07-.81q-.45-.2-1.35-.2-.94 0-1.6-.65a2.2 2.2 0 0 1-.65-1.59q0-.9-.19-1.34a4 4 0 0 0-.8-1.08 2.2 2.2 0 0 1-.5-2.44q.17-.41.5-.74.6-.62.8-1.07t.2-1.35q0-.94.65-1.6.65-.65 1.59-.65.9 0 1.34-.19.46-.2 1.08-.81A2.2 2.2 0 0 1 10 2.08a2.3 2.3 0 0 1 1.59.66q.62.62 1.07.81t1.35.2q.94 0 1.6.65.65.64.65 1.59 0 .9.19 1.34.2.46.81 1.08a2.2 2.2 0 0 1 .66 1.59 2.2 2.2 0 0 1-.66 1.59q-.61.63-.81 1.07t-.2 1.35q0 .94-.65 1.6-.64.65-1.59.65-.9 0-1.34.19-.45.2-1.08.8a2.2 2.2 0 0 1-1.59.67m0-1.25q.2 0 .39-.08a1 1 0 0 0 .31-.21q.8-.8 1.5-1.09.7-.28 1.81-.28.42 0 .71-.29.3-.29.29-.7 0-1.13.28-1.82.3-.7 1.08-1.5.3-.3.3-.7t-.3-.7a5 5 0 0 1-1.08-1.5 5 5 0 0 1-.28-1.8q0-.42-.29-.71a1 1 0 0 0-.7-.29q-1.14 0-1.83-.28a5 5 0 0 1-1.5-1.08 1 1 0 0 0-.69-.3 1 1 0 0 0-.7.3q-.8.8-1.5 1.08T6 4.99q-.42 0-.71.29a1 1 0 0 0-.29.7q0 1.14-.28 1.83t-1.09 1.5q-.29.28-.29.69t.3.7q.8.8 1.08 1.5T5 14.01q0 .42.29.71.29.3.7.29 1.14 0 1.83.28t1.5 1.09a1 1 0 0 0 .7.29m2.03-3.62q.43 0 .73-.3t.3-.72a1 1 0 0 0-.3-.73 1 1 0 0 0-.73-.3 1 1 0 0 0-.73.3 1 1 0 0 0-.3.73q0 .43.3.73t.73.3m-4.53-.54q.18.18.43.18a.6.6 0 0 0 .43-.18l4.14-4.12a.6.6 0 0 0 .2-.44.6.6 0 0 0-.2-.45.6.6 0 0 0-.44-.2.6.6 0 0 0-.45.2l-4.12 4.14a.6.6 0 0 0-.18.43q0 .25.19.44M7.97 9a1 1 0 0 0 .74-.3 1 1 0 0 0 .3-.74q0-.43-.3-.73a1 1 0 0 0-.74-.3q-.43 0-.73.3t-.3.73.3.73.73.3"
514
+ }
515
+ )
516
+ }
517
+ ),
518
+ type === "warning" && /* @__PURE__ */ jsx3(
519
+ "svg",
520
+ {
521
+ xmlns: "http://www.w3.org/2000/svg",
522
+ fill: "none",
523
+ viewBox: "0 0 20 20",
524
+ className: "size-5 shrink-0 text-(--warning)",
525
+ children: /* @__PURE__ */ jsx3(
526
+ "path",
527
+ {
528
+ fill: "currentColor",
529
+ d: "M10 13.96a.6.6 0 0 0 .44-.18.6.6 0 0 0 .18-.45V9.8a.6.6 0 0 0-.18-.44.6.6 0 0 0-.44-.18.6.6 0 0 0-.45.18.6.6 0 0 0-.18.44v3.54q0 .27.18.45a.6.6 0 0 0 .45.18m0-6.22q.29 0 .48-.2.2-.18.2-.47 0-.3-.2-.48a.7.7 0 0 0-.48-.2q-.3 0-.48.2a.7.7 0 0 0-.2.48q0 .29.2.48.2.2.48.19m0 10.18q-1.64 0-3.09-.63a8 8 0 0 1-4.2-4.2 8 8 0 0 1-.63-3.1q0-1.64.63-3.09a8 8 0 0 1 4.2-4.2q1.45-.63 3.09-.63 1.65 0 3.08.63a8 8 0 0 1 4.21 4.2q.63 1.45.63 3.09t-.63 3.09a8 8 0 0 1-4.2 4.2q-1.46.63-3.09.63m0-1.25q2.8 0 4.73-1.94A6.4 6.4 0 0 0 16.67 10q0-2.8-1.94-4.73A6.4 6.4 0 0 0 10 3.33q-2.8 0-4.73 1.94A6.4 6.4 0 0 0 3.33 10q0 2.8 1.94 4.73A6.4 6.4 0 0 0 10 16.67"
530
+ }
531
+ )
532
+ }
533
+ ),
534
+ /* @__PURE__ */ jsx3(
535
+ "p",
536
+ {
537
+ className: "text-[14px] leading-5 text-(--primary) [&_a]:underline",
538
+ dangerouslySetInnerHTML: { __html: xss(message) }
539
+ }
540
+ )
541
+ ]
542
+ }
543
+ );
544
+ };
545
+
546
+ // src/components/common/Legal.tsx
547
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
548
+ var Legal = () => {
549
+ const { spreePayConfig } = useSpreePayConfig();
550
+ return /* @__PURE__ */ jsxs2("p", { className: "text-center text-xs leading-5 font-medium text-(--secondary)", children: [
551
+ "By clicking on the button below I acknowledge that I have read and accepted the",
552
+ " ",
553
+ /* @__PURE__ */ jsx4("a", { className: "underline", href: spreePayConfig?.termsConditionsUrl, target: "_blank", rel: "noreferrer", children: "Terms and Conditions" }),
554
+ "."
555
+ ] });
556
+ };
557
+
558
+ // src/services/slapi.ts
559
+ var slapiLogger = logger.child("slapi");
560
+ var SlapiPaymentService = {
561
+ createPayment: async ({ capture = false, ...rest }) => {
562
+ slapiLogger.debug("Creating payment", {
563
+ type: rest.type,
564
+ hash: rest.hash,
565
+ capture
566
+ });
567
+ try {
568
+ const data = await slapiApi.post("/v1/payments", { ...rest, capture });
569
+ slapiLogger.info("Payment created successfully", {
570
+ paymentId: data.id,
571
+ status: data.status,
572
+ txId: data.txId
573
+ });
574
+ return { data };
575
+ } catch (error) {
576
+ slapiLogger.error("Failed to create payment", error, {
577
+ type: rest.type,
578
+ hash: rest.hash
579
+ });
580
+ throw error;
581
+ }
582
+ },
583
+ baseVerify: async ({ id, txHash }) => {
584
+ slapiLogger.debug("Verifying base transaction", { id, txHash });
585
+ try {
586
+ const result = await slapiApi.post(`/v1/base-transactions/transactions/${id}/verify`, {
587
+ txHash
588
+ });
589
+ slapiLogger.info("Base transaction verified", {
590
+ id,
591
+ txHash,
592
+ verified: result.verified
593
+ });
594
+ return result;
595
+ } catch (error) {
596
+ slapiLogger.error("Base transaction verification failed", error, {
597
+ id,
598
+ txHash
599
+ });
600
+ throw error;
601
+ }
602
+ },
603
+ addCard: async ({ source, hash }) => {
604
+ slapiLogger.debug("Adding card", { hash, sourcePrefix: source.substring(0, 10) });
605
+ try {
606
+ const data = await slapiApi.post("/v1/payments/cards", { hash, source });
607
+ slapiLogger.info("Card added successfully", { cardId: data.id, hash });
608
+ return { data };
609
+ } catch (error) {
610
+ slapiLogger.error("Failed to add card", error, { hash });
611
+ throw error;
612
+ }
613
+ },
614
+ validate3DS: async ({ paymentId }) => {
615
+ slapiLogger.debug("Validating 3DS", { paymentId });
616
+ try {
617
+ const data = await slapiApi.post("/v1/payments/validate", {
618
+ paymentId,
619
+ type: "CREDIT_CARD" /* CREDIT_CARD */
620
+ });
621
+ slapiLogger.info("3DS validated successfully", {
622
+ paymentId,
623
+ status: data.status
624
+ });
625
+ return { data };
626
+ } catch (error) {
627
+ slapiLogger.error("3DS validation failed", error, { paymentId });
628
+ throw error;
629
+ }
630
+ },
631
+ validateCDC: async ({ paymentId }) => {
632
+ slapiLogger.debug("Validating CDC payment", { paymentId });
633
+ try {
634
+ const data = await slapiApi.post("/v1/payments/validate", {
635
+ paymentId,
636
+ type: "CDC" /* CDC */
637
+ });
638
+ slapiLogger.info("CDC payment validated successfully", {
639
+ paymentId,
640
+ status: data.status
641
+ });
642
+ return { data };
643
+ } catch (error) {
644
+ slapiLogger.error("CDC payment validation failed", error, {
645
+ paymentId
646
+ });
647
+ throw error;
648
+ }
649
+ },
650
+ validatePoints: async ({ paymentId, txHash }) => {
651
+ slapiLogger.debug("Validating points payment", { paymentId, txHash });
652
+ try {
653
+ const data = await slapiApi.post("/v1/payments/validate", {
654
+ txHash,
655
+ paymentId,
656
+ type: "POINTS" /* POINTS */
657
+ });
658
+ slapiLogger.info("Points payment validated successfully", {
659
+ paymentId,
660
+ status: data.status,
661
+ txHash
662
+ });
663
+ return { data };
664
+ } catch (error) {
665
+ slapiLogger.error("Points payment validation failed", error, {
666
+ paymentId,
667
+ txHash
668
+ });
669
+ throw error;
670
+ }
671
+ },
672
+ getStatus: async (paymentId) => {
673
+ slapiLogger.debug("Getting payment status", { paymentId });
674
+ try {
675
+ const result = await slapiApi.get(`/v1/payments/${paymentId}/status`);
676
+ slapiLogger.debug("Payment status retrieved", {
677
+ paymentId,
678
+ status: result.detail.status
679
+ });
680
+ return result;
681
+ } catch (error) {
682
+ slapiLogger.error("Failed to get payment status", error, {
683
+ paymentId
684
+ });
685
+ throw error;
686
+ }
687
+ }
688
+ };
689
+
690
+ // src/ui/dialog.tsx
691
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
692
+ import { XIcon } from "lucide-react";
693
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
694
+ function Dialog({ ...props }) {
695
+ return /* @__PURE__ */ jsx5(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
696
+ }
697
+ function DialogPortal({ ...props }) {
698
+ const container = usePortalContainer();
699
+ const safeContainer = container && document.body.contains(container) ? container : void 0;
700
+ return /* @__PURE__ */ jsx5(DialogPrimitive.Portal, { container: safeContainer, "data-slot": "dialog-portal", ...props });
701
+ }
702
+ function DialogOverlay({ className, ...props }) {
703
+ return /* @__PURE__ */ jsx5(
704
+ DialogPrimitive.Overlay,
705
+ {
706
+ "data-slot": "dialog-overlay",
707
+ className: cn(
708
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-(--overlay)",
709
+ className
710
+ ),
711
+ ...props
712
+ }
713
+ );
714
+ }
715
+ function DialogContent({
716
+ className,
717
+ children,
718
+ showCloseButton = true,
719
+ ...props
720
+ }) {
721
+ return /* @__PURE__ */ jsxs3(DialogPortal, { "data-slot": "dialog-portal", children: [
722
+ /* @__PURE__ */ jsx5(DialogOverlay, {}),
723
+ /* @__PURE__ */ jsxs3(
724
+ DialogPrimitive.Content,
725
+ {
726
+ "data-slot": "dialog-content",
727
+ className: cn(
728
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border border-(--border-component-specific-card) bg-(--surface-component-specific-card-default-card) p-6 shadow-lg duration-200 sm:max-w-lg",
729
+ className
730
+ ),
731
+ ...props,
732
+ children: [
733
+ children,
734
+ showCloseButton && /* @__PURE__ */ jsxs3(
735
+ DialogPrimitive.Close,
736
+ {
737
+ "data-slot": "dialog-close",
738
+ className: "ring-offset-background focus:ring-ring absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-(--accent) data-[state=open]:text-(--secondary) [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
739
+ children: [
740
+ /* @__PURE__ */ jsx5(XIcon, {}),
741
+ /* @__PURE__ */ jsx5("span", { className: "sr-only", children: "Close" })
742
+ ]
743
+ }
744
+ )
745
+ ]
746
+ }
747
+ )
748
+ ] });
749
+ }
750
+ function DialogTitle({ className, ...props }) {
751
+ return /* @__PURE__ */ jsx5(
752
+ DialogPrimitive.Title,
753
+ {
754
+ "data-slot": "dialog-title",
755
+ className: cn("text-lg leading-none font-semibold", className),
756
+ ...props
757
+ }
758
+ );
759
+ }
760
+ function DialogDescription({ className, ...props }) {
761
+ return /* @__PURE__ */ jsx5(
762
+ DialogPrimitive.Description,
763
+ {
764
+ "data-slot": "dialog-description",
765
+ className: cn("text-sm text-(--secondary)", className),
766
+ ...props
767
+ }
768
+ );
769
+ }
770
+
771
+ export {
772
+ PaymentError,
773
+ PaymentType,
774
+ LogLevel,
775
+ logger,
776
+ configureLogger,
777
+ SpreePayProvider,
778
+ useSpreePay,
779
+ useSpreePaymentMethod,
780
+ useSpreePayEnv,
781
+ useSpreePayRegister,
782
+ StaticConfigProvider,
783
+ useStaticConfig,
784
+ cn,
785
+ PortalContainerProvider,
786
+ Dialog,
787
+ DialogContent,
788
+ DialogTitle,
789
+ DialogDescription,
790
+ registerApi,
791
+ SlapiPaymentService,
792
+ useSpreePayConfig,
793
+ InfoBanner,
794
+ Legal
795
+ };