SOPSellers' Open Protocol

連接預約服務

代理用MCP或REST API搜尋商家、查空檔,並建立與管理預約。

已上線預約與查詢功能已上線。預約時的線上付款見替客人付款;第一家商家(一位私人教練)已於2026-10-22啟用線上付款。

這頁是給串接的工程師看的。商家不需要讀這頁;想了解商家端怎麼運作,請看接受代理預約。

運作方式

SOP的預約連接器提供兩種介面:MCP伺服器與REST API,兩者共用同一套預約核心,行為一致。代理的典型流程是:搜尋商家、讀商家資料與價格、查空檔、建立預約;需要線上付款時再付款,之後可以查詢、改期或取消。

代理透過SOP預約並付款
客人代理SOP商家請代理找服務搜尋商家與空檔回傳服務、價格與空檔選定時段request_booking先佔名額:awaiting_payment、金額、期限、payment_handlers在錢包裡核准確切金額帶付款憑證呼叫complete_booking只扣一次款;商家婉拒或沒確認就全額退款通知新預約新客人的預約由商家確認;逾時未確認就釋出並退款回傳預約狀態(已成立或待確認)與付款結果告知已成立,或等待商家確認
步驟
  1. 客人 → 代理:請代理找服務
  2. 代理 → SOP:搜尋商家與空檔
  3. SOP → 代理:回傳服務、價格與空檔
  4. 客人 → 代理:選定時段
  5. 代理 → SOP:request_booking
  6. SOP → 代理:先佔名額:awaiting_payment、金額、期限、payment_handlers
  7. 客人 → 代理:在錢包裡核准確切金額
  8. 代理 → SOP:帶付款憑證呼叫complete_booking
  9. SOP → SOP:只扣一次款;商家婉拒或沒確認就全額退款
  10. SOP → 商家:通知新預約
  11. 商家 → SOP:新客人的預約由商家確認;逾時未確認就釋出並退款
  12. SOP → 代理:回傳預約狀態(已成立或待確認)與付款結果
  13. 代理 → 客人:告知已成立,或等待商家確認

MCP伺服器

端點是https://mcp.srct.tw/mcp,使用Streamable HTTP,無狀態,回應為JSON。連線後先呼叫initialize,再用tools/list取得工具清單。

開啟MCP連線:initialize

curl

curl -sS -X POST https://mcp.srct.tw/mcp \
  -H "User-Agent: my-agent/1.0" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"my-agent","version":"1.0"}}}'

Python

import json
import urllib.error
import urllib.request

BASE = "https://mcp.srct.tw"


def call(method, path, body=None, headers=None):
    req = urllib.request.Request(
        BASE + path,
        data=None if body is None else json.dumps(body).encode(),
        method=method,
        headers={"User-Agent": "my-agent/1.0", "Content-Type": "application/json", **(headers or {})},
    )
    try:
        with urllib.request.urlopen(req) as res:
            return res.status, json.loads(res.read())
    except urllib.error.HTTPError as err:  # 4xx and 5xx responses still carry a JSON body
        return err.code, json.loads(err.read())


def show(status, body):
    print("HTTP", status)
    print(json.dumps(body, ensure_ascii=False, indent=2))


MCP = {"Accept": "application/json, text/event-stream"}  # the server requires both types

show(*call("POST", "/mcp", {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "my-agent", "version": "1.0"}},
}, MCP))

Node.js

const BASE = "https://mcp.srct.tw";

async function call(method, path, body, headers = {}) {
  const res = await fetch(BASE + path, {
    method,
    headers: { "User-Agent": "my-agent/1.0", "Content-Type": "application/json", ...headers },
    body: body === undefined ? undefined : JSON.stringify(body),
  });
  return { status: res.status, body: await res.json() };
}

function show({ status, body }) {
  console.log("HTTP", status);
  console.log(JSON.stringify(body, null, 2));
}

const MCP = { Accept: "application/json, text/event-stream" }; // the server requires both types

(async () => {
  show(await call("POST", "/mcp", {
    jsonrpc: "2.0",
    id: 1,
    method: "initialize",
    params: { protocolVersion: "2025-06-18", capabilities: {}, clientInfo: { name: "my-agent", version: "1.0" } },
  }, MCP));
})();

回應範例

