org.solovyev.android.checkout.PurchaseFlow.java Source code

Java tutorial

Introduction

Here is the source code for org.solovyev.android.checkout.PurchaseFlow.java

Source

/*
 * Copyright 2014 serso aka se.solovyev
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Contact details
 *
 * Email: se.solovyev@gmail.com
 * Site:  http://se.solovyev.org
 */

package org.solovyev.android.checkout;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentSender;
import org.json.JSONException;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;

import static android.app.Activity.RESULT_OK;
import static java.util.Arrays.asList;
import static org.solovyev.android.checkout.ResponseCodes.*;

/**
 * Class which handles different events during the purchase process
 */
public final class PurchaseFlow implements CancellableRequestListener<PendingIntent> {

    static final String EXTRA_RESPONSE = "RESPONSE_CODE";
    static final String EXTRA_PURCHASE_DATA = "INAPP_PURCHASE_DATA";
    static final String EXTRA_PURCHASE_SIGNATURE = "INAPP_DATA_SIGNATURE";

    @Nonnull
    private final Activity activity;

    private final int requestCode;

    @Nullable
    private RequestListener<Purchase> listener;

    @Nonnull
    private final PurchaseVerifier verifier;

    PurchaseFlow(@Nonnull Activity activity, int requestCode, @Nonnull RequestListener<Purchase> listener,
            @Nonnull PurchaseVerifier verifier) {
        this.activity = activity;
        this.requestCode = requestCode;
        this.listener = listener;
        this.verifier = verifier;
    }

    @Override
    public void onSuccess(@Nonnull PendingIntent purchaseIntent) {
        if (listener == null) {
            // request was cancelled => stop here
            return;
        }
        try {
            activity.startIntentSenderForResult(purchaseIntent.getIntentSender(), requestCode, new Intent(), 0, 0,
                    0);
        } catch (RuntimeException e) {
            handleError(e);
        } catch (IntentSender.SendIntentException e) {
            handleError(e);
        }
    }

    void onActivityResult(int requestCode, int resultCode, Intent intent) {
        try {
            Check.equals(this.requestCode, requestCode);
            if (intent == null) {
                // sometimes intent is null (it's not obvious when it happens but it happens from time to time)
                handleError(NULL_INTENT);
                return;
            }
            final int responseCode = intent.getIntExtra(EXTRA_RESPONSE, OK);
            if (resultCode == RESULT_OK && responseCode == OK) {
                final String data = intent.getStringExtra(EXTRA_PURCHASE_DATA);
                final String signature = intent.getStringExtra(EXTRA_PURCHASE_SIGNATURE);
                Check.isNotNull(data);
                Check.isNotNull(signature);

                final Purchase purchase = Purchase.fromJson(data, signature);
                verifier.verify(asList(purchase), new VerificationListener());
            } else {
                handleError(responseCode);
            }
        } catch (RuntimeException e) {
            handleError(e);
        } catch (JSONException e) {
            handleError(e);
        }
    }

    private void handleError(int response) {
        Billing.error("Error response: " + response + " in " + PurchaseRequest.class.getSimpleName() + " request");
        onError(response, new BillingException(response));
    }

    private void handleError(@Nonnull Exception e) {
        Billing.error("Exception in " + PurchaseRequest.class.getSimpleName() + " request: ", e);
        onError(ResponseCodes.EXCEPTION, e);
    }

    @Override
    public void onError(int response, @Nonnull Exception e) {
        if (listener != null) {
            listener.onError(response, e);
        }
    }

    /**
     * Note that cancelling the purchase flow is not the same as cancelling the purchase process as purchase process
     * is not controlled by the app. This method only guarantees that there will be no more calls to {@link RequestListener}
     */
    @Override
    public void cancel() {
        if (listener != null) {
            Billing.cancel(listener);
            listener = null;
        }
    }

    private class VerificationListener implements RequestListener<List<Purchase>> {
        @Override
        public void onSuccess(@Nonnull List<Purchase> verifiedPurchases) {
            Check.isMainThread();
            if (!verifiedPurchases.isEmpty()) {
                if (listener != null) {
                    listener.onSuccess(verifiedPurchases.get(0));
                }
            } else {
                handleError(WRONG_SIGNATURE);
            }
        }

        @Override
        public void onError(int response, @Nonnull Exception e) {
            Check.isMainThread();
            if (response == EXCEPTION) {
                handleError(e);
            } else {
                handleError(response);
            }
        }
    }
}