Skill: stanestane/stripe-api-readonly (version 1.0.0)

--- name: stripe-api description: Use Stripe's live REST API for authenticated account inspection and operational lookup. Use when you need to connect to a Stripe account with a secret key, inspect account details, list customers/products/prices/payments/subscriptions/invoices/refunds/disputes/payouts/webhook endpoints, or retrieve a specific Stripe object safely via read-only commands. --- # Stripe API Use this skill for real Stripe API access. ## Quick start 1. Set `STRIPE_SECRET_KEY` in the local shell environment. 2. Start with read-only inspection: - `python skills/stripe-api/scripts/stripe_api.py account` - `python skills/stripe-api/scripts/stripe_api.py payment_intents --limit 10` - `python skills/stripe-api/scripts/stripe_api.py customers --limit 10` 3. Read `references/objects-and-workflows.md` when you need object guidance or a sensible inspection order. ## Core workflow ### 1. Verify access Run: ```bash python skills/stripe-api/scripts/stripe_api.py account ``` If this fails with authentication errors, fix the environment variable first. ### 2. Inspect the account safely Use read-only list commands first: ```bash python skills/stripe-api/scripts/stripe_api.py products --limit 20 python skills/stripe-api/scripts/stripe_api.py prices --limit 20 python skills/stripe-api/scripts/stripe_api.py payment_intents --limit 20 python skills/stripe-api/scripts/stripe_api.py charges --limit 20 python skills/stripe-api/scripts/stripe_api.py invoices --limit 20 python skills/stripe-api/scripts/stripe_api.py subscriptions --limit 20 python skills/stripe-api/scripts/stripe_api.py payouts --limit 20 python skills/stripe-api/scripts/stripe_api.py disputes --limit 20 python skills/stripe-api/scripts/stripe_api.py webhook_endpoints --limit 20 ``` ### 3. Retrieve a known object directly ```bash python skills/stripe-api/scripts/stripe_api.py get /customers/cus_123 python skills/stripe-api/scripts/stripe_api.py get /payment_intents/pi_123 ``` ### 4. Search customers ```bash python skills/stripe-api/scripts/stripe_api.py search_customers "email:'[REDACTED_SECRET]'" ``` ## Safety rules - Do not put live secrets in the skill files. - Do not commit or publish secrets. - Default to read-only operations. - Before any write action in the future, confirm with the user. - Treat secrets shared in chat as compromised and recommend rotation. ## Resources - `scripts/stripe_api.py` — minimal authenticated Stripe API helper using only Python standard library - `references/objects-and-workflows.md` — common Stripe objects, safe inspection order, and search examples

path: references/objects-and-workflows.md
content: # Stripe API Objects and Workflows

## Read-first safety rules

- Treat any `sk_live_...` secret as highly sensitive.
- Never store live keys in `SKILL.md`, scripts, git-tracked files, or chat transcripts.
- Prefer environment variable `STRIPE_SECRET_KEY`.
- Default to read-only inspection first.
- Ask before any write action that could move money, refund, cancel, or modify billing state.

## Common objects

- `account`: Stripe account identity, business profile, capabilities
- `customers`: people or businesses you bill
- `products`: what you sell
- `prices`: billing terms for products
- `payment_intents`: payment lifecycle state machine
- `charges`: resulting charge records
- `subscriptions`: recurring billing contracts
- `invoices`: billing documents and payment attempts
- `refunds`: money returned to customers
- `disputes`: chargebacks and card disputes
- `payouts`: transfers from Stripe balance to bank
- `balance_transactions`: ledger-style balance events
- `webhook_endpoints`: webhook destinations configured on the account

## Suggested inspection flow

1. Retrieve `/account`
2. List recent `payment_intents`, `charges`, and `invoices`
3. List `products` and `prices`
4. List `subscriptions` if recurring billing matters
5. Inspect `payouts`, `balance_transactions`, and `disputes`
6. Inspect `webhook_endpoints`

## Useful customer search examples

Customer search query language examples:

