Stripe Integration
Stripe is jekcms's default payment processor for non-TRY currencies. It handles USD, EUR, GBP, and 130+ other currencies, supports Apple Pay / Google Pay out of the box, and gives you a polished hosted checkout that works in every country Stripe operates in.
Use Stripe for: license purchases, one-off product sales, subscription plans, and anything you'd charge a customer for who isn't paying in Turkish Lira. For TRY payments, see the [iyzico integration](/docs/integrations/iyzico).
Getting your API keys
- Sign in at [dashboard.stripe.com](https://dashboard.stripe.com)
- Finish the business profile if this is a new account (you can start in test mode without this, but going live requires it)
- Developers → API keys — you'll see two pairs:
- Test mode: pk_test_... / sk_test_... - Live mode: pk_live_... / sk_live_... (revealed only after business profile activation)
Copy both the publishable key (pk_...) and the secret key (sk_...). Never commit the secret key to source control.
Adding the keys to jekcms
- Admin → Settings → Payment Settings
- Scroll to the Stripe section
- Paste:
- Publishable key → pk_test_... (starts with pk_) - Secret key → sk_test_... (starts with sk_)
- Leave Test mode toggle ON while you're integrating
- Save
Keys are encrypted at rest with the same AES-256 scheme used for other API credentials.
Test mode toggle
When Test mode is ON, jekcms uses the test keys for every Stripe API call. Customers see a normal checkout but no real money moves — you can use Stripe's test cards (e.g., 4242 4242 4242 4242) to simulate successful, declined, and 3DS-challenge flows.
Flipping test mode OFF switches to the live keys. Make sure both live keys are filled in before flipping — an empty live key = broken checkout.
Setting up the webhook
Stripe pushes async events (payment succeeded, refund, chargeback) to your webhook endpoint. jekcms ships a ready-to-use endpoint at:
https://yourdomain.com/api/payment/stripe-webhook.php
In the Stripe dashboard:
- Developers → Webhooks → Add endpoint
- Endpoint URL: paste the URL above
- Events to send — select:
- checkout.session.completed - charge.refunded - charge.dispute.created - payment_intent.payment_failed
- Click Add endpoint
- On the created endpoint's detail page, reveal the Signing secret (starts with
whsec_...)
Paste the signing secret back in jekcms Admin → Settings → Payment Settings → Stripe webhook secret and save.
What each event does
| Stripe event | jekcms action | |---|---| | checkout.session.completed | Marks the order as paid, issues a license key or fulfills the purchase, emails the customer | | charge.refunded | Marks the order refunded, revokes the license if it was license-based | | charge.dispute.created | Flags the order as disputed, locks the associated account pending resolution | | payment_intent.payment_failed | Marks the attempt failed; optional retry email to the customer |
Signature verification
Every incoming webhook includes a Stripe-Signature header. api/payment/stripe-webhook.php verifies this signature against your stored webhook secret using Stripe's official library. Requests that fail verification return 400 and are never acted on — someone spoofing checkout.session.completed from outside can't trick jekcms into issuing a license.
Supported flows
License purchase
The main integration point is customer/checkout.php. When a visitor clicks "Buy STD license":
customer/checkout.phpcreates a Stripe Checkout Session via the API using the server-side secret key- The visitor is redirected to Stripe's hosted checkout page
- On successful payment, Stripe redirects back to
customer/checkout-success.php - Stripe also fires
checkout.session.completedto your webhook — this is what actually issues the license (redirect-based confirmation is never trusted, since a user could fake the URL) - The customer receives their license key via email and sees it in their account dashboard
Custom products
If you sell other digital goods, the same checkout.session.completed event fires and the order type determines fulfillment. See the [orders API reference](/docs/api/orders) for extending this.
Going live
Pre-flight checklist:
- Business profile complete in Stripe — confirmed business address, tax ID, bank account for payouts
- Live keys in jekcms — paste
pk_live_...andsk_live_...into the payment settings (separate fields from test keys — both sets can coexist) - Production webhook created — add a second endpoint in Stripe Dashboard pointing to your live
https://yourdomain.com/api/payment/stripe-webhook.php. The test and live endpoints are separate — the test webhook secret is not valid in live mode. - Production webhook secret — paste the live
whsec_...into jekcms - Flip Test mode OFF in jekcms and save
- Run one small live purchase ($1 test license) to confirm end-to-end
Once a real payment goes through and the license is issued automatically, you're live.
Troubleshooting
Webhook returns 400. Signature verification failed. Usually because the webhook secret in jekcms doesn't match the one in Stripe, or you used the test secret on the live endpoint (or vice versa).
Customer paid but no license issued. Check Admin → Orders → Logs. If the order is stuck in pending, the webhook never arrived — check Stripe Dashboard → Webhooks → your endpoint → Events tab for delivery failures.
"No such customer" in Stripe logs. You flipped from test to live but some old test-mode data references test-mode Stripe IDs. Only relevant for subscription flows; one-off purchases don't hit this.