payment-security-clerk-billing-stripe
Implement secure payments using Clerk Billing and Stripe without ever touching card data. Use this skill when you need to set up subscription payments, handle webhooks, implement payment gating, understand PCI-DSS compliance, or integrate Stripe Checkout. Triggers include "payment", "Stripe", "Clerk Billing", "subscription", "PCI-DSS", "credit card", "payment security", "checkout", "webhook", "billing".
What this skill does
# Payment Security - Clerk Billing + Stripe
## Why We Don't Handle Payments Directly
### PCI-DSS Compliance Requirements
If you store, process, or transmit credit card data, you must comply with **Payment Card Industry Data Security Standard (PCI-DSS)**. Requirements include:
- Annual security audits ($20,000-$50,000)
- Quarterly vulnerability scans
- Secure network architecture
- Encryption of cardholder data
- Access control measures
- Regular security testing
**Small companies:** 84% fail initial PCI audit
**Ongoing compliance costs:** $50,000-$200,000 annually
### Real-World Payment Handling Failures
**Target Breach (2013):**
41 million card accounts compromised because they stored payment data and had insufficient security.
**Settlement: $18.5 million**
**Home Depot Breach (2014):**
56 million cards stolen. They were storing card data locally.
**Settlement: $17.5 million**
### The Secure Approach: Never Touch Card Data
By using Clerk Billing + Stripe, we **never see, store, or transmit** credit card data. We're not subject to PCI-DSS. Stripe is.
## Our Payment Architecture
### What Happens (What DOESN'T Happen)
**User subscribes:**
1. Frontend shows Clerk's `PricingTable` component
2. User clicks subscribe → Clerk opens Stripe Checkout
3. User enters card → **Stripe's servers (not ours)**
4. Stripe processes payment → **Stripe's servers (not ours)**
5. Stripe notifies Clerk → Webhook (verified by Clerk)
6. Clerk updates subscription status
7. Clerk notifies Convex → Webhook to our database
8. Our app reads subscription status → Grants access
### What Never Touches Our Servers
- ❌ Credit card numbers
- ❌ CVV codes
- ❌ Expiration dates
- ❌ Billing addresses (unless user separately provides)
### What We Store
- ✅ Subscription status (free/basic/pro)
- ✅ Subscription start date
- ✅ Customer ID (Stripe's internal ID, not card info)
### This Architecture Means
- We're **NOT subject to PCI-DSS** (Stripe is)
- We **can't leak card data** (we never have it)
- Stripe handles **fraud detection**
- Stripe handles **3D Secure**
- Clerk handles **webhook security**
## Implementation Files
- `components/custom-clerk-pricing.tsx` - Pricing table component
- `app/dashboard/payment-gated/page.tsx` - Example of subscription gating
- `convex/http.ts` - Webhook receiver (signature verified by Svix)
## Setting Up Clerk Billing
### 1. Configure in Clerk Dashboard
1. Go to Clerk Dashboard → Billing
2. Connect Stripe account
3. Create subscription plans (Free, Basic, Pro)
4. Copy Clerk Billing publishable key
### 2. Environment Variables
```bash
# .env.local
# Clerk Billing
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...
# Stripe (automatically configured by Clerk Billing)
# No manual Stripe keys needed!
# Webhook signing secret (from Clerk)
CLERK_WEBHOOK_SECRET=whsec_...
```
### 3. Add Pricing Table Component
```typescript
// components/custom-clerk-pricing.tsx
'use client';
import { PricingTable } from '@clerk/clerk-react';
export function CustomClerkPricing() {
return (
<div className="pricing-container">
<h1>Choose Your Plan</h1>
<PricingTable
appearance={{
elements: {
card: 'border rounded-lg p-6',
cardActive: 'border-blue-500',
button: 'bg-blue-600 hover:bg-blue-700 text-white',
}
}}
/>
</div>
);
}
```
## Checking Subscription Status
### Server-Side (API Routes)
```typescript
// app/api/premium-feature/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { auth } from '@clerk/nextjs/server';
import { handleUnauthorizedError, handleForbiddenError } from '@/lib/errorHandler';
export async function GET(request: NextRequest) {
const { userId, sessionClaims } = await auth();
if (!userId) {
return handleUnauthorizedError();
}
// Check subscription status from Clerk
const plan = sessionClaims?.metadata?.plan as string;
if (plan === 'free_user') {
return handleForbiddenError('Premium subscription required');
}
// User has paid subscription
return NextResponse.json({
message: 'Welcome to premium feature!',
plan: plan
});
}
```
### Client-Side (Components)
```typescript
'use client';
import { Protect } from '@clerk/nextjs';
import Link from 'next/link';
export function PremiumFeature() {
return (
<Protect
condition={(has) => !has({ plan: "free_user" })}
fallback={<UpgradePrompt />}
>
<div>
{/* Premium feature content */}
<h2>Premium Feature</h2>
<p>This content is only visible to paid subscribers</p>
</div>
</Protect>
);
}
function UpgradePrompt() {
return (
<div className="upgrade-prompt">
<h3>Upgrade to Premium</h3>
<p>This feature is available on our paid plans</p>
<Link href="/pricing">
<button>View Pricing</button>
</Link>
</div>
);
}
```
## Complete Payment-Gated Page Example
```typescript
// app/dashboard/payment-gated/page.tsx
'use client';
import { Protect } from '@clerk/nextjs';
import { CustomClerkPricing } from '@/components/custom-clerk-pricing';
export default function PaymentGatedPage() {
return (
<div>
<Protect
condition={(has) => !has({ plan: "free_user" })}
fallback={
<div className="upgrade-required">
<h1>Premium Access Required</h1>
<p>Subscribe to access this page</p>
<CustomClerkPricing />
</div>
}
>
<div className="premium-content">
<h1>Premium Dashboard</h1>
<p>Welcome to the premium features!</p>
{/* Premium features here */}
</div>
</Protect>
</div>
);
}
```
## Webhook Handling
### Clerk Webhook (User & Subscription Events)
```typescript
// app/api/webhooks/clerk/route.ts
import { Webhook } from 'svix';
import { headers } from 'next/headers';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const WEBHOOK_SECRET = process.env.CLERK_WEBHOOK_SECRET;
if (!WEBHOOK_SECRET) {
throw new Error('Missing CLERK_WEBHOOK_SECRET');
}
// Get webhook headers
const headerPayload = headers();
const svix_id = headerPayload.get("svix-id");
const svix_timestamp = headerPayload.get("svix-timestamp");
const svix_signature = headerPayload.get("svix-signature");
if (!svix_id || !svix_timestamp || !svix_signature) {
return new Response('Missing svix headers', { status: 400 });
}
const payload = await request.json();
const body = JSON.stringify(payload);
// Verify webhook signature
const wh = new Webhook(WEBHOOK_SECRET);
let evt: any;
try {
evt = wh.verify(body, {
"svix-id": svix_id,
"svix-timestamp": svix_timestamp,
"svix-signature": svix_signature,
});
} catch (err) {
console.error('Webhook verification failed:', err);
return new Response('Invalid signature', { status: 400 });
}
const { id, type, data } = evt;
// Handle subscription events
switch (type) {
case 'subscription.created':
await handleSubscriptionCreated(data);
break;
case 'subscription.updated':
await handleSubscriptionUpdated(data);
break;
case 'subscription.deleted':
await handleSubscriptionDeleted(data);
break;
case 'user.created':
await handleUserCreated(data);
break;
case 'user.updated':
await handleUserUpdated(data);
break;
}
return new Response('', { status: 200 });
}
async function handleSubscriptionCreated(data: any) {
const { user_id, plan, stripe_customer_id } = data;
// Store subscription in database
await db.subscriptions.create({
userId: user_id,
plan: plan,
stripeCustomerId: stripe_customer_id,
status: 'active',
createdAt: Date.now()
});
// Update user metadata
await db.users.update(
{ clerkId: user_id },
{ plan: plan, uRelated in Security
mac-ops
IncludedComprehensive macOS workstation operations — diagnose kernel panics, identify failing drives, audit launchd startup items, decode wake reasons, triage TCC permission denials, manage APFS snapshots, recover from no-boot. Use for: Mac is slow, slow bootup, won't boot, kernel panic, kernel_task hot, mds_stores CPU, photoanalysisd, cloudd, login loop, gray screen, sleep wake failure, drive failing, IO errors, APFS snapshots eating space, Time Machine local snapshots, Spotlight indexing, launchd, LaunchAgent, LaunchDaemon, login items, TCC permissions, Full Disk Access, Screen Recording denied, Gatekeeper, quarantine, com.apple.quarantine, app is damaged, helper tool, /Library/PrivilegedHelperTools, pmset, wake reasons, dark wake, sysdiagnose, panic.ips, DiagnosticReports, configuration profile, MDM profile, remote diagnostics over SSH.
a11y-audit
IncludedRun accessibility audits on web projects combining automated scanning (axe-core, Lighthouse) with WCAG 2.1 AA compliance mapping, manual check guidance, and structured reporting. Output is configurable: markdown report only, markdown plus machine-readable JSON, or markdown plus issue tracker integration. Use this skill whenever the user mentions "accessibility audit", "a11y audit", "WCAG audit", "accessibility check", "compliance scan", or asks to check a web project for accessibility issues. Also trigger when the user wants to verify WCAG conformance or map findings to a specific standard (CAN-ASC-6.2, EN 301 549, ADA/AODA).
erpclaw
IncludedAI-native ERP system with self-extending OS. Full accounting, invoicing, inventory, purchasing, tax, billing, HR, payroll, advanced accounting (ASC 606/842, intercompany, consolidation), and financial reporting. 413 actions across 14 domains, 43 expansion modules. Constitutional guardrails, adversarial audit, schema migration. Double-entry GL, immutable audit trail, US GAAP.
assess
IncludedAssesses and rates quality 0-10 across multiple dimensions (correctness, maintainability, security, performance, testability, simplicity) with pros/cons analysis. Compares against project conventions and prior decisions from memory. Produces structured evaluation reports with actionable improvement suggestions. Use when evaluating code, designs, architectures, or comparing alternative approaches.
spring-boot-security-jwt
IncludedProvides JWT authentication and authorization patterns for Spring Boot 3.5.x covering token generation with JJWT, Bearer/cookie authentication, database/OAuth2 integration, and RBAC/permission-based access control using Spring Security 6.x. Use when implementing authentication or authorization in Spring Boot applications.
code-hardcode-audit
IncludedDetect hardcoded values, magic numbers, and leaked secrets. TRIGGERS - hardcode audit, magic numbers, PLR2004, secret scanning.