Skip to content

Diff: architecture/crypto-license-sales-architecture

From 1c02ec1 to 1c02ec1

+0 / −0 lines
BeforeAfter
--- ---
schema: foundry-doc-v1 schema: foundry-doc-v1
title: "Crypto Payment and License Issuance Architecture" title: "Crypto Payment and License Issuance Architecture"
slug: crypto-license-sales-architecture slug: crypto-license-sales-architecture
category: architecture category: architecture
type: topic type: topic
content_type: topic content_type: topic
quality: complete quality: complete
status: active status: active
audience: vendor-public audience: vendor-public
bcsc_class: no-disclosure-implication bcsc_class: no-disclosure-implication
language_protocol: PROSE-TOPIC language_protocol: PROSE-TOPIC
last_edited: 2026-06-13 last_edited: 2026-06-13
editor: pointsav-engineering editor: pointsav-engineering
paired_with: crypto-license-sales-architecture.es.md paired_with: crypto-license-sales-architecture.es.md
short_description: "The payment and license architecture behind software.pointsav.com — a custodian-free flow from on-chain USDC transfer to Ed25519-signed download token, with no customer accounts and no payment intermediary." short_description: "The payment and license architecture behind software.pointsav.com — a custodian-free flow from on-chain USDC transfer to Ed25519-signed download token, with no customer accounts and no payment intermediary."
cites: [] cites: []
--- ---
A software license purchase on `software.pointsav.com` flows from a Polygon USDC A software license purchase on `software.pointsav.com` flows from a Polygon USDC
transfer on-chain to an Ed25519-signed token that authorises binary downloads. The transfer on-chain to an Ed25519-signed token that authorises binary downloads. The
design is custodian-free: the customer never creates an account, the vendor never holds design is custodian-free: the customer never creates an account, the vendor never holds
customer funds beyond the moment of settlement, and no intermediary is required to route customer funds beyond the moment of settlement, and no intermediary is required to route
the payment. The architecture has three moving parts — a payment watcher, a storefront, the payment. The architecture has three moving parts — a payment watcher, a storefront,
and a release server — described here at the level of their interactions. and a release server — described here at the level of their interactions.
## Why USDC on Polygon ## Why USDC on Polygon
USDC is a USD-pegged stablecoin issued by Circle. Its value is anchored to the US dollar, USDC is a USD-pegged stablecoin issued by Circle. Its value is anchored to the US dollar,
which makes it practical for fixed-price software purchases without exposing either party which makes it practical for fixed-price software purchases without exposing either party
to exchange-rate volatility. Polygon PoS is a proof-of-stake EVM-compatible chain with to exchange-rate volatility. Polygon PoS is a proof-of-stake EVM-compatible chain with
lower transaction fees than Ethereum mainnet, making it economical for purchases in the lower transaction fees than Ethereum mainnet, making it economical for purchases in the
single-digit dollar range. The payment system operates as a read-only observer of public single-digit dollar range. The payment system operates as a read-only observer of public
blockchain state: it watches for ERC-20 Transfer log events on the USDC contract addressed blockchain state: it watches for ERC-20 Transfer log events on the USDC contract addressed
to the vendor wallet. No vendor-side smart contract is required. Any blockchain explorer to the vendor wallet. No vendor-side smart contract is required. Any blockchain explorer
can independently verify a payment using the transaction hash. can independently verify a payment using the transaction hash.
## Payment watcher mechanics ## Payment watcher mechanics
The payment watcher polls the Polygon JSON-RPC endpoint at a configurable interval, The payment watcher polls the Polygon JSON-RPC endpoint at a configurable interval,
walking forward through confirmed blocks and inspecting ERC-20 Transfer log entries. When walking forward through confirmed blocks and inspecting ERC-20 Transfer log entries. When
it finds a Transfer to the vendor wallet address, it inspects the transferred amount to it finds a Transfer to the vendor wallet address, it inspects the transferred amount to
determine which license tier the payment corresponds to — the two tiers each map to a determine which license tier the payment corresponds to — the two tiers each map to a
distinct USDC amount. On each confirmed match, it writes a structured flat-file receipt distinct USDC amount. On each confirmed match, it writes a structured flat-file receipt
containing the transaction hash, the sender address, the block number, the confirmation containing the transaction hash, the sender address, the block number, the confirmation
timestamp, and the derived product identifier. It also appends an entry to a JSONL timestamp, and the derived product identifier. It also appends an entry to a JSONL
transaction log for bookkeeping and audit purposes. transaction log for bookkeeping and audit purposes.
Receipts are authoritative. The storefront will not issue a license token without a Receipts are authoritative. The storefront will not issue a license token without a
matching receipt on disk. The two-stage design — watcher writes receipt, storefront reads matching receipt on disk. The two-stage design — watcher writes receipt, storefront reads
receipt — means the storefront never queries the chain directly; it delegates that receipt — means the storefront never queries the chain directly; it delegates that
responsibility entirely to the watcher. The watcher also supports a fallback RPC URL for responsibility entirely to the watcher. The watcher also supports a fallback RPC URL for
resilience: if the primary RPC endpoint is unreachable, it retries against the fallback resilience: if the primary RPC endpoint is unreachable, it retries against the fallback
before failing. before failing.
## Per-order address derivation ## Per-order address derivation
By default, all payments are directed to a single static vendor wallet address, and the By default, all payments are directed to a single static vendor wallet address, and the
transaction hash serves as the order identifier. For customers who prefer a dedicated transaction hash serves as the order identifier. For customers who prefer a dedicated
receiving address for order tracking or accounting purposes, the storefront can derive one receiving address for order tracking or accounting purposes, the storefront can derive one
from the vendor's BIP-39 master seed using BIP-32 hierarchical deterministic key from the vendor's BIP-39 master seed using BIP-32 hierarchical deterministic key
derivation along the standard Ethereum derivation path. Each order receives a unique index, derivation along the standard Ethereum derivation path. Each order receives a unique index,
and the mapping from order identifier to derivation index is stored locally. Payments to and the mapping from order identifier to derivation index is stored locally. Payments to
derived addresses are watched and receipted by the same watcher that monitors the static derived addresses are watched and receipted by the same watcher that monitors the static
address. The derived-address flow is optional; the standard single-address flow remains address. The derived-address flow is optional; the standard single-address flow remains
the default. the default.
## License token issuance ## License token issuance
Once a receipt exists for a transaction hash, the storefront issues an Ed25519-signed Once a receipt exists for a transaction hash, the storefront issues an Ed25519-signed
license token. The signing key is held exclusively by the storefront and never leaves it. license token. The signing key is held exclusively by the storefront and never leaves it.
The corresponding public verification key is held exclusively by the release server. The corresponding public verification key is held exclusively by the release server.
Neither component holds the other's key material, and no key material is transmitted Neither component holds the other's key material, and no key material is transmitted
to the customer. to the customer.
The token payload records the product identifier, an expiry date (one year from the time The token payload records the product identifier, an expiry date (one year from the time
of issuance at current configuration), and the license tier as a list of entitlements. of issuance at current configuration), and the license tier as a list of entitlements.
The token is formed by prepending the 64-byte Ed25519 signature to the raw payload bytes The token is formed by prepending the 64-byte Ed25519 signature to the raw payload bytes
and encoding the result as a base64url string. The output is a single opaque string the and encoding the result as a base64url string. The output is a single opaque string the
customer stores and presents to the release server. customer stores and presents to the release server.
## Verification at the release server ## Verification at the release server
Verification is stateless and requires no network call. When a download request arrives Verification is stateless and requires no network call. When a download request arrives
with a token, the release server base64url-decodes the string, splits off the first 64 with a token, the release server base64url-decodes the string, splits off the first 64
bytes as the Ed25519 signature, verifies the signature over the remaining bytes using bytes as the Ed25519 signature, verifies the signature over the remaining bytes using
the stored public key, parses the payload, and checks that the product matches the the stored public key, parses the payload, and checks that the product matches the
requested product and that the expiry date has not passed. A mismatched product returns requested product and that the expiry date has not passed. A mismatched product returns
403; an invalid signature returns 401; an expired token returns 403 with a reason string 403; an invalid signature returns 401; an expired token returns 403 with a reason string
indicating the channel has expired. Because the release server holds no signing key, a indicating the channel has expired. Because the release server holds no signing key, a
compromise of the release server does not allow an attacker to mint new tokens. compromise of the release server does not allow an attacker to mint new tokens.
The release server exposes the public verification key at a well-known endpoint. External The release server exposes the public verification key at a well-known endpoint. External
tooling — such as a customer's own installer script — can download the public key once tooling — such as a customer's own installer script — can download the public key once
and subsequently verify tokens offline without contacting the release server at runtime. and subsequently verify tokens offline without contacting the release server at runtime.
## Receipt idempotency and the claim flow ## Receipt idempotency and the claim flow
The storefront's license issuance endpoint is idempotent: querying the same transaction The storefront's license issuance endpoint is idempotent: querying the same transaction
hash multiple times always returns the same token. If a receipt is already on disk, the hash multiple times always returns the same token. If a receipt is already on disk, the
token is issued immediately. If not, the storefront delegates a chain lookup to the token is issued immediately. If not, the storefront delegates a chain lookup to the
payment watcher's check subcommand and, on confirmation, writes the receipt before issuing payment watcher's check subcommand and, on confirmation, writes the receipt before issuing
the token. The first call for a new transaction may involve a chain round-trip; every the token. The first call for a new transaction may involve a chain round-trip; every
subsequent call is served from disk with no latency beyond local I/O. subsequent call is served from disk with no latency beyond local I/O.
A separate claim endpoint records an off-chain association between a binary SHA-256 hash A separate claim endpoint records an off-chain association between a binary SHA-256 hash
and the buyer's wallet address. This forms the basis for a future on-chain ownership and the buyer's wallet address. This forms the basis for a future on-chain ownership
attestation. On-chain minting capability is planned for a future version of the system; attestation. On-chain minting capability is planned for a future version of the system;
the claim record is written now so the data is available when that capability is added. the claim record is written now so the data is available when that capability is added.
## See also ## See also
- [[software-distribution-substrate]] — overview of the three-component distribution system of which this architecture is the payment leg - [[software-distribution-substrate]] — overview of the three-component distribution system of which this architecture is the payment leg
- [[private-git-paid-customer-endpoint]] — how the release server handles token verification and binary delivery at the download endpoint - [[private-git-paid-customer-endpoint]] — how the release server handles token verification and binary delivery at the download endpoint