Sales Block

Complete commerce infrastructure for subscriptions and payments
The Sales Block provides everything you need to monetize your application - from shopping carts and one-time purchases to subscription billing and payment processing. Integrated with Stripe out of the box.
Features
- Shopping Carts - Persistent carts with line items, discounts, taxes, and inventory validation
- Order Management - Complete order lifecycle from creation to fulfillment
- Subscriptions - Recurring billing with plans, trials, upgrades, and usage-based pricing
- Payment Processing - Secure payments with Stripe, multiple methods, automatic retries
- Invoicing - Automated invoice generation, PDF export, payment reminders
- Checkout Flow - Customizable checkout with guest support, addresses, and tax calculation
API Endpoint
| Service | URL |
|---|---|
| Sales | https://sales.api.us.23blocks.com |
Environment Routing: Use the same URL for all environments. Your API key determines the environment:
pk_test_*/sk_test_*→ Routes to Stagingpk_live_*/sk_live_*→ Routes to Production
Quick Start
Installation
npm install @23blocks/sdk
Initialize the Client
import { create23BlocksClient } from '@23blocks/sdk';
const client = create23BlocksClient({
urls: { sales: 'https://sales.api.us.23blocks.com' },
apiKey: 'your-api-key', // Use pk_test_* for staging, pk_live_* for production
});
Create a Shopping Cart
// Create a new cart
const cart = await client.sales.createCart({
userId: 'user-123',
currency: 'USD'
});
// Add items to cart
await client.sales.addCartItem(cart.id, {
productId: 'prod-abc',
quantity: 2,
unitPrice: 29.99
});
// Apply a discount
await client.sales.applyDiscount(cart.id, {
code: 'SAVE20'
});
Create a Subscription
const subscription = await client.sales.createSubscription({
userId: 'user-123',
planId: 'pro-monthly',
paymentMethodId: 'pm_xxx',
trialDays: 14
});
Authentication
All Sales Block endpoints require authentication via API key and App ID headers:
X-App-Id: your-app-id
X-Api-Key: your-api-key
Content-Type: application/json
User-scoped operations also require user authentication:
Authorization: Bearer <user-token>
Shopping Carts
Shopping carts manage the customer's items before checkout, handling inventory validation, pricing, taxes, and discounts.
Cart Object
{
"data": {
"id": "cart-uuid",
"type": "carts",
"attributes": {
"user_id": "user-123",
"status": "active",
"currency": "USD",
"subtotal": 89.97,
"discount_amount": 17.99,
"tax_amount": 5.76,
"total": 77.74,
"item_count": 3,
"metadata": {},
"expires_at": "2026-01-09T12:00:00Z",
"created_at": "2026-01-02T12:00:00Z",
"updated_at": "2026-01-02T12:30:00Z"
},
"relationships": {
"items": {
"data": [
{ "type": "cart_items", "id": "item-1" },
{ "type": "cart_items", "id": "item-2" }
]
},
"discount": {
"data": { "type": "discounts", "id": "discount-1" }
}
}
}
}
Create Cart
POST /api/v1/sales/carts
Request Body:
{
"data": {
"type": "carts",
"attributes": {
"user_id": "user-123",
"currency": "USD",
"metadata": {
"source": "web",
"campaign": "summer-sale"
}
}
}
}
Response: 201 Created
Get Cart
GET /api/v1/sales/carts/:cart_id
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
include | string | Include related resources: items, discount, user |
Response: 200 OK with cart object
Update Cart
PATCH /api/v1/sales/carts/:cart_id
Request Body:
{
"data": {
"type": "carts",
"attributes": {
"currency": "EUR",
"metadata": {
"notes": "Gift order"
}
}
}
}
Delete Cart
DELETE /api/v1/sales/carts/:cart_id
Response: 204 No Content
List User Carts
GET /api/v1/sales/users/:user_id/carts
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status: active, converted, abandoned, expired |
page[number] | integer | Page number (default: 1) |
page[size] | integer | Items per page (default: 20, max: 100) |
Cart Items
Cart Item Object
{
"data": {
"id": "item-uuid",
"type": "cart_items",
"attributes": {
"cart_id": "cart-123",
"product_id": "prod-abc",
"product_name": "Pro Subscription",
"quantity": 1,
"unit_price": 29.99,
"subtotal": 29.99,
"metadata": {},
"created_at": "2026-01-02T12:00:00Z"
}
}
}
Add Item to Cart
POST /api/v1/sales/carts/:cart_id/items
Request Body:
{
"data": {
"type": "cart_items",
"attributes": {
"product_id": "prod-abc",
"quantity": 2,
"unit_price": 29.99,
"metadata": {
"variant": "blue",
"size": "large"
}
}
}
}
Response: 201 Created
Update Cart Item
PATCH /api/v1/sales/carts/:cart_id/items/:item_id
Request Body:
{
"data": {
"type": "cart_items",
"attributes": {
"quantity": 3
}
}
}
Remove Cart Item
DELETE /api/v1/sales/carts/:cart_id/items/:item_id
Response: 204 No Content
Apply Discount to Cart
POST /api/v1/sales/carts/:cart_id/apply-discount
Request Body:
{
"code": "SUMMER20"
}
Response: 200 OK with updated cart
Remove Discount from Cart
DELETE /api/v1/sales/carts/:cart_id/discount
Response: 204 No Content
Orders
Orders represent completed purchases with full lifecycle management.
Order Object
{
"data": {
"id": "order-uuid",
"type": "orders",
"attributes": {
"order_number": "ORD-2026-00001",
"user_id": "user-123",
"status": "confirmed",
"fulfillment_status": "pending",
"payment_status": "paid",
"currency": "USD",
"subtotal": 89.97,
"discount_amount": 17.99,
"shipping_amount": 9.99,
"tax_amount": 5.76,
"total": 87.73,
"billing_address": {
"name": "John Doe",
"line1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102",
"country": "US"
},
"shipping_address": {
"name": "John Doe",
"line1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102",
"country": "US"
},
"metadata": {},
"notes": "Leave at door",
"created_at": "2026-01-02T12:00:00Z",
"updated_at": "2026-01-02T12:00:00Z"
},
"relationships": {
"line_items": {
"data": [
{ "type": "order_items", "id": "item-1" }
]
},
"payment": {
"data": { "type": "payments", "id": "pay-1" }
},
"invoice": {
"data": { "type": "invoices", "id": "inv-1" }
}
}
}
}
Order Statuses
| Status | Description |
|---|---|
pending | Order created, awaiting payment |
confirmed | Payment received, order confirmed |
processing | Order is being processed |
shipped | Order has been shipped |
delivered | Order delivered to customer |
cancelled | Order was cancelled |
refunded | Order was refunded |
Create Order
POST /api/v1/sales/orders
Request Body:
{
"data": {
"type": "orders",
"attributes": {
"user_id": "user-123",
"cart_id": "cart-456",
"payment_method_id": "pm_xxx",
"billing_address": {
"name": "John Doe",
"line1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102",
"country": "US"
},
"shipping_address": {
"name": "John Doe",
"line1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102",
"country": "US"
},
"notes": "Leave at door"
}
}
}
Response: 201 Created
Get Order
GET /api/v1/sales/orders/:order_id
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
include | string | Include: line_items, payment, invoice, user |
List Orders
GET /api/v1/sales/orders
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
filter[user_id] | string | Filter by user ID |
filter[status] | string | Filter by status |
filter[payment_status] | string | Filter: pending, paid, failed, refunded |
filter[fulfillment_status] | string | Filter: pending, processing, shipped, delivered |
filter[created_after] | datetime | Orders created after date |
filter[created_before] | datetime | Orders created before date |
sort | string | Sort by: created_at, -created_at, total, -total |
page[number] | integer | Page number |
page[size] | integer | Items per page |
Update Order Status
PATCH /api/v1/sales/orders/:order_id/status
Request Body:
{
"status": "shipped",
"tracking_number": "1Z999AA10123456784",
"carrier": "UPS"
}
Cancel Order
POST /api/v1/sales/orders/:order_id/cancel
Request Body:
{
"reason": "Customer requested cancellation"
}
Subscriptions
Manage recurring billing with flexible subscription plans.
Subscription Object
{
"data": {
"id": "sub-uuid",
"type": "subscriptions",
"attributes": {
"user_id": "user-123",
"plan_id": "plan-pro",
"status": "active",
"current_period_start": "2026-01-02T00:00:00Z",
"current_period_end": "2026-02-02T00:00:00Z",
"trial_start": "2025-12-19T00:00:00Z",
"trial_end": "2026-01-02T00:00:00Z",
"cancel_at_period_end": false,
"cancelled_at": null,
"ended_at": null,
"quantity": 1,
"metadata": {},
"created_at": "2025-12-19T12:00:00Z",
"updated_at": "2026-01-02T12:00:00Z"
},
"relationships": {
"plan": {
"data": { "type": "plans", "id": "plan-pro" }
},
"payment_method": {
"data": { "type": "payment_methods", "id": "pm-1" }
}
}
}
}
Subscription Statuses
| Status | Description |
|---|---|
trialing | Subscription is in trial period |
active | Subscription is active and billing |
past_due | Payment failed, retrying |
paused | Subscription temporarily paused |
cancelled | Subscription cancelled |
unpaid | Payment failed after all retries |
Create Subscription
POST /api/v1/sales/subscriptions
Request Body:
{
"data": {
"type": "subscriptions",
"attributes": {
"user_id": "user-123",
"plan_id": "plan-pro",
"payment_method_id": "pm_xxx",
"trial_days": 14,
"quantity": 5,
"metadata": {
"team_name": "Engineering"
}
}
}
}
Response: 201 Created
Get Subscription
GET /api/v1/sales/subscriptions/:subscription_id
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
include | string | Include: plan, payment_method, invoices, user |
List Subscriptions
GET /api/v1/sales/subscriptions
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
filter[user_id] | string | Filter by user |
filter[plan_id] | string | Filter by plan |
filter[status] | string | Filter by status |
page[number] | integer | Page number |
page[size] | integer | Items per page |
Update Subscription
PATCH /api/v1/sales/subscriptions/:subscription_id
Request Body:
{
"data": {
"type": "subscriptions",
"attributes": {
"plan_id": "plan-enterprise",
"quantity": 10,
"proration_behavior": "create_prorations"
}
}
}
Proration Behaviors:
| Value | Description |
|---|---|
create_prorations | Credit unused time, charge for new plan |
none | No proration, change at next billing cycle |
always_invoice | Invoice immediately for the change |
Pause Subscription
POST /api/v1/sales/subscriptions/:subscription_id/pause
Request Body:
{
"resume_at": "2026-03-01T00:00:00Z"
}
Resume Subscription
POST /api/v1/sales/subscriptions/:subscription_id/resume
Cancel Subscription
POST /api/v1/sales/subscriptions/:subscription_id/cancel
Request Body:
{
"cancel_at_period_end": true,
"reason": "Too expensive",
"feedback": "Will reconsider when budget allows"
}
Reactivate Subscription
Reactivate a subscription that was scheduled for cancellation:
POST /api/v1/sales/subscriptions/:subscription_id/reactivate
Subscription Plans
Define your pricing tiers and billing configurations.
Plan Object
{
"data": {
"id": "plan-uuid",
"type": "plans",
"attributes": {
"name": "Pro Plan",
"description": "For growing teams",
"product_id": "prod-123",
"currency": "USD",
"amount": 4999,
"interval": "month",
"interval_count": 1,
"trial_period_days": 14,
"usage_type": "licensed",
"billing_scheme": "per_unit",
"features": [
"Unlimited projects",
"Priority support",
"Advanced analytics"
],
"metadata": {},
"active": true,
"created_at": "2026-01-01T00:00:00Z"
}
}
}
Billing Intervals
| Interval | Description |
|---|---|
day | Daily billing |
week | Weekly billing |
month | Monthly billing |
year | Annual billing |
Usage Types
| Type | Description |
|---|---|
licensed | Fixed quantity billing (per seat) |
metered | Usage-based billing |
Create Plan
POST /api/v1/sales/plans
Request Body:
{
"data": {
"type": "plans",
"attributes": {
"name": "Enterprise",
"description": "For large organizations",
"product_id": "prod-123",
"currency": "USD",
"amount": 19999,
"interval": "month",
"interval_count": 1,
"trial_period_days": 30,
"usage_type": "licensed",
"features": [
"Unlimited everything",
"24/7 support",
"Custom integrations",
"SLA guarantee"
]
}
}
}
List Plans
GET /api/v1/sales/plans
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
filter[active] | boolean | Filter active/inactive plans |
filter[product_id] | string | Filter by product |
filter[interval] | string | Filter by billing interval |
Update Plan
PATCH /api/v1/sales/plans/:plan_id
Updating a plan's price does not affect existing subscriptions. Create a new plan for new pricing.
Archive Plan
POST /api/v1/sales/plans/:plan_id/archive
Usage Records (Metered Billing)
Track usage for metered subscriptions.
Usage Record Object
{
"data": {
"id": "usage-uuid",
"type": "usage_records",
"attributes": {
"subscription_id": "sub-123",
"quantity": 1500,
"action": "increment",
"timestamp": "2026-01-02T12:00:00Z",
"metadata": {
"api_calls": 1000,
"storage_gb": 5
}
}
}
}
Report Usage
POST /api/v1/sales/subscriptions/:subscription_id/usage
Request Body:
{
"data": {
"type": "usage_records",
"attributes": {
"quantity": 500,
"action": "increment",
"timestamp": "2026-01-02T12:00:00Z",
"metadata": {
"feature": "api_calls"
}
}
}
}
Actions:
| Action | Description |
|---|---|
increment | Add to current usage |
set | Override current usage |
Get Usage Summary
GET /api/v1/sales/subscriptions/:subscription_id/usage/summary
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
period_start | datetime | Start of period |
period_end | datetime | End of period |
Payments
Process payments securely with Stripe integration.
Payment Object
{
"data": {
"id": "pay-uuid",
"type": "payments",
"attributes": {
"amount": 4999,
"currency": "USD",
"status": "succeeded",
"payment_method_type": "card",
"card_brand": "visa",
"card_last4": "4242",
"stripe_payment_intent_id": "pi_xxx",
"order_id": "order-123",
"subscription_id": null,
"invoice_id": "inv-123",
"failure_code": null,
"failure_message": null,
"metadata": {},
"created_at": "2026-01-02T12:00:00Z"
}
}
}
Payment Statuses
| Status | Description |
|---|---|
pending | Payment initiated |
processing | Payment being processed |
succeeded | Payment successful |
failed | Payment failed |
cancelled | Payment cancelled |
refunded | Payment refunded |
partially_refunded | Partial refund issued |
Create Payment
POST /api/v1/sales/payments
Request Body:
{
"data": {
"type": "payments",
"attributes": {
"amount": 9999,
"currency": "USD",
"payment_method_id": "pm_xxx",
"order_id": "order-123",
"metadata": {
"description": "Order payment"
}
}
}
}
Get Payment
GET /api/v1/sales/payments/:payment_id
List Payments
GET /api/v1/sales/payments
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
filter[user_id] | string | Filter by user |
filter[status] | string | Filter by status |
filter[order_id] | string | Filter by order |
filter[subscription_id] | string | Filter by subscription |
filter[created_after] | datetime | Payments after date |
filter[created_before] | datetime | Payments before date |
sort | string | Sort field |
page[number] | integer | Page number |
page[size] | integer | Items per page |
Refund Payment
POST /api/v1/sales/payments/:payment_id/refund
Request Body:
{
"amount": 2500,
"reason": "customer_request",
"metadata": {
"refund_reason": "Product not as described"
}
}
Refund Reasons:
| Reason | Description |
|---|---|
duplicate | Duplicate charge |
fraudulent | Fraudulent transaction |
customer_request | Customer requested refund |
Payment Methods
Manage customer payment methods.
Payment Method Object
{
"data": {
"id": "pm-uuid",
"type": "payment_methods",
"attributes": {
"user_id": "user-123",
"type": "card",
"card": {
"brand": "visa",
"last4": "4242",
"exp_month": 12,
"exp_year": 2028,
"funding": "credit"
},
"billing_details": {
"name": "John Doe",
"email": "john@example.com"
},
"is_default": true,
"stripe_payment_method_id": "pm_xxx",
"created_at": "2026-01-02T12:00:00Z"
}
}
}
Add Payment Method
POST /api/v1/sales/payment-methods
Request Body:
{
"data": {
"type": "payment_methods",
"attributes": {
"user_id": "user-123",
"stripe_payment_method_id": "pm_xxx",
"set_as_default": true
}
}
}
List User Payment Methods
GET /api/v1/sales/users/:user_id/payment-methods
Set Default Payment Method
POST /api/v1/sales/payment-methods/:payment_method_id/set-default
Delete Payment Method
DELETE /api/v1/sales/payment-methods/:payment_method_id
Invoices
Automated invoice generation and management.
Invoice Object
{
"data": {
"id": "inv-uuid",
"type": "invoices",
"attributes": {
"invoice_number": "INV-2026-00001",
"user_id": "user-123",
"subscription_id": "sub-123",
"status": "paid",
"currency": "USD",
"subtotal": 4999,
"discount_amount": 0,
"tax_amount": 400,
"total": 5399,
"amount_paid": 5399,
"amount_due": 0,
"billing_reason": "subscription_cycle",
"period_start": "2026-01-02T00:00:00Z",
"period_end": "2026-02-02T00:00:00Z",
"due_date": "2026-01-16T00:00:00Z",
"paid_at": "2026-01-02T12:00:00Z",
"pdf_url": "https://api.23blocks.com/invoices/inv-uuid.pdf",
"hosted_invoice_url": "https://pay.23blocks.com/inv-uuid",
"metadata": {},
"created_at": "2026-01-02T00:00:00Z"
},
"relationships": {
"line_items": {
"data": [
{ "type": "invoice_items", "id": "ii-1" }
]
},
"payment": {
"data": { "type": "payments", "id": "pay-1" }
}
}
}
}
Invoice Statuses
| Status | Description |
|---|---|
draft | Invoice being prepared |
open | Invoice sent, awaiting payment |
paid | Invoice paid in full |
void | Invoice voided |
uncollectible | Invoice marked uncollectible |
Get Invoice
GET /api/v1/sales/invoices/:invoice_id
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
include | string | Include: line_items, payment, subscription |
List Invoices
GET /api/v1/sales/invoices
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
filter[user_id] | string | Filter by user |
filter[subscription_id] | string | Filter by subscription |
filter[status] | string | Filter by status |
filter[due_after] | datetime | Invoices due after date |
filter[due_before] | datetime | Invoices due before date |
sort | string | Sort field |
page[number] | integer | Page number |
page[size] | integer | Items per page |
Download Invoice PDF
GET /api/v1/sales/invoices/:invoice_id/pdf
Response: PDF file download
Send Invoice
POST /api/v1/sales/invoices/:invoice_id/send
Sends the invoice to the customer via email.
Void Invoice
POST /api/v1/sales/invoices/:invoice_id/void
Create Manual Invoice
POST /api/v1/sales/invoices
Request Body:
{
"data": {
"type": "invoices",
"attributes": {
"user_id": "user-123",
"due_date": "2026-01-16T00:00:00Z",
"line_items": [
{
"description": "Consulting services",
"quantity": 10,
"unit_price": 15000
},
{
"description": "Setup fee",
"quantity": 1,
"unit_price": 50000
}
],
"metadata": {
"project": "Website redesign"
}
}
}
}
Discounts & Coupons
Create promotional codes and discounts.
Discount Object
{
"data": {
"id": "disc-uuid",
"type": "discounts",
"attributes": {
"code": "SUMMER20",
"name": "Summer Sale 20% Off",
"type": "percentage",
"value": 20,
"currency": "USD",
"max_redemptions": 1000,
"redemptions_count": 456,
"applies_to": "all",
"minimum_amount": 5000,
"valid_from": "2026-06-01T00:00:00Z",
"valid_until": "2026-08-31T23:59:59Z",
"active": true,
"created_at": "2026-05-01T00:00:00Z"
}
}
}
Discount Types
| Type | Description |
|---|---|
percentage | Percentage off (e.g., 20% off) |
fixed_amount | Fixed amount off (e.g., $10 off) |
Create Discount
POST /api/v1/sales/discounts
Request Body:
{
"data": {
"type": "discounts",
"attributes": {
"code": "WELCOME10",
"name": "Welcome Discount",
"type": "percentage",
"value": 10,
"max_redemptions": 500,
"applies_to": "first_order",
"minimum_amount": 2500,
"valid_until": "2026-12-31T23:59:59Z"
}
}
}
Validate Discount Code
POST /api/v1/sales/discounts/validate
Request Body:
{
"code": "SUMMER20",
"cart_id": "cart-123",
"user_id": "user-123"
}
Response:
{
"valid": true,
"discount": {
"type": "percentage",
"value": 20,
"calculated_amount": 1799
}
}
List Discounts
GET /api/v1/sales/discounts
Deactivate Discount
POST /api/v1/sales/discounts/:discount_id/deactivate
Checkout
Complete checkout flow from cart to order.
Create Checkout Session
POST /api/v1/sales/checkout/sessions
Request Body:
{
"data": {
"type": "checkout_sessions",
"attributes": {
"cart_id": "cart-123",
"success_url": "https://yourapp.com/success?session={session_id}",
"cancel_url": "https://yourapp.com/cart",
"mode": "payment",
"allow_guest_checkout": true,
"collect_shipping_address": true,
"shipping_options": [
{
"id": "standard",
"name": "Standard Shipping",
"amount": 999,
"delivery_estimate": "5-7 business days"
},
{
"id": "express",
"name": "Express Shipping",
"amount": 2499,
"delivery_estimate": "2-3 business days"
}
]
}
}
}
Checkout Modes:
| Mode | Description |
|---|---|
payment | One-time payment |
subscription | Recurring subscription |
setup | Setup payment method only |
Response:
{
"data": {
"id": "cs-uuid",
"type": "checkout_sessions",
"attributes": {
"url": "https://checkout.23blocks.com/cs-uuid",
"expires_at": "2026-01-02T13:00:00Z",
"status": "open"
}
}
}
Get Checkout Session
GET /api/v1/sales/checkout/sessions/:session_id
Complete Checkout (API)
For custom checkout UIs:
POST /api/v1/sales/checkout/complete
Request Body:
{
"data": {
"type": "checkout",
"attributes": {
"cart_id": "cart-123",
"payment_method_id": "pm_xxx",
"billing_address": {
"name": "John Doe",
"line1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102",
"country": "US"
},
"shipping_address": {
"name": "John Doe",
"line1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102",
"country": "US"
},
"shipping_option_id": "express",
"discount_code": "SAVE20",
"email": "john@example.com"
}
}
}
Response: 201 Created with order object
Calculate Taxes
POST /api/v1/sales/checkout/calculate-taxes
Request Body:
{
"cart_id": "cart-123",
"shipping_address": {
"postal_code": "94102",
"country": "US",
"state": "CA"
}
}
Response:
{
"subtotal": 8997,
"tax_amount": 742,
"tax_rate": 8.25,
"tax_breakdown": [
{ "name": "State Tax", "rate": 6.0, "amount": 540 },
{ "name": "County Tax", "rate": 1.25, "amount": 112 },
{ "name": "City Tax", "rate": 1.0, "amount": 90 }
],
"total": 9739
}
Webhooks
Receive real-time notifications for sales events.
Available Events
| Event | Description |
|---|---|
cart.created | New cart created |
cart.updated | Cart modified |
cart.abandoned | Cart abandoned (no activity) |
order.created | New order placed |
order.paid | Order payment received |
order.shipped | Order shipped |
order.delivered | Order delivered |
order.cancelled | Order cancelled |
order.refunded | Order refunded |
subscription.created | New subscription created |
subscription.updated | Subscription modified |
subscription.trial_ending | Trial ending in 3 days |
subscription.cancelled | Subscription cancelled |
subscription.paused | Subscription paused |
subscription.resumed | Subscription resumed |
payment.succeeded | Payment successful |
payment.failed | Payment failed |
payment.refunded | Payment refunded |
invoice.created | Invoice generated |
invoice.paid | Invoice paid |
invoice.payment_failed | Invoice payment failed |
invoice.upcoming | Invoice will be sent soon |
Webhook Payload
{
"id": "evt-uuid",
"type": "subscription.created",
"created_at": "2026-01-02T12:00:00Z",
"data": {
"id": "sub-123",
"type": "subscriptions",
"attributes": {
"user_id": "user-123",
"plan_id": "plan-pro",
"status": "trialing"
}
}
}
Configure Webhooks
POST /api/v1/sales/webhooks
Request Body:
{
"data": {
"type": "webhooks",
"attributes": {
"url": "https://yourapp.com/webhooks/sales",
"events": [
"subscription.created",
"subscription.cancelled",
"payment.succeeded",
"payment.failed"
],
"secret": "whsec_xxx"
}
}
}
Verify Webhook Signature
import { verifyWebhookSignature } from '@23blocks/sdk';
const isValid = verifyWebhookSignature(
payload,
signature,
webhookSecret
);
SDK Examples
Complete E-commerce Flow
import { create23BlocksClient } from '@23blocks/sdk';
const client = create23BlocksClient({
urls: { sales: 'https://sales.api.us.23blocks.com' },
appId: 'your-app-id',
apiKey: 'your-api-key',
});
// 1. Create cart and add items
const cart = await client.sales.createCart({
userId: 'user-123',
currency: 'USD'
});
await client.sales.addCartItem(cart.id, {
productId: 'prod-shirt',
quantity: 2,
unitPrice: 29.99,
metadata: { size: 'L', color: 'blue' }
});
await client.sales.addCartItem(cart.id, {
productId: 'prod-hat',
quantity: 1,
unitPrice: 19.99
});
// 2. Apply discount
await client.sales.applyDiscount(cart.id, {
code: 'SAVE10'
});
// 3. Calculate taxes
const taxes = await client.sales.calculateTaxes(cart.id, {
shippingAddress: {
postalCode: '94102',
country: 'US',
state: 'CA'
}
});
// 4. Create checkout session
const session = await client.sales.createCheckoutSession({
cartId: cart.id,
successUrl: 'https://yourapp.com/success',
cancelUrl: 'https://yourapp.com/cart',
collectShippingAddress: true
});
// Redirect customer to session.url
Subscription Management
// Create subscription with trial
const subscription = await client.sales.createSubscription({
userId: 'user-123',
planId: 'plan-pro-monthly',
paymentMethodId: 'pm_xxx',
trialDays: 14,
metadata: { referral: 'partner-123' }
});
// Upgrade subscription
await client.sales.updateSubscription(subscription.id, {
planId: 'plan-enterprise-monthly',
prorationBehavior: 'create_prorations'
});
// Add seats
await client.sales.updateSubscription(subscription.id, {
quantity: 10
});
// Cancel at period end
await client.sales.cancelSubscription(subscription.id, {
cancelAtPeriodEnd: true,
reason: 'Switching to competitor'
});
// Reactivate before period ends
await client.sales.reactivateSubscription(subscription.id);
Usage-Based Billing
// Report API usage
await client.sales.reportUsage(subscription.id, {
quantity: 1500,
action: 'increment',
timestamp: new Date().toISOString(),
metadata: { feature: 'api_calls' }
});
// Get usage summary
const usage = await client.sales.getUsageSummary(subscription.id, {
periodStart: '2026-01-01T00:00:00Z',
periodEnd: '2026-01-31T23:59:59Z'
});
console.log(`Total API calls: ${usage.total}`);
Invoice Management
// List user invoices
const invoices = await client.sales.listInvoices({
filter: { userId: 'user-123', status: 'paid' },
sort: '-created_at',
page: { number: 1, size: 10 }
});
// Download invoice PDF
const pdfBlob = await client.sales.downloadInvoicePdf('inv-123');
// Create manual invoice
const invoice = await client.sales.createInvoice({
userId: 'user-123',
dueDate: '2026-01-16T00:00:00Z',
lineItems: [
{ description: 'Consulting', quantity: 8, unitPrice: 15000 },
{ description: 'Setup', quantity: 1, unitPrice: 25000 }
]
});
// Send invoice
await client.sales.sendInvoice(invoice.id);
Payment Processing
// Add payment method
const paymentMethod = await client.sales.addPaymentMethod({
userId: 'user-123',
stripePaymentMethodId: 'pm_xxx',
setAsDefault: true
});
// Process one-time payment
const payment = await client.sales.createPayment({
amount: 9999,
currency: 'USD',
paymentMethodId: paymentMethod.id,
metadata: { description: 'Custom service' }
});
// Refund payment
await client.sales.refundPayment(payment.id, {
amount: 5000, // Partial refund
reason: 'customer_request'
});
Error Handling
Error Response Format
{
"errors": [
{
"status": "422",
"code": "insufficient_funds",
"title": "Payment Failed",
"detail": "The card has insufficient funds to complete this transaction.",
"meta": {
"decline_code": "insufficient_funds",
"payment_intent_id": "pi_xxx"
}
}
]
}
Common Error Codes
| Code | HTTP Status | Description |
|---|---|---|
cart_not_found | 404 | Cart does not exist |
cart_expired | 410 | Cart has expired |
cart_empty | 422 | Cannot checkout empty cart |
invalid_quantity | 422 | Quantity must be positive |
insufficient_inventory | 422 | Product out of stock |
invalid_discount | 422 | Discount code invalid or expired |
discount_minimum_not_met | 422 | Order doesn't meet minimum for discount |
payment_failed | 422 | Payment could not be processed |
card_declined | 422 | Card was declined |
insufficient_funds | 422 | Card has insufficient funds |
subscription_not_found | 404 | Subscription does not exist |
plan_not_found | 404 | Plan does not exist |
plan_inactive | 422 | Plan is no longer available |
subscription_cancelled | 422 | Cannot modify cancelled subscription |
invoice_already_paid | 422 | Invoice has already been paid |
Rate Limits
| Endpoint | Limit |
|---|---|
| Cart operations | 100 requests/minute |
| Order creation | 30 requests/minute |
| Payment processing | 50 requests/minute |
| Subscription operations | 60 requests/minute |
| Usage reporting | 300 requests/minute |
| Webhook endpoints | 1000 events/minute |
Best Practices
Cart Management
- Set appropriate cart expiration times (default: 7 days)
- Clear abandoned carts periodically
- Validate inventory before checkout
Subscription Billing
- Always offer trial periods for new customers
- Use proration for plan changes
- Send reminder emails before trial ends
- Handle failed payments with retry logic
Payment Security
- Never store full card numbers
- Use Stripe's tokenization
- Verify webhook signatures
- Implement idempotency keys for payments
Checkout Optimization
- Minimize checkout steps
- Offer guest checkout
- Pre-fill known customer data
- Show clear pricing breakdown
Related Resources
- Products Block - Product catalog management
- Auth Block - User authentication
- Frontend SDK - Client-side integration
Support
- Documentation: 23blocks.com/developers/blocks/sales
- API Reference: api.23blocks.com/docs
- Support: 23blocks.com/support
- Status: heartbeat.23blocks.com