@mongoui/skills 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,188 @@
1
+ #!/usr/bin/env python3
2
+ """Generate or send signed requests for the Flink open-platform gateway.
3
+
4
+ Examples:
5
+ python scripts/flink_signed_request.py ^
6
+ --method POST ^
7
+ --path /openapi/v1/flink/zh_CN/airlines/list ^
8
+ --body-json "{\"page\":1,\"pageSize\":20}" ^
9
+ --app-key open_app_xxx ^
10
+ --app-secret sk_xxx
11
+
12
+ python scripts/flink_signed_request.py ^
13
+ --method GET ^
14
+ --path /openapi/v1/flink/fields ^
15
+ --query orderNo=dd20251020161158787680281 ^
16
+ --app-key open_app_xxx ^
17
+ --app-secret sk_xxx ^
18
+ --send
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import argparse
24
+ import hashlib
25
+ import hmac
26
+ import json
27
+ import os
28
+ import sys
29
+ import time
30
+ import urllib.parse
31
+ import urllib.request
32
+ import uuid
33
+ from pathlib import Path
34
+
35
+
36
+ def sha256_hex(data: bytes) -> str:
37
+ return hashlib.sha256(data).hexdigest()
38
+
39
+
40
+ def parse_query_items(items: list[str]) -> list[tuple[str, str]]:
41
+ pairs: list[tuple[str, str]] = []
42
+ for item in items:
43
+ if "=" not in item:
44
+ raise ValueError(f"invalid query item: {item!r}; expected key=value")
45
+ key, value = item.split("=", 1)
46
+ pairs.append((key, value))
47
+ return pairs
48
+
49
+
50
+ def canonical_query(pairs: list[tuple[str, str]]) -> str:
51
+ ordered = sorted(pairs, key=lambda item: (item[0], item[1]))
52
+ return "&".join(
53
+ f"{urllib.parse.quote(key, safe='~-._')}={urllib.parse.quote(value, safe='~-._')}"
54
+ for key, value in ordered
55
+ )
56
+
57
+
58
+ def build_sign_payload(
59
+ method: str,
60
+ path: str,
61
+ query_string: str,
62
+ body_bytes: bytes,
63
+ timestamp: str,
64
+ nonce: str,
65
+ app_key: str,
66
+ ) -> str:
67
+ return "\n".join(
68
+ [
69
+ method.upper(),
70
+ path,
71
+ query_string,
72
+ sha256_hex(body_bytes),
73
+ timestamp,
74
+ nonce,
75
+ app_key,
76
+ ]
77
+ )
78
+
79
+
80
+ def sign(secret: str, payload: str) -> str:
81
+ return hmac.new(secret.encode("utf-8"), payload.encode("utf-8"), hashlib.sha256).hexdigest()
82
+
83
+
84
+ def load_body(args: argparse.Namespace) -> bytes:
85
+ if args.body_file:
86
+ return Path(args.body_file).read_bytes()
87
+ if args.body_json is None:
88
+ return b""
89
+ parsed = json.loads(args.body_json)
90
+ return json.dumps(parsed, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
91
+
92
+
93
+ def build_url(base_url: str, path: str, query_string: str) -> str:
94
+ base = base_url.rstrip("/")
95
+ url = f"{base}{path}"
96
+ if query_string:
97
+ url = f"{url}?{query_string}"
98
+ return url
99
+
100
+
101
+ def build_headers(app_key: str, timestamp: str, nonce: str, signature: str, has_body: bool) -> dict[str, str]:
102
+ headers = {
103
+ "Accept": "application/json",
104
+ "X-OP-App-Key": app_key,
105
+ "X-OP-Timestamp": timestamp,
106
+ "X-OP-Nonce": nonce,
107
+ "X-OP-Sign": signature,
108
+ }
109
+ if has_body:
110
+ headers["Content-Type"] = "application/json"
111
+ return headers
112
+
113
+
114
+ def send_request(method: str, url: str, headers: dict[str, str], body_bytes: bytes, timeout: float) -> tuple[int, str]:
115
+ request = urllib.request.Request(url=url, data=body_bytes or None, headers=headers, method=method.upper())
116
+ try:
117
+ with urllib.request.urlopen(request, timeout=timeout) as response:
118
+ return response.status, response.read().decode("utf-8", errors="replace")
119
+ except urllib.error.HTTPError as exc:
120
+ return exc.code, exc.read().decode("utf-8", errors="replace")
121
+
122
+
123
+ def main() -> int:
124
+ parser = argparse.ArgumentParser(description="Generate or send a signed Flink open-platform request.")
125
+ parser.add_argument("--base-url", default=os.getenv("OPEN_PLATFORM_BASE_URL", "http://127.0.0.1:8671"))
126
+ parser.add_argument("--method", required=True)
127
+ parser.add_argument("--path", required=True)
128
+ parser.add_argument("--query", action="append", default=[], help="Repeatable key=value query parameter")
129
+ parser.add_argument("--body-json", help="JSON string request body")
130
+ parser.add_argument("--body-file", help="Path to a raw request body file")
131
+ parser.add_argument("--app-key", default=os.getenv("OPEN_PLATFORM_APP_KEY") or os.getenv("OPEN_PLATFORM_OPEN_APP_ID"))
132
+ parser.add_argument("--app-secret", default=os.getenv("OPEN_PLATFORM_APP_SECRET") or os.getenv("OPEN_PLATFORM_OPEN_SECRET"))
133
+ parser.add_argument("--timestamp", default=str(int(time.time())))
134
+ parser.add_argument("--nonce", default=uuid.uuid4().hex)
135
+ parser.add_argument("--send", action="store_true")
136
+ parser.add_argument("--timeout", type=float, default=15.0)
137
+ args = parser.parse_args()
138
+
139
+ if not args.app_key or not args.app_secret:
140
+ print("app key and app secret are required", file=sys.stderr)
141
+ return 2
142
+
143
+ try:
144
+ query_pairs = parse_query_items(args.query)
145
+ body_bytes = load_body(args)
146
+ except Exception as exc: # pragma: no cover - simple CLI error path
147
+ print(str(exc), file=sys.stderr)
148
+ return 2
149
+
150
+ query_string = canonical_query(query_pairs)
151
+ payload = build_sign_payload(
152
+ method=args.method,
153
+ path=args.path,
154
+ query_string=query_string,
155
+ body_bytes=body_bytes,
156
+ timestamp=args.timestamp,
157
+ nonce=args.nonce,
158
+ app_key=args.app_key,
159
+ )
160
+ signature = sign(args.app_secret, payload)
161
+ headers = build_headers(args.app_key, args.timestamp, args.nonce, signature, bool(body_bytes))
162
+ url = build_url(args.base_url, args.path, query_string)
163
+
164
+ print("URL:")
165
+ print(url)
166
+ print()
167
+ print("Canonical String:")
168
+ print(payload)
169
+ print()
170
+ print("Headers:")
171
+ print(json.dumps(headers, ensure_ascii=False, indent=2))
172
+ if body_bytes:
173
+ print()
174
+ print("Body:")
175
+ print(body_bytes.decode("utf-8", errors="replace"))
176
+
177
+ if not args.send:
178
+ return 0
179
+
180
+ status, response_text = send_request(args.method, url, headers, body_bytes, args.timeout)
181
+ print()
182
+ print(f"HTTP {status}")
183
+ print(response_text)
184
+ return 0
185
+
186
+
187
+ if __name__ == "__main__":
188
+ raise SystemExit(main())
@@ -0,0 +1,56 @@
1
+ ---
2
+ name: glink-openapi-integration
3
+ description: Integrate with the Glink hotel OpenAPI exposed through the platform runtime gateway. Use when Codex needs to read or implement against `docs/swagger/glink-api.json`, choose the correct Glink hotel endpoint, generate `X-OP-*` signatures, explain sandbox or production credential acquisition and reset, or drive static data, hotel search, hotel info, product, order, checkout, invoice, and balance workflows.
4
+ ---
5
+
6
+ # Glink OpenAPI Integration
7
+
8
+ ## Quick Start
9
+
10
+ Use this skill when the task is "call Glink through the open platform" rather than "call Glink supplier APIs directly".
11
+
12
+ Start with the smallest reference that answers the request:
13
+
14
+ - Read `references/auth-and-credentials.md` for signing, headers, key acquisition, reset, and auth troubleshooting.
15
+ - Read `references/api-workflows.md` for hotel search, booking, cancel, checkout, invoice, and balance workflows.
16
+ - Read `references/endpoint-index.md` for exact method, path, required fields, and supplier-field caveats.
17
+ - Use `scripts/glink_signed_request.py` to generate headers, inspect the canonical string, or send a signed request.
18
+
19
+ ## Working Rules
20
+
21
+ Follow this workflow:
22
+
23
+ 1. Confirm the environment: `sandbox` and `production` credentials are separate.
24
+ 2. Confirm the public path under `/openapi/v1/glink/*`.
25
+ 3. Build the minimum valid request body from `references/endpoint-index.md`.
26
+ 4. Sign the exact public path, sorted query string, and raw body bytes with the platform secret.
27
+ 5. Evaluate both HTTP status and response envelope `code`.
28
+
29
+ Do not violate these rules:
30
+
31
+ - Always send `X-OP-App-Key`, `X-OP-Timestamp`, `X-OP-Nonce`, and `X-OP-Sign`.
32
+ - Never send supplier-side `partnerCode`, `signature`, `requestType`, or supplier `timestamp`.
33
+ - Never wrap the payload in supplier-style `header` or `businessRequest`; send only business fields.
34
+ - Treat `code=SUCCESS` as success. Treat `code=SUPPLIER_BIZ_ERROR` as a business failure even if HTTP status is `200`.
35
+ - Keep timestamp skew within `+-5` minutes.
36
+ - Reuse of the same nonce under the same app key can trigger `NONCE_REPLAY`.
37
+ - If a swagger schema still shows supplier fields as required, follow the route description first and omit those supplier-managed fields from the client payload.
38
+
39
+ ## Typical Requests
40
+
41
+ Use these patterns by default:
42
+
43
+ - Static data: city, country, district, business area, and brand lookup calls
44
+ - Search and booking: destination or keyword lookup -> hotel search or hotel list -> product detail or lowest price -> `checkBooking` -> `createOrder` -> `payOrder` -> `queryOrderDetail`
45
+ - Order aftercare: `cancelOrder`, `queryOrderCheckDetailInfo`, `checkoutApply`, `queryCheckoutDetail`
46
+ - Finance: `createInvoice`, `queryCreditBalance`
47
+
48
+ ## Output Expectations
49
+
50
+ When answering or coding with this skill:
51
+
52
+ - Prefer concrete request examples over abstract summaries.
53
+ - State which environment the credential belongs to.
54
+ - State whether the payload should contain only business fields.
55
+ - Call out supplier-field residue in the swagger when relevant.
56
+ - Call out auth failures with the likely root cause and the exact header or field to inspect.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Glink OpenAPI 接入"
3
+ short_description: "帮助完成 Glink 酒店开放接口鉴权、凭证获取和业务调用"
4
+ default_prompt: "Use $glink-openapi-integration to integrate with the Glink hotel open API, including auth, credentials, and endpoint selection."
@@ -0,0 +1,84 @@
1
+ # Glink API Workflows
2
+
3
+ ## Static Data
4
+
5
+ Use these calls to build hotel search prerequisites:
6
+
7
+ - `glink.queryCountryList`
8
+ - `glink.queryCityList`
9
+ - `glink.queryHotCityList`
10
+ - `glink.queryDistrictList`
11
+ - `glink.queryDistrictAndBusiness`
12
+ - `glink.queryBusinessList`
13
+ - `glink.queryHotelPlateList`
14
+ - `glink.queryCityAndHotelByKeyword`
15
+
16
+ These APIs usually feed city selection, district filters, brand filters, and keyword suggestions.
17
+
18
+ ## Hotel Search And Product Selection
19
+
20
+ Use this sequence for discovery and pricing:
21
+
22
+ 1. `glink.getDestination` or `glink.queryCityAndHotelByKeyword`
23
+ - Resolve the destination or keyword to a city or hotel scope.
24
+ 2. `glink.getHotelSearch` or `glink.findHotelList`
25
+ - Search hotels using city, dates, room count, and optional filters.
26
+ 3. `glink.queryHotelLowestPrice`
27
+ - Pull summary pricing for selected hotel IDs.
28
+ 4. `glink.queryProductDetail`
29
+ - Read real-time sellable products for a selected hotel and stay range.
30
+ 5. `glink.queryHotelInfo`, `glink.queryHotelImage`, or `glink.queryHotelMainInfoByHotelIds`
31
+ - Hydrate hotel details for display or validation.
32
+
33
+ Use `queryHotelAllList`, `queryHotelIdList`, and `queryHotelIncrement` when synchronizing hotel catalogs rather than serving live booking UX.
34
+
35
+ ## Booking Flow
36
+
37
+ Use this sequence for a normal hotel booking flow:
38
+
39
+ 1. `glink.queryProductDetail`
40
+ - Identify the exact sellable room and rate.
41
+ 2. `glink.checkBooking`
42
+ - Perform pre-booking validation.
43
+ 3. `glink.createOrder`
44
+ - Submit the order with guest, contact, amount, and price item details.
45
+ 4. `glink.payOrder`
46
+ - Pay when the order flow requires explicit payment.
47
+ 5. `glink.queryOrderDetail`
48
+ - Track final status and supplier-side details.
49
+
50
+ Key order identifiers:
51
+
52
+ - `coOrderCode`: your platform-side order number
53
+ - `fcOrderCode`: supplier-side order number when available
54
+
55
+ Prefer carrying both once they appear, because several aftercare APIs accept one or both.
56
+
57
+ ## Order Aftercare
58
+
59
+ Use these calls after booking:
60
+
61
+ - `glink.cancelOrder`
62
+ - Cancel an order when policy permits.
63
+ - `glink.queryOrderCheckDetailInfo`
64
+ - Read extended order or check-in related detail.
65
+ - `glink.checkoutApply`
66
+ - Submit a checkout or refund-like aftercare request for an order.
67
+ - `glink.queryCheckoutDetail`
68
+ - Query the checkout task by `taskOrderCode`.
69
+
70
+ ## Finance
71
+
72
+ Use these calls for financial follow-up:
73
+
74
+ - `glink.createInvoice`
75
+ - Submit invoice information using business fields only.
76
+ - Ignore supplier-managed `partnerCode` and `sign` even if they appear in the swagger schema.
77
+ - `glink.queryCreditBalance`
78
+ - Read remaining credit balance.
79
+
80
+ ## Business Handling Rules
81
+
82
+ - Do not treat HTTP `200` as enough; inspect `code`.
83
+ - Do not rebuild supplier headers or wrapper payloads; the runtime gateway handles supplier authentication and wrapping.
84
+ - Preserve upstream order identifiers from earlier responses instead of recomputing them.
@@ -0,0 +1,157 @@
1
+ # Glink Auth And Credentials
2
+
3
+ ## Required Headers
4
+
5
+ Send these headers on every Glink open-platform request:
6
+
7
+ - `X-OP-App-Key`
8
+ - `X-OP-Timestamp`
9
+ - `X-OP-Nonce`
10
+ - `X-OP-Sign`
11
+
12
+ Optional headers that can help tracing or idempotency:
13
+
14
+ - `X-OP-Client-Request-Id`
15
+ - `X-Trace-Id`
16
+ - `X-Idempotency-Key`
17
+
18
+ `X-OP-App-Key` is the issued `open_app_id`.
19
+
20
+ ## Signature Algorithm
21
+
22
+ Build the canonical string in this exact order, separated by newline characters:
23
+
24
+ ```text
25
+ METHOD
26
+ PATH
27
+ CANONICAL_QUERY
28
+ BODY_SHA256
29
+ TIMESTAMP
30
+ NONCE
31
+ APP_KEY
32
+ ```
33
+
34
+ Rules:
35
+
36
+ - Uppercase `METHOD`.
37
+ - Use the exact public request path, for example `/openapi/v1/glink/queryBusinessList`.
38
+ - Sort query parameters by key and then value before joining them as `k=v&k2=v2`.
39
+ - Hash the raw request body bytes with SHA256 and use the lowercase hex digest.
40
+ - Use the request timestamp exactly as sent in `X-OP-Timestamp`.
41
+ - Use the request nonce exactly as sent in `X-OP-Nonce`.
42
+ - Use the same `open_app_id` that is sent in `X-OP-App-Key`.
43
+ - Sign the canonical string with the plain `open_app_secret` using HMAC-SHA256, then output lowercase hex.
44
+
45
+ Notes:
46
+
47
+ - Timestamp may be Unix seconds, Unix milliseconds, or RFC3339.
48
+ - The platform validates timestamp drift within roughly `+-5` minutes.
49
+ - Nonce must be unique for the same app key in a short time window.
50
+ - For empty bodies, hash the empty byte string.
51
+
52
+ ## Platform Wrapping Rules
53
+
54
+ Glink supplier authentication is handled by the runtime gateway.
55
+
56
+ Client responsibilities:
57
+
58
+ - Send only business fields.
59
+ - Sign the public open-platform request.
60
+
61
+ Do not send:
62
+
63
+ - `partnerCode`
64
+ - `signature` or `sign` used by the supplier
65
+ - `requestType`
66
+ - supplier-side `timestamp`
67
+ - wrapper objects like `header` or `businessRequest`
68
+
69
+ Important exception handling:
70
+
71
+ - `createInvoice` currently still exposes `partnerCode` and `sign` in schema examples and required lists.
72
+ - The route description still says the platform fills those fields automatically.
73
+ - Prefer the route description and treat those fields as supplier-managed.
74
+
75
+ ## Credential Acquisition
76
+
77
+ Treat credentials as environment-specific.
78
+
79
+ Sandbox:
80
+
81
+ 1. The developer applies for app access.
82
+ 2. The operator activates the app access with `env_type=sandbox`.
83
+ 3. The platform records sandbox supplier mapping and issues a sandbox `open_app_id/open_app_secret`.
84
+ 4. The app access becomes `sandbox_active`.
85
+
86
+ Production:
87
+
88
+ 1. The developer submits production access after sandbox work is complete.
89
+ 2. The operator approves production with `env_type=production`.
90
+ 3. The platform issues a production `open_app_id/open_app_secret`.
91
+ 4. The production credential becomes usable only after the app access reaches `production_active`.
92
+
93
+ Important constraints:
94
+
95
+ - Sandbox and production credentials are separate and not interchangeable.
96
+ - Developers can see platform credentials only.
97
+ - Developers cannot see supplier-side secrets.
98
+
99
+ ## How To View Or Reset Keys
100
+
101
+ Developer portal APIs:
102
+
103
+ - `GET /api/portal/apps/{app_id}/credentials`
104
+ - Returns the credential list for the app.
105
+ - Includes `open_app_id`, `provider_code`, `env_type`, `open_secret_version`, and masked secret data.
106
+ - Does not return the plain secret in normal list queries.
107
+ - `POST /api/portal/apps/{app_id}/credentials/reset`
108
+ - Resets the platform secret and returns `plain_open_secret`.
109
+ - If the app has more than one credential mapping, pass both `provider_code` and `env_type`.
110
+
111
+ Response fields that matter:
112
+
113
+ - `open_app_id`: the App Key to place into `X-OP-App-Key`
114
+ - `open_secret_masked`: masked secret for display only
115
+ - `open_secret_version`: useful for rotation tracking
116
+ - `plain_open_secret`: returned only on first issuance or reset
117
+
118
+ Practical guidance:
119
+
120
+ - If a developer says "I can only see a masked secret", instruct them to use the reset endpoint or the corresponding portal UI action.
121
+ - If the task is only to call sandbox APIs, do not assume production credentials work.
122
+ - If the app has both sandbox and production mappings, always name the target `env_type` explicitly.
123
+
124
+ ## Common Auth Failures
125
+
126
+ - `APP_KEY_INVALID`
127
+ - Wrong `open_app_id`, wrong environment, or the credential does not exist.
128
+ - `SIGN_ERROR`
129
+ - Wrong secret, wrong path, wrong query ordering, wrong body hash, or newline formatting mismatch.
130
+ - `TIMESTAMP_EXPIRED`
131
+ - Clock skew exceeds the allowed window.
132
+ - `NONCE_REPLAY`
133
+ - Nonce was reused under the same app key.
134
+ - `PRODUCT_FORBIDDEN`
135
+ - The app is not enabled for the product, the mapping is not active, or an IP whitelist rule failed.
136
+
137
+ ## Response Contract
138
+
139
+ The platform wraps business responses in a common envelope:
140
+
141
+ ```json
142
+ {
143
+ "request_id": "req_xxx",
144
+ "trace_id": "trc_xxx",
145
+ "downstream_request_id": "optional",
146
+ "code": "SUCCESS",
147
+ "message": "ok",
148
+ "data": {}
149
+ }
150
+ ```
151
+
152
+ Interpretation:
153
+
154
+ - `request_id` and `trace_id` are platform-side tracing keys.
155
+ - `downstream_request_id` is the supplier request identifier when available.
156
+ - `code=SUCCESS` means the business call succeeded.
157
+ - `code=SUPPLIER_BIZ_ERROR` means the supplier returned a business error and can still arrive with HTTP `200`.
@@ -0,0 +1,126 @@
1
+ # Glink Endpoint Index
2
+
3
+ All routes are public gateway routes. Sign the public path, not the downstream supplier path.
4
+
5
+ ## Static Data
6
+
7
+ - `glink.queryBusinessList`
8
+ - `POST /openapi/v1/glink/queryBusinessList`
9
+ - Required body fields: `cityCode`
10
+ - `glink.queryCountryList`
11
+ - `POST /openapi/v1/glink/queryCountryList`
12
+ - Required body fields: none
13
+ - `glink.queryHotelPlateList`
14
+ - `POST /openapi/v1/glink/queryHotelPlateList`
15
+ - Required body fields: none
16
+ - `glink.queryHotCityList`
17
+ - `POST /openapi/v1/glink/queryHotCityList`
18
+ - Required body fields: none
19
+ - `glink.queryDistrictList`
20
+ - `POST /openapi/v1/glink/queryDistrictList`
21
+ - Required body fields: `cityCode`
22
+ - `glink.queryDistrictAndBusiness`
23
+ - `POST /openapi/v1/glink/queryDistrictAndBusiness`
24
+ - Required body fields: `cityCode`
25
+ - `glink.queryCityList`
26
+ - `POST /openapi/v1/glink/queryCityList`
27
+ - Required body fields: `countryCode`
28
+
29
+ ## Hotel Query
30
+
31
+ - `glink.queryCityAndHotelByKeyword`
32
+ - `POST /openapi/v1/glink/queryCityAndHotelByKeyword`
33
+ - Required body fields: `keyWord`
34
+ - `glink.getDestination`
35
+ - `POST /openapi/v1/glink/getDestination`
36
+ - Required body fields: `keyWord`
37
+ - Swagger example uses `keyword`; double-check the field naming against current backend behavior before coding around it.
38
+ - `glink.getHotelSearch`
39
+ - `POST /openapi/v1/glink/getHotelSearch`
40
+ - Required body fields: none
41
+ - Common fields: `cityCode`, `checkInDate`, `checkOutDate`, `roomNum`, `language`
42
+ - `glink.findHotelList`
43
+ - `POST /openapi/v1/glink/findHotelList`
44
+ - Required body fields from schema: `roomFacilityCodes`, `hotelFacilityCodes`, `checkInDate`, `checkOutDate`, `distance`
45
+ - Treat this as a high-friction route and inspect the real example payload before implementing.
46
+ - `glink.queryHotelAllList`
47
+ - `POST /openapi/v1/glink/queryHotelAllList`
48
+ - Required body fields: none
49
+
50
+ ## Hotel Info
51
+
52
+ - `glink.queryHotelIdList`
53
+ - `POST /openapi/v1/glink/queryHotelIdList`
54
+ - Required body fields: none
55
+ - `glink.queryHotelInfo`
56
+ - `POST /openapi/v1/glink/queryHotelInfo`
57
+ - Required body fields: `hotelIds`
58
+ - `glink.queryHotelIncrement`
59
+ - `POST /openapi/v1/glink/queryHotelIncrement`
60
+ - Required body fields: `startTime`, `endTime`
61
+ - Swagger example uses `lastUpdateTime`; inspect schema versus example before generating code.
62
+ - `glink.queryHotelImage`
63
+ - `POST /openapi/v1/glink/queryHotelImage`
64
+ - Required body fields: `hotelIds`
65
+ - Swagger example uses `hotelId`; inspect schema versus example before generating code.
66
+ - `glink.queryHotelMainInfoByHotelIds`
67
+ - `POST /openapi/v1/glink/queryHotelMainInfoByHotelIds`
68
+ - Required body fields: `language`
69
+
70
+ ## Product Info
71
+
72
+ - `glink.queryHotelLowestPrice`
73
+ - `POST /openapi/v1/glink/queryHotelLowestPrice`
74
+ - Required body fields: `hotelIds`
75
+ - `glink.queryProductDetail`
76
+ - `POST /openapi/v1/glink/queryProductDetail`
77
+ - Required body fields: `hotelId`, `checkInDate`, `checkOutDate`, `roomNum`
78
+
79
+ ## Order Info
80
+
81
+ - `glink.checkBooking`
82
+ - `POST /openapi/v1/glink/checkBooking`
83
+ - Required body fields: `hotelId`, `checkInDate`, `checkOutDate`, `roomId`, `ratePlanId`, `roomNum`, `supplyCode`
84
+ - `glink.createOrder`
85
+ - `POST /openapi/v1/glink/createOrder`
86
+ - Required body fields: `hotelId`, `checkInDate`, `checkOutDate`, `roomId`, `ratePlanId`, `roomNum`, `supplyCode`, `totalAmount`, `coOrderCode`, `priceItems`, `guestInfos`, `linkMan`, `linkPhone`, `arriveTime`, `latestArriveTime`
87
+ - `glink.payOrder`
88
+ - `POST /openapi/v1/glink/payOrder`
89
+ - Required body fields: none
90
+ - Common fields: `coOrderCode`, `fcOrderCode`
91
+ - `glink.cancelOrder`
92
+ - `POST /openapi/v1/glink/cancelOrder`
93
+ - Required body fields: none
94
+ - Common fields: `coOrderCode`, `fcOrderCode`, `cancelReson`
95
+ - `glink.queryOrderDetail`
96
+ - `POST /openapi/v1/glink/queryOrderDetail`
97
+ - Required body fields: none
98
+ - Common fields: `coOrderCode`, `fcOrderCode`
99
+ - `glink.queryOrderCheckDetailInfo`
100
+ - `POST /openapi/v1/glink/queryOrderCheckDetailInfo`
101
+ - Required body fields: none
102
+ - Common fields: `coOrderCode`, `fcOrderCode`
103
+ - `glink.checkoutApply`
104
+ - `POST /openapi/v1/glink/checkoutApply`
105
+ - Required body fields: `fcOrderCode`, `checkoutReason`, `checkoutRoomInfoList`
106
+ - `glink.queryCheckoutDetail`
107
+ - `POST /openapi/v1/glink/queryCheckoutDetail`
108
+ - Required body fields: `taskOrderCode`
109
+ - Swagger example uses order codes instead of `taskOrderCode`; inspect the live contract before coding.
110
+
111
+ ## Finance
112
+
113
+ - `glink.createInvoice`
114
+ - `POST /openapi/v1/glink/createInvoice`
115
+ - Business-required fields to focus on: `orderCode`, `invoiceTypeInteger`, `invoiceHeader`, `amount`, `applyId`, `invoiceTitleType`, `getMethod`
116
+ - Schema still marks `partnerCode` and `sign` as required, but route docs say the platform injects supplier auth fields. Do not send supplier-managed fields from the client.
117
+ - `glink.queryCreditBalance`
118
+ - `POST /openapi/v1/glink/queryCreditBalance`
119
+ - Required body fields: none
120
+
121
+ ## Global Notes
122
+
123
+ - Most Glink routes accept plain business JSON bodies with no wrapper objects.
124
+ - Platform error responses can return `401`, `403`, `404`, `500`, `502`, or `504`.
125
+ - Common auth-related `401` codes are `APP_KEY_INVALID`, `SIGN_ERROR`, `TIMESTAMP_EXPIRED`, and `NONCE_REPLAY`.
126
+ - Common `403` cause is `PRODUCT_FORBIDDEN`.