System Overview
Partners place an HTML banner on their website. When a visitor clicks, a 30-day tracking cookie is set. If that visitor later completes a purchase — via Stripe, Square, or a manually entered sale — commission is calculated at the partner's rate and queued for payout.
Full Control
Manages partners, banners, sales, payouts, and all settings. Session key: bc_admin_id
Dashboard Access
Views clicks, commissions, payouts, and copies embed codes. Session key: bc_partner_id
Public Pages
Sees the landing page, registration, and login. No session required.
File & Directory Structure
├── admin/
│ ├── includes/
│ │ ├── config.php ← main config, all shared functions
│ │ ├── header.php ← admin sidebar layout
│ │ └── footer.php
│ ├── database/
│ │ ├── bannerconnect.db ← SQLite database
│ │ ├── database.sql ← schema
│ │ └── setup.php ← one-time setup (?key=lastcall_setup_2024)
│ ├── index.php ← admin dashboard
│ ├── partners.php ← partner management
│ ├── banners.php ← banner management
│ ├── record-sale.php ← manual sale entry
│ ├── payouts.php ← commission payouts
│ ├── settings.php ← all system settings
│ └── login.php ← admin login
├── assets/
│ ├── css/ ← partner portal CSS
│ ├── uploads/ ← banner images, logo, cover
│ └── postoffice/ ← forgot-password, reset, verify
├── includes/
│ ├── config.php ← thin wrapper → admin/includes/config.php
│ ├── header.php ← partner portal navbar
│ ├── footer.php
│ └── logout.php
├── partners/
│ ├── partner_dashboard.php
│ ├── partner_leads.php
│ ├── partner_login.php
│ ├── partner_profile.php
│ ├── partner_register.php
│ └── partner_verify.php
├── banner.php ← click tracker & cookie setter
├── lead-verify.php ← email verification for leads
├── login.php ← combined admin/partner login
├── stripe-webhook.php ← Stripe payment receiver
├── square-webhook.php ← Square payment receiver
└── index.php ← public landing page
admin/partners/admin_partners.php and partners/partner-register.php (hyphen). Neither is linked to from anywhere and both call functions that don't exist in the BC config (require_admin(), is_logged_in()). They would fatal-error if accessed.How It Works — End to End
Partner Onboarding
/partners/partner_register.php — status set to unverifiedpartner_verify.php?token=… — status becomes activeadmin/banners.php, assigns to partner<a><img></a> embed from their dashboard and pastes it on their siteClick & Cookie Flow
When a visitor clicks the banner, banner.php does exactly three things:
active. Redirects to destination if either is not.banner_clicks and increments banners.click_count. Visitor is never blocked if this fails.bc_ref cookie and sends visitor to destination_url.Sale Recording — Three Paths
| Method | Source value | Trigger |
|---|---|---|
| Stripe webhook | stripe_webhook | checkout.session.completed or payment_intent.succeeded |
| Square webhook | square_webhook | payment.completed |
| Admin manual | manual | Admin fills out Record Sale form |
All three paths insert a sales row with sale_status='confirmed' and commission_status='pending'.
Payout Flow
admin/payouts.php, clicks Pay Out next to a partner with pending commissionspayouts record created; payout_items rows link each salecommission_status='paid'The Affiliate Tracking Cookie
| Property | Value |
|---|---|
| Cookie name | bc_ref |
| Duration | Controlled by cookie_days setting (default 30 days) |
| Flags | HttpOnly, SameSite=Lax, Secure when on HTTPS |
| Format | Base64-encoded JSON: {"p":5,"b":3,"t":1713750000} |
Reading the cookie on your checkout page
$ref = json_decode(base64_decode($_COOKIE['bc_ref'] ?? ''), true);
$p_id = (int)($ref['p'] ?? 0); // partner_id
$b_id = (int)($ref['b'] ?? 0); // banner_id
Passing to Stripe (session metadata)
$session = \Stripe\Checkout\Session::create([
// ... line_items, mode, urls ...
'metadata' => [
'bc_partner_id' => $p_id,
'bc_banner_id' => $b_id,
],
]);
Passing to Square (payment note)
// In your Square CreatePayment call:
'note' => "bc_partner:{$p_id},bc_banner:{$b_id}"
Partner Portal
Pages
| Page | Path | Purpose |
|---|---|---|
| Login | /partners/partner_login.php | Partner sign-in |
| Register | /partners/partner_register.php | Self-registration with email verification |
| Verify | /partners/partner_verify.php?token=… | Email confirmation link |
| Dashboard | /partners/partner_dashboard.php | Stats, banners, embed codes, commissions |
| Clicks | /partners/partner_leads.php | Paginated click log (20 per page) |
| Profile | /partners/partner_profile.php | Edit business info, change password |
Partner Statuses
| Status | Meaning |
|---|---|
| unverified | Registered — email not yet confirmed |
| pending | Email verified — awaiting admin approval |
| active | Fully approved — can log in |
| suspended | Blocked by admin |
Session Keys Set on Partner Login
Admin Panel
Pages
| Page | Path | Purpose |
|---|---|---|
| Dashboard | /admin/index.php | Stats, pending approvals, recent sales |
| Partners | /admin/partners.php | Approve, suspend, reactivate, update commission rates |
| Banners | /admin/banners.php | Create, toggle, delete banners; copy embed codes |
| Record Sale | /admin/record-sale.php | Manually enter a sale with commission preview |
| Payouts | /admin/payouts.php | Pay out pending commissions, view history |
| Settings | /admin/settings.php | All configuration — general, branding, Stripe, Square |
| Login | /admin/login.php | Admin-only sign-in (users table, role=admin) |
Managing Banners
Create via the Add Banner modal — select an active partner, enter a title, upload an image (JPG/PNG/GIF/WebP, max 5MB). Status defaults to active. The embed code URL format:
https://a1appbuilders.com/addons/bannerconnect/banner.php?b={banner_id}
Toggle pauses/activates. Delete removes the DB record and the uploaded image file.
Managing Partners
Filter by status tab. Commission rate is an inline editable number field — save with the ✓ button. The Approve action sets status to active and sends an approval email. Delete is permanent and removes all partner data (requires confirmation).
Recording a Manual Sale
Use admin/record-sale.php for Stripe/Square payments that missed the webhook, or for invoiced/Zelle payments. Commission is calculated live as a preview. Required: partner + sale amount. If the partner has notify_on_sale = 1, they receive an email automatically.
Processing Payouts
The left panel shows all partners with pending commissions. Click Pay Out to pre-fill the form, set the date, add optional notes (e.g. "Paid via Zelle #123"), then submit. All pending commissions for that partner are marked paid in one atomic operation and the partner is emailed a summary.
Payment Processor Integration
Stripe
| Property | Value |
|---|---|
| Webhook URL | https://a1appbuilders.com/addons/bannerconnect/stripe-webhook.php |
| Register in | Stripe Dashboard → Developers → Webhooks |
| Events | checkout.session.completed, payment_intent.succeeded |
| Signature | Verified via Stripe SDK Webhook::constructEvent() |
| Duplicate guard | Checks processor_transaction_id before inserting |
Square
| Property | Value |
|---|---|
| Webhook URL | https://a1appbuilders.com/addons/bannerconnect/square-webhook.php |
| Register in | Square Developer Dashboard → Webhooks |
| Event | payment.completed |
| Affiliate data | Payment note field: bc_partner:5,bc_banner:3 |
| Signature | HMAC-SHA256 of notification_url + raw_body |
processor_transaction_id values silently with a 200 response.Settings Reference
All settings are stored in the settings table as key/value pairs and managed from admin/settings.php.
General
| Key | Description | Default |
|---|---|---|
destination_url | Where banner clicks redirect to | https://a1appbuilders.com/ |
cookie_days | Affiliate cookie lifetime in days | 30 |
default_commission_rate | Suggested rate for new partners | — |
admin_email | Override contact email shown on public pages | — |
Branding
| Key | Description |
|---|---|
site_name | Site name shown in nav, emails, and page titles |
site_tagline | Tagline on public landing page |
footer_text | Footer text in the partner portal |
primary_color | Primary brand color (hex) |
secondary_color | Secondary brand color (hex) |
accent_color | Accent color (hex) |
text_color | Body text color (hex) |
bg_color | Page background color (hex) |
logo_path | Filename of uploaded logo (stored in assets/uploads/) |
cover_image | Filename of uploaded hero/cover image |
Stripe Keys
stripe_public_key · stripe_secret_key · stripe_webhook_secret
Square Keys
square_access_token · square_location_id · square_application_id · square_webhook_secret
Email Notifications
All email goes through bc_email() in admin/includes/config.php, which wraps send_mail() (PHPMailer over SMTP). SMTP credentials are defined as constants — not stored in the database.
| Constant | Value |
|---|---|
SMTP_HOST | smtp.gmail.com |
SMTP_USER | a1appbuilders@gmail.com |
SMTP_PORT | 587 (STARTTLS) |
EMAIL_NAME | A1 App Builders |
ADMIN_EMAIL | support@a1appbuilders.com |
Emails Sent Automatically
| Trigger | Recipient |
|---|---|
| Partner registers | Partner (verify link) + Admin (new registration notice) |
| Admin approves partner | Partner (approval + login link) |
| Stripe webhook fires | Partner (if notify_on_sale = 1) |
| Square webhook fires | Partner (if notify_on_sale = 1) |
| Manual sale recorded | Partner (if notify_on_sale = 1) |
| Payout processed | Partner (payout summary) |
| Lead email verified | Banner owner + Partner (if notify_on_lead = 1) |
Session & Security
Session Timeout — Two-Layer Enforcement
Timeout is 30 minutes (SESSION_TIMEOUT = 1800). Two layers prevent stale sessions:
1 · Cookie Lifetime
session_set_cookie_params(['lifetime' => SESSION_TIMEOUT]) expires the browser cookie after 30 minutes of inactivity.
2 · Server-Side Check
After session_start(), compares bc_last_activity timestamp. If expired: destroys session, regenerates ID, starts fresh.
if (isset($_SESSION['bc_last_activity'])
&& (time() - $_SESSION['bc_last_activity']) > SESSION_TIMEOUT) {
session_unset();
session_destroy();
session_start();
session_regenerate_id(true);
}
$_SESSION['bc_last_activity'] = time();
CSRF Protection
Every POST form includes a hidden csrf_token field. Generated by csrf_token(), verified by verify_csrf(). Token stored in $_SESSION['bc_csrf'].
Auth Guards
require_bc_admin(); // redirects to /admin/login.php if not admin
require_bc_partner(); // redirects to /partners/login.php if not partner
// returns partner array on success
Security Headers
Set after session_start() in every request:
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Database
Engine: SQLite3 only. Path: admin/database/bannerconnect.db. WAL mode + foreign keys enabled on every connection.
$db->open() or $db->close() — the constructor/destructor handles connection lifecycle automatically.$db = new Database();
$stmt = $db->conn->prepare("SELECT ...");
$stmt->bindValue(':key', $val, SQLITE3_TEXT);
$row = $stmt->execute()->fetchArray(SQLITE3_ASSOC);
Core Tables
| Table | Purpose |
|---|---|
users | Admin accounts |
partners | Partner accounts and profile data |
banners | Banner records, linked to a partner |
banner_clicks | One row per banner click (IP, UA, referer) |
sales | Commission-eligible sales from all sources |
payouts | Payout batch records |
payout_items | Individual sales included in each payout |
settings | Key/value configuration store |
activity_log | All significant system actions |
login_log | Login attempts — success and failure |
Key Sales Columns
| Column | Values |
|---|---|
sale_status | confirmed |
commission_status | pending → paid |
source | stripe_webhook, square_webhook, manual |
payment_processor | stripe, square, manual, zelle, other |
Logs & Error Handling
PHP Error Log
bannerconnect/logs/php_errors.log — created automatically if the directory doesn't exist.
Activity Log
activity_log table via bc_log_activity(). Fields: actor type/id/name, action, IP.
Login Log
login_log table via bc_log_login(). Records both successes and failures with IP and user agent.
All DB operations and mail sends are wrapped in try/catch. A logging or email failure will never cause a 500 error.
Config Reference
File: admin/includes/config.php — included by every PHP file in the system.
Path Constants
| Constant | Resolves to |
|---|---|
BC_DIR | .../bannerconnect/ |
ADMIN_DIR | .../bannerconnect/admin/ |
SITE_ROOT | .../a1appbuilders.com/ |
DB_PATH | admin/database/bannerconnect.db |
UPLOADS_DIR | assets/uploads/ (server path) |
LOGS_DIR | bannerconnect/logs/ |
Web Path Constants
| Constant | Value |
|---|---|
WEB_ROOT | /addons/bannerconnect |
ADMIN_WEB_ROOT | /addons/bannerconnect/admin |
PARTNER_WEB_ROOT | /addons/bannerconnect/partners |
UPLOADS_WEB | /addons/bannerconnect/assets/uploads |
Shared Functions
| Function | Purpose |
|---|---|
get_setting($db, $key, $default) | Read value from settings table |
set_setting($db, $key, $value) | Upsert value into settings table |
sanitize($v) | htmlspecialchars wrapper |
post($k) | trim($_POST[$k]) |
get($k) | trim($_GET[$k]) |
csrf_token() | Return or generate session CSRF token |
verify_csrf($token) | Validate submitted CSRF token |
require_bc_admin() | Auth guard — admin only |
require_bc_partner() | Auth guard — partner only; returns partner array |
send_mail($to, $subject, $body) | Raw PHPMailer send |
bc_email($to, $subject, $html) | Branded email wrapper around send_mail |
bc_log_activity($db, $type, $id, $name, $action) | Write to activity_log |
bc_log_login($db, $type, $id, $email, $status) | Write to login_log |
Commission Tiers
Rates are set per-partner by admin from the Partners page — there is no automatic tier progression. When a rate is updated, the embed code stays the same; only the commission calculation changes.
| Tier | Default Rate | Banner Color | How to Earn |
|---|---|---|---|
| Starter | 10% | 🔵 Blue | Default on account approval |
| Level 2 | 12% | 🩵 Cyan | Admin rate increase |
| Level 3 | 14% | 🟢 Green | Admin rate increase |
| Level 4 | 16% | 🟡 Gold | Admin rate increase |
| Level 5 | 18% | 🟣 Purple | Admin rate increase |
| ⭐ Elite | 20% | 🔴 Red | Top performers only |