Hive SDK v1.19.1부터 IAP v1의 지원을 종료합니다. Hive SDK v1.19.1 이상의 환경에서는 Hive IAP v4를 적용하시기 바랍니다.
본 가이드는 Hive IAP v1에 대해 안내하며, Hive SDK v1.19.0 이하 환경에서 적용하실 수 있습니다.

마켓에서는 기본적으로 과금을 클라이언트 디바이스에서 결제가 진행되도록 한다.
서버 영수증 검증을 추가로 진행하지 않으면 아래와 같은 상황에서 어뷰징 및 과금 중복 지급이 발생할 수 있다.

해킹 툴 사용시 과금 아이템 지급

타 게임 영수증 해킹을 통해 과금 아이템 지금 및 아이템 중복 지급 해킹 툴 사용
클라이언트 디바이스에서 결제가 해킹툴을 이용하게 되면 영수증 발급 없이 바로 과금 성공으로 진행
이때 영수증 검증 과정이 없으면 바로 과금 아이템이 지급되는 상황이 발생하게 된다.

서버 영수증 검증과 Inapp Finish() 처리 관련

서버 영수증 검증 시 검증 실패 혹은 성공에 대한 정상적인 피드백을 받은 후에 Finish를 호출해야 하며 검증결과를 받지 못하는 상황( 네트워크 오류 등등..) 에서는 Finish를 호출하지 않아야 한다.
Finish를 하게되면 모든 구매처리가 완료된것으로 미지급건에 대한 재지급 로직이 작동하지 않아 결재는 되었는데 아이템이 미지급되는 상황이 발생 할 수 있다.

플랫폼 영수증 검증

영수증 해킹 사용 및 아이템 중복 지급

– 클라이언트에서 과금 쪽 우회 방법의 하나로 이미 다른 게임 또는 사용된 적이 있는 영수증을 다시 전송하는 방법으로, 이 경우 단순 애플 영수증 검증만으로는 해결되지 않으므로 영수증 내용을 서버에서 판단해야 한다. 과금아이템과 영수증 과금아이템 일치성 여부 등을 판단해야 한다.

– 아이템 중복 지급은 단순 영수증 검증만으로는 해결이 되지 않으며, 게임 서버에서 영수증 검증 후 전달되는 데이터에 유니크한 값을 저장하여 중복지급 여부를 판단해야 한다. 지급이 성공된 유니크한 값에 대한 지급은 게임 서버 쪽에서 막아야 한다. (마켓에 따른 유니크한 값은 아래 내용에 설명)


영수증 해킹 방지 게임 서버 구현 가이드
체크 사항

– 캐시 아이템 지금과 영수증 사용 체크는 하나의 트랜젝션으로 묶여야 해서 Hive에서는 처리 할 수 없고 게임 서버에서 구현해야 한다.
– 영수증 검증은 1회 이상 요청하더라도 영수증 자체가 유효하다면 항상 TRUE를 리턴한다.

자신의 게임에서 발급된 영수증인지 체크

– Google: 최초에 클라이언트로 날라오는 영수증 데이터의 packageName 항목에서 앞 3자리가 appid의 앞 3자리와 같은지 체크
– Apple: 영수증 검증 API 호출 후 날라오는 결과 json 에서, product_id 항목의 앞 3자리가 appid의 앞 3자리와 같은지 체크

이미 사용된 영수증인지 체크

– Key 값
i. Google: 테스트 계정으로 구매 테스트 시 구글에서 orderId가 영수증에 포함되지 않도록 변경이 되었음
(실 계정에서는 orderId 영수증에 포함되어 있음)
아래 2가지 방법 중 하나를 적용하거나 2개 다 적용 하여 영주증 중복 체크를 진행할 수 있음

1. 영수증 내 다른 유니크한 값인 purchaseToken 값을 사용하여 중복 체크
2. 영수증 내 developerPayLoad 값의 addtionalInfo 값을 사용하여 중복 체크
(addtionalInfo값은 클라이언트가 Purchase 시 넣을 수 있는값으로 유니크한 값을 넣으면 영수증에 포함됨)
예)유니크한 값: (유저ID + 시간정보) 혹은 인덱스 counter 등등 게임에 맞는 방법으로 유니크한 값 생성
참고: C2SModuleInapp_Purchase

* 유저가 cs로 실제 구매 영수증을 보냈을 때는 orderid값이 있어서 cs 대응을 위해서는 orderid도 남겨야 함


(주의) 구글 promo code로 구매가 적용되었을 경우
: 보통 프로모션용 pid를 새로 생성하여 진행하게 되는데, 이 경우 영수증에는 orderId와 developerPayLoad자체가 없으며, 결국 영수증을 보면 pid를 통해 promo code로 구매한지를 알 수 있게 됨.
일반적으로 promo code로 지급되는 상품은 유저당 한번만 지급하게 하기 때문에 유저별로 이 pid로 상품을 지급한 적이 있는지 체크하면 중복 지급을 막을 수 있음

ii. Apple: 영수증 검증 API 호출 후 날라오는 결과 json 에서 transaction_id 값

