# Embedded Components onramp quickstart

Build an app-native crypto onramp flow with the Embedded Components SDK.

## Set up the Embedded Components onramp 

This quickstart runs minimal code for the full flow. Use this when you want to get started quickly, or to explore the integration before you start building. The production SDK is [@stripe/stripe-react-native](https://github.com/stripe/stripe-react-native).

See our sample apps: [React Native (Expo)](https://github.com/stripe-samples/crypto-embedded-components-onramp) | [iOS](https://github.com/stripe/stripe-ios/tree/master/Example/CryptoOnramp%20Example) | [Android](https://github.com/stripe/stripe-android/tree/master/crypto-onramp-example). Follow the [integration guide](https://docs.stripe.com/crypto/onramp/embedded-components-integration-guide.md) for step-by-step instructions to build your integration.

### Install the Stripe Node library

Install the package and import it in your code. Alternatively, if you’re starting from scratch and need a package.json file, download the project files using the Download link in the code editor.

#### npm

Install the library:

```bash
npm install --save stripe
```

#### GitHub

Or download the stripe-node library source code directly [from GitHub](https://github.com/stripe/stripe-node).

### Install the Stripe Ruby library

Install the Stripe ruby gem and require it in your code. Alternatively, if you’re starting from scratch and need a Gemfile, download the project files using the link in the code editor.

#### Terminal

Install the gem:

```bash
gem install stripe
```

#### Bundler

Add this line to your Gemfile:

```bash
gem 'stripe'
```

#### GitHub

Or download the stripe-ruby gem source code directly [from GitHub](https://github.com/stripe/stripe-ruby).

### Install the Stripe Java library

Add the dependency to your build and import the library. Alternatively, if you’re starting from scratch and need a sample pom.xml file (for Maven), download the project files using the link in the code editor.

#### Maven

Add the following dependency to your POM and replace {VERSION} with the version number you want to use.

```bash
<dependency>\n<groupId>com.stripe</groupId>\n<artifactId>stripe-java</artifactId>\n<version>{VERSION}</version>\n</dependency>
```

#### Gradle

Add the dependency to your build.gradle file and replace {VERSION} with the version number you want to use.

```bash
implementation "com.stripe:stripe-java:{VERSION}"
```

#### GitHub

Download the JAR directly [from GitHub](https://github.com/stripe/stripe-java/releases/latest).

### Install the Stripe Python package

Install the Stripe package and import it in your code. Alternatively, if you’re starting from scratch and need a requirements.txt file, download the project files using the link in the code editor.

#### pip

Install the package through pip:

```bash
pip3 install stripe
```

#### GitHub

Download the stripe-python library source code directly [from GitHub](https://github.com/stripe/stripe-python).

### Install the Stripe PHP library

Install the library with composer and initialize with your secret API key. Alternatively, if you’re starting from scratch and need a composer.json file, download the files using the link in the code editor.

#### Composer

Install the library:

```bash
composer require stripe/stripe-php
```

#### GitHub

Or download the stripe-php library source code directly [from GitHub](https://github.com/stripe/stripe-php).

### Set up your server

Add the dependency to your build and import the library. Alternatively, if you’re starting from scratch and need a go.mod file, download the project files using the link in the code editor.

#### Go

Make sure to initialize with Go Modules:

```bash
go get -u github.com/stripe/stripe-go/v85
```

#### GitHub

Or download the stripe-go module source code directly [from GitHub](https://github.com/stripe/stripe-go).

### Install the Stripe.net library

Install the package with .NET or NuGet. Alternatively, if you’re starting from scratch, download the files which contains a configured .csproj file.

#### dotnet

Install the library:

```bash
dotnet add package Stripe.net
```

#### NuGet

Install the library:

```bash
Install-Package Stripe.net
```

#### GitHub

Or download the Stripe.net library source code directly [from GitHub](https://github.com/stripe/stripe-dotnet).

### Install the Stripe libraries

Install the packages and import them in your code. Alternatively, if you’re starting from scratch and need a `package.json` file, download the project files using the link in the code editor.

Install the libraries:

```bash
npm install --save stripe @stripe/stripe-js next
```

### Create a LinkAuthIntent

Add an endpoint that creates a [LinkAuthIntent](https://docs.stripe.com/crypto/onramp/embedded-components-integration-guide.md#create-a-linkauthintent) with the [onramp OAuth scopes](https://docs.stripe.com/crypto/onramp/embedded-components-integration-guide.md#step-3-authorize) described in the integration guide. The client uses the returned `authIntentId` when calling `authorize()` from the SDK.

### Exchange for access tokens

After the user consents using `authorize()` on the client, add an endpoint that calls the [Retrieve Access Tokens](https://docs.stripe.com/crypto/onramp/embedded-components-integration-guide.md#retrieve-access-tokens) API to exchange the `authIntentId` for OAuth access tokens. Store the access token and use it in the `Stripe-OAuth-Token` header for all subsequent crypto API calls.

### Retrieve a CryptoCustomer

Add an endpoint that calls the [Retrieve a CryptoCustomer](https://docs.stripe.com/api/crypto/customers/retrieve.md) API with the `customerId` from the authorize flow. Use the response to check the customer’s KYC and verification status so the client can prompt them complete setup (KYC, identity verification, wallet registration, payment method) before creating an onramp session.

### List ConsumerWallets

Add an endpoint that calls the [List ConsumerWallets](https://docs.stripe.com/api/crypto/consumer_wallets/list.md) API. Use it to see whether the user already has wallet addresses on file. If the list is empty, the client calls `registerWalletAddress()` before creating an onramp session.

### List PaymentTokens

Add an endpoint that calls the [List PaymentTokens](https://docs.stripe.com/api/crypto/payment_tokens/list.md) API. Use it to see which payment methods the user already has. If the list is empty, the client calls `collectPaymentMethod()` and `createCryptoPaymentToken()` before creating an onramp session.

### Create a CryptoOnrampSession

Add an endpoint that creates a [CryptoOnrampSession](https://docs.stripe.com/api/crypto/onramp_sessions/create.md) with `ui_mode: headless`. Pass the `crypto_customer_id` from the authorize flow and `payment_token` from `createCryptoPaymentToken()`. Include the amount, currencies, network, and an optional wallet address.

### Refresh an executable quote

Add an endpoint that calls the [Refresh a Quote](https://docs.stripe.com/api/crypto/onramp_sessions/quote.md) API to get an executable quote for the onramp session. You must call this before checkout to lock in the exchange rate. Quotes expire after a short window, so refresh the quote immediately before calling checkout. If checkout returns `charged_with_expired_quote`, refresh the quote and retry.

### Perform checkout

Add an endpoint that calls the [Checkout](https://docs.stripe.com/api/crypto/onramp_sessions/checkout.md) API to confirm and fulfill the onramp session. Your back end returns the `client_secret` so the client SDK can validate and complete any required steps (for example, 3DS) using `performCheckout()`.

### Basic setup

Wrap your application with `StripeProvider` at a high level so Stripe functionality is available throughout your component tree. The key properties are:

- `publishableKey` – Your Stripe publishable key
- `merchantIdentifier` – Your Apple Merchant ID (required for Apple Pay)
- `urlScheme` – Required for return URLs in authentication flows

### Initial configuration

Before you can call any Onramp APIs, configure the SDK using the `configure` method from the `useOnramp()` hook. `configure` takes an `Onramp.Configuration` instance to customize your business display name and appearance (colors, theme) for Stripe-provided interfaces such as wallets, one-time passcodes, and identity verification UIs. Call `configure()` when your component mounts.

### Authentication flow

Use `hasLinkAccount(email)` to check for an existing Link account. Then create a `LinkAuthIntent` (your back end calls [Create a LinkAuthIntent](https://docs.stripe.com/crypto/onramp/embedded-components-integration-guide.md#create-a-linkauthintent)) and call `authorize(authIntentId)` to exchange for tokens. Store the returned `customerId` and the `accessToken` for the rest of the flow.

### Identity flow

Check verification status (your back end calls [Retrieve CryptoCustomer](https://docs.stripe.com/api/crypto/customers/retrieve.md)). If KYC is missing, call `attachKycInfo()`. If KYC needs confirmation, call `presentKycInfoVerification()`. If identity verification is required, call `verifyIdentity()`. Advance to the next step after each completes.

### Payment flow

Refresh the quote to lock in the exchange rate, then perform checkout using the onramp session ID: call `performCheckout(sessionId, fetchClientSecret)` and pass a callback that fetches the `client_secret` from your back end for validation. If the quote expires before checkout completes, refresh it and retry.

// This is a public sample test API key.
// Don't submit any personally identifiable information in requests made with this key.
// Sign in to see your own test API key embedded in code samples.
// Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
const secretKey = '<<YOUR_SECRET_KEY>>';
app.post('/create-link-auth-intent', async (req, res) => {
  const { email, oauthScopes } = req.body;
  const linkRes = await fetch('https://login.link.com/v1/link_auth_intent', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${secretKey}`,
    },
    body: JSON.stringify({
      email,
      oauth_scopes: oauthScopes,
      oauth_client_id: process.env.LINK_OAUTH_CLIENT_ID || 'your_oauth_client_id',
    }),
  });
  const data = await linkRes.json();
  if (data.id) {
    res.json({ authIntentId: data.id });
  } else {
    res.status(400).json(data);
  }
});
app.post('/exchange-tokens', async (req, res) => {
  const { authIntentId } = req.body;
  const linkRes = await fetch(`https://login.link.com/v1/link_auth_intent/${authIntentId}/tokens`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${secretKey}`,
    },
  });
  const data = await linkRes.json();
  if (data.access_token) {
    res.json({ accessToken: data.access_token });
  } else {
    res.status(400).json(data);
  }
});
app.get('/crypto/customer/:id', async (req, res) => {
  const oauthToken = req.headers['stripe-oauth-token'];
  const stripeRes = await fetch(`https://api.stripe.com/v1/crypto/customers/${req.params.id}`, {
    headers: {
      'Authorization': `Bearer ${secretKey}`,
      'Stripe-OAuth-Token': oauthToken,
    },
  });
  const data = await stripeRes.json();
  res.status(stripeRes.status).json(data);
});
app.get('/crypto/customer/:id/wallets', async (req, res) => {
  const oauthToken = req.headers['stripe-oauth-token'];
  const stripeRes = await fetch(
    `https://api.stripe.com/v1/crypto/customers/${req.params.id}/crypto_consumer_wallets`,
    {
      headers: {
        'Authorization': `Bearer ${secretKey}`,
        'Stripe-OAuth-Token': oauthToken,
      },
    }
  );
  const data = await stripeRes.json();
  res.status(stripeRes.status).json(data);
});
app.get('/crypto/customer/:id/payment-tokens', async (req, res) => {
  const oauthToken = req.headers['stripe-oauth-token'];
  const stripeRes = await fetch(
    `https://api.stripe.com/v1/crypto/customers/${req.params.id}/payment_tokens`,
    {
      headers: {
        'Authorization': `Bearer ${secretKey}`,
        'Stripe-OAuth-Token': oauthToken,
      },
    }
  );
  const data = await stripeRes.json();
  res.status(stripeRes.status).json(data);
});
app.post('/create-onramp-session', async (req, res) => {
  const {
    crypto_customer_id,
    payment_token,
    source_amount,
    source_currency,
    destination_currency,
    destination_network,
    wallet_address,
    customer_ip_address,
  } = req.body;

  const oauthToken = req.headers['stripe-oauth-token'];
  const params = new URLSearchParams({
    ui_mode: 'headless',
    crypto_customer_id,
    payment_token,
    source_amount: String(source_amount),
    source_currency,
    destination_currency,
    destination_network,
    wallet_address,
    customer_ip_address,
  });
  params.append('destination_networks[]', destination_network);

  const stripeRes = await fetch('https://api.stripe.com/v1/crypto/onramp_sessions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Authorization': `Bearer ${secretKey}`,
      'Stripe-OAuth-Token': oauthToken,
    },
    body: params,
  });
  const data = await stripeRes.json();
  if (data.id) {
    res.json({ id: data.id });
  } else {
    res.status(stripeRes.status).json(data);
  }
});
app.post('/quote/:sessionId', async (req, res) => {
  const oauthToken = req.headers['stripe-oauth-token'];
  const stripeRes = await fetch(
    `https://api.stripe.com/v1/crypto/onramp_sessions/${req.params.sessionId}/quote`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${secretKey}`,
        'Stripe-OAuth-Token': oauthToken,
      },
    }
  );
  const data = await stripeRes.json();
  res.status(stripeRes.status).json(data);
});
app.post('/checkout/:sessionId', async (req, res) => {
  const oauthToken = req.headers['stripe-oauth-token'];
  const stripeRes = await fetch(
    `https://api.stripe.com/v1/crypto/onramp_sessions/${req.params.sessionId}/checkout`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${secretKey}`,
        'Stripe-OAuth-Token': oauthToken,
      },
    }
  );
  const data = await stripeRes.json();
  res.status(stripeRes.status).json(data);
});
\# This is a public sample test API key.
# Don't submit any personally identifiable information in requests made with this key.
# Sign in to see your own test API key embedded in code samples.
\# Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
SECRET_KEY = '<<YOUR_SECRET_KEY>>'
post '/create-link-auth-intent' do
  body = JSON.parse(request.body.read)
  uri = URI('https://login.link.com/v1/link_auth_intent')
  req = Net::HTTP::Post.new(uri)
  req['Authorization'] = "Bearer #{SECRET_KEY}"
  req['Content-Type'] = 'application/x-www-form-urlencoded'
  req.body = URI.encode_www_form(
    email: body['email'],
    oauth_scopes: body['oauthScopes'],
    oauth_client_id: ENV['LINK_OAUTH_CLIENT_ID'] || 'your_oauth_client_id'
  )
  res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
  data = JSON.parse(res.body)
  data['id'] ? { authIntentId: data['id'] }.to_json : [res.code.to_i, data.to_json]
end
post '/exchange-tokens' do
  body = JSON.parse(request.body.read)
  auth_intent_id = body['authIntentId']
  uri = URI("https://login.link.com/v1/link_auth_intent/#{auth_intent_id}/tokens")
  req = Net::HTTP::Post.new(uri)
  req['Authorization'] = "Bearer #{SECRET_KEY}"
  res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
  data = JSON.parse(res.body)
  data['access_token'] ? { accessToken: data['access_token'] }.to_json : [res.code.to_i, data.to_json]
end
get '/crypto/customer/:id' do
  oauth_token = request.env['HTTP_STRIPE_OAUTH_TOKEN']
  uri = URI("https://api.stripe.com/v1/crypto/customers/#{params['id']}")
  req = Net::HTTP::Get.new(uri)
  req['Authorization'] = "Bearer #{SECRET_KEY}"
  req['Stripe-OAuth-Token'] = oauth_token
  res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
  [res.code.to_i, res.body]
end
get '/crypto/customer/:id/wallets' do
  oauth_token = request.env['HTTP_STRIPE_OAUTH_TOKEN']
  uri = URI("https://api.stripe.com/v1/crypto/customers/#{params['id']}/crypto_consumer_wallets")
  req = Net::HTTP::Get.new(uri)
  req['Authorization'] = "Bearer #{SECRET_KEY}"
  req['Stripe-OAuth-Token'] = oauth_token
  res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
  [res.code.to_i, res.body]
end
get '/crypto/customer/:id/payment-tokens' do
  oauth_token = request.env['HTTP_STRIPE_OAUTH_TOKEN']
  uri = URI("https://api.stripe.com/v1/crypto/customers/#{params['id']}/payment_tokens")
  req = Net::HTTP::Get.new(uri)
  req['Authorization'] = "Bearer #{SECRET_KEY}"
  req['Stripe-OAuth-Token'] = oauth_token
  res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
  [res.code.to_i, res.body]
end
post '/create-onramp-session' do
  body = JSON.parse(request.body.read)
  oauth_token = request.env['HTTP_STRIPE_OAUTH_TOKEN']
  params_hash = {
    ui_mode: 'headless',
    crypto_customer_id: body['crypto_customer_id'],
    payment_token: body['payment_token'],
    source_amount: body['source_amount'],
    source_currency: body['source_currency'],
    destination_currency: body['destination_currency'],
    destination_network: body['destination_network']
  }
  params_hash[:customer_ip_address] = body['customer_ip_address'] if body['customer_ip_address']
  params_hash[:wallet_addresses] = body['wallet_addresses'].to_json if body['wallet_addresses']

  uri = URI('https://api.stripe.com/v1/crypto/onramp_sessions')
  req = Net::HTTP::Post.new(uri)
  req['Authorization'] = "Bearer #{SECRET_KEY}"
  req['Stripe-OAuth-Token'] = oauth_token
  req['Content-Type'] = 'application/x-www-form-urlencoded'
  req.body = URI.encode_www_form(params_hash)
  res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
  data = JSON.parse(res.body)
  data['id'] ? { id: data['id'] }.to_json : [res.code.to_i, data.to_json]
end
post '/quote/:sessionId' do
  oauth_token = request.env['HTTP_STRIPE_OAUTH_TOKEN']
  uri = URI("https://api.stripe.com/v1/crypto/onramp_sessions/#{params['sessionId']}/quote")
  req = Net::HTTP::Post.new(uri)
  req['Authorization'] = "Bearer #{SECRET_KEY}"
  req['Stripe-OAuth-Token'] = oauth_token
  res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
  [res.code.to_i, res.body]
end
post '/checkout/:sessionId' do
  oauth_token = request.env['HTTP_STRIPE_OAUTH_TOKEN']
  uri = URI("https://api.stripe.com/v1/crypto/onramp_sessions/#{params['sessionId']}/checkout")
  req = Net::HTTP::Post.new(uri)
  req['Authorization'] = "Bearer #{SECRET_KEY}"
  req['Stripe-OAuth-Token'] = oauth_token
  res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
  [res.code.to_i, res.body]
end
\# This is a public sample test API key.
# Don't submit any personally identifiable information in requests made with this key.
# Sign in to see your own test API key embedded in code samples.
\# Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
SECRET_KEY = '<<YOUR_SECRET_KEY>>'
@app.route('/create-link-auth-intent', methods=['POST'])
def create_link_auth_intent():
    body = request.get_json()
    data = urllib.parse.urlencode({
        'email': body['email'],
        'oauth_scopes': body['oauthScopes'],
        'oauth_client_id': os.environ.get('LINK_OAUTH_CLIENT_ID', 'your_oauth_client_id'),
    }).encode()
    req = urllib.request.Request(
        'https://login.link.com/v1/link_auth_intent',
        data=data,
        headers={
            'Authorization': f'Bearer {SECRET_KEY}',
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        method='POST'
    )
    with urllib.request.urlopen(req) as res:
        link_data = json.loads(res.read().decode())
    if 'id' in link_data:
        return jsonify(authIntentId=link_data['id'])
    return jsonify(link_data), 400
@app.route('/exchange-tokens', methods=['POST'])
def exchange_tokens():
    body = request.get_json()
    auth_intent_id = body['authIntentId']
    req = urllib.request.Request(
        f'https://login.link.com/v1/link_auth_intent/{auth_intent_id}/tokens',
        headers={'Authorization': f'Bearer {SECRET_KEY}'},
        method='POST'
    )
    with urllib.request.urlopen(req) as res:
        link_data = json.loads(res.read().decode())
    if 'access_token' in link_data:
        return jsonify(accessToken=link_data['access_token'])
    return jsonify(link_data), 400
@app.route('/crypto/customer/<customer_id>')
def get_crypto_customer(customer_id):
    oauth_token = request.headers.get('Stripe-OAuth-Token')
    data = stripe_request('GET', f'/v1/crypto/customers/{customer_id}', oauth_token=oauth_token)
    return jsonify(data)
@app.route('/crypto/customer/<customer_id>/wallets')
def get_wallets(customer_id):
    oauth_token = request.headers.get('Stripe-OAuth-Token')
    data = stripe_request('GET', f'/v1/crypto/customers/{customer_id}/crypto_consumer_wallets', oauth_token=oauth_token)
    return jsonify(data)
@app.route('/crypto/customer/<customer_id>/payment-tokens')
def get_payment_tokens(customer_id):
    oauth_token = request.headers.get('Stripe-OAuth-Token')
    data = stripe_request('GET', f'/v1/crypto/customers/{customer_id}/payment_tokens', oauth_token=oauth_token)
    return jsonify(data)
@app.route('/create-onramp-session', methods=['POST'])
def create_onramp_session():
    body = request.get_json()
    oauth_token = request.headers.get('Stripe-OAuth-Token')
    params = {
        'ui_mode': 'headless',
        'crypto_customer_id': body['crypto_customer_id'],
        'payment_token': body['payment_token'],
        'source_amount': str(body['source_amount']),
        'source_currency': body['source_currency'],
        'destination_currency': body['destination_currency'],
        'destination_network': body['destination_network'],
    }
    if body.get('customer_ip_address'):
        params['customer_ip_address'] = body['customer_ip_address']
    if body.get('wallet_addresses'):
        params['wallet_addresses'] = json.dumps(body['wallet_addresses'])

    data = urllib.parse.urlencode(params).encode()
    req = urllib.request.Request(
        'https://api.stripe.com/v1/crypto/onramp_sessions',
        data=data,
        headers={
            'Authorization': f'Bearer {SECRET_KEY}',
            'Stripe-OAuth-Token': oauth_token,
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        method='POST'
    )
    with urllib.request.urlopen(req) as res:
        session_data = json.loads(res.read().decode())
    if 'id' in session_data:
        return jsonify(id=session_data['id'])
    return jsonify(session_data), 400
@app.route('/quote/<session_id>', methods=['POST'])
def refresh_quote(session_id):
    oauth_token = request.headers.get('Stripe-OAuth-Token')
    req = urllib.request.Request(
        f'https://api.stripe.com/v1/crypto/onramp_sessions/{session_id}/quote',
        headers={
            'Authorization': f'Bearer {SECRET_KEY}',
            'Stripe-OAuth-Token': oauth_token,
        },
        method='POST'
    )
    with urllib.request.urlopen(req) as res:
        return jsonify(json.loads(res.read().decode()))
@app.route('/checkout/<session_id>', methods=['POST'])
def checkout(session_id):
    oauth_token = request.headers.get('Stripe-OAuth-Token')
    req = urllib.request.Request(
        f'https://api.stripe.com/v1/crypto/onramp_sessions/{session_id}/checkout',
        headers={
            'Authorization': f'Bearer {SECRET_KEY}',
            'Stripe-OAuth-Token': oauth_token,
        },
        method='POST'
    )
    with urllib.request.urlopen(req) as res:
        return jsonify(json.loads(res.read().decode()))
$secretKey = $stripeSecretKey;
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $path === '/create-link-auth-intent') {
    $ch = curl_init('https://login.link.com/v1/link_auth_intent');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => http_build_query([
            'email' => $input['email'] ?? '',
            'oauth_scopes' => $input['oauthScopes'] ?? '',
            'oauth_client_id' => getenv('LINK_OAUTH_CLIENT_ID') ?: 'your_oauth_client_id',
        ]),
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $secretKey,
            'Content-Type: application/x-www-form-urlencoded',
        ],
        CURLOPT_RETURNTRANSFER => true,
    ]);
    $res = curl_exec($ch);
    $data = json_decode($res, true);
    echo $data['id'] ? json_encode(['authIntentId' => $data['id']]) : $res;
    exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $path === '/exchange-tokens') {
    $authIntentId = $input['authIntentId'] ?? '';
    $ch = curl_init("https://login.link.com/v1/link_auth_intent/{$authIntentId}/tokens");
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $secretKey],
        CURLOPT_RETURNTRANSFER => true,
    ]);
    $res = curl_exec($ch);
    $data = json_decode($res, true);
    echo $data['access_token'] ? json_encode(['accessToken' => $data['access_token']]) : $res;
    exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'GET' && preg_match('#^/crypto/customer/([^/]+)$#', $path, $m)) {
    $customerId = $m[1];
    $oauthToken = $_SERVER['HTTP_STRIPE_OAUTH_TOKEN'] ?? '';
    $ch = curl_init("https://api.stripe.com/v1/crypto/customers/{$customerId}");
    curl_setopt_array($ch, [
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $secretKey,
            'Stripe-OAuth-Token: ' . $oauthToken,
        ],
        CURLOPT_RETURNTRANSFER => true,
    ]);
    echo curl_exec($ch);
    exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'GET' && preg_match('#^/crypto/customer/([^/]+)/wallets$#', $path, $m)) {
    $customerId = $m[1];
    $oauthToken = $_SERVER['HTTP_STRIPE_OAUTH_TOKEN'] ?? '';
    $ch = curl_init("https://api.stripe.com/v1/crypto/customers/{$customerId}/crypto_consumer_wallets");
    curl_setopt_array($ch, [
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $secretKey,
            'Stripe-OAuth-Token: ' . $oauthToken,
        ],
        CURLOPT_RETURNTRANSFER => true,
    ]);
    echo curl_exec($ch);
    exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'GET' && preg_match('#^/crypto/customer/([^/]+)/payment-tokens$#', $path, $m)) {
    $customerId = $m[1];
    $oauthToken = $_SERVER['HTTP_STRIPE_OAUTH_TOKEN'] ?? '';
    $ch = curl_init("https://api.stripe.com/v1/crypto/customers/{$customerId}/payment_tokens");
    curl_setopt_array($ch, [
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $secretKey,
            'Stripe-OAuth-Token: ' . $oauthToken,
        ],
        CURLOPT_RETURNTRANSFER => true,
    ]);
    echo curl_exec($ch);
    exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $path === '/create-onramp-session') {
    $oauthToken = $_SERVER['HTTP_STRIPE_OAUTH_TOKEN'] ?? '';
    $params = [
        'ui_mode' => 'headless',
        'crypto_customer_id' => $input['crypto_customer_id'],
        'payment_token' => $input['payment_token'],
        'source_amount' => $input['source_amount'],
        'source_currency' => $input['source_currency'],
        'destination_currency' => $input['destination_currency'],
        'destination_network' => $input['destination_network'],
    ];
    if (!empty($input['customer_ip_address'])) {
        $params['customer_ip_address'] = $input['customer_ip_address'];
    }
    if (!empty($input['wallet_addresses'])) {
        $params['wallet_addresses'] = json_encode($input['wallet_addresses']);
    }

    $ch = curl_init('https://api.stripe.com/v1/crypto/onramp_sessions');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => http_build_query($params),
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $secretKey,
            'Stripe-OAuth-Token: ' . $oauthToken,
            'Content-Type: application/x-www-form-urlencoded',
        ],
        CURLOPT_RETURNTRANSFER => true,
    ]);
    $res = curl_exec($ch);
    $data = json_decode($res, true);
    echo $data['id'] ? json_encode(['id' => $data['id']]) : $res;
    exit;
}
if (preg_match('#^/quote/([^/]+)$#', $path, $m) && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $sessionId = $m[1];
    $oauthToken = $_SERVER['HTTP_STRIPE_OAUTH_TOKEN'] ?? '';
    $ch = curl_init("https://api.stripe.com/v1/crypto/onramp_sessions/{$sessionId}/quote");
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $secretKey,
            'Stripe-OAuth-Token: ' . $oauthToken,
        ],
        CURLOPT_RETURNTRANSFER => true,
    ]);
    echo curl_exec($ch);
    exit;
}
if (preg_match('#^/checkout/([^/]+)$#', $path, $m) && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $sessionId = $m[1];
    $oauthToken = $_SERVER['HTTP_STRIPE_OAUTH_TOKEN'] ?? '';
    $ch = curl_init("https://api.stripe.com/v1/crypto/onramp_sessions/{$sessionId}/checkout");
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $secretKey,
            'Stripe-OAuth-Token: ' . $oauthToken,
        ],
        CURLOPT_RETURNTRANSFER => true,
    ]);
    echo curl_exec($ch);
    exit;
}
// Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
$stripeSecretKey = '<<YOUR_SECRET_KEY>>';
// This is a public sample test API key.
// Don't submit any personally identifiable information in requests made with this key.
// Sign in to see your own test API key embedded in code samples.
// Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
var secretKey = "<<YOUR_SECRET_KEY>>"
func handleCreateAuthIntent(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Method not allowed", 405)
		return
	}
	var body struct {
		Email       string `json:"email"`
		OauthScopes string `json:"oauthScopes"`
	}
	json.NewDecoder(r.Body).Decode(&body)
	form := url.Values{}
	form.Set("email", body.Email)
	form.Set("oauth_scopes", body.OauthScopes)
	form.Set("oauth_client_id", "your_oauth_client_id")
	req, _ := http.NewRequest("POST", "https://login.link.com/v1/link_auth_intent", strings.NewReader(form.Encode()))
	req.Header.Set("Authorization", "Bearer "+secretKey)
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	res, _ := http.DefaultClient.Do(req)
	io.Copy(w, res.Body)
}
func handleExchangeTokens(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Method not allowed", 405)
		return
	}
	var body struct {
		AuthIntentID string `json:"authIntentId"`
	}
	json.NewDecoder(r.Body).Decode(&body)
	req, _ := http.NewRequest("POST", "https://login.link.com/v1/link_auth_intent/"+body.AuthIntentID+"/tokens", nil)
	req.Header.Set("Authorization", "Bearer "+secretKey)
	res, _ := http.DefaultClient.Do(req)
	io.Copy(w, res.Body)
}
func handleCryptoCustomer(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path
	path = strings.TrimPrefix(path, "/crypto/customer/")
	parts := strings.SplitN(path, "/", 2)
	customerID := parts[0]
	stripePath := "/v1/crypto/customers/" + customerID
	if len(parts) > 1 {
		switch parts[1] {
		case "wallets":
			stripePath += "/crypto_consumer_wallets"
		case "payment-tokens":
			stripePath += "/payment_tokens"
		}
	}
	oauthToken := r.Header.Get("Stripe-OAuth-Token")
	req, _ := http.NewRequest(r.Method, "https://api.stripe.com"+stripePath, r.Body)
	req.Header.Set("Authorization", "Bearer "+secretKey)
	req.Header.Set("Stripe-OAuth-Token", oauthToken)
	res, _ := http.DefaultClient.Do(req)
	io.Copy(w, res.Body)
}
func handleCreateSession(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Method not allowed", 405)
		return
	}
	var body struct {
		CryptoCustomerID   string                 `json:"crypto_customer_id"`
		PaymentToken       string                 `json:"payment_token"`
		SourceAmount       float64                `json:"source_amount"`
		SourceCurrency     string                 `json:"source_currency"`
		DestinationCurrency string                `json:"destination_currency"`
		DestinationNetwork string                 `json:"destination_network"`
		CustomerIPAddress  string                 `json:"customer_ip_address"`
		WalletAddresses    map[string]interface{} `json:"wallet_addresses"`
	}
	json.NewDecoder(r.Body).Decode(&body)
	oauthToken := r.Header.Get("Stripe-OAuth-Token")
	form := url.Values{}
	form.Set("ui_mode", "headless")
	form.Set("crypto_customer_id", body.CryptoCustomerID)
	form.Set("payment_token", body.PaymentToken)
	form.Set("source_amount", fmt.Sprintf("%.0f", body.SourceAmount))
	form.Set("source_currency", body.SourceCurrency)
	form.Set("destination_currency", body.DestinationCurrency)
	form.Set("destination_network", body.DestinationNetwork)
	if body.CustomerIPAddress != "" {
		form.Set("customer_ip_address", body.CustomerIPAddress)
	}
	if len(body.WalletAddresses) > 0 {
		walletJSON, _ := json.Marshal(body.WalletAddresses)
		form.Set("wallet_addresses", string(walletJSON))
	}
	req, _ := http.NewRequest("POST", "https://api.stripe.com/v1/crypto/onramp_sessions", strings.NewReader(form.Encode()))
	req.Header.Set("Authorization", "Bearer "+secretKey)
	req.Header.Set("Stripe-OAuth-Token", oauthToken)
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	res, _ := http.DefaultClient.Do(req)
	io.Copy(w, res.Body)
}
func handleRefreshQuote(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Method not allowed", 405)
		return
	}
	parts := strings.Split(r.URL.Path, "/")
	sessionID := parts[len(parts)-1]
	oauthToken := r.Header.Get("Stripe-OAuth-Token")
	req, _ := http.NewRequest("POST", "https://api.stripe.com/v1/crypto/onramp_sessions/"+sessionID+"/quote", nil)
	req.Header.Set("Authorization", "Bearer "+secretKey)
	req.Header.Set("Stripe-OAuth-Token", oauthToken)
	res, _ := http.DefaultClient.Do(req)
	io.Copy(w, res.Body)
}
func handleCheckout(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Method not allowed", 405)
		return
	}
	parts := strings.Split(r.URL.Path, "/")
	sessionID := parts[len(parts)-1]
	oauthToken := r.Header.Get("Stripe-OAuth-Token")
	req, _ := http.NewRequest("POST", "https://api.stripe.com/v1/crypto/onramp_sessions/"+sessionID+"/checkout", nil)
	req.Header.Set("Authorization", "Bearer "+secretKey)
	req.Header.Set("Stripe-OAuth-Token", oauthToken)
	res, _ := http.DefaultClient.Do(req)
	io.Copy(w, res.Body)
}
// This is a public sample test API key.
// Don't submit any personally identifiable information in requests made with this key.
// Sign in to see your own test API key embedded in code samples.
// Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
var secretKey = "<<YOUR_SECRET_KEY>>";
app.MapPost("/create-link-auth-intent", async (HttpRequest req) => {
    var body = await JsonSerializer.DeserializeAsync<JsonElement>(req.Body);
    var content = new FormUrlEncodedContent(new[] {
        new KeyValuePair<string, string>("email", body.GetProperty("email").GetString()!),
        new KeyValuePair<string, string>("oauth_scopes", body.GetProperty("oauthScopes").GetString()!),
        new KeyValuePair<string, string>("oauth_client_id", "your_oauth_client_id"),
    });
    var msg = new HttpRequestMessage(HttpMethod.Post, "https://login.link.com/v1/link_auth_intent") {
        Content = content,
        Headers = { { "Authorization", $"Bearer {secretKey}" } }
    };
    var res = await http.SendAsync(msg);
    return Results.Json(await res.Content.ReadFromJsonAsync<JsonElement>(), statusCode: (int)res.StatusCode);
});
app.MapPost("/exchange-tokens", async (HttpRequest req) => {
    var body = await JsonSerializer.DeserializeAsync<JsonElement>(req.Body);
    var authIntentId = body.GetProperty("authIntentId").GetString()!;
    var msg = new HttpRequestMessage(HttpMethod.Post, $"https://login.link.com/v1/link_auth_intent/{authIntentId}/tokens") {
        Headers = { { "Authorization", $"Bearer {secretKey}" } }
    };
    var res = await http.SendAsync(msg);
    var data = await res.Content.ReadFromJsonAsync<JsonElement>();
    return data.TryGetProperty("access_token", out var tok)
        ? Results.Json(new { accessToken = tok.GetString() })
        : Results.Json(data, statusCode: (int)res.StatusCode);
});
app.MapPost("/create-onramp-session", async (HttpRequest req) => {
    var body = await JsonSerializer.DeserializeAsync<JsonElement>(req.Body);
    var oauthToken = req.Headers["Stripe-OAuth-Token"].FirstOrDefault();
    var form = new List<KeyValuePair<string, string>> {
        new("ui_mode", "headless"),
        new("crypto_customer_id", body.GetProperty("crypto_customer_id").GetString()!),
        new("payment_token", body.GetProperty("payment_token").GetString()!),
        new("source_amount", body.GetProperty("source_amount").GetRawText()),
        new("source_currency", body.GetProperty("source_currency").GetString()!),
        new("destination_currency", body.GetProperty("destination_currency").GetString()!),
        new("destination_network", body.GetProperty("destination_network").GetString()!),
    };
    if (body.TryGetProperty("customer_ip_address", out var ip))
        form.Add(new("customer_ip_address", ip.GetString()!));
    var msg = new HttpRequestMessage(HttpMethod.Post, "https://api.stripe.com/v1/crypto/onramp_sessions") {
        Content = new FormUrlEncodedContent(form),
        Headers = {
            { "Authorization", $"Bearer {secretKey}" },
            { "Stripe-OAuth-Token", oauthToken ?? "" },
        }
    };
    var res = await http.SendAsync(msg);
    var data = await res.Content.ReadFromJsonAsync<JsonElement>();
    return data.TryGetProperty("id", out _)
        ? Results.Json(new { id = data.GetProperty("id").GetString() })
        : Results.Json(data, statusCode: (int)res.StatusCode);
});
app.MapPost("/quote/{sessionId}", async (string sessionId, HttpRequest req) => {
    var oauthToken = req.Headers["Stripe-OAuth-Token"].FirstOrDefault();
    var msg = new HttpRequestMessage(HttpMethod.Post, $"https://api.stripe.com/v1/crypto/onramp_sessions/{sessionId}/quote") {
        Headers = {
            { "Authorization", $"Bearer {secretKey}" },
            { "Stripe-OAuth-Token", oauthToken ?? "" },
        }
    };
    var res = await http.SendAsync(msg);
    return Results.Content(await res.Content.ReadAsStringAsync(), "application/json", statusCode: (int)res.StatusCode);
});
app.MapPost("/checkout/{sessionId}", async (string sessionId, HttpRequest req) => {
    var oauthToken = req.Headers["Stripe-OAuth-Token"].FirstOrDefault();
    var msg = new HttpRequestMessage(HttpMethod.Post, $"https://api.stripe.com/v1/crypto/onramp_sessions/{sessionId}/checkout") {
        Headers = {
            { "Authorization", $"Bearer {secretKey}" },
            { "Stripe-OAuth-Token", oauthToken ?? "" },
        }
    };
    var res = await http.SendAsync(msg);
    return Results.Content(await res.Content.ReadAsStringAsync(), "application/json", statusCode: (int)res.StatusCode);
});
app.MapGet("/crypto/customer/{*path}", async (string path, HttpRequest req) => {
    var oauthToken = req.Headers["Stripe-OAuth-Token"].FirstOrDefault();
    var parts = (path ?? "").Split('/', 2, StringSplitOptions.RemoveEmptyEntries);
    var stripePath = parts.Length switch {
        1 => $"v1/crypto/customers/{parts[0]}",
        2 when parts[1] == "wallets" => $"v1/crypto/customers/{parts[0]}/crypto_consumer_wallets",
        2 when parts[1] == "payment-tokens" => $"v1/crypto/customers/{parts[0]}/payment_tokens",
        _ => $"v1/crypto/customers/{path}",
    };
    var msg = new HttpRequestMessage(HttpMethod.Get, $"https://api.stripe.com/{stripePath}") {
        Headers = {
            { "Authorization", $"Bearer {secretKey}" },
            { "Stripe-OAuth-Token", oauthToken ?? "" },
        }
    };
    var res = await http.SendAsync(msg);
    return Results.Content(await res.Content.ReadAsStringAsync(), "application/json", statusCode: (int)res.StatusCode);
});
  // This is a public sample test API key.
  // Don't submit any personally identifiable information in requests made with this key.
  // Sign in to see your own test API key embedded in code samples.
  // Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
  private static final String SECRET_KEY = "<<YOUR_SECRET_KEY>>";
    post("/create-link-auth-intent", (req, res) -> {
      JsonObject body = gson.fromJson(req.body(), JsonObject.class);
      String form = "email=" + body.get("email").getAsString()
          + "&oauth_scopes=" + body.get("oauthScopes").getAsString()
          + "&oauth_client_id=your_oauth_client_id";
      HttpRequest req2 = HttpRequest.newBuilder()
          .uri(URI.create("https://login.link.com/v1/link_auth_intent"))
          .header("Authorization", "Bearer " + SECRET_KEY)
          .header("Content-Type", "application/x-www-form-urlencoded")
          .POST(HttpRequest.BodyPublishers.ofString(form))
          .build();
      HttpResponse<String> resp = http.send(req2, HttpResponse.BodyHandlers.ofString());
      JsonObject data = gson.fromJson(resp.body(), JsonObject.class);
      return data.has("id") ? "{\"authIntentId\":\"" + data.get("id").getAsString() + "\"}" : resp.body();
    });
    post("/exchange-tokens", (req, res) -> {
      JsonObject body = gson.fromJson(req.body(), JsonObject.class);
      String authIntentId = body.get("authIntentId").getAsString();
      HttpRequest req2 = HttpRequest.newBuilder()
          .uri(URI.create("https://login.link.com/v1/link_auth_intent/" + authIntentId + "/tokens"))
          .header("Authorization", "Bearer " + SECRET_KEY)
          .POST(HttpRequest.BodyPublishers.noBody())
          .build();
      HttpResponse<String> resp = http.send(req2, HttpResponse.BodyHandlers.ofString());
      JsonObject data = gson.fromJson(resp.body(), JsonObject.class);
      return data.has("access_token")
          ? "{\"accessToken\":\"" + data.get("access_token").getAsString() + "\"}"
          : resp.body();
    });
    get("/crypto/customer/:id", (req, res) -> {
      String customerId = req.params(":id");
      String oauthToken = req.headers("Stripe-OAuth-Token");
      HttpRequest req2 = HttpRequest.newBuilder()
          .uri(URI.create("https://api.stripe.com/v1/crypto/customers/" + customerId))
          .header("Authorization", "Bearer " + SECRET_KEY)
          .header("Stripe-OAuth-Token", oauthToken)
          .GET()
          .build();
      return http.send(req2, HttpResponse.BodyHandlers.ofString()).body();
    });
    get("/crypto/customer/:id/wallets", (req, res) -> {
      String customerId = req.params(":id");
      String oauthToken = req.headers("Stripe-OAuth-Token");
      HttpRequest req2 = HttpRequest.newBuilder()
          .uri(URI.create("https://api.stripe.com/v1/crypto/customers/" + customerId + "/crypto_consumer_wallets"))
          .header("Authorization", "Bearer " + SECRET_KEY)
          .header("Stripe-OAuth-Token", oauthToken)
          .GET()
          .build();
      return http.send(req2, HttpResponse.BodyHandlers.ofString()).body();
    });
    get("/crypto/customer/:id/payment-tokens", (req, res) -> {
      String customerId = req.params(":id");
      String oauthToken = req.headers("Stripe-OAuth-Token");
      HttpRequest req2 = HttpRequest.newBuilder()
          .uri(URI.create("https://api.stripe.com/v1/crypto/customers/" + customerId + "/payment_tokens"))
          .header("Authorization", "Bearer " + SECRET_KEY)
          .header("Stripe-OAuth-Token", oauthToken)
          .GET()
          .build();
      return http.send(req2, HttpResponse.BodyHandlers.ofString()).body();
    });
    post("/create-onramp-session", (req, res) -> {
      JsonObject body = gson.fromJson(req.body(), JsonObject.class);
      String oauthToken = req.headers("Stripe-OAuth-Token");
      Map<String, String> params = new HashMap<>(Map.of(
          "ui_mode", "headless",
          "crypto_customer_id", body.get("crypto_customer_id").getAsString(),
          "payment_token", body.get("payment_token").getAsString(),
          "source_amount", body.get("source_amount").getAsString(),
          "source_currency", body.get("source_currency").getAsString(),
          "destination_currency", body.get("destination_currency").getAsString(),
          "destination_network", body.get("destination_network").getAsString()
      ));
      if (body.has("customer_ip_address") && !body.get("customer_ip_address").isJsonNull()) {
        params.put("customer_ip_address", body.get("customer_ip_address").getAsString());
      }
      if (body.has("wallet_addresses") && !body.get("wallet_addresses").isJsonNull()) {
        params.put("wallet_addresses", body.get("wallet_addresses").toString());
      }
      String form = params.entrySet().stream()
          .map(e -> e.getKey() + "=" + e.getValue())
          .collect(Collectors.joining("&"));
      HttpRequest req2 = HttpRequest.newBuilder()
          .uri(URI.create("https://api.stripe.com/v1/crypto/onramp_sessions"))
          .header("Authorization", "Bearer " + SECRET_KEY)
          .header("Stripe-OAuth-Token", oauthToken)
          .header("Content-Type", "application/x-www-form-urlencoded")
          .POST(HttpRequest.BodyPublishers.ofString(form))
          .build();
      HttpResponse<String> resp = http.send(req2, HttpResponse.BodyHandlers.ofString());
      JsonObject data = gson.fromJson(resp.body(), JsonObject.class);
      return data.has("id") ? "{\"id\":\"" + data.get("id").getAsString() + "\"}" : resp.body();
    });
    post("/quote/:sessionId", (req, res) -> {
      String sessionId = req.params(":sessionId");
      String oauthToken = req.headers("Stripe-OAuth-Token");
      HttpRequest req2 = HttpRequest.newBuilder()
          .uri(URI.create("https://api.stripe.com/v1/crypto/onramp_sessions/" + sessionId + "/quote"))
          .header("Authorization", "Bearer " + SECRET_KEY)
          .header("Stripe-OAuth-Token", oauthToken)
          .POST(HttpRequest.BodyPublishers.noBody())
          .build();
      return http.send(req2, HttpResponse.BodyHandlers.ofString()).body();
    });
    post("/checkout/:sessionId", (req, res) -> {
      String sessionId = req.params(":sessionId");
      String oauthToken = req.headers("Stripe-OAuth-Token");
      HttpRequest req2 = HttpRequest.newBuilder()
          .uri(URI.create("https://api.stripe.com/v1/crypto/onramp_sessions/" + sessionId + "/checkout"))
          .header("Authorization", "Bearer " + SECRET_KEY)
          .header("Stripe-OAuth-Token", oauthToken)
          .POST(HttpRequest.BodyPublishers.noBody())
          .build();
      return http.send(req2, HttpResponse.BodyHandlers.ofString()).body();
    });
