쿠폰 연동
소스라이브 쿠폰 기능을 자사몰의 쿠폰 시스템과 연동하는 가이드입니다. 어드민에서 쿠폰 클릭 이벤트 유형을 설정하면, 해당 유형에 맞는 브릿지 이벤트(sauceflexMoveCoupon)가 전송됩니다.
쿠폰 화면
▲ 플레이어의 쿠폰 탭 — 쿠폰 클릭 시 sauceflexMoveCoupon 이벤트가 전송됩니다
쿠폰 클릭 이벤트 유형
어드민에서 라이브 편성 시 또는 라이브콘솔에서 쿠폰의 클릭 이벤트 항목을 설정합니다. 선택한 유형에 따라 couponType 값이 달라지며, 브릿지 이벤트에서 이를 분기 처리합니다.
link
링크 이동
클릭 시 현재 창에서 지정된 URL로 이동
newWindow
새 창 열기
클릭 시 새 탭으로 지정된 URL을 오픈
download
쿠폰 코드 다운로드
쿠폰 코드로 자사몰 API 호출하여 발급
custom
사용자 정의
JSON metadata로 임의 값을 전달하여 처리
apiDownload
API 다운로드
고객사 API URL 등록 시 브릿지 이벤트 없이 자동 처리
⚠️
download, custom 유형은 브릿지 이벤트가 구현된 환경에서만 동작합니다. 브릿지 이벤트가 없으면 쿠폰 버튼이 동작하지 않을 수 있습니다.
이벤트 데이터
sauceflexMoveCoupon 수신 시 jsonData.params에 아래 필드가 전달됩니다. couponType을 기준으로 분기 처리합니다.
| 필드 | 타입 | 설명 |
|---|---|---|
| couponType | String | 'link' | 'newWindow' | 'download' | 'custom' | 'apiDownload' — 어드민에서 설정한 클릭 이벤트 유형 |
| linkUrl | String | null | 링크 이동 URL. link / newWindow 유형에서 사용 |
| couponCode | String | null | 쿠폰 코드. download 유형에서 사용 |
| couponId | String | null | 등록된 쿠폰 ID |
| couponName | String | null | 등록된 쿠폰명 |
| metaData | String | null | 사용자 정의 데이터(JSON 문자열). custom 유형에서 사용 |
| broadcastIdx | String | 이벤트가 발생한 라이브 ID |
유형별 구현 예시
환경을 선택한 뒤 couponType에 맞는 탭을 확인하세요.
현재 창에서 지정된 URL로 이동합니다.
linkUrl이 있고 couponType이 'link'인 경우에만 동작합니다.JavaScript
case 'sauceflexMoveCoupon': { const { linkUrl, couponType } = jsonData.params if (linkUrl && couponType === 'link') { window.location.href = linkUrl } break }
새 탭으로 지정된 URL을 엽니다. 라이브 시청을 끊지 않고 쿠폰 페이지를 확인할 때 적합합니다.
JavaScript
case 'sauceflexMoveCoupon': { const { linkUrl, couponType } = jsonData.params if (linkUrl && couponType === 'newWindow') { window.open(linkUrl, '_blank') } break }
쿠폰 코드(
couponCode)로 자사몰 API를 호출하여 쿠폰을 발급합니다. YOUR_COUPON_API를 실제 API URL로 교체하세요.JavaScript — 구조 참고용
case 'sauceflexMoveCoupon': { const { couponCode, couponType } = jsonData.params if (couponType !== 'download') break if (YOUR_COUPON_API) { fetch(`${YOUR_COUPON_API}/${couponCode}`) .then((res) => res.json()) .then((res) => { if (res.success) { window.alert(`${res.percent}% 쿠폰을 다운로드 받았습니다.`) } else { window.alert('쿠폰이 모두 소진되었습니다.') } }) .catch(() => window.alert('쿠폰 다운로드 중 오류가 발생했습니다.')) } else { location.href = `/coupon/download/${couponCode}` } break }
어드민에서 JSON 형태로 저장한
metaData를 파싱하여 쿠폰 ID, 할인율 등 임의 데이터를 처리합니다. metaData 예시: {"couponId":"1","title":"쿠폰1","discount":"10"}JavaScript — 구조 참고용
case 'sauceflexMoveCoupon': { const { metaData, couponType } = jsonData.params if (couponType !== 'custom' || !metaData) break const { couponId, title, discount } = JSON.parse(metaData) if (YOUR_COUPON_API) { fetch(`${YOUR_COUPON_API}/${couponId}`, { method: 'POST', body: JSON.stringify({ discount }) }) .then((res) => res.json()) .then((res) => { if (res.success) { window.alert(`${discount}% ${title} 쿠폰을 다운로드 받았습니다.`) } else { window.alert('쿠폰이 모두 소진되었습니다.') } }) .catch(() => window.alert('오류가 발생했습니다.')) } break }
API URL을 어드민에 등록하면 브릿지 이벤트 구현 없이 소스라이브 플레이어가 자동으로 API를 호출합니다. 고객사 API가 아래 응답 규격을 준수하면 플레이어가 결과를 UI에 직접 표시합니다.
| HTTP 상태 | 상황 | 플레이어 표시 문구 |
|---|---|---|
| 200 OK | 정상 다운로드 | "쿠폰이 다운로드 되었습니다." |
| 409 Conflict | 이미 다운로드한 쿠폰 | "쿠폰이 이미 다운로드 되었습니다." |
| 410 Gone | 쿠폰 소진 | "쿠폰이 소진 되었습니다." |
| 기타 | 오류 발생 | "관리자에게 문의하세요." |
Kotlin — SauceflexBridge.kt
class SauceflexBridge( private val context: Context, private val activity: Activity? ) { @JavascriptInterface fun sauceflexMoveCoupon(payload: String?) { val params = JSONObject(payload ?: "{}") val couponType = params.optString("couponType", "") val linkUrl = params.optString("linkUrl", "") val couponCode = params.optString("couponCode", "") when (couponType) { "link", "newWindow" -> { if (linkUrl.isEmpty()) return activity?.runOnUiThread { val intent = Intent(Intent.ACTION_VIEW, Uri.parse(linkUrl)) context.startActivity(intent) } } "download" -> { if (couponCode.isEmpty()) return // 자사몰 쿠폰 API 호출 후 결과 처리 activity?.runOnUiThread { Toast.makeText(context, "쿠폰이 발급되었습니다.", Toast.LENGTH_SHORT).show() } } "custom" -> { val metaStr = params.optString("metaData", "") if (metaStr.isEmpty()) return val meta = JSONObject(metaStr) val couponId = meta.optString("couponId", "") // couponId로 자사몰 쿠폰 API 호출 } "apiDownload" -> { // 플레이어가 자동 처리 — 브릿지 이벤트 구현 불필요 } } } }
Kotlin — WebView 등록
webView.addJavascriptInterface(SauceflexBridge(context, activity), "sauceflex")
⚡
apiDownload 유형은 플레이어가 직접 API를 호출하므로 @JavascriptInterface 구현이 필요 없습니다.
Swift — WKWebView 등록
contentController.add(self, name: "sauceflexMoveCoupon")
Swift — ViewController.swift
func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { switch message.name { case "sauceflexMoveCoupon": guard let body = message.body as? String, let data = body.data(using: .utf8), let params = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { return } let couponType = params["couponType"] as? String ?? "" let linkUrl = params["linkUrl"] as? String ?? "" let couponCode = params["couponCode"] as? String ?? "" switch couponType { case "link", "newWindow": guard !linkUrl.isEmpty(), let url = URL(string: linkUrl) else { return } DispatchQueue.main.async { UIApplication.shared.open(url) } case "download": guard !couponCode.isEmpty() else { return } // 자사몰 쿠폰 API 호출 후 결과 처리 DispatchQueue.main.async { // 발급 완료 Toast 또는 Alert 표시 } case "custom": guard let metaStr = params["metaData"] as? String, let metaData = try? JSONSerialization.jsonObject( with: metaStr.data(using: .utf8)!) as? [String: Any] else { return } let couponId = metaData["couponId"] as? String ?? "" // couponId로 자사몰 쿠폰 API 호출 case "apiDownload": break // 플레이어가 자동 처리 default: break } default: break } }
⚡ API 다운로드 필수 파라미터: 플레이어가 API 호출 시
couponCode(쿠폰 식별값)와 accessToken(고객사에서 모비두에 전달한 토큰)을 자동으로 포함합니다. API 서버에서 두 값을 검증하도록 구현하세요.
Updated 1 day ago