php를 이용해 FCM으로 메시지를 보내기 위한 참고 문서 입니다.

 

[공식문서]

보내기 요청승인 : https://firebase.google.com/docs/cloud-messaging/auth-server?authuser=0 .

 

[참고문서]

php 문서 : https://link2me.tistory.com/1358 .

 

1. php 서버키 확인.

프로젝트에서, 톱니 바퀴 버튼을 눌러 프로젝트 설정 버튼을 눌러 주도록 합니다.

설정페이지에서 클라우드 메시징 탭을 눌러 서버키를 확인합니다.

그다음 안드로이드 앱에서 조사된 키값을 조사해 둡니다.

 

 

2. 보내기 요청하기.

공식 문서에 보내기 요청 승인 부분의 HTTP v1 보내기 요청 승인 부분이 있습니다. 이 부분을 확인하시면 됩니다.

그리고 메시지를 작성할 때는 여러 플랫폼의 메시지 맞춤설정을 꼭 확인해서 보내주세요. 헤더만 맞고 어떤 데이터를 보내든지 푸쉬는 성공으로 인식합니다. 그래서 앱이 갑자기 다운될 수 있습니다. php는 REST 부분을 보면됩니다.

애플과 안드로이드 공용으로 보내려면, 아래 형식에 맞춰 주면됩니다.

{
  "message":{
     "topic":"industry-tech",
     "notification":{
       "title": "$GOOG" up 1.43% on the day",
       "body":"GOOG gained 11.80 points to close at 835.67, up 1.43% on the day."
     },
     "android":{
       "ttl":"3600s",
       "notification"{
         "icon:stock_ticker_update"
         "color:#f45342"
       }
     },
     "apns": {
       "payload": {
         "aps": {
           "badge": "42"
         }
       }
     },
     "webpush":{
       "headers":{
         "TTL":"86400"
       }
     }
   }
 }

아래는 푸쉬를 보내기 위한 클래스 입니다.

class c_app
  {


    // 입력받은 값들을 저장해 주도록 합니다.
    public $serverKey;
    public $title;
    public $body;
    public $userToken;


    function __construct( $_serverKey, $_title, $_body, $_userToken )
    {

      // 입력 받은 값들을 저장해 주도록 합니다.
      $this->serverKey = $_serverKey;
      $this->title = $_title;
      $this->body = $_body;


      // 토크을 저장합니다.
      $this->userToken = $_userToken;

      // 토큰이 배열이 아닌 하나라면, 배열로 감싸줍니다.
      if( is_array( $this->userToken ) )
      {
        $this->userToken = array( $_userToken );
      }



    }


    function get_message()
    {


      // 하나의 데이터를 만들어 주도록 합니다.
      $messageArray['notification'] = array();
      $messageArray['notification']['title'] = $this->title;
      $messageArray['notification']['body'] = $this->body;
      $messageArray['registration_ids'] = [$this->userToken];
      return json_encode( $messageArray ) ;



    }



    function send()
    {

          $sendQuery = $this->get_message(  );

          // 보낼 데이터를 만들어 주기 위한 기본값을 설정합니다.
          $endline = "\r\n";
          $req = "";


          // 구글에서 요청한 필수 헤더를 추가합니다.
          $req = "POST /fcm/send HTTP/1.1" . $endline
          . "Host: fcm.googleapis.com" . $endline
          . "Content-Type: application/json" . $endline
          . "Authorization: key={$this->serverKey}" . $endline
          ;


          // POST 데이터를 보내기 위한 기본 헤더를 추가힙니다.
          $req .= "Content-Length: " . strlen($sendQuery) . $endline
          . "Connection: Close" . $endline
          ;

          // 헤더의 끝을 표시하는 빈 문자열을 설정합니다.
          $req .= $endline;

          // 내용을 추가해 보내어 줍니다.
          $req .= $sendQuery;
          ;

          // 해당 구글 서버와 연결을 시도합니다.
          $fsock = @fsockopen( "ssl://fcm.googleapis.com", 443 );

          // 구글 서버에 접속 실패한 경우.
          if( !$fsock )
          {
              echo "구글 서버에 접속 실패하였습니다.!";
              return;
          }

          // 데이터 보내기를 합니다.
          fwrite( $fsock, $req );


          // 데이터 받기를 위해 필요한 값들을 선언합니다.
          $headPassed = false;
          $TokenJson = "";


          // 데이터 받기가 완료될 때 까지 대기하면 데이터를 받아 출력합니다.
          while( !feof($fsock) )
          {

              // 한 줄 라인을 가지고 옵니다.
              $line = fgets($fsock, 128);

              // 아직 헤더는 아니지만, 헤더의 끝을 만난 경우, 헤더가 끝났음을 마킹하고, 종료합니다.
              if( $line == "\r\n" && !$headPassed )
              {
                  $headPassed = true;
                  continue;
              }

              // 헤더가 아닌 경우만, 값을 출력하도록 합니다.
              if( $headPassed )
              {
                  $TokenJson .= $line;
              }

          }

          // 연결을 닫아 주도록 합니다.
          fclose( $fsock );


          // 받은 결과를 반환해 주도록 합니다.
          return $TokenJson;


    }






  }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Posted by 창업닉군
,

이 문서를 따라해 보거나 적용해 보려면, 트위터 개발자 계정이 있어야 합니다. 트위터 계정 만드는 것은 너무 쉬운 일이므로, 여기서는 다루지 않습니다.

 

[공식 문서]

 

 

[주요 사이트]

트위터 개발자 사이트 : https://developer.twitter.com .

 

 

1. OAuth 로그인(sns 로그인). 을 위한 준비.

OAuth 로그인의 핵심은, 이 로그인 기능을 이용하는 사이트에서는 로그인 단계에서는 관여를 하지 않고, 유저와 sns 사이트에서 로그인처리를 하고, 결과값을 이용 사이트에 되돌려 줍니다.

