{"info":{"_postman_id":"8e26b295-1418-4ee9-8648-f9048c95de86","name":"E-commerce Ordering & Payment System API","description":"<html><head></head><body><p>Full API reference for the Django/DRF backend: users/auth, products/categories, orders, payments (Stripe + bKash). Run requests top-to-bottom within a folder — auth, product, order, and payment IDs auto-save into collection variables via each request's Tests script.</p>\n</body></html>","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","toc":[],"owner":"47893159","collectionId":"8e26b295-1418-4ee9-8648-f9048c95de86","publishedId":"2sBY4SLJMb","public":true,"customColor":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"FF6C37"},"publishDate":"2026-07-27T18:26:38.000Z"},"item":[{"name":"Users & Auth","item":[{"name":"Register","event":[{"listen":"test","script":{"id":"36bafb01-d31b-4f59-b022-e0ad08021e4a","exec":["const data = pm.response.json();","if (data.access) { pm.collectionVariables.set('access_token', data.access); }","if (data.refresh) { pm.collectionVariables.set('refresh_token', data.refresh); }"],"type":"text/javascript","packages":{},"requests":{}}}],"id":"235a1ad8-7089-41bd-8932-fcb3558a165b","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"email\": \"buyer22@example.com\",\n  \"username\": \"buyer22\",\n  \"password\": \"testpass123\",\n  \"password_confirm\": \"testpass123\"\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8000/api/users/register/","description":"<p>Returns { user, access, refresh } — registering logs you in immediately, no separate Login call needed. access_token/refresh_token are auto-saved by this request's Tests script.</p>\n","urlObject":{"path":["api","users","register",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"235a1ad8-7089-41bd-8932-fcb3558a165b"},{"name":"Login","event":[{"listen":"test","script":{"type":"text/javascript","exec":["const data = pm.response.json();","if (data.access) { pm.collectionVariables.set('access_token', data.access); }","if (data.refresh) { pm.collectionVariables.set('refresh_token', data.refresh); }"],"id":"099b1a3a-2e56-47a5-8433-7f47aa0386a8"}}],"id":"4e6ce466-4497-40ec-8de2-f557f49ff0d2","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"email\": \"buyer1@example.com\",\n  \"password\": \"testpass123\"\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8000/api/users/login/","urlObject":{"path":["api","users","login",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"4e6ce466-4497-40ec-8de2-f557f49ff0d2"},{"name":"Login as Admin (seeded)","event":[{"listen":"test","script":{"type":"text/javascript","exec":["const data = pm.response.json();","if (data.access) { pm.collectionVariables.set('admin_access_token', data.access); }"],"id":"05f420a7-34b0-48d0-8e16-aa261abf25e2"}}],"id":"cca980f1-e630-434f-a6db-205300a10b1e","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"email\": \"admin@example.com\",\n  \"password\": \"admin12345\"\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8000/api/users/login/","description":"<p>Uses the account created by <code>python manage.py seed_admin</code>. Saved separately as <code>admin_access_token</code> so you can still use the regular <code>access_token</code> (buyer) for order/payment requests.</p>\n","urlObject":{"path":["api","users","login",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"cca980f1-e630-434f-a6db-205300a10b1e"},{"name":"Refresh Token","event":[{"listen":"test","script":{"type":"text/javascript","exec":["const data = pm.response.json();","if (data.access) { pm.collectionVariables.set('access_token', data.access); }","if (data.refresh) { pm.collectionVariables.set('refresh_token', data.refresh); }"],"id":"56ce4def-6659-40a8-b3e1-609f61b468a3"}}],"id":"2b96aae2-ba92-4a19-bd96-02265a2cd082","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"refresh\": \"\"\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8000/api/users/login/refresh/","description":"<p>ROTATE_REFRESH_TOKENS is on, so this also returns a new refresh token (saved automatically) — the old refresh token becomes invalid after this call.</p>\n","urlObject":{"path":["api","users","login","refresh",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"2b96aae2-ba92-4a19-bd96-02265a2cd082"},{"name":"Get My Profile","id":"7ba2f6fd-878f-4bd0-994f-34551d581359","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[{"key":"Authorization","value":"Bearer "}],"url":"http://localhost:8000/api/users/me/","urlObject":{"path":["api","users","me",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"7ba2f6fd-878f-4bd0-994f-34551d581359"},{"name":"Update My Profile","id":"a3536f9a-cfdb-4e2d-a8d9-4e5c2f3a3bd0","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"},{"key":"Authorization","value":"Bearer "}],"body":{"mode":"raw","raw":"{\n  \"first_name\": \"Riyan\",\n  \"last_name\": \"One\"\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8000/api/users/me/","description":"<p>email/username are read-only here — only first_name/last_name are editable.</p>\n","urlObject":{"path":["api","users","me",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"a3536f9a-cfdb-4e2d-a8d9-4e5c2f3a3bd0"},{"name":"Logout","id":"6da565ed-2951-4bd4-a40c-20c32e62f125","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"},{"key":"Authorization","value":"Bearer "}],"body":{"mode":"raw","raw":"{\n  \"refresh\": \"\"\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8000/api/users/logout/","description":"<p>Blacklists the refresh token. Re-using it afterward on Refresh Token will fail with 401.</p>\n","urlObject":{"path":["api","users","logout",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"6da565ed-2951-4bd4-a40c-20c32e62f125"}],"id":"3d5ab387-3233-4dce-97c8-044e3f0a0343","_postman_id":"3d5ab387-3233-4dce-97c8-044e3f0a0343","description":""},{"name":"Products & Categories","item":[{"name":"List Products","id":"bd4e66f2-a619-40cf-aaab-8f0f28a1eeed","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[],"url":"http://localhost:8000/api/products/","description":"<p>Public — no auth required.</p>\n","urlObject":{"path":["api","products",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"bd4e66f2-a619-40cf-aaab-8f0f28a1eeed"},{"name":"Get Category Tree","id":"efbb957e-32aa-42b5-ae71-0a394517c368","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[],"url":"http://localhost:8000/api/products/categories/tree/","description":"<p>Public. Cached in Redis for 1 hour. There is no API endpoint to create categories — use Django admin (<code>http://localhost:8000/admin/</code>, login <a href=\"mailto:admin@example.com\">admin@example.com</a> / admin12345) or <code>python manage.py seed_products</code>. Copy an <code>id</code> from this response into the <code>category_id</code> collection variable to use in 'Create Product' below.</p>\n","urlObject":{"path":["api","products","categories","tree",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"efbb957e-32aa-42b5-ae71-0a394517c368"},{"name":"Create Product (Admin only)","event":[{"listen":"test","script":{"type":"text/javascript","exec":["const data = pm.response.json();","if (data.id) { pm.collectionVariables.set('product_id', data.id); }"],"id":"67ab564a-457b-4f1e-8c5a-5d6d3319eb73"}}],"id":"7b3ef5e9-e755-43ec-8960-eee795569e4d","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"},{"key":"Authorization","value":"Bearer "}],"body":{"mode":"raw","raw":"{\n  \"category\": \"\",\n  \"name\": \"Wireless Mouse\",\n  \"sku\": \"WM-100\",\n  \"description\": \"Ergonomic wireless mouse\",\n  \"price\": \"25.00\",\n  \"stock\": 50,\n  \"status\": \"active\"\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8000/api/products/","description":"<p>Requires an admin (is_staff) token — use 'Login as Admin' first. <code>category</code> must be a real Category id (see 'Get Category Tree').</p>\n","urlObject":{"path":["api","products",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"7b3ef5e9-e755-43ec-8960-eee795569e4d"},{"name":"Get Product Detail","id":"f5ead734-5ab7-40aa-a8e9-03c24b35b854","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[],"url":"http://localhost:8000/api/products//","description":"<p>Public.</p>\n","urlObject":{"path":["api","products","",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"f5ead734-5ab7-40aa-a8e9-03c24b35b854"},{"name":"Update Product (Admin only)","id":"ba803927-4be0-4d42-b5d4-c2ae66705b25","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"},{"key":"Authorization","value":"Bearer "}],"body":{"mode":"raw","raw":"{\n  \"price\": \"22.50\",\n  \"stock\": 45\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8000/api/products//","urlObject":{"path":["api","products","",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"ba803927-4be0-4d42-b5d4-c2ae66705b25"},{"name":"Get Product Recommendations","id":"966fdf7f-b683-49ec-8afa-86203c408ebd","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[],"url":"http://localhost:8000/api/products//recommendations/","description":"<p>Public. Returns other products in the same category subtree (DFS over the cached category tree).</p>\n","urlObject":{"path":["api","products","","recommendations",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"966fdf7f-b683-49ec-8afa-86203c408ebd"},{"name":"Delete Product (Admin only)","id":"c2595774-99db-44a3-bf09-fa53a5864ae3","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"DELETE","header":[{"key":"Authorization","value":"Bearer "}],"url":"http://localhost:8000/api/products//","description":"<p>Run this last — it deletes the product you just created/tested with, so run it after the Orders/Payments folders if you're using this same product_id there.</p>\n","urlObject":{"path":["api","products","",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"c2595774-99db-44a3-bf09-fa53a5864ae3"}],"id":"b0f0bde1-913a-405f-b2fb-e82a3fc36f8d","_postman_id":"b0f0bde1-913a-405f-b2fb-e82a3fc36f8d","description":""},{"name":"Orders","item":[{"name":"Create Order","event":[{"listen":"test","script":{"type":"text/javascript","exec":["const data = pm.response.json();","if (data.id) { pm.collectionVariables.set('order_id', data.id); }"],"id":"55bbb617-8e1c-443f-a22e-dce78cc575fa"}}],"id":"8da13ff1-6480-4a47-8e76-02e3c66015f7","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"},{"key":"Authorization","value":"Bearer "}],"body":{"mode":"raw","raw":"{\n  \"items\": [\n    { \"product_id\": \"\", \"quantity\": 2 }\n  ]\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8000/api/orders/","description":"<p>user_id is never sent — the order is created for whoever the access_token belongs to. total_amount is computed server-side (quantity x price), never trusted from the client. Stock is NOT reduced yet — only after a successful payment.</p>\n","urlObject":{"path":["api","orders",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"8da13ff1-6480-4a47-8e76-02e3c66015f7"},{"name":"List My Orders","id":"12c5a9af-27e0-423e-9c78-6f5418fc043f","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[{"key":"Authorization","value":"Bearer "}],"url":"http://localhost:8000/api/orders/","description":"<p>Scoped to the authenticated user (staff/admin tokens see every order).</p>\n","urlObject":{"path":["api","orders",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"12c5a9af-27e0-423e-9c78-6f5418fc043f"}],"id":"63ebb6e4-ad4e-46e7-af9e-74e23a202354","_postman_id":"63ebb6e4-ad4e-46e7-af9e-74e23a202354","description":""},{"name":"Payments","item":[{"name":"Checkout — Stripe","event":[{"listen":"test","script":{"type":"text/javascript","exec":["const data = pm.response.json();","if (data.payment_id) { pm.collectionVariables.set('payment_id', data.payment_id); }"],"id":"f2209a49-cc9c-4f1f-b727-81b68cb5636f"}}],"id":"817a989e-749d-4988-add0-d04ce99d2265","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"},{"key":"Authorization","value":"Bearer "}],"body":{"mode":"raw","raw":"{\n  \"order_id\": \"\",\n  \"provider\": \"stripe\"\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8000/api/payments/checkout/","description":"<p>Returns a Stripe <code>client_secret</code> — complete the PaymentIntent client-side (or via Stripe's test tools), then Stripe calls your webhook, which marks the payment/order paid and reduces stock. order_id must belong to the authenticated user (others' orders 404).</p>\n","urlObject":{"path":["api","payments","checkout",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"817a989e-749d-4988-add0-d04ce99d2265"},{"name":"Checkout — bKash","event":[{"listen":"test","script":{"type":"text/javascript","exec":["const data = pm.response.json();","if (data.payment_id) { pm.collectionVariables.set('payment_id', data.payment_id); }"],"id":"637cfe94-5244-4e89-89b6-9c3730665322"}}],"id":"3f9aeebd-89cf-4f0f-b235-e68c64fc656e","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"},{"key":"Authorization","value":"Bearer "}],"body":{"mode":"raw","raw":"{\n  \"order_id\": \"\",\n  \"provider\": \"bkash\"\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8000/api/payments/checkout/","description":"<p>Returns a bkash_url — open it to complete the sandbox payment, which redirects to the callback endpoint below and marks the payment/order paid + reduces stock. IMPORTANT: call this endpoint through your ngrok URL (not localhost) if you want bKash's sandbox servers to be able to reach the callback.</p>\n","urlObject":{"path":["api","payments","checkout",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"3f9aeebd-89cf-4f0f-b235-e68c64fc656e"},{"name":"List My Payments","id":"2c239f94-8f6d-4644-afe9-54920f7e4250","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[{"key":"Authorization","value":"Bearer "}],"url":"http://localhost:8000/api/payments/","description":"<p>Scoped to the authenticated user (staff/admin tokens see every payment).</p>\n","urlObject":{"path":["api","payments",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"2c239f94-8f6d-4644-afe9-54920f7e4250"},{"name":"Get Payment Detail","id":"7f28d7e0-5c2b-42ed-8fbe-d6445973d508","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[{"key":"Authorization","value":"Bearer "}],"url":"http://localhost:8000/api/payments//","urlObject":{"path":["api","payments","",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"7f28d7e0-5c2b-42ed-8fbe-d6445973d508"},{"name":"Stripe Webhook (reference only — Stripe calls this, not Postman)","id":"10fe2f63-2d4e-4360-a9c2-18bed1470694","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"},{"key":"Stripe-Signature","value":"t=timestamp,v1=signature"}],"body":{"mode":"raw","raw":"{\n  \"type\": \"payment_intent.succeeded\",\n  \"data\": {\n    \"object\": {\n      \"id\": \"pi_xxx\",\n      \"status\": \"succeeded\"\n    }\n  }\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8000/api/payments/webhook/stripe/","description":"<p>Signature-verified — Stripe's servers sign this payload with STRIPE_WEBHOOK_SECRET, so sending it manually from Postman will fail with 400 (invalid signature) unless you compute a real HMAC signature. Included here to document the payload shape. Real end-to-end testing: register this URL (behind your ngrok tunnel) in the Stripe Dashboard and let Stripe call it for real, as already verified.</p>\n","urlObject":{"path":["api","payments","webhook","stripe",""],"host":["http://localhost:8000"],"query":[],"variable":[]}},"response":[],"_postman_id":"10fe2f63-2d4e-4360-a9c2-18bed1470694"},{"name":"bKash Callback (reference — bKash redirects here after sandbox payment)","id":"fa3150aa-f662-4385-8357-7e2b8d763a0b","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[],"url":"http://localhost:8000/api/payments/bkash/callback/?paymentID=bkash_txn_id&status=success","description":"<p>Normally reached via redirect from bKash's sandbox checkout page after Checkout — bKash, not called directly. paymentID must match a real Payment.transaction_id created by a prior bKash checkout.</p>\n","urlObject":{"path":["api","payments","bkash","callback",""],"host":["http://localhost:8000"],"query":[{"key":"paymentID","value":"bkash_txn_id"},{"key":"status","value":"success"}],"variable":[]}},"response":[],"_postman_id":"fa3150aa-f662-4385-8357-7e2b8d763a0b"}],"id":"d02dd278-fd49-4f02-b18b-11f9cfc7f6b0","_postman_id":"d02dd278-fd49-4f02-b18b-11f9cfc7f6b0","description":""}],"event":[{"listen":"prerequest","script":{"id":"61055f96-2b85-4b6a-a690-4ad18669ddfc","type":"text/javascript","packages":{},"requests":{},"exec":[""]}},{"listen":"test","script":{"id":"ad13a0a7-7a64-4531-832d-70054c4da247","type":"text/javascript","packages":{},"requests":{},"exec":[""]}}],"variable":[{"key":"base_url","value":"http://localhost:8000"},{"key":"access_token","value":""},{"key":"refresh_token","value":""},{"key":"admin_access_token","value":""},{"key":"category_id","value":""},{"key":"product_id","value":""},{"key":"order_id","value":""},{"key":"payment_id","value":""}]}