주문 서버 DB 스키마

사용자 앱 → 주문 서버 → 요기요 API 자동 대행을 위한 DB 설계. 관리자 계정/카드/디바이스 정보는 .env 로 분리.

  • 필수 4개 테이블: external_orders, order_items, yogiyo_orders, api_call_logs
  • 선택 2개: auth_tokens (Redis 대체 가능), carts (재시도용)
  • 출처 색상 코딩: 어디서 온 데이터인지 한눈에 확인 — 사용자 앱 입력, 좌표 변환, 각 요기요 API 응답

개요

주문 1건당 데이터의 흐름:

[사용자 앱] POST /api/orders        → external_orders + order_items
[좌표 변환]  주소 → lat/lng           → external_orders.delivery_lat/lng
[요기요 API] Step 1~7 자동 호출       → yogiyo_orders + api_call_logs
[상태 확인] 외부 앱이 결과 조회       → yogiyo_orders.status

요기요 API 호출 단계 (Step 1 ~ get-order)

주문 서버가 1건의 주문을 처리하면서 순차적으로 호출하는 요기요 API. 본 문서에서 "Step N" 으로 참조되는 단계의 정의.

1
refresh — 액세스 토큰 갱신
POST authyo.yogiyo.co.kr/api/v1/auth/refresh?customer_id={customer_id}
입력: .envYGY_REFRESH_TOKEN · 역할: 1시간 만료되는 access_token 새로 발급. 캐시된 토큰이 유효하면 호출 생략.
→ 저장: auth_tokens(ACCESS).token_value, expires_at
2
customer-info — 회원 정보 + WebView 인증 토큰
GET memberyo.yogiyo.co.kr/v3/customers/{customer_id}
입력: access_token · 역할: 관리자 계정의 회원 정보(이메일/전화/주소) + 1초결제 WebView 가 쓰는 별도의 RS256 JWT(authyo_token, 2시간 만료) 받기. Step 6 의 submit body 안 customer 필드 구성에 사용.
→ 저장: auth_tokens(AUTHYO), carts.customer_payload
3
create-cart — 카트 생성 (메뉴 담기)
POST api.yogiyo.co.kr/cart/v3/food/customers/{customer_id}/restaurants/{vendor_id}/{vd|od}/
입력: lat, lng, products[]{item_id, quantity, options}, coupon, referral · 역할: 요기요 서버에 새 카트 생성. cart_uuidcart_signature 를 발급받음. 여기서부터 사용자 주소(lat/lng) 가 매장 배달 범위와 비교됨 — 범위 초과 시 에러.
→ 저장: carts.cart_uuid, carts.cart_signature
4
read-cart — 카트 검증 + 최종 금액 확정
GET api.yogiyo.co.kr/cart/v3/food/{cart_uuid}-b24b/{vd|od}/?customer_id&lat&lng
입력: cart_uuid · 역할: 카트 재조회로 최종 결제 금액 확정(음식+배달비). 사용자 앱이 보낸 expected_total 과 비교해 ±오차 범위 초과 시 주문 중단.
→ 저장: carts.cart_total, carts.delivery_fee, external_orders.actual_total
5a
vendor-info — 매장 메타 조회
GET api.yogiyo.co.kr/order/checkout/vendors/{vendor_id}
입력: vendor_id · 역할: 매장 정보(이름, franchise_id 등) 조회. 응답 JSON 전체를 Step 6 submit body 의 vendor 필드에 그대로 넣어야 함.
→ 저장: carts.vendor_payload
5b
card-info 선택 — 등록 카드 검증 (env 사용 시 생략 가능)
GET payo.yogiyo.co.kr/v2/payments/card-info?customer_id={customer_id}
역할: 관리자 계정에 등록된 1초결제 카드 목록 + 토큰 조회. .envYGY_CARD_TOKEN 이 살아있는지 주기적으로 확인할 때만 호출.
6
⭐ zero-payment submit — 실제 결제 발생 (1초결제 진입점)
POST api.yogiyo.co.kr/order/cart/submit/food/{customer_id}/{lat}/{lng}
입력: cart_uuid, cart_signature, customer(Step 2), vendor(Step 5a), delivery_info, payment{ygypay_info.token=env 의 카드토큰}, agreements{efinance:true, tos:true} · 역할: 실제 결제 트리거. 등록된 1초결제 카드 사용 시 PG redirect 없이 즉시 결제 완료 → purchase_id, order_number 응답.
→ 저장: yogiyo_orders.{order_number, payment_no, purchase_id, total_paid, payment_url}
7
PG follow 선택 — KG이니시스 PG 체인 (zero payment 면 미발생)
GET payo.yogiyo.co.kr/v2/payments/{payment_no}/checkoutwpay.inicis.comorderyo.yogiyo.co.kr/callback/payment/result/
입력: Step 6 응답의 payment_url · 역할: 등록 카드 첫 사용 등 신규 결제 수단일 때만 발생하는 PG redirect chain. 1초결제 zero payment 응답에는 payment_url 이 없어 이 단계 자체가 생략됨.
→ 저장: yogiyo_orders.ygy_order_id (있을 때만)
get-order — 주문 활성화 확인
GET api.yogiyo.co.kr/order/api/v2/orders/{order_number}/
입력: Step 6 응답의 order_number · 역할: 주문이 매장 측에 ACTIVE 로 도달했는지 확인. 결제 직후 1~3초 폴링하여 상태 확정. 실패 시 cancel-order 호출.
→ 저장: yogiyo_orders.status, raw_response, external_orders.status=COMPLETED
중요: Step 1 과 Step 2 의 토큰은 캐시되어 매 주문마다 호출하지 않음. Step 5a/Step 3/Step 4/Step 6/get-order 는 주문 1건마다 반드시 순서대로 호출. Step 6 만 실제 결제를 발생시키므로, Step 6 직전까지의 검증 실패는 결제 없이 안전하게 중단 가능.