- `email:'[REDACTED_SECRET]'`
- `name:'Alice Example'`
- `metadata['external_id']:'12345'`

## Write actions to add later carefully

Possible future additions:

- create customer
- create product
- create price
- create refund
- cancel subscription
- create checkout session / payment link

Keep those separate from read-only inspection commands and require explicit user confirmation before use.


path: scripts/stripe_api.py
content: #!/usr/bin/env python3
import argparse
import json
import os
import urllib.parse
import urllib.request

BASE = "https://api.stripe.com/v1"


def get_key() -> str:
    key = os.environ.get("STRIPE_SECRET_KEY", "").strip()
    if not key:
        raise SystemExit("Missing STRIPE_SECRET_KEY environment variable.")
    return key


def request(method: str, path: str, params=None):
    key = get_key()
    url = BASE + path
    data = None
    headers = {
        "Authorization": f"Bearer {key}",
        "User-Agent": "openclaw-stripe-api-skill/1.0",
    }
    if params:
        encoded = urllib.parse.urlencode(params, doseq=True)
        if method.upper() == "GET":
            url += ("?" if "?" not in url else "&") + encoded
        else:
            data = encoded.encode("utf-8")
            headers["Content-Type"] = "application/x-www-form-urlencoded"
    req = urllib.request.Request(url, data=data, headers=headers, method=method.upper())
    try:
        with urllib.request.urlopen(req, timeout=60) as resp:
            return json.loads(resp.read().decode("utf-8"))
    except urllib.error.HTTPError as e:
        body = e.read().decode("utf-8", errors="replace")
        try:
            parsed = json.loads(body)
        except Exception:
            parsed = {"error": {"message": body, "type": "http_error", "status": e.code}}
        print(json.dumps(parsed, indent=2, ensure_ascii=False))
        raise SystemExit(1)


def print_json(obj):
    print(json.dumps(obj, indent=2, ensure_ascii=False))


def main():
    p = argparse.ArgumentParser(description="Minimal Stripe API helper for OpenClaw skill use")
    sub = p.add_subparsers(dest="cmd", required=True)

    sub.add_parser("account", help="Retrieve account details")

    for name, help_text in [
        ("customers", "List customers"),
        ("products", "List products"),
        ("prices", "List prices"),
        ("charges", "List charges"),
        ("payment_intents", "List payment intents"),
        ("subscriptions", "List subscriptions"),
        ("invoices", "List invoices"),
        ("refunds", "List refunds"),
        ("disputes", "List disputes"),
        ("payouts", "List payouts"),
        ("balance_transactions", "List balance transactions"),
        ("webhook_endpoints", "List webhook endpoints"),
    ]:
        sp = sub.add_parser(name, help=help_text)
        sp.add_argument("--limit", type=int, default=10)

    search = sub.add_parser("search_customers", help="Search customers")
    search.add_argument("query")
    search.add_argument("--limit", type=int, default=10)

    get_obj = sub.add_parser("get", help="Retrieve an object by API path, e.g. /customers/cus_123")
    get_obj.add_argument("path")

    args = p.parse_args()

    if args.cmd == "account":
        print_json(request("GET", "/account"))
        return

    listing = {
        "customers": "/customers",
        "products": "/products",
        "prices": "/prices",
        "charges": "/charges",
        "payment_intents": "/payment_intents",
        "subscriptions": "/subscriptions",
        "invoices": "/invoices",
        "refunds": "/refunds",
        "disputes": "/disputes",
        "payouts": "/payouts",
        "balance_transactions": "/balance_transactions",
        "webhook_endpoints": "/webhook_endpoints",
    }
    if args.cmd in listing:
        print_json(request("GET", listing[args.cmd], {"limit": args.limit}))
        return

    if args.cmd == "search_customers":
        print_json(request("GET", "/customers/search", {"query": args.query, "limit": args.limit}))
        return

    if args.cmd == "get":
        path = args.path if args.path.startswith("/") else f"/{args.path}"
        print_json(request("GET", path))
        return


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)