1. 구글 개발자 콘솔에서 인증서 발급하고 다운받기(json 키)
위 이미지 대로,
[구글 플레이 콘솔 api액세스 -> 새서비스계정만들기 -> 서비스계정 생성 -> 계정 key다운로드(json파일) -> 재무데이터 권한 주기]
를 완료하면 준비는 끝.!!
키 json 파일을 다운받아 열어보면, 위와같은 텍스트 들이 있다.
2. Firebase Functions 설정
Firebase 계정을 만들고, 내 컴퓨터에도 firefunction기능을 쓸 수 있도록 세팅. 세팅은 구글페이지 가이드를 참고 바람.
https://firebase.google.com/docs/functions/get-started?hl=ko
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "16"
},
"main": "index.js",
"dependencies": {
"firebase-admin": "^9.8.0",
"firebase-functions": "^3.14.1",
"iap": "^1.1.1",
},
"devDependencies": {
"firebase-functions-test": "^0.2.0"
},
"private": true
}
세팅한 firebase 디렉토리/functions/package.json에 iap모듈을 추가한다.
"iap":"^1.1.1"
해달 모듈 사용법은 아래 링크를 참고해주세요.
https://www.npmjs.com/package/iap
3. 함수 작성(index.js)
index.js를 열어 아래와 같이 작성한다. 여기서는 onRequest가 아닌 onCall을 이용해 함수를 구현했다.
onRequest는 http통신방식으로 function을 이용할 수 있고,
onCall는 유니티 firefunction라이브러리 함수로 간단하게 함수를 호출 할 수 있다. 대신 인앱 검증이 비동기 함수라, 서버에서 받아 올 수 없다.(있겠지만 귀찮아서 안함)
만약 클라이언트에서 인앱검증 실패에대한 처리를 따로 하고 싶다면 onRequest방식으로 구현하길 바란다.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const iap = require('iap');
admin.initializeApp(functions.config().firebase);
//이미 검증한 영수증인지 확인
function checkTranscationId(transactionId) {
return new Promise((resolve, reject) => {
admin.firestore()
.collection('iap')
.doc(transactionId)
.get()
.then(doc => {
if (doc.exists) {
reject(new Error("Duplicate Transaction"));
}
else {
resolve();
}
return Promise.resolve();
})
.catch((error) => {
reject(error)
});
});
}
//검증할 영수증 firestore에 기록
function saveTransactionId(transactionId, key) {
admin.firestore()
.collection('iap')
.doc(transactionId)
.set({"exist": true,
"user_key":key
});
return Promise.resolve();
}
exports.verifyInAppPurchase = functions.https.onCall((data, context) => {
let user_key = data.user_key;
let date_time = data.date_time;
let transactionId = data.transactionId;
let payment = {
receipt: data.purchaseToken,
productId: data.productId,
packageName: data.packageName,
subscription: false,
//다운로드 받은 key json파일에서 참고
keyObject: {
"type": "service_account",
"project_id": "{...}",
"private_key_id": "{...}",
"private_key": "-----BEGIN PRIVATE KEY-----\{...}\n-----END PRIVATE KEY-----\n",
"client_email": "{...}",
"client_id": "{...}",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "{...}"
}
};
return checkTranscationId(transactionId)
.then(() => saveTransactionId(transactionId,user_key))
.then(() =>iap.verifyPayment("google", payment, function(error, response) {
if(error == null && response != null)
{
//성공 처리
console.log('success');
}
else
{
//실패 처리. firestore에 해당 유저의 계정을 저장
console.log('failed : ' + error);
admin.firestore()
.collection('IapFault')
.add({
"user_key": user_key,
"date_time" : date_time,
"transactionId" : transactionId,
"productId" : data.productId
});
}
}))
.catch((error) =>{
//검증 실패, 이미 검수한 영수증인 경우 처리
console.log('end error : '+error);
//해당 유저정보 저장
admin.firestore()
.collection('IapFault')
.add({
"user_key": user_key,
"date_time" : date_time,
"transactionId" : transactionId,
"productId" : data.productId
});
} );
});
4. Client(Unity)에서 Function 호출
public class Receipt
{
// {
// packageName: 'The packge name of the item purchased',
// productId: 'The product ID of the item purchased',
// purchaseToken: 'PurchaseToken of the receipt from Google',
// subscription: true/false // if the receipt is a subscription, then true
//}
public string packageName;
public string productId;
public string purchaseToken;
public bool subscription;
}
public void IapVerify(string user_key, string type, string transactionId, Receipt receipt)
{
var data = new Dictionary<string, object>();
data["type"] = type;
data["transactionId"] = transactionId;
data["purchaseToken"] = receipt.purchaseToken;
data["productId"] = receipt.productId;
data["packageName"] = receipt.packageName;
data["user_key"] = user_key;
data["date_time"] = DateTime.Now.ToLocalTime().ToString("yyyy'-'MM'-'dd' 'HH':'mm':'ss");
var function = m_instance.GetHttpsCallable("verifyInAppPurchase");
function.CallAsync(data).ContinueWith((task) => {
//클라이언트에서 인앱 검증에 대한 정보를 따로 받아오지 않음..
if (task.IsFaulted || task.IsCanceled)
{
}
else
{
}
});
}
다시 말하지만, 위 Function호출은 onRequest가 아니라서, 검증 실패/성공에 대한 정보를 받을 수 없다.
'게임을 만들자 > 게임 서버(C#)' 카테고리의 다른 글
개인 PC 서버, 내부 IP, 외부 IP 확인하기.(IPTIME) (0) | 2021.03.29 |
---|---|
c#, SocketAsyncEventArgs 메모리 릭 현상 (0) | 2021.03.20 |
C#, 외부 프로세스 실행 (0) | 2021.03.16 |
c# 구글 인앱 iap 서버 검증 코드 (1) | 2021.01.08 |
윈도우 서버 TCPNoDelay, TcpAckFrequency 설정 (1) | 2020.12.23 |