Skip to content

Solana charge intent specification#188

Merged
brendanjryan merged 15 commits intotempoxyz:mainfrom
solana-foundation:feat/solana-support
Mar 24, 2026
Merged

Solana charge intent specification#188
brendanjryan merged 15 commits intotempoxyz:mainfrom
solana-foundation:feat/solana-support

Conversation

@lgalabru
Copy link
Copy Markdown
Contributor

@lgalabru lgalabru commented Mar 18, 2026

Summary

Defines the charge intent for the solana payment method within the HTTP Payment Authentication Scheme. Supports
one-time payments in native SOL and SPL tokens (including Token-2022).

Settlement modes

  • Pull mode (type="transaction", default): client signs, server broadcasts. Enables fee sponsorship and server-side
    retry.
  • Push mode (type="signature", fallback): client broadcasts and presents the confirmed signature.

Key features

  • Fee sponsorship — server co-signs as fee payer; client only signs the transfer authority
  • Payment splits — multiple recipients in a single transaction (ex: platform fees, revenue sharing, fee recovery)
  • Optional Server-provided blockhash — recentBlockhash in the challenge eliminates client RPC dependency in pull mode
  • Transaction simulation — servers SHOULD simulate before broadcast to prevent wasted fees
  • Token program auto-detection — tokenProgram is a hint; clients resolve from the mint if absent (supports PYUSD,
    Token-2022, etc.)
  • externalId as memo — written on-chain via Memo Program for auditing and reconciliation

Request schema

Shared fields: amount, currency, recipient, description, externalId

Method details: network, splToken, decimals, tokenProgram, reference, feePayer, feePayerKey, splits, recentBlockhash


Reference implementation

solana-mpp-sdk - includes unit tests, integration tests, and an interactive demo.


AI assistance (Claude + Codex) were used in drafting this specification. All content has been reviewed for accuracy and RFC compliance.

@lgalabru lgalabru changed the title feat: draft support for Solana Charge Solana charge intent specification Mar 19, 2026
"description": "Marketplace purchase",
"methodDetails": {
"network": "mainnet-beta",
"splToken": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: why not just token? spl is a bit redundant here given scope

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed with 4f2b994

account to cover transaction fees
- MUST verify the transaction contents before signing
(see {{transaction-verification}})
- MAY recover fee costs through pricing or other business
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: probably not necessary for spec -- technical spec itself doesn't need to define social rules around how to monetize

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed with 4f2b994

Details, with `Content-Type: application/problem+json`.
The following problem types are defined for this intent:

https://paymentauth.org/problems/solana/malformed-credential
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

for non-solana specific ones I think we can just use the standard error types

https://paymentauth.org/problems/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed with 4f2b994

~~~json
{
"amount": "10000000",
"currency": "SOL",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

can we use a lowercase currency here?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

in other cases we also just make this the token address directly (e.g. EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v) -- do we need to define splToken? I imagine this may stem from the split of the SPL tokens vs native sol?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Gotcha I think the mix symbol / address got me confused. I thought the "currency" had to be a ticker / human readable.
We'll match Tempo's approach.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed with 4f2b994

to present the same signature to the server. The
challenge binding (the credential echoes the challenge
`id`, which is HMAC-verified) and single-use signature
enforcement mitigate this: only the party that received
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Question on push mode security semantics: is there intended to be any challenge-specific onchain binding beyond matching payment terms?

I see the credential echoing the challenge, and I see the server verifying an unused signature plus a transaction that matches amount / recipient / token. What I’m not sure about is whether the transaction itself is meant to prove that it was constructed for this specific challenge, rather than just for a payment with the same parameters.

For example, if two valid challenges exist for the same payment terms, could a party watching the chain present a matching transaction signature against their own challenge first? If the intent is to provide stronger challenge-specific guarantees in push mode, would it make sense to require a Memo carrying the challenge id, or some other onchain marker, so the transaction is explicitly tied to the issued challenge?

If not, it may be worth documenting push mode as having weaker challenge-specific binding than pull mode.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Per my understanding, this does not materially diverge from the Tempo charge intent: requiring the memo to carry the challenge-id would strengthen challenge-instance binding, but it would also force additional on-chain correlation metadata, so the base spec leaves that privacy vs binding tradeoff to implementations.

Would love to get @brendanjryan's take!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

You are free to do whatever you want here, as long as you call out the tradeoffs.

In the tempo charge spec we are actively considering doing something like what was suggested in order to strengthen invariants, but this should be back compat

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks @brendanjryan. added a note in 0c86f19

MUST have the server's `feePayerKey` set as the fee
payer account.

3. Simulate the transaction using the `simulateTransaction`
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This reads like a MUST here, but the fee payer risks section later frames simulation as SHOULD.

For fee-payer mode, MUST seems right since the server is taking fee risk. For non-fee-payer mode, SHOULD seems reasonable

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I did some back and forth on that one, it deserves a last pass I agree.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed with 1856993

Comment on lines +1286 to +1287
~~~json
{
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This example is out of sync with the receipt schema above.

The schema defines challengeId, but this decoded receipt omits it. If challengeId is intended to be part of emitted receipts, it should appear here too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch thanks

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed with 9d2575b

(`TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`) or
the Token-2022 Program
(`TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb`).
If omitted, clients MUST determine the correct token
Copy link
Copy Markdown

@alexanderattar alexanderattar Mar 21, 2026

Choose a reason for hiding this comment

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

Worth specifying failure behavior here.

If the mint-owner lookup fails, clients should fail closed rather than falling back to the legacy Token Program, especially for Token-2022 assets.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed with 1856993

Copy link
Copy Markdown

@alexanderattar alexanderattar left a comment

Choose a reason for hiding this comment

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

Overall this looks strong and close. The main thing I wanted to call out was push-mode challenge-specific binding. I left a few smaller consistency and operational comments as well.

to present the same signature to the server. The
challenge binding (the credential echoes the challenge
`id`, which is HMAC-verified) and single-use signature
enforcement mitigate this: only the party that received
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

You are free to do whatever you want here, as long as you call out the tradeoffs.

In the tempo charge spec we are actively considering doing something like what was suggested in order to strengthen invariants, but this should be back compat

2. Verify the `mint` field matches the top-level
`currency` field from the challenge request.

3. Verify the `tokenAmount.amount` field matches the
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

is this still true for splits as well? wouldn't this fail on a split payment of

account A: 10
account B: 1

we would check for 11 (10 + 1), when in reality the transfer should be 10

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed with eea140b

@lgalabru lgalabru requested a review from brendanjryan March 24, 2026 14:15
Copy link
Copy Markdown
Collaborator

@brendanjryan brendanjryan left a comment

Choose a reason for hiding this comment

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

LGTM!

@github-actions
Copy link
Copy Markdown

Spec Preview

Spec Changed
draft-card-charge-00 Yes
draft-httpauth-payment-00 Yes
draft-lightning-charge-00 Yes
draft-lightning-session-00 Yes
draft-payment-discovery-00 Yes
draft-payment-intent-charge-00 Yes
draft-payment-transport-mcp-00 Yes
draft-solana-charge-00 -
draft-stripe-charge-00 Yes
draft-tempo-charge-00 Yes
draft-tempo-session-00 Yes

Download spec artifacts (HTML, TXT, XML, PDF)

@brendanjryan brendanjryan merged commit 7044623 into tempoxyz:main Mar 24, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants