跳转到内容

快速开始

本指南带你从新账号走到后端收到第一条带签名的事件。Kasskeep 是托管服务,你不需要为它运行任何服务器。API 地址是 https://api.kasskeep.com。

自助控制台即将上线。在此之前,账号按申请开通:联系我们,我们会为你创建租户,并邀请你成为所有者。

邀请里带有一个一次性 token。接受邀请、设置密码,然后绑定两步验证。每个成员都必须启用 TOTP。

终端窗口
curl -X POST https://api.kasskeep.com/v1/admin/invitations/accept \
-H "Content-Type: application/json" \
-d '{"token": "<invitation token>", "password": "<your password>", "name": "Your Name"}'

返回的会话只能用来绑定 TOTP。调用 POST /v1/admin/me/totp/setup,把返回的 otpauth_uri 添加到身份验证器 App,再用 POST /v1/admin/me/totp/enable 加 {"code": "123456"} 确认。之后登录,为 CLI 创建一个个人访问 token:

终端窗口
curl -X POST https://api.kasskeep.com/v1/admin/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]", "password": "...", "totp_code": "123456"}'
curl -X POST https://api.kasskeep.com/v1/admin/me/tokens \
-H "Authorization: Bearer kks_..." -H "Content-Type: application/json" \
-d '{"name": "cli", "expires_in_days": 90}'

token(kkp_...)只显示一次,请存进密码管理器。详见密钥与权限。

每个要卖东西的产品,在你的租户里就是一个 App。CLI 用 npx 运行,需要 Node.js 22 及以上。先用你的 token 登录一次,token 会保存在 ~/.config/kasskeep/credentials:

终端窗口
npx @kasskeep/cli login # 按提示粘贴 kkp_... token
npx @kasskeep/cli apps create \
--name "My App" \
--ios-bundle-id com.example.app \
--android-package com.example.app \
--webhook-url https://api.example.com/webhooks/kasskeep

输出里有三个密钥。它们只显示一次,之后无法再查看。

密钥 放在哪里
public_key(pk_...) App 里。可以提交收据、读取当前用户自己的状态。
secret_key(sk_...) 只放在你的后端。可以读取交易详情和事件日志。
webhook_secret(whsec_...) 只放在你的后端。用来校验回调签名。

记下 id(app_...),商店通知地址里会用到它。

  1. 在 App Store Connect 打开 Users and Access → Integrations → In-App Purchase,生成一个密钥并下载 .p8 文件。记下 Key ID 和 Issuer ID。
  2. 上传密钥。CLI 在本地读取文件,通过 HTTPS 发送;Kasskeep 会加密保存,且永远不会返回它。
终端窗口
npx @kasskeep/cli apps update --id app_xxx \
--apple-issuer-id <Issuer ID> --apple-key-id <Key ID> --apple-app-apple-id 1234567890 \
--apple-private-key-file ./AuthKey_XXXX.p8
  1. 在 App Information → App Store Server Notifications 里选择 Version 2,把正式和沙盒地址都设为 https://api.kasskeep.com/v1/stores/apple/<app_id>/notifications。

创建一个服务账号,在 Play Console 里给它授权,上传它的 JSON 密钥,再建一个推送到 https://api.kasskeep.com/v1/stores/google/<app_id>/rtdn 的 Pub/Sub 推送订阅。完整步骤见 Google Play。

打开测试商店,不需要任何商店账号也能走完本指南剩下的部分:

终端窗口
npx @kasskeep/cli test-store enable --id app_xxx

安装 @kasskeep/react-native(见 React Native 与 Expo),用公钥完成配置。appUserId 是你自己的用户 id,必须是 UUID。

import { Kasskeep } from "@kasskeep/react-native";
Kasskeep.configure({
apiKey: "pk_...",
baseUrl: "https://api.kasskeep.com",
appUserId: user.id,
testStore: __DEV__,
});
const result = await Kasskeep.purchase({ productId: "pro_monthly", type: "subs" });
if (result.status === "completed") {
console.log(result.customer.subscriptions);
}

SDK 把交易发给 Kasskeep,等 Kasskeep 保存成功后才结束交易。没结束的交易会在下次启动时重新提交。

Kasskeep 会把每个事件 POST 到 App 的 webhook_url。先用原始请求体校验签名,再按事件 id 去重,最后处理。

import express from "express";
import { createHmac, timingSafeEqual } from "node:crypto";
const secret = process.env.KASSKEEP_WEBHOOK_SECRET;
const app = express();
app.post("/webhooks/kasskeep", express.raw({ type: "application/json" }), (req, res) => {
const header = req.get("Kasskeep-Signature") ?? "";
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
const t = Number(parts.t);
if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > 300) return res.sendStatus(400);
const expected = createHmac("sha256", secret).update(`${parts.t}.`).update(req.body).digest();
const actual = Buffer.from(parts.v1 ?? "", "hex");
if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) return res.sendStatus(400);
const event = JSON.parse(req.body.toString("utf8"));
if (event.type === "test") return res.sendStatus(200);
if (alreadyHandled(event.id)) return res.sendStatus(200);
handleEvent(event);
res.sendStatus(200);
});

发一条带签名的测试事件,检查接收端:

终端窗口
curl -X POST https://api.kasskeep.com/v1/admin/tenants/<tenant_id>/apps/<app_id>/webhooks/test \
-H "Authorization: Bearer kkp_..."

返回结果里有状态码、耗时,以及你的接收端响应的前 500 字节。

  • 事件类型,以及每种事件该怎么处理。
  • 环境:别让沙盒和测试数据混进收入。
  • 签名:用测试向量检验你的校验代码。