# star365 Platform Deployment Kit

플랫폼별 signup hook 적용 가이드. 각 플랫폼 서버에서 `root@<host>` 로 SSH 후 해당 섹션 실행.

## 실제 배선 현황 (2026-06-24 Sprint1-P4 ① 검증)

> ⚠️ 정정: 아래는 **DB(kahan `gunja_news.platform_memberships`) + 소스 grep 으로 실증한 실제 상태**.
> 이전 버전의 "대상 9플랫폼" 표(vietnam-shop/mlm-platform/politics 등)는 **코드 미존재/오기재**라 폐기.

### ✅ signup hook 배선 완료 (9개, DB로 동작 실증)

| 플랫폼 | 서버 | hook 위치 | 패턴 |
|--------|-----|-----------|------|
| plus365 | hospital | `apps/web/.../api/sales/coord/signup/route.ts` | 인라인 fetch `/members/register` |
| safenet | kahan | `/opt/safenet-platform/lib/star365Signup.js` + routes/auth.js(3곳) | lib 헬퍼 |
| gws-platform | kahan | `/opt/gws-platform/lib/star365Signup.js` + routes/auth.js | lib 헬퍼 |
| finance | hospital | `central-membership.service.ts` ← auth.service.ts (POINT_PLATFORM=FINANCE) | NestJS 서비스 |
| legal | hospital | 동일 패턴 (POINT_PLATFORM=LEGAL) | NestJS 서비스 |
| pet | hospital | `modules/auth/auth.service.ts` + oauth.service.ts (PET) | NestJS 서비스 |
| assistant | hospital | `modules/auth/auth.service.ts` + oauth (ASSISTANT) | NestJS 서비스 |
| shop | hospital | `apps/api/.../auth.service.ts` 인라인 `/members/register` | 인라인 fetch |
| hospital | hospital | `apps/api-gateway/.../auth.service.ts` 인라인 | 인라인 fetch |

### ⚠️ 부분/미대상 (signup hook 아님)

| 플랫폼 | 서버 | 현황 |
|--------|-----|------|
| upfinance-card | kahan | `star365Bridge.js`는 카드발급 emit(card_issued, STA-12)만 — signup hook 아님 |
| clinic-emr | kahan | `routes/star365-token.js`는 SSO 토큰 발급만 — members/register 없음 |

### ❌ 코드 부재 (이전 README 오기재 — 재확인/폐기 대상)

`sjclinic`(hook 0건), `vietnam-shop`·`mlm-platform`·`politics`(서버에 디렉토리 없음).
신규로 진짜 필요하면 아래 방법 A/B/C 로 주입.

## 방법 A — 프론트엔드 스크립트 (가장 쉬움)

각 플랫폼의 가입 성공 페이지 HTML template 에 추가:

```html
<script src="https://docs.star365.site/sdk.js"></script>
<script>
  // 서버에서 렌더한 user 객체
  if (window.user && window.user.email) {
    star365.signup({
      email: window.user.email,
      platform: '__PLATFORM_ID__',
      localId: String(window.user.id)
    }).catch(function(e){ console.warn('star365 hook', e); });
  }
</script>
```

`__PLATFORM_ID__` 는 해당 플랫폼 키 (위 표의 "플랫폼" 컬럼).

## 방법 B — Node.js 서버사이드 훅

가입 처리 직후 호출 (패턴):

```js
async function notifyStar365({ email, platform, localId, extra }) {
  try {
    const r = await fetch('https://api.star365.site/members/register', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ email, platform, local_user_id: String(localId), extra }),
      signal: AbortSignal.timeout(3000)
    });
    if (!r.ok) console.warn('[star365] register failed', r.status);
  } catch (e) { console.warn('[star365] register error', e.message); }
}

// 가입 라우트에서
await notifyStar365({ email, platform: 'plus365', localId: newUser.id });
```

**중요**: 훅 실패가 가입 트랜잭션을 롤백시켜서는 안 됨. 반드시 try/catch + console.warn.

## 방법 C — PHP 스니펫

```php
function star365_register($email, $platform, $local_id) {
  $ch = curl_init('https://api.star365.site/members/register');
  curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode(['email'=>$email,'platform'=>$platform,'local_user_id'=>strval($local_id)]),
    CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 3
  ]);
  $out = curl_exec($ch); curl_close($ch);
  return $out;
}
```

## 검증

적용 후 테스트:

```bash
curl -X POST https://api.star365.site/members/register \
  -H 'content-type: application/json' \
  -d '{"email":"test@example.com","platform":"PLATFORM","local_user_id":"test-1"}'
# → { "star365_uid": "...", ... }
```

admin.star365.site 에서 "총 Journey 이벤트" 증가 확인.

## SSO 추가 (서브도메인 플랫폼만)

플랫폼 도메인이 `*.star365.site` 이면 SSO 쿠키 자동 인식 가능. 로그인 후 서버가 클라이언트에 응답 전:

```js
const r = await fetch('https://api.star365.site/auth/sso/issue', {
  method: 'POST', credentials: 'include',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ email, platform, local_user_id })
});
// 브라우저에 쿠키 자동 세팅 (httpOnly)
```

`*.metaquant.club` / `plus365.site` 등 외부 도메인은 SSO 쿠키 공유 불가 → signup 훅만 사용.

## 롤백

훅 제거 = 코드 1곳 삭제. DB 영향 없음 (kahan 측 데이터는 그대로 남음).
