CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-stripe

Stripe API wrapper for Node.js providing comprehensive payment processing, subscription management, and financial services integration.

Pending
Overview
Eval results
Files

terminal.mddocs/

Terminal

Stripe Terminal enables in-person payment processing through certified card readers. This system provides comprehensive support for physical point-of-sale transactions, including chip cards, contactless payments, and mobile wallets across various reader types.

Terminal Configuration

Terminal.Configurations

Manage reader configurations and settings:

interface TerminalConfiguration {
  id: string;
  object: 'terminal.configuration';
  name?: string;
  bbpos_wisepos_e?: {
    splashscreen?: string;
  };
  offline?: {
    enabled: boolean;
  };
  reboot_window?: {
    end_hour: number;
    start_hour: number;
  };
  stripe_s700?: {
    splashscreen?: string;
  };
  tipping?: {
    aud?: CurrencyTippingConfig;
    cad?: CurrencyTippingConfig;
    chf?: CurrencyTippingConfig;
    czk?: CurrencyTippingConfig;
    dkk?: CurrencyTippingConfig;
    eur?: CurrencyTippingConfig;
    gbp?: CurrencyTippingConfig;
    hkd?: CurrencyTippingConfig;
    myr?: CurrencyTippingConfig;
    nok?: CurrencyTippingConfig;
    nzd?: CurrencyTippingConfig;
    pln?: CurrencyTippingConfig;
    sek?: CurrencyTippingConfig;
    sgd?: CurrencyTippingConfig;
    usd?: CurrencyTippingConfig;
  };
  verifone_p400?: {
    splashscreen?: string;
  };
}

interface CurrencyTippingConfig {
  fixed_amounts?: number[];
  percentages?: number[];
  smart_tip_threshold?: number;
}

// Create basic configuration
const configuration = await stripe.terminal.configurations.create({
  name: 'Store Front Configuration',
  tipping: {
    usd: {
      fixed_amounts: [100, 150, 200], // $1, $1.50, $2
      percentages: [15, 18, 20],
      smart_tip_threshold: 1000 // $10 threshold for smart tips
    }
  },
  offline: {
    enabled: true
  },
  reboot_window: {
    start_hour: 2, // 2 AM
    end_hour: 4   // 4 AM
  }
});

// Create configuration with custom splash screen
const customConfig = await stripe.terminal.configurations.create({
  name: 'Restaurant Configuration',
  bbpos_wisepos_e: {
    splashscreen: 'file_restaurant_logo'
  },
  stripe_s700: {
    splashscreen: 'file_restaurant_logo'
  },
  tipping: {
    usd: {
      fixed_amounts: [200, 300, 500], // $2, $3, $5
      percentages: [18, 20, 22],
      smart_tip_threshold: 2000 // $20 threshold
    }
  }
});

// Retrieve configuration
const retrieved = await stripe.terminal.configurations.retrieve('tconf_123');

// Update configuration
const updated = await stripe.terminal.configurations.update('tconf_123', {
  name: 'Updated Store Configuration',
  tipping: {
    usd: {
      fixed_amounts: [150, 250, 350],
      percentages: [15, 20, 25]
    }
  }
});

// List configurations
const configurations = await stripe.terminal.configurations.list({
  limit: 10
});

// Delete configuration
const deleted = await stripe.terminal.configurations.del('tconf_123');

Location Management

Terminal.Locations

Manage physical locations where readers operate:

interface TerminalLocation {
  id: string;
  object: 'terminal.location';
  display_name?: string;
  address: {
    city?: string;
    country: string;
    line1?: string;
    line2?: string;
    postal_code?: string;
    state?: string;
  };
  configuration_overrides?: string;
  metadata?: { [key: string]: string };
}

// Create location
const location = await stripe.terminal.locations.create({
  display_name: 'Main Store Location',
  address: {
    line1: '123 Main Street',
    line2: 'Suite 100',
    city: 'San Francisco',
    state: 'CA',
    postal_code: '94105',
    country: 'US'
  },
  metadata: {
    store_id: 'store_001',
    manager: 'John Doe'
  }
});

