{
  "openapi": "3.0.3",
  "info": {
    "title": "YG OpenAPI",
    "version": "v1",
    "description": "YG Developer Center official downstream integration contract. All monetary values are decimal strings. HTTP status codes describe transport/protocol outcomes; the response code field describes business outcomes. Production signing secrets must never be uploaded to the documentation site or any sample endpoint."
  },
  "servers": [
    {
      "url": "https://openapi-test.yibo-api.com",
      "description": "Sandbox - 用于测试联调，不产生真实资金影响。默认用于 SDK 代码生成。"
    },
    {
      "url": "https://api.yibo-api.com",
      "description": "Production - 正式环境，仅供正式凭据调用；文档站禁止在线执行。"
    }
  ],
  "x-yg-error-code-registry": "./error-codes.json",
  "tags": [
    {
      "name": "System"
    },
    {
      "name": "Auth"
    },
    {
      "name": "Meta"
    },
    {
      "name": "Players"
    },
    {
      "name": "Merchant"
    },
    {
      "name": "Wallet"
    },
    {
      "name": "Games"
    },
    {
      "name": "Bets"
    },
    {
      "name": "Callbacks"
    }
  ],
  "security": [
    {
      "MerchantId": [],
      "Timestamp": [],
      "Nonce": [],
      "Sign": [],
      "SignVersion": []
    }
  ],
  "paths": {
    "/openapi/v1/system/time": {
      "get": {
        "tags": [
          "System"
        ],
        "summary": "Get server time",
        "description": "Signed read-only endpoint used to calibrate the millisecond timestamp. The public Demo merchant may call this System allowlist operation.",
        "operationId": "getOpenApiServerTime",
        "x-signed": true,
        "x-try-it-out": true,
        "responses": {
          "200": {
            "description": "Server timestamp",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TimeResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-rate-limit": {
          "defaultQps": 10,
          "burst": 20,
          "timeoutMs": 5000,
          "retry": "Exponential backoff with jitter; retry only idempotent requests.",
          "orderRecovery": "For transfer uncertainty, query by orderNo before retrying."
        }
      }
    },
    "/openapi/v1/system/error-codes": {
      "get": {
        "tags": [
          "System"
        ],
        "summary": "List OpenAPI error codes",
        "description": "Returns the current business error dictionary. This endpoint requires a signed sandbox request.",
        "operationId": "listOpenApiErrorCodes",
        "x-try-it-out": true,
        "responses": {
          "200": {
            "description": "Error code list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorCodeListResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-rate-limit": {
          "defaultQps": 10,
          "burst": 20,
          "timeoutMs": 5000,
          "retry": "Exponential backoff with jitter; retry only idempotent requests.",
          "orderRecovery": "For transfer uncertainty, query by orderNo before retrying."
        }
      }
    },
    "/openapi/v1/auth/sign/verify-sample": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Local signature example and fixed test vector",
        "description": "Documentation-only signing reference. The YG Developer Center does not request, receive, or verify production Signing Keys. Reproduce the fixed vector locally in your SDK; production Secret never leaves the merchant security boundary.",
        "operationId": "verifyOpenApiSignSample",
        "x-signed": true,
        "x-try-it-out": false,
        "x-sample-body": {
          "signingKey": "YG_DOC_TEST_KEY_ONLY",
          "params": {
            "method": "POST",
            "path": "/openapi/v1/players",
            "query": "",
            "rawBody": "{\"memberId\":\"YG_TEST_PLAYER\",\"currency\":\"CNY\"}",
            "bodySha256": "6c29ddf983122d0126f322dc8dae285a254d31020cadb2e157fe1e4c5c7c2889",
            "timestamp": "1735689600000",
            "nonce": "550e8400-e29b-41d4-a716-446655440000",
            "merchantId": "YG_TEST_MERCHANT",
            "signVersion": "V1"
          },
          "sign": "7520bd5e0056bb727356ba5bd03e0489936b8e897da9298f31e79526d688aa97"
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SignVerifyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Static documentation vector; no Secret is uploaded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenApiResponse"
                },
                "example": {
                  "code": 0,
                  "message": "Use the published fixed vector locally; production Secret is never uploaded.",
                  "data": {
                    "mode": "LOCAL_ONLY",
                    "productionSecretUpload": false
                  },
                  "requestId": "req_yg_signing_guide",
                  "timestamp": 1735689600000
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-rate-limit": {
          "defaultQps": 10,
          "burst": 20,
          "timeoutMs": 5000,
          "retry": "Exponential backoff with jitter; retry only idempotent requests.",
          "orderRecovery": "For transfer uncertainty, query by orderNo before retrying."
        },
        "x-documentation-only": true,
        "x-production-secret-upload": "forbidden",
        "x-fixed-test-vector": {
          "method": "POST",
          "path": "/openapi/v1/players",
          "queryOriginal": "",
          "querySorted": "",
          "timestamp": "1735689600000",
          "nonce": "550e8400-e29b-41d4-a716-446655440000",
          "merchantId": "YG_TEST_MERCHANT",
          "signVersion": "V1",
          "body": "{\"memberId\":\"YG_TEST_PLAYER\",\"currency\":\"CNY\"}",
          "bodySha256": "6c29ddf983122d0126f322dc8dae285a254d31020cadb2e157fe1e4c5c7c2889",
          "canonicalString": "POST\n/openapi/v1/players\n\n6c29ddf983122d0126f322dc8dae285a254d31020cadb2e157fe1e4c5c7c2889\n1735689600000\n550e8400-e29b-41d4-a716-446655440000\nYG_TEST_MERCHANT",
          "testSigningKey": "YG_DOC_TEST_KEY_ONLY",
          "hmacSha256": "7520bd5e0056bb727356ba5bd03e0489936b8e897da9298f31e79526d688aa97",
          "signature": "7520bd5e0056bb727356ba5bd03e0489936b8e897da9298f31e79526d688aa97"
        }
      }
    },
    "/openapi/v1/meta/games": {
      "get": {
        "tags": [
          "Meta"
        ],
        "summary": "Get dynamic game list",
        "description": "Returns the current game list from the test API. The docs site must not hardcode a fixed game list.",
        "operationId": "listOpenApiGames",
        "x-try-it-out": true,
        "parameters": [
          {
            "name": "vendorCode",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "gameCode",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "currency",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "language",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Dynamic games",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GameListResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-rate-limit": {
          "defaultQps": 10,
          "burst": 20,
          "timeoutMs": 5000,
          "retry": "Exponential backoff with jitter; retry only idempotent requests.",
          "orderRecovery": "For transfer uncertainty, query by orderNo before retrying."
        }
      }
    },
    "/openapi/v1/meta/vendors": {
      "get": {
        "tags": [
          "Meta"
        ],
        "summary": "Get dynamic vendor list",
        "description": "Returns current vendor codes and status; downstream systems must not hardcode this list.",
        "operationId": "listOpenApiVendors",
        "x-try-it-out": true,
        "responses": {
          "200": {
            "description": "Dynamic vendors",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VendorListResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-rate-limit": {
          "defaultQps": 10,
          "burst": 20,
          "timeoutMs": 5000,
          "retry": "Exponential backoff with jitter; retry only idempotent requests.",
          "orderRecovery": "For transfer uncertainty, query by orderNo before retrying."
        }
      }
    },
    "/openapi/v1/meta/currencies": {
      "get": {
        "tags": [
          "Meta"
        ],
        "summary": "Get dynamic currency list",
        "description": "Returns current currencies and status for member and wallet validation.",
        "operationId": "listOpenApiCurrencies",
        "x-try-it-out": true,
        "responses": {
          "200": {
            "description": "Dynamic currencies",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CurrencyListResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-rate-limit": {
          "defaultQps": 10,
          "burst": 20,
          "timeoutMs": 5000,
          "retry": "Exponential backoff with jitter; retry only idempotent requests.",
          "orderRecovery": "For transfer uncertainty, query by orderNo before retrying."
        }
      }
    },
    "/openapi/v1/meta/languages": {
      "get": {
        "tags": [
          "Meta"
        ],
        "summary": "Get dynamic language list",
        "description": "Returns current language codes supported by the sandbox metadata source.",
        "operationId": "listOpenApiLanguages",
        "x-try-it-out": true,
        "responses": {
          "200": {
            "description": "Dynamic languages",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LanguageListResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-rate-limit": {
          "defaultQps": 10,
          "burst": 20,
          "timeoutMs": 5000,
          "retry": "Exponential backoff with jitter; retry only idempotent requests.",
          "orderRecovery": "For transfer uncertainty, query by orderNo before retrying."
        }
      }
    },
    "/openapi/v1/meta/maintenance": {
      "get": {
        "tags": [
          "Meta"
        ],
        "summary": "Get dynamic maintenance status",
        "description": "Returns vendor, game, or system maintenance state used before generating login URLs.",
        "operationId": "listOpenApiMaintenance",
        "x-try-it-out": true,
        "responses": {
          "200": {
            "description": "Dynamic maintenance status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MaintenanceListResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-rate-limit": {
          "defaultQps": 10,
          "burst": 20,
          "timeoutMs": 5000,
          "retry": "Exponential backoff with jitter; retry only idempotent requests.",
          "orderRecovery": "For transfer uncertainty, query by orderNo before retrying."
        }
      }
    },
    "/openapi/v1/players": {
      "post": {
        "tags": [
          "Players"
        ],
        "summary": "Create or reuse a sandbox member",
        "description": "Creates a sandbox member idempotently. Reusing memberId with another currency returns 10007.",
        "operationId": "createOpenApiPlayer",
        "x-try-it-out": true,
        "x-sample-body": {
          "memberId": "YG_TEST_PLAYER",
          "currency": "CNY"
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePlayerRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Member created or existing member returned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-rate-limit": {
          "defaultQps": 10,
          "burst": 20,
          "timeoutMs": 5000,
          "retry": "Exponential backoff with jitter; retry only idempotent requests.",
          "orderRecovery": "For transfer uncertainty, query by orderNo before retrying."
        }
      }
    },
    "/openapi/v1/players/{memberId}": {
      "get": {
        "tags": [
          "Players"
        ],
        "summary": "Query sandbox member",
        "description": "Returns the member created in the current sandbox runtime.",
        "operationId": "getOpenApiPlayer",
        "x-try-it-out": true,
        "parameters": [
          {
            "name": "memberId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Member or business error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-rate-limit": {
          "defaultQps": 10,
          "burst": 20,
          "timeoutMs": 5000,
          "retry": "Exponential backoff with jitter; retry only idempotent requests.",
          "orderRecovery": "For transfer uncertainty, query by orderNo before retrying."
        }
      }
    },
    "/openapi/v1/wallet/balance/{memberId}": {
      "get": {
        "tags": [
          "Wallet"
        ],
        "summary": "Query sandbox balance",
        "description": "Returns a non-financial sandbox balance. It never reads or mutates production funds.",
        "operationId": "getOpenApiBalance",
        "x-try-it-out": true,
        "parameters": [
          {
            "name": "memberId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "gameCode",
            "in": "query",
            "required": false,
            "description": "Required for production balance routing. Obtain the value from /openapi/v1/meta/games.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sandbox balance or business error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-rate-limit": {
          "defaultQps": 10,
          "burst": 20,
          "timeoutMs": 5000,
          "retry": "Exponential backoff with jitter; retry only idempotent requests.",
          "orderRecovery": "For transfer uncertainty, query by orderNo before retrying."
        }
      }
    },
    "/openapi/v1/wallet/deposit": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "summary": "Deposit dry-run",
        "description": "P0 test API returns dry-run only and must not mutate real balance.",
        "operationId": "openApiDepositDryRun",
        "x-try-it-out": true,
        "x-sample-body": {
          "memberId": "YG_TEST_PLAYER",
          "orderNo": "YG-DEPOSIT-DEMO",
          "amount": "10.00",
          "currency": "CNY",
          "gameCode": "DYNAMIC_GAME_CODE",
          "dryRun": true
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WalletTransferRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent dry-run order",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransferOrderResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-rate-limit": {
          "defaultQps": 10,
          "burst": 20,
          "timeoutMs": 5000,
          "retry": "Exponential backoff with jitter; retry only idempotent requests.",
          "orderRecovery": "For transfer uncertainty, query by orderNo before retrying."
        }
      }
    },
    "/openapi/v1/wallet/withdraw": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "summary": "Withdraw dry-run",
        "description": "Validates and records a sandbox dry-run order without changing any balance.",
        "operationId": "openApiWithdrawDryRun",
        "x-try-it-out": true,
        "x-sample-body": {
          "memberId": "YG_TEST_PLAYER",
          "orderNo": "YG-WITHDRAW-DEMO",
          "amount": "5.00",
          "currency": "CNY",
          "gameCode": "DYNAMIC_GAME_CODE",
          "dryRun": true
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WalletTransferRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Dry-run response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransferOrderResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-rate-limit": {
          "defaultQps": 10,
          "burst": 20,
          "timeoutMs": 5000,
          "retry": "Exponential backoff with jitter; retry only idempotent requests.",
          "orderRecovery": "For transfer uncertainty, query by orderNo before retrying."
        }
      }
    },
    "/openapi/v1/wallet/transactions/{orderNo}": {
      "get": {
        "tags": [
          "Wallet"
        ],
        "summary": "Query sandbox transfer order",
        "description": "Query before retrying a timed-out transfer; do not replace orderNo blindly.",
        "operationId": "getOpenApiTransferOrder",
        "x-try-it-out": true,
        "parameters": [
          {
            "name": "orderNo",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Transfer order or business error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransferOrderResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-rate-limit": {
          "defaultQps": 10,
          "burst": 20,
          "timeoutMs": 5000,
          "retry": "Exponential backoff with jitter; retry only idempotent requests.",
          "orderRecovery": "For transfer uncertainty, query by orderNo before retrying."
        }
      }
    },
    "/openapi/v1/games/login-url": {
      "post": {
        "tags": [
          "Games"
        ],
        "summary": "Get sandbox game login URL",
        "description": "Returns a non-routable sandbox launch URL for contract verification; no vendor is called.",
        "operationId": "getOpenApiGameLoginUrl",
        "x-try-it-out": true,
        "x-sample-body": {
          "memberId": "YG_TEST_PLAYER",
          "gameCode": "DYNAMIC_GAME_CODE",
          "language": "zh-CN",
          "currency": "CNY"
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GameLoginUrlRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sandbox launch URL",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GameLoginResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-rate-limit": {
          "defaultQps": 10,
          "burst": 20,
          "timeoutMs": 5000,
          "retry": "Exponential backoff with jitter; retry only idempotent requests.",
          "orderRecovery": "For transfer uncertainty, query by orderNo before retrying."
        }
      }
    },
    "/openapi/v1/bets": {
      "get": {
        "tags": [
          "Bets"
        ],
        "summary": "Query sandbox bet records",
        "description": "Returns a deterministic empty page in sandbox. Window must be no longer than 24 hours and pageSize no greater than 500.",
        "operationId": "listOpenApiBets",
        "x-try-it-out": true,
        "x-sample-query": {
          "startTime": 1783699200000,
          "endTime": 1783785599000,
          "pageNum": 1,
          "pageSize": 100
        },
        "parameters": [
          {
            "name": "startTime",
            "in": "query",
            "required": true,
            "description": "Inclusive Unix milliseconds",
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 1
            }
          },
          {
            "name": "endTime",
            "in": "query",
            "required": true,
            "description": "Exclusive Unix milliseconds; maximum 24-hour window",
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 1
            }
          },
          {
            "name": "pageNum",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Bet page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BetPageResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-rate-limit": {
          "defaultQps": 10,
          "burst": 20,
          "timeoutMs": 5000,
          "retry": "Exponential backoff with jitter; retry only idempotent requests.",
          "orderRecovery": "For transfer uncertainty, query by orderNo before retrying."
        }
      }
    },
    "/openapi/v1/merchant/credit": {
      "get": {
        "tags": [
          "Merchant"
        ],
        "summary": "Query sandbox merchant credit",
        "description": "Returns a non-financial sandbox value for integration testing; it is not production merchant credit.",
        "operationId": "getOpenApiMerchantCredit",
        "x-try-it-out": true,
        "parameters": [
          {
            "name": "currency",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 3,
              "maxLength": 12
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sandbox merchant credit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantCreditResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-rate-limit": {
          "defaultQps": 10,
          "burst": 20,
          "timeoutMs": 5000,
          "retry": "Exponential backoff with jitter; retry only idempotent requests.",
          "orderRecovery": "For transfer uncertainty, query by orderNo before retrying."
        }
      }
    },
    "/openapi/v1/callbacks/sample": {
      "post": {
        "tags": [
          "Callbacks"
        ],
        "summary": "Callback acknowledgement sample",
        "description": "Demonstrates idempotent acknowledgement. Repeated eventId is accepted with duplicate=true.",
        "operationId": "openApiCallbackSample",
        "x-try-it-out": true,
        "x-sample-body": {
          "eventType": "BET_SETTLED",
          "eventId": "DOCS-CALLBACK-DEMO",
          "payload": {
            "betNo": "BET-DEMO-001"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallbackRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sample acknowledgement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallbackResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-rate-limit": {
          "defaultQps": 10,
          "burst": 20,
          "timeoutMs": 5000,
          "retry": "Exponential backoff with jitter; retry only idempotent requests.",
          "orderRecovery": "For transfer uncertainty, query by orderNo before retrying."
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "MerchantId": {
        "type": "apiKey",
        "in": "header",
        "name": "X-YG-Merchant-Id"
      },
      "Timestamp": {
        "type": "apiKey",
        "in": "header",
        "name": "X-YG-Timestamp"
      },
      "Nonce": {
        "type": "apiKey",
        "in": "header",
        "name": "X-YG-Nonce"
      },
      "Sign": {
        "type": "apiKey",
        "in": "header",
        "name": "X-YG-Sign"
      },
      "SignVersion": {
        "type": "apiKey",
        "in": "header",
        "name": "X-YG-Sign-Version"
      }
    },
    "schemas": {
      "ApiResponse": {
        "type": "object",
        "description": "Current response envelope. HTTP 200 does not imply business success; code must also equal 0.",
        "properties": {
          "code": {
            "type": "integer"
          },
          "message": {
            "type": "string"
          },
          "requestId": {
            "type": "string"
          },
          "timestamp": {
            "type": "integer",
            "format": "int64"
          },
          "data": {
            "nullable": true
          }
        },
        "required": [
          "code",
          "message",
          "data",
          "requestId",
          "timestamp"
        ]
      },
      "OpenApiResponse": {
        "$ref": "#/components/schemas/ApiResponse",
        "description": "Compatibility alias for ApiResponse."
      },
      "ErrorResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ApiResponse"
          },
          {
            "type": "object",
            "properties": {
              "code": {
                "type": "integer",
                "minimum": 1
              },
              "data": {
                "nullable": true,
                "example": null
              }
            }
          }
        ]
      },
      "ValidationError": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ErrorResponse"
          }
        ],
        "description": "CURRENT business validation envelope: normally HTTP 200 with code 10001. HTTP 400 mapping is PLANNED."
      },
      "RateLimitError": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ErrorResponse"
          },
          {
            "type": "object",
            "properties": {
              "code": {
                "type": "integer",
                "enum": [
                  10008
                ]
              }
            }
          }
        ],
        "description": "CURRENT HTTP 429 response with business code 10008."
      },
      "TimeResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OpenApiResponse"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object",
                "properties": {
                  "version": {
                    "type": "string"
                  },
                  "timestamp": {
                    "type": "integer",
                    "format": "int64"
                  }
                }
              }
            }
          }
        ]
      },
      "ErrorCode": {
        "type": "object",
        "properties": {
          "code": {
            "type": "integer"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "ErrorCodeListResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OpenApiResponse"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ErrorCode"
                }
              }
            }
          }
        ]
      },
      "GameMeta": {
        "type": "object",
        "properties": {
          "vendorCode": {
            "type": "string"
          },
          "gameCode": {
            "type": "string"
          },
          "gameName": {
            "type": "string"
          },
          "gameType": {
            "type": "string"
          },
          "currency": {
            "type": "string"
          },
          "language": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "sortNo": {
            "type": "integer",
            "nullable": true
          }
        }
      },
      "VendorMeta": {
        "type": "object",
        "properties": {
          "vendorCode": {
            "type": "string"
          },
          "vendorName": {
            "type": "string"
          },
          "status": {
            "type": "string"
          }
        }
      },
      "CurrencyMeta": {
        "type": "object",
        "properties": {
          "currency": {
            "type": "string"
          },
          "status": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "LanguageMeta": {
        "type": "object",
        "properties": {
          "language": {
            "type": "string"
          },
          "status": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "MaintenanceMeta": {
        "type": "object",
        "properties": {
          "resourceType": {
            "type": "string"
          },
          "resourceCode": {
            "type": "string"
          },
          "resourceName": {
            "type": "string"
          },
          "status": {
            "type": "string"
          }
        }
      },
      "GameListResponse": {
        "$ref": "#/components/schemas/ListResponseGameMeta"
      },
      "VendorListResponse": {
        "$ref": "#/components/schemas/ListResponseVendorMeta"
      },
      "CurrencyListResponse": {
        "$ref": "#/components/schemas/ListResponseCurrencyMeta"
      },
      "LanguageListResponse": {
        "$ref": "#/components/schemas/ListResponseLanguageMeta"
      },
      "MaintenanceListResponse": {
        "$ref": "#/components/schemas/ListResponseMaintenanceMeta"
      },
      "ListResponseGameMeta": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OpenApiResponse"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/GameMeta"
                }
              }
            }
          }
        ]
      },
      "ListResponseVendorMeta": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OpenApiResponse"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/VendorMeta"
                }
              }
            }
          }
        ]
      },
      "ListResponseCurrencyMeta": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OpenApiResponse"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/CurrencyMeta"
                }
              }
            }
          }
        ]
      },
      "ListResponseLanguageMeta": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OpenApiResponse"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/LanguageMeta"
                }
              }
            }
          }
        ]
      },
      "ListResponseMaintenanceMeta": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OpenApiResponse"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/MaintenanceMeta"
                }
              }
            }
          }
        ]
      },
      "SignVerifyRequest": {
        "type": "object",
        "properties": {
          "signingKey": {
            "type": "string",
            "description": "Test signing key only. Do not use production secrets."
          },
          "params": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "sign": {
            "type": "string"
          }
        }
      },
      "SignVerifyResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OpenApiResponse"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object",
                "properties": {
                  "canonicalString": {
                    "type": "string"
                  },
                  "calculatedSign": {
                    "type": "string"
                  },
                  "matched": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        ]
      },
      "CreatePlayerRequest": {
        "type": "object",
        "properties": {
          "memberId": {
            "type": "string",
            "minLength": 3,
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9_-]+$",
            "example": "YG_TEST_PLAYER"
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 12,
            "example": "CNY"
          },
          "gameCode": {
            "type": "string",
            "maxLength": 64,
            "nullable": true,
            "description": "Required for production member creation. Obtain the value from /openapi/v1/meta/games."
          }
        },
        "required": [
          "memberId",
          "currency"
        ]
      },
      "WalletTransferRequest": {
        "type": "object",
        "properties": {
          "memberId": {
            "type": "string",
            "minLength": 3,
            "maxLength": 64,
            "example": "YG_TEST_PLAYER"
          },
          "orderNo": {
            "type": "string",
            "minLength": 16,
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9_-]+$",
            "example": "YG-ORDER-00000001"
          },
          "amount": {
            "type": "string",
            "pattern": "^-?\\d{1,16}(?:\\.\\d{1,4})?$",
            "example": "10.00",
            "description": "Decimal amount encoded as a string; never parse as binary floating point."
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 12,
            "example": "CNY"
          },
          "gameCode": {
            "type": "string",
            "maxLength": 64,
            "nullable": true,
            "description": "Required for production transfers. Obtain the value from /openapi/v1/meta/games."
          },
          "dryRun": {
            "type": "boolean",
            "example": true,
            "description": "Must be true in Sandbox. Production OpenAPI is currently disabled; if separately enabled in the future, production transfers require false."
          }
        },
        "required": [
          "memberId",
          "orderNo",
          "amount",
          "currency",
          "dryRun"
        ]
      },
      "GameLoginUrlRequest": {
        "type": "object",
        "properties": {
          "memberId": {
            "type": "string",
            "minLength": 3,
            "maxLength": 64
          },
          "gameCode": {
            "type": "string",
            "maxLength": 64,
            "description": "Value must be obtained from /openapi/v1/meta/games."
          },
          "language": {
            "type": "string",
            "maxLength": 16,
            "example": "zh-CN"
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 12,
            "example": "CNY"
          },
          "platform": {
            "type": "string",
            "maxLength": 16,
            "nullable": true
          },
          "returnUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 512,
            "nullable": true
          }
        },
        "required": [
          "memberId",
          "gameCode",
          "language",
          "currency"
        ]
      },
      "Player": {
        "type": "object",
        "required": [
          "memberId",
          "currency",
          "gameCode",
          "status",
          "createdAt"
        ],
        "properties": {
          "memberId": {
            "type": "string",
            "example": "YG_TEST_PLAYER"
          },
          "currency": {
            "type": "string",
            "nullable": true,
            "example": "CNY"
          },
          "gameCode": {
            "type": "string",
            "nullable": true,
            "description": "Sandbox echoes the optional creation gameCode. Production lookup uses the player's most recent game when available."
          },
          "status": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "DISABLED"
            ],
            "example": "ACTIVE"
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "example": 1760000000000
          }
        }
      },
      "Balance": {
        "type": "object",
        "required": [
          "memberId",
          "currency",
          "balance",
          "mode"
        ],
        "properties": {
          "memberId": {
            "type": "string"
          },
          "currency": {
            "type": "string"
          },
          "gameCode": {
            "type": "string",
            "nullable": true
          },
          "balance": {
            "type": "string",
            "example": "0.00",
            "pattern": "^-?\\d+(\\.\\d{1,8})?$",
            "description": "Decimal amount encoded as a string; never parse as binary floating point."
          },
          "mode": {
            "type": "string",
            "enum": [
              "SANDBOX",
              "PRODUCTION"
            ]
          }
        }
      },
      "TransferOrder": {
        "type": "object",
        "required": [
          "orderNo",
          "memberId",
          "transferType",
          "amount",
          "currency",
          "status",
          "dryRun",
          "createdAt"
        ],
        "properties": {
          "orderNo": {
            "type": "string",
            "example": "YG-ORDER-00000001"
          },
          "memberId": {
            "type": "string"
          },
          "transferType": {
            "type": "string",
            "enum": [
              "DEPOSIT",
              "WITHDRAW"
            ]
          },
          "amount": {
            "type": "string",
            "example": "10.00",
            "pattern": "^-?\\d+(\\.\\d{1,8})?$",
            "description": "Decimal amount encoded as a string; never parse as binary floating point."
          },
          "currency": {
            "type": "string"
          },
          "gameCode": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "description": "Sandbox returns DRY_RUN_ACCEPTED. Production status values are backend transaction states and Production OpenAPI is currently disabled."
          },
          "dryRun": {
            "type": "boolean",
            "description": "Always true in Sandbox."
          },
          "createdAt": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "GameLoginResult": {
        "type": "object",
        "required": [
          "url",
          "expiresAt",
          "mode"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "example": "https://sandbox.invalid/launch?..."
          },
          "expiresAt": {
            "type": "integer",
            "format": "int64"
          },
          "mode": {
            "type": "string",
            "enum": [
              "SANDBOX",
              "PRODUCTION"
            ]
          }
        }
      },
      "BetRecord": {
        "type": "object",
        "properties": {
          "betNo": {
            "type": "string"
          },
          "memberId": {
            "type": "string"
          },
          "vendorCode": {
            "type": "string"
          },
          "gameCode": {
            "type": "string"
          },
          "currency": {
            "type": "string"
          },
          "betAmount": {
            "type": "string",
            "example": "10.00",
            "pattern": "^-?\\d+(\\.\\d{1,8})?$",
            "description": "Decimal amount encoded as a string; never parse as binary floating point."
          },
          "validBetAmount": {
            "type": "string",
            "example": "10.00",
            "pattern": "^-?\\d+(\\.\\d{1,8})?$",
            "description": "Decimal amount encoded as a string; never parse as binary floating point."
          },
          "netAmount": {
            "type": "string",
            "example": "10.00",
            "pattern": "^-?\\d+(\\.\\d{1,8})?$",
            "description": "Decimal amount encoded as a string; never parse as binary floating point."
          },
          "status": {
            "type": "string",
            "enum": [
              "UNSETTLED",
              "SETTLED",
              "VOID",
              "REFUNDED"
            ]
          },
          "betTime": {
            "type": "integer",
            "format": "int64"
          },
          "settledAt": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          }
        }
      },
      "BetPage": {
        "type": "object",
        "required": [
          "rows",
          "total",
          "pageNo",
          "pageSize"
        ],
        "properties": {
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BetRecord"
            }
          },
          "total": {
            "type": "integer",
            "format": "int64"
          },
          "pageNo": {
            "type": "integer"
          },
          "pageSize": {
            "type": "integer"
          }
        }
      },
      "CallbackRequest": {
        "type": "object",
        "required": [
          "eventType",
          "eventId"
        ],
        "properties": {
          "eventType": {
            "type": "string",
            "maxLength": 64
          },
          "eventId": {
            "type": "string",
            "maxLength": 128
          },
          "payload": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true
          }
        }
      },
      "CallbackAck": {
        "type": "object",
        "required": [
          "eventId",
          "accepted",
          "duplicate",
          "receivedAt"
        ],
        "properties": {
          "eventId": {
            "type": "string"
          },
          "accepted": {
            "type": "boolean"
          },
          "duplicate": {
            "type": "boolean"
          },
          "receivedAt": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "MerchantCredit": {
        "type": "object",
        "required": [
          "currency",
          "availableCredit",
          "mode"
        ],
        "properties": {
          "currency": {
            "type": "string"
          },
          "availableCredit": {
            "type": "string",
            "example": "0.00",
            "pattern": "^-?\\d+(\\.\\d{1,8})?$",
            "description": "Decimal amount encoded as a string; never parse as binary floating point."
          },
          "mode": {
            "type": "string",
            "enum": [
              "SANDBOX",
              "PRODUCTION"
            ]
          }
        }
      },
      "PlayerResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OpenApiResponse"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "$ref": "#/components/schemas/Player"
              }
            }
          }
        ]
      },
      "BalanceResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OpenApiResponse"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "$ref": "#/components/schemas/Balance"
              }
            }
          }
        ]
      },
      "TransferOrderResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OpenApiResponse"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "$ref": "#/components/schemas/TransferOrder"
              }
            }
          }
        ]
      },
      "GameLoginResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OpenApiResponse"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "$ref": "#/components/schemas/GameLoginResult"
              }
            }
          }
        ]
      },
      "BetPageResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OpenApiResponse"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "$ref": "#/components/schemas/BetPage"
              }
            }
          }
        ]
      },
      "CallbackResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OpenApiResponse"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "$ref": "#/components/schemas/CallbackAck"
              }
            }
          }
        ]
      },
      "MerchantCreditResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OpenApiResponse"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "$ref": "#/components/schemas/MerchantCredit"
              }
            }
          }
        ]
      },
      "StubResult": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string"
          },
          "dryRun": {
            "type": "boolean"
          },
          "note": {
            "type": "string"
          }
        }
      },
      "StubResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OpenApiResponse"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "$ref": "#/components/schemas/StubResult"
              }
            }
          }
        ]
      },
      "TransferResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/TransferOrderResponse"
          }
        ],
        "description": "YG canonical TransferResponse; monetary fields are decimal strings."
      },
      "OrderResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/TransferOrderResponse"
          }
        ],
        "description": "YG canonical OrderResponse; monetary fields are decimal strings."
      },
      "BetRecordResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/BetPageResponse"
          }
        ],
        "description": "YG canonical BetRecordResponse; monetary fields are decimal strings."
      },
      "WalletResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/BalanceResponse"
          }
        ],
        "description": "YG canonical WalletResponse; monetary fields are decimal strings."
      }
    },
    "responses": {
      "Success": {
        "description": "CURRENT. HTTP 200 response. Business success still requires response.code = 0.",
        "x-yg-implementation-status": "CURRENT",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiResponse"
            }
          }
        }
      },
      "BadRequest": {
        "description": "PLANNED transport mapping. The current backend normally returns HTTP 200 with business code 10001 for malformed JSON, missing fields, or invalid parameters.",
        "x-yg-implementation-status": "PLANNED",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "example": {
              "code": 10001,
              "message": "invalid request",
              "data": null,
              "requestId": "req_yg_example",
              "timestamp": 1735689600000
            }
          }
        }
      },
      "Unauthorized": {
        "description": "PLANNED transport mapping. The current backend returns HTTP 200 with code 10002 or 10004 for invalid signature or merchant authentication.",
        "x-yg-implementation-status": "PLANNED",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "code": 10004,
              "message": "unauthorized",
              "data": null,
              "requestId": "req_yg_example",
              "timestamp": 1735689600000
            }
          }
        }
      },
      "Forbidden": {
        "description": "PLANNED transport mapping. The current backend returns HTTP 200 with code 10005 when Origin, IP, scope, or endpoint policy denies a request.",
        "x-yg-implementation-status": "PLANNED",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "code": 10005,
              "message": "forbidden",
              "data": null,
              "requestId": "req_yg_example",
              "timestamp": 1735689600000
            }
          }
        }
      },
      "NotFound": {
        "description": "CURRENT. Disabled or missing OpenAPI routes return HTTP 404 with code 10006. Some service-level missing resources may still use HTTP 200 with code 10006.",
        "x-yg-implementation-status": "CURRENT",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "code": 10006,
              "message": "not found",
              "data": null,
              "requestId": "req_yg_example",
              "timestamp": 1735689600000
            }
          }
        }
      },
      "Conflict": {
        "description": "PLANNED transport mapping. The current backend returns HTTP 200 with code 10007 for idempotency conflicts.",
        "x-yg-implementation-status": "PLANNED",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "code": 10007,
              "message": "idempotent conflict",
              "data": null,
              "requestId": "req_yg_example",
              "timestamp": 1735689600000
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "CURRENT. Rate limit rejection returns HTTP 429 with code 10008. Retry-After is not currently emitted; apply bounded exponential backoff with jitter.",
        "x-yg-implementation-status": "CURRENT",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RateLimitError"
            },
            "example": {
              "code": 10008,
              "message": "rate limited",
              "data": null,
              "requestId": "req_yg_example",
              "timestamp": 1735689600000
            }
          }
        }
      },
      "InternalServerError": {
        "description": "PLANNED application transport mapping. The current OpenAPI exception handler normally returns HTTP 200 with code 99999. Infrastructure may still produce a non-envelope HTTP 500.",
        "x-yg-implementation-status": "PLANNED",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "code": 99999,
              "message": "system error",
              "data": null,
              "requestId": "req_yg_example",
              "timestamp": 1735689600000
            }
          }
        }
      }
    }
  },
  "x-yg-dynamic-data-policy": {
    "games": "GET /openapi/v1/meta/games",
    "vendors": "GET /openapi/v1/meta/vendors",
    "currencies": "GET /openapi/v1/meta/currencies",
    "languages": "GET /openapi/v1/meta/languages",
    "maintenance": "GET /openapi/v1/meta/maintenance"
  },
  "x-yg-canonical-signing-policy": {
    "method": "Uppercase HTTP method.",
    "path": "RFC 3986 path only; excludes scheme, authority, fragment, and query.",
    "query": "Send the URL query with RFC 3986 UTF-8 encoding. For signing, the current backend decodes parameters once using the servlet UTF-8 query parser, removes the sign parameter case-insensitively, sorts decoded names and then repeated decoded values, joins decoded key=value pairs with &, and uses an empty string when absent. Current query values are restricted to API-defined code/time/page tokens and must not contain decoded & or = characters.",
    "body": "For application/json, serialize UTF-8 JSON exactly as transmitted with no BOM; hash raw bytes with SHA-256 lowercase hex. Empty body hash is SHA-256 of zero bytes: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.",
    "timestamp": "Unix epoch milliseconds; decimal digits only.",
    "nonce": "Unique unpredictable value per merchant and request; 16-64 visible ASCII characters; never reuse within the replay window.",
    "sign": "HMAC-SHA256 over canonical string; output lowercase hexadecimal; compare case-insensitively only for migration, emit lowercase.",
    "canonicalOrder": [
      "METHOD",
      "PATH",
      "CANONICAL_QUERY",
      "BODY_SHA256",
      "TIMESTAMP",
      "NONCE",
      "MERCHANT_ID"
    ]
  },
  "x-yg-http-status-policy": {
    "current": "Most validation and business failures return HTTP 200 with a non-zero business code. Disabled/missing OpenAPI routes use HTTP 404 with code 10006; rate limiting uses HTTP 429 with code 10008.",
    "planned": "HTTP 400/401/403/409/500 application mappings are documented as PLANNED components and are not attached to current operations.",
    "businessResult": "Always inspect both HTTP status and response.code; code=0 means business success.",
    "document": "docs/openapi/YG_OPENAPI_HTTP_STATUS_POLICY.md"
  },
  "x-yg-callback-policy": {
    "implementationStatus": "PLANNED",
    "direction": "Future production callback direction is YG to merchant callback URL. The current /callbacks/sample operation is Sandbox inbound acknowledgement only.",
    "signature": "A production outbound callback signature/retry contract is not yet enabled or frozen.",
    "timeoutAndRetry": "Not yet confirmed; consumers must not treat any retry count or schedule as supported.",
    "idempotencyKey": "eventId",
    "successResponse": {
      "code": 0,
      "message": "OK"
    },
    "sourceIp": "Validate only against YG-published callback egress IP ranges; do not trust arbitrary forwarded headers."
  },
  "x-yg-rate-limit-policy": {
    "defaultQps": 10,
    "burst": 20,
    "metaCacheSeconds": 300,
    "timeoutMs": 5000,
    "retry": "Use bounded exponential backoff with jitter. Retry-After is not currently emitted.",
    "orderQuery": "On timeout/5xx for transfers, query order status by orderNo before resubmitting."
  }
}
