The Stripe integration connects your Cognest assistant to the Stripe Payments and Billing APIs. Create payment links, manage customers, issue invoices, handle subscriptions, process refunds, and react to real-time payment events through Stripe webhooks. Cognest verifies webhook signatures, serializes requests to avoid race conditions, and provides typed wrappers around every Stripe resource.
sk_test_...) during development..env file:STRIPE_SECRET_KEY=sk_test_51ABC...
STRIPE_WEBHOOK_SECRET=whsec_abc123...Use test mode keys during development
Never use live mode keys (sk_live_...) in development or staging environments. Stripe test mode provides a complete sandbox with test card numbers, simulated webhooks, and no real charges.
Add the Stripe integration to your cognest.config.yaml:
integrations:
stripe:
enabled: true
credentials:
secret_key: ${STRIPE_SECRET_KEY}
webhook_secret: ${STRIPE_WEBHOOK_SECRET}
settings:
# Stripe API version to pin (recommended for stability)
api_version: "2024-12-18.acacia"
# Webhook endpoint path (Cognest registers this route automatically)
webhook_path: /webhooks/stripe
# Events to listen for (empty = all events)
events:
- payment_intent.succeeded
- payment_intent.payment_failed
- invoice.created
- invoice.payment_succeeded
- customer.subscription.created
- customer.subscription.deleted
- charge.dispute.created
# Automatic retry for failed webhook deliveries
max_retries: 3
# Currency default for new payment links
default_currency: usdimport { Cognest } from '@cognest/sdk'
const cognest = new Cognest()
const stripe = cognest.integration('stripe')
// Create a payment link
const link = await stripe.createPaymentLink({
lineItems: [{ price: 'price_1ABC123', quantity: 1 }],
metadata: { orderId: 'order_456' },
})
console.log(`Payment link: ${link.url}`)
await cognest.start()| Method | Parameters | Description |
|---|---|---|
| createPaymentLink(options) | { lineItems, metadata?, afterCompletion? } | Generate a Stripe Payment Link with one or more line items. Returns the shareable URL. |
| getCustomer(customerId) | customerId: string | Retrieve a Stripe customer by ID, including email, name, and default payment method. |
| listPayments(options?) | options?: { customerId?, status?, limit?, startingAfter? } | List payment intents with optional filters. Supports pagination. |
| createInvoice(options) | { customerId, items, daysUntilDue?, metadata? } | Create and optionally finalize a draft invoice for a customer. |
| refund(options) | { paymentIntentId, amount?, reason? } | Issue a full or partial refund for a payment intent. |
| getBalance() | (none) | Retrieve the current Stripe account balance across all currencies. |
| createSubscription(options) | { customerId, priceId, trialDays?, metadata? } | Create a new subscription for a customer with an optional trial period. |
Stripe events are delivered in real time via webhooks. Cognest verifies the Stripe-Signature header against your webhook secret to ensure authenticity. Only events listed in your config events array are forwarded to your handlers.
| Event | Payload | Description |
|---|---|---|
| payment:succeeded | { paymentIntentId, amount, currency, customerId, metadata } | Fired when a payment is successfully captured. |
| payment:failed | { paymentIntentId, amount, currency, customerId, failureReason } | Fired when a payment attempt fails. |
| invoice:created | { invoiceId, customerId, amountDue, currency, dueDate } | Fired when a new invoice is generated. |
| subscription:created | { subscriptionId, customerId, priceId, status, currentPeriodEnd } | Fired when a new subscription starts. |
| subscription:canceled | { subscriptionId, customerId, canceledAt, cancelReason } | Fired when a subscription is canceled. |
| dispute:created | { disputeId, paymentIntentId, amount, reason, status } | Fired when a customer initiates a payment dispute. |
stripe.on('payment:succeeded', async (event) => {
console.log(
`Payment ${event.paymentIntentId} succeeded: ${event.amount / 100} ${event.currency.toUpperCase()}`
)
// Send a thank-you message via another integration
const customer = await stripe.getCustomer(event.customerId)
console.log(`Customer: ${customer.email}`)
})
stripe.on('payment:failed', async (event) => {
console.error(
`Payment failed for customer ${event.customerId}: ${event.failureReason}`
)
})
stripe.on('dispute:created', async (event) => {
console.warn(
`Dispute ${event.disputeId} opened for ${event.amount / 100} — reason: ${event.reason}`
)
})
stripe.on('subscription:canceled', async (event) => {
console.log(
`Subscription ${event.subscriptionId} canceled: ${event.cancelReason ?? 'no reason provided'}`
)
})A complete example that manages SaaS subscriptions, sends payment notifications via Slack, and auto-generates monthly revenue reports:
import { Cognest } from '@cognest/sdk'
const cognest = new Cognest()
const stripe = cognest.integration('stripe')
const slack = cognest.integration('slack')
// Notify Slack on successful payments
stripe.on('payment:succeeded', async (event) => {
const customer = await stripe.getCustomer(event.customerId)
const amount = (event.amount / 100).toFixed(2)
await slack.sendMessage({
channel: '#payments',
text: `Payment received: $${amount} ${event.currency.toUpperCase()} from ${customer.email}`,
})
})
// Alert on failed payments so the team can follow up
stripe.on('payment:failed', async (event) => {
const customer = await stripe.getCustomer(event.customerId)
await slack.sendMessage({
channel: '#payments-alerts',
text: `:warning: Payment failed for ${customer.email} — ${event.failureReason}. Follow up required.`,
})
})
// Escalate disputes immediately
stripe.on('dispute:created', async (event) => {
const amount = (event.amount / 100).toFixed(2)
await slack.sendMessage({
channel: '#disputes',
text: `:rotating_light: New dispute: $${amount} — reason: ${event.reason}. Respond within 7 days.`,
})
})
// Welcome new subscribers
stripe.on('subscription:created', async (event) => {
const customer = await stripe.getCustomer(event.customerId)
await slack.sendMessage({
channel: '#new-subscribers',
text: `New subscriber: ${customer.email} on plan ${event.priceId}`,
})
})
// Weekly revenue report
cognest.skill('revenue-report', {
schedule: '0 9 * * 1', // 9 AM every Monday
handler: async () => {
const balance = await stripe.getBalance()
const payments = await stripe.listPayments({
status: 'succeeded',
limit: 100,
})
const weekTotal = payments.data
.filter((p) => {
const age = Date.now() - p.created * 1000
return age < 7 * 24 * 60 * 60 * 1000
})
.reduce((sum, p) => sum + p.amount, 0)
const availableBalance = balance.available
.map((b) => `${(b.amount / 100).toFixed(2)} ${b.currency.toUpperCase()}`)
.join(', ')
await slack.sendMessage({
channel: '#revenue',
text: [
':chart_with_upwards_trend: *Weekly Revenue Report*',
`Payments this week: $${(weekTotal / 100).toFixed(2)}`,
`Available balance: ${availableBalance}`,
`Total transactions: ${payments.data.length}`,
].join('\n'),
})
},
})
// Create a checkout skill that can be invoked by other skills
cognest.skill('create-checkout', {
handler: async ({ params }) => {
const { priceId, quantity = 1, customerEmail } = params
const link = await stripe.createPaymentLink({
lineItems: [{ price: priceId, quantity }],
metadata: { source: 'cognest-bot', customerEmail },
afterCompletion: {
type: 'redirect',
url: 'https://app.example.com/thank-you',
},
})
return { url: link.url, expiresAt: link.expiresAt }
},
})
await cognest.start()
console.log('Stripe payment automation is running...')Common Issues
Webhook signature verification failed: Ensure STRIPE_WEBHOOK_SECRET matches the signing secret from the Stripe Dashboard (starts with whsec_). If you're using the Stripe CLI for local testing, use the CLI-provided secret, not the dashboard one. "No such customer" errors: Customer IDs from test mode (cus_test_...) won't work in live mode and vice versa. Ensure your STRIPE_SECRET_KEY matches the mode you're testing. Webhook events not arriving: Check that your webhook_path matches the URL configured in Stripe. For local development, use the Stripe CLI: stripe listen --forward-to localhost:3000/webhooks/stripe. Idempotency errors on retries: Cognest automatically adds idempotency keys to create operations. If you see duplicate creation errors, check that max_retries is not set too high and that your handlers are idempotent.
Local Development with Stripe CLI
Install the Stripe CLI and run "stripe listen --forward-to localhost:3000/webhooks/stripe" to forward webhook events to your local development server. The CLI outputs a webhook signing secret that you should use as STRIPE_WEBHOOK_SECRET during local development.