2026-09-21
How to Accept Crypto Payments Without a Browser Extension

The biggest drop-off point in most crypto checkouts isn't confirmation time or gas fees. It's the moment a customer realizes they need a browser extension they don't have. EIP-6963 wallet discovery and WalletConnect QR codes solve real problems, but they both assume the customer already has a wallet with a working connector reachable from that browser tab. A large share of customers don't: most of the mobile web, and most people new to crypto.
That's a UX problem, not a custody problem, and fixing it doesn't mean compromising on either. Klap One is built around a different entry point: verify who the customer is, let them approve a payment from a wallet they already have, and never ask them to install anything.
What "no extension" does and doesn't mean
Removing the extension requirement does not mean Klap holds a key on the customer's behalf. The customer's wallet still signs the transaction, and Klap never has custody of the funds or a private key. The change is where the approval happens. It no longer happens in an injected provider inside the same browser tab. It happens inside the customer's own wallet app, and the result is reported back independently instead of being taken on trust from the page that asked for it.
Three parties are involved, each with a narrow job:
- The merchant's page embeds a button. It never touches identity, wallet state, or transaction data, only a charge ID.
one-id, a hosted identity and wallet surface on Klap's own origin, runs inside a popup or iframe. It owns OTP verification, the customer's list of linked wallets, and the approval flow.- Klap Core independently verifies the resulting transaction on-chain before crediting the charge. A signed transaction hash is evidence to check, not a payment to trust.
The flow a customer sees
- The customer clicks pay. A popup or iframe opens to
one-id, scoped to that specific charge. - They verify with a one-time code sent to their email, with no password, no extension and no prior account.
- They pick from wallets they've previously linked, or link one on the spot via WalletConnect or MetaMask's own connect flow.
- They approve the transaction inside their own wallet app. The approval never leaves that app.
one-idnever sees a private key, and the merchant's page never sees the wallet at all. one-idreports the result back to the merchant's page overpostMessage. Klap Core separately confirms the transaction on-chain before the charge is marked paid.
A wallet approval means the same thing as before. What differs is what a customer needs installed to produce one.
Embedding it
On the merchant side it is a small script, not an SDK integration:
<klappay-button charge-id="ch_123" variant="black" size="md"></klappay-button>
Or programmatically, when you need to react to the outcome yourself:
import { createKlappayOne } from '@klappay/one'const checkout = createKlappayOne({chargeId: 'ch_123',onSuccess: (result) => {showConfirmationUI(result)},onError: (error) => showRetry(error),onCancel: () => resetButton(),})checkout.open()
onSuccess is a UX signal, not a payment confirmation. Treat it like a wallet signature in any other checkout flow: good enough to update the interface, not good enough to fulfill an order. Fulfillment still depends on Klap Core's webhook reaching your backend, for the same reason a client-reported transaction hash was never enough in a browser-extension flow.
A lifecycle you can build an interface around
The bridge between the popup/iframe and the merchant's page emits more than a single success or failure callback. A payment attempt moves through pending (the wallet has been asked to sign and hasn't responded yet) and confirming (a transaction hash exists and can be checked independently, even though Core hasn't confirmed it yet). It then ends in success, error, or cancel.
One more non-terminal state is worth designing for: reconnecting. A customer who leaves the tab to approve in a separate wallet app can come back to a dropped realtime connection. That is common enough on mobile to need its own state instead of failing silently. Showing "reconnecting, hold on" instead of a generic error is a small detail that saves a support ticket from a customer who only needs to wait a few seconds.
Where this fits next to a standard checkout
This doesn't replace wallet-extension checkout. It's a second front door. A customer who already has MetaMask installed and prefers connecting it directly loses nothing by using checkout-kit's standard flow. The OTP-plus-linked-wallet path exists for the much larger group of customers who don't have that set up, and who would otherwise leave checkout rather than install a browser extension mid-purchase. Custody doesn't move on either path.