22

I am implementing in-app billing in my app to unlock premium features. The in-app billing sets up correctly. Everything seems fine except the 'developer payload' thing.

The sample app says

 /*
     * TODO: verify that the developer payload of the purchase is correct. It will be
     * the same one that you sent when initiating the purchase.
     *
     * WARNING: Locally generating a random string when starting a purchase and
     * verifying it here might seem like a good approach, but this will fail in the
     * case where the user purchases an item on one device and then uses your app on
     * a different device, because on the other device you will not have access to the
     * random string you originally generated.
     *
     * So a good developer payload has these characteristics:
     *
     * 1. If two different users purchase an item, the payload is different between them,
     *    so that one user's purchase can't be replayed to another user.
     *
     * 2. The payload must be such that you can verify it even when the app wasn't the
     *    one who initiated the purchase flow (so that items purchased by the user on
     *    one device work on other devices owned by the user).
     *
     * Using your own server to store and verify developer payloads across app
     * installations is recommended.
     */

The sample app uses an empty string as developer payload. My question is what string to use as a developer payload? Can I use the user's primary email ID?

Maulik
  • 3,286
  • 18
  • 31
Amit Jayant
  • 2,223
  • 2
  • 25
  • 34
  • 3
    check this link: http://stackoverflow.com/questions/17196562/token-that-identify-the-user/17205999#17205999. I hope it will solve your all query. – Maulik Sep 05 '13 at 05:13
  • Thanks Maulik. The answer in the link really helped me :) – Amit Jayant Sep 05 '13 at 16:20
  • You should either close your question or right my answer below to make question solved so that others can check this question useful as for their need. – Maulik Sep 06 '13 at 04:56

2 Answers2

2

For me a random string isn't useful as firstly, it needs to be dependent on the user who bought it, not the device it was bought on. Secondly, it's a non-consumable item, so an empty string may suit, but isn't ideal.

So my way around it is to create an encrypted hash based on a key. Each time a purchase is made, it's uniquely identifiable since the hash should never be the same (this depends on the hashing method, such as bcrypt).

Since the key is the same on all the devices, it's easy to decrypt and verify that the secret message is correct.

In order for the key to remain a secret, I've used various string manipulation functions to mask it so it's not stored in a visible manner.

An example of the text maniluation can be found here: Android In App Billing: securing application public key

String Base64EncodedPublicKey key = DecrementEachletter("Bl4kgle") + GetMiddleBit() + ReverseString("D349824");

This method of creating a hash based on a key allows the payload to be unique and identifiable, at the same time as being reasonably secure. It's not bulletproof, but it sure makes it hard to crack.

Kc Gibson
  • 1,401
  • 11
  • 17
  • How do you identify the user with this approach? – Renjith Feb 04 '15 at 06:56
  • @Renjith, you don't. This method is to identify if the purchase is legit. – Kc Gibson Feb 04 '15 at 23:12
  • 6
    Please i do not understand, how can be unique payload for same inputs? Key is same all the time. And hash from same inputs returns same results. – t0m May 09 '16 at 13:13
  • 1
    @t0m it depends on the hashing method used. Bcrypt allows you to use the same string to get different hashing results. – Kc Gibson Dec 16 '17 at 21:34
1

Please check below answer, it may solved your problem:

if you are using consumable item(managed item) then you can use random generated string

step 1: before on create method declare this:

         private static final char[] symbols = new char[36];

                static {
                    for (int idx = 0; idx < 10; ++idx)
                        symbols[idx] = (char) ('0' + idx);
                    for (int idx = 10; idx < 36; ++idx)
                        symbols[idx] = (char) ('a' + idx - 10);
                }

step 2: set RandomString and SessionIdentifierGenerator class in your activity

          public class RandomString {

        /*
         * static { for (int idx = 0; idx < 10; ++idx) symbols[idx] = (char)
         * ('0' + idx); for (int idx = 10; idx < 36; ++idx) symbols[idx] =
         * (char) ('a' + idx - 10); }
         */

        private final Random random = new Random();

        private final char[] buf;

        public RandomString(int length) {
            if (length < 1)
                throw new IllegalArgumentException("length < 1: " + length);
            buf = new char[length];
        }

        public String nextString() {
            for (int idx = 0; idx < buf.length; ++idx)
                buf[idx] = symbols[random.nextInt(symbols.length)];
            return new String(buf);
        }

    }

    public final class SessionIdentifierGenerator {

        private SecureRandom random = new SecureRandom();

        public String nextSessionId() {
            return new BigInteger(130, random).toString(32);
        }

    }

step 3: pass payload into your puchase request:

RandomString randomString = new RandomString(36);
            System.out.println("RandomString>>>>" + randomString.nextString());
            /* String payload = ""; */
            // bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ
            String payload = randomString.nextString();
            Log.e("Random generated Payload", ">>>>>" + payload);

        Log.d(TAG, "Launching purchase flow for infinite gas subscription.");
            mHelper.launchPurchaseFlow(this, SKU_GAS,
                    IabHelper.ITEM_TYPE_INAPP, RC_REQUEST,
                    mPurchaseFinishedListener, payload);

for more inforamation check this link: Token that identify the user

Hope it will solve your problem.

Community
  • 1
  • 1
Maulik
  • 3,286
  • 18
  • 31
  • 20
    Do *NOT* use a random string. User can buy an item on one device and want to have it on another device. That point is described in the questing (2nd item) – defhlt Feb 25 '15 at 01:47
  • 2
    It is also a problem when you cancel your purchase and try to buy again. Google Play returns the first payload every time. – Kemal Taşkın Jan 12 '16 at 08:32
  • @mlatu https://developer.android.com/training/in-app-billing/purchase-iab-products.html#Purchase Please check the google website. We have to store developer payload in our own web server for the particular user to get rid of the device problem. – Maulik Aug 05 '16 at 07:12
  • @defhlt I thought that it is already attach to your gmail account which you use to complete the purchase. why would payload matter? – Emil Apr 25 '18 at 10:58