그 과정에서 리디렉션 기능이 사용 되는데, 로그인할 주소를 유저에게 리디렉션해서 주면, sns 로그인 화면이 표시되고, 로그인을 무사히 완료 되면, 다시 리디렉션을 이용해, 사이트로 접속해 Get 타입으로 결과 코드를 돌려 줍니다. 마지막 결과 코드를 돌려 받을  url 주소는 트위터 서버에 등록할 필요가 있습니다. ip 주소는 안되며, 도메인으로 시작해야 하는데, 테스트를 위한 localhost도 허용을 합니다.

저는 로컬 서버에서 Apache 웹사이트를 등록해 사용하고 있습니다. 주소는 다음과 같이 정해 두었습니다.

로그인 요청 주소 : http://localhost/p999/twitter_OAuth/req_code.php .
토큰 교환 주소 : http://localhost/p999/twitter_OAuth/exchange_token.php .

 

2. 개발자 적용하기.

개발자 페이지에 접속 하면 다음과 같은 페이지를 만날 것 입니다. 아직 개발자 계정으로 전환이 되어 있지않다면, 오른쪽 상단에 Apply 레이블이 표시 될 것 입니다. 이 것이 표시 되지 않으면, 이미 개발자 계정으로 전환된 상태 이므로, 다음 항목으로 넘어 가면 되며, 아니라면, 저와 같이 개발자 적용을 하면 됩니다. Apply 버튼을 눌러 주도록 합니다.

그러면, 개발자 계정을 적용하는 화면이 나옵니다. 여기서 [ Apply for a developer account ] 눌러 개발자 계정 전환 신청단계로 들어 갑시다.

개발자 계정을 적용하면 바로 개발자 계정을 살 수 있을 지 모르겠지만, 저 같은 경우 유효한 전화번호를 입력하지 않아, 적용이 되지 않은 상태였습니다. 일단 전화번호를 등록하는 버튼을 눌러 전화번호를 등록하겠습니다.

다음 화면이 나오면 전화번호를 등록해 주도록 합니다.

그럼 아래와 같이 전화 번호 인증이 되고, 버튼 Continue 상태가 됩니다. Continue 버튼을 눌러 줍니다.

그 다음 질문은 개인의 개발자 계정인지, 조직의 개발자 계정인지 묻습니다. 상황에 따라 선택하면 되는 데 개인을 선택할 경우, 단 2개만 묻습니다. 이름과 사는 국가. 하지만, 계정 문자 @계정명 을 지정할 수 없이 이상한 값을 받습니다. 그러므로, 조직을 선택하도록 합니다.

아래와 같은 요령으로, 작성한 뒤 [Continue] 버튼을 눌러 주도록 합니다.

다음 페이지는 트위터 API를 이용해 어떤 프로젝트를 진행하려는 지 알려 달라고 합니다. 폼을 보시면 아시겠지만, 최소 300자 이상의 프로젝트 설명난이 있습니다. 한글도 재대로 못 쓰는 제가 영어로 300자 문장을 만들기란 여간 힘들지 않았습니다. 그래도 써야 한다니 써 드렸습니다. 구글 번역기와 함께. (영포자가 쓰기에는 매우 높은 벽이 였습니다.).

그 다음 서비스 약관을 허용하는지 묻는 것 입니다. Accept를 선택하고 스크롤을 쭉 내린 뒤 체크 박스가 활성화 돠면, 첫 항목만 체크 한뒤, [ Submit application ] 버튼을 눌러 주시면 됩니다.

마지막 작업으로 Email 인증 처리를 하는 작업을 합니다. 

그럼 아래와 같은 메일이 왔을 것 입니다. (오지 않았다면, 스팸으로 분류 되었는지 확인해 주세요.). [ Confirm your email ] 버튼을 눌러 이메일을 인증하도록 합니다.

 

3. 앱 만들기.

개발자 계정으로 전환을 완료했다면, 트위터를 API를 사용하려는 홈페이지, 모바일 앱, 등을 관리하는 일종의 프로젝트 개념의 앱을 만들어야 하며, 그곳에 우리가 무엇인가를 하기 위한 셋팅을 해야 합니다. 말로 설명하는 것 보다 일단 앱을 만들고 시작해 보도록 합시다.

처음 개발자 사이트에 접속하고 로그인 하면 다음과 같은 화면을 만날 수 있습니다. 여기서 Apps를 선택해 주도록 합니다.

그리고, 나온 화면에서, [ Create an app ] 버튼을 눌러 주도록 합니다.

여기 서 애플 개발자 계정을 적용하지 않았다면, 다음과 같은 창을 만날 수 있습니다.

우리는 이미 개발자 적용이 되어 있으므로, 뜨지 않을 것 입니다. 만약 뜬다면, 개발자 계정으로 전환이 완벽하게 이뤄 지지 않은 경우 이므로, 문서의 처음으로 돌아가 천천히 따라해 주시기 바랍니다. 정상적이라면 아래와 같은 폼이 뜨고, 값을 입력하고, Create 버튼을 눌러 주면됩니다.

대개의 값은 신경 쓸 필요 없지만, Enable Sign in with Twitter를 체크해, 나오는 Callback URL 은 유저가 트위터 로그인 후 결과를 돌려 받을 페이지를 입력해야 합니다. 우리는 문서 처음에 http://localhost/p999/twitter_OAuth/exchange_token.php 페이지를 결과를 받을 주소로 정했습니다. (여러 분은 다르게 정해도 상관은 없지만, 이 것으로 로그인 결과를 콜백 받는다는 사실은 알고 계셔야 합니다.).

저는 아래와 같이 작성했습니다. (트위터는 사용 목적등을 서술하는 것을 많이 요구해, 영포자인 저를 매우 힘들게 하는 군요.).

누르면, 붉은 색 3가지 항목이 나오는데, 전 뭔가 잘 못 입력했나하고, 살펴 보니, 우리가 앞서 승인했던 약관 내용을 상기 시키며, 3가지 목적으로 사용하면 안됨을 경고 하고 있었습니다. Create 버튼을 눌러 줍니다.

