KDPika Licensing Sign inStart free
API v1
Show all languages
Browse documentation

Choose a page in the navigation drawer.

KDPika Licensing · API v1 Advanced

Connect your own store backend

Map verified store purchases to durable licensing commands with four tested reference servers and conservative unknown-result handling.

On this page
Updated 2026-10-18 UTCDownload Markdown

Translate a verified purchase into a durable backend command, then call the existing licensing API once for that authorized effect.

What you will learn

  • Separate payment-provider verification from licensing fulfillment.
  • Configure approved offer mappings without accepting a plan from a browser.
  • Run a complete Node.js, Python, PHP or C# reference receiver and worker.
  • Preserve a paid customer's access and stop safely after an uncertain result.

Prerequisites

  • Complete the Sandbox quickstart.
  • Read create license and idempotency, including the known replay limitations.
  • Create a product and plan; keep their ID or slug mapping privately.
  • Use a backend developer key with licenses:read and licenses:write for that product and environment.
  • Have a trusted store backend that can verify the provider's real purchase, refund and subscription state.
  • Keep private state, backups and exports outside every web root; start in Sandbox.

Separate the three contracts

BoundaryWho authenticates itWhat success proves
Store/provider → your adapterYour adapter uses that provider's actual signature/API verification and confirms merchant, environment, recipient, purchased offer and current stateA particular business effect is authorized; not that licensing completed
Your adapter → reference receiverSeparate private command HMAC, approved mapping and durable effect/sequence checksHTTP 202 means durable receipt, not payment confirmation, license issuance or an email
Reference worker → KDPikaScoped backend developer key, existing API v1, explicit accepted responseThe corresponding license action completed; a full new key is encrypted locally for delivery

KDPika does not process payments from your software buyers. The platform's own billing integration is not your store adapter. A browser return URL, success parameter, unsigned JSON field or customer-selected plan is never payment evidence. The private format below is not a new public KDPika endpoint or a native event format from a provider.

License outbound notifications are a fourth, separate flow. Their X-LMP-Signature must not be treated as a payment-provider signature or reused as this command secret.

Choose the complete reference implementation

Download the complete source archive. It contains all four servers, project/dependency files, the private protocol, example mapping, example command and tested command sender. The source manifest records the archive and member SHA-256 values. No state database, credentials, buyer files, binaries or exported keys are included.

All four implementations run on POSIX macOS/Linux-oriented backends. They are not native Windows ACL/lock examples. Your selected hosting must support the chosen runtime and private persistent SQLite filesystem. PHP shared hosting can use the PHP implementation; the other runtimes are not requirements for KDPika's hosting.

Configure an approved offer mapping

  1. Copy config.example.json to an absolute private file outside your web root.
  2. Replace source and seller_id with stable nonpersonal IDs identifying your store and merchant account.
  3. Keep environment as sandbox for this test.
  4. Set base_url to the HTTPS platform origin, without /api/v1, a path, query or fragment.
  5. Replace product_id with your saved product reference.
  6. Replace each offer key with an independently verified provider price/item reference and its plan_id.
  7. Choose renewal_days deliberately for that offer's renewal policy.
config.example.jsonjson
{
  "source": "your-store",
  "seller_id": "YOUR_STORE_ACCOUNT",
  "environment": "sandbox",
  "base_url": "https://licensing.kdpika.com",
  "product_id": "YOUR_PRODUCT_ID",
  "offers": {
    "YOUR_VERIFIED_PRICE_ID": {
      "plan_id": "YOUR_PLAN_ID",
      "renewal_days": 30
    }
  }
}
Mapping fieldTrusted sourceReject this alternative
source / seller_idPinned backend account configurationMerchant supplied by an unauthenticated caller
environmentExplicit Sandbox or Live adapter configuration and provider modeA URL parameter used to bypass a mismatch
product_id / plan_idApproved product mapping you saved from your workspaceArbitrary browser-selected KDPika plan
offer IDVerified provider object and purchased lineDisplay name or unverified callback metadata alone
recipientConfirmed order/buyer record and your delivery policyAn arbitrary replacement email on a retry
order IDStable purchase line, including subscription line where neededA fresh random ID each time a webhook arrives
effect IDOne durable business action, such as one paid invoice/periodA new ID for every delivery or overlapping event type
sequenceYour adapter's durable authorized command orderProvider delivery time or incrementing every arrival

Creation uses the selected product plan's stored duration. Renewal adds the mapped integer days; 30 days is not automatically a calendar month, and 365 days is not every calendar year. The existing API has no exact provider-period-end setter or in-place plan-change endpoint. Choose an explicit supported entitlement policy, or use a reviewed replacement-license workflow in recipes. Do not advertise exact period alignment that your mapping cannot provide.

Keep secrets and state private

