Skip to content

Commit 21c9c17

Browse files
author
Robot Media
committed
Differentiate between purchases and transactions. Closes robotmedia#11
1 parent 978813a commit 21c9c17

File tree

4 files changed

+192
-41
lines changed

4 files changed

+192
-41
lines changed

AndroidBillingLibrary/src/net/robotmedia/billing/BillingController.java

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@
2828
import org.json.JSONException;
2929
import org.json.JSONObject;
3030

31-
import net.robotmedia.billing.model.Purchase;
32-
import net.robotmedia.billing.model.PurchaseManager;
31+
import net.robotmedia.billing.model.Transaction;
32+
import net.robotmedia.billing.model.TransactionManager;
3333
import net.robotmedia.billing.request.BillingRequest;
3434
import net.robotmedia.billing.request.ResponseCode;
3535
import net.robotmedia.billing.utils.Compatibility;
@@ -160,7 +160,7 @@ private static void confirmNotifications(Context context, String[] notifyIds) {
160160
public static int countPurchases(Context context, String itemId) {
161161
final byte[] salt = getSalt();
162162
itemId = salt != null ? Security.obfuscate(context, salt, itemId) : itemId;
163-
return PurchaseManager.countPurchases(context, itemId);
163+
return TransactionManager.countPurchases(context, itemId);
164164
}
165165

166166
/**
@@ -181,20 +181,20 @@ private static void getPurchaseInformation(Context context, String notifyId) {
181181
}
182182

183183
/**
184-
* Lists all purchases stored locally, including cancellations and refunds.
184+
* Lists all transactions stored locally, including cancellations and refunds.
185185
*
186186
* @param context
187-
* @return list of purchases.
187+
* @return list of transactions.
188188
*/
189-
public static List<Purchase> getPurchases(Context context) {
190-
List<Purchase> purchases = PurchaseManager.getPurchases(context);
189+
public static List<Transaction> getTransactions(Context context) {
190+
List<Transaction> transactions = TransactionManager.getTransactions(context);
191191
final byte[] salt = getSalt();
192192
if (salt != null) {
193-
for (Purchase p : purchases) {
193+
for (Transaction p : transactions) {
194194
unobfuscate(context, p);
195195
}
196196
}
197-
return purchases;
197+
return transactions;
198198
}
199199

200200
/**
@@ -224,7 +224,7 @@ private static byte[] getSalt() {
224224
public static boolean isPurchased(Context context, String itemId) {
225225
final byte[] salt = getSalt();
226226
itemId = salt != null ? Security.obfuscate(context, salt, itemId) : itemId;
227-
return PurchaseManager.isPurchased(context, itemId);
227+
return TransactionManager.isPurchased(context, itemId);
228228
}
229229

230230
/**
@@ -235,7 +235,7 @@ public static boolean isPurchased(Context context, String itemId) {
235235
* @param state
236236
* new purchase state of the item.
237237
*/
238-
private static void notifyPurchaseStateChange(String itemId, Purchase.PurchaseState state) {
238+
private static void notifyPurchaseStateChange(String itemId, Transaction.PurchaseState state) {
239239
for (IBillingObserver o : observers) {
240240
switch (state) {
241241
case CANCELLED:
@@ -258,9 +258,9 @@ private static void notifyPurchaseStateChange(String itemId, Purchase.PurchaseSt
258258
* @param context
259259
* @param purchase
260260
* purchase to be obfuscated.
261-
* @see #unobfuscate(Context, Purchase)
261+
* @see #unobfuscate(Context, Transaction)
262262
*/
263-
private static void obfuscate(Context context, Purchase purchase) {
263+
private static void obfuscate(Context context, Transaction purchase) {
264264
final byte[] salt = getSalt();
265265
if (salt == null) {
266266
return;
@@ -345,7 +345,7 @@ protected static void onPurchaseStateChanged(Context context, String signedData,
345345
}
346346
}
347347

348-
List<Purchase> purchases;
348+
List<Transaction> purchases;
349349
try {
350350
JSONObject jObject = new JSONObject(signedData);
351351
if (!verifyNonce(jObject)) {
@@ -359,7 +359,7 @@ protected static void onPurchaseStateChanged(Context context, String signedData,
359359
}
360360

361361
ArrayList<String> confirmations = new ArrayList<String>();
362-
for (Purchase p : purchases) {
362+
for (Transaction p : purchases) {
363363
if (p.notificationId != null && automaticConfirmations.contains(p.productId)) {
364364
confirmations.add(p.notificationId);
365365
} else {
@@ -370,9 +370,9 @@ protected static void onPurchaseStateChanged(Context context, String signedData,
370370

371371
// Save itemId and purchaseState before obfuscation for later use
372372
final String itemId = p.productId;
373-
final Purchase.PurchaseState purchaseState = p.purchaseState;
373+
final Transaction.PurchaseState purchaseState = p.purchaseState;
374374
obfuscate(context, p);
375-
PurchaseManager.addPurchase(context, p);
375+
TransactionManager.addTransaction(context, p);
376376

377377
notifyPurchaseStateChange(itemId, purchaseState);
378378
}
@@ -427,16 +427,16 @@ protected static void onResponseCode(Context context, long requestId, int respon
427427
* @throws JSONException
428428
* if the data couldn't be properly parsed.
429429
*/
430-
private static List<Purchase> parsePurchases(JSONObject data) throws JSONException {
431-
ArrayList<Purchase> purchases = new ArrayList<Purchase>();
430+
private static List<Transaction> parsePurchases(JSONObject data) throws JSONException {
431+
ArrayList<Transaction> purchases = new ArrayList<Transaction>();
432432
JSONArray orders = data.optJSONArray(JSON_ORDERS);
433433
int numTransactions = 0;
434434
if (orders != null) {
435435
numTransactions = orders.length();
436436
}
437437
for (int i = 0; i < numTransactions; i++) {
438438
JSONObject jElement = orders.getJSONObject(i);
439-
Purchase p = Purchase.parse(jElement);
439+
Transaction p = Transaction.parse(jElement);
440440
purchases.add(p);
441441
}
442442
return purchases;
@@ -548,9 +548,9 @@ public static void startPurchaseIntent(Activity activity, PendingIntent purchase
548548
* @param context
549549
* @param purchase
550550
* purchase to unobfuscate.
551-
* @see #obfuscate(Context, Purchase)
551+
* @see #obfuscate(Context, Transaction)
552552
*/
553-
private static void unobfuscate(Context context, Purchase purchase) {
553+
private static void unobfuscate(Context context, Transaction purchase) {
554554
final byte[] salt = getSalt();
555555
if (salt == null) {
556556
return;

AndroidBillingLibrary/src/net/robotmedia/billing/model/BillingDB.java

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
package net.robotmedia.billing.model;
2020

21-
import net.robotmedia.billing.model.Purchase.PurchaseState;
21+
import net.robotmedia.billing.model.Transaction.PurchaseState;
2222
import android.content.ContentValues;
2323
import android.content.Context;
2424
import android.database.Cursor;
@@ -28,15 +28,15 @@
2828
public class BillingDB {
2929
static final String DATABASE_NAME = "billing.db";
3030
static final int DATABASE_VERSION = 1;
31-
static final String TABLE_PURCHASES = "purchases";
31+
static final String TABLE_TRANSACTIONS = "purchases";
3232

3333
public static final String COLUMN__ID = "_id";
3434
public static final String COLUMN_STATE = "state";
3535
public static final String COLUMN_PRODUCT_ID = "productId";
3636
public static final String COLUMN_PURCHASE_TIME = "purchaseTime";
3737
public static final String COLUMN_DEVELOPER_PAYLOAD = "developerPayload";
3838

39-
private static final String[] TABLE_PURCHASES_COLUMNS = {
39+
private static final String[] TABLE_TRANSACTIONS_COLUMNS = {
4040
COLUMN__ID, COLUMN_PRODUCT_ID, COLUMN_STATE,
4141
COLUMN_PURCHASE_TIME, COLUMN_DEVELOPER_PAYLOAD
4242
};
@@ -53,28 +53,28 @@ public void close() {
5353
mDatabaseHelper.close();
5454
}
5555

56-
public void insert(Purchase purchase) {
56+
public void insert(Transaction transaction) {
5757
ContentValues values = new ContentValues();
58-
values.put(COLUMN__ID, purchase.orderId);
59-
values.put(COLUMN_PRODUCT_ID, purchase.productId);
60-
values.put(COLUMN_STATE, purchase.purchaseState.ordinal());
61-
values.put(COLUMN_PURCHASE_TIME, purchase.purchaseTime);
62-
values.put(COLUMN_DEVELOPER_PAYLOAD, purchase.developerPayload);
63-
mDb.replace(TABLE_PURCHASES, null /* nullColumnHack */, values);
58+
values.put(COLUMN__ID, transaction.orderId);
59+
values.put(COLUMN_PRODUCT_ID, transaction.productId);
60+
values.put(COLUMN_STATE, transaction.purchaseState.ordinal());
61+
values.put(COLUMN_PURCHASE_TIME, transaction.purchaseTime);
62+
values.put(COLUMN_DEVELOPER_PAYLOAD, transaction.developerPayload);
63+
mDb.replace(TABLE_TRANSACTIONS, null /* nullColumnHack */, values);
6464
}
6565

66-
public Cursor queryPurchases() {
67-
return mDb.query(TABLE_PURCHASES, TABLE_PURCHASES_COLUMNS, null,
66+
public Cursor queryTransactions() {
67+
return mDb.query(TABLE_TRANSACTIONS, TABLE_TRANSACTIONS_COLUMNS, null,
6868
null, null, null, null);
6969
}
7070

71-
public Cursor queryPurchases(String productId, PurchaseState state) {
72-
return mDb.query(TABLE_PURCHASES, TABLE_PURCHASES_COLUMNS, COLUMN_PRODUCT_ID + " = ? AND " + COLUMN_STATE + " = ?",
71+
public Cursor queryTransactions(String productId, PurchaseState state) {
72+
return mDb.query(TABLE_TRANSACTIONS, TABLE_TRANSACTIONS_COLUMNS, COLUMN_PRODUCT_ID + " = ? AND " + COLUMN_STATE + " = ?",
7373
new String[] {productId, String.valueOf(state.ordinal())}, null, null, null);
7474
}
7575

76-
protected static final Purchase createPurchase(Cursor cursor) {
77-
final Purchase purchase = new Purchase();
76+
protected static final Transaction createTransaction(Cursor cursor) {
77+
final Transaction purchase = new Transaction();
7878
purchase.orderId = cursor.getString(0);
7979
purchase.productId = cursor.getString(1);
8080
purchase.purchaseState = PurchaseState.valueOf(cursor.getInt(2));
@@ -90,11 +90,11 @@ public DatabaseHelper(Context context) {
9090

9191
@Override
9292
public void onCreate(SQLiteDatabase db) {
93-
createPurchaseTable(db);
93+
createTransactionsTable(db);
9494
}
9595

96-
private void createPurchaseTable(SQLiteDatabase db) {
97-
db.execSQL("CREATE TABLE " + TABLE_PURCHASES + "(" +
96+
private void createTransactionsTable(SQLiteDatabase db) {
97+
db.execSQL("CREATE TABLE " + TABLE_TRANSACTIONS + "(" +
9898
COLUMN__ID + " TEXT PRIMARY KEY, " +
9999
COLUMN_PRODUCT_ID + " INTEGER, " +
100100
COLUMN_STATE + " TEXT, " +
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/* Copyright 2011 Robot Media SL <http://www.robotmedia.net>. All rights reserved.
2+
*
3+
* This file is part of Android Billing Library.
4+
*
5+
* Android Billing Library is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Lesser Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* Android Billing Library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Lesser Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser Public License
16+
* along with Android Billing Library. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
package net.robotmedia.billing.model;
20+
21+
import org.json.JSONException;
22+
import org.json.JSONObject;
23+
24+
public class Transaction {
25+
26+
public enum PurchaseState {
27+
// Responses to requestPurchase or restoreTransactions.
28+
PURCHASED, // 0: The charge failed on the server.
29+
CANCELLED, // 1: User was charged for the order.
30+
REFUNDED; // 2: User received a refund for the order.
31+
32+
// Converts from an ordinal value to the PurchaseState
33+
public static PurchaseState valueOf(int index) {
34+
PurchaseState[] values = PurchaseState.values();
35+
if (index < 0 || index >= values.length) {
36+
return CANCELLED;
37+
}
38+
return values[index];
39+
}
40+
}
41+
static final String DEVELOPER_PAYLOAD = "developerPayload";
42+
static final String NOTIFICATION_ID = "notificationId";
43+
static final String ORDER_ID = "orderId";
44+
static final String PACKAGE_NAME = "packageName";
45+
static final String PRODUCT_ID = "productId";
46+
static final String PURCHASE_STATE = "purchaseState";
47+
48+
static final String PURCHASE_TIME = "purchaseTime";
49+
50+
public static Transaction parse(JSONObject json) throws JSONException {
51+
final Transaction transaction = new Transaction();
52+
final int response = json.getInt(PURCHASE_STATE);
53+
transaction.purchaseState = PurchaseState.valueOf(response);
54+
transaction.productId = json.getString(PRODUCT_ID);
55+
transaction.packageName = json.getString(PACKAGE_NAME);
56+
transaction.purchaseTime = json.getLong(PURCHASE_TIME);
57+
transaction.orderId = json.optString(ORDER_ID, null);
58+
transaction.notificationId = json.optString(NOTIFICATION_ID, null);
59+
transaction.developerPayload = json.optString(DEVELOPER_PAYLOAD, null);
60+
return transaction;
61+
}
62+
63+
public String developerPayload;
64+
public String notificationId;
65+
public String orderId;
66+
public String packageName;
67+
public String productId;
68+
public PurchaseState purchaseState;
69+
public long purchaseTime;
70+
71+
public Transaction() {}
72+
73+
public Transaction(String orderId, String productId, String packageName, PurchaseState purchaseState,
74+
String notificationId, long purchaseTime, String developerPayload) {
75+
this.orderId = orderId;
76+
this.productId = productId;
77+
this.packageName = packageName;
78+
this.purchaseState = purchaseState;
79+
this.notificationId = notificationId;
80+
this.purchaseTime = purchaseTime;
81+
this.developerPayload = developerPayload;
82+
}
83+
84+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/* Copyright 2011 Robot Media SL <http://www.robotmedia.net>. All rights reserved.
2+
*
3+
* This file is part of Android Billing Library.
4+
*
5+
* Android Billing Library is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Lesser Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* Android Billing Library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Lesser Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser Public License
16+
* along with Android Billing Library. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
package net.robotmedia.billing.model;
20+
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
24+
import net.robotmedia.billing.model.Transaction.PurchaseState;
25+
import android.content.Context;
26+
import android.database.Cursor;
27+
28+
public class TransactionManager {
29+
30+
public synchronized static void addTransaction(Context context, Transaction transaction) {
31+
BillingDB db = new BillingDB(context);
32+
db.insert(transaction);
33+
db.close();
34+
}
35+
36+
public synchronized static boolean isPurchased(Context context, String itemId) {
37+
return countPurchases(context, itemId) > 0;
38+
}
39+
40+
public synchronized static int countPurchases(Context context, String itemId) {
41+
BillingDB db = new BillingDB(context);
42+
final Cursor c = db.queryTransactions(itemId, PurchaseState.PURCHASED);
43+
int count = 0;
44+
if (c != null) {
45+
count = c.getCount();
46+
c.close();
47+
}
48+
db.close();
49+
return count;
50+
}
51+
52+
public synchronized static List<Transaction> getTransactions(Context context) {
53+
BillingDB db = new BillingDB(context);
54+
final Cursor c = db.queryTransactions();
55+
final List<Transaction> transactions = new ArrayList<Transaction>();
56+
if (c != null) {
57+
while (c.moveToNext()) {
58+
final Transaction purchase = BillingDB.createTransaction(c);
59+
transactions.add(purchase);
60+
}
61+
c.close();
62+
}
63+
db.close();
64+
return transactions;
65+
}
66+
67+
}

0 commit comments

Comments
 (0)