저장소 분리

.env (정적 시크릿)

운영자가 직접 관리, 코드 배포 시 주입

  • YGY_CUSTOMER_ID
  • YGY_REFRESH_TOKEN
  • YGY_DEVICE_ID
  • YGY_PHONE_UUID
  • YGY_CARD_CODE
  • YGY_CARD_TOKEN
  • KAKAO_GEOCODE_API_KEY

DB (동적 데이터)

주문별 영속 데이터

  • external_orders
  • order_items
  • yogiyo_orders
  • api_call_logs

Redis (단기 캐시, 선택)

TTL 기반, 재시작 시 손실 OK

  • auth:access_token (1h)
  • auth:authyo_token (2h)
  • cart:{order_id} (30m)

출처 색상 범례

사용자 앱 입력 좌표 변환 요기요 API 응답 FK 참조 서버 상태머신 auto / 코드 기록

필수 테이블

1
external_orders
사용자 앱이 보낸 주문 요청 + 서버 진행 상태 (메인 테이블, 주문당 1 row)
컬럼 타입 출처 예시값
idBIGINT PKauto1001
external_ref UNIQUEVARCHAR사용자 앱 입력 · 멱등성 키"ext-20260512-0001"
external_user_idVARCHAR사용자 앱 입력"app_user_12345"
vendor_idBIGINT사용자 앱 입력 · 요기요 식당 코드1234567
serving_typeENUM사용자 앱 입력'vd' / 'od'
delivery_addressVARCHAR사용자 앱 입력"서울시 강남구 역삼동 123-45"
delivery_detailVARCHAR사용자 앱 입력"101동 202호"
delivery_latDECIMAL(10,7)좌표 변환 · 주소 → lat37.5012345
delivery_lngDECIMAL(10,7)좌표 변환 · 주소 → lng127.0396789
recipient_phoneVARCHAR사용자 앱 입력"01012345678"
delivery_requestTEXT사용자 앱 입력"문 앞에 놔주세요"
store_requestTEXT사용자 앱 입력"덜 맵게 해주세요"
expected_totalINTEGER사용자 앱 입력 · 검증용23000
actual_totalINTEGERGET api.yogiyo.co.kr/cart/v3/food/{uuid}-b24b/{vd|od}/23000
coupon_codeVARCHAR사용자 앱 입력 · 선택null
client_app_versionVARCHAR사용자 앱 입력"1.0.0"
client_platformVARCHAR사용자 앱 입력"ios" / "android"
statusENUM서버 상태머신'RECEIVED' → ... → 'COMPLETED'
current_stepTINYINT서버 상태머신 · 재시도 위치0~7
failure_reasonTEXT서버 상태머신 · 실패 시"out_of_range" / null
retry_countINTEGER서버 상태머신0
received_atTIMESTAMPauto2026-05-12 14:30:00
submitted_atTIMESTAMP코드 기록 · Step 6 완료 시2026-05-12 14:30:08
completed_atTIMESTAMP코드 기록 · 활성화 확인 시2026-05-12 14:30:12
2
order_items
주문 메뉴 항목 (주문당 메뉴 수만큼 N rows). 카트 생성 시 products[] 로 직결.
컬럼 타입 출처 예시값
idBIGINT PKauto5001
external_order_idBIGINTFK external_orders.id1001
item_idBIGINT사용자 앱 입력 · 요기요 menu item_id9877
item_nameVARCHAR사용자 앱 입력 · 표시용"김치찌개"
quantityINTEGER사용자 앱 입력2
unit_priceINTEGER사용자 앱 입력9000
optionsJSON사용자 앱 입력[{"id":11,"name":"공기밥","price":1000}]
per_item_requestTEXT사용자 앱 입력 · 선택""
subtotalINTEGER서버 계산 · unit_price×quantity + Σ options20000
3
yogiyo_orders
요기요 결제 결과 (성공 주문당 1 row). 외부 앱의 주문 상태 조회 대상.
컬럼 타입 출처 예시값
idBIGINT PKauto1
external_order_id UNIQUEBIGINTFK external_orders.id1001
order_number UNIQUEVARCHARPOST api.yogiyo.co.kr/order/cart/submit/food/{id}/{lat}/{lng}"F2605121430JXXX4B"
payment_noVARCHARPOST api.yogiyo.co.kr/order/cart/submit/food/{id}/{lat}/{lng}"20260512143005XXXXXX"
purchase_idBIGINTPOST api.yogiyo.co.kr/order/cart/submit/food/{id}/{lat}/{lng}147296710660
ygy_order_idBIGINTorderyo.yogiyo.co.kr/callback/payment/result/ 또는 GET api.yogiyo.co.kr/order/api/v2/orders/{order_number}/1459590930
payment_methodVARCHAR코드 상수'YGYPAY'
used_card_codeCHAR(2).env (YGY_CARD_CODE)"01"
total_paidINTEGERPOST api.yogiyo.co.kr/order/cart/submit/food/{id}/{lat}/{lng}23000
payment_urlTEXTPOST api.yogiyo.co.kr/order/cart/submit/food/{id}/{lat}/{lng} · zero payment 시 nullnull
statusENUMGET api.yogiyo.co.kr/order/api/v2/orders/{order_number}/'PAID' → 'ACTIVE' → 'DELIVERED'
paid_atTIMESTAMP코드 기록 · Step 6 성공 시2026-05-12 14:30:08
active_atTIMESTAMP코드 기록 · ACTIVE 도달 시2026-05-12 14:30:12
cancelled_atTIMESTAMP코드 기록 · 취소 시null
raw_responseJSONGET api.yogiyo.co.kr/order/api/v2/orders/{order_number}/ 응답 전체{"order_number":"...","items":[...]}
4
api_call_logs
요기요 API 호출 로그 (호출당 1 row, 주문당 7~10 rows). 디버깅 + 감사용.
컬럼 타입 출처 예시값
idBIGINT PKauto9001
external_order_idBIGINT NULLFK external_orders.id · 토큰 갱신 시 null 가능1001
stepENUM코드 상수'CREATE_CART' / 'SUBMIT' / ...
methodVARCHAR코드 기록'POST'
urlTEXT코드 기록"https://api.yogiyo.co.kr/cart/v3/food/customers/..."
req_headersJSON코드 기록 · 민감값 마스킹{"Authorization":"Bearer <REDACTED>"}
req_bodyJSON코드 기록 · 카드토큰 마스킹{"lat":37.5012,"products":[...]}
resp_statusINTEGER호출 대상 응답200
resp_bodyJSON호출 대상 응답 · 민감값 마스킹{"customer_cart":{...}}
duration_msINTEGER서버 측정342
errorTEXT코드 기록 · 실패 시null / "timeout"
created_atTIMESTAMPauto · INDEX2026-05-12 14:30:03.123

