@reevit/react 0.5.4 → 0.5.6

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.d.mts CHANGED
@@ -264,7 +264,7 @@ declare function useReevit(options: UseReevitOptions): {
264
264
  processPayment: (paymentData: Record<string, unknown>) => Promise<void>;
265
265
  handlePspSuccess: (pspData: Record<string, unknown>) => Promise<void>;
266
266
  handlePspError: (error: PaymentError) => void;
267
- reset: () => void;
267
+ reset: () => Promise<void>;
268
268
  close: () => Promise<void>;
269
269
  isLoading: boolean;
270
270
  isReady: boolean;
@@ -697,6 +697,7 @@ declare class ReevitAPIClient {
697
697
  constructor(config: ReevitAPIClientConfig);
698
698
  /**
699
699
  * Makes an authenticated API request
700
+ * @param idempotencyKey Optional deterministic idempotency key for the request
700
701
  */
701
702
  private request;
702
703
  /**
package/dist/index.d.ts CHANGED
@@ -264,7 +264,7 @@ declare function useReevit(options: UseReevitOptions): {
264
264
  processPayment: (paymentData: Record<string, unknown>) => Promise<void>;
265
265
  handlePspSuccess: (pspData: Record<string, unknown>) => Promise<void>;
266
266
  handlePspError: (error: PaymentError) => void;
267
- reset: () => void;
267
+ reset: () => Promise<void>;
268
268
  close: () => Promise<void>;
269
269
  isLoading: boolean;
270
270
  isReady: boolean;
@@ -697,6 +697,7 @@ declare class ReevitAPIClient {
697
697
  constructor(config: ReevitAPIClientConfig);
698
698
  /**
699
699
  * Makes an authenticated API request
700
+ * @param idempotencyKey Optional deterministic idempotency key for the request
700
701
  */
701
702
  private request;
702
703
  /**
package/dist/index.js CHANGED
@@ -152,6 +152,18 @@ function createPaymentError(response, errorData) {
152
152
  }
153
153
  };
154
154
  }
155
+ function generateIdempotencyKey(params) {
156
+ const sortedKeys = Object.keys(params).sort();
157
+ const stableString = sortedKeys.map((key) => `${key}:${JSON.stringify(params[key])}`).join("|");
158
+ let hash = 5381;
159
+ for (let i = 0; i < stableString.length; i++) {
160
+ hash = (hash << 5) + hash + stableString.charCodeAt(i);
161
+ hash = hash & hash;
162
+ }
163
+ const hashHex = (hash >>> 0).toString(16);
164
+ const timeBucket = Math.floor(Date.now() / (5 * 60 * 1e3));
165
+ return `reevit_${timeBucket}_${hashHex}`;
166
+ }
155
167
  var ReevitAPIClient = class {
156
168
  constructor(config) {
157
169
  this.publicKey = config.publicKey || "";
@@ -160,8 +172,9 @@ var ReevitAPIClient = class {
160
172
  }
161
173
  /**
162
174
  * Makes an authenticated API request
175
+ * @param idempotencyKey Optional deterministic idempotency key for the request
163
176
  */
164
- async request(method, path, body) {
177
+ async request(method, path, body, idempotencyKey) {
165
178
  const controller = new AbortController();
166
179
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
167
180
  const headers = {
@@ -173,7 +186,7 @@ var ReevitAPIClient = class {
173
186
  headers["X-Reevit-Key"] = this.publicKey;
174
187
  }
175
188
  if (method === "POST" || method === "PATCH" || method === "PUT") {
176
- headers["Idempotency-Key"] = `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
189
+ headers["Idempotency-Key"] = idempotencyKey || (body ? generateIdempotencyKey(body) : `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`);
177
190
  }
178
191
  try {
179
192
  const response = await fetch(`${this.baseUrl}${path}`, {
@@ -246,7 +259,16 @@ var ReevitAPIClient = class {
246
259
  allowed_providers: options?.allowedProviders
247
260
  };
248
261
  }
249
- return this.request("POST", "/v1/payments/intents", request);
262
+ const idempotencyKey = generateIdempotencyKey({
263
+ amount: config.amount,
264
+ currency: config.currency,
265
+ customer: config.email || config.metadata?.customerId || "",
266
+ reference: config.reference || "",
267
+ method: method || "",
268
+ provider: options?.preferredProviders?.[0] || options?.allowedProviders?.[0] || "",
269
+ publicKey: this.publicKey
270
+ });
271
+ return this.request("POST", "/v1/payments/intents", request, idempotencyKey);
250
272
  }
251
273
  /**
252
274
  * Retrieves a payment intent by ID
@@ -488,13 +510,21 @@ function useReevit(options) {
488
510
  let data;
489
511
  let error;
490
512
  if (config.paymentLinkCode) {
513
+ const idempotencyKey = generateIdempotencyKey({
514
+ paymentLinkCode: config.paymentLinkCode,
515
+ amount: config.amount,
516
+ email: config.email || "",
517
+ phone: config.phone || "",
518
+ method: paymentMethod || "",
519
+ provider: options2?.preferredProvider || options2?.allowedProviders?.[0] || ""
520
+ });
491
521
  const response = await fetch(
492
522
  `${apiBaseUrl || DEFAULT_PUBLIC_API_BASE_URL}/v1/pay/${config.paymentLinkCode}/pay`,
493
523
  {
494
524
  method: "POST",
495
525
  headers: {
496
526
  "Content-Type": "application/json",
497
- "Idempotency-Key": `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`
527
+ "Idempotency-Key": idempotencyKey
498
528
  },
499
529
  body: JSON.stringify({
500
530
  amount: config.amount,
@@ -631,11 +661,20 @@ function useReevit(options) {
631
661
  },
632
662
  [onError]
633
663
  );
634
- const reset = react.useCallback(() => {
664
+ const reset = react.useCallback(async () => {
665
+ if (state.paymentIntent && state.status !== "success") {
666
+ try {
667
+ const apiClient = apiClientRef.current;
668
+ if (apiClient) {
669
+ await apiClient.cancelPaymentIntent(state.paymentIntent.id);
670
+ }
671
+ } catch {
672
+ }
673
+ }
635
674
  initializingRef.current = false;
636
675
  initRequestIdRef.current += 1;
637
676
  dispatch({ type: "RESET" });
638
- }, []);
677
+ }, [state.paymentIntent, state.status]);
639
678
  const close = react.useCallback(async () => {
640
679
  if (state.paymentIntent && state.status !== "success") {
641
680
  try {
@@ -2258,10 +2297,10 @@ function ReevitCheckout({
2258
2297
  [selectMethod]
2259
2298
  );
2260
2299
  const handleProviderSelect = react.useCallback(
2261
- (provider) => {
2300
+ async (provider) => {
2262
2301
  if (provider === selectedProvider) {
2263
2302
  setSelectedProvider(null);
2264
- reset();
2303
+ await reset();
2265
2304
  setShowPSPBridge(false);
2266
2305
  setMomoData(null);
2267
2306
  return;
@@ -2269,7 +2308,7 @@ function ReevitCheckout({
2269
2308
  const providerMethods = providerOptions.find((option) => option.provider === provider)?.methods || paymentMethods;
2270
2309
  const methodForInit = selectedMethod && providerMethods.includes(selectedMethod) ? selectedMethod : providerMethods[0] || paymentMethods[0];
2271
2310
  setSelectedProvider(provider);
2272
- reset();
2311
+ await reset();
2273
2312
  setShowPSPBridge(false);
2274
2313
  setMomoData(null);
2275
2314
  initialize(methodForInit, { preferredProvider: provider, allowedProviders: [provider] });
@@ -2311,8 +2350,8 @@ function ReevitCheckout({
2311
2350
  const handlePSPClose = react.useCallback(() => {
2312
2351
  setShowPSPBridge(false);
2313
2352
  }, []);
2314
- const handleBack = react.useCallback(() => {
2315
- reset();
2353
+ const handleBack = react.useCallback(async () => {
2354
+ await reset();
2316
2355
  setMomoData(null);
2317
2356
  setShowPSPBridge(false);
2318
2357
  }, [reset]);