본문으로 건너뛰기

Unreal Engine SDK

CROSS Pay 결제 연동을 위한 공식 Unreal Engine 플러그인입니다. 인앱 WebView로 결제 세션을 열고, 딥링크 콜백을 수신한 뒤 Blueprint 호환 delegate로 결과를 반환합니다 — API 키는 클라이언트에 존재하지 않습니다.

호환성

환경지원 버전
Unreal Engine4.25+ · 5.x
플랫폼Android · iOS
언어C++ (Blueprint 호출 가능)
플러그인CROSSxSdkUnrealPlugin

설치

플러그인은 GitHub Releases를 통해 배포되며 샘플 프로젝트crossx-plugins.json 매니페스트 + Makefile 시스템으로 설치합니다. Plugins/ 폴더는 .gitignore 대상이므로 팀원 모두 클론/풀 후 make sdk-install을 실행합니다.

1. 프로젝트 루트에 crossx-plugins.json 추가

{
"$schema": "https://crossnexus.com/schemas/crossx-plugins.schema.json",
"comment": "Edit the versions below, then run `make sdk-install`. Registry repo is public, so no GITHUB_TOKEN is required.",
"registry": {
"type": "github-releases",
"owner": "to-nexus",
"repo": "crossy-sdk-unreal-sample"
},
"plugins": {
"CROSSxSdkUnrealPlugin": "0.0.0-beta.25"
}
}

버전 번호를 최신 릴리스로 업데이트합니다.

2. 설치 스크립트와 Makefile 복사

샘플 프로젝트에서 아래 파일을 프로젝트 루트에 복사합니다:

  • scripts/install-plugins.sh (macOS / Linux)
  • scripts/install-plugins.ps1 (Windows PowerShell)
  • Makefile

3. 설치 실행

make sdk-install

GitHub Releases에서 CROSSxSdkUnrealPlugin-{version}.zip을 다운로드하고 SHA-256 체크섬을 검증한 뒤 Plugins/에 압축 해제합니다. 의존 플러그인 CROSSxWebkitSdkUnrealPlugin도 자동으로 함께 설치됩니다.

버전을 업데이트하려면 crossx-plugins.json을 편집하고 make sdk-install을 다시 실행합니다:

make sdk-update name=CROSSxSdkUnrealPlugin version=0.0.0-beta.26

4. .uproject에 플러그인 활성화

{
"Plugins": [
{ "Name": "CROSSxSdkUnrealPlugin", "Enabled": true },
{ "Name": "CROSSxWebkitSdkUnrealPlugin", "Enabled": true }
]
}

5. Build.cs에 모듈 의존성 추가

PublicDependencyModuleNames.AddRange(new string[] {
"CROSSxSdkUnrealPlugin",
"CROSSxWebkitSdkUnrealPlugin"
});

6. Project ID 설정

Config/DefaultGame.ini에 추가합니다:

[/Script/CROSSxSdkUnrealPlugin.CROSSxSdkSettings]
ProdProjectId=YOUR_PROJECT_ID

Project ID는 CROSSx 콘솔에서 발급받습니다. 이 값은 빌드 시 딥링크 URI 스킴 주입에도 사용됩니다.

사전 준비

모든 예시의 CheckoutUrl / CheckoutId게임 서버POST /v1/payments를 호출해 발급받은 뒤 클라이언트로 전달한 값입니다.

  • MERCHANT_API_KEY는 서버 환경변수 또는 secret manager에만 저장합니다.
  • 게임 클라이언트는 서버로부터 checkout_urlcheckout_id만 수신합니다.
  • 딥링크 콜백 결과는 주문 확정 근거가 아닙니다. 최종 처리는 webhook 또는 서버 조회(GET /v1/payments/{id})로 확정하세요.

결제 흐름

한 줄 요약

게임 서버가 결제 세션을 생성 → SDK가 WebView를 열고 딥링크 콜백을 대기 → 서버가 webhook으로 주문을 최종 확정합니다. 딥링크 콜백 결과는 확정의 근거가 아닙니다.

Quick Start

#include "SDK/CROSSxSdkSubsystem.h"

// 1. 설정 및 초기화 (GameInstance::Init 등에서 1회 호출)
UCROSSxSdkSubsystem* Sdk = GetGameInstance()->GetSubsystem<UCROSSxSdkSubsystem>();

FCROSSxSdkConfig Config;
Config.ProjectId = TEXT("YOUR_PROJECT_ID");
Config.AppId = TEXT("com.example.mygame");
Config.AppName = TEXT("My Game");
Config.Environment = ECROSSxSdkEnvironment::Development;

Sdk->Configure(Config);
Sdk->InitializeSdk();

// 2. 게임 서버에서 checkout을 생성하고 이 값들을 받아옵니다
FString CheckoutUrl = ...; // 서버에서 수신
FString CheckoutId = ...; // 서버에서 수신

// 3. SDK가 WebView를 열고 딥링크 콜백을 대기
FCROSSxCrossPayPaymentDelegate OnComplete;
OnComplete.BindDynamic(this, &UMyWidget::HandleCrossPayResult);

Sdk->OpenCrossPayAndWaitResultAsync(CheckoutUrl, CheckoutId, OnComplete);
void UMyWidget::HandleCrossPayResult(const FCROSSxCrossPayPaymentResult& Result)
{
if (Result.bSuccess)
{
// 콜백 수신 — 재화 지급 전 서버에서 반드시 검증
UE_LOG(LogTemp, Log, TEXT("콜백 상태: %s"),
*UEnum::GetValueAsString(Result.Status));
MyGameServer->VerifyPayment(Result.CheckoutId);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("CrossPay 오류: %s"), *Result.ErrorMessage);
}
}