// Create location with configuration override
const restaurantLocation = await stripe.terminal.locations.create({
  display_name: 'Downtown Restaurant',
  address: {
    line1: '456 Restaurant Row',
    city: 'New York',
    state: 'NY',
    postal_code: '10001',
    country: 'US'
  },
  configuration_overrides: 'tconf_restaurant_config'
});

// Retrieve location
const retrieved = await stripe.terminal.locations.retrieve('tml_123');

// Update location
const updated = await stripe.terminal.locations.update('tml_123', {
  display_name: 'Updated Store Name',
  metadata: {
    store_id: 'store_001',
    manager: 'Jane Smith',
    last_updated: new Date().toISOString()
  }
});

// List locations
const locations = await stripe.terminal.locations.list({
  limit: 20
});

// Delete location
const deleted = await stripe.terminal.locations.del('tml_123');

Reader Management

Terminal.Readers

Manage and control card readers:

interface TerminalReader {
  id: string;
  object: 'terminal.reader';
  device_type: 'bbpos_wisepad3' | 'bbpos_wisepos_e' | 'simulated_wisepos_e' | 'stripe_m2' | 'stripe_s700' | 'verifone_p400' | 'mobile_phone_reader';
  label?: string;
  location?: string;
  serial_number: string;
  status: 'online' | 'offline';
  device_sw_version?: string;
  ip_address?: string;
  base_url?: string;
  registration_code?: string;
  action?: {
    type: 'process_payment_intent' | 'process_setup_intent' | 'set_reader_display';
    status: 'in_progress' | 'succeeded' | 'failed';
    failure_code?: string;
    failure_message?: string;
    process_payment_intent?: {
      payment_intent: string;
      process_config?: {
        skip_tipping?: boolean;
        tipping?: TippingConfiguration;
      };
    };
    process_setup_intent?: {
      setup_intent: string;
      process_config?: {
        customer_cancellation?: boolean;
      };
    };
  };
}

// Register new reader
const reader = await stripe.terminal.readers.create({
  registration_code: 'simulated-wpe',
  label: 'Front Desk Reader',
  location: 'tml_main_store'
});

// Register reader with specific device type
const physicalReader = await stripe.terminal.readers.create({
  registration_code: 'stripe-reader-12345',
  label: 'Checkout Station 1',
  location: 'tml_store_001',
  metadata: {
    station_number: '1',
    cashier_terminal: 'pos_001'
  }
});

// Retrieve reader
const retrieved = await stripe.terminal.readers.retrieve('tmr_123');

// Update reader
const updated = await stripe.terminal.readers.update('tmr_123', {
  label: 'Updated Reader Label',
  metadata: {
    last_maintenance: new Date().toISOString(),
    firmware_version: '1.2.3'
  }
});

// List readers
const readers = await stripe.terminal.readers.list({
  device_type: 'verifone_p400',
  location: 'tml_123',
  limit: 20
});

// List readers by status
const onlineReaders = await stripe.terminal.readers.list({
  status: 'online'
});

// Delete reader
const deleted = await stripe.terminal.readers.del('tmr_123');

Payment Processing

Process PaymentIntent

Handle in-person payments through terminal readers:

// Process payment with reader
const payment = await stripe.terminal.readers.processPaymentIntent(
  'tmr_reader_123',
  {
    payment_intent: 'pi_payment_123',
    process_config: {
      skip_tipping: false,
      tipping: {
        amount_eligible: 2000, // $20 eligible for tipping
      }
    }
  }
);

// Process payment with custom tipping
const tippedPayment = await stripe.terminal.readers.processPaymentIntent(
  'tmr_reader_123',
  {
    payment_intent: 'pi_payment_456',
    process_config: {
      tipping: {
        amount_eligible: 5000, // $50 eligible for tipping
        fixed_amounts: [200, 300, 500], // $2, $3, $5
        percentages: [15, 18, 20]
      }
    }
  }
);

// Process payment without tipping
const noTipPayment = await stripe.terminal.readers.processPaymentIntent(
  'tmr_reader_123',
  {
    payment_intent: 'pi_payment_789',
    process_config: {
      skip_tipping: true
    }
  }
);

