Overview
GritCMS integrates with Stripe to process payments for products and courses directly on your site. When a customer clicks "Buy Now" on a product or "Enroll Now" on a paid course, an inline Stripe Elements form appears on the page -- no redirect to an external checkout page. After payment succeeds, orders are fulfilled automatically (course enrollments are created, digital products are delivered, etc.).
How It Works
The payment flow follows these steps:
- Customer clicks Buy/Enroll -- The frontend sends a checkout request to the API with the product or course ID.
- API creates a PaymentIntent -- The backend creates a Stripe PaymentIntent for the correct amount and currency, creates an order in
pendingstatus, and returns the client secret. - Customer enters payment details -- Stripe Elements renders a secure payment form inline on the page. The customer enters their card details (or uses Apple Pay, Google Pay, etc.).
- Payment is confirmed -- The frontend confirms the payment with Stripe. On success, it calls the confirmation endpoint to verify and fulfill the order immediately.
- Webhook backup -- Stripe sends a
payment_intent.succeededwebhook as a backup. If the frontend confirmation didn't complete (e.g., the user closed the browser), the webhook ensures the order is still marked as paid and fulfilled.
Setup
1. Get Stripe API Keys
- Create a Stripe account if you don't have one.
- Navigate to Developers > API keys.
- Copy your Publishable key and Secret key.
For testing, use the test mode keys (prefixed with pk_test_ and sk_test_). Switch to live keys when you're ready to accept real payments.
2. Configure Environment Variables
Add the following to your .env file:
STRIPE_SECRET_KEY=sk_test_your_secret_key
STRIPE_PUBLISHABLE_KEY=pk_test_your_publishable_key
STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secretSee the Environment Variables reference for the full list.
3. Set Up Webhooks
Webhooks ensure orders are fulfilled even if the customer's browser disconnects after payment:
- Go to Developers > Webhooks in Stripe.
- Click Add endpoint.
- Set the URL to
https://api.yourdomain.com/api/webhooks/stripe. - Subscribe to these events:
payment_intent.succeededpayment_intent.payment_failed
- Copy the Signing secret and set it as
STRIPE_WEBHOOK_SECRETin your.env.
4. Restart the API
After updating the environment variables, restart the API server. If the Stripe keys are valid, payment features are automatically enabled.
Product Checkout
When a customer visits a product page at yoursite.com/products/{slug} and clicks the Buy Now button:
- If not logged in, they are redirected to the login page.
- After authentication, a checkout request is sent to the API.
- A Stripe PaymentIntent is created for the product's price.
- The Stripe Elements payment form appears inline on the product page.
- The customer enters their payment details and submits.
- On success, they are redirected to a confirmation page showing their order details.
Course Checkout
For paid courses at yoursite.com/courses/{slug}:
- Free courses show an Enroll Now button that grants immediate access.
- Paid courses show an Enroll Now button with the price displayed.
- When clicked, the checkout flow is identical to product checkout.
- After payment succeeds, the customer is automatically enrolled in the course and redirected to the learning page at
yoursite.com/learn/{slug}.
If a paid course does not have a linked product in the Commerce module, GritCMS automatically creates one behind the scenes using the course's price and currency.
Coupon Support
Customers can apply coupon codes during checkout. The API validates the coupon and applies the discount before creating the PaymentIntent. Coupons can be percentage-based or fixed-amount discounts. See the Coupons documentation for details on creating and managing coupons.
Order Fulfillment
When a payment succeeds, the following happens automatically:
- The order status is updated from
pendingtopaid. - The
paid_attimestamp is recorded. - For course products, the customer is enrolled in the linked course.
- A
purchase_completedevent is emitted, which can trigger workflow automations.
Fulfillment is triggered by two mechanisms for reliability:
- Direct confirmation -- The frontend calls
POST /api/checkout/{orderId}/confirmimmediately after payment. This verifies the PaymentIntent status via the Stripe API and fulfills the order in real-time. - Webhook -- The
payment_intent.succeededwebhook fires as a backup, ensuring orders are fulfilled even if the frontend confirmation was missed.
Refunds
To refund a paid order:
- Open the order in Commerce > Orders.
- Click the Refund button.
- The order status changes to
Refundedand apurchase_refundedevent is emitted.
For the actual money to be returned to the customer, process the refund through your Stripe Dashboard as well.
Testing
Use Stripe's test card numbers to simulate payments in test mode:
| Card Number | Scenario |
|---|---|
4242 4242 4242 4242 | Successful payment |
4000 0000 0000 3220 | 3D Secure authentication required |
4000 0000 0000 0002 | Card declined |
Use any future expiration date and any 3-digit CVC. See the Stripe testing docs for more test scenarios.
Supported Payment Methods
Stripe Elements automatically adapts to show the payment methods you have enabled in your Stripe Dashboard. By default, this includes:
- Credit and debit cards (Visa, Mastercard, Amex, etc.)
- Apple Pay and Google Pay (when available on the customer's device)
- Link (Stripe's one-click checkout)
You can enable additional payment methods (bank transfers, iDEAL, SEPA, etc.) from the Stripe Dashboard payment methods settings.
Theming
The Stripe Elements payment form automatically matches your site's light or dark mode. GritCMS detects the current theme and passes matching colors, fonts, and border styles to Stripe so the checkout form looks native to your site.