What integration means
Integrating payments into Semantic Kernel means two related things: giving the kernel a plugin so it can pay for what it calls (a paid API, a data source, another agent), and, when you want it, charging other callers for what your kernel serves. The first is the common case; the second is the earn side, turning a kernel into a paid service.
This page is the integration reference: the pieces, how they fit, and the one fact about Semantic Kernel's runtimes that shapes the whole setup. For the same overview in sibling frameworks, see crewai-payment-integration and autogen-payment-integration. The payment API product page is the broader reference. Here the focus is the shape of a Semantic Kernel integration, expressed as a plugin the kernel can call.
Every runtime uses the proxy
Here is the fact that shapes everything: Semantic Kernel runs on .NET, Python, and Java, and the payment packages, @blockchain0x/x402 and @blockchain0x/node, ship for Node. None of Semantic Kernel's runtimes is Node, so unlike some frameworks there is no in-process path; every Semantic Kernel runtime pays through a small local Node proxy.
That is not a drawback so much as a clean boundary. The proxy holds the wallet and does the real x402 work in about thirty lines, and your kernel function, in whatever language, just calls it over localhost. It keeps every payment identifier on the verified Node surface and means the integration looks the same whether you build in C#, Python, or Java. On the earn side the same holds: a Node gateway fronts your kernel's endpoint regardless of the kernel's language.
A payment plugin
In Semantic Kernel, capabilities are plugins made of kernel functions, so the wallet is a payment plugin whose function calls the proxy. Here is the Python form.
import requests
from semantic_kernel.functions import kernel_function
class PaymentPlugin:
@kernel_function(description="Fetch a URL, paying in USDC if it requires payment. Returns the body.")
def pay_and_fetch(self, url: str) -> str:
res = requests.post("http://127.0.0.1:8787", json={"url": url}, timeout=30)
return res.text if res.status_code == 200 else f"Failed: {res.status_code}"Register it with kernel.add_plugin(PaymentPlugin(), "payment"), start the proxy, and the kernel can pay. In C# the same plugin is a class with a [KernelFunction]-attributed method that calls the proxy; the shape is identical, only the language differs. When the kernel's function-calling decides a task needs a paid resource, it invokes pay_and_fetch, the proxy settles the 402 in USDC on Base, and the result returns. The payment is invisible to the model, and the wallet's spend limit is the backstop.
The description drives the planner
Semantic Kernel chooses functions from their descriptions, so the description on the kernel function is the real interface. Write it for the planner: say plainly that the function fetches a URL and pays if the URL requires payment, and that it returns the body. That is usually enough for the kernel to pick it when a task needs a paid resource and to leave it alone otherwise.
Decide deliberately whether to mention cost. If you want the planner to weigh price, note that calls may cost a small amount in USDC; if you would rather it not hesitate over routine sub-cent calls, leave cost out and let the wallet's spend limit be the control. Either is valid, but be intentional, because a vague description leaves the kernel guessing about when the function applies, while a precise one tells it exactly. The function body is small; the description is where you shape behavior, and in Semantic Kernel that description is exactly what the kernel reads when it plans which function to call.
The earn side
To charge for what your kernel serves, expose it over HTTP and front it with a small Node gateway running createX402Plugin. The gateway returns the 402, verifies payment, and proxies paid requests to your kernel.
import Fastify from "fastify";
import { createClient } from "@blockchain0x/node";
import { createX402Plugin } from "@blockchain0x/x402/server/fastify";
const sdk = createClient({ apiKey: process.env.B0X_API_KEY! });
const app = Fastify();
await app.register(createX402Plugin, {
sdk,
defaultNetwork: "testnet",
pricing: { "POST /kernel/run": { amountUsdc: "0.10", payToAddress: process.env.B0X_PAYTO_ADDRESS!, paymentRequestId: "pr_kernel" } },
});
app.post("/kernel/run", async (req) => {
const r = await fetch("http://127.0.0.1:8000/run", { method: "POST", body: JSON.stringify(req.body) });
return await r.json();
});
await app.listen({ port: 8080 });An unpaid call gets a 402; a paid one runs your kernel. The kernel's code does not change; the gateway is the paywall. Keep a free discovery route so callers can see what the kernel does before paying.
One proxy, many languages
A practical benefit of the proxy boundary is worth calling out: the same proxy serves a C#, Python, or Java kernel without change, because all of them just post a URL to it. If your team runs a .NET service and a Python prototype against the same wallet behavior, they share one proxy implementation and one configuration, and the only per-language code is the thin kernel function that calls localhost.
That keeps the payment logic in one audited place rather than reimplemented per runtime. When you move from prototype to production, or from one language to another, the wallet, the identifiers, and the settlement path stay identical; only the plugin wrapper changes. For a framework that deliberately spans .NET, Python, and Java, having payment live in a single language-neutral proxy fits the way Semantic Kernel teams actually work.
Compatibility
The integration works across Semantic Kernel's surfaces because it lives in a plugin (pay) or a gateway (earn), not in the framework internals.
| Semantic Kernel surface | Works? | Notes |
|---|---|---|
Python @kernel_function |
Yes | Calls the local Node proxy |
C# [KernelFunction] |
Yes | Same shape, calls the proxy |
| Java kernel function | Yes | Same shape, calls the proxy |
| Function-calling / planners | Yes | The kernel picks the payment function by description |
| Earning from a kernel endpoint | Yes | Front it with the Node gateway |
When this fits
The integration fits when a Semantic Kernel app needs to pay for what it calls, when you want to charge for what it serves, or both, and you are comfortable running a small Node proxy alongside your runtime. That covers most kernels that touch money: one paying for data, one selling a result, or both at once. Because the proxy is language-agnostic, it fits a .NET shop and a Python shop equally.
It fits less well when the kernel never touches paid resources or when a single human-approved payment is the only money movement. For per-call machine payments at agent scale, the payment plugin plus proxy, with an optional earn gateway, is the integration that fits Semantic Kernel's plugin model and its multi-language reality.
Pricing
Integration is free; you build it from open packages. What you pay is the wallet platform fee per agent on the pricing page: Free is $0 per agent per month at a 5% transaction fee, Pro is $9 at 2%, Business is $29 at 1%. Per-agent pricing means each agent that transacts, paying or earning, is billed on its own wallet, so you pay for the agents that actually move money.
What to ship today
Write a PaymentPlugin with a pay_and_fetch kernel function (C#, Python, or Java), register it on the kernel, start a proxy with a sk_test_ key, and make one paid call on Base Sepolia. To earn, front your kernel with the Node gateway and confirm an unpaid call gets a 402. For sibling-framework overviews see crewai-payment-integration and autogen-payment-integration. Pricing is on the pricing page.