페이 스테이션을 Firebase 인증과 함께 사용하는 방법
애플리케이션에서 Firebase를 사용하여 사용자 인증을 이미 구현한 경우 Firebase에서 결제 토큰을 생성한 다음 이를 애플리케이션의 클라이언트 측에 전달하여 결제 UI를 열 수 있습니다.
이 연동 옵션을 사용하는 경우 구매 시 비용을 지불하는 사용자의 국가 및 통화를 결정하는 로직을 독립적으로 구현해야 합니다.
연동 절차:
- 관리자 페이지에 등록한 후 새 프로젝트를 생성합니다. 이후 단계에서 생성하는 프로젝트의 ID가 필요합니다.
- 카탈로그 설정 방법:
- 엑솔라 측에서 아이템 카탈로그를 생성합니다. 아이템을 수동으로 추가하거나 Google Play 또는 PlayFab에서 가져올 수 있습니다.
Store 라이브러리를 사용하여 애플리케이션의 클라이언트 측에서 카탈로그 가져오기 및 표시를 구현합니다.
- 아이템 구매 설정 방법:
- Firebase 클라우드 함수를 사용하여 애플리케이션의 클라이언트 측에서 사용자 및 아이템 데이터로 주문을 생성합니다.
Payments 라이브러리를 사용하여 애플리케이션의 클라이언트 측에서 결제 UI 열기를 구현합니다.
연동을 완료하고 실제 결제 수락을 시작하려면 엑솔라와 라이선스 계약을 체결해야 합니다.
모든 연동 단계에서 계약에 서명할 수 있지만, 검토 절차에 최대 3일(영업일 기준)이 소요될 수 있다는 점에 유의해 주세요.
예를 들어 Firebase 인증과 Pay Station의 결합하는 사례를 샘플 웹 애플리케이션을 사용하여 구현해 보세요. 샘플 웹 애플리케이션의 소스 코드는 GitHub에서 확인할 수 있습니다.
프로젝트 생성
관리자 페이지는 엑솔라 기능을 구성하고 분석과 트랜잭션 작업에 사용하는 기본 도구입니다.
등록할 때 지정한 회사 및 애플리케이션에 대한 데이터는 엑솔라와의 라이선스 계약 초안을 생성하고 적합한 솔루션 추천을 생성하는 데 사용됩니다. 나중에 해당 데이터를 변경할 수 있지만 등록할 때 올바른 데이터를 제공하면 라이선스 계약 체결 프로세스가 빨라집니다.
프로젝트를 생성하는 방법:
- 관리자 페이지에 등록하기.
암호 생성 규칙
관리자 페이지의 암호는 라틴 문자, 숫자 및 특수 문자로 구성될 수 있으며 다음 최소 요건을 충족해야 합니다:
- 8자 이상
- 숫자 하나 이상
- 대문자 하나 이상
- 소문자 하나 이상
암호 보안을 위해 다음을 권장합니다.
- 90일에 한 번 이상 암호 변경
- 기존 계정 암호에 사용한 마지막 4개의 문자와 일치하지 않는 새 암호 사용
- 다른 곳에서 사용하는 암호와 일치하지 않는 고유한 암호 사용
- 쉽게 액세스할 수 없는 곳에 암호 저장
- 암호 관리 프로그램을 사용하여 암호 저장
관리자 페이지는 2단계 인증을 사용하며 인증을 시도할 때마다 확인 코드를 전송합니다.
- 계정을 생성하려면 다음 정보를 입력하세요:
- 이름 및 성.
- 회사 내 역할.
- 회사 이름. 개인으로 등록하는 경우 성명을 입력합니다.
- 사용자와 사용자의 게임 또는 제품에 대한 자세한 정보로 연결되는 링크(선택 사항).
- 사용자의 국가 또는 지역.
- 다음 단계를 클릭합니다.

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

프로젝트가 생성되면 회사 페이지로 리디렉션됩니다. 생성된 프로젝트가 사이드 메뉴에 표시됩니다:
- 엑솔라 제품 및 솔루션 통합을 시작하려면 이름을 클릭하십시오.
- 프로젝트 설정 섹션으로 이동하여 추가 언어 및 현지화된 프로젝트 이름을 추가합니다(선택 사항).

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

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

