Menu

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.

Note: The purchase flow is the foundation of the ApiCharge monetization model. After completing a purchase, clients receive an access token that they can use with the Authentication Flow.

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.

GET /apicharge/quote

Optionally, clients can request quotes for a specific route by providing a routeId:

GET /apicharge/quote?routeId=premium-api

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.

200 OK
{
  "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.

Note: Quotes have a limited validity period, typically 15 minutes. If a quote expires, clients must request a new one before continuing the purchase flow.

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.

POST /apicharge/nanosubscription/PurchaseInstruction
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.

200 OK
{
  "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:

  1. Sign the provided transaction using the client's private key
  2. Submit the signed transaction to the Stellar network
  3. 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);
  });
Important: Never share or expose your Stellar secret key. Keep it secure at all times.

7Complete the Purchase

After the blockchain transaction is confirmed, the client completes the purchase by providing proof of payment to the ApiCharge server.

POST /apicharge/nanosubscription/Purchase
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:

  1. The payment was completed successfully
  2. The client is the legitimate owner of the account that made the payment
  3. 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.

200 OK
{
  "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.

Note: The access token is bound to the client who purchased it and cannot be transferred to other users. Store the token securely according to your platform's best practices.

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

400 Bad Request
{
  "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

400 Bad Request
{
  "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

402 Payment Required
{
  "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

403 Forbidden
{
  "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:

Client Implementation Guidelines

Key Client Requirements

To implement the purchase flow, clients need these capabilities:

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

Security Warning: Never store Stellar secret keys in client-side code, local storage, or insecure locations. Consider using a secure key management service for production applications.

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

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);
  });
Note: For production deployments, consider using a dedicated Stellar account for ApiCharge purchases and implementing proper key management practices.

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:

  1. The signature on the Stellar transaction (payment proof)
  2. 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: