Sell for a seller

Add one header and your key works in the seller's space. Their checkout, their payment, your cut.

Send a seller's userUuid in the On-Behalf-Of header and the request reads and writes their products, checkouts and payments, as if they made it. The SDKs make it a client of its own:

  1. Your server → QBitFlow API: Creates a checkout, On-Behalf-Of: seller
  2. QBitFlow API → Your server: The seller's checkout
  3. Customer wallet → Payment contract, on-chain: Pays
  4. Payment contract → Seller's wallet, on-chain: Direct: the seller's share
  5. Payment contract → Your wallet, on-chain: Direct: your commission
  6. Payment contract → Your wallet, on-chain: Held: all of it, your commission withheld
  7. QBitFlow API → Your server: payment.completed
Blue mark: money, on-chain. The rest moves no funds.
Act in a member's space

client-on-behalf-of.sh

# An organization key acting in a member's space: the request reads the member's products, in the# key's mode. Without the header, the same key reads the organization's own.curl https://api.qbitflow.app/v2/products \  -H "X-API-Key: $QBITFLOW_API_KEY" \  -H "On-Behalf-Of: $MEMBER_UUID"# 200: a page of Product ({ items, hasMore, nextCursor? })
// An organization key acting in a member's space: every request of seller sends// On-Behalf-Of. It shares client's configuration.const seller = client.onBehalfOf(memberUuid);const products = await seller.products.list();console.log(`${products.items.length} products on the first page of the seller's space`);
# Every request of this client sends On-Behalf-Of: it acts in the member's space.seller = client.on_behalf_of(member_uuid)  # shares the connections and settings of clientproducts = seller.products.list()print(len(products.items), "products on the first page of the seller's space")
// A client acting in the member's space (organization key): every request sends// On-Behalf-Of. It shares the connections and configuration of client.seller := client.OnBehalfOf(memberUUID)products, err := seller.Products.List(ctx, nil)if err != nil {	log.Fatal(err)}fmt.Println(len(products.Items), "products on the first page of the seller's space")
// An organization key acts in a member's space: every request sends On-Behalf-Of.$seller = $client->onBehalfOf($memberUuid); // shares $client's transport and configurationecho count($seller->products->list()->items), " products on the first page of the member's space\n";

Create a checkout as the seller

Exactly as in the sell guide, on the seller's client (seller above, or -H "On-Behalf-Of: $MEMBER_UUID" in cURL):

Create a checkout (with the seller's client)

checkout-create-payment.sh

# A one-time checkout for an inline product. QBitFlow replaces {{UUID}} with the session's id on# redirect. The same Idempotency-Key on a retry never opens a second checkout.curl https://api.qbitflow.app/v2/checkout-sessions/payment \  -H "X-API-Key: $QBITFLOW_API_KEY" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{    "productName": "T-shirt",    "description": "Blue, size M",    "priceUsd": "4.99",    "reference": "order-1042",    "successUrl": "https://shop.example.com/orders/success?uuid={{UUID}}",    "cancelUrl": "https://shop.example.com/orders/cancel"  }'# 201: a CreatedCheckoutSession ({ "link": "https://…/checkout?uuid=pay@…", "uuid": "pay@…", "expiresAt": "…" })# Redirect the customer to "link".
// An inline product: nothing needs to exist in your catalog. QBitFlow replaces {{UUID}} with the// session's id on the redirect.const session = await client.checkoutSessions.createPayment({	productName: 'T-shirt',	description: 'Blue, size M',	priceUsd: '4.99', // USD, a decimal string, never a float	reference: 'order-1042', // your order id: unique per space	successUrl: `https://shop.example.com/orders/success?uuid=${Placeholders.UUID}`,	cancelUrl: 'https://shop.example.com/orders/cancel',}); // Redirect the customer to the hosted checkout page.console.log(`Redirect the customer to ${session.link}`);
session = client.checkout_sessions.create_payment(    product_name="T-shirt",    description="Blue, size M",    price_usd="4.99",  # USD, a decimal string: never a float    reference="order-1042",  # your order id: unique per space    # QBitFlow replaces the placeholder with the session's id when it redirects the customer.    success_url=f"https://shop.example.com/orders/success?uuid={qbitflow.PLACEHOLDER_UUID}",    cancel_url="https://shop.example.com/orders/cancel",)# Redirect the customer to the hosted checkout page.print("Send the customer to", session.link)
// An inline product: no product needed in the catalog. QBitFlow replaces {{UUID}}// (qbitflow.PlaceholderUUID) with the session's id on redirect.session, err := client.CheckoutSessions.CreatePayment(ctx, &qbitflow.CreatePaymentSessionParams{	ProductName: "T-shirt",	Description: "Blue, size M",	PriceUSD:    "4.99",       // a decimal string, never a float	Reference:   "order-1042", // your order id: unique per space	SuccessURL:  "https://shop.example.com/orders/success?uuid=" + qbitflow.PlaceholderUUID,	CancelURL:   "https://shop.example.com/orders/cancel",})if err != nil {	log.Fatal(err)}// In an HTTP handler: http.Redirect(w, r, session.Link, http.StatusSeeOther)fmt.Println("Redirect the customer to", session.Link)
$session = $client->checkoutSessions->createPayment(new \QBitFlow\Params\CreatePaymentSessionParams(	productName: 'T-shirt',	description: 'Blue, size M',	priceUsd: '4.99', // a decimal string, never a float	reference: 'order-1042', // your order id: unique per space	successUrl: 'https://shop.example.com/orders/success?uuid=' . \QBitFlow\Placeholders::UUID,	cancelUrl: 'https://shop.example.com/orders/cancel',)); header('Location: ' . $session->link); // send the customer to the hosted checkout

Paid directly, the money goes to the seller's wallet, your commission split off. Held, it all goes to yours, your commission withheld.

Watch out

When a seller's checkout is refused

409 merchant_not_ready says why in details.reason: noWallet (a seller paid directly has no wallet yet; only they can add one, from their own dashboard), organizationNoWallet (a held seller, while your own wallets accept nothing), fundsHeld (a subscription for a held seller: trust them first) or noTokenWallet (a subscription, while their wallets accept only native coins). Removing your wallet on a chain also removes the seller's currencies there: an open checkout meets 409 currency_not_accepted.

What On-Behalf-Of can and can't do

  • Every route that acts in a space (products, customers, checkouts, payments, subscriptions, webhook endpoints), refunds as reads only, in your key's mode, with the seller's role (user): /members and /invitations answer 403, so call those without the header. A userUuid that isn't your seller in that mode answers 404.
  • Every seller at once: without the header, includeMembers=true on the payment and subscription routes, on the lists and the single reads, reads every seller's, each naming its userUuid (read only: acting on one still takes the header).
  • Policies bind sellers, not you. Your key acting for a seller isn't bound by your organization's policies. A seller's own dashboard or key is: what a policy turns off (members.products, members.subscriptions, members.refunds, members.apiKeys, members.webhooks), reads included, answers 403 policy_disabled with details.policy, before any lookup. members.checkoutRedirects refuses a checkout's successUrl or cancelUrl, named in details.field. Your checkout.customerPaysProcessingFee is theirs too: a seller's checkout sending another fees.processingFee answers 403 processing_fee_set_by_organization.

Check it

me on the seller's client answers user, the seller in onBehalfOfUserUuid. Pay their test checkout: the payment is in the seller's space, its settlement.split showing your commission as organization.