GuidesAPI GuideChangelog
Log In
Guides

쿠폰 연동

쿠폰 연동 — 소스라이브 플레이어

소스라이브 쿠폰 기능을 자사몰의 쿠폰 시스템과 연동하는 가이드입니다. 어드민에서 쿠폰 클릭 이벤트 유형을 설정하면, 해당 유형에 맞는 브릿지 이벤트(sauceflexMoveCoupon)가 전송됩니다.

예상 소요 시간: 30분
📋 선행 조건: 브릿지 이벤트 연동 완료
⚙️ 어드민 또는 라이브콘솔 설정 필요
쿠폰 화면
플레이어 쿠폰 탭
▲ 플레이어의 쿠폰 탭 — 쿠폰 클릭 시 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을 엽니다. 라이브 시청을 끊지 않고 쿠폰 페이지를 확인할 때 적합합니다.
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 서버에서 두 값을 검증하도록 구현하세요.


bot에 문의하기