페이 스테이션을 Firebase 인증과 함께 사용하는 방법

애플리케이션에서 Firebase를 사용하여 사용자 인증을 이미 구현한 경우 Firebase에서 결제 토큰을 생성한 다음 이를 애플리케이션의 클라이언트 측에 전달하여 결제 UI를 열 수 있습니다.

이 연동 옵션을 사용하는 경우 구매 시 비용을 지불하는 사용자의 국가 및 통화를 결정하는 로직을 독립적으로 구현해야 합니다.

연동 절차:

  1. 프로젝트를 생성합니다.
  1. 관리자 페이지에 등록한 후 새 프로젝트를 생성합니다. 이후 단계에서 생성하는 프로젝트의 ID가 필요합니다.

  1. 카탈로그 설정 방법:
    • 엑솔라 측에서 아이템 카탈로그를 생성합니다. 아이템을 수동으로 추가하거나 Google Play 또는 PlayFab에서 가져올 수 있습니다.
    • Store 라이브러리를 사용하여 애플리케이션의 클라이언트 측에서 카탈로그 가져오기 및 표시를 구현합니다.

  1. 아이템 구매 설정 방법:
    • Firebase 클라우드 함수를 사용하여 애플리케이션의 클라이언트 측에서 사용자 및 아이템 데이터로 주문을 생성합니다.
    • Payments 라이브러리를 사용하여 애플리케이션의 클라이언트 측에서 결제 UI 열기를 구현합니다.

  1. 주문 상태 추적을 설정합니다.

주의

연동을 완료하고 실제 결제 수락을 시작하려면 엑솔라와 라이선스 계약을 체결해야 합니다.

모든 연동 단계에서 계약에 서명할 수 있지만, 검토 절차에 최대 3일(영업일 기준)이 소요될 수 있다는 점에 유의해 주세요.

예를 들어 Firebase 인증과 Pay Station의 결합하는 사례를 샘플 웹 애플리케이션을 사용하여 구현해 보세요. 샘플 웹 애플리케이션의 소스 코드는 GitHub에서 확인할 수 있습니다.

프로젝트 생성

관리자 페이지는 엑솔라 기능을 구성하고 분석과 트랜잭션 작업에 사용하는 기본 도구입니다.

등록할 때 지정한 회사 및 애플리케이션에 대한 데이터는 엑솔라와의 라이선스 계약 초안을 생성하고 적합한 솔루션 추천을 생성하는 데 사용됩니다. 나중에 해당 데이터를 변경할 수 있지만 등록할 때 올바른 데이터를 제공하면 라이선스 계약 체결 프로세스가 빨라집니다.

프로젝트를 생성하는 방법:

  1. 관리자 페이지에 등록하기.
알림
암호 생성 규칙

관리자 페이지의 암호는 라틴 문자, 숫자 및 특수 문자로 구성될 수 있으며 다음 최소 요건을 충족해야 합니다:

  • 8자 이상
  • 숫자 하나 이상
  • 대문자 하나 이상
  • 소문자 하나 이상

암호 보안을 위해 다음을 권장합니다.

  • 90일에 한 번 이상 암호 변경
  • 기존 계정 암호에 사용한 마지막 4개의 문자와 일치하지 않는 새 암호 사용
  • 다른 곳에서 사용하는 암호와 일치하지 않는 고유한 암호 사용
  • 쉽게 액세스할 수 없는 곳에 암호 저장
  • 암호 관리 프로그램을 사용하여 암호 저장

관리자 페이지는 2단계 인증을 사용하며 인증을 시도할 때마다 확인 코드를 전송합니다.

  1. 계정을 생성하려면 다음 정보를 입력하세요:
    1. 이름 및 성.
    2. 회사 내 역할.
    3. 회사 이름. 개인으로 등록하는 경우 성명을 입력합니다.
    4. 사용자와 사용자의 게임 또는 제품에 대한 자세한 정보로 연결되는 링크(선택 사항).
    5. 사용자의 국가 또는 지역.
  1. 다음 단계를 클릭합니다.
  1. 첫 번째 프로젝트를 자동으로 생성하려면 프로젝트에 대한 주요 정보를 다음과 같이 지정하세요:
    1. 프로젝트 유형을 선택합니다: 게임, 게임 플랫폼 또는 기타.
    2. 프로젝트 이름을 영어로 입력합니다.
    3. 하나 또는 여러 개의 출시 플랫폼을 선택합니다.
    4. 사용 중이거나 사용할 예정인 수익 실현 옵션을 선택합니다.
    5. 개발 단계를 선택합니다.
    6. 프로젝트 유형이 게임인 경우, 게임 장르와 게임 엔진을 선택합니다.
    7. 게임 또는 제품에 대한 링크를 추가합니다(선택 사항).
  1. 종료를 클릭합니다.

프로젝트가 생성되면 회사 페이지로 리디렉션됩니다. 생성된 프로젝트가 사이드 메뉴에 표시됩니다:

  • 엑솔라 제품 및 솔루션 통합을 시작하려면 이름을 클릭하십시오.
  • 프로젝트 설정 섹션으로 이동하여 추가 언어 및 현지화된 프로젝트 이름을 추가합니다(선택 사항).

게임이나 제품이 여러 개 있는 경우 각각에 대해 별도의 프로젝트를 생성하십시오. 이렇게 하려면 회사 페이지의 사이드 메뉴에서 프로젝트 생성을 클릭하고 필요한 정보를 지정합니다.

연동을 처리하는 동안 프로젝트 이름 옆의 관리자 페이지에서 확인할 수 있는 프로젝트 ID를 제공해야 합니다.

카탈로그 설정하기

관리자 페이지에서 아이템 생성하기

주의

엑솔라 측에서 카탈로그를 생성해야 합니다. 아이템 수동으로 추가하기App Store, Google Play 또는 PlayFab에서 가져오기를 수행할 수 있습니다. Google Play에서 가져오기를 수행할 경우 한 번에 최대 100개의 아이템을 가져올 수 있습니다.

이 지침은 가상 아이템의 기본 설정 단계를 제공합니다. 나중에 카탈로그에 다른 아이템(인게임 재화, 번들, 게임 키)을 추가하고, 아이템 그룹을 생성하고, 프로모션 캠페인, 지역별 가격 등을 설정할 수 있습니다.

카탈로그에 기본 설정이 있는 가상 아이템을 추가하는 방법:

  1. 관리자 페이지에서 프로젝트를 열고 아이템 카탈로그 > 가상 아이템 섹션으로 이동합니다.
  2. 드롭다운 목록에서 그룹 생성을 선택합니다.

  1. 다음 필드에서 아이템의 기본 설정을 설정합니다.
    • 이미지(선택 사항)
    • SKU(아이템 고유 ID)
    • 아이템 이름
    • 설명(선택 사항)

  1. 아이템 가격 지정:
    1. 실제 통화 가격 토글을 으로 설정합니다.
    2. 기본 통화 필드에서 통화를 변경하고(선택 사항) 아이템 가격을 지정합니다.
    3. 기본 통화 필드에서 통화를 변경한 경우 실제 통화 가격 필드에서 동일한 통화를 선택합니다.

알림
카탈로그를 가져오는 API 호출이 올바르게 작동되도록 하려면 모든 아이템에 대해 기본 통화와 가격이 지정된 통화 목록이 일치하는지 확인합니다.

  1. 아이템 상태를 사용 가능으로 변경합니다.

  1. 아이템 생성을 클릭합니다.

애플리케이션의 클라이언트 측에 카탈로그 표시하기

  1. 프로젝트에 Store 라이브러리를 추가합니다. 이렇게 하려면 build.gradle를 열고 종속성 섹션에 다음 줄을 추가합니다.

