/*
Swisscom Safe Connect
Copyright (C) 2014 Swisscom
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.swisscom.safeconnect.billing;
import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import com.swisscom.safeconnect.BuildConfig;
import com.swisscom.safeconnect.backend.BackendConnector;
import com.swisscom.safeconnect.model.PlumberPurchaseResponse;
import com.swisscom.safeconnect.model.PlumberResponse;
import com.swisscom.safeconnect.model.PlumberStatus;
import com.swisscom.safeconnect.model.PlumberSubscriptionResponse;
import com.swisscom.safeconnect.model.Subscription;
import com.swisscom.safeconnect.utils.Config;
import java.util.ArrayList;
import java.util.List;
/**
* Created by vadim on 30.09.14.
*/
public class PurchaseManager {
private static final String DEV_KEY = "";
// (arbitrary) request code for the purchase flow
private static final int RC_REQUEST = 0;
/**
* available subscriptions. Name of the subscription should coincide with the SKU
* name in the Developer Console
*/
private Subscription[] subscriptions;
/**
* helper object
*/
private IabHelper helper;
/**
* our backend
*/
private BackendConnector backend;
private Activity activity;
private Inventory inventory;
public static interface Callback<T> {
public void onRequestCompleted(T result);
public void onRequestFailed(int errorCode);
}
private class ConsumeDoneListener implements IabHelper.OnConsumeFinishedListener {
private Callback<Subscription> callback;
private Subscription subscription;
public ConsumeDoneListener(Subscription subscription, Callback<Subscription> callback) {
this.callback = callback;
this.subscription = subscription;
}
@Override
public void onConsumeFinished(final Purchase purchase, IabResult result) {
if (helper == null) return;
if (result.isSuccess()) {
callback.onRequestCompleted(subscription);
} else {
callback.onRequestFailed(0);
}
}
}
private void finishPurchase(final String phone, final Purchase purchase,
final Callback<Subscription> callback) {
backend.finishPurchase(phone, purchase.getDeveloperPayload(), purchase.getSignature(),
purchase.getOriginalJson(),
new BackendConnector.ResponseCallback<PlumberSubscriptionResponse>() {
@Override
public void onRequestComplete(int statusCode, PlumberSubscriptionResponse response) {
if (statusCode == 200 && response.getStatus() == PlumberStatus.SUCCESS) {
if (callback != null) {
Subscription s = new Subscription(response);
if (purchase.getSku().equals(s.getSku())) {
helper.consumeAsync(purchase, new ConsumeDoneListener(s, callback));
} else {
callback.onRequestFailed(0);
}
}
} else {
if (callback != null) {
callback.onRequestFailed(0);
}
}
}
});
}
public PurchaseManager(Activity activity, Subscription[] s) {
this.activity = activity;
subscriptions = s;
backend = new BackendConnector(activity);
}
/**
* using the available Plumber subscriptions, queries Google Play for additional data
* about them, such as prices
* @param callback
*/
public void queryInventory(final Callback<Inventory> callback) {
if (inventory != null) callback.onRequestCompleted(inventory);
List<String> skus = new ArrayList<String>(subscriptions.length);
for (Subscription s: subscriptions) {
skus.add(s.getSku());
}
helper.queryInventoryAsync(true, skus, new IabHelper.QueryInventoryFinishedListener() {
@Override
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
// ignore signature verification failures, we are anyway check it on a server
if (result.getResponse() != IabHelper.IABHELPER_VERIFICATION_FAILED &&
result.getResponse() != IabHelper.BILLING_RESPONSE_RESULT_OK) {
if (callback != null) callback.onRequestFailed(0);
} else {
callback.onRequestCompleted(inv);
inventory = inv;
}
}
});
}
/**
* call to initialize the billing
*/
public void init(final Callback<PlumberResponse> callback) {
helper = new IabHelper(activity, DEV_KEY);
helper.enableDebugLogging(BuildConfig.DEBUG, Config.TAG);
helper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
callback.onRequestFailed(0);
return;
}
ensureSubscriptionsConsumed(new Callback<Subscription>() {
@Override
public void onRequestCompleted(Subscription result) {
callback.onRequestCompleted(null);
}
@Override
public void onRequestFailed(int errorCode) {
callback.onRequestFailed(0);
}
});
}
});
}
/**
* gets the owned subscription and consumes (activates) it
*/
public void ensureSubscriptionsConsumed(final Callback<Subscription> callback) {
// Listener that's called when we finish querying the items and subscriptions we own
IabHelper.QueryInventoryFinishedListener gotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
boolean consuming = false;
// Have we been disposed of in the meantime? If so, quit.
if (helper == null) return;
// ignore signature verification failures, we are anyway check it on a server
if (result.getResponse() != IabHelper.IABHELPER_VERIFICATION_FAILED &&
result.getResponse() != IabHelper.BILLING_RESPONSE_RESULT_OK) {
if (callback != null) callback.onRequestFailed(0);
return;
}
// check what subscription plan we currently own
for (Subscription s: subscriptions) {
Purchase p = inventory.getPurchase(s.getSku());
if (p != null) {
finishPurchase(Config.getInstance().getPhoneNumber(), p, callback);
consuming = true;
}
}
if (!consuming) callback.onRequestCompleted(null);
}
};
helper.queryInventoryAsync(gotInventoryListener);
}
/**
* purchases the subscription with a specified name
* @param subscription
*/
public void purchaseSubscription(final Subscription subscription, final Callback<Subscription> callback) {
final IabHelper.OnIabPurchaseFinishedListener purchaseDoneListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, final Purchase purchase) {
// if we were disposed of in the meantime, quit.
if (helper == null) return;
if (result.isFailure() ) {
callback.onRequestFailed(result.getResponse());
return;
}
// need async because of verification routine
new AsyncTask<Void, Void, Void>() {
private boolean verified = false;
@Override
protected Void doInBackground(Void... params) {
verified = verifyDeveloperPayload(subscription.getId(), purchase);
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
if (!verified) {
callback.onRequestFailed(0);
return;
}
finishPurchase(Config.getInstance().getPhoneNumber(), purchase, callback);
}
}.execute();
}
};
if (subscription == null) {
callback.onRequestFailed(0);
} else {
backend.startPurchase(Config.getInstance().getPhoneNumber(), subscription.getId(),
new BackendConnector.ResponseCallback<PlumberPurchaseResponse>() {
@Override
public void onRequestComplete(int statusCode, PlumberPurchaseResponse response) {
if (statusCode == 200 && response.getStatus() == PlumberStatus.SUCCESS) {
helper.launchPurchaseFlow(activity, subscription.getSku(), RC_REQUEST,
purchaseDoneListener, response.getUid());
} else {
callback.onRequestFailed(0);
}
}
});
}
}
public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
if (helper != null) {
return helper.handleActivityResult(requestCode, resultCode, data);
} else {
return false;
}
}
/**
* Verifies the developer payload of a purchase. Do not call on UI thread!
*/
private boolean verifyDeveloperPayload(int subscriptionId, Purchase p) {
String payload = p.getDeveloperPayload();
// find subscription id
return backend.verifyPurchaseIdSync(Config.getInstance().getPhoneNumber(),
payload, subscriptionId).getStatus() == PlumberStatus.SUCCESS;
}
}