Unity SDK
CROSS Pay 결제 연동을 위한 공식 Unity SDK입니다. 인앱 WebView로 결제 세션을 열고, 딥링크 콜백을 수신한 뒤 결과를 반환합니다 — API 키는 클라이언트에 존재하지 않습니다.
호환성
| 환경 | 지원 버전 |
|---|---|
| Unity | 2022.3 LTS 이상 |
| 플랫폼 | Android · iOS · Windows |
| 언어 | C# |
| 패키지 | com.crossx.sdk.unity (UPM) |
설치
방법 A — Scoped Registry
Packages/manifest.json에 레지스트리와 패키지를 추가합니다:
{
"scopedRegistries": [
{
"name": "NexusCROSSxUPM",
"url": "https://registry.npmjs.org",
"scopes": ["com.crossx"]
}
],
"dependencies": {
"com.crossx.sdk.unity": "<version>"
}
}
방법 B — Git URL
Window → Package Manager → Add package from git URL:
https://github.com/to-nexus/crossy-sdk-unity.git?path=src/CROSSx.Sdk.Unity#<version>
Webkit SDK(com.crossx.webkit.sdk.unity)는 번들 의존성으로 자동 설치됩니다.
Project ID 설정
SDK는 Assets/Resources/CROSSx SDK Settings.asset에서 Project ID를 읽습니다. Unity 메뉴에서 생성합니다:
CROSSx SDK → Open Settings
CROSSx 콘솔에서 발급받은 Project ID를 입력합니다. 빌드 후처리기가 이 값을 사용해 딥링크 URI 스킴을 주입하며, 값이 비어 있으면 빌드가 실패합니다.
사전 준비
모든 예시의 checkoutUrl / checkoutId는 게임 서버가 POST /v1/payments를 호출해 발급받은 뒤 클라이언트로 전달한 값입니다.
MERCHANT_API_KEY는 서버 환경변수 또는 secret manager에만 저장합니다.- 게임 클라이언트는 서버로부터
checkout_url과checkout_id만 수신합니다. - 딥링크 콜백 결과는 주문 확정 근거가 아닙니다. 최종 처리는 webhook 또는 서버 조회(
GET /v1/payments/{id})로 확정하세요.
결제 흐름
게임 서버가 결제 세션을 생성 → SDK가 WebView를 열고 딥링크 콜백을 대기 → 서버가 webhook으로 주문을 최종 확정합니다. 딥링크 콜백 결과는 확정의 근거가 아닙니다.
Quick Start
using CROSSx.Sdk.Unity.SDK;
using CROSSx.Sdk.Unity.Core.Types;
using CROSSx.Sdk.Unity.Core.Utils;
// 1. SDK 초기화 (GameManager.Start 등에서 1회 호출)
var config = new SDKConfig
{
ProjectId = "YOUR_PROJECT_ID",
AppName = "My Game",
};
var sdk = CROSSxSDKFactory.Create(config);
await sdk.InitializeAsync();
// 2. 게임 서버에서 checkout을 생성하고 이 값들을 받아옵니다
string checkoutUrl = await MyGameServer.CreateCheckoutAsync(order);
string checkoutId = ExtractCheckoutId(checkoutUrl); // 또는 서버에서 별도로 수신
// 3. SDK가 WebView를 열고 딥링크 콜백을 대기
CrossPayCallbackResult result = await sdk.OpenCrossPayAndWaitResultAsync(
checkoutUrl,
checkoutId
);
// 4. 콜백 엔벨로프 상태 확인 (참고용)
if (result.IsSuccess)
Debug.Log("콜백 수신: success — 서버 webhook 확정 대기 중.");
else if (result.IsCancel)
Debug.Log("사용자가 결제창을 닫았습니다.");
else
Debug.LogWarning($"콜백: {result.Status} — {result.Error}");
// 5. 재화 지급 전 서버에서 최종 결제 상태를 반드시 확인합니다
bool paid = await MyGameServer.VerifyPaymentAsync(checkoutId);
딥링크 설정
결제 WebView는 모바일에서 webkit-{ProjectId} URI 스킴으로, 데스크톱에서는 로컬 루프백 URL로 리다이렉트됩니다.
Android
Assets/Plugins/Android/AndroidManifest.xml에 intent filter를 추가합니다:
<activity
android:name="com.unity3d.player.UnityPlayerActivity"
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="webkit-YOUR_PROJECT_ID" />
</intent-filter>
</activity>
iOS
SDK의 iOSBuildPostProcessor가 빌드 후 URI 스킴을 자동 등록합니다. 수동 등록 시 Project Settings → Supported URL schemes에 webkit-YOUR_PROJECT_ID를 추가합니다.
Windows
빌드 후 자동 생성되는 register_deeplink.reg를 1회 실행하면 루프백 콜백 핸들러가 Windows 레지스트리에 등록됩니다.
SDK 초기화
var config = new SDKConfig
{
ProjectId = "YOUR_PROJECT_ID",
AppName = "My Game",
Environment = SDKEnvironment.Development, // URL 자동 설정
Theme = ThemeMode.Dark,
Locale = "ko",
Debug = true,
};
var sdk = CROSSxSDKFactory.Create(config);
// 저장된 세션이 있으면 복원
var session = await sdk.InitializeAsync();
if (session != null)
Debug.Log($"세션 복원: {session.WalletAddress}");
SDKEnvironment.Development는 API URL을 자동으로 결정합니다. OAuthServerUrl, AuthApiUrl, EmbeddedWalletGatewayUrl을 직접 지정하려면 SDKEnvironment.Custom을 사용합니다.
결제 실행
CrossPayCallbackResult result = await sdk.OpenCrossPayAndWaitResultAsync(
checkoutUrl: checkoutUrl,
checkoutId: checkoutId,
timeoutMs: 300_000 // 기본값: 5분
);
SDK 동작 순서:
checkoutUrl에 CSRFstate토큰과 콜백 엔드포인트를 추가합니다.- 플랫폼 기본 WebView로 결제 페이지를 엽니다.
- 결제 콜백 수신을 대기합니다 — 플랫폼별로 방식이 다릅니다:
- Android · iOS · macOS: 딥링크 스킴
webkit-{ProjectId}://crosspay-callback - Windows · Editor: 루프백 HTTP
http://localhost:{port}/crosspay-callback
- Android · iOS · macOS: 딥링크 스킴
state를 검증하고 디코딩된 콜백 엔벨로프를 반환합니다.timeoutMs내에 콜백이 도착하지 않으면TimeoutException을 throw합니다.
결과 처리
CrossPayCallbackResult는 딥링크 콜백 엔벨로프를 담고 있습니다.
| 필드 | 타입 | 설명 |
|---|---|---|
Status | string | 엔벨로프 상태: "success" | "cancel" | "failed" (또는 PG 원시값) |
IsSuccess | bool | Status == "success"이면 true |
IsCancel | bool | Status == "cancel"이면 true |
IsFailed | bool | Status == "failed"이면 true |
Data | JObject? | PG 결제 데이터 — 제공자마다 필드 다름 |
Error | string? | IsFailed일 때 오류 메시지 |
RawParams | IReadOnlyDictionary<string, string>? | 콜백 URL의 모든 쿼리 파라미터 (예: checkout_id, payment_id, tx_hash) |
딥링크 콜백 결과는 참고용입니다. 재화 지급 전 서버에서 GET /v1/payments/{id} 조회 또는 webhook으로 결제 상태를 반드시 확인하세요.
오류 처리
try
{
var result = await sdk.OpenCrossPayAndWaitResultAsync(checkoutUrl, checkoutId);
}
catch (TimeoutException)
{
// 딥링크 콜백이 timeoutMs 내에 도착하지 않았습니다.
// 사용자가 결제를 완료했지만 콜백이 앱에 도달하지 못했을 수 있습니다.
// 서버에서 결제 상태를 확인하세요.
bool paid = await MyGameServer.VerifyPaymentAsync(checkoutId);
}
catch (InvalidOperationException ex)
{
// SDK 미초기화 또는 WebView 실행 실패.
Debug.LogError(ex.Message);
}
멱등성
| 시나리오 | 동작 |
|---|---|
| 같은 세션 내 재시도 (네트워크 장애, WebView 재실행 등) | 동일 checkoutId 재사용 — 같은 checkoutUrl과 checkoutId로 OpenCrossPayAndWaitResultAsync 재호출 |
결제가 터미널 상태(PAID / FAILED / EXPIRED / CANCELLED)에 도달 | 폐기 — 서버에서 POST /v1/payments를 호출해 새 checkout을 발급합니다 |
| 다른 주문 / 금액 / 플레이어의 새 결제 | 항상 새 checkout — 이전 checkoutId 재사용 시 금액 불일치로 거부될 수 있습니다 |
CROSS Pay는 현재 환불 기능을 제공하지 않습니다. 동일 주문이 두 번 결제되면 되돌릴 수 없습니다. 재시도 시 동일 Idempotency-Key로 POST /v1/payments를 호출하세요.