Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .changeset/funding-receipt-payment-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"@hypercerts-org/lexicon": minor
---

Add explicit crypto-vs-fiat classification to `org.hypercerts.funding.receipt`: a new optional `paymentType` field plus `knownValues` on `paymentRail`.

**Non-breaking:** the new field is optional and `knownValues` is advisory — existing receipts remain valid.

- New `paymentType` string with `knownValues: ["crypto", "fiat"]` — the explicit asset-class discriminator. Consumers classify by this field and treat receipts without it (or with an unrecognized value) as unclassified, never silently as fiat. The `amount`/`currency` pair cannot substitute: receipts often record normalized fiat-equivalent amounts for crypto payments, and stablecoins carry fiat-pegged currencies.
- `paymentType` is orthogonal to `paymentRail` (the transfer mechanism): a card-funded crypto purchase is `paymentType: "crypto"` + `paymentRail: "credit_card"`.
- `paymentRail` now declares `knownValues: ["onchain", "payment_processor", "bank_transfer", "credit_card", "cash", "check"]` (previously prose-only examples), giving form UIs a canonical vocabulary instead of free-text input.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep paymentRail wording advisory.

The lexicon still permits custom rail values, so this should not read as “instead of free-text input.” That phrasing can push consumers to reject valid non-listed rails.

Suggested wording
- `paymentRail` now declares `knownValues: ["onchain", "payment_processor", "bank_transfer", "credit_card", "cash", "check"]` (previously prose-only examples), giving form UIs a canonical vocabulary instead of free-text input.
+ `paymentRail` now declares `knownValues: ["onchain", "payment_processor", "bank_transfer", "credit_card", "cash", "check"]` (previously prose-only examples), giving form UIs a recommended vocabulary while still allowing custom values.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- `paymentRail` now declares `knownValues: ["onchain", "payment_processor", "bank_transfer", "credit_card", "cash", "check"]` (previously prose-only examples), giving form UIs a canonical vocabulary instead of free-text input.
`paymentRail` now declares `knownValues: ["onchain", "payment_processor", "bank_transfer", "credit_card", "cash", "check"]` (previously prose-only examples), giving form UIs a recommended vocabulary while still allowing custom values.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.changeset/funding-receipt-payment-type.md at line 11, Update the
`paymentRail` changelog note to keep the wording advisory rather than implying a
closed set of allowed values. In `.changeset/funding-receipt-payment-type.md`,
revise the sentence around `knownValues` so it says the listed rails provide a
canonical vocabulary or examples, while still allowing custom rail values; avoid
language like “instead of free-text input” that suggests consumers should reject
unlisted rails.

- `paymentNetwork`'s description now distinguishes its two uses (chain for `onchain`, processor/scheme for fiat rails) and strongly recommends it for `onchain` payments, since a transaction hash is not interpretable without knowing its chain.

Motivated by downstream integration work (Ma Earth publishing receipts for both fiat and crypto donations): platforms need consumers to split crypto from fiat deterministically, and free-string rails forced every consumer to invent its own heuristic.
29 changes: 15 additions & 14 deletions SCHEMAS.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,20 +272,21 @@ Overall score for an evaluation on a numeric scale.

#### Properties

