payment-domain-knowledge

star 1

Payment processing domain model for Perkcord — our role in the ecosystem, provider relationships, credential ownership, PCI scope, and merchant vs platform responsibilities.

BASIC-BIT By BASIC-BIT schedule Updated 6/5/2026

name: payment-domain-knowledge description: Payment processing domain model for Perkcord — our role in the ecosystem, provider relationships, credential ownership, PCI scope, and merchant vs platform responsibilities. compatibility: opencode metadata: audience: maintainers repo: perkcord

Purpose

Establish a shared mental model for how Perkcord relates to the payment processing ecosystem. This skill should be loaded when working on payment provider integrations, checkout flows, credential management, onboarding, or PCI-related decisions.

Perkcord's role in the ecosystem

Perkcord is a software platform, not a payment facilitator (PayFac) and not a merchant of record. We are analogous to WooCommerce, Shopify (without Shopify Payments), or a SaaS billing tool.

What we do

  • Build the checkout UI and subscribe flow
  • Collect a payment token (never raw card data) via provider-hosted or provider-JS tokenization
  • Send that token to the payment gateway using the guild owner's credentials
  • Listen for webhooks to update entitlement state
  • Sync Discord roles based on entitlement state

What we do NOT do

  • Hold merchant accounts
  • Go through underwriting
  • Bear liability for chargebacks (the merchant does)
  • Set or control transaction fee structures
  • Have a direct relationship with acquiring banks or card networks
  • Store, process, or transmit raw card data

The players

Role Who Example
Card networks Set the rules Visa, Mastercard, Amex, Discover
Issuing bank Customer's bank (issues their card) Chase, Capital One
Acquiring bank Merchant's bank (receives funds) Provided via the ISO/processor
Payment gateway Technology that routes transactions NMI, Authorize.Net, Stripe
ISO / Processor Bundles merchant account + gateway The "high-risk provider" a client works with, or Stripe directly
Merchant Entity legally selling goods/services The guild owner / community operator
Perkcord Software platform automating checkout + entitlements Us

Credential ownership

Every set of provider credentials belongs to the merchant (the guild owner), not to Perkcord.

The guild owner:

  1. Applies for a merchant account (through an ISO, processor, or directly with Stripe)
  2. Goes through underwriting (business verification, financial history, risk assessment)
  3. Gets approved and receives API credentials from their provider dashboard
  4. Provides those credentials to Perkcord (via the admin onboarding wizard)

Current state: credentials are stored as deployment-level environment variables (one merchant per deployment). This works for single-tenant / white-label deployments. Target state (see #163): credentials will be stored encrypted per-guild in Convex so each guild owner can configure their own merchant account. See reference/credentials-by-provider.md for the specific values each provider requires.

PCI compliance scope

PCI DSS requires anyone in the payment card chain to prove security compliance via a Self-Assessment Questionnaire (SAQ). The type depends on how much card data your system touches.

SAQ Level Questions When it applies Our providers
SAQ-A ~22 Card data never enters your page DOM. Entire form is hosted by the provider (redirect, iframe, or provider lightbox). Stripe (hosted checkout), NMI (hosted URL), Authorize.Net with AcceptUI.js
SAQ A-EP ~191 Card data enters your page DOM (custom form fields) but JS sends it directly to the provider — never hits your server. Historical Accept.js-only custom forms (not current Perkcord flow)
SAQ-C ~160 Card data passes through your server but you don't store it. Not applicable to us
SAQ-D ~329 You store or fully process card data yourself. Not applicable to us

Current posture: all shipping Perkcord payment collection paths are SAQ-A. Stripe uses hosted checkout, NMI uses a hosted checkout URL, and Authorize.Net uses AcceptUI.js hosted lightbox tokenization. The old Accept.js custom-form posture is now historical context only.

Invariants (from AGENTS.md)

  • Never store raw card data. Always tokenize.
  • Never log secrets, OAuth tokens, or full webhook bodies with PII.
  • Tokenization approach per provider: Stripe handles it via hosted checkout; Authorize.Net via AcceptUI.js hosted lightbox; NMI via hosted checkout (Collect.js is a future option, not currently implemented).

Fee structure (not our concern)

Transaction fees are negotiated between the merchant and their ISO/processor. Perkcord does not set, control, or participate in these fees. We charge our own separate SaaS fee for the platform.

Typical high-risk ranges (for awareness, not something we configure):

  • Transaction fee: 2.5%–5%+ per transaction
  • Per-transaction flat fee: $0.10–$0.30
  • Monthly gateway fee: $10–$50/mo
  • Chargeback fee: $15–$100 per chargeback
  • Rolling reserve: 5–10% held for 6 months

Authorize.Net recurring billing rule

When working on Authorize.Net subscriptions, do not treat ARB as an instant first-payment mechanism.

  • ARB is batch-based, not real-time. Same-day starts are processed on the next overnight batch, so subscription creation alone is not proof of payment.
  • The recommended Perkcord pattern is: real-time first charge now, then recurring renewals next cycle.
  • Model the first payment as a customer-initiated transaction (CIT) and later renewals as stored-credential / recurring merchant-initiated transactions (MIT).
  • Do not grant access from subscription.created alone unless the product explicitly supports a provisional-access state.
  • Do not synthesize provider payment-success events just to make access feel instant; if provisional access is ever needed, track it as a separate internal state with its own audit trail.

Recommended implementation shape

  • Tokenize card data with AcceptUI.js (preferred - SAQ-A, hosted lightbox, compatible with the ARB flow) or Accept Hosted for one-time-only flows.
  • Run a real-time first createTransactionRequest / authCaptureTransaction.
  • Create or fetch the stored customer payment profile from that successful charge.
  • Create the ARB subscription for future renewals, with startDate aligned to the next billing period instead of "today".
  • Persist the provider identifier that the eventual webhook will actually send back, so post-charge recovery paths still have a durable entitlement anchor.
  • Keep payment-provider events audit-pure: real provider events come from provider responses/webhooks, not internal convenience writes.

Sharp edges

  • End-of-month monthly starts must clamp to the last valid day of the target month.
  • A successful immediate charge can be followed by profile-creation or ARB-setup failure; the checkout flow must avoid surfacing that as an unpaid failure while still preserving a webhook-reachable provider link.
  • If the product wants "charge later, access now," use an explicit provisional-access design or ARB trial semantics; do not blur that with a paid entitlement.

Provider reference files

For provider-specific details (credentials, APIs, sandbox setup, tokenization approach), see:

  • reference/credentials-by-provider.md — What credentials each provider requires and who provides them
  • reference/stripe.md — Stripe-specific integration details
  • reference/authorize-net.md — Authorize.Net-specific integration details
  • reference/nmi.md — NMI-specific integration details

When to load this skill

  • Working on checkout or subscribe flow code
  • Adding or modifying a payment provider integration
  • Working on the admin onboarding wizard or credential management
  • Making PCI-related architecture decisions
  • Answering questions about who owns what in the payment chain
  • Debugging webhook or transaction failures
Install via CLI
npx skills add https://github.com/BASIC-BIT/perkcord --skill payment-domain-knowledge
Repository Details
star Stars 1
call_split Forks 0
navigation Branch main
article Path SKILL.md
Occupations
More from Creator