딥링크를 통한 인증 설정
딥 링크를 사용하면 사용자 ID 또는 엑솔라 로그인을 통한 인증 프로세스를 거치지 않아도 클릭 한 번으로 게임에서 웹샵으로 인증할 수 있습니다.
사용자의 모바일 기기에 게임이 설치되어 있으면 인증 토큰을 받을 수 있도록 딥 링크를 통해 게임으로 리디렉션됩니다.
사용자 절차
모바일 앱에서
- 웹샵에서 인증되지 않은 사용자가 로그인 버튼 또는 구매 버튼을 클릭합니다. 사용자 ID를 입력하거나 모바일 게임을 통해 로그인할 수 있는 모달 창이 열립니다.
- 사용자가 게임에서 로그인 버튼을 클릭합니다.
- 사용자는 인증된 사용자로 웹샵으로 자동 리디렉션되는 게임으로 리디렉션됩니다.
데스크톱 버전에서
- 웹샵에서 인증되지 않은 사용자가 로그인 버튼 또는 구매 버튼을 클릭합니다. 사용자 ID를 입력하거나 QR 코드를 사용하여 모바일 버전의 게임에 로그인할 수 있는 모달 창이 열립니다.
- 사용자가 모바일 기기로 QR 코드를 스캔하면 모바일 기기에서 웹샵이 열립니다.
- 사용자의 모바일 기기에서 모바일 버전의 게임이 열립니다.
- 사용자는 인증된 사용자로 모바일 기기의 웹샵으로 자동 리디렉션됩니다.
서비스 상호작용 절차
설정 방법
게임에서
- 게임의 모바일 앱 설정에서 딥링크를 통해 게임을 열 수 있는 URL 스키마를 등록합니다.
- 안드로이드 애플리케이션의 경우
AndroidManifest.xml
파일에서 - iOS 애플리케이션의 경우
Info.plist
파일에서
- 안드로이드 애플리케이션의 경우
gamename://authorize
, 여기서:gamename
- 사용자 인증 시 모바일 기기에서 열어야 하는 게임 이름.authorize
- gamename 게임이 열린 후 수행해야 하는 작업 이름의 예시. 애플리케이션 운영 체제의 작업과 일치하는 작업 이름을 사용합니다.
Copy
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>gamename</string>
</array>
</dict>
</array>
Android 애플리케이션에 URL 스키마를 등록하는 예시:
Copy
<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="gamename" android:host="authorize" />
</intent-filter>
- 게임의 사용자 ID를 사용하여 JWT 형식의 인증 토큰 생성을 구현합니다.
매개 변수 | 유형 | 설명 |
---|---|---|
loginId | string | 관리자 페이지에서 인증 방식의 ID입니다. 필수 항목. |
webhookUrl | string | 웹샵에서 사용자 인증 웹훅을 수신할 때 사용하는 URL입니다. 이 URL은 사용자 ID를 통한 인증 설정 시 지정된 주소와 일치해야 합니다. 엑솔라는 웹훅에 대한 응답으로 웹샵 사용자를 인증하기 위해 200 HTTP 코드를 기대합니다. 엑솔라가 404 HTTP 코드가 포함된 응답을 받거나 응답을 받지 못하면 사용자가 인증되지 않습니다."webhookUrl": "https://nowebhook.com" 값을 지정하여 딥 링크를 통해 인증할 때 웹훅을 비활성화할 수 있습니다. 이 경우 엑솔라는 사용자의 존재 여부를 확인하지 않으므로 user.id 매개 변수에서 게임 내에 존재하는 사용자 ID를 전달해야 합니다.필수 항목. |
settings.projectId | string | 프로젝트 ID는 관리자 페이지에서 찾을 수 있으며, 브라우저 주소 표시줄의 프로젝트 이름 옆에 표시됩니다. URL의 형식: https://publisher.xsolla.com/merchant ID/Publisher Account section . 필수 항목. |
user.id | string | 게임의 사용자 ID. 필수 항목. |
user.name | string | 사용자 이름. 필수 항목. |
curl을 사용하여 사용자 토큰 생성 API 메소드를 호출하는 예시:
Copy
- curl
curl --location 'https://sb-user-id-service.xsolla.com/api/v1/user-id' \
--header 'Content-Type: application/json' \
--data '{
"loginId": "000001aa-001a-0ab0-00001-01a01a01a01a",
"webhookUrl": "https://nowebhook.com",
"settings": {
"projectId": 123456,
"merchantId": 123456
},
"user": {
"id": "123",
"name": "a-user-name"
}
}'
{token=“JWT_TOKEN”}
과 같은 응답을 받게 됩니다.
- 인증 성공 알림이 있는 팝업 창을 추가합니다(선택 사항).
- 획득한 사용자 토큰을 사용하여 브라우저에서 웹샵 열기를 구현합니다.
인증된 사용자를 위한 브라우저에서 웹샵을 열 때 사용하는 URL의 생성 예시:
- 사용자 정의 도메인을 사용하는 경우
https://example.com/?token={token}
- 엑솔라 도메인을 사용하는 경우
https://example.xsollasitebuilder.com/?token={token}
{token}
는 사용자의 인증 토큰입니다.사이트 빌더 내에서
아래 제공된 코드 예시를 사용하여 웹사이트 빌더에서 직접 설정할 수 있습니다.설정 지원은 계정 관리자에게 문의하거나 csm@xsolla.com으로 이메일을 보내 주세요.
알림
먼저 사용자 ID를 통한 인증을 설정합니다. 이는 딥 링크 인증을 사용할 수 없는 경우 사용자가 다른 인증 방식을 사용할 수 있도록 하기 위해 필요합니다. 예를 들어, 사용자의 모바일 기기에 게임이 설치되어 있지 않은 경우입니다.
- 프로젝트를 관리자 페이지에서 엽니다.
- 사이드 메뉴에서 웹사이트 빌더를 클릭합니다.
- 사용자 ID를 통한 인증으로 웹샵 사이트의 카드에서 구성을 클릭합니다.
- 사용자 정의 코드 블록을 사용하여 딥 링크 인증을 추가합니다.
- 자바스크립트 탭의 사용자 정의 코드 블록에서 모바일 및 데스크톱 버전에 대한 딥 링크 인증을 구현하는 아래의 사용자 정의 코드를 추가합니다.
- 코드에 있는 상수 값을 사용자의 값으로 교체합니다.
DEEPLINK_URL
- 딥 링크를 통해 게임을 열 때 사용하는 URL 스키마.QR_CODE
- SVG 형식에서 게임을 열 때 사용하는 QR 코드. QR코드에는source = pc
매개 변수가 있는 웹샵 링크가 포함되어 있어야 합니다.deeplink__description
- 모바일 애플리케이션의 게임 인증 버튼 아래에 표시되는 설명.qr-code-auth__description
- 데스크톱 버전의 게임에서 QR 코드 아래에 표시되는 설명.
Copy
- js
{
const DEEPLINK_URL = 'gamename://authorize';
const QR_CODE = `YOUR_SVG_WITH_QR_CODE`;
handleQRCodeRedirect();
// Event handlers are set up for opening the authentication modal or for successful authentication.
// If the event of opening the authentication modal occurs, the handler is triggered and a button or QR code is added to this window.
// If the event of successful authentication occurs, the handler is triggered and the successful authentication modal is opened.
window.addEventListener('custom-event:modal:render', updateLoginModal);
window.addEventListener('custom-event:authorization:login', showLoginSuccessPopup);
let getToken;
const searchParams = new URLSearchParams(window.location.search);
const tokenParam = searchParams.get('token');
if (tokenParam) {
showLoginSuccessPopup(tokenParam);
}
window.SB.subscribe(api => {
getToken = api.getToken;
});
// Customization of the authentication modal window (adding a QR code for the desktop version of the game or a login button for the mobile version of the game).
function updateLoginModal({detail}) {
const {name, element} = detail;
if (name !== 'user-id' || element.dataset.isQrAdded === 'true') return;
const isMobile = checkMobileDevice();
const ModalBody = element.querySelector('.ui-site-modal-window__body');
const ModulInput = ModalBody.querySelector('.user-id-modal__input');
element.classList.toggle('mobile-layout', isMobile);
ModalBody.append(getTemplate(isMobile));
element.dataset.isQrAdded = true;
}
// Function to display the successful authentication modal window.
function showLoginSuccessPopup(t) {
let username = 'Gamer';
try {
const token = getToken?.() || t;
if (token) {
const jwtData = parseJwt(token);
const payload = JSON.parse(jwtData.payload);
if (payload.userInfo.name) {
username = payload.userInfo.name;
} else if (jwtData.username) {
username = jwtData.username;
} else if (jwtData.server_custom_id) {
username = jwtData.server_custom_id;
}
}
} catch (e) {
console.log('error', e);
}
const Modal = document.querySelector('.deeplink-auth');
const ModalTitle = Modal.querySelector('.deeplink-auth__title');
ModalTitle.textContent = ModalTitle.textContent.replace(/\{username}/g, username);
Modal.classList.add('_visible');
Modal.querySelector('.deeplink-auth__button').addEventListener('click', closeModal);
function closeModal() {
Modal.classList.remove('_visible');
}
}
// Function that displays a QR code for the desktop version of the game or a login button for the mobile version of the game. Your_game should be replaced with the actual name of your game. To continue with the authentication, the game has to be installed on the user's phone.
function getTemplate(isMobile) {
const Deeplink = document.createElement('div');
if (isMobile) {
Deeplink.innerHTML = `
<div class="deeplink__title"><span class="deeplink__title-text">or login via game</span></div>
<a href="${DEEPLINK_URL}" class="deeplink__button" rel="noopener">Login via Mobile Game</a>
<div class="deeplink__description">
You need to have the Your_game installed on your phone in order to proceed with authorization and purchase
</div>
`;
return Deeplink;
}
Deeplink.innerHTML = `
<div class="separator">
<span class="separator__text">or login via game</span>
</div>
<div class="qr-code-auth">
<div class="qr-code-auth__image">${QR_CODE}</div>
<span class="qr-code-auth__description">
Scan the QR code with your phone and you will continue purchasing on your mobile device
</span>
</div>
`;
return Deeplink;
}
function checkMobileDevice() {
return (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent));
}
// Function that decodes the JWT token and returns its payload as a JavaScript object.
function parseJwt(token) {
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
return JSON.parse(jsonPayload);
}
// Function to verify the vent occurred when the user scanned the QR code.
function handleQRCodeRedirect() {
const url = new URL(window.location.href);
const source = url.searchParams.get('source');
if (source === 'pc') {
if (checkMobileDevice()) {
const button = document.getElementById("deeplink-redirect");
button.click();
}
setTimeout(function () {
url.searchParams.delete('source');
window.history.pushState({}, '', url);
}, 2000);
}
}
}
- HTML 탭의 사용자 정의 코드 블록에서 웹샵에서 인증 성공을 나타내는 모달 창에 대한 코드를 추가합니다. 자체 텍스트 값을 설정할 수 있습니다.
Copy
- html
<div class="ui-site-modal-window deeplink-auth">
<div class="deeplink-auth__body">
<div class="deeplink-auth__image"></div>
<div class="deeplink-auth__title">Hi, {username}!</div>
<div class="deeplink-auth__description">You’ve successfully logged in to the Web Shop</div>
<button class="deeplink-auth__button">Continue</button>
</div>
</div>
<a href="gamename://authorize" id="deeplink-redirect" style="display: none;"></a>
- CSS 탭의 사용자 정의 코드 블록에서 새 인터페이스 구성 요소에 대한 스타일을 추가합니다. 권장 스타일은 아래와 같습니다. 인증 성공 시 표시되는 고유한 이미지의 URL을 지정합니다.
Copy
- css
--deeplink-success-image-url: url("Successful_authorization_image_url");
.html-v2:has(.deeplink-auth._visible) {
z-index: var(--modalsZIndex);
}
.user-id-modal__content {
padding: 0 16px !important;
}
@media (min-width: 768px) {
.user-id-modal__content {
max-width: 460px !important;
padding: 0 3.8rem 4rem !important;
overflow: auto;
justify-content: center;
}
}
.mobile-layout .user-id-modal__close {
right: 8px;
top: 16px;
}
.user-id-modal__logo {
width: 109px;
height: 38px;
margin: 4rem auto 2rem;
}
.user-id-modal.mobile-layout .user-id-modal__logo {
margin: 3rem auto 2.5rem;
}
.user-id-modal__wrapper {
margin-top: 0;
}
.user-id-modal__title {
margin-bottom: 3rem;
}
.user-id-modal__input-wrapper .ui-site-input {
height: 41px;
}
.user-id-modal__input {
margin-top: 0;
border-radius: var(--border-radius-16);
}
.user-id-modal__button {
height: auto;
margin: 12px auto 0;
padding: 1.25rem;
border-radius: var(--border-radius-16);
}
.user-id-modal__button .simple-button__text {
font-size: 1.8rem;
font-weight: 500;
line-height: 2.5rem;
letter-spacing: 0.5px;
}
.user-id-modal .ui-site-instruction-cut__title .ui-site-description {
margin-bottom: 3rem;
font-size: 1.2em;
}
.user-id-modal .ui-site-instruction-cut__steps {
overflow-y: visible;
}
.user-id-modal .ui-site-instruction-card:first-child {
margin-top: 0;
}
.user-id-modal .ui-site-instruction-card:last-child {
padding-bottom: 4rem;
}
.user-id-modal .separator {
display: flex;
margin-bottom: 3.8rem;
align-items: center;
}
.user-id-modal .separator__text {
padding: 0 1.5rem;
font-family: var(--main-font);
font-size: 1.8rem;
font-weight: 500;
line-height: 2.5rem;
letter-spacing: 0.8px;
}
.user-id-modal .separator::before,
.user-id-modal .separator::after {
content: "";
background-color: #797979;
height: 1px;
flex-grow: 1;
}
.user-id-modal .qr-code-auth__image {
display: block;
width: 160px;
height: 160px;
margin: 0 auto 20px;
}
.user-id-modal .qr-code-auth__image svg {
display: block;
width: 100%;
height: 100%;
}
.user-id-modal .qr-code-auth__description {
display: inline-block;
font-family: var(--main-font);
font-size: 1.5rem;
font-weight: 400;
line-height: 2.5rem;
text-align: center;
color: rgba(255, 255, 255, 0.7);
}
.deeplink__title {
display: flex;
margin-bottom: 3.8rem;
align-items: center;
}
.deeplink__title-text {
padding: 0 1.5rem;
font-family: var(--main-font);
font-size: 1.8rem;
font-weight: 500;
line-height: 2.5rem;
letter-spacing: 0.8px;
}
.user-id-modal .deeplink__title::before,
.user-id-modal .deeplink__title::after {
content: "";
background-color: #797979;
height: 1px;
flex-grow: 1;
}
.deeplink__button {
display: block;
margin-bottom: 2.2rem;
padding: 1.3rem;
font-family: var(--main-font);
font-size: 1.8rem;
font-weight: 500;
line-height: 2.5rem;
letter-spacing: 0.5px;
text-align: center;
color: var(--button-text-color);
background: var(--accent-color);
border-radius: var(--border-radius-16);
text-decoration: none;
transition: transform .2s cubic-bezier(.509, .001, .25, 1);
}
.deeplink__button:hover {
transform: scale(.95);
}
.deeplink__description {
margin-bottom: 2rem;
font-family: var(--main-font);
font-size: 1.6rem;
line-height: 2.3rem;
text-align: center;
color: rgba(255, 255, 255, 0.7);
letter-spacing: 0.5px;
}
.deeplink__cta-wrapper {
display: flex;
flex-direction: row;
gap: 1.8rem;
padding: 0 1.2rem;
}
.deeplink__cta-wrapper .ui-site-calltoaction__item {
margin: 0;
flex-grow: 2;
}
.deeplink__cta-wrapper .ui-site-calltoaction {
width: 100%;
min-width: auto;
height: auto;
min-height: auto;
padding-bottom: 36%;
background-size: contain;
border-radius: 0;
}
.deeplink-auth {
display: none;
}
.deeplink-auth._visible {
display: flex;
}
.deeplink-auth__body {
position: relative;
width: 100%;
max-width: 336px;
padding: 16px;
border-radius: 12px;
background-color: var(--colors-core-background-secondary-rgb);
border: var(--border-size) solid var(--border-color);
font-family: var(--main-font);
color: var(--colors-core-text-primary);
}
@media (min-width: 768px) {
.deeplink-auth__body {
max-width: 460px;
padding: 24px 30px;
}
}
.deeplink-auth__image {
margin-bottom: 20px;
padding-bottom: 49%;
border-radius: 12px;
background-image: var(--deeplink-success-image-url);
background-repeat: no-repeat;
background-position: center;
background-size: cover;
}
@media (min-width: 768px) {
.deeplink-auth__image {
padding-bottom: 37%;
}
}
.deeplink-auth__title {
margin-bottom: 10px;
font-size: 20px;
font-weight: 500;
line-height: 24px;
text-align: center;
letter-spacing: 0.7px;
}
@media (min-width: 768px) {
.deeplink-auth__title {
margin-bottom: 20px;
font-size: 24px;
line-height: 30px;
}
}
.deeplink-auth__description {
margin-bottom: 20px;
font-size: 13px;
font-weight: 400;
line-height: 18px;
text-align: center;
letter-spacing: 0.5px;
color: rgba(255, 255, 255, 0.7);
}
.deeplink-auth__button {
display: block;
width: 100%;
padding: 1.3rem;
font-family: var(--main-font);
font-size: 1.8rem;
font-weight: 500;
line-height: 2.5rem;
letter-spacing: 0.5px;
text-align: center;
color: var(--button-text-color);
background: var(--accent-color);
border-radius: var(--border-radius-16);
text-decoration: none;
transition: transform .2s cubic-bezier(.509, .001, .25, 1);
}
@media (min-width: 768px) {
.deeplink-auth__button {
padding: 12px;
font-size: 16px;
line-height: 24px;
}
}
.deeplink-auth__button:hover {
transform: scale(.95);
}
이 기사가 도움이 되었나요?
의견을 보내 주셔서 감사드립니다!
메시지를 검토한 후 사용자 경험 향상에 사용하겠습니다.이 페이지 평가
이 페이지 평가
답하기 원하지 않습니다
의견을 보내 주셔서 감사드립니다!
오자 또는 기타 텍스트 오류를 찾으셨나요? 텍스트를 선택하고 컨트롤+엔터를 누르세요.