카탈로그 설정하기
관리자 페이지에서 아이템 생성하기
엑솔라 측에서 카탈로그를 생성해야 합니다. 아이템 수동으로 추가하기나 App Store, Google Play 또는 PlayFab에서 가져오기를 수행할 수 있습니다. Google Play에서 가져오기를 수행할 경우 한 번에 최대 100개의 아이템을 가져올 수 있습니다.
이 지침은 가상 아이템의 기본 설정 단계를 제공합니다. 나중에 카탈로그에 다른 아이템(인게임 재화, 번들, 게임 키)을 추가하고, 아이템 그룹을 생성하고, 프로모션 캠페인, 지역별 가격 등을 설정할 수 있습니다.
카탈로그에 기본 설정이 있는 가상 아이템을 추가하는 방법:
- 관리자 페이지에서 프로젝트를 열고 아이템 카탈로그 > 가상 아이템 섹션으로 이동합니다.
- 드롭다운 목록에서 그룹 생성을 선택합니다.
- 다음 필드에서 아이템의 기본 설정을 설정합니다.
- 이미지(선택 사항)
- SKU(아이템 고유 ID)
- 아이템 이름
- 설명(선택 사항)
- 아이템 가격 지정:
- 실제 통화 가격 토글을 온으로 설정합니다.
- 기본 통화 필드에서 통화를 변경하고(선택 사항) 아이템 가격을 지정합니다.
- 기본 통화 필드에서 통화를 변경한 경우 실제 통화 가격 필드에서 동일한 통화를 선택합니다.
- 아이템 상태를 사용 가능으로 변경합니다.
- 아이템 생성을 클릭합니다.
애플리케이션의 클라이언트 측에 카탈로그 표시하기
- 프로젝트에
Store 라이브러리를 추가합니다. 이렇게 하려면build.gradle를 열고 종속성 섹션에 다음 줄을 추가합니다.
1implementation("com.xsolla.android:store:latest.release")
- 애플리케이션의 클라이언트 측에서 제품 카탈로그를 표시하는 UI를 추가합니다.
- 엑솔라 서버로부터 아이템 카탈로그를 요청하는 기능을 구현합니다.
XStore.getVirtualItems 메서드를 사용합니다. 다른 예시(샘플 웹 애플리케이션의
- kotlin
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 CLI(명령줄 인터페이스)를 설치합니다. 이렇게 하려면 다음 CLI 명령을 실행합니다.
1npm install -g firebase-tools
- 프로젝트를 Firebase 프로젝트에 연결하려면 다음 CLI 명령을 실행하여 Firebase 프로젝트를 초기화해야 합니다.
1firebase init functions
- 설치 프로그램의 지침에 따라 설정을 구성합니다.
- 기존 코드베이스를 선택합니다.
- 클라우드 함수 생성에 사용할 언어로 JavaScript를 지정합니다.
- 종속성을 설치합니다.
functions/index.js를 열고 이를 수정합니다.
- javascript
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})
- 스크립트에서 변수 값을 지정합니다.
projectId- 프로젝트 이름 옆의 관리자 페이지에서 찾을 수 있는 프로젝트 ID입니다.

apiKey- API 키. 생성할 때 한 번만 관리자 페이지에 표시되며 따로 저장하고 관리해야 합니다. 다음 섹션에서 새 키를 생성할 수 있습니다.
- 에뮬레이터로 클라우드 함수를 테스트하려면 다음 CLI 명령을 실행합니다.
1firebase emulators:start
- 클라우드 함수를 실행한 후 애플리케이션의 클라이언트 측에서 다음 메서드를 호출할 수 있습니다.
- 로컬에서 메서드를 호출하려면
https://localhost:5001/{firebase-project-id}/us-central1/getXsollaPaymentToken및https://localhost:5001/{firebase-project-id}/us-central1/webhookFakeResponseURL을 사용해야 하며, 여기에서{firebase-project-id}는 Firebase 프로젝트 ID입니다(Firebase 콘솔 > 프로젝트 설정 > 프로젝트 ID).
- 프로덕션에서 클라우드 함수를 배포하려면 다음 CLI 명령을 실행합니다.
1firebase deploy --only functions
- 프로덕션이 배포되면
https://us-central1-{firebase-project-id}.cloudfunctions.net/getXsollaPaymentToken및https://us-central1-{firebase-project-id}.cloudfunctions.net/webhookFakeResponseURL을 통해 메서드를 호출할 수 있으며, 여기에서{firebase-project-id}는 Firebase 프로젝트 ID입니다(Firebase 콘솔 > 프로젝트 설정 > 프로젝트 ID). 프로덕션에서 이 기능을 실행하는 방법에 대한 자세한 내용은 Firebase 문서를 참조하십시오.
결제 UI 시작 설정하기
- 프로젝트에
Payments 라이브러리를 추가합니다. 이렇게 하려면build.gradle를 열고 종속성 섹션에 다음 줄을 추가합니다.
1implementation("com.xsolla.android:payments:latest.release")
AndroidManifest.xml을 열고 인터넷 액세스 권한을 추가합니다.
- xml
1<uses-permission android:name="android.permission.INTERNET" />
- 주문을 생성하고(클라우드 함수의
XStore.getXsollaPaymentToken메서드 호출) 수신한 결제 토큰으로 결제 UI를 여는(XPayments.createIntentBuilder()클래스) 로직을 추가합니다.
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
- 로컬 액세스의 경우 -
예시(샘플 웹 애플리케이션의
- kotlin
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}
- 결제 결과를 처리하는
onActivityResult()메서드를 추가합니다.
예시(샘플 웹 애플리케이션의
- kotlin
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입니다.
메서드 작동 방식에 대한 자세한 내용은 주문 상태 추적 섹션을 참조하십시오.
서버 측에서 주문 상태 가져오기
엑솔라 측에서 웹훅을 구성하는 방법:
- 관리자 페이지에서 프로젝트를 열고 프로젝트 설정 > 웹훅 섹션으로 이동합니다.
- 웹훅 서버 필드에서 엑솔라가 웹훅을 전송할 URL을 입력합니다.
테스트를 위해 https://us-central1-{firebase-project-id}.cloudfunctions.net/webhookFakeResponse를 지정할 수 있으며, 여기에서 {firebase-project-id}는 Firebase 프로젝트 ID입니다(Firebase 콘솔 > 프로젝트 설정 > 프로젝트 ID). 이 경우에는 Firebase가 웹훅의 성공적인 처리를 시뮬레이션합니다. 실제 프로젝트에서는 구매 유효성 검사 로직을 추가해야 합니다.
웹훅을 테스트하기 위해 webhook.site와 같은 전용 사이트나 ngrok과 같은 플랫폼을 선택할 수도 있습니다.
- 비밀 키 필드의 값을 복사하여 저장합니다. 이 키는 기본값으로 생성되며 웹훅 서명에 사용됩니다. 변경하려면 업데이트 아이콘을 클릭합니다.
- 웹훅 사용을 클릭합니다.

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