그럼 아래와 같이 앱이 생성된 정보가 표시됩니다.

여기서 Keys and tokens 텝을 눌러 api키와 Access token & access token secret 항목 아래, [Create] 버튼을 눌러 주도록 합니다.

다음에 계속 이어 쓰겠습니다.

 

 

 

 

 

 

 

 

 

Posted by 창업닉군
,

OAuth 2.0 을 이용해 Token을 얻어, 이  토큰으로 REST API를 통한 서버의 자원에 접근해 이용하는 방법은 이제 보편화된 방법 입니다. OAuth.2.0은 대부분 http/1.1을 이용하기 때문에, 대부분은 웹서버라면 다 이용할 수 있습니다.

요즘 많이 사용하게 되어 한 번쯤 정리해 둘 필요가 있을 것 같아 블로그에 글을 남겨 둡니다.

 

[공식 문서]

구글 OAuth 2.0 OverView : https://developers.google.com/identity/protocols/OAuth2?hl=ko .
구글 OAuth 2.0 for Web Server Applications : https://developers.google.com/identity/protocols/OAuth2WebServer?hl=ko .
유저 프로필 요청 : https://developers.google.com/gmail/api/v1/reference/users/getProfile .

 

[주요 사이트]

구글 클라우드 콘솔 : https://console.developers.google.com .

 

 

1. 구글 클라우드 콘솔에 프로젝트 및 Gamil 라이브러리 사용설정.

구글 로그인 기능은 자신의 구글 자원에 접근(구글 드라이브, 메일 등) 하기 위해 처음 로그인 하는 부분만 활용해 로그인 연동 처리를 하는 것 입니다. 그래서 최소 하나의 API를 사용하겠다 라고, 신청을 해야 그 다음 부터 이용이 가능합니다. 우리는 Gmail을 신청할 것 입니다. 구글 사용자라면 누구나, Gmail을 이용하고, 모든 유저가 활성화 되어 있기 때문입니다.

1) 프로젝트 생성.

목적별로 프로젝트를 생성한 후, 그 프로젝트에 Gmail을 생성합니다. 우리는 php google oauth 라는 프로젝트를 만들어 보도록 합시다. 처음 구글에 접속하면 다음과 같은 페이지가 표시될 것 입니다. 여기서 만들기를 눌러 프로젝트 만들기 창으로 접근합니다.

모든 사항을 입력한 후 만들기 버튼을 눌러 줍니다. (프로젝트 아이디를 변경해도 상관없습니다. 하지만 여기서는 하지 않았습니다.).

2) Gmail 라이브러리 추가.

그럼 php google oauth 프로젝트가 자동으로 선택되고 (하나 뿐 이므로), API 라이브러리를 추가하라는 메시지를 만납니다. 여기서 우리가 필요로 하는 Gmail API를 추가 해 보도록 합시다.

그럼.  API 라이브러리를 관리하는 화면이 나오는데, 왼쪽 메뉴의 이메일을 선택해 줍니다.

Gmail API를 선택합니다. (이메일은 Gmail API 하나만 있지만, 다른 카테고리는 여러개의, API가 표시될 수 있습니다.)

Gmail API 상세화면에서 사용설정 버튼을 눌러 사용할 수 있도록 해 줍니다.

 

2. OAuth 2.0 동의 화면 설정.

OAuth 로그인을 하는 과정에서 어떤 권한을 사용하는지등을 나타내는 화면이 표시 됩니다. 이 화면을 표시하기 위한 동의 화면을 구성하도록 하겠습니다.

아래와 같이 OAuth 동의 화면 탭을 선택해 나온 페이지에 애플리케이션 이름을 입력하고, 하단의 저장 버튼을 눌러 줍니다.

 

 

3. OAuth 2.0을 위한 클라이언트 ID 만들기.

구글 클라우드 API를 사용하기 위해서는 반드시 OAuth 2.0 방식을 통한 로그인 후 토큰을 획득해야 합니다. 이 과정에서 프로젝트를 구분하는 클라이언트 ID가 필요한데, 지금 만들어 보도록 하겠습니다.

아래와 같이 사용자 인증 정보 탭을 선택하고, OAuth 클라이언트 ID 생성을 눌러 줍니다.

클라이언트 성성 화면에서, 웹 애플리케이션을 선택하고, 이름은 적당히 입력해 주고, 생성 버튼을 눌러 줍니다.

그리고 나면 OAuth 클라이언트 정보를 확인시켜 주는 창이 뜰 텐데 일단 확인을 누르고 닫아 줍니다.

 

4. 리디렉션 주소 설정.

OAuth 2.0에서는 필연적으로 사용자가 계정의 아이디 및 패스워드를 입력하게 됩니다. 이런 과정을 통해 서버 운영자인 우리는, 사용자의 계정이나 패스워드를 저장할 필요성이 사라집니다. 사용자가 로그인을 무사히 완료하면, 서버쪽으로 성공 결과 및 인증코드를 돌려 주는 데, 그 주소를 리디렉션 주소라고 합니다. 이 값은 중요하므로, 구글에 등록된 주소로만 리디렉션 해 줍니다.

이 과정에서 도메인이 필요한 데, 우리는 테스트 과정이므로, localhost를 사용할 것 입니다. 실제 서비스를 한다면, 도메인을 이용해 구성해 주세요.

저는 웹페이지 중 http://localhost/p999/google_OAuth2/token_exchange.php 라는 주소를 설정할 것 입니다. 파일이름이 token_exchange.php 인 이유는 받은 인증 코드를 이용해 토큰으로 교환할 것 이기 때문입니다.

사용자 인증탭에 OAuth 2.0에 사용할 클라이언트 ID의 수정 버튼을 눌러 줍니다. 그리고, 나온 페이지에 승인된 리디렉션 URI에 리디렉션 주소를 입력하고( 꼭 엔터를 눌러 줍니다. ), 저장 버튼을 눌러 줍니다.

 

