Проблема наступна: при тестуванні оплати з використанням 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();
}
На цьому все, спасибі за увагу! :)