– 해당 Key 값을 기준으로 해당 Key 값을 DB 에 insert 한 후 캐시 아이템 지급 처리 (이 두 가지는 트랜잭션 처리)
i. 만약 이미 insert된 Key 값이면 영수증 중복 사용 해킹 case 이므로 캐시 아이템 지급하지 않고 에러 처리


데이터 연동 방식 및 정보

영수증 데이터 연동 구조

– IAP 과금 성공 후 받은 영수증 데이터 json 타입이 json object가 아닌 string type으로 전송해야 한다.

– 애플에서 제공하는 데이터가 json object 가 아닌 형태로 넘어오기에, 양쪽 모두 동일한 구현이 가능하도록 string type 으로 넘기도록 구현

– 애플 데이터 형태는 json이 아닌 name 과 value 구분을 콜론(:)이 아니라 equal(=)로 전송한다.

내부적으로 string을 받아서 서버에서 처리하는 구조로 되어 있다.

– 위와 같은 데이터 연동 구조를 맞추기 위해선 아래와 같은 변환 순서에 맞춰 영수증 데이터를 변환해야 함

영수증 데이터 변환 순서
구글 및 애플에서 넘겨주는 영수증 데이터 아래와 같은 방법으로 변환하여 전달하면 된다.
(이미 String type로 되어 있으면 아래 치환 작업을 할 필요가 없음)

1. 역슬래시(\)를 더블 역슬래시(\)로 치환
2. 따옴표(“)를 역슬래시+따옴표 (\”)로 치환
3. 이를 저희 API 를 호출하실 때 transaction 부분에 string type으로 세팅
( 가장 바깥의 중괄호 {} 바깥에 큰따옴표 추가 )
( 즉, json format 이 아닌 애플 영수증 데이터를 따로 파싱을 하여 가공하지 않아도 된다. )

데이터 적용방법

영수증 데이터 적용 방법

– 전송방식 : HTTPS POST 방식
– Data Format : JSON
– HTTP Header : Content-Type:text/html

서버 정보

– 테스트 서버 : http://mdev.com2us.net/common/billing/verify.c2s
– 상용 서버 : http://s.qpyou.cn/common/billing/verify.c2s
– 샌드박스 서버 : http://sandbox-s.com2us.net/common/billing/verify.c2s

JSON 데이터 정보
Data(Body) 필드명 [JSON Type] 설명

기본 정보 필드

필드 타입 데이타 내용 필수여부
market [String] apple, google, amazon 필수
appid [String] AppID 필수
appversion [String] 어플 버전 정보 (x.x.x 형식) 필수
device [String] 디바이스 정보 필수
osversion [String] OS 버전 정보 필수
country [String] 디바이스 설정 국가정보 (ISO 3166-1 alpha-2)
Hive 연동 가이드라인 국가 항목을 참고한다.
language [String] 디바이스 설정 언어정보 (ISO 639-1)
Hive 연동 가이드라인 언어 항목을 참고한다.
mac [String] mac address 정보
mdn [String] 전화번호정보 (apple 제외)
androidid [String] Android ID (안드로이드 단말만 해당)
udid [String] 단말 고유 ID (안드로이드에서는 IMEI)

APPLE 검증 요청

Send Data (Body)
과금 공통 기본 정보
transaction [String] 영수증 데이터
Apple 검증 요청 json 샘플

APPLE 검증 응답

Recv Data (Body)
result [Number] 결과 (0:성공, 1:검증 실패, 2:서버 내부 에러, 3:에러)
errormsg [String] 에러 메세지 (에러 상황일 경우 에러 메세지)
transaction [JsonObject] apple 서버에 검증 요청하여 반환된 영수증 데이터
market_pid [String] 영수증 또는 영수증 검증결과에서 추출한 MARKET PID , 이 값으로 지급할 아이템을 판단하여 지급함. SDK에서 콜백으로 전달한 MARKET PID는 메모리 변조의 위험이 있어 그 값으로 사용하면 안됨. result값이 0(검증 성공)일 때만 값을 전달함.
Apple 검증 응답 json 샘플

Google 검증 요청

Send Data (Body)
과금 공통 기본 정보
apiver [Number] 구글 과금 API버전 (최신버전3)
transaction [String] 과금 정보 (마켓 서버 내용 JSON 문자열 동일하게 전송)
signature [String] Signature 정보
Google 검증 요청 json 샘플

 

Google 검증 응답

Recv Data (Body)
result [Number] 결과 (0:성공, 1:검증 실패, 2:서버 내부 에러, 3:에러)
errormsg [String] 에러 메세지 (에러 상황일 경우 에러 메세지)
market_pid [String] 영수증 또는 영수증 검증결과에서 추출한 MARKET PID , 이 값으로 지급할 아이템을 판단하여 지급함. SDK에서 콜백으로 전달한 MARKET PID는 메모리 변조의 위험이 있어 그 값으로 사용하면 안됨. result값이 0(검증 성공)일 때만 값을 전달함.
Google 검증 응답 json 샘플

