Accept a wallet payment
Let buyers pay with a Solana wallet through the embedded checkout.
Goal
Accept payment in stablecoins from a buyer's self-custodial wallet (currently Solana / USDC) and reconcile the on-chain transaction back to an Order in your dashboard. Wallet payments are exposed through the same Embedded Checkout surface as card payments — you opt into them by including "crypto" in the session's payment_methods. The iframe handles wallet connection, message signing, and broadcasting the transaction; your code listens for the confirmed-on-chain event.
Prerequisites
- Easy Labs API key with crypto enabled on your account — see Quickstart.
@easylabs/node(server) and@easylabs/reactor@easylabs/browser(frontend) installed.- Your origin added to
allowed_originsviaclient.updateEmbeddedCheckoutConfig.
Implementation
1. Create a session that allows crypto
import { createClient } from "@easylabs/node";
const easy = await createClient({ apiKey: process.env.EASY_API_KEY! });
const { data: session } = await easy.createEmbeddedCheckoutSession({
mode: "payment",
line_items: [{ price_id: "price_01HXXXXXXXXXXX", quantity: 1 }],
success_url: "https://your-app.com/checkout/success",
cancel_url: "https://your-app.com/checkout/cancel",
payment_methods: ["card", "crypto"], // both, or ["crypto"] only
});Pass ["crypto"] alone to force the wallet flow; pass both to let the buyer pick. The session's response carries a crypto_payment block with the destination address, USDC amount, and Solana Pay URL the iframe will use.
2. Mount the iframe and listen for confirmation
In React, EmbeddedCheckoutProvider's onSuccess callback fires for both card and crypto completions. The crypto-specific event includes the on-chain transaction signature:
"use client";
import { EmbeddedCheckout, EmbeddedCheckoutProvider } from "@easylabs/react";
export function CryptoCheckout({ clientSecret }: { clientSecret: string }) {
return (
<EmbeddedCheckoutProvider
config={{
clientSecret,
onSuccess: ({ sessionId, status, tx_signature }) => {
// For crypto payments, tx_signature is the Solana transaction signature.
console.log("paid", { sessionId, status, tx_signature });
},
}}
>
<EmbeddedCheckout clientSecret={clientSecret} />
</EmbeddedCheckoutProvider>
);
}In vanilla JS:
import { mountEmbeddedCheckout } from "@easylabs/browser";
const handle = mountEmbeddedCheckout("#checkout", {
clientSecret,
onCryptoConfirmed: (data) => {
console.log("crypto confirmed", data);
},
});3. (Optional) Poll the chain status server-side
If the buyer leaves the page before the iframe reports confirmation, you can poll the session's crypto status from your backend:
const { data: cryptoStatus } = await easy.getCryptoPaymentStatus(session.id);
// cryptoStatus.status: "pending" | "confirmed" | "expired" | "failed"
// cryptoStatus.tx_signature: the on-chain signature once confirmedThe canonical signal is still the checkout.session.crypto_confirmed webhook — register it and use it to fulfill orders without depending on the browser staying open.
Tradeoffs
- Crypto payments are USDC-only on Solana today. Other chains and tokens are on the roadmap.
- The iframe handles wallet connection — you do not need to bundle a wallet adapter or
@solana/web3.jsin your app. - Refunds for crypto transactions are not automatic; they require an off-chain reconciliation. Open a support ticket if you need to refund a confirmed crypto Order.
tx_signatureis the buyer's proof of payment on-chain. Store it alongside your Order for audit / customer service.