Проблема следующая: при тестировании оплаты с использованием in-app billing в Android не приходит свойство DEVELOPER_PAYLOAD от Google-маркета, хоть оно и установлено в REQUEST_PURCHASE запросе.
Свойство DEVELOPER_PAYLOAD необходимо для того чтобы "связать" отправляемый запрос с ответом от маркета так как общение между программой и маркетом происходит асинхронно. Для это в запросе REQUEST_PURCHASE разработчик должен установить свойство DEVELOPER_PAYLOAD, которое вернется вместе с запросом GET_PURCHASE_INFORMATION.
Это происходит при использовании следующих идентификаторов для тестирования:
При использовании в реальных условиях без эмуляции таких проблем не возникает. Поэтому либо искуственно установите это свойство, либо проводите тестирование с живими банковскими картами. Если Вы владеете аккаунтом продавца Google Play, то транзакции можно отменять.
Рассмотрим пример Dungeons от Google, в котором описана работа с In-app billing. В первую очередь, обратите внимание на следующие строки кода в классе Dungeons, именно так присваиваются упомянутые выше идентификаторы:
new CatalogEntry("android.test.purchased", R.string.android_test_purchased,
Managed.UNMANAGED),
new CatalogEntry("android.test.canceled", R.string.android_test_canceled,
Managed.UNMANAGED),
new CatalogEntry("android.test.refunded", R.string.android_test_refunded,
Managed.UNMANAGED),
new CatalogEntry("android.test.item_unavailable", R.string.android_test_item_unavailable,
Managed.UNMANAGED),
Также в примере есть подкласс RequestPurchase в классе BillingService, с помощью которого происходит отсылка свойства DEVELOPER_PAYLOAD:
class RequestPurchase extends BillingRequest {
public final String mProductId;
public final String mDeveloperPayload;
public RequestPurchase(String itemId) {
this(itemId, null);
}
public RequestPurchase(String itemId, String developerPayload) {
// This object is never created as a side effect of starting this
// service so we pass -1 as the startId to indicate that we should
// not stop this service after executing this request.
super(-1);
mProductId = itemId;
mDeveloperPayload = developerPayload;
}
@Override
protected long run() throws RemoteException {
Bundle request = makeRequestBundle("REQUEST_PURCHASE");
request.putString(Consts.BILLING_REQUEST_ITEM_ID, mProductId);
// Note that the developer payload is optional.
if (mDeveloperPayload != null) {
request.putString(Consts.BILLING_REQUEST_DEVELOPER_PAYLOAD, mDeveloperPayload);
}
Bundle response = mService.sendBillingRequest(request);
PendingIntent pendingIntent
= response.getParcelable(Consts.BILLING_RESPONSE_PURCHASE_INTENT);
if (pendingIntent == null) {
Log.e(TAG, "Error with requestPurchase");
return Consts.BILLING_RESPONSE_INVALID_REQUEST_ID;
}
Intent intent = new Intent();
ResponseHandler.buyPageIntentResponse(pendingIntent, intent);
return response.getLong(Consts.BILLING_RESPONSE_REQUEST_ID,
Consts.BILLING_RESPONSE_INVALID_REQUEST_ID);
}
@Override
protected void responseCodeReceived(ResponseCode responseCode) {
ResponseHandler.responseCodeReceived(BillingService.this, this, responseCode);
}
}
Обратите внимание на следующие строки кода:
if (mDeveloperPayload != null) {
request.putString(Consts.BILLING_REQUEST_DEVELOPER_PAYLOAD, mDeveloperPayload);
}
Убедитесь, что в переменную request добавляется свойство DEVELOPER_PAYLOAD, то есть, вызывается конструктор с двумя строковыми параметрами и свойство mDeveloperPayload нигде не переписывается.
Завершающий этап - получение DEVELOPER_PAYLOAD в ответе от сервера. Вся обработка находится в методе onPurchaseStateChange() класса Dungeons:
@Override
public void onPurchaseStateChange(PurchaseState purchaseState, String itemId,
int quantity, long purchaseTime, String developerPayload) {
if (Consts.DEBUG) {
Log.i(TAG, "onPurchaseStateChange() itemId: " + itemId + " " + purchaseState);
}
if (developerPayload == null) {
logProductActivity(itemId, purchaseState.toString());
} else {
logProductActivity(itemId, purchaseState + "\n\t" + developerPayload);
}
if (purchaseState == PurchaseState.PURCHASED) {
mOwnedItems.add(itemId);
}
mCatalogAdapter.setOwnedItems(mOwnedItems);
mOwnedItemsCursor.requery();
}
На этом всё, спасибо за внимание! :)