Files
simplex-chat/docs/protocol/badges-rpc.schema.json
T
spacedandy-agents-simplex c8b8ba4d4f badge-service: credit a store subscription purchase
The catalog now tells subscription products from one-time ones and maps a
store plan to its tier and months. A subscription receipt is checked with
the store once: with a grantable charge, the service plans the charge's
rows from the store's period start, signs the first month, and writes the
payment, purchase, charge, ledger rows, issuance, state and Play token in
one transaction, answering with the subscription state. Without one, it
answers payment_pending (300 s while the period has not started or the
store is pending, a day otherwise), receipt_invalid for an ended Play
subscription, or test_purchase, and writes nothing. Replays carry the
state. The store verifier interface gains the subscription check, which no
real store implements yet; the fake store does.

Run: 001-badge-subscriptions, slice 3b
2026-10-14 04:36:13 +00:00

402 lines
11 KiB
JSON

{
"metadata": {
"description": "JTD schema for badge service RPC requests and responses, see badges-rpc.md."
},
"definitions": {
"base64url": {
"type": "string",
"metadata": {"format": "base64url encoded string"}
},
"purchaseKey": {
"ref": "base64url",
"metadata": {"comment": "must match the RPC transport key"}
},
"currencyAmount": {
"type": "uint32",
"metadata": {"comment": "USD etc. are in minor units, following Stripe etc. convention"}
},
"badgeInfo": {
"properties": {
"badgeType": {"type": "string"},
"badgeExtra": {"type": "string"},
"badgeExpiry": {"type": "timestamp"}
}
},
"badgeRequest": {
"properties": {
"masterKey": {"ref": "base64url"},
"badgeInfo": {"ref": "badgeInfo"}
}
},
"credential": {
"properties": {
"badgeKeyIdx": {"type": "uint16"},
"masterKey": {"ref": "base64url"},
"signature": {"ref": "base64url"},
"badgeInfo": {"ref": "badgeInfo"}
}
},
"paymentVia": {
"discriminator": "type",
"mapping": {
"card": {
"properties": {"provider": {"enum": ["stripe"]}}
},
"crypto": {
"properties": {"currency": {"enum": ["btc", "xmr"]}}
}
}
},
"paymentTo": {
"discriminator": "type",
"mapping": {
"card": {
"properties": {
"provider": {"enum": ["stripe"]},
"url": {"type": "string"}
}
},
"crypto": {
"properties": {
"currency": {"enum": ["btc", "xmr"]},
"address": {"type": "string"},
"cryptoAmount": {"type": "string"}
}
}
}
},
"invoice": {
"properties": {
"invoiceId": {"type": "string"},
"price": {"ref": "currencyAmount"},
"amount": {
"ref": "currencyAmount",
"metadata": {"comment": "price - discount - credit"}
},
"currency": {"type": "string"},
"expiresAt": {"type": "timestamp"},
"paymentTo": {"ref": "paymentTo"}
},
"optionalProperties": {
"discount": {
"ref": "currencyAmount",
"metadata": {"comment": "discount amount from the price"}
},
"credit": {
"ref": "currencyAmount",
"metadata": {"comment": "credit for upgrade"}
}
}
},
"payment": {
"discriminator": "type",
"mapping": {
"apple": {
"properties": {"jws": {"type": "string"}}
},
"google": {
"properties": {"productId": {"type": "string"}, "token": {"type": "string"}}
},
"invoice": {
"properties": {"invoiceId": {"type": "string"}}
},
"receipt": {
"properties": {
"receipt": {
"type": "string",
"metadata": {"comment": "transfer of unissued months"}
}
}
}
}
},
"upgrade": {
"properties": {
"fromPurchaseKey": {"ref": "base64url"},
"receipt": {"type": "string"},
"receiptSignature": {"ref": "base64url"},
"balance": {"ref": "balance"}
}
},
"catalog": {
"properties": {
"prices": {"elements": {"ref": "badgePrice"}},
"offers": {"elements": {"ref": "badgeOffer"}}
}
},
"badgePrice": {
"properties": {
"priceId": {"type": "string"},
"badgeType": {"type": "string"},
"monthPrice": {"ref": "currencyAmount"},
"currency": {"type": "string"},
"status": {"ref": "badgeItemStatus"},
"createdAt": {"type": "timestamp"}
}
},
"badgeOffer": {
"properties": {
"offerId": {"type": "string"},
"months": {"type": "uint8"},
"discount": {"ref": "offerDiscount"},
"status": {"ref": "badgeItemStatus"},
"createdAt": {"type": "timestamp"}
},
"optionalProperties": {
"priceId": {
"type": "string",
"metadata": {"comment": "absent applies to any price"}
}
}
},
"offerDiscount": {
"discriminator": "type",
"mapping": {
"freeMonths": {
"properties": {"freeMonths": {"type": "uint8"}}
},
"discount": {
"properties": {
"discount": {
"type": "uint8",
"metadata": {"comment": "percent"}
}
}
}
}
},
"badgeItemStatus": {
"enum": ["active", "deprecated", "disabled"],
"metadata": {"comment": "disabled is not sent"}
},
"statement": {
"properties": {
"entries": {"elements": {"ref": "ledgerEntry"}}
},
"optionalProperties": {
"previousEntryId": {
"type": "string",
"metadata": {"comment": "matches the client's asserted entryId, absent for the full ledger"}
}
}
},
"balance": {
"properties": {
"lastEntry": {"ref": "ledgerEntry"}
}
},
"ledgerEntry": {
"properties": {
"entryId": {"type": "string"},
"changeMonths": {"type": "int16"},
"balanceMonths": {"type": "uint16"},
"balanceStartTs": {"type": "timestamp"},
"balanceAnchorTs": {
"type": "timestamp",
"metadata": {"comment": "start of the current run of months; boundaries are counted from it"}
},
"balanceBadgeType": {"type": "string"},
"createdAt": {"type": "timestamp"},
"entryType": {"ref": "ledgerEntryType"}
},
"optionalProperties": {
"wasPausedSince": {"type": "timestamp"}
}
},
"ledgerEntryType": {
"discriminator": "type",
"mapping": {
"credit": {
"properties": {"credit": {"ref": "ledgerCredit"}}
},
"debit": {
"properties": {"debit": {"ref": "ledgerDebit"}}
}
}
},
"ledgerCredit": {
"discriminator": "type",
"mapping": {
"payment": {
"optionalProperties": {
"invoiceId": {
"type": "string",
"metadata": {"comment": "absent for store payments"}
}
}
},
"code": {
"optionalProperties": {},
"metadata": {"comment": "a redeemed code; its invoice belongs to the buyer, not the redeemer"}
},
"charge": {
"properties": {"chargeId": {"type": "string"}},
"metadata": {"comment": "a store subscription's charge, dated (createdAt) to the store's billing period start"}
},
"support": {
"optionalProperties": {}
},
"transferIn": {
"properties": {"fromPurchaseKey": {"ref": "base64url"}}
},
"opening": {
"optionalProperties": {}
}
}
},
"ledgerDebit": {
"discriminator": "type",
"mapping": {
"refund": {
"optionalProperties": {}
},
"upgrade": {
"properties": {"toPurchaseKey": {"ref": "base64url"}}
},
"transferOut": {
"properties": {"toPurchaseKey": {"ref": "base64url"}}
},
"support": {
"optionalProperties": {}
},
"badge": {
"optionalProperties": {}
},
"lapse": {
"optionalProperties": {}
}
}
},
"request": {
"properties": {
"version": {"type": "uint16"},
"request": {"ref": "command"}
},
"optionalProperties": {
"purchaseKey": {
"ref": "purchaseKey",
"metadata": {"comment": "optional for getBadgeCatalog, required for other commands"}
}
}
},
"command": {
"discriminator": "type",
"mapping": {
"getBadgeCatalog": {
"optionalProperties": {}
},
"getBadgeInvoice": {
"properties": {
"priceId": {"type": "string"},
"badgeInfo": {"ref": "badgeInfo"},
"paymentVia": {"ref": "paymentVia"}
},
"optionalProperties": {
"offerId": {
"type": "string",
"metadata": {"comment": "absent for 1 month at the badge price"}
},
"upgrade": {
"ref": "upgrade",
"metadata": {"comment": "upgrade non-store badge"}
}
}
},
"redeemBadgeCode": {
"properties": {
"masterKey": {"ref": "base64url"},
"code": {"type": "string"}
}
},
"purchaseBadge": {
"properties": {
"masterKey": {"ref": "base64url"},
"payment": {"ref": "payment"}
},
"optionalProperties": {
"upgrade": {
"ref": "upgrade",
"metadata": {"comment": "upgrade store 1-time badge"}
}
}
},
"upgradeBadgeSubscription": {
"properties": {
"badgeRequest": {
"ref": "badgeRequest",
"metadata": {"comment": "known wrong: to be dropped when implemented, see badges-rpc.md"}
},
"payment": {
"ref": "payment",
"metadata": {"comment": "store payments only"}
},
"balance": {"ref": "balance"}
}
},
"issueBadge": {
"properties": {
"balance": {"ref": "balance"}
}
},
"pauseBadge": {
"optionalProperties": {}
}
}
},
"response": {
"discriminator": "type",
"mapping": {
"badgeCatalog": {
"properties": {
"catalog": {"ref": "catalog"}
},
"optionalProperties": {
"badgeStatement": {
"ref": "statement",
"metadata": {"comment": "for signed getBadgeCatalog"}
}
}
},
"badgeInvoice": {
"properties": {
"invoice": {"ref": "invoice"},
"badgeType": {"type": "string"},
"months": {"type": "uint8"}
}
},
"badgeCredential": {
"properties": {
"statement": {"ref": "statement"}
},
"optionalProperties": {
"credential": {
"ref": "credential",
"metadata": {"comment": "absent when no balance to issueBadge or no current credential for pause"}
},
"receipt": {
"type": "string",
"metadata": {"comment": "not provided for lifetime badges"}
},
"subscription": {
"type": "string",
"metadata": {"comment": "SubscriptionState: renewing, cancelled, billingRetry, ended; any other value is read as not ended. Present exactly when a store subscription funds the purchase"}
}
}
},
"error": {
"properties": {
"code": {
"type": "string",
"metadata": {"comment": "BadgeServiceErrorCode"}
}
},
"optionalProperties": {
"message": {"type": "string"},
"retryAfter": {"type": "uint32"}
}
}
}
}
}
}