Payments
How kreogen handles payments and billing.
kreogen uses Stripe by default for payments and billing. Implementing Stripe in your project is straightforward.
Stripe is an optional integration. If STRIPE_SECRET_KEY is not set, the stripe export will be undefined and payment webhooks will be skipped.
In-App Purchases
You can use Stripe anywhere in your app by importing the stripe object like so:
import { stripe } from '@kreogen/payments';
// ...
await stripe?.prices.list();Webhooks
kreogen ships a Stripe webhook handler at apps/api/app/webhooks/payments. It
verifies the signature, rejects a bad one with 400, and records the event id
before handling it so that a redelivery is a no-op. See
the API app for why each of those matters.
Anti-Fraud
As your app grows, you will inevitably encounter credit card fraud. Stripe Radar is enabled by default if you integrate payments using their SDK as described above. This provides a set of tools to help you detect and prevent fraud.
Stripe Radar supports more advanced anti-fraud features if you embed the Stripe JS script in every page load. This is not enabled by default in kreogen, but you can add it as follows:
1. Load Stripe.js in the app
Add the script to apps/app/app/layout.tsx, between the opening <html> and
<body> tags:
import './styles.css';
import { AnalyticsProvider } from '@kreogen/analytics/provider';
import { DesignSystemProvider } from '@kreogen/design-system';
import { fonts } from '@kreogen/design-system/lib/fonts';
import Script from 'next/script';
import type { ReactNode } from 'react';
interface RootLayoutProperties {
readonly children: ReactNode;
}
const RootLayout = ({ children }: RootLayoutProperties) => (
<html className={fonts} lang="en" suppressHydrationWarning>
<Script src="https://js.stripe.com/v3/" />
<body>
<AnalyticsProvider>
<DesignSystemProvider>{children}</DesignSystemProvider>
</AnalyticsProvider>
</body>
</html>
);
export default RootLayout;2. Do the same on the marketing site
apps/web/app/[locale]/layout.tsx needs the identical addition. Radar's signal comes
from seeing the visitor before checkout, so loading it only on the page that
takes the card defeats most of the point.
3. Allow the origin in the CSP
https://js.stripe.com must be in scriptSrc, and https://api.stripe.com in
connectSrc. Both are already grouped under STRIPE in
packages/security/proxy.ts — but if you have trimmed that policy, this is the
failure that works locally and is blocked in production, because CSP is
report-only in development. See
security headers.
4. Rate limit the checkout route
Card testing shows up as a burst of attempts from one source. Apply a limiter to whichever route creates a checkout session:
import { createRateLimiter, slidingWindow } from '@kreogen/rate-limit';
const limiter = createRateLimiter({
limiter: slidingWindow(5, '1 m'),
prefix: 'checkout',
});
const { success } = await limiter.limit(userId);
if (!success) {
throw new Error('Too many attempts. Try again shortly.');
}Fraud beyond the basics
kreogen deliberately ships no managed bot detection or IP reputation service. Those need a vendor account and a network hop on every request, and the right answer differs by project — a low-value subscription and a high-value marketplace have very different exposure.
If a project needs it, the effective options in rough order of effort are:
- Stripe Radar — already in the payment path, no new integration, and it sees signals your application cannot.
- Edge rules at your CDN or reverse proxy, where the request is cheapest to reject.
- A bot-detection vendor in middleware, if the first two prove insufficient.
Reach for these when you have evidence of abuse, not preemptively.