/* * 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 org.json.JSONException; import org.json.JSONObject; import android.text.TextUtils; import javax.annotation.Nonnull; import javax.annotation.concurrent.Immutable; /** * Purchase information as described <a href="http://developer.android.com/google/play/billing/billing_reference.html#purchase-data-table">here</a> */ @Immutable public final class Purchase { // the item's product identifier. Every item has a product ID, which you must specify // in the application's product list on the Google Play Developer Console @Nonnull public final String sku; // a unique order identifier for the transaction. This identifier corresponds to the // Google Wallet Order ID @Nonnull public final String orderId; // the application package from which the purchase originated @Nonnull public final String packageName; // the time the product was purchased, in milliseconds since the epoch (Jan 1, 1970) public final long time; // the purchase state of the order @Nonnull public final State state; // a developer-specified string that contains supplemental information about an order. // You can specify a value for this field when you make a getBuyIntent request @Nonnull public final String payload; // a token that uniquely identifies a purchase for a given item and user pair @Nonnull public final String token; // Indicates whether the subscription renews automatically. If true, the subscription is active, // and will automatically renew on the next billing date. If false, indicates that the user has // canceled the subscription. The user has access to subscription content until the next billing // date and will lose access at that time unless they re-enable automatic renewal public final boolean autoRenewing; /** * Raw data returned from {@link com.android.vending.billing.IInAppBillingService#getPurchases} */ @Nonnull public final String data; /** * Signature of {@link #data} */ @Nonnull public final String signature; Purchase(@Nonnull String sku, @Nonnull String orderId, @Nonnull String packageName, long time, int state, @Nonnull String payload, @Nonnull String token, boolean autoRenewing, @Nonnull String data, @Nonnull String signature) { this.sku = sku; this.orderId = orderId; this.packageName = packageName; this.time = time; this.state = State.valueOf(state); this.payload = payload; this.token = token; this.autoRenewing = autoRenewing; this.signature = signature; this.data = data; } Purchase(@Nonnull String data, @Nonnull String signature) throws JSONException { final JSONObject json = new JSONObject(data); this.sku = json.getString("productId"); this.orderId = json.optString("orderId"); this.packageName = json.optString("packageName"); this.time = json.getLong("purchaseTime"); this.state = State.valueOf(json.optInt("purchaseState", 0)); this.payload = json.optString("developerPayload"); this.token = json.optString("token", json.optString("purchaseToken")); this.autoRenewing = json.optBoolean("autoRenewing"); this.data = data; this.signature = signature; } @Nonnull static Purchase fromJson(@Nonnull String data, @Nonnull String signature) throws JSONException { return new Purchase(data, signature); } private static void tryPut(@Nonnull JSONObject json, @Nonnull String key, @Nonnull String name) throws JSONException { if (!TextUtils.isEmpty(name)) { json.put(key, name); } } /** * Same as {@link #toJson(boolean)} with {@code withSignature=false}. * Note that this method returns JSON which is not the same as original JSON returned by * Google. * Original JSON is * stored in {@link #data}, use it if you want to do a signature check (as {@link #signature} * signs {@link #data}) * * @return JSON representation of this object */ @Nonnull public String toJson() { return toJson(false); } /** * It might be useful to get a JSON of {@link Purchase} with signature information (Android * provides signature * separately). In that case use {@code withSignature=true}. * * @param withSignature if true then {@link #signature} will be included in the result * @return JSON representation of this object */ @Nonnull public String toJson(boolean withSignature) { return toJsonObject(withSignature).toString(); } @Nonnull JSONObject toJsonObject(boolean withSignature) { final JSONObject json = new JSONObject(); try { json.put("productId", sku); tryPut(json, "orderId", orderId); tryPut(json, "packageName", packageName); json.put("purchaseTime", time); json.put("purchaseState", state.id); tryPut(json, "developerPayload", payload); tryPut(json, "token", token); if (autoRenewing) { json.put("autoRenewing", true); } if (withSignature) { tryPut(json, "signature", signature); } } catch (JSONException e) { // JSON exception should never happen in runtime throw new AssertionError(e); } return json; } @Override public String toString() { return "Purchase{" + "state=" + state + ", time=" + time + ", sku='" + sku + '\'' + '}'; } public static enum State { PURCHASED(0), CANCELLED(1), REFUNDED(2), // billing v2 only EXPIRED(3); public final int id; State(int id) { this.id = id; } @Nonnull static State valueOf(int id) { switch (id) { case 0: return PURCHASED; case 1: return CANCELLED; case 2: return REFUNDED; case 3: return EXPIRED; } throw new IllegalArgumentException("Id=" + id + " is not supported"); } } }