Overview
Issue a license for every purchase on BuiltByBit, Tebex, Stripe, Gumroad and eight more stores, and revoke it on refunds.
The Stores module connects the stores you sell on. Every purchase gets its own license, and refunds, disputes and ended subscriptions change it without anyone copying keys by hand.
This module depends on core. It replaces the old BuiltByBit module: on first start it imports a BuiltByBit setup it finds, with its secrets, resource links and purchases, and the old /api/v1/builtbybit/placeholder and /api/v1/builtbybit/webhook URLs keep working.
Stores
| Store | How the buyer gets the key | Refund or chargeback |
|---|---|---|
| BuiltByBit | Written into the download by a license key placeholder | Revokes, through the purchase sync |
| Voxel Shop (Polymart) | Written into the download by an External API placeholder | Revokes |
| SpigotMC | Your plugin trades the injected buyer ID for the key | Revoke by hand |
| Tebex | Redeem page with the tbx- transaction ID and email | Revokes, disputes suspend |
| Gumroad | Gumroad's own key from the receipt | Revokes, disputes suspend |
| Lemon Squeezy | Lemon Squeezy's own key from the receipt | Revokes |
| Payhip | Payhip's own key, or the redeem page | Revokes |
| Ko-fi | Redeem page with the transaction ID and email | No refund events |
| Stripe | Shown on the redeem page right after checkout | Revokes, disputes suspend |
| WooCommerce | Redeem page with the order number and billing email | Revokes |
| itch.io | Redeem page with the download link | No refund events |
| Envato | Redeem page with the purchase code | No refund events |
Connecting a store
- Open Stores, press Create and pick the store. Its logo sits next to the name.
- Paste the secrets the store gives you, such as Tebex's webhook secret or Stripe's signing secret.
- Press Map Product and enter the store's product ID, or
*for every product, and pick the Kora product it sells. - Paste the store's Webhook URL into the store. Stores that cannot show the key to the buyer also get a Redeem page URL to link from your product page or receipt.
Each store has its own unguessable URL. Every request is also checked against the store's own signature or secret, so a leaked URL alone cannot issue licenses. New URL replaces both URLs if one leaks.
How purchases become licenses
- One license per purchase and product. Redelivered webhooks, re-downloads and renewals find the same license.
- Buyers become customers. A buyer seen before on the same store is matched by their store account, otherwise by email, otherwise a new customer is created.
- With Use the store's license key on, Gumroad, Lemon Squeezy and Payhip keys become the Kora key, so buyers use the key in their receipt.
- Refunds give the license the Refund status, disputes and ended subscriptions the Dispute status, and won disputes or resumed subscriptions bring it back. A refunded license is never reinstated.
- Purchases of products with no mapping are logged as ignored.
Each store's detail view lists what it sent and what Kora did: issued, existing, revoked, suspended, reinstated, ignored, rejected or failed. Every purchase is on the Purchases page with the license it issued.
The redeem page
/api/v1/stores/redeem/<token> on the API address is a public page in the dashboard's language.
| Store | What the buyer enters |
|---|---|
| Tebex, Gumroad, Lemon Squeezy, Payhip, Ko-fi, WooCommerce | The order or transaction ID and the email they bought with. A wrong email gets the same answer as an unknown order. |
| Stripe | Nothing. Send Checkout or the Payment Link to the page with ?session_id={CHECKOUT_SESSION_ID} and the key appears once the payment is confirmed. |
| itch.io | Their download link or key. Kora checks it with itch.io's API. |
| Envato | Their purchase code. Kora checks it with Envato's API. |
Keys are masked with Show and Copy buttons. The text is under Redeem page on the Lang page.
Store notes
BuiltByBit
Create a placeholder of type External license key with the store's webhook URL and secret. BuiltByBit writes the buyer's key into every download, the same key each time. With a Private API token, Kora also syncs purchases every ten minutes and on Sync Now, and revokes licenses of refunded or chargebacked buyers. A purchase webhook with the body {"event":"purchase","secret":"<secret>","user_id":"{user_id}","username":"{username}","resource_id":"{resource_id}"} issues the license at purchase time.
Voxel Shop
Add an External API placeholder and a webhook for Purchases and Purchase removed, both with the store's webhook URL. Paste the placeholder's API signing secret and the webhook secret into the store. Requests more than five minutes old are refused.
SpigotMC
SpigotMC has no purchase API. Your plugin posts {"user":"%%__USER__%%","resource":"%%__RESOURCE__%%","nonce":"%%__NONCE__%%"} to the webhook URL and gets {"key":"...","status":"active"} back. Values that were not replaced are refused. Fill Allowed buyer IDs from your buyer list to stop invented IDs.
Tebex
Add an endpoint with the webhook URL for payment.completed, payment.refunded, payment.dispute.opened, payment.dispute.won and payment.dispute.lost, paste the secret, then press Validate in Tebex. Kora answers the validation webhook.
Stripe
Add an endpoint for checkout.session.completed, checkout.session.async_payment_succeeded, charge.refunded, charge.dispute.created, charge.dispute.closed and customer.subscription.deleted. Map Payment Link IDs (plink_...) or a kora_product value in the Checkout Session metadata. Test and live mode need separate stores.
Gumroad
Turn on Generate a unique license key per sale, paste your seller ID, and set the webhook URL as the Ping endpoint. Gumroad pings are not signed, so the seller ID and the unguessable URL are what protect them.
Lemon Squeezy
Send order_created, order_refunded, license_key_created, license_key_updated, subscription_expired and subscription_resumed to the webhook URL with the store's signing secret.
Payhip, Ko-fi, WooCommerce
Payhip needs your API key and the paid and refunded webhooks. Ko-fi needs the verification token. WooCommerce needs an Order updated webhook with the store's secret.
itch.io and Envato
Paste an itch.io API key and game ID, or an Envato personal token with View your items' sales history. Buyers use the redeem page.