[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6CBMFSWD2ZGA2EP2WXKJH5T":3,"skill-same:01M6CBMFSWD2ZGA2EP2WXKJH5T":55},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":29,"linkOnly":30,"markdownContent":31,"markdownBody":32,"frontMatter":33,"refName":34,"commitSha":35,"skillDirectory":36,"forksCount":14,"repositoryHtmlUrl":37,"repositoryHomepageUrl":6,"ownerHtmlUrl":38,"ownerBlogUrl":6,"ownerDescription":39,"updatedAt":40,"install":41,"resources":54},"01M6CBMFSWD2ZGA2EP2WXKJH5T","42-purchase-entitlements-grants",null,"Define grants and entitlement projection rules for the purchase system. Use when deciding what the server stores in entitlements, how products map to grants, and how restore or sync data is represented for clients.","devwinsoft\u002Fdevian","devwinsoft","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F22570160?v=4","01M6C6RR8W05NH8NPKQNCE3Q72","maoshy","01M6C6RRDJ5TJJCZXRTY801YDH",0,"MIT","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian-unity\u002F50-mobile-package\u002F30-purchase-system\u002F42-purchase-entitlements-grants\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28],"grants","42 purchase entitlements grants","sync","server","data","how","system","Rules","use","restore","2026-11-10 10:12:30",false,"---\nname: 42-purchase-entitlements-grants\ndescription: Define grants and entitlement projection rules for the purchase system. Use when deciding what the server stores in entitlements, how products map to grants, and how restore or sync data is represented for clients.\n---\n\n# 42-purchase-entitlements-grants — Grants & Entitlements Rules (Server-side)\n\nStatus: ACTIVE\nAppliesTo: v10\n\n> Purchase SSOT: `skills\u002Fdevian-unity\u002F50-mobile-package\u002F30-purchase-system\u002F03-ssot\u002FSKILL.md` (C, D 섹션)\n\n## 목적\n\n`verifyPurchase` 결과의 `grants[]`와 `entitlements\u002Fcurrent` 스냅샷을 서버에서 **어떤 규칙으로 계산\u002F갱신하는지**를 정본으로 고정한다.\n\n이 문서가 없으면 \"지급\" 정책이 구현 중 임의로 바뀔 수 있다.\n\n\n---\n\n\n## A. 입력 정본\n\n- `internal_product_id`가 정본이다.\n- 상품 메타는 SSOT에서 합의된 PURCHASE 테이블을 따른다:\n  - PURCHASE 테이블 경로\u002F정본은 컨텐츠 레이어 SSOT를 따른다.\n\n\n---\n\n\n## B. Grants 형식 (정본)\n\n`grants[]` 각 항목은 `{ type, id, amount }` 형태다.\n\n- `type: string` — `\"item\"` | `\"currency\"` (고정)\n- `id: string`\n- `amount: long` (`>= 0`만 허용)\n\nHard Rule:\n- `grants[]`는 inventory 지급 전용이며, 클라이언트 지급 입력으로 사용하지 않는다 (서버 informational).\n- 클라 지급은 `reward_group_id` 경로로 수행한다.\n- 권한\u002F플래그 변화는 `grants[]`로 표현하지 않고, `entitlements\u002Fcurrent` 스냅샷으로 처리한다.\n\nNOTE:\n- `type=item`의 `id`는 `item_id(pk)`를 의미한다(`itemUid` 없음).\n- `grants[]`에는 `options`가 포함되지 않는다. 아이템 `options`는 Inventory 내부 속성으로만 관리된다.\n\nNEEDS CHECK:\n- `id` 규칙(예: `gem`, `gold`, `item_sword_01`)을 확정해야 한다.\n\n\n---\n\n\n## C. Entitlements Snapshot (정본)\n\n서버는 `verifyPurchase` 처리 후 entitlements를 재계산하여 `\u002Fusers\u002F{uid}\u002Fentitlements\u002Fcurrent` 에 upsert 한다.\n\n> NOTE: `verifyPurchase` 응답의 `entitlementsSnapshot`은 optional이며, 현재 클라에 미포함이다 (03-ssot §C 참조).\n> 클라이언트가 entitlements를 동기화하려면 별도 `getEntitlements` Callable을 호출한다.\n\n최소 필드 (서버 Firestore `\u002Fusers\u002F{uid}\u002Fentitlements\u002Fcurrent`):\n- `ownedSeasonPasses: string[]` (`seasonPassId` 목록)\n- `rentals: object` (`rentalId -> expiresAtServerUtcMs`)\n- `currencyBalances: object`\n- `updatedAt: Timestamp`\n\n\n---\n\n\n## D. 상태별 처리 (정본)\n\n- `GRANTED`\n  - grants 생성\n  - entitlements 재계산 및 저장\n- `ALREADY_GRANTED`\n  - grants는 비우거나(권장) \"이미 지급\"을 나타내는 최소 응답\n  - entitlements는 저장된 값 반환 가능\n- `PENDING`\n  - 지급 금지 (grants 없음)\n  - entitlements 변경 금지\n- `REJECTED`\n  - 지급 금지\n- `REFUNDED` \u002F `REVOKED`\n  - 지급 금지 (grants 없음)\n  - entitlements 재계산 및 저장(철회 반영)\n\n\n---\n\n\n## DoD\n\nHard (must be 0)\n- [ ] `internal_product_id` → grants\u002Fentitlements 매핑이 \"정본 데이터(테이블)\" 기준임이 명시됐다.\n- [ ] `PENDING\u002FREJECTED`에서 지급 금지가 하드룰로 명시됐다.\n- [ ] entitlements\u002Fcurrent upsert 규칙이 명시됐다.\n\nSoft\n- [ ] 환불\u002F철회 시나리오(REFUNDED\u002FREVOKED) 재계산 예시 추가\n","# 42-purchase-entitlements-grants — Grants & Entitlements Rules (Server-side)\n\nStatus: ACTIVE\nAppliesTo: v10\n\n> Purchase SSOT: `skills\u002Fdevian-unity\u002F50-mobile-package\u002F30-purchase-system\u002F03-ssot\u002FSKILL.md` (C, D 섹션)\n\n## 목적\n\n`verifyPurchase` 결과의 `grants[]`와 `entitlements\u002Fcurrent` 스냅샷을 서버에서 **어떤 규칙으로 계산\u002F갱신하는지**를 정본으로 고정한다.\n\n이 문서가 없으면 \"지급\" 정책이 구현 중 임의로 바뀔 수 있다.\n\n\n---\n\n\n## A. 입력 정본\n\n- `internal_product_id`가 정본이다.\n- 상품 메타는 SSOT에서 합의된 PURCHASE 테이블을 따른다:\n  - PURCHASE 테이블 경로\u002F정본은 컨텐츠 레이어 SSOT를 따른다.\n\n\n---\n\n\n## B. Grants 형식 (정본)\n\n`grants[]` 각 항목은 `{ type, id, amount }` 형태다.\n\n- `type: string` — `\"item\"` | `\"currency\"` (고정)\n- `id: string`\n- `amount: long` (`>= 0`만 허용)\n\nHard Rule:\n- `grants[]`는 inventory 지급 전용이며, 클라이언트 지급 입력으로 사용하지 않는다 (서버 informational).\n- 클라 지급은 `reward_group_id` 경로로 수행한다.\n- 권한\u002F플래그 변화는 `grants[]`로 표현하지 않고, `entitlements\u002Fcurrent` 스냅샷으로 처리한다.\n\nNOTE:\n- `type=item`의 `id`는 `item_id(pk)`를 의미한다(`itemUid` 없음).\n- `grants[]`에는 `options`가 포함되지 않는다. 아이템 `options`는 Inventory 내부 속성으로만 관리된다.\n\nNEEDS CHECK:\n- `id` 규칙(예: `gem`, `gold`, `item_sword_01`)을 확정해야 한다.\n\n\n---\n\n\n## C. Entitlements Snapshot (정본)\n\n서버는 `verifyPurchase` 처리 후 entitlements를 재계산하여 `\u002Fusers\u002F{uid}\u002Fentitlements\u002Fcurrent` 에 upsert 한다.\n\n> NOTE: `verifyPurchase` 응답의 `entitlementsSnapshot`은 optional이며, 현재 클라에 미포함이다 (03-ssot §C 참조).\n> 클라이언트가 entitlements를 동기화하려면 별도 `getEntitlements` Callable을 호출한다.\n\n최소 필드 (서버 Firestore `\u002Fusers\u002F{uid}\u002Fentitlements\u002Fcurrent`):\n- `ownedSeasonPasses: string[]` (`seasonPassId` 목록)\n- `rentals: object` (`rentalId -> expiresAtServerUtcMs`)\n- `currencyBalances: object`\n- `updatedAt: Timestamp`\n\n\n---\n\n\n## D. 상태별 처리 (정본)\n\n- `GRANTED`\n  - grants 생성\n  - entitlements 재계산 및 저장\n- `ALREADY_GRANTED`\n  - grants는 비우거나(권장) \"이미 지급\"을 나타내는 최소 응답\n  - entitlements는 저장된 값 반환 가능\n- `PENDING`\n  - 지급 금지 (grants 없음)\n  - entitlements 변경 금지\n- `REJECTED`\n  - 지급 금지\n- `REFUNDED` \u002F `REVOKED`\n  - 지급 금지 (grants 없음)\n  - entitlements 재계산 및 저장(철회 반영)\n\n\n---\n\n\n## DoD\n\nHard (must be 0)\n- [ ] `internal_product_id` → grants\u002Fentitlements 매핑이 \"정본 데이터(테이블)\" 기준임이 명시됐다.\n- [ ] `PENDING\u002FREJECTED`에서 지급 금지가 하드룰로 명시됐다.\n- [ ] entitlements\u002Fcurrent upsert 규칙이 명시됐다.\n\nSoft\n- [ ] 환불\u002F철회 시나리오(REFUNDED\u002FREVOKED) 재계산 예시 추가\n",{"name":5,"description":7},"main","8cb5bd877a3a7004867d2f55697cb9e4ff2f1734","skills\u002Fdevian-unity\u002F50-mobile-package\u002F30-purchase-system\u002F42-purchase-entitlements-grants","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian","https:\u002F\u002Fgithub.com\u002Fdevwinsoft","Kim Hyong Joon","2026-04-12 18:44:10",{"source":8,"skillName":5,"commands":42,"prompt":52,"zipAvailable":53},[43,46,49],{"tool":44,"command":45},"npx","npx skills add devwinsoft\u002Fdevian --skill 42-purchase-entitlements-grants",{"tool":47,"command":48},"bunx","bunx skills add devwinsoft\u002Fdevian --skill 42-purchase-entitlements-grants",{"tool":50,"command":51},"pnpm","pnpm dlx skills add devwinsoft\u002Fdevian --skill 42-purchase-entitlements-grants","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian-unity\u002F50-mobile-package\u002F30-purchase-system\u002F42-purchase-entitlements-grants\u002FSKILL.md",true,[],[56,62,70,78,87,94],{"skillNo":57,"name":58,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":59,"categories":60,"keywords":61,"publishedAt":29},"01M6CBX3K2JJV38ANHVGCAT8A0","devian","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002FSKILL.md",[],[58],{"skillNo":63,"name":64,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":65,"categories":66,"keywords":67,"publishedAt":29},"01M6CBX210BFMM8AMN770J9WY8","90-project-archive","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F90-project-archive\u002FSKILL.md",[],[68,69],"archive","90 project archive",{"skillNo":71,"name":72,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":73,"categories":74,"keywords":75,"publishedAt":29},"01M6CBWZSGRR9R3YJZB3177667","30-env-management","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F30-env-management\u002FSKILL.md",[],[76,77],"management","30 env management",{"skillNo":79,"name":80,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":81,"categories":82,"keywords":83,"publishedAt":29},"01M6CBWYD3TGWBQV7V35551THF","19-page-push-send","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F20-operation\u002F19-page-push-send\u002FSKILL.md",[],[84,85,86],"push","page","19 page push send",{"skillNo":88,"name":89,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":90,"categories":91,"keywords":92,"publishedAt":29},"01M6CBWWRQ05A7604GHJKTPJTF","18-page-savedata","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F20-operation\u002F18-page-savedata\u002FSKILL.md",[],[85,93],"18 page savedata",{"skillNo":95,"name":96,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":97,"categories":98,"keywords":99,"publishedAt":29},"01M6CBWVH4S2VF1V7B2GBTESCT","17-page-deobfuscate","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F20-operation\u002F17-page-deobfuscate\u002FSKILL.md",[],[85,100],"17 page deobfuscate"]