Skip to main content
RapidDev - Software Development Agency
stripe-guide

How to get a Stripe virtual terminal

Access the Stripe virtual terminal by going to Dashboard → Payments → + Create payment. Enter the customer's card number, expiration, CVC, and charge amount to process a manual payment. This works for phone orders and in-person payments without a card reader. No additional setup or hardware is required — it is built into every Stripe account.

What you'll learn

  • How to access the virtual terminal in the Stripe Dashboard
  • How to manually enter card details and process a charge
  • How to add customer information and receipts to manual charges
  • The limitations and fee differences for manually entered cards
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner6 min read5 minutesStripe Dashboard, any Stripe account (no code required)March 2026RapidDev Engineering Team
TL;DR

Access the Stripe virtual terminal by going to Dashboard → Payments → + Create payment. Enter the customer's card number, expiration, CVC, and charge amount to process a manual payment. This works for phone orders and in-person payments without a card reader. No additional setup or hardware is required — it is built into every Stripe account.

What Is a Stripe Virtual Terminal?

A virtual terminal lets you type in a customer's card details directly to process a payment — no physical card reader needed. Stripe includes this feature in every account through the Dashboard's 'Create payment' form. It is commonly used for phone orders, mail orders (MOTO), and occasional in-person transactions where a customer reads their card number to you. Unlike card-present transactions, manually keyed payments carry higher fraud risk, so Stripe charges a slightly different fee structure and these transactions are not eligible for the liability shift from 3D Secure.

Prerequisites

  • A Stripe account with live mode activated (identity verification complete)
  • The customer's card number, expiration date, and CVC
  • Admin or transfer-level access to the Stripe Dashboard

Step-by-step guide

1

Open the virtual terminal

Log in to the Stripe Dashboard. Click 'Payments' in the left sidebar, then click the '+ Create payment' button in the top right corner. This opens the manual payment form.

Expected result: A form appears with fields for amount, currency, card number, expiration, CVC, and optional customer details.

2

Enter payment details

Enter the charge amount in dollars (Stripe converts to cents automatically in this form). Type the customer's card number, expiration date (MM/YY), and CVC. Optionally add the customer's email for a receipt and a description for your records.

Expected result: All payment fields are filled in and ready to submit.

3

Process the payment

Review the details, then click 'Create payment'. Stripe processes the charge in real time. If the card is declined, you see an error message immediately and can ask the customer for a different card.

Expected result: The payment is processed and appears in your Payments list. If an email was provided, a receipt is sent to the customer.

4

Process a manual payment via the API

For automating manual card entry (e.g., a phone order system), create a PaymentMethod and PaymentIntent in sequence.

typescript
1const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
2
3// Create a PaymentMethod from raw card data
4// NOTE: This requires PCI DSS compliance. For most businesses,
5// use Stripe.js or Elements on the frontend instead.
6const paymentIntent = await stripe.paymentIntents.create({
7 amount: 7500, // $75.00
8 currency: 'usd',
9 payment_method_data: {
10 type: 'card',
11 card: {
12 token: 'tok_visa', // In production, collect via Stripe.js
13 },
14 },
15 confirm: true,
16 description: 'Phone order #1234',
17 receipt_email: 'customer@example.com',
18 automatic_payment_methods: {
19 enabled: true,
20 allow_redirects: 'never',
21 },
22});
23
24console.log('Payment:', paymentIntent.id, paymentIntent.status);

Expected result: The payment is processed and a receipt email is sent to the customer.

Complete working example

