Overview
Basis Theory Elements provides a secure method for tokenizing credit card information on the frontend. Card data is tokenized directly with Basis Theory — sensitive card details never touch your servers. This reduces your PCI scope by avoiding sensitive data transmission through your servers.
Use Rye’s Basis Theory Key to Generate Card Tokens
To generate a Basis Theory card token for confirming a Rye checkout intent, you must use Rye’s Basis Theory public key, not your own Basis Theory key.
This ensures the token is created under Rye’s account. Using your own key will cause the checkout intent to fail.
RYE_BT_PUBLIC_KEY_PRODUCTION=key_prod_us_pub_F4RCCEspEdE5hdtQnFfAFx
RYE_BT_PUBLIC_KEY_STAGING=key_test_us_pub_Gtquo4kCeDkj2hTFWJSYCX
Generate a Scoped Token
For better security, Rye requires developers to generate a scoped token. Doing so ensures that even if someone obtains your token ID, they will not be able to use it — the token is tied to your Rye account and must match your account credentials to function.
To scope the token, do the following:
- Create a session via Rye’s createSession endpoint. The response will return a session key and a container value.
- Use the returned session key in the BT-API-KEY header and pass the container value in the request body when creating the token.
The following example demonstrates how to do this using Rye’s TypeScript SDK.
import CheckoutIntents from "checkout-intents";
const client = new CheckoutIntents({
apiKey: "$RYE_API_KEY"
});
// Test card details (Basis Theory test card)
const CARD = {
number: "4242424242424242",
expiration_month: 12,
expiration_year: 2037,
cvc: "123",
};
async function tokenizeCard() {
// Create a session with Basis Theory to scope tokens to your account
console.log("Creating Basis Theory session...");
const { sessionKey, container } =
await client.paymentGateways.createSession("basis-theory");
console.log("Session created. Container:", container);
// Tokenize the card using the session key and container
console.log("Tokenizing card...");
const response = await fetch("https://api.basistheory.com/tokens", {
method: "POST",
headers: {
"Content-Type": "application/json",
"BT-API-KEY": sessionKey,
},
body: JSON.stringify({
type: "card",
data: CARD,
containers: [container],
}),
});
if (!response.ok) {
const error = await response.text();
console.error("Tokenization failed:", response.status, error);
process.exit(1);
}
const token = await response.json();
console.log("Token created:", token.id);
console.log("Full response:", JSON.stringify(token, null, 2));
}
tokenizeCard().catch((err) => {
console.error("Error:", err.message);
process.exit(1);
});
Generate a Token Using Demo App
You can use the demo app to generate a Basis Theory token for testing:
For the card number, use the test card number 4242 4242 4242 4242 with any future expiration date and any 3-digit CVC.
Alternatively, you can use the steps below to create a simple NextJS app to generate the token.
Generate a Token Using Basis Theory React Elements
This guide walks through the Basis Theory React Elements approach, including a NextJS demo app. For a complete example, see rye-com/basis-theory-cc-capture-ui.
Create a New NextJS App
npx create-next-app@latest bt-demo-next --typescript --tailwind --eslint --app --src-dir --no-import-alias --use-npm
cd bt-demo-next
Install Dependencies
npm install @basis-theory/react-elements checkout-intents
NextJS Example Code
Create src/app/actions.ts with code below.
checkout-intents sdk does not support client-side usage, so the session creation must be done in a server action. The createBasisTheorySession function creates a session with Basis Theory and returns the session key and container value, which are needed to scope tokens to your Rye account.
"use server";
import CheckoutIntents from "checkout-intents";
const client = new CheckoutIntents({
apiKey: "$RYE_API_KEY",
environment: "staging",
});
export async function createBasisTheorySession() {
const { sessionKey, container } =
await client.paymentGateways.createSession("basis-theory");
return { sessionKey, container };
}
Create /src/app/CardTokenizer.tsx with code below.
"use client";
import React, { useRef, useState, useCallback } from "react";
import {
BasisTheoryProvider,
CardElement,
useBasisTheory,
} from "@basis-theory/react-elements";
function CardForm({ sessionKey, container }: { sessionKey: string; container: string }) {
const { bt } = useBasisTheory(sessionKey);
const cardRef = useRef(null);
const [status, setStatus] = useState("");
const [loading, setLoading] = useState(false);
const handleSubmit = useCallback(
async (e: React.FormEvent) => {
e.preventDefault();
if (!bt || !cardRef.current || !container) return;
setLoading(true);
setStatus("");
try {
const token = await bt.tokens.create({
type: "card",
data: cardRef.current,
containers: [container],
});
setStatus(`Token created: ${token.id}`);
} catch (error: unknown) {
const message =
error instanceof Error ? error.message : "Unknown error";
setStatus(`Error: ${message}`);
} finally {
setLoading(false);
}
},
[bt, container]
);
return (
<BasisTheoryProvider bt={bt}>
<form
onSubmit={handleSubmit}
className="max-w-md w-full mx-auto p-6 border border-gray-300 rounded-xl shadow-sm bg-white"
>
<h2 className="text-lg font-semibold mb-4 text-gray-800">
Enter Card Details
</h2>
<div className="mb-4 p-3 border border-gray-300 rounded">
<CardElement id="card-element" ref={cardRef} />
</div>
<button
type="submit"
disabled={!bt || loading}
className="w-full bg-indigo-600 text-white font-medium py-2 px-4 rounded hover:bg-indigo-700 transition disabled:opacity-50"
>
{loading ? "Tokenizing..." : "Tokenize Card"}
</button>
{status && (
<p className="mt-4 text-sm text-gray-700 bg-gray-100 p-2 rounded break-all">
{status}
</p>
)}
</form>
</BasisTheoryProvider>
);
}
export default function CardTokenizer({ sessionKey, container }: { sessionKey: string; container: string }) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<CardForm sessionKey={sessionKey} container={container} />
</div>
);
}
Update src/app/page.tsx with code below.
import CardTokenizer from "./CardTokenizer";
import { createBasisTheorySession } from "./actions";
export default async function Home() {
// Create a session with Basis Theory and pass it to the CardTokenizer component to scope tokens to your Rye account
const { sessionKey, container } = await createBasisTheorySession();
return <CardTokenizer sessionKey={sessionKey} container={container} />;
}
Run the App Locally
The app will run locally at http://localhost:3000. Open that page in your browser to enter card details and generate a Basis Theory token. The token will be returned in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
For testing in the Rye staging environment, use the test card number 4242 4242 4242 4242 with any future expiration date and any 3-digit CVC.
Generate a Token Using Other Basis Theory SDKs
In addition to React, Basis Theory provides SDKs for other platforms, including vanilla JavaScript, iOS, and Android. The flow is similar across SDKs: initialize Basis Theory with Rye’s public key, present the card entry UI, and Basis Theory will return a token that you can pass to Rye.
Use the Token with Rye
Once you have a Basis Theory token, you can use it to complete a purchase through the Rye API. There are two flows available: a single-step purchase and a two-step flow.
Single-Step Purchase
Submit a complete purchase in one call by providing the product, buyer details, and payment method together:
curl -X POST https://api.rye.com/v1/checkout-intents/purchase \
-H "Authorization: Bearer $RYE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"productUrl": "https://example-store.myshopify.com/products/example-product",
"quantity": 1,
"buyer": {
"firstName": "Jane",
"lastName": "Doe",
"email": "jane@example.com",
"phone": "+15551234567",
"address1": "123 Main St",
"city": "New York",
"province": "NY",
"postalCode": "10001",
"country": "US"
},
"paymentMethod": {
"type": "basis_theory_token",
"basisTheoryToken": "<TOKEN_ID>"
}
}'
Two-Step Flow
If you have already created a checkout intent, you can submit payment separately using the intent ID:
curl -X POST https://api.rye.com/v1/checkout-intents/{id}/payment \
-H "Authorization: Bearer $RYE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"paymentMethod": {
"type": "basis_theory_token",
"basisTheoryToken": "<TOKEN_ID>"
}
}'
Notes
- Basis Theory tokens can be reused, but the CVC may need to be refreshed for subsequent orders.
- Sensitive card data is handled entirely by Basis Theory and never touches your servers, reducing your PCI scope.