# @prood/commerce (https://prood.com/docs/packages/commerce)

`@prood/commerce` is the **server-only data layer** used by `apps/api`. It wraps the configured `CommerceAdapter`, instantiates payment and storage providers, handles tenant scoping, and manages Next.js cache tags.

## Installation [#installation]

Workspace package. Available from the source repo for now.

This package is `"server-only"` — it cannot be imported in client components.

## Usage [#usage]

Only `apps/api` should import `@prood/commerce` directly. Other apps use `@prood/api-client`.

```ts
import {
  getProducts,
  createCart,
  placeOrder,
  getAdmin,
  withTenant,
  getTenantPaymentProvider,
  verifyPaymentWebhook,
} from '@prood/commerce'
```

## Catalog functions [#catalog-functions]

```ts
// Cached with per-tenant tags
const products = await getProducts(orgId, { query: 'shirt', page: 1, limit: 20 })
const product = await getProduct(orgId, 'prod_abc')
const categories = await getCategories(orgId)
const store = await getStoreInfo(orgId)
```

Cache tags: `products-{orgId}`, `categories-{orgId}`, `store-{orgId}`.

## Cart functions [#cart-functions]

```ts
const cart = await createCart(orgId)
const updated = await addCartItem(orgId, cartId, { productId, variantId, quantity: 1 })
await applyCartCoupon(orgId, cartId, 'SAVE10') // API and MCP only: stores have no coupon field
const order = await placeOrder(orgId, cartId)
```

## Admin functions [#admin-functions]

```ts
const admin = getAdmin(orgId)
const products = await admin.listProducts({ page: 1 })
const created = await admin.createProduct(input)
await admin.fulfillOrder(orderId, { trackingNumber: 'TRK123' })
await admin.recordRefund(orderId, 'Customer request') // records a refund already sent; moves no money
const stats = await admin.getDashboardStats()
```

## Payment providers [#payment-providers]

```ts
// Build provider with the tenant's stored config
const { provider } = await getTenantPaymentProvider(orgId, 'stripe')

// Verify webhook with tenant's stored secret
const result = await verifyPaymentWebhook(payload, signature, 'stripe', orgId)
```

The provider factory reads from the encrypted `integration_config` table. There are no environment-level provider defaults: a store with no stored credentials has no provider.

## Storage [#storage]

```ts
import { uploadForTenant, tenantStorageDirectory } from '@prood/commerce'

const result = await uploadForTenant(orgId, {
  file: buffer,
  filename: 'product-image.jpg',
  contentType: 'image/jpeg',
  directory: tenantStorageDirectory(orgId, 'products', productId),
})
// result.url → CDN URL
// result.key → org/{orgId}/products/{productId}/...
```

## Encryption [#encryption]

Integration credentials are encrypted at rest:

```ts
import { encryptConfig, decryptConfig } from '@prood/commerce'

const encrypted = encryptConfig({ secretKey: 'sk_live_...' })
// → { secretKey: 'enc:v1:...' }

const decrypted = decryptConfig(encrypted)
// → { secretKey: 'sk_live_...' }
```

Uses AES-256-GCM with `INTEGRATION_ENCRYPTION_KEY`. The key is required.

## Tenant scoping [#tenant-scoping]

All functions accept `orgId` as the first parameter and call `withTenant()` internally:

```ts
export async function getProducts(orgId: string, params: SearchParams) {
  return withTenant(orgId, () => adapter.getProducts(params))
}
```

## Configuration [#configuration]

Read via `getCommerceConfig()`:

| Setting | Env var | Default |
| --- | --- | --- |
| Database | `DATABASE_URL` | None. Required |

## Module structure [#module-structure]

```mermaid
flowchart TB
  IDX[index.ts]
  ENV[env.ts]
  ADAPTER[adapter.ts]
  CATALOG[catalog.ts]
  CART[cart.ts]
  CHECKOUT[checkout.ts]
  ORDERS[orders.ts]
  ADMIN[admin.ts]
  PAYMENTS[payments.ts]
  INTEGRATIONS[integrations.ts]
  CRYPTO[crypto.ts]
  STORAGE[storage.ts]
  ERRORS[errors.ts]
  REVAL[revalidate.ts]

  IDX --> ENV & ADAPTER & CATALOG & CART & CHECKOUT & ORDERS & ADMIN & PAYMENTS & INTEGRATIONS & CRYPTO & STORAGE & ERRORS & REVAL
```

## Related pages [#related-pages]

<Cards>
  <Card title="@prood/platform" href="/docs/packages/platform" description="Underlying Postgres engine." />
  <Card title="Commerce API" href="/docs/apps/api" description="HTTP layer consuming this package." />
  <Card title="Payment providers" href="/docs/packages/payments/stripe" description="Payment provider packages." />
</Cards>
