Accept AI-driven payments.
Pay By AI is a thin orchestration layer over Stripe Connect. Your card handling, PCI scope, and payouts all live on Stripe. We broker the agent handshake: the buyer's AI pays a token you issue, we charge the user's stored card, and Stripe settles directly into your Connect account. You fulfil the order on a webhook.
Sign up and subscribe
Go to /sign-up?role=merchant. Create your Clerk account, then start the 15-day free trial on /merchant/subscribe. After that, head to /merchant/onboarding, enter your business name, and click Continue. This creates:
- a Stripe Connect Express account owned by you — your payouts settle here;
- a Pay By AI merchant record bound to that Stripe account plus your Clerk user ID, in the current mode (test or live — flip the header toggle first if you need the other);
- a merchant API key you'll use to mint payment tokens.
Once created, every merchant you own appears on your dashboard— rotate the API key, copy the webhook signing key, find plugin setup guides, all in one place.
Save your API key
Immediately after step 1 you'll land on a page showing the key exactly once. Copy it somewhere safe — your password manager, a secrets vault, your plugin's settings store. It looks like:
pbai_mk_test_a1b2c3d4e5f6... # test mode
pbai_mk_live_z9y8x7w6v5u4... # live modeThen click Continue to Stripe and finish the Stripe Connect forms. Your API key keeps working even before Stripe verification is done — but you won't receive payouts until Stripe has approved your account.
Mint a payment token
When your order system is ready for payment, call Pay By AI to mint a token.
curl -X POST https://www.paybymyai.com/api/payment_requests \
-H "Authorization: Bearer pbai_mk_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"amountCents": 1500,
"description": "Large pepperoni pizza",
"merchantMetadata": {
"order_id": "WOO-42",
"cart": ["item-1", "item-2"]
},
"expiresInSeconds": 900
}'Response:
{
"id": "pr_kXf7z8...",
"merchantId": "cmabc123",
"mode": "test",
"amountCents": 1500,
"currency": "usd",
"description": "Large pepperoni pizza",
"status": "pending",
"merchantMetadata": { "order_id": "WOO-42", "cart": ["item-1", "item-2"] },
"expiresAt": "2026-04-19T00:15:00.000Z",
"createdAt": "2026-04-19T00:00:00.000Z"
}The id is your token. Hand it to whoever will pay — the user's AI, a checkout page, a QR code. Anyone with this string can pay the request but they can't modify it.
Wait for the payment
The buyer's AI agent calls Pay By AI's MCP with your token + the user's stored payment method. Pay By AI:
- verifies the agent's session and consent bounds;
- creates a Stripe PaymentIntent with
transfer_data.destination= your Stripe account ID; - on success, fires
payment_intent.succeededand dispatches a webhook to your configured URL.
Handle the webhook
Point webhookUrl at your endpoint. On payment_request.paid, we POST JSON to that URL with two headers:
X-PayByAI-Signature: t=<unix_seconds>,v1=<hmac_sha256_hex>
X-PayByAI-Event-Id: evt_<uuid>The HMAC is HMAC-SHA256("{timestamp}.{raw_body}", secret). Verify it before trusting the payload.
Node.js (Express) — drop-in verify
import crypto from "node:crypto";
import express from "express";
const app = express();
function verify(rawBody, signatureHeader, secret) {
if (!signatureHeader) return { ok: false, reason: "missing_signature" };
const parts = Object.fromEntries(
signatureHeader.split(",").map((p) => {
const i = p.indexOf("=");
return i < 0 ? ["", ""] : [p.slice(0, i).trim(), p.slice(i + 1).trim()];
}),
);
const t = parseInt(parts.t, 10);
if (!Number.isFinite(t) || !parts.v1) return { ok: false, reason: "malformed" };
if (Math.abs(Math.floor(Date.now() / 1000) - t) > 300)
return { ok: false, reason: "stale" };
const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.${rawBody}`)
.digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(parts.v1, "hex");
if (a.length !== b.length) return { ok: false, reason: "bad_signature" };
return { ok: crypto.timingSafeEqual(a, b) };
}
app.post(
"/webhooks/paybyai",
express.raw({ type: "application/json" }),
async (req, res) => {
const rawBody = req.body.toString("utf8");
const result = verify(
rawBody,
req.headers["x-paybyai-signature"],
process.env.PAYBYAI_WEBHOOK_SECRET,
);
if (!result.ok) return res.status(400).json({ error: result.reason });
const event = JSON.parse(rawBody);
if (event.type === "payment_request.paid") {
const orderId = event.data.merchantMetadata?.order_id;
await markOrderPaid(orderId, event.data.paymentRequestId);
}
res.json({ received: true });
},
);PHP (WordPress / WooCommerce)
add_action('rest_api_init', function () {
register_rest_route('paybyai/v1', '/webhook', [
'methods' => 'POST',
'callback' => 'paybyai_handle_webhook',
'permission_callback' => '__return_true',
]);
});
function paybyai_handle_webhook(WP_REST_Request $req) {
$raw = $req->get_body();
$sig = $req->get_header('x-paybyai-signature');
$secret = get_option('paybyai_webhook_secret');
if (!$sig) return new WP_Error('missing', 'no signature', ['status' => 400]);
parse_str(str_replace(',', '&', $sig), $parts);
$t = intval($parts['t'] ?? 0);
if (abs(time() - $t) > 300) {
return new WP_Error('stale', 'stale timestamp', ['status' => 400]);
}
$expected = hash_hmac('sha256', $t . '.' . $raw, $secret);
if (!hash_equals($expected, $parts['v1'] ?? '')) {
return new WP_Error('bad_sig', 'invalid signature', ['status' => 400]);
}
$event = json_decode($raw, true);
if ($event['type'] === 'payment_request.paid') {
$order_id = $event['data']['merchantMetadata']['order_id'] ?? null;
if ($order_id) {
$order = wc_get_order($order_id);
$order->payment_complete($event['data']['stripePaymentIntentId']);
}
}
return ['received' => true];
}API reference
Mint a token. Body: amountCents, description, currency?, merchantMetadata?, expiresInSeconds?. Returns the payment request object with id as the token.
Read-only preview. Anyone with the token can call this — the token IS the credential. Use it to show the amount and merchant name before your user pays.
Cancel a pending token. Merchants can only cancel their own tokens — we verify ownership.
Body: name, returnUrl, refreshUrl. Returns a Stripe onboarding link + the API key. Primarily used by the dashboard; plugins can use it to skip the web form.
Plugin integrations (roadmap)
Official plugins coming for WooCommerce, Shopify, Wix, and Squarespace. Each installs a per-store MCP server that exposes products to AI agents + wires checkout straight through Pay By AI — no code on your end.
Test mode
All new merchants start in test mode by default. The header toggle flips modes; a test-mode key cannot touch live data and vice versa. Use Stripe's 4242 4242 4242 4242 card for test payments.
