Docs/Integrations/Stripe

Stripe Integration

Connect Stripe for payment processing — accept card payments for door access and device usage.


Overview

Stripe handles all payment processing in Hygate. When a guest pays for access, Stripe charges their card and notifies Hygate via webhook. Hygate supports Stripe Checkout for a hosted payment experience, Stripe Payment Intents, and webhook-based event handling.


What You Need

  • A Stripe account (stripe.com)
  • API keys (Publishable Key + Secret Key)
  • Webhook signing secret

Setup Steps

Step 1: Get Your API Keys

  1. Log in to Stripe Dashboard
  2. Go to Developers → API keys
  3. Copy the Publishable key (starts with pk_)
  4. Copy the Secret key (starts with sk_)

Use test mode keys during development (prefix pk_test_ and sk_test_). Switch to live mode keys before going to production.

Step 2: Configure Webhooks

  1. Go to Stripe Dashboard → Developers → Webhooks
  2. Click Add endpoint
  3. Enter your webhook URL:
    https://your-domain.com/api/stripe/webhook
    
    Replace your-domain with your actual Hygate domain.
  4. Select these events:
    • checkout.session.completed
    • payment_intent.succeeded
    • charge.refunded
  5. Click Add endpoint
  6. Copy the Signing secret (starts with whsec_)

Step 3: Enter Credentials in Hygate

  1. Go to Settings → Stripe
  2. Paste:
    • Publishable Key
    • Secret Key
    • Webhook Secret
  3. Click Test Connection
  4. If successful, click Save

How Payments Work

Door Payment Flow

Guest scans QR code
        │
        ▼
  /pay/[uid] — payment page loads
  Shows: door name, price, duration, branding
        │
        ▼
  Guest enters email and clicks Pay
        │
        ▼
  Hygate → Stripe: createCheckoutSession()
  Returns: Stripe Checkout URL
        │
        ▼
  Guest redirected to Stripe Checkout
  Enters card details on Stripe's hosted page
        │
        ▼
  Guest pays → Stripe charges card
        │
        ▼
  Stripe → Hygate webhook: payment_intent.succeeded
        │
        ▼
  Hygate creates Payment record (SUCCEEDED)
  Hygate → TTLock: generate QR access code
        │
        ▼
  Guest redirected to /access/[token]
  Shows: QR code, access instructions

Device Payment Flow

Guest scans QR code
        │
        ▼
  /use/[uid] — usage plan selection page
  Shows: device name, available plans, branding
        │
        ▼
  Guest selects a usage plan
  Guest enters email and clicks Pay
        │
        ▼
  Hygate → Stripe: createPaymentIntent()
  Returns: Stripe Checkout URL
        │
        ▼
  Guest redirected to Stripe Checkout
  Enters card details
        │
        ▼
  Guest pays → Stripe charges card
        │
        ▼
  Stripe → Hygate webhook: payment_intent.succeeded
        │
        ▼
  Hygate creates Payment record (SUCCEEDED)
  Hygate creates DeviceSession (ACTIVE)
  Grace period begins → Relay turns ON
        │
        ▼
  Guest redirected to /session/[token]
  Shows: countdown timer, plan details

Stripe Webhook Events

EventAction in Hygate
checkout.session.completedCreates Payment record (door)
payment_intent.succeededCreates Payment + DeviceSession (device)
charge.refundedUpdates Payment status to REFUNDED

Idempotency

Hygate implements webhook idempotency to prevent duplicate charges:

  • Before processing a webhook, Hygate checks if a Payment already exists for that Stripe Payment Intent
  • If a duplicate is detected, the webhook is logged as duplicate_payment_ignored and no action is taken
  • This ensures multiple webhook deliveries (Stripe retries automatically) don't cause duplicate charges

Webhook Security

FeatureDescription
Signature verificationHygate verifies the Stripe signature header on every webhook
Secret storageWebhook secret is encrypted in the database
Replay preventionTimestamps are validated to prevent replay attacks

Test Payments

Stripe Test Mode

Use Stripe's test mode for development and QA:

  1. Toggle Test Mode in the Stripe Dashboard
  2. Use test API keys (prefix pk_test_, sk_test_)
  3. Test with Stripe's test card numbers:
Card NumberResult
4242 4242 4242 4242Successful payment
4000 0000 0000 0002Always declined
4000 0025 0000 3155Requires 3D Secure
4000 0000 0000 9995Insufficient funds
4100 0000 0000 0019Failed postcode

Testing Webhooks Locally

Use the Stripe CLI to forward webhooks to your local development server:

stripe listen --forward-to localhost:3000/api/stripe/webhook

This prints a webhook secret that you can enter in Hygate's Stripe settings.


Troubleshooting

Webhook Not Delivered

CheckAction
Webhook URLVerify the URL is exactly https://your-domain/api/stripe/webhook
Endpoint enabledCheck Stripe Dashboard → Webhooks → your endpoint
Events selectedConfirm payment_intent.succeeded and charge.refunded are subscribed
Test modeEnsure you're using the same mode (test/live) in both Stripe and Hygate

"Connection test failed" in Hygate

CheckAction
API keysVerify you're using the correct keys (test vs. live)
Key formatEnsure no extra spaces or newlines in pasted values
Account statusCheck that your Stripe account is active

Payment Succeeded But No Access Code

CheckAction
Webhook logsCheck Stripe Dashboard → Developers → Logs
Hygate paymentsCheck Payments module for the record
Door statusVerify the door is not in Maintenance Mode
TTLock connectionCheck TTLock integration status

Refund Not Showing

Refunds must be processed through the Stripe Dashboard. After initiating a refund:

  1. Stripe sends a charge.refunded webhook to Hygate
  2. Hygate updates the Payment status to REFUNDED
  3. Check the Payments module — the refund should appear within seconds

Duplicate Payments

If you see duplicate payment records, check:

  1. Stripe webhook logs for duplicate event deliveries
  2. Whether the guest refreshed the payment page
  3. Hygate audit logs for duplicate_payment_ignored entries

Production Checklist

Before going live:

  • Switch from test to live API keys
  • Configure the production webhook URL
  • Verify webhook events are subscribed (payment_intent.succeeded, checkout.session.completed, charge.refunded)
  • Test a full payment flow with a real card
  • Verify branding settings are correct on payment pages
  • Set up Stripe Dashboard alerts for failed payments