Purchase Flow Overview
The ApiCharge purchase flow enables clients to buy time-limited access to API resources through ApiCharge Subscriptions. This process links blockchain-based micropayments directly to API access rights, creating a seamless pay-per-use experience.
Purchase Flow Diagram
The full purchase process involves several steps between the client and the ApiCharge server:
+----------+ +-------------+ +-----------------+
| | | | | |
| Client | | ApiCharge | | Stellar Network |
| | | | | |
+----+-----+ +------+------+ +--------+--------+
| | |
| 1. Request Available Quotes | |
| -------------------------------------> | |
| | |
| 2. Return Signed Quotes | |
| <------------------------------------- | |
| | |
| 3. Request Purchase Instructions | |
| -------------------------------------> | |
| | |
| 4. Return Purchase Instructions | |
| <------------------------------------- | |
| | |
| | |
| 5. Execute Blockchain Transaction | |
| ----------------------------------------------------------------------> |
| | |
| 6. Transaction Confirmation | |
| <---------------------------------------------------------------------- |
| | |
| 7. Complete Purchase with Proof | |
| -------------------------------------> | |
| | |
| 8. Return Access Token | |
| <------------------------------------- | |
| | |
+----------------------------------------+--------------------------------------+
Detailed Purchase Steps
1Request Available Quotes
The purchase flow begins with the client requesting quotes for available services.
Optionally, clients can request quotes for a specific route by providing a routeId:
This request doesn't require authentication and returns all available access tiers for the requested routes.
2Receive Quotes
The server responds with available quotes, each containing information about the service, price, and access parameters.
{
"quotes": [
{
"signingPubkey": "GATCBZKXPNCVBKY5KLXSWLNUAXMUPARGW5JBSP75XYU6P3AK4JUMNXQ5",
"signature": "3rvQJW9BF4LLMrqxP6J9F6Vx8GVLUxdBf6Qd9eK95H2RQ3mS97nSQypGWGQatrP7LnDnbsMVYZ2Fj6DBLaQkBSUB",
"signableEntity": {
"routeId": "basic-api",
"duration": 3600,
"microUnitPrice": 100000,
"validUntilUnixTimeStamp": 1716387600,
"rateLimiters": [
{
"$type": "CallCount",
"Count": 100
}
]
}
},
{
"signingPubkey": "GATCBZKXPNCVBKY5KLXSWLNUAXMUPARGW5JBSP75XYU6P3AK4JUMNXQ5",
"signature": "2wQsSr8pTjb5DtBHFmRZg3yCMkMnzipGFQS39RzGZ9drWTaAuWxQWbAMgnKg5ZN9NCpv2LiT8ULfL7p6k8GhjCxE",
"signableEntity": {
"routeId": "premium-api",
"duration": 86400,
"microUnitPrice": 1000000,
"validUntilUnixTimeStamp": 1716387600,
"rateLimiters": [
{
"$type": "CallCount",
"Count": 1000
},
{
"$type": "DataLimitDownload",
"Bytes": 104857600
}
]
}
}
]
}
Each quote includes:
- routeId - The unique identifier for the API route
- duration - The ApiCharge Subscription duration in seconds
- microUnitPrice - The price in micro units (USDC, EURC, or XLM)
- validUntilUnixTimeStamp - The quote's expiration timestamp
- rateLimiters - The quality of service parameters for this tier
- signature - The server's cryptographic signature of the quote
- signingPubkey - The public key used to sign the quote
Quotes are digitally signed to prevent tampering and ensure quote authenticity. Clients should verify the quote signature using the provided signing public key before proceeding.
3Select Quote and Request Purchase Instructions
After receiving quotes, the client selects one based on their needs and requests purchase instructions by providing their Stellar public key and the chosen quote.
Content-Type: application/json
{
"clientPublicKey": "GBXDGHXCQVZUDDD4NNGBOGCXYQFKNBPIMJWW7GMUCQ2OJCPO7B3GIXWA",
"routeQuote": {
"signingPubkey": "GATCBZKXPNCVBKY5KLXSWLNUAXMUPARGW5JBSP75XYU6P3AK4JUMNXQ5",
"signature": "3rvQJW9BF4LLMrqxP6J9F6Vx8GVLUxdBf6Qd9eK95H2RQ3mS97nSQypGWGQatrP7LnDnbsMVYZ2Fj6DBLaQkBSUB",
"signableEntity": {
"routeId": "basic-api",
"duration": 3600,
"microUnitPrice": 100000,
"validUntilUnixTimeStamp": 1716387600,
"rateLimiters": [
{
"$type": "CallCount",
"Count": 100
}
]
}
},
"requestedNanosubscriptionUTCStartTime": 1716384000
}
The request includes:
- clientPublicKey - The Stellar public key of the client making the purchase (will be verified as the payer)
- routeQuote - The complete quote object selected from the previous step
- requestedNanosubscriptionUTCStartTime - Optional timestamp for when the subscription should start (defaults to immediate)
The client public key is critical as it establishes ownership of the ApiCharge Subscription and links the payment to the client's identity.
4Receive Purchase Instructions
The server responds with instructions for completing the blockchain transaction, including the transaction data and signatures needed.
{
"stellarTransaction": "AAAAAgAAAABNNGSN3OSOhP0xjMiQZXWsx5UqPNA0HJ071owK37HV2QAAAMgB+J7YAAAADQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAE00ZI3c5I6E/TGMyJBldazHlSo80DQcnTvWjArfsdXZAAAAAQAAAADj6WRQ09P1UyQXPO8YCzxRLwkMjwbUKKM92MkMDZd6dgAAAAFYTE0AAAAAACPgJf9suHF4y5YjZ+rDFzwkz7BKF0ZJw3nUGVVrjOJvAAAAF0h26AAAAAAAAAAAAdXZAAAAQMYvC0fdiHH38J5Gm/eWhAoUs3P+TO1qWXbr84HFJ9M20z9v28R5kDYAmapXgKiCm1JxFFAX6D+LCcJJBKzIXA0=",
"authorisationToSign": "e7QeBQ4r2Nb7mQ+JD8HkNe0oUDCieI2Za5nUkCjyfcvRQyVMt94jLbSomgm6VuZX5b+TG/BQybrv7QUPd4YGgdNbTxwjDfEswkQcgrNnCnlugVu7bnMXvALKzLzWHm5ow0Y/S51E2IpW6VV1k7+iQeNBc/DRDgJkBcF0/tFPkXQ=",
"transactionSignature": "q5JlXQtRRs9QhgcMQX2+5UYvzl0V0GUHXdjXy8JCc/7weBhIpHT7LqNzcHV2XF78Yp1jxPZSUXL9SqB67tCUCA=="
}
This response contains:
- stellarTransaction - The encoded Stellar transaction that transfers funds to the API provider
- authorisationToSign - A server-provided challenge that the client must sign to complete the purchase
- transactionSignature - The server's signature on the transaction (partial)
The purchase instructions enable the client to execute the payment transaction while providing cryptographic proof of intent for the specific subscription purchase.
5-6Execute Blockchain Transaction
The client takes the Stellar transaction provided by the server and executes it on the Stellar network.
This is typically done through these steps:
- Sign the provided transaction using the client's private key
- Submit the signed transaction to the Stellar network
- Wait for transaction confirmation (typically a few seconds)
The transaction execution can be performed using the Stellar SDK for your platform:
// JavaScript example using stellar-sdk
const StellarSdk = require('stellar-sdk');
const server = new StellarSdk.Server('https://horizon.stellar.org');
// The client's keypair (safeguard the secret key!)
const clientKeyPair = StellarSdk.Keypair.fromSecret('S...');
// Decode and sign the transaction
const transaction = StellarSdk.TransactionBuilder.fromXDR(
purchaseInstructions.stellarTransaction,
'PUBLIC'
);
// Add the client's signature
transaction.sign(clientKeyPair);
// Submit the transaction
server.submitTransaction(transaction)
.then(result => {
console.log('Payment successful:', result);
// Proceed to complete the purchase
})
.catch(error => {
console.error('Payment failed:', error);
});
7Complete the Purchase
After the blockchain transaction is confirmed, the client completes the purchase by providing proof of payment to the ApiCharge server.
Content-Type: application/json
{
"stellarTransaction": "AAAAAgAAAABNNGSN3OSOhP0xjMiQZXWsx5UqPNA0HJ071owK37HV2QAAAMgB+J7YAAAADQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAE00ZI3c5I6E/TGMyJBldazHlSo80DQcnTvWjArfsdXZAAAAAQAAAADj6WRQ09P1UyQXPO8YCzxRLwkMjwbUKKM92MkMDZd6dgAAAAFYTE0AAAAAACPgJf9suHF4y5YjZ+rDFzwkz7BKF0ZJw3nUGVVrjOJvAAAAF0h26AAAAAAAAAAAAdXZAAAAQMYvC0fdiHH38J5Gm/eWhAoUs3P+TO1qWXbr84HFJ9M20z9v28R5kDYAmapXgKiCm1JxFFAX6D+LCcJJBKzIXA0=",
"authorisationToSign": "e7QeBQ4r2Nb7mQ+JD8HkNe0oUDCieI2Za5nUkCjyfcvRQyVMt94jLbSomgm6VuZX5b+TG/BQybrv7QUPd4YGgdNbTxwjDfEswkQcgrNnCnlugVu7bnMXvALKzLzWHm5ow0Y/S51E2IpW6VV1k7+iQeNBc/DRDgJkBcF0/tFPkXQ=",
"transactionSignature": "q5JlXQtRRs9QhgcMQX2+5UYvzl0V0GUHXdjXy8JCc/7weBhIpHT7LqNzcHV2XF78Yp1jxPZSUXL9SqB67tCUCA==",
"authorisationSignature": "aFqTi2Dyw6zIUv6FLMnLGjhMswZH9PYJNJH7TZM9uB6rTKFHX1R+zPxjkPQ87+fKfKNsHJ7gC2MrYtgAB1j5Dg=="
}
This request includes:
- stellarTransaction - The transaction data from the purchase instructions
- authorisationToSign - The challenge from the purchase instructions
- transactionSignature - The server's signature from the purchase instructions
- authorisationSignature - The client's signature of the authorisation challenge
The server verifies the payment on the blockchain and the client's signature to ensure that:
- The payment was completed successfully
- The client is the legitimate owner of the account that made the payment
- The payment matches the quote that was previously provided
8Receive Access Token
Upon successful verification, the server issues an access token that grants the client access to the purchased route with the specified quality of service parameters.
{
"signableEntity": {
"signingPubkey": "GATCBZKXPNCVBKY5KLXSWLNUAXMUPARGW5JBSP75XYU6P3AK4JUMNXQ5",
"signature": "J8KpLzrDCF7Y4kqt9DLZLsCNapQ7SiwsCXf6EPfCxmD2kZEhf9m2LjNPFSiYpRj2Y9TUKJSgNr9ivLnJdjwwMRDk",
"signableEntity": {
"routeId": "basic-api",
"createdUnixTimeStamp": 1716384000,
"expirationUnixTimeStamp": 1716387600,
"nonce": 7283947621,
"accountId": "GBXDGHXCQVZUDDD4NNGBOGCXYQFKNBPIMJWW7GMUCQ2OJCPO7B3GIXWA",
"rateLimiters": [
{
"$type": "CallCount",
"Count": 100
}
]
}
}
}
The access token includes:
- routeId - The route this token grants access to
- createdUnixTimeStamp - When the token was issued
- expirationUnixTimeStamp - When the token expires
- nonce - A unique identifier to prevent replay attacks
- accountId - The client's public key (binding the token to the client)
- rateLimiters - The quality of service parameters
- signature - The server's cryptographic signature of the token
This token is what the client will use to authenticate API requests according to the Authentication Flow.
Error Handling
During the purchase flow, various errors can occur. ApiCharge returns standardized error responses to help clients handle these situations:
400 Bad Request - Invalid Quote
{
"type": "https://apicharge.com/errors/invalid-quote",
"title": "Invalid Quote",
"status": 400,
"detail": "The provided quote has an invalid signature or has been tampered with.",
"instance": "/apicharge/errors/12350"
}
400 Bad Request - Expired Quote
{
"type": "https://apicharge.com/errors/expired-quote",
"title": "Expired Quote",
"status": 400,
"detail": "The provided quote has expired. Please request a new quote.",
"instance": "/apicharge/errors/12351"
}
402 Payment Required - Payment Verification Failed
{
"type": "https://apicharge.com/errors/payment-verification-failed",
"title": "Payment Verification Failed",
"status": 402,
"detail": "The payment could not be verified on the blockchain. Ensure the transaction was submitted and confirmed.",
"instance": "/apicharge/errors/12352"
}
403 Forbidden - Invalid Authorisation
{
"type": "https://apicharge.com/errors/invalid-authorisation",
"title": "Invalid Authorisation",
"status": 403,
"detail": "The provided authorisation signature is invalid. The signature must be created by the owner of the public key.",
"instance": "/apicharge/errors/12353"
}
Clients should handle these errors gracefully, possibly by:
- Requesting a new quote if the current one has expired
- Verifying blockchain transaction status if payment verification fails
- Checking client signature generation if authorisation errors occur
- Displaying appropriate user feedback based on the error type
Client Implementation Guidelines
Key Client Requirements
To implement the purchase flow, clients need these capabilities:
- Stellar Integration - The ability to sign and submit Stellar transactions
- ED25519 Cryptography - Support for the ED25519 signature scheme
- HTTP Client - The ability to make HTTP requests to the ApiCharge server
- Access Token Storage - Secure storage for the received access token
Client SDK Example
ApiCharge provides client SDKs for various platforms to simplify the purchase flow implementation:
Stellar Transaction Handling
// TypeScript example using ApiCharge client SDK
import { ApiChargeClient, StellarAccount } from 'apicharge-client';
// Initialize the client
const client = new ApiChargeClient({
apiUrl: 'https://api.example.com/apicharge',
stellarNetwork: 'testnet' // or 'public' for mainnet
});
// Client's Stellar account (secure your private key!)
const account = new StellarAccount({
publicKey: 'GBXDGHXCQVZUDDD4NNGBOGCXYQFKNBPIMJWW7GMUCQ2OJCPO7B3GIXWA',
secretKey: 'S...' // Your private key
});
async function purchaseNanosubscription() {
try {
// 1. Get available quotes
const quotes = await client.getQuotes('premium-api');
// 2. Select a quote (e.g., the first one)
const selectedQuote = quotes[0];
// 3. Get purchase instructions
const purchaseInstructions = await client.getPurchaseInstructions({
quote: selectedQuote,
account: account.publicKey
});
// 4. Execute the payment
const paymentResult = await client.executePayment(
purchaseInstructions,
account
);
// 5. Complete the purchase
const accessToken = await client.completePurchase(
purchaseInstructions,
paymentResult
);
// 6. Store the access token for later use
client.storeAccessToken(accessToken);
return accessToken;
} catch (error) {
console.error('Purchase failed:', error);
throw error;
}
}
Purchase Best Practices
- Automate Renewals - Set up automatic renewal before tokens expire to ensure uninterrupted service
- Batch Purchases - For predictable usage, consider buying longer-duration ApiCharge Subscriptions
- Error Recovery - Implement robust error handling with retries for network issues
- Secure Key Storage - Store Stellar keys securely according to platform best practices
- Token Caching - Maintain a local cache of valid access tokens to minimize purchases
Stellar Account Setup
Before using the purchase flow, clients need a Stellar account with funds. Here's how to set one up:
1. Create a Stellar Account
// Using stellar-sdk to generate a keypair
const StellarSdk = require('stellar-sdk');
const keypair = StellarSdk.Keypair.random();
console.log('Public Key:', keypair.publicKey());
console.log('Secret Key:', keypair.secret());
// Important: Securely save both keys!
2. Fund the Account
- Testnet: Use the Stellar Laboratory to fund testnet accounts
- Mainnet: Purchase XLM from a cryptocurrency exchange and transfer to your account
3. Verify Account Activation
// Check if the account exists on the Stellar network
const server = new StellarSdk.Server('https://horizon-testnet.stellar.org');
server.loadAccount(keypair.publicKey())
.then(account => {
console.log('Account active with balance:', account.balances[0].balance);
})
.catch(error => {
console.error('Account not found or not activated:', error);
});
Transaction Security
The ApiCharge purchase flow incorporates several security features:
Quote Integrity
Quotes are cryptographically signed by the server to prevent tampering. Clients should verify the signature before proceeding with a purchase.
Double-Signature Verification
The purchase flow requires two signatures from the client:
- The signature on the Stellar transaction (payment proof)
- The signature on the authorisation challenge (identity proof)
This dual-signature approach ensures that the client is both the owner of the Stellar account and intends to purchase the specific ApiCharge Subscription.
Blockchain Verification
All payments are verified on the Stellar blockchain, providing an immutable record of the transaction.
Time-Limited Validity
Both quotes and access tokens have explicit time-based validity periods, reducing the window for potential attacks.
Next Steps
Now that you understand the purchase flow, explore:
- Authentication Flow - How to use access tokens to authenticate API requests
- Rate Limiters - Understanding the quality of service parameters
- Global Settings - How to configure the purchase flow parameters