HTTP 200
{
  "result": {
    "protocolVersion": "2025-06-18",
    "capabilities": {
      "tools": {
        "listChanged": true
      }
    },
    "serverInfo": {
      "name": "…-booking",
      "title": "…",
      "version": "1.0.0",
      "websiteUrl": "https://mcp.srct.tw/docs"
    },
    "instructions": "… lists local service businesses (gyms, personal trainers, studios) in Taiwan that accept bookings through AI agents. Times are Asia/Taipei. Bookings are paid at the venue, unless the business takes online payment: then request_booking returns status awaiting_payment with amount, currency, expires_at and payment_handlers, and complete_booking takes a Stripe shared payment token (payments are collected by Seller Tech U.S.A. LLC through Stripe; unconfirmed paid bookings are refunded in full). A booking is either confirmed immediately or waits for the business to confirm; the status field says which. A pending booking is held only for the business's pending_hold time (10 minutes unless the business sets another); if not confirmed by then, it is released."
  },
  "jsonrpc": "2.0",
  "id": 1
}

已對正式服務實際執行(唯讀)

列出工具:tools/list

curl

curl -sS -X POST https://mcp.srct.tw/mcp \
  -H "User-Agent: my-agent/1.0" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

Python

import json
import urllib.error
import urllib.request

BASE = "https://mcp.srct.tw"


def call(method, path, body=None, headers=None):
    req = urllib.request.Request(
        BASE + path,
        data=None if body is None else json.dumps(body).encode(),
        method=method,
        headers={"User-Agent": "my-agent/1.0", "Content-Type": "application/json", **(headers or {})},
    )
    try:
        with urllib.request.urlopen(req) as res:
            return res.status, json.loads(res.read())
    except urllib.error.HTTPError as err:  # 4xx and 5xx responses still carry a JSON body
        return err.code, json.loads(err.read())


def show(status, body):
    print("HTTP", status)
    print(json.dumps(body, ensure_ascii=False, indent=2))


MCP = {"Accept": "application/json, text/event-stream"}  # the server requires both types

show(*call("POST", "/mcp", {"jsonrpc": "2.0", "id": 2, "method": "tools/list"}, MCP))

Node.js

const BASE = "https://mcp.srct.tw";

async function call(method, path, body, headers = {}) {
  const res = await fetch(BASE + path, {
    method,
    headers: { "User-Agent": "my-agent/1.0", "Content-Type": "application/json", ...headers },
    body: body === undefined ? undefined : JSON.stringify(body),
  });
  return { status: res.status, body: await res.json() };
}

function show({ status, body }) {
  console.log("HTTP", status);
  console.log(JSON.stringify(body, null, 2));
}

const MCP = { Accept: "application/json, text/event-stream" }; // the server requires both types

(async () => {
  show(await call("POST", "/mcp", { jsonrpc: "2.0", id: 2, method: "tools/list" }, MCP));
})();

回應範例

HTTP 200
{
  "result": {
    "tools": [
      {
        "name": "search_businesses",
        "title": "Search businesses",
        "description": "Search …'s listed businesses by keyword (business name, city or district, or type of class) and/or category. Returns up to 10 businesses with a few matching services. Read-only.",
        "inputSchema": {
          "$schema": "http://json-schema.org/draft-07/schema#",
          "type": "object",
          "properties": {
            "keyword": {
              "description": "Words to match, e.g. 'personal training' or 'Taipei'",
              "type": "string",
              "maxLength": 40
            },
            "category": {
              "description": "gym, coaching (personal trainers), sauna, or recovery",
              "type": "string",
              "enum": [
                "gym",
                "coaching",
                "sauna",
                "recovery"
              ]
            },
            "limit": {
              "description": "Maximum results, default 5",
              "type": "integer",
              "minimum": 1,
              "maximum": 10
            }
          }
        },
        "annotations": {
          "title": "Search businesses",
          "readOnlyHint": true,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": false
        },
        "execution": {
          "taskSupport": "forbidden"
        }
      },
      …
    ]
  },
  "jsonrpc": "2.0",
  "id": 2
}

已對正式服務實際執行(唯讀):為顯示而截短;…表示省略的部分。服務回傳9個工具:search_businesses、get_business、get_availability、request_booking、complete_booking、modify_booking、get_booking_status、cancel_booking、get_booking_link。這裡只顯示第一個。

目前共有9個工具:

