Building an E-commerce Backend
Learn how to build a complete e-commerce backend with inventory management, order processing, payment integration, notifications, and automated features using Ductape SDK.
What You'll Build
- Product inventory management
- Shopping cart and order processing
- Stripe payment integration
- Email and SMS notifications
- Order fulfillment features
- Inventory tracking and alerts
- Customer management
- Order status updates
- Automated email campaigns
Prerequisites
- Node.js and npm installed
- Ductape account and API credentials
- Stripe account for payments
- Basic understanding of TypeScript/JavaScript
- Express.js knowledge (optional)
Setup
- TypeScript
- Java
- Go
- .NET
npm install @ductape/sdk@0.1.8
<dependency>
<groupId>app.ductape</groupId>
<artifactId>sdk</artifactId>
<version>0.1.8</version>
</dependency>
go get github.com/ductape/ductape/sdk/go@v0.1.8
dotnet add package Ductape.Sdk --version 0.1.8
Create .env:
DUCTAPE_API_KEY=your_api_key
STRIPE_SECRET_KEY=your_stripe_secret_key
STRIPE_WEBHOOK_SECRET=your_webhook_secret
Initialize Ductape SDK
import { Ductape } from '@ductape/sdk';
import express from 'express';
const ductape = new Ductape({
apiKey: process.env.DUCTAPE_API_KEY!
});
const app = express();
app.use(express.json());
Database Schema
- TypeScript
- Java
- Go
- .NET
// Products table
await ductape.databases.schema.create('products', {
sku: { type: 'String', unique: true, required: true },
name: { type: 'String', required: true },
description: { type: 'String' },
price: { type: 'Number', required: true },
compare_at_price: { type: 'Number' },
cost_price: { type: 'Number' },
currency: { type: 'String', default: 'USD' },
category: { type: 'String' },
tags: { type: 'Array' },
images: { type: 'Array' },
inventory_quantity: { type: 'Number', default: 0 },
low_stock_threshold: { type: 'Number', default: 10 },
is_active: { type: 'Boolean', default: true },
is_featured: { type: 'Boolean', default: false },
weight: { type: 'Number' }, // in grams
dimensions: { type: 'JSON' }, // { length, width, height }
created_at: { type: 'Date', default: 'now' },
updated_at: { type: 'Date', default: 'now' }
});
// Customers table
await ductape.databases.schema.create('customers', {
email: { type: 'String', unique: true, required: true },
first_name: { type: 'String', required: true },
last_name: { type: 'String', required: true },
phone: { type: 'String' },
stripe_customer_id: { type: 'String', unique: true },
addresses: { type: 'Array' },
default_address_index: { type: 'Number', default: 0 },
total_orders: { type: 'Number', default: 0 },
total_spent: { type: 'Number', default: 0 },
marketing_opt_in: { type: 'Boolean', default: false },
created_at: { type: 'Date', default: 'now' },
updated_at: { type: 'Date', default: 'now' }
});
// Orders table
await ductape.databases.schema.create('orders', {
order_number: { type: 'String', unique: true, required: true },
customer_id: { type: 'String', required: true },
email: { type: 'String', required: true },
line_items: { type: 'Array', required: true },
subtotal: { type: 'Number', required: true },
tax: { type: 'Number', default: 0 },
shipping_cost: { type: 'Number', default: 0 },
discount: { type: 'Number', default: 0 },
total: { type: 'Number', required: true },
currency: { type: 'String', default: 'USD' },
status: { type: 'String', default: 'pending' }, // pending, processing, fulfilled, cancelled
payment_status: { type: 'String', default: 'pending' }, // pending, paid, refunded, failed
fulfillment_status: { type: 'String', default: 'unfulfilled' },
stripe_payment_intent_id: { type: 'String' },
shipping_address: { type: 'JSON', required: true },
billing_address: { type: 'JSON', required: true },
tracking_number: { type: 'String' },
shipped_at: { type: 'Date' },
delivered_at: { type: 'Date' },
notes: { type: 'String' },
created_at: { type: 'Date', default: 'now' },
updated_at: { type: 'Date', default: 'now' }
});
// Inventory transactions table
await ductape.databases.schema.create('inventory_transactions', {
product_id: { type: 'String', required: true },
sku: { type: 'String', required: true },
quantity_change: { type: 'Number', required: true },
type: { type: 'String', required: true }, // sale, restock, adjustment, return
reference_id: { type: 'String' }, // order_id or purchase_order_id
notes: { type: 'String' },
created_at: { type: 'Date', default: 'now' }
});
// Create indexes
await ductape.databases.schema.createIndex('products', ['sku']);
await ductape.databases.schema.createIndex('products', ['category']);
await ductape.databases.schema.createIndex('products', ['is_active']);
await ductape.databases.schema.createIndex('customers', ['email']);
await ductape.databases.schema.createIndex('customers', ['stripe_customer_id']);
await ductape.databases.schema.createIndex('orders', ['order_number']);
await ductape.databases.schema.createIndex('orders', ['customer_id']);
await ductape.databases.schema.createIndex('orders', ['status']);
await ductape.databases.schema.createIndex('inventory_transactions', ['product_id']);
// Products table
ductape.databases.schema.create('products', Map.of(
sku: Map.of( "type", "String", "unique", true, "required", true ),
name: Map.of( "type", "String", "required", true ),
description: Map.of( "type", "String" ),
price: Map.of( "type", "Number", "required", true ),
compare_at_price: Map.of( "type", "Number" ),
cost_price: Map.of( "type", "Number" ),
currency: Map.of( "type", "String", "default", "USD" ),
category: Map.of( "type", "String" ),
tags: Map.of( "type", "Array" ),
images: Map.of( "type", "Array" ),
inventory_quantity: Map.of( "type", "Number", "default", 0 ),
low_stock_threshold: Map.of( "type", "Number", "default", 10 ),
is_active: Map.of( "type", "Boolean", "default", true ),
is_featured: Map.of( "type", "Boolean", "default", false ),
weight: Map.of( "type", "Number" ), // in grams
dimensions: Map.of( "type", "JSON" ), // Map.of( length, width, height )
created_at: Map.of( "type", "Date", "default", "now" ),
updated_at: Map.of( "type", "Date", "default", "now" )
));
// Customers table
ductape.databases.schema.create('customers', Map.of(
email: Map.of( "type", "String", "unique", true, "required", true ),
first_name: Map.of( "type", "String", "required", true ),
last_name: Map.of( "type", "String", "required", true ),
phone: Map.of( "type", "String" ),
stripe_customer_id: Map.of( "type", "String", "unique", true ),
addresses: Map.of( "type", "Array" ),
default_address_index: Map.of( "type", "Number", "default", 0 ),
total_orders: Map.of( "type", "Number", "default", 0 ),
total_spent: Map.of( "type", "Number", "default", 0 ),
marketing_opt_in: Map.of( "type", "Boolean", "default", false ),
created_at: Map.of( "type", "Date", "default", "now" ),
updated_at: Map.of( "type", "Date", "default", "now" )
));
// Orders table
ductape.databases.schema.create('orders', Map.of(
order_number: Map.of( "type", "String", "unique", true, "required", true ),
customer_id: Map.of( "type", "String", "required", true ),
email: Map.of( "type", "String", "required", true ),
line_items: Map.of( "type", "Array", "required", true ),
subtotal: Map.of( "type", "Number", "required", true ),
tax: Map.of( "type", "Number", "default", 0 ),
shipping_cost: Map.of( "type", "Number", "default", 0 ),
discount: Map.of( "type", "Number", "default", 0 ),
total: Map.of( "type", "Number", "required", true ),
currency: Map.of( "type", "String", "default", "USD" ),
status: Map.of( "type", "String", "default", "pending" ), // pending, processing, fulfilled, cancelled
payment_status: Map.of( "type", "String", "default", "pending" ), // pending, paid, refunded, failed
fulfillment_status: Map.of( "type", "String", "default", "unfulfilled" ),
stripe_payment_intent_id: Map.of( "type", "String" ),
shipping_address: Map.of( "type", "JSON", "required", true ),
billing_address: Map.of( "type", "JSON", "required", true ),
tracking_number: Map.of( "type", "String" ),
shipped_at: Map.of( "type", "Date" ),
delivered_at: Map.of( "type", "Date" ),
notes: Map.of( "type", "String" ),
created_at: Map.of( "type", "Date", "default", "now" ),
updated_at: Map.of( "type", "Date", "default", "now" )
));
// Inventory transactions table
ductape.databases.schema.create('inventory_transactions', Map.of(
product_id: Map.of( "type", "String", "required", true ),
sku: Map.of( "type", "String", "required", true ),
quantity_change: Map.of( "type", "Number", "required", true ),
type: Map.of( "type", "String", "required", true ), // sale, restock, adjustment, return
reference_id: Map.of( "type", "String" ), // order_id or purchase_order_id
notes: Map.of( "type", "String" ),
created_at: Map.of( "type", "Date", "default", "now" )
));
// Create indexes
ductape.databases.schema.createIndex('products', ['sku']);
ductape.databases.schema.createIndex('products', ['category']);
ductape.databases.schema.createIndex('products', ['is_active']);
ductape.databases.schema.createIndex('customers', ['email']);
ductape.databases.schema.createIndex('customers', ['stripe_customer_id']);
ductape.databases.schema.createIndex('orders', ['order_number']);
ductape.databases.schema.createIndex('orders', ['customer_id']);
ductape.databases.schema.createIndex('orders', ['status']);
ductape.databases.schema.createIndex('inventory_transactions', ['product_id']);
// Products table
client.databases.schema.create('products', {
sku: { "type": "String", "unique": true, "required": true },
name: { "type": "String", "required": true },
description: { "type": "String" },
price: { "type": "Number", "required": true },
compare_at_price: { "type": "Number" },
cost_price: { "type": "Number" },
currency: { "type": "String", "default": "USD" },
category: { "type": "String" },
tags: { "type": "Array" },
images: { "type": "Array" },
inventory_quantity: { "type": "Number", "default": 0 },
low_stock_threshold: { "type": "Number", "default": 10 },
is_active: { "type": "Boolean", "default": true },
is_featured: { "type": "Boolean", "default": false },
weight: { "type": "Number" }, // in grams
dimensions: { "type": "JSON" }, // { length, width, height }
created_at: { "type": "Date", "default": "now" },
updated_at: { "type": "Date", "default": "now" }
});
// Customers table
client.databases.schema.create('customers', {
email: { "type": "String", "unique": true, "required": true },
first_name: { "type": "String", "required": true },
last_name: { "type": "String", "required": true },
phone: { "type": "String" },
stripe_customer_id: { "type": "String", "unique": true },
addresses: { "type": "Array" },
default_address_index: { "type": "Number", "default": 0 },
total_orders: { "type": "Number", "default": 0 },
total_spent: { "type": "Number", "default": 0 },
marketing_opt_in: { "type": "Boolean", "default": false },
created_at: { "type": "Date", "default": "now" },
updated_at: { "type": "Date", "default": "now" }
});
// Orders table
client.databases.schema.create('orders', {
order_number: { "type": "String", "unique": true, "required": true },
customer_id: { "type": "String", "required": true },
email: { "type": "String", "required": true },
line_items: { "type": "Array", "required": true },
subtotal: { "type": "Number", "required": true },
tax: { "type": "Number", "default": 0 },
shipping_cost: { "type": "Number", "default": 0 },
discount: { "type": "Number", "default": 0 },
total: { "type": "Number", "required": true },
currency: { "type": "String", "default": "USD" },
status: { "type": "String", "default": "pending" }, // pending, processing, fulfilled, cancelled
payment_status: { "type": "String", "default": "pending" }, // pending, paid, refunded, failed
fulfillment_status: { "type": "String", "default": "unfulfilled" },
stripe_payment_intent_id: { "type": "String" },
shipping_address: { "type": "JSON", "required": true },
billing_address: { "type": "JSON", "required": true },
tracking_number: { "type": "String" },
shipped_at: { "type": "Date" },
delivered_at: { "type": "Date" },
notes: { "type": "String" },
created_at: { "type": "Date", "default": "now" },
updated_at: { "type": "Date", "default": "now" }
});
// Inventory transactions table
client.databases.schema.create('inventory_transactions', {
product_id: { "type": "String", "required": true },
sku: { "type": "String", "required": true },
quantity_change: { "type": "Number", "required": true },
type: { "type": "String", "required": true }, // sale, restock, adjustment, return
reference_id: { "type": "String" }, // order_id or purchase_order_id
notes: { "type": "String" },
created_at: { "type": "Date", "default": "now" }
});
// Create indexes
client.databases.schema.createIndex('products', ['sku']);
client.databases.schema.createIndex('products', ['category']);
client.databases.schema.createIndex('products', ['is_active']);
client.databases.schema.createIndex('customers', ['email']);
client.databases.schema.createIndex('customers', ['stripe_customer_id']);
client.databases.schema.createIndex('orders', ['order_number']);
client.databases.schema.createIndex('orders', ['customer_id']);
client.databases.schema.createIndex('orders', ['status']);
client.databases.schema.createIndex('inventory_transactions', ['product_id']);
// Products table
await ductape.databases.schema.create('products', {
sku: { ["type"] = "String", ["unique"] = true, ["required"] = true },
name: { ["type"] = "String", ["required"] = true },
description: { ["type"] = "String" },
price: { ["type"] = "Number", ["required"] = true },
compare_at_price: { ["type"] = "Number" },
cost_price: { ["type"] = "Number" },
currency: { ["type"] = "String", ["default"] = "USD" },
category: { ["type"] = "String" },
tags: { ["type"] = "Array" },
images: { ["type"] = "Array" },
inventory_quantity: { ["type"] = "Number", ["default"] = 0 },
low_stock_threshold: { ["type"] = "Number", ["default"] = 10 },
is_active: { ["type"] = "Boolean", ["default"] = true },
is_featured: { ["type"] = "Boolean", ["default"] = false },
weight: { ["type"] = "Number" }, // in grams
dimensions: { ["type"] = "JSON" }, // { length, width, height }
created_at: { ["type"] = "Date", ["default"] = "now" },
updated_at: { ["type"] = "Date", ["default"] = "now" }
});
// Customers table
await ductape.databases.schema.create('customers', {
email: { ["type"] = "String", ["unique"] = true, ["required"] = true },
first_name: { ["type"] = "String", ["required"] = true },
last_name: { ["type"] = "String", ["required"] = true },
phone: { ["type"] = "String" },
stripe_customer_id: { ["type"] = "String", ["unique"] = true },
addresses: { ["type"] = "Array" },
default_address_index: { ["type"] = "Number", ["default"] = 0 },
total_orders: { ["type"] = "Number", ["default"] = 0 },
total_spent: { ["type"] = "Number", ["default"] = 0 },
marketing_opt_in: { ["type"] = "Boolean", ["default"] = false },
created_at: { ["type"] = "Date", ["default"] = "now" },
updated_at: { ["type"] = "Date", ["default"] = "now" }
});
// Orders table
await ductape.databases.schema.create('orders', {
order_number: { ["type"] = "String", ["unique"] = true, ["required"] = true },
customer_id: { ["type"] = "String", ["required"] = true },
email: { ["type"] = "String", ["required"] = true },
line_items: { ["type"] = "Array", ["required"] = true },
subtotal: { ["type"] = "Number", ["required"] = true },
tax: { ["type"] = "Number", ["default"] = 0 },
shipping_cost: { ["type"] = "Number", ["default"] = 0 },
discount: { ["type"] = "Number", ["default"] = 0 },
total: { ["type"] = "Number", ["required"] = true },
currency: { ["type"] = "String", ["default"] = "USD" },
status: { ["type"] = "String", ["default"] = "pending" }, // pending, processing, fulfilled, cancelled
payment_status: { ["type"] = "String", ["default"] = "pending" }, // pending, paid, refunded, failed
fulfillment_status: { ["type"] = "String", ["default"] = "unfulfilled" },
stripe_payment_intent_id: { ["type"] = "String" },
shipping_address: { ["type"] = "JSON", ["required"] = true },
billing_address: { ["type"] = "JSON", ["required"] = true },
tracking_number: { ["type"] = "String" },
shipped_at: { ["type"] = "Date" },
delivered_at: { ["type"] = "Date" },
notes: { ["type"] = "String" },
created_at: { ["type"] = "Date", ["default"] = "now" },
updated_at: { ["type"] = "Date", ["default"] = "now" }
});
// Inventory transactions table
await ductape.databases.schema.create('inventory_transactions', {
product_id: { ["type"] = "String", ["required"] = true },
sku: { ["type"] = "String", ["required"] = true },
quantity_change: { ["type"] = "Number", ["required"] = true },
type: { ["type"] = "String", ["required"] = true }, // sale, restock, adjustment, return
reference_id: { ["type"] = "String" }, // order_id or purchase_order_id
notes: { ["type"] = "String" },
created_at: { ["type"] = "Date", ["default"] = "now" }
});
// Create indexes
await ductape.databases.schema.createIndex('products', ['sku']);
await ductape.databases.schema.createIndex('products', ['category']);
await ductape.databases.schema.createIndex('products', ['is_active']);
await ductape.databases.schema.createIndex('customers', ['email']);
await ductape.databases.schema.createIndex('customers', ['stripe_customer_id']);
await ductape.databases.schema.createIndex('orders', ['order_number']);
await ductape.databases.schema.createIndex('orders', ['customer_id']);
await ductape.databases.schema.createIndex('orders', ['status']);
await ductape.databases.schema.createIndex('inventory_transactions', ['product_id']);
Product Management
- TypeScript
- Java
- Go
- .NET
// Create product
async function createProduct(productData: {
sku: string;
name: string;
description: string;
price: number;
cost_price?: number;
category?: string;
inventory_quantity?: number;
images?: string[];
}) {
const result = await ductape.databases.insert({
table: 'products',
data: {
sku: productData.sku,
name: productData.name,
description: productData.description,
price: productData.price,
cost_price: productData.cost_price,
category: productData.category,
inventory_quantity: productData.inventory_quantity || 0,
images: productData.images || [],
low_stock_threshold: 10
}
});
return result.rows[0];
}
// Update inventory with transaction tracking
async function updateInventory(
productId: string,
quantityChange: number,
type: 'sale' | 'restock' | 'adjustment' | 'return',
referenceId?: string,
notes?: string
) {
// Get current product
const product = await ductape.databases.findOne({
table: 'products',
where: { id: productId }
});
if (!product.row) {
throw new Error('Product not found');
}
const newQuantity = product.row.inventory_quantity + quantityChange;
if (newQuantity < 0) {
throw new Error('Insufficient inventory');
}
// Update product inventory
await ductape.databases.update({
table: 'products',
where: { id: productId },
data: {
inventory_quantity: newQuantity,
updated_at: new Date()
}
});
// Create inventory transaction record
await ductape.databases.insert({
table: 'inventory_transactions',
data: {
product_id: productId,
sku: product.row.sku,
quantity_change: quantityChange,
type,
reference_id: referenceId,
notes
}
});
// Check for low stock and send notification
if (newQuantity <= product.row.low_stock_threshold && quantityChange < 0) {
await sendLowStockNotification(product.row);
}
return newQuantity;
}
// Low stock notification
async function sendLowStockNotification(product: any) {
await ductape.notifications.send({
channel: 'email',
to: 'inventory@yourstore.com',
template: 'low-stock-alert',
data: {
product_name: product.name,
sku: product.sku,
current_quantity: product.inventory_quantity,
threshold: product.low_stock_threshold
}
});
}
// Create product
async function createProduct(productData: Map.of(
sku: string;
name: string;
description: string;
price: number;
cost_price?: number;
category?: string;
inventory_quantity?: number;
images?: string[];
)) Map.of(
Map<String, Object> result = ductape.databases().insert(Map<String, Object>.of(
"table", "products",
data: Map.of(
sku: productData.sku,
name: productData.name,
description: productData.description,
price: productData.price,
cost_price: productData.cost_price,
category: productData.category,
inventory_quantity: productData.inventory_quantity || 0,
images: productData.images || [],
"low_stock_threshold", 10
)
));
return result.rows[0];
)
// Update inventory with transaction tracking
async function updateInventory(
productId: string,
quantityChange: number,
"type", "sale" | 'restock' | 'adjustment' | 'return',
referenceId?: string,
notes?: string
) Map.of(
// Get current product
Map<String, Object> product = ductape.databases.findOne(Map.of(
"table", "products",
where: Map.of( id: productId )
));
if (!product.row) Map.of(
throw new Error('Product not found');
)
Map<String, Object> newQuantity = product.row.inventory_quantity + quantityChange;
if (newQuantity < 0) Map.of(
throw new Error('Insufficient inventory');
)
// Update product inventory
ductape.databases.update(Map.of(
"table", "products",
where: Map.of( id: productId ),
data: Map.of(
inventory_quantity: newQuantity,
updated_at: Instant.now()
)
));
// Create inventory transaction record
ductape.databases().insert(Map<String, Object>.of(
"table", "inventory_transactions",
data: Map.of(
product_id: productId,
sku: product.row.sku,
quantity_change: quantityChange,
type,
reference_id: referenceId,
notes
)
));
// Check for low stock and send notification
if (newQuantity <= product.row.low_stock_threshold && quantityChange < 0) Map.of(
sendLowStockNotification(product.row);
)
return newQuantity;
)
// Low stock notification
async function sendLowStockNotification(product: any) Map.of(
ductape.notifications().send(Map<String, Object>.of(
"channel", "email",
"to", "inventory@yourstore.com",
"template", "low-stock-alert",
data: Map.of(
product_name: product.name,
sku: product.sku,
current_quantity: product.inventory_quantity,
threshold: product.low_stock_threshold
)
));
)
import "context"
// Create product
async function createProduct(productData: {
sku: string;
name: string;
description: string;
price: number;
cost_price?: number;
category?: string;
inventory_quantity?: number;
images?: string[];
}) {
result := client.Databases.Insert(ctx, map[string]any{
"table": "products",
data: {
sku: productData.sku,
name: productData.name,
description: productData.description,
price: productData.price,
cost_price: productData.cost_price,
category: productData.category,
inventory_quantity: productData.inventory_quantity || 0,
images: productData.images || [],
"low_stock_threshold": 10
}
});
return result.rows[0];
}
// Update inventory with transaction tracking
async function updateInventory(
productId: string,
quantityChange: number,
"type": "sale" | 'restock' | 'adjustment' | 'return',
referenceId?: string,
notes?: string
) {
// Get current product
product := client.databases.findOne({
"table": "products",
where: { id: productId }
});
if (!product.row) {
throw new Error('Product not found');
}
newQuantity := product.row.inventory_quantity + quantityChange;
if (newQuantity < 0) {
throw new Error('Insufficient inventory');
}
// Update product inventory
client.databases.update({
"table": "products",
where: { id: productId },
data: {
inventory_quantity: newQuantity,
updated_at: new Date()
}
});
// Create inventory transaction record
client.Databases.Insert(ctx, map[string]any{
"table": "inventory_transactions",
data: {
product_id: productId,
sku: product.row.sku,
quantity_change: quantityChange,
type,
reference_id: referenceId,
notes
}
});
// Check for low stock and send notification
if (newQuantity <= product.row.low_stock_threshold && quantityChange < 0) {
sendLowStockNotification(product.row);
}
return newQuantity;
}
// Low stock notification
async function sendLowStockNotification(product: any) {
client.Notifications.Send(ctx, map[string]any{
"channel": "email",
"to": "inventory@yourstore.com",
"template": "low-stock-alert",
data: {
product_name: product.name,
sku: product.sku,
current_quantity: product.inventory_quantity,
threshold: product.low_stock_threshold
}
});
}
// Create product
async function createProduct(productData: {
sku: string;
name: string;
description: string;
price: number;
cost_price?: number;
category?: string;
inventory_quantity?: number;
images?: string[];
}) {
var result = await ductape.Database.Insert(new Dictionary<string, object?>
{
["table"] = "products",
data: {
sku: productData.sku,
name: productData.name,
description: productData.description,
price: productData.price,
cost_price: productData.cost_price,
category: productData.category,
inventory_quantity: productData.inventory_quantity || 0,
images: productData.images || [],
["low_stock_threshold"] = 10
}
});
return result.rows[0];
}
// Update inventory with transaction tracking
async function updateInventory(
productId: string,
quantityChange: number,
["type"] = "sale" | 'restock' | 'adjustment' | 'return',
referenceId?: string,
notes?: string
) {
// Get current product
var product = await ductape.databases.findOne({
["table"] = "products",
where: { id: productId }
});
if (!product.row) {
throw new Error('Product not found');
}
var newQuantity = product.row.inventory_quantity + quantityChange;
if (newQuantity < 0) {
throw new Error('Insufficient inventory');
}
// Update product inventory
await ductape.databases.update({
["table"] = "products",
where: { id: productId },
data: {
inventory_quantity: newQuantity,
updated_at: DateTime.UtcNow
}
});
// Create inventory transaction record
await ductape.Database.Insert(new Dictionary<string, object?>
{
["table"] = "inventory_transactions",
data: {
product_id: productId,
sku: product.row.sku,
quantity_change: quantityChange,
type,
reference_id: referenceId,
notes
}
});
// Check for low stock and send notification
if (newQuantity <= product.row.low_stock_threshold && quantityChange < 0) {
await sendLowStockNotification(product.row);
}
return newQuantity;
}
// Low stock notification
async function sendLowStockNotification(product: any) {
await await ductape.Notifications.SendAsync(new Dictionary<string, object?>
{
["channel"] = "email",
["to"] = "inventory@yourstore.com",
["template"] = "low-stock-alert",
data: {
product_name: product.name,
sku: product.sku,
current_quantity: product.inventory_quantity,
threshold: product.low_stock_threshold
}
});
}
Customer Management with Stripe
- TypeScript
- Java
- Go
- .NET
// Create or get customer
async function createOrGetCustomer(customerData: {
email: string;
first_name: string;
last_name: string;
phone?: string;
address?: any;
}) {
// Check if customer exists
const existing = await ductape.databases.findOne({
table: 'customers',
where: { email: customerData.email }
});
if (existing.row) {
return existing.row;
}
// Create Stripe customer
const stripeCustomer = await ductape.api.execute({
action: 'stripe.create-customer',
input: {
email: customerData.email,
name: `${customerData.first_name} ${customerData.last_name}`,
phone: customerData.phone,
address: customerData.address,
metadata: {
source: 'ecommerce-api'
}
}
});
// Create customer in database
const customer = await ductape.databases.insert({
table: 'customers',
data: {
email: customerData.email,
first_name: customerData.first_name,
last_name: customerData.last_name,
phone: customerData.phone,
stripe_customer_id: stripeCustomer.id,
addresses: customerData.address ? [customerData.address] : []
}
});
return customer.rows[0];
}
// Create or get customer
async function createOrGetCustomer(customerData: Map.of(
email: string;
first_name: string;
last_name: string;
phone?: string;
address?: any;
)) Map.of(
// Check if customer exists
Map<String, Object> existing = ductape.databases.findOne(Map.of(
"table", "customers",
where: Map.of( email: customerData.email )
));
if (existing.row) Map.of(
return existing.row;
)
// Create Stripe customer
Map<String, Object> stripeCustomer = ductape.api.execute(Map.of(
"action", "stripe.create-customer",
input: Map.of(
email: customerData.email,
name: `$Map.of(customerData.first_name) $Map.of(customerData.last_name)`,
phone: customerData.phone,
address: customerData.address,
metadata: Map.of(
"source", "ecommerce-api"
)
)
));
// Create customer in database
Map<String, Object> customer = ductape.databases().insert(Map<String, Object>.of(
"table", "customers",
data: Map.of(
email: customerData.email,
first_name: customerData.first_name,
last_name: customerData.last_name,
phone: customerData.phone,
stripe_customer_id: stripeCustomer.id,
addresses: customerData.address ? [customerData.address] : []
)
));
return customer.rows[0];
)
import "context"
// Create or get customer
async function createOrGetCustomer(customerData: {
email: string;
first_name: string;
last_name: string;
phone?: string;
address?: any;
}) {
// Check if customer exists
existing := client.databases.findOne({
"table": "customers",
where: { email: customerData.email }
});
if (existing.row) {
return existing.row;
}
// Create Stripe customer
stripeCustomer := client.api.execute({
"action": "stripe.create-customer",
input: {
email: customerData.email,
name: `${customerData.first_name} ${customerData.last_name}`,
phone: customerData.phone,
address: customerData.address,
metadata: {
"source": "ecommerce-api"
}
}
});
// Create customer in database
customer := client.Databases.Insert(ctx, map[string]any{
"table": "customers",
data: {
email: customerData.email,
first_name: customerData.first_name,
last_name: customerData.last_name,
phone: customerData.phone,
stripe_customer_id: stripeCustomer.id,
addresses: customerData.address ? [customerData.address] : []
}
});
return customer.rows[0];
}
// Create or get customer
async function createOrGetCustomer(customerData: {
email: string;
first_name: string;
last_name: string;
phone?: string;
address?: any;
}) {
// Check if customer exists
var existing = await ductape.databases.findOne({
["table"] = "customers",
where: { email: customerData.email }
});
if (existing.row) {
return existing.row;
}
// Create Stripe customer
var stripeCustomer = await ductape.api.execute({
["action"] = "stripe.create-customer",
input: {
email: customerData.email,
name: `${customerData.first_name} ${customerData.last_name}`,
phone: customerData.phone,
address: customerData.address,
metadata: {
["source"] = "ecommerce-api"
}
}
});
// Create customer in database
var customer = await ductape.Database.Insert(new Dictionary<string, object?>
{
["table"] = "customers",
data: {
email: customerData.email,
first_name: customerData.first_name,
last_name: customerData.last_name,
phone: customerData.phone,
stripe_customer_id: stripeCustomer.id,
addresses: customerData.address ? [customerData.address] : []
}
});
return customer.rows[0];
}
Order Processing with Feature
- TypeScript
- Java
- Go
- .NET
// Create order
async function createOrder(orderData: {
customer_email: string;
line_items: Array<{ product_id: string; quantity: number; price: number }>;
shipping_address: any;
billing_address: any;
}) {
// Calculate totals
const subtotal = orderData.line_items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
const tax = subtotal * 0.1; // 10% tax
const shipping_cost = subtotal > 100 ? 0 : 10; // Free shipping over $100
const total = subtotal + tax + shipping_cost;
// Generate order number
const orderNumber = `ORD-${Date.now()}-${Math.random().toString(36).substr(2, 6).toUpperCase()}`;
// Get or create customer
const customer = await createOrGetCustomer({
email: orderData.customer_email,
first_name: orderData.shipping_address.first_name,
last_name: orderData.shipping_address.last_name,
phone: orderData.shipping_address.phone,
address: orderData.shipping_address
});
// Create order
const order = await ductape.databases.insert({
table: 'orders',
data: {
order_number: orderNumber,
customer_id: customer.id,
email: orderData.customer_email,
line_items: orderData.line_items,
subtotal,
tax,
shipping_cost,
total,
shipping_address: orderData.shipping_address,
billing_address: orderData.billing_address,
status: 'pending',
payment_status: 'pending'
}
});
// Start order processing feature
await startOrderFeature(order.rows[0]);
return order.rows[0];
}
// Order processing feature
async function startOrderFeature(order: any) {
await ductape.features.execute({
feature: 'process-order',
input: {
order_id: order.id,
order_number: order.order_number,
customer_email: order.email,
total: order.total
}
});
}
// Create order
async function createOrder(orderData: Map.of(
customer_email: string;
line_items: Array<Map.of( product_id: string; quantity: number; price: number )>;
shipping_address: any;
billing_address: any;
)) Map.of(
// Calculate totals
Map<String, Object> subtotal = orderData.line_items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
Map<String, Object> tax = subtotal * 0.1; // 10% tax
Map<String, Object> shipping_cost = subtotal > 100 ? 0 : 10; // Free shipping over $100
Map<String, Object> total = subtotal + tax + shipping_cost;
// Generate order number
Map<String, Object> orderNumber = `ORD-$Map.of(Date.now())-$Map.of(Math.random().toString(36).substr(2, 6).toUpperCase())`;
// Get or create customer
Map<String, Object> customer = createOrGetCustomer(Map.of(
email: orderData.customer_email,
first_name: orderData.shipping_address.first_name,
last_name: orderData.shipping_address.last_name,
phone: orderData.shipping_address.phone,
address: orderData.shipping_address
));
// Create order
Map<String, Object> order = ductape.databases().insert(Map<String, Object>.of(
"table", "orders",
data: Map.of(
order_number: orderNumber,
customer_id: customer.id,
email: orderData.customer_email,
line_items: orderData.line_items,
subtotal,
tax,
shipping_cost,
total,
shipping_address: orderData.shipping_address,
billing_address: orderData.billing_address,
"status", "pending",
"payment_status", "pending"
)
));
// Start order processing feature
startOrderFeature(order.rows[0]);
return order.rows[0];
)
// Order processing feature
async function startOrderFeature(order: any) Map.of(
ductape.features.execute(Map.of(
"feature", "process-order",
input: Map.of(
order_id: order.id,
order_number: order.order_number,
customer_email: order.email,
total: order.total
)
));
)
import "context"
// Create order
async function createOrder(orderData: {
customer_email: string;
line_items: Array<{ product_id: string; quantity: number; price: number }>;
shipping_address: any;
billing_address: any;
}) {
// Calculate totals
subtotal := orderData.line_items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
tax := subtotal * 0.1; // 10% tax
shipping_cost := subtotal > 100 ? 0 : 10; // Free shipping over $100
total := subtotal + tax + shipping_cost;
// Generate order number
orderNumber := `ORD-${Date.now()}-${Math.random().toString(36).substr(2, 6).toUpperCase()}`;
// Get or create customer
customer := createOrGetCustomer({
email: orderData.customer_email,
first_name: orderData.shipping_address.first_name,
last_name: orderData.shipping_address.last_name,
phone: orderData.shipping_address.phone,
address: orderData.shipping_address
});
// Create order
order := client.Databases.Insert(ctx, map[string]any{
"table": "orders",
data: {
order_number: orderNumber,
customer_id: customer.id,
email: orderData.customer_email,
line_items: orderData.line_items,
subtotal,
tax,
shipping_cost,
total,
shipping_address: orderData.shipping_address,
billing_address: orderData.billing_address,
"status": "pending",
"payment_status": "pending"
}
});
// Start order processing feature
startOrderFeature(order.rows[0]);
return order.rows[0];
}
// Order processing feature
async function startOrderFeature(order: any) {
client.features.execute({
"feature": "process-order",
input: {
order_id: order.id,
order_number: order.order_number,
customer_email: order.email,
total: order.total
}
});
}
// Create order
async function createOrder(orderData: {
customer_email: string;
line_items: Array<{ product_id: string; quantity: number; price: number }>;
shipping_address: any;
billing_address: any;
}) {
// Calculate totals
var subtotal = orderData.line_items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
var tax = subtotal * 0.1; // 10% tax
var shipping_cost = subtotal > 100 ? 0 : 10; // Free shipping over $100
var total = subtotal + tax + shipping_cost;
// Generate order number
var orderNumber = `ORD-${Date.now()}-${Math.random().toString(36).substr(2, 6).toUpperCase()}`;
// Get or create customer
var customer = await createOrGetCustomer({
email: orderData.customer_email,
first_name: orderData.shipping_address.first_name,
last_name: orderData.shipping_address.last_name,
phone: orderData.shipping_address.phone,
address: orderData.shipping_address
});
// Create order
var order = await ductape.Database.Insert(new Dictionary<string, object?>
{
["table"] = "orders",
data: {
order_number: orderNumber,
customer_id: customer.id,
email: orderData.customer_email,
line_items: orderData.line_items,
subtotal,
tax,
shipping_cost,
total,
shipping_address: orderData.shipping_address,
billing_address: orderData.billing_address,
["status"] = "pending",
["payment_status"] = "pending"
}
});
// Start order processing feature
await startOrderFeature(order.rows[0]);
return order.rows[0];
}
// Order processing feature
async function startOrderFeature(order: any) {
await ductape.features.execute({
["feature"] = "process-order",
input: {
order_id: order.id,
order_number: order.order_number,
customer_email: order.email,
total: order.total
}
});
}
Create Order Feature
Create a feature file features/process-order.ts:
- TypeScript
- Java
- Go
- .NET
import { Ductape } from '@ductape/sdk';
const ductape = new Ductape({
apiKey: process.env.DUCTAPE_API_KEY!
});
export const processOrderFeature = {
name: 'process-order',
version: '1.0.0',
steps: [
// Step 1: Validate inventory
{
name: 'validate-inventory',
type: 'function',
handler: async (context: any) => {
const order = await ductape.databases.findOne({
table: 'orders',
where: { id: context.input.order_id }
});
// Check inventory for all items
for (const item of order.row.line_items) {
const product = await ductape.databases.findOne({
table: 'products',
where: { id: item.product_id }
});
if (product.row.inventory_quantity < item.quantity) {
throw new Error(`Insufficient inventory for ${product.row.name}`);
}
}
return { valid: true };
}
},
// Step 2: Create Stripe payment intent
{
name: 'create-payment-intent',
type: 'action',
action: 'stripe.create-payment-intent',
input: {
amount: '{{ input.total * 100 }}', // Convert to cents
currency: 'usd',
customer: '{{ customer.stripe_customer_id }}',
metadata: {
order_id: '{{ input.order_id }}',
order_number: '{{ input.order_number }}'
},
receipt_email: '{{ input.customer_email }}'
}
},
// Step 3: Wait for payment confirmation
{
name: 'wait-for-payment',
type: 'wait',
event: 'payment.confirmed',
timeout: 3600 // 1 hour
},
// Step 4: Reserve inventory
{
name: 'reserve-inventory',
type: 'function',
handler: async (context: any) => {
const order = await ductape.databases.findOne({
table: 'orders',
where: { id: context.input.order_id }
});
// Reduce inventory for all items
for (const item of order.row.line_items) {
await updateInventory(
item.product_id,
-item.quantity,
'sale',
order.row.id,
`Order ${order.row.order_number}`
);
}
// Update order status
await ductape.databases.update({
table: 'orders',
where: { id: order.row.id },
data: {
status: 'processing',
payment_status: 'paid',
updated_at: new Date()
}
});
return { reserved: true };
}
},
// Step 5: Send order confirmation email
{
name: 'send-confirmation',
type: 'notification',
channel: 'email',
to: '{{ input.customer_email }}',
template: 'order-confirmation',
data: {
order_number: '{{ input.order_number }}',
order_id: '{{ input.order_id }}'
}
},
// Step 6: Update customer stats
{
name: 'update-customer-stats',
type: 'function',
handler: async (context: any) => {
const order = await ductape.databases.findOne({
table: 'orders',
where: { id: context.input.order_id }
});
await ductape.databases.update({
table: 'customers',
where: { id: order.row.customer_id },
data: {
total_orders: { $increment: 1 },
total_spent: { $increment: order.row.total },
updated_at: new Date()
}
});
return { updated: true };
}
},
// Step 7: Schedule fulfillment check
{
name: 'schedule-fulfillment',
type: 'job',
schedule: '+2h', // Check in 2 hours
handler: async (context: any) => {
// Check if order needs fulfillment reminder
const order = await ductape.databases.findOne({
table: 'orders',
where: { id: context.input.order_id }
});
if (order.row.fulfillment_status === 'unfulfilled') {
// Send notification to warehouse
await ductape.notifications.send({
channel: 'email',
to: 'warehouse@yourstore.com',
template: 'fulfillment-reminder',
data: {
order_number: order.row.order_number,
order_id: order.row.id
}
});
}
}
}
],
errorHandlers: [
{
step: 'validate-inventory',
handler: async (context: any, error: any) => {
// Send out of stock notification
await ductape.notifications.send({
channel: 'email',
to: context.input.customer_email,
template: 'order-failed-inventory',
data: {
order_number: context.input.order_number,
reason: error.message
}
});
// Update order status
await ductape.databases.update({
table: 'orders',
where: { id: context.input.order_id },
data: {
status: 'cancelled',
notes: error.message
}
});
}
},
{
step: 'wait-for-payment',
handler: async (context: any, error: any) => {
// Payment timeout
await ductape.notifications.send({
channel: 'email',
to: context.input.customer_email,
template: 'payment-timeout',
data: {
order_number: context.input.order_number
}
});
await ductape.databases.update({
table: 'orders',
where: { id: context.input.order_id },
data: {
status: 'cancelled',
notes: 'Payment timeout'
}
});
}
}
]
};
import app.ductape.sdk.Ductape;
import app.ductape.sdk.core.EnvType;
import app.ductape.sdk.core.RequestContext;
Map<String, Object> ductape = new Ductape(Map.of(
apiKey: System.getenv("DUCTAPE_API_KEY")!
));
export Map<String, Object> processOrderFeature = Map.of(
"name", "process-order",
"version", "1.0.0",
steps: [
// Step 1: Validate inventory
Map.of(
"name", "validate-inventory",
"type", "function",
handler: async (context: any) => Map.of(
Map<String, Object> order = ductape.databases.findOne(Map.of(
"table", "orders",
where: Map.of( id: context.input.order_id )
));
// Check inventory for all items
for (Map<String, Object> item of order.row.line_items) Map.of(
Map<String, Object> product = ductape.databases.findOne(Map.of(
"table", "products",
where: Map.of( id: item.product_id )
));
if (product.row.inventory_quantity < item.quantity) Map.of(
throw new Error(`Insufficient inventory for $Map.of(product.row.name)`);
)
)
return Map.of( "valid", true );
)
),
// Step 2: Create Stripe payment intent
Map.of(
"name", "create-payment-intent",
"type", "action",
"action", "stripe.create-payment-intent",
input: Map.of(
"amount", "Map.of(Map.of( input.total * 100 ))", // Convert to cents
"currency", "usd",
"customer", "Map.of(Map.of( customer.stripe_customer_id ))",
metadata: Map.of(
"order_id", "Map.of(Map.of( input.order_id ))",
"order_number", "Map.of(Map.of( input.order_number ))"
),
"receipt_email", "Map.of(Map.of( input.customer_email ))"
)
),
// Step 3: Wait for payment confirmation
Map.of(
"name", "wait-for-payment",
"type", "wait",
"event", "payment.confirmed",
"timeout", 3600 // 1 hour
),
// Step 4: Reserve inventory
Map.of(
"name", "reserve-inventory",
"type", "function",
handler: async (context: any) => Map.of(
Map<String, Object> order = ductape.databases.findOne(Map.of(
"table", "orders",
where: Map.of( id: context.input.order_id )
));
// Reduce inventory for all items
for (Map<String, Object> item of order.row.line_items) Map.of(
updateInventory(
item.product_id,
-item.quantity,
'sale',
order.row.id,
`Order $Map.of(order.row.order_number)`
);
)
// Update order status
ductape.databases.update(Map.of(
"table", "orders",
where: Map.of( id: order.row.id ),
data: Map.of(
"status", "processing",
"payment_status", "paid",
updated_at: Instant.now()
)
));
return Map.of( "reserved", true );
)
),
// Step 5: Send order confirmation email
Map.of(
"name", "send-confirmation",
"type", "notification",
"channel", "email",
"to", "Map.of(Map.of( input.customer_email ))",
"template", "order-confirmation",
data: Map.of(
"order_number", "Map.of(Map.of( input.order_number ))",
"order_id", "Map.of(Map.of( input.order_id ))"
)
),
// Step 6: Update customer stats
Map.of(
"name", "update-customer-stats",
"type", "function",
handler: async (context: any) => Map.of(
Map<String, Object> order = ductape.databases.findOne(Map.of(
"table", "orders",
where: Map.of( id: context.input.order_id )
));
ductape.databases.update(Map.of(
"table", "customers",
where: Map.of( id: order.row.customer_id ),
data: Map.of(
total_orders: Map.of( $"increment", 1 ),
total_spent: Map.of( $increment: order.row.total ),
updated_at: Instant.now()
)
));
return Map.of( "updated", true );
)
),
// Step 7: Schedule fulfillment check
Map.of(
"name", "schedule-fulfillment",
"type", "job",
"schedule", "+2h", // Check in 2 hours
handler: async (context: any) => Map.of(
// Check if order needs fulfillment reminder
Map<String, Object> order = ductape.databases.findOne(Map.of(
"table", "orders",
where: Map.of( id: context.input.order_id )
));
if (order.row.fulfillment_status === 'unfulfilled') Map.of(
// Send notification to warehouse
ductape.notifications().send(Map<String, Object>.of(
"channel", "email",
"to", "warehouse@yourstore.com",
"template", "fulfillment-reminder",
data: Map.of(
order_number: order.row.order_number,
order_id: order.row.id
)
));
)
)
)
],
errorHandlers: [
Map.of(
"step", "validate-inventory",
handler: async (context: any, error: any) => Map.of(
// Send out of stock notification
ductape.notifications().send(Map<String, Object>.of(
"channel", "email",
to: context.input.customer_email,
"template", "order-failed-inventory",
data: Map.of(
order_number: context.input.order_number,
reason: error.message
)
));
// Update order status
ductape.databases.update(Map.of(
"table", "orders",
where: Map.of( id: context.input.order_id ),
data: Map.of(
"status", "cancelled",
notes: error.message
)
));
)
),
Map.of(
"step", "wait-for-payment",
handler: async (context: any, error: any) => Map.of(
// Payment timeout
ductape.notifications().send(Map<String, Object>.of(
"channel", "email",
to: context.input.customer_email,
"template", "payment-timeout",
data: Map.of(
order_number: context.input.order_number
)
));
ductape.databases.update(Map.of(
"table", "orders",
where: Map.of( id: context.input.order_id ),
data: Map.of(
"status", "cancelled",
"notes", "Payment timeout"
)
));
)
)
]
);
import "context"
import { Ductape } from '@ductape/sdk';
ductape := new Ductape({
apiKey: os.Getenv("DUCTAPE_API_KEY")!
});
export processOrderFeature := map[string]any{
"name": "process-order",
"version": "1.0.0",
steps: [
// Step 1: Validate inventory
{
"name": "validate-inventory",
"type": "function",
handler: async (context: any) => {
order := client.databases.findOne({
"table": "orders",
where: { id: context.input.order_id }
});
// Check inventory for all items
for (const item of order.row.line_items) {
product := client.databases.findOne({
"table": "products",
where: { id: item.product_id }
});
if (product.row.inventory_quantity < item.quantity) {
throw new Error(`Insufficient inventory for ${product.row.name}`);
}
}
return { "valid": true };
}
},
// Step 2: Create Stripe payment intent
{
"name": "create-payment-intent",
"type": "action",
"action": "stripe.create-payment-intent",
input: {
"amount": "{{ input.total * 100 }}", // Convert to cents
"currency": "usd",
"customer": "{{ customer.stripe_customer_id }}",
metadata: {
"order_id": "{{ input.order_id }}",
"order_number": "{{ input.order_number }}"
},
"receipt_email": "{{ input.customer_email }}"
}
},
// Step 3: Wait for payment confirmation
{
"name": "wait-for-payment",
"type": "wait",
"event": "payment.confirmed",
"timeout": 3600 // 1 hour
},
// Step 4: Reserve inventory
{
"name": "reserve-inventory",
"type": "function",
handler: async (context: any) => {
order := client.databases.findOne({
"table": "orders",
where: { id: context.input.order_id }
});
// Reduce inventory for all items
for (const item of order.row.line_items) {
updateInventory(
item.product_id,
-item.quantity,
'sale',
order.row.id,
`Order ${order.row.order_number}`
);
}
// Update order status
client.databases.update({
"table": "orders",
where: { id: order.row.id },
data: {
"status": "processing",
"payment_status": "paid",
updated_at: new Date()
}
});
return { "reserved": true };
}
},
// Step 5: Send order confirmation email
{
"name": "send-confirmation",
"type": "notification",
"channel": "email",
"to": "{{ input.customer_email }}",
"template": "order-confirmation",
data: {
"order_number": "{{ input.order_number }}",
"order_id": "{{ input.order_id }}"
}
},
// Step 6: Update customer stats
{
"name": "update-customer-stats",
"type": "function",
handler: async (context: any) => {
order := client.databases.findOne({
"table": "orders",
where: { id: context.input.order_id }
});
client.databases.update({
"table": "customers",
where: { id: order.row.customer_id },
data: {
total_orders: { $"increment": 1 },
total_spent: { $increment: order.row.total },
updated_at: new Date()
}
});
return { "updated": true };
}
},
// Step 7: Schedule fulfillment check
{
"name": "schedule-fulfillment",
"type": "job",
"schedule": "+2h", // Check in 2 hours
handler: async (context: any) => {
// Check if order needs fulfillment reminder
order := client.databases.findOne({
"table": "orders",
where: { id: context.input.order_id }
});
if (order.row.fulfillment_status === 'unfulfilled') {
// Send notification to warehouse
client.Notifications.Send(ctx, map[string]any{
"channel": "email",
"to": "warehouse@yourstore.com",
"template": "fulfillment-reminder",
data: {
order_number: order.row.order_number,
order_id: order.row.id
}
});
}
}
}
],
errorHandlers: [
{
"step": "validate-inventory",
handler: async (context: any, error: any) => {
// Send out of stock notification
client.Notifications.Send(ctx, map[string]any{
"channel": "email",
to: context.input.customer_email,
"template": "order-failed-inventory",
data: {
order_number: context.input.order_number,
reason: error.message
}
});
// Update order status
client.databases.update({
"table": "orders",
where: { id: context.input.order_id },
data: {
"status": "cancelled",
notes: error.message
}
});
}
},
{
"step": "wait-for-payment",
handler: async (context: any, error: any) => {
// Payment timeout
client.Notifications.Send(ctx, map[string]any{
"channel": "email",
to: context.input.customer_email,
"template": "payment-timeout",
data: {
order_number: context.input.order_number
}
});
client.databases.update({
"table": "orders",
where: { id: context.input.order_id },
data: {
"status": "cancelled",
"notes": "Payment timeout"
}
});
}
}
]
};
import { Ductape } from '@ductape/sdk';
var ductape = new Ductape({
apiKey: Environment.GetEnvironmentVariable("DUCTAPE_API_KEY")!
});
export var processOrderFeature = new Dictionary<string, object?>
{
["name"] = "process-order",
["version"] = "1.0.0",
steps: [
// Step 1: Validate inventory
{
["name"] = "validate-inventory",
["type"] = "function",
handler: async (context: any) => {
var order = await ductape.databases.findOne({
["table"] = "orders",
where: { id: context.input.order_id }
});
// Check inventory for all items
for (var item of order.row.line_items) {
var product = await ductape.databases.findOne({
["table"] = "products",
where: { id: item.product_id }
});
if (product.row.inventory_quantity < item.quantity) {
throw new Error(`Insufficient inventory for ${product.row.name}`);
}
}
return { ["valid"] = true };
}
},
// Step 2: Create Stripe payment intent
{
["name"] = "create-payment-intent",
["type"] = "action",
["action"] = "stripe.create-payment-intent",
input: {
["amount"] = "{{ input.total * 100 }}", // Convert to cents
["currency"] = "usd",
["customer"] = "{{ customer.stripe_customer_id }}",
metadata: {
["order_id"] = "{{ input.order_id }}",
["order_number"] = "{{ input.order_number }}"
},
["receipt_email"] = "{{ input.customer_email }}"
}
},
// Step 3: Wait for payment confirmation
{
["name"] = "wait-for-payment",
["type"] = "wait",
["event"] = "payment.confirmed",
["timeout"] = 3600 // 1 hour
},
// Step 4: Reserve inventory
{
["name"] = "reserve-inventory",
["type"] = "function",
handler: async (context: any) => {
var order = await ductape.databases.findOne({
["table"] = "orders",
where: { id: context.input.order_id }
});
// Reduce inventory for all items
for (var item of order.row.line_items) {
await updateInventory(
item.product_id,
-item.quantity,
'sale',
order.row.id,
`Order ${order.row.order_number}`
);
}
// Update order status
await ductape.databases.update({
["table"] = "orders",
where: { id: order.row.id },
data: {
["status"] = "processing",
["payment_status"] = "paid",
updated_at: DateTime.UtcNow
}
});
return { ["reserved"] = true };
}
},
// Step 5: Send order confirmation email
{
["name"] = "send-confirmation",
["type"] = "notification",
["channel"] = "email",
["to"] = "{{ input.customer_email }}",
["template"] = "order-confirmation",
data: {
["order_number"] = "{{ input.order_number }}",
["order_id"] = "{{ input.order_id }}"
}
},
// Step 6: Update customer stats
{
["name"] = "update-customer-stats",
["type"] = "function",
handler: async (context: any) => {
var order = await ductape.databases.findOne({
["table"] = "orders",
where: { id: context.input.order_id }
});
await ductape.databases.update({
["table"] = "customers",
where: { id: order.row.customer_id },
data: {
total_orders: { $["increment"] = 1 },
total_spent: { $increment: order.row.total },
updated_at: DateTime.UtcNow
}
});
return { ["updated"] = true };
}
},
// Step 7: Schedule fulfillment check
{
["name"] = "schedule-fulfillment",
["type"] = "job",
["schedule"] = "+2h", // Check in 2 hours
handler: async (context: any) => {
// Check if order needs fulfillment reminder
var order = await ductape.databases.findOne({
["table"] = "orders",
where: { id: context.input.order_id }
});
if (order.row.fulfillment_status === 'unfulfilled') {
// Send notification to warehouse
await await ductape.Notifications.SendAsync(new Dictionary<string, object?>
{
["channel"] = "email",
["to"] = "warehouse@yourstore.com",
["template"] = "fulfillment-reminder",
data: {
order_number: order.row.order_number,
order_id: order.row.id
}
});
}
}
}
],
errorHandlers: [
{
["step"] = "validate-inventory",
handler: async (context: any, error: any) => {
// Send out of stock notification
await await ductape.Notifications.SendAsync(new Dictionary<string, object?>
{
["channel"] = "email",
to: context.input.customer_email,
["template"] = "order-failed-inventory",
data: {
order_number: context.input.order_number,
reason: error.message
}
});
// Update order status
await ductape.databases.update({
["table"] = "orders",
where: { id: context.input.order_id },
data: {
["status"] = "cancelled",
notes: error.message
}
});
}
},
{
["step"] = "wait-for-payment",
handler: async (context: any, error: any) => {
// Payment timeout
await await ductape.Notifications.SendAsync(new Dictionary<string, object?>
{
["channel"] = "email",
to: context.input.customer_email,
["template"] = "payment-timeout",
data: {
order_number: context.input.order_number
}
});
await ductape.databases.update({
["table"] = "orders",
where: { id: context.input.order_id },
data: {
["status"] = "cancelled",
["notes"] = "Payment timeout"
}
});
}
}
]
};
Stripe Webhook Handler
- TypeScript
- Java
- Go
- .NET
// Handle Stripe webhooks
app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), async (req, res) => {
const sig = req.headers['stripe-signature'] as string;
let event;
try {
// Verify webhook signature
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
event = stripe.webhooks.constructEvent(
req.body,
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err: any) {
console.error('Webhook signature verification failed:', err.message);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the event
switch (event.type) {
case 'payment_intent.succeeded':
const paymentIntent = event.data.object;
// Find order
const order = await ductape.databases.findOne({
table: 'orders',
where: { stripe_payment_intent_id: paymentIntent.id }
});
if (order.row) {
// Trigger feature continuation
await ductape.features.signal({
feature: 'process-order',
executionId: order.row.feature_execution_id,
signal: 'payment.confirmed',
data: {
payment_intent_id: paymentIntent.id,
amount: paymentIntent.amount
}
});
}
break;
case 'payment_intent.payment_failed':
const failedPayment = event.data.object;
const failedOrder = await ductape.databases.findOne({
table: 'orders',
where: { stripe_payment_intent_id: failedPayment.id }
});
if (failedOrder.row) {
await ductape.databases.update({
table: 'orders',
where: { id: failedOrder.row.id },
data: {
payment_status: 'failed',
status: 'cancelled',
notes: 'Payment failed'
}
});
// Send notification
await ductape.notifications.send({
channel: 'email',
to: failedOrder.row.email,
template: 'payment-failed',
data: {
order_number: failedOrder.row.order_number
}
});
}
break;
}
res.json({ received: true });
});
// Handle Stripe webhooks
app.post('/webhooks/stripe', express.raw(Map.of( "type", "application/json" )), async (req, res) => Map.of(
Map<String, Object> sig = req.headers['stripe-signature'] as string;
Map<String, Object> event;
try Map.of(
// Verify webhook signature
Map<String, Object> stripe = require('stripe')(System.getenv("STRIPE_SECRET_KEY"));
event = stripe.webhooks.constructEvent(
req.body,
sig,
System.getenv("STRIPE_WEBHOOK_SECRET")
);
) catch (err: any) Map.of(
console.error('Webhook signature verification "failed", ", err.message);
return res.status(400).send(`Webhook Error: $Map.of(err.message)`);
)
// Handle the event
switch (event.type) Map.of(
case "payment_intent.succeeded':
Map<String, Object> paymentIntent = event.data.object;
// Find order
Map<String, Object> order = ductape.databases.findOne(Map.of(
"table", "orders",
where: Map.of( stripe_payment_intent_id: paymentIntent.id )
));
if (order.row) Map.of(
// Trigger feature continuation
ductape.features.signal(Map.of(
"feature", "process-order",
executionId: order.row.feature_execution_id,
"signal", "payment.confirmed",
data: Map.of(
payment_intent_id: paymentIntent.id,
amount: paymentIntent.amount
)
));
)
break;
case 'payment_intent.payment_failed':
Map<String, Object> failedPayment = event.data.object;
Map<String, Object> failedOrder = ductape.databases.findOne(Map.of(
"table", "orders",
where: Map.of( stripe_payment_intent_id: failedPayment.id )
));
if (failedOrder.row) Map.of(
ductape.databases.update(Map.of(
"table", "orders",
where: Map.of( id: failedOrder.row.id ),
data: Map.of(
"payment_status", "failed",
"status", "cancelled",
"notes", "Payment failed"
)
));
// Send notification
ductape.notifications().send(Map<String, Object>.of(
"channel", "email",
to: failedOrder.row.email,
"template", "payment-failed",
data: Map.of(
order_number: failedOrder.row.order_number
)
));
)
break;
)
res.json(Map.of( "received", true ));
));
import "context"
// Handle Stripe webhooks
app.post('/webhooks/stripe', express.raw({ "type": "application/json" }), async (req, res) => {
sig := req.headers['stripe-signature'] as string;
let event;
try {
// Verify webhook signature
stripe := require('stripe')(os.Getenv("STRIPE_SECRET_KEY"));
event = stripe.webhooks.constructEvent(
req.body,
sig,
os.Getenv("STRIPE_WEBHOOK_SECRET")
);
} catch (err: any) {
console.error('Webhook signature verification "failed": ", err.message);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the event
switch (event.type) {
case "payment_intent.succeeded':
paymentIntent := event.data.object;
// Find order
order := client.databases.findOne({
"table": "orders",
where: { stripe_payment_intent_id: paymentIntent.id }
});
if (order.row) {
// Trigger feature continuation
client.features.signal({
"feature": "process-order",
executionId: order.row.feature_execution_id,
"signal": "payment.confirmed",
data: {
payment_intent_id: paymentIntent.id,
amount: paymentIntent.amount
}
});
}
break;
case 'payment_intent.payment_failed':
failedPayment := event.data.object;
failedOrder := client.databases.findOne({
"table": "orders",
where: { stripe_payment_intent_id: failedPayment.id }
});
if (failedOrder.row) {
client.databases.update({
"table": "orders",
where: { id: failedOrder.row.id },
data: {
"payment_status": "failed",
"status": "cancelled",
"notes": "Payment failed"
}
});
// Send notification
client.Notifications.Send(ctx, map[string]any{
"channel": "email",
to: failedOrder.row.email,
"template": "payment-failed",
data: {
order_number: failedOrder.row.order_number
}
});
}
break;
}
res.json({ "received": true });
});
// Handle Stripe webhooks
app.post('/webhooks/stripe', express.raw({ ["type"] = "application/json" }), async (req, res) => {
var sig = req.headers['stripe-signature'] as string;
var event;
try {
// Verify webhook signature
var stripe = require('stripe')(Environment.GetEnvironmentVariable("STRIPE_SECRET_KEY"));
event = stripe.webhooks.constructEvent(
req.body,
sig,
Environment.GetEnvironmentVariable("STRIPE_WEBHOOK_SECRET")
);
} catch (err: any) {
console.error('Webhook signature verification ["failed"] = ", err.message);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the event
switch (event.type) {
case "payment_intent.succeeded':
var paymentIntent = event.data.object;
// Find order
var order = await ductape.databases.findOne({
["table"] = "orders",
where: { stripe_payment_intent_id: paymentIntent.id }
});
if (order.row) {
// Trigger feature continuation
await ductape.features.signal({
["feature"] = "process-order",
executionId: order.row.feature_execution_id,
["signal"] = "payment.confirmed",
data: {
payment_intent_id: paymentIntent.id,
amount: paymentIntent.amount
}
});
}
break;
case 'payment_intent.payment_failed':
var failedPayment = event.data.object;
var failedOrder = await ductape.databases.findOne({
["table"] = "orders",
where: { stripe_payment_intent_id: failedPayment.id }
});
if (failedOrder.row) {
await ductape.databases.update({
["table"] = "orders",
where: { id: failedOrder.row.id },
data: {
["payment_status"] = "failed",
["status"] = "cancelled",
["notes"] = "Payment failed"
}
});
// Send notification
await await ductape.Notifications.SendAsync(new Dictionary<string, object?>
{
["channel"] = "email",
to: failedOrder.row.email,
["template"] = "payment-failed",
data: {
order_number: failedOrder.row.order_number
}
});
}
break;
}
res.json({ ["received"] = true });
});
Message Broker for Real-time Updates
- TypeScript
- Java
- Go
- .NET
// Publish order status updates
async function publishOrderUpdate(orderId: string, status: string) {
await ductape.messageBrokers.publish({
topic: 'order-updates',
message: {
order_id: orderId,
status,
timestamp: new Date().toISOString()
}
});
}
// Subscribe to order updates (for admin dashboard)
async function subscribeToOrderUpdates(callback: (message: any) => void) {
await ductape.messageBrokers.subscribe({
topic: 'order-updates',
handler: async (message) => {
callback(message);
}
});
}
// Publish order status updates
async function publishOrderUpdate(orderId: string, status: string) Map.of(
ductape.messageBrokers.publish(Map.of(
"topic", "order-updates",
message: Map.of(
order_id: orderId,
status,
timestamp: Instant.now().toISOString()
)
));
)
// Subscribe to order updates (for admin dashboard)
async function subscribeToOrderUpdates(callback: (message: any) => void) Map.of(
ductape.messageBrokers.subscribe(Map.of(
"topic", "order-updates",
handler: async (message) => Map.of(
callback(message);
)
));
)
// Publish order status updates
async function publishOrderUpdate(orderId: string, status: string) {
client.messageBrokers.publish({
"topic": "order-updates",
message: {
order_id: orderId,
status,
timestamp: new Date().toISOString()
}
});
}
// Subscribe to order updates (for admin dashboard)
async function subscribeToOrderUpdates(callback: (message: any) => void) {
client.messageBrokers.subscribe({
"topic": "order-updates",
handler: async (message) => {
callback(message);
}
});
}
// Publish order status updates
async function publishOrderUpdate(orderId: string, status: string) {
await ductape.messageBrokers.publish({
["topic"] = "order-updates",
message: {
order_id: orderId,
status,
timestamp: DateTime.UtcNow.toISOString()
}
});
}
// Subscribe to order updates (for admin dashboard)
async function subscribeToOrderUpdates(callback: (message: any) => void) {
await ductape.messageBrokers.subscribe({
["topic"] = "order-updates",
handler: async (message) => {
callback(message);
}
});
}
Quotas and Rate Limiting
- TypeScript
- Java
- Go
- .NET
// Configure quotas for API endpoints
await ductape.quotas.configure({
'api:create-order': {
limit: 100,
window: '1h',
per: 'customer_id'
},
'api:search-products': {
limit: 1000,
window: '1h',
per: 'ip_address'
}
});
// Check quota before creating order
app.post('/orders', async (req, res) => {
const customerId = req.body.customer_id;
// Check quota
const quotaCheck = await ductape.quotas.check({
key: 'api:create-order',
identifier: customerId
});
if (!quotaCheck.allowed) {
return res.status(429).json({
error: 'Rate limit exceeded',
retryAfter: quotaCheck.retryAfter
});
}
// Process order
const order = await createOrder(req.body);
// Increment quota
await ductape.quotas.increment({
key: 'api:create-order',
identifier: customerId
});
res.json(order);
});
// Configure quotas for API endpoints
ductape.quotas.configure(Map.of(
'api:create-order': Map.of(
"limit", 100,
"window", "1h",
"per", "customer_id"
),
'api:search-products': Map.of(
"limit", 1000,
"window", "1h",
"per", "ip_address"
)
));
// Check quota before creating order
app.post('/orders', async (req, res) => Map.of(
Map<String, Object> customerId = req.body.customer_id;
// Check quota
Map<String, Object> quotaCheck = ductape.quotas.check(Map.of(
"key", "api:create-order",
identifier: customerId
));
if (!quotaCheck.allowed) Map.of(
return res.status(429).json(Map.of(
"error", "Rate limit exceeded",
retryAfter: quotaCheck.retryAfter
));
)
// Process order
Map<String, Object> order = createOrder(req.body);
// Increment quota
ductape.quotas.increment(Map.of(
"key", "api:create-order",
identifier: customerId
));
res.json(order);
));
// Configure quotas for API endpoints
client.quotas.configure({
'api:create-order': {
"limit": 100,
"window": "1h",
"per": "customer_id"
},
'api:search-products': {
"limit": 1000,
"window": "1h",
"per": "ip_address"
}
});
// Check quota before creating order
app.post('/orders', async (req, res) => {
customerId := req.body.customer_id;
// Check quota
quotaCheck := client.quotas.check({
"key": "api:create-order",
identifier: customerId
});
if (!quotaCheck.allowed) {
return res.status(429).json({
"error": "Rate limit exceeded",
retryAfter: quotaCheck.retryAfter
});
}
// Process order
order := createOrder(req.body);
// Increment quota
client.quotas.increment({
"key": "api:create-order",
identifier: customerId
});
res.json(order);
});
// Configure quotas for API endpoints
await ductape.quotas.configure({
'api:create-order': {
["limit"] = 100,
["window"] = "1h",
["per"] = "customer_id"
},
'api:search-products': {
["limit"] = 1000,
["window"] = "1h",
["per"] = "ip_address"
}
});
// Check quota before creating order
app.post('/orders', async (req, res) => {
var customerId = req.body.customer_id;
// Check quota
var quotaCheck = await ductape.quotas.check({
["key"] = "api:create-order",
identifier: customerId
});
if (!quotaCheck.allowed) {
return res.status(429).json({
["error"] = "Rate limit exceeded",
retryAfter: quotaCheck.retryAfter
});
}
// Process order
var order = await createOrder(req.body);
// Increment quota
await ductape.quotas.increment({
["key"] = "api:create-order",
identifier: customerId
});
res.json(order);
});
Storage for Product Images
- TypeScript
- Java
- Go
- .NET
// Upload product images
async function uploadProductImages(files: File[], productId: string): Promise<string[]> {
const uploadPromises = files.map(file =>
ductape.storage.upload({
file,
path: `products/${productId}`,
metadata: {
product_id: productId
}
})
);
const uploads = await Promise.all(uploadPromises);
return uploads.map(upload => upload.url);
}
// Generate signed URL for temporary access
async function getProductImageUrl(imagePath: string): Promise<string> {
const signedUrl = await ductape.storage.getSignedUrl({
path: imagePath,
expiresIn: 3600 // 1 hour
});
return signedUrl;
}
// Upload product images
async function uploadProductImages(files: File[], productId: string): Promise<string[]> Map.of(
Map<String, Object> uploadPromises = files.map(file =>
ductape.storage.upload(Map.of(
file,
path: `products/$Map.of(productId)`,
metadata: Map.of(
product_id: productId
)
))
);
Map<String, Object> uploads = Promise.all(uploadPromises);
return uploads.map(upload => upload.url);
)
// Generate signed URL for temporary access
async function getProductImageUrl(imagePath: string): Promise<string> Map.of(
Map<String, Object> signedUrl = ductape.storage.getSignedUrl(Map.of(
path: imagePath,
"expiresIn", 3600 // 1 hour
));
return signedUrl;
)
// Upload product images
async function uploadProductImages(files: File[], productId: string): Promise<string[]> {
uploadPromises := files.map(file =>
client.storage.upload({
file,
path: `products/${productId}`,
metadata: {
product_id: productId
}
})
);
uploads := Promise.all(uploadPromises);
return uploads.map(upload => upload.url);
}
// Generate signed URL for temporary access
async function getProductImageUrl(imagePath: string): Promise<string> {
signedUrl := client.storage.getSignedUrl({
path: imagePath,
"expiresIn": 3600 // 1 hour
});
return signedUrl;
}
// Upload product images
async function uploadProductImages(files: File[], productId: string): Promise<string[]> {
var uploadPromises = files.map(file =>
ductape.storage.upload({
file,
path: `products/${productId}`,
metadata: {
product_id: productId
}
})
);
var uploads = await Promise.all(uploadPromises);
return uploads.map(upload => upload.url);
}
// Generate signed URL for temporary access
async function getProductImageUrl(imagePath: string): Promise<string> {
var signedUrl = await ductape.storage.getSignedUrl({
path: imagePath,
["expiresIn"] = 3600 // 1 hour
});
return signedUrl;
}
Automated Marketing with Jobs
- TypeScript
- Java
- Go
- .NET
// Schedule abandoned cart recovery
async function scheduleAbandonedCartEmail(cartId: string, customerEmail: string) {
await ductape.jobs.schedule({
name: 'abandoned-cart-recovery',
schedule: '+24h', // Send after 24 hours
data: {
cart_id: cartId,
customer_email: customerEmail
},
handler: async (data) => {
// Check if cart is still abandoned
const order = await ductape.databases.findOne({
table: 'orders',
where: { cart_id: data.cart_id }
});
if (!order.row) {
// Send recovery email
await ductape.notifications.send({
channel: 'email',
to: data.customer_email,
template: 'abandoned-cart',
data: {
cart_id: data.cart_id,
recovery_link: `https://yourstore.com/cart/${data.cart_id}`
}
});
}
}
});
}
// Schedule post-purchase follow-up
async function scheduleFollowUpEmail(orderId: string, customerEmail: string) {
await ductape.jobs.schedule({
name: 'post-purchase-followup',
schedule: '+7d', // Send 7 days after purchase
data: {
order_id: orderId,
customer_email: customerEmail
},
handler: async (data) => {
await ductape.notifications.send({
channel: 'email',
to: data.customer_email,
template: 'review-request',
data: {
order_id: data.order_id
}
});
}
});
}
// Daily inventory report
await ductape.jobs.schedule({
name: 'daily-inventory-report',
schedule: '0 9 * * *', // Every day at 9 AM
handler: async () => {
// Get low stock products
const lowStockProducts = await ductape.databases.find({
table: 'products',
where: {
inventory_quantity: { $lte: 10 }
}
});
if (lowStockProducts.rows.length > 0) {
await ductape.notifications.send({
channel: 'email',
to: 'inventory@yourstore.com',
template: 'daily-inventory-report',
data: {
low_stock_products: lowStockProducts.rows,
date: new Date().toISOString()
}
});
}
}
});
// Schedule abandoned cart recovery
async function scheduleAbandonedCartEmail(cartId: string, customerEmail: string) Map.of(
ductape.jobs.schedule(Map.of(
"name", "abandoned-cart-recovery",
"schedule", "+24h", // Send after 24 hours
data: Map.of(
cart_id: cartId,
customer_email: customerEmail
),
handler: async (data) => Map.of(
// Check if cart is still abandoned
Map<String, Object> order = ductape.databases.findOne(Map.of(
"table", "orders",
where: Map.of( cart_id: data.cart_id )
));
if (!order.row) Map.of(
// Send recovery email
ductape.notifications().send(Map<String, Object>.of(
"channel", "email",
to: data.customer_email,
"template", "abandoned-cart",
data: Map.of(
cart_id: data.cart_id,
recovery_link: `https://yourstore.com/cart/$Map.of(data.cart_id)`
)
));
)
)
));
)
// Schedule post-purchase follow-up
async function scheduleFollowUpEmail(orderId: string, customerEmail: string) Map.of(
ductape.jobs.schedule(Map.of(
"name", "post-purchase-followup",
"schedule", "+7d", // Send 7 days after purchase
data: Map.of(
order_id: orderId,
customer_email: customerEmail
),
handler: async (data) => Map.of(
ductape.notifications().send(Map<String, Object>.of(
"channel", "email",
to: data.customer_email,
"template", "review-request",
data: Map.of(
order_id: data.order_id
)
));
)
));
)
// Daily inventory report
ductape.jobs.schedule(Map.of(
"name", "daily-inventory-report",
"schedule", "0 9 * * *", // Every day at 9 AM
handler: async () => Map.of(
// Get low stock products
Map<String, Object> lowStockProducts = ductape.databases().query(Map<String, Object>.of(
"table", "products",
where: Map.of(
inventory_quantity: Map.of( $"lte", 10 )
)
));
if (lowStockProducts.rows.length > 0) Map.of(
ductape.notifications().send(Map<String, Object>.of(
"channel", "email",
"to", "inventory@yourstore.com",
"template", "daily-inventory-report",
data: Map.of(
low_stock_products: lowStockProducts.rows,
date: Instant.now().toISOString()
)
));
)
)
));
import "context"
// Schedule abandoned cart recovery
async function scheduleAbandonedCartEmail(cartId: string, customerEmail: string) {
client.jobs.schedule({
"name": "abandoned-cart-recovery",
"schedule": "+24h", // Send after 24 hours
data: {
cart_id: cartId,
customer_email: customerEmail
},
handler: async (data) => {
// Check if cart is still abandoned
order := client.databases.findOne({
"table": "orders",
where: { cart_id: data.cart_id }
});
if (!order.row) {
// Send recovery email
client.Notifications.Send(ctx, map[string]any{
"channel": "email",
to: data.customer_email,
"template": "abandoned-cart",
data: {
cart_id: data.cart_id,
recovery_link: `https://yourstore.com/cart/${data.cart_id}`
}
});
}
}
});
}
// Schedule post-purchase follow-up
async function scheduleFollowUpEmail(orderId: string, customerEmail: string) {
client.jobs.schedule({
"name": "post-purchase-followup",
"schedule": "+7d", // Send 7 days after purchase
data: {
order_id: orderId,
customer_email: customerEmail
},
handler: async (data) => {
client.Notifications.Send(ctx, map[string]any{
"channel": "email",
to: data.customer_email,
"template": "review-request",
data: {
order_id: data.order_id
}
});
}
});
}
// Daily inventory report
client.jobs.schedule({
"name": "daily-inventory-report",
"schedule": "0 9 * * *", // Every day at 9 AM
handler: async () => {
// Get low stock products
lowStockProducts := client.Databases.Query(ctx, map[string]any{
"table": "products",
where: {
inventory_quantity: { $"lte": 10 }
}
});
if (lowStockProducts.rows.length > 0) {
client.Notifications.Send(ctx, map[string]any{
"channel": "email",
"to": "inventory@yourstore.com",
"template": "daily-inventory-report",
data: {
low_stock_products: lowStockProducts.rows,
date: new Date().toISOString()
}
});
}
}
});
// Schedule abandoned cart recovery
async function scheduleAbandonedCartEmail(cartId: string, customerEmail: string) {
await ductape.jobs.schedule({
["name"] = "abandoned-cart-recovery",
["schedule"] = "+24h", // Send after 24 hours
data: {
cart_id: cartId,
customer_email: customerEmail
},
handler: async (data) => {
// Check if cart is still abandoned
var order = await ductape.databases.findOne({
["table"] = "orders",
where: { cart_id: data.cart_id }
});
if (!order.row) {
// Send recovery email
await await ductape.Notifications.SendAsync(new Dictionary<string, object?>
{
["channel"] = "email",
to: data.customer_email,
["template"] = "abandoned-cart",
data: {
cart_id: data.cart_id,
recovery_link: `https://yourstore.com/cart/${data.cart_id}`
}
});
}
}
});
}
// Schedule post-purchase follow-up
async function scheduleFollowUpEmail(orderId: string, customerEmail: string) {
await ductape.jobs.schedule({
["name"] = "post-purchase-followup",
["schedule"] = "+7d", // Send 7 days after purchase
data: {
order_id: orderId,
customer_email: customerEmail
},
handler: async (data) => {
await await ductape.Notifications.SendAsync(new Dictionary<string, object?>
{
["channel"] = "email",
to: data.customer_email,
["template"] = "review-request",
data: {
order_id: data.order_id
}
});
}
});
}
// Daily inventory report
await ductape.jobs.schedule({
["name"] = "daily-inventory-report",
["schedule"] = "0 9 * * *", // Every day at 9 AM
handler: async () => {
// Get low stock products
var lowStockProducts = await ductape.Database.Query(new Dictionary<string, object?>
{
["table"] = "products",
where: {
inventory_quantity: { $["lte"] = 10 }
}
});
if (lowStockProducts.rows.length > 0) {
await await ductape.Notifications.SendAsync(new Dictionary<string, object?>
{
["channel"] = "email",
["to"] = "inventory@yourstore.com",
["template"] = "daily-inventory-report",
data: {
low_stock_products: lowStockProducts.rows,
date: DateTime.UtcNow.toISOString()
}
});
}
}
});
Order Fulfillment
- TypeScript
- Java
- Go
- .NET
// Mark order as fulfilled
async function fulfillOrder(orderId: string, trackingNumber: string) {
const order = await ductape.databases.findOne({
table: 'orders',
where: { id: orderId }
});
if (!order.row) {
throw new Error('Order not found');
}
// Update order
await ductape.databases.update({
table: 'orders',
where: { id: orderId },
data: {
status: 'fulfilled',
fulfillment_status: 'fulfilled',
tracking_number: trackingNumber,
shipped_at: new Date(),
updated_at: new Date()
}
});
// Send shipping notification
await ductape.notifications.send({
channel: 'email',
to: order.row.email,
template: 'order-shipped',
data: {
order_number: order.row.order_number,
tracking_number: trackingNumber,
tracking_url: `https://tracking.com/${trackingNumber}`
}
});
// Send SMS notification if customer has phone
const customer = await ductape.databases.findOne({
table: 'customers',
where: { id: order.row.customer_id }
});
if (customer.row?.phone) {
await ductape.notifications.send({
channel: 'sms',
to: customer.row.phone,
template: 'order-shipped-sms',
data: {
order_number: order.row.order_number,
tracking_number: trackingNumber
}
});
}
// Publish to message broker
await publishOrderUpdate(orderId, 'fulfilled');
// Schedule delivery follow-up
await ductape.jobs.schedule({
name: 'delivery-followup',
schedule: '+3d', // 3 days after shipping
data: { order_id: orderId },
handler: async (data) => {
await ductape.notifications.send({
channel: 'email',
to: order.row.email,
template: 'delivery-confirmation',
data: {
order_number: order.row.order_number
}
});
}
});
}
// Mark order as fulfilled
async function fulfillOrder(orderId: string, trackingNumber: string) Map.of(
Map<String, Object> order = ductape.databases.findOne(Map.of(
"table", "orders",
where: Map.of( id: orderId )
));
if (!order.row) Map.of(
throw new Error('Order not found');
)
// Update order
ductape.databases.update(Map.of(
"table", "orders",
where: Map.of( id: orderId ),
data: Map.of(
"status", "fulfilled",
"fulfillment_status", "fulfilled",
tracking_number: trackingNumber,
shipped_at: Instant.now(),
updated_at: Instant.now()
)
));
// Send shipping notification
ductape.notifications().send(Map<String, Object>.of(
"channel", "email",
to: order.row.email,
"template", "order-shipped",
data: Map.of(
order_number: order.row.order_number,
tracking_number: trackingNumber,
tracking_url: `https://tracking.com/$Map.of(trackingNumber)`
)
));
// Send SMS notification if customer has phone
Map<String, Object> customer = ductape.databases.findOne(Map.of(
"table", "customers",
where: Map.of( id: order.row.customer_id )
));
if (customer.row?.phone) Map.of(
ductape.notifications().send(Map<String, Object>.of(
"channel", "sms",
to: customer.row.phone,
"template", "order-shipped-sms",
data: Map.of(
order_number: order.row.order_number,
tracking_number: trackingNumber
)
));
)
// Publish to message broker
publishOrderUpdate(orderId, 'fulfilled');
// Schedule delivery follow-up
ductape.jobs.schedule(Map.of(
"name", "delivery-followup",
"schedule", "+3d", // 3 days after shipping
data: Map.of( order_id: orderId ),
handler: async (data) => Map.of(
ductape.notifications().send(Map<String, Object>.of(
"channel", "email",
to: order.row.email,
"template", "delivery-confirmation",
data: Map.of(
order_number: order.row.order_number
)
));
)
));
)
import "context"
// Mark order as fulfilled
async function fulfillOrder(orderId: string, trackingNumber: string) {
order := client.databases.findOne({
"table": "orders",
where: { id: orderId }
});
if (!order.row) {
throw new Error('Order not found');
}
// Update order
client.databases.update({
"table": "orders",
where: { id: orderId },
data: {
"status": "fulfilled",
"fulfillment_status": "fulfilled",
tracking_number: trackingNumber,
shipped_at: new Date(),
updated_at: new Date()
}
});
// Send shipping notification
client.Notifications.Send(ctx, map[string]any{
"channel": "email",
to: order.row.email,
"template": "order-shipped",
data: {
order_number: order.row.order_number,
tracking_number: trackingNumber,
tracking_url: `https://tracking.com/${trackingNumber}`
}
});
// Send SMS notification if customer has phone
customer := client.databases.findOne({
"table": "customers",
where: { id: order.row.customer_id }
});
if (customer.row?.phone) {
client.Notifications.Send(ctx, map[string]any{
"channel": "sms",
to: customer.row.phone,
"template": "order-shipped-sms",
data: {
order_number: order.row.order_number,
tracking_number: trackingNumber
}
});
}
// Publish to message broker
publishOrderUpdate(orderId, 'fulfilled');
// Schedule delivery follow-up
client.jobs.schedule({
"name": "delivery-followup",
"schedule": "+3d", // 3 days after shipping
data: { order_id: orderId },
handler: async (data) => {
client.Notifications.Send(ctx, map[string]any{
"channel": "email",
to: order.row.email,
"template": "delivery-confirmation",
data: {
order_number: order.row.order_number
}
});
}
});
}
// Mark order as fulfilled
async function fulfillOrder(orderId: string, trackingNumber: string) {
var order = await ductape.databases.findOne({
["table"] = "orders",
where: { id: orderId }
});
if (!order.row) {
throw new Error('Order not found');
}
// Update order
await ductape.databases.update({
["table"] = "orders",
where: { id: orderId },
data: {
["status"] = "fulfilled",
["fulfillment_status"] = "fulfilled",
tracking_number: trackingNumber,
shipped_at: DateTime.UtcNow,
updated_at: DateTime.UtcNow
}
});
// Send shipping notification
await await ductape.Notifications.SendAsync(new Dictionary<string, object?>
{
["channel"] = "email",
to: order.row.email,
["template"] = "order-shipped",
data: {
order_number: order.row.order_number,
tracking_number: trackingNumber,
tracking_url: `https://tracking.com/${trackingNumber}`
}
});
// Send SMS notification if customer has phone
var customer = await ductape.databases.findOne({
["table"] = "customers",
where: { id: order.row.customer_id }
});
if (customer.row?.phone) {
await await ductape.Notifications.SendAsync(new Dictionary<string, object?>
{
["channel"] = "sms",
to: customer.row.phone,
["template"] = "order-shipped-sms",
data: {
order_number: order.row.order_number,
tracking_number: trackingNumber
}
});
}
// Publish to message broker
await publishOrderUpdate(orderId, 'fulfilled');
// Schedule delivery follow-up
await ductape.jobs.schedule({
["name"] = "delivery-followup",
["schedule"] = "+3d", // 3 days after shipping
data: { order_id: orderId },
handler: async (data) => {
await await ductape.Notifications.SendAsync(new Dictionary<string, object?>
{
["channel"] = "email",
to: order.row.email,
["template"] = "delivery-confirmation",
data: {
order_number: order.row.order_number
}
});
}
});
}
Complete Express API
- TypeScript
- Java
- Go
- .NET
// Get products
app.get('/products', async (req, res) => {
const { category, search, page = 1, limit = 20 } = req.query;
const where: any = { is_active: true };
if (category) {
where.category = category;
}
if (search) {
where.$or = [
{ name: { $regex: search, $options: 'i' } },
{ description: { $regex: search, $options: 'i' } }
];
}
const products = await ductape.databases.find({
table: 'products',
where,
limit: Number(limit),
offset: (Number(page) - 1) * Number(limit),
orderBy: { created_at: 'desc' }
});
res.json(products.rows);
});
// Create order
app.post('/orders', async (req, res) => {
try {
const order = await createOrder(req.body);
res.json(order);
} catch (error: any) {
res.status(400).json({ error: error.message });
}
});
// Get order status
app.get('/orders/:orderNumber', async (req, res) => {
const order = await ductape.databases.findOne({
table: 'orders',
where: { order_number: req.params.orderNumber }
});
if (!order.row) {
return res.status(404).json({ error: 'Order not found' });
}
res.json(order.row);
});
// Fulfill order (admin endpoint)
app.post('/admin/orders/:orderId/fulfill', async (req, res) => {
const { tracking_number } = req.body;
try {
await fulfillOrder(req.params.orderId, tracking_number);
res.json({ success: true });
} catch (error: any) {
res.status(400).json({ error: error.message });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`E-commerce API running on port ${PORT}`);
});
// Get products
app.get('/products', async (req, res) => Map.of(
Map<String, Object> Map.of( category, search, page = 1, limit = 20 ) = req.query;
Map<String, Object> where: any = Map.of( "is_active", true );
if (category) Map.of(
where.category = category;
)
if (search) Map.of(
where.$or = [
Map.of( name: Map.of( $regex: search, $"options", "i" ) ),
Map.of( description: Map.of( $regex: search, $"options", "i" ) )
];
)
Map<String, Object> products = ductape.databases().query(Map<String, Object>.of(
"table", "products",
where,
limit: Number(limit),
offset: (Number(page) - 1) * Number(limit),
orderBy: Map.of( "created_at", "desc" )
));
res.json(products.rows);
));
// Create order
app.post('/orders', async (req, res) => Map.of(
try Map.of(
Map<String, Object> order = createOrder(req.body);
res.json(order);
) catch (error: any) Map.of(
res.status(400).json(Map.of( error: error.message ));
)
));
// Get order status
app.get('/orders/:orderNumber', async (req, res) => Map.of(
Map<String, Object> order = ductape.databases.findOne(Map.of(
"table", "orders",
where: Map.of( order_number: req.params.orderNumber )
));
if (!order.row) Map.of(
return res.status(404).json(Map.of( "error", "Order not found" ));
)
res.json(order.row);
));
// Fulfill order (admin endpoint)
app.post('/admin/orders/:orderId/fulfill', async (req, res) => Map.of(
Map<String, Object> Map.of( tracking_number ) = req.body;
try Map.of(
fulfillOrder(req.params.orderId, tracking_number);
res.json(Map.of( "success", true ));
) catch (error: any) Map.of(
res.status(400).json(Map.of( error: error.message ));
)
));
Map<String, Object> PORT = System.getenv("PORT") || 3000;
app.listen(PORT, () => Map.of(
System.out.println(`E-commerce API running on port $Map.of(PORT)`);
));
import "context"
// Get products
app.get('/products', async (req, res) => {
const { category, search, page = 1, limit = 20 } = req.query;
const where: any = { "is_active": true };
if (category) {
where.category = category;
}
if (search) {
where.$or = [
{ name: { $regex: search, $"options": "i" } },
{ description: { $regex: search, $"options": "i" } }
];
}
products := client.Databases.Query(ctx, map[string]any{
"table": "products",
where,
limit: Number(limit),
offset: (Number(page) - 1) * Number(limit),
orderBy: { "created_at": "desc" }
});
res.json(products.rows);
});
// Create order
app.post('/orders', async (req, res) => {
try {
order := createOrder(req.body);
res.json(order);
} catch (error: any) {
res.status(400).json({ error: error.message });
}
});
// Get order status
app.get('/orders/:orderNumber', async (req, res) => {
order := client.databases.findOne({
"table": "orders",
where: { order_number: req.params.orderNumber }
});
if (!order.row) {
return res.status(404).json({ "error": "Order not found" });
}
res.json(order.row);
});
// Fulfill order (admin endpoint)
app.post('/admin/orders/:orderId/fulfill', async (req, res) => {
const { tracking_number } = req.body;
try {
fulfillOrder(req.params.orderId, tracking_number);
res.json({ "success": true });
} catch (error: any) {
res.status(400).json({ error: error.message });
}
});
PORT := os.Getenv("PORT") || 3000;
app.listen(PORT, () => {
fmt.Println(`E-commerce API running on port ${PORT}`);
});
// Get products
app.get('/products', async (req, res) => {
var { category, search, page = 1, limit = 20 } = req.query;
var where: any = { ["is_active"] = true };
if (category) {
where.category = category;
}
if (search) {
where.$or = [
{ name: { $regex: search, $["options"] = "i" } },
{ description: { $regex: search, $["options"] = "i" } }
];
}
var products = await ductape.Database.Query(new Dictionary<string, object?>
{
["table"] = "products",
where,
limit: Number(limit),
offset: (Number(page) - 1) * Number(limit),
orderBy: { ["created_at"] = "desc" }
});
res.json(products.rows);
});
// Create order
app.post('/orders', async (req, res) => {
try {
var order = await createOrder(req.body);
res.json(order);
} catch (error: any) {
res.status(400).json({ error: error.message });
}
});
// Get order status
app.get('/orders/:orderNumber', async (req, res) => {
var order = await ductape.databases.findOne({
["table"] = "orders",
where: { order_number: req.params.orderNumber }
});
if (!order.row) {
return res.status(404).json({ ["error"] = "Order not found" });
}
res.json(order.row);
});
// Fulfill order (admin endpoint)
app.post('/admin/orders/:orderId/fulfill', async (req, res) => {
var { tracking_number } = req.body;
try {
await fulfillOrder(req.params.orderId, tracking_number);
res.json({ ["success"] = true });
} catch (error: any) {
res.status(400).json({ error: error.message });
}
});
var PORT = Environment.GetEnvironmentVariable("PORT") || 3000;
app.listen(PORT, () => {
Console.WriteLine(`E-commerce API running on port ${PORT}`);
});
Next Steps
- Implement refunds and returns feature
- Add product reviews and ratings
- Create discount codes and promotions
- Set up analytics and reporting
- Implement multi-currency support
- Add gift cards and store credit
- Create admin dashboard
- Set up monitoring and alerts