Environment variableRequired value
KDP_FULFILLMENT_CONFIGAbsolute path to the private mapping JSON
KDP_FULFILLMENT_STATEAbsolute owner-only persistent directory outside public_html or other web roots
KDP_FULFILLMENT_SECRETAt least 32 unpredictable characters supplied by your secret store; separate from all provider and outbound notification secrets
KDP_FULFILLMENT_OUTBOX_KEYBase64 of 32 random bytes; back up separately from the encrypted ledger
KDP_API_KEYProduct/environment-scoped backend key; never a client App ID or installation token
KDP_FULFILLMENT_PORTOptional Node/Python/C# loopback port; default 8112
KDP_CONFIRM_LIVEExplicit 1 in addition to a Live mapping/key; never set for these tests
KDP_ALLOW_LOOPBACKTest-only 1 permits literal HTTP 127.0.0.1 API/receiver URLs in Sandbox; never a public TLS exception

Use your hosting's private environment/secret configuration rather than committing values or pasting them into public support logs. Keep state directory mode 0700 and private files 0600. The implementations refuse broad state permissions and bind the database to the mapping and encryption-key fingerprints. API key rotation does not erase order history. Changing a merchant, product, offer mapping or encryption key needs reviewed reconciliation/migration, not an empty state directory.

Create an authorized command

  1. Verify a provider event using that provider's documented authentication.
  2. Fetch or reconcile the actual current payment/order/subscription where the provider requires it.
  3. Confirm account ownership, environment, purchased item, payment outcome and recipient.
  4. Assign or recover the durable effect ID and command sequence in your adapter's own ledger.
  5. Save a complete command to an absolute private JSON file.
command.example.jsonjson
{
  "version": 1,
  "source": "your-store",
  "seller_id": "YOUR_STORE_ACCOUNT",
  "environment": "sandbox",
  "event_id": "YOUR_PROVIDER_EVENT_ID",
  "effect_id": "YOUR_STABLE_PAID_ORDER_LINE_EFFECT",
  "order_id": "YOUR_STABLE_ORDER_LINE_ID",
  "sequence": 1,
  "action": "issue",
  "offer_id": "YOUR_VERIFIED_PRICE_ID",
  "customer": {
    "email": "buyer@example.invalid",
    "name": "Demo Buyer"
  },
  "expected_status": null,
  "expected_expires_at": null
}

All shown fields are required; unknown fields are rejected. IDs use 1–190 ASCII letters, digits, underscore, period, colon or hyphen. Do not include personal details or keys in IDs. issue requires sequence 1 and null expected fields. Other actions require the recorded expected license status and exact expires_at value, including null if appropriate.

Different provider event types can describe the same purchase. For example, a checkout completion and an invoice payment must not each become a separate issuance. Choose one business effect and persist its identity. A second delivery reuses its effect and sequence. Out-of-order commands wait for the missing sequence; assigning a fresh sequence to stale state is not reconciliation.

Send to the private receiver

The supplied sender signs the exact file bytes with X-Fulfillment-Signature. Its message is ASCII UNIX timestamp, one period and the raw body, using HMAC-SHA256. The receiver enforces a 300-second timestamp window, a 262144-byte body limit and exact merchant/environment/mapping checks. These are reference policies, not changes to provider or KDPika contracts.

  1. Start your selected reference's loopback receiver as documented on its language page.
  2. Configure KDP_FULFILLMENT_URL privately as the receiver's exact /store-event URL.
  3. Run the command sender only after the authorization steps above.
bashbash
php send-command.php /private/verified-command.json

Expected receipt: {"accepted":true,"duplicate":false}. An unchanged replay returns duplicate:true. A conflict/refusal returns a nonzero sender exit and a plain instruction to inspect the private receiver; it never retries automatically. HTTP 202 is not a statement that a license or email already exists.

PHP/cURL is required only for this supplied sender CLI; the four HTTP receivers accept the same authenticated protocol. A production adapter can implement the exact HMAC privately in its own language. It must preserve durable receipt uncertainty and stable event/effect identities; it must not forward unverified buyer JSON.

Process and deliver

  1. Run work using your selected implementation's CLI or private cron.
  2. Inspect its safe processed/states JSON.
  3. For DONE issuance, authenticate the intended recipient before exporting the saved key.
  4. Run the selected implementation's export command with the stable order ID and a new absolute private output path.
  5. Deliver through your private authenticated delivery workflow.
  6. Retain the durable business-effect record so a repeated event cannot reissue.

Each work run processes at most 25 ready effects and waits 1.1 seconds after an attempted effect. This is a local pacing safeguard, not an exemption from the platform's shared rate limits. Separate orders can still progress when one order is held.

ActionBehaviorImportant boundary
issueCreate once with stable effect-derived Idempotency-Key; encrypt the returned full key using AES-256-GCMA lost successful response cannot be rolled back or safely reissued automatically
renewVerify current scoped license, then add the configured integer days onceUnknown result is held; no blind additive retry
suspendApply the eligible server suspension transitionActive installation tokens are revoked
restoreApply eligible restorationDoes not revive revoked installation tokens or a terminally revoked license
revokeApply terminal revocation after your verified policy decisionA later dispute/refund reversal does not automatically restore it
record_cancellationSave the verified intent without removing paid accessDoes not cancel/refund provider billing or schedule future work

