2011-12-05 24 views
34

我做了一個Android應用程序,其中的項目可以使用in-app-billing購買。購買物品時,交易可以在Android Market和手機之間輕鬆同步 - 可在應用程序中使用。但是,我需要MY服務器來了解購買情況。交付特定應用數據的決定應在我的服務器上進行,而不是在客戶端應用程序中進行。如何在我的服務器上驗證Android應用內結算交易?

E.g.

  1. 用戶從Android Market購買項目X。
  2. 交易數據Y被髮送到客戶端。
  3. 客戶端將Y發送到我的服務器。
  4. 客戶端要求服務器
  5. 服務器提供內容如果Y是有效爲X.提供內容。這如何實現?

問:我如何驗證的交易數據來自Android客戶端(從谷歌服務器大概始發)未來是不是假的?即黑客沒有生成數據。

谷歌服務器 - > Android客戶端 - >我的服務器 - > Android客戶端

也許這是比什麼都一個PHP問題。我的服務器腳本(PHP)應該如何確認檢索到的數據是真實的?

+0

這裏是我的項目中運行良好的簡單代碼: http://stackoverflow.com/a/35718040/2710505 –

回答

19

使用openssl_verify($數據,$簽名,$鍵)

$數據和$簽名應該從Android客戶端使用HTTPS被髮送到你的PHP服務器變量。交易包含這兩個項目。發送到您的服務器,你確認客戶端上的事務之前(在這裏看到的文檔 - http://developer.android.com/guide/market/billing/billing_integrate.html)。

變量$關鍵是要從許可&應用內結算面板可以從您的出版商帳戶您的谷歌公共密鑰。複製公鑰並在你的php代碼中使用它,最好使用你在你的服務器上安裝的配置文件,而不是在你實際的php代碼中。

如果openssl_verify調用成功,你應該存儲在服務器上的訂單號,並確保他們是唯一的,這樣它們不能被重播。請注意,單個數據收據和簽名對可能包含許多訂單號,儘管它通常是一個訂單。

+0

這是一個非常大的文檔。我有問題。什麼是$數據?什麼是$簽名?我們如何知道我們正在收到Google服務器的請求?我們在哪裏發送迴應? – Agamemnus

+0

openssl_verify是一個調用其openssl庫的PHP函數 - http://php.net/manual/en/function.openssl-verify.php。在檢查谷歌的服務器方面 - 我不認爲谷歌支持相互認證(http://en.wikipedia.org/wiki/Mutual_authentication),否則當你連接到谷歌的服務器時,你只需檢查類似於瀏覽器的SSL證書。 – abdollar

+0

@Agamemnus - 請注意,您不需要調用Google的服務器 - 您只需使用SSL調用您的服務器即可。你確實需要從谷歌的服務器獲取公鑰,但是你可以這樣做帶外 – abdollar

10

我們使用AndroidBillingLibrary

在Eclipse中安裝該項目並讓項目將其作爲庫導入。

我們實施BillingController.IConfiguration,像

import net.robotmedia.billing.BillingController; 

public class PhoneBillingConfiguration implements BillingController.IConfiguration{ 
    @Override 
    public byte[] getObfuscationSalt() { 
     return new byte[] {1,-2,3,4,-5,6,-7,theseshouldallberandombyteshere,8,-9,0}; 
    } 

    @Override 
    public String getPublicKey() { 
     return "superlongstringhereIforgothowwemadethis"; 
    } 
} 

那麼對於我們的應用程序,我們擴展Application

public class LocalizedApplication extends Application { 

    @Override 
    public void onCreate() { 
     super.onCreate(); 

//  BillingController.setDebug(true); 
     BillingController.setConfiguration(new PhoneBillingConfiguration()); 
    } 
} 

AndroidManifest包括這(和所有其他的東西)

<application 
    android:icon="@drawable/icon" 
    android:label="@string/app_name" 
    android:name=".LocalizedApplication" <!-- use your specific Application --> 
    android:largeHeap="true" 
    android:hardwareAccelerated="true" 
    > 

    <!-- For billing --> 
    <service android:name="net.robotmedia.billing.BillingService" /> 
     <receiver android:name="net.robotmedia.billing.BillingReceiver"> 
     <intent-filter> 
      <action android:name="com.android.vending.billing.IN_APP_NOTIFY" /> 
      <action android:name="com.android.vending.billing.RESPONSE_CODE" /> 
      <action android:name="com.android.vending.billing.PURCHASE_STATE_CHANGED" /> 
     </intent-filter> 
    </receiver> 

我們實施了ISignatureValidator

public class PhoneSignatureValidator implements ISignatureValidator { 
    private final String TAG = this.getClass().getSimpleName(); 
    private PhoneServerLink mServerLink; 


    private BillingController.IConfiguration configuration; 

    public PhoneSignatureValidator(Context context, BillingController.IConfiguration configuration, String our_product_sku) { 
     this.configuration = configuration; 
     mServerLink = new PhoneServerLink(context); 
     mServerLink.setSku(our_product_sku); 
    } 


    @Override 
    public boolean validate(String signedData, String signature) { 
     final String publicKey; 
     if (configuration == null || TextUtils.isEmpty(publicKey = configuration.getPublicKey())) { 
      Log.w(BillingController.LOG_TAG, "Please set the public key or turn on debug mode"); 
      return false; 
     } 
     if (signedData == null) { 
      Log.e(BillingController.LOG_TAG, "Data is null"); 
      return false; 
     } 
     // mServerLink will talk to your server 
     boolean bool = mServerLink.validateSignature(signedData, signature); 
     return bool; 
    } 

} 

這是上面最後幾行調用你的班級,實際上會談到你的服務器。

我們PhoneServerLink開始時是這樣的:

public class PhoneServerLink implements GetJSONListener { 

    public PhoneServerLink(Context context) { 
     mContext = context; 
    } 

    public boolean validateSignature(String signedData, String signature) { 
     return getPurchaseResultFromServer(signedData, signature, false); 
    } 

    private boolean getPurchaseResultFromServer(String signedData, String signature, boolean async) { 
      // send request to server using whatever protocols you like 
    } 

} 
+3

該代碼看起來不錯,但我相信這更多的是服務器端問題。我的服務器代碼究竟如何確定請求(事務信息)是否有效? – l33t

+1

啊廢話;我回答了一切*除了*你的問題!那麼,對於有相反問題的人來說,這也許是件好事。 ;-) –

+0

這個lib的鏈接是斷開的,你可以修改它,謝謝 – pengwang

4

交易數據與私鑰具體到你的應用程序簽名。還有一個隨機數可以防止重放(即多次發送相同的有效數據)。如果您驗證了隨機數是唯一的並且簽名在您的服務器上有效,則可以合理確定它不是假的。查看關於IAB的部分this Google IO presentation進行討論。

+0

嗯。聽起來像是正確的做法。但你如何檢查簽名是否有效?我使用PHP。 – l33t

+0

使用PHP的OpenSSl函數。您可以從開發控制檯獲取公鑰。確切的代碼可以在SO上找到,甚至有一個項目可以在Google Code,IIRC上做到這一點。 –

+0

會檢查IIRC,但我懷疑公鑰對此有幫助。它存儲在客戶端應用程序中,因此攻擊者可以提取它並使用它來生成虛假事務。 – l33t

相關問題