@@ -49,8 +49,6 @@ public static enum BillingStatus {
49
49
UNKNOWN , SUPPORTED , UNSUPPORTED
50
50
}
51
51
52
- private static BillingStatus status = BillingStatus .UNKNOWN ;
53
-
54
52
/**
55
53
* Used to provide on-demand values to the billing controller.
56
54
*/
@@ -72,6 +70,8 @@ public interface IConfiguration {
72
70
public String getPublicKey ();
73
71
}
74
72
73
+ private static BillingStatus status = BillingStatus .UNKNOWN ;
74
+
75
75
private static Set <String > automaticConfirmations = new HashSet <String >();
76
76
private static IConfiguration configuration = null ;
77
77
private static boolean debug = false ;
@@ -149,8 +149,9 @@ private static void confirmNotifications(Context context, String[] notifyIds) {
149
149
}
150
150
151
151
/**
152
- * Returns the number of purchases for the specified item. Refunded
153
- * purchases are not subtracted.
152
+ * Returns the number of purchases for the specified item. Refunded and
153
+ * cancelled purchases are not subtracted. See
154
+ * {@link #countPurchasesNet(Context, String)} if they need to be.
154
155
*
155
156
* @param context
156
157
* @param itemId
@@ -163,6 +164,34 @@ public static int countPurchases(Context context, String itemId) {
163
164
return TransactionManager .countPurchases (context , itemId );
164
165
}
165
166
167
+ /**
168
+ * Returns the number of purchases for the specified item, minus the number
169
+ * of cancellations and refunds.
170
+ *
171
+ * @param context
172
+ * @param itemId
173
+ * id of the item whose purchases will be counted.
174
+ * @return number of net purchases for the specified item.
175
+ */
176
+ public static int countPurchasesNet (Context context , String itemId ) {
177
+ final List <Transaction > transactions = BillingController .getTransactions (context );
178
+ int count = 0 ;
179
+ for (Transaction t : transactions ) {
180
+ switch (t .purchaseState ) {
181
+ case PURCHASED :
182
+ count ++;
183
+ break ;
184
+ case CANCELLED :
185
+ count --;
186
+ break ;
187
+ case REFUNDED :
188
+ count --;
189
+ break ;
190
+ }
191
+ }
192
+ return count ;
193
+ }
194
+
166
195
/**
167
196
* Requests purchase information for the specified notification. Immediately
168
197
* followed by a call to
@@ -181,33 +210,45 @@ private static void getPurchaseInformation(Context context, String notifyId) {
181
210
}
182
211
183
212
/**
184
- * Lists all transactions stored locally, including cancellations and refunds.
213
+ * Gets the salt from the configuration and logs a warning if it's null.
214
+ *
215
+ * @return salt.
216
+ */
217
+ private static byte [] getSalt () {
218
+ byte [] salt = null ;
219
+ if (configuration == null || ((salt = configuration .getObfuscationSalt ()) == null )) {
220
+ Log .w (TAG , "Can't (un)obfuscate purchases without salt" );
221
+ }
222
+ return salt ;
223
+ }
224
+
225
+ /**
226
+ * Lists all transactions stored locally, including cancellations and
227
+ * refunds.
185
228
*
186
229
* @param context
187
230
* @return list of transactions.
188
231
*/
189
232
public static List <Transaction > getTransactions (Context context ) {
190
233
List <Transaction > transactions = TransactionManager .getTransactions (context );
191
- final byte [] salt = getSalt ();
192
- if (salt != null ) {
193
- for (Transaction p : transactions ) {
194
- unobfuscate (context , p );
195
- }
196
- }
234
+ unobfuscate (context , transactions );
197
235
return transactions ;
198
236
}
199
237
200
238
/**
201
- * Gets the salt from the configuration and logs a warning if it's null .
239
+ * Lists all transactions of the specified item, stored locally .
202
240
*
203
- * @return salt.
241
+ * @param context
242
+ * @param itemId
243
+ * id of the item whose transactions will be returned.
244
+ * @return list of transactions.
204
245
*/
205
- private static byte [] getSalt ( ) {
206
- byte [] salt = null ;
207
- if ( configuration == null || (( salt = configuration . getObfuscationSalt ()) == null )) {
208
- Log . w ( TAG , "Can't (un)obfuscate purchases without salt" );
209
- }
210
- return salt ;
246
+ public static List < Transaction > getTransactions ( Context context , String itemId ) {
247
+ final byte [] salt = getSalt () ;
248
+ itemId = salt != null ? Security . obfuscate ( context , salt , itemId ) : itemId ;
249
+ List < Transaction > transactions = TransactionManager . getTransactions ( context , itemId );
250
+ unobfuscate ( context , transactions );
251
+ return transactions ;
211
252
}
212
253
213
254
/**
@@ -227,6 +268,19 @@ public static boolean isPurchased(Context context, String itemId) {
227
268
return TransactionManager .isPurchased (context , itemId );
228
269
}
229
270
271
+ /**
272
+ * Returns true if there have been purchases for the specified item and the
273
+ * number is greater than the number of cancellations and refunds.
274
+ *
275
+ * @param context
276
+ * @param itemId
277
+ * item id
278
+ * @return true if there are net purchases for the item, false otherwise.
279
+ */
280
+ public static boolean isPurchasedNet (Context context , String itemId ) {
281
+ return countPurchasesNet (context , itemId ) > 0 ;
282
+ }
283
+
230
284
/**
231
285
* Notifies observers of the purchase state change of the specified item.
232
286
*
@@ -260,7 +314,7 @@ private static void notifyPurchaseStateChange(String itemId, Transaction.Purchas
260
314
* purchase to be obfuscated.
261
315
* @see #unobfuscate(Context, Transaction)
262
316
*/
263
- private static void obfuscate (Context context , Transaction purchase ) {
317
+ static void obfuscate (Context context , Transaction purchase ) {
264
318
final byte [] salt = getSalt ();
265
319
if (salt == null ) {
266
320
return ;
@@ -367,14 +421,8 @@ protected static void onPurchaseStateChanged(Context context, String signedData,
367
421
// refunds.
368
422
addManualConfirmation (p .productId , p .notificationId );
369
423
}
370
-
371
- // Save itemId and purchaseState before obfuscation for later use
372
- final String itemId = p .productId ;
373
- final Transaction .PurchaseState purchaseState = p .purchaseState ;
374
- obfuscate (context , p );
375
- TransactionManager .addTransaction (context , p );
376
-
377
- notifyPurchaseStateChange (itemId , purchaseState );
424
+ storeTransaction (context , p );
425
+ notifyPurchaseStateChange (p .productId , p .purchaseState );
378
426
}
379
427
if (!confirmations .isEmpty ()) {
380
428
final String [] notifyIds = confirmations .toArray (new String [confirmations .size ()]);
@@ -542,15 +590,27 @@ public static void startPurchaseIntent(Activity activity, PendingIntent purchase
542
590
}
543
591
}
544
592
593
+ static void storeTransaction (Context context , Transaction t ) {
594
+ final Transaction t2 = t .clone ();
595
+ obfuscate (context , t2 );
596
+ TransactionManager .addTransaction (context , t2 );
597
+ }
598
+
599
+ static void unobfuscate (Context context , List <Transaction > transactions ) {
600
+ for (Transaction p : transactions ) {
601
+ unobfuscate (context , p );
602
+ }
603
+ }
604
+
545
605
/**
546
- * Unobfuscated the specified purchase.
606
+ * Unobfuscate the specified purchase.
547
607
*
548
608
* @param context
549
609
* @param purchase
550
610
* purchase to unobfuscate.
551
611
* @see #obfuscate(Context, Transaction)
552
612
*/
553
- private static void unobfuscate (Context context , Transaction purchase ) {
613
+ static void unobfuscate (Context context , Transaction purchase ) {
554
614
final byte [] salt = getSalt ();
555
615
if (salt == null ) {
556
616
return ;
0 commit comments