@reevit/core 0.9.0 → 0.9.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/dist/index.mjs CHANGED
@@ -32,6 +32,95 @@ function generateIdempotencyKey(params) {
32
32
  const timeBucket = Math.floor(Date.now() / (5 * 60 * 1e3));
33
33
  return `reevit_${timeBucket}_${hashHex}`;
34
34
  }
35
+ var IDEMPOTENCY_STORE_PREFIX = "reevit:idem:";
36
+ var memoryAttemptKeys = /* @__PURE__ */ new Map();
37
+ function getSessionStore() {
38
+ try {
39
+ const storage = globalThis.sessionStorage;
40
+ if (!storage) {
41
+ return null;
42
+ }
43
+ const probe = `${IDEMPOTENCY_STORE_PREFIX}probe`;
44
+ storage.setItem(probe, "1");
45
+ storage.removeItem(probe);
46
+ return storage;
47
+ } catch {
48
+ return null;
49
+ }
50
+ }
51
+ function newIdempotencyKey() {
52
+ const cryptoObj = globalThis.crypto;
53
+ if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
54
+ try {
55
+ return cryptoObj.randomUUID();
56
+ } catch {
57
+ }
58
+ }
59
+ const bytes = new Uint8Array(16);
60
+ if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
61
+ cryptoObj.getRandomValues(bytes);
62
+ } else {
63
+ for (let i = 0; i < bytes.length; i++) {
64
+ bytes[i] = Math.floor(Math.random() * 256);
65
+ }
66
+ }
67
+ bytes[6] = bytes[6] & 15 | 64;
68
+ bytes[8] = bytes[8] & 63 | 128;
69
+ const hex = [];
70
+ for (let i = 0; i < bytes.length; i++) {
71
+ hex.push(bytes[i].toString(16).padStart(2, "0"));
72
+ }
73
+ return [
74
+ hex.slice(0, 4).join(""),
75
+ hex.slice(4, 6).join(""),
76
+ hex.slice(6, 8).join(""),
77
+ hex.slice(8, 10).join(""),
78
+ hex.slice(10, 16).join("")
79
+ ].join("-");
80
+ }
81
+ function attemptIdempotencyKey(lookupKey) {
82
+ const storageKey = `${IDEMPOTENCY_STORE_PREFIX}${lookupKey}`;
83
+ const store = getSessionStore();
84
+ if (store) {
85
+ try {
86
+ const existing2 = store.getItem(storageKey);
87
+ if (existing2) {
88
+ return existing2;
89
+ }
90
+ const created2 = newIdempotencyKey();
91
+ store.setItem(storageKey, created2);
92
+ return created2;
93
+ } catch {
94
+ }
95
+ }
96
+ const existing = memoryAttemptKeys.get(storageKey);
97
+ if (existing) {
98
+ return existing;
99
+ }
100
+ const created = newIdempotencyKey();
101
+ memoryAttemptKeys.set(storageKey, created);
102
+ return created;
103
+ }
104
+ function clearIdempotencyAttemptKeys() {
105
+ memoryAttemptKeys.clear();
106
+ const store = getSessionStore();
107
+ if (!store) {
108
+ return;
109
+ }
110
+ try {
111
+ const keys = [];
112
+ for (let i = 0; i < store.length; i++) {
113
+ const key = store.key(i);
114
+ if (key && key.startsWith(IDEMPOTENCY_STORE_PREFIX)) {
115
+ keys.push(key);
116
+ }
117
+ }
118
+ for (const key of keys) {
119
+ store.removeItem(key);
120
+ }
121
+ } catch {
122
+ }
123
+ }
35
124
  var ReevitAPIClient = class {
36
125
  constructor(config) {
37
126
  this.publicKey = config.publicKey || "";
@@ -48,13 +137,13 @@ var ReevitAPIClient = class {
48
137
  const headers = {
49
138
  "Content-Type": "application/json",
50
139
  "X-Reevit-Client": "@reevit/core",
51
- "X-Reevit-Client-Version": "0.9.0"
140
+ "X-Reevit-Client-Version": "0.9.1"
52
141
  };
53
142
  if (this.publicKey) {
54
143
  headers["X-Reevit-Key"] = this.publicKey;
55
144
  }
56
145
  if (method === "POST" || method === "PATCH" || method === "PUT") {
57
- headers["Idempotency-Key"] = idempotencyKey || (body ? generateIdempotencyKey(body) : `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`);
146
+ headers["Idempotency-Key"] = idempotencyKey || newIdempotencyKey();
58
147
  }
59
148
  try {
60
149
  const response = await fetch(`${this.baseUrl}${path}`, {
@@ -144,7 +233,7 @@ var ReevitAPIClient = class {
144
233
  allowed_providers: options?.allowedProviders
145
234
  };
146
235
  }
147
- const idempotencyKey = config.idempotencyKey || generateIdempotencyKey({
236
+ const idempotencyKey = config.idempotencyKey || attemptIdempotencyKey(generateIdempotencyKey({
148
237
  amount: config.amount,
149
238
  currency: config.currency,
150
239
  customer: config.email || config.metadata?.customerId || "",
@@ -152,7 +241,7 @@ var ReevitAPIClient = class {
152
241
  method: method || "",
153
242
  provider: options?.preferredProviders?.[0] || options?.allowedProviders?.[0] || "",
154
243
  publicKey: this.publicKey
155
- });
244
+ }));
156
245
  return this.request("POST", "/v1/payments/intents", request, idempotencyKey);
157
246
  }
158
247
  /**
@@ -221,25 +310,62 @@ function createReevitClient(config) {
221
310
  }
222
311
 
223
312
  // src/utils.ts
313
+ var CURRENCY_LOCALES = {
314
+ GHS: "en-GH",
315
+ NGN: "en-NG",
316
+ KES: "en-KE",
317
+ USD: "en-US",
318
+ EUR: "de-DE",
319
+ GBP: "en-GB"
320
+ };
321
+ var ZERO_DECIMAL_CURRENCIES = /* @__PURE__ */ new Set([
322
+ "XOF",
323
+ "XAF",
324
+ "RWF",
325
+ "UGX",
326
+ "JPY",
327
+ "KRW",
328
+ "BIF",
329
+ "GNF",
330
+ "VND",
331
+ "CLP",
332
+ "ISK",
333
+ "KMF",
334
+ "DJF",
335
+ "PYG",
336
+ "MGA"
337
+ ]);
338
+ function currencyExponent(currency) {
339
+ const code = (currency || "").toUpperCase();
340
+ try {
341
+ const digits = new Intl.NumberFormat("en", {
342
+ style: "currency",
343
+ currency: code
344
+ }).resolvedOptions().maximumFractionDigits;
345
+ if (typeof digits === "number" && Number.isFinite(digits)) {
346
+ return digits;
347
+ }
348
+ } catch {
349
+ }
350
+ return ZERO_DECIMAL_CURRENCIES.has(code) ? 0 : 2;
351
+ }
352
+ function toMinorUnits(major, currency) {
353
+ return Math.round(major * 10 ** currencyExponent(currency));
354
+ }
224
355
  function formatAmount(amount, currency) {
225
- const majorUnit = amount / 100;
226
- const currencyFormats = {
227
- GHS: { locale: "en-GH", minimumFractionDigits: 2 },
228
- NGN: { locale: "en-NG", minimumFractionDigits: 2 },
229
- KES: { locale: "en-KE", minimumFractionDigits: 2 },
230
- USD: { locale: "en-US", minimumFractionDigits: 2 },
231
- EUR: { locale: "de-DE", minimumFractionDigits: 2 },
232
- GBP: { locale: "en-GB", minimumFractionDigits: 2 }
233
- };
234
- const format = currencyFormats[currency.toUpperCase()] || { locale: "en-US", minimumFractionDigits: 2 };
356
+ const code = (currency || "").toUpperCase();
357
+ const exponent = currencyExponent(code);
358
+ const majorUnit = amount / 10 ** exponent;
359
+ const locale = CURRENCY_LOCALES[code] || "en-US";
235
360
  try {
236
- return new Intl.NumberFormat(format.locale, {
361
+ return new Intl.NumberFormat(locale, {
237
362
  style: "currency",
238
- currency: currency.toUpperCase(),
239
- minimumFractionDigits: format.minimumFractionDigits
363
+ currency: code,
364
+ minimumFractionDigits: exponent,
365
+ maximumFractionDigits: exponent
240
366
  }).format(majorUnit);
241
367
  } catch {
242
- return `${currency} ${majorUnit.toFixed(2)}`;
368
+ return `${code} ${majorUnit.toFixed(exponent)}`;
243
369
  }
244
370
  }
245
371
  function generateReference(prefix = "reevit") {
@@ -357,33 +483,50 @@ function detectCountryFromCurrency(currency) {
357
483
  // src/intent.ts
358
484
  var INTENT_CACHE_TTL_MS = 10 * 60 * 1e3;
359
485
  var intentCache = /* @__PURE__ */ new Map();
486
+ var lookupKeyByWireKey = /* @__PURE__ */ new Map();
487
+ function forgetKey(lookupKey) {
488
+ const entry = intentCache.get(lookupKey);
489
+ if (entry?.idempotencyKey) {
490
+ lookupKeyByWireKey.delete(entry.idempotencyKey);
491
+ }
492
+ intentCache.delete(lookupKey);
493
+ }
494
+ function toLookupKey(key) {
495
+ return lookupKeyByWireKey.get(key) ?? key;
496
+ }
360
497
  function pruneIntentCache(now = Date.now()) {
361
498
  for (const [key, entry] of intentCache) {
362
499
  if (entry.expiresAt <= now) {
363
- intentCache.delete(key);
500
+ forgetKey(key);
364
501
  }
365
502
  }
366
503
  }
367
- function getIntentCacheEntryInternal(key) {
368
- const entry = intentCache.get(key);
504
+ function getIntentCacheEntryInternal(lookupKey) {
505
+ const entry = intentCache.get(lookupKey);
369
506
  if (!entry) {
370
507
  return void 0;
371
508
  }
372
509
  if (entry.expiresAt <= Date.now()) {
373
- intentCache.delete(key);
510
+ forgetKey(lookupKey);
374
511
  return void 0;
375
512
  }
376
513
  return entry;
377
514
  }
378
- function setIntentCacheEntryInternal(key, update) {
515
+ function setIntentCacheEntryInternal(lookupKey, update) {
379
516
  const now = Date.now();
380
- const existing = getIntentCacheEntryInternal(key);
517
+ const existing = getIntentCacheEntryInternal(lookupKey);
381
518
  const next = {
382
519
  ...existing,
383
520
  ...update,
384
521
  expiresAt: now + INTENT_CACHE_TTL_MS
385
522
  };
386
- intentCache.set(key, next);
523
+ if (existing?.idempotencyKey && existing.idempotencyKey !== next.idempotencyKey) {
524
+ lookupKeyByWireKey.delete(existing.idempotencyKey);
525
+ }
526
+ intentCache.set(lookupKey, next);
527
+ if (next.idempotencyKey && next.idempotencyKey !== lookupKey) {
528
+ lookupKeyByWireKey.set(next.idempotencyKey, lookupKey);
529
+ }
387
530
  return next;
388
531
  }
389
532
  function buildIdempotencyPayload(options) {
@@ -416,24 +559,26 @@ function buildIdempotencyPayload(options) {
416
559
  }
417
560
  function resolveIntentIdentity(options) {
418
561
  pruneIntentCache();
419
- const idempotencyKey = options.config.idempotencyKey || generateIdempotencyKey(buildIdempotencyPayload(options));
420
- const existing = getIntentCacheEntryInternal(idempotencyKey);
562
+ const explicitKey = options.config.idempotencyKey;
563
+ const lookupKey = explicitKey || generateIdempotencyKey(buildIdempotencyPayload(options));
564
+ const idempotencyKey = explicitKey || attemptIdempotencyKey(lookupKey);
565
+ const existing = getIntentCacheEntryInternal(lookupKey);
421
566
  const reference = options.config.reference || existing?.reference || generateReference();
422
- const cacheEntry = setIntentCacheEntryInternal(idempotencyKey, { reference });
423
- return { idempotencyKey, reference, cacheEntry };
567
+ const cacheEntry = setIntentCacheEntryInternal(lookupKey, { reference, idempotencyKey });
568
+ return { idempotencyKey, lookupKey, reference, cacheEntry };
424
569
  }
425
- function getIntentCacheEntry(idempotencyKey) {
570
+ function getIntentCacheEntry(key) {
426
571
  pruneIntentCache();
427
- return getIntentCacheEntryInternal(idempotencyKey);
572
+ return getIntentCacheEntryInternal(toLookupKey(key));
428
573
  }
429
- function cacheIntentPromise(idempotencyKey, promise) {
430
- return setIntentCacheEntryInternal(idempotencyKey, { promise });
574
+ function cacheIntentPromise(key, promise) {
575
+ return setIntentCacheEntryInternal(toLookupKey(key), { promise });
431
576
  }
432
- function cacheIntentResponse(idempotencyKey, response) {
433
- return setIntentCacheEntryInternal(idempotencyKey, { response, promise: void 0 });
577
+ function cacheIntentResponse(key, response) {
578
+ return setIntentCacheEntryInternal(toLookupKey(key), { response, promise: void 0 });
434
579
  }
435
- function clearIntentCacheEntry(idempotencyKey) {
436
- intentCache.delete(idempotencyKey);
580
+ function clearIntentCacheEntry(key) {
581
+ forgetKey(toLookupKey(key));
437
582
  }
438
583
 
439
584
  // src/state.ts
@@ -477,14 +622,17 @@ function reevitReducer(state, action) {
477
622
  }
478
623
  export {
479
624
  ReevitAPIClient,
625
+ attemptIdempotencyKey,
480
626
  cacheIntentPromise,
481
627
  cacheIntentResponse,
628
+ clearIdempotencyAttemptKeys,
482
629
  clearIntentCacheEntry,
483
630
  cn,
484
631
  createInitialState,
485
632
  createPaymentError,
486
633
  createReevitClient,
487
634
  createThemeVariables,
635
+ currencyExponent,
488
636
  detectCountryFromCurrency,
489
637
  detectNetwork,
490
638
  formatAmount,
@@ -493,8 +641,10 @@ export {
493
641
  generateReference,
494
642
  getIntentCacheEntry,
495
643
  isPaymentError,
644
+ newIdempotencyKey,
496
645
  reevitReducer,
497
646
  resolveIntentIdentity,
647
+ toMinorUnits,
498
648
  validatePhone
499
649
  };
500
650
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/api/client.ts","../src/utils.ts","../src/intent.ts","../src/state.ts"],"sourcesContent":["/**\n * Reevit API Client\n * \n * Handles communication with the Reevit backend for payment operations.\n */\n\nimport type { PaymentMethod, ReevitCheckoutConfig, PaymentError, HubtelSessionResponse } from '../types';\n\n// API Response Types (matching backend handlers_payments.go)\nexport interface CreatePaymentIntentRequest {\n amount: number;\n currency: string;\n method?: string;\n country: string;\n customer_id?: string;\n metadata?: Record<string, unknown>;\n description?: string;\n policy?: {\n prefer?: string[];\n allowed_providers?: string[];\n max_amount?: number;\n blocked_bins?: string[];\n allowed_bins?: string[];\n velocity_max_per_minute?: number;\n };\n}\n\nexport interface PaymentIntentResponse {\n id: string;\n org_id?: string;\n connection_id: string;\n provider: string;\n provider_ref_id?: string;\n status: string;\n client_secret: string;\n session_secret?: string;\n psp_public_key: string;\n psp_credentials?: {\n merchantAccount?: string | number;\n basicAuth?: string;\n [key: string]: unknown;\n };\n amount: number;\n currency: string;\n fee_amount: number;\n fee_currency: string;\n net_amount: number;\n reference?: string;\n available_psps?: Array<{\n provider: string;\n name: string;\n methods: string[];\n countries?: string[];\n }>;\n branding?: Record<string, unknown>;\n}\n\nexport interface CheckoutSessionResponse {\n id: string;\n client_secret: string;\n session_secret: string;\n payment_intent: PaymentIntentResponse;\n expires_at?: string;\n}\n\nexport interface ConfirmPaymentRequest {\n provider_ref_id: string;\n provider_data?: Record<string, unknown>;\n}\n\nexport interface PaymentDetailResponse {\n id: string;\n connection_id: string;\n provider: string;\n method: string;\n status: string;\n amount: number;\n currency: string;\n fee_amount: number;\n fee_currency: string;\n net_amount: number;\n customer_id?: string;\n client_secret: string;\n provider_ref_id?: string;\n metadata?: Record<string, unknown>;\n created_at: string;\n updated_at: string;\n /** Payment source type (payment_link, api, subscription) */\n source?: 'payment_link' | 'api' | 'subscription';\n /** ID of the source (payment link ID, subscription ID, etc.) */\n source_id?: string;\n /** Human-readable description of the source (e.g., payment link name) */\n source_description?: string;\n}\n\nexport interface APIErrorResponse {\n code: string;\n message: string;\n details?: Record<string, unknown>;\n}\n\nexport type ReevitAPIResult<T> = { data: T; error?: never } | { data?: never; error: PaymentError };\n\n// API Client configuration\nexport interface ReevitAPIClientConfig {\n /** Your Reevit public key */\n publicKey?: string;\n /** Base URL for the Reevit API (defaults to production) */\n baseUrl?: string;\n /** Request timeout in milliseconds */\n timeout?: number;\n}\n\n// Default API base URLs\nconst API_BASE_URL_PRODUCTION = 'https://api.reevit.io';\nconst DEFAULT_TIMEOUT = 30000; // 30 seconds\nlet hasWarnedAboutLiveBrowserIntents = false;\n\n/**\n * Determines if a public key is for sandbox mode\n */\nexport function isSandboxKey(publicKey: string): boolean {\n return publicKey.startsWith('pfk_test_');\n}\n\n/**\n * Creates a PaymentError from an API error response\n */\nexport function createPaymentError(response: Response, errorData: APIErrorResponse): PaymentError {\n return {\n code: errorData.code || 'api_error',\n message: errorData.message || 'An unexpected error occurred',\n recoverable: isRecoverableStatus(response.status),\n details: {\n httpStatus: response.status,\n requestId: response.headers.get('x-request-id') || response.headers.get('x-reevit-request-id') || undefined,\n ...errorData.details,\n },\n };\n}\n\nexport function isPaymentError(error: unknown): error is PaymentError {\n return typeof error === 'object' && error !== null && 'code' in error && 'message' in error;\n}\n\nfunction isRecoverableStatus(status: number): boolean {\n return status === 408 || status === 409 || status === 425 || status === 429 || status >= 500;\n}\n\n/**\n * Generates a deterministic idempotency key based on input parameters\n * Uses a simple hash function suitable for browser environments\n * Exported for use by SDK hooks (e.g., payment link flows)\n */\nexport function generateIdempotencyKey(params: Record<string, unknown>): string {\n // Create a stable string representation of the parameters\n const sortedKeys = Object.keys(params).sort();\n const stableString = sortedKeys\n .map(key => `${key}:${JSON.stringify(params[key])}`)\n .join('|');\n\n // Simple hash function (djb2 algorithm)\n let hash = 5381;\n for (let i = 0; i < stableString.length; i++) {\n hash = ((hash << 5) + hash) + stableString.charCodeAt(i);\n hash = hash & hash; // Convert to 32-bit integer\n }\n\n // Convert to positive hex string\n const hashHex = (hash >>> 0).toString(16);\n\n // Add a time bucket (5-minute windows) to allow retries within a reasonable window\n // but prevent keys from being reused across completely different sessions\n const timeBucket = Math.floor(Date.now() / (5 * 60 * 1000));\n\n return `reevit_${timeBucket}_${hashHex}`;\n}\n\n/**\n * Reevit API Client\n */\nexport class ReevitAPIClient {\n private readonly publicKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n\n constructor(config: ReevitAPIClientConfig) {\n this.publicKey = config.publicKey || '';\n this.baseUrl = config.baseUrl || API_BASE_URL_PRODUCTION;\n this.timeout = config.timeout || DEFAULT_TIMEOUT;\n }\n\n /**\n * Makes an authenticated API request\n * @param idempotencyKey Optional deterministic idempotency key for the request\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown,\n idempotencyKey?: string\n ): Promise<ReevitAPIResult<T>> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n // Generate headers with idempotency key for mutating requests\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Reevit-Client': '@reevit/core',\n 'X-Reevit-Client-Version': '0.9.0',\n };\n if (this.publicKey) {\n headers['X-Reevit-Key'] = this.publicKey;\n }\n\n if (method === 'POST' || method === 'PATCH' || method === 'PUT') {\n // Use provided deterministic key, or generate one based on request body\n headers['Idempotency-Key'] = idempotencyKey ||\n (body ? generateIdempotencyKey(body as Record<string, unknown>) : `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`);\n }\n\n try {\n const response = await fetch(`${this.baseUrl}${path}`, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n const responseData = await response.json().catch(() => ({}));\n\n if (!response.ok) {\n return {\n error: createPaymentError(response, responseData as APIErrorResponse),\n };\n }\n\n return { data: responseData as T };\n } catch (err) {\n clearTimeout(timeoutId);\n\n if (err instanceof Error) {\n if (err.name === 'AbortError') {\n return {\n error: {\n code: 'request_timeout',\n message: 'The request timed out. Please try again.',\n recoverable: true,\n },\n };\n }\n\n if (err.message.includes('Failed to fetch') || err.message.includes('NetworkError')) {\n return {\n error: {\n code: 'network_error',\n message: 'Unable to connect to Reevit. Please check your internet connection.',\n recoverable: true,\n },\n };\n }\n }\n\n return {\n error: {\n code: 'unknown_error',\n message: 'An unexpected error occurred. Please try again.',\n recoverable: true,\n },\n };\n }\n }\n\n /**\n * Creates a payment intent\n */\n async createPaymentIntent(\n config: ReevitCheckoutConfig,\n method?: PaymentMethod,\n country: string = 'GH',\n options?: { preferredProviders?: string[]; allowedProviders?: string[] }\n ): Promise<{ data?: PaymentIntentResponse; error?: PaymentError }> {\n if (\n this.publicKey.startsWith('pfk_live_') &&\n !hasWarnedAboutLiveBrowserIntents &&\n typeof console !== 'undefined'\n ) {\n hasWarnedAboutLiveBrowserIntents = true;\n console.warn(\n 'Creating live payment intents from the browser is deprecated. Create a checkout session on your server and pass sessionSecret to the browser SDK instead.'\n );\n }\n\n if (typeof config.amount !== 'number' || !config.currency) {\n return {\n error: {\n code: 'invalid_checkout_config',\n message: 'amount and currency are required when creating a payment intent in the browser.',\n recoverable: false,\n },\n };\n }\n\n // Build metadata with customer_email for PSP providers that require it\n const metadata: Record<string, unknown> = { ...config.metadata };\n if (config.email) {\n metadata.customer_email = config.email;\n }\n if (config.phone) {\n metadata.customer_phone = config.phone;\n }\n\n const request: CreatePaymentIntentRequest = {\n amount: config.amount,\n currency: config.currency,\n country,\n customer_id: config.email || (config.metadata?.customerId as string | undefined),\n metadata,\n };\n\n if (method) {\n request.method = this.mapPaymentMethod(method);\n }\n\n if (options?.preferredProviders?.length || options?.allowedProviders?.length) {\n request.policy = {\n prefer: options?.preferredProviders,\n allowed_providers: options?.allowedProviders,\n };\n }\n\n // Generate a deterministic idempotency key based on payment parameters\n // This ensures that duplicate requests for the same payment return the same intent\n const idempotencyKey = config.idempotencyKey || generateIdempotencyKey({\n amount: config.amount,\n currency: config.currency,\n customer: config.email || config.metadata?.customerId || '',\n reference: config.reference || '',\n method: method || '',\n provider: options?.preferredProviders?.[0] || options?.allowedProviders?.[0] || '',\n publicKey: this.publicKey,\n });\n\n return this.request<PaymentIntentResponse>('POST', '/v1/payments/intents', request, idempotencyKey);\n }\n\n /**\n * Retrieves a payment intent by ID\n */\n async getPaymentIntent(paymentId: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('GET', `/v1/payments/${paymentId}`);\n }\n\n /**\n * Retrieves a server-created checkout session using its public session secret.\n */\n async getCheckoutSession(sessionSecret: string): Promise<{ data?: CheckoutSessionResponse; error?: PaymentError }> {\n return this.request<CheckoutSessionResponse>(\n 'GET',\n `/v1/checkout/sessions/${encodeURIComponent(sessionSecret)}`\n );\n }\n\n /**\n * Confirms a payment after PSP callback\n */\n async confirmPayment(paymentId: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('POST', `/v1/payments/${paymentId}/confirm`);\n }\n\n /**\n * Confirms a payment intent using client secret (public endpoint)\n */\n async confirmPaymentIntent(paymentId: string, clientSecret: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>(\n 'POST',\n `/v1/payments/${paymentId}/confirm-intent?client_secret=${encodeURIComponent(clientSecret)}`\n );\n }\n\n /**\n * Cancels a payment intent\n */\n async cancelPaymentIntent(paymentId: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('POST', `/v1/payments/${paymentId}/cancel`);\n }\n\n /**\n * Creates a Hubtel session token for secure checkout\n * Returns a short-lived token that contains Hubtel credentials\n * Credentials are never exposed to the client directly\n */\n async createHubtelSession(\n paymentId: string,\n clientSecret?: string\n ): Promise<{ data?: HubtelSessionResponse; error?: PaymentError }> {\n const query = clientSecret ? `?client_secret=${encodeURIComponent(clientSecret)}` : '';\n return this.request<HubtelSessionResponse>('POST', `/v1/payments/hubtel/sessions/${paymentId}${query}`);\n }\n\n /**\n * Maps SDK payment method to backend format\n */\n private mapPaymentMethod(method: PaymentMethod): string {\n switch (method) {\n case 'card':\n return 'card';\n case 'mobile_money':\n return 'mobile_money';\n case 'bank_transfer':\n return 'bank_transfer';\n default:\n return method;\n }\n }\n}\n\n/**\n * Creates a new Reevit API client instance\n */\nexport function createReevitClient(config: ReevitAPIClientConfig): ReevitAPIClient {\n return new ReevitAPIClient(config);\n}\n","/**\n * Utility Functions\n * Shared utilities for Reevit SDKs\n */\n\nimport type { MobileMoneyNetwork, ReevitTheme } from './types';\n\n/**\n * Formats an amount from smallest currency unit to display format\n */\nexport function formatAmount(amount: number, currency: string): string {\n const majorUnit = amount / 100;\n\n const currencyFormats: Record<string, { locale: string; minimumFractionDigits: number }> = {\n GHS: { locale: 'en-GH', minimumFractionDigits: 2 },\n NGN: { locale: 'en-NG', minimumFractionDigits: 2 },\n KES: { locale: 'en-KE', minimumFractionDigits: 2 },\n USD: { locale: 'en-US', minimumFractionDigits: 2 },\n EUR: { locale: 'de-DE', minimumFractionDigits: 2 },\n GBP: { locale: 'en-GB', minimumFractionDigits: 2 },\n };\n\n const format = currencyFormats[currency.toUpperCase()] || { locale: 'en-US', minimumFractionDigits: 2 };\n\n try {\n return new Intl.NumberFormat(format.locale, {\n style: 'currency',\n currency: currency.toUpperCase(),\n minimumFractionDigits: format.minimumFractionDigits,\n }).format(majorUnit);\n } catch {\n // Fallback for unsupported currencies\n return `${currency} ${majorUnit.toFixed(2)}`;\n }\n}\n\n/**\n * Generates a unique payment reference\n */\nexport function generateReference(prefix: string = 'reevit'): string {\n const timestamp = Date.now().toString(36);\n const random = Math.random().toString(36).substring(2, 8);\n return `${prefix}_${timestamp}_${random}`;\n}\n\n/**\n * Validates a phone number for mobile money\n */\nexport function validatePhone(phone: string, country: string = 'GH'): boolean {\n // Remove non-digit characters\n const digits = phone.replace(/\\D/g, '');\n\n const patterns: Record<string, RegExp> = {\n GH: /^(?:233|0)?[235][0-9]{8}$/, // Ghana\n NG: /^(?:234|0)?[789][01][0-9]{8}$/, // Nigeria\n KE: /^(?:254|0)?[17][0-9]{8}$/, // Kenya\n };\n\n const pattern = patterns[country.toUpperCase()];\n if (!pattern) return digits.length >= 10;\n\n return pattern.test(digits);\n}\n\n/**\n * Formats a phone number for display\n */\nexport function formatPhone(phone: string, country: string = 'GH'): string {\n const digits = phone.replace(/\\D/g, '');\n\n if (country === 'GH') {\n // Format as 0XX XXX XXXX\n if (digits.startsWith('233') && digits.length === 12) {\n const local = '0' + digits.slice(3);\n return `${local.slice(0, 3)} ${local.slice(3, 6)} ${local.slice(6)}`;\n }\n if (digits.length === 10 && digits.startsWith('0')) {\n return `${digits.slice(0, 3)} ${digits.slice(3, 6)} ${digits.slice(6)}`;\n }\n }\n\n return phone;\n}\n\n/**\n * Detects mobile money network from phone number (Ghana)\n */\nexport function detectNetwork(phone: string): MobileMoneyNetwork | null {\n const digits = phone.replace(/\\D/g, '');\n\n // Get the network prefix (first 3 digits after country code or 0)\n let prefix: string;\n if (digits.startsWith('233')) {\n prefix = digits.slice(3, 5);\n } else if (digits.startsWith('0')) {\n prefix = digits.slice(1, 3);\n } else {\n prefix = digits.slice(0, 2);\n }\n\n // Ghana network prefixes\n const mtnPrefixes = ['24', '25', '53', '54', '55', '59'];\n const telecelPrefixes = ['20', '50'];\n const airteltigoPrefixes = ['26', '27', '56', '57'];\n\n if (mtnPrefixes.includes(prefix)) return 'mtn';\n if (telecelPrefixes.includes(prefix)) return 'telecel';\n if (airteltigoPrefixes.includes(prefix)) return 'airteltigo';\n\n return null;\n}\n\n/**\n * Creates CSS custom property variables from theme\n */\nexport function createThemeVariables(theme: ReevitTheme): Record<string, string> {\n const variables: Record<string, string> = {};\n\n // Primary color = main text color\n if (theme.primaryColor) {\n variables['--reevit-text'] = theme.primaryColor;\n }\n\n // Primary foreground = description/secondary text color\n if (theme.primaryForegroundColor) {\n variables['--reevit-text-secondary'] = theme.primaryForegroundColor;\n variables['--reevit-muted'] = theme.primaryForegroundColor;\n }\n\n // Button colors\n if (theme.buttonBackgroundColor) {\n variables['--reevit-primary'] = theme.buttonBackgroundColor;\n variables['--reevit-primary-hover'] = theme.buttonBackgroundColor;\n }\n if (theme.buttonTextColor) {\n variables['--reevit-primary-foreground'] = theme.buttonTextColor;\n }\n\n // Background and surface colors\n if (theme.backgroundColor) {\n variables['--reevit-background'] = theme.backgroundColor;\n variables['--reevit-surface'] = theme.backgroundColor;\n }\n if (theme.surfaceColor) {\n variables['--reevit-surface'] = theme.surfaceColor;\n }\n\n // Border color\n if (theme.borderColor) {\n variables['--reevit-border'] = theme.borderColor;\n }\n\n // Legacy text color support\n if (theme.textColor) {\n variables['--reevit-text'] = theme.textColor;\n }\n if (theme.mutedTextColor) {\n variables['--reevit-text-secondary'] = theme.mutedTextColor;\n }\n\n // Border radius\n if (theme.borderRadius) {\n variables['--reevit-radius'] = theme.borderRadius;\n variables['--reevit-radius-sm'] = theme.borderRadius;\n variables['--reevit-radius-lg'] = theme.borderRadius;\n }\n\n // Font family\n if (theme.fontFamily) {\n variables['--reevit-font'] = theme.fontFamily;\n }\n\n return variables;\n}\n\nfunction getContrastingColor(color: string): string | null {\n const hex = color.trim();\n if (!hex.startsWith('#')) {\n return null;\n }\n\n const normalized = hex.length === 4\n ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}`\n : hex;\n\n if (normalized.length !== 7) {\n return null;\n }\n\n const r = parseInt(normalized.slice(1, 3), 16);\n const g = parseInt(normalized.slice(3, 5), 16);\n const b = parseInt(normalized.slice(5, 7), 16);\n\n if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {\n return null;\n }\n\n const brightness = (r * 299 + g * 587 + b * 114) / 1000;\n return brightness >= 140 ? '#0b1120' : '#ffffff';\n}\n\n/**\n * Simple class name concatenation utility\n */\nexport function cn(...classes: (string | boolean | undefined | null)[]): string {\n return classes.filter(Boolean).join(' ');\n}\n\n/**\n * Detects country code from currency\n */\nexport function detectCountryFromCurrency(currency: string): string {\n const currencyToCountry: Record<string, string> = {\n GHS: 'GH',\n NGN: 'NG',\n KES: 'KE',\n UGX: 'UG',\n TZS: 'TZ',\n ZAR: 'ZA',\n XOF: 'CI',\n XAF: 'CM',\n USD: 'US',\n EUR: 'DE',\n GBP: 'GB',\n };\n\n return currencyToCountry[currency.toUpperCase()] || 'GH';\n}\n","/**\n * Intent identity + cache helpers\n */\n\nimport type { PaymentIntentResponse } from './api/client';\nimport { generateIdempotencyKey } from './api/client';\nimport type { PaymentMethod, ReevitCheckoutConfig } from './types';\nimport { generateReference } from './utils';\n\nconst INTENT_CACHE_TTL_MS = 10 * 60 * 1000; // 10 minutes\n\nexport interface IntentIdentityOptions {\n config: ReevitCheckoutConfig;\n method?: PaymentMethod;\n preferredProvider?: string;\n allowedProviders?: string[];\n publicKey?: string;\n}\n\nexport interface IntentCacheEntry {\n promise?: Promise<PaymentIntentResponse>;\n response?: PaymentIntentResponse;\n expiresAt: number;\n reference?: string;\n}\n\nconst intentCache = new Map<string, IntentCacheEntry>();\n\nfunction pruneIntentCache(now: number = Date.now()): void {\n for (const [key, entry] of intentCache) {\n if (entry.expiresAt <= now) {\n intentCache.delete(key);\n }\n }\n}\n\nfunction getIntentCacheEntryInternal(key: string): IntentCacheEntry | undefined {\n const entry = intentCache.get(key);\n if (!entry) {\n return undefined;\n }\n if (entry.expiresAt <= Date.now()) {\n intentCache.delete(key);\n return undefined;\n }\n return entry;\n}\n\nfunction setIntentCacheEntryInternal(key: string, update: Partial<IntentCacheEntry>): IntentCacheEntry {\n const now = Date.now();\n const existing = getIntentCacheEntryInternal(key);\n const next: IntentCacheEntry = {\n ...existing,\n ...update,\n expiresAt: now + INTENT_CACHE_TTL_MS,\n };\n intentCache.set(key, next);\n return next;\n}\n\nfunction buildIdempotencyPayload(options: IntentIdentityOptions): Record<string, unknown> {\n const { config, method, preferredProvider, allowedProviders, publicKey } = options;\n if (config.sessionSecret) {\n return {\n sessionSecret: config.sessionSecret,\n publicKey: publicKey || config.publicKey || '',\n };\n }\n\n const payload: Record<string, unknown> = {\n amount: config.amount,\n currency: config.currency,\n email: config.email || '',\n phone: config.phone || '',\n customerName: config.customerName || '',\n paymentLinkCode: config.paymentLinkCode || '',\n paymentMethods: config.paymentMethods || [],\n metadata: config.metadata || {},\n customFields: config.customFields || {},\n method: method || '',\n preferredProvider: preferredProvider || '',\n allowedProviders: allowedProviders || [],\n publicKey: publicKey || config.publicKey || '',\n };\n\n if (config.reference) {\n payload.reference = config.reference;\n }\n\n return payload;\n}\n\nexport function resolveIntentIdentity(options: IntentIdentityOptions): {\n idempotencyKey: string;\n reference: string;\n cacheEntry?: IntentCacheEntry;\n} {\n pruneIntentCache();\n\n const idempotencyKey =\n options.config.idempotencyKey || generateIdempotencyKey(buildIdempotencyPayload(options));\n const existing = getIntentCacheEntryInternal(idempotencyKey);\n const reference = options.config.reference || existing?.reference || generateReference();\n\n const cacheEntry = setIntentCacheEntryInternal(idempotencyKey, { reference });\n\n return { idempotencyKey, reference, cacheEntry };\n}\n\nexport function getIntentCacheEntry(idempotencyKey: string): IntentCacheEntry | undefined {\n pruneIntentCache();\n return getIntentCacheEntryInternal(idempotencyKey);\n}\n\nexport function cacheIntentPromise(\n idempotencyKey: string,\n promise: Promise<PaymentIntentResponse>\n): IntentCacheEntry {\n return setIntentCacheEntryInternal(idempotencyKey, { promise });\n}\n\nexport function cacheIntentResponse(\n idempotencyKey: string,\n response: PaymentIntentResponse\n): IntentCacheEntry {\n return setIntentCacheEntryInternal(idempotencyKey, { response, promise: undefined });\n}\n\nexport function clearIntentCacheEntry(idempotencyKey: string): void {\n intentCache.delete(idempotencyKey);\n}\n","/**\n * Reevit State Machine\n * Shared state management logic for all SDKs\n */\n\nimport type { CheckoutState, PaymentIntent, PaymentMethod, PaymentResult, PaymentError } from './types';\n\n// State shape\nexport interface ReevitState {\n status: CheckoutState;\n paymentIntent: PaymentIntent | null;\n selectedMethod: PaymentMethod | null;\n error: PaymentError | null;\n result: PaymentResult | null;\n}\n\n// Actions\nexport type ReevitAction =\n | { type: 'INIT_START' }\n | { type: 'INIT_SUCCESS'; payload: PaymentIntent }\n | { type: 'INIT_ERROR'; payload: PaymentError }\n | { type: 'SELECT_METHOD'; payload: PaymentMethod }\n | { type: 'PROCESS_START' }\n | { type: 'PROCESS_SUCCESS'; payload: PaymentResult }\n | { type: 'PROCESS_ERROR'; payload: PaymentError }\n | { type: 'RESET' }\n | { type: 'CLOSE' };\n\n/**\n * Creates the initial state for the checkout\n */\nexport function createInitialState(): ReevitState {\n return {\n status: 'idle',\n paymentIntent: null,\n selectedMethod: null,\n error: null,\n result: null,\n };\n}\n\n/**\n * State reducer for checkout flow\n */\nexport function reevitReducer(state: ReevitState, action: ReevitAction): ReevitState {\n switch (action.type) {\n case 'INIT_START':\n return { ...state, status: 'loading', error: null };\n case 'INIT_SUCCESS':\n return {\n ...state,\n status: 'ready',\n paymentIntent: action.payload,\n selectedMethod:\n action.payload.availableMethods?.length === 1 ? action.payload.availableMethods[0] : null,\n };\n case 'INIT_ERROR':\n return { ...state, status: 'failed', error: action.payload };\n case 'SELECT_METHOD':\n return { ...state, status: 'method_selected', selectedMethod: action.payload };\n case 'PROCESS_START':\n return { ...state, status: 'processing', error: null };\n case 'PROCESS_SUCCESS':\n return { ...state, status: 'success', result: action.payload };\n case 'PROCESS_ERROR':\n return { ...state, status: 'failed', error: action.payload };\n case 'RESET':\n return { ...createInitialState(), status: 'ready', paymentIntent: state.paymentIntent };\n case 'CLOSE':\n return { ...state, status: 'closed' };\n default:\n return state;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/api/client.ts","../src/utils.ts","../src/intent.ts","../src/state.ts"],"sourcesContent":["/**\n * Reevit API Client\n * \n * Handles communication with the Reevit backend for payment operations.\n */\n\nimport type { PaymentMethod, ReevitCheckoutConfig, PaymentError, HubtelSessionResponse } from '../types';\n\n// API Response Types (matching backend handlers_payments.go)\nexport interface CreatePaymentIntentRequest {\n amount: number;\n currency: string;\n method?: string;\n country: string;\n customer_id?: string;\n metadata?: Record<string, unknown>;\n description?: string;\n policy?: {\n prefer?: string[];\n allowed_providers?: string[];\n max_amount?: number;\n blocked_bins?: string[];\n allowed_bins?: string[];\n velocity_max_per_minute?: number;\n };\n}\n\nexport interface PaymentIntentResponse {\n id: string;\n org_id?: string;\n connection_id: string;\n provider: string;\n provider_ref_id?: string;\n status: string;\n client_secret: string;\n session_secret?: string;\n psp_public_key: string;\n psp_credentials?: {\n merchantAccount?: string | number;\n basicAuth?: string;\n [key: string]: unknown;\n };\n amount: number;\n currency: string;\n fee_amount: number;\n fee_currency: string;\n net_amount: number;\n reference?: string;\n available_psps?: Array<{\n provider: string;\n name: string;\n methods: string[];\n countries?: string[];\n }>;\n branding?: Record<string, unknown>;\n}\n\nexport interface CheckoutSessionResponse {\n id: string;\n client_secret: string;\n session_secret: string;\n payment_intent: PaymentIntentResponse;\n expires_at?: string;\n}\n\nexport interface ConfirmPaymentRequest {\n provider_ref_id: string;\n provider_data?: Record<string, unknown>;\n}\n\nexport interface PaymentDetailResponse {\n id: string;\n connection_id: string;\n provider: string;\n method: string;\n status: string;\n amount: number;\n currency: string;\n fee_amount: number;\n fee_currency: string;\n net_amount: number;\n customer_id?: string;\n client_secret: string;\n provider_ref_id?: string;\n metadata?: Record<string, unknown>;\n created_at: string;\n updated_at: string;\n /** Payment source type (payment_link, api, subscription) */\n source?: 'payment_link' | 'api' | 'subscription';\n /** ID of the source (payment link ID, subscription ID, etc.) */\n source_id?: string;\n /** Human-readable description of the source (e.g., payment link name) */\n source_description?: string;\n}\n\nexport interface APIErrorResponse {\n code: string;\n message: string;\n details?: Record<string, unknown>;\n}\n\nexport type ReevitAPIResult<T> = { data: T; error?: never } | { data?: never; error: PaymentError };\n\n// API Client configuration\nexport interface ReevitAPIClientConfig {\n /** Your Reevit public key */\n publicKey?: string;\n /** Base URL for the Reevit API (defaults to production) */\n baseUrl?: string;\n /** Request timeout in milliseconds */\n timeout?: number;\n}\n\n// Default API base URLs\nconst API_BASE_URL_PRODUCTION = 'https://api.reevit.io';\nconst DEFAULT_TIMEOUT = 30000; // 30 seconds\nlet hasWarnedAboutLiveBrowserIntents = false;\n\n/**\n * Determines if a public key is for sandbox mode\n */\nexport function isSandboxKey(publicKey: string): boolean {\n return publicKey.startsWith('pfk_test_');\n}\n\n/**\n * Creates a PaymentError from an API error response\n */\nexport function createPaymentError(response: Response, errorData: APIErrorResponse): PaymentError {\n return {\n code: errorData.code || 'api_error',\n message: errorData.message || 'An unexpected error occurred',\n recoverable: isRecoverableStatus(response.status),\n details: {\n httpStatus: response.status,\n requestId: response.headers.get('x-request-id') || response.headers.get('x-reevit-request-id') || undefined,\n ...errorData.details,\n },\n };\n}\n\nexport function isPaymentError(error: unknown): error is PaymentError {\n return typeof error === 'object' && error !== null && 'code' in error && 'message' in error;\n}\n\nexport function isRecoverableStatus(status: number): boolean {\n return status === 408 || status === 409 || status === 425 || status === 429 || status >= 500;\n}\n\n/**\n * Generates a deterministic **cache/lookup** key from input parameters.\n *\n * NEVER SEND THIS ON THE WIRE. It is a 32-bit djb2 hash bucketed into\n * 5-minute windows, so two unrelated shoppers can collide and be handed each\n * other's payment intent (and therefore each other's `client_secret`), and a\n * shopper legitimately buying the same item twice inside one window would be\n * charged once. Its only job is to identify \"the same checkout attempt\" inside\n * a single browser tab so the in-flight intent cache can dedupe a repeated\n * \"Continue\" click.\n *\n * The value actually sent as `Idempotency-Key` is produced by\n * {@link newIdempotencyKey} / {@link attemptIdempotencyKey}.\n *\n * Exported for use by SDK hooks (e.g. payment link flows).\n */\nexport function generateIdempotencyKey(params: Record<string, unknown>): string {\n // Create a stable string representation of the parameters\n const sortedKeys = Object.keys(params).sort();\n const stableString = sortedKeys\n .map(key => `${key}:${JSON.stringify(params[key])}`)\n .join('|');\n\n // Simple hash function (djb2 algorithm)\n let hash = 5381;\n for (let i = 0; i < stableString.length; i++) {\n hash = ((hash << 5) + hash) + stableString.charCodeAt(i);\n hash = hash & hash; // Convert to 32-bit integer\n }\n\n // Convert to positive hex string\n const hashHex = (hash >>> 0).toString(16);\n\n // Add a time bucket (5-minute windows) to allow retries within a reasonable window\n // but prevent keys from being reused across completely different sessions\n const timeBucket = Math.floor(Date.now() / (5 * 60 * 1000));\n\n return `reevit_${timeBucket}_${hashHex}`;\n}\n\nconst IDEMPOTENCY_STORE_PREFIX = 'reevit:idem:';\n\n/** Fallback store for SSR / privacy mode, where `sessionStorage` is unusable. */\nconst memoryAttemptKeys = new Map<string, string>();\n\nfunction getSessionStore(): Storage | null {\n try {\n const storage = (globalThis as { sessionStorage?: Storage }).sessionStorage;\n if (!storage) {\n return null;\n }\n // Safari private mode and some embedded webviews throw on write.\n const probe = `${IDEMPOTENCY_STORE_PREFIX}probe`;\n storage.setItem(probe, '1');\n storage.removeItem(probe);\n return storage;\n } catch {\n return null;\n }\n}\n\n/**\n * Generates a fresh, globally unique `Idempotency-Key` (RFC 4122 v4 UUID).\n * This is the only value that should ever be sent on the wire.\n */\nexport function newIdempotencyKey(): string {\n const cryptoObj = (globalThis as { crypto?: Crypto }).crypto;\n\n if (cryptoObj && typeof cryptoObj.randomUUID === 'function') {\n try {\n return cryptoObj.randomUUID();\n } catch {\n // fall through to the manual generator\n }\n }\n\n const bytes = new Uint8Array(16);\n if (cryptoObj && typeof cryptoObj.getRandomValues === 'function') {\n cryptoObj.getRandomValues(bytes);\n } else {\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n\n bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4\n bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 10\n\n const hex: string[] = [];\n for (let i = 0; i < bytes.length; i++) {\n hex.push(bytes[i].toString(16).padStart(2, '0'));\n }\n\n return [\n hex.slice(0, 4).join(''),\n hex.slice(4, 6).join(''),\n hex.slice(6, 8).join(''),\n hex.slice(8, 10).join(''),\n hex.slice(10, 16).join(''),\n ].join('-');\n}\n\n/**\n * Resolves the stable per-checkout-attempt wire key for a deterministic\n * lookup key (see {@link generateIdempotencyKey}).\n *\n * The first call for a lookup key mints a UUID and stores it in\n * `sessionStorage` (falling back to a module-level map when storage is\n * unavailable); every later call in the same tab returns that same UUID, so a\n * repeated \"Continue\" click is still deduped by the backend. A different tab,\n * a different shopper or a cleared store yields a different UUID.\n */\nexport function attemptIdempotencyKey(lookupKey: string): string {\n const storageKey = `${IDEMPOTENCY_STORE_PREFIX}${lookupKey}`;\n const store = getSessionStore();\n\n if (store) {\n try {\n const existing = store.getItem(storageKey);\n if (existing) {\n return existing;\n }\n const created = newIdempotencyKey();\n store.setItem(storageKey, created);\n return created;\n } catch {\n // fall through to the in-memory store\n }\n }\n\n const existing = memoryAttemptKeys.get(storageKey);\n if (existing) {\n return existing;\n }\n const created = newIdempotencyKey();\n memoryAttemptKeys.set(storageKey, created);\n return created;\n}\n\n/**\n * Forgets every stored per-attempt key, so the next checkout attempt gets a\n * fresh `Idempotency-Key`. Call it after a completed checkout (and in tests).\n */\nexport function clearIdempotencyAttemptKeys(): void {\n memoryAttemptKeys.clear();\n\n const store = getSessionStore();\n if (!store) {\n return;\n }\n\n try {\n const keys: string[] = [];\n for (let i = 0; i < store.length; i++) {\n const key = store.key(i);\n if (key && key.startsWith(IDEMPOTENCY_STORE_PREFIX)) {\n keys.push(key);\n }\n }\n for (const key of keys) {\n store.removeItem(key);\n }\n } catch {\n // nothing else we can do\n }\n}\n\n/**\n * Reevit API Client\n */\nexport class ReevitAPIClient {\n private readonly publicKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n\n constructor(config: ReevitAPIClientConfig) {\n this.publicKey = config.publicKey || '';\n this.baseUrl = config.baseUrl || API_BASE_URL_PRODUCTION;\n this.timeout = config.timeout || DEFAULT_TIMEOUT;\n }\n\n /**\n * Makes an authenticated API request\n * @param idempotencyKey Optional deterministic idempotency key for the request\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown,\n idempotencyKey?: string\n ): Promise<ReevitAPIResult<T>> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n // Generate headers with idempotency key for mutating requests\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Reevit-Client': '@reevit/core',\n 'X-Reevit-Client-Version': '0.9.1',\n };\n if (this.publicKey) {\n headers['X-Reevit-Key'] = this.publicKey;\n }\n\n if (method === 'POST' || method === 'PATCH' || method === 'PUT') {\n // Never derive the wire key from the request body: a body hash collides\n // across unrelated shoppers. Fall back to a fresh UUID instead.\n headers['Idempotency-Key'] = idempotencyKey || newIdempotencyKey();\n }\n\n try {\n const response = await fetch(`${this.baseUrl}${path}`, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n const responseData = await response.json().catch(() => ({}));\n\n if (!response.ok) {\n return {\n error: createPaymentError(response, responseData as APIErrorResponse),\n };\n }\n\n return { data: responseData as T };\n } catch (err) {\n clearTimeout(timeoutId);\n\n if (err instanceof Error) {\n if (err.name === 'AbortError') {\n return {\n error: {\n code: 'request_timeout',\n message: 'The request timed out. Please try again.',\n recoverable: true,\n },\n };\n }\n\n if (err.message.includes('Failed to fetch') || err.message.includes('NetworkError')) {\n return {\n error: {\n code: 'network_error',\n message: 'Unable to connect to Reevit. Please check your internet connection.',\n recoverable: true,\n },\n };\n }\n }\n\n return {\n error: {\n code: 'unknown_error',\n message: 'An unexpected error occurred. Please try again.',\n recoverable: true,\n },\n };\n }\n }\n\n /**\n * Creates a payment intent\n */\n async createPaymentIntent(\n config: ReevitCheckoutConfig,\n method?: PaymentMethod,\n country: string = 'GH',\n options?: { preferredProviders?: string[]; allowedProviders?: string[] }\n ): Promise<{ data?: PaymentIntentResponse; error?: PaymentError }> {\n if (\n this.publicKey.startsWith('pfk_live_') &&\n !hasWarnedAboutLiveBrowserIntents &&\n typeof console !== 'undefined'\n ) {\n hasWarnedAboutLiveBrowserIntents = true;\n console.warn(\n 'Creating live payment intents from the browser is deprecated. Create a checkout session on your server and pass sessionSecret to the browser SDK instead.'\n );\n }\n\n if (typeof config.amount !== 'number' || !config.currency) {\n return {\n error: {\n code: 'invalid_checkout_config',\n message: 'amount and currency are required when creating a payment intent in the browser.',\n recoverable: false,\n },\n };\n }\n\n // Build metadata with customer_email for PSP providers that require it\n const metadata: Record<string, unknown> = { ...config.metadata };\n if (config.email) {\n metadata.customer_email = config.email;\n }\n if (config.phone) {\n metadata.customer_phone = config.phone;\n }\n\n const request: CreatePaymentIntentRequest = {\n amount: config.amount,\n currency: config.currency,\n country,\n customer_id: config.email || (config.metadata?.customerId as string | undefined),\n metadata,\n };\n\n if (method) {\n request.method = this.mapPaymentMethod(method);\n }\n\n if (options?.preferredProviders?.length || options?.allowedProviders?.length) {\n request.policy = {\n prefer: options?.preferredProviders,\n allowed_providers: options?.allowedProviders,\n };\n }\n\n // The deterministic hash identifies this checkout attempt *locally*; the\n // key we put on the wire is a UUID minted once per attempt and reused for\n // the life of the tab, so a repeated \"Continue\" click still dedupes while\n // two unrelated shoppers can never share a key.\n const idempotencyKey = config.idempotencyKey || attemptIdempotencyKey(generateIdempotencyKey({\n amount: config.amount,\n currency: config.currency,\n customer: config.email || config.metadata?.customerId || '',\n reference: config.reference || '',\n method: method || '',\n provider: options?.preferredProviders?.[0] || options?.allowedProviders?.[0] || '',\n publicKey: this.publicKey,\n }));\n\n return this.request<PaymentIntentResponse>('POST', '/v1/payments/intents', request, idempotencyKey);\n }\n\n /**\n * Retrieves a payment intent by ID\n */\n async getPaymentIntent(paymentId: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('GET', `/v1/payments/${paymentId}`);\n }\n\n /**\n * Retrieves a server-created checkout session using its public session secret.\n */\n async getCheckoutSession(sessionSecret: string): Promise<{ data?: CheckoutSessionResponse; error?: PaymentError }> {\n return this.request<CheckoutSessionResponse>(\n 'GET',\n `/v1/checkout/sessions/${encodeURIComponent(sessionSecret)}`\n );\n }\n\n /**\n * Confirms a payment after PSP callback\n */\n async confirmPayment(paymentId: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('POST', `/v1/payments/${paymentId}/confirm`);\n }\n\n /**\n * Confirms a payment intent using client secret (public endpoint)\n */\n async confirmPaymentIntent(paymentId: string, clientSecret: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>(\n 'POST',\n `/v1/payments/${paymentId}/confirm-intent?client_secret=${encodeURIComponent(clientSecret)}`\n );\n }\n\n /**\n * Cancels a payment intent\n */\n async cancelPaymentIntent(paymentId: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('POST', `/v1/payments/${paymentId}/cancel`);\n }\n\n /**\n * Creates a Hubtel session token for secure checkout\n * Returns a short-lived token that contains Hubtel credentials\n * Credentials are never exposed to the client directly\n */\n async createHubtelSession(\n paymentId: string,\n clientSecret?: string\n ): Promise<{ data?: HubtelSessionResponse; error?: PaymentError }> {\n const query = clientSecret ? `?client_secret=${encodeURIComponent(clientSecret)}` : '';\n return this.request<HubtelSessionResponse>('POST', `/v1/payments/hubtel/sessions/${paymentId}${query}`);\n }\n\n /**\n * Maps SDK payment method to backend format\n */\n private mapPaymentMethod(method: PaymentMethod): string {\n switch (method) {\n case 'card':\n return 'card';\n case 'mobile_money':\n return 'mobile_money';\n case 'bank_transfer':\n return 'bank_transfer';\n default:\n return method;\n }\n }\n}\n\n/**\n * Creates a new Reevit API client instance\n */\nexport function createReevitClient(config: ReevitAPIClientConfig): ReevitAPIClient {\n return new ReevitAPIClient(config);\n}\n","/**\n * Utility Functions\n * Shared utilities for Reevit SDKs\n */\n\nimport type { MobileMoneyNetwork, ReevitTheme } from './types';\n\nconst CURRENCY_LOCALES: Record<string, string> = {\n GHS: 'en-GH',\n NGN: 'en-NG',\n KES: 'en-KE',\n USD: 'en-US',\n EUR: 'de-DE',\n GBP: 'en-GB',\n};\n\n/**\n * Currencies with no minor unit — the API's integer amount *is* the amount.\n * Used only when `Intl` cannot tell us (old or trimmed ICU builds).\n * Keep in sync with the reevit CLI's copy of this table.\n */\nconst ZERO_DECIMAL_CURRENCIES = new Set([\n 'XOF', 'XAF', 'RWF', 'UGX', 'JPY', 'KRW', 'BIF', 'GNF',\n 'VND', 'CLP', 'ISK', 'KMF', 'DJF', 'PYG', 'MGA',\n]);\n\n/**\n * Returns how many decimal places a currency's minor unit uses: 2 for GHS and\n * NGN, 0 for XOF, XAF, RWF, UGX, JPY and friends.\n *\n * Dividing every amount by 100 renders a 5,000 XOF charge as \"XOF 50.00\" while\n * the shopper is actually charged 5,000 — which is why this exists.\n */\nexport function currencyExponent(currency: string): number {\n const code = (currency || '').toUpperCase();\n\n try {\n const digits = new Intl.NumberFormat('en', {\n style: 'currency',\n currency: code,\n }).resolvedOptions().maximumFractionDigits;\n\n if (typeof digits === 'number' && Number.isFinite(digits)) {\n return digits;\n }\n } catch {\n // Unsupported currency code, or an ICU build without currency data.\n }\n\n return ZERO_DECIMAL_CURRENCIES.has(code) ? 0 : 2;\n}\n\n/**\n * Converts a major-unit amount (what a shopper types) into the minor units the\n * API expects: `toMinorUnits(45, 'GHS') === 4500`, `toMinorUnits(5000, 'XOF') === 5000`.\n */\nexport function toMinorUnits(major: number, currency: string): number {\n return Math.round(major * 10 ** currencyExponent(currency));\n}\n\n/**\n * Formats an amount from smallest currency unit to display format\n */\nexport function formatAmount(amount: number, currency: string): string {\n const code = (currency || '').toUpperCase();\n const exponent = currencyExponent(code);\n const majorUnit = amount / 10 ** exponent;\n const locale = CURRENCY_LOCALES[code] || 'en-US';\n\n try {\n return new Intl.NumberFormat(locale, {\n style: 'currency',\n currency: code,\n minimumFractionDigits: exponent,\n maximumFractionDigits: exponent,\n }).format(majorUnit);\n } catch {\n // Fallback for unsupported currencies\n return `${code} ${majorUnit.toFixed(exponent)}`;\n }\n}\n\n/**\n * Generates a unique payment reference\n */\nexport function generateReference(prefix: string = 'reevit'): string {\n const timestamp = Date.now().toString(36);\n const random = Math.random().toString(36).substring(2, 8);\n return `${prefix}_${timestamp}_${random}`;\n}\n\n/**\n * Validates a phone number for mobile money\n */\nexport function validatePhone(phone: string, country: string = 'GH'): boolean {\n // Remove non-digit characters\n const digits = phone.replace(/\\D/g, '');\n\n const patterns: Record<string, RegExp> = {\n GH: /^(?:233|0)?[235][0-9]{8}$/, // Ghana\n NG: /^(?:234|0)?[789][01][0-9]{8}$/, // Nigeria\n KE: /^(?:254|0)?[17][0-9]{8}$/, // Kenya\n };\n\n const pattern = patterns[country.toUpperCase()];\n if (!pattern) return digits.length >= 10;\n\n return pattern.test(digits);\n}\n\n/**\n * Formats a phone number for display\n */\nexport function formatPhone(phone: string, country: string = 'GH'): string {\n const digits = phone.replace(/\\D/g, '');\n\n if (country === 'GH') {\n // Format as 0XX XXX XXXX\n if (digits.startsWith('233') && digits.length === 12) {\n const local = '0' + digits.slice(3);\n return `${local.slice(0, 3)} ${local.slice(3, 6)} ${local.slice(6)}`;\n }\n if (digits.length === 10 && digits.startsWith('0')) {\n return `${digits.slice(0, 3)} ${digits.slice(3, 6)} ${digits.slice(6)}`;\n }\n }\n\n return phone;\n}\n\n/**\n * Detects mobile money network from phone number (Ghana)\n */\nexport function detectNetwork(phone: string): MobileMoneyNetwork | null {\n const digits = phone.replace(/\\D/g, '');\n\n // Get the network prefix (first 3 digits after country code or 0)\n let prefix: string;\n if (digits.startsWith('233')) {\n prefix = digits.slice(3, 5);\n } else if (digits.startsWith('0')) {\n prefix = digits.slice(1, 3);\n } else {\n prefix = digits.slice(0, 2);\n }\n\n // Ghana network prefixes\n const mtnPrefixes = ['24', '25', '53', '54', '55', '59'];\n const telecelPrefixes = ['20', '50'];\n const airteltigoPrefixes = ['26', '27', '56', '57'];\n\n if (mtnPrefixes.includes(prefix)) return 'mtn';\n if (telecelPrefixes.includes(prefix)) return 'telecel';\n if (airteltigoPrefixes.includes(prefix)) return 'airteltigo';\n\n return null;\n}\n\n/**\n * Creates CSS custom property variables from theme\n */\nexport function createThemeVariables(theme: ReevitTheme): Record<string, string> {\n const variables: Record<string, string> = {};\n\n // Primary color = main text color\n if (theme.primaryColor) {\n variables['--reevit-text'] = theme.primaryColor;\n }\n\n // Primary foreground = description/secondary text color\n if (theme.primaryForegroundColor) {\n variables['--reevit-text-secondary'] = theme.primaryForegroundColor;\n variables['--reevit-muted'] = theme.primaryForegroundColor;\n }\n\n // Button colors\n if (theme.buttonBackgroundColor) {\n variables['--reevit-primary'] = theme.buttonBackgroundColor;\n variables['--reevit-primary-hover'] = theme.buttonBackgroundColor;\n }\n if (theme.buttonTextColor) {\n variables['--reevit-primary-foreground'] = theme.buttonTextColor;\n }\n\n // Background and surface colors\n if (theme.backgroundColor) {\n variables['--reevit-background'] = theme.backgroundColor;\n variables['--reevit-surface'] = theme.backgroundColor;\n }\n if (theme.surfaceColor) {\n variables['--reevit-surface'] = theme.surfaceColor;\n }\n\n // Border color\n if (theme.borderColor) {\n variables['--reevit-border'] = theme.borderColor;\n }\n\n // Legacy text color support\n if (theme.textColor) {\n variables['--reevit-text'] = theme.textColor;\n }\n if (theme.mutedTextColor) {\n variables['--reevit-text-secondary'] = theme.mutedTextColor;\n }\n\n // Border radius\n if (theme.borderRadius) {\n variables['--reevit-radius'] = theme.borderRadius;\n variables['--reevit-radius-sm'] = theme.borderRadius;\n variables['--reevit-radius-lg'] = theme.borderRadius;\n }\n\n // Font family\n if (theme.fontFamily) {\n variables['--reevit-font'] = theme.fontFamily;\n }\n\n return variables;\n}\n\nfunction getContrastingColor(color: string): string | null {\n const hex = color.trim();\n if (!hex.startsWith('#')) {\n return null;\n }\n\n const normalized = hex.length === 4\n ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}`\n : hex;\n\n if (normalized.length !== 7) {\n return null;\n }\n\n const r = parseInt(normalized.slice(1, 3), 16);\n const g = parseInt(normalized.slice(3, 5), 16);\n const b = parseInt(normalized.slice(5, 7), 16);\n\n if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {\n return null;\n }\n\n const brightness = (r * 299 + g * 587 + b * 114) / 1000;\n return brightness >= 140 ? '#0b1120' : '#ffffff';\n}\n\n/**\n * Simple class name concatenation utility\n */\nexport function cn(...classes: (string | boolean | undefined | null)[]): string {\n return classes.filter(Boolean).join(' ');\n}\n\n/**\n * Detects country code from currency\n */\nexport function detectCountryFromCurrency(currency: string): string {\n const currencyToCountry: Record<string, string> = {\n GHS: 'GH',\n NGN: 'NG',\n KES: 'KE',\n UGX: 'UG',\n TZS: 'TZ',\n ZAR: 'ZA',\n XOF: 'CI',\n XAF: 'CM',\n USD: 'US',\n EUR: 'DE',\n GBP: 'GB',\n };\n\n return currencyToCountry[currency.toUpperCase()] || 'GH';\n}\n","/**\n * Intent identity + cache helpers\n *\n * Two different keys are in play here and mixing them up is a money bug:\n *\n * - the **lookup key** is the deterministic djb2 hash of the checkout\n * parameters (`generateIdempotencyKey`). It identifies \"the same checkout\n * attempt\" for the in-flight cache and is never sent to the API.\n * - the **wire key** is the per-attempt UUID (`attemptIdempotencyKey`) that\n * goes out as the `Idempotency-Key` header.\n *\n * The cache is keyed by the lookup key and remembers the wire key it minted.\n * The public helpers accept either key so callers that only ever saw the\n * `idempotencyKey` field keep working unchanged.\n */\n\nimport type { PaymentIntentResponse } from './api/client';\nimport { attemptIdempotencyKey, generateIdempotencyKey } from './api/client';\nimport type { PaymentMethod, ReevitCheckoutConfig } from './types';\nimport { generateReference } from './utils';\n\nconst INTENT_CACHE_TTL_MS = 10 * 60 * 1000; // 10 minutes\n\nexport interface IntentIdentityOptions {\n config: ReevitCheckoutConfig;\n method?: PaymentMethod;\n preferredProvider?: string;\n allowedProviders?: string[];\n publicKey?: string;\n}\n\nexport interface IntentCacheEntry {\n promise?: Promise<PaymentIntentResponse>;\n response?: PaymentIntentResponse;\n expiresAt: number;\n reference?: string;\n /** The `Idempotency-Key` sent on the wire for this attempt. */\n idempotencyKey?: string;\n}\n\nconst intentCache = new Map<string, IntentCacheEntry>();\n/** wire key -> lookup key, so callers can pass either one. */\nconst lookupKeyByWireKey = new Map<string, string>();\n\nfunction forgetKey(lookupKey: string): void {\n const entry = intentCache.get(lookupKey);\n if (entry?.idempotencyKey) {\n lookupKeyByWireKey.delete(entry.idempotencyKey);\n }\n intentCache.delete(lookupKey);\n}\n\n/** Accepts either the lookup key or the wire key. */\nfunction toLookupKey(key: string): string {\n return lookupKeyByWireKey.get(key) ?? key;\n}\n\nfunction pruneIntentCache(now: number = Date.now()): void {\n for (const [key, entry] of intentCache) {\n if (entry.expiresAt <= now) {\n forgetKey(key);\n }\n }\n}\n\nfunction getIntentCacheEntryInternal(lookupKey: string): IntentCacheEntry | undefined {\n const entry = intentCache.get(lookupKey);\n if (!entry) {\n return undefined;\n }\n if (entry.expiresAt <= Date.now()) {\n forgetKey(lookupKey);\n return undefined;\n }\n return entry;\n}\n\nfunction setIntentCacheEntryInternal(lookupKey: string, update: Partial<IntentCacheEntry>): IntentCacheEntry {\n const now = Date.now();\n const existing = getIntentCacheEntryInternal(lookupKey);\n const next: IntentCacheEntry = {\n ...existing,\n ...update,\n expiresAt: now + INTENT_CACHE_TTL_MS,\n };\n if (existing?.idempotencyKey && existing.idempotencyKey !== next.idempotencyKey) {\n lookupKeyByWireKey.delete(existing.idempotencyKey);\n }\n intentCache.set(lookupKey, next);\n if (next.idempotencyKey && next.idempotencyKey !== lookupKey) {\n lookupKeyByWireKey.set(next.idempotencyKey, lookupKey);\n }\n return next;\n}\n\nfunction buildIdempotencyPayload(options: IntentIdentityOptions): Record<string, unknown> {\n const { config, method, preferredProvider, allowedProviders, publicKey } = options;\n if (config.sessionSecret) {\n return {\n sessionSecret: config.sessionSecret,\n publicKey: publicKey || config.publicKey || '',\n };\n }\n\n const payload: Record<string, unknown> = {\n amount: config.amount,\n currency: config.currency,\n email: config.email || '',\n phone: config.phone || '',\n customerName: config.customerName || '',\n paymentLinkCode: config.paymentLinkCode || '',\n paymentMethods: config.paymentMethods || [],\n metadata: config.metadata || {},\n customFields: config.customFields || {},\n method: method || '',\n preferredProvider: preferredProvider || '',\n allowedProviders: allowedProviders || [],\n publicKey: publicKey || config.publicKey || '',\n };\n\n if (config.reference) {\n payload.reference = config.reference;\n }\n\n return payload;\n}\n\nexport function resolveIntentIdentity(options: IntentIdentityOptions): {\n /** The value to send as `Idempotency-Key`. */\n idempotencyKey: string;\n /** The local cache key. Never send this on the wire. */\n lookupKey: string;\n reference: string;\n cacheEntry?: IntentCacheEntry;\n} {\n pruneIntentCache();\n\n // A caller-supplied key is authoritative for both roles: the merchant owns\n // its retry semantics and we must send exactly what they asked for.\n const explicitKey = options.config.idempotencyKey;\n const lookupKey = explicitKey || generateIdempotencyKey(buildIdempotencyPayload(options));\n const idempotencyKey = explicitKey || attemptIdempotencyKey(lookupKey);\n\n const existing = getIntentCacheEntryInternal(lookupKey);\n const reference = options.config.reference || existing?.reference || generateReference();\n\n const cacheEntry = setIntentCacheEntryInternal(lookupKey, { reference, idempotencyKey });\n\n return { idempotencyKey, lookupKey, reference, cacheEntry };\n}\n\nexport function getIntentCacheEntry(key: string): IntentCacheEntry | undefined {\n pruneIntentCache();\n return getIntentCacheEntryInternal(toLookupKey(key));\n}\n\nexport function cacheIntentPromise(\n key: string,\n promise: Promise<PaymentIntentResponse>\n): IntentCacheEntry {\n return setIntentCacheEntryInternal(toLookupKey(key), { promise });\n}\n\nexport function cacheIntentResponse(\n key: string,\n response: PaymentIntentResponse\n): IntentCacheEntry {\n return setIntentCacheEntryInternal(toLookupKey(key), { response, promise: undefined });\n}\n\nexport function clearIntentCacheEntry(key: string): void {\n forgetKey(toLookupKey(key));\n}\n","/**\n * Reevit State Machine\n * Shared state management logic for all SDKs\n */\n\nimport type { CheckoutState, PaymentIntent, PaymentMethod, PaymentResult, PaymentError } from './types';\n\n// State shape\nexport interface ReevitState {\n status: CheckoutState;\n paymentIntent: PaymentIntent | null;\n selectedMethod: PaymentMethod | null;\n error: PaymentError | null;\n result: PaymentResult | null;\n}\n\n// Actions\nexport type ReevitAction =\n | { type: 'INIT_START' }\n | { type: 'INIT_SUCCESS'; payload: PaymentIntent }\n | { type: 'INIT_ERROR'; payload: PaymentError }\n | { type: 'SELECT_METHOD'; payload: PaymentMethod }\n | { type: 'PROCESS_START' }\n | { type: 'PROCESS_SUCCESS'; payload: PaymentResult }\n | { type: 'PROCESS_ERROR'; payload: PaymentError }\n | { type: 'RESET' }\n | { type: 'CLOSE' };\n\n/**\n * Creates the initial state for the checkout\n */\nexport function createInitialState(): ReevitState {\n return {\n status: 'idle',\n paymentIntent: null,\n selectedMethod: null,\n error: null,\n result: null,\n };\n}\n\n/**\n * State reducer for checkout flow\n */\nexport function reevitReducer(state: ReevitState, action: ReevitAction): ReevitState {\n switch (action.type) {\n case 'INIT_START':\n return { ...state, status: 'loading', error: null };\n case 'INIT_SUCCESS':\n return {\n ...state,\n status: 'ready',\n paymentIntent: action.payload,\n selectedMethod:\n action.payload.availableMethods?.length === 1 ? action.payload.availableMethods[0] : null,\n };\n case 'INIT_ERROR':\n return { ...state, status: 'failed', error: action.payload };\n case 'SELECT_METHOD':\n return { ...state, status: 'method_selected', selectedMethod: action.payload };\n case 'PROCESS_START':\n return { ...state, status: 'processing', error: null };\n case 'PROCESS_SUCCESS':\n return { ...state, status: 'success', result: action.payload };\n case 'PROCESS_ERROR':\n return { ...state, status: 'failed', error: action.payload };\n case 'RESET':\n return { ...createInitialState(), status: 'ready', paymentIntent: state.paymentIntent };\n case 'CLOSE':\n return { ...state, status: 'closed' };\n default:\n return state;\n }\n}\n"],"mappings":"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","names":["existing","created"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reevit/core",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Core utilities and API client for Reevit payment SDKs",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -20,6 +20,7 @@
20
20
  "build": "tsup",
21
21
  "dev": "tsup --watch",
22
22
  "typecheck": "tsc --noEmit",
23
+ "test": "vitest run",
23
24
  "prepublishOnly": "npm run build"
24
25
  },
25
26
  "keywords": [
@@ -40,7 +41,9 @@
40
41
  },
41
42
  "devDependencies": {
42
43
  "@types/node": "^20.0.0",
44
+ "jsdom": "^29.1.1",
43
45
  "tsup": "^8.0.0",
44
- "typescript": "^5.0.0"
46
+ "typescript": "^5.0.0",
47
+ "vitest": "^2.1.9"
45
48
  }
46
49
  }