5. 인증을 구현을 위한 필요 정보 조사.

인증에 사용할 클라이언트 ID를 눌러 주도록 합니다.

페이지에서 각각의 정보를 수집합니다.

클라이언트 ID : 107557211118-lpfm0l4gs5ffpas8cbjn5bp71f2hbag5.apps.googleusercontent.com
크라이언트 보안 비밀 : NaJSvmGdShGg1kkXkGdvHp4T
승인된 리디렉션 URI : http://localhost/p999/google_OAuth2/token_exchange.php

 

6. 인증서 코드 요청 페이지 만들기.

저는 PC의 웹서버에, /p999/google_OAuth2/ 폴더 속에서 웹 파일들을 만들어 주고 있습니다. 코드를 요청하는 페이지 이름은 request_code.php 로 정하고 코딩하였습니다. 이 페이지에서 사용되는 내용은, Using OAuth 2.0 for Web Server Application (위에 링크 있습니다.)공식 문서에 HTTP/REST 부분의, 내용 입니다.

scope 의 권한 부분은 OAuth 2.0 API Scopes ( 링크 )에서 확인할 수 있는 데, 우리는 메일의 모든 권한을 요청할 것 입니다. 그 권한은 ( https://mail.google.com/ ) 입니다.

state 값은 리디렉션 시 그대로 다시 전달 받을 값 입니다. 로그인 과정에서 수많은 유저가 동시에 접속하면, 어떤 유저의 로그인인지 확인할 필요가 있습니다. 이때 사용하면 좋으며, 세션을 사용한다면, 사용하지 않아도 상관이 없습니다.

코드 자체는 로그인할 주소를 입력한 뒤, 리디렉션 해 주면 끝이 납니다. 아래는, request_code.php 페이지의 내용 입니다.

<?php



    // 주요 값들.
    $client_id = "107557211118-lpfm0l4gs5ffpas8cbjn5bp71f2hbag5.apps.googleusercontent.com";
    $client_secret = "NaJSvmGdShGg1kkXkGdvHp4T";
    $redirection_url = "http://localhost/p999/google_OAuth2/token_exchange.php";
    $scope = "https://mail.google.com/";

    // 인증 코드 주소 생성.
    $reqAddr = "https://accounts.google.com/o/oauth2/v2/auth"
    . "?client_id=" . $client_id
    . "&redirect_uri=" . urlencode( $redirection_url )
    . "&scope=" . urlencode( $scope )
    . "&state=OK"
    . "&access_type=offline"
    . "&include_granted_scopes=true"
    . "&response_type=code"
    ;

    // 지정한 주소로 리디렉션 시키기.
    header( 'Location: ' . $reqAddr );

?>

인증 코드를 무사히 넘겨 받았을 때 결과를 리디렉션 받아 토큰을 교환하는 페이지의 소스는 일단 아래와 같이 둡니다.

<?php


    print_r( $_GET );

자그럼 request_code.php 파일을 실행해 보도록 합시다. 정상적으로 리디렉션 된다면, 아래와 같은 페이지를 만날 수 있습니다.

일반적으로 구글 로그인 화면이므로, 쉽게 무엇을 넣어야 할 지 알 수 있을 것 입니다. 계속해서 로그인을 하도록 합니다.

로그인이 완료되면, 우리가 scope로 요청했던, Gmail의 모든 권한을 부여해달라는 페이지가 뜹니다. 우리는 현재 우리 계정으로 로그인하지만, 일반 유저가 우리 홈페이지에 접근할 때, 이 페이지를 보개 될 것이며, 자신의 Gmail을 이용할 것을 알게 됩니다. 일단 계속 해서 허용을 해 봅시다.

자 그럼 우리가 설정해 놓은 리디렉션 페이지로 GET을 통해 code, scope, state 값이 넘어 온 것을 확인할 수 있습니다.

 

7. 토큰 교환

우리가 리디렉션 받은 페이지에서, 받은 코드값을 사용해 토큰을 받아 오도록 하겠습니다. 이 부분은 서버에서 POST 통신을 해야 하므로, 약간의 난이도(?)가 있습니다.

POST 통신을 위해, fsocketopen 메소드를 상용하겠습니다. 공식 문서에 보면, 아래와 같이 데이터를 보내 도록 되어 있습니다. 우리도 그 값 그대로 보내면 되겠습니다. 공식 문서에는 endpoint가 https://www.googleapis.com/oauth2/v4/token 라고 되어 있습니다. 여기로 접속할 것 입니다.

POST /oauth2/v4/token HTTP/1.1
Host: www.googleapis.com
Content-Type: application/x-www-form-urlencoded

code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=your_client_id&
client_secret=your_client_secret&
redirect_uri=https://oauth2.example.com/code&
grant_type=authorization_code

아래는 토큰 교환을 하는 코드 입니다.

    // 주요 값들.
    $client_id = "107557211118-lpfm0l4gs5ffpas8cbjn5bp71f2hbag5.apps.googleusercontent.com";
    $client_secret = "NaJSvmGdShGg1kkXkGdvHp4T";
    $redirection_url = "http://localhost/p999/google_OAuth2/token_exchange.php";
    $scope = "https://mail.google.com/";



    // 보낼 쿼리 만들기.
    $sendQuery = ""
    . "code=" . $_GET['code']
    . "&client_id=" . $client_id
    . "&client_secret=" . $client_secret
    . "&redirect_uri=" . urlencode( $redirection_url )
    . "&grant_type=authorization_code"
    ;


    // 보낼 데이터를 만들어 주기 위한 기본값을 설정합니다.
    $endline = "\r\n";
    $req = "";
    
    
    // 구글에서 요청한 필수 헤더를 추가합니다.
    $req = "POST /oauth2/v4/token HTTP/1.1" . $endline
    . "Host: www.googleapis.com" . $endline
    . "Content-Type: application/x-www-form-urlencoded" . $endline
    ;

    
    // POST 데이터를 보내기 위한 기본 헤더를 추가힙니다.
    $req .= "Content-Length: " . strlen($sendQuery) . $endline
    . "Connection: Close" . $endline
    ;
    
    // 헤더의 끝을 표시하는 빈 문자열을 설정합니다.
    $req .= $endline;
    
    // 내용을 추가해 보내어 줍니다.
    $req .= $sendQuery;
    ;

    // 해당 구글 서버와 연결을 시도합니다.
    $fsock = @fsockopen( "ssl://www.googleapis.com", 443 );

    // 구글 서버에 접속 실패한 경우.
    if( !$fsock )
    {
        echo "구글 서버에 접속 실패하였습니다.!";
        exit;
    }
    
    // 데이터 보내기를 합니다.
    fwrite( $fsock, $req );


    // 데이터 받기를 위해 필요한 값들을 선언합니다.
    $headPassed = false;
    $TokenJson = "";


    // 데이터 받기가 완료될 때 까지 대기하면 데이터를 받아 출력합니다.
    while( !feof($fsock) )
    {

        // 한 줄 라인을 가지고 옵니다.
        $line = fgets($fsock, 128);

        // 아직 헤더는 아니지만, 헤더의 끝을 만난 경우, 헤더가 끝났음을 마킹하고, 종료합니다.
        if( $line == "\r\n" && !$headPassed )
        {
            $headPassed = true;
            continue;
        }

        // 헤더가 아닌 경우만, 값을 출력하도록 합니다.
        if( $headPassed )
        {
            $TokenJson .= $line;
        }

    }

    // 연결을 닫아 주도록 합니다.
    fclose( $fsock );

    // 받아 온 Token Json을 토큰 객체로 만들어 줍니다.
    $TokenObj = json_decode( $TokenJson );

    // 받아 온 토큰을 출랙해 줍니다.
    echo "받은 토큰은 아래와 같습니다.<br>";
    print_r( $TokenObj );
    echo "<br>---------------------------<br><br>";

 

8. 유저 프로필 요청.

gmail의 간단한 사용자의 정보를 가지고 옵니다. GET 타입이므로, 리디렉션 처리를 하면 간단하지만, 우리는 지금 학습단계이므로, fsockopen 을 이용해 구현해 보도록 하겠습니다.

gmail 의 유저 프로필 요청을 합니다. 공식 문서를 참고해 주시고, 아래 코드를 추가하면 됩니다. 아마 별다른 설명은 필요 없을 것이라 봅니다.

    // 보낼 문장을 반들어 줍니다.
    $userinfor_req = "GET /gmail/v1/users/me/profile HTTP/1.1" . $endline
    . "Authorization: Bearer " . $TokenObj->access_token . $endline
    . "Host: www.googleapis.com" . $endline
    . "Connection: Close" . $endline

    . $endline


    ;


    // 해당 구글 서버와 연결을 시도합니다.
    $fsock = @fsockopen( "ssl://www.googleapis.com", 443 );

    // 구글 서버에 접속 실패한 경우.
    if( !$fsock )
    {
        echo "구글 서버에 접속 실패하였습니다.!";
        exit;
    }
    
    // 데이터 보내기를 합니다.
    fwrite( $fsock, $userinfor_req );




    // 데이터 받기를 위해 필요한 값들을 선언합니다.
    $headPassed = false;
    $profileJson = "";


    while( !feof($fsock) )
    {

        // 한 줄 라인을 가지고 옵니다.
        $line = fgets($fsock, 128);

        // 아직 헤더는 아니지만, 헤더의 끝을 만난 경우, 헤더가 끝났음을 마킹하고, 종료합니다.
        if( $line == "\r\n" && !$headPassed )
        {
            $headPassed = true;
            continue;
        }

        // 헤더가 아닌 경우만, 값을 출력하도록 합니다.
        if( $headPassed )
        {
            $profileJson .= $line;
        }

    }


    // 연결을 닫아 주도록 합니다.
    fclose( $fsock );



    // 가지고 온 유저 데이터를 출력하도록 합니다.
    echo "확인한 유저 정보는 아래와 같습니다.<br>";
    echo $profileJson;
    

잘 실행되었다면, 아래와 같은 페이지를 볼수 있습니다.

 

 

마지막. 전체 코드 입니다.

<?php



    // 주요 값들.
    $client_id = "107557211118-lpfm0l4gs5ffpas8cbjn5bp71f2hbag5.apps.googleusercontent.com";
    $client_secret = "NaJSvmGdShGg1kkXkGdvHp4T";
    $redirection_url = "http://localhost/p999/google_OAuth2/token_exchange.php";
    $scope = "https://www.googleapis.com/auth/gmail.readonly";
    // $scope = "profile|email|openid";
    // $scope = urlencode( $scope );


    // 보낼 쿼리 만들기.
    $sendQuery = ""
    . "code=" . $_GET['code']
    . "&client_id=" . $client_id
    . "&client_secret=" . $client_secret
    . "&redirect_uri=" . urlencode( $redirection_url )
    . "&grant_type=authorization_code"
    ;


    // 보낼 데이터를 만들어 주기 위한 기본값을 설정합니다.
    $endline = "\r\n";
    $req = "";
    
    
    // 구글에서 요청한 필수 헤더를 추가합니다.
    $req = "POST /oauth2/v4/token HTTP/1.1" . $endline
    . "Host: www.googleapis.com" . $endline
    . "Content-Type: application/x-www-form-urlencoded" . $endline
    ;

    
    // POST 데이터를 보내기 위한 기본 헤더를 추가힙니다.
    $req .= "Content-Length: " . strlen($sendQuery) . $endline
    . "Connection: Close" . $endline
    ;
    
    // 헤더의 끝을 표시하는 빈 문자열을 설정합니다.
    $req .= $endline;
    
    // 내용을 추가해 보내어 줍니다.
    $req .= $sendQuery;
    ;

    // 해당 구글 서버와 연결을 시도합니다.
    $fsock = @fsockopen( "ssl://www.googleapis.com", 443 );

    // 구글 서버에 접속 실패한 경우.
    if( !$fsock )
    {
        echo "구글 서버에 접속 실패하였습니다.!";
        exit;
    }
    
    // 데이터 보내기를 합니다.
    fwrite( $fsock, $req );


    // 데이터 받기를 위해 필요한 값들을 선언합니다.
    $headPassed = false;
    $TokenJson = "";


    // 데이터 받기가 완료될 때 까지 대기하면 데이터를 받아 출력합니다.
    while( !feof($fsock) )
    {

        // 한 줄 라인을 가지고 옵니다.
        $line = fgets($fsock, 128);

        // 아직 헤더는 아니지만, 헤더의 끝을 만난 경우, 헤더가 끝났음을 마킹하고, 종료합니다.
        if( $line == "\r\n" && !$headPassed )
        {
            $headPassed = true;
            continue;
        }

        // 헤더가 아닌 경우만, 값을 출력하도록 합니다.
        if( $headPassed )
        {
            $TokenJson .= $line;
        }

    }

    // 연결을 닫아 주도록 합니다.
    fclose( $fsock );

    // 받아 온 Token Json을 토큰 객체로 만들어 줍니다.
    $TokenObj = json_decode( $TokenJson );

    // 받아 온 토큰을 출랙해 줍니다.
    echo "받은 토큰은 아래와 같습니다.<br>";
    print_r( $TokenObj );
    echo "<br>---------------------------<br><br>";





    // [-- 아래는 유저 프로필 가지고 오기 관련 --]

    
    // 보낼 문장을 반들어 줍니다.
    $userinfor_req = "GET /gmail/v1/users/me/profile HTTP/1.1" . $endline
    . "Authorization: Bearer " . $TokenObj->access_token . $endline
    . "Host: www.googleapis.com" . $endline
    . "Connection: Close" . $endline

    . $endline


    ;


    // 해당 구글 서버와 연결을 시도합니다.
    $fsock = @fsockopen( "ssl://www.googleapis.com", 443 );

    // 구글 서버에 접속 실패한 경우.
    if( !$fsock )
    {
        echo "구글 서버에 접속 실패하였습니다.!";
        exit;
    }
    
    // 데이터 보내기를 합니다.
    fwrite( $fsock, $userinfor_req );




    // 데이터 받기를 위해 필요한 값들을 선언합니다.
    $headPassed = false;
    $profileJson = "";


    while( !feof($fsock) )
    {

        // 한 줄 라인을 가지고 옵니다.
        $line = fgets($fsock, 128);

        // 아직 헤더는 아니지만, 헤더의 끝을 만난 경우, 헤더가 끝났음을 마킹하고, 종료합니다.
        if( $line == "\r\n" && !$headPassed )
        {
            $headPassed = true;
            continue;
        }

        // 헤더가 아닌 경우만, 값을 출력하도록 합니다.
        if( $headPassed )
        {
            $profileJson .= $line;
        }

    }


    // 연결을 닫아 주도록 합니다.
    fclose( $fsock );



    // 가지고 온 유저 데이터를 출력하도록 합니다.
    echo "확인한 유저 정보는 아래와 같습니다.<br>";
    echo $profileJson;
    

 

 

 

 

 

 

 

Posted by 창업닉군
,

iOS in-app purchase

iOS 2019. 4. 14. 20:17

iOS 결제 모듈을 사용하려면, Provisioning Profile이 있어야 합니다.

 

선행작업.

프로비저닝 프로파일 및 푸쉬 인증서 생성 : https://nicgoon.tistory.com/202 .

 

공식 문서.

StoreKit : https://developer.apple.com/documentation/storekit .
In-App Purchase : https://developer.apple.com/documentation/storekit/in-app_purchase .

 

참고 문서.

참고 1 : https://medium.com/@bestiosdevelope/implement-in-app-purchase-iap-in-ios-applications-swift-4d1649509599 .
참고 2 : https://devmjun.github.io/archive/purchase-1 . (자세히 설명되어 있음).
참고 영상 2 : https://www.youtube.com/watch?v=o3hJ0rY1NNw .

 

참고 사이트.

Itunes Connect : https://itunesconnect.apple.com/ .
Apple ID : https://appleid.apple.com/

 

1. 테스터 등록.

테스터로 사용할 계정을 위해 메일을 만들고 애플 아이디에 접속해 계정을 생성할 필요가 없습니다. 애플 아이디에 메일을 그냥 입력하면 되며, 심지어는 실제 사용되고 있는 애플 아이디를 넣는 다면, 이미 사용중 이라는 오류가 발생합니다.

먼저 아이툰즈 컨넥트에 접속합니다. 사용자 및 액세스를 클릭 합니다.

사용자 및 액세스 화면이 나오면, Sandbox > 테스터를 선택합니다. 그리고 나온 화면에 + 키를 눌러 주도록 합니다.

그럼 아래와 같이 테스터를 초대 하는 화면이 나올 텐데, 적당히 입력하고 초대 버튼을 눌러 줍니다. 주의 할 점은 실제 사용중인 애플 아이디를 넣으면 오류가 납니다. 자신이 사용하지 않고, 다른 사람도 사용중이 않을 메일 계정을 넣어 테스터 계정으로 사용하도록 합니다.

2. 앱 등록.

테스트 앱을 등록해 주도록 합니다. 저는 번들 아이디가  com.tistory.yeslife.inapp 인 inapp 이라는 앱을 등록할 것 입니다.

먼저 애플 스토어 컨넥트 에서 나의 앱을 선택합니다.

나의 앱 페이지가 나타나면, + 를 눌러 앱을 추가하도록 합니다.

신규앱을 등록하는 폼이 나타나면 아래와 같이 입력해 줍니다. 이름은 앱스토어 내에서 고유해야 합니다. 이미 사용중인 경우, 오류가 납니다. SKU는 앱의 상점을 위한 아이디인데, 적당히 고유한 값을 주면됩니다. 다 입력했으면 생성 버튼을 눌러 주도록 합니다. 

앱 이 생성되고, 앱이 표시 되면, 메뉴의 [앱 내 추가 기능]을 선택해 주도록 합니다.

그럼 앱내 구입 항목을 선택할 수 있는데 여기서 + 버튼 눌러 소모성 상품을 추가하도록 합니다.

앱 내 생성하려는 구입 선택항 목에서, 소모품을 선택하고 생성 버튼을 눌러 주도록 합니다.

그 다음 상품을 넣는 창에서 각각 적당한 값을 넣으면 되지만, 제품 ID는 각 상품마다 고유한 값을 잘 넣어 줘야 합니다.

그럼 등록되었지만, [메타 데이터 누락됨] 이라고 나올 수 있는데, 이 것은 우리가 심사를 위한 데이터를 넣지 않아 생기는 일이고, 테스트를 위한 것은 여기까지 입력하면됩니다.

저는 아래와 같이 3개의 소모성 아이템을 구매 했습니다.

 

앱은 현재 메타 데이터가 누락되어 있는데, 메타데이터를 설정된 뒤, 다음과 같이 앱내 구입으로 선택되어야 합니다.

 

3. 결제 옵저버.

swift에서 옵저버는 멤버 프로퍼티의 값이 변할 때, 알려주는 일종의 콜백 입니다. 결제가 시작될 때, 거절 될때등, 결제 상태를 확인해 이를 처리할 옵저버가 필요합니다.

옵저버는 SKPaymentTransactionObserver 프로토콜(c#의 인터페이스)을 상속해 구현해야 합니다. 코드는 대략 아래와 같습니다.


class StoreObserver: NSObject, SKPaymentTransactionObserver {
    
    
    // 멤버 변수들.
    var purchased = [SKPaymentTransaction]();
    
    
    
    
    override init() {
        super.init()
        
        // 생성자를 위한 초기화 메소드.
        
    }
    
    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        
        
        
        for transaction in transactions
        {
            
            switch transaction.transactionState
            {
                
            // 결제가 진행 중인 경우.
            case .purchasing :
                print("결제가 진행되고 있습니다.");
                break;
                
            // 결제 창을 띄우는 데 실패했습니다.
            case .deferred:
                print("아이폰의 잠기는 등의 이유로 결제 창을 띄우지 못했습니다.");
                SKPaymentQueue.default().finishTransaction( transaction );
                break;
                
            // 결제를 성공한 경우.
            case .purchased:
                print("결제를 성공하였습니다.");
                handlePurchased( transaction );
                SKPaymentQueue.default().finishTransaction( transaction );
                break;
                
            // 결제를 실패한 경우.
            case .failed:
                print("결제를 실패하였습니다.");
                SKPaymentQueue.default().finishTransaction( transaction );
                break;
                
            // 결제 검증을 하였습니다.
            case .restored:
                print("상품 검증을 하였습니다.");
                SKPaymentQueue.default().finishTransaction( transaction );
                break;
                
            default :
                print("알수 없는 오류를 만났습니다.")
                SKPaymentQueue.default().finishTransaction( transaction );
                break;
                
            }
            
        }
        
        
        
    }
    
    
    func handlePurchased( _ transaction : SKPaymentTransaction ) {
        
        purchased.append(transaction)


        SKPaymentQueue.default().restoreCompletedTransactions();

        
        print( "영수증 주소 : \(Bundle.main.appStoreReceiptURL)" );
        
        
        let receiptData = NSData( contentsOf: Bundle.main.appStoreReceiptURL! );
        print(receiptData)
        
        let receiptString = receiptData!.base64EncodedString(options: NSData.Base64EncodingOptions());
        
        
        print ( "구매 성공 트랜젝션 아이디 : \(transaction.transactionIdentifier!)" );
        print ( "상품 아이디 : \(transaction.payment.productIdentifier)" );
        print ( "구매 영수증 : \(receiptString)" );
     
        // 결제를 마무리 하도록 합니다.
        SKPaymentQueue.default().finishTransaction( transaction );


        
    }
    
    
}

대게의 코드는 공식 문서를 통해 쉽게 알 수 있지만 중요한 부분은 영수증을 받는 부분 입니다. 이 부분은 크게 언급이 공식 문서에 없어, 애를 먹었는데, Bundle.main.appStoreReceiptURL 값에 영수증 파일명이 들어 있습니다. 이것을 NSData를 이용해 값을 가지고 오고, Base64  인코딩을 해 사용하면됩니다.

결제를 할 때마다 transaction 이 생성되는 데, 실패하던, 걸절하던, 꼭 finishTransaction 처리를 해 줘야 합니다. 그렇지 않으면, 껐다 키면 다시 트랜젝션값들이 계속 추가 됩니다. 이 것은 앱이 비정상적으로 종료되었을 때도, 처리를 하기 위함으로 보입니다.

 

4. 상품 리스트 가지고 오기 및 구매 처리.

결제는 대체로 뷰에서 일어 나므로, viewController에서 처리하는 것이 좋습니다.

처음 상품 리스트를 가지고 오는 부분이 있습니다. 이때, 위에서 등록한 상품의 ID들을 적어 주면됩니다. 코드는 아래와 같습니다.

import UIKit
import StoreKit

class ViewController: UIViewController, SKProductsRequestDelegate {
    
    
    // 멤버 변수들.
    let iapObserver = StoreObserver()
    var productRequest : SKProductsRequest?
    
    // 입력 받은 상풉 정보들을 저장하는 변수들.
    var validProductArray = [SKProduct]();
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        // 페이먼트 관련 설정을 해 주도록 합니다.
        SKPaymentQueue.default().add(iapObserver)
        
        // 상품 정보를 가지고 오는 리퀘스트를 설정하도록 합니다.
        let pIDs = Set(["gold500","gold100"]);
        productRequest = SKProductsRequest( productIdentifiers: pIDs );
        productRequest!.delegate = self;
        
        // 앱스토어 상품 정보를 요청합니다.
        productRequest!.start();
        
        
    }

    
    // 구매 진행 가능 여부를 확인합니다.
    @IBAction func checkPaymentAble(_ sender: Any) {
        
        if( SKPaymentQueue.canMakePayments() )
        {
            print("결제 요청이 가능합니다.");
        }else
        {
            print("결제 요청이 불가능합니다.");
        }
        
    }
    
    @IBAction func tryPaymentQueue(_ sender: Any) {
        
        print( "골드 100개 결제 할거에요." )
        
        let payment = SKMutablePayment( product: validProductArray[0] );
        
        SKPaymentQueue.default().add(payment)
        
        print( "변화가 있나요?" )
        
    }
    
    
    // 상품 정보를 받은 후 처리 메소드 입니다.
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        
        
        print("상품갯수 \(response.products.count)")
        print("확인하지 못한 상품 갯수 \(response.invalidProductIdentifiers.count)")
        
        
        for product in response.products
        {
            print("이름:\(product.localizedTitle)\n가격:\(product.price)")
            validProductArray.append(product)
        }
        
        
        
        
        
    }
    
    
}

 

