@swype-org/react-sdk 0.1.14 → 0.1.16

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.cts CHANGED
@@ -308,6 +308,18 @@ declare function createPasskeyCredential(userIdentifier: string): Promise<{
308
308
  credentialId: string;
309
309
  publicKey: string;
310
310
  }>;
311
+ /**
312
+ * Checks whether the current device's platform authenticator holds a passkey
313
+ * matching the given credential ID.
314
+ *
315
+ * Uses `navigator.credentials.get()` with a throwaway challenge and a short
316
+ * timeout. If the authenticator responds, the device has the key. If it
317
+ * throws (NotAllowedError, timeout, etc.), the device does not.
318
+ *
319
+ * @param credentialId - Base64-encoded WebAuthn credential ID from the server.
320
+ * @returns `true` if the device can authenticate with this credential.
321
+ */
322
+ declare function deviceHasPasskey(credentialId: string): Promise<boolean>;
311
323
  interface UseTransferPollingResult {
312
324
  transfer: Transfer | null;
313
325
  error: string | null;
@@ -449,4 +461,4 @@ declare namespace api {
449
461
  export { type api_CreateTransferParams as CreateTransferParams, api_createTransfer as createTransfer, api_fetchAccounts as fetchAccounts, api_fetchAuthorizationSession as fetchAuthorizationSession, api_fetchChains as fetchChains, api_fetchProviders as fetchProviders, api_fetchTransfer as fetchTransfer, api_fetchUserConfig as fetchUserConfig, api_registerPasskey as registerPasskey, api_reportActionCompletion as reportActionCompletion, api_signTransfer as signTransfer, api_updateUserConfig as updateUserConfig, api_updateUserConfigBySession as updateUserConfigBySession };
450
462
  }
451
463
 
452
- export { type Account, type ActionExecutionResult, type AdvancedSettings, type Amount, type AuthorizationAction, type AuthorizationSession, type AuthorizationSessionDetail, type Chain, type Destination, type ErrorResponse, type ListResponse, type PaymentStep, type Provider, type SourceOption, type SourceSelection, type SourceType, SwypePayment, type SwypePaymentProps, SwypeProvider, type SwypeProviderProps, type ThemeMode, type ThemeTokens, type TokenBalance, type Transfer, type TransferDestination, type UserConfig, type Wallet, type WalletSource, type WalletToken, createPasskeyCredential, darkTheme, getTheme, lightTheme, api as swypeApi, useAuthorizationExecutor, useSwypeConfig, useSwypeDepositAmount, useTransferPolling, useTransferSigning };
464
+ export { type Account, type ActionExecutionResult, type AdvancedSettings, type Amount, type AuthorizationAction, type AuthorizationSession, type AuthorizationSessionDetail, type Chain, type Destination, type ErrorResponse, type ListResponse, type PaymentStep, type Provider, type SourceOption, type SourceSelection, type SourceType, SwypePayment, type SwypePaymentProps, SwypeProvider, type SwypeProviderProps, type ThemeMode, type ThemeTokens, type TokenBalance, type Transfer, type TransferDestination, type UserConfig, type Wallet, type WalletSource, type WalletToken, createPasskeyCredential, darkTheme, deviceHasPasskey, getTheme, lightTheme, api as swypeApi, useAuthorizationExecutor, useSwypeConfig, useSwypeDepositAmount, useTransferPolling, useTransferSigning };
package/dist/index.d.ts CHANGED
@@ -308,6 +308,18 @@ declare function createPasskeyCredential(userIdentifier: string): Promise<{
308
308
  credentialId: string;
309
309
  publicKey: string;
310
310
  }>;
311
+ /**
312
+ * Checks whether the current device's platform authenticator holds a passkey
313
+ * matching the given credential ID.
314
+ *
315
+ * Uses `navigator.credentials.get()` with a throwaway challenge and a short
316
+ * timeout. If the authenticator responds, the device has the key. If it
317
+ * throws (NotAllowedError, timeout, etc.), the device does not.
318
+ *
319
+ * @param credentialId - Base64-encoded WebAuthn credential ID from the server.
320
+ * @returns `true` if the device can authenticate with this credential.
321
+ */
322
+ declare function deviceHasPasskey(credentialId: string): Promise<boolean>;
311
323
  interface UseTransferPollingResult {
312
324
  transfer: Transfer | null;
313
325
  error: string | null;
@@ -449,4 +461,4 @@ declare namespace api {
449
461
  export { type api_CreateTransferParams as CreateTransferParams, api_createTransfer as createTransfer, api_fetchAccounts as fetchAccounts, api_fetchAuthorizationSession as fetchAuthorizationSession, api_fetchChains as fetchChains, api_fetchProviders as fetchProviders, api_fetchTransfer as fetchTransfer, api_fetchUserConfig as fetchUserConfig, api_registerPasskey as registerPasskey, api_reportActionCompletion as reportActionCompletion, api_signTransfer as signTransfer, api_updateUserConfig as updateUserConfig, api_updateUserConfigBySession as updateUserConfigBySession };
450
462
  }
451
463
 
452
- export { type Account, type ActionExecutionResult, type AdvancedSettings, type Amount, type AuthorizationAction, type AuthorizationSession, type AuthorizationSessionDetail, type Chain, type Destination, type ErrorResponse, type ListResponse, type PaymentStep, type Provider, type SourceOption, type SourceSelection, type SourceType, SwypePayment, type SwypePaymentProps, SwypeProvider, type SwypeProviderProps, type ThemeMode, type ThemeTokens, type TokenBalance, type Transfer, type TransferDestination, type UserConfig, type Wallet, type WalletSource, type WalletToken, createPasskeyCredential, darkTheme, getTheme, lightTheme, api as swypeApi, useAuthorizationExecutor, useSwypeConfig, useSwypeDepositAmount, useTransferPolling, useTransferSigning };
464
+ export { type Account, type ActionExecutionResult, type AdvancedSettings, type Amount, type AuthorizationAction, type AuthorizationSession, type AuthorizationSessionDetail, type Chain, type Destination, type ErrorResponse, type ListResponse, type PaymentStep, type Provider, type SourceOption, type SourceSelection, type SourceType, SwypePayment, type SwypePaymentProps, SwypeProvider, type SwypeProviderProps, type ThemeMode, type ThemeTokens, type TokenBalance, type Transfer, type TransferDestination, type UserConfig, type Wallet, type WalletSource, type WalletToken, createPasskeyCredential, darkTheme, deviceHasPasskey, getTheme, lightTheme, api as swypeApi, useAuthorizationExecutor, useSwypeConfig, useSwypeDepositAmount, useTransferPolling, useTransferSigning };
package/dist/index.js CHANGED
@@ -588,6 +588,28 @@ var ACTION_POLL_MAX_RETRIES = 20;
588
588
  var SIGN_PERMIT2_POLL_MS = 1e3;
589
589
  var SIGN_PERMIT2_MAX_POLLS = 15;
590
590
  var TRANSFER_SIGN_MAX_POLLS = 60;
591
+ function waitForDocumentFocus(timeoutMs = 5e3, intervalMs = 100) {
592
+ return new Promise((resolve, reject) => {
593
+ if (typeof document === "undefined") {
594
+ resolve();
595
+ return;
596
+ }
597
+ if (document.hasFocus()) {
598
+ resolve();
599
+ return;
600
+ }
601
+ const deadline = Date.now() + timeoutMs;
602
+ const timer = setInterval(() => {
603
+ if (document.hasFocus()) {
604
+ clearInterval(timer);
605
+ resolve();
606
+ } else if (Date.now() >= deadline) {
607
+ clearInterval(timer);
608
+ resolve();
609
+ }
610
+ }, intervalMs);
611
+ });
612
+ }
591
613
  function actionSuccess(action, message, data) {
592
614
  return { actionId: action.id, type: action.type, status: "success", message, data };
593
615
  }
@@ -679,6 +701,7 @@ async function createPasskeyCredential(userIdentifier) {
679
701
  const challenge = new Uint8Array(32);
680
702
  crypto.getRandomValues(challenge);
681
703
  const rpId = resolvePasskeyRpId();
704
+ await waitForDocumentFocus();
682
705
  const credential = await navigator.credentials.create({
683
706
  publicKey: {
684
707
  challenge,
@@ -712,6 +735,28 @@ async function createPasskeyCredential(userIdentifier) {
712
735
  publicKey: publicKeyBytes ? toBase64(publicKeyBytes) : ""
713
736
  };
714
737
  }
738
+ async function deviceHasPasskey(credentialId) {
739
+ try {
740
+ const challenge = new Uint8Array(32);
741
+ crypto.getRandomValues(challenge);
742
+ await waitForDocumentFocus();
743
+ const assertion = await navigator.credentials.get({
744
+ publicKey: {
745
+ challenge,
746
+ rpId: resolvePasskeyRpId(),
747
+ allowCredentials: [{
748
+ type: "public-key",
749
+ id: base64ToBytes(credentialId)
750
+ }],
751
+ userVerification: "required",
752
+ timeout: 3e4
753
+ }
754
+ });
755
+ return assertion != null;
756
+ } catch {
757
+ return false;
758
+ }
759
+ }
715
760
  function useTransferPolling(intervalMs = 3e3) {
716
761
  const { apiBaseUrl } = useSwypeConfig();
717
762
  const { getAccessToken } = usePrivy();
@@ -1184,6 +1229,7 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
1184
1229
  type: "public-key",
1185
1230
  id: base64ToBytes(payload.passkeyCredentialId)
1186
1231
  }] : void 0;
1232
+ await waitForDocumentFocus();
1187
1233
  const assertion = await navigator.credentials.get({
1188
1234
  publicKey: {
1189
1235
  challenge: hashBytes,
@@ -1980,6 +2026,7 @@ function buildProcessingTimeoutMessage(status) {
1980
2026
  return `Payment is taking longer than expected (status: ${status}). Please try again.`;
1981
2027
  }
1982
2028
  var ACTIVE_CREDENTIAL_STORAGE_KEY = "swype_active_credential_id";
2029
+ var MIN_SEND_AMOUNT_USD = 0.25;
1983
2030
  function isMobile() {
1984
2031
  if (typeof navigator === "undefined") return false;
1985
2032
  return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
@@ -2113,12 +2160,27 @@ function SwypePayment({
2113
2160
  if (!token || cancelled) return;
2114
2161
  const { config } = await fetchUserConfig(apiBaseUrl, token);
2115
2162
  if (cancelled) return;
2116
- if (!config.passkey || !activeCredentialId) {
2117
- setStep("register-passkey");
2118
- } else if (depositAmount != null && depositAmount > 0) {
2119
- setStep("ready");
2163
+ if (config.passkey?.credentialId) {
2164
+ const hasKey = activeCredentialId ? activeCredentialId === config.passkey.credentialId : await deviceHasPasskey(config.passkey.credentialId);
2165
+ if (cancelled) return;
2166
+ if (hasKey) {
2167
+ if (!activeCredentialId) {
2168
+ setActiveCredentialId(config.passkey.credentialId);
2169
+ window.localStorage.setItem(
2170
+ ACTIVE_CREDENTIAL_STORAGE_KEY,
2171
+ config.passkey.credentialId
2172
+ );
2173
+ }
2174
+ if (depositAmount != null && depositAmount > 0) {
2175
+ setStep("ready");
2176
+ } else {
2177
+ setStep("enter-amount");
2178
+ }
2179
+ } else {
2180
+ setStep("register-passkey");
2181
+ }
2120
2182
  } else {
2121
- setStep("enter-amount");
2183
+ setStep("register-passkey");
2122
2184
  }
2123
2185
  } catch {
2124
2186
  if (!cancelled) {
@@ -2305,8 +2367,8 @@ function SwypePayment({
2305
2367
  }, [pendingSelectSourceAction, selectSourceChoices, selectSourceRecommended]);
2306
2368
  const handlePay = useCallback(async () => {
2307
2369
  const parsedAmount = parseFloat(amount);
2308
- if (isNaN(parsedAmount) || parsedAmount <= 0) {
2309
- setError("Enter a valid amount.");
2370
+ if (isNaN(parsedAmount) || parsedAmount < MIN_SEND_AMOUNT_USD) {
2371
+ setError(`Minimum amount is $${MIN_SEND_AMOUNT_USD.toFixed(2)}.`);
2310
2372
  return;
2311
2373
  }
2312
2374
  if (!sourceId) {
@@ -2682,7 +2744,7 @@ function SwypePayment({
2682
2744
  }
2683
2745
  if (step === "enter-amount") {
2684
2746
  const parsedAmount = parseFloat(amount);
2685
- const canContinue = !isNaN(parsedAmount) && parsedAmount > 0;
2747
+ const canContinue = !isNaN(parsedAmount) && parsedAmount >= MIN_SEND_AMOUNT_USD;
2686
2748
  let maxSourceBalance = null;
2687
2749
  for (const acct of accounts) {
2688
2750
  for (const wallet of acct.wallets) {
@@ -2729,7 +2791,7 @@ function SwypePayment({
2729
2791
  "input",
2730
2792
  {
2731
2793
  type: "number",
2732
- min: "0.01",
2794
+ min: MIN_SEND_AMOUNT_USD.toFixed(2),
2733
2795
  step: "0.01",
2734
2796
  value: amount,
2735
2797
  onChange: (e) => setAmount(e.target.value),
@@ -2799,7 +2861,7 @@ function SwypePayment({
2799
2861
  }
2800
2862
  if (step === "ready") {
2801
2863
  const parsedAmount = parseFloat(amount);
2802
- const canPay = !isNaN(parsedAmount) && parsedAmount > 0 && !!sourceId && !loadingData;
2864
+ const canPay = !isNaN(parsedAmount) && parsedAmount >= MIN_SEND_AMOUNT_USD && !!sourceId && !loadingData;
2803
2865
  const noAccounts = !loadingData && accounts.length === 0;
2804
2866
  return /* @__PURE__ */ jsxs("div", { style: cardStyle, children: [
2805
2867
  /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
@@ -3430,6 +3492,6 @@ function SwypePayment({
3430
3492
  return null;
3431
3493
  }
3432
3494
 
3433
- export { SwypePayment, SwypeProvider, createPasskeyCredential, darkTheme, getTheme, lightTheme, api_exports as swypeApi, useAuthorizationExecutor, useSwypeConfig, useSwypeDepositAmount, useTransferPolling, useTransferSigning };
3495
+ export { SwypePayment, SwypeProvider, createPasskeyCredential, darkTheme, deviceHasPasskey, getTheme, lightTheme, api_exports as swypeApi, useAuthorizationExecutor, useSwypeConfig, useSwypeDepositAmount, useTransferPolling, useTransferSigning };
3434
3496
  //# sourceMappingURL=index.js.map
3435
3497
  //# sourceMappingURL=index.js.map