Process SetupIntent

Handle card setup for future payments:

// Process setup intent for card on file
const setup = await stripe.terminal.readers.processSetupIntent(
  'tmr_reader_123',
  {
    setup_intent: 'seti_setup_123',
    process_config: {
      customer_cancellation: true // Allow customer to cancel
    }
  }
);

// Process setup intent without customer cancellation
const restrictedSetup = await stripe.terminal.readers.processSetupIntent(
  'tmr_reader_123',
  {
    setup_intent: 'seti_setup_456',
    process_config: {
      customer_cancellation: false
    }
  }
);

Reader Display Control

Set Reader Display

Control what appears on the reader screen:

interface ReaderDisplayCart {
  line_items: Array<{
    description: string;
    amount: number;
    quantity: number;
  }>;
  tax?: number;
  total: number;
  currency: string;
}

// Display shopping cart
const cartDisplay = await stripe.terminal.readers.setReaderDisplay(
  'tmr_reader_123',
  {
    type: 'cart',
    cart: {
      line_items: [
        {
          description: 'Coffee',
          amount: 350, // $3.50
          quantity: 2
        },
        {
          description: 'Pastry',
          amount: 250, // $2.50
          quantity: 1
        }
      ],
      tax: 95, // $0.95
      total: 1045, // $10.45
      currency: 'usd'
    }
  }
);

// Clear display
const clearDisplay = await stripe.terminal.readers.setReaderDisplay(
  'tmr_reader_123',
  {
    type: 'clear'
  }
);

Reader Actions

Cancel Reader Action

Cancel ongoing reader operations:

// Cancel current action on reader
const canceled = await stripe.terminal.readers.cancelAction('tmr_reader_123');

Connection Management

Terminal.ConnectionTokens

Create connection tokens for reader connectivity:

interface TerminalConnectionToken {
  object: 'terminal.connection_token';
  secret: string;
}

// Create connection token
const connectionToken = await stripe.terminal.connectionTokens.create({
  location: 'tml_store_location'
});

// Create connection token without location restriction
const globalToken = await stripe.terminal.connectionTokens.create();

Test Helpers

TestHelpers.Terminal

Simulate terminal scenarios in test mode:

// Present payment method to reader
const presented = await stripe.testHelpers.terminal.presentPaymentMethod(
  'tmr_simulated_reader'
);

// Simulate different card types
const visaPresented = await stripe.testHelpers.terminal.presentPaymentMethod(
  'tmr_simulated_reader',
  {
    type: 'card_present',
    card_present: {
      number: '4242424242424242'
    }
  }
);

const contactlessPresented = await stripe.testHelpers.terminal.presentPaymentMethod(
  'tmr_simulated_reader',
  {
    type: 'card_present',
    card_present: {
      number: '4000000000000002', // Declined card for testing
      brand: 'visa'
    }
  }
);

Integration Examples

Complete Terminal Payment Flow

// 1. Create payment intent
const paymentIntent = await stripe.paymentIntents.create({
  amount: 2500, // $25.00
  currency: 'usd',
  payment_method_types: ['card_present'],
  capture_method: 'manual' // For tip adjustment
});

// 2. Process payment on terminal
const terminalPayment = await stripe.terminal.readers.processPaymentIntent(
  'tmr_reader_123',
  {
    payment_intent: paymentIntent.id,
    process_config: {
      tipping: {
        amount_eligible: 2500,
        percentages: [15, 18, 20, 25]
      }
    }
  }
);

// 3. Wait for completion (via webhook or polling)
// Once completed, capture with tip if applicable
if (terminalPayment.status === 'succeeded') {
  const finalPayment = await stripe.paymentIntents.capture(paymentIntent.id, {
    amount_to_capture: terminalPayment.amount // Includes tip
  });
}

Point of Sale Integration

class TerminalPOS {
  private readerId: string;
  
  constructor(readerId: string) {
    this.readerId = readerId;
  }

