Skip to content

How To: Sell Through Third-Party Marketplaces

This guide walks you through distributing your PyLocket-protected application on third-party marketplaces such as AppSumo, Gumroad, Paddle, and LemonSqueezy — where the marketplace (not your own Stripe) processes customer payments.


Overview

When you sell through a third-party marketplace, the payment flow is handled by the marketplace. PyLocket provides two integration methods for provisioning licenses to marketplace customers:

Option A (Redemption Codes):
  You → Generate codes in portal → Upload codes to marketplace → Customer redeems → License + Download

Option B (API Provisioning):
  Marketplace → Webhook to your backend → Your backend calls PyLocket API → License + Download

Choose Option A when the marketplace supports distributing redemption codes (AppSumo, manual email campaigns, partner deals). Choose Option B when you have a backend that can receive webhooks and call APIs in real time (Gumroad, Paddle, LemonSqueezy, or any custom system).


Supported Marketplaces

Marketplace Typical Integration Notes
AppSumo Redemption codes (Option A) AppSumo distributes codes to customers directly
Gumroad API provisioning (Option B) Gumroad fires webhooks on purchase
Paddle API provisioning (Option B) Paddle fires webhooks on order completion
LemonSqueezy API provisioning (Option B) LemonSqueezy fires webhooks on order creation
Manual / Partner Redemption codes (Option A) For email campaigns, partner bundles, review copies

Option A: Redemption Codes

Best for AppSumo deals and manual distribution where you generate codes upfront and hand them off to the marketplace or directly to customers.

Step 1: Create a Batch of Codes

In the Developer Portal, go to Redemption Codes and click Create Batch.

Configure:

  • Application: Select the app customers will receive
  • Channel: Select the marketplace (e.g., appsumo)
  • Quantity: How many codes to generate (e.g., 500)
  • Label: A descriptive name (e.g., "AppSumo Launch — Tier 1")
  • Device Limit: How many devices each license allows
  • License Expiry: Set to 0 for perpetual, or a number of days
  • License Type: Perpetual, subscription, or trial

Click Generate Codes. The portal shows all generated codes once — save them immediately.

Step 2: Download the CSV

Click Download CSV to get all codes in a spreadsheet-compatible format. Each code follows the format PLR-XXXXXXXX-XXXX.

Step 3: Upload Codes to the Marketplace

For AppSumo:

  1. Go to your AppSumo partner dashboard
  2. Navigate to your product's code management section
  3. Upload the CSV file containing the redemption codes
  4. AppSumo distributes these codes to customers upon purchase

For manual distribution:

  • Send codes via email, share in a spreadsheet, or include in partner bundles
  • Each code can only be redeemed once

Step 4: Customer Redeems a Code

When a customer receives their code, they visit the PyLocket redemption page:

https://app.pylocket.com/redeem

Or you can provide a pre-filled link:

https://app.pylocket.com/redeem?code=PLR-XXXXXXXX-XXXX

The customer:

  1. Enters their redemption code
  2. Optionally provides their email (for license key backup)
  3. Clicks Redeem Code
  4. Receives their license key and download link

Step 5: Monitor Redemptions

In the Developer Portal, the Redemption Codes page shows:

  • Batch progress: Redeemed count vs total (with progress bar)
  • Individual code status: Available, Redeemed, Revoked, or Expired
  • Redeemed by: Customer email (if provided)
  • Timestamp: When each code was redeemed

Step 6: Revoke Unused Codes

If a deal ends or you need to recall unused codes:

  1. Open the batch in the portal
  2. Click Revoke All to revoke all unredeemed codes
  3. Or revoke individual codes from the codes table

Revoked codes return a "code has been revoked" error if someone tries to redeem them.


Option B: API Provisioning

Best for Gumroad, Paddle, LemonSqueezy, and custom backends that can receive webhooks in real time.

Step 1: Get Your API Key

  1. Go to Developer Portal → SettingsAPI Keys
  2. Generate or rotate your API key
  3. Save the key securely — it is shown only once

Step 2: Set Up a Webhook Receiver

Create an endpoint on your backend that receives purchase webhooks from the marketplace.

Python example (Flask):

import requests
from flask import Flask, request, jsonify

app = Flask(__name__)

PYLOCKET_API_KEY = "your-api-key-here"
PYLOCKET_API_URL = "https://api.pylocket.com/v1/marketplace/licenses"
APP_ID = "your-app-uuid"

@app.route("/webhook/purchase", methods=["POST"])
def handle_purchase():
    payload = request.json
    customer_email = payload["email"]
    order_id = payload["order_id"]

    # Call PyLocket to create a license
    response = requests.post(
        PYLOCKET_API_URL,
        headers={
            "X-API-Key": PYLOCKET_API_KEY,
            "Content-Type": "application/json",
        },
        json={
            "app_id": APP_ID,
            "channel": "gumroad",  # or paddle, lemonsqueezy, etc.
            "customer_email": customer_email,
            "marketplace_order_id": order_id,
            "license_config": {
                "device_limit": 1,
                "expiry_days": None,  # perpetual
                "license_type": "perpetual",
            },
        },
    )

    result = response.json()

    # Send the license key + download URL to the customer
    send_email_to_customer(
        email=customer_email,
        license_key=result["license_key"],
        download_url=result["download_url"],
    )

    return jsonify({"status": "ok"})

cURL example:

curl -X POST https://api.pylocket.com/v1/marketplace/licenses \
  -H "X-API-Key: your-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "app_id": "your-app-uuid",
    "channel": "gumroad",
    "customer_email": "customer@example.com",
    "marketplace_order_id": "order-12345",
    "license_config": {
      "device_limit": 1,
      "expiry_days": null,
      "license_type": "perpetual"
    }
  }'

Response:

{
  "license_key": "XXXX-XXXX-XXXX-XXXX",
  "license_id": "uuid-here",
  "app_name": "Your App",
  "download_url": "https://cdn.pylocket.com/v1/downloads/signed-token"
}

Step 3: Per-Marketplace Webhook Setup

Gumroad:

  1. Go to Gumroad → Settings → Webhooks (under the "Advanced" tab)
  2. Add your endpoint URL: https://yourbackend.com/webhook/purchase
  3. Gumroad sends a POST with email, product_id, sale_id, etc.

Paddle:

  1. Go to Paddle → Developer Tools → Events
  2. Subscribe to the transaction.completed event
  3. Paddle sends a POST with customer.email, transaction_id, etc.

LemonSqueezy:

  1. Go to LemonSqueezy → Settings → Webhooks
  2. Add your endpoint URL and subscribe to order_created
  3. LemonSqueezy sends a POST with data.attributes.user_email, data.id, etc.

Step 4: Error Handling

Your webhook handler should handle these cases:

HTTP Status Meaning Action
201 License created successfully Send license to customer
400 Invalid request (missing fields) Log and investigate
401 Invalid API key Check your API key
404 App not found Check your app_id
429 Rate limit exceeded Retry after the Retry-After header value

Billing

Marketplace-sourced licenses follow the same billing model as direct Stripe licenses:

  • $4 per license charged at the time of redemption (Option A) or API provisioning (Option B)
  • Billed via your existing Stripe metered billing
  • No upfront cost for generating redemption codes — you are only charged when a code is actually redeemed
  • Usage events are idempotent — redeeming or provisioning the same license twice does not double-charge

This means you can generate 1,000 codes for an AppSumo deal but only pay for the 347 that actually get redeemed.


Multi-Tier Deals (AppSumo Stacking)

AppSumo deals often use a tiered pricing model. To support this, create a separate batch for each tier with different license configurations:

Tier Batch Label Device Limit Features
Tier 1 "AppSumo Launch — Tier 1" 1 {"tier": "basic"}
Tier 2 "AppSumo Launch — Tier 2" 3 {"tier": "pro"}
Tier 3 "AppSumo Launch — Tier 3" 5 {"tier": "business", "priority_support": true}

In your application's Runtime, read the features field from the license to determine which tier the customer is on:

import pylocket

license_info = pylocket.get_license_info()
tier = license_info.get("features", {}).get("tier", "basic")

if tier == "business":
    enable_priority_support()
    enable_advanced_analytics()
elif tier == "pro":
    enable_advanced_analytics()

Tracking & Analytics

Source Column

The Licenses page in the Developer Portal now shows a Source column with color-coded badges:

  • Purple: AppSumo
  • Indigo: Stripe (direct)
  • Pink: Gumroad
  • Blue: Paddle
  • Yellow: LemonSqueezy
  • Gray: Manual
  • Green: API

Use this to track which marketplace channels are driving the most license activations.

Batch Monitoring

The Redemption Codes page shows real-time batch progress:

  • Total codes generated vs redeemed (with progress bar)
  • Per-code status and redemption details
  • Export data to CSV for marketplace reporting

Troubleshooting

Issue Cause Resolution
"Redemption code not found" Typo in code or wrong code format Verify the code matches the PLR-XXXXXXXX-XXXX format
"This code has already been redeemed" Code used by another customer Each code is single-use; generate more codes if needed
"This code has expired" Code past its expiry date Generate a fresh batch with no expiry or a longer expiry
"This code has been revoked" Code was manually revoked Issue a replacement code from a new batch
"Rate limit exceeded" (redemption page) More than 5 redemption attempts per minute from same IP Wait 60 seconds and retry
API returns 401 Unauthorized Invalid or expired API key Rotate your API key in Settings → API Keys
API returns 429 Too Many Requests Exceeded 60 requests per minute Implement exponential backoff in your webhook handler

See Also