工具用途唯讀
search_businesses依關鍵字或類別搜尋商家,最多回傳10筆。是
get_business取得商家資料、預約與取消規則,以及可預約的服務、價格與時長。是
get_availability查某項服務未來1至14天的空檔與剩餘名額。是
request_booking為客人建立一筆預約。否
complete_booking帶客人核准的付款憑證,完成需要線上付款的預約。否
modify_booking改預約的時段或人數。否
get_booking_status查預約狀態,含付款與退款資訊。是
cancel_booking取消預約;已付款的會退款,客人自己取消時扣除金流手續費與平台手續費。否
get_booking_link商家用自己的預約系統時,取得該系統的預約連結。否(會記錄一次點擊)

寫入類工具可帶idempotency_key,讓重試安全,見替客人付款。

search_businesses可依關鍵字(商家名稱、城市或行政區、課程類型)與類別搜尋。get_business回傳公開資料與可預約的服務。請直接使用這兩個工具回傳的business_id與service_id,不要自己組。

搜尋商家:search_businesses

curl

curl -sS -X POST https://mcp.srct.tw/mcp \
  -H "User-Agent: my-agent/1.0" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"search_businesses","arguments":{"keyword":"Taipei","limit":3}}}'

Python

import json
import urllib.error
import urllib.request

BASE = "https://mcp.srct.tw"


def call(method, path, body=None, headers=None):
    req = urllib.request.Request(
        BASE + path,
        data=None if body is None else json.dumps(body).encode(),
        method=method,
        headers={"User-Agent": "my-agent/1.0", "Content-Type": "application/json", **(headers or {})},
    )
    try:
        with urllib.request.urlopen(req) as res:
            return res.status, json.loads(res.read())
    except urllib.error.HTTPError as err:  # 4xx and 5xx responses still carry a JSON body
        return err.code, json.loads(err.read())


def show(status, body):
    print("HTTP", status)
    print(json.dumps(body, ensure_ascii=False, indent=2))


MCP = {"Accept": "application/json, text/event-stream"}  # the server requires both types

