주요 콘텐츠로 건너뛰기

스토어 Get 웹훅

store.get 웹훅은 Game Hub 스토어에서 플레이어가 이용 가능한 아이템 목록을 불러옵니다. Aghanim은 스토어 방문 시마다 귀하의 서버를 호출하고, 그 응답을 기반으로 스토어를 실시간으로 렌더링합니다.

이 웹훅은 다음과 같은 경우에 트리거됩니다:

  • 플레이어가 Game Hub에 로그인할 때(프리페치).
  • 플레이어가 스토어를 열 때.
  • 플레이어가 구매를 할 때.
Get store webhook
Get store webhook

Integration modes

store.get은 세 가지 레벨에서 작동할 수 있습니다. 귀하의 설정에 맞는 레벨을 선택하세요 — 더 깊은 레벨이 항상 더 나은 것은 아닙니다.

ModeSKU 설정제어할 수 있는 항목
Layer 1Aghanim 대시보드에 저장플레이어별로 어떤 SKU를 어떤 순서로 표시할지
Layer 2Aghanim 대시보드에 저장특정 필드 재정의: 가격, 이름, 이미지, 번들 구성
Layer 3완전히 동적 — Aghanim에 아무것도 저장하지 않음모든 필드를 요청마다 실시간으로 전송

Layer 1 은 가장 가벼운 통합 방식이며 권장되는 시작점입니다. SKU와 그 시각적 구성은 Aghanim에 저장되며, 귀하는 플레이어별로 개인화된 목록과 표시 순서를 반환합니다. 이 레벨에서는 모든 Aghanim 네이티브 기능이 완전하게 작동합니다.

Layer 2 는 동일한 SKU가 사용자 컨텍스트에 따라 다른 파라미터를 필요로 할 때 — 예를 들어 진행도 기반 가격 책정, 구매 이력을 고려한 번들 구성, 사용자별 이미지 등 — 적합한 선택입니다. SKU는 여전히 Aghanim에 존재하며, 귀하는 플레이어별로 변경이 필요한 필드만 재정의합니다.

Layer 3 은 완전한 제어권을 제공하며, 개별 사용자 레벨에서 모든 수익화 로직을 관리하는 완전히 구축된 LiveOps 백엔드를 이미 보유한 팀에 적합합니다. 핵심 신호는 속도입니다. 오퍼가 몇 시간 내에 변하는 게임 내 상태를 반영해야 하는 경우입니다. 이 레벨에서는 Aghanim에 폴백 구성이 없으므로 모든 아이템에 pricename이 필수입니다. Layer 3은 귀하의 인프라를 Hub의 단일 장애점으로 만든다는 점에 유의하세요 — 백엔드 성능 저하는 스토어 가용성에 직접적인 영향을 미칩니다. 이 모드를 선택하기 전에 Limitations를 참조하세요.

store.get을 아예 사용하지 말아야 할 경우. 목표가 단순히 게임 내 카탈로그를 Hub에 그대로 반영하는 것이라면 store.get은 적합한 도구가 아닙니다. Hub의 가치는 게임 내에서 이미 이용 가능한 것을 복제하는 데서가 아니라, 독점성과 플레이어를 위한 추가 가치에서 나옵니다. 실제로는 상위 1015%의 SKU가 웹 상점 수익의 8090%를 창출합니다. 잘 구성된 오퍼 세트가 전체 카탈로그보다 더 나은 성과를 냅니다.

경고

익명 사용자. event_data에서 is_anonymous: true인 경우, 빈 items 배열과 함께 200 OK를 반환하세요. 4xx5xx를 반환하지 마세요 — 이는 Hub에서 오류를 일으킵니다.

요구 사항

Aghanim의 store.get 웹훅을 사용하려면 웹훅 서버를 다음과 같이 구성해야 합니다:

  • POST 웹훅 요청을 수락하는 HTTPS 엔드포인트.
  • Aghanim이 생성하고 서명한 이벤트를 수신합니다.
  • 성공적으로 처리되었음을 알리려면 2xx 상태 코드로, 거부 또는 오류의 경우 4xx 또는 5xx로 응답합니다.
  • 500ms 이내에 응답합니다. 응답이 느리면 Hub 경험이 저하됩니다.

구성

다음은 각 플레이어에게 개인화된 아이템 목록을 반환하는 store.get 웹훅을 처리하는 엔드포인트의 함수 템플릿입니다:

