connectbase-client 4.1.0 → 4.3.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 +84 -0
- package/dist/connect-base.umd.js +5 -5
- package/dist/index.d.mts +253 -34
- package/dist/index.d.ts +253 -34
- package/dist/index.js +153 -9
- package/dist/index.mjs +153 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5042,6 +5042,101 @@ var NativeAPI = class {
|
|
|
5042
5042
|
}
|
|
5043
5043
|
);
|
|
5044
5044
|
});
|
|
5045
|
+
},
|
|
5046
|
+
/**
|
|
5047
|
+
* 위치 추적 시작.
|
|
5048
|
+
*
|
|
5049
|
+
* 패키징 앱(모바일)에서는 네이티브가 `nativeLocationUpdate` 이벤트로 갱신을 보내고,
|
|
5050
|
+
* 웹/데스크톱에서는 Geolocation `watchPosition` 을 사용한다.
|
|
5051
|
+
*
|
|
5052
|
+
* @example
|
|
5053
|
+
* ```typescript
|
|
5054
|
+
* const watch = await cb.native.location.watchPosition((pos) => console.log(pos))
|
|
5055
|
+
* // 나중에
|
|
5056
|
+
* await watch.clear()
|
|
5057
|
+
* ```
|
|
5058
|
+
*/
|
|
5059
|
+
watchPosition: async (onUpdate, options) => {
|
|
5060
|
+
const platform = this.getPlatform();
|
|
5061
|
+
const bridge = window.NativeBridge;
|
|
5062
|
+
if (platform === "mobile" && bridge?.location?.watchPosition) {
|
|
5063
|
+
const { watchId } = await bridge.location.watchPosition(options);
|
|
5064
|
+
const handler = (event) => {
|
|
5065
|
+
const detail = event.detail;
|
|
5066
|
+
if (!detail || detail.watchId && detail.watchId !== watchId) return;
|
|
5067
|
+
onUpdate({
|
|
5068
|
+
latitude: detail.latitude,
|
|
5069
|
+
longitude: detail.longitude,
|
|
5070
|
+
altitude: detail.altitude,
|
|
5071
|
+
accuracy: detail.accuracy,
|
|
5072
|
+
timestamp: detail.timestamp
|
|
5073
|
+
});
|
|
5074
|
+
};
|
|
5075
|
+
window.addEventListener("nativeLocationUpdate", handler);
|
|
5076
|
+
return {
|
|
5077
|
+
watchId,
|
|
5078
|
+
clear: async () => {
|
|
5079
|
+
window.removeEventListener("nativeLocationUpdate", handler);
|
|
5080
|
+
await bridge.location?.stopWatch(watchId);
|
|
5081
|
+
}
|
|
5082
|
+
};
|
|
5083
|
+
}
|
|
5084
|
+
const id = navigator.geolocation.watchPosition(
|
|
5085
|
+
(pos) => onUpdate({
|
|
5086
|
+
latitude: pos.coords.latitude,
|
|
5087
|
+
longitude: pos.coords.longitude,
|
|
5088
|
+
altitude: pos.coords.altitude,
|
|
5089
|
+
accuracy: pos.coords.accuracy,
|
|
5090
|
+
timestamp: pos.timestamp
|
|
5091
|
+
}),
|
|
5092
|
+
void 0,
|
|
5093
|
+
{
|
|
5094
|
+
enableHighAccuracy: options?.accuracy === "high",
|
|
5095
|
+
timeout: 1e4,
|
|
5096
|
+
maximumAge: 0
|
|
5097
|
+
}
|
|
5098
|
+
);
|
|
5099
|
+
return {
|
|
5100
|
+
watchId: String(id),
|
|
5101
|
+
clear: async () => navigator.geolocation.clearWatch(id)
|
|
5102
|
+
};
|
|
5103
|
+
}
|
|
5104
|
+
};
|
|
5105
|
+
/**
|
|
5106
|
+
* 모바일 전용 푸시 API
|
|
5107
|
+
*
|
|
5108
|
+
* 패키징 앱은 raw 디바이스 토큰(iOS=APNS, Android=FCM)을 발급받아
|
|
5109
|
+
* `cb.push.registerDevice()` 로 등록해야 서버 발송이 가능하다.
|
|
5110
|
+
*/
|
|
5111
|
+
this.push = {
|
|
5112
|
+
/**
|
|
5113
|
+
* 네이티브 디바이스 푸시 토큰 조회.
|
|
5114
|
+
* 웹(패키징 앱이 아닌 경우)에서는 null 을 반환한다.
|
|
5115
|
+
*/
|
|
5116
|
+
getToken: async () => {
|
|
5117
|
+
if (this.getPlatform() === "mobile" && window.NativeBridge?.push) {
|
|
5118
|
+
return window.NativeBridge.push.getToken();
|
|
5119
|
+
}
|
|
5120
|
+
return null;
|
|
5121
|
+
},
|
|
5122
|
+
/** 알림 권한 요청 (웹은 Web Notification 권한) */
|
|
5123
|
+
requestPermission: async () => {
|
|
5124
|
+
return this.notification.requestPermission();
|
|
5125
|
+
},
|
|
5126
|
+
/** 로컬 알림 예약 (패키징 앱 전용) */
|
|
5127
|
+
scheduleLocal: async (options) => {
|
|
5128
|
+
if (this.getPlatform() === "mobile" && window.NativeBridge?.push) {
|
|
5129
|
+
const result = await window.NativeBridge.push.scheduleLocal(options);
|
|
5130
|
+
return result.notificationId;
|
|
5131
|
+
}
|
|
5132
|
+
return null;
|
|
5133
|
+
},
|
|
5134
|
+
/** 앱 아이콘 배지 숫자 설정 (미지원 플랫폼에서는 무시) */
|
|
5135
|
+
setBadgeCount: async (count) => {
|
|
5136
|
+
const setter = window.NativeBridge?.push?.setBadgeCount;
|
|
5137
|
+
if (!setter) return false;
|
|
5138
|
+
const result = await setter(count);
|
|
5139
|
+
return result.success;
|
|
5045
5140
|
}
|
|
5046
5141
|
};
|
|
5047
5142
|
/**
|
|
@@ -5057,6 +5152,16 @@ var NativeAPI = class {
|
|
|
5057
5152
|
const result = await window.NativeBridge.notification.show(options);
|
|
5058
5153
|
return result.success;
|
|
5059
5154
|
}
|
|
5155
|
+
if (platform === "mobile" && window.NativeBridge?.push?.scheduleLocal) {
|
|
5156
|
+
const granted = await this.notification.requestPermission();
|
|
5157
|
+
if (!granted) return false;
|
|
5158
|
+
await window.NativeBridge.push.scheduleLocal({
|
|
5159
|
+
title: options.title,
|
|
5160
|
+
body: options.body,
|
|
5161
|
+
trigger: null
|
|
5162
|
+
});
|
|
5163
|
+
return true;
|
|
5164
|
+
}
|
|
5060
5165
|
if (!("Notification" in window)) return false;
|
|
5061
5166
|
if (Notification.permission !== "granted") {
|
|
5062
5167
|
const permission = await Notification.requestPermission();
|
|
@@ -5096,6 +5201,10 @@ var NativeAPI = class {
|
|
|
5096
5201
|
const result = await window.NativeBridge.shell.openExternal(url);
|
|
5097
5202
|
return result.success;
|
|
5098
5203
|
}
|
|
5204
|
+
if (platform === "mobile" && window.NativeBridge?.browser) {
|
|
5205
|
+
const result = await window.NativeBridge.browser.openExternal(url);
|
|
5206
|
+
return result.success;
|
|
5207
|
+
}
|
|
5099
5208
|
window.open(url, "_blank");
|
|
5100
5209
|
return true;
|
|
5101
5210
|
}
|
|
@@ -5341,6 +5450,26 @@ var NativeAPI = class {
|
|
|
5341
5450
|
if (bridge?.camera || window.ReactNativeWebView) return "mobile";
|
|
5342
5451
|
return "web";
|
|
5343
5452
|
}
|
|
5453
|
+
/**
|
|
5454
|
+
* 패키징 앱(네이티브 셸) 안에서 실행 중인지
|
|
5455
|
+
*/
|
|
5456
|
+
isNativeApp() {
|
|
5457
|
+
return this.getPlatform() !== "web";
|
|
5458
|
+
}
|
|
5459
|
+
/**
|
|
5460
|
+
* 패키징 앱 정보 조회 (웹에서는 null)
|
|
5461
|
+
*
|
|
5462
|
+
* @example
|
|
5463
|
+
* ```typescript
|
|
5464
|
+
* const info = await cb.native.getAppInfo()
|
|
5465
|
+
* // { platform: 'react-native', os: 'ios', appVersion: '1.0.0', webUrl: '...' }
|
|
5466
|
+
* ```
|
|
5467
|
+
*/
|
|
5468
|
+
async getAppInfo() {
|
|
5469
|
+
const getInfo = this.bridge?.app?.getInfo;
|
|
5470
|
+
if (!getInfo) return null;
|
|
5471
|
+
return getInfo();
|
|
5472
|
+
}
|
|
5344
5473
|
/**
|
|
5345
5474
|
* 특정 네이티브 기능 지원 여부 확인
|
|
5346
5475
|
*/
|
|
@@ -8858,14 +8987,19 @@ var SubscriptionAPI = class {
|
|
|
8858
8987
|
);
|
|
8859
8988
|
}
|
|
8860
8989
|
/**
|
|
8861
|
-
* 요금제 스왑 (플랜 변경) — Paddle
|
|
8990
|
+
* 요금제 스왑 (플랜 변경) — MoR(Paddle · Dodo) 구독 전용
|
|
8862
8991
|
*
|
|
8863
|
-
* 하나의 구독을 유지한 채(id 동일) 다른 recurring price 로 갈아타고 남은 금액을
|
|
8864
|
-
* 즉시 정산합니다. 예) 혼자→가족 in-place 업그레이드 — 이미 낸 혼자 미사용분을
|
|
8865
|
-
* 첫 가족 청구에서 차감합니다. 즉시청구 차액의 결제 확정은 웹훅(SoT)으로
|
|
8992
|
+
* 하나의 구독을 유지한 채(id 동일) 다른 recurring price/product 로 갈아타고 남은 금액을 PG 의
|
|
8993
|
+
* proration 으로 즉시 정산합니다. 예) 혼자→가족 in-place 업그레이드 — 이미 낸 혼자 미사용분을
|
|
8994
|
+
* PG 가 자동 크레딧해 첫 가족 청구에서 차감합니다. 즉시청구 차액의 결제 확정은 웹훅(SoT)으로
|
|
8995
|
+
* 수렴합니다.
|
|
8866
8996
|
*
|
|
8867
8997
|
* `proration_mode` 미지정 시 `prorated_immediately`(업그레이드 기본)가 적용됩니다.
|
|
8868
|
-
*
|
|
8998
|
+
* 프로바이더가 지원하지 않는 정산 모드는 근사 대체 없이 400 `invalid_proration_mode` 입니다
|
|
8999
|
+
* (지원표는 {@link ProrationMode} 참조). 확정 전 정산액을 보여주려면 {@link previewChangePlan}
|
|
9000
|
+
* 을 먼저 호출하세요.
|
|
9001
|
+
*
|
|
9002
|
+
* toss/stripe/payapp/paypal 구독은 400 `plan_change_unsupported` 입니다.
|
|
8869
9003
|
*
|
|
8870
9004
|
* @param subscriptionId - 구독 ID
|
|
8871
9005
|
* @param data - 새 price + 정산 옵션
|
|
@@ -8874,7 +9008,7 @@ var SubscriptionAPI = class {
|
|
|
8874
9008
|
* @example
|
|
8875
9009
|
* ```typescript
|
|
8876
9010
|
* await client.subscription.changePlan(subscriptionId, {
|
|
8877
|
-
* provider_price_ref: 'pri_family_monthly',
|
|
9011
|
+
* provider_price_ref: 'pri_family_monthly', // Dodo 는 'pdt_*'
|
|
8878
9012
|
* plan_name: '가족 플랜',
|
|
8879
9013
|
* amount: 24900,
|
|
8880
9014
|
* proration_mode: 'prorated_immediately',
|
|
@@ -8889,12 +9023,20 @@ var SubscriptionAPI = class {
|
|
|
8889
9023
|
);
|
|
8890
9024
|
}
|
|
8891
9025
|
/**
|
|
8892
|
-
* 요금제 스왑 정산 미리보기 — 실제 적용 없이 청구/크레딧 금액만 계산 (Paddle
|
|
9026
|
+
* 요금제 스왑 정산 미리보기 — 실제 적용 없이 청구/크레딧 금액만 계산 (Paddle · Dodo)
|
|
8893
9027
|
*
|
|
8894
|
-
* 세금·기존 크레딧 잔액까지 반영된
|
|
9028
|
+
* 세금·기존 크레딧 잔액까지 반영된 PG 계산값을 반환하므로, 확정 화면에
|
|
8895
9029
|
* "지금 ₩X 청구, 이후 ₩Y/주기 (다음 청구일 …)" 를 그대로 노출하면 됩니다.
|
|
8896
9030
|
* 직접 차액을 계산하지 마세요 — 이 값이 권위입니다.
|
|
8897
9031
|
*
|
|
9032
|
+
* **금액은 반드시 짝이 되는 통화와 함께 표시하세요.** 즉시청구는 고객 표시통화
|
|
9033
|
+
* (`currency`), 정기 단가는 상품 통화(`recurring_currency`, 비면 `currency` 와 동일)입니다 —
|
|
9034
|
+
* MoR 의 adaptive currency 에서 둘이 다를 수 있습니다.
|
|
9035
|
+
*
|
|
9036
|
+
* 미리보기를 제공하지 않는 프로바이더는 400 `plan_change_preview_unsupported` 입니다.
|
|
9037
|
+
* 이는 **미리보기만** 없다는 뜻이며 {@link changePlan} 자체는 동작합니다
|
|
9038
|
+
* (요금제 변경 자체가 불가한 경우는 `plan_change_unsupported` 로 구분됩니다).
|
|
9039
|
+
*
|
|
8898
9040
|
* @param subscriptionId - 구독 ID
|
|
8899
9041
|
* @param data - 새 price + 정산 옵션 ({@link changePlan} 과 동일 body)
|
|
8900
9042
|
* @returns 정산 미리보기 (지금 청구/크레딧 · 새 정기 청구액 · 다음 청구일)
|
|
@@ -8904,7 +9046,9 @@ var SubscriptionAPI = class {
|
|
|
8904
9046
|
* const preview = await client.subscription.previewChangePlan(subscriptionId, {
|
|
8905
9047
|
* provider_price_ref: 'pri_family_monthly',
|
|
8906
9048
|
* })
|
|
8907
|
-
* //
|
|
9049
|
+
* // 통화를 함께 포맷한다 — 금액만 쓰면 다통화에서 거짓 금액이 된다
|
|
9050
|
+
* format(preview.immediate_charge_amount, preview.currency)
|
|
9051
|
+
* format(preview.recurring_amount, preview.recurring_currency ?? preview.currency)
|
|
8908
9052
|
* ```
|
|
8909
9053
|
*/
|
|
8910
9054
|
async previewChangePlan(subscriptionId, data) {
|
package/dist/index.mjs
CHANGED
|
@@ -4996,6 +4996,101 @@ var NativeAPI = class {
|
|
|
4996
4996
|
}
|
|
4997
4997
|
);
|
|
4998
4998
|
});
|
|
4999
|
+
},
|
|
5000
|
+
/**
|
|
5001
|
+
* 위치 추적 시작.
|
|
5002
|
+
*
|
|
5003
|
+
* 패키징 앱(모바일)에서는 네이티브가 `nativeLocationUpdate` 이벤트로 갱신을 보내고,
|
|
5004
|
+
* 웹/데스크톱에서는 Geolocation `watchPosition` 을 사용한다.
|
|
5005
|
+
*
|
|
5006
|
+
* @example
|
|
5007
|
+
* ```typescript
|
|
5008
|
+
* const watch = await cb.native.location.watchPosition((pos) => console.log(pos))
|
|
5009
|
+
* // 나중에
|
|
5010
|
+
* await watch.clear()
|
|
5011
|
+
* ```
|
|
5012
|
+
*/
|
|
5013
|
+
watchPosition: async (onUpdate, options) => {
|
|
5014
|
+
const platform = this.getPlatform();
|
|
5015
|
+
const bridge = window.NativeBridge;
|
|
5016
|
+
if (platform === "mobile" && bridge?.location?.watchPosition) {
|
|
5017
|
+
const { watchId } = await bridge.location.watchPosition(options);
|
|
5018
|
+
const handler = (event) => {
|
|
5019
|
+
const detail = event.detail;
|
|
5020
|
+
if (!detail || detail.watchId && detail.watchId !== watchId) return;
|
|
5021
|
+
onUpdate({
|
|
5022
|
+
latitude: detail.latitude,
|
|
5023
|
+
longitude: detail.longitude,
|
|
5024
|
+
altitude: detail.altitude,
|
|
5025
|
+
accuracy: detail.accuracy,
|
|
5026
|
+
timestamp: detail.timestamp
|
|
5027
|
+
});
|
|
5028
|
+
};
|
|
5029
|
+
window.addEventListener("nativeLocationUpdate", handler);
|
|
5030
|
+
return {
|
|
5031
|
+
watchId,
|
|
5032
|
+
clear: async () => {
|
|
5033
|
+
window.removeEventListener("nativeLocationUpdate", handler);
|
|
5034
|
+
await bridge.location?.stopWatch(watchId);
|
|
5035
|
+
}
|
|
5036
|
+
};
|
|
5037
|
+
}
|
|
5038
|
+
const id = navigator.geolocation.watchPosition(
|
|
5039
|
+
(pos) => onUpdate({
|
|
5040
|
+
latitude: pos.coords.latitude,
|
|
5041
|
+
longitude: pos.coords.longitude,
|
|
5042
|
+
altitude: pos.coords.altitude,
|
|
5043
|
+
accuracy: pos.coords.accuracy,
|
|
5044
|
+
timestamp: pos.timestamp
|
|
5045
|
+
}),
|
|
5046
|
+
void 0,
|
|
5047
|
+
{
|
|
5048
|
+
enableHighAccuracy: options?.accuracy === "high",
|
|
5049
|
+
timeout: 1e4,
|
|
5050
|
+
maximumAge: 0
|
|
5051
|
+
}
|
|
5052
|
+
);
|
|
5053
|
+
return {
|
|
5054
|
+
watchId: String(id),
|
|
5055
|
+
clear: async () => navigator.geolocation.clearWatch(id)
|
|
5056
|
+
};
|
|
5057
|
+
}
|
|
5058
|
+
};
|
|
5059
|
+
/**
|
|
5060
|
+
* 모바일 전용 푸시 API
|
|
5061
|
+
*
|
|
5062
|
+
* 패키징 앱은 raw 디바이스 토큰(iOS=APNS, Android=FCM)을 발급받아
|
|
5063
|
+
* `cb.push.registerDevice()` 로 등록해야 서버 발송이 가능하다.
|
|
5064
|
+
*/
|
|
5065
|
+
this.push = {
|
|
5066
|
+
/**
|
|
5067
|
+
* 네이티브 디바이스 푸시 토큰 조회.
|
|
5068
|
+
* 웹(패키징 앱이 아닌 경우)에서는 null 을 반환한다.
|
|
5069
|
+
*/
|
|
5070
|
+
getToken: async () => {
|
|
5071
|
+
if (this.getPlatform() === "mobile" && window.NativeBridge?.push) {
|
|
5072
|
+
return window.NativeBridge.push.getToken();
|
|
5073
|
+
}
|
|
5074
|
+
return null;
|
|
5075
|
+
},
|
|
5076
|
+
/** 알림 권한 요청 (웹은 Web Notification 권한) */
|
|
5077
|
+
requestPermission: async () => {
|
|
5078
|
+
return this.notification.requestPermission();
|
|
5079
|
+
},
|
|
5080
|
+
/** 로컬 알림 예약 (패키징 앱 전용) */
|
|
5081
|
+
scheduleLocal: async (options) => {
|
|
5082
|
+
if (this.getPlatform() === "mobile" && window.NativeBridge?.push) {
|
|
5083
|
+
const result = await window.NativeBridge.push.scheduleLocal(options);
|
|
5084
|
+
return result.notificationId;
|
|
5085
|
+
}
|
|
5086
|
+
return null;
|
|
5087
|
+
},
|
|
5088
|
+
/** 앱 아이콘 배지 숫자 설정 (미지원 플랫폼에서는 무시) */
|
|
5089
|
+
setBadgeCount: async (count) => {
|
|
5090
|
+
const setter = window.NativeBridge?.push?.setBadgeCount;
|
|
5091
|
+
if (!setter) return false;
|
|
5092
|
+
const result = await setter(count);
|
|
5093
|
+
return result.success;
|
|
4999
5094
|
}
|
|
5000
5095
|
};
|
|
5001
5096
|
/**
|
|
@@ -5011,6 +5106,16 @@ var NativeAPI = class {
|
|
|
5011
5106
|
const result = await window.NativeBridge.notification.show(options);
|
|
5012
5107
|
return result.success;
|
|
5013
5108
|
}
|
|
5109
|
+
if (platform === "mobile" && window.NativeBridge?.push?.scheduleLocal) {
|
|
5110
|
+
const granted = await this.notification.requestPermission();
|
|
5111
|
+
if (!granted) return false;
|
|
5112
|
+
await window.NativeBridge.push.scheduleLocal({
|
|
5113
|
+
title: options.title,
|
|
5114
|
+
body: options.body,
|
|
5115
|
+
trigger: null
|
|
5116
|
+
});
|
|
5117
|
+
return true;
|
|
5118
|
+
}
|
|
5014
5119
|
if (!("Notification" in window)) return false;
|
|
5015
5120
|
if (Notification.permission !== "granted") {
|
|
5016
5121
|
const permission = await Notification.requestPermission();
|
|
@@ -5050,6 +5155,10 @@ var NativeAPI = class {
|
|
|
5050
5155
|
const result = await window.NativeBridge.shell.openExternal(url);
|
|
5051
5156
|
return result.success;
|
|
5052
5157
|
}
|
|
5158
|
+
if (platform === "mobile" && window.NativeBridge?.browser) {
|
|
5159
|
+
const result = await window.NativeBridge.browser.openExternal(url);
|
|
5160
|
+
return result.success;
|
|
5161
|
+
}
|
|
5053
5162
|
window.open(url, "_blank");
|
|
5054
5163
|
return true;
|
|
5055
5164
|
}
|
|
@@ -5295,6 +5404,26 @@ var NativeAPI = class {
|
|
|
5295
5404
|
if (bridge?.camera || window.ReactNativeWebView) return "mobile";
|
|
5296
5405
|
return "web";
|
|
5297
5406
|
}
|
|
5407
|
+
/**
|
|
5408
|
+
* 패키징 앱(네이티브 셸) 안에서 실행 중인지
|
|
5409
|
+
*/
|
|
5410
|
+
isNativeApp() {
|
|
5411
|
+
return this.getPlatform() !== "web";
|
|
5412
|
+
}
|
|
5413
|
+
/**
|
|
5414
|
+
* 패키징 앱 정보 조회 (웹에서는 null)
|
|
5415
|
+
*
|
|
5416
|
+
* @example
|
|
5417
|
+
* ```typescript
|
|
5418
|
+
* const info = await cb.native.getAppInfo()
|
|
5419
|
+
* // { platform: 'react-native', os: 'ios', appVersion: '1.0.0', webUrl: '...' }
|
|
5420
|
+
* ```
|
|
5421
|
+
*/
|
|
5422
|
+
async getAppInfo() {
|
|
5423
|
+
const getInfo = this.bridge?.app?.getInfo;
|
|
5424
|
+
if (!getInfo) return null;
|
|
5425
|
+
return getInfo();
|
|
5426
|
+
}
|
|
5298
5427
|
/**
|
|
5299
5428
|
* 특정 네이티브 기능 지원 여부 확인
|
|
5300
5429
|
*/
|
|
@@ -8812,14 +8941,19 @@ var SubscriptionAPI = class {
|
|
|
8812
8941
|
);
|
|
8813
8942
|
}
|
|
8814
8943
|
/**
|
|
8815
|
-
* 요금제 스왑 (플랜 변경) — Paddle
|
|
8944
|
+
* 요금제 스왑 (플랜 변경) — MoR(Paddle · Dodo) 구독 전용
|
|
8816
8945
|
*
|
|
8817
|
-
* 하나의 구독을 유지한 채(id 동일) 다른 recurring price 로 갈아타고 남은 금액을
|
|
8818
|
-
* 즉시 정산합니다. 예) 혼자→가족 in-place 업그레이드 — 이미 낸 혼자 미사용분을
|
|
8819
|
-
* 첫 가족 청구에서 차감합니다. 즉시청구 차액의 결제 확정은 웹훅(SoT)으로
|
|
8946
|
+
* 하나의 구독을 유지한 채(id 동일) 다른 recurring price/product 로 갈아타고 남은 금액을 PG 의
|
|
8947
|
+
* proration 으로 즉시 정산합니다. 예) 혼자→가족 in-place 업그레이드 — 이미 낸 혼자 미사용분을
|
|
8948
|
+
* PG 가 자동 크레딧해 첫 가족 청구에서 차감합니다. 즉시청구 차액의 결제 확정은 웹훅(SoT)으로
|
|
8949
|
+
* 수렴합니다.
|
|
8820
8950
|
*
|
|
8821
8951
|
* `proration_mode` 미지정 시 `prorated_immediately`(업그레이드 기본)가 적용됩니다.
|
|
8822
|
-
*
|
|
8952
|
+
* 프로바이더가 지원하지 않는 정산 모드는 근사 대체 없이 400 `invalid_proration_mode` 입니다
|
|
8953
|
+
* (지원표는 {@link ProrationMode} 참조). 확정 전 정산액을 보여주려면 {@link previewChangePlan}
|
|
8954
|
+
* 을 먼저 호출하세요.
|
|
8955
|
+
*
|
|
8956
|
+
* toss/stripe/payapp/paypal 구독은 400 `plan_change_unsupported` 입니다.
|
|
8823
8957
|
*
|
|
8824
8958
|
* @param subscriptionId - 구독 ID
|
|
8825
8959
|
* @param data - 새 price + 정산 옵션
|
|
@@ -8828,7 +8962,7 @@ var SubscriptionAPI = class {
|
|
|
8828
8962
|
* @example
|
|
8829
8963
|
* ```typescript
|
|
8830
8964
|
* await client.subscription.changePlan(subscriptionId, {
|
|
8831
|
-
* provider_price_ref: 'pri_family_monthly',
|
|
8965
|
+
* provider_price_ref: 'pri_family_monthly', // Dodo 는 'pdt_*'
|
|
8832
8966
|
* plan_name: '가족 플랜',
|
|
8833
8967
|
* amount: 24900,
|
|
8834
8968
|
* proration_mode: 'prorated_immediately',
|
|
@@ -8843,12 +8977,20 @@ var SubscriptionAPI = class {
|
|
|
8843
8977
|
);
|
|
8844
8978
|
}
|
|
8845
8979
|
/**
|
|
8846
|
-
* 요금제 스왑 정산 미리보기 — 실제 적용 없이 청구/크레딧 금액만 계산 (Paddle
|
|
8980
|
+
* 요금제 스왑 정산 미리보기 — 실제 적용 없이 청구/크레딧 금액만 계산 (Paddle · Dodo)
|
|
8847
8981
|
*
|
|
8848
|
-
* 세금·기존 크레딧 잔액까지 반영된
|
|
8982
|
+
* 세금·기존 크레딧 잔액까지 반영된 PG 계산값을 반환하므로, 확정 화면에
|
|
8849
8983
|
* "지금 ₩X 청구, 이후 ₩Y/주기 (다음 청구일 …)" 를 그대로 노출하면 됩니다.
|
|
8850
8984
|
* 직접 차액을 계산하지 마세요 — 이 값이 권위입니다.
|
|
8851
8985
|
*
|
|
8986
|
+
* **금액은 반드시 짝이 되는 통화와 함께 표시하세요.** 즉시청구는 고객 표시통화
|
|
8987
|
+
* (`currency`), 정기 단가는 상품 통화(`recurring_currency`, 비면 `currency` 와 동일)입니다 —
|
|
8988
|
+
* MoR 의 adaptive currency 에서 둘이 다를 수 있습니다.
|
|
8989
|
+
*
|
|
8990
|
+
* 미리보기를 제공하지 않는 프로바이더는 400 `plan_change_preview_unsupported` 입니다.
|
|
8991
|
+
* 이는 **미리보기만** 없다는 뜻이며 {@link changePlan} 자체는 동작합니다
|
|
8992
|
+
* (요금제 변경 자체가 불가한 경우는 `plan_change_unsupported` 로 구분됩니다).
|
|
8993
|
+
*
|
|
8852
8994
|
* @param subscriptionId - 구독 ID
|
|
8853
8995
|
* @param data - 새 price + 정산 옵션 ({@link changePlan} 과 동일 body)
|
|
8854
8996
|
* @returns 정산 미리보기 (지금 청구/크레딧 · 새 정기 청구액 · 다음 청구일)
|
|
@@ -8858,7 +9000,9 @@ var SubscriptionAPI = class {
|
|
|
8858
9000
|
* const preview = await client.subscription.previewChangePlan(subscriptionId, {
|
|
8859
9001
|
* provider_price_ref: 'pri_family_monthly',
|
|
8860
9002
|
* })
|
|
8861
|
-
* //
|
|
9003
|
+
* // 통화를 함께 포맷한다 — 금액만 쓰면 다통화에서 거짓 금액이 된다
|
|
9004
|
+
* format(preview.immediate_charge_amount, preview.currency)
|
|
9005
|
+
* format(preview.recurring_amount, preview.recurring_currency ?? preview.currency)
|
|
8862
9006
|
* ```
|
|
8863
9007
|
*/
|
|
8864
9008
|
async previewChangePlan(subscriptionId, data) {
|