diff --git a/.changeset/funding-receipt-payment-type.md b/.changeset/funding-receipt-payment-type.md new file mode 100644 index 00000000..9a47756b --- /dev/null +++ b/.changeset/funding-receipt-payment-type.md @@ -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. +- `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. diff --git a/SCHEMAS.md b/SCHEMAS.md index 1c16ca94..de274bb5 100644 --- a/SCHEMAS.md +++ b/SCHEMAS.md @@ -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 diff --git a/lexicons/org/hypercerts/funding/receipt.json b/lexicons/org/hypercerts/funding/receipt.json index e0522e7c..e2f1c492 100644 --- a/lexicons/org/hypercerts/funding/receipt.json +++ b/lexicons/org/hypercerts/funding/receipt.json @@ -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": {