필요한 일이 생겨서 간단히 프로토타이핑한 구글 수익보고서 다운로드하는 java프로그램
- 2024-07-22 간단히 업데이트 진행
필요 라이브러리(기타 추가 import가 필요할 수도 있음)
<!-- https://mvnrepository.com/artifact/com.google.apis/google-api-services-storage -->
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-storage</artifactId>
<version>v1-rev20240706-2.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.api-client/google-api-client -->
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
<version>2.6.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.http-client/google-http-client-jackson2 -->
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client-jackson2</artifactId>
<version>1.44.2</version>
</dependency>
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
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.jackson2.JacksonFactory;
import com.google.api.services.storage.Storage;
import com.google.api.services.storage.StorageScopes;
import com.google.api.services.storage.model.Objects;
import com.google.api.services.storage.model.StorageObject;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
/**
* 구글 재무보고서 중에서 '수익'보고서를 다운로드하는 샘플 프로그램
* - 참고: https://support.google.com/googleplay/android-developer/answer/6135870#zippy=%2C%EC%88%98%EC%9D%B5
*
* - 추가 참고사항
* 1) 수익 보고서를 사용하여 판매 대금과 거래 내역를 파악할 수 있습니다.
* 보고서의 각 행은 거래 유형(예: 고객에게 대금을 청구하거나 Google에 수수료를 지급하는 시기)과 함께 원래 금액 및 변환된 금액을 나타냅니다.
* 2) 수익 보고서에는 이전 달에 발생한 인보이스가 포함됩니다. 최소 지급액에 도달하면 인보이스를 받게 됩니다. 수익 보고서를 사용할 수 있게 되면 몇 주 후에판매 대금을 수령할 수 있습니다.
* 3) 수익 보고서는 한 달에 한 번 생성되며 일반적으로 다음 달 5일에 제공됩니다.
* 경우에 따라 Google에서 계산 오류를 바로잡기 위해 수익을 조정할 수 있습니다.
* 이 경우 Google에서 문제에 관해 알려 드리며 개발자 기록용으로 조정된 거래만 포함되어 있는 추가 수입 파일을 생성합니다.
* 4) Google은 유럽 경제 지역(EEA)의 사용자에게 판매되는 상품의 등록된 판매자이므로, 영향을 받는 국가에서 이루어진 판매가 주문당 한 줄씩 표시됩니다.
* 또한 거래 유형은 '청구'로 표시됩니다. 다른 국가에서 이루어진 판매에는 'Google 수수료' 거래 유형도 포함됩니다.
* 5) 수익 보고서에는 지불 거절이 포함되지 않습니다
*
* @author
*/
@Slf4j
public class GoogleEarningsReportDownloadTest {
//다운로드 저장 디렉토리 경로
final static String saveDirStr = ".temp_google_earnings";
//Google Cloud Platform에서 생성한 Service Account의 키 파일. 해당 키 파일의 유저(이메일주소) Google Play Console에 초대 후 권한 지급이 되어 있어야 함
//일괄 보고서에 액세스하려면 '앱 정보 보기' 권한이 '전체'로 설정되어 있어야 합니다.
//재무 보고서를 다운로드하려면 '재무 데이터 보기' 권한이 '전체'로 설정되어 있어야 합니다.
//참고: https://support.google.com/googleplay/android-developer/answer/6135870#zippy=%2C%EC%88%98%EC%9D%B5%2C%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EB%B0%8F-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B3%84%EC%A0%95%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-%EB%B3%B4%EA%B3%A0%EC%84%9C-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C%ED%95%98%EA%B8%B0
final static String SERVICE_ACCOUNT_KEY_FILE_PATH = "구글 Service Account 인증 파일 경로.json";
//버킷
final static String BUCKET_NAME = "pubsite_prod_블라블라"; //버킷명(Google Play Console -> 보고서 다운로드 -> 재무 -> Cloud Storage URI복사를 통해서 확인 가능)
//다운로드할 수익보고서 object name
final static String DOWNLOAD_OBJECT_NAME = "earnings/earnings_202406_블라블라.zip"; //object name 샘플. earnings/earnings_202110_블라블라숫자-숫자.zip
final static String applicationName = "test-app-0.0.1";
//인증 스콥
final static String GoogleCredential_SCOPE = StorageScopes.CLOUD_PLATFORM_READ_ONLY;
public static void main(String[] args) throws Exception {
LocalDateTime start = LocalDateTime.now();
log.info("\n\n==== Start: {} ====", start);
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
Credential credential = authorizeWithServiceAccount(httpTransport, jsonFactory);
Storage storage = new Storage.Builder(httpTransport, jsonFactory, credential).setApplicationName(applicationName).build();
//테스트로 prefixName 기반으로 리스트 조회
final String namePreifx = "earnings/earnings_2024"; //2024년 prefix로 전체 조회
listObject(storage, BUCKET_NAME, namePreifx).forEach(storageObject -> {
log.info("버킷의 {} prefix storageObject:{}", namePreifx, storageObject);
});
//버킷의 모든 object를 조회하는 경우: 오버헤드 주희
// storage.objects().list(BUCKET_NAME).execute().getItems().forEach(storageObject -> {
// log.info("storageObject:{}", storageObject);
// });
final Storage.Objects.Get downloartTargetObject = storage.objects().get(BUCKET_NAME, DOWNLOAD_OBJECT_NAME);
final StorageObject storageObject = downloartTargetObject.execute();
log.info("storageObject 정보 ==>\n\t{}", storageObject.toPrettyString());
FileUtils.forceMkdir(new File(saveDirStr)); //디렉토리 생성
log.info("저장 디렉토리:{}", saveDirStr);
String objectNameSplitArray[] = StringUtils.split(DOWNLOAD_OBJECT_NAME, "/");
String saveFileFullPathStr = saveDirStr + File.separator + objectNameSplitArray[1];
log.info("저장 파일 full path:{}", saveFileFullPathStr);
final File saveFile = new File(saveFileFullPathStr);
FileUtils.deleteQuietly(saveFile); //기존 다운로드된 파일이 존재할 수 있어서 선 삭제 진행
FileOutputStream out = new FileOutputStream(saveFile);
downloartTargetObject.getMediaHttpDownloader().setDirectDownloadEnabled(true); //true 설정 필요
downloartTargetObject.executeMediaAndDownloadTo(out);
LocalDateTime end = LocalDateTime.now();
log.info("\n\n==== End: {} ==== 소요시간: {}(second)", end, Duration.between(start, end).getSeconds());
}
/**
* 서비스 account 인증
*
* @param httpTransport
* @param jsonFactory
* @return
* @throws IOException
*/
private static Credential authorizeWithServiceAccount(HttpTransport httpTransport, JsonFactory jsonFactory) throws IOException {
final String privateKey = FileUtils.readFileToString(new File(SERVICE_ACCOUNT_KEY_FILE_PATH), "UTF-8");
InputStream inputStream = new ByteArrayInputStream(privateKey.getBytes(StandardCharsets.UTF_8));
GoogleCredential credential = GoogleCredential.fromStream(inputStream, httpTransport, jsonFactory);
credential = credential.createScoped(Collections.singleton(GoogleCredential_SCOPE));
return credential;
}
/**
* Storage의 object들 중에서, name prefix로 필터링하여 가져옴
*
* @param storage
* @param bucketName
* @param namePrefix
* @return
* @throws IOException
*/
public static Iterable<StorageObject> listObject(Storage storage, String bucketName, String namePrefix) throws IOException {
List<List<StorageObject>> pagedList = Lists.newArrayList();
Storage.Objects.List listObjects = storage.objects().list(bucketName).setPrefix(namePrefix); //name prefix로 필터링해서 가져오도록 설정 -> 속도 개선됨
Objects objects;
do {
objects = listObjects.execute();
List<StorageObject> items = objects.getItems();
if (items != null) {
pagedList.add(objects.getItems());
}
listObjects.setPageToken(objects.getNextPageToken());
} while (objects.getNextPageToken() != null);
return Iterables.concat(pagedList);
}
}
'JAVA > Java 일반' 카테고리의 다른 글
java stream을 이용한 정렬 (0) | 2022.01.10 |
---|---|
대용량 처리시 부하분산을 위한 데이터 분할 처리(간략) (0) | 2021.12.10 |
구글 수익 레포트 CSV파일 파싱 프로그램(Parsing Google earnings report csv file) (0) | 2021.11.18 |
java timeZone변경(PST->KST 또는 PDT->KST) 샘플 소스 (0) | 2021.11.16 |
CSV파일을 읽어서 파싱하여 DB(Mysql)에 저장하는 프로그램 샘플 (0) | 2021.11.12 |