선택 테이블

5
auth_tokens 선택
access/authyo 토큰 캐시. Redis 로 대체 가능 (TTL 자동 처리).
컬럼 타입 출처 예시값
idBIGINT PKauto1
token_type UNIQUEENUM코드 상수'ACCESS' / 'AUTHYO'
token_valueTEXT ENCRYPTEDPOST authyo.yogiyo.co.kr/api/v1/auth/refresh?customer_id={id} (ACCESS)
또는 GET memberyo.yogiyo.co.kr/v3/customers/{id} (AUTHYO)
"eyJhbGciOiJSUzI1NiIs..."
issued_atTIMESTAMP코드 기록2026-05-12 14:30:00
expires_atTIMESTAMPJWT exp claim 파싱 · ACCESS=1h, AUTHYO=2h2026-05-12 15:30:00
6
carts 선택
카트 생성 ~ 결제 submit 사이의 중간 상태. Step 6 실패 시 Step 3 부터 재시작 안 하고 재사용. TTL 짧으므로 Redis 도 적합.
컬럼 타입 출처 예시값
idBIGINT PKauto1
external_order_id UNIQUEBIGINTFK external_orders.id1001
cart_uuidVARCHARPOST api.yogiyo.co.kr/cart/v3/food/customers/{id}/restaurants/{vid}/{vd|od}/ · raw uuid"a1b2c3d4-e5f6-..."
cart_signatureVARCHARPOST api.yogiyo.co.kr/cart/v3/food/customers/{id}/restaurants/{vid}/{vd|od}/ · sha1 hex"abc123def456..."
cart_totalINTEGERGET api.yogiyo.co.kr/cart/v3/food/{uuid}-b24b/{vd|od}/23000
delivery_feeINTEGERGET api.yogiyo.co.kr/cart/v3/food/{uuid}-b24b/{vd|od}/3000
vendor_payloadJSONGET api.yogiyo.co.kr/order/checkout/vendors/{vid} · submit body 재구성용{"id":1234567,"name":"...",...}
customer_payloadJSONGET memberyo.yogiyo.co.kr/v3/customers/{id} · submit body 의 customer 필드{"id":21620000,"email":"...",...}
expires_atTIMESTAMP코드 기록 · 보통 +30분2026-05-12 15:00:00
stateENUM서버 상태머신'CREATED' / 'VERIFIED' / 'SUBMITTED'
created_atTIMESTAMPauto2026-05-12 14:30:03

