@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.mjs CHANGED
@@ -146,6 +146,18 @@ function createPaymentError(response, errorData) {
146
146
  }
147
147
  };
148
148
  }
149
+ function generateIdempotencyKey(params) {
150
+ const sortedKeys = Object.keys(params).sort();
151
+ const stableString = sortedKeys.map((key) => `${key}:${JSON.stringify(params[key])}`).join("|");
152
+ let hash = 5381;
153
+ for (let i = 0; i < stableString.length; i++) {
154
+ hash = (hash << 5) + hash + stableString.charCodeAt(i);
155
+ hash = hash & hash;
156
+ }
157
+ const hashHex = (hash >>> 0).toString(16);
158
+ const timeBucket = Math.floor(Date.now() / (5 * 60 * 1e3));
159
+ return `reevit_${timeBucket}_${hashHex}`;
160
+ }
149
161
  var ReevitAPIClient = class {
150
162
  constructor(config) {
151
163
  this.publicKey = config.publicKey || "";
@@ -154,8 +166,9 @@ var ReevitAPIClient = class {
154
166
  }
155
167
  /**
156
168
  * Makes an authenticated API request
169
+ * @param idempotencyKey Optional deterministic idempotency key for the request
157
170
  */
158
- async request(method, path, body) {
171
+ async request(method, path, body, idempotencyKey) {
159
172
  const controller = new AbortController();
160
173
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
161
174
  const headers = {
@@ -167,7 +180,7 @@ var ReevitAPIClient = class {
167
180
  headers["X-Reevit-Key"] = this.publicKey;
168
181
  }
169
182
  if (method === "POST" || method === "PATCH" || method === "PUT") {
170
- headers["Idempotency-Key"] = `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
183
+ headers["Idempotency-Key"] = idempotencyKey || (body ? generateIdempotencyKey(body) : `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`);
171
184
  }
172
185
  try {
173
186
  const response = await fetch(`${this.baseUrl}${path}`, {
@@ -240,7 +253,16 @@ var ReevitAPIClient = class {
240
253
  allowed_providers: options?.allowedProviders
241
254
  };
242
255
  }
243
- return this.request("POST", "/v1/payments/intents", request);
256
+ const idempotencyKey = generateIdempotencyKey({
257
+ amount: config.amount,
258
+ currency: config.currency,
259
+ customer: config.email || config.metadata?.customerId || "",
260
+ reference: config.reference || "",
261
+ method: method || "",
262
+ provider: options?.preferredProviders?.[0] || options?.allowedProviders?.[0] || "",
263
+ publicKey: this.publicKey
264
+ });
265
+ return this.request("POST", "/v1/payments/intents", request, idempotencyKey);
244
266
  }
245
267
  /**
246
268
  * Retrieves a payment intent by ID
@@ -482,13 +504,21 @@ function useReevit(options) {
482
504
  let data;
483
505
  let error;
484
506
  if (config.paymentLinkCode) {
507
+ const idempotencyKey = generateIdempotencyKey({
508
+ paymentLinkCode: config.paymentLinkCode,
509
+ amount: config.amount,
510
+ email: config.email || "",
511
+ phone: config.phone || "",
512
+ method: paymentMethod || "",
513
+ provider: options2?.preferredProvider || options2?.allowedProviders?.[0] || ""
514
+ });
485
515
  const response = await fetch(
486
516
  `${apiBaseUrl || DEFAULT_PUBLIC_API_BASE_URL}/v1/pay/${config.paymentLinkCode}/pay`,
487
517
  {
488
518
  method: "POST",
489
519
  headers: {
490
520
  "Content-Type": "application/json",
491
- "Idempotency-Key": `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`
521
+ "Idempotency-Key": idempotencyKey
492
522
  },
493
523
  body: JSON.stringify({
494
524
  amount: config.amount,
@@ -625,11 +655,20 @@ function useReevit(options) {
625
655
  },
626
656
  [onError]
627
657
  );
628
- const reset = useCallback(() => {
658
+ const reset = useCallback(async () => {
659
+ if (state.paymentIntent && state.status !== "success") {
660
+ try {
661
+ const apiClient = apiClientRef.current;
662
+ if (apiClient) {
663
+ await apiClient.cancelPaymentIntent(state.paymentIntent.id);
664
+ }
665
+ } catch {
666
+ }
667
+ }
629
668
  initializingRef.current = false;
630
669
  initRequestIdRef.current += 1;
631
670
  dispatch({ type: "RESET" });
632
- }, []);
671
+ }, [state.paymentIntent, state.status]);
633
672
  const close = useCallback(async () => {
634
673
  if (state.paymentIntent && state.status !== "success") {
635
674
  try {
@@ -2252,10 +2291,10 @@ function ReevitCheckout({
2252
2291
  [selectMethod]
2253
2292
  );
2254
2293
  const handleProviderSelect = useCallback(
2255
- (provider) => {
2294
+ async (provider) => {
2256
2295
  if (provider === selectedProvider) {
2257
2296
  setSelectedProvider(null);
2258
- reset();
2297
+ await reset();
2259
2298
  setShowPSPBridge(false);
2260
2299
  setMomoData(null);
2261
2300
  return;
@@ -2263,7 +2302,7 @@ function ReevitCheckout({
2263
2302
  const providerMethods = providerOptions.find((option) => option.provider === provider)?.methods || paymentMethods;
2264
2303
  const methodForInit = selectedMethod && providerMethods.includes(selectedMethod) ? selectedMethod : providerMethods[0] || paymentMethods[0];
2265
2304
  setSelectedProvider(provider);
2266
- reset();
2305
+ await reset();
2267
2306
  setShowPSPBridge(false);
2268
2307
  setMomoData(null);
2269
2308
  initialize(methodForInit, { preferredProvider: provider, allowedProviders: [provider] });
@@ -2305,8 +2344,8 @@ function ReevitCheckout({
2305
2344
  const handlePSPClose = useCallback(() => {
2306
2345
  setShowPSPBridge(false);
2307
2346
  }, []);
2308
- const handleBack = useCallback(() => {
2309
- reset();
2347
+ const handleBack = useCallback(async () => {
2348
+ await reset();
2310
2349
  setMomoData(null);
2311
2350
  setShowPSPBridge(false);
2312
2351
  }, [reset]);