show(*call("POST", "/mcp", {"jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "search_businesses", "arguments": {"keyword": "Taipei", "limit": 3}}}, MCP))

Node.js

const BASE = "https://mcp.srct.tw";

async function call(method, path, body, headers = {}) {
  const res = await fetch(BASE + path, {
    method,
    headers: { "User-Agent": "my-agent/1.0", "Content-Type": "application/json", ...headers },
    body: body === undefined ? undefined : JSON.stringify(body),
  });
  return { status: res.status, body: await res.json() };
}

function show({ status, body }) {
  console.log("HTTP", status);
  console.log(JSON.stringify(body, null, 2));
}

const MCP = { Accept: "application/json, text/event-stream" }; // the server requires both types

(async () => {
  show(await call("POST", "/mcp", { jsonrpc: "2.0", id: 3, method: "tools/call", params: { name: "search_businesses", arguments: { keyword: "Taipei", limit: 3 } } }, MCP));
})();

回應範例

HTTP 200
{
  "result": {
    "content": [
      {
        "type": "text",
        "text": …
      }
    ],
    "structuredContent": {
      "ok": true,
      "count": 1,
      "results": [
        {
          "business_id": "coach-dunken",
          "name": "Dunken教練",
          "city": "台北市信義區(Taipei Xinyi)",
          "categories": [
            "coaching"
          ],
          "booking_mode": "request_here",
          "services": [
            {
              "service_id": "first_assessment",
              "name": "初次體能檢查(含InBody一次)",
              "price": "免費"
            },
            {
              "service_id": "pt",
              "name": "PT一對一教練課",
              "price": "NT$18,000/10堂"
            },
            {
              "service_id": "trx",
              "name": "TRX懸吊訓練",
              "price": "NT$18,000/10堂"
            }
          ]
        }
      ]
    },
    "isError": false
  },
  "jsonrpc": "2.0",
  "id": 3
}

已對正式服務實際執行(唯讀):為顯示而截短;…表示省略的部分。result.content[0].text(同一份JSON的字串版)已省略。

取得商家與服務:get_business

curl

curl -sS -X POST https://mcp.srct.tw/mcp \
  -H "User-Agent: my-agent/1.0" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"get_business","arguments":{"business_id":"coach-dunken"}}}'

Python

import json
import urllib.error
import urllib.request

BASE = "https://mcp.srct.tw"


def call(method, path, body=None, headers=None):
    req = urllib.request.Request(
        BASE + path,
        data=None if body is None else json.dumps(body).encode(),
        method=method,
        headers={"User-Agent": "my-agent/1.0", "Content-Type": "application/json", **(headers or {})},
    )
    try:
        with urllib.request.urlopen(req) as res:
            return res.status, json.loads(res.read())
    except urllib.error.HTTPError as err:  # 4xx and 5xx responses still carry a JSON body
        return err.code, json.loads(err.read())


def show(status, body):
    print("HTTP", status)
    print(json.dumps(body, ensure_ascii=False, indent=2))


MCP = {"Accept": "application/json, text/event-stream"}  # the server requires both types

show(*call("POST", "/mcp", {"jsonrpc": "2.0", "id": 4, "method": "tools/call", "params": {"name": "get_business", "arguments": {"business_id": "coach-dunken"}}}, MCP))

Node.js

const BASE = "https://mcp.srct.tw";

async function call(method, path, body, headers = {}) {
  const res = await fetch(BASE + path, {
    method,
    headers: { "User-Agent": "my-agent/1.0", "Content-Type": "application/json", ...headers },
    body: body === undefined ? undefined : JSON.stringify(body),
  });
  return { status: res.status, body: await res.json() };
}

function show({ status, body }) {
  console.log("HTTP", status);
  console.log(JSON.stringify(body, null, 2));
}

const MCP = { Accept: "application/json, text/event-stream" }; // the server requires both types

(async () => {
  show(await call("POST", "/mcp", { jsonrpc: "2.0", id: 4, method: "tools/call", params: { name: "get_business", arguments: { business_id: "coach-dunken" } } }, MCP));
})();

回應範例

HTTP 200
{
  "result": {
    "content": [
      {
        "type": "text",
        "text": …
      }
    ],
    "structuredContent": {
      "ok": true,
      "business": {
        "business_id": "coach-dunken",
        "name": "Dunken教練",
        "categories": [
          "coaching"
        ],
        "booking_mode": "request_here",
        "city": "台北市信義區(Taipei Xinyi)",
        "profile": {
          "city": "台北市信義區(Taipei Xinyi)",
          "address": "…",
          "hours": "每天08:00至20:00",
          "booking_policy": "取消與改期需在課程開始24小時前(經AI助理線上取消也是開始前24小時截止)。遲到15分鐘將直接結束當次課程,且無法退費。",
          "payment": "現金或轉帳。課程一次購買10堂NT$18,000(每堂折合NT$1,800)。",
          "notes": "請自行準備合適的訓練服裝,至少提早10分鐘抵達教室。",
          "contact": "…"
        },
        "faq": [],
        "quantity": {
          "label": "人數",
          "unit": "位",
          "max_per_booking": 1
        },
        "online_cancellation": {
          "closes_minutes_before_start": 1440,
          "text": "Online cancellation closes 24 hours before the start time; after that, contact the business directly."
        },
        "pending_hold": {
          "minutes": 10,
          "text": "Bookings that need the business's confirmation are held for 10 minutes. If the business does not confirm by then, the hold is released automatically and the customer can choose another slot."
        },
        "payment": {
          "mode": "venue",
          "text": "Pay at the venue."
        }
      },
      "services": [
        {
          "service_id": "first_assessment",
          "name": "初次體能檢查(含InBody一次)",
          "category": "一對一",
          "summary": "第一次上課:基本動作與體能檢查,並免費做一次InBody身體組成量測(非醫療評估)。",
          "price": {
            "amount": 0,
            "currency": "TWD",
            "unit": "每次",
            "text": "免費"
          },
          "duration_min": 60,
          "schedule": [
            {
              "weekday": "sun",
              "time": "08:00",
              "teacher": "Dunken"
            },
            {
              "weekday": "sun",
              "time": "09:00",
              "teacher": "Dunken"
            },
            …
          ],
          "capacity_per_slot": 1,
          "teachers": [
            "Dunken"
          ],
          "credit_cost": 0
        },
        …
      ],
      "payment": "Paid services are paid at the venue; services priced at 0 are free. This connector does not take payments."
    },
    "isError": false
  },
  "jsonrpc": "2.0",
  "id": 4
}

已對正式服務實際執行(唯讀):為顯示而截短;…表示省略的部分。result.content[0].text(同一份JSON的字串版)與商家的地址、聯絡方式已省略。

查空檔

get_availability需要business_id與service_id,可加days(1至14,預設14)與teacher。回傳的每個時段都有固定不變的slot_id(與slot的值相同)、剩餘名額與價格;時區是Asia/Taipei。

查詢可預約時段

curl

curl -sS "https://mcp.srct.tw/api/v1/businesses/coach-dunken/availability?service_id=pt&days=2" \
  -H "User-Agent: my-agent/1.0"

Python

import json
import urllib.error
import urllib.request

BASE = "https://mcp.srct.tw"


def call(method, path, body=None, headers=None):
    req = urllib.request.Request(
        BASE + path,
        data=None if body is None else json.dumps(body).encode(),
        method=method,
        headers={"User-Agent": "my-agent/1.0", "Content-Type": "application/json", **(headers or {})},
    )
    try:
        with urllib.request.urlopen(req) as res:
            return res.status, json.loads(res.read())
    except urllib.error.HTTPError as err:  # 4xx and 5xx responses still carry a JSON body
        return err.code, json.loads(err.read())


def show(status, body):
    print("HTTP", status)
    print(json.dumps(body, ensure_ascii=False, indent=2))


show(*call("GET", "/api/v1/businesses/coach-dunken/availability?service_id=pt&days=2"))

Node.js

const BASE = "https://mcp.srct.tw";

async function call(method, path, body, headers = {}) {
  const res = await fetch(BASE + path, {
    method,
    headers: { "User-Agent": "my-agent/1.0", "Content-Type": "application/json", ...headers },
    body: body === undefined ? undefined : JSON.stringify(body),
  });
  return { status: res.status, body: await res.json() };
}

function show({ status, body }) {
  console.log("HTTP", status);
  console.log(JSON.stringify(body, null, 2));
}

(async () => {
  show(await call("GET", "/api/v1/businesses/coach-dunken/availability?service_id=pt&days=2"));
})();

回應範例

HTTP 200
{
  "ok": true,
  "service_id": "pt",
  "bookable_here": true,
  "payment": "venue",
  "timezone": "Asia/Taipei",
  "earliest_bookable": "120 minutes from now",
  "online_cancellation": "Online cancellation closes 24 hours before the start time; after that, contact the business directly.",
  "quantity_unit": "位",
  "slots": [
    {
      "slot_id": "2026-10-22T08:00",
      "slot": "2026-10-22T08:00",
      "start": "2026-10-22T08:00:00+08:00",
      "label": "10/22(四)08:00",
      "remaining": 1,
      "price": {
        "amount": 1800000,
        "currency": "twd",
        "per": "person or place",
        "text": "NT$18,000/10堂"
      },
      "resource": {
        "teacher": "Dunken"
      }
    },
    {
      "slot_id": "2026-10-22T09:00",
      "slot": "2026-10-22T09:00",
      "start": "2026-10-22T09:00:00+08:00",
      "label": "10/22(四)09:00",
      "remaining": 1,
      "price": {
        "amount": 1800000,
        "currency": "twd",
        "per": "person or place",
        "text": "NT$18,000/10堂"
      },
      "resource": {
        "teacher": "Dunken"
      }
    },
    …
  ]
}

已對正式服務實際執行(唯讀):為顯示而截短;…表示省略的部分。price.amount的單位是幣別最小單位(新臺幣乘以100);時間為Asia/Taipei。時段會隨呼叫當天而不同。

REST API

同一套核心也能用REST呼叫,路徑都在/api/v1/businesses/{business_id}/底下。預約相關端點用Authorization: Bearer <status_token>驗證;status_token只會在建立預約時回傳一次。

方法與路徑用途
GET availability?service_id=…&days=…查空檔
POST bookings建立預約(內容同request_booking)
GET bookings/{booking_id}查預約,含金額、期限與付款方式
POST bookings/{booking_id}/complete帶付款憑證完成預約
PATCH bookings/{booking_id}改時段或人數
DELETE bookings/{booking_id}取消預約(已付款的會退款,客人自己取消時扣除金流手續費與平台手續費)

錯誤一律回{"error": {"code", "message"}}與對應的HTTP狀態,常見代碼見接受代理預約。

探索文件

https://mcp.srct.tw/openapi.json目前只描述機器付款的付款連結,並把可付款的操作標上x-payment-info。目前只有測試網的機器付款會用到,見機器付款。

讀取探索文件:/openapi.json

curl

curl -sS "https://mcp.srct.tw/openapi.json" \
  -H "User-Agent: my-agent/1.0"

Python

import json
import urllib.error
import urllib.request

BASE = "https://mcp.srct.tw"


def call(method, path, body=None, headers=None):
    req = urllib.request.Request(
        BASE + path,
        data=None if body is None else json.dumps(body).encode(),
        method=method,
        headers={"User-Agent": "my-agent/1.0", "Content-Type": "application/json", **(headers or {})},
    )
    try:
        with urllib.request.urlopen(req) as res:
            return res.status, json.loads(res.read())
    except urllib.error.HTTPError as err:  # 4xx and 5xx responses still carry a JSON body
        return err.code, json.loads(err.read())


def show(status, body):
    print("HTTP", status)
    print(json.dumps(body, ensure_ascii=False, indent=2))


show(*call("GET", "/openapi.json"))

Node.js

const BASE = "https://mcp.srct.tw";

async function call(method, path, body, headers = {}) {
  const res = await fetch(BASE + path, {
    method,
    headers: { "User-Agent": "my-agent/1.0", "Content-Type": "application/json", ...headers },
    body: body === undefined ? undefined : JSON.stringify(body),
  });
  return { status: res.status, body: await res.json() };
}

function show({ status, body }) {
  console.log("HTTP", status);
  console.log(JSON.stringify(body, null, 2));
}

(async () => {
  show(await call("GET", "/openapi.json"));
})();

回應範例

HTTP 200
{
  "openapi": "3.1.0",
  "info": {
    "title": "… booking payments",
    "version": "1.0.0",
    "description": "Pay for … bookings with x402 or MPP. Create the booking first with the MCP tool request_booking or POST /api/v1/businesses/{business_id}/bookings; use the payment_link it returns."
  },
  "servers": [
    {
      "url": "https://mcp.srct.tw"
    }
  ],
  "x-service-info": {
    "categories": [
      "booking",
      "fitness",
      "services"
    ],
    "docs": {
      "homepage": "https://mcp.srct.tw/",
      "apiReference": "https://mcp.srct.tw/docs"
    }
  },
  "paths": {
    "/pay/{business_id}/{booking_id}": {
      "post": {
        "summary": "Pay for a booking (x402 or MPP)",
        "parameters": [
          {
            "name": "business_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "booking_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "t",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "pay token from the booking's payment_link"
          }
        ],
        "x-payment-info": {
          "offers": [
            {
              "intent": "charge",
              "method": "evm",
              "amount": null,
              "currency": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
              "description": "USDC on Base Sepolia (test network); also accepted as x402 exact"
            },
            {
              "intent": "charge",
              "method": "tempo",
              "amount": null,
              "currency": "0x20c0000000000000000000000000000000000000",
              "description": "pathUSD on Tempo test network (Moderato)"
            }
          ]
        },
        "responses": {
          "200": {
            "description": "Paid: booking and payment result"
          },
          "402": {
            "description": "Payment Required: x402 PAYMENT-REQUIRED and MPP WWW-Authenticate challenges"
          },
          "409": {
            "description": "Booking not payable"
          },
          "410": {
            "description": "Payment window passed"
          }
        }
      },
      "get": {
        "summary": "Pay for a booking (x402 or MPP)",
        "parameters": [
          {
            "name": "business_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "booking_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "t",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "pay token from the booking's payment_link"
          }
        ],
        "x-payment-info": {
          "offers": [
            {
              "intent": "charge",
              "method": "evm",
              "amount": null,
              "currency": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
              "description": "USDC on Base Sepolia (test network); also accepted as x402 exact"
            },
            {
              "intent": "charge",
              "method": "tempo",
              "amount": null,
              "currency": "0x20c0000000000000000000000000000000000000",
              "description": "pathUSD on Tempo test network (Moderato)"
            }
          ]
        },
        "responses": {
          "200": {
            "description": "Paid: booking and payment result"
          },
          "402": {
            "description": "Payment Required: x402 PAYMENT-REQUIRED and MPP WWW-Authenticate challenges"
          },
          "409": {
            "description": "Booking not payable"
          },
          "410": {
            "description": "Payment window passed"
          }
        }
      }
    }
  }
}

已對正式服務實際執行(唯讀)

使用時的注意事項

機器可讀的資源

測試你的串接