Skip to main content

Overview

File: src/tools/PaymentTool.ts Demonstrates payment integration using Stripe API with secure environment variable management.

Complete Code

import { LuaTool, env } from 'lua-cli';
import { z } from 'zod';

export default class CreatePaymentLinkTool implements LuaTool {
  name = "create_payment_link";
  description = "Create a payment checkout link via Stripe";
  
  inputSchema = z.object({
    amount: z.number().positive().describe("Amount in dollars"),
    currency: z.string().default('USD'),
    description: z.string()
  });

  async execute(input: z.infer<typeof this.inputSchema>) {
    // ⭐ Get API key from environment
    const stripeKey = env('STRIPE_API_KEY');
    
    // ⭐ Validate it exists
    if (!stripeKey) {
      throw new Error(
        'STRIPE_API_KEY not configured. ' +
        'Please add it to your .env file or use `lua env` for production'
      );
    }
    
    // Create Stripe checkout session
    const response = await fetch('https://api.stripe.com/v1/checkout/sessions', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${stripeKey}`,
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      body: new URLSearchParams({
        'line_items[0][price_data][currency]': input.currency.toLowerCase(),
        'line_items[0][price_data][unit_amount]': (input.amount * 100).toString(),
        'line_items[0][price_data][product_data][name]': input.description,
        'line_items[0][quantity]': '1',
        'mode': 'payment',
        'success_url': 'https://example.com/success',
        'cancel_url': 'https://example.com/cancel'
      })
    });
    
    if (!response.ok) {
      const error = await response.text();
      throw new Error(`Stripe API error: ${error}`);
    }
    
    const session = await response.json();
    
    return {
      paymentUrl: session.url,
      sessionId: session.id,
      amount: `$${input.amount.toFixed(2)}`,
      message: "Payment link created successfully"
    };
  }
}

Key Concepts

1. Environment Variables

Never hardcode API keys!
// ❌ Bad
const apiKey = 'sk_test_abc123';

// ✅ Good
import { env } from 'lua-cli';
const apiKey = env('STRIPE_API_KEY');

2. Validation

Always check environment variables exist:
const apiKey = env('STRIPE_API_KEY');

if (!apiKey) {
  throw new Error(
    'STRIPE_API_KEY not configured. ' +
    'Add it to .env file or use `lua env` for production'
  );
}

3. Error Handling

Handle external API failures gracefully:
if (!response.ok) {
  const error = await response.text();
  throw new Error(`Stripe API error: ${error}`);
}

4. Amount Conversion

Stripe uses cents, not dollars:
// Convert dollars to cents
const amountInCents = input.amount * 100;

'unit_amount': amountInCents.toString()

Setup Required

1. Get Stripe API Key

  1. Create account at https://stripe.com
  2. Go to Dashboard → Developers → API Keys
  3. Copy “Secret key” (starts with sk_test_)

2. Add to .env File

Create .env in project root:
STRIPE_API_KEY=sk_test_your_key_here

3. Test

lua test
Select create_payment_link:
  • Amount: 29.99
  • Currency: USD
  • Description: Product Purchase

Customization Ideas

Add Customer Info

inputSchema = z.object({
  amount: z.number(),
  customerEmail: z.string().email()
});

// In Stripe API call
body: {
  customer_email: input.customerEmail,
  // ... other fields
}

Add Success/Cancel URLs

inputSchema = z.object({
  amount: z.number(),
  successUrl: z.string().url(),
  cancelUrl: z.string().url()
});

// Use in API call
'success_url': input.successUrl,
'cancel_url': input.cancelUrl

Add Metadata

// Track custom data
const orderId = generateOrderId();

body: {
  metadata: {
    orderId,
    source: 'ai_chat',
    timestamp: Date.now()
  }
}

Other Payment Providers

Same pattern works for other providers:
const apiKey = env('PAYPAL_CLIENT_ID');

// PayPal API integration

Security Best Practices

# .env (development)
STRIPE_API_KEY=sk_test_abc123
# lua.skill.yaml (production)
skill:
  env:
    STRIPE_API_KEY: sk_live_xyz789
// ❌ Bad
console.log('API Key:', apiKey);

// ✅ Good
console.log('API Key configured:', !!apiKey);
if (input.amount < 0.50) {
  throw new Error('Minimum amount is $0.50');
}

if (input.amount > 999999) {
  throw new Error('Amount exceeds maximum limit');
}
# .gitignore
.env
.env.local
.env.*.local

What You’ll Learn

Environment Variables

Secure secret management

External Integration

Third-party API integration

Payment Processing

Real payment workflows

Error Handling

Graceful failure handling

Next Steps