Wallet Checkout API
Process payments using stablecoin wallets (USDC, USDT) for instant settlement and lower fees.
walletCheckout
Process payments with existing customers using their connected stablecoin wallets.
const { walletCheckout } = useEasy();
const result = await walletCheckout({
identity_id: "existing_customer_identity_id",
wallet_id: "wallet_12345",
line_items: [{ price_id: "price_123", quantity: 1 }],
customer_details: {
first_name: "John",
last_name: "Doe",
email: "[email protected]",
},
});
Parameters
interface CreateWalletCheckout {
identity_id: string; // Required: existing customer ID
wallet_id: string; // Required: wallet ID for payment
line_items: LineItem[]; // Required: items to purchase
customer_details?: {
// Optional: customer info update
first_name?: string;
last_name?: string;
email?: string;
phone?: string;
};
metadata?: Record<string, string>; // Optional: custom order data
}
interface LineItem {
price_id: string; // One-time purchase price only
quantity: number;
}
Returns
Promise<
ApiResponse<{
order: OrderData & { solana_signature: string };
transation: {
signature: string;
token_amount: number;
explorerUrl: string;
};
}>
>;
Examples
Basic Wallet Payment
import { useEasy } from "@easylabs/react-native";
import { useState } from "react";
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
Alert,
} from "react-native";
function WalletCheckout() {
const { walletCheckout } = useEasy();
const [loading, setLoading] = useState(false);
const [walletId, setWalletId] = useState("");
const [formData, setFormData] = useState({
first_name: "",
last_name: "",
email: "",
phone: "",
});
const handleSubmit = async () => {
if (!walletId) {
Alert.alert("Error", "Please enter a wallet ID");
return;
}
setLoading(true);
try {
const result = await walletCheckout({
identity_id: "existing_customer_identity_id", // Required: existing customer only
wallet_id: walletId,
line_items: [{ price_id: "price_123", quantity: 1 }],
customer_details: {
first_name: formData.first_name,
last_name: formData.last_name,
email: formData.email,
phone: formData.phone,
},
metadata: {
payment_method: "wallet",
order_source: "mobile",
},
});
Alert.alert("Success", "Wallet checkout successful!");
console.log("Order:", result.data);
} catch (error) {
Alert.alert("Error", "Wallet checkout failed");
console.error(error);
} finally {
setLoading(false);
}
};
return (
<View style={styles.container}>
<Text style={styles.title}>Wallet Checkout</Text>
<TextInput
style={styles.input}
value={formData.first_name}
onChangeText={(text) => setFormData({ ...formData, first_name: text })}
placeholder="First Name"
/>
<TextInput
style={styles.input}
value={formData.last_name}
onChangeText={(text) => setFormData({ ...formData, last_name: text })}
placeholder="Last Name"
/>
<TextInput
style={styles.input}
value={formData.email}
onChangeText={(text) => setFormData({ ...formData, email: text })}
placeholder="Email"
keyboardType="email-address"
autoCapitalize="none"
/>
<TextInput
style={styles.input}
value={formData.phone}
onChangeText={(text) => setFormData({ ...formData, phone: text })}
placeholder="Phone"
keyboardType="phone-pad"
/>
<TextInput
style={styles.input}
value={walletId}
onChangeText={setWalletId}
placeholder="Wallet ID"
/>
<TouchableOpacity
style={[styles.button, loading && styles.buttonDisabled]}
onPress={handleSubmit}
disabled={loading}
>
<Text style={styles.buttonText}>
{loading ? "Processing..." : "Pay with Wallet"}
</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
padding: 16,
},
title: {
fontSize: 24,
fontWeight: "bold",
marginBottom: 20,
},
input: {
borderWidth: 1,
borderColor: "#ddd",
borderRadius: 8,
padding: 12,
marginBottom: 12,
fontSize: 16,
},
button: {
backgroundColor: "#007AFF",
padding: 16,
borderRadius: 8,
alignItems: "center",
marginTop: 8,
},
buttonDisabled: {
backgroundColor: "#ccc",
},
buttonText: {
color: "#fff",
fontSize: 16,
fontWeight: "600",
},
});
Minimal Wallet Payment
import { useEasy } from "@easylabs/react-native";
import { useState } from "react";
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
Alert,
} from "react-native";
function MinimalWalletCheckout({ identityId }) {
const { walletCheckout } = useEasy();
const [loading, setLoading] = useState(false);
const [walletId, setWalletId] = useState("");
const handleCheckout = async () => {
if (!walletId) {
Alert.alert("Error", "Please enter a wallet ID");
return;
}
setLoading(true);
try {
const result = await walletCheckout({
identity_id: identityId,
wallet_id: walletId,
line_items: [{ price_id: "price_456", quantity: 2 }],
});
Alert.alert("Success", "Wallet checkout successful!");
console.log("Order:", result.data);
} catch (error) {
Alert.alert("Error", "Wallet checkout failed");
console.error(error);
} finally {
setLoading(false);
}
};
return (
<View style={styles.container}>
<Text style={styles.label}>Wallet ID</Text>
<TextInput
style={styles.input}
value={walletId}
onChangeText={setWalletId}
placeholder="Enter your wallet ID"
/>
<TouchableOpacity
style={[styles.button, (!walletId || loading) && styles.buttonDisabled]}
onPress={handleCheckout}
disabled={loading || !walletId}
>
<Text style={styles.buttonText}>
{loading ? "Processing..." : "Pay with Wallet"}
</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
padding: 16,
},
label: {
fontSize: 16,
fontWeight: "600",
marginBottom: 8,
},
input: {
borderWidth: 1,
borderColor: "#ddd",
borderRadius: 8,
padding: 12,
marginBottom: 16,
fontSize: 16,
},
button: {
backgroundColor: "#007AFF",
padding: 16,
borderRadius: 8,
alignItems: "center",
},
buttonDisabled: {
backgroundColor: "#ccc",
},
buttonText: {
color: "#fff",
fontSize: 16,
fontWeight: "600",
},
});
Complete Wallet Checkout Flow
import { useEasy } from "@easylabs/react-native";
import { useState } from "react";
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
Alert,
ActivityIndicator,
Linking,
} from "react-native";
function CompleteWalletCheckout({ identityId, priceId }) {
const { walletCheckout } = useEasy();
const [loading, setLoading] = useState(false);
const [walletId, setWalletId] = useState("");
const [transaction, setTransaction] = useState(null);
const handleCheckout = async () => {
if (!walletId) {
Alert.alert("Error", "Please enter a wallet ID");
return;
}
setLoading(true);
try {
const result = await walletCheckout({
identity_id: identityId,
wallet_id: walletId,
line_items: [{ price_id: priceId, quantity: 1 }],
});
setTransaction(result.data.transation);
Alert.alert("Success", "Payment processed successfully!");
} catch (error) {
if (error.code === "WALLET_INSUFFICIENT_FUNDS") {
Alert.alert("Error", "Insufficient wallet balance");
} else if (error.code === "WALLET_NOT_CONNECTED") {
Alert.alert("Error", "Wallet not connected");
} else {
Alert.alert("Error", "Checkout failed");
}
} finally {
setLoading(false);
}
};
const openExplorer = () => {
if (transaction?.explorerUrl) {
Linking.openURL(transaction.explorerUrl);
}
};
return (
<View style={styles.container}>
<Text style={styles.title}>Wallet Checkout</Text>
{!transaction ? (
<>
<Text style={styles.label}>Wallet ID</Text>
<TextInput
style={styles.input}
value={walletId}
onChangeText={setWalletId}
placeholder="Enter your wallet ID"
editable={!loading}
/>
{loading ? (
<ActivityIndicator size="large" color="#007AFF" />
) : (
<TouchableOpacity
style={[styles.button, !walletId && styles.buttonDisabled]}
onPress={handleCheckout}
disabled={!walletId}
>
<Text style={styles.buttonText}>Pay with Wallet</Text>
</TouchableOpacity>
)}
</>
) : (
<View style={styles.successContainer}>
<Text style={styles.successTitle}>Payment Successful!</Text>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>Signature:</Text>
<Text style={styles.detailValue} numberOfLines={1}>
{transaction.signature}
</Text>
</View>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>Amount:</Text>
<Text style={styles.detailValue}>
{transaction.token_amount} tokens
</Text>
</View>
<TouchableOpacity
style={styles.explorerButton}
onPress={openExplorer}
>
<Text style={styles.explorerButtonText}>
View on Blockchain Explorer
</Text>
</TouchableOpacity>
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
padding: 16,
},
title: {
fontSize: 24,
fontWeight: "bold",
marginBottom: 20,
},
label: {
fontSize: 16,
fontWeight: "600",
marginBottom: 8,
},
input: {
borderWidth: 1,
borderColor: "#ddd",
borderRadius: 8,
padding: 12,
marginBottom: 16,
fontSize: 16,
},
button: {
backgroundColor: "#007AFF",
padding: 16,
borderRadius: 8,
alignItems: "center",
},
buttonDisabled: {
backgroundColor: "#ccc",
},
buttonText: {
color: "#fff",
fontSize: 16,
fontWeight: "600",
},
successContainer: {
padding: 16,
backgroundColor: "#E8F5E9",
borderRadius: 8,
},
successTitle: {
fontSize: 20,
fontWeight: "bold",
color: "#2E7D32",
marginBottom: 16,
},
detailRow: {
marginBottom: 12,
},
detailLabel: {
fontSize: 14,
fontWeight: "600",
color: "#666",
marginBottom: 4,
},
detailValue: {
fontSize: 14,
color: "#333",
},
explorerButton: {
backgroundColor: "#007AFF",
padding: 12,
borderRadius: 8,
alignItems: "center",
marginTop: 8,
},
explorerButtonText: {
color: "#fff",
fontSize: 14,
fontWeight: "600",
},
});
Benefits
Wallet Advantages
- Instant Settlement: Payments settle immediately on-chain
- Lower Fees: Significantly reduced processing costs compared to traditional cards
- Global Access: Accept payments from anywhere with stablecoin support
- Transparency: All transactions are verifiable on the blockchain
Important Limitations
Current Restrictions
- Existing Customers Only: Requires an existing customer (
identity_id) - No Customer Creation: Customer creation is not supported during wallet checkout
- One-time Purchases Only: Subscriptions are not currently supported
- Connected Wallets: Customer must have a connected wallet (
wallet_id)
Error Handling
try {
const result = await walletCheckout({
identity_id: customerId,
wallet_id: walletId,
line_items: lineItems,
});
// Handle success
Alert.alert("Success", `Order ID: ${result.data.orderId}`);
} catch (error) {
if (error.code === "WALLET_INSUFFICIENT_FUNDS") {
Alert.alert("Error", "Insufficient wallet balance");
} else if (error.code === "WALLET_NOT_CONNECTED") {
Alert.alert("Error", "Wallet not connected");
} else {
Alert.alert("Error", "Checkout failed");
console.error("Checkout failed:", error);
}
}
Best Practices
1. Validate Wallet Connection
Always check if the customer has a connected wallet before initiating checkout:
const checkWalletConnection = async (customerId) => {
try {
const customer = await getCustomer(customerId);
if (!customer.data.wallet_id) {
Alert.alert("Error", "Please connect your wallet first");
return false;
}
return true;
} catch (error) {
console.error("Failed to check wallet:", error);
return false;
}
};
2. Display Transaction Details
Show blockchain transaction details to users for transparency:
<View>
<Text>Transaction Signature: {transaction.signature}</Text>
<Text>Amount: {transaction.token_amount} tokens</Text>
<TouchableOpacity onPress={() => Linking.openURL(transaction.explorerUrl)}>
<Text>View on Explorer</Text>
</TouchableOpacity>
</View>
3. Handle Network Delays
Blockchain transactions may take a few seconds to confirm:
const [status, setStatus] = useState("idle");
const handleCheckout = async () => {
setStatus("processing");
try {
const result = await walletCheckout({ ... });
setStatus("confirming");
// Wait for confirmation if needed
await waitForConfirmation(result.data.transation.signature);
setStatus("confirmed");
} catch (error) {
setStatus("failed");
}
};
Related APIs
- Checkout API - For traditional card and bank account payments
- Customer Management - Managing customer wallet connections