Как проверить транзакции Android в приложении для биллинга на моем сервере?
Я сделал приложение для Android, где предметы можно приобрести с помощью в приложении-биллинге. Когда товар приобретается, транзакция может быть легко синхронизирована между Android Market и телефоном - для использования в приложении. Но мне нужно, чтобы мой сервер знал о покупке. Решение о предоставлении конкретных приложений должно быть сделано на моем сервере, а не в клиентском приложении.
например.
- Пользователь покупает элемент X из Android Market.
- Данные транзакции Y отправляются клиенту.
- Клиент отправляет Y на мой сервер.
- Клиент запрашивает сервер для доставки контента для X.
- Сервер обеспечивает контент , если Y действителен. Как это можно сделать?
Q:. Как проверить, что данные транзакций, поступающие от клиента Android (предположительно из серверов Google), не являются поддельными? То есть хакер не генерировал данные.
Google Сервер → клиент Android → Мой сервер → клиент Android
Возможно, это больше вопрос PHP, чем что-либо еще. То, что должен сделать мой сервер script (PHP), чтобы убедиться, что полученные данные реальны?
Ответы
Ответ 1
Используйте openssl_verify ($ data, $signature, $key)
Переменные $data и $signature должны быть отправлены от клиента android на ваш php-сервер с помощью https. Транзакция содержит оба этих элемента. Отправьте это на свои серверы, прежде чем вы подтвердите транзакцию на клиенте (см. Здесь документацию - http://developer.android.com/guide/market/billing/billing_integrate.html)
Переменная $key - это ваш общедоступный ключ google, доступный из вашей учетной записи издателя на панели лицензирования и в приложении. Скопируйте открытый ключ и используйте его в своем php-коде, предпочтительно используя конфигурационный файл, который вы устанавливаете на своих серверах, а не в ваш фактический php-код.
Если вызов openssl_verify завершен успешно, вы должны сохранить номера заказов на своих серверах и убедиться, что они уникальны, поэтому их нельзя воспроизвести. Имейте в виду, что одна пара данных и подпись могут содержать много номеров заказов, хотя обычно это один заказ.
Ответ 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
}
}
Ответ 3
Данные транзакции подписываются с закрытым ключом, специфичным для вашего приложения. Существует также nonce для предотвращения повторов (т.е. Отправки одинаковых, действительных, данных несколько раз). Если вы убедитесь, что nonce уникален и подпись действительна на вашем сервере, вы можете быть достаточно уверены, что это не подделка. Ознакомьтесь с информацией о IAB этой презентации Google IO для обсуждения.