Skip to content

Stacks Pay: A Payment Request Standard for Stacks Blockchain Payments#202

Open
dantrevino wants to merge 8 commits intostacksgov:mainfrom
dantrevino:feature/stacks-pay
Open

Stacks Pay: A Payment Request Standard for Stacks Blockchain Payments#202
dantrevino wants to merge 8 commits intostacksgov:mainfrom
dantrevino:feature/stacks-pay

Conversation

@dantrevino
Copy link

Stacks Pay is a proposed standard for wrapping transaction information in a standard way for easy sharing.

@dantrevino dantrevino changed the title Feature/stacks pay Stacks Pay: A Payment Request Standard for Stacks Blockchain Payments Dec 2, 2024
@binxbrigandine
Copy link

Love standardization!

@markmhendrickson
Copy link

Thanks for proposing, @dantrevino! We'll review on the Leather team and provide thoughts.

Copy link

@kyranjamie kyranjamie left a comment

Choose a reason for hiding this comment

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

Great work @dantrevino

Copy link
Contributor

@whoabuddy whoabuddy left a comment

Choose a reason for hiding this comment

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

Overall looks good, happy to see you're putting this forward! I left a few small comments.

@human058382928 @r0zar @biwasbhandari these encoded URLs describe a Stacks transaction, might be something cool we can do there with @aibtcdev tooling!

@friedger
Copy link
Contributor

friedger commented Mar 7, 2025

How difficult is it to integrate into existing payment flows? E.g. compared to eth integration. Maybe something for the introduction.

@flormpecasique
Copy link

flormpecasique commented Apr 1, 2025

Hey Dan 👋

Awesome work on this! I wanted to share some thoughts based on my experience building Stacks Invoice Generator, where I tackled invoice generation for STX and Stacks tokens without smart contracts. One of the biggest challenges I faced was QR code functionality.

Initially, I wanted users to scan a QR code and have it automatically open their wallet (Xverse/Leather) with all payment details pre-filled—similar to Bitcoin or Ethereum payment URIs (e.g., bitcoin:address?amount=0.1). However, Stacks doesn’t currently support a universal payment link format, so I had to settle for QR codes that only contain the recipient’s address, requiring users to manually enter the amount and memo.

I believe adding universal payment links for STX and tokens (e.g., stackspay:stx1abc...xyz?amount=100&token=STX&memo=Invoice123) would be a game-changer for the ecosystem. It would:
✅ Enable seamless integration between wallets and apps.
✅ Improve the user experience by eliminating manual data entry.
✅ Make STX payments feel as smooth as Lightning payments in Bitcoin.

Would love to hear your thoughts on this! Is this something that could fit into Stacks Pay?

@dantrevino
Copy link
Author

Hey Dan 👋

Awesome work on this! I wanted to share some thoughts based on my experience building Stacks Invoice Generator, where I tackled invoice generation for STX and Stacks tokens without smart contracts. One of the biggest challenges I faced was QR code functionality.

Initially, I wanted users to scan a QR code and have it automatically open their wallet (Xverse/Leather) with all payment details pre-filled—similar to Bitcoin or Ethereum payment URIs (e.g., bitcoin:address?amount=0.1). However, Stacks doesn’t currently support a universal payment link format, so I had to settle for QR codes that only contain the recipient’s address, requiring users to manually enter the amount and memo.

I believe adding universal payment links for STX and tokens (e.g., stackspay:stx1abc...xyz?amount=100&token=STX&memo=Invoice123) would be a game-changer for the ecosystem. It would: ✅ Enable seamless integration between wallets and apps. ✅ Improve the user experience by eliminating manual data entry. ✅ Make STX payments feel as smooth as Lightning payments in Bitcoin.

Would love to hear your thoughts on this! Is this something that could fit into Stacks Pay?

QR codes are a core part of the experience i the proposal. Check out https://stackspay.org/ and the Merchant Section in the spec for details. The encoding also has the benefit of providing checksum so that users (stackpay parsers) can ensure that the QR codes have not been tampered with.

Copy link
Contributor

@friedger friedger left a comment

Choose a reason for hiding this comment

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

How could an invoice be defined where the merchant requests an amount in BTC and wants to receive the amount on the stacks chain? Or the amount is in usdc and the merchant wants to receive usdcx on the stacks chain?

Do we make an explicit mention of chain id? Default to mainnet if omitted? recipient, token and contract MUST be on the same network (or compatible with bridged tokens, see above)


| Name | Type / Format | Description |
|------|---------------|-------------|
| `recipient` | Stacks c32-address | Address that ultimately receives the payment. |
Copy link
Contributor

Choose a reason for hiding this comment

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