import React from 'react';
import { StripeProvider } from '@stripe/stripe-react-native';
import { EmbeddedComponentsOnramp } from './EmbeddedComponentsOnramp';

export default function App() {
  return (
    <StripeProvider
      publishableKey="pk_test_..."
      merchantIdentifier="merchant.identifier"
      urlScheme="your-url-scheme"
    >
      <EmbeddedComponentsOnramp />
    </StripeProvider>
  );
}
import { useOnramp } from '@stripe/stripe-react-native';
export function EmbeddedComponentsOnramp() {
  const {
    configure,
    hasLinkAccount,
    authorize,
    attachKycInfo,
    verifyIdentity,
    registerWalletAddress,
    collectPaymentMethod,
    createCryptoPaymentToken,
    performCheckout,
  } = useOnramp();
  useEffect(() => {
    configure({ merchantDisplayName: 'My Crypto App' });
  }, [configure]);
  async function handleLogInWithLink() {
    const { hasLinkAccount: has } = await hasLinkAccount(email);
    if (!has) return;

    const authIntent = await api('/create-link-auth-intent', {
      method: 'POST',
      body: JSON.stringify({ email, oauthScopes: OAUTH_SCOPES }),
    });
    const auth = await authorize(authIntent.authIntentId);
    if (auth?.status !== 'Consented' || !auth.customerId) return;

    const { accessToken: tok } = await api('/exchange-tokens', {
      method: 'POST',
      body: JSON.stringify({ authIntentId: authIntent.authIntentId }),
    });
    setAccessToken(tok);
    setCryptoCustomerId(auth.customerId);
    await routeToNextStep(auth.customerId, tok);
  }
  async function handleAttachKycInfo() {
    await attachKycInfo({
      firstName: 'FirstName',
      lastName: 'LastName',
      idNumber: '000000000',
      dateOfBirth: { day: 1, month: 1, year: 1990 },
      address: { line1: '123 Main St', city: 'San Francisco', state: 'CA', postalCode: '94111', country: 'US' },
    });
    if (cryptoCustomerId) await routeToNextStep(cryptoCustomerId);
  }

  async function handleVerifyIdentity() {
    await verifyIdentity();
    if (cryptoCustomerId) await routeToNextStep(cryptoCustomerId);
  }
  async function handleCheckout() {
    if (!cryptoCustomerId || !cryptoPaymentToken || !accessToken) return;

    const session = await api('/create-onramp-session', {
      method: 'POST',
      body: JSON.stringify({
        crypto_customer_id: cryptoCustomerId,
        payment_token: cryptoPaymentToken,
        source_amount: parseFloat(amount) || 100,
        source_currency: 'usd',
        destination_currency: 'usdc',
        destination_network: 'base',
        wallet_addresses: walletAddress ? { base: walletAddress } : undefined,
      }),
    });

    await api(`/quote/${session.id}`, { method: 'POST' });

    const result = await performCheckout(session.id, async () => {
      const data = await api(`/checkout/${session.id}`, { method: 'POST' });
      if (data.transaction_details?.last_error === 'charged_with_expired_quote') {
        await api(`/quote/${session.id}`, { method: 'POST' });
        const retry = await api(`/checkout/${session.id}`, { method: 'POST' });
        return retry.client_secret;
      }
      return data.client_secret;
    });
    setStep('complete');
  }
{
  "name": "stripe-sample",
  "version": "1.0.0",
  "description": "A sample Stripe implementation",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "author": "stripe-samples",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1",
    "stripe": "^21.0.1"
  }
}
{
  "name": "stripe-sample",
  "version": "0.1.0",
  "dependencies": {
    "@stripe/react-stripe-js": "^3.7.0",
    "@stripe/stripe-js": "^7.3.0",
    "express": "^4.17.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-scripts": "^3.4.0",
    "stripe": "21.0.1"
  },
  "devDependencies": {
    "concurrently": "4.1.2"
  },
  "homepage": "http://localhost:3000/checkout",
  "proxy": "http://localhost:4242",
  "scripts": {
    "start-client": "react-scripts start",
    "start-server": "node server.js",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "start": "concurrently \"yarn start-client\" \"yarn start-server\""
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}
certifi==2026.1.4
chardet==5.2.0
click==8.3.1
Flask==3.1.2
idna==3.11
itsdangerous==2.2.0
Jinja2==3.1.6
MarkupSafe==3.0.3
requests==2.32.5
stripe==15.0.0
toml==0.10.2
Werkzeug==3.1.5
require github.com/stripe/stripe-go/v85 v85.0.0
## Next steps

#### [Embedded Components onramp integration guide](https://docs.stripe.com/crypto/onramp/embedded-components-integration-guide.md)

Detailed step-by-step instructions for building the integration. Use this to build your own flow.

#### [Embedded Components onramp overview](https://docs.stripe.com/crypto/onramp/embedded-components.md)

Learn about the customer flow and integration phases for the Embedded Components onramp.
