@agenzo/merchant-cli 0.8.17 → 0.9.1
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.
- package/README.md +343 -343
- package/dist/index.js +520 -131
- package/dist/index.js.map +1 -1
- package/package.json +28 -28
package/dist/index.js
CHANGED
|
@@ -1197,6 +1197,22 @@ var ridePayOrderSchema = {
|
|
|
1197
1197
|
default: false,
|
|
1198
1198
|
description: "[\u65B9\u6848 B/R16] Explicit opt-in to settle this pay_per_call ride via the EVO bound-card fallback rail. Set true ONLY when the user explicitly chose EVO; forwarded to the pay body as evo_explicit=true. Without it, a pay_per_call settlement lacking --payment-token-id is hard-gated server-side. Ignored for monthly_settlement and on the payment-token-id direct-charge path."
|
|
1199
1199
|
},
|
|
1200
|
+
authenticate: {
|
|
1201
|
+
type: "bool",
|
|
1202
|
+
required: false,
|
|
1203
|
+
default: false,
|
|
1204
|
+
description: "Opt-in to require cardholder 3DS on the network-token direct charge (payment-token-id, EVO/Mastercard). On step-up the response is AUTHENTICATION_REQUIRED with three_ds_url + charge_no; resume via authorized-charge-no. Default off = frictionless. Ignored on the EVO preauth path and for UnionPay/Visa."
|
|
1205
|
+
},
|
|
1206
|
+
"return-url": {
|
|
1207
|
+
type: "string",
|
|
1208
|
+
required: false,
|
|
1209
|
+
description: "Optional 3DS/passkey return URL for the network-token authenticate path; falls back to the platform EVO_3DS_RETURN_URL."
|
|
1210
|
+
},
|
|
1211
|
+
"authorized-charge-no": {
|
|
1212
|
+
type: "string",
|
|
1213
|
+
required: false,
|
|
1214
|
+
description: "Resume a network-token 3DS challenge: charge_no from a prior AUTHENTICATION_REQUIRED response (settled via resume_token). Relaxes the exactly-one-credential rule (continuation, not a fresh charge)."
|
|
1215
|
+
},
|
|
1200
1216
|
"idempotency-key": {
|
|
1201
1217
|
type: "string",
|
|
1202
1218
|
required: true,
|
|
@@ -1662,6 +1678,22 @@ var hotelPayOrderSchema = {
|
|
|
1662
1678
|
default: false,
|
|
1663
1679
|
description: "[\u65B9\u6848 B/R16] Explicit opt-in to settle this pay_per_call order via the EVO bound-card fallback rail. Set true ONLY when the user explicitly chose EVO; forwarded to the pay body as evo_explicit=true. Without it, a pay_per_call settlement lacking --payment-token-id is hard-gated server-side. Ignored for monthly_settlement and on the payment-token-id direct-charge path."
|
|
1664
1680
|
},
|
|
1681
|
+
authenticate: {
|
|
1682
|
+
type: "bool",
|
|
1683
|
+
required: false,
|
|
1684
|
+
default: false,
|
|
1685
|
+
description: "Opt-in to require cardholder 3DS on the network-token direct charge (payment-token-id, EVO/Mastercard). On step-up the response is AUTHENTICATION_REQUIRED with three_ds_url + charge_no; resume via authorized-charge-no. Default off = frictionless. Ignored on the EVO preauth path and for UnionPay/Visa."
|
|
1686
|
+
},
|
|
1687
|
+
"return-url": {
|
|
1688
|
+
type: "string",
|
|
1689
|
+
required: false,
|
|
1690
|
+
description: "Optional 3DS/passkey return URL for the network-token authenticate path; falls back to the platform EVO_3DS_RETURN_URL."
|
|
1691
|
+
},
|
|
1692
|
+
"authorized-charge-no": {
|
|
1693
|
+
type: "string",
|
|
1694
|
+
required: false,
|
|
1695
|
+
description: "Resume a network-token 3DS challenge: charge_no from a prior AUTHENTICATION_REQUIRED response (settled via resume_token), then triggers upstream payOrder on success (pay_per_call only)."
|
|
1696
|
+
},
|
|
1665
1697
|
"idempotency-key": {
|
|
1666
1698
|
type: "string",
|
|
1667
1699
|
required: true,
|
|
@@ -2231,6 +2263,97 @@ var unifiedOrdersGetSchema = {
|
|
|
2231
2263
|
INTERNAL_ERROR: "The order's provider detail lookup is temporarily unavailable. Retry once after a short delay."
|
|
2232
2264
|
}
|
|
2233
2265
|
};
|
|
2266
|
+
var ordersPayBatchSchema = {
|
|
2267
|
+
cli: CLI_NAME,
|
|
2268
|
+
noun: ORDERS_NOUN,
|
|
2269
|
+
verb: "pay-batch",
|
|
2270
|
+
description: "Settle a batch of orders (may mix ride/hotel/flight \u2014 hho_/ffo_/rio_) in ONE aggregate payment via a single payment credential. Physical landing point of the logical write tool orders__aggregate__pay-batch, invoked by the trip-aggregate pay-orders write verb through UniversalGateway (executed only after the single confirmation gate; never exposed to the LLM). Amounts are NOT passed here \u2014 the platform sums each order's locked authoritative price. Provide the aggregate total via one rail: --payment-token-id (\u8F68A: network-token minted for the aggregate total, external_transaction_id bound to this payment_group \u2014 one UnionPay/Visa passkey) OR --payment-method-id (\u8F68B: EVO bound card \u2014 one 3DS challenge); both may be omitted (monthly_settlement / platform default card). On EVO cardholder authentication the response is AUTHENTICATION_REQUIRED with a three_ds_url + merchant_trans_id; finish it out-of-band, then an idempotent replay settles. On success the batch becomes settled / partially_settled (upstream-confirmation-failed orders are refunded). Supports --watch to poll until a terminal batch status.",
|
|
2271
|
+
flags: {
|
|
2272
|
+
"order-ids": {
|
|
2273
|
+
type: "string",
|
|
2274
|
+
required: true,
|
|
2275
|
+
description: `Ordered order ids to settle together \u2014 comma-separated (hho_1,ffo_2,rio_3) or a JSON array (["hho_1","ffo_2"]). May mix hho_/ffo_/rio_ domains. Amounts come from each order's locked authoritative price (NOT passed here).`
|
|
2276
|
+
},
|
|
2277
|
+
"payment-token-id": {
|
|
2278
|
+
type: "string",
|
|
2279
|
+
required: false,
|
|
2280
|
+
description: "[\u8F68A] Network-token id (UnionPay/Visa) minted for the aggregate total with external_transaction_id bound to this payment_group (direct-charge main path). Mutually exclusive with --payment-method-id; both may be omitted for monthly_settlement / platform default."
|
|
2281
|
+
},
|
|
2282
|
+
"payment-method-id": {
|
|
2283
|
+
type: "string",
|
|
2284
|
+
required: false,
|
|
2285
|
+
description: "[\u8F68B] EVO bound-card id to settle the aggregate total via one 3DS challenge (explicit/fallback rail). Mutually exclusive with --payment-token-id."
|
|
2286
|
+
},
|
|
2287
|
+
"evo-explicit": {
|
|
2288
|
+
type: "bool",
|
|
2289
|
+
required: false,
|
|
2290
|
+
default: false,
|
|
2291
|
+
description: "[\u8F68B \u786C\u95E8\u69DB] Explicit opt-in to settle via the EVO bound-card rail. Set true ONLY when the user explicitly chose EVO; forwarded to the body as evo_explicit=true. Omit on the payment-token-id direct-charge path."
|
|
2292
|
+
},
|
|
2293
|
+
"payment-group-id": {
|
|
2294
|
+
type: "string",
|
|
2295
|
+
required: false,
|
|
2296
|
+
description: "Optional caller-pre-generated payment group id; omit to let the platform generate one. The whole batch is associated with this group for cross-provider reconciliation."
|
|
2297
|
+
},
|
|
2298
|
+
member: MEMBER_FLAG_SCHEMA,
|
|
2299
|
+
"idempotency-key": {
|
|
2300
|
+
type: "string",
|
|
2301
|
+
required: true,
|
|
2302
|
+
description: "Unique key forwarded verbatim as the Idempotency-Key header; never auto-generated. The SAME key + the SAME ordered order_ids replays as the same batch (dedup re-check, no double charge); any change to order_ids yields a new batch.",
|
|
2303
|
+
constraints: "1-128 chars [A-Za-z0-9_-]"
|
|
2304
|
+
},
|
|
2305
|
+
watch: {
|
|
2306
|
+
type: "bool",
|
|
2307
|
+
required: false,
|
|
2308
|
+
default: false,
|
|
2309
|
+
description: "Poll until a terminal batch status (settled / partially_settled / failed) or timeout, emitting one NDJSON line per poll. Non-terminal states (authorizing/capturing, AUTHENTICATION_REQUIRED) keep polling \u2014 after the user finishes 3DS out-of-band, the next idempotent replay settles."
|
|
2310
|
+
},
|
|
2311
|
+
// watch 默认值与 pay-batch.ts 的 DEFAULT_PAY_BATCH_WATCH_* 常量一致;此处内联字面量
|
|
2312
|
+
// 以避免 verb-schema.ts ↔ pay-batch.ts 循环依赖(pay-batch.ts 反向依赖本模块的 schema)。
|
|
2313
|
+
"watch-interval": {
|
|
2314
|
+
type: "int",
|
|
2315
|
+
required: false,
|
|
2316
|
+
default: 5,
|
|
2317
|
+
description: "Seconds between polls when --watch is set"
|
|
2318
|
+
},
|
|
2319
|
+
"watch-timeout": {
|
|
2320
|
+
type: "int",
|
|
2321
|
+
required: false,
|
|
2322
|
+
default: 300,
|
|
2323
|
+
description: "Max seconds to poll before giving up"
|
|
2324
|
+
}
|
|
2325
|
+
},
|
|
2326
|
+
response: {
|
|
2327
|
+
payment_group_id: { type: "string", description: "The payment group this batch settled under (echoes --payment-group-id when supplied, else server-generated)." },
|
|
2328
|
+
status: { type: "string", description: "settled | partially_settled | failed | AUTHENTICATION_REQUIRED (3DS challenge body)." },
|
|
2329
|
+
currency: { type: "string", description: "ISO 4217 currency of the aggregate settlement (all orders share one currency)." },
|
|
2330
|
+
total_amount: { type: "float", description: "Aggregate total in DECIMAL units = sum of each order's authoritative price (computed by the platform, NOT sent by the caller)." },
|
|
2331
|
+
orders: {
|
|
2332
|
+
type: "array",
|
|
2333
|
+
description: "Per-order final result after upstream confirmation.",
|
|
2334
|
+
items: {
|
|
2335
|
+
order_id: { type: "string", description: "hho_/ffo_/rio_ order id." },
|
|
2336
|
+
status: { type: "string", description: "confirmed | refunded | failed." },
|
|
2337
|
+
amount: { type: "float", description: "That order's authoritative amount." },
|
|
2338
|
+
refunded_amount: { type: "float", description: "Amount refunded when upstream confirmation failed for this order." }
|
|
2339
|
+
}
|
|
2340
|
+
},
|
|
2341
|
+
three_ds_url: { type: "string", description: "[AUTHENTICATION_REQUIRED only] EVO 3DS challenge URL \u2014 complete out-of-band, then replay to settle." },
|
|
2342
|
+
merchant_trans_id: { type: "string", description: "[AUTHENTICATION_REQUIRED only] merchant trans id of the batch preauth." },
|
|
2343
|
+
amount: { type: "float", description: "[AUTHENTICATION_REQUIRED only] challenge amount = aggregate total." }
|
|
2344
|
+
},
|
|
2345
|
+
example: {
|
|
2346
|
+
command: "agenzo-merchant-cli orders pay-batch --order-ids hho_01K...,ffo_01K...,rio_01K... --payment-token-id ntk_01J2ABC --idempotency-key trip-pay-1",
|
|
2347
|
+
output_summary: "Settles all listed orders in one charge (banks/EVO see one transaction). Returns {payment_group_id, status, total_amount, currency, orders[]}. Use --watch to poll until settled / partially_settled / failed; upstream-confirmation-failed orders are refunded."
|
|
2348
|
+
},
|
|
2349
|
+
error_recovery: {
|
|
2350
|
+
PARAM_INVALID: "Fix --order-ids (a non-empty comma-separated list or JSON array of order ids) and ensure --idempotency-key is present, then retry.",
|
|
2351
|
+
ORDER_NOT_PAYABLE: "One of the order_ids is not in AWAITING_PAYMENT (the error names the specific order_id). Remove/replace it and retry with a NEW idempotency-key.",
|
|
2352
|
+
CURRENCY_MISMATCH: "The batch mixes currencies \u2014 orders in one pay-batch must share a single currency. Split by currency and settle each group separately.",
|
|
2353
|
+
TOKEN_ORDER_MISMATCH: "The token external_transaction_id is not bound to this payment_group. Mint a token for the aggregate total bound to THIS payment_group_id and retry; no funds moved.",
|
|
2354
|
+
AUTHENTICATION_REQUIRED: "EVO requires cardholder 3DS: open three_ds_url, finish authentication, then replay pay-batch with the SAME idempotency-key (or use --watch to poll automatically)."
|
|
2355
|
+
}
|
|
2356
|
+
};
|
|
2234
2357
|
function flightSchema(verb, description, flags, response, example, extra = {}) {
|
|
2235
2358
|
return { cli: CLI_NAME, noun: FLIGHT_NOUN, verb, description, flags, response, example, ...extra };
|
|
2236
2359
|
}
|
|
@@ -2272,30 +2395,38 @@ var flightListNationalitiesSchema = flightSchema(
|
|
|
2272
2395
|
);
|
|
2273
2396
|
var flightSearchSchema = flightSchema(
|
|
2274
2397
|
"search",
|
|
2275
|
-
"Search flights (one-way/round-trip/multi-city). journeys always carries all legs; journey-id
|
|
2398
|
+
"Search flights (one-way/round-trip/multi-city). journeys always carries all legs; for round-trip/multi-city relay the CHOSEN candidate's next_journey_ids into --journey-id until is_final_leg is true \u2014 only then do offers carry a real priceKey (product_token).",
|
|
2276
2399
|
{
|
|
2277
2400
|
"trip-type": { type: "int", required: true, description: "1=one-way, 2=round-trip, 3=multi-city." },
|
|
2278
|
-
journeys: { type: "json", required: true, description: "JSON array of {date, origin, destination, origin_type?, destination_type?}. origin/destination are IATA 3-letter codes; *_type 1=city, 2=airport." },
|
|
2401
|
+
journeys: { type: "json", required: true, description: "JSON array of {date, origin, destination, origin_type?, destination_type?}. origin/destination are IATA 3-letter codes; *_type 1=city, 2=airport. Carries ALL legs on every call, including relay calls." },
|
|
2279
2402
|
"cabin-class": { type: "string", required: false, default: "economy", description: "economy | premium_economy | business | first." },
|
|
2280
2403
|
"adult-num": { type: "int", required: false, default: 1, description: "Adults (1-9)." },
|
|
2281
2404
|
"child-num": { type: "int", required: false, default: 0, description: "Children (0-9)." },
|
|
2282
2405
|
"infant-num": { type: "int", required: false, default: 0, description: "Infants (0-9)." },
|
|
2283
2406
|
airline: { type: "string", required: false, description: "Airline 2-letter code filter." },
|
|
2284
2407
|
"transfer-number": { type: "int", required: false, default: 0, description: "0=any,1=direct,2=1 stop,3=2 stops." },
|
|
2285
|
-
"journey-id": { type: "json", required: false, description: "
|
|
2408
|
+
"journey-id": { type: "json", required: false, description: "Relay key: copy offers[].next_journey_ids VERBATIM from the candidate the user chose in the previous search. Omit on the first search. Do not build it from the top-level journey_ids, and never send more ids than there are legs (rejected)." }
|
|
2286
2409
|
},
|
|
2287
2410
|
{
|
|
2288
|
-
offers: { type: "array", description: "
|
|
2411
|
+
offers: { type: "array", description: "Candidates for the current leg. Each has next_journey_ids (relay key), journey_id, segment_ids, paired_segment_ids, total_sale_price (WHOLE-TRIP total), currency, cabin_class, baggage/refund/change_rules, and product_token when price_key_ready." },
|
|
2289
2412
|
price_key_ready: { type: "bool", description: "True only when all legs are selected (final search)." },
|
|
2290
|
-
|
|
2413
|
+
current_leg: { type: "int", description: "Which leg this result is choosing (1-based)." },
|
|
2414
|
+
total_legs: { type: "int", description: "Total legs in the trip." },
|
|
2415
|
+
is_final_leg: { type: "bool", description: "True when these offers can be bookable. False means relay another round with the chosen candidate's next_journey_ids." },
|
|
2416
|
+
journey_ids: { type: "array", description: "DEPRECATED for relay: flattened across all candidates, so it cannot tell you which id belongs to which. Relay offers[].next_journey_ids instead." }
|
|
2291
2417
|
},
|
|
2292
|
-
{ command: `agenzo-merchant-cli flight-flink search --trip-type 1 --journeys '[{"date":"2026-08-01","origin":"PEK","destination":"SHA"}]'`, output_summary: "Returns offers[]; use a product_token where price_key_ready is true, then verify." }
|
|
2418
|
+
{ command: `agenzo-merchant-cli flight-flink search --trip-type 1 --journeys '[{"date":"2026-08-01","origin":"PEK","destination":"SHA"}]'`, output_summary: "Returns offers[]; use a product_token where price_key_ready is true, then verify. Round-trip: the first search has is_final_leg=false and no product_token \u2014 relay the chosen candidate's next_journey_ids to get bookable offers." }
|
|
2293
2419
|
);
|
|
2294
2420
|
var flightMoreOffersSchema = flightSchema(
|
|
2295
2421
|
"more-offers",
|
|
2296
2422
|
"More fare offers for a priceKey (carried by product_token).",
|
|
2297
2423
|
{ "product-token": { type: "string", required: true, description: "Opaque token from search." } },
|
|
2298
|
-
{
|
|
2424
|
+
{
|
|
2425
|
+
offers: { type: "array", description: "Additional fare candidates for the same flight (same shape as search offers[]); all are bookable." },
|
|
2426
|
+
current_leg: { type: "int", description: "Always 1 \u2014 alternative fares for one chosen flight, not a relay round." },
|
|
2427
|
+
total_legs: { type: "int", description: "Always 1." },
|
|
2428
|
+
is_final_leg: { type: "bool", description: "Always true." }
|
|
2429
|
+
},
|
|
2299
2430
|
{ command: "agenzo-merchant-cli flight-flink more-offers --product-token pt_...", output_summary: "Additional fare offers." }
|
|
2300
2431
|
);
|
|
2301
2432
|
var flightVerifySchema = flightSchema(
|
|
@@ -2306,7 +2437,8 @@ var flightVerifySchema = flightSchema(
|
|
|
2306
2437
|
product_token: { type: "string", description: "Authoritative token to pass to create-order." },
|
|
2307
2438
|
total_price: { type: "int", description: "Verified total price (integer)." },
|
|
2308
2439
|
currency: { type: "string", description: "Currency code." },
|
|
2309
|
-
price_changed: { type: "bool", description: "True if the price changed since search; re-confirm with the user." }
|
|
2440
|
+
price_changed: { type: "bool", description: "True if the price changed since search; re-confirm with the user." },
|
|
2441
|
+
journeys: { type: "array", description: "The COMPLETE itinerary being priced \u2014 every leg, not just the last one relayed. Use it to show the full trip on a confirmation screen." }
|
|
2310
2442
|
},
|
|
2311
2443
|
{ command: "agenzo-merchant-cli flight-flink verify --product-token pt_...", output_summary: "Returns the authoritative product_token + price_changed." }
|
|
2312
2444
|
);
|
|
@@ -2349,7 +2481,10 @@ var flightPayOrderSchema = flightSchema(
|
|
|
2349
2481
|
"order-no": { type: "string", required: true, description: "Our order reference from create-order." },
|
|
2350
2482
|
"payment-method-id": { type: "string", required: false, description: "Optional bound-card id to charge (pay_per_call only; omit to use the default card)." },
|
|
2351
2483
|
"payment-token-id": { type: "string", required: false, description: "Optional network-token id; settles via direct charge instead of EVO preauth." },
|
|
2352
|
-
"authorized-merchant-trans-id": { type: "string", required: false, description: "Resume
|
|
2484
|
+
"authorized-merchant-trans-id": { type: "string", required: false, description: "Resume an EVO-card 3DS challenge with an already-authorised preauth trans id." },
|
|
2485
|
+
authenticate: { type: "bool", required: false, default: false, description: "Opt-in to require cardholder 3DS on the network-token direct charge (payment-token-id, EVO/Mastercard). On step-up the response is AUTHENTICATION_REQUIRED with three_ds_url + charge_no; resume via authorized-charge-no. Default off = frictionless." },
|
|
2486
|
+
"return-url": { type: "string", required: false, description: "Optional 3DS/passkey return URL for the network-token authenticate path; falls back to the platform EVO_3DS_RETURN_URL." },
|
|
2487
|
+
"authorized-charge-no": { type: "string", required: false, description: "Resume a network-token 3DS challenge: charge_no from a prior AUTHENTICATION_REQUIRED response (settled via resume_token), then triggers ticketing on success." },
|
|
2353
2488
|
"evo-explicit": { type: "bool", required: false, default: false, description: "[\u65B9\u6848 B/R16] Explicit opt-in to settle this pay_per_call order via the EVO bound-card fallback rail. Set true ONLY when the user explicitly chose EVO; forwarded to the pay body as evo_explicit=true. Without it, a pay_per_call settlement lacking --payment-token-id is hard-gated server-side." },
|
|
2354
2489
|
"idempotency-key": { type: "string", required: true, description: "Forwarded verbatim as the Idempotency-Key header." }
|
|
2355
2490
|
},
|
|
@@ -3012,6 +3147,17 @@ function formatPayOrder(data) {
|
|
|
3012
3147
|
lines.push(["Amount", `${data.price.amount} ${data.price.currency}`]);
|
|
3013
3148
|
if (data.price.quote_id) lines.push(["Quote ID", String(data.price.quote_id)]);
|
|
3014
3149
|
}
|
|
3150
|
+
if (data.charge_no) lines.push(["Charge no", String(data.charge_no)]);
|
|
3151
|
+
if (data.three_ds_url) lines.push(["3DS URL", String(data.three_ds_url)]);
|
|
3152
|
+
if (String(data.status) === "AUTHENTICATION_REQUIRED") {
|
|
3153
|
+
return [
|
|
3154
|
+
Formatter.keyValue(lines),
|
|
3155
|
+
Formatter.status(
|
|
3156
|
+
"warning",
|
|
3157
|
+
`Order ${orderRef} needs cardholder 3DS \u2014 open the 3DS URL, then resume pay-order with --authorized-charge-no ${data.charge_no ?? ""}.`
|
|
3158
|
+
)
|
|
3159
|
+
].join("\n\n");
|
|
3160
|
+
}
|
|
3015
3161
|
return [
|
|
3016
3162
|
Formatter.keyValue(lines),
|
|
3017
3163
|
Formatter.status(
|
|
@@ -3029,7 +3175,16 @@ function registerRidePayOrderCommand(parent, deps) {
|
|
|
3029
3175
|
"Bound-card id to charge via EVO preauth+capture (fallback path). Exactly one of --payment-token-id / --payment-method-id is required."
|
|
3030
3176
|
).option(
|
|
3031
3177
|
"--authorized-merchant-trans-id <id>",
|
|
3032
|
-
"Resume
|
|
3178
|
+
"Resume an EVO-card 3DS challenge: merchant trans id of an already-authorised preauth"
|
|
3179
|
+
).option(
|
|
3180
|
+
"--authenticate",
|
|
3181
|
+
"Opt-in to require cardholder 3DS on the network-token direct charge (--payment-token-id, EVO/Mastercard). When a step-up is needed the response is AUTHENTICATION_REQUIRED with three_ds_url + charge_no; resume with --authorized-charge-no. Default off = frictionless."
|
|
3182
|
+
).option(
|
|
3183
|
+
"--return-url <url>",
|
|
3184
|
+
"Optional 3DS/passkey return URL for the network-token authenticate path (falls back to the platform EVO_3DS_RETURN_URL)"
|
|
3185
|
+
).option(
|
|
3186
|
+
"--authorized-charge-no <no>",
|
|
3187
|
+
"Resume a network-token 3DS challenge: charge_no returned by a prior AUTHENTICATION_REQUIRED response (settled via ChargeService.resume_token). When set, the exactly-one-credential rule is relaxed (this is a continuation, not a fresh charge)."
|
|
3033
3188
|
).option(
|
|
3034
3189
|
"--evo-explicit",
|
|
3035
3190
|
"[\u65B9\u6848 B/R16] Explicit opt-in to settle this pay_per_call ride via the EVO bound-card fallback rail. Set ONLY when the user explicitly chose EVO; forwarded to the pay body as evo_explicit=true. Without it, a pay_per_call settlement lacking --payment-token-id is hard-gated server-side."
|
|
@@ -3045,7 +3200,10 @@ function registerRidePayOrderCommand(parent, deps) {
|
|
|
3045
3200
|
const orderId = need4(opts.orderId, "order-id");
|
|
3046
3201
|
const paymentTokenId = opts.paymentTokenId;
|
|
3047
3202
|
const paymentMethodId = opts.paymentMethodId;
|
|
3048
|
-
|
|
3203
|
+
const authorizedChargeNo = opts.authorizedChargeNo;
|
|
3204
|
+
if (authorizedChargeNo === void 0) {
|
|
3205
|
+
assertExactlyOneCredential(paymentTokenId, paymentMethodId);
|
|
3206
|
+
}
|
|
3049
3207
|
const apiKey = await PromptEngine.resolveInput(opts.apiKey, {
|
|
3050
3208
|
message: "API Key:",
|
|
3051
3209
|
type: "password"
|
|
@@ -3056,6 +3214,9 @@ function registerRidePayOrderCommand(parent, deps) {
|
|
|
3056
3214
|
if (opts.authorizedMerchantTransId !== void 0) {
|
|
3057
3215
|
body.authorized_merchant_trans_id = opts.authorizedMerchantTransId;
|
|
3058
3216
|
}
|
|
3217
|
+
if (opts.authenticate) body.authenticate = true;
|
|
3218
|
+
if (opts.returnUrl !== void 0) body.return_url = opts.returnUrl;
|
|
3219
|
+
if (authorizedChargeNo !== void 0) body.authorized_charge_no = authorizedChargeNo;
|
|
3059
3220
|
if (opts.evoExplicit) body.evo_explicit = true;
|
|
3060
3221
|
if (!isYes) {
|
|
3061
3222
|
const confirmed = await confirm3({
|
|
@@ -4002,15 +4163,201 @@ function registerOrdersGetCommand(parent, deps) {
|
|
|
4002
4163
|
});
|
|
4003
4164
|
}
|
|
4004
4165
|
|
|
4005
|
-
// src/
|
|
4166
|
+
// src/orders/pay-batch.ts
|
|
4167
|
+
import { confirm as confirm6 } from "@inquirer/prompts";
|
|
4168
|
+
var DEFAULT_PAY_BATCH_WATCH_INTERVAL_SECONDS = 5;
|
|
4169
|
+
var DEFAULT_PAY_BATCH_WATCH_TIMEOUT_SECONDS = 300;
|
|
4170
|
+
var PAY_BATCH_TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
4171
|
+
"settled",
|
|
4172
|
+
"partially_settled",
|
|
4173
|
+
"failed"
|
|
4174
|
+
]);
|
|
4175
|
+
function parseOrderIds(raw) {
|
|
4176
|
+
const INVALID = '--order-ids must be a comma-separated list OR a JSON array of order ids (e.g. "hho_1,ffo_2,rio_3" or ["hho_1","ffo_2"]).';
|
|
4177
|
+
const trimmed = raw.trim();
|
|
4178
|
+
let ids;
|
|
4179
|
+
if (trimmed.startsWith("[")) {
|
|
4180
|
+
let parsed;
|
|
4181
|
+
try {
|
|
4182
|
+
parsed = JSON.parse(trimmed);
|
|
4183
|
+
} catch {
|
|
4184
|
+
throw new CliError("PARAM_INVALID", INVALID);
|
|
4185
|
+
}
|
|
4186
|
+
if (!Array.isArray(parsed)) {
|
|
4187
|
+
throw new CliError("PARAM_INVALID", INVALID);
|
|
4188
|
+
}
|
|
4189
|
+
if (!parsed.every((el) => typeof el === "string")) {
|
|
4190
|
+
throw new CliError("PARAM_INVALID", INVALID);
|
|
4191
|
+
}
|
|
4192
|
+
ids = parsed.map((s) => s.trim());
|
|
4193
|
+
} else {
|
|
4194
|
+
ids = trimmed.split(",").map((s) => s.trim());
|
|
4195
|
+
}
|
|
4196
|
+
ids = ids.filter((s) => s.length > 0);
|
|
4197
|
+
if (ids.length === 0) {
|
|
4198
|
+
throw new CliError("PARAM_INVALID", INVALID);
|
|
4199
|
+
}
|
|
4200
|
+
return ids;
|
|
4201
|
+
}
|
|
4202
|
+
function formatPayBatch(data) {
|
|
4203
|
+
const lines = [
|
|
4204
|
+
["Payment group", String(data.payment_group_id ?? "-")],
|
|
4205
|
+
["Status", String(data.status ?? "-")]
|
|
4206
|
+
];
|
|
4207
|
+
if (data.total_amount != null && data.currency) {
|
|
4208
|
+
lines.push(["Total", `${data.total_amount} ${data.currency}`]);
|
|
4209
|
+
}
|
|
4210
|
+
if (data.status === "AUTHENTICATION_REQUIRED") {
|
|
4211
|
+
if (data.three_ds_url) lines.push(["3DS URL", String(data.three_ds_url)]);
|
|
4212
|
+
if (data.merchant_trans_id) lines.push(["Merchant trans ID", String(data.merchant_trans_id)]);
|
|
4213
|
+
if (data.amount != null && data.currency) {
|
|
4214
|
+
lines.push(["Challenge amount", `${data.amount} ${data.currency}`]);
|
|
4215
|
+
}
|
|
4216
|
+
}
|
|
4217
|
+
const out = [Formatter.keyValue(lines)];
|
|
4218
|
+
const perOrder = data.per_order ?? [];
|
|
4219
|
+
if (perOrder.length > 0) {
|
|
4220
|
+
const headers = ["Order ID", "Domain", "Status", "Amount", "Refunded"];
|
|
4221
|
+
const rows = perOrder.map((o) => [
|
|
4222
|
+
String(o.order_id ?? "-"),
|
|
4223
|
+
String(o.domain ?? "-"),
|
|
4224
|
+
String(o.status ?? "-"),
|
|
4225
|
+
String(o.amount ?? "-"),
|
|
4226
|
+
String(o.refunded_amount ?? "-")
|
|
4227
|
+
]);
|
|
4228
|
+
out.push("", Formatter.table(headers, rows));
|
|
4229
|
+
}
|
|
4230
|
+
return out.join("\n");
|
|
4231
|
+
}
|
|
4232
|
+
function isBatchTerminal(record) {
|
|
4233
|
+
const status = record.status;
|
|
4234
|
+
return typeof status === "string" && PAY_BATCH_TERMINAL_STATUSES.has(status);
|
|
4235
|
+
}
|
|
4236
|
+
function registerOrdersPayBatchCommand(parent, deps) {
|
|
4237
|
+
const cmd = parent.command("pay-batch").description(
|
|
4238
|
+
"Settle a batch of orders (may mix ride/hotel/flight) in one aggregate payment via a single payment credential"
|
|
4239
|
+
).option("--api-key <key>", "API Key for authentication (X-Api-Key)").option(
|
|
4240
|
+
"--order-ids <ids>",
|
|
4241
|
+
"Ordered order ids to settle together \u2014 comma-separated (hho_1,ffo_2,rio_3) or a JSON array. May mix hho_/ffo_/rio_ domains. Amounts come from each order's locked authoritative price (not passed here)."
|
|
4242
|
+
).option(
|
|
4243
|
+
"--payment-token-id <id>",
|
|
4244
|
+
"[\u8F68 A] Network-token id minted for the aggregate total and bound to this payment_group (unionpay/visa charge path)"
|
|
4245
|
+
).option(
|
|
4246
|
+
"--payment-method-id <id>",
|
|
4247
|
+
"[\u8F68 B] EVO bound-card id to settle the aggregate total (one 3DS challenge)"
|
|
4248
|
+
).option(
|
|
4249
|
+
"--evo-explicit",
|
|
4250
|
+
"[\u8F68 B \u786C\u95E8\u69DB] Explicit opt-in to settle via the EVO bound-card rail. Set ONLY when the user explicitly chose EVO; forwarded to the body as evo_explicit=true."
|
|
4251
|
+
).option(
|
|
4252
|
+
"--payment-group-id <id>",
|
|
4253
|
+
"Optional caller-pre-generated payment group id; omit to let the platform generate one"
|
|
4254
|
+
).option("--member <id>", MEMBER_OPTION_DESCRIPTION).option(
|
|
4255
|
+
"--idempotency-key <key>",
|
|
4256
|
+
"Idempotency key forwarded verbatim as the Idempotency-Key header"
|
|
4257
|
+
).option("--watch", "Poll until a terminal batch status or timeout (NDJSON output)", false).option(
|
|
4258
|
+
"--watch-interval <seconds>",
|
|
4259
|
+
"Seconds between polls when --watch is set",
|
|
4260
|
+
String(DEFAULT_PAY_BATCH_WATCH_INTERVAL_SECONDS)
|
|
4261
|
+
).option(
|
|
4262
|
+
"--watch-timeout <seconds>",
|
|
4263
|
+
"Max seconds to poll before giving up",
|
|
4264
|
+
String(DEFAULT_PAY_BATCH_WATCH_TIMEOUT_SECONDS)
|
|
4265
|
+
);
|
|
4266
|
+
attachSchemaHelp(cmd, ordersPayBatchSchema);
|
|
4267
|
+
cmd.action(async () => {
|
|
4268
|
+
const opts = cmd.optsWithGlobals();
|
|
4269
|
+
const format = resolveFormat(opts.format);
|
|
4270
|
+
const isYes = Boolean(opts.yes);
|
|
4271
|
+
const apiKey = await PromptEngine.resolveInput(opts.apiKey, {
|
|
4272
|
+
message: "API Key:",
|
|
4273
|
+
type: "password"
|
|
4274
|
+
});
|
|
4275
|
+
const orderIds = parseOrderIds(need9(opts.orderIds, "order-ids"));
|
|
4276
|
+
const idempotencyKey = await resolveIdempotencyKey(opts.idempotencyKey, {
|
|
4277
|
+
yes: isYes,
|
|
4278
|
+
commandPath: "orders pay-batch"
|
|
4279
|
+
});
|
|
4280
|
+
const watchEnabled = Boolean(opts.watch);
|
|
4281
|
+
const watchInterval = resolveSeconds(opts.watchInterval, DEFAULT_PAY_BATCH_WATCH_INTERVAL_SECONDS);
|
|
4282
|
+
const watchTimeout = resolveSeconds(opts.watchTimeout, DEFAULT_PAY_BATCH_WATCH_TIMEOUT_SECONDS);
|
|
4283
|
+
const body = { order_ids: orderIds };
|
|
4284
|
+
if (opts.paymentTokenId !== void 0) body.payment_token_id = opts.paymentTokenId;
|
|
4285
|
+
if (opts.paymentMethodId !== void 0) body.payment_method_id = opts.paymentMethodId;
|
|
4286
|
+
if (opts.evoExplicit) body.evo_explicit = true;
|
|
4287
|
+
if (opts.paymentGroupId !== void 0) body.payment_group_id = opts.paymentGroupId;
|
|
4288
|
+
const member = memberIdOf(opts);
|
|
4289
|
+
if (member !== void 0) body.member_id = member;
|
|
4290
|
+
if (!isYes) {
|
|
4291
|
+
const confirmed = await confirm6({
|
|
4292
|
+
message: `Settle ${orderIds.length} order(s) in one aggregate payment? This moves money \u2014 a single charge covers the authoritative total of all listed orders (banks/EVO see one transaction).`,
|
|
4293
|
+
default: false
|
|
4294
|
+
});
|
|
4295
|
+
if (!confirmed) {
|
|
4296
|
+
throw new CliError("CLIENT_ABORTED", "Aggregate payment aborted by user.");
|
|
4297
|
+
}
|
|
4298
|
+
}
|
|
4299
|
+
const path = "/orders/pay-batch";
|
|
4300
|
+
const headers = { "Idempotency-Key": idempotencyKey };
|
|
4301
|
+
const auth = { type: "api-key", key: apiKey };
|
|
4302
|
+
if (!watchEnabled) {
|
|
4303
|
+
const spinner = format === "json" ? null : createSpinner("Settling order batch...");
|
|
4304
|
+
const result = await deps.apiClient.post(path, auth, body, headers);
|
|
4305
|
+
spinner?.stop();
|
|
4306
|
+
if (!result.success) {
|
|
4307
|
+
throw CliError.fromApi(result, { auth: "api-key" });
|
|
4308
|
+
}
|
|
4309
|
+
const data = result.data;
|
|
4310
|
+
const configManager = new ConfigManager();
|
|
4311
|
+
const commandResult = {
|
|
4312
|
+
data,
|
|
4313
|
+
text: () => formatPayBatch(data)
|
|
4314
|
+
};
|
|
4315
|
+
await renderWithContext(commandResult, { format }, configManager);
|
|
4316
|
+
} else {
|
|
4317
|
+
const deadline = Date.now() + watchTimeout * 1e3;
|
|
4318
|
+
let lastRecord = null;
|
|
4319
|
+
for (; ; ) {
|
|
4320
|
+
const result = await deps.apiClient.post(path, auth, body, headers);
|
|
4321
|
+
if (result.success) {
|
|
4322
|
+
const data = result.data;
|
|
4323
|
+
lastRecord = data;
|
|
4324
|
+
ndjsonWriteLine(data);
|
|
4325
|
+
if (isBatchTerminal(data)) {
|
|
4326
|
+
return;
|
|
4327
|
+
}
|
|
4328
|
+
} else {
|
|
4329
|
+
throw CliError.fromApi(result, { auth: "api-key" });
|
|
4330
|
+
}
|
|
4331
|
+
if (Date.now() + watchInterval * 1e3 >= deadline) {
|
|
4332
|
+
const timeoutLine = {
|
|
4333
|
+
watch_status: "timeout",
|
|
4334
|
+
message: `Polling stopped after ${watchTimeout}s without reaching a terminal batch status.`,
|
|
4335
|
+
last_status: lastRecord
|
|
4336
|
+
};
|
|
4337
|
+
ndjsonWriteLine(timeoutLine);
|
|
4338
|
+
return;
|
|
4339
|
+
}
|
|
4340
|
+
await realSleep(watchInterval * 1e3);
|
|
4341
|
+
}
|
|
4342
|
+
}
|
|
4343
|
+
});
|
|
4344
|
+
}
|
|
4006
4345
|
function need9(value, flag) {
|
|
4007
4346
|
if (value === void 0) {
|
|
4008
4347
|
throw new CliError("PARAM_INVALID", `Missing required --${flag}.`);
|
|
4009
4348
|
}
|
|
4010
4349
|
return value;
|
|
4011
4350
|
}
|
|
4351
|
+
|
|
4352
|
+
// src/hotel-redaug/search.ts
|
|
4353
|
+
function need10(value, flag) {
|
|
4354
|
+
if (value === void 0) {
|
|
4355
|
+
throw new CliError("PARAM_INVALID", `Missing required --${flag}.`);
|
|
4356
|
+
}
|
|
4357
|
+
return value;
|
|
4358
|
+
}
|
|
4012
4359
|
function num5(value, flag) {
|
|
4013
|
-
const n = Number(
|
|
4360
|
+
const n = Number(need10(value, flag));
|
|
4014
4361
|
if (!Number.isFinite(n)) {
|
|
4015
4362
|
throw new CliError("PARAM_INVALID", `--${flag} must be a number.`);
|
|
4016
4363
|
}
|
|
@@ -4032,7 +4379,7 @@ function positiveInt3(value, flag) {
|
|
|
4032
4379
|
}
|
|
4033
4380
|
var YMD_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
4034
4381
|
function stayDate(value, flag) {
|
|
4035
|
-
const raw =
|
|
4382
|
+
const raw = need10(value, flag);
|
|
4036
4383
|
if (!YMD_RE.test(raw)) {
|
|
4037
4384
|
throw new CliError("PARAM_INVALID", `--${flag} must be a YYYY-MM-DD date.`);
|
|
4038
4385
|
}
|
|
@@ -4104,7 +4451,7 @@ function registerHotelSearchCommand(parent, deps) {
|
|
|
4104
4451
|
const pageSize = positiveInt3(opts.pageSize, "page-size");
|
|
4105
4452
|
const body = {};
|
|
4106
4453
|
if (hasDestination) {
|
|
4107
|
-
body.destination_id =
|
|
4454
|
+
body.destination_id = need10(opts.destinationId, "destination-id");
|
|
4108
4455
|
} else {
|
|
4109
4456
|
body.lat = coord(opts.lat, "lat", -90, 90);
|
|
4110
4457
|
body.lng = coord(opts.lng, "lng", -180, 180);
|
|
@@ -4147,14 +4494,14 @@ function registerHotelSearchCommand(parent, deps) {
|
|
|
4147
4494
|
}
|
|
4148
4495
|
|
|
4149
4496
|
// src/hotel-redaug/quote.ts
|
|
4150
|
-
function
|
|
4497
|
+
function need11(value, flag) {
|
|
4151
4498
|
if (value === void 0) {
|
|
4152
4499
|
throw new CliError("PARAM_INVALID", `Missing required --${flag}.`);
|
|
4153
4500
|
}
|
|
4154
4501
|
return value;
|
|
4155
4502
|
}
|
|
4156
4503
|
function num6(value, flag) {
|
|
4157
|
-
const n = Number(
|
|
4504
|
+
const n = Number(need11(value, flag));
|
|
4158
4505
|
if (!Number.isFinite(n)) {
|
|
4159
4506
|
throw new CliError("PARAM_INVALID", `--${flag} must be a number.`);
|
|
4160
4507
|
}
|
|
@@ -4162,7 +4509,7 @@ function num6(value, flag) {
|
|
|
4162
4509
|
}
|
|
4163
4510
|
var YMD_RE2 = /^\d{4}-\d{2}-\d{2}$/;
|
|
4164
4511
|
function stayDate2(value, flag) {
|
|
4165
|
-
const raw =
|
|
4512
|
+
const raw = need11(value, flag);
|
|
4166
4513
|
if (!YMD_RE2.test(raw)) {
|
|
4167
4514
|
throw new CliError("PARAM_INVALID", `--${flag} must be a YYYY-MM-DD date.`);
|
|
4168
4515
|
}
|
|
@@ -4202,7 +4549,7 @@ function registerHotelQuoteCommand(parent, deps) {
|
|
|
4202
4549
|
message: "API Key:",
|
|
4203
4550
|
type: "password"
|
|
4204
4551
|
});
|
|
4205
|
-
const hotelId =
|
|
4552
|
+
const hotelId = need11(opts.hotelId, "hotel-id");
|
|
4206
4553
|
const checkIn = stayDate2(opts.checkIn, "check-in");
|
|
4207
4554
|
const checkOut = stayDate2(opts.checkOut, "check-out");
|
|
4208
4555
|
if (!(checkOut > checkIn)) {
|
|
@@ -4236,7 +4583,7 @@ function registerHotelQuoteCommand(parent, deps) {
|
|
|
4236
4583
|
}
|
|
4237
4584
|
|
|
4238
4585
|
// src/hotel-redaug/create-order.ts
|
|
4239
|
-
import { confirm as
|
|
4586
|
+
import { confirm as confirm7 } from "@inquirer/prompts";
|
|
4240
4587
|
function parsePriceItems(raw) {
|
|
4241
4588
|
const INVALID = "--price-items must be a JSON array of {sale_date (string), sale_price (number), breakfast_num (number)}.";
|
|
4242
4589
|
let parsed;
|
|
@@ -4294,14 +4641,14 @@ function parseCancellation(raw) {
|
|
|
4294
4641
|
}
|
|
4295
4642
|
return parsed;
|
|
4296
4643
|
}
|
|
4297
|
-
function
|
|
4644
|
+
function need12(value, flag) {
|
|
4298
4645
|
if (value === void 0) {
|
|
4299
4646
|
throw new CliError("PARAM_INVALID", `Missing required --${flag}.`);
|
|
4300
4647
|
}
|
|
4301
4648
|
return value;
|
|
4302
4649
|
}
|
|
4303
4650
|
function num7(value, flag) {
|
|
4304
|
-
const n = Number(
|
|
4651
|
+
const n = Number(need12(value, flag));
|
|
4305
4652
|
if (!Number.isFinite(n)) {
|
|
4306
4653
|
throw new CliError("PARAM_INVALID", `--${flag} must be a number.`);
|
|
4307
4654
|
}
|
|
@@ -4309,7 +4656,7 @@ function num7(value, flag) {
|
|
|
4309
4656
|
}
|
|
4310
4657
|
var YMD_RE3 = /^\d{4}-\d{2}-\d{2}$/;
|
|
4311
4658
|
function stayDate3(value, flag) {
|
|
4312
|
-
const raw =
|
|
4659
|
+
const raw = need12(value, flag);
|
|
4313
4660
|
if (!YMD_RE3.test(raw)) {
|
|
4314
4661
|
throw new CliError("PARAM_INVALID", `--${flag} must be a YYYY-MM-DD date.`);
|
|
4315
4662
|
}
|
|
@@ -4378,18 +4725,18 @@ function registerHotelCreateOrderCommand(parent, deps) {
|
|
|
4378
4725
|
message: "API Key:",
|
|
4379
4726
|
type: "password"
|
|
4380
4727
|
});
|
|
4381
|
-
const productToken =
|
|
4728
|
+
const productToken = need12(opts.productToken, "product-token");
|
|
4382
4729
|
const totalAmount = num7(opts.totalAmount, "total-amount");
|
|
4383
|
-
const currency =
|
|
4384
|
-
const priceItems = parsePriceItems(
|
|
4730
|
+
const currency = need12(opts.currency, "currency");
|
|
4731
|
+
const priceItems = parsePriceItems(need12(opts.priceItems, "price-items"));
|
|
4385
4732
|
const checkIn = stayDate3(opts.checkIn, "check-in");
|
|
4386
4733
|
const checkOut = stayDate3(opts.checkOut, "check-out");
|
|
4387
4734
|
if (!(checkOut > checkIn)) {
|
|
4388
4735
|
throw new CliError("PARAM_INVALID", "--check-out must be strictly after --check-in.");
|
|
4389
4736
|
}
|
|
4390
|
-
const guestName =
|
|
4391
|
-
const contactName =
|
|
4392
|
-
const contactPhone =
|
|
4737
|
+
const guestName = need12(opts.guestName, "guest-name");
|
|
4738
|
+
const contactName = need12(opts.contactName, "contact-name");
|
|
4739
|
+
const contactPhone = need12(opts.contactPhone, "contact-phone");
|
|
4393
4740
|
const body = {
|
|
4394
4741
|
product_token: productToken,
|
|
4395
4742
|
total_amount: totalAmount,
|
|
@@ -4423,7 +4770,7 @@ function registerHotelCreateOrderCommand(parent, deps) {
|
|
|
4423
4770
|
if (member !== void 0) body.member_id = member;
|
|
4424
4771
|
if (!isYes) {
|
|
4425
4772
|
const chargeNote = opts.paymentTokenId !== void 0 ? "Payment has already been captured via UPI Agent Pay; this only locks the room." : "This locks the rate but does NOT charge anything yet.";
|
|
4426
|
-
const confirmed = await
|
|
4773
|
+
const confirmed = await confirm7({
|
|
4427
4774
|
message: `Create this hotel order for ${totalAmount} ${currency} (check-in ${checkIn}, check-out ${checkOut})? ${chargeNote}`,
|
|
4428
4775
|
default: false
|
|
4429
4776
|
});
|
|
@@ -4457,7 +4804,7 @@ function registerHotelCreateOrderCommand(parent, deps) {
|
|
|
4457
4804
|
}
|
|
4458
4805
|
|
|
4459
4806
|
// src/hotel-redaug/pay-order.ts
|
|
4460
|
-
import { confirm as
|
|
4807
|
+
import { confirm as confirm8 } from "@inquirer/prompts";
|
|
4461
4808
|
|
|
4462
4809
|
// src/hotel-redaug/watch.ts
|
|
4463
4810
|
var DEFAULT_ORDER_WATCH_INTERVAL_SECONDS = 5;
|
|
@@ -4587,11 +4934,14 @@ function formatPayOrder2(data) {
|
|
|
4587
4934
|
];
|
|
4588
4935
|
if (data.settlement_path) lines.push(["Settlement path", String(data.settlement_path)]);
|
|
4589
4936
|
if (data.pay_status) lines.push(["Pay status", String(data.pay_status)]);
|
|
4937
|
+
if (data.status) lines.push(["Status", String(data.status)]);
|
|
4590
4938
|
if (data.total_amount != null && data.currency) {
|
|
4591
4939
|
lines.push(["Amount", `${data.total_amount} ${data.currency}`]);
|
|
4592
4940
|
}
|
|
4593
4941
|
if (data.billing_entry_id) lines.push(["Billing entry", String(data.billing_entry_id)]);
|
|
4594
4942
|
if (data.merchant_trans_id) lines.push(["Merchant trans ID", String(data.merchant_trans_id)]);
|
|
4943
|
+
if (data.charge_no) lines.push(["Charge no", String(data.charge_no)]);
|
|
4944
|
+
if (data.three_ds_url) lines.push(["3DS URL", String(data.three_ds_url)]);
|
|
4595
4945
|
return Formatter.keyValue(lines);
|
|
4596
4946
|
}
|
|
4597
4947
|
function isPaymentTerminal(record) {
|
|
@@ -4601,7 +4951,16 @@ function isPaymentTerminal(record) {
|
|
|
4601
4951
|
function registerHotelPayOrderCommand(parent, deps) {
|
|
4602
4952
|
const cmd = parent.command("pay-order").description("Settle an existing hotel order (path decided by billing_mode: monthly_settlement or pay_per_call)").option("--api-key <key>", "API Key for authentication (X-Api-Key)").option("--order-id <id>", "Order ID to pay (from create-order response)").option("--payment-method-id <id>", "Optional bound-card id to charge (pay_per_call / EVO preauth)").option("--payment-token-id <id>", "Optional network-token id (unionpay/visa charge path)").option(
|
|
4603
4953
|
"--authorized-merchant-trans-id <id>",
|
|
4604
|
-
"Resume
|
|
4954
|
+
"Resume an EVO-card 3DS challenge: merchant trans id of an already-authorised preauth"
|
|
4955
|
+
).option(
|
|
4956
|
+
"--authenticate",
|
|
4957
|
+
"Opt-in to require cardholder 3DS on the network-token direct charge (--payment-token-id, EVO/Mastercard). When a step-up is needed the response is AUTHENTICATION_REQUIRED with three_ds_url + charge_no; resume with --authorized-charge-no. Default off = frictionless."
|
|
4958
|
+
).option(
|
|
4959
|
+
"--return-url <url>",
|
|
4960
|
+
"Optional 3DS/passkey return URL for the network-token authenticate path (falls back to the platform EVO_3DS_RETURN_URL)"
|
|
4961
|
+
).option(
|
|
4962
|
+
"--authorized-charge-no <no>",
|
|
4963
|
+
"Resume a network-token 3DS challenge: charge_no returned by a prior AUTHENTICATION_REQUIRED response (settled via ChargeService.resume_token)"
|
|
4605
4964
|
).option(
|
|
4606
4965
|
"--evo-explicit",
|
|
4607
4966
|
"[\u65B9\u6848 B/R16] Explicit opt-in to settle this pay_per_call order via the EVO bound-card fallback rail. Set ONLY when the user explicitly chose EVO; forwarded to the pay body as evo_explicit=true. Without it, a pay_per_call settlement lacking --payment-token-id is hard-gated server-side."
|
|
@@ -4643,9 +5002,14 @@ function registerHotelPayOrderCommand(parent, deps) {
|
|
|
4643
5002
|
if (opts.authorizedMerchantTransId !== void 0) {
|
|
4644
5003
|
body.authorized_merchant_trans_id = opts.authorizedMerchantTransId;
|
|
4645
5004
|
}
|
|
5005
|
+
if (opts.authenticate) body.authenticate = true;
|
|
5006
|
+
if (opts.returnUrl !== void 0) body.return_url = opts.returnUrl;
|
|
5007
|
+
if (opts.authorizedChargeNo !== void 0) {
|
|
5008
|
+
body.authorized_charge_no = opts.authorizedChargeNo;
|
|
5009
|
+
}
|
|
4646
5010
|
if (opts.evoExplicit) body.evo_explicit = true;
|
|
4647
5011
|
if (!isYes) {
|
|
4648
|
-
const confirmed = await
|
|
5012
|
+
const confirmed = await confirm8({
|
|
4649
5013
|
message: `Settle hotel order ${orderId}? This moves money via the order's configured billing mode (monthly_settlement or pay_per_call).`,
|
|
4650
5014
|
default: false
|
|
4651
5015
|
});
|
|
@@ -4723,7 +5087,7 @@ function registerHotelPayOrderCommand(parent, deps) {
|
|
|
4723
5087
|
}
|
|
4724
5088
|
|
|
4725
5089
|
// src/hotel-redaug/get.ts
|
|
4726
|
-
function
|
|
5090
|
+
function need13(value, flag) {
|
|
4727
5091
|
if (value === void 0) {
|
|
4728
5092
|
throw new CliError("PARAM_INVALID", `Missing required --${flag}.`);
|
|
4729
5093
|
}
|
|
@@ -4787,7 +5151,7 @@ function registerHotelGetCommand(parent, deps) {
|
|
|
4787
5151
|
message: "API Key:",
|
|
4788
5152
|
type: "password"
|
|
4789
5153
|
});
|
|
4790
|
-
const orderId =
|
|
5154
|
+
const orderId = need13(opts.orderId, "order-id");
|
|
4791
5155
|
if (opts.watch) {
|
|
4792
5156
|
await watchOrderStatus(deps.apiClient, apiKey, orderId, {
|
|
4793
5157
|
intervalSeconds: resolveSeconds(
|
|
@@ -4821,7 +5185,7 @@ function registerHotelGetCommand(parent, deps) {
|
|
|
4821
5185
|
}
|
|
4822
5186
|
|
|
4823
5187
|
// src/hotel-redaug/cancel.ts
|
|
4824
|
-
function
|
|
5188
|
+
function need14(value, flag) {
|
|
4825
5189
|
if (value === void 0) {
|
|
4826
5190
|
throw new CliError("PARAM_INVALID", `Missing required --${flag}.`);
|
|
4827
5191
|
}
|
|
@@ -4874,8 +5238,8 @@ function registerHotelCancelCommand(parent, deps) {
|
|
|
4874
5238
|
message: "API Key:",
|
|
4875
5239
|
type: "password"
|
|
4876
5240
|
});
|
|
4877
|
-
const orderId =
|
|
4878
|
-
const fcOrderCode =
|
|
5241
|
+
const orderId = need14(opts.orderId, "order-id");
|
|
5242
|
+
const fcOrderCode = need14(opts.fcOrderCode, "fc-order-code");
|
|
4879
5243
|
const body = { fc_order_code: fcOrderCode };
|
|
4880
5244
|
if (opts.reason !== void 0) body.reason = opts.reason;
|
|
4881
5245
|
const idempotencyKey = await resolveIdempotencyKey(opts.idempotencyKey, {
|
|
@@ -4904,15 +5268,15 @@ function registerHotelCancelCommand(parent, deps) {
|
|
|
4904
5268
|
}
|
|
4905
5269
|
|
|
4906
5270
|
// src/hotel-redaug/checkout.ts
|
|
4907
|
-
import { confirm as
|
|
4908
|
-
function
|
|
5271
|
+
import { confirm as confirm9 } from "@inquirer/prompts";
|
|
5272
|
+
function need15(value, flag) {
|
|
4909
5273
|
if (value === void 0) {
|
|
4910
5274
|
throw new CliError("PARAM_INVALID", `Missing required --${flag}.`);
|
|
4911
5275
|
}
|
|
4912
5276
|
return value;
|
|
4913
5277
|
}
|
|
4914
5278
|
function num8(value, flag) {
|
|
4915
|
-
const n = Number(
|
|
5279
|
+
const n = Number(need15(value, flag));
|
|
4916
5280
|
if (!Number.isFinite(n)) {
|
|
4917
5281
|
throw new CliError("PARAM_INVALID", `--${flag} must be a number.`);
|
|
4918
5282
|
}
|
|
@@ -4978,10 +5342,10 @@ function registerHotelCheckoutCommand(parent, deps) {
|
|
|
4978
5342
|
message: "API Key:",
|
|
4979
5343
|
type: "password"
|
|
4980
5344
|
});
|
|
4981
|
-
const orderId =
|
|
4982
|
-
const fcOrderCode =
|
|
4983
|
-
const reason =
|
|
4984
|
-
const checkoutRooms = parseCheckoutRooms(
|
|
5345
|
+
const orderId = need15(opts.orderId, "order-id");
|
|
5346
|
+
const fcOrderCode = need15(opts.fcOrderCode, "fc-order-code");
|
|
5347
|
+
const reason = need15(opts.reason, "reason");
|
|
5348
|
+
const checkoutRooms = parseCheckoutRooms(need15(opts.checkoutRooms, "checkout-rooms"));
|
|
4985
5349
|
const refundType = num8(opts.refundType, "refund-type");
|
|
4986
5350
|
const body = {
|
|
4987
5351
|
fc_order_code: fcOrderCode,
|
|
@@ -4990,7 +5354,7 @@ function registerHotelCheckoutCommand(parent, deps) {
|
|
|
4990
5354
|
checkout_rooms: checkoutRooms
|
|
4991
5355
|
};
|
|
4992
5356
|
if (!isYes) {
|
|
4993
|
-
const confirmed = await
|
|
5357
|
+
const confirmed = await confirm9({
|
|
4994
5358
|
message: `Request check-out / out-of-policy cancellation for order ${orderId} (${checkoutRooms.length} room(s))? The property must approve and charges may apply.`,
|
|
4995
5359
|
default: false
|
|
4996
5360
|
});
|
|
@@ -5024,7 +5388,7 @@ function registerHotelCheckoutCommand(parent, deps) {
|
|
|
5024
5388
|
}
|
|
5025
5389
|
|
|
5026
5390
|
// src/hotel-redaug/get-checkout.ts
|
|
5027
|
-
function
|
|
5391
|
+
function need16(value, flag) {
|
|
5028
5392
|
if (value === void 0) {
|
|
5029
5393
|
throw new CliError("PARAM_INVALID", `Missing required --${flag}.`);
|
|
5030
5394
|
}
|
|
@@ -5060,7 +5424,7 @@ function registerHotelGetCheckoutCommand(parent, deps) {
|
|
|
5060
5424
|
message: "API Key:",
|
|
5061
5425
|
type: "password"
|
|
5062
5426
|
});
|
|
5063
|
-
const taskOrderCode =
|
|
5427
|
+
const taskOrderCode = need16(opts.taskOrderCode, "task-order-code");
|
|
5064
5428
|
if (opts.watch) {
|
|
5065
5429
|
await watchCheckoutStatus(deps.apiClient, apiKey, taskOrderCode, {
|
|
5066
5430
|
intervalSeconds: resolveSeconds(
|
|
@@ -5164,7 +5528,7 @@ function registerHotelListOrdersCommand(parent, deps) {
|
|
|
5164
5528
|
}
|
|
5165
5529
|
|
|
5166
5530
|
// src/hotel-redaug/find-destination.ts
|
|
5167
|
-
function
|
|
5531
|
+
function need17(value, flag) {
|
|
5168
5532
|
if (value === void 0 || value.trim() === "") {
|
|
5169
5533
|
throw new CliError("PARAM_INVALID", `Missing required --${flag}.`);
|
|
5170
5534
|
}
|
|
@@ -5198,7 +5562,7 @@ function registerHotelFindDestinationCommand(parent, deps) {
|
|
|
5198
5562
|
message: "API Key:",
|
|
5199
5563
|
type: "password"
|
|
5200
5564
|
});
|
|
5201
|
-
const keyword =
|
|
5565
|
+
const keyword = need17(opts.keyword, "keyword");
|
|
5202
5566
|
const body = { keyword };
|
|
5203
5567
|
if (opts.dataType !== void 0) {
|
|
5204
5568
|
body.data_type = opts.dataType;
|
|
@@ -5222,14 +5586,14 @@ function registerHotelFindDestinationCommand(parent, deps) {
|
|
|
5222
5586
|
}
|
|
5223
5587
|
|
|
5224
5588
|
// src/hotel-redaug/hotel-filters.ts
|
|
5225
|
-
function
|
|
5589
|
+
function need18(value, flag) {
|
|
5226
5590
|
if (value === void 0 || value.trim() === "") {
|
|
5227
5591
|
throw new CliError("PARAM_INVALID", `Missing required --${flag}.`);
|
|
5228
5592
|
}
|
|
5229
5593
|
return value;
|
|
5230
5594
|
}
|
|
5231
5595
|
function num9(value, flag) {
|
|
5232
|
-
const n = Number(
|
|
5596
|
+
const n = Number(need18(value, flag));
|
|
5233
5597
|
if (!Number.isFinite(n)) {
|
|
5234
5598
|
throw new CliError("PARAM_INVALID", `--${flag} must be a number.`);
|
|
5235
5599
|
}
|
|
@@ -5309,7 +5673,7 @@ function registerHotelFiltersCommand(parent, deps) {
|
|
|
5309
5673
|
}
|
|
5310
5674
|
const body = {};
|
|
5311
5675
|
if (hasDestination) {
|
|
5312
|
-
body.destination_id =
|
|
5676
|
+
body.destination_id = need18(opts.destinationId, "destination-id");
|
|
5313
5677
|
} else {
|
|
5314
5678
|
body.lat = coord2(opts.lat, "lat", -90, 90);
|
|
5315
5679
|
body.lng = coord2(opts.lng, "lng", -180, 180);
|
|
@@ -5334,7 +5698,7 @@ function registerHotelFiltersCommand(parent, deps) {
|
|
|
5334
5698
|
}
|
|
5335
5699
|
|
|
5336
5700
|
// src/hotel-redaug/list-cities.ts
|
|
5337
|
-
function
|
|
5701
|
+
function need19(value, flag) {
|
|
5338
5702
|
if (value === void 0 || value.trim() === "") {
|
|
5339
5703
|
throw new CliError("PARAM_INVALID", `Missing required --${flag}.`);
|
|
5340
5704
|
}
|
|
@@ -5367,7 +5731,7 @@ function registerHotelListCitiesCommand(parent, deps) {
|
|
|
5367
5731
|
message: "API Key:",
|
|
5368
5732
|
type: "password"
|
|
5369
5733
|
});
|
|
5370
|
-
const country =
|
|
5734
|
+
const country = need19(opts.country, "country");
|
|
5371
5735
|
const body = { country_code: country };
|
|
5372
5736
|
const result = await deps.apiClient.post(
|
|
5373
5737
|
"/hotel/cities",
|
|
@@ -5388,7 +5752,7 @@ function registerHotelListCitiesCommand(parent, deps) {
|
|
|
5388
5752
|
}
|
|
5389
5753
|
|
|
5390
5754
|
// src/hotel-redaug/hotel-detail.ts
|
|
5391
|
-
function
|
|
5755
|
+
function need20(value, flag) {
|
|
5392
5756
|
if (value === void 0 || value.trim() === "") {
|
|
5393
5757
|
throw new CliError("PARAM_INVALID", `Missing required --${flag}.`);
|
|
5394
5758
|
}
|
|
@@ -5535,7 +5899,7 @@ function registerHotelDetailCommand(parent, deps) {
|
|
|
5535
5899
|
message: "API Key:",
|
|
5536
5900
|
type: "password"
|
|
5537
5901
|
});
|
|
5538
|
-
const hotelId =
|
|
5902
|
+
const hotelId = need20(opts.hotelId, "hotel-id");
|
|
5539
5903
|
const rawWithImages = opts.withImages;
|
|
5540
5904
|
const withImages = rawWithImages !== "false" && rawWithImages !== false;
|
|
5541
5905
|
const settings = parseSettings(opts.settings);
|
|
@@ -5743,14 +6107,14 @@ function registerHotelSkillCommand(parent) {
|
|
|
5743
6107
|
}
|
|
5744
6108
|
|
|
5745
6109
|
// src/flight-flink/_helpers.ts
|
|
5746
|
-
function
|
|
6110
|
+
function need21(value, flag) {
|
|
5747
6111
|
if (value === void 0) {
|
|
5748
6112
|
throw new CliError("PARAM_INVALID", `Missing required --${flag}.`);
|
|
5749
6113
|
}
|
|
5750
6114
|
return value;
|
|
5751
6115
|
}
|
|
5752
6116
|
function num10(value, flag) {
|
|
5753
|
-
const n = Number(
|
|
6117
|
+
const n = Number(need21(value, flag));
|
|
5754
6118
|
if (!Number.isFinite(n)) {
|
|
5755
6119
|
throw new CliError("PARAM_INVALID", `--${flag} must be a number.`);
|
|
5756
6120
|
}
|
|
@@ -5785,7 +6149,7 @@ function registerFindAirportCommand(parent, deps) {
|
|
|
5785
6149
|
const opts = cmd.optsWithGlobals();
|
|
5786
6150
|
const format = resolveFormat(opts.format);
|
|
5787
6151
|
const apiKey = await resolveApiKey(opts.apiKey);
|
|
5788
|
-
const keyword =
|
|
6152
|
+
const keyword = need21(opts.keyword, "keyword");
|
|
5789
6153
|
const spinner = format === "json" ? null : createSpinner("Resolving airports...");
|
|
5790
6154
|
const result = await deps.apiClient.post(
|
|
5791
6155
|
"/flight/find-airport",
|
|
@@ -5874,7 +6238,7 @@ function registerListNationalitiesCommand(parent, deps) {
|
|
|
5874
6238
|
|
|
5875
6239
|
// src/flight-flink/search.ts
|
|
5876
6240
|
function registerSearchCommand(parent, deps) {
|
|
5877
|
-
const cmd = parent.command("search").description("Search flights (one-way/round-trip/multi-city, journeyId relay)").option("--api-key <key>", "API Key for authentication (X-Api-Key)").option("--trip-type <n>", "1=one-way, 2=round-trip, 3=multi-city").option("--journeys <json>", "JSON array of {date, origin, destination, origin_type?, destination_type?}").option("--cabin-class <c>", "economy | premium_economy | business | first", "economy").option("--adult-num <n>", "Adults (1-9)", "1").option("--child-num <n>", "Children (0-9)", "0").option("--infant-num <n>", "Infants (0-9)", "0").option("--airline <code>", "Airline 2-letter code filter").option("--transfer-number <n>", "0=any,1=direct,2=1 stop,3=2 stops", "0").option("--journey-id <json>", "
|
|
6241
|
+
const cmd = parent.command("search").description("Search flights (one-way/round-trip/multi-city, journeyId relay)").option("--api-key <key>", "API Key for authentication (X-Api-Key)").option("--trip-type <n>", "1=one-way, 2=round-trip, 3=multi-city").option("--journeys <json>", "JSON array of {date, origin, destination, origin_type?, destination_type?}").option("--cabin-class <c>", "economy | premium_economy | business | first", "economy").option("--adult-num <n>", "Adults (1-9)", "1").option("--child-num <n>", "Children (0-9)", "0").option("--infant-num <n>", "Infants (0-9)", "0").option("--airline <code>", "Airline 2-letter code filter").option("--transfer-number <n>", "0=any,1=direct,2=1 stop,3=2 stops", "0").option("--journey-id <json>", "Relay: the chosen offer's next_journey_ids, verbatim (round-trip/multi-city)");
|
|
5878
6242
|
attachSchemaHelp(cmd, flightSearchSchema);
|
|
5879
6243
|
cmd.action(async () => {
|
|
5880
6244
|
const opts = cmd.optsWithGlobals();
|
|
@@ -5882,7 +6246,7 @@ function registerSearchCommand(parent, deps) {
|
|
|
5882
6246
|
const apiKey = await resolveApiKey(opts.apiKey);
|
|
5883
6247
|
const body = {
|
|
5884
6248
|
trip_type: num10(opts.tripType, "trip-type"),
|
|
5885
|
-
journeys: jsonArray(
|
|
6249
|
+
journeys: jsonArray(need21(opts.journeys, "journeys"), "journeys"),
|
|
5886
6250
|
cabin_class: opts.cabinClass,
|
|
5887
6251
|
adult_num: num10(opts.adultNum, "adult-num"),
|
|
5888
6252
|
child_num: num10(opts.childNum, "child-num"),
|
|
@@ -5913,7 +6277,7 @@ function registerMoreOffersCommand(parent, deps) {
|
|
|
5913
6277
|
const opts = cmd.optsWithGlobals();
|
|
5914
6278
|
const format = resolveFormat(opts.format);
|
|
5915
6279
|
const apiKey = await resolveApiKey(opts.apiKey);
|
|
5916
|
-
const productToken =
|
|
6280
|
+
const productToken = need21(opts.productToken, "product-token");
|
|
5917
6281
|
const spinner = format === "json" ? null : createSpinner("Fetching offers...");
|
|
5918
6282
|
const result = await deps.apiClient.post(
|
|
5919
6283
|
"/flight/more-offers",
|
|
@@ -5934,7 +6298,7 @@ function registerVerifyCommand(parent, deps) {
|
|
|
5934
6298
|
const opts = cmd.optsWithGlobals();
|
|
5935
6299
|
const format = resolveFormat(opts.format);
|
|
5936
6300
|
const apiKey = await resolveApiKey(opts.apiKey);
|
|
5937
|
-
const productToken =
|
|
6301
|
+
const productToken = need21(opts.productToken, "product-token");
|
|
5938
6302
|
const spinner = format === "json" ? null : createSpinner("Verifying price...");
|
|
5939
6303
|
const result = await deps.apiClient.post(
|
|
5940
6304
|
"/flight/verify",
|
|
@@ -5956,7 +6320,7 @@ function registerVerifyCommand(parent, deps) {
|
|
|
5956
6320
|
}
|
|
5957
6321
|
|
|
5958
6322
|
// src/flight-flink/create-order.ts
|
|
5959
|
-
import { confirm as
|
|
6323
|
+
import { confirm as confirm10 } from "@inquirer/prompts";
|
|
5960
6324
|
function registerCreateOrderCommand(parent, deps) {
|
|
5961
6325
|
const cmd = parent.command("create-order").description("Create a flight order without charging (locks the fare, await payment)").option("--api-key <key>", "API Key for authentication (X-Api-Key)").option("--product-token <token>", "Authoritative rate token from verify").option("--total-amount <amount>", "Verified total price in decimal units").option("--currency <currency>", "ISO 4217 currency code").option("--trip-type <n>", "1/2/3", "1").option("--contact-name <name>", "Booking contact name").option("--contact-region <code>", "Contact phone country code (no '+')").option("--contact-phone <phone>", "Booking contact phone").option("--contact-email <email>", "Booking contact email").option("--passengers <json>", "JSON array of passengers (gender/id_type are strings)").option("--payment-method-id <id>", "Optional bound-card id (pay_per_call only)").option("--payment-token-id <id>", "Optional UPI network-token id (unionpay charge path)").option(
|
|
5962
6326
|
"--authorized-merchant-trans-id <id>",
|
|
@@ -5968,19 +6332,19 @@ function registerCreateOrderCommand(parent, deps) {
|
|
|
5968
6332
|
const format = resolveFormat(opts.format);
|
|
5969
6333
|
const isYes = Boolean(opts.yes);
|
|
5970
6334
|
const apiKey = await resolveApiKey(opts.apiKey);
|
|
5971
|
-
const productToken =
|
|
6335
|
+
const productToken = need21(opts.productToken, "product-token");
|
|
5972
6336
|
const totalAmount = num10(opts.totalAmount, "total-amount");
|
|
5973
|
-
const currency =
|
|
6337
|
+
const currency = need21(opts.currency, "currency");
|
|
5974
6338
|
const body = {
|
|
5975
6339
|
product_token: productToken,
|
|
5976
6340
|
total_amount: totalAmount,
|
|
5977
6341
|
currency,
|
|
5978
6342
|
trip_type: num10(opts.tripType, "trip-type"),
|
|
5979
|
-
contact_name:
|
|
5980
|
-
contact_region:
|
|
5981
|
-
contact_phone:
|
|
5982
|
-
contact_email:
|
|
5983
|
-
passengers: jsonArray(
|
|
6343
|
+
contact_name: need21(opts.contactName, "contact-name"),
|
|
6344
|
+
contact_region: need21(opts.contactRegion, "contact-region"),
|
|
6345
|
+
contact_phone: need21(opts.contactPhone, "contact-phone"),
|
|
6346
|
+
contact_email: need21(opts.contactEmail, "contact-email"),
|
|
6347
|
+
passengers: jsonArray(need21(opts.passengers, "passengers"), "passengers")
|
|
5984
6348
|
};
|
|
5985
6349
|
if (opts.paymentMethodId !== void 0) body.payment_method_id = opts.paymentMethodId;
|
|
5986
6350
|
if (opts.paymentTokenId !== void 0) body.payment_token_id = opts.paymentTokenId;
|
|
@@ -5990,7 +6354,7 @@ function registerCreateOrderCommand(parent, deps) {
|
|
|
5990
6354
|
const member = memberIdOf(opts);
|
|
5991
6355
|
if (member !== void 0) body.member_id = member;
|
|
5992
6356
|
if (!isYes) {
|
|
5993
|
-
const ok = await
|
|
6357
|
+
const ok = await confirm10({
|
|
5994
6358
|
message: `Create this flight order for ${totalAmount} ${currency}? This locks the fare but does NOT charge yet.`,
|
|
5995
6359
|
default: false
|
|
5996
6360
|
});
|
|
@@ -6029,11 +6393,20 @@ function registerCreateOrderCommand(parent, deps) {
|
|
|
6029
6393
|
}
|
|
6030
6394
|
|
|
6031
6395
|
// src/flight-flink/pay-order.ts
|
|
6032
|
-
import { confirm as
|
|
6396
|
+
import { confirm as confirm11 } from "@inquirer/prompts";
|
|
6033
6397
|
function registerPayOrderCommand(parent, deps) {
|
|
6034
6398
|
const cmd = parent.command("pay-order").description("Settle a created order by --order-no (triggers ticketing)").option("--api-key <key>", "API Key for authentication (X-Api-Key)").option("--order-no <id>", "Our order reference from create-order").option("--payment-method-id <id>", "Optional bound-card id to charge (pay_per_call only)").option("--payment-token-id <id>", "Optional network-token id (unionpay/visa charge path)").option(
|
|
6035
6399
|
"--authorized-merchant-trans-id <id>",
|
|
6036
|
-
"Resume
|
|
6400
|
+
"Resume an EVO-card 3DS challenge: merchant trans id of an already-authorised preauth"
|
|
6401
|
+
).option(
|
|
6402
|
+
"--authenticate",
|
|
6403
|
+
"Opt-in to require cardholder 3DS on the network-token direct charge (--payment-token-id, EVO/Mastercard). When a step-up is needed the response is AUTHENTICATION_REQUIRED with three_ds_url + charge_no; resume with --authorized-charge-no. Default off = frictionless."
|
|
6404
|
+
).option(
|
|
6405
|
+
"--return-url <url>",
|
|
6406
|
+
"Optional 3DS/passkey return URL for the network-token authenticate path (falls back to the platform EVO_3DS_RETURN_URL)"
|
|
6407
|
+
).option(
|
|
6408
|
+
"--authorized-charge-no <no>",
|
|
6409
|
+
"Resume a network-token 3DS challenge: charge_no returned by a prior AUTHENTICATION_REQUIRED response (settled via ChargeService.resume_token)"
|
|
6037
6410
|
).option(
|
|
6038
6411
|
"--evo-explicit",
|
|
6039
6412
|
"[\u65B9\u6848 B/R16] Explicit opt-in to settle this pay_per_call order via the EVO bound-card fallback rail. Set ONLY when the user explicitly chose EVO; forwarded to the pay body as evo_explicit=true. Without it, a pay_per_call settlement lacking --payment-token-id is hard-gated server-side."
|
|
@@ -6044,9 +6417,9 @@ function registerPayOrderCommand(parent, deps) {
|
|
|
6044
6417
|
const format = resolveFormat(opts.format);
|
|
6045
6418
|
const isYes = Boolean(opts.yes);
|
|
6046
6419
|
const apiKey = await resolveApiKey(opts.apiKey);
|
|
6047
|
-
const orderNo =
|
|
6420
|
+
const orderNo = need21(opts.orderNo, "order-no");
|
|
6048
6421
|
if (!isYes) {
|
|
6049
|
-
const ok = await
|
|
6422
|
+
const ok = await confirm11({
|
|
6050
6423
|
message: `Pay and ticket order ${orderNo}? This settles the order and issues tickets.`,
|
|
6051
6424
|
default: false
|
|
6052
6425
|
});
|
|
@@ -6063,6 +6436,11 @@ function registerPayOrderCommand(parent, deps) {
|
|
|
6063
6436
|
if (opts.authorizedMerchantTransId !== void 0) {
|
|
6064
6437
|
body.authorized_merchant_trans_id = opts.authorizedMerchantTransId;
|
|
6065
6438
|
}
|
|
6439
|
+
if (opts.authenticate) body.authenticate = true;
|
|
6440
|
+
if (opts.returnUrl !== void 0) body.return_url = opts.returnUrl;
|
|
6441
|
+
if (opts.authorizedChargeNo !== void 0) {
|
|
6442
|
+
body.authorized_charge_no = opts.authorizedChargeNo;
|
|
6443
|
+
}
|
|
6066
6444
|
if (opts.evoExplicit) body.evo_explicit = true;
|
|
6067
6445
|
const result = await deps.apiClient.post(
|
|
6068
6446
|
`/flight/${encodeURIComponent(orderNo)}/pay`,
|
|
@@ -6072,15 +6450,16 @@ function registerPayOrderCommand(parent, deps) {
|
|
|
6072
6450
|
);
|
|
6073
6451
|
spinner?.stop();
|
|
6074
6452
|
if (!result.success) throw CliError.fromApi(result, { auth: "api-key" });
|
|
6075
|
-
await render2(
|
|
6076
|
-
|
|
6077
|
-
opts.format,
|
|
6078
|
-
(d) => Formatter.keyValue([
|
|
6453
|
+
await render2(result.data, opts.format, (d) => {
|
|
6454
|
+
const rows = [
|
|
6079
6455
|
["Order no", String(d.order_no ?? "-")],
|
|
6080
6456
|
["Status", String(d.status ?? "-")],
|
|
6081
6457
|
["Amount", `${d.amount ?? "-"} ${d.currency ?? ""}`.trim()]
|
|
6082
|
-
]
|
|
6083
|
-
|
|
6458
|
+
];
|
|
6459
|
+
if (d.charge_no) rows.push(["Charge no", String(d.charge_no)]);
|
|
6460
|
+
if (d.three_ds_url) rows.push(["3DS URL", String(d.three_ds_url)]);
|
|
6461
|
+
return Formatter.keyValue(rows);
|
|
6462
|
+
});
|
|
6084
6463
|
});
|
|
6085
6464
|
}
|
|
6086
6465
|
|
|
@@ -6096,7 +6475,7 @@ function registerGetOrderCommand(parent, deps) {
|
|
|
6096
6475
|
const opts = cmd.optsWithGlobals();
|
|
6097
6476
|
const format = resolveFormat(opts.format);
|
|
6098
6477
|
const apiKey = await resolveApiKey(opts.apiKey);
|
|
6099
|
-
const orderNo =
|
|
6478
|
+
const orderNo = need21(opts.orderNo, "order-no");
|
|
6100
6479
|
const path = `/flight/${encodeURIComponent(orderNo)}/status`;
|
|
6101
6480
|
const auth = { type: "api-key", key: apiKey };
|
|
6102
6481
|
if (opts.watch) {
|
|
@@ -6136,7 +6515,7 @@ function registerGetOrderCommand(parent, deps) {
|
|
|
6136
6515
|
}
|
|
6137
6516
|
|
|
6138
6517
|
// src/flight-flink/cancel-order.ts
|
|
6139
|
-
import { confirm as
|
|
6518
|
+
import { confirm as confirm12 } from "@inquirer/prompts";
|
|
6140
6519
|
function registerCancelOrderCommand(parent, deps) {
|
|
6141
6520
|
const cmd = parent.command("cancel-order").description("Cancel an un-ticketed flight order (with refund)").option("--api-key <key>", "API Key for authentication (X-Api-Key)").option("--order-no <id>", "Our order reference").option("--reason <text>", "Optional cancellation reason").option("--idempotency-key <key>", "Forwarded verbatim as the Idempotency-Key header");
|
|
6142
6521
|
attachSchemaHelp(cmd, flightCancelOrderSchema);
|
|
@@ -6145,9 +6524,9 @@ function registerCancelOrderCommand(parent, deps) {
|
|
|
6145
6524
|
const format = resolveFormat(opts.format);
|
|
6146
6525
|
const isYes = Boolean(opts.yes);
|
|
6147
6526
|
const apiKey = await resolveApiKey(opts.apiKey);
|
|
6148
|
-
const orderNo =
|
|
6527
|
+
const orderNo = need21(opts.orderNo, "order-no");
|
|
6149
6528
|
if (!isYes) {
|
|
6150
|
-
const ok = await
|
|
6529
|
+
const ok = await confirm12({
|
|
6151
6530
|
message: `Cancel order ${orderNo}? This requests cancellation and a refund.`,
|
|
6152
6531
|
default: false
|
|
6153
6532
|
});
|
|
@@ -6221,10 +6600,10 @@ function registerChangeSearchCommand(parent, deps) {
|
|
|
6221
6600
|
const format = resolveFormat(opts.format);
|
|
6222
6601
|
const apiKey = await resolveApiKey(opts.apiKey);
|
|
6223
6602
|
const body = {
|
|
6224
|
-
order_no:
|
|
6225
|
-
date:
|
|
6226
|
-
passenger:
|
|
6227
|
-
segment_id:
|
|
6603
|
+
order_no: need21(opts.orderNo, "order-no"),
|
|
6604
|
+
date: need21(opts.date, "date"),
|
|
6605
|
+
passenger: need21(opts.passenger, "passenger"),
|
|
6606
|
+
segment_id: need21(opts.segmentId, "segment-id"),
|
|
6228
6607
|
cabin_class: opts.cabinClass
|
|
6229
6608
|
};
|
|
6230
6609
|
const spinner = format === "json" ? null : createSpinner("Searching change options...");
|
|
@@ -6240,7 +6619,7 @@ function registerChangeSearchCommand(parent, deps) {
|
|
|
6240
6619
|
}
|
|
6241
6620
|
|
|
6242
6621
|
// src/flight-flink/change-apply.ts
|
|
6243
|
-
import { confirm as
|
|
6622
|
+
import { confirm as confirm13 } from "@inquirer/prompts";
|
|
6244
6623
|
function registerChangeApplyCommand(parent, deps) {
|
|
6245
6624
|
const cmd = parent.command("change-apply").description("Submit a change request (returns change_order_no, pending review)").option("--api-key <key>", "API Key for authentication (X-Api-Key)").option("--order-no <id>", "Our order reference").option("--passenger <code>", "passengerCode").option("--segment-id <ids>", "Comma-separated segment ids").option("--product-token <token>", "Change priceKey token").option("--contact-name <name>", "Contact name").option("--contact-region <code>", "Contact region").option("--contact-phone <phone>", "Contact phone").option("--contact-email <email>", "Contact email").option("--idempotency-key <key>", "Forwarded verbatim as the Idempotency-Key header");
|
|
6246
6625
|
attachSchemaHelp(cmd, flightChangeApplySchema);
|
|
@@ -6250,17 +6629,17 @@ function registerChangeApplyCommand(parent, deps) {
|
|
|
6250
6629
|
const isYes = Boolean(opts.yes);
|
|
6251
6630
|
const apiKey = await resolveApiKey(opts.apiKey);
|
|
6252
6631
|
const body = {
|
|
6253
|
-
order_no:
|
|
6254
|
-
passenger:
|
|
6255
|
-
segment_id:
|
|
6256
|
-
product_token:
|
|
6257
|
-
contact_name:
|
|
6258
|
-
contact_region:
|
|
6259
|
-
contact_phone:
|
|
6260
|
-
contact_email:
|
|
6632
|
+
order_no: need21(opts.orderNo, "order-no"),
|
|
6633
|
+
passenger: need21(opts.passenger, "passenger"),
|
|
6634
|
+
segment_id: need21(opts.segmentId, "segment-id"),
|
|
6635
|
+
product_token: need21(opts.productToken, "product-token"),
|
|
6636
|
+
contact_name: need21(opts.contactName, "contact-name"),
|
|
6637
|
+
contact_region: need21(opts.contactRegion, "contact-region"),
|
|
6638
|
+
contact_phone: need21(opts.contactPhone, "contact-phone"),
|
|
6639
|
+
contact_email: need21(opts.contactEmail, "contact-email")
|
|
6261
6640
|
};
|
|
6262
6641
|
if (!isYes) {
|
|
6263
|
-
const ok = await
|
|
6642
|
+
const ok = await confirm13({
|
|
6264
6643
|
message: `Submit a change request for order ${body.order_no}?`,
|
|
6265
6644
|
default: false
|
|
6266
6645
|
});
|
|
@@ -6291,7 +6670,7 @@ function registerChangeDetailCommand(parent, deps) {
|
|
|
6291
6670
|
const opts = cmd.optsWithGlobals();
|
|
6292
6671
|
const format = resolveFormat(opts.format);
|
|
6293
6672
|
const apiKey = await resolveApiKey(opts.apiKey);
|
|
6294
|
-
const changeOrderNo =
|
|
6673
|
+
const changeOrderNo = need21(opts.changeOrderNo, "change-order-no");
|
|
6295
6674
|
const spinner = format === "json" ? null : createSpinner("Fetching change detail...");
|
|
6296
6675
|
const result = await deps.apiClient.get(
|
|
6297
6676
|
`/flight/change/${encodeURIComponent(changeOrderNo)}`,
|
|
@@ -6304,7 +6683,7 @@ function registerChangeDetailCommand(parent, deps) {
|
|
|
6304
6683
|
}
|
|
6305
6684
|
|
|
6306
6685
|
// src/flight-flink/change-cancel.ts
|
|
6307
|
-
import { confirm as
|
|
6686
|
+
import { confirm as confirm14 } from "@inquirer/prompts";
|
|
6308
6687
|
function registerChangeCancelCommand(parent, deps) {
|
|
6309
6688
|
const cmd = parent.command("change-cancel").description("Cancel a change request (immediate terminal)").option("--api-key <key>", "API Key for authentication (X-Api-Key)").option("--change-order-no <id>", "Change order number").option("--idempotency-key <key>", "Forwarded verbatim as the Idempotency-Key header");
|
|
6310
6689
|
attachSchemaHelp(cmd, flightChangeCancelSchema);
|
|
@@ -6313,9 +6692,9 @@ function registerChangeCancelCommand(parent, deps) {
|
|
|
6313
6692
|
const format = resolveFormat(opts.format);
|
|
6314
6693
|
const isYes = Boolean(opts.yes);
|
|
6315
6694
|
const apiKey = await resolveApiKey(opts.apiKey);
|
|
6316
|
-
const changeOrderNo =
|
|
6695
|
+
const changeOrderNo = need21(opts.changeOrderNo, "change-order-no");
|
|
6317
6696
|
if (!isYes) {
|
|
6318
|
-
const ok = await
|
|
6697
|
+
const ok = await confirm14({ message: `Cancel change request ${changeOrderNo}?`, default: false });
|
|
6319
6698
|
if (!ok) throw new CliError("CLIENT_ABORTED", "Change cancellation aborted by user.");
|
|
6320
6699
|
}
|
|
6321
6700
|
const idempotencyKey = await resolveIdempotencyKey(opts.idempotencyKey, {
|
|
@@ -6336,7 +6715,7 @@ function registerChangeCancelCommand(parent, deps) {
|
|
|
6336
6715
|
}
|
|
6337
6716
|
|
|
6338
6717
|
// src/flight-flink/change-pay.ts
|
|
6339
|
-
import { confirm as
|
|
6718
|
+
import { confirm as confirm15 } from "@inquirer/prompts";
|
|
6340
6719
|
function registerChangePayCommand(parent, deps) {
|
|
6341
6720
|
const cmd = parent.command("change-pay").description("Pay a change request fee (like a normal order) and trigger change ticketing").option("--api-key <key>", "API Key for authentication (X-Api-Key)").option("--change-order-no <id>", "Change order number").option("--order-no <id>", "Original order reference (ownership check)").option("--amount <amount>", "Change fee total in decimal units (change-detail price_total)").option("--currency <currency>", "ISO 4217 currency code", "USD").option("--payment-method-id <id>", "Optional bound-card id (EVO path)").option("--payment-token-id <id>", "Optional UPI network-token id (unionpay charge path)").option(
|
|
6342
6721
|
"--authorized-merchant-trans-id <id>",
|
|
@@ -6348,10 +6727,10 @@ function registerChangePayCommand(parent, deps) {
|
|
|
6348
6727
|
const format = resolveFormat(opts.format);
|
|
6349
6728
|
const isYes = Boolean(opts.yes);
|
|
6350
6729
|
const apiKey = await resolveApiKey(opts.apiKey);
|
|
6351
|
-
const changeOrderNo =
|
|
6352
|
-
const orderNo =
|
|
6730
|
+
const changeOrderNo = need21(opts.changeOrderNo, "change-order-no");
|
|
6731
|
+
const orderNo = need21(opts.orderNo, "order-no");
|
|
6353
6732
|
const amount = num10(opts.amount, "amount");
|
|
6354
|
-
const currency =
|
|
6733
|
+
const currency = need21(opts.currency, "currency");
|
|
6355
6734
|
const body = {
|
|
6356
6735
|
order_no: orderNo,
|
|
6357
6736
|
amount,
|
|
@@ -6365,7 +6744,7 @@ function registerChangePayCommand(parent, deps) {
|
|
|
6365
6744
|
if (!isYes) {
|
|
6366
6745
|
const isResume = opts.authorizedMerchantTransId !== void 0;
|
|
6367
6746
|
const message = isResume ? `Resume change fee ${amount} ${currency} for change order ${changeOrderNo}? The card is already authorised; this captures it and triggers change ticketing.` : `Pay change fee ${amount} ${currency} for change order ${changeOrderNo}? This charges the customer and triggers change ticketing.`;
|
|
6368
|
-
const ok = await
|
|
6747
|
+
const ok = await confirm15({ message, default: false });
|
|
6369
6748
|
if (!ok) throw new CliError("CLIENT_ABORTED", "Change payment aborted by user.");
|
|
6370
6749
|
}
|
|
6371
6750
|
const idempotencyKey = await resolveIdempotencyKey(opts.idempotencyKey, {
|
|
@@ -6412,7 +6791,7 @@ function registerChangePayCommand(parent, deps) {
|
|
|
6412
6791
|
}
|
|
6413
6792
|
|
|
6414
6793
|
// src/flight-flink/refund-apply.ts
|
|
6415
|
-
import { confirm as
|
|
6794
|
+
import { confirm as confirm16 } from "@inquirer/prompts";
|
|
6416
6795
|
function registerRefundApplyCommand(parent, deps) {
|
|
6417
6796
|
const cmd = parent.command("refund-apply").description("Submit a refund request (returns refund_order_no, pending review)").option("--api-key <key>", "API Key for authentication (X-Api-Key)").option("--order-no <id>", "Our order reference").option("--passenger <code>", "passengerCode").option("--segment-id <ids>", "Comma-separated segment ids").option("--reason-type <n>", "Refund reason type code").option("--reason <text>", "Free-text reason").option("--contact-name <name>", "Contact name").option("--contact-region <code>", "Contact region").option("--contact-phone <phone>", "Contact phone").option("--contact-email <email>", "Contact email").option("--idempotency-key <key>", "Forwarded verbatim as the Idempotency-Key header");
|
|
6418
6797
|
attachSchemaHelp(cmd, flightRefundApplySchema);
|
|
@@ -6422,18 +6801,18 @@ function registerRefundApplyCommand(parent, deps) {
|
|
|
6422
6801
|
const isYes = Boolean(opts.yes);
|
|
6423
6802
|
const apiKey = await resolveApiKey(opts.apiKey);
|
|
6424
6803
|
const body = {
|
|
6425
|
-
order_no:
|
|
6426
|
-
passenger:
|
|
6427
|
-
segment_id:
|
|
6804
|
+
order_no: need21(opts.orderNo, "order-no"),
|
|
6805
|
+
passenger: need21(opts.passenger, "passenger"),
|
|
6806
|
+
segment_id: need21(opts.segmentId, "segment-id"),
|
|
6428
6807
|
reason_type: num10(opts.reasonType, "reason-type"),
|
|
6429
|
-
contact_name:
|
|
6430
|
-
contact_region:
|
|
6431
|
-
contact_phone:
|
|
6432
|
-
contact_email:
|
|
6808
|
+
contact_name: need21(opts.contactName, "contact-name"),
|
|
6809
|
+
contact_region: need21(opts.contactRegion, "contact-region"),
|
|
6810
|
+
contact_phone: need21(opts.contactPhone, "contact-phone"),
|
|
6811
|
+
contact_email: need21(opts.contactEmail, "contact-email")
|
|
6433
6812
|
};
|
|
6434
6813
|
if (opts.reason !== void 0) body.reason = opts.reason;
|
|
6435
6814
|
if (!isYes) {
|
|
6436
|
-
const ok = await
|
|
6815
|
+
const ok = await confirm16({
|
|
6437
6816
|
message: `Submit a refund request for order ${body.order_no}?`,
|
|
6438
6817
|
default: false
|
|
6439
6818
|
});
|
|
@@ -6464,7 +6843,7 @@ function registerRefundDetailCommand(parent, deps) {
|
|
|
6464
6843
|
const opts = cmd.optsWithGlobals();
|
|
6465
6844
|
const format = resolveFormat(opts.format);
|
|
6466
6845
|
const apiKey = await resolveApiKey(opts.apiKey);
|
|
6467
|
-
const refundOrderNo =
|
|
6846
|
+
const refundOrderNo = need21(opts.refundOrderNo, "refund-order-no");
|
|
6468
6847
|
const spinner = format === "json" ? null : createSpinner("Fetching refund detail...");
|
|
6469
6848
|
const result = await deps.apiClient.get(
|
|
6470
6849
|
`/flight/refund/${encodeURIComponent(refundOrderNo)}`,
|
|
@@ -6477,7 +6856,7 @@ function registerRefundDetailCommand(parent, deps) {
|
|
|
6477
6856
|
}
|
|
6478
6857
|
|
|
6479
6858
|
// src/flight-flink/refund-confirm.ts
|
|
6480
|
-
import { confirm as
|
|
6859
|
+
import { confirm as confirm17 } from "@inquirer/prompts";
|
|
6481
6860
|
function registerRefundConfirmCommand(parent, deps) {
|
|
6482
6861
|
const cmd = parent.command("refund-confirm").description('Confirm ("1") or cancel ("2") a refund').option("--api-key <key>", "API Key for authentication (X-Api-Key)").option("--refund-order-no <id>", "Refund order number").option("--confirm <flag>", '"1" confirm / "2" cancel').option("--idempotency-key <key>", "Forwarded verbatim as the Idempotency-Key header");
|
|
6483
6862
|
attachSchemaHelp(cmd, flightRefundConfirmSchema);
|
|
@@ -6486,14 +6865,14 @@ function registerRefundConfirmCommand(parent, deps) {
|
|
|
6486
6865
|
const format = resolveFormat(opts.format);
|
|
6487
6866
|
const isYes = Boolean(opts.yes);
|
|
6488
6867
|
const apiKey = await resolveApiKey(opts.apiKey);
|
|
6489
|
-
const refundOrderNo =
|
|
6490
|
-
const confirmFlag =
|
|
6868
|
+
const refundOrderNo = need21(opts.refundOrderNo, "refund-order-no");
|
|
6869
|
+
const confirmFlag = need21(opts.confirm, "confirm");
|
|
6491
6870
|
if (confirmFlag !== "1" && confirmFlag !== "2") {
|
|
6492
6871
|
throw new CliError("PARAM_INVALID", '--confirm must be "1" (confirm) or "2" (cancel).');
|
|
6493
6872
|
}
|
|
6494
6873
|
if (!isYes) {
|
|
6495
6874
|
const action = confirmFlag === "1" ? "confirm" : "cancel";
|
|
6496
|
-
const ok = await
|
|
6875
|
+
const ok = await confirm17({ message: `${action} refund ${refundOrderNo}?`, default: false });
|
|
6497
6876
|
if (!ok) throw new CliError("CLIENT_ABORTED", "Refund confirmation aborted by user.");
|
|
6498
6877
|
}
|
|
6499
6878
|
const idempotencyKey = await resolveIdempotencyKey(opts.idempotencyKey, {
|
|
@@ -6533,10 +6912,18 @@ verbs and pass data between them. For exact flags and field types, read the sche
|
|
|
6533
6912
|
## Workflow (book)
|
|
6534
6913
|
1. find-airport --keyword "<place>" \u2192 resolve to IATA city/airport codes
|
|
6535
6914
|
2. search --trip-type <1|2|3> --journeys '<json>'
|
|
6536
|
-
- journeys ALWAYS carries ALL legs
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6915
|
+
- journeys ALWAYS carries ALL legs, on every call including relay calls.
|
|
6916
|
+
- Round-trip/multi-city: keep searching until is_final_leg is true. Each round, present the
|
|
6917
|
+
candidates, let the USER pick one, and pass THAT candidate's next_journey_ids verbatim as
|
|
6918
|
+
--journey-id on the next search. Never assemble the list yourself (the top-level journey_ids
|
|
6919
|
+
is flattened across all candidates) and never send more ids than there are legs \u2014 it is
|
|
6920
|
+
rejected, because upstream would otherwise silently answer with the FIRST leg's candidates.
|
|
6921
|
+
- Only offers where price_key_ready is true carry a real product_token. Earlier-leg candidates
|
|
6922
|
+
are NOT bare flight rows: total_sale_price is already the WHOLE-TRIP total and
|
|
6923
|
+
paired_segment_ids names the legs upstream paired with it, so you can show the full
|
|
6924
|
+
itinerary and real total from the first round \u2014 they just are not bookable yet.
|
|
6925
|
+
- A relay search returning ZERO offers means the journey ids went stale OR that leg sold out
|
|
6926
|
+
(upstream reports both as an empty list). Restart from leg 1 for fresh ids.
|
|
6540
6927
|
- Code<->type consistency: tag each code with its matching type \u2014 an AIRPORT code (e.g. NRT)
|
|
6541
6928
|
uses type 2, a CITY code (e.g. TYO) uses type 1. Mislabeling (e.g. NRT with type 1, since NRT
|
|
6542
6929
|
is an airport whose city is TYO) is rejected with code 400 "destination not exist".
|
|
@@ -6572,7 +6959,7 @@ verbs and pass data between them. For exact flags and field types, read the sche
|
|
|
6572
6959
|
## Data passing
|
|
6573
6960
|
- product_token : search \u2192 verify \u2192 create-order (opaque; never fabricate or edit; verify's token wins)
|
|
6574
6961
|
- order_no : create-order \u2192 pay-order / get-order / cancel-order
|
|
6575
|
-
-
|
|
6962
|
+
- next_journey_ids : chosen offer \u2192 next search's --journey-id (relay for round-trip/multi-city)
|
|
6576
6963
|
- change_order_no / refund_order_no : *-apply \u2192 *-detail / *-confirm / pay-order (change)
|
|
6577
6964
|
|
|
6578
6965
|
## Invariants
|
|
@@ -6667,10 +7054,11 @@ Key notes:
|
|
|
6667
7054
|
registerServicesListCommand(servicesCmd, { discoveryClient, program });
|
|
6668
7055
|
registerServiceGetCommand(servicesCmd, { discoveryClient, program });
|
|
6669
7056
|
const ordersCmd = program.command("orders").description(
|
|
6670
|
-
|
|
7057
|
+
'Unified cross-provider order index (spans ride + hotel + flight). Use list/get for generic "my orders" requests (use ride-elife/hotel-redaug/flight-flink commands once the business is known); pay-batch settles a batch of AWAITING_PAYMENT orders in one aggregate payment (trip checkout).'
|
|
6671
7058
|
);
|
|
6672
7059
|
registerOrdersListCommand(ordersCmd, deps);
|
|
6673
7060
|
registerOrdersGetCommand(ordersCmd, deps);
|
|
7061
|
+
registerOrdersPayBatchCommand(ordersCmd, deps);
|
|
6674
7062
|
const hotelCmd = program.command("hotel-redaug").description(
|
|
6675
7063
|
`Hotel booking (Redaug) \u2014 international hotel search, quote, create-order, pay-order, cancel, and check-out.
|
|
6676
7064
|
|
|
@@ -6724,7 +7112,8 @@ Key notes:
|
|
|
6724
7112
|
Workflow (typical order):
|
|
6725
7113
|
1. find-airport Resolve a place name \u2192 IATA city/airport codes
|
|
6726
7114
|
2. search Search flights (one-way/round-trip/multi-city). journeys carries ALL legs;
|
|
6727
|
-
|
|
7115
|
+
round-trip/multi-city: relay the chosen offer's next_journey_ids into
|
|
7116
|
+
--journey-id until is_final_leg is true
|
|
6728
7117
|
3. verify Verify the fare \u2192 authoritative product_token + price_changed flag
|
|
6729
7118
|
4. create-order Create the order using verify's product_token (locks the fare, no charge)
|
|
6730
7119
|
5. pay-order Settle by --order-no (triggers ticketing); order \u2192 PAID
|