Metadata-Version: 2.4
Name: concinno-skills-commerce
Version: 0.1.0
Summary: Commerce agent skills (Shopify + Stripe + QuickBooks) for Concinno — storefront, payments, accounting.
Project-URL: Homepage, https://github.com/aiking931931/concinno
Project-URL: Issues, https://github.com/aiking931931/concinno/issues
Project-URL: Changelog, https://github.com/aiking931931/concinno/blob/main/projects/concinno-skills-commerce/CHANGELOG.md
Author-email: "AI King (Chen-Xuan Wang)" <me@ai-king.dev>
License-Expression: Apache-2.0
Keywords: agent,commerce,concinno,quickbooks,shopify,skills,stripe
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: concinno>=2.15.1
Requires-Dist: httpx>=0.27
Requires-Dist: intuit-oauth>=1.2
Requires-Dist: shopifyapi>=12.0
Requires-Dist: stripe>=10.0
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-cov>=5; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.7; extra == 'dev'
Description-Content-Type: text/markdown

# concinno-skills-commerce

Commerce agent skills for [Concinno](https://pypi.org/project/concinno/)
— Shopify + Stripe + QuickBooks Online. Commerce agents need three
systems-of-record (storefront, payments, accounting) end-to-end; this
package lands all three as a single plugin, fourth in the
`concinno-skills-*` ecosystem after `concinno-skills-chat`,
`concinno-skills-google`, and `concinno-skills-crm`.

## Status

MVP (0.1.0) — five tools, three ecosystems:

| Tool class          | Platform   | Underlying SDK         | SDK licence |
|---------------------|------------|------------------------|-------------|
| `ShopifyOrder`      | Shopify    | `ShopifyAPI`           | MIT         |
| `ShopifyProduct`    | Shopify    | `ShopifyAPI`           | MIT         |
| `StripePayment`     | Stripe     | `stripe`               | MIT         |
| `StripeCustomer`    | Stripe     | `stripe`               | MIT         |
| `QuickBooksInvoice` | QuickBooks | `intuit-oauth` + `httpx` | Apache-2.0 + BSD |

`python-quickbooks` was considered for QuickBooks but it ships under
OSL-3.0 (copyleft); we depend on the permissive `intuit-oauth` auth
SDK plus a thin `httpx` REST wrapper instead so downstream users are
never forced into OSL-3.0 terms.

WooCommerce / BigCommerce / Xero are deferred to 0.2.x.

## Install

```bash
pip install concinno-skills-commerce
```

All three SDKs plus `httpx` are hard dependencies and pulled in
automatically.

## Credentials

Tokens live under well-known keys in the Concinno `CredentialStore`,
which reads (in order):

1. Runtime overrides via `CredentialStore.set(...)`.
2. Env var `CONCINNO_CRED_<UPPER_KEY>`.
3. `~/.concinno/credentials.json`.

### Shopify

| Key                        | Env var                                   |
|----------------------------|-------------------------------------------|
| `shopify_shop_url`         | `CONCINNO_CRED_SHOPIFY_SHOP_URL`          |
| `shopify_access_token`     | `CONCINNO_CRED_SHOPIFY_ACCESS_TOKEN`      |

`shopify_shop_url` takes the `my-store.myshopify.com` form. Produce
the Admin API access token via **Shopify admin → Settings → Apps and
sales channels → Develop apps → Custom app → API credentials** (or an
OAuth install for a public app).

### Stripe

| Key                 | Env var                              |
|---------------------|--------------------------------------|
| `stripe_api_key`    | `CONCINNO_CRED_STRIPE_API_KEY`       |

Use a secret key (`sk_live_...` for production, `sk_test_...` for the
Stripe test mode). Publishable keys (`pk_...`) are not accepted — all
five Stripe actions in the MVP require the secret key scope.

### QuickBooks Online

| Key                           | Env var                                    |
|-------------------------------|--------------------------------------------|
| `quickbooks_access_token`     | `CONCINNO_CRED_QUICKBOOKS_ACCESS_TOKEN`    |
| `quickbooks_realm_id`         | `CONCINNO_CRED_QUICKBOOKS_REALM_ID`        |
| `quickbooks_environment`      | `CONCINNO_CRED_QUICKBOOKS_ENVIRONMENT`     |

`quickbooks_environment` is `"sandbox"` (default) or `"production"`.
An OAuth2 access token is required; token refresh is intentionally
out-of-scope for the MVP — run your token mint / refresh flow in a
separate process and push the current access token into the
CredentialStore. A dedicated `concinno-quickbooks-auth` CLI will land
when enough users ask for it.

Example `~/.concinno/credentials.json`:

```jsonc
{
  "shopify_shop_url": "my-store.myshopify.com",
  "shopify_access_token": "shpat_...",
  "stripe_api_key": "sk_test_...",
  "quickbooks_access_token": "eyJ...",
  "quickbooks_realm_id": "9341454...",
  "quickbooks_environment": "sandbox"
}
```

Missing credentials return `{"error": "no <service> credentials — set
via CredentialStore / env ..."}` rather than crashing.

## Usage via Concinno `ToolRegistry`

When the consumer sets `CONCINNO_LOAD_PLUGINS=1`, the default registry
auto-mounts every commerce tool:

```python
import os
os.environ["CONCINNO_LOAD_PLUGINS"] = "1"

from concinno.tools.registry import get_default_registry

reg = get_default_registry()
assert {
    "ShopifyOrder",
    "ShopifyProduct",
    "StripePayment",
    "StripeCustomer",
    "QuickBooksInvoice",
} <= set(reg.list_deferred())
```

## Direct Python usage

```python
from concinno_skills_commerce import (
    ShopifyOrder,
    ShopifyProduct,
    StripePayment,
    StripeCustomer,
    QuickBooksInvoice,
)

ShopifyOrder().call(action="list", limit=20, filters={"status": "open"})
ShopifyOrder().call(action="fulfill", order_id=123456789,
                    tracking_number="1Z999",
                    tracking_company="UPS")

ShopifyProduct().call(action="create",
                      data={"title": "New product", "vendor": "Acme"})

StripePayment().call(
    action="create",
    amount=5000,        # 5000 cents = USD $50
    currency="usd",
    customer="cus_...",
    description="Order #1001",
)
StripePayment().call(
    action="refund",
    payment_intent_id="pi_...",
    refund_amount=5000,  # partial; omit for full refund
)

StripeCustomer().call(action="create",
                      email="a@b.com",
                      name="A. Customer")

QuickBooksInvoice().call(
    action="create",
    CustomerRef={"value": "1"},
    Line=[
        {
            "Amount": 100.0,
            "DetailType": "SalesItemLineDetail",
            "SalesItemLineDetail": {"ItemRef": {"value": "1"}},
        }
    ],
    TxnDate="2026-04-22",
)
QuickBooksInvoice().call(
    action="query",
    query="SELECT * FROM Invoice WHERE TxnDate > '2026-01-01'",
)
```

All tools return `{"ok": True, ...}` (or `{"results": [...]}` for
`list`) on success and `{"error": "..."}` on failure — same shape as
every other Concinno built-in tool. No exceptions escape `call()`.

## Concurrency

All five tools set `is_concurrency_safe = False`. Two reasons:

- **Shopify** — `ShopifyAPI` uses a thread-local session singleton
  (`shopify.ShopifyResource.activate_session`); parallel calls from
  the same process would corrupt that state.
- **Stripe** — the `stripe` module stores the API key on a module-
  level global (`stripe.api_key`); mixing keys across threads is
  unsafe.
- **QuickBooks** — each call mints its own short-lived `httpx.Client`,
  which is thread-safe in isolation, but QBO's per-realm bucket rate
  limiter benefits from serial access while the token is rotating.

## Safety notes

- **Stripe `create`** warn-logs when `amount` exceeds `1_000_000`
  cents (~USD $10 000). A mis-typed amount is the single largest
  foot-gun on payment APIs.
- **Stripe `refund`** always warn-logs because the refund is
  irreversible once Stripe processes it.
- **`list` actions** cap page size at 250 (Shopify) / 100 (Stripe) /
  1000 (QuickBooks). Cursor-based `list_all` pagination lands in
  0.2.x.
- **QuickBooks writes** target whichever `environment` the
  CredentialStore holds. The default is `"sandbox"`, so a freshly-
  installed CLI cannot accidentally hit production until the operator
  flips the value explicitly.

## License

Apache-2.0.
