快速开始
本指南带你从新账号走到后端收到第一条带签名的事件。Kasskeep 是托管服务,你不需要为它运行任何服务器。API 地址是 https://api.kasskeep.com。
1. 开通账号
Section titled “1. 开通账号”自助控制台即将上线。在此之前,账号按申请开通:联系我们,我们会为你创建租户,并邀请你成为所有者。
邀请里带有一个一次性 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" \
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_...)只显示一次,请存进密码管理器。详见密钥与权限。
2. 创建 App
Section titled “2. 创建 App”每个要卖东西的产品,在你的租户里就是一个 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_...),商店通知地址里会用到它。
3. 接入商店
Section titled “3. 接入商店”App Store
Section titled “App Store”- 在 App Store Connect 打开 Users and Access → Integrations → In-App Purchase,生成一个密钥并下载
.p8文件。记下 Key ID 和 Issuer ID。 - 上传密钥。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- 在 App Information → App Store Server Notifications 里选择 Version 2,把正式和沙盒地址都设为
https://api.kasskeep.com/v1/stores/apple/<app_id>/notifications。
Google Play
Section titled “Google Play”创建一个服务账号,在 Play Console 里给它授权,上传它的 JSON 密钥,再建一个推送到 https://api.kasskeep.com/v1/stores/google/<app_id>/rtdn 的 Pub/Sub 推送订阅。完整步骤见 Google Play。
还没有商店账号?
Section titled “还没有商店账号?”打开测试商店,不需要任何商店账号也能走完本指南剩下的部分:
npx @kasskeep/cli test-store enable --id app_xxx4. 集成 SDK
Section titled “4. 集成 SDK”安装 @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 保存成功后才结束交易。没结束的交易会在下次启动时重新提交。
5. 接收回调
Section titled “5. 接收回调”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 字节。