| name | stripe-terminal-issuing |
| description | Use when implementing in-person payments with Terminal or issuing cards with Stripe Issuing. Invoke for POS terminal setup, card reader integration, in-person payment processing, card issuance, authorization controls, or physical/virtual card management. |
| allowed-tools | Read, Grep, Glob |
Stripe Terminal & Issuing Expert
Purpose
Expert knowledge of Stripe Terminal for in-person payments and Stripe Issuing for card programs. Covers POS reader setup, Terminal SDK integration, card issuance, authorization controls, and card lifecycle management.
When to Use
Invoke this skill when:
- Implementing in-person payments
- Setting up card readers (Terminal)
- Processing payments at physical locations
- Creating card programs (Issuing)
- Issuing physical or virtual cards
- Setting up spending controls
- Managing card authorizations
- Handling card disputes
- Debugging Terminal or Issuing issues
Documentation Available
Location: /Users/zach/Documents/cc-skills/docs/stripe/
Coverage (~149 files in terminal/ + issuing/):
-
Terminal (~75 files):
- Reader management (Stripe Reader M2, BBPOS, Verifone)
- Connection tokens
- Payment collection
- Refunds and cancellations
- Terminal SDK (JS, iOS, Android)
- Reader locations
-
Issuing (~74 files):
- Card creation (physical, virtual)
- Cardholder management
- Authorization controls
- Spending limits
- Authorization holds
- Card disputes
- Transaction management
Related Skills
- stripe-payments: For online payment flows
- stripe-api-integration: For webhooks and API patterns
Best Practices
Terminal
- Test with simulated readers - Use test mode readers
- Handle connection states - Manage reader connections properly
- Implement offline mode - Store payments when offline
- Update reader software - Keep firmware updated
- Handle timeouts - Set appropriate payment timeouts
Issuing
- Set authorization controls - Prevent fraud
- Monitor spending - Track card usage
- Handle real-time authorizations - Approve/decline instantly
- Implement 3DS - For online card use
- Manage card lifecycle - Issue, activate, cancel properly
Examples
Example 1: Terminal Reader Setup
Question: "How do I set up Stripe Terminal?"
Response:
import { loadStripeTerminal } from '@stripe/terminal-js';
export async function createConnectionToken(req, res) {
try {
const terminal = require('stripe')(process.env.STRIPE_SECRET_KEY!).terminal;
const connectionToken = await terminal.connectionTokens.create();
res.json({ secret: connectionToken.secret });
} catch (error) {
res.status(500).json({ error: error.message });
}
}
async function initializeTerminal() {
const StripeTerminal = await loadStripeTerminal();
const terminal = StripeTerminal.create({
onFetchConnectionToken: async () => {
const response = await fetch('/api/connection-token', {
method: 'POST',
}).( r.());
response.;
},
: {
.();
},
});
terminal;
}
() {
discoverResult = terminal.({
: ,
: ,
});
(discoverResult.) {
.(, discoverResult.);
} {
.(, discoverResult.);
discoverResult.;
}
}
() {
connectResult = terminal.(reader);
(connectResult.) {
.(, connectResult.);
} {
.(, connectResult.);
connectResult.;
}
}
() {
terminal = ();
readers = (terminal);
(readers && readers. > ) {
(terminal, readers[]);
}
terminal;
}
References:
- See:
docs/stripe/terminal/
Example 2: Collect Payment with Terminal
Question: "How do I collect an in-person payment?"
Response:
export async function createTerminalPaymentIntent(req, res) {
try {
const { amount } = req.body;
const paymentIntent = await stripe.paymentIntents.create({
amount: amount * 100,
currency: 'usd',
payment_method_types: ['card_present'],
capture_method: 'automatic',
metadata: {
orderId: 'order_123',
},
});
res.json({ clientSecret: paymentIntent.client_secret });
} catch (error) {
res.status(500).json({ error: error.message });
}
}
async function collectPayment(terminal: any, amount: number) {
try {
const response = await fetch('/api/create-terminal-payment-intent', {
: ,
: { : },
: .({ amount }),
}).( r.());
collectResult = terminal.(
response.
);
(collectResult.) {
(collectResult..);
}
.(, collectResult.);
processResult = terminal.(
collectResult.
);
(processResult.) {
(processResult..);
}
.(, processResult.);
(, {
: ,
: { : },
: .({
: processResult..,
}),
});
processResult.;
} (error) {
.(, error);
error;
}
}
() {
cancelResult = terminal.();
(cancelResult.) {
.(, cancelResult.);
} {
.();
}
}
terminal.(, {
.(, status);
});
terminal.(, {
.(, status);
});
References:
- See:
docs/stripe/terminal/payments/
Example 3: Create Location and Register Reader
Question: "How do I register a physical reader?"
Response:
export async function createLocation(req, res) {
try {
const { displayName, address } = req.body;
const location = await stripe.terminal.locations.create({
display_name: displayName,
address: {
line1: address.line1,
city: address.city,
state: address.state,
postal_code: address.postalCode,
country: address.country,
},
});
res.json({ location });
} catch (error) {
res.status(500).json({ error: error.message });
}
}
export async function listLocations(req, res) {
try {
const locations = await stripe.terminal.locations.list({
limit: 10,
});
res.json({ locations: locations.data });
} (error) {
res.().({ : error. });
}
}
() {
{
{ locationId } = req.;
readers = stripe...({
: locationId ,
: ,
});
res.({ : readers. });
} (error) {
res.().({ : error. });
}
}
() {
{
{ readerId, label, metadata } = req.;
reader = stripe...(readerId, {
label,
metadata,
});
res.({ reader });
} (error) {
res.().({ : error. });
}
}
() {
{
{ readerId } = req.;
deleted = stripe...(readerId);
res.({ deleted });
} (error) {
res.().({ : error. });
}
}
References:
- See:
docs/stripe/terminal/locations/
- See:
docs/stripe/terminal/readers/
Example 4: Issue Cards
Question: "How do I issue cards with Stripe Issuing?"
Response:
export async function createCardholder(req, res) {
try {
const { name, email, phone, address } = req.body;
const cardholder = await stripe.issuing.cardholders.create({
name,
email,
phone_number: phone,
billing: {
address: {
line1: address.line1,
city: address.city,
state: address.state,
postal_code: address.postalCode,
country: address.country,
},
},
type: 'individual',
metadata: {
userId: req.user.id,
},
});
res.json({ cardholder });
} catch (error) {
res.status(500).json({ error: error.message });
}
}
export async function issueVirtualCard(req, res) {
try {
const { cardholderId } = req.;
card = stripe...({
: cardholderId,
: ,
: ,
: ,
: {
: [
{
: ,
: ,
},
],
: [, ],
},
: {
: ,
},
});
res.({ card });
} (error) {
res.().({ : error. });
}
}
() {
{
{ cardholderId, shippingAddress } = req.;
card = stripe...({
: cardholderId,
: ,
: ,
: ,
: {
: ,
: {
: shippingAddress.,
: shippingAddress.,
: shippingAddress.,
: shippingAddress.,
: shippingAddress.,
},
: ,
},
});
res.({ card });
} (error) {
res.().({ : error. });
}
}
() {
{
{ cardId } = req.;
card = stripe...(cardId , {
: [, ],
});
res.({
: card.,
: card.,
: card.,
: card.,
});
} (error) {
res.().({ : error. });
}
}
() {
{
{ cardId, status } = req.;
card = stripe...(cardId, {
status,
});
res.({ card });
} (error) {
res.().({ : error. });
}
}
Card Types:
- Virtual: Instant issuance, online use
- Physical: 3-7 day delivery, in-person use
References:
- See:
docs/stripe/issuing/cards/
Example 5: Authorization Controls
Question: "How do I control card authorizations?"
Response:
export async function setSpendingControls(req, res) {
try {
const { cardId, limits } = req.body;
const card = await stripe.issuing.cards.update(cardId, {
spending_controls: {
spending_limits: [
{
amount: 100000,
interval: 'per_month',
categories: ['restaurants'],
},
{
amount: 5000,
interval: 'per_transaction',
},
],
allowed_categories: [
'restaurants',
'gas_stations',
'grocery_stores',
],
blocked_categories: [
'gambling',
'adult_digital_content',
],
},
});
res.json({ card });
} catch (error) {
res.status(500).json({ error: error.message });
}
}
export async function handleAuthorizationWebhook() {
event = req.;
(event. === ) {
authorization = event..;
shouldApprove = (authorization);
(shouldApprove) {
stripe...(authorization.);
} {
stripe...(authorization., {
: ,
});
}
}
res.({ : });
}
(): <> {
cardholder = db..({
: { : authorization. },
});
(cardholder. < authorization.) {
;
}
(.(authorization..)) {
;
}
hour = ().();
(hour < || hour > ) {
;
}
;
}
() {
{
{ cardId } = req.;
authorizations = stripe...({
: cardId ,
: ,
});
res.({ : authorizations. });
} (error) {
res.().({ : error. });
}
}
() {
{
{ authorizationId, captureAmount } = req.;
authorization = stripe...(
authorizationId,
{
: { : captureAmount },
}
);
res.({ authorization });
} (error) {
res.().({ : error. });
}
}
Authorization States:
pending: Awaiting approval
approved: Approved
declined: Declined
References:
- See:
docs/stripe/issuing/controls/
Common Patterns
Terminal Payment Flow
Card Lifecycle
Real-time Authorization
Search Helpers
grep -r "Terminal\|reader\|in-person" /Users/zach/Documents/cc-skills/docs/stripe/terminal/
grep -r "Issuing\|card\|authorization" /Users/zach/Documents/cc-skills/docs/stripe/issuing/
ls /Users/zach/Documents/cc-skills/docs/stripe/terminal/
ls /Users/zach/Documents/cc-skills/docs/stripe/issuing/
Common Errors
Terminal
-
Reader not found: Reader not registered or offline
- Solution: Check reader power and connection
-
Connection token expired: Token older than 60 seconds
- Solution: Fetch new connection token
-
Payment timeout: Reader timeout waiting for card
- Solution: Set appropriate timeout or cancel
Issuing
-
Cardholder required: Missing cardholder
- Solution: Create cardholder first
-
Spending limit exceeded: Transaction over limit
- Solution: Adjust spending controls
-
Authorization declined: Custom logic declined
- Solution: Check authorization rules
Security Notes
Terminal
- Secure connection tokens - Use HTTPS
- Validate reader ID - Ensure reader belongs to your account
- Monitor offline mode - Reconcile offline payments
Issuing
- Protect card details - Never log full card numbers
- Implement fraud detection - Monitor unusual patterns
- Use real-time authorizations - Control spending instantly
- Secure cardholder data - Follow PCI compliance
Notes
- Documentation covers latest Stripe API (2023+)
- Terminal requires physical hardware (readers)
- Issuing requires approval from Stripe
- Real-time authorization webhook must respond within 5 seconds
- Virtual cards are instant, physical cards take 3-7 days
- File paths reference local documentation cache
- For latest updates, check: