Stripe Integration Guide
Connect your Stripe account to ExisOne to automatically generate and email license keys when customers purchase on your website.
1. Get Your Stripe API Keys
- Log into Stripe Dashboard.
- Go to Developers > API keys.
- Copy your Publishable key (
pk_test_...orpk_live_...). - Copy your Secret key (
sk_test_...orsk_live_...).
2. Configure Stripe Webhook
- In Stripe Dashboard, go to Developers > Webhooks.
- Click Add endpoint.
- Set the endpoint URL to:
https://www.exisone.com/api/stripe/webhook - Select these events to listen for:
checkout.session.completed- Primary event for paymentspayment_intent.succeeded- Backup for one-time paymentsinvoice.paid- For subscription renewals
- Click Add endpoint, then copy the Signing secret (starts with
whsec_).
3. Register Your Stripe App in ExisOne
- In ExisOne, go to Stripe Integration > Stripe Apps and Events.
- Click Add App and enter:
- Publishable key - from step 1
- Secret key - from step 1
- Webhook signing secret - from step 2
- Set Is Live to match your keys (unchecked for test keys, checked for live keys).
- Check Active and Default.
- Save the app.
ExisOne uses the webhook signing secret to verify incoming webhooks are from your Stripe account.
App Status Indicators
After saving, the Stripe Apps table shows a Status column to help you verify your configuration:
| Status | Meaning | Action Required |
|---|---|---|
| Ready | Secret key and webhook signing secret are both configured and can be decrypted | None - your app is ready to process payments |
| Partial | Secret key is configured, but webhook signing secret is missing | Add your webhook signing secret (whsec_...) from Stripe |
| Not configured | Secret key is missing or cannot be decrypted | Enter your secret key (sk_test_... or sk_live_...). If you already entered it, ensure you have an active tenant encryption key. |
4. Configure Your Product in ExisOne
For each product you sell, configure these settings on the Products page:
- Auto Email: Check this box to automatically email license keys after purchase.
- Default License Days: Set to 0 for Perpetual, or a number of days for time-limited licenses.
- Email Template (optional): Customize the email sent to customers.
5. Find Your Product ID
You'll need your Product ID to include in Stripe checkout sessions. Find it on the Products page - it's displayed in the ID column.
42, you'll use "productId": "42" in your Stripe metadata.
6. How the Payment Flow Works
Your Website Stripe ExisOne
| | |
|-- Create checkout session ----->| |
| (using Stripe API directly) | |
| metadata: { productId: "42" } | |
| | |
|<---- Return checkout URL -------| |
| | |
| Customer completes payment | |
| | |
| |-- Webhook ------------------ >|
| | |
| | Verify signature (matches |
| | your stored webhook secret) |
| | |
| | Extract productId from |
| | metadata |
| | |
| | Generate license key |
| | |
| | Email key to customer |
| | |
7. Integrate Stripe on Your Website
Use Stripe's API directly on your website. The key is to include your ExisOne productId in the checkout session metadata.
Server-Side (Node.js Example)
const stripe = require('stripe')('sk_test_YOUR_SECRET_KEY');
app.post('/create-checkout-session', async (req, res) => {
const session = await stripe.checkout.sessions.create({
mode: 'payment',
line_items: [{
price_data: {
currency: 'usd',
unit_amount: 2999, // $29.99 in cents
product_data: {
name: 'Pro License',
},
},
quantity: 1,
}],
metadata: {
productId: '42', // Your ExisOne Product ID - REQUIRED
quantity: '1' // Number of licenses to generate
},
customer_email: req.body.email, // Optional: pre-fill customer email
success_url: 'https://yoursite.com/success',
cancel_url: 'https://yoursite.com/cancel',
});
res.json({ url: session.url });
});
Client-Side (JavaScript)
<button onclick="buyLicense()">Buy Pro License - $29.99</button>
<script>
async function buyLicense() {
// Call your server to create checkout session
const response = await fetch('/create-checkout-session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: customerEmail })
});
const { url } = await response.json();
window.location.href = url; // Redirect to Stripe Checkout
}
</script>
metadata.productId field is required. Without it, ExisOne cannot determine which product to generate a license for.
Required Metadata Fields (One-Time Payments)
| Field | Required | Description |
|---|---|---|
productId | Yes | Your ExisOne Product ID (from Products page) |
quantity | No | Number of license keys to generate (default: 1) |
7b. Subscription Licenses
For subscription-based licensing (e.g., annual renewals), ExisOne generates a new license key on each billing cycle - both on initial signup and every renewal.
productId in the subscription metadata, not just the checkout session metadata. The subscription metadata persists and is read on each renewal.
Server-Side Subscription Example (Node.js)
const stripe = require('stripe')('sk_test_YOUR_SECRET_KEY');
app.post('/create-subscription-checkout', async (req, res) => {
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
line_items: [{
price: 'price_YOUR_RECURRING_PRICE_ID', // Created in Stripe Dashboard
quantity: 1,
}],
// IMPORTANT: Use subscription_data.metadata for renewals to work
subscription_data: {
metadata: {
productId: '42', // Your ExisOne Product ID - REQUIRED
quantity: '1' // Number of licenses per renewal
}
},
customer_email: req.body.email,
success_url: 'https://yoursite.com/success',
cancel_url: 'https://yoursite.com/cancel',
});
res.json({ url: session.url });
});
How Subscription Licensing Works
Initial Subscription Renewals (Annual)
| |
|-- checkout.session.completed --> |-- invoice.paid ----------------->
| |
| ExisOne reads productId from | ExisOne fetches subscription
| session metadata | from Stripe, reads productId
| | from subscription metadata
| |
| Generates license key | Generates NEW license key
| Emails to customer | Emails to customer
| |
Required Metadata Fields (Subscriptions)
| Field | Location | Required | Description |
|---|---|---|---|
productId | subscription_data.metadata | Yes | Your ExisOne Product ID - must be on the subscription for renewals |
quantity | subscription_data.metadata | No | Number of license keys per billing cycle (default: 1) |
price_xxx) to use in your code.
8. Test Your Integration
- Make sure you're using test mode keys (starting with
pk_test_andsk_test_). - Trigger a checkout from your website.
- Complete payment using Stripe test card:
4242 4242 4242 4242(any future expiry, any CVC). - Check Stripe Apps and Events in ExisOne - you should see the webhook event with status "verified".
- Verify the license key appears under License Keys in the ExisOne dashboard.
- Confirm the customer received the license key email (if Auto Email is enabled).
You can also use ExisOne's built-in Test Purchase page to verify webhook connectivity before integrating with your website.
9. Notes
- One-time payments: The
productIdandquantityare read from the Checkout Session metadata. - Subscriptions: Initial purchase reads from session metadata. Renewals read from subscription metadata (set via
subscription_data.metadata). Without this, renewal keys won't be generated. - Perpetual (0 days) overrides any incoming days; otherwise an explicit plan days value overrides product defaults.
- Emails use the product's template (placeholders:
{{ActivationKey}},{{ProductName}},{{CustomerEmail}},{{PaymentProcessorCustomerName}}). - Trials: validation without a key returns a trial if the device was never activated for that product and the product has TrialDays > 0.
Troubleshooting
Status shows "Partial" (missing webhook signing secret)
This means your secret key is configured but the webhook signing secret is missing. To fix this:
- Go to Stripe Dashboard > Webhooks (use test or live mode as appropriate).
- If you don't have a webhook endpoint yet, click Add endpoint:
- Set the URL to:
https://www.exisone.com/api/stripe/webhook - Select events:
checkout.session.completed,payment_intent.succeeded,invoice.paid - Click Add endpoint
- Set the URL to:
- Click on your endpoint, then find Signing secret and click Reveal.
- Copy the value (starts with
whsec_...). - In ExisOne, go to Stripe Apps and Events, click Edit on your app.
- Paste the signing secret into the Webhook Signing Secret field and click Save.
The status should change from "Partial" to "Ready".
Status shows "Not configured" (missing secret key)
This means the secret key is missing or cannot be decrypted. To fix this:
- Go to Stripe Dashboard > API keys.
- Copy your Secret key (
sk_test_...orsk_live_...). - In ExisOne, go to Stripe Apps and Events, click Edit on your app.
- Paste the secret key into the Secret Key field and click Save.
If you still see "Not configured" after saving, you may need to generate a tenant encryption key first. Go to Settings > Tenant Keys and ensure you have an active key.
Subscription renewals not generating license keys
If initial subscription purchases generate keys but renewals don't, the issue is likely missing subscription metadata:
- Ensure you're using
subscription_data.metadata(not justmetadata) when creating the checkout session. - The
productIdmust be on the subscription object itself, not just the checkout session. - Verify the subscription has metadata by checking in Stripe Dashboard: Customers > [Customer] > Subscriptions > [Subscription] > Metadata.
- For existing subscriptions missing metadata, you can add it via Stripe Dashboard or API:
stripe subscriptions update sub_xxx --metadata[productId]=42 - Make sure your webhook endpoint is listening for
invoice.paidevents.
Signature verification fails ("no app matched signature")
- Ensure the webhook Signing secret (
whsec_...) is copied from the exact Stripe endpoint that targetshttps://YOUR-SITE/api/stripe/webhook(test vs live must match). - Re-enter the secret in Stripe Apps and Events and Save; avoid leading/trailing spaces.
- We tolerate Stripe API version mismatches during verification. Optionally, set your Stripe webhook endpoint API version to match your library (e.g.,
2024-04-10) for stricter validation. - Timestamp tolerance is 300s. Use a brand-new checkout rather than resending very old events.
Diagnostics
Verify that your saved secrets decrypt correctly for the right tenant:
GET /api/stripe/diagnostics
Authorization: ExisOneApi <your_token>
Both HasSecret and HasWebhookSigningSecret should be true for your app. If not, generate/activate a tenant key (Tenant Keys page), then re-save your secrets.
Event log
- View events in the UI or
GET /api/stripe/events. - Status values:
failed(includes first error detail),received,verified.
10. Setup Checklist
Before going live, verify all items are complete:
| Step | Where | Status |
|---|---|---|
| Stripe API keys obtained | Stripe Dashboard | |
| Webhook endpoint created pointing to ExisOne | Stripe Dashboard > Webhooks | |
| Stripe app registered with keys and webhook secret | ExisOne > Stripe Apps | |
| Product created in ExisOne | ExisOne > Products | |
| Product has Auto Email enabled | ExisOne > Products | |
| Email template configured | ExisOne > Products > Email Template | |
| Website integration includes productId in metadata | Your website code | |
| Test purchase completed successfully | Your website (test mode) | |
| Webhook received and verified in ExisOne | ExisOne > Stripe Events | |
| License key generated and emailed | ExisOne > License Keys |
11. Go Live
- In Stripe Dashboard, switch to live mode and create a new webhook endpoint pointing to
https://www.exisone.com/api/stripe/webhook. - In ExisOne, add a new Stripe app with your live keys (
pk_live_...,sk_live_...) and the live webhook signing secret. Check Is Live and Active. - Update your website to use your live Stripe secret key.
- Make a real purchase (you can refund it) to verify the complete flow works in production.