SHKeeper
This commit is contained in:
@@ -3,6 +3,9 @@
|
||||
Apache serves the static eom.dev website. A separate Django service provides
|
||||
public accounts, Dex sign-in, and Stripe membership under **/members/**.
|
||||
|
||||
Optional SHKeeper support adds prepaid crypto membership alongside Stripe.
|
||||
Both payment methods feed the same LDAP group reconciliation and local expiry.
|
||||
|
||||
## Membership flow
|
||||
|
||||
1. Anyone can request a free account. A one-hour, single-use email link allows
|
||||
@@ -146,6 +149,103 @@ No production credentials or live account changes are made by the test suite.
|
||||
|
||||
## Operations and limitations
|
||||
|
||||
### SHKeeper prepaid crypto
|
||||
|
||||
Stripe remains the automatically recurring payment method. SHKeeper accepts
|
||||
**one-time payments for one calendar month** of the same tiers, at the USD
|
||||
amounts in [membership/tiers.eom.json](membership/tiers.eom.json). It does not
|
||||
debit a wallet or automatically renew. The user must create and pay a new
|
||||
invoice for each month. Amounts are server-side configuration, never supplied by
|
||||
the browser and never parsed from the display-price label. Keep `usd_amount`
|
||||
aligned with your Stripe prices when changing prices; existing orders retain
|
||||
the original amount.
|
||||
|
||||
To enable:
|
||||
|
||||
1. Set `SHKEEPER_URL` to your actual HTTPS origin, `SHKEEPER_API_KEY` to its
|
||||
invoice API key, and `SHKEEPER_CURRENCIES` to a comma-separated allowlist of
|
||||
the API currency identifiers enabled on **your** instance (e.g. BTC or XMR).
|
||||
These identifiers are network-specific for tokens (e.g. ETH-USDT is not
|
||||
interchangeable with TRX-USDT). No instance URL, wallets or keys are assumed.
|
||||
2. For an internal CA, mount the trusted CA file read-only into both web and
|
||||
worker containers and set `SHKEEPER_CA_CERT_FILE`. TLS verification is always
|
||||
enabled. API requests have timeouts, disallow redirects, and cap response
|
||||
size; the API key never reaches the browser. No payout/UI administrator
|
||||
credentials or wallet private keys are needed. Restrict network/API access
|
||||
because SHKeeper's API key may expose more than invoice endpoints.
|
||||
3. Review SHKeeper fee, exchange-rate recalculation, payment-tolerance and
|
||||
confirmation settings. This integration requires a full invoice amount and
|
||||
confirmed transactions; it intentionally does not grant access for partial,
|
||||
underpaid or unconfirmed transfers, even if a provider tolerance says paid.
|
||||
The payment page identifies its initial quote as such; a partial/late
|
||||
payment may require administrator assistance with the updated remainder.
|
||||
4. Each new invoice supplies the callback URL
|
||||
`https://eom.dev/members/shkeeper/callback/`. Permit SHKeeper to reach it.
|
||||
**SHKeeper's documented callbacks have no signature.** This endpoint only
|
||||
acknowledges them: it never trusts amounts/status/IDs, writes payment state,
|
||||
or triggers an API request. The worker polls known local invoice UUIDs over
|
||||
authenticated HTTPS, so forged, repeated, missing or reordered callbacks
|
||||
cannot grant access or extend a term.
|
||||
5. Redeploy the image and run the migration service, which applies
|
||||
[membership/members/migrations/0002_crypto_invoices.py](membership/members/migrations/0002_crypto_invoices.py).
|
||||
Keep the membership worker running; it independently refreshes Stripe and
|
||||
SHKeeper, and performs a local-only expiry sweep before remote requests.
|
||||
6. Smoke-test a low-value invoice on your actual deployment before offering
|
||||
public crypto payments. The adapter follows the documented SHKeeper 2.1 API:
|
||||
`POST /api/v1/{crypto}/payment_request` and
|
||||
`GET /api/v1/invoices/{external_id}`. Require the GET response to include
|
||||
`external_id`, `fiat`, `amount_fiat`, `balance_fiat`, `status` and `txs`
|
||||
(transaction status, asset identifier, address and txid). Unsupported or
|
||||
ambiguous responses do not grant access. Different SHKeeper versions must
|
||||
be verified against these fields, especially transaction crypto identifiers.
|
||||
|
||||
Payment lifecycle:
|
||||
|
||||
- An authenticated user selects the currency, then sees a private invoice
|
||||
with its deposit address and quote. One unfinished invoice is allowed per
|
||||
account. Repeated submissions reopen that same invoice, even if a second
|
||||
tab requests another tier or currency. The UUID and USD amount are committed
|
||||
**before** contacting SHKeeper; retrying after a timeout reuses SHKeeper's
|
||||
documented create/update-by-external-ID operation with the same parameters.
|
||||
- There is no fabricated invoice deadline: SHKeeper deposit addresses can
|
||||
receive late transfers. Unpaid/partial orders remain polled and cannot be
|
||||
abandoned/switched from the UI. Contact the administrator if the wrong
|
||||
network was selected; do not delete orders with potentially in-flight funds.
|
||||
- Access begins when the worker first verifies a fully paid invoice with
|
||||
matching USD order, amount, receiving address and asset and confirmed txs.
|
||||
A renewal of the **same tier** starts at the later of now or that tier's
|
||||
previous prepaid end and adds one calendar month in UTC. Month-end dates
|
||||
clamp to the last day of the next month. Queued future terms do not grant
|
||||
early access. Delayed worker verification starts a new term at verification,
|
||||
not at an untrusted callback timestamp.
|
||||
- Each invoice can grant **exactly one term**. Overpayments, repeated callbacks
|
||||
and additional transfers to a paid address never buy extra months. Create a
|
||||
new invoice for renewal. Refund/overpayment handling is manual; this app does
|
||||
not issue cryptocurrency payouts.
|
||||
- Active/queued paid crypto invoices are rechecked until their end. If an
|
||||
invoice reverts to unpaid/partial/unconfirmed (e.g. a chain reorganization),
|
||||
its grant is removed. Recovery restores only its original term, not another
|
||||
month. During API outages, the last verified term remains valid only until
|
||||
its stored expiry; local expiry never waits for provider recovery. Alert on
|
||||
stale `last_shkeeper_sync`, failed polls and worker errors.
|
||||
- LDAP desired groups are the **union** of all currently valid Stripe and
|
||||
SHKeeper terms. One provider's failure/cancellation cannot revoke a still
|
||||
valid grant from another. Removed tiers are revoked regardless of provider.
|
||||
- To avoid accidental double payments, a current Stripe subscription or open
|
||||
Stripe Checkout blocks a new crypto invoice. Unfinished or unexpired crypto
|
||||
orders block a new Stripe subscription; another crypto tier must expire
|
||||
before switching tiers. Same-tier crypto renewals remain available. This
|
||||
does not cancel existing Stripe recurring charges: finish/cancel them through
|
||||
Stripe's billing portal before changing methods. Provider-side/manual changes
|
||||
can still create overlapping payments; grants remain a safe union.
|
||||
- Leaving SHKeeper configuration empty hides new crypto checkouts. Previously
|
||||
verified prepaid grants still expire locally, but remote status cannot be
|
||||
rechecked while the provider is disabled. Do not delete payment history or
|
||||
change your SHKeeper server/key to an unrelated instance while orders exist.
|
||||
`CryptoInvoice` protects its parent Account from accidental deletion.
|
||||
|
||||
### General operations
|
||||
|
||||
- Keep `membership-worker` running. Alert on its errors and on accounts whose
|
||||
`last_ldap_sync` or `last_billing_sync` is stale. A one-shot
|
||||
`sync_memberships` command exits nonzero on a failed sweep. Logs record account
|
||||
@@ -185,6 +285,11 @@ webhooks, paid invoice periods, cancellation, failed renewals, unavailable Strip
|
||||
LDAP retries, tier removal, multiple subscriptions, and the last member's removal
|
||||
from a `groupOfUniqueNames` using its sentinel. Live Stripe, Dex, SMTP and OpenLDAP
|
||||
must still be smoke-tested together in a non-production environment before
|
||||
enabling public registration/payment.
|
||||
enabling public registration/payment. SHKeeper regressions additionally cover
|
||||
API-key/TLS transport, immutable prices and account-scoped invoices, callback
|
||||
spoofing, partial/unconfirmed/underpaid transfers, duplicate confirmation,
|
||||
calendar-month renewals, overpayments, reorg recovery, provider outages, and
|
||||
cross-provider overlap. PostgreSQL also verifies that concurrent workers credit
|
||||
one crypto invoice only once.
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user