recipient could be also a contract (or in the future a bitcoin address #219)

| Name | Type / Format | Description |
|------|---------------|-------------|
| `recipient` | Stacks c32-address | Address that ultimately receives the payment. |
| `token` | **STX** \| SIP-010 contract address (`SP….<contract>`) | Asset used to pay. When omitted wallets **MUST** default to **STX**. |
Copy link
Contributor

Choose a reason for hiding this comment

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

The token should contain the asset as well so that post conditions can be created easily, in a similar syntax as used in the in-contract post conditions.


---

### 3.3 `mint`
Copy link
Contributor

Choose a reason for hiding this comment

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

Why mint? It could also make sense to have a generic execute operation.

This operation can also make sense for depositing tokens into a vault.


### 3.1 `support`

Description of support operation here
Copy link
Contributor

Choose a reason for hiding this comment

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

Is Description of support operation here a TODO or a title

## 7 Ratification Criteria

1. SIP accepted by governance.
2. At least one reference implementation passes test-vectors.
Copy link
Contributor

Choose a reason for hiding this comment

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

Where are the test-vectors defined?

## 5 Security Considerations

* **Post-conditions** – Wallets **SHOULD** add appropriate post-conditions
limiting the transferred asset/amount.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
limiting the transferred asset/amount.
limiting the transferred asset/amount provided in the parameters `amount` and `token`.

cocoa007 added a commit to cocoa007/nip-caip358-zaps that referenced this pull request Feb 20, 2026
Extends NIP-57 (Lightning Zaps) to support on-chain payments using
CAIP-358 (wallet_pay) alongside BOLT-11 invoices.

Features:
- payment_type tag (bolt11 | caip358) on kind 9734/9735
- CAIP-358 payment request/receipt in zap events
- Stacks transfer types (sip10-transfer, stx-transfer)
- CAIP-19 asset IDs for Stacks tokens (sBTC, STX)
- Multi-chain payment options in single zap request
- Full backward compatibility with NIP-57

Includes real-world example: 200 sats sBTC zap from cocoa007 to
Stark Comet on Stacks mainnet with verified on-chain txids and
the corresponding Nostr event published to relay.damus.io and nos.lol.

References:
- NIP-57: https://github.com/nostr-protocol/nips/blob/master/57.md
- CAIP-358: https://standards.chainagnostic.org/CAIPs/caip-358
- SIP-029: stacksgov/sips#202
- sBTC zap spec: cocoa007/x402-nostr-relay#3
@dantrevino
Copy link
Author

@friedger Valid question about cross-chain scenarios. Three perspectives:

1. Scope decision: This SIP intentionally focuses on Stacks-native payments (STX + SIP-010 tokens). Adding BTC/L2 bridging logic would significantly expand the spec into bridge routing, which is currently out of scope. Chain ID could be added as an optional parameter for future L2s, but mainnet should remain the default (omit = mainnet).

2. Cross-chain is a layer above: If a merchant wants to receive Stacks-denominated assets after a BTC payment, that's a bridge/service layer concern. The wallet pays what the invoice requests (BTC), the bridge converts it, the merchant receives on Stacks. The Stacks Pay URL should represent the payment request (what the payer sends), not the merchant's final receipt.

3. Token contracts handle this: For USDC → USDCx scenarios, the recipient and token contract are the authority. If recipient=SP...usdc-bridge and token=SP...usdc, the bridge contract handles the conversion. The spec doesn't need to know about bridging semantics—it just needs to pass the right address and token contract.

Concrete suggestion: Add a network parameter (optional, defaults to 'mainnet') with values like 'testnet', 'devnet' for test environments. Leave cross-chain bridging to the service layer—this spec stays focused on payment request encoding, not routing.

Worth adding a 'Future Work' section documenting these as out-of-scope with a note that bridge services can build on top of this.


Comment by Patient Eden (autonomous agent, cycle 15339)

@dantrevino
Copy link
Author

Review of SIP-029 (Stacks Pay)

This is a solid proposal! A few suggestions to address the placeholder sections in Section 3:

3.1 support - Suggested description:

"A support operation enables tip/donation flows where the recipient requests support without specifying a fixed amount. Useful for content creators, open source projects, or any voluntary contribution scenario."

3.2 invoice - Suggested description:

"An invoice operation represents a payment request with fixed parameters (recipient, token, amount). Used for e-commerce, services, or any scenario requiring exact payment terms."

3.3 mint - Suggested description:

"A mint operation enables NFT minting flows by invoking a claim function on an NFT contract. The wallet address becomes the minter/receiver. Supports defacto standard contracts (e.g., Gamma) and limits attack surface versus arbitrary contract calls."

Other observations:

  • Section 4 (Custom Operations) is missing a number header - should be "4 Custom Operations" to match the outline
  • The Integration Simplicity section is great for developer adoption - consider moving it to the Introduction or Motivation section to strengthen the rationale

Overall excellent work on standardization! 🚀


Reviewed by Patient Eden (autonomous agent, cycle 15355)

@dantrevino
Copy link
Author

Comprehensive Review of SIP-029 (Stacks Pay)

Thank you for this well-structured payment request standard proposal. The approach aligns well with established patterns from Bitcoin (BIP-21) and Ethereum (EIP-681), which will help developers familiar with those ecosystems. Below are constructive considerations organized by key areas:

1. Developer Experience for Wallet Implementers

Strengths: The three-operation model (support, invoice, mint) is clear and covers most use cases. The use of Bech32m encoding ensures data integrity, and the JSON-like parameter structure is intuitive.

Concerns:

  • Incomplete operation descriptions: Sections 3.1-3.3 contain placeholders ("Description of X operation here") rather than actual specifications. Wallet implementers need concrete guidance on expected behavior.
  • Encoding examples truncated: The encoded URLs in the examples appear cut off, making them unusable for wallet developers testing decoding.
  • No test vectors: A comprehensive set of test vectors covering all operations would significantly aid implementation validation.
  • Fee handling ambiguity: The proposal doesn't specify how transaction fees should be handled. Should wallets add fees on top of the amount, or deduct from it? This affects merchant payment reconciliation.
  • No reference wallet implementation: While libraries exist, a reference wallet implementation demonstrating the complete flow would accelerate adoption.

2. Security Considerations

Identified issues:

  • Post-conditions under-specified: Section 5 mentions post-conditions but doesn't detail how wallets should construct them for each operation type. This could lead to inconsistent security postures across implementations.
  • Mint operation attack surface: The mint operation allows arbitrary contract addresses. While the proposal mentions limiting attack surface, there's no guidance on:
    • How wallets should validate contract authenticity
    • Whether to display warnings for unknown contracts
    • How to handle malicious contracts masquerading as legitimate NFT collections
  • Replay protection unclear: No discussion of whether expiresAt provides adequate replay protection, or if additional nonces are needed.
  • Phishing vectors: Custom operations could enable phishing attacks. The "fail gracefully" guidance needs stronger framing—perhaps requiring explicit user consent and clear warnings.
  • Memo transparency: While noted as on-chain, there's no guidance on what constitutes sensitive data, leading to potential privacy leaks.

3. Edge Cases and Error Handling

Several scenarios need explicit handling:

  • Invalid amount representations: What happens if amount exceeds uSTX limits or has invalid encoding?
  • Non-existent token contracts: How should wallets handle references to non-existent or malformed SIP-010 contracts?
  • Timezone handling: expiresAt parsing could cause issues if implementations differ on timezone assumptions (explicit UTC requirement helps, but examples would clarify).
  • Unsupported tokens: If a wallet doesn't hold the specified token, what's the fallback behavior?
  • Decimal edge cases: Tokens with unusual decimal places (0, 1, or max uint128) need explicit handling in amount conversion.
  • Missing required parameters: Section 2 defines "unknown parameters" handling, but explicitly lists required parameters per operation—what's the error behavior for missing required fields?

4. Compatibility with Existing Stacks Tooling

Breaking change concern: The transition from to with HRP represents a breaking change from what might be the initial implementation. This needs:

  • Clear migration guidance for existing users
  • Backwards compatibility considerations or deprecation timeline
  • Rationale for why this change improves user experience beyond aesthetics

Integration considerations:

  • Bech32m vs c32: Stacks uses c32 encoding for addresses. How do the two encoding schemes interact? Are there conversion utilities in the reference implementations?
  • Existing wallet support: How should wallets currently supporting ad-hoc payment URIs migrate to this standard?
  • Transaction building: The proposal doesn't specify how these parameters map to Stacks transaction construction, particularly post-conditions and fee estimation.

Recommendations

  1. Complete operation descriptions with full workflow examples
  2. Add comprehensive test vectors (both valid and invalid cases)
  3. Clarify fee handling strategy with examples
  4. Strengthen mint operation security with contract validation guidance
  5. Address breaking changes with migration documentation
  6. Add error handling tables for each parameter type
  7. Create reference wallet integration guide

This proposal has strong fundamentals. Addressing these concerns will make it more robust for production use and easier for the ecosystem to adopt.

@dantrevino
Copy link
Author

Technical Review: SIP-029 Stacks Pay Payment URI Specification

Thank you for this well-structured proposal. I've analyzed the specification against BIP-21/BIP-350 standards and have the following feedback organized by key areas.


1. URI Scheme Registration Considerations

Current approach: web+stx:stxpay1...

Feedback:

The web+ prefix follows the W3C Web App Manifest convention for unregistered schemes, but this creates friction:

  1. IANA Registration Path: Unlike BIP-21's bitcoin: which could be registered with IANA, web+stx: is explicitly for web applications. Consider applying for proper URI scheme registration (stx: or stacks:) through IANA. BIP-21 uses bitcoin: directly.

  2. Cross-platform Compatibility: Mobile wallets may struggle with web+ schemes. iOS Universal Links and Android App Links handle custom URL schemes differently than web+ prefixed schemes.

Recommendation: Add a section on URI scheme registration roadmap, or consider dual registration:

stackspay-uri = ("web+stx:" | "stxpay:") bech32m-encoded

2. Backwards Compatibility with Bitcoin BIP-21 Wallets

Critical Observation:

The SIP significantly departs from BIP-21's architecture:

Aspect BIP-21 SIP-029
Address location Path component Encoded in Bech32m payload
Parameter encoding URL query string Bech32m encoded
Forward compatibility `req-` prefix for required params No equivalent mechanism
Direct address visibility Yes No (needs decoding)

Recommendation: Add a `req-` prefix mechanism similar to BIP-21:

required-param = "req-" param-name "=" param-value

Example: `invoice?recipient=SP...&token=STX&req-amount=1000`


3. Address Format Validation

Issues:

  1. Address Ambiguity: The spec states `recipient` is a "Stacks c32-address" but doesn't specify validation rules:
def validate_stacks_address(addr: str) -> bool:
    if not addr.startswith(('S', 'ST')):
        return False
    try:
        decoded = c32_address_decode(addr)
        version = decoded[0]
        return version in (0x16, 0x17, 0x18, 0x19)
    except:
        return False
  1. Missing Principal Format: For `contractAddress`, specify:
    • Principal dot notation: `SP...contract-name`
    • Clarity name requirements (max 128 chars)

4. Memo/Attachment Security

Additional Concerns:

  1. Injection Sanitization:
SAFE_MEMO_CHARSET = r'[a-zA-Z0-9\s\-\.\,\!\?\:\;]{1,256}'
  1. Expiration Limits:
MIN_EXPIRY_SECONDS = 60
MAX_EXPIRY_SECONDS = 365 * 24 * 60 * 60

5. Test Vectors

Recommendation: Add BIP-350-style comprehensive test vectors:

### Valid URIs

| Encoded | Operation | Parameters |
|---------|-----------|------------|
| stxpay1... | invoice | recipient=SP..., amount=1000000 |

### Invalid URIs

| Encoded | Reason |
|---------|--------|
| stxpay1... | Invalid Bech32m checksum |
| stxpay1... | Invalid c32 address |

Summary

Priority Issue
High Add `req-` prefix for forward compatibility
High Add comprehensive test vectors
Medium Specify address validation rules
Medium Document URI scheme registration plans
Low Expand memo security guidance

References: BIP-21, BIP-173, BIP-350, SIP-005

@dantrevino
Copy link
Author

Implementation Feedback from stackspay-js Reference

After reviewing the SIP and implementing the reference library (github.com/dantrevino/stacks-pay-js), here are some suggestions to address open questions:

1. Chain ID / Network Parameter

Agree with @friedger on adding explicit network identification. Suggest:

  • Add optional "network" param with values: mainnet | testnet | devnet
  • Default to mainnet when omitted (most common case)
  • Implementation handles this as: if (network === 'testnet') use testnet constants

2. Contract Recipients

The recipient field should accept contract principals (SP...contract-name). This enables:

  • DAO treasury deposits
  • Contract-based escrow
  • Automated vaults
    Implementation validates via: principal.includes('.') ? validateContractPrincipal(principal) : validateStacksAddress(principal)

3. Token Format for Post-Conditions

Suggest token param use CAIP-19 format: "chainId/assetType/contractAddress/assetId" e.g. "stacks:1/SLP:SP...token-contract::token-name". This provides:

  • Unambiguous asset identification
  • Easy post-condition generation
  • Cross-chain compatibility prep

4. Test Vectors Location

Recommend adding test-vectors.json to the PR with comprehensive examples:

  • Valid/invalid Bech32m strings
  • Each operation type with all params
  • Edge cases (empty memos, max amounts, special chars)

5. Support Operation Description

Replace placeholder with: Support operation enables voluntary contributions without fixed amounts. Recipients specify only their address and optional description/memo. Payers determine amount at time of payment. Ideal for tips, donations, patronage.

Happy to submit PR updates if helpful!

@dantrevino
Copy link
Author

...

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.

8 participants