DEV Community

ZNY
ZNY

Posted on

Gumroad Software License Key Delivery Automation Setup

Gumroad Software License Key Delivery Automation Setup

软件许可密钥的自动化交付是数字产品销售的核心环节。Gumroad 作为流行的数字产品销售平台,提供了灵活的 API 和 webhook 功能,可实现许可证密钥的自动生成与交付。本文将详细介绍如何配置 Gumroad 软件许可证密钥的自动化交付系统。

整体架构概述

自动化许可证交付系统包含三个核心组件:Gumroad 平台作为销售渠道,自动化后端服务处理许可证生成与存储,以及邮件系统完成最终交付。整个流程从用户付款开始,经由 webhook 触发,在数秒内完成密钥交付。

这种架构的优势在于全流程自动化,无需人工干预即可完成销售闭环。开发者可以将更多精力投入到产品优化而非客服响应上。

第一步:Gumroad Product 配置

登录 Gumroad 后台,创建软件产品并配置产品选项。进入产品编辑页面,找到"许可证密钥"(License Keys)选项卡,启用自动许可证交付功能。

Gumroad 支持两种许可证模式:自行生成密钥或使用外部密钥生成器。若选择外部生成,需提供密钥验证 API 端点。对于需要更高定制化的场景,建议使用自有密钥生成逻辑。

配置产品时需要设置的参数包括:产品 ID(用于 API 调用)、价格、货币、以及可选的分层定价(Tiered Pricing)。确保产品状态为"已发布"以便正常销售。

第二步:创建 Webhook 端点

Webhook 是 Gumroad 向外部系统发送购买事件的方式。你需要在服务器上创建一个公开可访问的 HTTPS 端点来处理这些事件。以下是使用 Node.js Express 框架的基础实现。

const express = require('express');
const crypto = require('crypto');
const app = express();

app.use(express.json());

// 验证 Gumroad webhook 签名
function verifySignature(payload, signature, secret) {
  const expectedSig = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSig)
  );
}

// Webhook 处理端点
app.post('/webhook/gumroad', (req, res) => {
  const signature = req.headers['x-gumroad-signature'];
  const payload = JSON.stringify(req.body);

  if (!verifySignature(payload, signature, process.env.GUMROAD_WEBHOOK_SECRET)) {
    return res.status(403).json({ error: 'Invalid signature' });
  }

  const { purchase } = req.body;

  if (purchase.charge_immediately || purchase.status === 'completed') {
    const licenseKey = generateLicenseKey(purchase.product_id, purchase.email);
    sendLicenseEmail(purchase.email, licenseKey, purchase.product_name);
  }

  res.status(200).json({ received: true });
});

function generateLicenseKey(productId, email) {
  const prefix = productId.substring(0, 4).toUpperCase();
  const timestamp = Date.now().toString(36).toUpperCase();
  const hash = crypto.createHash('sha256')
    .update(email + productId + Date.now())
    .digest('hex')
    .substring(0, 8)
    .toUpperCase();
  return `${prefix}-${timestamp}-${hash}`;
}

app.listen(3000, () => console.log('Webhook server running on port 3000'));
Enter fullscreen mode Exit fullscreen mode

第三步:存储与验证系统

完善的许可证系统需要存储、检索和验证三个功能。以下数据库 schema 设计可用于 MySQL 或 PostgreSQL:

CREATE TABLE license_keys (
  id INT AUTO_INCREMENT PRIMARY KEY,
  license_key VARCHAR(64) UNIQUE NOT NULL,
  product_id VARCHAR(64) NOT NULL,
  customer_email VARCHAR(255) NOT NULL,
  purchase_id VARCHAR(64),
  is_active BOOLEAN DEFAULT TRUE,
  activated_at TIMESTAMP NULL,
  activation_count INT DEFAULT 0,
  max_activations INT DEFAULT 1,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  expires_at TIMESTAMP NULL,
  INDEX idx_license_key (license_key),
  INDEX idx_customer_email (customer_email)
);

CREATE TABLE activation_logs (
  id INT AUTO_INCREMENT PRIMARY KEY,
  license_key_id INT NOT NULL,
  device_fingerprint VARCHAR(255),
  activated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  ip_address VARCHAR(45),
  user_agent TEXT,
  FOREIGN KEY (license_key_id) REFERENCES license_keys(id)
);
Enter fullscreen mode Exit fullscreen mode

第四步:密钥验证 API

软件客户端需要调用验证 API 来确认许可证有效性。以下是验证端点的实现示例:

app.post('/api/verify-license', async (req, res) => {
  const { license_key, device_fingerprint } = req.body;

  const license = await db.query(
    'SELECT * FROM license_keys WHERE license_key = ? AND is_active = TRUE',
    [license_key]
  );

  if (!license.length) {
    return res.json({ valid: false, error: 'Invalid license key' });
  }

  const lic = license[0];

  if (lic.expires_at && new Date(lic.expires_at) < new Date()) {
    return res.json({ valid: false, error: 'License has expired' });
  }

  if (lic.activation_count >= lic.max_activations) {
    return res.json({ valid: false, error: 'Activation limit reached' });
  }

  // 记录激活
  await db.query(
    'UPDATE license_keys SET activation_count = activation_count + 1, activated_at = NOW() WHERE id = ?',
    [lic.id]
  );

  await db.query(
    'INSERT INTO activation_logs (license_key_id, device_fingerprint) VALUES (?, ?)',
    [lic.id, device_fingerprint]
  );

  res.json({ valid: true, product: lic.product_id });
});
Enter fullscreen mode Exit fullscreen mode

第五步:邮件通知配置

使用邮件服务(如 SendGrid、Mailgun 或 Amazon SES)发送许可证密钥。以下是集成示例:

const sgMail = require('@sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);

async function sendLicenseEmail(email, licenseKey, productName) {
  const msg = {
    to: email,
    from: 'licenses@yourdomain.com',
    subject: `Your ${productName} License Key`,
    text: `Thank you for your purchase! Your license key is: ${licenseKey}\n\nTo activate, enter this key in the application settings.`,
    html: `<h2>Thank you for your purchase!</h2><p>Your license key is:</p><code style="font-size:18px;background:#f4f4f4;padding:10px;">${licenseKey}</code><p>To activate, enter this key in the application settings.</p>`
  };

  await sgMail.send(msg);
}
Enter fullscreen mode Exit fullscreen mode

安全最佳实践

生产环境中务必使用 HTTPS 传输,确保 webhook 端点安全;验证 Gumroad 签名防止伪造请求;对许可证密钥进行加密存储;实现速率限制防止滥用;定期清理过期或未使用的许可证记录。

通过以上配置,你将拥有一个完全自动化的 Gumroad 许可证交付系统,从用户付款到密钥送达全程无需人工干预,显著提升客户体验和销售效率。


作者:DevTools Review


This article contains affiliate links. If you sign up through my link, I may earn a commission at no extra cost to you.

Start with Systeme.io free: https://systeme.io/?sa=sa027165898925d92be9ae43faf074864fd9a639b6

Top comments (0)