The encrypted outbox and explicit export are not an email service. No full key is printed by work/inspect/export. A successful export means a new owner-only key file exists, not that the buyer received it. Reuse the saved key for authenticated delivery; do not create another license to resend. Review manual key delivery for the corresponding private workflow.

Reconcile unknown results

StateMeaningWhat to do
WAITINGDurable receipt awaiting its per-order sequenceReconcile the missing authorized command; never skip a gap blindly
IN_FLIGHTIntent saved before the remote call; interruption may have hidden its resultHold the order and inspect provider/dashboard evidence before another write
REVIEWAPI/transport/response/state did not meet the accepted result checksDo not interpret it as proof no license exists; investigate first
DONEExplicit accepted result saved with the local completion stateDeliver or inspect; unchanged event replay is harmless

The current API has known create-replay expiration/concurrency limitations and no public license-list/search endpoint to automatically recover an unknown new ID. The local ledger is durable beyond the API's 24-hour replay retention, but cannot guarantee cross-system exactly-once behavior. Keep backups; never delete a held record or invent another order to get a green result.

Before a non-issue mutation the worker compares a scoped GET with the authorized expected status/expiration. That read and later write are not atomic compare-and-swap. Keep one reviewed writer for that order; manual dashboard changes or another integration can race it. Stop and reconcile such changes rather than claiming automatic consistency.

Node's built-in modules do not expose flock. Its atomic worker.lockdir remains after a killed worker. Confirm the worker is stopped, reconcile held IN_FLIGHT effects, and remove only that reviewed empty lock directory. Never run automatic stale-lock deletion or mix language workers in one state directory. The other three references use POSIX flock.

Expected result

The exact four sources pass actual local Sandbox receiver/worker tests: receipt before issuance, duplicate/conflicting events, signature/mode/mapping refusal, real create/activate/renew/suspend/restore/revoke, durable restart, encrypted export and a gateway that hides a genuinely successful 201. The worker holds the unknown result and sends no second create. This is local application evidence, not a real external provider purchase or SMTP delivery test.

The C# reference builds with zero warnings on .NET 10; its NuGet lock records exact direct/transitive versions. Python's exact requirements are executed in an isolated environment. Package-advisory checks cover known database entries at the recorded check time, not a certification or absence of future vulnerabilities. Recheck dependencies and supported runtimes before go-live.

Common mistakes

  • Accepting the private example command directly from an untrusted buyer: only your authenticated, verified adapter may authorize it.
  • Treating a signed provider event as sufficient without confirming merchant, mode and current purchase outcome.
  • Using a provider delivery ID as the sole business-effect dedupe key when several event types describe one paid purchase.
  • Cancelling an already-paid license immediately when a subscription only stops renewing.
  • Deleting the ledger to retry an unknown create, or silently changing offer mappings on an existing state directory.

Security notes

  • Use TLS and a reviewed ingress with body limits, timeouts and rate controls before any external deployment. Loopback development servers are not production internet servers.
  • PHP's SAPI may buffer request bodies before the script starts; configure the web server's 256 KiB limit as well as the application check. Ask the shared host for the correct per-site setting.
  • Keep full keys, command payloads and recipient email out of web logs, Git, screenshots and analytics. The local ledger contains buyer email/name and needs your retention/access policy.
  • Back up encrypted state and its key separately. Test recovery; encryption does not replace filesystem isolation or protect a compromised backend that holds both.
  • Give the backend key only the required product/environment and read/write scopes. Never put it in desktop/mobile/browser application code.
  • Do not add card collection, provider secret configuration or payment callbacks to KDPika to use these examples.

Troubleshooting

SymptomCauseFix
Command not confirmedReceipt was refused or transport result is unknownInspect the existing inbox/effect before any resend; preserve IDs and exact meaning
401Wrong/duplicate/stale signature or modified bodyCheck the separate command secret and clock; sign exact bytes after serialization
409Merchant/environment/mapping or event/effect/order conflictCorrect trusted configuration; reconcile existing records instead of bypassing them
413Body exceeds the local reference boundKeep commands small; do not send raw provider histories or attachments
WAITING never executesEarlier sequence missing or order heldReconcile the actual authorized history and held result
Work exits with safe configuration errorState permissions, key/config drift or busy lockInspect private configuration and worker ownership; never publish secrets to debug
REVIEW after successful provider paymentLicensing result was refused, mismatched or unknownKeep payment and licensing evidence separate; use request_id/dashboard support to reconcile
Buyer has no emailNo email-delivery workflow was implementedDeliver the saved key through your private authenticated workflow; do not reissue

Next steps

Follow your chosen reference page, test Sandbox issuance and refusal cases, then review licensing recipes before defining renewal and refund policies.

Was this page helpful?

Choose an answer

Do not include personal data, license keys or credentials. Retained for up to 30 days. This is not a support inbox.

Search documentation

Arrow keys to move, Enter to open, Escape to close. Search stays in your browser.

Cookie settings