Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This repository is the consolidation point for the CLI/API-access programs that
- `Guardian.idric` — Guardian API checkpoint.
- `Nyt.idric` — New York Times API checkpoint.
- `Reddit.idric` — Reddit Data API checkpoint, with a synthetic fixture and manual receipt.
- `Stripe.idric` — Stripe API checkpoint; first slice is pinned, read-only Balance access with a synthetic fixture and manual receipt.
- `Reuters.idric` — Reuters GraphQL checkpoint.
- `Wayback.idric` — Internet Archive Wayback/CDX checkpoint.

Expand All @@ -27,6 +28,6 @@ Where these clients need networking, ICU/Idric-Net remains the intended transpor

## Tests

`make test` runs the existing Amazon and AbeBooks smoke tests. Reddit has a separate manual compiler checkpoint at `checkpoints/reddit/check`; it is not part of `make test` while named Idriç holes remain.
`make test` runs the existing Amazon and AbeBooks smoke tests. Reddit and Stripe have separate manual compiler checkpoints under `checkpoints/reddit/check` and `checkpoints/stripe/check`; they are not part of `make test` while named Idriç holes remain.

See `PROVENANCE.md` for the source branches copied into this repository.
1 change: 1 addition & 0 deletions Stripe.idric
131 changes: 131 additions & 0 deletions checkpoints/stripe/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Stripe CLI compiler checkpoint

This directory is the Idriç Stripe CLI checkpoint. Idriç is the implementation target; ICU/Idric-Net owns HTTP transport.

The first slice is deliberately read-only. Stripe has many operations that create, capture, refund, invoice, or pay out money. Those do not belong in the first transport/compiler checkpoint.

## Command contract

```text
stripe version
stripe url balance
stripe fixture FILE
stripe balance
```

### `version`

Print the API version pinned by this checkpoint:

```text
2026-08-26.dahlia
```

Stripe's API is account-versioned unless a request supplies `Stripe-Version`. Pinning the version makes the response contract reproducible.

### `url balance`

No network. Print:

```text
https://api.stripe.com/v1/balance
```

### `fixture`

No network. Read a Stripe Balance-shaped JSON file and emit TSV.

The committed synthetic fixture is:

```text
fixture/balance.json
```

Expected output is:

```text
fixture/expected.tsv
```

Columns are:

```text
state currency amount
```

`amount` is emitted as Stripe's integer value without converting it to a decimal. Keeping the currency code and integer intact avoids silently applying the wrong decimal convention to zero-decimal or other currencies.

### `balance`

Perform one authenticated `GET /v1/balance`, decode the same shape as the fixture, and emit the same TSV.

The process environment supplies:

```text
STRIPE_API_KEY=...
```

Do not accept a secret key as a positional command-line argument and do not commit a key. Restricted keys should be preferred when the eventual command needs only a subset of the account.

The request needs:

```text
Authorization: Bearer KEY
Stripe-Version: 2026-08-26.dahlia
```

Stripe documents both Basic and Bearer authentication. Bearer avoids introducing base64/basic-auth machinery solely for this client.

## Idriç and ICU

`idric/Stripe.idric` is the implementation target.

ICU/Idric-Net remains the networking boundary. The Stripe client currently exposes one narrow request seam: a GET carrying caller-supplied `Authorization` and `Stripe-Version` headers. Do not introduce a Stripe-specific socket/TLS implementation or a curl fallback.

The remaining named holes are intentionally visible:

- file input;
- Stripe Balance JSON decoding;
- environment access;
- authenticated ICU GET with caller-supplied headers.

The Reddit checkpoint already needs the same general caller-supplied-header capability. That should be solved once in Idric-Net/ICU rather than independently in each service client.

## Manual receipt

Run:

```text
ysh check
```

or:

```text
IDRIC=/opt/Idric/build/exec/idris2 ysh check
```

The runner emits only:

```text
PASS checkpoint
FAIL checkpoint
SKIP checkpoint
```

