한국어
React hooks를 사용하여 맞춤 결제 흐름을 구축하고 주문 수명 주기를 관리하기
useAnyspendQuote
import { useAnyspendQuote } from "@b3dotfun/sdk/anyspend"; const { anyspendQuote, isLoadingAnyspendQuote, getAnyspendQuoteError, refetchAnyspendQuote } = useAnyspendQuote(quoteRequest);
interface QuoteRequest { srcChain: number; // 출발 체인 ID dstChain: number; // 도착 체인 ID srcTokenAddress: string; // 출발 토큰 계약 주소 dstTokenAddress: string; // 도착 토큰 계약 주소 type: "swap" | "custom"; // 주문 유형 tradeType: "EXACT_INPUT" | "EXACT_OUTPUT"; amount: string; // 최소 단위(wei)로의 금액 }
function SwapQuote() { const quoteRequest = { srcChain: 1, // 이더리움 dstChain: 8333, // B3 srcTokenAddress: "0xA0b86a33E6Fb6Dd9a9B3d8B5FEb2b3C8e7D9Ff1E", // USDC dstTokenAddress: "0x0000000000000000000000000000000000000000", // ETH type: "swap", tradeType: "EXACT_INPUT", amount: "1000000", // 1 USDC (6 decimals) }; const { anyspendQuote, isLoadingAnyspendQuote, getAnyspendQuoteError } = useAnyspendQuote(quoteRequest); if (isLoadingAnyspendQuote) return <div>최적의 가격을 찾는 중...</div>; if (getAnyspendQuoteError) return <div>견적을 가져오는 데 실패했습니다</div>; return ( <div> <p>받을 금액: {anyspendQuote?.expectedOutput} ETH</p> <p>네트워크 수수료: ${anyspendQuote?.networkFeeUsd}</p> <p>서비스 수수료: ${anyspendQuote?.serviceFeeUsd}</p> <p>총 비용: ${anyspendQuote?.totalUsdCost}</p> </div> ); }
useAnyspendCreateOrder
import { useAnyspendCreateOrder } from "@b3dotfun/sdk/anyspend"; const { createOrder, isCreatingOrder, createOrderError } = useAnyspendCreateOrder(options);
interface CreateOrderOptions { onSuccess?: (data: OrderResponse) => void; onError?: (error: Error) => void; onSettled?: () => void; }
function PaymentForm() { const { createOrder, isCreatingOrder } = useAnyspendCreateOrder({ onSuccess: (data) => { console.log("주문 생성됨:", data.data.id); // 결제로 리다이렉트하거나 성공 표시 router.push(`/payment/${data.data.id}`); }, onError: (error) => { console.error("주문 실패:", error.message); toast.error("결제 실패. 다시 시도해주세요."); }, }); const handlePayment = () => { createOrder({ recipientAddress: userWalletAddress, orderType: "swap", srcChain: 1, dstChain: 8333, srcToken: { chainId: 1, address: "0xA0b86a33E6Fb6Dd9a9B3d8B5FEb2b3C8e7D9Ff1E", name: "USD Coin", symbol: "USDC", decimals: 6, }, dstToken: { chainId: 8333, address: "0x0000000000000000000000000000000000000000", name: "Ether", symbol: "ETH", decimals: 18, }, srcAmount: "1000000", // 1 USDC expectedDstAmount: "500000000000000000", // ~0.5 ETH creatorAddress: userWalletAddress, }); }; return ( <button onClick={handlePayment} disabled={isCreatingOrder} > {isCreatingOrder ? "처리 중..." : "암호화폐로 결제하기"} </button> ); }
useAnyspendOrderAndTransactions
import { useAnyspendOrderAndTransactions } from "@b3dotfun/sdk/anyspend"; const { orderAndTransactions, isLoadingOrderAndTransactions, getOrderAndTransactionsError } = useAnyspendOrderAndTransactions(orderId);
interface OrderWithTransactions { data: { order: Order; // 주문 세부 정보 및 상태 depositTxs: Transaction[]; // 사용자 예치 거래 relayTx?: Transaction; // 크로스 체인 중계 거래 executeTx?: Transaction; // 최종 실행 거래 refundTxs: Transaction[]; // 환불 거래(해당되는 경우) }; }
function OrderTracker({ orderId }: { orderId: string }) { const { orderAndTransactions, isLoadingOrderAndTransactions } = useAnyspendOrderAndTransactions(orderId); if (isLoadingOrderAndTransactions) { return <div>주문 상태를 불러오는 중...</div>; } if (!orderAndTransactions) { return <div>주문을 찾을 수 없습니다</div>; } const { order, depositTxs, executeTx, refundTxs } = orderAndTransactions.data; const getStatusMessage = (status: string) => { switch (status) { case "scanning_deposit_transaction": return "⏳ 결제 확인 대기 중..."; case "relay": return "🔄 크로스 체인 거래 처리 중..."; case "executed": return "✅ 거래가 성공적으로 완료되었습니다!"; case "refunded": return "↩️ 환불 처리됨"; default: return "🔄 처리 중..."; } }; return ( <div className="order-status"> <h2>주문 #{orderId.slice(0, 8)}</h2> <p>{getStatusMessage(order.status)}</p> {depositTxs.length > 0 && ( <div> <h3>결제 거래</h3> <a href={`https://etherscan.io/tx/${depositTxs[0].txHash}`} target="_blank" rel="noopener noreferrer" > Etherscan에서 보기 </a> </div> )} {executeTx && ( <div> <h3>실행 거래</h3> <a href={`https://explorer.b3.fun/tx/${executeTx.txHash}`} target="_blank" rel="noopener noreferrer" > B3 탐색기에서 보기 </a> </div> )} {order.errorDetails && ( <div className="error"> <strong>오류:</strong> {order.errorDetails} </div> )} </div> ); }
useAnyspendOrderHistory
import { useAnyspendOrderHistory } from "@b3dotfun/sdk/anyspend"; const { orderHistory, isLoadingOrderHistory, getOrderHistoryError } = useAnyspendOrderHistory(creatorAddress, limit, offset);
function OrderHistory({ userAddress }: { userAddress: string }) { const [page, setPage] = useState(0); const pageSize = 10; const { orderHistory, isLoadingOrderHistory } = useAnyspendOrderHistory( userAddress, pageSize, page * pageSize ); if (isLoadingOrderHistory) { return <div>주문 기록을 불러오는 중...</div>; } return ( <div> <h2>귀하의 주문</h2> {orderHistory?.data.map((order) => ( <div key={order.id} className="order-item"> <p>유형: {order.type}</p> <p>상태: {order.status}</p> <p>금액: {order.srcAmount} {order.srcToken.symbol}</p> <p>날짜: {new Date(order.createdAt).toLocaleDateString()}</p> </div> ))} <button onClick={() => setPage(page - 1)} disabled={page === 0} > 이전 </button> <button onClick={() => setPage(page + 1)} disabled={!orderHistory?.data || orderHistory.data.length < pageSize} > 다음 </button> </div> ); }
useAnyspendTokens
const { tokens, isLoadingTokens } = useAnyspendTokens(1, "USDC");
useCoinbaseOnrampOptions
const { coinbaseOptions, isLoadingCoinbaseOptions } = useCoinbaseOnrampOptions();
useStripeClientSecret
const { clientSecret, isLoadingClientSecret } = useStripeClientSecret(orderData);
function PaymentComponent() { const { createOrder, isCreatingOrder } = useAnyspendCreateOrder({ onError: (error) => { // 디버깅을 위한 오류 로깅 console.error("결제 실패:", error); // 사용자 친화적 메시지 표시 switch (error.message) { case "INSUFFICIENT_BALANCE": toast.error("잔액이 부족합니다. 자금을 추가해주세요."); break; case "SLIPPAGE": toast.error("가격이 불리하게 움직였습니다. 다시 시도해주세요."); break; default: toast.error("결제 실패. 다시 시도해주세요."); } }, }); // 컴포넌트 구현... }
function SwapInterface() { const { anyspendQuote, isLoadingAnyspendQuote } = useAnyspendQuote(quoteRequest); const { createOrder, isCreatingOrder } = useAnyspendCreateOrder(); const isLoading = isLoadingAnyspendQuote || isCreatingOrder; return ( <div> {isLoading && <LoadingSpinner />} {/* 컴포넌트의 나머지 부분 */} </div> ); }
function OrderStatus({ orderId }: { orderId: string }) { const { orderAndTransactions } = useAnyspendOrderAndTransactions(orderId); // 대기 중인 주문에 대해 5초마다 자동 새로고침 useEffect(() => { if (orderAndTransactions?.data.order.status === "relay") { const interval = setInterval(() => { // 훅에 의해 자동으로 다시 조회됨 }, 5000); return () => clearInterval(interval); } }, [orderAndTransactions?.data.order.status]); // 컴포넌트 구현... }