import fastapi, hashlib, hmac, json, typing
from fastapi.responses import JSONResponse

app = fastapi.FastAPI()

@app.post("/webhook")
async def webhook(request: fastapi.Request) -> dict[str, typing.Any]:
secret_key = "<YOUR_S2S_KEY>" # 실제 웹훅 비밀 키로 교체하세요

raw_payload = await request.body()
payload = raw_payload.decode()
timestamp = request.headers["x-aghanim-signature-timestamp"]
received_signature = request.headers["x-aghanim-signature"]

if not verify_signature(secret_key, payload, timestamp, received_signature):
raise fastapi.HTTPException(status_code=403, detail="Invalid signature")

data = json.loads(payload)
event_type = data["event_type"]
event_data = data["event_data"]

if event_type == "store.get":
if event_data.get("is_anonymous"):
return {"items": []}
items = get_store_items(event_data["player_id"])
return {"items": items}

raise fastapi.HTTPException(status_code=400, detail="Unknown event type")

def verify_signature(secret_key: str, payload: str, timestamp: str, received_signature: str) -> bool:
signature_data = f"{timestamp}.{payload}"
computed_hash = hmac.new(secret_key.encode(), signature_data.encode(), hashlib.sha256)
computed_signature = computed_hash.hexdigest()
return hmac.compare_digest(computed_signature, received_signature)

def get_store_items(player_id: str) -> list[dict]:
# Placeholder logic for fetching store items for this player.
# In a real application, this function would interact with your database or merchandising system.
return [
{
"sku": "starter_bundle",
"price": 999,
"name": "Starter Bundle",
"card_type": "featured",
"category_slugs": ["special-offers"],
"nested_items": [
{
"sku": "coins",
"name": "Coins",
"quantity": 500,
"image_url": "https://cdn.example.com/coins.png",
}
],
}
]

  1. store.get 웹훅 처리를 위한 함수를 개발합니다.
  2. 엔드포인트를 사용 가능하게 설정합니다.
  3. Aghanim 계정 내에서 GameWebhooksAdd webhook에서 store.get 이벤트 유형을 선택하여 엔드포인트를 등록합니다.

또는, Create Webhook API 방법을 사용하여 Aghanim 내에서 엔드포인트를 등록할 수 있습니다.

트리거 값

설명
hub.login플레이어가 Game Hub를 열 때. 로그인 시 스토어를 프리페치합니다.
hub.store.open스토어가 열릴 때.
hub.purchase플레이어가 구매 버튼을 클릭할 때 — 아이템이 여전히 사용 가능한지 확인하기 위해.
order.captured결제 처리가 시작되기 직전 — 아이템이 여전히 사용 가능한지 확인하기 위해.
testDashboard에서 "Send test event"를 사용할 때.

store.get이 다른 이벤트 유형과 어떻게 관련되는지에 대해서는 전체 이벤트 및 트리거 매트릭스를 참조하세요.

요청 스키마

아래는 예시입니다 store.get 웹훅 요청:

POST /your/webhook/uri HTTP/1.1
Content-Type: application/json
Host: your-webhook-endpoint.com
User-Agent: Aghanim/0.1.0
X-Aghanim-Signature: 2e45ed4dede5e09506717490655d2f78e96d4261040ef48cc623a780bda38812
X-Aghanim-Signature-Timestamp: 1725548450

{
"event_type": "store.get",
"event_data": {
"player_id": "2D2R-OP3C",
"is_anonymous": false,
"placement_keys": null,
"category_slugs": null,
"current_page_path": "/store",
"locale": "en"
},
"event_time": 1725548450,
"event_id": "whevt_eCacGbJVbvToOgzjXUgOCitkQE",
"idempotency_key": "idmpt_aXRlb...JkX2VFS",
"request_id": "d1593e9c-c291-4004-8846-6679c2e5810b",
"sandbox": false,
"trigger": "hub.store.open",
"transaction_id": "whtx_eCacGbJVbvT",
"context": null,
"game_id": "gm_exTAyxPsVwh"
}

이벤트 스키마

