public void onPurchaseStateChange(Consts.PurchaseState purchaseState, string itemId, int quantity, long purchaseTime, string developerPayload) //bool fromBackgroundTHread = false) { m_activity.RunOnUiThread(() => { Toast.MakeText(m_activity, string.Format("PurchaseState: {0}", purchaseState.ToString()), ToastLength.Long).Show(); }); }
public VerifiedPurchase(Consts.PurchaseState purchaseState, string notificationId, string productId, string orderId, long purchaseTime, string developerPayload) { this.purchaseState = purchaseState; this.notificationId = notificationId; this.productId = productId; this.orderId = orderId; this.purchaseTime = purchaseTime; this.developerPayload = developerPayload; }
public Transaction(String orderId, String productId, String packageName, PurchaseState purchaseState, String notificationId, long purchaseTime, String developerPayload) { this.orderId = orderId; this.productId = productId; this.packageName = packageName; this.purchaseState = purchaseState; this.notificationId = notificationId; this.purchaseTime = purchaseTime; this.developerPayload = developerPayload; }
/** * Inserts a purchased product into the database. There may be multiple * rows in the table for the same product if it was purchased multiple times * or if it was refunded. * @param orderId the order ID (matches the value in the product list) * @param productId the product ID (sku) * @param state the state of the purchase * @param purchaseTime the purchase time (in milliseconds since the epoch) * @param developerPayload the developer provided "payload" associated with * the order. */ private void insertOrder(string orderId, string productId, Consts.PurchaseState state, long purchaseTime, string developerPayload) { ContentValues values = new ContentValues(); values.Put(HISTORY_ORDER_ID_COL, orderId); values.Put(HISTORY_PRODUCT_ID_COL, productId); values.Put(HISTORY_STATE_COL, (int)state); values.Put(HISTORY_PURCHASE_TIME_COL, purchaseTime); values.Put(HISTORY_DEVELOPER_PAYLOAD_COL, developerPayload); mDb.Replace(PURCHASE_HISTORY_TABLE_NAME, null /* nullColumnHack */, values); }
/** * Adds the given purchase information to the database and returns the total * number of times that the given product has been purchased. * @param orderId a string identifying the order * @param productId the product ID (sku) * @param purchaseState the purchase state of the product * @param purchaseTime the time the product was purchased, in milliseconds * since the epoch (Jan 1, 1970) * @param developerPayload the developer provided "payload" associated with * the order * @return the number of times the given product has been purchased. */ public int updatePurchase(string orderId, string productId, Consts.PurchaseState purchaseState, long purchaseTime, string developerPayload) { insertOrder(orderId, productId, purchaseState, purchaseTime, developerPayload); ICursor cursor = mDb.Query(PURCHASE_HISTORY_TABLE_NAME, HISTORY_COLUMNS, HISTORY_PRODUCT_ID_COL + "=?", new String[] { productId }, null, null, null, null); if (cursor == null) { return(0); } int quantity = 0; try { // Count the number of times the product was purchased while (cursor.MoveToNext()) { int stateIndex = cursor.GetInt(2); Consts.PurchaseState state = (Consts.PurchaseState)stateIndex; // Note that a refunded purchase is treated as a purchase. Such // a friendly refund policy is nice for the user. if (state == Consts.PurchaseState.PURCHASED || state == Consts.PurchaseState.REFUNDED) { quantity += 1; } } // Update the "purchased items" table updatePurchasedItem(productId, quantity); } finally { if (cursor != null) { cursor.Close(); } } return(quantity); }
/** * Notifies the application of purchase state changes. The application * can offer an item for sale to the user via * {@link BillingService#requestPurchase(String)}. The BillingService * calls this method after it gets the response. Another way this method * can be called is if the user bought something on another device running * this same app. Then Android Market notifies the other devices that * the user has purchased an item, in which case the BillingService will * also call this method. Finally, this method can be called if the item * was refunded. * @param purchaseState the state of the purchase request (PURCHASED, * CANCELED, or REFUNDED) * @param productId a string identifying a product for sale * @param orderId a string identifying the order * @param purchaseTime the time the product was purchased, in milliseconds * since the epoch (Jan 1, 1970) * @param developerPayload the developer provided "payload" associated with * the order */ public static void purchaseResponse( Context context, Consts.PurchaseState purchaseState, String productId, String orderId, long purchaseTime, String developerPayload) { // Update the database with the purchase state. We shouldn't do that // from the main thread so we do the work in a background thread. // We don't update the UI here. We will update the UI after we update // the database because we need to read and update the current quantity // first. new System.Threading.Tasks.TaskFactory().StartNew(() => { var db = new Db(context); var quantity = db.updatePurchase( orderId, productId, purchaseState, purchaseTime, developerPayload); db.close(); if (sPurchaseObserver != null) { sPurchaseObserver.onPurchaseStateChange(purchaseState, productId, quantity, purchaseTime, developerPayload); } }); }
public void onPurchaseStateChanged(string itemId, Consts.PurchaseState state) { throw new NotImplementedException(); }
/** * Verifies that the data was signed with the given signature, and returns * the list of verified purchases. The data is in JSON format and contains * a nonce (number used once) that we generated and that was signed * (as part of the whole data string) with a private key. The data also * contains the {@link PurchaseState} and product ID of the purchase. * In the general case, there can be an array of purchase transactions * because there may be delays in processing the purchase on the backend * and then several purchases can be batched together. * @param signedData the signed JSON string (signed, not encrypted) * @param signature the signature for the data, signed with the private key */ public static List <VerifiedPurchase> verifyPurchase(string signedData, string signature) { if (signedData == null) { Log.Error(TAG, "data is null"); return(null); } if (Consts.DEBUG) { Log.Info(TAG, "signedData: " + signedData); } var verified = !ExpectSignature; if (!TextUtils.IsEmpty(signature)) { /** * Compute your public key (that you got from the Android Market publisher site). * * Instead of just storing the entire literal string here embedded in the * program, construct the key at runtime from pieces or * use bit manipulation (for example, XOR with some other string) to hide * the actual key. The key itself is not secret information, but we don't * want to make it easy for an adversary to replace the public key with one * of their own and then fake messages from the server. * * Generally, encryption keys / passwords should only be kept in memory * long enough to perform the operation they need to perform. */ string base64EncodedPublicKey = "place your key here"; IPublicKey key = Security.generatePublicKey(base64EncodedPublicKey); verified = Security.verify(key, signedData, signature); if (!verified) { Log.Warn(TAG, "signature does not match data."); //return null; } } JsonObject jObject; JsonArray jTransactionsArray = null; int numTransactions = 0; long nonce = 0L; try { jObject = JsonObject.Parse(signedData) as JsonObject; // The nonce might be null if the user backed out of the buy page. nonce = jObject["nonce"]; jTransactionsArray = jObject["orders"] as JsonArray; if (jTransactionsArray != null) { numTransactions = jTransactionsArray.Count; } } catch //(MalformedJsonException) { return(null); } if (!Security.isNonceKnown(nonce)) { Log.Warn(TAG, "Nonce not found: " + nonce); return(null); } List <VerifiedPurchase> purchases = new List <VerifiedPurchase>(); try { for (int i = 0; i < numTransactions; i++) { var jElement = jTransactionsArray[i]; int response = jElement["purchaseState"]; Consts.PurchaseState purchaseState = (Consts.PurchaseState)response; string productId = jElement["productId"]; string packageName = jElement["packageName"]; long purchaseTime = jElement["purchaseTime"]; string orderId = jElement["orderId"]; string notifyId = null; string developerPayload = null; if (jElement.ContainsKey("notificationId")) { notifyId = jElement["notificationId"]; } if (jElement.ContainsKey("developerPayload")) { developerPayload = jElement["developerPayload"]; } // If the purchase state is PURCHASED, then we require a // verified nonce. if (purchaseState == Consts.PurchaseState.PURCHASED && !verified) { continue; } purchases.Add(new VerifiedPurchase(purchaseState, notifyId, productId, orderId, purchaseTime, developerPayload)); } } catch (Exception e) //(MalformedJsonException e) { Log.Error(TAG, "JSON exception: ", e); return(null); } removeNonce(nonce); return(purchases); }
public ICursor queryTransactions(String productId, PurchaseState state) { return mDb.Query(TABLE_TRANSACTIONS, TABLE_TRANSACTIONS_COLUMNS, COLUMN_PRODUCT_ID + " = ? AND " + COLUMN_STATE + " = ?", new String[] { productId, Convert.ToString(state.ToString()) }, null, null, null); }