package yuku.easybilling; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.IntentSender.SendIntentException; import android.util.Log; import java.security.SecureRandom; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import yuku.easybilling.BillingRequest.CheckBillingSupportedRequest; import yuku.easybilling.BillingRequest.ConfirmNotificationsRequest; import yuku.easybilling.BillingRequest.GetPurchaseInformationRequest; import yuku.easybilling.BillingRequest.RequestPurchaseRequest; import yuku.easybilling.BillingRequest.RestoreTransactionsRequest; import yuku.easybilling.BillingResult.CheckBillingSupportedResult; import yuku.easybilling.BillingResult.ConfirmNotificationsResult; import yuku.easybilling.BillingResult.GetPurchaseInformationResult; import yuku.easybilling.BillingResult.RequestPurchaseResult; import yuku.easybilling.BillingResult.RestoreTransactionsResult; import yuku.easybilling.BillingSecurity.SignedData; /** Static methods to call from anywhere! */ public class EasyBilling { public static final String TAG = EasyBilling.class.getSimpleName(); static Context appContext; static BillingService service; static String base64Key; private static ResponseCode checkBillingSupportedResult; private static WeakHashMap<EasyBillingListener, Object> listeners = new WeakHashMap<EasyBillingListener, Object>(); private static final Object DUMMY = new Object(); public interface OnBillingInitListener { void onBillingInitFinished(); } public static void init(Context appContext, String base64Key) { init(appContext, base64Key, null); } public static void init(Context appContext, String base64Key, final OnBillingInitListener listener) { EasyBilling.appContext = appContext; EasyBilling.base64Key = base64Key; // for checking if billing supported initService(); CheckBillingSupportedRequest request = new CheckBillingSupportedRequest(appContext); service.request(request, new BillingResultListener<CheckBillingSupportedResult>() { @Override public void onBillingResult(BillingRequestStatus status, CheckBillingSupportedResult result) { Log.d(TAG, "CheckBillingSupportedResult result: " + result.responseCode); checkBillingSupportedResult = result.responseCode; if (listener != null) { listener.onBillingInitFinished(); } } }); } /** Check if Billing is supported. * @return {@link ResponseCode#RESULT_OK} if yes, non-null if no, and null if no result yet. */ public static ResponseCode isBillingSupported() { return checkBillingSupportedResult; } /** * This keeps track of the nonces that we generated and sent to the server. * We need to keep track of these until we get back the purchase state and * send a confirmation message back to Android Market. If we are killed and * lose this list of nonces, it is not fatal. Android Market will send us a * new "notify" message and we will re-generate a new nonce. This has to be * "static" so that the {@link BillingReceiver} can check if a nonce exists. */ private static HashSet<Long> sKnownNonces = new HashSet<Long>(); private static SecureRandom sRandom = new SecureRandom(); /** Generates a nonce (a random number used once). */ static long storeAndGetNonce() { long nonce = sRandom.nextLong(); Log.i(TAG, "Nonce generated: " + nonce); sKnownNonces.add(nonce); return nonce; } static void removeNonce(long nonce) { sKnownNonces.remove(nonce); } static boolean isNonceKnown(long nonce) { return sKnownNonces.contains(nonce); } public static void addListener(EasyBillingListener listener) { listeners.put(listener, DUMMY); } public static void removeListener(EasyBillingListener listener) { listeners.remove(listener); } public static int getInventoryAmount(String productId) { return BillingDb.get(appContext).getInventoryAmount(productId); } public static Map<String, Integer> getAllInventory() { return BillingDb.get(appContext).getAllInventory(); } private static void initService() { if (service == null) { service = new BillingService(); service.setContext(appContext); } } public static BillingRequestStatus startPurchase(final Activity activity, String productId, String optionalDeveloperPayload) { initService(); RequestPurchaseRequest request = new RequestPurchaseRequest(appContext, productId, optionalDeveloperPayload); return service.request(request, new BillingResultListener<RequestPurchaseResult>() { @Override public void onBillingResult(BillingRequestStatus status, RequestPurchaseResult result) { try { activity.startIntentSender(result.purchaseIntent.getIntentSender(), new Intent(), 0, 0, 0); } catch (SendIntentException e) { Log.e(TAG, "error starting activity", e); } } }); } public static BillingRequestStatus startRestoreTransactions() { initService(); RestoreTransactionsRequest request = new RestoreTransactionsRequest(appContext, storeAndGetNonce()); return service.request(request, new BillingResultListener<RestoreTransactionsResult>() { @Override public void onBillingResult(BillingRequestStatus status, RestoreTransactionsResult result) { Log.d(TAG, "RestoreTransactions result: " + result.responseCode); } }); } static void gotInAppNotify(String notification_id) { Log.d(TAG, "@@gotInAppNotify"); String[] notification_ids = { notification_id }; initService(); GetPurchaseInformationRequest request = new GetPurchaseInformationRequest(appContext, storeAndGetNonce(), notification_ids); service.request(request, new BillingResultListener<GetPurchaseInformationResult>() { @Override public void onBillingResult(BillingRequestStatus status, GetPurchaseInformationResult result) { Log.d(TAG, "GetPurchaseInformation result: " + result.responseCode); } }); } static void gotPurchaseStateChanged(String inapp_signed_data, String inapp_signature) { Log.d(TAG, "@@gotPurchaseStateChanged"); boolean verified = BillingSecurity.verifySignedData(inapp_signed_data, inapp_signature); if (!verified) { Log.e(TAG, "Signature verification error"); return; } SignedData signedData = BillingSecurity.decodeSignedData(inapp_signed_data); if (!isNonceKnown(signedData.nonce)) { Log.e(TAG, "Nonce is unknown: " + signedData.nonce); return; } // make a copy of current listeners Set<EasyBillingListener> listenerSet = new HashSet<EasyBillingListener>(listeners.keySet()); List<String> notification_ids = new ArrayList<String>(); for (SignedData.Order change: signedData.orders) { notification_ids.add(change.notificationId); BillingDb.get(appContext).updateWithChange(change, listenerSet); } initService(); ConfirmNotificationsRequest request = new ConfirmNotificationsRequest(appContext, notification_ids.toArray(new String[notification_ids.size()])); service.request(request, new BillingResultListener<ConfirmNotificationsResult>() { @Override public void onBillingResult(BillingRequestStatus status, ConfirmNotificationsResult result) { Log.d(TAG, "ConfirmNotifications result: " + result.responseCode); } }); } }