Stacks Pay is a proposed payment request standard for the Stacks blockchain. It provides a standardized method for encoding, decoding, and processing payment requests, ensuring interoperability between wallets and applications within the Stacks ecosystem.
- Supports Multiple Operations:
support,invoice,mint, and custom operations. - Bech32m Encoding: Ensures compatibility and data integrity, suitable for QR codes and URL sharing.
- Comprehensive Validation: Ensures all parameters meet required formats and constraints.
- Extensible: Allows for custom operations tailored to specific use cases.
npm install stacks-payimport { encodeStacksPayURL, decodeStacksPayURL } from "stacks-pay";
import {
InvoiceOperation,
SupportOperation,
MintOperation,
CommonParameters,
} from "stacks-pay/dist/types";import { encodeStacksPayURL, InvoiceOperation } from "stacks-pay";
const invoiceParams: InvoiceOperation = {
operation: "invoice",
recipient: "SP2RTE7F21N6GQ6BBZR7JGGRWAT0T5Q3Z9ZHB9KRS",
token: "STX",
amount: "1000",
description: "Payment for services rendered",
expiresAt: "2024-12-31T23:59:59Z",
};
const encodedURL = encodeStacksPayURL(invoiceParams);
console.log("Encoded Invoice URL:", encodedURL);import { encodeStacksPayURL, SupportOperation } from "stacks-pay";
const supportParams: SupportOperation = {
operation: "support",
recipient: "SP2RTE7F21N6GQ6BBZR7JGGRWAT0T5Q3Z9ZHB9KRS",
description: "Support for ongoing development",
memo: "Thank you for your support!",
};
const encodedURL = encodeStacksPayURL(supportParams);
console.log("Encoded Support URL:", encodedURL);import { encodeStacksPayURL, MintOperation } from "stacks-pay";
const mintParams: MintOperation = {
operation: "mint",
contractName: "my-nft-contract",
functionName: "mint",
token: "STX",
amount: "500",
recipient: "SP2RTE7F21N6GQ6BBZR7JGGRWAT0T5Q3Z9ZHB9KRS",
description: "Minting a new NFT",
expiresAt: "2024-12-31T23:59:59Z",
};
const encodedURL = encodeStacksPayURL(mintParams);
console.log("Encoded Mint URL:", encodedURL);import { encodeStacksPayURL, CommonParameters } from "stacks-pay";
const customParams: CommonParameters = {
operation: "custom-action",
contractName: "SP2RTE7F21N6GQ6BBZR7JGGRWAT0T5Q3Z9ZHB9KRS",
functionName: "public-dao-function",
memo: "Memo for special action",
data: "Custom action parameters",
};
const encodedURL = encodeStacksPayURL(customParams);
console.log("Encoded Custom Operation URL:", encodedURL);import { decodeStacksPayURL } from "stacks-pay";
const fullURL =
"web+stx:stx1wajky2mnw3u8qcte8ghj76twwehkjcm98ahhqetjv96xjmmw845kuan0d93k2fnjv43kjurfv4h8g02n2qe9y4z9xarryv2wxer4zdjzgfd9yd62gar4y46p2sc9gd23xddrjkjgggu5k5jnye6x76m9dc74x4zcyesk6mm4de6r6vfsxqczver9wd3hy6tsw35k7m3a2pshjmt9de6zken0wg4hxetjwe5kxetnyejhsurfwfjhxst585erqv3595cnytfnx92ryve9xdqn2wf9xdqn2w26juk65n";
const decodedParams = decodeStacksPayURL(fullURL);
console.log("Decoded Parameters:", decodedParams);
// output:
// {
// "operation": "invoice",
// "recipient": "SP2RTE7F21N6GQ6BBZR7JGGRWAT0T5Q3Z9ZHB9KRS",
// "token": "STX",
// "amount": "1000",
// "description": "Payment for services rendered",
// "expiresAt": "2024-12-31T23:59:59Z"
// }Custom operations allow you to extend the standard set of operations (support, invoice, mint) with your own defined actions. Here's how to handle them:
When encoding a custom operation, include any additional parameters relevant to your use case:
import { encodeStacksPayURL, CommonParameters } from "stacks-pay";
const customParams: CommonParameters = {
operation: "custom-action",
recipient: "SP2RTE7F21N6GQ6BBZR7JGGRWAT0T5Q3Z9ZHB9KRS",
memo: "Custom memo for special action",
data: "Some custom data related to the action",
};
const encodedURL = encodeStacksPayURL(customParams);
console.log("Encoded Custom Operation URL:", encodedURL);Upon decoding, all custom parameters will be included in the resulting object:
import { decodeStacksPayURL } from "stacks-pay";
const fullURL = "web+stx:stx1..."; // Your encoded custom operation URL
const decodedParams = decodeStacksPayURL(fullURL);
console.log("Decoded Custom Operation Parameters:", decodedParams);
// output:
//{
// "operation": "custom-action",
// "recipient": "SP2RTE7F21N6GQ6BBZR7JGGRWAT0T5Q3Z9ZHB9KRS",
// "memo": "Custom memo for special action",
// "data": "Some custom data related to the action"
//}This section demonstrates how wallet developers can handle decoded payment requests from Stacks Pay URLs.
import { decodeStacksPayURL } from "stacks-pay";
// 1. User scans QR code or clicks a Stacks Pay link
const paymentURL = "web+stx:stx1wajky2mnw3u8qcte8ghj76twwehkjcm98ahhqetjv96xjmmw845kuan0d93k2fnjv43kjurfv4h8g02n2qe9y4z9xarryv2wxer4zdjzgfd9yd62gar4y46p2sc9gd23xddrjkjgggu5k5jnye6x76m9dc74x4zcyesk6mm4de6r6vfsxqczver9wd3hy6tsw35k7m3a2pshjmt9de6zken0wg4hxetjwe5kxetnyejhsurfwfjhxst585erqv3595cnytfnx92ryve9xdqn2wf9xdqn2w26juk65n";
// 2. Decode the URL
try {
const paymentRequest = decodeStacksPayURL(paymentURL);
// 3. Display payment details to user
console.log("Operation:", paymentRequest.operation);
console.log("Recipient:", paymentRequest.recipient);
console.log("Amount:", paymentRequest.amount);
console.log("Token:", paymentRequest.token);
console.log("Description:", paymentRequest.description);
// 4. Handle based on operation type
switch (paymentRequest.operation) {
case "invoice":
// Show invoice payment UI with amount locked
await showInvoicePaymentUI(paymentRequest);
break;
case "support":
// Show support/donation UI (amount editable by user)
await showSupportUI(paymentRequest);
break;
case "mint":
// Show NFT minting UI
await showMintUI(paymentRequest);
break;
default:
// Handle custom operations
await showCustomOperationUI(paymentRequest);
}
} catch (error) {
console.error("Invalid payment URL:", error.message);
showErrorToUser("Unable to process this payment request");
}Check if a payment request has expired before processing:
function isPaymentExpired(paymentRequest: CommonParameters): boolean {
if (!paymentRequest.expiresAt) return false;
const expirationDate = new Date(paymentRequest.expiresAt);
return new Date() > expirationDate;
}
// Usage
if (isPaymentExpired(paymentRequest)) {
showErrorToUser("This payment request has expired");
return;
}Always validate payment parameters before executing transactions:
import { validateStacksAddress } from "@stacks/transactions";
function validatePaymentRequest(request: CommonParameters): boolean {
// Validate recipient address
if (request.recipient && !validateStacksAddress(request.recipient)) {
throw new Error("Invalid recipient address");
}
// Validate amount for numeric operations
if (request.amount) {
const amount = parseInt(request.amount);
if (isNaN(amount) || amount <= 0) {
throw new Error("Invalid amount");
}
}
// Validate contract address for mint operations
if (request.operation === "mint" && request.contractAddress) {
if (!validateStacksAddress(request.contractAddress)) {
throw new Error("Invalid contract address");
}
}
return true;
}Description:
Encodes the provided payment parameters into a Stacks Pay URL using Bech32m encoding.
Parameters:
params(CommonParameters): An object containing the payment parameters. Must include theoperationfield and any other required fields based on the operation type.
Returns:
string: The encoded Stacks Pay URL.
Throws:
- Error if required parameters are missing or invalid.
Example:
const encodedURL = encodeStacksPayURL(invoiceParams);Description:
Decodes a Stacks Pay URL back into its constituent payment parameters.
Parameters:
fullURL(string): The complete Stacks Pay URL to decode.
Returns:
CommonParameters: An object containing the decoded payment parameters.
Throws:
- Error if the URL has an invalid protocol or encoding, or if required parameters are missing/invalid.
Example:
const decodedParams = decodeStacksPayURL(fullURL);Defines the structure of payment parameters used in encoding and decoding.
export interface CommonParameters {
operation: OperationType;
recipient?: string;
token?: string;
amount?: string;
description?: string;
memo?: string;
expiresAt?: string;
invoiceNumber?: string;
dueDate?: string;
contractAddress?: string;
contractName?: string;
functionName?: string;
[key: string]: string | undefined; // Allows additional custom parameters
}Fields:
operation(OperationType): The type of operation (support,invoice,mint, or custom).recipient(string, optional): The recipient's Stacks address.token(string, optional): The token type (e.g.,STX).amount(string, optional): The amount to be transferred.description(string, optional): A description of the payment.memo(string, optional): An optional memo for the payment.expiresAt(string, optional): Expiration timestamp in ISO format.invoiceNumber(string, optional): Invoice number for tracking.dueDate(string, optional): Due date for the invoice.contractAddress(string, optional): Address of the smart contract (formintoperations).contractName(string, optional): Name of the smart contract (formintoperations).functionName(string, optional): Function name in the smart contract (formintoperations).[key: string]: string | undefined;
Allows inclusion of additional custom parameters for custom operations.
Ensure that all functionalities work as expected by running the test suite.
npm testThe package includes basic unit tests covering:
- Encoding and decoding of standard operations (
support,invoice,mint). - Handling of custom operations with arbitrary parameters.
- Validation of required and optional parameters.
- Error handling for invalid inputs and malformed URLs.
Contributions are welcome! Whether it's improving the documentation, fixing bugs, or adding new features, your input is valuable.
-
Fork the Repository
Click the "Fork" button at the top right of the repository page to create your own copy.
-
Clone Your Fork
git clone https://github.com/your-username/stacks-pay-js.git cd stacks-pay-js -
Create a New Branch
git checkout -b feature/YourFeatureName
-
Make Changes
Implement your changes, ensuring that the code adheres to the existing style and passes all tests.
-
Commit Your Changes
git commit -m "Add feature: YourFeatureName" -
Push to Your Fork
git push origin feature/YourFeatureName
-
Open a Pull Request
Navigate to the original repository and click "Compare & pull request" to submit your changes for review.
- Code Quality: Ensure your code is clean, well-documented, and follows TypeScript best practices.
- Testing: Add tests for new features or bug fixes to maintain high test coverage.
- Documentation: Update the README and other documentation as necessary to reflect your changes.
This project is licensed under the MIT license.
For any questions, suggestions, or support, please reach out:
- GitHub Issues: stacks-pay-js/issues
Access the source code and contribute on GitHub:
- GitHub Repository: https://github.com/dantrevino/stacks-pay-js
- Stacks Pay SIP: Stacks Pay SIP on GitHub
- Bech32m Specification: BIP-350
- Stacks Documentation: Stacks Docs