1주문 흐름 — 시간별 DB 변화

외부 앱 요청 도착 후 약 3초 안에 모든 단계 완료.

T+0.00s
외부 앱 POST /api/orders
INSERT external_orders (status='RECEIVED') + order_items N rows
T+0.05s
서버 검증 + 좌표 변환
UPDATE external_orders (lat/lng, status='VALIDATED')
T+0.10s
Step 1: refresh (토큰 만료 시만)
UPSERT auth_tokens(ACCESS) + INSERT api_call_logs
T+0.30s
Step 2: customer-info
UPSERT auth_tokens(AUTHYO) + carts.customer_payload + log
T+0.50s
Step 5a: vendor-info
UPDATE carts.vendor_payload + log
T+1.00s
Step 3: create-cart
INSERT carts (state='CREATED') + log
T+1.30s
Step 4: read-cart + 금액 검증
UPDATE carts (cart_total, state='VERIFIED') + log
T+1.50s
Step 6: zero-payment submit ⭐
INSERT yogiyo_orders (status='PAID') + UPDATE carts (state='SUBMITTED') + log
T+3.00s
get-order ACTIVE 확인
UPDATE yogiyo_orders (status='ACTIVE', raw_response) + UPDATE external_orders (status='COMPLETED') + log
실패 시: 어느 단계에서 실패하든 external_orders.failure_reason 기록 + status='FAILED' + current_step 으로 재시도 위치 표시. 재시도 시 carts 가 살아 있으면 Step 6 부터 재개 가능.

요기요 API 출처 ↔ 컬럼 매핑

각 요기요 API 응답에서 어느 컬럼으로 들어가는지 역참조.

요기요 API 응답 저장 위치
POST authyo.yogiyo.co.kr/api/v1/auth/refresh?customer_id={id} auth_tokens(ACCESS)
GET memberyo.yogiyo.co.kr/v3/customers/{id} auth_tokens(AUTHYO) · carts.customer_payload
GET api.yogiyo.co.kr/order/checkout/vendors/{vendor_id} carts.vendor_payload
POST api.yogiyo.co.kr/cart/v3/food/customers/{id}/restaurants/{vid}/{vd|od}/ carts.cart_uuid · carts.cart_signature
GET api.yogiyo.co.kr/cart/v3/food/{uuid}-b24b/{vd|od}/ carts.cart_total · carts.delivery_fee · external_orders.actual_total
POST api.yogiyo.co.kr/order/cart/submit/food/{id}/{lat}/{lng} yogiyo_orders.order_number · payment_no · purchase_id · total_paid · payment_url
orderyo.yogiyo.co.kr/callback/payment/result/ (PG callback) yogiyo_orders.ygy_order_id (선택)
GET api.yogiyo.co.kr/order/api/v2/orders/{order_number}/ yogiyo_orders.status · ygy_order_id · raw_response