【问题标题】:How do I verify Android in-app-billing transactions on MY server?如何在我的服务器上验证 Android 应用内结算交易?
【发布时间】:2012-01-12 20:51:25
【问题描述】:

我制作了一个 Android 应用,可以使用 in-app-billing 购买商品。购买商品后,交易可以轻松在 Android Market 和手机之间同步 - 以便在应用程序中使用。但是,我需要我的服务器来了解购买。应在我的服务器上而不是在客户端应用程序中决定交付特定于应用程序的数据。

例如

  1. 用户从 Android Market 购买商品 X。
  2. 交易数据 Y 被发送到客户端。
  3. 客户端将 Y 发送到我的服务器。
  4. 客户端要求服务器为 X 提供内容。
  5. 服务器传送内容如果 Y 有效。如何做到这一点?

问:如何验证来自 Android 客户端(可能来自 Google 服务器)的交易数据不是假的? IE。黑客没有生成数据。

Google 服务器 -> Android 客户端 -> 我的服务器 -> Android 客户端

也许这更像是一个 PHP 问题。为了验证检索到的数据是否真实,我的服务器脚本 (PHP) 究竟应该做什么?

【问题讨论】:

标签: php android in-app-billing


【解决方案1】:

使用 openssl_verify ($data, $signature, $key)

变量 $data 和 $signature 应该使用 https 从 android 客户端发送到您的 php 服务器。交易包含这两个项目。在您确认客户端上的交易之前将其发送到您的服务器。(请参阅此处的文档 - http://developer.android.com/guide/market/billing/billing_integrate.html

变量 $key 是您的 google 公钥,可从您的发布商帐户的许可和应用内结算面板中获得。复制公钥并在您的 php 代码中使用它,最好使用您安装在服务器上的配置文件,而不是在您的实际 php 代码中。

如果 openssl_verify 调用成功,您应该将订单号存储在您的服务器上并确保它们是唯一的,这样它们就不会被重放。请注意,单个数据收据和签名对可能包含许多订单号,尽管通常是一个订单。

【讨论】:

  • 这是一个非常大的文档。我有问题。 $数据是什么? $签名是什么?我们如何知道我们收到了来自 Google 服务器的请求?我们在哪里发回响应?
  • openssl_verify 是对其 openssl 库 - php.net/manual/en/function.openssl-verify.php 的 PHP 函数调用。在检查 google 的服务器方面 - 我认为 google 不支持相互身份验证 (en.wikipedia.org/wiki/Mutual_authentication) 否则您只需在连接到 google 的服务器时检查类似于浏览器的 SSL 证书。
  • @Agamemnus - 请注意,您不需要调用 google 的服务器 - 您只需要使用 SSL 调用您的服务器。您确实需要从 Google 的服务器获取公钥,但您可以在带外进行此操作
  • 我很困惑。例如,如果我要使用 Paypal 的系统验证购买,Paypal 会向我的服务器发送一条消息以验证交易。如果它是 Google Play 应用内购买,并且我的服务器向用户发送了一些东西,我的服务器如何知道有购买,以及购买者是谁? WAIT..你是说谷歌发送一个解码为 $data 的 $signature 字符串,我可以使用他们的公钥(在哪里?)来解码和验证?谷歌的例子开始变得有意义,但它看起来仍然很抽象。
  • @Agamemnus - Google 将签名和数据发送到手机/设备。手机将此有效负载发送到您的服务器。您正在验证此有效负载实际上是由 google 发送的 - 由 google 签名。您仍然需要检查有效负载没有被重放,并且有效负载可能有多个订单。
【解决方案2】:

我们使用了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 
    }

}

【讨论】:

  • 代码看起来不错,但我相信这更多的是服务器端问题。我的服务器代码究竟如何确定请求(交易信息)是否有效?
  • 啊废话;我回答了所有除了你的问题!好吧,也许这对有相反问题的人有好处。 ;-)
  • 已修复。 (真令人惊讶,当他们改变它时 github 没有考虑到这一点!)
  • @ThunderRabbit 我想为应用内购买编写一个代码,我想在其中提供在我的应用内购买视频的功能,并且视频存储在我自己的服务器中..那么你有任何教程吗这个和“Android Billing Library”可以安全地在我们的项目中使用吗??
【解决方案3】:

交易数据使用特定于您的应用的私钥进行签名。还有一个随机数可以防止重放(即多次发送相同的有效数据)。如果您验证 nonce 是唯一的并且签名在您的服务器上是有效的,那么您可以合理地确定它不是假的。查看this Google IO presentation关于IAB的部分进行讨论。

【讨论】:

  • 嗯。听起来是正确的方法。但是如何检查签名是否有效?我正在使用 PHP。
  • 使用 PHP 的 OpenSSl 函数。您可以从开发控制台获取公钥。可以在 SO 上找到确切的代码,甚至还有一个在 Google Code IIRC 上执行此操作的项目。
  • 会检查 IIRC,但我怀疑公钥在这里会有所帮助。它存储在客户端应用程序中,因此攻击者可以提取它并使用它来生成虚假交易。
  • 您需要再次阅读文档。 private 密钥位于 Google 服务器中。它签署交易。除非有人破解这些,否则他们无法生成“虚假交易”。公钥通常存储在客户端应用程序中,仅用于验证。如果您有验证服务器,它不必在应用程序中,它保留在服务器上。研究我链接的幻灯片以获取更多详细信息。
  • 不难找到:code.google.com/p/android-market-license-verification。它似乎不是很活跃,但你应该明白。
猜你喜欢
  • 1970-01-01
  • 2011-08-30
  • 1970-01-01
  • 2014-01-19
  • 1970-01-01
  • 2012-01-13
  • 1970-01-01
  • 2019-03-21
  • 2013-04-24
相关资源
最近更新 更多