@superlogic/spree-pay 0.1.4 → 0.1.5

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/README.md CHANGED
@@ -18,8 +18,8 @@ import { SpreePay, SpreePayProvider, useSpreePay } from '@superlogic/spree-pay';
18
18
  const spreeEnv = {
19
19
  tenantId: 'moca' // Keep moca for crypto payments on BASE network
20
20
  environment: 'dev',
21
- accessToken: 'eyJhbGciOiJSUzI1NiIs...', // Use user JWT token
22
21
  redirect3dsURI: '/3ds' // Any valid URI in your app that captures 3ds redirect
22
+ ssoPageURI: '/silent-check-sso.html' // Any valid URI in your app that will handle keyklock SSO (example below)
23
23
  };
24
24
 
25
25
  function Checkout() {
@@ -70,3 +70,18 @@ export default function ThreeDSRedirectPage() {
70
70
  }
71
71
 
72
72
  ```
73
+
74
+ ## Keyklock SSO page example (/silent-check-sso.html)
75
+
76
+ ```html
77
+ <!doctype html>
78
+ <html>
79
+ <body>
80
+ <script>
81
+ try {
82
+ parent.postMessage(location.href, location.origin);
83
+ } catch (e) {}
84
+ </script>
85
+ </body>
86
+ </html>
87
+ ```
package/build/index.cjs CHANGED
@@ -38,7 +38,7 @@ __export(index_exports, {
38
38
  module.exports = __toCommonJS(index_exports);
39
39
 
40
40
  // src/SpreePay.tsx
41
- var import_react11 = require("react");
41
+ var import_react12 = require("react");
42
42
  var import_nice_modal_react6 = __toESM(require("@ebay/nice-modal-react"), 1);
43
43
  var import_swr4 = require("swr");
44
44
 
@@ -5648,7 +5648,8 @@ var cardPayment = async ({ card, hash, redirect3dsURI }) => {
5648
5648
  }
5649
5649
  }
5650
5650
  return {
5651
- status: paymentResData.status
5651
+ status: paymentResData.status,
5652
+ paymentId: paymentResData.id
5652
5653
  };
5653
5654
  };
5654
5655
 
@@ -6643,31 +6644,170 @@ var import_react_query = require("@tanstack/react-query");
6643
6644
  var import_nice_modal_react5 = __toESM(require("@ebay/nice-modal-react"), 1);
6644
6645
  var import_rainbowkit2 = require("@rainbow-me/rainbowkit");
6645
6646
  var import_styles = require("@rainbow-me/rainbowkit/styles.css");
6646
- var import_wagmi4 = require("wagmi");
6647
+ var import_wagmi5 = require("wagmi");
6647
6648
  var import_chains = require("wagmi/chains");
6648
6649
 
6649
6650
  // src/components/CryptoTab/Crypto/Crypto.tsx
6650
6651
  var import_react10 = require("react");
6651
- var import_wagmi3 = require("wagmi");
6652
+ var import_wagmi4 = require("wagmi");
6652
6653
 
6653
- // src/services/cryptoPayment.ts
6654
- var cryptoPayment = async (params) => {
6655
- const { publicKey, coinSymbol, hash, sendTransaction } = params;
6656
- const paymentRes = await SlapiPaymentService.createPayment({
6657
- type: "CRYPTO" /* CRYPTO */,
6658
- hash,
6659
- crypto: {
6660
- token: coinSymbol,
6661
- publicKey,
6662
- slippageType: "fixed",
6663
- slippageBps: 0.5 * 100
6654
+ // ../../node_modules/@wagmi/core/dist/esm/utils/getAction.js
6655
+ function getAction(client, actionFn, name) {
6656
+ const action_implicit = client[actionFn.name];
6657
+ if (typeof action_implicit === "function")
6658
+ return action_implicit;
6659
+ const action_explicit = client[name];
6660
+ if (typeof action_explicit === "function")
6661
+ return action_explicit;
6662
+ return (params) => actionFn(client, params);
6663
+ }
6664
+
6665
+ // ../../node_modules/@wagmi/core/dist/esm/actions/readContract.js
6666
+ var import_actions = require("viem/actions");
6667
+ function readContract(config2, parameters) {
6668
+ const { chainId, ...rest } = parameters;
6669
+ const client = config2.getClient({ chainId });
6670
+ const action = getAction(client, import_actions.readContract, "readContract");
6671
+ return action(rest);
6672
+ }
6673
+
6674
+ // ../../node_modules/@wagmi/core/dist/esm/actions/waitForTransactionReceipt.js
6675
+ var import_viem = require("viem");
6676
+ var import_actions2 = require("viem/actions");
6677
+ async function waitForTransactionReceipt(config2, parameters) {
6678
+ const { chainId, timeout = 0, ...rest } = parameters;
6679
+ const client = config2.getClient({ chainId });
6680
+ const action = getAction(client, import_actions2.waitForTransactionReceipt, "waitForTransactionReceipt");
6681
+ const receipt = await action({ ...rest, timeout });
6682
+ if (receipt.status === "reverted") {
6683
+ const action_getTransaction = getAction(client, import_actions2.getTransaction, "getTransaction");
6684
+ const txn = await action_getTransaction({ hash: receipt.transactionHash });
6685
+ const action_call = getAction(client, import_actions2.call, "call");
6686
+ const code = await action_call({
6687
+ ...txn,
6688
+ data: txn.input,
6689
+ gasPrice: txn.type !== "eip1559" ? txn.gasPrice : void 0,
6690
+ maxFeePerGas: txn.type === "eip1559" ? txn.maxFeePerGas : void 0,
6691
+ maxPriorityFeePerGas: txn.type === "eip1559" ? txn.maxPriorityFeePerGas : void 0
6692
+ });
6693
+ const reason = code?.data ? (0, import_viem.hexToString)(`0x${code.data.substring(138)}`) : "unknown reason";
6694
+ throw new Error(reason);
6695
+ }
6696
+ return {
6697
+ ...receipt,
6698
+ chainId: client.chain.id
6699
+ };
6700
+ }
6701
+
6702
+ // ../../node_modules/@wagmi/core/dist/esm/exports/index.js
6703
+ var import_viem2 = require("viem");
6704
+
6705
+ // src/hooks/useCryptoPayment.ts
6706
+ var import_viem3 = require("viem");
6707
+ var import_wagmi = require("wagmi");
6708
+
6709
+ // src/config/baseTokens.ts
6710
+ var BASE_CHAIN_ID = 8453;
6711
+ var BASE_TOKENS = [
6712
+ {
6713
+ address: "0x2b11834ed1feaed4b4b3a86a6f571315e25a884d",
6714
+ chainId: BASE_CHAIN_ID,
6715
+ decimals: 18,
6716
+ symbol: "MOCA" /* MOCA */,
6717
+ name: "Moca",
6718
+ logoURI: "https://assets.coingecko.com/coins/images/30046/standard/moca.png"
6719
+ },
6720
+ {
6721
+ address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bDA02913",
6722
+ chainId: BASE_CHAIN_ID,
6723
+ decimals: 6,
6724
+ symbol: "USDC" /* USDC */,
6725
+ name: "USD Coin",
6726
+ logoURI: "https://cryptologos.cc/logos/usd-coin-usdc-logo.png"
6727
+ },
6728
+ {
6729
+ address: "0x4200000000000000000000000000000000000006",
6730
+ chainId: BASE_CHAIN_ID,
6731
+ decimals: 18,
6732
+ symbol: "WETH" /* WETH */,
6733
+ name: "Wrapped Ether",
6734
+ logoURI: "https://static.cdnlogo.com/logos/e/84/ethereum-eth_thumb.png"
6735
+ },
6736
+ {
6737
+ address: "0xfde4c96c8593536e31f229ea8f37b2ada2699bb2",
6738
+ chainId: BASE_CHAIN_ID,
6739
+ decimals: 6,
6740
+ symbol: "USDT" /* USDT */,
6741
+ name: "Tether USD",
6742
+ logoURI: "https://cryptologos.cc/logos/tether-usdt-logo.png"
6743
+ }
6744
+ ];
6745
+
6746
+ // src/hooks/useCryptoPayment.ts
6747
+ var MAX_UINT256 = BigInt(2) ** BigInt(256) - BigInt(1);
6748
+ var ONE_INCH_AGGREGATION_ROUTER_V6 = "0x111111125421ca6dc452d289314280a0f8842a65";
6749
+ var useCryptoPayment = () => {
6750
+ const { data: walletClient } = (0, import_wagmi.useWalletClient)();
6751
+ const config2 = (0, import_wagmi.useConfig)();
6752
+ const { selectedPaymentMethod } = useSpreePaymentMethod();
6753
+ const cryptoPayment = async ({ hash }) => {
6754
+ if (!walletClient) {
6755
+ throw new Error("Wallet not connected");
6664
6756
  }
6665
- });
6666
- const encodedTX = paymentRes.data.encodedTx;
6667
- const parsedTX = JSON.parse(encodedTX);
6668
- const txHash = await sendTransaction(parsedTX);
6669
- const res = await SlapiPaymentService.baseVerify({ id: paymentRes.data.txId, txHash });
6670
- return { status: res.verified ? "AUTHORIZED" /* AUTHORIZED */ : "FAILED" /* FAILED */ };
6757
+ if (selectedPaymentMethod.type !== "CRYPTO" /* CRYPTO */ || !selectedPaymentMethod.method?.symbol) {
6758
+ throw new Error("Unsupported payment method");
6759
+ }
6760
+ const TOKEN = selectedPaymentMethod.method.symbol;
6761
+ if (["MOCA" /* MOCA */, "WETH" /* WETH */, "USDC" /* USDC */, "USDT" /* USDT */].includes(TOKEN)) {
6762
+ const tokenAddress = selectedPaymentMethod.method.address;
6763
+ if (!tokenAddress) {
6764
+ throw new Error("Token address not found");
6765
+ }
6766
+ const allowance = await readContract(config2, {
6767
+ address: tokenAddress,
6768
+ abi: import_viem3.erc20Abi,
6769
+ functionName: "allowance",
6770
+ args: [walletClient.account.address, ONE_INCH_AGGREGATION_ROUTER_V6]
6771
+ });
6772
+ if (allowance <= 0n) {
6773
+ const result = await walletClient.writeContract({
6774
+ address: tokenAddress,
6775
+ abi: import_viem3.erc20Abi,
6776
+ functionName: "approve",
6777
+ args: [ONE_INCH_AGGREGATION_ROUTER_V6, MAX_UINT256]
6778
+ });
6779
+ await waitForTransactionReceipt(config2, {
6780
+ hash: result,
6781
+ confirmations: 1
6782
+ // You can change the number of block confirmations as per your requirement
6783
+ });
6784
+ }
6785
+ }
6786
+ const paymentRes = await SlapiPaymentService.createPayment({
6787
+ type: "CRYPTO" /* CRYPTO */,
6788
+ hash,
6789
+ crypto: {
6790
+ token: TOKEN,
6791
+ publicKey: walletClient.account.address,
6792
+ slippageType: "fixed",
6793
+ slippageBps: 0.5 * 100
6794
+ }
6795
+ });
6796
+ const parsedTX = JSON.parse(paymentRes.data.encodedTx);
6797
+ const txHash = await walletClient.sendTransaction({
6798
+ account: walletClient.account.address,
6799
+ to: parsedTX.to,
6800
+ data: parsedTX.data,
6801
+ value: parsedTX.value
6802
+ });
6803
+ const res = await SlapiPaymentService.baseVerify({ id: paymentRes.data.txId, txHash });
6804
+ return {
6805
+ txHash,
6806
+ paymentId: paymentRes.data.txId,
6807
+ status: res.verified ? "AUTHORIZED" /* AUTHORIZED */ : "FAILED" /* FAILED */
6808
+ };
6809
+ };
6810
+ return { cryptoPayment };
6671
6811
  };
6672
6812
 
6673
6813
  // src/components/CryptoTab/Crypto/ConnectButton.tsx
@@ -6830,50 +6970,12 @@ var import_nice_modal_react3 = __toESM(require("@ebay/nice-modal-react"), 1);
6830
6970
 
6831
6971
  // src/hooks/useBaseERC20Token.ts
6832
6972
  var React30 = __toESM(require("react"), 1);
6833
- var import_viem = require("viem");
6834
- var import_wagmi = require("wagmi");
6835
-
6836
- // src/config/baseTokens.ts
6837
- var BASE_TOKENS = [
6838
- {
6839
- address: "0x2b11834ed1feaed4b4b3a86a6f571315e25a884d",
6840
- chainId: 8453,
6841
- decimals: 18,
6842
- symbol: "MOCA",
6843
- name: "Moca",
6844
- logoURI: "https://assets.coingecko.com/coins/images/30046/standard/moca.png"
6845
- },
6846
- {
6847
- address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bDA02913",
6848
- chainId: 8453,
6849
- decimals: 6,
6850
- symbol: "USDC",
6851
- name: "USD Coin",
6852
- logoURI: "https://cryptologos.cc/logos/usd-coin-usdc-logo.png"
6853
- },
6854
- {
6855
- address: "0x4200000000000000000000000000000000000006",
6856
- chainId: 8453,
6857
- decimals: 18,
6858
- symbol: "WETH",
6859
- name: "Wrapped Ether",
6860
- logoURI: "https://static.cdnlogo.com/logos/e/84/ethereum-eth_thumb.png"
6861
- },
6862
- {
6863
- address: "0xfde4c96c8593536e31f229ea8f37b2ada2699bb2",
6864
- chainId: 8453,
6865
- decimals: 6,
6866
- symbol: "USDT",
6867
- name: "Tether USD",
6868
- logoURI: "https://cryptologos.cc/logos/tether-usdt-logo.png"
6869
- }
6870
- ];
6871
-
6872
- // src/hooks/useBaseERC20Token.ts
6973
+ var import_viem4 = require("viem");
6974
+ var import_wagmi2 = require("wagmi");
6873
6975
  function useBaseERC20Token() {
6874
- const { address } = (0, import_wagmi.useAccount)();
6875
- const baseClient = (0, import_wagmi.usePublicClient)({ chainId: 8453 });
6876
- const defaultClient = (0, import_wagmi.usePublicClient)();
6976
+ const { address } = (0, import_wagmi2.useAccount)();
6977
+ const baseClient = (0, import_wagmi2.usePublicClient)({ chainId: BASE_CHAIN_ID });
6978
+ const defaultClient = (0, import_wagmi2.usePublicClient)();
6877
6979
  const [rows, setRows] = React30.useState([]);
6878
6980
  const [isLoading, setLoading] = React30.useState(false);
6879
6981
  const [error, setError] = React30.useState(null);
@@ -6890,7 +6992,7 @@ function useBaseERC20Token() {
6890
6992
  const normalizedTokens = [];
6891
6993
  for (const t of BASE_TOKENS) {
6892
6994
  try {
6893
- const addr = (0, import_viem.getAddress)(t.address);
6995
+ const addr = (0, import_viem4.getAddress)(t.address);
6894
6996
  normalizedTokens.push({ ...t, address: addr });
6895
6997
  } catch {
6896
6998
  }
@@ -6899,7 +7001,7 @@ function useBaseERC20Token() {
6899
7001
  allowFailure: true,
6900
7002
  contracts: normalizedTokens.map((t) => ({
6901
7003
  address: t.address,
6902
- abi: import_viem.erc20Abi,
7004
+ abi: import_viem4.erc20Abi,
6903
7005
  functionName: "balanceOf",
6904
7006
  args: [address]
6905
7007
  }))
@@ -6910,14 +7012,16 @@ function useBaseERC20Token() {
6910
7012
  const t = normalizedTokens[idx];
6911
7013
  if (r2.status === "success") {
6912
7014
  const raw = r2.result;
6913
- if (raw > 0n) acc.push({ ...t, raw, formatted: (0, import_viem.formatUnits)(raw, t.decimals) });
7015
+ if (raw > 0n) acc.push({ ...t, raw, formatted: (0, import_viem4.formatUnits)(raw, t.decimals) });
6914
7016
  }
6915
7017
  }
6916
7018
  if (!cancelled) setRows(acc);
6917
7019
  } catch (e) {
6918
7020
  if (!cancelled) {
6919
7021
  const msg = e instanceof Error ? e.message : "Multicall failed";
6920
- setError(baseClient ? msg : "Base client unavailable. Ensure Base (8453) is configured in Wagmi.");
7022
+ setError(
7023
+ baseClient ? msg : `Base client unavailable. Ensure Base (${BASE_CHAIN_ID}) is configured in Wagmi.`
7024
+ );
6921
7025
  }
6922
7026
  } finally {
6923
7027
  if (!cancelled) setLoading(false);
@@ -6932,16 +7036,15 @@ function useBaseERC20Token() {
6932
7036
  }
6933
7037
 
6934
7038
  // src/hooks/useBaseNativeToken.ts
6935
- var import_wagmi2 = require("wagmi");
7039
+ var import_wagmi3 = require("wagmi");
6936
7040
  function useBaseNativeToken() {
6937
- const { address } = (0, import_wagmi2.useAccount)();
6938
- const { data, isLoading, error } = (0, import_wagmi2.useBalance)({
7041
+ const { address } = (0, import_wagmi3.useAccount)();
7042
+ const { data, isLoading, error } = (0, import_wagmi3.useBalance)({
6939
7043
  address,
6940
- chainId: 8453,
6941
- // Base
7044
+ chainId: BASE_CHAIN_ID,
6942
7045
  query: { enabled: !!address }
6943
7046
  });
6944
- const nativeBalance = data ? { ...data, logoURI: "https://static.cdnlogo.com/logos/e/84/ethereum-eth_thumb.png" } : void 0;
7047
+ const nativeBalance = data ? { ...data, symbol: "ETH" /* ETH */, logoURI: "https://static.cdnlogo.com/logos/e/84/ethereum-eth_thumb.png" } : void 0;
6945
7048
  return {
6946
7049
  isLoadingNative: isLoading,
6947
7050
  nativeError: error?.message ?? null,
@@ -7009,7 +7112,6 @@ var CryptoSelectModal = import_nice_modal_react3.default.create(() => {
7009
7112
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
7010
7113
  "button",
7011
7114
  {
7012
- disabled: true,
7013
7115
  className: "flex h-11 w-full items-center justify-between gap-4 rounded-sm px-1.5 hover:bg-gray-100 disabled:cursor-not-allowed disabled:opacity-50",
7014
7116
  onClick: () => handleSelect(coin),
7015
7117
  children: [
@@ -7069,41 +7171,24 @@ var SelectedCoin = (props) => {
7069
7171
  // src/components/CryptoTab/Crypto/Crypto.tsx
7070
7172
  var import_jsx_runtime31 = require("react/jsx-runtime");
7071
7173
  var Crypto = () => {
7072
- const { address } = (0, import_wagmi3.useAccount)();
7073
- const { data: walletClient } = (0, import_wagmi3.useWalletClient)();
7174
+ const { address } = (0, import_wagmi4.useAccount)();
7074
7175
  const { selectedPaymentMethod } = useSpreePaymentMethod();
7176
+ const { cryptoPayment } = useCryptoPayment();
7075
7177
  const isWalletConnected = Boolean(address);
7076
7178
  const { register } = useSpreePayRegister();
7077
7179
  const handlePay = (0, import_react10.useCallback)(
7078
7180
  async (data) => {
7079
7181
  try {
7080
- if (selectedPaymentMethod.type === "CRYPTO" /* CRYPTO */ && selectedPaymentMethod.method) {
7081
- if (!walletClient || !address) throw new Error("Wallet not connected");
7082
- const sendTransaction = (encodedTx) => {
7083
- return walletClient.sendTransaction({
7084
- account: address,
7085
- to: encodedTx.to,
7086
- data: encodedTx.data,
7087
- value: encodedTx.value
7088
- });
7089
- };
7090
- const res = await cryptoPayment({
7091
- hash: data.hash,
7092
- coinSymbol: selectedPaymentMethod.method.symbol,
7093
- publicKey: address,
7094
- sendTransaction
7095
- });
7096
- if (["AUTHORIZED" /* AUTHORIZED */, "CAPTURED" /* CAPTURED */].includes(res.status)) {
7097
- return Promise.resolve(res);
7098
- }
7099
- return Promise.reject(new PaymentError("Crypto payment failed", res.status));
7182
+ const res = await cryptoPayment({ hash: data.hash });
7183
+ if (["AUTHORIZED" /* AUTHORIZED */, "CAPTURED" /* CAPTURED */].includes(res.status)) {
7184
+ return Promise.resolve(res);
7100
7185
  }
7101
- return Promise.reject(new PaymentError("Unsupported payment method", "FAILED" /* FAILED */));
7186
+ return Promise.reject(new PaymentError("Crypto payment failed", res.status));
7102
7187
  } catch (_) {
7103
7188
  return Promise.reject(new PaymentError("Payment failed", "FAILED" /* FAILED */));
7104
7189
  }
7105
7190
  },
7106
- [selectedPaymentMethod, address, walletClient]
7191
+ [cryptoPayment]
7107
7192
  );
7108
7193
  (0, import_react10.useEffect)(() => {
7109
7194
  register(handlePay);
@@ -7138,7 +7223,7 @@ var config = (0, import_rainbowkit2.getDefaultConfig)({
7138
7223
  ssr: true
7139
7224
  });
7140
7225
  var CryptoWrapper = () => {
7141
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_wagmi4.WagmiProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_react_query.QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_rainbowkit2.RainbowKitProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_nice_modal_react5.default.Provider, { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Crypto, {}) }) }) }) });
7226
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_wagmi5.WagmiProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_react_query.QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_rainbowkit2.RainbowKitProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_nice_modal_react5.default.Provider, { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Crypto, {}) }) }) }) });
7142
7227
  };
7143
7228
 
7144
7229
  // src/components/CryptoTab/CryptoTab.tsx
@@ -7213,7 +7298,7 @@ var Tabs = () => {
7213
7298
  const { selectedPaymentMethod, setSelectedPaymentMethod } = useSpreePaymentMethod();
7214
7299
  return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "mb-4 rounded-2xl border border-black/25 bg-white", children: [
7215
7300
  /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex w-full flex-col gap-4 border-b-1 border-black/7 px-7 py-6", children: [
7216
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("h2", { className: "text-primary text-2xl font-semibold", children: "Choose a 123Payment Method" }),
7301
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("h2", { className: "text-primary text-2xl font-semibold", children: "Choose a Payment Method" }),
7217
7302
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(TabButtons, { value: selectedPaymentMethod.type, onChange: setSelectedPaymentMethod })
7218
7303
  ] }),
7219
7304
  selectedPaymentMethod.type === "CREDIT_CARD" /* CREDIT_CARD */ && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(CreditCardTab, {}),
@@ -7224,32 +7309,150 @@ var Tabs = () => {
7224
7309
  // src/SpreePayContent.tsx
7225
7310
  var import_jsx_runtime36 = require("react/jsx-runtime");
7226
7311
  var SpreePayContent = (props) => {
7227
- const { className, amount, onProcess } = props;
7228
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: cn("sl-spreepay", className), children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "w-full", children: [
7312
+ const { amount, onProcess } = props;
7313
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "w-full", children: [
7229
7314
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Tabs, {}),
7230
7315
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(CheckoutButton, { onCheckout: onProcess, amount }),
7231
7316
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(SpreeLegal, {})
7232
- ] }) });
7317
+ ] });
7233
7318
  };
7234
7319
 
7320
+ // src/hooks/useKeycloakSSO.ts
7321
+ var import_react11 = require("react");
7322
+ var import_keycloak_js = __toESM(require("keycloak-js"), 1);
7323
+ var refreshAheadSeconds = 60;
7324
+ function useKeycloakSSO(config2) {
7325
+ const { url, realm, clientId, ssoPageURI } = config2;
7326
+ const initRef = (0, import_react11.useRef)(false);
7327
+ const kcRef = (0, import_react11.useRef)(null);
7328
+ const refreshTimerRef = (0, import_react11.useRef)(null);
7329
+ const [error, setError] = (0, import_react11.useState)(null);
7330
+ const [isChecking, setIsChecking] = (0, import_react11.useState)(false);
7331
+ const [accessToken, setAccessToken] = (0, import_react11.useState)(null);
7332
+ const scheduleRefresh = (0, import_react11.useCallback)(() => {
7333
+ const kc = kcRef.current;
7334
+ if (!kc || !kc.tokenParsed || !kc.tokenParsed.exp) {
7335
+ return;
7336
+ }
7337
+ const expSeconds = kc.tokenParsed.exp;
7338
+ const nowSeconds = Math.floor(Date.now() / 1e3);
7339
+ const delaySeconds = Math.max(expSeconds - nowSeconds - refreshAheadSeconds, 5);
7340
+ const delayMs = delaySeconds * 1e3;
7341
+ if (refreshTimerRef.current) clearTimeout(refreshTimerRef.current);
7342
+ refreshTimerRef.current = setTimeout(() => {
7343
+ kc.updateToken(refreshAheadSeconds).then((refreshed) => {
7344
+ if (refreshed) {
7345
+ setAccessToken(kc.token ?? null);
7346
+ scheduleRefresh();
7347
+ }
7348
+ }).catch((_) => {
7349
+ kc.login().catch(console.error);
7350
+ });
7351
+ }, delayMs);
7352
+ }, []);
7353
+ (0, import_react11.useEffect)(() => {
7354
+ if (initRef.current) return;
7355
+ initRef.current = true;
7356
+ setIsChecking(true);
7357
+ const kc = new import_keycloak_js.default({ url, realm, clientId });
7358
+ kcRef.current = kc;
7359
+ kc.onTokenExpired = () => {
7360
+ kc.updateToken(refreshAheadSeconds).then((refreshed) => {
7361
+ if (refreshed) {
7362
+ setAccessToken(kc.token ?? null);
7363
+ scheduleRefresh();
7364
+ }
7365
+ }).catch((err) => {
7366
+ console.error("[Keycloak] onTokenExpired refresh failed", err);
7367
+ });
7368
+ };
7369
+ kc.init({
7370
+ onLoad: "check-sso",
7371
+ silentCheckSsoRedirectUri: `${window.location.origin}${ssoPageURI}`,
7372
+ silentCheckSsoFallback: true
7373
+ }).then((auth) => {
7374
+ console.log("[Keycloak] init success, authenticated=", auth);
7375
+ setAccessToken(kc.token ?? null);
7376
+ if (auth) scheduleRefresh();
7377
+ }).catch((err) => {
7378
+ setError(err);
7379
+ }).finally(() => setIsChecking(false));
7380
+ return () => {
7381
+ if (refreshTimerRef.current) clearTimeout(refreshTimerRef.current);
7382
+ };
7383
+ }, [ssoPageURI, scheduleRefresh, clientId, url, realm]);
7384
+ return { isChecking, accessToken, error };
7385
+ }
7386
+
7235
7387
  // src/SpreePay.tsx
7236
7388
  var import_jsx_runtime37 = require("react/jsx-runtime");
7237
7389
  var envConfig = {
7238
- dev: { slapiUrl: "https://slapi.dev.superlogic.com" },
7239
- stg: { slapiUrl: "https://slapi.dev.superlogic.com" },
7240
- prod: { slapiUrl: "https://slapi.dev.superlogic.com" }
7390
+ dev: {
7391
+ bookit: {
7392
+ slapiUrl: "https://slapi.dev.superlogic.com",
7393
+ keyklockUrl: "https://auth.dev.join.bookit.com",
7394
+ keyklockClientId: "oneof-next"
7395
+ },
7396
+ moca: {
7397
+ slapiUrl: "https://slapi.dev.air.shop",
7398
+ keyklockUrl: "https://login.dev.air.shop",
7399
+ keyklockClientId: "oneof-next"
7400
+ }
7401
+ },
7402
+ stg: {
7403
+ bookit: {
7404
+ slapiUrl: "https://slapi.stg.superlogic.com",
7405
+ keyklockUrl: "https://auth.stg.join.bookit.com",
7406
+ keyklockClientId: "oneof-next"
7407
+ },
7408
+ moca: {
7409
+ slapiUrl: "https://slapi.stg.superlogic.com",
7410
+ keyklockUrl: "https://login.stg.air.shop",
7411
+ keyklockClientId: "oneof-next"
7412
+ }
7413
+ },
7414
+ prod: {
7415
+ bookit: {
7416
+ slapiUrl: "https://slapi.superlogic.com",
7417
+ keyklockUrl: "https://auth.join.bookit.com",
7418
+ keyklockClientId: "oneof-next"
7419
+ },
7420
+ moca: {
7421
+ slapiUrl: "https://slapi.superlogic.com",
7422
+ keyklockUrl: "https://login.air.shop",
7423
+ keyklockClientId: "oneof-next"
7424
+ }
7425
+ }
7241
7426
  };
7242
- var SpreePay = (props) => {
7427
+ var SpreePay = ({ className, ...rest }) => {
7243
7428
  const { env } = useSpreePayEnv();
7244
- const slapiFetcher = (0, import_react11.useMemo)(
7245
- () => registerApi({
7246
- tenantId: env.tenantId,
7247
- accessToken: env.accessToken,
7248
- baseUrl: envConfig[env.environment].slapiUrl
7249
- }),
7250
- [env]
7251
- );
7252
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
7429
+ const environment = env?.environment || "dev";
7430
+ const tenantId = env?.tenantId || "bookit";
7431
+ const { isChecking, accessToken, error } = useKeycloakSSO({
7432
+ realm: env.tenantId,
7433
+ url: envConfig[environment][tenantId].keyklockUrl,
7434
+ clientId: envConfig[environment][tenantId].keyklockClientId,
7435
+ ssoPageURI: env.ssoPageURI
7436
+ });
7437
+ const slapiFetcher = (0, import_react12.useMemo)(() => {
7438
+ if (accessToken) {
7439
+ return registerApi({
7440
+ accessToken,
7441
+ tenantId,
7442
+ baseUrl: envConfig[environment][tenantId].slapiUrl
7443
+ });
7444
+ }
7445
+ }, [environment, tenantId, accessToken]);
7446
+ if (isChecking) {
7447
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: cn("sl-spreepay", className), children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "w-full text-center text-sm", children: "Loading..." }) });
7448
+ }
7449
+ if (error) {
7450
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: cn("sl-spreepay", className), children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("p", { className: "w-full text-center text-sm", children: [
7451
+ "Error: ",
7452
+ error.message
7453
+ ] }) });
7454
+ }
7455
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: cn("sl-spreepay", className), children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
7253
7456
  import_swr4.SWRConfig,
7254
7457
  {
7255
7458
  value: {
@@ -7258,15 +7461,15 @@ var SpreePay = (props) => {
7258
7461
  revalidateIfStale: false,
7259
7462
  revalidateOnReconnect: false
7260
7463
  },
7261
- children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_nice_modal_react6.default.Provider, { children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(SpreePayContent, { ...props }) })
7464
+ children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_nice_modal_react6.default.Provider, { children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(SpreePayContent, { ...rest }) })
7262
7465
  }
7263
- );
7466
+ ) });
7264
7467
  };
7265
7468
 
7266
7469
  // src/hooks/useCapture3DS.ts
7267
- var import_react12 = require("react");
7470
+ var import_react13 = require("react");
7268
7471
  var useCapture3DS = (searchParams) => {
7269
- (0, import_react12.useEffect)(() => {
7472
+ (0, import_react13.useEffect)(() => {
7270
7473
  if (window?.parent && searchParams?.paymentIntent) {
7271
7474
  window.parent.SP_EVENT_BUS?.emit("paymentIntent", { paymentIntent: searchParams.paymentIntent });
7272
7475
  }
package/build/index.d.cts CHANGED
@@ -12,11 +12,19 @@ declare const useCapture3DS: (searchParams: Record<string, string | null>) => vo
12
12
 
13
13
  type ENV = {
14
14
  environment: 'dev' | 'stg' | 'prod';
15
- accessToken: string;
15
+ ssoPageURI: string;
16
16
  tenantId: string;
17
17
  redirect3dsURI: string;
18
18
  };
19
19
 
20
+ declare enum BASE_SYMBOLS {
21
+ MOCA = "MOCA",
22
+ USDC = "USDC",
23
+ WETH = "WETH",
24
+ USDT = "USDT",
25
+ ETH = "ETH"
26
+ }
27
+
20
28
  type Coin = {
21
29
  address: `0x${string}`;
22
30
  chainId: number;
@@ -25,13 +33,13 @@ type Coin = {
25
33
  logoURI?: string;
26
34
  name: string;
27
35
  raw?: bigint;
28
- symbol: string;
36
+ symbol: BASE_SYMBOLS;
29
37
  };
30
38
  type NativeCoin = {
31
39
  decimals: number;
32
40
  formatted: string;
33
41
  logoURI: string;
34
- symbol: string;
42
+ symbol: BASE_SYMBOLS;
35
43
  value: bigint;
36
44
  };
37
45
 
@@ -72,6 +80,20 @@ declare enum PaymentType {
72
80
  CREDIT_CARD = "CREDIT_CARD",
73
81
  CRYPTO = "CRYPTO"
74
82
  }
83
+ declare const enum SlapiPaymentStatus {
84
+ NOT_INITIALIZED = "NOT_INITIALIZED",// back-end only
85
+ PENDING = "PENDING",// back-end only
86
+ AUTHORIZED = "AUTHORIZED",// success
87
+ FAILED = "FAILED",// failed
88
+ CAPTURED = "CAPTURED",// success
89
+ VOIDED = "VOIDED",// back-end only
90
+ CAPTURE_FAILED = "CAPTURE_FAILED"
91
+ }
92
+ type PaymentMethodResult = {
93
+ status: SlapiPaymentStatus;
94
+ txHash?: string;
95
+ paymentId?: string;
96
+ };
75
97
 
76
98
  type SpreePayProviderProps = {
77
99
  children: ReactNode;
@@ -81,9 +103,7 @@ declare const SpreePayProvider: FC<SpreePayProviderProps>;
81
103
  declare const useSpreePay: () => {
82
104
  process: (data: {
83
105
  hash: string;
84
- }) => Promise<{
85
- status: string;
86
- }>;
106
+ }) => Promise<PaymentMethodResult>;
87
107
  enabled: boolean;
88
108
  selectedPaymentMethod: SelectedPaymentMethod;
89
109
  };
package/build/index.d.ts CHANGED
@@ -12,11 +12,19 @@ declare const useCapture3DS: (searchParams: Record<string, string | null>) => vo
12
12
 
13
13
  type ENV = {
14
14
  environment: 'dev' | 'stg' | 'prod';
15
- accessToken: string;
15
+ ssoPageURI: string;
16
16
  tenantId: string;
17
17
  redirect3dsURI: string;
18
18
  };
19
19
 
20
+ declare enum BASE_SYMBOLS {
21
+ MOCA = "MOCA",
22
+ USDC = "USDC",
23
+ WETH = "WETH",
24
+ USDT = "USDT",
25
+ ETH = "ETH"
26
+ }
27
+
20
28
  type Coin = {
21
29
  address: `0x${string}`;
22
30
  chainId: number;
@@ -25,13 +33,13 @@ type Coin = {
25
33
  logoURI?: string;
26
34
  name: string;
27
35
  raw?: bigint;
28
- symbol: string;
36
+ symbol: BASE_SYMBOLS;
29
37
  };
30
38
  type NativeCoin = {
31
39
  decimals: number;
32
40
  formatted: string;
33
41
  logoURI: string;
34
- symbol: string;
42
+ symbol: BASE_SYMBOLS;
35
43
  value: bigint;
36
44
  };
37
45
 
@@ -72,6 +80,20 @@ declare enum PaymentType {
72
80
  CREDIT_CARD = "CREDIT_CARD",
73
81
  CRYPTO = "CRYPTO"
74
82
  }
83
+ declare const enum SlapiPaymentStatus {
84
+ NOT_INITIALIZED = "NOT_INITIALIZED",// back-end only
85
+ PENDING = "PENDING",// back-end only
86
+ AUTHORIZED = "AUTHORIZED",// success
87
+ FAILED = "FAILED",// failed
88
+ CAPTURED = "CAPTURED",// success
89
+ VOIDED = "VOIDED",// back-end only
90
+ CAPTURE_FAILED = "CAPTURE_FAILED"
91
+ }
92
+ type PaymentMethodResult = {
93
+ status: SlapiPaymentStatus;
94
+ txHash?: string;
95
+ paymentId?: string;
96
+ };
75
97
 
76
98
  type SpreePayProviderProps = {
77
99
  children: ReactNode;
@@ -81,9 +103,7 @@ declare const SpreePayProvider: FC<SpreePayProviderProps>;
81
103
  declare const useSpreePay: () => {
82
104
  process: (data: {
83
105
  hash: string;
84
- }) => Promise<{
85
- status: string;
86
- }>;
106
+ }) => Promise<PaymentMethodResult>;
87
107
  enabled: boolean;
88
108
  selectedPaymentMethod: SelectedPaymentMethod;
89
109
  };
package/build/index.js CHANGED
@@ -5609,7 +5609,8 @@ var cardPayment = async ({ card, hash, redirect3dsURI }) => {
5609
5609
  }
5610
5610
  }
5611
5611
  return {
5612
- status: paymentResData.status
5612
+ status: paymentResData.status,
5613
+ paymentId: paymentResData.id
5613
5614
  };
5614
5615
  };
5615
5616
 
@@ -6609,26 +6610,165 @@ import { base } from "wagmi/chains";
6609
6610
 
6610
6611
  // src/components/CryptoTab/Crypto/Crypto.tsx
6611
6612
  import { useCallback as useCallback9, useEffect as useEffect19 } from "react";
6612
- import { useAccount as useAccount3, useWalletClient } from "wagmi";
6613
+ import { useAccount as useAccount3 } from "wagmi";
6613
6614
 
6614
- // src/services/cryptoPayment.ts
6615
- var cryptoPayment = async (params) => {
6616
- const { publicKey, coinSymbol, hash, sendTransaction } = params;
6617
- const paymentRes = await SlapiPaymentService.createPayment({
6618
- type: "CRYPTO" /* CRYPTO */,
6619
- hash,
6620
- crypto: {
6621
- token: coinSymbol,
6622
- publicKey,
6623
- slippageType: "fixed",
6624
- slippageBps: 0.5 * 100
6615
+ // ../../node_modules/@wagmi/core/dist/esm/utils/getAction.js
6616
+ function getAction(client, actionFn, name) {
6617
+ const action_implicit = client[actionFn.name];
6618
+ if (typeof action_implicit === "function")
6619
+ return action_implicit;
6620
+ const action_explicit = client[name];
6621
+ if (typeof action_explicit === "function")
6622
+ return action_explicit;
6623
+ return (params) => actionFn(client, params);
6624
+ }
6625
+
6626
+ // ../../node_modules/@wagmi/core/dist/esm/actions/readContract.js
6627
+ import { readContract as viem_readContract } from "viem/actions";
6628
+ function readContract(config2, parameters) {
6629
+ const { chainId, ...rest } = parameters;
6630
+ const client = config2.getClient({ chainId });
6631
+ const action = getAction(client, viem_readContract, "readContract");
6632
+ return action(rest);
6633
+ }
6634
+
6635
+ // ../../node_modules/@wagmi/core/dist/esm/actions/waitForTransactionReceipt.js
6636
+ import { hexToString } from "viem";
6637
+ import { call, getTransaction, waitForTransactionReceipt as viem_waitForTransactionReceipt } from "viem/actions";
6638
+ async function waitForTransactionReceipt(config2, parameters) {
6639
+ const { chainId, timeout = 0, ...rest } = parameters;
6640
+ const client = config2.getClient({ chainId });
6641
+ const action = getAction(client, viem_waitForTransactionReceipt, "waitForTransactionReceipt");
6642
+ const receipt = await action({ ...rest, timeout });
6643
+ if (receipt.status === "reverted") {
6644
+ const action_getTransaction = getAction(client, getTransaction, "getTransaction");
6645
+ const txn = await action_getTransaction({ hash: receipt.transactionHash });
6646
+ const action_call = getAction(client, call, "call");
6647
+ const code = await action_call({
6648
+ ...txn,
6649
+ data: txn.input,
6650
+ gasPrice: txn.type !== "eip1559" ? txn.gasPrice : void 0,
6651
+ maxFeePerGas: txn.type === "eip1559" ? txn.maxFeePerGas : void 0,
6652
+ maxPriorityFeePerGas: txn.type === "eip1559" ? txn.maxPriorityFeePerGas : void 0
6653
+ });
6654
+ const reason = code?.data ? hexToString(`0x${code.data.substring(138)}`) : "unknown reason";
6655
+ throw new Error(reason);
6656
+ }
6657
+ return {
6658
+ ...receipt,
6659
+ chainId: client.chain.id
6660
+ };
6661
+ }
6662
+
6663
+ // ../../node_modules/@wagmi/core/dist/esm/exports/index.js
6664
+ import { custom, http, webSocket } from "viem";
6665
+
6666
+ // src/hooks/useCryptoPayment.ts
6667
+ import { erc20Abi } from "viem";
6668
+ import { useConfig as useConfig2, useWalletClient } from "wagmi";
6669
+
6670
+ // src/config/baseTokens.ts
6671
+ var BASE_CHAIN_ID = 8453;
6672
+ var BASE_TOKENS = [
6673
+ {
6674
+ address: "0x2b11834ed1feaed4b4b3a86a6f571315e25a884d",
6675
+ chainId: BASE_CHAIN_ID,
6676
+ decimals: 18,
6677
+ symbol: "MOCA" /* MOCA */,
6678
+ name: "Moca",
6679
+ logoURI: "https://assets.coingecko.com/coins/images/30046/standard/moca.png"
6680
+ },
6681
+ {
6682
+ address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bDA02913",
6683
+ chainId: BASE_CHAIN_ID,
6684
+ decimals: 6,
6685
+ symbol: "USDC" /* USDC */,
6686
+ name: "USD Coin",
6687
+ logoURI: "https://cryptologos.cc/logos/usd-coin-usdc-logo.png"
6688
+ },
6689
+ {
6690
+ address: "0x4200000000000000000000000000000000000006",
6691
+ chainId: BASE_CHAIN_ID,
6692
+ decimals: 18,
6693
+ symbol: "WETH" /* WETH */,
6694
+ name: "Wrapped Ether",
6695
+ logoURI: "https://static.cdnlogo.com/logos/e/84/ethereum-eth_thumb.png"
6696
+ },
6697
+ {
6698
+ address: "0xfde4c96c8593536e31f229ea8f37b2ada2699bb2",
6699
+ chainId: BASE_CHAIN_ID,
6700
+ decimals: 6,
6701
+ symbol: "USDT" /* USDT */,
6702
+ name: "Tether USD",
6703
+ logoURI: "https://cryptologos.cc/logos/tether-usdt-logo.png"
6704
+ }
6705
+ ];
6706
+
6707
+ // src/hooks/useCryptoPayment.ts
6708
+ var MAX_UINT256 = BigInt(2) ** BigInt(256) - BigInt(1);
6709
+ var ONE_INCH_AGGREGATION_ROUTER_V6 = "0x111111125421ca6dc452d289314280a0f8842a65";
6710
+ var useCryptoPayment = () => {
6711
+ const { data: walletClient } = useWalletClient();
6712
+ const config2 = useConfig2();
6713
+ const { selectedPaymentMethod } = useSpreePaymentMethod();
6714
+ const cryptoPayment = async ({ hash }) => {
6715
+ if (!walletClient) {
6716
+ throw new Error("Wallet not connected");
6625
6717
  }
6626
- });
6627
- const encodedTX = paymentRes.data.encodedTx;
6628
- const parsedTX = JSON.parse(encodedTX);
6629
- const txHash = await sendTransaction(parsedTX);
6630
- const res = await SlapiPaymentService.baseVerify({ id: paymentRes.data.txId, txHash });
6631
- return { status: res.verified ? "AUTHORIZED" /* AUTHORIZED */ : "FAILED" /* FAILED */ };
6718
+ if (selectedPaymentMethod.type !== "CRYPTO" /* CRYPTO */ || !selectedPaymentMethod.method?.symbol) {
6719
+ throw new Error("Unsupported payment method");
6720
+ }
6721
+ const TOKEN = selectedPaymentMethod.method.symbol;
6722
+ if (["MOCA" /* MOCA */, "WETH" /* WETH */, "USDC" /* USDC */, "USDT" /* USDT */].includes(TOKEN)) {
6723
+ const tokenAddress = selectedPaymentMethod.method.address;
6724
+ if (!tokenAddress) {
6725
+ throw new Error("Token address not found");
6726
+ }
6727
+ const allowance = await readContract(config2, {
6728
+ address: tokenAddress,
6729
+ abi: erc20Abi,
6730
+ functionName: "allowance",
6731
+ args: [walletClient.account.address, ONE_INCH_AGGREGATION_ROUTER_V6]
6732
+ });
6733
+ if (allowance <= 0n) {
6734
+ const result = await walletClient.writeContract({
6735
+ address: tokenAddress,
6736
+ abi: erc20Abi,
6737
+ functionName: "approve",
6738
+ args: [ONE_INCH_AGGREGATION_ROUTER_V6, MAX_UINT256]
6739
+ });
6740
+ await waitForTransactionReceipt(config2, {
6741
+ hash: result,
6742
+ confirmations: 1
6743
+ // You can change the number of block confirmations as per your requirement
6744
+ });
6745
+ }
6746
+ }
6747
+ const paymentRes = await SlapiPaymentService.createPayment({
6748
+ type: "CRYPTO" /* CRYPTO */,
6749
+ hash,
6750
+ crypto: {
6751
+ token: TOKEN,
6752
+ publicKey: walletClient.account.address,
6753
+ slippageType: "fixed",
6754
+ slippageBps: 0.5 * 100
6755
+ }
6756
+ });
6757
+ const parsedTX = JSON.parse(paymentRes.data.encodedTx);
6758
+ const txHash = await walletClient.sendTransaction({
6759
+ account: walletClient.account.address,
6760
+ to: parsedTX.to,
6761
+ data: parsedTX.data,
6762
+ value: parsedTX.value
6763
+ });
6764
+ const res = await SlapiPaymentService.baseVerify({ id: paymentRes.data.txId, txHash });
6765
+ return {
6766
+ txHash,
6767
+ paymentId: paymentRes.data.txId,
6768
+ status: res.verified ? "AUTHORIZED" /* AUTHORIZED */ : "FAILED" /* FAILED */
6769
+ };
6770
+ };
6771
+ return { cryptoPayment };
6632
6772
  };
6633
6773
 
6634
6774
  // src/components/CryptoTab/Crypto/ConnectButton.tsx
@@ -6791,49 +6931,11 @@ import NiceModal3, { useModal as useModal2 } from "@ebay/nice-modal-react";
6791
6931
 
6792
6932
  // src/hooks/useBaseERC20Token.ts
6793
6933
  import * as React30 from "react";
6794
- import { erc20Abi, formatUnits, getAddress } from "viem";
6934
+ import { erc20Abi as erc20Abi2, formatUnits, getAddress } from "viem";
6795
6935
  import { useAccount, usePublicClient } from "wagmi";
6796
-
6797
- // src/config/baseTokens.ts
6798
- var BASE_TOKENS = [
6799
- {
6800
- address: "0x2b11834ed1feaed4b4b3a86a6f571315e25a884d",
6801
- chainId: 8453,
6802
- decimals: 18,
6803
- symbol: "MOCA",
6804
- name: "Moca",
6805
- logoURI: "https://assets.coingecko.com/coins/images/30046/standard/moca.png"
6806
- },
6807
- {
6808
- address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bDA02913",
6809
- chainId: 8453,
6810
- decimals: 6,
6811
- symbol: "USDC",
6812
- name: "USD Coin",
6813
- logoURI: "https://cryptologos.cc/logos/usd-coin-usdc-logo.png"
6814
- },
6815
- {
6816
- address: "0x4200000000000000000000000000000000000006",
6817
- chainId: 8453,
6818
- decimals: 18,
6819
- symbol: "WETH",
6820
- name: "Wrapped Ether",
6821
- logoURI: "https://static.cdnlogo.com/logos/e/84/ethereum-eth_thumb.png"
6822
- },
6823
- {
6824
- address: "0xfde4c96c8593536e31f229ea8f37b2ada2699bb2",
6825
- chainId: 8453,
6826
- decimals: 6,
6827
- symbol: "USDT",
6828
- name: "Tether USD",
6829
- logoURI: "https://cryptologos.cc/logos/tether-usdt-logo.png"
6830
- }
6831
- ];
6832
-
6833
- // src/hooks/useBaseERC20Token.ts
6834
6936
  function useBaseERC20Token() {
6835
6937
  const { address } = useAccount();
6836
- const baseClient = usePublicClient({ chainId: 8453 });
6938
+ const baseClient = usePublicClient({ chainId: BASE_CHAIN_ID });
6837
6939
  const defaultClient = usePublicClient();
6838
6940
  const [rows, setRows] = React30.useState([]);
6839
6941
  const [isLoading, setLoading] = React30.useState(false);
@@ -6860,7 +6962,7 @@ function useBaseERC20Token() {
6860
6962
  allowFailure: true,
6861
6963
  contracts: normalizedTokens.map((t) => ({
6862
6964
  address: t.address,
6863
- abi: erc20Abi,
6965
+ abi: erc20Abi2,
6864
6966
  functionName: "balanceOf",
6865
6967
  args: [address]
6866
6968
  }))
@@ -6878,7 +6980,9 @@ function useBaseERC20Token() {
6878
6980
  } catch (e) {
6879
6981
  if (!cancelled) {
6880
6982
  const msg = e instanceof Error ? e.message : "Multicall failed";
6881
- setError(baseClient ? msg : "Base client unavailable. Ensure Base (8453) is configured in Wagmi.");
6983
+ setError(
6984
+ baseClient ? msg : `Base client unavailable. Ensure Base (${BASE_CHAIN_ID}) is configured in Wagmi.`
6985
+ );
6882
6986
  }
6883
6987
  } finally {
6884
6988
  if (!cancelled) setLoading(false);
@@ -6898,11 +7002,10 @@ function useBaseNativeToken() {
6898
7002
  const { address } = useAccount2();
6899
7003
  const { data, isLoading, error } = useBalance({
6900
7004
  address,
6901
- chainId: 8453,
6902
- // Base
7005
+ chainId: BASE_CHAIN_ID,
6903
7006
  query: { enabled: !!address }
6904
7007
  });
6905
- const nativeBalance = data ? { ...data, logoURI: "https://static.cdnlogo.com/logos/e/84/ethereum-eth_thumb.png" } : void 0;
7008
+ const nativeBalance = data ? { ...data, symbol: "ETH" /* ETH */, logoURI: "https://static.cdnlogo.com/logos/e/84/ethereum-eth_thumb.png" } : void 0;
6906
7009
  return {
6907
7010
  isLoadingNative: isLoading,
6908
7011
  nativeError: error?.message ?? null,
@@ -6970,7 +7073,6 @@ var CryptoSelectModal = NiceModal3.create(() => {
6970
7073
  return /* @__PURE__ */ jsxs16(
6971
7074
  "button",
6972
7075
  {
6973
- disabled: true,
6974
7076
  className: "flex h-11 w-full items-center justify-between gap-4 rounded-sm px-1.5 hover:bg-gray-100 disabled:cursor-not-allowed disabled:opacity-50",
6975
7077
  onClick: () => handleSelect(coin),
6976
7078
  children: [
@@ -7031,40 +7133,23 @@ var SelectedCoin = (props) => {
7031
7133
  import { jsx as jsx31, jsxs as jsxs19 } from "react/jsx-runtime";
7032
7134
  var Crypto = () => {
7033
7135
  const { address } = useAccount3();
7034
- const { data: walletClient } = useWalletClient();
7035
7136
  const { selectedPaymentMethod } = useSpreePaymentMethod();
7137
+ const { cryptoPayment } = useCryptoPayment();
7036
7138
  const isWalletConnected = Boolean(address);
7037
7139
  const { register } = useSpreePayRegister();
7038
7140
  const handlePay = useCallback9(
7039
7141
  async (data) => {
7040
7142
  try {
7041
- if (selectedPaymentMethod.type === "CRYPTO" /* CRYPTO */ && selectedPaymentMethod.method) {
7042
- if (!walletClient || !address) throw new Error("Wallet not connected");
7043
- const sendTransaction = (encodedTx) => {
7044
- return walletClient.sendTransaction({
7045
- account: address,
7046
- to: encodedTx.to,
7047
- data: encodedTx.data,
7048
- value: encodedTx.value
7049
- });
7050
- };
7051
- const res = await cryptoPayment({
7052
- hash: data.hash,
7053
- coinSymbol: selectedPaymentMethod.method.symbol,
7054
- publicKey: address,
7055
- sendTransaction
7056
- });
7057
- if (["AUTHORIZED" /* AUTHORIZED */, "CAPTURED" /* CAPTURED */].includes(res.status)) {
7058
- return Promise.resolve(res);
7059
- }
7060
- return Promise.reject(new PaymentError("Crypto payment failed", res.status));
7143
+ const res = await cryptoPayment({ hash: data.hash });
7144
+ if (["AUTHORIZED" /* AUTHORIZED */, "CAPTURED" /* CAPTURED */].includes(res.status)) {
7145
+ return Promise.resolve(res);
7061
7146
  }
7062
- return Promise.reject(new PaymentError("Unsupported payment method", "FAILED" /* FAILED */));
7147
+ return Promise.reject(new PaymentError("Crypto payment failed", res.status));
7063
7148
  } catch (_) {
7064
7149
  return Promise.reject(new PaymentError("Payment failed", "FAILED" /* FAILED */));
7065
7150
  }
7066
7151
  },
7067
- [selectedPaymentMethod, address, walletClient]
7152
+ [cryptoPayment]
7068
7153
  );
7069
7154
  useEffect19(() => {
7070
7155
  register(handlePay);
@@ -7174,7 +7259,7 @@ var Tabs = () => {
7174
7259
  const { selectedPaymentMethod, setSelectedPaymentMethod } = useSpreePaymentMethod();
7175
7260
  return /* @__PURE__ */ jsxs22("div", { className: "mb-4 rounded-2xl border border-black/25 bg-white", children: [
7176
7261
  /* @__PURE__ */ jsxs22("div", { className: "flex w-full flex-col gap-4 border-b-1 border-black/7 px-7 py-6", children: [
7177
- /* @__PURE__ */ jsx35("h2", { className: "text-primary text-2xl font-semibold", children: "Choose a 123Payment Method" }),
7262
+ /* @__PURE__ */ jsx35("h2", { className: "text-primary text-2xl font-semibold", children: "Choose a Payment Method" }),
7178
7263
  /* @__PURE__ */ jsx35(TabButtons, { value: selectedPaymentMethod.type, onChange: setSelectedPaymentMethod })
7179
7264
  ] }),
7180
7265
  selectedPaymentMethod.type === "CREDIT_CARD" /* CREDIT_CARD */ && /* @__PURE__ */ jsx35(CreditCardTab, {}),
@@ -7185,32 +7270,150 @@ var Tabs = () => {
7185
7270
  // src/SpreePayContent.tsx
7186
7271
  import { jsx as jsx36, jsxs as jsxs23 } from "react/jsx-runtime";
7187
7272
  var SpreePayContent = (props) => {
7188
- const { className, amount, onProcess } = props;
7189
- return /* @__PURE__ */ jsx36("div", { className: cn("sl-spreepay", className), children: /* @__PURE__ */ jsxs23("div", { className: "w-full", children: [
7273
+ const { amount, onProcess } = props;
7274
+ return /* @__PURE__ */ jsxs23("div", { className: "w-full", children: [
7190
7275
  /* @__PURE__ */ jsx36(Tabs, {}),
7191
7276
  /* @__PURE__ */ jsx36(CheckoutButton, { onCheckout: onProcess, amount }),
7192
7277
  /* @__PURE__ */ jsx36(SpreeLegal, {})
7193
- ] }) });
7278
+ ] });
7194
7279
  };
7195
7280
 
7281
+ // src/hooks/useKeycloakSSO.ts
7282
+ import { useCallback as useCallback10, useEffect as useEffect20, useRef as useRef14, useState as useState18 } from "react";
7283
+ import Keycloak from "keycloak-js";
7284
+ var refreshAheadSeconds = 60;
7285
+ function useKeycloakSSO(config2) {
7286
+ const { url, realm, clientId, ssoPageURI } = config2;
7287
+ const initRef = useRef14(false);
7288
+ const kcRef = useRef14(null);
7289
+ const refreshTimerRef = useRef14(null);
7290
+ const [error, setError] = useState18(null);
7291
+ const [isChecking, setIsChecking] = useState18(false);
7292
+ const [accessToken, setAccessToken] = useState18(null);
7293
+ const scheduleRefresh = useCallback10(() => {
7294
+ const kc = kcRef.current;
7295
+ if (!kc || !kc.tokenParsed || !kc.tokenParsed.exp) {
7296
+ return;
7297
+ }
7298
+ const expSeconds = kc.tokenParsed.exp;
7299
+ const nowSeconds = Math.floor(Date.now() / 1e3);
7300
+ const delaySeconds = Math.max(expSeconds - nowSeconds - refreshAheadSeconds, 5);
7301
+ const delayMs = delaySeconds * 1e3;
7302
+ if (refreshTimerRef.current) clearTimeout(refreshTimerRef.current);
7303
+ refreshTimerRef.current = setTimeout(() => {
7304
+ kc.updateToken(refreshAheadSeconds).then((refreshed) => {
7305
+ if (refreshed) {
7306
+ setAccessToken(kc.token ?? null);
7307
+ scheduleRefresh();
7308
+ }
7309
+ }).catch((_) => {
7310
+ kc.login().catch(console.error);
7311
+ });
7312
+ }, delayMs);
7313
+ }, []);
7314
+ useEffect20(() => {
7315
+ if (initRef.current) return;
7316
+ initRef.current = true;
7317
+ setIsChecking(true);
7318
+ const kc = new Keycloak({ url, realm, clientId });
7319
+ kcRef.current = kc;
7320
+ kc.onTokenExpired = () => {
7321
+ kc.updateToken(refreshAheadSeconds).then((refreshed) => {
7322
+ if (refreshed) {
7323
+ setAccessToken(kc.token ?? null);
7324
+ scheduleRefresh();
7325
+ }
7326
+ }).catch((err) => {
7327
+ console.error("[Keycloak] onTokenExpired refresh failed", err);
7328
+ });
7329
+ };
7330
+ kc.init({
7331
+ onLoad: "check-sso",
7332
+ silentCheckSsoRedirectUri: `${window.location.origin}${ssoPageURI}`,
7333
+ silentCheckSsoFallback: true
7334
+ }).then((auth) => {
7335
+ console.log("[Keycloak] init success, authenticated=", auth);
7336
+ setAccessToken(kc.token ?? null);
7337
+ if (auth) scheduleRefresh();
7338
+ }).catch((err) => {
7339
+ setError(err);
7340
+ }).finally(() => setIsChecking(false));
7341
+ return () => {
7342
+ if (refreshTimerRef.current) clearTimeout(refreshTimerRef.current);
7343
+ };
7344
+ }, [ssoPageURI, scheduleRefresh, clientId, url, realm]);
7345
+ return { isChecking, accessToken, error };
7346
+ }
7347
+
7196
7348
  // src/SpreePay.tsx
7197
- import { jsx as jsx37 } from "react/jsx-runtime";
7349
+ import { jsx as jsx37, jsxs as jsxs24 } from "react/jsx-runtime";
7198
7350
  var envConfig = {
7199
- dev: { slapiUrl: "https://slapi.dev.superlogic.com" },
7200
- stg: { slapiUrl: "https://slapi.dev.superlogic.com" },
7201
- prod: { slapiUrl: "https://slapi.dev.superlogic.com" }
7351
+ dev: {
7352
+ bookit: {
7353
+ slapiUrl: "https://slapi.dev.superlogic.com",
7354
+ keyklockUrl: "https://auth.dev.join.bookit.com",
7355
+ keyklockClientId: "oneof-next"
7356
+ },
7357
+ moca: {
7358
+ slapiUrl: "https://slapi.dev.air.shop",
7359
+ keyklockUrl: "https://login.dev.air.shop",
7360
+ keyklockClientId: "oneof-next"
7361
+ }
7362
+ },
7363
+ stg: {
7364
+ bookit: {
7365
+ slapiUrl: "https://slapi.stg.superlogic.com",
7366
+ keyklockUrl: "https://auth.stg.join.bookit.com",
7367
+ keyklockClientId: "oneof-next"
7368
+ },
7369
+ moca: {
7370
+ slapiUrl: "https://slapi.stg.superlogic.com",
7371
+ keyklockUrl: "https://login.stg.air.shop",
7372
+ keyklockClientId: "oneof-next"
7373
+ }
7374
+ },
7375
+ prod: {
7376
+ bookit: {
7377
+ slapiUrl: "https://slapi.superlogic.com",
7378
+ keyklockUrl: "https://auth.join.bookit.com",
7379
+ keyklockClientId: "oneof-next"
7380
+ },
7381
+ moca: {
7382
+ slapiUrl: "https://slapi.superlogic.com",
7383
+ keyklockUrl: "https://login.air.shop",
7384
+ keyklockClientId: "oneof-next"
7385
+ }
7386
+ }
7202
7387
  };
7203
- var SpreePay = (props) => {
7388
+ var SpreePay = ({ className, ...rest }) => {
7204
7389
  const { env } = useSpreePayEnv();
7205
- const slapiFetcher = useMemo7(
7206
- () => registerApi({
7207
- tenantId: env.tenantId,
7208
- accessToken: env.accessToken,
7209
- baseUrl: envConfig[env.environment].slapiUrl
7210
- }),
7211
- [env]
7212
- );
7213
- return /* @__PURE__ */ jsx37(
7390
+ const environment = env?.environment || "dev";
7391
+ const tenantId = env?.tenantId || "bookit";
7392
+ const { isChecking, accessToken, error } = useKeycloakSSO({
7393
+ realm: env.tenantId,
7394
+ url: envConfig[environment][tenantId].keyklockUrl,
7395
+ clientId: envConfig[environment][tenantId].keyklockClientId,
7396
+ ssoPageURI: env.ssoPageURI
7397
+ });
7398
+ const slapiFetcher = useMemo7(() => {
7399
+ if (accessToken) {
7400
+ return registerApi({
7401
+ accessToken,
7402
+ tenantId,
7403
+ baseUrl: envConfig[environment][tenantId].slapiUrl
7404
+ });
7405
+ }
7406
+ }, [environment, tenantId, accessToken]);
7407
+ if (isChecking) {
7408
+ return /* @__PURE__ */ jsx37("div", { className: cn("sl-spreepay", className), children: /* @__PURE__ */ jsx37("p", { className: "w-full text-center text-sm", children: "Loading..." }) });
7409
+ }
7410
+ if (error) {
7411
+ return /* @__PURE__ */ jsx37("div", { className: cn("sl-spreepay", className), children: /* @__PURE__ */ jsxs24("p", { className: "w-full text-center text-sm", children: [
7412
+ "Error: ",
7413
+ error.message
7414
+ ] }) });
7415
+ }
7416
+ return /* @__PURE__ */ jsx37("div", { className: cn("sl-spreepay", className), children: /* @__PURE__ */ jsx37(
7214
7417
  SWRConfig,
7215
7418
  {
7216
7419
  value: {
@@ -7219,15 +7422,15 @@ var SpreePay = (props) => {
7219
7422
  revalidateIfStale: false,
7220
7423
  revalidateOnReconnect: false
7221
7424
  },
7222
- children: /* @__PURE__ */ jsx37(NiceModal6.Provider, { children: /* @__PURE__ */ jsx37(SpreePayContent, { ...props }) })
7425
+ children: /* @__PURE__ */ jsx37(NiceModal6.Provider, { children: /* @__PURE__ */ jsx37(SpreePayContent, { ...rest }) })
7223
7426
  }
7224
- );
7427
+ ) });
7225
7428
  };
7226
7429
 
7227
7430
  // src/hooks/useCapture3DS.ts
7228
- import { useEffect as useEffect20 } from "react";
7431
+ import { useEffect as useEffect21 } from "react";
7229
7432
  var useCapture3DS = (searchParams) => {
7230
- useEffect20(() => {
7433
+ useEffect21(() => {
7231
7434
  if (window?.parent && searchParams?.paymentIntent) {
7232
7435
  window.parent.SP_EVENT_BUS?.emit("paymentIntent", { paymentIntent: searchParams.paymentIntent });
7233
7436
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superlogic/spree-pay",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Spree-pay React component and utilities",
5
5
  "private": false,
6
6
  "type": "module",
@@ -32,6 +32,7 @@
32
32
  "@stripe/react-stripe-js": "^3.9.0",
33
33
  "@stripe/stripe-js": "^7.8.0",
34
34
  "@tanstack/react-query": "^5.85.5",
35
+ "keycloak-js": "^26.2.0",
35
36
  "swr": "^2.3.6",
36
37
  "viem": "^2.35.1",
37
38
  "wagmi": "^2.16.5"