# Connect to Coinvay · 1.2.0

## Two connection methods

**Automatic (recommended):** choose Connect to Coinvay in your store's administrator settings. You are redirected to Coinvay to sign in or register, review the exact store origin and permissions, then explicitly authorize it. Coinvay returns a short-lived authorization code. Your store backend exchanges that code using S256 PKCE, stores the credentials privately, and fills the API and webhook configuration. The API key never appears in a browser return URL. Connecting does not turn on your store's customer payment method; enable that separately after staging checks.

**Manual:** keep entering a merchant API key in protected server/gateway settings. Confirm manual setup (or save WooCommerce settings) to register a store descriptor, its webhook and its checkout branding. Manual keys retain their existing merchant-wide permission; use Automatic for a store-limited key. Old installations without a registered store retain their existing global branding and webhook behavior.

## Checkout business identity

Set Business name and upload a PNG, JPG or WebP logo up to 2 MB in the plugin/app administrator settings. Save to sync to Coinvay. Each connected store has a separate business name, logo and webhook. A new invoice snapshots its store branding: later edits do not change the identity on previous invoices. The small Powered by Coinvay label stays visible. Global merchant branding is unchanged. Account plan branding permission still applies. No wallet/private-key data is requested by this connection.

## Where the controls are

- **WooCommerce:** WooCommerce → Settings → Payments → Coinvay. Select Manual API key or Automatic account connection; use Connect to Coinvay below the fields. Business name and logo are on the same settings page. The callback requires the same signed-in WordPress session and manage_woocommerce permission. Credentials are encrypted with the installation's WordPress authentication salt. Back up that salt securely; changing it requires reconnecting. HTTPS frontend, WordPress admin and webhook must have the same origin. Saving registers the store's callback without changing other stores.
- **WHMCS:** activate Coinvay in Payment Gateways, then open Manage connection & checkout branding. Select Use manual setup or Connect to Coinvay. The management page requires a signed-in administrator with Configure Payment Gateways permission and a session CSRF token. WHMCS encrypt/decrypt functions protect credentials. Connection data is in mod_coinvay_connection; preserve WHMCS's encryption configuration with backups.
- **PHP:** mount public/admin-connection.php inside your existing HTTPS app. Its session must have a trusted user_id and is_admin === true. Set the optional COINVAY_CONNECT_* environment variables first. A stable, private 32+ character encryption key is mandatory for automatic setup. Keep it separate from the SQLite database and back up both. No app administrator authentication is invented or bypassed by the kit. Existing customer checkout/session ownership remains in force.
- **JavaScript / mobile / Kajabi:** use the bundled Node Connections manager and connectionAdmin() page, mounted behind your existing app administrator session as shown below. Mobile apps open this web administration page through their authenticated backend; credentials never enter the React Native bundle. Kajabi's own editor cannot safely hold an API key, so connection and branding are on your separate Node backend. The browser purchase component remains separate from merchant administration.
- **Shopify:** connection.mjs and the optional connection argument on ShopifyPaymentAdapter support an app's server-side merchant connection. Mount the administrator page/callback in your authenticated Shopify app backend, with separate Connections storage per authenticated shop. Your HTTPS app-backend origin is the registered connection origin. This does not replace Shopify OAuth, Payments Partner/app approval, mTLS, restricted scopes, app scaffolding or lifecycle implementation. The package remains a developer adapter, not a public installable Shopify payments app.

## Node administration page

Run the included backend on Node 22.13+ behind HTTPS. Set COINVAY_CONNECT_ENCRYPTION_KEY, COINVAY_STORE_URL, COINVAY_CONNECT_CALLBACK, COINVAY_STORE_WEBHOOK and COINVAY_BUSINESS_NAME. The callback and webhook must have the same exact HTTPS origin as STORE_URL. Use a fixed backend origin, not customer input. To embed the ready-made Manual / Connect / Disconnect and logo page, construct your backend handler with adminSession:

