Payment Sessions vs Payment Links: Which to Use When

Published Sep 2, 2026 · 5 min read

Solpaygate gives you two ways to hand a customer a checkout URL: short-lived payment sessions created from your backend, or persistent payment links created in the dashboard. They look similar from the customer's side — both open the hosted /pay page — but they behave differently for reconciliation, security, and integration effort. Here's how to choose.

Payment sessions in one line

A payment session is a short-lived, backend-created checkout token: your server calls POST /api/company/{companyId}/payment-session and gets back a 30-minute URL you hand to one specific customer for one specific order.

Payment links in one line

A payment link is a long-lived, shareable URL created in the dashboard for a fixed product or amount: anyone with the link can pay it, and each payment is tracked separately in your dashboard.

The difference matters. Sessions are ephemeral and personalized; links are durable and public.

When sessions win

Sessions fit anywhere your backend already knows who is checking out:

Because each session is one-shot and scoped to an orderId, reconciliation is trivial: every webhook you receive maps to exactly one order in your database. Sessions also carry an optional returnUrl, so after payment the customer's browser is redirected back to your success page automatically.

Security-wise, a session URL is worthless once the customer has paid or the 30 minutes are up. If it leaks, the damage window is small.

When links win

Links fit anywhere the payer isn't known ahead of time or the same URL is reused many times:

Because a link is public, you can't rely on the payer having a known user record. You get the payment, the amount, the currency, and whatever metadata the customer entered (email, note) at checkout. Reconciliation is by paymentId rather than orderId — every payment is a new row, and you match it to a customer by whatever contact detail they gave you at the /pay page.

A hybrid pattern

Some flows want both. A common pattern for a mixed audience of logged-in and anonymous buyers:

  1. Publish a payment link for a fixed-price product on your marketing pages.
  2. For logged-in customers, generate a session on the fly instead — so the payment ties to their user record automatically.
  3. For anonymous visitors, fall back to the link and rely on the /pay page to capture their email.

The backend logic:

function checkoutUrl(user, product) {
  if (user) {
    // Session — tied to the user, expires in 30 min
    return createSession({
      userId: user.id,
      orderId: `${user.id}:${product.id}:${Date.now()}`,
      description: product.name,
      returnUrl: `${SITE}/thanks`,
    });
  }
  // Public link — anyone can pay
  return product.paymentLinkUrl;
}

The customer sees one button either way; your reconciliation script handles both cases and normalizes them into your own payments table.

If your flow is closer to an e-commerce cart, sessions almost always win — the wiring is walked through step-by-step in our developer guide to the payment API. If instead you're building a top-up flow where users add credit to an account balance, the pattern is a slight variation again; see building a crypto wallet top-up flow for the full write-up.

Ready to accept crypto payments?

Solpaygate lets your business accept SOL, USDT, and USDC on Solana with a single API call. Non-custodial, no smart contract to deploy.

Start for free