Sandbox on Solana devnet. No real funds move here. Use sk_test_ keys and a wallet set to devnet. Get devnet SOL from the Solana faucet and devnet USDC from the Circle faucet.
Register a business account, create crypto payment intents, confirm Solana payments, and verify signed callbacks. Keep API keys server-side and collect Telegram chat ids only after customers add @solknife_sandbox_bot.
What Gateway does
Add a crypto payment gateway to your app on the Solana blockchain. Accept USDC, USDT, SOL, or another token of the SPL Token program (Token-2022 mints are not supported), send customers Telegram updates through @solknife_sandbox_bot, and let your backend receive signed payment callbacks.
You receive the full payment
Your business wallet receives the full customer payment in the token you choose. SolKnife takes a separate fixed SOL fee.
Verified on Solana
Gateway checks the Solana payment details before marking an order as paid, so your app does not rely on browser claims.
Simple SaaS onboarding
Start with USDC, USDT, SOL, or another token of the SPL Token program. Token-2022 mints are not supported. Connect your wallet, callback URL, and Telegram chat id.
Telegram notifications
Customers add @solknife_sandbox_bot and share a chat id, then receive payment status updates in Telegram.
App-ready callbacks
Signed callbacks let your backend update orders, subscriptions, or credits after Gateway confirms payment.
Payment flow
Create checkout. Customer pays in crypto. Your app gets the result. Your backend creates the intent after collecting the customer's Telegram chat id. The customer pays with a Solana wallet. Gateway verifies the payment before sending a signed callback to your system and a bot notification to the customer.
Register your business wallet, accepted payment token, callback URL, and Telegram chat id.
Store the API key and callback secret on your backend. The developer console takes the key in the browser for testing only; never ship it in your app's client code.
Ask the customer to add @solknife_sandbox_bot, then create an intent with the chat id they provide.
Send the customer through a Solana payment that includes your business payment and SolKnife's fixed SOL fee.
Gateway finds the payment by its reference, verifies it on Solana, and confirms it. Verify the signed callback before updating app records.
Get started
Register business accountRegister business account
POST /api/merchants/register
{
"displayName": "Acme SaaS",
"ownerWallet": "business payout wallet",
"indexTokenMint": "accepted token address",
"callbackUrl": "https://app.example/callback",
"telegramChatId": "123456789"
}
Limited to 5 registrations per hour per client IP; over the limit the API returns 429 REGISTRATION_RATE_LIMITED.
One-off payments
Create hosted checkoutCreate hosted checkout
POST /api/payments
x-gateway-key: sk_live_...
{
"checkoutReference": "order-1001",
"customerTelegramChatId": "123456789",
"merchantAmountRaw": "250000000",
"merchantTokenMint": "accepted token address"
}
Response:
{
"merchantAmountRaw": "250000000",
"gatewayFeeLamports": "1200000",
"checkoutUrl": "https://sandbox.gate.solknife.xyz/checkout/..."
}
Send the customer to checkoutUrl. Gateway watches Solana for the transaction that carries the payment reference, verifies it at finalized commitment, marks the payment CONFIRMED, and sends the signed payment.confirmed callback. No confirm call is needed.
Checkout statusCheckout status
GET /api/checkout/{paymentId}?reference=...
Public checkout status for the hosted checkout page. Needs no API key; the reference from checkoutUrl is the access key. status moves from PENDING to CONFIRMED once Gateway finds and verifies the payment, usually within 30 seconds of the customer sending it.
Confirm paymentConfirm payment
POST /api/payments/{paymentId}/confirm
x-gateway-key: sk_live_...
{
"signature": "solana transaction signature"
}
Gateway confirms hosted checkout payments on its own, so this call is only needed when your app sends the customer's transaction itself and wants the result right away. Verifies the transaction at finalized commitment, about 13 seconds after it is sent. Before that the request fails with PAYMENT_NOT_FOUND_ONCHAIN; retry it.
Callbacks and reporting
Callback verificationCallback verification
x-solknife-event: payment.confirmed | subscription_plan.confirmed | subscription_plan.updated_onchain | subscription.subscribe | subscription.collect | subscription.cancel | subscription.resume | subscription.past_due | subscription.expired | gateway.test
x-solknife-delivery-id: 5f0c2a8e-3b1d-4c6f-9a7e-2d4b8c1e6f30
x-solknife-timestamp: 1800000000
x-solknife-signature: hex(hmac_sha256(webhook_secret, timestamp + "." + raw_body))
{
"eventType": "payment.confirmed",
"customerTelegramChatId": "123456789",
"merchantAmountRaw": "250000000",
"gatewayFeeLamports": "1200000",
"gatewayFeeChargedLamports": "1200000",
"status": "CONFIRMED"
}
Subscription callbacks use eventType subscription_plan.confirmed, subscription_plan.updated_onchain, subscription.subscribe, subscription.collect, subscription.cancel, subscription.resume, subscription.past_due, or subscription.expired and include the customer reference, plan id, billing period, status, configured gateway fee, and gateway fee charged for that action.
Verify every callback before you act on it:
1. Compute the hex HMAC-SHA256 of "<x-solknife-timestamp>.<raw body>" with your webhook secret and compare it to x-solknife-signature in constant time.
2. Reject the callback when x-solknife-timestamp is more than 5 minutes (300 seconds) away from your clock.
3. Store each x-solknife-delivery-id you accept for at least 10 minutes and drop any callback whose delivery id you already processed. A manual retry from the dashboard is a new delivery with a new delivery id, so also make your handler idempotent on the payment or subscription id.
Node.js verifier:
import { createHmac, timingSafeEqual } from "node:crypto";
const TOLERANCE_SECONDS = 300;
export const verifySolknifeCallback = (secret, rawBody, headers, nowSeconds = Math.floor(Date.now() / 1000)) => {
const timestamp = headers["x-solknife-timestamp"] ?? "";
const signature = headers["x-solknife-signature"] ?? "";
if (!/^\d+$/.test(timestamp)) return false;
if (Math.abs(nowSeconds - Number(timestamp)) > TOLERANCE_SECONDS) return false;
const expected = Buffer.from(createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex"), "hex");
const received = Buffer.from(signature, "hex");
return received.length === expected.length && timingSafeEqual(received, expected);
};
GET /api/analytics/summary
x-gateway-key: sk_live_...
Returns payment counts, confirmed gateway fee lamports including subscription collect fees, callback health, and indexed token count.
Callback delivery historyCallback delivery history
GET /api/webhooks
x-gateway-key: sk_live_...
Returns the latest callback attempts with event type, status, HTTP code, and target URL.
Retry callback deliveryRetry callback delivery
POST /api/webhooks/{deliveryId}/retry
x-gateway-key: sk_live_...
Creates a fresh callback delivery attempt for payment.confirmed and stored subscription callback events.
Retries and test callbacks share a limit of 10 per minute per merchant; over the limit the API returns 429 WEBHOOK_RATE_LIMITED.
Publish subscription plan transactionPublish subscription plan transaction
POST /api/subscription-plans/{planId}/transactions/create-plan
x-gateway-key: sk_live_...
{
"account": "business payout wallet"
}
Returns an unsigned base64 transaction for the business wallet to publish the Solana subscription plan.
Confirm subscription planConfirm subscription plan
POST /api/subscription-plans/{planId}/confirm
x-gateway-key: sk_live_...
{
"signature": "solana transaction signature"
}
Verifies the create-plan transaction, marks the plan ACTIVE, and sends a signed subscription_plan.confirmed callback. Gateway reads the transaction at finalized commitment, about 13 seconds after it is sent; before that the request fails with a not-found error and should be retried.
Update on-chain plan transactionUpdate on-chain plan transaction
POST /api/subscription-plans/{planId}/transactions/update-plan
x-gateway-key: sk_live_...
{
"account": "business payout wallet",
"onchainStatus": "sunset",
"endTs": "1782000000"
}
Returns an unsigned base64 transaction for the business wallet to restore an on-chain plan or sunset it from new subscribers. Use "active" without endTs to restore the on-chain plan.
Use Gateway pause, resume, and archive endpoints to control whether the SaaS app offers the plan to new customers.
Confirm on-chain plan updateConfirm on-chain plan update
POST /api/subscription-plans/{planId}/confirm
x-gateway-key: sk_live_...
{
"action": "update-plan",
"signature": "solana transaction signature",
"onchainStatus": "sunset",
"endTs": "1782000000"
}
Verifies the update-plan transaction, records the signature, and sends a signed subscription_plan.updated_onchain callback. Use active without endTs to confirm an on-chain restore. Gateway reads the transaction at finalized commitment, about 13 seconds after it is sent; before that the request fails with a not-found error and should be retried.
The Gateway plan status is still controlled by pause, resume, and archive endpoints.
Pause subscription planPause subscription plan
POST /api/subscription-plans/{planId}/pause
x-gateway-key: sk_live_...
Pauses an ACTIVE subscription plan. New customer subscriptions and collection workers should stop using a paused plan until it is resumed. Existing customer records remain stored in Gateway.
Resume subscription planResume subscription plan
POST /api/subscription-plans/{planId}/resume
x-gateway-key: sk_live_...
Resumes a paused subscription plan so new customer subscriptions and due collection workers can use it again.
Archive subscription planArchive subscription plan
POST /api/subscription-plans/{planId}/archive
x-gateway-key: sk_live_...
Archives a subscription plan when the offer should no longer be sold. Archived plans stay available in records and reports, but new customer subscriptions and collection workers should stop using them.
POST /api/subscriptions
x-gateway-key: sk_live_...
{
"planId": "subscription plan id",
"customerReference": "cus_1001",
"customerTelegramChatId": "123456789",
"subscriberWallet": "customer Solana wallet"
}
Requires an ACTIVE subscription plan. Publish and confirm the plan before opening customer subscriptions.
customerReference is idempotent per plan, so the same SaaS customer can hold separate subscriptions for different plans or add-ons.
The customer wallet is required because Gateway derives the on-chain subscription account before activation.
POST /api/subscription-checkout/{subscriptionId}/transactions/init-authority?reference=cus_1001
{
"account": "customer Solana wallet"
}
Hosted checkout route. Returns an unsigned base64 wallet setup transaction without requiring the merchant Gateway API key.
POST /api/subscription-checkout/{subscriptionId}/transactions/subscribe?reference=cus_1001
{
"account": "customer Solana wallet",
"expectedSubscriptionAuthorityInitId": "1"
}
Hosted checkout route. Returns an unsigned base64 subscribe transaction scoped to the customer reference on the checkout URL.
POST /api/subscription-checkout/{subscriptionId}/transactions/cancel?reference=cus_1001
{
"account": "customer Solana wallet"
}
Hosted checkout route. Returns an unsigned base64 cancel transaction for the customer wallet without requiring the merchant Gateway API key.
POST /api/subscription-checkout/{subscriptionId}/transactions/resume?reference=cus_1001
{
"account": "customer Solana wallet"
}
Hosted checkout route. Returns an unsigned base64 resume transaction for a canceled subscription before the current billing period ends.
POST /api/subscription-checkout/{subscriptionId}/confirm?reference=cus_1001
{
"action": "subscribe | cancel | resume",
"signature": "solana transaction signature"
}
Verifies the customer subscribe, cancel, or resume signature, updates the subscription, sends the signed callback, and notifies the customer chat in Telegram. Gateway reads the transaction at finalized commitment, about 13 seconds after it is sent; before that the request fails with a not-found error and should be retried.
POST /api/subscriptions/{subscriptionId}/transactions/init-authority
x-gateway-key: sk_live_...
{
"account": "customer Solana wallet"
}
Returns an unsigned base64 transaction that prepares the customer's wallet for recurring Solana payments.
POST /api/subscriptions/{subscriptionId}/transactions/subscribe
x-gateway-key: sk_live_...
{
"account": "customer Solana wallet",
"expectedSubscriptionAuthorityInitId": "1"
}
Returns an unsigned base64 transaction for the customer wallet to subscribe to the plan.
expectedSubscriptionAuthorityInitId is optional. Leave it out to let Gateway read the customer's SubscriptionAuthority from Solana.
POST /api/subscriptions/{subscriptionId}/transactions/cancel
x-gateway-key: sk_live_...
{
"account": "customer Solana wallet"
}
Returns an unsigned base64 transaction that lets the customer cancel the subscription on Solana.
Customers can still cancel subscriptions on paused or archived plans, so businesses can retire offers without trapping existing customers.
POST /api/subscriptions/{subscriptionId}/transactions/resume
x-gateway-key: sk_live_...
{
"account": "customer Solana wallet"
}
Returns an unsigned base64 transaction that lets the customer resume a canceled subscription on Solana before the current period ends. Gateway keeps the existing billing window instead of restarting billing.
POST /api/subscriptions/{subscriptionId}/confirm
x-gateway-key: sk_live_...
{
"action": "subscribe | collect | cancel | resume",
"signature": "solana transaction signature"
}
Verifies the matching subscription transaction, updates the customer subscription status or next billing date, sends a signed subscription callback, and notifies the customer chat in Telegram. Gateway reads the transaction at finalized commitment, about 13 seconds after it is sent; before that the request fails with a not-found error and should be retried.
POST /api/subscriptions/{subscriptionId}/expire
x-gateway-key: sk_live_...
Expires a canceled customer subscription after the current period ends. This sends a subscription.expired callback and Telegram notification, but does not build a Solana transaction or charge a gateway fee. Retries for an already EXPIRED subscription return the stored subscription without sending another subscription.expired callback or Telegram message.
Collecting subscription payments
List due subscriptionsList due subscriptions
GET /api/subscriptions/due
x-gateway-key: sk_live_...
Returns active or past due customer subscriptions whose active plan is ready for collection. Use each id to build a collect transaction from your backend worker.
Collection worker queueCollection worker queue
GET /api/subscriptions/due/queue
x-gateway-key: sk_live_...
Returns due collection worker items with subscription, plan, pullerWallet, collectTransactionPath, confirmPath, and pastDuePath. For each item, build the collect transaction with the returned pullerWallet, send it on Solana, then confirm the signature through confirmPath. Use pastDuePath when your worker cannot collect after the billing date.
POST /api/subscriptions/{subscriptionId}/transactions/collect
x-gateway-key: sk_live_...
{
"account": "subscription puller wallet"
}
Returns an unsigned base64 transaction that collects the next due subscription payment: the merchant receives the full subscription token amount, and the plan's puller wallet pays SolKnife 1,200,000 lamports. The signer must be the plan's puller wallet, which is the merchant wallet by default. Gateway confirms a collect only when it carries that fee.
Past due customer subscriptions use the same collect action. After the collect signature is confirmed, Gateway marks the customer subscription ACTIVE again and advances the next billing date.
Mark subscription past dueMark subscription past due
POST /api/subscriptions/{subscriptionId}/past-due
x-gateway-key: sk_live_...
Marks an active customer subscription PAST_DUE after the next billing date when your backend worker could not collect the payment. Use this only for subscriptions on active plans. This sends a subscription.past_due callback and Telegram notification, but does not build a Solana transaction or charge a gateway fee. Retries for an already PAST_DUE subscription return the stored subscription without sending another callback or Telegram message.
Subscription status
Gateway now stores Solana subscription plans, customer subscription records, and an unsigned transaction flow for publishing plans, preparing a customer wallet, subscribing, collecting each due payment, canceling, resuming, and updating on-chain plan status. Confirm the signed Solana customer transactions through Gateway so the plan status, customer subscription status, and next billing date stay in sync with the blockchain.
Each collect that Gateway confirms carries the fixed 0.0012 SOL gateway fee. The puller wallet that signs the collect pays it, and the puller is your business wallet by default. The customer pays only the subscription amount.
Test in the sandbox
The sandbox at https://sandbox.gate.solknife.xyz runs the same Gateway on Solana devnet. No real funds move there. Sandbox accounts are separate from production accounts, so register your business there first.
Register on the sandbox. Its API keys start with sk_test_. The sandbox rejects sk_live_ keys, and production rejects sk_test_ keys.
Set the paying wallet to devnet. Get devnet SOL from the Solana faucet and devnet USDC from the Circle faucet.
Create payments and subscription plans as you would on production. Checkouts, confirmations, and signed callbacks work the same way.
To go live, register again on https://gate.solknife.xyz, then change the base URL and the API key in your backend. Your sk_live_ key and its callback secret replace the sandbox ones.