Skip to content

Commit

Permalink
add a README notice on how to work with promo codes (anjlab#220)
Browse files Browse the repository at this point in the history
* add a README notice on how to work with promo codes

* add UPGRADING.md
  • Loading branch information
serggl authored Dec 21, 2016
1 parent 325bd36 commit be2df43
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 6 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,18 @@ Call `bp.getSubscriptionTransactionDetails(...)` and check the `purchaseInfo.pur
It will be set to `False` once subscription gets cancelled.
Also notice, that you will need to call periodically `bp.loadOwnedPurchasesFromGoogle()` method in order to update subscription information

## Promo Codes Support

You can use promo codes along with this library, they are supported with one extra notice. According to [this issue](https://github.com/googlesamples/android-play-billing/issues/7)
there is currently a bug in Google Play Services, and it does not respect _Developer Payload_ token made by this library, causing a security validation fault (`BILLING_ERROR_INVALID_DEVELOPER_PAYLOAD` error code).
While Google engineers are working on fixing this (lets hope so, you can also leave a feedback on this issue to make them work faster).

Still, there are couple of workarounds you can use:

1. Handle `BILLING_ERROR_INVALID_DEVELOPER_PAYLOAD` error code in your `onBillingError` implementation. You can check out [#156](https://github.com/anjlab/android-inapp-billing-v3/issues/156) for a suggested workaround. This does not look nice, but it works.
2. Avoid using promo codes in a purchase dialog, prefer entering these codes in Google Play's App _Redeem promo code_ menu.
One way to do this is to distribute your promo codes in form of a redeem link (`https://play.google.com/redeem?code=YOURPROMOCODE`) instead of just a `YOURPROMOCODE` values.
You can find a sample on how to bundle it inside your app [here](https://gist.github.com/SuperThomasLab/6d44b4920dbdc8482a2467d95f66c5df).

## Protection Against Fake "Markets"

Expand Down
12 changes: 12 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## Upgrading Android In-App Billing v3 Library

### Upgrading to >= 1.0.37

If you were supporting promo codes and faced troubled described in #156,
you will need to change your workaround code:

```java
errorCode == Constants.BILLING_ERROR_OTHER_ERROR && _billingProcessor.loadOwnedPurchasesFromGoogle() && _billingProcessor.isPurchased(SKU)
```

`errorCode` needs to be changed to `Constants.BILLING_ERROR_INVALID_DEVELOPER_PAYLOAD`
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,24 @@ public TransactionDetails getSubscriptionTransactionDetails(String productId) {
return getPurchaseTransactionDetails(productId, cachedSubscriptions);
}

private String getDeveloperPayloadFromPurchaseData(JSONObject purchase) {
String value = null;
try
{
value = purchase.has(Constants.RESPONSE_PAYLOAD)
? purchase.getString(Constants.RESPONSE_PAYLOAD) : null;
}
catch (JSONException e)
{
Log.e(LOG_TAG, "Failed to extract developer payload value!");
}
return value != null ? value : "";
}

private boolean validateDeveloperPayload(String expectedValue, String actualValue) {
return expectedValue.equals(actualValue);
}

public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != PURCHASE_FLOW_REQUEST_CODE)
return false;
Expand All @@ -483,11 +501,9 @@ public boolean handleActivityResult(int requestCode, int resultCode, Intent data
try {
JSONObject purchase = new JSONObject(purchaseData);
String productId = purchase.getString(Constants.RESPONSE_PRODUCT_ID);
String developerPayload = purchase.getString(Constants.RESPONSE_PAYLOAD);
if (developerPayload == null)
developerPayload = "";
String developerPayload = getDeveloperPayloadFromPurchaseData(purchase);
boolean purchasedSubscription = purchasePayload.startsWith(Constants.PRODUCT_TYPE_SUBSCRIPTION);
if (purchasePayload.equals(developerPayload)) {
if (validateDeveloperPayload(purchasePayload, developerPayload)) {
if (verifyPurchaseSignature(productId, purchaseData, dataSignature)) {
BillingCache cache = purchasedSubscription ? cachedSubscriptions : cachedProducts;
cache.put(productId, purchaseData, dataSignature);
Expand All @@ -501,7 +517,7 @@ public boolean handleActivityResult(int requestCode, int resultCode, Intent data
} else {
Log.e(LOG_TAG, String.format("Payload mismatch: %s != %s", purchasePayload, developerPayload));
if (eventHandler != null)
eventHandler.onBillingError(Constants.BILLING_ERROR_INVALID_SIGNATURE, null);
eventHandler.onBillingError(Constants.BILLING_ERROR_INVALID_DEVELOPER_PAYLOAD, null);
}
} catch (Exception e) {
Log.e(LOG_TAG, "Error in handleActivityResult", e);
Expand Down Expand Up @@ -539,7 +555,7 @@ private boolean isPurchaseHistoryRestored() {
return loadBoolean(getPreferencesBaseKey() + RESTORE_KEY, false);
}

public void setPurchaseHistoryRestored() {
private void setPurchaseHistoryRestored() {
saveBoolean(getPreferencesBaseKey() + RESTORE_KEY, true);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public class Constants {
public static final int BILLING_ERROR_INVALID_SIGNATURE = 102;
public static final int BILLING_ERROR_LOST_CONTEXT = 103;
public static final int BILLING_ERROR_INVALID_MERCHANT_ID = 104;
public static final int BILLING_ERROR_INVALID_DEVELOPER_PAYLOAD = 105;
public static final int BILLING_ERROR_OTHER_ERROR = 110;
public static final int BILLING_ERROR_CONSUME_FAILED = 111;
public static final int BILLING_ERROR_SKUDETAILS_FAILED = 112;
Expand Down

0 comments on commit be2df43

Please sign in to comment.