이 것만으로 결제는 코드는 끝 입니다. 실제 결제를 해 볼때는 아이폰에서, 애플 아이디를 로그아웃하는 것이 좋습니다.

 

5. 검증 서버. (php)

원래 node js만 구현하려고 했으나, 잘 되지 않아 php로 먼저 결제 서버를 구현하였고, 그것을 바탕으로 node.js를 구현하였습니다.

애플 결제는 서버로 검증하는 것이 100배 쉽습니다. 그냥 POST 로 영수증을 Json형태로 보내면 끝 입니다. 받은 값에, 결제 날자 구매 수량 등이 있으므로, 이 것을 비교해 앱의 정보를 확인하면 되겠습니다. 아래는 샌드박스 주소로 되어 있습니다. 실제 결제 주소는 https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW2 에서 확인할 수 있습니다.

                // 영수증.
                $receipt = "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";
                $addr = "https://sandbox.itunes.apple.com/verifyReceipt";
                $host = "sandbox.itunes.apple.com";
                $path = "/verifyReceipt";

                // 보낼 json 데이터를 만들어 주도록 합니다.
                $bodyJson = '{ "receipt-data" : "' . $receipt . '" }';


                // 요청을 위한 주소를 만들어 주도록 합니다.
                $endline = "\r\n";
                $req = "";


                // 주소 및 타입 관련 헤더를 추가합니다.
                $req = "POST {$path} HTTP/1.1" . $endline
                . "Host: {$host}" . $endline
                . "Content-Type: application/json" . $endline
                ;

                // 데이터를 보내기 위한 기본 헤더를 추가합니다.
                $req .= "Content-Length: " . strlen($bodyJson) . $endline
                . "Connection: Close" . $endline
                ;

                // 헤더의 끝을 표시하는 빈 문자열을 설정합니다.
                $req .= $endline;
                
                // 보낼 내용을 추가합니다.
                $req .= $bodyJson;
                ;




                // 영수증 서버에 접속을 시도합니다.
                $fsock = @fsockopen( "ssl://{$host}", 443 );

                // 영수증 서버에 접속을 실패한 경우 처리 입니다.
                if( !$fsock )
                {
                    echo "구글 서버에 접속 실패하였습니다.!";
                    exit;
                }
                
                // 데이터를 보내도록 합니다.
                fwrite( $fsock, $req );


                // 데이터 받기를 위해 필요한 값들을 선언합니다.
                $headPassed = false;
                $resDatas = "";


                // 데이터 받기가 완료될 때 까지 대기하면 데이터를 받아 출력합니다.
                while( !feof($fsock) )
                {

                    // 한 줄 라인을 가지고 옵니다.
                    $line = fgets($fsock, 128);

                    // 아직 헤더는 아니지만, 헤더의 끝을 만난 경우, 헤더가 끝났음을 마킹하고, 종료합니다.
                    if( $line == "\r\n" && !$headPassed )
                    {
                        $headPassed = true;
                        continue;
                    }

                    // 헤더가 아닌 경우만, 값을 출력하도록 합니다.
                    if( $headPassed )
                    {
                        $resDatas .= $line;
                    }

                }

                // 연결을 닫아 주도록 합니다.
                fclose( $fsock );



                // JSON 데이터를 표시하도록 합니다.
                echo "받은 데이터 : " . $resDatas;


                echo "보낸 데이터 :" . $req;

 

6. 검증서버 (node.js)

알고리즘 및 구현은 php와 동일하므로, 설명을 생략하겠습니다.

const request_json = require("request-json");



// 인앱 서버 주소.
const serverAddr = "https://sandbox.itunes.apple.com/verifyReceipt";
// const serverAddr = "https://buy.itunes.apple.com/verifyReceipt";

// 구매 영수증 주소.
const receipt = "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";



const host = "https://sandbox.itunes.apple.com";
const path = "/verifyReceipt";



const receiptJson = {
    'receipt-data': receipt
};



var client = request_json.createClient( host );
client.post( path, receiptJson, function( err, res, body ){

    if( err )
    {
        console.error( err );
        return;
    }

    console.log( JSON.stringify( body ) );

});

 

7. Restore

앱을 지우거나 했을 때, 기존의 결제 내역을 복구하는 기능입니다. 저 같은 경우 서버에서, 결제 검증을 하고, 데이터를 가지고 있을 것 이므로, 필요하지 않아 구현하지 않았습니다.

 

 

 

Posted by 창업닉군
,