Skip to content

Commit

Permalink
Merge pull request #203 from Genti2024/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
LeeJae-H authored Nov 30, 2024
2 parents f33509d + 704de14 commit 2b0b454
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 1 deletion.
8 changes: 8 additions & 0 deletions .github/workflows/cicd-ec2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ jobs:
echo "${{ secrets.APPLICATION_STAGING }}" > ./application-staging.yml
shell: bash

- name: Create Google key.json file
if: contains(github.ref, 'staging') || contains(github.ref, 'main-test')
run: |
cd ./genti-api/src/main/resources
mkdir -p ./google
echo "${{ secrets.GOOGLE_ACCOUNT_KEY }}" > ./google/key.json
shell: bash

- name: make apple private key
run: |
cd ./genti-external/src/main
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ logs/
firebase-genti.json

*.pem

/genti-api/src/main/resources/jsonkey/
AuthKey_ZRZMQQX883.p8
/genti-api/src/main/resources/static/swagger.json
update-github-secret.sh
4 changes: 4 additions & 0 deletions genti-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ dependencies {
// redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

compileOnly("com.google.api-client:google-api-client:1.33.0")
implementation 'com.google.auth:google-auth-library-oauth2-http:1.6.0'
compileOnly('com.google.apis:google-api-services-androidpublisher:v2-rev22-1.21.0')

// test && test h2
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.gt.genti.purchase.controller;

import com.gt.genti.purchase.dto.request.PurchaseRequestDto;
import com.gt.genti.purchase.service.InAppPurchaseService;
import com.gt.genti.response.GentiResponse;
import com.gt.genti.user.model.AuthUser;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/v1/users/in-app-purchases")
@RequiredArgsConstructor
public class UserPurchaseController {

private final InAppPurchaseService inAppPurchaseService;

@PostMapping("/google/receipt-validation")
public ResponseEntity<GentiResponse.ApiResult<Boolean>> validateReceipt(
@AuthUser Long userId,
PurchaseRequestDto purchaseRequestDto) {
return GentiResponse.success(inAppPurchaseService.validateReceipt(userId, purchaseRequestDto));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.gt.genti.purchase.dto.request;

import lombok.Getter;

@Getter
public class PurchaseRequestDto {
private String packageName; //인앱 상품이 판매된 애플리케이션의 패키지 이름
private String productId; //인앱 상품 SKU
private String purchaseToken; //안드로이드에서 받아올 구매 토큰
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.gt.genti.purchase.service;

import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.androidpublisher.AndroidPublisher;
import com.google.api.services.androidpublisher.AndroidPublisherScopes;
import com.google.api.services.androidpublisher.model.ProductPurchase;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import com.gt.genti.purchase.dto.request.PurchaseRequestDto;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;

@Service
public class InAppPurchaseService {

@Value("{google-account.file-path}")
private String googleAccountFilePath;

@Value("{google-application.package-name}")
private String googleApplicationPackageName;

public Boolean validateReceipt(Long userId, PurchaseRequestDto purchaseRequestDto) {

try {
// ================= Google Credential 생성 =================
JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();

InputStream inputStream = new ClassPathResource(googleAccountFilePath).getInputStream();
GoogleCredentials credentials = GoogleCredentials
.fromStream(inputStream)
.createScoped(AndroidPublisherScopes.ANDROIDPUBLISHER);

// ======================== API 호출 ========================
AndroidPublisher.Builder builder = new AndroidPublisher.Builder(httpTransport, JSON_FACTORY, new HttpCredentialsAdapter(credentials));
AndroidPublisher publisher = builder.setApplicationName(googleApplicationPackageName).build();
AndroidPublisher.Purchases.Products.Get gas = publisher.purchases()
.products()
.get(
"packageName",
"productId",
"purchaseToken");
ProductPurchase purchase = gas.execute();

AndroidPublisher.Purchases.Products.Get get = publisher.purchases().products()
.get(purchaseRequestDto.getPackageName(), purchaseRequestDto.getProductId(), purchaseRequestDto.getPurchaseToken()); //inapp 아이템의 구매 및 소모 상태 확인
ProductPurchase productPurchase = get.execute(); //검증 결과
System.out.println(productPurchase.toPrettyString());

// 인앱 상품의 소비 상태. 0 아직 소비 안됨(Yet to be consumed) / 1 소비됨(Consumed)
Integer consumptionState = productPurchase.getConsumptionState();

// 개발자가 지정한 임의 문자열 정보
String developerPayload = productPurchase.getDeveloperPayload();

// 구매 상태. 0 구매완료 / 1 취소됨
Integer purchaseState = productPurchase.getPurchaseState();
if(purchaseState == 1){
return false;
}

// 상품이 구매된 시각. 타임스탬프 형태
Long purchaseTimeMillis = productPurchase.getPurchaseTimeMillis();

return true;
} catch (IOException | GeneralSecurityException e) {
return false;
}
}
}

0 comments on commit 2b0b454

Please sign in to comment.