Build in-app transaction
https://api.triport.io/v1/billing/invoices/inv_8f2a1c9d4e6b40f1/inapp/buildBuild the unsigned Solana transaction that settles an in-app invoice, ready for the customer's wallet to sign and submit.
Builds the unsigned Solana transaction that pays a crypto-inapp invoice
from the customer's own wallet. You supply the wallet's public key as the
payer; the backend assembles a transfer to the invoice's deposit address (a
SystemProgram::Transfer for sol-native, or an SPL Token::TransferChecked
for sol-spl-usdc / sol-spl-usdt, creating the recipient token account in the
same transaction when needed), attaches the invoice reference key so the payment
can be matched on-chain, and returns it base64-encoded together with the recent
blockhash it was built against.
This endpoint only builds the transaction — it does not sign or send it. The
returned transaction_b64 must be signed client-side by the customer's wallet
and then handed to POST /v1/billing/invoices/{id}/inapp/submit. The invoice
itself is not marked paid here or at submit time; a background watcher detects
the on-chain transaction and settles the invoice (track it over the SSE stream
at GET /v1/billing/invoices/{id}/events).
The transaction is built against a recent blockhash that expires quickly.
blockhash_valid_until tells you when to stop trusting it — if the customer has
not signed and submitted by then, call this endpoint again to build a fresh
transaction rather than submitting a stale one.
This endpoint is Solana in-app only. The invoice must have been created with
channel: "crypto-inapp" and a sol-* rail (sol-native, sol-spl-usdc, or
sol-spl-usdt); other channels or rails are rejected. It also requires the
in-app handler to be enabled in the server configuration — when it is not, the
endpoint returns 503 (see Notes).
Parameters
Path parameters
idstringrequiredPOST /v1/billing/invoices. Must belong to the signed-in user and be a pending, in-app, Solana invoice.Request bodyobjectpayerstringrequiredResponse
200 OK:
transaction_b64stringblockhashstringblockhash_valid_untilstring (RFC 3339, UTC)Errors
Errors use the shared {"error": "<message>"} envelope.
| Code | Meaning | When it happens |
|---|---|---|
inapp not configured | 503 | The in-app handler is not enabled on this deployment. |
auth required | 401 | No valid session cookie on the request. |
bad json: … | 400 | The request body is not valid JSON. |
payer required | 400 | payer was empty or missing. |
bad payer pubkey: … | 400 | payer is not a valid base58 public key. |
payer is zero pubkey | 400 | payer decodes to the all-zero pubkey. |
invoice not found | 404 | No such invoice, or it is not owned by the signed-in user. |
invoice_not_inapp | 400 | The invoice was not created on the crypto-inapp channel. |
invoice_not_pending: <status> | 409 | The invoice is no longer pending (e.g. already paid, cancelled, expired). |
invoice_expired | 410 | The invoice's expiry time has passed. |
rail_not_solana_inapp | 400 | The invoice's rail is not one of sol-native, sol-spl-usdc, sol-spl-usdt. |
payer_equals_recipient | 400 | payer is the same account as the invoice's deposit address. |
build_failed: … | 502 | The transaction could not be built (e.g. the upstream RPC call for the recent blockhash failed). |
See Errors for the shared error envelope and conventions.
Examples
JavaScript (fetch)
const res = await fetch(
`https://api.triport.io/v1/billing/invoices/${invoiceId}/inapp/build`,
{
method: "POST",
credentials: "include", // send the console session cookie
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ payer: wallet.publicKey.toBase58() }),
},
);
if (!res.ok) {
const { error } = await res.json();
throw new Error(error);
}
const { transaction_b64, blockhash_valid_until } = await res.json();
// Decode, sign with the customer's wallet, then POST to .../inapp/submit
// before `blockhash_valid_until`.TypeScript SDK (@triport/sdk)
import { Triport } from "@triport/sdk";
// Console client: authenticates with the browser session cookie.
const client = new Triport({ session: true });
const built = await client.billing.buildInappTx(invoiceId, {
payer: wallet.publicKey.toBase58(),
});
console.log(built.transactionB64, built.blockhashValidUntil);Python (triport-sdk)
import os
from triport import Triport
# Console client authenticates with the session cookie.
client = Triport(session_cookie=os.environ["TRIPORT_SESSION"])
built = client.billing.build_inapp_tx(
invoice_id="inv_8f2a1c9d4e6b40f1",
payer="5sQ2sJ3oU9d2W6kq3qQy3rQ4Yb8pY2v9bN1cR7uA3kZx",
)
print(built["transaction_b64"], built["blockhash_valid_until"])Notes
- In-app, Solana only. This endpoint is the first step of the
crypto-inappflow and only handlessol-*rails. Create the invoice withchannel: "crypto-inapp"and a Solana rail first — see Create an invoice. For on-chain (crypto-onchain) invoices the customer pays the deposit address directly and no transaction is built here. - Build → sign → submit. The full sequence is: build here → wallet signs the
returned
transaction_b64→POST /v1/billing/invoices/{id}/inapp/submitwith the signature (or the signed transaction). Submit is a best-effort UX relay; it does not settle the invoice. - Respect
blockhash_valid_until. A Solana blockhash is short-lived. If the customer takes too long to sign, the transaction will fail to land — rebuild to get a fresh blockhash rather than submitting a stale one. - Settlement is asynchronous. The invoice flips to
paidonly after a background watcher observes the on-chain transfer. Subscribe toGET /v1/billing/invoices/{id}/events(SSE) forinvoice_paid/invoice_progressupdates. - Handler not enabled. A
503(inapp not configured) means this deployment has the in-app handler turned off in server config; the on-chain channel and pay-from-balance flows are unaffected.