Key유형설명
event_idstringAghanim에 의해 생성된 고유 이벤트 ID.
game_idstringAghanim 시스템에서의 귀하의 게임 ID.
event_typestring이벤트의 유형, store.get 이럴 경우.
event_timenumber유닉스 에포크 시간으로 된 이벤트 날짜.
event_dataEventData이벤트 특정 데이터가 포함되어 있으며, 상속된 객체에 대한 가능한 키가 포함됩니다.
idempotency_keystring웹훅 작업이 재시도되어도 한 번만 실행되도록 보장합니다.
request_idstring|null이벤트가 API 요청에 의해 트리거된 경우, 요청 ID가 포함됩니다.
sandboxboolean이 이벤트가 샌드박스 게임 환경에서 전송되었는지를 표시합니다.
triggerstring|nullThe trigger that caused the event to be sent.
transaction_idstringAghanim이 생성한 거래 ID입니다. 이 ID는 동일한 거래 내에서 발생한 여러 이벤트에서 동일할 수 있습니다.
contextEventContext|null이벤트에 대한 컨텍스트 정보.

EventContext 스키마

Key유형설명
orderOrderContext|null해당되는 경우 이벤트와 관련된 주문 정보입니다.
playerPlayerContext|null플레이어 정보를 추가하려면 웹훅 설정에서 "플레이어 컨텍스트 추가"를 활성화하세요.

EventData 스키마

Key유형설명
player_idstring플레이어 인증을 위해 선택된 고유한 플레이어 ID.
is_anonymousboolean현재 사용자가 인증되지 않음(익명)인지 여부를 나타냅니다.
placement_keysstring[]|nullstore.get이 요청된 위치의 placement key 목록입니다.
category_slugsstring[]|nullstore.get이 요청된 위치의 카테고리 slug 목록입니다.
current_page_pathstring|null현재 열려 있는 페이지의 경로로, Game Hub 루트를 기준으로 한 상대 경로입니다(예: /store/offers).
localestring현재 플레이어의 로케일 코드(ISO 639-1)입니다. 가능한 로케일의 전체 목록은 Locales에서 확인하세요.

응답 스키마

다음 JSON 페이로드와 함께 200 OK를 반환합니다.

Key유형설명필수 여부
items[Item|BundleItem]스토어에서 플레이어가 이용 가능한 아이템.아니오
rolling_offersRollingOffer[]플레이어에게 제공되는 롤링 오퍼.아니오

Item 스키마

노트

단독 제품(통화, 장비, 소모품)에는 Item을 사용하세요.

Key유형설명필수 여부
skustring고유한 SKU 식별자. Layer 1 및 2의 경우: Aghanim 대시보드의 SKU와 일치해야 합니다. Layer 3(완전히 동적)의 경우: pricename도 필수입니다.
pricenumberUSD 센트 단위 가격. Layer 3에서 필수.아니오
namestring아이템 이름. Layer 3에서 필수.아니오
is_stackableboolean아이템을 여러 번 구매하여 플레이어 인벤토리에 스택할 수 있는지 여부.아니오
quantitynumber아이템 수량.아니오
descriptionstring아이템 설명.아니오
image_urlstring메인 이미지 URL.아니오
background_image_urlstring배경 이미지 URL.아니오
background_image_colorstring배경색.아니오
image_url_featuredstring추천 아이템으로 표시될 때의 이미지 URL.아니오
card_background_image_urlstring아이템 카드의 배경 이미지 URL.아니오
category_slugsstring[]아이템이 스토어에서 표시되는 위치를 결정하는 카테고리 slug. Aghanim 대시보드에 구성된 카테고리와 일치해야 합니다.아니오
start_atnumber아이템이 사용 가능해지는 Unix 타임스탬프.아니오
end_atnumber아이템이 만료되는 Unix 타임스탬프.아니오
max_purchasesnumber허용되는 최대 구매 수. 도달하면 아이템이 스토어에서 사라집니다.아니오
current_purchasesnumber이 플레이어의 현재 구매 수.아니오
price_template_idstring국가별 가격 책정을 위한 가격 템플릿의 ID. 템플릿 이름이 아니라 /v1/price_templates의 내부 ID(예: ptm_eGhmGhfwjVL)를 사용하세요.아니오
custom_badgestring커스텀 배지 텍스트.아니오
bonus_itemsWebhookItemBonus[]|Item[]이 아이템에 포함된 보너스 아이템.아니오
bonus_badgestring보너스를 설명하는 배지 텍스트.아니오
bonus_percentnumbernested_items의 첫 번째 스택 가능 아이템에 적용되는 보너스 퍼센트.아니오
bonus_fixednumbernested_items의 첫 번째 스택 가능 아이템에 적용되는 고정 보너스 값. 설정된 경우 bonus_percent를 재정의합니다.아니오
reward_points_fixednumber구매 시 지급되는 고정 수량의 Reward Points. 설정된 경우 reward_points_percent를 재정의합니다.아니오
reward_points_percentnumber구매 시 지급되는 Reward Points의 비율.아니오
metadataobject추가 데이터를 위한 키-값 쌍.아니오
view_optionItemViewOption스토어 외관: default, in_title.아니오
card_typeStoreCardType카드 표시 유형: default, featured.아니오
free_claimsFreeClaims아이템을 무료로 수령할 수 있도록 하는 설정.아니오
show_disabled_by_max_purchasesboolean최대 구매 수에 도달한 후 아이템을 비활성화된 것으로 표시할지 여부.아니오