| Property | Type | Required | Description | Comments |
| ---------------- | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- |
| `from` | `union` | ❌ | The sender of the funds (a free-text string, an account DID, or a strong reference to a record). Optional — omit to represent anonymity. | |
| `to` | `union` | ✅ | The recipient of the funds (a free-text string, an account DID, or a strong reference to a record). | |
| `amount` | `string` | ✅ | Amount of funding received as a numeric string (e.g. '1000.50'). | maxLength: 50 |
| `currency` | `string` | ✅ | Currency of the payment (e.g. EUR, USD, ETH). | maxLength: 10 |
| `paymentRail` | `string` | ❌ | How the funds were transferred (e.g. bank_transfer, credit_card, onchain, cash, check, payment_processor). | maxLength: 50 |
| `paymentNetwork` | `string` | ❌ | Optional network within the payment rail (e.g. arbitrum, ethereum, sepa, visa, paypal). | maxLength: 50 |
| `transactionId` | `string` | ❌ | Identifier of the underlying payment transaction (e.g. bank reference, onchain transaction hash, or processor-specific ID). Use paymentNetwork to specify the network where applicable. | maxLength: 256 |
| `for` | `ref` | ❌ | Optional strong reference to the activity, project, or organization this funding relates to. | |
| `notes` | `string` | ❌ | Optional notes or additional context for this funding receipt. | maxLength: 500 |
| `occurredAt` | `string` | ❌ | Timestamp when the payment occurred. | |
| `createdAt` | `string` | ✅ | Client-declared timestamp when this receipt record was created. | |
| `signatures` | `ref` | ❌ | Optional cryptographic signatures attesting to this record's content. | |
| Property | Type | Required | Description | Comments |
| ---------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
| `from` | `union` | ❌ | The sender of the funds (a free-text string, an account DID, or a strong reference to a record). Optional — omit to represent anonymity. | |
| `to` | `union` | ✅ | The recipient of the funds (a free-text string, an account DID, or a strong reference to a record). | |
| `amount` | `string` | ✅ | Amount of funding received as a numeric string (e.g. '1000.50'). | maxLength: 50 |
| `currency` | `string` | ✅ | Currency of the payment (e.g. EUR, USD, ETH). | maxLength: 10 |
| `paymentType` | `string` | ❌ | Asset class of the payment: 'crypto' for payments settled in cryptocurrency, 'fiat' for payments in government-issued currency. This is the explicit crypto-vs-fiat discriminator — consumers should classify by this field and treat receipts without it (or with an unrecognized value) as unclassified rather than assuming fiat. Orthogonal to paymentRail: a card-funded crypto purchase is paymentType 'crypto' with paymentRail 'credit_card'. Note the amount/currency pair cannot substitute for this field: receipts often record a normalized fiat-equivalent amount (e.g. USD) for crypto payments, and stablecoin payments carry fiat-pegged currencies. knownValues is an open, advisory vocabulary. | maxLength: 20, Known values: `crypto`, `fiat` |
| `paymentRail` | `string` | ❌ | Mechanism by which the funds moved. knownValues is an open, advisory vocabulary — other values are permitted. The rail alone does not determine the asset class (a crypto purchase can settle via 'credit_card'); use paymentType for the crypto-vs-fiat distinction. For 'onchain' payments, set paymentNetwork to the chain and transactionId to the transaction hash. | maxLength: 50, Known values: `onchain`, `payment_processor`, `bank_transfer`, `credit_card`, `cash`, `check` |
| `paymentNetwork` | `string` | ❌ | Optional network within the payment rail: the blockchain for 'onchain' payments (e.g. ethereum, arbitrum, celo) or the processor/scheme for fiat rails (e.g. stripe, paypal, sepa, visa). Strongly recommended for 'onchain' payments — a transaction hash is not interpretable without knowing its chain. | maxLength: 50 |
| `transactionId` | `string` | ❌ | Identifier of the underlying payment transaction (e.g. bank reference, onchain transaction hash, or processor-specific ID). Use paymentNetwork to specify the network where applicable. | maxLength: 256 |
| `for` | `ref` | ❌ | Optional strong reference to the activity, project, or organization this funding relates to. | |
| `notes` | `string` | ❌ | Optional notes or additional context for this funding receipt. | maxLength: 500 |
| `occurredAt` | `string` | ❌ | Timestamp when the payment occurred. | |
| `createdAt` | `string` | ✅ | Client-declared timestamp when this receipt record was created. | |
| `signatures` | `ref` | ❌ | Optional cryptographic signatures attesting to this record's content. | |

#### Defs

Expand Down
18 changes: 16 additions & 2 deletions lexicons/org/hypercerts/funding/receipt.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,28 @@
"description": "Currency of the payment (e.g. EUR, USD, ETH).",
"maxLength": 10
},
"paymentType": {
"type": "string",
"description": "Asset class of the payment: 'crypto' for payments settled in cryptocurrency, 'fiat' for payments in government-issued currency. This is the explicit crypto-vs-fiat discriminator — consumers should classify by this field and treat receipts without it (or with an unrecognized value) as unclassified rather than assuming fiat. Orthogonal to paymentRail: a card-funded crypto purchase is paymentType 'crypto' with paymentRail 'credit_card'. Note the amount/currency pair cannot substitute for this field: receipts often record a normalized fiat-equivalent amount (e.g. USD) for crypto payments, and stablecoin payments carry fiat-pegged currencies. knownValues is an open, advisory vocabulary.",
"knownValues": ["crypto", "fiat"],
"maxLength": 20
},
"paymentRail": {
"type": "string",
"description": "How the funds were transferred (e.g. bank_transfer, credit_card, onchain, cash, check, payment_processor).",
"description": "Mechanism by which the funds moved. knownValues is an open, advisory vocabulary — other values are permitted. The rail alone does not determine the asset class (a crypto purchase can settle via 'credit_card'); use paymentType for the crypto-vs-fiat distinction. For 'onchain' payments, set paymentNetwork to the chain and transactionId to the transaction hash.",
"knownValues": [
"onchain",
"payment_processor",
"bank_transfer",
"credit_card",
"cash",
"check"
],
"maxLength": 50
},
"paymentNetwork": {
"type": "string",
"description": "Optional network within the payment rail (e.g. arbitrum, ethereum, sepa, visa, paypal).",
"description": "Optional network within the payment rail: the blockchain for 'onchain' payments (e.g. ethereum, arbitrum, celo) or the processor/scheme for fiat rails (e.g. stripe, paypal, sepa, visa). Strongly recommended for 'onchain' payments — a transaction hash is not interpretable without knowing its chain.",
"maxLength": 50
},
"transactionId": {
Expand Down