autocart-sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -0
- package/index.d.ts +14 -0
- package/index.js +136 -0
- package/package.json +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# @autocart/sdk
|
|
2
|
+
|
|
3
|
+
The official Node.js SDK for the AutoCart Agentic Commerce Platform.
|
|
4
|
+
|
|
5
|
+
This SDK allows you to instantly expose your existing e-commerce inventory to autonomous AI Buyers, while enforcing strict, cryptographic spending policies and risk-tier firewalls to protect your revenue.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @autocart/sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start Integration
|
|
14
|
+
|
|
15
|
+
You do not need to change your database structure to use AutoCart. You simply provide a `fetchCatalog` adapter function that maps your existing database fields to the standard AutoCart format (`sku`, `name`, `price`, `stock`).
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
import express from 'express';
|
|
19
|
+
import { AutoCartGateway } from '@autocart/sdk';
|
|
20
|
+
|
|
21
|
+
const app = express();
|
|
22
|
+
|
|
23
|
+
// Initialize the Gateway
|
|
24
|
+
const autocart = new AutoCartGateway({
|
|
25
|
+
merchantKey: 'YOUR_MERCHANT_KEY', // Generated in the AutoCart Portal
|
|
26
|
+
merchantSecret: 'YOUR_MERCHANT_SECRET', // Keep this safe!
|
|
27
|
+
|
|
28
|
+
// The Adapter Function: Translate your DB format to the AutoCart format
|
|
29
|
+
fetchCatalog: async () => {
|
|
30
|
+
// 1. Fetch data from your specific database (SQL, MongoDB, Shopify, etc.)
|
|
31
|
+
const myRawInventory = await myCustomDatabase.getProducts();
|
|
32
|
+
|
|
33
|
+
// 2. Map your custom fields to the strict AutoCart schema
|
|
34
|
+
return myRawInventory.map(product => ({
|
|
35
|
+
sku: product.item_id, // Must be a unique string
|
|
36
|
+
name: product.product_title, // String
|
|
37
|
+
price: product.price_in_inr, // Number (e.g., 4500)
|
|
38
|
+
stock: product.inventory_qty, // Number (e.g., 10)
|
|
39
|
+
|
|
40
|
+
// Optional: Add a short description to help the AI make decisions
|
|
41
|
+
description: product.short_desc
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Mount the SDK on your Express server
|
|
47
|
+
app.use('/api/ai-store', autocart.createRouter());
|
|
48
|
+
|
|
49
|
+
app.listen(3000, () => {
|
|
50
|
+
console.log('AI-Ready Storefront running on port 3000');
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## How It Works
|
|
55
|
+
|
|
56
|
+
By mounting `autocart.createRouter()`, the SDK automatically generates two endpoints on your server:
|
|
57
|
+
|
|
58
|
+
1. **`GET /api/ai-store/catalog`**: A highly optimized, token-lean JSON endpoint that AI Scout Agents query to compare prices and check stock.
|
|
59
|
+
2. **`POST /api/ai-store/checkout`**: The Policy Firewall interceptor. When an AI attempts a purchase, this endpoint verifies the price against your live DB, cryptographically signs the payload, and pings the AutoCart Trust Engine to ensure the human buyer has approved the budget.
|
|
60
|
+
|
|
61
|
+
For support, visit [merchants.autocart.com](https://autocart.com).
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
|
|
3
|
+
export interface AutoCartConfig {
|
|
4
|
+
merchantKey: string;
|
|
5
|
+
merchantSecret: string;
|
|
6
|
+
nexusUrl?: string;
|
|
7
|
+
fetchCatalog?: () => Promise<Array<{ sku: string; name: string; price: number; stock: number }>>;
|
|
8
|
+
fetchProduct: (sku: string) => Promise<{ sku: string; price: number; stock: number } | null>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class AutoCartGateway {
|
|
12
|
+
constructor(config: AutoCartConfig);
|
|
13
|
+
createRouter(): Router;
|
|
14
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import crypto from 'crypto';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export class AutoCartGateway {
|
|
6
|
+
constructor(config) {
|
|
7
|
+
if (!config.merchantKey || !config.merchantSecret || !config.fetchProduct) {
|
|
8
|
+
throw new Error('AutoCartGateway requires merchantKey, merchantSecret, and fetchProduct');
|
|
9
|
+
}
|
|
10
|
+
this.merchantKey = config.merchantKey;
|
|
11
|
+
this.merchantSecret = config.merchantSecret;
|
|
12
|
+
this.fetchCatalog = config.fetchCatalog; // Optional for legacy/indexing
|
|
13
|
+
this.fetchProduct = config.fetchProduct;
|
|
14
|
+
this.nexusUrl = config.nexusUrl || 'http://localhost:5000';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
_signPayload(payload) {
|
|
18
|
+
return crypto
|
|
19
|
+
.createHmac('sha256', this.merchantSecret)
|
|
20
|
+
.update(JSON.stringify(payload))
|
|
21
|
+
.digest('hex');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
createRouter() {
|
|
25
|
+
const router = express.Router();
|
|
26
|
+
router.use(express.json());
|
|
27
|
+
|
|
28
|
+
router.get('/catalog', async (req, res) => {
|
|
29
|
+
try {
|
|
30
|
+
if (!this.fetchCatalog) {
|
|
31
|
+
return res.status(501).json({ error: 'Merchant does not support full catalog fetching' });
|
|
32
|
+
}
|
|
33
|
+
const fullCatalog = await this.fetchCatalog();
|
|
34
|
+
const leanCatalog = fullCatalog.map(item => ({
|
|
35
|
+
sku: item.sku,
|
|
36
|
+
n: item.name,
|
|
37
|
+
p: item.price,
|
|
38
|
+
s: item.stock
|
|
39
|
+
}));
|
|
40
|
+
res.json({ catalog: leanCatalog });
|
|
41
|
+
} catch (err) {
|
|
42
|
+
res.status(500).json({ error: 'Failed to fetch catalog' });
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
router.post('/checkout', async (req, res) => {
|
|
47
|
+
try {
|
|
48
|
+
const { sku, qty, idempotencyKey, maxAuthorizedAmount } = req.body;
|
|
49
|
+
const buyerKey = req.headers['x-buyer-key'];
|
|
50
|
+
|
|
51
|
+
if (!buyerKey || !sku || !qty || !idempotencyKey) {
|
|
52
|
+
return res.status(400).json({ error: 'Missing required checkout fields or x-buyer-key header' });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const product = await this.fetchProduct(sku);
|
|
56
|
+
|
|
57
|
+
if (!product) {
|
|
58
|
+
return res.status(404).json({ error: 'Product SKU not found in merchant catalog' });
|
|
59
|
+
}
|
|
60
|
+
if (product.stock < qty) {
|
|
61
|
+
return res.status(409).json({ error: 'Insufficient stock' });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const lineTotal = product.price * qty;
|
|
65
|
+
|
|
66
|
+
const enginePayload = {
|
|
67
|
+
merchantKey: this.merchantKey,
|
|
68
|
+
buyerKey,
|
|
69
|
+
sku,
|
|
70
|
+
qty,
|
|
71
|
+
lineTotal,
|
|
72
|
+
idempotencyKey,
|
|
73
|
+
maxAuthorizedAmount
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const signature = this._signPayload(enginePayload);
|
|
77
|
+
|
|
78
|
+
const response = await fetch(`${this.nexusUrl}/api/engine/verify-intent`, {
|
|
79
|
+
method: 'POST',
|
|
80
|
+
headers: {
|
|
81
|
+
'Content-Type': 'application/json',
|
|
82
|
+
'x-autocart-signature': signature
|
|
83
|
+
},
|
|
84
|
+
body: JSON.stringify(enginePayload)
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const engineResult = await response.json();
|
|
88
|
+
|
|
89
|
+
if (!response.ok) {
|
|
90
|
+
return res.status(response.status).json(engineResult);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (engineResult.status !== 'AUTO_APPROVED') {
|
|
94
|
+
return res.status(200).json({
|
|
95
|
+
status: engineResult.status,
|
|
96
|
+
message: 'Transaction requires human approval or is blocked.',
|
|
97
|
+
auditId: engineResult.auditId
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Simulating the Webhook since headless payments aren't tokenized in this demo
|
|
102
|
+
await fetch(`${this.nexusUrl}/api/webhook/razorpay`, {
|
|
103
|
+
method: 'POST',
|
|
104
|
+
headers: {
|
|
105
|
+
'Content-Type': 'application/json',
|
|
106
|
+
'x-razorpay-signature': 'test-webhook-bypass'
|
|
107
|
+
},
|
|
108
|
+
body: JSON.stringify({
|
|
109
|
+
event: 'payment.captured',
|
|
110
|
+
payload: {
|
|
111
|
+
payment: {
|
|
112
|
+
entity: {
|
|
113
|
+
id: 'pay_sdk_auto',
|
|
114
|
+
order_id: engineResult.razorpayOrderId
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return res.status(200).json({
|
|
122
|
+
status: 'PAYMENT_CAPTURED',
|
|
123
|
+
razorpayOrderId: engineResult.razorpayOrderId,
|
|
124
|
+
auditId: engineResult.auditId,
|
|
125
|
+
receipt: `Paid ${lineTotal} INR`
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
} catch (err) {
|
|
129
|
+
console.error('[AutoCart SDK] Checkout Error:', err.message);
|
|
130
|
+
res.status(500).json({ error: 'SDK Checkout processing failed' });
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
return router;
|
|
135
|
+
}
|
|
136
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name":"autocart-sdk","version":"1.0.0","type":"module","main":"index.js","dependencies":{"axios":"^1.19.0","express":"^5.2.1"}}
|