BundleItem 스키마

노트

여러 아이템을 포함하는 제품에는 BundleItem을 사용하세요. nested_items가 추가된 점을 제외하면 Item과 동일합니다.

Key유형설명필수 여부
skustring고유한 SKU 식별자. Layer 3의 경우: pricename도 필수입니다.
pricenumberUSD 센트 단위 가격. Layer 3에서 필수.아니오
namestring번들 이름. Layer 3에서 필수.아니오
nested_itemsNestedItem[]번들에 포함된 아이템.아니오
descriptionstring번들 설명.아니오
image_urlstring번들 이미지 URL.아니오
background_image_urlstring배경 이미지 URL.아니오
background_image_colorstring배경색.아니오
image_url_featuredstring추천 아이템으로 표시될 때의 이미지 URL.아니오
category_slugsstring[]스토어 배치를 위한 카테고리 slug. Aghanim 대시보드의 카테고리와 일치해야 합니다.아니오
start_atnumber번들이 사용 가능해지는 Unix 타임스탬프.아니오
end_atnumber번들이 만료되는 Unix 타임스탬프.아니오
max_purchasesnumber허용되는 최대 구매 수.아니오
price_template_idstring가격 템플릿의 내부 ID(예: ptm_eGhmGhfwjVL).아니오
custom_badgestring커스텀 배지 텍스트.아니오
bonus_itemsWebhookItemBonus[]|Item[]번들에 포함된 보너스 아이템.아니오
bonus_badgestring보너스를 설명하는 배지 텍스트.아니오
bonus_percentnumbernested_items의 첫 번째 스택 가능 아이템에 적용되는 보너스 퍼센트.아니오
bonus_fixednumbernested_items의 첫 번째 스택 가능 아이템에 적용되는 고정 보너스 값. 설정된 경우 bonus_percent를 재정의합니다.아니오
reward_points_fixednumber구매 시 지급되는 고정 Reward Points. 설정된 경우 reward_points_percent를 재정의합니다.아니오
reward_points_percentnumber구매 시 지급되는 Reward Points의 비율.아니오
metadataobject추가 데이터를 위한 키-값 쌍.아니오
view_optionItemViewOption스토어 외관: default, in_title.아니오
card_typeStoreCardType카드 유형: default, featured.아니오
free_claimsFreeClaims번들을 무료로 수령할 수 있도록 하는 설정.아니오
show_disabled_by_max_purchasesboolean최대 구매 수에 도달한 후 비활성화된 것으로 표시.아니오

NestedItem 스키마

경고

번들 내부의 아이템. nameimage_url은 기술적으로는 선택 사항이지만 올바른 표시를 위해 실제로는 필수입니다 — name이 없으면 번들은 Hub에서 중첩 아이템 없이 렌더링됩니다.

Key유형설명필수 여부
skustring게임과 Aghanim 양쪽에서 일치하는 아이템 SKU.
namestring번들 카드 내부 표시에 사용되는 아이템 이름. 실제로는 필수 — 생략하면 번들이 중첩 아이템 없이 렌더링됩니다.사실상 필수
quantitynumber아이템 수량. 스택 가능한 아이템에 해당.아니오
image_urlstring번들 카드 내부 표시에 사용되는 아이템 이미지 URL. 올바른 시각적 렌더링을 위해 실제로는 필수.사실상 필수
background_image_urlstring번들 카드 내부에서 이 아이템의 배경 이미지.아니오
is_featuredboolean이 아이템이 번들 목록에서 추천되는지 여부.아니오
metadataobject추가 데이터를 위한 키-값 쌍.아니오

