@loop/commerce
Framework-agnostic commerce package with cart, checkout, payment engine, subscription lifecycle, and dunning system.
Installation
pnpm add @loop/commerceOverview
@loop/commerce extracts 47,700 lines of production-tested commerce logic from loopbio-v2 into a reusable package. Built for multi-brand e-commerce at scale.
Key modules:
- Cart & Checkout - Multi-tier cart state machine with Basis Theory integration
- Payment Engine - Stripe + Basis Theory with payment ledger and webhooks
- Subscription Engine - Lifecycle management with 27-attempt dunning system
- Order Management - Order creation, fulfillment, tracking
Subpath Exports
import { createCart, checkoutFlow } from '@loop/commerce/cart'
import { processPayment, handleWebhook } from '@loop/commerce/payment'
import { createSubscription, processDunning } from '@loop/commerce/subscription'Cart & Checkout
Creating a Cart
import { createCart } from '@loop/commerce/cart'
const result = await createCart({
customerId: 'cus_123',
items: [
{ productId: 'prod_abc', quantity: 2, price: 4900 }
],
metadata: {
source: 'web',
affiliateCode: 'PARTNER10'
}
})
if (result.ok) {
console.log('Cart created:', result.data.id)
}Checkout Flow
import { checkoutFlow } from '@loop/commerce/cart'
const result = await checkoutFlow({
cartId: 'cart_123',
paymentMethodId: 'pm_xxx',
shippingAddress: {
line1: '123 Main St',
city: 'Austin',
state: 'TX',
zip: '78701'
}
})Payment Engine
Processing Payments
import { processPayment } from '@loop/commerce/payment'
const result = await processPayment({
customerId: 'cus_123',
amount: 9900, // $99.00 in cents
currency: 'usd',
paymentMethodId: 'pm_xxx',
metadata: {
orderId: 'order_456',
subscriptionId: 'sub_789'
}
})Webhook Handling
import { handleWebhook } from '@loop/commerce/payment'
// In your API route (e.g., /api/webhooks/stripe)
export async function POST(req: Request) {
const sig = req.headers.get('stripe-signature')
const body = await req.text()
const result = await handleWebhook({
body,
signature: sig!,
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!
})
if (!result.ok) {
return new Response('Webhook error', { status: 400 })
}
return new Response('Success', { status: 200 })
}Subscription Engine
Creating Subscriptions
import { createSubscription } from '@loop/commerce/subscription'
const result = await createSubscription({
customerId: 'cus_123',
priceId: 'price_monthly',
interval: 'month',
intervalCount: 1,
metadata: {
tier: 'plus'
}
})Dunning System
The package includes a battle-tested 27-attempt dunning system over 14 days:
Four phases:
- Phase 1: 6 retries @ 4h intervals (hours 4, 8, 12, 16, 20, 24)
- Phase 2: 6 retries @ 8h intervals (hours 32, 40, 48, 56, 64, 72)
- Phase 3: 8 retries @ 12h intervals (hours 84-168)
- Phase 4: 7 retries @ 24h intervals (hours 192-336)
import { processDunning, getDunningPhase } from '@loop/commerce/subscription'
const phase = getDunningPhase(failureCount) // 0-3
const shouldEmail = shouldSendDunningEmail(failureCount)
// Process dunning (called automatically via webhook)
await processDunning({
subscriptionId: 'sub_123',
failureReason: 'insufficient_funds'
})Hard decline codes (instant cancel):
stolen_card,lost_card,fraudulentexpired_card,invalid_numberrestricted_card,card_not_supported
Environment Variables
# Stripe
STRIPE_SECRET_KEY=sk_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
# Basis Theory (tokenization)
BASIS_THEORY_API_KEY=key_xxxArchitecture
Framework-agnostic:
- Pure TypeScript functions
- No framework coupling
- Bring your own database
- Composable utilities
Production-ready:
- Full Zod schemas for type safety
- Comprehensive error handling
- Vitest test suites
- Battle-tested in loopbio-v2 (2+ years)
Migration from loopbio-v2
If you’re migrating from the loopbio-v2 implementation:
// Old (loopbio-v2)
import { createCart } from '@/lib/commerce/cart'
// New (@loop/commerce)
import { createCart } from '@loop/commerce/cart'All function signatures are identical - just change the import path.
Used By
@loop/commerce is used by applications that handle e-commerce functionality:
- @loop/my-loop-health — Cart, checkout, payment processing, and subscriptions
Related Packages
- @loop/bigcommerce - BigCommerce API integration
- @loop/notifications - Subscription lifecycle emails
- @loop/core - Result monad for error handling
Source
Extracted from loopbio-v2 production codebase: