connectbase-client 6.12.0 → 6.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/README.md +8 -1
- package/dist/cli.js +1 -1
- package/dist/connect-base.umd.js +5 -5
- package/dist/index.d.mts +210 -66
- package/dist/index.d.ts +210 -66
- package/dist/index.js +118 -32
- package/dist/index.mjs +118 -32
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12709,32 +12709,41 @@ var SubscriptionAPI = class {
|
|
|
12709
12709
|
* 구독 생성
|
|
12710
12710
|
* 정기결제 구독을 생성합니다.
|
|
12711
12711
|
*
|
|
12712
|
-
*
|
|
12713
|
-
*
|
|
12712
|
+
* **금액과 요금제는 서버가 정합니다.** 브라우저에서는 콘솔 가격표의 정기 상품 `product_id` 만 보내고, 서버가
|
|
12713
|
+
* 금액, 통화, 주기, 플랜명, 무료 체험, MoR PG 가격 ref 를 상품에서 정합니다. 금액을 직접 보내는 형태(레거시)는
|
|
12714
|
+
* 결제 설정의 금액 출처가 `client` 인 앱이거나 서버(시크릿 키, service_role 함수)에서 부를 때만 받습니다.
|
|
12715
|
+
*
|
|
12716
|
+
* @param data - 가격표 정기 상품(`product_id`) 또는 금액과 요금제, 빌링키, 고객 정보 등
|
|
12717
|
+
* @returns 생성된 구독 정보 (`product_id` 포함)
|
|
12714
12718
|
*
|
|
12715
12719
|
* @example
|
|
12716
12720
|
* ```typescript
|
|
12721
|
+
* // 가격표 구독 (권장) — 금액, 주기, 체험 기간은 콘솔 가격표의 premium_month 가 정한다
|
|
12717
12722
|
* const subscription = await client.subscription.create({
|
|
12718
12723
|
* billing_key_id: 'bk_xxxxx',
|
|
12719
|
-
*
|
|
12720
|
-
* amount: 9900,
|
|
12721
|
-
* billing_cycle: 'monthly',
|
|
12722
|
-
* billing_day: 15, // 매월 15일 결제
|
|
12723
|
-
* trial_days: 7 // 7일 무료 체험
|
|
12724
|
+
* product_id: 'premium_month',
|
|
12724
12725
|
* })
|
|
12725
12726
|
*
|
|
12726
|
-
* // MoR(dodo/paddle) 구독 + 할인 코드
|
|
12727
|
+
* // MoR(dodo/paddle) 구독 + 할인 코드 — PG 가격 ref 도 상품의 provider_price_refs 에서 서버가 고른다
|
|
12727
12728
|
* const promo = await client.subscription.create({
|
|
12729
|
+
* product_id: 'premium_month',
|
|
12730
|
+
* discount_code: 'LAUNCH50', // 첫 결제에 적용 (몇 주기까지인지는 PG 쿠폰 설정이 결정)
|
|
12731
|
+
* })
|
|
12732
|
+
*
|
|
12733
|
+
* // 금액 직접 지정 (레거시 앱, 서버 호출) — 가격표 전용 앱에서 브라우저가 보내면 403 client_amount_not_allowed
|
|
12734
|
+
* const legacy = await client.subscription.create({
|
|
12735
|
+
* billing_key_id: 'bk_xxxxx',
|
|
12728
12736
|
* plan_name: '프리미엄 플랜',
|
|
12729
12737
|
* amount: 9900,
|
|
12730
12738
|
* billing_cycle: 'monthly',
|
|
12731
|
-
* provider_price_ref: 'pdt_xxxxx', // PG 카탈로그의 recurring 상품
|
|
12732
|
-
* discount_code: 'LAUNCH50', // 첫 결제에 적용 (몇 주기까지인지는 PG 쿠폰 설정이 결정)
|
|
12733
|
-
* trial_days: 14, // Dodo 만 지원 — Paddle 은 400 trial_days_unsupported
|
|
12734
12739
|
* })
|
|
12735
12740
|
* ```
|
|
12736
12741
|
*
|
|
12737
12742
|
* @remarks
|
|
12743
|
+
* 권한은 구독의 `product_id` 로 지급합니다(`metadata` 는 클라이언트 값). 가격표 전용 앱의 브라우저 호출은
|
|
12744
|
+
* `start_now: false` 와 `billing_day` 를 쓸 수 없습니다(403 `client_schedule_not_allowed`) — 둘 다 한 번 낸 돈으로 쓰는
|
|
12745
|
+
* 기간을 늘립니다. 무료 체험은 상품의 `trial_days` 가 정합니다.
|
|
12746
|
+
*
|
|
12738
12747
|
* 할인 코드(`discount_code`)와 `trial_days` 의 프로바이더 지원 범위가 다릅니다:
|
|
12739
12748
|
* - **Dodo**: 할인 코드 O, 결제창 쿠폰 입력 토글 O, 구독별 체험 일수 O
|
|
12740
12749
|
* - **Paddle**: 할인 코드 O(코드→내부 ID 자동 해소), 쿠폰 입력칸은 항상 노출(숨김 불가),
|
|
@@ -12806,9 +12815,9 @@ var SubscriptionAPI = class {
|
|
|
12806
12815
|
*
|
|
12807
12816
|
* @example
|
|
12808
12817
|
* ```typescript
|
|
12818
|
+
* // 서버(시크릿 키, service_role 함수)에서 다른 가격표 상품으로 바꾸기 — 다음 청구부터 새 금액
|
|
12809
12819
|
* await client.subscription.update(subscriptionId, {
|
|
12810
|
-
*
|
|
12811
|
-
* amount: 29900
|
|
12820
|
+
* product_id: 'enterprise_month',
|
|
12812
12821
|
* })
|
|
12813
12822
|
*
|
|
12814
12823
|
* // 다음 결제일을 특정 날짜로 옮기기 (기간권·보상)
|
|
@@ -12994,11 +13003,10 @@ var SubscriptionAPI = class {
|
|
|
12994
13003
|
*
|
|
12995
13004
|
* @example
|
|
12996
13005
|
* ```typescript
|
|
13006
|
+
* // 가격표 정기 상품으로 바꾼다 — 새 PG 가격 ref 와 플랜명은 서버가 상품에서 정한다
|
|
12997
13007
|
* await client.subscription.changePlan(subscriptionId, {
|
|
12998
|
-
*
|
|
12999
|
-
*
|
|
13000
|
-
* amount: 24900,
|
|
13001
|
-
* proration_mode: 'prorated_immediately',
|
|
13008
|
+
* product_id: 'family_month',
|
|
13009
|
+
* proration_mode: 'prorated_immediately', // 브라우저는 지금 정산하는 모드만
|
|
13002
13010
|
* })
|
|
13003
13011
|
* ```
|
|
13004
13012
|
*/
|
|
@@ -13031,7 +13039,7 @@ var SubscriptionAPI = class {
|
|
|
13031
13039
|
* @example
|
|
13032
13040
|
* ```typescript
|
|
13033
13041
|
* const preview = await client.subscription.previewChangePlan(subscriptionId, {
|
|
13034
|
-
*
|
|
13042
|
+
* product_id: 'family_month',
|
|
13035
13043
|
* })
|
|
13036
13044
|
* // 통화를 함께 포맷한다 — 금액만 쓰면 다통화에서 거짓 금액이 된다
|
|
13037
13045
|
* format(preview.immediate_charge_amount, preview.currency)
|
|
@@ -13085,10 +13093,11 @@ var SubscriptionAPI = class {
|
|
|
13085
13093
|
*
|
|
13086
13094
|
* @example
|
|
13087
13095
|
* ```typescript
|
|
13096
|
+
* // 가격표 단건 상품으로 청구 — 금액과 상품명은 서버가 정한다
|
|
13088
13097
|
* const result = await client.subscription.chargeWithBillingKey({
|
|
13089
13098
|
* billing_key_id: 'bk_xxxxx',
|
|
13090
|
-
*
|
|
13091
|
-
*
|
|
13099
|
+
* product_id: 'gem_100',
|
|
13100
|
+
* quantity: 2,
|
|
13092
13101
|
* })
|
|
13093
13102
|
*
|
|
13094
13103
|
* if (result.status === 'done') {
|
|
@@ -13322,8 +13331,8 @@ var SupportAPI = class {
|
|
|
13322
13331
|
}
|
|
13323
13332
|
};
|
|
13324
13333
|
function detectSdkVersion() {
|
|
13325
|
-
if ("6.
|
|
13326
|
-
return "6.
|
|
13334
|
+
if ("6.13.0") {
|
|
13335
|
+
return "6.13.0";
|
|
13327
13336
|
}
|
|
13328
13337
|
return "unknown";
|
|
13329
13338
|
}
|
|
@@ -14217,7 +14226,14 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14217
14226
|
this.currentRoomId = null;
|
|
14218
14227
|
this.currentPeerId = null;
|
|
14219
14228
|
this.currentUserId = null;
|
|
14220
|
-
|
|
14229
|
+
/** connect 가 요청한 방송자 역할 (switchRoom 이 이어받는다) */
|
|
14230
|
+
this.broadcasterRequested = false;
|
|
14231
|
+
/** 서버가 정한 방송자 역할 (`joined` 의 is_broadcaster) */
|
|
14232
|
+
this.broadcasterGranted = false;
|
|
14233
|
+
/** 비공개 룸 허용 목록 요청 (이 연결에서만 쓴다) */
|
|
14234
|
+
this.allowedMemberIds = null;
|
|
14235
|
+
this.privateRoom = false;
|
|
14236
|
+
this.warningListeners = [];
|
|
14221
14237
|
this.localStream = null;
|
|
14222
14238
|
this.channelType = "interactive";
|
|
14223
14239
|
// 피어 연결 관리
|
|
@@ -14271,7 +14287,10 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14271
14287
|
this.setState("connecting");
|
|
14272
14288
|
this.currentRoomId = options.roomId;
|
|
14273
14289
|
this.currentUserId = options.userId || null;
|
|
14274
|
-
this.
|
|
14290
|
+
this.broadcasterRequested = options.isBroadcaster || false;
|
|
14291
|
+
this.broadcasterGranted = false;
|
|
14292
|
+
this.allowedMemberIds = options.allowedMemberIds ? [...options.allowedMemberIds] : null;
|
|
14293
|
+
this.privateRoom = false;
|
|
14275
14294
|
this.localStream = options.localStream || null;
|
|
14276
14295
|
if (this.iceServers.length === 0) {
|
|
14277
14296
|
try {
|
|
@@ -14303,13 +14322,16 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14303
14322
|
return;
|
|
14304
14323
|
}
|
|
14305
14324
|
this.reconnectAttempts = 0;
|
|
14325
|
+
const joinData = {
|
|
14326
|
+
is_broadcaster: this.broadcasterRequested
|
|
14327
|
+
};
|
|
14328
|
+
if (this.allowedMemberIds) {
|
|
14329
|
+
joinData.allowed_member_ids = this.allowedMemberIds;
|
|
14330
|
+
}
|
|
14306
14331
|
this.sendSignaling({
|
|
14307
14332
|
type: "join",
|
|
14308
14333
|
room_id: this.currentRoomId,
|
|
14309
|
-
data:
|
|
14310
|
-
user_id: this.currentUserId,
|
|
14311
|
-
is_broadcaster: this.isBroadcaster
|
|
14312
|
-
}
|
|
14334
|
+
data: joinData
|
|
14313
14335
|
});
|
|
14314
14336
|
};
|
|
14315
14337
|
ws.onmessage = async (event) => {
|
|
@@ -14377,6 +14399,8 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14377
14399
|
if (data.channel_type) {
|
|
14378
14400
|
this.channelType = data.channel_type;
|
|
14379
14401
|
}
|
|
14402
|
+
this.broadcasterGranted = data.is_broadcaster === true;
|
|
14403
|
+
this.privateRoom = data.private === true;
|
|
14380
14404
|
const peers = data.peers || [];
|
|
14381
14405
|
for (const peer of peers) {
|
|
14382
14406
|
if (peer.peer_id !== this.currentPeerId) {
|
|
@@ -14417,9 +14441,16 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14417
14441
|
await this.handleICECandidate(msg.peer_id, msg.candidate);
|
|
14418
14442
|
}
|
|
14419
14443
|
break;
|
|
14444
|
+
case "warning": {
|
|
14445
|
+
const data = msg.data && typeof msg.data === "object" ? msg.data : {};
|
|
14446
|
+
this.emitWarning({
|
|
14447
|
+
code: typeof data.code === "string" ? data.code : "unknown",
|
|
14448
|
+
message: typeof data.message === "string" ? data.message : ""
|
|
14449
|
+
});
|
|
14450
|
+
break;
|
|
14451
|
+
}
|
|
14420
14452
|
case "error": {
|
|
14421
|
-
const
|
|
14422
|
-
const error = new Error(errorMsg);
|
|
14453
|
+
const error = new Error(_WebRTCAPI.signalingErrorMessage(msg.data));
|
|
14423
14454
|
this.emitError(error);
|
|
14424
14455
|
reject?.(error);
|
|
14425
14456
|
break;
|
|
@@ -14517,6 +14548,19 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14517
14548
|
}
|
|
14518
14549
|
this.remoteStreams.delete(remotePeerId);
|
|
14519
14550
|
}
|
|
14551
|
+
/**
|
|
14552
|
+
* 서버 error 메시지의 data 에서 안내 문구를 꺼낸다. 대부분은 문자열이지만 할당량, 동시접속 초과는
|
|
14553
|
+
* `{ error, message, ... }` 객체로 온다.
|
|
14554
|
+
*/
|
|
14555
|
+
static signalingErrorMessage(data) {
|
|
14556
|
+
if (typeof data === "string" && data) return data;
|
|
14557
|
+
if (data && typeof data === "object") {
|
|
14558
|
+
const obj = data;
|
|
14559
|
+
if (typeof obj.message === "string" && obj.message) return obj.message;
|
|
14560
|
+
if (typeof obj.error === "string" && obj.error) return obj.error;
|
|
14561
|
+
}
|
|
14562
|
+
return "Unknown error";
|
|
14563
|
+
}
|
|
14520
14564
|
sendSignaling(msg) {
|
|
14521
14565
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
14522
14566
|
this.ws.send(JSON.stringify(msg));
|
|
@@ -14582,6 +14626,8 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14582
14626
|
this.currentRoomId = null;
|
|
14583
14627
|
this.currentPeerId = null;
|
|
14584
14628
|
this.localStream = null;
|
|
14629
|
+
this.broadcasterGranted = false;
|
|
14630
|
+
this.privateRoom = false;
|
|
14585
14631
|
this.setState("disconnected");
|
|
14586
14632
|
}
|
|
14587
14633
|
/**
|
|
@@ -14599,9 +14645,15 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14599
14645
|
async switchRoom(roomId, options = {}) {
|
|
14600
14646
|
const localStream = options.localStream ?? this.localStream ?? void 0;
|
|
14601
14647
|
const userId = options.userId ?? this.currentUserId ?? void 0;
|
|
14602
|
-
const isBroadcaster = options.isBroadcaster ?? this.
|
|
14648
|
+
const isBroadcaster = options.isBroadcaster ?? this.broadcasterRequested;
|
|
14603
14649
|
this.disconnect();
|
|
14604
|
-
await this.connect({
|
|
14650
|
+
await this.connect({
|
|
14651
|
+
roomId,
|
|
14652
|
+
userId,
|
|
14653
|
+
isBroadcaster,
|
|
14654
|
+
localStream,
|
|
14655
|
+
allowedMemberIds: options.allowedMemberIds
|
|
14656
|
+
});
|
|
14605
14657
|
}
|
|
14606
14658
|
/**
|
|
14607
14659
|
* 소켓, 룸, 이벤트 리스너, 피어 연결을 **독립적으로** 갖는 새 WebRTC 세션을 만든다.
|
|
@@ -14654,6 +14706,19 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14654
14706
|
getChannelType() {
|
|
14655
14707
|
return this.channelType;
|
|
14656
14708
|
}
|
|
14709
|
+
/**
|
|
14710
|
+
* 서버가 정한 방송자 역할인지 조회한다. 요청(`isBroadcaster: true`)과 다를 수 있다 — 익명 연결이나
|
|
14711
|
+
* 채널의 방송자 역할이 없는 회원은 시청자로 참가한다(`onWarning` 으로 이유가 온다).
|
|
14712
|
+
*/
|
|
14713
|
+
isBroadcaster() {
|
|
14714
|
+
return this.broadcasterGranted;
|
|
14715
|
+
}
|
|
14716
|
+
/**
|
|
14717
|
+
* 지금 룸이 회원 허용 목록이 있는 비공개 룸인지 조회한다.
|
|
14718
|
+
*/
|
|
14719
|
+
isPrivateRoom() {
|
|
14720
|
+
return this.privateRoom;
|
|
14721
|
+
}
|
|
14657
14722
|
/**
|
|
14658
14723
|
* 원격 스트림 조회
|
|
14659
14724
|
*/
|
|
@@ -14724,6 +14789,24 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14724
14789
|
this.errorListeners = this.errorListeners.filter((cb) => cb !== callback);
|
|
14725
14790
|
};
|
|
14726
14791
|
}
|
|
14792
|
+
/**
|
|
14793
|
+
* 서버가 join 요청을 바꿨다는 알림 이벤트 (예: 방송자 요청을 시청자로 낮춤)
|
|
14794
|
+
*
|
|
14795
|
+
* @example
|
|
14796
|
+
* ```typescript
|
|
14797
|
+
* cb.webrtc.onWarning(({ code, message }) => {
|
|
14798
|
+
* if (code === 'broadcaster_role_required') showToast(message)
|
|
14799
|
+
* })
|
|
14800
|
+
* ```
|
|
14801
|
+
*/
|
|
14802
|
+
onWarning(callback) {
|
|
14803
|
+
this.warningListeners.push(callback);
|
|
14804
|
+
return () => {
|
|
14805
|
+
this.warningListeners = this.warningListeners.filter(
|
|
14806
|
+
(cb) => cb !== callback
|
|
14807
|
+
);
|
|
14808
|
+
};
|
|
14809
|
+
}
|
|
14727
14810
|
/**
|
|
14728
14811
|
* 피어 참가 이벤트
|
|
14729
14812
|
*/
|
|
@@ -14769,6 +14852,9 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14769
14852
|
emitError(error) {
|
|
14770
14853
|
this.errorListeners.forEach((cb) => cb(error));
|
|
14771
14854
|
}
|
|
14855
|
+
emitWarning(warning) {
|
|
14856
|
+
this.warningListeners.forEach((cb) => cb(warning));
|
|
14857
|
+
}
|
|
14772
14858
|
emitPeerJoined(peerId, info) {
|
|
14773
14859
|
this.peerJoinedListeners.forEach((cb) => cb(peerId, info));
|
|
14774
14860
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -12657,32 +12657,41 @@ var SubscriptionAPI = class {
|
|
|
12657
12657
|
* 구독 생성
|
|
12658
12658
|
* 정기결제 구독을 생성합니다.
|
|
12659
12659
|
*
|
|
12660
|
-
*
|
|
12661
|
-
*
|
|
12660
|
+
* **금액과 요금제는 서버가 정합니다.** 브라우저에서는 콘솔 가격표의 정기 상품 `product_id` 만 보내고, 서버가
|
|
12661
|
+
* 금액, 통화, 주기, 플랜명, 무료 체험, MoR PG 가격 ref 를 상품에서 정합니다. 금액을 직접 보내는 형태(레거시)는
|
|
12662
|
+
* 결제 설정의 금액 출처가 `client` 인 앱이거나 서버(시크릿 키, service_role 함수)에서 부를 때만 받습니다.
|
|
12663
|
+
*
|
|
12664
|
+
* @param data - 가격표 정기 상품(`product_id`) 또는 금액과 요금제, 빌링키, 고객 정보 등
|
|
12665
|
+
* @returns 생성된 구독 정보 (`product_id` 포함)
|
|
12662
12666
|
*
|
|
12663
12667
|
* @example
|
|
12664
12668
|
* ```typescript
|
|
12669
|
+
* // 가격표 구독 (권장) — 금액, 주기, 체험 기간은 콘솔 가격표의 premium_month 가 정한다
|
|
12665
12670
|
* const subscription = await client.subscription.create({
|
|
12666
12671
|
* billing_key_id: 'bk_xxxxx',
|
|
12667
|
-
*
|
|
12668
|
-
* amount: 9900,
|
|
12669
|
-
* billing_cycle: 'monthly',
|
|
12670
|
-
* billing_day: 15, // 매월 15일 결제
|
|
12671
|
-
* trial_days: 7 // 7일 무료 체험
|
|
12672
|
+
* product_id: 'premium_month',
|
|
12672
12673
|
* })
|
|
12673
12674
|
*
|
|
12674
|
-
* // MoR(dodo/paddle) 구독 + 할인 코드
|
|
12675
|
+
* // MoR(dodo/paddle) 구독 + 할인 코드 — PG 가격 ref 도 상품의 provider_price_refs 에서 서버가 고른다
|
|
12675
12676
|
* const promo = await client.subscription.create({
|
|
12677
|
+
* product_id: 'premium_month',
|
|
12678
|
+
* discount_code: 'LAUNCH50', // 첫 결제에 적용 (몇 주기까지인지는 PG 쿠폰 설정이 결정)
|
|
12679
|
+
* })
|
|
12680
|
+
*
|
|
12681
|
+
* // 금액 직접 지정 (레거시 앱, 서버 호출) — 가격표 전용 앱에서 브라우저가 보내면 403 client_amount_not_allowed
|
|
12682
|
+
* const legacy = await client.subscription.create({
|
|
12683
|
+
* billing_key_id: 'bk_xxxxx',
|
|
12676
12684
|
* plan_name: '프리미엄 플랜',
|
|
12677
12685
|
* amount: 9900,
|
|
12678
12686
|
* billing_cycle: 'monthly',
|
|
12679
|
-
* provider_price_ref: 'pdt_xxxxx', // PG 카탈로그의 recurring 상품
|
|
12680
|
-
* discount_code: 'LAUNCH50', // 첫 결제에 적용 (몇 주기까지인지는 PG 쿠폰 설정이 결정)
|
|
12681
|
-
* trial_days: 14, // Dodo 만 지원 — Paddle 은 400 trial_days_unsupported
|
|
12682
12687
|
* })
|
|
12683
12688
|
* ```
|
|
12684
12689
|
*
|
|
12685
12690
|
* @remarks
|
|
12691
|
+
* 권한은 구독의 `product_id` 로 지급합니다(`metadata` 는 클라이언트 값). 가격표 전용 앱의 브라우저 호출은
|
|
12692
|
+
* `start_now: false` 와 `billing_day` 를 쓸 수 없습니다(403 `client_schedule_not_allowed`) — 둘 다 한 번 낸 돈으로 쓰는
|
|
12693
|
+
* 기간을 늘립니다. 무료 체험은 상품의 `trial_days` 가 정합니다.
|
|
12694
|
+
*
|
|
12686
12695
|
* 할인 코드(`discount_code`)와 `trial_days` 의 프로바이더 지원 범위가 다릅니다:
|
|
12687
12696
|
* - **Dodo**: 할인 코드 O, 결제창 쿠폰 입력 토글 O, 구독별 체험 일수 O
|
|
12688
12697
|
* - **Paddle**: 할인 코드 O(코드→내부 ID 자동 해소), 쿠폰 입력칸은 항상 노출(숨김 불가),
|
|
@@ -12754,9 +12763,9 @@ var SubscriptionAPI = class {
|
|
|
12754
12763
|
*
|
|
12755
12764
|
* @example
|
|
12756
12765
|
* ```typescript
|
|
12766
|
+
* // 서버(시크릿 키, service_role 함수)에서 다른 가격표 상품으로 바꾸기 — 다음 청구부터 새 금액
|
|
12757
12767
|
* await client.subscription.update(subscriptionId, {
|
|
12758
|
-
*
|
|
12759
|
-
* amount: 29900
|
|
12768
|
+
* product_id: 'enterprise_month',
|
|
12760
12769
|
* })
|
|
12761
12770
|
*
|
|
12762
12771
|
* // 다음 결제일을 특정 날짜로 옮기기 (기간권·보상)
|
|
@@ -12942,11 +12951,10 @@ var SubscriptionAPI = class {
|
|
|
12942
12951
|
*
|
|
12943
12952
|
* @example
|
|
12944
12953
|
* ```typescript
|
|
12954
|
+
* // 가격표 정기 상품으로 바꾼다 — 새 PG 가격 ref 와 플랜명은 서버가 상품에서 정한다
|
|
12945
12955
|
* await client.subscription.changePlan(subscriptionId, {
|
|
12946
|
-
*
|
|
12947
|
-
*
|
|
12948
|
-
* amount: 24900,
|
|
12949
|
-
* proration_mode: 'prorated_immediately',
|
|
12956
|
+
* product_id: 'family_month',
|
|
12957
|
+
* proration_mode: 'prorated_immediately', // 브라우저는 지금 정산하는 모드만
|
|
12950
12958
|
* })
|
|
12951
12959
|
* ```
|
|
12952
12960
|
*/
|
|
@@ -12979,7 +12987,7 @@ var SubscriptionAPI = class {
|
|
|
12979
12987
|
* @example
|
|
12980
12988
|
* ```typescript
|
|
12981
12989
|
* const preview = await client.subscription.previewChangePlan(subscriptionId, {
|
|
12982
|
-
*
|
|
12990
|
+
* product_id: 'family_month',
|
|
12983
12991
|
* })
|
|
12984
12992
|
* // 통화를 함께 포맷한다 — 금액만 쓰면 다통화에서 거짓 금액이 된다
|
|
12985
12993
|
* format(preview.immediate_charge_amount, preview.currency)
|
|
@@ -13033,10 +13041,11 @@ var SubscriptionAPI = class {
|
|
|
13033
13041
|
*
|
|
13034
13042
|
* @example
|
|
13035
13043
|
* ```typescript
|
|
13044
|
+
* // 가격표 단건 상품으로 청구 — 금액과 상품명은 서버가 정한다
|
|
13036
13045
|
* const result = await client.subscription.chargeWithBillingKey({
|
|
13037
13046
|
* billing_key_id: 'bk_xxxxx',
|
|
13038
|
-
*
|
|
13039
|
-
*
|
|
13047
|
+
* product_id: 'gem_100',
|
|
13048
|
+
* quantity: 2,
|
|
13040
13049
|
* })
|
|
13041
13050
|
*
|
|
13042
13051
|
* if (result.status === 'done') {
|
|
@@ -13270,8 +13279,8 @@ var SupportAPI = class {
|
|
|
13270
13279
|
}
|
|
13271
13280
|
};
|
|
13272
13281
|
function detectSdkVersion() {
|
|
13273
|
-
if ("6.
|
|
13274
|
-
return "6.
|
|
13282
|
+
if ("6.13.0") {
|
|
13283
|
+
return "6.13.0";
|
|
13275
13284
|
}
|
|
13276
13285
|
return "unknown";
|
|
13277
13286
|
}
|
|
@@ -14165,7 +14174,14 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14165
14174
|
this.currentRoomId = null;
|
|
14166
14175
|
this.currentPeerId = null;
|
|
14167
14176
|
this.currentUserId = null;
|
|
14168
|
-
|
|
14177
|
+
/** connect 가 요청한 방송자 역할 (switchRoom 이 이어받는다) */
|
|
14178
|
+
this.broadcasterRequested = false;
|
|
14179
|
+
/** 서버가 정한 방송자 역할 (`joined` 의 is_broadcaster) */
|
|
14180
|
+
this.broadcasterGranted = false;
|
|
14181
|
+
/** 비공개 룸 허용 목록 요청 (이 연결에서만 쓴다) */
|
|
14182
|
+
this.allowedMemberIds = null;
|
|
14183
|
+
this.privateRoom = false;
|
|
14184
|
+
this.warningListeners = [];
|
|
14169
14185
|
this.localStream = null;
|
|
14170
14186
|
this.channelType = "interactive";
|
|
14171
14187
|
// 피어 연결 관리
|
|
@@ -14219,7 +14235,10 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14219
14235
|
this.setState("connecting");
|
|
14220
14236
|
this.currentRoomId = options.roomId;
|
|
14221
14237
|
this.currentUserId = options.userId || null;
|
|
14222
|
-
this.
|
|
14238
|
+
this.broadcasterRequested = options.isBroadcaster || false;
|
|
14239
|
+
this.broadcasterGranted = false;
|
|
14240
|
+
this.allowedMemberIds = options.allowedMemberIds ? [...options.allowedMemberIds] : null;
|
|
14241
|
+
this.privateRoom = false;
|
|
14223
14242
|
this.localStream = options.localStream || null;
|
|
14224
14243
|
if (this.iceServers.length === 0) {
|
|
14225
14244
|
try {
|
|
@@ -14251,13 +14270,16 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14251
14270
|
return;
|
|
14252
14271
|
}
|
|
14253
14272
|
this.reconnectAttempts = 0;
|
|
14273
|
+
const joinData = {
|
|
14274
|
+
is_broadcaster: this.broadcasterRequested
|
|
14275
|
+
};
|
|
14276
|
+
if (this.allowedMemberIds) {
|
|
14277
|
+
joinData.allowed_member_ids = this.allowedMemberIds;
|
|
14278
|
+
}
|
|
14254
14279
|
this.sendSignaling({
|
|
14255
14280
|
type: "join",
|
|
14256
14281
|
room_id: this.currentRoomId,
|
|
14257
|
-
data:
|
|
14258
|
-
user_id: this.currentUserId,
|
|
14259
|
-
is_broadcaster: this.isBroadcaster
|
|
14260
|
-
}
|
|
14282
|
+
data: joinData
|
|
14261
14283
|
});
|
|
14262
14284
|
};
|
|
14263
14285
|
ws.onmessage = async (event) => {
|
|
@@ -14325,6 +14347,8 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14325
14347
|
if (data.channel_type) {
|
|
14326
14348
|
this.channelType = data.channel_type;
|
|
14327
14349
|
}
|
|
14350
|
+
this.broadcasterGranted = data.is_broadcaster === true;
|
|
14351
|
+
this.privateRoom = data.private === true;
|
|
14328
14352
|
const peers = data.peers || [];
|
|
14329
14353
|
for (const peer of peers) {
|
|
14330
14354
|
if (peer.peer_id !== this.currentPeerId) {
|
|
@@ -14365,9 +14389,16 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14365
14389
|
await this.handleICECandidate(msg.peer_id, msg.candidate);
|
|
14366
14390
|
}
|
|
14367
14391
|
break;
|
|
14392
|
+
case "warning": {
|
|
14393
|
+
const data = msg.data && typeof msg.data === "object" ? msg.data : {};
|
|
14394
|
+
this.emitWarning({
|
|
14395
|
+
code: typeof data.code === "string" ? data.code : "unknown",
|
|
14396
|
+
message: typeof data.message === "string" ? data.message : ""
|
|
14397
|
+
});
|
|
14398
|
+
break;
|
|
14399
|
+
}
|
|
14368
14400
|
case "error": {
|
|
14369
|
-
const
|
|
14370
|
-
const error = new Error(errorMsg);
|
|
14401
|
+
const error = new Error(_WebRTCAPI.signalingErrorMessage(msg.data));
|
|
14371
14402
|
this.emitError(error);
|
|
14372
14403
|
reject?.(error);
|
|
14373
14404
|
break;
|
|
@@ -14465,6 +14496,19 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14465
14496
|
}
|
|
14466
14497
|
this.remoteStreams.delete(remotePeerId);
|
|
14467
14498
|
}
|
|
14499
|
+
/**
|
|
14500
|
+
* 서버 error 메시지의 data 에서 안내 문구를 꺼낸다. 대부분은 문자열이지만 할당량, 동시접속 초과는
|
|
14501
|
+
* `{ error, message, ... }` 객체로 온다.
|
|
14502
|
+
*/
|
|
14503
|
+
static signalingErrorMessage(data) {
|
|
14504
|
+
if (typeof data === "string" && data) return data;
|
|
14505
|
+
if (data && typeof data === "object") {
|
|
14506
|
+
const obj = data;
|
|
14507
|
+
if (typeof obj.message === "string" && obj.message) return obj.message;
|
|
14508
|
+
if (typeof obj.error === "string" && obj.error) return obj.error;
|
|
14509
|
+
}
|
|
14510
|
+
return "Unknown error";
|
|
14511
|
+
}
|
|
14468
14512
|
sendSignaling(msg) {
|
|
14469
14513
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
14470
14514
|
this.ws.send(JSON.stringify(msg));
|
|
@@ -14530,6 +14574,8 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14530
14574
|
this.currentRoomId = null;
|
|
14531
14575
|
this.currentPeerId = null;
|
|
14532
14576
|
this.localStream = null;
|
|
14577
|
+
this.broadcasterGranted = false;
|
|
14578
|
+
this.privateRoom = false;
|
|
14533
14579
|
this.setState("disconnected");
|
|
14534
14580
|
}
|
|
14535
14581
|
/**
|
|
@@ -14547,9 +14593,15 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14547
14593
|
async switchRoom(roomId, options = {}) {
|
|
14548
14594
|
const localStream = options.localStream ?? this.localStream ?? void 0;
|
|
14549
14595
|
const userId = options.userId ?? this.currentUserId ?? void 0;
|
|
14550
|
-
const isBroadcaster = options.isBroadcaster ?? this.
|
|
14596
|
+
const isBroadcaster = options.isBroadcaster ?? this.broadcasterRequested;
|
|
14551
14597
|
this.disconnect();
|
|
14552
|
-
await this.connect({
|
|
14598
|
+
await this.connect({
|
|
14599
|
+
roomId,
|
|
14600
|
+
userId,
|
|
14601
|
+
isBroadcaster,
|
|
14602
|
+
localStream,
|
|
14603
|
+
allowedMemberIds: options.allowedMemberIds
|
|
14604
|
+
});
|
|
14553
14605
|
}
|
|
14554
14606
|
/**
|
|
14555
14607
|
* 소켓, 룸, 이벤트 리스너, 피어 연결을 **독립적으로** 갖는 새 WebRTC 세션을 만든다.
|
|
@@ -14602,6 +14654,19 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14602
14654
|
getChannelType() {
|
|
14603
14655
|
return this.channelType;
|
|
14604
14656
|
}
|
|
14657
|
+
/**
|
|
14658
|
+
* 서버가 정한 방송자 역할인지 조회한다. 요청(`isBroadcaster: true`)과 다를 수 있다 — 익명 연결이나
|
|
14659
|
+
* 채널의 방송자 역할이 없는 회원은 시청자로 참가한다(`onWarning` 으로 이유가 온다).
|
|
14660
|
+
*/
|
|
14661
|
+
isBroadcaster() {
|
|
14662
|
+
return this.broadcasterGranted;
|
|
14663
|
+
}
|
|
14664
|
+
/**
|
|
14665
|
+
* 지금 룸이 회원 허용 목록이 있는 비공개 룸인지 조회한다.
|
|
14666
|
+
*/
|
|
14667
|
+
isPrivateRoom() {
|
|
14668
|
+
return this.privateRoom;
|
|
14669
|
+
}
|
|
14605
14670
|
/**
|
|
14606
14671
|
* 원격 스트림 조회
|
|
14607
14672
|
*/
|
|
@@ -14672,6 +14737,24 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14672
14737
|
this.errorListeners = this.errorListeners.filter((cb) => cb !== callback);
|
|
14673
14738
|
};
|
|
14674
14739
|
}
|
|
14740
|
+
/**
|
|
14741
|
+
* 서버가 join 요청을 바꿨다는 알림 이벤트 (예: 방송자 요청을 시청자로 낮춤)
|
|
14742
|
+
*
|
|
14743
|
+
* @example
|
|
14744
|
+
* ```typescript
|
|
14745
|
+
* cb.webrtc.onWarning(({ code, message }) => {
|
|
14746
|
+
* if (code === 'broadcaster_role_required') showToast(message)
|
|
14747
|
+
* })
|
|
14748
|
+
* ```
|
|
14749
|
+
*/
|
|
14750
|
+
onWarning(callback) {
|
|
14751
|
+
this.warningListeners.push(callback);
|
|
14752
|
+
return () => {
|
|
14753
|
+
this.warningListeners = this.warningListeners.filter(
|
|
14754
|
+
(cb) => cb !== callback
|
|
14755
|
+
);
|
|
14756
|
+
};
|
|
14757
|
+
}
|
|
14675
14758
|
/**
|
|
14676
14759
|
* 피어 참가 이벤트
|
|
14677
14760
|
*/
|
|
@@ -14717,6 +14800,9 @@ var WebRTCAPI = class _WebRTCAPI {
|
|
|
14717
14800
|
emitError(error) {
|
|
14718
14801
|
this.errorListeners.forEach((cb) => cb(error));
|
|
14719
14802
|
}
|
|
14803
|
+
emitWarning(warning) {
|
|
14804
|
+
this.warningListeners.forEach((cb) => cb(warning));
|
|
14805
|
+
}
|
|
14720
14806
|
emitPeerJoined(peerId, info) {
|
|
14721
14807
|
this.peerJoinedListeners.forEach((cb) => cb(peerId, info));
|
|
14722
14808
|
}
|