Stablecoins

Stripe Onramp Tutorial

How to integrate Stripe Onramp into your application

Use Stripe's Crypto Onramp API to let users buy USDC on Polygon directly inside your app. You'll create an Onramp session on the backend and render Stripe's hosted widget on the frontend.

You will achieve:

  • Create a Stripe Crypto Onramp session via API
  • Pass client_secret securely to the frontend
  • Render a live fiat-to-Polygon USDC purchase widget

Security notice

All examples below are demonstrations only.
In production, never expose STRIPE_SECRET_KEY or private wallet keys in client code — store them in a secure vault or secrets manager.

Overview

Stripe's Crypto Onramp lets users convert fiat (e.g., USD) to supported crypto assets such as USDC on Polygon.

Requirements

  • A US-based business entity (excluding Hawaii)
  • Stripe account
  • Onramp feature enabled (automatically appears after compliance verification)
  • API key (sk_test_… or sk_live_…)

Reference docs

Schema

nametyperequiredexampledescription
destination_currencystring"usdc"The target crypto asset
destination_networkstring"polygon"Blockchain network
wallet_address[0][type]string"self_custody"Type of wallet
wallet_address[0][address]string"0xYourPolygonAddress"Destination wallet address
STRIPE_SECRET_KEYstring"sk_test_..."Your Stripe API key (server-side only)

Create Session cURL

Run this on your backend server only (never in a browser):

curl https://api.stripe.com/v1/crypto/onramp_sessions \
  -u sk_test_your_secret_key: \
  -d destination_currency=usdc \
  -d destination_network=polygon \
  -d wallet_addresses[0][type]=self_custody \
  -d wallet_addresses[0][address]=0xYOUR_POLYGON_ADDRESS

If successful, Stripe returns a JSON response containing a client_secret:

{
  "id": "cos_0MYvmj589O8KAxCGp14dTjiw",
  "object": "crypto.onramp_session",
  "client_secret": "cos_0MYvmj589O8KAxCGp14dTjiw_secret_BsxEqQLiYKANcTAoVnJ2ikH5q002b9xzouk",
  "created": 1675794053,
  "livemode": false
}

Bun / NodeJS backend

import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

export async function createOnrampSession(req, res) {
  const session = await stripe.crypto.onramps.sessions.create({
    destination_currency: "usdc",
    destination_network: "polygon",
    wallet_addresses: [
      { type: "self_custody", address: "0xYOUR_POLYGON_ADDRESS" }
    ],
    // optional: customer, email, reference
  });
  res.json({ client_secret: session.client_secret });
}

Serve to Frontend

Pass only the client_secret to your client app — never your Stripe secret key.

<script src="https://js.stripe.com/v3/crypto/onramp.js"></script>
<div id="onramp"></div>

<script>
(async () => {
  // Fetch client_secret from your backend
  const { client_secret } = await fetch("/api/create-onramp-session").then(r => r.json());

  const onramp = await window.StripeOnramp.init({
    clientSecret: client_secret,
    appearance: {
      theme: "light", // optional UI customization
    },
  });

  onramp.mount("#onramp");
})();
</script>

Users can now buy USDC on Polygon directly from your site.

The funds are sent to the wallet specified in your onramp_session.

Extra

Debugging, sanity checks, and guardrails.

Do / Don't Do Guardrails

✅ Do❌ Don't
Store API keys in a vault, not in codeHardcode STRIPE_SECRET_KEY in frontend
Use Stripe Sandbox firstTest with live key before compliance is cleared
Log only non-sensitive session infoLog full client_secret values

Errors

codemeaningfix
403_access_deniedOnramp not enabledWait for compliance review
400_invalid_wallet_addressBad or unsupported walletVerify address format
401_unauthorizedWrong API keyUse correct/test or live key

Quick Checklist

  • Backend has STRIPE_SECRET_KEY set
  • Frontend only receives client_secret
  • Wallet address is valid on Polygon
  • Tested in Sandbox before production