AstroCal
Guides

Payments

Collect payments for bookings using Stripe Connect

Payments

AstroCal supports paid bookings through Stripe Connect. Charge invitees for consultations, coaching sessions, or any bookable event type.

Overview

The payment flow uses Stripe Connect Standard, where your organization connects their own Stripe account. AstroCal creates Payment Intents on behalf of your connected account with a 1% platform fee.

Payment lifecycle:

  1. Connect your Stripe account via OAuth
  2. Set a price on an event type
  3. Invitee creates a booking — receives a client_secret to complete payment
  4. Invitee completes payment on your frontend using Stripe.js
  5. Stripe webhook confirms payment — booking moves to confirmed
  6. If payment times out (30 minutes), booking is automatically cancelled

Connect Stripe

Before accepting payments, connect your Stripe account:

# Initiate OAuth flow — returns a URL to redirect the user to
curl -X GET https://api.astrocal.dev/v1/stripe/connect \
  -H "Authorization: Bearer YOUR_API_KEY"

# Response
{
  "url": "https://connect.stripe.com/oauth/authorize?..."
}

After the user authorizes on Stripe, they're redirected back to your application. Check connection status:

curl -X GET https://api.astrocal.dev/v1/stripe/status \
  -H "Authorization: Bearer YOUR_API_KEY"

# Response (connected)
{
  "connected": true,
  "stripe_account_id": "acct_...",
  "connected_at": "2026-02-14T12:00:00.000Z",
  "charges_enabled": true,
  "payouts_enabled": true
}

Set a Price on an Event Type

Add price_amount (in cents) when creating or updating an event type:

# Create a paid event type ($50 consultation)
curl -X POST https://api.astrocal.dev/v1/event-types \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Strategy Consultation",
    "slug": "strategy-consultation",
    "duration_minutes": 60,
    "price_amount": 5000,
    "price_currency": "usd"
  }'

# Make an event type free again
curl -X PATCH https://api.astrocal.dev/v1/event-types/:id \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "price_amount": null }'
  • price_amount is in the smallest currency unit (cents for USD). Minimum is 100 (i.e., $1.00).
  • price_currency defaults to "usd". Must be a 3-character ISO currency code.
  • Event types without price_amount (or with null) are free — the booking flow is unchanged.

Create a Paid Booking

When an invitee books a paid event type, the response includes a payment object:

curl -X POST https://api.astrocal.dev/v1/bookings \
  -H "Content-Type: application/json" \
  -d '{
    "event_type_id": "...",
    "start_time": "2026-03-05T14:00:00Z",
    "invitee_name": "Jane Doe",
    "invitee_email": "jane@example.com",
    "invitee_timezone": "America/New_York"
  }'

# Response for paid booking
{
  "id": "...",
  "status": "pending_payment",
  "start_time": "2026-03-05T14:00:00.000Z",
  "end_time": "2026-03-05T15:00:00.000Z",
  "payment": {
    "amount": 5000,
    "currency": "usd",
    "client_secret": "pi_xxx_secret_yyy",
    "stripe_payment_intent_id": "pi_xxx"
  }
}

Key differences from free bookings:

  • Status is pending_payment instead of confirmed
  • payment field contains the Stripe client_secret needed to complete payment
  • No calendar event or emails are sent until payment succeeds
  • The time slot is held — other invitees cannot book the same slot

Complete Payment on the Frontend

Use the client_secret with Stripe.js to collect payment:

const stripe = Stripe("pk_live_...");

const { error } = await stripe.confirmPayment({
  clientSecret: booking.payment.client_secret,
  confirmParams: {
    return_url: "https://yourapp.com/booking-confirmed",
  },
});

Handle Webhooks

Set up a Stripe webhook endpoint pointing to your AstroCal instance:

POST https://api.astrocal.dev/v1/stripe/webhooks

Subscribe to these events in your Stripe dashboard:

  • payment_intent.succeeded — confirms the booking, triggers calendar event and emails
  • payment_intent.payment_failed — cancels the booking

AstroCal verifies the webhook signature using your webhook signing secret (STRIPE_WEBHOOK_SECRET env var).

Payment Timeout

If payment is not completed within 30 minutes of booking creation:

  1. The Stripe Payment Intent is cancelled
  2. The booking status changes to cancelled with reason "Payment timeout"
  3. The held time slot is released

A background worker checks for timed-out bookings every 5 minutes.

Refund Policy

When a confirmed paid booking is cancelled:

  • If the cancellation is 24+ hours before the start time: a full refund is issued automatically
  • If the cancellation is less than 24 hours before: no automatic refund (the payment stands)

When a pending_payment booking is cancelled, the Payment Intent is cancelled at Stripe (no charge was made).

Platform Fee

AstroCal charges a 1% platform fee on each payment, with a minimum of $0.50. This is collected via Stripe's application_fee_amount on the Payment Intent.

Booking PricePlatform Fee
$10.00$0.50 (minimum)
$50.00$0.50
$100.00$1.00
$500.00$5.00

Disconnect Stripe

To remove the Stripe connection:

curl -X DELETE https://api.astrocal.dev/v1/stripe/disconnect \
  -H "Authorization: Bearer YOUR_API_KEY"

This deauthorizes the OAuth connection and removes the stored account ID. Existing paid bookings are not affected, but new paid bookings cannot be created until Stripe is reconnected.

Environment Variables

VariableRequiredDescription
STRIPE_SECRET_KEYYes (for payments)Your Stripe platform secret key
STRIPE_CONNECT_CLIENT_IDYes (for payments)Stripe Connect OAuth client ID (ca_...)
STRIPE_WEBHOOK_SECRETYes (for payments)Webhook signing secret (whsec_...)

On this page