# Tabby simulator: Postman collection

A single-file collection for demoing and testing the simulator without curl. 13 folders, 99 requests, each with **Tests** that assert the status code
and the key fields or error text, so the Collection Runner shows pass or fail.

Files:

| File | What it is |
|---|---|
| `tabby_simulator.postman_collection.json` | The collection (Postman v2.1) |

## Import

1. Postman, **Import**, drop `tabby_simulator.postman_collection.json`. That is all: the variables and their values are inside the
   collection, no environment is needed (leave the top-right dropdown on "No Environment", or an environment with the same names would override them).
2. Open the collection, **Variables** tab, and check `base_url` (see below) and `admin_token`. The shipped `admin_token` is the test value
   `admin-test-123`; it must equal the `TABBY_SIM_ADMIN_TOKEN` the server was started with. Use a different value in any shared setup.
3. If you edit a value, fill the **Current value** column and **Save**.

## Importing: the "Unlock your vault" prompt

Postman may say it found secrets and ask for a vault key. Nothing in the collection is a real secret. Close the dialog (X) or choose to continue without the vault; you do not need to unlock it. If an import seems stuck or you imported twice, delete the extra "Tabby Simulator" collection and import once.

## Variables

| Variable | Value | Notes |
|---|---|---|
| `base_url` | `http://78.47.213.209:4030/tabby-sim` | The simulator, including `/tabby-sim`. Change it to match your server. Plain HTTP |
| `secret_key` | `sk_test_000000000` | The simulator's test key (any `sk_test_...` works). Sent as `Authorization: Bearer {{secret_key}}` by the whole collection |
| `merchant_code` | `demo` | Sent in `merchant_code` and `X-Merchant-Code`. The simulator allows 4 webhooks per merchant code |
| `admin_user` | `admin` | Any name works; the admin page checks only the password |
| `admin_token` | `admin-test-123` | Basic-auth password for `/admin`; must match the server's `TABBY_SIM_ADMIN_TOKEN` |
| `receiver_url` | `http://localhost:9099/ok` | Where the **server** sends webhooks. `localhost` is the server itself, not your PC |
| `session_id`, `payment_id`, `web_url`, `form_token`, `delivery_id`, `fault_id` | filled automatically | By the Tests scripts |

All of these are collection variables. `wrong_key` (a deliberately wrong API key, used only by "Wrong key on retrieve" in folder 9; it is a variable because Postman's secret scanner flags any literal value typed into an auth field) Further ids for the replay and paging checks are created by the scripts.

## What you need running

- The simulator: `TABBY_SIM_ENABLED=true`, migrations applied (including `tabby_sim_faults`).
- **Folders 10 and 11:** the server started with `TABBY_SIM_ADMIN_TOKEN=<same value as admin_token>`.
- **Folder 11 and the webhooks demo:** a receiver listening where `receiver_url` points, for example
  `python3 test/tabby_sim/scripts/webhook_receiver.py 9099` on the server. It prints every webhook; Postman cannot show webhooks.
- **Folder 12:** short timers, for example `TABBY_SIM_SESSION_TTL_MIN=1 TABBY_SIM_EXPIRE_AFTER_MIN=2 TABBY_SIM_SEND_EXPIRED_WEBHOOK=true`.
  Expect `TABBY_SIM_SEND_EXPIRED_WEBHOOK=true` to make the receiver print an `expired` webhook.

## How to run

Send a request with **Send**, or run a folder: right-click it, **Run folder**. In the runner set a delay of about 300 ms between requests.
Run the folders in order for a demo; every folder from 1 to 7 and 10 to 12 creates its own payment, so after folder 0 they can also be run alone.
Folder 8 (list) needs payments from folders 1 to 7, so run those first. Each request's **description** says what it shows and which Tabby
doc page it is based on.

| Folder | Shows | Receiver prints |
|---|---|---|
| 0. Setup | Register the webhook, list it, get the admin form token | |
| 1. Successful payment | Create session, hosted page, approve with OTP 8888, retrieve, capture, retrieve | three: `authorized` after the approval; `authorized` after the capture (`captures` filled, no `closed_at`); then `closed` (`closed_at` set) |
| 2. Pre-scoring reject | Phone +971500000002 is rejected up front, no hosted page | |
| 3. Customer cancels | "Back to Store" redirects to the cancel URL, payment EXPIRED | `expired` if enabled |
| 4. Tabby rejects on the page | `otp.rejected@tabby.ai`, redirect to the failure URL, REJECTED | `rejected` |
| 5. Customer closes the tab | Approve with no redirect, still AUTHORIZED | `authorized` |
| 6. Partial capture, refund, idempotency | Capture 60, replay, over-capture 400, remaining 40, refunds | one per real change; the capture that completes the amount sends two (`authorized`, then `closed`) |
| 7. Close and update | `PUT` order.reference_id, close, close again 400 | `update`, `closed` |
| 8. List payments | Default, `new`, `captured`, `cancelled`, `refunded`, `rejected`, paging, errors | |
| 9. Errors | 401 texts, 404, 400s, 403 `no_permission` for a `token` | |
| 10. Admin fault injection | 409 lock, 409 disputed, 500 before and after, remove a fault, refused combination | |
| 11. Admin webhook tools | Resend (delayed), out of order, force status | duplicates and reversed order |
| 12. Expiry | Hosted page 410, payment EXPIRED | `expired` |

## Notes

- **Result pages:** the `merchant_urls` in every checkout body are `{{base_url}}/merchant/success`, `/cancel` and `/failure`, simulator pages that show a green, amber or red card with the `payment_id` and the payment's current status. Approving, cancelling or rejecting in a browser therefore ends on a page you can tell apart. They need the simulator restarted after this change; on an older server the hosted-page redirects would end on a 404 and the Location tests would still pass.

- **Hosted-page POSTs** (approve, approve without redirect, back to store, reject) are form-encoded and **do not follow redirects**, so the
  302 and its `Location: ...?payment_id=...` are visible in the response headers. Browsers follow it; Postman is told not to.
- **Admin POSTs** need the hidden form token. The request **Get admin form token** (folders 0, 10, 11) reads it from the page and saves it as
  `form_token`. It is valid for one hour; fetch a new one if you get `400 Invalid or expired form token`.
- **Webhook limit:** 4 per merchant code. Running folder 0 again after that shows the documented 400 `bad_request`; the test accepts both.
- **Timing:** webhooks are sent by a background job, so a few requests wait about 3 seconds (a pre-request script) before looking for them.
- **Folder 12** fails if you run it before the timers have elapsed; wait and send the last request again.
- **Tabby docs** each request is based on are linked in its description; the full list of simulator behaviour is in `lib/da_product_app/tabby_sim/README.md`.
- **Security:** the server runs plain HTTP and the admin page uses Basic auth, so the password travels unencrypted. Use a long random
  `admin_token` and keep the port restricted (see "Admin page" in the simulator README).