virtual-terminal.js
1const express = require('express');
2const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
3
4const app = express();
5app.use(express.json());
6
7// Process a manual/phone-order payment
8// In production, card details should be tokenized via Stripe.js
9// to avoid PCI compliance requirements on your server
10app.post('/api/manual-charge', async (req, res) => {
11 try {
12 const { amount, token, email, description } = req.body;
13
14 if (!amount || !token) {
15 return res.status(400).json({ error: 'Amount and card token are required' });
16 }
17
18 const paymentIntent = await stripe.paymentIntents.create({
19 amount, // in cents
20 currency: 'usd',
21 payment_method_data: {
22 type: 'card',
23 card: { token },
24 },
25 confirm: true,
26 description: description || 'Manual charge',
27 receipt_email: email || undefined,
28 automatic_payment_methods: {
29 enabled: true,
30 allow_redirects: 'never',
31 },
32 });
33
34 res.json({
35 id: paymentIntent.id,
36 status: paymentIntent.status,
37 amount: paymentIntent.amount / 100,
38 receipt_email: paymentIntent.receipt_email,
39 });
40 } catch (err) {
41 if (err.type === 'StripeCardError') {
42 return res.status(402).json({
43 error: err.message,
44 decline_code: err.decline_code,
45 });
46 }
47 res.status(500).json({ error: err.message });
48 }
49});
50
51// List recent manual payments
52app.get('/api/manual-charges', async (req, res) => {
53 try {
54 const payments = await stripe.paymentIntents.list({
55 limit: 20,
56 });
57
58 res.json(payments.data.map((pi) => ({
59 id: pi.id,
60 amount: pi.amount / 100,
61 status: pi.status,
62 description: pi.description,
63 created: new Date(pi.created * 1000).toISOString(),
64 })));
65 } catch (err) {
66 res.status(500).json({ error: err.message });
67 }
68});
69
70const PORT = process.env.PORT || 3000;
71app.listen(PORT, () => console.log(`Server on port ${PORT}`));

Common mistakes when getting a Stripe virtual terminal

Why it's a problem: Using the virtual terminal as the primary checkout method for an online business

How to avoid: The virtual terminal is for occasional manual charges (phone orders, etc.). For online sales, use Stripe Checkout, Payment Links, or Elements for better security and conversion.

Why it's a problem: Handling raw card numbers on your server without PCI compliance

How to avoid: If you build a custom virtual terminal, use Stripe.js to tokenize card data on the frontend. Only the token (tok_ or pm_) should reach your server. This keeps you out of PCI DSS scope.

Why it's a problem: Not adding a description to manual charges

How to avoid: Always add a description (e.g., 'Phone order #1234') so you can identify the charge later in your Dashboard and for dispute evidence.

Why it's a problem: Forgetting to include the customer's email for receipt

How to avoid: Always enter the customer's email so Stripe sends an automatic receipt. This creates a paper trail useful for potential disputes.

Best practices

  • Always add a description and customer email to manual charges for record-keeping and receipt delivery
  • Use the Dashboard virtual terminal for occasional charges — build a custom terminal with Stripe.js for regular phone-order workflows
  • Never store or log raw card numbers — use tokenization via Stripe.js or Stripe Terminal
  • Test in test mode with card 4242424242424242 before processing live manual charges
  • Be aware that manually entered card payments have higher fraud risk — verify customer identity when possible
  • Consider Stripe Terminal (hardware card readers) if you regularly accept in-person payments
  • Enable receipt emails in Settings → Emails to automatically send confirmations for all charges

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

Write a Node.js Express endpoint that processes manual Stripe payments similar to a virtual terminal. Accept a card token (from Stripe.js), amount, customer email, and description. Create a PaymentIntent with confirm: true and handle card decline errors gracefully.

Stripe Prompt

Build a simple virtual terminal for my phone-order business. Create a web form that tokenizes card details with Stripe.js on the frontend, sends the token to my server, and creates a confirmed PaymentIntent with receipt email and order description.

Frequently asked questions

Does Stripe charge extra for virtual terminal (manually keyed) transactions?

Stripe's standard online rate is 2.9% + $0.30. Manually keyed (card-not-present) transactions use the same rate. However, manually entered cards have higher fraud risk and are not protected by 3D Secure's liability shift.

Is the Stripe virtual terminal PCI compliant?

Yes. The Dashboard virtual terminal is fully PCI compliant because card data is entered directly into Stripe's interface, not through your systems. If you build a custom terminal, you must tokenize card data via Stripe.js.

Can I save the customer's card for future charges?

When using the Dashboard virtual terminal, you can create a customer record and save the card for future use. Check 'Save card for future use' during manual payment creation.

What is the difference between a virtual terminal and Stripe Terminal?

The virtual terminal is for manually typing card numbers (phone/mail orders). Stripe Terminal is a hardware card reader system for in-person chip and tap payments, which qualifies for lower card-present rates.

Can multiple team members use the virtual terminal?

Yes. Add team members in Dashboard → Settings → Team. Assign them the 'Transfer Analyst' or 'Administrator' role to let them create manual payments.

What if I need a custom phone-order system integrated with Stripe?

For businesses handling regular phone orders that need a branded interface, order management, and CRM integration, the RapidDev team can build a custom virtual terminal solution on top of Stripe's API.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.