TriportRPC

Build in-app transaction

POSThttps://api.triport.io/v1/billing/invoices/inv_8f2a1c9d4e6b40f1/inapp/build

Build the unsigned Solana transaction that settles an in-app invoice, ready for the customer's wallet to sign and submit.

Solana

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

idstringrequired
Invoice id, as returned by POST /v1/billing/invoices. Must belong to the signed-in user and be a pending, in-app, Solana invoice.
Request bodyobject
payerstringrequired
The customer wallet's public key, base58-encoded. Becomes the fee payer and source account of the transfer. Must be a valid, non-zero pubkey and must differ from the invoice's deposit address.

Response

200 OK:

transaction_b64string
The base64-encoded unsigned Solana transaction. Decode, have the customer's wallet sign it, then send it to the submit endpoint.
blockhashstring
The recent blockhash the transaction was built against.
blockhash_valid_untilstring (RFC 3339, UTC)
Conservative expiry for the transaction. After this time the blockhash may be too old to land; build a new transaction instead of submitting.

Errors

Errors use the shared {"error": "<message>"} envelope.

CodeMeaningWhen it happens
inapp not configured503The in-app handler is not enabled on this deployment.
auth required401No valid session cookie on the request.
bad json: …400The request body is not valid JSON.
payer required400payer was empty or missing.
bad payer pubkey: …400payer is not a valid base58 public key.
payer is zero pubkey400payer decodes to the all-zero pubkey.
invoice not found404No such invoice, or it is not owned by the signed-in user.
invoice_not_inapp400The invoice was not created on the crypto-inapp channel.
invoice_not_pending: <status>409The invoice is no longer pending (e.g. already paid, cancelled, expired).
invoice_expired410The invoice's expiry time has passed.
rail_not_solana_inapp400The invoice's rail is not one of sol-native, sol-spl-usdc, sol-spl-usdt.
payer_equals_recipient400payer is the same account as the invoice's deposit address.
build_failed: …502The 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-inapp flow and only handles sol-* rails. Create the invoice with channel: "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_b64POST /v1/billing/invoices/{id}/inapp/submit with 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 paid only after a background watcher observes the on-chain transfer. Subscribe to GET /v1/billing/invoices/{id}/events (SSE) for invoice_paid / invoice_progress updates.
  • 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.