Skip to main content

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");
}
};