딥링크 설정

플러그인이 빌드 시 Unreal Plugin Language(UPL) XML을 통해 Config/DefaultGame.iniProdProjectId를 읽어 필요한 URI 스킴을 자동으로 주입합니다. ProdProjectId가 설정되어 있으면 AndroidManifest.xml이나 Info.plist를 수동으로 편집할 필요가 없습니다.

ProdProjectId가 비어있으면 빌드 오류

플러그인이 빌드 시 ProdProjectId 유효성을 검사합니다. 값이 비어 있으면 의도적으로 빌드가 실패합니다.

참고로 플러그인이 주입하는 내용은 다음과 같습니다:

Android

AndroidManifest.xmlGameActivity에 추가되는 intent filter:

<activity android:name="com.epicgames.unreal.GameActivity"
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

Info.plistURL Typeswebkit-YOUR_PROJECT_ID 추가.

SDK 설정

FCROSSxSdkConfig Config;
Config.ProjectId = TEXT("YOUR_PROJECT_ID");
Config.AppId = TEXT("com.example.mygame"); // 필수: gateway X-App-Id 헤더
Config.AppName = TEXT("My Game");
Config.Environment = ECROSSxSdkEnvironment::Development; // URL 자동 설정
Config.Theme = ECROSSxThemeMode::Dark;
Config.Locale = TEXT("ko");
Config.bEnableDebugLogs = true;

Sdk->Configure(Config);
FCROSSxAuthResult Session = Sdk->InitializeSdk(); // 저장된 세션 복원

ECROSSxSdkEnvironment::Development는 API URL을 자동으로 결정합니다. OAuthServerUrl, AuthApiUrl, EmbeddedWalletGatewayUrl을 직접 지정하려면 Custom을 사용합니다.

결제 실행

Sdk->OpenCrossPayAndWaitResultAsync(
CheckoutUrl,
CheckoutId,
OnComplete,
300000 // TimeoutMs (기본값: 5분)
);

SDK 동작 순서:

  1. CheckoutUrl에 CSRF state 토큰과 콜백 엔드포인트를 추가합니다.
  2. 플랫폼 기본 WebView로 결제 페이지를 엽니다.
  3. 결제 콜백 수신을 대기합니다 — 플랫폼별로 방식이 다릅니다:
    • Android · iOS: 딥링크 스킴 webkit-{ProjectId}://crosspay-callback
    • Windows · macOS: 루프백 HTTP http://localhost:{port}/crosspay-callback
  4. state를 검증하고 디코딩된 결과로 OnComplete를 호출합니다.
  5. 타임아웃 시 bSuccess = false와 오류 메시지로 OnComplete를 호출합니다.

결과 처리

FCROSSxCrossPayPaymentResult는 딥링크 콜백 엔벨로프를 담고 있습니다.

필드타입설명
bSuccessbool오류 없이 콜백이 수신되면 true
StatusECROSSxCrossPayPaymentStatusPENDING · PAID · FAILED · EXPIRED · CANCELLED
ProviderECROSSxCrossPayPaymentProviderBINANCE_PAY · PAYLETTER · CROSS_PAY
CheckoutIdFString콜백에서 반환된 Checkout ID
CheckoutUrlFString원본 checkout URL
CurrencyFString결제 통화
AmountFString결제 금액
ErrorMessageFString!bSuccess일 때 오류 설명
서버에서 반드시 검증하세요

딥링크 콜백 결과는 참고용입니다. 재화 지급 전 서버에서 GET /v1/payments/{id} 조회 또는 webhook으로 결제 상태를 반드시 확인하세요.

오류 처리

Unreal은 예외 대신 결과 struct로 오류를 전달합니다.

void UMyWidget::HandleCrossPayResult(const FCROSSxCrossPayPaymentResult& Result)
{
if (!Result.bSuccess)
{
// 타임아웃, WebView 실패, 또는 딥링크 오류.
// 사용자가 결제를 완료했지만 콜백이 앱에 도달하지 못했을 수 있습니다.
UE_LOG(LogTemp, Warning, TEXT("CrossPay 결과: %s"), *Result.ErrorMessage);

// 성공/실패 여부와 관계없이 서버에서 항상 검증합니다.
MyGameServer->VerifyPayment(Result.CheckoutId);
return;
}
// 성공 처리
}

멱등성

시나리오동작
같은 세션 내 재시도 (네트워크 장애, WebView 재실행 등)동일 CheckoutId 재사용 — 같은 CheckoutUrlCheckoutIdOpenCrossPayAndWaitResultAsync 재호출
결제가 터미널 상태(PAID / FAILED / EXPIRED / CANCELLED)에 도달폐기 — 서버에서 POST /v1/payments를 호출해 새 checkout을 발급합니다
다른 주문 / 금액 / 플레이어의 새 결제항상 새 checkout — 이전 CheckoutId 재사용 시 금액 불일치로 거부될 수 있습니다
환불 없음

CROSS Pay는 현재 환불 기능을 제공하지 않습니다. 동일 주문이 두 번 결제되면 되돌릴 수 없습니다. 재시도 시 동일 Idempotency-KeyPOST /v1/payments를 호출하세요.