package jp.mixi.training.inapppurchase.helper;
import java.lang.reflect.Type;
import java.util.Date;
import javax.annotation.Nullable;
import jp.mixi.android.commons.json.JsonConvertible;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.IntentSender.SendIntentException;
import android.os.Bundle;
import android.os.RemoteException;
import com.android.vending.billing.IInAppBillingService;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
public class StartPurchaseHelper {
private static final int BILLING_RESPONSE_RESULT_OK = 0;
private static final int BILLING_RESPONSE_RESULT_USER_CANCELED = 1;
private static final int BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE = 3;
private static final int BILLING_RESPONSE_RESULT_ITEM_UNAVAILABLE = 4;
@SuppressWarnings("unused")
private static final int BILLING_RESPONSE_RESULT_DEVELOPER_ERROR = 5;
@SuppressWarnings("unused")
private static final int BILLING_RESPONSE_RESULT_ERROR = 6;
private static final int BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED = 7;
private static final int BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED = 8;
private static final String RESPONSE_KEY_RESPONSE_CODE = "RESPONSE_CODE";
private static final String RESPONSE_KEY_BUY_INTENT = "BUY_INTENT";
private static final String RESPONSE_KEY_PURCHASE_DATA = "INAPP_PURCHASE_DATA";
private final GsonBuilder mGsonBuilder;
{
mGsonBuilder = new GsonBuilder();
mGsonBuilder.registerTypeAdapter(Date.class, new JsonDeserializer<Date>() {
@Override
public Date deserialize (final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
if (json != null && json.isJsonPrimitive()) {
final JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive();
if (jsonPrimitive != null && jsonPrimitive.isNumber()) {
final long dateLong = jsonPrimitive.getAsLong();
return new Date(dateLong);
}
}
//not a number, default deserialization:
return context.deserialize(json, typeOfT);
}
});
}
public static enum BillingActionResponse {
/**
* 決済アクションが成功した
*/
DONE,
/**
* ユーザが決済を中止した
*/
CANCELED,
/**
* 課金サービスのバージョンがアクションに対応しない
*/
SERVICE_VERSION_ERROR,
/**
* 購入不可能なアイテム(sku)
*/
ITEM_UNAVAILABLE,
/**
* 保有していないアイテムの消費(consume)や保有しているアイテムの購入等
*/
ITEM_OWNERSHIP_ERROR,
/**
* 内部実装によるエラーが発生した
*/
INTERNAL_ERROR
}
public static class PurchaseData implements JsonConvertible {
private String orderId;
private String packageName;
private String productId;
private Date purchaseTime;
private String purchaseToken;
private String developerPayload;
public String getOrderId() { return orderId; }
public String getPackageName() { return packageName; }
public String getProductId() { return productId; }
public Date getPurchaseTime() { return purchaseTime; }
public String getPurchaseToken() { return purchaseToken; }
public String getDeveloperPayload() { return developerPayload; }
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("orderId", orderId)
.add("packageName", packageName)
.add("productId", productId)
.add("purchaseTime", purchaseTime)
.add("purchaseToken", purchaseToken)
.add("developerPayload", developerPayload)
.toString();
}
};
private BillingActionResponse billingActionResponseCodeToEnum (final int billingActionResponseCode, final BillingActionResponse defaultEnum) {
switch (billingActionResponseCode) {
case BILLING_RESPONSE_RESULT_USER_CANCELED:
return BillingActionResponse.CANCELED;
case BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE:
return BillingActionResponse.SERVICE_VERSION_ERROR;
case BILLING_RESPONSE_RESULT_ITEM_UNAVAILABLE:
return BillingActionResponse.ITEM_UNAVAILABLE;
case BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED:
case BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED:
return BillingActionResponse.ITEM_OWNERSHIP_ERROR;
default:
return defaultEnum;
}
}
public BillingActionResponse startPurchaseAction (final Activity callingActivity, final int activityResultCode, final IInAppBillingService billingService, final DummySku sku) throws RemoteException {
if (callingActivity == null || billingService == null) throw new IllegalArgumentException("activity and service argument must be non-null");
final Bundle purchaseIntentBundle = billingService.getBuyIntent(3, callingActivity.getPackageName(), sku.getId(), "inapp", null);
final int responseCodeGetBuyIntent = purchaseIntentBundle.getInt(RESPONSE_KEY_RESPONSE_CODE);
switch (responseCodeGetBuyIntent) {
case BILLING_RESPONSE_RESULT_OK:
final PendingIntent pendingIntent = purchaseIntentBundle.getParcelable(RESPONSE_KEY_BUY_INTENT);
if (pendingIntent != null) {
try {
callingActivity.startIntentSenderForResult(pendingIntent.getIntentSender(), activityResultCode, null, 0, 0, 0);
return BillingActionResponse.DONE;
} catch (final SendIntentException sendIntentException) {
return BillingActionResponse.INTERNAL_ERROR;
}
} else {
return BillingActionResponse.INTERNAL_ERROR;
}
default:
return billingActionResponseCodeToEnum(responseCodeGetBuyIntent, BillingActionResponse.INTERNAL_ERROR);
}
}
public Optional<PurchaseData> extractPurchaseActionData (@Nullable final Bundle purchaseActionResult) {
if (purchaseActionResult != null) {
final String purchaseActionResultJson = purchaseActionResult.getString(RESPONSE_KEY_PURCHASE_DATA);
if (purchaseActionResultJson != null) {
final Gson purchaseActionBuilder = mGsonBuilder.create();
final PurchaseData purchaseActionData = purchaseActionBuilder.fromJson(purchaseActionResultJson, PurchaseData.class);
return Optional.of(purchaseActionData);
}
}
//no data:
return Optional.absent();
}
public BillingActionResponse consumePurchase (final Activity callingActivity, final IInAppBillingService billingService, final String purchaseToken) throws RemoteException {
if (callingActivity == null || billingService == null) throw new IllegalArgumentException("activity and service argument must be non-null");
final int responseCode = billingService.consumePurchase(3, callingActivity.getPackageName(), purchaseToken);
switch (responseCode) {
case BILLING_RESPONSE_RESULT_OK:
return BillingActionResponse.DONE;
default:
return billingActionResponseCodeToEnum(responseCode, BillingActionResponse.INTERNAL_ERROR);
}
}
}