'서버'에 해당되는 글 2건

  1. 2019.04.14 iOS in-app purchase 7
  2. 2019.04.14 node.js를 이용한 iOS Push 구현하기 (requests to APNs)

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 창업닉군
,

 Push Notification 서버를 기본 REST API 형태로 구현하려고 했으나, 애플 공식 문서를 보는 순간, 아, 그냥 node js의 모듈을 이용해야 겠다는 생각이 들었습니다. iOS 서버를 따로 만들어 구동 방식을 이해하고, Fire base 에 통합할 예정이라, 별 고민 없이 node의 모듈을 이용하게 되었습니다.

node js의 apn 이라는 모듈을 이용할 예정이며, 선행작업으로는 프로비저닝 프로파일 및 푸쉬 인증서, 클라이언트 푸쉬구현이 되어 있어야 합니다.

 

선행 작업.

프로비저닝 프로파일 및 푸쉬 인증서 생성 : https://nicgoon.tistory.com/202 (필수)
클라이언트 Push 구현 : https://nicgoon.tistory.com/203 (필수)

 

공식 문서.

푸쉬서버 설치 : https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server .
APNs로 알림요청 : https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns .
푸쉬 메시지 구조 : https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification .
node-apn 문서 : https://www.npmjs.com/package/apn .

 

참고 문서.

참고 1. https://g-y-e-o-m.tistory.com/72 .
참고 2. http://blog.naver.com/PostView.nhn?blogId=jipmouse&logNo=220829918858 .

 

1. apn 설치.

npm i apn --save

 

2. 인증서 복사.

위의 선행 작업 문서 중, [프로비저닝 프로파일 및 푸쉬 인증서 생성] 링크를 눌러 나타나는 문서에, 푸쉬 인증서 .pem 을 만드는 방법이 있습니다. 여기서 푸쉬 인증서 pem 파일과, 푸쉬 인증서의 key pem 파일(암호를 풀어 놓은 것) 을 만들어 이것을 복사해 둡니다.

저는 인증서 파일의 이름은 [yeslife_ap_cer.pem] 이며, 키 파일의 이름은 [yeslife_ap_key.unencrypted.pem] 입니다.

 

3. apn 프로바이더 만들기.

옵션을 만들어 프로바이더를 생성하는 구조이며, 옵션은 토큰을 넣는 방법, 인증서를 넣는 방법 2가지가 있습니다. 저는 인증서를 넣는 방법을 선택했습니다. 아래 2가지 방법 중 하나로 옵션을 만들면 되겠습니다.

1) 푸쉬 인증서로 보내기.

gateway는 개발단계에서는  gateway.sandbox.push.apple.com 이며, 실제 서비스 할 때는 gateway.push.apple.com 입니다. 해당 내용은 애플 개발자 공식문서 (링크) 에 나와 있습니다.

var option = {
    gateway:"gateway.sandbox.push.apple.com",
    cert:'yeslife_ap_cer.pem',
    key:'yeslife_ap_key.unencrypted.pem'
};

let apn_provider = new apn.Provider( option );

 

2) 토큰 으로 보내기.

먼저, 애플 개발자 사이트의 Certificates, Identifiers & Profiles 부분페이지에서 토큰을 생성하고 다운로드 받도록 하겠습니다. Certificates, Identifiers & Profiles 페이지로 에서 Keys, All 을 선택하고, + 버튼을 눌러 주도록 합니다.

나온 페이지에서, Name 과 APNs 항목을 클릭하고, Continue 버튼을 눌러 주도록 합니다.

키를 만들기 전 입력사항이 맞는지 확인하는 페이지에서 Confirm을 눌러 생성하도록 합니다.

키가 만들어 졌습니다. Key ID는 중요하므로, 꼭 적어 두도록 합니다. 그리고 키는 한 번만 다운로드 가능합니다. 옵션은 아래와 같은 요령으로 만들어 주면되겠습니다. Production은 개발인지 실제 서비스하는 지 여부 입니다. (호출 주소가 틀림).

var option = {
    token : {
        key : '.p8 파일 경로',
        keyId:'키를 만들 때 표시된 아이디',
        teamId: "팀명"
    },
    production: false
};

 

4. 보낼 내용 및 장치 토큰 저장해 두기.

앱이 켜지면,  APNs 에 앱이 등록되는 데, 성공하면, 토큰을 받아 옵니다. 이 값을 deviceToken 값에 저장해 줍니다.

보낼 내용은 apn.Notification 객체에 저장이 되는 데, 이 부분은 공식 문서의 payload 부분을 참고 하면 되겠습니다.

// 앱에서 APNs에 앱을 등록하고, 얻은 값.
let deviceToken = 'A89B3B5731AD3821D0AA316CFBC6E54E750C216ED6308112CE437BAC3730005F';



// 보낼 데이터를 만들어 줍니다.
var note = new apn.Notification();

// 보내기 실패할 경우 언제까지 재시돌 할 것인지 여부.
note.expiry = Math.floor( Date.now() / 1000 ) + 3600;

// 앱의 아이콘에 표시될 숫자. ( 우리가 흔히 몇 개의 메시지가 있다고 인식하는 )
note.badge = 3;

// 메시지가 도착했을 때 나는 소리.
note.sound = "ping.aiff";

// 메시지 내용.
note.alert = "왓쇼이 왓쇼이";

// 누가 보냈는지 여부.
note.payload = {"messageFrom" : "메시지"};

// ios app 번들 명.
note.topic = "com.tistory.yeslife.hello";

 

 

5. 메시지 보내기.

apn.provider의 send 메소드를 이용해 보내면 됩니다.

// 실제 메시지를 보내도록 합니다.
apn_provider.send(note, deviceToken).then( function( result ){

    console.log("결과 : " + result);
    console.log( result );

}).catch( function (err){

    throw( err );
    
});

 

별다른 문제가 없다면, 메시지가 성공적으로 도착합니다.

 

 

 

 

 

 

 

'Node.js' 카테고리의 다른 글

node.js nodemailer gmail  (0) 2019.05.24
node js OAuth 2.0 Login  (0) 2019.04.16
Android 결제 서버 개발  (0) 2019.04.09
node.js FCM (Push Notification) 서버 환경 구축하기  (1) 2019.04.06
express 사용시 주의 사항.  (0) 2019.03.04
Posted by 창업닉군
,