Add Mellat e-payment, videos/instructions CMS, and legacy domain lookup.
Store gateway credentials per business, initiate/verify Mellat at checkout, and resolve old CMS ids from domains in the migrate modal. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
513673a7da
commit
d5e502cf77
+12
-2
@@ -342,7 +342,16 @@ Base: `/tenants/:host/user-products`
|
||||
| PATCH | `/cart/items/:itemId` | Update cart item quantity |
|
||||
| DELETE | `/cart/items/:itemId` | Remove cart item |
|
||||
| DELETE | `/cart` | Clear cart |
|
||||
| POST | `/cart/checkout` | Place order from cart (requires `addressId` or `shippingAddress`) |
|
||||
| POST | `/cart/checkout` | Place order from cart (`addressId` or `shippingAddress` + `payment`). For `e_payment_gate`, also send absolute `returnUrl`; response includes `payment.redirect` for bank redirect |
|
||||
|
||||
#### Payments (public bank callbacks + methods)
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| GET | `/payments/methods` | Enabled gateways for this business (no secrets). Also on `GET /tenants/:host` as `ePayment` |
|
||||
| POST/GET | `/payments/:gateway/callback` | Bank return URL (Mellat first). Verifies/settles, then 303 → `returnUrl?status=…` |
|
||||
|
||||
Gateway credentials live in `businesses.settings.store.ePayment` (dashboard: Website → E-Payment). Adapter registry supports Mellat now; stubs reserved for `sep`, `snappay`, `digipay`, `zarinpal`.
|
||||
|
||||
#### Orders
|
||||
|
||||
@@ -584,7 +593,7 @@ See `.env.example` for the full list. Key groups:
|
||||
|-------|-----------|
|
||||
| Database | `DATABASE_URL`, `POSTGRES_*` |
|
||||
| Redis | `REDIS_URL`, `REDIS_HOST`, `REDIS_PORT` |
|
||||
| API | `PORT` |
|
||||
| API | `PORT`, `API_PUBLIC_BASE_URL` (bank payment callbacks) |
|
||||
| JWT | `JWT_ACCESS_SECRET`, `JWT_REFRESH_SECRET`, `JWT_*_EXPIRES_IN` |
|
||||
| SMS | `SMS_ENABLED`, `SMS_GAMA_BASE_URL`, `SMS_GAMA_USERNAME`, `SMS_GAMA_PASSWORD`, `SMS_GAMA_SOURCE_SERVICE`, `SMS_PARTNERS` |
|
||||
| S3 | `S3_ENDPOINT`, `S3_BUCKET`, `S3_PUBLIC_URL`, `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY` |
|
||||
@@ -608,6 +617,7 @@ See `.env.example` for the full list. Key groups:
|
||||
- Product variation values (which options a product offers)
|
||||
- Store items / product variants (price, stock, SKU)
|
||||
- Shopping cart + checkout + orders (customer + admin)
|
||||
- Online e-payment (Mellat first; multi-gateway registry for SEP / Snapp Pay / DigiPay / Zarinpal)
|
||||
- Product technical info
|
||||
- Category variations & technical forms
|
||||
- Tenant resolution by domain
|
||||
|
||||
@@ -34,6 +34,10 @@ You are building a **Meshkee business website (storefront)**. You must use the M
|
||||
3. Catalog: categories, products, store-items, **user-products** (customer stock listings)
|
||||
4. Auth: register/login → store tokens. Optional: `POST /auth/send-otp` then `POST /auth/login-otp` (passwordless) or `POST /auth/reset-password` (forgot password). `POST /auth/verify-otp` only marks the cell verified (no tokens).
|
||||
5. Cart checkout with `addressId` or inline `shippingAddress` + `payment`
|
||||
- For online pay: `payment.type = "e_payment_gate"`, `gatewayType` (e.g. `"mellat"`), and absolute `returnUrl`
|
||||
- Response includes `payment.redirect` `{ method, url, fields }` — POST/redirect shopper to the bank
|
||||
- Bank callback hits API then redirects to `returnUrl?status=success|failed&orderId=…`
|
||||
- Enabled gateways: `GET /tenants/{domain}` → `ePayment`, or `GET /businesses/{businessId}/payments/methods`
|
||||
|
||||
### User products (customer listings)
|
||||
Public marketplace listings owned by customers — not catalog `products`.
|
||||
|
||||
@@ -767,6 +767,113 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Videos",
|
||||
"item": [
|
||||
{
|
||||
"name": "List published videos (website)",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"exec": [
|
||||
"if (pm.response.code === 200) {",
|
||||
" const json = pm.response.json();",
|
||||
" if (json.items?.[0]?.id) pm.collectionVariables.set('videoId', json.items[0].id);",
|
||||
" if (json.items?.[0]?.slug) pm.collectionVariables.set('videoSlug', json.items[0].slug);",
|
||||
"}"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
}
|
||||
}
|
||||
],
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/tenants/{{domain}}/videos?page=1&pageSize=12",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"tenants",
|
||||
"{{domain}}",
|
||||
"videos"
|
||||
],
|
||||
"query": [
|
||||
{
|
||||
"key": "page",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"key": "pageSize",
|
||||
"value": "12"
|
||||
},
|
||||
{
|
||||
"key": "provider",
|
||||
"value": "",
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"key": "categoryId",
|
||||
"value": "",
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"key": "title",
|
||||
"value": "",
|
||||
"disabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get published video by slug (website)",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "{{baseUrl}}/tenants/{{domain}}/videos/{{videoSlug}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "List video comments (website)",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "{{baseUrl}}/tenants/{{domain}}/videos/{{videoId}}/comments"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Submit video comment (website)",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"exec": [
|
||||
"if (pm.response.code === 200 || pm.response.code === 201) {",
|
||||
" const json = pm.response.json();",
|
||||
" if (json.comment?.id) pm.collectionVariables.set('commentId', json.comment.id);",
|
||||
"}"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
}
|
||||
}
|
||||
],
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"authorName\": \"Video Viewer\",\n \"authorEmail\": \"viewer@example.com\",\n \"text\": \"Great video!\"\n}"
|
||||
},
|
||||
"url": "{{baseUrl}}/tenants/{{domain}}/videos/{{videoId}}/comments"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Portfolios",
|
||||
"item": [
|
||||
@@ -1526,7 +1633,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Checkout cart (saved address)",
|
||||
"name": "Checkout cart (e-payment / Mellat)",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
@@ -1555,9 +1662,59 @@
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"addressId\": \"{{addressId}}\",\n \"customerNotes\": \"Deliver after 5pm\",\n \"payment\": {\n \"type\": \"e_payment_gate\",\n \"gatewayType\": \"zarinpal\"\n }\n}"
|
||||
"raw": "{\n \"addressId\": \"{{addressId}}\",\n \"customerNotes\": \"Deliver after 5pm\",\n \"returnUrl\": \"https://YOUR_WEBSITE_DOMAIN/checkout/result\",\n \"payment\": {\n \"type\": \"e_payment_gate\",\n \"gatewayType\": \"mellat\"\n }\n}"
|
||||
},
|
||||
"url": "{{baseUrl}}/businesses/{{businessId}}/cart/checkout"
|
||||
"url": "{{baseUrl}}/businesses/{{businessId}}/cart/checkout",
|
||||
"description": "Creates a pending order + transaction, calls the gateway, and returns payment.redirect { method, url, fields }. Website must POST/redirect the shopper to the bank. After payment, bank hits /payments/{gateway}/callback which redirects to returnUrl?status=success|failed."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Payments",
|
||||
"item": [
|
||||
{
|
||||
"name": "List payment methods",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": "{{baseUrl}}/businesses/{{businessId}}/payments/methods",
|
||||
"description": "Public. Returns enabled gateways (no secrets). Also available on GET /tenants/{host} as ePayment."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Mellat callback (bank → API)",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/x-www-form-urlencoded"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "urlencoded",
|
||||
"urlencoded": [
|
||||
{
|
||||
"key": "ResCode",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"key": "SaleOrderId",
|
||||
"value": "{{transactionId}}"
|
||||
},
|
||||
{
|
||||
"key": "SaleReferenceId",
|
||||
"value": "123456789"
|
||||
},
|
||||
{
|
||||
"key": "RefId",
|
||||
"value": "{{refId}}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"url": "{{baseUrl}}/businesses/{{businessId}}/payments/mellat/callback",
|
||||
"description": "Called by Mellat (not by the website). Verifies+settles, then 303 redirect to returnUrl."
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+2205
-491
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user