  async processOrder(orderItems: OrderItem[], customerId?: string) {
    // Calculate total
    const subtotal = orderItems.reduce((sum, item) => 
      sum + (item.price * item.quantity), 0
    );
    const tax = Math.round(subtotal * 0.0875); // 8.75% tax
    const total = subtotal + tax;

    // Display cart on reader
    await stripe.terminal.readers.setReaderDisplay(this.readerId, {
      type: 'cart',
      cart: {
        line_items: orderItems.map(item => ({
          description: item.name,
          amount: item.price,
          quantity: item.quantity
        })),
        tax,
        total,
        currency: 'usd'
      }
    });

    // Create payment intent
    const paymentIntent = await stripe.paymentIntents.create({
      amount: total,
      currency: 'usd',
      customer: customerId,
      payment_method_types: ['card_present'],
      metadata: {
        order_items: JSON.stringify(orderItems),
        subtotal: subtotal.toString(),
        tax: tax.toString()
      }
    });

    // Process payment
    const result = await stripe.terminal.readers.processPaymentIntent(
      this.readerId,
      {
        payment_intent: paymentIntent.id,
        process_config: {
          tipping: {
            amount_eligible: subtotal,
            percentages: [15, 18, 20]
          }
        }
      }
    );

    return result;
  }

  async handleRefund(originalPaymentIntentId: string, amount?: number) {
    const refund = await stripe.refunds.create({
      payment_intent: originalPaymentIntentId,
      amount: amount, // Partial refund if specified
      reason: 'requested_by_customer'
    });

    return refund;
  }
}

Multi-location Terminal Management

class TerminalManager {
  async deployReadersToLocation(locationId: string, readerCount: number) {
    const readers = [];
    
    for (let i = 1; i <= readerCount; i++) {
      const reader = await stripe.terminal.readers.create({
        registration_code: `simulated-reader-${i}`,
        label: `Station ${i}`,
        location: locationId,
        metadata: {
          station_number: i.toString(),
          deployment_date: new Date().toISOString()
        }
      });
      
      readers.push(reader);
    }
    
    return readers;
  }

  async getLocationStatus(locationId: string) {
    const readers = await stripe.terminal.readers.list({
      location: locationId
    });
    
    const onlineCount = readers.data.filter(r => r.status === 'online').length;
    const totalCount = readers.data.length;
    
    return {
      location: locationId,
      total_readers: totalCount,
      online_readers: onlineCount,
      offline_readers: totalCount - onlineCount,
      readers: readers.data
    };
  }

  async updateAllReadersConfiguration(locationId: string, configId: string) {
    const location = await stripe.terminal.locations.update(locationId, {
      configuration_overrides: configId
    });
    
    return location;
  }
}

Real-time Reader Monitoring

// Webhook handler for terminal events
app.post('/terminal-webhook', async (req, res) => {
  const event = req.body;
  
  switch (event.type) {
    case 'terminal.reader.action_succeeded':
      const successfulAction = event.data.object;
      await handleSuccessfulPayment(successfulAction);
      break;
      
    case 'terminal.reader.action_failed':
      const failedAction = event.data.object;
      await handleFailedPayment(failedAction);
      break;
      
    default:
      console.log(`Unhandled terminal event: ${event.type}`);
  }
  
  res.status(200).send('OK');
});

async function handleSuccessfulPayment(reader: TerminalReader) {
  if (reader.action?.type === 'process_payment_intent') {
    const paymentIntentId = reader.action.process_payment_intent?.payment_intent;
    
    // Update order status, send receipt, etc.
    await updateOrderStatus(paymentIntentId, 'paid');
    await sendCustomerReceipt(paymentIntentId);
  }
}

Stripe Terminal provides a comprehensive in-person payment processing solution with support for various reader types, flexible tipping configurations, and real-time transaction monitoring capabilities.

Install with Tessl CLI

npx tessl i tessl/npm-stripe

docs

billing.md

checkout.md

configuration.md

core-resources.md

identity.md

index.md

issuing.md

radar.md

subscriptions.md

tax.md

terminal.md

treasury.md

webhooks.md

tile.json