WebhookItemBonus 스키마

Key유형설명필수 여부
skustring게임과 Aghanim 양쪽에서 일치하는 아이템 SKU.
quantitynumber보너스 아이템의 수량. 스택 가능한 아이템에 해당.아니오

FreeClaims 스키마

Key유형설명필수 여부
enabledboolean아이템을 무료로 수령할 수 있는지 여부.
max_claimsnumber기간 내에 아이템을 수령할 수 있는 최대 횟수.
periodPeriodmax_claims를 재설정하는 데 사용되는 기간 창. 생략된 경우 제한이 재설정되지 않습니다.아니오
exceeded_claims_behaviorExceededClaimsBehavior최대 수령 횟수에 도달한 후의 동작: hide — 스토어에서 제거합니다; disable_with_timer — 표시는 유지하되 카운트다운과 함께 비활성화됩니다.아니오

기간 스키마

Key유형설명필수 여부
unitPeriodType기간 유형: month, week, day, hour.
durationnumber지정된 단위로 표시된 기간(예: duration: 2, unit: day = 이틀 기간).

RollingOffer 스키마

노트

롤링 오퍼에는 Hub 페이지에 배치된 전용 블록이 필요합니다. placement_key는 해당 블록의 키와 일치해야 합니다.

Key유형설명필수 여부
keystring구매 또는 수령 상태를 추적하는 데 사용되는 고유 오퍼 키.
placement_keystring롤링 오퍼가 표시될 Hub 블록의 키.
namestring롤링 오퍼 제목.
descriptionstring롤링 오퍼 설명.
rolling_itemsRollingItem[]롤링 오퍼에 포함된 아이템.
background_image_urlstring배경 이미지 URL.아니오
background_sizestring배경 이미지 크기: contain, repeat, cover.아니오
expire_atnumber만료 Unix 타임스탬프. Hub는 카운트다운을 표시하고 도달하면 오퍼를 숨깁니다.아니오

RollingItem 스키마

Key유형설명필수 여부
skustring동적 번들 아이템의 고유 SKU 식별자.
quantitynumber아이템 수량. 스택 가능한 아이템에 해당.아니오
is_free_itemboolean아이템을 무료로 수령할 수 있는지 여부.아니오

Example responses

Layer 1 — SKU 목록만 반환

{
"items": [
{ "sku": "crystals" },
{ "sku": "shield" },
{ "sku": "starter_bundle" }
]
}

Layer 2 — 특정 필드 재정의

{
"items": [
{
"sku": "starter_bundle",
"price": 999,
"nested_items": [
{ "sku": "crystals", "quantity": 200 },
{ "sku": "shield", "quantity": 1 }
]
}
]
}

Layer 3 — 완전히 동적

{
"items": [
{
"sku": "fly_bundle",
"price": 2000,
"name": "Fly Bundle",
"description": "Fly Bundle Description",
"image_url": "https://example.com/fly-bundle.png",
"card_type": "featured",
"card_background_image_url": "https://example.com/bg.png",
"category_slugs": ["special-offers"],
"start_at": 1630000000,
"end_at": 1635000000,
"max_purchases": 1,
"nested_items": [
{
"sku": "crystals",
"name": "Crystals",
"quantity": 100,
"image_url": "https://example.com/crystals.png"
},
{
"sku": "shield",
"name": "Shield",
"quantity": 1,
"image_url": "https://example.com/shield.png"
}
]
}
]
}

익명 사용자

{
"items": []
}

Limitations

Layer 2와 Layer 3은 여러 Aghanim 네이티브 기능과 호환성 격차가 있습니다.

기능Layer 1Layer 2Layer 3
Abandoned Cart 리타게팅⚠️ Limited
Insufficient Funds 캠페인⚠️ Limited
Loyalty Program
Piggy Bank
Sequential Offers
동적 할인 / 보너스 레이어링⚠️ Limited⚠️ Limited

이러한 기능에 의존하는 경우 Layer 1 또는 하이브리드 방식(Aghanim 내 정적 SKU + Layer 1 개인화)을 사용하세요.

도움이 필요하신가요? 통합 팀 integration@aghanim.com으로 문의하세요.

도움이 필요하세요?
통합팀에 문의하십시오 integration@aghanim.com