Skip to content

Billing & License Fulfillment

firmflow. integrates with Paystack to provide a seamless, automated licensing experience for firms while maintaining the strict security required for on-premise software.

Overview

The billing system is designed with a "Hybrid-Cloud" approach: 1. Checkout: Users initiate payments via the PaystackCheckout component. 2. Verification: Payments are verified via cryptographic signatures from Paystack. 3. Fulfillment: Licenses are generated locally using the firm's LICENSE_SECRET and stored in the firm's local database.

Components

1. Paystack Integration (src/lib/paystack.ts)

Handles communication with the Paystack API. Includes: - verifyTransaction: Direct verification for client-side completion. - verifyWebhookSignature: SHA-512 HMAC verification for secure server-to-server callbacks.

2. Billing Service (src/lib/billing-service.ts)

The core fulfillment engine. It is idempotent by design, ensuring that a single payment reference never issues multiple licenses. - Checks for existing audit logs with the same reference. - Generates a cryptographically signed license key. - Deactivates old licenses within an atomic database transaction.

Webhooks

firmflow. provides a secure webhook endpoint at: POST /api/billing/webhook/paystack

Configuration

  1. Log in to your Paystack Dashboard.
  2. Navigate to Settings > API Keys & Webhooks.
  3. Set the Webhook URL to https://your-domain.com/api/billing/webhook/paystack.
  4. Copy your Secret Key and set it as PAYSTACK_SECRET_KEY in your .env.

Resilience

The webhook handler ensures license delivery even if the user: - Closes their browser before the "Success" screen. - Experiences a network failure after payment. - Refresh the page prematurely.

Security

Licenses are generated using a symmetric HMAC signature. To verify a license, the system requires: - The Firm ID. - The License Secret (only known to the vendor and the firm's installation). - The License Metadata (Tier, Expiry, Seats).

Any manual tampering with the license key stored in the database will cause it to be invalidated by the integrity checker.