bitsnap 0.0.0-stage → 0.1.1
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/AGENTS.md +110 -0
- package/LICENSE +21 -0
- package/README.md +105 -2
- package/dist/cli.js +1685 -0
- package/package.json +35 -4
package/AGENTS.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# bitsnap CLI for agents
|
|
2
|
+
|
|
3
|
+
`bitsnap` is a CLI for the Bitsnap public API (`/api/v1`). Run it with
|
|
4
|
+
`npx bitsnap …` or install it. Always pass `--json`.
|
|
5
|
+
|
|
6
|
+
## Rules
|
|
7
|
+
|
|
8
|
+
- Authenticate with `BITSNAP_API_KEY` (preferred in agent sandboxes) or
|
|
9
|
+
`bitsnap login --key <key>`. Use a **test key** (`bs_test_…`) unless the user asked
|
|
10
|
+
for live. Never print, log or commit a key; the CLI masks keys as
|
|
11
|
+
`bs_test_…ab12`, keep it that way. Do not put the key in the project's client code.
|
|
12
|
+
- Check first: `bitsnap whoami --json` shows project, `environment`
|
|
13
|
+
(`ENVIRONMENT_TEST` / `ENVIRONMENT_LIVE`), `scopes` and `canWrite`.
|
|
14
|
+
- `--json`: result on stdout, errors on stderr as one line
|
|
15
|
+
`{"error":{"kind":"usage|api|auth|network|timeout","message","detail?","hint?","reason?","code?","status?","requestId?"}}`.
|
|
16
|
+
Branch on `reason` (stable), not on `message`.
|
|
17
|
+
- Exit codes: `0` ok · `1` API or usage error · `2` auth error (no/bad key) · `3` network error.
|
|
18
|
+
- No command prompts. Missing input is a usage error (exit 1).
|
|
19
|
+
- Money is an integer in minor units: `--price 4900` = 49.00 PLN.
|
|
20
|
+
- Writes send an `Idempotency-Key` (random UUID). To make a retry safe, pass the
|
|
21
|
+
same `--idempotency-key <key>` with the same arguments; the API returns the first
|
|
22
|
+
result instead of creating a second product or order. The same key with
|
|
23
|
+
different arguments fails with `idempotency_key_reused`.
|
|
24
|
+
- `--json` prints the API's response body verbatim (Connect JSON: lowerCamelCase,
|
|
25
|
+
enums by name such as `ORDER_STATUS_PAID`, timestamps RFC 3339). Fields may be added.
|
|
26
|
+
- Global options: `--json`, `--host <url>`, `--idempotency-key <key>`, `-h/--help`.
|
|
27
|
+
|
|
28
|
+
## Commands and `--json` output
|
|
29
|
+
|
|
30
|
+
| Command | stdout with `--json` |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| `login --key K` | `GetAccount` fields + `key` (masked), `host`, `configPath` |
|
|
33
|
+
| `logout` | `{ok, removed, configPath}` |
|
|
34
|
+
| `whoami` | `{projectId, projectName, environment, credentialType, scopes[], canWrite, key (masked), keySource: "env"\|"config", host}` |
|
|
35
|
+
| `products list [--search t] [--limit 1-100] [--drafts] [--cursor c]` | `{products: Product[], nextCursor}` (`nextCursor` `""` = last page) |
|
|
36
|
+
| `products get ID` | `{product}` |
|
|
37
|
+
| `products create --name N --price MINOR [--currency PLN] [--status draft\|published\|unpublished] [--stock n] [--image URL]…` | `{product}` (draft unless `--status`) |
|
|
38
|
+
| `orders list [--status paid]… [--limit n] [--cursor c]` | `{orders: Order[], nextCursor, totalCount}` |
|
|
39
|
+
| `orders get ID` | `{order}` |
|
|
40
|
+
| `coupons list` | `{coupons: Coupon[]}` |
|
|
41
|
+
| `checkout create --product ID [--variant ID] [--quantity n] [--success-url U] [--cancel-url U] [--email E] [--reference R]` | `{url, orderId, environment}` (without `--json` only the URL is printed) |
|
|
42
|
+
| `payments settle ORDER_ID [--fail]` | `{orderId, outcome: "TEST_PAYMENT_OUTCOME_PAID"\|"TEST_PAYMENT_OUTCOME_FAILED"}`; exit 1 locally for live keys |
|
|
43
|
+
| `webhooks list` | `{webhooks: Webhook[]}` |
|
|
44
|
+
| `webhooks create --url HTTPS_URL [--name N] [--event E]…` | `{webhook, signingSecret}` (secret shown once; omit `--event` for all) |
|
|
45
|
+
| `webhooks delete ID` | `{}` |
|
|
46
|
+
| `webhooks listen [--forward URL] [--poll] [--max-events n] [--timeout s]` | NDJSON, see below |
|
|
47
|
+
| `webhooks trigger [EVENT]` | `{eventId, event, deliveredTo}` (default `TRANSACTION_SUCCESS`) |
|
|
48
|
+
| `openapi [--format yaml\|json] [--out F]` | the document on stdout (json with `--json`); with `--out`: `{ok, path, format, bytes}` |
|
|
49
|
+
| `stats [--from D] [--to D]` | `{from, to, orders, unitsSold, revenue: Money[], daily: [{date, units, revenue}]}`; `D` is `YYYY-MM-DD` or RFC 3339, a plain `--to` date covers that whole day |
|
|
50
|
+
|
|
51
|
+
`Product`: `{id, name, status: PRODUCT_STATUS_DRAFT|UNPUBLISHED|PUBLISHED, price?: {amount, currency}, availableQuantity?, images[], variants[{id, name, price?, availableQuantity?}], revision, createdAt, updatedAt}`.
|
|
52
|
+
`Order`: `{id, status: ORDER_STATUS_*, environment, createdAt, customer{email,name,phone}, items[{productId,name,quantity,unitPrice}], total, paymentGateway, couponCode, deliveryPrice?}`.
|
|
53
|
+
Order statuses for `--status`: `created paid unpaid canceled sent completed issue returned in_progress`.
|
|
54
|
+
|
|
55
|
+
### `webhooks listen`
|
|
56
|
+
|
|
57
|
+
Test keys only. One JSON object per line:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{"type":"ready","secret":"whsec_…","projectId":"…","host":"https://bitsnap.pl","mode":"sse"}
|
|
61
|
+
{"type":"event","id":"evt_1","event":"TRANSACTION_SUCCESS","signature":"<hex hmac-sha512>","payload":"<exact JSON body string>","forward":{"url":"http://localhost:3000/api/webhooks","status":200,"ok":true,"durationMs":12}}
|
|
62
|
+
{"type":"status","state":"reconnecting"}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`forward` is present with `--forward`; on a connection failure it is
|
|
66
|
+
`{"url","error","durationMs"}`; a non-2xx answer adds `body`. `payload` is the exact
|
|
67
|
+
string that was signed; `JSON.parse` it, never re-serialize it before verifying.
|
|
68
|
+
The signature is HMAC-SHA512 (hex) of `payload` with the signing secret, sent as
|
|
69
|
+
`X-Content-Signature` and `?sig=`. The command runs until Ctrl+C; use
|
|
70
|
+
`--max-events 1 --timeout 120` to wait for a single event (exit 0 when received,
|
|
71
|
+
exit 1 `kind: "timeout"` when not). Tip: run it in the background, trigger the
|
|
72
|
+
event (`bitsnap webhooks trigger` or `bitsnap payments settle`), then read the output.
|
|
73
|
+
|
|
74
|
+
## Error reasons and what to do
|
|
75
|
+
|
|
76
|
+
| `reason` | Meaning | Do |
|
|
77
|
+
| --- | --- | --- |
|
|
78
|
+
| `missing_credentials`, `invalid_credentials` (exit 2) | No/bad key | Ask the user for a key from Settings → Developer |
|
|
79
|
+
| `writes_disabled` | Key has writes off | Ask the user to turn on writes for the key in Settings → Developer |
|
|
80
|
+
| `missing_scope` (`metadata.scope`) | Key lacks the scope | Ask the user to add the scope |
|
|
81
|
+
| `test_mode_only` | Command needs a test key | Use a `bs_test_…` key |
|
|
82
|
+
| `invalid_request` (`metadata.field`) | Bad argument | Fix the field named in `detail` |
|
|
83
|
+
| `*_not_found` | Wrong id or environment | List first (`products list`, `orders list`) |
|
|
84
|
+
| `product_not_purchasable`, `checkout_unavailable` | Product is a draft / no stock / shop has no gateway | Publish the product; set up the test gateway in the dashboard |
|
|
85
|
+
| `order_not_settleable` | Not an open test order of the Bitsnap test gateway (cancelled, expired, live, or another gateway). Settling twice is not an error: it returns the first outcome | Create a new checkout with a test key |
|
|
86
|
+
| `idempotency_key_in_progress` | Same key still running | Retry in seconds with the same key |
|
|
87
|
+
| `rate_limited`, `daily_write_limit`, `service_unavailable`, `internal` | Server side limits | Wait and retry (`daily_write_limit` resets 00:00 UTC) |
|
|
88
|
+
|
|
89
|
+
## Recipes
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
# 1. Is the key right?
|
|
93
|
+
bitsnap whoami --json
|
|
94
|
+
|
|
95
|
+
# 2. A product to sell (published so it can be bought)
|
|
96
|
+
bitsnap products create --name "Ebook" --price 4900 --status published --json
|
|
97
|
+
|
|
98
|
+
# 3. Buy it in test mode without a card
|
|
99
|
+
URL_JSON=$(bitsnap checkout create --product "$PRODUCT_ID" --success-url http://localhost:3000/thanks --json)
|
|
100
|
+
bitsnap payments settle "$ORDER_ID" --json
|
|
101
|
+
|
|
102
|
+
# 4. See the webhook arrive in the user's dev server
|
|
103
|
+
bitsnap webhooks listen --forward http://localhost:3000/api/webhooks --json --max-events 1 --timeout 120 &
|
|
104
|
+
bitsnap payments settle "$ORDER_ID" --json
|
|
105
|
+
wait
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Set `BITSNAP_WEBHOOK_SECRET` in the app's local env to the `secret` of the `ready`
|
|
109
|
+
record. The `bitsnap-js` package verifies the forwarded request unchanged
|
|
110
|
+
(`handleWebhook(rawBody, request.url, headers, secret)`).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Piotr Michalik
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,106 @@
|
|
|
1
|
-
#
|
|
1
|
+
# bitsnap
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Command-line tool for the [Bitsnap](https://bitsnap.pl) public API. Create
|
|
4
|
+
products, make test checkout links, settle test payments and forward test
|
|
5
|
+
webhooks to your local dev server, from a terminal or from an AI coding agent.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npx bitsnap --help
|
|
9
|
+
npm install -g bitsnap # or: bun add -g bitsnap
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Needs Node.js 20 or newer. No runtime dependencies.
|
|
13
|
+
|
|
14
|
+
## Log in
|
|
15
|
+
|
|
16
|
+
Create an API key in **Settings → Developer** of your Bitsnap dashboard. Use a
|
|
17
|
+
**test key** (`bs_test_…`) while you build: test orders move no money.
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
bitsnap login --key bs_test_…
|
|
21
|
+
bitsnap whoami
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`login` checks the key with the API and stores it in
|
|
25
|
+
`~/.config/bitsnap/config.json` (or `$XDG_CONFIG_HOME/bitsnap/config.json`) with
|
|
26
|
+
mode `0600`. `BITSNAP_API_KEY` always wins over the stored key, which is the
|
|
27
|
+
better choice in CI and for agents. `--host` / `BITSNAP_HOST` change the API
|
|
28
|
+
host (default `https://bitsnap.pl`). A key is never printed in full
|
|
29
|
+
(`bs_test_…ab12`). `bitsnap logout` deletes the stored key.
|
|
30
|
+
|
|
31
|
+
Writes (create product, checkout links, webhooks) need the key's write switch
|
|
32
|
+
and the matching scope; if they are off the CLI says so and where to turn them
|
|
33
|
+
on.
|
|
34
|
+
|
|
35
|
+
## Commands
|
|
36
|
+
|
|
37
|
+
| Command | What it does |
|
|
38
|
+
| --- | --- |
|
|
39
|
+
| `login --key <key>` / `logout` / `whoami` | Store, remove or inspect the credentials. |
|
|
40
|
+
| `products list [--search] [--limit] [--drafts] [--cursor]` | List products. |
|
|
41
|
+
| `products get <id>` | One product with its variants. |
|
|
42
|
+
| `products create --name --price <minor units> [--currency PLN] [--status draft\|published\|unpublished] [--stock n] [--image url]…` | Create a product. `--price 4900` is 49.00 PLN. |
|
|
43
|
+
| `orders list [--status paid]… [--limit] [--cursor]` / `orders get <id>` | Orders of the key's environment. |
|
|
44
|
+
| `coupons list` | Coupons and their codes. |
|
|
45
|
+
| `checkout create --product <id> [--variant] [--quantity] [--success-url] [--cancel-url] [--email]` | Prints the checkout URL. |
|
|
46
|
+
| `payments settle <orderId> [--fail]` | Settle a **test** order as paid (or failed). Refused locally for live keys. |
|
|
47
|
+
| `webhooks list` / `create --url <https>` / `delete <id>` | Manage webhook endpoints. |
|
|
48
|
+
| `webhooks listen [--forward <url>]` | Stream test webhook events; forward them to your local server. |
|
|
49
|
+
| `webhooks trigger [event]` | Send a sample event (default `TRANSACTION_SUCCESS`). |
|
|
50
|
+
| `openapi [--format yaml\|json] [--out file]` | Download the OpenAPI document (no key needed). |
|
|
51
|
+
| `stats [--from] [--to]` | Sales summary. |
|
|
52
|
+
|
|
53
|
+
`bitsnap <command> --help` shows every option.
|
|
54
|
+
|
|
55
|
+
## Test a purchase end to end
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
bitsnap products create --name "Ebook" --price 4900 --status published
|
|
59
|
+
bitsnap checkout create --product prodv2_… --success-url http://localhost:3000/thanks
|
|
60
|
+
# open the URL, or skip paying and settle the test order:
|
|
61
|
+
bitsnap payments settle <orderId>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Receive webhooks on localhost
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
bitsnap webhooks listen --forward http://localhost:3000/api/webhooks
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
It prints the signing secret; set it as `BITSNAP_WEBHOOK_SECRET` in your app. Each
|
|
71
|
+
event is `POST`ed to your URL with the exact payload as body, the HMAC-SHA512
|
|
72
|
+
signature in `X-Content-Signature` and also as `?sig=`, so your normal webhook
|
|
73
|
+
verification (for example `handleWebhook` from `bitsnap-js`) works unchanged. The
|
|
74
|
+
stream reconnects by itself (with `Last-Event-ID`) and falls back to long
|
|
75
|
+
polling if the event stream is blocked. `bitsnap webhooks trigger` sends a
|
|
76
|
+
sample event so you can see it arrive.
|
|
77
|
+
|
|
78
|
+
## For scripts and agents
|
|
79
|
+
|
|
80
|
+
- `--json` on any command: result as JSON on stdout. Errors go to stderr as
|
|
81
|
+
`{"error":{"kind","message","hint","reason",…}}`.
|
|
82
|
+
- Exit codes: `0` ok, `1` API or usage error, `2` auth error, `3` network error.
|
|
83
|
+
- The CLI never asks questions.
|
|
84
|
+
- Writes send an `Idempotency-Key` (random UUID; set it with `--idempotency-key`),
|
|
85
|
+
so a retry cannot create a second product or order.
|
|
86
|
+
- `bitsnap --help --json` lists every command and option.
|
|
87
|
+
|
|
88
|
+
See [AGENTS.md](./AGENTS.md) for the JSON shapes and recipes.
|
|
89
|
+
|
|
90
|
+
## Develop
|
|
91
|
+
|
|
92
|
+
This package lives in the Bitsnap monorepo as `packages/cli` (workspace name
|
|
93
|
+
`@bitsnap/cli`; the published name is `bitsnap`).
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
bun run typecheck && bun test && bun run build
|
|
97
|
+
node dist/cli.js --help
|
|
98
|
+
bun run release:prepare # assembles release/ with the public `bitsnap` manifest
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Releases go out through the `Publish CLI` workflow
|
|
102
|
+
(`.github/workflows/publish.yml`): bump the version in `package.json`, commit,
|
|
103
|
+
then tag and push `cli-vX.Y.Z`. The workflow authenticates with npm trusted
|
|
104
|
+
publishing, so no token is involved.
|
|
105
|
+
|
|
106
|
+
MIT licensed.
|