[Spring boot] App Store Connect API를 이용하여 앱정보 가져오기

정명진·2022년 4월 6일
1

App Store API

Apple은 Appstore open API를 제공합니다. 그 중 이번에 사용해볼 API는 App Store Connect API입니다.
https://developer.apple.com/documentation/appstoreconnectapi

해당 API를 사용하면 Apps, App Metadata, and App Clips등 메타 데이터와 앱 출시를 자동화 시킬 수 있는 등 여러 기능을 활용 할 수 있습니다. 하지만 해당 API를 사용하기 위해서는 API Key, Private Key, 그리고 JWT가 필요합니다.

API Key, Private Key 발급은 아래 주소를 참고하시면 됩니다.
https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api

API Key, Private Key가 준비되면 이제 JWT를 보내 인가를 받은 뒤 App Store Connect API를 통해 여러 기능들을 사용할 수 있게 됩니다.
JWT를 보내기 위해 해당 라이브러리를 빌드해줘야 합니다.(다른 라이브러리도 사용 가능합니다.)

implementation 'com.nimbusds:nimbus-jose-jwt:3.10'

Spring boot는 마음에 드시는 버전을 사용해주시면 되고 Java 8버전을 꼭 사용해주셔야 합니다.

Private key 설정 과정에서 자바 8에서만 작동하는 코드가 존재하기 때문! 이번 글은 정확하지 않으므로 참고만 해주시길 바랍니다..!

구현 하기

App Store Connect API에 사용되는 JWT Header 조건입니다. alg는 ES256으로 설정하고 type은 JWT 마지막으로 kid는 해당 사이트를 참고하시어 따라하시면 됩니다. 아래는 JWT Header를 설정하는 코드입니다.

JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.ES256).keyID(keyId).type(JOSEObjectType.JWT).build();

Header를 설정하고 나면 Payload에 보낼 정보를 세팅해줘야 합니다. 다음은 Payload에 포함해야 하는 정보들 입니다. scope에 API 호출 정보를 넣어주면 됩니다.

만약 호출 URL이 GET https://api.appstoreconnect.apple.com/v1/apps/{id}/appInfos 라면 scope에 "GET /v1/apps/{appId}/appInfos"를 넣어주시면 됩니다. 구현 코드는 아래와 같습니다.

  
  JWTClaimsSet claimsSet = new JWTClaimsSet();
  Date now = new Date();
  claimsSet.setIssuer(issuer_Id);
  claimsSet.setIssueTime(now);
  claimsSet.setExpirationTime(new Date(now.getTime()+3600000));
  claimsSet.setAudience("appstoreconnect-v1");
  claimsSet.setClaim("scope", "GET /v1/apps/"+appId+"/appInfos");

Header와 Payload를 설정 했으므로 Signature만 작성해주면 JWT가 완성됩니다.

SignedJWT jwt = new SignedJWT(header,claimsSet);

try{
	ECPrivateKey ecPrivateKey = new ECPrivateKeyImpl(readPrivateKey(keyPath));
  JWSSigner jwsSigner = new ECDSASigner(ecPrivateKey.getS());
  jwt.sign(jwsSigner);

 }catch(InvalidKeyException e)
  {
     e.printStackTrace();
  }catch (JOSEException e)
  {
     e.printStackTrace();
  }

전체 코드는 다음과 같습니다.

 public String createJWT( )
 {
      JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.ES256).keyID(keyId).type(JOSEObjectType.JWT).build();

      JWTClaimsSet claimsSet = new JWTClaimsSet();
      Date now = new Date();
      claimsSet.setIssuer(issuer_Id);
      claimsSet.setIssueTime(now);
      claimsSet.setExpirationTime(new Date(now.getTime()+3600000));
      claimsSet.setAudience("appstoreconnect-v1");
      claimsSet.setClaim("scope", "GET /v1/apps/"+appId+"/appInfos");

      SignedJWT jwt = new SignedJWT(header,claimsSet);

      try{
          ECPrivateKey ecPrivateKey = new ECPrivateKeyImpl(readPrivateKey(keyPath));
          JWSSigner jwsSigner = new ECDSASigner(ecPrivateKey.getS());
          jwt.sign(jwsSigner);

        }catch(InvalidKeyException e)
        {
            e.printStackTrace();
        }catch (JOSEException e)
        {
            e.printStackTrace();
        }

        return jwt.serialize();

  }

JWT 작성이 끝났으므로 이제 JWT를 전달 하면 끝입니다. 다음은 JWT를 Send하는 코드입니다.

 public String getAppInfos(String jwt) throws MalformedURLException {
        System.out.println("jwt = " + jwt);
        String result = "";
        URL url = new URL("https://api.appstoreconnect.apple.com/v1/apps");

        try{
            HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();

            urlConnection.setRequestMethod("GET");
            urlConnection.setRequestProperty("Authorization", "Bearer "+jwt);

            BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));

            int responseCode = urlConnection.getResponseCode();
            System.out.println("responseCode = " + responseCode);

            String line = "";
            String res = "";
            while((line=br.readLine())!=null)
            {
                res+=line;
            }

            System.out.println("res = " + res);
            result = res;

        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

부족한 글이지만 읽어주셔서 감사합니다..

profile
개발자로 입사했지만 정체성을 잃어가는중... 다시 준비 시작이다..

0개의 댓글