A focused failure at a named hole is useful evidence. Do not make it green with an unrelated implementation.

## Checkpoint ladder

1. source parses/checks;
2. `version` prints the pinned version;
3. `url balance` prints the exact endpoint;
4. `fixture` reads the synthetic file;
5. `fixture` decodes Balance and matches `expected.tsv` byte-for-byte;
6. `balance` reads `STRIPE_API_KEY` from the environment;
7. ICU sends `Authorization` and `Stripe-Version`;
8. live balance output uses the same TSV contract as the fixture;
9. add read-only list/retrieve resources with common cursor pagination;
10. add mutating operations only with explicit verbs and idempotency-key support.

See `SURFACE.md` for the API pass and sequencing.
116 changes: 116 additions & 0 deletions checkpoints/stripe/SURFACE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Stripe API surface pass

Audit baseline: 2026-09-06.

Pinned API version for this checkpoint: `2026-08-26.dahlia`.

Canonical references:

- API reference: https://docs.stripe.com/api
- authentication: https://docs.stripe.com/api/authentication
- versioning: https://docs.stripe.com/api/versioning
- pagination: https://docs.stripe.com/api/pagination
- idempotent requests: https://docs.stripe.com/api/idempotent_requests
- key handling: https://docs.stripe.com/keys-best-practices
- public GA OpenAPI: https://github.com/stripe/openapi/tree/master/latest

The GA OpenAPI `latest/` directory contains a unified public specification for both v1 and v2. The repository master observed for this pass was commit `58e06a3214ae1574600fba64d40b770e5da6d505`; the public YAML blob was `0eeb1665acf426ce068aac8cac6092db5739d363`.

## Wire contract

Base URL:

```text
https://api.stripe.com
```

v1 requests use resource-oriented paths. Request bodies for the v1 API are form-encoded and responses are JSON. Authentication is by API key. This client uses bearer auth so the key remains a direct header value rather than introducing Basic-auth encoding.

Always send:

```text
Authorization: Bearer $STRIPE_API_KEY
Stripe-Version: 2026-08-26.dahlia
```

The secret belongs in the environment or a secrets store, never in source. Prefer a restricted key with the smallest permissions that cover the command.

## First read-side inventory

These are the useful command-line surfaces to implement before money-moving writes.

| Resource | Read endpoints to cover | CLI priority |
| --- | --- | --- |
| Balance | `GET /v1/balance` | 1 |
| Balance transactions | `GET /v1/balance_transactions`, `GET /v1/balance_transactions/:id` | 2 |
| PaymentIntents | `GET /v1/payment_intents`, `GET /v1/payment_intents/:id`, search | 3 |
| Charges | `GET /v1/charges`, `GET /v1/charges/:id`, search | 3 |
| Refunds | `GET /v1/refunds`, `GET /v1/refunds/:id` | 4 |
| Payouts | `GET /v1/payouts`, `GET /v1/payouts/:id` | 4 |
| Customers | `GET /v1/customers`, `GET /v1/customers/:id`, search | 5 |
| Products | `GET /v1/products`, `GET /v1/products/:id`, search | 6 |
| Prices | `GET /v1/prices`, `GET /v1/prices/:id`, search | 6 |
| Checkout Sessions | `GET /v1/checkout/sessions`, retrieve, line items | 7 |
| Subscriptions | `GET /v1/subscriptions`, retrieve, search | 8 |
| Invoices | `GET /v1/invoices`, retrieve, search | 8 |
| Events | `GET /v1/events`, `GET /v1/events/:id` | 9 |

The first committed checkpoint implements only Balance. It is the smallest authenticated read with no pagination or user-supplied query parameters, so it isolates the transport/header and JSON-decoding boundaries cleanly.

## Pagination

v1 list endpoints share cursor pagination:

```text
limit
starting_after
ending_before
```

`starting_after` and `ending_before` are mutually exclusive. List results contain `data`, `has_more`, and `url`.