Copy
Full screen
Small screen
    1implementation("com.xsolla.android:store:latest.release")
    

    1. 애플리케이션의 클라이언트 측에서 제품 카탈로그를 표시하는 UI를 추가합니다.
    2. 엑솔라 서버로부터 아이템 카탈로그를 요청하는 기능을 구현합니다.

    알림
    이 예제에서는 가상 아이템 목록을 가져오는 XStore.getVirtualItems 메서드를 사용합니다. 다른 Store 라이브러리 메서드를 사용하는 카탈로그 아이템에 대한 정보를 가져올 수도 있습니다.

    예시(샘플 웹 애플리케이션의 StoreActivity 클래스):

    Copy
    Full screen
    Small screen
     1package com.xsolla.androidsample
     2
     3import androidx.appcompat.app.AppCompatActivity
     4import android.os.Bundle
     5import android.widget.Toast
     6import androidx.recyclerview.widget.LinearLayoutManager
     7import androidx.recyclerview.widget.RecyclerView
     8import com.xsolla.android.store.XStore
     9import com.xsolla.android.store.callbacks.GetVirtualItemsCallback
    10import com.xsolla.android.store.entity.response.items.VirtualItemsResponse
    11import com.xsolla.androidsample.adapter.BuyItemAdapter
    12
    13class StoreActivity : AppCompatActivity() {
    14
    15    private lateinit var itemsView: RecyclerView
    16
    17    override fun onCreate(savedInstanceState: Bundle?) {
    18        super.onCreate(savedInstanceState)
    19        setContentView(R.layout.activity_store)
    20
    21        XStore.init(<projectId>)
    22
    23        initUI()
    24        loadVirtualItems()
    25    }
    26
    27    private fun initUI() {
    28        itemsView = findViewById(R.id.buy_recycler_view)
    29        itemsView.layoutManager = LinearLayoutManager(this)
    30    }
    31
    32    private fun loadVirtualItems() {
    33        val parentActivity = this
    34        XStore.getVirtualItems(object : GetVirtualItemsCallback {
    35            override fun onSuccess(response: VirtualItemsResponse) {
    36                itemsView.adapter = BuyItemAdapter(parentActivity, response.items.filter { item -> item.virtualPrices.isEmpty() && !item.isFree })
    37            }
    38
    39            override fun onError(throwable: Throwable?, errorMessage: String?) {
    40                showNotificationMessage(errorMessage ?: throwable?.javaClass?.name ?: "Error")
    41            }
    42        })
    43    }
    44
    45    private fun showNotificationMessage(message: String) {
    46        Toast.makeText(
    47            baseContext,
    48            message,
    49            Toast.LENGTH_SHORT,
    50        ).show()
    51    }
    52}
    

    스크립트의 XStore.init() 초기화 블록에서 프로젝트 이름 옆의 관리자 페이지에서 확인할 수 있는 프로젝트 ID를 지정합니다.

    아이템 구매 설정하기

    클라우드 함수를 사용하여 주문 생성하기

    엑솔라 측에서 사용자 및 아이템 데이터로 주문을 생성하려면 구매용 결제 토큰 생성 API 호출을 사용하는 클라우드 함수를 프로젝트에 추가합니다. 이 호출은 결제 UI를 열고 구매를 하는 데 필요한 결제 토큰을 반환합니다.

    제한 사항:

    • 결제 토큰을 요청할 때 사용자 국가 또는 사용자의 IP 주소를 전달해야 합니다.
    • 토큰에서 통화를 전달하지 않으면 국가에 따라 결정됩니다.
    • 토큰에서 통화를 전달하면 사용자가 이 통화로 결제합니다.

    알림
    먼저 Firebase 프로젝트를 생성하고 초기화해야 하며, Firebase를 사용하여 사용자 인증을 활성화해야 합니다. 이러한 단계에 대한 자세한 내용은 다음 Firebase 지침을 참조하십시오.

    프로젝트에 클라우드 함수를 추가하는 방법:

    1. Firebase CLI(명령줄 인터페이스)를 설치합니다. 이렇게 하려면 다음 CLI 명령을 실행합니다.

    Copy
    Full screen
    Small screen
      1npm install -g firebase-tools
      

      1. 프로젝트를 Firebase 프로젝트에 연결하려면 다음 CLI 명령을 실행하여 Firebase 프로젝트를 초기화해야 합니다.

      Copy
      Full screen
      Small screen
        1firebase init functions
        

        1. 설치 프로그램의 지침에 따라 설정을 구성합니다.
          1. 기존 코드베이스를 선택합니다.
          2. 클라우드 함수 생성에 사용할 언어로 JavaScript를 지정합니다.
          3. 종속성을 설치합니다.

        1. functions/index.js를 열고 이를 수정합니다.

        Copy
        Full screen
        Small screen
         1// The Cloud Functions for Firebase SDK to create Cloud Functions and triggers.
         2const functions = require('firebase-functions/v1');
         3
         4const projectId = <projectId>;
         5const apiKey = <apiKey>;
         6
         7exports.getXsollaPaymentToken = functions.https.onRequest((req, res) => {
         8
         9  const requestBody = req.body;
        10    if (!requestBody) {
        11      res.status(400).send('Request body is missing');
        12      return;
        13    }
        14
        15  const userId = requestBody.data.uid;
        16  const email = requestBody.data.email;
        17  const sku = requestBody.data.sku;
        18  const returnUrl = requestBody.data.returnUrl;
        19
        20  const payload = {
        21    user: {
        22      id: {value: userId},
        23      name: {
        24        value: email
        25      },
        26      email: {
        27        value: email
        28      },
        29      country: {
        30        value: 'US',
        31        allow_modify: false
        32      }
        33    },
        34    purchase: {
        35      items: [
        36        {
        37          sku: sku,
        38          quantity: 1
        39        }
        40      ]
        41    },
        42    sandbox: true,
        43    settings: {
        44      language: 'en',
        45      currency: 'USD',
        46      return_url: returnUrl,
        47      ui: {
        48        theme: '63295aab2e47fab76f7708e3'
        49      }
        50    }
        51  }
        52
        53  let url = `https://store.xsolla.com/api/v3/project/${projectId.toString()}/admin/payment/token`;
        54
        55  fetch(
        56    url,
        57    {
        58      method: "POST",
        59      headers: {
        60        'Content-Type': 'application/json',
        61        Authorization: `Basic ${btoa(`${projectId}:${apiKey}`)}`
        62      },
        63      body: JSON.stringify(payload)
        64    },
        65  )
        66  .then(xsollaRes => {
        67    // Handle the response data
        68    if (xsollaRes.ok) {
        69      return xsollaRes.json();
        70    } else {
        71      throw new Error(`HTTP request failed with status ${xsollaRes.status} and statusText: ${xsollaRes.statusText}`)
        72    }
        73  })
        74  .then(data => {
        75    res.send(JSON.stringify(data));
        76  })
        77  .catch(error => {
        78    res.send(`Error = ${error}`);
        79  });
        80});
        81
        82exports.webhookFakeResponse = functions.https.onRequest((request, response) => {
        83  response.status(200).send()
        84})
        

        1. 스크립트에서 변수 값을 지정합니다.
          • projectId - 프로젝트 이름 옆의 관리자 페이지에서 찾을 수 있는 프로젝트 ID입니다.

        1. 에뮬레이터로 클라우드 함수를 테스트하려면 다음 CLI 명령을 실행합니다.

        Copy
        Full screen
        Small screen
          1firebase emulators:start
          

          1. 클라우드 함수를 실행한 후 애플리케이션의 클라이언트 측에서 다음 메서드를 호출할 수 있습니다.
            • getXsollaPaymentToken - 결제 인터페이스 열기용 결제 토큰을 반환합니다.
            • webhookFakeResponse - 결제 웹훅의 응답으로 HTTP 코드 200를 전송합니다. 이 메서드에는 구매 유효성 검사 로직이 포함되어 있지 않으므로 테스트용으로만 사용해야 합니다. 전체 웹훅 목록과 웹훅 작업에 대한 일반 정보는 웹훅 문서를 참조하십시오.

          1. 로컬에서 메서드를 호출하려면 https://localhost:5001/{firebase-project-id}/us-central1/getXsollaPaymentTokenhttps://localhost:5001/{firebase-project-id}/us-central1/webhookFakeResponse URL을 사용해야 하며, 여기에서 {firebase-project-id}는 Firebase 프로젝트 ID입니다(Firebase 콘솔 > 프로젝트 설정 > 프로젝트 ID).

          1. 프로덕션에서 클라우드 함수를 배포하려면 다음 CLI 명령을 실행합니다.

          Copy
          Full screen
          Small screen
            1firebase deploy --only functions
            

            1. 프로덕션이 배포되면 https://us-central1-{firebase-project-id}.cloudfunctions.net/getXsollaPaymentTokenhttps://us-central1-{firebase-project-id}.cloudfunctions.net/webhookFakeResponse URL을 통해 메서드를 호출할 수 있으며, 여기에서 {firebase-project-id}는 Firebase 프로젝트 ID입니다(Firebase 콘솔 > 프로젝트 설정 > 프로젝트 ID). 프로덕션에서 이 기능을 실행하는 방법에 대한 자세한 내용은 Firebase 문서를 참조하십시오.

            결제 UI 시작 설정하기

            1. 프로젝트에 Payments 라이브러리를 추가합니다. 이렇게 하려면 build.gradle를 열고 종속성 섹션에 다음 줄을 추가합니다.

            Copy
            Full screen
            Small screen
              1implementation("com.xsolla.android:payments:latest.release")
              

              1. AndroidManifest.xml을 열고 인터넷 액세스 권한을 추가합니다.

              Copy
              Full screen
              Small screen
              1<uses-permission android:name="android.permission.INTERNET" />
              

              1. 주문을 생성하고(클라우드 함수의 XStore.getXsollaPaymentToken 메서드 호출) 수신한 결제 토큰으로 결제 UI를 여는(XPayments.createIntentBuilder() 클래스) 로직을 추가합니다.

              1. getXsollaPaymentToken 메서드를 호출하려면 다음 URL 중 하나를 제공합니다. 여기에서 {firebase-project-id}는 Firebase 프로젝트 ID입니다(Firebase 콘솔 > 프로젝트 설정 > 프로젝트 ID).
                • 로컬 액세스의 경우 - https://localhost:5001/{firebase-project-id}/us-central1/getXsollaPaymentToken
                • 프로덕션 액세스용 - https://us-central1-{firebase-project-id}.cloudfunctions.net/getXsollaPaymentToken

              예시(샘플 웹 애플리케이션의 BuyItemAdapter 클래스):

              Copy
              Full screen
              Small screen
                1package com.xsolla.androidsample.adapter
                2
                3import android.R.attr.duration
                4import android.os.Handler
                5import android.os.Looper
                6import android.view.LayoutInflater
                7import android.view.ViewGroup
                8import android.widget.Toast
                9import androidx.recyclerview.widget.RecyclerView
               10import com.bumptech.glide.Glide
               11import com.xsolla.android.payments.XPayments
               12import com.xsolla.android.payments.data.AccessToken
               13import com.xsolla.android.store.entity.response.items.VirtualItemsResponse
               14import com.xsolla.androidsample.R
               15import com.xsolla.androidsample.StoreActivity
               16import org.json.JSONObject
               17import java.io.BufferedReader
               18import java.io.BufferedWriter
               19import java.io.OutputStream
               20import java.io.OutputStreamWriter
               21import java.net.HttpURLConnection
               22import java.net.URL
               23
               24
               25class BuyItemAdapter(private val parentActivity: StoreActivity, private val items: List<VirtualItemsResponse.Item>) :
               26    RecyclerView.Adapter<BuyItemViewHolder>() {
               27    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BuyItemViewHolder {
               28        return BuyItemViewHolder( LayoutInflater.from(parent.context)
               29            .inflate(R.layout.buy_item_sample, parent, false))
               30    }
               31
               32    override fun onBindViewHolder(holder: BuyItemViewHolder, position: Int) {
               33        val item = items[position]
               34        Glide.with(holder.view).load(item.imageUrl).into(holder.itemImage)
               35        holder.itemName.text = item.name
               36        holder.itemDescription.text = item.description
               37        var priceText: String
               38        if(item.virtualPrices.isNotEmpty()) {
               39            priceText = "${item.virtualPrices[0].getAmountRaw()} ${item.virtualPrices[0].name}"
               40        } else {
               41            priceText = "${item.price?.getAmountRaw()} ${item.price?.currency.toString()}"
               42        }
               43
               44        holder.itemPrice.text = priceText
               45
               46        holder.itemButton.setOnClickListener {
               47            Thread {
               48                purchase(item.sku!!)
               49            }.start()
               50        }
               51    }
               52
               53    private fun purchase(sku: String) {
               54
               55        val uid = parentActivity.intent.getStringExtra("uid")
               56        val email = parentActivity.intent.getStringExtra("email")
               57
               58        val jsonBody = JSONObject()
               59        jsonBody.put("data", JSONObject().apply {
               60            put("uid", uid)
               61            put("email", email)
               62            put("sku", sku)
               63            put("returnUrl", "app://xpayment.${parentActivity.packageName}")
               64        })
               65
               66        val connection = URL(https://localhost:5001/{firebase-project-id}/us-central1/getXsollaPaymentToken).openConnection() as HttpURLConnection
               67        connection.requestMethod = "POST"
               68        connection.setRequestProperty("Content-Type", "application/json")
               69        connection.doOutput = true
               70
               71        val outputStream: OutputStream = connection.outputStream
               72        val writer = BufferedWriter(OutputStreamWriter(outputStream))
               73        writer.write(jsonBody.toString())
               74        writer.flush()
               75        writer.close()
               76
               77        val responseCode = connection.responseCode
               78
               79        if (responseCode == HttpURLConnection.HTTP_OK) {
               80            val response = connection.inputStream.bufferedReader().use(BufferedReader::readText)
               81            connection.disconnect()
               82
               83            val jsonObject = JSONObject(response)
               84            val token = jsonObject.getString("token")
               85            val orderId = jsonObject.getString("order_id")
               86
               87            val intent = XPayments.createIntentBuilder(parentActivity)
               88                .accessToken(AccessToken(token))
               89                .isSandbox(true)
               90                .build()
               91            parentActivity.startActivityForResult(intent, 1)
               92        } else {
               93            Handler(Looper.getMainLooper()).post {
               94                showNotificationMessage("HTTP request failed with error: $responseCode")
               95            }
               96        }
               97    }
               98
               99    override fun getItemCount() = items.size
              100
              101    private fun showNotificationMessage(message: String) {
              102        Toast.makeText(
              103            parentActivity,
              104            message,
              105            Toast.LENGTH_SHORT,
              106        ).show()
              107    }
              108}
              

              1. 결제 결과를 처리하는 onActivityResult() 메서드를 추가합니다.

              예시(샘플 웹 애플리케이션의 StoreActivity 클래스):

              Copy
              Full screen
              Small screen
               1override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
               2        super.onActivityResult(requestCode, resultCode, data)
               3        if (requestCode == 1) {
               4            val (status, _) = XPayments.Result.fromResultIntent(data)
               5            when (status) {
               6                XPayments.Status.COMPLETED -> showNotificationMessage("Payment completed")
               7                XPayments.Status.CANCELLED -> showNotificationMessage("Payment canceled")
               8                XPayments.Status.UNKNOWN -> showNotificationMessage("Payment error")
               9            }
              10        }
              11    }
              

              주문 상태 추적 설정하기

              주문 상태 추적은 결제가 성공적으로 이루어졌는지 확인하고 사용자에게 아이템을 제공할 때 필요합니다.

              클라이언트 측에서 주문 상태 가져오기

              애플리케이션의 클라이언트 측에서 주문 상태 변경 사항을 구독하려면 XStore.getOrderStatus 메서드를 호출하고 다음 매개 변수를 메서드에 전달합니다.

              • listener - OrderStatusListener 유형의 리스너 개체입니다.
              • orderId - 쇼핑 장바구니, 원클릭 구매 또는 인게임 재화 구매를 통해 구매 시 받은 주문 ID입니다.

              메서드 작동 방식에 대한 자세한 내용은 주문 상태 추적 섹션을 참조하십시오.

              서버 측에서 주문 상태 가져오기

              주의

              SDK를 사용하면 애플리케이션의 클라이언트 측에서 주문 상태를 추적할 수 있습니다. 다만 애플리케이션의 백엔드에서 주문 정보를 수신하도록 결제 웹훅 핸들러를 설정하는 것이 좋습니다. 이렇게 하면 완료된 구매에 대한 추가 유효성 검사를 구현할 수 있습니다.

              웹훅의 전체 목록과 웹훅 작업에 대한 일반적인 정보는 웹훅 문서를 참조하십시오.

              엑솔라 측에서 웹훅을 구성하는 방법:

              1. 관리자 페이지에서 프로젝트를 열고 프로젝트 설정 > 웹훅 섹션으로 이동합니다.
              2. 웹훅 서버 필드에서 엑솔라가 웹훅을 전송할 URL을 입력합니다.

              알림

              테스트를 위해 https://us-central1-{firebase-project-id}.cloudfunctions.net/webhookFakeResponse를 지정할 수 있으며, 여기에서 {firebase-project-id}는 Firebase 프로젝트 ID입니다(Firebase 콘솔 > 프로젝트 설정 > 프로젝트 ID). 이 경우에는 Firebase가 웹훅의 성공적인 처리를 시뮬레이션합니다. 실제 프로젝트에서는 구매 유효성 검사 로직을 추가해야 합니다.

              웹훅을 테스트하기 위해 webhook.site와 같은 전용 사이트나 ngrok과 같은 플랫폼을 선택할 수도 있습니다.

              1. 비밀 키 필드의 값을 복사하여 저장합니다. 이 키는 기본값으로 생성되며 웹훅 서명에 사용됩니다. 변경하려면 업데이트 아이콘을 클릭합니다.
              2. 웹훅 사용을 클릭합니다.

              이 기사가 도움이 되었나요?
              감사합니다!
              개선해야 할 점이 있을까요? 메시지
              유감입니다
              이 기사가 도움이 안 된 이유를 설명해 주세요. 메시지
              의견을 보내 주셔서 감사드립니다!
              메시지를 검토한 후 사용자 경험 향상에 사용하겠습니다.
              마지막 업데이트: 2025년 12월 5일

              오자 또는 기타 텍스트 오류를 찾으셨나요? 텍스트를 선택하고 컨트롤+엔터를 누르세요.

              문제 보고
              콘텐츠를 항상 검토합니다. 여러분의 피드백은 콘텐츠를 개선에 도움이 됩니다.
              후속 조치를 위해 이메일을 제공해 주세요
              의견을 보내 주셔서 감사드립니다!
              피드백을 보내는 중 문제가 발생했습니다
              잠시 후 다시 시도하거나 doc_feedback@xsolla.com으로 연락해 주세요.