Amazon 검증 요청

Send Data (Body)
과금 공통 기본 정보
apiver [Number] API 버전 (현재 2로 고정)
userid [String] userid 정보
receiptid [String] receiptid 정보
Amazon 검증 요청 json 샘플

Amazon 검증 응답

Recv Data (Body)
result [Number] 결과 (0:성공, 1:검증 실패, 2:서버 내부 에러, 3:에러)
errormsg [String] 에러 메세지 (에러 상황일 경우 에러 메세지)
market_pid [String]
영수증 또는 영수증 검증결과에서 추출한 MARKET PID , 이 값으로 지급할 아이템을 판단하여 지급함. SDK에서 콜백으로 전달한 MARKET PID는 메모리 변조의 위험이 있어 그 값으로 사용하면 안됨. result값이 0(검증 성공)일 때만 값을 전달함.
Amazon 검증 응답 json 샘플

ONE Store 검증 요청

Send Data (Body)
과금 공통 기본 정보
apiver [Number] API버전 (현재 1로 고정)
tstore_appid [String] ONE Store ApplicationID
transaction [String] 구매 성공 시 응답 받은 JSON 스트링
ONE Store 요청 응답 json 샘플

ONE Store 검증 응답

Recv Data (Body)
result [Number] 결과 (0:성공, 1:검증 실패, 2:서버 내부 에러, 3:에러)
errormsg [String] 에러 메세지 (에러 상황일 경우 에러 메세지)
market_pid [String] 영수증 또는 영수증 검증결과에서 추출한 MARKET PID , 이 값으로 지급할 아이템을 판단하여 지급함. SDK에서 콜백으로 전달한 MARKET PID는 메모리 변조의 위험이 있어 그 값으로 사용하면 안됨. result값이 0(검증 성공)일 때만 값을 전달함.
ONE Store 검증 응답 json 샘플

 

러비 영수증 검증

Request URL
  • TEST: http://test.m.billapp.com2us.com/module/VerifyOrder
  • REAL: https://m-billapp.qpyou.cn/module/VerifyOrder
  • Sandbox API: https://sandbox-m-billapp.qpyou.cn/module/api
요청 검증을 위한 SIGN값 생성
  • sign String 생성

1. Key값 기준 ASCII코드 오름차순 정렬
2. 예

1. 전송파라메터 : corp=gvi&tradeno=2286203&memberid=123456
2. 정렬 후 : corp=gvi&memberid=123456&tradeno=2286203

3. 주의 : sign을 제외한 전송되는 모든 파라메터 포함 (문서에서 요구 하지 않은 파라메터가 추가 되더라도 해당 값은 sign값을 생성할 때 포함되어야 한다. 예를 들어 a=a라는 파라메터가 문서 상 요구하는 파라메터가 아니더라도 함께 전송된다면 a=a&corp=gvi&memberid=123456&tradeno=2286203 라고 생성해야 한다.)

 

  • Key값 추가

1. 암호화에 필요한 Key값(플랫폼클라이언트팀에 요청 필요)을 Sign String 맨 뒤에 추가한다.
2. 예: corp=gvi&memberid=123456&tradeno=2286203&key=test1234

 

  • MD5로암호화

1. Sign String 을 MD5 로 암호화
2. Sign String 을 BYTE로 변환시 UTF-8로 인코딩
3. 예: 7109969883EF75B9C2B72B0BDE6C962E

 

  • 예제:

1. sign 생성: corp=gvi&memberid=123456&tradeno=2286203&key=test1234
2. key 미포함 요청: corp=gvi&memberid=123456&tradeno=2286203&Sign=7109969883EF75B9C2B72B0BDE6C962E

 

요청(요청타입 : GET, POST)

Key 형식 설명 필수여부
corp String 회사구분 (컴투스홀딩스: GVI) 필수
tradeno bigint 아이템 구매시 러비에서 발급한 거래번호 필수
memberid bigint 구매자 허브UID 필수
sign String 요청 검증을 위한 Sign값 필수

반환값(반환타입 : json)

Key Data type 설명
result_code int 결과 코드
result_msg String 결과 메세지
OrderInfo(JSON) : 결제 정보
tradeno bigint 거래 번호
memberid bigint 허브 회원 UID
trademoney integer 거래 금액
tradedate String 거래 시간
billitemid String 아이템 코드
itemname String 아이템명
game_name String 게임명
appidcode String APPID
{
“result_msg”:”success”,
“orderInfo”:{
“appidcode”:”com.com2us.smon.lebi.freefull.google.global.android.common”,
“trademoney”:360,
“tradeno”:2286203,
“tradedate”:”2014-11-10 18:48:28.503391″,
“itemname”:”????”,
“billitemid”:”020101500001″,
“memberid”:”130944654″,
“game_name_ori”:”Summoners War:Sky Arena”
},
“result_code”:200
}

결과 코드(Result_code)

200 성공
500 트랜잭션 에러
404 일치하는 거래건 없음 (Tradeno. Memberid가 다를 경우)
403 Sign값 검증 실패