Do not hide pagination behind an unbounded fetch-all default. A CLI should expose a bounded page size and explicit continuation. v2 pagination is different and should be modeled separately rather than pretending it is v1.

## Mutation boundary

Important write surfaces include:

- PaymentIntents: create, update, confirm, capture, cancel;
- Customers: create, update, delete;
- Refunds: create, update, cancel where supported;
- Payouts: create, update, cancel, reverse;
- Products and Prices: create/update, plus product delete;
- Checkout Sessions: create/update/expire;
- Subscriptions: create/update/cancel/migrate/resume;
- Invoices: create/update/finalize/pay/send/void/delete draft invoices.

Do not make a generic `post PATH ...` command the primary interface. These operations have materially different consequences and should get named commands with typed inputs.

All Stripe `POST` requests accept idempotency keys. When writes are added, every mutating command should either require or generate an idempotency key and make the chosen key observable without leaking other secrets. GET and DELETE do not need idempotency keys.

## Payment model

PaymentIntents are the primary modern payment workflow. Charges still have list/retrieve/search value, but direct Charge creation is legacy for most new integrations. Sequence the client accordingly: PaymentIntents first, Charges mainly as observable payment-attempt records.

## Event model

Events are read-only through the API and are useful for diagnosis and replay-oriented tooling. Webhook/event-destination configuration is a separate surface. Keep event retrieval independent from any future local webhook listener so the CLI remains useful without a daemon.

## Expansion order

1. Balance fixture + live balance.
2. Shared v1 list envelope and cursor types.
3. Balance transactions.
4. PaymentIntent list/retrieve/search.
5. Charge list/retrieve/search.
6. Refund and payout read side.
7. Customer list/retrieve/search.
8. Product/Price read side.
9. Checkout Session read side.
10. Subscription/Invoice read side.
11. Event list/retrieve.
12. Write request body encoding + idempotency keys.
13. Named write commands, starting in sandbox/test usage.
14. v2 resources as a distinct namespace with its own pagination/response contracts.

Each new resource gets the same receipt pattern: exact URL construction, synthetic fixture, byte-stable output contract, then live transport through ICU/Idric-Net.
57 changes: 57 additions & 0 deletions checkpoints/stripe/check
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/usr/bin/env ysh

# Manual Idriç Stripe checkpoint runner.
# Deliberately not part of the root make test while named holes remain.

set -u

HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
IDRIC=${IDRIC:-idris2}

TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT HUP INT TERM

status=0

available() {
case "$1" in
*/*) test -x "$1" ;;
*) command -v "$1" >/dev/null 2>&1 ;;
esac
}

pass() {
printf 'PASS\t%s\n' "$1"
}

fail() {
printf 'FAIL\t%s\n' "$1"
status=1
}

skip() {
printf 'SKIP\t%s\n' "$1"
}

show_diagnostics() {
for path in "$@"; do
if test -s "$path"; then
sed 's/^/ /' "$path"
fi
done
}

if ! available "$IDRIC"; then
skip "idric (not found: $IDRIC)"
exit 0
fi

if "$IDRIC" --check "$HERE/idric/Stripe.idric" \
>"$TMP/idric" 2>"$TMP/idric.err"; then
pass 'idric/check'
else
fail 'idric/check'
show_diagnostics "$TMP/idric" "$TMP/idric.err"
fi

exit "$status"
30 changes: 30 additions & 0 deletions checkpoints/stripe/fixture/balance.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"object": "balance",
"available": [
{
"amount": 1250,
"currency": "usd",
"source_types": {
"card": 1250
}
},
{
"amount": 900,
"currency": "eur",
"source_types": {
"card": 900
}
}
],
"connect_reserved": [],
"livemode": false,
"pending": [
{
"amount": 640,
"currency": "usd",
"source_types": {
"card": 640
}
}
]
}
4 changes: 4 additions & 0 deletions checkpoints/stripe/fixture/expected.tsv
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
state currency amount
available usd 1250
available eur 900
pending usd 640
Loading
Loading