```js
import { handler } from './server/server.mjs';
import { connectionFromEnv } from './server/connection.mjs';
const connection = connectionFromEnv(store.db);
const appHandler = handler({
  store, connection, adminKey: process.env.INTEGRATION_ADMIN_KEY,
  allowedOrigins: ['https://store.example.com'],
  adminSession: async req => {
    const s = await yourExistingServerSession(req); // your authenticated app
    if (!s?.user?.isAdministrator) return null;
    return { isAdmin: true, id: s.user.id, sessionId: s.id,
      data: s.data, save: () => s.save() };
  }
});
```

Mount this handler after your real session middleware. Session cookies must be Secure, HttpOnly and SameSite=Lax, and the server-side session ID must rotate on login. /coinvay-connect renders the admin controls; configure the callback at /coinvay-return. adminSession must validate your own administrator role and durable server session, never a browser-supplied flag. The command-line sample has no access to your app's login system and does not expose the admin page until this adapter is supplied. Existing backend-only /internal/connection endpoints require the separate INTEGRATION_ADMIN_KEY; keep all /internal/ routes private at the reverse proxy and never embed that secret in JavaScript.

Backend endpoints: GET /internal/connection (nonsecret status), POST /internal/connection/start, /manual, /disconnect and /branding. Branding JSON is {businessName, logoBase64?, removeLogo?}; enforce your app's administrator session/CSRF at any frontend proxy. The bundled admin page already checks its session, CSRF token and exact Origin. Starting returns an authorizationUrl; redirect the administrator to it. The configured callback completes and cleans the return URL. Node and PHP polling jobs read the saved connection automatically.

## Permissions, limits and disconnect

Automatic keys can create, read and cancel only their own store invoices, update that store's checkout branding and disconnect it. They cannot change wallets, make payouts, access settings/security/admin pages, mint other keys, list another store or read another store's invoices. Each automatic connection consumes one active API-key slot under the current plan. Reconnecting the same installation rotates its key and secret while preserving its store ID and invoice access. Store webhooks remain plan-dependent; polling works independently. Coinvay never enables an optional advertising integration through this flow or adds tracking to customer checkout.

Coinvay → Integration lists connected stores and offers Disconnect. It revokes an automatic store key and cancels queued store webhook deliveries. Disconnect does not delete invoices, payments, order mappings or receiving wallets. Finish/reconcile outstanding orders before removing a gateway or moving it to a different merchant account. Store-local backups contain encrypted credentials; anyone holding both the database and encryption key may decrypt them. Protect both.

## Connection API

POST /api/connect/requests: {name, storeUrl, installation, redirectUri, webhookUrl?, state, codeChallenge, codeChallengeMethod:"S256"}. installation/state are cryptographically random URL-safe strings, 32–128 characters; the S256 challenge is 43 characters. Exact public HTTPS origins only. Response includes authorizationPath and expires. The request expires in 10 minutes.

GET /api/connect/requests/{requestId}: nonsecret request information. The interactive Coinvay account approves or denies through its review screen. An API key cannot authorize a store. After approval, the callback contains only code and state. Validate state against the initiating app session before exchanging.

POST /api/connect/token: {code, codeVerifier, redirectUri}. Backend-only, one use. PKCE verifier is 43–128 URL-safe characters and the exact redirectUri must match the registered request. Save apiKey, storeId and webhookSecret encrypted; never log the response or expose it to browser/mobile code. Expired/consumed/denied codes do not issue credentials. Network failure or a lost credential response requires starting a fresh connection.

GET /api/stores lists the signed-in merchant's connections. GET/DELETE /api/stores/{storeId} reads/disconnects an owned store. POST /api/stores/{storeId}/branding accepts JSON {businessName,removeLogo} or multipart fields businessName + logo. POST /api/stores/manual uses a full merchant API key and {name,storeUrl,installation,webhookUrl?}; it returns a descriptor and per-store webhook secret. Invoice creation accepts storeId for a full manual key; an automatic key is always bound to its own store. Idempotency keys are isolated per store. No wallet setup is bypassed.

## Before enabling your store gateway

Test in your actual staging installation: denied/expired authorization, wrong administrator session, callback retry, missing permission, reconnect, disconnect, absent API-key capacity, plan-gated branding/webhooks, business name/logo, two-store invoice isolation, background polling and exactly-once fulfillment. Native WordPress/WHMCS installation, Shopify approval and customer-device OAuth have not been performed for you. The repository's fixture tests do not certify a platform or constitute a live payment.
