| 플랫폼 | 구분 | 연동 방식 | 지원 기능 |
|---|---|---|---|
| 치지직 (CHZZK) | 공식 | OpenAPI OAuth + WebSocket | 치즈(후원) |
| 숲 (SOOP) | 공식 | OpenAPI OAuth + Binary WebSocket | 별풍선 |
| 씨미 (CIME) | 공식 | OpenAPI OAuth + WebSocket | 빔(후원) |
| SSAPI | 통합 | Socket.io 기반 통합 중계 | 치지직 + 숲 통합 |
| 투네이션 (Toonation) | 비공식 | 페이로드 토큰 + WebSocket | 일반 후원 |
| 유튜브 (YouTube) | 비공식 | 라이브챗 폴링 | 슈퍼챗 |
| 항목 | 최소 버전 |
|---|---|
| Minecraft | 1.20.1+ (Paper/Folia) |
| Java | 21 |
| RSFramework | 4.7.17+ |
- 최신 릴리즈에서
DonationAPI-1.6.1.jar를 다운로드합니다. - 서버의
plugins/폴더에 JAR 파일을 배치합니다. - 서버를 시작하면 기본 설정 파일이 자동 생성됩니다.
plugins/DonationAPI/Config/Services/하위의 플랫폼별 설정 파일을 편집합니다./후원API reload또는 서버 재시작으로 설정을 적용합니다.
OAuth 기반 플랫폼(치지직, 숲, 씨미)을 사용하려면, 먼저 각 플랫폼의 개발자 사이트에서 어플리케이션을 생성하고 Client ID와 Client Secret을 발급받아야 합니다.
| 플랫폼 | 개발자 포털 | 비고 |
|---|---|---|
| 치지직 (CHZZK) | https://developers.chzzk.naver.com/application | 네이버 로그인 필요 |
| 숲 (SOOP) | https://developers.sooplive.com/?szWork=myinfo | SOOP 계정 필요 |
| 씨미 (CIME) | https://developers.ci.me/applications | 씨미 계정 필요 |
Redirect URI 설정: 어플리케이션 생성 시 Redirect URI(콜백 주소)를 설정해야 합니다. 설정 파일의
base-uri값을 기준으로 자동 생성되며, 기본값은http://localhost:3663입니다. 외부 접속이 필요한 경우 포트포워딩 후 공인 IP 또는 도메인으로 변경하세요.
플러그인을 처음 실행하면 아래와 같은 설정 파일이 자동 생성됩니다.
plugins/DonationAPI/Config/
├── Global.yml # 전역 설정 (후원 알림 등)
└── Services/
├── CHZZK.yml # 치지직 설정
├── SOOP.yml # 숲 설정
├── CIME.yml # 씨미 설정
├── SSAPI.yml # SSAPI 설정
├── Toonation.yml # 투네이션 설정
└── YouTube.yml # 유튜브 설정
사전 준비:치지직 개발자 포털에서 어플리케이션을 생성하고
Client ID와Client Secret을 발급받으세요.
plugins/DonationAPI/Config/Services/CHZZK.yml:
# 서비스 활성화 여부enabled: true# 브랜드 색상 (HEX)color: "#00FFA3"# 클라이언트 ID (개발자 포털에서 발급)client-id: "00000000-0000-0000-0000-000000000000"# 클라이언트 시크릿 (개발자 포털에서 발급)client-secret: "secret"# OAuth 콜백을 받을 접속용 주소base-uri: "http://localhost:3663"# 내장 HTTP 서버 바인드 호스트host: "0.0.0.0"# 내장 HTTP 서버 바인드 포트port: 3663# 라이브 상태 확인 주기 (ms)live-check-interval: 15000# 소켓 옵션socket:
# 소켓 연결 타임아웃 (ms)timeout: 3000keepalive:
# 서버-클라이언트 간 keepalive(ping) 사용 여부enabled: false# keepalive 주기 (ms)interval: 60000reconnection:
# 자동 재연결 활성화 여부enabled: false# 재연결 최초 지연 (ms)delay: 1000# 재연결 최대 지연 (ms)max-delay: 3000설정 방법:
- 개발자 포털에서 발급받은 값을
client-id,client-secret에 입력 - Redirect URI에
{base-uri}/auth/chzzk/callback형태로 등록 (ex:http://localhost:3663/auth/chzzk/callback) - 외부 접속 시
base-uri를 공인 IP/도메인으로 변경하고port에 맞게 포트포워딩 설정 - 인게임에서
/후원API 치지직 연동명령어로 OAuth 인증 진행
사전 준비:숲 개발자 포털에서 어플리케이션을 생성하고
Client ID와Client Secret을 발급받으세요.
plugins/DonationAPI/Config/Services/SOOP.yml:
# 서비스 활성화 여부enabled: true# 브랜드 색상 (HEX)color: "#0050FF"# 클라이언트 ID (개발자 포털에서 발급)client-id: "00000000000000000000000000000000"# 클라이언트 시크릿 (개발자 포털에서 발급)client-secret: "secret"# OAuth 콜백을 받을 접속용 주소base-uri: "http://localhost:3663"# 내장 HTTP 서버 바인드 호스트host: "0.0.0.0"# 내장 HTTP 서버 바인드 포트port: 3663# 라이브 상태 확인 주기 (ms)live-check-interval: 30000# 소켓 옵션socket:
# 소켓 연결 타임아웃 (ms)timeout: 3000keepalive:
# 서버-클라이언트 간 keepalive(ping) 사용 여부enabled: true# keepalive 주기 (ms)interval: 60000reconnection:
# 자동 재연결 활성화 여부enabled: true# 재연결 최초 지연 (ms)delay: 1000# 재연결 최대 지연 (ms)max-delay: 30000설정 방법:
- 개발자 포털에서 발급받은 값을
client-id,client-secret에 입력 - Redirect URI에
{base-uri}/auth/soop/callback형태로 등록 (ex:http://localhost:3663/auth/soop/callback) - 외부 접속 시
base-uri를 공인 IP/도메인으로 변경하고port에 맞게 포트포워딩 설정 - 인게임에서
/후원API 숲 연동명령어로 OAuth 인증 진행
사전 준비:씨미 개발자 포털에서 어플리케이션을 생성하고
Client ID와Client Secret을 발급받으세요.
plugins/DonationAPI/Config/Services/CIME.yml:
# 서비스 활성화 여부enabled: true# 브랜드 색상 (HEX)color: "#8A2BE2"# 클라이언트 ID (개발자 포털에서 발급)client-id: "00000000-0000-0000-0000-000000000000"# 클라이언트 시크릿 (개발자 포털에서 발급)client-secret: "secret"# OAuth 콜백을 받을 접속용 주소base-uri: "http://localhost:3663"# 내장 HTTP 서버 바인드 호스트host: "0.0.0.0"# 내장 HTTP 서버 바인드 포트port: 3663# 라이브 상태 확인 주기 (ms)live-check-interval: 30000# 소켓 옵션socket:
# 소켓 연결 타임아웃 (ms)timeout: 3000keepalive:
# 서버-클라이언트 간 keepalive(ping) 사용 여부# (씨미 API 10분 유휴 타임아웃 방지)enabled: true# keepalive 주기 (ms)interval: 60000reconnection:
# 자동 재연결 활성화 여부enabled: true# 재연결 최초 지연 (ms)delay: 1000# 재연결 최대 지연 (ms)max-delay: 30000설정 방법:
- 개발자 포털에서 발급받은 값을
client-id,client-secret에 입력 - Redirect URI에
{base-uri}/auth/cime/callback형태로 등록 (ex:http://localhost:3663/auth/cime/callback) - 외부 접속 시
base-uri를 공인 IP/도메인으로 변경하고port에 맞게 포트포워딩 설정 - 인게임에서
/후원API 씨미 연동명령어로 OAuth 인증 진행
참고: 씨미 WebSocket은 10분간 데이터가 없으면 유휴 타임아웃으로 연결이 끊길 수 있습니다.
keepalive.enabled: true를 유지하는 것을 강력히 권장합니다.
SSAPI는 스트리머 지원 API 통합 중계 서비스로, 별도의 OAuth 과정 없이 API 키 하나로 치지직과 숲 후원을 동시에 수신할 수 있습니다.
plugins/DonationAPI/Config/Services/SSAPI.yml:
# 서비스 활성화 여부enabled: true# 브랜드 색상 (HEX)color: "#FFFFFF"# SSAPI API 키api-key: ""# 로그인 재시도 간격 (ms)login-retry-delay: 1000# 소켓 옵션socket:
# 소켓 연결 타임아웃 (ms)timeout: 3000keepalive:
enabled: trueinterval: 60000reconnection:
enabled: truedelay: 1000max-delay: 3000설정 방법:
- SSAPI 사이트에서 API 키를 발급받아
api-key에 입력 - 인게임에서
/후원API SSAPI(치지직) 연동 <ID>또는/후원API SSAPI(숲) 연동 <ID>명령어로 연동
투네이션은 비공식 연동으로, 알림 위젯 페이지에서 발급하는 페이로드 토큰을 사용합니다.
plugins/DonationAPI/Config/Services/Toonation.yml:
# 서비스 활성화 여부enabled: true# 브랜드 색상 (HEX)color: "#00C7ED"# 소켓 옵션socket:
# 소켓 연결 타임아웃 (ms)timeout: 3000keepalive:
enabled: falseinterval: 60000reconnection:
enabled: truedelay: 1000max-delay: 30000설정 방법:
- 투네이션 알림 위젯 페이지에서 페이로드 토큰을 복사
- 인게임에서
/후원API 투네이션 연동 <토큰>명령어로 연동
토큰 획득 방법: 투네이션 대시보드 → 위젯 → 알림 위젯 → 임의 알림 위젯을 선택하고 주소(URL)의
payload파라미터 값이 토큰입니다.
유튜브는 비공식 연동으로, 별도의 인증 없이 채널 ID만으로 라이브 슈퍼챗을 수신합니다.
plugins/DonationAPI/Config/Services/YouTube.yml:
# 서비스 활성화 여부enabled: true# 브랜드 색상 (HEX)color: "#FF0000"# 유튜브 라이브챗 폴링 간격 (ms)polling-interval-ms: 5000# 라이브 상태 확인 주기 (ms)live-check-interval: 60000설정 방법:
- 유튜브 채널의 채널 ID를 확인 (채널 URL의
@handle또는UC...형태의 ID) - 인게임에서
/후원API 유튜브 연동 <채널ID>명령어로 연동
참고: 유튜브 연동은 라이브 방송 중에만 슈퍼챗을 수신합니다. 폴링 방식이므로 실시간성이 다소 떨어질 수 있습니다.
plugins/DonationAPI/Config/Global.yml:
# 후원 알림 설정 (서버 전체 브로드캐스트)donation-notify: false기본 명령어: /donationapi (별칭: /후원API)
| 명령어 | 설명 |
|---|---|
/후원API 치지직 연동 | 치지직 OAuth 인증 링크 생성 |
/후원API 숲 연동 | 숲 OAuth 인증 링크 생성 |
/후원API 씨미 연동 | 씨미 OAuth 인증 링크 생성 |
/후원API 투네이션 연동 <토큰> | 투네이션 위젯 토큰으로 연동 |
/후원API 유튜브 연동 <채널ID> | 유튜브 채널 연동 |
/후원API SSAPI(치지직) 연동 <ID> | SSAPI 치지직 통합 연동 |
/후원API SSAPI(숲) 연동 <ID> | SSAPI 숲 통합 연동 |
/후원API [플랫폼] 연동해제 | 해당 플랫폼의 연결 해제 |
| 명령어 | 설명 |
|---|---|
/후원API 이벤트 <닉네임> <금액> [메시지] | 테스트용 후원 이벤트 발생 |
/후원API reload | 설정 및 서비스 전체 재로드 |
모든 명령어는 OP 권한이 필요합니다.
PlaceholderAPI가 설치되면 자동으로 Placeholder가 등록됩니다.
| Placeholder | 설명 |
|---|---|
%donationapi_chzzk% | 치지직 연결 상태 |
%donationapi_soop% | 숲 연결 상태 |
%donationapi_cime% | 씨미 연결 상태 |
%donationapi_toonation% | 투네이션 연결 상태 |
%donationapi_youtube% | 유튜브 연결 상태 |
%donationapi_ssapi_chzzk% | SSAPI 치지직 연결 상태 |
%donationapi_ssapi_soop% | SSAPI 숲 연결 상태 |
참고: 연결 상태는 테스트 후원 또는 실제 후원이 최소 1회 이상 인식되어야
true로 표시됩니다.
| Placeholder | 설명 |
|---|---|
%donationapi_live_[플랫폼]% | 방송 중 여부 (true/false) |
%donationapi_title_[플랫폼]% | 방송 제목 |
%donationapi_viewers_[플랫폼]% | 시청자 수 |
%donationapi_url_[플랫폼]% | 방송 채널 URL |
[플랫폼]에는chzzk,soop,cime,youtube를 사용할 수 있습니다.
모든 플랫폼의 후원은 Bukkit의 DonationEvent로 통합됩니다.
importkr.rtustudio.donation.bukkit.event.DonationEvent;
importorg.bukkit.event.EventHandler;
importorg.bukkit.event.Listener;
publicclassDonationListenerimplementsListener {
@EventHandlerpublicvoidonDonation(DonationEventevent) {
Stringnickname = event.getNickname();
intamount = event.getAmount(); // 플랫폼 단위 수량intprice = event.getPrice(); // 원 환산 금액Stringunit = event.getUnit(); // 단위명 (치즈, 개 등)Stringmessage = event.getMessage();
if (event.getPlayer() != null) {
event.getPlayer().sendMessage(
String.format("§e%s§f님이 §d%,d§f%s를 후원! §7%s",
nickname, amount, unit, message)
);
}
}
}DonationEvent 메서드:
| 메서드 | 반환 타입 | 설명 |
|---|---|---|
getPlayer() | @Nullable Player | 후원 대상 플레이어 |
getDonation() | Donation | 통합 후원 데이터 |
getService() | Services | 서비스 유형 (CHZZK, SOOP 등) |
getPlatform() | Platform | 실제 플랫폼 |
getNickname() | String | 후원자 닉네임 |
getAmount() | int | 후원 수량 (플랫폼 단위) |
getPrice() | int | 원 환산 금액 |
getUnit() | String | 단위명 |
getMessage() | String | 후원 메시지 |
isCancelled() | boolean | 이벤트 취소 여부 |
Donation은 불변 record로 정의됩니다.
publicrecordDonation(
@NullableUUIDuniqueId, // 후원 대상 플레이어 UUIDServicesservice, // 서비스 유형Platformplatform, // 실제 플랫폼DonationTypetype, // CHAT 또는 VIDEOStringstreamer, // 스트리머 식별자Stringdonator, // 후원자 식별자Stringnickname, // 후원자 닉네임 (기본: "익명의 후원자")Stringmessage, // 후원 메시지intamount// 후원 수량
) {
publicStringunit() { returnplatform.unit(); }
publicintprice() { returnamount * platform.rate(); }
}플랫폼별 단위 및 환산:
| 플랫폼 | 단위 | 환산(rate) | 예시 |
|---|---|---|---|
| 치지직 | 치즈 | ×1 | 1,000치즈 → 1,000원 |
| 숲 | 개 | ×100 | 5개 → 500원 |
| 씨미 | 빔 | ×1 | 1,000빔 → 1,000원 |
| 투네이션 | 캐시 | ×1 | 5,000캐시 → 5,000원 |
| 유튜브 | 원 | ×1 | 1,000원 → 1,000원 |
Services vs Platform:
Services는 연동 서비스를 나타내고, Platform은 후원이 발생한 실제 플랫폼을 나타냅니다.
예를 들어 SSAPI 서비스는 치지직과 숲 두 플랫폼을 모두 지원합니다.
| Services | 지원 Platform |
|---|---|
CHZZK | CHZZK |
SOOP | SOOP |
CIME | CIME |
SSAPI | CHZZK, SOOP |
Toonation | TOONATION |
YOUTUBE | YOUTUBE |
LiveStatusManager를 통해 연결된 스트리머의 방송 상태를 조회할 수 있습니다.
LiveStatusManagermanager = BukkitDonationAPI.getInstance().getLiveStatusManager();
LiveStatusstatus = manager.getLiveStatus(Services.CHZZK, player.getUniqueId());
if (status != null && status.live()) {
player.sendMessage(
String.format("방송 중: %s (시청자 %,d명)", status.title(), status.viewerCount())
);
}LiveStatus 필드:
| 필드 | 타입 | 설명 |
|---|---|---|
live() | boolean | 방송 중 여부 |
title() | @Nullable String | 방송 제목 |
viewerCount() | int | 시청자 수 |
channelUrl() | String | 채널 URL |
updatedAt() | long | 캐시 갱신 시각 (epoch ms) |
라이브 상태는 플랫폼별 설정의
liveCheckInterval에 지정된 주기로 자동 폴링됩니다.
DonationAPI는 자체적으로 Skript 연동을 지원하므로, 애드온 없이 플러그인 환경에서 직접 스크립트로 후원 이벤트를 감지할 수 있습니다.
이벤트 구문:
on donation:
if player isset:
send "&e&l[후원] &f%donation nickname%님이 &d%donation amount%%donation unit%&f를 후원했습니다!"to player
broadcast "&7메시지: %donation message%"
broadcast "&7플랫폼: %donation platform%"지원 수식어(Expression):
| 구문 | 반환 타입 | 설명 |
|---|---|---|
event-player 또는 player | Player | 이벤트를 연동한 대상 플레이어 |
donation message | String | 후원 메시지 |
donation amount | Number | 후원 수량 (플랫폼 기준 단위) |
donation price | Number | 원(KRW) 환산 금액 |
donation unit | String | 단위명 (치즈, 개, 캐시 등) |
donation nickname | String | 후원자 닉네임 |
donation donator | String | 후원자 고유 식별자 |
donation streamer | String | 후원 대상 스트리머 / 방송국 |
donation platform | String | 결제 플랫폼 (CHZZK, SOOP 등) |
DonationAPI/
├── Services/ # 프레임워크 독립 코어 서비스 (순수 Java)
│ ├── Common/ # 통합 모델 (Donation, Platform, AsyncExecutor)
│ ├── CHZZK/ # 치지직 — OpenAPI + WebSocket
│ ├── SOOP/ # 숲 — OpenAPI + Binary WebSocket
│ ├── CIME/ # 씨미 — OpenAPI + WebSocket
│ ├── SSAPI/ # SSAPI — Socket.io 통합 중계
│ ├── Toonation/ # 투네이션 — WebSocket
│ └── YouTube/ # 유튜브 — 라이브챗 폴링
│
└── Platform/ # 프레임워크 종속 어댑터
├── Common/ # 플랫폼 공통 API (DonationAPI 레지스트리)
└── Bukkit/ # Bukkit 통합 계층
├── command/ # 명령어 처리
├── configuration/ # GlobalConfig + 플랫폼별 Config
├── event/ # DonationEvent
├── handler/ # PlayerJoinQuit 리스너
├── integration/ # PlaceholderAPI 연동
├── manager/ # DonationManager, LiveStatusManager
└── platform/ # ServiceBuilder, PlatformRegistry
- BukkitDonationAPI — RSPlugin 메인 클래스. 서비스 생명주기와 설정 관리를 담당합니다.
- PlatformRegistry —
DonationPlatform인스턴스를 관리하며 플레이어 연결/해제 상태를 추적합니다. - DonationAPI —
Service인스턴스 레지스트리. 등록된 서비스의 시작/종료를 관리합니다. - ServiceBuilder — 서비스 생성, 핸들러 연결, 플랫폼 등록을 선언적으로 처리하는 빌더입니다.
- ServiceHandler<R> — 후원(
donation), 성공(success), 실패(failure), 알림(messenger) 콜백을 묶는 record입니다. - AsyncExecutor — Java 21 Virtual Thread 기반 공유 Executor. 모든 I/O 비동기 작업에 사용됩니다.
새 후원 플랫폼을 추가할 때 ServiceBuilder로 서비스 등록을 선언적으로 처리합니다.
register(ServiceBuilder.builder()
.config(ChzzkConfig.class) // 설정 클래스
.data(ChzzkPlayer.class) // 플레이어 데이터 타입
.factory(ChzzkService::new) // 서비스 팩토리
.reconnect((service, data) -> // 재연결 콜백 (선택)service.reconnect(data.uuid(), data))
.build(this)
);빌더 흐름:
builder()→ 공통 빌더 생성.config(Class)→ 설정 클래스 바인딩.data(Class)→ 데이터 타입 바인딩 →TypedBuilder전환.factory(ServiceFactory)→ 서비스 팩토리 설정.reconnect(ReconnectFunction)→ 재연결 로직 설정 (선택).build(plugin)→ServiceBuilder<D, S>완성
# 빌드 (테스트 포함)
./gradlew build
# 빌드 (테스트 제외)
./gradlew build -x test빌드 결과물: build/libs/DonationAPI-1.6.1.jar
이 프로젝트는 GNU General Public License v3.0 하에 배포됩니다.
Made with ❤️ by RTU Studio