Kora/extensions/Overview

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

StoreHow the buyer gets the keyRefund or chargeback
BuiltByBitWritten into the download by a license key placeholderRevokes, through the purchase sync
Voxel Shop (Polymart)Written into the download by an External API placeholderRevokes
SpigotMCYour plugin trades the injected buyer ID for the keyRevoke by hand
TebexRedeem page with the tbx- transaction ID and emailRevokes, disputes suspend
GumroadGumroad's own key from the receiptRevokes, disputes suspend
Lemon SqueezyLemon Squeezy's own key from the receiptRevokes
PayhipPayhip's own key, or the redeem pageRevokes
Ko-fiRedeem page with the transaction ID and emailNo refund events
StripeShown on the redeem page right after checkoutRevokes, disputes suspend
WooCommerceRedeem page with the order number and billing emailRevokes
itch.ioRedeem page with the download linkNo refund events
EnvatoRedeem page with the purchase codeNo refund events

Connecting a store

  1. Open Stores, press Create and pick the store. Its logo sits next to the name.
  2. Paste the secrets the store gives you, such as Tebex's webhook secret or Stripe's signing secret.
  3. Press Map Product and enter the store's product ID, or * for every product, and pick the Kora product it sells.
  4. 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.

StoreWhat the buyer enters
Tebex, Gumroad, Lemon Squeezy, Payhip, Ko-fi, WooCommerceThe order or transaction ID and the email they bought with. A wrong email gets the same answer as an unknown order.
StripeNothing. 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.ioTheir download link or key. Kora checks it with itch.io's API.
EnvatoTheir 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.